VideoBackgroundReplacer / utils /cv_processing.py
MogensR's picture
Update utils/cv_processing.py
efe9b1b
raw
history blame
4.36 kB
#!/usr/bin/env python3
"""
cv_processing.py Β· slim orchestrator layer
──────────────────────────────────────────────────────────────────────────────
Keeps the public API (segment_person_hq, refine_mask_hq, replace_background_hq,
create_professional_background, validate_video_file) exactly the same so that
existing callers do **not** need to change their imports.
All heavy-lifting implementations live in:
utils.segmentation
utils.refinement
utils.compositing
utils.background_factory
utils.background_presets
"""
from __future__ import annotations
# ── std / 3rd-party ────────────────────────────────────────────────────────
import os, logging, cv2, numpy as np
from pathlib import Path
from typing import Tuple, Dict, Any, Optional
# ── project helpers (new modules we split out) ─────────────────────────────
from utils.segmentation import (
segment_person_hq,
segment_person_hq_original,
SegmentationError,
)
from utils.refinement import (
refine_mask_hq, MaskRefinementError,
)
from utils.compositing import (
replace_background_hq, BackgroundReplacementError,
)
from utils.background_factory import create_professional_background
from utils.background_presets import PROFESSIONAL_BACKGROUNDS # still used in the UI
logger = logging.getLogger(__name__)
# ----------------------------------------------------------------------------
# LIGHT CONFIG – only what the UI still needs
# ----------------------------------------------------------------------------
USE_AUTO_TEMPORAL_CONSISTENCY = True # placeholder for future smoothing
# Validator soft-limits (kept here because validate_video_file still lives here)
MIN_AREA_RATIO = 0.015
MAX_AREA_RATIO = 0.97
# ----------------------------------------------------------------------------
# PUBLIC 1-LINERS to keep old call-sites working
# ----------------------------------------------------------------------------
# (They’re just re-exports from their new homes.)
__all__ = [
"segment_person_hq",
"segment_person_hq_original",
"refine_mask_hq",
"replace_background_hq",
"create_professional_background",
"validate_video_file",
"SegmentationError",
"MaskRefinementError",
"BackgroundReplacementError",
"PROFESSIONAL_BACKGROUNDS",
]
# ----------------------------------------------------------------------------
# VIDEO VALIDATION (unchanged)
# ----------------------------------------------------------------------------
def validate_video_file(video_path: str) -> Tuple[bool, str]:
"""
Quick sanity-check before passing a file to OpenCV / FFmpeg.
Returns (ok, human_readable_reason)
"""
if not video_path or not Path(video_path).exists():
return False, "Video file not found"
try:
size = Path(video_path).stat().st_size
if size == 0:
return False, "File is empty"
if size > 2 * 1024 * 1024 * 1024:
return False, "File > 2 GB β€” too large for the Space quota"
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return False, "OpenCV cannot read the file"
n_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
fps = cap.get(cv2.CAP_PROP_FPS)
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
cap.release()
if n_frames == 0:
return False, "No frames detected"
if fps <= 0 or fps > 120:
return False, f"Suspicious FPS: {fps}"
if w <= 0 or h <= 0:
return False, "Zero resolution"
if w > 4096 or h > 4096:
return False, f"Resolution {w}Γ—{h} too high (max 4 096Β²)"
if (n_frames / fps) > 300:
return False, "Video longer than 5 minutes"
return True, f"OK β†’ {w}Γ—{h}, {fps:.1f} fps, {n_frames/fps:.1f} s"
except Exception as e:
logger.error(f"validate_video_file: {e}")
return False, f"Validation error: {e}"