Pepe-Meme-Generator / src /utils /image_processor.py
MJaheen's picture
Add new features and fixes
cc5958e
raw
history blame
8.12 kB
"""Image Processing Utilities for Meme Creation.
This module provides utilities for post-processing generated images:
- Adding classic meme text with outlines
- Adding signatures/watermarks
- Enhancing image quality (sharpness, contrast)
All methods are static and can be used without instantiation.
The ImageProcessor class acts as a namespace for image manipulation functions.
Author: MJaheen
License: MIT
"""
from PIL import Image, ImageDraw, ImageFont, ImageEnhance
from typing import Optional, Tuple
import logging
logger = logging.getLogger(__name__)
class ImageProcessor:
"""
Static utility class for image post-processing operations.
This class provides methods for enhancing generated images with meme text,
signatures, and quality improvements. All methods are static and work with
PIL Image objects.
Methods:
add_meme_text: Add top/bottom text in classic meme style
add_signature: Add watermark/signature to image
enhance_image: Apply sharpness and contrast enhancements
"""
@staticmethod
def add_meme_text(
image: Image.Image,
top_text: str = "",
bottom_text: str = "",
font_size: int = 40,
font_path: Optional[str] = None,
) -> Image.Image:
"""
Add classic Impact-font meme text with white text and black outline.
Creates the traditional meme format with text at the top and/or bottom
of the image. Text is automatically converted to uppercase and rendered
with a thick black outline for readability on any background.
Args:
image: Input PIL Image to add text to
top_text: Text to display at top of image (default: "")
bottom_text: Text to display at bottom of image (default: "")
font_size: Size of the font in points (default: 40)
font_path: Optional path to custom font file (default: uses Impact)
Returns:
PIL Image with meme text overlay (copy of original, not modified in-place)
Note:
Falls back to default font if Impact font is not found.
Text is centered horizontally automatically.
"""
img = image.copy()
draw = ImageDraw.Draw(img)
# Load font
try:
if font_path:
font = ImageFont.truetype(font_path, font_size)
else:
font = ImageFont.truetype("impact.ttf", font_size)
except:
font = ImageFont.load_default()
logger.warning("Using default font")
# Add top text
if top_text:
ImageProcessor._draw_text_with_outline(
draw, top_text.upper(), (img.width // 2, 30), font
)
# Add bottom text
if bottom_text:
ImageProcessor._draw_text_with_outline(
draw, bottom_text.upper(), (img.width // 2, img.height - 50), font
)
return img
@staticmethod
def _draw_text_with_outline(
draw: ImageDraw.Draw,
text: str,
position: Tuple[int, int],
font: ImageFont.FreeTypeFont,
outline_width: int = 3,
):
"""Draw text with black outline"""
x, y = position
# Draw outline
for adj_x in range(-outline_width, outline_width + 1):
for adj_y in range(-outline_width, outline_width + 1):
draw.text(
(x + adj_x, y + adj_y),
text,
font=font,
fill="black",
anchor="mm"
)
# Draw main text
draw.text(position, text, font=font, fill="white", anchor="mm")
@staticmethod
def add_signature(
image: Image.Image,
signature: str = "MJ",
position: str = "bottom-right",
font_size: int = 20,
opacity: int = 180,
) -> Image.Image:
"""Add a small signature/watermark to the image
Args:
image: Input image
signature: Text to add as signature
position: Position of signature (bottom-right, bottom-left, top-right, top-left)
font_size: Size of the signature font
opacity: Opacity of the signature (0-255)
"""
img = image.copy()
# Create a transparent overlay
overlay = Image.new('RGBA', img.size, (255, 255, 255, 0))
draw = ImageDraw.Draw(overlay)
# Load font
try:
font = ImageFont.truetype("arial.ttf", font_size)
except:
try:
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", font_size)
except:
font = ImageFont.load_default()
logger.warning("Using default font for signature")
# Calculate text size and position
bbox = draw.textbbox((0, 0), signature, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
padding = 10
if position == "bottom-right":
x = img.width - text_width - padding
y = img.height - text_height - padding
elif position == "bottom-left":
x = padding
y = img.height - text_height - padding
elif position == "top-right":
x = img.width - text_width - padding
y = padding
elif position == "top-left":
x = padding
y = padding
else:
x = img.width - text_width - padding
y = img.height - text_height - padding
# Draw signature with semi-transparent background
bg_padding = 5
draw.rectangle(
[x - bg_padding, y - bg_padding,
x + text_width + bg_padding, y + text_height + bg_padding],
fill=(0, 0, 0, opacity // 2)
)
# Draw text
draw.text((x, y), signature, font=font, fill=(255, 255, 255, opacity))
# Convert to RGB if needed and composite
if img.mode != 'RGBA':
img = img.convert('RGBA')
img = Image.alpha_composite(img, overlay)
# Convert back to RGB
if img.mode == 'RGBA':
rgb_img = Image.new('RGB', img.size, (255, 255, 255))
rgb_img.paste(img, mask=img.split()[3])
return rgb_img
return img
@staticmethod
def enhance_image(
image: Image.Image,
sharpness: float = 1.2,
contrast: float = 1.1,
) -> Image.Image:
"""
Apply sharpness and contrast enhancements to improve image quality.
This method applies PIL's ImageEnhance filters to make the image
crisper and more vibrant. Useful for post-processing AI-generated
images which can sometimes appear slightly soft.
Args:
image: Input PIL Image to enhance
sharpness: Sharpness multiplier (default: 1.2)
- 0.0: Blurred
- 1.0: Original sharpness
- 2.0: Very sharp
Recommended range: 1.0-1.5
contrast: Contrast multiplier (default: 1.1)
- 0.0: Gray
- 1.0: Original contrast
- 2.0: High contrast
Recommended range: 1.0-1.3
Returns:
Enhanced PIL Image (modified in-place)
Example:
>>> image = Image.open("soft_image.png")
>>> enhanced = ImageProcessor.enhance_image(image, sharpness=1.3, contrast=1.2)
>>> enhanced.save("sharp_image.png")
"""
# Sharpen
enhancer = ImageEnhance.Sharpness(image)
image = enhancer.enhance(sharpness)
# Adjust contrast
enhancer = ImageEnhance.Contrast(image)
image = enhancer.enhance(contrast)
return image