Meme-caption-generator / utils /image_utils.py
nursulu
add functions
ceec8fc
raw
history blame
5.3 kB
import os
import re
from PIL import Image, ImageDraw, ImageFont
import textwrap
def get_unique_filename(filename):
"""
Generate a unique filename by appending a number if a file with the same name already exists.
"""
if not os.path.exists(filename):
return filename
base, ext = os.path.splitext(filename)
counter = 1
new_filename = f"{base}_{counter}{ext}"
while os.path.exists(new_filename):
counter += 1
new_filename = f"{base}_{counter}{ext}"
return new_filename
def save_image_with_unique_name(image, path):
unique_path = get_unique_filename(path)
image.save(unique_path)
print(f"Image saved as: {unique_path}")
def find_text_in_answer(text):
print("Full caption:", text)
text = text.split("Caption:")[1]
text = text.replace("\n", "")
text = text.replace("model", "")
# Remove everything that lookslike <>
text = re.sub(r'<[^>]*>', '', text)
# Remove non-alphanumeric characters (keeping spaces)
text = re.sub(r'[^a-zA-Z0-9\?\!\s]', '', text)
print("Filtered caption:", text)
if text:
return text
else:
return "Me when I couldn't parse the model's answer but I still want you to smile :)"
def draw_text(draw, text, position, font, max_width, outline_color="black", text_color="white", outline_width=2):
"""
Draw text on the image with an outline, splitting it into lines if necessary and returning the total height used by the text.
The text is horizontally centered in the specified max_width.
"""
print("Adding the caption on the image...")
# Split the text into multiple lines based on the max width
lines = []
words = text.split()
line = ''
for word in words:
test_line = f'{line} {word}'.strip()
bbox = draw.textbbox((0, 0), test_line, font=font)
width = bbox[2] - bbox[0] # Width of the text
if width <= max_width:
line = test_line
else:
if line: # Avoid appending empty lines
lines.append(line)
line = word
if line:
lines.append(line)
y = position[1]
# Draw the text with an outline (black) first, centered horizontally
for line in lines:
# Calculate the width of the line and adjust the x position to center it
bbox = draw.textbbox((0, 0), line, font=font)
line_width = bbox[2] - bbox[0]
x = (max_width - line_width) // 2 + position[0]
# Draw the outline by drawing the text multiple times around the original position
for offset_x in [-outline_width, 0, outline_width]:
for offset_y in [-outline_width, 0, outline_width]:
if offset_x != 0 or offset_y != 0:
draw.text((x + offset_x, y + offset_y), line, font=font, fill=outline_color)
# Draw the main text (white) on top of the outline
draw.text((x, y), line, font=font, fill=text_color)
y += bbox[3] - bbox[1] # Update y position based on line height
return y - position[1] # Return the total height used by the text
def calculate_text_height(caption, font, max_width):
"""
Calculate the height of the text when drawn, given the caption, font, and maximum width.
"""
image = Image.new('RGB', (max_width, 1))
draw = ImageDraw.Draw(image)
return draw_text(draw, caption, (0, 0), font, max_width)
def add_caption(image_path, caption, output_path, top_margin=10, bottom_margin=10, max_caption_length=10, min_distance_from_bottom_mm=10):
image = Image.open(image_path)
draw = ImageDraw.Draw(image)
width, height = image.size
# Convert mm to pixels (assuming 96 DPI)
dpi = 96
min_distance_from_bottom_px = min_distance_from_bottom_mm * dpi / 25.4
# Split the caption into two parts if it is too long
if len(caption.split()) > max_caption_length:
font_size=20
total_len = len(caption.split())
mid = int(total_len / 2)
top_caption = caption.split()[:mid]
bottom_caption = caption.split()[mid:]
top_caption = " ".join(top_caption)
bottom_caption = " ".join(bottom_caption)
else:
top_caption = ""
bottom_caption = caption
font_size=30
# Load a font
font = ImageFont.truetype(r"fonts/Anton/Anton-Regular.ttf", font_size)
# Top caption
top_caption_position = (width // 10, top_margin)
draw_text(draw, top_caption, top_caption_position, font, width - 2 * (width // 10))
# Bottom caption
if bottom_caption: # Draw bottom caption only if it's not empty
# Calculate the height of the bottom caption
bottom_caption_height = calculate_text_height(bottom_caption, font, width - 2 * (width // 10))
bottom_caption_position = (width // 10, height - min_distance_from_bottom_px - bottom_caption_height)
draw_text(draw, bottom_caption, bottom_caption_position, font, width - 2 * (width // 10))
save_image_with_unique_name(image, output_path)
return image
def overlay_caption(text, img_path, output_dir):
img_name = img_path.split("/")[-1]
text = find_text_in_answer(text)
text = text.strip(".")
image = add_caption(img_path, text, output_dir+"/"+img_name)
return image