OpenCV 4.11.0
开源计算机视觉
加载中…
搜索中…
未找到匹配项
菱形标记检测

上一教程: 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 实际上是一个可以由 `cv::Vec4i` 表示的包含 4 个整数的数组。

可以使用 `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