Nodes/ComfyUI CV/cv2.cornerEigenValsAndVecs
ComfyUI Node

cv2.cornerEigenValsAndVecs

The eigen-decomposition hiding under every corner detector

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

If Harris and Shi-Tomasi are the two corner detectors everyone quotes, cv2.cornerEigenValsAndVecs is the thing they're both approximating. It returns the actual eigenvectors and eigenvalues of the structure tensor at every pixel - six floats per pixel - instead of collapsing them into a single score. The ComfyUI CV node cv2.cornerEigenValsAndVecs exposes it directly (category image/CV/low-level/cv2 C).

Which is to say: this is an analysis node, not a detection node. You reach for it when you need to know why a pixel reads as a corner or an edge - the magnitude of the response in each direction, and the orientation that goes with it - not when you want coordinates of corners. For that, the pack's cv2.goodFeaturesToTrack or the curated CV Detect Corners are the tools.

How it works

OpenCV builds the same 2×2 second-moment matrix of image gradients that cv2.cornerHarris builds - averaged over a blockSize neighborhood, derivative approximated with a ksize-tap Sobel. Harris then throws away the eigen-decomposition and scores the matrix with det − k·trace². This function keeps everything: the two eigenvalues λ1 ≥ λ2 and their two eigenvectors.

Read the output like this: both λ small → flat area. λ1 large with λ2 near zero → an edge, and the eigenvector tells you its direction. Both large → a corner, and the two eigenvectors are the two dominant gradient orientations. That's the mechanism behind cv2.cornerMinEigenVal (which just keeps λ2) and behind Shi-Tomasi, which is the λ2 test with non-max suppression.

According to the OpenCV documentation the pack copies verbatim into the node's tooltip, the destination is CV_32FC(6) - six float32 channels per pixel, in the order λ1, λ2, x1, y1, x2, y2.

The inputs and outputs that matter

src is the image; feed it a color IMAGE and the pack grayscales it for you, since the function takes one channel only. blockSize is the neighborhood - 2 to 5 is the useful range, and bigger gives smoother, slower-to-respond eigenvalues. ksize is the Sobel aperture, 3 by default. borderType is optional, and the author's tooltip mentions the one thing worth knowing: BORDER_WRAP is not supported by this function, so don't pick it from the dropdown.

The output is a single NPARRAY named nparray, six channels deep. That shape is the awkward part. Inspect CV Data confirms what you've got, CV Slice Array pulls one channel out - λ2 alone is the interesting one, because it's literally the Shi-Tomasi measure - and Preview CV Array with heatmap mode shows it. Do the slicing at array level: this is not an image you can wire into Save Image, and the six-channel layout doesn't survive the trip through CV Array → Image anyway.

Where it's genuinely useful

Three honest uses. First, teaching yourself or verifying a detector: watch λ2 light up on the same corners your CV Detect Corners node found and you understand why it found them. Second, orientation: the eigenvectors give you a local gradient direction per pixel, which is the start of anisotropy maps, edge-orientation statistics, or non-max suppression of your own. Third, structure-aware masking - pixels where λ1 is large and λ2 tiny are edges, so a threshold on the channel pair is an edge mask you didn't have to Canny.

For everything else, remember that computing six floats per pixel costs roughly six times cornerHarris for information you usually don't use. If all you want is "where are the corners", go get the points.

Installing it

ComfyUI Manager → search the pack title (ComfyUI CV) → install → restart the server. Manual:

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

Requires Python ≥ 3.12 and a ComfyUI recent enough for the V3 node API. This pack is a fork of opencv-comfyui, is honest about being a single-author LLM-assisted project, and states plainly that updates aren't planned - pin your version if you depend on it. Avoid installing a non-contrib opencv-python alongside; it silently strips the contrib submodules from the shared cv2 and the contrib nodes stop loading (tools/repair_opencv_contrib.py fixes that).

Common issues and troubleshooting

The output has six channels and nothing accepts it. Expected. Slice the channel you need with CV Slice Array and keep the result in NPARRAY space - ComfyUI's IMAGE socket is a 3-channel float tensor and can't carry this.

Values are near zero everywhere. Same scaling problem as any structure-tensor output: six orders of magnitude compressed into a map you're eyeballing. Normalize before you judge, and remember the eigenvalues are squared-gradient quantities, so contrast in the source image drives them directly.

BORDER_WRAP raises or misbehaves. The function doesn't support it - it's in the shared border dropdown but not in this algorithm.

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 details below).
ksizeINT0-2147483648–2147483647Aperture parameter for the Sobel operator.
borderTypeoptCOMBOBORDER_DEFAULTPixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported.

Outputs (1)

NameTypeDescription
nparrayNPARRAY—