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

如何在Python中将目录帧转为视频且避免首帧每FPS重复出现

解决帧转视频时每15帧重复首帧的问题

提问内容

我是本平台新用户,将尽量清晰表述问题。我的目标是把目录中的帧转换为视频,但不知为何每15FPS就会重复出现首帧。以下是我最初使用的代码:

import cv2
import glob
from pathlib import Path

def play_frames(frames_dir, fps, output_video_path):
    # Get the list of frames in the directory
    frames = sorted(glob.glob(str(frames_dir / '*.png')))

    # Read the first frame to get the width and height
    frame = cv2.imread(frames[0])
    height, width, _ = frame.shape

    # Create the video writer object
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    video_writer = cv2.VideoWriter(output_video_path, fourcc, fps, (width, height))

    for frame_path in frames:
        # Read the frame
        frame = cv2.imread(frame_path)

        # Display the frame
        cv2.imshow('Frame', frame)
        cv2.waitKey(int(1000 / fps))

        # Write the frame to the video
        video_writer.write(frame)

    # Release the video writer
    video_writer.release()
    cv2.destroyAllWindows()

# Directory path
frames_dir = Path('./detected_objects')
fps = 15
output_video_path = 'vid2output.mp4'

# Play the frames and save as video
play_frames(frames_dir, fps, output_video_path)

我尝试过多种代码,但所有试过的代码都存在每15FPS重复首帧的问题。我哪里出错了?有没有其他方法可以将图像转为视频且避免该问题?(我已检查目录中的帧,序列均正确。)

更新:我在代码中添加了分辨率适配逻辑,但首帧每FPS重复的问题仍存在。更新后的代码如下:

import cv2
import glob
import numpy as np
from pathlib import Path

def play_frames(frames_dir, fps, output_video_path):
    # Get the list of frames in the directory
    frames = sorted(glob.glob(str(frames_dir / '*.png')))

    # Read the first frame to get the width and height
    frame = cv2.imread(frames[0])
    height, width, _ = frame.shape

    # Calculate the maximum width and height among all frames
    max_width = width
    max_height = height

    for frame_path in frames[1:]:
        # Read each frame to get its dimensions
        frame = cv2.imread(frame_path)
        height, width, _ = frame.shape

        # Update the maximum width and height if necessary
        max_width = max(max_width, width)
        max_height = max(max_height, height)

    # Create the video writer object
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    video_writer = cv2.VideoWriter(output_video_path, fourcc, fps, (max_width, max_height))

    # Resize and pad each frame before writing to the video
    for frame_path in frames:
        frame = cv2.imread(frame_path)

        # Resize the frame to the maximum dimensions
        resized_frame = cv2.resize(frame, (max_width, max_height))

        # Write the resized frame to the video
        video_writer.write(resized_frame)

    # Release the video writer
    video_writer.release()

# Directory path
frames_dir = Path('./detected_objects')
fps = 15
output_video_path = 'vid1output.mp4'

# Play the frames and save as video
play_frames(frames_dir, fps, output_video_path)

感谢大家的帮助,我很乐意提供更多相关信息!


问题分析与解决方案

核心原因

你遇到的重复首帧问题,大概率是文件名排序逻辑错误导致的。默认的sorted(glob.glob(...))按字符串字典序排序,比如frame10.png会排在frame2.png前面,导致帧序列混乱。当循环到错误位置时,可能误读首帧并重复写入,最终出现每15帧重复首帧的现象。

代码修复方案

1. 修正文件名排序逻辑

替换原代码中获取帧列表的部分,改为按文件名中的数字排序:

# 替换原来的 frames = sorted(glob.glob(...))
frames = sorted(
    glob.glob(str(frames_dir / '*.png')),
    key=lambda x: int(Path(x).stem.split('_')[-1])  # 适配frame_1.png、frame_2.png格式
)

如果你的文件名是frame1.png、frame2.png格式,把split('_')[-1]改成strip('frame')即可,根据实际文件名格式调整提取数字的逻辑。

2. 增加帧读取有效性校验

在读取帧后添加判断,避免读取失败时写入空帧或重复帧:

for frame_path in frames:
    frame = cv2.imread(frame_path)
    if frame is None:
        print(f"Warning: 无法读取帧 {frame_path},已跳过")
        continue
    # 后续的resize、写入逻辑...

3. 完整修复后的代码

import cv2
import glob
import numpy as np
from pathlib import Path

def play_frames(frames_dir, fps, output_video_path):
    # 按文件名中的数字排序帧列表
    frames = sorted(
        glob.glob(str(frames_dir / '*.png')),
        key=lambda x: int(Path(x).stem.split('_')[-1])
    )

    # 计算最大分辨率
    max_width, max_height = 0, 0
    for frame_path in frames:
        frame = cv2.imread(frame_path)
        if frame is None:
            continue
        height, width = frame.shape[:2]
        max_width = max(max_width, width)
        max_height = max(max_height, height)

    # 创建视频写入器
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    video_writer = cv2.VideoWriter(output_video_path, fourcc, fps, (max_width, max_height))

    # 写入每帧
    for frame_path in frames:
        frame = cv2.imread(frame_path)
        if frame is None:
            print(f"Warning: 跳过无效帧 {frame_path}")
            continue
        # 调整帧尺寸
        resized_frame = cv2.resize(frame, (max_width, max_height))
        video_writer.write(resized_frame)

    video_writer.release()

# 配置参数
frames_dir = Path('./detected_objects')
fps = 15
output_video_path = 'fixed_output.mp4'

play_frames(frames_dir, fps, output_video_path)

替代方案:使用FFmpeg直接转换

如果代码方式仍有问题,推荐用FFmpeg命令行直接转换,更稳定且无需编写代码:

ffmpeg -framerate 15 -i detected_objects/frame_%d.png -c:v libx264 -r 15 -pix_fmt yuv420p output.mp4
  • 若文件名是frame_1.png格式,把frame_%d.png改成frame_%d.png或frame_%04d.png(根据数字位数调整)
  • 该命令会自动按正确的数字顺序读取帧,避免排序问题

内容的提问来源于stack exchange,提问作者kora_num14

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 20:50:54