Aidemy Tech Blog

機械学習・ディープラーニング関連技術の活用事例や実装方法をまとめる、株式会社アイデミーの技術ブログです。

OpenCVで乃木坂46秋元真夏と銀シャリ鰻和弘の類似度を調べてみた

初めまして、アイデミー研修生のだっちー(@dacciinfo)です。

仮想通貨のブログを書いているので「仮想通貨が大暴落したときのTwitter負の感情分析」みたいなことやろうかなーとざっくり思っていたのですが、ツイートの取得が1週間前までしか無理みたいなので断念しました。

 

そこで今回は好きな乃木坂46を取り上げたいと思います!

いきなりですが、このふたりをご存知でしょうか。

左が乃木坂46の秋元真夏さん、右が銀シャリの鰻和弘さんです。

f:id:h25e28:20180610190151p:plainf:id:h25e28:20180610151811p:plain


以前から、このふたりが似てるとネットで話題となっていました。

このふたりもブログやTwitterでコメントしています。

f:id:h25e28:20180610143603p:plain

鰻さんと真夏さんヽ(。・ω・。)ノ546 | 乃木坂46 秋元真夏 公式ブログ


確かに似てる感じしますね笑

そこで今回はOpenCVを用いて顔の類似点を調べ、どのくらい似ているのか調べたいと思います!

 

今回、プログラム書くにあたり参考とさせていただいたサイトは以下になります。

ありがとうございました。

OpenCVを使って誰の顔なのかを推定する(Eigenface, Fisherface, LBPH)

OpenCVを使った顔認識(Haar-like特徴分類器)

Fisherfaces


目次

使用するアルゴリズムと環境

今回、顔の類似度を調べるにあたり、使用したアルゴリズムは
Fisherfacesです。これはEigenfaceの改良版で、照明や角度の違いに影響されにくいといった特徴があります。
使用する画像からHaar-like特徴分類器を使用し、顔領域を抽出します。
その抽出した顔画像をFisherfaceで学習させ、未学習の画像と比較し、類似度を調べます。

環境は
Python 3.6.5
opencv 3.3.1

秋元さん15枚の画像を学習データとし、未学習の画像である鰻さんの画像をテストデータとします。鰻さんの比較対象として乃木坂メンバー数人の画像もテストデータとして用意します。
f:id:h25e28:20180614185515p:plain

f:id:h25e28:20180614191006p:plain

プログラム

import cv2
import os
import numpy as np
from PIL import Image

#トレーニング画像
train_path = './train_images'

#テスト画像
test_path = './test_images'

#Haar-like特徴分類器
cascadePath = "/Users/ユーザー名/Downloads/opencv-3.4.1/opencv-3.4.1/data/haarcascades/haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascadePath)

#FisherFace
#recognizer = cv2.face_FisherFaceRecognizer.create()
recognizer = cv2.face_LBPHFaceRecognizer.create()

#pathしたフォルダ内の画像を習得
def get_images_and_labels(path):
    #画像を格納する配列
    images = []
    #ラベルを格納する配列
    labels = []
    #ファイル名を格納する配列
    files = []
    for f in os.listdir(path):
        #画像のパス
        image_path = os.path.join(path, f)
        #グレースケールで読み込み
        image_pil = Image.open(image_path).convert('L')
        #Numpyの配列に格納
        image = np.array(image_pil, 'uint8')
        #Haar-like特徴分類器で顔を検知
        faces = faceCascade.detectMultiScale(image)
        #検出した画像の処理
        for(x, y, w, h) in faces:
            #200×200にリサイズ
            roi = cv2.resize(image[y: y + h, x: x + w], (200, 200), interpolation=cv2.INTER_LINEAR)
            #画像を配列に格納
            images.append(roi)
            #ファイル名からラベルを取得
            labels.append(int(f[7:9]))
            #ファイル名を配列に格納
            files.append(f)
    
    return images, labels, files

#トレーニング画像を取得
images, labels, files = get_images_and_labels(train_path)

#トレーニング実施
recognizer.train(images, np.array(labels))

#テスト画像を取得
test_images, test_labels, test_files = get_images_and_labels(test_path)

i=0
while i < len(test_labels):
    #テスト画像に対して予測実施
    label, confidence = recognizer.predict(test_images[i])
    #予測結果をコンソール出力
    print("Test Image: {}, Predicted Label: {}, Confidence: {}".format(test_files[i], label, confidence))
    #テスト画像表示
    cv2.imshow("test image", test_images[i])
    cv2.waitKey(300)
    i += 1

#終了処理
cv2.destroyAllWindows()

実行結果はこちらです。

