Nodes/kubakub nodes/kubakub regions from matrix / masks
ComfyUI Node

kubakub regions from matrix / masks

Turn a facade into a list of named regions

By kubakubkub·Created about a month ago·Updated 2 days ago· 10
kubakub regions from matrix / masks
  • matrix
  • scope
  • masks
  • regions_json
  • preview
  • scope
  • regions
  • report
◄modecolor_regions►
◄min_region_area400►
◄merge_small_regionstrue►
◄color_tolerance24►
◄split_disconnectedtrue►
◄group_tolerance0.08►
◄recursivetrue►
◄group_bystem►
◄output_maskstrue►
◄line_threshold0.50►
◄line_polarityauto►
◄line_gap_close_px1►
◄color_names►
◄mask_folder►
◄scope_masks►
◄split_masks►
◄tag_only_masks►

What it's for

Everything else in this pack runs on regions. A region is one window, one pier, one wall, one cornice - a named piece of the building you can give its own prompt, its own mask behaviour, its own animation. Before you can diffuse region by region, or clip a layer to "only the floor-1 windows", something has to produce that partition.

That's this node. You give it the facade matrix - the full projection image at delivery size, e.g. a 3840×2160 PNG, the thing a VJ would call the pixelmap - and it hands back a partition: every pixel belongs to exactly one region, regions are named, repeated elements share a group, and larger masks tag the regions they cover.

Nobody in the corpus is solving this with AI. Projection mapping is a tiny niche on Reddit (a couple of dozen threads a year, mostly unanswered questions from working VJs), and the professional workflow is After Effects masks plus a pixelmap - so the node meets people there and reads an AE mask export as readily as it reads colours off a flat illustration.

How it works

Three modes, and they're genuinely different tools:

  • color_regions - flat colour fills in the matrix. Each colour becomes a region; with split_disconnected on, each separate patch of that colour is its own region, so twenty window panes in one blue are twenty regions, not one. Anti-aliased rims are handled: pixels further than color_tolerance from every fill go to the spatially nearest region instead of becoming specks of their own.
  • line_drawing - the closed cells between lines. line_polarity auto takes whichever of dark/light is the minority as the lines; line_gap_close_px thickens lines slightly before filling so a one-pixel break doesn't leak one cell into its neighbour.
  • mask_folder - one region per PNG, named after the file. windows_03.png becomes region windows_03 in group windows. Alpha is used when it varies, otherwise brightness > 50%. recursive reads subfolders, group_by takes the group from the file stem or the subfolder name, and overlapping masks resolve smaller-wins, so windows cut out of a full wall mask works the way you'd hope.

The mask_folder mode is where the depth is. scope_masks names masks that aren't regions but a limit - nothing outside their union can ever be a region, which is how you keep the projection out of the parts of the image that will never be lit. split_masks names masks holding several shapes, each becoming its own region in reading order. tag_only_masks names masks that only label what they cover. The result is the hierarchy your AE export already encoded: region tagged with the floor and the facade.

Small stuff gets sorted too: min_region_area (400 px) with merge_small_regions on merges a speck into the neighbour it shares the longest border with, or drops it so its pixels stay unassigned and never get diffused. group_tolerance decides when two regions count as repeats of the same thing - relative bbox size and shape IoU - so 20 identical windows share a group_id and one prompt covers them.

Inputs and outputs

Required: matrix (only the first image of a batch is used), mode, min_region_area, merge_small_regions. Optional and important: mask_folder - and note that an empty file field is the sample: leave it blank and the node runs on the built-in sample facade's masks, which is why the starter workflows need no files from you. scope is a plain mask input usable in any mode.

Outputs, in the order I'd wire them:

  • regions - a label map plus the region table. This is the one you feed to region plan, mask field, brightness compensation. It's tiny (33 MB at 4K) compared to the mask batch.
  • masks - a MASK batch, one per region, index = region_id. Convenient and expensive: 100 regions at 4K is about 3.3 GB, and the node warns above 2 GB. Turn output_masks off when you only need regions.
  • regions_json - the table as text: ids, groups, tags, boxes. Save it; it's the audit trail of what the partition actually did.
  • preview - matrix in grey with one colour per group, borders and ids drawn in. This is how you debug a partition without diffusing anything.
  • scope and report - where regions may be, and how many were found, how many pixels went unassigned, which regions were merged or dropped.

Install

