Streaming video from Raspberry Pi on web server to multiple users - flask

I have a Raspberry Pi, using Wifi, it is running a people counting model, and will send the processed image to my server using a ZeroMQ socket. On this server I build a web server, using Flask. I got an error that is not shown during the streaming for the first person who accessed the website, but the next access following the first one will fail:
zmq.error.ZMQError: Address in use
What should i do to more people access my server and can see video streaming?
The server code :
from flask import Flask, render_template, Response, request, jsonify
from flask_restful import Api
import numpy as np
from api.configs import configs
from flask_cors import CORS
import zmq
import cv2
import base64
app = Flask(__name__, static_url_path='/static')
api_restful = Api(app)
cors = CORS(app)
def gen():
context = zmq.Context()
footage_socket = context.socket(zmq.SUB)
footage_socket.bind('tcp://*:{}'.format(configs.SOCKET_PORT))
footage_socket.setsockopt_string(zmq.SUBSCRIBE, np.unicode(''))
while True:
frame = footage_socket.recv()
npimg = np.fromstring(frame, dtype=np.uint8)
source = cv2.imdecode(npimg, 1)
s = cv2.imencode('.jpg', source)[1].tobytes()
yield ( b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + s + b'\r\n' )
#app.route('/video_feed')
def video_feed():
return Response( gen(),
mimetype = 'multipart/x-mixed-replace; boundary=frame'
)
if __name__ == '__main__':
app.run( debug = True,
threaded = True,
port = configs.PORT,
host = configs.HOST
)
The Raspberry Pi client code :
import socket
import logging as log
import numpy as np
from api.configs import configs
import zmq
import numpy as np
import cv2
context = zmq.Context()
footage_socket = context.socket(zmq.PUB)
footage_socket.connect( 'tcp://{}:{}'.format( configs.SERVER_HOST,
configs.SOCKET_PORT )
)
time.sleep(1)
cap = cv2.VideoCapture(0)
while True:
while cap.isOpened():
ret, frame = cap.read()
frame = cv2.resize(frame, (640, 480))
encoded, buffer_ = cv2.imencode('.jpg', frame)
footage_socket.send(buffer_)
I omitted some code to make it easier to see

I cannot speak about the details hidden inside the Flask() web-served video re-wrapping/delivery, yet the ZeroMQ part seems to be the trouble, so :
a )it seems that every web-watcher spawns another #app.route( '/video_feed' )-decorated Flask()-instance ( check the mode - if just a thread-based, or a process-based - it is important, whether a central Context() instance may or cannot get shared and efficiently re-used ).
b ) the code, residing inside there hooked Response()-feeder, attempts to .bind() for each subsequent Flask()-served user, which for obvious reasons collides, as the resource ( the address:port ) has been POSACK'ed for exclusive use for the first-came, first served visitor ( any next one must crash and crashed as documented above ).
PROFESSIONAL SOLUTION ( or just a quick-but-dirty-fix ? ):
For indeed small scale use-cases, it shall be enough to reverse the .bind()/.connect()-s, i.e. the PUB will .bind() ( as it does not replicate itself and can serve any small counts of further SUB(s), arriving via .connect()-s )
However, this quick-fix is a bit dirty. This should never be done in a production-grade system. The workload envelope is soon to grow above reasonable levels. The proper architecture should work in a different manner.
instantiate a central zmq.Context( configs.nIOthreads )-instance ( could be shared to re-use in called methods ), having thus also a central scaling of the nIOthreads for performance reasons.
instantiate a one, central SUB-instance, that communicates with Rpi and collects video-frames, to avoid any re-duplications of image/serving
configure both sides to use .setsockopt( zmq.CONFLATE ) to principally avoid any re-broadcasting of a video "History of No Value"
instantiate all the #app-decorated Flask()-tools to re-use the central Context()-instance and to internally connect to a central video-re-PUB-lisher, using a SUB-side of another PUB/SUB archetype, here, using an efficient memory-mapped inproc://, again using .setsockopt( zmq.CONFLATE )
configure all resources to have higher robustness - at least with an explicit .setsockopt( zmq.LINGER, 0 )
detect and handle all error-states, as the ZeroMQ API documents all of them well enough to help you diagnose + manage any failed state professionally and safely

