OpenCV 4.12.0
开源计算机视觉
加载中...
搜索中...
无匹配项
使用 ArUco 和 ChArUco 进行校准

上一个教程: 检测 Diamond Markers
下一个教程: Aruco 模块常见问题解答
ArUco 模块也可以用于标定相机。相机标定包括获取相机的内参和畸变系数。这些参数是固定的,除非相机的光学器件被修改,因此相机标定只需要做一次。

相机标定通常使用 OpenCV 的 cv::calibrateCamera() 函数执行。此函数需要从不同视点的环境点及其在相机图像中的投影之间的一些对应关系。一般来说,这些对应关系是从棋盘格图案的角点获得的。有关更多详细信息,请参阅 cv::calibrateCamera() 函数文档或 OpenCV 标定教程。

使用 ArUco 模块,可以基于 ArUco 标记角点或 ChArUco 角点执行标定。使用 ArUco 进行标定比使用传统的棋盘格图案更通用,因为它允许遮挡或局部视图。

如前所述,可以使用标记角点或 ChArUco 角点进行标定。但是,强烈建议使用 ChArUco 角点方法,因为与标记角点相比,提供的角点更准确。仅应在由于任何类型的限制而无法使用 ChArUco 板的场景中才应使用标准板进行标定。

使用 ChArUco 板进行标定

要使用 ChArUco 板进行标定,需要从不同的视点检测板,就像标准标定使用传统的棋盘格图案一样。但是,由于使用 ChArUco 的好处,允许遮挡和局部视图,并且并非所有角点都需要在所有视点中都可见。

ChArUco 标定视点

使用 cv::calibrateCamera()cv::aruco::CharucoBoard 的示例

// 创建 charuco 板对象和 CharucoDetector
aruco::CharucoBoard board(Size(squaresX, squaresY), squareLength, markerLength, dictionary);
aruco::CharucoDetector detector(board, charucoParams, detectorParams);
// 从每个帧收集数据
vector<Mat> allCharucoCorners, allCharucoIds;
vector<vector<Point2f>> allImagePoints;
vector<vector<Point3f>> allObjectPoints;
vector<Mat> allImages;
Size imageSize;
while(inputVideo.grab()) {
Mat image, imageCopy;
inputVideo.retrieve(image);
vector<int> markerIds;
vector<vector<Point2f>> markerCorners;
Mat currentCharucoCorners, currentCharucoIds;
vector<Point3f> currentObjectPoints;
vector<Point2f> currentImagePoints;
// 检测 ChArUco 板
detector.detectBoard(image, currentCharucoCorners, currentCharucoIds);
if(key == 'c' && currentCharucoCorners.total() > 3) {
// 匹配图像点
board.matchImagePoints(currentCharucoCorners, currentCharucoIds, currentObjectPoints, currentImagePoints);
if(currentImagePoints.empty() || currentObjectPoints.empty()) {
cout << "Point matching failed, try again." << endl;
continue;
}
cout << "Frame captured" << endl;
allCharucoCorners.push_back(currentCharucoCorners);
allCharucoIds.push_back(currentCharucoIds);
allImagePoints.push_back(currentImagePoints);
allObjectPoints.push_back(currentObjectPoints);
allImages.push_back(image);
imageSize = image.size();
}
}
Mat cameraMatrix, distCoeffs;
if(calibrationFlags & CALIB_FIX_ASPECT_RATIO) {
cameraMatrix = Mat::eye(3, 3, CV_64F);
cameraMatrix.at<double>(0, 0) = aspectRatio;
}
// 使用 ChArUco 标定相机
double repError = calibrateCamera(allObjectPoints, allImagePoints, imageSize, cameraMatrix, distCoeffs,
noArray(), noArray(), noArray(), noArray(), noArray(), calibrationFlags);

在每个视点捕获的 ChArUco 角点和 ChArUco 标识符存储在向量 allCharucoCornersallCharucoIds 中,每个视点一个元素。

