使用 OpenCV 创建视频(74)

news/2024/9/22 21:01:32/
 返回:OpenCV系列文章目录(持续更新中......)
上一篇:OpenCV 库来捕获和处理视频输入和相似度测量(73)
下一篇 :OpenCV系列文章目录(持续更新中......)

目标

每当您使用视频源时,您最终可能希望将图像处理结果保存为新视频文件的形式。对于简单的视频输出,您可以使用专为此设计的 OpenCV 内置 cv::VideoWriter 类。

  • 如何使用 OpenCV 创建视频文件
  • 您可以使用 OpenCV 创建什么类型的视频文件
  • 如何从视频中提取给定的颜色通道

作为一个简单的演示,我将只将输入视频文件的一个 BGR 颜色通道提取到新视频中。您可以从应用程序的控制台行参数中控制应用程序的流:

  • 第一个参数指向要处理的视频文件
  • 第二个参数可能是字符之一:R G B。这将指定要提取的通道。
  • 最后一个参数是字符 Y(是)或 N(否)。如果为否,则用于输入视频文件的编解码器将与用于输出的编解码器相同。否则,将弹出一个窗口,允许您选择要使用的编解码器。

例如,有效的命令行如下所示:

video-write.exe video/Megamind.avi R Y

源代码

您也可以在 OpenCV 源库的文件夹samples/cpp/tutorial_code/videoio/video-write/中找到源代码和这些视频文件,或从此处下载。

#include <iostream> // for standard I/O
#include <string> // for strings#include <opencv2/core.hpp> // Basic OpenCV structures (cv::Mat)
#include <opencv2/videoio.hpp> // Video writeusing namespace std;
using namespace cv;static void help()
{cout<< "------------------------------------------------------------------------------" << endl<< "This program shows how to write video files." << endl<< "You can extract the R or G or B color channel of the input video." << endl<< "Usage:" << endl<< "./video-write <input_video_name> [ R | G | B] [Y | N]" << endl<< "------------------------------------------------------------------------------" << endl<< endl;
}int main(int argc, char *argv[])
{help();if (argc != 4){cout << "Not enough parameters" << endl;return -1;}const string source = argv[1]; // the source file nameconst bool askOutputType = argv[3][0] =='Y'; // If false it will use the inputs codec typeVideoCapture inputVideo(source); // Open inputif (!inputVideo.isOpened()){cout << "Could not open the input video: " << source << endl;return -1;}string::size_type pAt = source.find_last_of('.'); // Find extension pointconst string NAME = source.substr(0, pAt) + argv[2][0] + ".avi"; // Form the new name with containerint ex = static_cast<int>(inputVideo.get(CAP_PROP_FOURCC)); // Get Codec Type- Int form// Transform from int to char via Bitwise operatorschar EXT[] = {(char)(ex & 0XFF) , (char)((ex & 0XFF00) >> 8),(char)((ex & 0XFF0000) >> 16),(char)((ex & 0XFF000000) >> 24), 0};Size S = Size((int) inputVideo.get(CAP_PROP_FRAME_WIDTH), // Acquire input size(int) inputVideo.get(CAP_PROP_FRAME_HEIGHT));VideoWriter outputVideo; // Open the outputif (askOutputType)outputVideo.open(NAME, ex=-1, inputVideo.get(CAP_PROP_FPS), S, true);elseoutputVideo.open(NAME, ex, inputVideo.get(CAP_PROP_FPS), S, true);if (!outputVideo.isOpened()){cout << "Could not open the output video for write: " << source << endl;return -1;}cout << "Input frame resolution: Width=" << S.width << " Height=" << S.height<< " of nr#: " << inputVideo.get(CAP_PROP_FRAME_COUNT) << endl;cout << "Input codec type: " << EXT << endl;int channel = 2; // Select the channel to saveswitch(argv[2][0]){case 'R' : channel = 2; break;case 'G' : channel = 1; break;case 'B' : channel = 0; break;}Mat src, res;vector<Mat> spl;for(;;) //Show the image captured in the window and repeat{inputVideo >> src; // readif (src.empty()) break; // check if at endsplit(src, spl); // process - extract only the correct channelfor (int i =0; i < 3; ++i)if (i != channel)spl[i] = Mat::zeros(S, spl[0].type());merge(spl, res);//outputVideo.write(res); //save oroutputVideo << res;}cout << "Finished writing" << endl;return 0;
}

视频的结构

