You call cv::fisheye::undistortImage with a camera matrix and a distortion vector from a finished calibration, and the call dies with an assertion: D.total() == 4. Your calibration produced a 5x1 vector, the fisheye functions demand a 4x1 one, and nothing you pass seems to satisfy them. This is not a bug and not a version problem: you fed a standard polynomial distortion model to a function that implements a different model entirely. This guide explains why the assertion fires, how to rerun the calibration so the coefficients match, and how to control the cropped field of view you will see in the undistorted result. It is written for anyone wiring a wide-angle area scan camera into an OpenCV pipeline.
Why the assertion fires
OpenCV ships two separate distortion models. The classic calibration path (cv::calibrateCamera and the calibration sample programs) fits the polynomial model with five coefficients: k1, k2, p1, p2, k3, where p1 and p2 describe tangential distortion. The cv::fisheye namespace implements the equidistant model, which works on the incident angle of each ray rather than on the pinhole radius, and it uses exactly four coefficients: k1, k2, k3, k4.
The functions in the cv::fisheye namespace assert on the input shape first. A 3x3 camera matrix and a distortion vector of exactly four entries passes; a 5x1 vector fails the check and throws the error you saw. The two vectors are not numerically compatible, so padding, truncating or reordering coefficients will not work: the models describe different geometry.
The symptom, summarised
- You calibrated with the standard model, giving a 5x1 distortion vector.
- You called cv::fisheye::initUndistortRectifyMap or cv::fisheye::undistortImage.
- The call aborted with an assertion similar to: K.size() == Size(3, 3) && (D.empty() || D.total() == 4).
The error message is accurate. It is telling you the vector shape is wrong, and the shape is wrong because the underlying model is wrong.
The fix: recalibrate with the fisheye model
The dependable fix is to rerun the calibration with the fisheye model enabled. If you use the OpenCV camera_calibration sample program, it has a CALIBRATE_FISHEYE setting; turn it on and rerun the capture session. The program then fits the four equidistant coefficients and writes a 4x1 vector that the fisheye functions accept.
- Capture a fresh set of checkerboard images, 20 to 40 poses, with the target in the corners and edges of the frame as well as the centre.
- Enable the fisheye calibration flag in your calibration program (or switch to cv::fisheye::calibrate directly).
- Rerun the calibration and confirm the output distortion matrix is 4x1.
- Check the reprojection error; for a well-captured fisheye session expect values below roughly one pixel.
- Pass the new camera matrix and 4x1 vector to cv::fisheye::initUndistortRectifyMap, then cv::remap in your acquisition loop.
- Verify the result on a straight-edge target: lines that curve in the raw frame should be straight after remapping.
If you calibrate in your own code, call cv::fisheye::calibrate with the same image points. There is no reliable conversion from five polynomial coefficients to four equidistant ones; a fitted recalibration takes minutes and is correct by construction.
Why the undistorted image loses corners
Once the call works, the next surprise is that the undistorted output shows less of the scene than the raw frame. That is expected. Defishing maps a curved, very wide field onto a flat output grid, and the outer parts of the raw frame fall off that grid entirely. Nothing is corrupted; the corners are simply outside the output image.
The degree of cropping is not fixed. The newCameraMatrix argument controls it. The cv::fisheye::estimateNewCameraMatrixForUndistortRectify function builds that matrix for you, and its alpha balancing parameter sets the trade-off:
- alpha = 0 keeps only pixels that map from valid source data: no black borders, but the smallest field.
- alpha = 1 keeps every source pixel, filling unmapped output areas with black.
- Intermediate values sit between the two.
For a measurement application, alpha = 0 is usually right: every output pixel is trustworthy, which matters if you feed the image into the kind of length measurement described in our guide on camera calibration for length measurement from image markers. For pure viewing or detection, alpha nearer 1 preserves more of the scene. The related trade-off between cropping and scaling after any undistortion is covered in undistortion scaling parameter: cropping versus field of view, and black output regions in general in black borders after undistortion: cropping or calibration?.
How wide before you need the fisheye model
A rough rule: below about 90 degrees of horizontal field of view, the five-coefficient polynomial fits well and the standard calibration path is fine. Beyond that, residuals climb because the polynomial cannot bend far enough at large radii. Above roughly 150 degrees, fisheye calibration is the only sensible option. If your lens is marginal, calibrate both ways on the same image set and compare reprojection error; the better fit is the model to keep.
A lens such as the LS-FFL-1582, an 8 mm 2/3" C-mount design with a 57.6° field of view, sits comfortably in polynomial territory. Defishing is unnecessary there: a normal calibration plus undistortion is simpler and faster. The fisheye path earns its keep on lenses that were designed as fisheye optics from the start.
Choosing hardware that keeps calibration simple
If you have not yet bought the camera, sensor format and lens resolution should match, or the calibration residuals will suffer no matter which model you use. The LS-CMR-0053 is a 12 MP GigE camera with a 1/1.7" sensor and a C-mount. Choose a lens whose image circle covers that sensor format; the mapping of sensor sizes to image circles is explained in lens sensor format: 1/1.8 inch, 2/3 inch and 1 inch lenses. Focal length follows from the field of view you need; the worked examples in how to choose a machine vision lens focal length and the lens focal length calculator take you from working distance and field width to a focal length, and the lens finder filters the catalogue by format, mount and resolution. If you are starting from nothing, the camera configurator walks through sensor, interface and mount in order.
- Sensor format
- 1/1.7"
- Interface
- GigE
- Resolution
- 12 MP
- Shutter
- Rolling
The remap step itself is light enough to run almost anywhere: a standard PC copes with typical frame rates, and a vision controller with camera ports makes sense when the undistorted image must feed a guidance or measurement routine on the cell floor. Wide-angle work also stresses the geometry checks downstream. If the undistorted image feeds a stereo or depth pipeline, coverage differences between the two cameras matter; depth image field of view: why stereo coverage is smaller explains that effect. And if your distortion measurement needs a quantitative check rather than an eyeball, the procedure in how to measure barrel distortion correction accuracy applies to fisheye results too.
Prevention checklist
- Match the distortion model to the lens type before calibrating, not after.
- Confirm the distortion vector shape (4x1 for fisheye, 5x1 for polynomial) before calling undistort functions.
- Cover the full frame with calibration poses, corners included.
- Record the alpha value and new camera matrix with the calibration file, so the crop is reproducible.
- Re-verify straightness on a target after any lens refocus or mount change.
For a deeper walk-through of applying corrected fisheye images to a robot guidance cell, see this fisheye distortion correction guide for vision-guided pick and place.
Next step
If you are selecting a camera and lens for a wide-angle inspection cell, tell us the field width and working distance and we will check the format match and coverage before you order, or start from the lens configurator. For help with an existing set-up, the contact page reaches the applications team.
Questions
Why does initUndistortRectifyMap assert that D.total() == 4?
The cv::fisheye namespace implements the equidistant projection model, which uses exactly four distortion coefficients: k1, k2, k3, k4. A standard polynomial calibration produces five coefficients (k1, k2, p1, p2, k3), so the size check fails before any mapping is computed. The two models are not interchangeable, so there is no way to convert one vector into the other.
Can I convert five polynomial coefficients into four fisheye coefficients?
No, not reliably. The polynomial models radial distortion relative to a pinhole projection, while the equidistant model works on the incident angle. They describe different geometry, so a straight conversion does not exist. The dependable fix is to rerun the calibration with the fisheye model enabled, which yields the 4x1 coefficient vector the fisheye functions expect.
Why do I lose part of the image after fisheye undistortion?
Rectifying a very wide-angle image maps the curved field onto a flat output grid, and the corners of the original frame fall outside that grid. The new camera matrix controls this: alpha set to 0 keeps only fully valid pixels, alpha set to 1 keeps all source pixels with black borders. Values in between trade border pixels for coverage.
Do I need a fisheye calibration for a 90 degree lens?
Below roughly 90 degrees of horizontal field of view, the standard polynomial model usually fits well enough and the normal calibration functions work. Beyond that, polynomial coefficients struggle to describe the strong radial bending, residuals climb, and the equidistant fisheye model gives a far better fit. For extreme lenses above 150 degrees, fisheye calibration is the only sensible choice.
Which calibration target works best for fisheye cameras?
A flat checkerboard still works, but place it in many positions including the image corners and edges, where fisheye distortion is strongest. A centred-only target leaves the outer field unconstrained and the fitted coefficients will not correct it. Aim for 20 to 40 poses covering the whole frame, at several distances, and check reprojection error stays below about one pixel.
