Nodes/ComfyUI CV/cv2.cornerMinEigenVal
ComfyUI Node

cv2.cornerMinEigenVal

The one-number corner test (and why it beats Harris)

By bmad4ever·Created 4 months ago·Updated 14 days ago· 1
cv2.cornerMinEigenVal
  • src
  • nparray
◄blockSize0►
◄ksize3►
◄borderTypeBORDER_DEFAULT►

cv2.cornerMinEigenVal is the cleanest corner response in OpenCV, and it's the one to understand first. It builds the same gradient structure tensor that cv2.cornerHarris does, but instead of combining the two eigenvalues with a magic constant, it just returns the smaller one. A pixel is a corner exactly when both eigenvalues are large, so thresholding the smaller one is the direct test - no k to tune, no negative response to reason about.

The ComfyUI CV node cv2.cornerMinEigenVal wraps it, category image/CV/low-level/cv2 C. As with every raw wrapper in this pack, you get the cv2 function's signature and its output shape, and nothing curated on top.

How it works

For each pixel, take the gradients over a blockSize neighborhood (Sobel, ksize taps), form the 2×2 matrix M = [[ΣIx², ΣIxIy], [ΣIxIy, ΣIy²]], and compute its eigenvalues λ1 ≥ λ2. λ2 is the minimum eigenvalue - the response along the weakest gradient direction. Flat: both zero. Straight edge: λ2 ≈ 0, so an edge never registers, no matter how strong. Corner: both large, λ2 goes up.

That's the Shi-Tomasi measure, and it's the same measure you're implicitly using whenever you tick useHarrisDetector off in cv2.goodFeaturesToTrack. The practical difference from Harris is that the response is bounded below by zero and monotone in corner strength, so a threshold behaves predictably across images - which is why GFTT is the classic KLT/tracking seed and Harris is mostly a history lesson now.

The pack's own tooltip for the OpenCV dst parameter spells the connection out: this is the (same details as) cornerEigenValsAndVecs family, with the eigen-decomposition collapsed to one channel.

The inputs and outputs that matter

src is the image - a 3-channel IMAGE is auto-converted to grayscale by the pack, because the function only takes single-channel input. blockSize is the neighborhood and it's the one you'll actually tune: 2 is snappy and picks up fine structure, 5–7 makes broader, more stable responses. The optional ksize is the Sobel aperture, preset to 3 in this node (the OpenCV default, and the sensible one). The optional borderType is there if you need it, and the function does not support BORDER_WRAP.

One output, nparray: a single-channel float32 response map, same size as the input. Not an IMAGE socket - this is data. Preview CV Array (heatmap or normalize mode) is how you look at it, Inspect CV Data tells you the range so you know where to put the threshold, and cv2.threshold on a chosen value followed by CV Array → Mask converts the response into a mask you can feed anywhere.

Unlike cornerHarris you don't have to normalize first: λ2 starts at 0 for flat areas, so a fixed threshold like 10–30 on a typical 8-bit-scaled image means something. It's still image-dependent, but it's a number you can carry between images and only nudge.

Points, not a map

If what you want is the corner coordinates, this node isn't it - it emits one score per pixel and no points. Two options in this pack return points instead: cv2.goodFeaturesToTrack (the raw wrapper, maxCorners/qualityLevel/minDistance, Harris off = Shi-Tomasi, outputs an NPARRAY of points), and the curated CV Detect Corners node, whose GFTT (Shi-Tomasi) mode is this exact measure plus non-max suppression, returning proper KEYPOINTS. Use cv2.cornerMinEigenVal when the map is what you want: structure weighting, a corner-density heatmap, or a mask of "busy" regions.

Installing it

ComfyUI Manager → search the pack title (ComfyUI CV) → install → restart. Or 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 plus a ComfyUI on the V3 node API. The wheel has to be the contrib build - but note the headless pin doesn't mean fewer cv2 functions, it only means no imshow/window code, which this pack never uses anyway. If you install a plain opencv-python on top, the shared site-packages/cv2 loses its contrib submodules and nodes vanish; tools/repair_opencv_contrib.py --check/--apply repairs it.

Common issues and troubleshooting

Everything reads as a corner. blockSize too small for the noise in the image, or the threshold is below the sensor-noise floor. Bump blockSize to 5–7 and raise the threshold; for photos, a slight blur (cv2.GaussianBlur) before this node is the standard pre-step.

Nothing reads as a corner. Threshold set for a different image's contrast. Check the actual max with Inspect CV Data or CV Array Statistic and pick a fraction of it.

The map is huge and the preview is useless. Preview CV Array defaults may not stretch the values - switch it to normalize mode. It's a score map, not a photograph.

Categoryimage/CV/low-level/cv2 C

Inputs (4)

NameTypeDefaultDescription
srcNPARRAY,IMAGE,MASKInput single-channel 8-bit or floating-point image. 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.
blockSizeINT0-2147483648–2147483647Neighborhood size (see the details on #cornerEigenValsAndVecs ).
ksizeoptINT3-2147483648–2147483647Aperture parameter for the Sobel operator. Preset to the OpenCV default (3).
borderTypeoptCOMBOBORDER_DEFAULTPixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported.

Outputs (1)

NameTypeDescription
nparrayNPARRAY—