Created
February 3, 2024 05:24
-
-
Save Gowee/c078343b6058f4acc90e751456e17e38 to your computer and use it in GitHub Desktop.
Add a date stamp to photos
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import sys | |
from PIL import Image, ImageDraw, ImageFont | |
from PIL.ExifTags import TAGS, GPSTAGS | |
from datetime import date, datetime, timezone | |
def calculate_crop_dimensions(image, ratio=3/2): | |
width, height = image.size | |
l = width > height # is landscape | |
if not l: | |
width, height = height, width | |
width, height = min(width, height * ratio), min(height, width / ratio) | |
if not l: | |
width, height = height, width | |
return width, height | |
# if width > height: | |
# return (width, int(width * 2/3)) # Landscape, crop to 3:2 | |
# else: | |
# return (int(height * 3/2), height) # Portrait, crop to 2:3 | |
def adjust_orientation(image): | |
# Check the orientation from Exif data | |
try: | |
exif_data = image.getexif() | |
if exif_data is not None: | |
orientation = exif_data.get(0x0112, 1) | |
if orientation == 3: | |
# Rotate 180 degrees | |
return 180 | |
elif orientation == 6: | |
# Rotate 90 degrees clockwise | |
return 270 | |
elif orientation == 8: | |
# Rotate 90 degrees counter-clockwise | |
return 90 | |
except (AttributeError, KeyError, IndexError, TypeError): | |
print("Error extracting or processing EXIF orientation.") | |
# Default rotation (0 degrees) | |
return 0 | |
def extract_timestamp(image): | |
try: | |
# Get EXIF information | |
exif_data = image.getexif() | |
if exif_data is not None: | |
print(exif_data) | |
exif_info = {TAGS[key]: exif_data[key] for key in exif_data.keys() if key in TAGS} | |
print(exif_info) | |
if date_taken := exif_info.get('DateTimeOriginal', exif_info.get('DateTime', None)): | |
# Convert date string to datetime object | |
date_obj = datetime.strptime(date_taken, '%Y:%m:%d %H:%M:%S') | |
return date_obj | |
elif 'GPSInfo' in exif_info: | |
gps_data = exif_data.get_ifd(next(key for key, value in TAGS.items() if value == "GPSInfo")) #(exif_info['GPSInfo'])) | |
gps_info = {GPSTAGS.get(key, key): value for key, value in gps_data.items()} | |
date_obj = datetime(*map(int, gps_info['GPSDateStamp'].split(":")), *map(int, gps_info['GPSTimeStamp']), tzinfo=timezone.utc).astimezone() | |
return date_obj | |
except Exception as e: | |
print(f"Error extracting EXIF information: {e}") | |
raise e | |
def add_date_watermark(image, font_path='/home/lotus/Downloads/Date-Stamp-Font/Date Stamp Bold.otf'): | |
# draw = ImageDraw.Draw(image) | |
date_taken = extract_timestamp(image) | |
if date_taken: | |
# Format the date | |
date_text = date_taken.strftime('%Y.%m.%d %H:%M') | |
# Determine base point size | |
r = max(image.height, image.width) * 0.001 | |
# Choose font and size | |
font_size = 30 * r | |
font = ImageFont.truetype(font_path, font_size) | |
# Create a blank image to draw the rotated text | |
temp_image = Image.new('RGBA', image.size, (0, 0, 0, 0)) | |
temp_draw = ImageDraw.Draw(temp_image) | |
# Calculate angle based on EXIF orientation | |
orientation = adjust_orientation(image) | |
angle = { | |
0: 0, | |
90: 270, | |
180: 180, | |
270: 90 | |
}.get(orientation, 0) | |
# Draw text on the temporary image, rotated if needed | |
temp_draw.text((0, 0), date_text, font=font, fill='#e5711f') | |
# Calculate text size | |
text_width, text_height = temp_draw.textlength(date_text, font=font), font_size | |
print(image.width, image.height, text_width, text_height) | |
temp_image = temp_image.crop((0, 0, text_width, text_height)) | |
temp_image = temp_image.rotate(angle, expand=True) | |
#temp_image.show() | |
# Calculate position for the watermark (right-bottom corner) | |
# position = (temp_image.width - text_width - 300, temp_image.height - text_height - 300) | |
dx = 10 * r | |
dy = 10 * r | |
if orientation == 90: | |
position = (dx, image.height - text_width - dy) | |
elif orientation == 180: | |
position = (dx, dy) | |
elif orientation == 270: | |
position = (image.width - text_height - dx, dy) | |
else: | |
position = (image.width - text_width - dx, image.height - text_height - dy) | |
position = tuple(int(t) for t in position) | |
# Paste the temporary image onto the original image | |
image.paste(temp_image, position, temp_image) | |
if __name__ == "__main__": | |
if len(sys.argv) != 3: | |
print("Usage: python script.py input_path output_path") | |
sys.exit(1) | |
input_path = sys.argv[1] | |
output_path = sys.argv[2] | |
# Open the image | |
image = Image.open(input_path) | |
# Calculate crop dimensions | |
crop_dimensions = calculate_crop_dimensions(image) | |
# print(image.size, calculate_crop_dimensions(image)) | |
dw = image.width - crop_dimensions[0] | |
dh = image.height - crop_dimensions[1] | |
# Crop the image | |
image = image.crop((dw / 2, dh / 2, dw / 2 + crop_dimensions[0], dh / 2 + crop_dimensions[1])) | |
# image = image.crop((0, 0, crop_dimensions[0], crop_dimensions[1])) | |
# Add date watermark | |
add_date_watermark(image) | |
# Save the result | |
image.save(output_path, exif=image.info['exif'], quality=95) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment