🥞

InsightFaceの顔検出結果をブラウザ上で動画にオーバーレイ表示してみた

2021/08/14に公開

目次

初めに

前回の記事では、InsightFaceによる顔検出の結果をOpenCVを使って可視化(オーバーレイ表示)し、動画ファイルとして出力しました。

今回は、ウェブブラウザ上で動画にオーバーレイ表示してみたいと思います。

デモ

今回もできあがったデモの動画からご覧ください。

上記はアニメーションGIFなのでカクカクしていますが、実際にはウェブブラウザ上でリアルタイムにレンダリングされた認識結果がヌルヌル動きます。
また、上部のチェックボックスをON/OFFすることで表示内容を切り替えることができます。ウェブブラウザ上で描画しているからこそできる機能ですね。

ビルド&実行

コード全体はGitHub上のリポジトリ「202107-face-detector」にあります。
この記事に対応するタグは20210814aです。

https://github.com/nayutaya/202107-face-detector/tree/20210814a

ビルド、実行例は以下の通りです。

# リポジトリを取得する
git clone https://github.com/nayutaya/202107-face-detector.git
cd 202107-face-detector
# タグをチェックアウトする
git checkout 20210814a

# video-overlayサービスのイメージをビルドする
docker-compose build video-overlay
# video-overlayサービスのコンテナをバックグラウンドで起動する
docker-compose up -d video-overlay

# ブラウザで下記のURLを開く
open http://localhost:8002/

# (試したあとに)Dockerコンテナを停止する
docker-compose down

OpenCVによるオーバーレイ表示

動画の各フレームに対して顔検出などの物体検出を行い、その結果をオーバーレイ表示したいケースはよくあると思います。

手軽な方法の1つとして、前回の記事で行ったように、OpenCVでオーバーレイ表示を行い、動画ファイルとして出力する方法があります。
この方法には、以下のメリット、デメリットがあります。

メリット:

  • OpenCVだけで手軽に実現できる。
  • フレーム単位で正確な結果を描画できる。(時間方向の正確性が高い)
  • 表示の処理負荷が低い。

デメリット:

  • 動画ファイルを生成する必要がある。(容量が増え、管理が大変)
  • OpenCVで凝った描画を行うのが面倒。
  • 表示内容を再生時に変更できない。(生成時に決定する必要がある)

ウェブブラウザ+SVGによるオーバーレイ表示

前述の通り、OpenCVによるオーバーレイ表示は動画ファイルの生成が必要で容量が増え、管理が面倒です。
そこで、ウェブブラウザ上でリアルタイムにオーバーレイ表示する処理を実装してみました。

具体的には、ウェブブラウザ上のvideo要素で動画を再生しつつ、その前面に配置したsvg要素でオーバーレイ表示を行っています。

この方法には、以下のメリット、デメリットがあります。

メリット:

  • 動画ファイルを生成する必要がない。(元の動画ファイルのみ管理すればよい)
  • SVGによる凝った描画を行うことができる。
  • 表示内容を再生時に変更できる。

デメリット:

  • フレームが前後する可能性がある。(時間方向の正確性が低い)
  • 顔検出を行うPythonコードとは別に、JavaScriptで可視化処理を実装する必要がある。
  • 表示の処理負荷が高い。

svg要素を使う方法以外にも、canvas要素とdrawImageメソッドを使い、video要素からフレーム画像を転送した上で結果の描画を行う方法も思いつきます。
ただし、drawImageメソッドはオリジンによる制約を受けるため、クロスオリジンを考慮する必要があります。(svg要素による描画を行う場合、オリジンによる制約は受けません)
また、宣言的な記述を行うReact.jsと相性が良かったため、svg要素を使って描画を行いました。

ちなみに、処理負荷は動画再生に比べて高くはありますが、MacBook Pro上のGoogle Chromeの他、iPad Pro、Android端末(Xperia 1 II)でも問題なく再生できました。

実装のポイント

詳細はソースコードを読んで頂くとして、実装のポイントは以下の通りです。

  • video要素とsvg要素を重ねる
  • video要素から再生位置を頻繁に読み出す
  • 動画のメタ情報は予め取得しておく

video要素とsvg要素を重ねる

前述の通り、動画へのオーバーレイ表示は、video要素とsvg要素を同じ場所に重ねることで実現しています。

video要素をそのまま使用しているため、再生、停止、音量などのコントロールは、ブラウザ標準のインターフェースをそのまま使うことができ、独自に実装する必要がありません。

ただ、video要素にsvg要素を重ねると、マウスイベントはsvg要素が受け取ってしまいます。
そのため、CSSのpointer-eventsプロパティをnoneに設定することで、video要素にマウスイベントが渡るようにしています。

video要素から再生位置を頻繁に読み出す

video要素で再生を行っている間、再生位置の更新を知らせるtimeupdateイベントが定期的に発生します。
ただこのイベントは、(システムに依存しますが)1秒に1回ほどの頻度でしか発生せず、オーバーレイ表示を更新するには頻度が足りません。

そのため、動画の再生時には別途タイマを起動し、30fpsでオーバーレイ表示を更新するため、33ミリ秒毎に再生位置(currentTime)を取得し、オーバーレイ表示を更新しています。

動画のメタ情報は予め取得しておく

video要素から取得できるのは再生位置(単位は秒)だけで、フレーム番号を取得できません。
そのため、再生位置とフレームレート(fps)からフレーム番号を計算し、オーバーレイ表示を行っています。
video要素からはフレームレートを取得できないため、予め動画のメタ情報(幅、高さ、フレームレート)を取得し、ウェブブラウザに渡しています。

その他の補足

今回はデモが目的だったため、動画からの顔検出は別途実施し、変換済みのJSONファイルを配置しています。
再生時に参照されるファイルは、以下の3ファイルです。

  • video-overlay/app/public/pixabay_76889_960x540.mp4
    • 動画ファイルの本体。
    • 動画はPixabayからお借りしました。
  • video-overlay/app/public/pixabay_76889_960x540.mp4.meta.json
    • 動画ファイルのメタ情報を記録したJSONファイル。
    • 手作業で作成しました。
  • video-overlay/app/public/pixabay_76889_960x540.mp4.data.json
    • 顔検出の結果をコンパクトに格納したJSONファイル。
    • video-analyzer/src/convert.pyを使って生成しました。

最後に

動画を入力してInsightFaceによる顔検出を行い、その結果をウェブブラウザ上でリアルタイムにオーバーレイ表示することができました。

次回は、「動画のアップロード」→「顔検出」→「結果の表示」の一通りの流れを行えるウェブアプリを実装してみたいと思っています。

Discussion