Your MATLAB call returns R1, R2 and Q, but when you feed those into initUndistortRectifyMap the OpenCV frames come out different. Neither toolkit shows you the dense map it actually used. This guide is for anyone wiring two toolkits into one stereo pipeline. The rectification map is not a black box: it is a plain per-pixel lookup table of source coordinates that you can print, export and diff like any image. Below you get the map format to request, the way to build a matching MATLAB map and sample it with interp2, and the difference patterns — a 1.0 px shift, a linear ramp, an edge-concentrated error — with the numbers that let you sign a rebuilt pair off for production.
Where are the stereo rectification map values stored?
Nowhere hidden: a rectification map is just a dense, per-pixel lookup table holding one source-coordinate pair for every destination pixel. OpenCV's initUndistortRectifyMap hands those values to you as ordinary arrays. MATLAB's rectifyStereoImages applies the table internally and returns the rectified images plus Q, and on request R1 and R2, but never the dense map itself.
For every output pixel (u, v) the table says where to sample the source image:
The remap call walks the table row by row and interpolates bilinearly between the four neighbouring source pixels. A 4000 × 3000 destination holds 12,000,000 coordinate pairs per camera, and that is the entire content: no residual rotation, no metadata, nothing else. Print one row of mapx and you are reading the same numbers the remap call samples. Every live rectified-video pipeline runs on exactly this table, whoever built it.
Which map format should you request to read the values?
Request CV_32FC1. With m1type set to CV_32FC1, initUndistortRectifyMap returns map1 as the source x coordinate and map2 as the source y coordinate in plain floats. The CV_16SC2 default packs fixed-point integer pairs plus an interpolation index — faster for remap, useless for diffing values.
The three representations OpenCV documents:
| m1type | map1 holds | map2 holds | Bytes per pixel | Good for |
|---|---|---|---|---|
| CV_32FC1 | source x, single-channel float | source y, single-channel float | 8 | printing, diffing, plotting |
| CV_32FC2 | source (x, y), two-channel float | nothing | 8 | one-array handling |
| CV_16SC2 | fixed-point (x, y) integer pair | interpolation index, CV_16UC1 | 6 | remap speed at runtime |
convertMaps converts between the forms in either direction, so you can keep the compressed pair for runtime and rebuild float planes only when you need to inspect them.
The compressed form pays for its speed with precision: it is integer-based, so a value that should land on 1234.52 px is held in fixed point, with the interpolation choice folded into map2. If the values you print move in small identical steps instead of varying smoothly, you are reading the fixed-point form by mistake.
Memory is the trade, and it is small. Two 12 MP area scan cameras such as the LS-CMR-0166, 4000 × 3000 over USB3, give the pair behind this guide's numbers:
- Sensor format
- 1/1.7"
- Interface
- USB3
- Resolution
- 12 MP
- Shutter
- Rolling
Per camera, 4000 × 3000 = 12,000,000 pixels, so the float planes cost 12,000,000 × 8 B = 96 MB and the compressed pair costs 12,000,000 × 6 B = 72 MB. A debugging session holding both forms for both cameras needs about 336 MB, which any development machine swallows — inspect in floats, deploy in whatever the runtime prefers.
How do you build a MATLAB map you can diff against OpenCV?
Compute it yourself from the rectification parameters. MATLAB's rectifyStereoImages does not expose the dense map, so build the mapping per destination pixel from the rectification homographies and the undistortion equations, then sample with interp2. The result is an x plane and a y plane like CV_32FC1 gives you.
Everything the build needs is already in your call. The documented route for stereoscopic rectification in MATLAB: derive the (x, y) output bounds and the rectification homographies from the calibration, push every destination pixel back through its homography, undo the lens distortion with the undistortion equations, and resample the source with interp2. Save the two planes as float32 and the MATLAB side becomes diffable against the OpenCV side line for line.
Make the comparison honest first: both maps must be built from one calibration — the same camera matrix, the same distortion coefficients, the same R1 and R2, the same new camera matrix, the same output size. Two maps fitted from separate calibration sessions measure the calibration, not the toolkit; comparing calibration parameters across workflows is its own problem with its own checks.
On the OpenCV side the call is short (illustrative input values, documented API names):
import numpy as np
import cv2
K = np.array([[3810.0, 0.0, 2000.0], # float64, 3x3 camera matrix
[0.0, 3810.0, 1500.0],
[0.0, 0.0, 1.0]])
D = np.array([-0.12, 0.06, 0.0, 0.0, 0.0]) # k1, k2, p1, p2, k3
R1 = np.eye(3) # 3x3 float64 rectification rotation
Knew = K # new camera matrix: the projection your reference run used
size = (4000, 3000) # output size, (width, height)
mapx, mapy = cv2.initUndistortRectifyMap(K, D, R1, Knew, size, cv2.CV_32FC1)
print(mapx.dtype, mapy.dtype, mapx.shape) # float32 float32 (3000, 4000)
The test that closes the loop: import the MATLAB-built x plane as mapx_matlab, a float32 array of shape (3000, 4000), and subtract:
diff = np.abs(mapx - mapx_matlab)
print(diff.max(), diff.mean()) # 1.0 1.0 — the uniform origin shift explained below
A maximum of 1.0 px, the same everywhere, means the two maps differ only by the pixel-origin convention. Anything structured means an input differs too, and the next section names it.
Why do the same R1 and R2 give different frames in each toolkit?
Because R1 and R2 are only three of the inputs: the map also depends on the camera matrix, the distortion coefficients and the new camera matrix. The projection matrices your MATLAB call returns (CamLeft, CamRight) must go into newCameraMatrix, and MATLAB counts pixels from 1 where OpenCV counts from 0.
newCameraMatrix is the input most often passed wrong. initUndistortRectifyMap samples the destination through it, so giving it the original camera matrix instead of the rectified projection lands every pixel at a slightly wrong scale, and the error grows linearly from the image center:
Say the reference projection has f = 3810 px and you pass 3800 px, so Δf = 10 px. At the border of the 4000-wide frame, 2000 px from the center: 2000 × 10 / 3810 ≈ 5.3 px. A 0.3% scale error is invisible at the center and pushes the framing five pixels out at the edges.
Coordinates add a constant rather than a pattern: MATLAB addresses the center of the top-left pixel as (1, 1), OpenCV as (0, 0). Identical matrices through the two pipelines therefore differ by exactly 1.0 px in x and y — the uniform shift in the test above, a convention rather than a fault.
The distortion terms are the sneaky input. Two toolboxes fitting the same chessboard images can weight the radial terms differently, so one map pulls edges straighter than the other, and the difference hides in the corners where radial distortion peaks. It is the same mechanism that warps rectified depth when inverse distortion coefficients are reapplied the wrong way.
Finally, the rectified pair is not unique. Rotate both rectified cameras by any common rotation and they are still rectified, so MATLAB's chosen frame and OpenCV's need not agree even with perfect inputs. Identical output is defined only once the camera matrix, the distortion terms, the rotation and the new camera matrix all match; everything else is a different, equally correct rectification.
What does each difference pattern tell you?
The map diff diagnoses itself: a uniform 1.0 px offset is the 0-based versus 1-based origin, an error growing linearly from the center is a new-camera-matrix scale mismatch, error concentrated at the corners is a distortion mismatch, and gross error everywhere means the rotation arrived wrong.
| Signature in the diff image | Cause | Fix |
|---|---|---|
| Uniform 1.0 px in x and y | 0-based vs 1-based pixel origin | accept it, or subtract 1 when building one side |
| Linear ramp from the center, about 5.3 px at the border in the example above | new camera matrix scale mismatch | pass the reference run's projection as newCameraMatrix |
| Near zero at the center, rising steeply into the corners | distortion coefficients differ | rebuild both maps from one coefficient set |
| Shear or rotation across the whole frame | rotation wrong-handed, or the 3×3 transposed in transit | check the matrix layout at the language boundary, then rebuild |
| Sub-0.05 px speckle | interpolation and rounding | ignore |
The cheapest visual check beats any diff: remap a chessboard through both maps and overlay the corner detections. Corners that sit on common rows mean the pair is rectified; rows that tilt mean camera order or baseline is wrong, which is the stereo rectification horizontal lines fault, not map arithmetic.
When should you not rebuild the maps at all?
Skip the rebuild entirely when the pair is a factory stereo or structured-light unit: those cameras ship already rectified, the SDK hands you rectified frames or registered depth directly, and your effort belongs in verifying that output rather than reproducing the toolkit's map.
A factory 3D sensor projects its own pattern and computes correspondence on board, so the geometry questions above are already solved inside the housing. Work the stereo disparity troubleshooting path when its output looks wrong. Come back to map surgery only when you are calibrating a pair of separate area scan cameras yourself. Most factory stereo work falls into one of two families:
- Type
- Stereo / structured light
- Type
- Stereo / structured light
Both are complete 3D cameras rather than bare sensor pairs: the rectification lives in their internal pipeline, and yours consumes the depth output.
Which checks must a rebuilt map pair pass before production?
Three checks pass a rebuilt pair for production: the mean vertical epipolar error on matched features stays below 0.3 px with no point worse than 1 px, and the maximum map difference against your reference build stays under 0.05 px on identical inputs.
The numbers have a basis. Sub-pixel stereo matching localizes to a few tenths of a pixel, so rectification error feeds straight into disparity noise; a mean under 0.3 px keeps it below the matcher's own floor. The 0.05 px map tolerance keeps resampling an order of magnitude below that: on a hard edge of 50 gray levels per pixel, 0.05 px is under three levels of brightness shift, invisible next to matching noise.
Also confirm stability: re-power the cameras, rerun the diff against the stored maps and expect unchanged values; the table is pure calibration arithmetic, so a change means the head moved.
Before you sign the pair off:
- Print dtype and shape before diffing — CV_16SC2 data read as floats looks like noise.
- Build both maps from one calibration: same K, distortion terms, R1, R2, new camera matrix and output size.
- Diff per plane and report the max and mean absolute difference in pixels, plus the error image, not one number.
- Match the pattern to the table above and fix the input; never patch map pixels.
- Check the frames themselves: horizontal epipolar lines on a chessboard, then the recorded stereo frame verification on saved pairs.
If the rebuild is happening because you are specifying your own stereo head, pick the pair in the camera finder or reach us through the contact page with resolution, baseline and cycle time. The 3D topic page holds more on rectification and depth.
Questions
What does the Q matrix returned by rectifyStereoImages do?
Q is the reprojection matrix. Multiply it by the homogeneous pixel vector (x, y, disparity, 1) and you get (X, Y, Z, W) in the left camera's 3D frame; divide by W and the depth is baseline times focal length over disparity. The maps place pixels into rectified rows so this one matrix is enough to go from a disparity map to metric 3D.
Can I edit a rectification map to shift the rectified image?
Yes. Each value is a source coordinate, so adding a constant n to every x value makes the output sample n pixels further right, which shifts the visible content left by n pixels; adding to the y plane moves content up. Keep the change as a separate derived map file so the calibrated original stays untouched.
How much memory do rectification maps need at 5 MP?
A 2448 × 2048 map holds 5,013,504 pixels. As two CV_32FC1 planes that is 5,013,504 × 8 B ≈ 40 MB per camera, and the compressed CV_16SC2 + CV_16UC1 pair is 6 B per pixel ≈ 30 MB. A calibrated stereo pair therefore holds every representation comfortably under half a gigabyte, so while debugging there is no reason to accept the fixed-point pair's loss of precision.
Why does my rectified image have black borders after remap?
The map is allowed to point outside the source frame: wherever a destination pixel's source coordinate falls beyond the edge, remap has no data and fills the border value, black by default. Restrict your working region to the valid overlap, or rectify to a slightly smaller output view; the same valid-region logic is why disparity maps lose their borders.
Do the maps change when the cameras are powered off and on?
No. A map depends only on the calibrated intrinsics, the extrinsic rotation and translation and the chosen rectified geometry — fixed numbers once calibration is done. Rebuild only after a recalibration, a lens change or any mechanical disturbance of a camera; after a power cycle, a quick epipolar check on a test scene is enough to confirm nothing moved.


