Deepro Bardhan commited on
Commit
72e0b27
·
1 Parent(s): c2faec4

added checkbox of audio mixing and share commandline

Browse files
Files changed (1) hide show
  1. app.py +260 -206
app.py CHANGED
@@ -6,6 +6,7 @@ import shutil
6
  import subprocess
7
  import time
8
  from SinglePhoto import FaceSwapper
 
9
 
10
  wellcomingMessage = """
11
  <h1>Face Swapping Suite</h1>
@@ -14,6 +15,7 @@ wellcomingMessage = """
14
 
15
  swapper = FaceSwapper()
16
 
 
17
  def swap_single_photo(src_img, src_idx, dst_img, dst_idx, progress=gr.Progress(track_tqdm=True)):
18
  log = ""
19
  start_time = time.time()
@@ -54,123 +56,43 @@ def swap_single_photo(src_img, src_idx, dst_img, dst_idx, progress=gr.Progress(t
54
  log += f"Elapsed time: {elapsed:.2f} seconds\n"
55
  return None, log
56
 
57
- def swap_video(src_img, src_idx, video, dst_idx, delete_frames_dir=True, progress=gr.Progress()):
58
  log = ""
59
- start_time = time.time()
60
- src_path = "VideoSwapping/data_src.jpg"
61
- dst_video_path = "VideoSwapping/data_dst.mp4"
62
- frames_dir = "VideoSwapping/video_frames"
63
- swapped_dir = "VideoSwapping/swapped_frames"
64
- output_video_path = "VideoSwapping/output_tmp_output_video.mp4"
65
- final_output_path = "VideoSwapping/output_with_audio.mp4"
66
-
67
- os.makedirs(os.path.dirname(src_path), exist_ok=True)
68
- os.makedirs(os.path.dirname(dst_video_path), exist_ok=True)
69
- os.makedirs(frames_dir, exist_ok=True)
70
- os.makedirs(swapped_dir, exist_ok=True)
71
-
72
- src_img_bgr = cv2.cvtColor(src_img, cv2.COLOR_RGB2BGR)
73
- cv2.imwrite(src_path, src_img_bgr)
74
- log += f"Saved source image to {src_path}\n"
75
- progress(0.05, desc="Saved source image")
76
-
77
- if isinstance(video, str) and os.path.exists(video):
78
- shutil.copy(video, dst_video_path)
79
- log += f"Copied video to {dst_video_path}\n"
80
- else:
81
- dst_video_path = video
82
-
83
- from VideoSwapping import extract_frames, frames_to_video
84
 
85
- frame_paths = extract_frames(dst_video_path, frames_dir)
86
- log += f"Extracted {len(frame_paths)} frames to {frames_dir}\n"
87
- progress(0.15, desc="Extracted frames")
 
 
 
 
88
 
89
- swapped_files = set(os.listdir(swapped_dir))
90
- total_frames = len(frame_paths)
91
- start_loop_time = time.time()
92
- for idx, frame_path in enumerate(frame_paths):
93
- swapped_name = f"swapped_{idx:05d}.jpg"
94
- out_path = os.path.join(swapped_dir, swapped_name)
95
- if swapped_name in swapped_files and os.path.exists(out_path):
96
- log += f"Frame {idx}: already swapped, skipping.\n"
97
- elapsed = time.time() - start_loop_time
98
- avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
99
- remaining = avg_time * (total_frames - (idx + 1))
100
- mins, secs = divmod(int(remaining), 60)
101
- progress(0.15 + 0.6 * (idx + 1) / total_frames, desc=f"Swapping {idx+1}/{total_frames} | {mins:02d}:{secs:02d} left")
102
- continue
103
  try:
104
- try:
105
- swapped = swapper.swap_faces(src_path, int(src_idx), frame_path, int(dst_idx))
106
- except ValueError as ve:
107
- if int(dst_idx) != 1 and "Target image contains" in str(ve):
108
- swapped = swapper.swap_faces(src_path, int(src_idx), frame_path, 1)
109
- log += f"Frame {idx}: dst_idx {dst_idx} not found, used 1 instead.\n"
110
- else:
111
- raise ve
112
- cv2.imwrite(out_path, swapped)
113
- log += f"Swapped frame {idx} and saved to {out_path}\n"
114
  except Exception as e:
115
- cv2.imwrite(out_path, cv2.imread(frame_path))
116
- log += f"Failed to swap frame {idx}: {e}\n"
117
- elapsed = time.time() - start_loop_time
118
- avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
119
- remaining = avg_time * (total_frames - (idx + 1))
120
- mins, secs = divmod(int(remaining), 60)
121
- progress(0.15 + 0.6 * (idx + 1) / total_frames, desc=f"Swapping {idx+1}/{total_frames} | {mins:02d}:{secs:02d} left")
122
- cap = cv2.VideoCapture(dst_video_path)
123
- fps = cap.get(cv2.CAP_PROP_FPS)
124
- cap.release()
125
- frames_to_video(swapped_dir, output_video_path, fps)
126
- log += f"Combined swapped frames into video {output_video_path}\n"
127
- progress(0.8, desc="Muxing audio")
128
-
129
- ok, audio_log = add_audio_to_video(dst_video_path, output_video_path, final_output_path)
130
- if ok:
131
- log += f"Added audio to {final_output_path}\n"
132
- else:
133
- log += f"Audio muxing failed: {audio_log}\n"
134
- final_output_path = output_video_path
135
-
136
- try:
137
- if os.path.exists(src_path):
138
- os.remove(src_path)
139
- if os.path.exists(dst_video_path):
140
- os.remove(dst_video_path)
141
- if delete_frames_dir and os.path.exists(frames_dir):
142
- shutil.rmtree(frames_dir)
143
- log += "Deleted video_frames directory.\n"
144
- elif not delete_frames_dir:
145
- log += "Kept video_frames directory as requested.\n"
146
- if os.path.exists(swapped_dir):
147
- shutil.rmtree(swapped_dir)
148
- log += "Cleaned up temp files and folders.\n"
149
- except Exception as cleanup_error:
150
- log += f"Cleanup error: {cleanup_error}\n"
151
  progress(1, desc="Done")
152
- elapsed = time.time() - start_time
153
- log += f"Elapsed time: {elapsed:.2f} seconds\n"
154
- return final_output_path, log
155
-
156
- def add_audio_to_video(original_video_path, video_no_audio_path, output_path):
157
- cmd = [
158
- "ffmpeg",
159
- "-y",
160
- "-i", video_no_audio_path,
161
- "-i", original_video_path,
162
- "-c:v", "copy",
163
- "-c:a", "aac",
164
- "-map", "0:v:0",
165
- "-map", "1:a:0?",
166
- "-shortest",
167
- output_path
168
- ]
169
- try:
170
- subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
171
- return True, ""
172
- except subprocess.CalledProcessError as e:
173
- return False, e.stderr.decode()
174
 
175
  def swap_multi_src_single_dst(src_imgs, dst_img, dst_idx, progress=gr.Progress(track_tqdm=True)):
176
  log = ""
@@ -324,7 +246,205 @@ def swap_single_src_multi_dst(src_img, dst_imgs, dst_indices, progress=gr.Progre
324
  progress(1, desc="Done")
325
  return results, log
326
 
327
- def swap_video_all_faces(src_img, video, num_faces_to_swap, delete_frames_dir=True, progress=gr.Progress()):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  log = ""
329
  start_time = time.time()
330
  src_path = "VideoSwappingAllFaces/data_src.jpg"
@@ -404,12 +524,16 @@ def swap_video_all_faces(src_img, video, num_faces_to_swap, delete_frames_dir=Tr
404
  log += f"Combined swapped frames into video {output_video_path}\n"
405
  progress(0.8, desc="Muxing audio")
406
 
407
- ok, audio_log = add_audio_to_video(dst_video_path, output_video_path, final_output_path)
408
- if ok:
409
- log += f"Added audio to {final_output_path}\n"
 
 
 
 
410
  else:
411
- log += f"Audio muxing failed: {audio_log}\n"
412
  final_output_path = output_video_path
 
413
 
414
  try:
415
  if os.path.exists(src_path):
@@ -431,88 +555,7 @@ def swap_video_all_faces(src_img, video, num_faces_to_swap, delete_frames_dir=Tr
431
  log += f"Elapsed time: {elapsed:.2f} seconds\n"
432
  return final_output_path, log
433
 
434
- def swap_faces_custom(src_imgs, dst_img, mapping_str, progress=gr.Progress(track_tqdm=True)):
435
- """
436
- src_imgs: list of source images (numpy arrays)
437
- dst_img: destination image (numpy array)
438
- mapping_str: comma-separated string, e.g. "2,1,3"
439
- """
440
- log = ""
441
- start_time = time.time()
442
- dst_path = "CustomSwap/data_dst.jpg"
443
- output_path = "CustomSwap/output_swapped.jpg"
444
- src_dir = "CustomSwap/src"
445
- temp_dir = "CustomSwap/temp"
446
- os.makedirs(src_dir, exist_ok=True)
447
- os.makedirs(temp_dir, exist_ok=True)
448
- os.makedirs(os.path.dirname(dst_path), exist_ok=True)
449
- os.makedirs(os.path.dirname(output_path), exist_ok=True)
450
-
451
- # Save destination image
452
- dst_img_bgr = cv2.cvtColor(dst_img, cv2.COLOR_RGB2BGR)
453
- cv2.imwrite(dst_path, dst_img_bgr)
454
- log += f"Saved destination image to {dst_path}\n"
455
-
456
- # Save all source images
457
- src_paths = []
458
- for i, src_img in enumerate(src_imgs):
459
- src_path = os.path.join(src_dir, f"data_src_{i+1}.jpg")
460
- if isinstance(src_img, tuple):
461
- src_img = src_img[0]
462
- if src_img is None:
463
- log += f"Source image {i+1} is None, skipping.\n"
464
- continue
465
- if isinstance(src_img, np.ndarray):
466
- src_img_bgr = cv2.cvtColor(src_img, cv2.COLOR_RGB2BGR)
467
- cv2.imwrite(src_path, src_img_bgr)
468
- src_paths.append(src_path)
469
- log += f"Saved source image {i+1} to {src_path}\n"
470
- elif isinstance(src_img, str) and os.path.exists(src_img):
471
- shutil.copy(src_img, src_path)
472
- src_paths.append(src_path)
473
- log += f"Copied source image {i+1} from {src_img} to {src_path}\n"
474
- else:
475
- log += f"Source image {i+1} is not a valid image, skipping.\n"
476
-
477
- # Parse mapping
478
- try:
479
- mapping = [int(x.strip()) for x in mapping_str.split(",") if x.strip().isdigit()]
480
- except Exception as e:
481
- log += f"Error parsing mapping: {e}\n"
482
- elapsed = time.time() - start_time
483
- log += f"Elapsed time: {elapsed:.2f} seconds\n"
484
- return None, log
485
-
486
- # Use a temp file for iterative swapping
487
- temp_dst_path = os.path.join(temp_dir, "temp_dst.jpg")
488
- shutil.copy(dst_path, temp_dst_path)
489
-
490
- for face_idx, src_idx in enumerate(mapping, start=1):
491
- if src_idx < 1 or src_idx > len(src_paths):
492
- log += f"Invalid source index {src_idx} for face {face_idx}, skipping.\n"
493
- continue
494
- try:
495
- swapped_img = swapper.swap_faces(src_paths[src_idx-1], 1, temp_dst_path, face_idx)
496
- cv2.imwrite(temp_dst_path, swapped_img)
497
- log += f"Swapped face {face_idx} in destination with source {src_idx}\n"
498
- except Exception as e:
499
- log += f"Failed to swap face {face_idx} with source {src_idx}: {e}\n"
500
-
501
- shutil.copy(temp_dst_path, output_path)
502
- log += f"Saved swapped image to {output_path}\n"
503
- if os.path.exists(temp_dst_path):
504
- os.remove(temp_dst_path)
505
- elapsed = time.time() - start_time
506
- log += f"Elapsed time: {elapsed:.2f} seconds\n"
507
- return output_path, log
508
-
509
- def swap_video_custom_mapping(src_imgs, video, mapping_str, delete_frames_dir=True, progress=gr.Progress()):
510
- """
511
- Swaps faces in each frame of the video according to the user mapping.
512
- src_imgs: list of source images (numpy arrays)
513
- video: path or numpy array of the video
514
- mapping_str: comma-separated string, e.g. "2,1,3"
515
- """
516
  log = ""
517
  start_time = time.time()
518
  src_dir = "CustomVideoSwap/src"
@@ -621,12 +664,16 @@ def swap_video_custom_mapping(src_imgs, video, mapping_str, delete_frames_dir=Tr
621
  log += f"Combined swapped frames into video {output_video_path}\n"
622
  progress(0.9, desc="Muxing audio")
623
 
624
- ok, audio_log = add_audio_to_video(dst_video_path, output_video_path, final_output_path)
625
- if ok:
626
- log += f"Added audio to {final_output_path}\n"
 
 
 
 
627
  else:
628
- log += f"Audio muxing failed: {audio_log}\n"
629
  final_output_path = output_video_path
 
630
 
631
  try:
632
  if os.path.exists(dst_video_path):
@@ -646,6 +693,7 @@ def swap_video_custom_mapping(src_imgs, video, mapping_str, delete_frames_dir=Tr
646
  log += f"Elapsed time: {elapsed:.2f} seconds\n"
647
  return final_output_path, log
648
 
 
649
  # Gradio UI
650
  with gr.Blocks() as demo:
651
  gr.Markdown(wellcomingMessage)
@@ -724,7 +772,8 @@ with gr.Blocks() as demo:
724
  gr.Number(value=1, label="Source Face Index"),
725
  gr.Video(label="Target Video"),
726
  gr.Number(value=1, label="Destination Face Index"),
727
- gr.Checkbox(label="Delete video_frames directory after processing", value=True)
 
728
  ],
729
  outputs=[
730
  gr.Video(label="Swapped Video"),
@@ -738,7 +787,8 @@ with gr.Blocks() as demo:
738
  gr.Image(label="Source Image"),
739
  gr.Video(label="Target Video"),
740
  gr.Number(value=1, label="Number of Faces to Swap"),
741
- gr.Checkbox(label="Delete video_frames directory after processing", value=True)
 
742
  ],
743
  outputs=[
744
  gr.Video(label="Swapped Video"),
@@ -752,7 +802,8 @@ with gr.Blocks() as demo:
752
  gr.Gallery(label="Source Images", type="numpy", columns=3),
753
  gr.Video(label="Target Video"),
754
  gr.Textbox(label="Mapping (comma-separated, e.g. 2,1,3)"),
755
- gr.Checkbox(label="Delete video_frames directory after processing", value=True)
 
756
  ],
757
  outputs=[
758
  gr.Video(label="Swapped Video"),
@@ -762,4 +813,7 @@ with gr.Blocks() as demo:
762
 
763
 
764
  if __name__ == "__main__":
765
- demo.launch(share=True)
 
 
 
 
6
  import subprocess
7
  import time
8
  from SinglePhoto import FaceSwapper
9
+ import argparse
10
 
11
  wellcomingMessage = """
12
  <h1>Face Swapping Suite</h1>
 
15
 
16
  swapper = FaceSwapper()
17
 
18
+ #Photo Swapping Functions
19
  def swap_single_photo(src_img, src_idx, dst_img, dst_idx, progress=gr.Progress(track_tqdm=True)):
20
  log = ""
21
  start_time = time.time()
 
56
  log += f"Elapsed time: {elapsed:.2f} seconds\n"
57
  return None, log
58
 
59
+ def swap_single_src_multi_dst(src_img, dst_imgs, dst_indices, progress=gr.Progress(track_tqdm=True)):
60
  log = ""
61
+ results = []
62
+ src_dir = "SingleSrcMultiDst/src"
63
+ dst_dir = "SingleSrcMultiDst/dst"
64
+ output_dir = "SingleSrcMultiDst/output"
65
+ os.makedirs(src_dir, exist_ok=True)
66
+ os.makedirs(dst_dir, exist_ok=True)
67
+ os.makedirs(output_dir, exist_ok=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
+ if isinstance(dst_img, tuple):
70
+ dst_img = dst_img[0]
71
+ dst_img_bgr = cv2.cvtColor(dst_img, cv2.COLOR_RGB2BGR)
72
+ dst_path = os.path.join(dst_dir, "data_dst.jpg")
73
+ cv2.imwrite(dst_path, dst_img_bgr)
74
+ log += f"Saved destination image to {dst_path}\n"
75
+ progress(0.05, desc="Saved destination image")
76
 
77
+ for i, src_img in enumerate(src_imgs):
78
+ if isinstance(src_img, tuple):
79
+ src_img = src_img[0]
80
+ src_path = os.path.join(src_dir, f"data_src_{i}.jpg")
81
+ output_path = os.path.join(output_dir, f"output_swapped_{i}.jpg")
82
+ src_img_bgr = cv2.cvtColor(src_img, cv2.COLOR_RGB2BGR)
83
+ cv2.imwrite(src_path, src_img_bgr)
84
+ log += f"Saved source image {i} to {src_path}\n"
 
 
 
 
 
 
85
  try:
86
+ result = swapper.swap_faces(src_path, 1, dst_path, int(dst_idx))
87
+ cv2.imwrite(output_path, result)
88
+ results.append(output_path)
89
+ log += f"Swapped and saved result to {output_path}\n"
 
 
 
 
 
 
90
  except Exception as e:
91
+ results.append(f"Error: {e}")
92
+ log += f"Error swapping source {i}: {e}\n"
93
+ progress((i + 1) / len(src_imgs), desc=f"Swapping source {i+1}/{len(src_imgs)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  progress(1, desc="Done")
95
+ return results, log
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  def swap_multi_src_single_dst(src_imgs, dst_img, dst_idx, progress=gr.Progress(track_tqdm=True)):
98
  log = ""
 
246
  progress(1, desc="Done")
247
  return results, log
248
 
249
+ def swap_faces_custom(src_imgs, dst_img, mapping_str, progress=gr.Progress(track_tqdm=True)):
250
+ """
251
+ src_imgs: list of source images (numpy arrays)
252
+ dst_img: destination image (numpy array)
253
+ mapping_str: comma-separated string, e.g. "2,1,3"
254
+ """
255
+ log = ""
256
+ start_time = time.time()
257
+ dst_path = "CustomSwap/data_dst.jpg"
258
+ output_path = "CustomSwap/output_swapped.jpg"
259
+ src_dir = "CustomSwap/src"
260
+ temp_dir = "CustomSwap/temp"
261
+ os.makedirs(src_dir, exist_ok=True)
262
+ os.makedirs(temp_dir, exist_ok=True)
263
+ os.makedirs(os.path.dirname(dst_path), exist_ok=True)
264
+ os.makedirs(os.path.dirname(output_path), exist_ok=True)
265
+
266
+ # Save destination image
267
+ dst_img_bgr = cv2.cvtColor(dst_img, cv2.COLOR_RGB2BGR)
268
+ cv2.imwrite(dst_path, dst_img_bgr)
269
+ log += f"Saved destination image to {dst_path}\n"
270
+
271
+ # Save all source images
272
+ src_paths = []
273
+ for i, src_img in enumerate(src_imgs):
274
+ src_path = os.path.join(src_dir, f"data_src_{i+1}.jpg")
275
+ if isinstance(src_img, tuple):
276
+ src_img = src_img[0]
277
+ if src_img is None:
278
+ log += f"Source image {i+1} is None, skipping.\n"
279
+ continue
280
+ if isinstance(src_img, np.ndarray):
281
+ src_img_bgr = cv2.cvtColor(src_img, cv2.COLOR_RGB2BGR)
282
+ cv2.imwrite(src_path, src_img_bgr)
283
+ src_paths.append(src_path)
284
+ log += f"Saved source image {i+1} to {src_path}\n"
285
+ elif isinstance(src_img, str) and os.path.exists(src_img):
286
+ shutil.copy(src_img, src_path)
287
+ src_paths.append(src_path)
288
+ log += f"Copied source image {i+1} from {src_img} to {src_path}\n"
289
+ else:
290
+ log += f"Source image {i+1} is not a valid image, skipping.\n"
291
+
292
+ # Parse mapping
293
+ try:
294
+ mapping = [int(x.strip()) for x in mapping_str.split(",") if x.strip().isdigit()]
295
+ except Exception as e:
296
+ log += f"Error parsing mapping: {e}\n"
297
+ elapsed = time.time() - start_time
298
+ log += f"Elapsed time: {elapsed:.2f} seconds\n"
299
+ return None, log
300
+
301
+ # Use a temp file for iterative swapping
302
+ temp_dst_path = os.path.join(temp_dir, "temp_dst.jpg")
303
+ shutil.copy(dst_path, temp_dst_path)
304
+
305
+ for face_idx, src_idx in enumerate(mapping, start=1):
306
+ if src_idx < 1 or src_idx > len(src_paths):
307
+ log += f"Invalid source index {src_idx} for face {face_idx}, skipping.\n"
308
+ continue
309
+ try:
310
+ swapped_img = swapper.swap_faces(src_paths[src_idx-1], 1, temp_dst_path, face_idx)
311
+ cv2.imwrite(temp_dst_path, swapped_img)
312
+ log += f"Swapped face {face_idx} in destination with source {src_idx}\n"
313
+ except Exception as e:
314
+ log += f"Failed to swap face {face_idx} with source {src_idx}: {e}\n"
315
+
316
+ shutil.copy(temp_dst_path, output_path)
317
+ log += f"Saved swapped image to {output_path}\n"
318
+ if os.path.exists(temp_dst_path):
319
+ os.remove(temp_dst_path)
320
+ elapsed = time.time() - start_time
321
+ log += f"Elapsed time: {elapsed:.2f} seconds\n"
322
+ return output_path, log
323
+
324
+ #Video Swapping Functions
325
+ def add_audio_to_video(original_video_path, video_no_audio_path, output_path):
326
+ cmd = [
327
+ "ffmpeg",
328
+ "-y",
329
+ "-i", video_no_audio_path,
330
+ "-i", original_video_path,
331
+ "-c:v", "copy",
332
+ "-c:a", "aac",
333
+ "-map", "0:v:0",
334
+ "-map", "1:a:0?",
335
+ "-shortest",
336
+ output_path
337
+ ]
338
+ try:
339
+ subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
340
+ return True, ""
341
+ except subprocess.CalledProcessError as e:
342
+ return False, e.stderr.decode()
343
+
344
+ def swap_video(src_img, src_idx, video, dst_idx, delete_frames_dir=True, add_audio=True, progress=gr.Progress()):
345
+ log = ""
346
+ start_time = time.time()
347
+ src_path = "VideoSwapping/data_src.jpg"
348
+ dst_video_path = "VideoSwapping/data_dst.mp4"
349
+ frames_dir = "VideoSwapping/video_frames"
350
+ swapped_dir = "VideoSwapping/swapped_frames"
351
+ output_video_path = "VideoSwapping/output_tmp_output_video.mp4"
352
+ final_output_path = "VideoSwapping/output_with_audio.mp4"
353
+
354
+ os.makedirs(os.path.dirname(src_path), exist_ok=True)
355
+ os.makedirs(os.path.dirname(dst_video_path), exist_ok=True)
356
+ os.makedirs(frames_dir, exist_ok=True)
357
+ os.makedirs(swapped_dir, exist_ok=True)
358
+
359
+ src_img_bgr = cv2.cvtColor(src_img, cv2.COLOR_RGB2BGR)
360
+ cv2.imwrite(src_path, src_img_bgr)
361
+ log += f"Saved source image to {src_path}\n"
362
+ progress(0.05, desc="Saved source image")
363
+
364
+ if isinstance(video, str) and os.path.exists(video):
365
+ shutil.copy(video, dst_video_path)
366
+ log += f"Copied video to {dst_video_path}\n"
367
+ else:
368
+ dst_video_path = video
369
+
370
+ from VideoSwapping import extract_frames, frames_to_video
371
+
372
+ frame_paths = extract_frames(dst_video_path, frames_dir)
373
+ log += f"Extracted {len(frame_paths)} frames to {frames_dir}\n"
374
+ progress(0.15, desc="Extracted frames")
375
+
376
+ swapped_files = set(os.listdir(swapped_dir))
377
+ total_frames = len(frame_paths)
378
+ start_loop_time = time.time()
379
+ for idx, frame_path in enumerate(frame_paths):
380
+ swapped_name = f"swapped_{idx:05d}.jpg"
381
+ out_path = os.path.join(swapped_dir, swapped_name)
382
+ if swapped_name in swapped_files and os.path.exists(out_path):
383
+ log += f"Frame {idx}: already swapped, skipping.\n"
384
+ elapsed = time.time() - start_loop_time
385
+ avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
386
+ remaining = avg_time * (total_frames - (idx + 1))
387
+ mins, secs = divmod(int(remaining), 60)
388
+ progress(0.15 + 0.6 * (idx + 1) / total_frames, desc=f"Swapping {idx+1}/{total_frames} | {mins:02d}:{secs:02d} left")
389
+ continue
390
+ try:
391
+ try:
392
+ swapped = swapper.swap_faces(src_path, int(src_idx), frame_path, int(dst_idx))
393
+ except ValueError as ve:
394
+ if int(dst_idx) != 1 and "Target image contains" in str(ve):
395
+ swapped = swapper.swap_faces(src_path, int(src_idx), frame_path, 1)
396
+ log += f"Frame {idx}: dst_idx {dst_idx} not found, used 1 instead.\n"
397
+ else:
398
+ raise ve
399
+ cv2.imwrite(out_path, swapped)
400
+ log += f"Swapped frame {idx} and saved to {out_path}\n"
401
+ except Exception as e:
402
+ cv2.imwrite(out_path, cv2.imread(frame_path))
403
+ log += f"Failed to swap frame {idx}: {e}\n"
404
+ elapsed = time.time() - start_loop_time
405
+ avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
406
+ remaining = avg_time * (total_frames - (idx + 1))
407
+ mins, secs = divmod(int(remaining), 60)
408
+ progress(0.15 + 0.6 * (idx + 1) / total_frames, desc=f"Swapping {idx+1}/{total_frames} | {mins:02d}:{secs:02d} left")
409
+ cap = cv2.VideoCapture(dst_video_path)
410
+ fps = cap.get(cv2.CAP_PROP_FPS)
411
+ cap.release()
412
+ frames_to_video(swapped_dir, output_video_path, fps)
413
+ log += f"Combined swapped frames into video {output_video_path}\n"
414
+ progress(0.8, desc="Muxing audio")
415
+
416
+ if add_audio:
417
+ ok, audio_log = add_audio_to_video(dst_video_path, output_video_path, final_output_path)
418
+ if ok:
419
+ log += f"Added audio to {final_output_path}\n"
420
+ else:
421
+ log += f"Audio muxing failed: {audio_log}\n"
422
+ final_output_path = output_video_path
423
+ else:
424
+ final_output_path = output_video_path
425
+ log += "Audio was not added as per user request.\n"
426
+
427
+ try:
428
+ if os.path.exists(src_path):
429
+ os.remove(src_path)
430
+ if os.path.exists(dst_video_path):
431
+ os.remove(dst_video_path)
432
+ if delete_frames_dir and os.path.exists(frames_dir):
433
+ shutil.rmtree(frames_dir)
434
+ log += "Deleted video_frames directory.\n"
435
+ elif not delete_frames_dir:
436
+ log += "Kept video_frames directory as requested.\n"
437
+ if os.path.exists(swapped_dir):
438
+ shutil.rmtree(swapped_dir)
439
+ log += "Cleaned up temp files and folders.\n"
440
+ except Exception as cleanup_error:
441
+ log += f"Cleanup error: {cleanup_error}\n"
442
+ progress(1, desc="Done")
443
+ elapsed = time.time() - start_time
444
+ log += f"Elapsed time: {elapsed:.2f} seconds\n"
445
+ return final_output_path, log
446
+
447
+ def swap_video_all_faces(src_img, video, num_faces_to_swap, delete_frames_dir=True, add_audio=True, progress=gr.Progress()):
448
  log = ""
449
  start_time = time.time()
450
  src_path = "VideoSwappingAllFaces/data_src.jpg"
 
524
  log += f"Combined swapped frames into video {output_video_path}\n"
525
  progress(0.8, desc="Muxing audio")
526
 
527
+ if add_audio:
528
+ ok, audio_log = add_audio_to_video(dst_video_path, output_video_path, final_output_path)
529
+ if ok:
530
+ log += f"Added audio to {final_output_path}\n"
531
+ else:
532
+ log += f"Audio muxing failed: {audio_log}\n"
533
+ final_output_path = output_video_path
534
  else:
 
535
  final_output_path = output_video_path
536
+ log += "Audio was not added as per user request.\n"
537
 
538
  try:
539
  if os.path.exists(src_path):
 
555
  log += f"Elapsed time: {elapsed:.2f} seconds\n"
556
  return final_output_path, log
557
 
558
+ def swap_video_custom_mapping(src_imgs, video, mapping_str, delete_frames_dir=True, add_audio=True, progress=gr.Progress()):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
559
  log = ""
560
  start_time = time.time()
561
  src_dir = "CustomVideoSwap/src"
 
664
  log += f"Combined swapped frames into video {output_video_path}\n"
665
  progress(0.9, desc="Muxing audio")
666
 
667
+ if add_audio:
668
+ ok, audio_log = add_audio_to_video(dst_video_path, output_video_path, final_output_path)
669
+ if ok:
670
+ log += f"Added audio to {final_output_path}\n"
671
+ else:
672
+ log += f"Audio muxing failed: {audio_log}\n"
673
+ final_output_path = output_video_path
674
  else:
 
675
  final_output_path = output_video_path
676
+ log += "Audio was not added as per user request.\n"
677
 
678
  try:
679
  if os.path.exists(dst_video_path):
 
693
  log += f"Elapsed time: {elapsed:.2f} seconds\n"
694
  return final_output_path, log
695
 
696
+
697
  # Gradio UI
698
  with gr.Blocks() as demo:
699
  gr.Markdown(wellcomingMessage)
 
772
  gr.Number(value=1, label="Source Face Index"),
773
  gr.Video(label="Target Video"),
774
  gr.Number(value=1, label="Destination Face Index"),
775
+ gr.Checkbox(label="Delete video_frames directory after processing", value=True),
776
+ gr.Checkbox(label="Add audio from original video", value=True)
777
  ],
778
  outputs=[
779
  gr.Video(label="Swapped Video"),
 
787
  gr.Image(label="Source Image"),
788
  gr.Video(label="Target Video"),
789
  gr.Number(value=1, label="Number of Faces to Swap"),
790
+ gr.Checkbox(label="Delete video_frames directory after processing", value=True),
791
+ gr.Checkbox(label="Add audio from original video", value=True)
792
  ],
793
  outputs=[
794
  gr.Video(label="Swapped Video"),
 
802
  gr.Gallery(label="Source Images", type="numpy", columns=3),
803
  gr.Video(label="Target Video"),
804
  gr.Textbox(label="Mapping (comma-separated, e.g. 2,1,3)"),
805
+ gr.Checkbox(label="Delete video_frames directory after processing", value=True),
806
+ gr.Checkbox(label="Add audio from original video", value=True)
807
  ],
808
  outputs=[
809
  gr.Video(label="Swapped Video"),
 
813
 
814
 
815
  if __name__ == "__main__":
816
+ parser = argparse.ArgumentParser()
817
+ parser.add_argument("--share", action="store_true", help="Launch Gradio with share=True")
818
+ args = parser.parse_args()
819
+ demo.launch(share=args.share)