File size: 6,057 Bytes
6135232 f0dc2a6 157d197 f0dc2a6 157d197 2652e08 157d197 6135232 157d197 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
#!/usr/bin/env python3
"""
utils package (lightweight __init__)
- Export only light helpers/consts at import time
- Provide LAZY wrappers for heavy CV functions so legacy imports still work:
from utils import segment_person_hq -> OK (resolved at call time)
"""
from __future__ import annotations
import os
import logging
from typing import Dict, Any, Tuple, Optional
# import numpy as np # light; OK at import time
logger = logging.getLogger(__name__)
# ---------------------------------------------------------------------
# Background presets & builders (lightweight)
# ---------------------------------------------------------------------
PROFESSIONAL_BACKGROUNDS: Dict[str, Dict[str, Any]] = {
"office": {"color": (240, 248, 255), "gradient": True},
"studio": {"color": (32, 32, 32), "gradient": False},
"nature": {"color": (34, 139, 34), "gradient": True},
"abstract": {"color": (75, 0, 130), "gradient": True},
"white": {"color": (255, 255, 255), "gradient": False},
"black": {"color": (0, 0, 0), "gradient": False},
# add more if you like
}
def _solid_bg(color: Tuple[int,int,int], width: int, height: int) -> np.ndarray:
return np.full((height, width, 3), tuple(int(x) for x in color), dtype=np.uint8)
def _vertical_gradient(top: Tuple[int,int,int], bottom: Tuple[int,int,int], width: int, height: int) -> np.ndarray:
bg = np.zeros((height, width, 3), dtype=np.uint8)
for y in range(height):
t = y / max(1, height - 1)
r = int(top[0] * (1 - t) + bottom[0] * t)
g = int(top[1] * (1 - t) + bottom[1] * t)
b = int(top[2] * (1 - t) + bottom[2] * t)
bg[y, :] = (r, g, b)
return bg
def create_professional_background(key_or_cfg: Any, width: int, height: int) -> np.ndarray:
"""
Accepts either:
- string key in PROFESSIONAL_BACKGROUNDS
- a config dict with {"color": (r,g,b), "gradient": bool}
Returns RGB uint8 background (H, W, 3).
"""
if isinstance(key_or_cfg, str):
cfg = PROFESSIONAL_BACKGROUNDS.get(key_or_cfg, PROFESSIONAL_BACKGROUNDS["office"])
elif isinstance(key_or_cfg, dict):
cfg = key_or_cfg
else:
cfg = PROFESSIONAL_BACKGROUNDS["office"]
color = tuple(int(x) for x in cfg.get("color", (255, 255, 255)))
use_grad = bool(cfg.get("gradient", False))
if not use_grad:
return _solid_bg(color, width, height)
# simple vertical gradient dark->color
dark = (int(color[0]*0.7), int(color[1]*0.7), int(color[2]*0.7))
return _vertical_gradient(dark, color, width, height)
def create_gradient_background(spec: Dict[str, Any], width: int, height: int) -> np.ndarray:
"""
spec: {"type": "linear"|"radial", "start": (r,g,b)|"#RRGGBB", "end": (r,g,b)|"#RRGGBB", "angle_deg": float}
Returns RGB uint8 background (H, W, 3). (Radial treated as linear fallback unless extended.)
"""
import re
import cv2 # import locally to keep top-level light
def _to_rgb(c):
if isinstance(c, (list, tuple)) and len(c) == 3:
return tuple(int(x) for x in c)
if isinstance(c, str) and re.match(r"^#[0-9a-fA-F]{6}$", c):
return tuple(int(c[i:i+2], 16) for i in (1,3,5))
return (255, 255, 255)
start = _to_rgb(spec.get("start", (32, 32, 32)))
end = _to_rgb(spec.get("end", (200, 200, 200)))
angle = float(spec.get("angle_deg", 0.0))
bg = _vertical_gradient(start, end, width, height)
# rotate by angle
center = (width / 2, height / 2)
rot = cv2.getRotationMatrix2D(center, angle, 1.0)
bg = cv2.warpAffine(bg, rot, (width, height), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101)
return bg
# ---------------------------------------------------------------------
# Video validation (lightweight)
# ---------------------------------------------------------------------
def validate_video_file(video_path: str) -> bool:
"""
Fast sanity check: file exists, cv2 can open, first frame is readable.
Returns True/False (lightweight for UI).
"""
try:
if not video_path or not os.path.exists(video_path):
return False
import cv2 # local import
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return False
ok, frame = cap.read()
cap.release()
return bool(ok and frame is not None)
except Exception as e:
logger.warning("validate_video_file error: %s", e)
return False
def validate_video_file_detail(video_path: str) -> Tuple[bool, str]:
if not video_path:
return False, "No path provided"
if not os.path.exists(video_path):
return False, "File does not exist"
try:
import cv2
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return False, "cv2 could not open file"
ok, frame = cap.read()
cap.release()
if not ok or frame is None:
return False, "Could not read first frame"
return True, "OK"
except Exception as e:
return False, f"cv2 error: {e}"
# ---------------------------------------------------------------------
# LAZY WRAPPERS (avoid importing utils.cv_processing at module import time)
# ---------------------------------------------------------------------
def segment_person_hq(*args, **kwargs):
from .cv_processing import segment_person_hq as _f
return _f(*args, **kwargs)
def refine_mask_hq(*args, **kwargs):
from .cv_processing import refine_mask_hq as _f
return _f(*args, **kwargs)
def replace_background_hq(*args, **kwargs):
from .cv_processing import replace_background_hq as _f
return _f(*args, **kwargs)
__all__ = [
# backgrounds
"PROFESSIONAL_BACKGROUNDS",
"create_professional_background",
"create_gradient_background",
# validation
"validate_video_file",
"validate_video_file_detail",
# lazy CV exports (back-compat)
"segment_person_hq",
"refine_mask_hq",
"replace_background_hq",
]
|