jobanpreet123 commited on
Commit
00e0a9e
Β·
1 Parent(s): 5cdf9d4
Files changed (3) hide show
  1. README.md +6 -5
  2. app.py +446 -0
  3. requirements.txt +13 -0
README.md CHANGED
@@ -1,12 +1,13 @@
1
  ---
2
- title: Faceswap
3
- emoji: 😻
4
- colorFrom: purple
5
- colorTo: pink
6
  sdk: gradio
7
- sdk_version: 6.2.0
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: BFS Best Face Swap
3
+ emoji: πŸ’»
4
+ colorFrom: blue
5
+ colorTo: blue
6
  sdk: gradio
7
+ sdk_version: 6.1.0
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,446 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import spaces
3
+ import torch
4
+ from diffusers import DiffusionPipeline
5
+ from diffusers.utils import load_image
6
+ from PIL import Image
7
+ import numpy as np
8
+ from typing import Optional, Tuple, List
9
+
10
+ # Initialize CUDA
11
+ zero = torch.Tensor([0]).cuda()
12
+ print(f"Initial device: {zero.device}")
13
+
14
+ # Model configurations
15
+ BASE_MODEL = "Qwen/Qwen-Image-Edit-2509" # Qwen Image Edit model
16
+ BFS_LORA = "Alissonerdx/BFS-Best-Face-Swap"
17
+ BFS_LORA_FILENAME = "bfs_head_v3_qwen_image_edit_2509.safetensors" # Qwen-specific version
18
+ ANGLES_LORA = "dx8152/Qwen-Edit-2509-Multiple-angles"
19
+ SKIN_LORA = "tlennon-ie/qwen-edit-skin"
20
+
21
+ # Fixed prompt for head swap
22
+ FIXED_PROMPT = """head_swap: start with Picture 1 as the base image, keeping its lighting, environment, and background. remove the head from Picture 1 completely and replace it with the head from Picture 2. ensure the head and body have correct anatomical proportions, and blend the skin tones, shadows, and lighting naturally so the final result appears as one coherent, realistic person."""
23
+
24
+ # Cache for loaded pipe
25
+ pipe_cache = None
26
+
27
+ @spaces.GPU(duration=60)
28
+ def face_swap(
29
+ body_image,
30
+ face_image,
31
+ custom_prompt_addon,
32
+ bfs_lora_scale,
33
+ angles_lora_scale,
34
+ skin_lora_scale,
35
+ enable_angles_lora,
36
+ enable_skin_lora,
37
+ num_inference_steps,
38
+ guidance_scale,
39
+ seed
40
+ ):
41
+ """
42
+ Perform head swap using Qwen-Image-Edit with multiple LoRAs
43
+ """
44
+ print(f"GPU device: {zero.device}")
45
+
46
+ # Validate inputs
47
+ if body_image is None or face_image is None:
48
+ raise gr.Error("Please provide both body (Picture 1) and face (Picture 2) images")
49
+
50
+ # Set seed for reproducibility
51
+ if seed != -1:
52
+ torch.manual_seed(seed)
53
+ generator = torch.Generator(device="cuda").manual_seed(seed)
54
+ else:
55
+ generator = None
56
+
57
+ try:
58
+ global pipe_cache
59
+
60
+ # Load the pipeline (only once)
61
+ if pipe_cache is None:
62
+ print(f"Loading pipeline: {BASE_MODEL}")
63
+ pipe_cache = DiffusionPipeline.from_pretrained(
64
+ BASE_MODEL,
65
+ torch_dtype=torch.bfloat16, # Qwen uses bfloat16
66
+ device_map="cuda"
67
+ )
68
+
69
+ pipe = pipe_cache
70
+
71
+ # Prepare the LoRA adapters list
72
+ adapters = []
73
+ adapter_weights = []
74
+
75
+ # Always load BFS Face Swap LoRA (Qwen-specific version)
76
+ if bfs_lora_scale > 0:
77
+ print(f"Loading BFS Face Swap LoRA (Qwen version) with scale {bfs_lora_scale}")
78
+ try:
79
+ pipe.load_lora_weights(
80
+ BFS_LORA,
81
+ weight_name=BFS_LORA_FILENAME, # Using the Qwen-specific file
82
+ adapter_name="bfs_face_swap"
83
+ )
84
+ adapters.append("bfs_face_swap")
85
+ adapter_weights.append(bfs_lora_scale)
86
+ except Exception as e:
87
+ print(f"Warning: Could not load BFS LoRA: {e}")
88
+ gr.Warning(f"BFS Face Swap LoRA could not be loaded: {e}")
89
+
90
+ # Load Multiple Angles LoRA if enabled
91
+ if enable_angles_lora and angles_lora_scale > 0:
92
+ print(f"Loading Multiple Angles LoRA with scale {angles_lora_scale}")
93
+ try:
94
+ pipe.load_lora_weights(
95
+ ANGLES_LORA,
96
+ adapter_name="angles"
97
+ )
98
+ adapters.append("angles")
99
+ adapter_weights.append(angles_lora_scale)
100
+ except Exception as e:
101
+ print(f"Warning: Could not load Angles LoRA: {e}")
102
+ gr.Warning(f"Multiple Angles LoRA could not be loaded: {e}")
103
+
104
+ # Load Skin LoRA if enabled
105
+ if enable_skin_lora and skin_lora_scale > 0:
106
+ print(f"Loading Skin LoRA with scale {skin_lora_scale}")
107
+ try:
108
+ pipe.load_lora_weights(
109
+ SKIN_LORA,
110
+ adapter_name="skin"
111
+ )
112
+ adapters.append("skin")
113
+ adapter_weights.append(skin_lora_scale)
114
+ except Exception as e:
115
+ print(f"Warning: Could not load Skin LoRA: {e}")
116
+ gr.Warning(f"Skin LoRA could not be loaded: {e}")
117
+
118
+ # Set the active adapters
119
+ if len(adapters) > 0:
120
+ if len(adapters) == 1:
121
+ pipe.set_adapters(adapters[0], adapter_weights=adapter_weights[0])
122
+ else:
123
+ pipe.set_adapters(adapters, adapter_weights=adapter_weights)
124
+ print(f"Active LoRAs: {adapters} with weights {adapter_weights}")
125
+
126
+ # Prepare images
127
+ body_img = Image.fromarray(body_image).convert("RGB")
128
+ face_img = Image.fromarray(face_image).convert("RGB")
129
+
130
+ # Combine fixed prompt with any additional instructions
131
+ final_prompt = FIXED_PROMPT
132
+ if custom_prompt_addon and custom_prompt_addon.strip():
133
+ final_prompt = f"{FIXED_PROMPT} {custom_prompt_addon}"
134
+
135
+ print(f"Using prompt: {final_prompt[:100]}...")
136
+ print(f"Using BFS LoRA file: {BFS_LORA_FILENAME}")
137
+
138
+ # Generate the head swap
139
+ result = pipe(
140
+ image=body_img, # Picture 1 - Body/Base
141
+ prompt=final_prompt,
142
+ input_image=face_img, # Picture 2 - Face to swap
143
+ num_inference_steps=num_inference_steps,
144
+ guidance_scale=guidance_scale,
145
+ generator=generator
146
+ ).images[0]
147
+
148
+ # Create status message
149
+ active_loras = []
150
+ if bfs_lora_scale > 0:
151
+ active_loras.append(f"BFS-Qwen-v3({bfs_lora_scale:.2f})")
152
+ if enable_angles_lora and angles_lora_scale > 0:
153
+ active_loras.append(f"Angles({angles_lora_scale:.2f})")
154
+ if enable_skin_lora and skin_lora_scale > 0:
155
+ active_loras.append(f"Skin({skin_lora_scale:.2f})")
156
+
157
+ status = f"βœ… Head swap completed | Active LoRAs: {', '.join(active_loras) if active_loras else 'None'}"
158
+
159
+ return result, status
160
+
161
+ except Exception as e:
162
+ print(f"Error: {str(e)}")
163
+ error_img = Image.new('RGB', (512, 512), color=(200, 50, 50))
164
+ return error_img, f"❌ Error: {str(e)}"
165
+
166
+ # Create the Gradio interface
167
+ with gr.Blocks(title="BFS-Best Face Swap with Qwen", theme=gr.themes.Soft(), css="""
168
+ .container {max-width: 1200px; margin: auto;}
169
+ .image-container {border-radius: 10px; border: 2px dashed #ccc;}
170
+ .fixed-prompt {background-color: #f0f0f0; padding: 10px; border-radius: 5px; font-family: monospace;}
171
+ .lora-info {background-color: #e8f4ff; padding: 8px; border-radius: 5px; margin: 5px 0; font-size: 0.9em;}
172
+ """) as demo:
173
+ gr.Markdown(
174
+ """
175
+ # 🎭 BFS-Best Face Swap with Qwen-Image-Edit-2509
176
+
177
+ ## Professional Head Swap using Multiple LoRAs
178
+
179
+ This interface uses:
180
+ - **Base Model**: Qwen-Image-Edit-2509
181
+ - **Primary LoRA**: BFS-Best Face Swap v3 (Qwen-optimized: `bfs_head_v3_qwen_image_edit_2509.safetensors`)
182
+ - **Enhancement LoRAs**: Multiple Angles & Skin Blending
183
+ """
184
+ )
185
+
186
+ with gr.Row():
187
+ with gr.Column(scale=1):
188
+ gr.Markdown("### πŸ“₯ Input Images")
189
+
190
+ with gr.Row():
191
+ body_image = gr.Image(
192
+ label="πŸ‘€ Picture 1: Body/Base Image",
193
+ type="numpy",
194
+ height=300,
195
+ elem_classes="image-container"
196
+ )
197
+ face_image = gr.Image(
198
+ label="😊 Picture 2: Head/Face to Swap",
199
+ type="numpy",
200
+ height=300,
201
+ elem_classes="image-container"
202
+ )
203
+
204
+ gr.Markdown("### 🎯 Fixed Head Swap Prompt")
205
+ gr.Markdown(
206
+ f'<div class="fixed-prompt">{FIXED_PROMPT}</div>',
207
+ elem_classes="fixed-prompt"
208
+ )
209
+
210
+ custom_prompt_addon = gr.Textbox(
211
+ label="Additional Instructions (Optional)",
212
+ placeholder="Add any extra details or style instructions...",
213
+ value="",
214
+ lines=2
215
+ )
216
+
217
+ with gr.Accordion("πŸŽ›οΈ LoRA Controls", open=True):
218
+ gr.Markdown("#### BFS Face Swap LoRA (Primary)")
219
+ gr.Markdown(
220
+ '<div class="lora-info">πŸ“Œ Using: bfs_head_v3_qwen_image_edit_2509.safetensors</div>',
221
+ elem_classes="lora-info"
222
+ )
223
+ bfs_lora_scale = gr.Slider(
224
+ minimum=0.0,
225
+ maximum=1.5,
226
+ step=0.05,
227
+ value=1.0,
228
+ label="BFS Face Swap Strength (Qwen v3)",
229
+ info="Main face swapping LoRA optimized for Qwen - set to 0 to disable"
230
+ )
231
+
232
+ gr.Markdown("#### Enhancement LoRAs")
233
+
234
+ with gr.Row():
235
+ enable_angles_lora = gr.Checkbox(
236
+ label="Enable Multiple Angles LoRA",
237
+ value=True,
238
+ info="Improves head angle matching"
239
+ )
240
+ angles_lora_scale = gr.Slider(
241
+ minimum=0.0,
242
+ maximum=1.5,
243
+ step=0.05,
244
+ value=0.7,
245
+ label="Multiple Angles Strength",
246
+ interactive=True
247
+ )
248
+
249
+ with gr.Row():
250
+ enable_skin_lora = gr.Checkbox(
251
+ label="Enable Skin Blending LoRA",
252
+ value=True,
253
+ info="Improves skin tone matching"
254
+ )
255
+ skin_lora_scale = gr.Slider(
256
+ minimum=0.0,
257
+ maximum=1.5,
258
+ step=0.05,
259
+ value=0.6,
260
+ label="Skin Blending Strength",
261
+ interactive=True
262
+ )
263
+
264
+ with gr.Accordion("βš™οΈ Generation Settings", open=False):
265
+ num_inference_steps = gr.Slider(
266
+ minimum=10,
267
+ maximum=100,
268
+ step=5,
269
+ value=30,
270
+ label="Inference Steps",
271
+ info="Higher = better quality but slower"
272
+ )
273
+
274
+ guidance_scale = gr.Slider(
275
+ minimum=1.0,
276
+ maximum=20.0,
277
+ step=0.5,
278
+ value=7.5,
279
+ label="Guidance Scale",
280
+ info="How closely to follow the prompt"
281
+ )
282
+
283
+ seed = gr.Number(
284
+ value=-1,
285
+ label="Seed",
286
+ info="Use -1 for random, or specific number for reproducible results",
287
+ precision=0
288
+ )
289
+
290
+ generate_btn = gr.Button("🎨 Generate Head Swap", variant="primary", size="lg")
291
+
292
+ with gr.Column(scale=1):
293
+ gr.Markdown("### πŸ“€ Output")
294
+ output_image = gr.Image(
295
+ label="Result",
296
+ type="pil",
297
+ interactive=False,
298
+ height=500
299
+ )
300
+ status_text = gr.Textbox(
301
+ label="Status",
302
+ interactive=False,
303
+ max_lines=2,
304
+ value="Ready to process..."
305
+ )
306
+
307
+ gr.Markdown(
308
+ """
309
+ ### πŸ’‘ Quick Tips:
310
+ - **Picture 1**: Body/environment to keep
311
+ - **Picture 2**: Face/head to transplant
312
+ - **BFS Strength**: 0.8-1.2 for best results
313
+ - **Angles LoRA**: Helps with different head angles
314
+ - **Skin LoRA**: Smooths skin tone transitions
315
+ """
316
+ )
317
+
318
+ # Interaction logic for enabling/disabling LoRA controls
319
+ def toggle_angles(enabled):
320
+ return gr.update(interactive=enabled)
321
+
322
+ def toggle_skin(enabled):
323
+ return gr.update(interactive=enabled)
324
+
325
+ enable_angles_lora.change(
326
+ fn=toggle_angles,
327
+ inputs=enable_angles_lora,
328
+ outputs=angles_lora_scale
329
+ )
330
+
331
+ enable_skin_lora.change(
332
+ fn=toggle_skin,
333
+ inputs=enable_skin_lora,
334
+ outputs=skin_lora_scale
335
+ )
336
+
337
+ # Examples
338
+ gr.Examples(
339
+ examples=[
340
+ [
341
+ None, # body_image
342
+ None, # face_image
343
+ "", # custom_prompt_addon
344
+ 1.0, # bfs_lora_scale
345
+ 0.7, # angles_lora_scale
346
+ 0.6, # skin_lora_scale
347
+ True, # enable_angles_lora
348
+ True, # enable_skin_lora
349
+ 30, # num_inference_steps
350
+ 7.5, # guidance_scale
351
+ 42 # seed
352
+ ],
353
+ [
354
+ None,
355
+ None,
356
+ "professional lighting, high resolution",
357
+ 1.2,
358
+ 0.8,
359
+ 0.5,
360
+ True,
361
+ True,
362
+ 40,
363
+ 8.0,
364
+ 123
365
+ ],
366
+ [
367
+ None,
368
+ None,
369
+ "",
370
+ 0.9,
371
+ 0.0,
372
+ 0.0,
373
+ False,
374
+ False,
375
+ 25,
376
+ 7.0,
377
+ -1
378
+ ]
379
+ ],
380
+ inputs=[
381
+ body_image,
382
+ face_image,
383
+ custom_prompt_addon,
384
+ bfs_lora_scale,
385
+ angles_lora_scale,
386
+ skin_lora_scale,
387
+ enable_angles_lora,
388
+ enable_skin_lora,
389
+ num_inference_steps,
390
+ guidance_scale,
391
+ seed
392
+ ],
393
+ outputs=[output_image, status_text],
394
+ fn=face_swap,
395
+ cache_examples=False
396
+ )
397
+
398
+ # Event handlers
399
+ generate_btn.click(
400
+ fn=face_swap,
401
+ inputs=[
402
+ body_image,
403
+ face_image,
404
+ custom_prompt_addon,
405
+ bfs_lora_scale,
406
+ angles_lora_scale,
407
+ skin_lora_scale,
408
+ enable_angles_lora,
409
+ enable_skin_lora,
410
+ num_inference_steps,
411
+ guidance_scale,
412
+ seed
413
+ ],
414
+ outputs=[output_image, status_text]
415
+ )
416
+
417
+ gr.Markdown(
418
+ """
419
+ ---
420
+ ### πŸ“š Documentation
421
+
422
+ **Model Chain:**
423
+ 1. **Qwen-Image-Edit-2509**: Advanced image editing base model
424
+ 2. **BFS-Best Face Swap v3**: Primary face swapping LoRA
425
+ 3. **Multiple Angles**: Improves head angle matching
426
+ 4. **Skin Blending**: Natural skin tone transitions
427
+
428
+ **LoRA Settings Guide:**
429
+ - **All at 0**: Uses only base Qwen model
430
+ - **BFS only (1.0)**: Basic face swap
431
+ - **BFS + Angles**: Better angle matching
432
+ - **BFS + Skin**: Better skin blending
433
+ - **All enabled**: Maximum quality (slower)
434
+
435
+ ### πŸ”— Resources:
436
+ - [Qwen-Image-Edit-2509](https://huggingface.co/Qwen/Qwen-Image-Edit-2509)
437
+ - [BFS-Best Face Swap](https://huggingface.co/Alissonerdx/BFS-Best-Face-Swap)
438
+ - [Multiple Angles LoRA](https://huggingface.co/dx8152/Qwen-Edit-2509-Multiple-angles)
439
+ - [Skin Blending LoRA](https://huggingface.co/tlennon-ie/qwen-edit-skin)
440
+ """
441
+ )
442
+
443
+ # Launch the app
444
+ if __name__ == "__main__":
445
+ demo.queue(max_size=10)
446
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio==4.44.0
2
+ spaces==0.30.0
3
+ torch==2.1.0
4
+ torchvision==0.16.0
5
+ diffusers==0.31.0
6
+ transformers==4.44.0
7
+ accelerate==0.33.0
8
+ safetensors==0.4.5
9
+ Pillow==10.4.0
10
+ numpy==1.26.4
11
+ huggingface-hub==0.24.0
12
+ sentencepiece==0.2.0
13
+ protobuf==5.27.0