注釈

こんにちは、SunFounder Raspberry Pi & Arduino & ESP32 Enthusiast Community on Facebookへようこそ!他の愛好家と一緒に、Raspberry Pi、Arduino、ESP32の世界により深く入り込みましょう。

参加する理由

  • 専門家サポート: 購入後の問題や技術的な課題を、コミュニティと私たちのチームの助けを借りて解決します。

  • 学習と共有: ヒントやチュートリアルを交換して、スキルを向上させましょう。

  • 限定プレビュー: 新製品の発表や先行プレビューに早期アクセスできます。

  • 特別割引: 最新製品を特別割引でお楽しみいただけます。

  • 季節限定キャンペーンとプレゼント: プレゼント企画やホリデーキャンペーンに参加しましょう。

👉 一緒に発見し、創造する準備はできましたか? [こちら] をクリックして、今すぐ参加しましょう!

8. 顔と目の検出

この章では、Raspberry Pi の Picamera2 を使って映像を取得し、OpenCV の Haar 特徴分類器を用いて リアルタイムの顔検出と目の検出 を行います。 この方法は軽量で実用性が高く、Raspberry Pi 上での導入を始める初学者にも適しています。

1. Haar 特徴と検出の原理

  1. Haar 特徴の本質

Haar 特徴は、物体検出に使われる古典的な手法です。画像領域内の 明るさの差のパターン を表現することで、その領域に顔や目などが含まれている可能性を判断します。

代表的な Haar 特徴の例:

  • 目の領域は、上にある額よりも暗いことが多い

  • 鼻筋の左右では、明るさが対称になりやすい

  • 口の下には、はっきりしたエッジパターンが現れやすい

Illustration of Haar features

OpenCV では、事前学習済みの Haar 分類器( .xml ファイル)が必要です。これらはすでにサンプルディレクトリに含まれているため、そのまま読み込んで使用できます。

  1. 検出パイプライン

    1. CascadeClassifier を使って学習済み Haar モデルを読み込む

    2. リアルタイム映像をグレースケールに変換する(処理効率向上のため)

    3. detectMultiScale を使って顔や目の領域を検出する

    4. 検出された対象の周囲に矩形を描画する

Detection pipeline illustration

2. コードの実行

重要

開始する前に、次の項目を確認してください:

  • パンチルトが組み立てられている

  • Raspberry Pi のデスクトップにアクセスできる

  • コードパッケージがインストールされている

  • Fusion HAT+ がインストールされ、設定されている

  • OpenCV がインストールされている

詳細については 0. OpenCV のセットアップ を参照してください。

  1. ターミナルを開き、次のコマンドを入力します:

    cd ~/ai-lab-kit/opencv_python
    python3 cv_8_haarcascade.py
    

    Tip

    動画ファイルから顔と目を検出するための cv_8_haarcascade_video.py も用意しています。

  2. プログラムを実行すると、 Raspberry Pi Camera - Face Detection という名前のウィンドウが表示され、Raspberry Pi Camera のライブ映像が映し出されます。

    映像内で検出された顔は 黄色の矩形 で囲まれ、それぞれの顔には Face 1Face 2 ... のようにラベルが付けられます。 また、各顔領域の内部では目も検出され、 オレンジ色の矩形 で表示されます。

    検出はリアルタイムで行われ、人がカメラの前で動くと矩形もそれに追従して移動します。

    プログラムを停止するには、次のいずれかを行ってください:

    • キーボードの q キーを押す

    • ウィンドウ右上の閉じるボタン(X)でウィンドウを閉じる

    終了後は、カメラが停止し、OpenCV のウィンドウもすべて閉じられます。

3. 完全なコード

# Face and eye detection using Raspberry Pi Camera (Picamera2 + OpenCV Haar Cascades)
import cv2
from picamera2 import Picamera2
from pathlib import Path

# -----------------------------
# Load Haar cascade classifiers
# -----------------------------
BASE_DIR = Path(__file__).resolve().parent

face_cascade = cv2.CascadeClassifier(str(BASE_DIR / "haarcascade_frontalface_default.xml"))
eye_cascade  = cv2.CascadeClassifier(str(BASE_DIR / "haarcascade_eye.xml"))

# Check if cascade files are loaded correctly
if face_cascade.empty():
   raise FileNotFoundError("Failed to load haarcascade_frontalface_default.xml")
