车牌识别系统 opencv

这个车牌识别系统(opencv定位,切割+BP神经网络识别字符)我已经在本科毕业的时候顺利完成了,相关的文档可以给大家一个链接~祝顺。http://wenku.baidu.com/view/0d69765dbed5b9f3f90f1cd5.html 
1.图像预处理
车牌识别系统的第一个步骤为图像预处理。简单的说,为了方便计算,减少计算量,我们通常将获取的图片灰度化。所谓灰度化就是让图片每个像素点在0-255之间。灰度化后图片可以方便我们操作而且不影响我们对车牌的操作。这里先说下,我们获取的照片因为光照的不同,或者车牌本身有污染等原因,会让我们的照片存在干扰信号。将图片傅里叶变换之后,这些干扰信后又通常为高频信号,所以我们需要做一个滤波器,将高频滤掉。对,就是一个低通。推荐使用均值滤波哈,方便我们后面的几个操作!

注意一下,这里的0代表的是强行将图片转化为灰度图,再读入内存。当然,你完全可以用另一个函数:
	cvCvtColor( SRC_PICTURE	, DES_PICTURE	, CV_BGR2GRAY );

       
	img0 = cvLoadImage( names2[i], 0 ); 
	

        if( !img0 )  
        {  
            printf("Couldn't load %s/n", names[i] );  
            continue;  
        } 

        img=cvCreateImage(cvSize(400,300),8,1);  
	imgsource=cvCreateImage(cvSize(400,300),8,1);  
        
	/*目标图片img*/
        cvResize(img0,img);  


        /*显示目标文件img*/
        cvNamedWindow("input",1);  
        cvShowImage("input",img);  

	/*均值滤波*/
        cvSmooth(img,img,CV_MEDIAN);  



到此为止,我们就做好了图片预处理50%啦!接下来的一个操作是将图片进行sobel变换。简单的说,sobel变化是一种边缘检测,它可以帮我们检测出一张图片的边缘。而它到底又是怎么样的原理呢?我帮大家推荐一篇文章,应该可以解决大家大部分的疑惑!点击打开链接而sobel在opencv里面对应的函数为:void cvSobel( const CvArr* src, CvArr* dst, int xorder, int yorder, int aperture_size=3 );根据大量的实验,观察,我们发现车牌是纵向纹理的,所以我们可以通过sobel函数将车牌的纹理提取出来!下面附一张效果图

大家可以清楚地看到,此时,车牌的纵向纹理就被很好的提取出来。现在到了最关键的一步,就是二值化。所谓二值化就是在图片中设定一个阀值T,当某点的像素小于T时,就将此点像素置为0,否则置为255;而关键却在于如何寻找合适阀值?
阀值的需找有很多种,我选择了大律法。因为据说这种算法十分经典。下面给出C源码:
#define GrayScale 256

int otsu(const IplImage *frame) //大津法求阈值
{
 //frame灰度级
int width=frame->width;
int	height=frame->height;
 int pixelCount[GrayScale]={0};
 float pixelPro[GrayScale]={0};
 int i, j, pixelSum = width * height, threshold = 0;
 uchar* data = (uchar*)frame->imageData;
 float w0, w1, u0tmp, u1tmp, u0, u1, deltaTmp, deltaMax = 0;
 //统计每个灰度级中像素的个数
 for(i = 0; i < height; i++)
 {
  for(j = 0;j < width;j++)
  {
   pixelCount[(int)data[i * width + j]]++;
  }
 }
 //计算每个灰度级的像素数目占整幅图像的比例
 for(i = 0; i < GrayScale; i++)
 {
  pixelPro[i] = (float)pixelCount[i] / pixelSum;
 }
 //遍历灰度级[0,255],寻找合适的threshold
 for(i = 0; i < GrayScale; i++)
 {
  w0 = w1 = u0tmp = u1tmp = u0 = u1 = deltaTmp = 0;
  for(j = 0; j < GrayScale; j++)
  {
   if(j <= i)   //背景部分
   {
    w0 += pixelPro[j];
    u0tmp += j * pixelPro[j];
   }
   else   //前景部分
   {
    w1 += pixelPro[j];
    u1tmp += j * pixelPro[j];
   }
  }
  u0 = u0tmp / w0;
  u1 = u1tmp / w1;
  deltaTmp = (float)(w0 *w1* pow((u0 - u1), 2)) ;
  if(deltaTmp > deltaMax)
  {
   deltaMax = deltaTmp;
   threshold = i;
  }
 }
 return threshold;
}

