Professional Noob commited on
Commit
2ce9dc7
·
verified ·
1 Parent(s): 30144f4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +120 -61
app.py CHANGED
@@ -101,12 +101,12 @@ dtype = torch.bfloat16
101
  pipe = QwenImageEditPlusPipeline.from_pretrained(
102
  "Qwen/Qwen-Image-Edit-2511",
103
  transformer=QwenImageTransformer2DModel.from_pretrained(
104
- "Pr0f3ssi0n4ln00b/Phr00t-Qwen-Rapid-AIO", # -> diffusers compatiable transformer weights extracted from [Phr00t/Qwen-Image-Edit-Rapid-AIO]
105
- subfolder='transformer',
106
  torch_dtype=dtype,
107
- device_map='cuda'
108
  ),
109
- torch_dtype=dtype
110
  ).to(device)
111
 
112
  # Apply FA3 Optimization
@@ -118,84 +118,95 @@ except Exception as e:
118
 
119
  MAX_SEED = np.iinfo(np.int32).max
120
 
 
 
 
 
 
 
121
  # Define the config for all adapters
122
  ADAPTER_SPECS = {
123
  "Photo-to-Anime": {
124
  "repo": "autoweeb/Qwen-Image-Edit-2509-Photo-to-Anime",
125
  "weights": "Qwen-Image-Edit-2509-Photo-to-Anime_000001000.safetensors",
126
- "adapter_name": "anime"
127
  },
128
  "Semirealistic-photo-detailer": {
129
  "repo": "rzgar/Qwen-Image-Edit-semi-realistic-detailer",
130
  "weights": "Qwen-Image-Edit-Anime-Semi-Realistic-Detailer-v1.safetensors",
131
- "adapter_name": "semirealistic"
132
  },
133
  "Any2Real_Alpha": {
134
  "repo": "lrzjason/QwenEdit-Anything2Real_Alpha",
135
  "weights": "Anything2RealAlpha.safetensors",
136
- "adapter_name": "photorealpha"
137
  },
138
  "Any2Real_2601": {
139
  "repo": "lrzjason/Anything2Real_2601",
140
  "weights": "anything2real_2601_A_final_patched.safetensors",
141
- "adapter_name": "photoreal"
142
  },
143
  "Multiple-Angles": {
144
  "repo": "dx8152/Qwen-Edit-2509-Multiple-angles",
145
  "weights": "镜头转换.safetensors",
146
- "adapter_name": "multiple-angles"
147
  },
148
  "Light-Restoration": {
149
  "repo": "dx8152/Qwen-Image-Edit-2509-Light_restoration",
150
  "weights": "移除光影.safetensors",
151
- "adapter_name": "light-restoration"
152
  },
153
  "Relight": {
154
  "repo": "dx8152/Qwen-Image-Edit-2509-Relight",
155
  "weights": "Qwen-Edit-Relight.safetensors",
156
- "adapter_name": "relight"
157
  },
158
  "Multi-Angle-Lighting": {
159
  "repo": "dx8152/Qwen-Edit-2509-Multi-Angle-Lighting",
160
  "weights": "多角度灯光-251116.safetensors",
161
- "adapter_name": "multi-angle-lighting"
162
  },
163
  "Edit-Skin": {
164
  "repo": "tlennon-ie/qwen-edit-skin",
165
  "weights": "qwen-edit-skin_1.1_000002750.safetensors",
166
- "adapter_name": "edit-skin"
167
  },
168
  "Next-Scene": {
169
  "repo": "lovis93/next-scene-qwen-image-lora-2509",
170
  "weights": "next-scene_lora-v2-3000.safetensors",
171
- "adapter_name": "next-scene"
172
  },
173
  "Flat-Log": {
174
  "repo": "tlennon-ie/QwenEdit2509-FlatLogColor",
175
  "weights": "QwenEdit2509-FlatLogColor.safetensors",
176
- "adapter_name": "flat-log"
177
  },
178
  "Upscale-Image": {
179
  "repo": "vafipas663/Qwen-Edit-2509-Upscale-LoRA",
180
  "weights": "qwen-edit-enhance_64-v3_000001000.safetensors",
181
- "adapter_name": "upscale-image"
182
  },
183
  "Upscale2K": {
184
  "repo": "valiantcat/Qwen-Image-Edit-2509-Upscale2K",
185
  "weights": "qwen_image_edit_2509_upscale.safetensors",
186
- "adapter_name": "upscale-2k"
187
  },
188
  }
189
 
 
 
 
 
 
190
  # Track what is currently loaded in memory
