Nano Banana - Face Swap (Crop & Composite)
The face swap that works when Nano Banana says no
- target_image
- identity_1
- identity_2
- identity_3
- identity_4
- identity_5
- identity_6
- network
- image
- status
- mask
- debug_sheet
If you've spent an evening with Nano Banana 2's API, you know the pain: you send a target frame, a reference face, and a perfectly innocent edit request, and the model comes back with a safety refusal. Google tightened IMAGE_SAFETY and celebrity detection hard in January 2026, and the word "swap" itself is a tripwire for the classifier. This pack exists to route around that, and NanoBananaCropSwap is its escape hatch: detect the head, swap only that crop, composite it back into the frame.
The honest headline comes straight from the README, so let's get it out front: cropping fixes refusals caused by scene content - background, clothing, peripheral objects. It can actually increase refusals when the safety classifier is reacting to identity recognition itself. It is not a universal anti-refusal button. But when your whole-image attempt keeps dying on a busy background, this is the node you reach for.
How it works
Instead of sending the full frame, the node runs a head detector, crops tight, swaps the crop, and pastes it back with post-processing meant to make the seam vanish. The detector cascade is worth knowing: detector=auto tries MediaPipe (fast, CPU-only), then OpenCV YuNet (a ~1.8MB ONNX auto-downloaded to ~/.cache/nanobanana_faceswap/), then a Gemini vision bbox call as last resort. Pick one explicitly and it won't silently fall back - you get ERROR:detector_unavailable:<name> if it's missing.
The crop expansion is scope-driven: face grows the bbox 25% on each side, head goes up 60%, head+styling up 100%, so the crop carries just enough context. The fiddly part is the math, and the author deliberately does not clamp the expanded bbox to image bounds before squaring - clamping would shift the face center and make the swapped head land misaligned with the body. Out-of-bounds pixels are filled with cv2.copyMakeBorder replicate and composited back at the (possibly negative) anchor. That's the difference between "looks pasted" and "looks native."
Inputs that matter
api_key/model- your Gemini key (or setGEMINI_API_KEYin the environment), and which model:gemini-3.1-flash-image-preview(fast, default),gemini-3-pro-image-preview(Pro, slower, higher-res), orgemini-2.5-flash-image.target_image+identity_1..identity_6- the frame to change and up to six reference faces.scope-face(eyes/nose/mouth/skin, keep hair and ears),head(adds hair + ears),head+styling(adds jewelry and makeup).detector,crop_size(default 1024),feather_px(24) - the mechanics above.composite_method(laplaciandefault) pluscolor_match,grain_match,sharpness_match- the integration stack that makes the result blend.match_directional_lightingis same-character mode only; leave it off for cross-identity swaps or it imposes the original person's skull geometry on the new identity.dry_runskips the API call and shows you the prompt that would be sent - free debugging.ref_cap_px(1024) downscales refs, which saves bandwidth and actually improves identity transfer; 4K skin-pore detail confuses the model.
Outputs
The four outputs are (IMAGE, STRING, MASK, IMAGE): image (the result, or a red-tinted placeholder on refusal), status (OK / REFUSED:<category> / ERROR:<reason>), mask (the feathered alpha composite in full-image coordinates - handy if you want to inspect or reuse it), and debug_sheet - a 2x2 montage of tight bbox, expanded bbox, the square sent, and the square returned. That last one is your friend when something looks off; it tells you instantly whether the model or the compositing is to blame.
Install
Same as the rest of the pack - it's in ComfyUI Manager if you search "NanoBanana FaceSwap," or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/IxMxAMAR/ComfyUI-NanoBanana-FaceSwap
cd ComfyUI-NanoBanana-FaceSwap
pip install -r requirements.txt
Restart ComfyUI. Heavy dependency alert: this is an API node, not a local model, so there's no big checkpoint to download - but mediapipe is required for the auto cascade, so run the requirements even if you only want one node. You'll need a Gemini API key either in the env or pasted into api_key.
Troubleshooting
REFUSED:<category>- red output means the classifier fired. That's the node working as designed; tryauto_relax_on_refusedor fall back to the pack's mask-inpaint or obfuscation pathways.- Face landing misaligned - almost always the crop math, but only if you've been in the source. In practice, re-check
scope:facekeeps hair, so if you wanted a full head replacement and only swapped the face, it'll look wrong. ERROR:detector_unavailable:mediapipe- you skipped the requirements install.
Batch tip: batch_axis lets you iterate frames (target) or identities against a fixed target, and each iteration is its own API call with its own retry - one refusal doesn't kill the batch.
Inputs (32)
| Name | Type | Default | Description |
|---|---|---|---|
| api_key | STRING | — | |
| model | COMBO | gemini-3.1-flash-image-preview | 3 options: gemini-3.1-flash-image-preview, gemini-3-pro-image-preview, gemini-2.5-flash-image |
| target_image | IMAGE | — | |
| identity_1 | IMAGE | — | |
| scope | COMBO | head | 3 options: face, head, head+styling |
| grid_mode | COMBO | separate_refs | 2 options: separate_refs, auto_sheet |
| custom_hint | STRING | — | |
| safety_threshold | COMBO | BLOCK_NONE | 4 options: BLOCK_NONE, BLOCK_ONLY_HIGH, BLOCK_MEDIUM_AND_ABOVE, BLOCK_LOW_AND_ABOVE |
| seed | INT | 00–2147483647 | — |
| batch_axis | COMBO | target | 2 options: target, identity |
| detector | COMBO | auto | 4 options: auto, mediapipe, opencv_yunet, gemini_bbox |
| crop_size | INT | 1024256–2048 | — |
| histogram_match | BOOLEAN | false | Legacy RGB histogram match. Superseded by color_match below; keep off unless debugging. |
| feather_px | INT | 240–256 | — |
| image_size | COMBO | 2K | 3 options: 1K, 2K, 4K |
| color_match | BOOLEAN | true | LAB color match with face-polygon stats. Fixes skin-tone cast. |
| grain_match | BOOLEAN | true | Add synthetic Gaussian noise matching the target's cheek variance. |
| sharpness_match | BOOLEAN | true | Blur Gemini's face if it's sharper than the body. Never sharpens. |
| composite_method | COMBO | laplacian | 2 options: laplacian, feather |
| match_directional_lighting | BOOLEAN | false | Same-character mode. Imposes the original face's lighting envelope on the new face. Corrupts identity for cross-identity swaps. |
| lab_strength | FLOAT | 0.600–1 | — |
| grain_strength | FLOAT | 1.000–2 | — |
| identity_2opt | IMAGE | — | |
| identity_3opt | IMAGE | — | |
| identity_4opt | IMAGE | — | |
| identity_5opt | IMAGE | — | |
| identity_6opt | IMAGE | — | |
| dry_runopt | BOOLEAN | false | Skip API call; return a structured preview of the prompt + parts. |
| timeout_msopt | INT | 1800005000–600000 | — |
| ref_cap_pxopt | INT | 10240–4096 | Downscale ref longest-edge to this px. 0=off. |
| auto_relax_on_refusedopt | BOOLEAN | false | On refusal, retry once with no safety_settings. |
| networkopt | NB_NETWORK | Optional. Wire a NanoBanana - Network Route node here to route this swap's API call through that proxy (e.g. US egress). |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| status | STRING | — |
| mask | MASK | — |
| debug_sheet | IMAGE | — |