借助官方离线文档中的samples来理解VideoWriter
文档位置:samples/cpp/tutorial_code/videoio/video-write/video-write.cpp
注:
需要提前下载 openh264-1.8.0-win64.dll,然后放在Release文件夹下 ,否则无法正确对输出文件进行编码从而运行失败
1 #include <iostream>
2 #include <string>
4 #include <opencv2/core.hpp>
5 #include <opencv2/videoio.hpp>
7 using namespace std;
8 using namespace cv;
10 void help() {
11 cout
12 << "------------------------------------------------------------------------------" << endl
13 << "This program shows how to write video files." << endl
14 << "You can extract the R or G or B color channel of the input video." << endl
15 << "Usage:" << endl
16 << "./video-write <input_video_name> [ R | G | B] [Y | N]" << endl
17 << "------------------------------------------------------------------------------" << endl
18 << endl;
19 }
21 int main(int argc, char* argv[]) {
22 help();
24 // 如果命令行接收的参数长度不等于4则异常退出。
25 if (argc != 4) {
26 cout << "Not enough parameters" << endl;
27 return -1;
28 }
30 // 输入的视频文件,如:CV.exe AVI.avi R G , source = "AVI.avi"
31 const string source = argv[1];
32 // 判断用户是否询问输出类型,如果是N,将使用输入视频的编码器类型
33 const bool askOutputType = argv[3][0] == 'Y';
35 VideoCapture inputVideo(source);
36 if (!inputVideo.isOpened()) {
37 cout << "Could not open the input video: " << source << endl;
38 return -1;
39 }
41 // 查找最后一个扩展点,返回下标索引位置,如:CV.exe AVI.avi R G ,pAt = 3
42 string::size_type pAt = source.find_last_of('.');
44 // 定义输出文件名,NAME = "AVIR.avi"
45 const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi";
47 // 获取视频流中的第一帧的编码类型,并转换为int类型(视频流中,每一帧的编码格式可能都不一样)
48 // 例:
49 // 转换前 -- 8.75967e+08
50 // 转换后 -- 875967080
51 unsigned int ex = static_cast<int>(inputVideo.get(CAP_PROP_FOURCC));
53 // 通过按位运算符从int转换为char
54 // 转换前 -- 875967080 对应的二进制:00110100 00110110 00110010 01101000
55 // 转换后 -- h264
56 char EXT[] = {
57 (char)(ex & 0XFF),
58 (char)((ex & 0XFF00) >> 8),
59 (char)((ex & 0XFF0000) >> 16),
60 (char)((ex & 0XFF000000) >> 24),
61 0
62 };
64 // 获取输入视频的尺寸
65 Size S(
66 (int)inputVideo.get(CAP_PROP_FRAME_WIDTH),
67 (int)inputVideo.get(CAP_PROP_FRAME_HEIGHT)
68 );
70 // 创建一个写入对象,并指定相关参数(输出视频文件名、编码格式、输入视频帧率、输出视频尺寸、是否为彩色或者灰色)
71 VideoWriter outputVideo;
72 if (askOutputType)
73 outputVideo.open(NAME, ex = -1, inputVideo.get(CAP_PROP_FPS), S, true); // 由CV自动选择帧的编码格式
74 else
75 outputVideo.open(NAME, ex, inputVideo.get(CAP_PROP_FPS), S, true); // 和输入视频中的每一帧编码格式保持一致
77 // 如果打开失败则输出错误并异常退出
78 if (!outputVideo.isOpened()) {
79 cout << "Could not open the output video for write: " << source << endl;
80 return -1;
81 }
83 // 输出视频尺寸、总帧数和输入视频的编码格式
84 cout << "Input frame resolution: Width = " << S.width << " Height = " << S.height
85 << "of nr#: " << inputVideo.get(CAP_PROP_FRAME_COUNT) << endl;
86 cout << "Input codec type: " << EXT << endl;
88 // 选择颜色通道
89 int channel = 2;
90 switch (argv[2][0]) {
91 case 'R':channel = 2; break;
92 case 'G':channel = 1; break;
93 case 'B':channel = 0; break;
94 }
96 Mat src, res;
97 vector<Mat> spl;
99 // 循环写入视频
100 for (;;) {
101 inputVideo >> src;
102 if (src.empty()) break;
104 // 分离通道
105 split(src, spl);
107 // 将不符合选择的颜色通道都置零处理
108 for (int i = 0; i < 3; ++i)
109 if (i != channel)
110 spl[i] = Mat::zeros(S, spl[0].type());
112 // 合并通道
113 merge(spl, res);
115 outputVideo << res;
116 }
118 cout << "Finished writing" << endl;
119 return 0;
120 }
运行【CV.exe Video.avi R N】结果:
运行【CV.exe Video.avi R Y】结果:对于给出的报错说文件名有误实在没有头绪,就懒得继续深究了。
上述的注释基本解释了每行代码的作用,现在总结下运行逻辑:
上述代码功能——通过选择不同的颜色通道,将输入视频另存为选择好的单一颜色通道的视频文件。
1、判断命令行启动参数个数是否正确;
2、获取输入视频文件的首帧编码格式、视频文件尺寸;
3、通过输入参数中的【R | G | B】的选择定义输出视频文件名;
4、创建输出类对象,并通过输入参数中的最后一个来确定编码类型;
5、将输入文件根据选择好的颜色通道进行分离置零再合并,然后循环写入输出文件;
注:
split对帧进行分离后,得到的三通道顺序是BGR,这就是为什么 ‘R’ 对应的是 channel[2]。