cv2.minMaxLoc
Find the extreme pixel, then get its coordinates
- src
- mask
- min
- max
- min_loc
- max_loc
You don't reach for cv2.minMaxLoc because you want to know the brightest value in an array. You reach for it because the brightest pixel has an address, and the address is the useful part. It's the node that turns "somewhere in this score map there's a peak" into (x, y) you can draw on, crop around, or feed into the next cv2 call.
The canonical pairing is template matching. cv2_matchTemplate gives you a result map where each value is the match score at one offset, and the whole point of the map is that its maximum (or minimum - the TM_SQDIFF family is inverted, lower is better) tells you where the template is. So: match, minMaxLoc, add the template's width and height to the location, done. The pack ships curated CV Match Template and CV Match Template Multi-Scale nodes that do this for you, but the raw pair is what you'd build on if you want the score map itself for a threshold, a heatmap, or a debug view. Same story for a distance transform's farthest point, a saliency map's hottest blob, or a blurred mask's alpha peak.
How it actually works
One pass over the array, tracking four things: the smallest value, the largest value, and the coordinates of each. It is not normalized, not smoothed, not per-channel - whatever numbers are in the array come back out, which means a uint8 image gives you 0–255 and a float score map gives you raw floats that could be any magnitude. If you wire in the optional mask, only pixels where the mask is non-zero get to compete.
One thing worth internalizing before you wire it up: min_loc and max_loc are (x, y), i.e. (column, row). If your next step indexes a numpy array, that's arr[y, x]. Everybody gets this backwards once.
Inputs and outputs
The one input that matters is src - required, and it accepts a ComfyUI IMAGE, a MASK, or an NPARRAY. The author's tooltip repeats OpenCV's own wording: single-channel array. The wrapper does not grayscale for you here (it does for threshold and the Hough lines, but minMaxLoc isn't on that list), so if you're coming from a 3-channel image, put a cv2_cvtColor with COLOR_BGR2GRAY in front of it. The optional mask (CV_8U, CV_8S or CV_Bool) restricts the search to a region - that's how you ask "where's the peak inside this person's mask" instead of inside the whole frame.
Also note the framing: an IMAGE link is read as frame 0 of a batch. Analysis on a whole batch means the batch bridges, not this node.
Outputs are min and max as FLOATs, plus min_loc and max_loc as CV_TUPLE point values. The two points travel as one composite value each, which is the pack's design for composite cv2 arguments - you can wire max_loc straight into any cv2 point input (a circle's center, floodFill's seedPoint, a line's pt1/pt2), or into CV Split Tuple when you need x and y as separate numbers. The FLOATs go wherever a number goes: a note, a text node, a threshold you're comparing against.
Install
ComfyUI Manager → search ComfyUI CV → install → restart. By hand:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Python ≥ 3.12 and a recent ComfyUI on the V3 node API. This node needs no models and no contrib submodule - plain cv2.
Where people get burned
- Multi-channel input. OpenCV documents this as single-channel; linking a BGR IMAGE is outside that contract. Grayscale first and the question goes away.
- Ties. Flat or quantized data has thousands of pixels sharing the extreme value, and you get one of them. Don't build logic on which one.
- Degenerate arrays. All-zero or all-constant input gives
min == maxand a location of whatever the scan hits first - fine, but don't read meaning into it. - Batch expectations. Link a batch, get frame 0's answer. That's the tooltip's own wording, not a bug.
- Missing from the menu. The low-level wrappers are generated from your installed
cv2and skipped when the build lacks the function. If acv2_*node is absent, that's a cv2 build problem, not a ComfyUI one - check with the pack'sCV Build Informationnode.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| src | NPARRAY,IMAGE,MASK | input single-channel array. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. | |
| maskopt | NPARRAY,IMAGE,MASK | optional mask used to select a sub-array of type CV_8U, CV_8S or CV_Bool. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| min | FLOAT | — |
| max | FLOAT | — |
| min_loc | CV_TUPLE | optional mask used to select a sub-array of type CV_8U, CV_8S or CV_Bool. Pixel coordinates (x, y) as ONE composite value - wire it straight into any cv2 point input (center, seedPoint, pt1/pt2) or into 'CV Split Tuple' for the separate numbers. |
| max_loc | CV_TUPLE | optional mask used to select a sub-array of type CV_8U, CV_8S or CV_Bool. Pixel coordinates (x, y) as ONE composite value - wire it straight into any cv2 point input (center, seedPoint, pt1/pt2) or into 'CV Split Tuple' for the separate numbers. |