4

I am using OpenCV4 along with python 3 to open a webcam, grab the frames and display them in a window, just like the first code tutorial provided here. However, it takes a different amount of time grabbing different frames: sometimes it takes 0.01 s to grab, and sometimes it takes 0.33 s, which creates lags when showing the frames in the window.

Is there a way to force a constant time when grabbing frames so that i can see the video without lag? I think it is happening with OpenCV because when i use a default windows camera viewer to see the video it displays it normally.

What i already tried is wait for some time using time.sleep() before grabbing a frame again. But this does not help.

import numpy as np
import cv2

cap = cv2.VideoCapture(0)

while(True):
    # Capture frame-by-frame
    ret, frame = cap.read()

    # Display the resulting frame
    cv2.imshow('frame',gray)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
nathancy
  • 42,661
  • 14
  • 115
  • 137
underfloor
  • 301
  • 3
  • 12

3 Answers3

2

One potential way is to set a timestamp within the loop and keep track of the time the last frame was shown. For instance, only once a certain amount of time has elapsed then you show the frame. At the same time, you constantly read frames to keep the buffer empty to ensure that you have the most recent frame. You don't want to use time.sleep() because it will freeze the program and not keep the buffer empty. Once the timestamp hits, you show the frame and reset the timestamp.

import cv2
import time

cap = cv2.VideoCapture(0)

# Timeout to display frames in seconds
# FPS = 1/TIMEOUT 
# So 1/.025 = 40 FPS
TIMEOUT = .025
old_timestamp = time.time()

while(True):
    # Capture frame-by-frame
    ret, frame = cap.read()

    if (time.time() - old_timestamp) > TIMEOUT:
        # Display the resulting frame
        cv2.imshow('frame',frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
        old_timestamp = time.time()

# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
nathancy
  • 42,661
  • 14
  • 115
  • 137
0

When working correctly, this app will be suspended at the read() call until the next frame from the streaming webcam is available. Smooth display depends on being able to execute whatever you may have added to the loop in less than the 1/FPS seconds. It also depends on the camera being UVC compliant and it may depend on the encoding algorithm being MJPEG, which is the case for most webcams. However the fact that you see delay up to 1/3 second is curious because that is a typical GOP period for mpeg or other inter-frame encoders.

If none of the above applies to your case then I suspect the problem is platform related rather than an OCV issue. Have you tried to duplicate the problem on another system?

Roger Levy
  • 61
  • 1
  • 3
-2

I was facing a similar problem, and this is the solution I came up with. This would be the exact way to set a constant fps. This works on both live video and recorded video.

import cv2
import time

cap = cv2.VideoCapture('your video location')

initial_time = time.time()
to_time = time.time()

set_fps = 25 # Set your desired frame rate

# Variables Used to Calculate FPS
prev_frame_time = 0 # Variables Used to Calculate FPS
new_frame_time = 0

while True:
    while_running = time.time() # Keep updating time with each frame

    new_time = while_running - initial_time # If time taken is 1/fps, then read a frame

    if new_time >= 1 / set_fps:
        ret, frame = cap.read()
        if ret:
            # Calculating True FPS
            new_frame_time = time.time()
            fps = 1 / (new_frame_time - prev_frame_time)
            prev_frame_time = new_frame_time
            fps = int(fps)
            fps = str(fps)
            print(fps)

            cv2.imshow('joined', frame)
            initial_time = while_running # Update the initial time with current time

        else:
            total_time_of_video = while_running - to_time # To get the total time of the video
            print(total_time_of_video)
            break

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

cap.release()
cv2.destroyAllWindows()
desertnaut
  • 57,590
  • 26
  • 140
  • 166
  • this is an **improper** approach when dealing with video cameras. those things produce frames regardless of when you read the frames. if you read slower than necessary, frames queue up, causing **latency**. – Christoph Rackwitz Sep 08 '22 at 12:31