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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 13:31:01