#include "stdafx.h"
#include "opencvCar.h"
#include "MyDialog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define DEBUGTEST
#define POINT_X 18 // 水平方向不重疊點
#define POINT_Y 2 // 垂直方向不重疊點
#define WITH_X 0.1 // 水平方向車牌在圖區域
#define WITH_Y 0.2 // 垂直方向車牌在圖區域
#define HIGH_WITH_CAR 4.8 //(440/85) // 小車的寬高比 440/140
#define CHARACTER 15
#define TEMPLETENUM 44
char traingout[];
char msg[];
int num_t_out[CHARACTER]={0};
// 車牌字符模板特征值
const int Num_Templete[TEMPLETENUM][CHARACTER]=
{
{16,19,10,12,10,10,15,18,110,3,2,2,3,3,3}, //0
{9,11,10,10,10,10,9,10,79,2,2,2,0,2,12}, //1
{18,19,3,18,10,10,23,22,123,4,2,2,7,6,8}, //2
{19,21,11,14,4,20,18,22,129,2,2,4,6,6,7}, //3
{2,18,11,22,20,21,11,18,123,2,4,2,6,7,5}, //4
{23,19,20,12,9,20,18,22,143,2,4,4,6,6,6}, //5
{6,13,17,8,15,20,18,20,117,2,2,4,5,7,6}, //6
{21,21,0,20,8,12,9,11,102,2,2,2,2,8,15}, //7
{17,18,18,19,14,20,17,20,143,4,2,4,6,6,6}, //8
{16,18,15,21,7,19,13,7,116,3,2,2,6,6,5}, //9
{10,10,16,16,20,20,18,19,129,2,4,2,8,3,6}, //A
{24,20,20,19,22,22,24,20,171,4,8,4,6,6,6}, //B
{18,19,20,4,20,8,17,21,127,3,2,4,4,4,4}, //C
{23,19,11,20,12,20,22,21,148,3,3,3,4,4,4}, //D
{23,19,21,9,22,8,23,23,148,2,2,2,6,6,6}, //E
{25,17,20,9,22,8,19,0,120,2,2,2,4,4,4}, //F
{17,18,22,14,12,24,18,21,146,4,7,4,4,6,6}, //G
{14,20,18,22,17,22,16,20,149,4,1,4,2,2,2}, //H
{0,17,0,20,3,20,18,22,100,2,2,4,2,2,2}, //J
{19,20,26,10,20,20,20,22,157,4,4,4,3,5,11}, //K
{20,0,20,0,20,0,25,20,105,2,2,2,2,2,2}, //L
{20,10,27,17,20,10,22,14,140,1,3,3,4,1,5}, //M
{21,12,25,17,26,12,18,18,149,3,5,3,5,5,6}, //N
{23,19,18,20,21,8,22,0,131,3,3,2,4,4,4}, //P
{18,19,20,10,26,15,18,21,147,3,3,4,5,7,5}, //Q
{26,19,21,18,21,17,20,21,163,4,3,4,4,6,5}, //R
{18,18,18,10,8,17,17,22,128,4,3,4,6,6,6}, //S
{22,18,10,10,10,10,10,10,100,2,2,2,33,2,2}, //T
{18,12,20,10,20,10,19,21,130,3,3,3,2,2,2}, //U
{20,19,20,20,15,14,9,10,127,4,4,2,9,1,8}, //V
{21,25,26,28,16,16,21,19,172,6,2,4,13,0,7}, //W
{21,21,13,13,12,11,22,21,134,4,2,4,8,0,10}, //X
{21,20,10,11,10,10,10,11,103,3,2,2,5,2,6}, //Y
{21,23,5,15,15,5,24,20,128,2,2,2,8,8,7}, //Z
{13,14,10,10,10,10,13,13,93,2,2,2,29,2,29}, //I
{20,20,13,20,19,12,17,20,141,3,3,4,4,4,4}, //O //36
{14,15,17,17,16,10,25,24,138,0,2,4,12,8,9}, //雲 //37
{17,20,17,12,33,28,23,20,170,3,4,7,13,6,4}, //蘇
{21,21,23,24,24,25,31,27,196,0,9,6,8,6,7}, //京
{19,27,20,34,19,36,24,37,216,4,4,7,13,28,3}, //湘
{17,14,23,27,36,40,26,27,210,4,13,4,16,14,14}, //魯
{24,24,32,38,34,32,17,22,223,9,6,10,11,12,9}, // 粵
{22,20,33,37,25,24,24,25,210,13,3,6,12,8,7}, //蒙
{20,18,30,25,36,29,24,27,0,5,4,7,8,6} //皖 24,20,30,24,37,30,25,27,0,5,4,7,9,8,不是特別准確 還需要訓練
/*識別過程:將待識別字符規一化成40*20的標准字符。然后提取特征:
1. 前8個的特征的特取算法是將規一化一的字符打成4行2列的網格,分別統計各網格內的灰度值。
2. 第9個特征為前8個特征的和;
3. 10-12個特征分別為第10行,20行,30行的灰度值和;
4. 13-15個特征分別為第7列,10列,13列的灰度值和;
總共是15維的特征。 特征值已經在界面顯示,如果不需要可以資源視圖刪除。算法不是很好,只是應付畢業論文級別。注釋已經盡可能仔細,值得研究*/
};
char msg[256];
//車牌字符
char *PlateCode[TEMPLETENUM] =
{
"0", "1", "2", "3", "4" ,
"5","6", "7", "8", "9",
"A", "B", "C", "D","E",
"F", "G","H", "J", "K",
"L", "M", "N","P", "Q",
"R", "S", "T", "U", "V",
"W","X", "Y", "Z", "I", "O",
"雲", "蘇","京", "湘", "魯","粵","蒙","皖"
};
char *G_PlateChar[7]={"沒", "有","找", "到", "車", "牌" ,"!"}; // 車牌號
/////////////////////////////////////////////////////////////////////////////
// CMyDialog dialog
CMyDialog::CMyDialog(CWnd* pParent /*=NULL*/)
: CDialog(CMyDialog::IDD, pParent)
{
//{{AFX_DATA_INIT(CMyDialog)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
//初始化 變量
IplImage *src = NULL; //原始圖片
IplImage *pImgCanny=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;
}
void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMyDialog)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
//{{AFX_MSG_MAP(CMyDialog)
ON_BN_CLICKED(IDC_LOADIMAGE, OnLoadimage)
ON_BN_CLICKED(IDC_BINARYIMG, OnBinaryimg)
ON_BN_CLICKED(IDC_CARLOCATE, OnCarlocate)
ON_BN_CLICKED(IDC_SPLITCAR, OnSplitcar)
ON_BN_CLICKED(IDC_SHIBIECAR, OnShibiecar)
ON_BN_CLICKED(ID_HELP, ABOUT)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMyDialog message handlers
//把圖像顯示在 MFC圖片控件里
void CMyDialog::DrawPicToHDC(IplImage *img, UINT ID)
{
CDC *pDC = GetDlgItem(ID)->GetDC();
HDC hDC = pDC->GetSafeHdc();
CRect rect;
GetDlgItem(ID)->GetClientRect(&rect);
CvvImage cimg;
cimg.CopyOf(img,3);
cimg.DrawToHDC(hDC,&rect);
ReleaseDC(pDC);
}
//打開圖片 並記錄下路徑
void CMyDialog::OnLoadimage()
{
// TODO: Add your control notification handler code here
src = NULL ;
CString filePath;
CFileDialog dlg(TRUE, _T("*.bmp"),"",OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY,"image files (*.bmp; *.jpg) |*.bmp;*.jpg|All Files (*.*)|*.*||",NULL);
char title[]= {"Open Image"};
dlg.m_ofn.lpstrTitle= title;
if (dlg.DoModal() == IDOK) {
filePath= dlg.GetPathName();
//LoadBmpFile(filePath);
src=cvLoadImage(filePath);
DrawPicToHDC(src,IDC_IMAGESRC);
}
pImgCanny=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1); // 2值化后圖片大小初始化
cvCvtColor(src,pImgCanny,CV_RGB2GRAY); //轉化為灰度圖 openCV函數 Y = 0.299*R + 0.587*G + 0.114*B
// 轉為灰度圖 Y=0.299*R + 0.587*G + 0.114*B
cvSmooth(pImgCanny,pImgCanny,CV_GAUSSIAN,3,0,0); //平滑高斯濾波 濾波后的圖片保存在 pImgCanny
}
/* ----------------------自適應閾值法---------------------------------//
//--input: t:中心閾值
//-- Image: 圖片指針
//-- output: return 自適應均值的閾值
//-------------------------------------------------------------------------*/
int CMyDialog::AdaptiveThreshold(int t, IplImage *Image)
{
int t1=0,t2=0,tnew=0,i=0,j=0;
int Allt1=0,Allt2=0,accountt1=0,accountt2=0;//Allt1 Allt2 保存兩部分的和
for(j=0;j<Image->height;j++) //根據現有T,將圖像分為兩部分,分別求兩部分的平均值t1、t2
{
for(i=0;i<Image->width;i++)
{
if(CV_IMAGE_ELEM(Image,uchar,j,i)<t)
{
Allt1+=CV_IMAGE_ELEM(Image,uchar,j,i);
accountt1++;
}
else
{
Allt2+=CV_IMAGE_ELEM(Image,uchar,j,i);
accountt2++;
}
}
}
t1=Allt1/accountt1;
t2=Allt2/accountt2;
tnew=0.5*(t1+t2);
if(tnew==t) //若t1、t2的平均值和t相等,則閾值確定
return tnew;
else
AdaptiveThreshold(tnew,Image); //不等則以t1、t2的平均值為新閾值迭代
}
/* -----------------------二值化-----------------------------------------//
// --Input:
// IplImage *Image: 圖片指針
// int AdaptiveThreshold(int t,IplImage *Image) //自適應閾值法
//-- Output:
// IplImage *Image_O 二值化后的圖片
//-- Description:
// 采用Canny邊緣檢測二值化
//-------------------------------------------------------------------------*/
void CMyDialog::Threshold(IplImage *Image, IplImage *Image_O)
{
//得到圖片的最大灰度值和最小灰度值
int thresMax=0,thresMin=255,i=0,j=0,t=0;
for(j=0;j<Image->height;j++)
for(i=0;i<Image->width;i++)
{
if(CV_IMAGE_ELEM(Image,uchar,j,i)>thresMax) //像素值 大於 255
thresMax=CV_IMAGE_ELEM(Image,uchar,j,i); //把元素值賦給 thresMax
else if(CV_IMAGE_ELEM(Image,uchar,j,i)<thresMin) //如果小於 0
thresMin=CV_IMAGE_ELEM(Image,uchar,j,i); //則 改變thresMin
}
//int T=(thresMax+thresMin)*0.5; //灰度的最大值和最小值的平均
cvCanny(Image,Image_O,AdaptiveThreshold((thresMax+thresMin)*0.5,Image),thresMax*0.7,3);
//小閾值用來控制邊緣連接 大閾值用來控制強邊緣的初始化分割 cvCanny只接受單通道的輸入
}
//車牌區域檢測
/***************************************************
INPUT : pImg_Image 二值化后的圖像
src 原始圖像
OUTPUT : pImgResize 歸一化后的車牌灰度圖像
Description :
輸出歸一化圖片大小 40*20
歸一化的區域為在在原始圖片(src) 歸一化之后,在再二值化后 圖
定位方法: 水平分割,垂直分割,歸一化。
***********************************************************/
int CMyDialog::PlateAreaSearch(IplImage *pImg_Image)
{
if (pImg_Image==NULL) { return 0; } // 檢測是否有值
IplImage* imgTest =0;
int i=0, j=0,k=0,m=0;
bool flag=0;
int plate_n=0 ,plate_s=0,plate_e=0 ,plate_w=0; //關於車牌的一些變量
int *num_h=new int[max(pImg_Image->width,pImg_Image->height)];
if ( num_h==NULL )
{
//cout<<"memory exhausted"<<endl;
MessageBox("memory exhausted!");
return 0;
// exit(1);
} // end if
for(i=0;i<pImg_Image->width;i++){num_h[i]=0;} // 初始化 分配的空間
imgTest = cvCreateImage(cvSize(pImg_Image->width,pImg_Image->height),IPL_DEPTH_8U,1);
cvCopy(pImg_Image, imgTest);
//-- 水平 輪廓細化
for(j=0; j<imgTest->height; j++)
{
for(i=0;i<imgTest->width-1;i++)
{
CV_IMAGE_ELEM(imgTest,uchar,j,i)=CV_IMAGE_ELEM(imgTest,uchar,j,i+1)-CV_IMAGE_ELEM(imgTest,uchar,j,i);
num_h[j]+=CV_IMAGE_ELEM(imgTest,uchar,j,i)/250;
}
}
int temp_1=0;
int temp_max =0;
int temp_i = 0; //說明這里for 循環 是找出數據量最大的地方 20行 也即是 車牌區域
for(j=0; j<imgTest->height-20; j++)
{
temp_1=0;
for(i=0;i<20;i++)//此處for循環 是為了計算20行的總數據量
temp_1 += num_h[i+j];
if(temp_1>=temp_max)
{
temp_max=temp_1;
temp_i = j;//記錄20行的最大數據量的開始 行
}
}
k=temp_i;//以下兩個while 循環是為了找出 車牌的上下邊界 當一行的數據量小於某個數值時 設定此為分界行
while ( ((num_h[k +1]>POINT_X )||(num_h[k +2]>POINT_X )||(num_h[k]>POINT_X )) && k ) k--;//找出行邊界行
plate_n=k+1;//k+2;
k=temp_i+10;
while (((num_h[k -1]>POINT_X )||(num_h[k-2]>POINT_X )||(num_h[k]>POINT_X ))&&(k<imgTest->height)) k++; //找出下邊界行
plate_s=k;//k-2;
// 沒找到水平分割線,設置為默認值
if ( !(plate_n && plate_s //行為負值 或者 上行大於下行 或者 車牌寬度大於 設定值 則水平分割失敗
&& (plate_n<plate_s) && ((plate_s-plate_n)*HIGH_WITH_CAR<imgTest->width*(1-WITH_X))))
{
//flag=1;
// cout<<"水平分割失敗"<<endl;
MessageBox("水平分割失敗!");
return 0;
}
else//找到水平線
{
int max_count = 0;
int plate_length = (imgTest->width-(plate_s-plate_n)*HIGH_WITH_CAR);
plate_w=imgTest->width*WITH_X-1;//車牌寬度 默認
//--垂直方向
for(i=0;i<imgTest->width;i++)
for(j=0;j<imgTest->height-1;j++)//用的方法是 差分賦值法——我起的名字 應該是為了細化
{
CV_IMAGE_ELEM(imgTest,uchar,j,i)=CV_IMAGE_ELEM(imgTest,uchar,j+1,i)-CV_IMAGE_ELEM(imgTest,uchar,j,i);
}
// 車牌左邊界的橫坐標是 k
//這里 plate_length原圖像的寬度減去車牌的寬度差值。
for(k=0;k<plate_length;k++)
{
for(i=0; i<(int)((plate_s-plate_n)*HIGH_WITH_CAR); i++)
for (j=plate_n;j<plate_s;j++)//兩水平線之間
{
num_h[k] =num_h[k]+ CV_IMAGE_ELEM(imgTest,uchar,j,(i+k))/250;
}
if (num_h[k]>max_count)
{
max_count = num_h[k];
plate_w = k;
} // end if
}
CvRect ROI_rect; //獲得圖片感興趣區域
ROI_rect.x=plate_w;
ROI_rect.y=plate_n;
ROI_rect.width=(plate_s-plate_n)*HIGH_WITH_CAR;
ROI_rect.height=plate_s-plate_n;
if ((ROI_rect.width+ROI_rect.x)> pImg_Image->width)
{
ROI_rect.width=pImg_Image->width-ROI_rect.x;
// cout<<"垂直方向分割失敗!";
MessageBox("垂直方向分割失敗!");
return 0;
}
else
{
IplImage *pImg8uROI=NULL; //感興趣的圖片
pImg8uROI=cvCreateImage(cvSize(ROI_rect.width,ROI_rect.height), src->depth,src->nChannels);
IplImage *pImg8u11=NULL; //車牌區域灰度圖
pImg8u11=cvCreateImage(cvSize(40*HIGH_WITH_CAR,40),pImg8uROI->depth,pImg8uROI->nChannels);
cvSetImageROI(src,ROI_rect);//將 ROI_rect 設置為感興趣區域
cvCopy(src,pImg8uROI,NULL);//把感興趣區域 復制到 pImg8uROI
cvResetImageROI(src); //重新設置感興趣區域
pImgResize=cvCreateImage(cvSize(40*HIGH_WITH_CAR,40),IPL_DEPTH_8U,1);
cvResize(pImg8uROI,pImg8u11,CV_INTER_LINEAR); //線性插值 歸一化 把車牌變成統一大小
cvCvtColor(pImg8u11,pImgResize,CV_RGB2GRAY); // 轉為灰度圖 Y=0.299*R + 0.587*G + 0.114*B
Threshold(pImgResize,pImgResize); // 二值化
cvReleaseImage(&pImg8uROI);
cvReleaseImage(&pImg8u11);
cvReleaseImage(&imgTest);
} // end if
} // end if
// 釋放內存
delete []num_h;
num_h=NULL;
return 1;
}
//* -----------------------字符分割-----------------------------------------//
// --Input:
// IplImage * pImgResize : 歸一化的車牌區域灰度圖
//-- Output:
// IplImage *pImgCharOne // 字符圖片
// IplImage *pImgCharTwo
// IplImage *pImgCharThree
// IplImage *pImgCharFour
// IplImage *pImgCharFive
// IplImage *pImgCharSix
// IplImage *pImgCharSeven
//-- Description:
// 利用垂直投影法和車牌的特征結合分割字符區域
//-------------------------------------------------------------------------*/
int CMyDialog::SegmentPlate()
{
if (pImgResize==NULL) { return 0; } // 沒有切割成功,直接彈出
int *num_h=new int[max(pImgResize->width,pImgResize->height)]; // 開辟空間 width 大小
if ( num_h==NULL ) //
{
//cout<<"memory exhausted"<<endl;
MessageBox("字符分割memory exhausted");
return 0;
//exit(1);
} // end if
int i=0,j=0,k=0;//循環變量 12
int letter[14]={0,20,23,43,55,75,78,98,101,121,124,127,147,167}; // 默認分割
bool flag1=0;// 1 2 3 4 5 6 7
// 垂直投影
for(i=0;i<40*HIGH_WITH_CAR;i++)
{
num_h[i]=0; // 初始化指針
for(j=0;j<17;j++) // 0-16 /40
{
num_h[i]+=CV_IMAGE_ELEM(pImgResize,uchar,j,i)/45;
}
for(j=24;j<40;j++) // 24-39 /40
{
num_h[i]+=CV_IMAGE_ELEM(pImgResize,uchar,j,i)/45;
}
}
// 初定位,定位點 第二個字符末端,
int max_count=0;
int flag=0;
for(i=30;i<40*HIGH_WITH_CAR;i++)
{
if(num_h[i]<POINT_Y)//小於2
{
max_count++;
if(max_count==11)
{
letter[3]=i-11; // find letter[3]//第二字符的開始位置
while( (num_h[i]<POINT_Y)||(num_h[i-1]<POINT_Y) ) i++;
letter[4]=i-1; // find letter[4] //第三個字符的開始位置
break;//只要找到 第二個字符的末端 和 第三個字符的開始 就退出循環
}
}
else
{
max_count=0;
}
}
// 精定位
for(i=0;i<40*HIGH_WITH_CAR;i++)//每一列的
{
for(j=17;j<=24;j++) // 17-24 /40 每一列的17 到 24 行相加
{
num_h[i]+=CV_IMAGE_ELEM(pImgResize,uchar,j,i)/45;
}
}
for(j=letter[3];j>0;j--)//從第二個字符的末端開始 往前找第一個和第二個字符起始位置
{
if((num_h[j]<POINT_Y)&&(num_h[j-1]<POINT_Y))//只要有兩個列的 17到24 行的值小於 2,
{ //即找到第二個字符的開始位置
letter[2]=j; // find letter[2] 第二個字符的開始位置
letter[1]=(j>=23)?j-3:letter[1]; //第一個字符的結束位置
letter[0]=(j>=23)?j-23:letter[0]; //第一個字符的起始位置
break; //找到就退出循環
}
}
j=2; flag=0;flag1=0;//兩個標記
for(i=letter[4];i<40*HIGH_WITH_CAR;i++) //從第三個字符的開始位置算起
{
if((num_h[i]>POINT_Y)&&(num_h[i-1]>POINT_Y) && !flag )
{
flag=1;
flag1=0;
letter[2*j]=i-1; //這里 只記錄字符的開始位置
if(j==6) //判斷 最后一個字符的結束位置 是否越界 超出界限,如果沒有,則letter[13]=letter[12]+20
{
letter[2*j+1]=((letter[2*j]+20)>40*HIGH_WITH_CAR-1)?40*HIGH_WITH_CAR-1:letter[2*j]+20;
break;//退出 for循環
}
}
else if((num_h[i]<POINT_Y)&&(num_h[i-1]<POINT_Y) && !flag1 && flag)//如果是 空白區域
{
flag=0;
flag1=1;
letter[2*j+1]=i-1;
j++; //j自動加 1
} // 1
}
// 刪除角點 1 0 1
for(i=0;i<40*HIGH_WITH_CAR-1;i++)
{ // 1 刪除角點 相當於拿一個半徑為1 的圓 去圈 如果四周有兩個是1 則自己設置為0
for(j=0;j<39;j++) // 0-16 /40
{
if(CV_IMAGE_ELEM(pImgResize,uchar,j,i)&&CV_IMAGE_ELEM(pImgResize,uchar,j,i+1)&&CV_IMAGE_ELEM(pImgResize,uchar,j+1,i)) // 01
CV_IMAGE_ELEM(pImgResize,uchar,j,i)=0; // 1
if(CV_IMAGE_ELEM(pImgResize,uchar,j,i)&& CV_IMAGE_ELEM(pImgResize,uchar,j,i-1) &&CV_IMAGE_ELEM(pImgResize,uchar,j+1,i)) // 10
CV_IMAGE_ELEM(pImgResize,uchar,j,i)=0; // 1
if(CV_IMAGE_ELEM(pImgResize,uchar,j,i)&&CV_IMAGE_ELEM(pImgResize,uchar,j,i-1) &&CV_IMAGE_ELEM(pImgResize,uchar,j-1,i)) // 1
CV_IMAGE_ELEM(pImgResize,uchar,j,i)=0; // 10
if(CV_IMAGE_ELEM(pImgResize,uchar,j,i)&&CV_IMAGE_ELEM(pImgResize,uchar,j,i+1) &&CV_IMAGE_ELEM(pImgResize,uchar,j-1,i)) // 1
CV_IMAGE_ELEM(pImgResize,uchar,j,i)=0; // 01
}
}
// 分割出字符圖片
pImgCharOne=cvCreateImage(cvSize(20,40),IPL_DEPTH_8U,1);
pImgCharTwo=cvCreateImage(cvSize(20,40),IPL_DEPTH_8U,1);
pImgCharThree=cvCreateImage(cvSize(20,40),IPL_DEPTH_8U,1);
pImgCharFour=cvCreateImage(cvSize(20,40),IPL_DEPTH_8U,1);
pImgCharFive=cvCreateImage(cvSize(20,40),IPL_DEPTH_8U,1);
pImgCharSix=cvCreateImage(cvSize(20,40),IPL_DEPTH_8U,1);
pImgCharSeven=cvCreateImage(cvSize(20,40),IPL_DEPTH_8U,1);
CvRect ROI_rect1;
ROI_rect1.x=0.5*(letter[1]+letter[0])-10; //為什么減10
ROI_rect1.y=0;
ROI_rect1.width=20;
ROI_rect1.height=40;
cvSetImageROI(pImgResize,ROI_rect1);
cvCopy(pImgResize,pImgCharOne,NULL); //獲取第1個字符
cvResetImageROI(pImgResize);
ROI_rect1.x=0.5*(letter[3]+letter[2])-10;
ROI_rect1.y=0;
ROI_rect1.width=20;
ROI_rect1.height=40;
cvSetImageROI(pImgResize,ROI_rect1);
cvCopy(pImgResize,pImgCharTwo,NULL); //獲取第2個字符
cvResetImageROI(pImgResize);
ROI_rect1.x=0.5*(letter[5]+letter[4])-10;
ROI_rect1.y=0;
ROI_rect1.width=20;
ROI_rect1.height=40;
cvSetImageROI(pImgResize,ROI_rect1);
cvCopy(pImgResize,pImgCharThree,NULL); //獲取第3個字符
cvResetImageROI(pImgResize);
ROI_rect1.x=0.5*(letter[7]+letter[6])-10;
ROI_rect1.y=0;
ROI_rect1.width=20;
ROI_rect1.height=40;
cvSetImageROI(pImgResize,ROI_rect1);
cvCopy(pImgResize,pImgCharFour,NULL); //獲取第4個字符
cvResetImageROI(pImgResize);
ROI_rect1.x=0.5*(letter[9]+letter[8])-10;
ROI_rect1.y=0;
ROI_rect1.width=20;
ROI_rect1.height=40;
cvSetImageROI(pImgResize,ROI_rect1);
cvCopy(pImgResize,pImgCharFive,NULL); //獲取第5個字符
cvResetImageROI(pImgResize);
ROI_rect1.x=0.5*(letter[11]+letter[10])-10;
ROI_rect1.y=0;
ROI_rect1.width=20;
ROI_rect1.height=40;
cvSetImageROI(pImgResize,ROI_rect1);
cvCopy(pImgResize,pImgCharSix,NULL); //獲取第6個字符
cvResetImageROI(pImgResize);
ROI_rect1.x=0.5*(letter[13]+letter[12])-10;
ROI_rect1.y=0;
ROI_rect1.width=20;
ROI_rect1.height=40;
cvSetImageROI(pImgResize,ROI_rect1);
cvCopy(pImgResize,pImgCharSeven,NULL); //獲取第7個字符
cvResetImageROI(pImgResize);
// 釋放內存
delete []num_h;
num_h=NULL;
}
//* -----------------------字符識別-----------------------------------------//
// --Input:
// IplImage *pImgCharOne // 字符圖片
// IplImage *pImgCharTwo
// IplImage *pImgCharThree
// IplImage *pImgCharFour
// IplImage *pImgCharFive
// IplImage *pImgCharSix
// IplImage *pImgCharSeven
// int num // 數字字符漢字識別用,
// // 0 數字 1英文 2英文和數字 3中文
// int char_num // 第char_num個車牌字符
//-- Output:
// char *G_G_PlateChar[7] // 車牌號
//-- Description:
// 利用垂直投影法和車牌的特征結合分割字符區域
//-------------------------------------------------------------------------*/
//對於二值圖像,水平方向的投影就是每行的非零像素值的個數,在這里就是1或者255,
//垂直投影就是每列圖像數據中非零像素值的個數。
//車牌特征 就是 車牌一般大小 即長寬比例,還有每個字符之間的間隔 第二個和第三個字符之間間
//隔大些,車牌里有漢字、數字和字母,第一個是漢字,第二個是字母,后面是字母和數字,大體也就這么些特征
int CMyDialog::CodeRecognize(IplImage *imgTest, int num, int char_num)
{
if (imgTest==NULL){ return 0;}
int i=0,j=0,k=0,t=0;//循環變量
int char_start=0,char_end=0;//*PlateCode[TEMPLETENUM] 車牌字符里字母、數字、漢字起始位置
int num_t[CHARACTER]={0};
switch(num)//這里這樣分 可以提高效率,並且提高了識別率
{
case 0: char_start =0; // 數字
char_end = 9;
break;
case 1: char_start =10; // 英文
char_end = 35;
break;
case 2: char_start =0; // 英文和數字
char_end = 35;
break;
case 3: char_start =36; // 中文
char_end = TEMPLETENUM-1;
break;
default: break;
}
// 提取前8個特征 前8個特征 可以說是固定位置的值 固定算法
for(k=0; k<8; k++)
{
for(j=int(k/2)*10; j<int(k/2+1)*10; j++)
{
for(i=(k%2)*10;i<(k%2+1)*10;i++)
{
num_t[k]+=CV_IMAGE_ELEM(imgTest,uchar,j,i)/255 ;
num_t_out[k]=num_t[k];
}
}
// num_t[8]+= num_t[k]; // 第9個特征 前8個特征的和作為第9個特征值
}
num_t[8]=num_t[0]+num_t[1]+num_t[2]+num_t[3]+num_t[4]+num_t[5]+num_t[6]+num_t[7];
num_t_out[8]=num_t[8];
for(i=0;i<20;i++) //以下特征也是 固定算法得到的
{num_t[9]+=CV_IMAGE_ELEM(imgTest,uchar,10,i)/255 ; num_t_out[9]=num_t[9];}
for(i=0;i<20;i++)
{ num_t[10]+=CV_IMAGE_ELEM(imgTest,uchar,20,i)/255 ;num_t_out[10]=num_t[10];}
for(i=0;i<20;i++)
{ num_t[11]+=CV_IMAGE_ELEM(imgTest,uchar,30,i)/255 ;num_t_out[11]=num_t[11];}
for(j=0;j<40;j++)
{ num_t[12]+=CV_IMAGE_ELEM(imgTest,uchar,j,7)/255;num_t_out[12]=num_t[12];}
for(j=0;j<40;j++)
{ num_t[13]+=CV_IMAGE_ELEM(imgTest,uchar,j,10)/255 ;num_t_out[13]=num_t[13];}
for(j=0;j<40;j++)
{ num_t[14]+=CV_IMAGE_ELEM(imgTest,uchar,j,13)/255 ;num_t_out[14]=num_t[14];}
int num_tt[CHARACTER]={0};
int matchnum=0; //可以說是 匹配度或 相似度
int matchnum_max=0;
int matchcode = 0; // 匹配號
//int matchtempnum[10]={0};
j=0;
for(k=char_start;k<=char_end;k++)
//for(k=40;k<42;k++)
{
matchnum=0;
for(i=0;i<8;i++) //區域的匹配
{
// num_tt[i]= abs(num_t[i]-num[k][i]);
if (abs(num_t[i]-Num_Templete[k][i])<=2)//與模板里的相應值進行匹配
matchnum++;//兩者相減,如果絕對值小於2,標記匹配成功一次
}
if(Num_Templete[k][i]-abs(num_t[i])<=8)//對第9個特征進行匹配
matchnum+=2;
for(i=9;i<CHARACTER;i++) // 橫豎的匹配
{
if (Num_Templete[k][i]>=5) //特征值 大於5
{
if(abs(num_t[i]-Num_Templete[k][i])<=1)
matchnum+=2;
}
else if( num_t[i]==Num_Templete[k][i])
{
matchnum+=2;
}
}
if(matchnum>matchnum_max)
{
matchnum_max=matchnum; //保留最大的 匹配
matchcode= k; //記錄 識別的字符的 索引
//matchtempnum[j]=matchnum_min
}
}
//識別輸出 存放輸出結果
G_PlateChar[char_num]=PlateCode[matchcode]; //保存下該字符
}
void CMyDialog::OnBinaryimg()
{
// TODO: Add your control notification handler code here
Threshold(pImgCanny,pImgCanny);
DrawPicToHDC(pImgCanny,IDC_BIIMAGE);
}
void CMyDialog::OnCarlocate()
{
// TODO: Add your control notification handler code here
PlateAreaSearch(pImgCanny); // 車牌定位
DrawPicToHDC(pImgResize,IDC_BICAR); //把定位好的車牌顯示出來
}
void CMyDialog::OnSplitcar()
{
// TODO: Add your control notification handler code here
SegmentPlate(); // 車牌字符分割
DrawPicToHDC(pImgCharOne,IDC_ONE);
DrawPicToHDC(pImgCharTwo,IDC_TWO);
DrawPicToHDC(pImgCharThree,IDC_THREE);
DrawPicToHDC(pImgCharFour,IDC_FOUR);
DrawPicToHDC(pImgCharFive,IDC_FIVE);
DrawPicToHDC(pImgCharSix,IDC_SIX);
DrawPicToHDC(pImgCharSeven,IDC_SEVEN);
//DrawPicToHDC(pImgCharOne,IDC_ONE);
}
void CMyDialog::OnShibiecar()
{ int i;
// TODO: Add your control notification handler code here
//---------------------- 車牌識別 -----------------------------//
CodeRecognize(pImgCharOne,3,0); //篩選器1 參數:0 數字 1英文 2英文和數字 3中文
CString outFilet1= "";
for(i =0;i<=14;i++) //把結果放到 outFilet CString 里
{
char tmsg[5];
itoa(num_t_out[i],tmsg,10);
outFilet1 += tmsg;
outFilet1 += ",";
}
GetDlgItem(IDC_tzz1)->SetWindowText(outFilet1); //輸出特征值1
CodeRecognize(pImgCharTwo,1,1);//篩選器2 參數:0 數字 1英文 2英文和數字 3中文
CString outFilet2= "";
for(i =0;i<=14;i++) //把結果放到 outFilet CString 里
{
char tmsg[5];
itoa(num_t_out[i],tmsg,10);
outFilet2 += tmsg;
outFilet2 += ",";
}
GetDlgItem(IDC_tzz2)->SetWindowText(outFilet2); //輸出特征值2
CodeRecognize(pImgCharThree,2,2);//篩選器3 參數:0 數字 1英文 2英文和數字 3中文
CString outFilet3= "";
for(i =0;i<=14;i++) //把結果放到 outFilet CString 里
{
char tmsg[5];
itoa(num_t_out[i],tmsg,10);
outFilet3 += tmsg;
outFilet3 += ",";
}
GetDlgItem(IDC_tzz3)->SetWindowText(outFilet3); //輸出特征值3
CodeRecognize(pImgCharFour,2,3);//篩選器4 參數:0 數字 1英文 2英文和數字 3中文
CString outFilet4= "";
for(i =0;i<=14;i++) //把結果放到 outFilet CString 里
{
char tmsg[5];
itoa(num_t_out[i],tmsg,10);
outFilet4 += tmsg;
outFilet4 += ",";
}
GetDlgItem(IDC_tzz4)->SetWindowText(outFilet4); //輸出特征值4
CodeRecognize(pImgCharFive,2,4);//篩選器5 參數:0 數字 1英文 2英文和數字 3中文
CString outFilet5= "";
for(i =0;i<=14;i++) //把結果放到 outFilet CString 里
{
char tmsg[5];
itoa(num_t_out[i],tmsg,10);
outFilet5 += tmsg;
outFilet5 += ",";
}
GetDlgItem(IDC_tzz5)->SetWindowText(outFilet5); //輸出特征值5
CodeRecognize(pImgCharSix,2,5);//篩選器6 參數:0 數字 1英文 2英文和數字 3中文
CString outFilet6= "";
for(i =0;i<=14;i++) //把結果放到 outFilet CString 里
{
char tmsg[5];
itoa(num_t_out[i],tmsg,10);
outFilet6 += tmsg;
outFilet6 += ",";
}
GetDlgItem(IDC_tzz6)->SetWindowText(outFilet6); //輸出特征值6
CodeRecognize(pImgCharSeven,2,6);//篩選器7 參數:0 數字 1英文 2英文和數字 3中文
CString outFilet7= "";
for(i =0;i<=14;i++) //把結果放到 outFilet CString 里
{
char tmsg[5];
itoa(num_t_out[i],tmsg,10);
outFilet7 += tmsg;
outFilet7 += ",";
}
GetDlgItem(IDC_tzz7)->SetWindowText(outFilet7); //輸出特征值7
CString outFile= "";
for(i =0;i<7;i++) //把結果放到 outFile CString 里
{
outFile += G_PlateChar[i];
}
GetDlgItem(IDC_RESULT)->SetWindowText(outFile); //輸出到靜態文本 靜態文本是IDC_RESULT
CodeRecognize(pImgCharOne,3,0);
}
//void CMyDialog::TZZXL()
//{
// CString tzz= "";
// int i ;
// for(i =0;i<7;i++) //把結果放到 outFile CString 里
// {
// tzz += ;
// }
// GetDlgItem(TZZ1)->SetWindowText(outFile); //輸出到靜態文本 靜態文本是IDC_RESULT
//}
void CMyDialog::ABOUT()
{
MessageBox(TEXT("基於opencv的車牌照識別系統\n2016年5月\n微信:viplsk\n"),TEXT("關於"),MB_OK);
}
完整下載:http://download.csdn.net/detail/lskforjll/9473203
