stm32 OV7670/摄像头模块颜色区域定位(腐蚀中心算法)

  • Post author:
  • Post category:其他


前两天用到的摄像头模块ov7670 ,想在摄像头做一个色块识别,查阅了部分资料和教程,发现有用的文章挺多,于是下载了几个demo 学习了一下,感谢一些博主的分享,今天整理一下分享给大家。


实现原理


将摄像头的数据读出写入tft屏,读取tft屏幕上的像素点的颜色进行识别。由于RGB格式的颜色数据的效果不好,所以将其转换为HSL格式数据。首先遍历寻找腐蚀中心,然后在之前腐蚀中心点处进行迭代向外寻找新的腐蚀中心。腐蚀算法从该点开始分别向上下左右四个方向进行读点,若点的颜色符合条件则往外读,等四个方向都结束后得到四个边缘点的坐标,记左边缘点的x轴坐标为left,右边缘点的x轴坐标为right,上边缘点的y轴坐标为up,下边缘点的y轴坐标为bottom,那么坐标( (right-left)/2 , (up-bottom)/2 ) 即为新的腐蚀中心。当确定4个点通过,泽围绕4个点画框,将色块识别区域框起来。

1、实现基本的摄像头画面传输并显示在显示屏上(ov7670摄像头模块的使用之前已经分享过,)

2、腐蚀中心算法(如下图详解)

一个40/3=13 13

13大小的色块为单位进行识别

每次只读取这色块的以y的2/1 为点(也就是这个13

13色块y轴为中心点) x轴向右开始查询识别颜色 如识别失败个数大于6次 则认为这一个13

13的色块无效 跳出循环 继续查询下一个13

13的色块。

在这里插入图片描述

如果失败次数少于6次则改变查询方向 查询这色块的以X/2 为点 对y轴进行查询方法同上 ,如果y轴有6次失败则,退出循环不认同这个色块合格(因为太小了,失败次数又多),但如果少于六次失败,则认为这个色块识别成功 并记录下这个色块的中心点,

在这里插入图片描述

这就是腐蚀中心 /。

unsigned char Hue;					//色度	,[0,240]				
unsigned char Lightness;		//亮度,[0,240]	     
unsigned char Saturation;		//饱和度,[0,240]	     
unsigned char Red;					// [0,255]
unsigned char Green;        // [0,255]
unsigned char Blue;         // [0,255]

下面就是源码层,因为参考的是部分博主的源码移植过来的,这里说明一下。(color 兼容多型号的摄像头)

color.c 函数

#include "color.h"
#include "system.h"	 
#include "tftlcd.h"
#include <stdio.h>
RESULT result;
TARGET_CONDITION condition={
	60,				 //目标最小色度,H_MIN
	110,       //目标最大色度,H_MAX
	           
	50,        //目标最小饱和度,S_MIN
	240,       //目标最大饱和度,S_MAX
	           
	60,        //目标最小亮度,L_MIN
	190,       //目标最大亮度,L_MAX
	           
	40,        //目标最小宽度,WIDTH_MIN
	40,        //目标最小高度,HEIGHT_MIN
	           
	320,       //目标最大宽度,WIDTH_MAX
	240        //目标最大高度,HEIGHT_MAX蟾叨龋琀EIGHT_MAX
};


typedef struct						//HLS颜色
{
	unsigned char Hue;					//色度	,[0,240]				
	unsigned char Lightness;		//亮度,[0,240]	     
	unsigned char Saturation;		//饱和度,[0,240]	     
}COLOR_HLS;

typedef struct 						//RGB
{
	unsigned char Red;					// [0,255]
	unsigned char Green;        // [0,255]
	unsigned char Blue;         // [0,255]
}COLOR_RGB;
/**************************************/
//读取某点的颜色
static void ReadColor( uint16_t usX, uint16_t usY, COLOR_RGB* color_rgb )
{
	unsigned short rgb;
	
	rgb = LCD_ReadPoint( usX, usY );					//获取颜色数据
	
	//转换成值域为[0,255]的三原色值
	color_rgb->Red 		= (unsigned char)( ( rgb & 0xF800 ) >> 8 );
	color_rgb->Green  = (unsigned char)( ( rgb & 0x07E0 ) >> 3 );
	color_rgb->Blue 	= (unsigned char)( ( rgb & 0x001F ) << 3 );	
}
/*************************************/
//RGB转换为HLS
//H:色度
//L:亮度
//S:饱和度
static void RGB2HSL( const COLOR_RGB* color_rgb, COLOR_HLS* 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 )//无定义
	{
		s = 0;
		h = 0;
	}
	else
	{
		//计算色度
		if( max == 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;
		}
		
		//计算饱和度
		if( l == 0 )
		{
			s = 0;
		}
		else if( l <= 120 )
		{
			s = dif * 240 / ( max + min );
		}
		else
		{
			s = dif * 240 / ( 480 - ( max + min ) );
		}		 
	}   
    color_hls->Hue = h;							//色度
	color_hls->Lightness = l;				//亮度
	color_hls->Saturation = s;			//饱和度
}

/************************************************/
 //  颜色匹配
//color_hls :COLOR_HLS结构体,存储HLS格式颜色数据
//  condition :TARGET_CONDITION结构体,存放希望的颜色数据阈值
// 1:像素点颜色在目标范围内;0:像素点颜色不在目标范围内。
static int ColorMatch(const COLOR_HLS* color_hls, const TARGET_CONDITION* condition )
{
	if(
			color_hls->Hue > condition->H_MIN &&
			color_hls->Hue < condition->H_MAX &&
			color_hls->Lightness > condition->L_MIN &&
			color_hls->Lightness < condition->L_MAX &&
			color_hls->Saturation > condition->S_MIN &&
			color_hls->Saturation < condition->S_MAX
	)
		return 1;
	else
		return 0;
}



/****************************************************/
//  寻找腐蚀中心
//  x :腐蚀中心x坐标
//  y :腐蚀中心y坐标
//  condition :TARGET_CONDITION结构体,存放希望的颜色数据阈值
//  area :SEARCH_AREA结构体,查找腐蚀中心的区域
// 1:找到了腐蚀中心,x、y为腐蚀中心的坐标;0:没有找到腐蚀中心。
static int SearchCenter(unsigned int* x, unsigned int* y, const TARGET_CONDITION* condition, SEARCH_AREA* area )
{
	unsigned int i, j, k;
	unsigned int FailCount=0;
	unsigned int SpaceX, SpaceY;
	COLOR_RGB rgb;
	COLOR_HLS hls;
	
	SpaceX = condition->WIDTH_MIN / 3;
	SpaceY = condition->HEIGHT_MIN / 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++;
				
				if(FailCount>( (SpaceX+SpaceY) >> ALLOW_FAIL_PER ))
					break;
				
			}
			
			if(k == SpaceX+SpaceY)
			{
				*x = j + SpaceX / 2;
				*y = i + SpaceY / 2;
				return 1;
			}
			
		}
			
	}
	
	return 0;
		
}

/***************************************************/
// 从腐蚀中心向外腐蚀,得到新的腐蚀中心
//  oldX :先前的腐蚀中心x坐标
//  oldX :先前的腐蚀中心y坐标
//  condition :TARGET_CONDITION结构体,存放希望的颜色数据阈值
//  result :RESULT结构体,存放检测结果
// 1:检测成功;0:检测失败。
 
static int Corrode(unsigned int oldX, unsigned int oldY, const TARGET_CONDITION* condition, RESULT* result )
{
	unsigned int Xmin, Xmax, Ymin, Ymax;
	unsigned int i;
	unsigned int FailCount=0;
	COLOR_RGB rgb;
	COLOR_HLS hls;
	
	for(i=oldX; i>IMG_X; i--)
	{
		ReadColor(i, oldY, &rgb);
		RGB2HSL(&rgb, &hls);
		if(!ColorMatch(&hls, condition))
			FailCount++;
		if(FailCount>(((condition->WIDTH_MIN+condition->WIDTH_MAX)>>2)>>ALLOW_FAIL_PER))
			break;
	}
	Xmin=i;
	
	FailCount=0;
	for(i=oldX; i<IMG_X+IMG_W; i++)
	{
		ReadColor(i, oldY, &rgb);
		RGB2HSL(&rgb, &hls);
		if(!ColorMatch(&hls, condition))
			FailCount++;
		if(FailCount>(((condition->WIDTH_MIN+condition->WIDTH_MAX)>>2)>>ALLOW_FAIL_PER))
			break;
	}
	Xmax=i;
	
	FailCount=0;
	for(i=oldY; i>IMG_Y; i--)
	{
		ReadColor(oldX, i, &rgb);
		RGB2HSL(&rgb, &hls);
		if(!ColorMatch(&hls, condition))
			FailCount++;
		if(FailCount>(((condition->HEIGHT_MIN+condition->HEIGHT_MAX)>>2)>>ALLOW_FAIL_PER))
			break;
	}
	Ymin=i;
	
	FailCount=0;
	for(i=oldY; i<IMG_Y+IMG_H; i++)
	{
		ReadColor(oldX, i, &rgb);
		RGB2HSL(&rgb, &hls);
		if(!ColorMatch(&hls, condition))
			FailCount++;
		if(FailCount>(((condition->HEIGHT_MIN+condition->HEIGHT_MAX)>>2)>>ALLOW_FAIL_PER))
			break;
	}
	Ymax=i;
	
	FailCount=0;
	
	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;
}


int Trace(const TARGET_CONDITION* condition, RESULT* result_final)
{
	unsigned int i;
	static unsigned int x0, y0, Flag = 0;
	static SEARCH_AREA area = {IMG_X, IMG_X+IMG_W, IMG_Y, IMG_Y+IMG_H};
	RESULT result;
	
	if(Flag == 0)
	{
		if(SearchCenter(&x0, &y0, condition, &area))
		{
			Flag = 1;
		}
		else
		{
			area.X_Start = IMG_X;
			area.X_End   = IMG_X+IMG_W;
			area.Y_Start = IMG_Y;
			area.Y_End   = IMG_Y+IMG_H;
			
			if(SearchCenter(&x0, &y0, condition, &area))
			{
				Flag = 0;
				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) )
	{
		x0 = result.x;
		y0 = result.y;
		result_final->x = result.x;
		result_final->y = result.y;
		result_final->w = result.w;
		result_final->h = result.h;
		Flag = 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);
		
		return 1;
	}
	else
	{
		Flag = 0;
		return 0;
	}
	
}

color.h

#ifndef __COLOR_H
#define __COLOR_H	 
#include "system.h"	 
#include "stm32f10x.h"

#define IMG_X 0							  //图片x坐标
#define IMG_Y 0               //图片y坐标
#define IMG_W 240             //图片宽度
#define IMG_H 320             //图片高度

#define ALLOW_FAIL_PER 3    //容错率
#define ITERATER_NUM   8    //迭代次数

#define minOf3Values( v1, v2, v3 )			( (v1<v2) ? ( (v1<v3) ? (v1) : (v3) ) \
																									: ( (v2<v3) ? (v2) : (v3) ) )

#define maxOf3Values( v1, v2, v3 )			( (v1>v2) ? ( (v1>v3) ? (v1) : (v3) ) \
																									: ( (v2>v3) ? (v2) : (v3) ) )
typedef struct							//判定为目标的条件
{
	unsigned char H_MIN;			//目标最小色度
	unsigned char H_MAX;			//目标最大色度
	
	unsigned char S_MIN;			//目标最小饱和度
	unsigned char S_MAX;			//目标最大饱和度
	
	unsigned char L_MIN;			//目标最小亮度
	unsigned char L_MAX;			//目标最大亮度
	
	unsigned int WIDTH_MIN;		//目标最小宽度
	unsigned int HEIGHT_MIN;	//目标最小高度
	
	unsigned int WIDTH_MAX;		//目标最大宽度
	unsigned int HEIGHT_MAX;	//目标最大高度
}TARGET_CONDITION;

typedef struct						//搜寻区域
{
	unsigned int X_Start;
	unsigned int X_End;
	unsigned int Y_Start;
	unsigned int Y_End;
}SEARCH_AREA;


typedef struct				//结果
{
	unsigned int x;			//目标x坐标
	unsigned int y;			//目标y坐标
	unsigned int w;			//目标宽度
	unsigned int h;			//目标高度
}RESULT;

extern RESULT result;
extern TARGET_CONDITION condition;

int Trace(const TARGET_CONDITION* condition, RESULT* result_final);	

#endif

主函数模块的引入这里是更新屏幕的时候引入的

//定位颜色识别区域(画框)

if(Trace(&condition, &result))

{


LCD_DrawRectangle( result.x-result.w/2, result.y-result.h/2, result.x-result.w/2+result.w, result.y-result.h/2+result.h);

LCD_DrawRectangle( result.x-2, result.y-2,result.x+2, result.y+2);

}

更新LCD显示
void camera_refresh(void)
{
	u32 j;
 	u16 color;
	if(ov_sta)//有帧中断更新?
	{
		LCD_Display_Dir(1);
		LCD_Set_Window(0,0,320-1,240-1);//将显示区域设置到屏幕中央
		
		OV7670_RRST=0;				//开始复位读指针 
		OV7670_RCK_L;
		OV7670_RCK_H;
		OV7670_RCK_L;
		OV7670_RRST=1;				//复位读指针结束 
		OV7670_RCK_H;

		for(j=0;j<76800;j++)   //此种方式需清楚TFT内部显示方向控制寄存器值 速度较快
		{
			OV7670_RCK_L;
			color=GPIOC->IDR&0XFF;	//读数据
			OV7670_RCK_H; 
			color<<=8;  
			OV7670_RCK_L;
			color|=GPIOC->IDR&0XFF;	//读数据
			OV7670_RCK_H; 
			LCD_WriteData_Color(color); 
		}   							  
 		ov_sta=0;					//清零帧中断标记
		ov_frame++; 
		LCD_Display_Dir(1); //设置扫描方向
		//定位颜色识别区域(画框)
		if(Trace(&condition, &result))
		{
				LCD_DrawRectangle( result.x-result.w/2, result.y-result.h/2, result.x-result.w/2+result.w,  result.y-result.h/2+result.h);
				LCD_DrawRectangle( result.x-2, result.y-2,result.x+2, result.y+2);
		}
	} 
}

之前分享的原子哥的demo 免费的,大家可以直接下载看看,

stm32 原子哥战舰V3 +ov7670摄像头,形状识别颜色识别源码+ov7670摄像头资料【免费】

用来学习和参考即可。

上一节,

stm32 OV7670摄像头模块的介绍以及应用(SCCB的使用)



版权声明:本文为qq_35653974原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。