From ffeb4caca13c1880bc0df0fd45408dd31492d951 Mon Sep 17 00:00:00 2001 From: Ahmet Uludag <ahmet.uludag.net@gmail.com> Date: Fri, 6 Mar 2020 09:50:15 +0300 Subject: [PATCH] Added example for web streaming images processed by OpenCV --- docs/examples/web_streaming_opencv.py | 94 +++++++++++++++++++++++++++ docs/recipes2.rst | 12 ++++ 2 files changed, 106 insertions(+) create mode 100644 docs/examples/web_streaming_opencv.py diff --git a/docs/examples/web_streaming_opencv.py b/docs/examples/web_streaming_opencv.py new file mode 100644 index 00000000..7f0bce89 --- /dev/null +++ b/docs/examples/web_streaming_opencv.py @@ -0,0 +1,94 @@ +import io +import picamera +import logging +import socketserver +from threading import Condition +from http import server +import cv2, numpy as np +from datetime import datetime + +PAGE="""\ +<html> +<head> +<title>picamera MJPEG streaming demo</title> +</head> +<body> +<h1>PiCamera MJPEG Streaming Demo</h1> +<img src="stream.mjpg" width="640" height="480" /> +</body> +</html> +""" + +class StreamingOutput(object): + def __init__(self): + self.frame = None + self.buffer = io.BytesIO() + self.condition = Condition() + + def write(self, buf): + if buf: + # New frame, copy the existing buffer's content and notify all + # clients it's available + self.buffer.truncate() + with self.condition: + self.frame = self.buffer.getvalue() + if self.frame: + frame_cv = np.frombuffer(self.frame, np.uint8).reshape((480, 640, 3)) + cv2.putText(frame_cv, str(datetime.now()), (0, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 0), 1, cv2.LINE_AA) + (flag, self.frame) = cv2.imencode('.jpg', frame_cv) + self.condition.notify_all() + self.buffer.seek(0) + return self.buffer.write(buf) + +class StreamingHandler(server.BaseHTTPRequestHandler): + def do_GET(self): + if self.path == '/': + self.send_response(301) + self.send_header('Location', '/index.html') + self.end_headers() + elif self.path == '/index.html': + content = PAGE.encode('utf-8') + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.send_header('Content-Length', len(content)) + self.end_headers() + self.wfile.write(content) + elif self.path == '/stream.mjpg': + self.send_response(200) + self.send_header('Age', 0) + self.send_header('Cache-Control', 'no-cache, private') + self.send_header('Pragma', 'no-cache') + self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=FRAME') + self.end_headers() + try: + while True: + with output.condition: + output.condition.wait() + frame = output.frame + self.wfile.write(b'--FRAME\r\n') + self.send_header('Content-Type', 'image/jpeg') + self.send_header('Content-Length', len(frame)) + self.end_headers() + self.wfile.write(frame) + self.wfile.write(b'\r\n') + except Exception as e: + logging.warning( + 'Removed streaming client %s: %s', + self.client_address, str(e)) + else: + self.send_error(404) + self.end_headers() + +class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer): + allow_reuse_address = True + daemon_threads = True + +with picamera.PiCamera(resolution='640x480', framerate=24) as camera: + output = StreamingOutput() + camera.start_recording(output, format='bgr') # capture unencoded bgr image as it will be encoded by opencv + try: + address = ('', 8000) + server = StreamingServer(address, StreamingHandler) + server.serve_forever() + finally: + camera.stop_recording() diff --git a/docs/recipes2.rst b/docs/recipes2.rst index 0c2378b9..de9a2b5c 100644 --- a/docs/recipes2.rst +++ b/docs/recipes2.rst @@ -492,6 +492,18 @@ web-browser to view the video stream. ``SimpleHTTPServer`` in Python 2.x) +.. _web_streaming_opencv: + +Web streaming and OpenCV +============= + +Similar to the previous example, but this time frames are processed and +encoded by OpenCV. So frames are captured as unencoded bgr instead of mjpeg. +This example will display current time at the top left corner: + +.. literalinclude:: examples/web_streaming_opencv.py + + .. _record_and_capture: Capturing images whilst recording