Timer Camera X で遊ぼう

動画ストリーミングを M5Stack の LCD に表示させる

まずは Timer Camera X に Web カメラのサンプルを書き込みます。

サーバーが起動したら、M5Stack に以下のコードを書き込みます。

loop() で API エンドポイント /capture から静止画(JPEG形式)を取得し、バイト配列に変換して M5.Lcd.drawJpg() に渡します。この処理を繰り返し続けると動画として表示されます。

#include <M5Stack.h>
#include <WiFi.h>
#include <HTTPClient.h>
#define min(a, b) ((a) < (b) ? (a) : (b))

const char* ssid = "***";
const char* password = "***";
const char* url = "http://192.168.11.7/capture";

void setup() {
    M5.begin();
    M5.Lcd.setBrightness(100);
    WiFi.begin(ssid, password);
}

void loop() {
    if (WiFi.status() == WL_CONNECTED) {
        HTTPClient http;
        http.begin(url);

        int httpCode = http.GET();
        Serial.printf("[HTTP] GET... code: %d\n", httpCode);

        if (httpCode == HTTP_CODE_OK) {
            // get the size of the message body
            int len = http.getSize();

            // create buffer for read
            uint8_t buff[len] = { 0 };

            // get tcp stream
            WiFiClient *stream = http.getStreamPtr();

            uint8_t *p = buff;
            int size_read = 0;
            while (http.connected() && (len > size_read || len == -1)) {
                // get available data size
                size_t size = stream->available();

                if (size) {
                    // write data to buffer
                    int c = stream->readBytes(p, min(size, sizeof(buff)));
                    p += c;
                    size_read += c;
                }
            }
            M5.Lcd.drawJpg(buff, len);
        } else {
            Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
        }
        http.end();
    } else {
        Serial.println("WiFi connection failed.");
    }
}

OpenCV で顔検出を行う

OpenCV の Haar Cascade を利用しています。

顔検出用の分類器 haarcascade_frontalface_default.xmlここ から取得できます。

import cv2
import requests
import numpy as np

url = 'http://192.168.11.7/capture'
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

cv2.namedWindow("Camera", cv2.WINDOW_AUTOSIZE)

while True:
    r = requests.get(url)

    # convert bytes to numpy array
    x = np.array(bytearray(r.content), dtype=np.uint8)

    # decode image from numpy array
    frame = cv2.imdecode(x, cv2.IMREAD_COLOR)

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    faces = face_cascade.detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=5,
        minSize=(100, 100),
        flags=cv2.CASCADE_SCALE_IMAGE
    )

    for (x, y, w, h) in faces:
        # draw a rectangle around the face
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
        # display text
        cv2.putText(frame, "Face", (int(x), y - int(h / 5)),
                    cv2.FONT_HERSHEY_PLAIN, int(w / 50),
                    (0, 255, 0), 3, cv2.LINE_AA)

    cv2.imshow('Camera', frame)

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

cv2.destroyAllWindows()