Eduardo Speroni
Eduardo Speroni

Reputation: 341

OpenCV grab does nothing on webcam

I've been trying to simultaneously grab frames from two different cameras, but no matter how many times I call VideoCapture::grab(), there seems to be no effect. The frame retrieved using VideoCapture::retrieve() is always the first frame captured from the last VideoCapture::retrieve().

I've tested it on both OpenCV 2.4 and 3.1, with a Logitech C920 camera on windows.

Example:

VideoCapture vCapture;
Mat imgResult;
vCapture.open(0); //at this point, there is a green sheet in front of the camera
Sleep(100000); //change green sheet with red sheet
vCapture.grab(); //returns true
vCapture.retrieve(imgResult); //image with green sheet is retrieved
Sleep(100000); //change red sheet with blue sheet
vCapture.retrieve(imgResult); //red sheet is retrieved

I've also tried:

for(int i = 0; i < 1000; i++){
    vCapture.grab();
} //takes almost no processing time, like an empty for
vCapture.retrieve(imgResult); //same as before

Retrieve always returns true and retrieves a frame, even if no grab was called since opening vCapture.

My current solution has been retrieving the frame twice (multi-threaded) to ensure the latest frame, but it isn't reliable enough to sync both cameras. Can anyone shed some light on how to force the camera to grab the current frame?

Thanks!

Edit:

A variation of the first example, for clarity:

VideoCapture vCapture;
Mat imgResult;
vCapture.open(0); //at this point, there is a green sheet in front of the camera
vCapture.retrieve(imgResult); //image with green sheet is retrieved
Sleep(100000); //change green sheet with red sheet
vCapture.grab(); //returns true
vCapture.retrieve(imgResult); //green sheet is retrieved
vCapture.retrieve(imgResult); //red sheet is retrieved
Sleep(100000); //change red sheet with blue sheet
vCapture.retrieve(imgResult); //red sheet is retrieved
vCapture.retrieve(imgResult); //blue sheet is retrieved

Expected behavior:

VideoCapture vCapture;
Mat imgResult;
vCapture.open(0); //at this point, there is a green sheet in front of the camera
vCapture.retrieve(imgResult); //error or image with green sheet is retrieved
Sleep(100000); //change green sheet with red sheet
vCapture.grab(); //returns true
vCapture.retrieve(imgResult); //red sheet is retrieved

Per OpenCV documentation:

VideoCapture::grab: The methods/functions grab the next frame from video file or camera and return true (non-zero) in the case of success.

VideoCapture::retrieve: The methods/functions decode and return the just grabbed frame. If no frames has been grabbed (camera has been disconnected, or there are no more frames in video file), the methods return false and the functions return NULL pointer.

Upvotes: 1

Views: 3087

Answers (2)

freeliver
freeliver

Reputation: 1

I ran my test and note very strange behavior of .garab and .retrive functions. This is example:

cv::Mat input = cv::Mat(512, 512, CV_8UC1, cv::Scalar(0));
cv::VideoCapture cap(0);

while (true)
{
    cap.grab();
    cap.retrieve(input, 5);
    cv::imshow("input", input);
    cv::waitKey(0);
}

If you press any key slowly, about every 5 seconds, and change something in front of the camera between pressing, the position of the object on the image will change every second image showing, that is, every second call of the .grab and .retrive functions.

If you press any key quickly, about every 1 seconds, and also change something in front of the camera between pressing, the position of the object on the image will be changed every image showing.

This circumstance tell about that this function can be used to sync cameras.

Upvotes: 0

Micka
Micka

Reputation: 20160

Please try this code with the following instructions:

  1. before and while you start the program, hold a red sheet in front of the camera. That moment, the first .grab will be called.

  2. Once you see the black window popping up, remove the red sheet and hold a blue sheet or something else (except the red or the green sheet) in front of the camera. Then press keyboard key 'q'.

  3. Now you have 5 seconds time to change the scene again. Hold hold the green sheet in front of the camera and wait. The black window will be switched to one of your camera images.

    int main(int argc, char* argv[])
    {
        cv::Mat input = cv::Mat(512,512,CV_8UC1, cv::Scalar(0));
    
        cv::VideoCapture cap(0);
    
    
        while (cv::waitKey(10) != 'q')
        {
            cap.grab();
            cv::imshow("input", input);
        }
        cv::waitKey(5000);
        cap.retrieve(input);
    
        cv::imshow("input", input);
        cv::waitKey(0);
        return 0;
    }
    

3 possible results:

  1. you see the red sheet: this means that the first grab was called and fixed the image, until a retrieve was called.

  2. you see blue sheet: this means every .grab call "removes" one image and the camera will capture a new image on the next call of .grab

  3. you see the green sheet: this means your .retrieve doesn't need a .grab at all and just grabs images automatically.

For me, result 1 occurs, so you can't grab and grab and grab and just .retrieve the last image.


Test 2: control about everything:

  • looks like you are right, on my machine no matter when or how often I call grab, the next image will be the one captured at the time when I called the previous .retrieve and the calls of .grab don't seem to influence the time position of capturing at all.

Would be very interesting whether the same behaviour occurs for different (all) kind of cameras and operating systems.

I've tested on the internal camera of a T450s and Windows 7.

int main(int argc, char* argv[])
{
    cv::Mat input = cv::Mat(512,512,CV_8UC1, cv::Scalar(0));

    cv::VideoCapture cap(0);

    bool grabbed;
    bool retrieved;

    while (true)
    {
        char w = cv::waitKey(0);
        switch (w)
        {
        case 'q':  return 0;
        case 27:  return 0;
        case ' ': retrieved = cap.retrieve(input); break;
        case 'p': grabbed = cap.grab(); break;
        }

        cv::imshow("input", input);
    }

    return 0;
}

In addition, this simple code seems to be off 1 frame for my camera (which therefore probably has a buffersize of 2??):

while (true)
    {
        cap >> input;

        cv::imshow("input", input);
        cv::waitKey(0);
    }

Upvotes: 2

Related Questions