mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-08-10 08:00:32 +02:00
Add the sdxl-zimage profile: the same recipe on an SDXL global stage
SdxlZImagePipeline subclasses QwenZImagePipeline and overrides only _run_global and preload, so the face stage is inherited rather than copied and cannot drift between the two profiles. A test asserts the shared methods are the same objects. Four things are architecture-bound and swap with the model: the ControlNet, the four-step distillation LoRA (SDXL-Lightning at its documented 1.0, not the reference graph's 0.8, which belongs to a different LoRA), the sampler (Euler trailing, no AuraFlow shift), and the latent grid at 8 px against Qwen's 16. Strength is architecture-bound too, which is the easy mistake and cost two wrong conclusions before it was caught. An SDXL global pass leaves SynthID at the strength Qwen needs: through the Gemini app on a native 2816x1536 original, 0.154 is FOUND while 0.20, 0.25 and 0.30 are clean. The profile therefore takes a flat vendor policy - OpenAI 0.15, Gemini 0.25, unknown following Gemini - rather than resolution_adaptive_denoise, because flat values are what was measured and no size dependence has been established for this stage. requested_steps exists because the runtimes truncate differently: DiffSynth sets sigma_start = denoising_strength and runs every requested step, while Diffusers img2img truncates the step count, so four steps at 0.15 executes zero and returns a bare VAE round-trip. Also records both measured provider boundaries for the shipped qwen-zimage curve - OpenAI detected at 0.06 and clean from 0.08, Gemini detected at 0.08 and clean from 0.10 - together with the two low-resolution Gemini verdicts that explain why the curve's sub-1 MP rungs are not under-driven despite looking short against a boundary measured at 4.33 MP. The curve is left unchanged; nothing measured fails. The profile is not deployed and not production-ready: every verdict so far comes from one fixture and one seed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a4d8de4367
commit
a9e64b9628
@@ -728,3 +728,74 @@ def test_invisible_engine_uses_qwen_zimage_step_default(tmp_image_path, tmp_path
|
||||
)
|
||||
|
||||
assert engine._remover.remove_watermark.call_args.kwargs["num_inference_steps"] == 4
|
||||
|
||||
|
||||
def test_sdxl_zimage_strength_is_vendor_adaptive_and_leaves_other_profiles_alone():
|
||||
"""An SDXL global pass needs more strength than Qwen, so it gets its own policy."""
|
||||
from remove_ai_watermarks._internal.watermark_profiles import (
|
||||
SDXL_ZIMAGE_GEMINI_STRENGTH,
|
||||
SDXL_ZIMAGE_OPENAI_STRENGTH,
|
||||
resolve_strength,
|
||||
)
|
||||
|
||||
assert resolve_strength(None, "openai", "sdxl-zimage") == pytest.approx(SDXL_ZIMAGE_OPENAI_STRENGTH)
|
||||
assert resolve_strength(None, "google", "sdxl-zimage") == pytest.approx(SDXL_ZIMAGE_GEMINI_STRENGTH)
|
||||
# Unknown provenance takes the stricter of the two.
|
||||
assert resolve_strength(None, None, "sdxl-zimage") == pytest.approx(SDXL_ZIMAGE_GEMINI_STRENGTH)
|
||||
# An explicit value still wins, and the older profiles are untouched.
|
||||
assert resolve_strength(0.4, "google", "sdxl-zimage") == pytest.approx(0.4)
|
||||
assert resolve_strength(None, "openai", "controlnet") == pytest.approx(0.10)
|
||||
assert resolve_strength(None, "google", "controlnet") == pytest.approx(0.15)
|
||||
|
||||
|
||||
def test_sdxl_zimage_shares_the_four_step_seed_and_step_contract():
|
||||
from remove_ai_watermarks._internal.watermark_profiles import (
|
||||
normalize_profile,
|
||||
resolve_seed,
|
||||
resolve_steps,
|
||||
)
|
||||
|
||||
assert normalize_profile("sdxl_zimage") == "sdxl-zimage"
|
||||
assert resolve_steps(None, "sdxl-zimage") == 4
|
||||
assert resolve_seed(None, "sdxl-zimage") == 0
|
||||
assert resolve_steps(None, "controlnet") == 50
|
||||
assert resolve_seed(None, "controlnet") is None
|
||||
|
||||
|
||||
def test_sdxl_requested_steps_compensate_for_the_diffusers_truncation():
|
||||
"""Diffusers truncates the step COUNT where DiffSynth truncates the sigma range.
|
||||
|
||||
Asking Diffusers for four steps at strength 0.15 runs int(4 * 0.15) = 0 and
|
||||
returns a bare VAE round-trip, so the request has to be scaled up instead.
|
||||
"""
|
||||
from remove_ai_watermarks._internal.sdxl_zimage_pipeline import requested_steps
|
||||
|
||||
for strength in (0.15, 0.25, 0.0896):
|
||||
steps = requested_steps(4, strength)
|
||||
assert int(steps * strength) >= 4
|
||||
# Naively asking for four would have under-spent every time, and at the
|
||||
# strengths this profile actually uses it would have run nothing at all.
|
||||
assert int(4 * strength) < 4
|
||||
assert int(4 * 0.15) == 0
|
||||
|
||||
|
||||
def test_sdxl_zimage_floors_to_its_own_latent_grid():
|
||||
"""SDXL aligns to 8 pixels where Qwen aligns to 16."""
|
||||
from remove_ai_watermarks._internal.qwen_zimage_pipeline import _target_size
|
||||
from remove_ai_watermarks._internal.sdxl_zimage_pipeline import sdxl_target_size
|
||||
|
||||
assert sdxl_target_size(1122, 1402) == (1120, 1400)
|
||||
assert _target_size(1122, 1402) == (1120, 1392)
|
||||
assert sdxl_target_size(3, 3) == (8, 8)
|
||||
|
||||
|
||||
def test_sdxl_zimage_inherits_the_face_stage_rather_than_copying_it():
|
||||
"""The face stage must not be able to diverge between the two profiles."""
|
||||
from remove_ai_watermarks._internal.qwen_zimage_pipeline import QwenZImagePipeline
|
||||
from remove_ai_watermarks._internal.sdxl_zimage_pipeline import SdxlZImagePipeline
|
||||
|
||||
assert issubclass(SdxlZImagePipeline, QwenZImagePipeline)
|
||||
for shared in ("_run_faces", "_sam_masks", "_load_zimage", "_load_sam", "run"):
|
||||
assert getattr(SdxlZImagePipeline, shared) is getattr(QwenZImagePipeline, shared)
|
||||
# Only the global stage and what it needs may differ.
|
||||
assert SdxlZImagePipeline._run_global is not QwenZImagePipeline._run_global
|
||||
|
||||
Reference in New Issue
Block a user