cydyffufufurty commited on
Commit
1681fcd
·
verified ·
1 Parent(s): 524fd59

Preserve non-square photo canvas in Qwen worker

Browse files

Use area-based canvas sizing, apply EXIF orientation, and keep fixed VAE conditioning area to avoid portrait/wide photo crop regressions.

Files changed (1) hide show
  1. app.py +402 -396
app.py CHANGED
@@ -1,226 +1,232 @@
1
- import os
2
- import gc
3
- import gradio as gr
4
- import numpy as np
5
- import spaces
6
- import torch
7
- import random
8
- from PIL import Image
9
- from typing import Iterable
10
- from gradio.themes import Soft
11
- from gradio.themes.utils import colors, fonts, sizes
12
-
13
- colors.orange_red = colors.Color(
14
- name="orange_red",
15
- c50="#FFF0E5",
16
- c100="#FFE0CC",
17
- c200="#FFC299",
18
- c300="#FFA366",
19
- c400="#FF8533",
20
- c500="#FF4500",
21
- c600="#E63E00",
22
- c700="#CC3700",
23
- c800="#B33000",
24
- c900="#992900",
25
- c950="#802200",
26
- )
27
-
28
- class OrangeRedTheme(Soft):
29
- def __init__(
30
- self,
31
- *,
32
- primary_hue: colors.Color | str = colors.gray,
33
- secondary_hue: colors.Color | str = colors.orange_red,
34
- neutral_hue: colors.Color | str = colors.slate,
35
- text_size: sizes.Size | str = sizes.text_lg,
36
- font: fonts.Font | str | Iterable[fonts.Font | str] = (
37
- fonts.GoogleFont("Outfit"), "Arial", "sans-serif",
38
- ),
39
- font_mono: fonts.Font | str | Iterable[fonts.Font | str] = (
40
- fonts.GoogleFont("IBM Plex Mono"), "ui-monospace", "monospace",
41
- ),
42
- ):
43
- super().__init__(
44
- primary_hue=primary_hue,
45
- secondary_hue=secondary_hue,
46
- neutral_hue=neutral_hue,
47
- text_size=text_size,
48
- font=font,
49
- font_mono=font_mono,
50
- )
51
- super().set(
52
- background_fill_primary="*primary_50",
53
- background_fill_primary_dark="*primary_900",
54
- body_background_fill="linear-gradient(135deg, *primary_200, *primary_100)",
55
- body_background_fill_dark="linear-gradient(135deg, *primary_900, *primary_800)",
56
- button_primary_text_color="white",
57
- button_primary_text_color_hover="white",
58
- button_primary_background_fill="linear-gradient(90deg, *secondary_500, *secondary_600)",
59
- button_primary_background_fill_hover="linear-gradient(90deg, *secondary_600, *secondary_700)",
60
- button_primary_background_fill_dark="linear-gradient(90deg, *secondary_600, *secondary_700)",
61
- button_primary_background_fill_hover_dark="linear-gradient(90deg, *secondary_500, *secondary_600)",
62
- button_secondary_text_color="black",
63
- button_secondary_text_color_hover="white",
64
- button_secondary_background_fill="linear-gradient(90deg, *primary_300, *primary_300)",
65
- button_secondary_background_fill_hover="linear-gradient(90deg, *primary_400, *primary_400)",
66
- button_secondary_background_fill_dark="linear-gradient(90deg, *primary_500, *primary_600)",
67
- button_secondary_background_fill_hover_dark="linear-gradient(90deg, *primary_500, *primary_500)",
68
- slider_color="*secondary_500",
69
- slider_color_dark="*secondary_600",
70
- block_title_text_weight="600",
71
- block_border_width="3px",
72
- block_shadow="*shadow_drop_lg",
73
- button_primary_shadow="*shadow_drop_lg",
74
- button_large_padding="11px",
75
- color_accent_soft="*primary_100",
76
- block_label_background_fill="*primary_200",
77
- )
78
-
79
- orange_red_theme = OrangeRedTheme()
80
-
81
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
82
-
83
- print("CUDA_VISIBLE_DEVICES=", os.environ.get("CUDA_VISIBLE_DEVICES"))
84
- print("torch.__version__ =", torch.__version__)
85
- print("Using device:", device)
86
-
87
- from diffusers import FlowMatchEulerDiscreteScheduler
88
- from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
89
- from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
90
-
91
- dtype = torch.bfloat16
92
-
93
- pipe = QwenImageEditPlusPipeline.from_pretrained(
94
- "Qwen/Qwen-Image-Edit-2511",
95
- transformer=QwenImageTransformer2DModel.from_pretrained(
96
- "prithivMLmods/Qwen-Image-Edit-Rapid-AIO-V19",
97
- torch_dtype=dtype,
98
- device_map='cuda'
99
- ),
100
- torch_dtype=dtype
101
- ).to(device)
102
-
103
  print("Flash Attention 3 disabled for ZeroGPU A10G compatibility; using default attention.")
104
-
105
- MAX_SEED = np.iinfo(np.int32).max
106
-
107
- ADAPTER_SPECS = {
108
- "Multiple-Angles": {
109
- "repo": "dx8152/Qwen-Edit-2509-Multiple-angles",
110
- "weights": "镜头转换.safetensors",
111
- "adapter_name": "multiple-angles"
112
- },
113
- "Photo-to-Anime": {
114
- "repo": "autoweeb/Qwen-Image-Edit-2509-Photo-to-Anime",
115
- "weights": "Qwen-Image-Edit-2509-Photo-to-Anime_000001000.safetensors",
116
- "adapter_name": "photo-to-anime"
117
- },
118
- "Anime-V2": {
119
- "repo": "prithivMLmods/Qwen-Image-Edit-2511-Anime",
120
- "weights": "Qwen-Image-Edit-2511-Anime-2000.safetensors",
121
- "adapter_name": "anime-v2"
122
- },
123
- "Light-Migration": {
124
- "repo": "dx8152/Qwen-Edit-2509-Light-Migration",
125
- "weights": "参考色调.safetensors",
126
- "adapter_name": "light-migration"
127
- },
128
- "Upscaler": {
129
- "repo": "starsfriday/Qwen-Image-Edit-2511-Upscale2K",
130
- "weights": "qwen_image_edit_2511_upscale.safetensors",
131
- "adapter_name": "upscale-2k"
132
- },
133
- "Style-Transfer": {
134
- "repo": "zooeyy/Style-Transfer",
135
- "weights": "Style Transfer-Alpha-V0.1.safetensors",
136
- "adapter_name": "style-transfer"
137
- },
138
- "Manga-Tone": {
139
- "repo": "nappa114514/Qwen-Image-Edit-2509-Manga-Tone",
140
- "weights": "tone001.safetensors",
141
- "adapter_name": "manga-tone"
142
- },
143
- "Anything2Real": {
144
- "repo": "lrzjason/Anything2Real_2601",
145
- "weights": "anything2real_2601.safetensors",
146
- "adapter_name": "anything2real"
147
- },
148
- "Fal-Multiple-Angles": {
149
- "repo": "fal/Qwen-Image-Edit-2511-Multiple-Angles-LoRA",
150
- "weights": "qwen-image-edit-2511-multiple-angles-lora.safetensors",
151
- "adapter_name": "fal-multiple-angles"
152
- },
153
- "Polaroid-Photo": {
154
- "repo": "prithivMLmods/Qwen-Image-Edit-2511-Polaroid-Photo",
155
- "weights": "Qwen-Image-Edit-2511-Polaroid-Photo.safetensors",
156
- "adapter_name": "polaroid-photo"
157
- },
158
- "Unblur-Anything": {
159
- "repo": "prithivMLmods/Qwen-Image-Edit-2511-Unblur-Upscale",
160
- "weights": "Qwen-Image-Edit-Unblur-Upscale_15.safetensors",
161
- "adapter_name": "unblur-anything"
162
- },
163
- "Midnight-Noir-Eyes-Spotlight": {
164
- "repo": "prithivMLmods/Qwen-Image-Edit-2511-Midnight-Noir-Eyes-Spotlight",
165
- "weights": "Qwen-Image-Edit-2511-Midnight-Noir-Eyes-Spotlight.safetensors",
166
- "adapter_name": "midnight-noir-eyes-spotlight"
167
- },
168
- "Hyper-Realistic-Portrait": {
169
- "repo": "prithivMLmods/Qwen-Image-Edit-2511-Hyper-Realistic-Portrait",
170
- "weights": "HRP_20.safetensors",
171
- "adapter_name": "hyper-realistic-portrait"
172
- },
173
- "Ultra-Realistic-Portrait": {
174
- "repo": "prithivMLmods/Qwen-Image-Edit-2511-Ultra-Realistic-Portrait",
175
- "weights": "URP_20.safetensors",
176
- "adapter_name": "ultra-realistic-portrait"
177
- },
178
- "Pixar-Inspired-3D": {
179
- "repo": "prithivMLmods/Qwen-Image-Edit-2511-Pixar-Inspired-3D",
180
- "weights": "PI3_20.safetensors",
181
- "adapter_name": "pi3"
182
- },
183
- "Noir-Comic-Book": {
184
- "repo": "prithivMLmods/Qwen-Image-Edit-2511-Noir-Comic-Book-Panel",
185
- "weights": "Noir-Comic-Book-Panel_20.safetensors",
186
- "adapter_name": "ncb"
187
- },
188
- "Any-light": {
189
- "repo": "lilylilith/QIE-2511-MP-AnyLight",
190
- "weights": "QIE-2511-AnyLight_.safetensors",
191
- "adapter_name": "any-light"
192
- },
193
- "Studio-DeLight": {
194
- "repo": "prithivMLmods/QIE-2511-Studio-DeLight",
195
- "weights": "QIE-2511-Studio-DeLight-5000.safetensors",
196
- "adapter_name": "studio-delight"
197
- },
198
- "Cinematic-FlatLog": {
199
- "repo": "prithivMLmods/QIE-2511-Cinematic-FlatLog-Control",
200
- "weights": "QIE-2511-Cinematic-FlatLog-Control-3200.safetensors",
201
- "adapter_name": "flat-log"
202
- },
203
- }
204
-
205
- LOADED_ADAPTERS = set()
206
-
207
  def update_dimensions_on_upload(image):
