Lenses & opticsHow-to

Fisheye undistortion field of view loss: stitch on a sphere

Why fisheye undistortion causes field of view loss on wide lenses, what the balance parameter keeps, and how to stitch two fisheye cameras on a sphere.

LumaSpec Vision10 min read Share on LinkedIn

On this page 9 sections
  1. 01The new camera matrix sets the output view
  2. 02How much view a rectified frame can hold
  3. 03Worked example: a 224 px focal length on a 640 px frame
  4. 04Keep the view: the procedure
  5. 05Stitch on a sphere: canvas size and overlap
  6. 06Pick the lens for the path you chose
  7. 07Checks before you order the second camera
  8. 08When the usual fix is the wrong fix
  9. 09Questions

You calibrated a fisheye camera, ran the undistortion, and got back a small, narrow picture with the rim gone — the very rim a stitch needs. Nothing is broken. The output field of view is set by the new camera matrix handed to the remap, and its default keeps only the undistorted center. This guide shows where the crop comes from, how much view a rectified frame can physically hold, how to drive the balance parameter and the output size, and when to stop rectifying altogether and put both cameras on a shared spherical canvas.

The new camera matrix sets the output view

The lens did not change; the projection did. Flattening a fisheye frame needs a destination camera matrix, and estimateNewCameraMatrixForUndistortRectify from the cv2.fisheye module builds it. Its balance argument picks the new focal length between two ends. Near 0 the matrix shows the largest region with no empty pixels, which on a wide fisheye is a modest center crop. Near 1 it shows every pixel the lens captured, at a lower pixel scale, with empty corners. The default is 0, so an unmodified call crops hardest.

A rectified frame is a pinhole projection, and pinhole geometry sets a hard wall. A pinhole image places a ray at radius f·tan(theta), so a ray 90° off axis would need infinite radius. No rectified output at any resolution holds it: content beyond 90° is unreachable in principle, and content between roughly 60° and 90° survives only as heavy stretch.

Commit to one of three paths:

  • Lens 120° or narrower: rectify normally. The table below shows you lose almost nothing.
  • One camera, rim needed: raise balance toward 1 and accept the scale drop, or enlarge the output canvas.
  • Two lenses around 150° or wider, built for stitching: skip pinhole rectification and remap both cameras onto one spherical canvas.

For non-fisheye lenses the same trade appears as the free scaling factor of the remap; undistortion scaling parameter: cropping versus field of view works through that case.

How much view a rectified frame can hold

theta_edge = r_edge / f equidistant fisheye: angle = image radius / focal length f_new = (W / 2) / tan(theta_edge) pinhole focal length that fits that cone into width W scale = f_new / f pixel scale kept at the frame center overlap = FOV1/2 + FOV2/2 - alpha shared band of two cameras with axes alpha apart

The numbers for an output the size of a 640 px input, with a 224 px source focal length:

Cone that must stay visible New focal length Pixel scale kept
110°, the default crop 224 px 100%
120° 185 px 82%
150° 86 px 38%
164°, a full fisheye circle 45 px 20%

This table is pure geometry: only tan and the output width set it, whatever the lens and whatever the library. Past a 90° half-angle there is no entry at all.

The same arithmetic, run backwards from a target view to the focal length that delivers it, is what the lens calculator on this site does:

Lens focal length, field of view and working distance
Units
Solve for
Focal length
– mm
Field of view
– mm
Working distance
– mm
Magnification
– x
Angle of view
– °
Pixel on the part
– mm

Thin-lens model: m = sensor width / field of view, f = WD × m / (1 + m). Accurate to a few percent above about 150 mm; check the lens's minimum object distance.

Find lenses with this focal length Open the full calculator

Worked example: a 224 px focal length on a 640 px frame

Take an example calibration of the kind a 640 px board-camera fisheye produces: fx = 223.66 px, fy = 259.93 px, cx = 290.87 px, cy = 309.10 px, four distortion coefficients k1 through k4 each below 0.016 in magnitude, fitted from 43 checkerboard poses.

First, a sanity check that fails. fx and fy differ by 36 px, which is 16%. On a square-pixel sensor those two numbers are the same physical length, and a healthy fit lands within about 2%. Refit with more poses tilted 30° to 45° before trusting anything below: the field of view swings with that gap, 141° through fy against 164° through fx, and the fitted k terms push the fx figure toward 176°. Evaluated with these coefficients, the mapped radius also stops growing near 350 px, so the frame corners lie outside what this fit describes at all.

