maldons77's picture
Upload 5 files
9878f3e verified
import gradio as gr
from transformers import pipeline, set_seed
from PIL import Image, ImageDraw, ImageFont
import textwrap
import io
# Lazy-load models
_captioner = None
_generator = None
def get_captioner():
global _captioner
if _captioner is None:
_captioner = pipeline("image-to-text", model="Salesforce/blip-image-captioning-base")
return _captioner
def get_generator():
global _generator
if _generator is None:
_generator = pipeline("text-generation", model="distilgpt2")
return _generator
def wrap_text(text, width=22):
return "\n".join(textwrap.wrap(text, width=width))
def draw_meme(img: Image.Image, caption: str, position: str = "bottom", uppercase: bool = True) -> Image.Image:
if uppercase:
caption = caption.upper()
# Prepare drawing
draw = ImageDraw.Draw(img)
W, H = img.size
# Choose a font (fallback to default if Impact is not available)
try:
font = ImageFont.truetype("Impact.ttf", size=max(24, W // 15))
except Exception:
font = ImageFont.load_default()
# Wrap caption to fit width
wrapped = wrap_text(caption, width=max(18, W // 25))
# Determine text size
bbox = draw.multiline_textbbox((0, 0), wrapped, font=font, align="center")
text_w = bbox[2] - bbox[0]
text_h = bbox[3] - bbox[1]
# Choose y coordinate
margin = int(0.03 * H)
if position == "top":
y = margin
elif position == "center":
y = (H - text_h) // 2
else:
y = H - text_h - margin
# Draw black outline then white text (classic meme style)
x = (W - text_w) // 2
outline = max(2, W // 200)
for dx in (-outline, outline):
for dy in (-outline, outline):
draw.multiline_text((x + dx, y + dy), wrapped, font=font, fill="black", align="center")
draw.multiline_text((x, y), wrapped, font=font, fill="white", align="center")
return img
def explain_caption(caption: str) -> str:
cap = caption.lower()
cues = []
if any(w in cap for w in ["when ", "me:", "you ", "pov", "that moment"]):
cues.append("relatable setup")
if any(w in cap for w in ["but", "yet", "although", "instead"]):
cues.append("incongruity/twist")
if any(w in cap for w in ["too", "very", "super", "always", "never"]):
cues.append("mild exaggeration")
if any(w in cap for w in ["monday", "wifi", "coffee", "deadline", "sleep"]):
cues.append("everyday theme")
pieces = [
"Short, high-contrast phrasing (classic meme style).",
"Focuses on a common situation to trigger recognition.",
"Adds a small twist that creates incongruity (the core of the joke)."
]
if cues:
pieces.append("Detected humor cues: " + ", ".join(cues) + ".")
return " ".join(pieces)
def generate_meme(image, style, uppercase, seed):
if image is None:
return None, "Please upload an image.", ""
if seed is not None and str(seed).strip() != "":
try:
set_seed(int(seed))
except Exception:
pass
# Step 1: caption the image
captioner = get_captioner()
raw_caption = captioner(image)[0]["generated_text"]
# Step 2: create a witty meme caption from the caption
prompt = (
"Write a short, witty meme caption (max 12 words) based on this image description: "
f"'{raw_caption}'. Use relatable internet humor. Avoid names, no offensive content."
)
generator = get_generator()
gen = generator(prompt, max_new_tokens=24, temperature=0.9, top_p=0.95, do_sample=True)[0]["generated_text"]
# Get only the newly generated tail after the prompt
meme_caption = gen[len(prompt):].strip().split("\n")[0]
# Clean fallbacks
if len(meme_caption) < 3 or len(meme_caption.split()) < 2:
meme_caption = "When Monday hits before the coffee"
# Step 3: draw caption onto the image
img = image.convert("RGB").copy()
img_out = draw_meme(img, meme_caption, position=style, uppercase=uppercase)
# Step 4: generate a simple explanation
explanation = explain_caption(meme_caption)
return img_out, meme_caption, explanation
with gr.Blocks(title="AI Meme Generator + Explainer") as demo:
gr.Markdown(
"""
# AI Meme Generator + Explainer
Upload an image → the app captions it, writes a short meme line, and explains why it works.
Runs on CPU using lightweight models (BLIP-base + DistilGPT2).
"""
)
with gr.Row():
image = gr.Image(type="pil", label="Upload image")
with gr.Column():
style = gr.Radio(choices=["top", "center", "bottom"], value="bottom", label="Caption position")
uppercase = gr.Checkbox(value=True, label="Uppercase caption (meme style)")
seed = gr.Textbox(value="", label="Seed (optional)", placeholder="e.g., 42")
btn = gr.Button("Generate Meme")
out_img = gr.Image(label="Meme")
out_caption = gr.Textbox(label="Generated caption", lines=2)
out_expl = gr.Textbox(label="Why it’s funny (explanation)", lines=4)
btn.click(generate_meme, inputs=[image, style, uppercase, seed], outputs=[out_img, out_caption, out_expl])
if __name__ == "__main__":
demo.launch()