I'm doing some rendering on a 45-min 1.2GB video 80,0000 frames of size 1344x756 each and the video is in the mp4 format, I'm trying to output a video with x265 compression the problem is when I'm using cv2.VideoWriter the output size for 10 minutes of the video exceeds 2GB which is not what I'm intending to end up with so I tried the following on mac osx and ubuntu 18:
codec = cv2.VideoWriter_fourcc(*'HEVC')
out = cv2.VideoWriter('output.mp4', 'HEVC', fps, (width, height))
All I'm getting is runtime warnings:
OpenCV: FFMPEG: tag 0x43564548/'HEVC' is not found (format 'mp4 / MP4 (MPEG-4 Part 14)')'
What I'm trying to achieve is not necessarily the highest quality output but should be a good quality and the lowest size possible.
As far as I know, OpenCV VideoWriter
has no support for HEVC encoding (yet).
I recommend you using FFmpeg as sub-process, and PIPE the rendered frames to stdin
input stream of ffmpeg
.
You may use Python binding for ffmpeg
like ffmpeg-python, or execute ffmpeg
using Python subprocess.
Using ffmpeg
, you have much more control over video encoding parameters compared to cv2.VideoWriter
(cv2.VideoWriter
is designed for simplicity on expanse of flexibility).
Here is a sample code that renders 50 frames, stream frames to ffmpeg
that encodes MP4 video file with HEVC video codec:
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()
Notes:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With