【ルネサス技術コラム】Raspberry Pi 5で動かす簡易「笑顔チェック」Webアプリ

Raspberry Pi 5で動かす簡易「笑顔チェック」Webアプリを作ってみた

受付端末やセルフ撮影ブース、会員登録の顔写真確認、プレゼン練習などでは、「今この人が笑顔っぽい状態か」をその場で手軽に表示したい場面があります。そこで今回も、「エレクトロニクスを簡単に」をテーマに、Raspberry Pi 5に接続したUSBカメラ映像を使って笑顔らしさを簡易表示し、同一ネットワーク内のPCブラウザから確認できるWebアプリを、できるだけ短いプログラムで試作してみました。
Raspberry Pi

今回の構成は、人物識別や感情推定ではなく、固定カメラ前提で顔と口元を簡易検出し、笑顔っぽい状態を可視化するPoCに絞っています。この割り切りによって、PoCとして扱いやすく、追加のモデルランタイムにも依存しにくい構成にできます。

笑顔チェックアプリ

実行環境には Raspberry Pi 5を想定しています。Raspberry Pi 5 は Raspberry Pi 公式でも高性能な汎用コンピュータとして案内されており、Raspberry Pi OS と組み合わせることで、軽量なカメラPoCやローカルWebアプリを載せやすいです。Raspberry Pi 5は従来世代比で2~3倍の性能向上が案内されており、USBカメラ入力と軽量な画像処理を同時に動かす用途でも扱いやすいです。

今回使うのは FlaskOpenCVです。顔検出と笑顔検出には OpenCVの CascadeClassifier を使い、公式リポジトリにも含まれている haarcascade_frontalface_default.xmlhaarcascade_smile.xml を利用します。OpenCV 4.5.0以降は Apache 2.0 ライセンスで提供されています。

目次

はじめに

今回のアプリは、Raspberry Pi 5にUSBカメラを接続して固定設置し、そのライブ映像の中から顔を見つけ、口元に笑顔らしい特徴が見つかったら状態表示を切り替える仕組みです。映像処理自体は Raspberry Pi 5側で行い、確認用の画面は同一ネットワーク内のPCブラウザから開く構成にしています。

構成としてはとてもシンプルで、

  • Raspberry Pi 5にUSBカメラを接続する

  • カメラ映像を Raspberry Pi 5側で読み込む

  • 顔を検出する

  • 顔領域の下半分を中心に笑顔らしい口元パターンを検出する

  • SMILE OK / KEEP SMILING / NO FACE を表示する

  • 同一ネットワーク内のPCブラウザで結果を見る

という流れだけに絞っています。

OpenCVのCascade Classifierを選んだ理由

今回は大規模なAIモデルを利用せず、Raspberry Pi単体で手軽に試せることを重視して Haar Cascade を採用しました。

Webアプリの基本構成

今回は Flask ベースのWebアプリとして実装し、ブラウザにはMJPEG ストリームとしてUSBカメラ映像を表示します。Flask では generator を使ったストリーミングレスポンスのパターンが公式ドキュメントで説明されており、継続的にフレームを返す構成を作れます。
Flask Streaming

カメラ入力は OpenCVの VideoCapture を使います。OpenCVのドキュメントでも、VideoCapture はカメラ・動画ファイル・画像列から映像を取得するためのクラスとして説明されています。Raspberry Pi 5 に接続したUSBカメラをここで開き、そのフレームを逐次処理して返すだけでも、PoCとしては十分動く構成にできます。
OpenCV VideoCapture

また、今回は Raspberry Pi 5上でFlask を 0.0.0.0 バインドで起動し、同一ネットワーク内のPCから http://RaspberryPiのIPアドレス:5000 にアクセスする構成を取ります。これにより、処理はPi側、確認はPC側という役割分担にできます。

今回の処理フローは以下の通りです。

  1. Raspberry Pi 5でUSBカメラを起動

  2. 各フレームをグレースケール化

  3. 顔を検出

  4. 顔領域の下半分で笑顔を検出

  5. 検出結果に応じてSMILE OK / KEEP SMILING / NO FACE を表示

  6. 同一ネットワーク内のPCブラウザに映像を表示

ディレクトリ構成

smile-check-pi5-app/
├── app.py
└── requirements.txt

サンプルコード

今回はできるだけ短くするために、HTML も Python 側へ埋め込んだ1ファイル構成にしています。
Raspberry Pi 5にUSBカメラを接続して起動すると、同一ネットワーク内のPCブラウザから映像を見ながら、笑顔らしい状態かどうかを簡易表示できます。
※本サンプルコードは生成AIを活用して作成して、動作確認を行っています。

python

import cv2
from flask import Flask, Response

app = Flask(__name__)

face_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)
smile_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_smile.xml"
)

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)