Related

How to import the same flask limiter in a structured flask app

I'm trying to organize my Flask app, as it's getting quite big in length at close to 1000 lines
I am trying to separate the REST API from my main app, by using the approach shown here: https://flask-restx.readthedocs.io/en/latest/scaling.html#multiple-apis-with-reusable-namespaces
What remains in my main.py is something like
from apiv1 import blueprint as api1
REST_API = Flask(__name__)
REST_API.wsgi_app = ProxyFix(REST_API.wsgi_app, x_for=1)
REST_API.register_blueprint(api1)
However in my app, I am using the flask limiter
# Very basic DOS prevention
try:
limiter = Limiter(
REST_API,
key_func=get_remote_address,
storage_uri="redis://localhost:6379/1",
# storage_options={"connect_timeout": 30},
strategy="fixed-window", # or "moving-window"
default_limits=["90 per minute"]
)
# Allow local workatation run
except:
limiter = Limiter(
REST_API,
key_func=get_remote_address,
default_limits=["90 per minute"]
)
This is likewise placed in a decorator to my various API functions
decorators = [limiter.limit("30/minute")]
def post(self, server_id = ''):
# [..]
Now that I am splitting my REST api from the same file that declaring my endpoints, I don't know how to pass its object. The REST_API var exists only in my main.py
How should I handle passing the limiter variable, or any other global objects for that matter?
I worked for a few hours yesterday but I finally understood the pythonic way to do this sort of thing.
I just couldn't wrap my head around how imports function so I was struggling with questions like "how do I pass the variable during import" etc.
Finally it clicked for me that I need to follow a "pull" method with my imports, instead of trying to push variables into them. I.e. I setup the center location in my package's __init__ which will import my logger module, and then my other modules will import THAT logger variable from there.
So in my app's __init__, I have
from .limiter import limiter
And in the app/apis/v1.py I have
from .. import limiter
And this seems to finally work. I don't know if this is the expected way, meaning to play with relative module paths, so if there;s a more elegant way, please let me know

How to load and use word2vec model properly in a web-application via Flask RESTful APIs?

