天天看點

視覺标記定位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