欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

识别车牌-识别颜色-基于stm32f4 ov7670(无晶振,无fifo,ov7725,ov2640类似可用)

程序员文章站 2022-06-22 19:06:45
真正识别真正的车牌号的步骤ov7670摄像头识别车牌号识别,二值化,识别区域,分割,匹配一,找车牌的上下位置ov7670摄像头输出图像后进行二值化,图像上就只有黑色和白色两种颜色,至于阈值就需要自己慢慢找了,只有车牌上的字是白色的,其他全部是黑色的,扫描整个屏幕的像素点,记录下来黑白跳变点比较多的行号,上下行间距就是车牌的上下位置。二,找车牌的左右位置找到上下位置之后就可以在车牌的上下位置进行读像素解析,将读到的RGB值转化成HSL值,与蓝色的HSL值得范围进行对比,可以找到蓝色区域,也就找到车...

直接附上我完整的工程代码,点击此处

真正识别真正的车牌号的步骤

ov7670摄像头识别车牌号
识别,二值化,识别区域,分割,匹配

一,找车牌的上下位置

ov7670摄像头输出图像后进行二值化,图像上就只有黑色和白色两种颜色,至于阈值就需要自己慢慢找了,只有车牌上的字是白色的,其他全部是黑色的,扫描整个屏幕的像素点,记录下来黑白跳变点比较多的行号,上下行间距就是车牌的上下位置。

二,找车牌的左右位置

找到上下位置之后就可以在车牌的上下位置进行读像素解析,将读到的RGB值转化成HSL值,与蓝色的HSL值得范围进行对比,可以找到蓝色区域,也就找到车牌的左右边界。

三,将车牌区域二值化

将车牌区域二值化,字是白色的其他区域是黑色的。

四,字符分割

从后向前进行字符分割,将每个字符所在开始坐标和结束坐标记录下来。

五,字符归一化

将分割出来的字符读取出来,放大为标准像素25*50.

六,转化为数据

将分割出来的图像转化为字符数据。

七,字符识别

将转化出来的字符数据和标准的字符数据进行对比,每个字节的八位数据都要对比,将相似的数据数记录下来,默认识别为相似度最大的那个数。

请参考以下文章

stm32+ov7670数字识别

基于STM32单片机的车牌识别

色彩空间中的 HSL、HSV、HSB 有什么区别?

图像识别-纯数字识别

学生作品识别颜色,对应车牌号(只需分辨车辆即可),也就是说重在识别

但学生作品有时并不需要这样的识别,演示过程没有真正车牌给你识别,自己打印小小的印刷体纸条粘上,好像也不是那么一回事,所以就通过识别颜色或形状等分辨车辆。

这里,我用的颜色识别

//color.c
#include "colorcfg.h"
#include "sys.h"
#include "lcd.h"
#include <stdio.h>

RESULT_t result[TRACE_NUM];


 
//红色0 绿色80 蓝色160 
//50 200
u8 global_page=0;
TARGET_CONDITION_t condition[TRACE_NUM]=
{
	{220,240,	50,200,5,200,40,40,200,200},//红0  
	{80,120,	50,200,5,200,40,40,200,200},//绿80
	{140,160,	50,200,5,200,40,40,200,200},//蓝160
	
 	{0,40,		50,200,5,200,40,40,200,200},//黄40   

};
SEARCH_AREA_t area = {IMG_X, IMG_X+IMG_W, IMG_Y, IMG_Y+IMG_H};//定义搜索区域


//读取某点的颜色
void ReadColor( uint16_t usX, uint16_t usY, COLOR_RGB_t* color_rgb )
{
	uint16_t rgb;
	rgb = LCD_READPOINT( usX, usY );					//获取颜色数据
	
	//转换成值域为[0,255]的三原色值
	color_rgb->Red 		= (uint8_t)( ( rgb & 0xF800 ) >> 8 );
	color_rgb->Green    = (uint8_t)( ( rgb & 0x07E0 ) >> 3 );
	color_rgb->Blue 	= (uint8_t)( ( rgb & 0x001F ) << 3 );	
}
/*************************************/
//RGB转换为HLS
//H:色度
//L:亮度
//S:饱和度
void RGB2HSL( const COLOR_RGB_t* color_rgb, COLOR_HLS_t* color_hls )
{
	int r, g, b;
	int h, l, s;
	int max, min, dif;
	
	r = color_rgb->Red;
	g = color_rgb->Green;
	b = color_rgb->Blue;
	
	max = maxOf3Values( r, g, b );
	min = minOf3Values( r, g, b );
	dif = max - min;
	
	//计算l,亮度
	l = ( max + min ) * 240 / 255 / 2;
	
	//计算h,色度
	if( max == min )//无定义 RGB一样  黑灰白
	{
		s = 0;//饱和度0
		h = 0;//色度0
	}
	else
	{
		/*计算色度h*/
		if( max == r )//如果R值最大
		{
			if( min == b )//h介于0到40
			{
				h = 40 * ( g - b ) / dif;
			}
			else if( min == g )//h介于200到240
			{
				h = 40 * ( g - b ) / dif + 240;
			}
			
		}
		else if( max == g )
		{
			h = 40 * ( b - r ) / dif + 80;
		}
		else if( max == b )
		{
			h = 40 * ( r - g ) / dif + 160;
		}
		
		//计算饱和度s
		if( l == 0 )
		{
			s = 0;
		}
		else if( l <= 120 )
		{
			s = dif * 240 / ( max + min );
		}
		else
		{
			//s = dif * 240 / ( 480 - ( max + min ) );
            s = (dif)*240/(511 - (max+min));
		}		 
	}   
    color_hls->Hue = h;				//色度
	color_hls->Lightness = l;			//亮度
	color_hls->Saturation = s;			//饱和度
}

/************************************************/
 //  颜色匹配
//color_hls :COLOR_HLS结构体,存储HLS格式颜色数据
//condition :TARGET_CONDITION结构体,存放希望的颜色数据阈值
// 1:像素点颜色在目标范围内;0:像素点颜色不在目标范围内。
int ColorMatch(const COLOR_HLS_t* color_hls, const TARGET_CONDITION_t* condition )
{

    
	if(	
			color_hls->Lightness > condition->L_MIN &&
			color_hls->Lightness < condition->L_MAX &&
			color_hls->Saturation > condition->S_MIN &&
			color_hls->Saturation < condition->S_MAX
	)//比较饱和度和亮度
    {
                
        if( color_hls->Hue > condition->H_MIN &&
			color_hls->Hue < condition->H_MAX  )//颜色在范围内
            return 1;
        else
        if (condition->H_MAX < condition->H_MIN)  //设定的最大颜色小于最小颜色 说明有向下溢出 可能需要和高位颜色匹配            
        {
            if(color_hls->Hue < condition->H_MAX )
                return 1;
            if(color_hls->Hue > (condition->H_MIN-65295) )
                return 1;
        }else
        if(condition->H_MAX>240)//设定的最大颜色超过240 说明有向上溢出 可能需要和低位颜色匹配
        {
            if(color_hls->Hue > condition->H_MAX )
                return 1;
            if(color_hls->Hue < (condition->H_MAX-240) )
                return 1;
        }
   
		return 0;
    }
	else
		return 0;
}



