首页
/ OpenCV 视频写入实战:cv::VideoWriter、FOURCC 与视频文件容器解析

OpenCV 视频写入实战:cv::VideoWriter、FOURCC 与视频文件容器解析

2026-09-06 15:51:09作者:吴年前Myrtle

本篇基于 OpenCV 官方教程 video_write.markdown 展开,讲解如何用 cv::VideoWriter 写出视频文件、FOURCC 编码的原理与转换方法,以及如何从输入视频中提取单个 BGR 颜色通道并写成新的 AVI 文件。读完后你将能够独立编写“读入视频→逐帧处理→写出视频”的完整管线,并理解视频容器、编码器的关系及 OpenCV 写入端的边界。

视频文件结构示意:容器包含视频流、音频流等多个轨道

1. 目标与演示程序

当你处理视频流时,常常需要把图像处理结果保存为一个新的视频文件。对于简单的视频输出,OpenCV 内置了专为该场景设计的 cv::VideoWriter 类。本教程围绕三个核心问题展开:

  • 如何用 OpenCV 创建视频文件;
  • OpenCV 能创建哪些类型的视频文件;
  • 如何从一个视频中提取指定颜色通道。

作为演示,官方示例提取输入视频 BGR 三通道中的某一个,写入新的视频文件。应用程序的行为由命令行参数控制:

参数 含义
第 1 个 要处理的视频文件路径
第 2 个 RGB 之一,指定要提取的通道
第 3 个 YN。若为 N,输出视频沿用输入视频的编码器;若为 Y,运行时弹出窗口让你从系统已安装的编码器中自行选择

一个合法的命令行示例:

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

2. 视频文件的内部结构

在写视频之前,需要先建立对视频文件结构的认识:

  • 每个视频文件本身是一个容器(container)。容器类型由文件扩展名表达,例如 avimovmkv
  • 容器内部包含多个轨道(track):视频流、音频流、字幕等其他数据;
  • 各轨道的具体存储方式由各自的编解码器(codec)决定。音频轨道常见 mp3aac;视频轨道的常见编码器包括 XVIDDIVXH264,以及无损编码器 LAGS(Lagarith Lossless Codec)。系统上可用的编码器完整列表取决于你安装了哪些编解码器。

OpenCV 的定位是计算机视觉库,而非视频流/编解码器库,因此开发者刻意将写入部分保持得尽可能简单。按教程所述,OpenCV 对视频容器仅支持 avi 扩展名及其第一版本格式。由此带来两个直接限制:

  1. 不能保存大于 2 GB 的视频文件(32 位 AVI 索引的固有上限);
  2. 容器内只能创建和扩展单一视频轨道,不支持音频或其他轨道的编辑。

尽管如此,系统中存在的任何视频编码器在 OpenCV 写入端都可能可用。遇到上述限制时,教程建议的路线是:使用更专门的视频写入库(如 FFmpeg)或无损编码器(HuffYUVCorePNGLCL);或者先用 OpenCV 创建视频轨道,再用 VirtualDubAviSynth 等工具加音轨或转封装。

补充说明:从当前仓库头文件 videoio.hpp 的文档注释看,现代 OpenCV 的写入后端已经扩展——Linux 默认使用 FFMPEG 写视频,Windows 可选 FFMPEG/MSWF/DSHOW,macOS 使用 AVFoundation;并且通过 img_%02d.jpg 这类文件名配合 fourcc=0fps=0 还可以写出图像序列。这与教程成文时期的“仅 AVI”限制属于同一主题的演进,实际能力应以你所用版本的构建配置为准。

3. cv::VideoWriter 类:五个关键参数

创建视频文件只需实例化一个 cv::VideoWriter。其属性既可以在构造函数中给出,也可以稍后通过 cv::VideoWriter::open() 给出,两者参数完全相同。当前实现见 VideoWriter 类定义,核心签名与参数如下:

VideoWriter(const String& filename, int fourcc, double fps,
            Size frameSize, bool isColor = true);
bool open(const String& filename, int fourcc, double fps,
          Size frameSize, bool isColor = true);

3.1 参数 1:输出文件名(容器类型)

文件名包含容器扩展名,示例中只支持 avi。示例代码从输入文件名派生出输出文件名:取输入文件去掉扩展名的部分,追加通道名(R/G/B),再拼上 .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";  // 构造带容器扩展名的新文件名

3.2 参数 2:编码器 FOURCC

每个视频编码器都有一个不超过 4 个字符的唯一短名,如 XVIDDIVXH264,这种形式称为 four character code(FOURCC)。它可以直接从输入视频读取,使用 VideoCapture::get(CAP_PROP_FOURCC)(该属性枚举值见 videoio.hppCAP_PROP_FOURCC = 6)。由于 get 是通用接口,返回值恒为 double

  • 双精度浮点数占 64 位,而四个字符是 4 字节、32 位;
  • 这 4 个字符编码在该 double 值的低 32 位中;
  • 最简单的“丢弃高 32 位”方式就是直接转换成 int
VideoCapture inputVideo(source);                                   // 打开输入
int ex = static_cast<int>(inputVideo.get(CAP_PROP_FOURCC));        // 取编码器类型(整型形式)

OpenCV 内部使用这种整型表示,并期望 fourcc 参数以此形式传入。从整型转回字符串有两种方法:

位运算法(按字节“与”+移位,末尾补 0 结束字符串):

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

union 法(赋值后自动完成转换,且编码器变化时无需重复运算):

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

如果事先知道编码器的四个字符,则用 CV_FOURCC 宏由字符构造整型,例如 MPEG-1 编码器:

CV_FOURCC('P','I','M','1')  // 由字符到整型,MPEG1 编码器

