立体鱼眼镜头标定
由于鱼眼镜头图像存在畸变,直接处理立体相机拍摄的鱼眼镜头图像可能会比较困难。使用高品质的鱼眼镜头模型(例如 Scaramuzza 模型 [1])将鱼眼镜头拍摄的图像转换为标准图像,通常会带来好处。尽管这种转换会牺牲部分视场,但它使得能够使用诸如立体校正和视差计算等标准处理技术,这些技术原本是为非鱼眼立体相机设计的。
本示例演示了如何使用 estimateFisheyeParameters 函数和 estimateStereoBaseline 函数对立体鱼眼镜头进行标定。
概述
请按照以下步骤对立体鱼眼镜头进行标定,具体方法是确定每台相机的内参,并估算两台相机之间的基线距离:
拍摄标定图像:为了准确标定相机,请按照准备相机并拍摄图像以进行相机标定中列出的指南采集 10 至 20 张标定图像。本示例使用了来自 WPI 激光雷达视觉 SLAM 数据集 [2] 的立体鱼眼镜头图像。这些图像使用英特尔 RealSense T265 相机采集的 [3]。
估计内参:估算每台相机的鱼眼镜头内参。这些参数描述了每台相机的镜头属性。
构建虚拟针孔相机:将鱼眼镜头拍摄的图像转换为仿佛是用针孔相机拍摄的图像。实现这一目标的方法是:对鱼眼镜头图像的中心部分去畸变,并生成描述该新虚拟相机的相机内参。
估算基准:利用虚拟针孔相机的参数来估算立体鱼眼镜头的基线。
下载数据
使用以下代码将标定图像下载到一个文件夹中。
datasetFolder = "stereo_fisheye_camera_calibration"; datasetURL = "https://ssd.mathworks.com/supportfiles/shared_nav_vision/data/"; dataFolder = fullfile("wpi_stereo_visual_slam_dataset"); if ~isfolder(dataFolder) mkdir(dataFolder) end folderFullPath = fullfile(dataFolder,datasetFolder); if ~isfolder(folderFullPath) fileWithExt = datasetFolder + ".zip"; zipFilePath = fullfile(dataFolder,fileWithExt); disp("Downloading " + fileWithExt + " (46 MB). This download can take a few seconds.") websave(folderFullPath,datasetURL + fileWithExt); unzip(zipFilePath,dataFolder); end
Downloading stereo_fisheye_camera_calibration.zip (46 MB). This download can take a few seconds.
检查第一组标定图像。
imds1 = imageDatastore(fullfile(folderFullPath, "left")); imds2 = imageDatastore(fullfile(folderFullPath, "right")); I1 = readimage(imds1, 1); I2 = readimage(imds2, 1); imshowpair(I1, I2, "montage");

估计鱼眼相机内参
分别估计相机 1 和相机 2 的鱼眼镜头内参。检测立体图像对中的棋盘格关键点。
[imagePoints, patternDims, pairsUsed] = detectCheckerboardPoints(imds1.Files, imds2.Files, HighDistortion=true);
为棋盘格关键点生成世界坐标。
squareSize = 40; % millimeters worldPoints = patternWorldPoints("checkerboard", patternDims, squareSize);
利用图像点和世界坐标点,估算鱼眼镜头标定参数。
imageSize = [size(I1,1) size(I1,2)]; imagePoints1 = imagePoints(:,:,:,1); imagePoints2 = imagePoints(:,:,:,2); % Calibrate camera 1 camParams1 = estimateFisheyeParameters(imagePoints1, worldPoints, imageSize); % Calibrate camera 2 camParams2 = estimateFisheyeParameters(imagePoints2, worldPoints, imageSize);
构建虚拟针孔相机
使用 undistortFisheyePoints 函数对检测到的关键点进行去畸变校正,并根据校正后的点获取对应虚拟针孔相机的相机内参。
undistortedImagePoints1 = imagePoints1; undistortedImagePoints2 = imagePoints2; numImagesUsed = sum(pairsUsed); for i = 1:numImagesUsed [undistortedImagePoints1(:,:,i), camIntrinsics1] = undistortFisheyePoints(imagePoints1(:,:,i), camParams1.Intrinsics); [undistortedImagePoints2(:,:,i), camIntrinsics2] = undistortFisheyePoints(imagePoints2(:,:,i), camParams2.Intrinsics); end
估算基准
利用虚拟相机的内参,估算两台相机之间的平移和方向关系。
undistortedImagePoints = cat(4, undistortedImagePoints1, undistortedImagePoints2); stereoParams = estimateStereoBaseline(undistortedImagePoints, worldPoints, camIntrinsics1, camIntrinsics2);
标定后的基线与相机数据手册 [3] 中规定的 64 毫米值非常吻合。
disp("Calibrated stereo baseline (mm): " + abs(stereoParams.PoseCamera2.Translation(1)));Calibrated stereo baseline (mm): 64.9251
直观展示标定精度。
figure showReprojectionErrors(stereoParams);

可视化相机外参。
figure showExtrinsics(stereoParams);

校正立体鱼眼镜头图像
对立体鱼眼镜头完成标定后,您可以对立体图像进行校正并计算视差,作为三维重建和立体视觉 SLAM 的预处理步骤。
对第一组鱼眼镜头立体图像进行畸变校正,并显示处理结果。
J1 = undistortFisheyeImage(I1, camParams1.Intrinsics);
J2 = undistortFisheyeImage(I2, camParams2.Intrinsics);
figure
imshowpair(J1, J2, "montage");
对未去畸变的立体鱼眼镜头图像进行校正。
[J1Full, J2Full, reprojectionMatrix] = rectifyStereoImages(J1,J2,stereoParams);
显示红蓝立体图。
figure imshow(stereoAnaglyph(J1Full, J2Full));

利用校正后的图像,通过半全局匹配算法计算视差图。将视差范围指定为 [0, 128],并将唯一性最小值指定为 10。
disparityRange = [0 128]; disparityMap = disparitySGM(J1Full, J2Full, DisparityRange=disparityRange, UniquenessThreshold=10);
显示视差图。将显示范围设置为与视差范围相同的值。
figure
imshow(disparityMap, disparityRange)
colormap parula
colorbar
您可以在以下页面中查阅有关三维重建和视觉 SLAM 的更多详细信息:
参考资料
[1] Scaramuzza, D., A. Martinelli, and R. Siegwart."A Toolbox for Easy Calibrating Omnidirectional Cameras."Proceedings to IEEE International Conference on Intelligent Robots and Systems (IROS 2006).Beijing, China, October 7–15, 2006.
[2] https://github.com/WPI-APA-Lab/WPI-Lidar-Visual-SLAM-Dataset