cv2.boundingRect
Turn any mask or point cloud into a crop rectangle
- array
- bbox
Give it the non-zero pixels of a mask, or an arbitrary pile of 2D points, and it hands back the smallest upright rectangle that contains them, as a proper BOUNDING_BOX - the same structure core ComfyUI uses for crop and region nodes. That's the job: a mask is a shape, and most of what you want to do downstream (crop it, upscale it, re-render it, label it) needs a rectangle.
This is a raw wrapper from comfyui_cv, the pack that surfaces ~470 cv2.* functions as nodes. Worth stating plainly: OpenCV's boundingRect is a one-line geometry helper, and the pack doesn't dress it up. What makes it useful here is the output type - the pack converts cv2's Rect into ComfyUI's BOUNDING_BOX (nested one group per frame), which is what makes it composable instead of dead-ending in a raw array.
Why you'd reach for it
It's the first step of the detect → crop → re-render → paste-back loop - the automation pattern that keeps small broken regions from ruining an otherwise good generation. A detector or a hand-drawn mask gives you where, and this node converts that into the rectangle the crop consumes. It's also the cheap sanity check on a mask: is this region 3% of the frame or 80%? One number tells you whether your detailer pass is about to touch a thumbnail or the whole image.
The other half is point sets. Keypoints, tracked features, corner arrays, cv2.findNonZero output - anything that's an N×2 matrix of coordinates can be boxed, which is how you'd quantify "where did the tracked features go this frame" or scope a region to overlay.
Inputs and output
There's exactly one input:
array(required) - and it is NPARRAY-only. This is the thing people trip on first: an IMAGE link will not connect, and neither will a MASK. Data arrays here are a separate socket type by design, because half of these cv2 functions take points or matrices rather than pictures. Bridge withMask → CV Arrayor* → CV Arrayand you're through. Passing a grayscale mask array is fine - OpenCV reads the non-zero pixels and boxes those.
Output is bbox, a BOUNDING_BOX: {x, y, width, height}, one group per frame. The author's own tooltip names the destinations - Crop By Bounding Boxes, Draw BBoxes, or CV Split Tuple when you want the four numbers loose. Because it's the native core type, other packs' box consumers accept it too.
Two details about those numbers. The width and height are inclusive of both extreme pixels, which is OpenCV's convention; the pack's crop node consumes them directly, and if you slice by hand, [y : y+h, x : x+w] is the right form - don't shave a pixel off the end. And this is a single rectangle for the whole array: ten disconnected blobs become one box around all of them. If you want one box per blob, that's the curated CV Masks to BBoxes node in the same pack, not this wrapper.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Restart, or ComfyUI Manager → search "comfyui_cv". Needs Python ≥ 3.12, a recent ComfyUI on the V3 node API, and OpenCV 5 contrib:
pip install "opencv-contrib-python-headless~=5.0.0.93"
Behaviour is curated against that version. No model downloads for this node - it's geometry.
Common issues
The socket is red / won't connect. You're feeding an IMAGE or MASK. Insert Mask → CV Array (for a mask) or Image → CV Array (for a picture); only an NPARRAY link is accepted.
cv2 throws on an empty input. A mask with no non-zero pixels has no bounding rectangle to compute, and OpenCV errors rather than returning a zero rect. Gate it upstream, or accept that "nothing found" is an error path here rather than a found flag - the curated CV Masks to BBoxes node exists partly because it handles that case.
The box is bigger than the blob. Remember what an upright box is: for a diagonal shape or a rotated object, the axis-aligned rectangle around it is much larger than the object. cv2.minAreaRect plus cv2.boxPoints gives you the tight rotated version when the loose one won't do.
The node doesn't exist in your install. Low-level wrappers are generated at import time against the OpenCV build actually installed, and the pack skips what it can't find. If the whole low-level catalog is missing, your cv2 is probably a non-contrib or wrong-version wheel - the pack ships tools/repair_opencv_contrib.py (--check, then --apply) for exactly that, and installing a plain opencv-python over a contrib wheel silently empties the shared site-packages/cv2.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| array | NPARRAY | - - - A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| bbox | BOUNDING_BOX | - - - A cv2 Rect as core BOUNDING_BOX data ({x, y, width, height}, nested one group per frame) - feed 'Crop By Bounding Boxes', 'Draw BBoxes', or 'CV Split Tuple' for x/y/w/h. |