208
- if image is None:
209
- return 1024, 1024
210
-
211
- original_width, original_height = image.size
212
-
213
- if original_width > original_height:
214
- new_width = 1024
215
- aspect_ratio = original_height / original_width
216
- new_height = int(new_width * aspect_ratio)
217
- else:
218
- new_height = 1024
219
- aspect_ratio = original_width / original_height
220
- new_width = int(new_height * aspect_ratio)
221
-
222
- new_width = (new_width // 8) * 8
223
- new_height = (new_height // 8) * 8
 
 
 
 
 
 
224
 
225
  return new_width, new_height
226
 
@@ -241,22 +247,22 @@ def get_qwen_gpu_duration(*args, **kwargs):
241
 
242
  @spaces.GPU(duration=get_qwen_gpu_duration)
243
  def infer(
244
- images,
245
- prompt,
246
- lora_adapter,
247
- seed,
248
- randomize_seed,
249
- guidance_scale,
250
- steps,
251
- progress=gr.Progress(track_tqdm=True)
252
- ):
253
- gc.collect()
254
- torch.cuda.empty_cache()
255
-
256
- if not images:
257
- raise gr.Error("Please upload at least one image to edit.")
258
-
259
- pil_images = []
260
  if images is not None:
261
  if isinstance(images, (str, Image.Image, dict)) or hasattr(images, "name"):
262
  images = [images]
@@ -272,106 +278,106 @@ def infer(
272
  path_or_img = item
273
 
274
  if isinstance(path_or_img, str):
275
- pil_images.append(Image.open(path_or_img).convert("RGB"))
276
  elif isinstance(path_or_img, Image.Image):
277
- pil_images.append(path_or_img.convert("RGB"))
278
  elif hasattr(path_or_img, "path"):
279
- pil_images.append(Image.open(path_or_img.path).convert("RGB"))
280
  else:
281
- pil_images.append(Image.open(path_or_img.name).convert("RGB"))
282
- except Exception as e:
283
- print(f"Skipping invalid image item: {e}")
284
- continue
285
-
286
- if not pil_images:
287
- raise gr.Error("Could not process uploaded images.")
288
-
289
- spec = ADAPTER_SPECS.get(lora_adapter)
290
- if not spec:
291
- raise gr.Error(f"Configuration not found for: {lora_adapter}")
292
-
293
- adapter_name = spec["adapter_name"]
294
-
295
- if adapter_name not in LOADED_ADAPTERS:
296
- print(f"--- Downloading and Loading Adapter: {lora_adapter} ---")
297
- try:
298
- pipe.load_lora_weights(
299
- spec["repo"],
300
- weight_name=spec["weights"],
301
- adapter_name=adapter_name
302
- )
303
- LOADED_ADAPTERS.add(adapter_name)
304
- except Exception as e:
305
- raise gr.Error(f"Failed to load adapter {lora_adapter}: {e}")
306
- else:
307
- print(f"--- Adapter {lora_adapter} is already loaded. ---")
308
-
309
- pipe.set_adapters([adapter_name], adapter_weights=[1.0])
310
-
311
- if randomize_seed:
312
- seed = random.randint(0, MAX_SEED)
313
-
314
- generator = torch.Generator(device=device).manual_seed(seed)
315
- negative_prompt = "worst quality, low quality, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, jpeg artifacts, signature, watermark, username, blurry"
316
-
317
- width, height = update_dimensions_on_upload(pil_images[0])
318
-
319
- try:
320
- result_image = pipe(
321
- image=pil_images,
322
- prompt=prompt,
323
- negative_prompt=negative_prompt,
324
- height=height,
325
- width=width,
326
- num_inference_steps=steps,
327
- generator=generator,
328
- true_cfg_scale=guidance_scale,
329
- ).images[0]
330
-
331
- return result_image, seed
332
-
333
- except Exception as e:
334
- raise e
335
- finally:
336
- gc.collect()
337
- torch.cuda.empty_cache()
338
-
339
  @spaces.GPU(duration=get_qwen_gpu_duration)
340
  def infer_example(images, prompt, lora_adapter):
341
- if not images:
342
- return None, 0
343
-
344
- if isinstance(images, str):
345
- images_list = [images]
346
- else:
347
- images_list = images
348
-
349
- result, seed = infer(
350
- images=images_list,
351
- prompt=prompt,
352
- lora_adapter=lora_adapter,
353
- seed=0,
354
- randomize_seed=True,
355
- guidance_scale=1.0,
356
- steps=4
357
- )
358
- return result, seed
359
-
360
- css="""
361
- #col-container {
362
- margin: 0 auto;
363
- max-width: 1000px;
364
- }
365
- #main-title h1 {font-size: 2.4em !important;}
366
- """
367
-
368
- with gr.Blocks() as demo:
369
- with gr.Column(elem_id="col-container"):
370
- gr.Markdown("# **Qwen-Image-Edit-2511-LoRAs-Fast**", elem_id="main-title")
371
- gr.Markdown("Perform diverse image edits using specialized [LoRA](https://huggingface.co/models?other=base_model:adapter:Qwen/Qwen-Image-Edit-2511) adapters. Open on [GitHub](https://github.com/PRITHIVSAKTHIUR/Qwen-Image-Edit-2511-LoRAs-Fast-Lazy-Load).")
372
-
373
- with gr.Row(equal_height=True):
374
- with gr.Column():
375
  images = gr.File(
376
  label="Upload Images",
377
  file_count="multiple",
@@ -379,68 +385,68 @@ with gr.Blocks() as demo:
379
  file_types=["image"],
380
  height=300,
381
  )
382
-
383
- prompt = gr.Text(
384
- label="Edit Prompt",
385
- #max_lines=1,
386
- show_label=True,
387
- placeholder="e.g., transform into anime..",
388
- )
389
-
390
- run_button = gr.Button("Edit Image", variant="primary")
391
-
392
- with gr.Column():
393
- output_image = gr.Image(label="Output Image", interactive=False, format="png", height=365)
394
-
395
- with gr.Row():
396
- lora_adapter = gr.Dropdown(
397
- label="Choose Editing Style",
398
- choices=list(ADAPTER_SPECS.keys()),
399
- value="Photo-to-Anime"
400
- )
401
-
402
- with gr.Accordion("Advanced Settings", open=False, visible=False):
403
- seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0)
404
- randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
405
- guidance_scale = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
406
- steps = gr.Slider(label="Inference Steps", minimum=1, maximum=50, step=1, value=4)
407
-
408
- gr.Examples(
409
- examples=[
410
- [["examples/B.jpg"], "Transform into anime.", "Photo-to-Anime"],
411
- [["examples/HRP.jpg"], "Transform into a hyper-realistic face portrait.", "Hyper-Realistic-Portrait"],
412
- [["examples/A.jpeg"], "Rotate the camera 45 degrees to the right.", "Multiple-Angles"],
413
- [["examples/U.jpg"], "Upscale this picture to 4K resolution.", "Upscaler"],
414
- [["examples/L1.jpg", "examples/L2.jpg"], "Apply the lighting from image 2 to image 1.", "Any-light"],
415
- [["examples/PP1.jpg"], "cinematic polaroid with soft grain subtle vignette gentle lighting white frame handwritten photographed by hf‪‪‬ preserving realistic texture and details", "Polaroid-Photo"],
416
- [["examples/Z1.jpg"], "Front-right quarter view.", "Fal-Multiple-Angles"],
417
- [["examples/URP.jpg"], "Transform into a cinematic flat log.", "Cinematic-FlatLog"],
418
- [["examples/SL.jpg"], "Neutral uniform lighting Preserve identity and composition.", "Studio-DeLight"],
419
- [["examples/PI.jpg"], "Transform it into Pixar-inspired 3D.", "Pixar-Inspired-3D"],
420
- [["examples/MT.jpg"], "Paint with manga tone.", "Manga-Tone"],
421
- [["examples/NCB.jpg"], "Transform into a noir comic book style.", "Noir-Comic-Book"],
422
- [["examples/URP.jpg"], "ultra-realistic portrait.", "Ultra-Realistic-Portrait"],
423
- [["examples/MN.jpg"], "Transform into Midnight Noir Eyes Spotlight.", "Midnight-Noir-Eyes-Spotlight"],
424
- [["examples/ST1.jpg", "examples/ST2.jpg"], "Convert Image 1 to the style of Image 2.", "Style-Transfer"],
425
- [["examples/R1.jpg"], "Change the picture to realistic photograph.", "Anything2Real"],
426
- [["examples/UA.jpeg"], "Unblur and upscale.", "Unblur-Anything"],
427
- [["examples/L1.jpg", "examples/L2.jpg"], "Refer to the color tone, remove the original lighting from Image 1, and relight Image 1 based on the lighting and color tone of Image 2.", "Light-Migration"],
428
- [["examples/P1.jpg"], "Transform into anime (while preserving the background and remaining elements maintaining realism and original details.)", "Anime-V2"],
429
- ],
430
- inputs=[images, prompt, lora_adapter],
431
- outputs=[output_image, seed],
432
- fn=infer_example,
433
- cache_examples=False,
434
- label="Examples"
435
- )
436
-
437
- gr.Markdown("[*](https://huggingface.co/spaces/prithivMLmods/Qwen-Image-Edit-2511-LoRAs-Fast)This is still an experimental Space for Qwen-Image-Edit-2511.")
438
-
439
- run_button.click(
440
- fn=infer,
441
- inputs=[images, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps],
442
- outputs=[output_image, seed]
443
- )
444
-
445
- if __name__ == "__main__":
446
  demo.queue(max_size=30).launch(css=css, theme=orange_red_theme, mcp_server=True, ssr_mode=False, show_error=True)
 
1
+ import os
2
+ import gc
3
+ import gradio as gr
4
+ import numpy as np
5
+ import spaces
6
+ import torch
7
+ import random
8
+ from PIL import Image, ImageOps
9
+ from typing import Iterable
10
+ from gradio.themes import Soft
11
+ from gradio.themes.utils import colors, fonts, sizes
12
+
13
+ colors.orange_red = colors.Color(
14
+ name="orange_red",
15
+ c50="#FFF0E5",
16
+ c100="#FFE0CC",
17
+ c200="#FFC299",
18
+ c300="#FFA366",
19
+ c400="#FF8533",
20
+ c500="#FF4500",
21
+ c600="#E63E00",
22
+ c700="#CC3700",
23
+ c800="#B33000",
24
+ c900="#992900",
25
+ c950="#802200",
26
+ )
27
+
28
+ class OrangeRedTheme(Soft):
29
+ def __init__(
30
+ self,
31
+ *,
32
+ primary_hue: colors.Color | str = colors.gray,
33
+ secondary_hue: colors.Color | str = colors.orange_red,
34
+ neutral_hue: colors.Color | str = colors.slate,
35
+ text_size: sizes.Size | str = sizes.text_lg,
36
+ font: fonts.Font | str | Iterable[fonts.Font | str] = (
37
+ fonts.GoogleFont("Outfit"), "Arial", "sans-serif",
38
+ ),
39
+ font_mono: fonts.Font | str | Iterable[fonts.Font | str] = (
40
+ fonts.GoogleFont("IBM Plex Mono"), "ui-monospace", "monospace",
41
+ ),
42
+ ):
43
+ super().__init__(
44
+ primary_hue=primary_hue,
45
+ secondary_hue=secondary_hue,
46
+ neutral_hue=neutral_hue,
47
+ text_size=text_size,
48
+ font=font,
49
+ font_mono=font_mono,
50
+ )
51
+ super().set(
52
+ background_fill_primary="*primary_50",
53
+ background_fill_primary_dark="*primary_900",
54
+ body_background_fill="linear-gradient(135deg, *primary_200, *primary_100)",
55
+ body_background_fill_dark="linear-gradient(135deg, *primary_900, *primary_800)",
56
+ button_primary_text_color="white",
57
+ button_primary_text_color_hover="white",
58
+ button_primary_background_fill="linear-gradient(90deg, *secondary_500, *secondary_600)",
59
+ button_primary_background_fill_hover="linear-gradient(90deg, *secondary_600, *secondary_700)",
60
+ button_primary_background_fill_dark="linear-gradient(90deg, *secondary_600, *secondary_700)",
61
+ button_primary_background_fill_hover_dark="linear-gradient(90deg, *secondary_500, *secondary_600)",
62
+ button_secondary_text_color="black",
63
+ button_secondary_text_color_hover="white",
64
+ button_secondary_background_fill="linear-gradient(90deg, *primary_300, *primary_300)",
65
+ button_secondary_background_fill_hover="linear-gradient(90deg, *primary_400, *primary_400)",
66
+ button_secondary_background_fill_dark="linear-gradient(90deg, *primary_500, *primary_600)",
67
+ button_secondary_background_fill_hover_dark="linear-gradient(90deg, *primary_500, *primary_500)",
68
+ slider_color="*secondary_500",
69
+ slider_color_dark="*secondary_600",
70
+ block_title_text_weight="600",
71
+ block_border_width="3px",
72
+ block_shadow="*shadow_drop_lg",
73
+ button_primary_shadow="*shadow_drop_lg",
74
+ button_large_padding="11px",
75
+ color_accent_soft="*primary_100",
76
+ block_label_background_fill="*primary_200",
77
+ )
78
+
79
+ orange_red_theme = OrangeRedTheme()
80
+
81
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
82
+
83
+ print("CUDA_VISIBLE_DEVICES=", os.environ.get("CUDA_VISIBLE_DEVICES"))
84
+ print("torch.__version__ =", torch.__version__)
85
+ print("Using device:", device)
86
+
87
+ from diffusers import FlowMatchEulerDiscreteScheduler
88
+ from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
89
+ from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
90
+
91
+ dtype = torch.bfloat16
92
+
93
+ pipe = QwenImageEditPlusPipeline.from_pretrained(
94
+ "Qwen/Qwen-Image-Edit-2511",
95
+ transformer=QwenImageTransformer2DModel.from_pretrained(
96
+ "prithivMLmods/Qwen-Image-Edit-Rapid-AIO-V19",
97
+ torch_dtype=dtype,
98
+ device_map='cuda'
99
+ ),
100
+ torch_dtype=dtype
101
+ ).to(device)
102
+
103
  print("Flash Attention 3 disabled for ZeroGPU A10G compatibility; using default attention.")
104
+
105
+ MAX_SEED = np.iinfo(np.int32).max
106
+
107
+ ADAPTER_SPECS = {
108
+ "Multiple-Angles": {
109
+ "repo": "dx8152/Qwen-Edit-2509-Multiple-angles",
110
+ "weights": "镜头转换.safetensors",
111
+ "adapter_name": "multiple-angles"
112
+ },
113
+ "Photo-to-Anime": {
114
+ "repo": "autoweeb/Qwen-Image-Edit-2509-Photo-to-Anime",
115
+ "weights": "Qwen-Image-Edit-2509-Photo-to-Anime_000001000.safetensors",
116
+ "adapter_name": "photo-to-anime"
117
+ },
118
+ "Anime-V2": {
119
+ "repo": "prithivMLmods/Qwen-Image-Edit-2511-Anime",
120
+ "weights": "Qwen-Image-Edit-2511-Anime-2000.safetensors",
121
+ "adapter_name": "anime-v2"
122
+ },
123
+ "Light-Migration": {
124
+ "repo": "dx8152/Qwen-Edit-2509-Light-Migration",
125
+ "weights": "参考色调.safetensors",
126
+ "adapter_name": "light-migration"
127
+ },
128
+ "Upscaler": {
129
+ "repo": "starsfriday/Qwen-Image-Edit-2511-Upscale2K",
130
+ "weights": "qwen_image_edit_2511_upscale.safetensors",
131
+ "adapter_name": "upscale-2k"
132
+ },
133
+ "Style-Transfer": {
134
+ "repo": "zooeyy/Style-Transfer",
135
+ "weights": "Style Transfer-Alpha-V0.1.safetensors",
136
+ "adapter_name": "style-transfer"
137
+ },
138
+ "Manga-Tone": {
139
+ "repo": "nappa114514/Qwen-Image-Edit-2509-Manga-Tone",
140
+ "weights": "tone001.safetensors",
141
+ "adapter_name": "manga-tone"
142
+ },
143
+ "Anything2Real": {
144
+ "repo": "lrzjason/Anything2Real_2601",
145
+ "weights": "anything2real_2601.safetensors",
146
+ "adapter_name": "anything2real"
147
+ },
148
+ "Fal-Multiple-Angles": {
149
+ "repo": "fal/Qwen-Image-Edit-2511-Multiple-Angles-LoRA",
150
+ "weights": "qwen-image-edit-2511-multiple-angles-lora.safetensors",
151
+ "adapter_name": "fal-multiple-angles"
152
+ },
153
+ "Polaroid-Photo": {
154
+ "repo": "prithivMLmods/Qwen-Image-Edit-2511-Polaroid-Photo",
155
+ "weights": "Qwen-Image-Edit-2511-Polaroid-Photo.safetensors",
156
+ "adapter_name": "polaroid-photo"
157
+ },
158
+ "Unblur-Anything": {
159
+ "repo": "prithivMLmods/Qwen-Image-Edit-2511-Unblur-Upscale",
160
+ "weights": "Qwen-Image-Edit-Unblur-Upscale_15.safetensors",
161
+ "adapter_name": "unblur-anything"
162
+ },
163
+ "Midnight-Noir-Eyes-Spotlight": {
164
+ "repo": "prithivMLmods/Qwen-Image-Edit-2511-Midnight-Noir-Eyes-Spotlight",
165
+ "weights": "Qwen-Image-Edit-2511-Midnight-Noir-Eyes-Spotlight.safetensors",
166
+ "adapter_name": "midnight-noir-eyes-spotlight"
167
+ },
168
+ "Hyper-Realistic-Portrait": {
169
+ "repo": "prithivMLmods/Qwen-Image-Edit-2511-Hyper-Realistic-Portrait",
170
+ "weights": "HRP_20.safetensors",
171
+ "adapter_name": "hyper-realistic-portrait"
172
+ },
173
+ "Ultra-Realistic-Portrait": {
174
+ "repo": "prithivMLmods/Qwen-Image-Edit-2511-Ultra-Realistic-Portrait",
175
+ "weights": "URP_20.safetensors",
176
+ "adapter_name": "ultra-realistic-portrait"
177
+ },
178
+ "Pixar-Inspired-3D": {
179
+ "repo": "prithivMLmods/Qwen-Image-Edit-2511-Pixar-Inspired-3D",
180
+ "weights": "PI3_20.safetensors",
181
+ "adapter_name": "pi3"
182
+ },
183
+ "Noir-Comic-Book": {
184
+ "repo": "prithivMLmods/Qwen-Image-Edit-2511-Noir-Comic-Book-Panel",
185
+ "weights": "Noir-Comic-Book-Panel_20.safetensors",
186
+ "adapter_name": "ncb"
187
+ },
188
+ "Any-light": {
189
+ "repo": "lilylilith/QIE-2511-MP-AnyLight",
190
+ "weights": "QIE-2511-AnyLight_.safetensors",
191
+ "adapter_name": "any-light"
192
+ },
193
+ "Studio-DeLight": {
194
+ "repo": "prithivMLmods/QIE-2511-Studio-DeLight",
195
+ "weights": "QIE-2511-Studio-DeLight-5000.safetensors",
196
+ "adapter_name": "studio-delight"
197
+ },
198
+ "Cinematic-FlatLog": {
199
+ "repo": "prithivMLmods/QIE-2511-Cinematic-FlatLog-Control",
200
+ "weights": "QIE-2511-Cinematic-FlatLog-Control-3200.safetensors",
201
+ "adapter_name": "flat-log"
202
+ },
203
+ }
204
+
205
+ LOADED_ADAPTERS = set()
206
+
207
  def update_dimensions_on_upload(image):
208
+ if image is None:
209
+ return 1024, 1024
210
+
211
+ original_width, original_height = image.size
212
+ if original_width <= 0 or original_height <= 0:
213
+ return 1024, 1024
214
+
215
+ # Keep roughly a 1MP canvas while preserving the source aspect ratio.
216
+ # The old long-side=1024 rule shrank portrait/wide photos too aggressively.
217
+ target_area = 1024 * 1024
218
+ max_side = 1536
219
+ aspect_ratio = original_width / original_height
220
+ new_width = int((target_area * aspect_ratio) ** 0.5)
221
+ new_height = int(new_width / aspect_ratio)
222
+
223
+ if max(new_width, new_height) > max_side:
224
+ scale = max_side / max(new_width, new_height)
225
+ new_width = int(new_width * scale)
226
+ new_height = int(new_height * scale)
227
+
228
+ new_width = max(64, (new_width // 32) * 32)
229
+ new_height = max(64, (new_height // 32) * 32)
230
 
231
  return new_width, new_height
232
 
 
247
 
248
  @spaces.GPU(duration=get_qwen_gpu_duration)
249
  def infer(
250
+ images,
251
+ prompt,
252
+ lora_adapter,
253
+ seed,
254
+ randomize_seed,
255
+ guidance_scale,
256
+ steps,
257
+ progress=gr.Progress(track_tqdm=True)
258
+ ):
259
+ gc.collect()
260
+ torch.cuda.empty_cache()
261
+
262
+ if not images:
263
+ raise gr.Error("Please upload at least one image to edit.")
264
+
265
+ pil_images = []
266
  if images is not None:
267
  if isinstance(images, (str, Image.Image, dict)) or hasattr(images, "name"):
268
  images = [images]
 
278
  path_or_img = item
279
 
280
  if isinstance(path_or_img, str):
281
+ pil_images.append(ImageOps.exif_transpose(Image.open(path_or_img)).convert("RGB"))
282
  elif isinstance(path_or_img, Image.Image):
283
+ pil_images.append(ImageOps.exif_transpose(path_or_img).convert("RGB"))
284
  elif hasattr(path_or_img, "path"):
285
+ pil_images.append(ImageOps.exif_transpose(Image.open(path_or_img.path)).convert("RGB"))
286
  else:
287
+ pil_images.append(ImageOps.exif_transpose(Image.open(path_or_img.name)).convert("RGB"))
288
+ except Exception as e:
289
+ print(f"Skipping invalid image item: {e}")
290
+ continue
291
+
292
+ if not pil_images:
293
+ raise gr.Error("Could not process uploaded images.")
294
+
295
+ spec = ADAPTER_SPECS.get(lora_adapter)
296
+ if not spec:
297
+ raise gr.Error(f"Configuration not found for: {lora_adapter}")
298
+
299
+ adapter_name = spec["adapter_name"]
300
+
301
+ if adapter_name not in LOADED_ADAPTERS:
302
+ print(f"--- Downloading and Loading Adapter: {lora_adapter} ---")
303
+ try:
304
+ pipe.load_lora_weights(
305
+ spec["repo"],
306
+ weight_name=spec["weights"],
307
+ adapter_name=adapter_name
308
+ )
309
+ LOADED_ADAPTERS.add(adapter_name)
310
+ except Exception as e:
311
+ raise gr.Error(f"Failed to load adapter {lora_adapter}: {e}")
312
+ else:
313
+ print(f"--- Adapter {lora_adapter} is already loaded. ---")
314
+
315
+ pipe.set_adapters([adapter_name], adapter_weights=[1.0])
316
+
317
+ if randomize_seed:
318
+ seed = random.randint(0, MAX_SEED)
319
+
320
+ generator = torch.Generator(device=device).manual_seed(seed)
321
+ negative_prompt = "worst quality, low quality, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, jpeg artifacts, signature, watermark, username, blurry"
322
+
323
+ width, height = update_dimensions_on_upload(pil_images[0])
324
+
325
+ try:
326
+ result_image = pipe(
327
+ image=pil_images,
328
+ prompt=prompt,
329
+ negative_prompt=negative_prompt,
330
+ height=height,
331
+ width=width,
332
+ num_inference_steps=steps,
333
+ generator=generator,
334
+ true_cfg_scale=guidance_scale,
335
+ ).images[0]
336
+
337
+ return result_image, seed
338
+
339
+ except Exception as e:
340
+ raise e
341
+ finally:
342
+ gc.collect()
343
+ torch.cuda.empty_cache()
344
+
345
  @spaces.GPU(duration=get_qwen_gpu_duration)
346
  def infer_example(images, prompt, lora_adapter):
347
+ if not images:
348
+ return None, 0
349
+
350
+ if isinstance(images, str):
351
+ images_list = [images]
352
+ else:
353
+ images_list = images
354
+
355
+ result, seed = infer(
356
+ images=images_list,
357
+ prompt=prompt,
358
+ lora_adapter=lora_adapter,
359
+ seed=0,
360
+ randomize_seed=True,
361
+ guidance_scale=1.0,
362
+ steps=4
363
+ )
364
+ return result, seed
365
+
366
+ css="""
367
+ #col-container {
368
+ margin: 0 auto;
369
+ max-width: 1000px;
370
+ }
371
+ #main-title h1 {font-size: 2.4em !important;}
372
+ """
373
+
374
+ with gr.Blocks() as demo:
375
+ with gr.Column(elem_id="col-container"):
376
+ gr.Markdown("# **Qwen-Image-Edit-2511-LoRAs-Fast**", elem_id="main-title")
377
+ gr.Markdown("Perform diverse image edits using specialized [LoRA](https://huggingface.co/models?other=base_model:adapter:Qwen/Qwen-Image-Edit-2511) adapters. Open on [GitHub](https://github.com/PRITHIVSAKTHIUR/Qwen-Image-Edit-2511-LoRAs-Fast-Lazy-Load).")
378
+
379
+ with gr.Row(equal_height=True):
380
+ with gr.Column():
381
  images = gr.File(
382
  label="Upload Images",
383
  file_count="multiple",
 
385
  file_types=["image"],
386
  height=300,
387
  )
388
+
389
+ prompt = gr.Text(
390
+ label="Edit Prompt",
391
+ #max_lines=1,
392
+ show_label=True,
393
+ placeholder="e.g., transform into anime..",
394
+ )
395
+
396
+ run_button = gr.Button("Edit Image", variant="primary")
397
+
398
+ with gr.Column():
399
+ output_image = gr.Image(label="Output Image", interactive=False, format="png", height=365)
400
+
401
+ with gr.Row():
402
+ lora_adapter = gr.Dropdown(
403
+ label="Choose Editing Style",
404
+ choices=list(ADAPTER_SPECS.keys()),
405
+ value="Photo-to-Anime"
406
+ )
407
+
408
+ with gr.Accordion("Advanced Settings", open=False, visible=False):
409
+ seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0)
410
+ randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
411
+ guidance_scale = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
412
+ steps = gr.Slider(label="Inference Steps", minimum=1, maximum=50, step=1, value=4)
413
+
414
+ gr.Examples(
415
+ examples=[
416
+ [["examples/B.jpg"], "Transform into anime.", "Photo-to-Anime"],
417
+ [["examples/HRP.jpg"], "Transform into a hyper-realistic face portrait.", "Hyper-Realistic-Portrait"],
418
+ [["examples/A.jpeg"], "Rotate the camera 45 degrees to the right.", "Multiple-Angles"],
419
+ [["examples/U.jpg"], "Upscale this picture to 4K resolution.", "Upscaler"],
420
+ [["examples/L1.jpg", "examples/L2.jpg"], "Apply the lighting from image 2 to image 1.", "Any-light"],
421
+ [["examples/PP1.jpg"], "cinematic polaroid with soft grain subtle vignette gentle lighting white frame handwritten photographed by hf‪‪‬ preserving realistic texture and details", "Polaroid-Photo"],
422
+ [["examples/Z1.jpg"], "Front-right quarter view.", "Fal-Multiple-Angles"],
423
+ [["examples/URP.jpg"], "Transform into a cinematic flat log.", "Cinematic-FlatLog"],
424
+ [["examples/SL.jpg"], "Neutral uniform lighting Preserve identity and composition.", "Studio-DeLight"],
425
+ [["examples/PI.jpg"], "Transform it into Pixar-inspired 3D.", "Pixar-Inspired-3D"],
426
+ [["examples/MT.jpg"], "Paint with manga tone.", "Manga-Tone"],
427
+ [["examples/NCB.jpg"], "Transform into a noir comic book style.", "Noir-Comic-Book"],
428
+ [["examples/URP.jpg"], "ultra-realistic portrait.", "Ultra-Realistic-Portrait"],
429
+ [["examples/MN.jpg"], "Transform into Midnight Noir Eyes Spotlight.", "Midnight-Noir-Eyes-Spotlight"],
430
+ [["examples/ST1.jpg", "examples/ST2.jpg"], "Convert Image 1 to the style of Image 2.", "Style-Transfer"],
431
+ [["examples/R1.jpg"], "Change the picture to realistic photograph.", "Anything2Real"],
432
+ [["examples/UA.jpeg"], "Unblur and upscale.", "Unblur-Anything"],
433
+ [["examples/L1.jpg", "examples/L2.jpg"], "Refer to the color tone, remove the original lighting from Image 1, and relight Image 1 based on the lighting and color tone of Image 2.", "Light-Migration"],
434
+ [["examples/P1.jpg"], "Transform into anime (while preserving the background and remaining elements maintaining realism and original details.)", "Anime-V2"],
435
+ ],
436
+ inputs=[images, prompt, lora_adapter],
437
+ outputs=[output_image, seed],
438
+ fn=infer_example,
439
+ cache_examples=False,
440
+ label="Examples"
441
+ )
442
+
443
+ gr.Markdown("[*](https://huggingface.co/spaces/prithivMLmods/Qwen-Image-Edit-2511-LoRAs-Fast)This is still an experimental Space for Qwen-Image-Edit-2511.")
444
+
445
+ run_button.click(
446
+ fn=infer,
447
+ inputs=[images, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps],
448
+ outputs=[output_image, seed]
449
+ )
450
+
451
+ if __name__ == "__main__":
452
  demo.queue(max_size=30).launch(css=css, theme=orange_red_theme, mcp_server=True, ssr_mode=False, show_error=True)