diff --git a/bgfilter/chroma.py b/bgfilter/chroma.py index 64d4afb..18a8827 100644 --- a/bgfilter/chroma.py +++ b/bgfilter/chroma.py @@ -112,22 +112,6 @@ def _background_border_cluster( return full, share, agree -def detect_background_color( - rgb: np.ndarray, settings: ChromaSettings | None = None -) -> tuple[float, float, float]: - """Detect a single flat background colour from the image border. - - Returns the colour as a 0..1 RGB triple. Raises RuntimeError (via - :func:`_background_border_cluster`) if the border is not dominated by one flat - colour, so callers fail loudly rather than matting against a wrong colour. - Pure: reads pixels only, with no matting side effects. - """ - settings = settings or ChromaSettings() - rgb_f, lab = convert_color_spaces(rgb) - mask, _, _ = _background_border_cluster(lab, settings) - return tuple(float(x) for x in np.median(rgb_f[mask], axis=0)) - - def estimate_background_model( rgb: np.ndarray, settings: ChromaSettings, diff --git a/bgfilter/foreground.py b/bgfilter/foreground.py index a26f2b3..b898761 100644 --- a/bgfilter/foreground.py +++ b/bgfilter/foreground.py @@ -1,7 +1,5 @@ from __future__ import annotations -from dataclasses import dataclass - import numpy as np from .chroma import BackgroundModel @@ -9,27 +7,16 @@ from .deps import require_cv2 from .settings import ForegroundSettings -@dataclass(frozen=True) -class ForegroundEstimate: - rgb: np.ndarray # estimated foreground colour F (uint8, HxWx3) - background: np.ndarray # estimated background colour B (uint8, HxWx3) - correction: np.ndarray # per-pixel |F - input| magnitude, 0..1 (float32, HxW) - - def estimate_foreground_rgb( rgb: np.ndarray, alpha: np.ndarray, bg_confidence: np.ndarray, model: BackgroundModel, settings: ForegroundSettings, -) -> ForegroundEstimate: +) -> np.ndarray: + """Estimated foreground colour F (uint8 HxWx3) for compositing over alpha.""" if not settings.enabled: - bg = np.broadcast_to(np.asarray(model.rgb_center, dtype=np.float32), rgb.shape) - return ForegroundEstimate( - rgb=rgb.copy(), - background=np.clip(bg * 255.0, 0, 255).astype(np.uint8), - correction=np.zeros(alpha.shape, dtype=np.float32), - ) + return rgb.copy() method = settings.method if method == "ml": @@ -48,7 +35,7 @@ def estimate_foreground_rgb( def _estimate_ml( rgb: np.ndarray, alpha: np.ndarray, settings: ForegroundSettings -) -> ForegroundEstimate: +) -> np.ndarray: """Multi-level closed-form foreground/background estimation (pymatting). Solves image = alpha * F + (1 - alpha) * B with spatial smoothness, so the @@ -66,17 +53,8 @@ def _estimate_ml( image = rgb.astype(np.float64) / 255.0 a = np.clip(alpha.astype(np.float64), 0.0, 1.0) - foreground, background = estimate_foreground_ml( - image, a, regularization=settings.ml_regularization, return_background=True - ) - foreground = np.clip(foreground, 0.0, 1.0) - background = np.clip(background, 0.0, 1.0) - correction = np.linalg.norm(foreground - image, axis=2) / np.sqrt(3.0) - return ForegroundEstimate( - rgb=np.clip(foreground * 255.0, 0, 255).astype(np.uint8), - background=np.clip(background * 255.0, 0, 255).astype(np.uint8), - correction=correction.astype(np.float32), - ) + foreground = estimate_foreground_ml(image, a, regularization=settings.ml_regularization) + return np.clip(foreground * 255.0, 0, 255).astype(np.uint8) def _weighted_blur(values: np.ndarray, weights: np.ndarray, radius: int) -> np.ndarray: @@ -97,7 +75,7 @@ def _estimate_unmix( bg_confidence: np.ndarray, model: BackgroundModel, settings: ForegroundSettings, -) -> ForegroundEstimate: +) -> np.ndarray: """Legacy heuristic: edge-band alpha unmix plus local foreground blur.""" rgb_f = rgb.astype(np.float32) / 255.0 r = rgb_f[..., 0] @@ -131,10 +109,4 @@ def _estimate_unmix( local_mask = base_mask * settings.local_strength * low_alpha_weight out = out * (1.0 - local_mask[..., None]) + local_fg * local_mask[..., None] - background = np.broadcast_to(bg, rgb_f.shape) - correction = np.linalg.norm(out - rgb_f, axis=2) / np.sqrt(3.0) - return ForegroundEstimate( - rgb=np.clip(out * 255.0, 0, 255).astype(np.uint8), - background=np.clip(background * 255.0, 0, 255).astype(np.uint8), - correction=correction.astype(np.float32), - ) + return np.clip(out * 255.0, 0, 255).astype(np.uint8) diff --git a/bgfilter/pipeline.py b/bgfilter/pipeline.py index 348df37..ce27d01 100644 --- a/bgfilter/pipeline.py +++ b/bgfilter/pipeline.py @@ -178,7 +178,7 @@ def _process_rgb(rgb: np.ndarray, pipeline: MattingPipeline) -> MattingResult: foreground = estimate_foreground_rgb( rgb, alpha, bg_confidence, model, settings.foreground ) - corrected_rgb, color_mask = despill(foreground.rgb, alpha, model, settings.despill) + corrected_rgb, color_mask = despill(foreground, alpha, model, settings.despill) metadata = { "alpha_source": alpha_source,