Spaces:
Sleeping
Sleeping
File size: 5,271 Bytes
9878f3e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
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()
|