Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions src/diffusers/modular_pipelines/stable_diffusion_xl/decoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,14 @@ def __call__(self, components, state: PipelineState) -> PipelineState:
else:
block_state.images = block_state.latents

# apply watermark if available
if hasattr(components, "watermark") and components.watermark is not None:
block_state.images = components.watermark.apply_watermark(block_state.images)
if not block_state.output_type == "latent":
# apply watermark if available
if hasattr(components, "watermark") and components.watermark is not None:
block_state.images = components.watermark.apply_watermark(block_state.images)

block_state.images = components.image_processor.postprocess(
block_state.images, output_type=block_state.output_type
)
block_state.images = components.image_processor.postprocess(
block_state.images, output_type=block_state.output_type
)

self.set_block_state(state, block_state)

Expand Down
9 changes: 5 additions & 4 deletions src/diffusers/pipelines/pag/pipeline_pag_sd_xl_img2img.py
Original file line number Diff line number Diff line change
Expand Up @@ -1521,11 +1521,12 @@ def denoising_value_valid(dnv):
else:
image = latents

# apply watermark if available
if self.watermark is not None:
image = self.watermark.apply_watermark(image)
if not output_type == "latent":
# apply watermark if available
if self.watermark is not None:
image = self.watermark.apply_watermark(image)

image = self.image_processor.postprocess(image, output_type=output_type)
image = self.image_processor.postprocess(image, output_type=output_type)

# Offload all models
self.maybe_free_model_hooks()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1477,11 +1477,12 @@ def denoising_value_valid(dnv):
else:
image = latents

# apply watermark if available
if self.watermark is not None:
image = self.watermark.apply_watermark(image)
if not output_type == "latent":
# apply watermark if available
if self.watermark is not None:
image = self.watermark.apply_watermark(image)

image = self.image_processor.postprocess(image, output_type=output_type)
image = self.image_processor.postprocess(image, output_type=output_type)

# Offload all models
self.maybe_free_model_hooks()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,25 @@ def test_stable_diffusion_xl_img2img_negative_conditions(self):

assert (image_slice_with_no_neg_conditions - image_slice_with_neg_conditions).abs().max() > 1e-4

def test_latent_output_skips_watermark_and_postprocess(self):
# Regression: `output_type="latent"` sets `image = latents`, but this pipeline still ran the
# watermarker and `image_processor.postprocess` over it, treating raw latents as decoded RGB.
# The text-to-image pipeline already guards both behind `output_type != "latent"`.
class SentinelWatermark:
def apply_watermark(self, images):
raise AssertionError("watermark must not be applied to latent output")

sd_pipe = self.get_pipeline()
sd_pipe.watermark = SentinelWatermark()

inputs = self.get_dummy_inputs()
inputs["output_type"] = "latent"
output = sd_pipe(**inputs).images

assert torch.is_tensor(output)
# Raw latents keep the VAE latent channel count; postprocess would have produced 3 channels.
assert output.shape[1] == sd_pipe.unet.config.in_channels

def test_pipeline_interrupt(self):
sd_pipe = self.get_pipeline().to(torch_device)

Expand Down
Loading