MogensR commited on
Commit
c7daa4f
Β·
1 Parent(s): 211fb51

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +134 -0
app.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import gradio as gr
4
+ import torch
5
+ import numpy as np
6
+ import onnxruntime as ort
7
+ import requests
8
+ import time
9
+ import ffmpeg
10
+ import asyncio
11
+ import shutil
12
+ from rembg import remove, new_session
13
+ from concurrent.futures import ThreadPoolExecutor
14
+
15
+ # Ensure model directory exists
16
+ MODEL_DIR = os.path.expanduser("~/.u2net/")
17
+ MODEL_PATH = os.path.join(MODEL_DIR, "u2net.onnx")
18
+ CACHE_MODEL_PATH = "cached_model.onnx"
19
+
20
+ if not os.path.exists(CACHE_MODEL_PATH):
21
+ print("Checking model cache...")
22
+ if not os.path.exists(MODEL_PATH):
23
+ print("Downloading U2Net model...")
24
+ os.makedirs(MODEL_DIR, exist_ok=True)
25
+ response = requests.get("https://github.com/danielgatis/rembg/releases/download/v0.0.0/u2net.onnx", stream=True)
26
+ with open(MODEL_PATH, "wb") as f:
27
+ for chunk in response.iter_content(chunk_size=8192):
28
+ f.write(chunk)
29
+ print("Download complete!")
30
+ shutil.copy(MODEL_PATH, CACHE_MODEL_PATH)
31
+ print("Model cached for future use.")
32
+ else:
33
+ print("Cached model found. Skipping download.")
34
+
35
+ # Enable GPU Execution if available
36
+ execution_providers = ["CUDAExecutionProvider"] if torch.cuda.is_available() else ["CPUExecutionProvider"]
37
+
38
+ # Lazy-load rembg session to avoid blocking
39
+ session = None
40
+ def get_rembg_session():
41
+ global session
42
+ if session is None:
43
+ session = new_session("u2net", providers=execution_providers)
44
+ return session
45
+
46
+ print("Model successfully loaded and ready!")
47
+
48
+ def extract_audio(video_path, audio_path, progress_callback):
49
+ progress_callback.update(0.05, "πŸ”΄ Extracting Audio")
50
+ ffmpeg.input(video_path).output(audio_path, codec="aac").run(overwrite_output=True, quiet=True)
51
+ progress_callback.update(0.15, "πŸ”΄ Audio Extracted")
52
+
53
+ def merge_audio(video_path, audio_path, output_path, progress_callback):
54
+ progress_callback.update(0.85, "🟒 Preparing to Merge Audio")
55
+ temp_video = "temp_video.mp4"
56
+ ffmpeg.input(video_path).output(temp_video, vcodec="copy", acodec="aac").run(overwrite_output=True, quiet=True)
57
+ progress_callback.update(0.90, "🟒 Merging Audio & Finalizing")
58
+ ffmpeg.input(temp_video).input(audio_path).output(
59
+ output_path, vcodec="copy", acodec="aac", shortest=True, **{"map": "0:v:0", "map": "1:a:0"}
60
+ ).run(overwrite_output=True, quiet=False)
61
+ progress_callback.update(1.00, "βœ… Processing Complete")
62
+
63
+ def process_video(video_file, background_file, progress=gr.Progress(track_tqdm=True)):
64
+ if not video_file:
65
+ return "Error: No video file provided."
66
+ if not background_file:
67
+ return "Error: No background image provided."
68
+
69
+ foreground_path = video_file.name
70
+ background_path = background_file.name
71
+ temp_video_path = "temp_output.mp4"
72
+ final_output_path = "output_with_audio.mp4"
73
+ audio_path = "temp_audio.aac"
74
+
75
+ cap_fg = cv2.VideoCapture(foreground_path)
76
+ if not cap_fg.isOpened():
77
+ return "Error: Unable to open foreground video."
78
+
79
+ progress.update(0.05, "πŸ”΄ Extracting Audio")
80
+ extract_audio(foreground_path, audio_path, progress)
81
+
82
+ background_img = cv2.imread(background_path)
83
+ if background_img is None:
84
+ return "Error: Unable to open background image."
85
+
86
+ frame_width = int(cap_fg.get(cv2.CAP_PROP_FRAME_WIDTH))
87
+ frame_height = int(cap_fg.get(cv2.CAP_PROP_FRAME_HEIGHT))
88
+ fps = int(cap_fg.get(cv2.CAP_PROP_FPS))
89
+ total_frames = int(cap_fg.get(cv2.CAP_PROP_FRAME_COUNT))
90
+
91
+ fourcc = cv2.VideoWriter_fourcc(*"mp4v")
92
+ out = cv2.VideoWriter(temp_video_path, fourcc, fps, (frame_width, frame_height))
93
+
94
+ background_img = cv2.resize(background_img, (frame_width, frame_height))
95
+ session = get_rembg_session()
96
+
97
+ for frame_idx in range(total_frames):
98
+ ret_fg, frame_fg = cap_fg.read()
99
+ if not ret_fg:
100
+ break
101
+
102
+ progress.update((frame_idx + 1) / total_frames, f"πŸ”΅ Processing Frame {frame_idx + 1}/{total_frames}")
103
+
104
+ frame_no_bg = remove(frame_fg, session=session)
105
+ frame_no_bg = cv2.cvtColor(frame_no_bg, cv2.COLOR_RGBA2BGRA)
106
+
107
+ alpha_channel = frame_no_bg[:, :, 3]
108
+ mask_inv = cv2.bitwise_not(alpha_channel)
109
+
110
+ fg_part = cv2.bitwise_and(frame_fg, frame_fg, mask=alpha_channel)
111
+ bg_part = cv2.bitwise_and(background_img, background_img, mask=mask_inv)
112
+ final_frame = cv2.add(bg_part, fg_part)
113
+ out.write(final_frame)
114
+
115
+ cap_fg.release()
116
+ out.release()
117
+
118
+ progress.update(0.85, "🟒 Merging Audio")
119
+ merge_audio(temp_video_path, audio_path, final_output_path, progress)
120
+ progress.update(1.00, "βœ… Processing Complete")
121
+
122
+ return final_output_path
123
+
124
+ iface = gr.Interface(
125
+ fn=process_video,
126
+ inputs=[gr.File(type="filepath"), gr.File(type="filepath")],
127
+ outputs=gr.File(type="filepath"),
128
+ title="🎬 AI Video Background Remover - Optimized",
129
+ description="Upload a video and a background image. The AI replaces the background while keeping the audio.",
130
+ live=False
131
+ )
132
+
133
+ if __name__ == "__main__":
134
+ iface.launch()