Test Image: subject01.akimoto.png, Predicted Label: 1, Confidence: 48.77426502706794
Test Image: subject01.asuka.jpg, Predicted Label: 1, Confidence: 54.37620231697899
Test Image: subject01.hori.jpg, Predicted Label: 1, Confidence: 53.24049819391969
Test Image: subject01.ikuta.jpg, Predicted Label: 1, Confidence: 56.262042555618166
Test Image: subject01.nishino.png, Predicted Label: 1, Confidence: 50.30127169485847
Test Image: subject01.shiraishi.jpg, Predicted Label: 1, Confidence: 72.06184327769823
Test Image: subject01.unagi.png, Predicted Label: 1, Confidence: 63.37511081588498

subject01の「01」をラベルに設定し、学習しています。複数の画像を学習させる際は、この数値を変更してください。学習した画像は今回秋元さんのみなのでラベルも1のみです。
分散に基づいて計算されているため、Confidence(確度)は0に近い方が確度が高くなります。
学習に使用した画像をテストで使用すると特徴点が一致するため、Confidenceは0です。

未学習の秋元さんの画像は48.77、もちろんこの中で一番近い値です。
類似度順に並び替えると

f:id:h25e28:20180614192751j:plainf:id:h25e28:20180610151811p:plainf:id:h25e28:20180614192801j:plainf:id:h25e28:20180614192754j:plainf:id:h25e28:20180614193458p:plainf:id:h25e28:20180614192805p:plainf:id:h25e28:20180610190151p:plain
白石<鰻<生田<齋藤<堀<西野<秋元

のようになりました。ちなみにニャンちゅうは顔認識されず対象外となってしまいました。

学習データを変更

こちらは秋元さんが番組で平野ノラさんのモノマネをしたときのものです。

f:id:h25e28:20180614194754j:plain


この番組でのモノマネした秋元さんの画像をできるだけ集めて学習させます。あとは先ほどと同様です。
f:id:h25e28:20180614195153p:plain


実行結果

Test Image: subject01.akimoto.png, Predicted Label: 1, Confidence: 64.72162690757675
Test Image: subject01.asuka.jpg, Predicted Label: 1, Confidence: 70.898033127977
Test Image: subject01.hori.png, Predicted Label: 1, Confidence: 71.68987057398674
Test Image: subject01.ikuta.jpg, Predicted Label: 1, Confidence: 72.3624149846466
Test Image: subject01.nishino.png, Predicted Label: 1, Confidence: 71.81215441739869
Test Image: subject01.shiraishi.jpg, Predicted Label: 1, Confidence: 78.99918092527798
Test Image: subject01.unagi.png, Predicted Label: 1, Confidence: 65.89760962981548

f:id:h25e28:20180614192751j:plainf:id:h25e28:20180614192801j:plainf:id:h25e28:20180614192805p:plainf:id:h25e28:20180614192757j:plainf:id:h25e28:20180614192754j:plainf:id:h25e28:20180610151811p:plainf:id:h25e28:20180610190151p:plain
白石<生田<西野<堀<齋藤<鰻<秋元

という結果になりました。
学習データを鰻さんに似ているモノマネした秋元さんの画像に変更することでより近い結果となりました。

学習データの追加

次は、先ほどの乃木坂メンバー白石、生田、西野、堀、齋藤の画像10枚学習させます。
もちろん、テストデータとは違うものです。

f:id:h25e28:20180615211357p:plain

ラベルは01が秋元、02が齋藤、03が堀、04が生田、05が白石、06が西野、07が鰻です。

実行結果

Test Image: subject01.akimoto.jpg, Predicted Label: 1, Confidence: 21.786044627006834
Test Image: subject02.asuka.jpg, Predicted Label: 2, Confidence: 51.351808634017296
Test Image: subject03.hori.jpg, Predicted Label: 3, Confidence: 44.75424378786359
Test Image: subject04.ikuta.jpg, Predicted Label: 4, Confidence: 45.70965779143789
Test Image: subject05shiraishi.jpg, Predicted Label: 5, Confidence: 49.08969544979982
Test Image: subject06nishino.jpg, Predicted Label: 6, Confidence: 43.72499865726888
Test Image: subject07.unagi.png, Predicted Label: 1, Confidence: 63.37511081588498

各乃木坂メンバーはsubjectとPredicted Labelが同じであるため、自分の顔の学習データとテストデータが一致していることが分かります。
鰻さんは学習させていないですが、Predicted Labelが1となりました。これは学習データの中から秋元さんの顔に近いと判断され、ラベル1に振り分けられました。

考察と感想

今回は、Haar-like特徴分類器を使って顔領域を抽出、Fisherfaceで学習、テスト、結果表示という流れで類似度を調べました。ブログに載せた以外にもAKAZE特徴量を使用した特徴点マッチングで類似度を試みましたが上手くいきませんでした。
学習データを変更、追加することでより近い結果になったと思います。今回は比較対象として乃木坂の数人を挙げましたが、少なくともこのメンバーの中では秋元さんが一番鰻さんに似ていることが分かりました。
Pythonにふれてまだ3週間で、まだまだ未熟ではありますがこれから自分で色々実装してみたいと思います。