2014-03-20 14:52:42 wolfseek 阅读数 6593

一. 条码图像处理过程

        图像的大致处理流程如下:

         1. 将彩色图转变成灰度图

         2. 对灰度图均衡化

         3. 将灰度图转换成二值图

         4. 对二值图进行腐蚀

         5. 识别边界检测轮廓

         6. 对每一个轮廓包围的区域进行特征识别,判断是否是条码区域。

         7. 对条码区域测量解码。

 

 

二.代码解读

 

bool TestBarCode()

{

         InitCodeMap();

 

    IplImage* src = cvLoadImage(strPictureName,0);                  //装载图像                                                               

    if( !src )

         {

                   printf("打开文件失败,请重新打开");                  //读取失败返回

                   return false;

         }

        

         cvNamedWindow("src",1);

         cvShowImage("src",src);

 

        

    IplImage * dst = cvCreateImage( cvGetSize(src), 8, 1 );         //创建图像

    IplImage * dst1 = cvCreateImage( cvGetSize(src), 8, 1 );//创建图像

 

         //进行图像平滑,并转成灰度图像

         cvSmooth(src,dst1,CV_BLUR,3, 3);  

         Process(dst1);    //处理的核心在这

 

    cvWaitKey(0);                      //等待按键

 

         //释放图像分配的内存

         cvReleaseImage(&src);    

         cvReleaseImage(&dst);             

         cvReleaseImage(&dst1);

 

         cvDestroyWindow("src");

         cvDestroyWindow("dest");

         cvDestroyWindow("cpImg");

 

         return true;

}

 

这段代码是处理的主代码,对图片载入并平滑处理。然后调用了Process函数做进一步处理。

 

//型体分割,获取感兴趣的区域

void Process(IplImage * pImg)

