应用程序实用工具——使用 OpenCV 创建视频 OpenCV v4.8.0

上一个教程使用 OpenCV 和相似度测量输入视频

下一个教程使用 Kinect 和其他兼容 OpenNI 的深度传感器

原作者Bernát Gábor
兼容性OpenCV >= 3.0

目标

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

  • 如何使用 OpenCV 创建视频文件
  • 使用 OpenCV 可以创建哪些类型的视频文件
  • 如何从视频中提取指定颜色通道

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

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

例如,有效的命令行如下

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

源代码

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

#include <iostream> // 用于标准 I/O
#include <string> // 用于字符串
#include <opencv2/core.hpp> // OpenCV 基本结构 (cv::Mat)
#include <opencv2/videoio.hpp> // 视频写入

using 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]; // 源文件名
 const bool askOutputType = argv[3][0] =='Y'; // 如果为假,将使用输入的编解码器类型
 VideoCapture inputVideo(source); // 打开输入
 if (!inputVideo.isOpened())
 {
 cout << "无法打开输入视频: " << source << endl;
 return -1}
 string::size_type pAt = source.find_last_of('.'); // 查找扩展点
 const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi"; // 使用容器形成新名称
 int ex = static_cast<int>(inputVideo.get(CAP_PROP_FOURCC)); // Get Codec Type- Int form
 // 通过比特运算符将 int 转换为 char
 char 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), // 获取输入大小
 (int) inputVideo.get(CAP_PROP_FRAME_HEIGHT));
 VideoWriter outputVideo; // 打开输出
 if (askOutputType)
 outputVideo.open(NAME,ex=-1,inputVideo.get(CAP_PROP_FPS),S,true)else
 outputVideo.open(NAME, ex, inputVideo.get(CAP_PROP_FPS), S, true)if (!outputVideo.isOpened())
 {
 cout << "无法打开输出视频进行写入: " << source << endl;
 return -1}
 cout << "输入帧分辨率: 宽度=" << S.width << " 高度=" << S.height
 << " of nr#: " << inputVideo.get(CAP_PROP_FRAME_COUNT) << endl;
 cout << "输入编解码器类型: " << EXT << endl;
 int channel = 2; // 选择要保存的通道
 switch(argv[2][0])
 {
 case 'R' : channel = 2; breakcase 'G' : channel = 1; breakcase 'B' : channel = 0; break}
 Mat src, res;
 vector<Mat> spl;
 for(;;) //显示窗口中捕获的图像并重复
 {
 inputVideo >> src; // 读取
 if (src.empty()) break; // 检查是否已结束
 split(src, spl); // 处理 - 仅提取正确的通道
 for (int i =0; i < 3; ++i)
 if (i != channel)
 spl[i] = Mat::zeros(S, spl[0].type())merge(spl, res)//outputVideo.write(res); //保存或
 outputVideo << res;
 }
 cout << "Finished writing" << endl;
 return 0}

视频的结构

首先,您应该了解视频文件的外观。每个视频文件本身就是一个容器。容器的类型由文件扩展名表示(如 avi、mov 或 mkv)。它包含多种元素,如:视频源、音频源或其他轨道(如字幕)。这些素材的存储方式由每个素材所使用的编解码器决定。音频轨道常用的编解码器是 mp3 或 aac。视频文件的编解码器则更长,包括 XVID、DIVX、H264 或 LAGS(Lagarith Lossless Codec)。您可以在系统中使用的全部编解码器列表取决于您安装的编解码器。

在这里插入图片描述

正如您所看到的,视频方面的事情可能会变得非常复杂。不过,OpenCV 主要是一个计算机视觉库,而不是一个视频流、编解码器和写入库。因此,开发人员试图让这部分尽可能简单。因此,用于视频容器的 OpenCV 仅支持其第一个版本的 avi 扩展名。这样做的一个直接限制是,你无法保存超过 2 GB 的视频文件。此外,您只能在容器内创建和扩展单个视频轨。这里不支持音频或其他音轨编辑。不过,您系统中的任何视频编解码器都可以使用。如果遇到上述限制,你需要了解更专业的视频编写库,如 FFmpeg 或 HuffYUV、CorePNG 和 LCL 等编解码器。作为替代方案,您可以使用 OpenCV 创建视频音轨,然后用音轨进行扩展,或者使用 VirtualDub 或 AviSynth 等视频处理程序将其转换为其他格式。

视频写入器类

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

const string source = argv[1]; // 源文件名
string::size_type pAt = source.find_last_of('.'); // 查找扩展点
const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi"; // 使用容器形成新名称
  1. 视频轨要使用的编解码器。现在,所有视频编解码器都有一个最多四个字符的唯一简短名称。因此,就有了 XVID、DIVX 或 H264 名称。这就是所谓的四字符代码。您也可以通过使用输入视频的 get 函数来查询。因为 get 函数是一个通用函数,它总是返回双数值。双数值存储在 64 位上。四个字符是四个字节,即 32 位。这四个字符被编码在 double 值的低 32 位。扔掉上面 32 位的简单方法是将该值转换为 int:
VideoCapture inputVideo(source); // 打开输入端
int ex = static_cast<int>(inputVideo.get(CAP_PROP_FOURCC)); // Get Codec Type- Int form

OpenCV 内部使用这种整数类型,并将其作为第二个参数。现在,要将整数形式转换为字符串,我们可以使用两种方法:位运算符和联合方法。第一种方法是从 int 中提取字符("和 "运算、移位并在字符串末尾添加 0 以关闭字符串):

char EXT[] = {ex & 0XFF , (ex & 0XFF00) >> 8,(ex & 0XFF0000) >> 16,(ex & 0XFF000000) >> 24, 0}

你也可以用 union 做同样的事情:

union { int v; char c[5];} uEx ;
uEx.v = ex; // 通过联合从 Int 到 char
uEx.c[4]='\0'

这样做的好处是在赋值后自动完成转换,而对于位运算符,则需要在改变编解码器类型时进行操作。如果事先知道编解码器的四位字符代码,可以使用 CV_FOURCC 宏来生成整数:

CV_FOURCC('P','I','M,'1') // 这是一个从字符到整数的 MPEG1 编解码器。

如果将该参数减一,运行时将弹出一个窗口,其中包含系统中安装的所有编解码器,并要求您选择要使用的编解码器:

在这里插入图片描述

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

下面是我在示例中的使用方法:

VideoWriter outputVideo.Size S = Size((int)
Size S = Size((int) inputVideo.get(CAP_PROP_FRAME_WIDTH), //获取输入尺寸
 (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); // 处理--只提取正确的通道
for( int i =0; i < 3; ++i)
 if (i != channel)
 spl[i] = Mat::zeros(S, spl[0].type())merge(spl, res)

将所有这些合并在一起,您将得到上部源代码,其运行结果将显示与该想法大致相同的内容:

在这里插入图片描述

您可以在 YouTube 上看到运行时的实例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值