catalinaの備忘録

ソフトウェアやハードウェアの備忘録。後で逆引きできるように。

Hololensでカメラの解像度を変更する

前回つくったカード検出器をHololensで使ってみました。かたりぃなです。 処理がやっぱり重いみたいで表示がカクつくくらいに気持ち悪いです。

本当に負荷が原因なのか知るために、画像処理全体の負荷を下げて試すことにします。

簡単に負荷を下げる方法として、カメラの設定(解像度、フレームレートなど)を下げてしまうことにしました。

もちろんアルゴリズムによっては低解像度では使えないなど弊害はありうるので注意が必要です。

簡単に試したい理由は、アルゴリズムの最適化をかける前に「そもそも負荷が低ければアプリとして成立するのか?」を確認したいからです。

個人開発での限りある時間を無駄にはしたくないので。

カメラ(MediaCapture)の初期化

いつもどおりマニフェストファイルにカメラを使うよう設定してから、mediaCaptureクラスを使います。 Platform::AgileはいわゆるスレッドセーフなCLRらしいです。

初期化コードはこんな感じです。

        Platform::Agile<MediaCapture> mediaCapture( ref new MediaCapture() );
        return create_task(mediaCapture->InitializeAsync(settings))
            .then([=]
        {
                // ここでmediacaptureのカメラを起動する
        });        

カメラの解像度を変更

カメラ選択のUIとか作ろうかと思いましたが、面倒なのでやめました。

実験だけのつもりなのでデバッガで出力して、パラメータを書き換えるだけのほうが手っ取り早いです。

コードだけ簡単に。

         // ここまででmediaCaptureはインスタンス化されていること。開始はしてなくてもよい。

            // 負荷が低くなるよう、小さめのサイズのカメラ入力画像を設定する
            auto FindPreviewResolutions = [](Platform::Agile<MediaCapture> cap) -> Windows::Media::MediaProperties::VideoEncodingProperties ^
            {
                auto prop_list = cap->VideoDeviceController->GetAvailableMediaStreamProperties(MediaStreamType::VideoPreview);
                if (prop_list->Size == 0) {
                    // todo
                }

                Windows::Media::MediaProperties::VideoEncodingProperties ^ vp;
                for (auto prop : prop_list) {
                    auto name = prop->GetType()->FullName;
                    if (name == ref new String(L"Windows.Media.MediaProperties.VideoEncodingProperties")) {
                        auto video_prop = static_cast<Windows::Media::MediaProperties::VideoEncodingProperties ^>(prop);
                        char buf[1024];
                        sprintf_s(buf, 1024, "w=%d, h=%d\n", video_prop->Width, video_prop->Height);
                        OutputDebugStringA(buf);
                        if (video_prop->Width == 896) {
                            vp = video_prop;
                        }
                    }
                }
                return vp;
            };
            auto vp = FindPreviewResolutions(mediaCapture);
            // 低解像度のプロファイルがとれたので、設定する

簡単に説明。 cap->VideoDeviceController->GetAvailableMediaStreamProperties(type)で、メディアのプロパティリストが取れます。

このリストの要素はIMediaEncodingPropertiesというインターフェースなので、prop->GetType()->FullNameに目的のプロパティが入っていることを確認します。これはStringなので文字列比較です。

目的の型が入っていることが確認できたら、その型にキャストしてからプロパティを読み出します。

こういうキャストって個人的にはちょっと嫌です。ダウンキャストっぽく見えて。 たぶんただの宗教観なので気にしないことにします。MSのサンプルコードもこういう形になっていたので。

最後に取得したプロファイルをmediaCaptureに設定してあげれば完了です。 こんな感じに。 asyncなのでtaskでラップしてあげるのを忘れずに。

            mediaCapture->VideoDeviceController->SetMediaStreamPropertiesAsync(MediaStreamType::VideoPreview, vp);

というわけでこのコードをHololensの実機に放り込むと、サクサクとカメラ映像を処理できるようになりました。

つまり、カード検出まわりのアルゴリズムをもうちょっと工夫すればなんとかなるレベルというわけですね。 もしくは低解像度で検出できる仕組みを考えるか。

感想

C++らしくテンプレートで分岐できないかなと考えましたが、すぐには無理でした。

というのも、Stringの中身は実行時に確定するので、テンプレートを展開するコンパイル時にそれを判断する処理を入れるのにはちょっと工夫が必要になってしまいます。

ここまで書いて気づきましたが、いわゆるvisitorパターンを実装してあげればキャストなしでいけそうな気がします。 つまり任意のメディア型をacceptするクラスを作る。

しかしvisitorまで書き始めてしまうと今回の目的と比べると大がかりすぎるからやめておきます。

では今回はこれくらいで。