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