Reputation: 359
Desired output
Input: Camera feed using OpenCV or from a REST camera url. (Not a concern for this question)
Output: Streaming jpeg images after doing some OpenCV processing
So far I have done the following based on Falcon
tutorial
Input: Image file as a POST request
Output: GET request endpoint with the path to the image
import mimetypes
import os
import re
import uuid
import cv2
import io
import falcon
from falcon import media
import json
import msgpack
class Collection(object):
def __init__(self, image_store):
self._image_store = image_store
def on_get(self, req, resp):
# TODO: Modify this to return a list of href's based on
# what images are actually available.
doc = {
'images': [
{
'href': '/images/1eaf6ef1-7f2d-4ecc-a8d5-6e8adba7cc0e.png'
}
]
}
resp.data = msgpack.packb(doc, use_bin_type=True)
resp.content_type = falcon.MEDIA_MSGPACK
resp.status = falcon.HTTP_200
def on_post(self, req, resp):
name = self._image_store.save(req.stream, req.content_type)
# Unnecessary Hack to read the saved file in OpenCV
image = cv2.imread("images/" + name)
new_image = do_something_with_image(image)
_ = cv2.imwrite("images/" + name, new_image)
resp.status = falcon.HTTP_201
resp.location = '/images/' + name
class Item(object):
def __init__(self, image_store):
self._image_store = image_store
def on_get(self, req, resp, name):
resp.content_type = mimetypes.guess_type(name)[0]
resp.stream, resp.stream_len = self._image_store.open(name)
class ImageStore(object):
_CHUNK_SIZE_BYTES = 4096
_IMAGE_NAME_PATTERN = re.compile(
'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.[a-z]{2,4}$'
)
def __init__(self, storage_path, uuidgen=uuid.uuid4, fopen=io.open):
self._storage_path = storage_path
self._uuidgen = uuidgen
self._fopen = fopen
def save(self, image_stream, image_content_type):
ext = mimetypes.guess_extension(image_content_type) # Issue with this code, Not returning the extension so hard coding it in next line
ext = ".jpg"
name = '{uuid}{ext}'.format(uuid=self._uuidgen(), ext=ext)
image_path = os.path.join(self._storage_path, name)
with self._fopen(image_path, 'wb') as image_file:
while True:
chunk = image_stream.read(self._CHUNK_SIZE_BYTES)
if not chunk:
break
image_file.write(chunk)
return name
def open(self, name):
# Always validate untrusted input!
if not self._IMAGE_NAME_PATTERN.match(name):
raise IOError('File not found')
image_path = os.path.join(self._storage_path, name)
stream = self._fopen(image_path, 'rb')
stream_len = os.path.getsize(image_path)
return stream, stream_len
def create_app(image_store):
api = falcon.API()
api.add_route('/images', Collection(image_store))
api.add_route('/images/{name}', Item(image_store))
api.add_sink(handle_404, '')
return api
def get_app():
storage_path = os.environ.get('LOOK_STORAGE_PATH', './images')
image_store = ImageStore(storage_path)
return create_app(image_store)
The response is something like this:
HTTP/1.1 201 Created
Connection: close
Date: Mon, 03 Dec 2018 13:08:14 GMT
Server: gunicorn/19.7.1
content-length: 0
content-type: application/json; charset=UTF-8
location: /images/e69a83ee-b369-47c3-8b1c-60ab7bf875ec.jpg
There are 2 problems with the above code:
So, how can I read req.stream
data as numpy array? And more importantly what changes I need to make to stream images from this service?
P.S. Apologies for a long post
Upvotes: 2
Views: 1142
Reputation: 359
I have found a solution which works perfectly fine. For more on this check out this beautiful code.
def gen(camera):
while True:
image = camera.get_frame()
new_image = do_something_with_image(image)
ret, jpeg = cv2.imencode('.jpg', new_image)
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + jpeg.tobytes() + b'\r\n\r\n')
class StreamResource(object):
def on_get(self, req, resp):
labeled_frame = gen(VideoCamera())
resp.content_type = 'multipart/x-mixed-replace; boundary=frame'
resp.stream = labeled_frame
def get_app():
api = falcon.API()
api.add_route('/feed', StreamResource())
return api
Upvotes: 4