使用多线程对四幅视频图像进行拼接并实时显示

本文介绍了一个使用OpenCV在C++中实现的多线程视频图像拼接项目,通过三个线程分别处理四幅视频的不同部分,并演示了如何利用OpenCV的VideoCapture和Mat对象进行图像截取、拼接和显示。
摘要由CSDN通过智能技术生成

引言

项目中需要用到多线程对四幅视频图像进行拼接,但是目前还未采集用于拼接图像数据,目前先使用同一幅图像(四个图像文件,视频的内容相同),从四幅视频中分别截取一块区域,直接将截取到的图像拼接在一起。

设计原理

线程划分

分为三个线程,每个线程的具体工作如下所示

 线程1:

  • 读取video1和video2的视频图像
  • 获取视频图像的宽度和高度
  • 取video1每一帧的左边四分之一图像
  • 取video2中上图所示的四分之一图像区域
  • 将获取的两幅图像直接拼接在一起

线程2:

线程2对video3和video4进行操作,流程和线程1类似。线程1和线程2属于并行线程互不干扰。不需要加锁

线程3:

  • 获取线程1中拼接好的一帧图像
  • 获取线程2中拼接好的一帧图像(与线程1获得的图像是同一帧)
  • 将获取的两幅图像进行拼接
  • 显示拼接结果

 线程之间的逻辑

        线程1与线程2属于并行线程,互相独立,没有共有的数据;线程3会用到线程1和线程2得到的数据,所以在设计时线程1和线程2之间不用考虑加锁等防止同时操作同一块内存的方法;反之,线程3需要用到前两个线程得到的数据,需要通过合理的设计防止同时操作同一内存位置的数据

线程实现

线程1

获取video1中每一帧的最左边的四分之一图像,获取video2中左边第二个四分之一区域的图像,然后将两幅图像拼接,涉及的操作如下:

  • 获取两个视频中每一帧的感兴趣区域
  • 将获取的两幅感兴趣区域的图像进行拼接
  • 将拼接的结果存放到全局变量vector<Mat> dst12中
  • 将存入的图像数放在全局变量num12中

对应的代码如下所示:

void stitch12(const string & video1, const string & video2) {
	VideoCapture cap1(video1);
	VideoCapture cap2(video2);

	if (!cap1.isOpened() || !cap2.isOpened()) {
		cout << "Failed to open the videos!" << endl;
		return;
	}

	//计算想要获取的感兴趣区域
	int W = cap1.get(CAP_PROP_FRAME_WIDTH);
	int H = cap2.get(CAP_PROP_FRAME_HEIGHT);
	Rect roi1(0, 0, W/4, H);
	Rect roi2(W / 4, 0, W / 4, H);

	
	while (true) {
		Mat fram1, fram2;
		cap1 >> fram1;
		cap2 >> fram2;

		if (fram1.empty() || fram2.empty()) {
			break;
		}

		Mat result;
		Mat r1 = fram1(roi1).clone();
		Mat r2 = fram2(roi2).clone();

		hconcat(r1, r2, result);
		dst12.push_back(result);
		num12++;
		imshow("dst12", result);
		waitKey(50);
	}

}

 线程2

获取video3中每一帧的最右边的四分之一图像,获取video3中右边边第二个四分之一区域的图像,然后将两幅图像拼接,涉及的操作如下:

  • 获取两个视频中每一帧的感兴趣区域
  • 将获取的两幅感兴趣区域的图像进行拼接
  • 将拼接的结果存放到全局变量vector<Mat> dst34中
  • 将存入的图像数放在全局变量num34中

线程3

此线程要获取前两个线程得到的拼接图像,并将两幅图像拼接,涉及到对共享内存的读写操作

逻辑分析

  1.  只能读取已经存好的数据对应的内存位置,正在存储的内存位置不能读取;(不能同时对一个内存区域进行读写操作)
  2. 由于要读取要两幅对应帧的图像,对图像进行拼接,所以读取的图像要一一对应

代码实现

总上所述,对于上图的情况,只能对前三幅图像进行读取并拼接,所以线程3的代码如下所示:

void stitch() {
	int i = 0;//存储图像的容器对应的小标
	Mat result;
	while (true) {
        //判断图像是否可以读取
		if ((i+1) <= min(num12, num34)){
			Mat img1 = dst12[i];
			Mat img2 = dst34[i];
			if (img1.empty() || img2.empty()) {
				break;
			}
			hconcat(img1, img2, result);
			imshow("result", result);
			waitKey(50);
			i++;
		}	
		//cout << "Wait data write ..." << endl;
		if (waitKey(1) == 27) { // 27是ESC的ASCII码
			break;
		}
	}
}

完整代码

#include <iostream>
#include <thread>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

vector<Mat> dst12;	//储存前两幅图像的拼接结果
vector<Mat> dst34;	//存储后两幅图像的拼接结果
vector<Mat> dst;	//存储最终拼接结果
//存入的图像数量
int num12 = 0;		
int num34 = 0;


