File size: 9,907 Bytes
feca544
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import cv2
import numpy as np
import math
from PIL import Image, ImageDraw, ImageFont, ImageOps
Image.MAX_IMAGE_PIXELS = None
import gradio as gr

def get_exif(image_path):
    try:
        with Image.open(image_path) as img:
            exif_data = img.getexif()
            # print(exif_data)
            if not exif_data:
                return None, None, None
            if "Local Coordinates (m)" in exif_data[34737]:
                return None, None, None
            pixel_scale = exif_data[33550]  # Tag ID for PixelScale
            model_tie_point = exif_data[33922]  # Tag ID for ModelTiePoint
            pixel_scale_x, pixel_scale_y, _ = pixel_scale
            _, _, _, _, latitude, _ = model_tie_point
            return pixel_scale_x, pixel_scale_y, latitude
    except Exception as e:
        print(f"Error extracting metadata: {e}")
        return None, None, None
    
def pixel_size_in_feet(x_scale_degrees, latitude):
    meters_per_degree_lat = 111320  # Average meters per degree of latitude
    meters_per_degree_lon = meters_per_degree_lat * math.cos(math.radians(latitude))
    pixel_size_x_feet = x_scale_degrees * meters_per_degree_lon * 3.28084
    return pixel_size_x_feet

def feet_per_inch(dpi, pixel_size_x_feet):
    # Calculate the number of pixels in 1 inch
    pixels_per_inch = dpi
    # Calculate the number of feet in 1 inch
    feet_per_inch = pixels_per_inch * pixel_size_x_feet
    return feet_per_inch

def nearest_number(n):
    numbers = [1, 5, 10, 20, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000]
    return min(numbers, key=lambda x: abs(x - n))
    
def calculate_dpi(x_scale_degrees, latitude):
    pixel_size_x_feet = pixel_size_in_feet(x_scale_degrees, latitude)
    pixel_size_x_inches = pixel_size_x_feet / 12
    dpi = 1 / pixel_size_x_inches
    
    return dpi

def add_white_space(img):
    _, height_px = img.size
    bottom_space = int(height_px * 0.2)  # Calculate the size of the bottom space
    top_space = int(height_px * 0.1)  # Calculate the size of the top space
    new_height_px = int(height_px + bottom_space + top_space)
    img = ImageOps.expand(img, border=(0, top_space, 0, bottom_space), fill='white')
    return img, new_height_px

def add_white_space_right(img):
    width_px, height_px = img.size
    right_space = int(width_px * 0.3)  # Calculate the size of the right space
    left_space = int(width_px * 0.1)  # Calculate the size of the left space
    new_width_px = int(width_px + right_space)
    img = ImageOps.expand(img, border=(0, 0, right_space, 0), fill='white')
    return img

def draw_scale_bar(right_img, exif_data, compass, line_img, paste_x_position, paste_y_position):
    pixel_scale_x = exif_data[0]
    pixel_scale_y = exif_data[1]
    latitude = exif_data[2]
    if pixel_scale_x is None or pixel_scale_y is None or latitude is None:
        return
    # Calculate pixel size in feet
    pixel_size_x = pixel_size_in_feet(pixel_scale_x, latitude)
    image_width_feet = round(line_img.width * pixel_size_x)
    scale_max = nearest_number(image_width_feet / 5)
    # Calculate scale bar dimensions
    scale_bar_width_px = int(line_img.width * (scale_max / image_width_feet))
    scale_bar_height_px = int(scale_bar_width_px * 0.08)
    # x_position = int(right_img.width * 0.90 - scale_bar_width_px)  # Adjusted for 5% from the right
    x_position = paste_x_position
    y_position = int(right_img.height - (right_img.height - paste_y_position - scale_bar_height_px) * 2)   # Adjusted for new height
    font_size = int(scale_bar_width_px * 0.1)
    # Draw scale bar
    draw = ImageDraw.Draw(right_img)
    try:
        font = ImageFont.truetype("Arial Unicode.ttf", font_size) #DejaVuSans.ttf, Arial.ttf
        print("loaded font")
    except IOError:
        font = ImageFont.load_default()
        print("default :()")
    section_width = scale_bar_width_px / 4
    section_x_position = x_position - 1 * section_width #base 0 incremental position
    text_position = (section_x_position + section_width - font_size / 4, y_position - font_size - 20)
    draw.text(text_position, "0", fill="black", font=font)
    for i in range(4): #other incremental positions
        section_x_position = x_position + i * section_width
        fill_color = "black" if i % 2 == 0 else "white"
        draw.rectangle([section_x_position, y_position, section_x_position + section_width, y_position + scale_bar_height_px], fill=fill_color)
        text = str(round(scale_max / 4 * (i + 1), 2))
        if text.endswith('.0'):
            text = text.rstrip('0').rstrip('.')
        if i == 3:
            text += " feet"
        text_position = (section_x_position + section_width - font_size / 4, y_position - font_size - 20)
        draw.text(text_position, text, fill="black", font=font)
            
    # Draw outline around scale bar
    outline_width = 3
    draw.line([(x_position, y_position), (x_position + scale_bar_width_px, y_position)], fill="black", width=outline_width)
    draw.line([(x_position, y_position), (x_position, y_position + scale_bar_height_px)], fill="black", width=outline_width)
    draw.line([(x_position + scale_bar_width_px, y_position), (x_position + scale_bar_width_px, y_position + scale_bar_height_px)], fill="black", width=outline_width)
    draw.line([(x_position, y_position + scale_bar_height_px), (x_position + scale_bar_width_px, y_position + scale_bar_height_px)], fill="black", width=outline_width)
    
    #feet per inch
    try:
        font = ImageFont.truetype("Arial Unicode.ttf", int(font_size * 1.3)) #DejaVuSans.ttf, Arial.ttf
    except IOError:
        font = ImageFont.load_default()
    dpi = calculate_dpi(pixel_scale_x, latitude)
    dpi = 96
    fpi = round(feet_per_inch(dpi, pixel_size_x), 2)
    feet_per_inch_text = '1" = ' + str(fpi) + ' feet'
    paste_y_position = int(right_img.height - (right_img.height - paste_y_position - scale_bar_height_px) * 3)
    text_position = (x_position, paste_y_position)
    # text_position = (x_position, y_position - font_size - 200)
    draw.text(text_position, feet_per_inch_text, fill='black', font=font)
    
    # # Add in a small compass
    compass = compass.convert("RGBA")
    new_compass_width = int(right_img.width * 0.05)
    compass_ratio = new_compass_width / compass.width
    new_compass_height = int(compass.height * compass_ratio)
    compass = compass.resize((new_compass_width, new_compass_height))
    # Paste the compass in the top right of the image with 5% margin
    paste_compass_x_position = int(right_img.width - (right_img.width + new_compass_width * 2 - paste_x_position) / 2)
    # paste_compass_x_position = int(right_img.width * 0.05)
    paste_compass_y_position = int(right_img.height * 0.05)  # 5% margin on the top
    right_img.paste(compass, (paste_compass_x_position, paste_compass_y_position), compass)
    return right_img

