6

I am using cv2.VideoCapture to read the frames of an RTSP video link in a python script. The .read() function is in a while loop which runs once every second, However, I do not get the most current frame from the stream. I get older frames and in this way my lag builds up. Is there anyway that I can get the most current frame and not older frames which have piped into the VideoCapture object?

user3916798
  • 93
  • 1
  • 5
  • Look at the grab method. This should let you skip frames and then use the retrieve command on the frame you want to process and return. – ditkin Oct 19 '15 at 19:55
  • 1
    @ditkin I did, it doesn't really seem to help it still gets me older frames. Thank you for the help though. – user3916798 Nov 15 '15 at 03:27
  • @user3916798 did you ever solve this? Having a similar problem... – Aerophilic Dec 15 '15 at 10:35
  • 2
    Does this answer your question? [OpenCV-Python: How to get latest frame from the live video stream or skip old ones](https://stackoverflow.com/questions/45310718/opencv-python-how-to-get-latest-frame-from-the-live-video-stream-or-skip-old-on) – user202729 Aug 14 '21 at 00:30

6 Answers6

1

I also faced the same problem. Seems that once the VideoCapture object is initialized it keeps storing the frames in some buffer of sort and returns a frame from that for every read operation. What I did is I initialized the VideoCapture object every time I wanted to read a frame and then released the stream. Following code captures 10 images at an interval of 10 seconds and stores them. Same can be done using while(True) in a loop.

for x in range(0,10):
    cap = cv2.VideoCapture(0)
    ret, frame = cap.read()
    cv2.imwrite('test'+str(x)+'.png',frame)
    cap.release()
    time.sleep(10)
novice_91
  • 11
  • 1
1

I've encountered the same problem and found a git repository of Azure samples for their computer vision service. The relevant part is the Camera Capture module, specifically the Video Stream class.

You can see they've implemented a Queue that is being updated to keep only the latest frame:

def update(self):
    try:
        while True:
            if self.stopped:
                return

            if not self.Q.full():
                (grabbed, frame) = self.stream.read()

                # if the `grabbed` boolean is `False`, then we have
                # reached the end of the video file
                if not grabbed:
                    self.stop()
                    return

                self.Q.put(frame)

                # Clean the queue to keep only the latest frame
                while self.Q.qsize() > 1:
                    self.Q.get()
Avihay
  • 63
  • 7
0

I'm working with a friend in a hack doing the same. We don't want to use all the frames. So far we found that very same thing: grab() (or read) tries to get you all the frames, and I guess with rtp: it will maintain a buffer and drop if you're not responsive enough.

Instead of read you can also use grab() and receive(). First one ask for the frame. Receives reads it into memory. So if you call grab several times it will effectively skip those.

We got away with doing this:

#show some initial image
while True:
    cv2.grab()
    if cv2.waitKey(10):
       im = cv2.receive()
       # process
       cv2.imshow...

Not production code but...

helios
  • 13,574
  • 2
  • 45
  • 55
0

Using the following was causing a lot of issues for me. The frames being passed to the function were not sequention.

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    function_that_uses_frame(frame)
    time.sleep(0.5)

The following also didn't work for me as suggested by other comments. I was STILL getting issues with taking the most recent frame.

cap = cv2.VideoCapture(0)

while True:
    ret = capture.grab()
    ret, frame = videocapture.retrieve()
    function_that_uses_frame(frame)
    time.sleep(0.5)

Finally, this worked but it's bloody filthy. I only need to grab a few frames per second, so it will do for the time being. For context, I was using the camera to generate some data for an ML model and my labels compared to what was being captured was out of sync.

while True:
    ret = capture.grab()
    ret, frame = videocapture.retrieve()
    ret = capture.grab()
    ret, frame = videocapture.retrieve()
    function_that_uses_frame(frame)
    time.sleep(0.5)
Alex
  • 126
  • 1
  • 4
0

Inside the 'while' you can use:

while True:
    cap = cv2.VideoCapture()
    urlDir = 'rtsp://ip:port/h264_ulaw.sdp'
    cap.open(urlDir)
    
    # get the current frame
    _,frame = cap.read()
    cap.release() #releasing camera
    image = frame
DavidG
  • 1
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-ask). – Community Sep 15 '21 at 04:21
0

I made an adaptive system as the ones the others on here posted here still resulted in somewhat inaccurate frame representation and have completely variable results depending on the hardware.

from time import time
#...
cap = cv2.VideoCapture(url)
cap_fps = cap.get(cv2.CAP_PROP_FPS)
time_start = time()
time_end = time_start
while True:
   time_difference = int((((end_time-start_time))*cap_fps)+1) #Note that the 1 might be changed to fit script bandwidth
   for i in range(0, time_difference):
      a = cap.grab()
   _, frame = cap.read()
   time_start = time()
   #Put your code here
   variable = function(frame)
   #...
   time_end = time()

This way the skipped frames adapt to the amount of frames missed in the video stream - allowing for a much smoother transition and a relatively real-time frame representation.

Vincent Casey
  • 178
  • 2
  • 11