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


目次
1. はじめに
SCRFD(Sample and Computation Redistribution for Efficient Face Detection)技術
InsightFaceは顔認識のための統合フレームワークであり、SCRFDはその中で使用される顔検出アルゴリズムの一つである。顔検出は画像から顔の位置を特定する技術であり、顔認識(個人識別)の前段階として使用される。
技術名: SCRFD(Sample and Computation Redistribution for Efficient Face Detection)
出典: Guo, J., Deng, J., An, X., & Yu, J. (2021). Sample and Computation Redistribution for Efficient Face Detection. arXiv:2105.04714.
SCRFDはサンプル分布を効率的に再配分すること特徴とする顔検出技術である。Webカメラからの映像をリアルタイムで処理し、顔検出と5点キーポイント(両目、鼻、口の両端)の検出を実行する。
2. 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
3. プログラムコード
# プログラム名: 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対応
# - 調整を必要とする設定値: DET_SCORE_THRESHOLD(検出信頼度閾値、デフォルト0.5)
# その他の重要事項: 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')
# ===== 調整可能な設定値 =====
DET_SCORE_THRESHOLD = 0.5 # 検出信頼度閾値(0.0-1.0)
# ===== 設定・定数管理 =====
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 # フレームレート
# 検出パラメータ
DET_SIZE = (640, 640) # 検出時の画像サイズ
# 顔とキーポイントの表示色(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():
"""SCRFDモデルの存在を確認し、未ダウンロード時にはダウンロードが必要であることを通知する"""
model_dir = os.path.join(os.path.expanduser('~'), '.insightface', 'models', 'buffalo_sc')
if not os.path.exists(model_dir):
print('SCRFD buffalo_scモデルの初回ダウンロードを行います...')
print('モデル: buffalo_sc(~300MB)')
print('注意: 初回はネットワーク環境により数分かかる場合があります')
else:
print('SCRFD buffalo_scモデルが既に存在します')
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):
"""フレーム単位の処理。顔検出、フィルタ、描画を行う"""
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 >= DET_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']:.1%}"
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']:.1%}"
else:
result = "検出数:0"
# 画面情報表示
info1 = f'InsightFace ({DEVICE_LABEL}) | フレーム: {frame_count} | 検出数: {len(faces_data)}'
info2 = f'操作: q=終了 | 閾値: {DET_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(f'設定: 検出信頼度閾値 = {DET_SCORE_THRESHOLD}, 実行デバイス = {DEVICE_LABEL}')
print()
# システム初期化
print('システム初期化中...')
# フォント確認
check_font_availability()
# SCRFDモデルダウンロード確認
download_insightface_model()
# 顔検出用アプリケーション初期化
print('SCRFD buffalo_scモデルを初期化中...')
app = FaceAnalysis(name='buffalo_sc', providers=PROVIDERS)
app.prepare(ctx_id=CTX_ID, det_size=DET_SIZE)
print('SCRFDモデルをロードしました')
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:
# サンプル動画ダウンロード・処理
SAMPLE_URL = 'https://raw.githubusercontent.com/opencv/opencv/master/samples/data/vtest.avi'
SAMPLE_FILE = 'vtest.avi'
urllib.request.urlretrieve(SAMPLE_URL, SAMPLE_FILE)
cap = cv2.VideoCapture(SAMPLE_FILE)
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)
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.txt', 'w', encoding='utf-8') as f:
f.write('=== 結果 ===\n')
f.write(f'処理フレーム数: {frame_count}\n')
f.write(f'使用デバイス: {DEVICE_LABEL}\n')
f.write('\n')
f.write('\n'.join(results_log))
print(f'\n処理結果をresult.txtに保存しました')
4. 使用方法
- 上記のプログラムを実行する
- 検出された顔に赤色の境界ボックスと緑色のキーポイントが表示される。
- 各顔の下部に検出信頼度(0.000~1.000)が表示される。検出信頼度は0.0から1.0の値で、1.0に近いほど顔である確信度が高い。0.5以上を閾値として使用することが多い。
- 'q'キーを押すとプログラムが終了する。
5. 実験・探求のアイデア
様々な条件での検出性能テスト
- 複数人の同時検出
- 横顔や斜め顔での検出精度
- マスク着用時の検出性能
- 暗い環境での検出限界
- 手で顔の一部を隠した場合の挙動
キーポイント活用の探求
検出された5点キーポイントの座標を利用して、顔の向きや表情の簡易推定を実行する。両目の座標差から顔の傾きを計算したり、口の両端の位置から表情の変化を確認する。