ARCoreのPoint Cloudを調べています。UnityでAndroidアプリのデータ書き出し処理を作るのに意外に手間取ったので、まずはPoint Cloudデータのファイル 書き出しの手順をまとめました。
ARCoreを例に書いていますが、UnityでAndroidスマートフォンの任意の場所にファイル 書き出しをしたい、という場合にも使えると思います。
以下の環境で動作確認しています。
- Unity2017.4.4f1
- Android8.1.0
- jdk1.8.0_171.jdk
1. 今回の検討をした背景
今回、PointCloudのデータを取り出して調べるのに、データをcsvで書き出してPC経由で取り出したいと思いました。アプリ領域内だとcom/limes/appname/<任意のデータ>、のように保存できますが、階層が深くなります。
そこで、浅い階層でも取り出せることを優先し、PC接続時にすぐ取り出せるように、アプリ領域外で保存できる仕組みを整理しました。
なお、ここでの「アプリ領域外」とは、「Download」、「DCIM」など、PCとAndroidを接続した時、エクスプローラ上ですぐ見えるフォルダを指します。
2. 書き出しの手順
2-1. 保存先のパスを取得する
「Unity ファイル パス」のように調べると、Application.dataPath、Application.persistentDataPathなどを使う方法が出てきます。しかし、これらは今回のようなアプリ領域外書き出しには使えません。
一方、Androidのnativeコードには、書き出し先のパスを取得するメソッド「getExternalStorageDirectory」があります。少し前の記事ですが、こちらがわかりやすく、参考になりました。
Android におけるストレージまとめ - Unmotivated
(注)
Android8.1では、getExternalStorageDirectoryメソッドで取得したパスは、「/storage/sdcard0」ではなく、「/storage/sdcard/0」でした。
パスの取得方法を作ろうと思ったら、シンプルに実現する方法が書かれているサイトがありました。
Unityで画像をAndroid本体に保存する | Narumium Blog
こちらを使わせていただいて、パスを取得するコードを書いたのが以下です。
gist2a7003d67c7b9d973291d74275cc32bf
ここに記載のAndroidJavaClassは、UnityからAndroid nativeの関数を呼ぶための方法です。だいぶ前になりますが、下記の記事にUnityでAndroidのnative関数を使う方法を色々とまとめたので、よかったらご参考ください。
Android StudioでUnity向けmoduleを作る方法 - CrossRoad
Android Studioで作ったmoduleをUnityから呼ぶ方法 - CrossRoad
【2017/7更新】Android StudioでUnity向けmodule開発時のTips - CrossRoad
【2016/11/27 更新】UnityでBing Speech to Text API(Android用)を使う方法(Android Studio側の構築手順) - CrossRoad
【2017/3/1更新】UnityでBing Speech to Text API(Android用)を使う方法(Unity側の構築手順) - CrossRoad
2-2. 取得するデータを決める
ARCoreのUnitySDKには、PointCloudVisualizer.csというソースがあります。その一部を使って、point cloudのindexとx,y,z座標を取得します。
gist532fe9f658eba341d45a6a88ac34d9cd
実際は、下記の2つを使います。
private int[] indices = new int[k_MaxPointCount]; private Vector3[] m_Points = new Vector3[k_MaxPointCount];
2-3. ファイル に書き出す
C#にはFileというクラスがあります。しかし、このような記述があるため、今回のように随時書き込む可能性がある場合は、C#のStreamWriterを使う必要があります。
Fileクラスのメソッドは言わばユーティリティメソッドで、ファイル内容すべてを読み書きするというごく基本的な読み込み・書き込み操作しか行えません。 そのため、ファイルの一部分のみを読み込んだり、データ構造やフォーマットが定められたファイルを扱う目的には不向きです。
引用:ファイル 入出力, smdn:総武ソフトウェア推進所, http://smdn.jp/programming/netfx/filesystem/2_filereadwrite/
今回は、先ほど選んだ、indicesとm_Pointsを書き込みます。書き込むための処理は下記の通りです。
gist5aa5c451857e9024b3cabdef13297fbb
ソースコードの通りですが、書き込み場所のフルパスを指定して、StreamWriterクラスのインスタンスを作成し、StreamWriter.WriteLineで1行ずつ書きこむだけです。
sw.Flush(); sw.Close();
を使って最後にクローズ処理をしています。下記の記事より、closeを入れないとうまくいかないようです。
https://qiita.com/HisatakaSuzuki/items/a97f4ebdd4b89afe3dc4
2-4. ファイル 書き出し用のソースコードをPoint Cloudオブジェクトにアタッチする
ARCoreのUnity SDKをインポートし、Assets/GoogleARCore/Example/HelloAR/HellowAR.sceneを開きます。
Hierarchy ViewにあるPoint CloudオブジェクトにあるPoint Cloud Visualizerコンポーネントを削除し、下記のSavePointCloudDataをアタッチします。
gistcfd8aa376a79cbdca3a935c1a375580a
途中のendLoopCountですが、この処理を入れないと、point cloudの数がいくつであっても、indices.Length = k_MaxPointCount = 61440 行の値が書き込まれます。
実際、表面検出に数分かけた程度ではpoint cloudは200個程度でしたので、毎回61440行書き込むのは無駄になります。
そこで、全ての要素が0であることが2回続いたら書き込みを終了させました。
あとは、uGUIのボタンなどで下記を呼び出します。
public void savePointCloudInfo()
すると、
/storage/sdcard/0/pointcloud/yyyyMMddHHmmss.csvというファイル が生成され、point cloudデータが書き込まれます。
2-5. ビルド設定、ビルド
PlayerSettingsを以下のように修正します。
Company Name、Product Name :
任意の名前に変更
Other Settings
Multi Thread Rendering :チェックを外す
Package Name : com.<開発者名>.<アプリ名> のようにする。"."で区切って入れば任意の文字列でよい。たとえば、com.Limes.SampleApp
Minimum API Level:Android 7.0以上
Target API Level:Android 7.0以上で、ビルド対象のスマートフォンのバージョンに合わせる
XR Settings
AR Core Supported :チェックをつける
あとは、BuildまたはBuild and Runを実行します。(Build and Runの場合、Buildと実機デプロイを実行します)
3. 動作確認結果
実行すると、このようにindicesとm_Points(x,y,z)の値を取得できます。
4. 終わりに
今回はARCore特有の話が少なかったので、次回はPoint Cloudデータをもう少し細かく見てみようと思います。