Pynote

Python、機械学習、画像処理について

画像処理 - face_recognition ライブラリで顔検出を試す。

概要

顔認識が行える face_recognition ライブラリを紹介する。
dlib という機械学習ライブラリをラップする形で構築されており、最先端の顔認識技術を簡単に利用することができる。

インストール

pip install face_recognition

インストール時の dlib のビルドに cmake が必要だったので、cmake がなくてビルドがコケた場合は入れておく。

sudo apt-get install -y cmake

顔検出

基本的な使い方

face_locations() で顔検出を行える。

ret = face_recognition.face_locations(img, number_of_times_to_upsample=1, model='hog')

返り値は検出された顔の数分のリストであり、各要素は矩形を表す (上の y 座標、右の x 座標、下の y 座標、左の x 座標) の tuple となっている。
number_of_times_to_upsample 引数は画像をアップサンプリングする回数であり、デフォルトで1となっている。アップサンプリングを行うことで小さい顔も検出できるようになるが、その分処理が重くなるので、デフォルトの値で検出できない小さい顔があった場合は値を2, 3 と増やすといいかもしれない。

画像のソース: 無料の写真素材はフリー素材のぱくたそ

[顔1の情報, 顔2の情報, ...]
顔 i の情報 = (top, right, bottom, left)
import face_recognition
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy as np


def draw_face_locations(img, locations):
    fig, ax = plt.subplots(figsize=(7, 7))
    ax.imshow(img)
    ax.set_axis_off()
    for top, right, bottom, left in locations:
        print('face (Top: {}, Left: {}, Bottom: {}, Right: {})'.format(
            top, left, bottom, right))
        # 矩形を描画する。
        w, h = right - left, bottom - top
        rect = mpatches.Rectangle(xy=(left, top), width=w, height=h,
                                  ec='orange', lw=3, fill=None)
        ax.add_patch(rect)
    plt.show()


# 画像を読み込む。
img = plt.imread('sample.jpg')

# 顔検出する。
locations = face_recognition.face_locations(img)

# 可視化する。
draw_face_locations(img, locations)


face (Top: 106, Left: 305, Bottom: 142, Right: 341)
face (Top: 94, Left: 204, Bottom: 137, Right: 247)

検出に CNN を使う。

face_locations() の model 引数で検出手法を設定できる。
デフォルトでは model='hog' となっており、HOG 特徴量による顔検出を行う。
model='cnn' とすることで、より高精度な CNN による検出に切り替えることができる。
GPU がない場合、CNN の検出は HOG 特徴量による検出より多くの時間がかかってしまうので、注意すること。


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

# 画像を読み込む。
img = plt.imread('sample.jpg')

# HOG 特徴量を使った顔検出
locations = face_recognition.face_locations(img)
draw_face_locations(img, locations)

# CNN を使った顔検出
locations = face_recognition.face_locations(
    img, number_of_times_to_upsample=0, model='cnn')
draw_face_locations(img, locations)


HOG 特徴量では検出できていなかった顔が CNN では検出できていることがわかる。

顔の各パーツの検出

face_landmarks() で顔ランドマーク (facial landmark) の検出を行える。
顔ランドマークとは、鼻、眉毛、唇など顔を認識する上で重要となる各パーツのことである。

ret = face_recognition.face_landmarks(face_image, face_locations=None, model='large')

返り値は検出された顔の数分のリストであり、各要素はキーがパーツ名、値が画像上の座標値のリストである dict となっている。

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

画像のソース: 無料の写真素材はフリー素材のぱくたそ

from pprint import pprint

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

# 画像を読み込む。
img = plt.imread('sample.jpg')

# 顔の特徴点 facial landmarks を検出する。
landmarks = face_recognition.face_landmarks(img)
pprint(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 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()
[{'bottom_lip': [(408, 411),
                 (386, 439),
                 (364, 449),
                 (348, 450),
                 (334, 446),
                 (318, 435),
                 (305, 409),
                 (311, 413),
                 (334, 433),
                 (348, 437),
                 (363, 436),
                 (402, 414)],
  'chin': [(256, 330),
           (256, 354),
           (260, 377),
           (268, 400),
           (282, 421),
           (300, 441),
           (318, 461),
           (337, 480),
           (358, 487),
           (382, 485),
           (405, 472),
           (427, 457),
           (447, 437),
           (461, 412),
           (466, 383),
           (467, 353),
           (468, 323)],
  'left_eye': [(278, 323),
               (289, 316),
               (303, 318),
               (315, 329),
               (302, 329),
               (288, 328)],
  'left_eyebrow': [(263, 297), (277, 292), (294, 294), (310, 297), (325, 303)],
  'nose_bridge': [(342, 325), (341, 348), (341, 371), (340, 393)],
  'nose_tip': [(323, 395), (333, 400), (345, 405), (356, 400), (367, 397)],
  'right_eye': [(375, 328),
                (386, 316),
                (401, 315),
                (416, 322),
                (403, 327),
                (388, 328)],
  'right_eyebrow': [(361, 300), (379, 291), (399, 288), (420, 288), (438, 296)],
  'top_lip': [(305, 409),
              (319, 409),
              (333, 411),
              (347, 414),
              (361, 412),
              (383, 412),
              (408, 411),
              (402, 414),
              (361, 419),
              (347, 420),
              (333, 417),
              (311, 413)]}]

顔の位置が判明している場合、face_locations に渡すことでその中から顔ランドマークを検出できる。

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

# 画像を読み込む。
img = plt.imread('sample.jpg')

# 顔の位置を検出する。
locations = face_recognition.face_locations(img)

# 顔の特徴点 facial landmarks を検出する。
landmarks = face_recognition.face_landmarks(img, face_locations=locations)