def watermark(right_img, sb_logo, line_img):
    sb_logo = sb_logo.convert("RGBA")
    new_logo_width = int(line_img.width * 0.2)
    logo_ratio = new_logo_width / sb_logo.width
    new_logo_height = int(sb_logo.height * logo_ratio)
    sb_logo = sb_logo.resize((new_logo_width, new_logo_height))
    # Calculate the position for the logo to be 5% from the right
    paste_x_position = int(right_img.width * 0.95 - new_logo_width)
    paste_y_position = int(right_img.height * 0.95 - new_logo_height)#new_height_px - new_logo_height - int(bottom_space / 4)
    right_img.paste(sb_logo, (paste_x_position, paste_y_position), sb_logo)
    return right_img, paste_x_position, paste_y_position
        
def freemium_watermark(img, sb_logo):
    width_px, height_px = img.size
    # Convert the logo to "RGBA" mode and resize it to 80% of the image width
    sb_logo = sb_logo.convert("RGBA")
    new_logo_width = int(width_px * 0.8)  # 80% of the image width
    logo_ratio = new_logo_width / sb_logo.width
    new_logo_height = int(sb_logo.height * logo_ratio)
    sb_logo = sb_logo.resize((new_logo_width, new_logo_height))
    # Make the logo semi-translucent
    transparent_img = Image.new('RGBA', (sb_logo.width, sb_logo.height), (0, 0, 0, 0))  # Create a transparent image of the same size
    sb_logo = Image.blend(transparent_img, sb_logo, alpha=0.3)  # Blend the logo with the transparent image
    # Calculate the position to paste the logo at the center of the image
    paste_x_position = (width_px - new_logo_width) // 2
    paste_y_position = (height_px - new_logo_height) // 2
    img.paste(sb_logo, (paste_x_position, paste_y_position), sb_logo)
    # Save the image
    return img

def create_line_drawing(input_path):
    with Image.open(input_path) as img:
        img_array = np.array(img)
    # Line processing
    blurred = cv2.GaussianBlur(img_array, (7, 7), 0)
    edges = cv2.Canny(blurred, 100, 180)
    structuring_element = cv2.getStructuringElement(cv2.MORPH_RECT, (4,3))
    dilated_edges = cv2.dilate(edges, structuring_element, iterations=1)
    line_drawing = 255 - dilated_edges
    line_img = Image.fromarray(line_drawing)
    
    right_img = add_white_space_right(line_img)
    right_img, paste_x_position, paste_y_position = watermark(right_img, sb_logo, line_img)
    exif_data = list(get_exif(input_path))
    if str(exif_data) != str([None, None, None]):
        print(str(exif_data))
        print("drawing scale bar")
        line_img = draw_scale_bar(right_img, exif_data, compass, line_img, paste_x_position, paste_y_position)
        line_img = freemium_watermark(line_img, sb_logo)
        return line_img
    else:
        line_img = freemium_watermark(line_img, sb_logo)
        return line_img
        
sb_logo = Image.open("./SB_logo_horizontal.png")
compass = Image.open("./compass.jpg")

demo = gr.Interface(create_line_drawing, 
                    inputs=gr.File(type="filepath"), 
                    outputs="image",
                    live=True)
demo.queue(max_size=10).launch()