其中对条码与二维码的识别分为以下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)

源码如下:

 /******************************************************
函数名称: Dis_Barcode
函数功能: 识别条形码和二维码
传入参数:
返 回 值:
建立时间: 2018-05-19
修改时间:
建 立 人:
修 改 人:
其它说明:这里是借鉴其他人的代码:
原文链接:https://www.cnblogs.com/dengxiaojun/p/5278679.html
以下代码是经过改动的
******************************************************/
void MyClass::Dis_code(Mat image){
Mat imageGray; // 所转化成的灰度图像
//定义一个扫描仪
ImageScanner scanner;
scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, ); cvtColor(image, imageGray, CV_RGB2GRAY);
imshow("灰度图", imageGray);
// 获取所摄取图像的长和宽
int width = imageGray.cols;
int height = imageGray.rows;
// 在Zbar中进行扫描时候,需要将OpenCV中的Mat类型转换为(uchar *)类型,raw中存放的是图像的地址;对应的图像需要转成Zbar中对应的图像zbar::Image
uchar *raw = (uchar *)imageGray.data;
Image imageZbar(width, height, "Y800", raw, width * height);
// 扫描相应的图像imageZbar(imageZbar是zbar::Image类型,存储着读入的图像)
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;
} waitKey(); // 等待按下esc键,若需要延时1s则改用waitKey(1000); // 将图像中的数据置为0
imageZbar.set_data(NULL, );
system("pause");
}

结果如下:

条形码识别:

二维码识别:

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

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

处理的目标:

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

B.划定条码的范围

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

