InsightFaceによる顔検出
【概要】 InsightFaceフレームワークのSCRFD技術を用いた顔検出プログラムの実装と実験を行う。SCRFDはサンプル分布を再配分することを特徴とする顔検出技術である。Webカメラから顔と5点キーポイントを検出する。Windows環境での実行手順、プログラムコード、実験アイデアを含む。


ツール利用ガイド
1. このツールの利用シーン
このツールは、動画や顔を自動検出し、顔の特徴点を可視化するソフトウェアである。監視カメラ映像の解析、ビデオ会議システムの開発、顔認証システムの前処理、映像コンテンツの編集などで活用される。
2. 主な機能
- リアルタイム顔検出: SCRFDアルゴリズムにより、動画の各フレームから顔を高速検出する
- 5点キーポイント表示: 左目、右目、鼻、左口角、右口角の座標を赤い点で表示する
- 信頼度フィルタリング: DET_SCORE_THRESHOLD(デフォルト0.5)以上の信頼度を持つ顔のみを表示する
- 複数入力対応: 動画ファイル、ウェブカメラ、サンプル動画から入力源を選択できる
- 実行環境自動選択: GPU(CUDA/DirectML)またはCPUを自動検出し、最適な処理方式を選択する
- 検出結果の保存: 処理終了時にresult.txtファイルへ検出履歴を自動保存する
3. 基本的な使い方
- 起動と入力選択:
プログラムを実行すると、入力選択メニューが表示される。0(動画ファイル)、1(ウェブカメラ)、2(サンプル動画)から選択し、Enterキーを押す。
- 顔検出の実行:
選択した入力源から映像が読み込まれ、自動的に顔検出が開始される。検出された顔は緑色の枠で囲まれ、5つの特徴点が赤い点で表示される。
- 終了方法:
映像表示ウィンドウを選択した状態で、キーボードのqキーを押すと処理を終了する。
4. 便利な機能
- モデル自動取得: モデル自動ダウンロードする
- 日本語表示: 検出結果や操作方法を日本語で画面表示する
- 検出パラメータ調整: DET_SCORE_THRESHOLDの値を変更することで、検出感度を調整できる
- 処理状況表示: フレーム番号、検出数、使用デバイス(CUDA/DirectML/CPU)を画面上部に表示する
- タイムスタンプ記録: カメラ入力時は時刻情報、動画ファイル時はフレーム番号を記録する
Python開発環境,ライブラリ類
Python 3.12 のインストール
インストール済みの場合は実行不要。
管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要である。
REM Python をシステム領域にインストール
winget install --scope machine --id Python.Python.3.12 -e --silent
REM Python のパス設定
set "PYTHON_PATH=C:\Program Files\Python312"
set "PYTHON_SCRIPTS_PATH=C:\Program Files\Python312\Scripts"
echo "%PATH%" | find /i "%PYTHON_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_PATH%" /M >nul
echo "%PATH%" | find /i "%PYTHON_SCRIPTS_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_SCRIPTS_PATH%" /M >nul
【関連する外部ページ】
Python の公式ページ: https://www.python.org/
AI エディタ Windsurf のインストール
Pythonプログラムの編集・実行には、AI エディタの利用を推奨する。ここでは,Windsurfのインストールを説明する。
管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行して、Windsurfをシステム全体にインストールする。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要となる。
winget install --scope machine Codeium.Windsurf -e --silent
【関連する外部ページ】
Windsurf の公式ページ: https://windsurf.com/
必要なパッケージのインストール
コマンドプロンプトを管理者として実行(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。
pip install insightface opencv-python onnxruntime numpy pillow
GPU使用の場合,追加で次を実行
pip install onnxruntime-gpu
プログラムコード
概要
このプログラムは、動画から顔と顔の特徴点を検出する。InsightFaceフレームワークのSCRFDアルゴリズムを用いて、検出した顔にバウンディングボックスを描画し、5点キーポイント(両目、鼻、口角)の座標を表示する。
主要技術
SCRFD (Sample and Computation Redistribution for Efficient Face Detection)
Guo, J.らが2022年に発表した顔検出アルゴリズム[1]。画像ピラミッドの各スケールでサンプルと計算量を適応的に再分配する手法により、検出精度を維持しながら計算効率を向上させる。
InsightFace
2次元および3次元の顔分析のための統合フレームワーク[3]。SCRFDを含む複数の顔検出・認識アルゴリズムを提供し、ONNX Runtimeを通じて推論を実現する。
技術的特徴
- 画像ピラミッドにおけるサンプルと計算量の動的再分配により、小さい顔と大きい顔の検出バランスを最適化
- 信頼度スコアに基づくフィルタリング機能により、誤検出を抑制(閾値はDET_SCORE_THRESHOLDで調整可能)
- 5点キーポイント検出により、顔の向きや表情の基本情報を取得
実装の特色
- 実行環境の自動判定:GPU(CUDA)、GPU(DirectML)、CPUを自動検出し、最適な実行プロバイダを選択
- 日本語表示対応:Pillowライブラリを用いて、検出結果を日本語で画面表示(Meiryoフォント使用)
- 複数の入力ソース対応:動画ファイル、ウェブカメラ、サンプル動画からの入力を選択可能
- 検出結果の記録:処理結果をresult.txtファイルに自動保存し、後から検証可能
- リアルタイム表示:検出した顔の信頼度スコア、フレーム番号、検出数を画面上に表示
参考文献
[1] Guo, J., Deng, J., Lattas, A., & Zafeiriou, S. (2022). Sample and Computation Redistribution for Efficient Face Detection. International Conference on Learning Representations (ICLR 2022). https://openreview.net/forum?id=RjpsFJiMiQ5
[2] Deng, J., Guo, J., Ververas, E., Kotsia, I., & Zafeiriou, S. (2020). RetinaFace: Single-Shot Multi-Level Face Localisation in the Wild. IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 5203-5212. https://doi.org/10.1109/CVPR.2020.00525
[3] InsightFace GitHub Repository. (2024). InsightFace: State-of-the-art 2D and 3D Face Analysis Project. https://github.com/deepinsight/insightface
ソースコード
# プログラム名: InsightFace顔検出プログラム
# 特徴技術名: SCRFD (Sample and Computation Redistribution for Efficient Face Detection)
# 出典: Guo, J., et al. (2022). Sample and Computation Redistribution for Efficient Face Detection. ICLR 2022. https://github.com/deepinsight/insightface
# 特徴機能: サンプルと計算量の再分配による顔検出。画像ピラミッドの各スケールでサンプルと計算量を適応的に再分配し、検出精度を維持しながら計算効率を向上
# 学習済みモデル: buffalo_sc - InsightFaceの軽量モデルパッケージ。SCRFD検出器を含む統合モデル。初回実行時に自動ダウンロード(~300MB)
# 方式設計:
# - 関連利用技術:
# - InsightFace: SCRFDアルゴリズムによる顔検出、CNN基盤の深層学習手法
# - OpenCV: 画像処理、カメラ制御、描画処理
# - ONNX Runtime: モデル推論エンジン、GPU/CPU/DirectMLの実行プロバイダ
# - 入力と出力: 入力は「0:動画ファイル,1:カメラ,2:サンプル動画」から選択。出力はOpenCV画面でリアルタイム表示(検出した顔をバウンディングボックスと5点キーポイントで表示)。print()は検出結果を表示し、終了時にresult.txtに保存する
# - 処理手順: 1.InsightFace(SCRFD)で顔検出、2.信頼度閾値によるフィルタリング、3.5点キーポイント抽出、4.検出結果の表示
# - 前処理、後処理: 前処理は最新フレーム取得。後処理は信頼度閾値によるフィルタリング
# - 追加処理: 5点キーポイント(左目、右目、鼻、左口角、右口角)の座標表示。Windows環境でのDirectML対応
# - 調整を必要とする設定値: SCORE_TH(検出信頼度閾値、デフォルト0.5 for buffalo_sc, 0.3 for buffalo_l)
# その他の重要事項: Windows環境専用。初回実行時は学習済みモデルのダウンロードに時間がかかる。GPU/DirectML/CPUを状況に応じて選択
# 前準備:
# - pip install insightface opencv-python onnxruntime numpy pillow
# - GPU使用の場合: pip install onnxruntime-gpu
# - DirectML利用の場合(Windows): pip install onnxruntime-directml
import cv2
import tkinter as tk
from tkinter import filedialog
import os
import numpy as np
from insightface.app import FaceAnalysis
import warnings
import time
import urllib.request
from PIL import Image, ImageDraw, ImageFont
from datetime import datetime
# InsightFace関連の将来警告・ユーザ警告を抑制(他の警告は抑制しない)
warnings.filterwarnings('ignore', category=FutureWarning, module='insightface')
warnings.filterwarnings('ignore', category=UserWarning, module='insightface')
# ===== モデル設定 =====
MODEL_CONFIG = {
'buffalo_sc': {
'name': 'buffalo_sc',
'description': '高速・軽量',
'size': '約300MB',
'score_threshold': 0.5,
'det_size': (640, 640)
},
'buffalo_l': {
'name': 'buffalo_l',
'description': '高精度',
'size': '約1GB',
'score_threshold': 0.3,
'det_size': (640, 640)
}
}
# ===== 設定・定数管理 =====
SAMPLE_VIDEO_URL = 'https://raw.githubusercontent.com/opencv/opencv/master/samples/data/vtest.avi'
SAMPLE_VIDEO_NAME = 'vtest.avi'
RESULT_FILE = 'result.txt'
# カメラ設定
WINDOW_WIDTH = 1280 # カメラ解像度幅
WINDOW_HEIGHT = 720 # カメラ解像度高さ
FPS = 30 # フレームレート
# 顔とキーポイントの表示色(BGR形式)
FACE_COLOR = (0, 255, 0) # バウンディングボックス用
KPS_COLOR = (0, 0, 255) # キーポイント用
KPS_RADIUS = 3 # キーポイントの円の半径
# キーポイント表示名(SCRFDの並びに整合)
KPS_NAMES = ['左目', '右目', '鼻', '左口角', '右口角']
# フォント設定(日本語表示用)
FONT_PATH = 'C:/Windows/Fonts/meiryo.ttc'
FONT_SIZE = 20
FONT_SIZE_SMALL = 16
# グローバル変数の初期化
frame_count = 0
results_log = []
def detect_gpu():
"""ONNX Runtimeの実行プロバイダからGPU/DirectML/CPUの使用可否を判定し、ctx_idとprovidersを返す"""
import onnxruntime as ort
providers = ort.get_available_providers()
if 'CUDAExecutionProvider' in providers:
print('GPU(CUDA)検出 - GPU使用モードで実行')
return 0, ['CUDAExecutionProvider', 'CPUExecutionProvider']
if 'DmlExecutionProvider' in providers:
print('GPU(DirectML)検出 - DirectML使用モードで実行')
return -1, ['DmlExecutionProvider', 'CPUExecutionProvider']
print('GPU未検出 - CPU使用モードで実行')
return -1, ['CPUExecutionProvider']
def get_device_label(providers):
"""表示用のデバイス名を返す"""
if 'CUDAExecutionProvider' in providers:
return 'CUDA'
if 'DmlExecutionProvider' in providers:
return 'DirectML'
return 'CPU'
def check_font_availability():
"""フォントファイルの存在を確認する"""
if not os.path.exists(FONT_PATH):
print(f'警告: 日本語フォントが見つかりません ({FONT_PATH})')
print('日本語表示が正しく行われない可能性があります')
return False
return True
def download_insightface_model(model_name):
"""SCRFDモデルの存在を確認し、未ダウンロード時にはダウンロードが必要であることを通知する"""
model_dir = os.path.join(os.path.expanduser('~'), '.insightface', 'models', model_name)
model_info = MODEL_CONFIG[model_name]
if not os.path.exists(model_dir):
print(f'SCRFD {model_name}モデルの初回ダウンロードを行います...')
print(f'モデル: {model_name}({model_info["size"]})')
print('注意: 初回はネットワーク環境により数分かかる場合があります')
else:
print(f'SCRFD {model_name}モデルが既に存在します')
def extract_face_info(face, face_id):
"""検出顔から座標・キーポイント・信頼度を抽出して辞書化する"""
bbox = face.bbox.astype(int)
face_info = {
'id': face_id,
'box': (bbox[0], bbox[1], bbox[2], bbox[3]),
'keypoints': []
}
if hasattr(face, 'kps') and face.kps is not None:
for i, kp in enumerate(face.kps):
if i < len(KPS_NAMES):
face_info['keypoints'].append({
'name': KPS_NAMES[i],
'x': int(kp[0]),
'y': int(kp[1])
})
if hasattr(face, 'det_score'):
face_info['detection_conf'] = float(face.det_score)
return face_info
def draw_japanese_text(img, text, position, font_size, color):
"""日本語テキストをPillowで画像に描画する"""
try:
font = ImageFont.truetype(FONT_PATH, font_size)
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img_pil)
draw.text(position, text, font=font, fill=color[::-1]) # BGRをRGB順に
return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
except Exception:
# フォントエラー時はOpenCVの英数字フォントで代替
cv2.putText(img, text.encode('ascii', 'ignore').decode('ascii'), position,
cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2)
return img
def video_frame_processing(frame, score_threshold, model_name):
"""フレーム単位の処理。顔検出、フィルタ、描画を行う"""
global frame_count
current_time = time.time()
frame_count += 1
faces_data = []
# 顔検出
faces = app.get(frame)
# 信頼度でフィルタリング
faces = [face for face in faces if face.det_score >= score_threshold]
# 各顔の情報抽出
for i, face in enumerate(faces):
face_info = extract_face_info(face, i + 1)
faces_data.append(face_info)
# 描画処理
for face_info in faces_data:
x1, y1, x2, y2 = face_info['box']
cv2.rectangle(frame, (x1, y1), (x2, y2), FACE_COLOR, 2)
for kp in face_info['keypoints']:
cv2.circle(frame, (kp['x'], kp['y']), KPS_RADIUS, KPS_COLOR, -1)
label1 = f"顔 {face_info['id']}"
frame = draw_japanese_text(frame, label1, (x1, y1 - 10), FONT_SIZE, FACE_COLOR)
if 'detection_conf' in face_info:
label2 = f"信頼度:{face_info['detection_conf']:.2f}"
frame = draw_japanese_text(frame, label2, (x1, y2 + 15), FONT_SIZE_SMALL, (255, 255, 255))
# 検出結果の文字列化
if faces_data:
result = f"検出数:{len(faces_data)}"
for face_info in faces_data:
if 'detection_conf' in face_info:
result += f" 顔{face_info['id']}信頼度:{face_info['detection_conf']:.2f}"
else:
result = "検出数:0"
# 画面情報表示
info1 = f'InsightFace ({model_name}/{DEVICE_LABEL}) | フレーム: {frame_count} | 検出数: {len(faces_data)}'
info2 = f'操作: q=終了 | 閾値: {score_threshold}'
frame = draw_japanese_text(frame, info1, (10, 30), FONT_SIZE, (255, 255, 255))
frame = draw_japanese_text(frame, info2, (10, 60), FONT_SIZE_SMALL, (255, 255, 0))
return frame, result, current_time
# GPU判定
CTX_ID, PROVIDERS = detect_gpu()
DEVICE_LABEL = get_device_label(PROVIDERS)
# プログラム概要表示(ガイダンス)
print('=== InsightFace顔検出プログラム ===')
print('概要: リアルタイムで顔と5点キーポイントを検出し表示する')
print('機能: SCRFD(InsightFace)による顔検出と顔特徴点検出')
print('操作: qキーで終了')
print('注意: 初回はモデルの自動ダウンロードに時間がかかる場合があります')
print('注意: 画面上の日本語表示にはMeiryoフォント(C:/Windows/Fonts/meiryo.ttc)が必要です')
print('出力: 終了時にresult.txtへ保存')
print()
# システム初期化
print('システム初期化中...')
# フォント確認
check_font_availability()
# モデル選択メニュー
print('\n=== モデル選択 ===')
print('0: buffalo_sc (高速・軽量)')
print('1: buffalo_l (高精度)')
model_choice = input('モデル選択: ')
if model_choice == '1':
selected_model = 'buffalo_l'
else:
selected_model = 'buffalo_sc'
model_config = MODEL_CONFIG[selected_model]
MODEL_NAME = model_config['name']
SCORE_TH = model_config['score_threshold']
DET_SIZE = model_config['det_size']
print(f'\n選択モデル: {MODEL_NAME} ({model_config["description"]})')
print(f'設定: 検出信頼度閾値 = {SCORE_TH}, 実行デバイス = {DEVICE_LABEL}')
# SCRFDモデルダウンロード確認
download_insightface_model(MODEL_NAME)
# 顔検出用アプリケーション初期化
print(f'{MODEL_NAME}モデルを初期化中...')
app = FaceAnalysis(name=MODEL_NAME, providers=PROVIDERS)
app.prepare(ctx_id=CTX_ID, det_size=DET_SIZE)
print(f'{MODEL_NAME}モデルをロードしました')
print('初期化完了')
print()
# 入力選択メニュー
print('0: 動画ファイル')
print('1: カメラ')
print('2: サンプル動画')
choice = input('選択: ')
if choice == '0':
root = tk.Tk()
root.withdraw()
path = filedialog.askopenfilename()
if not path:
exit()
cap = cv2.VideoCapture(path)
elif choice == '1':
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
if not cap.isOpened():
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, WINDOW_WIDTH)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, WINDOW_HEIGHT)
cap.set(cv2.CAP_PROP_FPS, FPS)
else:
# サンプル動画ダウンロード・処理
urllib.request.urlretrieve(SAMPLE_VIDEO_URL, SAMPLE_VIDEO_NAME)
cap = cv2.VideoCapture(SAMPLE_VIDEO_NAME)
if not cap.isOpened():
print('動画ファイル・カメラを開けませんでした')
exit()
# メイン処理
print('\n=== 動画処理開始 ===')
print('操作方法:')
print(' q キー: プログラム終了')
try:
while True:
ret, frame = cap.read()
if not ret:
break
MAIN_FUNC_DESC = "InsightFace Detection"
processed_frame, result, current_time = video_frame_processing(frame, SCORE_TH, MODEL_NAME)
cv2.imshow(MAIN_FUNC_DESC, processed_frame)
if choice == '1': # カメラの場合
print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S.%f")[:-3], result)
else: # 動画ファイルの場合
print(frame_count, result)
results_log.append(result)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
finally:
print('\n=== プログラム終了 ===')
cap.release()
cv2.destroyAllWindows()
if results_log:
with open(RESULT_FILE, 'w', encoding='utf-8') as f:
f.write('=== 結果 ===\n')
f.write(f'処理フレーム数: {frame_count}\n')
f.write(f'使用モデル: {MODEL_NAME}\n')
f.write(f'使用デバイス: {DEVICE_LABEL}\n')
f.write('\n')
f.write('\n'.join(results_log))
print(f'\n処理結果を{RESULT_FILE}に保存しました')
実験・探求のアイデア
様々な条件での検出性能テスト
- 複数人の同時検出
- 横顔や斜め顔での検出精度
- マスク着用時の検出性能
- 暗い環境での検出限界
- 手で顔の一部を隠した場合の挙動
キーポイント活用の探求
検出された5点キーポイントの座標を利用して、顔の向きや表情の簡易推定を実行する。両目の座標差から顔の傾きを計算したり、口の両端の位置から表情の変化を確認する。