天天看点

视觉标记定位aruco使用视觉标记定位aruco使用

转载自:https://lightsail.blog.csdn.net/article/details/102752780

视觉标记定位aruco使用

沧海飞帆 2019-10-26 11:05:51 2657 收藏 14

分类专栏: SLAM 文章标签: opencv aruco定位

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/ktigerhero3/article/details/102752780

版权

本文的目的是实现生成一张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);//显示marker
    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 ]
           

的相机拍出来的图像如下

视觉标记定位aruco使用视觉标记定位aruco使用

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;
    }
    //read para
   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 at least one marker detected
    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);//求解旋转矩阵rvecs和平移矩阵tvecs
            cv::aruco::drawAxis(imageCopy,intrinsics,distCoeffs, rvecs[i], tvecs[i], 0.1);
            //3.rotaion vector to eulerAngles
            cv::Mat rmat;
            Rodrigues(rvecs[i], rmat);
            Eigen::Matrix3d rotation_matrix3d;
            cv2eigen(rmat,rotation_matrix3d);
            Eigen::Vector3d eulerAngle = rotation_matrix3d.eulerAngles(0,1,2);//(0,1,2)表示分别绕XYZ轴顺序,即 顺序,逆时针为正
            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
           
视觉标记定位aruco使用视觉标记定位aruco使用

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;//X轴上标记的数量
    int markersY = 5;//Y轴上标记的数量   本例生成5x5的棋盘
    int markerLength = 100;//标记的长度,单位是像素
    int markerSeparation = 20;//每个标记之间的间隔,单位像素
    int dictionaryId = cv::aruco::DICT_4X4_50;//生成标记的字典ID
    int margins = markerSeparation;//标记与边界之间的间隔

    int borderBits = 1;//标记的边界所占的bit位数
    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);

    // show created board
    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