Nodes/ComfyUI CV/cv2.HuMoments
ComfyUI Node

cv2.HuMoments

Seven numbers that recognise a shape after you rotate it

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
cv2.HuMoments
  • m
  • hu

Hu moments are shape fingerprints from 1962 and they still work. Seven values, computed from the raw image moments of a raster or point set, invariant to translation, scale and rotation - so a logo, a screw head, or a blob silhouette measures nearly the same whether it is at the top-left corner or rotated 40 degrees and half the size. That is an extraordinary property for seven numbers, and it is why the technique outlived the era it came from.

Where you use this in a ComfyUI graph: you have contours, masks or blobs and you need to decide which ones are the same kind of thing. cv2.matchShapes - also in this pack as a raw wrapper - compares exactly these invariants between two shapes and returns a distance, so the common pattern is: threshold or segment, extract contours, compute moments, then either match against a reference or cluster the descriptors. Shape filtering is the deterministic alternative to training a detector, and it is the honest tool when your target has a stable silhouette and your data does not justify a model (masking-detection-detailing.md is about the detector world this can replace on the easy cases).

How it works

cv2.HuMoments(m) takes the named moments - the dict cv2.moments returns - and derives the seven invariants from it. That naming detail is the entire interface here, and the author flags it in the tooltip: cv2 reads them by name (m00, m10, …), so a plain list of the same numbers would silently produce zeros. The node's input type is the moments type the pack's cv2.moments node emits, not a generic array.

You get raw rasters via the raw cv2.moments node, whose optional binaryImage flag tells OpenCV to treat the input as a binarised image (the right setting when you are feeding a mask, not a grayscale intensity image).

Output is one nparray: the seven Hu moments, float64, in a single row. Compare them after taking log10(abs(value)) - the raw numbers span many orders of magnitude, and the sign information you want to keep is easier to reason about post-log.

Inputs and output

  • m - the moments. Wire the moments output of cv2.moments in. The socket also accepts a string, which is how the pack lets you paste a saved moments literal; the tooltip is explicit that it must be the named dict, because a bare list of 24 numbers fails quietly rather than loudly.
  • Output hu - the seven values.

What to wire hu into: CV Array To Text to read them, Preview CV Array to look at the distribution across a batch of shapes, cv2.norm for a quick L2 between two hu vectors, or the pack's own distance/clustering nodes if you want to group shapes rather than compare pairs. For a one-liner comparison, cv2.matchShapes takes two contours and does the moment comparison internally.

The curated alternative, and when to prefer it

CV Shape Moments computes moments and Hu moments per contour from a contour set and adds what a shape pipeline usually needs next - a canonical 0–360 orientation, signed chirality, and a pose confidence. CV Image Moments is the raster-intensity counterpart, output-for-output parallel. CV Match Image Moments goes further and compares a reference against a whole batch, returning distances, a keep-mask, the best index and an invariance policy - literally the loop most people rebuild by hand around this raw node. Use the raw cv2.HuMoments when you already hold a moments dict or want the seven numbers as an array to do your own math on; use the curated nodes when you want the comparison answer.

Also from the pack's README: the 5th and 6th Hu moments were initially dropped because the development test suite had no case covering them. The fix landed, but if you ever see fewer than seven values from an older copy, that is the bug family.

Install

Manager → search comfyui_cv (bmad4ever), 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 and a recent ComfyUI on the V3 node API. No models, one pinned dependency, and the pack is a fork of geroldmeisinger/opencv-comfyui rebuilt on the newer V3 node API.

When it goes wrong

  • Zeros out of the node. You passed an array instead of the named moments dict. This is the documented failure mode and it is silent, which is why the tooltip shouts about it.
  • Two shapes that are obviously identical give different values. Check whether you fed moments computed over a raster versus a point set, and whether binaryImage was set consistently. Scale invariance is near, not exact, on small blobs - a 12-pixel shape has quantisation noise that swamps the invariants.
  • Mirrored shapes look different. The seventh invariant flips sign under reflection; that is a property, not a bug, and it is what CV Shape Moments exposes as signed chirality rather than hiding.
  • Not invariant to perspective or occlusion. Hu moments handle rotation, scale and translation. Bend the shape or cut a bite out of it and the fingerprint moves - use them as a coarse filter, not an identity check.
Categoryimage/CV/low-level/cv2 H

Inputs (1)

NameTypeDefaultDescription
mCV_MOMENTS,STRING - - - The NAMED image moments from cv2.moments - wire that node's output in. cv2 reads them by name (m00, m10, ...), so a plain list of the same numbers would silently give zeros.

Outputs (1)

NameTypeDescription
huNPARRAY—