savvy7007 commited on
Commit
747009e
·
verified ·
1 Parent(s): 1c10d3b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +164 -1
app.py CHANGED
@@ -254,4 +254,167 @@ def swap_faces_in_video(
254
  break
255
 
256
  # FPS cap by skipping frames
257
- if frame_step > 1 and (re_
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
  break
255
 
256
  # FPS cap by skipping frames
257
+ if frame_step > 1 and (read_idx % frame_step != 0):
258
+ read_idx += 1
259
+ if frame_count > 0:
260
+ progress.progress(min(1.0, read_idx / frame_count))
261
+ continue
262
+
263
+ # Resize for processing
264
+ if (proc_w, proc_h) != (orig_w, orig_h):
265
+ proc_frame = cv2.resize(frame, (proc_w, proc_h), interpolation=cv2.INTER_AREA)
266
+ else:
267
+ proc_frame = frame
268
+
269
+ try:
270
+ # Detect faces on processed frame
271
+ try:
272
+ target_faces = app.get(proc_frame)
273
+ except Exception as det_e:
274
+ print(f"[WARN] Detection failed on frame {read_idx}: {det_e}")
275
+ target_faces = []
276
+
277
+ if target_faces:
278
+ # Optionally limit faces to largest N for speed
279
+ target_faces = sorted(
280
+ target_faces,
281
+ key=lambda f: (f.bbox[2]-f.bbox[0])*(f.bbox[3]-f.bbox[1]),
282
+ reverse=True
283
+ )[:max_faces]
284
+
285
+ # Swap into a working buffer
286
+ result_frame = proc_frame.copy()
287
+ for tface in target_faces:
288
+ try:
289
+ # Some insightface builds want base=proc_frame, some allow in-place
290
+ result_frame = swapper.get(
291
+ proc_frame, tface, source_face, paste_back=True
292
+ )
293
+ except Exception:
294
+ result_frame = swapper.get(
295
+ result_frame, tface, source_face, paste_back=True
296
+ )
297
+
298
+ # Upscale back to original if requested
299
+ if keep_original_res and (proc_w, proc_h) != (orig_w, orig_h):
300
+ result_frame = cv2.resize(result_frame, (orig_w, orig_h), interpolation=cv2.INTER_CUBIC)
301
+
302
+ out.write(result_frame)
303
+
304
+ except Exception as e:
305
+ # Log & write fallback frame (processed size or original size)
306
+ print(f"[WARN] Frame {read_idx} failed: {e}")
307
+ traceback.print_exc()
308
+ fallback = proc_frame
309
+ if keep_original_res and (proc_w, proc_h) != (orig_w, orig_h):
310
+ fallback = cv2.resize(proc_frame, (orig_w, orig_h), interpolation=cv2.INTER_CUBIC)
311
+ out.write(fallback)
312
+
313
+ read_idx += 1
314
+ processed_frames += 1
315
+
316
+ # Update progress
317
+ if frame_count > 0:
318
+ progress.progress(min(1.0, read_idx / frame_count))
319
+ elif processed_frames % 30 == 0:
320
+ # Fallback progress for unknown frame counts
321
+ progress.progress(min(1.0, (processed_frames % 300) / 300.0))
322
+
323
+ finally:
324
+ cap.release()
325
+ out.release()
326
+
327
+ return output_path
328
+
329
+ # -------------------------
330
+ # UI: Uploads & Preview
331
+ # -------------------------
332
+ st.write("Upload a **source face image** and a **target video**, preview them, tweak speed options, then start swapping.")
333
+
334
+ image_file = st.file_uploader("Upload Source Image", type=["jpg", "jpeg", "png"])
335
+ video_file = st.file_uploader("Upload Target Video", type=["mp4", "mov", "mkv", "avi"])
336
+
337
+ # Previews (Streamlit handles these safely)
338
+ if image_file:
339
+ st.subheader("📷 Source Image Preview")
340
+ st.image(image_file, caption="Source Image", use_column_width=True)
341
+
342
+ if video_file:
343
+ st.subheader("🎬 Target Video Preview")
344
+ st.video(video_file)
345
+
346
+ # -------------------------
347
+ # Run button
348
+ # -------------------------
349
+ if st.button("🚀 Start Face Swap"):
350
+ if not image_file or not video_file:
351
+ st.error("⚠️ Please upload both a source image and a target video.")
352
+ else:
353
+ # Read uploads safely (do not consume file pointer used by preview)
354
+ try:
355
+ image_bytes = image_file.getvalue()
356
+ source_image = _safe_imdecode(image_bytes)
357
+ if source_image is None:
358
+ st.error("❌ Failed to decode source image. Please use a valid JPG/PNG.")
359
+ st.stop()
360
+ except Exception as e:
361
+ st.error(f"❌ Failed to read the source image bytes: {e}")
362
+ st.stop()
363
+
364
+ try:
365
+ # Persist temp video for OpenCV
366
+ video_bytes = video_file.getvalue()
367
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_video:
368
+ tmp_video.write(video_bytes)
369
+ tmp_video_path = tmp_video.name
370
+ except Exception as e:
371
+ st.error(f"❌ Failed to save the uploaded video to a temp file: {e}")
372
+ st.stop()
373
+
374
+ with st.spinner("Processing video… This can take a while ⏳"):
375
+ progress_bar = st.progress(0)
376
+ output_video_path = swap_faces_in_video(
377
+ source_image,
378
+ tmp_video_path,
379
+ proc_res=proc_res,
380
+ fps_cap=fps_cap,
381
+ keep_original_res=keep_original_res,
382
+ max_faces=max_faces,
383
+ progress=progress_bar
384
+ )
385
+
386
+ if output_video_path:
387
+ st.success("✅ Face swapping completed!")
388
+
389
+ st.subheader("📺 Output Video Preview")
390
+ st.video(output_video_path)
391
+
392
+ # Download button
393
+ try:
394
+ with open(output_video_path, "rb") as f:
395
+ st.download_button(
396
+ label="⬇️ Download Processed Video",
397
+ data=f,
398
+ file_name="output_swapped_video.mp4",
399
+ mime="video/mp4"
400
+ )
401
+ except Exception as e:
402
+ st.warning(f"⚠️ Could not open the output file for download: {e}")
403
+
404
+ # Cleanup temp input video; keep output so it can be downloaded
405
+ try:
406
+ os.remove(tmp_video_path)
407
+ except Exception:
408
+ pass
409
+
410
+ # -------------
411
+ # Diagnostics
412
+ # -------------
413
+ with st.expander("🩺 Diagnostics"):
414
+ st.write(
415
+ "- If you see **SessionInfo** errors: this app initializes `st.session_state` early and defers heavy loads via "
416
+ "`@st.cache_resource`. If errors persist, restart the Space/Runtime.\n"
417
+ "- If output is jumpy/stutters: lower **Target FPS** or choose **480p** processing.\n"
418
+ "- If video fails to open: re-encode your input to **MP4 (H.264, AAC)**.\n"
419
+ "- If VideoWriter fails: try **480p** and **Target FPS 24**."
420
+ )