总体分为:

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

 /******************************************************
函数名称: Run
函数功能: 开始
传入参数:
返 回 值:
建立时间: 2018-05-19
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
void MyClass::Run(){
Mat image;
image = getGray(srcimage);//获取灰度图
image = getGass(image);//高斯平滑滤波
image = getSobel(image);//Sobel x—y梯度差
image = getBlur(image);//均值滤波除高频噪声
image = getThold(image);//二值化
image = getBys(image);//闭运算
image = getErode(image);//腐蚀
image = getDilate(image);//膨胀
image = getRect(image, srcimage);//获取ROI
imshow("最后的图", image);
Dis_code(image);
waitKey();
}

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

 /******************************************************
函数名称: getGray
函数功能: 灰度处理
传入参数: Mat image
返 回 值:
建立时间: 2018-05-19
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
Mat MyClass::getGray(Mat image, bool show){//show默认false 待定参数法
Mat cimage;
cvtColor(image, cimage, CV_RGBA2GRAY);
if (show)
imshow("灰度图", cimage);
return cimage;
}

处理结果:

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

 /******************************************************
函数名称: getGass
函数功能: 高斯滤波处理
传入参数: Mat image
返 回 值:
建立时间: 2018-05-19
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
Mat MyClass::getGass(Mat image, bool show){
Mat cimage;
GaussianBlur(image, cimage, Size(, ), );
if (show)
imshow("高斯滤波图", cimage);
return cimage;
}
 

处理结果:

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

 /******************************************************
函数名称: getSobel
函数功能: Sobel处理
传入参数: Mat image
返 回 值:
建立时间: 2018-05-19
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
Mat MyClass::getSobel(Mat image, bool show){
Mat cimageX16s, cimageY16s, imageSobelX, imageSobelY, out;
Sobel(image, cimageX16s, CV_16S, , , , , , );
Sobel(image, cimageY16s, CV_16S, , , , , , );
convertScaleAbs(cimageX16s, imageSobelX, , );
convertScaleAbs(cimageY16s, imageSobelY, , );
out = imageSobelX - imageSobelY;
if (show)
imshow("Sobelx-y差 图", out);
return out;
}
 

处理结果:

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

 /******************************************************
函数名称: getBlur
函数功能: 均值滤波处理
传入参数: Mat image
返 回 值:
建立时间: 2018-05-19
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
Mat MyClass::getBlur(Mat image, bool show){
Mat cimage;
blur(image, cimage, Size(, ));
if (show)
imshow("均值滤波图", cimage);
return cimage;
}
 

处理结果:

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

 /******************************************************
函数名称: getThold
函数功能: 二值化处理
传入参数: Mat image
返 回 值:
建立时间: 2018-05-19
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
Mat MyClass::getThold(Mat image, bool show){
Mat cimage;
threshold(image, cimage, , , CV_THRESH_BINARY);
if (show)
imshow("二值化图", cimage);
return cimage;
}
 

处理结果:

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

 /******************************************************
函数名称: getBys
函数功能: 闭运算处理
传入参数: Mat image
返 回 值:
建立时间: 2018-05-19
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
Mat MyClass::getBys(Mat image, bool show){
morphologyEx(image, image, MORPH_CLOSE, element);
if (show)
imshow("闭运算图", image);
return image;
}

处理结果:

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

 /******************************************************
函数名称: getErode
函数功能: 腐蚀处理
传入参数: Mat image
返 回 值:
建立时间: 2018-05-19
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
Mat MyClass::getErode(Mat image, bool show){
//Mat cimage;
erode(image, image, element);
if (show)
imshow("腐蚀图", image);
return image;
}
/******************************************************
函数名称: getDilate
函数功能: 膨胀处理
传入参数: Mat image
返 回 值:
建立时间: 2018-05-19
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
Mat MyClass::getDilate(Mat image, bool show){
for (int i = ; i < ; i++)
dilate(image, image, element);
if (show)
imshow("膨胀图", image);
return image;
}

处理结果:

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

 /******************************************************
函数名称: getRect
函数功能: 获取码的区域
传入参数: Mat image, Mat simage原图
返 回 值:
建立时间: 2018-05-19
修改时间:
建 立 人:
修 改 人:
其它说明:借鉴其他人进行改进
******************************************************/
Mat MyClass::getRect(Mat image, Mat simage, bool show){
vector<vector<Point>> contours;
vector<Vec4i> hiera;
Mat cimage;
findContours(image, contours, hiera, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
vector<float>contourArea;
for (int i = ; i < contours.size(); i++)
{
contourArea.push_back(cv::contourArea(contours[i]));
}
//找出面积最大的轮廓
double maxValue; Point maxLoc;
minMaxLoc(contourArea, NULL, &maxValue, NULL, &maxLoc);
//计算面积最大的轮廓的最小的外包矩形
RotatedRect minRect = minAreaRect(contours[maxLoc.x]);
//为了防止找错,要检查这个矩形的偏斜角度不能超标
//如果超标,那就是没找到
if (minRect.angle<2.0)
{
//找到了矩形的角度,但是这是一个旋转矩形,所以还要重新获得一个外包最小矩形
Rect myRect = boundingRect(contours[maxLoc.x]);
//把这个矩形在源图像中画出来
//rectangle(srcImage,myRect,Scalar(0,255,255),3,LINE_AA);
//看看显示效果,找的对不对
//imshow(windowNameString,srcImage);
//将扫描的图像裁剪下来,并保存为相应的结果,保留一些X方向的边界,所以对rect进行一定的扩张
myRect.x = myRect.x - (myRect.width / );
myRect.width = myRect.width*1.1;
Mat resultImage = Mat(srcimage, myRect);
return resultImage;
} for (int i = ; i<contours.size(); i++)
{
Rect rect = boundingRect((Mat)contours[i]);
//cimage = simage(rect);
rectangle(simage, rect, Scalar(), );
if (show)
imshow("转变图", simage);
}
return simage;
}

处理结果:

最后识别处理结果:

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

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

例图:

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

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

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

其中处理的步骤分为:

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

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

边缘检测:

特征轮廓检测

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

纠正旋转

提取ROI

识别

源码如下:

 /******************************************************
函数名称: QrRun
函数功能: 开始
传入参数:
返 回 值:
建立时间: 2018-05-19
修改时间:
建 立 人:
修 改 人:
其它说明:
******************************************************/
void MyClass::QrRun(){
RNG rng();
//imshow("原图", srcimage);
Mat src_all = srcimage.clone();
Mat src_gray;
//灰度处理
src_gray = getBlur(getGray(srcimage)); Scalar color = Scalar(, , );
Mat threshold_output;
vector<vector<Point> > contours, contours2;
vector<Vec4i> hierarchy;
Mat drawing = Mat::zeros(srcimage.size(), CV_8UC3);
Mat drawing2 = Mat::zeros(srcimage.size(), CV_8UC3);
Mat drawingAllContours = Mat::zeros(srcimage.size(), CV_8UC3); threshold_output = getThold(src_gray); findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_NONE, Point(, )); int c = , ic = , k = , area = ;
// 边缘检测
//通过黑色定位角作为父轮廓,有两个子轮廓的特点,筛选出三个定位角
int parentIdx = -;
for (int i = ; i< contours.size(); i++)
{
//画出所以轮廓图
drawContours(drawingAllContours, contours, parentIdx, CV_RGB(, , ), , );
if (hierarchy[i][] != - && ic == )
{
parentIdx = i;
ic++;
}
else if (hierarchy[i][] != -)
{
ic++;
}
else if (hierarchy[i][] == -)
{
ic = ;
parentIdx = -;
}
//特征轮廓检测 - 》
//有两个子轮廓
if (ic >= )
{
//保存找到的三个黑色定位角
contours2.push_back(contours[parentIdx]);
//画出三个黑色定位角的轮廓
drawContours(drawing, contours, parentIdx, CV_RGB(rng.uniform(, ), rng.uniform(, ), rng.uniform(, )), , );
ic = ;
parentIdx = -;
}
}
//提取特征点
//填充的方式画出黑色定位角的轮廓
for (int i = ; i<contours2.size(); i++)
drawContours(drawing2, contours2, i, CV_RGB(rng.uniform(, ), rng.uniform(, ), rng.uniform(, )), -, , hierarchy[k][], , Point()); //获取定位角的中心坐标
vector<Point> pointfind;
for (int i = ; i<contours2.size(); i++)
{
pointfind.push_back(Center_cal(contours2, i));
}
//排除干扰点
Mat dst;
Point point[]; double angle; Mat rot_mat;
///选择合适的点-核心筛选
if (pointfind.size()>){
double lengthA = , lengthB = ;
for (int i = ; i < pointfind.size(); i++){
for (int j = ; j < pointfind.size(); j++){
for (int k = ; k < pointfind.size(); k++){
if (i != j&&j != k&&i != k){
double dxa, dxb,dya,dyb;
double k1, k2, wa, wb;
dxa = pointfind[i].x - pointfind[j].x;
dxb = pointfind[i].x - pointfind[k].x;
dya = pointfind[i].y - pointfind[j].y;
dyb = pointfind[i].y - pointfind[k].y;
if (dxa == || dxb == )continue;
k1 = dya/dxa;
k2 = dyb/dxb ;
wa = sqrt(pow(dya, ) + pow(dya, ));
wb = sqrt(pow(dyb, ) + pow(dxb, ));
double anglea = abs(atan(k1) * / CV_PI) + abs(atan(k2) * / CV_PI);
if (int(anglea)>=&&int(anglea)<=&&wa<=lengthA&&wb<=lengthB){
lengthA = wa;
lengthB = wb;
point[] = pointfind[i];
point[] = pointfind[j];
point[] = pointfind[k];
}
}
}
}
}
}
else{
for (int i = ; i < ; i++){
point[i] = pointfind[i];
}
}
//绘制直角三角形
//计算轮廓的面积,计算定位角的面积,从而计算出边长
area = contourArea(contours2[]);
int area_side = cvRound(sqrt(double(area)));
for (int i = ; i < ; i++){
line(drawing2, point[i], point[(i + )%], color, area_side / , );
} //纠正旋转
//判断是否正对
if (!IsCorrect(point)){
//进入修正环节
double angle; Mat rot_mat;
int start = ;
for (int i = ; i < ; i++){
double k1, k2,kk;
k1 = (point[i].y - point[(i + ) % ].y) / (point[i].x - point[(i + ) % ].x);
k2 = (point[i].y - point[(i + ) % ].y) / (point[i].x - point[(i + ) % ].x);
kk = k1*k2;
if (k1*k2 <)
start = i;
}
double ax, ay, bx, by;
ax = point[(start + ) % ].x;
ay = point[(start + ) % ].y;
bx = point[(start + ) % ].x;
by = point[(start + ) % ].y;
Point2f center(abs(ax - bx) / , abs(ay -by)/ );
double dy = ay - by;
double dx = ax - bx;
double k3 = dy / dx;
angle =atan(k3) * / CV_PI;//转化角度
rot_mat = getRotationMatrix2D(center, angle, 1.0); warpAffine(src_all, dst, rot_mat, src_all.size(), , , );//旋转原图查看
warpAffine(drawing2, drawing2, rot_mat, src_all.size(), , , );//旋转连线图
warpAffine(src_all, src_all, rot_mat, src_all.size(), , , );//旋转原图 namedWindow("Dst");
imshow("Dst", dst);
} namedWindow("DrawingAllContours");
imshow("DrawingAllContours", drawingAllContours); namedWindow("Drawing2");
imshow("Drawing2", drawing2); namedWindow("Drawing");
imshow("Drawing", drawing); //提取ROI
//接下来要框出这整个二维码
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, , , THRESH_BINARY);
findContours(threshold_output_all, contours_all, hierarchy_all, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(, ));//RETR_EXTERNAL表示只寻找最外层轮廓 Point2f fourPoint2f[];
//求最小包围矩形
RotatedRect rectPoint = minAreaRect(contours_all[]);//pointfind.size()-3 //将rectPoint变量中存储的坐标值放到 fourPoint的数组中
rectPoint.points(fourPoint2f); int maxx = , maxy = , minx = , miny = ;
for (int i = ; i < ; i++)
{
if (maxx < fourPoint2f[i].x)maxx = fourPoint2f[i].x;
if (maxy < fourPoint2f[i].y)maxy = fourPoint2f[i].y;
if (minx > fourPoint2f[i].x)minx = fourPoint2f[i].x;
if (miny > fourPoint2f[i].y)miny = fourPoint2f[i].y;
line(src_all, fourPoint2f[i % ], fourPoint2f[(i + ) % ]
, Scalar(), );
}
namedWindow("Src_all");
///边际处理
int set_inter = ;
while (true)
{
minx -= set_inter;
miny -= set_inter;
maxx += set_inter;
maxy += set_inter;
if (maxx > srcimage.size().width || maxy > srcimage.size().height || minx < || miny < ){
minx += set_inter;
miny += set_inter;
maxx -= set_inter;
maxy -= set_inter;
set_inter--;
}
else
{
break;
}
}
imshow("Src_all", src_all(Rect(minx, miny, maxx - minx, maxy - miny)));//ROI
Mat fout = src_all(Rect(minx, miny, maxx - minx, maxy - miny));//ROI //识别
Dis_code(fout); waitKey();
destroyAllWindows();
}

