Python – 顔認識ライブラリ Face Recognition で顔検出を行う方法

目次

概要

dlib を使った Python の顔認識ライブラリ Face Recognition を使って、画像から顔がある領域を特定する顔検出を行う方法について紹介します。

環境

コード全体は GitHub にあります。

このコードは、以下の環境で実行しました。

  • OS
    • Ubuntu: 16.04
  • ライブラリ
    • Face Recognition: 1.2.3
  • GPU の実行環境
    • GPU: GeForce GTX 1080
    • CUDA: 9.0
    • CuDNN: 7
  • CPU の実行環境
    • CPU: Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
    • メモリ: 16G
検出に CNN を使う場合、CUDA 10.1/cudnn7 環境では、`CUDA Runtime API initialization failed.` というエラーが発生して動きませんでした。 CUDA 9/cudnn7 環境では動作したので、動作しない場合は CUDA のバージョンを確認してみてください。

インストール

インストール時に Face Recognition が利用している dlib のビルドが必要になります。 dlib をビルドできる環境が整っていない場合、その際に失敗してしまうので、先に C++ のビルド環境及び CMake をインストールします。 Ubuntu の場合、apt で導入できます。

apt install -y build-essential cmake

Windows の場合、Visual Studio 及び CMake が必要になると思いますが、具体的な手順は試してないのでわかりません。

windowsでdlibインストールで詰まったときに – Qiita

準備ができたら、pip で Face Recognition をインストールします。

pip install face_recognition

画像から顔を検出する

以下を検出対象の画像とします。

sample.jpg

load_image_file() で画像を読み込みます。

In [1]:
import face_recognition
import matplotlib.pyplot as plt

# 画像を読み込む。
img = face_recognition.load_image_file("sample.jpg")

face_locations() で読み込んだ顔の画像から、顔の領域を検出します。 返り値は顔の領域を表す (top, right, bottom, left) の tuple の list になっています。

face_recognition.face_locations(img, number_of_times_to_upsample=1, model='hog')
  • 引数
    • img: 検出対象の画像
    • number_of_times_to_upsample: アップサンプリングを行う回数
    • model: 検出に使用するモデル
In [2]:
# 画像から顔の領域を検出する。
face_locs = face_recognition.face_locations(img, model="cnn", number_of_times_to_upsample=2)

結果を確認するために、Pillow で顔の領域を画像上に描画します。

In [3]:
from PIL import ImageDraw, Image
from IPython.display import display

def draw_faces(img, locs):
    img = Image.fromarray(img)
    draw = ImageDraw.Draw(img, mode="RGBA")

    for top, right, bottom, left in locs:
        draw.rectangle((left, top, right, bottom), outline="lime", width=2)

    display(img)


draw_faces(img, face_locs)

画像から顔の領域が正しく検出できていることがわかりました。

model は検出に使用するモデルを指定します。 “hog” (デフォルト) を指定した場合、HOG 特徴量 ベースのモデルになります。 “cnn” を指定した場合、CNN ベースのモデルになります。

HOG CNN
計算量 少ない 多い
精度

CNN のほうが高精度ですが、計算量が多く、CPU で実行した場合は時間がかかってしまいます。GPU が使える PC ではこちらを選択するとよいでしょう。

number_of_times_to_upsample は画像をアップサンプリングする回数です。 アップサンプリングを行うことで小さい顔も検出できるようになりますが、その分計算量が増えてしまうため、デフォルトの値で検出できない場合は値を2や3に増やしてください。

sample2.jpg

In [4]:
import face_recognition
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy as np

# 画像を読み込む。
img = face_recognition.load_image_file("sample2.jpg")

# HOG 特徴量を使った顔検出
locs = face_recognition.face_locations(img, model="hog")
draw_faces(img, locs)

# CNN を使った顔検出
locs = face_recognition.face_locations(img, model="cnn")
draw_faces(img, locs)

HOG 特徴量を使ったモデルでは検出できていなかった顔が CNN を使ったモデルでは検出できていることがわかります。

画像から顔の各部位を検出する

face_landmarks() で画像から鼻、眉毛、唇など顔を認識する上で重要となる以下の部位 (facial landmark) を検出できます。

英語 日本語
nose_bridge 鼻筋
nose_tip 鼻先
top_lip 上唇
bottom_lip 下唇
left_eye 左目
right_eye 右目
left_eyebrow 左眉毛
right_eyebrow 右眉毛
chin 下顎
face_recognition.face_landmarks(face_image, face_locations=None)
  • 引数
    • face_image: 画像
    • known_face_locations: face_locations() の返り値 locations を渡します。

返り値は [顔1の情報, 顔2の情報, ...] という list となっています。 各要素はキーが部位名、値が画像上の座標の list である dict となっています。

顔 i の情報 = {'パーツ名': [(x, y), (x, y), ...], 'パーツ名': [(x, y), (x, y), ...], ...}

sample3.jpg

In [5]:
from pprint import pprint

import face_recognition
import matplotlib.pyplot as plt
import numpy as np

# 画像を読み込む。
img = face_recognition.load_image_file("sample3.jpg")

# 画像から顔の領域を検出する。
face_locs = face_recognition.face_locations(img, model="cnn")

# 顔の各部位を検出する。
facial_landmarks = face_recognition.face_landmarks(img, face_locs)
#pprint(facial_landmarks)

# 日本語訳
jp_names = {'nose_bridge': '鼻筋',
            'nose_tip': '鼻先',
            'top_lip': '上唇',
            'bottom_lip': '下唇',
            'left_eye': '左目',
            'right_eye': '左目',
            'left_eyebrow': '左眉毛',
            'right_eyebrow': '右眉毛',
            'chin': '下顎'}

# 可視化する。
fig, ax = plt.subplots(figsize=(7, 7))
ax.imshow(img)
ax.set_axis_off()
for face in facial_landmarks:
    for name, points in face.items():
        points = np.array(points)
        ax.plot(points[:, 0], points[:, 1], 'o-', ms=3, label=jp_names[name])
ax.legend(fontsize=14)

plt.show()

コメント

コメントする

目次