You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用FFmpeg/CV2将NumPy帧转MP4时多数编码格式失效问题排查

NumPy图像帧转MP4视频损坏问题

问题描述

我有4个np.uint8格式的NumPy图像帧,需转换为MP4文件。使用mpv4编码可正常转换,但压缩率过高导致画质严重损失。尝试h264、h265、av1、vp9等多种编码格式及.mkv、.avi容器,采用cv2.VideoWriter、skvideo.io.FFmpegWriter、ffmpeg-python三种工具转换,生成的视频均出现完全或部分损坏(约末尾1/4帧异常)。用VLC查看无报错,Windows自带播放器有时可正常读取。

所用代码

skvideo实现代码

def to_lossless_mp4(img_list: np.array, target_path):
    height, width, _ = img_list[0].shape
    writer = skvideo.io.FFmpegWriter(target_path,
                                     inputdict={
                                         '-framerate': str(FPS),
                                         '-f': 'rawvideo',
                                         '-s': '{}x{}'.format(width, height)
                                     },
                                     outputdict={
                                         '-vcodec': 'libx265',  # h.264 codec
                                         '-crf': '0',  
                                         '-preset': 'veryslow',  # 速度越慢压缩效果越好
                                         '-framerate': str(FPS),
                                     })
    for i, image in enumerate(img_list):
        # 部分图像为灰度图,需转为三通道保证帧尺寸一致
        if len(image.shape) < 3:
            image = np.stack([image] * 3, axis=2)

        image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
        writer.writeFrame(image)
    writer.close()

OpenCV实现代码

def to_mp4(img_list: np.array, target_path):
    height, width, _ = img_list[0].shape
    if OVERRIDE or not os.path.exists(target_path):
        out = cv2.VideoWriter(target_path, cv2.VideoWriter_fourcc(*'x265'), FPS, (width, height))

        for image in img_list:
            if len(image.shape) < 3:
                image = np.stack([image] * 3, axis=2)
            image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
            out.write(image)
        out.release()

ffmpeg-python实现代码

def to_mp4(fn, images, framerate=1, vcodec='libx264'):
    if not isinstance(images, np.ndarray):
        images = np.asarray(images)
    n, height, width, channels = images.shape
    process = (
        ffmpeg
        .input('pipe:', format='rawvideo', pix_fmt='rgb24', s='{}x{}'.format(width, height))
        .output(fn, pix_fmt='yuv420p', vcodec=vcodec, r=framerate)
        .run_async(pipe_stdin=True)
        .overwrite_output()
        )
    for frame in images:
        if len(frame.shape) < 3:
            frame = np.stack([frame] * 3, axis=2)
        process.write(
            frame
            .astype(np.uint8)
            .tobytes()
        )
    process.stdin.close()
    process.wait()

FFmpeg输出日志(中文翻译)

[00:00<?, ?it/s]ffmpeg版本 2.7 版权所有 (c) 2000-2015 FFmpeg开发者
  编译时使用gcc 4.9.2 (GCC)
  配置参数: --enable-gpl --enable-version3 --disable-w32threads --enable-avisynth --enable-bzlib --enable-fontconfig --enable-frei0r --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libdcadec --enable-libfreetype --enable-libgme --enable-libgsm --enable-libilbc --enable-libmodplug --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libschroedinger --enable-libsoxr --enable-libspeex --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs --enable-libxvid --enable-lzma --enable-decklink --enable-zlib
  libavutil      54. 27.100 / 54. 27.100
  libavcodec     56. 41.100 / 56. 41.100
  libavformat    56. 36.100 / 56. 36.100
  libavdevice    56.  4.100 / 56.  4.100
  libavfilter     5. 16.101 /  5. 16.101
  libswscale      3.  1.101 /  3.  1.101
  libswresample   1.  2.100 /  1.  2.100
  libpostproc    53.  3.100 / 53.  3.100
[rawvideo @ 00000000051767c0] 流 #0: 帧数不足无法估算码率;考虑增大probesize参数
输入 #0, rawvideo, 来自 'pipe:':
  时长: N/A, 起始时间: 0.000000, 码率: 75497 kb/s
    流 #0:0: 视频: rawvideo (BGR[24] / 0x18524742), bgr24, 2048x1536, 75497 kb/s, 1 tbr, 1 tbn, 1 tbc
