PVL Gemini 2.5 Segmentation
Ask Gemini 2.5 for a mask, not a caption
- image1
- image2
- image3
- image4
- image5
- mask1
- mask2
- mask3
- mask4
- mask5
- labels_json1
- labels_json2
- labels_json3
- labels_json4
- labels_json5
Every segmentation node in this pack asks a specialist model - SAM-3, Moondream - to find an object. This one does something weirder: it asks a generalist vision-language model to segment, and Gemini 2.5 is surprisingly good at it. You give it an image and a prompt like "the red car" and it returns an actual MASK, not a description. That's the kind of trick that sounded absurd when people first tried it and works disturbingly well on clean subjects.
Why reach for it over a dedicated segmenter? No segmentation model download, and - the real kicker - it's prompted with natural language. SAM needs point/box prompts or GroundingDINO-style text; Gemini just needs a sentence. If you want masks that follow "everything except the person in the background," Gemini's understanding of that instruction beats most specialist pipelines' parsing of it.
How it works
The node sends each image+prompt pair to Google's generateContent endpoint, asks the model to produce a mask, then converts the returned data into a ComfyUI MASK tensor. Up to five image/prompt pairs run independently, each producing its own mask output and its own labels_json - so you get the segmentation AND the model's text labels for each region.
The post-processing knobs are where the practical tuning lives:
mask_threshold(0.5) - binarizes the model's soft mask. Lower it if the mask is losing thin structures.combine_masks(on) - merge the model's per-region masks into one per image. Off if you want separate objects.mask_blur(0) andmask_expand(0) - smooth or dilate/erode the result. Negativemask_expanderodes; positive dilates. Tiny nudges fix the ragged edges you get from language-segmentation.
The inputs that matter
image1…image5andprompt1…prompt5- up to five independent segmentation jobs per node. EachpromptNpairs with itsimageN.model-gemini-2.5-flash(default) orgemini-2.5-pro. Flash is much cheaper; pro for hard scenes.api_key- blank defaults toGEMINI_API_KEY.temperature(0.7),tries(3),timeout(45),seed- engine knobs.triesdefaults higher here because segmentation responses can be flaky.debug,show_full_json_debug,debug_max_preview_chars- the debug trio; turn onshow_full_json_debugwhen a mask comes back empty to see what the model actually returned.
Outputs: mask1 … mask5 (MASK) and labels_json1 … labels_json5 (STRING).
Installing it
Standard pack install:
cd ComfyUI/custom_nodes
git clone https://github.com/pvlprk/comfyui-pvl-api-nodes
Restart, set GEMINI_API_KEY or fill the field. No models download; Gemini runs on Google's infrastructure.
Common issues
The honest limitation is that this is a language model doing a geometry task. On clean subjects it's great; on cluttered scenes with thin boundaries it produces masks that need the blur/expand cleanup, and sometimes it just returns a coarse region. If a mask comes back all-black, enable the debug outputs and look at what JSON the model sent - usually it either refused ("not enough context") or produced a degenerate polygon. Second, combine_masks on vs off changes what you get on the mask ports; if regions are missing, check that toggle before blaming the prompt. Finally, costs: five images × flash is cheap, but gemini-2.5-pro with retries is not - keep the pro model for the one hard image, not the whole grid.
Inputs (23)
| Name | Type | Default | Description |
|---|---|---|---|
| api_key | STRING | — | |
| model | COMBO | gemini-2.5-flash | 2 options: gemini-2.5-flash, gemini-2.5-pro |
| temperature | FLOAT | 0.70–2 | — |
| tries | INT | 31–10 | — |
| timeout | INT | 455–300 | — |
| seed | INT | 00–2147483647 | — |
| mask_threshold | FLOAT | 0.500–1 | — |
| combine_masks | BOOLEAN | true | — |
| mask_blur | INT | 00–64 | — |
| mask_expand | INT | 0-64–64 | — |
| debug | BOOLEAN | false | — |
| show_full_json_debug | BOOLEAN | false | — |
| debug_max_preview_chars | INT | 1200200–20000 | — |
| image1opt | IMAGE | — | |
| prompt1opt | STRING | — | |
| image2opt | IMAGE | — | |
| prompt2opt | STRING | — | |
| image3opt | IMAGE | — | |
| prompt3opt | STRING | — | |
| image4opt | IMAGE | — | |
| prompt4opt | STRING | — | |
| image5opt | IMAGE | — | |
| prompt5opt | STRING | — |
Outputs (10)
| Name | Type | Description |
|---|---|---|
| mask1 | MASK | — |
| mask2 | MASK | — |
| mask3 | MASK | — |
| mask4 | MASK | — |
| mask5 | MASK | — |
| labels_json1 | STRING | — |
| labels_json2 | STRING | — |
| labels_json3 | STRING | — |
| labels_json4 | STRING | — |
| labels_json5 | STRING | — |