{

         //转换成二值图

         IplImage * dst = cvCreateImage( cvGetSize(pImg), 8, 1 );

         cvThreshold(pImg,dst,100,200,CV_THRESH_BINARY_INV);//将灰度图转成二值图

         cvNamedWindow("ThImage",1);

         cvShowImage("ThImage",dst);

 

         //膨胀

         IplImage * dst2 = cvCreateImage( cvGetSize(pImg), 8, 1 );

         IplConvKernel * element = cvCreateStructuringElementEx( 5, 5, 1, 1, CV_SHAPE_ELLIPSE, 0);

         cvDilate(dst,dst2,element,3);//图像膨胀

 

         cvNamedWindow("DilateImage",1);

         cvShowImage("DilateImage",dst2);

 

         //检测轮廓

         CvMemStorage * storage = cvCreateMemStorage(0);

    CvSeq * contour = 0;

    int n = cvFindContours( dst2, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

        

         double maxArea = 20;

         IplImage* pContourImg = cvCreateImage(cvGetSize(dst2), IPL_DEPTH_8U, 3);

         //初始化为黑色

         cvZero(pContourImg);

 

         int k = 0;

         while(contour)

         {    

                   double area=fabs(cvContourArea(contour, CV_WHOLE_SEQ)); //求轮廓包含的面积   

                   if(area > 100) //当面积小于100时的忽略

                   {

                            if(k++ < 8)

                            {  

                                     cvDrawContours(pContourImg,contour,cvScalarAll(255),cvScalarAll(0),0,CV_FILLED);

                                     CvRect rect = cvBoundingRect(contour,1); //获取外接矩形

                                     cvRectangleR(pContourImg,rect,CV_RGB(0,255,0));  //绘制外接矩形

 

                                     CvBox2D box = cvMinAreaRect2(contour,NULL);  //求取轮廓最小的外接矩形

                                     IplImage * cpImg = CreateAndCopyBox2D(dst,box);

                                     if(cpImg)

                                     {

                                               if(FutrueFind(cpImg))

                                               {

                                                        string str = DecodeImage(cpImg);

                                                        cvNamedWindow("cpImg",1);

                                                        cvShowImage("cpImg",cpImg);

                                               }

                                               else cvReleaseImage(&cpImg);

                                                       

                                     }

                            }  

                   }

                   contour=contour->h_next;

         }

         cvReleaseMemStorage(&storage);

}

 

Process是核心处理代码,首先对图像进行了二值化,这样能去除更多的干扰。二值化是使用了CV_THRESH_BINARY_INV参数,对图像中黑白色反转。这样条码部分呈了白色。然后对其膨胀,膨胀的目的是将条码说在的整个区域编程白色,之后的轮廓识别能将其划分在一个轮廓里。

cvFindContours查找了轮廓,将其保存在contour列表之中。之后就是遍历整个列表,遍历时,首先使用cvMinAreaRect2对每一个轮廓求取其最小外接矩形,他返回一个CvBox2D数据,其中包含有矩形的大小,中心点坐标,还有一个角度。利用这个角度可以可以知道条码型位信息,这个角度就是对裁剪后的感兴趣区域旋转的角度。

CreateAndCopyBox2D对遍历的每个区域进行了裁剪和旋转。这样就得到了型位是正位的区域图片,之后要做的事情就是识别图片特征,判断是否是条码区域,若是就会对图片的解码。

 

FutrueFind承担了图片特征识别的任务。

//特征识别,找到条码区域

bool FutrueFind(IplImage * pImg)

{

         double max = 0;

         double min = 0;

 

         vector<double> ds;

         HistY(pImg,ds);

         double avgY = DataAvg(ds);

        

         ds.clear();

         HistX(pImg,ds);

         double avgX = DataAvg(ds);

 

         if(avgX/avgY > 5)return true;

 

         return false;

}

 

特征识别是一个图形处理系统的核心技术。这里使用了简单的特征识别,目前还不是很健壮。在一维条码的x,y方向上强度特征会有所不同,首先统计出X和Y方向的强度值,然后求取相邻强度值差值的绝对值,在对这些值求平均值。这样得到了avgY和avgX,经多个条码观察发现avgX都在avgY的5倍以上。

 

 

string DecodeImage(IplImage * pImg)

{

         vector<int> ds;

         double color = 0;

         int h = pImg->height/2;

         for(int i = 0; i<pImg->width; i++)

         {

                   double d = cvGet2D(pImg,h,i).val[0];

                   if(0 == i)color = d;

                   else

                   {

                            if(abs(color -d) > 100)

                            {

                                     ds.push_back(i);

                            }

 

                            color = d;

                   }

         }

 

         printf("decode size :%d\n",ds.size());

 

         //计算宽度系列

         vector<int> wids;

         double w = 0;

         for(int i = 0; i<ds.size(); i++)

         {

                   double d = ds[i];

                   if(0== i)w = d;

                   else

                   {

                            wids.push_back(d - w);

                            w = d;

                   }

         }

         wids.pop_back();

         printf("width size :%d\n",wids.size());

 

         int min = FindMin(wids);

         vector<int> tgs;

         DevTh(wids,tgs,min);

 

         string code = Getcode(tgs);

 

         printf("code :%s\n",code.c_str());

         return code;

}

 

DecodeImage函数解码条码,他基于像素为单位对条码宽度测量,再找出最小宽度,之后再将其转换成条码识别的标注序列,和编码表比较获得条码值。

2016-08-23 17:37:02 Jacky_Ponder 阅读数 2518

1、步骤

(1) 提取图像中条码区

(2) 识别条码

2、示例

2.1、一维码的识别


图1.1原图


图1.2提取条码区


图1.3识别结果

 

2.2、二维码的识别


图2.1原图


图2.2提取条码区


图2.3识别结果


作者:Jacky_Ponder,转载或分享请注明出处。QQ:2814152689

2009-10-29 15:23:00 lulu831110 阅读数 9275

 论文:

《一维条形码识别系统的设计与实现》钟书成 成都信息工程学院软件工程系

 

 

 

 

 

1 中值滤波去噪

       中值滤波是一种非线形信号处理方法,在这里由于条码图片是列的形式,所以采用改进的5个点采样窗口,取一列上的5个点。扫描整幅图片,对窗口内的像素点按像素值大小排序,最后取中间值代替窗口内所有像素点的原始像素值。

 2 二值化处理

       这其中有三个步骤,首先,对图片的灰度值进行统计,其次再求阈值,最后根据求得的阈值,将图片转换为像素值只有01的黑白图片,以方便进一步分析。

       阈值的选取,采用的是大律法。其原理为,取某个灰度值,以它为分界将图像分为灰度值大小两类,分别计算这两类中的像素点数及灰度平均值。然后计算它们的类间方差,取方差最大值对应的灰度为阈值。

 

3 边缘检测

       这里的边缘检测,不是检测图片的边缘,而是查找一块比较适合条码识别的区域,并将这块区域的上下边界回传给它的主调函数。

l  采用的方法是依次扫描图片的每一行,求取每一行中的黑点个数

l  统计有黑点的行的数量

l  求出图片中所有的黑点数

l  求出平均每行的黑点数(总黑点数除以有黑点的行数)

l  判断一个行是否适合于识别,判定的规范是:如果一行和它的下一行比较相差的黑点个数在5个以内,而且这一行的黑点个数比每行的平均数的75%多,则认为这一行可以用于识别。

l  找到条形码用于识别的上下边缘,通过以上方法,扫描图片,得到每行是否适合用于识别(用01标记)。寻找最佳的识别范围则是通过扫描图片的每一行,查找一定范围内的可以用于识别的行数大于某个值时,这个范围即可以作为最佳的识别区域。如果没有找到,则认为图片严重受损,不能用于识别。 

 

4 条形码识别

       条形码识别过程包括:垂直投影、计算宽度、码字识别等。

 

 

 

 

l 垂直投影:同水平投影一样,先求每一列的黑点个数(在已经找到的上下边界范围内),总黑点数,统计共有多少有黑点的列,求出每列平均有多少个黑点。判定是否为条的规范是:如果这一列的黑点数大于平均数的75%,则认为这列像素是属于条的范围,同时将这列置1,否则置为0

l  求各个条空的宽度:在垂直投影的基础上根据条空的边沿的变化来求。

l 识别码字,根据编码原理的逆过程。

 

缺点:

在图像处理中大量使用到复杂的循环处理,影响了程序的执行效率。

其次在图像特别模糊或背景色干扰太多的时候,识别效果不好。

未实现倾斜校正,所以系统的通用性不好,当图片的倾斜角度太大的时候,系统无法识别。

 

 

 

 

2015-07-16 06:22:00 weixin_33898876 阅读数 27
一维二维码的提取、识别和产生
 
零、相关说明:
    在“jsxyhelu.cnblogs.com/机器视觉”栏目主要介绍和图像处理和机器视觉相关的的成套的解决方案、思路和软件集。希望能够为大家在实际工作中解决具体问题提供一些帮助。
一、背景资料:
      现在一维码、二维码广泛使用于工业各方面。很多和图像处理相关的需求也有所涉及。这里也看过一些论文,里面有一些方法还是art-of-air的,也有一些具备一定参考价值。这里只是谈一谈一般情况下的提取和识别。对于特殊情况,肯定要特殊对待,其实本身是一个增强的处理。在条码的处理和产生部分,主要借助了zxing
      
       那么,问题可以分为三个
      1.1如何从图片中获取条码区域并且提取条码(由于缺乏二维码图片所有这里只分析一维码,以后有了二维码实际拍摄的图片再补上);
      1.2如何识别条码;
      1.3如何参数条码
二、主要内容:
      2.1如何从图片中获取条码区域并且提取条码
      条码(也就是一维码)在设计的时候就被设计成纵向冗余的。提取就可以借助这一特性。
      比如这样的图像,经过canny->dilate->erode就可以得到这样的结果

    Mat cannyClone= Mat::zeros(Size(gray.cols,gray.rows),gray.type());
    Canny(gray,canny,100,255);
    Mat element = getStructuringElement(MORPH_ELLIPSE,Size(7,3));
    morphologyEx(canny,canny,CV_MOP_DILATE,element);
    morphologyEx(canny,canny,CV_MOP_ERODE ,element);
    imwrite("canny.jpg",canny);
      特征已经比较明显了,紧接着选取所有轮廓中最大的,并且将这个最大轮廓的外界长方形整个的扣出来,一般情况下就能够得到正确的结果:

findContours(canny,contours,CV_RETR_TREE,CV_CHAIN_APPROX_NONE);
    for (int i=0;i<contours.size();i++)
    {
        int itmp =  contourArea(contours[i]);
        if (imaxcontour < itmp )
        {
            imax = i;
            imaxcontour = itmp;
        }
    }
    //找到轮廓的处理
    Rect boundRect;//最小外接矩形
    drawContours(cannyClone,contours,imax,Scalar(255),-1);
    boundRect = boundingRect(Mat(contours[imax]));
    Mat srcRoi = src(boundRect);
    imwrite("barcode.jpg",srcRoi);
 
2.2如何识别条码;
     借助zxing的相关方法,这里的解法是一维/二维通用的 
            string opFilePath = System.Environment.CurrentDirectory + "\\result.jpg";
            MultiFormatReader mutiReader = new com.google.zxing.MultiFormatReader();
            Bitmap img = (Bitmap)Bitmap.FromFile(opFilePath);
            if (img == null)
                return;
            LuminanceSource ls = new RGBLuminanceSource(img, img.Width, img.Height);
            BinaryBitmap bb = new BinaryBitmap(new com.google.zxing.common.HybridBinarizer(ls));
            Result r = mutiReader.decode(bb);
            tb.Text = r.Text;
2.2如何产生条码;
      同样是借助zxing的相关方法,这个具体看代码和相关资料。
       
三、遗留问题:
      由于zxing没有从源代码上吸收,所以无法保证算法的强壮性和可移植性,许多时候最终平台的选择还要考虑到zxing的本身的问题;
      同时,对于一些可能比较复杂的情况,可能这里的比较简单的获取方法还有问题,还需要具体问题具体对待。
 
 
 





目前方向:图像拼接融合、图像识别 联系方式:jsxyhelu@foxmail.com
2015-08-18 17:33:59 bytekiller 阅读数 1468

这是一个基于ZBar库开发的windows条码读取识别MFC应用程序。支持一维和二维(QR)条形码。条形码通过摄像头读取,识别有可把相关字符自动输入到活动窗口,或者复制到剪贴板。如果配合手机上的IP Webcam应用,也可以利用手机的摄像头作为读取器。

关于摄像头读取,画面预览以及条码识别的功能被打包成一个控件。方便整合到自己的应用程序中。

ZBar库提供了一个示范程序zbarcam.exe。但是这个程序使用起来有些不便。

(1)示范程序是基于控制台的,其识别输出到标准输出。对于将条形码作为输入设备的应用不方便。因此在barcoder reader中增加了自动输入和复制到剪贴板的功能。

(2)示范程序不能对预览画面调整。特别是对于使用笔记本电脑上的摄像头,由于面对摄像头,预览画面左右镜像后操作更顺手。因此在barcode reader中增加预览画面的旋转和镜像功能。

(3)在MFP应用中,提供更方便使用的控件。

基于以上原因开发barcode reader应用程序。同时在开发过程中,学习了以下知识,将在后续文章中陆续介绍。

图像处理相关:opencv库和zbar库的使用,在MFC中显示opencv的图像;图形的仿射变换原理及优化;

Windows开发相关:全局模拟键盘输入;全局快捷键的注册和响应;系统托盘操作;


源代码和可执行文件开在资源中下载:

源代码:http://download.csdn.net/detail/bytekiller/9020599

编译方法:需要zbar和opencv库

(1)解压到硬盘。
(2)进入./tool/目录
(3)运行auto_config.ps1 -path_zbar"<zbar dir>" -path_opencv"<opencv dir>"
(4)进入./application/image_process/目录
(5)打开barcode_reader.sln
(6)编译,运行

可执执行文件:http://download.csdn.net/detail/bytekiller/9020641

运行环境:windows 7, 32bit, 需要vc 2008 sp1 redistribution支持

使用方法:

运行barcode_reader.exe,右击标题栏出现系统菜单,在config/select camera中选择摄像头。
关闭窗口有最小化到系统托盘,可右击托盘图标唤醒,也可用快捷键唤醒。
ctrl+alt+B:选择第一个摄像头,ctrl+alt+C:第二个摄像头,以此类推。
ctrl+alt+A:关闭摄像头。
菜单config/auto input有效:识别到条形码后,自动输入到活动窗口。
菜单config/copy to clipboard有效:识别到条形码后,自动复制到剪贴板。




图像处理的前景

阅读数 1883

没有更多推荐了,返回首页