adamelliotfields commited on
Commit
0d34381
1 Parent(s): af35186

Sync with adamelliotfields/diffusion

Browse files
Files changed (14) hide show
  1. DOCS.md +8 -30
  2. README.md +3 -7
  3. app.css +7 -20
  4. app.py +169 -171
  5. data/prompts.json +23 -46
  6. data/styles.json +0 -136
  7. lib/__init__.py +2 -12
  8. lib/config.py +12 -20
  9. lib/inference.py +47 -86
  10. lib/loader.py +73 -72
  11. lib/logger.py +55 -0
  12. lib/utils.py +18 -32
  13. partials/intro.html +3 -14
  14. requirements.txt +6 -13
DOCS.md CHANGED
@@ -1,8 +1,8 @@
1
- # Diffusion XL
2
 
3
  TL;DR: Enter a prompt or roll the `🎲` and press `Generate`.
4
 
5
- ## Prompting
6
 
7
  Positive and negative prompts are embedded by [Compel](https://github.com/damian0815/compel) for weighting. See [syntax features](https://github.com/damian0815/compel/blob/main/doc/syntax.md) to learn more.
8
 
@@ -10,52 +10,30 @@ Use `+` or `-` to increase the weight of a token. The weight grows exponentially
10
 
11
  For groups of tokens, wrap them in parentheses and multiply by a float between 0 and 2. For example, `a (birthday cake)1.3 on a table` will increase the weight of both `birthday` and `cake` by 1.3x. This also means the entire scene will be more birthday-like, not just the cake. To counteract this, you can use `-` inside the parentheses on specific tokens, e.g., `a (birthday-- cake)1.3`, to reduce the birthday aspect.
12
 
13
- This is the same syntax used in [InvokeAI](https://invoke-ai.github.io/InvokeAI/features/PROMPTS/) and it differs from AUTOMATIC1111:
14
-
15
- | Compel | AUTOMATIC1111 |
16
- | ----------- | ------------- |
17
- | `blue++` | `((blue))` |
18
- | `blue--` | `[[blue]]` |
19
- | `(blue)1.2` | `(blue:1.2)` |
20
- | `(blue)0.8` | `(blue:0.8)` |
21
-
22
- ### Arrays
23
-
24
- Arrays allow you to generate multiple different images from a single prompt. For example, `an adult [[blonde,brunette]] [[man,woman]]` will expand into **4** different prompts. This implementation was inspired by [Fooocus](https://github.com/lllyasviel/Fooocus/pull/1503).
25
-
26
- > NB: Make sure to set `Images` to the number of images you want to generate. Otherwise, only the first prompt will be used.
27
-
28
- ## Models
29
 
30
  Each model checkpoint has a different aesthetic:
31
 
32
- * [cagliostrolab/animagine-xl-3.1](https://huggingface.co/cagliostrolab/animagine-xl-3.1): anime
33
  * [cyberdelia/CyberRealisticXL](https://huggingface.co/cyberdelia/CyberRealsticXL): photorealistic
34
  * [fluently/Fluently-XL-Final](https://huggingface.co/fluently/Fluently-XL-Final): general purpose
35
  * [segmind/Segmind-Vega](https://huggingface.co/segmind/Segmind-Vega): lightweight general purpose (default)
36
  * [SG161222/RealVisXL_V5.0](https://huggingface.co/SG161222/RealVisXL_V5.0): photorealistic
37
  * [stabilityai/stable-diffusion-xl-base-1.0](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0): base
38
 
39
- ## Styles
40
-
41
- [Styles](https://huggingface.co/spaces/adamelliotfields/diffusion/blob/main/data/styles.json) are prompt templates that wrap your positive and negative prompts. They were originally derived from the [twri/sdxl_prompt_styler](https://github.com/twri/sdxl_prompt_styler) Comfy node, but have since been entirely rewritten.
42
-
43
- Start by framing a simple subject like `portrait of a young adult woman` or `landscape of a mountain range` and experiment.
44
-
45
- ## Scale
46
 
47
  Rescale up to 4x using [Real-ESRGAN](https://github.com/xinntao/Real-ESRGAN) with weights from [ai-forever](ai-forever/Real-ESRGAN). Necessary for high-resolution images.
48
 
49
- ## Advanced
50
 
51
- ### DeepCache
52
 
53
- [DeepCache](https://github.com/horseee/DeepCache) caches lower UNet layers and reuses them every `Interval` steps. Trade quality for speed:
54
  * `1`: no caching (default)
55
  * `2`: more quality
56
  * `3`: balanced
57
  * `4`: more speed
58
 
59
- ### Refiner
60
 
61
  Use the [ensemble of expert denoisers](https://research.nvidia.com/labs/dir/eDiff-I/) technique, where the first 80% of timesteps are denoised by the base model and the remaining 80% by the [refiner](https://huggingface.co/stabilityai/stable-diffusion-xl-refiner-1.0). Not available with image-to-image pipelines.
 
1
+ ## Usage
2
 
3
  TL;DR: Enter a prompt or roll the `🎲` and press `Generate`.
4
 
5
+ ### Prompting
6
 
7
  Positive and negative prompts are embedded by [Compel](https://github.com/damian0815/compel) for weighting. See [syntax features](https://github.com/damian0815/compel/blob/main/doc/syntax.md) to learn more.
8
 
 
10
 
11
  For groups of tokens, wrap them in parentheses and multiply by a float between 0 and 2. For example, `a (birthday cake)1.3 on a table` will increase the weight of both `birthday` and `cake` by 1.3x. This also means the entire scene will be more birthday-like, not just the cake. To counteract this, you can use `-` inside the parentheses on specific tokens, e.g., `a (birthday-- cake)1.3`, to reduce the birthday aspect.
12
 
13
+ ### Models
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  Each model checkpoint has a different aesthetic:
16
 
 
17
  * [cyberdelia/CyberRealisticXL](https://huggingface.co/cyberdelia/CyberRealsticXL): photorealistic
18
  * [fluently/Fluently-XL-Final](https://huggingface.co/fluently/Fluently-XL-Final): general purpose
19
  * [segmind/Segmind-Vega](https://huggingface.co/segmind/Segmind-Vega): lightweight general purpose (default)
20
  * [SG161222/RealVisXL_V5.0](https://huggingface.co/SG161222/RealVisXL_V5.0): photorealistic
21
  * [stabilityai/stable-diffusion-xl-base-1.0](https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0): base
22
 
23
+ ### Scale
 
 
 
 
 
 
24
 
25
  Rescale up to 4x using [Real-ESRGAN](https://github.com/xinntao/Real-ESRGAN) with weights from [ai-forever](ai-forever/Real-ESRGAN). Necessary for high-resolution images.
26
 
27
+ ### Advanced
28
 
29
+ #### DeepCache
30
 
31
+ [DeepCache](https://github.com/horseee/DeepCache) caches lower UNet layers and reuses them every _n_ steps. Trade quality for speed:
32
  * `1`: no caching (default)
33
  * `2`: more quality
34
  * `3`: balanced
35
  * `4`: more speed
36
 
37
+ #### Refiner
38
 
39
  Use the [ensemble of expert denoisers](https://research.nvidia.com/labs/dir/eDiff-I/) technique, where the first 80% of timesteps are denoised by the base model and the remaining 80% by the [refiner](https://huggingface.co/stabilityai/stable-diffusion-xl-refiner-1.0). Not available with image-to-image pipelines.
README.md CHANGED
@@ -6,16 +6,15 @@ emoji: 🦣
6
  colorFrom: gray
7
  colorTo: red
8
  sdk: gradio
9
- sdk_version: 4.41.0
10
  python_version: 3.11.9
11
  app_file: app.py
12
  fullWidth: false
13
- pinned: false
14
  header: mini
15
  license: apache-2.0
16
  models:
17
  - ai-forever/Real-ESRGAN
18
- - cagliostrolab/animagine-xl-3.1
19
  - cyberdelia/CyberRealsticXL
20
  - fluently/Fluently-XL-Final
21
  - madebyollin/sdxl-vae-fp16-fix
@@ -25,7 +24,6 @@ models:
25
  - stabilityai/stable-diffusion-xl-refiner-1.0
26
  preload_from_hub:
27
  - ai-forever/Real-ESRGAN RealESRGAN_x2.pth,RealESRGAN_x4.pth
28
- - cagliostrolab/animagine-xl-3.1 animagine-xl-3.1.safetensors
29
  - cyberdelia/CyberRealsticXL CyberRealisticXLPlay_V1.0.safetensors
30
  - fluently/Fluently-XL-Final FluentlyXL-Final.safetensors
31
  - madebyollin/sdxl-vae-fp16-fix config.json,diffusion_pytorch_model.safetensors
@@ -46,9 +44,7 @@ preload_from_hub:
46
  Gradio app for Stable Diffusion XL featuring:
47
 
48
  * txt2img pipeline with refiner (img2img with IP-Adapter and ControlNet coming soon)
49
- * Curated models (LoRAs and TIs coming soon)
50
- * Compel prompt weighting
51
- * Dozens of styles and starter prompts
52
  * Multiple samplers with Karras scheduling
53
  * DeepCache available
54
  * Real-ESRGAN upscaling
 
6
  colorFrom: gray
7
  colorTo: red
8
  sdk: gradio
9
+ sdk_version: 4.44.1
10
  python_version: 3.11.9
11
  app_file: app.py
12
  fullWidth: false
13
+ pinned: true
14
  header: mini
15
  license: apache-2.0
16
  models:
17
  - ai-forever/Real-ESRGAN
 
18
  - cyberdelia/CyberRealsticXL
19
  - fluently/Fluently-XL-Final
20
  - madebyollin/sdxl-vae-fp16-fix
 
24
  - stabilityai/stable-diffusion-xl-refiner-1.0
25
  preload_from_hub:
26
  - ai-forever/Real-ESRGAN RealESRGAN_x2.pth,RealESRGAN_x4.pth
 
27
  - cyberdelia/CyberRealsticXL CyberRealisticXLPlay_V1.0.safetensors
28
  - fluently/Fluently-XL-Final FluentlyXL-Final.safetensors
29
  - madebyollin/sdxl-vae-fp16-fix config.json,diffusion_pytorch_model.safetensors
 
44
  Gradio app for Stable Diffusion XL featuring:
45
 
46
  * txt2img pipeline with refiner (img2img with IP-Adapter and ControlNet coming soon)
47
+ * Compel prompt weighting and blending
 
 
48
  * Multiple samplers with Karras scheduling
49
  * DeepCache available
50
  * Real-ESRGAN upscaling
app.css CHANGED
@@ -26,7 +26,7 @@
26
  overflow-y: auto;
27
  }
28
  .gallery, .gallery .grid-wrap {
29
- height: calc(100vh - 422px);
30
  max-height: none;
31
  }
32
 
@@ -59,24 +59,6 @@
59
  #intro > div > svg:is(.dark *) {
60
  fill: #10b981 !important;
61
  }
62
- #intro nav {
63
- display: flex;
64
- column-gap: 0.5rem;
65
- }
66
- #intro nav a, #intro nav span {
67
- white-space: nowrap;
68
- font-family: monospace;
69
- }
70
- #intro nav span {
71
- font-weight: 500;
72
- color: var(--body-text-color);
73
- }
74
- #intro nav a {
75
- color: var(--body-text-color-subdued);
76
- }
77
- #intro nav a:hover {
78
- color: var(--body-text-color);
79
- }
80
 
81
  .popover {
82
  position: relative;
@@ -100,12 +82,17 @@
100
  content: 'Random prompt';
101
  }
102
  .popover#clear:hover::after {
103
- content: 'Clear gallery';
104
  }
105
  .popover#refresh:hover::after {
106
  content: var(--seed, "-1");
107
  }
108
 
 
 
 
 
 
109
  .tabs, .tabitem, .tab-nav, .tab-nav > .selected {
110
  border-width: 0px;
111
  }
 
26
  overflow-y: auto;
27
  }
28
  .gallery, .gallery .grid-wrap {
29
+ height: calc(100vh - 430px);
30
  max-height: none;
31
  }
32
 
 
59
  #intro > div > svg:is(.dark *) {
60
  fill: #10b981 !important;
61
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  .popover {
64
  position: relative;
 
82
  content: 'Random prompt';
83
  }
84
  .popover#clear:hover::after {
85
+ content: 'Clear';
86
  }
87
  .popover#refresh:hover::after {
88
  content: var(--seed, "-1");
89
  }
90
 
91
+ #settings h3 {
92
+ color: var(--block-title-text-color) !important;
93
+ margin-top: 8px !important;
94
+ }
95
+
96
  .tabs, .tabitem, .tab-nav, .tab-nav > .selected {
97
  border-width: 0px;
98
  }
app.py CHANGED
@@ -1,12 +1,19 @@
1
  import argparse
2
- import json
3
- import random
4
 
5
  import gradio as gr
6
 
7
- from lib import Config, async_call, disable_progress_bars, download_repo_files, generate, read_file
8
 
9
- # the CSS `content` attribute expects a string so we need to wrap the number in quotes
 
 
 
 
 
 
 
 
 
10
  refresh_seed_js = """
11
  () => {
12
  const n = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
@@ -16,14 +23,7 @@ refresh_seed_js = """
16
  }
17
  """
18
 
19
- seed_js = """
20
- (seed) => {
21
- const button = document.getElementById("refresh");
22
- button.style.setProperty("--seed", `"${seed}"`);
23
- return seed;
24
- }
25
- """
26
-
27
  aspect_ratio_js = """
28
  (ar, w, h) => {
29
  if (!ar) return [w, h];
@@ -32,13 +32,29 @@ aspect_ratio_js = """
32
  }
33
  """
34
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
- def random_fn():
37
- prompts = read_file("data/prompts.json")
38
- prompts = json.loads(prompts)
39
- return gr.Textbox(value=random.choice(prompts))
 
 
 
 
40
 
41
 
 
42
  async def generate_fn(*args, progress=gr.Progress(track_tqdm=True)):
43
  if len(args) > 0:
44
  prompt = args[0]
@@ -59,7 +75,7 @@ async def generate_fn(*args, progress=gr.Progress(track_tqdm=True)):
59
  progress=progress,
60
  )
61
  except RuntimeError:
62
- raise gr.Error("Please try again later")
63
 
64
  return images
65
 
@@ -78,8 +94,8 @@ with gr.Blocks(
78
  radius_size=gr.themes.sizes.radius_sm,
79
  spacing_size=gr.themes.sizes.spacing_md,
80
  # fonts
81
- font=[gr.themes.GoogleFont("Inter"), *Config.SANS_FONTS],
82
- font_mono=[gr.themes.GoogleFont("Ubuntu Mono"), *Config.MONO_FONTS],
83
  ).set(
84
  layout_gap="8px",
85
  block_shadow="0 0 #0000",
@@ -93,27 +109,24 @@ with gr.Blocks(
93
  with gr.Tabs():
94
  with gr.TabItem("🏠 Home"):
95
  with gr.Column():
96
- with gr.Group():
97
- output_images = gr.Gallery(
98
- elem_classes=["gallery"],
99
- show_share_button=False,
100
- object_fit="cover",
101
- interactive=False,
102
- show_label=False,
103
- label="Output",
104
- format="png",
105
- columns=2,
106
- )
107
- prompt = gr.Textbox(
108
- placeholder="What do you want to see?",
109
- autoscroll=False,
110
- show_label=False,
111
- label="Prompt",
112
- max_lines=3,
113
- lines=3,
114
- )
115
-
116
- # Buttons
117
  with gr.Row():
118
  generate_btn = gr.Button("Generate", variant="primary")
119
  random_btn = gr.Button(
@@ -139,145 +152,130 @@ with gr.Blocks(
139
  value="🗑️",
140
  )
141
 
142
- with gr.TabItem("⚙️ Menu"):
143
- with gr.Group():
 
 
144
  negative_prompt = gr.Textbox(
145
- value="nsfw+",
146
  label="Negative Prompt",
147
- lines=2,
148
  )
149
 
150
- with gr.Row():
151
- model = gr.Dropdown(
152
- choices=Config.MODELS,
153
- filterable=False,
154
- value=Config.MODEL,
155
- label="Model",
156
- min_width=240,
157
- )
158
- scheduler = gr.Dropdown(
159
- choices=Config.SCHEDULERS.keys(),
160
- value=Config.SCHEDULER,
161
- elem_id="scheduler",
162
- label="Scheduler",
163
- filterable=False,
164
- )
165
-
166
- with gr.Row():
167
- styles = json.loads(read_file("data/styles.json"))
168
- style_ids = list(styles.keys())
169
- style_ids = [sid for sid in style_ids if not sid.startswith("_")]
170
- style = gr.Dropdown(
171
- value=Config.STYLE,
172
- label="Style",
173
- min_width=240,
174
- choices=[("None", None)] + [(styles[sid]["name"], sid) for sid in style_ids],
175
- )
176
-
177
- with gr.Row():
178
- guidance_scale = gr.Slider(
179
- value=Config.GUIDANCE_SCALE,
180
- label="Guidance Scale",
181
- minimum=1.0,
182
- maximum=15.0,
183
- step=0.1,
184
- )
185
- inference_steps = gr.Slider(
186
- value=Config.INFERENCE_STEPS,
187
- label="Inference Steps",
188
- minimum=1,
189
- maximum=50,
190
- step=1,
191
- )
192
- deepcache_interval = gr.Slider(
193
- value=Config.DEEPCACHE_INTERVAL,
194
- label="DeepCache",
195
- minimum=1,
196
- maximum=4,
197
- step=1,
198
- )
199
-
200
- with gr.Row():
201
- width = gr.Slider(
202
- value=Config.WIDTH,
203
- label="Width",
204
- minimum=512,
205
- maximum=1536,
206
- step=64,
207
- )
208
- height = gr.Slider(
209
- value=Config.HEIGHT,
210
- label="Height",
211
- minimum=512,
212
- maximum=1536,
213
- step=64,
214
- )
215
- aspect_ratio = gr.Dropdown(
216
- value=f"{Config.WIDTH},{Config.HEIGHT}",
217
- label="Aspect Ratio",
218
- filterable=False,
219
- choices=[
220
- ("Custom", None),
221
- ("4:7 (768x1344)", "768,1344"),
222
- ("7:9 (896x1152)", "896,1152"),
223
- ("1:1 (1024x1024)", "1024,1024"),
224
- ("9:7 (1152x896)", "1152,896"),
225
- ("7:4 (1344x768)", "1344,768"),
226
- ],
227
- )
228
 
229
- with gr.Row():
230
- file_format = gr.Dropdown(
231
- choices=["png", "jpeg", "webp"],
232
- label="File Format",
233
- filterable=False,
234
- value="png",
235
- )
236
- num_images = gr.Dropdown(
237
- choices=list(range(1, 5)),
238
- value=Config.NUM_IMAGES,
239
- filterable=False,
240
- label="Images",
241
- )
242
- scale = gr.Dropdown(
243
- choices=[(f"{s}x", s) for s in Config.SCALES],
244
- filterable=False,
245
- value=Config.SCALE,
246
- label="Scale",
247
- )
248
- seed = gr.Number(
249
- value=Config.SEED,
250
- label="Seed",
251
- minimum=-1,
252
- maximum=(2**64) - 1,
253
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
- with gr.Row():
256
- use_karras = gr.Checkbox(
257
- elem_classes=["checkbox"],
258
- label="Karras σ",
259
- value=True,
260
- )
261
- use_refiner = gr.Checkbox(
262
- elem_classes=["checkbox"],
263
- label="Refiner",
264
- value=False,
265
- )
266
 
267
- random_btn.click(random_fn, inputs=[], outputs=[prompt], show_api=False)
 
268
 
 
269
  refresh_btn.click(None, inputs=[], outputs=[seed], js=refresh_seed_js)
270
 
 
271
  seed.change(None, inputs=[seed], outputs=[], js=seed_js)
272
 
273
- file_format.change(
274
- lambda f: gr.Gallery(format=f),
275
- inputs=[file_format],
276
- outputs=[output_images],
277
- show_api=False,
278
- )
279
-
280
- # input events are only user input; change events are both user and programmatic
281
  aspect_ratio.input(
282
  None,
283
  inputs=[aspect_ratio, width, height],
@@ -285,15 +283,16 @@ with gr.Blocks(
285
  js=aspect_ratio_js,
286
  )
287
 
288
- # show "Custom" aspect ratio when manually changing width or height
289
  gr.on(
290
  triggers=[width.input, height.input],
291
  fn=None,
292
- inputs=[],
293
  outputs=[aspect_ratio],
294
- js="() => { return null; }",
295
  )
296
 
 
297
  gr.on(
298
  triggers=[generate_btn.click, prompt.submit],
299
  fn=generate_fn,
@@ -302,7 +301,6 @@ with gr.Blocks(
302
  inputs=[
303
  prompt,
304
  negative_prompt,
305
- style,
306
  seed,
307
  model,
308
  scheduler,
 
1
  import argparse
 
 
2
 
3
  import gradio as gr
4
 
5
+ from lib import Config, async_call, disable_progress_bars, download_repo_files, generate, read_file, read_json
6
 
7
+ # Update refresh button hover text
8
+ seed_js = """
9
+ (seed) => {
10
+ const button = document.getElementById("refresh");
11
+ button.style.setProperty("--seed", `"${seed}"`);
12
+ return seed;
13
+ }
14
+ """
15
+
16
+ # The CSS `content` attribute expects a string so we need to wrap the number in quotes
17
  refresh_seed_js = """
18
  () => {
19
  const n = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
 
23
  }
24
  """
25
 
26
+ # Update width and height on aspect ratio change
 
 
 
 
 
 
 
27
  aspect_ratio_js = """
28
  (ar, w, h) => {
29
  if (!ar) return [w, h];
 
32
  }
33
  """
34
 
35
+ # Show "Custom" aspect ratio when manually changing width or height, or one of the predefined ones
36
+ custom_aspect_ratio_js = """
37
+ (w, h) => {
38
+ if (w === 768 && h === 1344) return "768,1344";
39
+ if (w === 896 && h === 1152) return "896,1152";
40
+ if (w === 1024 && h === 1024) return "1024,1024";
41
+ if (w === 1152 && h === 896) return "1152,896";
42
+ if (w === 1344 && h === 768) return "1344,768";
43
+ return null;
44
+ }
45
+ """
46
 
47
+ # Inject prompts into random function
48
+ random_prompt_js = f"""
49
+ (prompt) => {{
50
+ const prompts = {read_json("data/prompts.json")};
51
+ const filtered = prompts.filter(p => p !== prompt);
52
+ return filtered[Math.floor(Math.random() * filtered.length)];
53
+ }}
54
+ """
55
 
56
 
57
+ # Transform the raw inputs before generation
58
  async def generate_fn(*args, progress=gr.Progress(track_tqdm=True)):
59
  if len(args) > 0:
60
  prompt = args[0]
 
75
  progress=progress,
76
  )
77
  except RuntimeError:
78
+ raise gr.Error("Error: Please try again")
79
 
80
  return images
81
 
 
94
  radius_size=gr.themes.sizes.radius_sm,
95
  spacing_size=gr.themes.sizes.spacing_md,
96
  # fonts
97
+ font=[gr.themes.GoogleFont("Inter"), "sans-serif"],
98
+ font_mono=[gr.themes.GoogleFont("Ubuntu Mono"), "monospace"],
99
  ).set(
100
  layout_gap="8px",
101
  block_shadow="0 0 #0000",
 
109
  with gr.Tabs():
110
  with gr.TabItem("🏠 Home"):
111
  with gr.Column():
112
+ output_images = gr.Gallery(
113
+ elem_classes=["gallery"],
114
+ show_share_button=False,
115
+ object_fit="cover",
116
+ interactive=False,
117
+ show_label=False,
118
+ label="Output",
119
+ format="png",
120
+ columns=2,
121
+ )
122
+ prompt = gr.Textbox(
123
+ placeholder="What do you want to see?",
124
+ autoscroll=False,
125
+ show_label=False,
126
+ label="Prompt",
127
+ max_lines=3,
128
+ lines=3,
129
+ )
 
 
 
130
  with gr.Row():
131
  generate_btn = gr.Button("Generate", variant="primary")
132
  random_btn = gr.Button(
 
152
  value="🗑️",
153
  )
154
 
155
+ with gr.TabItem("⚙️ Settings", elem_id="settings"):
156
+ # Prompt settings
157
+ gr.HTML("<h3>Prompt</h3>")
158
+ with gr.Row():
159
  negative_prompt = gr.Textbox(
160
+ value="nsfw",
161
  label="Negative Prompt",
162
+ lines=1,
163
  )
164
 
165
+ # Model settings
166
+ gr.HTML("<h3>Settings</h3>")
167
+ with gr.Row():
168
+ model = gr.Dropdown(
169
+ choices=Config.MODELS,
170
+ value=Config.MODEL,
171
+ filterable=False,
172
+ label="Checkpoint",
173
+ min_width=240,
174
+ )
175
+ scheduler = gr.Dropdown(
176
+ choices=Config.SCHEDULERS.keys(),
177
+ value=Config.SCHEDULER,
178
+ elem_id="scheduler",
179
+ label="Scheduler",
180
+ filterable=False,
181
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
 
183
+ # Generation settings
184
+ gr.HTML("<h3>Generation</h3>")
185
+ with gr.Row():
186
+ guidance_scale = gr.Slider(
187
+ value=Config.GUIDANCE_SCALE,
188
+ label="Guidance Scale",
189
+ minimum=1.0,
190
+ maximum=15.0,
191
+ step=0.1,
192
+ )
193
+ inference_steps = gr.Slider(
194
+ value=Config.INFERENCE_STEPS,
195
+ label="Inference Steps",
196
+ minimum=1,
197
+ maximum=50,
198
+ step=1,
199
+ )
200
+ deepcache_interval = gr.Slider(
201
+ value=Config.DEEPCACHE_INTERVAL,
202
+ label="DeepCache",
203
+ minimum=1,
204
+ maximum=4,
205
+ step=1,
206
+ )
207
+ with gr.Row():
208
+ width = gr.Slider(
209
+ value=Config.WIDTH,
210
+ label="Width",
211
+ minimum=512,
212
+ maximum=1536,
213
+ step=64,
214
+ )
215
+ height = gr.Slider(
216
+ value=Config.HEIGHT,
217
+ label="Height",
218
+ minimum=512,
219
+ maximum=1536,
220
+ step=64,
221
+ )
222
+ aspect_ratio = gr.Dropdown(
223
+ value=f"{Config.WIDTH},{Config.HEIGHT}",
224
+ label="Aspect Ratio",
225
+ filterable=False,
226
+ choices=[
227
+ ("Custom", None),
228
+ ("4:7 (768x1344)", "768,1344"),
229
+ ("7:9 (896x1152)", "896,1152"),
230
+ ("1:1 (1024x1024)", "1024,1024"),
231
+ ("9:7 (1152x896)", "1152,896"),
232
+ ("7:4 (1344x768)", "1344,768"),
233
+ ],
234
+ )
235
+ with gr.Row():
236
+ num_images = gr.Dropdown(
237
+ choices=list(range(1, 5)),
238
+ value=Config.NUM_IMAGES,
239
+ filterable=False,
240
+ label="Images",
241
+ )
242
+ scale = gr.Dropdown(
243
+ choices=[(f"{s}x", s) for s in Config.SCALES],
244
+ filterable=False,
245
+ value=Config.SCALE,
246
+ label="Scale",
247
+ )
248
+ seed = gr.Number(
249
+ value=Config.SEED,
250
+ label="Seed",
251
+ minimum=-1,
252
+ maximum=(2**64) - 1,
253
+ )
254
+ with gr.Row():
255
+ use_karras = gr.Checkbox(
256
+ elem_classes=["checkbox"],
257
+ label="Karras σ",
258
+ value=True,
259
+ )
260
+ use_refiner = gr.Checkbox(
261
+ elem_classes=["checkbox"],
262
+ label="Refiner",
263
+ value=False,
264
+ )
265
 
266
+ with gr.TabItem("ℹ️ Info"):
267
+ gr.Markdown(read_file("DOCS.md"))
 
 
 
 
 
 
 
 
 
268
 
269
+ # Random prompt on click
270
+ random_btn.click(None, inputs=[prompt], outputs=[prompt], js=random_prompt_js)
271
 
272
+ # Update seed on click
273
  refresh_btn.click(None, inputs=[], outputs=[seed], js=refresh_seed_js)
274
 
275
+ # Update seed button hover text
276
  seed.change(None, inputs=[seed], outputs=[], js=seed_js)
277
 
278
+ # Update width and height on aspect ratio change
 
 
 
 
 
 
 
279
  aspect_ratio.input(
280
  None,
281
  inputs=[aspect_ratio, width, height],
 
283
  js=aspect_ratio_js,
284
  )
285
 
286
+ # Show "Custom" aspect ratio when manually changing width or height
287
  gr.on(
288
  triggers=[width.input, height.input],
289
  fn=None,
290
+ inputs=[width, height],
291
  outputs=[aspect_ratio],
292
+ js=custom_aspect_ratio_js,
293
  )
294
 
295
+ # Generate images
296
  gr.on(
297
  triggers=[generate_btn.click, prompt.submit],
298
  fn=generate_fn,
 
301
  inputs=[
302
  prompt,
303
  negative_prompt,
 
304
  seed,
305
  model,
306
  scheduler,
data/prompts.json CHANGED
@@ -1,48 +1,25 @@
1
  [
2
- "stunning sunset over a futuristic city, with towering skyscrapers, dramatic clouds, golden-hour lighting, atmospheric",
3
- "epic dragon perched atop a cliff, detailed claws, fiery landscape, plumes of smoke",
4
- "serene beach with crystal clear water and white sand, tropical palm trees swaying in the breeze, paradise",
5
- "post-apocalyptic wasteland with rusted and abandoned vehicles, dust storms and towering dust clouds in the distance, dark, gritty, dramatic",
6
- "mysterious underwater world with vibrant coral and a school of colorful fish, sun beams shining through the water, magical, enchanting, otherworldly",
7
- "snowy winter wonderland with a lone cabin in the distance, surrounded by frosty trees and fresh snowfall, peaceful, serene",
8
- "mysterious and abandoned temple in the jungle, surrounded by lush vegetation and tall trees, ancient, atmospheric",
9
- "vibrant and bustling city street, busy traffic, bright lights, fast-paced, intense",
10
- "gothic cathedral on a stormy night, lightning illuminating the sky, rain pouring down, epic, dramatic, atmospheric",
11
- "fantasy castle on a hilltop, surrounded by rolling hills, breathtaking sunset, magical, enchanting, charming, romantic",
12
- "vast desert with sand dunes, a lone oasis in the distance, hot sun, blue sky, peaceful, serene",
13
- "beautiful waterfall in a lush jungle, sunlight shining through the trees, tropical, peaceful, serene",
14
- "enchanted forest with a babbling brook, glowing fireflies, towering trees, shrouded in mist, magical, ethereal, dreamlike, fantasy",
15
- "volcanic island with a boiling crater, clouds of ash rise from the peak, intense, dramatic, atmospheric",
16
- "lonely lighthouse on a rocky cliff overlooking the sea, stormy sky, crashing waves, ominous, intense, dramatic, atmospheric",
17
- "mysterious underground cave with glowing crystals, underground stream, dark, mysterious, otherworldly",
18
- "glowing aurora borealis over a frozen lake, with towering mountains in the distance, ethereal, magical, peaceful, serene",
19
- "vibrant flower field in the spring, surrounded by rolling hills, brilliant blue sky, full-bloom, colorful, peaceful, serene",
20
- "tranquil pond surrounded by tall trees, with a beautiful lily pad garden and calm reflection of the sky, peaceful, serene",
21
- "stunning sunset over an ocean horizon, orange and pink hues spread across the sky, peaceful, serene",
22
- "abandoned temple in a mountain range, surrounded by misty clouds and tall peaks, mysterious, ancient",
23
- "steam locomotive in a snowy mountain range, surrounded by tall peaks, frosted trees, nostalgic",
24
- "radiant nebula, star clusters and gas clouds shining brightly, celestial, otherworldly, abstract, space art",
25
- "beautiful Santorini island, iconic white buildings, pristine beach, Mediterranean, charming, romantic",
26
- "breathtaking Grand Canyon, vast, awe-inspiring, otherworldly, iconic, historic landmark",
27
- "breathtaking Machu Picchu, set against a backdrop of towering mountains, iconic, historic landmark",
28
- "iconic New York City skyline, with towering skyscrapers, golden-hour lighting, dramatic, atmospheric",
29
- "iconic Great Wall of China, stretching along the countryside, historic landmark",
30
- "iconic Sydney Opera House, with the harbor and cityscape in the background, stunning, historic landmark",
31
- "iconic Taj Mahal, set against a backdrop of lush greenery, stunning, historic landmark",
32
- "bowl of steaming hot ramen with a sliced egg, thin slices of meat, green onions, noodles, chopsticks, solo, minimal",
33
- "large pizza with melted cheese, seared pepperoni, crispy crust, solo, minimal",
34
- "sizzling hot sirloin steak with a perfect crust, seared to perfection, served with a side of roasted vegetables and mashed potatoes, solo, minimal",
35
- "wedding cake, white frosting, colorful accent flowers, fresh berry garnish, tiers, layers, elegant, minimal",
36
- "baked salmon fillet with a perfectly crispy skin and flaky flesh, side of steamed vegetables and quinoa, healthy, fresh, solo, minimal",
37
- "steaming bowl of hearty chili with tender chunks of beef, rich tomato sauce, topped with grated cheddar cheese and green onions, solo, minimal",
38
- "platter of sushi rolls, tuna, salmon, california maki, rainbow, colorful, beautiful arrangement, solo, minimal",
39
- "stuffed bell pepper filled with browned ground beef, rice, melted cheese, parsley garnish, solo, minimal",
40
- "pair of tacos filled with shredded chicken, red onions, cilantro, a drizzle of cream, white plate, blue tablecloth, solo, minimal",
41
- "contemporary living room, floor-to-ceiling windows, neutral color palette, minimalistic design, modern furniture, wall-mounted television, ceiling fan, recessed lighting, open floor plan",
42
- "rustic kitchen with reclaimed wood cabinetry, large farmhouse sink, industrial lighting fixtures, open shelving, cast iron cookware, exposed brick wall",
43
- "luxurious bathroom with freestanding bathtub, marble tiles, brass fixtures, double-sink floating vanity, spa-like atmosphere",
44
- "cozy bedroom with a four-poster bed, decorative throw pillows, plush bedding, single large window with natural light, statement wallpaper, relaxing atmosphere",
45
- "formula one race car, aerodynamic design, captured in a high speed motion blur, shallow depth-of-field, dramatic lighting, epic, intense",
46
- "luxury supercar with aerodynamic curves, high-key lighting, depth-of-field, exotic",
47
- "stunning yacht with sleek lines, golden-hour lighting, depth-of-field, breathtaking"
48
  ]
 
1
  [
2
+ "portrait of a blonde woman, detailed facial features, soft natural lighting, bokeh, professional photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
3
+ "portrait of a young adult woman, freckled complexion, vibrant red hair, smiling, under tree, golden hour lighting, professional photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
4
+ "portrait of an elderly woman, weathered features, gentle smile, natural window lighting, professional photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
5
+ "headshot of a middle-aged man, salt and pepper hair, confident expression, studio lighting, neutral backdrop, professional photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
6
+
7
+ "portrait of a majestic Norwegian forest cat, soft fur detail, green eyes, natural lighting, professional photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
8
+ "portrait of a British shorthair cat, yellow eyes, gentle expression, soft lighting, bokeh, professional photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
9
+ "adorable Siamese kitten, blue eyes, soft natural lighting, shallow depth of field, professional photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
10
+ "portrait of a regal German shepherd, alert ears, noble expression, natural background, golden hour lighting, bokeh, professional photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
11
+ "Welsh corgi in a colorful garden, cheerful expression, daylight, shallow depth of field, professional photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
12
+
13
+ "Porsche 911 Turbo (991), front 3/4 view, motion blur, dramatic lighting, professional photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
14
+ "BMW E92 M3 on mountain road, side profile, motion blur, daylight, professional photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
15
+
16
+ "tropical beach at sunset, light sand, azure water, swaying palm trees, scattered clouds, distant volcano, ultra wide angle lens, HDR photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
17
+ "lighthouse on rocky coast at dawn, crashing waves, pastel sky, cumulus clouds, soft morning light, HDR photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
18
+ "California vineyard at sunset, rows of grapevines stretching to horizon, golden hour lighting, rolling hills, scattered oak trees, ultra wide angle lens, HDR photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
19
+ "mountain stream in autumn, moss-covered rocks, fallen maple leaves, dappled sunlight through canopy, HDR photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
20
+ "tranquil alpine lake at night, snow-capped mountain range in distance, scattered evergreens along shoreline, aurora borealis in sky, HDR photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
21
+ "desert ranch house at dusk, saguaro cactus, purple mountain backdrop, last rays of sunlight, HDR photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
22
+ "Japanese pagoda between cherry blossom trees, misty mountain backdrop, golden hour lighting, HDR photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
23
+ "red Dutch barn, colorful tulip rows in front, low evening sun, scattered clouds, HDR photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd",
24
+ "lavender field in Provence, ancient stone farmhouse, sunset lighting, scattered clouds, distant mountains, HDR photography, breathtaking, masterpiece, highly detailed, best quality, sharp focus, 8k, uhd"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  ]
data/styles.json DELETED
@@ -1,136 +0,0 @@
1
- {
2
- "_base": {
3
- "positive": "good, perfect, accurate, precise, professional, highly detailed, best quality, masterpiece",
4
- "negative": "watermark, trademark, signature, autograph, artifacts, deformed, mutated, bad, ugly, unattractive, noisy, grainy, blurry, distorted, oversaturated, undersaturated, overexposed, underexposed, amateur, sloppy, cluttered, low detail, worst quality"
5
- },
6
- "abstract": {
7
- "name": "Abstract",
8
- "positive": "({prompt}), in an abstract art style, non-representational colors and shapes, expressive, imaginative, vibrant, {_base}",
9
- "negative": "({prompt}), discrete, objective, realism, photographic, monochrome, muted, {_base}"
10
- },
11
- "anime_josei": {
12
- "name": "Anime: Josei",
13
- "positive": "({prompt}), in a josei anime style, inspired by Paradise Kiss, by Ai Yazawa, manga, mature, emotional, sophisticated, soft colors, refined lines, {_base}",
14
- "negative": "({prompt}), shonen, shoujo, seinen, dark, gritty, realism, photographic, {_base}"
15
- },
16
- "anime_seinen": {
17
- "name": "Anime: Seinen",
18
- "positive": "({prompt}), in a seinen anime style, inspired by Ghost in the Shell, by Masamune Shirow, manga, adult, mature, dark, gritty, intricate design, dramatic lighting, high contrast, {_base}",
19
- "negative": "({prompt}), shonen, shoujo, josei, realism, photographic, dull, plain, low contrast, {_base}"
20
- },
21
- "anime_shojo": {
22
- "name": "Anime: Shoujo",
23
- "positive": "({prompt}), in a shoujo anime style, manga, romantic, emotional, pastel colors, soft lines, {_base}",
24
- "negative": "({prompt}), shonen, seinen, josei, dark, gritty, realism, photographic, {_base}"
25
- },
26
- "anime_shonen": {
27
- "name": "Anime: Shonen",
28
- "positive": "({prompt}), in a shonen anime style, manga, action, adventure, heroic, youthful, vibrant, high contrast, {_base}",
29
- "negative": "({prompt}), shoujo, seinen, josei, realism, photographic, dull, plain, monochrome, muted, low contrast, {_base}"
30
- },
31
- "art_deco": {
32
- "name": "Art Deco",
33
- "positive": "({prompt}), in an art deco style, inspired by Tamara de Lempicka, geometric shapes, bold colors, luxurious, elegant, sleek, streamlined, symmetrical, vibrant, high contrast, {_base}",
34
- "negative": "({prompt}), realism, photographic, asymmetrical, monochrome, muted, low contrast, {_base}"
35
- },
36
- "biomechanical": {
37
- "name": "Biomechanical",
38
- "positive": "({prompt}), in a biomechanical style, organic and mechanical, flesh and metal, cyborg, cybernetic, intricate design, futuristic, sci-fi, {_base}",
39
- "negative": "({prompt}), natural, rustic, primitive, medieval, {_base}"
40
- },
41
- "cubism": {
42
- "name": "Cubism",
43
- "positive": "({prompt}), in a cubist style, inspired by Picasso, fragmented shapes and planes, abstract forms, collage, {_base}",
44
- "negative": "({prompt}), discrete, objective, {_base}"
45
- },
46
- "cyberpunk": {
47
- "name": "Cyberpunk",
48
- "positive": "({prompt}), in a cyberpunk style, 2077, synthwave, neon, digital, high-tech, futuristic, dystopian, vibrant, high contrast, {_base}",
49
- "negative": "({prompt}), rustic, primitive, medieval, monochrome, muted, low contrast, {_base}"
50
- },
51
- "enhance": {
52
- "name": "Enhance",
53
- "positive": "({prompt}), {_base}",
54
- "negative": "({prompt}), {_base}"
55
- },
56
- "expressionism": {
57
- "name": "Expressionism",
58
- "positive": "({prompt}), in an expressionist style, energetic brushwork, bold colors, abstract forms, vibrant, expressive, imaginative, high contrast, {_base}",
59
- "negative": "({prompt}), discrete, objective, realism, photographic, dull, plain, monochrome, muted, low contrast, {_base}"
60
- },
61
- "fantasy": {
62
- "name": "Fantasy",
63
- "positive": "({prompt}), in a fantasy style, digital concept art, by Greg Rutkowski, trending on ArtStation, magical, enchanting, ethereal, dreamlike, graphic, illustration, high contrast, {_base}",
64
- "negative": "({prompt}), realism, photographic, ordinary, mundane, monochrome, muted, low contrast, {_base}"
65
- },
66
- "graffiti": {
67
- "name": "Graffiti",
68
- "positive": "({prompt}), in a graffiti style, street art, creative composition, dynamic lines, spray paint, hip-hop, stylized, bold, vibrant, urban, mural, high contrast, {_base}",
69
- "negative": "({prompt}), dull, plain, monochrome, muted, low contrast, {_base}"
70
- },
71
- "line_art": {
72
- "name": "Line Art",
73
- "positive": "({prompt}), in a line art drawing style, graphic, illustration, sleek, streamlined, centered composition, solo subject, isolated subject, white background, minimalist arrangement, {_base}",
74
- "negative": "({prompt}), off-center, oil, acrylic, watercolor, {_base}"
75
- },
76
- "papercraft": {
77
- "name": "Papercraft",
78
- "positive": "({prompt}), as a papercraft model, Kirigami style, folded paper, papercut, sharp edges, intricate design, 3d, layered, textural, color block, centered composition, minimalist arrangement, {_base}",
79
- "negative": "({prompt}), 2d, flat, {_base}"
80
- },
81
- "photography_food": {
82
- "name": "Photography: Food",
83
- "positive": "({prompt}), food photography style, fresh ingredients, delicious, culinary, real, authentic, macro details, soft natural lighting, high resolution, uhd, centered composition, minimalist arrangement, {_base}",
84
- "negative": "({prompt}), unappetizing, fake, artificial, low resolution, {_base}"
85
- },
86
- "photography_hdr": {
87
- "name": "Photography: HDR",
88
- "positive": "({prompt}), breathtaking HDR photography, high dynamic range, vivid colors, shadows and highlights, dramatic lighting, high contrast, high resolution, uhd, {_base}",
89
- "negative": "({prompt}), flat colors, plain, dull, low dynamic range, low contrast, low resolution, {_base}"
90
- },
91
- "photography_iphone": {
92
- "name": "Photography: iPhone",
93
- "positive": "({prompt}), taken by iPhone ProRAW camera, XDR, Retina, depth-of-field, vivid colors, dynamic range, dramatic lighting, real, authentic, high contrast, high resolution, uhd, {_base}",
94
- "negative": "({prompt}), shallow depth-of-field, bokeh, fake, artificial, low contrast, low resolution, {_base}"
95
- },
96
- "photography_iphone_portrait": {
97
- "name": "Photography: iPhone Portrait",
98
- "positive": "({prompt}), taken by iPhone Portrait Mode, XDR, Retina, shallow depth-of-field, bokeh, vivid colors, dramatic lighting, real, authentic, high contrast, high resolution, uhd, {_base}",
99
- "negative": "({prompt}), fake, artificial, low contrast, low resolution, {_base}"
100
- },
101
- "photography_real_estate": {
102
- "name": "Photography: Real Estate",
103
- "positive": "({prompt}), real estate photography style, on Zillow, inviting, staged, well-lit, real, authentic, high resolution, uhd, {_base}",
104
- "negative": "({prompt}), dark, fake, artificial, low resolution, {_base}"
105
- },
106
- "photography_street": {
107
- "name": "Photography: Street",
108
- "positive": "({prompt}), street photography style, taken on Fujifilm X100V, f2 aperture, 35mm, RAW format, candid, authentic, gritty, urban, high contrast, high resolution, uhd, {_base}",
109
- "negative": "({prompt}), staged, fake, artificial, low contrast, low resolution, {_base}"
110
- },
111
- "pointillism": {
112
- "name": "Pointillism",
113
- "positive": "({prompt}), pointillism style, composed of small dots, inspired by Georges Seurat, intricate design, vibrant, {_base}",
114
- "negative": "({prompt}), line drawing, smooth shading, wide color gamut, dull, plain, monochrome, muted, {_base}"
115
- },
116
- "pop_art": {
117
- "name": "Pop Art",
118
- "positive": "({prompt}), in a pop art style, inspired by Warhol, bright colors, bold outlines, popular culture, kitsch, vibrant, high contrast, {_base}",
119
- "negative": "({prompt}), discrete, objective, dull, plain, monochrome, muted, low contrast, {_base}"
120
- },
121
- "render": {
122
- "name": "Render",
123
- "positive": "({prompt}), Octane render, Unreal Engine, volumetric lighting, ray tracing, ambient occlusion, subsurface scattering, high resolution, uhd, {_base}",
124
- "negative": "({prompt}), glitch, error, painting, sketch, low resolution, {_base}"
125
- },
126
- "vaporwave": {
127
- "name": "Vaporwave",
128
- "positive": "({prompt}), in a vaporwave style, retro aesthetic, neon colors, vibrant, high contrast, {_base}",
129
- "negative": "({prompt}), dark, monochrome, muted, low contrast, {_base}"
130
- },
131
- "watercolor": {
132
- "name": "Watercolor",
133
- "positive": "({prompt}), painted with watercolors, colorful, painterly, artistic, fluid, {_base}",
134
- "negative": "({prompt}), realism, photographic, oil, acrylic, digital, {_base}"
135
- }
136
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lib/__init__.py CHANGED
@@ -1,29 +1,19 @@
1
  from .config import Config
2
  from .inference import generate
3
- from .loader import Loader
4
- from .upscaler import RealESRGAN
5
  from .utils import (
6
  async_call,
7
  disable_progress_bars,
8
- download_civit_file,
9
  download_repo_files,
10
- enable_progress_bars,
11
- load_json,
12
  read_file,
13
- timer,
14
  )
15
 
16
  __all__ = [
17
  "Config",
18
- "Loader",
19
- "RealESRGAN",
20
  "async_call",
21
  "disable_progress_bars",
22
- "download_civit_file",
23
  "download_repo_files",
24
- "enable_progress_bars",
25
  "generate",
26
- "load_json",
27
  "read_file",
28
- "timer",
29
  ]
 
1
  from .config import Config
2
  from .inference import generate
 
 
3
  from .utils import (
4
  async_call,
5
  disable_progress_bars,
 
6
  download_repo_files,
 
 
7
  read_file,
8
+ read_json,
9
  )
10
 
11
  __all__ = [
12
  "Config",
 
 
13
  "async_call",
14
  "disable_progress_bars",
 
15
  "download_repo_files",
 
16
  "generate",
 
17
  "read_file",
18
+ "read_json",
19
  ]
lib/config.py CHANGED
@@ -16,10 +16,10 @@ from diffusers import (
16
  from diffusers.utils import logging as diffusers_logging
17
  from transformers import logging as transformers_logging
18
 
19
- # improved GPU handling and progress bars; set before importing spaces
20
  os.environ["ZEROGPU_V2"] = "1"
21
 
22
- # use Rust downloader
23
  if find_spec("hf_transfer"):
24
  os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1"
25
 
@@ -29,6 +29,7 @@ filterwarnings("ignore", category=FutureWarning, module="transformers")
29
  diffusers_logging.set_verbosity_error()
30
  transformers_logging.set_verbosity_error()
31
 
 
32
  _sdxl_refiner_files = [
33
  "scheduler/scheduler_config.json",
34
  "text_encoder_2/config.json",
@@ -44,6 +45,7 @@ _sdxl_refiner_files = [
44
  "model_index.json",
45
  ]
46
 
 
47
  _sdxl_files = [
48
  *_sdxl_refiner_files,
49
  "text_encoder/config.json",
@@ -54,39 +56,30 @@ _sdxl_files = [
54
  "tokenizer/vocab.json",
55
  ]
56
 
 
57
  Config = SimpleNamespace(
58
  HF_TOKEN=os.environ.get("HF_TOKEN", None),
59
- CIVIT_TOKEN=os.environ.get("CIVIT_TOKEN", None),
60
  ZERO_GPU=import_module("spaces").config.Config.zero_gpu,
61
- MONO_FONTS=["monospace"],
62
- SANS_FONTS=[
63
- "sans-serif",
64
- "Apple Color Emoji",
65
- "Segoe UI Emoji",
66
- "Segoe UI Symbol",
67
- "Noto Color Emoji",
68
- ],
69
- PIPELINES={
70
- "txt2img": StableDiffusionXLPipeline,
71
- "img2img": StableDiffusionXLImg2ImgPipeline,
72
- },
73
  HF_MODELS={
74
  "segmind/Segmind-Vega": [*_sdxl_files],
75
  "stabilityai/stable-diffusion-xl-base-1.0": [*_sdxl_files, "vae_1_0/config.json"],
76
  "stabilityai/stable-diffusion-xl-refiner-1.0": [*_sdxl_refiner_files],
77
  },
 
 
 
 
78
  MODEL="segmind/Segmind-Vega",
79
  MODELS=[
80
- "cagliostrolab/animagine-xl-3.1",
81
  "cyberdelia/CyberRealsticXL",
82
  "fluently/Fluently-XL-Final",
83
  "segmind/Segmind-Vega",
84
  "SG161222/RealVisXL_V5.0",
85
  "stabilityai/stable-diffusion-xl-base-1.0",
86
  ],
 
87
  MODEL_CHECKPOINTS={
88
- # keep keys lowercase
89
- "cagliostrolab/animagine-xl-3.1": "animagine-xl-3.1.safetensors",
90
  "cyberdelia/cyberrealsticxl": "CyberRealisticXLPlay_V1.0.safetensors", # typo in "realistic"
91
  "fluently/fluently-xl-final": "FluentlyXL-Final.safetensors",
92
  "sg161222/realvisxl_v5.0": "RealVisXL_V5.0_fp16.safetensors",
@@ -101,12 +94,11 @@ Config = SimpleNamespace(
101
  "Euler": EulerDiscreteScheduler,
102
  "Euler a": EulerAncestralDiscreteScheduler,
103
  },
104
- STYLE="enhance",
105
  WIDTH=1024,
106
  HEIGHT=1024,
107
  NUM_IMAGES=1,
108
  SEED=-1,
109
- GUIDANCE_SCALE=7.5,
110
  INFERENCE_STEPS=40,
111
  DEEPCACHE_INTERVAL=1,
112
  SCALE=1,
 
16
  from diffusers.utils import logging as diffusers_logging
17
  from transformers import logging as transformers_logging
18
 
19
+ # Improved GPU handling and progress bars; set before importing spaces
20
  os.environ["ZEROGPU_V2"] = "1"
21
 
22
+ # Use Rust-based downloader; errors if enabled and not installed
23
  if find_spec("hf_transfer"):
24
  os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1"
25
 
 
29
  diffusers_logging.set_verbosity_error()
30
  transformers_logging.set_verbosity_error()
31
 
32
+ # Standard refiner structure
33
  _sdxl_refiner_files = [
34
  "scheduler/scheduler_config.json",
35
  "text_encoder_2/config.json",
 
45
  "model_index.json",
46
  ]
47
 
48
+ # Standard SDXL structure
49
  _sdxl_files = [
50
  *_sdxl_refiner_files,
51
  "text_encoder/config.json",
 
56
  "tokenizer/vocab.json",
57
  ]
58
 
59
+ # Using namespace instead of dataclass for simplicity
60
  Config = SimpleNamespace(
61
  HF_TOKEN=os.environ.get("HF_TOKEN", None),
 
62
  ZERO_GPU=import_module("spaces").config.Config.zero_gpu,
 
 
 
 
 
 
 
 
 
 
 
 
63
  HF_MODELS={
64
  "segmind/Segmind-Vega": [*_sdxl_files],
65
  "stabilityai/stable-diffusion-xl-base-1.0": [*_sdxl_files, "vae_1_0/config.json"],
66
  "stabilityai/stable-diffusion-xl-refiner-1.0": [*_sdxl_refiner_files],
67
  },
68
+ PIPELINES={
69
+ "txt2img": StableDiffusionXLPipeline,
70
+ "img2img": StableDiffusionXLImg2ImgPipeline,
71
+ },
72
  MODEL="segmind/Segmind-Vega",
73
  MODELS=[
 
74
  "cyberdelia/CyberRealsticXL",
75
  "fluently/Fluently-XL-Final",
76
  "segmind/Segmind-Vega",
77
  "SG161222/RealVisXL_V5.0",
78
  "stabilityai/stable-diffusion-xl-base-1.0",
79
  ],
80
+ # Single-file model weights
81
  MODEL_CHECKPOINTS={
82
+ # keep keys lowercase for case-insensitive matching in the loader
 
83
  "cyberdelia/cyberrealsticxl": "CyberRealisticXLPlay_V1.0.safetensors", # typo in "realistic"
84
  "fluently/fluently-xl-final": "FluentlyXL-Final.safetensors",
85
  "sg161222/realvisxl_v5.0": "RealVisXL_V5.0_fp16.safetensors",
 
94
  "Euler": EulerDiscreteScheduler,
95
  "Euler a": EulerAncestralDiscreteScheduler,
96
  },
 
97
  WIDTH=1024,
98
  HEIGHT=1024,
99
  NUM_IMAGES=1,
100
  SEED=-1,
101
+ GUIDANCE_SCALE=6,
102
  INFERENCE_STEPS=40,
103
  DEEPCACHE_INTERVAL=1,
104
  SCALE=1,
lib/inference.py CHANGED
@@ -1,8 +1,5 @@
1
- import gc
2
- import re
3
  import time
4
  from datetime import datetime
5
- from itertools import product
6
 
7
  import torch
8
  from compel import Compel, ReturnedEmbeddingsType
@@ -11,45 +8,11 @@ from spaces import GPU
11
 
12
  from .config import Config
13
  from .loader import Loader
14
- from .utils import load_json
 
15
 
16
 
17
- def parse_prompt_with_arrays(prompt: str) -> list[str]:
18
- arrays = re.findall(r"\[\[(.*?)\]\]", prompt)
19
-
20
- if not arrays:
21
- return [prompt]
22
-
23
- tokens = [item.split(",") for item in arrays] # [("a", "b"), (1, 2)]
24
- combinations = list(product(*tokens)) # [("a", 1), ("a", 2), ("b", 1), ("b", 2)]
25
-
26
- # find all the arrays in the prompt and replace them with tokens
27
- prompts = []
28
- for combo in combinations:
29
- current_prompt = prompt
30
- for i, token in enumerate(combo):
31
- current_prompt = current_prompt.replace(f"[[{arrays[i]}]]", token.strip(), 1)
32
- prompts.append(current_prompt)
33
- return prompts
34
-
35
-
36
- def apply_style(positive_prompt, negative_prompt, style_id="none"):
37
- if style_id.lower() == "none":
38
- return (positive_prompt, negative_prompt)
39
-
40
- styles = load_json("./data/styles.json")
41
- style = styles.get(style_id)
42
- if style is None:
43
- return (positive_prompt, negative_prompt)
44
-
45
- style_base = style.get("_base", {})
46
- return (
47
- style.get("positive").format(prompt=positive_prompt, _base=style_base.get("positive")).strip(),
48
- style.get("negative").format(prompt=negative_prompt, _base=style_base.get("negative")).strip(),
49
- )
50
-
51
-
52
- # max 60s per image
53
  def gpu_duration(**kwargs):
54
  loading = 15
55
  duration = 15
@@ -76,13 +39,12 @@ def gpu_duration(**kwargs):
76
  def generate(
77
  positive_prompt,
78
  negative_prompt="",
79
- style=None,
80
  seed=None,
81
  model="stabilityai/stable-diffusion-xl-base-1.0",
82
- scheduler="DDIM",
83
  width=1024,
84
  height=1024,
85
- guidance_scale=7.5,
86
  inference_steps=40,
87
  deepcache=1,
88
  scale=1,
@@ -93,6 +55,13 @@ def generate(
93
  Info=None,
94
  progress=None,
95
  ):
 
 
 
 
 
 
 
96
  if not torch.cuda.is_available():
97
  raise Error("CUDA not available")
98
 
@@ -108,7 +77,6 @@ def generate(
108
  # custom progress bar for multiple images
109
  def callback_on_step_end(pipeline, step, timestep, latents):
110
  nonlocal CURRENT_IMAGE, CURRENT_STEP
111
-
112
  if progress is not None:
113
  # calculate total steps for img2img based on denoising strength
114
  strength = 1
@@ -121,19 +89,12 @@ def generate(
121
  else:
122
  refining = True
123
  CURRENT_STEP += 1
124
-
125
  progress(
126
  (CURRENT_STEP, total_steps),
127
  desc=f"{'Refining' if refining else 'Generating'} image {CURRENT_IMAGE}/{num_images}",
128
  )
129
  return latents
130
 
131
- start = time.perf_counter()
132
- print(f"Generating {num_images} image{'s' if num_images > 1 else ''}")
133
-
134
- if Config.ZERO_GPU and progress is not None:
135
- progress((100, 100), desc="ZeroGPU init")
136
-
137
  loader = Loader()
138
  loader.load(
139
  KIND,
@@ -173,19 +134,13 @@ def generate(
173
 
174
  images = []
175
  current_seed = seed
176
- for i in range(num_images):
177
- generator = torch.Generator(device=pipe.device).manual_seed(current_seed)
178
 
 
179
  try:
180
- positive_prompts = parse_prompt_with_arrays(positive_prompt)
181
- index = i % len(positive_prompts)
182
- positive_styled, negative_styled = apply_style(positive_prompts[index], negative_prompt, style)
183
-
184
- if negative_styled.startswith("(), "):
185
- negative_styled = negative_styled[4:]
186
-
187
- conditioning_1, pooled_1 = compel_1([positive_styled, negative_styled])
188
- conditioning_2, pooled_2 = compel_2([positive_styled, negative_styled])
189
  except PromptParser.ParsingException:
190
  raise Error("Invalid prompt")
191
 
@@ -214,46 +169,52 @@ def generate(
214
  "negative_pooled_prompt_embeds": pooled_1[1:2],
215
  }
216
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  if progress is not None:
218
  pipe_kwargs["callback_on_step_end"] = callback_on_step_end
 
219
 
220
  try:
221
  image = pipe(**pipe_kwargs).images[0]
222
-
223
- refiner_kwargs = {
224
- "image": image,
225
- "denoising_start": 0.8,
226
- "generator": generator,
227
- "output_type": refiner_output_type,
228
- "guidance_scale": guidance_scale,
229
- "num_inference_steps": inference_steps,
230
- "prompt_embeds": conditioning_2[0:1],
231
- "pooled_prompt_embeds": pooled_2[0:1],
232
- "negative_prompt_embeds": conditioning_2[1:2],
233
- "negative_pooled_prompt_embeds": pooled_2[1:2],
234
- }
235
-
236
- if progress is not None:
237
- refiner_kwargs["callback_on_step_end"] = callback_on_step_end
238
  if use_refiner:
 
239
  image = refiner(**refiner_kwargs).images[0]
240
- if scale > 1:
241
- image = upscaler.predict(image)
242
  images.append((image, str(current_seed)))
243
  current_seed += 1
244
- except Exception as e:
245
- raise Error(f"{e}")
246
  finally:
247
  CURRENT_STEP = 0
248
  CURRENT_IMAGE += 1
249
 
250
- # cleanup
251
- loader.collect()
252
- gc.collect()
 
 
 
 
 
 
 
 
 
253
 
254
  end = time.perf_counter()
255
  msg = f"Generated {len(images)} image{'s' if len(images) > 1 else ''} in {end - start:.2f}s"
256
- print(msg)
 
 
257
  if Info:
258
  Info(msg)
 
259
  return images
 
 
 
1
  import time
2
  from datetime import datetime
 
3
 
4
  import torch
5
  from compel import Compel, ReturnedEmbeddingsType
 
8
 
9
  from .config import Config
10
  from .loader import Loader
11
+ from .logger import Logger
12
+ from .utils import clear_cuda_cache, safe_progress, timer
13
 
14
 
15
+ # Dynamic signature for the GPU duration function; max 60s per image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  def gpu_duration(**kwargs):
17
  loading = 15
18
  duration = 15
 
39
  def generate(
40
  positive_prompt,
41
  negative_prompt="",
 
42
  seed=None,
43
  model="stabilityai/stable-diffusion-xl-base-1.0",
44
+ scheduler="Euler",
45
  width=1024,
46
  height=1024,
47
+ guidance_scale=6.0,
48
  inference_steps=40,
49
  deepcache=1,
50
  scale=1,
 
55
  Info=None,
56
  progress=None,
57
  ):
58
+ start = time.perf_counter()
59
+ log = Logger("generate")
60
+ log.info(f"Generating {num_images} image{'s' if num_images > 1 else ''}...")
61
+
62
+ if Config.ZERO_GPU:
63
+ safe_progress(progress, 100, 100, "ZeroGPU init")
64
+
65
  if not torch.cuda.is_available():
66
  raise Error("CUDA not available")
67
 
 
77
  # custom progress bar for multiple images
78
  def callback_on_step_end(pipeline, step, timestep, latents):
79
  nonlocal CURRENT_IMAGE, CURRENT_STEP
 
80
  if progress is not None:
81
  # calculate total steps for img2img based on denoising strength
82
  strength = 1
 
89
  else:
90
  refining = True
91
  CURRENT_STEP += 1
 
92
  progress(
93
  (CURRENT_STEP, total_steps),
94
  desc=f"{'Refining' if refining else 'Generating'} image {CURRENT_IMAGE}/{num_images}",
95
  )
96
  return latents
97
 
 
 
 
 
 
 
98
  loader = Loader()
99
  loader.load(
100
  KIND,
 
134
 
135
  images = []
136
  current_seed = seed
137
+ safe_progress(progress, 0, num_images, f"Generating image 0/{num_images}")
 
138
 
139
+ for i in range(num_images):
140
  try:
141
+ generator = torch.Generator(device=pipe.device).manual_seed(current_seed)
142
+ conditioning_1, pooled_1 = compel_1([positive_prompt, negative_prompt])
143
+ conditioning_2, pooled_2 = compel_2([positive_prompt, negative_prompt])
 
 
 
 
 
 
144
  except PromptParser.ParsingException:
145
  raise Error("Invalid prompt")
146
 
 
169
  "negative_pooled_prompt_embeds": pooled_1[1:2],
170
  }
171
 
172
+ refiner_kwargs = {
173
+ "denoising_start": 0.8,
174
+ "generator": generator,
175
+ "output_type": refiner_output_type,
176
+ "guidance_scale": guidance_scale,
177
+ "num_inference_steps": inference_steps,
178
+ "prompt_embeds": conditioning_2[0:1],
179
+ "pooled_prompt_embeds": pooled_2[0:1],
180
+ "negative_prompt_embeds": conditioning_2[1:2],
181
+ "negative_pooled_prompt_embeds": pooled_2[1:2],
182
+ }
183
+
184
  if progress is not None:
185
  pipe_kwargs["callback_on_step_end"] = callback_on_step_end
186
+ refiner_kwargs["callback_on_step_end"] = callback_on_step_end
187
 
188
  try:
189
  image = pipe(**pipe_kwargs).images[0]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  if use_refiner:
191
+ refiner_kwargs["image"] = image
192
  image = refiner(**refiner_kwargs).images[0]
 
 
193
  images.append((image, str(current_seed)))
194
  current_seed += 1
 
 
195
  finally:
196
  CURRENT_STEP = 0
197
  CURRENT_IMAGE += 1
198
 
199
+ # Upscale
200
+ if scale > 1:
201
+ msg = f"Upscaling {scale}x"
202
+ with timer(msg):
203
+ safe_progress(progress, 0, num_images, desc=msg)
204
+ for i, image in enumerate(images):
205
+ images = upscaler.predict(image[0])
206
+ images[i] = image
207
+ safe_progress(progress, i + 1, num_images, desc=msg)
208
+
209
+ # Flush memory after generating
210
+ clear_cuda_cache()
211
 
212
  end = time.perf_counter()
213
  msg = f"Generated {len(images)} image{'s' if len(images) > 1 else ''} in {end - start:.2f}s"
214
+ log.info(msg)
215
+
216
+ # Alert if notifier provided
217
  if Info:
218
  Info(msg)
219
+
220
  return images
lib/loader.py CHANGED
@@ -6,8 +6,9 @@ from DeepCache import DeepCacheSDHelper
6
  from diffusers.models import AutoencoderKL
7
 
8
  from .config import Config
 
9
  from .upscaler import RealESRGAN
10
- from .utils import timer
11
 
12
 
13
  class Loader:
@@ -22,17 +23,9 @@ class Loader:
22
  cls._instance.model = None
23
  cls._instance.refiner = None
24
  cls._instance.upscaler = None
 
25
  return cls._instance
26
 
27
- # switching models
28
- def _should_reset_refiner(self, model=""):
29
- if self.refiner is None:
30
- return False
31
- if self.model and self.model.lower() != model.lower():
32
- return True
33
- return False
34
-
35
- # switching refiner
36
  def _should_unload_refiner(self, refiner=False):
37
  if self.refiner is None:
38
  return False
@@ -60,13 +53,6 @@ class Loader:
60
  return True
61
  return False
62
 
63
- def _reset_refiner(self):
64
- if self.refiner is not None:
65
- self.refiner.vae = None
66
- self.refiner.scheduler = None
67
- self.refiner.tokenizer_2 = None
68
- self.refiner.text_encoder_2 = None
69
-
70
  def _unload_refiner(self):
71
  if self.refiner is not None:
72
  with timer("Unloading refiner"):
@@ -79,7 +65,7 @@ class Loader:
79
 
80
  def _unload_deepcache(self):
81
  if self.pipe.deepcache is not None:
82
- print("Disabling DeepCache")
83
  self.pipe.deepcache.disable()
84
  delattr(self.pipe, "deepcache")
85
  if self.refiner is not None:
@@ -91,15 +77,17 @@ class Loader:
91
  if self.pipe is not None:
92
  with timer(f"Unloading {self.model}"):
93
  self.pipe.to("cpu", silence_dtype_warnings=True)
 
 
 
 
 
94
 
95
  def _unload(self, model, refiner, deepcache, scale):
96
  to_unload = []
97
  if self._should_unload_deepcache(deepcache): # remove deepcache first
98
  self._unload_deepcache()
99
 
100
- if self._should_reset_refiner(model):
101
- self._reset_refiner()
102
-
103
  if self._should_unload_refiner(refiner):
104
  self._unload_refiner()
105
  to_unload.append("refiner")
@@ -113,59 +101,73 @@ class Loader:
113
  to_unload.append("model")
114
  to_unload.append("pipe")
115
 
116
- self.collect()
 
117
  for component in to_unload:
118
  setattr(self, component, None)
119
- gc.collect()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
  def _load_refiner(self, refiner, progress, **kwargs):
122
- if refiner and self.refiner is None:
123
  model = Config.REFINER_MODEL
124
  pipeline = Config.PIPELINES["img2img"]
125
  try:
126
  with timer(f"Loading {model}"):
127
  self.refiner = pipeline.from_pretrained(model, **kwargs).to("cuda")
128
  except Exception as e:
129
- print(f"Error loading {model}: {e}")
130
  self.refiner = None
131
  return
132
  if self.refiner is not None:
133
  self.refiner.set_progress_bar_config(disable=progress is not None)
134
 
135
  def _load_upscaler(self, scale=1):
136
- if self.upscaler is None and scale > 1:
137
  try:
138
  with timer(f"Loading {scale}x upscaler"):
139
  self.upscaler = RealESRGAN(scale, device=self.pipe.device)
140
  self.upscaler.load_weights()
141
  except Exception as e:
142
- print(f"Error loading {scale}x upscaler: {e}")
143
  self.upscaler = None
144
 
145
  def _load_deepcache(self, interval=1):
146
- pipe_has_deepcache = hasattr(self.pipe, "deepcache")
147
- if not pipe_has_deepcache and interval == 1:
148
- return
149
- if pipe_has_deepcache and self.pipe.deepcache.params["cache_interval"] == interval:
150
- return
151
- print("Enabling DeepCache")
152
- self.pipe.deepcache = DeepCacheSDHelper(pipe=self.pipe)
153
- self.pipe.deepcache.set_params(cache_interval=interval)
154
- self.pipe.deepcache.enable()
155
-
156
- if self.refiner is not None:
157
- refiner_has_deepcache = hasattr(self.refiner, "deepcache")
158
- if not refiner_has_deepcache and interval == 1:
159
- return
160
- if refiner_has_deepcache and self.refiner.deepcache.params["cache_interval"] == interval:
161
- return
162
- self.refiner.deepcache = DeepCacheSDHelper(pipe=self.refiner)
163
- self.refiner.deepcache.set_params(cache_interval=interval)
164
- self.refiner.deepcache.enable()
165
 
166
  def _load_pipeline(self, kind, model, progress, **kwargs):
167
  pipeline = Config.PIPELINES[kind]
168
- if self.pipe is None:
169
  try:
170
  with timer(f"Loading {model}"):
171
  self.model = model
@@ -183,21 +185,16 @@ class Loader:
183
  self.refiner.text_encoder_2 = self.pipe.text_encoder_2
184
  self.refiner.to(self.pipe.device)
185
  except Exception as e:
186
- print(f"Error loading {model}: {e}")
187
  self.model = None
188
  self.pipe = None
 
189
  return
190
  if not isinstance(self.pipe, pipeline):
191
  self.pipe = pipeline.from_pipe(self.pipe).to("cuda")
192
  if self.pipe is not None:
193
  self.pipe.set_progress_bar_config(disable=progress is not None)
194
 
195
- def collect(self):
196
- torch.cuda.empty_cache()
197
- torch.cuda.ipc_collect()
198
- torch.cuda.reset_peak_memory_stats()
199
- torch.cuda.synchronize()
200
-
201
  def load(self, kind, model, scheduler, deepcache, scale, karras, refiner, progress):
202
  scheduler_kwargs = {
203
  "beta_start": 0.00085,
@@ -245,27 +242,31 @@ class Loader:
245
  # same model, different scheduler
246
  if self.model.lower() == model.lower():
247
  if not same_scheduler:
248
- print(f"Switching to {scheduler}")
249
  if not same_karras:
250
- print(f"{'Enabling' if karras else 'Disabling'} Karras sigmas")
251
  if not same_scheduler or not same_karras:
252
  self.pipe.scheduler = Config.SCHEDULERS[scheduler](**scheduler_kwargs)
253
  if self.refiner is not None:
254
  self.refiner.scheduler = self.pipe.scheduler
255
 
256
- # https://huggingface.co/stabilityai/stable-diffusion-xl-refiner-1.0/blob/main/model_index.json
257
- refiner_kwargs = {
258
- "variant": "fp16",
259
- "torch_dtype": dtype,
260
- "add_watermarker": False,
261
- "requires_aesthetics_score": True,
262
- "force_zeros_for_empty_prompt": False,
263
- "vae": self.pipe.vae,
264
- "scheduler": self.pipe.scheduler,
265
- "tokenizer_2": self.pipe.tokenizer_2,
266
- "text_encoder_2": self.pipe.text_encoder_2,
267
- }
268
-
269
- self._load_refiner(refiner, progress, **refiner_kwargs) # load refiner before deepcache
270
- self._load_deepcache(deepcache)
271
- self._load_upscaler(scale)
 
 
 
 
 
6
  from diffusers.models import AutoencoderKL
7
 
8
  from .config import Config
9
+ from .logger import Logger
10
  from .upscaler import RealESRGAN
11
+ from .utils import clear_cuda_cache, timer
12
 
13
 
14
  class Loader:
 
23
  cls._instance.model = None
24
  cls._instance.refiner = None
25
  cls._instance.upscaler = None
26
+ cls._instance.log = Logger("Loader")
27
  return cls._instance
28
 
 
 
 
 
 
 
 
 
 
29
  def _should_unload_refiner(self, refiner=False):
30
  if self.refiner is None:
31
  return False
 
53
  return True
54
  return False
55
 
 
 
 
 
 
 
 
56
  def _unload_refiner(self):
57
  if self.refiner is not None:
58
  with timer("Unloading refiner"):
 
65
 
66
  def _unload_deepcache(self):
67
  if self.pipe.deepcache is not None:
68
+ self.log.info("Disabling DeepCache")
69
  self.pipe.deepcache.disable()
70
  delattr(self.pipe, "deepcache")
71
  if self.refiner is not None:
 
77
  if self.pipe is not None:
78
  with timer(f"Unloading {self.model}"):
79
  self.pipe.to("cpu", silence_dtype_warnings=True)
80
+ if self.refiner is not None:
81
+ self.refiner.vae = None
82
+ self.refiner.scheduler = None
83
+ self.refiner.tokenizer_2 = None
84
+ self.refiner.text_encoder_2 = None
85
 
86
  def _unload(self, model, refiner, deepcache, scale):
87
  to_unload = []
88
  if self._should_unload_deepcache(deepcache): # remove deepcache first
89
  self._unload_deepcache()
90
 
 
 
 
91
  if self._should_unload_refiner(refiner):
92
  self._unload_refiner()
93
  to_unload.append("refiner")
 
101
  to_unload.append("model")
102
  to_unload.append("pipe")
103
 
104
+ # Flush cache and run garbage collector
105
+ clear_cuda_cache()
106
  for component in to_unload:
107
  setattr(self, component, None)
108
+ gc.collect()
109
+
110
+ def _should_load_refiner(self, refiner=False):
111
+ if self.refiner is None and refiner:
112
+ return True
113
+ return False
114
+
115
+ def _should_load_upscaler(self, scale=1):
116
+ if self.upscaler is None and scale > 1:
117
+ return True
118
+ return False
119
+
120
+ def _should_load_deepcache(self, interval=1):
121
+ has_deepcache = hasattr(self.pipe, "deepcache")
122
+ if not has_deepcache and interval != 1:
123
+ return True
124
+ if has_deepcache and self.pipe.deepcache.params["cache_interval"] != interval:
125
+ return True
126
+ return False
127
+
128
+ def _should_load_pipeline(self):
129
+ if self.pipe is None:
130
+ return True
131
+ return False
132
 
133
  def _load_refiner(self, refiner, progress, **kwargs):
134
+ if self._should_load_refiner(refiner):
135
  model = Config.REFINER_MODEL
136
  pipeline = Config.PIPELINES["img2img"]
137
  try:
138
  with timer(f"Loading {model}"):
139
  self.refiner = pipeline.from_pretrained(model, **kwargs).to("cuda")
140
  except Exception as e:
141
+ self.log.error(f"Error loading {model}: {e}")
142
  self.refiner = None
143
  return
144
  if self.refiner is not None:
145
  self.refiner.set_progress_bar_config(disable=progress is not None)
146
 
147
  def _load_upscaler(self, scale=1):
148
+ if self._should_load_upscaler(scale):
149
  try:
150
  with timer(f"Loading {scale}x upscaler"):
151
  self.upscaler = RealESRGAN(scale, device=self.pipe.device)
152
  self.upscaler.load_weights()
153
  except Exception as e:
154
+ self.log.error(f"Error loading {scale}x upscaler: {e}")
155
  self.upscaler = None
156
 
157
  def _load_deepcache(self, interval=1):
158
+ if self._should_load_deepcache(interval):
159
+ self.log.info("Enabling DeepCache")
160
+ self.pipe.deepcache = DeepCacheSDHelper(pipe=self.pipe)
161
+ self.pipe.deepcache.set_params(cache_interval=interval)
162
+ self.pipe.deepcache.enable()
163
+ if self.refiner is not None:
164
+ self.refiner.deepcache = DeepCacheSDHelper(pipe=self.refiner)
165
+ self.refiner.deepcache.set_params(cache_interval=interval)
166
+ self.refiner.deepcache.enable()
 
 
 
 
 
 
 
 
 
 
167
 
168
  def _load_pipeline(self, kind, model, progress, **kwargs):
169
  pipeline = Config.PIPELINES[kind]
170
+ if self._should_load_pipeline():
171
  try:
172
  with timer(f"Loading {model}"):
173
  self.model = model
 
185
  self.refiner.text_encoder_2 = self.pipe.text_encoder_2
186
  self.refiner.to(self.pipe.device)
187
  except Exception as e:
188
+ self.log.error(f"Error loading {model}: {e}")
189
  self.model = None
190
  self.pipe = None
191
+ self.refiner = None
192
  return
193
  if not isinstance(self.pipe, pipeline):
194
  self.pipe = pipeline.from_pipe(self.pipe).to("cuda")
195
  if self.pipe is not None:
196
  self.pipe.set_progress_bar_config(disable=progress is not None)
197
 
 
 
 
 
 
 
198
  def load(self, kind, model, scheduler, deepcache, scale, karras, refiner, progress):
199
  scheduler_kwargs = {
200
  "beta_start": 0.00085,
 
242
  # same model, different scheduler
243
  if self.model.lower() == model.lower():
244
  if not same_scheduler:
245
+ self.log.info(f"Enabling {scheduler}")
246
  if not same_karras:
247
+ self.log.info(f"{'Enabling' if karras else 'Disabling'} Karras sigmas")
248
  if not same_scheduler or not same_karras:
249
  self.pipe.scheduler = Config.SCHEDULERS[scheduler](**scheduler_kwargs)
250
  if self.refiner is not None:
251
  self.refiner.scheduler = self.pipe.scheduler
252
 
253
+ if self._should_load_refiner(refiner):
254
+ # https://huggingface.co/stabilityai/stable-diffusion-xl-refiner-1.0/blob/main/model_index.json
255
+ refiner_kwargs = {
256
+ "variant": "fp16",
257
+ "torch_dtype": dtype,
258
+ "add_watermarker": False,
259
+ "requires_aesthetics_score": True,
260
+ "force_zeros_for_empty_prompt": False,
261
+ "vae": self.pipe.vae,
262
+ "scheduler": self.pipe.scheduler,
263
+ "tokenizer_2": self.pipe.tokenizer_2,
264
+ "text_encoder_2": self.pipe.text_encoder_2,
265
+ }
266
+ self._load_refiner(refiner, progress, **refiner_kwargs) # load refiner before deepcache
267
+
268
+ if self._should_load_deepcache(deepcache):
269
+ self._load_deepcache(deepcache)
270
+
271
+ if self._should_load_upscaler(scale):
272
+ self._load_upscaler(scale)
lib/logger.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from threading import Lock
3
+
4
+
5
+ class Logger:
6
+ _instances = {}
7
+ _lock = Lock()
8
+
9
+ def __new__(cls, name="root"):
10
+ with cls._lock:
11
+ if name not in cls._instances:
12
+ instance = super().__new__(cls)
13
+ instance._init(name)
14
+ cls._instances[name] = instance
15
+ return cls._instances[name]
16
+
17
+ def _init(self, name):
18
+ self.logger = logging.getLogger(name)
19
+ self.logger.setLevel(logging.DEBUG)
20
+ self.logger.propagate = False
21
+
22
+ console_handler = logging.StreamHandler()
23
+ console_handler.setLevel(logging.INFO)
24
+
25
+ file_handler = logging.FileHandler("app.log")
26
+ file_handler.setLevel(logging.DEBUG)
27
+
28
+ formatter = logging.Formatter(
29
+ "%(asctime)s [%(threadName)s] %(levelname)-5s %(name)s - %(message)s",
30
+ datefmt="%Y-%m-%d %H:%M:%S", # no milliseconds
31
+ )
32
+ console_handler.setFormatter(formatter)
33
+ file_handler.setFormatter(formatter)
34
+
35
+ self.logger.addHandler(console_handler)
36
+ self.logger.addHandler(file_handler)
37
+
38
+ def _log(self, level, message):
39
+ log_message = f"{message}".strip()
40
+ self.logger.log(level, log_message)
41
+
42
+ def debug(self, message, **kwargs):
43
+ self._log(logging.DEBUG, message, **kwargs)
44
+
45
+ def info(self, message, **kwargs):
46
+ self._log(logging.INFO, message, **kwargs)
47
+
48
+ def warning(self, message, **kwargs):
49
+ self._log(logging.WARNING, message, **kwargs)
50
+
51
+ def error(self, message, **kwargs):
52
+ self._log(logging.ERROR, message, **kwargs)
53
+
54
+ def critical(self, message, **kwargs):
55
+ self._log(logging.CRITICAL, message, **kwargs)
lib/utils.py CHANGED
@@ -1,13 +1,12 @@
1
  import functools
2
  import inspect
3
  import json
4
- import os
5
  import time
6
  from contextlib import contextmanager
7
  from typing import Callable, TypeVar
8
 
9
  import anyio
10
- import httpx
11
  from anyio import Semaphore
12
  from diffusers.utils import logging as diffusers_logging
13
  from huggingface_hub._snapshot_download import snapshot_download
@@ -34,9 +33,10 @@ def timer(message="Operation", logger=print):
34
 
35
 
36
  @functools.lru_cache()
37
- def load_json(path: str) -> dict:
38
  with open(path, "r", encoding="utf-8") as file:
39
- return json.load(file)
 
40
 
41
 
42
  @functools.lru_cache()
@@ -56,6 +56,19 @@ def enable_progress_bars():
56
  diffusers_logging.enable_progress_bar()
57
 
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  def download_repo_files(repo_id, allow_patterns, token=None):
60
  was_disabled = are_progress_bars_disabled()
61
  enable_progress_bars()
@@ -72,34 +85,7 @@ def download_repo_files(repo_id, allow_patterns, token=None):
72
  return snapshot_path
73
 
74
 
75
- def download_civit_file(lora_id, version_id, file_path=".", token=None):
76
- base_url = "https://civitai.com/api/download/models"
77
- file = f"{file_path}/{lora_id}.{version_id}.safetensors"
78
-
79
- if os.path.exists(file):
80
- return
81
-
82
- try:
83
- params = {"token": token}
84
- response = httpx.get(
85
- f"{base_url}/{version_id}",
86
- timeout=None,
87
- params=params,
88
- follow_redirects=True,
89
- )
90
-
91
- response.raise_for_status()
92
- os.makedirs(file_path, exist_ok=True)
93
-
94
- with open(file, "wb") as f:
95
- f.write(response.content)
96
- except httpx.HTTPStatusError as e:
97
- print(f"HTTPError: {e.response.status_code} {e.response.text}")
98
- except httpx.RequestError as e:
99
- print(f"RequestError: {e}")
100
-
101
-
102
- # like the original but supports args and kwargs instead of a dict
103
  # https://github.com/huggingface/huggingface-inference-toolkit/blob/0.2.0/src/huggingface_inference_toolkit/async_utils.py
104
  async def async_call(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
105
  async with MAX_THREADS_GUARD:
 
1
  import functools
2
  import inspect
3
  import json
 
4
  import time
5
  from contextlib import contextmanager
6
  from typing import Callable, TypeVar
7
 
8
  import anyio
9
+ import torch
10
  from anyio import Semaphore
11
  from diffusers.utils import logging as diffusers_logging
12
  from huggingface_hub._snapshot_download import snapshot_download
 
33
 
34
 
35
  @functools.lru_cache()
36
+ def read_json(path: str) -> dict:
37
  with open(path, "r", encoding="utf-8") as file:
38
+ data = json.load(file)
39
+ return json.dumps(data, indent=4)
40
 
41
 
42
  @functools.lru_cache()
 
56
  diffusers_logging.enable_progress_bar()
57
 
58
 
59
+ def safe_progress(progress, current=0, total=0, desc=""):
60
+ if progress is not None:
61
+ progress((current, total), desc=desc)
62
+
63
+
64
+ def clear_cuda_cache():
65
+ if torch.cuda.is_available():
66
+ torch.cuda.empty_cache()
67
+ torch.cuda.ipc_collect()
68
+ torch.cuda.reset_peak_memory_stats()
69
+ torch.cuda.synchronize()
70
+
71
+
72
  def download_repo_files(repo_id, allow_patterns, token=None):
73
  was_disabled = are_progress_bars_disabled()
74
  enable_progress_bars()
 
85
  return snapshot_path
86
 
87
 
88
+ # Like the original but supports args and kwargs instead of a dict
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  # https://github.com/huggingface/huggingface-inference-toolkit/blob/0.2.0/src/huggingface_inference_toolkit/async_utils.py
90
  async def async_call(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
91
  async with MAX_THREADS_GUARD:
partials/intro.html CHANGED
@@ -7,18 +7,7 @@
7
  <path d="M7.48877 6.75C7.29015 6.75 7.09967 6.82902 6.95923 6.96967C6.81879 7.11032 6.73989 7.30109 6.73989 7.5C6.73989 7.69891 6.81879 7.88968 6.95923 8.03033C7.09967 8.17098 7.29015 8.25 7.48877 8.25C7.68738 8.25 7.87786 8.17098 8.0183 8.03033C8.15874 7.88968 8.23764 7.69891 8.23764 7.5C8.23764 7.30109 8.15874 7.11032 8.0183 6.96967C7.87786 6.82902 7.68738 6.75 7.48877 6.75ZM7.8632 0C11.2331 0 11.3155 2.6775 9.54818 3.5625C8.80679 3.93 8.47728 4.7175 8.335 5.415C8.69446 5.565 9.00899 5.7975 9.24863 6.0975C12.0195 4.5975 15 5.19 15 7.875C15 11.25 12.3265 11.325 11.4428 9.5475C11.0684 8.805 10.2746 8.475 9.57813 8.3325C9.42836 8.6925 9.19621 9 8.89665 9.255C10.3869 12.0225 9.79531 15 7.11433 15C3.74438 15 3.67698 12.315 5.44433 11.43C6.17823 11.0625 6.50774 10.2825 6.65751 9.5925C6.29056 9.4425 5.96855 9.2025 5.72891 8.9025C2.96555 10.3875 0 9.8025 0 7.125C0 3.75 2.666 3.6675 3.54967 5.445C3.92411 6.1875 4.71043 6.51 5.40689 6.6525C5.54918 6.2925 5.78882 5.9775 6.09586 5.7375C4.60559 2.97 5.1972 0 7.8632 0Z"></path>
8
  </svg>
9
  </div>
10
- <div>
11
- <nav>
12
- <a href="https://huggingface.co/spaces/adamelliotfields/diffusion" target="_blank" rel="noopener noreferrer">1.5</a>
13
- <span>XL</span>
14
- <a href="https://huggingface.co/spaces/adamelliotfields/diffusion-flux" target="_blank" rel="noopener noreferrer">FLUX.1</a>
15
- <a href="https://huggingface.co/spaces/adamelliotfields/diffusion-xl/blob/main/DOCS.md" target="_blank" rel="noopener noreferrer">Docs</a>
16
- <a href="https://adamelliotfields-diffusion-xl.hf.space" target="_blank" rel="noopener noreferrer">
17
- <svg style="display: inline-block" width="16px" height="16px" viewBox="0 0 12 12" fill="currentColor" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet">
18
- <path fill-rule="evenodd" clip-rule="evenodd" d="M7.5 1.75H9.75C9.88807 1.75 10 1.86193 10 2V4.25C10 4.38807 9.88807 4.5 9.75 4.5C9.61193 4.5 9.5 4.38807 9.5 4.25V2.60355L6.42678 5.67678C6.32915 5.77441 6.17085 5.77441 6.07322 5.67678C5.97559 5.57915 5.97559 5.42085 6.07322 5.32322L9.14645 2.25H7.5C7.36193 2.25 7.25 2.13807 7.25 2C7.25 1.86193 7.36193 1.75 7.5 1.75Z" fill="currentColor"></path>
19
- <path fill-rule="evenodd" clip-rule="evenodd" d="M6 2.5C6 2.22386 5.77614 2 5.5 2H2.69388C2.50985 2 2.33336 2.07311 2.20323 2.20323C2.0731 2.33336 2 2.50986 2 2.69389V8.93885C2 9.12288 2.0731 9.29933 2.20323 9.42953C2.33336 9.55963 2.50985 9.63273 2.69388 9.63273H8.93884C9.12287 9.63273 9.29941 9.55963 9.42951 9.42953C9.55961 9.29933 9.63271 9.12288 9.63271 8.93885V6.5C9.63271 6.22386 9.40885 6 9.13271 6C8.85657 6 8.63271 6.22386 8.63271 6.5V8.63273H3V3H5.5C5.77614 3 6 2.77614 6 2.5Z" fill="currentColor" fill-opacity="0.3"></path>
20
- </svg>
21
- </a>
22
- </nav>
23
- </div>
24
  </div>
 
7
  <path d="M7.48877 6.75C7.29015 6.75 7.09967 6.82902 6.95923 6.96967C6.81879 7.11032 6.73989 7.30109 6.73989 7.5C6.73989 7.69891 6.81879 7.88968 6.95923 8.03033C7.09967 8.17098 7.29015 8.25 7.48877 8.25C7.68738 8.25 7.87786 8.17098 8.0183 8.03033C8.15874 7.88968 8.23764 7.69891 8.23764 7.5C8.23764 7.30109 8.15874 7.11032 8.0183 6.96967C7.87786 6.82902 7.68738 6.75 7.48877 6.75ZM7.8632 0C11.2331 0 11.3155 2.6775 9.54818 3.5625C8.80679 3.93 8.47728 4.7175 8.335 5.415C8.69446 5.565 9.00899 5.7975 9.24863 6.0975C12.0195 4.5975 15 5.19 15 7.875C15 11.25 12.3265 11.325 11.4428 9.5475C11.0684 8.805 10.2746 8.475 9.57813 8.3325C9.42836 8.6925 9.19621 9 8.89665 9.255C10.3869 12.0225 9.79531 15 7.11433 15C3.74438 15 3.67698 12.315 5.44433 11.43C6.17823 11.0625 6.50774 10.2825 6.65751 9.5925C6.29056 9.4425 5.96855 9.2025 5.72891 8.9025C2.96555 10.3875 0 9.8025 0 7.125C0 3.75 2.666 3.6675 3.54967 5.445C3.92411 6.1875 4.71043 6.51 5.40689 6.6525C5.54918 6.2925 5.78882 5.9775 6.09586 5.7375C4.60559 2.97 5.1972 0 7.8632 0Z"></path>
8
  </svg>
9
  </div>
10
+ <p>
11
+ Image generation studio for Stable Diffusion XL.
12
+ </p>
 
 
 
 
 
 
 
 
 
 
 
13
  </div>
requirements.txt CHANGED
@@ -1,19 +1,12 @@
1
- anyio==4.4.0
2
- accelerate
3
- einops==0.8.0
4
  compel==2.0.3
5
  deepcache==0.1.1
6
- diffusers==0.30.2
7
- h2
 
8
  hf-transfer
9
- httpx
10
- gradio==4.41.0
11
  numpy==1.26.4
12
- ruff==0.5.7
13
- spaces
14
  torch==2.2.0
15
  torchvision==0.17.0
16
- transformers==4.43.4
17
- # TODO: unpin once fixed upstream
18
- # https://github.com/gradio-app/gradio/issues/9278
19
- fastapi==0.112.2
 
1
+ anyio==4.6.1
 
 
2
  compel==2.0.3
3
  deepcache==0.1.1
4
+ diffusers==0.30.3
5
+ einops==0.8.0
6
+ gradio==4.44.1
7
  hf-transfer
 
 
8
  numpy==1.26.4
9
+ ruff==0.6.9
10
+ spaces==0.30.4
11
  torch==2.2.0
12
  torchvision==0.17.0