gradio workspace app Jarvis 2024/06/08
#1
by
JarvisLabs
- opened
- app.py +423 -0
- config.py +111 -0
- requirements.txt +10 -0
- style.css +63 -0
- utils.py +164 -0
app.py
ADDED
@@ -0,0 +1,423 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import gc
|
3 |
+
import gradio as gr
|
4 |
+
import numpy as np
|
5 |
+
import torch
|
6 |
+
import json
|
7 |
+
import spaces
|
8 |
+
import config
|
9 |
+
import utils
|
10 |
+
import logging
|
11 |
+
from PIL import Image, PngImagePlugin
|
12 |
+
from datetime import datetime
|
13 |
+
from diffusers.models import AutoencoderKL
|
14 |
+
from diffusers import StableDiffusionXLPipeline, StableDiffusionXLImg2ImgPipeline
|
15 |
+
|
16 |
+
|
17 |
+
"""
|
18 |
+
Changes to base animagine-xl-3.1 log
|
19 |
+
- Cut the wildcard
|
20 |
+
- add in lora pipeline
|
21 |
+
- use let get env variable
|
22 |
+
- add in lora strenght variable
|
23 |
+
|
24 |
+
"""
|
25 |
+
|
26 |
+
logging.basicConfig(level=logging.INFO)
|
27 |
+
logger = logging.getLogger(__name__)
|
28 |
+
|
29 |
+
DESCRIPTION = "Animagine XL 3.1 X Galverse MAMA "
|
30 |
+
if not torch.cuda.is_available():
|
31 |
+
DESCRIPTION += "\n<p>Running on CPU 🥶 This demo does not work on CPU. </p>"
|
32 |
+
IS_COLAB = utils.is_google_colab() or os.getenv("IS_COLAB") == "1"
|
33 |
+
HF_TOKEN = os.getenv("HF_TOKEN")
|
34 |
+
CACHE_EXAMPLES = torch.cuda.is_available() and os.getenv("CACHE_EXAMPLES") == "1"
|
35 |
+
MIN_IMAGE_SIZE = 512
|
36 |
+
MAX_IMAGE_SIZE = 2048
|
37 |
+
USE_TORCH_COMPILE = os.getenv("USE_TORCH_COMPILE") == "1"
|
38 |
+
ENABLE_CPU_OFFLOAD = os.getenv("ENABLE_CPU_OFFLOAD") == "1"
|
39 |
+
OUTPUT_DIR = os.getenv("OUTPUT_DIR", "./outputs")
|
40 |
+
|
41 |
+
MODEL = os.getenv(
|
42 |
+
"MODEL",
|
43 |
+
"https://huggingface.co/cagliostrolab/animagine-xl-3.1/blob/main/animagine-xl-3.1.safetensors",
|
44 |
+
)
|
45 |
+
LORA_MODEL ="galverse/mama-1.5"
|
46 |
+
|
47 |
+
torch.backends.cudnn.deterministic = True
|
48 |
+
torch.backends.cudnn.benchmark = False
|
49 |
+
|
50 |
+
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
51 |
+
|
52 |
+
|
53 |
+
|
54 |
+
def load_pipeline(model_name):
|
55 |
+
vae = AutoencoderKL.from_pretrained(
|
56 |
+
"madebyollin/sdxl-vae-fp16-fix",
|
57 |
+
torch_dtype=torch.float16,
|
58 |
+
)
|
59 |
+
pipeline = (
|
60 |
+
StableDiffusionXLPipeline.from_single_file
|
61 |
+
if MODEL.endswith(".safetensors")
|
62 |
+
else StableDiffusionXLPipeline.from_pretrained
|
63 |
+
)
|
64 |
+
|
65 |
+
pipe = pipeline(
|
66 |
+
model_name,
|
67 |
+
vae=vae,
|
68 |
+
torch_dtype=torch.float16,
|
69 |
+
custom_pipeline="lpw_stable_diffusion_xl",
|
70 |
+
use_safetensors=True,
|
71 |
+
add_watermarker=False,
|
72 |
+
use_auth_token=HF_TOKEN,
|
73 |
+
)
|
74 |
+
|
75 |
+
pipe.unload_lora_weights()
|
76 |
+
pipe.load_lora_weights(LORA_MODEL ) #Add in lora to pipe
|
77 |
+
|
78 |
+
pipe.to(device)
|
79 |
+
return pipe
|
80 |
+
|
81 |
+
|
82 |
+
@spaces.GPU
|
83 |
+
def generate(
|
84 |
+
prompt: str,
|
85 |
+
negative_prompt: str = "",
|
86 |
+
lora_scale: float = 1.0, #added in lora strength
|
87 |
+
seed: int = 0,
|
88 |
+
custom_width: int = 1024,
|
89 |
+
custom_height: int = 1024,
|
90 |
+
guidance_scale: float = 7.0,
|
91 |
+
num_inference_steps: int = 28,
|
92 |
+
sampler: str = "Euler a",
|
93 |
+
aspect_ratio_selector: str = "896 x 1152",
|
94 |
+
style_selector: str = "(None)",
|
95 |
+
quality_selector: str = "Standard v3.1",
|
96 |
+
use_upscaler: bool = False,
|
97 |
+
upscaler_strength: float = 0.55,
|
98 |
+
upscale_by: float = 1.5,
|
99 |
+
add_quality_tags: bool = True,
|
100 |
+
progress=gr.Progress(track_tqdm=True),
|
101 |
+
):
|
102 |
+
generator = utils.seed_everything(seed)
|
103 |
+
|
104 |
+
width, height = utils.aspect_ratio_handler(
|
105 |
+
aspect_ratio_selector,
|
106 |
+
custom_width,
|
107 |
+
custom_height,
|
108 |
+
)
|
109 |
+
|
110 |
+
|
111 |
+
|
112 |
+
prompt, negative_prompt = utils.preprocess_prompt(
|
113 |
+
quality_prompt, quality_selector, prompt, negative_prompt, add_quality_tags
|
114 |
+
)
|
115 |
+
prompt, negative_prompt = utils.preprocess_prompt(
|
116 |
+
styles, style_selector, prompt, negative_prompt
|
117 |
+
)
|
118 |
+
|
119 |
+
width, height = utils.preprocess_image_dimensions(width, height)
|
120 |
+
|
121 |
+
backup_scheduler = pipe.scheduler
|
122 |
+
pipe.scheduler = utils.get_scheduler(pipe.scheduler.config, sampler)
|
123 |
+
|
124 |
+
if use_upscaler:
|
125 |
+
upscaler_pipe = StableDiffusionXLImg2ImgPipeline(**pipe.components)
|
126 |
+
metadata = {
|
127 |
+
"prompt": prompt,
|
128 |
+
"negative_prompt": negative_prompt,
|
129 |
+
"resolution": f"{width} x {height}",
|
130 |
+
"guidance_scale": guidance_scale,
|
131 |
+
"num_inference_steps": num_inference_steps,
|
132 |
+
"seed": seed,
|
133 |
+
"sampler": sampler,
|
134 |
+
"sdxl_style": style_selector,
|
135 |
+
"add_quality_tags": add_quality_tags,
|
136 |
+
"quality_tags": quality_selector,
|
137 |
+
}
|
138 |
+
#add in lora
|
139 |
+
metadata["cross_attention_kwargs"] = {"scale": lora_scale}
|
140 |
+
|
141 |
+
if use_upscaler:
|
142 |
+
new_width = int(width * upscale_by)
|
143 |
+
new_height = int(height * upscale_by)
|
144 |
+
metadata["use_upscaler"] = {
|
145 |
+
"upscale_method": "nearest-exact",
|
146 |
+
"upscaler_strength": upscaler_strength,
|
147 |
+
"upscale_by": upscale_by,
|
148 |
+
"new_resolution": f"{new_width} x {new_height}",
|
149 |
+
}
|
150 |
+
else:
|
151 |
+
metadata["use_upscaler"] = None
|
152 |
+
metadata["Model"] = {
|
153 |
+
"Model": DESCRIPTION,
|
154 |
+
"Model hash": "e3c47aedb0",
|
155 |
+
}
|
156 |
+
|
157 |
+
logger.info(json.dumps(metadata, indent=4))
|
158 |
+
|
159 |
+
try:
|
160 |
+
if use_upscaler:
|
161 |
+
latents = pipe(
|
162 |
+
prompt=prompt,
|
163 |
+
negative_prompt=negative_prompt,
|
164 |
+
width=width,
|
165 |
+
height=height,
|
166 |
+
guidance_scale=guidance_scale,
|
167 |
+
num_inference_steps=num_inference_steps,
|
168 |
+
generator=generator,
|
169 |
+
cross_attention_kwargs={"scale": lora_scale}, #add in lora scale setting
|
170 |
+
output_type="latent",
|
171 |
+
).images
|
172 |
+
upscaled_latents = utils.upscale(latents, "nearest-exact", upscale_by)
|
173 |
+
images = upscaler_pipe(
|
174 |
+
prompt=prompt,
|
175 |
+
negative_prompt=negative_prompt,
|
176 |
+
image=upscaled_latents,
|
177 |
+
guidance_scale=guidance_scale,
|
178 |
+
num_inference_steps=num_inference_steps,
|
179 |
+
strength=upscaler_strength,
|
180 |
+
generator=generator,
|
181 |
+
output_type="pil",
|
182 |
+
).images
|
183 |
+
else:
|
184 |
+
images = pipe(
|
185 |
+
prompt=prompt,
|
186 |
+
negative_prompt=negative_prompt,
|
187 |
+
width=width,
|
188 |
+
height=height,
|
189 |
+
guidance_scale=guidance_scale,
|
190 |
+
num_inference_steps=num_inference_steps,
|
191 |
+
generator=generator,
|
192 |
+
cross_attention_kwargs={"scale": lora_scale}, #add in lora scale setting
|
193 |
+
output_type="pil",
|
194 |
+
).images
|
195 |
+
|
196 |
+
if images:
|
197 |
+
image_paths = [
|
198 |
+
utils.save_image(image, metadata, OUTPUT_DIR, IS_COLAB)
|
199 |
+
for image in images
|
200 |
+
]
|
201 |
+
|
202 |
+
for image_path in image_paths:
|
203 |
+
logger.info(f"Image saved as {image_path} with metadata")
|
204 |
+
|
205 |
+
return image_paths, metadata
|
206 |
+
except Exception as e:
|
207 |
+
logger.exception(f"An error occurred: {e}")
|
208 |
+
raise
|
209 |
+
finally:
|
210 |
+
if use_upscaler:
|
211 |
+
del upscaler_pipe
|
212 |
+
pipe.scheduler = backup_scheduler
|
213 |
+
utils.free_memory()
|
214 |
+
|
215 |
+
|
216 |
+
if torch.cuda.is_available():
|
217 |
+
pipe = load_pipeline(MODEL)
|
218 |
+
logger.info("Loaded on Device!")
|
219 |
+
else:
|
220 |
+
pipe = None
|
221 |
+
|
222 |
+
styles = {k["name"]: (k["prompt"], k["negative_prompt"]) for k in config.style_list}
|
223 |
+
quality_prompt = {
|
224 |
+
k["name"]: (k["prompt"], k["negative_prompt"]) for k in config.quality_prompt_list
|
225 |
+
}
|
226 |
+
|
227 |
+
|
228 |
+
|
229 |
+
with gr.Blocks(css="style.css", theme="NoCrypt/miku@1.2.1") as demo:
|
230 |
+
title = gr.HTML(
|
231 |
+
f"""<h1><span>{DESCRIPTION}</span></h1>""",
|
232 |
+
elem_id="title",
|
233 |
+
)
|
234 |
+
gr.Markdown(
|
235 |
+
f"""Gradio demo for [cagliostrolab/animagine-xl-3.1](https://huggingface.co/cagliostrolab/animagine-xl-3.1)""",
|
236 |
+
elem_id="subtitle",
|
237 |
+
)
|
238 |
+
gr.DuplicateButton(
|
239 |
+
value="Duplicate Space for private use",
|
240 |
+
elem_id="duplicate-button",
|
241 |
+
visible=os.getenv("SHOW_DUPLICATE_BUTTON") == "1",
|
242 |
+
)
|
243 |
+
with gr.Row():
|
244 |
+
with gr.Column(scale=2):
|
245 |
+
with gr.Tab("Txt2img"):
|
246 |
+
with gr.Group():
|
247 |
+
prompt = gr.Text(
|
248 |
+
label="Prompt",
|
249 |
+
max_lines=5,
|
250 |
+
placeholder="Enter your prompt",
|
251 |
+
)
|
252 |
+
negative_prompt = gr.Text(
|
253 |
+
label="Negative Prompt",
|
254 |
+
max_lines=5,
|
255 |
+
placeholder="Enter a negative prompt",
|
256 |
+
)
|
257 |
+
lora_scale = gr.Slider(
|
258 |
+
label="lora strength",
|
259 |
+
minimum=0,
|
260 |
+
maximum=1.2,
|
261 |
+
step=0.1,
|
262 |
+
value=1,
|
263 |
+
)
|
264 |
+
with gr.Accordion(label="Quality Tags", open=True):
|
265 |
+
add_quality_tags = gr.Checkbox(
|
266 |
+
label="Add Quality Tags", value=True
|
267 |
+
)
|
268 |
+
quality_selector = gr.Dropdown(
|
269 |
+
label="Quality Tags Presets",
|
270 |
+
interactive=True,
|
271 |
+
choices=list(quality_prompt.keys()),
|
272 |
+
value="Standard v3.1",
|
273 |
+
)
|
274 |
+
with gr.Tab("Advanced Settings"):
|
275 |
+
with gr.Group():
|
276 |
+
style_selector = gr.Radio(
|
277 |
+
label="Style Preset",
|
278 |
+
container=True,
|
279 |
+
interactive=True,
|
280 |
+
choices=list(styles.keys()),
|
281 |
+
value="(None)",
|
282 |
+
)
|
283 |
+
with gr.Group():
|
284 |
+
aspect_ratio_selector = gr.Radio(
|
285 |
+
label="Aspect Ratio",
|
286 |
+
choices=config.aspect_ratios,
|
287 |
+
value="896 x 1152",
|
288 |
+
container=True,
|
289 |
+
)
|
290 |
+
with gr.Group(visible=False) as custom_resolution:
|
291 |
+
with gr.Row():
|
292 |
+
custom_width = gr.Slider(
|
293 |
+
label="Width",
|
294 |
+
minimum=MIN_IMAGE_SIZE,
|
295 |
+
maximum=MAX_IMAGE_SIZE,
|
296 |
+
step=8,
|
297 |
+
value=1024,
|
298 |
+
)
|
299 |
+
custom_height = gr.Slider(
|
300 |
+
label="Height",
|
301 |
+
minimum=MIN_IMAGE_SIZE,
|
302 |
+
maximum=MAX_IMAGE_SIZE,
|
303 |
+
step=8,
|
304 |
+
value=1024,
|
305 |
+
)
|
306 |
+
with gr.Group():
|
307 |
+
use_upscaler = gr.Checkbox(label="Use Upscaler", value=False)
|
308 |
+
with gr.Row() as upscaler_row:
|
309 |
+
upscaler_strength = gr.Slider(
|
310 |
+
label="Strength",
|
311 |
+
minimum=0,
|
312 |
+
maximum=1,
|
313 |
+
step=0.05,
|
314 |
+
value=0.55,
|
315 |
+
visible=False,
|
316 |
+
)
|
317 |
+
upscale_by = gr.Slider(
|
318 |
+
label="Upscale by",
|
319 |
+
minimum=1,
|
320 |
+
maximum=1.5,
|
321 |
+
step=0.1,
|
322 |
+
value=1.5,
|
323 |
+
visible=False,
|
324 |
+
)
|
325 |
+
with gr.Group():
|
326 |
+
sampler = gr.Dropdown(
|
327 |
+
label="Sampler",
|
328 |
+
choices=config.sampler_list,
|
329 |
+
interactive=True,
|
330 |
+
value="Euler a",
|
331 |
+
)
|
332 |
+
with gr.Group():
|
333 |
+
seed = gr.Slider(
|
334 |
+
label="Seed", minimum=0, maximum=utils.MAX_SEED, step=1, value=0
|
335 |
+
)
|
336 |
+
randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
|
337 |
+
with gr.Group():
|
338 |
+
with gr.Row():
|
339 |
+
guidance_scale = gr.Slider(
|
340 |
+
label="Guidance scale",
|
341 |
+
minimum=1,
|
342 |
+
maximum=12,
|
343 |
+
step=0.1,
|
344 |
+
value=7.0,
|
345 |
+
)
|
346 |
+
num_inference_steps = gr.Slider(
|
347 |
+
label="Number of inference steps",
|
348 |
+
minimum=1,
|
349 |
+
maximum=50,
|
350 |
+
step=1,
|
351 |
+
value=28,
|
352 |
+
)
|
353 |
+
with gr.Column(scale=3):
|
354 |
+
with gr.Blocks():
|
355 |
+
run_button = gr.Button("Generate", variant="primary")
|
356 |
+
result = gr.Gallery(
|
357 |
+
label="Result",
|
358 |
+
columns=1,
|
359 |
+
height='100%',
|
360 |
+
preview=True,
|
361 |
+
show_label=False
|
362 |
+
)
|
363 |
+
with gr.Accordion(label="Generation Parameters", open=False):
|
364 |
+
gr_metadata = gr.JSON(label="metadata", show_label=False)
|
365 |
+
gr.Examples(
|
366 |
+
examples=config.examples,
|
367 |
+
inputs=prompt,
|
368 |
+
outputs=[result, gr_metadata],
|
369 |
+
fn=lambda *args, **kwargs: generate(*args, use_upscaler=True, **kwargs),
|
370 |
+
cache_examples=CACHE_EXAMPLES,
|
371 |
+
)
|
372 |
+
use_upscaler.change(
|
373 |
+
fn=lambda x: [gr.update(visible=x), gr.update(visible=x)],
|
374 |
+
inputs=use_upscaler,
|
375 |
+
outputs=[upscaler_strength, upscale_by],
|
376 |
+
queue=False,
|
377 |
+
api_name=False,
|
378 |
+
)
|
379 |
+
aspect_ratio_selector.change(
|
380 |
+
fn=lambda x: gr.update(visible=x == "Custom"),
|
381 |
+
inputs=aspect_ratio_selector,
|
382 |
+
outputs=custom_resolution,
|
383 |
+
queue=False,
|
384 |
+
api_name=False,
|
385 |
+
)
|
386 |
+
|
387 |
+
gr.on(
|
388 |
+
triggers=[
|
389 |
+
prompt.submit,
|
390 |
+
negative_prompt.submit,
|
391 |
+
run_button.click,
|
392 |
+
],
|
393 |
+
fn=utils.randomize_seed_fn,
|
394 |
+
inputs=[seed, randomize_seed],
|
395 |
+
outputs=seed,
|
396 |
+
queue=False,
|
397 |
+
api_name=False,
|
398 |
+
).then(
|
399 |
+
fn=generate,
|
400 |
+
inputs=[
|
401 |
+
prompt,
|
402 |
+
negative_prompt,
|
403 |
+
lora_scale,
|
404 |
+
seed,
|
405 |
+
custom_width,
|
406 |
+
custom_height,
|
407 |
+
guidance_scale,
|
408 |
+
num_inference_steps,
|
409 |
+
sampler,
|
410 |
+
aspect_ratio_selector,
|
411 |
+
style_selector,
|
412 |
+
quality_selector,
|
413 |
+
use_upscaler,
|
414 |
+
upscaler_strength,
|
415 |
+
upscale_by,
|
416 |
+
add_quality_tags,
|
417 |
+
],
|
418 |
+
outputs=[result, gr_metadata],
|
419 |
+
api_name="run",
|
420 |
+
)
|
421 |
+
|
422 |
+
if __name__ == "__main__":
|
423 |
+
demo.queue(max_size=20).launch(debug=IS_COLAB, share=IS_COLAB)
|
config.py
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
examples = [
|
2 |
+
"1girl, souryuu asuka langley, neon genesis evangelion, plugsuit, pilot suit, red bodysuit, sitting, crossing legs, black eye patch, cat hat, throne, symmetrical, looking down, from bottom, looking at viewer, outdoors",
|
3 |
+
"1boy, male focus, yuuki makoto \(persona 3\), persona 3, black jacket, white shirt, long sleeves, closed mouth, glowing eyes, gun, hair over one eye, holding gun, handgun, looking at viewer, solo, upper body",
|
4 |
+
"1girl, makima \(chainsaw man\), chainsaw man, black jacket, black necktie, black pants, braid, business suit, fingernails, formal, hand on own chin, jacket on shoulders, light smile, long sleeves, looking at viewer, looking up, medium breasts, office lady, smile, solo, suit, upper body, white shirt, outdoors",
|
5 |
+
"1boy, male focus, gojou satoru, jujutsu kaisen, black jacket, blindfold lift, blue eyes, glowing, glowing eyes, high collar, jacket, jujutsu tech uniform, solo, grin, white hair",
|
6 |
+
"1girl, cagliostro, granblue fantasy, violet eyes, standing, hand on own chin, looking at object, smile, closed mouth, table, beaker, glass tube, experiment apparatus, dark room, laboratory",
|
7 |
+
"kimi no na wa., building, cityscape, cloud, cloudy sky, gradient sky, lens flare, no humans, outdoors, power lines, scenery, shooting star, sky, sparkle, star \(sky\), starry sky, sunset, tree, utility pole",
|
8 |
+
]
|
9 |
+
|
10 |
+
quality_prompt_list = [
|
11 |
+
{
|
12 |
+
"name": "(None)",
|
13 |
+
"prompt": "{prompt}",
|
14 |
+
"negative_prompt": "nsfw, lowres",
|
15 |
+
},
|
16 |
+
{
|
17 |
+
"name": "Standard v3.0",
|
18 |
+
"prompt": "{prompt}, masterpiece, best quality",
|
19 |
+
"negative_prompt": "nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, artist name",
|
20 |
+
},
|
21 |
+
{
|
22 |
+
"name": "Standard v3.1",
|
23 |
+
"prompt": "{prompt}, masterpiece, best quality, very aesthetic, absurdres",
|
24 |
+
"negative_prompt": "nsfw, lowres, (bad), text, error, fewer, extra, missing, worst quality, jpeg artifacts, low quality, watermark, unfinished, displeasing, oldest, early, chromatic aberration, signature, extra digits, artistic error, username, scan, [abstract]",
|
25 |
+
},
|
26 |
+
{
|
27 |
+
"name": "Light v3.1",
|
28 |
+
"prompt": "{prompt}, (masterpiece), best quality, very aesthetic, perfect face",
|
29 |
+
"negative_prompt": "nsfw, (low quality, worst quality:1.2), very displeasing, 3d, watermark, signature, ugly, poorly drawn",
|
30 |
+
},
|
31 |
+
{
|
32 |
+
"name": "Heavy v3.1",
|
33 |
+
"prompt": "{prompt}, (masterpiece), (best quality), (ultra-detailed), very aesthetic, illustration, disheveled hair, perfect composition, moist skin, intricate details",
|
34 |
+
"negative_prompt": "nsfw, longbody, lowres, bad anatomy, bad hands, missing fingers, pubic hair, extra digit, fewer digits, cropped, worst quality, low quality, very displeasing",
|
35 |
+
},
|
36 |
+
]
|
37 |
+
|
38 |
+
sampler_list = [
|
39 |
+
"DPM++ 2M Karras",
|
40 |
+
"DPM++ SDE Karras",
|
41 |
+
"DPM++ 2M SDE Karras",
|
42 |
+
"Euler",
|
43 |
+
"Euler a",
|
44 |
+
"DDIM",
|
45 |
+
]
|
46 |
+
|
47 |
+
aspect_ratios = [
|
48 |
+
"1024 x 1024",
|
49 |
+
"1152 x 896",
|
50 |
+
"896 x 1152",
|
51 |
+
"1216 x 832",
|
52 |
+
"832 x 1216",
|
53 |
+
"1344 x 768",
|
54 |
+
"768 x 1344",
|
55 |
+
"1536 x 640",
|
56 |
+
"640 x 1536",
|
57 |
+
"Custom",
|
58 |
+
]
|
59 |
+
|
60 |
+
style_list = [
|
61 |
+
{
|
62 |
+
"name": "(None)",
|
63 |
+
"prompt": "{prompt}",
|
64 |
+
"negative_prompt": "",
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"name": "Cinematic",
|
68 |
+
"prompt": "{prompt}, cinematic still, emotional, harmonious, vignette, highly detailed, high budget, bokeh, cinemascope, moody, epic, gorgeous, film grain, grainy",
|
69 |
+
"negative_prompt": "nsfw, cartoon, graphic, text, painting, crayon, graphite, abstract, glitch, deformed, mutated, ugly, disfigured",
|
70 |
+
},
|
71 |
+
{
|
72 |
+
"name": "Photographic",
|
73 |
+
"prompt": "{prompt}, cinematic photo, 35mm photograph, film, bokeh, professional, 4k, highly detailed",
|
74 |
+
"negative_prompt": "nsfw, drawing, painting, crayon, sketch, graphite, impressionist, noisy, blurry, soft, deformed, ugly",
|
75 |
+
},
|
76 |
+
{
|
77 |
+
"name": "Anime",
|
78 |
+
"prompt": "{prompt}, anime artwork, anime style, key visual, vibrant, studio anime, highly detailed",
|
79 |
+
"negative_prompt": "nsfw, photo, deformed, black and white, realism, disfigured, low contrast",
|
80 |
+
},
|
81 |
+
{
|
82 |
+
"name": "Manga",
|
83 |
+
"prompt": "{prompt}, manga style, vibrant, high-energy, detailed, iconic, Japanese comic style",
|
84 |
+
"negative_prompt": "nsfw, ugly, deformed, noisy, blurry, low contrast, realism, photorealistic, Western comic style",
|
85 |
+
},
|
86 |
+
{
|
87 |
+
"name": "Digital Art",
|
88 |
+
"prompt": "{prompt}, concept art, digital artwork, illustrative, painterly, matte painting, highly detailed",
|
89 |
+
"negative_prompt": "nsfw, photo, photorealistic, realism, ugly",
|
90 |
+
},
|
91 |
+
{
|
92 |
+
"name": "Pixel art",
|
93 |
+
"prompt": "{prompt}, pixel-art, low-res, blocky, pixel art style, 8-bit graphics",
|
94 |
+
"negative_prompt": "nsfw, sloppy, messy, blurry, noisy, highly detailed, ultra textured, photo, realistic",
|
95 |
+
},
|
96 |
+
{
|
97 |
+
"name": "Fantasy art",
|
98 |
+
"prompt": "{prompt}, ethereal fantasy concept art, magnificent, celestial, ethereal, painterly, epic, majestic, magical, fantasy art, cover art, dreamy",
|
99 |
+
"negative_prompt": "nsfw, photographic, realistic, realism, 35mm film, dslr, cropped, frame, text, deformed, glitch, noise, noisy, off-center, deformed, cross-eyed, closed eyes, bad anatomy, ugly, disfigured, sloppy, duplicate, mutated, black and white",
|
100 |
+
},
|
101 |
+
{
|
102 |
+
"name": "Neonpunk",
|
103 |
+
"prompt": "{prompt}, neonpunk style, cyberpunk, vaporwave, neon, vibes, vibrant, stunningly beautiful, crisp, detailed, sleek, ultramodern, magenta highlights, dark purple shadows, high contrast, cinematic, ultra detailed, intricate, professional",
|
104 |
+
"negative_prompt": "nsfw, painting, drawing, illustration, glitch, deformed, mutated, cross-eyed, ugly, disfigured",
|
105 |
+
},
|
106 |
+
{
|
107 |
+
"name": "3D Model",
|
108 |
+
"prompt": "{prompt}, professional 3d model, octane render, highly detailed, volumetric, dramatic lighting",
|
109 |
+
"negative_prompt": "nsfw, ugly, deformed, noisy, low poly, blurry, painting",
|
110 |
+
},
|
111 |
+
]
|
requirements.txt
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
accelerate==0.27.2
|
2 |
+
diffusers==0.26.3
|
3 |
+
gradio==4.20.0
|
4 |
+
invisible-watermark==0.2.0
|
5 |
+
Pillow==10.2.0
|
6 |
+
spaces==0.24.0
|
7 |
+
torch==2.0.1
|
8 |
+
transformers==4.38.1
|
9 |
+
omegaconf==2.3.0
|
10 |
+
timm==0.9.10
|
style.css
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
:root {
|
2 |
+
--title-font-size: clamp(1.5rem, 6vw, 3rem);
|
3 |
+
--subtitle-font-size: clamp(1rem, 2vw, 1.2rem);
|
4 |
+
}
|
5 |
+
|
6 |
+
h1 {
|
7 |
+
text-align: center;
|
8 |
+
font-size: var(--title-font-size);
|
9 |
+
display: block;
|
10 |
+
}
|
11 |
+
|
12 |
+
h2 {
|
13 |
+
text-align: center;
|
14 |
+
font-size: 2rem;
|
15 |
+
display: block;
|
16 |
+
}
|
17 |
+
|
18 |
+
#duplicate-button {
|
19 |
+
display: block;
|
20 |
+
margin: 1rem auto;
|
21 |
+
color: #fff;
|
22 |
+
background: #1565c0;
|
23 |
+
border-radius: 100vh;
|
24 |
+
padding: 0.5rem 1rem;
|
25 |
+
}
|
26 |
+
|
27 |
+
#component-0 {
|
28 |
+
max-width: 85%;
|
29 |
+
margin: 2rem auto;
|
30 |
+
padding: 2rem;
|
31 |
+
}
|
32 |
+
|
33 |
+
@media (max-width: 600px) {
|
34 |
+
#component-0 {
|
35 |
+
max-width: 100%;
|
36 |
+
padding: 0.5rem;
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
+
#title-container {
|
41 |
+
text-align: center;
|
42 |
+
padding: 2rem 0;
|
43 |
+
}
|
44 |
+
|
45 |
+
#title {
|
46 |
+
font-size: var(--title-font-size);
|
47 |
+
color: #333;
|
48 |
+
font-family: 'Helvetica Neue', sans-serif;
|
49 |
+
text-transform: uppercase;
|
50 |
+
background: transparent;
|
51 |
+
}
|
52 |
+
|
53 |
+
#title span {
|
54 |
+
background: linear-gradient(45deg, #4EACEF, #28b485);
|
55 |
+
background-clip: text;
|
56 |
+
color: transparent;
|
57 |
+
}
|
58 |
+
|
59 |
+
#subtitle {
|
60 |
+
text-align: center;
|
61 |
+
font-size: var(--subtitle-font-size);
|
62 |
+
margin-top: 1rem;
|
63 |
+
}
|
utils.py
ADDED
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gc
|
2 |
+
import os
|
3 |
+
import random
|
4 |
+
import numpy as np
|
5 |
+
import json
|
6 |
+
import torch
|
7 |
+
import uuid
|
8 |
+
from PIL import Image, PngImagePlugin
|
9 |
+
from datetime import datetime
|
10 |
+
from dataclasses import dataclass
|
11 |
+
from typing import Callable, Dict, Optional, Tuple
|
12 |
+
from diffusers import (
|
13 |
+
DDIMScheduler,
|
14 |
+
DPMSolverMultistepScheduler,
|
15 |
+
DPMSolverSinglestepScheduler,
|
16 |
+
EulerAncestralDiscreteScheduler,
|
17 |
+
EulerDiscreteScheduler,
|
18 |
+
)
|
19 |
+
|
20 |
+
MAX_SEED = np.iinfo(np.int32).max
|
21 |
+
|
22 |
+
|
23 |
+
@dataclass
|
24 |
+
class StyleConfig:
|
25 |
+
prompt: str
|
26 |
+
negative_prompt: str
|
27 |
+
|
28 |
+
|
29 |
+
def randomize_seed_fn(seed: int, randomize_seed: bool) -> int:
|
30 |
+
if randomize_seed:
|
31 |
+
seed = random.randint(0, MAX_SEED)
|
32 |
+
return seed
|
33 |
+
|
34 |
+
|
35 |
+
def seed_everything(seed: int) -> torch.Generator:
|
36 |
+
torch.manual_seed(seed)
|
37 |
+
torch.cuda.manual_seed_all(seed)
|
38 |
+
np.random.seed(seed)
|
39 |
+
generator = torch.Generator()
|
40 |
+
generator.manual_seed(seed)
|
41 |
+
return generator
|
42 |
+
|
43 |
+
|
44 |
+
def parse_aspect_ratio(aspect_ratio: str) -> Optional[Tuple[int, int]]:
|
45 |
+
if aspect_ratio == "Custom":
|
46 |
+
return None
|
47 |
+
width, height = aspect_ratio.split(" x ")
|
48 |
+
return int(width), int(height)
|
49 |
+
|
50 |
+
|
51 |
+
def aspect_ratio_handler(
|
52 |
+
aspect_ratio: str, custom_width: int, custom_height: int
|
53 |
+
) -> Tuple[int, int]:
|
54 |
+
if aspect_ratio == "Custom":
|
55 |
+
return custom_width, custom_height
|
56 |
+
else:
|
57 |
+
width, height = parse_aspect_ratio(aspect_ratio)
|
58 |
+
return width, height
|
59 |
+
|
60 |
+
|
61 |
+
def get_scheduler(scheduler_config: Dict, name: str) -> Optional[Callable]:
|
62 |
+
scheduler_factory_map = {
|
63 |
+
"DPM++ 2M Karras": lambda: DPMSolverMultistepScheduler.from_config(
|
64 |
+
scheduler_config, use_karras_sigmas=True
|
65 |
+
),
|
66 |
+
"DPM++ SDE Karras": lambda: DPMSolverSinglestepScheduler.from_config(
|
67 |
+
scheduler_config, use_karras_sigmas=True
|
68 |
+
),
|
69 |
+
"DPM++ 2M SDE Karras": lambda: DPMSolverMultistepScheduler.from_config(
|
70 |
+
scheduler_config, use_karras_sigmas=True, algorithm_type="sde-dpmsolver++"
|
71 |
+
),
|
72 |
+
"Euler": lambda: EulerDiscreteScheduler.from_config(scheduler_config),
|
73 |
+
"Euler a": lambda: EulerAncestralDiscreteScheduler.from_config(
|
74 |
+
scheduler_config
|
75 |
+
),
|
76 |
+
"DDIM": lambda: DDIMScheduler.from_config(scheduler_config),
|
77 |
+
}
|
78 |
+
return scheduler_factory_map.get(name, lambda: None)()
|
79 |
+
|
80 |
+
|
81 |
+
def free_memory() -> None:
|
82 |
+
torch.cuda.empty_cache()
|
83 |
+
gc.collect()
|
84 |
+
|
85 |
+
|
86 |
+
def preprocess_prompt(
|
87 |
+
style_dict,
|
88 |
+
style_name: str,
|
89 |
+
positive: str,
|
90 |
+
negative: str = "",
|
91 |
+
add_style: bool = True,
|
92 |
+
) -> Tuple[str, str]:
|
93 |
+
p, n = style_dict.get(style_name, style_dict["(None)"])
|
94 |
+
|
95 |
+
if add_style and positive.strip():
|
96 |
+
formatted_positive = p.format(prompt=positive)
|
97 |
+
else:
|
98 |
+
formatted_positive = positive
|
99 |
+
|
100 |
+
combined_negative = n
|
101 |
+
if negative.strip():
|
102 |
+
if combined_negative:
|
103 |
+
combined_negative += ", " + negative
|
104 |
+
else:
|
105 |
+
combined_negative = negative
|
106 |
+
|
107 |
+
return formatted_positive, combined_negative
|
108 |
+
|
109 |
+
|
110 |
+
def common_upscale(
|
111 |
+
samples: torch.Tensor,
|
112 |
+
width: int,
|
113 |
+
height: int,
|
114 |
+
upscale_method: str,
|
115 |
+
) -> torch.Tensor:
|
116 |
+
return torch.nn.functional.interpolate(
|
117 |
+
samples, size=(height, width), mode=upscale_method
|
118 |
+
)
|
119 |
+
|
120 |
+
|
121 |
+
def upscale(
|
122 |
+
samples: torch.Tensor, upscale_method: str, scale_by: float
|
123 |
+
) -> torch.Tensor:
|
124 |
+
width = round(samples.shape[3] * scale_by)
|
125 |
+
height = round(samples.shape[2] * scale_by)
|
126 |
+
return common_upscale(samples, width, height, upscale_method)
|
127 |
+
|
128 |
+
|
129 |
+
|
130 |
+
def get_random_line_from_file(file_path: str) -> str:
|
131 |
+
with open(file_path, "r") as file:
|
132 |
+
lines = file.readlines()
|
133 |
+
if not lines:
|
134 |
+
return ""
|
135 |
+
return random.choice(lines).strip()
|
136 |
+
|
137 |
+
def preprocess_image_dimensions(width, height):
|
138 |
+
if width % 8 != 0:
|
139 |
+
width = width - (width % 8)
|
140 |
+
if height % 8 != 0:
|
141 |
+
height = height - (height % 8)
|
142 |
+
return width, height
|
143 |
+
|
144 |
+
|
145 |
+
def save_image(image, metadata, output_dir, is_colab):
|
146 |
+
if is_colab:
|
147 |
+
current_time = datetime.now().strftime("%Y%m%d_%H%M%S")
|
148 |
+
filename = f"image_{current_time}.png"
|
149 |
+
else:
|
150 |
+
filename = str(uuid.uuid4()) + ".png"
|
151 |
+
os.makedirs(output_dir, exist_ok=True)
|
152 |
+
filepath = os.path.join(output_dir, filename)
|
153 |
+
metadata_str = json.dumps(metadata)
|
154 |
+
info = PngImagePlugin.PngInfo()
|
155 |
+
info.add_text("metadata", metadata_str)
|
156 |
+
image.save(filepath, "PNG", pnginfo=info)
|
157 |
+
return filepath
|
158 |
+
|
159 |
+
|
160 |
+
def is_google_colab():
|
161 |
+
try:
|
162 |
+
import google.colab
|
163 |
+
return True
|
164 |
+
except:
|