Spaces:
Running
on
Zero
Running
on
Zero
adamelliotfields
commited on
Commit
•
51fab87
1
Parent(s):
effc0a0
Remove arrays from prompts
Browse files- DOCS.md +2 -8
- lib/__init__.py +0 -20
- lib/inference.py +13 -35
- lib/loader.py +3 -9
- lib/utils.py +9 -0
DOCS.md
CHANGED
@@ -19,12 +19,6 @@ This is the same syntax used in [InvokeAI](https://invoke-ai.github.io/InvokeAI/
|
|
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:
|
@@ -61,7 +55,7 @@ Select one or more [textual inversion](https://huggingface.co/docs/diffusers/en/
|
|
61 |
|
62 |
[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.
|
63 |
|
64 |
-
Start by framing a simple subject like `portrait of a
|
65 |
|
66 |
#### Anime
|
67 |
|
@@ -79,7 +73,7 @@ Rescale up to 4x using [Real-ESRGAN](https://github.com/xinntao/Real-ESRGAN) wit
|
|
79 |
|
80 |
### Image-to-Image
|
81 |
|
82 |
-
The
|
83 |
|
84 |
#### Strength
|
85 |
|
|
|
19 |
| `(blue)1.2` | `(blue:1.2)` |
|
20 |
| `(blue)0.8` | `(blue:0.8)` |
|
21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
### Models
|
23 |
|
24 |
Each model checkpoint has a different aesthetic:
|
|
|
55 |
|
56 |
[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.
|
57 |
|
58 |
+
Start by framing a simple subject like `portrait of a cat` or `landscape of a mountain range` and experiment.
|
59 |
|
60 |
#### Anime
|
61 |
|
|
|
73 |
|
74 |
### Image-to-Image
|
75 |
|
76 |
+
The `Image-to-Image` settings allows you to provide input images for the initial latents, ControlNet, and IP-Adapter.
|
77 |
|
78 |
#### Strength
|
79 |
|
lib/__init__.py
CHANGED
@@ -1,39 +1,19 @@
|
|
1 |
-
from .annotators import CannyAnnotator
|
2 |
from .config import Config
|
3 |
from .inference import generate
|
4 |
-
from .loader import Loader
|
5 |
-
from .logger import Logger
|
6 |
-
from .upscaler import RealESRGAN
|
7 |
from .utils import (
|
8 |
-
annotate_image,
|
9 |
async_call,
|
10 |
disable_progress_bars,
|
11 |
download_civit_file,
|
12 |
download_repo_files,
|
13 |
-
enable_progress_bars,
|
14 |
-
load_json,
|
15 |
read_file,
|
16 |
-
resize_image,
|
17 |
-
safe_progress,
|
18 |
-
timer,
|
19 |
)
|
20 |
|
21 |
__all__ = [
|
22 |
-
"CannyAnnotator",
|
23 |
"Config",
|
24 |
-
"Loader",
|
25 |
-
"Logger",
|
26 |
-
"RealESRGAN",
|
27 |
-
"annotate_image",
|
28 |
"async_call",
|
29 |
"disable_progress_bars",
|
30 |
"download_civit_file",
|
31 |
"download_repo_files",
|
32 |
-
"enable_progress_bars",
|
33 |
"generate",
|
34 |
-
"load_json",
|
35 |
"read_file",
|
36 |
-
"resize_image",
|
37 |
-
"safe_progress",
|
38 |
-
"timer",
|
39 |
]
|
|
|
|
|
1 |
from .config import Config
|
2 |
from .inference import generate
|
|
|
|
|
|
|
3 |
from .utils import (
|
|
|
4 |
async_call,
|
5 |
disable_progress_bars,
|
6 |
download_civit_file,
|
7 |
download_repo_files,
|
|
|
|
|
8 |
read_file,
|
|
|
|
|
|
|
9 |
)
|
10 |
|
11 |
__all__ = [
|
|
|
12 |
"Config",
|
|
|
|
|
|
|
|
|
13 |
"async_call",
|
14 |
"disable_progress_bars",
|
15 |
"download_civit_file",
|
16 |
"download_repo_files",
|
|
|
17 |
"generate",
|
|
|
18 |
"read_file",
|
|
|
|
|
|
|
19 |
]
|
lib/inference.py
CHANGED
@@ -1,9 +1,6 @@
|
|
1 |
-
import gc
|
2 |
import os
|
3 |
-
import re
|
4 |
import time
|
5 |
from datetime import datetime
|
6 |
-
from itertools import product
|
7 |
|
8 |
import torch
|
9 |
from compel import Compel, DiffusersTextualInversionManager, ReturnedEmbeddingsType
|
@@ -16,6 +13,7 @@ from .loader import Loader
|
|
16 |
from .logger import Logger
|
17 |
from .utils import (
|
18 |
annotate_image,
|
|
|
19 |
load_json,
|
20 |
resize_image,
|
21 |
safe_progress,
|
@@ -23,25 +21,7 @@ from .utils import (
|
|
23 |
)
|
24 |
|
25 |
|
26 |
-
|
27 |
-
arrays = re.findall(r"\[\[(.*?)\]\]", prompt)
|
28 |
-
|
29 |
-
if not arrays:
|
30 |
-
return [prompt]
|
31 |
-
|
32 |
-
tokens = [item.split(",") for item in arrays] # [("a", "b"), ("1", "2")]
|
33 |
-
combinations = list(product(*tokens)) # [("a", "1"), ("a", "2"), ("b", "1"), ("b", "2")]
|
34 |
-
|
35 |
-
# find all the arrays in the prompt and replace them with tokens
|
36 |
-
prompts = []
|
37 |
-
for combo in combinations:
|
38 |
-
current_prompt = prompt
|
39 |
-
for i, token in enumerate(combo):
|
40 |
-
current_prompt = current_prompt.replace(f"[[{arrays[i]}]]", token.strip(), 1)
|
41 |
-
prompts.append(current_prompt)
|
42 |
-
return prompts
|
43 |
-
|
44 |
-
|
45 |
def apply_style(positive_prompt, negative_prompt, style_id="none"):
|
46 |
if style_id.lower() == "none":
|
47 |
return (positive_prompt, negative_prompt)
|
@@ -78,6 +58,7 @@ def gpu_duration(**kwargs):
|
|
78 |
return loading + (duration * num_images)
|
79 |
|
80 |
|
|
|
81 |
@GPU(duration=gpu_duration)
|
82 |
def generate(
|
83 |
positive_prompt,
|
@@ -143,7 +124,7 @@ def generate(
|
|
143 |
else:
|
144 |
IP_ADAPTER = ""
|
145 |
|
146 |
-
#
|
147 |
def callback_on_step_end(pipeline, step, timestep, latents):
|
148 |
nonlocal CURRENT_STEP, CURRENT_IMAGE
|
149 |
if progress is not None:
|
@@ -224,7 +205,7 @@ def generate(
|
|
224 |
except (EnvironmentError, HFValidationError, RepositoryNotFoundError):
|
225 |
raise Error(f"Invalid embedding: {embedding}")
|
226 |
|
227 |
-
#
|
228 |
compel = Compel(
|
229 |
device=pipe.device,
|
230 |
tokenizer=pipe.tokenizer,
|
@@ -241,15 +222,9 @@ def generate(
|
|
241 |
for i in range(num_images):
|
242 |
try:
|
243 |
generator = torch.Generator(device=pipe.device).manual_seed(current_seed)
|
|
|
244 |
|
245 |
-
|
246 |
-
index = i % len(positive_prompts)
|
247 |
-
positive_styled, negative_styled = apply_style(
|
248 |
-
positive_prompts[index],
|
249 |
-
negative_prompt,
|
250 |
-
style,
|
251 |
-
)
|
252 |
-
|
253 |
if negative_styled.startswith("(), "):
|
254 |
negative_styled = negative_styled[4:]
|
255 |
|
@@ -305,6 +280,7 @@ def generate(
|
|
305 |
CURRENT_STEP = 0
|
306 |
CURRENT_IMAGE += 1
|
307 |
|
|
|
308 |
if scale > 1:
|
309 |
msg = f"Upscaling {scale}x"
|
310 |
with timer(msg, logger=log.info):
|
@@ -314,13 +290,15 @@ def generate(
|
|
314 |
images[i] = image
|
315 |
safe_progress(progress, i + 1, num_images, desc=msg)
|
316 |
|
317 |
-
#
|
318 |
-
|
319 |
-
gc.collect()
|
320 |
|
321 |
end = time.perf_counter()
|
322 |
msg = f"Generating {len(images)} image{'s' if len(images) > 1 else ''} took {end - start:.2f}s"
|
323 |
log.info(msg)
|
|
|
|
|
324 |
if Info:
|
325 |
Info(msg)
|
|
|
326 |
return images
|
|
|
|
|
1 |
import os
|
|
|
2 |
import time
|
3 |
from datetime import datetime
|
|
|
4 |
|
5 |
import torch
|
6 |
from compel import Compel, DiffusersTextualInversionManager, ReturnedEmbeddingsType
|
|
|
13 |
from .logger import Logger
|
14 |
from .utils import (
|
15 |
annotate_image,
|
16 |
+
clear_cuda_cache,
|
17 |
load_json,
|
18 |
resize_image,
|
19 |
safe_progress,
|
|
|
21 |
)
|
22 |
|
23 |
|
24 |
+
# Inject prompts into style templates
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
def apply_style(positive_prompt, negative_prompt, style_id="none"):
|
26 |
if style_id.lower() == "none":
|
27 |
return (positive_prompt, negative_prompt)
|
|
|
58 |
return loading + (duration * num_images)
|
59 |
|
60 |
|
61 |
+
# Request GPU when deployed to Hugging Face
|
62 |
@GPU(duration=gpu_duration)
|
63 |
def generate(
|
64 |
positive_prompt,
|
|
|
124 |
else:
|
125 |
IP_ADAPTER = ""
|
126 |
|
127 |
+
# Custom progress bar for multiple images
|
128 |
def callback_on_step_end(pipeline, step, timestep, latents):
|
129 |
nonlocal CURRENT_STEP, CURRENT_IMAGE
|
130 |
if progress is not None:
|
|
|
205 |
except (EnvironmentError, HFValidationError, RepositoryNotFoundError):
|
206 |
raise Error(f"Invalid embedding: {embedding}")
|
207 |
|
208 |
+
# Embed prompts with weights
|
209 |
compel = Compel(
|
210 |
device=pipe.device,
|
211 |
tokenizer=pipe.tokenizer,
|
|
|
222 |
for i in range(num_images):
|
223 |
try:
|
224 |
generator = torch.Generator(device=pipe.device).manual_seed(current_seed)
|
225 |
+
positive_styled, negative_styled = apply_style(positive_prompt, negative_prompt, style)
|
226 |
|
227 |
+
# User didn't provide a negative prompt
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
if negative_styled.startswith("(), "):
|
229 |
negative_styled = negative_styled[4:]
|
230 |
|
|
|
280 |
CURRENT_STEP = 0
|
281 |
CURRENT_IMAGE += 1
|
282 |
|
283 |
+
# Upscale
|
284 |
if scale > 1:
|
285 |
msg = f"Upscaling {scale}x"
|
286 |
with timer(msg, logger=log.info):
|
|
|
290 |
images[i] = image
|
291 |
safe_progress(progress, i + 1, num_images, desc=msg)
|
292 |
|
293 |
+
# Flush memory after generating
|
294 |
+
clear_cuda_cache()
|
|
|
295 |
|
296 |
end = time.perf_counter()
|
297 |
msg = f"Generating {len(images)} image{'s' if len(images) > 1 else ''} took {end - start:.2f}s"
|
298 |
log.info(msg)
|
299 |
+
|
300 |
+
# Alert if notifier provided
|
301 |
if Info:
|
302 |
Info(msg)
|
303 |
+
|
304 |
return images
|
lib/loader.py
CHANGED
@@ -10,7 +10,7 @@ from diffusers.models.attention_processor import AttnProcessor2_0, IPAdapterAttn
|
|
10 |
from .config import Config
|
11 |
from .logger import Logger
|
12 |
from .upscaler import RealESRGAN
|
13 |
-
from .utils import safe_progress, timer
|
14 |
|
15 |
|
16 |
class Loader:
|
@@ -184,10 +184,10 @@ class Loader:
|
|
184 |
to_unload.append("model")
|
185 |
to_unload.append("pipe")
|
186 |
|
187 |
-
|
188 |
for component in to_unload:
|
189 |
setattr(self, component, None)
|
190 |
-
|
191 |
|
192 |
def _should_load_upscaler(self, scale=1):
|
193 |
if self.upscaler is None and scale > 1:
|
@@ -311,12 +311,6 @@ class Loader:
|
|
311 |
variant="fp16",
|
312 |
).to(self.pipe.device)
|
313 |
|
314 |
-
def collect(self):
|
315 |
-
torch.cuda.empty_cache()
|
316 |
-
torch.cuda.ipc_collect()
|
317 |
-
torch.cuda.reset_peak_memory_stats()
|
318 |
-
torch.cuda.synchronize()
|
319 |
-
|
320 |
def load(
|
321 |
self,
|
322 |
kind,
|
|
|
10 |
from .config import Config
|
11 |
from .logger import Logger
|
12 |
from .upscaler import RealESRGAN
|
13 |
+
from .utils import clear_cuda_cache, safe_progress, timer
|
14 |
|
15 |
|
16 |
class Loader:
|
|
|
184 |
to_unload.append("model")
|
185 |
to_unload.append("pipe")
|
186 |
|
187 |
+
clear_cuda_cache()
|
188 |
for component in to_unload:
|
189 |
setattr(self, component, None)
|
190 |
+
gc.collect()
|
191 |
|
192 |
def _should_load_upscaler(self, scale=1):
|
193 |
if self.upscaler is None and scale > 1:
|
|
|
311 |
variant="fp16",
|
312 |
).to(self.pipe.device)
|
313 |
|
|
|
|
|
|
|
|
|
|
|
|
|
314 |
def load(
|
315 |
self,
|
316 |
kind,
|
lib/utils.py
CHANGED
@@ -9,6 +9,7 @@ from typing import Callable, Tuple, TypeVar
|
|
9 |
import anyio
|
10 |
import httpx
|
11 |
import numpy as np
|
|
|
12 |
from anyio import Semaphore
|
13 |
from diffusers.utils import logging as diffusers_logging
|
14 |
from huggingface_hub._snapshot_download import snapshot_download
|
@@ -66,6 +67,14 @@ def safe_progress(progress, current=0, total=0, desc=""):
|
|
66 |
progress((current, total), desc=desc)
|
67 |
|
68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
def download_repo_files(repo_id, allow_patterns, token=None):
|
70 |
was_disabled = are_progress_bars_disabled()
|
71 |
enable_progress_bars()
|
|
|
9 |
import anyio
|
10 |
import httpx
|
11 |
import numpy as np
|
12 |
+
import torch
|
13 |
from anyio import Semaphore
|
14 |
from diffusers.utils import logging as diffusers_logging
|
15 |
from huggingface_hub._snapshot_download import snapshot_download
|
|
|
67 |
progress((current, total), desc=desc)
|
68 |
|
69 |
|
70 |
+
def clear_cuda_cache():
|
71 |
+
if torch.cuda.is_available():
|
72 |
+
torch.cuda.empty_cache()
|
73 |
+
torch.cuda.ipc_collect()
|
74 |
+
torch.cuda.reset_peak_memory_stats()
|
75 |
+
torch.cuda.synchronize()
|
76 |
+
|
77 |
+
|
78 |
def download_repo_files(repo_id, allow_patterns, token=None):
|
79 |
was_disabled = are_progress_bars_disabled()
|
80 |
enable_progress_bars()
|