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

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

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

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 

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开发板的人脸识别

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

你可能感兴趣的:(笔记,stm32)