据我所知,OpenCV
VideoWriter
目前尚不支持HEVC编码。
我建议您使用
FFmpeg作为子进程,并将渲染的帧管道传输到
ffmpeg
的
stdin
输入流中。
您可以使用像
ffmpeg-python这样的Python绑定,或者使用Python
subprocess来执行
ffmpeg
。
与
cv2.VideoWriter
相比,使用
ffmpeg
可以更好地控制视频编码参数(
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
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)
for i in range(n_frames):
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)
process.stdin.write(img.tobytes())
process.stdin.close()
process.wait()
process.terminate()
注:
ffmpeg
executable must be in the execution path of the Python script.
For Linux, in case ffmpeg
is not in the execution path, you may use the full path:
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)
(Assuming ffmpeg
executable is in /usr/bin/
).
Python 3's f-Strings syntax requires Python version 3.6 or above.
C++示例:
在Python中,有多个FFmpeg绑定可以实现H.265视频编码。
在C++中,可选项要少得多...
我们可以使用类似的解决方案来使用C++(使用FFmpeg子进程)。
为了执行FFmpeg子进程并打开stdin
管道,我们可以在Windows中使用_popen,在Linux中使用popen。
注意:
- 我注意到
_popen
不如CreateProcess
可靠,并且我们需要等待(例如在结尾处等待一秒钟)输出文件关闭。
我不确定在Linux中是否存在类似的问题与popen
。
C++代码示例:
#include <stdio.h>
#include <chrono>
#include <thread>
#include "opencv2/opencv.hpp"
#include <string>
int main()
{
int width = 1344;
int height = 756;
int n_frames = 50;
int fps = 25;
const std::string output_filename = "output.mp4";
std::string ffmpeg_cmd = std::string("ffmpeg -y -f rawvideo -r ") + std::to_string(fps) +
" -video_size " + std::to_string(width) + "x" + std::to_string(height) +
" -pixel_format bgr24 -i pipe: -vcodec libx265 -crf 24 -pix_fmt yuv420p " + output_filename;
#ifdef _MSC_VER
FILE* pipeout = _popen(ffmpeg_cmd.c_str(), "wb");
#else
FILE* pipeout = popen(ffmpeg_cmd.c_str(), "w");
#endif
for (int i = 0; i < n_frames; i++)
{
cv::Mat frame = cv::Mat(height, width, CV_8UC3);
frame = cv::Scalar(60, 60, 60);
cv::putText(frame, std::to_string(i+1), cv::Point(width/2 - 100*(int)(std::to_string(i+1).length()), height/2+100), cv::FONT_HERSHEY_DUPLEX, 10, cv::Scalar(255, 30, 30), 20);
fwrite(frame.data, 1, (size_t)width*height*3, pipeout);
}
fflush(pipeout);
#ifdef _MSC_VER
_pclose(pipeout);
#else
pclose(pipeout);
#endif
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
return 0;
}