Conversation
Mirrors the IP-Adapter integration in StableDiffusionInstructPix2PixPipeline: the image embeddings are ordered [cond, negative, negative] to match this pipeline's three-way [text, image, uncond] classifier-free guidance batch.
|
Self-review notes Ran the Verdict: READY — no blocking issues in the submitted diff. Found and fixed during review
Not fixed — deliberateThe four items in the Notes section of the description: the Dead-code check
|
What does this PR do?
Adds IP-Adapter support to
StableDiffusionXLInstructPix2PixPipeline, the only InstructPix2Pix pipeline that didn't have it - the SD version got it in #7820.I hit this building a depth-aware image editing pipeline on top of CosXL Edit, which
StableDiffusionXLInstructPix2PixPipelineis the only way to load in diffusers. I needed to combine an instruction edit with an IP-Adapter style reference, and with no IP-Adapter support on this pipeline I ended up wiring the adapter in by hand outside the library - which meant noset_ip_adapter_scale, no masks, and no multi-adapter support. The three-way CFG batch is the part that makes this pipeline different from the other SDXL ones, and it's what the standard two-way embedding helper can't produce.Fixes #14761
Changes
IPAdapterMixinon the pipeline, with optionalimage_encoder/feature_extractorcomponents andimage_encoderinmodel_cpu_offload_seq.encode_imageandprepare_ip_adapter_image_embeds, both# Copied fromtheir counterparts.prepare_ip_adapter_image_embedsis copied fromStableDiffusionInstructPix2PixPipeline, notStableDiffusionPipeline, because InstructPix2Pix runs a three-way CFG batch.ip_adapter_image/ip_adapter_image_embedsin__call__andcheck_inputs.image_encoder/feature_extractorin the dummy components.On the CFG ordering
InstructPix2Pix expands the batch three ways as
[text, image, uncond], andprompt_embedsis laid out as[prompt, negative, negative]. IP-Adapter conditioning is cross-attention conditioning like the prompt, so its embeddings follow the same layout - meaning IP-Adapter strength is scaled byguidance_scale, consistent with every other pipeline. I checked this by hookingunet.encoder_hid_proj: with CFG on, the projected hidden states come back as batch 3, chunk 0 carries the image embedding and chunks 1 and 2 are identical negatives.I first ordered them
[cond, cond, uncond], which puts IP-Adapter conditioning in theimage_guidance_scaleterm instead. That's a real behaviour difference and it disagrees with the SD pipeline, so I dropped it. Happy to switch if you'd rather the edit pipelines behaved that way.Tests
The 18 skips are the accelerator-gated memory tests - no GPU on this machine.
Two tests in that module that weren't passing before now do:
test_components_function(the new components are inget_dummy_components) andtest_save_load_optional_components, which was skipped as "every optional component is needed to encode the prompt" and is now implemented over the droppableimage_encoder/feature_extractor, matchingtest_stable_diffusion_xl_img2img.py.Also green:
The shared
IPAdapterTesterMixinonly exercises pre-computedip_adapter_image_embeds, so I ran theip_adapter_imagepath by hand as well: a single PIL image,num_images_per_prompt=3, a batch of 2 prompts, CFG off, and two stacked adapters all produce correctly shaped output, andset_ip_adapter_scale(0.0)reproduces the no-adapter output exactly.Notes
__init__parameters sit beforeforce_zeros_for_empty_prompt, matchingStableDiffusionXLImg2ImgPipeline. That shifts positions for anyone constructing the pipeline positionally -- appending them at the end would avoid that but break consistency with the sibling pipelines. Happy to move them.docs/source/en/using-diffusers/ip_adapter.mddoesn't keep a list of supported pipelines, so I didn't add a docs change there. Let me know if you'd like an example added.Before submitting
self-reviewskill on the diff?__call__docstrings for the new argumentsWho can review?
@yiyixuxu @asomoza