cv2.triangulatePoints
Two views in, 3D points out (and a 4-row array you have to divide)
- projMatr1
- projMatr2
- projPoints1
- projPoints2
- nparray
Triangulation is the moment a stereo or two-view pipeline stops being about pixels and starts being about space. You have the same feature seen in two images, you have the geometry that relates the two cameras, and you want the 3D point that both observations agree on. cv2.triangulatePoints is OpenCV's linear (DLT) answer: feed it the two projection matrices and the two point sets, get back world points.
It's the step right before a point cloud, a depth estimate you computed yourself, or a 3D reconstruction that you then render with the pack's 3D viewer nodes.
One of roughly 470 auto-generated raw cv2.* wrappers in ComfyUI CV (bmad4ever/comfyui_cv) - uncurated by design, LLM-generated, don't ship it without reading it.
Inputs
Four required inputs, all NPARRAY-only data arrays:
projMatr1,projMatr2- 3x4 projection matrices for the two cameras, each projecting world points into one image. In a calibrated stereo pipeline these areP1andP2from cv2.stereoRectify; in a two-view pipeline you'd assemble them fromK [R|t].projPoints1,projPoints2- 2xN arrays of corresponding image points, same order in both. This shape matters:Nx2will not do, and the layout difference is the single most common reason a working pipeline suddenly throws. cv2.transpose is the one-node fix.
Output is a single nparray, and here's the part that catches people: it's 4xN homogeneous, not 3xN. To get real 3D coordinates you divide the first three rows by the fourth - the homogeneous coordinate. cv2.convertPointsFromHomogeneous does it for you, and the pack wraps that function too; the array nodes (CV Array To Numbers, CV Reshape Array, arithmetic wrappers) can do it by hand if you'd rather see the division happen.
The catch nobody mentions
triangulatePoints performs no cheirality check. It will happily return points behind the cameras if that's what the linear algebra says, and they look like ordinary coordinates. In a real reconstruction you have to filter them: a valid point has a positive depth in both cameras, and the standard way to test that is to project it back - cv2.projectPoints in each view - and keep only the ones that land in front and reproject near where you observed them.
That's not a flaw in the wrapper, it's the nature of the linear method, and it's why the calibrated stereo examples in the pack that end in point clouds carry their own validity filtering.
There's a curated node
Worth knowing before you wire this one: the pack also ships CV Triangulate Points (Two-View), a curated node that takes two point sets, one camera matrix, and the relative rotation and translation from CV Recover Pose (Essential Matrix), and outputs Nx3 points in view-1 camera coordinates, plus a valid mask ("1 = finite and in front of both cameras") and a mean reprojection error in pixels. It does the cheirality check, the homogeneous division and the diagnostics for you.
So: use the curated node when you have K, R and t and want usable points with an honesty check attached. Use cv2.triangulatePoints when you specifically have two projection matrices - the stereo case, where P1/P2 came out of stereoRectify - or when you want the raw 4xN form for your own maths.
Installing the pack
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"
Manager → search ComfyUI CV → install → restart. Python ≥ 3.12 and a ComfyUI with the V3 node API, or the nodes don't appear. The OpenCV wheel has to be contrib - all four distributions share one site-packages/cv2, so installing plain opencv-python over it empties the contrib submodules (tools/repair_opencv_contrib.py --check then --apply). Curated against 5.0.0.93; updates aren't planned.
Where it bites
Nx2 points instead of 2xN - transpose them. Float dtype mismatches between the projection matrices and the points; cast both to CV_64F with CV Cast Array if cv2 complains. Point ordering differences between projPoints1 and projPoints2, which produces confidently wrong 3D rather than an error. And the homogeneous form: reading row 0..2 as metres and finding everything is 40x too big because you skipped the division by row 3.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| projMatr1 | NPARRAY | 3x4 projection matrix of the first camera, i.e. this matrix projects 3D points given in the world's coordinate system into the first image. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| projMatr2 | NPARRAY | 3x4 projection matrix of the second camera, i.e. this matrix projects 3D points given in the world's coordinate system into the second image. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| projPoints1 | NPARRAY | 2xN array of feature points in the first image. In the case of the c++ version, it can be also a vector of feature points or two-channel matrix of size 1xN or Nx1. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| projPoints2 | NPARRAY | 2xN array of corresponding points in the second image. In the case of the c++ version, it can be also a vector of feature points or two-channel matrix of size 1xN or Nx1. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |