opencv二维码识别解码

目的:

使用opencv库识别QR二维码,框出图片中的二维码,并使用开源库Zxing解码,在这过程中学习理解opencv库相应的函数。

环境:

1. window7系统

      2. QT create

1.准备

首先安装QT和QT create开发环境,window下使用cmake编译opencv生产lib库。这部分内容网上很多资料,直接搜索安装就行。

2.工程代码

QT create配置使用opencv库。在工程的pro文件下面添加指定opencv库的头文件和lib的路径。如下:

  
  
  1. INCLUDEPATH+=C:\Qt\opencv\include\opencv\
  2. C:\Qt\opencv\include\opencv2\
  3. C:\Qt\opencv\include
  4. LIBS+=C:\Qt\opencv\ lib\libopencv_calib3d320.dll.a\
  5. C:\Qt\opencv\ lib\libopencv_core320.dll.a\
  6. C:\Qt\opencv\ lib\libopencv_features2d320.dll.a\
  7. C:\Qt\opencv\ lib\libopencv_flann320.dll.a\
  8. C:\Qt\opencv\ lib\libopencv_highgui320.dll.a\
  9. C:\Qt\opencv\ lib\libopencv_imgcodecs320.dll.a\
  10. C:\Qt\opencv\ lib\libopencv_imgproc320.dll.a\
  11. C:\Qt\opencv\ lib\libopencv_ml320.dll.a\
  12. C:\Qt\opencv\ lib\libopencv_objdetect320.dll.a\
  13. C:\Qt\opencv\ lib\libopencv_photo320.dll.a\
  14. C:\Qt\opencv\ lib\libopencv_shape320.dll.a\
  15. C:\Qt\opencv\ lib\libopencv_stitching320.dll.a\
  16. C:\Qt\opencv\ lib\libopencv_superres320.dll.a\
  17. C:\Qt\opencv\ lib\libopencv_video320.dll.a\
  18. C:\Qt\opencv\ lib\libopencv_videoio320.dll.a\
  19. C:\Qt\opencv\ lib\libopencv_videostab320.dll.a

