opencv车牌识别

网上有很多车牌识别的源代码,很乱,于是自己整理了一份,并把主函数精简到最简单,因为如果主函数里面子函数太多的话,想自己再添加东西进去,不容易,处处是大括号,一会就迷糊了。但是把子函数拉出去的话,就需要把
形参和实参的关系协调好。处理不好就老是出问题,还有就是让主函数中调用子函数时,让谁当实参,也要引起注意!这也是设计子函数形参个数与类别的依据。下面这是作者花了大约一周的时间,对本文的整理,理解,修改,调试并最终定稿的!期望能对广大学习计算机视觉的大学生和爱好者有所帮助。
/**********************************************************************************
                                车牌识别总思路
    一:载入图像,初步处理,并二值化;                //二值化
	二:寻找含有车牌有效信息的区域                    //定位
	       1:找上行位置
		   2;找下行位置
		   3:找左列位置
		   4:找右列位置
	三:提取ROI并归一化处理                           //ROI
	四:分割字符并画白线显示分割区域                  //分割
	五:定位每个字符的区域并独立显示                  //显示
	                                              *** 迷途中的前进——2015_02_06***
***************************************************************************************/
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <iostream>
using namespace std;
#define T 27                            //判断一行是不是车牌有效信息的阈值
#define T1 2                            //判断一列是不是车牌有效信息的阈值
#define S(image,x,y) ((uchar*)(image->imageData + image->widthStep*(y)))[(x)]	
                                        //S(image,x,y)指该图像(x,y)像素点的像素值,[(x)]是数组,类似于a[i]
    IplImage *src;
	IplImage *pImg8u=NULL;             //灰度图
	IplImage *pImg8uSmooth=NULL;       //高斯滤波后的图
	IplImage *pImgCanny=NULL;          //二值化的图	
	IplImage *pImg8uROI=NULL;         //感兴趣的图片
	IplImage *pImgResize=NULL;        //归一化的灰度图

	IplImage *pImgCharOne=NULL;
	IplImage *pImgCharTwo=NULL;
	IplImage *pImgCharThree=NULL;
	IplImage *pImgCharFour=NULL;
	IplImage *pImgCharFive=NULL;
	IplImage *pImgCharSix=NULL;
	IplImage *pImgCharSeven=NULL;


	int i,j;
	int row_start,row_end;             //用来记录车牌开始,结束行
	int col_start,col_end;             //用来记录车牌开始,结束列
    int row[120];                      //row[]存放含有有效车牌信息的第j行,把所有有效行放一个数组里面,统一管理,有利于判断。
	int col[340];                      //存放每个字符双边界(列)的位置
	int k=0;                           //含有有效车牌信息的行数
	int nCharWidth=45;                 //每个字符的列,也就是宽度
	int nSpace=12;                     //字符之间的间隙
	int nWidth=409;                    //(409,90)分别为感兴趣图像的宽度与高度
	int nHeight=90;
	
	void find_UpAndDown_row(IplImage *src_son_row);    
	                                  //定义一个子函数,找到图片中含有车牌有效信息的最上行和最下行
	void find_LeftAndRight_col(IplImage *src_son_col);    
	                                  //定义一个子函数,找到图片中含有车牌有效信息的最左列和最右列
	void find_ROI(IplImage *before_ROI,IplImage *after_ROI);
	                                 //定义一个子函数find_ROI,找到图片中只含有目标区域的部分
	void cut_and_drawLine(IplImage *befour_cut_image );
	                                 //定义一个子函数cut_and_drawLine,对含有车牌有效信息的图片分割出字符,并画出分割线
	void show_every_char(IplImage *showChar);
	                                 //定义一个子函数show_every_char,显示出所有分割出来的字符
