Apply latent injection (middlek)
Keep your product intact while everything around it changes
- model
- latents
- inject_image_embed
- inject_mask
- MODEL
- LATENT
This is the node the pack is named around. ApplyLatentInjection hijacks the KSampler so that, during sampling, your original product's latent gets re-injected where the product actually is - while the rest of the frame gets regenerated with the lighting, background, and composition you asked for. It's the difference between "the bottle is still recognizable" and "the bottle is a deformed suggestion of a bottle."
Why it exists
Plain inpainting preserves an input by compositing the original latent back into the masked region at each sampling step. That works, but if your input is a low-quality phone photo, you're gluing a low-quality latent into your output - the regeneration can't fix it. IC-Light solved the quality side by relighting the subject, but relighting regenerates the foreground too, which is exactly where text and logos get mangled.
Latent injection threads the needle. It's a Kandinsky-2.2-style idea (the pack's README credits that lineage): instead of re-injecting the raw original latent, it re-injects the original latent noise-scaled to the scheduler's current sigma, so the preserved region stays consistent with the sampling noise. And it only does this inside a sigma window - skipping the very first and very last steps so IC-Light's global lighting changes can still bake in. The result: the product keeps its detail, the world around it gets rebuilt.
How it actually works under the hood
The node patches KSamplerX0Inpaint.__call__ globally with a wrapper. When the model's options contain the is_latent_inject marker, every sampling step inside your start_sigma–end_sigma window does:
x = x * denoise_mask + noise_scaled(product_latent) * (1 - denoise_mask)
In plain words: where the mask says "generate," it generates; where the mask says "keep," it writes back the noise-scaled original product latent instead of the plain inpainting latent. When remain_injected is False, the original __call__ is restored automatically at the end of the run. (Default is True, which leaves the patch in place - harmless, because the wrapper only acts when the is_latent_inject marker is present, and re-patching is idempotent.)
The inputs that matter
model- your model, ideally after IC-Light has been applied (ICLightApplyfrom ComfyUI-IC-Light-Native, in the shipped workflow).latents- the latent the KSampler will denoise. In the pack's workflow this is the VAE-encoded product image.inject_image_embed- the product's latent that gets noise-scaled and re-injected. Also a VAE-encoded version of the product.inject_mask- the mask telling the sampler where to generate. The demo inverts the product mask before feeding this in, so generation happens around the product and the product region is what stays pinned.start_sigma/end_sigma- the injection window. Defaults are15.0and0.0; the shipped workflow uses10and0.4. Lowerend_sigma(toward 0) enforces product detail for more of the run; raising it lets the final steps relight and harmonize more aggressively.remain_injected- optional,True/False.
Outputs: a patched MODEL (feed that into the KSampler) and a LATENT (the input latents with samples replaced by the injection embed and the noise mask set).
Install
The pack's usual path:
cd ComfyUI/custom_nodes
git clone https://github.com/MiddleKD/ComfyUI-productfix
pip install -r requirements.txt
Restart. The full workflow additionally wants the IC-Light SD1.5 fc model, a depth ControlNet, a detail LoRA, and companion packs (ComfyUI-IC-Light-Native, Impact Pack, essentials, controlnet_aux) - but the node itself only needs the two-pack dependency install above.
Common issues
- Text still warps - the injection window is too narrow or the region isn't masked right. Check that
inject_maskactually covers the product, and consider pairing withDetailTransferAdd+GetTextMaskto transplant crisp text back afterwards. - Result looks pasted-on / disconnected -
end_sigmatoo high means the final harmonizing steps never run. Nudge it down. - Other inpaint workflows behave oddly after this runs - only if
remain_injectedisTrueand you're worried: the patch is a no-op without the injection marker, so it's effectively invisible. Setremain_injectedtoFalseto restore eagerly anyway.
It's fiddly, it's sigma-dependent, and it's a genuinely niche technique from a tiny pack - but if you're compositing product shots against new backgrounds with IC-Light, it's the only tool doing this job. The author's demo images speak for themselves.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| latents | LATENT | — | |
| inject_image_embed | LATENT | — | |
| inject_mask | MASK | — | |
| start_sigma | FLOAT | 15.00 | — |
| end_sigma | FLOAT | 0.00 | — |
| remain_injectedopt | COMBO | true | 2 options: true, false |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| MODEL | MODEL | — |
| LATENT | LATENT | — |