基于opencv的一维条码识别

一. 条码图像处理过程

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

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

你可能感兴趣的:(机器智能,opencv,软件开发)