获取视频中每一帧的时间戳

Getting timestamp of each frame in a video

我用我编写的 Android 5.2 应用程序从平板电脑的前置摄像头录制了几个视频。我已经为每个视频存储了以毫秒(Unix 时间)为单位的开始时间戳。

遗憾的是,每个视频的帧率都不同(从 20 到 30 不等)。使用 OpenCV,我可以获得每个视频的帧率:

import cv2
video = cv2.VideoCapture(videoFile)
fps = video.get(cv2.CAP_PROP_FPS)

这很好用,理论上我可以为视频中的每一帧添加 1000/fps(由于毫秒数)。但这假设帧率在整个录制过程中保持稳定。不知道是不是这样

在 Python 中是否有可能独立于帧率获取视频中每个帧的时间戳(以毫秒为单位)?

你想要cv2.CAP_PROP_POS_MSEC。查看所有不同的捕获属性 here.

编辑:实际上,作为 Dan Mašek pointed out to me, when you grab that property, it looks like OpenCV is exactly doing that calculation(至少假设您使用的是 FFMPEG):

case CV_FFMPEG_CAP_PROP_POS_MSEC:
    return 1000.0*(double)frame_number/get_fps();

看来您总是要依赖恒定的帧速率假设。然而,即使假设帧速率恒定,重要的是您乘以帧数而不是一直添加 1000/fps。当您重复添加浮动时,错误会累积,在一段较长的视频中,这会产生很大的不同。例如:

import cv2

cap = cv2.VideoCapture('vancouver2.mp4')
fps = cap.get(cv2.CAP_PROP_FPS)

timestamps = [cap.get(cv2.CAP_PROP_POS_MSEC)]
calc_timestamps = [0.0]

while(cap.isOpened()):
    frame_exists, curr_frame = cap.read()
    if frame_exists:
        timestamps.append(cap.get(cv2.CAP_PROP_POS_MSEC))
        calc_timestamps.append(calc_timestamps[-1] + 1000/fps)
    else:
        break

cap.release()

for i, (ts, cts) in enumerate(zip(timestamps, calc_timestamps)):
    print('Frame %d difference:'%i, abs(ts - cts))

Frame 0 difference: 0.0
Frame 1 difference: 0.0
Frame 2 difference: 0.0
Frame 3 difference: 1.4210854715202004e-14
Frame 4 difference: 0.011111111111091532
Frame 5 difference: 0.011111111111091532
Frame 6 difference: 0.011111111111091532
Frame 7 difference: 0.011111111111119953
Frame 8 difference: 0.022222222222183063
Frame 9 difference: 0.022222222222183063
...
Frame 294 difference: 0.8111111111411446

这当然是以毫秒为单位的,所以可能看起来并没有那么大。但是这里我在计算中差了将近 1 毫秒,而且这只是一个 11 秒的视频。无论如何,使用这个 属性 更容易。

通常这些相机都有卷帘快门,这意味着图像是逐行扫描的,所以严格来说不能在图像上加上时间戳。我一直在使用精确定时(ns 级)led 闪光灯同步多个卷帘快门相机(iPhone 6)。我发现帧速率是可变的(高速时标称 240 fps,但在 239 和 241 之间变化。相互同步可以达到 1/500000 秒,但这需要特殊设置。如果你有兴趣我可以发给你一些文档(恐怕我的软件是在 Matlab 中,所以没有现成的 python 代码)

我已经使用 moviepy 获取单个帧的时间(以秒为单位)

pip install moviepy

import sys
import numpy as np
import cv2
import moviepy.editor as mpy
from matplotlib import pyplot as plt

vid = mpy.VideoFileClip('input_video\v3.mp4')

for i, (tstamp, frame) in enumerate(vid.iter_frames(with_times=True)):
    print(tstamp%60)
    plt.imshow(frame)
    plt.show()

这是一个简化版本,它只读取视频并打印出带有时间戳的帧编号。

import cv2

cap = cv2.VideoCapture('path_to_video/video_filename.avi')

frame_no = 0
while(cap.isOpened()):
    frame_exists, curr_frame = cap.read()
    if frame_exists:
        print("for frame : " + str(frame_no) + "   timestamp is: ", str(cap.get(cv2.CAP_PROP_POS_MSEC)))
    else:
        break
    frame_no += 1

cap.release()

这给出了如下所示的输出:

for frame : 0   timestamp is:  0.0
for frame : 1   timestamp is:  40.0
for frame : 2   timestamp is:  80.0
for frame : 3   timestamp is:  120.0
for frame : 4   timestamp is:  160.0
for frame : 5   timestamp is:  200.0
for frame : 6   timestamp is:  240.0
for frame : 7   timestamp is:  280.0
for frame : 8   timestamp is:  320.0
for frame : 9   timestamp is:  360.0
for frame : 10   timestamp is:  400.0
for frame : 11   timestamp is:  440.0
for frame : 12   timestamp is:  480.0
...