0 前言
?? 這兩年開始畢業(yè)設計和畢業(yè)答辯的要求和難度不斷提升,傳統(tǒng)的畢設題目缺少創(chuàng)新和亮點,往往達不到畢業(yè)答辯的要求,這兩年不斷有學弟學妹告訴學長自己做的項目系統(tǒng)達不到老師的要求。
為了大家能夠順利以及最少的精力通過畢設,學長分享優(yōu)質畢業(yè)設計項目,今天要分享的是
?? 基于stm32的實現車牌識別系統(tǒng)
??學長這里給一個題目綜合評分(每項滿分5分)
- 難度系數:4分
- 工作量:4分
- 創(chuàng)新點:3分
1 簡介
本系統(tǒng)利用STM32單片機驅動攝像頭采集圖像,通過模式識別,匹配車牌的識別結果,并在屏幕上顯示。
2 主要器件
- STM32F103RCT6 主控芯片
- TFT液晶屏
- OV7670攝像頭
- 蜂鳴器
- LED電路
3 實現效果
識別效果
攝像頭模塊
4 設計原理
4.1 軟件總體過程:
(1)OV7670帶FIFO攝像頭圖像采集,采用GPIO模擬攝像頭時序,通過讀取FIFO輸出值將圖像直接顯示在LCD屏上
(2)LCD屏相當于一個圖像緩存,同時也做顯示,通過讀取LCD屏上的像素值進行圖像處理;
(3)車牌定位處理,車牌定位常用二值化分割,腐蝕膨脹處理,連通域計算等操作,顯然這些算法在stm32f1上實現是很困難的,且處理速度太慢,因此,采用RGB轉HSV顏色空間變換和閾值選擇進行車牌定位,然后將車牌定位區(qū)域進行二值化處理,不是藍色車牌的部分就是字符區(qū)域;
(4)車牌字符分割處理,字符分割先采用行統(tǒng)計加列統(tǒng)計的方式,確定每行和每列的有效像素和,進一步確定字符區(qū)域;然后進行橫向統(tǒng)計分割,通過每一列的像素和閾值判斷字符的分界線和個數;
(5)車牌歸一化處理,歸一化處理先將每個字符提取出來,然后按照像素值進行橫向和縱向壓縮,最終處理成模板一樣大小的字符;并在液晶屏上保存字符的數據;
(6)模板匹配,將歸一化之后的字符,與模板中的字符通過像素值一一比較,確定相似度最高的字符就是目標值;
4.2 具體解釋
圖像采集
通過OV7670攝像頭進行圖像采集,采集的圖像大小為320*240像素,像素格式為RGB565。每個像素由兩字節(jié)組成,第一字節(jié)的高五位是Red,第一字節(jié)的低三位和第二字節(jié)的高三位組成Green,第二字節(jié)的低五位是Blue。
二值化
二值化就是讓圖像的像素點矩陣中的每個像素點的灰度值為0(黑色)或者255(白色),讓整個圖片呈現出只有黑色和白色的效果。二值化后的圖像中灰度值范圍是0或者255。這時需要設定一個閾值來對像素點進行設置。
常用二值化方法:
- 取中值:設置閾值為127,灰度值小于127的為0,大于127的為255。這樣設置計算量小,計算快。缺點也嚴重:在不同的圖像中,顏色分布差別大,處理效果也不會很好。程序開始之前設置R,G,B的閾值,通過閾值判斷將像素設置為全黑(0x0000)或者全白(0xFFFF).同時根據色彩的變化記錄每一行的顏色跳變點,由此識別出車牌區(qū)域。
- 取平均值:像素點平均值 = (像素點1灰度值 + 像素點2灰度值 + …… + 像素點n灰度值) / n
- 雙峰法:此方法適用于具有明顯雙峰直方圖的圖像,不適合直方圖中雙峰差別很大或雙峰間的谷比較寬廣而平坦的圖像。該方法認為圖像由前景和背景組成,在灰度直方圖上,前景和背景會形成高峰,在雙峰之間的最低谷處就是閾值。
識別車牌區(qū)域
根據上一步的二值化,由于車牌區(qū)域跳變點多,由此可以得出車牌區(qū)域。分別記錄車牌區(qū)域的上下高度。然后通過RGB-HSV顏色轉換,識別出車牌區(qū)域的左右邊界。
字符分割
我國常見車牌以及排列順序大部分都是按照如下設計的:漢字、英文字母、點、英文字母、阿拉伯數字、阿拉伯數字、阿拉伯數字、阿拉伯數字。基于這個規(guī)律,以及圖像采集高度一致,設計了如下的分割方法:
- 在內存中開辟七個長為車牌長的七分之一和寬為車牌寬的區(qū)域
- 從車牌圖像長邊的巾問向下開始掃描車牌圖像,并把掃描到的所有的點灰度值復制到0區(qū)域的第四個區(qū)域對應位置上。然后再從上向下掃描剛掃描過這一努的左邊或右邊,直到所掃描的這一峰上的所有點的灰度都是0時為止,并把這一豎認為是字符的分離處。
- 切割第五到第七個字符。方法就是,切割完了第四個字符之后,再依次掃描剩下的空間,直到所掃描的這一豎上的所有點的灰度值不全為0時,認為是字符的開始并依次掃描直到所掃描的這一豎上的所有點的灰度值全為0時認為是字符的結束。
- 切割第三到第四個字符。這兩個字符的切割方式與第五到第七個字符一樣。
- 切割第一到第二個字符。當第三個字符切割完之后,我們將遇到一個點,我們也把它看作一個字符,只不過這個點掃描之后就不要了。掃描完這個點之后,我們來切割第二個字符,它的切割方式與前面一樣。切割完了第二個字符之后,再向左掃描,直到所掃描的這一豎上的所有點的灰度值不全為0時,認為是字符的開始,并依次掃描直到所掃描所有剩下的,并填到相應的位置,直到剩下的空間填滿。經過粗分割后,可以得到一些單個字符區(qū)域和多余的空間。下一步我們將把這些多余的空間去掉。這將更有利于下一步字符的識別。
- 去除圖像上多余空間:車牌上的字符經過了粗切割所得到的是一些單的字符,但在分配空間時是按照車牌的寬和長的七分之一來分配的;所以這個空間可能大于字符應該占的空問。所以,要將多余空間去除。對于第一個字符從第一行開始向下掃描,把那些一行中所有的點的灰度值全為0的點去掉,直到掃描到有一行不全為0時為止。然后再從第一列開始向右掃描把那些一列中所有的點的灰度值全為0的點去掉,直到掃描到有一列不全為0時為止。接下來從最后一行開始向上掃描,把那些一行中所有的點的灰度值全為0的點去掉,直到掃描到有一行不全為0時為止。最后從最后一列開始向左掃描把那些一列中所有的點的灰度值全為0的點去掉,直到掃描到有一列不全為0時為止。重復上面的步驟完成剩下字符的切割。
- 根據二值化的結果,以及記錄的跳變點位置,對字符進行分割,同時記錄字符的左右邊界。
字符匹配
對分割出來的字符進行歸一化處理,這里用到圖片的擴大算法,擴大之后逐一的去進行字符匹配。字符模板事前通過字模軟件轉換成二進制數據保存在數組中。最后根據匹配結果相似度最大的做為輸出結果。
歸一化圖像就是要把原來各不相同的字符統(tǒng)一到同一尺寸。因為掃描進來的圖像中字符大小存在較大的差異,而相對來說,統(tǒng)一尺寸的字符識別的標準性更強,準確率自然也更高。具體算法如下:先得到原來字符的高度和寬度,與系統(tǒng)已存字模的數據作比較,得出要變換的系數,然后根據得到的系數按照插值的方法映射到原圖像中。
5 部分核心代碼
#define COLOR_RGB565_TO_R8(pixel) \
({ \
__typeof__ (pixel) __pixel = (pixel); \
__pixel = (__pixel >> 8) & 0xF8; \
__pixel | (__pixel >> 5); \
})
#define COLOR_RGB565_TO_G8(pixel) \
({ \
__typeof__ (pixel) __pixel = (pixel); \
__pixel = (__pixel >> 3) & 0xFC; \
__pixel | (__pixel >> 6); \
})
#define COLOR_RGB565_TO_B8(pixel) \
({ \
__typeof__ (pixel) __pixel = (pixel); \
__pixel = (__pixel << 3) & 0xF8; \
__pixel | (__pixel >> 5); \
})
int8_t imlib_rgb565_to_l(uint16_t pixel)
{
float r_lin = xyz_table[COLOR_RGB565_TO_R8(pixel)];
float g_lin = xyz_table[COLOR_RGB565_TO_G8(pixel)];
float b_lin = xyz_table[COLOR_RGB565_TO_B8(pixel)];
float y = ((r_lin * 0.2126f) + (g_lin * 0.7152f) + (b_lin * 0.0722f)) * (1.0f / 100.000f);
y = (y>0.008856f) ? fast_cbrtf(y) : ((y * 7.787037f) + 0.137931f);
return fast_floorf(116 * y) - 16;
}
int8_t imlib_rgb565_to_a(uint16_t pixel)
{
float r_lin = xyz_table[COLOR_RGB565_TO_R8(pixel)];
float g_lin = xyz_table[COLOR_RGB565_TO_G8(pixel)];
float b_lin = xyz_table[COLOR_RGB565_TO_B8(pixel)];
float x = ((r_lin * 0.4124f) + (g_lin * 0.3576f) + (b_lin * 0.1805f)) * (1.0f / 095.047f); float y = ((r_lin * 0.2126f) + (g_lin * 0.7152f) + (b_lin * 0.0722f)) * (1.0f / 100.000f);
x = (x>0.008856f) ? fast_cbrtf(x) : ((x * 7.787037f) + 0.137931f); y = (y>0.008856f) ? fast_cbrtf(y) : ((y * 7.787037f) + 0.137931f);
return fast_floorf(500 * (x-y));
}
int8_t imlib_rgb565_to_b(uint16_t pixel)
{
float r_lin = xyz_table[COLOR_RGB565_TO_R8(pixel)];
float g_lin = xyz_table[COLOR_RGB565_TO_G8(pixel)];
float b_lin = xyz_table[COLOR_RGB565_TO_B8(pixel)];
float y = ((r_lin * 0.2126f) + (g_lin * 0.7152f) + (b_lin * 0.0722f)) * (1.0f / 100.000f); float z = ((r_lin * 0.0193f) + (g_lin * 0.1192f) + (b_lin * 0.9505f)) * (1.0f / 108.883f);
y = (y>0.008856f) ? fast_cbrtf(y) : ((y * 7.787037f) + 0.137931f); z = (z>0.008856f) ? fast_cbrtf(z) : ((z * 7.787037f) + 0.137931f);
return fast_floorf(200 * (y-z));
}
unsigned short xrIntFloat_16=(Src_width<<8)/Dst_width+1; //擴大倍數
unsigned short yrIntFloat_16=(Src_height<<8)/Dst_height+1;
unsigned char* pDstLine=Dst_y8;
unsigned short srcy_16=0;
for (y=0;y<Dst_height;++y)
{
unsigned char* pSrcLine=((unsigned char*)((unsigned char*)Src_y8+Src_width*(srcy_16>>8)));
unsigned short srcx_16=0;
for (x=0;x<Dst_width;++x)
{
pDstLine[x]=pSrcLine[srcx_16>>8];
srcx_16+=xrIntFloat_16;
}
srcy_16+=yrIntFloat_16;
pDstLine+=Dst_width;
}
}
模板匹配算法文章來源:http://www.zghlxwxcb.cn/news/detail-829358.html
/**
* @function 歐幾里得距離計算,用于圖片相似度計算
* @param[in] src1和src2,必須是相同大小灰度圖片
* @param[out] 歐幾里得距離
* @retval ERROR -1 錯誤
*/
float Euclidean_Distance(unsigned char *Src1,unsigned char *Src2,int length,float Euclideandis)
{
if(Src1==NULL||Src2==NULL)
{
return -1;
}
int sum=0;
int i=0;
for(i=0;i<length;i++)
{
sum+=(int)pow((*Src1-*Src2),2);
Src1++;
Src2++;
}
Euclideandis=(float)sqrt(sum);
return Euclideandis;
}
/**
* @function 余弦相似度計算,用于圖片相似度計算
* @param[in] src1和src2,必須是相同大小灰度圖片
* @param[out] 余弦相似度
* @retval 0 相似度小于0
* @retval ERROR -1 錯誤
* @retval CosineSimilar
*/
float Cosine_Similarity(unsigned char *Src1,unsigned char *Src2,int length,float CosineSimilar)
{
if(Src1==NULL||Src2==NULL)
{
return -1;
}
int sum=0,sum1=0,sum2=0;
float temp0=0,temp1=0;
int i=0;
for(i=0;i<length;i++)
{
sum+=(int)(*Src1)*(*Src2);
sum1+=(int)pow((*Src1),2);
sum2+=(int)pow((*Src2),2);
Src1++;
Src2++;
}
if(sum<=0)
{
return 0;
}
temp0=(float)(sqrt(sum1));
temp1=(float)(sqrt(sum2));
CosineSimilar=(float)((sum/temp0)/temp1);
return CosineSimilar;
}
/**
* @function 皮爾遜相似度計算,用于圖片相似度計算
* @param[in] src1和src2,必須是相同大小灰度圖片
* @param[out] 皮爾遜相似度
* @retval ERROR -1 錯誤
* @par 2021年5月28日 zhengmf
*/
float Pearson_Correlation(unsigned char *Src1,unsigned char *Src2,int length,float PearsonSimilar)
{
if(Src1==NULL||Src2==NULL)
{
return -1;
}
unsigned char aver1=0,aver2=0;
int sum=0,sum1=0,sum2=0;
float temp0=0,temp1=0;
int i=0;
for(i=0;i<length;i++)
{
sum1+=*Src1;
sum2+=*Src2;
Src1++;
Src2++;
}
aver1=(unsigned char)(sum1/length);
aver2=(unsigned char)(sum2/length);
sum1=0;
sum2=0;
for(i=0;i<length;i++)
{
sum+=(int)(*Src1-aver1)*(*Src2-aver2);
sum1+=(int)pow((*Src1-aver1),2);
sum2+=(int)pow((*Src2-aver2),2);
Src1++;
Src2++;
}
if(sum<=0)
{
return 0;
}
temp0=(float)(sqrt(sum1));
temp1=(float)(sqrt(sum2));
PearsonSimilar=(float)((sum/temp0)/temp1);
return PearsonSimilar;
}
6 最后
?? 項目分享與指導:https://gitee.com/sinonfin/sharing文章來源地址http://www.zghlxwxcb.cn/news/detail-829358.html
到了這里,關于【單片機畢設選題】stm32實現車牌識別系統(tǒng) -物聯網 嵌入式 單片機的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!