opencv 识别定位二维码的代码如下:

  
  
  1. #include "opencv2/highgui/highgui.hpp"
  2. #include "opencv2/imgproc/imgproc.hpp"
  3. #include <iostream>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <math.h>
  7. #include <QDebug>
  8. using namespace cv;
  9. using namespace std;
  10. Mat src; Mat src_gray;
  11. RNG rng(12345);
  12. //Scalar colorful = CV_RGB(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255));
  13. //获取轮廓的中心点
  14. Point Center_cal(vector<vector<Point> > contours,int i)
  15. {
  16. int centerx= 0,centery= 0,n=contours[i].size();
  17. //在提取的小正方形的边界上每隔周长个像素提取一个点的坐标,
  18. //求所提取四个点的平均坐标(即为小正方形的大致中心)
  19. centerx = (contours[i][n/ 4].x + contours[i][n* 2/ 4].x + contours[i][ 3*n/ 4].x + contours[i][n -1].x)/ 4;
  20. centery = (contours[i][n/ 4].y + contours[i][n* 2/ 4].y + contours[i][ 3*n/ 4].y + contours[i][n -1].y)/ 4;
  21. Point point1=Point(centerx,centery);
  22. return point1;
  23. }
  24. int main( int argc, char** argv[] )
  25. {
  26. src = imread( "core.jpg", 1 );
  27. Mat src_all=src.clone();
  28. //彩色图转灰度图
  29. cvtColor( src, src_gray, CV_BGR2GRAY );
  30. //对图像进行平滑处理
  31. blur( src_gray, src_gray, Size( 3, 3) );
  32. //使灰度图象直方图均衡化
  33. equalizeHist( src_gray, src_gray );
  34. namedWindow( "src_gray");
  35. imshow( "src_gray",src_gray);
  36. Scalar color = Scalar( 1, 1, 255 );
  37. Mat threshold_output;
  38. vector< vector<Point> > contours,contours2;
  39. vector<Vec4i> hierarchy;
  40. Mat drawing = Mat::zeros( src.size(), CV_8UC3 );
  41. Mat drawing2 = Mat::zeros( src.size(), CV_8UC3 );
  42. Mat drawingAllContours = Mat::zeros( src.size(), CV_8UC3 );
  43. //指定112阀值进行二值化
  44. threshold( src_gray, threshold_output, 112, 255, THRESH_BINARY );
  45. namedWindow( "Threshold_output");
  46. imshow( "Threshold_output",threshold_output);
  47. /*查找轮廓
  48. * 参数说明
  49. 输入图像image必须为一个2值单通道图像
  50. contours参数为检测的轮廓数组,每一个轮廓用一个point类型的vector表示
  51. hiararchy参数和轮廓个数相同,每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ],
  52. 分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,该值设置为负数。
  53. mode表示轮廓的检索模式
  54. CV_RETR_EXTERNAL 表示只检测外轮廓
  55. CV_RETR_LIST 检测的轮廓不建立等级关系
  56. CV_RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
  57. CV_RETR_TREE 建立一个等级树结构的轮廓。具体参考contours.c这个demo
  58. method为轮廓的近似办法
  59. CV_CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
  60. CV_CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
  61. CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS 使用teh-Chinl chain 近似算法
  62. offset表示代表轮廓点的偏移量,可以设置为任意值。对ROI图像中找出的轮廓,并要在整个图像中进行分析时,这个参数还是很有用的。
  63. */
  64. findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_NONE, Point( 0, 0) );
  65. int c= 0,ic= 0,k= 0,area= 0;
  66. //通过黑色定位角作为父轮廓,有两个子轮廓的特点,筛选出三个定位角
  67. int parentIdx= -1;
  68. for( int i = 0; i< contours.size(); i++ )
  69. {
  70. //画出所以轮廓图
  71. drawContours( drawingAllContours, contours, parentIdx, CV_RGB( 255, 255, 255) , 1, 8);
  72. if (hierarchy[i][ 2] != -1 && ic== 0)
  73. {
  74. parentIdx = i;
  75. ic++;
  76. }
  77. else if (hierarchy[i][ 2] != -1)
  78. {
  79. ic++;
  80. }
  81. else if(hierarchy[i][ 2] == -1)
  82. {
  83. ic = 0;
  84. parentIdx = -1;
  85. }
  86. //有两个子轮廓
  87. if ( ic >= 2)
  88. {
  89. //保存找到的三个黑色定位角
  90. contours2.push_back(contours[parentIdx]);
  91. //画出三个黑色定位角的轮廓
  92. drawContours( drawing, contours, parentIdx, CV_RGB(rng.uniform( 0, 255),rng.uniform( 0, 255),rng.uniform( 0, 255)) , 1, 8);
  93. ic = 0;
  94. parentIdx = -1;
  95. }
  96. }
  97. //填充的方式画出三个黑色定位角的轮廓
  98. for( int i= 0; i<contours2.size(); i++)
  99. drawContours( drawing2, contours2, i, CV_RGB(rng.uniform( 100, 255),rng.uniform( 100, 255),rng.uniform( 100, 255)) , -1, 4, hierarchy[k][ 2], 0, Point() );
  100. //获取三个定位角的中心坐标
  101. Point point[ 3];
  102. for( int i= 0; i<contours2.size(); i++)
  103. {
  104. point[i] = Center_cal( contours2, i );
  105. }
  106. //计算轮廓的面积,计算定位角的面积,从而计算出边长
  107. area = contourArea(contours2[ 1]);
  108. int area_side = cvRound ( sqrt ( double(area)));
  109. for( int i= 0; i<contours2.size(); i++)
  110. {
  111. //画出三个定位角的中心连线
  112. line(drawing2,point[i%contours2.size()],point[(i+ 1)%contours2.size()],color,area_side/ 2, 8);
  113. }
  114. namedWindow( "DrawingAllContours");
  115. imshow( "DrawingAllContours", drawingAllContours );
  116. namedWindow( "Drawing2");
  117. imshow( "Drawing2", drawing2 );
  118. namedWindow( "Drawing");
  119. imshow( "Drawing", drawing );
  120. //接下来要框出这整个二维码
  121. Mat gray_all,threshold_output_all;
  122. vector< vector<Point> > contours_all;
  123. vector<Vec4i> hierarchy_all;
  124. cvtColor( drawing2, gray_all, CV_BGR2GRAY );
  125. threshold( gray_all, threshold_output_all, 45, 255, THRESH_BINARY );
  126. findContours( threshold_output_all, contours_all, hierarchy_all, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point( 0, 0) ); //RETR_EXTERNAL表示只寻找最外层轮廓
  127. Point2f fourPoint2f[ 4];
  128. //求最小包围矩形
  129. RotatedRect rectPoint = minAreaRect(contours_all[ 0]);
  130. //将rectPoint变量中存储的坐标值放到 fourPoint的数组中
  131. rectPoint.points(fourPoint2f);
  132. for ( int i = 0; i < 4; i++)
  133. {
  134. line(src_all, fourPoint2f[i% 4], fourPoint2f[(i + 1)% 4]
  135. , Scalar( 20, 21, 237), 3);
  136. }
  137. namedWindow( "Src_all");
  138. imshow( "Src_all", src_all );
  139. //框出二维码后,就可以提取出二维码,然后使用解码库zxing,解出码的信息。
  140. //或者研究二维码的排布规则,自己写解码部分
  141. waitKey( 0);
  142. return( 0);
  143. }

下面是代码运行的图片,代码处理过程的图片都有,包括二值化图片,轮廓图,找到的定位角图和最终的框出二维码图都显示出来了。如下:


这部分的代码主要是修改自guanyonglai博主的代码,在此基础上学习了相应的opencv库函数,并根据自己的理解添加了一些注释。实现的主要功能是根据QR二维码三个定位角的特点找出定位角的坐标,并框出整个QR二维码。
框出二维码后能做的事情就多了,可以使用相应的开源解码库解出二维码的信息,比如Zxing库,libdmtx库。Zxing库能解码的格式和支持的语音格式挺多的,网址: https://github.com/zxing/zxing 。libdmtx库主要解码data matrix二维码,网址: http://libdmtx.sourceforge.net/ 。 这个库我使用过,很简单,直接调用几个函数就可以解出码的信息。
解码部分使用libdmtx库的方式已经实现的了,有时间再补充。去除opencv库,直接用算法处理图片并定位二维码并解码的也写了demo,这部分内容较多,有时间再补充,需要大概思路的可以留言。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DLANDML

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值