【问题标题】:How to output x265 compressed video with cv2.VideoWriter如何使用 cv2.VideoWriter 输出 x265 压缩视频
【发布时间】:2020-07-30 06:42:16
【问题描述】:

我正在对一个 45 分钟的 1.2GB 视频进行一些渲染,每个 80,0000 个大小为 1344x756 的帧,视频为 mp4 格式,我正在尝试输出具有 x265 压缩的视频问题是当我我使用 cv2.VideoWriter 10 分钟视频的输出大小超过 2GB,这不是我想要的结果,所以我在 mac osx 和 ubuntu 18 上尝试了以下操作:

codec = cv2.VideoWriter_fourcc(*'HEVC')
out = cv2.VideoWriter('output.mp4', 'HEVC', fps, (width, height))

我得到的只是运行时警告:

OpenCV: FFMPEG: tag 0x43564548/'HEVC' is not found (format 'mp4 / MP4 (MPEG-4 Part 14)')'

我想要实现的不一定是最高质量的输出,而应该是高质量和尽可能小的尺寸。

【问题讨论】:

标签: python-3.x opencv rendering codec


【解决方案1】:

据我所知,OpenCV VideoWriter 还不支持 HEVC 编码。

我建议您使用FFmpeg 作为子进程,并将渲染的帧通过管道传输到ffmpegstdin 输入流。

您可以对ffmpeg 使用Python 绑定,例如ffmpeg-python,或使用Python subprocess 执行ffmpeg

使用ffmpeg,与cv2.VideoWriter 相比,您可以更好地控制视频编码参数(cv2.VideoWriter 的设计目的是为了简化和扩展灵活性)。

这是一个示例代码,它呈现 50 帧,将帧流式传输到 ffmpeg,它使用 HEVC 视频编解码器对 MP4 视频文件进行编码:

import cv2
import numpy as np
import subprocess as sp
import shlex

width, height, n_frames, fps = 1344, 756, 50, 25  # 50 frames, resolution 1344x756, and 25 fps

output_filename = 'output.mp4'

# Open ffmpeg application as sub-process
# FFmpeg input PIPE: RAW images in BGR color format
# FFmpeg output MP4 file encoded with HEVC codec.
# Arguments list:
# -y                   Overwrite output file without asking
# -s {width}x{height}  Input resolution width x height (1344x756)
# -pixel_format bgr24  Input frame color format is BGR with 8 bits per color component
# -f rawvideo          Input format: raw video
# -r {fps}             Frame rate: fps (25fps)
# -i pipe:             ffmpeg input is a PIPE
# -vcodec libx265      Video codec: H.265 (HEVC)
# -pix_fmt yuv420p     Output video color space YUV420 (saving space compared to YUV444)
# -crf 24              Constant quality encoding (lower value for higher quality and larger output file).
# {output_filename}    Output file name: output_filename (output.mp4)
process = sp.Popen(shlex.split(f'ffmpeg -y -s {width}x{height} -pixel_format bgr24 -f rawvideo -r {fps} -i pipe: -vcodec libx265 -pix_fmt yuv420p -crf 24 {output_filename}'), stdin=sp.PIPE)

# Build synthetic video frames and write them to ffmpeg input stream.
for i in range(n_frames):
    # Build synthetic image for testing ("render" a video frame).
    img = np.full((height, width, 3), 60, np.uint8)
    cv2.putText(img, str(i+1), (width//2-100*len(str(i+1)), height//2+100), cv2.FONT_HERSHEY_DUPLEX, 10, (255, 30, 30), 20)  # Blue number

    # Write raw video frame to input stream of ffmpeg sub-process.
    process.stdin.write(img.tobytes())

# Close and flush stdin
process.stdin.close()

# Wait for sub-process to finish
process.wait()

# Terminate the sub-process
process.terminate()

注意事项:

  • ffmpeg 可执行文件必须在 Python 脚本的执行路径中。

  • 对于Linux,如果ffmpeg不在执行路径中,您可以使用完整路径:

     process = sp.Popen(shlex.split(f'/usr/bin/ffmpeg -y -s {width}x{height} -pixel_format bgr24 -f rawvideo -r {fps} -i pipe: -vcodec libx265 -pix_fmt yuv420p -crf 24 {output_filename}'), stdin=sp.PIPE)
    

    (假设ffmpeg 可执行文件在/usr/bin/ 中)。

  • Python 3's f-Strings 语法需要 Python 3.6 或更高版本。

【讨论】:

  • 很棒的代码!对于得到“找不到文件:ffmpeg ...”的任何人,请尝试在命令字符串周围使用import shlexshlex.split(...),这将在空格处分割字符串,同时保留字符串的引用部分,如文件路径,同时写入@987654342 @ 而不是 ffmpeg 有助于解决执行路径问题。
  • 谢谢你,我更新了 Linux 支持的帖子。除了shlex.split(...),你也可以使用'...'.split()
  • .split() 将破坏包含空格的文件路径,如果它们像这样引用'... -crf 24 "{output_filename}"',这就是为什么我更喜欢shlex 解决方案,但在很多情况下你是对的.split()就足够了。
  • 非常感谢!我想我会默认使用你的方法来写视频!
  • 您有机会获得X265 encodes 0 frames。这很可能是因为 ffmpeg 启动的线程没有安全关闭。它有助于添加time.sleep(2) post video write out。
猜你喜欢
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-27
  • 1970-01-01
  • 2020-10-10
  • 2020-05-19
  • 2014-05-28
  • 1970-01-01
相关资源
最近更新 更多