Dlib FaceLandmark Detectorの初期化時に画面が固まる問題
FaceLandmarkDetectorクラスのインスタンスを生成時、数秒間ほど画面が固まってしまう。
PCやiPhoneではギリで気にならない程度だが、Android端末ではちょっと焦る!
原因
Assetに同梱されている顔器官検出用学習データファイル(sp_human_face_68.dat)のサイズが約100MBもあり、コンストラクターでの初期化の際にそのファイルの読み込み処理に時間がかかるためだと思われる。
対策
初期化処理に時間がかかるのは仕方が無いとして、せめて画面が固まるのを何とかしたい。
重い処理をメインスレッドで同期的にではなく別スレッドを立てて非同期的に実行することでフレームを止める事無く初期化処理が可能になるはず。
手軽にマルチスレッドで処理するために今回は「UniRx」を利用することにした。
UniRxとは
UnityでReactive Programmingを実現できる神Asset(無料)
https://assetstore.unity.com/packages/tools/integration/unirx-reactive-extensions-for-unity-17276
(今回はObservable.Start()という処理を別スレッドで実行することが可能になるオペレータを使用)具体的な使い方はgitのREADMEに詳しく書いてあります。
https://github.com/neuecc/UniRx
コード例
あらかじめAssetStoreからUniRxをインポートしておいて、以下のコードに置き換える。
public class ExampleScene : MonoBehaviour
{
FaceLandmarkDetector faceLandmarkDetector;
void Start()
{
Debug.Log("Start!");
//次のフレームで実行
Observable.NextFrame()
.Subscribe(_ => initFaceLandmarkDetector(DlibFaceLandmarkDetector.Utils.getFilePath("shape_predictor_68_face_landmarks.dat")));
}
private void initFaceLandmarkDetector(string filePath)
{
//別スレッドでDlib初期化処理開始、終了を待って処理後の処理へ
Observable.Start(() =>
{
// heavy method...
faceLandmarkDetector = new FaceLandmarkDetector(filePath));
})
.ObserveOnMainThread() // return to main thread
.Subscribe(x =>
{
Debug.Log("Finish!");
//次のフレームで実行
Observable.NextFrame()
.Subscribe(_ => startGame());
});
}
private void startGame()
{
//Dlib初期化処理後の処理
}
}
この方法で画面が何秒間も固まるような事態は回避出来た。
あとは初期化処理終了待っている間に画面にローディングアニメを表示するなど、ユーザーに不安を与えないような演出を追加すればいい。