条形码是当前超市和部分工厂使用比较普遍的物品,产品标识技术,使用摄像头检测一张图片的条形码包含有两个步骤,第一是定位条形码的位置,定位之后剪切出条形码,并且识别出条形码对应的字符串,然后就可以调用网络,数据库等手段快速进行后续处理.

条形码识别要考虑到条形码的特点,本文针对的是条形码在图片中的位置相对垂直,没有各种倾斜的那种条形码,如下图所示

要定位首先要检视这种条形码的特点,这种图像在X方向上的梯度肯定很明显,同时,Y方向的梯度就没这么明显,所以第一步,我们应该将图像的灰度图像分别计算梯度,用X方向梯度减去Y方向梯度,这样可以保留X方向特征并且去除Y方向的干扰,处理之后图像如下所示

可以看到,二维码对一维码的定位形成了干扰,但是二维码的空间漏洞相对一维码多很懂,于是我们考虑进行一次模糊并且二值化,看能不能有所效果,如下(记得调整相应的模糊化参数和阈值参数,得到相对最好的结果)

有一定的效果,但是此时又出现问题条形码出现了黑色的缝隙,不利于定位完整区域,这个时候要进行一些形态学操作,去除黑色缝隙,我们选择闭运算,算子根据缝隙的情况,宽度大于高度,矩形缝隙.处理以后的结果.

效果可以,又出现问题,二维码的区域连着,还是面积很大,对后面我们算区域面积依然有影响,但是我们观测二维码的连接区域明显要比一维码的连接区域要细很多,也就是说,我们可以很快的腐蚀断二维码的连接,同时还保持一维码的连接,然后在膨胀回来,二维码的连接断开就应该不会有这个大块的区域连着了,注意,膨胀和腐蚀的次数应当是一致的,保证得到结果区域的准确.我选择膨胀腐蚀四次,先膨胀断开二维码连接,最后的结果显示如下

此时,二维码的影响就基本没有了,现在我们只需要先查找轮廓,然后计算图像中每个轮廓的面积,选出面积最大的那个轮廓,计算这个轮廓的最小外包矩形,就能找到相应的图像区域了.这样操作的结果和切分出来的条形码如下所示

到目前为止,我们已经完成了条形码的位置定位,并且剪切出了条形码的团,接下里对这个图案进行识别,识别之前,总结一下

  1. 形态学梯度运算,忽略Y方向梯度,着眼于X方向梯度
  2. 图像模糊化,为了便于后期的图像连接
  3. 图像求阈值,加速算法处理,并合理使用模糊化的效果
  4. 形态学去除黑洞,闭运算
  5. 膨胀腐蚀,断开二维码连接
  6. 查找轮廓,计算轮廓最大面积,拟合轮廓矩形,得到最终结果

接下来条形码识别,可以使用zbar识别库,库的简介就不说了,可以自己去官网下载,安装时候记得选上第三个选项,否则没有头文件.

安装完成后,到安装目录,将bin目录加入环境变量,在VS中VC++目录的include中加入头文件地址,lib地址,并加入lib名称(连接器-输入-附加依赖项),然后就可以使用了,具体使用查看下面的代码,结果如下

代码如下

#include <opencv2/opencv.hpp>
#include <iostream>
#include <zbar.h> using namespace cv;
using namespace std;
using namespace zbar; int main(int argc,char* argv[])
{
char fileNameString[];
char windowNameString[];
char resultFileNameSring[];
Mat srcImage,grayImage,blurImage,thresholdImage,gradientXImage,gradientYImage,gradientImage,morphImage;
for (int fileCount = ;fileCount < ;fileCount++)
{
sprintf(fileNameString,"F:\\opencv\\条形码检测与识别\\barcode_0%d.jpg",fileCount);
sprintf(windowNameString,"result 0%d",fileCount);
sprintf(resultFileNameSring,"F:\\opencv\\条形码检测与识别\\barcodeResult_0%d.jpg",fileCount);
//读取图像
srcImage = imread(fileNameString);
if(srcImage.empty())
{
cout<<"image file read error"<<endl; return -;
}
//图像转换为灰度图像
if(srcImage.channels() == )
{
cvtColor(srcImage,grayImage,CV_RGB2GRAY);
}
else
{
grayImage = srcImage.clone();
}
//建立图像的梯度幅值
Scharr(grayImage,gradientXImage,CV_32F,,);
Scharr(grayImage,gradientYImage,CV_32F,,);
//因为我们需要的条形码在需要X方向水平,所以更多的关注X方向的梯度幅值,而省略掉Y方向的梯度幅值
subtract(gradientXImage,gradientYImage,gradientImage);
//归一化为八位图像
convertScaleAbs(gradientImage,gradientImage);
//看看得到的梯度图像是什么样子
//imshow(windowNameString,gradientImage);
//对图片进行相应的模糊化,使一些噪点消除
blur(gradientImage,blurImage,Size(,));
//模糊化以后进行阈值化,得到到对应的黑白二值化图像,二值化的阈值可以根据实际情况调整
threshold(blurImage,thresholdImage,,,THRESH_BINARY);
//看看二值化图像
//imshow(windowNameString,thresholdImage);
//二值化以后的图像,条形码之间的黑白没有连接起来,就要进行形态学运算,消除缝隙,相当于小型的黑洞,选择闭运算
//因为是长条之间的缝隙,所以需要选择宽度大于长度
Mat kernel = getStructuringElement(MORPH_RECT,Size(,));
morphologyEx(thresholdImage,morphImage,MORPH_CLOSE,kernel);
//看看形态学操作以后的图像
//imshow(windowNameString,morphImage);
//现在要让条形码区域连接在一起,所以选择膨胀腐蚀,而且为了保持图形大小基本不变,应该使用相同次数的膨胀腐蚀
//先腐蚀,让其他区域的亮的地方变少最好是消除,然后膨胀回来,消除干扰,迭代次数根据实际情况选择
erode(morphImage, morphImage, getStructuringElement(MORPH_RECT, Size(,)),Point(-,-),);
dilate(morphImage, morphImage, getStructuringElement(MORPH_RECT, Size(,)),Point(-,-),);
//看看形态学操作以后的图像
//imshow(windowNameString,morphImage);
vector<vector<Point2i>>contours;
vector<float>contourArea;
//接下来对目标轮廓进行查找,目标是为了计算图像面积
findContours(morphImage,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
//计算轮廓的面积并且存放
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);
if(!imwrite(resultFileNameSring,resultImage))
{
cout<<"file save error!"<<endl;
return -;
}
}
}
//检测到了之后进行条形码识别
FileStorage file("F:\\opencv\\条形码检测与识别\\result.xml",FileStorage::WRITE);
for (int fileCount = ;fileCount < ;fileCount++)
{
sprintf(resultFileNameSring,"F:\\opencv\\条形码检测与识别\\barcodeResult_0%d.jpg",fileCount);
sprintf(windowNameString,"result 0%d",fileCount);
Mat result = imread(resultFileNameSring);
if(!result.empty())
{
//现在开始识别
cvtColor(result,grayImage,CV_RGB2GRAY);
int width = grayImage.cols; // extract dimensions
int height = grayImage.rows;
Image image(width,height,"Y800",grayImage.data,width*height);
ImageScanner scanner;
scanner.set_config(ZBAR_NONE,ZBAR_CFG_ENABLE,);
int n = scanner.scan(image);
for (Image::SymbolIterator symbol = image.symbol_begin(); symbol != image.symbol_end();++symbol)
{
cout <<"pic name:\t"<<resultFileNameSring<<endl<<"code type:\t"<<symbol->get_type_name()<<endl<<\
"decode string:\t"<<symbol->get_data()<<endl;
image.set_data(NULL,);
//xml文件写入 }
}
}
waitKey();
return ;
}

资源如下

http://download.csdn.net/detail/dengrengong/9461797

