超长板和并行板识别算法(仿真环境中获取到的图像)

背景

本来想利用面阵相机进行传送带上超长目标(超长面阵视野)的识别,在做实验的过程中,虽然在传送带上贴靶标,也布置好待识别目标,但在传送带运动过程中采集到的视频数据却不能完成拼接,这主要的原因是传送带上靶标的固定方式。传送带是带有弹性的,贴上不带弹性的靶标后,由于相对滑动,靶标固定不牢固。如果在靶标外再贴一圈胶布,则由于传送带左右两侧弹性不一致,导致整个一圈靶标全部滑向弹性较小的一边,直到被两边的限位装置卡住。

如果在虚拟环境中采集图片,则可以认为图像是等间隔采集的,传送带运动速度也是均匀的。这样可以省去算法的第一步,图像的拼接。而这一步在使用线阵相机后就不再是问题。

仿真环境

在这里插入图片描述

在虚拟环境下采集的视频如下:

超长板:
单个目标超出相机视野,需要通过目标的平移运动来进行目标轮廓的恢复。
在这里插入图片描述

并行板:
不仅单个目标超出相机视野,而且会有多个目标并行的情况,这对算法的逻辑提出了更高的要求。
在这里插入图片描述

问题分析:

对于超长相机视野的板,需要进行图像的拼接。原来做过用靶标进行拼接的算法,https://blog.csdn.net/iamqianrenzhan/article/details/89913917

对于在仿真环境下获取到的视频,由于传送带运动速度固定,相机采集帧率固定门,所有不需要进行拼接,每次从当前帧中提取固定高度的区域进行拼接就可以。这个固定的高度可以认为是传送带的“像素速度”,“像素速度”这个概念在后面需要给识别到的目标打时间戳时很关键。

代码:

需要事先定义一些变量:

int count = 0;
bool newpic = false;
Mat rawpic;		//原始RGB图像
Mat resultMat;   //拼接结果
Mat process;    //拼接结果后续处理

Mat current;   //当前正在处理的部分
Mat left; //上次留下的部分
bool isleft = false; //上次是否留下

每次取固定高度进行拼接:

Mat submat = frame(Rect(0, 0, 1280, BELTVELOCITY));
cvtColor(submat, submat, COLOR_RGB2GRAY);
GaussianBlur(submat, submat, Size(3, 3), 0);
threshold(submat, submat, 200, 255, THRESH_BINARY); //90以上
Mat temp = submat.clone();

//imshow("aa", submat);

if (resultMat.rows > 500)
{
	string filename = to_string(count) + ".bmp";
	//imwrite(filename, resultMat);
	process = resultMat.clone();
	resultMat = temp;
	newpic = true;
	cout << "拼接到一帧图像" << endl;
}
else
{
	if (count == 1)
		resultMat = temp;
	else
	{
		resultMat = mergeRows(temp, resultMat);  //.clone()
		newpic = false;
	}
}

图像处理:

