基于opencv3.0和zbar下的条形码与二维码识别

Posted 重交亲爸爸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于opencv3.0和zbar下的条形码与二维码识别相关的知识,希望对你有一定的参考价值。

其中对条码与二维码的识别分为以下4个步骤

1. 利用opencv和Zbar(或者Zxing)对标准的条形码图片(即没有多余背景干扰,且图片没有倾斜)进行解码,将解码信息显示出来,并与原始信息对比。

2. 利用opencv和Zbar(或者Zxing)对标准的QR二维码图片(即没有多余背景干扰,且图片没有倾斜)进行解码,将解码信息显示出来,并与原始信息对比。

3. 对非标准条形码,进行定位,然后用Zbar(或者Zxing)解码显示。

4. 对非标准的QR二维码图片,进行定位,然后用Zbar(或者Zxing)解码显示。

 

1. 利用opencv和Zbar(或者Zxing)对标准的条形码图片(即没有多余背景干扰,且图片没有倾斜)进行解码,将解码信息显示出来,并与原始信息对比。

2. 利用opencv和Zbar(或者Zxing)对标准的QR二维码图片(即没有多余背景干扰,且图片没有倾斜)进行解码,将解码信息显示出来,并与原始信息对比。

这两部对于zbar可以一并操作。

操作步骤主要分为两部分:A.原图进行灰度转化,B.送入Zbar扫描仪进行扫描(调用ImageScanner)

源码如下:

 

 1 /******************************************************
 2 函数名称: Dis_Barcode
 3 函数功能: 识别条形码和二维码
 4 传入参数:
 5 返 回 值:
 6 建立时间: 2018-05-19
 7 修改时间:
 8 建 立 人: 
 9 修 改 人:
10 其它说明:这里是借鉴其他人的代码:
11 原文链接:https://www.cnblogs.com/dengxiaojun/p/5278679.html
12 以下代码是经过改动的
13 ******************************************************/
14 void MyClass::Dis_code(Mat image){
15     Mat imageGray;  // 所转化成的灰度图像 
16     //定义一个扫描仪  
17     ImageScanner scanner;
18     scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);
19 
20     cvtColor(image, imageGray, CV_RGB2GRAY);
21     imshow("灰度图", imageGray);
22     // 获取所摄取图像的长和宽  
23     int width = imageGray.cols;
24     int height = imageGray.rows;
25     // 在Zbar中进行扫描时候,需要将OpenCV中的Mat类型转换为(uchar *)类型,raw中存放的是图像的地址;对应的图像需要转成Zbar中对应的图像zbar::Image  
26     uchar *raw = (uchar *)imageGray.data;
27     Image imageZbar(width, height, "Y800", raw, width * height);
28     // 扫描相应的图像imageZbar(imageZbar是zbar::Image类型,存储着读入的图像)  
29     scanner.scan(imageZbar); //扫描条码      
30     Image::SymbolIterator symbol = imageZbar.symbol_begin();
31     if (imageZbar.symbol_begin() == imageZbar.symbol_end())
32     {
33         cout << "查询条码失败,请检查图片!" << endl;
34     }
35     for (; symbol != imageZbar.symbol_end(); ++symbol)
36     {
37         cout << "类型:" << endl << symbol->get_type_name() << endl << endl;
38         cout << "条码:" << endl << symbol->get_data() << endl << endl;
39     }
40 
41     waitKey(); // 等待按下esc键,若需要延时1s则改用waitKey(1000);  
42 
43     // 将图像中的数据置为0  
44     imageZbar.set_data(NULL, 0);
45     system("pause");
46 }

 

结果如下:

条形码识别:

 

 

二维码识别:

 

 

3. 对非标准条形码,进行定位,然后用Zbar(或者Zxing)解码显示

在条形码的识别上,根据条形码的特性,我们只关心x轴上的形态。通过x轴的宽度进行确定条码的大小,y轴根据实际提取进行区分

处理的目标:

A.消去非码的其他物体图形

B.划定条码的范围

C.提取图片的ROI区域(即条码区域)

 总体分为:

灰度处理-》高斯平滑-》Sobel x—y梯度差-》均值滤波-》二值化-》闭运算-》腐蚀膨胀-》获取ROI

 1 /******************************************************
 2 函数名称: Run
 3 函数功能: 开始
 4 传入参数:
 5 返 回 值:
 6 建立时间: 2018-05-19
 7 修改时间:
 8 建 立 人: 
 9 修 改 人:
10 其它说明:
11 ******************************************************/
12 void MyClass::Run(){
13     Mat image;
14     image = getGray(srcimage);//获取灰度图
15     image = getGass(image);//高斯平滑滤波
16     image = getSobel(image);//Sobel x—y梯度差
17     image = getBlur(image);//均值滤波除高频噪声
18     image = getThold(image);//二值化
19     image = getBys(image);//闭运算
20     image = getErode(image);//腐蚀
21     image = getDilate(image);//膨胀
22     image = getRect(image, srcimage);//获取ROI
23     imshow("最后的图", image);
24     Dis_code(image);
25     waitKey();
26 }

灰度处理(消除颜色干扰)

 1 /******************************************************
 2 函数名称: getGray
 3 函数功能: 灰度处理
 4 传入参数: Mat image
 5 返 回 值:
 6 建立时间: 2018-05-19
 7 修改时间:
 8 建 立 人: 
 9 修 改 人:
10 其它说明:
11 ******************************************************/
12 Mat MyClass::getGray(Mat image, bool show){//show默认false 待定参数法
13     Mat cimage;
14     cvtColor(image, cimage, CV_RGBA2GRAY);
15     if (show)
16     imshow("灰度图", cimage);
17     return cimage;
18 }

处理结果:

高斯滤波处理(消除高斯噪声)

 1 /******************************************************
 2 函数名称: getGass
 3 函数功能: 高斯滤波处理
 4 传入参数: Mat image
 5 返 回 值:
 6 建立时间: 2018-05-19
 7 修改时间:
 8 建 立 人: 
 9 修 改 人:
10 其它说明:
11 ******************************************************/
12 Mat MyClass::getGass(Mat image, bool show){
13     Mat cimage;
14     GaussianBlur(image, cimage, Size(3, 3), 0);
15     if (show)
16     imshow("高斯滤波图", cimage);
17     return cimage;
18 }
 

处理结果:

Sobel x-y差处理(只考虑x轴,消除y轴不必要信息)

 1 /******************************************************
 2 函数名称: getSobel
 3 函数功能: Sobel处理
 4 传入参数: Mat image
 5 返 回 值:
 6 建立时间: 2018-05-19
 7 修改时间:
 8 建 立 人: 
 9 修 改 人:
10 其它说明:
11 ******************************************************/
12 Mat MyClass::getSobel(Mat image, bool show){
13     Mat cimageX16s, cimageY16s, imageSobelX, imageSobelY, out;
14     Sobel(image, cimageX16s, CV_16S, 1, 0, 3, 1, 0, 4);
15     Sobel(image, cimageY16s, CV_16S, 0, 1, 3, 1, 0, 4);
16     convertScaleAbs(cimageX16s, imageSobelX, 1, 0);
17     convertScaleAbs(cimageY16s, imageSobelY, 1, 0);
18     out = imageSobelX - imageSobelY;
19     if (show)
20     imshow("Sobelx-y差 图", out);
21     return out;
22 }
 

处理结果:

均值滤波处理(消除高频噪声)

 1 /******************************************************
 2 函数名称: getBlur
 3 函数功能: 均值滤波处理
 4 传入参数: Mat image
 5 返 回 值:
 6 建立时间: 2018-05-19
 7 修改时间:
 8 建 立 人: 
 9 修 改 人:
10 其它说明:
11  ******************************************************/
12 Mat MyClass::getBlur(Mat image, bool show){
13      Mat cimage;
14      blur(image, cimage, Size(3, 3));
15      if (show)
16      imshow("均值滤波图", cimage);
17      return cimage;
18  }
 

处理结果:

二值化处理(使图像中数据量大为减少,从而能凸显出目标的轮廓)

 1 /******************************************************
 2 函数名称: getThold
 3 函数功能: 二值化处理
 4 传入参数: Mat image
 5 返 回 值:
 6 建立时间: 2018-05-19
 7 修改时间:
 8 建 立 人: 
 9 修 改 人:
10 其它说明:
11 ******************************************************/
12 Mat MyClass::getThold(Mat image, bool show){
13     Mat cimage;
14     threshold(image, cimage, 112, 255, CV_THRESH_BINARY);
15     if (show)
16     imshow("二值化图", cimage);
17     return cimage;
18 }
 

