cv2.composeRT
Chain two camera poses into one (plus eight Jacobians you'll ignore)
- rvec1
- tvec1
- rvec2
- tvec2
- rvec3
- tvec3
- dr3dr1
- dr3dt1
- dr3dr2
- dr3dt2
- dt3dr1
- dt3dt1
- dt3dr2
- dt3dt2
What it's for
You have two rigid transforms, each written as a rotation vector plus a translation vector, and you want the single transform that does both. That's it. cv2.composeRT is the multiplication step of 3D geometry, and in this pack it shows up wherever a graph builds a trajectory or a rig: relative poses from visual odometry, a camera-to-camera transform, a hand-eye offset you want to fold into a render.
The reason it's worth a node is that rotation vectors are not angles you can add. Two rotations about different axes don't commute, and Rodrigues vectors hide that - add them and you get a plausible, wrong pose. Composition has to go through the matrices. That's what this does for you.
It's a raw wrapper from ComfyUI CV (bmad4ever/comfyui_cv), category image/CV/low-level/cv2 C, and it's a good example of the pack's generator being thorough: it exposes all ten of cv2.composeRT's return values, not just the two useful ones.
How it works
Given the first pose (rvec1, tvec1) and the second (rvec2, tvec2), it returns the composed pose: rotation R2 · R1 and translation R2 · t1 + t2. In English, the first pair is applied first and the second sits on top of it. The order is load-bearing - swap the argument pairs and you get a genuinely different result, not a rounding difference. Wire it deliberately.
The inputs arrive in the order rvec1, tvec1, rvec2, tvec2. Rotation, translation, rotation, translation. Easy to mis-wire when every socket looks like a 3-element array.
Inputs and outputs that matter
All four inputs are required and NPARRAY-only - these are vectors, not pictures, and the sockets reject IMAGE/MASK links:
- rvec1, rvec2 - rotation vectors (Rodrigues form). 3×1 or 1×3.
- tvec1, tvec2 - translation vectors, same shapes.
Outputs - and there are ten, which is a lot of sockets to have opinions about:
- rvec3, tvec3 - the composed pose. This is what you want 99% of the time. Feed it to a renderer, to
projectPoints, or to CV Pose To Matrix to get a 4×4. - dr3dr1, dr3dt1, dr3dr2, dr3dt2, dt3dr1, dt3dt1, dt3dr2, dt3dt2 - the analytic Jacobians of the result with respect to each input. These exist for solver code (bundle adjustment, gradient-based pose refinement). If you don't know why you'd want a Jacobian, you don't want it. Leave them unconnected; unconnected outputs cost nothing.
Two things to know before you wire it
Shape matters to cv2. Rotation and translation vectors need to arrive as proper 3-element column/row arrays. A flat (3,) numpy vector - exactly what a lot of node chains produce - is the classic source of (-215) overload errors here. CV Reshape Array is the fix and the pack's docs call it out as required "before cv2 arithmetic on (3,) rows".
There may be a better-fitting node in the same pack. cv2_composeRT is the primitive. The curated CV Compose Pose (Trajectory) chains relative poses into a whole trajectory, CV Matrix To Pose turns a 4×4 or 3×4 back into rvec/tvec, and CV Matrix Multiply does the same composition in matrix space if you already have 4×4s. Reach for this node when you specifically want the Jacobians or want to stay in vector form for a downstream cv2 call.
Installing the pack
Manager → search ComfyUI CV, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Restart ComfyUI. Requirements: Python ≥ 3.12, a V3 node API ComfyUI, and opencv-contrib-python-headless~=5.0.0.93. Some of the pack's example workflows also want ComfyUI-Inspire-Pack, ComfyUI-Custom-Scripts, or Basic Data Handling - the nodes themselves don't.
Where people get burned
- Argument order. Ten sockets and four near-identical inputs. If your trajectory drifts in a consistent, physically implausible direction, suspect which pair you wired first before you suspect the math.
- Rotation vectors vs rotation matrices. If your pose came out of an ONNX model or a 4×4, convert it (CV Matrix To Pose) rather than reshaping and hoping.
- This is calibration-grade math on an LLM-assisted pack. The README says it plainly: developed with heavy LLM use, some code possibly overfitted to test data, updates not planned, not production-recommended without your own review. For trajectory work - where a wrong pose is invisible until the whole sequence is wrong - do a sanity check against a known-good pair, e.g. compose a transform with its own inverse and confirm you get near-identity back. There's no community corpus behind this pack to catch you: the corpus search for it comes up empty.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| rvec1 | NPARRAY | First rotation vector. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| tvec1 | NPARRAY | First translation vector. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| rvec2 | NPARRAY | Second rotation vector. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| tvec2 | NPARRAY | Second translation vector. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. |
Outputs (10)
| Name | Type | Description |
|---|---|---|
| rvec3 | NPARRAY | — |
| tvec3 | NPARRAY | — |
| dr3dr1 | NPARRAY | — |
| dr3dt1 | NPARRAY | — |
| dr3dr2 | NPARRAY | — |
| dr3dt2 | NPARRAY | — |
| dt3dr1 | NPARRAY | — |
| dt3dt1 | NPARRAY | — |
| dt3dr2 | NPARRAY | — |
| dt3dt2 | NPARRAY | — |