阀值找到后,我们可以用opencv的库函数将图片阀值话了!原型为:void cvThreshold( const CvArr* src, CvArr* dst, double threshold, double max_value, int threshold_type );让我们看一看阀值后的效果:
到此为止,图片预处理已经完成了,我贴下这部分的源码:

	IplImage* imgS=cvCreateImage(cvGetSize(img),IPL_DEPTH_16S,1);  
        IplImage* imgTh=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);  
        IplImage* temp=cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);  
        cvSobel(img,imgS,2,0,3);  
        cvNormalize(imgS,imgTh,255,0,CV_MINMAX);  
        cvNamedWindow("Sobel",1);  
        cvShowImage("Sobel",imgTh);  

	cvReleaseImage(&imgS); 
	cvReleaseImage(&temp); 
		
	/*阀值化*/
	printf("threshold=%d",otsu(imgTh));
        cvThreshold( imgTh, imgTh, otsu(imgTh), 255, CV_THRESH_BINARY );
车牌识别的第二部分为车牌定位,我所用的方法是基于数学形态变化。将上面得到的图片不断地进行开操作,闭操作,从而可以让车牌部分连成一个区域,大家可以看下效

果图:

大家可以很清楚得看到我们已经得到了车牌明显的区域。在进行开闭操作的时候有两点需要注意:

1.Opencv的开闭操作函数不是很好,因为它对计算过后的核没有进行进步一步操作。(大致意思)所以我查看了几位牛人的博客,得到了他们无私奉献的开源函数。(我是小菜啊,刚大3,接触cv不到1个月,以前一直玩单片机的。)现在公布如下哈:

void lhMorpROpen(const IplImage* src, IplImage* dst, IplConvKernel* se, int iterations)
{
assert(src != NULL  && dst != NULL && src != dst );

IplImage*  temp = cvCreateImage(cvGetSize(src), 8, 1);

cvErode(src, temp, se, iterations);

lhMorpRDilate(temp, src, dst, se, -1);

cvReleaseImage(&temp);

}
void lhMorpRClose(const IplImage* src, IplImage* dst, IplConvKernel* se , int iterations)

{

       assert(src != NULL  && dst != NULL && src != dst );

       IplImage*  temp = cvCreateImage(cvGetSize(src), 8, 1);

       cvDilate(src, temp, se, iterations);

       lhMorpRErode(temp, src, dst, se, -1);

       cvReleaseImage(&temp);

} 

介于篇幅,lhMorpRErode等函数大家可以百度之,或者联系我哈~

2.关于开闭操作的核的问题。膨胀,腐蚀都是岩石形态学里面的东西,我国庆节利用时间看了一下这方面的只是,也问我数学系的哥们,都感觉比较难。它卷积的计算方法应该是自有一套体系吧,懂的大神帮助下我~我再去告诉其他人嘛!我看了几个论文,最终试出了比较好的核。1*3,中心0,1.在下面的代码中大家会看到哈!

接下来就是找到车牌区域了。在opencv里面有专门的寻找轮廓的函数,简直是所向披靡。 int cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour,

int header_size=sizeof(CvContour), int mode=CV_RETR_LIST,int method=CV_CHAIN_APPROX_SIMPLE, CvPoint offset=cvPoint(0,0) );它的返回值是找到的轮廓条数。找到的轮廓被这个函数放在了队列里面存储,(opencv里面采用队列存储)并用一根指针指向了这个队列。CvSeq** first_contour,就是这根指针哈。具体的函数flag的含义大家google,百度很方便的,我也就不多嘴了。下面给出效果图和源码

cvFindContours( src, storage, &contours, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );  
	
	
     for( ; contours != 0; contours = contours->h_next)  
            {  
				
				
               CvRect aRect = cvBoundingRect( contours, 1 );  //使用边界框的方式 
		
		int tmparea=aRect.width*aRect.height; 
		float fact=float(float(aRect.width)/float(aRect.height));
		printf("Are0=%d,Width=%d,Hei=%d,fact=%f\n",tmparea,aRect.width,aRect.height,fact);

	if (fact>=3.2  && fact<=8.0&& tmparea>=1000&&tmparea<=9500)  
	{  
					
	cvRectangle(dst,cvPoint(aRect.x,aRect.y),cvPoint(aRect.x+aRect.width ,aRect.y+aRect.height),color,2);
	printf("Are1=%d,Width=%d,Hei=%d,fact=%f\n",tmparea,aRect.width,aRect.height,fact);
					
	CarID_Picture_Rec(aRect);	
	cvNamedWindow("carID",1);  
	cvShowImage("carID",pImg8uROI); 
						
					
						
					
	}
		
  }  


最近还要忙字符识别啊,欢迎大家交流啊,我也是小菜一个

你可能感兴趣的:(车牌识别系统 opencv)