Deepro Bardhan commited on
Commit
6d83718
·
1 Parent(s): 366e7e8

added swap face custom in single photo and video

Browse files
Files changed (3) hide show
  1. .gitignore +2 -0
  2. app.py +280 -29
  3. requirements.txt +2 -1
.gitignore CHANGED
@@ -5,6 +5,8 @@ VideoSwappingAllFaces/
5
  MultiSrcSingleDst/
6
  MultiSrcMultiDst/
7
  SingleSrcMultiDst/
 
 
8
  __pycache__/*
9
  .gradio/
10
 
 
5
  MultiSrcSingleDst/
6
  MultiSrcMultiDst/
7
  SingleSrcMultiDst/
8
+ CustomSwap/
9
+ CustomVideoSwap/
10
  __pycache__/*
11
  .gradio/
12
 
app.py CHANGED
@@ -16,6 +16,7 @@ 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
  try:
20
  progress(0, desc="Preparing files")
21
  src_path = "SinglePhoto/data_src.jpg"
@@ -43,14 +44,19 @@ def swap_single_photo(src_img, src_idx, dst_img, dst_idx, progress=gr.Progress(t
43
  except Exception as cleanup_error:
44
  log += f"Cleanup error: {cleanup_error}\n"
45
  progress(1, desc="Done")
 
 
46
  return output_path, log
47
  except Exception as e:
48
  log += f"Error: {e}\n"
49
  progress(1, desc="Error")
 
 
50
  return None, log
51
 
52
  def swap_video(src_img, src_idx, video, dst_idx, progress=gr.Progress()):
53
  log = ""
 
54
  src_path = "VideoSwapping/data_src.jpg"
55
  dst_video_path = "VideoSwapping/data_dst.mp4"
56
  frames_dir = "VideoSwapping/video_frames"
@@ -83,14 +89,14 @@ def swap_video(src_img, src_idx, video, dst_idx, progress=gr.Progress()):
83
 
84
  # Prepare swapped frames list and resume if possible
85
  swapped_files = set(os.listdir(swapped_dir))
86
- start_time = time.time()
87
  total_frames = len(frame_paths)
 
88
  for idx, frame_path in enumerate(frame_paths):
89
  swapped_name = f"swapped_{idx:05d}.jpg"
90
  out_path = os.path.join(swapped_dir, swapped_name)
91
  if swapped_name in swapped_files and os.path.exists(out_path):
92
  log += f"Frame {idx}: already swapped, skipping.\n"
93
- elapsed = time.time() - start_time
94
  avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
95
  remaining = avg_time * (total_frames - (idx + 1))
96
  mins, secs = divmod(int(remaining), 60)
@@ -110,7 +116,7 @@ def swap_video(src_img, src_idx, video, dst_idx, progress=gr.Progress()):
110
  except Exception as e:
111
  cv2.imwrite(out_path, cv2.imread(frame_path))
112
  log += f"Failed to swap frame {idx}: {e}\n"
113
- elapsed = time.time() - start_time
114
  avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
115
  remaining = avg_time * (total_frames - (idx + 1))
116
  mins, secs = divmod(int(remaining), 60)
@@ -143,6 +149,8 @@ def swap_video(src_img, src_idx, video, dst_idx, progress=gr.Progress()):
143
  except Exception as cleanup_error:
144
  log += f"Cleanup error: {cleanup_error}\n"
145
  progress(1, desc="Done")
 
 
146
  return final_output_path, log
147
 
148
  def add_audio_to_video(original_video_path, video_no_audio_path, output_path):
@@ -325,6 +333,7 @@ def swap_video_all_faces(src_img, video, num_faces_to_swap, progress=gr.Progress
325
  For each frame, swaps one by one, always using the latest swapped image for the next face.
326
  """
327
  log = ""
 
328
  src_path = "VideoSwappingAllFaces/data_src.jpg"
329
  dst_video_path = "VideoSwappingAllFaces/data_dst.mp4"
330
  frames_dir = "VideoSwappingAllFaces/video_frames"
@@ -357,8 +366,8 @@ def swap_video_all_faces(src_img, video, num_faces_to_swap, progress=gr.Progress
357
  swapped_files = set(os.listdir(swapped_dir))
358
  temp_dir = os.path.join(swapped_dir, "temp_swap")
359
  os.makedirs(temp_dir, exist_ok=True)
360
- start_time = time.time()
361
  total_frames = len(frame_paths)
 
362
 
363
  for idx, frame_path in enumerate(frame_paths):
364
  swapped_name = f"swapped_{idx:05d}.jpg"
@@ -366,7 +375,7 @@ def swap_video_all_faces(src_img, video, num_faces_to_swap, progress=gr.Progress
366
  temp_frame_path = os.path.join(temp_dir, "temp.jpg")
367
  if swapped_name in swapped_files and os.path.exists(out_path):
368
  log += f"Frame {idx}: already swapped, skipping.\n"
369
- elapsed = time.time() - start_time
370
  avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
371
  remaining = avg_time * (total_frames - (idx + 1))
372
  mins, secs = divmod(int(remaining), 60)
@@ -387,7 +396,7 @@ def swap_video_all_faces(src_img, video, num_faces_to_swap, progress=gr.Progress
387
  except Exception as e:
388
  cv2.imwrite(out_path, cv2.imread(frame_path))
389
  log += f"Failed to swap frame {idx}: {e}\n"
390
- elapsed = time.time() - start_time
391
  avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
392
  remaining = avg_time * (total_frames - (idx + 1))
393
  mins, secs = divmod(int(remaining), 60)
@@ -422,8 +431,223 @@ def swap_video_all_faces(src_img, video, num_faces_to_swap, progress=gr.Progress
422
  except Exception as cleanup_error:
423
  log += f"Cleanup error: {cleanup_error}\n"
424
  progress(1, desc="Done")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  return final_output_path, log
426
 
 
427
  with gr.Blocks() as demo:
428
  gr.Markdown(wellcomingMessage)
429
  with gr.Tab("Single Photo Swapping"):
@@ -440,38 +664,38 @@ with gr.Blocks() as demo:
440
  gr.Textbox(label="Log Output", lines=8, interactive=False)
441
  ],
442
  )
443
- with gr.Tab("Video Swapping"):
 
444
  gr.Interface(
445
- fn=swap_video,
446
  inputs=[
447
  gr.Image(label="Source Image"),
448
- gr.Number(value=1, label="Source Face Index"),
449
- gr.Video(label="Target Video"),
450
- gr.Number(value=1, label="Destination Face Index"),
451
  ],
452
  outputs=[
453
- gr.Video(label="Swapped Video"),
454
  gr.Textbox(label="Log Output", lines=8, interactive=False)
455
  ],
456
  )
457
- with gr.Tab("Video All Faces"):
458
  gr.Interface(
459
- fn=swap_video_all_faces,
460
  inputs=[
461
- gr.Image(label="Source Image"),
462
- gr.Video(label="Target Video"),
463
- gr.Number(value=1, label="Number of Faces to Swap"),
464
  ],
465
  outputs=[
466
- gr.Video(label="Swapped Video"),
467
  gr.Textbox(label="Log Output", lines=8, interactive=False)
468
  ],
469
  )
470
- with gr.Tab("SingleSrc MultiDst"):
471
  gr.Interface(
472
- fn=swap_single_src_multi_dst,
473
  inputs=[
474
- gr.Image(label="Source Image"),
475
  gr.Gallery(label="Destination Images", type="numpy", columns=3),
476
  gr.Textbox(label="Destination Face Indices (comma-separated, e.g. 1,1,2)"),
477
  ],
@@ -480,29 +704,56 @@ with gr.Blocks() as demo:
480
  gr.Textbox(label="Log Output", lines=8, interactive=False)
481
  ],
482
  )
483
- with gr.Tab("MultiSrc SingleDst"):
484
  gr.Interface(
485
- fn=swap_multi_src_single_dst,
486
  inputs=[
487
  gr.Gallery(label="Source Images", type="numpy", columns=3),
488
  gr.Image(label="Destination Image"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  gr.Number(value=1, label="Destination Face Index"),
490
  ],
491
  outputs=[
492
- gr.Gallery(label="Swapped Images"),
493
  gr.Textbox(label="Log Output", lines=8, interactive=False)
494
  ],
495
  )
496
- with gr.Tab("MultiSrc MultiDst"):
497
  gr.Interface(
498
- fn=swap_multi_src_multi_dst,
 
 
 
 
 
 
 
 
 
 
 
 
 
499
  inputs=[
500
  gr.Gallery(label="Source Images", type="numpy", columns=3),
501
- gr.Gallery(label="Destination Images", type="numpy", columns=3),
502
- gr.Textbox(label="Destination Face Indices (comma-separated, e.g. 1,1,2)"),
503
  ],
504
  outputs=[
505
- gr.Gallery(label="Swapped Images"),
506
  gr.Textbox(label="Log Output", lines=8, interactive=False)
507
  ],
508
  )
 
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()
20
  try:
21
  progress(0, desc="Preparing files")
22
  src_path = "SinglePhoto/data_src.jpg"
 
44
  except Exception as cleanup_error:
45
  log += f"Cleanup error: {cleanup_error}\n"
46
  progress(1, desc="Done")
47
+ elapsed = time.time() - start_time
48
+ log += f"Elapsed time: {elapsed:.2f} seconds\n"
49
  return output_path, log
50
  except Exception as e:
51
  log += f"Error: {e}\n"
52
  progress(1, desc="Error")
53
+ elapsed = time.time() - start_time
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, 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"
 
89
 
90
  # Prepare swapped frames list and resume if possible
91
  swapped_files = set(os.listdir(swapped_dir))
 
92
  total_frames = len(frame_paths)
93
+ start_loop_time = time.time()
94
  for idx, frame_path in enumerate(frame_paths):
95
  swapped_name = f"swapped_{idx:05d}.jpg"
96
  out_path = os.path.join(swapped_dir, swapped_name)
97
  if swapped_name in swapped_files and os.path.exists(out_path):
98
  log += f"Frame {idx}: already swapped, skipping.\n"
99
+ elapsed = time.time() - start_loop_time
100
  avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
101
  remaining = avg_time * (total_frames - (idx + 1))
102
  mins, secs = divmod(int(remaining), 60)
 
116
  except Exception as e:
117
  cv2.imwrite(out_path, cv2.imread(frame_path))
118
  log += f"Failed to swap frame {idx}: {e}\n"
119
+ elapsed = time.time() - start_loop_time
120
  avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
121
  remaining = avg_time * (total_frames - (idx + 1))
122
  mins, secs = divmod(int(remaining), 60)
 
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):
 
333
  For each frame, swaps one by one, always using the latest swapped image for the next face.
334
  """
335
  log = ""
336
+ start_time = time.time()
337
  src_path = "VideoSwappingAllFaces/data_src.jpg"
338
  dst_video_path = "VideoSwappingAllFaces/data_dst.mp4"
339
  frames_dir = "VideoSwappingAllFaces/video_frames"
 
366
  swapped_files = set(os.listdir(swapped_dir))
367
  temp_dir = os.path.join(swapped_dir, "temp_swap")
368
  os.makedirs(temp_dir, exist_ok=True)
 
369
  total_frames = len(frame_paths)
370
+ start_loop_time = time.time()
371
 
372
  for idx, frame_path in enumerate(frame_paths):
373
  swapped_name = f"swapped_{idx:05d}.jpg"
 
375
  temp_frame_path = os.path.join(temp_dir, "temp.jpg")
376
  if swapped_name in swapped_files and os.path.exists(out_path):
377
  log += f"Frame {idx}: already swapped, skipping.\n"
378
+ elapsed = time.time() - start_loop_time
379
  avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
380
  remaining = avg_time * (total_frames - (idx + 1))
381
  mins, secs = divmod(int(remaining), 60)
 
396
  except Exception as e:
397
  cv2.imwrite(out_path, cv2.imread(frame_path))
398
  log += f"Failed to swap frame {idx}: {e}\n"
399
+ elapsed = time.time() - start_loop_time
400
  avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
401
  remaining = avg_time * (total_frames - (idx + 1))
402
  mins, secs = divmod(int(remaining), 60)
 
431
  except Exception as cleanup_error:
432
  log += f"Cleanup error: {cleanup_error}\n"
433
  progress(1, desc="Done")
434
+ elapsed = time.time() - start_time
435
+ log += f"Elapsed time: {elapsed:.2f} seconds\n"
436
+ return final_output_path, log
437
+
438
+ def swap_faces_custom(src_imgs, dst_img, mapping_str, progress=gr.Progress(track_tqdm=True)):
439
+ """
440
+ src_imgs: list of source images (numpy arrays)
441
+ dst_img: destination image (numpy array)
442
+ mapping_str: comma-separated string, e.g. "2,1,3"
443
+ """
444
+ log = ""
445
+ start_time = time.time()
446
+ dst_path = "CustomSwap/data_dst.jpg"
447
+ output_path = "CustomSwap/output_swapped.jpg"
448
+ src_dir = "CustomSwap/src"
449
+ temp_dir = "CustomSwap/temp"
450
+ os.makedirs(src_dir, exist_ok=True)
451
+ os.makedirs(temp_dir, exist_ok=True)
452
+ os.makedirs(os.path.dirname(dst_path), exist_ok=True)
453
+ os.makedirs(os.path.dirname(output_path), exist_ok=True)
454
+
455
+ # Save destination image
456
+ dst_img_bgr = cv2.cvtColor(dst_img, cv2.COLOR_RGB2BGR)
457
+ cv2.imwrite(dst_path, dst_img_bgr)
458
+ log += f"Saved destination image to {dst_path}\n"
459
+
460
+ # Save all source images
461
+ src_paths = []
462
+ for i, src_img in enumerate(src_imgs):
463
+ src_path = os.path.join(src_dir, f"data_src_{i+1}.jpg")
464
+ if isinstance(src_img, tuple):
465
+ src_img = src_img[0]
466
+ if src_img is None:
467
+ log += f"Source image {i+1} is None, skipping.\n"
468
+ continue
469
+ if isinstance(src_img, np.ndarray):
470
+ src_img_bgr = cv2.cvtColor(src_img, cv2.COLOR_RGB2BGR)
471
+ cv2.imwrite(src_path, src_img_bgr)
472
+ src_paths.append(src_path)
473
+ log += f"Saved source image {i+1} to {src_path}\n"
474
+ elif isinstance(src_img, str) and os.path.exists(src_img):
475
+ shutil.copy(src_img, src_path)
476
+ src_paths.append(src_path)
477
+ log += f"Copied source image {i+1} from {src_img} to {src_path}\n"
478
+ else:
479
+ log += f"Source image {i+1} is not a valid image, skipping.\n"
480
+
481
+ # Parse mapping
482
+ try:
483
+ mapping = [int(x.strip()) for x in mapping_str.split(",") if x.strip().isdigit()]
484
+ except Exception as e:
485
+ log += f"Error parsing mapping: {e}\n"
486
+ elapsed = time.time() - start_time
487
+ log += f"Elapsed time: {elapsed:.2f} seconds\n"
488
+ return None, log
489
+
490
+ # Use a temp file for iterative swapping
491
+ temp_dst_path = os.path.join(temp_dir, "temp_dst.jpg")
492
+ shutil.copy(dst_path, temp_dst_path)
493
+
494
+ for face_idx, src_idx in enumerate(mapping, start=1):
495
+ if src_idx < 1 or src_idx > len(src_paths):
496
+ log += f"Invalid source index {src_idx} for face {face_idx}, skipping.\n"
497
+ continue
498
+ try:
499
+ swapped_img = swapper.swap_faces(src_paths[src_idx-1], 1, temp_dst_path, face_idx)
500
+ cv2.imwrite(temp_dst_path, swapped_img)
501
+ log += f"Swapped face {face_idx} in destination with source {src_idx}\n"
502
+ except Exception as e:
503
+ log += f"Failed to swap face {face_idx} with source {src_idx}: {e}\n"
504
+
505
+ shutil.copy(temp_dst_path, output_path)
506
+ log += f"Saved swapped image to {output_path}\n"
507
+ if os.path.exists(temp_dst_path):
508
+ os.remove(temp_dst_path)
509
+ elapsed = time.time() - start_time
510
+ log += f"Elapsed time: {elapsed:.2f} seconds\n"
511
+ return output_path, log
512
+
513
+ def swap_video_custom_mapping(src_imgs, video, mapping_str, progress=gr.Progress()):
514
+ """
515
+ Swaps faces in each frame of the video according to the user mapping.
516
+ src_imgs: list of source images (numpy arrays)
517
+ video: path or numpy array of the video
518
+ mapping_str: comma-separated string, e.g. "2,1,3"
519
+ """
520
+ log = ""
521
+ start_time = time.time()
522
+ src_dir = "CustomVideoSwap/src"
523
+ temp_dir = "CustomVideoSwap/temp"
524
+ frames_dir = "CustomVideoSwap/frames"
525
+ swapped_dir = "CustomVideoSwap/swapped_frames"
526
+ output_video_path = "CustomVideoSwap/output_tmp_output_video.mp4"
527
+ final_output_path = "CustomVideoSwap/output_with_audio.mp4"
528
+ dst_video_path = "CustomVideoSwap/data_dst.mp4"
529
+
530
+ os.makedirs(src_dir, exist_ok=True)
531
+ os.makedirs(temp_dir, exist_ok=True)
532
+ os.makedirs(frames_dir, exist_ok=True)
533
+ os.makedirs(swapped_dir, exist_ok=True)
534
+
535
+ # Save all source images
536
+ src_paths = []
537
+ for i, src_img in enumerate(src_imgs):
538
+ src_path = os.path.join(src_dir, f"data_src_{i+1}.jpg")
539
+ if isinstance(src_img, tuple):
540
+ src_img = src_img[0]
541
+ if src_img is None:
542
+ log += f"Source image {i+1} is None, skipping.\n"
543
+ continue
544
+ if isinstance(src_img, np.ndarray):
545
+ src_img_bgr = cv2.cvtColor(src_img, cv2.COLOR_RGB2BGR)
546
+ cv2.imwrite(src_path, src_img_bgr)
547
+ src_paths.append(src_path)
548
+ log += f"Saved source image {i+1} to {src_path}\n"
549
+ elif isinstance(src_img, str) and os.path.exists(src_img):
550
+ shutil.copy(src_img, src_path)
551
+ src_paths.append(src_path)
552
+ log += f"Copied source image {i+1} from {src_img} to {src_path}\n"
553
+ else:
554
+ log += f"Source image {i+1} is not a valid image, skipping.\n"
555
+
556
+ # Parse mapping
557
+ try:
558
+ mapping = [int(x.strip()) for x in mapping_str.split(",") if x.strip().isdigit()]
559
+ except Exception as e:
560
+ log += f"Error parsing mapping: {e}\n"
561
+ elapsed = time.time() - start_time
562
+ log += f"Elapsed time: {elapsed:.2f} seconds\n"
563
+ return None, log
564
+
565
+ # Prepare video
566
+ if isinstance(video, str) and os.path.exists(video):
567
+ shutil.copy(video, dst_video_path)
568
+ log += f"Copied video to {dst_video_path}\n"
569
+ else:
570
+ dst_video_path = video
571
+
572
+ from VideoSwapping import extract_frames, frames_to_video
573
+
574
+ frame_paths = extract_frames(dst_video_path, frames_dir)
575
+ log += f"Extracted {len(frame_paths)} frames to {frames_dir}\n"
576
+ progress(0.1, desc="Extracted frames")
577
+
578
+ swapped_files = set(os.listdir(swapped_dir))
579
+ temp_frame_path = os.path.join(temp_dir, "temp.jpg")
580
+ total_frames = len(frame_paths)
581
+ start_loop_time = time.time()
582
+
583
+ for idx, frame_path in enumerate(frame_paths):
584
+ swapped_name = f"swapped_{idx:05d}.jpg"
585
+ out_path = os.path.join(swapped_dir, swapped_name)
586
+ if swapped_name in swapped_files and os.path.exists(out_path):
587
+ log += f"Frame {idx}: already swapped, skipping.\n"
588
+ elapsed = time.time() - start_loop_time
589
+ avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
590
+ remaining = avg_time * (total_frames - (idx + 1))
591
+ mins, secs = divmod(int(remaining), 60)
592
+ progress(0.1 + 0.7 * (idx + 1) / total_frames, desc=f"Swapping {idx+1}/{total_frames} | {mins:02d}:{secs:02d} left")
593
+ continue
594
+ try:
595
+ shutil.copy(frame_path, temp_frame_path)
596
+ for face_idx, src_idx in enumerate(mapping, start=1):
597
+ if src_idx < 1 or src_idx > len(src_paths):
598
+ log += f"Invalid source index {src_idx} for face {face_idx} in frame {idx}, skipping.\n"
599
+ continue
600
+ try:
601
+ swapped_img = swapper.swap_faces(src_paths[src_idx-1], 1, temp_frame_path, face_idx)
602
+ cv2.imwrite(temp_frame_path, swapped_img)
603
+ log += f"Frame {idx}: Swapped face {face_idx} with source {src_idx}\n"
604
+ except Exception as e:
605
+ log += f"Frame {idx}: Failed to swap face {face_idx} with source {src_idx}: {e}\n"
606
+ shutil.copy(temp_frame_path, out_path)
607
+ log += f"Swapped all faces in frame {idx} and saved to {out_path}\n"
608
+ if os.path.exists(temp_frame_path):
609
+ os.remove(temp_frame_path)
610
+ except Exception as e:
611
+ cv2.imwrite(out_path, cv2.imread(frame_path))
612
+ log += f"Failed to swap frame {idx}: {e}\n"
613
+ elapsed = time.time() - start_loop_time
614
+ avg_time = elapsed / (idx + 1) if idx + 1 > 0 else 0
615
+ remaining = avg_time * (total_frames - (idx + 1))
616
+ mins, secs = divmod(int(remaining), 60)
617
+ progress(0.1 + 0.7 * (idx + 1) / total_frames, desc=f"Swapping {idx+1}/{total_frames} | {mins:02d}:{secs:02d} left")
618
+ if os.path.exists(temp_dir):
619
+ shutil.rmtree(temp_dir)
620
+
621
+ cap = cv2.VideoCapture(dst_video_path)
622
+ fps = cap.get(cv2.CAP_PROP_FPS)
623
+ cap.release()
624
+ frames_to_video(swapped_dir, output_video_path, fps)
625
+ log += f"Combined swapped frames into video {output_video_path}\n"
626
+ progress(0.9, desc="Muxing audio")
627
+
628
+ ok, audio_log = add_audio_to_video(dst_video_path, output_video_path, final_output_path)
629
+ if ok:
630
+ log += f"Added audio to {final_output_path}\n"
631
+ else:
632
+ log += f"Audio muxing failed: {audio_log}\n"
633
+ final_output_path = output_video_path
634
+
635
+ try:
636
+ if os.path.exists(dst_video_path):
637
+ os.remove(dst_video_path)
638
+ if os.path.exists(frames_dir):
639
+ shutil.rmtree(frames_dir)
640
+ if os.path.exists(swapped_dir):
641
+ shutil.rmtree(swapped_dir)
642
+ log += "Cleaned up temp files and folders.\n"
643
+ except Exception as cleanup_error:
644
+ log += f"Cleanup error: {cleanup_error}\n"
645
+ progress(1, desc="Done")
646
+ elapsed = time.time() - start_time
647
+ log += f"Elapsed time: {elapsed:.2f} seconds\n"
648
  return final_output_path, log
649
 
650
+ # Add this to your Gradio UI:
651
  with gr.Blocks() as demo:
652
  gr.Markdown(wellcomingMessage)
653
  with gr.Tab("Single Photo Swapping"):
 
664
  gr.Textbox(label="Log Output", lines=8, interactive=False)
665
  ],
666
  )
667
+
668
+ with gr.Tab("SingleSrc MultiDst"):
669
  gr.Interface(
670
+ fn=swap_single_src_multi_dst,
671
  inputs=[
672
  gr.Image(label="Source Image"),
673
+ gr.Gallery(label="Destination Images", type="numpy", columns=3),
674
+ gr.Textbox(label="Destination Face Indices (comma-separated, e.g. 1,1,2)"),
 
675
  ],
676
  outputs=[
677
+ gr.Gallery(label="Swapped Images"),
678
  gr.Textbox(label="Log Output", lines=8, interactive=False)
679
  ],
680
  )
681
+ with gr.Tab("MultiSrc SingleDst"):
682
  gr.Interface(
683
+ fn=swap_multi_src_single_dst,
684
  inputs=[
685
+ gr.Gallery(label="Source Images", type="numpy", columns=3),
686
+ gr.Image(label="Destination Image"),
687
+ gr.Number(value=1, label="Destination Face Index"),
688
  ],
689
  outputs=[
690
+ gr.Gallery(label="Swapped Images"),
691
  gr.Textbox(label="Log Output", lines=8, interactive=False)
692
  ],
693
  )
694
+ with gr.Tab("MultiSrc MultiDst"):
695
  gr.Interface(
696
+ fn=swap_multi_src_multi_dst,
697
  inputs=[
698
+ gr.Gallery(label="Source Images", type="numpy", columns=3),
699
  gr.Gallery(label="Destination Images", type="numpy", columns=3),
700
  gr.Textbox(label="Destination Face Indices (comma-separated, e.g. 1,1,2)"),
701
  ],
 
704
  gr.Textbox(label="Log Output", lines=8, interactive=False)
705
  ],
706
  )
707
+ with gr.Tab("Custom Face Mapping"):
708
  gr.Interface(
709
+ fn=swap_faces_custom,
710
  inputs=[
711
  gr.Gallery(label="Source Images", type="numpy", columns=3),
712
  gr.Image(label="Destination Image"),
713
+ gr.Textbox(label="Mapping (comma-separated, e.g. 2,1,3)"),
714
+ ],
715
+ outputs=[
716
+ gr.Image(label="Swapped Image"),
717
+ gr.Textbox(label="Log Output", lines=8, interactive=False)
718
+ ],
719
+ )
720
+ with gr.Tab("Video Swapping"):
721
+ gr.Interface(
722
+ fn=swap_video,
723
+ inputs=[
724
+ gr.Image(label="Source Image"),
725
+ gr.Number(value=1, label="Source Face Index"),
726
+ gr.Video(label="Target Video"),
727
  gr.Number(value=1, label="Destination Face Index"),
728
  ],
729
  outputs=[
730
+ gr.Video(label="Swapped Video"),
731
  gr.Textbox(label="Log Output", lines=8, interactive=False)
732
  ],
733
  )
734
+ with gr.Tab("Video All Faces"):
735
  gr.Interface(
736
+ fn=swap_video_all_faces,
737
+ inputs=[
738
+ gr.Image(label="Source Image"),
739
+ gr.Video(label="Target Video"),
740
+ gr.Number(value=1, label="Number of Faces to Swap"),
741
+ ],
742
+ outputs=[
743
+ gr.Video(label="Swapped Video"),
744
+ gr.Textbox(label="Log Output", lines=8, interactive=False)
745
+ ],
746
+ )
747
+ with gr.Tab("Custom Video Face Mapping"):
748
+ gr.Interface(
749
+ fn=swap_video_custom_mapping,
750
  inputs=[
751
  gr.Gallery(label="Source Images", type="numpy", columns=3),
752
+ gr.Video(label="Target Video"),
753
+ gr.Textbox(label="Mapping (comma-separated, e.g. 2,1,3)"),
754
  ],
755
  outputs=[
756
+ gr.Video(label="Swapped Video"),
757
  gr.Textbox(label="Log Output", lines=8, interactive=False)
758
  ],
759
  )
requirements.txt CHANGED
@@ -5,4 +5,5 @@ scikit-learn
5
  scikit-image
6
  tqdm
7
  mxnet
8
- insightface
 
 
5
  scikit-image
6
  tqdm
7
  mxnet
8
+ insightface
9
+ gradio