如何用cv2在Python中获取文件的总帧数

114 投票
5 回答
150489 浏览
提问于 2025-04-18 17:38

如何通过Python和OpenCV模块来知道一个文件(.avi格式)中总共有多少帧。

如果可以的话,我们还可以通过这个方法获取视频文件的哪些信息(比如分辨率、帧率、时长等等)。

5 个回答

6

还有一种解决方案,不依赖于有时会出问题的 CV_CAP_PROP 获取器,就是通过循环遍历整个视频文件。

  • 每当遇到一个有效的帧时,就增加一个帧计数器,当遇到无效帧(视频文件结束)时停止。
  • 获取分辨率的信息比较复杂,因为有些编码格式支持可变分辨率(类似于音频文件中的可变比特率,音频的比特率不是固定的,而是覆盖某个预定义的范围)。

    • 固定分辨率 - 在这种情况下,只需要第一个帧就能确定整个视频文件的分辨率,所以不需要遍历整个视频。
    • 可变分辨率 - 你需要获取每一帧的分辨率(宽度和高度),然后计算平均值来得到视频的平均分辨率。
  • 帧率(FPS)可以计算,但这里也有和分辨率一样的问题 - 固定帧率(CFR)和可变帧率(VFR)。这更像是一个多线程的问题。个人建议使用帧计数器,在每个有效帧后增加计数,同时在每秒的间隔内,后台线程会触发保存当前计数器的值并重置它。你可以把这些值存储在一个列表中,以便在最后计算平均/固定帧率时,也能知道视频的总帧数。

这种相对简单的方法的缺点是你必须遍历整个文件,如果视频长达几个小时,用户肯定会注意到这一点。在这种情况下,你可以聪明地在后台进程中进行处理,让用户在你的应用程序收集这些关于加载视频文件的信息时可以做其他事情。

好处是,无论你有什么视频文件,只要OpenCV能读取,你都会得到相当准确的结果,而不像 CV_CAP_PROP,它可能会按你预期的那样工作,也可能不会。

17

这是在Python 3.6.5(使用Anaconda)和OpenCV 3.4.2下的工作原理。
【注意】:在使用官方OpenCV 网站上提供的任何属性时,你需要去掉“CV_”这个前缀,比如“CV_CAP_PROP_xx”。

import cv2
cap = cv2.VideoCapture("video.mp4")
property_id = int(cv2.CAP_PROP_FRAME_COUNT) 
length = int(cv2.VideoCapture.get(cap, property_id))
print( length )
23

有两种方法可以确定视频文件中的帧数。

  • 方法一:利用OpenCV自带的属性来获取视频文件的元信息,这种方法速度快且高效,但不太准确
  • 方法二:手动逐帧遍历视频文件并进行计数,这种方法速度慢且效率低,但非常准确

方法一的速度很快,依赖于OpenCV的视频属性功能,几乎可以瞬间确定视频文件中的帧数。不过,这种方法的准确性会受到你使用的OpenCV和视频编码器版本的影响。另一方面,手动计数每一帧虽然会非常慢,但准确率是100%。下面是一个函数,默认尝试使用方法一,如果失败了,就会自动使用方法二。

def frame_count(video_path, manual=False):
    def manual_count(handler):
        frames = 0
        while True:
            status, frame = handler.read()
            if not status:
                break
            frames += 1
        return frames 

    cap = cv2.VideoCapture(video_path)
    # Slow, inefficient but 100% accurate method 
    if manual:
        frames = manual_count(cap)
    # Fast, efficient but inaccurate method
    else:
        try:
            frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        except:
            frames = manual_count(cap)
    cap.release()
    return frames

基准测试

if __name__ == '__main__':
    import timeit
    import cv2

    start = timeit.default_timer()
    print('frames:', frame_count('fedex.mp4', manual=False))
    print(timeit.default_timer() - start, '(s)')

    start = timeit.default_timer()
    print('frames:', frame_count('fedex.mp4', manual=True))
    print(timeit.default_timer() - start, '(s)')

方法一的结果

frames: 3671
0.018054921 (s)

方法二的结果

frames: 3521
9.447095287 (s)

注意,这两种方法的结果相差150帧,并且方法二明显比方法一慢。所以如果你需要速度,并且愿意牺牲一些准确性,可以选择方法一。如果你能接受延迟,但需要确切的帧数,就用方法二。

56
import cv2

cap = cv2.VideoCapture(fn)

if not cap.isOpened(): 
    print("could not open :",fn)
    return
    
length = int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT))
width  = int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT))
fps    = cap.get(cv2.cv.CV_CAP_PROP_FPS)

想了解更多信息,可以查看这里

另外,记得要有点保留态度,并不是所有的参数都是必须的,有些可能在你的捕捉设备或视频编码中并不存在。

187

在更新的OpenCV版本中(我用的是3.1.0),它的用法是这样的:

import cv2

cap = cv2.VideoCapture("video.mp4")
length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print( length )

其他视频属性也类似,比如 cv2.CAP_PROP_*

撰写回答