Nodes/ComfyUI CV/cv2.PCACompute (1/2)
ComfyUI Node

cv2.PCACompute (1/2)

Find the axes your data actually lies along

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
cv2.PCACompute (1/2)
  • data
  • mean
  • mean
  • eigenvectors
◄maxComponents0►

Principal component analysis, the 120-year-old one. You have a cloud of samples - points, HOG vectors, deep embeddings, whatever - and you want the directions they spread along. cv2's version is a direct implementation: rows are samples, columns are features, and it hands back the mean plus the eigenvectors, sorted by how much variance they carry.

In a ComfyUI graph this is the "understand the data" node, and you'll know you need it when you have an NPARRAY of features and no idea which axes matter. The pack's curated shape nodes already fold PCA in internally - CV Shape Moments emits orientations, eccentricities and axes for exactly this reason - so reach for the raw wrapper when the matrix is yours (from CV HOG Features, CV Deep Features, CV Stack Feature Classes, CV Coordinate Grid, CV Points, or a hand-built array) and you want to reduce it or rotate into its own frame.

The two inputs and the two outputs

data is the matrix, N x M: N samples, M features per sample. cv2 requires float32 or float64 - CV Cast Array if you're not there already - and cv2 is rigid about the layout. If your array is transposed, PCA of the transpose is a valid but entirely different computation, and nothing will tell you.

mean is the interesting one. In cv2, mean is an input/output argument, not a return value: the Python API writes the computed mean out through the array you passed in. The generated wrapper makes it a required NPARRAY socket, so unlike in Python you can't just leave it empty - you have to hand it something. What you pass is the centroid cv2 works around, so a zero vector is not a harmless placeholder; it's a different question ("variance about the origin"). The safe pattern is to feed it the mean from a previous run, or the same array you're about to project - and above all to use the mean output of this node for anything downstream. The reason mean is a socket rather than a hidden detail is that PCAProject and PCABackProject need the same centroid; mismatch them and the round trip doesn't return what you put in.

maxComponents is optional (advanced input) and preset to 0, which is OpenCV's "keep everything". Set it to 2 and you get two rows of eigenvectors - the two biggest - which is the compression step. The (2/2) variant of this node takes retainedVariance instead of a count; same call, different stopping rule.

Outputs are mean (1 x M) and eigenvectors (K x M), rows ordered by decreasing variance. Both are NPARRAY.

Using them

The useful pair is PCACompute → PCAProject → your classifier. Project into the low-dimensional space, train on the small version, and keep the mean and eigenvectors around so you can project a query the same way. Going back the other way - PCABackProject - reconstructs the original space from projected data, which is what you'd use to measure reconstruction error, i.e. "how much did I throw away". For a sanity check on any of this, Inspect CV Data and CV Array Shape tell you what you're actually holding.

PCA also gives you a free orientation for a blob of pixels: feed it the pixel coordinates of a mask via CV Contour To Points, and the first eigenvector is the blob's major axis. That's the mechanism behind the orientations output of the pack's contour/moment nodes - worth knowing when you'd otherwise be rebuilding it.

Gotchas

Rows are samples. cv2's Python binding exposes PCACompute(data, mean, maxComponents) with the data-as-rows convention. A 512-features-per-image matrix built as M x N will "work" and be meaningless.

It's SVD of a covariance matrix, so scale matters. If your features are in wildly different units (pixels next to 0-1 intensities), the largest-unit feature dominates the first component and you'll conclude that PCA is useless. Normalise first - a CV Cast Array won't do that, but an arithmetic wrapper like cv2.divide or cv2.normalize will.

No batch loop. Frame 0 of an IMAGE, if you link one, and one array out. This is a matrix node; it doesn't know about frames.

Installing

ComfyUI Manager → search ComfyUI CV → Install → restart, or:

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, recent ComfyUI (V3 node API), OpenCV 5.0.0.93 as the curated reference. PCACompute is core cv2, so no contrib dependency for this particular node - no models either.

Categoryimage/CV/low-level/cv2 P

Inputs (3)

NameTypeDefaultDescription
dataNPARRAY - - - A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here.
meanNPARRAY - - - A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here.
maxComponentsoptINT0-2147483648–2147483647 - - - Preset to the OpenCV default (0).

Outputs (2)

NameTypeDescription
meanNPARRAY—
eigenvectorsNPARRAY—