如何在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
相关产品推荐
相关产品推荐

