4
votes

I'm writing a simple OpenCV script in python to do some machine learning. I want to feed it a sequence of video files that it will process and then exit and spit out some parameters. However when I open a video file with cv2.VideoCapture it loops infinitely. How can I detect when I hit the end of the video?

**Edit ** So a few people noted that there should be an empty frame returned when the read hits end of file. I tried looking for that and it works on my Linux machine but doesn't on my mac. On the mac it just loops and keeps going as if there is no end to the file. Any ideas?

Code is shown below for the reading loop, it loops infinitely unless I hit the esc key.

cam = cv2.VideoCapture(sourceVideo)            
while True:
    ret, img = cam.read()                      
    cv2.imshow('detection', img)
    print ret
    if (0xFF & cv2.waitKey(5) == 27) or img.size == 0:
        break

Note that ret always returns as True, and the img is always valid data. Is it possible that I shouldn't be using VideoCapture() for this kind of reading? I don't want to use the older cv commands because I need to be using the NumPy arrays that cv2 uses.

** Edit ** Ok, found a reasonable solution. I found that at least one other person has had a similar issue in Mac OS: OpenCV capture loops video/Does not detect last frame

My guess is that there is an issue either with the OpenCV setup we have in Mac OS or with the file format somehow.

The correct solution generally is to check the type of the returned frame, so this should work:

    cam = cv2.VideoCapture(sourceVideo)            
    while True:
        ret, img = cam.read()                      
        if (type(img) == type(None)):
            break

        cv2.imshow('detection', img)
        print ret
        if (0xFF & cv2.waitKey(5) == 27) or img.size == 0:
            break

However, if you're having the problem I am, the final frame doesn't get detected. At any rate, there is a reasonable workaround, which is to check the number of frames in the file and simply keep track.

    framecount = cam.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT)

    frames = 0
    while frames < framecount:
        ret, img = cam.read()  
        frames += 1     

        if (type(img) == type(None)):
            break

        cv2.imshow('detection', img)
        print ret
        if (0xFF & cv2.waitKey(5) == 27) or img.size == 0:
            break
1

1 Answers

2
votes

Don't forget to add a check if the video file has been successfully opened.

if( !cam.isOpened() )
  throw "Error when reading image file";

Grab frames by piping them into a cv::Mat in your loop

Mat frame;

for( ; ; )
{
    cam >> frame;
    if(!frame)
        break;
    imshow("w", frame);
    waitKey(20);
}

The loop should terminate correctly after frame is false.

In order to also terminate the loop exactly you could do

cam.get(CV_CAP_PROP_FRAME_COUNT)

to get the exact number of frames. Getting the video without loop is not possible as its read in increments. You could wrap this into a separate function though.