顔認証の技術などでも使われる顔の類似度判定を、OpenCVを使い追ってみたいと思います。今回は顔の特徴点を比較して類似度判定をしますが、人間が見て似ている・似ていないを判定するのと、どのくらい異なるのかも一緒に調べてみました。

チャレンジ&ナレッジ最終更新日: 20210928

OpenCVのAKAZEで顔写真の類似度判定をやってみた

  • このエントリーをはてなブックマークに追加

顔認証の技術などでも使われる顔の類似度判定を、OpenCVを使い追ってみたいと思います。今回は顔の特徴点を比較して類似度判定をしますが、人間が見て似ている・似ていないを判定するのと、どのくらい異なるのかも一緒に調べてみました。

100人にアンケートをとって似ているランキングを作成

今回、この顔写真をターゲットとし、比較対象の顔写真の類似度を出していきたいと思います。

比較対象として、10名分の顔写真を用意しました。
まずは、ターゲットの顔写真と比較対象をアンケート回答者100名に見比べてもらい、似ているランキングを作成してみました。

回答者には、「全く似ていない(1点)」「あまり似ていない(2点)」「どちらとも言えない(3点)」「少し似ている(4点)」「とても似ている(5点)」の5段階で評価してもらいました。各写真で平均値をとり、値が大きい順に並べたランキングが以下の通りです。

上記と類似度判定によるランキングがどのように変わってくるかを比べてみたいと思います。

特徴量マッチング

今回、顔の類似度判定を行うために特徴量のマッチングを行います。特徴量のマッチングはOpenCVのAKAZE(Accelerated KAZE)特徴量検出を用います。特徴量検出は、名前の通り、画像の中から特徴的な部分を抽出するアルゴリズムです。用いられる特徴としては角(コーナー)や輝度の勾配などが多いそうです。

コーナーを検出して特徴点とするアルゴリズムの1つにSIFT(scale-invariant feature transformation)があります。これを改良し、OpenCVに組み込んだものがAKAZEです。

このAKAZEを用いて、検出した特徴点と比較する写真の全ての特徴点を比べて距離を計算すること(Brute-Force Matcher)によりどれだけ似ているかを判定します。

AKAZE local features matching
https://docs.opencv.org/4.0.0/db/d70/tutorial_akaze_matching.html
Feature Matching
https://docs.opencv.org/4.0.0/dc/dc3/tutorial_py_matcher.html
SIFT 提案論文
https://ieeexplore.ieee.org/document/790410

OpenCVのインストールは以下のコマンドで行うことができます。

pip install opencv-contrib-python

特徴量の検出結果を顔写真に描画すると以下のようになります。

上記の描写は以下のコードで行うことができます。

#OpenCVをインポート
import cv2  

# 1枚目の画像を読み込む
img1 = cv2.imread(ファイル名) 
# 2枚目の画像を読み込む
img2 = cv2.imread(ファイル名) 

# 1枚目の画像をグレースケールで読み出し
gray1 = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY) 
# 2枚目の画像をグレースケールで読み出し
gray2 = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY) 

# AKAZE検出器の生成
akaze = cv2.AKAZE_create() 
# gray1にAKAZEを適用、特徴点を検出
kp1, des1 = akaze.detectAndCompute(gray1,None) 
# gray2にAKAZEを適用、特徴点を検出
kp2, des2 = akaze.detectAndCompute(gray2,None) 

# BFMatcherオブジェクトの生成
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)

# Match descriptorsを生成
matches = bf.match(des1, des2)

# matchesをdescriptorsの似ている順にソートする 
matches = sorted(matches, key = lambda x:x.distance)

# 検出結果を描画する
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags = cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)

#検出結果を描画した画像を出力する
cv2.imwrite(ファイル名,img3)

類似度判定

それでは、類似度判定を行なっていきます。特徴量を抽出するところまでは、ほとんど変わりません。特徴量を抽出後、Brute-Force Matcherにより総当たりマッチングを行い、距離の平均を取ります。