void main()
{
	src=cvLoadImage("20.jpg",-1);
	pImg8uSmooth=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
	pImg8u=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
	pImgCanny=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
	cvCvtColor(src,pImg8u,CV_RGB2GRAY);                //灰度化
	cvSmooth(pImg8u,pImg8uSmooth,CV_GAUSSIAN,3,0,0);   //高斯滤波
	cvCanny(pImg8uSmooth,pImgCanny,100,200,3);         //二值化	
	cvDilate(pImgCanny,pImgCanny,0,1);
	cvErode(pImgCanny,pImgCanny,0,1);
	cvNamedWindow("cvcanny",1);
	cvShowImage("cvcanny",pImgCanny);


	row_start=0;
	row_end=0;
	col_start=0;
	col_end=0;
	cout<<"图片的高度值(即像素的行数)为:"<<pImgCanny->height<<endl;
	cout<<"图片的宽度值(即像素的列数)为:"<<pImgCanny->width<<endl;


	 find_UpAndDown_row(pImgCanny);   //找到图片中含有车牌有效信息的最上行和最下行
	 find_LeftAndRight_col(pImgCanny);//找到图片中含有车牌有效信息的最左列和最右列
  	 find_ROI(pImg8u,pImg8uROI);      //找到图片中只含有目标区域的部分

	pImgResize=cvCreateImage(cvSize(nWidth,nHeight),IPL_DEPTH_8U,1);
	cvResize(pImg8uROI,pImgResize,CV_INTER_LINEAR); //线性插值
	cvNamedWindow("感兴趣图像的宽度与高度",1);
	cvShowImage("感兴趣图像的宽度与高度",pImgResize);
	
	cut_and_drawLine(pImgResize );    //对含有车牌有效信息的图片分割出字符,并画出分割线
	show_every_char(pImgResize);      //显示出所有分割出来的字符	
	cvWaitKey(0);

	cvReleaseImage(&pImgResize);
	cvReleaseImage(&pImg8uROI);
	cvReleaseImage(&pImgCharOne);
	cvReleaseImage(&pImgCharTwo);
	cvReleaseImage(&pImgCharThree);
	cvReleaseImage(&pImgCharFour);
	cvReleaseImage(&pImgCharFive);
	cvReleaseImage(&pImgCharSix);
	cvReleaseImage(&pImgCharSeven);

	cvDestroyAllWindows();
}

