1 | initial version |
I'm sorry for long but done too fast answer... I hope it's useful.
Your code looks OK (even if should be cleaned), I think your issue is coming from the camera. More in details, maybe your camera has some automatic exposure adjustment. When low light condition AUTO_EXPOSURE increases shutter time or camera gain.
Because, in any case your frame rate is max(wantedFps,1/shutter_time)
if camera increases shutter time the fps might decrease.
You can easily test this case, pointing a light in front of your camera so it will become saturated. If I'm right your frame rate should increase.
Not all web cams has exposure control. You can test it using CV_CAP_PROP_AUTO_EXPOSURE and/or CV_CAP_PROP_EXPOSURE
It should be (my change from cams and driver)
cap.set(CV_CAP_PROP_AUTO_EXPOSURE,-1);
cap.set(CV_CAP_PROP_EXPOSURE,divider);
where divider reduce in some way the max shutter time. Maybe 0 uses max shuttercap.set(CV_CAP_PROP_SETTINGS, -1);
this will pop up a window from driver Remember that using VideoCapture cap1(CV_CAP_DSHOW + device);
you will can the DirectShow driver. DSHOW gives you more control on capture controls param. (Some CV_CAP_PROPs doesn't work with default VFW driver).
At the end keep in mind that the calling cap >> frame
you will wait for an available frame. You wont to be surprised if loop time for
while(1){
cap >> frame;
if (waitKey(5) >= 0)
break;
}
has same duration of simpler
while(1){
cap >> frame;
}
This is because 5ms is short time, in the mean time the camera is preparing the frame so you can spend time to do something (as is in 1st case) instead of waiting for the camera (as in in 2nd case). Exactly you can do something up to 1/fps seconds without loosing !
If you spend more time to do things the readTime will reduce because the image is always ready. This doesn't mean that cam is grabbing faster but just you are loosing frames
here is my function to measure loopTime and readTime. In my case they are both fixed at 30fps
int MeasureFps(int device = CV_CAP_DSHOW + 0)
{
Mat frame;
cout << "Opening device " << to_string(device) <<"..."<< endl;
VideoCapture cap(device);
if (!cap.isOpened())
{
cout << "Problem opening streams!" << endl;
return -1;
}
int maxFrames = 100;
cout << "Wait while grabbing " << to_string(maxFrames) << "frames..." << endl;
double capprop;
capprop = cap.get(CV_CAP_PROP_AUTO_EXPOSURE);
capprop = cap.get(CV_CAP_PROP_EXPOSURE);
cap.set(CV_CAP_PROP_AUTO_EXPOSURE,-1); //disable. doesn't works for all cams
cap.set(CV_CAP_PROP_EXPOSURE,0); //default. doesn't works for all cams
cap.set(CV_CAP_PROP_FRAME_WIDTH, 640);
cap.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
cap.set(CV_CAP_PROP_FPS, 300000.0); //very high to catch the maximum
cap.set(CV_CAP_PROP_SETTINGS, -1); //pop up the driver window
cap >> frame; //grab 1st frame to initialize all chain
int64 t0, t1, r0, readTicks;
int tickFreq = cv::getTickFrequency();
int numberOfFrames = 0;
readTicks = 0;
t0 = cv::getTickCount();
while (numberOfFrames<maxFrames)
{
r0 = cv::getTickCount();
cap >> frame;
readTicks += (cv::getTickCount() - r0);
numberOfFrames++;
/*
if (frame.empty()) continue;
imshow("frame", frame);
*/
if (waitKey(5) >= 0)
break;
}
t1 = cv::getTickCount();
double loopTime = (double)(t1 - t0) / tickFreq; // seconds
double averageLoopTime = loopTime / numberOfFrames; // seconds
cout << "Loop time: "
<< averageLoopTime * 1000 << "ms"
<< " ~= " << cvRound(1 / averageLoopTime) << "fps" << endl;
double readTime = (double)readTicks / tickFreq; // seconds;
double averageReadTime = readTime / numberOfFrames; // seconds
cout << "Read time: "
<< averageReadTime * 1000 << "ms" << endl;
cap.release();
PRESS_ENTER;
return 0;
}