01.07 DeepFace ~顔検索(find) STEP1~
前回DeepFaceは画像データの中から顔をキャプシャーし、前処理と共に顔ベクトルに変換するところまでを詳細に分析してきました。顔ベクトルと、その可視化に伴う副産物として、QRコードを作成してみました。
その前では、顔ベクトルを使用して2つの画像を比較する「照合」を行いました。
この「照合」には、ディープラーニング入門編でやりました類似度を計算します。例えばモデルFaceNetはユークリッド距離を計算し、閾値を基準として同一人物を見極めます。つまり、サンプル間の距離(metric)や類似度(similarity)をベクトル間で計算してマッチングを行っています。
DeepFaceのデフォルト類似度(metrics)「"cosine"」ラップを例にとると、これは「コサイン類似度計算」を示します。
計算式は下記となります。
実際には
⦿は2つのベクトルの内積で求めます。
この計算結果は、ベクトル長が変化しても必ず結果は一致領域内に収まり、一致した場合は「0」となるため、これが類似度となるわけです。
★ 顔検索(find)
顔照合が出来たということは、次の課題は「顔検索」=「顔認識」です。以前との違いは、対象が複数個のデータベース化された中から似た顔画像を探し出します。
これから検索の難易度に応じて段階的に試験を行っていきます。
ステップ1は「顔検索」です。
以前感情分析編動体検知用に某アイドルグループの写真を10名分ネットからダウンロードして次のような条件で保管されています。
【保管条件】
・ フォルダー「nogi01」~「nogi10」で人物別に保管されています。
・ フォルダー内には同じ人物が単独で写っている画像をネットから集めました。
・ 画像ファイルの形式・写真サイズ・ファイル名の長さに縛りはありません。
・ 全身・顔のみが混在しています。
・ 全部でファイル数は1489枚あります。
★ 試験条件とプログラム
【試験条件と仕様】
・ 検索対象
フォルダー内にある下の写真をデータベース内から探します。
「"c:\\Photo\\nogi01-kaki.jpg"」
・ 対象データベース
「d:\\VisualStudio2017\\Python3.5_GPU\\test_dataset\\DeepFac」
【DeepFace.findメソッド】
データベース内の個人を識別します。
【引数】
img_path(strまたはnp.ndarray) :
画像への正確なパス、BGR形式のnumpy配列、または base64
でエンコードされた画像。ソース画像に複数の顔が含まれている場合、結果は検出された各顔の情報を含めます。
db_path (文字列) : 画像ファイルを含むフォルダーへのパス。
検出されたすべての顔データベースでは、意思決定プロセスで考慮されます。
model_name (str) : 顔認識のモデル。
オプション:VGG-Face、Facenet、Facenet512、OpenFace、DeepFace、DeepID、Dlib、ArcFace、SFace
(デフォルトは VGG-Face)。
distance_metric (文字列) : 類似性を測定するためのメトリック。
オプション: 'cosine'、'euclidean'、'euclidean_l2' (既定値は cosine)。
enforce_detection (ブール値) : 画像内で顔が検出されない場合、例外を発生させます。
低解像度の画像の例外を回避するには、False に設定します (既定値は True)。
detector_backend (文字列) : 顔検出バックエンド。
オプション: 'opencv', 'retinaface','mtcnn', 'ssd', 'dlib', 'mediapipe', 'yolov8'
(デフォルトは opencv)。
align (boolean) : 目の位置に基づいて位置合わせを実行します
(既定値は True)。
expand_percentage (int) :検出された顔の領域をパーセンテージで拡大します
(既定値は 0)。
threshold (float) : ペアが同じものを表しているかどうかを判断する閾値を指定します
人または異なる個人。この閾値は、距離の比較に使用されます。未設定のままにすると、指定されたモデル名と距離メトリック(デフォルトはなし)。
normalization (文字列) : 入力画像をモデルに供給する前に正規化します。
オプション: base、raw、Facenet、Facenet2018、VGGFace、VGGFace2、ArcFace(デフォルトはbase)。
silent (ブール値) : より静かな分析プロセスのために、一部のログメッセージを抑制または許可します
(既定値は False です)。
【注意点】
今までのDeepFace出力結果(戻り値)とは異なり「pandas」ライブラリのデータフレームで出力されます。
pandasはpythonのデータ解析を支援する機能を提供するライブラリです。冒頭のインストールリストに入れておきましたが、インストールされていない方はインストールしておいてください。
また、本サイト「未来の技術編」でもDeepFace.findメソッドサンプルプログラムを作成しましたが、出力値が旧DeepFaceバージョンからリスト型pandasに変更されているため、公式サイトおよび本サイトにあるサンプルプログラムではエラーになってしまいます。新しいバージョンで実施する場合は、下記のように変更してください。
【DF_FaceFind_test01.py】
#coding: utf-8
# ////////////////////////////////////////////////////////////////////////////
# ///【DeepFace・顔検索】
# ////////////////////////////////////////////////////////////////////////////
if "__file__" in globals():
import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
sys.path.append(os.path.join(os.path.dirname(__file__), "..") + "//..//"+ "//..//")
os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'
from DeZero.common.nlp_util import *
import cv2
import pandas as pd
import datetime
from deepface import DeepFace
from tabulate import tabulate
tensorflow_warning_cancel()
if __name__ == "__main__":
StartTime = datetime.datetime.now()
print("STEP01 ≫ ", datetime.datetime.now() , " → ", datetime.datetime.now() - StartTime)
# /// 顔認識モデル(models)
# ////////////////////////////////////////////////////////////////////////////
models = [
"VGG-Face",
"Facenet",
"Facenet512",
"OpenFace",
"DeepFace",
"DeepID",
"ArcFace",
"Dlib",
"SFace",
]
# /// バックエンド(backends)
# ////////////////////////////////////////////////////////////////////////////
backends = [
'opencv',
'ssd',
'dlib',
'mtcnn',
'retinaface',
'mediapipe',
'yolov8',
'yunet',
'fastmtcnn',
]
# /// 顔検索(find)
# ////////////////////////////////////////////////////////////////////////////
img1_path = "c:\\Photo\\nogi01-kaki.jpg"
db_path = "d:\\VisualStudio2017\\Python3.5_GPU\\test_dataset\\DeepFace"
print("STEP02 ≫ ", datetime.datetime.now() , " → ", datetime.datetime.now() - StartTime)
list_df = DeepFace.find(
img_path = img1_path, # img_path: Union[str, np.ndarray]
db_path = db_path, # db_path: str
model_name = models[0], # model_name: str = "VGG-Face"
distance_metric = metrics[0], # distance_metric: str = "cosine"
enforce_detection=False, # enforce_detection: bool = True
detector_backend = backends[0], # detector_backend: str = "opencv"
align=True, # align: bool = True
expand_percentage=0, # expand_percentage: int = 0
threshold=None, # threshold: Optional[float] = None
normalization=normalizations[0], # normalization: str = "base"
silent=False # silent: bool = False
)
df = list_df[0]
print("STEP03 ≫ ", datetime.datetime.now() , " → ", datetime.datetime.now() - StartTime)
pd.set_option('display.max_rows', 1000)
pd.set_option('display.max_columns', 4096)
df.head()
dataframe = pd.DataFrame(df)
print("STEP04 ≫ ", datetime.datetime.now() , " → ", datetime.datetime.now() - StartTime)
print(tabulate(dataframe, headers = 'keys', tablefmt = 'pretty', colalign=('center','left','center')))
★ 動作結果
動作結果は「pandas」のデータフレームリスト(List[pd.DataFrame]) でpandas データフレームに対応しています、また、起動後最初の一回目のみデータベース・ソース画像のID情報変更作業が行われるため、少々処理時間かかるのとかなりの量の進捗コメントが表示されます。
【戻り値】
- 'identity' :検出された個人の ID ラベル。
(ファイル名は絶対パス付ですが長いので省略)
- 'target_x', 'target_y', 'target_w',
'target_h' : バウンディングボックス座標、データベース内のターゲットの顔。
- 'source_x', 'source_y', 'source_w', 'source_h' :バウンディングボックス座標、ソース画像で検出された顔。
- 'threshold' : ペアを同一人物か異なる人物かを判断するための閾値
- 'distance' : 以下に基づく面間の類似性スコア指定されたモデルと距離メトリック
# [result]
# (py39) d:\VisualStudio2017\Python3.5_GPU\Sample_TEST\顔認識2024\03_DF_FaceFind>python DF_FaceFind_test01.py
# STEP01 ≫ 2024-03-27 11:25:42.436774 → 0:00:00
# STEP02 ≫ 2024-03-27 11:25:42.439774 → 0:00:00.003000
# 24-03-27 11:25:44 - Searching c:\Photo\nogi01-kaki.jpg in 1505 length datastore
# 24-03-27 11:25:47 - find function duration 4.84630012512207 seconds
# STEP03 ≫ 2024-03-27 11:25:47.343559 → 0:00:04.906785
# STEP04 ≫ 2024-03-27 11:25:47.344559 → 0:00:04.907785
# +-----+----------------------------------------------------------------------------------------------------------------------+------------------------------------------+----------+----------+----------+----------+----------+----------+----------+----------+-----------+---------------------+
# | | identity | hash | target_x | target_y | target_w | target_h | source_x | source_y | source_w | source_h | threshold | distance |
# +-----+----------------------------------------------------------------------------------------------------------------------+------------------------------------------+----------+----------+----------+----------+----------+----------+----------+----------+-----------+---------------------+
# | 0 | nogi01\index.jpg | 7ddc637a7554ba41c2ecbfb646ac955c8a615de1 | 71 | 49 | 50 | 50 | 124 | 87 | 80 | 80 | 0.68 | 0.1830687137044812 |
# | 1 | nogi01\1554609.jpg | fc3b3b9b072257d2a4e55710100cbcea58c4f5f7 | 142 | 40 | 68 | 68 | 124 | 87 | 80 | 80 | 0.68 | 0.28360544030705803 |
# | 2 | nogi01\HK_38-thumb-550xauto-250780.jpg | 150f71720a9bf2cc85c260e0263acf68fc94a02a | 123 | 21 | 53 | 53 | 124 | 87 | 80 | 80 | 0.68 | 0.30305929319280744 |
# | 3 | nogi01\737671.jpg | f66ce0a2b98902e66f70391bb5c7f95c7f2b16bb | 46 | 39 | 108 | 108 | 124 | 87 | 80 | 80 | 0.68 | 0.329093685963859 |
# | 4 | nogi01\1.jpg | 7739b216a4a0df6588172221383261ddddec261f | 73 | 43 | 106 | 106 | 124 | 87 | 80 | 80 | 0.68 | 0.34039565440839015 |
# | 5 | nogi07\1610451214.jpg | 838e126b12663539a43d8d734c9007d5df974c1b | 63 | 37 | 59 | 59 | 124 | 87 | 80 | 80 | 0.68 | 0.34090774093169873 |
# | 6 | nogi08\250px-2021E5B9B4E4B983E69CA8E59D8246E38397E383ADE38395E382A3E383BCE383AB_E7AD92E4BA95E38182E38284E38281_4.jpg | f9e173dc05f8c86d36c2674d952788bb62c3aa27 | 57 | 47 | 124 | 124 | 124 | 87 | 80 | 80 | 0.68 | 0.36282107129761143 |
# | 7 | nogi01\P20200620-k008.jpg | 8fbfd7c686b4964d0ce081cd9867665302c2da81 | 110 | 32 | 75 | 75 | 124 | 87 | 80 | 80 | 0.68 | 0.3648022909914431 |
# | 8 | nogi01\52b9bc52c1a86305a6a443a95b5a63cd-904x1024.jpg | 225761d6011ad0e73c90fdd52fe4bc9894b4943b | 74 | 31 | 81 | 81 | 124 | 87 | 80 | 80 | 0.68 | 0.36502568641487976 |
# | 9 | nogi01\i-img1200x900-1650717113mgsffn333752.jpg | 9ac7cc119af65fa431108ef4fa0ff6860026b075 | 81 | 39 | 63 | 63 | 124 | 87 | 80 | 80 | 0.68 | 0.3791875529338382 |
# | 10 | nogi01\g1050475477.1.jpg | d39d5f8e6d1c0102eec6263f22e5bdc1f4c48c2e | 66 | 44 | 86 | 86 | 124 | 87 | 80 | 80 | 0.68 | 0.37972646470649973 |
# | 11 | nogi08\202107270000057-w500_1.jpg | 1705e47e7e86534c0495aa997e9768dbac0e561d | 96 | 40 | 63 | 63 | 124 | 87 | 80 | 80 | 0.68 | 0.3837098739163778 |
# | 12 | nogi01\ab6dc38b19e58282b2045c490d044228.jpg | 55f9671d531f7a1c71798db3be0f38532231a696 | 45 | 44 | 74 | 74 | 124 | 87 | 80 | 80 | 0.68 | 0.3887246866145929 |
# | 13 | nogi01\m42746503530_1.jpg | e33f895585671f475fde875ee85f053fec41f5f2 | 72 | 55 | 69 | 69 | 124 | 87 | 80 | 80 | 0.68 | 0.39096322031819253 |
# | 14 | nogi05\10413871.jpg | 2743168b579072fbf74d7bd15ca2e0d43f1c37a6 | 67 | 42 | 67 | 67 | 124 | 87 | 80 | 80 | 0.68 | 0.39111674567962873 |
# | 15 | nogi01\teaser_nogizaka46.jpg | ac5539980f312538d2e1891e0eb4de1638023fa8 | 72 | 34 | 61 | 61 | 124 | 87 | 80 | 80 | 0.68 | 0.39486816274062075 |
# | 16 | nogi01\m32842030688_3.jpg | 1ec3a8772cc4ca14814d30dc3b23187c0c4270cb | 76 | 45 | 47 | 47 | 124 | 87 | 80 | 80 | 0.68 | 0.39588263639535226 |
# | 17 | nogi01\h1029523714.1.jpg | 53e655ebc6eaaac030c732efc9f41548a87a8b42 | 67 | 42 | 77 | 77 | 124 | 87 | 80 | 80 | 0.68 | 0.39855942896872554 |
# | 18 | nogi04\1408172_650.jpg | 26794a3f5150bc0e2a0258681dfbf830313bdf25 | 84 | 37 | 97 | 97 | 124 | 87 | 80 | 80 | 0.68 | 0.39873161995244133 |
# | 19 | nogi08\E7AD92E4BA95E38182E38284E38281E381AEE9AB98E6A0A1.jpg | ab1b255e58f08321a4ac6ccacf6c03dbc018e6ad | 88 | 9 | 154 | 154 | 124 | 87 | 80 | 80 | 0.68 | 0.39892893505491134 |
# | 20 | nogi03\kakehashisayaka_art202108.jpg | 0ca2124faec45a77d93bb58a1f754f5cc075e355 | 68 | 44 | 56 | 56 | 124 | 87 | 80 | 80 | 0.68 | 0.4037229756035562 |
# | 21 | nogi01\img_901915c5558ca82bf18c8705563d77a8315278.jpg | 243b798a8dff379f611989f27704aae26510cc69 | 123 | 33 | 51 | 51 | 124 | 87 | 80 | 80 | 0.68 | 0.40454837454659054 |
# | 22 | nogi08\n0rDebc5jHgvTU0vornUJGaENGQ2EwqoQU-oYDp5GQE.jpg | 3d135f0c982ce1c3b0afa8b363d52396ad982367 | 98 | 25 | 55 | 55 | 124 | 87 | 80 | 80 | 0.68 | 0.40467856445983275 |
# | 23 | nogi07\41IBeVFOSHL.CR014350350.jpg | 33e24975b2e569c4ba6bda971a466ab4363743ed | 46 | 65 | 118 | 118 | 124 | 87 | 80 | 80 | 0.68 | 0.4053154540013497 |
# | 24 | nogi07\fukumimi2009_3.jpg | ac66dffc5e16217d5c7508d141a25b90e22915bd | 54 | 98 | 66 | 66 | 124 | 87 | 80 | 80 | 0.68 | 0.40800112678489375 |
# | 25 | nogi01\2110041604_3-714x1071.jpg | b074b23e02636b98cecc890afb0e6f1de6913fb0 | 39 | 52 | 83 | 83 | 124 | 87 | 80 | 80 | 0.68 | 0.4100480934729023 |
# | 26 | nogi01\maxresdefault (1).jpg | 6d5120f563e22b506fd4171aaef89a82903b80a1 | 128 | 46 | 100 | 100 | 124 | 87 | 80 | 80 | 0.68 | 0.41026243252861694 |
# | 27 | nogi01\rectangle_large_type_2_8d31aa9aaf1f21efbf9def3438a5cb5f.jpg | c876214527fca58717d42fd60230d3e1c371bbf2 | 204 | 35 | 80 | 80 | 124 | 87 | 80 | 80 | 0.68 | 0.41122276475967035 |
# | 28 | nogi08\ayamen.jpg | 966e3a8418df53c8180a3c1d6644520edd58cce5 | 195 | 33 | 82 | 82 | 124 | 87 | 80 | 80 | 0.68 | 0.4174920002196646 |
# | 29 | nogi01\3c6c4faee2771f9097d50db6c4ee036a14e9ba52.10.9.9.3.jpg | d62ae3d9d43a3c4a3cb2bcf5874814d209b0d219 | 98 | 55 | 56 | 56 | 124 | 87 | 80 | 80 | 0.68 | 0.41980488888572476 |
# | 30 | nogi05\yumiki_nao_2020_250.jpg | ccb2a53e7eac82c533eb2935ac6312f94e89e29c | 66 | 56 | 114 | 114 | 124 | 87 | 80 | 80 | 0.68 | 0.4278078226184272 |
# | 31 | nogi07\tamuramayu_prof.jpg | 0677753ee255f368991f8e771966bbf091f57f9a | 43 | 51 | 111 | 111 | 124 | 87 | 80 | 80 | 0.68 | 0.4282987934529676 |
# | 32 | nogi01\thumbnail (1).jpg | 22e70ee2dfa15770a895774b1dcf73b30f604fb4 | 71 | 43 | 60 | 60 | 124 | 87 | 80 | 80 | 0.68 | 0.4309907256888412 |
# | 33 | nogi05\1612351620.jpg | 4b15aafcb6959a7a63892c084a3111873f98db88 | 74 | 41 | 56 | 56 | 124 | 87 | 80 | 80 | 0.68 | 0.431483426983292 |
# | 34 | nogi08\85c0695e.jpg | 14d02ed6e126a325068e9cc53583ab4a45408eba | 40 | 47 | 83 | 83 | 124 | 87 | 80 | 80 | 0.68 | 0.43293727554695804 |
# | 35 | nogi08\tsutsuiayame_nogisaka46_thumbnail.jpg | 946073e19328273ad9d8eba7129dc62d66d3c108 | 99 | 45 | 106 | 106 | 124 | 87 | 80 | 80 | 0.68 | 0.43314782475385194 |
# | 36 | nogi08\v1035667595.1.jpg | 33dc3d45687c48f6dbd97db7b9e9bc64bc42bb11 | 48 | 45 | 92 | 92 | 124 | 87 | 80 | 80 | 0.68 | 0.43538664047251585 |
# | 37 | nogi10\20220903_155901_size640wh_66405490.jpg | dc6c8f71d41bcc45e673dd24732c4aa3b6c4a284 | 84 | 35 | 58 | 58 | 124 | 87 | 80 | 80 | 0.68 | 0.43667067101452384 |
# | 38 | nogi01\922-1.jpg | 1c0b26ed9d2c3e9a005468919dfd881dcdb35eaf | 80 | 48 | 70 | 70 | 124 | 87 | 80 | 80 | 0.68 | 0.43677156539860307 |
# | 39 | nogi06\111-16-640x360.jpg | 92d92416b02645f7c69dc0021e5c0f0e4b1c015a | 14 | 41 | 111 | 111 | 124 | 87 | 80 | 80 | 0.68 | 0.43867180128101735 |
# | 40 | nogi07\gE1yTAPl8TtXWBnrzNFM.jpg | 03b417f07d274e880f798a6349f90eb5cd43aacb | 64 | 49 | 56 | 56 | 124 | 87 | 80 | 80 | 0.68 | 0.4387869165303352 |
# | 41 | nogi01\ent_63084.jpg | 3c7c660d46e767ca94e18a194c13231378fd4a93 | 124 | 30 | 58 | 58 | 124 | 87 | 80 | 80 | 0.68 | 0.4389680369897512 |
# | 42 | nogi08\1477831_650.jpg | d488e269b0ca88326d3471195f951b5619c64578 | 80 | 8 | 65 | 65 | 124 | 87 | 80 | 80 | 0.68 | 0.44023796396332737 |
# | 43 | nogi01\29b2c8db8ec6fcb21794e667a668306c_1651910561_2.jpg | 4365ddb1b23c76cabffc28264eed773236f32f13 | 61 | 36 | 60 | 60 | 124 | 87 | 80 | 80 | 0.68 | 0.44070842870444493 |
# | 44 | nogi05\1kYFKVsx.jpg | 719c6aa8c7e2881d24c8cf66f8e5100a186ec35a | 66 | 24 | 66 | 66 | 124 | 87 | 80 | 80 | 0.68 | 0.44144774675412657 |
# | 45 | nogi10\001 (1).jpg | 7916f6ae334aa0f03f7bed90e27450cb12500fe5 | 72 | 39 | 62 | 62 | 124 | 87 | 80 | 80 | 0.68 | 0.44159445049209356 |
# | 46 | nogi07\3daa0e2d6c6379d3d2c54da28eb2668f.jpg | 439a2dac662b931ef7c6032a948a2cb12111af70 | 94 | 35 | 108 | 108 | 124 | 87 | 80 | 80 | 0.68 | 0.4439742613613812 |
# | 47 | nogi01\gg303079.jpg | 4001d33d5c5a4e986989a4dd864ec04476dde7c8 | 31 | 62 | 109 | 109 | 124 | 87 | 80 | 80 | 0.68 | 0.44513506834909977 |
# | 48 | nogi06\20210102195244128.jpg | 994878165e5445b5f3adb884ae05088feef994f2 | 40 | 28 | 123 | 123 | 124 | 87 | 80 | 80 | 0.68 | 0.4462251281269112 |
# | 49 | nogi07\90ee1ed2586dea8a9357f4e2ca7f1425.jpg | 38f145fba09c82675a55c02e3c27e98c1b736ee8 | 89 | 41 | 63 | 63 | 124 | 87 | 80 | 80 | 0.68 | 0.4470778706811096 |
<以下省略>
★ 動作結果解析
どういう仕組みになっているのかを解析していきましょう。
結果はリスト型pandas用データフレームになっています(ちなみに旧バージョンは単純なpandas用データフレーム)。このため内容を確認するためには、tabulateモジュールをインストールし出力させます。
作業開始最初の一回だけ、データベースにある全てのファイル名が「絶対パス付ファイル名」として保存されると同時に、「個別のID」と「ハッシュタグ」が付与されデータベース情報となる処理が実行されます。
+-----+-------------------------------------------------------+------------------------------------------+
| | identity | hash |
+-----+-------------------------------------------------------+------------------------------------------+
| 0 | nogi01\index.jpg | 7ddc637a7554ba41c2ecbfb646ac955c8a615de1 |
| 1 | nogi01\1554609.jpg | fc3b3b9b072257d2a4e55710100cbcea58c4f5f7 |
| 2 | nogi01\HK_38-thumb-550xauto-250780.jpg | 150f71720a9bf2cc85c260e0263acf68fc94a02a |
| 3 | nogi01\737671.jpg | f66ce0a2b98902e66f70391bb5c7f95c7f2b16bb |
| 4 | nogi01\1.jpg | 7739b216a4a0df6588172221383261ddddec261f |
| 5 | nogi07\1610451214.jpg | 838e126b12663539a43d8d734c9007d5df974c1b |
| 6 | nogi08\250px-2021E5B9B4E4B983E69CA8E59_4.jpg | f9e173dc05f8c86d36c2674d952788bb62c3aa27 |
| 7 | nogi01\P20200620-k008.jpg | 8fbfd7c686b4964d0ce081cd9867665302c2da81 |
| 8 | nogi01\52b9bc52c1a86305a6a443a95b5a63cd-904x1024.jpg | 225761d6011ad0e73c90fdd52fe4bc9894b4943b |
次に、「DeepFace.verify」メソッドと同じ操作を行っていくと推測され、初段階で入力パラメータ「align」がデフォルトTrueがセットされると、目の位置に基づいて位置合わせが行われ、ソース画像で検出された顔のバウンディングボックス座標「 'source_x', 'source_y', 'source_w',
'source_h'」、ターゲットの顔のバウンディングボックス座標「'target_x', 'target_y', 'target_w',
'target_h'」がデータベースにセットされます。
次に顔情報に基づき「類似度」が計算され、その値に対し「'threshold'」(ペアを同一人物か異なる人物かを判断するための閾値)で同一かを決めます。
「DeepFace.verify」メソッドと異なる点として、今回は必ずしも同じ写真があるとは限らないため、類似性スコア指定された距離メトリック「'distance'」がデータベースに登録されていきます。そうすると
「pandas」はデータベースと同じと考えてもよいので、常に「'distance'」をキーとして昇順にソートされ表示されます。つまり距離メトリックが最も小さいデータ(表先頭)が「同一」または「最も近い」データということになります。
+-----+----------+--+----------+--+-----------+---------------------+
| | target_x | | source_x | | threshold | distance |
+-----+----------+--+----------+--+-----------+---------------------+
| 0 | 71 | | 124 | | 0.68 | 0.1830687137044812 |
| 1 | 142 | | 124 | | 0.68 | 0.28360544030705803 |
| 2 | 123 | | 124 | | 0.68 | 0.30305929319280744 |
| 3 | 46 | | 124 | | 0.68 | 0.329093685963859 |
| 4 | 73 | | 124 | | 0.68 | 0.34039565440839015 |
| 5 | 63 | | 124 | | 0.68 | 0.34090774093169873 |
| 6 | 57 | | 124 | | 0.68 | 0.36282107129761143 |
| 7 | 110 | | 124 | | 0.68 | 0.3648022909914431 |
| 8 | 74 | | 124 | | 0.68 | 0.36502568641487976 |
次にソートされた結果「identity」のフォルダ名に注目して下さい。
同データベースで同一人物は同じフォルダに格納されており、今回の対象はフォルダ名「nogi01」です。9個中7個が「nogi01」となっています(人間が考えると同じ人物の写真ですから同じなのは当たり前ですが)。
ここから推定されることは、抽出される対象は、実際には
「一致した」のではなく、距離メトリック「'distance'」が一番小さな画像
ということは、
「全く同一でなくてもよい」=「一番似た画像」
だということが分かります。
だとすると、もう少し具体的に言うと、人間が考える「似た」とは異なり、「モデル」と「バックエンド」の構造結果をメトリックで距離化した場合の量で評価した結果と言えます。
それがホントかは次のステップで試験して検証していきます。
+-----+-------------------------------------------------------+------------------------------------------+
| | identity | hash |
+-----+-------------------------------------------------------+------------------------------------------+
| 0 | nogi01\index.jpg | 7ddc637a7554ba41c2ecbfb646ac955c8a615de1 |
| 1 | nogi01\1554609.jpg | fc3b3b9b072257d2a4e55710100cbcea58c4f5f7 |
| 2 | nogi01\HK_38-thumb-550xauto-250780.jpg | 150f71720a9bf2cc85c260e0263acf68fc94a02a |
| 3 | nogi01\737671.jpg | f66ce0a2b98902e66f70391bb5c7f95c7f2b16bb |
| 4 | nogi01\1.jpg | 7739b216a4a0df6588172221383261ddddec261f |
| 5 | nogi07\1610451214.jpg | 838e126b12663539a43d8d734c9007d5df974c1b |
| 6 | nogi08\250px-2021E5B9B4E4B983E69CA8E59_4.jpg | f9e173dc05f8c86d36c2674d952788bb62c3aa27 |
| 7 | nogi01\P20200620-k008.jpg | 8fbfd7c686b4964d0ce081cd9867665302c2da81 |
| 8 | nogi01\52b9bc52c1a86305a6a443a95b5a63cd-904x1024.jpg | 225761d6011ad0e73c90fdd52fe4bc9894b4943b |
★ 画像検索関数化改修
それでは、次のステップに向けて検索結果がpandasのフレームデータでは困りますので、検索結果の合致ファイル名と距離メトリックを算出する関数にしてみました。
【DF_FaceFind_test03.py】
#coding: utf-8
# ////////////////////////////////////////////////////////////////////////////
# ///【DeepFace・顔検索】
# ////////////////////////////////////////////////////////////////////////////
if "__file__" in globals():
import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
sys.path.append(os.path.join(os.path.dirname(__file__), "..") + "//..//"+ "//..//")
os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'
from DeZero.common.nlp_util import *
tensorflow_warning_cancel()
import cv2
import pandas as pd
import datetime
from deepface import DeepFace
from tabulate import tabulate
# ////////////////////////////////////////////////////////////////////////////
# /// [顔検索(find)]
# ///
# /// [input]
# /// img1_path : 比較画像パス1
# /// db_path : 検索フォルダー
# /// model : 使用モデル
# /// metric : 類似度
# /// backend : バックエンド
# /// normalization : 正規化
# /// silent : サイレントモード
# /// [output]
# /// matched : 一致ファイル名
# /// distance : 距離メトリック
# ///
# ////////////////////////////////////////////////////////////////////////////
# ////////////////////////////////////////////////////////////////////////////
def deepface_find(img1_path, db_path, model, metric, backend, normalization, silent):
list_df = DeepFace.find(
img_path = img1_path, # img_path: Union[str, np.ndarray]
db_path = db_path, # db_path: str
model_name = model, # model_name: str = "VGG-Face"
distance_metric = metric, # distance_metric: str = "cosine"
enforce_detection=False, # enforce_detection: bool = True
detector_backend = backend, # detector_backend: str = "opencv"
align=True, # align: bool = True
expand_percentage=0, # expand_percentage: int = 0
threshold=None, # threshold: Optional[float] = None
normalization=normalization, # normalization: str = "base"
silent=silent # silent: bool = False
)
# /// V1.2A 2024/03/20 検索できないケースがある
# ////////////////////////////////////////////////////////////////////////////
if list_df == []:
return "ERROR"
else:
df = list_df[0]
pd.set_option('display.max_rows', 1000)
pd.set_option('display.max_columns', 4096)
df.head()
dataframe = pd.DataFrame(df)
matched = ""
if df.shape[0] > 0:
matched = df.iloc[0].identity
distance = float(df.iloc[0].distance)
return matched, distance
if __name__ == "__main__":
StartTime = datetime.datetime.now()
models = [
"VGG-Face",
"Facenet",
"Facenet512",
"OpenFace",
"DeepFace",
"DeepID",
"ArcFace",
"Dlib",
"SFace",
]
backends = [
'opencv',
'ssd',
'dlib',
'mtcnn',
'retinaface',
'mediapipe',
'yolov8',
'yunet',
'fastmtcnn',
]
metrics = ["cosine", "euclidean", "euclidean_l2"]
img1_path = "c:\\Photo\\nogi01-kaki.jpg"
db_path = "d:\\VisualStudio2017\\Python3.5_GPU\\test_dataset\\DeepFace"
# /// 顔検索(find)
# ////////////////////////////////////////////////////////////////////////////
matched, distance = deepface_find(img1_path, db_path, models[0], metrics[0], backends[0], normalizations[0], silent=False)
now = datetime.datetime.now()
d = "{0:%y-%m-%d %H:%M:%S}".format(now)
print(d,"- matched ", matched, distance)
★ 結果の解析
前述のサンプルプログラムは、単純にデータベースの中から一致する画像を検索する例です。必ず同一写真があるので、結果が次のように出てきます。
# [result]
# (py39) d:\VisualStudio2017\Python3.5_GPU\Sample_TEST\顔認識2024\03_DF_FaceFind>python DF_FaceFind_test03.py
# 24-03-27 12:28:51 - Searching c:\Photo\nogi01-kaki.jpg in 1505 length datastore
# 24-03-27 12:28:53 - find function duration 3.7284440994262695 seconds
# 24-03-27 12:28:53 - matched d:\VisualStudio2017\Python3.5_GPU\test_dataset\DeepFace\nogi01\index.jpg 0.1830687137044812
但し、事前調査の結果データベース内には探し出すことができない画像というのがあるようです。理由は推測の域を出ませんが、初段階で作成されるID情報に、ソース画像で検出された顔のバウンディングボックス座標「
'source_x', 'source_y', 'source_w',
'source_h'」、ターゲットの顔のバウンディングボックス座標「'target_x', 'target_y', 'target_w',
'target_h'」が対で作成されていきます。もし対象の顔が抽出できないとターゲットの顔のバウンディングボックス座標が無いため、以降の操作が全くできなくなるという現象ではないかと推測します。
+-----+----------+--+----------+--+-----------+---------------------+
| | target_x | | source_x | | threshold | distance |
+-----+----------+--+----------+--+-----------+---------------------+
| 0 | 71 | | 124 | | 0.68 | 0.1830687137044812 |
| 1 | 142 | | 124 | | 0.68 | 0.28360544030705803 |
| 2 | 123 | | 124 | | 0.68 | 0.30305929319280744 |
| 3 | 46 | | 124 | | 0.68 | 0.329093685963859 |
| 4 | 73 | | 124 | | 0.68 | 0.34039565440839015 |
| 5 | 63 | | 124 | | 0.68 | 0.34090774093169873 |
| 6 | 57 | | 124 | | 0.68 | 0.36282107129761143 |
| 7 | 110 | | 124 | | 0.68 | 0.3648022909914431 |
| 8 | 74 | | 124 | | 0.68 | 0.36502568641487976 |
モデルとバックエンドの組み合わせによっては顔抽出や認識ができない場合、例外処理でトラップしてしまい処理が中断してしまいます。これを防ぐために、パラメータ「enforce_detection」をデフォルト「True」から「False」に変更しておくと、例がトラップは回避されて「空のリスト型」データとして出てくるようになります。
【DF_FaceFind_test03.py】
# ////////////////////////////////////////////////////////////////////////////
# /// [顔検索(find)]
# ////////////////////////////////////////////////////////////////////////////
def deepface_find(img1_path, db_path, model, metric, backend, normalization, silent):
list_df = DeepFace.find(
img_path = img1_path, # img_path: Union[str, np.ndarray]
db_path = db_path, # db_path: str
model_name = model, # model_name: str = "VGG-Face"
distance_metric = metric, # distance_metric: str = "cosine"
enforce_detection=False, # enforce_detection: bool = True
detector_backend = backend, # detector_backend: str = "opencv"
align=True, # align: bool = True
expand_percentage=0, # expand_percentage: int = 0
threshold=None, # threshold: Optional[float] = None
normalization=normalization, # normalization: str = "base"
silent=silent # silent: bool = False
)
例外処理対策を行うと、エラー時このように出力されることになります。
# [result]
# (py39) d:\VisualStudio2017\Python3.5_GPU\Sample_TEST\顔認識2024\03_DF_FaceFind>python DF_FaceFind_test03.py
# 24-03-27 12:28:36 - Searching d:\VisualStudio2017\Python3.5_GPU\test_dataset\DeepFace\nogi04\b1c038403f5e865905335859d82f3e26.jpg in 1505 length datastore
# 24-03-27 12:28:38 - find function duration 2.72244930267334 seconds
# 24-03-27 12:28:38 - matched ERROR 0.0
≪清須電脳倶楽部メインページへ戻る場合はここをクリックしてください≫