WatchOutForMike commited on
Commit
af3be6d
·
1 Parent(s): d691aa2
Files changed (1) hide show
  1. app.py +162 -85
app.py CHANGED
@@ -1,24 +1,18 @@
1
  import gradio as gr
2
  import numpy as np
3
  import random
 
4
  import torch
5
  from diffusers import DiffusionPipeline
6
- from typing import Dict, Tuple
 
 
 
 
7
 
8
  # Constants
9
  MAX_SEED = np.iinfo(np.int32).max
10
  MAX_IMAGE_SIZE = 2048
11
- DEFAULT_MODEL = "black-forest-labs/FLUX.1-schnell"
12
- DEFAULT_STYLE_NAME = "D&D Map"
13
-
14
- # Load the default model
15
- def load_model(model_name: str = DEFAULT_MODEL):
16
- dtype = torch.bfloat16
17
- device = "cuda" if torch.cuda.is_available() else "cpu"
18
- return DiffusionPipeline.from_pretrained(model_name, torch_dtype=dtype).to(device)
19
-
20
- # Initialize the model
21
- pipe = load_model()
22
 
23
  # Style list for prompt customization
24
  style_list = [
@@ -206,62 +200,54 @@ style_list = [
206
  {"name": "(No style)", "prompt": "{prompt}", "negative_prompt": ""},
207
  ]
208
 
209
- # Create a styles dictionary for quick lookup
210
- STYLES: Dict[str, Tuple[str, str]] = {
211
- style["name"]: (style["prompt"], style["negative_prompt"]) for style in STYLE_LIST
212
- }
213
 
214
  # Function to apply selected style
215
- def apply_style(style_name: str, prompt: str, negative_prompt: str = "") -> Tuple[str, str]:
216
- positive, negative = STYLES.get(style_name, STYLES[DEFAULT_STYLE_NAME])
217
- return positive.replace("{prompt}", prompt), negative + (negative_prompt or "")
218
-
219
- # Inference function for generating images
220
- def generate_image(
221
- prompt: str,
222
- style: str,
223
- seed: int = 42,
224
- randomize_seed: bool = False,
225
- width: int = 1024,
226
- height: int = 1024,
227
- num_inference_steps: int = 4,
228
- progress: gr.Progress = gr.Progress(track_tqdm=True)
229
- ) -> Tuple[str, int]:
230
- try:
231
- # Randomize seed if requested
232
- if randomize_seed:
233
- seed = random.randint(0, MAX_SEED)
234
-
235
- # Apply style to prompt
236
- styled_prompt, negative_prompt = apply_style(style, prompt)
237
-
238
- # Generate image
239
- generator = torch.Generator().manual_seed(seed)
240
- with torch.no_grad():
241
- image = pipe(
242
- prompt=styled_prompt,
243
- width=width,
244
- height=height,
245
- num_inference_steps=num_inference_steps,
246
- generator=generator,
247
- guidance_scale=7.5,
248
- negative_prompt=negative_prompt,
249
- ).images[0]
250
-
251
- return image, seed
252
- except Exception as e:
253
- return f"Error during image generation: {str(e)}", seed
254
 
255
- # Example prompts for inspiration
256
- EXAMPLES = [
257
  ["A 10x8 battle map located in Ravenloft", "D&D Map"],
258
- ["A hero wielding a glowing sword atop a cliff", "Heroic Portrait"],
259
- ["A sprawling dungeon with traps and treasures", "Dungeon Map"],
260
- ["A fierce dragon battling adventurers in a cavern", "Epic Battle"],
261
  ]
262
 
263
- # Custom CSS for a thematic UI
264
- CUSTOM_CSS = """
265
  body {
266
  background-color: #1b1b1b;
267
  font-family: 'Cinzel', serif;
@@ -270,9 +256,11 @@ body {
270
  margin: 0;
271
  padding: 0;
272
  }
 
 
273
  #col-container {
274
  margin: 0 auto;
275
- max-width: 600px;
276
  padding: 20px;
277
  border: 6px solid #8b4513;
278
  background: radial-gradient(circle, #302d2b 0%, #3b3634 60%, #1b1b1b 100%);
@@ -280,31 +268,124 @@ body {
280
  box-shadow: 0 0 30px rgba(0, 0, 0, 0.9), 0 0 15px rgba(139, 69, 19, 0.7);
281
  transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
282
  }
 
283
  #col-container:hover {
284
  transform: scale(1.03);
285
  box-shadow: 0 0 40px rgba(255, 223, 0, 0.8), 0 0 20px rgba(255, 140, 0, 0.8);
286
  }
287
- h1, h2, h3 {
 
 
288
  color: #ffebcc;
289
  text-shadow: 0 0 10px #8b4513, 0 0 20px #ffd700;
290
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  """
292
 
293
- # Gradio interface
294
- with gr.Blocks(css=CUSTOM_CSS) as demo:
295
  with gr.Column(elem_id="col-container"):
296
  # Title and Description
297
  gr.Markdown(
298
  """
299
- # 🛡️ D&D Art & Map Generator ⚔️
300
- **Create stunning artwork and maps for your Dungeons & Dragons campaigns!**
301
- - Generate battle maps, character portraits, and epic scenes.
302
- - Tailored for Dungeon Masters and adventurers alike.
303
- - [Visit Our Website](https://chatdnd.net) | [Support Us](https://buymeacoffee.com/watchoutformike)
304
  """
305
  )
306
 
307
- # Input fields
308
  with gr.Row():
309
  prompt = gr.Textbox(
310
  label="🎲 Describe Your Vision:",
@@ -313,7 +394,7 @@ with gr.Blocks(css=CUSTOM_CSS) as demo:
313
  )
314
  style = gr.Dropdown(
315
  label="🎨 Select a Style",
316
- choices=list(STYLES.keys()),
317
  value=DEFAULT_STYLE_NAME,
318
  )
319
 
@@ -321,7 +402,7 @@ with gr.Blocks(css=CUSTOM_CSS) as demo:
321
  with gr.Row():
322
  run_button = gr.Button("Generate Image")
323
  result = gr.Image(label="🖼️ Your Legendary Vision")
324
-
325
  # Advanced settings
326
  with gr.Accordion("⚙️ Advanced Settings", open=False):
327
  seed = gr.Slider(
@@ -357,23 +438,19 @@ with gr.Blocks(css=CUSTOM_CSS) as demo:
357
  value=4,
358
  )
359
 
360
- # Examples
361
  gr.Examples(
362
- examples=EXAMPLES,
363
  inputs=[prompt, style],
364
  outputs=[result],
365
- fn=generate_image,
366
  cache_examples="lazy",
367
  )
368
 
369
- # Attach event handlers
370
- run_button.click(
371
- fn=generate_image,
372
- inputs=[prompt, style, seed, randomize_seed, width, height, num_inference_steps],
373
- outputs=[result, seed],
374
- )
375
- prompt.submit(
376
- fn=generate_image,
377
  inputs=[prompt, style, seed, randomize_seed, width, height, num_inference_steps],
378
  outputs=[result, seed],
379
  )
 
1
  import gradio as gr
2
  import numpy as np
3
  import random
4
+ import spaces
5
  import torch
6
  from diffusers import DiffusionPipeline
7
+
8
+ # Load the model
9
+ dtype = torch.bfloat16
10
+ device = "cuda" if torch.cuda.is_available() else "cpu"
11
+ pipe = DiffusionPipeline.from_pretrained("black-forest-labs/FLUX.1-schnell", torch_dtype=dtype).to(device)
12
 
13
  # Constants
14
  MAX_SEED = np.iinfo(np.int32).max
15
  MAX_IMAGE_SIZE = 2048
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  # Style list for prompt customization
18
  style_list = [
 
200
  {"name": "(No style)", "prompt": "{prompt}", "negative_prompt": ""},
201
  ]
202
 
203
+ styles = {k["name"]: (k["prompt"], k["negative_prompt"]) for k in style_list}
204
+ STYLE_NAMES = list(styles.keys())
205
+ DEFAULT_STYLE_NAME = "D&D Map"
 
206
 
207
  # Function to apply selected style
208
+ def apply_style(style_name: str, positive: str, negative: str = ""):
209
+ p, n = styles.get(style_name, styles[DEFAULT_STYLE_NAME])
210
+ return p.replace("{prompt}", positive), n + (negative or "")
211
+
212
+ # Inference function
213
+ @spaces.GPU()
214
+ def infer(
215
+ prompt,
216
+ style,
217
+ seed=42,
218
+ randomize_seed=False,
219
+ width=1024,
220
+ height=1024,
221
+ num_inference_steps=4,
222
+ progress=gr.Progress(track_tqdm=True),
223
+ ):
224
+ if randomize_seed:
225
+ seed = random.randint(0, MAX_SEED)
226
+
227
+ # Apply style to prompt
228
+ styled_prompt, negative_prompt = apply_style(style, prompt)
229
+ generator = torch.Generator().manual_seed(seed)
230
+ image = pipe(
231
+ prompt=styled_prompt,
232
+ width=width,
233
+ height=height,
234
+ num_inference_steps=num_inference_steps,
235
+ generator=generator,
236
+ guidance_scale=0.0,
237
+ negative_prompt=negative_prompt,
238
+ ).images[0]
239
+ return image, seed
 
 
 
 
 
 
 
240
 
241
+ # Example prompts
242
+ examples = [
243
  ["A 10x8 battle map located in Ravenloft", "D&D Map"],
244
+ ["A heroic adventurer wielding a flaming sword standing on a cliff", "D&D Art"],
245
+ ["A mystical library with ancient scrolls and glowing runes", "Dark Fantasy"],
246
+ ["A ferocious dragon breathing fire in a dark cavern", "Epic Battle"],
247
  ]
248
 
249
+ css = """
250
+ /* General body styling */
251
  body {
252
  background-color: #1b1b1b;
253
  font-family: 'Cinzel', serif;
 
256
  margin: 0;
257
  padding: 0;
258
  }
259
+
260
+ /* Container */
261
  #col-container {
262
  margin: 0 auto;
263
+ max-width: 550px;
264
  padding: 20px;
265
  border: 6px solid #8b4513;
266
  background: radial-gradient(circle, #302d2b 0%, #3b3634 60%, #1b1b1b 100%);
 
268
  box-shadow: 0 0 30px rgba(0, 0, 0, 0.9), 0 0 15px rgba(139, 69, 19, 0.7);
269
  transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
270
  }
271
+
272
  #col-container:hover {
273
  transform: scale(1.03);
274
  box-shadow: 0 0 40px rgba(255, 223, 0, 0.8), 0 0 20px rgba(255, 140, 0, 0.8);
275
  }
276
+
277
+ /* Fancy title and text effects */
278
+ #col-container h1, #col-container h2, #col-container h3 {
279
  color: #ffebcc;
280
  text-shadow: 0 0 10px #8b4513, 0 0 20px #ffd700;
281
  }
282
+
283
+ #col-container p {
284
+ color: #dcdcdc;
285
+ text-shadow: 0 0 6px rgba(139, 69, 19, 0.8);
286
+ }
287
+
288
+ /* Markdown links */
289
+ #col-container a {
290
+ color: #e6ac00;
291
+ text-decoration: none;
292
+ font-weight: bold;
293
+ transition: color 0.3s ease-in-out, text-shadow 0.3s ease-in-out;
294
+ }
295
+
296
+ #col-container a:hover {
297
+ color: #fff700;
298
+ text-shadow: 0 0 10px #fff700, 0 0 20px #ffebcc;
299
+ }
300
+
301
+ /* Buttons with D&D glowing effect */
302
+ .gr-button {
303
+ background-color: #8b4513;
304
+ color: #f5f5f5;
305
+ font-weight: bold;
306
+ padding: 10px 20px;
307
+ border: 2px solid #b8860b;
308
+ border-radius: 8px;
309
+ cursor: pointer;
310
+ transition: background-color 0.4s ease-in-out, box-shadow 0.4s ease-in-out, transform 0.2s ease-in-out;
311
+ }
312
+
313
+ .gr-button:hover {
314
+ background-color: #b8860b;
315
+ transform: scale(1.05);
316
+ box-shadow: 0 0 12px rgba(255, 223, 0, 0.9), 0 0 24px rgba(255, 140, 0, 0.7);
317
+ }
318
+
319
+ /* Input and Dropdown styling */
320
+ input, select, .gr-input, .gr-slider {
321
+ background-color: #302d2b;
322
+ color: #b59e54;
323
+ border: 2px solid #8b4513;
324
+ border-radius: 5px;
325
+ padding: 8px 12px;
326
+ font-family: 'Cinzel', serif;
327
+ transition: border-color 0.3s ease, box-shadow 0.3s ease;
328
+ }
329
+
330
+ input:focus, select:focus, .gr-input:focus, .gr-slider:focus {
331
+ outline: none;
332
+ border-color: #ffd700;
333
+ box-shadow: 0 0 8px #ffd700;
334
+ }
335
+
336
+ /* Slider styling */
337
+ .gr-slider > div {
338
+ background-color: #8b4513 !important;
339
+ }
340
+
341
+ /* Checkbox customization */
342
+ .gr-checkbox-label {
343
+ font-weight: bold;
344
+ color: #f5f5f5;
345
+ text-shadow: 0 0 5px rgba(139, 69, 19, 0.7);
346
+ }
347
+
348
+ /* Accordion effects */
349
+ .gr-accordion {
350
+ background: linear-gradient(145deg, #302d2b, #3b3634);
351
+ color: #f5f5f5;
352
+ border: 2px solid #8b4513;
353
+ border-radius: 10px;
354
+ padding: 8px;
355
+ transition: all 0.3s ease;
356
+ }
357
+
358
+ .gr-accordion:hover {
359
+ box-shadow: 0 0 12px rgba(255, 223, 0, 0.7), 0 0 20px rgba(255, 140, 0, 0.5);
360
+ }
361
+
362
+ /* Image glow effect */
363
+ .gr-image {
364
+ border: 4px solid #8b4513;
365
+ border-radius: 10px;
366
+ box-shadow: 0 0 10px #8b4513, 0 0 20px #ffd700;
367
+ }
368
+
369
+ /* Add glow on hover for images */
370
+ .gr-image:hover {
371
+ box-shadow: 0 0 20px rgba(255, 140, 0, 0.7), 0 0 40px rgba(255, 223, 0, 0.9);
372
+ }
373
  """
374
 
375
+ # Interface
376
+ with gr.Blocks(css=css) as demo:
377
  with gr.Column(elem_id="col-container"):
378
  # Title and Description
379
  gr.Markdown(
380
  """
381
+ # 🛡️ ChatDnD.net D&D Map Generator ⚔️
382
+ **Unleash Your Imagination!** Create heroes, maps, quests, and epic scenes to bring your campaigns to life.
383
+ Tailored for adventurers seeking inspiration or Dungeon Masters constructing their next grand story. <br>
384
+ [Visit Our Website](https://chatdnd.net) | [Support Us](https://buymeacoffee.com/watchoutformike)
 
385
  """
386
  )
387
 
388
+ # Prompt input and style selector
389
  with gr.Row():
390
  prompt = gr.Textbox(
391
  label="🎲 Describe Your Vision:",
 
394
  )
395
  style = gr.Dropdown(
396
  label="🎨 Select a Style",
397
+ choices=STYLE_NAMES,
398
  value=DEFAULT_STYLE_NAME,
399
  )
400
 
 
402
  with gr.Row():
403
  run_button = gr.Button("Generate Image")
404
  result = gr.Image(label="🖼️ Your Legendary Vision")
405
+
406
  # Advanced settings
407
  with gr.Accordion("⚙️ Advanced Settings", open=False):
408
  seed = gr.Slider(
 
438
  value=4,
439
  )
440
 
441
+ # Examples with styles
442
  gr.Examples(
443
+ examples=examples,
444
  inputs=[prompt, style],
445
  outputs=[result],
446
+ fn=infer,
447
  cache_examples="lazy",
448
  )
449
 
450
+ # Interactivity
451
+ gr.on(
452
+ triggers=[run_button.click, prompt.submit],
453
+ fn=infer,
 
 
 
 
454
  inputs=[prompt, style, seed, randomize_seed, width, height, num_inference_steps],
455
  outputs=[result, seed],
456
  )