这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。
为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是
基于stm32的实现车牌识别系统
学长这里给一个题目综合评分(每项满分5分)
本系统利用STM32单片机驱动摄像头采集图像,通过模式识别,匹配车牌的识别结果,并在屏幕上显示。
识别效果
摄像头模块
(1)OV7670带FIFO摄像头图像采集,采用GPIO模拟摄像头时序,通过读取FIFO输出值将图像直接显示在LCD屏上
(2)LCD屏相当于一个图像缓存,同时也做显示,通过读取LCD屏上的像素值进行图像处理;
(3)车牌定位处理,车牌定位常用二值化分割,腐蚀膨胀处理,连通域计算等操作,显然这些算法在stm32f1上实现是很困难的,且处理速度太慢,因此,采用RGB转HSV颜色空间变换和阈值选择进行车牌定位,然后将车牌定位区域进行二值化处理,不是蓝色车牌的部分就是字符区域;
(4)车牌字符分割处理,字符分割先采用行统计加列统计的方式,确定每行和每列的有效像素和,进一步确定字符区域;然后进行横向统计分割,通过每一列的像素和阈值判断字符的分界线和个数;
(5)车牌归一化处理,归一化处理先将每个字符提取出来,然后按照像素值进行横向和纵向压缩,最终处理成模板一样大小的字符;并在液晶屏上保存字符的数据;
(6)模板匹配,将归一化之后的字符,与模板中的字符通过像素值一一比较,确定相似度最高的字符就是目标值;
图像采集
通过OV7670摄像头进行图像采集,采集的图像大小为320*240像素,像素格式为RGB565。每个像素由两字节组成,第一字节的高五位是Red,第一字节的低三位和第二字节的高三位组成Green,第二字节的低五位是Blue。
二值化
二值化就是让图像的像素点矩阵中的每个像素点的灰度值为0(黑色)或者255(白色),让整个图片呈现出只有黑色和白色的效果。二值化后的图像中灰度值范围是0或者255。这时需要设定一个阈值来对像素点进行设置。
常用二值化方法:
识别车牌区域
根据上一步的二值化,由于车牌区域跳变点多,由此可以得出车牌区域。分别记录车牌区域的上下高度。然后通过RGB-HSV颜色转换,识别出车牌区域的左右边界。
字符分割
我国常见车牌以及排列顺序大部分都是按照如下设计的:汉字、英文字母、点、英文字母、阿拉伯数字、阿拉伯数字、阿拉伯数字、阿拉伯数字。基于这个规律,以及图像采集高度一致,设计了如下的分割方法:
字符匹配
对分割出来的字符进行归一化处理,这里用到图片的扩大算法,扩大之后逐一的去进行字符匹配。字符模板事前通过字模软件转换成二进制数据保存在数组中。最后根据匹配结果相似度最大的做为输出结果。
归一化图像就是要把原来各不相同的字符统一到同一尺寸。因为扫描进来的图像中字符大小存在较大的差异,而相对来说,统一尺寸的字符识别的标准性更强,准确率自然也更高。具体算法如下:先得到原来字符的高度和宽度,与系统已存字模的数据作比较,得出要变换的系数,然后根据得到的系数按照插值的方法映射到原图像中。
#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;
}
}
模板匹配算法
/**
* @function 欧几里得距离计算,用于图片相似度计算
* @param[in] src1和src2,必须是相同大小灰度图片
* @param[out] 欧几里得距离
* @retval ERROR -1 错误
* @par 2021年5月28日 zhengmf
*/
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
* @par 2021年5月28日 zhengmf
*/
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;
}