• 正文
    • 1、前言
    • 2、步驟說明
    • 3、區(qū)域識別
    • 4、字符分割
    • 5、字符識別
  • 相關推薦
申請入駐 產業(yè)圖譜

基于STM32F4的車牌識別(1)——車牌區(qū)域識別和字符分割

04/01 11:15
1556
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

1、前言

曾經寫過一篇文章,介紹如何利用ESP32獲取車牌數(shù)據上傳至百度云平臺識別車牌。不過這種方式既需要無線傳輸,還需要額外對車牌識別進行繳費。

本期我們探索如何利用STM32進行車牌識別的本地化部署。這里疊個甲,作者做這塊純屬好奇,不涉及真正的應。

本期硬件采用STM32F4,TFTLCD以及OV2640顯示屏,本來是想用H7的,但是由于不可抗力因素換為使用F4,攝像頭之所以用OV2640,主要是比較便宜,其他型號手上暫時也沒有。

2、步驟說明

如何實現(xiàn)STM32和攝像頭(DCMIPP)的通訊這里不過多贅述,我們主要介紹一下實現(xiàn)車牌識別的步驟,這里我總結為三個部分:

1.車牌區(qū)域識別

2.字符分割

3.字符識別(尚未寫完)

接下來我將逐一介紹其實現(xiàn)方式,以下是區(qū)域識別和字符分割的實現(xiàn)。

目前字符識別還沒做完,所有的流程均有不同時間的延時以便展示。

3、區(qū)域識別

首先就是要如何識別出車牌的有效區(qū)域,我看許多人的做法是進行灰度化之后再進行二值化,再去檢測每行中跳變點的個數(shù)之類的。

不過我覺得跳變點個數(shù)對照片的效果要求太理想了,而且單純的灰度化的話引入的噪聲又比較大。

因此我嘗試著使用藍色閾值+藍色與紅色差異值的方式來進行二值化,總而言之就是盡可能的提取出藍色特征。