if (newpic)
{
	//首先判断上次是否留下需要拼接的部分
	//需要拼接的情况有:上次找到的轮廓中至少有一个在上边缘。
	if (isleft)   //如果有,和本次图像进行拼接
	{
		current = mergeRows(process, left);
		isleft = false;
	}
	else    //如果没有,直接使用当前图像
	{
		current = process;
	}

	Mat temp = current.clone();
	bitwise_not(temp,temp);   //黑色背景,白色前景作为一个轮廓
	findContours(temp, contours_all, hierarchy_all, RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0));

	//Mat show;
	//cvtColor(process, show, COLOR_GRAY2BGR);
	//drawContours(show, contours_all, -1, Scalar(255, 0, 0),-1);

	//imshow("轮廓", show);
	//string filename = to_string(count) + ".bmp";
	//imwrite(filename, show);

	int max = 0;  //不能裁的轮廓的最下
	bool need = false;
	for (int i = 0; i < contours_all.size(); i++)  //对每一条轮廓进行处理
	{
		//判断该轮廓是否在边缘,不在,进行提取处理,否则,进行拼接操作
		int edge;
		for (int j = 0; j < contours_all[i].size(); j++)
		{
			if (contours_all[i][j].y == 0)
			{
				edge = 1;    //上边缘
				break;
			}
			else if (contours_all[i][j].y == temp.rows-1)
			{
				edge = -1;    //下边缘
				break;
			}
			else
				edge = 0;
		}

		if (edge == 0)  //如果一个轮廓不在边缘,需要进行提取
		{
			//输出轮廓
			Mat show;
			cvtColor(current, show, COLOR_GRAY2BGR);
			drawContours(show, contours_all, i, Scalar(255, 0, 0),-1);
			imshow("轮廓", show);
			//计算轮廓重心位置
			Point p;
			GetContourCenter(contours_all[i], p);
			cout << "重心纵坐标:" << p.y << endl;
			//图片产生时间计算
			int outcount = count - p.y / BELTVELOCITY;
			string filename = to_string(count) + "-" + to_string(outcount) + ".bmp";
			imwrite(filename, show);
			//轮廓颜色判断
			
			//轮廓长宽,匹配情况判读

			//使用时间戳和像素x值进行判重
		}
		else if(edge == 1)  //如果在上边缘,需要进行拼接操作
		{
			//找到所有轮廓的最下
			for (int j = 0; j < contours_all[i].size(); j++)
			{
				if (contours_all[i][j].y > max)
					max = contours_all[i][j].y;
			}
			//置需要裁剪标志位
			need = true;
		}
		else  //如果在下边缘,不做处理
		{

		}
	}

	//把是否需要裁剪的判断放到循环外面,主要为了处理多个轮廓都和上边缘接触。
	/*
	如果没有和上边缘相交的轮廓,则它们已经被提取出去了
	(这还有一个bug,即一个短板和长板在一起时,短板可能会被识别多次。
	但是可以使用时间戳和像素x位置进行判重)
	*/
	if (need)
	{
		max = min(max + 10, temp.rows - 1);
		Mat temp = current(Rect(0, 0, 1280, max + 10));
		left = temp.clone();
		isleft = true;
	}
	newpic = false;
}

处理结果:

超长板:
识别帧-时间戳-颜色-轮廓信息
在这里插入图片描述
并行板:
识别帧-时间戳
在这里插入图片描述

用到的函数

把视频处理成需要的分辨率

void processvideo()
{
	VideoWriter writer;
	string outFlie = "testoutputp2.avi";

	Size videoSize(1280, 320);
	int rate = 30;

	writer.open(outFlie, VideoWriter::fourcc('M', 'J', 'P', 'G'), rate, videoSize);

	VideoCapture cap("testp2.avi");

	if (!cap.isOpened())
	{
		return;
	}
	Mat frame;
	int count = 0;
	string show;
	while (1)
	{
		cap >> frame;
		if (frame.empty())
			break;
		//进行分辨率转换
		Mat submat = frame(Rect(0, 0, 512, 128));
		Size size(1280, 320);
		Mat dst;
		resize(submat, dst, size);
		writer.write(dst);
		if (waitKey(1) >= 0)
			break;
	}
	writer.release();
}

图像拼接

Mat mergeRows(Mat A, Mat B)
{
	// cv::CV_ASSERT(A.cols == B.cols&&A.type() == B.type());
	int totalRows = A.rows + B.rows;
	Mat mergedDescriptors(totalRows, A.cols, A.type());
	Mat submat = mergedDescriptors.rowRange(0, A.rows);
	A.copyTo(submat);
	submat = mergedDescriptors.rowRange(A.rows, totalRows);
	B.copyTo(submat);
	return mergedDescriptors;
}

获取轮廓的重心

void GetContourCenter(vector<Point> contour, Point &p)
{
	Moments m = moments(contour);
	p.x = m.m10 / m.m00;
	p.y = m.m01 / m.m00;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仟人斩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值