191
  LOADED_ADAPTERS = set()
192
 
193
  def update_dimensions_on_upload(image):
194
  if image is None:
195
  return 1024, 1024
196
-
197
  original_width, original_height = image.size
198
-
199
  if original_width > original_height:
200
  new_width = 1024
201
  aspect_ratio = original_height / original_width
@@ -204,13 +215,29 @@ def update_dimensions_on_upload(image):
204
  new_height = 1024
205
  aspect_ratio = original_width / original_height
206
  new_width = int(new_height * aspect_ratio)
207
-
208
  # Ensure dimensions are multiples of 8
209
  new_width = (new_width // 8) * 8
210
  new_height = (new_height // 8) * 8
211
-
212
  return new_width, new_height
213
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  @spaces.GPU
215
  def infer(
216
  input_image,
@@ -220,47 +247,60 @@ def infer(
220
  randomize_seed,
221
  guidance_scale,
222
  steps,
223
- progress=gr.Progress(track_tqdm=True)
224
  ):
225
  # Cleanup memory before starting
226
  gc.collect()
227
- torch.cuda.empty_cache()
 
228
 
229
  if input_image is None:
230
  raise gr.Error("Please upload an image to edit.")
231
 
232
- # 1. Get Config for Selected Adapter
233
- spec = ADAPTER_SPECS.get(lora_adapter)
234
- if not spec:
235
- raise gr.Error(f"Configuration not found for: {lora_adapter}")
236
-
237
- adapter_name = spec["adapter_name"]
238
-
239
- # 2. Lazy Loading Logic
240
- if adapter_name not in LOADED_ADAPTERS:
241
- print(f"--- Downloading and Loading Adapter: {lora_adapter} ---")
242
  try:
243
- pipe.load_lora_weights(
244
- spec["repo"],
245
- weight_name=spec["weights"],
246
- adapter_name=adapter_name
247
- )
248
- LOADED_ADAPTERS.add(adapter_name)
249
- except Exception as e:
250
- raise gr.Error(f"Failed to load adapter {lora_adapter}: {e}")
251
  else:
252
- print(f"--- Adapter {lora_adapter} is already loaded. ---")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
- # 3. Activate the specific adapter
255
- # Unload others by exclusively setting this one to weight 1.0
256
- pipe.set_adapters([adapter_name], adapter_weights=[1.0])
257
 
258
- # 4. Standard Inference Setup
259
  if randomize_seed:
260
  seed = random.randint(0, MAX_SEED)
261
 
262
  generator = torch.Generator(device=device).manual_seed(seed)
263
- 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"
 
 
 
264
 
265
  original_image = input_image.convert("RGB")
266
  width, height = update_dimensions_on_upload(original_image)
@@ -276,7 +316,7 @@ def infer(
276
  generator=generator,
277
  true_cfg_scale=guidance_scale,
278
  ).images[0]
279
-
280
  return result, seed
281
 
282
  except Exception as e:
@@ -284,20 +324,21 @@ def infer(
284
  finally:
285
  # Cleanup
286
  gc.collect()
287
- torch.cuda.empty_cache()
 
288
 
289
  @spaces.GPU
290
  def infer_example(input_image, prompt, lora_adapter):
291
  if input_image is None:
292
  return None, 0
293
-
294
  input_pil = input_image.convert("RGB")
295
  guidance_scale = 1.0
296
  steps = 4
297
  result, seed = infer(input_pil, prompt, lora_adapter, 0, True, guidance_scale, steps)
298
  return result, seed
299
 
300
- css="""
301
  #col-container {
302
  margin: 0 auto;
303
  max-width: 960px;
@@ -308,12 +349,16 @@ css="""
308
  with gr.Blocks() as demo:
309
  with gr.Column(elem_id="col-container"):
310
  gr.Markdown("# **Qwen-Image-Edit-2511-LoRAs-Fast**", elem_id="main-title")
311
- gr.Markdown("Perform diverse image edits using specialized [LoRA](https://huggingface.co/models?other=base_model:adapter:Qwen/Qwen-Image-Edit-2511) adapters for the [Qwen-Image-Edit](https://huggingface.co/Qwen/Qwen-Image-Edit-2511) model.")
 
 
 
 
312
 
313
  with gr.Row(equal_height=True):
314
  with gr.Column():
315
  input_image = gr.Image(label="Upload Image", type="pil", height=290)
316
-
317
  prompt = gr.Text(
318
  label="Edit Prompt",
319
  show_label=True,
@@ -324,20 +369,28 @@ with gr.Blocks() as demo:
324
 
325
  with gr.Column():
326
  output_image = gr.Image(label="Output Image", interactive=False, format="png", height=353)
327
-
328
  with gr.Row():
329
- # Dynamic keys based on the config dict
330
  lora_adapter = gr.Dropdown(
331
  label="Choose Editing Style",
332
- choices=list(ADAPTER_SPECS.keys()),
333
- value="Photo-to-Anime"
334
  )
 
335
  with gr.Accordion("Advanced Settings", open=False, visible=False):
336
  seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0)
337
  randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
338
  guidance_scale = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
339
  steps = gr.Slider(label="Inference Steps", minimum=1, maximum=50, step=1, value=4)
340
-
 
 
 
 
 
 
 
341
  gr.Examples(
342
  examples=[
343
  ["examples/1.jpg", "Transform into anime.", "Photo-to-Anime"],
@@ -362,14 +415,20 @@ with gr.Blocks() as demo:
362
  outputs=[output_image, seed],
363
  fn=infer_example,
364
  cache_examples=False,
365
- label="Examples"
366
  )
367
 
368
  run_button.click(
369
  fn=infer,
370
  inputs=[input_image, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps],
371
- outputs=[output_image, seed]
372
  )
373
 
374
  if __name__ == "__main__":
375
- demo.queue(max_size=30).launch(css=css, theme=orange_red_theme, mcp_server=True, ssr_mode=False, show_error=True)
 
 
 
 
 
 
 
101
  pipe = QwenImageEditPlusPipeline.from_pretrained(
102
  "Qwen/Qwen-Image-Edit-2511",
103
  transformer=QwenImageTransformer2DModel.from_pretrained(
104
+ "Pr0f3ssi0n4ln00b/Phr00t-Qwen-Rapid-AIO", # diffusers-compatible transformer weights
105
+ subfolder="transformer",
106
  torch_dtype=dtype,
107
+ device_map="cuda",
108
  ),
109
+ torch_dtype=dtype,
110
  ).to(device)
111
 
112
  # Apply FA3 Optimization
 
118
 
119
  MAX_SEED = np.iinfo(np.int32).max
120
 
121
+ # -------------------------
122
+ # LoRA adapters + presets
123
+ # -------------------------
124
+
125
+ NONE_LORA = "None"
126
+
127
  # Define the config for all adapters
128
  ADAPTER_SPECS = {
129
  "Photo-to-Anime": {
130
  "repo": "autoweeb/Qwen-Image-Edit-2509-Photo-to-Anime",
131
  "weights": "Qwen-Image-Edit-2509-Photo-to-Anime_000001000.safetensors",
132
+ "adapter_name": "anime",
133
  },
134
  "Semirealistic-photo-detailer": {
135
  "repo": "rzgar/Qwen-Image-Edit-semi-realistic-detailer",
136
  "weights": "Qwen-Image-Edit-Anime-Semi-Realistic-Detailer-v1.safetensors",
137
+ "adapter_name": "semirealistic",
138
  },
139
  "Any2Real_Alpha": {
140
  "repo": "lrzjason/QwenEdit-Anything2Real_Alpha",
141
  "weights": "Anything2RealAlpha.safetensors",
142
+ "adapter_name": "photorealpha",
143
  },
144
  "Any2Real_2601": {
145
  "repo": "lrzjason/Anything2Real_2601",
146
  "weights": "anything2real_2601_A_final_patched.safetensors",
147
+ "adapter_name": "photoreal",
148
  },
149
  "Multiple-Angles": {
150
  "repo": "dx8152/Qwen-Edit-2509-Multiple-angles",
151
  "weights": "镜头转换.safetensors",
152
+ "adapter_name": "multiple-angles",
153
  },
154
  "Light-Restoration": {
155
  "repo": "dx8152/Qwen-Image-Edit-2509-Light_restoration",
156
  "weights": "移除光影.safetensors",
157
+ "adapter_name": "light-restoration",
158
  },
159
  "Relight": {
160
  "repo": "dx8152/Qwen-Image-Edit-2509-Relight",
161
  "weights": "Qwen-Edit-Relight.safetensors",
162
+ "adapter_name": "relight",
163
  },
164
  "Multi-Angle-Lighting": {
165
  "repo": "dx8152/Qwen-Edit-2509-Multi-Angle-Lighting",
166
  "weights": "多角度灯光-251116.safetensors",
167
+ "adapter_name": "multi-angle-lighting",
168
  },
169
  "Edit-Skin": {
170
  "repo": "tlennon-ie/qwen-edit-skin",
171
  "weights": "qwen-edit-skin_1.1_000002750.safetensors",
172
+ "adapter_name": "edit-skin",
173
  },
174
  "Next-Scene": {
175
  "repo": "lovis93/next-scene-qwen-image-lora-2509",
176
  "weights": "next-scene_lora-v2-3000.safetensors",
177
+ "adapter_name": "next-scene",
178
  },
179
  "Flat-Log": {
180
  "repo": "tlennon-ie/QwenEdit2509-FlatLogColor",
181
  "weights": "QwenEdit2509-FlatLogColor.safetensors",
182
+ "adapter_name": "flat-log",
183
  },
184
  "Upscale-Image": {
185
  "repo": "vafipas663/Qwen-Edit-2509-Upscale-LoRA",
186
  "weights": "qwen-edit-enhance_64-v3_000001000.safetensors",
187
+ "adapter_name": "upscale-image",
188
  },
189
  "Upscale2K": {
190
  "repo": "valiantcat/Qwen-Image-Edit-2509-Upscale2K",
191
  "weights": "qwen_image_edit_2509_upscale.safetensors",
192
+ "adapter_name": "upscale-2k",
193
  },
194
  }
195
 
196
+ # Preset prompt per LoRA (leave empty for others)
197
+ LORA_PRESET_PROMPTS = {
198
+ "Any2Real_2601": "change the picture 1 to realistic photograph",
199
+ }
200
+
201
  # Track what is currently loaded in memory
202
  LOADED_ADAPTERS = set()
203
 
204
  def update_dimensions_on_upload(image):
205
  if image is None:
206
  return 1024, 1024
207
+
208
  original_width, original_height = image.size
209
+
210
  if original_width > original_height:
211
  new_width = 1024
212
  aspect_ratio = original_height / original_width
 
215
  new_height = 1024
216
  aspect_ratio = original_width / original_height
217
  new_width = int(new_height * aspect_ratio)
218
+
219
  # Ensure dimensions are multiples of 8
220
  new_width = (new_width // 8) * 8
221
  new_height = (new_height // 8) * 8
222
+
223
  return new_width, new_height
224
 
225
+ def on_lora_change(selected_lora, current_prompt):
226
+ """
227
+ - If user selects a LoRA with a preset prompt:
228
+ - Fill prompt ONLY if it's currently empty or matches the previous preset behavior.
229
+ (To keep this simple and non-destructive: only fill when empty.)
230
+ - If user selects None: don't touch prompt.
231
+ """
232
+ if selected_lora == NONE_LORA:
233
+ return gr.update(value=current_prompt)
234
+
235
+ preset = LORA_PRESET_PROMPTS.get(selected_lora, "")
236
+ if preset and (current_prompt is None or str(current_prompt).strip() == ""):
237
+ return gr.update(value=preset)
238
+
239
+ return gr.update(value=current_prompt)
240
+
241
  @spaces.GPU
242
  def infer(
243
  input_image,
 
247
  randomize_seed,
248
  guidance_scale,
249
  steps,
250
+ progress=gr.Progress(track_tqdm=True),
251
  ):
252
  # Cleanup memory before starting
253
  gc.collect()
254
+ if torch.cuda.is_available():
255
+ torch.cuda.empty_cache()
256
 
257
  if input_image is None:
258
  raise gr.Error("Please upload an image to edit.")
259
 
260
+ # 1) Handle "None" LoRA: disable adapters
261
+ if lora_adapter == NONE_LORA:
262
+ # Ensure no adapters are active
 
 
 
 
 
 
 
263
  try:
264
+ pipe.set_adapters([], adapter_weights=[])
265
+ except Exception:
266
+ # Some versions may not like empty lists; as a fallback set weights of all loaded adapters to 0
267
+ if LOADED_ADAPTERS:
268
+ pipe.set_adapters(list(LOADED_ADAPTERS), adapter_weights=[0.0] * len(LOADED_ADAPTERS))
 
 
 
269
  else:
270
+ # 2) Get Config for Selected Adapter
271
+ spec = ADAPTER_SPECS.get(lora_adapter)
272
+ if not spec:
273
+ raise gr.Error(f"Configuration not found for: {lora_adapter}")
274
+
275
+ adapter_name = spec["adapter_name"]
276
+
277
+ # 3) Lazy Loading Logic
278
+ if adapter_name not in LOADED_ADAPTERS:
279
+ print(f"--- Downloading and Loading Adapter: {lora_adapter} ---")
280
+ try:
281
+ pipe.load_lora_weights(
282
+ spec["repo"],
283
+ weight_name=spec["weights"],
284
+ adapter_name=adapter_name,
285
+ )
286
+ LOADED_ADAPTERS.add(adapter_name)
287
+ except Exception as e:
288
+ raise gr.Error(f"Failed to load adapter {lora_adapter}: {e}")
289
+ else:
290
+ print(f"--- Adapter {lora_adapter} is already loaded. ---")
291
 
292
+ # 4) Activate ONLY the selected adapter
293
+ pipe.set_adapters([adapter_name], adapter_weights=[1.0])
 
294
 
295
+ # 5) Standard Inference Setup
296
  if randomize_seed:
297
  seed = random.randint(0, MAX_SEED)
298
 
299
  generator = torch.Generator(device=device).manual_seed(seed)
300
+ negative_prompt = (
301
+ "worst quality, low quality, bad anatomy, bad hands, text, error, missing fingers, "
302
+ "extra digit, fewer digits, cropped, jpeg artifacts, signature, watermark, username, blurry"
303
+ )
304
 
305
  original_image = input_image.convert("RGB")
306
  width, height = update_dimensions_on_upload(original_image)
 
316
  generator=generator,
317
  true_cfg_scale=guidance_scale,
318
  ).images[0]
319
+
320
  return result, seed
321
 
322
  except Exception as e:
 
324
  finally:
325
  # Cleanup
326
  gc.collect()
327
+ if torch.cuda.is_available():
328
+ torch.cuda.empty_cache()
329
 
330
  @spaces.GPU
331
  def infer_example(input_image, prompt, lora_adapter):
332
  if input_image is None:
333
  return None, 0
334
+
335
  input_pil = input_image.convert("RGB")
336
  guidance_scale = 1.0
337
  steps = 4
338
  result, seed = infer(input_pil, prompt, lora_adapter, 0, True, guidance_scale, steps)
339
  return result, seed
340
 
341
+ css = """
342
  #col-container {
343
  margin: 0 auto;
344
  max-width: 960px;
 
349
  with gr.Blocks() as demo:
350
  with gr.Column(elem_id="col-container"):
351
  gr.Markdown("# **Qwen-Image-Edit-2511-LoRAs-Fast**", elem_id="main-title")
352
+ gr.Markdown(
353
+ "Perform diverse image edits using specialized "
354
+ "[LoRA](https://huggingface.co/models?other=base_model:adapter:Qwen/Qwen-Image-Edit-2511) adapters for the "
355
+ "[Qwen-Image-Edit](https://huggingface.co/Qwen/Qwen-Image-Edit-2511) model."
356
+ )
357
 
358
  with gr.Row(equal_height=True):
359
  with gr.Column():
360
  input_image = gr.Image(label="Upload Image", type="pil", height=290)
361
+
362
  prompt = gr.Text(
363
  label="Edit Prompt",
364
  show_label=True,
 
369
 
370
  with gr.Column():
371
  output_image = gr.Image(label="Output Image", interactive=False, format="png", height=353)
372
+
373
  with gr.Row():
374
+ lora_choices = [NONE_LORA] + list(ADAPTER_SPECS.keys())
375
  lora_adapter = gr.Dropdown(
376
  label="Choose Editing Style",
377
+ choices=lora_choices,
378
+ value=NONE_LORA, # default is None
379
  )
380
+
381
  with gr.Accordion("Advanced Settings", open=False, visible=False):
382
  seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0)
383
  randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
384
  guidance_scale = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
385
  steps = gr.Slider(label="Inference Steps", minimum=1, maximum=50, step=1, value=4)
386
+
387
+ # When a user selects a LoRA, optionally auto-fill the prompt (only if the prompt is currently empty)
388
+ lora_adapter.change(
389
+ fn=on_lora_change,
390
+ inputs=[lora_adapter, prompt],
391
+ outputs=[prompt],
392
+ )
393
+
394
  gr.Examples(
395
  examples=[
396
  ["examples/1.jpg", "Transform into anime.", "Photo-to-Anime"],
 
415
  outputs=[output_image, seed],
416
  fn=infer_example,
417
  cache_examples=False,
418
+ label="Examples",
419
  )
420
 
421
  run_button.click(
422
  fn=infer,
423
  inputs=[input_image, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps],
424
+ outputs=[output_image, seed],
425
  )
426
 
427
  if __name__ == "__main__":
428
+ demo.queue(max_size=30).launch(
429
+ css=css,
430
+ theme=orange_red_theme,
431
+ mcp_server=True,
432
+ ssr_mode=False,
433
+ show_error=True,
434
+ )