内容
概括
关键词
一、系统规划
1.1 系统基本方案
1.2 程序算法的具体流程
2. 视觉程序识别框架
2.1多线程
2.2 opencv配置文件
2.3 主函数
3. 装甲板识别算法
3.1 装甲板识别
3.2 识别函数介绍
四、目标位置解法
4.1 摄像头标定
4.2 根据小孔成像原理得到需要的转角
(这里打公式太麻烦了,我只是在文档里截了个图就忘了……)
4.3 角度测量验证
五、程序源码(代码及完整论文可私聊我qq2521170001获取)
概括:
该视觉操作的主要目标是通过摄像头识别其前方的矩形设备。
甲板(矩形装甲板装有灯条)判断其前面是否有装甲板,如果有,则
车身在哪里,当摄像头检测到装甲板上的灯条时,对图像进行预处理、过滤等。
过程,找到满足灯条条件的轮廓,匹配装甲板,然后计算出装甲板的位置,即相位
从装甲板上获取飞机的角度、距离、俯仰角等信息,并将这些信息发送到下位机,进而控制
在下位机上控制舵机的运动,使枪口中心始终朝向装甲板中心,实现自动瞄准功能。
能够。
关键词:机器视觉,相机,矩形装甲板,预处理,滤波算法
一、系统规划
1.1 系统基本方案
我的程序主要是基于 C++中的 Opencv 实现的,实际运行在 linux 环境下,摄像头驱
动采用的是 V4L2 驱动框架,接下来介绍一下该框架下视觉识别基本算法。
下面的思维导图展示了程序算法的整个流程:
1.2 程序算法的具体流程
①减少摄像头曝光:在装甲识别中,曝光是决定能否成功识别装甲板的关键因素。当我们拍摄可以自行发光的物体时,我们可以减少曝光时间,以减少环境光的影响。此时,可以对阈值化得到的二值图像进行进一步处理。
这是我比较的相机的正常曝光和低曝光之间的巨大差异:
为了便于灯条与其他光线的分离,曝光量一般设置得很低。下图为相机获取的实际图片:
②预处理:通过摄像头,我们可以连续获取当前图片,也就是一帧一帧的图像。算法处理的对象是这些图像中的每一个。因为我们要从图像中找到装甲板上的灯条,所以需要先对图像进行预处理,即排除图片中其他多余的光。
这是我的程序中使用的处理方法:
下图是预处理后的效果(二值化图像),可以看出除了灯条之外几乎所有地方都被排除了:
③找到符合灯条条件的轮廓:通过预处理得到的图片是二值图,即我们把可能是灯条的位置处理变成白色,其他地方都是黑的。此时可以通过Opencv的函数对轮廓用矩形框起来,然后通过几何关系判断其是否符合灯条的条件(角度,长宽比等条件)
下图为符合框选灯条条件的灯条:
④匹配装甲板:如果上一步得到的灯条个数大于1,就进行装甲板的匹配,主要是通过两个灯条的几何信息进行匹配(高度差,宽度差,角度差,形成的装甲板的角度,长宽比等信息),最终得到若干个装甲板。
下图显示了用黄色矩形选择的装甲板:
⑤选择最后的装甲板:如果上一步得到的装甲板个数大于0,则进行装甲板的选择。
⑥PNP结算装甲板信息:我们找到了装甲板的位置,还需要告诉舵机应当如何移动才能让枪管指向装甲板的中心。这里的如何移动,其实就是角度的偏移,左右偏多少度(yaw角),上下偏多少度(pitch角),相机距离装甲板的距离是多少,得到这些信息之后,发送给下位机,舵机就知道怎么动了。
最后,展示最终的算法效果图:
2. 视觉程序识别框架
2.1多线程
由于我们的摄像头每秒可以获取很多张图片,所以在实际处理中最好能在几毫秒内处理一张图片,否则会因为延迟而跟不上装甲板的运动。在程序中使用多线程是一个很好的实现加速的方法,因为相机的图片获取和我们对图片的处理是两个相对独立的进程,所以可以使用多线程来进行加速。
使用 opencv 的时候写程序通常的流程就是读入图片/视频/摄像头,对每张图片进行处理,处理后进行输出(显示图片/显示识别结果等等)。这种模式基本上是一条线走下来的,前一个步骤没有完成就无法进行下一步的操作。
使用多线程后,可以拆分每个步骤,使用单独的线程完成相应的操作。
由于使用了多线程,可以进行流水线操作,使得图像读取线程在图像处理线程处理这张图像时,可以读取下一张图像。
多结构结构框图如下:
2.2 opencv配置文件
我是在linux平台上使用qtcreate搭建的opencv程序,在程序中的.pro文件就是该项目的配置文件。
然后是头文件和源文件,这些是添加相应文件后 qtcreator 自动生成的,也可以手动添加或者手动注释。
2.3 主函数
主函数的主要功能是:作为一个入口,创建 ImgProdCons 类,执行初始化成员函数,创建多线程。
主要功能如下:
其中produceThread()函数负责获取图像保存到缓存队列中,consumeThread()函数负责对图图片的处理和指令的发送,而 senseThread()函数用于接收数据。
3. 装甲板识别算法
3.1 装甲板识别
装甲板识别步骤:
1、找出图片中所有的灯条
2、根据长宽比、面积大小和凸度来筛选灯条
3、对找出的灯条进行匹配找到合适的配对
4、配对灯条作为候选装甲板
在识别主程序中首先定义要用到的一些变量,定义时间变量t1记录当前时间用于之后的算法耗费时间的度量,之后获取待识别图片,图片可以通过produce线程获得也可以通过视频获得,之后将图片载入到armorDetector中进行识别,识别到后的返回值在
ArmorDetector.h中有定义:
enum ArmorFlag
{
ARMOR_NO = 0,
ARMOR_LOST = 1,
ARMOR_GLOBAL = 2,
ARMOR_LOCAL = 3
};
如果被识别,则获取装甲板矩形四个顶点的图像坐标,计算出装甲板相对于摄像头的空间坐标,然后可以将数据发送到下位机(发送到下位机就是以相机或装甲板的相对坐标值转换成的角度值为原点拍摄这张照片,可以跟踪)。
3.2识别函数介绍
识别函数int ArmorDetector::detect()函数是算法的核心,类中的其他函数都是为它服务的。
算法的第一步是存储上面第一步和第二步对应的灯条。
对于图像中红色物体来说,其rgb分量中r的值最大,g和b在理想情况下应该是0,同理蓝色物体的b分量应该最大。
如果识别红色物体可以直接用r通道-b通道。由于在低曝光下只有灯条是有颜色的,两通道相减后,其他区域的部分会因为r和b的值差不多而被减去,而蓝色灯条部分由于r通道比b通道的值小,相减后就归0了,也就是剩下的灰度图只留下了红色灯条。
①颜色识别处理流程如下:
// 功能:把一个3通道图像转换成3个单通道图像
split(_roiImg,channels);//分离色彩通道
if(_enemy_color==RED)
_grayImg=channels.at(2)-channels.at(0);//Get red-blue image;
Else
_grayImg=channels.at(0)-channels.at(2);//Get blue-red image;
②扩展处理:得到灰度图像后,需要进行阈值处理,得到二值图像,然后可以进行扩展处理,使图像中的轮廓更加明显。
Mat binBrightImg;
//阈值化
threshold(_grayImg, binBrightImg,_param.brightness_threshold
, 255, cv::THRESH_BINARY);
Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3));
//膨胀
dilate(binBrightImg, binBrightImg, element);
③ 寻找轮廓:这一步是整个算法中最耗时的部分。如果预处理做得好,可以大大减少寻找轮廓所花费的时间。
④ 找到轮廓后,开始遍历轮廓提取灯条:
⑤ 灯条匹配过滤:
⑥筛选找到的装甲板:我使用模板匹配的方法来判断它是否是装甲板。模板匹配特别适用于待识别图片不会发生变化的场景。选择合适的模板可以获得很高的准确率。
⑦筛选装甲板后,可能有多个装甲板。这时候,您需要选择一个进行跟踪。
四、目标位置解法
4.1 摄像头标定
因为我们需要图像中有关物理世界的信息,所以我们需要校准相机以获取相机内在函数。
校准板:
直接而在MATLAB的Command Window里面输入cameraCalibrator即可调用标定应用。
Matlab中标定如下:
点击show Undistorted即可看到无畸变的图像:
最后选择导出参数保存参数。
4.2 根据小孔成像原理得到需要的转角
(这里打公式太麻烦了,我只是在文档里截了个图就忘了……)
4.3 角度测量验证
我们怎么知道我们测量的角度是正确的?可以用三角形来测量。
将三角尺的30度尖对准摄像头,不断调整位置,使尖对准cx的像素值位置,之后不断调整摄像头,使三角尺的两条边在图像中平行于y轴,此时就可以选取三角尺边上的像素点来测量角度了,如下图:
拍摄的图像如下:
这个时候选择左边那条边测得的角度就是30度,选择右边的那条边测得的角度就是0度。
五、程序源码(代码及完整论文可私聊我qq2521170001获取)
版权声明:本文为博主qq_2521170001原创文章,版权归属原作者,如果侵权,请联系我们删除!
原文链接:https://blog.csdn.net/qq_41834692/article/details/123208349