一. 条码图像处理过程

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

         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
int  n = cvFindContours(  dst2, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
         
double  maxArea
IplImage*  pContourImg = cvCreateImage(cvGetSize(dst2), IPL_DEPTH_8U, 3); 
//初始化为黑色
cvZero(pContourImg); 
 
int  k
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
double  min
 
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
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
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函数解码条码,他基于像素为单位对条码宽度测量,再找出最小宽度,之后再将其转换成条码识别的标注序列,和编码表比较获得条码值。