使用Opencv+Zbar组合可以很容易的识别图片中的二维码,特别是标准的二维码,这里标准指的是二维码成像清晰,图片中二维码的空间占比在40%~100%之间,这样标准的图片,Zbar识别起来很容易,不需要Opencv额外的处理。

下边这个例程演示两者配合对条形码和二维码的识别:

#include "zbar.h"
#include "cv.h"
#include "highgui.h"
#include <iostream> using namespace std;
using namespace zbar; //添加zbar名称空间
using namespace cv; int main(int argc,char*argv[])
{
ImageScanner scanner;
scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);
Mat image = imread(argv[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<<"查询条码失败,请检查图片!"<<endl;
}
for(;symbol != imageZbar.symbol_end();++symbol)
{
cout<<"类型:"<<endl<<symbol->get_type_name()<<endl<<endl;
cout<<"条码:"<<endl<<symbol->get_data()<<endl<<endl;
}
imshow("Source Image",image);
waitKey();
imageZbar.set_data(NULL,0);
return 0;
}

条形码:

二维码:

这样“标准的”二维码是Zbar非常拿手的,能准确快速的检测出来,包括在条形码外有部分其他信息的,也是小菜一碟:

Zbar很省心,我们还是可以为它做点什么的,比如在一些情况下,需要把条形码裁剪出来,这就涉及到条形码位置的定位,这篇文章准备记录一下如何定位条形码,在定位之后再把裁剪出来的条形码区域丢给Zbar识别读码。

方法一. 水平、垂直方向投影

#include "zbar.h"
#include "cv.h"
#include "highgui.h"
#include <iostream> using namespace std;
using namespace zbar; //添加zbar名称空间
using namespace cv;
//***********************************************
// 函数通过水平和垂直方向投影,找到两个方向上投影的交叉矩形,定位到条形码/二维码
// int threshodValue 投影的最少像素单位
// int binaryzationValue 原图像阈值分割值
//***********************************************
Rect DrawXYProjection(const Mat image,Mat &imageOut,const int threshodValue,const int binaryzationValue); int main(int argc,char*argv[])
{
Mat image = imread(argv[1]);
Mat imageCopy=image.clone();
Mat imageGray,imagOut;
cvtColor(image,imageGray,CV_RGB2GRAY);
Rect rect(0,0,0,0);
rect= DrawXYProjection(image,imagOut,image.rows/10,100);
Mat roi=image(rect);
//画出条形码的矩形框
rectangle(imageCopy,Point(rect.x,rect.y),Point(rect.x+rect.width,rect.y+rect.height),Scalar(0,0,255),2);
imshow("Source Image",image);
imshow("水平垂直投影",imagOut);
imshow("Output Image",roi);
imshow("Source Image Rect",imageCopy);
waitKey();
return 0;
} Rect DrawXYProjection(const Mat image,Mat &imageOut,const int threshodValue,const int binaryzationValue)
{
Mat img=image.clone();
if(img.channels()>1)
{
cvtColor(img,img,CV_RGB2GRAY);
}
Mat out(img.size(),img.type(),Scalar(255));
imageOut=out;
//对每一个传入的图片做灰度归一化,以便使用同一套阈值参数
normalize(img,img,0,255,NORM_MINMAX);
vector<int> vectorVertical(img.cols,0);
for(int i=0;i<img.cols;i++)
{
for(int j=0;j<img.rows;j++)
{
if(img.at<uchar>(j,i)<binaryzationValue)
{
vectorVertical[i]++;
}
}
}
//列值归一化
int high=img.rows/6;
normalize(vectorVertical,vectorVertical,0,high,NORM_MINMAX);
for(int i=0;i<img.cols;i++)
{
for(int j=0;j<img.rows;j++)
{
if(vectorVertical[i]>threshodValue)
{
line(imageOut,Point(i,img.rows),Point(i,img.rows-vectorVertical[i]),Scalar(0));
}
}
}
//水平投影
vector<int> vectorHorizontal(img.rows,0);
for(int i=0;i<img.rows;i++)
{
for(int j=0;j<img.cols;j++)
{
if(img.at<uchar>(i,j)<binaryzationValue)
{
vectorHorizontal[i]++;
}
}
}
normalize(vectorHorizontal,vectorHorizontal,0,high,NORM_MINMAX);
for(int i=0;i<img.rows;i++)
{
for(int j=0;j<img.cols;j++)
{
if(vectorHorizontal[i]>threshodValue)
{
line(imageOut,Point(img.cols-vectorHorizontal[i],i),Point(img.cols,i),Scalar(0));
}
}
}
//找到投影四个角点坐标
vector<int>::iterator beginV=vectorVertical.begin();
vector<int>::iterator beginH=vectorHorizontal.begin();
vector<int>::iterator endV=vectorVertical.end()-1;
vector<int>::iterator endH=vectorHorizontal.end()-1;
int widthV=0;
int widthH=0;
int highV=0;
int highH=0;
while(*beginV<threshodValue)
{
beginV++;
widthV++;
}
while(*endV<threshodValue)
{
endV--;
widthH++;
}
while(*beginH<threshodValue)
{
beginH++;
highV++;
}
while(*endH<threshodValue)
{
endH--;
highH++;
}
//投影矩形
Rect rect(widthV,highV,img.cols-widthH-widthV,img.rows-highH-highV);
return rect;
}

