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

xriantvc  于 2022-11-15  发布在  其他
关注(0)|答案(5)|浏览(343)

我用自己编写的Android 5.2应用程序从平板电脑的前置摄像头录制了几段视频,并以毫秒(Unix时间)为单位存储了每段视频的开始时间戳。
不幸的是,每个视频都有不同的帧率(范围从20到30)。使用OpenCV,我可以获得每个视频的帧率:

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

这很好用,理论上我可以为视频中的每一帧增加1000/fps(由于毫秒)。但这是假设帧率在整个录制过程中保持稳定。我不知道是否是这样。
在Python中是否有可能获得视频中每一帧的时间戳(以毫秒为单位),而不依赖于帧速率?

xe55xuns

xe55xuns1#

您需要cv2.CAP_PROP_POS_MSEC。请在此处查看所有不同的捕获属性。
编辑:实际上,正如Dan Mašek向我指出的那样,当您获取该属性时,看起来OpenCV确实在进行该计算(至少假设您使用的是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))

帧0差异:0.0
帧1差异:0.0
帧2差异:0.0
帧3的差异:1.4210854715202004e-14
第4帧差异:0.011111111111091532
第5帧差异:0.011111111111091532
帧6差异:0.011111111111091532
帧7差异:0.011111111111119953
第8帧差异:0.022222222222183063
帧9差异:0.022222222222183063
...
帧294差异:0.8111111111411446
当然,这是以毫秒为单位的,所以可能看起来并不大。但是在这里,我在计算中几乎差了1毫秒,而且这只是针对11秒的视频。无论如何,使用这个属性更容易。

bfrts1fy

bfrts1fy2#

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

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
...
8dtrkrch

8dtrkrch3#

我用moviey来计算每一帧秒数

第一个

bzzcjhmw

bzzcjhmw4#

通常这些相机有一个滚动快门,这意味着图像是逐行扫描的,所以严格地说,一个人不能把一个时间戳的图像。(iPhone 6)使用精确定时的(ns-scale)led闪光灯。我发现帧速率是可变的(高速时标称为240 fps,但在239,something和241,something之间变化。相互同步最高可达1/500000 s,但这需要一个特殊的设置。如果你感兴趣,我可以给你一些文档(我的软件恐怕是在Matlab,所以没有现成的Python代码)

a14dhokn

a14dhokn5#

我用多个库做了一些测试。

import av
import cv2
import time
from decord import VideoReader
from ffms2 import VideoSource
from moviepy.editor import VideoFileClip
from typing import List

def with_movie_py(video: str) -> List[int]:
    """
    Link: https://pypi.org/project/moviepy/
    My comments:
        The timestamps I get are not good compared to gMKVExtractGUI or ffms2. (I only tried with VFR video)

    Parameters:
        video (str): Video path
    Returns:
        List of timestamps in ms
    """
    vid = VideoFileClip(video)

    timestamps = [
        round(tstamp * 1000) for tstamp, frame in vid.iter_frames(with_times=True)
    ]

    return timestamps

def with_cv2(video: str) -> List[int]:
    """
    Link: https://pypi.org/project/opencv-python/
    My comments:
        I don't know why, but the last 4 or 5 timestamps are equal to 0 when they should not.
        Also, cv2 is slow. It took my computer 132 seconds to process the video.

    Parameters:
        video (str): Video path
    Returns:
        List of timestamps in ms
    """
    timestamps = []
    cap = cv2.VideoCapture(video)

    while cap.isOpened():
        frame_exists, curr_frame = cap.read()
        if frame_exists:
            timestamps.append(round(cap.get(cv2.CAP_PROP_POS_MSEC)))
        else:
            break

    cap.release()

    return timestamps

def with_pyffms2(video: str) -> List[int]:
    """
    Link: https://pypi.org/project/ffms2/
    My comments:
        Works really well, but it doesn't install ffms2 automatically, so you need to do it by yourself.
        The easiest way is to install Vapoursynth and use it to install ffms2.
        Also, the library doesn't seems to be really maintained.

    Parameters:
        video (str): Video path
    Returns:
        List of timestamps in ms
    """
    video_source = VideoSource(video)

    # You can also do: video_source.track.timecodes
    timestamps = [
        int(
            (frame.PTS * video_source.track.time_base.numerator)
            / video_source.track.time_base.denominator
        )
        for frame in video_source.track.frame_info_list
    ]

    return timestamps

def with_decord(video: str) -> List[int]:
    """
    Link: https://github.com/dmlc/decord
    My comments:
        Works really well, but it seems to only work with mkv and mp4 file.
        Mp4 file can have a +- 1 ms difference with ffms2, but it is acceptable.

    Parameters:
        video (str): Video path
    Returns:
        List of timestamps in ms
    """
    vr = VideoReader(video)

    timestamps = vr.get_frame_timestamp(range(len(vr)))
    timestamps = (timestamps[:, 0] * 1000).round().astype(int).tolist()

    return timestamps

def with_pyav(video: str) -> List[int]:
    """
    Link: https://pypi.org/project/av/
    My comments:
        I don't know why, but sometimes it simply doesn't work with certain video.
        Also, I tested with different mp4 file and sometimes it take 8 seconds and sometimes 117 seconds.

    Parameters:
        video (str): Video path
    Returns:
        List of timestamps in ms
    """
    container = av.open(video)
    video = container.streams.video[0]
    av_timestamps = [
        int(frame.pts * video.time_base * 1000) for frame in container.decode(video)
    ]

    container.close()

    return av_timestamps

def main():
    video = r"WRITE_YOUR_VIDEO_PATH"

    start = time.process_time()
    movie_py_timestamps = with_movie_py(video)
    print(f"With Movie py {time.process_time() - start} seconds")

    start = time.process_time()
    cv2_timestamps = with_cv2(video)
    print(f"With cv2 {time.process_time() - start} seconds")

    start = time.process_time()
    ffms2_timestamps = with_pyffms2(video)
    print(f"With ffms2 {time.process_time() - start} seconds")

    start = time.process_time()
    decord_timestamps = with_decord(video)
    print(f"With decord {time.process_time() - start} seconds")

    start = time.process_time()
    av_timestamps = with_pyav(video)
    print(f"With av {time.process_time() - start} seconds")

if __name__ == "__main__":
    main()

以下是获取24分钟mkv的时间戳所需的时间。

With Movie py 11.71875 seconds
With cv2 23.359375 seconds
With ffms2 0.3125 seconds
With decord 0.125 seconds
With av 8.75 seconds

相关问题