fix(invisible): ctrlregen defaults to clean-noise strength, not the SDXL 0.10

The ctrlregen profile inherited the SDXL img2img --strength default (0.10), a
near-identity pass that loaded ControlNet + DINOv2-giant and barely changed the
image -- a no-op for removal. resolve_strength() now resolves an unset strength
per profile: 0.10 for the SDXL default, CTRLREGEN_DEFAULT_STRENGTH (1.0,
clean-noise) for ctrlregen. It checks `is None` rather than falsiness, so an
explicit 0.0 is respected (the old `strength or DEFAULT` swallowed it).

Research basis: CtrlRegen (ICLR 2025, arXiv:2410.05470) removes robust
watermarks by regenerating from clean Gaussian noise; partial-noise img2img
retains watermark info that diffuses back, so a high (clean-noise) strength is
the lever, not a knob on the light SDXL pass. CLI wiring (--strength default
None) lands with the cli refactor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Victor Kuznetsov
2026-05-31 15:07:19 -07:00
parent 33bd401e2a
commit 2d49c3cb58
5 changed files with 66 additions and 7 deletions
+26
View File
@@ -13,8 +13,11 @@ import pytest
from remove_ai_watermarks.noai.progress import is_mps_error
from remove_ai_watermarks.noai.utils import get_image_format, is_supported_format
from remove_ai_watermarks.noai.watermark_profiles import (
CTRLREGEN_DEFAULT_STRENGTH,
DEFAULT_STRENGTH,
detect_model_profile,
get_model_id_for_profile,
resolve_strength,
)
from remove_ai_watermarks.noai.watermark_remover import get_device, is_watermark_removal_available
@@ -121,6 +124,29 @@ class TestModelProfiles:
assert detect_model_profile("yepengliu/ctrlregen") == "ctrlregen"
class TestResolveStrength:
"""resolve_strength applies the profile default only when strength is unset."""
def test_none_default_profile_uses_sdxl_default(self):
assert resolve_strength(None, "default") == DEFAULT_STRENGTH
def test_none_ctrlregen_uses_clean_noise_default(self):
# ctrlregen must NOT inherit the light SDXL 0.10 (that makes it a no-op);
# clean-noise regeneration is the lever against robust marks.
assert resolve_strength(None, "ctrlregen") == CTRLREGEN_DEFAULT_STRENGTH
assert CTRLREGEN_DEFAULT_STRENGTH > DEFAULT_STRENGTH
def test_explicit_value_overrides_both_profiles(self):
assert resolve_strength(0.3, "default") == 0.3
assert resolve_strength(0.3, "ctrlregen") == 0.3
def test_explicit_zero_is_respected_not_treated_as_unset(self):
# 0.0 is falsy but explicit -- must not fall through to the profile default
# (the old `strength or DEFAULT` bug would have). Range validation lives in
# remove_watermark, not here.
assert resolve_strength(0.0, "ctrlregen") == 0.0
# ── Format utilities ────────────────────────────────────────────────