利用OpenCV将多个视频拼接在一起同时显示

1.引言

当您希望将多个视频按照不同的方式拼接在一起并同时显示时,OpenCV库提供了强大的功能来实现这个目标。在本篇博客中,介绍如何使用OpenCV来创建一个多视频拼接程序。通过命令行交互获取视频信息,并根据用户的选择,将视频按照水平、垂直或2x2网格的方式进行拼接,并最终保存视频。

2.工作环境

OpenCV4.5.4、vs2022

3.代码实现

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

int main()
{
	int n = 0;
	std::cout << "多视频【拼接】显示:" << std::endl;
	std::cout << "请输入你要拼接的视频数量(2-4):";
	std::cin >> n;
	if (n < 2 || n>4)
	{
		std::cout << "拼接视频数量超过限制,请重新输入......" << std::endl;
		return -1;
	}
	std::vector<std::string> video_paths;
	for (int i = 0; i < n; i++)
	{
		std::cout << "输入第" << i + 1 << "个视频路径:";
		std::string video_path;
		std::cin >> video_path;
		video_paths.push_back(video_path);
	}
	std::cout << "请选择拼接模式:(0)水平方向拼接;(1)垂直方向拼接;(2)2*2网格拼接(仅限输入视频数量为4)" << std::endl;
	int concate_mode;
	while (true)
	{
		std::cin >> concate_mode;
		if (concate_mode != 0 && concate_mode != 1 && concate_mode != 2)
		{
			std::cout << "拼接模式选择错误请重新选择:" << std::endl;
			continue;
		}
		if (concate_mode == 2 && n != 4)
		{
			std::cout << "拼接模式选择错误请重新选择:" << std::endl;
			continue;
		}
		else
		{
			break;
		}

	}

	std::vector<cv::VideoCapture>caps(n);
	for (int i = 0; i < n; i++)
	{
		if (!caps[i].open(video_paths[i])) {
			std::cerr << "错误:打开视频" << i << "失败!" << std::endl;
			return -1;
		}
	}
	// 创建VideoWriter写入结果  
	cv::VideoWriter writer;
	int codec = cv::VideoWriter::fourcc('M', 'J', 'P', 'G');
	double fps = caps[0].get(cv::CAP_PROP_FPS);
	int height = (int)caps[0].get(cv::CAP_PROP_FRAME_HEIGHT);
	int width = (int)caps[0].get(cv::CAP_PROP_FRAME_WIDTH);
	cv::Size outputFrame_Size;
	std::vector<cv::Mat>frames(n);
	switch (concate_mode)
	{
	case 0:
	{
		outputFrame_Size = cv::Size(width * n, height);
		writer.open("output.avi", codec, fps, outputFrame_Size);

		if (!writer.isOpened()) {
			std::cerr << "无法打开输出视频文件!" << std::endl;
			for (int i = 0; i < n; ++i) {
				caps[i].release(); // 释放资源
			}
			return -1;
		}

		while (true)
		{
			// 读取每段视频的当前帧
			bool allFramesRead = true;
			for (int i = 0; i < n; ++i)
			{
				caps[i] >> frames[i];
				// 检查是否结束
				if (frames[i].empty()) {
					allFramesRead = false;
					break;
				}
			}

			if (!allFramesRead) {
				break;
			}

			cv::Mat outputImg(outputFrame_Size, CV_8UC3);
			int offset = 0;
			for (const auto& img : frames) {
				img.copyTo(outputImg(cv::Rect(offset, 0, img.cols, img.rows)));
				offset += img.cols;
			}
			writer.write(outputImg);
		}
		writer.release();

		for (int i = 0; i < n; ++i) {
			caps[i].release(); // 释放资源
		}

		break;
	}
	case 1:
	{
		outputFrame_Size = cv::Size(width, height * n);
		writer.open("output.avi", codec, fps, outputFrame_Size);

		if (!writer.isOpened()) {
			std::cerr << "无法打开输出视频文件!" << std::endl;
			for (int i = 0; i < n; ++i) {
				caps[i].release(); // 释放资源
			}
			return -1;
		}

		while (true)
		{
			// 读取每段视频的当前帧
			bool allFramesRead = true;
			for (int i = 0; i < n; ++i)
			{
				caps[i] >> frames[i];
				// 检查是否结束
				if (frames[i].empty()) {
					allFramesRead = false;
					break;
				}
			}

			if (!allFramesRead) {
				break;
			}

			cv::Mat outputImg(outputFrame_Size, CV_8UC3);
			int offset = 0;
			for (auto& img : frames) {
				img.copyTo(outputImg(cv::Rect(0, offset, img.cols, img.rows)));

				offset += img.rows;
			}
			writer.write(outputImg);
		}
		writer.release();
		for (int i = 0; i < n; ++i) {
			caps[i].release(); // 释放资源
		}
		break;
	}
	case 2:
	{
		cv::Size outputFrame_Size = cv::Size(width * 2, height * 2);

		writer.open("output.avi", codec, fps, outputFrame_Size);

		if (!writer.isOpened()) {
			std::cerr << "无法打开输出视频文件!" << std::endl;
			for (int i = 0; i < n; ++i) {
				caps[i].release(); // 释放资源
			}
			return -1;
		}

		cv::Mat outputImg(outputFrame_Size, CV_8UC3);

		while (true)
		{
			bool allFramesRead = true;
			for (int i = 0; i < n; ++i)
			{
				caps[i] >> frames[i];
				if (frames[i].empty()) {
					allFramesRead = false;
					break;
				}
			}

			if (!allFramesRead) {
				break;
			}

			// 拼接视频帧成2x2网格
			frames[0].copyTo(outputImg(cv::Rect(0, 0, width, height)));
			frames[1].copyTo(outputImg(cv::Rect(width, 0, width, height)));
			frames[2].copyTo(outputImg(cv::Rect(0, height, width, height)));
			frames[3].copyTo(outputImg(cv::Rect(width, height, width, height)));

			writer.write(outputImg);
		}

		writer.release();

		for (int i = 0; i < n; ++i) {
			caps[i].release();
		}

		break;
	}
	default:
		break;

		return 0;

	}
}

注意:所拼接的视频需要注意长、宽统一,当多个视频的时长不一致时,以时间最短的为准。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值