image-regeneration / stitching.py
dev-immersfy's picture
Upload 4 files
4a52afd verified
from typing import Tuple
from PIL import Image, ImageDraw, ImageFont, ImageEnhance
from io import BytesIO
import boto3
s3_client = boto3.client("s3")
STYLE_CONFIG = {
"default": {
"patch": {
"background_color": (10, 10, 10, 255),
"text_color": (255, 255, 255, 255),
"opacity": 1,
"top_height": 100,
"bottom_height": 300,
},
"fade": {"color": (10, 10, 10, 255), "strength": 1.7},
"image": {"contrast": 1, "brightness": 1, "saturation": 1},
}
}
def wrap_text(
text: str, font: ImageFont.FreeTypeFont, max_width: int, padding: int = 60
) -> list:
"""Wrap text to fit within the specified width."""
max_width -= 2 * padding
words = text.split()
lines, current_line = [], []
for word in words:
test_line = " ".join(current_line + [word])
text_width = ImageDraw.Draw(
Image.new("RGBA", (max_width + 2 * padding, 1000))
).textbbox((0, 0), test_line, font)[2]
if text_width <= max_width:
current_line.append(word)
else:
lines.append(" ".join(current_line))
current_line = [word]
if current_line:
lines.append(" ".join(current_line))
return lines
def apply_style_effects(
image: Image.Image, visual_style: str = "default"
) -> Image.Image:
"""Apply image style effects such as contrast, brightness, and saturation."""
style_config = STYLE_CONFIG.get(visual_style, STYLE_CONFIG["default"])
enhancers = [
(ImageEnhance.Contrast, style_config["image"]["contrast"]),
(ImageEnhance.Brightness, style_config["image"]["brightness"]),
(ImageEnhance.Color, style_config["image"]["saturation"]),
]
for enhancer_class, factor in enhancers:
image = enhancer_class(image).enhance(factor)
return image.convert("RGBA")
def create_fade_mask(
size: Tuple[int, int], visual_style: str = "default"
) -> Image.Image:
"""Create a fade mask with a gradient effect."""
width, height = size
style_config = STYLE_CONFIG.get(visual_style, STYLE_CONFIG["default"])
fade_height = int(height * 0.25)
mask = Image.new("L", size, 255)
gradient_data = [
int(255 * (i / fade_height) * style_config["fade"]["strength"])
for i in range(fade_height)
]
gradient = Image.new("L", (1, fade_height))
gradient.putdata(gradient_data)
gradient = gradient.resize((width, fade_height))
mask.paste(gradient, (0, 0))
mask.paste(gradient.transpose(Image.FLIP_TOP_BOTTOM), (0, height - fade_height))
return mask
def create_text_patch(
text: str, width: int, height: int, font_path: str, visual_style: str
) -> Image.Image:
"""Create a text patch with wrapping and styling."""
style_config = STYLE_CONFIG.get(visual_style, STYLE_CONFIG["default"])
patch = Image.new(
"RGBA", (width, height), style_config["patch"]["background_color"]
)
draw = ImageDraw.Draw(patch)
font_size = 40
font = None
while font_size > 10:
try:
font = ImageFont.truetype(font_path, font_size)
lines = wrap_text(text, font, width - 40, 60)
font_height = font.getbbox("A")[3] - font.getbbox("A")[1]
total_text_height = len(lines) * (font_height + 10)
if total_text_height <= height - 40 and len(lines) > 0:
break
except Exception:
pass
font_size -= 2
font = font or ImageFont.load_default()
lines = wrap_text(text, font, width - 40, 60)
font_height = font.getbbox("A")[3] - font.getbbox("A")[1]
total_text_height = len(lines) * (font_height + 10)
y = (height - total_text_height) // 2
for line in lines:
bbox = draw.textbbox((0, 0), line, font=font)
text_width = bbox[2] - bbox[0]
x = (width - text_width) // 2
draw.text((x, y), line, font=font, fill=style_config["patch"]["text_color"])
y += font_height + 10
return patch
def process_single_image(
image_data: bytes,
text: str,
font_path: str,
target_width: int = 1024,
target_height: int = 1024,
visual_style: str = "default",
) -> BytesIO:
"""Process a single image with text overlay and styling."""
img = Image.open(BytesIO(image_data))
style_config = STYLE_CONFIG.get(visual_style, STYLE_CONFIG["default"])
top_height, bottom_height = (
style_config["patch"]["top_height"],
style_config["patch"]["bottom_height"],
)
# Process the main image
processed = apply_style_effects(img, visual_style).resize(
(target_width, target_height)
)
# Apply fade effect
mask = create_fade_mask(processed.size, visual_style)
background = Image.new("RGBA", processed.size, style_config["fade"]["color"])
processed = Image.composite(processed, background, mask)
# Create text patches
bottom_patch = create_text_patch(
text, target_width, bottom_height, font_path, visual_style
)
top_patch = Image.new(
"RGBA", (target_width, top_height), style_config["patch"]["background_color"]
)
# Combine all elements
final_image = Image.new(
"RGBA", (target_width, target_height + top_height + bottom_height)
)
final_image.paste(top_patch, (0, 0))
final_image.paste(processed, (0, top_height))
final_image.paste(bottom_patch, (0, top_height + target_height))
# Save to BytesIO
output_buffer = BytesIO()
final_image.save(output_buffer, format="PNG")
output_buffer.seek(0)
return output_buffer