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

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

菱形标记示例

在 ChArUco 棋盘和菱形标记中,它们的目标检测都基于先前检测到的 ArUco 标记。在 ChArUco 案例中,所用标记是通过直接查看其标识符来选择的。这意味着如果图像中找到了一个标记(包含在棋盘中),则系统会自动假定该标记属于棋盘。此外,如果图像中找到多个标记棋盘,则会产生歧义,因为系统无法知道哪一个应该用作棋盘。

另一方面,菱形标记的检测并不基于标识符。然而,它们的检测是基于标记的相对位置。结果,标记标识符可以在同一菱形或不同菱形中重复,并且它们可以同时检测,而不会产生歧义。然而,由于基于相对位置查找标记的复杂性,菱形标记的限制为 3x3 方格和 4 个标记。

正如单个 ArUco 标记,每个菱形标记由 4 个角和一个标识符组成。四个角对应于标记中的 4 个棋盘角,而标识符实际上是一个由 4 个数字组成的数组,它们是菱形内四个 ArUco 标记的标识符。

菱形标记在允许重复标记的场景中很有用。例如:

  • 通过使用菱形标记进行标记,来增加单个标记的标识符数量。它们将允许最多 N^4 个不同的 id,其中 N 是所用词典中的标记数量。
  • 为四个标记中的每一个赋予一个概念含义。例如,四个标记 id 之一可用于指示标记的比例(即方格的大小),以便仅通过更改四个标记中的一个来在以不同大小的环境中找到同一个菱形,而用户无需手动指示每个菱形的比例。此案例包含在模块示例文件夹中的 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 中包含一个完整的示例。

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

"_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` 的四个整数数组。

可使用函数 `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));
n 维稠密数组类
定义 mat.hpp:812
#define CV_32FC3
定义 interface.h:120
if(estimatePose) {
forsize_t i = 0u;i < diamondIds.size();i++)
cv::drawFrameAxes(imageCopy,camMatrix,distCoeffs,rvecs[i],tvecs[i],squareLength*1.1f);
}
void drawFrameAxes(InputOutputArray image,InputArray cameraMatrix,InputArray distCoeffs,InputArray rvec,InputArray tvec,float length,int thickness=3)
从位姿估计中绘制世界/对象坐标系的轴。

该函数将获取各个菱形标记的旋转和平移矢量,并将它们存储在 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