目的:
使用opencv库识别QR二维码,框出图片中的二维码,并使用开源库Zxing解码,在这过程中学习理解opencv库相应的函数。
环境:
1. window7系统
2. QT create
1.准备
首先安装QT和QT create开发环境,window下使用cmake编译opencv生产lib库。这部分内容网上很多资料,直接搜索安装就行。
2.工程代码
QT create配置使用opencv库。在工程的pro文件下面添加指定opencv库的头文件和lib的路径。如下:
-
INCLUDEPATH+=C:\Qt\opencv\include\opencv\
-
C:\Qt\opencv\include\opencv2\
-
C:\Qt\opencv\include
-
-
LIBS+=C:\Qt\opencv\
lib\libopencv_calib3d320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_core320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_features2d320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_flann320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_highgui320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_imgcodecs320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_imgproc320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_ml320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_objdetect320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_photo320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_shape320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_stitching320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_superres320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_video320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_videoio320.dll.a\
-
C:\Qt\opencv\
lib\libopencv_videostab320.dll.a
opencv
识别定位二维码的代码如下:
-
#include "opencv2/highgui/highgui.hpp"
-
#include "opencv2/imgproc/imgproc.hpp"
-
#include <iostream>
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <math.h>
-
-
#include <QDebug>
-
-
using
namespace cv;
-
using
namespace
std;
-
-
-
Mat src; Mat src_gray;
-
-
-
RNG rng(12345);
-
//Scalar colorful = CV_RGB(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255));
-
-
//获取轮廓的中心点
-
Point Center_cal(vector<vector<Point> > contours,int i)
-
{
-
int centerx=
0,centery=
0,n=contours[i].size();
-
//在提取的小正方形的边界上每隔周长个像素提取一个点的坐标,
-
//求所提取四个点的平均坐标(即为小正方形的大致中心)
-
centerx = (contours[i][n/
4].x + contours[i][n*
2/
4].x + contours[i][
3*n/
4].x + contours[i][n
-1].x)/
4;
-
centery = (contours[i][n/
4].y + contours[i][n*
2/
4].y + contours[i][
3*n/
4].y + contours[i][n
-1].y)/
4;
-
Point point1=Point(centerx,centery);
-
return point1;
-
}
-
-
-
int main( int argc, char** argv[] )
-
{
-
src = imread(
"core.jpg",
1 );
-
Mat src_all=src.clone();
-
-
-
//彩色图转灰度图
-
cvtColor( src, src_gray, CV_BGR2GRAY );
-
//对图像进行平滑处理
-
blur( src_gray, src_gray, Size(
3,
3) );
-
//使灰度图象直方图均衡化
-
equalizeHist( src_gray, src_gray );
-
namedWindow(
"src_gray");
-
imshow(
"src_gray",src_gray);
-
-
-
Scalar color = Scalar(
1,
1,
255 );
-
Mat threshold_output;
-
vector<
vector<Point> > contours,contours2;
-
vector<Vec4i> hierarchy;
-
Mat drawing = Mat::zeros( src.size(), CV_8UC3 );
-
Mat drawing2 = Mat::zeros( src.size(), CV_8UC3 );
-
Mat drawingAllContours = Mat::zeros( src.size(), CV_8UC3 );
-
-
//指定112阀值进行二值化
-
threshold( src_gray, threshold_output,
112,
255, THRESH_BINARY );
-
-
namedWindow(
"Threshold_output");
-
imshow(
"Threshold_output",threshold_output);
-
-
-
/*查找轮廓
-
* 参数说明
-
输入图像image必须为一个2值单通道图像
-
contours参数为检测的轮廓数组,每一个轮廓用一个point类型的vector表示
-
hiararchy参数和轮廓个数相同,每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ],
-
分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,该值设置为负数。
-
mode表示轮廓的检索模式
-
CV_RETR_EXTERNAL 表示只检测外轮廓
-
CV_RETR_LIST 检测的轮廓不建立等级关系
-
CV_RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
-
CV_RETR_TREE 建立一个等级树结构的轮廓。具体参考contours.c这个demo
-
method为轮廓的近似办法
-
CV_CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
-
CV_CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
-
CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS 使用teh-Chinl chain 近似算法
-
offset表示代表轮廓点的偏移量,可以设置为任意值。对ROI图像中找出的轮廓,并要在整个图像中进行分析时,这个参数还是很有用的。
-
*/
-
findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_NONE, Point(
0,
0) );
-
-
int c=
0,ic=
0,k=
0,area=
0;
-
-
//通过黑色定位角作为父轮廓,有两个子轮廓的特点,筛选出三个定位角
-
int parentIdx=
-1;
-
for(
int i =
0; i< contours.size(); i++ )
-
{
-
//画出所以轮廓图
-
drawContours( drawingAllContours, contours, parentIdx, CV_RGB(
255,
255,
255) ,
1,
8);
-
if (hierarchy[i][
2] !=
-1 && ic==
0)
-
{
-
parentIdx = i;
-
ic++;
-
}
-
else
if (hierarchy[i][
2] !=
-1)
-
{
-
ic++;
-
}
-
else
if(hierarchy[i][
2] ==
-1)
-
{
-
ic =
0;
-
parentIdx =
-1;
-
}
-
-
//有两个子轮廓
-
if ( ic >=
2)
-
{
-
//保存找到的三个黑色定位角
-
contours2.push_back(contours[parentIdx]);
-
//画出三个黑色定位角的轮廓
-
drawContours( drawing, contours, parentIdx, CV_RGB(rng.uniform(
0,
255),rng.uniform(
0,
255),rng.uniform(
0,
255)) ,
1,
8);
-
ic =
0;
-
parentIdx =
-1;
-
}
-
}
-
-
//填充的方式画出三个黑色定位角的轮廓
-
for(
int i=
0; i<contours2.size(); i++)
-
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() );
-
-
//获取三个定位角的中心坐标
-
Point point[
3];
-
for(
int i=
0; i<contours2.size(); i++)
-
{
-
point[i] = Center_cal( contours2, i );
-
}
-
-
//计算轮廓的面积,计算定位角的面积,从而计算出边长
-
area = contourArea(contours2[
1]);
-
int area_side = cvRound (
sqrt (
double(area)));
-
for(
int i=
0; i<contours2.size(); i++)
-
{
-
//画出三个定位角的中心连线
-
line(drawing2,point[i%contours2.size()],point[(i+
1)%contours2.size()],color,area_side/
2,
8);
-
}
-
-
namedWindow(
"DrawingAllContours");
-
imshow(
"DrawingAllContours", drawingAllContours );
-
-
namedWindow(
"Drawing2");
-
imshow(
"Drawing2", drawing2 );
-
-
namedWindow(
"Drawing");
-
imshow(
"Drawing", drawing );
-
-
-
//接下来要框出这整个二维码
-
Mat gray_all,threshold_output_all;
-
vector<
vector<Point> > contours_all;
-
vector<Vec4i> hierarchy_all;
-
cvtColor( drawing2, gray_all, CV_BGR2GRAY );
-
-
-
threshold( gray_all, threshold_output_all,
45,
255, THRESH_BINARY );
-
findContours( threshold_output_all, contours_all, hierarchy_all, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(
0,
0) );
//RETR_EXTERNAL表示只寻找最外层轮廓
-
-
-
Point2f fourPoint2f[
4];
-
//求最小包围矩形
-
RotatedRect rectPoint = minAreaRect(contours_all[
0]);
-
-
//将rectPoint变量中存储的坐标值放到 fourPoint的数组中
-
rectPoint.points(fourPoint2f);
-
-
-
for (
int i =
0; i <
4; i++)
-
{
-
line(src_all, fourPoint2f[i%
4], fourPoint2f[(i +
1)%
4]
-
, Scalar(
20,
21,
237),
3);
-
}
-
-
namedWindow(
"Src_all");
-
imshow(
"Src_all", src_all );
-
-
//框出二维码后,就可以提取出二维码,然后使用解码库zxing,解出码的信息。
-
//或者研究二维码的排布规则,自己写解码部分
-
-
waitKey(
0);
-
return(
0);
-
}
-
下面是代码运行的图片,代码处理过程的图片都有,包括二值化图片,轮廓图,找到的定位角图和最终的框出二维码图都显示出来了。如下:
框出二维码后能做的事情就多了,可以使用相应的开源解码库解出二维码的信息,比如Zxing库,libdmtx库。Zxing库能解码的格式和支持的语音格式挺多的,网址:
https://github.com/zxing/zxing
。libdmtx库主要解码data matrix二维码,网址:
http://libdmtx.sourceforge.net/
。
这个库我使用过,很简单,直接调用几个函数就可以解出码的信息。
解码部分使用libdmtx库的方式已经实现的了,有时间再补充。去除opencv库,直接用算法处理图片并定位二维码并解码的也写了demo,这部分内容较多,有时间再补充,需要大概思路的可以留言。