if eye_cascade.empty():
   raise FileNotFoundError("Failed to load haarcascade_eye.xml")

# -----------------------------
# Initialize Picamera2
# -----------------------------
picam2 = Picamera2()

# Video configuration (resolution can be adjusted)
config = picam2.create_video_configuration(main={"size": (640, 480)})
picam2.configure(config)
picam2.start()

WIN = "Raspberry Pi Camera - Face Detection"
print("Camera started. Press 'q' to quit.")

try:
   while True:
      # Capture a frame (Picamera2 typically provides RGB)
      frame_rgb = picam2.capture_array()

      # Convert RGB -> Grayscale directly (faster than RGB->BGR->GRAY)
      gray = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2GRAY)

      # Improve contrast to make detection more stable under different lighting
      gray = cv2.equalizeHist(gray)

      # Detect faces
      faces = face_cascade.detectMultiScale(
            gray,
            scaleFactor=1.2,
            minNeighbors=5,
            minSize=(60, 60)
      )

      # Convert RGB -> BGR only for display and drawing (OpenCV imshow expects BGR)
      frame_bgr = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2BGR)

      # Draw face and eye results
      for i, (x, y, w, h) in enumerate(faces, start=1):
            # Draw face rectangle + label
            cv2.rectangle(frame_bgr, (x, y), (x + w, y + h), (255, 255, 0), 2)
            cv2.putText(frame_bgr, f"Face {i}", (x, max(0, y - 10)),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)

            # ROI for eye detection (search eyes only inside the detected face area)
            roi_gray = gray[y:y + h, x:x + w]
            roi_color = frame_bgr[y:y + h, x:x + w]

            eyes = eye_cascade.detectMultiScale(
               roi_gray,
               scaleFactor=1.2,
               minNeighbors=8,
               minSize=(20, 20)
            )

            # Draw up to 2 eyes (typical for a face)
            for (ex, ey, ew, eh) in eyes[:2]:
               cv2.rectangle(roi_color, (ex, ey), (ex + ew, ey + eh), (0, 127, 255), 2)

      # Show the frame
      cv2.imshow(WIN, frame_bgr)

      # Handle keyboard input
      key = cv2.waitKey(1) & 0xFF
      if key == ord("q"):
            break

      # Exit if the user closes the window (click X)
      if cv2.getWindowProperty(WIN, cv2.WND_PROP_VISIBLE) < 1:
            break

finally:
   picam2.stop()
   cv2.destroyAllWindows()
   print("Camera stopped.")

