cv2.omnidir.initUndistortRectifyMap
Rectify a fisheye without the edge-pinching
- K
- D
- xi
- R
- P
- size
- nparray_0
- nparray_1
cv2.omnidir is OpenCV's implementation of the unified omnidirectional camera model - the one that can describe a lens or mirror with a field of view past 180°, which pinhole-plus-Brown-Conrady cannot. The pack's curated nodes call it CMei, after the Mei–Rives formulation, and if you've ever calibrated a fisheye with cv2.fisheye, this is the sibling model: an extra parameter xi (the mirror parameter) that slides between a pinhole (xi = 0) and a strongly curved mirror or fisheye (xi around 1), usually landing somewhere in the 0.5–1 range in practice.
This node is the one that builds the mapping tables. You give it a calibration and a decision about what projection you want the output in, and it hands back two NPARRAYs - unnamed, nparray_0 and nparray_1, holding what the underlying call calls map1 and map2 - which you feed to cv2_remap alongside the image. Nothing is rectified until the remap happens; these are recipes.
That split is the whole point, and it's why you'd use this rather than cv2_undistort: the maps don't depend on the image. Build them once, remap every frame of a clip with them. The pack even ships an exercise_efficient_video_remap example for the pattern.
The inputs
K, D, xi, R and P are all arrays. K is your 3×3 intrinsics; D is the omnidir distortion vector - expect four values ({k1, k2, p1, p2}), not the five-to-eight the pinhole model uses; xi is the mirror parameter and travels as an array here, which is not a typo (it's how the C++ signature reads - the sibling projectPoints takes the same quantity as a plain float, which is a nice example of why the generated layer exposes argument order rather than meaning). R is the rectification rotation - pass identity if all you want is undistortion. P is the new camera matrix of the virtual pinhole camera you're projecting onto, which is where the pack's CV Camera Matrix bridge node comes in handy (it builds a 3×3 K from fx/fy/cx/cy). Parse Matrix is the other way to get any of these into the graph from calibration text.
size is a CV_TUPLE of (w, h), and it's an input rather than something derived, so a (0, 0) here means an empty output. m1type and flags are bare integers with no dropdown: type 5 for CV_32FC1 (two float32 maps, the standard) or 11 for CV_16SC2 (a fixed-point pair, which remap runs faster), and flags picks the projection you're rectifying to. That last one is the interesting choice - perspective is the default assumption, but the module also offers cylindrical and stereographic output, and for a 180° lens stereographic is how you keep the edges from stretching into infinity. Look up the module's constants; they're not in the tooltips.
Why you'd want a non-perspective output
Stated plainly, because it's the reason to reach for omnidir over the pinhole path: a 180° fisheye forced onto a flat perspective plane has unbounded magnification at the frame edge. Peripheral pixels that occupied a few degrees get smeared across hundreds. Rectifying to stereographic (or cylindrical, for a panorama-ish strip) keeps that stretch bounded and gives you a view you can actually feed to a detector, a stitcher, or your eyes.
Install
ComfyUI Manager → search ComfyUI CV → install → restart, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Python ≥ 3.12, ComfyUI on the V3 node API. cv2.omnidir is contrib-only - no contrib wheel, no node - and the pack's CV Build Information node will confirm what your installed build actually exposes. The cv2.omnidir tooltips in the UI are empty blanks; that's the generator finding no documentation in the type stubs, not a broken install.
Practical notes
- You need a real calibration. The pack's
CV Omnidir Calibrate (Chessboard)(and the54_omnidir_calibrationexample) produceK,Dandxi; guessing them produces a warped mess. - Maps are reusable, images are not. Build once per camera-and-output-size combination, then remap a whole batch. That's the performance story.
- Plenty of people don't need this at all. As the KB's depth docs keep pointing out, the ecosystem's default answer to "get 3D out of an image" is a learned monocular model that needs no rig and no calibration. This node is for when you have the hardware and want geometry you can trust.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| K | NPARRAY,IMAGE,MASK | - - - Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. | |
| D | NPARRAY,IMAGE,MASK | - - - Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. | |
| xi | NPARRAY,IMAGE,MASK | - - - Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. | |
| R | NPARRAY,IMAGE,MASK | - - - Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. | |
| P | NPARRAY,IMAGE,MASK | - - - Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. | |
| size | CV_TUPLE | 0,0 | One value with 2 components (w, h) - it travels as a whole, so it cannot arrive half-connected. Wire it from 'CV Tuple' or type the components in place. |
| m1type | INT | 0-2147483648–2147483647 | - - - |
| flags | INT | 0-2147483648–2147483647 | - - - |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| nparray_0 | NPARRAY | — |
| nparray_1 | NPARRAY | — |