prithivMLmods commited on
Commit
bab4acd
·
verified ·
1 Parent(s): 4c2f1aa

update app

Browse files
Files changed (1) hide show
  1. app.py +96 -120
app.py CHANGED
@@ -14,12 +14,12 @@ try:
14
  except ImportError:
15
  class spaces:
16
  @staticmethod
17
- def GPU(duration=30):
18
  def decorator(func):
19
  return func
20
  return decorator
21
 
22
- # --- Custom Theme Setup (Steel Blue) ---
23
  colors.steel_blue = colors.Color(
24
  name="steel_blue",
25
  c50="#EBF3F8",
@@ -90,15 +90,7 @@ steel_blue_theme = SteelBlueTheme()
90
 
91
  # --- Hardware Setup ---
92
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
93
-
94
- print("CUDA_VISIBLE_DEVICES=", os.environ.get("CUDA_VISIBLE_DEVICES"))
95
- print("torch.__version__ =", torch.__version__)
96
- print("cuda available:", torch.cuda.is_available())
97
- if torch.cuda.is_available():
98
- print("current device:", torch.cuda.current_device())
99
- print("device name:", torch.cuda.get_device_name(torch.cuda.current_device()))
100
-
101
- print("Using device:", device)
102
 
103
  # --- Imports for Custom Pipeline ---
104
  from diffusers import FlowMatchEulerDiscreteScheduler
@@ -106,9 +98,8 @@ from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
106
  from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
107
  from qwenimage.qwen_fa3_processor import QwenDoubleStreamAttnProcessorFA3
108
 
109
- dtype = torch.bfloat16
110
-
111
- # Load Pipeline with Rapid-AIO Transformer (Fast Version)
112
  pipe = QwenImageEditPlusPipeline.from_pretrained(
113
  "Qwen/Qwen-Image-Edit-2509",
114
  transformer=QwenImageTransformer2DModel.from_pretrained(
@@ -120,31 +111,37 @@ pipe = QwenImageEditPlusPipeline.from_pretrained(
120
  torch_dtype=dtype
121
  ).to(device)
122
 
123
- # --- Load Fusion/Texture/Face-Swap LoRAs ---
124
- print("Loading LoRA adapters...")
 
 
 
 
125
 
126
- # 1. Texture Edit
127
- pipe.load_lora_weights("tarn59/apply_texture_qwen_image_edit_2509",
128
- weight_name="apply_texture_v2_qwen_image_edit_2509.safetensors",
129
- adapter_name="texture-edit")
130
 
131
- # 2. Fuse Objects
132
- pipe.load_lora_weights("dx8152/Qwen-Image-Edit-2509-Fusion",
133
- weight_name="溶图.safetensors",
134
- adapter_name="fuse-objects")
135
 
136
- # 3. Face Swap
137
- pipe.load_lora_weights("Alissonerdx/BFS-Best-Face-Swap",
138
- weight_name="bfs_face_v1_qwen_image_edit_2509.safetensors",
139
- adapter_name="face-swap")
140
 
 
 
 
 
141
 
142
- # Attempt to set Flash Attention 3
143
  try:
144
  pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
145
  print("Flash Attention 3 Processor set successfully.")
146
  except Exception as e:
147
- print(f"Could not set FA3 processor (likely hardware mismatch): {e}. Using default attention.")
148
 
149
  MAX_SEED = np.iinfo(np.int32).max
150
 
@@ -163,15 +160,16 @@ def update_dimensions_on_upload(image):
163
  aspect_ratio = original_width / original_height
164
  new_width = int(new_height * aspect_ratio)
165
 
166
- # Ensure dimensions are multiples of 16 (safer for transformers)
167
  new_width = (new_width // 16) * 16
168
  new_height = (new_height // 16) * 16
169
 
170
  return new_width, new_height
171
 
172
- @spaces.GPU(duration=30)
173
  def infer(
174
- input_gallery_items,
 
175
  prompt,
176
  lora_adapter,
177
  seed,
@@ -180,34 +178,28 @@ def infer(
180
  steps,
181
  progress=gr.Progress(track_tqdm=True)
182
  ):
183
- """
184
- Input:
185
- input_gallery_items: Since type="pil", this is a List[Tuple[PIL.Image, str]] or List[PIL.Image]
186
- """
187
- if not input_gallery_items:
188
- raise gr.Error("Please upload an image to edit.")
189
-
190
- # Extract the image from the Gallery input
191
- # When type='pil', Gradio Gallery returns a list of tuples (image, caption) or just images
192
- first_item = input_gallery_items[0]
193
 
194
- if isinstance(first_item, tuple):
195
- # Format is (PIL.Image, Caption)
196
- input_pil = first_item[0]
197
- else:
198
- # Format is PIL.Image directly
199
- input_pil = first_item
200
-
201
- # Map Dropdown choices to internal Adapter names
 
 
 
202
  adapters_map = {
203
- "Texture Edit": "texture-edit",
204
- "Fuse-Objects": "fuse-objects",
205
- "Face-Swap": "face-swap",
206
  }
207
 
208
  active_adapter = adapters_map.get(lora_adapter)
209
 
210
- # Reset adapters first, then activate selected
211
  if active_adapter:
212
  pipe.set_adapters([active_adapter], adapter_weights=[1.0])
213
  else:
@@ -219,11 +211,15 @@ def infer(
219
  generator = torch.Generator(device=device).manual_seed(seed)
220
  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"
221
 
222
- original_image = input_pil.convert("RGB")
223
- width, height = update_dimensions_on_upload(original_image)
 
 
 
 
224
 
225
  result = pipe(
226
- image=original_image,
227
  prompt=prompt,
228
  negative_prompt=negative_prompt,
229
  height=height,
@@ -235,32 +231,27 @@ def infer(
235
 
236
  return result, seed
237
 
238
- @spaces.GPU(duration=30)
239
- def infer_example(input_gallery_items, prompt, lora_adapter):
240
- # input_gallery_items will be the list structure from gr.Examples
241
- if not input_gallery_items:
242
  return None, 0
243
-
244
- # When passed from gr.Examples with type="pil" and a Gallery component,
245
- # we might need to handle file paths if cache_examples=False or PIL if processed.
246
- # However, since we use infer_example as the fn, we mimic the infer logic.
247
-
248
- # For examples with type="pil", gradio usually converts paths to PIL.
249
- return infer(
250
- input_gallery_items,
251
  prompt,
252
  lora_adapter,
253
- seed=0,
254
- randomize_seed=True,
255
- guidance_scale=1.0,
256
- steps=4
257
  )
258
-
259
 
260
  css="""
261
  #col-container {
262
  margin: 0 auto;
263
- max-width: 960px;
264
  }
265
  #main-title h1 {font-size: 2.1em !important;}
266
  """
@@ -268,72 +259,57 @@ css="""
268
  with gr.Blocks(css=css, theme=steel_blue_theme) as demo:
269
  with gr.Column(elem_id="col-container"):
270
  gr.Markdown("# **Qwen-Image-Edit-2509-LoRAs-Fast-Fusion**", elem_id="main-title")
271
- gr.Markdown("Perform advanced image manipulation including Texture editing, Object Fusion, and Face Swapping using specialized [LoRA](https://huggingface.co/models?other=base_model:adapter:Qwen/Qwen-Image-Edit-2509) adapters.")
272
 
273
  with gr.Row(equal_height=True):
274
- with gr.Column():
275
- # Changed to Gallery to support potential multi-image flows (conceptually) and match user request
276
- input_image = gr.Gallery(
277
- label="Input Images",
278
- show_label=False,
279
- type="pil",
280
- interactive=True,
281
- height=290,
282
- columns=1
283
- )
284
 
 
 
 
 
 
 
 
285
  prompt = gr.Text(
286
  label="Edit Prompt",
287
  show_label=True,
288
- placeholder="e.g., Change the material to wooden texture...",
289
  )
290
-
291
- run_button = gr.Button("Edit Image", variant="primary")
292
-
293
- with gr.Column():
294
- output_image = gr.Image(label="Output Image", interactive=False, format="png", height=350)
295
 
296
- with gr.Row():
297
- lora_adapter = gr.Dropdown(
298
- label="Choose Editing Style",
299
- choices=["Texture Edit", "Fuse-Objects", "Face-Swap"],
300
- value="Texture Edit"
301
- )
302
- with gr.Accordion("Advanced Settings", open=False, visible=False):
303
  seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0)
304
  randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
305
- guidance_scale = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
306
  steps = gr.Slider(label="Inference Steps", minimum=1, maximum=50, step=1, value=4)
 
 
 
 
307
 
 
308
  gr.Examples(
309
  examples=[
310
- # Format: [ [Image_List], Prompt, Adapter ]
311
- [
312
- ["examples/texture_sample.jpg", "examples/texture_sample.2jpg"],
313
- "Change the material of the object to rusted metal texture.",
314
- "Texture Edit"
315
- ],
316
- [
317
- ["examples/fusion_sample.jpg"],
318
- "Fuse the product naturally into the background.",
319
- "Fuse-Objects"
320
- ],
321
- [
322
- ["examples/face_sample.jpg"],
323
- "Swap the face with a cyberpunk robot face.",
324
- "Face-Swap"
325
- ],
326
  ],
327
- inputs=[input_image, prompt, lora_adapter],
328
  outputs=[output_image, seed],
329
  fn=infer_example,
330
  cache_examples=False,
331
- label="Examples (Ensure images exist in 'examples/' folder)"
332
  )
333
 
334
  run_button.click(
335
  fn=infer,
336
- inputs=[input_image, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps],
337
  outputs=[output_image, seed]
338
  )
339
 
 
14
  except ImportError:
15
  class spaces:
16
  @staticmethod
17
+ def GPU(duration=60):
18
  def decorator(func):
19
  return func
20
  return decorator
21
 
22
+ # --- Custom Theme Setup ---
23
  colors.steel_blue = colors.Color(
24
  name="steel_blue",
25
  c50="#EBF3F8",
 
90
 
91
  # --- Hardware Setup ---
92
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
93
+ dtype = torch.bfloat16
 
 
 
 
 
 
 
 
94
 
95
  # --- Imports for Custom Pipeline ---
96
  from diffusers import FlowMatchEulerDiscreteScheduler
 
98
  from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
99
  from qwenimage.qwen_fa3_processor import QwenDoubleStreamAttnProcessorFA3
100
 
101
+ # --- Model Initialization ---
102
+ print("Loading Qwen Image Edit Pipeline...")
 
103
  pipe = QwenImageEditPlusPipeline.from_pretrained(
104
  "Qwen/Qwen-Image-Edit-2509",
105
  transformer=QwenImageTransformer2DModel.from_pretrained(
 
111
  torch_dtype=dtype
112
  ).to(device)
113
 
114
+ # 1. Load and Fuse Lightning (for speed)
115
+ print("Loading and Fusing Lightning LoRA...")
116
+ pipe.load_lora_weights("lightx2v/Qwen-Image-Lightning",
117
+ weight_name="Qwen-Image-Lightning-4steps-V2.0-bf16.safetensors",
118
+ adapter_name="lightning")
119
+ pipe.fuse_lora(adapter_names=["lightning"], lora_scale=1.0)
120
 
121
+ # 2. Load Task Specific LoRAs
122
+ print("Loading Task Adapters...")
 
 
123
 
124
+ # Texture
125
+ pipe.load_lora_weights("tarn59/apply_texture_qwen_image_edit_2509",
126
+ weight_name="apply_texture_v2_qwen_image_edit_2509.safetensors",
127
+ adapter_name="texture")
128
 
129
+ # Fusion (Fuse-Objects)
130
+ pipe.load_lora_weights("dx8152/Qwen-Image-Edit-2509-Fusion",
131
+ weight_name="溶图.safetensors",
132
+ adapter_name="fusion")
133
 
134
+ # Face Swap
135
+ pipe.load_lora_weights("Alissonerdx/BFS-Best-Face-Swap",
136
+ weight_name="bfs_head_v3_qwen_image_edit_2509.safetensors",
137
+ adapter_name="faceswap")
138
 
139
+ # Attempt Flash Attention 3
140
  try:
141
  pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
142
  print("Flash Attention 3 Processor set successfully.")
143
  except Exception as e:
144
+ print(f"Could not set FA3 processor (likely hardware mismatch): {e}. using default attention.")
145
 
146
  MAX_SEED = np.iinfo(np.int32).max
147
 
 
160
  aspect_ratio = original_width / original_height
161
  new_width = int(new_height * aspect_ratio)
162
 
163
+ # Ensure dimensions are multiples of 16
164
  new_width = (new_width // 16) * 16
165
  new_height = (new_height // 16) * 16
166
 
167
  return new_width, new_height
168
 
169
+ @spaces.GPU(duration=60)
170
  def infer(
171
+ image_1,
172
+ image_2,
173
  prompt,
174
  lora_adapter,
175
  seed,
 
178
  steps,
179
  progress=gr.Progress(track_tqdm=True)
180
  ):
181
+ if image_1 is None or image_2 is None:
182
+ raise gr.Error("Please upload both images for Fusion/Texture/FaceSwap tasks.")
 
 
 
 
 
 
 
 
183
 
184
+ if not prompt:
185
+ # Add default prompts based on mode if user leaves empty (optional helper)
186
+ if lora_adapter == "Face-Swap":
187
+ prompt = "Swap the face."
188
+ elif lora_adapter == "Texture Edit":
189
+ prompt = "Apply texture to object."
190
+ elif lora_adapter == "Fuse-Objects":
191
+ prompt = "Fuse object into background."
192
+
193
+ # Switch Adapters
194
+ # Note: Lightning is already fused, so we just enable the style adapter
195
  adapters_map = {
196
+ "Texture Edit": "texture",
197
+ "Fuse-Objects": "fusion",
198
+ "Face-Swap": "faceswap",
199
  }
200
 
201
  active_adapter = adapters_map.get(lora_adapter)
202
 
 
203
  if active_adapter:
204
  pipe.set_adapters([active_adapter], adapter_weights=[1.0])
205
  else:
 
211
  generator = torch.Generator(device=device).manual_seed(seed)
212
  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"
213
 
214
+ # Prepare Images
215
+ img1_pil = image_1.convert("RGB")
216
+ img2_pil = image_2.convert("RGB")
217
+
218
+ # Calculate dimensions based on the primary image (Image 1)
219
+ width, height = update_dimensions_on_upload(img1_pil)
220
 
221
  result = pipe(
222
+ image=[img1_pil, img2_pil], # Pass both images
223
  prompt=prompt,
224
  negative_prompt=negative_prompt,
225
  height=height,
 
231
 
232
  return result, seed
233
 
234
+ @spaces.GPU(duration=60)
235
+ def infer_example(image_1, image_2, prompt, lora_adapter):
236
+ # Wrapper for examples that sets defaults
237
+ if image_1 is None or image_2 is None:
238
  return None, 0
239
+ result, seed = infer(
240
+ image_1.convert("RGB"),
241
+ image_2.convert("RGB"),
 
 
 
 
 
242
  prompt,
243
  lora_adapter,
244
+ 0, # seed
245
+ True, # randomize
246
+ 1.0, # guidance
247
+ 4 # steps (Lightning optimized)
248
  )
249
+ return result, seed
250
 
251
  css="""
252
  #col-container {
253
  margin: 0 auto;
254
+ max-width: 1100px;
255
  }
256
  #main-title h1 {font-size: 2.1em !important;}
257
  """
 
259
  with gr.Blocks(css=css, theme=steel_blue_theme) as demo:
260
  with gr.Column(elem_id="col-container"):
261
  gr.Markdown("# **Qwen-Image-Edit-2509-LoRAs-Fast-Fusion**", elem_id="main-title")
262
+ gr.Markdown("Advanced dual-image editing: **Texture Application**, **Object Fusion**, and **Face Swapping** using Qwen-Image-Edit-2509 + Lightning ⚡ (4 Steps).")
263
 
264
  with gr.Row(equal_height=True):
265
+ # Left Column: Inputs
266
+ with gr.Column(scale=1):
267
+ with gr.Row():
268
+ image_1 = gr.Image(label="Base / Background / Body", type="pil", height=250)
269
+ image_2 = gr.Image(label="Reference / Texture / Face", type="pil", height=250)
 
 
 
 
 
270
 
271
+ lora_adapter = gr.Dropdown(
272
+ label="Choose Editing Style",
273
+ choices=["Texture Edit", "Fuse-Objects", "Face-Swap"],
274
+ value="Texture Edit",
275
+ info="Select the operation to perform."
276
+ )
277
+
278
  prompt = gr.Text(
279
  label="Edit Prompt",
280
  show_label=True,
281
+ placeholder="e.g., Apply wood texture to the mug...",
282
  )
 
 
 
 
 
283
 
284
+ run_button = gr.Button("Generate Fusion", variant="primary")
285
+
286
+ with gr.Accordion("Advanced Settings", open=False):
 
 
 
 
287
  seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0)
288
  randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
289
+ guidance_scale = gr.Slider(label="True Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
290
  steps = gr.Slider(label="Inference Steps", minimum=1, maximum=50, step=1, value=4)
291
+
292
+ # Right Column: Output
293
+ with gr.Column(scale=1):
294
+ output_image = gr.Image(label="Output Image", interactive=False, format="png", height=550)
295
 
296
+ # Examples
297
  gr.Examples(
298
  examples=[
299
+ ["examples/mug.jpg", "examples/wood.jpg", "Apply wood texture to the mug.", "Texture Edit"],
300
+ ["examples/room.jpg", "examples/chair.jpg", "Put the chair in the room naturally.", "Fuse-Objects"],
301
+ ["examples/body.jpg", "examples/face.jpg", "Swap the face.", "Face-Swap"],
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  ],
303
+ inputs=[image_1, image_2, prompt, lora_adapter],
304
  outputs=[output_image, seed],
305
  fn=infer_example,
306
  cache_examples=False,
307
+ label="Examples (Ensure files exist in 'examples/' folder)"
308
  )
309
 
310
  run_button.click(
311
  fn=infer,
312
+ inputs=[image_1, image_2, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps],
313
  outputs=[output_image, seed]
314
  )
315