x265 [info]: HEVC编码器版本 1.7
x265 [info]: 编译信息 [Windows][GCC 4.9.2][64位] 8bpp
x265 [info]: 使用CPU特性: MMX2 SSE2Fast SSSE3 SSE4.2 AVX AVX2 FMA3 LZCNT BMI2
x265 [info]: 主 profile, Level-5 (主 tier)
x265 [info]: 创建了包含16个线程的线程池
x265 [info]: 帧线程数 / 池特性       : 5 / wpp(24行)
x265 [info]: 编码QT: 最大CU尺寸, 最小CU尺寸 : 64 / 8
x265 [info]: 残差QT: 最大TU尺寸, 最大深度 : 32 / 1 帧间 / 1 帧内
x265 [info]: 运动估计 / 搜索范围 / 亚像素精度 / 合并模式         : hex / 57 / 2 / 2
x265 [info]: 关键帧最小/最大/场景切换阈值       : 1 / 250 / 40
x265 [info]: 前瞻帧数 / B帧数量 / B帧自适应        : 20 / 4 / 2
x265 [info]: B帧金字塔 / P帧加权 / B帧加权 / 参考帧数量: 1 / 1 / 0 / 3
x265 [info]: 自适应量化(AQ): 模式 / 强度 / 量化组尺寸 / CU树  : 1 / 1.0 / 64 / 1
x265 [info]: 码率控制 / qCompress            : CRF-24.0 / 0.60
x265 [info]: 工具: rd=3 psy-rd=0.30 signhide tmvp strong-intra-smoothing
x265 [info]: 工具: deblock sao
输出 #0, mp4, 输出到 'C:datamerged_datatestsubsubsub2022-10-12_11-03-44_3583968045500ns_io.mp4':
  元数据:
    encoder         : Lavf56.36.100
    流 #0:0: 视频: hevc (libx265) ([35][0][0][0] / 0x0023), yuv420p, 2048x1536, q=2-31, 1 fps, 16384 tbn, 1 tbc
    元数据:
      encoder         : Lavc56.41.100 libx265
流映射:
  流 #0:0 -> #0:0 (rawvideo (native) -> hevc (libx265))
frame=    4 fps=0.0 q=0.0 Lsize=     761kB time=00:00:02.00 bitrate=3117.4kbits/s    
video:759kB audio:0kB subtitle:0kB 其他流:0kB 全局头:1kB 封装开销: 0.236524%
x265 [info]: I帧数量:      2, 平均QP:13.55  kb/s: 2078.50 
x265 [info]: P帧数量:      1, 平均QP:15.08  kb/s: 1560.14 
x265 [info]: B帧数量:      1, 平均QP:18.28  kb/s: 502.67  
x265 [info]: 全局统计:      4, 平均QP:15.11  kb/s: 1554.95 
x265 [info]: 加权P帧占比: Y:0.0% UV:0.0%
x265 [info]: 连续B帧分布: 66.7% 33.3% 0.0% 0.0% 0.0% 

解决方向

  • 升级FFmpeg版本:当前使用的FFmpeg 2.7是2015年的旧版本,libx265也仅为1.7,旧版本编码器或封装器可能存在小帧数视频的兼容性问题,建议升级到最新稳定版。
  • 调整编码器参数:
    • 对libx265添加'-x265-params': 'keyint=1',强制每帧都是关键帧,避免B/P帧依赖导致的末尾帧解码异常。
    • 添加'-bf': '0'关闭B帧,减少帧间依赖,降低小帧数视频损坏概率。
  • 校验帧数据一致性:确保所有帧的尺寸、通道数完全一致,避免因帧格式不统一引发封装错误。
  • 显式指定像素格式:编码时明确输入输出像素格式匹配,比如输入为BGR24时对应正确的输出格式,避免格式转换过程中的异常。

内容的提问来源于Stack Exchange,提问作者Hi_its_me

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 02:05:45