/******************************************************************************************
            定义一个子函数find_UpAndDown_row,找到图片中含有车牌有效信息的最上行和最下行
******************************************************************************************/
void find_UpAndDown_row(IplImage *src_son_row){
/////////////////////////////////////////////////////////判断每行是不是含有车牌信息的行是通过查看黑点白点变化的次数来确定的

	for(j=0;j<src_son_row->height;j++)          
		                                               //遍历整幅图的行和列,寻找包含车牌信息的行数
	{
		int count=0;                                       //  count/2  记录每行白点(水平的白线看做一个点)的个数
		for(i=0;i<src_son_row->width;i++)
		{
			if(S(src_son_row,i,j)!=S(src_son_row,i+1,j))  //统计行跳数
				count++;
			if(count>T)                               //把含有车牌有效信息的行j存放到row[k]
			{
				row[k]=j;
				k++;                                  //记录含有有效车牌信息的行数
				break;
			}
		}
		//cout<<"count值为:"<<count<<endl;
	}
	cout<<"有效行k值为:"<<k<<endl;
	for(i=0;i<k;i++)                                 //从上边开始,3行连续时认为是起始行
	{
		if((row[i]==row[i+1]-1)&&(row[i+1]==row[i+2]-1)){
			row_start=row[i];
           // cout<<"the start row123:"<<row_start<<endl;
			break;
		}
	}
	cout<<"the start row:"<<row_start<<endl;
	cvLine(pImg8u,cvPoint(0,row_start),cvPoint(src->width,row_start),cvScalar(255,0,0),1,8,0);
	cvNamedWindow("划线_上",1);
	cvShowImage("划线_上",pImg8u);

	for(i=k-1;i>row_start;i--)     //从下边开始,3行连续时认为是起始行
	{
		if((row[i]==row[i-1]+1)&&(row[i-1]==row[i-2]+1)){
			row_end=row[i];
			break;
		}
	}
	cout<<"the end row:"<<row_end<<endl;
	cvLine(pImg8u,cvPoint(0,row_end),cvPoint(src->width,row_end),cvScalar(255,0,0),1,8,0);
	cvNamedWindow("划线_上下",1);
	cvShowImage("划线_上下",pImg8u);
}
/******************************************************************************************
      定义一个子函数find_LeftAndRight_col,找到图片中含有车牌有效信息的最左列和最右列
******************************************************************************************/
void find_LeftAndRight_col(IplImage *src_son_col){
/////////////////////////////////////////////////判断每列是不是含有车牌有效信息是查看每列中含有白点像素的个数
	bool flag=false;
	for(i=10;i<src_son_col->width;i++)           //找到左列开始???i为什么是10???
	{
		int count=0;
		for(j=row_start;j<row_end;j++)
		{
			if(S(src_son_col,i,j)==255)
				count++;
			if(count>T1)
			{
				col_start=i;
				flag=true;
				break;
			}
		}
		if(flag) break;
	}
	cout<<"the start col:"<<col_start<<endl;
	cvLine(pImg8u,cvPoint(col_start,row_start),cvPoint(col_start,row_end),cvScalar(255,0,0),1,8,0);
	cvNamedWindow("划线_左",1);
	cvShowImage("划线_左",pImg8u);

	flag=false;
	for(i=src_son_col->width-10;i>col_start;i--)           //找到右列开始
	{
		int count=0;
		for(j=row_start;j<row_end;j++)
		{
			if(S(src_son_col,i,j)==255)
				count++;
			if(count>T1)
			{
				col_end=i;
				flag=true;
				break;
			}
		}
		if(flag) break;
	}
	cout<<"the end col:"<<col_end<<endl;
	cvLine(pImg8u,cvPoint(col_end,row_start),cvPoint(col_end,row_end),cvScalar(255,0,0),1,8,0);
	cvNamedWindow("划线_左右",1);
	cvShowImage("划线_左右",pImg8u);
}
/******************************************************************************************
              定义一个子函数find_ROI,找到图片中只含有目标区域的部分
******************************************************************************************/
void find_ROI(IplImage *before_ROI,IplImage *after_ROI){
	CvRect ROI_rect;                 //获得图片感兴趣区域
	ROI_rect.x=col_start;
	ROI_rect.y=row_start;
	ROI_rect.width=col_end-col_start;
	ROI_rect.height=row_end-row_start;
	cvSetImageROI(pImg8u,ROI_rect);
	pImg8uROI=cvCreateImage(cvSize(ROI_rect.width,ROI_rect.height),IPL_DEPTH_8U,1);
	cvCopy(pImg8u,pImg8uROI);
	cvResetImageROI(pImg8u);
	}
 /******************************************************************************************
      定义一个子函数cut_and_drawLine,对含有车牌有效信息的图片分割出字符,并画出分割线
******************************************************************************************/
 void cut_and_drawLine(IplImage *befour_cut_image ){
///////////////////////////////////////////////////////////////得到每个字符的双边界
	//七个字符的间距坐标间距,只看水平方向。分别为:0(0,45),1(57,102),2(136,181),3(193,238)
	//                                              4(250,295),5(307,352),6(364,409)
//////////////////////////////////////////////////////////////并存放在col[]里面
	 for(i=0;i<7;i++)          
	{
		switch(i){
			case 0:                                          //0是省份名占45个像素
			case 1:                                          //1是市名的字母占45个像素
				col[i*2]=i*nCharWidth+i*nSpace;
				cout<<col[i*2]<<endl;
				col[i*2+1]=(i+1)*nCharWidth+i*nSpace;
				cout<<col[i*2+1]<<endl;
				break;                                       //1与2之间有个点宽度是34个像素
			case 2:                                          //2---6是剩余的字母和数字各自占45个像素
			case 3:
			case 4:
			case 5:
			case 6:
				col[i*2]=i*nCharWidth+i*nSpace+22;
				cout<<col[i*2]<<endl;
				col[i*2+1]=(i+1)*nCharWidth+i*nSpace+22;
				cout<<col[i*2+1]<<endl;
				break;
		}

	}
	for(i=0;i<14;i++)        //画出每个字符的区域
	{
		cvLine(befour_cut_image,cvPoint(col[i],0),cvPoint(col[i],nHeight),cvScalar(255,0,0),1,8,0);
		//cout<<col[i*2]<<" "<<col[2*i+1]<<" ";
	}
	//cvNamedWindow("画出每个字符的区域",1);
	//cvShowImage("画出每个字符的区域",befour_cut_image);

 }
