Is there an OpenCV function that allows me to grab still images at a specific frame rate. Like I can tell this function to grab 5 images at 10fps, it will take 5 images 0.1 seconds apart exactly.
If not what is a good way to achieve this? My current attempt is to constantly grab images and only save when time is 0.1 seconds after previous frame but not accurate 10fps
afterNextFrame = False
while x < 20:
now = time.monotonic()
if now >= nextFrame:
afterNextFrame = True
if afterNextFrame == True:
cameraCap.grab()
print("\nNow: ", now, "\n")
_, frame = cameraCap.retrieve()
# save frame here
nextFrame += 0.1 # wait 0.1 second for 10 fps
afterNextFrame = False
Related
In my model, I am saving results from numerous Parameter Variation runs in a Histogram Data object.
Here are my Histogram Data settings:
Number of intervals: 7
Value range:
Automatically detected
Initial Interval Size: 10
I then print out these results using the following :
//if final replication, write Histogram Data into Excel
if(getCurrentReplication() == lastReplication){
double intervalWidth = histogramData.getIntervalWidth();
int intervalQty = histogramData.getNumberOfIntervals();
for(int i = 0; i < intervalQty; i++){
traceln(intervalWidth*i + " " + histogramData.getPDF(i));
excelRecords.setCellValue(String.valueOf(intervalWidth*i) + " - " + String.valueOf(intervalWidth*(i+1)), 1, rowIndex, columnIndex);
excelRecords.setCellValue(histogramData.getPDF(i), 1, rowIndex, columnIndex+1);
rowIndex++;
}
}
Example of my intended results:
10 - 80%
20 - 10%
30 - 5%
40 - 2%
50...
60...
Actual results:
0.0 0.0
10.0 0.0
20.0 0.0
30.0 0.998782775272379
40.0 0.0011174522089635631
50.0 9.9772518657461E-5
60.0 0.0
Results after settings initial interval size to 0.1:
0.0 0.9974651710510558
4.0 0.001117719851502934
8.0 9.181270208774101E-4
12.0 2.3951139675062872E-4
16.0 1.5967426450041916E-4
20.0 9.979641531276197E-5
24.0 0.0
How would I go about obtaining my desired results? Am I fundamentally misunderstanding something about the HistogramData object?
Thank you for your help.
The function you are using (getPDF(i)) returns value for the interval in fractions (not in percentages). So, you have to multiply the value by 100 in order to get it as a percentage. As for histogram bars, model analyze the results, specified interval numbers and interval size. After that, it will build the respective number of bars that cover all results. In your case, intervals from 0 to 30 do not provide any results and bars are not presented (PDF here is 0.0).
I tried using schedule. It works fine but the viewfinder of the webcam is stuck at the initial state so it produces only one image multiple times.
Any help?
import cv2
import time
import schedule
cam = cv2.VideoCapture(0)
cv2.namedWindow("Webcam")
img_counter = 0
def capture():
global img_counter
img_name = "opencv_frame_{}.png".format(img_counter)
cv2.imwrite(img_name, frame)
print("screenshot taken")
img_counter += 1
while True:
ret, frame = cam.read()
if not ret:
print("failed to grab frame")
break
cv2.imshow("test", frame)
k = cv2.waitKey(1)
if k % 256 == 27:
print("closing the app")
break
else:
schedule.every(5).seconds.do(capture)
while 1:
schedule.run_pending()
time.sleep(1)
cam.release()
cam.destroyAllWindows()
You are suffering from buffering. OpenCV VideoCapture() reads a few frames into a buffer - I think it is 5 but have not checked for a while and it may differ between platforms or versions.
There are a few possible work-arounds depending on your situation:
call read() 4-5 times when you want a frame - it will only take a couple of hundred milliseconds
call grab() either repeatedly in another thread or just before you want a frame
reduce the size of the buffer so it can only hold a single frame.
Sorry for the somewhat woolly answer as I am not set up to test more at the moment.
I think your code could be simplified to something like this while still retaining a near-realtime preview of the periodically saved images.
import cv2
import schedule
cam = cv2.VideoCapture(0)
cv2.namedWindow("Webcam")
img_counter = 0
def capture():
global img_counter
img_name = "opencv_frame_{}.png".format(img_counter)
cv2.imwrite(img_name, frame)
print("screenshot taken")
img_counter += 1
# Set up schedule before loop
schedule.every(5).seconds.do(capture)
while True:
ret, frame = cam.read()
if not ret:
print("failed to grab frame")
break
cv2.imshow("test", frame)
schedule.run_pending()
k = cv2.waitKey(100) # 1/10 sec delay; no need for separate sleep
if k % 256 == 27:
print("closing the app")
break
cam.release()
cam.destroyAllWindows()
I am using the following code to read each frame from an HLS stream (I invoke init_camera, in the following, with an HLS URL).
I simply call read_camera_frame in a while loop (as fast as I can, since I believe the VideoCapture read should block and return frames at a rate that corresponds to the FPS of the video stream).
def init_camera(camera_id):
return cv2.VideoCapture(camera_id)
self.camera_cap = init_camera(self.image_info.get_camera_id())
def read_camera_frame(self):
syst = time.time_ns()
time_since_last_pub = (syst - self.last_pub_time)/1000000000
time_since_last_stat = (syst - self.last_stat_time)/1000000000
if time_since_last_stat > self.stat_report_interval:
fps = self.frames_collected_since_last_report/self.stat_report_interval
self.logger.info(f"Total Frames: {self.frame_cnt}"
f"Total Discards: {self.frame_discard_cnt}"
f" Frames Since Last Report: {self.frames_collected_since_last_report} "
f" FPS: {fps} "
)
self.frames_collected_since_last_report = 0
self.last_stat_time = syst
self.logger.info(f"CameraReader: read a frame {self.frame_cnt}")
ret, img = self.camera_cap.read()
if ret:
self.frame_cnt += 1
self.frames_collected_since_last_report += 1
ts = self.min_frame_pub_interval - time_since_last_pub
if ts > 0:
self.frame_discard_cnt += 1
return []
self.last_pub_time = syst
return [(img, [copy.deepcopy(self.image_info)])]
raise CameraReaderException("Failed To Read Frame")
The FPS for the video I am playing is just about 30.
fps = self.camera_cap.get(cv2.CAP_PROP_FPS)
self.logger.info(f"Source FPS {fps}")
yet I see frames read at around 130 per second.
Why is the VideoCapture read returning frames roughly 4 times faster than I expect?
I thought the VideoCapture read would read frames at the FPS for the video.
I am working on face recognition which involves object detection too. I am using Yolov5 for object detection and Facenet for face recognition. I am getting very low fps (~0.400) which makes the task laggy. So how do I limit the fps for first N frames for few preliminary tasks and then instead of 30 frames per second I want to take only 1 frame per second for recognition task?
I tried using cap.set(cv2.CAP_PROP_FPS, 5) but I get an error saying 'Can't grab a frame.'
with tf.Graph().as_default():
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.6)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False))
with sess.as_default():
pnet, rnet, onet = detect_face.create_mtcnn(sess, './models/')
minsize = 20 # minimum size of face
threshold = [0.6, 0.7, 0.7] # three steps's threshold
factor = 0.709 # scale factor
margin = 44
frame_interval = 3
batch_size = 1000
image_size = 182
input_image_size = 160
print('Loading feature extraction model')
modeldir = './models/'
facenet.load_model(modeldir)
images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0")
embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0")
phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")
embedding_size = embeddings.get_shape()[1]
classifier_filename = './myclassifier/my_classifier.pkl'
classifier_filename_exp = os.path.expanduser(classifier_filename)
with open(classifier_filename_exp, 'rb') as infile:
(model, class_names) = pickle.load(infile)
print('load classifier file-> %s' % type(class_names))
HumanNames = class_names
video_capture = cv2.VideoCapture(0)
c = 0
print('Start!')
prevTime = 0
FPSLimit = 10
StartTime = time.time()
while True:
ret, frame = video_capture.read()
# frame = cv2.resize(frame, (0,0), fx=0.5, fy=0.5) #resize frame (optional)
curTime = time.time() # calcq fps
timeF = frame_interval
#if int(curTime - StartTime) > FPSLimit:
if (c % timeF == 0):
DETECTION TASK
if nrof_faces > 0:
OBJECT DETECTION TASKS
RECOGNITION TASK
I tried to add this as a comment, but it got too long. This isn't exactly an answer to your question, but it might help for an approach. I would start with identifying the delay by measuring the milliseconds for these tasks: DETECTION TASK, OBJECT DETECTION TASKS, RECOGNITION TASK in your code above.
You should be able to do detection in real-time, or at least 5-10 FPS or better, which may suit your needs. If recognition is your bottleneck, I would do that on another thread. That works because you don't need to detect the same face over and over. If you have 30 FPS and the same face in the frame for 5 seconds, then only perform recognition on that face once, not 5x30 times.
Use multi object tracking to track objects (faces) across frames without having to perform face recognition on each one. This tracking algorithm is easy to implement and works fast. So keep track of objects across frames, then submit for recognition only once per track - and do that on another thread.
I have an iPhone camera attachment that captures video at 9FPS and makes it available as individual UIImages. I'm trying to stitch these images together to create a timelapse video of what the camera sees using AVFoundation.
I'm not sure how to properly convert frames and timing to achieve the time compression I want.
For example - I want 1 hour of real life footage to be converted into 1 minute of time lapse. This tells me that I need to capture every 60th frame and append it to timelapse.
Does the code below accomplish 60 seconds to 1 second time lapse conversion? Or do I need to add some more multiplication/division by kRecordingFPS?
#define kRecordingFPS 9
#define kTimelapseCaptureFrameWithMod 60
//frameCount is the number of frames that the camera has output so far
if(frameCount % kTimelapseCaptureFrameWithMod == 0)
{
//...
//convert image and prepare it for recording
[self appendImage:image
atTime:CMTimeMake(currentRecordingFrameNumber++, kRecordingFPS)];
}
Your code will make 1 frame out of 60 go in your film every 1/9s, and increase the frameIndex by one each time.
The result should be a film of [max value for frameCount] / (60 * 9). If you have 32400 frames (1h of film at 9fps), the film will be of {540:9 = 60s}.
Try printing the CMTime using CMTimeShow() at every call to appendImage:atTime: to check.
//at 5 fps, 300 frames recorded per 60 seconds
//to compress 300 frames into 1 second at 5 fps, we need to take every 300/5 = 60th frame
//to compress 300 frames into 1 second at 15 fps, take every 300/15 = 20th frame
//to compress 300 frames into 1 sec at 30 fps, take every 300/30 = 10th frame
#define kReceivedFPS 9
#define kStoreFPS 9
#define kSecondsPerSecondCompression 60
#define kTimelapseCaptureFrameWithMod (kSecondsPerSecondCompression * kReceivedFPS) / kStoreFPS