OpenCV – カスケード分類器 CascadeClassifier で画像から顔や目を検出する方法について

目次

概要

OpenCV のカスケード分類器 CascadeClassifier を使用して、画像から顔や目を検出する方法について解説します。

カスケード分類器

カスケード分類器は、複数の分類器を連結して行うアンサンブル学習の一種です。 画像が与えられたとき、まず特徴抽出器で画像から特徴量を抽出します。 その特徴量がカスケード分類器に入力され、分類結果 (positive または negative) が出力されます。

カスケード検出器

カスケード分類器は複数の分類器で構成されており、各分類器で positive または negative の判定が行われます。ある分類器で negative と判定された場合、その時点で棄却されます。positive と判定された場合は、分類器の出力結果が次の分類器に渡されます。

CascadeClassifier クラス

OpenCV では CascadeClassifier クラスでカスケード分類器が提供されています。

cv2.CascadeClassifier(filename)
  • 引数
    • filename: カスケード分類器の設定ファイル

XML ファイルの一覧 CascadeClassifier は、特徴量の種類やカスケード分類器を構成している分類器の情報が記載された設定ファイルを読み込んで作成します。 OpenCV では、顔や目を検出するための設定ファイルが用意されています。 これらの設定ファイルは cv2.data.haarcascades ディレクトリ以下にあるので、以下のようにファイルパスを指定するとよいでしょう。

cascade_path = os.path.join(
    cv2.data.haarcascades, "<ファイル名>"
)
cascade = cv2.CascadeClassifier(cascade_path)
ファイル名 内容 特徴量
haarcascade_eye.xml Haar-like
haarcascade_eye_tree_eyeglasses.xml メガネ Haar-like
haarcascade_frontalcatface.xml 猫の顔 (正面) Haar-like
haarcascade_frontalcatface_extended.xml 猫の顔 (正面) Haar-like
haarcascade_frontalface_alt.xml 顔 (正面) Haar-like
haarcascade_frontalface_alt2.xml 顔 (正面) Haar-like
haarcascade_frontalface_alt_tree.xml 顔 (正面) Haar-like
haarcascade_frontalface_default.xml 顔 (正面) Haar-like
haarcascade_fullbody.xml 全身 Haar-like
haarcascade_lefteye_2splits.xml 左目 Haar-like
haarcascade_licence_plate_rus_16stages.xml ロシアのナンバープレート Haar-like
haarcascade_lowerbody.xml 下半身 Haar-like
haarcascade_profileface.xml 顔 (正面) Haar-like
haarcascade_righteye_2splits.xml 右目 Haar-like
haarcascade_russian_plate_number.xml ロシアのナンバープレート Haar-like
haarcascade_smile.xml 顔 (笑顔) Haar-like
haarcascade_upperbody.xml 上半身 Haar-like

顔を検出する

今回は、haarcascade_frontalface_default.xml で正面の顔の検出を試してみます。

In [1]:
from IPython.display import Image, display


def imshow(img):
    """ndarray 配列をインラインで Notebook 上に表示する。"""
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))
In [2]:
import os

import cv2


# 正面の顔を検出するカスケード分類器を作成する。
cascade_path = os.path.join(
    cv2.data.haarcascades, "haarcascade_frontalface_default.xml"
)
face_cascade = cv2.CascadeClassifier(cascade_path)

sample.jpg

CascadeClassifier.detectMultiScale() はグレースケール画像を要求するので、cv2.cvtColor() でグレースケールに変換してから渡します。

CascadeClassifier.detectMultiScale() で画像から対象物の検出を行います。

objects = CascadeClassifier.detectMultiScale(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]])
  • 引数
    • image: グレースケール画像
    • scaleFactor: 検索窓の拡大率 (1.0 より大きい値で指定、デフォルトは 1.1)
    • minNeighbors: 近傍にこの値以上の検出矩形がない場合は、棄却する。値を大きくするほど、誤検出は減るが、未検出が増える可能性がある。(自然数で指定、デフォルトは 3)
    • flags: 使用しない。
    • minSize: 検出矩形の最小サイズ
    • maxSize: 検出矩形の最大サイズ
  • 返り値
    • 検出矩形の一覧。各要素が (左上の x 座標、左上の y 座標、幅、高さ) のタプルで表されるリスト。

CascadeClassifier.detectMultiScale() を使用して検出し、検出された矩形を cv2.rectangle() で画像に描画します。

In [3]:
# 画像を読み込む。
img = cv2.imread("sample.jpg")

# グレースケールに変換する。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 検出する。
faces = face_cascade.detectMultiScale(gray)

# 矩形を画像に描画する。
for x, y, w, h in faces:
    cv2.rectangle(img, (x, y), (x + w, y + h), color=(0, 255, 0), thickness=2)
imshow(img)

顔が検出できました。

!empty() in function 'detectMultiScale というエラーが発生する場合

以下のエラーが発生した場合は、cv2.CascadeClassifier() に指定した xml ファイルが間違っています。名前が正しいかどうか確認してください。

error: OpenCV(4.2.0) /io/opencv/modules/objdetect/src/cascadedetect.cpp:1689: error: (-215:Assertion failed) !empty() in function 'detectMultiScale'

目を検出する

haarcascade_eye.xml を使って、顔の領域にある目の検出も試してみます。 顔を検出するカスケード分類器で検出した各顔の領域を切り抜き、その領域に対して目の検出を行います。 デフォルトの minNeighbors=3 では誤検出が発生したため、minNeighbors=5 以上の値に設定しました。

In [4]:
# 目を検出するカスケード分類器を作成する。
cascade_path = os.path.join(cv2.data.haarcascades, "haarcascade_eye.xml")
eye_cascade = cv2.CascadeClassifier(cascade_path)

for x, y, w, h in faces:
    # 顔の領域を切り取る。
    roi = gray[y : y + h, x : x + w]

    # 目を検出する。
    eyes = eye_cascade.detectMultiScale(roi, minNeighbors=5)

    # 矩形を画像に描画する。
    for ex, ey, ew, eh in eyes:
        cv2.rectangle(
            img,
            (x + ex, y + ey),
            (x + ex + ew, y + ey + eh),
            color=(255, 0, 0),
            thickness=2,
        )


imshow(img)

コメント

コメントする

目次