fix(visible): safe-inpaint pill gate, cut metadata-only false fires

Verified 0.13.0 pill removal on a 32k real-upload corpus. The metadata-OR-wordmark
gate was only ~1/3 precise: TC260 metadata confirms Jimeng-class provenance, not pill
presence, so the weak edge-NCC detector's false fires (textured ceilings/walls, where
inpaint visibly smears) were admitted whenever metadata was present.

Split into two arms (_keep_pill): the reliable bottom-right wordmark (~94% precise,
survives metadata stripping) removes the pill unrestricted; the metadata-only arm
removes it ONLY when the top-left footprint is flat enough for an invisible inpaint
(PillEngine.footprint_is_flat, median-Sobel <= _FLAT_TEXTURE_MAX). Keeps real
flat-scene pills and harmless flat false fires; leaves the damaging textured false
fires untouched. Corpus: 270 -> 118 removals, ~90 true preserved, damaging FP -> ~0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Victor Kuznetsov
2026-07-07 11:26:28 +03:00
co-authored by Claude Opus 4.8
parent 0e5a4cbc54
commit a8fd02a8f7
6 changed files with 128 additions and 26 deletions
+6 -4
View File
@@ -340,10 +340,12 @@ def _warn_if_esrgan_unavailable(upscaler: str) -> None:
def _aigc_metadata_present(path: Path) -> bool:
"""True when the file carries a China-AIGC (TC260) metadata label. Used to gate
the weak-detector 'AI生成' pill: metadata confirms Jimeng-class provenance. NB
this is only ONE of two confirmations -- ``remove_auto_marks`` also accepts the
bottom-right wordmark, so a metadata-STRIPPED upload can still be handled."""
"""True when the file carries a China-AIGC (TC260) metadata label. Feeds the
weak-detector 'AI生成' pill gate (``remove_auto_marks`` → ``_keep_pill``): metadata
confirms Jimeng-class provenance but not pill presence, so the metadata-only arm
removes the pill ONLY on a flat, safe-to-inpaint footprint. The reliable
bottom-right wordmark is the other, unrestricted confirmation arm (and it survives
a metadata-STRIPPED upload)."""
with contextlib.suppress(Exception):
from remove_ai_watermarks import metadata
+49 -4
View File
@@ -51,6 +51,19 @@ _DETECT_THRESHOLD = 0.22 # edge-NCC gate, corpus-calibrated
_MASK_X0, _MASK_Y0 = 0.012, 0.006 # x0 of W, y0 of H
_MASK_W, _MASK_H = 0.205, 0.115 # width of W, height of W
# Background-flatness gate for the metadata-only pill arm (see remove_auto_marks).
# The pill detector is weak (~7% raw false-fire); metadata confirms the platform,
# not pill presence, so its false fires are real Jimeng-class content WITHOUT a pill.
# Those false fires cluster on TEXTURED top-left corners (ceiling fixtures, structure)
# where inpaint visibly SMEARS, while real pills and harmless false fires sit on FLAT
# corners (sky / wall / solid) where inpaint is invisible. So the metadata-only arm
# removes the pill only when the footprint background is flat enough for a safe,
# invisible inpaint. Threshold = median Sobel magnitude over the footprint box at a
# normalized width; corpus-validated on 32k real uploads 2026-07 (real pills median
# ~3.2, textured-ceiling false fires median ~8+). The reliable bottom-right wordmark
# arm is NOT texture-gated -- a wordmark-confirmed pill is removed regardless.
_FLAT_TEXTURE_MAX = 6.0
_silhouette: NDArray[Any] | None = None
@@ -104,6 +117,38 @@ class PillEngine:
score, box = m
return PillDetection(score >= _DETECT_THRESHOLD, score, box)
def _footprint_box(self, image: NDArray[Any]) -> tuple[int, int, int, int] | None:
h, w = image.shape[:2]
x0, y0 = int(_MASK_X0 * w), int(_MASK_Y0 * h)
x1, y1 = min(w, x0 + int(_MASK_W * w)), min(h, y0 + int(_MASK_H * w))
if x1 <= x0 or y1 <= y0:
return None
return x0, y0, x1, y1
def footprint_texture(self, image: NDArray[Any]) -> float:
"""Median gradient magnitude over the fixed top-left footprint box at a
normalized width. A robust flatness proxy: low = flat (sky / wall / solid,
inpaint invisible), high = textured (ceiling fixtures / structure, inpaint
smears). Median (not mean) so the pill's own edges -- a minority of the box --
do not inflate it. Backs the metadata-only arm's safe-inpaint gate."""
if image is None or image.size == 0:
return 0.0
box = self._footprint_box(image)
if box is None:
return 0.0
x0, y0, x1, y1 = box
crop = image[y0:y1, x0:x1]
gray = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY) if crop.ndim == 3 else crop
tw = 220
gray = cv2.resize(gray, (tw, max(1, int(gray.shape[0] * tw / gray.shape[1])))).astype(np.float32)
gx = cv2.Sobel(gray, cv2.CV_32F, 1, 0, ksize=3)
gy = cv2.Sobel(gray, cv2.CV_32F, 0, 1, ksize=3)
return float(np.median(cv2.magnitude(gx, gy)))
def footprint_is_flat(self, image: NDArray[Any], *, thresh: float = _FLAT_TEXTURE_MAX) -> bool:
"""True when the top-left footprint is flat enough for an invisible inpaint."""
return self.footprint_texture(image) <= thresh
def footprint_mask(self, image: NDArray[Any], *, force: bool = False) -> NDArray[Any] | None:
"""Full-frame uint8 mask (255 = pill) over the pill's known top-left region.
@@ -116,11 +161,11 @@ class PillEngine:
fixed regardless)."""
if image is None or image.size == 0:
return None
h, w = image.shape[:2]
x0, y0 = int(_MASK_X0 * w), int(_MASK_Y0 * h)
x1, y1 = min(w, x0 + int(_MASK_W * w)), min(h, y0 + int(_MASK_H * w))
if x1 <= x0 or y1 <= y0:
box = self._footprint_box(image)
if box is None:
return None
x0, y0, x1, y1 = box
h, w = image.shape[:2]
mask = np.zeros((h, w), np.uint8)
mask[y0:y1, x0:x1] = 255
return mask
+29 -8
View File
@@ -344,6 +344,31 @@ def best_auto_mark(image: NDArray[Any]) -> MarkDetection | None:
return max(fired, key=lambda d: d.confidence) if fired else None
def _keep_pill(image: NDArray[Any], keys: set[str], *, pill_metadata: bool) -> bool:
"""Whether to auto-remove the capture-less 'AI生成' pill given the fired marks.
The pill detector is weak (~7% raw false-fire) and metadata confirms the platform,
not pill presence, so a naive metadata-OR-wordmark gate over-fires: on a 32k
real-upload corpus (2026-07) the metadata-only arm was only ~27% precise and its
false fires were textured ceilings/walls that inpaint visibly SMEARS. Two arms:
* bottom-right "★ 即梦AI" wordmark fired -> ~94% precise, and it survives
metadata-STRIPPED uploads: remove the pill unrestricted;
* metadata only (TC260 AIGC, no wordmark) -> remove ONLY when the top-left
footprint is flat enough for an invisible inpaint (``footprint_is_flat``),
so real flat-scene pills (and harmless flat false fires) are cleaned while
the damaging textured false fires are left untouched.
A Doubao image is TC260 too but is not Jimeng-basic, so the pill never rides on a
Doubao detection. No confirmation at all -> never remove (blocks false fires on
non-Jimeng content)."""
if "doubao" in keys:
return False
if "jimeng" in keys:
return True
if pill_metadata:
return bool(_engine("jimeng_pill").footprint_is_flat(image))
return False
def remove_auto_marks(
image: NDArray[Any],
*,
@@ -362,16 +387,12 @@ def remove_auto_marks(
its own corner on the progressively-cleaned image, so order does not matter.
The capture-less ``jimeng_pill`` has a weak edge-NCC detector (~7% raw
false-fire), so it is kept only when the image is CONFIRMED Jimeng and NOT
Doubao. Confirmation is ``pill_metadata`` (the China-AIGC / TC260 metadata
signal, supplied by the caller) OR the reliable bottom-right wordmark firing --
the wordmark keeps recall on metadata-STRIPPED uploads (screenshots / re-saved
files) that the metadata gate alone would miss. Returns ``(result, [labels
removed])``; an empty list means nothing fired."""
false-fire), so its removal is gated by ``_keep_pill`` (Jimeng-class confirmation
+ a safe-inpaint check on the metadata-only arm; see that helper). Returns
``(result, [labels removed])``; an empty list means nothing fired."""
fired = [d for d in detect_marks(image, include_explicit=False) if d.detected]
keys = {d.key for d in fired}
jimeng_confirmed = pill_metadata or "jimeng" in keys
if "jimeng_pill" in keys and (not jimeng_confirmed or "doubao" in keys):
if "jimeng_pill" in keys and not _keep_pill(image, keys, pill_metadata=pill_metadata):
fired = [d for d in fired if d.key != "jimeng_pill"]
result = image
for det in fired: