車牌定位是車牌識別中第一步,也是最重要的一步。
由于中國車牌種類多樣,顏色不一, 再加上車牌經常有污損,以及車牌周圍干擾因素太多,都成為了車牌定位的難點。
這里首先使用最簡單算法來描述車牌定位,以及他的缺陷和改進。
一、投影法
1、車輛圖像信息獲取

2、HSV顏色轉換
把RGB數據轉換成HSV空間圖像數據
hsvzation(image,hsv,width,height);
3、HSV顏色過濾
設置藍色車牌底色閾值范圍,進行顏色過濾
藍色車牌
H值范圍:190 ~ 245
S值范圍: 0.35 ~ 1
V值范圍: 0.3 ~ 1
過濾后圖像如下:

4、噪聲處理
過濾后,一般要進行去噪處理,這里早點不明顯,如果車牌周圍有藍色物體,噪點就非常明顯了
這里使用平均去噪,一些孤立白點將被去除,效果如下:

5、邊緣檢測
去噪后,進行邊緣檢測,邊緣檢測的目的就是為了突出車牌信息的突變,因為車牌背景和字體顏色區分開了;
這樣做的目的也是為了防止周圍有和車牌底色相同顏色的物體干擾,尤其是車輛顏色,因為經過邊緣檢測后車體顏色沒有那么多跳變干擾或者與車牌跳變規律不一樣,這樣就可以濾去車體顏色,去除干擾

6、確定車牌位置
通過水平投影和垂直投影確定車牌位置。
(這里投影方法有很大缺陷,在車牌周圍除了車輛還有其他背景信息時尤為明顯,這在下面的另一種方法中改善)

7、截取車牌圖像

二、投影法定位缺陷示例
1、讀取復雜背景的車牌圖像
2、HSV濾波
HSV過濾后可以看到明顯的干擾信息,車輛后面的欄桿,和車輛同樣的顏色

3、均值去噪
去噪后,雖然大部分噪點都去除了,但是欄桿依然清晰

4、邊緣檢測
邊緣檢測后的圖像,車牌區域的形狀特征,給我們解決投影缺陷的一些啟示

5,、 投影后錯誤的定位

6、車牌提取錯誤
后半塊車身,比例也不符合車牌特征

由此看見,在復雜背景的車牌識別中,全局投影就無法抑制干擾,在下面黃色車牌示例中就更加明顯;
投影法簡單,但只是在只有整體車輛信息,沒有復雜背景信息的時候才可以使用。
三、基于候選區域判斷方法
這個方法放棄了投影,直接遍歷整個圖像信息,檢查每個聯通域的長度和寬度,當符合車牌的寬高比時,才選定為候選區域,由后面處理流程進行處理,來判斷是否能夠提取正常的字符。
前兩個步驟還是同上面一樣的。
1、HSV空間轉換

2、均值去噪

3、水平膨脹
目的:盡可能的形成連通域

4、邊緣檢測
也可以不用邊緣檢測,這里主要考慮到 盡量減少 圖像白點 遍歷中的運算

5、候選區域篩選
候選區域篩選,?這是區別投影方法的主要部分,
從上圖可以看出有幾個候選區域,大概有4塊, 而中間的車牌有明顯矩形特征,符合一定的長寬比例, 其他三塊區域不具備這樣的特征,在篩選的過程中予以舍棄。
篩選的方法采用 深度優先遍歷, 當遇到連通域時,記錄他的長度和高度,并設定閾值,當符合長寬比時,才會選中為候選區域,否則予以舍棄,當然還可以添加別的算法,比如檢測跳變,畢竟符合這一比率的不一定就是車牌區域。
下圖中黃框就是篩選后的區域:

