ChArUco 标定板检测
ArUco 标记和标定板由于其快速的检测和通用性而非常有用。然而,ArUco 标记的问题之一是其角点位置的精度不是太高,即使应用了亚像素精化也是如此。
相反,棋盘格图案的角点可以更精确地精化,因为每个角点都被两个黑色方块包围。然而,查找棋盘格图案不如查找 ArUco 标定板通用:它必须完全可见,不允许遮挡。
ChArUco 标定板试图结合这两种方法的优势:
ArUco 部分用于插值棋盘角点的位置,从而使其具有标记标定板的通用性,因为它允许遮挡或部分可见。此外,由于插值的角点属于棋盘,它们在亚像素精度方面非常精确。
当需要高精度时,例如在相机标定中,ChArUco 标定板是比标准 ArUco 标定板更好的选择。

在本教程中,你将学到:
- 如何创建 ChArUco 标定板?
- 如何在不进行相机标定的情况下检测 ChArUco 角点?
- 如何在相机标定和位姿估计的情况下检测 ChArUco 角点?
你可以在 samples/cpp/tutorial_code/objectDetection/detect_board_charuco.cpp 中找到此代码
以下是如何实现目标列表中列出的所有内容的示例代码。
ChArUco 标定板创建
Section titled “ChArUco 标定板创建”aruco 模块提供了 cv::aruco::CharucoBoard 类,它表示一个 ChArUco 标定板,并继承自 cv::aruco::Board 类。
该类以及 ChArUco 的其余功能定义在:
要定义一个 cv::aruco::CharucoBoard,需要:
- X 和 Y 方向上棋盘方格的数量。
- 方格边长。
- 标记边长。
- 标记的字典。
- 所有标记的 id。
至于 cv::aruco::GridBoard 对象,aruco 模块可以轻松创建 cv::aruco::CharucoBoard。可以使用 cv::aruco::CharucoBoard 构造函数从这些参数轻松创建该对象:
- 第一个参数分别是 X 和 Y 方向上的方格数量。
- 第二和第三个参数分别是方格和标记的边长。它们可以用任何单位提供,但请记住,此标定板估计的位姿将以相同的单位度量(通常使用米)。
- 最后,提供标记的字典。
每个标记的 id 默认按从 0 开始的升序分配,与 cv::aruco::GridBoard 构造函数中一样。这可以通过 board.ids 访问 ids 向量来轻松自定义,就像在 cv::aruco::Board 父类中一样。
一旦我们有了 cv::aruco::CharucoBoard 对象,就可以创建一个图像来打印它。有两种方法可以做到这一点:
- 使用脚本
apps/pattern-tools/generate_pattern.py,参见 @subpage tutorial_camera_calibration_pattern。 - 使用函数
cv::aruco::CharucoBoard::generateImage()。
cv::aruco::CharucoBoard::generateImage() 函数在 cv::aruco::CharucoBoard 类中提供,可以使用以下代码调用:
- 第一个参数是输出图像的大小(以像素为单位)。如果这与标定板尺寸不成比例,它将居中于图像中。
- 第二个参数是带有 ChArUco 标定板的输出图像。
- 第三个参数是(可选的)边距(以像素为单位),这样不会有任何标记碰到图像边界。
- 最后是标记边框的大小,类似于
cv::aruco::generateImageMarker()函数。默认值为 1。
输出图像将类似于这样:
samples/cpp/tutorial_code/objectDetection/ 中的 create_board_charuco.cpp 包含了一个完整的可运行示例。
现在,示例 create_board_charuco.cpp 通过 cv::CommandLineParser 从命令行获取输入。对于此文件,示例参数如下:
"_output_path_/chboard.png" -w=5 -h=7 -sl=100 -ml=60 -d=10