Manager → search kubakub nodes → install → restart. Manually:

cd ComfyUI/custom_nodes
git clone https://github.com/kubakubkub/ComfyUI_KubaNodes
python -m pip install -r ComfyUI_KubaNodes/requirements.txt

ComfyUI 0.37.0 or newer (this pack uses the newer node API and advanced inputs). The only added package is opencv-python-headless; keep your own OpenCV build if you have one. No models, no Blender, no ffmpeg here - this node is array work plus your masks.

Common issues

  • Hundreds of tiny regions. Your matrix isn't flat-colour - a rendered facade with gradients and shading won't split cleanly. Raise color_tolerance and min_region_area, or just use the mask folder, which is the honest answer for anything photographic.
  • Everything is one region. split_disconnected off in colour mode does exactly this. Also check color_tolerance isn't so high that your window blue and your wall blue are the same colour.
  • Out of memory on the masks output. 4K × many regions is real RAM. Use regions downstream and set output_masks off.
  • Line mode leaks between cells. Bump line_gap_close_px - small gaps in a scanned drawing are normal, and the node closes them rather than asking you to fix the drawing.
  • Region ids look random. They follow reading order - rows top to bottom, left to right - and the mask batch index equals region_id. Once you know that, masks[7] and region 7 stop being mysterious.
Categorykubakub/2d/regions

Inputs (19)

NameTypeDefaultDescription
matrixIMAGEThe facade matrix: the full projection image at delivery size (e.g. a 3840x2160 PNG). Only the first image of a batch is used.
modeCOMBOcolor_regionsWhere the regions come from. color_regions: flat colour fills in the matrix. line_drawing: closed cells of a line drawing. mask_folder: a folder of PNG masks (e.g. exported from After Effects).
min_region_areaINT4000–100000000Regions smaller than this (in pixels) are merged or dropped.
merge_small_regionsBOOLEANtrueOn: merge small regions into the neighbour with the longest shared border. Off: drop them (they stay unassigned).
color_toleranceINT240–442color_regions: colours closer than this (RGB distance, 0-255 units) count as the same fill.
split_disconnectedBOOLEANtruecolor_regions: on = every separate patch of a colour is its own region (each window), off = one region per colour.
group_toleranceFLOAT0.080–0.5How different two regions may be (relative bbox size, 1 - shape IoU) and still share a group_id. 0 = identical only.
recursiveBOOLEANtruemask_folder: also read PNGs in subfolders (Groups/, Windows/ ...).
group_byCOMBOstemmask_folder: group_id from the file name without its trailing number (stem) or from the subfolder name (folder).
output_masksBOOLEANtrueOff: the masks output is a single empty mask, saving RAM when only the regions output is used.
line_thresholdFLOAT0.500–1line_drawing: brightness that separates lines from paper.
line_polarityCOMBOautoline_drawing: auto takes whichever of dark/light is the minority as the lines.
line_gap_close_pxINT10–32line_drawing: thicken lines by this much first, so small gaps don't leak one cell into the next.
color_namesoptSTRINGcolor_regions: name the groups by matrix colour. Every region of that colour gets the name as group_id.
mask_folderoptSTRINGmask_folder: folder of PNG masks (alpha or white = inside). 'windows_03.png' becomes region 'windows_03' in group 'windows'. Empty: the sample facade's masks at the matrix size, so the node runs as it is.
scope_masksoptSTRINGmask_folder: masks (names or wildcards, comma or one per line) that are not regions but the area regions may use. Nothing outside their union is ever a region.
split_masksoptSTRINGmask_folder: masks holding several separate shapes. Each shape becomes a region <name>_01, _02 ... in reading order, all in group <name>.
tag_only_masksoptSTRINGmask_folder: masks that only tag the regions they cover and are no region themselves.
scopeoptMASKAny mode: pixels outside this mask are never part of a region.

Outputs (6)

NameTypeDescription
masksMASKOne mask per region; batch index = region_id.
regions_jsonSTRINGThe region table as text: ids, groups, tags, boxes and notes.
previewIMAGEThe matrix in grey with one colour per group, region borders and ids.
scopeMASK1 where regions may be (all 1 without a scope).
regionsKUBA_REGIONSLabel map + region table for the kubakub region nodes.
reportSTRINGHow many regions and groups were found, pixels left without a region, and the notes (merged or dropped regions, the sample used when no folder is given).