【RaspberryPi Zero W】カメラモジュールからリアルタイムで映像を取得し、動体検知を行う

以下は、Raspberry Piのカメラモジュールからリアルタイムで映像を取得し、動体検知を行うPythonコードの例です。OpenCVを使用します。

from flask import Flask, render_template, Response
import time
import io
import cv2
import numpy as np

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

def gen(camera):
    while True:
        time.sleep(0.1)
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')

@app.route('/video_feed')
def video_feed():
    return Response(gen(Camera()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

class Camera(object):
    def __init__(self):
        # カメラの設定
        self.cap = cv2.VideoCapture(0) # 0は内蔵カメラ、1はUSBカメラ
        self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # フレームの横幅を設定
        self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # フレームの縦幅を設定
        # 動体検知用の背景差分の設定
        self.fgbg = cv2.createBackgroundSubtractorMOG2()

    def __del__(self):
        # 終了処理
        self.cap.release()
        cv2.destroyAllWindows()

    def get_frame(self):
        # カメラからフレームを取得
        ret, frame = self.cap.read()
        # 背景差分による前景領域の抽出
        fgmask = self.fgbg.apply(frame)
        # 前景領域の二値化
        ret,thresh = cv2.threshold(fgmask,127,255,0)
        # ノイズ除去
        kernel = np.ones((3,3),np.uint8)
        thresh = cv2.erode(thresh, kernel, iterations=1)
        thresh = cv2.dilate(thresh, kernel, iterations=1)
        # 輪郭抽出
        contours, hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
        # 輪郭を囲む矩形を描画
        for contour in contours:
            # 輪郭が一定の大きさ以上である場合に描画する
            if cv2.contourArea(contour) > 500:
                (x, y, w, h) = cv2.boundingRect(contour)
                cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
        # フレームをリサイズ
        frame = cv2.resize(frame, (640, 480))
        _, imgencode = cv2.imencode('.jpg', frame)
        return imgencode.tostring()

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=True, threaded=True)

このコードでは、cv2.VideoCapture()を使用して、カメラからリアルタイムで映像を取得し、cv2.createBackgroundSubtractorMOG2()を使用して、前景領域を抽出します。そして、cv2.findContours()を使用して、前景領域の輪郭を抽出し、cv2.rectangle()を使用して、輪郭を囲む矩形を描画します。

また、以下の記事で紹介しているラズパイでのキャプチャ映像をストリーミング配信する方法も加えて、動体検知した様子をそのままストリーミングします。

fclout.hateblo.jp

実物はこんな感じです。