OpenCV中的ArUco模块包括了对aruco标记的创建和检测,以及将aruco标记用于姿势估计和相机矫正等应用的相关API,同时还提供了标记板等等。 现学习整理一下大致代码结构。

1.背景知识

ArUco 标记是由宽黑色边框和确定其标识符(id)的内部二进制矩阵组成的正方形标记。aruco标记的黑色边框有助于其在图像中的快速检测,内部二进制编码用于识别标记和提供错误检测和纠正。aruco标记尺寸的大小决定内部矩阵的大小,例如尺寸为 4x4 的标记由 16 位二进制数组成。

单个aruco标记就可以提供足够的对应关系,例如有四个明显的角点及内部的二进制编码,所以aruco标记被广泛用来增加从二维世界映射到三维世界时的信息量,便于发现二维世界与三维世界之间的投影关系,从而实现姿态估计、相机矫正等等应用。

2.创建Aruco码

首先我们要指定一个字典,这个字典表示的是创建出来的aruco标记具有怎样的尺寸、怎样的编码等
我们使用:APIgetPredefinedDictionary()来声明我们使用的字典。
在OpenCV中,提供了多种预定义字典,我们可以通过PREDEFINED_DICTIONARY_NAME来查看有哪些预定义字典。而且字典名称表示了该字典的aruco标记数量和尺寸,例如DICT_7X7_50表示一个包含了50种7x7位标记的字典。
主要就三句:
Mat markerImage;
auto dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
aruco::drawMarker(dictionary, 23, 200, markerImage, 1);

3.检测Aruco码

检测aruco标记的API是detectMarkers(),其参数含义如下:(按顺序)

(1)image:输入的需要检测标记的图像。
(2)dictionary:进行检测的字典对象指针,这里的字典就是我们创建aruco标记时所使用的字典,检测什么类型的aruco标记就使用什么类型的字典。
(3)corners:检测到的aruco标记的角点列表,对于每个标记,其四个角点均按其原始顺序返回(从右上角开始顺时针旋转),第一个角是右上角,然后是右下角,左下角和左上角。
注意这里的返回角点顺序,在OpenCV官方文档中记录的是从左上角开始进行旋转,而经过实际测试,发现是以右上角为起点的,需要注意。
(4)ids:检测到的每个标记的 id,需要注意的是第三个参数和第四个参数具有相同的大小。
(5)parameters: DetectionParameters 类的对象,该对象包括在检测过程中可以自定义的所有参数。
(6)参数rejectedImgPoints

当我们进行aruco标记检测后,为了方便我们观察,通常需要进行可视化操作,也就是把检测到的aruco标记绘制出来,我们使用drawDetectedMarkers()这个API来绘制检测到的aruco标记,其参数含义如下:(按顺序)
(1) 参数image: 绘制标记的输入 / 输出图像(通常就是检测到标记的图像)
(2)参数corners:检测到的aruco标记的角点列表
(3)参数ids:检测到的每个标记**对应到其所属字典中的id
(4)参数borderColor:绘制标记外框的颜色

注意:这个函数只是对图像做了处理,要显示图片还得调用imshow函数。
具体实现:

  1. auto dictionary = aruco::getPredefinedDictionary(
  2. aruco::PREDEFINED_DICTIONARY_NAME::DICT_6X6_250);
  3. vector<vector<Point2f>>corners, rejectedImgPoints;
  4. vector<int>ids;
  5. auto parameters = aruco::DetectorParameters::create();
  6. aruco::detectMarkers(test_image, dictionary, corners, ids, parameters, rejectedImgPoints);
  7. aruco::drawDetectedMarkers(test_image, corners, ids, Scalar(0, 255, 0));

4.姿态估计

所谓姿态估计问题就是要确定某个三维物体的方位指向问题,也就是确定以该物体为中心原点的一个坐标系。
我们需要调用estimatePoseSingleMarkers()这个API来实现对简单标记的姿态估计
参数含义如下:(按顺序)
(1)corners: detectMarkers()返回的检测到标记的角点列表,是一个vector<ve**ctor<Point2f>>类型的元素。
(2)markerLength:aruco标记的实际物理尺寸,也05就是我们打印出来的aruco标记的实际尺寸,一般以米为单位。
(3)cameraMatrix:用来拍摄aruco标记的相机的内参矩阵
(4)distCoeffs:用来拍摄aruco标记的相机的畸变参数
(5)rvecs:vector类型的向量,其中每个元素为每个标记相对于相机的旋转向量。
(6)tvecs:vector类型的向量,其中每个元素为每个标记相对于相机的平移向量。
(7)_objPoints:每个标记角点的
对应点数组**。

estimatePoseSingleMarkers()接收 detectMarkers()检测到的corners(以输出角点列表的方式表示),并分别对每个标记进行姿态估计。
因此,每个aruco标记都将返回一个相对于相机的旋转向量和平移矢量,返回的点数组将标记角点从每个标记坐标系转换到相机坐标系下的表示。
标记坐标系原点位于标记的中心,Z轴垂直于标记平面,每个标记的四个角点在其坐标系中的坐标为:(-markerLength / 2,markerLength / 2,0),
(markerLength / 2, markerLength / 2,0),
(markerLength / 2,-markerLength / 2 ,0),
(-markerLength / 2,-markerLength / 2,0),其中,markerLength是aruco标记的实际边长。

为了使姿态估计可视化,我们使用drawAxis()来绘制坐标轴,其参数含义为:
(1)image:绘制坐标轴的输入、输出图像(通常是进行检测标记的图像);
(2)cameraMatrix:相机的内参矩阵;
(3)distCoeffs:相机的畸变参数;
(4)/(5)参数rvec和参数tvec:当前要绘制坐标轴的物体的姿态参数,分别是旋转向量和平移向量; (6)参数length:绘制坐标轴的长度,单位通常为米。

结果图:image.png

补充:我们可以使用多个Aruco码组成的标记板,其精确度肯定会比单个aruco标记更高,而且即使其中有部分标记未被检测出来,同样可以识别出这是一个标记板。
image.png

5.相机标定