Undistorting a cheap fisheye webcam without losing the corners

2026-10-19 · 6 min read

The standard pinhole model falls apart beyond about 120° field of view. cv2.fisheye handles it much better, but the default output crops away a surprising amount of the image.

Calibrate with the fisheye model

flags = (cv2.fisheye.CALIB_RECOMPUTE_EXTRINSIC
         | cv2.fisheye.CALIB_CHECK_COND
         | cv2.fisheye.CALIB_FIX_SKEW)
rms, K, D, rvecs, tvecs = cv2.fisheye.calibrate(
    objpoints, imgpoints, gray.shape[::-1], None, None, flags=flags,
    criteria=(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-6))

Use at least 25 views of the board, and make sure some of them put the board into the corners of the frame. That is where the distortion lives.

Keep the corners

The trick is the balance parameter of estimateNewCameraMatrixForUndistortRectify: 0 crops to valid pixels only, 1 keeps everything.

newK = cv2.fisheye.estimateNewCameraMatrixForUndistortRectify(
    K, D, (w, h), np.eye(3), balance=0.6)
map1, map2 = cv2.fisheye.initUndistortRectifyMap(
    K, D, np.eye(3), newK, (w, h), cv2.CV_16SC2)
out = cv2.remap(frame, map1, map2, cv2.INTER_LINEAR)

Compute the maps once and reuse them: remap alone runs at well over 100 fps on 720p.