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

如何用OpenCV100%稳定获取MP4视频的最后一帧?

如何用OpenCV100%稳定获取MP4视频的最后一帧?

我尝试用OpenCV获取MP4视频的最后一帧,使用CAP_PROP_POS_FRAMES方法时部分视频有效、部分无效;改用毫秒定位CAP_PROP_POS_MSEC也存在同样问题。所有视频都是未损坏的MP4格式,已知MP4使用CAP_PROP_POS_FRAMES有兼容性问题,官方推荐CAP_PROP_POS_MSEC,请问有没有100%稳定获取最后一帧的方法?

我的尝试代码

import cv2
from PIL import Image
import os
import numpy as np

last_video_frame_cache = {}

def get_last_frame(video_file):
    # Open the video file
    cap = cv2.VideoCapture(video_file)

    # get last ms
    fps = cap.get(cv2.CAP_PROP_FPS)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    total_duration_ms = int(total_frames/fps*1000)
    last_frame_ms = total_duration_ms - 1  # go to the last millisecond
    print(f"fps {fps} total_frames {total_frames} total_duration_ms {total_duration_ms} last_frame_ms {last_frame_ms}")

    cap.set(cv2.CAP_PROP_POS_MSEC, last_frame_ms)
    ret, last_frame = cap.read()

    # # # go back a few frames since for mp4s you can't go to the exact last frame
    # total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    # cap.set(cv2.CAP_PROP_POS_FRAMES, total_frames-20) # optional
    # # read current frame
    # current_frame_index = int(cap.get(cv2.CAP_PROP_POS_FRAMES))
    # print(f"current_frame_index {current_frame_index} total_Frames {total_frames}")

    # #works # Iterate until at last frame. went back 3 frames so this shouldn't take too long
    # while cap.isOpened():
    #     # read frame
    #     ret, frame = cap.read()
    #     # for debugging
    #     current_frame_index = int(cap.get(cv2.CAP_PROP_POS_FRAMES))
    #     print(f"current_frame_index {current_frame_index}")
    #     if not ret:
    #         break
    #     # needs to be here or else wont work
    #     last_frame = frame

    # Convert the frame to an image
    image = Image.fromarray(last_frame)
    # put into cache
    last_video_frame_cache[video_file] = image

    video_file_seperated, video_file_ext_seperated = os.path.splitext(video_file)
    last_frame_path =  video_file_seperated + "_last_frame" + ".jpg"
    # write last frame
    cv2.imwrite(last_frame_path, last_frame)

    # for mse
    last_frame_array = np.array(image)
    last_frame_grey_global = cv2.cvtColor(last_frame_array, cv2.COLOR_BGR2GRAY)

    # Release the video file
    cap.release()
    print("getting manually")
    return image

稳定解决方案

MP4的索引和GOP(图像组)结构会导致直接通过CAP_PROP_POS_FRAMES或CAP_PROP_POS_MSEC定位到最后一帧失败——因为视频的最后几帧可能不是关键帧,OpenCV无法直接解码非关键帧(依赖前面的关键帧)。最可靠的方法是从视频末尾向前跳一段距离后,逐帧读取直到结束,记录最后成功读取的帧:

优化后的代码

import cv2
from PIL import Image
import os
import numpy as np

last_video_frame_cache = {}

def get_last_frame_stable(video_file):
    cap = cv2.VideoCapture(video_file)
    if not cap.isOpened():
        raise ValueError(f"无法打开视频文件: {video_file}")

    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    # 向前跳转到总帧数-50的位置,避免从头读取,同时避开最后可能的非关键帧问题
    # 如果视频帧数少于50,直接跳转到第0帧
    jump_to_frame = max(0, total_frames - 50)
    cap.set(cv2.CAP_PROP_POS_FRAMES, jump_to_frame)

    last_frame = None
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break
        last_frame = frame

    if last_frame is None:
        # 极端情况:跳转后仍无法读取,尝试从头读取
        cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
        while cap.isOpened():
            ret, frame = cap.read()
            if not ret:
                break
            last_frame = frame

    if last_frame is None:
        raise RuntimeError(f"无法读取视频的任何帧: {video_file}")

    # 后续处理逻辑保持不变
    image = Image.fromarray(last_frame)
    last_video_frame_cache[video_file] = image

    video_file_seperated, _ = os.path.splitext(video_file)
    last_frame_path = f"{video_file_seperated}_last_frame.jpg"
    cv2.imwrite(last_frame_path, last_frame)

    last_frame_grey_global = cv2.cvtColor(np.array(image), cv2.COLOR_BGR2GRAY)

    cap.release()
    return image

方案说明

  • 跳转策略:先跳转到总帧数-50的位置,既避免了从头读取的低效,又确保落在某个关键帧之后,能顺利解码后续所有帧。
  • 双重保障:如果跳转后无法读取到帧(比如视频实际帧数和CAP_PROP_FRAME_COUNT返回值不符),则退化为从头逐帧读取,确保不会失败。
  • 兼容性:该方法不依赖视频的索引准确性,完全通过逐帧读取的方式获取最后一帧,几乎能兼容所有MP4视频。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 00:48:11