その前に、前回使ったPointXYZRGBの色の部分がfloat rgbとなっていて気になったので調べてみました。
// pack r/g/b into rgb
uint8_t r = 255, g = 0, b = 0; // Example: Red color
uint32_t rgb = ((uint32_t)r << 16 | (uint32_t)g << 8 | (uint32_t)b);
p.rgb = *reinterpret_cast<float*>(&rgb);
↑のような感じで使うみたいです。
悪名高きreinterpret_castか!と思ったら、
ver.1.1.0からはr,g,b変数も使えるみたいで、
point.r = 255;
point.g = 100;
point.b = 10;
みたいに指定できるようです。
という小ネタを挟みまして、今回は平面検出です。
↓のようにPointCloudから平面っぽいところを切りだしてくれるはずです。
チュートリアルはこちら
チュートリアルにあるコードは簡単なんですが、ビューワが付いていないので、ビューワを付けてみました。
以下をplanar_segmentation.cppとして保存してください。
以下をplanar_segmentation.cppとして保存してください。
- #include <iostream>
- #include <pcl/ModelCoefficients.h>
- #include <pcl/io/pcd_io.h>
- #include <pcl/point_types.h>
- #include <pcl/sample_consensus/method_types.h>
- #include <pcl/sample_consensus/model_types.h>
- #include <pcl/segmentation/sac_segmentation.h>
- #include <pcl/visualization/cloud_viewer.h>
- int
- main (int argc, char** argv)
- {
- pcl::PointCloud<pcl::PointXYZRGB> cloud;
- // Fill in the cloud data
- cloud.width = 15;
- cloud.height = 10;
- cloud.points.resize (cloud.width * cloud.height);
- // Generate the data
- size_t i;
- for (i = 0; i < cloud.points.size ()/2; ++i)
- {
- // 平面におく
- cloud.points[i].x = 1024 * rand () / (RAND_MAX + 1.0);
- cloud.points[i].y = 1024 * rand () / (RAND_MAX + 1.0);
- cloud.points[i].z = 0.1 * rand () / (RAND_MAX + 1.0);
- cloud.points[i].r = 255;
- cloud.points[i].g = 255;
- cloud.points[i].b = 255;
- }
- for (; i < cloud.points.size (); ++i)
- {
- // ちらばらせる
- cloud.points[i].x = 1024 * rand () / (RAND_MAX + 1.0);
- cloud.points[i].y = 1024 * rand () / (RAND_MAX + 1.0);
- cloud.points[i].z = 1024 * rand () / (RAND_MAX + 1.0);
- cloud.points[i].r = 255;
- cloud.points[i].g = 255;
- cloud.points[i].b = 255;
- }
- // Set a few outliers
- cloud.points[0].z = 2.0;
- cloud.points[3].z = -2.0;
- cloud.points[6].z = 4.0;
- std::cerr << "Point cloud data: " << cloud.points.size () << " points" << std::endl;
- for (size_t i = 0; i < cloud.points.size (); ++i)
- std::cerr << " " << cloud.points[i].x << " "
- << cloud.points[i].y << " "
- << cloud.points[i].z << std::endl;
- pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
- pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
- // Create the segmentation object
- pcl::SACSegmentation<pcl::PointXYZRGB> seg;
- // Optional
- seg.setOptimizeCoefficients (true);
- // Mandatory
- seg.setModelType (pcl::SACMODEL_PLANE);
- seg.setMethodType (pcl::SAC_RANSAC);
- seg.setDistanceThreshold (0.1);
- seg.setInputCloud (cloud.makeShared ());
- seg.segment (*inliers, *coefficients);
- if (inliers->indices.size () == 0)
- {
- PCL_ERROR ("Could not estimate a planar model for the given dataset.");
- return (-1);
- }
- std::cerr << "Model coefficients: " << coefficients->values[0] << " "
- << coefficients->values[1] << " "
- << coefficients->values[2] << " "
- << coefficients->values[3] << std::endl;
- std::cerr << "Model inliers: " << inliers->indices.size () << std::endl;
- for (size_t i = 0; i < inliers->indices.size (); ++i) {
- std::cerr << inliers->indices[i] << " " << cloud.points[inliers->indices[i]].x << " "
- << cloud.points[inliers->indices[i]].y << " "
- << cloud.points[inliers->indices[i]].z << std::endl;
- cloud.points[inliers->indices[i]].r = 255;
- cloud.points[inliers->indices[i]].g = 0;
- cloud.points[inliers->indices[i]].b = 0;
- }
- pcl::visualization::CloudViewer viewer("Cloud Viewer");
- viewer.showCloud(cloud.makeShared());
- while (!viewer.wasStopped ())
- {
- }
- return (0);
- }
CMakeLists.txtはいつもと同じです。
$ cmake .
$ make
でビルドして、
$ ./planar_segmentation
で実行してみます。
SACSegmentationというClassが今回の本質のようです。ここにSACMODEL_PLANE=平面モデルを指定したり、
setDistanceThresholdでモデルとの誤差の閾値を指定しています。setMethodTypeで計算方法を選択(今回はSAC_RANSAC)しています。RANSACは確率的なモデル推定だったきがします。
coefficients->valuesに入っているのは推定されたモデル
ax+by+cz+d=0の平面の式になります。
これで平面上にあるポイントが検出できました。
さらにKinectでやってみました。前回のKinectのキャプチャサンプルに今回のを合体させてみました。
一番広い平面が真っ赤にそまります。これは楽しい!
一応ソースコードです。適当です。
- #include <iostream>
- #include <pcl/ModelCoefficients.h>
- #include <pcl/io/openni_grabber.h>
- #include <pcl/io/pcd_io.h>
- #include <pcl/point_types.h>
- #include <pcl/sample_consensus/method_types.h>
- #include <pcl/sample_consensus/model_types.h>
- #include <pcl/segmentation/sac_segmentation.h>
- #include <pcl/visualization/cloud_viewer.h>
- void segmentate(pcl::PointCloud<pcl::PointXYZRGB>& cloud, double threshould) {
- pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
- pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
- // Create the segmentation object
- pcl::SACSegmentation<pcl::PointXYZRGB> seg;
- // Optional
- seg.setOptimizeCoefficients (true);
- // Mandatory
- seg.setModelType (pcl::SACMODEL_PLANE);
- seg.setMethodType (pcl::SAC_RANSAC);
- seg.setDistanceThreshold (threshould);
- seg.setInputCloud (cloud.makeShared ());
- seg.segment (*inliers, *coefficients);
- for (size_t i = 0; i < inliers->indices.size (); ++i) {
- cloud.points[inliers->indices[i]].r = 255;
- cloud.points[inliers->indices[i]].g = 0;
- cloud.points[inliers->indices[i]].b = 0;
- }
- }
- class SimpleOpenNIViewer
- {
- public:
- SimpleOpenNIViewer () : viewer ("PCL OpenNI Viewer") {}
- void cloud_cb_ (const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr &cloud)
- {
- if (!viewer.wasStopped()) {
- pcl::PointCloud<pcl::PointXYZRGB> segmented_cloud(*cloud);
- segmentate(segmented_cloud, 0.01);
- viewer.showCloud (segmented_cloud.makeShared());
- }
- }
- void run ()
- {
- pcl::Grabber* interface = new pcl::OpenNIGrabber();
- boost::function<void (const pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr&)> f =
- boost::bind (&SimpleOpenNIViewer::cloud_cb_, this, _1);
- interface->registerCallback (f);
- interface->start ();
- while (!viewer.wasStopped())
- {
- sleep (1);
- }
- interface->stop ();
- }
- pcl::visualization::CloudViewer viewer;
- };
- int main ()
- {
- SimpleOpenNIViewer v;
- v.run ();
- return 0;
- }
PointXYZRGBのRGBは私も悩みました。きちんとキャストしないと変な色になってしまいます..
返信削除