Skip to content

Do not watermark or postprocess SDXL latent output - #14678

Open
Devam0311 wants to merge 1 commit into
huggingface:mainfrom
Devam0311:sdxl-latent-watermark
Open

Do not watermark or postprocess SDXL latent output#14678
Devam0311 wants to merge 1 commit into
huggingface:mainfrom
Devam0311:sdxl-latent-watermark

Conversation

@Devam0311

@Devam0311 Devam0311 commented Sep 1, 2026

Copy link
Copy Markdown

Issue link: this addresses Issue 4 of #13610.
I have deliberately not used a Fixes keyword: #13610 tracks 7 separate findings, so a closing keyword would close it as soon as this one merges while the others are still open. Happy for the no-issue-needed label to be applied, or I can add a closing keyword if you would rather the review issue be closed and reopened.

What does this PR do?

Fixes Issue 4 of #13610 (stable_diffusion_xl model/pipeline review).

With output_type="latent" the SDXL img2img pipeline sets image = latents and then runs the watermarker and image_processor.postprocess() over it, so raw latents are handled as if they were decoded RGB.

The text-to-image pipeline already guards both:

# pipeline_stable_diffusion_xl.py
if not output_type == "latent":
    if self.watermark is not None:
        image = self.watermark.apply_watermark(image)
    image = self.image_processor.postprocess(image, output_type=output_type)

With a real watermarker this can corrupt or fail on larger latent tensors; with any custom watermarker it is simply called with the wrong data.

Solution

Applies the text-to-image guard to the sites that lack it.

The issue names two. I swept the codebase for the same unguarded shape and found one more — pipeline_pag_sd_xl_img2img.py — so all three are fixed together:

  • pipelines/stable_diffusion_xl/pipeline_stable_diffusion_xl_img2img.py
  • modular_pipelines/stable_diffusion_xl/decoders.py
  • pipelines/pag/pipeline_pag_sd_xl_img2img.py (found by sweep, not listed in the issue)

The SDXL inpaint pipeline was checked and already guards correctly.

Testing

test_latent_output_skips_watermark_and_postprocess installs a watermarker that raises if called, requests output_type="latent", and asserts the result is still a latent tensor — checking the channel count, since postprocess would have collapsed it to 3 channels.

Verified it catches the bug: with the fix reverted it fails.

pytest tests/pipelines/stable_diffusion_xl/test_stable_diffusion_xl_img2img.py -k "not ip_adapter"
  -> 45 passed, 28 skipped
pytest .../test_stable_diffusion_xl_img2img.py .../test_stable_diffusion_xl.py -k "not ip_adapter"
  -> 110 passed, 65 skipped
ruff check / ruff format --check   -> clean
git diff --check                   -> clean

The TestStableDiffusionXL*PipelineIPAdapter classes are deselected above: they fail on a clean checkout of main in my environment with a ModuleNotFoundError from an optional dependency I do not have installed, unrelated to this change.

Before submitting

Self-review notes

Reviewed against .ai/references/review-rules.md and pipelines.md. No blocking issues.

For the reviewer:

  1. This changes what output_type="latent" returns in these pipelines — previously postprocessed, now raw latents. That is the documented and intended behaviour and matches text-to-image, but it is a behaviour change for anyone who was relying on the postprocessed result.
  2. I included one file beyond the issue's list (pipeline_pag_sd_xl_img2img.py), found by sweeping for the identical unguarded pattern. Happy to drop it if you'd rather keep this to the two files named.
  3. The modular decoder uses hasattr(components, "watermark"); I left that guard as-is and only wrapped it in the output_type check.
  4. Scope kept to Issue 4. Issue 2 of the same review is in Use negative crop coordinates when building SDXL negative time ids #14676.

Who can review?

@yiyixuxu @asomoza @hlky

With `output_type="latent"` the SDXL img2img pipeline sets `image = latents`
but still called `watermark.apply_watermark()` and
`image_processor.postprocess()` on it, so raw latents were treated as
decoded RGB. The text-to-image pipeline already guards both behind
`output_type != "latent"`.

Apply the same guard in the img2img pipeline, the PAG img2img variant that
shares the pattern, and the modular SDXL decoder step.

Add a regression test that installs a watermarker which fails if called and
asserts the latent output keeps its latent channel count.

Ref huggingface#13610 (Issue 4)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XBYeq5vB4DNZDEaqUsroKR
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Hi @Devam0311, thanks for the PR! It does not appear to link an issue it fixes. If this PR addresses an existing issue, please add a closing keyword (e.g. Fixes #1234) to the PR description so the issue is linked. See the contribution guide for more details. If this PR intentionally does not fix a tracked issue, a maintainer can add the no-issue-needed label to silence this reminder.

Please note that PRs without a linked issue are likely to be automatically closed 10 days after this notice.

Once the PR links an issue (or gets the no-issue-needed label), you can ignore this message — it stays here as a comment, but it no longer applies.

@adi-IL adi-IL left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally on Linux x86_64.

Before this change, running output_type='latent' on StableDiffusionXLImg2ImgPipeline, PAG img2img, and the modular SDXL decoder step passed raw 4-channel latents through RGB watermarking and image_processor.postprocess().

Guarding both behind output_type != 'latent' matches the behavior in the base text-to-image SDXL pipeline and preserves the latent dimensions. The new test asserting watermark bypass passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants