python-opencv&ffmpegcv合成视频对比

Xial 发布于 2023-08-18 15 次阅读


python-opencv

100 帧纯黑

codec time (s) size (KB)
XVID 0.62 309.97
MJPG 1.15 1,260.75
mp4v 0.61 288.99
DIVX 0.63 309.97
h264 0.61 34.74
WMV1 0.61 222.48
WMV2 0.66 581.86
VP80 3.07 70.25
VP90 3.52 27.29
AVC1 0.62 34.74
FLV1 0.59 567.51
I420 0.36 300,823.94
WMV3 2.19 25.32
X264 0.60 34.79
YUYV 0.37 300,823.94
IYUV 0.36 300,823.94
YV12 0.37 300,823.94
YVYU 0.37 300,823.94
UYVY 0.37 300,823.94
M4S2 0.64 309.97
FMP4 0.62 309.97
MPEG 1.17 368.08

100 帧纯随机

codec time (s) size (KB)
XVID 2.28 58,673.16
MJPG 4.68 149,974.70
mp4v 2.35 58,648.45
DIVX 2.29 58,673.16
h264 3.76 50,504.66
WMV1 3.10 59,066.23
WMV2 3.60 58,994.36
VP80 129.31 209,960.44
VP90 266.21 40,090.48
AVC1 3.83 50,504.66
FLV1 2.71 58,419.96
I420 0.38 303,861.46
WMV3 8.54 39,181.57
X264 3.90 50,504.71
YUYV 0.40 303,861.46
IYUV 0.40 303,861.46
YV12 0.39 303,861.46
YVYU 0.39 303,861.46
UYVY 0.42 303,861.46
M4S2 2.38 58,673.16
FMP4 2.34 58,673.16
MPEG 3.69 57,666.62

ffmpegcv

手动设置 bitrate 为 400M,不手动设置的话 gpu 编码出来的会很糊

截止本文完成时(2023.8.18),ffmpegcv 的 VideoWriter 暂不支持手动设置 bitrate(给作者提了 PR)

100 帧纯黑

codec time (s) size (KB)
h264 0.65 9.85
hevc 1.23 15.84
h264_nvenc 0.73 9.85
hevc_nvenc 0.70 15.84

100 帧纯随机

codec time (s) size (KB)
h264 3.03 78,500.08
hevc 16.29 47,981.26
h264_nvenc 1.14 78,500.08
hevc_nvenc 1.22 47,981.26

VPF (VideoProcessingFramework)

配置如下:

key value
preset P5
codec hevc
tuning_info high_quality
fps 60
s 1920x1080
bitrate 400M

100 帧纯黑

codec time (s) size (KB)
h264 0.58 6.83
hevc 0.43 6.87

100 帧纯随机

codec time (s) size (KB)
h264 1.20 176,795.80
hevc 0.97 173,873.63

附录

测试代码

import ffmpegcv

import time
import cv2
import os
import numpy as np

res_opencv = {
    ("XVID", ".avi"): None,
    ("MJPG", ".avi"): None,
    ("mp4v", ".mp4"): None,
    ("DIVX", ".avi"): None,
    ("h264", ".mp4"): None,
    ("WMV1", ".wmv"): None,
    ("WMV2", ".wmv"): None,
    ("VP80", ".avi"): None,
    ("VP90", ".avi"): None,
    ("AVC1", ".mp4"): None,
    ("FLV1", ".flv"): None,
    ("I420", ".avi"): None,
    ("WMV3", ".wmv"): None,
    ("X264", ".mkv"): None,
    ("YUYV", ".avi"): None,
    ("IYUV", ".avi"): None,
    ("YV12", ".avi"): None,
    ("YVYU", ".avi"): None,
    ("UYVY", ".avi"): None,
    ("M4S2", ".avi"): None,
    ("FMP4", ".avi"): None,
    ("MPEG", ".avi"): None,
}

res_ffmpegcv: dict[tuple[str, str], tuple | None] = {
    ("h264", ".mp4"): None,
    ("hevc", ".mp4"): None,
    ("h264_nvenc", ".mp4"): None,
    ("hevc_nvenc", ".mp4"): None,
}

def test_opencv():
    img = np.zeros((1080, 1920, 3), dtype=np.uint8)
    imgs = [np.random.randint(0, 255, (1080, 1920, 3), dtype=np.uint8) for _ in range(100)]

    for codec, suffix in res_opencv.keys():
        fourcc = cv2.VideoWriter_fourcc(*codec)
        out = cv2.VideoWriter(f'./test/test_{codec}{suffix}', fourcc, 60, (1920, 1080))

        start = time.time()
        for i in range(1, 100):
            out.write(img)
        out.release()
        end = time.time()

        # start = time.time()
        # for img in imgs:
        #     out.write(img)
        # out.release()
        # end = time.time()

        res_opencv[(codec, suffix)] = (end - start, os.stat(f'./test/test_{codec}{suffix}').st_size)
        print(f"{codec}: {end - start}")

    with open("./test/test.md", "w") as f:
        f.write("|codec|time (s)|size (KB)|\n")
        f.write("|---|---|---|\n")
        for codec, suffix in res_opencv.keys():
            f.write(f"|{codec}|{res_opencv[(codec, suffix)][0]:.2f}|{res_opencv[(codec, suffix)][1] / 1024:,.2f}|\n")

def test_ffmpegcv():
    img = np.zeros((1080, 1920, 3), dtype=np.uint8)
    imgs = [np.random.randint(0, 255, (1080, 1920, 3), dtype=np.uint8) for _ in range(100)]

    for codec, suffix in res_ffmpegcv.keys():
        out = ffmpegcv.VideoWriter(f'./test/test_{codec}{suffix}', codec, 60, bitrate="400M")

        # start = time.time()
        # for i in range(1, 100):
        #     out.write(img)
        # out.release()
        # end = time.time()

        start = time.time()
        for img in imgs:
            out.write(img)
        out.release()
        end = time.time()

        res_ffmpegcv[(codec, suffix)] = (end - start, os.stat(f'./test/test_{codec}{suffix}').st_size)
        print(f"{codec}: {end - start}")

    with open("./test/test.md", "w") as f:
        f.write("|codec|time (s)|size (KB)|\n")
        f.write("|---|---|---|\n")
        for codec, suffix in res_ffmpegcv.keys():
            f.write(
                f"|{codec}|{res_ffmpegcv[(codec, suffix)][0]:.2f}|{res_ffmpegcv[(codec, suffix)][1] / 1024:,.2f}|\n")

def test_VPF():
    total_num_frames = 100
    uploader = PyNvCodec.PyFrameUploader(1920, 1080, PyNvCodec.PixelFormat.NV12, 0)

    img = np.zeros((1080, 1920, 3), dtype=np.uint8)
    img = bgr2nv12(img)
    img_converted = uploader.UploadSingleFrame(img)

    imgs = [np.random.randint(0, 255, (1080, 1920, 3), dtype=np.uint8) for _ in range(total_num_frames)]
    start = time.time()
    for i in range(len(imgs)):
        img = bgr2nv12(imgs[i])
        uploader.UploadSingleFrame(img)
    end = time.time()
    print("Convert time: ", end - start, " seconds")

    out = PyNvCodec.PyNvEncoder({
        "preset": "P5",
        "codec": "hevc",
        "tuning_info": "high_quality",
        "fps": "60",
        "s": "1920x1080",
        "bitrate": "400M",
    }, gpu_id=0)

    encFile = open("./test/test.mp4", "wb")
    encFrame = np.ndarray(shape=(0,), dtype=np.uint8)
    framesReceived = 0

    start = time.time()
    # for _ in range(total_num_frames):
    #     success = out.EncodeSingleSurface(img_converted, encFrame, sync=False)
    for img in imgs:
        success = out.EncodeSingleSurface(uploader.UploadSingleFrame(img), encFrame, sync=False)
        if success:
            encByteArray = bytearray(encFrame)
            encFile.write(encByteArray)
            framesReceived += 1
    while framesReceived < total_num_frames:
        success = out.FlushSinglePacket(encFrame)
        if success:
            encByteArray = bytearray(encFrame)
            encFile.write(encByteArray)
            framesReceived += 1
    end = time.time()
    encFile.close()

    print("Frames received: ", framesReceived)
    print("Total time: ", end - start, " seconds")

if __name__ == "__main__":
    test_opencv()
    test_ffmpegcv()
最后更新于 2023-08-19