started working on vision package
This commit is contained in:
parent
ce1c82a192
commit
b06962e955
2 changed files with 150 additions and 0 deletions
|
@ -20,3 +20,9 @@ pip install sphinx-rtd-theme
|
|||
|
||||
[Inline documentation example](https://pythonhosted.org/an_example_pypi_project/sphinx.html#full-code-example)
|
||||
[reStructured Text](https://pythonhosted.org/an_example_pypi_project/sphinx.html#restructured-text-rest-resources)
|
||||
|
||||
# Stream Video
|
||||
|
||||
```
|
||||
sudo raspivid -t 0 -b 10000000 -w 1920 -h 1080 -fps 30 -n -o - | gst-launch-1.0 fdsrc ! video/x-h264,width=1280,height=720,framerate=30/1,noise-reduction=1,profile=high,stream-format=byte-stream ! h264parse ! queue ! flvmux streamable=true ! rtmpsink location=\"rtmp://10.1.1.68/live/stream\"```
|
||||
```
|
||||
|
|
144
compLib/Vision.py
Normal file
144
compLib/Vision.py
Normal file
|
@ -0,0 +1,144 @@
|
|||
import os
|
||||
import threading
|
||||
|
||||
import cv2
|
||||
from flask import Flask, Response
|
||||
|
||||
RTMP_SERVER = os.getenv("RTMP_SERVER", "rtmp://localhost/live/stream")
|
||||
SERVE_VIDEO = os.getenv("SERVER_SRC", "/live")
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
HTML = """
|
||||
<html>
|
||||
<head>
|
||||
<title>Opencv Output</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Opencv Output</h1>
|
||||
<img src="{{ VIDEO_DST }}">
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
# it would be better to use jinja2 here, but I don't want to blow up the package dependencies...
|
||||
HTML = HTML.replace("{{ VIDEO_DST }}", SERVE_VIDEO)
|
||||
|
||||
|
||||
class __Streaming:
|
||||
"""
|
||||
Private class for opencv stuff.
|
||||
|
||||
grab frames -> do your own processing -> publish frame -> view on http server
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Create instance of __Streaming class
|
||||
|
||||
This is done implicitly when importing the vision module and will only fail if you would
|
||||
create an object of this class. (There can (SHOULD!) only be one VideCapture)
|
||||
"""
|
||||
self.__camera_stream = cv2.VideoCapture(RTMP_SERVER)
|
||||
#self.__camera_stream = cv2.VideoCapture(0)
|
||||
self.__newest_frame = None
|
||||
self.__lock = threading.Lock()
|
||||
|
||||
def get_frame(self):
|
||||
"""
|
||||
Grab the newest frame from the rtmp stream.
|
||||
|
||||
:return: An opencv frame
|
||||
"""
|
||||
ret, img16 = self.__camera_stream.read()
|
||||
return img16
|
||||
|
||||
def publish_frame(self, image):
|
||||
"""
|
||||
Publish an opencv frame to the http webserver.
|
||||
|
||||
:param image: Opencv frame that will be published
|
||||
:return: None
|
||||
"""
|
||||
with self.__lock:
|
||||
self.__newest_frame = image.copy()
|
||||
|
||||
def _newest_frame_generator(self):
|
||||
"""
|
||||
Private generator which is called directly from flask server.
|
||||
|
||||
:return: Yields image/jpeg encoded frames published from publish_frame function.
|
||||
"""
|
||||
while True:
|
||||
# use a buffer frame to copy the newest frame with lock and then freeing it immediately
|
||||
buffer_frame = None
|
||||
with self.__lock:
|
||||
if self.__newest_frame is None:
|
||||
continue
|
||||
|
||||
buffer_frame = self.__newest_frame.copy()
|
||||
|
||||
# encode frame for jpeg stream
|
||||
(flag, encodedImage) = cv2.imencode(".jpg", buffer_frame)
|
||||
|
||||
# if there was an error try again with the next frame
|
||||
if not flag:
|
||||
continue
|
||||
|
||||
# else yield encoded frame with mimetype image/jpeg
|
||||
yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' +
|
||||
bytearray(encodedImage) + b'\r\n')
|
||||
|
||||
|
||||
# instantiate private class __Streaming
|
||||
Streaming = __Streaming()
|
||||
|
||||
|
||||
@app.route("/live")
|
||||
def video_feed():
|
||||
"""
|
||||
Define route for serving jpeg stream.
|
||||
|
||||
:return: Return the response generated along with the specific media.
|
||||
"""
|
||||
return Response(Streaming._newest_frame_generator(),
|
||||
mimetype="multipart/x-mixed-replace; boundary=frame")
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def __index():
|
||||
"""
|
||||
Define route for serving a static http site to view the stream.
|
||||
|
||||
:return: Static html page
|
||||
"""
|
||||
return HTML
|
||||
|
||||
|
||||
def __start_flask():
|
||||
"""
|
||||
Function for running flask server in a thread.
|
||||
|
||||
:return:
|
||||
"""
|
||||
app.run(host="0.0.0.0", port=9898, debug=True, threaded=True, use_reloader=False)
|
||||
|
||||
|
||||
# start flask service in the background
|
||||
__webserver_thread = threading.Thread(target=__start_flask)
|
||||
__webserver_thread.start()
|
||||
|
||||
# for debugging and testing start processing frames and detecting a 6 by 9 calibration chessboard
|
||||
if __name__ == '__main__':
|
||||
while True:
|
||||
frame = Streaming.get_frame()
|
||||
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
|
||||
|
||||
# processing
|
||||
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
# find the chessboard corners
|
||||
ret, corners = cv2.findChessboardCorners(gray, (6, 9), None)
|
||||
|
||||
cv2.drawChessboardCorners(frame, (6, 9), corners, ret)
|
||||
Streaming.publish_frame(frame)
|
Reference in a new issue