Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added an example for web streaming images processed by OpenCV #615

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions docs/examples/web_streaming_opencv.py
Original file line number Diff line number Diff line change
@@ -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()
12 changes: 12 additions & 0 deletions docs/recipes2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down