Add content-adaptive Google floor for chroma-zimage: face content resolves to 0.125 instead of 0.40

The four-fixture Google calibration showed a clean YuNet face-count split:
both zero-face text cards need 0.25 first-clean, both face fixtures clear
at 0.12 with zero cross-source spread. The adaptive arm routes face
content (face_count > 0) to the measured 0.125 rung, saving 6.4 dB PSNR
and doubling face identity cosine vs the flat 0.40 floor. The split is
Google-SynthID-specific: OpenAI's face fixture was harder than its text
fixture, so no other cohort gets an adaptive arm.
This commit is contained in:
Victor Kuznetsov committed 2026-08-30 17:50:56 -07:00
1 parent fa4748a6a8
commit 1d0e6b54dd
6 files changed
+90 -14

No files matched your search

+4 -2
View File
@@ -287,8 +287,10 @@ Both need the seed-sensitivity and face-stage composition checks called out
above before shipping. Both checks have now been run (Pre-ship validation
above): the Meta boundary is seed-stable across seeds 0-2 and the full
profile path stays clean through the face stage on the shipped Google floor.
The profile ships with option 1's floors; option 2's adaptive policy remains
the documented follow-up.
The profile ships with option 1's floors plus a content-adaptive Google arm
(face content resolves to 0.125 instead of 0.40, keyed on YuNet detection).
Option 2's full adaptive policy for the remaining cohorts remains the
documented follow-up.
## Oracle session notes
+5 -3
View File
@@ -215,9 +215,11 @@ established for that stage.
- OpenAI: `0.09` (below qwen's `0.07675` is false economy neither way -- the
matched-strength fidelity favors Chroma1 on this cohort);
- Microsoft InvisMark: `0.125` (below qwen's `0.15`);
- Google: `0.40` (above qwen's `0.27`; at this floor the regeneration destroys
dense text and collapses face identity -- the tradeoff that motivates the
documented content-adaptive follow-up);
- Google: `0.40` for zero-face content (above qwen's `0.27`; at this floor the
regeneration destroys dense text and collapses face identity -- the tradeoff
that motivates the adaptive arm below), or `0.125` when YuNet detects at
least one face (both measured face fixtures clear at 0.12 with zero
cross-source spread; oracle-verified 2026-08-30);
- Meta Content Seal: `0.17` (above qwen's `0.1`);
- unknown: `0.40`, following the strictest measured cohort.
+11 -7
View File
@@ -1174,13 +1174,17 @@ conditioning: the floors were measured on a plain strength pass.
The flat vendor floors (`CHROMA_ZIMAGE_*_STRENGTH` in watermark_profiles):
OpenAI 0.09 and Microsoft 0.125 (both BELOW qwen's, with better
matched-strength fidelity), Google 0.40 and Meta 0.17 (both ABOVE qwen's,
because Chroma1's per-fixture boundaries scatter wider). The
matched-strength addendum in the research doc is the honest read: at the
strength each image actually needs, Chroma1 regenerates better almost
everywhere except face identity (which the inherited Z-Image face stage
supplies); at the flat worst-case floors it destroys dense text and face
identity. A content-adaptive strength policy is the documented follow-up.
matched-strength fidelity), Google 0.40 for zero-face content / 0.125 when
faces are detected (a content-adaptive arm from the clean face-count split
in the calibration: both text cards need 0.25, both face fixtures clear at
0.12, so the YuNet detector -- already loaded for the face stage -- routes
the operating point), and Meta 0.17 (ABOVE qwen's, because Chroma1's
per-fixture boundaries scatter wider). The matched-strength addendum in the
research doc is the honest read: at the strength each image actually needs,
Chroma1 regenerates better almost everywhere except face identity (which the
inherited Z-Image face stage supplies); at the flat worst-case floors it
destroys dense text and face identity. The Google face-content arm is the
first shipped piece of the content-adaptive policy.
### SDXL plus Z-Image
@@ -153,6 +153,19 @@ CHROMA_ZIMAGE_GOOGLE_STRENGTH = 0.40
CHROMA_ZIMAGE_META_STRENGTH = 0.17
CHROMA_ZIMAGE_UNKNOWN_STRENGTH = CHROMA_ZIMAGE_GOOGLE_STRENGTH
# Content-adaptive Google floor: the four-fixture calibration showed a clean
# face-count split. Both zero-face fixtures (dense-text cards 633uuy and akdbei)
# need 0.25 first-clean; both face fixtures (y48j3c with 17 detected faces and
# 3mc4t9 with 7) clear at 0.12. The flat 0.40 floor is the zero-face policy
# (worst 0.25 + spread 0.13 = 0.38, rounded to 0.40). Face content can use the
# measured 0.125 rung instead: both face fixtures first-cleaned at 0.12, and
# with identical boundaries the cross-source spread is zero, so 0.125 (the next
# measured rung above 0.12) is the operating point. This split is
# Google-SynthID-specific: OpenAI's face fixture was HARDER than its text
# fixture (0.075 vs 0.06), so no other cohort gets an adaptive arm.
# Oracle-verified 2026-08-30; see docs/chroma1-engine-research.md.
CHROMA_ZIMAGE_GOOGLE_FACE_STRENGTH = 0.125
_CHROMA_ZIMAGE_STRENGTH_BY_VENDOR: dict[str, float] = {
"openai": CHROMA_ZIMAGE_OPENAI_STRENGTH,
"google": CHROMA_ZIMAGE_GOOGLE_STRENGTH,
@@ -204,6 +217,7 @@ def resolve_strength(
pipeline: str | None = None,
*,
size: tuple[int, int] | None = None,
face_count: int | None = None,
) -> float:
"""Resolve a user override or the calibrated policy for a profile and vendor.
@@ -222,7 +236,10 @@ def resolve_strength(
if normalized == SDXL_ZIMAGE_PROFILE:
return _SDXL_ZIMAGE_STRENGTH_BY_VENDOR.get((vendor or "").casefold(), SDXL_ZIMAGE_UNKNOWN_STRENGTH)
if normalized == CHROMA_ZIMAGE_PROFILE:
return _CHROMA_ZIMAGE_STRENGTH_BY_VENDOR.get((vendor or "").casefold(), CHROMA_ZIMAGE_UNKNOWN_STRENGTH)
vendor_key = (vendor or "").casefold()
if vendor_key == "google" and face_count is not None and face_count > 0:
return CHROMA_ZIMAGE_GOOGLE_FACE_STRENGTH
return _CHROMA_ZIMAGE_STRENGTH_BY_VENDOR.get(vendor_key, CHROMA_ZIMAGE_UNKNOWN_STRENGTH)
if size is None:
raise ValueError("qwen-zimage resolves strength from image area, so size is required")
vendor_strength = _QWEN_ZIMAGE_FLAT_STRENGTH_BY_VENDOR.get((vendor or "").casefold())
@@ -188,6 +188,26 @@ class WatermarkRemover:
remove_ai_metadata(output_path, output_path, keep_standard=True)
def _resolve_chroma_strength(
self,
strength: float | None,
vendor: str | None,
source: Image.Image,
) -> float:
"""Resolve strength, running face detection for the chroma Google arm.
The content-adaptive Google floor needs a face count. YuNet is fast
(~50 ms) and the model download is already managed by the shared base.
"""
if self.model_profile != CHROMA_ZIMAGE_PROFILE or (vendor or "").casefold() != "google":
return resolve_strength(strength, vendor, self.model_profile, size=source.size)
if strength is not None:
return strength
from remove_ai_watermarks._internal.two_stage_pipeline import detect_faces
face_count = len(detect_faces(source))
return resolve_strength(strength, vendor, self.model_profile, size=source.size, face_count=face_count)
def remove_watermark(
self,
image_path: Path,
@@ -213,7 +233,7 @@ class WatermarkRemover:
with Image.open(image_path) as opened:
source = opened.convert("RGB")
resolved_strength = resolve_strength(strength, vendor, self.model_profile, size=source.size)
resolved_strength = self._resolve_chroma_strength(strength, vendor, source)
if not 0.0 <= resolved_strength <= 1.0:
raise ValueError(f"Strength must be between 0.0 and 1.0, got {resolved_strength}")
if text_manifest is not None and self.model_profile in (SDXL_ZIMAGE_PROFILE, CHROMA_ZIMAGE_PROFILE):
+31
View File
@@ -1204,3 +1204,34 @@ def test_chroma_vae_roundtrip_raises_not_implemented():
pipeline = ChromaZImagePipeline(device="cuda", torch_dtype=torch.bfloat16)
with pytest.raises(NotImplementedError, match="does not expose a VAE donor"):
pipeline._vae_roundtrip(Image.new("RGB", (32, 32)))
def test_chroma_google_face_content_gets_the_lower_adaptive_floor():
"""Face content on Google SynthID clears at 0.125, not 0.40.
The four-fixture calibration showed a clean face-count split: both zero-face
text cards need 0.25, both face fixtures clear at 0.12. The adaptive arm
saves face identity (0.611 vs 0.279 cosine) and 6.4 dB PSNR on face content.
"""
from remove_ai_watermarks._internal.watermark_profiles import (
CHROMA_ZIMAGE_GOOGLE_FACE_STRENGTH,
CHROMA_ZIMAGE_GOOGLE_STRENGTH,
resolve_strength,
)
assert pytest.approx(0.125) == CHROMA_ZIMAGE_GOOGLE_FACE_STRENGTH
# Face content gets the lower adaptive floor.
assert resolve_strength(None, "google", "chroma-zimage", face_count=17) == pytest.approx(0.125)
assert resolve_strength(None, "google", "chroma-zimage", face_count=1) == pytest.approx(0.125)
# Zero-face content gets the flat floor (text/graphic cohort).
assert resolve_strength(None, "google", "chroma-zimage", face_count=0) == pytest.approx(0.40)
assert pytest.approx(0.40) == CHROMA_ZIMAGE_GOOGLE_STRENGTH
# No face_count hint: conservative (flat floor).
assert resolve_strength(None, "google", "chroma-zimage") == pytest.approx(0.40)
# Other vendors ignore face_count: the split is Google-SynthID-specific.
assert resolve_strength(None, "openai", "chroma-zimage", face_count=5) == pytest.approx(0.09)
assert resolve_strength(None, "meta", "chroma-zimage", face_count=5) == pytest.approx(0.17)
# An explicit strength still wins over the adaptive arm.
assert resolve_strength(0.20, "google", "chroma-zimage", face_count=17) == pytest.approx(0.20)
# Other profiles are untouched by the face_count parameter.
assert resolve_strength(None, "google", "qwen-zimage", size=(2000, 1850), face_count=17) == pytest.approx(0.27)