使用PyAV从PIL.Image创建MP4视频时遇时间戳错误求解决
问题描述
想要编写一个函数,从另一视频提取的帧创建新MP4视频,这些帧为PIL.Image格式,常被裁剪为原视频的一部分,但所有图像尺寸一致。尝试的代码如下:
def modify_image(img): return img test_input = av.open('input_vid.mp4') test_output =av.open('output_vid.mp4', 'w') in_stream = test_input.streams.video[0] out_stream = test_output.add_stream(template=in_stream) for frame in test_input.decode(in_stream): img_frame = frame.to_image() # Some possible modifications to img_frame... img_frame = modify_image(img_frame) out_frame = av.VideoFrame.from_image(img_frame) out_packet = out_stream.encode(out_frame) print(out_packet) test_input.close() test_output.close()
运行后出现错误:
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[23], line 11 8 img_frame = frame.to_image() 10 out_frame = av.VideoFrame.from_image(img_frame) ---> 11 out_packet = out_stream.encode(out_frame) 12 print(out_packet) 15 test_input.close() File av\stream.pyx:153, in av.stream.Stream.encode() File av\codec\context.pyx:490, in av.codec.context.CodecContext.encode() File av\frame.pyx:52, in av.frame.Frame._rebase_time() ValueError: Cannot rebase to zero time.
对比原VideoFrame和从图像创建的VideoFrame,发现新帧的pts值为None而非整数;覆盖原pts值仍报错,尝试修改dts值则出现AttributeError: attribute 'dts' of 'av.frame.Frame' objects is not writable。需要解决是否有修改dts值的方法,或其他从PIL.Image对象创建视频的方法。
解决方案
方法一:修复PyAV时间戳问题
从PIL.Image生成的VideoFrame缺少有效时间戳,无需修改dts(该属性不可写),只需将原帧的pts和时间基复制到新帧即可解决:
def modify_image(img): return img test_input = av.open('input_vid.mp4') test_output = av.open('output_vid.mp4', 'w') in_stream = test_input.streams.video[0] out_stream = test_output.add_stream(template=in_stream) # 同步输入输出流的时间基 out_stream.time_base = in_stream.time_base for frame in test_input.decode(in_stream): img_frame = frame.to_image() img_frame = modify_image(img_frame) out_frame = av.VideoFrame.from_image(img_frame) # 复制原帧的时间戳与时间基 out_frame.pts = frame.pts out_frame.time_base = frame.time_base # 编码并写入有效数据包 out_packet = out_stream.encode(out_frame) if out_packet: test_output.mux(out_packet) # 刷新编码器剩余数据包,确保视频完整 flush_packet = out_stream.encode(None) if flush_packet: test_output.mux(flush_packet) test_input.close() test_output.close()
关键注意点:
- 必须设置
out_frame.pts和out_frame.time_base,否则编码器无法处理时序 - 编码完成后要调用
encode(None)刷新剩余数据,避免视频结尾缺失 - 仅当
out_packet非空时执行mux,避免写入无效数据
方法二:使用OpenCV+PIL替代PyAV
如果PyAV的时间戳逻辑过于繁琐,可切换到OpenCV方案,无需手动处理时间戳:
import cv2 from PIL import Image import numpy as np def modify_image(img): # 替换为你的实际图像处理逻辑,例如裁剪 return img.crop((100, 100, 500, 500)) # 读取输入视频 cap = cv2.VideoCapture('input_vid.mp4') fps = cap.get(cv2.CAP_PROP_FPS) # 读取第一帧确定输出尺寸 ret, frame = cap.read() if not ret: raise ValueError("无法读取输入视频") pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) processed_img = modify_image(pil_img) output_size = (processed_img.width, processed_img.height) # 创建视频写入器 fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter('output_vid.mp4', fourcc, fps, output_size) # 写入第一帧 cv_img = cv2.cvtColor(np.array(processed_img), cv2.COLOR_RGB2BGR) out.write(cv_img) # 处理剩余帧 while cap.isOpened(): ret, frame = cap.read() if not ret: break pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) processed_img = modify_image(pil_img) cv_img = cv2.cvtColor(np.array(processed_img), cv2.COLOR_RGB2BGR) out.write(cv_img) # 释放资源 cap.release() out.release()
该方案依赖OpenCV自动处理帧时序,逻辑更简洁,适合大多数图像转视频场景。
内容的提问来源于stack exchange,提问作者ATK
相关产品推荐
相关产品推荐

