stm32F4车牌识别原理

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.基于stm32f407的车牌识别
2.1基本硬件
硬件采用stm32f407vet6(168M主频,192KB RAM)+OV7670无fifo+SPI-LCD;处理速度和内存完全够处理车牌图像,因此不用采用读取像素值的方式,甚至不用液晶屏显示也能进行识别处理。
2.2识别算法流程
(1)基于LAB颜色空间的车牌定位
LAB颜色空间较HSV,HSL颜色空间抗干扰性更好,适应的环境和光照条件也较好,因此使用LAB颜色空间进行车牌定位效果比传统HSV方式更加有效;
(2)基于LAB颜色空间的字符分割
使用LAB颜色空间在车牌区域进行连通域计算,能更加精确的找出所有字符的位置和区域,然后再通过二值化分割和滤波处理,将字符提取出来;
(3)基于图像缩放的字符归一化处理
采用领域插值法能快速进行图像缩放处理,将字符图像缩放到模板大小,然后再进行像素归一化
(4)模板匹配
将归一化处理后的字符图像,与模板图像进行相似度计算,找出相似度最高的为目标值;

2.3 关键算法流程
(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));
}

	
```

(2)连通域计算方法
连通域计算有很多方法4领域/8领域计算,具体方法可以直接进入下面这些网址去看:
1.线段统计:https://www.cnblogs.com/liuxin0430/p/9822366.html
2.two-pass:https://blog.csdn.net/liujiabin076/article/details/80788459
3.种子填充法:https://blog.csdn.net/qq_41698119/article/details/102547663

(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 ((0==Dst_width)||(0==Dst_height)||(0==Src_width)||(0==Src_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;
}

版权声明:本文为CSDN博主「梦飞小梦」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_40672861/article/details/122230801

生成海报
点赞 0

梦飞小梦

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

HC-05主从模式自动连接配置

HC-05蓝牙模块主要用于短距离的数据无线传输领域。可以方便的和 PC蓝牙设备相连,也可以两个模块之间的数据互通。避免繁琐的线缆连接,能直接替代串口线。采用英国CSR公司BlueCore4-Ext 芯片&#xff0c

基于stm32的自平衡小车

引言 1、系统概述 1.1、设计任务 利用stm32做一辆自平衡小车 1.2、设计要求 利用IIC和MPU6050、OLED12864进行通信,使用pid算法到自平衡,熟练掌握PID算法 2、方案设计与论

蓝牙模块 HC-06

HC-06蓝牙模块视图 使用TCL转串口使蓝牙模块进入AT指令模式 使用STM32cubemx配置串口模块,一般HC-06默认115200波特率来进行串口传输。并且要开启串口中断,目的是用来处理手机发送到蓝牙模块的