通过图像在水平和垂直方向上的投影,按照一定的阈值,找到二维码所在位置,剪切出来用于下一步Zbar条码识别。当然这个方法只能识别出背景简单的图片中的二维码。

条形码效果:

水平、垂直投影

检出条形码区域

二维码效果:

         

方法二.梯度运算

#include "core/core.hpp"
#include "highgui/highgui.hpp"
#include "imgproc/imgproc.hpp" using namespace cv; int main(int argc,char *argv[])
{
Mat image,imageGray,imageGuussian;
Mat imageSobelX,imageSobelY,imageSobelOut;
image=imread(argv[1]); //1. 原图像大小调整,提高运算效率
resize(image,image,Size(500,300));
imshow("1.原图像",image); //2. 转化为灰度图
cvtColor(image,imageGray,CV_RGB2GRAY);
imshow("2.灰度图",imageGray); //3. 高斯平滑滤波
GaussianBlur(imageGray,imageGuussian,Size(3,3),0);
imshow("3.高斯平衡滤波",imageGuussian); //4.求得水平和垂直方向灰度图像的梯度差,使用Sobel算子
Mat imageX16S,imageY16S;
Sobel(imageGuussian,imageX16S,CV_16S,1,0,3,1,0,4);
Sobel(imageGuussian,imageY16S,CV_16S,0,1,3,1,0,4);
convertScaleAbs(imageX16S,imageSobelX,1,0);
convertScaleAbs(imageY16S,imageSobelY,1,0);
imageSobelOut=imageSobelX-imageSobelY;
imshow("4.X方向梯度",imageSobelX);
imshow("4.Y方向梯度",imageSobelY);
imshow("4.XY方向梯度差",imageSobelOut); //5.均值滤波,消除高频噪声
blur(imageSobelOut,imageSobelOut,Size(3,3));
imshow("5.均值滤波",imageSobelOut); //6.二值化
Mat imageSobleOutThreshold;
threshold(imageSobelOut,imageSobleOutThreshold,180,255,CV_THRESH_BINARY);
imshow("6.二值化",imageSobleOutThreshold); //7.闭运算,填充条形码间隙
Mat element=getStructuringElement(0,Size(7,7));
morphologyEx(imageSobleOutThreshold,imageSobleOutThreshold,MORPH_CLOSE,element);
imshow("7.闭运算",imageSobleOutThreshold); //8. 腐蚀,去除孤立的点
erode(imageSobleOutThreshold,imageSobleOutThreshold,element);
imshow("8.腐蚀",imageSobleOutThreshold); //9. 膨胀,填充条形码间空隙,根据核的大小,有可能需要2~3次膨胀操作
dilate(imageSobleOutThreshold,imageSobleOutThreshold,element);
dilate(imageSobleOutThreshold,imageSobleOutThreshold,element);
dilate(imageSobleOutThreshold,imageSobleOutThreshold,element);
imshow("9.膨胀",imageSobleOutThreshold);
vector<vector<Point>> contours;
vector<Vec4i> hiera; //10.通过findContours找到条形码区域的矩形边界
findContours(imageSobleOutThreshold,contours,hiera,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);
for(int i=0;i<contours.size();i++)
{
Rect rect=boundingRect((Mat)contours[i]);
rectangle(image,rect,Scalar(255),2);
}
imshow("10.找出二维码矩形区域",image); waitKey();
}

原图像

平滑滤波

水平和垂直方向灰度图像的梯度差

闭运算、腐蚀、膨胀后通过findContours找到条形码区域的矩形边界

二维码:

原图:

平衡滤波

梯度和

闭运算、腐蚀、膨胀后通过findContours找到条形码区域的矩形边界