首先,您应该对视频文件的外观有所了解。每个视频文件本身都是一个容器。容器的类型以文件扩展名表示(例如 avimov 或 mkv)。这包含多个元素,例如:视频源、音频源或其他轨道(例如字幕)。这些源的存储方式由用于每个源的编解码器决定。对于音轨,常用的编解码器是mp3aac。对于视频文件,列表在某种程度上更长,包括 XVID、DIVXH264 或 LAGSLagarith 无损编解码器)等名称。您可以在系统上使用的编解码器的完整列表仅取决于您安装了什么编解码器。

如您所见,视频可能会变得非常复杂。但是,OpenCV 主要是一个计算机视觉库,而不是视频流、编解码器和写入库。因此,开发人员试图使这部分尽可能简单。因此,用于视频容器的 OpenCV 仅支持 avi 扩展,即其第一个版本。这样做的直接限制是您不能保存大于 2 GB 的视频文件。此外,您只能在容器内创建和扩展单个视频轨道。此处不支持音频或其他轨道编辑。尽管如此,您系统上存在的任何视频编解码器都可能有效。如果您遇到其中一些限制,您将需要研究更专业的视频编写库,例如 FFmpeg 或编解码器,如 HuffYUVCorePNG 和 LCL。或者,使用 OpenCV 创建视频轨道并使用音轨扩展它,或者使用 VirtualDub 或 AviSynth 等视频处理程序将其转换为其他格式。

VideoWriter 类

此处编写的内容基于以下假设:您已经阅读了使用 OpenCV 的视频输入和相似度测量教程,并且您知道如何阅读视频文件。要创建视频文件,您只需要创建 cv::VideoWriter 类的实例。您可以通过构造函数中的参数指定其属性,也可以稍后通过 cv::VideoWriter::open 函数指定其属性。无论哪种方式,参数都是相同的:1. 在其扩展中包含容器类型的输出的名称。目前仅支持 avi。我们从输入文件构造它,将要使用的通道名称添加到其中,然后使用容器扩展完成它。