Now the design estimate from the first-order model, using fx. The usable circle touches the half-width at r = 320 px, so theta = 320/224 = 1.43 rad = 82.0°: a 164° lens. Two outputs to compare:

  • Default rectification at the source focal length: the fully visible cone is 2·atan(320/224) = 110°, with corners reaching 127° along the diagonals. The lens delivers 2π(1 − cos 82°) = 5.4 steradians; the rectified rectangle holds about 3.5 sr, so 65% survives. The missing 35% is the rim — the seam material.
  • Full circle at balance near 1: f_new = 320/tan(82°) = 45 px. Pixel scale falls to 45/224 = 20%, five times fewer pixels per object at the center, and the blend samples a heavily stretched rim.

Keep the view: the procedure

  1. Refit until fx and fy agree within about 2%. Add tilted poses, not more frontal ones, and re-check the reprojection error. A board camera ships with no lens profile, and building a lens distortion profile for a board camera covers that step. Use a proper calibration target rather than a printed sheet: flatness and pitch accuracy are the point.
  2. Write down the field of view you must keep, then the seam requirement. For a stitch, plan at least 15° of shared view; below that the blend has too little common texture to align on.
  3. Sweep balance and print the returned focal length each time:
import cv2
import numpy as np

K = np.array([[223.66, 0.0, 290.87],
              [0.0, 259.93, 309.10],
              [0.0, 0.0, 1.0]], dtype=np.float64)            # float64, from your calibration
D = np.array([-0.0157997, -0.00707966,
              0.00217689, -0.000716948], dtype=np.float64)   # float64, k1..k4
dim = (640, 640)                                             # (width, height), pixels
balance = 0.5                                                # float, 0.0 to 1.0

new_K = cv2.fisheye.estimateNewCameraMatrixForUndistortRectify(
    K, D, dim, np.eye(3), balance=balance)

print(new_K[0, 0])   # new focal length in pixels:
                     # ~224 = sharp center only, ~45 = the whole 164-degree circle

The printed number, read against the table above, tells you exactly what you kept — no eyeballing required. Pick the smallest balance whose empty corners your software tolerates. To keep scale instead, pass a larger newSize in the same call and let the focal length follow the canvas.

  1. For a single inspection camera, stop here. Run the remap with the matrix you chose, count the empty pixels, and mask the corners explicitly if your measurement region touches them, so edge detection does not fire on the boundary.
  2. For a stitch, do not rectify at all. Map fisheye pixels straight onto the spherical canvas of the next section. The mapping arithmetic — distortion polynomial, rotation, bilinear sampling — is laid out in fisheye undistortion: how the pixel mapping works. If the library rejects your coefficients outright, the distortion-model mismatch behind the fisheye undistortImage assertion error names the fit you need.

Stitch on a sphere: canvas size and overlap

An equirectangular canvas stores the whole sphere on a 2:1 image: longitude across, latitude down. For each output pixel, compute the ray, rotate it into each camera's frame with that camera's rotation, and read the source pixel through the fisheye model:

phi = 2*pi*(u/W - 0.5) longitude of output pixel (u, v) psi = pi*(0.5 - v/H) latitude, positive up r = f*(theta + k1*theta^3 + k2*theta^5 + k3*theta^7 + k4*theta^9) source = (cx + r*cos(azimuth), cy + r*sin(azimuth)), sampled bilinearly

Size the canvas to the source sampling, not to a round number. The example camera delivers 1 px per 1/224 rad along any radius, so a full 360° needs W = 2π·224 = 1407 px: use 1408 × 704. A 4096 × 2048 canvas upsamples about three-to-one in each direction and wastes memory; a 512 × 256 canvas throws away most of what the sensor paid for.

Overlap decides whether a stitch is possible at all. With two 164° lenses the shared band is 82° + 82° − alpha, where alpha is the angle between the axes:

  • Back to back, alpha = 180°: 82 + 82 − 180 = −16°. No overlap at all, and a 16° blind band runs exactly where the seam should be.
  • Perpendicular, alpha = 90°: a 74° shared band around the bisector, comfortable to stitch, but a 53° blind cone sits behind the bisector (135° − 82°).
  • Full sphere from two cameras: both lenses must reach 180°. The 190° class gives a 10° band (95 + 95 − 180); aim that band at scenery with texture, not at a fixture pole.

To register more than two frames into one frame of reference, three image stitching with a shared coordinate frame extends the same idea.

Pick the lens for the path you chose

For the spherical path you want a true fisheye on each camera. The LS-MBL-0041 is a 1.45 mm M12 fisheye lens for 1/2.3-inch, 10 MP sensors.

