PCLライブラリの使い方

pclが使えるセンサーはKinect v2など多数存在する。
PCLの大まかな使い方はDERiVE コンピュータビジョンブログ[「PCLを触ってみよう」](http://derivecv.tumblr.com/tagged/PCL%E3%82%92%E8%A7%A6%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86%EF%BC%81)(全17回)が詳しい。

今回はたまたま VLP と呼ばれるセンサーを扱う機会があったので、要点をメモ。
### **PCLの型について**
点群の変数を宣言するときには、
> - pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGB>);
-  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
-  pcl::PointCloud<pcl::PointXYZI>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZI>);

などの宣言方法がある。
pcl::PointXYZRGB型は、点群の位置情報＋色情報RGB。
pcl::PointXYZ型は、点群の位置情報。
pcl::PointXYZI型は、点群の位置情報＋輝度値。

これら自体は簡単であるが、サンプルコードによっては(特に少し前の)、pcl::PointXYZ型がやたらと多いので、もし色情報もviewerに表示させたいときは注意が必要である。
違う型でコンパイルが通らないときは、pcl::copyPointCloud を使う。ただし、コンパイルは通ったとしても、pcl::PointXYZ から pcl::PointXYZRGBの変換では、色情報はないので注意。

### **PCLのライブラリについて**

PCLは、平面除去やクラスタリングなど様々な実装が既にされており、フルスクラッチで実装する必要はない( [公式チュートリアル](http://pointclouds.org/documentation/) ) 。

#### **PassThrough Filter**
これは、探索範囲を限定することができ、XYZの各軸ごとに設定ができる
例えば、X軸のみを探索する場合

            pcl::PassThrough<pcl::PointXYZI> pass;
            pass.setInputCloud (cloud);
            pass.setFilterFieldName ("x");
            pass.setFilterLimits (最小値,最大値);

と設定する。センサーの分解能にもよるがこの際、あまり小さな範囲でフィルターをかけてしまうと点群が得られない。XYZすべてでフィルターをかけたい場合は、X→Y→Zの順番で行う必要がある。

#### **平面除去(Surface segmentation)**

            pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new    pcl::PointCloud<pcl::PointXYZRGB>);
            pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
            pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
            
            pcl::SACSegmentation<pcl::PointXYZRGB> seg;
            
            seg.setOptimizeCoefficients (true);
            // Mandatory
            seg.setModelType (pcl::SACMODEL_PLANE);//検出するモデルのタイプを指定
            seg.setMethodType (pcl::SAC_RANSAC);//検出に使用する方法を指定
            seg.setDistanceThreshold (0.1);//RANSACの最小二乗法の許容誤差範囲
            seg.setMaxIterations(100);
            seg.setProbability(0.95);


            pcl::ExtractIndices<pcl::PointXYZRGB> extract;
            pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_output (new pcl::PointCloud<pcl::PointXYZRGB>);//点群を保存
            extract.setInputCloud(cloud);
            extract.setIndices(inliers);
            extract.setNegative(true);//trueの場合出力は検出された平面以外のデータ falseの場合は平面のデータ
            extract.filter(*cloud_output);

ここではRANSACを用いて平面の検出を行っている。setDistanceThresholdの閾値を高くすればするほど、平面の検出はシビアになる。

除去される平面は、検出された平面のなかで一番面積が大きい平面である。同じ処理をおこなえば、平面を二回除去できる。

#### **クラスタリング**

PCLなどを扱う際にはリアルタイムで動作するようなものが求められるので、Min-cut based Algorithmなどは中々使いにくい。kdtreeやoctreeを使うのが適切である。

            pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new 　　　　　　　　　　pcl::search::KdTree<pcl::PointXYZ>);
            tree->setInputCloud (cloud);
            std::vector<pcl::PointIndices> cluster_indices;
            pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
            ec.setClusterTolerance (0.1); // 0.02なら2cm　VLPは分解能は低めなので許容範囲は広く
            ec.setMinClusterSize (100);//点群の最小サイズを指定
            ec.setMaxClusterSize (2500);//点群の最大サイズを指定
            ec.setSearchMethod (tree);
            ec.setInputCloud (cloud);
            ec.extract (cluster_indices);

上記のコードは、kdtreeを用いたものである。このクラスタ一つ一つにアクセスしようとするには、for文を使う必要がある。
>for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin (); it != cluster_indices.end (); ++it)

という風に書く。

次回も、他のライブラリの実際に使い方について記載するつもりです＼(_ _。）。