その前に、前回使った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は私も悩みました。きちんとキャストしないと変な色になってしまいます..
返信削除