calibrateCamera() 函数将使用相机标定参数填充 cameraMatrixdistCoeffs 数组。它将返回从标定获得的重投影误差。rvecstvecs 中的元素将填充相机在每个视点中的估计姿势(相对于 ChArUco 板)。

最后,calibrationFlags 参数确定标定的一些选项。

一个完整的示例包含在 samples/cpp/tutorial_code/objectDetection 文件夹中的 calibrate_camera_charuco.cpp 中。

这些示例现在通过命令行通过 cv::CommandLineParser 获取输入。对于此文件,示例参数如下所示

"camera_calib.txt" -w=5 -h=7 -sl=0.04 -ml=0.02 -d=10
-v=path/img_%02d.jpg

来自 opencv/samples/cpp/tutorial_code/objectDetection/tutorial_camera_charuco.yml 的相机标定参数是通过放置在此 文件夹 中的 img_00.jpg-img_03.jpg 获得的。

使用 ArUco 板进行标定

如前所述,建议使用 ChAruco 板代替 ArUco 板进行相机标定,因为 ChArUco 角点比标记角点更准确。但是,在某些特殊情况下,必须要求使用基于 ArUco 板的标定。与前一种情况一样,它需要从不同的视点检测 ArUco 板。

ArUco 标定视点

使用 cv::calibrateCamera()cv::aruco::GridBoard 的示例

// 创建板对象和 ArucoDetector
aruco::GridBoard gridboard(Size(markersX, markersY), markerLength, markerSeparation, dictionary);
aruco::ArucoDetector detector(dictionary, detectorParams);
// 收集用于标定的帧
vector<vector<vector<Point2f>>> allMarkerCorners;
vector<vector<int>> allMarkerIds;
Size imageSize;
while(inputVideo.grab()) {
Mat image, imageCopy;
inputVideo.retrieve(image);
vector<int> markerIds;
vector<vector<Point2f>> markerCorners, rejectedMarkers;
// 检测标记
detector.detectMarkers(image, markerCorners, markerIds, rejectedMarkers);
// 重新查找策略以检测更多标记
if(refindStrategy) {
detector.refineDetectedMarkers(image, gridboard, markerCorners, markerIds, rejectedMarkers);
}
if(key == 'c' && !markerIds.empty()) {
cout << "Frame captured" << endl;
allMarkerCorners.push_back(markerCorners);
allMarkerIds.push_back(markerIds);
imageSize = image.size();
}
}
Mat cameraMatrix, distCoeffs;
if(calibrationFlags & CALIB_FIX_ASPECT_RATIO) {
cameraMatrix = Mat::eye(3, 3, CV_64F);
cameraMatrix.at<double>(0, 0) = aspectRatio;
}
// 准备用于标定的数据
vector<Point3f> objectPoints;
vector<Point2f> imagePoints;
vector<Mat> processedObjectPoints, processedImagePoints;
size_t nFrames = allMarkerCorners.size();
for(size_t frame = 0; frame < nFrames; frame++) {
Mat currentImgPoints, currentObjPoints;
gridboard.matchImagePoints(allMarkerCorners[frame], allMarkerIds[frame], currentObjPoints, currentImgPoints);
if(currentImgPoints.total() > 0 && currentObjPoints.total() > 0) {
processedImagePoints.push_back(currentImgPoints);
processedObjectPoints.push_back(currentObjPoints);
}
}
// 标定相机
double repError = calibrateCamera(processedObjectPoints, processedImagePoints, imageSize, cameraMatrix, distCoeffs,
noArray(), noArray(), noArray(), noArray(), noArray(), calibrationFlags);

一个完整的示例包含在 samples/cpp/tutorial_code/objectDetection 文件夹中的 calibrate_camera.cpp 中。

这些示例现在通过命令行通过 cv::CommandLineParser 获取输入。对于此文件,示例参数如下所示

"camera_calib.txt" -w=5 -h=7 -l=100 -s=10 -d=10 -v=path/aruco_videos_or_images