CV Stereo Calibration From CSV
Already Have Calibration in a CSV? Skip the Chessboard Entirely
- K_left
- dist_left
- K_right
- dist_right
- R
- T
- found
Most stereo pipelines on the internet assume you shot your own calibration board. Some sets - StereoGeo-CARLA being the pack's own example - ship the calibration as a per-image table instead: camera intrinsics and the rig's rotation/translation, one row per frame. This node reads those rows and hands you the same six arrays a chessboard calibration would, with no board, no waving, and no corner detection.
That last part is the real pitch. Everything downstream - cv2_stereoRectify, then CV Stereo Disparity (SGBM) - cares about K_left, dist_left, K_right, dist_right, R, T. Where they came from is nobody's business. If a dataset gives you those numbers already, calibrating is wasted work.
What it reads, and how it builds K
Two CSVs, left and right, both keyed on image_name. The left file carries image_name, width, height, roll, pitch, vfov. The right file has the same columns plus the nine rotation entries R_00..R_22 and the translation t_0..t_2.
The intrinsics are a plain pinhole built from the vertical FOV: fy = (height/2) / tan(vfov/2), with fx = fy and the principal point dropped at the image centre (cx = width/2, cy = height/2). Both distortion vectors come back as five zeros, because this model has no distortion term at all. In other words it trusts the source: if your CSVs came from a real physical camera with a fisheye-ish lens, the whole panorama of that distortion is invisible to the node, and rectification will be approximate.
Note which width and height are actually used. The node has its own image_width / image_height widgets (640 × 480 by default) and it's those that set the principal point and the focal length. The width/height columns in the CSV are read as part of the row but don't drive the K. If your frames aren't 640 × 480, change the widgets - this is the one setting people forget.
What you actually set
csv_path_left and csv_path_right: a bare filename resolves against ComfyUI/input/, and absolute paths pass straight through. So left_images.csv and right_images.csv just work once they're in your input folder - the pack's 01_install_example_inputs.json copies exactly those two files, along with the matching stereo_scene_L_000000.png / ..._R_000000.png frames.
image_name is the lookup key, e.g. 000000.png. It has to be present in both CSVs.
image_width and image_height are your frame size. See above.
Outputs are K_left, dist_left, K_right, dist_right, R, T - and found.
Install
ComfyUI Manager → search ComfyUI CV (publisher bmad4ever), or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Restart after. Python ≥ 3.12 and a recent V3-API ComfyUI. Keep the contrib wheel - a plain opencv-python install overwrites the shared site-packages/cv2 and silently empties the contrib submodules. tools/repair_opencv_contrib.py --check diagnoses it.
Where it bites
found=false is the whole failure mode, and it's quiet: if image_name isn't in both CSVs you get found=false along with identity matrices and a zero translation. Wire that flag into your branching, because a zero baseline downstream produces a perfectly plausible-looking depth map with completely wrong scale - the worst kind of wrong.
Second: the CSV's roll and pitch columns are in the format but the node only reads vfov to build K; there's no camera-tilt term in the intrinsics it produces. That's fine for these datasets, surprising if you assumed otherwise.
And the general pack caveat applies here more than anywhere: bmad4ever's README says outright that the stereo workflows are tuned to StereoGeo-CARLA and aren't production-grade. This node is the reason that's worth saying out loud - it's built for a specific dataset's CSV dialect. If your columns are named differently, no amount of fiddling in the graph will fix it.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| csv_path_left | STRING | Left camera CSV. Bare filename resolves against ComfyUI/input/; absolute paths pass through. | |
| csv_path_right | STRING | Right camera CSV. Bare filename resolves against ComfyUI/input/; absolute paths pass through. | |
| image_name | STRING | Image filename to look up (e.g. '000000.png'). | |
| image_width | INT | 640 | Image width in pixels (used for cx = width/2). |
| image_height | INT | 480 | Image height in pixels (used for cy = height/2). |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| K_left | NPARRAY | 3x3 intrinsic matrix for the left camera. |
| dist_left | NPARRAY | 1x5 distortion coefficients (zeros for pinhole). |
| K_right | NPARRAY | 3x3 intrinsic matrix for the right camera. |
| dist_right | NPARRAY | 1x5 distortion coefficients (zeros for pinhole). |
| R | NPARRAY | 3x3 rotation matrix from left to right camera. |
| T | NPARRAY | 3x1 translation vector from left to right camera. |
| found | BOOLEAN | — |