GPT-Image-2 Material Region Channel V1
One region, one billable call, no surprises
- base_image
- flat_image
- height_map
- material_region_set
- region_image
- region_mask
- call_status
What it is and why you'd reach for it
Four materials on one badge means four masked edits. The convenient way to build that is a loop node that fires four API calls behind one widget. The problem with the convenient way is that you can't see what it's doing, you can't re-roll one region, and when the run costs money you have no idea which child blew up.
This node takes the opposite approach: it's a visible single-region channel. You get four copies of it on the canvas, slot 1 through 4, each one expanding exactly one native GPT-Image-2 node - and only when its region actually exists. No region in that slot means the node returns your base image and an empty mask, having created no API child at all. That's the "billable-call-aware" bit in the author's description, and it's the right instinct for a pack where every generative hop is metered.
How it works
The material_region_set comes from Badge Material Region V1 (DAELab), which maps source colours in your flat artwork to a material catalogue and emits per-region masks. This node runs the same preflight the executor does: groups with no matched pixels, groups assigned the default transparent_lacquer, groups smaller than minimum_region_pixels, and groups missing a mask are all skipped with a reason. If the number of active regions exceeds max_regions it raises before anything is expanded - a budget guard, not an error about your mask.
region_slot then selects the nth active region, not the nth configured group. That's a distinction worth internalising: if region 1 is transparent lacquer and gets skipped, slot 1 picks region 2. Wire all four channels and read the call_status JSON on each one - it carries region_id, material_id, gpt_node_used and a reason field, so the mapping is auditable rather than guessed.
When a slot does resolve, the node expands a subgraph at execution time (ComfyUI's V3 node expansion): one native OpenAIGPTImageNodeV2 with the region's material prompt, 1024x1024, opaque background, Image 1 = your immutable base, mask = the region mask, seed = the region's reroll_revision; feeding one Badge Material Constraint V1 child with conservative fixed limits. You get the constraint's image and mask out, plus the status. Changing one region's material or re-roll revision invalidates only that channel's cache, so the other three GPT calls are served from cache instead of re-billed.
Inputs and outputs that matter
base_image (must be a single image - this raises on a batch), flat_image (colour reference), height_map, material_region_set, region_slot, quality (low/medium/high, passed straight to the model), max_regions (default 4), minimum_region_pixels (16 by default; raise it if you keep getting edits on stray specks).
Outputs: region_image and region_mask go to the matching slot of Badge Material Region Merge V1, along with call_status - the merge node requires the status string as an input, which is how it can report the true GPT-node count afterwards.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/TeutonicMepheles/ComfyUI-DAELab-Custom-Nodes-Library
Restart. You need ComfyUI's official OpenAI/GPT Image nodes installed and logged in - this node doesn't call the API itself, it grafts a native node into the graph, so if OpenAIGPTImageNodeV2 isn't available the expansion fails. It's also a V3-schema node, so keep core current (No module named 'comfy_api' = update core). OpenCV is required by the module and absent from requirements.txt.
Common issues
GPT masked editing requires exactly one immutable base image. You're feeding a batch. Every channel edits the same single base on purpose - that's what makes the regions comparable.- Slot 3 does nothing. Fewer than three active regions, or
call_statussaysno_active_region_for_slot. Check theskippedarray in the status; it names each skipped region and why. Material API budget exceeded before GPT execution. More thanmax_regionsactive regions. Either consolidate regions or raise the cap deliberately - the check exists so a colour-match rule that suddenly hits everything doesn't cost you four calls.- A channel returns your image unchanged with a zero mask. Correct behaviour for an empty slot. Wire
call_statusinto a report or text node if you want to see it without opening the node.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| base_image | IMAGE | — | |
| flat_image | IMAGE | — | |
| height_map | MASK | — | |
| material_region_set | BADGE_MATERIAL_REGION_SET | — | |
| region_slot | INT | 11–4 | — |
| quality | COMBO | medium | 3 options: low, medium, high |
| max_regions | INT | 41–4 | — |
| minimum_region_pixels | INT | 161–65536 | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| region_image | IMAGE | — |
| region_mask | MASK | — |
| call_status | STRING | — |