OPENCV条形码检测与识别

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

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

 

要定位首先要检视这种条形码的特色,这种图像在X方向上的梯度确定很明显,同时,Y方向的梯度就没这么明显,因此第一步,咱们应该将图像的灰度图像分别计算梯度,用X方向梯度减去Y方向梯度,这样能够保留X方向特征而且去除Y方向的干扰,处理以后图像以下所示数据库

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

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

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

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

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

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

接下来条形码识别,可使用zbar识别库,库的简介就不说了,能够本身去官网下载,安装时候记得选上第三个选项,不然没有头文件.blog

安装完成后,到安装目录,将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[100];
    char windowNameString[50];
    char resultFileNameSring[100];
    Mat srcImage,grayImage,blurImage,thresholdImage,gradientXImage,gradientYImage,gradientImage,morphImage;
    for (int fileCount = 1;fileCount < 8;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 -1;
        }
        //图像转换为灰度图像
        if(srcImage.channels() == 3)
        {
            cvtColor(srcImage,grayImage,CV_RGB2GRAY);
        }
        else
        {
            grayImage = srcImage.clone();
        }
        //创建图像的梯度幅值
        Scharr(grayImage,gradientXImage,CV_32F,1,0);
        Scharr(grayImage,gradientYImage,CV_32F,0,1);
        //由于咱们须要的条形码在须要X方向水平,因此更多的关注X方向的梯度幅值,而省略掉Y方向的梯度幅值
        subtract(gradientXImage,gradientYImage,gradientImage);
        //归一化为八位图像
        convertScaleAbs(gradientImage,gradientImage);
        //看看获得的梯度图像是什么样子
        //imshow(windowNameString,gradientImage);
        //对图片进行相应的模糊化,使一些噪点消除
        blur(gradientImage,blurImage,Size(9,9));
        //模糊化之后进行阈值化,获得到对应的黑白二值化图像,二值化的阈值能够根据实际状况调整
        threshold(blurImage,thresholdImage,210,255,THRESH_BINARY);
        //看看二值化图像
        //imshow(windowNameString,thresholdImage);
        //二值化之后的图像,条形码之间的黑白没有链接起来,就要进行形态学运算,消除缝隙,至关于小型的黑洞,选择闭运算
        //由于是长条之间的缝隙,因此须要选择宽度大于长度
        Mat kernel = getStructuringElement(MORPH_RECT,Size(21,7));
        morphologyEx(thresholdImage,morphImage,MORPH_CLOSE,kernel);
        //看看形态学操做之后的图像
        //imshow(windowNameString,morphImage);
        //如今要让条形码区域链接在一块儿,因此选择膨胀腐蚀,并且为了保持图形大小基本不变,应该使用相同次数的膨胀腐蚀
        //先腐蚀,让其余区域的亮的地方变少最好是消除,而后膨胀回来,消除干扰,迭代次数根据实际状况选择
        erode(morphImage, morphImage, getStructuringElement(MORPH_RECT, Size(3,3)),Point(-1,-1),4);
        dilate(morphImage, morphImage, getStructuringElement(MORPH_RECT, Size(3,3)),Point(-1,-1),4);
        //看看形态学操做之后的图像
        //imshow(windowNameString,morphImage);
        vector<vector<Point2i>>contours;
        vector<float>contourArea;
        //接下来对目标轮廓进行查找,目标是为了计算图像面积
        findContours(morphImage,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
        //计算轮廓的面积而且存放
        for(int i = 0; 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/20);
             myRect.width = myRect.width*1.1;
            Mat resultImage = Mat(srcImage,myRect);
            if(!imwrite(resultFileNameSring,resultImage))
            {
                cout<<"file save error!"<<endl;
                return -2;
            }
        }
    }
    //检测到了以后进行条形码识别
    FileStorage file("F:\\opencv\\条形码检测与识别\\result.xml",FileStorage::WRITE);
    for (int fileCount = 1;fileCount < 8;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,1);
            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,0);
                //xml文件写入

            }
        }
    }
    waitKey(0);
    return 1;
}

资源以下

http://download.csdn.net/detail/dengrengong/9461797
相关文章
相关标签/搜索