赞
踩
目录
前阵子用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 地址中,重复上述过程。
网上也有很多现成的文章,分享几个链接
摄像头原理:
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 完成一副图像发送。
链接: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);
RGB是从颜色发光的原理来设计定的,通俗点说它的颜色混合方式就好像有红、绿、蓝三盏灯,当它们的光相互叠合的时候,色彩相混,而亮度却等于两者亮度之总和,越混合亮度越高,即加法混合。
红、绿、蓝三个颜色通道每种色各分为256阶亮度,在0时“灯”最弱——是关掉的,而在255时“灯”最亮。当三色灰度数值相同时,产生不同灰度值的灰色调,即三色灰度都为0时,是最暗的黑色调;三色灰度都为255时,是最亮的白色调。
在电脑中,RGB的所谓“多少”就是指亮度,并使用整数来表示。通常情况下,RGB各有256级亮度,用数字表示为从0、1、2...直到255。注意虽然数字最高是255,但0也是数值之一,因此共256级。
在计算机中图像基本是以RGB888格式显示的,24位图每个像素保存了32bit的数据,即RGB888+Alpha,Alpha就是半透明填充字节。对于真彩的图像而言,肉眼在16bit的时候已经难以分辨了,有些时候,可以将RGB888转换为RGB565来存储,减少了存储器的容量的同时,降低了数据量;在显示的时候,再次把RGB565转换为RGB888,实现数据宽度的匹配
RGB888->RGB565
只要提取相应单色高位即可(R5 G6 B5),但会导致低位的缺失,影响精度,而且无法恢复
RGB565->RGB888
填充相应单色低位即可
- 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 );
- }
HSL的H(hue)分量,代表的是人眼所能感知的颜色范围,这些颜色分布在一个平面的色相环上,取值范围是0°到360°的圆心角,每个角度可以代表一种颜色。色相值的意义在于,我们可以在不改变光感的情况下,通过旋转色相环来改变颜色。在实际应用中,我们需要记住色相环上的六大主色,用作基本参照:360°/0°红、60°黄、120°绿、180°青、240°蓝、300°洋红,它们在色相环上按照60°圆心角的间隔排列
HSL的S(saturation)分量,指的是色彩的饱和度,它用0%至100%的值描述了相同色相、明度下色彩纯度的变化。数值越大,颜色中的灰色越少,颜色越鲜艳,呈现一种从灰度到纯色的变化。
HSL的L(lightness)分量,指的是色彩的明度,作用是控制色彩的明暗变化。它同样使用了0%至100%的取值范围。数值越小,色彩越暗,越接近于黑色;数值越大,色彩越亮,越接近于白色。
- 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; //饱和度
- }
摄像头采集到的每个像素点,先由RGB格式转换成HSL格式,再与定义阈值进行对比,判断颜色是否和定义颜色匹配
- 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);
将存放在缓存中的二值化数据,通过简单位操作输出到调试助手
首先遍历寻找腐蚀中心,然后在之前腐蚀中心点处进行迭代向外寻找新的腐蚀中心。腐蚀算法从该点开始分别向上下左右四个方向进行读点,若点的颜色符合条件则往外读,等四个方向都结束后得到四个边缘点的坐标,记左边缘点的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的色块。
如果失败次数少于6次则改变查询方向 查询这色块的以X/2 为点 对y轴进行查询方法同上 ,如果y轴有6次失败则,退出循环不认同这个色块合格(因为太小了,失败次数又多),但如果少于六次失败,则认为这个色块识别成功 并记录下这个色块的中心点,这就是腐蚀中心 /。
- 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;
- }
- }
代码有点多,都有写注释哈,就不一一讲解了
- 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);
- }
识别物体满足颜色和大小的判断条件,更新二值化数组,用矩形框出物体,并标出中心点
第一次写博客,有诸多不足,望多包涵,后续会更新多物体识别和图形识别。
END...
参考文章
stm32 OV7670/摄像头模块颜色区域定位(腐蚀中心算法)_摄像头模块颜色识别_闰土小蒋的博客-CSDN博客
识别车牌-识别颜色-基于stm32f4 ov7670(无晶振,无fifo,ov7725,ov2640类似可用)_stm32f4 ov7670颜色识别_qq斯国一的博客-CSDN博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。