PCLVisualizerのラッパー的なものを作った話

3D Sensor Advent Calendar 2019の11日目の記事です．

# 概要
Point Cloud LibraryのPCLVisualizerが叩きにくいので4年くらい前からラッパーを書いていたのを公開します．ヘッダオンリーです．
プロトタイプ用途で点群可視化・保存・読み込み周りの実装を書き散らかすのに便利な設計を目指したつもりです．
GitHubリポジトリ: https://github.com/naoya-chiba/RabVisualizer

実装力が無い時代からの遺産なので汚いのですが，今後ちゃんと整理することもなさそうですのでそのまま公開ということでお許しください．

## なりゆき
3D Sensor Advent Calendar 2019になにか書きたかったのですが，多忙で新ネタがちょっとできなさそうなので見送っていました．
今見たら枠が空いてたようですので，一応3D Sensor周りということで点群可視化しやすくしましたということで紹介させてもらおうかと思います．

## できること
* 点群の描画
* 点群法線の描画
* 座標系（原点での各軸方向）の描画
* 点ペアの対応関係の描画（点群位置合わせを想定）
* 線の描画
* 2D/3D文字列の描画
* Cubeの描画
* 上記で描画されるデータの保存・読み込み

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37191/5ee0ca3f-2edf-6681-f326-fa85bf582b89.png)
こんな感じの諸々を描画・保存・読み込みできます．

# 依存ライブラリ等
* PCL (Point Cloud Library) 1.9.1
* Boost 1.70.0
* VTK 8.2.0
バージョンは動作確認した時点のものです．PCLがBoostとVTKに依存しますので，PCLが（Visualization込みで）入っていればOKです．