ChArUco 标定板检测
Section titled “ChArUco 标定板检测”当你检测 ChArUco 标定板时,你实际检测的是标定板的每个棋盘角点。
ChArUco 标定板上的每个角点都分配了一个唯一的标识符(id)。这些 id 从 0 到标定板中角点的总数。 ChArUco 标定板检测的步骤可以分解为以下步骤:
- 获取输入图像
要检测标记的原始图像。该图像是进行 ChArUco 角点亚像素精化所必需的。
- 读取相机标定参数(仅用于带相机标定的检测)
readCameraParameters 的参数是:
- 第一个参数是相机内参矩阵和畸变系数的路径。
- 第二和第三个参数是 cameraMatrix 和 distCoeffs。
该函数将这些参数作为输入,并返回一个表示相机标定参数是否有效的布尔值。对于不带标定的 ChArUco 角点检测,此步骤不是必需的。
- 检测标记并从标记插值 ChArUco 角点
ChArUco 角点的检测基于先前检测到的标记。
因此,首先检测标记,然后从标记插值 ChArUco 角点。
检测 ChArUco 角点的方法是 cv::aruco::CharucoDetector::detectBoard()。
detectBoard 的参数是:
image- 输入图像。charucoCorners- 检测到的角点的图像位置输出列表。charucoIds-charucoCorners中每个检测到的角点的输出 id。markerCorners- 检测到的标记角点的输入/输出向量。markerIds- 检测到的标记的标识符的输入/输出向量。
如果 markerCorners 和 markerIds 为空,该函数将检测 ArUco 标记和 id。
如果提供了标定参数,则 ChArUco 角点的插值过程是:首先从 ArUco 标记估计一个粗略的位姿,然后将 ChArUco 角点重新投影回图像。
另一方面,如果未提供标定参数,则通过计算 ChArUco 平面与 ChArUco 图像投影之间的相应单应性来插值 ChArUco 角点。
使用单应性的主要问题是,插值对图像畸变更敏感。实际上,单应性仅使用每个 ChArUco 角点最近的标记来执行,以减少畸变的影响。
当为 ChArUco 标定板检测标记时,特别是使用单应性时,建议禁用标记的角点精化。原因是,由于棋盘方格彼此邻近,亚像素过程可能在角点位置产生重要偏差,而这些偏差会传播到 ChArUco 角点插值,导致结果不佳。
注意: 为避免偏差,棋盘方格与 ArUco 标记之间的边距应大于一个标记模块的 70%。
此外,只返回其周围两个标记都被找到的那些角点。如果周围两个标记中的任何一个未被检测到,这通常意味着存在某些遮挡或该区域的图像质量不佳。无论如何,最好不要考虑该角点,因为我们要确保插值的 ChArUco 角点非常精确。
插值 ChArUco 角点之后,会执行亚像素精化。
一旦我们插值了 ChArUco 角点,我们可能想绘制它们以查看其检测是否正确。这可以使用 cv::aruco::drawDetectedCornersCharuco() 函数轻松完成:
imageCopy是将要绘制角点的图像(通常与检测到角点的图像相同)。outputImage将是带有绘制角点的inputImage的副本。charucoCorners和charucoIds是来自cv::aruco::CharucoDetector::detectBoard()函数的检测到的 ChArUco 角点。- 最后,最后一个参数是我们想要绘制角点的(可选)颜色,类型为
cv::Scalar。
对于此图像:
结果将是:
在存在遮挡的情况下,如下图所示,尽管一些角点清晰可见,但由于遮挡,并非其周围所有标记都被检测到,因此它们没有被插值:
示例视频:
samples/cpp/tutorial_code/objectDetection/ 中的 detect_board_charuco.cpp 包含了一个完整的可运行示例。
现在,示例 detect_board_charuco.cpp 通过 cv::CommandLineParser 从命令行获取输入。对于此文件,示例参数如下:
-w=5 -h=7 -sl=0.04 -ml=0.02 -d=10 -v=/path_to_opencv/opencv/doc/tutorials/objdetect/charuco_detection/images/choriginal.jpg
ChArUco 位姿估计
Section titled “ChArUco 位姿估计”ChArUco 标定板的最终目标是为高精度标定或位姿估计找到非常精确的角点。
aruco 模块提供了一个可以轻松执行 ChArUco 位姿估计的函数。与 cv::aruco::GridBoard 中一样,cv::aruco::CharucoBoard 的坐标系位于标定板平面上,Z 轴朝内指向,并以标定板左下角为中心。
注意: 在 OpenCV 4.6.0 之后,标定板的坐标系有一个不兼容的变更,现在坐标系位于标定板平面上,Z 轴朝向平面内(以前该轴朝向平面外)。
CW 顺序的 objPoints 对应于朝向平面内的 Z 轴。
CCW 顺序的 objPoints 对应于朝向平面外的 Z 轴。
参见 PR https://github.com/opencv/opencv_contrib/pull/3174
要对 ChArUco 标定板进行位姿估计,你应当使用 cv::aruco::CharucoBoard::matchImagePoints() 和 cv::solvePnP():
charucoCorners和charucoIds参数是来自cv::aruco::CharucoDetector::detectBoard()函数的检测到的 ChArUco 角点。cameraMatrix和distCoeffs是位姿估计所必需的相机标定参数。- 最后,
rvec和tvec参数是 ChArUco 标定板的输出位姿。 - 如果位姿被正确估计,
cv::solvePnP()返回 true,否则返回 false。失败的主要原因是用于位姿估计的角点不够,或者它们位于同一条直线上。
可以使用 cv::drawFrameAxes() 绘制坐标轴来检查位姿是否被正确估计。结果将是:(X:红色,Y:绿色,Z:蓝色)
samples/cpp/tutorial_code/objectDetection/ 中的 detect_board_charuco.cpp 包含了一个完整的可运行示例。
现在,示例 detect_board_charuco.cpp 通过 cv::CommandLineParser 从命令行获取输入。对于此文件,示例参数如下:
-w=5 -h=7 -sl=0.04 -ml=0.02 -d=10 -v=/path_to_opencv/opencv/doc/tutorials/objdetect/charuco_detection/images/choriginal.jpg -c=/path_to_opencv/opencv/samples/cpp/tutorial_code/objectDetection/tutorial_camera_charuco.yml