OpenCV 4.12.0
开源计算机视觉
加载中...
搜索中...
无匹配项
Diamond 标记检测

上一教程: ChArUco板检测
下一教程: 使用ArUco和ChArUco进行标定
ChArUco钻石标记(或简称钻石标记)是由3x3的方格组成的棋盘,白色方格内有4个ArUco标记。它在外观上类似于ChArUco板,但概念上有所不同。

钻石标记示例

在ChArUco板和钻石标记中,它们的检测都基于先前检测到的ArUco标记。在ChArUco的情况下,使用的标记是通过直接查看它们的标识符来选择的。这意味着如果在图像中找到一个标记(包含在板中),它将自动被假定为属于该板。此外,如果在图像中多次找到标记板,则会产生歧义,因为系统将无法知道应该使用哪一个作为板。

另一方面,钻石标记的检测不是基于标识符的。相反,它们的检测是基于标记的相对位置。因此,标记标识符可以在同一钻石中或不同钻石之间重复,并且可以同时检测到它们而不会产生歧义。但是,由于基于标记的相对位置查找标记的复杂性,钻石标记的尺寸仅限于3x3方格和4个标记。

与单个ArUco标记一样,每个钻石标记由4个角和一个标识符组成。四个角对应于标记中的4个棋盘角,标识符实际上是一个包含4个数字的数组,这些数字是钻石内四个ArUco标记的标识符。

钻石标记在那些应该允许重复标记的场景中很有用。例如

  • 通过使用钻石标记进行标记来增加单个标记的标识符数量。它们将允许最多N^4个不同的ID,其中N是所用字典中的标记数量。
  • 赋予四个标记中的每一个一个概念意义。例如,四个标记ID中的一个可以用于指示标记的比例(即方格的大小),这样可以通过仅更改四个标记中的一个,就可以在具有不同大小的环境中找到相同的钻石,而用户无需手动指示每个标记的比例。此示例包含在模块的samples文件夹中的 detect_diamonds.cpp 文件中。

此外,由于它的角是棋盘角,因此它们可用于精确的姿态估计。

钻石功能包含在 <opencv2/objdetect/charuco_detector.hpp>

ChArUco钻石创建

可以使用 cv::aruco::CharucoBoard::generateImage() 函数轻松创建钻石标记的图像。例如

vector<int> diamondIds = {ids[0], ids[1], ids[2], ids[3]};
aruco::CharucoBoard charucoBoard(Size(3, 3), (float)squareLength, (float)markerLength, dictionary, diamondIds);
Mat markerImg;
charucoBoard.generateImage(Size(3*squareLength + 2*margins, 3*squareLength + 2*margins), markerImg, margins, borderBits);

这将创建一个钻石标记图像,其正方形大小为200像素,标记大小为120像素。标记ID在第二个参数中以 cv::Vec4i 对象给出。钻石布局中标记ID的顺序与标准ChArUco板中的顺序相同,即顶部、左侧、右侧和底部。

生成的图像将是

钻石标记

一个完整的示例包含在 samples/cpp/tutorial_code/objectDetection/ 中的 create_diamond.cpp 中。

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

"_path_/mydiamond.png" -sl=200 -ml=120 -d=10 -ids=0,1,2,3

ChArUco钻石检测

与大多数情况一样,钻石标记的检测需要先前检测到的ArUco标记。在检测到标记后,使用 cv::aruco::CharucoDetector::detectDiamonds() 函数检测钻石

vector<int> markerIds;
vector<Vec4i> diamondIds;
vector<vector<Point2f> > markerCorners, diamondCorners;
vector<Vec3d> rvecs, tvecs;
detector.detectDiamonds(image, diamondCorners, diamondIds, markerCorners, markerIds);

cv::aruco::CharucoDetector::detectDiamonds() 函数接收原始图像和先前检测到的标记角和ID。如果markerCorners和markerIds为空,则该函数将检测aruco标记和ID。输入图像对于在ChArUco角上执行亚像素细化是必要的。它还接收正方形大小和标记大小之间的比率,这对于从标记的相对位置检测钻石和插值ChArUco角都是必需的。

该函数在两个参数中返回检测到的钻石。第一个参数 diamondCorners 是一个数组,包含每个检测到的钻石的所有四个角。其格式类似于 cv::aruco::ArucoDetector::detectMarkers() 函数检测到的角,并且对于每个钻石,角的表示顺序与ArUco标记中的顺序相同,即从左上角开始的顺时针顺序。第二个返回的参数 diamondIds 包含 diamondCorners 中返回的钻石角的所有ID。每个ID实际上是一个包含4个整数的数组,可以用 cv::Vec4i 表示。

