網上有很多車牌識別的源代碼,很亂,於是自己整理了一份,並把主函數精簡到最簡單,因為如果主函數里面子函數太多的話,想自己再添加東西進去,不容易,處處是大括號,一會就迷糊了。但是把子函數拉出去的話,就需要把
形參和實參的關系協調好。處理不好就老是出問題,還有就是讓主函數中調用子函數時,讓誰當實參,也要引起注意!這也是設計子函數形參個數與類別的依據。下面這是作者花了大約一周的時間,對本文的整理,理解,修改,調試並最終定稿的!期望能對廣大學習計算機視覺的大學生和愛好者有所幫助。
/**********************************************************************************\
車牌識別總思路
一:載入圖像,初步處理,並二值化; //二值化
二:尋找含有車牌有效信息的區域 //定位
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);
}
