style: drop dead Candidate fields, simplify resolve_backend, US spelling

- Candidate carries only the fields the arbiter reads (key, label,
  detected_strict, detected_relaxed, features); location/region/confidence were
  vestigial from the removed best_auto_mark max-by-confidence path.
- resolve_backend returns preferred_inpaint_backend() directly (typed Literal)
  instead of an identity ternary.
- colour/normalise/behaviour -> US spelling across code comments and docs.

No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Victor Kuznetsov
2026-07-09 16:18:13 +03:00
co-authored by Claude Opus 4.8
parent 178fed69a7
commit 9756189eaf
10 changed files with 27 additions and 34 deletions
+1 -1
View File
@@ -699,7 +699,7 @@ def cmd_erase(
"""Erase arbitrary region(s) from an image via inpainting.
Universal and position-agnostic: removes any logo / watermark / object inside
the boxes you pass, regardless of colour or location. Runs on CPU. Use this
the boxes you pass, regardless of color or location. Runs on CPU. Use this
for marks the dedicated ``visible`` engines (Gemini, Doubao) do not cover.
"""
from remove_ai_watermarks.region_eraser import erase
+1 -1
View File
@@ -81,7 +81,7 @@ def feather_weights(width: int, height: int, overlap: int) -> NDArray[Any]:
Separable linear taper over ``overlap`` pixels from every edge (capped at
half the tile so short tiles still taper symmetrically). Strictly positive
everywhere, so the normalised blend is well-defined even at an image corner
everywhere, so the normalized blend is well-defined even at an image corner
that only one tile covers.
"""
import numpy as np
+2 -2
View File
@@ -2,7 +2,7 @@
Position- and content-agnostic. You supply the rectangle(s); the eraser inpaints
whatever is inside, so it removes any visible logo / watermark / object regardless
of colour, style, or location. Localisation is the user's responsibility (pass the
of color, style, or location. Localization is the user's responsibility (pass the
box); restoration runs on CPU. This is the universal fallback for marks the
deterministic per-generator engines (Gemini sparkle, Doubao) do not cover.
@@ -151,7 +151,7 @@ def erase_lama(image_bgr: NDArray[Any], mask: NDArray[Any]) -> NDArray[Any]:
crop_mask = mask[cy0:cy1, cx0:cx1]
ch, cw = crop.shape[:2]
# Resize crop + mask to the model size, normalise to [0,1] RGB CHW.
# Resize crop + mask to the model size, normalize to [0,1] RGB CHW.
crop_rs = cv2.resize(crop, (size, size), interpolation=cv2.INTER_AREA)
mask_rs = cv2.resize(crop_mask, (size, size), interpolation=cv2.INTER_NEAREST)
img_in = cv2.cvtColor(crop_rs, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
+4 -11
View File
@@ -11,7 +11,7 @@ that mask to ONE shared, swappable fill backend (``region_eraser``: cv2 Telea/NS
MI-GAN, or big-LaMa). No mark carries a reverse-alpha step any more: the old
``original = (wm - a*logo)/(1-a)`` recovery depended on a fixed captured alpha map
at a fixed position, broke whenever a vendor re-rendered or moved its mark, and was
not colour-lossless even with the right map (it amplifies quantization/JPEG-chroma
not color-lossless even with the right map (it amplifies quantization/JPEG-chroma
error by ``1/(1-a)`` -- the "the color just changed, not removed" reports). The
localizer stays cheap (cv2/numpy, CPU) so a memory-tight caller can run it on a
small worker; the heavy fill (MI-GAN / LaMa) is opt-in and chosen by the caller.
@@ -123,11 +123,8 @@ class Candidate:
key: str
label: str
location: str
region: Region
detected_strict: bool
detected_relaxed: bool
confidence: float
features: dict[str, float] # generic; both construction sites always supply it (empty when none)
@@ -279,7 +276,7 @@ def inpaint_model_available() -> bool:
return region_eraser.migan_available() or region_eraser.lama_available()
def preferred_inpaint_backend() -> str:
def preferred_inpaint_backend() -> Literal["migan", "cv2"]:
"""Backend used by the ``auto`` fill: MI-GAN (light, droplet-friendly, the
default) when its ONNX runtime is available, else cv2. big-LaMa is NOT auto-
selected -- it is a heavier explicit opt-in via ``--backend lama`` (both models
@@ -293,7 +290,7 @@ def preferred_inpaint_backend() -> str:
def resolve_backend(backend: Backend) -> Literal["cv2", "migan", "lama"]:
"""Resolve ``auto`` to the preferred installed backend; pass the rest through."""
if backend == "auto":
return "migan" if preferred_inpaint_backend() == "migan" else "cv2"
return preferred_inpaint_backend()
return backend
@@ -494,11 +491,7 @@ def _build_candidates(image: NDArray[Any]) -> list[Candidate]:
strict = m.detect(image, provenance=False)
relaxed = m.detect(image, provenance=True)
feats = m.features(image) if (strict.detected or relaxed.detected) else {}
cands.append(
Candidate(
m.key, m.label, m.location, strict.region, strict.detected, relaxed.detected, strict.confidence, feats
)
)
cands.append(Candidate(m.key, m.label, strict.detected, relaxed.detected, feats))
return cands