Coordinates From Mask
Turn a mask into a scatter of points (and a point mask)
- mask
- coordinates
- point_mask
Any time you have a mask and you need places to put things, this node earns its keep. Do you want point targets for a detailer pass, spawn points for a particle or animation system, anchor points to drive a warping node, or just a set of coordinates to feed a JSON-driven workflow? Coordinates From Mask samples the interior of your mask and hands you both the points and a picture of them.
How it works
The pipeline is short and deterministic:
- Threshold the mask to binary.
- Run an OpenCV distance transform, so every white pixel knows how far it is from the nearest black (edge) pixel.
- Keep only points at least
min_distance_from_edgepixels away from an edge - that's your "valid" pool. - Sample a fraction of that pool using a Poisson-disk-style sampler - points get spread out evenly-ish instead of clumping - and cap it with
max_points. - Return the points as a JSON string and as a new mask with a white dot on each point.
Poisson disk sampling is the nice part. If it just picked random points you'd get clusters; this walks the valid points and refuses to add one that's too close to an already-chosen point (a grid-based distance check). The result is a nicely distributed scatter, seedable so you can reproduce it.
The inputs that matter
mask- the region to sample. Only the first mask in the batch is used.percentage- the fraction of valid points to select (default 0.1). Not a fraction of the whole mask - a mask with a lot of edge-near area will have fewer valid points to begin with.min_distance_from_edge- minimum distance from the mask edge (default 5 pixels). Raise it to keep points safely inside, away from fiddly borders.max_points- hard cap (default 100).percentagepicks the number, this sets the ceiling.seed- reproducibility. The code appliesabs(seed), so a negative seed doesn't error, it just becomes positive.
The outputs
Two outputs, and they're aimed at different consumers:
coordinates- aSTRINGof JSON, e.g.[{"x": 120, "y": 80}, ...]. In ComfyUI a raw string isn't that useful on its own - you'll want a JSON-parse node (or something that reads coordinates) downstream to actually split it into numbers. If you're feeding a node that expects coordinate text, this is your wire.point_mask- aMASKwith a white pixel at each selected point. Plug this straight into anything that takes a mask.
Install
From the Quasimondo pack, installed the usual ways - ComfyUI Manager, search ComfyUI-QuasimondoNodes, install and restart, or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/Quasimondo/ComfyUI-QuasimondoNodes
cd ComfyUI-QuasimondoNodes
pip install -r requirements.txt
No models to download. This node uses OpenCV (the pack's opencv-contrib-python dependency) for the threshold and distance transform, plus a touch of numpy - nothing heavy.
Where people get burned
The main trip-up is expecting the coordinates string to do something on its own. It's text; you have to route it to something that consumes text and parses it. The point_mask output is the more immediately useful one for most workflows.
Second: percentage is relative to the valid points, and min_distance_from_edge heavily influences how many of those exist. Crank min_distance_from_edge to 100 on a small mask and you may get zero valid points - and zero coordinates back. Start low (5–10) and raise it while watching the count.
Also worth knowing: with max_points at 1 you get exactly one point, which makes this a cheap "find a good spot inside this region" picker - a poor-man's Distance Map, if you will.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| mask | MASK | — | |
| percentage | FLOAT | 0.100–1 | — |
| min_distance_from_edge | INT | 50–100 | — |
| max_points | INT | 1001–1000 | — |
| seed | INT | 1234-9007199254740991–9007199254740991 | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| coordinates | STRING | — |
| point_mask | MASK | — |