Update pipeline/video_pipeline.py
Browse files- pipeline/video_pipeline.py +26 -7
pipeline/video_pipeline.py
CHANGED
|
@@ -174,27 +174,38 @@ def smooth_alpha_video(alpha_path, output_path, window_size=5):
|
|
| 174 |
def create_transparent_mov(foreground_path, alpha_path, output_dir):
|
| 175 |
"""Create transparent MOV using FFmpeg (reliable alpha handling)"""
|
| 176 |
output_path = str(output_dir / "transparent.mov")
|
|
|
|
| 177 |
try:
|
| 178 |
cmd = [
|
| 179 |
-
"ffmpeg", "-y", "-hide_banner", "-loglevel", "
|
| 180 |
"-i", foreground_path,
|
| 181 |
"-i", alpha_path,
|
| 182 |
"-filter_complex", "[0:v][1:v]alphamerge[out]",
|
| 183 |
"-map", "[out]",
|
| 184 |
-
"-c:v", "png",
|
| 185 |
"-pix_fmt", "rgba",
|
| 186 |
output_path
|
| 187 |
]
|
| 188 |
-
|
|
|
|
|
|
|
|
|
|
| 189 |
# Verify alpha channel
|
| 190 |
cap = cv2.VideoCapture(output_path)
|
| 191 |
ret, frame = cap.read()
|
| 192 |
if ret:
|
| 193 |
-
logger.info(f"FFmpeg MOV: Shape={frame.shape} | Alpha={np.unique(frame[:, :, 3])}")
|
|
|
|
|
|
|
| 194 |
cap.release()
|
|
|
|
|
|
|
|
|
|
| 195 |
return output_path
|
| 196 |
except Exception as e:
|
| 197 |
-
logger.error(f"FFmpeg MOV creation failed: {e}")
|
|
|
|
|
|
|
| 198 |
return None
|
| 199 |
# --- Stage 1: Transparent Video Creation ---
|
| 200 |
def stage1_create_transparent_video(input_file):
|
|
@@ -212,13 +223,16 @@ def stage1_create_transparent_video(input_file):
|
|
| 212 |
with tempfile.TemporaryDirectory() as temp_dir:
|
| 213 |
temp_dir = Path(temp_dir)
|
| 214 |
input_path = _normalize_input(input_file, temp_dir)
|
|
|
|
| 215 |
# Extract audio from input video
|
| 216 |
audio_path = str(temp_dir / "audio.aac")
|
| 217 |
-
extract_audio(input_path, audio_path)
|
|
|
|
| 218 |
# Generate first-frame mask
|
| 219 |
mask = generate_first_frame_mask(input_path, sam2_predictor)
|
| 220 |
mask_path = str(temp_dir / "mask.png")
|
| 221 |
cv2.imwrite(mask_path, mask)
|
|
|
|
| 222 |
# MatAnyone processing
|
| 223 |
foreground_path, alpha_path = matanyone_processor.process_video(
|
| 224 |
input_path=input_path,
|
|
@@ -226,8 +240,12 @@ def stage1_create_transparent_video(input_file):
|
|
| 226 |
output_path=str(temp_dir),
|
| 227 |
max_size=720
|
| 228 |
)
|
|
|
|
|
|
|
|
|
|
| 229 |
# Temporal smoothing
|
| 230 |
smoothed_alpha = smooth_alpha_video(alpha_path, str(temp_dir / "alpha_smoothed.mp4"))
|
|
|
|
| 231 |
# Create transparent MOV
|
| 232 |
transparent_path = create_transparent_mov(foreground_path, smoothed_alpha, temp_dir)
|
| 233 |
if not transparent_path:
|
|
@@ -235,10 +253,11 @@ def stage1_create_transparent_video(input_file):
|
|
| 235 |
# Save to persistent storage
|
| 236 |
persist_path = Path("tmp") / "transparent_video.mov"
|
| 237 |
shutil.copyfile(transparent_path, persist_path)
|
|
|
|
| 238 |
# Return both transparent video and audio paths for Stage 2
|
| 239 |
return str(persist_path), audio_path
|
| 240 |
except Exception as e:
|
| 241 |
-
logger.error(f"Stage 1 failed: {e}", exc_info=True)
|
| 242 |
st.error(f"Stage 1 Error: {str(e)}")
|
| 243 |
return None, None
|
| 244 |
finally:
|
|
|
|
| 174 |
def create_transparent_mov(foreground_path, alpha_path, output_dir):
|
| 175 |
"""Create transparent MOV using FFmpeg (reliable alpha handling)"""
|
| 176 |
output_path = str(output_dir / "transparent.mov")
|
| 177 |
+
logger.info(f"[create_transparent_mov] Foreground: {foreground_path}, Alpha: {alpha_path}, Output: {output_path}")
|
| 178 |
try:
|
| 179 |
cmd = [
|
| 180 |
+
"ffmpeg", "-y", "-hide_banner", "-loglevel", "info",
|
| 181 |
"-i", foreground_path,
|
| 182 |
"-i", alpha_path,
|
| 183 |
"-filter_complex", "[0:v][1:v]alphamerge[out]",
|
| 184 |
"-map", "[out]",
|
| 185 |
+
"-c:v", "png",
|
| 186 |
"-pix_fmt", "rgba",
|
| 187 |
output_path
|
| 188 |
]
|
| 189 |
+
logger.info(f"[create_transparent_mov] Running FFmpeg command: {' '.join(cmd)}")
|
| 190 |
+
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
| 191 |
+
logger.info(f"[create_transparent_mov] FFmpeg stdout: {result.stdout}")
|
| 192 |
+
logger.info(f"[create_transparent_mov] FFmpeg stderr: {result.stderr}")
|
| 193 |
# Verify alpha channel
|
| 194 |
cap = cv2.VideoCapture(output_path)
|
| 195 |
ret, frame = cap.read()
|
| 196 |
if ret:
|
| 197 |
+
logger.info(f"[create_transparent_mov] FFmpeg MOV: Shape={frame.shape} | Alpha={np.unique(frame[:, :, 3])}")
|
| 198 |
+
else:
|
| 199 |
+
logger.error("[create_transparent_mov] Failed to read output video")
|
| 200 |
cap.release()
|
| 201 |
+
if not os.path.exists(output_path):
|
| 202 |
+
logger.error("[create_transparent_mov] Output file not created")
|
| 203 |
+
return None
|
| 204 |
return output_path
|
| 205 |
except Exception as e:
|
| 206 |
+
logger.error(f"[create_transparent_mov] FFmpeg MOV creation failed: {e}")
|
| 207 |
+
logger.error(f"[create_transparent_mov] FFmpeg stdout: {result.stdout if 'result' in locals() else 'N/A'}")
|
| 208 |
+
logger.error(f"[create_transparent_mov] FFmpeg stderr: {result.stderr if 'result' in locals() else 'N/A'}")
|
| 209 |
return None
|
| 210 |
# --- Stage 1: Transparent Video Creation ---
|
| 211 |
def stage1_create_transparent_video(input_file):
|
|
|
|
| 223 |
with tempfile.TemporaryDirectory() as temp_dir:
|
| 224 |
temp_dir = Path(temp_dir)
|
| 225 |
input_path = _normalize_input(input_file, temp_dir)
|
| 226 |
+
logger.info(f"[stage1] Input video: {input_path}")
|
| 227 |
# Extract audio from input video
|
| 228 |
audio_path = str(temp_dir / "audio.aac")
|
| 229 |
+
if not extract_audio(input_path, audio_path):
|
| 230 |
+
logger.warning("[stage1] Audio extraction failed, continuing without audio")
|
| 231 |
# Generate first-frame mask
|
| 232 |
mask = generate_first_frame_mask(input_path, sam2_predictor)
|
| 233 |
mask_path = str(temp_dir / "mask.png")
|
| 234 |
cv2.imwrite(mask_path, mask)
|
| 235 |
+
logger.info(f"[stage1] First-frame mask saved: {mask_path}")
|
| 236 |
# MatAnyone processing
|
| 237 |
foreground_path, alpha_path = matanyone_processor.process_video(
|
| 238 |
input_path=input_path,
|
|
|
|
| 240 |
output_path=str(temp_dir),
|
| 241 |
max_size=720
|
| 242 |
)
|
| 243 |
+
logger.info(f"[stage1] MatAnyone output: foreground={foreground_path}, alpha={alpha_path}")
|
| 244 |
+
if not foreground_path or not alpha_path or not os.path.exists(foreground_path) or not os.path.exists(alpha_path):
|
| 245 |
+
raise RuntimeError("MatAnyone failed to produce valid foreground or alpha paths")
|
| 246 |
# Temporal smoothing
|
| 247 |
smoothed_alpha = smooth_alpha_video(alpha_path, str(temp_dir / "alpha_smoothed.mp4"))
|
| 248 |
+
logger.info(f"[stage1] Smoothed alpha: {smoothed_alpha}")
|
| 249 |
# Create transparent MOV
|
| 250 |
transparent_path = create_transparent_mov(foreground_path, smoothed_alpha, temp_dir)
|
| 251 |
if not transparent_path:
|
|
|
|
| 253 |
# Save to persistent storage
|
| 254 |
persist_path = Path("tmp") / "transparent_video.mov"
|
| 255 |
shutil.copyfile(transparent_path, persist_path)
|
| 256 |
+
logger.info(f"[stage1] Transparent video saved: {persist_path}")
|
| 257 |
# Return both transparent video and audio paths for Stage 2
|
| 258 |
return str(persist_path), audio_path
|
| 259 |
except Exception as e:
|
| 260 |
+
logger.error(f"[stage1] Stage 1 failed: {e}", exc_info=True)
|
| 261 |
st.error(f"Stage 1 Error: {str(e)}")
|
| 262 |
return None, None
|
| 263 |
finally:
|