Opencv+Zbar二维码识别(标准条形码/二维码识别)的更多相关文章

  1. iOS开发——iOS7(及以后版本) SDK自带二维码(含条形码)扫码、二维码生成

    本文转载至 http://www.cnblogs.com/leotangcn/p/4357907.html 现在很多APP都涉及了二维码扫码功能,这个功能简单实用,很多情况下用户乐于使用,现在本文带来 ...

  2. Opencv+Zbar二维码识别(二维码校正)

    二维码和车牌识别基本都会涉及到图像的校正,主要是形变和倾斜角度的校正,一种二维码的畸变如下图: 这个码用微信扫了一下,识别不出来,但是用Zbar还是可以准确识别的~~. 这里介绍一种二维码校正方法,通 ...

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

    ZBar 是款桌面电脑用条形码/二维码扫描工具 windows平台python 2.7环境编译安装zbar   最近一个项目需要识别二维码,找来找去找到了zbar和zxing,中间越过无数坑,总算基本 ...

  4. QRCode 扫描二维码、扫描条形码、相册获取图片后识别、生成带 Logo 二维码、支持微博微信 QQ 二维码扫描样式

    目录 功能介绍 常见问题 效果图与示例 apk Gradle 依赖 布局文件 自定义属性说明 接口说明 关于我 功能介绍 根据之前公司的产品需求,参考 barcodescanner 改的,希望能帮助到 ...

  5. 基于Opencv识别,矫正二维码(C++)

    参考链接 [ 基于opencv 识别.定位二维码 (c++版) ](https://www.cnblogs.com/yuanchenhui/p/opencv_qr.html) OpenCV4.0.0二 ...

  6. 在Android上使用ZXing识别条形码/二维码

    越来越多的手机具备自动对焦的拍摄功能,这也意味着这些手机可以具备条码扫描的功能.......手机具备条码扫描的功能,可以优化购物流程,快速存储电子名片(二维码)等. 本文使用ZXing 1.6实现条码 ...

  7. 基于opencv 识别、定位二维码 (c++版)

    前言 因工作需要,需要定位图片中的二维码:我遂查阅了相关资料,也学习了opencv开源库.通过一番努力,终于很好的实现了二维码定位.本文将讲解如何使用opencv定位二维码. 定位二维码不仅仅是为了识 ...

  8. Java 条形码 二维码 的生成与解析

    Barcode简介 Barcode是由一组按一定编码规则排列的条,空符号,用以表示一定的字符,数字及符号组成的,一种机器可读的数据表示方式. Barcode的形式多种多样,按照它们的外观分类: Lin ...

  9. C# 生成二维码,彩色二维码,带有Logo的二维码及普通条形码

    每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不 ...

随机推荐

  1. mysql续

    接上篇博客,写完以后看了看,还是觉的写的太简单,就算是自己复习都不够,所以再补充一些 1.创建多表关联 需求:图书管理系统,创建几张表,包含书籍,出版社,作者,作者详细信息等内容 分析: (1)图书只 ...

  2. MongoDB 复制(副本集)学习

    MongoDB 复制(副本集)学习 replication set复制集,复制集,多台服务器维护相同的数据副本,提高服务器的可用性.MongoDB复制是将数据同步在多个服务器的过程.复制提供了数据的冗 ...

  3. Unity3D游戏开发之C#编程中常见数据结构的比较

    一.前言 Unity3D是如今最火爆的游戏开发引擎,它可以让我们能轻松创建诸如三维视频游戏.建筑可视化.实时三维动画等类型的互动内容.它支持2D/3D游戏开发,据不完全统计,目前国内80%的手机游戏都 ...

  4. 数据结构之区间K大数

    求区间的问题有很多类,虽然前人有很多讲解了: 但是我在这里在普及一下,算是自己的一种复习吧. 1.静态询问一个区间的的第k大数,比如询问[l,r] k大数.虽然主席树可以处理,但是这类问题应该是划分树 ...

  5. How to resolve 'Potential Leak' issue

    -1 down vote favorite I am using the 'analyze' tool in xcode to check for potential leakages in my a ...

  6. 常用Git命令手册

    常用Git命令手册 此文只是对Git有一定基础的人当记忆使用,比较简略,初级学员强烈推荐廖雪峰老师的Git系列教程,通俗易懂,戳此处即可开始学习 1.安装Git Linux sudo apt-get ...

  7. 【Todo】已经打开的页面需要清掉的坑

    下面是当前我浏览器里面打开的技术文章.需要清掉.一个坑一个坑地填吧. 微信文件传输里面也有几篇12.6号的<Akuna Capital电面面经><2016最流行的Java EE服务器 ...

  8. [转]通俗易懂的php多线程解决方案

    原文: https://www.w3cschool.cn/php/php-thread.html --------------------------------------------------- ...

  9. 子组件跟随父组件re-render

    想象一下这种场景,一个父组件下面一大堆子组件.然后呢,这个父组件re-render.是不是下面的子组件都得跟着re-render.可是很多子组件里面是冤枉的啊!!很多子组件的props 和 state ...

  10. Computer form factor

    http://en.wikipedia.org/wiki/Motherboard_form_factor Computer form factor From Wikipedia, the free e ...