OPENCV条形码检测与识别的更多相关文章

  1. 基于OpenCv的人脸检测、识别系统学习制作笔记之三

    1.在windows下编写人脸检测.识别系统.目前已完成:可利用摄像头提取图像,并将人脸检测出来,未进行识别. 2.在linux下进行编译在windows环境下已经能运行的代码. 为此进行了linux ...

  2. OpenCV 学习笔记 07 目标检测与识别

    目标检测与识别是计算机视觉中最常见的挑战之一.属于高级主题. 本章节将扩展目标检测的概念,首先探讨人脸识别技术,然后将该技术应用到显示生活中的各种目标检测. 1 目标检测与识别技术 为了与OpenCV ...

  3. OpenCV 学习笔记 05 人脸检测和识别

    本节将介绍 Haar 级联分类器,通过对比分析相邻图像区域来判断给定图像或子图像与已知对象是否匹配. 本章将考虑如何将多个  Haar 级联分类器构成一个层次结构,即一个分类器能识别整体区域(如人脸) ...

  4. keras系列︱人脸表情分类与识别:opencv人脸检测+Keras情绪分类(四)

    引自:http://blog.csdn.net/sinat_26917383/article/details/72885715 人脸识别热门,表情识别更加.但是表情识别很难,因为人脸的微表情很多,本节 ...

  5. 基于Opencv的人脸检测及识别

    一.实验目的:我这里完成的是,将8张人脸图片(4组,每组两张)存入库中,选取1张图片,程序识别出与其匹配的另一张. 这里介绍分三个步骤完成该工作,①程序读取摄像头.拍照 ②程序从电脑文档中读取图片   ...

  6. 第十九节、基于传统图像处理的目标检测与识别(词袋模型BOW+SVM附代码)

    在上一节.我们已经介绍了使用HOG和SVM实现目标检测和识别,这一节我们将介绍使用词袋模型BOW和SVM实现目标检测和识别. 一 词袋介绍 词袋模型(Bag-Of-Word)的概念最初不是针对计算机视 ...

  7. 第十八节、基于传统图像处理的目标检测与识别(HOG+SVM附代码)

    其实在深度学习中我们已经介绍了目标检测和目标识别的概念.为了照顾一些没有学过深度学习的童鞋,这里我重新说明一次:目标检测是用来确定图像上某个区域是否有我们要识别的对象,目标识别是用来判断图片上这个对象 ...

  8. opencv人脸检测,旋转处理

    年会签到,拍自己的大头照,有的人可能会拍成横向的,需要旋转,用人脸检测并修正它(图片). 1. 无脑检测步骤为: 1. opencv 读取图片,灰度转换 2. 使用CascadeClassifier( ...

  9. 人脸检测及识别python实现系列(6)——终篇:从实时视频流识别出“我”

    人脸检测及识别python实现系列(6)——终篇:从实时视频流识别出“我” 终于到了最后一步,激动时刻就要来临了,先平复一下心情,把剩下的代码加上,首先是为Model类增加一个预测函数: #识别人脸 ...

随机推荐

  1. struts2修改文件上传的大小

    那天写了一个web上传图片的程序,明明修改了上传文件的默认值(2M),可就是一直没有起作用 <action name="fileupload" class="upl ...

  2. Html的第一次小结

    一 Html的文档结构  (1) <html> 标记html文件的头标记,没有什么实质性的作用,但是却是必不可少的  (2) <head> 放置html文件信息.如css的一些 ...

  3. 1988: Sn 爆long long 的处理方法

    题目描述 给你两个数 n, p(0 < n,p <= 10^15); a1 = 1;  a2 = 1+2;  a3 = 1+2+3;  ... an = 1+2+3+...+n    Sn ...

  4. eclipse的调试方法的简单介绍

    声明:本文不是自己 作为编程人员,程序的调试是一项基本功.在不使用IDE的时候,程序的调试多数是通过日志或者输入语句(System.out.println)的方式.可以把程序运行的轨迹或者程序运行过程 ...

  5. 关于js向jsp中传输中文乱码问题

    最近做项目遇到的js向jsp中传中文结果是乱码,不知道是否是我换了用eclipse的原因还是什么,以前用的MyEclipse反正最后解决办法如下: 1.把js文件复制到桌面: 2.打开文件并用另存为u ...

  6. Activity与Activity之间的传值

    //----------Activity1中的布局--------------------------------- <LinearLayout xmlns:android="http ...

  7. linux和windows之间上传 下载文件 非ftp方式

    用 命令 rz   上传   sz 下载  文件夹加上 -r  rz上传替换时用 -y   谁用谁知道 两台linux传 : scp -r  文件夹  username@ip:路径  (如果传输文件就 ...

  8. Dev之ChartControl控件(一)

    ChartControl控件主要包括Chart Title,Legend,Annotations,Diagram,Series五部分:如图: 1.  用RangeControl控件控制ChartCon ...

  9. KNN邻近分类算法

    K邻近(k-Nearest Neighbor,KNN)分类算法是最简单的机器学习算法了.它采用测量不同特征值之间的距离方法进行分类.它的思想很简单:计算一个点A与其他所有点之间的距离,取出与该点最近的 ...

  10. RedHat Enterprise Linux AS4&5 安装gcc过程

    三.Gcc安装方法(redhat 4): 一.安装步骤 1.使用which gcc命令查看gcc是否安装安装 2.如若没有安装则下载如下安装包,所需安装包如下 一共需要拷贝以下五个安装包: binut ...