M12 board lenses1.45 mm M12 fisheye lens, 1/2.3", 10 MP (LS-MBL-0041)LS-MBL-0041
Sensor format
1/2.3"
Mount
M12
Resolution
6-10 MP
Image circle
Up to 8 mm
$88.00View partBuild a system with it

Image circle, back focus length and mount clearance matter more than focal length here; fisheye lens selection for M12 board cameras works those numbers, and the M12 board lenses category and the lens finder list what fits your sensor.

For the rectify-normally path at 120° and below, a low-distortion wide lens avoids the whole problem. The low-distortion 3.3 mm LS-MBL-0159 and the 12 mm LS-MBL-0012 sit in the catalog's low-distortion series.

M12 board lenses3.3 mm, 1/2.3", 16 MP, M12-mount Low-Distortion M12 lens (LS-MBL-0159)LS-MBL-0159
Type
Low-distortion M12
Sensor format
1/2.3"
$109.00View part
M12 board lenses5 MP S-mount low distortion board lens, 12 mm, 1/1.8" (LS-MBL-0012)LS-MBL-0012
Sensor format
1/1.8"
Mount
M12
$89.00View part

Run your own numbers through the lens configurator before ordering, and see the lenses topic page for mounting and focus guides.

LensesM12 board lenses

S-mount lenses for board cameras

195 models · from $20.00

Checks before you order the second camera

  • fx and fy within about 2% after the refit; if not, every number downstream is soft.
  • The printed new focal length matched against the table, so the kept cone is known before anything is bought.
  • The overlap band computed from both lenses' measured half-angles at your mount angle — 15° minimum for a blend with shared texture.
  • A calibration target large enough to fill the seam region; a small board calibrates only the center of the frame.
  • The empty-corner fraction at your chosen balance, with a mask plan if measurements touch the corners.
  • One resampling step in the pipeline: fisheye to sphere, never fisheye to pinhole to sphere.

When the usual fix is the wrong fix

Setting balance to 1 keeps every angle but is the wrong tool for a two-camera stitch past about 150°: the output still lives in a pinhole frame at 20% scale, and a later spherical remap resamples it a second time. Enlarging the canvas holds scale only by multiplication: an 82° rim at a 224 px focal length lands 224·tan(82°), about 1594 px from the center, so the canvas grows to 3188 px wide — the FAQ prices what that costs per frame. And a lens at 180° or wider carries rays at or beyond 90° off axis, which need infinite image radius: it can never be fully rectified at any size, and it belongs on the sphere from the start.

Questions

What does the balance parameter actually change?

It sets the focal length of the new camera matrix, and that focal length alone decides how much of the fisheye frame the output shows. Print the fx element of the returned matrix for each value you try: near the original focal length you kept the sharp center; near (W/2)/tan(theta_edge) you kept the whole circle. Values between 0.2 and 0.6 usually suit inspection, where empty corners break downstream processing.

How wide a lens can a rectified pinhole output hold?

Anything up to about 120° costs little: a 120° cone fits a 640 px output at a 185 px focal length, 82% of source scale. At 150° you are down to 38%, at 164° to 20%. Rays 90° off axis need infinite image radius, so no rectified frame of any size holds a 180° lens; anything that wide belongs on a spherical or cubemap canvas instead.

My calibration gives fx and fy 16% apart. Is that a problem?

Yes. On a square-pixel sensor both focal lengths are the same physical length, so a healthy fit lands within about 2%. A 16% gap means the rim of the fit is unreliable, and the field of view computed from it swings between 141° through fy and 164° through fx. Refit with more poses tilted 30° to 45° toward the camera, then recompute before you choose lenses or mount angles.

Why not keep full scale with a much larger output image?

You can, and the arithmetic sets the price. Holding an 82° half-angle at the source scale of 224 px needs a half-width of 224·tan(82°), about 1594 px, so the frame becomes 3188 px wide: 10.2 MP instead of 0.41 MP, roughly 25 times the storage per frame. Only the inner 4% of that canvas lies inside the well-sampled 320 px radius; the other 96% is stretched rim. Worth one offline render, punishing in a loop.

Can two 160-degree fisheye cameras cover the full sphere?

No. Back to back, each lens covers an 80° cone, and 80 + 80 − 180 = −20: a 20° wide blind band with no overlap to stitch on. Full-sphere coverage from two cameras needs both lenses at 180° or wider; a pair of 190° lenses shares a 10° band (95 + 95 − 180). With 160° lenses, accept the blind band, add a third camera, or drop the sphere requirement.