Nodes/ComfyUI CV/CV Decompose Projection Matrix (RQ)
ComfyUI Node

CV Decompose Projection Matrix (RQ)

You have a camera matrix — here's how to get intrinsics and rotation back out

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
CV Decompose Projection Matrix (RQ)
  • matrix
  • matrix
  • Q
  • R
  • Qx
  • Qy
  • Qz

CV Decompose Projection Matrix (RQ) is the node you reach for when you have a 3x3 or 3x4 matrix and you want to know what's inside it. It's cv2.RQDecomp3x3 with a readable face on it: it splits the matrix into an upper-triangular part and an orthogonal rotation, and hands you OpenCV's own per-axis rotation matrices as a bonus.

This is the node for inspecting a calibration, checking that a homography you estimated is a rotation and not a shear, or pulling the K-like block out of a full P = K[R|t] projection matrix that some upstream step produced.

What the two outputs actually mean

The decomposition itself is M = R @ Q. Feed it a 3x3 and R is upper-triangular, Q is orthogonal. Feed it a full 3x4 projection matrix and only the left 3x3 block is touched - and here's the naming trap that catches nearly everyone: for a camera matrix that block is K @ R, so the R output is your intrinsics and the Q output is the camera rotation. The names read backwards from the way photographers talk, which is why the node description spells it out.

The matrix output is the block that was actually decomposed, returned as float64. That sounds like a boring passthrough and it mostly is, but it's the one to wire into R @ Q comparisons when you want to prove the split was lossless. Translation is not recovered from a 3x4 - for that you want CV Solve PnP (Pose) or CV Pose To Matrix.

The inputs and outputs that matter

There is exactly one input, matrix, and it takes a 3x3 or a full 3x4 projection matrix as an NPARRAY. Give it a 4x4 and you'll get an error rather than a guess, which is the right behaviour.

Six outputs:

  • matrix - the 3x3 block that was decomposed, float64.
  • Q - the 3x3 orthogonal rotation.
  • R - the 3x3 upper-triangular part, K-like when the input was a camera matrix.
  • Qx, Qy, Qz - OpenCV's individual axis-rotation matrices. Read the tooltip on these before you multiply them: OpenCV reduces the matrix by right-multiplying, so Qx @ Qy @ Qz gives you Q transposed. The identity that actually holds is Q = Qz.T @ Qy.T @ Qx.T. If your rotation comes out as a plausible-looking inverse, this is why.

The rotation outputs are the useful ones in a graph - pipe Q into CV Matrix To Pose, and dump R into Inspect CV Data or Parse Matrix to read the focal lengths off K[0,0] and K[1,1].

Installing it

It ships in comfyui_cv (bmad4ever/comfyui_cv), a Computer-Vision pack that wraps OpenCV for ComfyUI. Find it in ComfyUI Manager by searching "ComfyUI CV", or do it by hand:

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv

then restart ComfyUI. One dependency, and the version is pinned on purpose:

pip install "opencv-contrib-python-headless~=5.0.0.93"

The pack needs Python ≥ 3.12 and a recent ComfyUI on the V3 node API. Behavior is curated against OpenCV 5.0.0.93, so other versions may behave differently. Contrib matters - installing a non-contrib opencv-python wheel over a contrib one silently empties the contrib submodules and those nodes vanish. tools/repair_opencv_contrib.py --check diagnoses it, --apply fixes it.

Common issues

  • Everything you get back looks transposed. You multiplied Qx, Qy, Qz in the obvious order. See above - it's Qz.T @ Qy.T @ Qx.T.
  • You expected a translation. A 3x4's fourth column is ignored by design. Use the pose nodes.
  • R doesn't look like intrinsics. Check what kind of matrix you fed in. A homography decomposes into a triangular and an orthogonal part too, but it isn't K; CV Decompose Homography is the node that knows the difference.

One honest note about this pack, since it applies to everything below: the README states the code was written with heavy LLM assistance and warns about test-driven overfitting during development. Individual nodes like this one are thin wrappers around a single, well-tested OpenCV call, which is the safest category in the whole pack - but don't ship a production pipeline on it without reading the source you depend on.

Categoryimage/CV/features

Inputs (1)

NameTypeDefaultDescription
matrixNPARRAYMatrix to decompose: either a 3x3 matrix, or a full 3x4 camera projection matrix P = K[R|t] (only its left 3x3 block is decomposed; the translation column is ignored).

Outputs (6)

NameTypeDescription
matrixNPARRAYThe 3x3 block that was actually decomposed, as float64 (the input itself for a 3x3 matrix, its left 3x3 block for a 3x4 one). R @ Q reconstructs exactly this - handy for matrix comparisons and inspectors.
QNPARRAY3x3 orthogonal matrix from the RQ decomposition.
RNPARRAY3x3 upper-triangular matrix from the RQ decomposition (K-like for camera matrices).
QxNPARRAYOpenCV's x-axis rotation matrix component. NOTE the composition order: OpenCV reduces M by RIGHT-multiplying these, so Qx @ Qy @ Qz is Q TRANSPOSED - the identity that holds is Q = Qz.T @ Qy.T @ Qx.T (exactly, and M = R @ Qz.T @ Qy.T @ Qx.T).
QyNPARRAYOpenCV's y-axis rotation matrix component. Composes as Q = Qz.T @ Qy.T @ Qx.T, not Qx @ Qy @ Qz (see Qx).
QzNPARRAYOpenCV's z-axis rotation matrix component. Composes as Q = Qz.T @ Qy.T @ Qx.T, not Qx @ Qy @ Qz (see Qx).