/****************************************************/
//  寻找腐蚀中心
//  x :腐蚀中心x坐标
//  y :腐蚀中心y坐标
//  condition :TARGET_CONDITION结构体,存放希望的颜色数据阈值
//  area :SEARCH_AREA结构体,查找腐蚀中心的区域
// 1:找到了腐蚀中心,x、y为腐蚀中心的坐标;0:没有找到腐蚀中心。
int SearchCenter(uint16_t* x, uint16_t* y, const TARGET_CONDITION_t* condition,const SEARCH_AREA_t* area )
{
	uint16_t i, j, k;
	uint16_t FailCount=0;
	uint16_t SpaceX, SpaceY;
	COLOR_RGB_t rgb;
	COLOR_HLS_t hls;
	
	SpaceX = condition->WIDTH_MIN / 3;//以最小宽度除以3 为 横向查询的步进的一个单位
	SpaceY = condition->HEIGHT_MIN / 3;//以最小高度除以3 为 垂直查询的步进的一个单位
	
	/*横向步进单位+垂直步进单位 组成了一个矩形的色块*/
	for(i=area->Y_Start; i<area->Y_End; i+=SpaceY)
	{
		for(j=area->X_Start; j<area->X_End; j+=SpaceX)
		{
			FailCount = 0;
			for(k=0; k<SpaceX+SpaceY; k++)
			{
				if(k<SpaceX)
					ReadColor( j+k, i+SpaceY/2, &rgb );//查询色块中间一横的颜色
				else
					ReadColor( j+SpaceX/2, i+k-SpaceX, &rgb );//查询色块中间一竖的颜色
				RGB2HSL( &rgb, &hls );
				
				if(!ColorMatch( &hls, condition ))
					FailCount++;//颜色不匹配 失败计数+1
				
				if(FailCount>( (SpaceX+SpaceY) >> ALLOW_FAIL_PER ))//失败计数大于 色块需要查询的总点数/2^容错率
					break;//失败次数太多 退出
				
			}
			
			if(k == SpaceX+SpaceY)//k坚持到查询完毕,说明基本匹配
			{
				/*记录该色块的中心点为腐蚀中心*/
				*x = j + SpaceX / 2;
				*y = i + SpaceY / 2;
				return 1;   //记录到第一个腐蚀中心后退出函数
			}
			
		}
			
	}
	
	return 0;
		
}

/***************************************************/
// 从腐蚀中心向外腐蚀,得到新的腐蚀中心
//  oldX :先前的腐蚀中心x坐标
//  oldX :先前的腐蚀中心y坐标
//  condition :TARGET_CONDITION结构体,存放希望的颜色数据阈值
//  result :RESULT结构体,存放检测结果
// 1:检测成功;0:检测失败。
 
int Corrode(uint16_t oldX, uint16_t oldY, const TARGET_CONDITION_t* condition, RESULT_t* result )
{
	uint16_t Xmin, Xmax, Ymin, Ymax;
	uint16_t i;
	uint16_t FailCount=0;
	COLOR_RGB_t rgb;
	COLOR_HLS_t hls;
	
	for(i=oldX; i>IMG_X; i--)//从旧x点向左腐蚀
	{
		ReadColor(i, oldY, &rgb);//读点
		RGB2HSL(&rgb, &hls);//转换
		if(!ColorMatch(&hls, condition))
			FailCount++;//不匹配计数自加1
		
        if( FailCount> ((condition->WIDTH_MIN)/ALLOW_FAIL_PER) )//当识别失败点大于最小宽度/2是跳出            
			break;
	}
	Xmin=i;//获得最新蔓延的x最左边的值
	
	FailCount=0;
	for(i=oldX; i<IMG_X+IMG_W; i++)//从旧x点向右腐蚀
	{
		ReadColor(i, oldY, &rgb);
		RGB2HSL(&rgb, &hls);
		if(!ColorMatch(&hls, condition))
			FailCount++;

        if( FailCount> ((condition->WIDTH_MIN)/ALLOW_FAIL_PER) )
			break;
	}
	Xmax=i;
	
	FailCount=0;
	for(i=oldY; i>IMG_Y; i--)//从旧y点向上腐蚀
	{
		ReadColor(oldX, i, &rgb);
		RGB2HSL(&rgb, &hls);
		if(!ColorMatch(&hls, condition))
			FailCount++;

        if( FailCount> ((condition->WIDTH_MIN)/ALLOW_FAIL_PER) )
			break;
	}
	Ymin=i;
	
	FailCount=0;
	for(i=oldY; i<IMG_Y+IMG_H; i++)//从旧y点向下腐蚀
	{
		ReadColor(oldX, i, &rgb);
		RGB2HSL(&rgb, &hls);
		if(!ColorMatch(&hls, condition))
			FailCount++;
        
        if( FailCount> ((condition->WIDTH_MIN)/ALLOW_FAIL_PER) )
			break;
	}
	Ymax=i;
	
	FailCount=0;
	
	//获得腐蚀区域的中点和xy范围
	result->x = (Xmin + Xmax) / 2;
	result->y = (Ymin + Ymax) / 2;
	result->w = (Xmax - Xmin);
	result->h = (Ymax - Ymin);
	
	if( (result->w > condition->WIDTH_MIN) && (result->w < condition->WIDTH_MAX) &&
			(result->h > condition->HEIGHT_MIN) && (result->h < condition->HEIGHT_MAX)  )
		return 1;//如果腐蚀后的区域没有超过最大限定区域且没有小于最小限定区域 有效!!
	else
		return 0;
}

/*
 *返回0识别失败,1成功
 *得到匹配色块的信息
 */
int Trace(const TARGET_CONDITION_t* condition, RESULT_t* result_final)
{
	uint16_t i;
	static uint16_t x0, y0;	
	RESULT_t result;
	
    if(!SearchCenter(&x0, &y0, condition, &area))//寻找腐蚀中心
    {
        area.X_Start = IMG_X;
		area.X_End   = IMG_X+IMG_W;
		area.Y_Start = IMG_Y;
        area.Y_End   = IMG_Y+IMG_H;
        return 0;
	}
	//找到腐蚀中心 得到中点
	result.x = x0;
	result.y = y0;
	
	for(i=0; i<ITERATER_NUM; i++)//多次迭代 精确中心
	{
		Corrode(result.x, result.y, condition, &result);	//从腐蚀中心向外腐蚀,得到新的腐蚀中心
		
	}
	
	if( Corrode(result.x, result.y, condition, &result) )//重新腐蚀成功
	{
		result_final->x = result.x;
		result_final->y = result.y;
		result_final->w = result.w;
		result_final->h = result.h;
#if TRACE_NUM==1 //只有一个图像时才使用快速查找		
		/*为了快速对下一个图像进行找腐蚀中心,直接定义本次图像的腐蚀中心为下一个图像的扫描区域*/
		area.X_Start = result.x - ((result.w)>>1);
		area.X_End   = result.x + ((result.w)>>1);
		area.Y_Start = result.y - ((result.h)>>1);
		area.Y_End   = result.y + ((result.h)>>1);
#endif		
		return 1;
	}
	else
	{
		return 0;
	}
	
}

