c++point-cloud-librarypoint-cloudsransacpcl

Why does pcl::PointNormal RANSAC cylinder model state that it does not contain normals when estimating model coefficients?


I have been trying to fit a cylinder model to a generated point cloud using the pcl tools. I have been adapting the example code that is provided in the documentation here.

From my understanding, the pcl::SampleConsensusModelCylinder requires normals data so I have added a new check in the code that checks for a -cf argument. The code then calculates the normals of each point in a pcl::Normals-type point cloud called cloud_normals and then concatenates this with the original pcl::PointXYZ-type point cloud, cloud . I save this to a new pcl::PointNormal-type point cloud called cloud_normalpoints and with this I attempt to fit the the pcl::SampleConsensusModelCylinder using RAndom SAmple Consensus (RANSAC).

I have included the code below:

  else if (pcl::console::find_argument (argc, argv, "-cf") >= 0 )
  {
    //TODO: find fastest way to fit cylinder model to point cloud
    // the cloud_normals point cloud will be used to store the point cloud or normals
    pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);
    cloud_normals->width    = 500;
    cloud_normals->height   = 1;
    // is_dense is True if no points have NaN or Inf in any of their floating points field
    cloud_normals->is_dense = false;
    cloud_normals->points.resize (cloud_normals->width * cloud_normals->height);


    // the NormalEstimation object ne is created and will estimate the normals and curvature at each point
    pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
    // the search:KdTree object pointer points to a search method for finding points in 3D space (3D point clouds)
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
    // use the filtered cloud as an input to the normal estimator
    ne.setInputCloud (cloud);
    // set number of k-nearest neighbours to use for feature estimation to 50
    ne.setKSearch (50);
    // compute normals and save these to the clouds_normals point cloud
    ne.compute (*cloud_normals);

    pcl::PointCloud<pcl::PointNormal>::Ptr cloud_normalpoints (new pcl::PointCloud<pcl::PointNormal>);
    cloud_normalpoints->width    = 500;
    cloud_normalpoints->height   = 1;
    // is_dense is True if no points have NaN or Inf in any of their floating points field
    cloud_normalpoints->is_dense = false;
    cloud_normalpoints->points.resize (cloud_normalpoints->width * cloud_normalpoints->height);

    pcl::concatenateFields(*cloud,*cloud_normals,*cloud_normalpoints);

    //TODO: Solve normals not given error
    pcl::SampleConsensusModelCylinder<pcl::PointNormal, pcl::Normal>::Ptr
      model_c (new pcl::SampleConsensusModelCylinder<pcl::PointNormal, pcl::Normal> (cloud_normalpoints));
    // Declares ransac as a ransac implementation searching for a cylinder (according to model_c -> in cloud)
    pcl::RandomSampleConsensus<pcl::PointNormal> ransac (model_c);
    // Set distance threshold of .01 -> believe this is for inliers
    ransac.setDistanceThreshold (.01);
    // Compute model coefficients and find inliers
    ransac.computeModel();
    
    // Return indices of best set of inliers so far for this model
    ransac.getInliers(inliers);
  }

I also added some more code to generate an original point cloud containing a cylinder, but this works so I will not go into it here.

When I run my code it gets to the compute model stage and then throws the following error:

[pcl::SampleConsensusModelCylinder::computeModelCoefficients] No input dataset containing normals was given!

Does anyone know why this is? The cloud_normalpoints cloud includes the normal data that was found for each point. Should I be setting up the RANSAC estimator differently? Should I use a different point type? I am relatively new to pcl so any help would be greatly appreciated!


Solution

  • You have to call the function setInputNormals of your model_c, where you pass the cloud_normals. The cloud you pass in the constructor of SampleConsensusModelCylinder only sets the XYZ information, but is not used for normals.

    This tutorial could also be interesting for you: https://pcl.readthedocs.io/projects/tutorials/en/latest/cylinder_segmentation.html