基于stm32的简单车牌识别原理
1.车牌识别基本原理本文讲述的是基于stm32单片机的车牌识别原理,包括车牌图像定位,字符分割,字符归一化处理,以及模板匹配等操作,当然如果芯片处理性能足够还可以加入旋转矫正,多车牌处理等,本文尽量采用最简单易懂的方法进行图像分割处理,同时加快处理速度。首先,讲一讲网络上大部分stm32F103车牌识别的硬件和软件实现,硬件采用的是stm32f103rct6+OV7670&FIFO+16b
前言
车牌识别的目的是交通牌照识别系统,用于交通违法拍照,停车缴费等交通应用场景,车牌识别的流程可分为车牌定位和字符识别两个部分,传统的车牌定位算法常采用二值化分割加上边缘提取,然后进行目标晒选,常采用连线法和车牌宽高比确定真实车牌,定位到车牌位置后再进行OCR字符分割,为了高精度的车牌定位和字符识别,目前引入了深度学习算法进行精准识别和处理。但是,本文所要讲述的是一种基于stm32单片机的车牌识别实现,使用上述高级算法和复杂计算是不现实的,因此,总结出一套可行的,能简单进行车牌定位和字符分割识别的算法,供大家参考和学习。
1.车牌识别基本原理
本文讲述的是基于stm32单片机的车牌识别原理,包括车牌图像定位,字符分割,字符归一化处理,以及模板匹配等操作,当然如果芯片处理性能足够还可以加入旋转矫正,多车牌处理等,本文尽量采用最简单易懂的方法进行图像分割处理,同时加快处理速度。
首先,讲一讲网络上大部分stm32F103车牌识别的硬件和软件实现,硬件采用的是stm32f103rct6+OV7670&FIFO+16bit并口LCD屏;72M主频,不支持浮点运算等操作;
软件实现过程大致如下:
(1)OV7670带FIFO摄像头图像采集,采用GPIO模拟摄像头时序,通过读取FIFO输出值将图像直接显示在LCD屏上
(2)LCD屏相当于一个图像缓存,同时也做显示,通过读取LCD屏上的像素值进行图像处理;
(3)车牌定位处理,车牌定位常用二值化分割,腐蚀膨胀处理,连通域计算等操作,显然这些算法在stm32f1上实现是很困难的,且处理速度太慢,因此,采用RGB转HSV颜色空间变换和阈值选择进行车牌定位,然后将车牌定位区域进行二值化处理,不是蓝色车牌的部分就是字符区域;
(4)车牌字符分割处理,字符分割先采用行统计加列统计的方式,确定每行和每列的有效像素和,进一步确定字符区域;然后进行横向统计分割,通过每一列的像素和阈值判断字符的分界线和个数;
(5)车牌归一化处理,归一化处理先将每个字符提取出来,然后按照像素值进行横向和纵向压缩,最终处理成模板一样大小的字符;并在液晶屏上保存字符的数据;
(6)模板匹配,将归一化之后的字符,与模板中的字符通过像素值一一比较,确定相似度最高的字符就是目标值;
上述方法简单有效,但是对于图像模糊,杂色较多以及光照条件不同的情况下,识别效果会很差。
2.基于stm32的车牌识别算法
2.1基本硬件
硬件采用stm32f407vet6(168M主频,192KB RAM)+OV7670无fifo+SPI-LCD(采用F407及以上具有DCMI数字摄像头接口的主芯片);处理速度和内存都比F103强太多,因此不用采用读取像素值的方式,甚至不用液晶屏显示也能进行识别处理。
2.2识别算法流程
将车牌识别分为四个步骤,分别是双HSV颜色阈值定位,车牌旋转矫正,字符分割和提取,以及最终的车牌字符识别,全部采用的是传统图像处理算法,适合单片机运行。
(1)基于双阈值HSV颜色空间的车牌定位
首先,我们的车牌区域是具有双阈值特性的,蓝牌车具有蓝底白字的特性,绿牌车具有绿底黑字的特性,这种特性使得基于颜色空间进行定位分割是非常方便的(虽然受环境光照影响可能阈值不太准确,但是这种方法是最简单且有效的),通过颜色阈值分割之后,再根据车牌长宽比进行真实车牌目标的定位。
(2)基于霍夫变换的车牌矫正
对于定位之后的车牌,再进行二值化处理和边界计算,可以得到车牌的边界线,根据霍夫变换寻找车牌边界上的最长线段,计算线段的偏转角度进行车牌旋转矫正,矫正之后的车牌区域再进行字符分割,能增加准确率。通过矫正之后,建议先将车牌图像进行归一化处理,即,将车牌区域进行剪裁和缩放到固定大小,便于后续处理。
(3)字符分割和提取
首先对二值化的车牌进行滤波处理,去除一些杂质,然后根据线段分割法,计算车牌水平和垂直方向投影的像素线段长度,可以分割出每个字符所在的区域,但是由于车牌的边缘固定件,车牌污渍的影响,此时需要做非字符区域去除,首先去除车牌固定件影响的区域,通过水平投影线计算车牌的上下界限,将车牌外部的边缘排除出去,然后进行局部边缘滤波,去除污渍,最后车牌部分分割,并提取出来进行下一步处理;
常用简单滤波方法有,中值滤波,局部均方差滤波,高斯滤波,盒子滤波,拉普拉斯边缘增强等等,边缘提取算法有sobel算子,pewrit算子,canny算子等,可在车牌字符区域处理过程中综合运用
(4)字符识别
将车牌部分字符提取出来后,先将字符做归一化处理,采用图像缩放算法进行字符大小调整到统一尺寸,然后做字符指纹图转换(将8bit转换成1bit,便于字符指纹比对),最后采用模板匹配算法计算目标字符与模板的相似度,这里可以采用逐一像素比对法,也可以采用余弦距离计算,最后将相似度最高的模板做标记,车牌的全部字符处理完成可得到最终的结果。
2.3 图像处理算法
以下讲述几种可用于车牌图像处理的算法,其中LAB颜色阈值处理可比HSV颜色阈值精准度更好一些,可做参考
(1)RGB转LAB颜色空间
直接查表法最快最简单,这里直接引用openmv中的算法,具体可以在openmv 的github上查看。
#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));
}
3)领域插值法图像缩放
void PicZoom_y8(unsigned char* Dst_y8,unsigned short Dst_width,unsigned short Dst_height, unsigned char*Src_y8,unsigned short Src_width,unsigned short Src_height)
{
unsigned short y=0;
unsigned short x=0;
if ((0Dst_width)||(0Dst_height)||(0Src_width)||(0Src_height)) return;
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;
}
}
(4)模板匹配算法
/**
* @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;
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)