如何知道一个文件的总帧数与 cv2在 python

如何使用 open cv 模块通过 Python 知道文件(. avi)中的 Frame 总数。

如果可能的话,我们可以通过这个获得视频文件的所有信息(分辨率、 fps、持续时间等)。

120562 次浏览
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)

see here for more info.

also, all of it with a grain of salt, not all those props are mandatory, some might not be available with your capture / video codec

Another solution that doesn't depend on the sometimes buggy CV_CAP_PROP getters is to traverse your whole video file in a loop

  • Increase a frame counter variable every time a valid frame is encountered and stop when an invalid one comes (end of the video file).
  • Gathering information about the resolution is trickier because some codecs support variable resolution (similar to VBR in audio files where the bitrate is not a constant but instead covers some predefined range).

    • constant resolution - you need only the first frame to determine the resolution of the whole video file in this case so traversing the full video is not required
    • variable resolution - you need to get the resolution of every single frame (width and height) and calculate an average to get the average resolution of the video
  • FPS can be calculated however here you have the same problem as with the resolution - constant (CFR) vs variable (VFR). This is more of a mutli-threading problem omho. Personally I would use a frame counter, which increased after each valid frame while at an interval of 1 second a timer (running in a background thread) would trigger saving the current counter's value and then resetting it. You can store the values in a list in order to calculate the average/constant frame rate at the end when you will also know the total number of frames the video has.

The disadvantage of this rather simplistic way of doing things is that you have to traverse the whole file, which - in case it's several hours long - will definitely be noticeable by the user. In this case you can be smart about it and do that in a background process while letting the user do something else while your application is gathering this information about the loaded video file.

The advantage is that no matter what video file you have as long as OpenCV can read from it you will get quite accurate results unlike the CV_CAP_PROP which may or may not work as you expect it to.

With a newer OpenCV version (I use 3.1.0) it works like this:

import cv2


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

And similar for other video properties cv2.CAP_PROP_*

Here is how it works with Python 3.6.5 (on Anaconda) and OpenCV 3.4.2. [Note]: You need to drop the "CV_" from the "CV_CAP_PROP_xx" for any property as given on the official OpenCV website.

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 )

There are two methods to determine the number of frames in a video file

  • Method #1: Utilize built-in OpenCV properties to access video file meta information which is fast and efficient but inaccurate
  • Method #2: Manually loop over each frame in the video file with a counter which is slow and inefficient but accurate

Method #1 is fast and relys on OpenCV's video property functionality which almost instantaneously determines the number of frames in a video file. However, there is an accuracy trade-off since it is dependent on your OpenCV and video codec versions. On the otherhand, manually counting each frame will be 100% accurate although it will be significantly slower. Here's a function that attempts to perform Method #1 by default, if it fails, it will automatically utilize method #2

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

Benchmarks

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)')

Method #1 results

frames: 3671
0.018054921 (s)

Method #2 results

frames: 3521
9.447095287 (s)

Note the two methods differ by 150 frames and Method #2 is significantly slower than Method #1. Therefore if you need speed but willing to sacrifice accuracy, use Method #1. In situations where you're fine with a delay but need the exact number of frames, use Method #2