
OpenCV VideoWriter 详解
opencv 提供了 cv2.videowriter 类,用于将一系列图像帧写入视频文件。它是图像处理和计算机视觉应用中创建视频输出的关键工具。正确理解和配置其参数对于生成有效的视频文件至关重要。
cv2.VideoWriter 的构造函数通常接收以下参数:
- filename: 输出视频文件的路径和名称(例如 "output.mp4")。
- fourcc: 视频编解码器。这是一个四字符代码(FourCC),用于指定视频流的压缩格式。例如,cv2.VideoWriter_fourcc(*'XVID') 或 cv2.VideoWriter_fourcc(*'mp4v')。不同的操作系统和 OpenCV 版本可能支持不同的编解码器,并且编解码器与文件容器(如 .mp4, .avi, .mkv)之间存在兼容性要求。
- fps: 视频的帧率(每秒帧数)。
- frameSize: 视频帧的尺寸,通常是一个 (width, height) 的元组或列表。这是本文要重点讨论的关键参数。
- isColor: 可选参数,布尔值,表示视频是否为彩色。默认为 True。如果为 False,则表示灰度视频。
创建 VideoWriter 对象后,可以使用 write() 方法逐帧写入图像,并在所有帧写入完毕后调用 release() 方法释放资源并完成文件写入。
核心问题:frameSize 参数的陷阱
在实际应用中,许多开发者在使用 cv2.VideoWriter 时会遇到一个令人困惑的问题:代码运行没有报错,也生成了视频文件,但文件大小极小(通常只有几百字节),且无法正常播放。通过媒体信息工具检查,文件结构似乎不完整,例如 VLC 播放器可能会提示“无法找到任何集群或章节”。
这个问题的根本原因在于 frameSize 参数的误用。当从图像(如 NumPy 数组)获取尺寸时,我们通常习惯于 img.shape 返回 (height, width, channels) 的顺序。然而,cv2.VideoWriter 的 frameSize 参数却明确要求 (width, height) 的顺序。
错误示例:
假设你有一张尺寸为 120 像素高 x 160 像素宽的图片。当你读取这张图片并检查其 shape 时,你会得到 (120, 160, 3) (对于彩色图片)。如果直接将 (120, 160) 作为 frameSize 传递给 VideoWriter,即:
import cv2
# 假设图片尺寸是 160宽 x 120高
# 错误地将 (height, width) 传递给 frameSize
fourcc = cv2.VideoWriter.fourcc(*'x264')
# 错误的 frameSize: [120, 160] (实际是 height, width)
writer = cv2.VideoWriter("py_test_error.mkv", fourcc, 60.0, [120, 160])
def record_error(writer_obj):
for i in range(121):
img = cv2.imread(f"capture.{i}.jpg") # 假设图片是 160x120
if img is None:
print(f"Error: Could not read image capture.{i}.jpg")
continue
writer_obj.write(img)
record_error(writer)
writer.release() # 释放资源
print("视频文件生成完毕,但可能为空。")在这种情况下,VideoWriter 期望的帧尺寸是 120 像素宽 x 160 像素高,而你实际写入的图像是 160 像素宽 x 120 像素高。尺寸不匹配导致 VideoWriter 无法正确处理帧数据,从而生成一个空的或损坏的视频文件。
完整解决方案与示例代码
解决这个问题的关键在于确保 frameSize 参数的值严格遵循 (width, height) 的顺序,并且与你实际写入的图像帧的尺寸相匹配。
正确示例:
如果你的图片是 160 像素宽 x 120 像素高,那么 frameSize 应该设置为 [160, 120]。
import cv2
import os
# 假设当前目录下有 capture.0.jpg 到 capture.120.jpg 共121张图片
# 这些图片尺寸均为 160宽 x 120高
def record_frames(writer_obj):
for i in range(121):
img_path = f"capture.{i}.jpg"
img = cv2.imread(img_path)
if img is None:
print(f"警告: 无法读取图像 {img_path},跳过此帧。")
continue
# 确保写入的图像尺寸与 VideoWriter 期望的 frameSize 一致
writer_obj.write(img)
print("所有图像帧已写入。")
# 1. 定义输出文件路径和编解码器
output_filename = "py_test_correct.mkv"
# 推荐使用 'mp4v' 或 'XVID' 对于 .mp4/.avi 容器,
# 'x264' 对于 .mkv 容器通常效果更好,但需要系统支持。
fourcc = cv2.VideoWriter.fourcc(*'x264')
# 2. 定义帧率
fps = 60.0
# 3. 定义正确的帧尺寸:(宽度, 高度)
# 假设图片是 160宽 x 120高
frame_width = 160
frame_height = 120
frame_size = (frame_width, frame_height)
# 4. 创建 VideoWriter 对象
try:
writer = cv2.VideoWriter(output_filename, fourcc, fps, frame_size)
if not writer.isOpened():
print(f"错误: 无法打开视频写入器。请检查编解码器和文件路径。")
else:
print(f"成功创建 VideoWriter 对象,准备写入 {output_filename}")
# 5. 写入所有帧
record_frames(writer)
print(f"视频文件 '{output_filename}' 生成完毕。")
except Exception as e:
print(f"发生异常: {e}")
finally:
# 6. 释放 VideoWriter 资源,确保文件被正确写入和关闭
if 'writer' in locals() and writer.isOpened():
writer.release()
print("VideoWriter 资源已释放。")在上述代码中,frame_size = (frame_width, frame_height) 确保了 VideoWriter 接收到的是正确的宽度和高度顺序,从而解决了生成空文件的问题。
注意事项与最佳实践
- writer.release() 的重要性: 无论视频写入成功与否,务必在所有帧写入完毕后调用 writer.release()。这个方法会关闭文件句柄,刷新缓冲区,并完成视频文件的最终写入。如果忘记调用,即使代码没有报错,生成的视频文件也可能损坏或无法播放。将其放在 finally 块中是良好的编程习惯,确保即使发生异常也能被调用。
-
fourcc 编解码器与容器的兼容性:
- 不同的编解码器 (fourcc) 与文件容器(.mp4, .avi, .mkv 等)之间存在兼容性。例如,'mp4v' 和 'XVID' 通常用于 .mp4 或 .avi 文件,而 'x264' 更常用于 .mkv 或 .mp4 (如果系统支持 H.264 编码)。
- 如果 fourcc 不受系统支持或与容器不兼容,writer.isOpened() 可能会返回 False,或者生成的文件无法播放。在 Linux 系统上,确保安装了 ffmpeg 或 gstreamer 等后端库,因为 OpenCV 依赖它们进行视频编解码。
- 尝试不同的 fourcc 代码和文件扩展名组合,直到找到一个适合你系统和需求的。常见的 FourCCs 包括:
- `*'mp










