HoloLensで部屋の3Dスキャン試してみた

# HoloLensで部屋の3Dスキャン試してみた

## tango 凄い！しかし HoloLens だって…。
- 先日、twitterで以下の話が出てました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Tango持って家の中をぐるぐる歩いてMatterport Scenesアプリに自宅をスキャンさせてみた。完全に俺の家だ… <a href="https://t.co/iCIZpDHeEZ">pic.twitter.com/iCIZpDHeEZ</a></p>&mdash; ようてん (@youten_redo) <a href="https://twitter.com/youten_redo/status/856163553849376773">2017年4月23日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

- tangoは低レベルでPoint Cloudデータ（点群データ）取れるらしく、こういうアプリがあるそうです。
- なのでHoloLensで同じ事できないか？と思いテストしてみました。<br />
（もし、既にHoloLensでこういったアプリがあったら教えてくださいっ！）

## Special Mapping をそのまま利用する？

[HoloLensの Special Mapping データ(点群)を取り出す](http://www.naturalsoftware.jp/entry/2016/06/01/120341)（Natural Software）

- 最初に上記ブログを見たのですが、HoloLensの空間認識は単色ポリゴンで行ってるようで、
グレイスケールで思ってたのと違いました。
（すいません）

## Locatable camera を使う？
[Locatable camera in Unity](https://developer.microsoft.com/en-us/windows/mixed-reality/locatable_camera_in_unity)（Microsoft Windows Dev Center）

- twitterで色々聞いたりして、このサイトにたどり着きました。
こちらによると、フォトキャプチャで３つの事ができるようです。
 1. ファイルに写真を取り込む
 2. テクスチャ2Dに写真を取り込む
 3. 写真を撮影し、生のバイトデータを取得する

- １番はjpegファイルが作成できますが今回の目的とは違います。
- ２番は表示ができるようになります、以下のような表現もできるようです。
[Holographic Photo Blending with PhotoCapture](https://forum.unity3d.com/threads/holographic-photo-blending-with-photocapture.416023/)（forum.unity3d）
ですが、このような記述を見て心おれました。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ですよね～。＞空間マッピングされたメッシュに独自のUVを追加することはできますが、それはやりたいことによっては少し複雑かもしれません。<a href="https://t.co/a6jYrl5g9V">https://t.co/a6jYrl5g9V</a></p>&mdash; saco (@sacokoala) <a href="https://twitter.com/sacokoala/status/857313799262445568">2017年4月26日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
- ３番は色情報をそのまま取れるようで、２番がダメでもこっちなら都合良くできそうです。

## Point Cloud データの表示
[Point Cloud Free Viewer](https://www.assetstore.unity3d.com/jp/#!/content/19811)（unity asset store）

- このassetをそのまま使おうかと思ったら、事前処理？が必要そうでうまくいきませんでした。
- assetからは頂点カラー表示のshaderだけお借りして、以下のサイトを参考にRawDataからmeshを作成しました。

[UnityでPoint Cloudを表示する方法](https://blog.negativemind.com/2015/05/15/point-cloud-for-unity/)（NegativeMindException）

## 写真を撮影した方向にPoint Cloud データ表示

- 先程作成したMeshデータを表示する際、カメラの向きに応じた座標変換が必要になります。
- 理論的な事はここに書いてあるようです。
[Locatable camera](https://developer.microsoft.com/en-us/windows/mixed-reality/locatable_camera)（Microsoft Windows Dev Center）

- 理論はとにかく、実際の座標変換コードはさっき提示したここに載っています。
[Holographic Photo Blending with PhotoCapture](https://forum.unity3d.com/threads/holographic-photo-blending-with-photocapture.416023/)（forum.unity3d）

- めっちゃ簡単に言うと、 photoCaptureからTryGetCameraToWorldMatrixでカメラの方向とってそれを反映するだけです。
- こんなカンジに表示されるようになりました。
![camera01.jpg](https://qiita-image-store.s3.amazonaws.com/0/61493/89a08441-5c05-80d0-204a-ca02de8ef881.jpeg)

※画像はunity上のイメージで、実際の部屋とは関係ありません。

## Point Cloud データをSpatial Mapping の表面に張り付ける。

- これは皆さんお馴染みの、RaycastでカメラからPoint Cloud方向へ地形判定して、当たったらSpatialMappingと判定して座標移動、外れたらPoint Cloud捨ててるだけです。
- こんなカンジで地形に沿うようになりました。
![camera_anim01.gif](https://qiita-image-store.s3.amazonaws.com/0/61493/80c55e24-4b93-04c3-2a1b-7980ce7533fb.gif)

※画像はunity上のイメージで、実際の部屋とは関係ありません。

## 座標系を合わせる（5/9追記）
- そういえば、出力したPoint Cloudが何故か左右逆だった時がありました。
- 理由もわからずとりあえず左右逆転しておいたのですが、つい先日理由が判明しました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">皆さん大好きな地獄の座標系のお話<br>HoloLens: X右 Y上 Z後<br>Unity: X右 Y上 Z前<br>OpenCV: X右 Y下 Z前<br>全部共通なのはX右だけという…。HoloLensのPhotoCaptureは上記座標系で値を返すので注意が必要です</p>&mdash; あるしおうね (@AmadeusSVX) <a href="https://twitter.com/AmadeusSVX/status/861441312062529539">2017年5月8日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

    PointColorList.Add(String.Format(" {0} {1} {2} {3} {4} {5} {6}\n",
                                     -PointAll[i].x, PointAll[i].y, PointAll[i].z,
                                      ColorAll[i].r, ColorAll[i].g, ColorAll[i].b, ColorAll[i].a));

- なので、上の謎のマイナス記号は察してください（多分off形式の座標系が違う。）

## Point Cloud データをファイルセーブする。

[HoloLensでのアプリからのファイル書き込み](http://www.naturalsoftware.jp/entry/2017/03/04/203152)（Natural Software）

- 上記ブログを参考に、MeshにせずListに残しておいたPoint Cloudデータにヘッダつけて、*.off形式のファイルとしてセーブしています。
- いつソフトが落ちても大丈夫なように、途中経過毎回全部セーブしてます、無駄ですみません…。

## MeshLab で Point Cloud データを表示する。

- *.offファイルは[MeshLab](http://www.meshlab.net/)で簡単に表示できます。
- こんなカンジで、HoloLens単体で3Dスキャンした物体をカラーで表示一応できました。
![ueno_panda01.jpg](https://qiita-image-store.s3.amazonaws.com/0/61493/33cdfce9-ab4a-1fc9-5161-6972806090f1.jpeg)
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/HoloLens?src=hash">#HoloLens</a> で点群データ、小さいぬいぐるみじゃなくて大きなぬいぐるみなら行けるはずだ！という思いで上野駅でチャレンジしてみたが、ガラスケースに入っていて、赤外線は透過、可視光線は反射という罠にはまり、パンダの顔に盛大に反射した白いラインが…。 <a href="https://t.co/3E7h7KJXPb">pic.twitter.com/3E7h7KJXPb</a></p>&mdash; saco (@sacokoala) <a href="https://twitter.com/sacokoala/status/860912203125276672">2017年5月6日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

## ソースコード
- 以下に置きました。
[HoloPointCloud](https://github.com/sacokoala/HoloPointCloud/blob/master/README-jp.md)（GitHub）

- まだ試した方いないので、ファイル不足していたら追加します。

## 実行ファイル
- 以下に置きました、手っ取り早く試してみたい方は是非どうぞ。<br />
[HoloPointCloud_1.0.1.0_Test.zip](https://drive.google.com/open?id=0B9SjzvwZtqNJQ2wtQ2EyMEJGTk0)（GoogleDrive）

## 今後の課題

- 移動しながら撮影していると、どこかの誤差から少しづつ座標がずれてる気がする。（要検証）
- カメラの露出で色が不安定になるので露出固定して撮影したい（今はライティングでカバー）
- 適当にmesh作成してるだけで落ちるので、もっとunityの状況（描画やメモリ）を調べてエラーで落ちないようにする。
- point cloud データが重なって無駄な部分を削除する。（MeshLab上でできますが）
- 点群データの座標の倍率、実は見た目で合わせたマジックナンバー入ってます（200）これを、カメラからの距離（正規化されたfrontなので多分1m）と画角と解像度から、正しく計算した値に直す。
- Point Cloud の密度がカメラ全体でmeshが落ちない65000以下で最大と適当なので、例えば撮影範囲の中心だけ使って高密度とか、もっと速度重視の低密度とか、色々試してみたい。(数値65000に修正しました)
- ファイルセーブが手抜きすぎるのでDocumentにoffファイル関連付けてロードもできるように改造すべき。

GW終わってしまったので、課題手を付ける予定は未定です（すみません）