CV Parse Object Transform
You parked the model by hand — now get that placement into the graph as data
- matrix
- position
- quaternion
- scale
- valid
The pack ships a 3D viewer with a move/rotate/scale gizmo (CV 3D Object Transform), and dragging a model into place is by far the fastest way to decide where it should go. The problem is that a nudge inside a viewer is a widget value, and widgets don't wire into nodes. CV Parse Object Transform is the node that turns that widget into a 4x4 matrix and three vectors, so a placement you found by eye becomes data - the mesh, the annotation anchors and the silhouette all follow the model you parked.
That's the standard ComfyUI value-node pattern, applied to a 3D transform: one authoritative source, many consumers.
What you feed it
object_transform is the JSON string the viewer writes - position, quaternion [x, y, z, w], scale, in Three.js world space. You can wire it from the viewer or just paste it. Scale may be a plain number, missing keys default to identity, and an empty string is identity and counts as valid - that's what an untouched widget holds, so a fresh node doesn't look broken.
space picks the coordinate system for all the outputs: OpenCV (Y down, Z into the scene) or the Three.js convention. Match this to CV Mesh From 3D Model's axis_convention, or the transform you get back won't apply to the mesh you got back - the anchors end up mirrored through the object and you'll spend twenty minutes wondering why. The input string is always Three.js regardless of what you pick here.
What comes out
Five outputs, all in the selected space:
matrix- the 4x4 float64 model matrix, ready forCV Transform Points 3D.position- 1-D[x, y, z]translation.quaternion- 1-D unit quaternion[x, y, z, w]. In OpenCV space it's conjugated by the axis flip, so it still recomposesmatrixtogether withpositionandscale.scale- per-axis[x, y, z]. The gizmo can scale non-uniformly, so this is a vector even though the transform node only emits uniform scales.valid- false when the string couldn't be read, with identity on everything else.
valid is the one to actually wire up. A half-typed JSON string returns identity rather than halting the run - deliberately failure-tolerant, and a silent trap if you don't branch on it: your render quietly puts the model at the origin. Send valid into an If/Else Switch and fall back to a preset placement.
One subtlety worth knowing: what comes back is the whole placement as a single rotation. If the transform node was set with a model_up option, this parses to the combined turn - not to that dropdown plus the rotate_* values separately. So don't try to decompose it back into the widgets that produced it.
Install
Manager → search ComfyUI CV, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
cd comfyui_cv && pip install "opencv-contrib-python-headless~=5.0.0.93"
Restart. Python ≥ 3.12, ComfyUI on the V3 node API. This node is pure Python and JSON - no cv2 class, no model download, no contrib dependency - but the pack it ships in wants the pinned contrib headless wheel.
Common issues
validis false and the model sits at the origin - the widget string is malformed or half-edited. Branch onvalid, and check the string is real JSON before blaming the parse.- The model appears mirrored or rotated 180° -
spaceand the mesh'saxis_conventiondisagree. This is the one mistake this node makes easy, so make it the first thing you check. - Anchors don't ride the object - the anchors have to come from the same model file as the mesh. Different GLB, different geometry, same visual placement.
- Scene 3D nodes going red - some 3D workflows in the pack depend on other packs (Inspire Pack, Custom-Scripts, Basic Data Handling). Install those from Manager if a workflow from the repo complains.
The pack's README is explicit that this is a personal, LLM-assisted project without production support - worth remembering when a viewer widget is the only record of a placement you spent time finding. Copy the JSON somewhere safe.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| object_transform | STRING | The JSON from the viewer's object_transform widget: position, quaternion [x, y, z, w] and scale, in Three.js world space. Scale may be a single number and missing keys default to identity, exactly as the renderer reads it. Empty is identity and counts as VALID - that is what an untouched widget holds. | |
| space | COMBO | OpenCV (Y down, Z into the scene) | Coordinate system for ALL the outputs. Match it to 'CV Mesh From 3D Model'.axis_convention so the matrix applies to that mesh directly; the input string itself is always Three.js. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| matrix | NPARRAY | 4x4 float64 model matrix in the selected space, ready for 'CV Transform Points 3D'. |
| position | NPARRAY | 1-D [x, y, z] translation, in the selected space. |
| quaternion | NPARRAY | 1-D unit quaternion [x, y, z, w] of the rotation, in the selected space (conjugated by the axis flip when that is OpenCV, so it recomposes 'matrix' together with 'position' and 'scale'). |
| scale | NPARRAY | 1-D per-axis [x, y, z] scale. The gizmo can scale non-uniformly, so this is a vector even though 'CV 3D Object Transform' only emits uniform ones. |
| valid | BOOLEAN | False when the string could not be read as an object_transform - the other outputs are then identity. Wire it to an 'If/Else Switch' to fall back to a preset placement instead of rendering the model at the origin. |