/******************************************************************************************
              定义一个子函数show_every_char,显示出所有分割出来的字符
******************************************************************************************/
void show_every_char(IplImage *showChar){
	
	pImgCharOne=cvCreateImage(cvSize(nCharWidth,nHeight),IPL_DEPTH_8U,1);
	pImgCharTwo=cvCreateImage(cvSize(nCharWidth,nHeight),IPL_DEPTH_8U,1);
	pImgCharThree=cvCreateImage(cvSize(nCharWidth,nHeight),IPL_DEPTH_8U,1);
	pImgCharFour=cvCreateImage(cvSize(nCharWidth,nHeight),IPL_DEPTH_8U,1);
	pImgCharFive=cvCreateImage(cvSize(nCharWidth,nHeight),IPL_DEPTH_8U,1);
	pImgCharSix=cvCreateImage(cvSize(nCharWidth,nHeight),IPL_DEPTH_8U,1);
	pImgCharSeven=cvCreateImage(cvSize(nCharWidth,nHeight),IPL_DEPTH_8U,1);

	CvRect ROI_rect1;
	ROI_rect1.x=col[0];
	ROI_rect1.y=0;
	ROI_rect1.width=nCharWidth;
	ROI_rect1.height=nHeight;
	cvSetImageROI(showChar,ROI_rect1);
	cvCopy(showChar,pImgCharOne,NULL); //获取第1个字符
	cvResetImageROI(showChar);

	ROI_rect1.x=col[2];
	ROI_rect1.y=0;
	ROI_rect1.width=nCharWidth;
	ROI_rect1.height=nHeight;
	cvSetImageROI(showChar,ROI_rect1);
	cvCopy(showChar,pImgCharTwo,NULL); //获取第2个字符
	cvResetImageROI(showChar);

	ROI_rect1.x=col[4];
	ROI_rect1.y=0;
	ROI_rect1.width=nCharWidth;
	ROI_rect1.height=nHeight;
	cvSetImageROI(showChar,ROI_rect1);
	cvCopy(showChar,pImgCharThree,NULL); //获取第3个字符
	cvResetImageROI(showChar);

	ROI_rect1.x=col[6];
	ROI_rect1.y=0;
	ROI_rect1.width=nCharWidth;
	ROI_rect1.height=nHeight;
	cvSetImageROI(showChar,ROI_rect1);
	cvCopy(showChar,pImgCharFour,NULL); //获取第4个字符
	cvResetImageROI(showChar);

	ROI_rect1.x=col[8];
	ROI_rect1.y=0;
	ROI_rect1.width=nCharWidth;
	ROI_rect1.height=nHeight;
	cvSetImageROI(showChar,ROI_rect1);
	cvCopy(showChar,pImgCharFive,NULL); //获取第5个字符
	cvResetImageROI(showChar);

	ROI_rect1.x=col[10];
	ROI_rect1.y=0;
	ROI_rect1.width=nCharWidth;
	ROI_rect1.height=nHeight;
	cvSetImageROI(showChar,ROI_rect1);
	cvCopy(showChar,pImgCharSix,NULL); //获取第6个字符
	cvResetImageROI(showChar);

	ROI_rect1.x=col[12];
	ROI_rect1.y=0;
	ROI_rect1.width=nCharWidth;
	ROI_rect1.height=nHeight;
	cvSetImageROI(showChar,ROI_rect1);
	cvCopy(showChar,pImgCharSeven,NULL); //获取第7个字符
	cvResetImageROI(showChar);
	
	cvNamedWindow("分割后的车牌",1);
	cvShowImage("分割后的车牌",showChar);
	cvNamedWindow("one",CV_WINDOW_AUTOSIZE);
	cvShowImage("one",pImgCharOne);
	cvNamedWindow("two",1);
	cvShowImage("two",pImgCharTwo);
	cvNamedWindow("three",1);
	cvShowImage("three",pImgCharThree);
	cvNamedWindow("four",1);
	cvShowImage("four",pImgCharFour);
	cvNamedWindow("five",1);
	cvShowImage("five",pImgCharFive);
	cvNamedWindow("six",1);
	cvShowImage("six",pImgCharSix);
	cvNamedWindow("seven",1);
	cvShowImage("seven",pImgCharSeven);
	}

opencv车牌识别