maldons77 commited on
Commit
9878f3e
·
verified ·
1 Parent(s): 3884714

Upload 5 files

Browse files
Files changed (5) hide show
  1. LICENSE +22 -0
  2. README.md +34 -0
  3. app.py +148 -0
  4. requirements.txt +4 -0
  5. runtime.txt +1 -0
LICENSE ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2025 Eric Maldon
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
README.md ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ ---
3
+ title: AI Meme Generator + Explainer
4
+ emoji: 😂
5
+ colorFrom: purple
6
+ colorTo: pink
7
+ sdk: gradio
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # AI Meme Generator + Explainer
13
+
14
+ ## Overview
15
+ Upload an image and get a short meme caption **plus a brief explanation** of why the joke works. The app combines an image captioning model (BLIP-base) with a lightweight text generator (DistilGPT‑2), so it runs on CPU hardware.
16
+
17
+ ## How it works
18
+ 1. **Caption**: BLIP generates a plain description of the image.
19
+ 2. **Meme line**: a small language model turns that description into a short, witty caption (≤ 12 words).
20
+ 3. **Explain**: a tiny ruleset highlights classic humor cues (relatability, incongruity, exaggeration).
21
+
22
+ ## Features
23
+ - Works on **CPU** (no GPU required).
24
+ - Classic meme text overlay (top/center/bottom + uppercase).
25
+ - Safe, family‑friendly captions (no names, no offensive content).
26
+
27
+ ## How to Run Locally
28
+ ```bash
29
+ pip install -r requirements.txt
30
+ python app.py
31
+ ```
32
+
33
+ ## Acceptable Use
34
+ This project is intended for educational and entertainment purposes only. Do **not** use it to create harmful, offensive, or illegal content. Please respect the model licenses and the Hugging Face Acceptable Use Policy.
app.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from transformers import pipeline, set_seed
3
+ from PIL import Image, ImageDraw, ImageFont
4
+ import textwrap
5
+ import io
6
+
7
+ # Lazy-load models
8
+ _captioner = None
9
+ _generator = None
10
+
11
+ def get_captioner():
12
+ global _captioner
13
+ if _captioner is None:
14
+ _captioner = pipeline("image-to-text", model="Salesforce/blip-image-captioning-base")
15
+ return _captioner
16
+
17
+ def get_generator():
18
+ global _generator
19
+ if _generator is None:
20
+ _generator = pipeline("text-generation", model="distilgpt2")
21
+ return _generator
22
+
23
+ def wrap_text(text, width=22):
24
+ return "\n".join(textwrap.wrap(text, width=width))
25
+
26
+ def draw_meme(img: Image.Image, caption: str, position: str = "bottom", uppercase: bool = True) -> Image.Image:
27
+ if uppercase:
28
+ caption = caption.upper()
29
+
30
+ # Prepare drawing
31
+ draw = ImageDraw.Draw(img)
32
+ W, H = img.size
33
+
34
+ # Choose a font (fallback to default if Impact is not available)
35
+ try:
36
+ font = ImageFont.truetype("Impact.ttf", size=max(24, W // 15))
37
+ except Exception:
38
+ font = ImageFont.load_default()
39
+
40
+ # Wrap caption to fit width
41
+ wrapped = wrap_text(caption, width=max(18, W // 25))
42
+
43
+ # Determine text size
44
+ bbox = draw.multiline_textbbox((0, 0), wrapped, font=font, align="center")
45
+ text_w = bbox[2] - bbox[0]
46
+ text_h = bbox[3] - bbox[1]
47
+
48
+ # Choose y coordinate
49
+ margin = int(0.03 * H)
50
+ if position == "top":
51
+ y = margin
52
+ elif position == "center":
53
+ y = (H - text_h) // 2
54
+ else:
55
+ y = H - text_h - margin
56
+
57
+ # Draw black outline then white text (classic meme style)
58
+ x = (W - text_w) // 2
59
+ outline = max(2, W // 200)
60
+ for dx in (-outline, outline):
61
+ for dy in (-outline, outline):
62
+ draw.multiline_text((x + dx, y + dy), wrapped, font=font, fill="black", align="center")
63
+ draw.multiline_text((x, y), wrapped, font=font, fill="white", align="center")
64
+
65
+ return img
66
+
67
+ def explain_caption(caption: str) -> str:
68
+ cap = caption.lower()
69
+ cues = []
70
+ if any(w in cap for w in ["when ", "me:", "you ", "pov", "that moment"]):
71
+ cues.append("relatable setup")
72
+ if any(w in cap for w in ["but", "yet", "although", "instead"]):
73
+ cues.append("incongruity/twist")
74
+ if any(w in cap for w in ["too", "very", "super", "always", "never"]):
75
+ cues.append("mild exaggeration")
76
+ if any(w in cap for w in ["monday", "wifi", "coffee", "deadline", "sleep"]):
77
+ cues.append("everyday theme")
78
+
79
+ pieces = [
80
+ "Short, high-contrast phrasing (classic meme style).",
81
+ "Focuses on a common situation to trigger recognition.",
82
+ "Adds a small twist that creates incongruity (the core of the joke)."
83
+ ]
84
+ if cues:
85
+ pieces.append("Detected humor cues: " + ", ".join(cues) + ".")
86
+ return " ".join(pieces)
87
+
88
+ def generate_meme(image, style, uppercase, seed):
89
+ if image is None:
90
+ return None, "Please upload an image.", ""
91
+
92
+ if seed is not None and str(seed).strip() != "":
93
+ try:
94
+ set_seed(int(seed))
95
+ except Exception:
96
+ pass
97
+
98
+ # Step 1: caption the image
99
+ captioner = get_captioner()
100
+ raw_caption = captioner(image)[0]["generated_text"]
101
+
102
+ # Step 2: create a witty meme caption from the caption
103
+ prompt = (
104
+ "Write a short, witty meme caption (max 12 words) based on this image description: "
105
+ f"'{raw_caption}'. Use relatable internet humor. Avoid names, no offensive content."
106
+ )
107
+ generator = get_generator()
108
+ gen = generator(prompt, max_new_tokens=24, temperature=0.9, top_p=0.95, do_sample=True)[0]["generated_text"]
109
+ # Get only the newly generated tail after the prompt
110
+ meme_caption = gen[len(prompt):].strip().split("\n")[0]
111
+ # Clean fallbacks
112
+ if len(meme_caption) < 3 or len(meme_caption.split()) < 2:
113
+ meme_caption = "When Monday hits before the coffee"
114
+
115
+ # Step 3: draw caption onto the image
116
+ img = image.convert("RGB").copy()
117
+ img_out = draw_meme(img, meme_caption, position=style, uppercase=uppercase)
118
+
119
+ # Step 4: generate a simple explanation
120
+ explanation = explain_caption(meme_caption)
121
+
122
+ return img_out, meme_caption, explanation
123
+
124
+ with gr.Blocks(title="AI Meme Generator + Explainer") as demo:
125
+ gr.Markdown(
126
+ """
127
+ # AI Meme Generator + Explainer
128
+ Upload an image → the app captions it, writes a short meme line, and explains why it works.
129
+ Runs on CPU using lightweight models (BLIP-base + DistilGPT2).
130
+ """
131
+ )
132
+
133
+ with gr.Row():
134
+ image = gr.Image(type="pil", label="Upload image")
135
+ with gr.Column():
136
+ style = gr.Radio(choices=["top", "center", "bottom"], value="bottom", label="Caption position")
137
+ uppercase = gr.Checkbox(value=True, label="Uppercase caption (meme style)")
138
+ seed = gr.Textbox(value="", label="Seed (optional)", placeholder="e.g., 42")
139
+ btn = gr.Button("Generate Meme")
140
+
141
+ out_img = gr.Image(label="Meme")
142
+ out_caption = gr.Textbox(label="Generated caption", lines=2)
143
+ out_expl = gr.Textbox(label="Why it’s funny (explanation)", lines=4)
144
+
145
+ btn.click(generate_meme, inputs=[image, style, uppercase, seed], outputs=[out_img, out_caption, out_expl])
146
+
147
+ if __name__ == "__main__":
148
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio>=4.36.1
2
+ transformers>=4.41.0
3
+ torch
4
+ pillow
runtime.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ python-3.10