const string source = argv[1]; // the source file name
string::size_type pAt = source.find_last_of('.'); // Find extension point
const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi"; // Form the new name with container
  1. 用于视频轨道的编解码器。现在,所有视频编解码器都有一个唯一的短名称,最多四个字符。因此,XVID、DIVX 或 H264 名称。这称为四字符代码。您也可以使用其 get 函数从输入视频中询问此问题。由于 get 函数是通用函数,因此它始终返回双精度值。双精度值存储在 64 位上。四个字符是四个字节,即 32 位。这四个字符以双精度的下 32 位编码。丢弃上面 32 位的一种简单方法是将此值转换为 int
    VideoCapture inputVideo(source); // Open input
    int ex = static_cast<int>(inputVideo.get(CAP_PROP_FOURCC)); // Get Codec Type- Int form

    OpenCV 在内部使用此整数类型,并期望将其作为其第二个参数。现在,要从整数形式转换为字符串,我们可以使用两种方法:按位运算符和并集方法。第一个从 int 中提取字符看起来像(一个“and”操作,一些移动并在末尾添加一个 0 以关闭字符串):
    char EXT[] = {ex & 0XFF , (ex & 0XFF00) >> 8,(ex & 0XFF0000) >> 16,(ex & 0XFF000000) >> 24, 0};
    您可以对联合执行以下操作:​​​​​​​
    union { int v; char c[5];} uEx ;
    uEx.v = ex; // From Int to char via union
    uEx.c[4]='\0';
    这样做的优点是转换是在分配后自动完成的,而对于按位运算符,您需要在更改编解码器类型时执行操作。如果您事先知道编解码器的四个字符代码,则可以使用 CV_FOURCC 宏来构建整数:​​​​​​​
    CV_FOURCC('P','I','M,'1') // this is an MPEG1 codec from the characters to integer
    如果通过此参数减去 1,则在运行时会弹出一个窗口,其中包含系统上安装的所有编解码器,并要求您选择要使用的编解码器:

  1. 输出视频的每秒帧数。同样,在这里,我使用 get 函数保持每秒输入视频帧数。
  2. 输出视频的帧大小。在这里,我也使用 get 函数保持每秒输入视频帧大小。
  3. 最后一个参数是可选的。默认情况下为 true,并表示输出将是彩色的(因此对于写入,您将发送三个通道图像)。要创建灰度视频,请在此处传递 false 参数。

以下是我在示例中如何使用它:

VideoWriter outputVideo;
Size S = Size((int) inputVideo.get(CAP_PROP_FRAME_WIDTH), //Acquire input size(int) inputVideo.get(CAP_PROP_FRAME_HEIGHT));
outputVideo.open(NAME , ex, inputVideo.get(CAP_PROP_FPS),S, true);

之后,使用 cv::VideoWriter::isOpened()函数来确定打开的操作是否成功。当 VideoWriter 对象被销毁时,视频文件将自动关闭。成功打开对象后,可以使用类的 cv::VideoWriter::write 函数按顺序发送视频帧。或者,您可以使用其重载运算符<<:

outputVideo.write(res); //or
outputVideo << res;

从 BGR 图像中提取颜色通道意味着将其他通道的 BGR 值设置为零。您可以使用图像扫描操作或使用拆分和合并操作来执行此操作。首先将通道拆分为不同的图像,将其他通道设置为相同大小和类型的零图像,最后将它们合并回来:

split(src, spl); // process - extract only the correct channel
for( int i =0; i < 3; ++i)if (i != channel)spl[i] = Mat::zeros(S, spl[0].type());
merge(spl, res);

把所有这些放在一起,你会得到上面的源代码,其运行时结果将显示围绕这个想法的东西:

您可以在 YouTube 上观察此操作时实例。

参考文献:

1、《Creating a video with OpenCV》------Bernát Gábor


http://www.ppmy.cn/news/1457394.html

相关文章

Android Studio之ImageView

ImageView是图像显示控件&#xff0c;与图形显示有关的属性说明如下: scaleType&#xff1a;指定图形的拉伸类型&#xff0c;默认是fitCenter。src&#xff1a;指定图形来源&#xff0c;src图形按照scaleType拉伸。 注意背景图不按scaleType指定的方式拉伸&#xff0c;背景默…

等保测评考试重点题库分享上

一、单选题 1、下列不属于网络安全测试范畴的是&#xff08;C&#xff09; A&#xff0e;结构安全 B.便捷完整性检查 C.剩余信息保护 D.网络设备防护 2、下列关于安全审计的内容说法中错误的是&#xff08;D&#xff09; A&#xff0e;应对网络系统中的网络设备运行情况、网…

AI-数学-高中53-离散型随机变量的均值与方差

原作者视频&#xff1a;【随机变量】【一数辞典】3离散型随机变量的均值与方差_哔哩哔哩_bilibili 标准差 方差开根

0060__设计模式

1. 简单工厂模式( Simple Factory Pattern ) — Graphic Design Patterns 工厂模式 | 菜鸟教程 【设计模式——学习笔记】23种设计模式——建造者模式Builder&#xff08;原理讲解应用场景介绍案例介绍Java代码实现&#xff09;-CSDN博客

典型相关分析注意事项

在进行典型相关分析时&#xff0c;有几个注意事项需要考虑&#xff1a; 数据假设检验&#xff1a;在进行典型相关分析之前&#xff0c;需要检验数据是否符合典型相关分析的假设&#xff0c;例如变量之间的线性关系、正态分布等。如果数据不符合这些假设&#xff0c;分析结果可能…

人脸识别开源算法库和开源数据库

目录 1. 人脸识别开源算法库 1.1 OpenCV人脸识别模块 1.2 Dlib人脸识别模块 1.3 SeetaFace6 1.4 DeepFace 1.5 InsightFace 2. 人脸识别开源数据库 2.1 CelebA 2.2 LFW 2.3 MegaFace 2.4 Glint360K 2.5 WebFace260M 人脸识别 (Face Recognition) 是一种基于人的面部…

Kafka应用Demo:按主题订阅消费消息

安装环境 Kafka安装可参考官方网站的指导(https://kafka.apache.org/quickstart), 按步骤解压压缩包&#xff0c;修改配置。然后再启动zookeeper和kafka-server即可。 需要注意的一点&#xff1a;如果是在VMware虚拟机上启动的kafka, 需要修改一下server.properties配置文件&am…

Python基础之运算符操作

在Python中&#xff0c;运算符的作用就是用于执行各种的运算操作&#xff0c;常见的运算符有算数运算符、比较运算符、逻辑运算符、赋值运算符、成员运算符、身份运算符等。下面我们就来看看在Python中这些运算的详细操作。 算术运算符 算术运算符是用来执行一些基本的数学运…