prithivMLmods commited on
Commit
6798543
·
verified ·
1 Parent(s): 3fa5cc1

update app

Browse files
Files changed (1) hide show
  1. app.py +112 -105
app.py CHANGED
@@ -84,8 +84,11 @@ class OrangeRedTheme(Soft):
84
 
85
  orange_red_theme = OrangeRedTheme()
86
 
87
- # --- Hardware Setup ---
88
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
 
 
 
89
  print("Using device:", device)
90
 
91
  from diffusers import FlowMatchEulerDiscreteScheduler
@@ -95,7 +98,6 @@ from qwenimage.qwen_fa3_processor import QwenDoubleStreamAttnProcessorFA3
95
 
96
  dtype = torch.bfloat16
97
 
98
- # --- Model Loading ---
99
  pipe = QwenImageEditPlusPipeline.from_pretrained(
100
  "Qwen/Qwen-Image-Edit-2511",
101
  transformer=QwenImageTransformer2DModel.from_pretrained(
@@ -117,7 +119,6 @@ MAX_SEED = np.iinfo(np.int32).max
117
  TMP_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp_rerun')
118
  os.makedirs(TMP_DIR, exist_ok=True)
119
 
120
- # --- Adapters ---
121
  ADAPTER_SPECS = {
122
  "Multiple-Angles": {
123
  "repo": "dx8152/Qwen-Edit-2509-Multiple-angles",
@@ -155,7 +156,7 @@ def update_dimensions_on_upload(image):
155
 
156
  @spaces.GPU
157
  def infer(
158
- input_gallery,
159
  prompt,
160
  lora_adapter,
161
  seed,
@@ -167,10 +168,36 @@ def infer(
167
  gc.collect()
168
  torch.cuda.empty_cache()
169
 
170
- if not input_gallery:
171
  raise gr.Error("Please upload at least one image to edit.")
172
 
173
- # --- Adapter Loading ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  spec = ADAPTER_SPECS.get(lora_adapter)
175
  if not spec:
176
  raise gr.Error(f"Configuration not found for: {lora_adapter}")
@@ -193,107 +220,87 @@ def infer(
193
 
194
  pipe.set_adapters([adapter_name], adapter_weights=[1.0])
195
 
196
- # --- Setup Rerun ---
197
- run_id = str(uuid.uuid4())
198
- if hasattr(rr, "new_recording"):
199
- rec = rr.new_recording(application_id="Qwen-Image-Edit", recording_id=run_id)
200
- elif hasattr(rr, "RecordingStream"):
201
- rec = rr.RecordingStream(application_id="Qwen-Image-Edit", recording_id=run_id)
202
- else:
203
- rr.init("Qwen-Image-Edit", recording_id=run_id, spawn=False)
204
- rec = rr
205
-
206
- # --- Processing Loop ---
207
- # gr.Gallery(type="pil") returns a list of tuples: [(PIL.Image, str_caption), ...]
208
- # We iterate over them.
209
-
210
- total_images = len(input_gallery)
211
-
212
- for i, item in enumerate(input_gallery):
213
- # Handle format: item might be (image, caption) tuple or just image depending on version/updates
214
- if isinstance(item, (tuple, list)):
215
- input_pil = item[0]
216
- else:
217
- input_pil = item
218
-
219
- if randomize_seed:
220
- current_seed = random.randint(0, MAX_SEED)
221
- else:
222
- current_seed = seed
223
 
224
- generator = torch.Generator(device=device).manual_seed(current_seed)
225
- 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"
226
 
227
- input_pil = input_pil.convert("RGB")
228
- width, height = update_dimensions_on_upload(input_pil)
229
 
230
- try:
231
- progress((i + 0.5) / total_images, desc=f"Processing Image {i+1}/{total_images}...")
232
-
233
- with torch.inference_mode():
234
- result_image = pipe(
235
- image=input_pil,
236
- prompt=prompt,
237
- negative_prompt=negative_prompt,
238
- height=height,
239
- width=width,
240
- num_inference_steps=steps,
241
- generator=generator,
242
- true_cfg_scale=guidance_scale,
243
- ).images[0]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
- # --- Log to Rerun ---
246
- # We use set_time_sequence to create a timeline slider in the Rerun viewer
247
- # allowing the user to slide through their batch of images.
248
- rec.set_time_sequence("batch_index", i)
249
- rec.log("images/original", rr.Image(np.array(input_pil)))
250
- rec.log("images/edited", rr.Image(np.array(result_image)))
251
 
252
- except Exception as e:
253
- print(f"Error processing image {i}: {e}")
254
- continue
255
- finally:
256
- # Clear VRAM after every image to avoid stacking up memory usage
257
- gc.collect()
258
- torch.cuda.empty_cache()
259
-
260
- # Save RRD
261
- rrd_path = os.path.join(TMP_DIR, f"{run_id}.rrd")
262
- rec.save(rrd_path)
263
-
264
- return rrd_path, seed
 
265
 
266
  @spaces.GPU
267
- def infer_example(input_gallery, prompt, lora_adapter):
268
- # Wrapper for examples
269
- if not input_gallery:
270
  return None, 0
271
 
272
- # input_gallery comes as a list of paths from Examples,
273
- # we need to load them as PIL images to mimic the Gallery output structure for the main function if needed,
274
- # BUT gr.Gallery in examples usually passes list of paths.
275
- # The main logic above expects tuples of (PIL, caption) OR PIL.
276
- # Let's ensure we convert paths to PIL here.
277
-
278
- processed_gallery = []
279
- for path in input_gallery:
280
- if isinstance(path, str):
281
- processed_gallery.append((Image.open(path), ""))
282
- else:
283
- processed_gallery.append((path, "")) # Already PIL or weird format
284
-
285
  result_rrd, seed = infer(
286
- processed_gallery,
287
- prompt,
288
- lora_adapter,
289
- 0, # seed
290
- True, # randomize
291
- 1.0, # guidance
292
- 4 # steps
293
  )
294
  return result_rrd, seed
295
 
296
- # --- Gradio UI Layout ---
297
  css="""
298
  #col-container {
299
  margin: 0 auto;
@@ -304,16 +311,17 @@ css="""
304
 
305
  with gr.Blocks() as demo:
306
  with gr.Column(elem_id="col-container"):
307
- gr.Markdown("# **Qwen-Image-Edit-2511-LoRAs-Fast (Multi-Image)**", elem_id="main-title")
308
- gr.Markdown("Perform diverse image edits using specialized adapters. Upload multiple images to process them in a batch. Use the timeline slider in the output to view results.")
309
 
310
  with gr.Row(equal_height=True):
311
  with gr.Column():
312
- # Changed to Gallery for multi-upload
313
- input_gallery = gr.Gallery(
314
  label="Upload Images",
315
- type="pil",
316
  columns=2,
 
317
  height=300,
318
  allow_preview=True
319
  )
@@ -324,7 +332,7 @@ with gr.Blocks() as demo:
324
  placeholder="e.g., transform into anime..",
325
  )
326
 
327
- run_button = gr.Button("Edit Batch", variant="primary")
328
 
329
  with gr.Column():
330
  rerun_output = Rerun(
@@ -344,25 +352,24 @@ with gr.Blocks() as demo:
344
  guidance_scale = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
345
  steps = gr.Slider(label="Inference Steps", minimum=1, maximum=50, step=1, value=4)
346
 
347
- # Updated Examples to be lists of paths
348
  gr.Examples(
349
  examples=[
350
  [["examples/B.jpg"], "Transform into anime.", "Photo-to-Anime"],
351
  [["examples/A.jpeg"], "Rotate the camera 45 degrees to the right.", "Multiple-Angles"],
352
- [["examples/B.jpg", "examples/A.jpeg"], "Transform into sketches.", "Photo-to-Anime"],
353
  ],
354
- inputs=[input_gallery, prompt, lora_adapter],
355
  outputs=[rerun_output, seed],
356
  fn=infer_example,
357
  cache_examples=False,
358
  label="Examples"
359
  )
360
 
361
- gr.Markdown("[*](https://huggingface.co/spaces/prithivMLmods/Qwen-Image-Edit-2511-LoRAs-Fast) Experimental Space.")
362
 
363
  run_button.click(
364
  fn=infer,
365
- inputs=[input_gallery, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps],
366
  outputs=[rerun_output, seed]
367
  )
368
 
 
84
 
85
  orange_red_theme = OrangeRedTheme()
86
 
87
+ # --- Model & Device Setup ---
88
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
89
+
90
+ print("CUDA_VISIBLE_DEVICES=", os.environ.get("CUDA_VISIBLE_DEVICES"))
91
+ print("torch.__version__ =", torch.__version__)
92
  print("Using device:", device)
93
 
94
  from diffusers import FlowMatchEulerDiscreteScheduler
 
98
 
99
  dtype = torch.bfloat16
100
 
 
101
  pipe = QwenImageEditPlusPipeline.from_pretrained(
102
  "Qwen/Qwen-Image-Edit-2511",
103
  transformer=QwenImageTransformer2DModel.from_pretrained(
 
119
  TMP_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp_rerun')
120
  os.makedirs(TMP_DIR, exist_ok=True)
121
 
 
122
  ADAPTER_SPECS = {
123
  "Multiple-Angles": {
124
  "repo": "dx8152/Qwen-Edit-2509-Multiple-angles",
 
156
 
157
  @spaces.GPU
158
  def infer(
159
+ images,
160
  prompt,
161
  lora_adapter,
162
  seed,
 
168
  gc.collect()
169
  torch.cuda.empty_cache()
170
 
171
+ if not images:
172
  raise gr.Error("Please upload at least one image to edit.")
173
 
174
+ # --- Process Gallery Input ---
175
+ pil_images = []
176
+ if images is not None:
177
+ for item in images:
178
+ # Gradio Gallery returns a list of tuples (filepath, label) or (image, label) depending on version/type
179
+ try:
180
+ # Check for tuple (standard Gradio Gallery output)
181
+ if isinstance(item, tuple) or isinstance(item, list):
182
+ path_or_img = item[0]
183
+ else:
184
+ path_or_img = item
185
+
186
+ if isinstance(path_or_img, str):
187
+ pil_images.append(Image.open(path_or_img).convert("RGB"))
188
+ elif isinstance(path_or_img, Image.Image):
189
+ pil_images.append(path_or_img.convert("RGB"))
190
+ else:
191
+ # Fallback for complex Gradio objects
192
+ pil_images.append(Image.open(path_or_img.name).convert("RGB"))
193
+ except Exception as e:
194
+ print(f"Skipping invalid image item: {e}")
195
+ continue
196
+
197
+ if not pil_images:
198
+ raise gr.Error("Could not process uploaded images.")
199
+
200
+ # --- Load Adapter ---
201
  spec = ADAPTER_SPECS.get(lora_adapter)
202
  if not spec:
203
  raise gr.Error(f"Configuration not found for: {lora_adapter}")
 
220
 
221
  pipe.set_adapters([adapter_name], adapter_weights=[1.0])
222
 
223
+ # --- Setup Generation ---
224
+ if randomize_seed:
225
+ seed = random.randint(0, MAX_SEED)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
+ generator = torch.Generator(device=device).manual_seed(seed)
228
+ 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"
229
 
230
+ # Use dimensions from the first image for the output
231
+ width, height = update_dimensions_on_upload(pil_images[0])
232
 
233
+ try:
234
+ progress(0.4, desc="Generating Image...")
235
+
236
+ # Pass the list of PIL images to the pipeline
237
+ result_image = pipe(
238
+ image=pil_images,
239
+ prompt=prompt,
240
+ negative_prompt=negative_prompt,
241
+ height=height,
242
+ width=width,
243
+ num_inference_steps=steps,
244
+ generator=generator,
245
+ true_cfg_scale=guidance_scale,
246
+ ).images[0]
247
+
248
+ # --- Rerun Visualization Logic ---
249
+ progress(0.9, desc="Preparing Rerun Visualization...")
250
+
251
+ run_id = str(uuid.uuid4())
252
+
253
+ # Handle different Rerun SDK versions
254
+ rec = None
255
+ if hasattr(rr, "new_recording"):
256
+ rec = rr.new_recording(application_id="Qwen-Image-Edit", recording_id=run_id)
257
+ elif hasattr(rr, "RecordingStream"):
258
+ rec = rr.RecordingStream(application_id="Qwen-Image-Edit", recording_id=run_id)
259
+ else:
260
+ rr.init("Qwen-Image-Edit", recording_id=run_id, spawn=False)
261
+ rec = rr
262
 
263
+ # Log all input images
264
+ for i, img in enumerate(pil_images):
265
+ rec.log(f"images/input_{i}", rr.Image(np.array(img)))
 
 
 
266
 
267
+ # Log result
268
+ rec.log("images/edited_result", rr.Image(np.array(result_image)))
269
+
270
+ # Save RRD
271
+ rrd_path = os.path.join(TMP_DIR, f"{run_id}.rrd")
272
+ rec.save(rrd_path)
273
+
274
+ return rrd_path, seed
275
+
276
+ except Exception as e:
277
+ raise e
278
+ finally:
279
+ gc.collect()
280
+ torch.cuda.empty_cache()
281
 
282
  @spaces.GPU
283
+ def infer_example(images, prompt, lora_adapter):
284
+ # Wrapper for examples (images coming from gr.Examples are usually list of filepaths)
285
+ if not images:
286
  return None, 0
287
 
288
+ # Ensure input is treated as a list even if example passes single path string
289
+ if isinstance(images, str):
290
+ images = [images]
291
+
292
+ # infer expects the gallery format or list of paths
 
 
 
 
 
 
 
 
293
  result_rrd, seed = infer(
294
+ images=images,
295
+ prompt=prompt,
296
+ lora_adapter=lora_adapter,
297
+ seed=0,
298
+ randomize_seed=True,
299
+ guidance_scale=1.0,
300
+ steps=4
301
  )
302
  return result_rrd, seed
303
 
 
304
  css="""
305
  #col-container {
306
  margin: 0 auto;
 
311
 
312
  with gr.Blocks() as demo:
313
  with gr.Column(elem_id="col-container"):
314
+ gr.Markdown("# **Qwen-Image-Edit-2511-LoRAs-Fast**", elem_id="main-title")
315
+ gr.Markdown("Perform diverse image edits using specialized [LoRA](https://huggingface.co/models?other=base_model:adapter:Qwen/Qwen-Image-Edit-2511) adapters. Upload one or more images.")
316
 
317
  with gr.Row(equal_height=True):
318
  with gr.Column():
319
+ # Changed to Gallery to support multiple images
320
+ images = gr.Gallery(
321
  label="Upload Images",
322
+ type="filepath",
323
  columns=2,
324
+ rows=1,
325
  height=300,
326
  allow_preview=True
327
  )
 
332
  placeholder="e.g., transform into anime..",
333
  )
334
 
335
+ run_button = gr.Button("Edit Image", variant="primary")
336
 
337
  with gr.Column():
338
  rerun_output = Rerun(
 
352
  guidance_scale = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
353
  steps = gr.Slider(label="Inference Steps", minimum=1, maximum=50, step=1, value=4)
354
 
355
+ # Updated examples to use list of paths for Gallery input
356
  gr.Examples(
357
  examples=[
358
  [["examples/B.jpg"], "Transform into anime.", "Photo-to-Anime"],
359
  [["examples/A.jpeg"], "Rotate the camera 45 degrees to the right.", "Multiple-Angles"],
 
360
  ],
361
+ inputs=[images, prompt, lora_adapter],
362
  outputs=[rerun_output, seed],
363
  fn=infer_example,
364
  cache_examples=False,
365
  label="Examples"
366
  )
367
 
368
+ gr.Markdown("[*](https://huggingface.co/spaces/prithivMLmods/Qwen-Image-Edit-2511-LoRAs-Fast)This is still an experimental Space for Qwen-Image-Edit-2511.")
369
 
370
  run_button.click(
371
  fn=infer,
372
+ inputs=[images, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps],
373
  outputs=[rerun_output, seed]
374
  )
375