使用cv2.VideoWriter()修改fps无法降低视频速度的原因排查
问题描述
使用cv2.VideoWriter()实现图片合成视频功能时,修改传入的fps参数(如从30调整为20)后,输出视频的实际播放速度无任何变化,始终高于预期速度。
原实现代码
def video_Writer(images_path_read, output_vid_path, fps=30): ''' parameters: - images_path_read - the path containing images to be combined and make a video - output_vid_path - the path and file name of output video (must include .mp4) method: - the function get the images path, read the sorted images and make a video according to the first image size. ''' dir_images_list = os.listdir(images_path_read) dir_images_list = natsorted(dir_images_list) # get the size of the first image fourcc = VideoWriter_fourcc(*'mp4v') img_0 = cv2.imread(images_path_read + dir_images_list[0]) # find the image shape size = (img_0.shape[1], img_0.shape[0]) # Creating VideoWriter Object and open it to start in the loop vid_writer = cv2.VideoWriter(output_vid_path, fourcc, fps, size) vid_writer.open(output_vid_path, fourcc, fps, size) for image in dir_images_list: # Reading the images from the path folder img = cv2.imread(images_path_read + image) # Checking the size of frames if size[0] != img.shape[1] or size[1] != img.shape[0]: img = resize(img, size) print('Resizing Happend') # Adding images to Video vid_writer.write(img) # print(image) # Releasing VideoWriter Object vid_writer.release()
问题原因
- 重复调用
open()方法导致参数异常:cv2.VideoWriter构造函数在传入合法参数时会自动完成流初始化、打开写入通道,实例化对象后再次手动调用open()方法,部分OpenCV版本会在二次打开时重置配置,导致传入的自定义fps参数失效,回退到编码器默认帧率(通常为25/30fps),因此无论怎么修改传入的fps值,最终写入视频的帧率都是固定值。 mp4v编码器兼容性缺陷:使用的mp4v(MPEG-4 Part 2)编码器在pip源分发的轻量版OpenCV中,存在帧率元数据写入bug,经常出现自定义fps不生效、文件头帧率信息错误的问题,直接导致播放器读取到错误的帧率参数,按固定高速播放。- 缺少写入流有效性校验:代码没有判断
VideoWriter是否真的初始化成功,部分环境下编码器加载失败时,写入操作会走异常回退逻辑,所有自定义参数都不会生效。
修复方案
- 删除重复的
open()调用,仅在构造函数中传入配置参数,同时增加初始化成功校验,避免异常回退导致参数失效。 - 更换兼容性更好的编码器:输出mp4格式时将fourcc替换为
*'avc1'(H.264编码),该编码器对自定义帧率的支持更稳定,兼容绝大多数播放器;如果无H.264依赖,也可先输出avi格式搭配*'XVID'编码器验证帧率逻辑。 - 修复代码中的隐性bug:路径拼接改用
os.path.join()避免跨平台路径错误,resize调用补全cv2命名空间,避免调用到非OpenCV的resize方法导致帧写入异常。 - 增加写入结果校验:视频生成后可用OpenCV读取生成的文件,打印
cap.get(cv2.CAP_PROP_FPS)确认实际写入的帧率是否符合预期,排除部分播放器强制按固定帧率播放的干扰。
修复后参考代码
import os import cv2 from natsort import natsorted def video_Writer(images_path_read, output_vid_path, fps=30): ''' parameters: - images_path_read - the path containing images to be combined and make a video - output_vid_path - the path and file name of output video (must include .mp4) method: - the function get the images path, read the sorted images and make a video according to the first image size. ''' dir_images_list = [f for f in os.listdir(images_path_read) if f.lower().endswith(('.png', '.jpg', '.jpeg'))] dir_images_list = natsorted(dir_images_list) if not dir_images_list: raise ValueError("指定目录下未找到有效图片文件") # 优先使用avc1(H.264)编码器,兼容性更好 fourcc = cv2.VideoWriter_fourcc(*'avc1') img_0 = cv2.imread(os.path.join(images_path_read, dir_images_list[0])) if img_0 is None: raise ValueError("首帧图片读取失败,请检查路径") size = (img_0.shape[1], img_0.shape[0]) # 仅在构造函数中初始化,不重复调用open vid_writer = cv2.VideoWriter(output_vid_path, fourcc, fps, size) if not vid_writer.isOpened(): # 如果avc1加载失败,回退到mp4v fourcc = cv2.VideoWriter_fourcc(*'mp4v') vid_writer = cv2.VideoWriter(output_vid_path, fourcc, fps, size) if not vid_writer.isOpened(): raise RuntimeError("视频写入流初始化失败,请检查编码器依赖和输出路径") for image in dir_images_list: img_path = os.path.join(images_path_read, image) img = cv2.imread(img_path) if img is None: print(f"跳过无效图片: {img_path}") continue # 补全cv2命名空间调用resize if size[0] != img.shape[1] or size[1] != img.shape[0]: img = cv2.resize(img, size) print(f"图片{image}尺寸不匹配,已自动缩放") vid_writer.write(img) # 释放资源前强制刷新缓冲区 vid_writer.release() cv2.destroyAllWindows() # 校验实际写入的帧率 verify_cap = cv2.VideoCapture(output_vid_path) actual_fps = verify_cap.get(cv2.CAP_PROP_FPS) verify_cap.release() print(f"视频生成完成,实际写入帧率为: {actual_fps}")
内容的提问来源于stack exchange,提问作者Moheb Zandinezhad
相关产品推荐
相关产品推荐

