cv2.ximgproc.createQuaternionImage
The doorway into colour Fourier, which almost nobody walks through
- img
- nparray
A quaternion is a four-component number. You can think of a colour pixel as one: three colour channels plus a component to spare. That's the entire premise here - instead of transforming the red, green and blue planes separately, you pack them into a single quaternion image and transform the whole thing at once, so the colour relationships between channels survive the transform instead of being computed independently and stitched back together badly.
It's elegant, it's in ximgproc, and it's one of the least-used corners of OpenCV. The reason to know it exists is a real one: quaternion Fourier phase correlation gives you colour-aware matching and registration - the kind that can find a translated and rotated copy of an image where a grayscale correlation would be fooled by a colour change. The reason to be cautious is that nothing else in the ComfyUI ecosystem speaks this format, so you're on your own for the whole pipeline.
What the node does
Takes an image and returns an NPARRAY that is no longer a picture: it's a 4-channel float array where the four channels are the quaternion's components. That's it - this node doesn't process anything, it converts. Everything interesting happens in the family around it, which this pack also exposes:
cv2.ximgproc.qdft- the quaternion discrete Fourier transform.cv2.ximgproc.qconj- quaternion conjugate.cv2.ximgproc.qmultiply- quaternion multiplication, i.e. how you correlate.cv2.ximgproc.qunitary- unitary quaternion (used in the transform's setup).cv2.ximgproc.fourierDescriptor/transformFD/PeiLinNormalization- the neighbouring descriptor family, for shape rather than colour.
The canonical pipeline is: quaternion image → transform both the reference and the search image → multiply one by the conjugate of the other → inverse transform → find the peak, which is the translation between them. In polar/log-polar coordinates the same trick extends to rotation and scale, which is where it gets genuinely powerful.
Honest labelling: the pack ships no tooltip text for this function (auto-generated wrapper, thin upstream docs) and none of the example workflows use it. So the first thing to do with this node is not build a pipeline but look at the output:
Load Image → cv2.ximgproc.createQuaternionImage → Inspect CV Data
Inspect CV Data reports shape, dtype and value statistics of any array, which tells you the channel layout and the value range you're working with - the two things you need before you feed it to qdft and start interpreting results.
Inputs and output
img- the source image.NPARRAY,IMAGEorMASK, frame 0 of a batch.- Output - one
NPARRAY, the quaternion image. Data, not pixels: don't wire it into a preview and expect a picture.
Why bother?
Two defensible reasons.
Colour-aware registration. If you're aligning frames that differ in colour grade or exposure, or matching a template whose colour is part of its identity, a transform that treats the three planes jointly is a strictly better tool than three independent correlations. This is the same family of reasoning as cv2.ximgproc.colorMatchTemplate, which exists for the same reason.
Structure/detail work at the transform level. The pack is full of spectral tools - CV DFT Flags, cv2_dft, cv2_mulSpectrums, CV Phase Correlate - and quaternion Fourier is the colour extension of that toolbox. If you're already working in the frequency domain and colour is signal rather than appearance, this is the right representation.
That said: if what you actually want is "find this logo in this frame", reach for cv2.ximgproc.colorMatchTemplate or the curated CV Match Features. The quaternion route is for people who have already run out of simpler options.
Installing it
Contrib module, part of ComfyUI CV. Manager → search ComfyUI CV, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
# restart ComfyUI
Python ≥3.12 and a recent, V3-API ComfyUI. Quaternion functions live in ximgproc, so a non-contrib OpenCV wheel will leave the whole family as empty stubs - that's a single shared site-packages/cv2 directory across all four opencv-python* distributions. tools/repair_opencv_contrib.py --check in the pack repo tells you which you have.
What goes wrong
- Wiring the output somewhere expecting an image. It's a four-channel float array.
CV Array → Imagecan render arrays, but the result will look like nonsense or a normal map, which is expected. - Odd-sized images and float precision. Frequency-domain tricks care about both. Pad to a power-of-two-friendly size if results look unstable;
cv2.getOptimalDFTSizeis in the pack for that. - Interpreting the first channel as brightness. The components aren't BGR in that order. Inspect before you index.
- Missing neighbours. If
createQuaternionImageworks butqdftdoesn't exist in your menu, you have a partially contrib-installed environment. Same diagnosis, same repair script.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| img | 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 | — |