2021年电赛D题图像处理经验分享

目录

1:写在前面

  感谢四天三夜以来,我们所做的所有努力!

2:思路

3: 实现过程

 简易版图像腐蚀和膨胀

首先要对图像进行处理:

4:总结


1:写在前面

  感谢四天三夜以来,我们所做的所有努力!

讨论一天才确定下来选题导致时间十分紧张。开始的方案是想用树莓派,考虑到学习时长太高,遂还是选择了学了半年的32。选材上对我们产生了不小的限制,存储空间小,精确度低,很多问题第一次见。作为才升入大二的我们简直是难以攻克。

好险是负责通信的队友有重大突破,我们有了克服的动力。感谢大家的不放弃,明年,后年,我相信会有更好的结局。

结果:我们最后对角度的误差控制在1cm以内,角度误差在0.1°。是个非常不错的结果。

2:思路

  五版方案一改再改,大量的数据跑不起来。

我是组内负责图像处理和长度,角度计算的。所有这篇文章分享的也仅仅是我这一部分。

最开始面临的第一个问题就是:我难以找到图像的中心

出现的问题还有:框住了但是框的不稳,经常晃动

                             容易受外界环境干扰

                             读取到的数据经常出错

再结合题目:1.框住激光笔轮廓

                      2.测量长度

                      3.测量角度

所以我的思路大概是,先想办法对图像进行处理,得到中心点坐标。

长度使用:l=T^2*g/4Π^2

一个关于单摆的公式,可以完美的解决长度计算问题。

角度使用:argtan x/y

利用投影的知识,将两个摄像头(A,B)读取到的x值分别当作投影面的x,y。通过反正切函数来计算。

3: 实现过程

 简易版图像腐蚀和膨胀

 为了得到最重要的东西:一个准确的中心点数据

首先要对图像进行处理:

我开始得到的图像抖动明显,受光线影响较大。受光线影响,那就是需要调节阈值的问题了。

	if(KEY0==0)
	{
				delay_ms(190);
		chen=chen+1;
	}
		if(KEY1==0)
	{
				delay_ms(190);
		chen=chen-1;
	}
//按键来对阈值做调节


if(gm_red<=chen)  
 {

   color=0x0000;//黑色

				 }					 
else
{			
					
	color=0xffff;//白色					
				}

 会注意到,我对阈值判断时选择了红色,而不是常用的

#define GRAY_2_RGB565(gray)             ((u16)((((u8)(gray)>>3)<<11)|(((u8)(gray)>>2)<<5)|((u8)(gray)>>3)))

这是因为,我需要判断的物体颜色的单一的,不需要去平衡着计算。

然后我面对的就是框不住的问题。因为环境中颜色相同的物体较多且杂乱,为了减少这些干扰,我首先想到的是对图像做背景扣除

可是第一个问题就是,存储不了这么多数据。第二个问题是,每次比较需要大量的时间,导致图像卡顿。

然后我就想到了常用的图像腐蚀和膨胀

//src原图大小为Wid*Hei
//图像膨胀
void DilateFilter(unsigned char*  src, int Wid, int Hei)
{
	int  i, j, k, l, m;
	int max=0;
	l=0;
	for (i = 0; i < Wid; i++)
	{
		for (j = 0; j < Hei; j++)
		{
			for(m=-3;m<=3;m++){
				for(k=-3;k<=3;k++){
					if(*(src+i+m+(j+k)*Wid)>max){
						max=*(src+i+m+(j+k)*Wid);
					}
				}
			}
			temp_IMG[i+j*Wid]=max;
			max=0;
		}
	}
	memcpy(src,temp_IMG,sizeof(unsigned char)*Wid*Hei);
}

//图像腐蚀
void ErodeFilter(unsigned char*  src, int Wid, int Hei)
{
	int  i, j, k, l, m;
	int min=255;
	l=0;
	for (i = 0; i < Wid; i++)
	{
		for (j = 0; j < Hei; j++)
		{
			for(m=3;m<=3;m++){
				for(k=-3;k<=3;k++){
					if(*(src+i+m+(j+k)*Wid)<min){
						min=*(src+i+m+(j+k)*Wid);
					}
				}
			}
			temp_IMG[i+j*Wid]=min;
			min=255;
		}
	}
	memcpy(src,temp_IMG,sizeof(unsigned char)*Wid*Hei);
}
//此处为作者xiaoyang0307文章中的代码