处理结果:

闭运算处理(扩大轴之间的间隙)

 1 /******************************************************
 2 函数名称: getBys
 3 函数功能: 闭运算处理
 4 传入参数: Mat image
 5 返 回 值:
 6 建立时间: 2018-05-19
 7 修改时间:
 8 建 立 人: 
 9 修 改 人:
10 其它说明:
11 ******************************************************/
12 Mat MyClass::getBys(Mat image, bool show){
13     morphologyEx(image, image, MORPH_CLOSE, element);
14     if (show)
15     imshow("闭运算图", image);
16     return image;
17 }

处理结果:

腐蚀膨胀(消去干扰点和合并条码区域)

 1 /******************************************************
 2 函数名称: getErode
 3 函数功能: 腐蚀处理
 4 传入参数: Mat image
 5 返 回 值:
 6 建立时间: 2018-05-19
 7 修改时间:
 8 建 立 人: 
 9 修 改 人:
10 其它说明:
11 ******************************************************/
12 Mat MyClass::getErode(Mat image, bool show){
13     //Mat cimage;
14     erode(image, image, element);
15     if (show)
16     imshow("腐蚀图", image);
17     return image;
18 }
19 /******************************************************
20 函数名称: getDilate
21 函数功能: 膨胀处理
22 传入参数: Mat image
23 返 回 值:
24 建立时间: 2018-05-19
25 修改时间:
26 建 立 人: 
27 修 改 人:
28 其它说明:
29 ******************************************************/
30 Mat MyClass::getDilate(Mat image, bool show){
31     for (int i = 0; i < 3; i++)
32         dilate(image, image, element);
33     if (show)
34     imshow("膨胀图", image);
35     return image;
36 }

处理结果:

获取ROI(为Zbar处理作预处理)

 1 /******************************************************
 2 函数名称: getRect
 3 函数功能: 获取码的区域
 4 传入参数: Mat image, Mat simage原图
 5 返 回 值:
 6 建立时间: 2018-05-19
 7 修改时间:
 8 建 立 人: 
 9 修 改 人:
10 其它说明:借鉴其他人进行改进
11 ******************************************************/
12 Mat MyClass::getRect(Mat image, Mat simage, bool show){
13     vector<vector<Point>> contours;
14     vector<Vec4i> hiera;
15     Mat cimage;
16     findContours(image, contours, hiera, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
17     vector<float>contourArea;
18     for (int i = 0; i < contours.size(); i++)
19     {
20         contourArea.push_back(cv::contourArea(contours[i]));
21     }
22     //找出面积最大的轮廓
23     double maxValue; Point maxLoc;
24     minMaxLoc(contourArea, NULL, &maxValue, NULL, &maxLoc);
25     //计算面积最大的轮廓的最小的外包矩形
26     RotatedRect minRect = minAreaRect(contours[maxLoc.x]);
27     //为了防止找错,要检查这个矩形的偏斜角度不能超标
28     //如果超标,那就是没找到
29     if (minRect.angle<2.0)
30     {
31         //找到了矩形的角度,但是这是一个旋转矩形,所以还要重新获得一个外包最小矩形
32         Rect myRect = boundingRect(contours[maxLoc.x]);
33         //把这个矩形在源图像中画出来
34         //rectangle(srcImage,myRect,Scalar(0,255,255),3,LINE_AA);
35         //看看显示效果,找的对不对
36         //imshow(windowNameString,srcImage);
37         //将扫描的图像裁剪下来,并保存为相应的结果,保留一些X方向的边界,所以对rect进行一定的扩张
38         myRect.x = myRect.x - (myRect.width / 20);
39         myRect.width = myRect.width*1.1;
40         Mat resultImage = Mat(srcimage, myRect);
41         return resultImage;
42     }
43 
44     for (int i = 0; i<contours.size(); i++)
45     {
46         Rect rect = boundingRect((Mat)contours[i]);
47         //cimage = simage(rect);
48         rectangle(simage, rect, Scalar(0), 2);
49         if (show)
50         imshow("转变图", simage);
51     }
52     return simage;
53 }

处理结果:

最后识别处理结果:

 4. 对非标准的QR二维码图片,进行定位,然后用Zbar(或者Zxing)解码显示。

这里主要参考https://blog.csdn.net/nick123chao/article/details/77573675的博客。不过该博客的处理没有考虑多个识别点时的情况:

例图:

本文主要处理去除干扰的识别点的方向进行研究解决。根据二维码特性:

我们只要找到90°±Δx的角,且夹角两边为最小的边即可。

找到三个点后,我们需要对齐做旋转处理,旋转的角度如下:

其中处理的步骤分为:

灰度处理-》边缘检测-》特征轮廓检测-》提取特征点-》排除干扰点-》绘制直角三角形-》纠正旋转-》提取ROI-》识别

这里先给效果,后展示代码

边缘检测:

特征轮廓检测

提取特征点-》排除干扰点-》绘制直角三角形

纠正旋转

提取ROI

识别

源码如下:

  1 /******************************************************
  2 函数名称: QrRun
  3 函数功能: 开始
  4 传入参数:
  5 返 回 值:
  6 建立时间: 2018-05-19
  7 修改时间:
  8 建 立 人: 
  9 修 改 人:
 10 其它说明:
 11 ******************************************************/
 12 void MyClass::QrRun(){
 13     RNG rng(12345);
 14     //imshow("原图", srcimage);
 15     Mat src_all = srcimage.clone();
 16     Mat src_gray;
 17     //灰度处理
 18     src_gray = getBlur(getGray(srcimage));
 19 
 20     Scalar color = Scalar(1, 1, 255);
 21     Mat threshold_output;
 22     vector<vector<Point> > contours, contours2;
 23     vector<Vec4i> hierarchy;
 24     Mat drawing = Mat::zeros(srcimage.size(), CV_8UC3);
 25     Mat drawing2 = Mat::zeros(srcimage.size(), CV_8UC3);
 26     Mat drawingAllContours = Mat::zeros(srcimage.size(), CV_8UC3);
 27 
 28     threshold_output = getThold(src_gray);
 29 
 30     findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0));
 31 
 32     int c = 0, ic = 0, k = 0, area = 0;
 33     // 边缘检测 
 34     //通过黑色定位角作为父轮廓,有两个子轮廓的特点,筛选出三个定位角  
 35     int parentIdx = -1;
 36     for (int i = 0; i< contours.size(); i++)
 37     {
 38         //画出所以轮廓图  
 39         drawContours(drawingAllContours, contours, parentIdx, CV_RGB(255, 255, 255), 1, 8);
 40         if (hierarchy[i][2] != -1 && ic == 0)
 41         {
 42             parentIdx = i;
 43             ic++;
 44         }
 45         else if (hierarchy[i][2] != -1)
 46         {
 47             ic++;
 48         }
 49         else if (hierarchy[i][2] == -1)
 50         {
 51             ic = 0;
 52             parentIdx = -1;
 53         }
 54         //特征轮廓检测 - 》
 55         //有两个子轮廓  
 56         if (ic >= 2)
 57         {
 58             //保存找到的三个黑色定位角  
 59             contours2.push_back(contours[parentIdx]);
 60             //画出三个黑色定位角的轮廓  
 61             drawContours(drawing, contours, parentIdx, CV_RGB(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), 1, 8);
 62             ic = 0;
 63             parentIdx = -1;
 64         }
 65     }
 66     //提取特征点 
 67     //填充的方式画出黑色定位角的轮廓  
 68     for (int i = 0; i<contours2.size(); i++)
 69         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());
 70 
 71     //获取定位角的中心坐标  
 72     vector<Point> pointfind;
以上是关于基于opencv3.0和zbar下的条形码与二维码识别的主要内容,如果未能解决你的问题,请参考以下文章

ZBar 是款桌面电脑用条形码/二维码扫描工具

zbar+opencv检测图片中的二维码或条形码

用OpenCV和Python识别二维码与条形码

OpenCV和Zbar两个Python模块实现二维码和条形码识别

安装二维码条形码识别工具zbar

二维码解码器Zbar+VS2010开发环境配置(使用opencv库)