You calibrated the camera, the solver printed five distortion coefficients, and nothing on screen says what those numbers do to a pixel. A distortion vector map answers that in one picture: an arrow at each grid point of the frame, from the calibrated pixel position to its corrected position. You can build one in a few lines around your toolbox's undistort-points function, read it in seconds, and catch a bad calibration before it reaches production measurements.
What the vector map shows
The map is a vector field over the frame. At a grid point (x, y) in calibrated pixel coordinates, compute the corrected position with the same coefficients your production code applies. The arrow runs from the original to the corrected position, and its length is the local shift a raw measurement would absorb at that spot.
Barrel distortion, the common case on fixed-focal machine vision lenses, compresses the outer field, so correction pushes edge points outward and the arrows grow with radius; pincushion points them inward. When you locate defects by pixel coordinates and calibration, the arrow under the defect is exactly the position error the correction removes.
What you need
- The camera matrix (fx, fy, cx, cy) and the distortion coefficients (k1, k2, p1, p2, k3) from one calibration session — the same values your pixel-to-mm correction already applies.
- The image size in pixels.
- Your toolbox's undistort-points function; the arguments are the grid points, the camera matrix and the coefficient vector.
- An untouched focus setting. Coefficients belong to one focus distance, so lock the focus at a repeatable position before you calibrate.
Build the map in five steps
- Sample the grid. Lay points over the frame in pixel coordinates. A step of 153 px — width ÷ 16 — gives a 16 × 14 grid, 224 arrows, on a 2448 × 2048 frame: enough to show the shape, few enough to read.
- Undistort the grid. Call undistortPoints with the grid, the camera matrix and the coefficients. If the function returns normalized coordinates, convert back with x_pix = x_n·fx + cx and y_pix = y_n·fy + cy.
- Subtract. The displacement per point is corrected minus original: an N × 2 array in pixels.
- Draw the arrows. Plot the grid against the displacement as a quiver plot, set the axes equal, and overlay the arrows on a frame from the same camera at 40-60% opacity.
- Label the scale and run two tests. Add one reference arrow labelled 10 px. Then map the principal point (cx, cy): by model its displacement is zero, so anything above 0.5 px means matrix and coefficients do not belong together. Finally compare the corner arrow with the datasheet distortion percentage, as in the worked example below.
The pseudo-code, with illustrative values you replace from your own run:
% Illustrative values; replace with your own calibration output
fx = 3318.0; fy = 3317.0; cx = 1224.5; cy = 1023.5; % camera matrix, pixels
k1 = -0.110; k2 = 0.034; p1 = 0.0001; p2 = -0.0002; k3 = -0.004
K = [fx 0 cx; 0 fy cy; 0 0 1]
kc = [k1 k2 p1 p2 k3]
% grid: one point every 153 px over the 2448 x 2048 frame -> 224 points
pts = makeGrid(0:153:2447, 0:153:2047) % your own grid builder, N x 2 array
undN = undistortPoints(pts, K, kc) % corrected points, normalized coordinates
undPix = undN .* [fx fy] + [cx cy] % back to pixel coordinates
d = undPix - pts % N x 2 displacement, pixels
quiver(pts(:,1), pts(:,2), d(:,1), d(:,2)); axis equal
Worked example: arrow length on a 20 MP 1-inch sensor with a 0.3% lens
The camera: 20 MP resolution, 1-inch sensor format, C-mount, USB3, mono — the MV-CS200-10UM:
- Sensor format
- 1"
- Lens mount
- C-mount
- Interface
- USB3
- Resolution
- 16-25 MP
The lens: 16 mm focal length, 1.1-inch sensor format, distortion under 0.3%, F2.8-22, C-mount — the LS-FFL-0567:
- Type
- Machine vision fixed
- Sensor format
- 1.1"
- Mount
- C-mount
- Resolution
- 16-25 MP
The 16-22 mm image circle clears the 16 mm diagonal of a 1-inch sensor with margin, and a sensor of this class resolves 5496 × 3672 pixels at a 2.4 µm pitch. The datasheet percentage bounds how far the image edge sits from its ideal position; to first order the edge shift equals that percentage of the image height. The printed number is usually TV distortion, a sibling quantity — geometric vs TV distortion explains the difference — so read the result as the value at the spec limit.
- Image height: 3672 px × 2.4 µm/px = 8812.8 µm ≈ 8.81 mm.
- Edge shift at the 0.3% limit: 0.003 × 8.81 mm = 0.0264 mm = 26.4 µm.
- In pixels: 26.4 µm ÷ 2.4 µm/px ≈ 11 px.
- At half the edge radius the radial term falls as r³: 11 px × (1/2)³ ≈ 1.4 px, about 3.4 µm on the sensor.
A healthy map for this camera and lens shows corner arrows near 10 px, shrinking to about 1.4 px at half radius. The decision: raw measurements inside the central half of the frame carry at most about 1.4 px of uncorrected shift — acceptable for presence checks, not for gauging — and everything beyond that radius needs the undistort mapping applied before you measure. A built map with 100 px corner arrows means a broken fit; near-zero arrows on this lens means the coefficients never reached the function.
Reading the map: healthy, suspect, broken
- Healthy. Zero-length arrows within the central 10-20% of the radius, smooth outward growth while k1 dominates (roughly with r³), every arrow staying radial — aligned with the line to the centre.
- Suspect. Left-right asymmetry points at the principal point or the tangential terms p1 and p2. Arrows that flip sign partway out usually mean an over-fitted high-order term: drop k3 and refit. A length jump in the outer band means too few poses covered the corners.
- Broken. Random directions, or neighbouring arrows of opposite sign, mean too few poses, a target that is not flat, or motion blur. Refit with 10-20 poses at varied positions and moderate tilts, then rebuild the map and compare.
Turn the map into a decision
Convert the measurement tolerance to pixels, then compare it with the arrow length at the same place:
The one-third margin leaves room for calibration residual, pixel noise and threshold effects. A 100 mm field on 2448 px gives s = 100 / 2448 = 0.0409 mm/px. A 0.10 mm tolerance is t_px = 0.10 / 0.0409 ≈ 2.4 px, so the correction applies wherever the arrow exceeds about 0.8 px — beyond roughly 40% of the edge radius for the lens above, where 11 px × 0.42³ ≈ 0.8 px crosses the line.
If the sensor itself is not chosen yet, size it the other way round: from the field and the tolerance to the pixel count.
The calculator's button opens the area scan cameras finder filtered to the result. Applying the correction is one function call per feature point, or one image remap per session; on a GPU host, check the coordinate and sampling assumptions first with GPU lens undistortion mapping.
When arrows stay large after a clean refit, change the optics instead of fighting the fit. Lenses that print low distortion exist across the machine vision lenses range, and for dimensional work an object-side telecentric design holds magnification constant across the field, which removes the radial term — entocentric vs telecentric lenses covers that choice. One C-mount telecentric option sized for 1.2-inch sensors:
- Sensor format
- 1.2"
- Mount
- C-mount
- Image circle
- 16-22 mm
Before you trust the map
- Print the coefficients first. An all-zero vector means they never reached the function, and the map will show nothing while the lens distorts.
- One session, one map. Matrix, coefficients, image size and the frame under the arrows all come from the same calibration run.
- Principal point test. Its displacement must stay under 0.5 px.
- Shape test. Arrows radial, growing smoothly outward, with the corner length agreeing with the datasheet percentage in order of magnitude.
- Focus unchanged. Any refocus since calibration invalidates the coefficients; recalibrate.
- Repeat after changes. Lens swap, refocus or a knock to the camera: rebuild the map before the first production part.
Keep the map with the calibration
Export the map image into the calibration record. The corner-arrow length next to the datasheet percentage is the fastest health check anyone can run later, with no toolbox open. If the calibrated camera feeds a robot cell, the corrected pixels are exactly what hand-eye calibration consumes — a pixel-to-robot-base calibration walkthrough shows that next transform. To pick optics that keep the arrows small from the start, browse the lens finder or configure a lens around your sensor; the surrounding guides sit on the systems topic page.
Questions
How many grid points should a distortion vector map have?
Start near 16 × 12 grid points, about 200 arrows: dense enough to show the shape, sparse enough to read. Drop to 10 × 8 for a first look; go to 24 × 18 only when hunting a local bump near a measurement zone. The undistort-points call is computationally trivial, so refine the grid once the first map looks sane.
Does refocusing the lens invalidate the distortion map?
Yes. The coefficients were fitted at one focus distance; moving focus changes the image distance and the radial mapping, first at the frame edge. Lock focus mechanically after calibration and rebuild the map whenever focus moves. Fixed-focus or telecentric set-ups avoid the problem entirely.
Can I use the arrow lengths as a correction lookup table?
Not directly. The map is a sampling, and interpolating between arrows loses accuracy where displacement curves fastest, in the outer band of the radius. Correct from the coefficients through the undistort or remap path of your library, and keep the map as the visual check of what that correction does.
What do inward-pointing arrows mean?
Pincushion distortion: correction pulls edge points back toward the centre because magnification rises with field radius. It shows up on longer focal lengths used off-axis. The reading logic is otherwise identical: arrows should still grow smoothly with radius and vanish at the principal point.
My map shows near-zero arrows everywhere. Is the lens that good?
Only if the datasheet distortion supports it. Map one corner point by hand and compare with the printed percentage: a 16 mm fixed-focal lens should give edge shifts of several pixels, not fractions of one. Near-zero arrows on a wide lens usually mean the coefficients never reached the function, so print them before plotting.