I built a small code to find analogies using word2vec and it runs fine as stand alone application. Here is the working code
import numpy as np
# Get the interactive Tools for Matplotlib
%matplotlib notebook
from gensim.test.utils import datapath, get_tmpfile
from gensim.models import KeyedVectors
from gensim.scripts.glove2word2vec import glove2word2vec
import os
glove_file = os.path.abspath('glove.6B/glove.6B.100d.txt')
word2vec_glove_file = get_tmpfile("glove.6B.100d.word2vec.txt")
glove2word2vec(glove_file, word2vec_glove_file)
model = KeyedVectors.load_word2vec_format(word2vec_glove_file)
def analogy(x1, x2, y1):
result = model.most_similar(positive=[y1, x2], negative=[x1])
return result[0][0]
analogy('woman', 'queen', 'man')
Now, I plan to use flask to create a small web application, so that users can find analogies via the webpage. For this I have a basic question
I assume I need to save the model and then load it when I start the server. Please correct me I am I am wrong.
Here is the code that using Flask, it is working, but can you please suggest if saving model is required here?
2. Any suggestions to improve this code are welcome!
import numpy as np
from gensim.test.utils import datapath, get_tmpfile
from gensim.models import KeyedVectors
from gensim.scripts.glove2word2vec import glove2word2vec
import os
from flask import Flask, request
app = Flask(__name__)
#app.route("/", methods=['GET'])
def welcome():
return "Welcome to our Machine Learning REST API!"
#app.route("/analogy", methods=['GET'])
def analogy_route():
word1 = request.args.get("word1")
word2 = request.args.get("word2")
word3 = request.args.get("word3")
result = model.most_similar(positive=[word3, word2], negative=[word1])
return str(result[0][0])
if __name__ == "__main__":
glove_file = os.path.abspath('glove.6B/glove.6B.100d.txt')
word2vec_glove_file = get_tmpfile("glove.6B.100d.word2vec.txt")
glove2word2vec(glove_file, word2vec_glove_file)
model = KeyedVectors.load_word2vec_format(word2vec_glove_file)
app.run(host='0.0.0.0', port=5000, debug=True)
You probably don't want to be doing the GLoVe-to-word2vec format conversion, into a temporary file, every time you start your service. (It probably takes a noticeable amount of time, and may be filling a temp directory with redundant copies of the same data.)
Instead, perform the conversion only once, into a non-temporary location. Then, ignore the original glove.6B.100d.txt file entirely – it's no longer needed. Instead, just ensure the converted file is available to your web service in a stable location.
Very roughly, that means:
Run once, anywhere:
glove2word2vec('glove.6B/glove.6B.100d.txt', `glove.6B.100d.word2vec.txt`)
(Note that neither the use of absfile() for get_tmpfile() are strictly necessary – you can supply string paths directly to the glove2word2vec() function.)
Ensure that the new file glove.6B.100d.word2vec.txt is available in the working directory of your web service.
Have your web service's __main__ branch just load the already-converted file, avoiding redundant repeated conversion work:
if __name__ == "__main__":
model = KeyedVectors.load_word2vec_format('glove.6B.100d.word2vec.txt')
app.run(host='0.0.0.0', port=5000, debug=True)
(The exact path 'glove.6B.100d.word2vec.txt' might be slightly different depending on where you choose to place the full file.)

Is there a user-controlled way to refresh data in a Dash app?

I have a Dash app that makes some graphs based on data drawn from an API, and I'd like to give the user an option to change a parameter, pull new data based on this, and then redraw the graphs. It could be through a form, but I figured the simplest method would be to use the <pathname> route system from Flask. Dash allows me to do this:
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
app = dash.Dash(__name__)
app.layout = html.Div(children=[
dcc.Location(id='url', refresh=False),
html.Div(id='page-content'),
])
#app.callback(dash.dependencies.Output('page-content', 'children'),
[dash.dependencies.Input('url', 'pathname')])
def display_page(pathname):
if pathname == '/':
return html.Div('Please append a pathname to the route')
else:
data = get_data_from_api(int(pathname))
fig_1 = px.line(data, x="time", y="price")
fig_2 = px.line(data, x="time", y="popularity")
return html.Div(children=[
dcc.Graph(id='fig_1',figure=fig_1),
dcc.Graph(id='fig_2',figure=fig_2),
])
if __name__ == '__main__':
app.run_server(debug=True)
But the problem is that the API call takes a minute or two, and it seems to be constantly polling it, such that the request times out and and the graphs never redraw. What I need is something which doesn't auto-refresh, which can run the API call, update the underlying data, and then tell the app to refresh its state.
I did consider a Dash-within-Flask hybrid like this, but it seems excessively complicated for my use-case. Is there a simpler way to do this?
I think you can add a html.Button to your layout.
html.Button('Update', id='update-button')
To your Callback you can add:
#app.callback(dash.dependencies.Output('page-content', 'children'),
[dash.dependencies.Input('url', 'pathname'),
dash.dependencies.Input('update-button', 'n_clicks')])
def display_page(pathname, n_clicks):
....
No need to process the variabel n_clicks in anyway. The Callback is always triggerd.
Cheers

Multiple Greenlets in a loop and ZMQ. Greenlet blocks in a first _run

