3morixd commited on
Commit
5ec2bf4
·
verified ·
1 Parent(s): b4b069a

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +203 -0
app.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import random
4
+ import gradio as gr
5
+ from huggingface_hub import InferenceClient
6
+ from PIL import Image, ImageDraw, ImageFont
7
+
8
+ # --- Configuration -----------------------------------------------------------
9
+ HF_TOKEN = os.environ.get("HF_TOKEN", None)
10
+ MODEL_ID = "black-forest-labs/FLUX.1-schnell"
11
+
12
+ client = InferenceClient(model=MODEL_ID, token=HF_TOKEN)
13
+
14
+ BG_COLOR = "#0A0F1A"
15
+ ACCENT = "#1FE0E6"
16
+
17
+ # Preset templates: name -> (base_image_prompt, default_top, default_bottom)
18
+ TEMPLATES = {
19
+ "This is Fine AI": (
20
+ "A cartoon character sitting in a burning AI server room surrounded by flames, smiling and saying 'this is fine', meme style, comic illustration",
21
+ "MY GPU IS ON FIRE",
22
+ "THIS IS FINE",
23
+ ),
24
+ "Distracted Boyfriend AI": (
25
+ "A meme photo of a boyfriend walking with his girlfriend but looking back at a passing robot labeled 'NEW AI MODEL', girlfriend labeled 'MY CURRENT MODEL', meme style",
26
+ "MY CURRENT MODEL",
27
+ "NEW HYPED AI MODEL",
28
+ ),
29
+ "Drake Hotline AI": (
30
+ "Drake hotline bling meme format, Drake disapproving panel on top and Drake approving panel on bottom, left panel says 'Writing code myself', right panel says 'Asking ChatGPT to do it', meme template",
31
+ "WRITING CODE MYSELF",
32
+ "ASKING AI TO DO IT",
33
+ ),
34
+ }
35
+
36
+ # Random AI topics for the Random AI Topic button
37
+ AI_TOPICS = [
38
+ "AGI is coming next year",
39
+ "My model hallucinated a whole paper",
40
+ "Prompt engineering is real engineering",
41
+ "I fine-tuned on my DMs",
42
+ "The model refused my prompt",
43
+ "I ran out of GPU credits",
44
+ "My agent looped forever",
45
+ "Context window too small for my TODO list",
46
+ "Open source beat closed source",
47
+ "Inference costs more than my rent",
48
+ "My dataset was just vibes",
49
+ "RLHF made my bot too polite",
50
+ "I forgot to clip the gradients",
51
+ "The model learned to lie",
52
+ "Quantization ruined my quality",
53
+ "My LoRA saved my marriage",
54
+ "Distillation taught the student to cheat",
55
+ "The tokenizer hates Arabic",
56
+ "I spent $0 on training and won the benchmark",
57
+ "Agents are just while loops with vibes",
58
+ ]
59
+
60
+ # Try to find a usable font
61
+ FONT_CANDIDATES = [
62
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
63
+ "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
64
+ "C:\\Windows\\Fonts\\arialbd.ttf",
65
+ "arial.ttf",
66
+ ]
67
+
68
+
69
+ def load_font(size):
70
+ for path in FONT_CANDIDATES:
71
+ if os.path.exists(path):
72
+ return ImageFont.truetype(path, size)
73
+ # fallback default
74
+ return ImageFont.load_default()
75
+
76
+
77
+ def random_topic():
78
+ return random.choice(AI_TOPICS)
79
+
80
+
81
+ # --- Core function -----------------------------------------------------------
82
+ def generate_meme(top_caption, bottom_caption, template_name):
83
+ """Generate a meme image: generate base via FLUX, then overlay text."""
84
+ if template_name and template_name in TEMPLATES:
85
+ base_prompt, def_top, def_bot = TEMPLATES[template_name]
86
+ top = top_caption.strip() if top_caption.strip() else def_top
87
+ bot = bottom_caption.strip() if bottom_caption.strip() else def_bot
88
+ else:
89
+ base_prompt = "A humorous meme style illustration related to artificial intelligence, comic style"
90
+ top = top_caption.strip() if top_caption.strip() else "TOP TEXT"
91
+ bot = bottom_caption.strip() if bottom_caption.strip() else "BOTTOM TEXT"
92
+
93
+ w, h = 1024, 1024
94
+
95
+ try:
96
+ image = client.text_to_image(base_prompt, width=w, height=h)
97
+ if not isinstance(image, Image.Image):
98
+ image = Image.open(io.BytesIO(image)) if hasattr(image, "read") else Image.open(image)
99
+ except Exception as e:
100
+ image = Image.new("RGB", (w, h), BG_COLOR)
101
+ d = ImageDraw.Draw(image)
102
+ d.text((20, h // 2), f"Error: {e}", fill=ACCENT)
103
+
104
+ # Overlay meme text (classic Impact style)
105
+ image = overlay_meme_text(image, top.upper(), bot.upper())
106
+ return image, f"✅ Meme generated! Top: '{top}' | Bottom: '{bot}'"
107
+
108
+
109
+ def overlay_meme_text(img, top_text, bottom_text):
110
+ """Draw classic Impact-font style meme text with black outline."""
111
+ img = img.convert("RGB")
112
+ draw = ImageDraw.Draw(img)
113
+ W, H = img.size
114
+
115
+ # Font sizes proportional to image
116
+ font_size = max(24, int(W / 12))
117
+ font = load_font(font_size)
118
+
119
+ def draw_text_with_outline(text, y, anchor="ma"):
120
+ # Outline (black)
121
+ for dx in (-2, -1, 1, 2):
122
+ for dy in (-2, -1, 1, 2):
123
+ draw.text((W // 2 + dx, y + dy), text, fill="black", font=font, anchor=anchor)
124
+ # Fill (white)
125
+ draw.text((W // 2, y), text, fill="white", font=font, anchor=anchor)
126
+
127
+ # Top text near top
128
+ draw_text_with_outline(top_text, int(H * 0.08), anchor="ma")
129
+ # Bottom text near bottom
130
+ draw_text_with_outline(bottom_text, int(H * 0.92), anchor="ma")
131
+
132
+ return img
133
+
134
+
135
+ def load_template(template_name):
136
+ """Load default captions from a template."""
137
+ if template_name and template_name in TEMPLATES:
138
+ _, def_top, def_bot = TEMPLATES[template_name]
139
+ return def_top, def_bot
140
+ return "", ""
141
+
142
+
143
+ # --- UI -----------------------------------------------------------------------
144
+ CUSTOM_CSS = f"""
145
+ .gradio-container {{background-color: {BG_COLOR} !important;}}
146
+ h1, h2, h3 {{color: {ACCENT} !important;}}
147
+ """
148
+
149
+ with gr.Blocks(
150
+ title="Dispatch AI Meme Lab",
151
+ theme=gr.themes.Base(
152
+ primary_hue="cyan",
153
+ neutral_hue="slate",
154
+ font=("Inter", "system-ui", "sans-serif"),
155
+ ),
156
+ css=CUSTOM_CSS,
157
+ ) as demo:
158
+ gr.Markdown(
159
+ f"""
160
+ <div style="text-align:center;">
161
+ <h1>Dispatch AI Meme Lab</h1>
162
+ <p style="color:{ACCENT};">Generate AI/tech memes with FLUX.1-schnell + PIL text overlay · Dispatch AI (FZE)</p>
163
+ </div>
164
+ """
165
+ )
166
+
167
+ with gr.Row():
168
+ with gr.Column(scale=1):
169
+ template_select = gr.Dropdown(
170
+ list(TEMPLATES.keys()),
171
+ label="Meme Template",
172
+ value=None,
173
+ info="Pick a template to auto-fill captions and base image",
174
+ )
175
+ load_template_btn = gr.Button("Load Template Captions", variant="secondary")
176
+ top_caption = gr.Textbox(label="Top Caption", placeholder="TOP TEXT", lines=1)
177
+ bottom_caption = gr.Textbox(label="Bottom Caption", placeholder="BOTTOM TEXT", lines=1)
178
+ random_topic_btn = gr.Button("🎲 Random AI Topic", variant="secondary")
179
+ topic_box = gr.Textbox(label="Random AI Topic", interactive=True, lines=1)
180
+ generate_btn = gr.Button("🤣 Generate Meme", variant="primary")
181
+ with gr.Column(scale=2):
182
+ output_image = gr.Image(label="Generated Meme", type="pil", show_download_button=True)
183
+ status_box = gr.Textbox(label="Status", interactive=False)
184
+
185
+ # Events
186
+ load_template_btn.click(load_template, inputs=template_select, outputs=[top_caption, bottom_caption])
187
+ random_topic_btn.click(random_topic, outputs=topic_box)
188
+ generate_btn.click(
189
+ generate_meme,
190
+ inputs=[top_caption, bottom_caption, template_select],
191
+ outputs=[output_image, status_box],
192
+ )
193
+
194
+ gr.Markdown(
195
+ """
196
+ <div style="text-align:center; opacity:0.6; padding-top:20px;">
197
+ <small>Dispatch AI (FZE) · UAE · Model: FLUX.1-schnell · Text overlay via Pillow</small>
198
+ </div>
199
+ """
200
+ )
201
+
202
+ if __name__ == "__main__":
203
+ demo.launch()