jz Composite Back
Stitch the Original Back Without the Seam
- generated_image
- original_image
- image
- outpaint_mask
- paste_mask
Gemini Composite is the glue step in a Gemini / "Nano Banana" outpainting workflow. It's the node that answers the question every outpaint ends with: I've got the AI's extended version, now how do I keep my original pixels and lose the visible rectangle where the two meet?
The name is honest about the shape of the problem. Gemini image models (Nano Banana Pro, Gemini 3 Pro Image) are brilliant at filling borders, but the Gemini API has no true mask input like a local inpaint model - you pad the canvas, the model paints the whole thing, and the original content comes back slightly reimagined. The clean way to do outpainting with these models is therefore a pad-and-stitch pattern: pad to a supported API size, let Gemini fill it, then paste your original back on top so nothing inside the original frame changes. This node is that paste, and it does it with a feathered edge instead of a hard cut.
How it works
Mechanically it's simple and you can see all of it in the source. It builds a grayscale mask (white = padded area, black = your original), dilates that mask a few pixels into the original so the feather starts a little inside your image, blurs it for a soft gradient, then inverts it and uses it as the alpha to paste your original over Gemini's output at the pad offsets. The result: near the boundary, your original pixels show through with partial opacity over the AI fill, which is exactly what kills the "I can see where the composite happened" tell.
The inputs that matter
- generated_image - Gemini's outpainted output (from GeminiImageGenerate).
- original_image - your pre-outpaint image, unchanged.
- pad_left / pad_top / pad_right / pad_bottom - wire these straight from GeminiPadCalculator's pad outputs. Getting these right is the whole game: if they don't match the padding the generate call actually used, your original lands in the wrong spot.
- expand (default 4) - how far the feather grows into your original. Higher = the composite eats more of the AI's border blend.
- blur_radius (default 4) - Gaussian blur on the mask. Set 0 if you want a hard edge for some reason.
- max_filter_size (default 3) - the dilate kernel size. Slider steps by 2 because the kernel must be odd; the code enforces that anyway, so don't stress about it.
The defaults are sane. The only things you'll realistically touch are expand and blur_radius, and only if you're seeing a seam.
Outputs
image is the finished composite - that's your Save Image target. outpaint_mask and paste_mask are grayscale debug outputs (white = filled area, and the inverted feathered mask respectively). They're genuinely useful the first time: if something looks wrong, view these to see what the node thinks is padded versus original.
Install
The pack has no requirements.txt and depends on scipy at import time - if scipy is missing, none of its nodes register. So install is:
# ComfyUI Manager → search "ComfyUI-Outpainting-Gemini" is easiest
cd ComfyUI/custom_nodes
git clone https://github.com/jzhang-POP/ComfyUI-Outpainting-Gemini
# only if scipy isn't already in your ComfyUI venv (it often isn't):
source ../venv/bin/activate && pip install scipy
Then restart ComfyUI. Watch out: the pack README's own install line points at a YOUR_USERNAME/Vermeer-Gemini.git placeholder URL - that's a typo, the real repo is jzhang-POP/ComfyUI-Outpainting-Gemini.
Troubleshooting
- A hard rectangle visible around your original → the feather is too weak or the mask is off; bump expand and blur_radius.
- Your original is offset / the composite doesn't line up → pad values and the API call disagree. This is the classic one: you set an aspect ratio and resolution manually on GeminiImageGenerate, but the calculator resolved different ones. Wire the calculator's resolved
aspect_ratioandresolutionoutputs into the generate node so the padding and the API size always match. - The node doesn't show up at all → check scipy (see install), then reload.
This is a small node that does one job well. Once your padding is wired from the calculator, it's set-and-forget - which is how you want the seam-removal step of an outpainting pipeline to feel.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| generated_image | IMAGE | — | |
| original_image | IMAGE | — | |
| pad_left | INT | 0 | — |
| pad_top | INT | 0 | — |
| pad_right | INT | 0 | — |
| pad_bottom | INT | 0 | — |
| expand | INT | 40–64 | — |
| max_filter_size | INT | 33–15 | — |
| blur_radius | INT | 40–64 | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| outpaint_mask | IMAGE | — |
| paste_mask | IMAGE | — |