I wrote two types of greenlets. MyGreenletPUB will publish message via ZMQ with message type 1 and message type 2.
MyGreenletSUB instances will subscribe to ZMQ PUB, based on parameter ( "1" and "2" ).
Problem here is that when I start my Greenlets run method in MyGreenletSUB code will stop on message = sock.recv() and will never return run time execution to other greenlets.
My question is how can I avoid this and how can I start my greenlets asynchronous with a while TRUE, without using gevent.sleep() in while methods to switch execution between greenlets
from gevent.monkey import patch_all
patch_all()
import zmq
import time
import gevent
from gevent import Greenlet
class MyGreenletPUB(Greenlet):
def _run(self):
# ZeroMQ Context
context = zmq.Context()
# Define the socket using the "Context"
sock = context.socket(zmq.PUB)
sock.bind("tcp://127.0.0.1:5680")
id = 0
while True:
gevent.sleep(1)
id, now = id + 1, time.ctime()
# Message [prefix][message]
message = "1#".format(id=id, time=now)
sock.send(message)
# Message [prefix][message]
message = "2#".format(id=id, time=now)
sock.send(message)
id += 1
class MyGreenletSUB(Greenlet):
def __init__(self, b):
Greenlet.__init__(self)
self.b = b
def _run(self):
context = zmq.Context()
# Define the socket using the "Context"
sock = context.socket(zmq.SUB)
# Define subscription and messages with prefix to accept.
sock.setsockopt(zmq.SUBSCRIBE, self.b)
sock.connect("tcp://127.0.0.1:5680")
while True:
message = sock.recv()
print message
g = MyGreenletPUB.spawn()
g2 = MyGreenletSUB.spawn("1")
g3 = MyGreenletSUB.spawn("2")
try:
gevent.joinall([g, g2, g3])
except KeyboardInterrupt:
print "Exiting"
A default ZeroMQ .recv() method modus operandi is to block until there has arrived anything, that will pass to the hands of the .recv() method caller.
For indeed smart, non-blocking agents, always use rather .poll() instance-methods and .recv( zmq.NOBLOCK ).
Beware, that ZeroMQ subscription is based on topic-filter matching from left and may get issues if mixed unicode and non-unicode strings are being distributed / collected at the same time.
Also, mixing several event-loops might become a bit tricky, depends on your control-needs. I personally always prefer non-blocking systems, even at a cost of more complex design efforts.

Do not reload all Flask apps

I'm using flask blueprints just as written here:
http://flask.pocoo.org/docs/0.12/patterns/appdispatch/
Specifically, I'm running the following piece of code:
from werkzeug.wsgi import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend
application = DispatcherMiddleware(frontend, {
'/backend': backend
})
Flask has an awesome feature of reloading application as it detects change in code. However, in my case it is not desirable for both apps: backend app is too heavy to be reloaded every few minutes. When I reload fronend, backend gets reloaded as well and it takes too long. Is there any way to reload only app that was changed?
I realize it might be difficult, because as far as I understand at this point
application = DispatcherMiddleware(frontend, {
'/backend': backend
})
applications are merged into a single one and they are not separate anymore.
Anyway, maybe there is a different way to achieve what I want?
Thanks.
I am not sure if there is anything direct for doing such thing. Only thing I can think if you preventing a reinitialization of your module.
See a below example of how one can achieve this
reloadme.py
import sys
if 'reloadme' in sys.modules and hasattr(sys.modules['reloadme'], '__MODULE_LOADED'):
print ("module is being reloaded")
mod = sys.modules['reloadme']
x = mod.x
else:
print ("Loading module for first time")
__MODULE_LOADED = True
x = 2
def printx():
print (x)
reloadtest.py
from importlib import reload
import reloadme
reloadme.printx()
reloadme.x = 10
reloadme.printx()
print("Reloading reloadme to check if x is changed back to 2")
reload(reloadme)
reloadme.printx()
The output of running reloadtest.py will be as below
Loading module for first time
2
10
Reloading reloadme to check if x is changed back to 2
Reloading module
10