DeepFaceによる顔認識

DeepFaceGitHub)という顔認識ライブラリを使ってみよう。

pip install deepface でインストールできる(はずである)。

使い方の基本は、

from deepface import DeepFace

r = DeepFace.represent(img_path=画像ファイルのパスまたはBGR形式の画像,
                       model_name=モデル,
                       enforoce_detection=False,
                       detector_backend=顔検出器)

とすれば顔情報のリストが返る。画像ファイルのパスを渡すのが簡単だが、日本語ファイル名だとうまくいかないので、以下では自前で読み込んでから渡す。モデルは "VGG-Face" がデフォルトだが、"Facenet512" のほうが性能が良さそうである。enforce_detection=False を付けないと、顔が検出されないときエラーになる。顔検出器は "opencv" がデフォルトだがあまり賢くないので以下では "mtcnn" を使う。

まずはフォルダにいろいろ写真を入れてやってみよう。

import pathlib

path = pathlib.Path("/path/to/dir")
names = [p for p in path.iterdir() if p.match("*.jp*g")] # .jpg .jpeg
# あるいは import glob で name = glob.glob("/path/to/dir/*.jp*g")

でファイルパス一覧を読み出す。ここで日本語ファイル名があると後で失敗する。

まず、それぞれのファイルについて、埋め込みベクトルを計算する:

from deepface import DeepFace
import cv2
import matplotlib.pyplot as plt
import os

embeddings = []
basenames = []
for name in names:
    basename = os.path.splitext(os.path.basename(name))[0]
    img = cv2.imread(name)
    rs = DeepFace.represent(img_path=img,
                            model_name="Facenet512",
                            enforce_detection=False,
                            detector_backend="mtcnn")
    color = [0, 255, 0]
    for r in rs:
        print(r['face_confidence'], end=" ")
        if r['face_confidence'] > 0.9:
            embeddings.append(r['embedding'])
            basenames.append(basename)
            f = r['facial_area']
            cv2.rectangle(img,
                          (f['x'], f['y']), (f['x']+f['w'], f['y']+f['h']),
                          color, 2)
            color = color[1:] + color[:1]
    print(basename)
    # cv2.imshow("Image", img) または
    plt.figure()
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    plt.axis("off")
    plt.show()

距離行列を表示する:

import numpy as np

def distance(a, b): # コサイン距離
    return max(1 - np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)), 0)

# 距離行列(0.6より大きければ * を付ける)
for e1 in embeddings:
    for e2 in embeddings:
        d = distance(e1, e2)
        if d > 0.6:
            print(f" {d:4.2f}*", end="")
        else:
            print(f" {d:4.2f} ", end="")
    print()

クラスタリングのためのデンドログラムを描く:

from scipy.cluster.hierarchy import dendrogram, linkage

n = len(embeddings)
condensed_dist_mat = np.array([distance(embeddings[i], embeddings[j])
                               for i in range(n) for j in range(i+1, n)])
linked = linkage(condensed_dist_mat)
plt.figure(figsize=(8, 4))
dendrogram(linked, labels=basenames, orientation="left")
plt.show()

linkage()method="single|complete|average" などのオプションを与えることができる(デフォルトは single)。