由于在解码上是采用其他人的方法,存在解码问题。(到时候有机会自己再写下)

ps:目前的zbar不支持中文识别,但是zxing可以。所以借鉴本文的需要改进下识别的模块即可。

本文的不足之处:

这里还做了测试,对于旋转180°以上的二维码图片存在可能无法识别的问题。以及码眼为非正方形的也无法识别。

如需要源码请转移至码云:https://gitee.com/cjqbaba/MediaTest/tree/Code_Find进行源码克隆下载

如有问题请留言评论。转载请注明出处,谢谢。

基于opencv3.0和下的条形码与二维码识别的更多相关文章

  1. 实例源码--ZXing识别条形码和二维码识别源码

      下载源码 技术要点: 1.ZXing库的 使用 2.识别条形码和二 维码 3.自定义视图 4.源码带有非常详 细的中文注释 ...... 详细介绍: 1.ZXing库 ZXing是个很经典的条码/ ...

  2. (整理).net实现条形码与二维码

    本文由来源网络的知识点组合而成,感谢分享的作者,文章结尾处给出查询资料连接. 条形码(barcode)是将宽度不等的多个黑条和空白,按照一定的编码规则排列,用以表达一组信息的图形标识符.常见的条形码是 ...

  3. asp.net 生成、解析条形码和二维码

    原文 asp.net 生成.解析条形码和二维码 一.条形码 一维码,俗称条形码,广泛的用于电子工业等行业.比如我们常见的书籍背面就会有条形码,通过扫描枪等设备扫描就可以获得书籍的ISBN(Intern ...

  4. 有关python下二维码识别用法及识别率对比分析

    最近项目中用到二维码图片识别,在python下二维码识别,目前主要有三个模块:zbar .zbarlight.zxing. 1.三个模块的用法: #-*-coding=utf-8-*- import ...

  5. Opencv+Zbar二维码识别(标准条形码/二维码识别)

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

  6. Android之条形码、二维码扫描框架(非原创)

    文章大纲 一.条形码.二维码扫描框架介绍二.条形码.二维码的区别和组成结构介绍三.条形码.二维码扫描框架应用场景四.BGAQRCode-Android框架实战五.项目源码下载六.参考文章 一.条形码. ...

  7. Java 创建/识别条形码、二维码

    条形码(Barcode)是将宽度不等的多个黑条和空白,按照一定的编码规则排列,用以表达一组信息的图形标识符.常用于标示物品的生产国.制造厂家.商品名称.生产日期.图书分类号.邮件起止地点.类别.日期等 ...

  8. [转]用C#实现的条形码和二维码编码解码器

    条形码的标准: 条形码的标准有ENA条形码.UPC条形码.二五条形码.交叉二五条形码.库德巴条形码.三九条形码和128条形码等,而商品上最常使用的就是EAN商品条形码.EAN商品条形码亦称通用商品条形 ...

  9. 【转】Android平台下利用zxing实现二维码开发

    http://www.cnblogs.com/dolphin0520/p/3355728.html 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描 ...

随机推荐

  1. mysql 无法插入中文

    MySQL数据库默认编码已经是utf8了, default-character-set = utf8,可是向数据库中表中插入中文时,却老是出现 ....\xB5\xA5\xD1\xA1 for col ...

  2. 在Eclipse 中集成SVN

    在项目开发的过程中,我们需要用到版本控制工具,最常见的也就是SVN了,下面就来介绍最简单的一种在Elipse中集成svn工具. 第一步:下载 svn包,如site-1.6.5.zip或者site-1. ...

  3. 怎么分别javascript写在<head>里还是<body>里面?

    怎么分别javascript写在<head>里还是<body>里面? 具体哪些语句写在<body>里,哪些语句写在<head>里 满意答案 BeginN ...

  4. 读JVM相关的一些笔记

    1.JVM的运行模式 vm一般有两种运行模式,client和server(JDK 7 后有第三种 Tiered server,后续会涉及到). client : 启动快,内存占用少,JIT编译器生成代 ...

  5. 《你必须掌握的Entity Framework 6.x与Core 2.0》书籍出版

    前言 到目前为止写过刚好两百来篇博客,看过我博客的读者应该大概知道我每一篇博客都沿袭着一贯的套路,从前言到话题最终到总结,本文依然是一如既往的套路,但是不是介绍技术,也可说是介绍技术,不过是介绍书中的 ...

  6. Day2_and_Day3 文件操作

    文件修改操作: 文件的修改操作:文件并没有修改操作,实际是将一个编写的新文件覆盖了原有的文件 替换文件中的某个内容: with open('old.txt','r',encoding='utf-8') ...

  7. .net c#获取自定义Attribute

    前言: 在c#开发中,有时候我们需要读取 Attribute中的信息(关于Attribute , 我自己把他理解成一个可以为类,属性标记的东西,这个标记可以为你提供一些关于类,方法,属性的额外信息) ...

  8. Mybatis 系列4

    上篇系列3中 介绍了properties与environments, 本篇继续讲剩下的配置节点之一:typeAliases. typeAliases节点主要用来设置别名,其实这是挺好用的一个功能, 通 ...

  9. Drupal7.8的安装注意的问题

    首先推荐在安装之前,首先阅读一下官方的Installation guide ,说实话话,这玩意安装没有Joomla安装那么平滑,大多数问题,guide都会提到.我是先安装后看的这个guide,比较折腾 ...

  10. php使用http_build_query,parse_url,parse_str创建与解析url详解

    1.http_build_query string http_build_query ( mixed $query_data [, string $numeric_prefix [, string $ ...