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

上一篇教程: 钻石标记检测
下一篇教程: 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 << "点匹配失败,请重试." << endl;
continue;
}
cout << "帧已捕获" << 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);
#define CV_64F
定义 interface.h:79

在每个视点上捕获的 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 << "帧已捕获" << 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);

calibrate_camera.cpp 中包含一个完整的工作示例,该示例位于 samples/cpp/tutorial_code/objectDetection 文件夹内。

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

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