因为作者选用的是stm32+ov7725的方案,还是遇到了存储空间不够的问题,本来是想在这个代码基础上减少行列数。但是灵光一闪:

        已知我读取数据是以打点的形式由上到下,由左到右进行的。我需要读取的激光笔是一段连续的数据,而外界数据连续度和激光笔连续度不同,那么我利用连续的长度为判读依据,进行保留和删除,不就得到激光笔的形状了吗!

//标志位
if(color==0x0000)
{
	if(j<270)
    {	
	  biaozhi_x+=j;
	  biaozhi++;
		if(biaozhi>20)
		 {
			biaozhi_y+=i;
			num++;
			biaozhi=0;
		 }				
	}
}
else
{
 biaozhi=0;
}

至此为止,已经解决了百分之六十的问题,剩下的就是对数据的读取了。

长度L:

直接读取长度显然是不可取的。根据单摆的公式,我得知,我现在只需要得到周期就可以算出值了。

通过对单摆的现象分析,在摆动过程中一直有个规律:假设我们的单摆从左到右,那么我们下一帧的数据比上一帧大,到最高点时,会出现一个左边到右边的跳变。以这一点为起始点记做t1。然后激光笔继续摆动,下一帧数据会比上一帧小,直到另一个最高点处,发生跳变,记录此时为t2,然后下一帧数据比上一帧大,直到回到最开始的跳变点,记作t3。我们的周期就是t3-t1的值。

if(biaozhi_x<10||biaozhi_y<10)
{
	
		}
else
{
			
	k[0]=biaozhi_y;
	LCD_DrawRectangle(biaozhi_x-50,biaozhi_y-10,biaozhi_x+50,biaozhi_y+10);

if(((k[1]-k[0])>t)&&n!=2&&n!=3)
	{
		n=1;
				
	}
if(n==1&&((k[1]-k[0])<(-1*t)))
	{
		n=2;
	
		shu=tim_a=0;
	}
if(n==2&&((k[1]-k[0])>t))
	{
		y_max=biaozhi_y;
		x_max=biaozhi_x;
		n=3;
	}
if(n==3&&((k[1]-k[0])<(-1*t)))
	{ 
		n=4;
		shu=tim_a;
		//tim_a=0;
							
		y_min=biaozhi_y;
		x_min=biaozhi_x;
//	    shu=TIM1->CNT;
						}

这里注意到,我对一段连续值进行了去除处理,这里就是上文提到的“看不见的白点”

不过这个纯属编者对现象做出的判断,理论支持较少。

对周期的读取使用了定时器的功能,因为和通信有不少冲突,最终选择了高级定时器1,如果只是进行图像处理的读者可以使用其他定时器。

角度:

角度的算法上文也提到过,通过我们以上的处理,这一步就十分容易了。

我们需要得到x值,也就是激光笔从左边最高点到右边最高点的距离。

jiajiao=atan((double)141/152)*180/3.1415926;
printf("%lf",jiajiao);
		 
LCD_ShowString(30,180,200,16,16,"jiajia:   00度");	 
LCD_ShowxNum(30+10*8,180,jiajiao,2,16, 0X80);	//显示小数部分

至此,该题需要的图像部分已经结束。希望这篇文章能给你帮助!

由于编者水平有限,本篇文章中很多问题的叙述并不清楚,如有不明白的地方欢迎大家给我留言。

4:总结

以实际现象为核心,有目的的改变。

本次题目难度确实有点超出我的预期,准备工作做的并不充分。

很多问题选择的方案并不合理,浪费了很多时间。

调试时太过于盲目,没有想清楚逻辑就去更改。

但是本次电赛还是让我受益良多,凌晨四点的学校,忙里偷闲看了场edg的比赛,深夜的加餐,还有为每一个突破而由衷的喜悦。

再次感谢四天三夜以来大家的努力,我们明年再见!

猜你喜欢

转载自blog.csdn.net/qq_54504522/article/details/121580267