该宏在当前仓库中的实现见 cap_ffmpeg_impl.hpp

#define CV_FOURCC(c1, c2, c3, c4) (((c1) & 255) + (((c2) & 255) << 8) + (((c3) & 255) << 16) + (((c4) & 255) << 24))

可见它正是“每字符占 1 字节,依次左移 0/8/16/24 位”的组合方式,与 3.2 节读取方向的位运算严格互逆。当前头文件还提供等价的静态成员函数 VideoWriter::fourcc(char c1, char c2, char c3, char c4)定义位置)。

特殊值:若该参数传 -1,运行时将弹出一个包含系统全部已安装编码器的窗口,供你交互式选择:

fourcc 传 -1 时弹出的编码器选择对话框

3.3 参数 3 与 4:帧率与帧尺寸

  • fps:输出视频的每秒帧数。示例沿用输入视频的帧率,通过 get(CAP_PROP_FPS) 获取;
  • frameSize:输出视频的帧尺寸。示例同样沿用输入视频,通过 get(CAP_PROP_FRAME_WIDTH)get(CAP_PROP_FRAME_HEIGHT) 获取。

3.4 参数 5:isColor

最后一个参数是可选的,默认为 true,表示输出为彩色视频(写入时送入三通道图像);传入 false 则输出灰度视频。头文件注释还提醒:颜色图像一般期望为 BGR 顺序。

示例中完整的调用方式:

VideoWriter outputVideo;
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);

3.5 打开检查与逐帧写入

  • cv::VideoWriter::isOpened() 判断 open 是否成功;
  • 视频文件会在 VideoWriter 对象析构时自动关闭(析构函数内部先调用 release(),见 头文件注释);
  • 打开成功后即可用 write() 按顺序写入帧,或使用等价的流运算符 operator <<
outputVideo.write(res);  // 或
outputVideo << res;

注意帧的尺寸必须与 open 时指定的尺寸一致,否则底层后端会拒绝写入。从当前实现注释看(write 接口),write() 返回 true 表示底层后端成功写入该帧,false 表示编码器失败等错误,实际写循环中建议检查该返回值。

4. 从 BGR 图像中提取单个颜色通道

“提取一个颜色通道”的语义是:保留目标通道,把其余两个 BGR 通道全部置零。可以用逐像素扫描实现,也可以用 split + merge 操作完成:先把通道拆分为独立图像,把非目标通道置为同尺寸同类型的零矩阵,再合并回去:

split(src, spl);                 // 处理 - 只提取正确的通道
for (int i = 0; i < 3; ++i)
    if (i != channel)
        spl[i] = Mat::zeros(S, spl[0].type());
merge(spl, res);

其中通道号与字符的映射关系为:'B' → 0'G' → 1'R' → 2(OpenCV 的 BGR 通道顺序中 B 为第 0 通道)。

5. 完整示例源码走读

完整示例位于 video-write.cpp,与上文各片段一一对应:

  1. 参数校验L26-L30):要求恰好 3 个参数,否则打印帮助并退出;
  2. 打开输入视频L35-L40):VideoCapture inputVideo(source),并检查 isOpened()
  3. 构造输出文件名与读取 FOURCCL42-L47):如 3.1、3.2 节所述,位运算法转字符数组 EXT 用于打印输入编码器名称;
  4. 打开输出视频L52-L62):当第 3 个参数为 Y 时以 fourcc = -1 打开(弹出编码器选择框),否则复用输入视频的编码器;打开后再次检查 isOpened()
  5. 打印输入信息L64-L66):分辨率、帧数(CAP_PROP_FRAME_COUNT)、输入编码器类型;
  6. 主写入循环L78-L91):inputVideo >> src 逐帧读取,src.empty() 时结束;每帧执行 split → 置零非目标通道 → mergeoutputVideo << res
  7. 收尾:循环结束后打印 "Finished writing",VideoWriter 对象在函数返回析构时自动关闭文件。

运行结果:提取单一颜色通道后写入的新视频画面

6. 实践要点与常见坑

结合教程与当前仓库头文件,整理以下可直接落地的注意事项:

事项 说明
大小上限 教程指出 AVI 容器下不能保存超过 2 GB 的视频;需要更大文件时应转向 FFmpeg 等方案
轨道限制 容器内仅支持单一视频轨道,无音频;需要音轨时后续用专门工具合成
fourcc = -1 对部分后端会弹出系统编码器选择对话框(头文件提示
图像序列输出 使用 img_%02d.jpg 这类文件名并配合 fourcc=0fps=0 可写出图像序列;用未压缩格式(如 BMP)可保存原始帧
无损输出 多数编码器有损;需要无损时使用无损编码器(如 FFV1、Huffman HFYU、Lagarith LAGS 等)
奇数尺寸 启用 FFMPEG 时允许奇数宽或高,但会截断最右列/最底行(部分底层 swscale 函数要求偶数宽高)
后端选择 头文件提供了带 apiPreference 参数的重载(如 cv::CAP_FFMPEGcv::CAP_GSTREAMER),可在多个后端可用时强制指定实现
灰度输出 isColor=false 时编码器按灰度帧工作

7. 小结

本教程以“提取输入视频的单个 BGR 通道并写成新视频”为主线,串起了 OpenCV 视频写入的完整知识链:视频容器与编码器的关系、cv::VideoWriter 五个参数的确切含义、FOURCC 在 double/int/char 三种表示间的互转,以及 split/merge 完成通道提取的标准做法。配套的完整源码可直接参考 samples/cpp/tutorial_code/videoio/video-write/video-write.cpp,API 细节以 modules/videoio/include/opencv2/videoio.hpp 中的 VideoWriter 类文档为准。

登录后查看全文
热门项目推荐
相关项目推荐