Creating a function using yield instead of return to generate frames from an http stream continuously

iterator, opencv, python, python-requests, stream

Solution

Python has a very beautiful concept of `generators`, which may be the thing you are looking for, so basically to fabricate a `generator` we use keyword `yield` instead of `return`. The basic difference between these two keywords is that the `return` statement simply halts the function execution once encountered, while on the other hand `yield` keyword let's the execution to continue and keeps on generating value until alive. It can be visualized in a simple example as:

def sample_function():
    # The body would be replaced by image generating code.
    for i in xrange(20):
        yield i**2

for x in sample_function():
    print x, 
>>> 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361

If We would had reproduce the same scenario using `return` statement then it may look like:

def sample_function(i):
    return i**2

for i in xrange(0, 20):
    print sample_function(i),

So in the above code if you replace the `return` keyword with `yield`, then you can iterate the frames as:

for frame in get_frame_from_stream(r):
    cv2.imshow('i', frame)

There is no need of a `while` loop is this case, unless and until this stream is alive, the method would keep on generating frames.

Problem

I would like to create a function that reads frames from an HTTP stream using requests and returns each frame. But because of the fact that the stream reader is based on an iterator object (if I understand correctly), returning a frame is breaking the stream. The code I am using (works perfectly fine, from this answer): ``` import cv2 import requests import numpy as np r = requests.get('http://roofcam.warwick.ac.uk/cgi-bin/faststream.jpg', stream=True) if(r.status_code == 200): bytes_buffer = bytes() for chunk in r.iter_content(chunk_size=1024): bytes += chunk a = bytes_buffer.find(b'\xff\xd8') b = bytes_buffer.find(b'\xff\xd9') if a != -1 and b != -1: jpg = bytes_buffer[a:b+2] bytes_buffer = bytes_buffer[b+2:] i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8), cv2.IMREAD_COLOR) cv2.imshow('i', i) if cv2.waitKey(1) == 27: exit(0) else: print("Received unexpected status code {}".format(r.status_code)) ``` Concept of what I'd like to do (with the return that I'd like to work if it was a while function instead of a for using iterator): ``` import cv2 import requests import numpy as np r = requests.get('http://roofcam.warwick.ac.uk/cgi-bin/faststream.jpg', stream=True) def get_frame_from_stream(r): if(r.status_code == 200): bytes_buffer = bytes() for chunk in r.iter_content(chunk_size=1024): bytes_buffer += chunk a = bytes_buffer.find(b'\xff\xd8') b = bytes_buffer.find(b'\xff\xd9') if a != -1 and b != -1: jpg = bytes_buffer[a:b + 2] bytes_buffer = bytes_buffer[b + 2:] i = cv2.imdecode(np.fromstring( jpg, dtype=np.uint8), cv2.IMREAD_COLOR) return i else: print("Received unexpected status code {}".format(r.status_code)) return None while True: if img is not None: img = get_frame_from_stream(r) cv2.imshow('i', img) cv2.waitKey(0) else: break ``` So basically I'd like to return each frame where the original code is displaying the frame so that I can perform some processing on it. But I don't understand how exactly is the iter_content allowing the original code to work continuously. (I didn't know how to name the question - will welcome a better title)

Original source

Related problems