Files
remove-ai-watermarks/tests/test_baidu_engine.py
T
Victor KuznetsovandClaude Opus 5 78d9e81d0f Collapse the duplicated detection path and lift the image pipeline into the library
The visible-mark path had grown three copies of one ladder sweep, four
near-identical `detect` arms, and four hand-rolled `footprint_mask` overrides;
mark knowledge sat in five hand-maintained tables across three modules; and the
flagship `all`/`batch` pipeline existed only in cli.py, written twice with
divergent behavior.

Detection is now one measurement. `_ladder_best` replaces the three sweeps,
`_scan`/`_verdict` replace the four arms, and the winning box travels to the
mask on `TextMarkDetection.match_box` instead of being swept a second time.
`detect_both` returns the strict and relaxed verdicts from one scan, which
halves the arbiter's perception cost (260 -> 130 matchTemplate calls on a 2048²
image, verdicts identical field for field). A per-mark demotion goes in the new
`_post_gate` hook, never in a `detect` override -- an override is invisible to
the single-pass path, which is how the RunningHub and Yuanbao anchor gates
briefly stopped applying.

Everything about a mark is now one registry row: product, label regime, the
platform sentence `identify` reports, the metadata signals that confirm it, and
its TC260 producer codes. `identify._VISIBLE_MARK_PLATFORM`, the signal mapping
in `api.visible_provenance`, `_PRODUCT_OF` and the pill veto are derived from
those rows.

`api.remove_all` / `api.remove_batch` are the library form of the `all` and
`batch` commands; the CLI is a wrapper that owns console text and exit codes.
Progress is a `(stage, detail)` pair of stable tokens, so the CLI keys its
wording off structure rather than parsing the library's prose back.

Two intentional behavior changes, both verified against a recorded 811-image
sample of detector verdicts, removal-mask hashes, arbiter decisions and
`identify` reports:

  * A TC260 label now relaxes the vendor its `ContentProducer` names rather than
    ByteDance's pair on every China-AIGC image. 333 of 811 samples move; on 185
    of them the previously relaxed pair was simply the wrong vendor, and the
    mark actually present never reached the relaxed gate its own
    `provenance_ncc_factor` was calibrated for.
  * A confident LibLibAI detection suppresses the Jimeng pill, like every other
    TC260 product's mark. It was registered alongside RunningHub and Baidu, both
    of which were added to the hand-written veto list, and it was not. 1 sample
    moves, and it is exactly the co-firing case.

Nothing else in that record changes: detector verdicts, mask hashes and
`identify` verdicts are byte-identical, and all 200 calibration constants are
untouched.

Also: `aigc_label` and friends plus `extract_c2pa_info` are memoized on
(path, mtime_ns, size) -- size because this package rewrites in place; the
native TC260 container readers route on magic bytes instead of the file
extension, so a mislabeled AVI or FLV is no longer invisible; `identify` shares
one pixel decode between the DWT-DCT and visible stages (TrustMark keeps its own
Pillow decode, which is not substitutable); and the six `stabilize_*` video
wrappers collapse into one policy table.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 22:49:45 -07:00

145 lines
5.8 KiB
Python

