Update app.py
Browse files
app.py
CHANGED
|
@@ -8,7 +8,6 @@ import cv2
|
|
| 8 |
import insightface
|
| 9 |
from insightface.app import FaceAnalysis
|
| 10 |
import tempfile
|
| 11 |
-
import os
|
| 12 |
|
| 13 |
st.set_page_config(page_title="Face Swapper", layout="centered")
|
| 14 |
|
|
@@ -41,44 +40,56 @@ app, swapper = load_models(ctx_id)
|
|
| 41 |
# ------------------------------
|
| 42 |
def swap_faces_in_video(image, video, progress):
|
| 43 |
source_faces = app.get(image)
|
| 44 |
-
if
|
| 45 |
st.error("❌ No face detected in the source image.")
|
| 46 |
return None
|
| 47 |
|
| 48 |
-
|
|
|
|
| 49 |
|
| 50 |
-
# Temporary output file
|
| 51 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_out:
|
| 52 |
output_path = tmp_out.name
|
| 53 |
|
| 54 |
cap = cv2.VideoCapture(video)
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
| 57 |
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
| 58 |
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
| 59 |
-
fps = cap.get(cv2.CAP_PROP_FPS)
|
| 60 |
|
| 61 |
# MP4 writer
|
| 62 |
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
| 63 |
out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height))
|
| 64 |
|
| 65 |
i = 0
|
| 66 |
-
while
|
| 67 |
ret, frame = cap.read()
|
| 68 |
if not ret:
|
| 69 |
break
|
| 70 |
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
-
|
| 75 |
-
# depending on insightface version, frame or result_frame may be needed
|
| 76 |
-
try:
|
| 77 |
-
result_frame = swapper.get(frame, target_face, source_face, paste_back=True)
|
| 78 |
-
except:
|
| 79 |
-
result_frame = swapper.get(result_frame, target_face, source_face, paste_back=True)
|
| 80 |
|
| 81 |
-
|
|
|
|
|
|
|
| 82 |
|
| 83 |
i += 1
|
| 84 |
if frame_count > 0:
|
|
@@ -110,12 +121,15 @@ if video_file:
|
|
| 110 |
# Button to process
|
| 111 |
if st.button("🚀 Start Face Swap"):
|
| 112 |
if image_file and video_file:
|
|
|
|
|
|
|
| 113 |
source_image = cv2.imdecode(
|
| 114 |
-
np.frombuffer(
|
| 115 |
)
|
| 116 |
|
|
|
|
| 117 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_video:
|
| 118 |
-
tmp_video.write(
|
| 119 |
tmp_video_path = tmp_video.name
|
| 120 |
|
| 121 |
with st.spinner("Processing video... Please wait ⏳"):
|
|
@@ -135,9 +149,7 @@ if st.button("🚀 Start Face Swap"):
|
|
| 135 |
mime="video/mp4"
|
| 136 |
)
|
| 137 |
|
| 138 |
-
# Cleanup
|
| 139 |
os.remove(tmp_video_path)
|
| 140 |
-
# Optionally delete output file after preview
|
| 141 |
-
# os.remove(output_video_path)
|
| 142 |
else:
|
| 143 |
st.error("⚠️ Please upload both a source image and a video.")
|
|
|
|
| 8 |
import insightface
|
| 9 |
from insightface.app import FaceAnalysis
|
| 10 |
import tempfile
|
|
|
|
| 11 |
|
| 12 |
st.set_page_config(page_title="Face Swapper", layout="centered")
|
| 13 |
|
|
|
|
| 40 |
# ------------------------------
|
| 41 |
def swap_faces_in_video(image, video, progress):
|
| 42 |
source_faces = app.get(image)
|
| 43 |
+
if not source_faces:
|
| 44 |
st.error("❌ No face detected in the source image.")
|
| 45 |
return None
|
| 46 |
|
| 47 |
+
# Use largest face if multiple
|
| 48 |
+
source_face = max(source_faces, key=lambda f: (f.bbox[2]-f.bbox[0]) * (f.bbox[3]-f.bbox[1]))
|
| 49 |
|
| 50 |
+
# Temporary output file
|
| 51 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_out:
|
| 52 |
output_path = tmp_out.name
|
| 53 |
|
| 54 |
cap = cv2.VideoCapture(video)
|
| 55 |
+
if not cap.isOpened():
|
| 56 |
+
st.error("❌ Could not open video file.")
|
| 57 |
+
return None
|
| 58 |
|
| 59 |
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
| 60 |
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
| 61 |
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
| 62 |
+
fps = cap.get(cv2.CAP_PROP_FPS) or 25.0
|
| 63 |
|
| 64 |
# MP4 writer
|
| 65 |
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
| 66 |
out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height))
|
| 67 |
|
| 68 |
i = 0
|
| 69 |
+
while True:
|
| 70 |
ret, frame = cap.read()
|
| 71 |
if not ret:
|
| 72 |
break
|
| 73 |
|
| 74 |
+
try:
|
| 75 |
+
target_faces = app.get(frame)
|
| 76 |
+
result_frame = frame.copy()
|
| 77 |
+
|
| 78 |
+
for target_face in target_faces:
|
| 79 |
+
try:
|
| 80 |
+
result_frame = swapper.get(
|
| 81 |
+
frame, target_face, source_face, paste_back=True
|
| 82 |
+
)
|
| 83 |
+
except Exception:
|
| 84 |
+
result_frame = swapper.get(
|
| 85 |
+
result_frame, target_face, source_face, paste_back=True
|
| 86 |
+
)
|
| 87 |
|
| 88 |
+
out.write(result_frame)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
+
except Exception as e:
|
| 91 |
+
# Skip problematic frame
|
| 92 |
+
print(f"⚠️ Frame {i} skipped due to error: {e}")
|
| 93 |
|
| 94 |
i += 1
|
| 95 |
if frame_count > 0:
|
|
|
|
| 121 |
# Button to process
|
| 122 |
if st.button("🚀 Start Face Swap"):
|
| 123 |
if image_file and video_file:
|
| 124 |
+
# Use .getvalue() instead of .read() (safe for multiple access)
|
| 125 |
+
image_bytes = image_file.getvalue()
|
| 126 |
source_image = cv2.imdecode(
|
| 127 |
+
np.frombuffer(image_bytes, np.uint8), cv2.IMREAD_COLOR
|
| 128 |
)
|
| 129 |
|
| 130 |
+
video_bytes = video_file.getvalue()
|
| 131 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_video:
|
| 132 |
+
tmp_video.write(video_bytes)
|
| 133 |
tmp_video_path = tmp_video.name
|
| 134 |
|
| 135 |
with st.spinner("Processing video... Please wait ⏳"):
|
|
|
|
| 149 |
mime="video/mp4"
|
| 150 |
)
|
| 151 |
|
| 152 |
+
# Cleanup temp input (keep output so it can be downloaded)
|
| 153 |
os.remove(tmp_video_path)
|
|
|
|
|
|
|
| 154 |
else:
|
| 155 |
st.error("⚠️ Please upload both a source image and a video.")
|