updated to version 0.0.3
added vision daemon which runs in the background added more documentation to vision module now including opencv dependencies in complib package
This commit is contained in:
parent
b06962e955
commit
77c2354c00
12 changed files with 201 additions and 16 deletions
19
build_deb.sh
19
build_deb.sh
|
@ -9,8 +9,25 @@ fpm -s python --python-bin python3 --python-pip pip3 --python-package-name-prefi
|
|||
--after-install postinstall.sh \
|
||||
--deb-generate-changes \
|
||||
--deb-priority "optional" \
|
||||
--deb-systemd "complib.service" \
|
||||
-d "python3-pip" \
|
||||
-v 0.0.2-8 -t deb setup.py
|
||||
-d "nginx" \
|
||||
-d "libnginx-mod-rtmp" \
|
||||
-d "libsystemd-dev" \
|
||||
-d "python3-systemd" \
|
||||
-d "gstreamer1.0-tools" \
|
||||
-d "gstreamer1.0-plugins-bad" \
|
||||
-d "gstreamer1.0-plugins-base" \
|
||||
-d "gstreamer1.0-plugins-good" \
|
||||
-d "gstreamer1.0-omx-rpi" \
|
||||
-d "gstreamer1.0-omx-rpi-config" \
|
||||
-d "opencv-dev" \
|
||||
-d "opencv-libs" \
|
||||
-d "opencv-licenses" \
|
||||
-d "opencv-main" \
|
||||
-d "opencv-python" \
|
||||
-d "opencv-scripts" \
|
||||
-v 0.0.3-3 -t deb setup.py
|
||||
|
||||
# --deb-changelog changelog \
|
||||
# --deb-upstream-changelog changelog \
|
||||
|
|
|
@ -6,6 +6,7 @@ from flask import Flask, Response
|
|||
|
||||
RTMP_SERVER = os.getenv("RTMP_SERVER", "rtmp://localhost/live/stream")
|
||||
SERVE_VIDEO = os.getenv("SERVER_SRC", "/live")
|
||||
BUILDING_DOCS = os.getenv("BUILDING_DOCS", "false")
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
@ -27,7 +28,12 @@ HTML = HTML.replace("{{ VIDEO_DST }}", SERVE_VIDEO)
|
|||
|
||||
class __Streaming:
|
||||
"""
|
||||
Private class for opencv stuff.
|
||||
Class that handles rtmp streaming for opencv.
|
||||
|
||||
DO NOT CREATE AN INSTANCE OF THIS CLASS YOURSELF!
|
||||
|
||||
This is automatically done when importing this module. Use Vision.Streaming which is
|
||||
an instance of this class!
|
||||
|
||||
grab frames -> do your own processing -> publish frame -> view on http server
|
||||
"""
|
||||
|
@ -40,7 +46,7 @@ class __Streaming:
|
|||
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.__camera_stream = cv2.VideoCapture(0)
|
||||
self.__newest_frame = None
|
||||
self.__lock = threading.Lock()
|
||||
|
||||
|
@ -79,7 +85,7 @@ class __Streaming:
|
|||
buffer_frame = self.__newest_frame.copy()
|
||||
|
||||
# encode frame for jpeg stream
|
||||
(flag, encodedImage) = cv2.imencode(".jpg", buffer_frame)
|
||||
(flag, encoded_image) = cv2.imencode(".jpg", buffer_frame)
|
||||
|
||||
# if there was an error try again with the next frame
|
||||
if not flag:
|
||||
|
@ -87,15 +93,17 @@ class __Streaming:
|
|||
|
||||
# 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')
|
||||
bytearray(encoded_image) + b'\r\n')
|
||||
|
||||
|
||||
# instantiate private class __Streaming
|
||||
Streaming = __Streaming()
|
||||
Streaming = None
|
||||
if BUILDING_DOCS == "false":
|
||||
# instantiate private class __Streaming
|
||||
Streaming = __Streaming()
|
||||
|
||||
|
||||
@app.route("/live")
|
||||
def video_feed():
|
||||
def __video_feed():
|
||||
"""
|
||||
Define route for serving jpeg stream.
|
||||
|
||||
|
@ -124,12 +132,13 @@ def __start_flask():
|
|||
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()
|
||||
if BUILDING_DOCS == "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__':
|
||||
if __name__ == '__main__' and BUILDING_DOCS == "false":
|
||||
while True:
|
||||
frame = Streaming.get_frame()
|
||||
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
|
||||
|
@ -140,5 +149,5 @@ if __name__ == '__main__':
|
|||
# find the chessboard corners
|
||||
ret, corners = cv2.findChessboardCorners(gray, (6, 9), None)
|
||||
|
||||
cv2.drawChessboardCorners(frame, (6, 9), corners, ret)
|
||||
cv2.drawChessboardCorners(frame, (6, 9), corners, ret)
|
||||
Streaming.publish_frame(frame)
|
||||
|
|
20
compLib/VisionDaemon.py
Normal file
20
compLib/VisionDaemon.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
import os
|
||||
|
||||
import systemd.daemon
|
||||
import LogstashLogging
|
||||
from LogstashLogging import logstash_logger
|
||||
import logging
|
||||
|
||||
__run = """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://localhost/live/stream\""""
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
systemd.daemon.notify(systemd.daemon.Notification.READY)
|
||||
except:
|
||||
logstash_logger.warning("Warning, old systemd version detected")
|
||||
systemd.daemon.notify('READY=1')
|
||||
|
||||
logging.info("starting gstreamer background process")
|
||||
os.system(__run)
|
||||
logstash_logger.error("gstreamer stopped...")
|
|
@ -1,4 +1,4 @@
|
|||
__version__ = "0.0.2"
|
||||
__version__ = "0.0.3"
|
||||
|
||||
import compLib.LogstashLogging
|
||||
import logging
|
||||
|
|
11
complib.service
Normal file
11
complib.service
Normal file
|
@ -0,0 +1,11 @@
|
|||
[Unit]
|
||||
Description=Monitoring service
|
||||
[Service]
|
||||
ExecStart=/usr/bin/python3 /usr/local/lib/python3.7/dist-packages/compLib/VisionDaemon.py
|
||||
Environment="debug=False"
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
Type=notify
|
||||
[Install]
|
||||
Alias=complib
|
||||
WantedBy=default.target
|
102
docs/source/lib/Vision.rst
Normal file
102
docs/source/lib/Vision.rst
Normal file
|
@ -0,0 +1,102 @@
|
|||
.. _lib_vision:
|
||||
|
||||
Vision
|
||||
=====
|
||||
|
||||
This module provides an interface for grabbing an rtmp stream and using the images to do some processing in opencv.
|
||||
|
||||
How do I use this module?
|
||||
|
||||
1. Get frames from the raspberry pi camera
|
||||
2. -- here comes your own processing --
|
||||
3. Publish the processed frames on an http server
|
||||
4. You can view the http stream of your processed images in a web browser
|
||||
|
||||
Opencv Stream
|
||||
*************
|
||||
|
||||
Because of the rtmp stream needing to buffer some frames and waiting for P-Frames, importing this module might take up
|
||||
to 5 Seconds.
|
||||
|
||||
.. autoclass:: compLib.Vision.__Streaming
|
||||
:members:
|
||||
|
||||
Examples
|
||||
*********
|
||||
|
||||
Using the Vision Module
|
||||
---------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cv2
|
||||
from compLib import Vision
|
||||
|
||||
# get newest opencv frame from camera
|
||||
frame = Vision.Streaming.get_frame()
|
||||
|
||||
# do some processing with the frame.....
|
||||
|
||||
# publish frame to streaming server
|
||||
Vision.Streaming.publish_frame(frame)
|
||||
|
||||
Connect the raspberry pi to your internet and view the stream at: "http://your_raspi_ip:9898/". This should display
|
||||
your raspberry pi camera. Note: the stream will lag a little bit BUT the processing of the image will be done in
|
||||
realtime.
|
||||
|
||||
The output on the website should show whatever your raspberry pi cam records:
|
||||
|
||||
.. image:: images/opencv_http_stream.png
|
||||
:width: 680
|
||||
:alt: Processed frames from opencv
|
||||
|
||||
|
||||
Chessboard Detection
|
||||
------------------------------------------
|
||||
|
||||
In this example we process the captured stream of images and want to detect chessboards. Run this example and
|
||||
point your raspberry pi camera to a chessboard and it should be detected.
|
||||
|
||||
For testing you can point it at this image:
|
||||
|
||||
.. image:: images/chessboard.jpg
|
||||
:width: 680
|
||||
:alt: Chessboard for opencv processing
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import cv2
|
||||
from compLib import Vision
|
||||
|
||||
# get newest opencv frame from camera
|
||||
frame = Vision.Streaming.get_frame()
|
||||
|
||||
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
|
||||
|
||||
# convert image to grayscale image
|
||||
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
# find the chessboard corners
|
||||
ret, corners = cv2.findChessboardCorners(gray, (6, 9), None)
|
||||
|
||||
# draw detected chessboard position onto the image
|
||||
cv2.drawChessboardCorners(frame, (6, 9), corners, ret)
|
||||
|
||||
# publish frame to streaming server
|
||||
Vision.Streaming.publish_frame(frame)
|
||||
|
||||
Connect the raspberry pi to your internet and view the stream at: "http://your_raspi_ip:9898/".
|
||||
|
||||
The output image should look like this:
|
||||
|
||||
.. image:: images/chessboard_detected.jpg
|
||||
:width: 680
|
||||
:alt: Processed frames from opencv
|
||||
|
||||
Here is a screenshot of the stream website while viewing the chessboard in this documentation.
|
||||
|
||||
.. image:: images/opencv_processed.png
|
||||
:width: 680
|
||||
:alt: Processed frames from opencv
|
||||
|
||||
|
BIN
docs/source/lib/images/chessboard.jpg
Normal file
BIN
docs/source/lib/images/chessboard.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
BIN
docs/source/lib/images/chessboard_detected.jpg
Normal file
BIN
docs/source/lib/images/chessboard_detected.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
BIN
docs/source/lib/images/opencv_http_stream.png
Normal file
BIN
docs/source/lib/images/opencv_http_stream.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 391 KiB |
BIN
docs/source/lib/images/opencv_processed.png
Normal file
BIN
docs/source/lib/images/opencv_processed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 356 KiB |
|
@ -11,4 +11,28 @@ install_package() {
|
|||
|
||||
install_package "smbus"
|
||||
install_package "requests"
|
||||
install_package "python-logstash-async"
|
||||
install_package "python-logstash-async"
|
||||
|
||||
echo "Setting up opencv4"
|
||||
pkg-config --modversion opencv4
|
||||
|
||||
echo "Setting up nginx rtmp server"
|
||||
sudo /etc/init.d/nginx start
|
||||
|
||||
sudo tee /etc/nginx/nginx.conf > /dev/null <<EOT
|
||||
load_module "modules/ngx_rtmp_module.so";
|
||||
worker_processes auto;
|
||||
rtmp_auto_push on;
|
||||
events {}
|
||||
rtmp {
|
||||
server {
|
||||
listen 1935;
|
||||
listen [::]:1935 ipv6only=on;
|
||||
|
||||
application live {
|
||||
live on;
|
||||
record off;
|
||||
}
|
||||
}
|
||||
}
|
||||
EOT
|
|
@ -1,3 +1,4 @@
|
|||
export BUILDING_DOCS=true
|
||||
cd docs || exit
|
||||
rm -rf build
|
||||
rm -rf gh-pages
|
||||
|
@ -18,4 +19,5 @@ git push origin gh-pages
|
|||
cd ..
|
||||
rm -rf gh-pages
|
||||
|
||||
cd ..
|
||||
cd ..
|
||||
export BUILDING_DOCS=false
|
Reference in a new issue