本文的目的是实现生成一张marker broad图片,告诉标记检测程序tag在真实世界中的实际大小。
检测成功后得到marker的id,四个角点坐标,marker到相机的平移和旋转。
1.下载安装参考
opencv 中的aruco源码下载要到下面地址
opencv 中的aruco源码下载
https://github.com/opencv/opencv_contrib/tree/master/modules/aruco
https://github.com/opencv/opencv_contrib/releases/tag/3.3.0
2.生成单个marker图片
程序如下
#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include "opencv2/core/core.hpp"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
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);
imwrite("./aruco_tag.png",markerImage);
imshow("test", markerImage);
waitKey();
return 0;
}
cv::aruco::drawMarker
第一个参数是之前创建的Dictionary对象。
第二个参数是marker的id,在这个例子中选择的是字典DICT_6X6_250 的第23个marker。注意到每个字典是由不同数目的Marker组成的,在这个例子中的字典中,有效的Id数字范围是0到249。不在有效区间的特定id将会产生异常。
三个参数,200,是输出Marker图像的大小。在这个例子中,输出的图像将是200x200像素大小。注意到这一参数需要满足能够存储特定字典 的所有位。举例来说,你不能为6x6大小的marker生成一个5x5图像(这还没有考虑到Marker的边界)。除此之外,为了避免变形,这一参数最好和位数+边界的大小成正比,或者至少要比marker的大小大得多(如这个例子中的200),这样变形就不显著了
第四个参数是输出的图像。
最终,最后一个参数是一个可选的参数,它指定了Marer黑色边界的大小。这一大小与位数数目成正比。例如,值为2意味着边界的宽度将会是2的倍数。默认的值为1。
3.打印并标定相机内参
注意,打印的时候如果用像素为200200的图像打印,实际打印大小为20cm20cm,那么一个像素对应1毫米。
内参标定就不介绍了,此实验使用内参为
intrinsic_matrix: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ 420.019, 0., 330.8676, 0.,
419.6044, 217.8731, 0., 0., 1. ]
distortion_vector: !!opencv-matrix
rows: 1
cols: 4
dt: d
data: [ -0.3549, 0.1151, -0.0035, -0.0029 ]
的相机拍出来的图像如下
4.检测marker并得到id和相对位移
确定好实际打印出来的marker的边长和内参就可以检测并计算了。
其中markerlength表示marker的实际物理长度。
使用上面的图像和内参程序如下
#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include "opencv2/core/core.hpp"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <eigen3/Eigen/Core>
#include <eigen3/Eigen/Geometry>
#include <opencv2/core/eigen.hpp>
using namespace std;
using namespace cv;
int main()
{
cv::Mat m_image=imread("./mark.jpg");
if(m_image.empty())
{
cout<<"m_image is empty"<<endl;
return 0;
}
double markerlength=0.105;
cv::Mat intrinsics = (Mat_<double>(3, 3) <<
420.019, 0.0, 330.8676,
0.0,419.6044, 217.8731,
0.0, 0.0, 1.0);
cv::Mat distCoeffs=(cv::Mat_<double>(4, 1) << -0.3549, 0.1151, -0.0035, -0.0029);
cv::Mat imageCopy;
m_image.copyTo(imageCopy);
cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
std::vector<int> ids;
std::vector<std::vector<cv::Point2f>> corners;
cv::aruco::detectMarkers(m_image, dictionary, corners, ids);
if (ids.size() > 0) {
cv::aruco::drawDetectedMarkers(imageCopy, corners, ids);
for(unsigned int i=0; i<ids.size(); i++)
{
std::vector<cv::Vec3d> rvecs, tvecs;
cv::aruco::estimatePoseSingleMarkers(corners[i], markerlength, intrinsics, distCoeffs, rvecs, tvecs);
cv::aruco::drawAxis(imageCopy,intrinsics,distCoeffs, rvecs[i], tvecs[i], 0.1);
cv::Mat rmat;
Rodrigues(rvecs[i], rmat);
Eigen::Matrix3d rotation_matrix3d;
cv2eigen(rmat,rotation_matrix3d);
Eigen::Vector3d eulerAngle = rotation_matrix3d.eulerAngles(0,1,2);
cout<<"pitch "<<eulerAngle.x()<<"yaw "<<eulerAngle.y()<<"roll"<<eulerAngle.z()<<endl;
cout<<"x= "<<tvecs[i][0]<<"y="<<tvecs[i][1]<<"z="<<tvecs[i][2]<<endl;
}
}
cv::imshow("out", imageCopy);
cv::waitKey();
return 0;
}
其中
The parameters of detectMarkers are:
The first parameter is the image where the markers are going to be detected.
The second parameter is the dictionary object, in this case one of the predefined dictionaries (DICT_6X6_250).
The detected markers are stored in the markerCorners and markerIds structures:
markerCorners is the list of corners of the detected markers. For each marker, its four corners are returned in their original order (which is clockwise starting with top left). So, the first corner is the top left corner, followed by the top right, bottom right and bottom left.
markerIds is the list of ids of each of the detected markers in markerCorners. Note that the returned markerCorners and markerIds vectors have the same sizes.
The fourth parameter is the object of type DetectionParameters. This object includes all the parameters that can be customized during the detection process. This parameters are commented in detail in the next section.
The final parameter, rejectedCandidates, is a returned list of marker candidates, i.e. those squares that have been found but they do not present a valid codification. Each candidate is also defined by its four corners, and its format is the same than the markerCorners parameter. This parameter can be omitted and is only useful for debugging purposes and for ‘refind’ strategies (see refineDetectedMarkers() ).
5实验效果
输出
pitch 3.12894yaw -0.0187251roll-1.5281
x= -0.011554y=-0.0038433z=0.17224
6.生成多个marker组成的board
参考http://www.pianshen.com/article/2639341324/
#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include "opencv2/core/core.hpp"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
int markersX = 5;
int markersY = 5;
int markerLength = 100;
int markerSeparation = 20;
int dictionaryId = cv::aruco::DICT_4X4_50;
int margins = markerSeparation;
int borderBits = 1;
bool showImage = true;
Size imageSize;
imageSize.width = markersX * (markerLength + markerSeparation) - markerSeparation + 2 * margins;
imageSize.height =
markersY * (markerLength + markerSeparation) - markerSeparation + 2 * margins;
Ptr<aruco::Dictionary> dictionary =
aruco::getPredefinedDictionary(aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId));
Ptr<aruco::GridBoard> board = aruco::GridBoard::create(markersX, markersY, float(markerLength),
float(markerSeparation), dictionary);
Mat boardImage;
board->draw(imageSize, boardImage, margins, borderBits);
if (showImage) {
imwrite("./aruco_tag_board.png",boardImage);
imshow("board", boardImage);
waitKey(0);
}
return 0;
}
参考文献
https://blog.csdn.net/A_L_A_N/article/details/83657878
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)