CV Stack Feature Classes
Building a labeled training set out of feature arrays
- class_a
- class_b
- class_c
- class_d
- features
- labels
- count
- class_count
Most of this pack is measurement. This node is the start of the one thing measurement feeds into: training a small classifier inside a ComfyUI graph. It takes per-class feature arrays and produces the (features, labels) pair that CV Train Classifier wants.
There's something satisfying about the shape of it. CV HOG Features, CV DeepFeatures or plain point arrays give you descriptors; CV Stack Feature Classes attaches the ground truth; CV Train Classifier fits; CV PredictClassifier runs it. No Python script, no notebook, no dataset folder - the training set is the wire between two nodes, and the labels come from which input slot you wired it to.
How it works
Class membership is the slot. Rows from class_a get label 0, rows from class_b get label 1, and so on. There's no label widget, no naming step, no mapping to maintain - the graph itself is the label definition, which is the most ComfyUI-shaped solution to the problem there could be.
Each input is flattened to one row per sample, and the tooltip is explicit that this is agnostic about what the features are: points, HOG rows, deep embeddings all work. Every connected input must yield the same feature length, because concatenating them is the point.
What you set
Two required inputs - class_a (label 0) and class_b (label 1) - and two optional ones, class_c and class_d, for the three- and four-class cases. Leave the optional ones unconnected rather than feeding them something empty and you'll get a cleaner label space.
An empty class is valid: it contributes no rows. That matters in practice, because a feature extractor that found nothing in one category shouldn't be a hard error in a graph you're iterating on - the same house style as the found flags elsewhere.
Outputs:
- features -
(N, D)float32, all class rows concatenated in class order. - labels -
(N,)int32, aligned with the rows. 0 for class_a, 1 for class_b, and so on. - count - the total sample count N.
- class_count - how many class inputs are connected. Handy as a sanity check when you're building a graph by duplicating slots.
features and labels go into CV Train Classifier; the labels also color CV Draw Points, which is the cheap way to see whether your features are separable at all before you spend time training.
The honest caveat
This is classical machine learning on hand-designed features, not a neural network, and the KB's framing of the mask-and-measure layer applies: it's deterministic, instant, and precise within its domain - and its domain is your data. A HOG + SVM classifier trained on fifteen examples of a bolt head will classify bolt heads remarkably well and will not generalize to a photograph of a different bolt. That's not a flaw in the node, it's the tradeoff. For "sort these three logo shapes in a batch of scans" it's the right tool and far lighter than the alternative. For "understand images" it isn't a tool at all.
Install
ComfyUI Manager → ComfyUI CV → install → restart. Manual:
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 on the V3 node API, no model downloads - the trainable model is the one you're about to fit. Registered under image/CV/ml, next to the feature extractors and the save/load pair (CV Save Classifier / CV LoadLabels exist for round-tripping the result).
Common issues
- Feature-length mismatch between classes. The message is about shapes and the cause is almost always that two classes went through different extractors, or through the same extractor at different image sizes. HOG descriptor length depends on your window and cell settings - check those before the data.
- Labels don't line up with what you expected. Label order is slot order: a=0, b=1, c=2, d=3. If you swapped the two wires you swapped the classes, and the classifier will happily learn it backwards.
countis 0. Every class input was empty. That's a valid result and a useless training set; the pack won't stop you, andCV Train Classifierwill be the one to complain.- Category missing after a pip install. Non-contrib OpenCV wheel over the contrib one in the shared
site-packages/cv2.python tools/repair_opencv_contrib.py --check, then--apply.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| class_a | NPARRAY | Feature array of class 0: N samples, any shape - flattened to one row per sample. | |
| class_b | NPARRAY | Feature array of class 1 (same feature length as class_a). | |
| class_copt | NPARRAY | Feature array of class 2 (optional). | |
| class_dopt | NPARRAY | Feature array of class 3 (optional). |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| features | NPARRAY | (N, D) float32: all class rows concatenated in class order. |
| labels | NPARRAY | (N,) int32: 0 for class_a rows, 1 for class_b, ... aligned with features. |
| count | INT | Total sample count N. |
| class_count | INT | How many class inputs are connected. |