"""Tests for the Baidu ("百度 AI生成") visible-watermark engine.
Every tuned constant in ``baidu_engine`` was measured on the 16-frame vendor
cohort (2026-07-22); these tests pin the load-bearing ones: detection keys on
the 百度 text run ONLY (the text+pill template was a measured bright-blob
magnet), the load-bearing Doubao rival margin, the strict-only gate, and the
corner-extended footprint (the tag's flat white interior gives no top-hat
response, so a blob-bbox mask leaves the tag as a ghost).
"""
from __future__ import annotations
import cv2
import numpy as np
from remove_ai_watermarks import watermark_registry as registry
from remove_ai_watermarks.baidu_engine import (
_ALPHA_HEIGHT_FRAC,
_ALPHA_WIDTH_FRAC,
BaiduEngine,
_alpha_template,
)
_TEXT_FRAC = 0.090 # measured 百度 text-run width, fraction of the short side
_TEXT_RIGHT = 0.099 # measured right margin of the text run (the tag is right of it)
_TAG_FRAC = 0.075 # the white tag's width, approx (text-right to corner)
def _compose(w: int, h: int, bg: float = 100.0):
"""Composite the 百度 text run + a solid white tag at the measured layout."""
img = np.full((h, w, 3), bg, np.float32)
at = _alpha_template()
short = min(w, h)
gw = int(_TEXT_FRAC * short)
gh = max(4, int(_TEXT_FRAC * (_ALPHA_HEIGHT_FRAC / _ALPHA_WIDTH_FRAC) * short))
margin_b = int(0.006 * short)
ax = w - int(_TEXT_RIGHT * short) - gw
ay = h - margin_b - gh
amap = np.zeros((h, w), np.float32)
amap[ay : ay + gh, ax : ax + gw] = cv2.resize(at, (gw, gh))
# the white rounded tag between the text and the corner
tx0 = w - int(0.015 * short) - int(_TAG_FRAC * short)
amap[ay - gh // 8 : ay + gh + gh // 8, tx0 : w - int(0.015 * short)] = 1.0
a3 = amap[:, :, None]
wm = (a3 * 255.0 + (1 - a3) * img).clip(0, 255).astype(np.uint8)
return wm, (ax, ay, gw, gh, tx0)
class TestLocate:
def test_box_anchored_bottom_right(self):
eng = BaiduEngine()
img = np.zeros((2048, 2048, 3), np.uint8)
loc = eng.locate(img)
assert 2048 - (loc.x + loc.w) < 40
assert 2048 - (loc.y + loc.h) < 40
def test_box_scales_with_short_side(self):
eng = BaiduEngine()
landscape = eng.locate(np.zeros((640, 1280, 3), np.uint8))
wider = eng.locate(np.zeros((640, 2560, 3), np.uint8))
assert wider.w == landscape.w
class TestConfig:
def test_tophat_frontend(self):
assert BaiduEngine().config.detect_frontend == "tophat"
def test_doubao_rival_margin(self):
# 百度 vs 豆包 share a glyph and a corner: the candidate fires on 45.8% of
# Doubao-marked frames at the gate, and the 0.10 margin suppresses ALL of
# it at zero genuine-detection cost (crossfire, 2026-07-22).
assert "doubao_alpha.png" in BaiduEngine().config.rivals
def test_strict_only_no_provenance_relaxation(self):
assert BaiduEngine().config.provenance_ncc_factor == 1.0
def test_gate_above_clean_arm_max(self):
# Compatibility testing separated true Baidu marks from unrelated
# bottom-right text, so the gate sits at 0.48.
assert BaiduEngine().config.detect_ncc_threshold >= 0.48
def test_qwen_is_a_rival(self):
# 百度 and 千问 are near-identical after binarization, so Qwen's template
# must act as a rival.
assert "qwen_alpha.png" in BaiduEngine().config.rivals
def test_registry_row(self):
mark = registry.get_mark("baidu")
assert mark.location == "bottom-right"
assert mark.in_auto
class TestDetectAndMask:
def test_detects_composed_mark(self):
eng = BaiduEngine()
wm, _ = _compose(1024, 1024)
det = eng.detect(wm)
assert det.detected, f"composed mark missed (conf={det.confidence:.3f})"
def test_clean_frame_stays_quiet(self):
eng = BaiduEngine()
img = np.full((1024, 1024, 3), 100, np.uint8)
assert not eng.detect(img).detected
def test_mask_extends_to_the_corner_tag(self):
"""Regression (2026-07-22): the tag's flat white interior gives no top-hat
response, so a blob-bbox mask ended at the text run and the fill left the
tag as a ghost. The footprint must extend right to the corner."""
eng = BaiduEngine()
wm, (ax, _ay, gw, _gh, tx0) = _compose(1024, 1024)
mask = eng.footprint_mask(wm)
assert mask is not None
_ys, xs = np.where(mask > 0)
assert xs.min() <= ax + int(0.1 * gw) # covers the text run's left edge
assert xs.max() >= tx0 + 10 # covers the white tag right of the text
def test_no_mask_on_clean_frame(self):
eng = BaiduEngine()
img = np.full((1024, 1024, 3), 100, np.uint8)
assert eng.footprint_mask(img) is None
def test_force_masks_the_whole_locate_box_on_a_clean_frame(self):
"""``force`` takes priority over detection for this mark, unlike the base
policy: a --no-detect caller named the mark, so the honest footprint is the
whole geometry box even though nothing was detected."""
eng = BaiduEngine()
img = np.full((1024, 1024, 3), 100, np.uint8)
mask = eng.footprint_mask(img, force=True)
assert mask is not None
bx, by, bw, bh = eng.locate(img).bbox
ys, xs = np.where(mask > 0)
assert xs.min() <= bx
assert xs.max() >= bx + bw - 1
assert ys.min() <= by
assert ys.max() >= by + bh - 1
class TestPillInteraction:
def test_confident_baidu_detection_suppresses_the_jimeng_pill(self):
# A Baidu image is TC260 too but is not Jimeng-basic: like Doubao/Qwen/
# Kling, a confident Baidu detection must veto the pill (``_keep_pill``).
from remove_ai_watermarks.watermark_registry import _keep_pill
assert not _keep_pill({"baidu"}, provenance=frozenset({"jimeng"}), footprint_flat=1.0)