可以使用函数 cv::aruco::drawDetectedDiamonds() 可视化检测到的钻石,该函数简单地接收图像以及钻石角和ID

if(diamondIds.size() > 0) {
aruco::drawDetectedDiamonds(imageCopy, diamondCorners, diamondIds);

结果与 cv::aruco::drawDetectedMarkers() 产生的结果相同,但会打印钻石的四个ID

检测到的钻石标记

一个完整的示例包含在 samples/cpp/tutorial_code/objectDetection/ 中的 detect_diamonds.cpp 中。

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

-dp=path_to_opencv/opencv/samples/cpp/tutorial_code/objectDetection/detector_params.yml -sl=0.4 -ml=0.25 -refine=3
-v=path_to_opencv/opencv/doc/tutorials/objdetect/charuco_diamond_detection/images/diamondmarkers.jpg
-cd=path_to_opencv/opencv/samples/cpp/tutorial_code/objectDetection/tutorial_dict.yml

ChArUco钻石姿态估计

由于ChArUco钻石由其四个角表示,因此可以使用与单个ArUco标记相同的方式估计其姿态,即使用 cv::solvePnP() 函数。例如

// 估计钻石姿态
size_t N = diamondIds.size();
if(estimatePose && N > 0) {
cv::Mat objPoints(4, 1, CV_32FC3);
rvecs.resize(N);
tvecs.resize(N);
if(!autoScale) {
// 设置坐标系
objPoints.ptr<Vec3f>(0)[0] = Vec3f(-squareLength/2.f, squareLength/2.f, 0);
objPoints.ptr<Vec3f>(0)[1] = Vec3f(squareLength/2.f, squareLength/2.f, 0);
objPoints.ptr<Vec3f>(0)[2] = Vec3f(squareLength/2.f, -squareLength/2.f, 0);
objPoints.ptr<Vec3f>(0)[3] = Vec3f(-squareLength/2.f, -squareLength/2.f, 0);
// 计算每个标记的姿态
for (size_t i = 0ull; i < N; i++)
solvePnP(objPoints, diamondCorners.at(i), camMatrix, distCoeffs, rvecs.at(i), tvecs.at(i));
if(estimatePose) {
for(size_t i = 0u; i < diamondIds.size(); i++)
cv::drawFrameAxes(imageCopy, camMatrix, distCoeffs, rvecs[i], tvecs[i], squareLength*1.1f);
}

该函数将获得每个钻石标记的旋转和平移向量,并将它们存储在 rvecstvecs 中。请注意,钻石角是棋盘正方形角,因此必须提供正方形长度以进行姿态估计,而不是标记长度。还需要相机标定参数。

最后,可以绘制一个轴来检查估计的姿态是否正确,使用 drawFrameAxes()

检测到的钻石轴

钻石姿态的坐标系将位于标记的中心,Z轴指向外,就像简单的ArUco标记姿态估计一样。

示例视频

ChArUco钻石姿态也可以估计为ChArUco板

for (size_t i = 0ull; i < N; i++) { // 将钻石姿态估计为Charuco板
Mat objPoints_b, imgPoints;
// 钻石的坐标系放置在板平面上,以左下角为中心
vector<int> charucoIds = {0, 1, 3, 2}; // 如果是逆时针顺序,则Z轴指向平面内
// vector<int> charucoIds = {0, 2, 3, 1}; // 如果是顺时针顺序,则Z轴指向平面外
charucoBoard.matchImagePoints(diamondCorners[i], charucoIds, objPoints_b, imgPoints);
solvePnP(objPoints_b, imgPoints, camMatrix, distCoeffs, rvecs[i], tvecs[i]);
}

一个完整的示例包含在 samples/cpp/tutorial_code/objectDetection/ 中的 detect_diamonds.cpp 中。

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

-dp=path_to_opencv/opencv/samples/cpp/tutorial_code/objectDetection/detector_params.yml -sl=0.4 -ml=0.25 -refine=3
-v=path_to_opencv/opencv/doc/tutorials/objdetect/charuco_diamond_detection/images/diamondmarkers.jpg
-cd=path_to_opencv/opencv/samples/cpp/tutorial_code/objectDetection/tutorial_dict.yml
-c=path_to_opencv/opencv/samples/cpp/tutorial_code/objectDetection/tutorial_camera_params.yml