使用OpenCV和Python计算视频中的总帧数

2020-10-23 15:02:26 浏览数 (1)

本文来自光头哥哥的博客【Count the total number of frames in a video with penCV and Python】,仅做学习分享。

代码语言:javascript复制
原文链接:https://www.pyimagesearch.com/2017/01/09/count-the-total-number-of-frames-in-a-video-with-opencv-and-python/

一个读者的问题:

我需要用OpenCV计算视频文件中帧的总数。我发现的唯一的方法是对视频文件中的每一帧逐个循环,并增加一个计数器。有更快的方法吗?

在使用OpenCV和Python处理视频文件时,有两种方法来确定帧的总数:

  • 方法1:使用OpenCV提供的内置属性访问视频文件元信息并返回帧总数的快速、高效的方法。
  • 方法2:缓慢、低效的方法,需要我们手动循环每一帧,并为我们读的每一帧增加一个计数器。

方法1显然是理想的。 我们所需要做的就是打开视频文件的指针,告诉OpenCV我们感兴趣的元属性,并获得返回值。

不用手动循环所有帧。 不用浪费的CPU来循环解码。 但是有一个问题,因为OpenCV版本不同和安装的视频编解码器的多样性,导致方法1有很多bug。

你会发现在某些情况下,超过一半的.get和.set方法在视频指针上不起作用。在这种情况下,我们将不可避免地回到方法2。

那么,有没有办法将这两个方法封装到一个函数中呢?

我已经在imutils库中实现了count_frames函数,但为了确保你理解其中的内容,我们今天将回顾整个函数。

计算帧数的简单方法

在OpenCV中计算视频帧数的第一种方法非常快——它只是使用OpenCV提供的内置属性来访问视频文件并读取视频的元信息。

现在让我们来看看这个函数是如何在imutils中实现的:

代码语言:javascript复制
# import the necessary packages
from ..convenience import is_cv3
import cv2
def count_frames(path, override=False):
  # grab a pointer to the video file and initialize the total
  # number of frames read
  video = cv2.VideoCapture(path)
  total = 0
  # if the override flag is passed in, revert to the manual
  # method of counting frames
  if override:
    total = count_frames_manual(video)

首先,我们在第2行和第3行上导入必要的Python包。我们需要is_cv3函数来检查实际的OpenCV使用的是cv2还是OpenCV的哪个版本。

我们在第5行定义count_frames函数。这个方法需要一个参数以及一个可选参数:

path:这是我们的视频文件在磁盘上的路径。 override:一个布尔标志,用来决定我们是否应该跳过方法1而直接使用速度较慢(但保证准确无错误)的方法2。

我们访问cv2.VideoCapture,在第7行上的VideoCapture获得一个指向实际视频文件的指针,然后初始化视频中的帧总数。

然后我们在第11行进行检查,看看是否应该重写。如果是,我们调用count_frames_manual函数(我们将在下一节中定义)。

如果否,就让我们看看方法1是如何实际实现的:

代码语言:javascript复制
  # otherwise, let's try the fast way first
  else:
    # lets try to determine the number of frames in a video
    # via video properties; this method can be very buggy
    # and might throw an error based on your OpenCV version
    # or may fail entirely based on your which video codecs
    # you have installed
    try:
      # check if we are using OpenCV 3
      if is_cv3():
        total = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
      # otherwise, we are using OpenCV 2.4
      else:
        total = int(video.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT))
    # uh-oh, we got an error -- revert to counting manually
    except:
      total = count_frames_manual(video)
  # release the video file pointer
  video.release()
  # return the total number of frames in the video
  return total

为了通过OpenCV提供的API来确定视频文件中的帧数,我们需要利用所谓的捕获属性,其被OpenCV称为CAP_PROP(任何时候你看到一个以CAP_PROP_*开头的常量,你应该知道它与视频处理相关)。

在opencv3中,帧计数属性的名称是cv2.CAP_PROP_FRAME_COUNT,理想情况下,将各自的属性名称传递给视频指针的.get方法将允许我们获得视频中的总帧数(第10-15行)。

但是,根据你的OpenCV安装版本和视频编解码器的不同,这种方法在某些情况下会失效。

如果是这种情况,我们已经用一个try/except块包装了关键代码段。如果出现异常,我们只需还原为手工计算帧数(第16和17行)。

最后,我们释放视频文件指针(19行)并返回视频的总帧数(21行)。

循环计数

上文介绍了快速、高效的方法来计算视频帧数,现在让我们转到较慢的count_frames_manual方法。

代码语言:javascript复制
def count_frames_manual(video):
  # initialize the total number of frames read
  total = 0
  # loop over the frames of the video
  while True:
    # grab the current frame
    (grabbed, frame) = video.read()
   
    # check to see if we have reached the end of the
    # video
    if not grabbed:
      break
    # increment the total number of frames read
    total  = 1
  # return the total number of frames in the video file
  return total

count_frames_manual函数只需要一个单独的参数video,我们假设它是一个由cv2.VideoCapture实例化的指针。

首先我们初始化从视频的帧数变量total=0,循环帧,直到我们到达视频的末尾,并在此过程中增加计数器total。

然后将total返回给调用函数。

值得一提的是,该方法是完全准确无误的。

在使用这个函数时,也可能会返回零帧。当这种情况发生时,99%的可能性是:

  • 你给cv2.VideoCapture提供了无效的视频文件路径。
  • 您没有安装适当的视频编解码器,因此OpenCV无法读取该文件。如果是这种情况,则需要安装适当的视频编解码器,然后重新编译并重新安装OpenCV。

0 人点赞