連通域篩選函數如下:
- int?find_connected_region_location(struct?BMP_img?*img,?unsigned?char?*src,?int?xthreashold,?int?ythreashold,?float?rateLow,?float?rateHigh)??
- {??
- ????int?i,j;??
- ????int?x1,?y1,?x2,?y2;??
- ????int?width;??
- ????int?height;??
- ????unsigned?char?*temp;??
- ??????
- ????int?head,?rear;??
- ????struct?XY_Queue?*queue;??
- ????static?int?direction[4][2]={{1,?0},?{-1,?0},?{0,?1},?{0,?-1}};??
- ??
- ????width?=?img->width;??
- ????height?=?img->height;??
- ??
- ????queue?=?(struct?XY_Queue?*)malloc(sizeof(struct?XY_Queue)?*?width?*?height);??
- ??
- ????temp?=?(unsigned?char?*)malloc(width?*?height?*?sizeof(unsigned?char));??
- ??
- ????if(temp?==?NULL)??
- ????{??
- ????????printf("find_connected_region_location?mem?alloc?fail\n");??
- ????????return?-1;??
- ????}??
- ????memcpy(temp,?src,?width?*?height);??
- ??
- ????head?=?rear?=?0;??
- ????img->region_num?=?0;??
- ??
- ????for(i?=?0;?i?<?height;?i++)??
- ????????for(j?=?0;?j?<?width;?j++)??
- ????{??
- ????????if(temp[i?*?width?+?j]?==?255)??
- ????????{??
- ??????????????????????
- ????????????queue[rear].x?=?j;??
- ????????????queue[rear].y?=?i;??
- ????????????rear?++;??
- ????????????temp[i?*?width?+?j]?=?0;??
- ??
- ????????????img->pre_region[img->region_num].x1?=?j;??
- ????????????img->pre_region[img->region_num].x2?=?j;??
- ????????????img->pre_region[img->region_num].y1?=?i;??
- ????????????img->pre_region[img->region_num].y2?=?i;??
- ??
- ????????????if(img->region_num?>?CAN_REGION_NUM)??
- ????????????{??
- ????????????????printf("over?the?CAN_REGION_NUM\n");??
- ????????????????return?-1;??
- ????????????}??
- ??
- ????????????while(head?<?rear)??
- ????????????{??
- ????????????????x1?=?queue[head].x;??
- ????????????????y1?=?queue[head].y;??
- ????????????????head?++;??
- ??
- ????????????????if(x1?<?img->pre_region[img->region_num].x1)??
- ????????????????????img->pre_region[img->region_num].x1?=?x1;??
- ????????????????else?if(x1?>?img->pre_region[img->region_num].x2)??
- ????????????????????img->pre_region[img->region_num].x2?=?x1;??
- ????????????????if(y1?<?img->pre_region[img->region_num].y1)??
- ????????????????????img->pre_region[img->region_num].y1?=?y1;??
- ????????????????else?if(y1?>?img->pre_region[img->region_num].y2)??
- ????????????????????img->pre_region[img->region_num].y2?=?y1;??
- ??????????????????
- ??????
- ????????????????for(i?=?0;?i?<?4;?i++)??
- ????????????????{??
- ????????????????????x2?=?x1?+?direction[i][0];??
- ????????????????????y2?=?y1?+?direction[i][1];??
- ??
- ????????????????????if(x2?>?0?&&?x2?<?width?&&?y2?>?0?&&?y2?<?height?&&?temp[y2?*?width?+?x2])??
- ????????????????????{??
- ????????????????????????temp[y2?*?width?+?x2]?=?0;??
- ????????????????????????queue[rear].x?=?x2;??
- ????????????????????????queue[rear].y?=?y2;??
- ????????????????????????rear?++;??
- ????????????????????}??
- ??????????????
- ????????????????}??
- ????????????}??
- ????????????if((img->pre_region[img->region_num].x2?-?img->pre_region[img->region_num].x1?>?xthreashold)?&&?(img->pre_region[img->region_num].y2?-?img->pre_region[img->region_num].y1?>?ythreashold))??
- ????????????{??
- ??
- ????????????????img->pre_region[img->region_num].width?=?img->pre_region[img->region_num].x2?-?img->pre_region[img->region_num].x1?+?1;??
- ????????????????img->pre_region[img->region_num].height?=?img->pre_region[img->region_num].y2?-?img->pre_region[img->region_num].y1?+?1;??
- ????????????????img->pre_region[img->region_num].rate?=?(float)img->pre_region[img->region_num].width/img->pre_region[img->region_num].height;??
- ????????????????if((img->pre_region[img->region_num].width?<?img->width?/?2)?&&?(img->pre_region[img->region_num].height?<?img->height?/?2))??
- ????????????????if((img->pre_region[img->region_num].rate?>?rateLow)?&&?(img->pre_region[img->region_num].rate?<?rateHigh))??
- ????????????????{??
- ????????????????????if(img->pre_region[img->region_num].x2?+?PRE_LOCATION_BIAS?>?img->width)??
- ????????????????????????????img->pre_region[img->region_num].x2?=?img->width;??
- ????????????????????else??
- ????????????????????????????img->pre_region[img->region_num].x2?+=?PRE_LOCATION_BIAS;??
- ????????????????????if(img->pre_region[img->region_num].x1?-?PRE_LOCATION_BIAS?<?0)??
- ????????????????????????????img->pre_region[img->region_num].x1?=?0;??
- ????????????????????else??
- ????????????????????????????img->pre_region[img->region_num].x1?-=?PRE_LOCATION_BIAS;??
- ????????????????????if(img->pre_region[img->region_num].y2?+?PRE_LOCATION_BIAS?>?img->height)??
- ????????????????????????????img->pre_region[img->region_num].y2?=?img->height;??
- ????????????????????else??
- ????????????????????????????img->pre_region[img->region_num].y2?+=?PRE_LOCATION_BIAS;??
- ????????????????????if(img->pre_region[img->region_num].y1?-?PRE_LOCATION_BIAS?<?0)??
- ????????????????????????????img->pre_region[img->region_num].y1?=?0;??
- ????????????????????else??
- ????????????????????????????img->pre_region[img->region_num].y1?-=?PRE_LOCATION_BIAS;??
- ??
- ????????????????????img->pre_region[img->region_num].width?=?img->pre_region[img->region_num].x2?-?img->pre_region[img->region_num].x1?+?1;??
- ????????????????????img->pre_region[img->region_num].height?=?img->pre_region[img->region_num].y2?-?img->pre_region[img->region_num].y1?+?1;??
- ??????????????
- ????????????????????img->region_num++;??
- ????????????????}??
- ????????????}??
- ????????}??
- ??????????
- ??????????
- ????}??
- ????free(temp);??
- ????temp?=?NULL;??
- ????return?0;??
- }??
6、截取車牌區域圖像

四、黃色車牌檢測
1、車輛圖像信息

2、HSV過濾分割
由于車牌顏色 與 車輛顏色一直,出現大量噪聲信息,全局投影已不可能分割出車牌信息了,
這里只是 利用候選區域長寬比來進行矩形分割,肯定會出現一些符合比例但是不是車牌的區域,必須在后面的處理中加以區分或者添加判斷跳變規律的函數

3、去噪

4、 膨脹

6、邊緣檢測

7、候選區域 連通域篩選
從圖中可以看到黃框 部分即是符合候選區域的地方

8、截取候選區域
此候選區域只是符合長寬比,需要另行處理 除去不是車牌的區域

五、小結
車牌定位比較復雜,但對于車牌識別來說,最為重要,我認為它是影響車牌識別最大因素。雖然復雜,但是方法多種多樣。
這里僅此個人愛好和研究,希望各位朋友繼續提出批評和建議,大家的鼓勵給了我堅持下去的勇氣。