Remove dead code: unused detect_background_color and ForegroundEstimate extras

- chroma.detect_background_color had no callers anywhere (auto-detection
  goes through estimate_background_model / _background_border_cluster).
- ForegroundEstimate.background and .correction were computed on every
  image but never consumed; only .rgb was used. estimate_foreground_rgb
  now returns the foreground array directly and the dataclass is gone,
  which also drops pymatting return_background work and a per-image
  correction-norm pass.

Verified: CLI output on samples/TestImage3.png is byte-identical
(SHA256) to the pre-cleanup baseline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 22:41:11 +08:00
parent 8417653598
commit 88987f508c
3 changed files with 9 additions and 53 deletions
-16
View File
@@ -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,
+8 -36
View File
@@ -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)
+1 -1
View File
@@ -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,