此代码(来源于网络,将要识别的区域的像素遍历 需找匹配范围内的像素,找到匹配像素后,向它的四周进行蔓延匹配像素。
直到匹配到符合色块大小的区域,标记识别成功。
为了提高识别速度和降低cpu压力,并不是每个多个像素都要比较的。我们以(最小识别区域/3)为一个单位色块,
只用识别色块的中间一横和一竖上的像素点作为该色块的匹配标准,大大减少了cpu的压力。

在识别算法和RGB转HSL函数
只要调用Trace这一个函数就能实现颜色的识别,非常方便。
在使用前 需要在colorcfg.h中根据自己的图像大小在LCD中的位置配置宏定义
提供自己lcd的读点函数,读点格式是rgb565.
)只需修改以下部分,这是我测好的四种颜色的hsl数据,(因为环境不一样,可能需要自己重新测试,每次都能识别到即可
TARGET_CONDITION_t condition[TRACE_NUM]=
{
{220,240, 50,200,5,200,40,40,200,200},//红0
{80,120, 50,200,5,200,40,40,200,200},//绿80
{140,160, 50,200,5,200,40,40,200,200},//蓝160
{0,40, 50,200,5,200,40,40,200,200},//黄40

};

//colorcfg.h
#ifndef colorcfg_h
#define colorcfg_h

#include "color.h"

#include "test.h"



/*配置色块查询的范围  图像在LCD的坐标*/
#define IMG_X 0			      //图片x坐标
#define IMG_Y 0               //图片y坐标
#define IMG_W 240             //图片宽度
#define IMG_H 320             //图片高度

#define ALLOW_FAIL_PER       3     //容错率越大 颜色匹配越高,也越难识别 取值>1
#define ITERATER_NUM         5     //迭代次数 越多精度越准
#define COLOR_RANG           90    //设定颜色的偏移范围 越大越容易识别 太大容易误识别
#define TRACE_NUM            4     //设定追踪颜色的数目

extern u8 global_page;//当前颜色的
extern SEARCH_AREA_t area;//定义搜索区域
extern RESULT_t result[TRACE_NUM];//定义搜索结果
extern TARGET_CONDITION_t condition[TRACE_NUM];//定义目标参数

#define LCD_READPOINT( usX, usY )  LCD_ReadPoint(usX,usY)//定义读点函数

#endif

这里需要修改以下部分
#define IMG_W 240 //图片宽度
#define IMG_H 320 //图片高度

#define ALLOW_FAIL_PER 3 //容错率越大 颜色匹配越高,也越难识别 取值>1
#define ITERATER_NUM 5 //迭代次数 越多精度越准
#define COLOR_RANG 90 //设定颜色的偏移范围 越大越容易识别 太大容易误识别
#define TRACE_NUM 4 //设定追踪颜色的数目

u8 car_distinguish(void)
{
	u8 CAR_FLAG=0;
	u8 i=0;
	
	LCD_ShowString(30,Y11,200,16,16,"Camera Starting...");
	
	//初始化OV7670
	while(OV7670_Init())
	{
		LCD_ShowString(30,Y12,240,16,16,"Camera ERR");
		delay_ms(200);
	  LCD_Fill(30,Y12,239,170,WHITE);
		delay_ms(200);
	}
	LCD_ShowString(30,Y12,200,16,16,"Camera OK"); 
	
	//OV7670设置输出窗口
	OV7670_Window_Set(12,176,240,320);
	
	//DCMI配置
	My_DCMI_Init();
	//DCMI DMA配置
	DCMI_DMA_Init((u32)&LCD->LCD_RAM,10,DMA_MemoryDataSize_HalfWord,DMA_MemoryInc_Disable);
	//启动传输	
	DCMI_Start();

	while(CAR_FLAG==0)
	{
		DCMI_Stop();
		
    for(i=0;i<TRACE_NUM;i++)
		{
      if(Trace(&condition[i], &result[i]))//执行颜色识别
      {     
        /*颜色匹配 画矩形圈出色块 LED0熄灭 */                  
        LCD_DrawRectangle( result[i].x-result[i].w/2, result[i].y-result[i].h/2, result[i].x-result[i].w/2+result[i].w, result[i].y-result[i].h/2+result[i].h);                    
        draw_cross(result[i].x, result[i].y);
				CAR_FLAG=i+1;
       } 
		}
		DCMI_Start();
		delay_ms(10);			
	} 
	
	DCMI_Stop();
	LCD_Fill(0,0,320,240,WHITE);
	LCD_Scan_Dir(0);
	return CAR_FLAG;
}

此函数是具体结合ov7670的调用识别函数,因为我的7670是无晶振,无fifo的(某宝15元+就可买到),而且通过dcmi接口,dma传输到lcd显示的,所以读数据就只能用lcd的读点函数了,

	car_color=car_distinguish();
	
	switch(car_color)
	{
		
		case 1:showhz32str(200,Y12,"吉",RED,WHITE);POINT_COLOR=RED;LCD_ShowString(240-4*16,Y11,200,16,16,"R.66666"); break;
		case 2:showhz32str(200,Y12,"吉",GREEN,WHITE);POINT_COLOR=GREEN;LCD_ShowString(240-4*16,Y11,200,16,16,"G.88888"); break;
		case 3:showhz32str(200,Y12,"吉",BLUE,WHITE);POINT_COLOR=BLUE;LCD_ShowString(240-4*16,Y11,200,16,16,"B.dzxh9"); break;
	}

识别完后,拿去给lcd显示。

推荐以下参考资料
阿莫

基于stm32f407vet开发板的人脸识别

如果觉得对你有帮助的话,关注我,点个赞再走呗

本文地址:https://blog.csdn.net/qq_35988224/article/details/108179834

相关标签: 笔记 stm32