void stitch12(const string & video1, const string & video2) {
	VideoCapture cap1(video1);
	VideoCapture cap2(video2);

	if (!cap1.isOpened() || !cap2.isOpened()) {
		cout << "Failed to open the videos!" << endl;
		return;
	}

	//计算想要获取的感兴趣区域
	int W = cap1.get(CAP_PROP_FRAME_WIDTH);
	int H = cap2.get(CAP_PROP_FRAME_HEIGHT);
	Rect roi1(0, 0, W/4, H);
	Rect roi2(W / 4, 0, W / 4, H);

	
	while (true) {
		Mat fram1, fram2;
		cap1 >> fram1;
		cap2 >> fram2;

		if (fram1.empty() || fram2.empty()) {
			break;
		}

		Mat result;
		Mat r1 = fram1(roi1).clone();
		Mat r2 = fram2(roi2).clone();

		hconcat(r1, r2, result);
		dst12.push_back(result);
		num12++;
		imshow("dst12", result);
		waitKey(50);
	}

}

void stitch34(const string & video3, const string & video4) {
	VideoCapture cap3(video3);
	VideoCapture cap4(video4);

	if (!cap3.isOpened() || !cap4.isOpened()) {
		cout << "Failed to open the videos!" << endl;
		return;
	}

	//计算想要获取的感兴趣区域
	int W = cap3.get(CAP_PROP_FRAME_WIDTH);
	int H = cap4.get(CAP_PROP_FRAME_HEIGHT);
	Rect roi3(W / 2, 0, W / 4, H);
	Rect roi4(3*W/4, 0, W / 4, H);


	while (true) {
		Mat fram3, fram4;
		cap3 >> fram3;
		cap4 >> fram4;

		if (fram3.empty() || fram4.empty()) {
			break;
		}

		Mat result;
		Mat r3 = fram3(roi3).clone();
		Mat r4 = fram4(roi4).clone();
		hconcat(r3, r4, result);
		dst34.push_back(result);
		num34++;
		imshow("dst34", result);
		waitKey(50);
	}

}

void stitch() {
	int i = 0;
	Mat result;
	while (true) {
		if ((i+1) <= min(num12, num34)){
			Mat img1 = dst12[i];
			Mat img2 = dst34[i];
			if (img1.empty() || img2.empty()) {
				break;
			}
			hconcat(img1, img2, result);
			imshow("result", result);
			waitKey(50);
			i++;
		}	
		//cout << "Wait data write ..." << endl;
		if (waitKey(1) == 27) { // 27是ESC的ASCII码
			break;
		}
	}
}

int main() {
	std::string video1 = "E:/imgMoasic/mulThreadImgSticth/vedio/video1.mp4";
	std::string video2 = "E:/imgMoasic/mulThreadImgSticth/vedio/video2.mp4";
	std::string video3 = "E:/imgMoasic/mulThreadImgSticth/vedio/video3.mp4";
	std::string video4 = "E:/imgMoasic/mulThreadImgSticth/vedio/video4.mp4";

	thread t1(stitch12, video1, video2);
	thread t2(stitch34, video3, video4);
	thread t3(stitch);

	t1.join();
	t2.join();
	t3.join();

	destroyAllWindows();
	system("pause");
	return 0;
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
视频监控应用中,如何有效实现宽视场范围视频的完整获取,是视频监控系统的关键功能之一。本文针对多摄像头硬件平台,重点研究 360 度全景视频拼接的实现技术。在保证高质量的拼接图像的前提下,满足实际应用场景的实时性要求是本文算法设计的主要目标。针对全景拼接视频监控领域这一应用背景,本文对多摄像头系统全景拼接存在的一些优势和约束进行详细分析,并在此基础上,采用多个广角镜头进行视频图像采集,实现摄像头个数和单个镜头视角之间的合理折中。在控制成本的前提下,有效实现水平方向 360 度无盲区监控。 针对全景视频拼接算法的实时性能和拼接效果,本文重点在以下几个方面开展了研究工作: 1、本文采用普通的监控 CCD 单板机和广角镜头组装摄像单元,对多个摄像单元通道采用多线程同步视频采集。针对普通监控镜头采集图像质量不高的问题,提出基于颜色校正板的颜色校正方法,对输入图像进行预处理,有效改善图像质量。并采用基于灰度均值的方法,对相邻图像的重叠区域进行亮度调整。 2、本文基于经典的直线标定法思想,提出两步法镜头矫正方案。通过所提出的直线标定法对镜头进行一次矫正,然后通过手动设置并调节畸变参数,对矫正后的图像进行二次矫正,所提出的算法能保证良好的镜头矫正效果。 3、研究了柱面投影模型的原理,提出将柱面映射的投影中心修正为镜头畸变中心,克服了由于畸变和透视失真等因素造成的重叠区物体成像尺寸不一致的问题。然后,利用所提出的坐标映射表的方式,一次性实现镜头畸变矫正和柱面映射,有效提高了算法执行速度。 4、分析了基于 Harris 特征和 SIFT 特征的配准算法原理。SIFT 特征配准算法鲁棒性高,但难以满足硬件系统的实时性要求;基于 Harris 特征的配准算法复杂度低,但难以应对广角镜头畸变引起的图像质量差的问题,匹配性能较差。最后采用了基于积分图像的快速归一化互相关配准方案,实验验证了算法的可行性和有效性。 5、此外,在图像融合方面,基于经典的多频带融合算法,结合线性融合的思想,优化了一种简单的多分辨率线性融合方法。在保证融合质量的基础上,提升了速度。 关键字:多镜头多传感器;图像配准;全景拼接实时监控;广角镜头
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值