ComfyUI Node

cv2.min

Per-pixel minimum, the Darken blend mode and your cheapest mask intersection

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
cv2.min
  • src1
  • src2
  • result

cv2.min takes two arrays and keeps the smaller value at every pixel position. That's the whole function - and it's Photoshop's Darken blend mode, which means if you've ever used a "darken" layer you already know what this does.

In a ComfyUI graph the useful framing is mask and level algebra. Min of two masks is intersection (AND): the result is on only where both were on, which is how you restrict a region to two overlapping conditions without any boolean nodes. Min of an image and a ceiling value clamps highlights. Min of an image and a blurred copy of itself does a soft "shrink to the darker neighbourhood" pass, which is the cheap version of an erode.

And yes, this is the counterpart people reach for without realising it's just min: when a graph gets a "levels" treatment by hand, max(image, floor) then min(image, ceiling) is what's happening.

The sockets

src1 echoes its format - IMAGE in, IMAGE out; MASK in, MASK out - so if the mask algebra above is your goal, the MASK goes on src1. src2 takes NPARRAY, IMAGE/MASK or LATENT, and CV Scalar is how you feed it a constant vector instead of a second picture.

LATENT support is real here: a latent arrives as float32 [H,W,C] frame 0 with values untouched, and min is on the pack's list of operations that can process a whole latent batch when both inputs share a batch size. IMAGE inputs, when both sides are batches of equal length, are processed frame by frame across the entire batch rather than just the first frame.

No optional parameters at all. One output, echoing src1.

Where it earns its keep

  • Mask intersection. min(a, b) is AND. Two contours, two detectors, a face box and a subject mask - the overlap, in one node.
  • Highlight clamping. min(image, 0.95) so nothing blows to pure white before a match or a print-style grade.
  • Erode-ish cleanup. Min against a shifted or blurred copy trims a mask's fringe, which is the trick people hand-build with blur + threshold.
  • Latent curiosity. Fine legally, crude in principle. Useful for checking whether two latents are near copies of each other.

Installing the pack

ComfyUI CV (bmad4ever/comfyui_cv) - search "comfyui_cv" in ComfyUI Manager, or:

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

Restart ComfyUI. Python ≥ 3.12, V3-API ComfyUI build, that one pinned contrib OpenCV wheel, no model files. Core OpenCV function, so fast it's effectively free.

Where people get burned

"Min of my image" - you wanted cv2.minMaxLoc. This is element-wise, not a reduction. If you're after the darkest pixel and where it lives, that's a different node.

No broadcasting, ever. Both arrays must be the same size, same channel count, same type. Resize first; cast with CV Cast Array if one branch is float and the other uint8. And keep in mind that two IMAGE links get converted to uint8 BGR 0–255 on the way in, so "min of two images" is happening on 8-bit values with 8-bit clamping.

Unequal batches. The full-batch path only engages when both inputs have the same batch size. Mismatched, you silently get frame 0 - the classic "why is this node ignoring my batch" symptom.

MASK-in, IMAGE-out confusion. The echo follows src1. Put the MASK where you want the MASK to come back.

Contrib packages missing from the menu. Pack-wide issue, worth repeating because it looks like a broken install: all four opencv-* wheels share one site-packages/cv2, so a non-contrib wheel installed over the contrib one empties the contrib submodules and those nodes never register. python tools/repair_opencv_contrib.py --check, then --apply. cv2.min is core and would still be there - the pack's contrib nodes would not.

Categoryimage/CV/low-level/cv2 M

Inputs (2)

NameTypeDefaultDescription
src1COMFY_MATCHTYPE_V3first input array. The image output(s) echo this input's format. A LATENT link is processed in latent space: frame 0 becomes a float32 [H,W,C] array (any channel count), values untouched. Arithmetic ops (add, multiply, etc.) also accept a full LATENT batch ({samples: [B,C,H,W]}) — the whole batch flows through when both inputs have the same batch size. 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.
src2NPARRAY,IMAGE,MASK,LATENTsecond input array of the same size and type as src1. A LATENT link is processed in latent space: frame 0 becomes a float32 [H,W,C] array (any channel count), values untouched. Arithmetic ops (add, multiply, etc.) also accept a full LATENT batch ({samples: [B,C,H,W]}) — the whole batch flows through when both inputs have the same batch size. 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 (1)

NameTypeDescription
resultCOMFY_MATCHTYPE_V3Echoes the 'src1' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY.