fffiloni commited on
Commit
96c3a9e
·
verified ·
1 Parent(s): 71da59a

Update app_zero.py

Browse files
Files changed (1) hide show
  1. app_zero.py +333 -70
app_zero.py CHANGED
@@ -3,13 +3,23 @@ import os
3
  import types
4
  import random
5
  import datetime
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  import torch
8
  import numpy as np
9
  import einops
10
  import spaces
11
  import gradio as gr
12
- import huggingface_hub
13
 
14
  from PIL import Image
15
  from torchvision import transforms
@@ -26,7 +36,9 @@ from diffusers import (
26
  UniPCMultistepScheduler,
27
  )
28
 
29
- # ---- GPU spoof ----
 
 
30
  torch.cuda.get_device_capability = lambda *args, **kwargs: (8, 6)
31
  torch.cuda.get_device_properties = lambda *args, **kwargs: types.SimpleNamespace(
32
  name="NVIDIA A10G",
@@ -36,7 +48,9 @@ torch.cuda.get_device_properties = lambda *args, **kwargs: types.SimpleNamespace
36
  multi_processor_count=80,
37
  )
38
 
39
- # ---- Downloads ----
 
 
40
  huggingface_hub.snapshot_download(
41
  repo_id="camenduru/PASD",
42
  allow_patterns=[
@@ -60,48 +74,168 @@ huggingface_hub.hf_hub_download(
60
  local_dir="PASD/annotator/ckpts",
61
  )
62
 
63
- # ---- PASD ----
 
 
64
  sys.path.append("./PASD")
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  from pipelines.pipeline_pasd import StableDiffusionControlNetPipeline
67
  from myutils.misc import load_dreambooth_lora
68
  from myutils.wavelet_color_fix import wavelet_color_fix
69
  from annotator.retinaface import RetinaFaceDetection
70
 
71
- from models.pasd.unet_2d_condition import UNet2DConditionModel
72
- from models.pasd.controlnet import ControlNetModel
73
 
74
- # ---- Setup ----
 
 
 
 
 
 
 
 
 
75
  pretrained_model_path = "stable-diffusion-v1-5/stable-diffusion-v1-5"
76
  ckpt_path = "PASD/runs/pasd/checkpoint-100000"
77
  dreambooth_lora_path = "PASD/checkpoints/personalized_models/majicmixRealistic_v6.safetensors"
78
 
79
- device = "cuda"
80
  weight_dtype = torch.float16
 
81
 
82
- # ---- Load ----
83
- scheduler = UniPCMultistepScheduler.from_pretrained(pretrained_model_path, subfolder="scheduler")
84
- text_encoder = CLIPTextModel.from_pretrained(pretrained_model_path, subfolder="text_encoder")
85
- tokenizer = CLIPTokenizer.from_pretrained(pretrained_model_path, subfolder="tokenizer")
86
- vae = AutoencoderKL.from_pretrained(pretrained_model_path, subfolder="vae")
87
- feature_extractor = CLIPImageProcessor.from_pretrained(pretrained_model_path, subfolder="feature_extractor")
88
-
89
- unet = UNet2DConditionModel.from_pretrained(ckpt_path, subfolder="unet")
90
- controlnet = ControlNetModel.from_pretrained(ckpt_path, subfolder="controlnet")
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
  vae.requires_grad_(False)
93
  text_encoder.requires_grad_(False)
94
  unet.requires_grad_(False)
95
  controlnet.requires_grad_(False)
96
 
97
- unet, vae, text_encoder = load_dreambooth_lora(unet, vae, text_encoder, dreambooth_lora_path)
 
 
98
 
99
  text_encoder.to(device, dtype=weight_dtype)
100
  vae.to(device, dtype=weight_dtype)
101
  unet.to(device, dtype=weight_dtype)
102
  controlnet.to(device, dtype=weight_dtype)
103
 
104
- pipeline = StableDiffusionControlNetPipeline(
105
  vae=vae,
106
  text_encoder=text_encoder,
107
  tokenizer=tokenizer,
@@ -113,23 +247,36 @@ pipeline = StableDiffusionControlNetPipeline(
113
  requires_safety_checker=False,
114
  )
115
 
116
- pipeline._init_tiled_vae(decoder_tile_size=224)
117
 
118
- # ---- ResNet ----
 
 
119
  weights = ResNet50_Weights.DEFAULT
120
  preprocess = weights.transforms()
121
  resnet = resnet50(weights=weights)
122
  resnet.eval()
123
 
124
- # ---- Utils ----
125
- def resize_image(image_path, target_height):
126
  with Image.open(image_path) as img:
127
  ratio = target_height / float(img.size[1])
128
  new_width = int(float(img.size[0]) * ratio)
129
  return img.resize((new_width, target_height), Image.LANCZOS)
130
 
 
131
  @spaces.GPU(enable_queue=True)
132
- def inference(input_image, prompt, a_prompt, n_prompt, steps, upscale, alpha, cfg, seed):
 
 
 
 
 
 
 
 
 
 
133
  if seed == -1:
134
  seed = 0
135
 
@@ -138,35 +285,53 @@ def inference(input_image, prompt, a_prompt, n_prompt, steps, upscale, alpha, cf
138
 
139
  with torch.no_grad():
140
  seed_everything(seed)
141
- generator = torch.Generator(device=device).manual_seed(seed)
 
142
 
143
  input_image = input_image.convert("RGB")
144
 
145
- prompt = a_prompt if prompt == "" else f"{prompt}, {a_prompt}"
 
 
 
 
146
 
147
- ori_w, ori_h = input_image.size
148
- rscale = upscale
149
 
150
- input_image = input_image.resize((input_image.size[0]*rscale, input_image.size[1]*rscale))
151
- input_image = input_image.resize((input_image.size[0]//8*8, input_image.size[1]//8*8))
152
 
153
- w, h = input_image.size
154
 
155
- image = pipeline(
156
- None,
157
- prompt,
158
- input_image,
159
- num_inference_steps=steps,
160
- generator=generator,
161
- height=h,
162
- width=w,
163
- guidance_scale=cfg,
164
- negative_prompt=n_prompt,
165
- conditioning_scale=alpha,
166
- ).images[0]
167
-
168
- image = wavelet_color_fix(image, input_image)
169
- image = image.resize((ori_w*rscale, ori_h*rscale))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
  result_path = f"result_{timestamp}.jpg"
172
  input_path = f"input_{timestamp}.jpg"
@@ -176,30 +341,128 @@ def inference(input_image, prompt, a_prompt, n_prompt, steps, upscale, alpha, cf
176
 
177
  return input_path, result_path, result_path
178
 
179
- # ---- UI ----
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  with gr.Blocks() as demo:
181
- with gr.Row():
182
- with gr.Column():
183
- input_image = gr.Image(type="filepath")
184
- prompt = gr.Textbox(label="Prompt")
185
-
186
- steps = gr.Slider(10, 50, 20)
187
- upscale = gr.Slider(1, 4, 2)
188
- alpha = gr.Slider(0.5, 1.5, 1.1)
189
- cfg = gr.Slider(0.1, 10.0, 7.5)
190
- seed = gr.Slider(-1, 2147483647, randomize=True)
191
-
192
- btn = gr.Button("Generate")
193
-
194
- with gr.Column():
195
- before = gr.Image()
196
- after = gr.Image()
197
- file = gr.File()
198
-
199
- btn.click(
200
- inference,
201
- inputs=[input_image, prompt, prompt, prompt, steps, upscale, alpha, cfg, seed],
202
- outputs=[before, after, file],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  )
204
 
205
- demo.queue().launch(ssr_mode=False, mcp_server=False)
 
 
 
 
 
3
  import types
4
  import random
5
  import datetime
6
+ from pathlib import Path
7
+
8
+ import huggingface_hub
9
+
10
+ # -------------------------------------------------------------------
11
+ # Compatibility shim: older diffusers may still expect cached_download
12
+ # -------------------------------------------------------------------
13
+ if not hasattr(huggingface_hub, "cached_download"):
14
+ def cached_download(*args, **kwargs):
15
+ return huggingface_hub.hf_hub_download(*args, **kwargs)
16
+ huggingface_hub.cached_download = cached_download
17
 
18
  import torch
19
  import numpy as np
20
  import einops
21
  import spaces
22
  import gradio as gr
 
23
 
24
  from PIL import Image
25
  from torchvision import transforms
 
36
  UniPCMultistepScheduler,
37
  )
38
 
39
+ # -------------------------------------------------------------------
40
+ # GPU spoof for Spaces env compatibility
41
+ # -------------------------------------------------------------------
42
  torch.cuda.get_device_capability = lambda *args, **kwargs: (8, 6)
43
  torch.cuda.get_device_properties = lambda *args, **kwargs: types.SimpleNamespace(
44
  name="NVIDIA A10G",
 
48
  multi_processor_count=80,
49
  )
50
 
51
+ # -------------------------------------------------------------------
52
+ # Download required assets
53
+ # -------------------------------------------------------------------
54
  huggingface_hub.snapshot_download(
55
  repo_id="camenduru/PASD",
56
  allow_patterns=[
 
74
  local_dir="PASD/annotator/ckpts",
75
  )
76
 
77
+ # -------------------------------------------------------------------
78
+ # PASD local path
79
+ # -------------------------------------------------------------------
80
  sys.path.append("./PASD")
81
 
82
+
83
+ # -------------------------------------------------------------------
84
+ # Runtime patching for PASD legacy imports
85
+ # -------------------------------------------------------------------
86
+ def patch_file(path_str: str, replacements: list[tuple[str, str]]) -> None:
87
+ path = Path(path_str)
88
+ if not path.exists():
89
+ print(f"[patch] file not found: {path}")
90
+ return
91
+
92
+ try:
93
+ text = path.read_text(encoding="utf-8")
94
+ except Exception as e:
95
+ print(f"[patch] failed reading {path}: {e}")
96
+ return
97
+
98
+ original = text
99
+
100
+ for old, new in replacements:
101
+ text = text.replace(old, new)
102
+
103
+ if text != original:
104
+ try:
105
+ path.write_text(text, encoding="utf-8")
106
+ print(f"[patch] updated: {path}")
107
+ except Exception as e:
108
+ print(f"[patch] failed writing {path}: {e}")
109
+ else:
110
+ print(f"[patch] no changes: {path}")
111
+
112
+
113
+ def patch_pasd_for_diffusers() -> None:
114
+ # 1) pipeline_utils path moved
115
+ patch_file(
116
+ "./PASD/pipelines/pipeline_pasd.py",
117
+ [
118
+ (
119
+ "from diffusers.pipeline_utils import DiffusionPipeline",
120
+ "from diffusers import DiffusionPipeline",
121
+ ),
122
+ ],
123
+ )
124
+
125
+ # 2) PositionNet was renamed/replaced by GLIGENTextBoundingboxProjection
126
+ # This patch handles the common legacy multiline import block.
127
+ patch_file(
128
+ "./PASD/models/pasd/unet_2d_condition.py",
129
+ [
130
+ (
131
+ " PositionNet,\n",
132
+ "",
133
+ ),
134
+ (
135
+ " GLIGENTextBoundingboxProjection,\n",
136
+ " GLIGENTextBoundingboxProjection as PositionNet,\n",
137
+ ),
138
+ ],
139
+ )
140
+
141
+ # 3) internal module paths moved in newer diffusers
142
+ patch_file(
143
+ "./PASD/models/pasd/unet_2d_blocks.py",
144
+ [
145
+ (
146
+ "from diffusers.models.attention import AdaGroupNorm",
147
+ "from diffusers.models.normalization import AdaGroupNorm",
148
+ ),
149
+ (
150
+ "from diffusers.models.dual_transformer_2d import DualTransformer2DModel",
151
+ "from diffusers.models.transformers.dual_transformer_2d import DualTransformer2DModel",
152
+ ),
153
+ (
154
+ "from diffusers.models.transformer_2d import Transformer2DModel",
155
+ "from diffusers.models.transformers.transformer_2d import Transformer2DModel",
156
+ ),
157
+ ],
158
+ )
159
+
160
+ # 4) loader mixin path/name changed across diffusers versions
161
+ patch_file(
162
+ "./PASD/models/pasd/controlnet.py",
163
+ [
164
+ (
165
+ "from diffusers.loaders import FromOriginalControlnetMixin",
166
+ "from diffusers.loaders.single_file_model import FromOriginalModelMixin as FromOriginalControlnetMixin",
167
+ ),
168
+ ],
169
+ )
170
+
171
+
172
+ patch_pasd_for_diffusers()
173
+
174
+ # -------------------------------------------------------------------
175
+ # Import PASD modules only after patching
176
+ # -------------------------------------------------------------------
177
  from pipelines.pipeline_pasd import StableDiffusionControlNetPipeline
178
  from myutils.misc import load_dreambooth_lora
179
  from myutils.wavelet_color_fix import wavelet_color_fix
180
  from annotator.retinaface import RetinaFaceDetection
181
 
182
+ use_pasd_light = False
183
+ face_detector = RetinaFaceDetection()
184
 
185
+ if use_pasd_light:
186
+ from models.pasd_light.unet_2d_condition import UNet2DConditionModel
187
+ from models.pasd_light.controlnet import ControlNetModel
188
+ else:
189
+ from models.pasd.unet_2d_condition import UNet2DConditionModel
190
+ from models.pasd.controlnet import ControlNetModel
191
+
192
+ # -------------------------------------------------------------------
193
+ # Model setup
194
+ # -------------------------------------------------------------------
195
  pretrained_model_path = "stable-diffusion-v1-5/stable-diffusion-v1-5"
196
  ckpt_path = "PASD/runs/pasd/checkpoint-100000"
197
  dreambooth_lora_path = "PASD/checkpoints/personalized_models/majicmixRealistic_v6.safetensors"
198
 
 
199
  weight_dtype = torch.float16
200
+ device = "cuda"
201
 
202
+ scheduler = UniPCMultistepScheduler.from_pretrained(
203
+ pretrained_model_path, subfolder="scheduler"
204
+ )
205
+ text_encoder = CLIPTextModel.from_pretrained(
206
+ pretrained_model_path, subfolder="text_encoder"
207
+ )
208
+ tokenizer = CLIPTokenizer.from_pretrained(
209
+ pretrained_model_path, subfolder="tokenizer"
210
+ )
211
+ vae = AutoencoderKL.from_pretrained(
212
+ pretrained_model_path, subfolder="vae"
213
+ )
214
+ feature_extractor = CLIPImageProcessor.from_pretrained(
215
+ pretrained_model_path, subfolder="feature_extractor"
216
+ )
217
+ unet = UNet2DConditionModel.from_pretrained(
218
+ ckpt_path, subfolder="unet"
219
+ )
220
+ controlnet = ControlNetModel.from_pretrained(
221
+ ckpt_path, subfolder="controlnet"
222
+ )
223
 
224
  vae.requires_grad_(False)
225
  text_encoder.requires_grad_(False)
226
  unet.requires_grad_(False)
227
  controlnet.requires_grad_(False)
228
 
229
+ unet, vae, text_encoder = load_dreambooth_lora(
230
+ unet, vae, text_encoder, dreambooth_lora_path
231
+ )
232
 
233
  text_encoder.to(device, dtype=weight_dtype)
234
  vae.to(device, dtype=weight_dtype)
235
  unet.to(device, dtype=weight_dtype)
236
  controlnet.to(device, dtype=weight_dtype)
237
 
238
+ validation_pipeline = StableDiffusionControlNetPipeline(
239
  vae=vae,
240
  text_encoder=text_encoder,
241
  tokenizer=tokenizer,
 
247
  requires_safety_checker=False,
248
  )
249
 
250
+ validation_pipeline._init_tiled_vae(decoder_tile_size=224)
251
 
252
+ # -------------------------------------------------------------------
253
+ # ResNet helper
254
+ # -------------------------------------------------------------------
255
  weights = ResNet50_Weights.DEFAULT
256
  preprocess = weights.transforms()
257
  resnet = resnet50(weights=weights)
258
  resnet.eval()
259
 
260
+
261
+ def resize_image(image_path: str, target_height: int) -> Image.Image:
262
  with Image.open(image_path) as img:
263
  ratio = target_height / float(img.size[1])
264
  new_width = int(float(img.size[0]) * ratio)
265
  return img.resize((new_width, target_height), Image.LANCZOS)
266
 
267
+
268
  @spaces.GPU(enable_queue=True)
269
+ def inference(
270
+ input_image,
271
+ prompt,
272
+ a_prompt,
273
+ n_prompt,
274
+ denoise_steps,
275
+ upscale,
276
+ alpha,
277
+ cfg,
278
+ seed,
279
+ ):
280
  if seed == -1:
281
  seed = 0
282
 
 
285
 
286
  with torch.no_grad():
287
  seed_everything(seed)
288
+ generator = torch.Generator(device=device)
289
+ generator.manual_seed(seed)
290
 
291
  input_image = input_image.convert("RGB")
292
 
293
+ batch = preprocess(input_image).unsqueeze(0)
294
+ prediction = resnet(batch).squeeze(0).softmax(0)
295
+ class_id = prediction.argmax().item()
296
+ score = prediction[class_id].item()
297
+ category_name = weights.meta["categories"][class_id]
298
 
299
+ if score >= 0.1:
300
+ prompt += f"{category_name}" if prompt == "" else f", {category_name}"
301
 
302
+ prompt = a_prompt if prompt == "" else f"{prompt}, {a_prompt}"
 
303
 
304
+ ori_width, ori_height = input_image.size
305
 
306
+ rscale = upscale
307
+ input_image = input_image.resize(
308
+ (input_image.size[0] * rscale, input_image.size[1] * rscale)
309
+ )
310
+ input_image = input_image.resize(
311
+ (input_image.size[0] // 8 * 8, input_image.size[1] // 8 * 8)
312
+ )
313
+ width, height = input_image.size
314
+
315
+ try:
316
+ image = validation_pipeline(
317
+ None,
318
+ prompt,
319
+ input_image,
320
+ num_inference_steps=denoise_steps,
321
+ generator=generator,
322
+ height=height,
323
+ width=width,
324
+ guidance_scale=cfg,
325
+ negative_prompt=n_prompt,
326
+ conditioning_scale=alpha,
327
+ eta=0.0,
328
+ ).images[0]
329
+
330
+ image = wavelet_color_fix(image, input_image)
331
+ image = image.resize((ori_width * rscale, ori_height * rscale))
332
+ except Exception as e:
333
+ print(f"[inference] error: {e}")
334
+ image = Image.new(mode="RGB", size=(512, 512))
335
 
336
  result_path = f"result_{timestamp}.jpg"
337
  input_path = f"input_{timestamp}.jpg"
 
341
 
342
  return input_path, result_path, result_path
343
 
344
+
345
+ css = """
346
+ #col-container{
347
+ margin: 0 auto;
348
+ max-width: 720px;
349
+ }
350
+ #project-links{
351
+ margin: 0 0 12px !important;
352
+ column-gap: 8px;
353
+ display: flex;
354
+ justify-content: center;
355
+ flex-wrap: nowrap;
356
+ flex-direction: row;
357
+ align-items: center;
358
+ }
359
+ """
360
+
361
  with gr.Blocks() as demo:
362
+ with gr.Column(elem_id="col-container"):
363
+ gr.HTML("""
364
+ <h2 style="text-align: center;">
365
+ PASD Magnify
366
+ </h2>
367
+ <p style="text-align: center;">
368
+ Pixel-Aware Stable Diffusion for Realistic Image Super-resolution and Personalized Stylization
369
+ </p>
370
+ <p id="project-links" align="center">
371
+ <a href="https://github.com/yangxy/PASD"><img src="https://img.shields.io/badge/Project-Page-Green"></a>
372
+ <a href="https://huggingface.co/papers/2308.14469"><img src="https://img.shields.io/badge/Paper-Arxiv-red"></a>
373
+ </p>
374
+ <p style="margin:12px auto;display: flex;justify-content: center;">
375
+ <a href="https://huggingface.co/spaces/fffiloni/PASD?duplicate=true">
376
+ <img src="https://huggingface.co/datasets/huggingface/badges/resolve/main/duplicate-this-space-lg.svg" alt="Duplicate this Space">
377
+ </a>
378
+ </p>
379
+ """)
380
+
381
+ with gr.Row():
382
+ with gr.Column():
383
+ input_image = gr.Image(
384
+ type="filepath",
385
+ sources=["upload"],
386
+ value="PASD/samples/frog.png",
387
+ label="Input image",
388
+ )
389
+ prompt_in = gr.Textbox(label="Prompt", value="Frog")
390
+
391
+ with gr.Accordion(label="Advanced settings", open=False):
392
+ added_prompt = gr.Textbox(
393
+ label="Added Prompt",
394
+ value="clean, high-resolution, 8k, best quality, masterpiece",
395
+ )
396
+ neg_prompt = gr.Textbox(
397
+ label="Negative Prompt",
398
+ value="dotted, noise, blur, lowres, oversmooth, longbody, bad anatomy, bad hands, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality",
399
+ )
400
+ denoise_steps = gr.Slider(
401
+ label="Denoise Steps",
402
+ minimum=10,
403
+ maximum=50,
404
+ value=20,
405
+ step=1,
406
+ )
407
+ upsample_scale = gr.Slider(
408
+ label="Upsample Scale",
409
+ minimum=1,
410
+ maximum=4,
411
+ value=2,
412
+ step=1,
413
+ )
414
+ condition_scale = gr.Slider(
415
+ label="Conditioning Scale",
416
+ minimum=0.5,
417
+ maximum=1.5,
418
+ value=1.1,
419
+ step=0.1,
420
+ )
421
+ classifier_free_guidance = gr.Slider(
422
+ label="Classifier-free Guidance",
423
+ minimum=0.1,
424
+ maximum=10.0,
425
+ value=7.5,
426
+ step=0.1,
427
+ )
428
+ seed = gr.Slider(
429
+ label="Seed",
430
+ minimum=-1,
431
+ maximum=2147483647,
432
+ step=1,
433
+ randomize=True,
434
+ )
435
+
436
+ submit_btn = gr.Button("Submit")
437
+
438
+ with gr.Column():
439
+ before_img = gr.Image(label="Input")
440
+ after_img = gr.Image(label="Result")
441
+ file_output = gr.File(label="Downloadable image result")
442
+
443
+ submit_btn.click(
444
+ fn=inference,
445
+ inputs=[
446
+ input_image,
447
+ prompt_in,
448
+ added_prompt,
449
+ neg_prompt,
450
+ denoise_steps,
451
+ upsample_scale,
452
+ condition_scale,
453
+ classifier_free_guidance,
454
+ seed,
455
+ ],
456
+ outputs=[
457
+ before_img,
458
+ after_img,
459
+ file_output,
460
+ ],
461
+ api_visibility="private",
462
  )
463
 
464
+ demo.queue(max_size=10).launch(
465
+ ssr_mode=False,
466
+ mcp_server=False,
467
+ css=css,
468
+ )