def gen_frames():
    while True:
        ok, frame = cap.read()
        if not ok or frame is None:
            continue

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

        faces = face_cascade.detectMultiScale(
            gray,
            scaleFactor=1.1,
            minNeighbors=5,
            minSize=(80, 80)
        )

        status = "NO FACE"
        color = (120, 120, 120)

        for (x, y, w, h) in faces[:1]:
            roi_gray = gray[y:y + h, x:x + w]
            mouth_gray = roi_gray[h // 2:h, :]

            smiles = smile_cascade.detectMultiScale(
                mouth_gray,
                scaleFactor=1.7,
                minNeighbors=20,
                minSize=(25, 25)
            )

            if len(smiles) > 0:
                status = "SMILE OK"
                color = (0, 200, 0)
            else:
                status = "KEEP SMILING"
                color = (0, 170, 255)

            cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)

            for (sx, sy, sw, sh) in smiles[:1]:
                cv2.rectangle(
                    frame,
                    (x + sx, y + h // 2 + sy),
                    (x + sx + sw, y + h // 2 + sy + sh),
                    color,
                    2
                )
            break

        cv2.putText(
            frame, status, (20, 40),
            cv2.FONT_HERSHEY_SIMPLEX, 1.0, color, 3, cv2.LINE_AA
        )

        ret, buffer = cv2.imencode(".jpg", frame, [cv2.IMWRITE_JPEG_QUALITY, 85])
        if not ret:
            continue

        yield (
            b"--frame\r\n"
            b"Content-Type: image/jpeg\r\n\r\n" +
            buffer.tobytes() +
            b"\r\n"
        )


@app.route("/")
def index():
    return """<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Raspberry Pi 5 笑顔チェックアプリ</title>
<style>
body { font-family: system-ui, sans-serif; background:#f6f7fb; margin:0; padding:24px; color:#222; }
.wrap { max-width:980px; margin:auto; background:#fff; border-radius: 16px; padding: 24px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); }
h1 { margin-top:0; }
.card { margin:16px 0; padding: 16px; border:1px solid #e5e7eb; border-radius: 12px; background:#fafafa; }
img { width: 100%; max-width: 900px; border-radius: 12px; border:1px solid #ddd; display:block; }
.muted { color: #555; font-size:14px; }
</style>
</head>
<body>
<div class="wrap">
<h1>Raspberry Pi 5 笑顔チェックアプリ</h1>
<p>USBカメラ映像から笑顔らしい状態を簡易表示し、同一ネットワーク内のPCブラウザから確認します。</p>
<div class="card">
<p class="muted">※ このPoCは人物識別や感情断定ではなく、顔と口元の簡易検出によるデモです。</p>
</div>
<div class="card">
<h3>USBカメラ映像</h3>
<img src="/video_feed" alt="camera stream">
</div>
</div>
</body>
</html>"""


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


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=False)

requirements.txt

Flask>=3.1,<4
opencv-python-headless>=4.10,<5

セットアップ手順

まずは Raspberry Pi 5Raspberry Pi OS を入れて起動しておきます。Raspberry Pi 公式でも、Raspberry Pi OS は公式サポートOSとして案内されています。
Raspberry Pi OS

その後、Raspberry Pi 5上で仮想環境を作り、依存ライブラリをインストールします。

bash

mkdir smile-check-pi5-app
cd smile-check-pi5-app
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip setuptools wheel
python -m pip install -r requirements.txt

今回は OpenCVの Haar Cascade XMLcv2.data.haarcascades から参照するため、追加のモデルファイルを別途ダウンロードしなくても動かしやすい構成です。OpenCVの公式リポジトリには顔用・笑顔用を含む複数の cascade XML が含まれています。
OpenCV Haar Cascades

app.py を保存したら、以下で起動します。

bash

python app.py

同一ネットワーク内のPCブラウザから以下へアクセスします。

http://RaspberryPiのIPアドレス:5000

使い方

操作は以下の流れです。

  1. Raspberry Pi 5にUSBカメラを接続する

  2. カメラが顔を正面から見やすい位置に固定されていることを確認する

  3. Raspberry Pi 5上でアプリを起動する

  4. 同一ネットワーク内のPCブラウザからアクセスする

  5. 顔が映ると、笑顔らしい口元が見つかった場合に SMILE OK と表示される

  6. 顔はあるが笑顔検出が無い場合は KEEP SMILING と表示される

  7. 顔が映っていない場合は NO FACE と表示される

このサンプルでは、最も主要な1人の顔だけを対象にし、笑顔らしさの目安を単純表示する構成にしています。複数人の同時判定や感情分類は、あえて外して短いコードにしています。

顔が検出されていないとき
笑顔ではないとき
笑顔と検出されたとき

実装のポイント

今回の実装は、表情チェックの流れをなるべくストレートに書いています。まず顔全体を検出し、その顔領域の下半分を口元候補として切り出し、そこで笑顔検出をかけています。顔全体に直接 smile cascade をかけるよりも、対象範囲を絞ったほうがPoCとしては扱いやすいです。

また、ライブ映像のブラウザ表示は Flask のストリーミングレスポンスで実現しています。Flask 公式ドキュメントでは generator で逐次データを返す方法が説明されており、この方式を使うとMJPEGのような単純なライブ表示を作りやすいです。
Flask Streaming

カメラ入力には OpenCVの VideoCapture を使っています。これはカメラデバイスを開いてフレームを読むための標準的なクラスで、Raspberry Pi 5に接続したUSBカメラを扱うPoCとしてもシンプルです。
OpenCV VideoCapture

さらに、今回使っている Cascade Classifier は OpenCVの基本的な物体検出機能のひとつです。事前に用意された分類器ファイルを読み込んで矩形検出する仕組みなので、重い推論ランタイムを使わずにPoCを組みやすいのが利点です。
OpenCV Documentation

注意点

このサンプルは「笑顔を感情として判定する」アプリではなく、「笑顔らしい口元パターンを簡易検出する」 最小構成です。そのため、実運用を考えるといくつか注意点があります。

まず、この構成は感情そのものを正確に推定しているわけではありません。口元の形状パターンに近いものを拾っているだけなので、発話中、あくび、口元の影、歯の見え方などで反応する可能性があります。逆に、控えめな笑顔や顔の角度によっては検出しにくい場合もあります。

また、精度は撮影条件に強く影響されます。逆光、強い影、横顔、マスク、解像度不足、カメラの高さのズレなどでは、顔検出や笑顔検出が安定しにくくなります。PoCの段階では十分でも、実環境では scaleFactorminNeighborsminSize の調整が必要です。

Raspberry Pi 5 は高性能な部類ですが、それでもエッジ側で連続映像処理を行う以上、解像度やフレームレートを上げすぎると負荷が増えます。PoCの段階では640×480程度から試し、必要に応じて画質と負荷のバランスを見るほうが扱いやすいです。
Raspberry Pi

最後に、顔画像を扱う用途では、利用場所や運用方法に応じたプライバシー配慮も必要です。今回の構成はローカル処理の簡易PoCですが、実運用前には保存の有無、利用目的、表示方法などを整理しておくほうが安全です。

今後の改善案

今回のサンプルは最小構成なので、今後は以下のような拡張が考えられます。

  • 複数人対応にする
    最初の1人だけでなく、検出した複数の顔に対して個別に表示するようにすると、イベント受付などでも使いやすくなります。

  • 笑顔の強さを段階表示する
    単純なOK/NGだけでなく、笑顔検出の回数や口元矩形の安定度を使って3段階程度に分けると、デモとして見やすくなります。

  • 撮影品質チェックに広げる
    顔の大きさ、中央寄りかどうか、明るさなども合わせれば、受付写真や会員登録写真の簡易撮影OK判定にも広げやすいです。

  • CSVログ保存を追加する
    一定時間ごとの笑顔検出回数を残しておくと、デモ評価や改善検討に使いやすくなります。

  • systemd で自動起動する
    Raspberry Pi 5の電源投入後にアプリを自動で立ち上げるようにすると、現場設置がしやすくなります。

関連技術参考トピック

今回は Raspberry Pi 5を用いた簡易PoCとして実装しましたが、実際に受付端末や店舗端末、セルフ撮影機器などへ展開する場合には、AIアクセラレータを搭載した Renesas RZ/V シリーズも有力な選択肢になります。RZ/Vシリーズは画像認識や人物検出などのエッジAI用途を想定しており、カメラ入力を活用した組込み機器開発への展開も検討できます。
RZ/V Embedded AI MPU

まとめ

今回は、Raspberry Pi 5上で動く簡易「笑顔チェック」 Webアプリを作ってみました。USBカメラ映像から顔を検出し、口元の特徴をもとに笑顔らしい状態を簡易表示することで、受付端末やセルフ撮影ブース、プレゼン練習などで活用できるPoCを手軽に試せる構成です。

構成自体はシンプルですが、

  • USBカメラ映像をリアルタイムで取得する

  • 顔領域を検出する

  • 笑顔らしい口元パターンを簡易検出する

  • Webブラウザから結果を確認する

  • ローカル環境で完結しやすい

という流れを通して、画像処理やエッジAIアプリケーションの入り口として扱いやすい題材になりました。

今回は OpenCVのHaar Cascade を用いた最小構成としましたが、今後は複数人対応や撮影品質チェック、より高度な画像認識機能の追加などへ発展させることもできます。また、PoCから実運用・組込み機器への展開を検討する際には、RenesasのRZ/VシリーズのようなAI処理に対応したMPUを活用することで、より本格的なエッジAIソリューションへ発展させることも期待できます。

「エレクトロニクスを簡単に」の観点職でも、まずは身近なRaspberry PiとOSSを活用して小さく試し、そこから実用的な画像認識アプリケーションへ発展させていく第一歩として楽しめるテーマではないでしょうか。

参考リンク

Raspberry Pi 5
Raspberry Pi Software / Raspberry Pi OS
OpenCV License
OpenCV CascadeClassifier
OpenCV VideoCapture
OpenCV Haar Cascades
Flask: Streaming Contents
RZ/V Embedded AI MPU