重複保存を抑える設計を重視しています。midori225 は顔検出に加えて年齢・性別・感情を推定するブラウザ向けアプリです。Webカメラや画面共有を往復させながら、IndexedDB(ブラウザ内のデータベース)に残す履歴をきれいに保つラインを探りました。
対象読者
- ブラウザで顔検出を走らせているフロントエンドエンジニア
- IndexedDB(ブラウザ内のデータベース)を使った履歴管理の実例を探している人
- face-api.js(顔検出ライブラリ)や TensorFlow.js(ブラウザ上で機械学習を実行するライブラリ)の運用を数字で把握したい開発者
記事に書いてあること
- midori225 で導入した類似度フィルタと履歴ビューの構造
- 色ヒストグラムと統計パネルを実際に計測した結果
- 設定パネルやソース切り替えでハマった実装上の落とし穴
作ったもの
midori225 は Web カメラ / Web 動画 / 画面共有をタブで切り替え、顔と感情の検出ログを 20 件まで保持します。source-tab をクリックイベントだけで切り替え、Three.js の背景や Google Translate コンポーネントと衝突しない最小構成にしました。
類似度判定を信頼できる形に
最も手を入れたのは予測処理を行うコンポーネントの重複判定です。バウンディングボックスの IoU(重なり具合の指標)・色ヒストグラム・面積の類似度を 0.4 : 0.3 : 0.3 で重み付けし、combined が 0.75 を超えたら保存しません。
// midori225/libs/prediction-component.js(抜粋)
async evaluateSimilarity(currentDetection, previousDetection) {
const weights = { iou: 0.4, color: 0.3, size: 0.3 };
let maxIoU = 0;
for (const currentBox of currentDetection.boxes) {
for (const previousBox of previousDetection.boxes) {
const iou = calculateIoU(currentBox, previousBox);
if (iou > maxIoU) {
maxIoU = iou;
}
}
}
const combined = weights.iou * maxIoU +
weights.color * colorSimilarity +
weights.size * sizeSimilarity;
return {
isSimilar: combined > this.similarityThreshold.combined,
scores: { iou: maxIoU, color: colorSimilarity, size: sizeSimilarity, combined }
};
}
実際に数値を出すと、同じフレームは combined 0.946、画角が少しズレても 0.833 で弾かれ、別人になると 0.586 まで落ちることが分かりました。意外でした。ヒストグラムだけでなく面積比を足したことで、基準値 0.75 がちょうどよかったです。
重複排除に通ったフレームだけ IndexedDB(ブラウザ内のデータベース)に落とし、検出保存イベントで履歴を更新します。シミュレーターでは 4 フレーム中 2 件のみ保存され、残りは combined が 0.75 を超えたためスキップされました。
履歴カードのサムネイルは 32×32 に縮めてから 20px ずつ余白を足すため、面積は 32400px² から 48400px² へ約 1.49 倍になります。肌色以外の情報も残せる形です。
ヒストグラムと統計の検証
calculateColorHistogram は 32×32 に縮めた画像を 512 ビンの RGB ヒストグラムに落とし込みます。夕焼けや肌色のノイズを当てて、支配的なビンを可視化しました。
updateDetectionStatistics は 1 秒ごとに frameCount/elapsed を計算し、検出数と平均信頼度を即時表示します。0.2 秒刻みの 12 フレームで試すと、最初の 1 秒は 5.99 fps、次の 1 秒は 5.00 fps でした。想定どおりの挙動を確認しました。
設定とソース切り替えで噛んだ話
設定パネルは localStorage に EmotionDetectionSettings をそのまま保存します。処理速度セレクトには「15/8/4 FPS」とコメントを付け、次のアップデートで predictionComponent.fps に反映させる余地を残しました。
この時点では未反映のため、高速を選んでも 8 fps のままでした。まだ predictionComponent に値を渡していないためで、次の課題に残しています。
ログと初期化の見通し
addLog は最新 20 件を上に積み、種別ごとに色を変えます。detection-complete のたびに「😄 3人の顔を検出しました!」が流れ、エラーも埋もれません。
初期化フローは DOMContentLoaded → webcam-ready → ensureModelsLoaded → startPrediction → detection-complete。face-api.js が固まったら、ここを追えば大体原因が見えます。不具合が疑われる場合は、このフローを辿ると原因を特定しやすくなります。
使ってみて
まとめ
- IoU(重なり具合の指標)・色ヒストグラム・面積の combined を 0.75 で区切ると、ほぼ同じフレームは 0.946、別人は 0.586 まで下がった
- 32×32 の切り抜きに 20px の余白を付けると面積が 1.49 倍になり、履歴の顔サムネイルが安定した
- 統計パネルは 1 秒で 5.99 fps → 5.00 fps と動き、平均信頼度 88% 付近を正しく追従できた
- 設定パネルは localStorage に保存するだけなので、次回アップデートで推論間隔への反映を実装予定
さらに深く学ぶには
最後まで読んでくださり、ありがとうございました。少しでもカメラアプリのチューニングのヒントになれば嬉しいです。