13

I am trying to get the fps from my camera so that I can pass it to the VideoWriter for outputting the video. However, I am getting 0 fps by calling VideoCapture::get(CV_CAP_PROP_FPS) from my camera. If I hardcode it, my video may be too slow or too fast.

#include "opencv2/opencv.hpp"
#include <stdio.h>
#include <stdlib.h>

using namespace std;
using namespace cv;

int main(int argc, char *argv[])
{
    cv::VideoCapture cap;
    int key = 0;

    if(argc > 1){
        cap.open(string(argv[1]));
    }
    else
    {
        cap.open(CV_CAP_ANY);
    }
    if(!cap.isOpened())
    {
        printf("Error: could not load a camera or video.\n");
    }

    Mat frame;
    cap >> frame;
    waitKey(5);

    namedWindow("video", 1);
    double fps = cap.get(CV_CAP_PROP_FPS);
    CvSize size = cvSize((int)cap.get(CV_CAP_PROP_FRAME_WIDTH),(int)cap.get(CV_CAP_PROP_FRAME_HEIGHT));

    int codec = CV_FOURCC('M', 'J', 'P', 'G');
    if(!codec){ waitKey(0); return 0; }
    std::cout << "CODEC: " << codec << std::endl;
    std::cout << "FPS: " << fps << std::endl;
    VideoWriter v("Hello.avi",-1,fps,size);
    while(key != 'q'){
        cap >> frame;
        if(!frame.data)
        {
            printf("Error: no frame data.\n");
            break;
        }
        if(frame.empty()){ break; }
        v << frame;
        imshow("video", frame);
        key = waitKey(5);
    }
    return(0);
}

How can I get VideoCapture::get(CV_CAP_PROP_FPS) to return the right fps or give a fps to the VideoWriter that works universally for all webcams?

Angie Quijano
  • 4,167
  • 3
  • 25
  • 30
swtdrgn
  • 1,154
  • 4
  • 17
  • 49

4 Answers4

4

CV_CAP_PROP_FPS only works on videos as far as I know. If you want to capture video data from a webcam you have to time it correctly yourself. For example use a timer to capture a frame from the webcam every 40ms and then save as 25fps video.

littleimp
  • 1,159
  • 9
  • 13
  • That proposal is almost guaranteed to produce a jerky video, as the captured fps in the video won't match the capture fps of the webcam. – Jose Gómez Dec 23 '15 at 14:48
  • Can you elaborate on that? Why would it produce a jerky video? – littleimp Dec 27 '15 at 12:55
  • 3
    Webcams capture using a fixed or defined framerate. For a given resolution, capture will happen at 15 fps, 25 fps, 60 fps... That depends on the speed of the webcam, bandwidth of the link, driver, and settings. If the framerate of the webcam does not match the frames that you capture, sometimes you may capture the same frame twice, and sometimes you may skip frames, so the movement will not be smooth. – Jose Gómez Dec 27 '15 at 14:37
  • 2
    The way to getting a smooth movement is to set the desired FPS (if allowed by the driver, see my response in http://stackoverflow.com/a/22920585/1816603) and capture at the framerate offered by the webcam, (or a divider of it, e.g. 60 -> 30, 50 -> 25, 30 -> 15). – Jose Gómez Dec 27 '15 at 14:37
  • Ok thank you for your explanation. I still have to manually time the capturing though, right? – littleimp Dec 28 '15 at 09:28
  • What I do is to request the _desired_ framerate. But sometimes the webcam driver will override this and select some other framerate. So, you have to find out the real framerate output by the webcam, because you want the output video to match the source framerate; otherwise the video would be reproduced too fast or too slow. – Jose Gómez Dec 28 '15 at 10:19
2

You can use VideoCapture::set(CV_CAP_PROP_FPS) to set the desired FPS for a webcam. However, you can't use get for some reason.

Note that sometimes the driver will choose a different FPS than what you have requested depending on the limitations of the webcam.

My workaround: capture frames during a few seconds (4 is fine in my tests, with 0.5 seconds of initial delay), and estimate the fps the camera outputs.

Angie Quijano
  • 4,167
  • 3
  • 25
  • 30
Jose Gómez
  • 3,110
  • 2
  • 32
  • 54
1

I've never observed CV_CAP_PROP_FPS to work. I have tried with various flavors of OpenCV 2.4.x (currently 2.4.11) using file inputs.

As a workaround in one scenario, I directly used libavformat (from ffmpeg) to get the frame rate, which I can then use in my other OpenCV code:

static double get_frame_rate(const char *filePath) {
    AVFormatContext *gFormatCtx = avformat_alloc_context();
    av_register_all();

    if (avformat_open_input(&gFormatCtx, filePath, NULL, NULL) != 0) {
        return -1;
    } else if (avformat_find_stream_info(gFormatCtx, NULL) < 0) {
        return -1;
    } 

    for (int i = 0; i < gFormatCtx->nb_streams; i++) {
        if (gFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            AVRational rate = gFormatCtx->streams[i]->avg_frame_rate;
            return (double)av_q2d(rate);
        }
    }

    return -1;
}

Aside from that, undoubtedly one of the slowest possible (although sure to work) methods to get the average fps, would be to step through each frame and divide the current frame number by the current time:

for (;;) {
    currentFrame = cap.get(CV_CAP_PROP_POS_FRAMES);
    currentTime = cap.get(CV_CAP_PROP_POS_MSEC);
    fps = currentFrame / (currentTime / 1000);

    # ... code ...
    # stop this loop when you're satisfied ...
}

You'd probably only want to do the latter if the other methods of directly finding the fps failed, and further, there were no better way to summarily get overall duration and frame count information.

The example above works on a file -- to adapt to a camera, you could use elapsed wallclock time since beginning of capture, instead of getting CV_CAP_PROP_POS_MSEC. Then the average fps for the session would be the elapsed wall clock time divided by the current frame number.

Jameson
  • 6,400
  • 6
  • 32
  • 53
  • Useful information. However, the question referred to (web)cams as input, as opposed to using video files. – Jose Gómez Dec 28 '15 at 10:21
  • @JoseGómez last paragraph not doing it for you, huh? – Jameson Dec 28 '15 at 19:24
  • yeah I missed that one; that sounds reasonable. Basically, what you propose is to calculate the effective framerate, right? That is a similar approach that I ended up going for. Note that I had to start calculating the framerate only after an initial delay, for the numbers to be accurate (probably due to some initialisations). It seems weird that one has to go through so much trouble for something as simple as getting the current framerate set by a video stream from a webcam. – Jose Gómez Dec 28 '15 at 22:52
  • Usually the longest time is waiting for the first frame. I measured it on a few webcams, and if we measure real time (not wall time - CPU time) starting *after* the first frame, the intervals were quite consistent. Also note, that sometimes `cap.set(CV_CAP_PROP_FPS, /*someting*/)` works. E.g. on Linux with V4L2, but only if the camera can physically bring up that many FPS. – Tomasz Gandor Jan 16 '18 at 21:43
-2

For live video from webcam use cap.get(cv2.CAP_PROP_FPS)

Athul Soori
  • 91
  • 1
  • 8