Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

cv::VideoCapture::read returns the frame as soon as it's available. With a simple loop grab+process you'll achieve exactly what you are looking for. Short hints:

  • If your processing time is always faster than 1/FPS than a simple grab+process loop works fine without threads too.
  • If sometime your processing time is slower than 1/FPS than you can use a grabbing thread, a processing thread and a shared queue (check here for an example)
  • If your processing time is always slower than 1/FPS than you have to reduce FPS or improve processing performance.

Details:

On windows, cv::VideoCapture uses internal callback with DSHOW, VFW and Microsoft Media Foundation. This means that cv::VideoCapture::read waits for the next frame as provided camera FPS. It waits (in idle state) for a signal from the callback function. Check the code for DSHOW

Duration of cv::VideoCapture::read can change from close to zero (if a frame is ready) up to 1/FPS (if a frame has just been grabbed).

Results from the code below shows that loop time (grab+process) will not change if processing time < 1/FPS... when processing time increases, grabbing time decreases because grabbing will waits less time for a new frame:

Setting wanted fps to 25.0
Measuring real fps...: 19.2fps
    Max processing time: 1/19.2 = 52.1ms

Start processing time tests...
Processing: 0.0ms   Grabbing: 46.8ms    Total: 46.8ms
Processing: 10.0ms  Grabbing: 35.4ms    Total: 45.4ms
Processing: 20.0ms  Grabbing: 26.8ms    Total: 46.8ms
Processing: 30.0ms  Grabbing: 16.8ms    Total: 46.8ms
Processing: 40.0ms  Grabbing: 6.6ms     Total: 46.6ms
Processing: 50.0ms  Grabbing: 4.1ms     Total: 54.1ms

when processing > 1/fps :

Processing: 60.0ms  Grabbing: 3.6ms Total: 63.6ms  <- we are loosing frames

The test code:

#define SimulateProcessing(_processingTimeMs)  \
    int64 _start= getTickCount();  \
    int64 _end = _start + _processingTimeMs * getTickFrequency() / 1000.;  \
    while (getTickCount() <= _end) {double d = 2*CV_PI;} 

int Main_GrabbingMeasureTimes()
{
    int camId = 0;                //the default camera
    //int driver = CV_CAP_VFW;    //use VFW driver
    //int driver = CV_CAP_MSMF;   //use MSMF driver
    int driver = CV_CAP_DSHOW;    //use DSHOW
    double fps = 25.0;            //wanted fps
    cv::Mat frame;
    int64 grabStart, grabSum, procStart, procSum;
    int cnt, maxCnt = 50;
    double procTestTime,procTestTimeMax;
    double procTime, grabTime;
    std::clog.precision(1);
    clog.setf(std::ios::fixed, std::ios::floatfield);

    //OPEN THE CAMERA
    VideoCapture cap(camId + driver);
    if (!cap.isOpened()) return -1;
    cap >> frame;                    //grab to prepare memory

    //--------------
    //MEASURE REAL FPS
    clog << endl << "Setting wanted fps to " << fps;
    cap.set(CV_CAP_PROP_FPS, fps);   //set grabbing fps
    fps = cap.get(CV_CAP_PROP_FPS);  //read fps from cam
    clog << endl << "Measuring real fps...";
    for (cnt = 0,grabSum = 0; cnt < maxCnt; cnt++) {
        grabStart = getTickCount();
        cap >> frame;
        grabSum += (getTickCount() - grabStart);
    }
    procTestTimeMax = 1000.0 * grabSum / getTickFrequency() / cnt;
    clog << ": " << 1000. / procTestTimeMax << "fps" << endl
        << "\tMax processing time: " << procTestTimeMax << "ms" << endl;

    //--------------------------------------------
    //MEASURE LOOP DURATION VS PROCESSING DURATION
    clog << "\nStart processing time tests...\n";
    //same loop with increasing processing time
    for (procTestTime = 0; procTestTime < procTestTimeMax + 10; procTestTime +=10) {
        //N frames for average
        procSum  = 0;
        for (cnt = 0,grabSum = 0; cnt < maxCnt; cnt++) {
            grabStart = getTickCount();
            cap >> frame;
            grabSum += (getTickCount() - grabStart);

            procStart = getTickCount();
            SimulateProcessing(procTestTime);
            procSum += (getTickCount() - procStart);
        }
        grabTime = 1000.0 * grabSum / getTickFrequency() / cnt;
        procTime = 1000.0 * procSum / getTickFrequency() / cnt;
        clog << "Processing: " << procTime << "ms"
            << "\tGrabbing: " << grabTime << "ms"
            << "\tTotal: " << grabTime + procTime << "ms";

        if (procTime > procTestTimeMax)
            clog << "  <- we are loosing frames";
        clog << endl;
    }
    cout << endl << "Press Enter to terminate "; cin.get();
    return 0;
}