Spaces:
Running
Running
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() |