YOLOベースのリアルタイムマスク検出システムの構築とWebインタフェース実装

パンデミック期における公共衛生管理の一環として、マスク着用状況の自動検出は重要な課題です。本稿では、YOLOアーキテクチャを活用したリアルタイムマスク検出システムの設計・実装プロセスを体系的に解説します。特に、YOLOv8を基盤とし、PyTorchによるカスタム学習、OpenCVを用いた推論パイプライン、およびFlaskによる軽量Webフロントエンドの統合に焦点を当てます。

前提環境

以下のソフトウェア構成を準備してください:

  • Python 3.9 以上
  • torch==2.1.0, torchvision==0.16.0
  • opencv-python==4.8.1
  • flask==2.3.3
  • ultralytics==8.1.0(YOLOv8公式ライブラリ)

依存パッケージのインストールコマンド:

pip install torch torchvision opencv-python flask ultralytics

データセット構造とアノテーション形式

学習用データは以下のような階層で整理します:

mask_dataset/
├── train/
│   ├── images/     # JPEG/PNG画像
│   └── labels/     # YOLOフォーマットのTXTファイル(例: img001.txt)
├── val/
│   ├── images/
│   └── labels/
└── test/
    ├── images/
    └── labels/

各ラベルファイルは1行1オブジェクトで、クラスID(0: マスク着用、1: 無着用)、正規化された中心座標(x_center, y_center)および相対サイズ(width, height)を含みます:

0 0.423 0.517 0.285 0.392

モデル学習の実行

YOLOv8のカスタム学習には、dataset.yaml設定ファイルを作成します:

train: ./mask_dataset/train/images
val: ./mask_dataset/val/images
test: ./mask_dataset/test/images

nc: 2
names: ["masked", "unmasked"]

学習コマンド(GPU利用時):

yolo detect train \
  data=dataset.yaml \
  model=yolov8s.pt \
  epochs=100 \
  imgsz=640 \
  batch=32 \
  name=mask_detection_v1 \
  device=0

学習完了後、重みファイルはruns/detect/mask_detection_v1/weights/best.ptに保存されます。

Webアプリケーションの実装

Flaskを用いた推論サーバーを構築します。推論処理では、OpenCVで画像を前処理し、YOLOv8モデルでバウンディングボックスと信頼度を取得し、結果をHTMLに埋め込みます。

app.py(再設計版):

from flask import Flask, request, render_template, jsonify
import cv2
import numpy as np
from pathlib import Path
from ultralytics import YOLO

app = Flask(__name__)
MODEL_PATH = "runs/detect/mask_detection_v1/weights/best.pt"
detector = YOLO(MODEL_PATH)

@app.route("/", methods=["GET"])
def index():
    return render_template("upload.html")

@app.route("/predict", methods=["POST"])
def predict():
    if "image" not in request.files:
        return jsonify({"error": "No image uploaded"}), 400

    file = request.files["image"]
    img_bytes = np.frombuffer(file.read(), np.uint8)
    frame = cv2.imdecode(img_bytes, cv2.IMREAD_COLOR)
    
    # 推論実行(confidence threshold: 0.5)
    results = detector(frame, conf=0.5, verbose=False)
    annotated_frame = results[0].plot()  # BBoxとラベル描画
    
    # OpenCV BGR → RGB変換 → base64エンコード
    rgb_frame = cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB)
    _, buffer = cv2.imencode(".png", rgb_frame)
    img_b64 = "data:image/png;base64," + np.array(buffer).tobytes().hex()
    
    # 検出数を集計
    class_counts = {"masked": 0, "unmasked": 0}
    for box in results[0].boxes:
        cls_id = int(box.cls.item())
        label = "masked" if cls_id == 0 else "unmasked"
        class_counts[label] += 1

    return render_template(
        "result.html",
        image_data=img_b64,
        stats=class_counts
    )

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

templates/upload.html

<!DOCTYPE html>
<html>
<head><title>マスク検出ツール</title></head>
<body style="font-family: sans-serif; padding: 2rem;">
  <h1>🪪 マスク着用状況分析</h1>
  <p>画像をアップロードして、マスクの有無をAIが判定します。</p>
  <form method="post" action="/predict" enctype="multipart/form-data">
    <input type="file" name="image" accept="image/*" required><br><br>
    <button type="submit">分析開始</button>
  </form>
</body>
</html>

templates/result.html

<!DOCTYPE html>
<html>
<head><title>分析結果</title></head>
<body style="font-family: sans-serif; padding: 2rem;">
  <h1>✅ 分析結果</h1>
  <div style="margin: 1rem 0">
    <strong>着用者:</strong> {{ stats.masked }}人 | 
    <strong>未着用者:</strong> {{ stats.unmasked }}人
  </div>
  <img src="{{ image_data }}" alt="検出結果" style="max-width: 100%; border: 1px solid #ccc;"><br><br>
  <a href="/">← 別の画像を分析する</a>
</body>
</html>

実行と検証

アプリケーションを起動し、ローカルサーバーでアクセスします:

python app.py

ブラウザで http://localhost:5000 を開き、テスト画像をアップロードすると、検出結果と統計情報が即時に表示されます。

タグ: YOLOv8 flask PyTorch OpenCV computer-vision

6月20日 22:14 投稿