cv2.ximgproc.qunitary
Keep the phase, throw away the magnitude (in colour)
- qimg
- nparray
qunitary normalises every quaternion in a 4-channel array to unit length - divide each pixel's quaternion by its own modulus. That's it. One input, no parameters, one output.
The reason a vision library wants it: once every value is a unit quaternion, only phase information survives, and magnitude is gone. That's the quaternion-domain version of phase-only filtering, which is how you get correlation peaks that are sharp and insensitive to brightness or exposure. If you're heading towards phase correlation between two colour images, or a quaternion colour-edge operator, this is the normalisation step in the middle of the recipe.
Inputs and outputs
- qimg - a 4-channel quaternion array, i.e. the output of the generated
cv2.ximgproc.createQuaternionImagewrapper (BGR in,(real, B, G, R)out). A normal three-channel IMAGE isn't valid input; the wholeq*family is defined over four planes. - nparray - the unit-quaternion array, same shape and layout, as a plain NPARRAY. It is not an image and there's no channel for an image converter to use correctly; keep it in the quaternion lane (another
q*node, orCV Slice ArrayplusCV Array → Imageif you only want to eyeball one plane).
Watch out for the all-zero pixel case: normalising a quaternion whose modulus is zero has nothing to normalise. Black areas of a frame are exactly that, so if you see odd values in the shadows, that's where they came from - not from the wrapper misbehaving.
Where it fits
Typical chain: createQuaternionImage → qdft forward → qunitary on the spectrum (whitening it) or on one operand → qmultiply with the other side's transform → qdft with DFT_INVERSE, using the same sideLeft you used going forward, or the round trip doesn't close → take the real part or a magnitude → CV Array → Image.
The sideLeft pairing rule applies to the whole family, and it's the single most common way a quaternion pipeline silently produces the wrong answer: left- and right-sided transforms are genuinely different operators, and each has to be undone by its own inverse.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Restart ComfyUI, or install ComfyUI CV through ComfyUI Manager. Python ≥3.12, a recent ComfyUI on the V3 node API, opencv-contrib-python-headless~=5.0.0.93 - no model files. qunitary is contrib, so it only exists in a contrib OpenCV build: if a plain opencv-python wheel has been installed over the contrib one, the node disappears from the menu and tools/repair_opencv_contrib.py --check is how you confirm that's the cause.
Common issues
No visible change. You normalised something that was already unit-length, or you're looking at a preview of the wrong plane.
Blown-out pixels where the image is black. Zero-modulus quaternions - see above. Mask or clamp before normalising if your downstream maths can't tolerate it.
You wanted colour edges and this feels like a detour. It is one. The pack also ships the simple versions of these jobs: plain Canny, Sobel, Laplacian and the curated detect-lines/contours nodes are all present and all easier. Reach for the quaternion family only when a specific algorithm calls for it - the nodes exist because the pack wraps substantially all of cv2 and ximgproc, not because anyone thinks a colour edge map needs a hypercomplex transform.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| qimg | NPARRAY,IMAGE,MASK | - - - 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. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |