参考 opencv aruco 实现对二维码(QR码)的检测与定位

2023-05-16

参考 opencv aruco 实现对单个QR码的检测与定位

aruco是opencv_contrib的一个模块,实现了对AR码的检测/姿态估计
使用aruco需要安装opencv_contrib,本文将aruco中姿态估计用到的函数提取出来,结合zbar对QR码识别定位.

效果如下图
在这里插入图片描述

安装zbar

# ubuntu  apt install
sudo apt install libzbar-dev

将aruco中姿态估计用到的函数提取出来

estimate_marker_pose.h

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>  
using namespace std;           
using namespace cv;     
//绘制坐标轴
void myDrawFrameAxes(InputOutputArray image, InputArray cameraMatrix, InputArray distCoeffs,
                   InputArray rvec, InputArray tvec, float length, int thickness);
//绘制边框以及id
void myDrawDetectedMarkers(InputOutputArray _image, InputArrayOfArrays _corners,
                         InputArray _ids, Scalar borderColor);
//生成实际四个定点的坐标
void myGetSingleMarkerObjectPoints(float markerLength, OutputArray _objPoints); 
//解PnP获得旋转向量和平移向量
void myEstimatePoseSingleMarkers(InputArrayOfArrays _corners, float markerLength,
                               InputArray _cameraMatrix, InputArray _distCoeffs,
                               OutputArray _rvecs, OutputArray _tvecs); 

estimate_marker_pose.cpp

#include <estimate_marker_pose.h>
using namespace std;        
using namespace cv;     

void myDrawFrameAxes(InputOutputArray image, InputArray cameraMatrix, InputArray distCoeffs,
                   InputArray rvec, InputArray tvec, float length, int thickness)
{

    CV_Assert(image.getMat().total() > 0);
    CV_Assert(length > 0);

    // project axes points
    vector<Point3f> axesPoints;
    axesPoints.push_back(Point3f(0, 0, 0));
    axesPoints.push_back(Point3f(length, 0, 0));
    axesPoints.push_back(Point3f(0, length, 0));
    axesPoints.push_back(Point3f(0, 0, length));
    vector<Point2f> imagePoints;
    projectPoints(axesPoints, rvec, tvec, cameraMatrix, distCoeffs, imagePoints);

    // draw axes lines
    line(image, imagePoints[0], imagePoints[1], Scalar(0, 0, 255), thickness);
    line(image, imagePoints[0], imagePoints[2], Scalar(0, 255, 0), thickness);
    line(image, imagePoints[0], imagePoints[3], Scalar(255, 0, 0), thickness);
}

void myDrawDetectedMarkers(InputOutputArray _image, InputArrayOfArrays _corners,
                         InputArray _ids, Scalar borderColor) 
    {
    CV_Assert(_image.getMat().total() != 0 &&
              (_image.getMat().channels() == 1 || _image.getMat().channels() == 3));
    CV_Assert((_corners.total() == _ids.total()) || _ids.total() == 0);

    // calculate colors
    Scalar textColor, cornerColor;
    textColor = cornerColor = borderColor;
    swap(textColor.val[0], textColor.val[1]);     // text color just sawp G and R
    swap(cornerColor.val[1], cornerColor.val[2]); // corner color just sawp G and B

    int nMarkers = (int)_corners.total();
    for(int i = 0; i < nMarkers; i++) {
        Mat currentMarker = _corners.getMat(i);
        CV_Assert(currentMarker.total() == 4 && currentMarker.type() == CV_32FC2);

        // draw marker sides
        for(int j = 0; j < 4; j++) {
            Point2f p0, p1;
            p0 = currentMarker.ptr< Point2f >(0)[j];
            p1 = currentMarker.ptr< Point2f >(0)[(j + 1) % 4];
            line(_image, p0, p1, borderColor, 1);
        }
        // draw first corner mark
        rectangle(_image, currentMarker.ptr< Point2f >(0)[0] - Point2f(3, 3),
                  currentMarker.ptr< Point2f >(0)[0] + Point2f(3, 3), cornerColor, 1, LINE_AA);

        // draw ID
        if(_ids.total() != 0) {
            Point2f cent(0, 0);
            for(int p = 0; p < 4; p++)
                cent += currentMarker.ptr< Point2f >(0)[p];
            cent = cent / 4.;
            stringstream s;
            s << "id=" << _ids.getMat().ptr< int >(0)[i];
            putText(_image, s.str(), cent, FONT_HERSHEY_SIMPLEX, 0.5, textColor, 2);
        }
    }
}

void myGetSingleMarkerObjectPoints(float markerLength, OutputArray _objPoints) {


    _objPoints.create(4, 1, CV_32FC3);
    Mat objPoints = _objPoints.getMat();
    // set coordinate system in the middle of the marker, with Z pointing out
    objPoints.ptr< Vec3f >(0)[0] = Vec3f(-markerLength / 2.f, markerLength / 2.f, 0);
    objPoints.ptr< Vec3f >(0)[1] = Vec3f(markerLength / 2.f, markerLength / 2.f, 0);
    objPoints.ptr< Vec3f >(0)[2] = Vec3f(markerLength / 2.f, -markerLength / 2.f, 0);
    objPoints.ptr< Vec3f >(0)[3] = Vec3f(-markerLength / 2.f, -markerLength / 2.f, 0);
}


void myEstimatePoseSingleMarkers(InputArrayOfArrays _corners, float markerLength,
                               InputArray _cameraMatrix, InputArray _distCoeffs,
                               OutputArray _rvecs, OutputArray _tvecs) {


    Mat markerObjPoints;
    myGetSingleMarkerObjectPoints(markerLength, markerObjPoints);
    int nMarkers = (int)_corners.total();
    _rvecs.create(nMarkers, 1, CV_64FC3);
    _tvecs.create(nMarkers, 1, CV_64FC3);

    Mat rvecs = _rvecs.getMat(), tvecs = _tvecs.getMat();

     for each marker, calculate its pose
    parallel_for_(Range(0, nMarkers), [&](const Range& range) {
        const int begin = range.start;
        const int end = range.end;

        for (int i = begin; i < end; i++) {
            solvePnP(markerObjPoints, _corners.getMat(i), _cameraMatrix, _distCoeffs, rvecs.at<Vec3d>(i),
                     tvecs.at<Vec3d>(i));
        }
    });

}

main.cpp

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>     
#include <iostream>        
#include <zbar.h>
#include <estimate_marker_pose.h>
#include <opencv2/imgproc/types_c.h>

using namespace std;        
using namespace zbar;  //添加zbar名称空间      
using namespace cv;     

    
int main(int argc,char*argv[])      
{        
    cout<< "CV_VERSION: " << CV_VERSION << endl;
    //相机标定的参数-------
    double fx = 412.433229;
	double cx = 318.310004;
	double fy = 414.182775;
	double cy = 236.769192;
	double k1 = -0.320394;
	double k2 =  0.108028;
	double p1 = -0.000993;
	double p2 =  0.001297;
	double k3 =  0;
    Mat cameraMatrix = (cv::Mat_<float>(3, 3) <<
        fx, 0.0, cx,
        0.0, fy, cy,
        0.0, 0.0, 1.0);
    Mat distCoeffs = (cv::Mat_<float>(5, 1) << k1, k2, p1, p2, k3);
    //相机标定的参数-------
    //二维码的边长
	double marker_size = 5.0; //单位  cm
    //zbar::ImageScanner
    ImageScanner scanner;        
    scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);      
    
    cv::VideoCapture inputVideo;
    inputVideo.open(0);
	inputVideo.set(cv::CAP_PROP_FRAME_WIDTH,1280);
	inputVideo.set(cv::CAP_PROP_FRAME_HEIGHT,720);

    while (inputVideo.grab()) 
    {
        cv::Mat image;
		inputVideo.retrieve(image);//抓取视频中的一张照片
		flip(image,image,1);//  水平方向镜像
        Mat imageGray;        
        cvtColor(image,imageGray,CV_RGB2GRAY);   
        int width = imageGray.cols;        
        int height = imageGray.rows;        
        uchar *raw = (uchar *)imageGray.data;           
        Image imageZbar(width, height, "Y800", raw, width * height);          
        scanner.scan(imageZbar); //扫描条码      
        Image::SymbolIterator symbol = imageZbar.symbol_begin();    
        if(imageZbar.symbol_begin()==imageZbar.symbol_end())    
        {    
            cout<<"can't detect QR code!"<<endl;    
        }    
        std::vector<int> ids;
        std::vector<std::vector<cv::Point2f> > corners;
        std::vector<cv::Vec3d> rvecs, tvecs;
        for(int i = 0;symbol != imageZbar.symbol_end();++symbol)      
        {        
            cout<<"type:"<<endl<<symbol->get_type_name()<<endl<<endl;      
            cout<<"data:"<<endl<<symbol->get_data()<<endl<<endl; 
            cout<<"data_length:"<<endl<<symbol->get_data_length()<<endl<<endl; 
            cout<<"location_size:"<<endl<<symbol->get_location_size()<<endl<<endl; 
             
            std::vector<cv::Point2f> corner;
            corner.push_back(cv::Point2f(symbol->get_location_x(0),symbol->get_location_y(0)));
            corner.push_back(cv::Point2f(symbol->get_location_x(3),symbol->get_location_y(3)));
            corner.push_back(cv::Point2f(symbol->get_location_x(2),symbol->get_location_y(2)));
            corner.push_back(cv::Point2f(symbol->get_location_x(1),symbol->get_location_y(1)));
            corners.push_back(corner);
            ids.push_back(i);
            i++; 
        }        

        //求解旋转向量rvecs和平移向量tvecs
        myEstimatePoseSingleMarkers(corners, marker_size, cameraMatrix, distCoeffs, rvecs, tvecs);
        for (int i = 0; i < ids.size(); i ++)
        {
            // cv::aruco::drawDetectedMarkers(image, corners, ids);//绘制检测到的靶标的框
            myDrawDetectedMarkers(image, corners, ids,Scalar(100, 0, 255));//绘制检测到的靶标的框
            // cv::aruco::drawAxis(image, cameraMatrix, distCoeffs, rvecs[i], tvecs[i], 5);
            myDrawFrameAxes(image, cameraMatrix, distCoeffs, rvecs[i], tvecs[i], 5, 3);
            // drawFrameAxes(image, cameraMatrix, distCoeffs, rvecs[i], tvecs[i], 5, 3);//opencv 3.3.1 没有 4.2.1有
            cout<<"  T :"<<tvecs[i]<<endl;
            cout<<"  R :"<<rvecs[i]<<endl;
        }
        imshow("Source Image",image);    
        waitKey(1);      
    }

    return 0;    
}     

CMakeLists.txt

# estimate_marker_pose
add_library( estimate_marker_pose src/estimate_marker_pose.cpp)
target_link_libraries(estimate_marker_pose ${OpenCV_LIBS} )
# main
add_executable(main src/main.cpp)
target_link_libraries(main zbar estimate_marker_pose ${OpenCV_LIBS} )

打印

type:
QR-Code

data:
1

data_length:
1

location_size:
4

  T :[2.16432, -0.594964, 15.9725]
  R :[2.47789, -0.487188, -0.156933]

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

参考 opencv aruco 实现对二维码(QR码)的检测与定位 的相关文章

  • 训练神经网络中最基本的三个概念:Epoch, Batch, Iteration

    转载地址 xff1a https zhuanlan zhihu com p 29409502 原作者 xff1a Michael Yuan 作者主页 xff1a https www zhihu com people mikeyuan 今天让
  • 使用makefile编译freeRTOS

    freeRTOS的文件结构 FreeRTOS LabsFreeRTOS Plus 包含freeRTOS 43 的组件和demo项目FreeRTOS 包含内核和demo项目 Source目录 xff1a 三个必须文件list c queue
  • 2013 一路走过

    2013 一路走过 想起当初找工作的时候 xff0c 一个人早上坐火车跑到其他大学的招聘会上去逛一圈 xff0c 了解招聘情况 然后下午又坐火车回学校 记得那天我投了十几份简历出去 xff0c 本打算投着试试 xff0c 没想到回来后有几家
  • 编译vs2008的samples程序总是跳过

    编译vs2008的samples程序总是跳过 xff0c 要配置属性还显示 未能完成操作 未指定的错误 的解决办法 作者 admin 分类 开发问题 发布时间 2013 03 12 09 22 974 浏览数 6 没有评论 文章转自王牌软件
  • MFC 用户界面线程:界面线程的退出 窗口关闭的流程

    原文链接 xff1a http wenku baidu com link url 61 6CFkWbLOeFgNoUsJniCX3ksw6 RztxMr9Z e6t7uu3e vV7UTKThUEkyRkq8IXwxIw5qYctN8gIx
  • MFC用户界面多线程实例2

    以下是 MFC 用户界面线程相关知识 由于用户界面线程含有自己的消息循环 xff0c 可以出来 Windows 消息 xff0c 并可创建和管理诸如窗口和控件等用户界面 元素 因此 xff0c 这 种线程较工程线程更为复杂 创建用户界面线程
  • 反汇编定位代码崩溃位置_1

    原帖 xff1a http blog csdn net gwzz1228 article details 9045853 利用map xff0c cod文件定位崩溃代码行 利用vs2010 新建一个空的控制台项目 xff0c 添加文件gtg
  • 反汇编定位代码崩溃位置_3

    原帖 xff1a http blog sina com cn s blog 141f234870102van8 html win7 43 vs2010通过map文件和cod文件找到崩溃的代码行 2015 01 11 11 31 04 转载
  • 反汇编定位代码崩溃位置_4

    原帖 xff1a http blog csdn net xiao article details 23177577 GDB如何从Coredump文件恢复动态库信息 标签 xff1a GDBcoredumpso调试动态库 2014 04 08
  • STM32Cube的串口设置(二)一个串口接收另一个串口发送

    串口系列 STM32Cube的串口设置 xff08 一 xff09 即学即用 通过串口设置第一部分大家应该基本会使用单个串口进行收发了 所以本次介绍通过串口进行转发 适合情景为一个串口设备波特率为38400 xff0c 但是接收模块仅支持1
  • C链表反转

    节点 struct Note int value Note pNext typedef struct Note PList 生成一个链表 Note GenerateList 输出一个链表 void PrintList Note pHead
  • PMP考试重点知识

    第一章 引论 前三章 是整个知识体系的支撑框架 xff0c 每次考试中都会考到 xff0c 但是一般在15道题左右 xff0c 前 三章 学不好后面的章 节很难理解透彻 1 项目的特点 xff1f 2 什么是项目管理 xff1f 3 项目和
  • pcb焊接技巧

    焊接的先后次序 要想更高效 可靠地焊好一块板子 xff0c 是要遵循一定的原则 xff08 如 先小后大 xff09 的 xff0c 不可乱来 xff0c 更不是看哪个元件顺眼就焊哪个 一般我拿到一块板子后的处理流程是 xff1a 打印 P
  • js中通过document获取标签节点

    使用id名表示标签 xff0c 不够严谨 在html语法中 xff0c id名随便起 xff0c 可以是js中的关键字 xff0c 但是在js中使用id代表标签 xff0c 就不能使用关键字 xff0c 所以我们需要一种更加严谨的方式获取标
  • 安装ubuntu-desktop

    目录 安装ubuntu desktop 解决root登录受限 安装远程访问软件 方法一 xff1a 安装vnc4server 方法二 xff1a Teamviwer安装 传送门 推荐 正文 回到顶部 安装ubuntu desktop 复制代
  • python读取C语言头文件

    在使用python编程过程中 xff0c 经常需要对C语言文件进行操作 xff0c 即 h文件进行读取操作 xff0c 这里举例说明 xff0c python读取C语言头文件 xff0c 提取其中 define 宏定义 废话少说 xff0c
  • makefile(详细讲解)

    目录 1 makeflie2 多个文件执行makefile 1 makeflie makefile带来的好处就是 自动化编译 xff0c 一旦写好 xff0c 只需要一个make命令 xff0c 整个工程完全自动编译 xff0c 极大的提高
  • WLAN、LAN、WAN的区别

    1 LAN局域网 xff08 Local Area Network xff09 xff1a 通俗讲就是路由器和用户之间接口 2 WAN广域网 xff08 Wide Area Network xff09 xff1a 通俗讲就是路由器和外部网络
  • PX4环境搭建记录(ROS+Gazebo+mavros+PX4+QGC)

    全过程记录PX4环境搭建 xff08 ROS 43 Gazebo 43 Mavros 43 PX4 43 QGC xff09 本人飞控专业在读 xff0c 近段时间在老师的建议下 xff0c 开始搭建PX4环境配置 因为并没有之前相关环境配
  • 无人机仿真—PX4编译,gazebo仿真及简单off board控制模式下无人机起飞

    无人机仿真 PX4编译 xff0c gazebo仿真及简单off board控制模式下无人机起飞 前言 在上篇记录中 xff0c 已经对整体的PX4仿真环境有了一定的了解 xff0c 现如今就要开始对无人机进行起飞等仿真环境工作 xff0c

随机推荐