類似度判定は以下のように行います。比較対象の画像はimagesという名前のフォルダにまとめて入れ、ファイル名は1.png、2png、3.png…としました。

#OpenCVとosをインポート
import cv2
import os
 
TARGET_FILE = [ターゲットの顔写真のファイル名]
IMG_DIR = os.path.abspath(os.path.dirname(__file__)) + 'images/'
IMG_SIZE = (200, 200)

target_img_path = IMG_DIR + TARGET_FILE
#ターゲット画像をグレースケールで読み出し
target_img = cv2.imread(target_img_path, cv2.IMREAD_GRAYSCALE)
#ターゲット画像を200px×200pxに変換
target_img = cv2.resize(target_img, IMG_SIZE)

# BFMatcherオブジェクトの生成
bf = cv2.BFMatcher(cv2.NORM_HAMMING)

# AKAZEを適用、特徴点を検出
detector = cv2.AKAZE_create()
(target_kp, target_des) = detector.detectAndCompute(target_img, None)

print('TARGET_FILE: %s' % (TARGET_FILE))

files = os.listdir(IMG_DIR)
for file in files:
    if file == '.DS_Store' or file == TARGET_FILE:
        continue
    #比較対象の写真の特徴点を検出
    comparing_img_path = IMG_DIR + file
    try:
        comparing_img = cv2.imread(comparing_img_path, cv2.IMREAD_GRAYSCALE)
        comparing_img = cv2.resize(comparing_img, IMG_SIZE)
        (comparing_kp, comparing_des) = detector.detectAndCompute(comparing_img, None)
    # BFMatcherで総当たりマッチングを行う
        matches = bf.match(target_des, comparing_des)
        #特徴量の距離を出し、平均を取る
        dist = [m.distance for m in matches]
        ret = sum(dist) / len(dist)
    except cv2.error:
        # cv2がエラーを吐いた場合の処理
        ret = 100000

    print(file, ret)

上記を実行すると、以下のような出力がされます。距離の平均が近い方が類似度が高いため、値が小さい方が類似度が高いということになります。

TARGET_FILE: target.png
8.png 126.52380952380952
9.png 119.95238095238095
10.png 142.92857142857142
4.png 138.02380952380952
5.png 132.0
7.png 133.54761904761904
6.png 124.04761904761905
2.png 134.42857142857142
3.png 126.66666666666667
1.png 127.02380952380952

先ほどのように顔写真と合わせてランキングにしてみます。

人間によるアンケート結果のランキングとはかなり順位が入れ替わってしまう結果となりました。

筆者自身も人間なので、直感的な考察にはなりますが、各ランキングを眺めてみると、アンケート結果の方は少なからず、写真自体の雰囲気や髪型、服装といったところにも左右されているのではないかと思いました。もちろん、類似度判定のほうも顔の向きなどに影響を受けていると考えられますが、顔自体の類似度でいうと、アンケート結果よりも正確そうな気がしました。

まとめ

今回は、OpenCVのAKAZEを使って顔写真の類似度判定を行いました。

OpenCVを使うことでディープラーニングなどを使わずにかなり手軽に類似度判定ができることがわかりました。今回は、フリー素材の写真を使って類似度判定を行ったため、顔の向きが揃っていない写真も含まれており、比較が難しかった印象があります。記事内で行うのは難しいかもしれませんが、卒業アルバムの写真などを使って類似度判定を行うと、比較がかなりしやすいのではないかと思いました。

比較画像さえあれば簡単に実装ができるので、ぜひみなさんも類似度判定にチャレンジしてみてください!

ranranプロフィール画像
ライタープロフィール ranran

株式会社メンヘラテクノロジー CEO
女性向けチャット相談サービス「メンヘラせんぱい」
作ってます
https://menhera-senpai.com/

記事を
シェア