4. コード解説

  1. 必要なライブラリをインポートする:

    import cv2
    from picamera2 import Picamera2
    from pathlib import Path
    

    OpenCV は検出と描画に使用し、Picamera2 は Raspberry Pi カメラからフレームを取得するために使用します。

  2. 現在のスクリプトのディレクトリを取得する:

    BASE_DIR = Path(__file__).resolve().parent
    

    これにより、Python スクリプトと同じフォルダにある cascade XML ファイルを読み込めます。

  3. Haar 分類器(顔と目)を読み込む:

    face_cascade = cv2.CascadeClassifier(str(BASE_DIR / "haarcascade_frontalface_default.xml"))
    eye_cascade  = cv2.CascadeClassifier(str(BASE_DIR / "haarcascade_eye.xml"))
    

    Haar Cascade は、顔や目を検出するために事前学習されたモデルです。

  4. Cascade ファイルが正しく読み込まれたか確認する:

    if face_cascade.empty():
        raise FileNotFoundError("Failed to load haarcascade_frontalface_default.xml")
    if eye_cascade.empty():
        raise FileNotFoundError("Failed to load haarcascade_eye.xml")
    

    パスが間違っていたり、ファイルが存在しない場合、 CascadeClassifier は空になります。 この確認を行うことで、問題を早い段階で見つけやすくなります。

  5. カメラを初期化し、解像度を設定する:

    picam2 = Picamera2()
    config = picam2.create_video_configuration(main={"size": (640, 480)})
    picam2.configure(config)
    picam2.start()
    

    これにより、640×480 のビデオモードでカメラが起動します。

  6. フレームを連続して取得する:

    frame_rgb = picam2.capture_array()
    

    ループごとに 1 フレームを取得します。Picamera2 は通常 RGB 形式のフレームを返します。

  7. グレースケールへ変換する(検出を高速化するため):

    gray = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2GRAY)
    

    顔検出や目検出はグレースケール画像で動作し、カラー画像よりも高速です。

  8. コントラストを改善して検出を安定させる:

    gray = cv2.equalizeHist(gray)
    

    ヒストグラム平坦化を行うことで、異なる照明条件でも検出結果が安定しやすくなります。

  9. フレーム内の顔を検出する:

    faces = face_cascade.detectMultiScale(
        gray,
        scaleFactor=1.2,
        minNeighbors=5,
        minSize=(60, 60)
    )
    

    これにより、検出されたすべての顔について (x, y, w, h) の矩形リストが返されます。

    • scaleFactor は画像スケールの縮小ステップを制御します(小さいほど高精度になりやすい一方、遅くなります)。

    • minNeighbors は誤検出を減らします(大きいほど厳しくなります)。

    • minSize は非常に小さい検出結果を無視します。

  10. 描画と表示のために RGB を BGR へ変換する:

    frame_bgr = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2BGR)
    

    OpenCV の描画関数と imshow は、カラー画像に対して BGR を前提としています。

  11. 顔の矩形とラベルを描画する:

    cv2.rectangle(frame_bgr, (x, y), (x + w, y + h), (255, 255, 0), 2)
    cv2.putText(frame_bgr, f"Face {i}", (x, max(0, y - 10)),
                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)
    

    これにより、検出された各顔の周囲に矩形を描き、 Face 1 のようなラベルを表示します。

  12. 各顔の内部(ROI)で目を検出する:

    roi_gray = gray[y:y + h, x:x + w]
    roi_color = frame_bgr[y:y + h, x:x + w]
    
    eyes = eye_cascade.detectMultiScale(
        roi_gray,
        scaleFactor=1.2,
        minNeighbors=8,
        minSize=(20, 20)
    )
    

    ROI は “Region of Interest” の略です。目の検出を顔領域の内部だけに限定することで、高速化でき、誤検出も減らせます。

  13. 最大 2 つの目を描画する:

    for (ex, ey, ew, eh) in eyes[:2]:
        cv2.rectangle(roi_color, (ex, ey), (ex + ew, ey + eh), (0, 127, 255), 2)
    

    これにより、最初に検出された 2 つの目の周囲に矩形を描きます。

  14. 結果を表示し、終了を処理する:

    cv2.imshow(WIN, frame_bgr)
    
    key = cv2.waitKey(1) & 0xFF
    if key == ord("q"):
        break
    
    if cv2.getWindowProperty(WIN, cv2.WND_PROP_VISIBLE) < 1:
        break
    

    q キーを押すか、ウィンドウを閉じることで安全に終了できます。

  15. 後片付け(必ず実行される):

    picam2.stop()
    cv2.destroyAllWindows()
    

    エラーが発生した場合でも、最後にカメラを停止し、OpenCV のウィンドウをすべて閉じます。

5. Haar 検出の長所と短所

Aspect

Advantages

Disadvantages

Speed

非常に高速で、Raspberry Pi に適している

Accuracy

正面顔にはよく機能する

回転や横顔には弱い

Lighting

均一な照明下では良好

明るすぎる/暗すぎる環境では性能が低下する

Model

モデルサイズが小さく、導入が容易

深層学習ベースの手法より精度は低い

軽量かつ高速であるため、Haar 特徴は現在でも組み込み機器において十分実用的です。

6. よく使われる改善方法

  1. 照明前処理:検出前にヒストグラム平坦化や CLAHE を適用すると、暗所での性能を改善できます。

  2. 多角度検出:正面顔用だけでなく横顔用の分類器も読み込むことで、より多くの顔姿勢に対応できます。

  3. 顔パーツの追加検出:目や口、鼻の Haar 分類器を追加し、検出内容をより充実させることができます。

  4. Haar の代わりに DNN を使う:OpenCV DNN + ResNet/MobileNet を使うと、より高い精度が得られます(ただし計算量は増えます)。

7. 発展練習

  • cv2.equalizeHist をグレースケール画像に適用して、暗い環境での検出性能を改善してみましょう。

  • 口や鼻の Haar 分類器を追加して、より多くの顔パーツを検出してみましょう。

  • cv2.VideoWriter を使って、検出結果を動画として記録してみましょう。

  • GPIO 出力と組み合わせて、「顔を検出したら LED を点灯する」Raspberry Pi プロジェクトを作ってみましょう。