【OpenCV応用編 第1回】人の表情を読む!目と笑顔をリアルタイムで検出しよう 😊

「顔が検出できるようになったけど、その人が笑っているかどうかも知りたい!」
「検出した顔の中から、さらに『目』だけを見つけ出すにはどうすればいい?」
「OpenCVの顔検出の知識を、もっと色々なことに応用してみたい!」

こんにちは! OpenCV探検隊、隊長のPythonistaです! 前回の入門シリーズでは、OpenCVを使って画像や動画から「顔」を検出する、コンピュータビジョンの基本的な手法をマスターしましたね。

今回から始まる**応用編シリーズ**では、その知識をさらに拡張し、顔以外の様々な物体を検出する冒険に出ます! 第1回となる今回のテーマは、顔という大きな枠の中から、さらに細かいパーツである**「目」と「笑顔」**を検出する技術です。これをマスターすれば、あなたのプログラムは単に人の存在を認識するだけでなく、「その人がどこを見ているか」「どんな表情をしているか」といった、より高度な情報を読み取れるようになります。さあ、人の表情を読み解く、新たな探検を始めましょう!


1. 今回の戦略:階層的検出で効率アップ!

いきなり画像全体から小さな「目」を探し始めるのは、非常に効率が悪く、間違い(誤検出)も多くなります。例えるなら、日本全国の地図からいきなり特定の家の表札を探すようなものです。

そこで今回は、よりスマートな**「階層的検出 (Hierarchical Detection)」**というアプローチを取ります。

  1. ステップ1(大規模探索): まず、画像全体から大きなターゲットである**「顔」**を見つけ出します。(=市を見つける)
  2. ステップ2(範囲の限定): 見つかった「顔」の領域だけを切り取って、**関心領域 (ROI - Region of Interest)** とします。
  3. ステップ3(小規模探索): その小さな**顔のROIの中でのみ**、「目」や「笑顔」を探します。(=市の中だけで、特定の家を探す)

この手順を踏むことで、処理速度が向上し、検出の精度も格段に上がります。


2. 準備:新しい学習済みモデル(XMLファイル)の入手

顔検出の時と同じように、今回もOpenCVが提供するハールカスケードの学習済みモデルを使います。「目」用と「笑顔」用のXMLファイルを、前回と同じ場所からダウンロードしましょう。

  1. OpenCVの公式GitHubリポジトリにアクセスします。
    https://github.com/opencv/opencv/tree/master/data/haarcascades
  2. 以下の2つのファイルを見つけて、ダウンロードします。(入門シリーズでダウンロードした顔検出用のファイルも引き続き使います)
    • 目の検出用: haarcascade_eye.xml
    • 笑顔の検出用: haarcascade_smile.xml
  3. ダウンロードした2つのXMLファイルを、あなたのPythonスクリプトや顔検出用のXMLファイルと同じディレクトリに保存してください。

これで、顔、目、笑顔を検出するための3つの「頭脳」が揃いました。


3. 実装:リアルタイム目・笑顔検出プログラム

それでは、リアルタイムのWebカメラ映像から、顔、目、笑顔を同時に検出するプログラムを作成します。前回のリアルタイム顔検出のコードをベースに、階層的検出のロジックを加えていきます。

import cv2
import sys

# --- 準備:3つのカスケード分類器を読み込む ---
face_cascade_path = 'haarcascade_frontalface_default.xml'
eye_cascade_path = 'haarcascade_eye.xml'
smile_cascade_path = 'haarcascade_smile.xml'

face_cascade = cv2.CascadeClassifier(face_cascade_path)
eye_cascade = cv2.CascadeClassifier(eye_cascade_path)
smile_cascade = cv2.CascadeClassifier(smile_cascade_path)

if face_cascade.empty() or eye_cascade.empty() or smile_cascade.empty():
    print("エラー: カスケードファイルを1つ以上読み込めませんでした。")
    sys.exit()

# Webカメラに接続
cap = cv2.VideoCapture(0)
if not cap.isOpened():
    print("エラー: Webカメラを開けませんでした。")
    sys.exit()

print("リアルタイム検出を開始します...'q'キーで終了します。")

# --- メインループ ---
while True:
    ret, frame = cap.read()
    if not ret:
        break
        
    gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # --- ステップ1: 顔を検出 ---
    faces = face_cascade.detectMultiScale(gray_frame, scaleFactor=1.1, minNeighbors=5)
    
    for (x, y, w, h) in faces:
        # 顔を緑の矩形で囲む
        cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
        
        # --- ステップ2: 顔領域(ROI)を切り出す ---
        roi_gray = gray_frame[y:y+h, x:x+w]
        roi_color = frame[y:y+h, x:x+w]
        
        # --- ステップ3: 顔領域の中から「目」を検出 ---
        eyes = eye_cascade.detectMultiScale(roi_gray)
        for (ex, ey, ew, eh) in eyes:
            # 目を青の矩形で囲む (座標はROI内のものであることに注意)
            cv2.rectangle(roi_color, (ex, ey), (ex+ew, ey+eh), (255, 0, 0), 2)
            
        # --- ステップ4: 顔領域の中から「笑顔」を検出 ---
        smiles = smile_cascade.detectMultiScale(roi_gray, scaleFactor=1.8, minNeighbors=20)
        for (sx, sy, sw, sh) in smiles:
            # 笑顔を赤の矩形で囲む
            cv2.rectangle(roi_color, (sx, sy), (sx+sw, sy+sh), (0, 0, 255), 2)
            
    # 結果を表示
    cv2.imshow('Real-time Detection', frame)
    
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# --- 後片付け ---
cap.release()
cv2.destroyAllWindows()


コードのポイント:

  • 3つのカスケード分類器をそれぞれロードしています。
  • 顔を検出した後、roi_gray = gray_frame[y:y+h, x:x+w]というコードで、顔の部分だけを切り抜いています。これはNumPyの強力な「スライシング」機能です。
  • 目と笑顔の検出は、画像全体(gray_frame)ではなく、切り抜いた顔の領域(roi_gray)に対して行っています。これにより、計算コストが削減され、精度が向上します。
  • 矩形の描画も、顔領域のカラー版(roi_color)に対して行っていることに注意してください。

笑顔の検出は、口の形が変化しやすいため、目よりも少しパラメータ調整(scaleFactor, minNeighbors)がシビアになることがあります。色々試してみてください。


まとめと次回予告

今回は、OpenCV応用編の第一歩として、顔検出の知識を応用し、より細かい顔のパーツである「目」と「笑顔」を検出する方法を学びました。

  • 検出対象を変えるには、対応する**学習済みモデル(XML)を読み込む**だけでよい、というOpenCVの手軽さ。
  • 大きな物体を見つけてから、その内部で小さな物体を探す**「階層的検出」**という、効率的で高精度なアプローチ。
  • NumPyのスライシングを使った**関心領域(ROI)**の切り出し方。

これであなたのプログラムは、人の表情の変化を捉えることができるようになりました。例えば、「笑顔が検出された瞬間に自動で写真を撮る」といった、スマートなカメラアプリの機能も実装できるはずです。

さて、今回は顔というパーツを「深掘り」しましたが、次は逆に視野を「広げて」みましょう。次回、第2回では、人物の**「全身」**を検出する**歩行者検出**に挑戦します。これにより、監視カメラの映像から人の数を数えたり、人の流れを分析したりといった、より広範囲なアプリケーションへの道が開かれます。お楽しみに!

その他の投稿はこちら

コメント

このブログの人気の投稿

タイトルまとめ

これで迷わない!WindowsでPython環境構築する一番やさしい方法 #0

【Python標準ライブラリ入門 第3回】データ交換の標準!jsonモジュールでJSONを自由自在に扱おう