Nodes/ComfyUI CV/cv2.fisheye.undistortImage
ComfyUI Node

cv2.fisheye.undistortImage

Straightening a wide-angle frame

By bmad4ever·Created 4 months ago·Updated 14 days ago· 1
cv2.fisheye.undistortImage
  • distorted
  • K
  • D
  • new_size
  • nparray

Some jobs can't be done on a fisheye frame at all. Face detection, depth estimation, an img2img pass, a LoRA that was trained entirely on rectilinear photos - all of them assume straight lines are straight. cv2.fisheye.undistortImage is the node that converts a distorted wide-angle frame into a normal-looking one, using the equidistant model the fisheye calibrator fitted.

This is the deterministic half of the pipeline the post-processing notes in our KB keep making the same point about: a remap is $0, milliseconds and perfectly repeatable, and no amount of model quality substitutes for doing geometry with geometry.

How it works

cv2.fisheye.undistortImage is shorthand, and it's worth knowing for what: it's fisheye.initUndistortRectifyMap with an identity rotation plus cv2.remap with bilinear interpolation. The map is built by inverting the lens distortion - for every output pixel, find which input pixel it came from - which is why straightened fisheye images have black corners: corners of a rectilinear rectangle map to places outside the lens's field of view. It always keeps the whole original pixels; it just doesn't invent what wasn't captured.

The four sockets you care about are distorted (the frame), K (the 3×3 intrinsics), D (exactly four fisheye coefficients, k1..k4) and the optional new_size, a CV_TUPLE with two components (w, h). Leave new_size at its default and the output matches the input size; type or wire a different size and cv2 builds the map for that canvas instead. It's one value that travels as a whole, so you can't half-connect it - wire it from CV Tuple or type the two numbers in place.

The single output is nparray, an NPARRAY. Watch that: unlike most filter wrappers in this pack, this one does not echo your input's format, so there's no IMAGE socket to plug straight into Preview Image. Push it through CV Array → Image (or look at it with Preview CV Array) and you're done.

Where K and D come from

Not from the pinhole calibrator. CV Fisheye Calibrate (Chessboard) returns K plus a four-coefficient dist_coeffs, and the pack's curated CV Fisheye Undistort node refuses a five-element pinhole vector outright rather than silently truncating it - because the two models mean different things by the same letters. If you saved parameters earlier, CV Load Camera Params (JSON) reads them back.

Honest recommendation: if all you want is "straighten this photo", use the curated CV Fisheye Undistort node instead of this raw one. Same cv2 calls underneath, but it adds the framing controls (same K versus estimate new K, a balance knob that trades field of view against black corners, fov_scale), returns the new K, and detects the pathological case where cv2's own estimateNewCameraMatrixForUndistortRectify returns an all-NaN matrix. This raw node is for when you already have K/D/new_size as data, or you're assembling the pieces by hand.

Install

ComfyUI Manager → ComfyUI CV, or:

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install -r comfyui_cv/requirements.txt

Python ≥3.12, current ComfyUI (the pack is written against the V3 node API), restart after. The install pins opencv-contrib-python-headless~=5.0.0.93; the fisheye module is contrib-only, so a plain opencv-python wheel installed alongside will empty it and the node disappears from the search.

Common issues

The output looks like a mild zoom with stretched edges, not a straightening. Your D isn't the fisheye four. Five Brown–Conrady coefficients fit a fisheye frame surprisingly well in the centre - the pack measures its own synthetic views fitting at 0.13 px RMS under both calibrators - so the wrong model gives you a result that looks plausible and is wrong at the borders.

Everything outside the centre goes black. Expected. Straightening a 180° lens into a rectangle means the corners have no source pixels. Use the curated node's balance knob, or a larger output canvas via new_size, and accept a softer stretched border.

Multi-frame input only processed once. This node isn't in the pack's per-frame batch list, so an IMAGE batch goes through as frame 0. Loop it yourself, or use CV Batch → Image Batch / the batch bridges if you're working on a sequence.

Category missing entirely. Contrib wheel overwritten - python ComfyUI/custom_nodes/comfyui_cv/tools/repair_opencv_contrib.py --check, then --apply with ComfyUI stopped.

Categoryimage/CV/low-level/fisheye

Inputs (4)

NameTypeDefaultDescription
distortedNPARRAY,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.
KNPARRAY,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.
DNPARRAY,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.
new_sizeoptCV_TUPLE0,0One 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.

Outputs (1)

NameTypeDescription
nparrayNPARRAY—