# 使い方
以下で一通り紹介しますが， [sample_main.cpp](https://github.com/naoya-chiba/RabVisualizer/blob/master/sample_main.cpp) を読んでもらうのが良いです．

## 基本的な使い方
ヘッダオンリーなので`rabv.hpp`をインクルードしたら使えます．

描画するデータはすべて`Rab`クラスのインスタンスとして取り扱います．PCLに合わせて`boost::shared_ptr`で管理することを前提にしています．
自分でnewしても良いですが，`create()`で作成，`make_shared()`で複製できます．

```
auto rab = rabv::Rab::create();
```

## 各種可視化対象の追加

rabネームスペースにあるLines, Text, Text3D, FlatText3D, Cloud, Normal, Correspondence, CoordinateSystemあたりを生成してrabに加えていくことができます．メンバ関数の`addほげほげ`が対応します．削除時には`removeほげほげ`です．
追加時には第一引数として名前，第二引数で可視化対象データを渡します．個々で指定する名前で管理しますので，同種の可視化対象にはユニークな名前を与えてください．
第二引数に可視化対象データのインスタンスの代わりに，可視化対象データのコンストラクタの引数を渡すことができます．こちらのほうが使いやすいので推奨，以下の例でもこちらを使用します．

```
// 点群を可視化
rab->addCloud(
	"sample1",   // ユニークな名前
	sample_cloud // pcl::PointCloud<pcl::PointXYZ>::Ptrな点群データ
);

// 点群を可視化，色々と同時に設定
rab->addCloud(
	"sample2",                    // ユニークな名前
	sample_cloud,                 // pcl::PointCloud<pcl::PointXYZ>::Ptrな点群データ
	1,                            // 点の大きさ
	rabv::Color(255, 255, 128),   // 点群の色 (r, g, b)
	rabv::Point(2.0, 0.0, 0.0),   // 並進オフセット (x, y, z)
	rabv::Rotation(0.5, 0.0, 0.0) // 回転 (X-axis, Y-axis, Z-axis)
);

// 点群と法線の可視化
rab->addCloudNormal(
	"sample3",                  // ユニークな名前
	sample_point_normal,        // pcl::PointCloud<pcl::PointNormal>::Ptrな点群＋法線データ
	1,                          // 点の大きさ
	rabv::Color(255, 0, 128),   // 点群の色 (r, g, b)
	rabv::Point(3.0, 0.0, 0.0), // 並進オフセット (x, y, z)
	rabv::Rotation(),           // 回転 (X-axis, Y-axis, Z-axis)
	1,                          // 法線の表示密度
	0.05,                       // 法線の長さ
	1,                          // 法線の太さ
	rabv::Color(0, 255, 255)    // 法線の色
);

// 点群を追加した後に法線を追加
rab->addNormal(
	"sample1",                  // 法線を追加する対象の点群名
	sample_normal,              // pcl::PointCloud<pcl::Normal>::Ptrな法線データ
	1,                          // 法線の表示密度
	0.05,                       // 法線の長さ
	2,                          // 法線の太さ
	rabv::Color(255, 0, 128)    // 法線の色
);
	
// ワールド座標系の表示
rab->addCoordinateSystem(
	"world", // 座標系名，"world"を指定するとワールド座標系
	0.3      // 矢印のスケール
);

// 点群座標系の表示
rab->addCoordinateSystem(
	"sample2", // 座標系名，ここでは"sample2"の座標系を表示
	0.2        // 矢印のスケール
);

// 2つの点群に含まれる点の対応を線で結んで可視化
rab->addCorrespondence(
	"corr1",   // 対応名
	"sample1", // 対応元の点群名
	"sample2", // 対応先の点群名
	corrs1     // pcl::Correspondencesかpcl::CorrespondencesPtrかstd::vector<std::pair<int, int>>な対応関係データ
);

// 2つの点群に含まれる点の対応を線で結んで可視化，色々と同時に設定
rab->addCorrespondence(
	"corr2",                   // 対応名
	"sample1",                 // 対応元の点群名
	"sample2",                 // 対応先の点群名
	corrs2,                    // pcl::Correspondencesかpcl::CorrespondencesPtrかstd::vector<std::pair<int, int>>な対応関係データ
	2,                         // 線の太さ
	rabv::Color(255, 255, 128) // 線の色 (r, g, b)
);
```
Line, Text, Text3D, FlatText3D, Cubeは煩雑で面白くないので省略，[sample_main.cpp](https://github.com/naoya-chiba/RabVisualizer/blob/master/sample_main.cpp) を見てください．．．

## 可視化
`rab::Viewer`を使います．コンストラクタに`boost::shared_ptr<rabv::Rab>`を渡してあげるとそのまま可視化できます．
メンバ関数`spinLoop()`でウインドウを閉じるまで表示していてくれます．ブロッキングして欲しくない場合はメンバ関数`spinOnce(time[ミリ秒])`で指定した時間で返ってきてくれます．
Shiftキーを押しながら点群の点をクリックすると，その点に関する情報（インデックス，座標，原点からの距離，前に選択した点からの距離）がコンソールに出力されます．

```
const auto& viewer1 = rabv::Viewer::create(
		"Viewer1",	// ビューアーのウインドウ名
		rab		// boost::shared_ptr<rabv::Rab>なrabデータ
	);

viewer1->spinLoop();
```

## 保存と読み込み
rabデータはまるごと保存と読み込みができます（多分）．
保存先パスに指定したファイル名の拡張子の前までと同名のディレクトリが作られ，点群などのデータが保存されます．
rabファイル自体はxmlですので修正などが容易です．特にvisibleというタグがあり，これをtrue/falseで切り替えることで可視化のオンオフができます．
`main.cpp`をコンパイルするとrabファイルを読み込んで表示するビューアーとなっています．

```
// 保存
rabv::Writer::saveRabFile(
	"./test1.rab", // 保存先パス
	rab            // 保存するboost::shared_ptr<rabv::Rab>なrabデータ
);

// 読み込み
auto load_rab = rabv::Reader::loadRabFile("./test1.rab");
```

## その他
- 3D Sensorの話じゃないのでは？
    - ごめんなさい，他に数時間でかけるようなネタがなかったのですがどうしても何か書きたかったので．．．ダメであれば消しますのでお知らせください．．
- rabって何？
    - 当時Stanford Bunnyばかり見る日々で心が疲れていたのですが，Is the Order a Rabbit?に救われたので．Rabbit Visualizerです．

最後ですが，当時実装を手伝ってくれた研究室元同期の友人，使ってくれた先輩・後輩諸氏に感謝します．ありがとうございます．