OpenCV SFace で顔 embedding の近傍検索を試す
こんにちは、皆さん。
顔認識というと少し大げさに聞こえますが、技術的には「顔画像を数字のベクトルに変換し、近いものを探す」処理として扱えます。
さて、今日は OpenCV Zoo の SFace を使い、顔画像から embedding を作り、同一人物の別画像を近傍検索で見つけられるかを検証しました。
今回検証する内容
SFace は、顔認識向けのモデルです。顔画像を 128 次元の embedding に変換します。embedding は、画像の特徴を数値の並びで表したものです。近い顔ほど、この数値の並びも近くなることを期待します。
今回は 2 種類の顔画像データセットを使い、次の点を確認します。
- SFace の ONNX モデルから 128 次元 embedding を生成できるか
- 同一人物の別画像が、1-nearest-neighbor(いちばん近い画像)として見つかるか
- 同一人物ペアと別人物ペアで cosine similarity に差が出るか
- grayscale の小さな顔 crop と、カラーの整列済み顔 crop で結果がどう変わるか
cosine similarity は、2 つのベクトルの向きがどれくらい近いかを表す値です。今回は embedding を L2 normalize(長さを 1 にそろえる処理)してから比較しました。
対象の lab: kiarina/labs/2026/07/10/sface-face-embedding
検証環境の再現
以下のコマンドで、検証環境を構築して手元で実行できます。mise、uv が必要です。初回実行時には SFace の ONNX モデル、Olivetti Faces、LFW をダウンロードするため、インターネット接続も必要です。
git clone --depth 1 --filter=blob:none --sparse \
https://github.com/kiarina/labs.git
cd labs
git sparse-checkout set .gitignore .mise/tasks Makefile mise.toml 2026/07/10/sface-face-embedding
mise -C 2026/07/10/sface-face-embedding run
LFW は展開後に約 395 MB 使用します。Python 環境やモデルも含め、500 MB 程度の空き容量を見ておくと安心です。
使用したモデルとライセンス
使用したモデルは、OpenCV Zoo の face_recognition_sface_2021dec.onnx です。
- モデル: face_recognition_sface_2021dec.onnx
- モデル URL:
https://media.githubusercontent.com/media/opencv/opencv_zoo/47534e27c9851bb1128ccc0102f1145e27f23f98/models/face_recognition_sface/face_recognition_sface_2021dec.onnx - SHA-256:
0ba9fbfa01b5270c96627c4ef784da859931e02f04419c829e83484087c34e79 - 入力: 112x112 の aligned BGR face crop
- 出力: 128 次元 embedding
- ライセンス: Apache-2.0
OpenCV Zoo の SFace ディレクトリでは、同ディレクトリ内のファイルは Apache-2.0 License と明記されています。
使用したデータセット
比較には 2 つのデータセットを使いました。
Olivetti Faces
Olivetti Faces は、40 人 x 10 枚、合計 400 枚の 64x64 grayscale 顔画像です。grayscale は白黒画像のことです。
images: 400
resolution: 64x64 grayscale
people: 40
images per person: 10
dataset SHA-256: b612fb967f2dc77c9c62d3e1266e0c73d5fca46a4b8906c18e454d41af987794
LFW people, funneled color crop
Labeled Faces in the Wild は、実環境に近い顔画像データセットです。今回は sklearn.datasets.fetch_lfw_people で funneled 版を取得し、20 枚以上ある人物のうち固定順で 40 人を選び、各人物の先頭 10 枚だけを使いました。funneled は、顔の向きや位置がある程度そろうように前処理された版です。
selected images: 400
selected resolution: 125x94 RGB
selected people: 40
selected images per person: 10
archive SHA-256: b47c8422c8cded889dc5a13418c4bc2abbda121092b3533a83306f90d900100a
検証方法
各データセットで、40 人 x 10 枚の画像を使いました。各人物の前半 5 枚を参照集合、後半 5 枚を query(検索したい画像)にします。
流れは次のとおりです。
- 顔画像を 112x112 にリサイズする
- grayscale または RGB 画像を OpenCV 用の BGR にそろえる
-
cv2.FaceRecognizerSF.featureで embedding を作る - embedding を L2 normalize する
- query と参照集合の cosine similarity を計算する
- top-1 accuracy と top-5 accuracy を確認する
top-1 accuracy は、いちばん近い画像が同一人物だった割合です。top-5 accuracy は、近い順の 5 件の中に同一人物が含まれた割合です。
本検証では、顔検出や追加の landmark alignment は行っていません。landmark alignment は、目や鼻などの位置を使って顔の向きをそろえる前処理です。今回はデータセットの crop をそのまま 112x112 にリサイズしています。
検証結果
Mac Studio (Apple M4 Max) で実行した結果は以下のとおりです。
Dataset | Top-1 | Top-5 | Pos cosine | Neg cosine | sec/image
-------------------------------------------------------------------------------------
Olivetti Faces | 0.800 | 0.920 | 0.722 | 0.577 | 0.0057
LFW people, funneled color crop | 0.820 | 0.945 | 0.463 | 0.228 | 0.0058
実行環境は次のとおりです。
machine: Mac Studio (Apple M4 Max)
OS: macOS 26.5.1, arm64
Python: 3.12.10
OpenCV: 5.0.0
NumPy: 2.5.1
SciPy: 1.18.0
scikit-learn: 1.9.0
Pillow: 12.3.0
より細かい結果は lab の output/olivetti_report.json、output/lfw_report.json、output/summary.json に保存されます。
結果の考察
専門的なデータは上に載せたとおりですが、簡単に見ると次のようになります。
-
SFace embedding で同一人物をある程度近くに集められた
Olivetti Faces では top-1 accuracy が 80.0%、top-5 accuracy が 92.0% でした。64x64 の小さな白黒顔画像でも、同一人物の画像が近くに集まることは確認できました。 -
LFW のほうが少し良い結果だった
LFW の funneled color crop では top-1 accuracy が 82.0%、top-5 accuracy が 94.5% でした。SFace が想定する「カラーで、ある程度整列した顔 crop」に近い条件のほうが、わずかに良い結果になりました。 -
同一人物と別人物の距離差は LFW のほうが大きい
Olivetti Faces では、同一人物ペアの平均 cosine similarity が 0.722、別人物ペアが 0.577 でした。差は 0.145 です。LFW では同一人物ペアが 0.463、別人物ペアが 0.228 で、差は 0.235 でした。絶対値は LFW のほうが低いですが、同一人物と別人物の分離は LFW のほうが明確でした。 -
完全な識別には届いていない
LFW でも 18% の query は、top-1 で別人物を最近傍としていました。今回は学習なしの最近傍検索であり、分類器を学習したわけではありません。また、顔検出や landmark alignment も追加していません。一般的な顔認識性能を示す結果ではなく、この条件での技術検証として見るのがよさそうです。 -
embedding 生成は軽い
400 枚の embedding 生成は、どちらのデータセットでも約 2.3 秒でした。1 枚あたり約 0.0058 秒です。今回の規模なら、CPU でもかなり扱いやすい速度でした。
検証後の感想
SFace は、顔画像を「比較しやすい数値」に変換する部品としてかなり使いやすい印象でした。OpenCV の FaceRecognizerSF からそのまま呼べて、ONNX モデルの取得と SHA-256 検証まで lab に入れておけば、再現もしやすいです。
人間も、顔だけではなく、声、喋り方、服装、その場の文脈などを合わせて「この人かも」と思い出しているはずです。そう考えると、今回の結果を顔だけで完全に識別するためのものではなく、参考値のひとつとして利用するのであれば、LLM エージェントへの組み込みも現実的に感じました。