for?(x =?0; x <?RGB_X; x++) {? ? ? ??for?(y =?0; y <?RGB_Y; y++) {? ? ? ? ? ? color = rgb_buf[x][y];? ? ? ? ? ? R = (color >>?11) *?255?/?31; ?// 提取紅色分量? ? ? ? ? ? G = ((color >>?5) &?0x3f) *?255?/?63; ?// 提取綠色分量? ? ? ? ? ? B = (color &?0x1f) *?255?/?31; ?// 提取藍色分量? ? ? ? ? ??// 根據藍色和紅色、綠色的差異進行二值化處理? ? ? ? ? ??if?(B > (R + G) *?0.8) { ?// 如果藍色比紅色和綠色的總和大一定比例? ? ? ? ? ? ? ? color =?0xffff; ?// 白色? ? ? ? ? ? }?else?{? ? ? ? ? ? ? ? color =?0x0000; ?// 黑色? ? ? ? ? ? }? ? ? ? ? ??// 更新圖像緩沖區(qū)? ? ? ? ? ? rgb_buf[x][y] = color;? ? ? ? ? ??LCD->LCD_RAM?= rgb_buf[x][y];? ? ? ? ? ??LCD_SetCursor(X_Offset + y, Y_Offset + x);? ? ? ? ? ??LCD_WriteRAM_Prepare();? ? ? ? }? ? }

將RGB三色提取出來后,比較藍色和紅綠值來進行二值化,這樣子可以很好的提取出藍色車牌我們需要的部分。

1

2

不過這個做法也有明顯的缺點:就是只能識別藍色的車牌~~

可以看到二值化后仍然存留著一些噪聲,這里我們可以通過濾波降噪。

void?MedianFilter(u16 (*input)[RGB_Y], u16 (*output)[RGB_Y]){? ? u16 x, y;? ? u16 median[9]; ?// 用于存儲3x3鄰域的像素值? ? u16 temp;
? ??// 處理內部像素(非邊界像素)? ??for?(x =?1; x < RGB_X -?1; x++) {? ? ? ??for?(y =?1; y < RGB_Y -?1; y++) {? ? ? ? ? ??// 獲取3x3鄰域的像素值? ? ? ? ? ? median[0] = input[x-1][y-1];? ? ? ? ? ? median[1] = input[x-1][y];? ? ? ? ? ? median[2] = input[x-1][y+1];? ? ? ? ? ? median[3] = input[x][y-1];? ? ? ? ? ? median[4] = input[x][y];? ? ? ? ? ? median[5] = input[x][y+1];? ? ? ? ? ? median[6] = input[x+1][y-1];? ? ? ? ? ? median[7] = input[x+1][y];? ? ? ? ? ? median[8] = input[x+1][y+1];
? ? ? ? ? ??// 對鄰域像素值進行排序? ? ? ? ? ??for?(u8?i?=?0; i <?9; i++) {? ? ? ? ? ? ? ??for?(u8?j?=?i +?1; j <?9; j++) {? ? ? ? ? ? ? ? ? ??if?(median[i] > median[j]) {? ? ? ? ? ? ? ? ? ? ? ? temp = median[i];? ? ? ? ? ? ? ? ? ? ? ? median[i] = median[j];? ? ? ? ? ? ? ? ? ? ? ? median[j] = temp;? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? }
? ? ? ? ? ??// 取中值作為當前像素的值? ? ? ? ? ? output[x][y] = median[4];? ? ? ? }? ? }? ? }

采用3x3的中值濾波算法降噪,可以有效的降低噪聲。

為了找到車牌區(qū)域,我們可以掃描每行白點最多的一行作為基準,自上,自下分別尋找這個最大值80%的行為哪一行,即確定車牌的上下行。

之后在此基礎上,對列也進行這種操作,從右至左和從左至右找到車牌的左右區(qū)間。

4、字符分割

得到了車牌區(qū)域我們就可以考慮如何分割字符了,這里我采用的策略是從右到左去測量空白間隙。

即利用各字符之間的空隙,這里正好也是因為這個川字,所以考慮從右到左,因為這樣子可以避免去處理川字中間的幾個空隙。

/*? ? ??? ? ? ? 尋找分割線? ? ??? ? ? ? */? ? ? ? col_threshold = max_col_white *?80?/?100;? ? ? ??for(int i = right_col;i>left_col;i--)? ? ? ? {? ? ? ? ??if(YuzhiFlag)//右邊緣? ? ? ? ? {? ? ? ? ? ??if(col_white_counts[i]<max_col_white *?70?/?100)//左邊緣? ? ? ? ? ? {? ? ? ? ? ? ? line[number++] = i;? ? ? ? ? ? ??YuzhiFlag?= !YuzhiFlag;? ? ? ? ? ? }? ? ? ? ? }? ? ? ? ??else? ? ? ? ? {? ? ? ? ? ??if(col_white_counts[i]>max_col_white *?90?/?100)//最后一個字符? ? ? ? ? ? {? ? ? ? ? ? ? line[number++] = i;? ? ? ? ? ? ??YuzhiFlag?= !YuzhiFlag;? ? ? ? ? ? }? ? ? ? ? }? ? ? ??? ? ? ? }? ? ? ? int zifunumber =?0;? ? ? ??POINT_COLOR?=?RED;? ? ? ??for(int i =?0;i<number;i++)? ? ? ? {? ? ? ? ??? ? ? ? ??? ? ? ? ??if(((line[i]-line[i+1])>(right_col-left_col)*5/100)&&zifunumber<6)? ? ? ? ? {? ? ? ? ? ? box.zifu[zifunumber][0] = line[i];? ? ? ? ? ? box.zifu[zifunumber][1] = line[i+1];? ? ? ? ? ??? ? ? ? ? ??LCD_DrawLine(box.zifu[zifunumber][0]+65,top_row+225,box.zifu[zifunumber][0]+65,bottom_row+225);? ? ? ? ? ??LCD_DrawLine(box.zifu[zifunumber][1]+65,top_row+225,box.zifu[zifunumber][1]+65,bottom_row+225);? ? ? ? ? ? zifunumber++;? ? ? ? ? ? i++;? ? ? ? ? ??delay_ms(1000);? ? ? ? ? }? ? ? ? ??else?if(number>=6)? ? ? ? ? {? ? ? ? ? ? box.zifu[zifunumber][0] = line[i];? ? ? ? ? ? box.zifu[zifunumber][1] = left_col+3;? ? ? ? ? ??? ? ? ? ? ??LCD_DrawLine(box.zifu[zifunumber][0]+65,top_row+225,box.zifu[zifunumber][0]+65,bottom_row+225);? ? ? ? ? ??LCD_DrawLine(box.zifu[zifunumber][1]+65,top_row+225,box.zifu[zifunumber][1]+65,bottom_row+225);? ? ? ? ? ??break;? ? ? ? ? ??delay_ms(1000);? ? ? ? ? }? ? ? ? }

這樣子就可以實現(xiàn)分割字符的目的了。

5、字符識別

字符識別這兩天做,大體應該是采用模板識別的策略,利用分割出來的字符和模板的匹配程度實現(xiàn)字符的識別。

這幾天有空在鉆研一下。

相關推薦