Skip to content

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> 中

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

这将创建一个方格尺寸为 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

diamondmarkers.jpg diamondmarker.png detecteddiamonds.jpg

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

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:

结果与 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

diamondsaxis.jpg

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

该函数将为每个菱形标记获取旋转向量和平移向量,并将它们存储在 rvecs 和 tvecs 中。注意,菱形角点是棋盘方格的角点,因此位姿估计必须提供方格边长,而不是标记边长。还需要相机标定参数。

最后,可以使用 drawFrameAxes() 绘制坐标轴以检查估计的位姿是否正确:

菱形位姿的坐标系将位于标记的中心,Z 轴朝外指向,如同简单的 ArUco 标记位姿估计一样。

示例视频:

视频演示

ChArUco 菱形位姿也可以像 ChArUco 标定板那样估计:

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