Greeting, I was working on a drone project, I wanted to take stream from my drone process it on my laptop and give a command based on processing, I was using the flask framework from the same.
Currently, as the first step I want to take the stream from drone and PUT it to the flask server and view it on the flask website, not doing the processing part right now.
I PUT the video to server after compressing it into jpg and using base 64 to encode it and then finally use json.dumps()
and then requests.put()
it.
On the server side in flask server program I get its using request.json
, use json.loads()
, but I am not clear what to do next.
I am not experienced enough with flask, web development and with limited experience and knowledge made the programs, but it returns error 405 on the flask program.
Here are the programs
flask server
import base64 import json from flask import Flask, make_response, render_template, request app = Flask(__name__) def getFrames(img): pass @app.route('/video', methods=['POST', 'GET']) def video(): if request.method == 'PUT': load = json.loads(request.json) imdata = base64.b64decode(load['image']) respose = make_response(imdata.tobytes()) return respose @app.route('/') def index(): return render_template('index.html') @app.route('/cmd') def cmd(): pass if __name__ == "__main__": app.run(debug=True)
index.html
<!DOCTYPE html> <html> <head> <title>Video Stream</title> </head> <body> <h1> Live Stream </h1> <div> <img src="{{ url_for('video') }}" width="50%"> </div> </body> </html>
drone program
import base64 import json import requests import cv2 cap = cv2.VideoCapture(1) ip = '' #url returned by the flask program while True: success, img = cap.read() cv2.imshow("OUTPUT", img) _, imdata = cv2.imencode('.JPG', img) jStr = json.dumps({"image": base64.b64encode(imdata).decode('ascii')}) requests.put(url=(ip + '/video'), data=jStr) if cv2.waitKey(1) == 27: break
Any help is highly appreciated!!!
Advertisement
Answer
You don’t have to convert to base64
and use JSON. it can be simpler and faster to send JPG directly as raw bytes
And to make it simpler I would use /upload
to send image from drone to server, and /video
to send image to users.
import requests import cv2 cap = cv2.VideoCapture(0) while True: success, img = cap.read() if success: cv2.imshow("OUTPUT", img) _, imdata = cv2.imencode('.JPG', img) print('.', end='', flush=True) requests.put('http://127.0.0.1:5000/upload', data=imdata.tobytes()) # 40ms = 25 frames per second (1000ms/40ms), # 1000ms = 1 frame per second (1000ms/1000ms) # but this will work only when `imshow()` is used. # Without `imshow()` it will need `time.sleep(0.04)` or `time.sleep(1)` if cv2.waitKey(40) == 27: # 40ms = 25 frames per second (1000ms/40ms) break cv2.destroyAllWindows() cap.release()
Now flask. This part is not complete.
It gets image from drone and keep in global variable. And when user open page then it loads single image from /video
from flask import Flask, make_response, render_template, request app = Flask(__name__) frame = None # global variable to keep single JPG @app.route('/upload', methods=['PUT']) def upload(): global frame # keep jpg data in global variable frame = request.data return "OK" @app.route('/video') def video(): if frame: return make_response(frame) else: return "" @app.route('/') def index(): return 'image:<br><img src="/video">' if __name__ == "__main__": app.run(debug=True)
At this moment it can display only one static image. It needs to send it as motion-jpeg
EDIT:
Version which sends motion-jpeg
so you see video.
It works correctly with Chrome
, Microsoft Edge
and Brave
(all use chrome engine).
Problem makes Firefox
. It hangs and tries to load image all time. I don’t know what is the real problem but if I add time.sleep()
then it can solve problem.
from flask import Flask, Response, render_template_string, request import time app = Flask(__name__) frame = None # global variable to keep single JPG, # at start you could assign bytes from empty JPG @app.route('/upload', methods=['PUT']) def upload(): global frame # keep jpg data in global variable frame = request.data return "OK" def gen(): while True: yield (b'--framern' b'Content-Type: image/jpegrn' b'rn' + frame + b'rn') time.sleep(0.04) # my Firefox needs some time to display image / Chrome displays image without it # 0.04s = 40ms = 25 frames per second @app.route('/video') def video(): if frame: # if you use `boundary=other_name` then you have to yield `b--other_namern` return Response(gen(), mimetype='multipart/x-mixed-replace; boundary=frame') else: return "" @app.route('/') def index(): return 'image:<br><img src="/video">' #return render_template_string('image:<br><img src="{{ url_for("video") }}">') if __name__ == "__main__": app.run(debug=True)#, use_reloader=False)
Server may runs users in separated threads on processes and sometimes it may not share frame
between users. If I use use_reloader=False
then I can stop sending to /upload
and this stops video in browser, and later I can start again sending to /upload
and browser again displays stream (without reloading page). Without use_reloader=False
browser doesn’t restart video and it needs to reload page. Maybe it will need to use flask.g
to keep frame. Or /upload
will have to save frame in file or database and /video
will have to read frame from file or database.