背景:项目需要用到marker知识,所以到官网上临时补一些知识。
概要:主要介绍marker一些接口的含义,纯属个人理解,有误则希望大佬不吝赐教
1、
涉及ar码操作学习,其头文件为:
#include
1)创建marker
cv::Mat markerImage;
cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
cv::aruco::drawMarker(dictionary, 23, 200, markerImage, 1);
cv::imwrite("marker23.png", markerImage);
参数1: dictionary :表示marker字典类型为DICT_6X6_250,使用构造函数进行创建
参数2: 23 表示准备绘制的ar码在该字典里面的编号id为23
参数3: 200 表示输出ar码图片像素为200x200
参数4: markerImage 保存的是输出ar图片
dictionary表示的是marker数据集
可以参考:
const char* keys =
"{@outfile | | 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}"
"{cd | | Input file with custom dictionary }"
"{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 }";
}
2)marker检测函数detectMarkers()解析:
cv::Mat inputImage;
...
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f>> markerCorners, rejectedCandidates;
cv::Ptr<cv::aruco::DetectorParameters> parameters = cv::aruco::DetectorParameters::create();
cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
cv::aruco::detectMarkers(inputImage, dictionary, markerCorners, markerIds, parameters, rejectedCandidates);
参数1 inputImage : 表示包含 ar码marker的图片=
参数2: dictionary : 表示ar码来自哪个字典类型
参数3: markerCorners : 该容器作用是保存的是检测出来的ar码
参数4: markerIds : 作用记录检测出来的ar码存储在markerCorners在字典中的编号
参数5: parameters : DetectorParameters类的实例化对象,其包含了检测ar码过程所涉及的所有自定义的参数
参数6: rejectedCandidates : 保存没有检测出来有效的ar码 marker
参考:https://docs.opencv.org/4.x/d5/dae/tutorial_aruco_detection.html
3)绘制marker框框 drawDetectedMarkers()
在上一步骤检出ar码后,下一步检测是否真的检查正确,这时候需要使用绘制marker框框来验证,此时可使用函数drawDetectedMarkers():
cv::Mat outputImage = inputImage.clone();
cv::aruco::drawDetectedMarkers(outputImage, markerCorners, markerIds);
参数1 : outputImage 表示输入的图片
参数2 : markerCorners 来自detectMarkers()函数获取得到保存了ar码容器
参数3 : markerIds 来自detectMarkers()函数获取的,记录检测出来的ar码存储在markerCorners的ar码在字典中的编号
参考:https://docs.opencv.org/4.x/d5/dae/tutorial_aruco_detection.html
4)姿态信息函数estimatePoseSingleMarkers()
在检测校验检测出来的ar码之后,我们需要做的是从ar码里面获取对应的姿态信息,姿态信息函数estimatePoseSingleMarkers():
cv::Mat cameraMatrix, distCoeffs;
// You can read camera parameters from tutorial_camera_params.yml
readCameraParameters(filename, cameraMatrix, distCoeffs); // This function is located in detect_markers.cpp
std::vector<cv::Vec3d> rvecs, tvecs;
cv::aruco::estimatePoseSingleMarkers(markerCorners, 0.05, cameraMatrix, distCoeffs, rvecs, tvecs);
参数1: markerCorners 来自detectMarkers()函数,意义为存储检测出来的ar码
参数2: 0.05 ar码的尺寸,单位一般为米(也可以为其他单位,如mm),注意尺寸单位和姿态估计变换矩阵单位一致
参数3: cameraMatrix 相机内参
参数4: distCoeffs 相机畸变参数
参数5: rvecs 该容器保存的是markerCorners里面每个ar的旋转信息
参数6: tvecs 该容器保存的是markerCorners里面每个ar的平移信息
相机内参和畸变参数可以查看:https://blog.csdn.net/weixin_43206570/article/details/84797361
5)打印姿态信息cv::drawFrameAxes()
姿态信息获取后,可以打印出检验出来的姿态信息进行校验,参考函数cv::drawFrameAxes()
inputImage.copyTo(outputImage);
for (int i = 0; i < rvecs.size(); ++i) {
auto rvec = rvecs[i];
auto tvec = tvecs[i];
cv::drawFrameAxes(outputImage, cameraMatrix, distCoeffs, rvec, tvec, 0.1);
}
参数1: outputImage 绘制ar码的图片
参数2: cameraMatrix 相机内参
参数3: distCoeffs 相机畸变参数
参数4: rvec 该容器保存的是markerCorners里面每个ar的旋转信息
参数5: tvecs 该容器保存的是markerCorners里面每个ar的平移信息
参数6: 0.1 轴线长度,单位同rvec、tvecs,一般为米
以下为一个简单ar码,从检测到姿态估计的完整过程:
cv::VideoCapture inputVideo;
inputVideo.open(0);
cv::Mat cameraMatrix, distCoeffs;
// You can read camera parameters from tutorial_camera_params.yml读取相机参数
readCameraParameters(filename, cameraMatrix, distCoeffs); // This function is located in detect_markers.cpp
cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
while (inputVideo.grab()) {
cv::Mat image, imageCopy;
inputVideo.retrieve(image);
image.copyTo(imageCopy);
std::vector<int> ids;
std::vector<std::vector<cv::Point2f>> corners;
//检测
cv::aruco::detectMarkers(image, dictionary, corners, ids);
// if at least one marker detected
if (ids.size() > 0) {
cv::aruco::drawDetectedMarkers(imageCopy, corners, ids);//绘制检测框
std::vector<cv::Vec3d> rvecs, tvecs;
cv::aruco::estimatePoseSingleMarkers(corners, 0.05, cameraMatrix, distCoeffs, rvecs, tvecs);//姿态估计
// draw axis for each marker
for(int i=0; i<ids.size(); i++)
cv::drawFrameAxes(imageCopy, cameraMatrix, distCoeffs, rvecs[i], tvecs[i], 0.1);//绘制姿态
}
cv::imshow("out", imageCopy);//显示图片
char key = (char) cv::waitKey(waitTime);
if (key == 27)
break;
}
其他内容为解析一些自定义检测过程参数事情,后续想要学习,可以打开链接加以学习
参考:https://docs.opencv.org/4.x/d5/dae/tutorial_aruco_detection.html
2、利用ar 码集(注意跟上一节的单个ar码检测到姿态估计区别)进行姿态估计
单独检测一个ar码和 board 码检测的区别,board码可以知道各个ar码之间的相对位置而单独ar码不知,也就是有一个先验值。
boards好处:
姿态估计更加丰富;获取的姿态更加准确
1)头文件
#include
2)Board类
class Board {
public:
std::vector<std::vector<cv::Point3f> > objPoints;
cv::Ptr<cv::aruco::Dictionary> dictionary;
std::vector<int> ids;
};
参数1: objPoints 角点位置列表,就是存储所有ar码四个角点位置信息
参数2: dictionary 表示board上面使用的ar码归属字典
参数3: ids 存储的是objPoints 里面的ar码对应在字典里面的编号
参考:https://docs.opencv.org/4.x/d5/dae/tutorial_aruco_detection.html
3)Board detection
板检测和marker检测类似,唯一差别在于姿态估计上。板在board姿态估计之前,务必进行一次marker检测
cv::Mat inputImage;
// camera parameters are read from somewhere
cv::Mat cameraMatrix, distCoeffs; //相机的内参、相机畸变参数
// You can read camera parameters from tutorial_camera_params.yml
readCameraParameters(filename, cameraMatrix, distCoeffs); // This function is located in detect_board.cpp
// assume we have a function to create the board object
cv::Ptr<cv::aruco::Board> board = cv::aruco::Board::create();
...
std::vector<int> markerIds;//ar码在字典中的编号,对应markerCorners中ar码
std::vector<std::vector<cv::Point2f>> markerCorners;// 所有ar码
cv::aruco::detectMarkers(inputImage, board.dictionary, markerCorners, markerIds);
// if at least one marker detected
if(markerIds.size() > 0) {
cv::Vec3d rvec, tvec;
int valid = cv::aruco::estimatePoseBoard(markerCorners, markerIds, board, cameraMatrix, distCoeffs, rvec, tvec);
}
参数1: markerCorners 存储的是板上的ar码
参数2: markerIds 存储的是存储ar码在字典中的编号
参数3: board 记录板的布局和ar码的id(?)
参数4: cameraMatrix 相机内参
参数5: distCoeffs 相机畸变参数
参数6: rvec 板的旋转姿态估计
参数7: tvec 板的平移姿态估计
注意一个点,markerCorners
和markerIds
存储的并非所有检测到的ar信息,而是在存储在Board::ids里面给到的ar编号所属信息。
参考:https://docs.opencv.org/4.x/db/da9/tutorial_aruco_board_detection.html
3、延伸
marker可以提供一些姿态信息,比如我想直到图像里面的某个平面的姿态信息,那么,我们可以给平面贴上一些marker,构造board,调用opencv接口可以返回变换矩阵信息,那么我们根据预设的board信息就可以直到某个平面的姿态信息。
#####################
不积硅步,无以至千里
好记性不如烂笔头
觉得nice,记得点赞收藏