ArUco是一个开源的小型的AR虚拟现实增强库,已经集成在OpenCV3.0以上的版本,它除了用于现实增强,还很用于实现一些机器视觉方面的应用。
ArUco与AprilTag简介
ArUco
ArUco标记是一种二进制正方形的基准标志物,可用于相机的姿态估计,该标准板主要的优点是检测快速,简单且稳健
ArUco模块包括检测这些不同类型的标记板的方法,以及使用它们进行姿态估计和相机标定的工具。
此外,ChArUco功能将ArUco标记与传统棋盘相结合,以实现简单而多功能的角点检测。该模块还包括检测ChArUco角点并将其用于姿态估计和相机机标定的功能。
在opencv中aruco有以下几个模块:
(1)Detection of ArUco Markers:检测单个ArUco标记板:基于单个ArUco标记板的基本检测和姿态估计。
(2)Detection of ArUco Boards:基于ArUco标记板的检测与姿态估计
(3) Detection of ChArUco Corners检测ChArUco标记板角点:检测ChArUco标记板的角点
(4)Detection of Diamond Markers:基于ChArUco标记板的检测与姿态估计
(5)Calibration with ArUco and ChArUco:使用ArUco和ChArUco标定板进行相机标定
(6)ArUco module FAQ : 关于ArUco 模块的常见且有用问题的汇总
单个标记板的生成
在检测标记板之前,需要打印标记板放置在环境中。可以使用drawMarker()函数生成标记图像。
这里举个例子
代码语言:javascript复制 cv::Mat markerImage;
cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
cv::aruco::drawMarker(dictionary, 23, 200, markerImage, 1);
首先通过在aruco模块中选择一个预定义的字典来创建字典对象,具体的说,这个字典由250个标记和每个标记是6*6大小组成(DICT_6X6_250)
drawMarker的参数为:
第一个参数是先前创建的字典的对象
第二个参数是标记物的ID,在本例中DICT_6X6_250的字典标记为23,这里需要注意的是每个字典由不同数量大小的标记物组成,在本例中有效地id是从0到249,任何超过有效范围的特定ID都将产生异常。
第三个参数是200是输出标记物图像的大小,在这种情况下,输出的图像的大小将为200*200像素,注意这个参数应该足够大以能够存储特定字典的位数,因此对于6*6位的标记大小,是无法生成5*5像素的图像,因此为了避免形变,该参数应该是比特数 边界大小成比例,甚至事少远高于标记大小,比如实例中的200以便变形不显著.
最后一个参数是一个可选的参数,用于指定标记黑色边框的宽度,指定的大小与位数成比例,例如,当值为2意味着边框的宽度将相当于两个内部位的大小,默认值位1。
生成的图像实例
代码示例:
代码语言:javascript复制/*
生成单个的aruco标记物
*/
#include <opencv2/highgui.hpp>
#include <opencv2/aruco.hpp>
using namespace cv;
namespace {
const char* about = "Create an ArUco marker image";
const char* keys =
"{@outfile |<none> | Output image }"
"{d | | dictionary: DICT_4X4_50=0, DICT_4X4_100=1, DICT_4X4_250=2,"
"DICT_4X4_1000=3, DICT_5X5_50=4, DICT_5X5_100=5, DICT_5X5_250=6, DICT_5X5_1000=7, "
"DICT_6X6_50=8, DICT_6X6_100=9, DICT_6X6_250=10, DICT_6X6_1000=11, DICT_7X7_50=12,"
"DICT_7X7_100=13, DICT_7X7_250=14, DICT_7X7_1000=15, DICT_ARUCO_ORIGINAL = 16}"
"{id | | Marker id in the dictionary }"
"{ms | 200 | Marker size in pixels }"
"{bb | 1 | Number of bits in marker borders }"
"{si | false | show generated image }";
}
int main(int argc, char* argv[]) {
CommandLineParser parser(argc, argv, keys);
parser.about(about);
if (argc < 4) {
parser.printMessage();
return 0;
}
int dictionaryId = parser.get<int>("d");//选择字典
int markerId = parser.get<int>("id");//生成的第几个marker
int borderBits = parser.get<int>("bb");//marker的边界大小
int markerSize = parser.get<int>("ms");//marker的像素大小
bool showImage = parser.get<bool>("si");//是否显示生成的maarker
String out = parser.get<String>(0);//第一个参数是生成的maker文件名
if (!parser.check()) {
parser.printErrors();
return 0;
}
/*生成单个的marker只需要两句话,分别是选择字典,画marker*/
Ptr<aruco::Dictionary> dictionary =
aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId));
Mat markerImg;
aruco::drawMarker(dictionary, markerId, markerSize, markerImg, borderBits);
if (showImage) {
imshow("marker", markerImg);
waitKey(0);
}
imwrite(out, markerImg);
return 0;
}
标记检测
如果图像中有一些aruco标记可见,则检测过程必须返回检测到的标记列表。每个检测到的标记包括:
1,它的四个角在图像中的位置(按其原始顺序)。
2,标记的id。
标记检测过程由两个主要步骤组成:
1,候选标记的检测。在这一步中,对图像进行分析,以便找到作为候选标记的正方形。该算法首先对图像进行自适应阈值分割,然后从分割后的图像中提取轮廓线,剔除不凸或不近似正方形的轮廓线。还应用了一些额外的噪声滤波算法(去除过小或过大的轮廓,去除彼此太接近的轮廓等)。
2,在候选标记检测之后,需要通过分析它们的内部编码来确定它们是否真的是标记物。此步骤首先提取每个标记的标记位。为此,首先应用透视变换来获得标准形式的标记。然后,利用Otsu对标准图像进行阈值分割,分离出黑白图像。根据标记大小和边框大小将图像划分为不同的单元,并且计算每个单元上的黑色或白色像素的数量以确定它是白色还是黑色位。最后,对位进行分析以确定标记是否属于特定字典,并在必要时采用纠错技术。
可视化结果依次如下: