STM32 OV7725摄像头模块识别颜色物体(1)--HSL二值化和腐蚀中心算法,并用串口输出数据

这篇具有很好参考价值的文章主要介绍了STM32 OV7725摄像头模块识别颜色物体(1)--HSL二值化和腐蚀中心算法,并用串口输出数据。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

前言

一、摄像头采集数据流程

二、如何将图像显示到电脑上

 三、图像二值化

1、什么是RGB?

2、RGB565转RGB888

I、RGB565和RGB888的区别

II、代码

3、RGB转HSL

I、什么是HSL

 II、转换公式

 III、代码

3、输出一张摄像头二值化图片

I、原理

II、代码 

四、简单的物体识别

1、原理参考

 2、识别代码

3、显示代码

总结



前言

前阵子用STM32弄摄像头,断断续续有段时间,也在网上翻阅了不少资料,写篇博客记录一下学习过程。最后成功识别单个物体,图形和多个物体暂不支。

一、摄像头采集数据流程

(1) 利用 SIO_C、SIO_D 引脚通过 SCCB 协议向 OV7725 的寄存器写入初始化配置;

(2) 初始化完成后,OV7725 传感器会使用 VGA 时序输出图像数据,它的 VSYNC 会

首先输出帧有效信号(低电平跳变),当外部的控制器(如 STM32)检测到该信号

时,把 WEN 引脚设置为高电平,并且使用 WRST 引脚复位 FIFO 的写指针到 0 地

址;

(3) 随着 OV7725 继续按 VGA 时序输出图像数据,它在传输每行有效数据时, HREF

引脚都会持续输出高电平,由于 WEN 和 HREF 同时为高电平输入至与非门,使得

其连接到 FIFO WE 引脚的输出为低电平,允许向 FIFO 写入数据,所以在这期间,

OV7725 通过它的 PCLK 和 D[0:7]信号线把图像数据存储到 FIFO 中,由于前面复

位了写指针,所以图像数据是从 FIFO 的 0 地址开始记录的;

(4) 各行图像数据持续传输至 FIFO,受 HREF 控制的 WE 引脚确保了写入到 FIFO 中

的都是有效的图像数据,OV7725 输出完一帧数据时,VSYNC 会再次输出帧有效

信号,表示一帧图像已输出完成;

(5) 控制器检测到上述 VSYNC 信号后,可知 FIFO 中已存储好一帧图像数据,这时控

制 WEN 引脚为低电平,使得 FIFO 禁止写入,防止 OV7725 持续输出的下一帧数

据覆盖当前 FIFO 数据;

(6) 控制器使用RRST复位读指针到FIFO的0地址,然后通过FIFO的RCLK和DO[0:7]

引脚,从 0 地址开始把 FIFO 缓存的整帧图像数据读取出来。在这期间,OV7725

是持续输出它采集到的图像数据的,但由于禁止写入 FIFO,这些数据被丢弃了;

(7) 控制器使用 WRST 复位写指针到 FIFO 的 0 地址,然后等待新的 VSYNC 有效信号

到来,检测到后把 WEN 引脚设置为高电平,恢复 OV7725 向 FIFO 的写入权限,

OV7725 输出的新一帧图像数据会被写入到 FIFO 的 0 地址中,重复上述过程。

ov7725 stm32,stm32,图像处理

网上也有很多现成的文章,分享几个链接

摄像头原理:

stm32 OV7670摄像头模块的介绍以及应用(SCCB的使用)_闰土小蒋的博客-CSDN博客

正点原子开发板程序:

http://www.openedv.com/docs/modules/camera/ov7725-fifo.html

二、如何将图像显示到电脑上

使用串口发送数据到电脑,用山外调试助手显示,这里的波特率使用256000,因为调试助手的最大也只能是256000,图像配置,宽:320,高:240,RGB565大端(这个是个大坑)

一副图像的通信协议为:[0x01] [0xFE][…数据…][0xFE] [0x01]

[…数据…] 是图像的数据,一帧图像有多少数据,这里的数据长度就有多少。换句话说, […数据…]与图像的格式,图像的宽高有关。此处的图像数据,都是从上往下,从左往右存储 的。 只有下位机发送的数据与上位机配置的格式的长度相同时,才可正确识别图像格式,从 而正确显示图像。 下位机发送图像时,先发送帧头:0x01,0xFE,接着发送图像数据,最后发送帧尾: 0xFE,0x01 完成一副图像发送。

ov7725 stm32,stm32,图像处理

链接:https://pan.baidu.com/s/1eA3rhtNiocKxtH0rxvyGzg?pwd=0722 
提取码:0722

摄像头采集代码如下:

        OV7725_RRST(0);                //开始复位读指针 
        OV7725_RCK_L;
        OV7725_RCK_H;
        OV7725_RCK_L;
        OV7725_RRST(1);                //复位读指针结束 
        OV7725_RCK_H;
   
        while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
        USART_SendData(UART4, 0x01);
        while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
        USART_SendData(UART4, 0xfe);
       
        for(x_size=0;x_size<240;x_size++)
        {
            for(y_size=0;y_size<320;y_size++)
            {
                OV7725_RCK_L;
                color=GPIOE->IDR;    //读数据
                OV7725_RCK_H;
                colorH=(color>>8) &0xff;
                OV7725_RCK_L;
                color=GPIOE->IDR ;    //读数据
                OV7725_RCK_H; 
                colorL=(color>>8) &0xff;
                
                while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
                USART_SendData(UART4, colorH);
                while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
                USART_SendData(UART4, colorL);
            }
        }

        while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
        USART_SendData(UART4, 0xfe);
        while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
        USART_SendData(UART4, 0x01);

 三、图像二值化

1、什么是RGB?

        RGB是从颜色发光的原理来设计定的,通俗点说它的颜色混合方式就好像有红、绿、蓝三盏灯,当它们的光相互叠合的时候,色彩相混,而亮度却等于两者亮度之总和,越混合亮度越高,即加法混合。

        红、绿、蓝三个颜色通道每种色各分为256阶亮度,在0时“灯”最弱——是关掉的,而在255时“灯”最亮。当三色灰度数值相同时,产生不同灰度值的灰色调,即三色灰度都为0时,是最暗的黑色调;三色灰度都为255时,是最亮的白色调。

        在电脑中,RGB的所谓“多少”就是指亮度,并使用整数来表示。通常情况下,RGB各有256级亮度,用数字表示为从0、1、2...直到255。注意虽然数字最高是255,但0也是数值之一,因此共256级。

ov7725 stm32,stm32,图像处理

2、RGB565转RGB888

I、RGB565和RGB888的区别

在计算机中图像基本是以RGB888格式显示的,24位图每个像素保存了32bit的数据,即RGB888+Alpha,Alpha就是半透明填充字节。对于真彩的图像而言,肉眼在16bit的时候已经难以分辨了,有些时候,可以将RGB888转换为RGB565来存储,减少了存储器的容量的同时,降低了数据量;在显示的时候,再次把RGB565转换为RGB888,实现数据宽度的匹配

RGB888->RGB565

只要提取相应单色高位即可(R5 G6 B5),但会导致低位的缺失,影响精度,而且无法恢复

RGB565->RGB888

填充相应单色低位即可

ov7725 stm32,stm32,图像处理

ov7725 stm32,stm32,图像处理

II、代码

typedef struct 					//RGB888
{
	unsigned char Red;			//红色,[0,255]
	unsigned char Green;        //绿色,[0,255]
	unsigned char Blue;         //蓝色,[0,255]
}COLOR_RGB;

void RGB565_To_RGB888(u16 rgb,COLOR_RGB *color_rgb)
{
	color_rgb->Red 	    = (unsigned char)( ( rgb & 0xF800 ) >> 8 );
	color_rgb->Green    = (unsigned char)( ( rgb & 0x07E0 ) >> 3 );
	color_rgb->Blue 	= (unsigned char)( ( rgb & 0x001F ) << 3 );	
}

3、RGB转HSL

I、什么是HSL

ov7725 stm32,stm32,图像处理

 HSL的H(hue)分量,代表的是人眼所能感知的颜色范围,这些颜色分布在一个平面的色相环上,取值范围是0°到360°的圆心角,每个角度可以代表一种颜色。色相值的意义在于,我们可以在不改变光感的情况下,通过旋转色相环来改变颜色。在实际应用中,我们需要记住色相环上的六大主色,用作基本参照:360°/0°红、60°黄、120°绿、180°青、240°蓝、300°洋红,它们在色相环上按照60°圆心角的间隔排列

ov7725 stm32,stm32,图像处理

HSL的S(saturation)分量,指的是色彩的饱和度,它用0%至100%的值描述了相同色相、明度下色彩纯度的变化。数值越大,颜色中的灰色越少,颜色越鲜艳,呈现一种从灰度到纯色的变化。

ov7725 stm32,stm32,图像处理

HSL的L(lightness)分量,指的是色彩的明度,作用是控制色彩的明暗变化。它同样使用了0%至100%的取值范围。数值越小,色彩越暗,越接近于黑色;数值越大,色彩越亮,越接近于白色。

ov7725 stm32,stm32,图像处理

 II、转换公式

ov7725 stm32,stm32,图像处理

 III、代码

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

#define maxOf3Values( v1, v2, v3 )			( (v1>v2) ? ( (v1>v3) ? (v1) : (v3) ) : ( (v2>v3) ? (v2) : (v3) ) ) //取rgb中的最大值
#define minOf3Values( v1, v2, v3 )			( (v1<v2) ? ( (v1<v3) ? (v1) : (v3) ) : ( (v2<v3) ? (v2) : (v3) ) ) //取rgb中的最小值


void RGB888_TO_HSL(COLOR_RGB* color_rgb, COLOR_HLS* color_hls)
{
	unsigned char r, g, b;
	unsigned char h, l, s;
	unsigned char max, min, dif;
	
	r = color_rgb->Red;
	g = color_rgb->Green;
	b = color_rgb->Blue;
	
	max = maxOf3Values( r, g, b );  //取rgb中的最大值
	min = minOf3Values( r, g, b );  //取rgb中的最小值
	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( g >= b )    //h介于0到40
			{
				h = 40 * ( g - b ) / dif;
			}
			else if( g < b )//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 )      /* 0<l<=1/2 */
		{
			s = dif * 240 / ( max + min );                  
		}
		else                    /* l>1/2 */
		{
			s = dif * 240 / ( 480 - ( max + min ) );        
		}		 
	}   
    color_hls->Hue = h;				//色度
	color_hls->Lightness = l;		//亮度
	color_hls->Saturation = s;		//饱和度
}

3、输出一张摄像头二值化图片

I、原理

摄像头采集到的每个像素点,先由RGB格式转换成HSL格式,再与定义阈值进行对比,判断颜色是否和定义颜色匹配

II、代码 

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 short WIDTH_MIN;	//目标最小宽度
	unsigned short HEIGHT_MIN;	//目标最小高度
	
	unsigned short WIDTH_MAX;   //目标最大宽度
	unsigned short HEIGHT_MAX;	//目标最大高度
}TARGET_CONDITION;

int ColorMatch(const COLOR_HLS* color_hls, const TARGET_CONDITION* 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 )  //设定的最大颜色小于最小颜色 说明有向下溢出 可能需要和高位颜色匹配            
        {
            /*0——有效——最大值——无效——最小值——有效——240*/
            if( color_hls->Hue <= condition->H_MAX )     //小于最大值
                return 1;
            if( color_hls->Hue >= condition->H_MIN )     //大于最小值
                return 1;
        } 
    }
	return 0;
}

判断颜色是否和定义颜色匹配。

输入变量,color_hls:COLOR_HLS结构体,存储HLS格式颜色数据; 

                 condition :TARGET_CONDITION结构体,存放希望的颜色数据阈值。

返回数据,1:像素点颜色在目标范围内;0:像素点颜色不在目标范围内。

u8 Bmp_Minimize_SDRAM[240][40];  /*用于存放二值化后像素点数据,Bmp_Minimize_SDRAM[X][Y]*/

TARGET_CONDITION condition0={
	40,		  //目标最小色度,H_MIN
	80,       //目标最大色度,H_MAX
	           
	0,        //目标最小饱和度,S_MIN
	240,       //目标最大饱和度,S_MAX
	           
	0,        //目标最小亮度,L_MIN
	240,       //目标最大亮度,L_MAX
	           
	40,        //目标最小宽度,WIDTH_MIN
	40,        //目标最小高度,HEIGHT_MIN
	           
	240,       //目标最大宽度,WIDTH_MAX
	320        //目标最大高度,HEIGHT_MAX
};

for(x_size=0;x_size<240;x_size++)   //此种方式可以兼容任何彩屏,但是速度很慢
{
    for(y_size=0;y_size<320;y_size++)
    {
        OV7725_RCK_L;
        color=GPIOE->IDR;	//读数据
        OV7725_RCK_H;
        colorH=(color>>8) &0xff;
        OV7725_RCK_L;
        color=GPIOE->IDR ;	//读数据
        OV7725_RCK_H; 
        colorL=(color>>8) &0xff;
        
        color=(colorH<<8)|colorL;

        RGB565_TO_HSL(color,&hls_value);        /*RGB565转换为HLS*/
        
        if(y_size%8==0)
        {
            Bmp_Minimize_SDRAM[x_size][y_size/8]=color_buffer;      /*二值化后的数据,存入缓存*/
        }
        color_buffer<<=1;
        color_buffer|=ColorMatch(&hls_value,&condition0);
    }
}

摄像头采集数据二值化,由于STM32F1内存有限,存放不了一帧320*240的彩色图像,故将处理好后的数据存入数组

while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
USART_SendData(UART4, 0x01);
while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
USART_SendData(UART4, 0xfe);
for(y_size=0;y_size<240;y_size++)     /*数组数据串口打印到电脑*/
{
    for(x_size=0;x_size<40;x_size++)
    {
        color=Bmp_Minimize_SDRAM[y_size][x_size+1];
        for(color_buffer=0;color_buffer<8;color_buffer++)
        {
            if((color<<color_buffer) &0x80)
            {
                while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
                USART_SendData(UART4, 0xff);
                while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
                USART_SendData(UART4, 0xff);
            }
            else
            {
                while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
                USART_SendData(UART4, 0x00);
                while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
                USART_SendData(UART4, 0x00);                        
            }
        }
    }
}
while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
USART_SendData(UART4, 0xfe);
while(USART_GetFlagStatus(UART4, USART_FLAG_TC)==RESET);      //判断是否发送完成。
USART_SendData(UART4, 0x01);

将存放在缓存中的二值化数据,通过简单位操作输出到调试助手

ov7725 stm32,stm32,图像处理

四、简单的物体识别

1、原理参考

首先遍历寻找腐蚀中心,然后在之前腐蚀中心点处进行迭代向外寻找新的腐蚀中心。腐蚀算法从该点开始分别向上下左右四个方向进行读点,若点的颜色符合条件则往外读,等四个方向都结束后得到四个边缘点的坐标,记左边缘点的x轴坐标为left,右边缘点的x轴坐标为right,上边缘点的y轴坐标为up,下边缘点的y轴坐标为bottom,那么坐标( (right-left)/2 , (up-bottom)/2 ) 即为新的腐蚀中心。当确定4个点通过,泽围绕4个点画框,将色块识别区域框起来。

2、腐蚀中心算法(如下图详解)
一个40/3=13 1313大小的色块为单位进行识别
每次只读取这色块的以y的2/1 为点(也就是这个1313色块y轴为中心点) x轴向右开始查询识别颜色 如识别失败个数大于6次 则认为这一个1313的色块无效 跳出循环 继续查询下一个1313的色块。

ov7725 stm32,stm32,图像处理

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

 

ov7725 stm32,stm32,图像处理

 

 2、识别代码

extern u8 Bmp_Minimize_SDRAM[240][40];

static uint8_t ReadColor( uint16_t usX, uint16_t usY)
{
    uint8_t color_value;
    usY=usY/8;
    color_value=Bmp_Minimize_SDRAM[usX][usY];
    usY=usY%8;
    return (color_value>>usY)&0x01;
}

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

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

/**
 * @brief  寻找腐蚀中心
 * @param  x :腐蚀中心x坐标
 * @param  y :腐蚀中心y坐标
 * @param  condition :TARGET_CONDITION结构体,存放希望的颜色数据阈值
 * @param  area :SEARCH_AREA结构体,查找腐蚀中心的区域
 * @retval 1:找到了腐蚀中心,x、y为腐蚀中心的坐标;0:没有找到腐蚀中心。
 */
static int SearchCenter(unsigned short* x, unsigned short* y, const TARGET_CONDITION* condition, SEARCH_AREA* area )
{
	unsigned short i, j, k;
	unsigned short FailCount=0;
	unsigned short SpaceX, SpaceY;
	
	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)    //查询色块中间一横的颜色
                {   
                    if(ReadColor(j+k, i+SpaceY/2)==0)
                    {
                       FailCount++;     //颜色不匹配 失败计数+1 
                    }
                }
				else            //查询色块中间一竖的颜色     
                {
					if(ReadColor( j+SpaceX/2, i+k-SpaceX)==0)
                    {
                        FailCount++;    //颜色不匹配 失败计数+1
                    }
				}
					
				if(FailCount>( (SpaceX+SpaceY) / ALLOW_FAIL_PER ))     //失败计数大于 色块需要查询的总点数/容错率
					break;  //失败次数太多 退出
			}
			
			if(k == SpaceX+SpaceY)  //k坚持到查询完毕,说明基本匹配
			{
                /*记录该色块的中心点为腐蚀中心*/
				*x = j + SpaceX / 2;
				*y = i + SpaceY / 2;
				return 1;   //记录到第一个腐蚀中心后退出函数
			}
		}	
	}
	return 0;	
}


/**
 * @brief  从腐蚀中心向外腐蚀,得到新的腐蚀中心
 * @param  oldX :先前的腐蚀中心x坐标
 * @param  oldX :先前的腐蚀中心y坐标
 * @param  condition :TARGET_CONDITION结构体,存放希望的颜色数据阈值
 * @param  result :RESULT结构体,存放检测结果
 * @retval 1:检测成功;0:检测失败。
 */
static int Corrode(unsigned short oldX, unsigned short oldY, const TARGET_CONDITION* condition, RESULT* result )
{
	unsigned short Xmin, Xmax, Ymin, Ymax;
	unsigned short i;
	unsigned short FailCount=0;
	for(i=oldX; i>IMG_X; i--)           //从中心点查到x最左侧
	{
		if(!ReadColor(i, oldY))
			FailCount++;    //不匹配计数自加1
        if( FailCount> ((condition->WIDTH_MIN)/ALLOW_FAIL_PER) | i<=0)//当识别失败点大于最小宽度/2是跳出  
			break;
	}
	Xmin=i;     //更新X轴最小坐标值
    
	FailCount=0;    //清空错误次数
	for(i=oldX; i<IMG_X+IMG_W; i++)     //从中心点查到x最右侧
	{
		if(!ReadColor(i, oldY))
			FailCount++;    //不匹配计数自加1

        if( FailCount> ((condition->WIDTH_MIN)/ALLOW_FAIL_PER) | i>=240)
			break;
	}
	Xmax=i;     //更新X轴最大坐标值

	FailCount=0;    //清空错误次数
	for(i=oldY; i>IMG_Y; i--)           //从中心点查到y最上侧
	{
		if(!ReadColor(oldX, i)) 
			FailCount++;    //不匹配计数自加1

        if( FailCount> ((condition->HEIGHT_MIN)/ALLOW_FAIL_PER) | i<=0)
			break;
	}
	Ymin=i;     //更新Y轴最小坐标值

	FailCount=0;    //清空错误次数
	for(i=oldY; i<IMG_Y+IMG_H; i++)     //从中心点查到y最下侧
	{
		if(!ReadColor(oldX, i))
			FailCount++;

        if( FailCount> ((condition->HEIGHT_MIN)/ALLOW_FAIL_PER)| i>=320)
			break;
	}
	Ymax=i;     //更新Y轴最大坐标值
	
    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;   //如果腐蚀后的区域没有超过最大限定区域且没有小于最小限定区域 有效!! 
        }
	return 0;
}

/**
 * @brief  用户将识别条件写入Condition指向的结构体中,该函数将返回目标的x,y坐标和长宽
 * @param  condition :TARGET_CONDITION结构体,存放希望的颜色数据阈值
 * @param  result :RESULT结构体,存放检测结果
 * @retval 1:检测成功;0:检测失败。
 */
int Trace(const TARGET_CONDITION* condition, RESULT* result_final)
{
	unsigned char i;
	static unsigned short x0, y0;
    static unsigned char Flag=0;
	static SEARCH_AREA area = {IMG_X, IMG_X+IMG_W, IMG_Y, IMG_Y+IMG_H};//搜索区域
	RESULT result;      //RESULT识别结果
    
    if(Flag==0)     //如果首次使用或上一次腐蚀失败
    {
        if(SearchCenter(&x0, &y0, condition, &area))    //搜索腐蚀中心并返回给x0,y0,如果成功搜索到,那么flag置1
        {
            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置1
            {
                Flag = 1;
                return 1;
            }
            else
            {
				Flag = 0;
				return 0;               
            }
        }      
    }
    
	//找到腐蚀中心 得到中点
	result.x = x0;      //如果flag!=0,说明上一次有腐蚀中心结果,所以直接使用上一次结果腐蚀即可,而不需要再次遍历图像搜索腐蚀中心
	result.y = y0;      //上一次的腐蚀中心赋值给这次的oldx,oldy
    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,返回失败值0
    {
        Flag=0;
        return 0;
    }
}

代码有点多,都有写注释哈,就不一一讲解了

3、显示代码

void GUI_DrawPoint(u16 x,u16 y)
{
    u8 color_value=0x80;
    color_value>>=y%8;
    Bmp_Minimize_SDRAM[x][y/8]|=color_value;
}

void GUI_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2)
{
	u16 t; 
	int xerr=0,yerr=0,delta_x,delta_y,distance; 
	int incx,incy,uRow,uCol; 

	delta_x=x2-x1; //计算坐标增量 
	delta_y=y2-y1; 
	uRow=x1; 
	uCol=y1; 
	if(delta_x>0)incx=1; //设置单步方向 
	else if(delta_x==0)incx=0;//垂直线 
	else {incx=-1;delta_x=-delta_x;} 
	if(delta_y>0)incy=1; 
	else if(delta_y==0)incy=0;//水平线 
	else{incy=-1;delta_y=-delta_y;} 
	if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 
	else distance=delta_y; 
	for(t=0;t<=distance+1;t++ )//画线输出 
	{  
		GUI_DrawPoint(uRow,uCol);//画点 
		xerr+=delta_x ; 
		yerr+=delta_y ; 
		if(xerr>distance) 
		{ 
			xerr-=distance; 
			uRow+=incx; 
		} 
		if(yerr>distance) 
		{ 
			yerr-=distance; 
			uCol+=incy; 
		} 
	}  
} 

void GUI_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2)
{
	GUI_DrawLine(x1,y1,x2,y1);
	GUI_DrawLine(x1,y1,x1,y2);
	GUI_DrawLine(x1,y2,x2,y2);
	GUI_DrawLine(x2,y1,x2,y2);
}

在二值化数据中画矩形

if(Trace(&condition0, &result))
{
    for(y_size=0;y_size<240;y_size++)     /*检测到物体,清空二值化数据*/
    {
        for(x_size=0;x_size<40;x_size++)
        {
            Bmp_Minimize_SDRAM[y_size][x_size]=0;
        }
    }
    /*画矩形,和中心点*/
    GUI_DrawRectangle ( result.x-result.w/2, result.y-result.h/2, result.x+result.w/2, result.y+result.h/2);
    GUI_DrawLine(result.x-10,result.y,result.x+10,result.y);
    GUI_DrawLine(result.x,result.y-10,result.x,result.y+10);
}

识别物体满足颜色和大小的判断条件,更新二值化数组,用矩形框出物体,并标出中心点 

ov7725 stm32,stm32,图像处理


总结

第一次写博客,有诸多不足,望多包涵,后续会更新多物体识别和图形识别。

END...

参考文章

stm32 OV7670/摄像头模块颜色区域定位(腐蚀中心算法)_摄像头模块颜色识别_闰土小蒋的博客-CSDN博客

识别车牌-识别颜色-基于stm32f4 ov7670(无晶振,无fifo,ov7725,ov2640类似可用)_stm32f4 ov7670颜色识别_qq斯国一的博客-CSDN博客

STM32+ov7725图像识别(HSL原理)_stm32图像识别_大桶矿泉水的博客-CSDN博客

《STM32》EasyTrace物体追踪 源代码个人注释+完整例程 - 知乎文章来源地址https://www.toymoban.com/news/detail-675524.html

到了这里,关于STM32 OV7725摄像头模块识别颜色物体(1)--HSL二值化和腐蚀中心算法,并用串口输出数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA 20个例程篇:19.OV7725摄像头实时采集送HDMI显示(四)

            在介绍完OV7725初始化配置和视频采集模块后,就到了整个项目的核心部分即DDR3乒乓存储图像模块,为了实现整个FPGA项目工程当中良好的实时性,乒乓操作在广泛应用在FPGA视频加速处理和数字信号处理中。        关于乒乓操作,有很多的FPGA相关书籍都多多少少做了

    2024年02月02日
    浏览(49)
  • 学习笔记之STM32的ov7670摄像头实验

    目录 1. OV7670摄像头模块 1.1 OV7670 传感器内置功能模块 1.2 OV7670模块的引脚  1.3 OV7670的时序图 1.4 OV7670的分辨率及其计算 2. FIFO模块 2.1 FIFO的简介 2.2 FIFO的信号 2.3 常用的FIFO数据存储器 3. BMP编码   3.1 BMP文件的组成 3.2 BMP编码步骤 4. 摄像头实验 4.1 工作流程 4.2 主要函数 4.3

    2024年02月04日
    浏览(45)
  • stm32(SCCB)+ov7670摄像头输出图像程序

    一、简介:   OV7670一般模块指低成本数字输出CMOS摄像头,其摄像头包含30w像素的CMOS图像感光芯片,3.6mm焦距的镜头和镜头座,板载CMOS芯片所需要的各种不同电源(电源要求详见芯片的数据文件),板子同时引出控制管脚和数据管脚,方便操作和使用。 二、管脚定义 3V3---

    2024年02月13日
    浏览(47)
  • 通过stm32cubemx配置DCMI驱动ov5640摄像头

    打开stm32cubemx选择芯片 选择外部时钟源 选择debug方式 配置dcmi   打开dma 打开dcmi中断 选择合适的io 设置reset、pwdn、scl、sda引脚,注意scl和sda设置为开漏输出,之前参考别人的设置为推挽输出,导致一直没有成功,不知道什么原因  配置时钟 配置项目 最后生成代码 系统初始化

    2024年02月13日
    浏览(52)
  • 【FPGA】摄像头模块OV5640

    开发板:正点原子的达芬奇开发板(或MicroPhase的Z7-Lite 7020开发板) FPGA型号:XC7A35TFGG484-2(或XC7Z020CLG400-2) Vivado版本:2020.2 参考课程链接:正点原子手把手教你学FPGA-基于达芬奇开发板 A7 OV5640模块:正点原子ATK-OV5640   OV5640是OV(OMNIVISION)公司设计的一款CMOS图像传感器,

    2024年03月21日
    浏览(50)
  • STM32连接OV2640摄像头串口方式在PyQt5界面上展示视频界面 以及使用MQTT方式进行展示

    STM32连接OV2640 视频源接入到PyQt5界面中 作为AI模型的输入,视频流是非常重要的。在本文中,我们将介绍如何将STM32连接到OV2640摄像头,并将视频流接入到PyQt5界面中。 硬件连接 首先,我们需要将OV2640摄像头连接到STM32上。我们可以使用以下连接方式: OV2640 | STM32 ------|------

    2024年02月06日
    浏览(60)
  • 33、基于STM32单片机车牌识别系统摄像头图像处理系统设计

    毕设帮助、开题指导、技术解答(有偿)见文末。 目录 摘要 一、硬件方案 二、设计功能 三、实物图 四、原理图 五、PCB图 六、程序源码 七、资料包括 随着汽车工业的迅猛发展,我国汽车拥有量急剧增加。停车场作为交通设施的组成部分,随着交通运输的繁忙和不断发展,

    2024年02月15日
    浏览(45)
  • 76、基于STM32单片机车牌识别摄像头图像处理扫描设计(程序+原理图+PCB源文件+相关资料+参考PPT+元器件清单等)

    单片机主芯片选择方案 方案一:AT89C51是美国ATMEL公司生产的低电压,高性能CMOS型8位单片机,器件采用ATMEL公司的高密度、非易失性存储技术生产,兼容标准MCS-51指令系统,片内置通用8位中央处理器(CPU)和Flash存储单元,功能强大。其片内的4K程序存储器是FLASH工艺的,这种单

    2024年02月12日
    浏览(55)
  • RK3568驱动OV13850摄像头模组调试过程

    品牌:Omnivision 型号:CMK-OV13850 接口: MIPI 像素:1320W OV13850彩色图像传感器是一款低电压、高性能1/3.06英寸1320万像素 CMOS图像传感器 ,使用OmniBSI+?技术提供了单-1320万像素(4224×3136)摄像头的功能。通过串行摄像头控制总线(SCCB)接口的控制,它提供了全帧、下采样、开窗的

    2023年04月27日
    浏览(57)
  • RK3568-android11-适配ov13850摄像头

    参考链接 相关概念 相关接口 相关协议 图像格式

    2024年02月09日
    浏览(76)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包