Setting a frame rate doesn't always work like you expect. It depends on two things:
- What your camera is capable of outputting.
- Whether the current capture backend you're using supports changing frame rates.
So point (1). Your camera will have a list of formats which it is capable of delivering to a capture device (e.g. your computer). This might be 1920x1080 @ 30 fps or 1920x1080 @ 60 fps and it also specifies a pixel format. The vast majority of consumer cameras do not let you change their frame rates with any more granularity than that. And most capture libraries will refuse to change to a capture format that the camera isn't advertising.
Even machine vision cameras, which allow you much more control, typically only offer a selection of frame rates (e.g. 1, 2, 5, 10, 15, 25, 30, etc). If you want a non-supported frame rate at a hardware level, usually the only way to do it is to use hardware triggering.
And point (2). When you use cv.VideoCapture you're really calling a platform-specific library like DirectShow or V4L2. We call this a backend. You can specify exactly which backend is in use by using something like:
cv2.VideoCapture(0 + cv2.CAP_DSHOW)
There are lots of CAP_X's defined, but only some will apply to your platform (e.g CAP_V4L2 is for Linux only). On Windows, forcing the system to use DirectShow is a pretty good bet. However as above, if your camera only reports it can output 30fps and 60fps, requesting 10fps will be meaningless. Worse, a lot of settings simply report True in OpenCV when they're not actually implemented. You've seen that most of the time reading parameters will give you sensible results though, however if the parameter isn't implemented (e.g exposure is a common one that isn't) then you might get nonsense.
You're better off waiting for a period of time and then reading the last image.
Be careful with this strategy. Don't do this:
while capturing:
res, image = cap.read()
time.sleep(1)
you need to make sure you're continually purging the camera's frame buffer otherwise you will start to see lag in your videos. Something like the following should work:
frame_rate = 10
prev = 0
while capturing:
time_elapsed = time.time() - prev
res, image = cap.read()
if time_elapsed > 1./frame_rate:
prev = time.time()
# Do something with your image here.
process_image()
For an application like a hand detector, what works well is to have a thread capturing images and the detector running in another thread (which also controls the GUI). Your detector pulls the last image captured, runs and display the results (you may need to lock access to the image buffer while you're reading/writing it). That way your bottleneck is the detector, not the performance of the camera.
Answer from Josh on Stack Overflowc++ - How to set camera fps in opencv? - Stack Overflow
python - Changing FPS in OpenCV - Stack Overflow
How to modify the FPS of a video when using cv::VideoCapture to read frames - C++ - OpenCV
Increase Camera FPS OpenCV python
Setting a frame rate doesn't always work like you expect. It depends on two things:
- What your camera is capable of outputting.
- Whether the current capture backend you're using supports changing frame rates.
So point (1). Your camera will have a list of formats which it is capable of delivering to a capture device (e.g. your computer). This might be 1920x1080 @ 30 fps or 1920x1080 @ 60 fps and it also specifies a pixel format. The vast majority of consumer cameras do not let you change their frame rates with any more granularity than that. And most capture libraries will refuse to change to a capture format that the camera isn't advertising.
Even machine vision cameras, which allow you much more control, typically only offer a selection of frame rates (e.g. 1, 2, 5, 10, 15, 25, 30, etc). If you want a non-supported frame rate at a hardware level, usually the only way to do it is to use hardware triggering.
And point (2). When you use cv.VideoCapture you're really calling a platform-specific library like DirectShow or V4L2. We call this a backend. You can specify exactly which backend is in use by using something like:
cv2.VideoCapture(0 + cv2.CAP_DSHOW)
There are lots of CAP_X's defined, but only some will apply to your platform (e.g CAP_V4L2 is for Linux only). On Windows, forcing the system to use DirectShow is a pretty good bet. However as above, if your camera only reports it can output 30fps and 60fps, requesting 10fps will be meaningless. Worse, a lot of settings simply report True in OpenCV when they're not actually implemented. You've seen that most of the time reading parameters will give you sensible results though, however if the parameter isn't implemented (e.g exposure is a common one that isn't) then you might get nonsense.
You're better off waiting for a period of time and then reading the last image.
Be careful with this strategy. Don't do this:
while capturing:
res, image = cap.read()
time.sleep(1)
you need to make sure you're continually purging the camera's frame buffer otherwise you will start to see lag in your videos. Something like the following should work:
frame_rate = 10
prev = 0
while capturing:
time_elapsed = time.time() - prev
res, image = cap.read()
if time_elapsed > 1./frame_rate:
prev = time.time()
# Do something with your image here.
process_image()
For an application like a hand detector, what works well is to have a thread capturing images and the detector running in another thread (which also controls the GUI). Your detector pulls the last image captured, runs and display the results (you may need to lock access to the image buffer while you're reading/writing it). That way your bottleneck is the detector, not the performance of the camera.
I could not set the FPS for my camera so I manage to limit the FPS based on time so that only 1 frame per second made it into the rest of my code. It is not exact, but I do not need exact, just a limiter instead of 30fps. HTH
import time
fpsLimit = 1 # throttle limit
startTime = time.time()
cv = cv2.VideoCapture(0)
While True:
frame = cv.read
nowTime = time.time()
if (int(nowTime - startTime)) > fpsLimit:
# do other cv2 stuff....
startTime = time.time() # reset time
When I modify my code like below, it works @ 60 fps.
#include <stack>
#include <iostream>
#include <math.h>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/videoio.hpp"
#include <opencv2/video.hpp>
#include "opencv2/imgcodecs.hpp"
#include <time.h>
using namespace cv;
using namespace std;
int keyboard;
int main(int argc, char** argv)
{
VideoCapture cap(0); //capture the video from web cam
if (!cap.isOpened()) // if not success, exit program
{
cout << "Cannot open the web cam" << endl;
return -1;
}
cap.set(CV_CAP_PROP_FOURCC, CV_FOURCC('M', 'J', 'P', 'G'));
cap.set(CV_CAP_PROP_FRAME_WIDTH, 1280);
cap.set(CV_CAP_PROP_FRAME_HEIGHT, 720);
while ((char)keyboard != 'q' && (char)keyboard != 27)
{
Mat imgOriginal;
Mat ROOI;
clock_t a = clock();
bool bSuccess = cap.read(imgOriginal);
if (!bSuccess)
{
cout << "Cannot read a frame from video stream" << endl;
break;
}
printf("Captue Time : %f\n", double(clock() - a) / double(CLOCKS_PER_SEC));
imshow("Original", imgOriginal);
if (waitKey(1) == 27)
{
cout << "esc key is pressed by user" << endl;
break;
}
}
return 0;
}
The key for camera working @ 60 fps is
cap.set(CV_CAP_PROP_FOURCC, CV_FOURCC('M', 'J', 'P', 'G'));
My camera works @60 fps in MJPG mode. So I add above code, it works fine!
You could try setting the camera's frame rate outside of OpenCV, e.g. on Linux you can control UVC cameras (Logitech, etc.) using libwebcam, and uvcdynctrl in particular.
You set the FPS for the output via the VideoWriter,
but you didn't attempt to set the FPS for the input via the VideoCapture.
In order to do that you can try to call cv2.VideoCapture, with the cv2.CAP_PROP_FPS property after you create cap.
For example:
cap.set(cv2.CAP_PROP_FPS, 3)
However - note that the actual behavior is dependant on the specific capture device you are using. Some support only certain FPSs. See also this post regarding it: change frame rate in opencv 3.4.2.
If it does work you will be able to simplify your code a lot - just capture frames, process them and save (without any manual fps management).
This method programmatically sets frames per second. A 6.1mb file was created when frame rate was set for 30fps, and a 0.9mb file when set for 3fps.
#!/usr/bin/env python3
import cv2
import numpy as np
import time
import datetime
import pathlib
import imutils
cap = cv2.VideoCapture(0)
if (cap.isOpened() == False):
print("Unable to read camera feed")
capture_duration = 15
frame_per_sec = 30
prev = 0
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))
out = cv2.VideoWriter('C:\\videoPy\\LZ\\'outpout.avi',cv2.VideoWriter_fourcc('m','j','p','g'),frame_per_sec, (frame_width,frame_height))
start_time = time.time()
while( int(time.time() - start_time) < capture_duration ):
#start fps
time_elapsed = time.time() - prev
while(time_elapsed > 1./frame_per_sec):
ret, frame = cap.read()
if not ret:
break
if time_elapsed > 1./frame_per_sec:
prev = time.time()
#end fps
if ret==True:
frame = imutils.resize(frame, width=frame_width)
out.write(frame)
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
break
cap.release()
out.release()
cv2.destroyAllWindows()
OpenCV is a heavy weight API and following tips may introduce minor improvements:
you can disable RGB conversion:
cap.set(CV_CAP_PROP_CONVERT_RGB , false);
you can increase frame rate if its default frame rate is low:
cap.set(CV_CAP_PROP_FPS , 60);
I'd suggest to do direct video capture via V4L, since OpenCV may do YUYV to RGB transformations and other stuff that involves floating point calculations, that are expensive on this kind of hardware. We have done many robotics projects on embedded systems and the rule of the thumb is that you will be always better of either directly using V4L or small 3rd party libraries like CMVision (http://www.cs.cmu.edu/~jbruce/cmvision/) to do image processing on embedded systems.