主要内容

本页采用了机器翻译。点击此处可查看英文原文。

立体鱼眼镜头标定

自 R2025a 起

由于鱼眼镜头图像存在畸变,直接处理立体相机拍摄的鱼眼镜头图像可能会比较困难。使用高品质的鱼眼镜头模型(例如 Scaramuzza 模型 [1])将鱼眼镜头拍摄的图像转换为标准图像,通常会带来好处。尽管这种转换会牺牲部分视场,但它使得能够使用诸如立体校正和视差计算等标准处理技术,这些技术原本是为非鱼眼立体相机设计的。

本示例演示了如何使用 estimateFisheyeParameters 函数和 estimateStereoBaseline 函数对立体鱼眼镜头进行标定。

概述

请按照以下步骤对立体鱼眼镜头进行标定,具体方法是确定每台相机的内参,并估算两台相机之间的基线距离:

  1. 拍摄标定图像:为了准确标定相机,请按照准备相机并拍摄图像以进行相机标定中列出的指南采集 10 至 20 张标定图像。本示例使用了来自 WPI 激光雷达视觉 SLAM 数据集 [2] 的立体鱼眼镜头图像。这些图像使用英特尔 RealSense T265 相机采集的 [3]

  2. 估计内参:估算每台相机的鱼眼镜头内参。这些参数描述了每台相机的镜头属性。

  3. 构建虚拟针孔相机:将鱼眼镜头拍摄的图像转换为仿佛是用针孔相机拍摄的图像。实现这一目标的方法是:对鱼眼镜头图像的中心部分去畸变,并生成描述该新虚拟相机的相机内参。

  4. 估算基准:利用虚拟针孔相机的参数来估算立体鱼眼镜头的基线。

下载数据

使用以下代码将标定图像下载到一个文件夹中。

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");

Figure contains an axes object. The hidden axes object contains an object of type image.

估计鱼眼相机内参

分别估计相机 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 contains an axes object. The axes object with title Mean Reprojection Error per Image, xlabel Image Pairs, ylabel Mean Error in Pixels contains 5 objects of type bar, line. These objects represent Camera 1, Camera 2, Overall Mean Error: 0.84 pixels.

可视化相机外参。

figure
showExtrinsics(stereoParams);

Figure contains an axes object. The axes object with title Extrinsic Parameters Visualization, xlabel X (mm), ylabel Z (mm) contains 42 objects of type patch, text, line.

校正立体鱼眼镜头图像

对立体鱼眼镜头完成标定后,您可以对立体图像进行校正并计算视差,作为三维重建和立体视觉 SLAM 的预处理步骤。

对第一组鱼眼镜头立体图像进行畸变校正,并显示处理结果。

J1 = undistortFisheyeImage(I1, camParams1.Intrinsics);
J2 = undistortFisheyeImage(I2, camParams2.Intrinsics);

figure
imshowpair(J1, J2, "montage");

Figure contains an axes object. The hidden axes object contains an object of type image.

对未去畸变的立体鱼眼镜头图像进行校正。

[J1Full, J2Full, reprojectionMatrix] = rectifyStereoImages(J1,J2,stereoParams);

显示红蓝立体图。

figure
imshow(stereoAnaglyph(J1Full, J2Full));

Figure contains an axes object. The hidden axes object contains an object of type image.

利用校正后的图像,通过半全局匹配算法计算视差图。将视差范围指定为 [0, 128],并将唯一性最小值指定为 10。

disparityRange = [0 128];
disparityMap = disparitySGM(J1Full, J2Full, DisparityRange=disparityRange, UniquenessThreshold=10);

显示视差图。将显示范围设置为与视差范围相同的值。

figure
imshow(disparityMap, disparityRange)
colormap parula
colorbar

Figure contains an axes object. The hidden axes object contains an object of type image.

您可以在以下页面中查阅有关三维重建和视觉 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

[3] Intel® RealSense™ Tracking Camera T265