今天要整理记录的是OpenCV中ArUco模块的基础内容,包含aruco标记的创建与检测。要注意的是ArUco模块是包含在OpenCV的contrib拓展库中的,需要自行下载OpenCV基础库和contrib拓展库进行联合编译才可以使用,这里就不整理编译的过程了,不得不说编译的时候还是有好多坑的。。。如果有需要的朋友可以在我的资源里面下载 “OpenCV430(contrib).txt”,这是编译好了的包含contrib拓展库的OpenCV4.3.0版本,由于文件过大所以使用的是某度网盘链接的方式上传,介意下载速度的朋友就还是别下载了。把压缩包下载解压后再进行环境配置,就可以直接使用OpenCV中拓展库的内容了,顺带一提,编译的时候我选择的是AVX2指令集,相比起OpenCV默认的SSE3指令集有一定程度的优化,当然了这个需要个人的电脑CPU支持才可以,所以下载的朋友需要注意一下。
言归正传,下面先来了解一下什么是aruco标记:
aruco标记是可用于摄像机姿态估计的二进制方形基准标记。它的主要优点是检测简单、快速,并且具有很强的鲁棒性。ArUco 标记是由宽黑色边框和确定其标识符(id)的内部二进制矩阵组成的正方形标记。aruco标记的黑色边框有助于其在图像中的快速检测,内部二进制编码用于识别标记和提供错误检测和纠正。aruco标记尺寸的大小决定内部矩阵的大小,例如尺寸为 4x4 的标记由 16 位二进制数组成。
通俗地说,aruco标记其实就是一种编码,就和我们日常生活中的二维码是相似的,只不过由于编码方式的不同,导致它们存储信息的方式、容量等等有所差异,所以在应用层次上也会有所不同。由于单个aruco标记就可以提供足够的对应关系,例如有四个明显的角点及内部的二进制编码,所以aruco标记被广泛用来增加从二维世界映射到三维世界时的信息量,便于发现二维世界与三维世界之间的投影关系,从而实现姿态估计、相机矫正等等应用。
OpenCV中的ArUco模块包括了对aruco标记的创建和检测,以及将aruco标记用于姿势估计和相机矫正等应用的相关API,同时还提供了标记板等等。本次笔记中主要先整理aruco标记的创建与检测。
首先我们创建aruco标记时,需要先指定一个字典,这个字典表示的是创建出来的aruco标记具有怎样的尺寸、怎样的编码等等内容,我们使用APIgetPredefinedDictionary()
来声明我们使用的字典。在OpenCV中,提供了多种预定义字典,我们可以通过PREDEFINED_DICTIONARY_NAME
来查看有哪些预定义字典。而且字典名称表示了该字典的aruco标记数量和尺寸,例如DICT_7X7_50
表示一个包含了50种7x7位标记的字典。
确定好我们需要的字典后,就可以通过APIdrawMarker()
来绘制出aruco标记,其参数含义如下:
(1)参数dictionary: 之前创建的 Dictionary 对象指针
(2)参数id:标记 id,表示绘制字典中的哪一个aruco标记。每个字典由不同数量的标记组成,id 有效范围是 [ 0,字典包含的标记数 ),任何超出有效范围的特定 id 都会产生异常。
(3)参数 sidepixel: 输出标记图像的尺寸,输出标记图像的尺寸为Size(sidepixel,sidepixel);此参数应足够大以存储特定字典的位数,至少需要满足(sidepixel - 标记的边长)>= 2;
并且为了避免输出标记图像变形,sidepixel应与位数 + 边界大小成比例,或者至少比标记尺寸大得多,以使变形不明显。
(4)参数img:输出的标记图像;
(5)参数borderBist:用于指定标记黑色边框的宽度,例如borderBist=2 表示边框的宽度等于两个内部像素的大小,默认值 borderBist= 1。
这样,我们就创建了一个选定字典的aruco标记了,下面我们创建5个不同的aruco标记来对比下,演示代码如下:
//创建aruco标记
Mat marker;
auto dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME::DICT_6X6_250);
for (int i = 0;i < 5;i++)
{
aruco::drawMarker(dictionary, i, 200, marker, 1);
string windowName = "marker" + to_string(i);
imshow(windowName, marker);
string path ="D:\\"+ windowName + ".jpg";
imwrite(path, marker);
}
接下来,当我们把aruco标记打印出来后贴在任意其它地方后,再进行拍摄,就可以对aruco标记进行检测。检测aruco标记的API是detectMarkers()
,其参数含义如下:
(1)参数image:输入的需要检测标记的图像;
(2)参数dictionary:进行检测的字典对象指针,这里的字典就是我们创建aruco标记时所使用的字典,检测什么类型的aruco标记就使用什么类型的字典;
(3)参数corners:检测到的aruco标记的角点列表,对于每个标记,其四个角点均按其原始顺序返回(从右上角开始顺时针旋转),第一个角是右上角,然后是右下角,左下角和左上角。
注意这里的返回角点顺序,在OpenCV官方文档中记录的是从左上角开始进行旋转,而经过我的测试,发现是以右上角为起点的,这应该是官方文档中的笔误,而且官方文档中关于contrib拓展库部分的内容可能由于更新频率低,其中还存在着不少小问题,朋友们在看的时候要注意自己检验一下里面的内容,千万不用拿来即用,很可能掉坑。
(4)参数ids:检测到的每个标记的 id,需要注意的是第三个参数和第四个参数具有相同的大小;
(5)参数parameters: DetectionParameters
类的对象,该对象包括在检测过程中可以自定义的所有参数;
(6)参数rejectedImgPoints:抛弃的候选标记列表,即检测到的、但未提供有效编码的正方形。每个候选标记也由其四个角定义,其格式与第三个参数相同,该参数若无特殊要求可以省略。
当我们进行aruco标记检测后,为了方便我们观察,通常需要进行可视化操作,也就是把检测到的aruco标记绘制出来,我们使用drawDetectedMarkers()
这个API来绘制检测到的aruco标记,其参数含义如下:
(1)参数image: 是将绘制标记的输入 / 输出图像(通常就是检测到标记的图像)
(2)参数corners:检测到的aruco标记的角点列表
(3)参数ids:检测到的每个标记对应到其所属字典中的id
(4)参数borderColor:绘制标记外框的颜色
对aruco标记进行检测并绘制的演示代码如下:
auto dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME::DICT_6X6_250);
vector<vector<Point2f>>corners, rejectedImgPoints;
vector<int>ids;
auto parameters = aruco::DetectorParameters::create();
aruco::detectMarkers(test_image, dictionary, corners, ids, parameters, rejectedImgPoints);
aruco::drawDetectedMarkers(test_image, corners, ids, Scalar(0, 255, 0));
效果如下:
放大观察:
可以看到每个aruco标记的边框上,都有一个绿色矩形框,而在标记的中心,有一个id值,这个id就是该标记在其所属字典中的id。
那么到这里,我们就完成了对aruco标记的创建和检测工作,这是一个基础操作,后续的姿态估计、相机标定等等操作都是建立在此基础之上的。本次笔记就先到此为止,下次再来整理一下对aruco标记进行实时姿态估计的相关内容。
PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!