Raspberry PiとOpenCVでWEB監視カメラを作成する
はじめに
Mac環境の記事ですが、Windows環境も同じ手順になります。環境依存の部分は読み替えてお試しください。
目的
ブラウザにストリーミング動画を表示します。
この記事を最後まで読むと、次のことができるようになります。
No. | 概要 | キーワード |
---|---|---|
1 | REST API | Flask |
2 | OpenCV | cv2 |
完成イメージ
ストリーミング |
---|
実行環境
環境 | Ver. |
---|---|
macOS Catalina | 10.15.6 |
Raspberry Pi 4 Model B 4GB RAM | - |
Raspberry Pi OS (Raspbian) | 10 |
Python | 3.7.3 |
Flask | 1.1.2 |
opencv-python | 4.4.0.42 |
ソースコード
実際に実装内容やソースコードを追いながら読むとより理解が深まるかと思います。是非ご活用ください。
関連する記事
- Raspberry PiのセットアップからPython環境のインストールまで
- Raspberry PiとPythonでリモコンカーを作成する
- Raspberry PiとPythonでLCD(16x2)ゲームを作成する
Camera設定
Raspberry Pi Software Configuration Tool起動
command.sh
~$ sudo raspi-config
Camera有効化
-
5 Interfacing Options
を選択 -
P1 Camera
を選択
再起動
command.sh
~$ sudo reboot
OpenCV依存関係
HDF5
HDF5 is a file format and library for storing scientific data.
command.sh
~$ sudo apt-get install -y libhdf5-dev libhdf5-serial-dev libhdf5-103
ATLAS
ATLAS is an approach for the automatic generation and optimization of numerical software.
command.sh
~$ sudo apt-get install -y libatlas-base-dev
JasPer
JasPer is a collection of software (i.e., a library and application programs) for the coding and manipulation of images.
command.sh
~$ sudo apt-get install -y libjasper-dev
ハンズオン
ダウンロード
command.sh
~$ git clone https://github.com/nsuhara/raspi-streaming.git -b master
セットアップ
command.sh
~$ cd raspi-streaming ~$ python -m venv .venv ~$ source .venv/bin/activate ~$ pip install -r requirements.txt ~$ source config
サービス起動
command.sh
~$ flask run --host=0.0.0.0 --port=5000
アクセス
command.sh
~$ open "http://{host}:5000/raspi-streaming/api?process=front_end&request=app_form&secret_key=M7XvWE9fSFg3"
サービス終了
command.sh
~$ Control Key + C
アプリ構成
target.sh
/app ├── __init__.py ├── apis │ ├── templates │ │ └── app_form.html │ └── views │ ├── __init__.py │ ├── back_end_handler.py │ ├── camera.py │ ├── front_end_handler.py │ └── main_handler.py ├── common │ ├── __init__.py │ └── utility.py ├── config │ ├── __init__.py │ ├── localhost.py │ └── production.py └── run.py
front-end
target.sh
/app └── apis ├── templates │ └── app_form.html └── views └── front_end_handler.py
front-end制御
front_end_handler.py
"""app/apis/views/front_end_handler.py """ from flask import jsonify, render_template from app import secret_key def handler(req): """handler """ param1 = req.get('param1') param2 = req.get('param2') if param1 == 'app_form': return _app_form(req=param2) return jsonify({'message': 'no route matched with those values'}), 200 def _app_form(req): """_app_form """ if req.get('secret_key', '') != secret_key: return jsonify({'message': 'no route matched with those values'}), 200 return render_template('app_form.html', secret_key=req.get('secret_key', ''))
front-endレイアウト
app_form.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>raspi-streaming</title> <style type="text/css"> html, body { -webkit-user-select: none; width: 100%; height: 100%; } table { width: 100%; height: 100%; } table, td { border: 1px gray solid; padding: 10px; } img.img-option { width: 100%; /* height: 100%; */ } </style> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script type="text/javascript"> // nothing to do </script> </head> <body> <table> <tr height="5%"> <td> <h1>raspi-streaming</h1> </td> </tr> <tr> <td> <img class="img-option" src="{{ url_for('raspi-streaming.api', process='back_end', request='video_feed', secret_key=secret_key) }}"> </td> </tr> </table> </body> </html>
back-end
target.sh
/app └── apis └── views ├── back_end_handler.py └── camera.py
back-end制御
back_end_handler.py
"""app/apis/views/back_end_handler.py """ from flask import Response, jsonify from app import secret_key from app.apis.views.camera import Camera def handler(req): """handler """ param1 = req.get('param1') param2 = req.get('param2') if param1 == 'video_feed': return _video_feed(req=param2) return jsonify({'message': 'no route matched with those values'}), 200 def _video_feed(req): """_video_feed """ if req.get('secret_key', '') != secret_key: return jsonify({'message': 'no route matched with those values'}), 200 return Response(_generator(Camera()), mimetype='multipart/x-mixed-replace; boundary=frame') def _generator(camera): """_generator """ while True: frame = camera.frame() yield b'--frame\r\n' yield b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n'
カメラ制御
camera.py
"""app/apis/views/camera.py """ import cv2 class Camera(): """Camera """ def __init__(self): self.video_capture = cv2.VideoCapture(-1) def __del__(self): self.video_capture.release() def frame(self): """frame """ _, frame = self.video_capture.read() _, image = cv2.imencode('.jpeg', frame) return image.tobytes()