cv2.compare
Turn \"which is bigger\" into a mask you can actually use
- src1
- src2
- result
The one-line version
cv2.compare answers a comparison question about two arrays pixel by pixel and gives you back an 8-bit mask - 255 where the comparison is true, 0 where it's false. That output is the reason to care. ComfyUI is full of things that want a MASK, and this is one of the few nodes that lets you manufacture one from arithmetic instead of from a detector.
Concretely: compare a greyscale image against a blurred copy of itself and you get a "brighter than its local average" mask - a poor man's edge/high-pass detector with two nodes and no model. Compare two frames of a video and you get a motion mask. Both are legitimate, both are deterministic, both cost a millisecond. masking-detection-detailing.md makes the broader point that where a mask comes from is the interesting decision in a detailing pipeline; this is one more source.
It's a raw wrapper from ComfyUI CV (bmad4ever/comfyui_cv), category image/CV/low-level/cv2 C.
How it works
Element-wise: for every pixel, compare src1 against src2 using the operator you picked, write 255 or 0. The subtlety is in the operand types. The author's tooltip is precise about it: when the input is an array, it must have a single channel. Two arrays of the same shape compare fine; a 3-channel IMAGE against a scalar constant is also fine - OpenCV broadcasts - but the arrays themselves need to be one channel each.
There's a second gotcha hidden in that: the result of comparing a 3-channel image against a constant still comes back 3-channel, and the pack's type-echo rule means an IMAGE in gives an IMAGE out. A 3-channel white-on-black picture is not a MASK, and feeding it to a mask consumer is a common way to get a "worked, but wrong" result. Convert to grey first (Image → CV Array then a cvtColor, or the curated conversion nodes) when you want a true single-channel mask.
A constant operand comes from the CV Scalar node from this same pack - that's what it's for: authoring a constant vector for scalar-typed cv2 arguments.
Inputs and outputs that matter
- src1 - required. Array or scalar; single-channel when an array. The output echoes this input's format.
- src2 - required. The same rule applies. Wire an image, a mask, or a
CV Scalarconstant. - cmpop - the operator, a dropdown:
CMP_EQ,CMP_GT,CMP_GE,CMP_LT,CMP_LE,CMP_NE. Defaults toCMP_EQ. - result - 255/0, echoing
src1's format. IMAGE in, IMAGE out (previewable directly); NPARRAY in, NPARRAY out.
Note what's not here: no threshold value, no fuzzy tolerance. For "distance from this colour within a band", use the curated CV Color Range node instead - it's the same idea with the knob you actually want. And compare is on the pack's per-frame-safe list, so a batched IMAGE is looped frame by frame and re-stacked rather than quietly processed only at frame 0.
Installing the pack
Manager → search ComfyUI CV. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Restart. Python ≥ 3.12 and a V3 node API ComfyUI are required; the dependency list is just opencv-contrib-python-headless~=5.0.0.93 (plus numpy and torch).
Where people get burned
- Two 3-channel images and an exception. OpenCV only supports the single-channel-array case. Greyscale first, then compare.
- A mask that isn't a mask. The 3-channel result of comparing two colour images is white-on-black pixels, not a MASK.
cv2.compareon grey images keeps it single-channel and behaves the way you expect. - Antialiased soft inputs compared with
CMP_GTat a fixed constant. Tiny gradients flip the comparison along a boundary and you get speckle. Blur the input first, or compare against a blurred copy and accept a fatter mask. - The pack's own caveats, once per install, so they're not a surprise later. The README declares LLM-assisted development, a self-documented risk of overfitting to test cases, no planned updates, and "not recommended in production" without your own source review. And OpenCV itself is the shared point of failure across half your node packs: the classic community wounds are
DLL load failed while importing cv2on Windows portable builds and a non-contrib wheel silently replacing the contrib one.tools/repair_opencv_contrib.py --checkexists for the second.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| src1 | COMFY_MATCHTYPE_V3 | first input array or a scalar; when it is an array, it must have a single channel. The image output(s) echo this input's format. 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. | |
| src2 | NPARRAY,IMAGE,MASK | second input array or a scalar; when it is an array, it must have a single channel. 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. | |
| cmpop | COMBO | CMP_EQ | a flag, that specifies correspondence between the arrays (cv::CmpTypes) |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| result | COMFY_MATCHTYPE_V3 | Echoes the 'src1' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY. |