在車牌識別系統中, 車牌字符能夠正確分割的前提是車牌圖像能夠水平,以至於水平投影和垂直投影能夠正常進行。如果車牌傾斜沒有矯正,那么水平投影和垂直投影,甚至鉚釘都無法正常處理。所以,當車輛信息中獲取車牌的第一步,應該是檢查傾斜角度,做傾斜矯正。
傾斜矯正,這里使用的算法:
1、傾斜角度檢測: 霍夫變換
關於hough變換,可以參考前面圖像處理博文:
http://blog.csdn.net/liujia2100/article/details/6989693 直線檢測
http://blog.csdn.net/liujia2100/article/details/6989688 文本圖像傾斜矯正
2、傾斜矯正: 圖像旋轉
下面詳細說明傾斜矯正過程:
原車牌圖像為(從車牌圖像中,可以看到車牌有傾斜角度):
1、 獲取車牌在車輛中的粗略位置(可以用多種方法,這里暫不分析)
2、提取車牌整體圖片數據, 根據第一步結果,提取出,車牌在輛大體位置信息。
關於車牌定位,我使用兩部,第一步粗略定位,然后做一些預處理,比如傾斜矯正,然后第二部才是精確定位,只提取車牌的位置信息圖像
3、利用HSV顏色空間轉換,獲取車牌背景藍色區域位置,獲取車牌粗略信息圖像后,由於車牌背景顏色與周圍顏色有很明顯的區別,這里采用HSV顏色過濾的方法,過濾綠色背景圖像
4、水平膨脹, 水平膨脹的目的,是為了邊緣檢測,只要求檢測邊緣,盡量除去字符信息,也可以降低hough變換的運算量
5、水平差分運算,相當於 邊緣檢測,經過上面的處理后,才進行邊緣檢測
6、這個時候就可以利用hough變換檢測直線了。
由於hough變換運算量十分大,所以,盡量減少圖像中的白點,來降低計算量,因此前面才做了這么多步驟。
請看下圖的紅線,就是檢測出來的角度,為177度(Hough代碼在下面)。
7、利用旋轉算法,旋轉剛才粗略提取的車牌位置(旋轉代碼在下面),盡管旋轉后的車牌有些鋸齒,但是已經能夠保證水平,就可以使用水平投影和垂直投影了
這是旋轉后的車牌,有些鋸齒出現,由於圖像分辨率較低,就沒有用差值運算。
8、精確提取車牌
9、正常分割字符
10、識別結果
由於正弦余弦運算,計算量比較大,這里進行一部分優化,就是正弦余弦計算用數組代替。
生成正弦,余弦數組的的代碼如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> int main(void) { char buf[20]; int i; float p; float k; FILE *fcos; FILE *fsin; fcos = fopen(".\\cos.txt", "wb"); fsin = fopen(".\\sin.txt", "wb"); if(fcos == NULL || fsin == NULL) { printf("open error\n"); exit(-1); } i = 0; for(i = 0; i <= 180; i++) { k = 3.1415926 * i / 180.0; p = cos(k); if((i%16 == 0)) fwrite("\n",strlen("\n"),1,fcos); sprintf(buf,"%f, ", p); fwrite(buf,strlen(buf),1,fcos); } for(i = 0; i <= 180; i++) { k = 3.1415926 * i / 180.0; p = sin(k); if((i%16 == 0)) fwrite("\n",strlen("\n"),1,fsin); sprintf(buf,"%f, ", p); fwrite(buf,strlen(buf),1,fsin); } fclose(fcos); fclose(fsin); return 0; }
生成數組為:
float carCos[] = { 1.000000, 0.999848, 0.999391, 0.998630, 0.997564, 0.996195, 0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627, 0.978148, 0.974370, 0.970296, 0.965926, 0.961262, 0.956305, 0.951057, 0.945519, 0.939693, 0.933580, 0.927184, 0.920505, 0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.874620, 0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152, 0.809017, 0.798636, 0.788011, 0.777146, 0.766044, 0.754710, 0.743145, 0.731354, 0.719340, 0.707107, 0.694658, 0.681998, 0.669131, 0.656059, 0.642788, 0.629320, 0.615662, 0.601815, 0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038, 0.500000, 0.484810, 0.469472, 0.453991, 0.438371, 0.422618, 0.406737, 0.390731, 0.374607, 0.358368, 0.342020, 0.325568, 0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951, 0.207912, 0.190809, 0.173648, 0.156434, 0.139173, 0.121869, 0.104528, 0.087156, 0.069757, 0.052336, 0.034900, 0.017452, 0.000000, -0.017452, -0.034899, -0.052336, -0.069756, -0.087156, -0.104528, -0.121869, -0.139173, -0.156434, -0.173648, -0.190809, -0.207912, -0.224951, -0.241922, -0.258819, -0.275637, -0.292372, -0.309017, -0.325568, -0.342020, -0.358368, -0.374607, -0.390731, -0.406737, -0.422618, -0.438371, -0.453990, -0.469472, -0.484810, -0.500000, -0.515038, -0.529919, -0.544639, -0.559193, -0.573576, -0.587785, -0.601815, -0.615661, -0.629320, -0.642788, -0.656059, -0.669131, -0.681998, -0.694658, -0.707107, -0.719340, -0.731354, -0.743145, -0.754710, -0.766044, -0.777146, -0.788011, -0.798635, -0.809017, -0.819152, -0.829038, -0.838671, -0.848048, -0.857167, -0.866025, -0.874620, -0.882948, -0.891007, -0.898794, -0.906308, -0.913545, -0.920505, -0.927184, -0.933580, -0.939693, -0.945519, -0.951056, -0.956305, -0.961262, -0.965926, -0.970296, -0.974370, -0.978148, -0.981627, -0.984808, -0.987688, -0.990268, -0.992546, -0.994522, -0.996195, -0.997564, -0.998630, -0.999391, -0.999848, -1.000000 }; float carSin[] = { 0.000000, 0.017452, 0.034899, 0.052336, 0.069756, 0.087156, 0.104528, 0.121869, 0.139173, 0.156434, 0.173648, 0.190809, 0.207912, 0.224951, 0.241922, 0.258819, 0.275637, 0.292372, 0.309017, 0.325568, 0.342020, 0.358368, 0.374607, 0.390731, 0.406737, 0.422618, 0.438371, 0.453990, 0.469472, 0.484810, 0.500000, 0.515038, 0.529919, 0.544639, 0.559193, 0.573576, 0.587785, 0.601815, 0.615661, 0.629320, 0.642788, 0.656059, 0.669131, 0.681998, 0.694658, 0.707107, 0.719340, 0.731354, 0.743145, 0.754710, 0.766044, 0.777146, 0.788011, 0.798635, 0.809017, 0.819152, 0.829038, 0.838671, 0.848048, 0.857167, 0.866025, 0.874620, 0.882948, 0.891007, 0.898794, 0.906308, 0.913545, 0.920505, 0.927184, 0.933580, 0.939693, 0.945519, 0.951056, 0.956305, 0.961262, 0.965926, 0.970296, 0.974370, 0.978148, 0.981627, 0.984808, 0.987688, 0.990268, 0.992546, 0.994522, 0.996195, 0.997564, 0.998630, 0.999391, 0.999848, 1.000000, 0.999848, 0.999391, 0.998630, 0.997564, 0.996195, 0.994522, 0.992546, 0.990268, 0.987688, 0.984808, 0.981627, 0.978148, 0.974370, 0.970296, 0.965926, 0.961262, 0.956305, 0.951057, 0.945519, 0.939693, 0.933580, 0.927184, 0.920505, 0.913545, 0.906308, 0.898794, 0.891007, 0.882948, 0.874620, 0.866025, 0.857167, 0.848048, 0.838671, 0.829038, 0.819152, 0.809017, 0.798636, 0.788011, 0.777146, 0.766044, 0.754710, 0.743145, 0.731354, 0.719340, 0.707107, 0.694658, 0.681998, 0.669131, 0.656059, 0.642788, 0.629320, 0.615662, 0.601815, 0.587785, 0.573576, 0.559193, 0.544639, 0.529919, 0.515038, 0.500000, 0.484810, 0.469472, 0.453991, 0.438371, 0.422618, 0.406737, 0.390731, 0.374607, 0.358368, 0.342020, 0.325568, 0.309017, 0.292372, 0.275637, 0.258819, 0.241922, 0.224951, 0.207912, 0.190809, 0.173648, 0.156435, 0.139173, 0.121869, 0.104529, 0.087156, 0.069757, 0.052336, 0.034900, 0.017452, 0.000000 };
旋轉RGB圖像的主要函數:
image: 圖像數據
iRotateAngle: 要旋轉的角度
width, height:原始圖像的寬度,高度
lwidth,lheight:旋轉后圖像的寬度,高度
unsigned char *RotateRGB(unsigned char *image, float iRotateAngle,int width,int height,int &lwidth,int &lheight) { int i,j,k,m,n; long lNewWidth; long lNewHeight; float gray; long i0; long j0; float fRotateAngle; float fSina, fCosa; float fSrcX1,fSrcY1,fSrcX2,fSrcY2,fSrcX3,fSrcY3,fSrcX4,fSrcY4; float fDstX1,fDstY1,fDstX2,fDstY2,fDstX3,fDstY3,fDstX4,fDstY4; float f1,f2; if(iRotateAngle >= 0) { fSina = (float)carSin[(int)iRotateAngle]; fCosa = (float)carCos[(int)iRotateAngle]; } else { fSina = 0 - (float)carSin[0 -(int)iRotateAngle]; fCosa = (float)carCos[0 - (int)iRotateAngle]; } fSrcX1 = (float) (- (width - 1) / 2); fSrcY1 = (float) ( (height - 1) / 2); fSrcX2 = (float) ( (width - 1) / 2); fSrcY2 = (float) ( (height - 1) / 2); fSrcX3 = (float) (- (width - 1) / 2); fSrcY3 = (float) (- (height - 1) / 2); fSrcX4 = (float) ( (width - 1) / 2); fSrcY4 = (float) (- (height - 1) / 2); fDstX1 = fCosa * fSrcX1 + fSina * fSrcY1; fDstY1 = -fSina * fSrcX1 + fCosa * fSrcY1; fDstX2 = fCosa * fSrcX2 + fSina * fSrcY2; fDstY2 = -fSina * fSrcX2 + fCosa * fSrcY2; fDstX3 = fCosa * fSrcX3 + fSina * fSrcY3; fDstY3 = -fSina * fSrcX3 + fCosa * fSrcY3; fDstX4 = fCosa * fSrcX4 + fSina * fSrcY4; fDstY4 = -fSina * fSrcX4 + fCosa * fSrcY4; lNewWidth = (long) ( max( fabs(fDstX4 - fDstX1), fabs(fDstX3 - fDstX2) ) + 0.5); lNewHeight = (long) ( max( fabs(fDstY4 - fDstY1), fabs(fDstY3 - fDstY2) ) + 0.5); unsigned char *temp=myMalloc(lNewHeight*lNewWidth*3,0,0); f1 = (float) (-0.5 * (lNewWidth - 1) * fCosa - 0.5 * (lNewHeight - 1) * fSina + 0.5 * (width - 1)); f2 = (float) ( 0.5 * (lNewWidth - 1) * fSina - 0.5 * (lNewHeight - 1) * fCosa + 0.5 * (height - 1)); for(i = 0; i < lNewHeight; i++) { for(m=0,j = 0;j < lNewWidth,m<lNewWidth*3;m+=3,j++) { i0 = (long) (-((float) j) * fSina + ((float) i) * fCosa + f2 + 0.5); j0 = (long) ( ((float) j) * fCosa + ((float) i) * fSina + f1 + 0.5); if( (j0 >= 0) && (j0 < width) && (i0 >= 0) && (i0 < height)) { n=i0 * width * 3 + j0 * 3; *(temp + lNewWidth * i * 3 + m + 1) = *(image + n + 1); *(temp + lNewWidth * i * 3 + m + 2) = *(image + n + 2); *(temp + lNewWidth * i * 3 + m) = *(image + n); } else { *(temp + lNewWidth * i*3+ m+1)=0; *(temp + lNewWidth * i*3+ m+2)=0; *(temp + lNewWidth * i*3+ m)=0; } } } lwidth = lNewWidth; lheight = lNewHeight; return temp; }
Hough變化的主要函數
返回值Kmax,就是檢測到最長直線的角度,就是車牌的傾斜角度。
int hough(unsigned char *srcBmp,int width,int height) { int kmax=0; int pmax=0; int yuzhi=0; int i,j,k,m,n,p; int mp = (int) (sqrt(width*width + height*height)+1); int ma = 180;//180 int ap; int npp[180][1000]; for(i=0;i<180;i++) for(j=0;j<1000;j++) npp[i][j]=0; for(i = 0;i < height;i++) for(j = 0; j < width;j++) { if(srcBmp[i * width + j]==255) { for(k = 0; k < ma; k++) { p=(int)(i * carCos[k] + j * carSin[k]); p=(int)(p/2 + mp/2); npp[k][p]=npp[k][p]++; } } } kmax=0; pmax=0; n=0; for(i = 0; i < ma; i++) for(j = 0; j < mp; j++) { if(npp[i][j] > yuzhi) { yuzhi=npp[i][j]; kmax=i; pmax=j; } } for(i = 0; i < height;i++) for(j = 0; j < width;j++) { if(srcBmp[i*width+j]==255) { p=(int)(i*carCos[kmax] + j *carSin[kmax]); p=(int)(p/2+mp/2); #if defined(DISPLAYDEBUG) if(p==pmax) putpixel(j,i,RGB(255,0,0)); #endif } } return kmax; }