顔検出は,機械学習の強力かつ一般的なユースケースである.学校への出席や法の執行といった手動タスクの自動化に利用できる。
一方、生体認証にも利用できます。
この記事では、OpenCVを使用してPythonで顔検出を実行します。
OpenCV
OpenCV は最も人気のあるコンピュータビジョンライブラリの一つです.CとC++で書かれており、JavaとMATLABの他にPythonのサポートも提供しています。
最速のライブラリではありませんが、作業が簡単で、高レベルのインタフェースを提供するため、開発者は安定したコードを書くことができます。
OpenCVをインストールし、Pythonのコードで使えるようにしましょう。
$ pip install opencv-contrib-python
また,OpenCV の主要なモジュールだけをインストールするために, opencv-python
をインストールすることもできます.opencv-contrib-python` は、メインモジュールと、拡張機能を提供する contrib モジュールを含んでいます。
OpenCVによる画像中の顔の検出
OpenCV がインストールされていれば,コードの中で cv2
としてインポートすることができます.
画像を読み込むためには,処理したい画像のパスを指定して imread()
関数を利用します.imread()関数は、指定されたファイルから画像を
ndarrayにロードします。
ファイルが存在しない場合やサポートされていないフォーマットである場合など、画像を読み込むことができなかった場合、この関数はNone` を返します。
ここでは、Kaggle のデータセットから画像を使用する。
import cv2
path_to_image = 'Parade_12.jpg'
original_image = cv2.imread(path_to_image)
顔の検出には、完全なRGB情報は必要ありません。
色は画像に無関係な情報を多く含んでいるので、それを取り除き、グレースケール画像で作業する方が効率的です。
また、OpenCVの内部で動作するViola-Jonesアルゴリズムは、画像内の領域の強度の違いをチェックします。
グレースケール画像は、この差をより劇的に指摘します。
注意:カラー画像の場合,デコードされた画像はチャンネルが BGR 順で保存されているので,グレースケールに変換する場合は, cv2.COLOR_BGR2GRAY
フラグを利用する必要があります.
image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
これは, imread()
を利用する際に, cv2.IMREAD_GRAYSCALE
フラグを設定することで直接行うことができます.
original_image = cv2.imread(path_to_image, cv2.IMREAD_GRAYSCALE)
OpenCV ライブラリには,顔,目,笑顔,上半身など,様々なものを検出するためにあらかじめ学習された分類器が付属しています.
これらのオブジェクトを検出するための Haar 機能は XML として保存されており、OpenCV をどのようにインストールしたかによりますが、多くの場合 Libsite-packages-001data
で見つけることができます。
コードからそれらにアクセスするには, cv2.data.haarcascades
を用いて,使用したい XML ファイルの名前を追加します.
CascadeClassifier()` コンストラクタにファイルパスを追加することで、オブジェクトの検出にどの Haar 特徴を利用するかを選択することができます。
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
次に、この face_cascade
オブジェクトを使用して、画像から顔を検出します。
detected_faces = face_cascade.detectMultiScale(image=image, scaleFactor=1.3, minNeighbors=4)
物体検出モデルが学習される際、ある大きさの顔を検出するように学習されるため、予想より大きかったり小さかったりする顔を見逃す可能性があります。
このことを念頭に置いて、顔が最終的に「検出可能な」サイズになることを期待して、画像のサイズを何度も変更します。
ScaleFactorは、OpenCV に画像をどの程度拡大縮小すればよいかを知らせます。
この例では、1.3は
30%` ダウンスケールして、顔とのマッチングをより良くすることを意味します。
minNeighborsパラメータについては、偽陽性と偽陰性の数を制御するために使用します。
これは、陽性の矩形(顔の特徴を検出する)が、実際に陽性とみなされるために隣接している必要がある最小の数を定義します。
scaleFactorと
minNeighbors` の両パラメータはある程度任意に設定可能であり、実験的に設定されたものである。
私たちは、偽陽性が出ないような値を選びましたが、偽陰性(顔が検出されない)が多くなるというトレードオフがあります。
detectMultiScale()メソッドは、検出されたすべてのオブジェクト(最初の例では顔)の矩形のリストを返します。
返された矩形のリストを利用し, cv2.rectangle()
関数を用いることで,顔が検出された矩形を簡単に描画することができます.ここで指定する色は,RGBオーダーのタプルである必要があることに注意してください.
for (x, y, width, height) in detected_faces:
cv2.rectangle(
image,
(x, y),
(x + width, y + height),
color,
thickness=2
)
さて、これをまとめてみましょう。
import cv2
def draw_found_faces(detected, image, color: tuple):
for (x, y, width, height) in detected:
cv2.rectangle(
image,
(x, y),
(x + width, y + height),
color,
thickness=2
)
path_to_image = 'Parade_12.jpg'
original_image = cv2.imread(path_to_image)
if original_image is not None:
# Convert image to grayscale
image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
# Create Cascade Classifiers
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
profile_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_profileface.xml")
# Detect faces using the classifiers
detected_faces = face_cascade.detectMultiScale(image=image, scaleFactor=1.3, minNeighbors=4)
detected_profiles = profile_cascade.detectMultiScale(image=image, scaleFactor=1.3, minNeighbors=4)
# Filter out profiles
profiles_not_faces = [x for x in detected_profiles if x not in detected_faces]
# Draw rectangles around faces on the original, colored image
draw_found_faces(detected_faces, original_image, (0, 255, 0)) # RGB - green
draw_found_faces(detected_profiles, original_image, (0, 0, 255)) # RGB - red
# Open a window to display the results
cv2.imshow(f'Detected Faces in {path_to_image}', original_image)
# The window will close as soon as any key is pressed (not a mouse click)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print(f'En error occurred while trying to load {path_to_image}')
この画像には2つの異なるモデルを使用しました。
正面を向いた顔を検出するデフォルトのモデルと、横を向いた顔をより良く検出するために作られたモデルです。
frontalfaceモデルで検出された顔は緑で、profileface
モデルで検出された顔は赤で輪郭が描かれています。
1番目のモデルが検出した顔のほとんどは2番目のモデルでも検出されたはずなので、profileface
モデルが顔を検出したがfrontalface
が検出しなかった場合のみ、赤い四角を描きました。
profiles_not_faces = [x for x in detected_profiles if x not in detected_faces]
imshow()` メソッドは、渡された画像を指定されたタイトルのウィンドウに表示するだけです。
私たちが選択した画像では、次のような出力が得られます。
scaleFactorと
minNeighborsに異なる値を使用すると、異なる結果が得られます。
例えば、scaleFactor = 1.1と
minNeighbors = 4` を使用すると、どちらのモデルでも偽陽性が多く、真陽性が多いという結果になります。
このアルゴリズムは完璧ではありませんが、非常に効率的であることがわかります。
これは、ウェブカメラからのビデオフィードのようなリアルタイムデータを扱うときに最も顕著に現れます。
ウェブカメラを用いたリアルタイム顔検出技術
ビデオストリームは単なる画像のストリームである。
Viola-Jonesアルゴリズムの効率性を利用すれば、リアルタイムで顔検出を行うことができます。
必要なステップは、画像が1枚だけの前の例と非常に似ています – ストリーム内の各画像に対してこれを実行します。
ビデオストリームを取得するために, cv2.VideoCapture
クラスを利用します.このクラスのコンストラクタは、ビデオストリームを表す整数のパラメータを受け取ります。
ほとんどのマシンでは、 0
を渡すことでウェブカメラにアクセスすることができますが、複数のビデオストリームを持つマシンでは、異なる値を試す必要があるかもしれません。
次に、入力ストリームから個々の画像を読み込む必要があります。
これは read()
関数で行います。
この関数は retval
と image
を返します。
imageは単に取得したフレームです。
retval の戻り値は、フレームが取得されたかどうかを検出するために使用され、取得されていない場合は False
になります。
しかし、これはビデオ入力ストリームと矛盾する傾向があるため(例えばウェブカメラが切断されたことを検出しない)、この値を無視することにします。
それでは、ビデオストリームを処理するために、前のコードを変更してみましょう。
import cv2
def draw_found_faces(detected, image, color: tuple):
for (x, y, width, height) in detected:
cv2.rectangle(
image,
(x, y),
(x + width, y + height),
color,
thickness=2
)
# Capturing the Video Stream
video_capture = cv2.VideoCapture(0)
# Creating the cascade objects
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_eye_tree_eyeglasses.xml")
while True:
# Get individual frame
_, frame = video_capture.read()
# Covert the frame to grayscale
grayscale_image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Detect all the faces in that frame
detected_faces = face_cascade.detectMultiScale(image=grayscale_image, scaleFactor=1.3, minNeighbors=4)
detected_eyes = eye_cascade.detectMultiScale(image=grayscale_image, scaleFactor=1.3, minNeighbors=4)
draw_found_faces(detected_faces, frame, (0, 0, 255))
draw_found_faces(detected_eyes, frame, (0, 255, 0))
# Display the updated frame as a video stream
cv2.imshow('Webcam Face Detection', frame)
# Press the ESC key to exit the loop
# 27 is the code for the ESC key
if cv2.waitKey(1) == 27:
break
# Releasing the webcam resource
video_capture.release()
# Destroy the window that was showing the video stream
cv2.destroyAllWindows()
結論
今回は、PythonとOpenCVを使った顔検出のアプリケーションを作成しました。
OpenCVライブラリの使用は、基本的な物体検出プログラムには非常にわかりやすいものです。
処理したい画像の種類に応じて scaleFactor
と minNeighbors
パラメータを実験的に調整すると、かなり正確な結果を非常に効率的に得ることができます。