前提介绍:
ArUco模块是OpenCV的contrib拓展库中一个模块,需要安装OpenCV的contrib拓展库才能正常使用。
ArUco标记:
ArUco 标记是由宽黑色边框和确定其标识符(id)的内部二进制矩阵组成的正方形标记,如图1所示。通俗地说,ArUco标记其实就是一种编码,就和我们日常生活中的二维码是相似的,只不过由于编码方式的不同,导致它们存储信息的方式、容量等等有所差异,所以在应用层次上也会有所不同——单个ArUco标记就可以提供足够的对应关系,例如有四个明显的角点及内部的二进制编码,所以ArUco标记被广泛用来增加从二维世界映射到三维世界时的信息量,便于发现二维世界与三维世界之间的投影关系,从而实现姿态估计、相机矫正等应用。
ArUco标记的黑色边框有助于其在图像中的快速检测,内部二进制编码用于识别标记和提供错误检测和纠正。ArUco标记尺寸的大小决定内部矩阵的大小,例如尺寸为4x4的标记由16位二进制数组成。
图1 id=0,字典为DICT_4X4_250的ArUco标记
总结:
ArUco标记是一种基于二进制方形基准编码的标记,可用于摄像机姿态估计、相机矫正等。它的主要优点是检测简单、快速,并且具有很强的鲁棒性。
ArUco使用:
ArUco标记的创建:
创建ArUco标记时,首先需要指定一个字典,这个字典表示的是创建出来的ArUco标记具有怎样的尺寸、怎样的编码等内容。
使用getPredefinedDictionary()函数来声明使用的字典。在OpenCV中,提供了多种预定义字典,可以通过PREDEFINED_DICTIONARY_NAME来查看有哪些预定义字典。而且字典名称表示了该字典的ArUco标记数量和尺寸,例如DICT_7X7_50表示一个包含了50种7x7位标记的字典。
指定好需要的字典后,就可以通过drawMarker()函数来绘制出ArUco标记。
函数解析:
dictionary = getPredefinedDictionary(Dictionary)
Dictionary | 预定义字典 |
dictionary | Dictionary对象 |
drawMarker(dictionary,id,sidepixel,img,borderBist)
dictionary |
Dictionary对象 |
id | marker的id,表示绘制字典中的哪一个ArUco标记。每个字典由不同数量的标记组成,id的有效范围是[0,字典包含的标记数),超出有效范围的特定id会产生异常 |
sidepixel | 输出标记图像的尺寸,输出标记图像的尺寸为sidepixel×sidepixel(此参数应足够大以存储特定字典的位数,至少需要满足(sidepixel - 标记的边长)>= 2。并且为了避免输出标记图像变形,sidepixel应与(位数 + 边界)大小成比例,或者至少比标记尺寸大得多,以使变形不明显) |
img | 输出的标记图像 |
borderBist | (可选)用于指定标记黑色边框的宽度,例如borderBist=2表示边框的宽度等于两个内部像素的大小,默认值 borderBist=1。 |
示例代码:
cv::Mat marker;
cv::aruco::Dictionary dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
cv::aruco::drawMarker(dictionary, 0, 200, marker, 1);
ArUco标记的检测:
检测ArUco标记的是detectMarkers()函数。方便观察检测结果,通常需要进行可视化操作,也就是将检测到的ArUco标记绘制出来,此过程使用的是drawDetectedMarkers()函数。
函数解析:
detectMarkers(img,dictionary,corners,ids,parameters,rejectedImgPoints)
img | 待检测标记的图像 |
dictionary | 同创建ArUco标记时所使用的字典。检测什么类型的ArUco标记就使用什么类型的字典 |
corners | 检测到的ArUco标记的角点列表,对于每个标记,其四个角点均按其原始顺序返回(从右上角开始顺时针顺序) |
ids | 检测到的每个ArUco标记的 id |
parameters | DetectionParameters 类的对象,该对象包括在检测过程中可以自定义的所有参数 |
rejectedImgPoints | 抛弃的候选标记列表,即检测到的、但未提供有效编码的正方形。每个候选标记也由其四个角定义,其格式与第三个参数相同 |
drawDetectedMarkers(img,corners,ids,borderColor)
img | 待绘测标记的图像 |
corners | 检测到的ArUco标记的角点列表 |
ids | 检测到的每个标记对应到其所属字典中的id |
borderColor | 绘制标记外框的颜色 |
示例代码:
auto dictionary = aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME::DICT_4X4_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标记用作姿态估计:
通过ArUco标记的检测来获取相机pose,需要知道相机的校准(Calibration)参数,即相机矩阵和畸变系数。(当用ArUco标记来检测相机pose时,可以单独地检测每个标记的pose。如果想要从一堆标记里检测出一个pose,需要的是ArUco板)。
涉及到ArUco标记的相机pose是一个从标记坐标系到相机坐标系的三维变换。这是由一个旋转和一个平移向量确定的。
每当我们每检测到一个aruco标记时,vector<vector<Point2f>>corners中就会获得一个检测到标记的角点列表,通过这个角点列表可以对该标记进行姿态估计。
对ArUco标记的姿态估计可以使用estimatePoseSingleMarkers()函数,每个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()函数来绘制坐标轴。
函数解析:
estimatePoseSingleMarkers(corners,markerLength,cameraMatrix,distCoeffs,rvecs,tvecs,_objPoints)
corners | detectMarkers()返回的检测到标记的角点列表 |
markerLength | ArUco标记的实际物理尺寸,也就是打印出来的ArUco标记的实际尺寸,一般以米为单位 |
cameraMatrix | 相机的内参矩阵 |
distCoeffs | 相机的畸变参数 |
rvecs | vector<cv::Vec3d>类型的向量,其中每个元素为每个标记相对于相机的旋转向量 |
tvecs | vector<cv::Vec3d>类型的向量,其中每个元素为每个标记相对于相机的平移向量 |
_objPoints | 每个标记角点的对应点数组 |
drawAxis(img,cameraMatrix,distCoeffs,rvec,tvec,length)
img | 待绘制坐标系的图像 |
cameraMatrix | 相机的内参矩阵 |
distCoeffs | 相机的畸变参数 |
rvec | 旋转向量 |
tvec | 平移向量 |
length | 坐标轴的长度,单位通常为米 |
示例代码:
cv::Mat cameraMatrix, distCoeffs;
vector<double> camera = { 657.1548323619423, 0, 291.8582472145741,0, 647.384819351103, 391.254810476919,0, 0, 1 };
cameraMatrix = Mat(camera);
cameraMatrix = cameraMatrix.reshape(1,3);
vector<double> dist = { 0.1961793476399528, -1.38146317350581, -0.002301820186177369, -0.001054637905895881, 2.458286937422959 };
distCoeffs = Mat(dist);
distCoeffs = distCoeffs.reshape(1, 1);
VideoCapture capture;
capture.open(0);
if (!capture.isOpened())
{
cout << "can't open camera" << endl;
exit(-1);
}
Mat frame;
while (capture.read(frame))
{
Mat test_image;
resize(frame, test_image, Size(800, 800));
imshow("test_image", test_image);
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));
std::vector<cv::Vec3d> rvecs;
std::vector<cv::Vec3d> tvecs;
cv::aruco::estimatePoseSingleMarkers(corners, 0.053, cameraMatrix, distCoeffs, rvecs, tvecs);
for (int i = 0;i < rvecs.size();i++)
{
//绘制坐标轴,检查姿态估计结果
cv::aruco::drawAxis(test_image, cameraMatrix, distCoeffs, rvecs[i], tvecs[i], 0.02);
}
imshow("pose", test_image);
char ch = cv::waitKey(1);
if (27 == ch)
{
break;
}
}
参考博客:
https://blog.csdn.net/weixin_45224869/article/details/106305543文章来源:https://www.toymoban.com/news/detail-582666.html
https://blog.csdn.net/Mufafafa/article/details/124464644文章来源地址https://www.toymoban.com/news/detail-582666.html
到了这里,关于OpenCV学习——ArUco模块的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!