Exemple #1
0
  void
  callback (const sensor_msgs::Image::ConstPtr &depth,
            const sensor_msgs::Image::ConstPtr &rgb,
            const sensor_msgs::CameraInfo::ConstPtr &info)
  {
    //typedef pcl_cuda::SampleConsensusModel<pcl_cuda::Host>::Indices Indices;

    //pcl_cuda::PointCloudAOS<pcl_cuda::Host>::Ptr data (new pcl_cuda::PointCloudAOS<pcl_cuda::Host>);
    PointCloudAOS<Device>::Ptr data;
    d2c.callback (depth, rgb, info, data);

    SampleConsensusModelPlane<Device>::Ptr sac_model (new SampleConsensusModelPlane<Device> (data));
//    SampleConsensusModelPlane<Host>::Ptr sac_model (new pcl_cuda::SampleConsensusModelPlane<pcl_cuda::Host> (data));
    RandomSampleConsensus<Device> sac (sac_model);
    sac.setDistanceThreshold (0.05);

    if (!sac.computeModel (2))
      std::cerr << "Failed to compute model" << std::endl;

    // Convert data from Thrust/HOST to PCL
    pcl::PointCloud<pcl::PointXYZRGB> output;
//    pcl_cuda::toPCL (*data, output);

    output.header.stamp = ros::Time::now ();
    pub.publish (output);
  }
TEST (ModelOutlierRemoval, Model_Outlier_Filter)
{
  PointCloud<PointXYZ>::Ptr cloud_filter_out (new PointCloud<PointXYZ>);
  std::vector<int> ransac_inliers;
  float thresh = 0.01;
  //run ransac
  Eigen::VectorXf model_coefficients;
  pcl::SampleConsensusModelPlane<pcl::PointXYZ>::Ptr model_p (new pcl::SampleConsensusModelPlane<pcl::PointXYZ> (cloud_in));
  RandomSampleConsensus < pcl::PointXYZ > ransac (model_p);
  ransac.setDistanceThreshold (thresh);
  ransac.computeModel ();
  ransac.getInliers (ransac_inliers);
  ransac.getModelCoefficients (model_coefficients);
  // test ransacs result
  EXPECT_EQ (model_coefficients.size (), 4);
  if (model_coefficients.size () != 4)
    return;
  //run filter
  pcl::ModelCoefficients model_coeff;
  model_coeff.values.resize (4);
  for (int i = 0; i < 4; i++)
    model_coeff.values[i] = model_coefficients[i];
  pcl::ModelOutlierRemoval < pcl::PointXYZ > filter;
  filter.setModelCoefficients (model_coeff);
  filter.setThreshold (thresh);
  filter.setModelType (pcl::SACMODEL_PLANE);
  filter.setInputCloud (cloud_in);
  filter.filter (*cloud_filter_out);
  //compare results
  EXPECT_EQ (cloud_filter_out->points.size (), ransac_inliers.size ());
  //TODO: also compare content
}
TEST (RANSAC, SampleConsensusModelCircle2D)
{
  srand (0);

  // Use a custom point cloud for these tests until we need something better
  PointCloud<PointXYZ> cloud;
  cloud.points.resize (18);

  cloud.points[0].x = 3.587751f;  cloud.points[0].y = -4.190982f;  cloud.points[0].z = 0.0f;
  cloud.points[1].x = 3.808883f;  cloud.points[1].y = -4.412265f;  cloud.points[1].z = 0.0f;
  cloud.points[2].x = 3.587525f;  cloud.points[2].y = -5.809143f;  cloud.points[2].z = 0.0f;
  cloud.points[3].x = 2.999913f;  cloud.points[3].y = -5.999980f;  cloud.points[3].z = 0.0f;
  cloud.points[4].x = 2.412224f;  cloud.points[4].y = -5.809090f;  cloud.points[4].z = 0.0f;
  cloud.points[5].x = 2.191080f;  cloud.points[5].y = -5.587682f;  cloud.points[5].z = 0.0f;
  cloud.points[6].x = 2.048941f;  cloud.points[6].y = -5.309003f;  cloud.points[6].z = 0.0f;
  cloud.points[7].x = 2.000397f;  cloud.points[7].y = -4.999944f;  cloud.points[7].z = 0.0f;
  cloud.points[8].x = 2.999953f;  cloud.points[8].y = -6.000056f;  cloud.points[8].z = 0.0f;
  cloud.points[9].x = 2.691127f;  cloud.points[9].y = -5.951136f;  cloud.points[9].z = 0.0f;
  cloud.points[10].x = 2.190892f; cloud.points[10].y = -5.587838f; cloud.points[10].z = 0.0f;
  cloud.points[11].x = 2.048874f; cloud.points[11].y = -5.309052f; cloud.points[11].z = 0.0f;
  cloud.points[12].x = 1.999990f; cloud.points[12].y = -5.000147f; cloud.points[12].z = 0.0f;
  cloud.points[13].x = 2.049026f; cloud.points[13].y = -4.690918f; cloud.points[13].z = 0.0f;
  cloud.points[14].x = 2.190956f; cloud.points[14].y = -4.412162f; cloud.points[14].z = 0.0f;
  cloud.points[15].x = 2.412231f; cloud.points[15].y = -4.190918f; cloud.points[15].z = 0.0f;
  cloud.points[16].x = 2.691027f; cloud.points[16].y = -4.049060f; cloud.points[16].z = 0.0f;
  cloud.points[17].x = 2.0f;      cloud.points[17].y = -3.0f;      cloud.points[17].z = 0.0f;

  // Create a shared 2d circle model pointer directly
  SampleConsensusModelCircle2DPtr model (new SampleConsensusModelCircle2D<PointXYZ> (cloud.makeShared ()));

  // Create the RANSAC object
  RandomSampleConsensus<PointXYZ> sac (model, 0.03);

  // Algorithm tests
  bool result = sac.computeModel ();
  ASSERT_EQ (result, true);

  std::vector<int> sample;
  sac.getModel (sample);
  EXPECT_EQ (int (sample.size ()), 3);

  std::vector<int> inliers;
  sac.getInliers (inliers);
  EXPECT_EQ (int (inliers.size ()), 17);

  Eigen::VectorXf coeff;
  sac.getModelCoefficients (coeff);
  EXPECT_EQ (int (coeff.size ()), 3);
  EXPECT_NEAR (coeff[0],  3, 1e-3);
  EXPECT_NEAR (coeff[1], -5, 1e-3);
  EXPECT_NEAR (coeff[2],  1, 1e-3);

  Eigen::VectorXf coeff_refined;
  model->optimizeModelCoefficients (inliers, coeff, coeff_refined);
  EXPECT_EQ (int (coeff_refined.size ()), 3);
  EXPECT_NEAR (coeff_refined[0],  3, 1e-3);
  EXPECT_NEAR (coeff_refined[1], -5, 1e-3);
  EXPECT_NEAR (coeff_refined[2],  1, 1e-3);
}
TEST (SampleConsensusModelParallelLine, RANSAC)
{
  PointCloud<PointXYZ> cloud (16, 1);

  // Line 1
  cloud.points[0].getVector3fMap () <<  1.0f,  2.00f,  3.00f;
  cloud.points[1].getVector3fMap () <<  4.0f,  5.00f,  6.00f;
  cloud.points[2].getVector3fMap () <<  7.0f,  8.00f,  9.00f;
  cloud.points[3].getVector3fMap () << 10.0f, 11.00f, 12.00f;
  cloud.points[4].getVector3fMap () << 13.0f, 14.00f, 15.00f;
  cloud.points[5].getVector3fMap () << 16.0f, 17.00f, 18.00f;
  cloud.points[6].getVector3fMap () << 19.0f, 20.00f, 21.00f;
  cloud.points[7].getVector3fMap () << 22.0f, 23.00f, 24.00f;
  // Random points
  cloud.points[8].getVector3fMap () << -5.0f,  1.57f,  0.75f;
  cloud.points[9].getVector3fMap () <<  4.0f,  2.00f,  3.00f;
  // Line 2 (parallel to the Z axis)
  cloud.points[10].getVector3fMap () << -1.00f,  5.00f,  0.0f;
  cloud.points[11].getVector3fMap () << -1.05f,  5.01f,  1.0f;
  cloud.points[12].getVector3fMap () << -1.01f,  5.05f,  2.0f;
  cloud.points[13].getVector3fMap () << -1.05f,  5.01f,  3.0f;
  cloud.points[14].getVector3fMap () << -1.01f,  5.05f,  4.0f;
  cloud.points[15].getVector3fMap () << -1.05f,  5.01f,  5.0f;

  // Create a shared line model pointer directly
  SampleConsensusModelParallelLinePtr model (new SampleConsensusModelParallelLine<PointXYZ> (cloud.makeShared ()));
  model->setAxis (Eigen::Vector3f (0, 0, 1));
  model->setEpsAngle (0.1);

  // Create the RANSAC object
  RandomSampleConsensus<PointXYZ> sac (model, 0.1);

  // Algorithm tests
  bool result = sac.computeModel ();
  ASSERT_TRUE (result);

  std::vector<int> sample;
  sac.getModel (sample);
  EXPECT_EQ (2, sample.size ());

  std::vector<int> inliers;
  sac.getInliers (inliers);
  EXPECT_EQ (6, inliers.size ());

  Eigen::VectorXf coeff;
  sac.getModelCoefficients (coeff);
  EXPECT_EQ (6, coeff.size ());
  EXPECT_NEAR (0, coeff[3], 1e-4);
  EXPECT_NEAR (0, coeff[4], 1e-4);
  EXPECT_NEAR (1, coeff[5], 1e-4);

  // Projection tests
  PointCloud<PointXYZ> proj_points;
  model->projectPoints (inliers, coeff, proj_points);

  EXPECT_XYZ_NEAR (PointXYZ (-1.05, 5.05, 3.0), proj_points.points[13], 0.1);
  EXPECT_XYZ_NEAR (PointXYZ (-1.05, 5.05, 4.0), proj_points.points[14], 0.1);
}
TEST (SampleConsensusModelLine, RANSAC)
{
  // Use a custom point cloud for these tests until we need something better
  PointCloud<PointXYZ> cloud;
  cloud.points.resize (10);

  cloud.points[0].getVector3fMap () <<  1.0f,  2.00f,  3.00f;
  cloud.points[1].getVector3fMap () <<  4.0f,  5.00f,  6.00f;
  cloud.points[2].getVector3fMap () <<  7.0f,  8.00f,  9.00f;
  cloud.points[3].getVector3fMap () << 10.0f, 11.00f, 12.00f;
  cloud.points[4].getVector3fMap () << 13.0f, 14.00f, 15.00f;
  cloud.points[5].getVector3fMap () << 16.0f, 17.00f, 18.00f;
  cloud.points[6].getVector3fMap () << 19.0f, 20.00f, 21.00f;
  cloud.points[7].getVector3fMap () << 22.0f, 23.00f, 24.00f;
  cloud.points[8].getVector3fMap () << -5.0f,  1.57f,  0.75f;
  cloud.points[9].getVector3fMap () <<  4.0f,  2.00f,  3.00f;

  // Create a shared line model pointer directly
  SampleConsensusModelLinePtr model (new SampleConsensusModelLine<PointXYZ> (cloud.makeShared ()));

  // Create the RANSAC object
  RandomSampleConsensus<PointXYZ> sac (model, 0.001);

  // Algorithm tests
  bool result = sac.computeModel ();
  ASSERT_TRUE (result);

  std::vector<int> sample;
  sac.getModel (sample);
  EXPECT_EQ (2, sample.size ());

  std::vector<int> inliers;
  sac.getInliers (inliers);
  EXPECT_EQ (8, inliers.size ());

  Eigen::VectorXf coeff;
  sac.getModelCoefficients (coeff);
  EXPECT_EQ (6, coeff.size ());
  EXPECT_NEAR (1, coeff[4] / coeff[3], 1e-4);
  EXPECT_NEAR (1, coeff[5] / coeff[3], 1e-4);

  Eigen::VectorXf coeff_refined;
  model->optimizeModelCoefficients (inliers, coeff, coeff_refined);
  EXPECT_EQ (6, coeff_refined.size ());
  EXPECT_NEAR (1, coeff[4] / coeff[3], 1e-4);
  EXPECT_NEAR (1, coeff[5] / coeff[3], 1e-4);

  // Projection tests
  PointCloud<PointXYZ> proj_points;
  model->projectPoints (inliers, coeff_refined, proj_points);

  EXPECT_XYZ_NEAR (PointXYZ ( 7.0,  8.0,  9.0), proj_points.points[2], 1e-4);
  EXPECT_XYZ_NEAR (PointXYZ (10.0, 11.0, 12.0), proj_points.points[3], 1e-4);
  EXPECT_XYZ_NEAR (PointXYZ (16.0, 17.0, 18.0), proj_points.points[5], 1e-4);
}
int main(int argc, char** argv)
{
	PointCloud<PointXYZ>::Ptr cloud (new PointCloud<PointXYZ>);
	PointCloud<PointXYZ>::Ptr final (new PointCloud<PointXYZ>);

	cloud->width = 500;
	cloud->height = 1;
	cloud->is_dense = false;
	cloud->points.resize(cloud->width * cloud->height);
	for (size_t i=0; i<cloud->points.size(); ++i)
	{
		cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.00);
		cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.00);
		if (console::find_argument(argc, argv, "-s") >= 0 || console::find_argument(argc, argv, "-sf") >= 0)
		{
			if (i % 5 == 0)
				cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.00);
			else if (i % 2 == 0)
				cloud->points[i].z = sqrt(1 - (cloud->points[i].x * cloud->points[i].x)
										   - (cloud->points[i].y * cloud->points[i].y));
			else
				cloud->points[i].z = -sqrt(1 - (cloud->points[i].x * cloud->points[i].x)
										   - (cloud->points[i].y * cloud->points[i].y));
		}
		else
		{
			if (i % 2 == 0)
				cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.00);
			else
				cloud->points[i].z = -1 * (cloud->points[i].x + cloud->points[i].y);
		}
	}

	vector<int> inliers;

	SampleConsensusModelSphere<PointXYZ>::Ptr 
		model_s(new SampleConsensusModelSphere<PointXYZ>(cloud));
	SampleConsensusModelPlane<PointXYZ>::Ptr
		model_p(new SampleConsensusModelPlane<PointXYZ>(cloud));
	if (console::find_argument(argc, argv, "-f") >= 0)
	{
		RandomSampleConsensus<PointXYZ> ransac (model_p);
		ransac.setDistanceThreshold(0.01);
		ransac.computeModel();
		ransac.getInliers(inliers);
	}
	else if (console::find_argument(argc, argv, "-sf") >= 0)
	{
		RandomSampleConsensus<PointXYZ> ransac (model_s);
		ransac.setDistanceThreshold(0.01);
		ransac.computeModel();
		ransac.getInliers(inliers);
	}

	copyPointCloud<PointXYZ>(*cloud, inliers, *final);

	boost::shared_ptr<visualization::PCLVisualizer> viewer;
	if (console::find_argument(argc, argv, "-f") >= 0 || console::find_argument(argc, argv, "-sf") >= 0)
		viewer = simpleVis(final);
	else
TEST (RANSAC, SampleConsensusModelSphere)
{
  srand (0);

  // Use a custom point cloud for these tests until we need something better
  PointCloud<PointXYZ> cloud;
  cloud.points.resize (10);
  cloud.points[0].x = 1.7068f; cloud.points[0].y = 1.0684f; cloud.points[0].z = 2.2147f;
  cloud.points[1].x = 2.4708f; cloud.points[1].y = 2.3081f; cloud.points[1].z = 1.1736f;
  cloud.points[2].x = 2.7609f; cloud.points[2].y = 1.9095f; cloud.points[2].z = 1.3574f;
  cloud.points[3].x = 2.8016f; cloud.points[3].y = 1.6704f; cloud.points[3].z = 1.5009f;
  cloud.points[4].x = 1.8517f; cloud.points[4].y = 2.0276f; cloud.points[4].z = 1.0112f;
  cloud.points[5].x = 1.8726f; cloud.points[5].y = 1.3539f; cloud.points[5].z = 2.7523f;
  cloud.points[6].x = 2.5179f; cloud.points[6].y = 2.3218f; cloud.points[6].z = 1.2074f;
  cloud.points[7].x = 2.4026f; cloud.points[7].y = 2.5114f; cloud.points[7].z = 2.7588f;
  cloud.points[8].x = 2.6999f; cloud.points[8].y = 2.5606f; cloud.points[8].z = 1.5571f;
  cloud.points[9].x = 0.0f;    cloud.points[9].y = 0.0f;    cloud.points[9].z = 0.0f;

  // Create a shared sphere model pointer directly
  SampleConsensusModelSpherePtr model (new SampleConsensusModelSphere<PointXYZ> (cloud.makeShared ()));

  // Create the RANSAC object
  RandomSampleConsensus<PointXYZ> sac (model, 0.03);

  // Algorithm tests
  bool result = sac.computeModel ();
  ASSERT_EQ (result, true);

  std::vector<int> sample;
  sac.getModel (sample);
  EXPECT_EQ (int (sample.size ()), 4);

  std::vector<int> inliers;
  sac.getInliers (inliers);
  EXPECT_EQ (int (inliers.size ()), 9);

  Eigen::VectorXf coeff;
  sac.getModelCoefficients (coeff);
  EXPECT_EQ (int (coeff.size ()), 4);
  EXPECT_NEAR (coeff[0]/coeff[3], 2,  1e-2);
  EXPECT_NEAR (coeff[1]/coeff[3], 2,  1e-2);
  EXPECT_NEAR (coeff[2]/coeff[3], 2,  1e-2);

  Eigen::VectorXf coeff_refined;
  model->optimizeModelCoefficients (inliers, coeff, coeff_refined);
  EXPECT_EQ (int (coeff_refined.size ()), 4);
  EXPECT_NEAR (coeff_refined[0]/coeff_refined[3], 2,  1e-2);
  EXPECT_NEAR (coeff_refined[1]/coeff_refined[3], 2,  1e-2);
  EXPECT_NEAR (coeff_refined[2]/coeff_refined[3], 2,  1e-2);
}
TEST (SampleConsensusModelLine, OnGroundPlane)
{
  PointCloud<PointXYZ> cloud;
  cloud.points.resize (10);

  // All the points are on the ground plane (z=0).
  // The line is parallel to the x axis, so all the inlier points have the same z and y coordinates.
  cloud.points[0].getVector3fMap () <<  0.0f,  0.0f,  0.0f;
  cloud.points[1].getVector3fMap () <<  1.0f,  0.0f,  0.0f;
  cloud.points[2].getVector3fMap () <<  2.0f,  0.0f,  0.0f;
  cloud.points[3].getVector3fMap () <<  3.0f,  0.0f,  0.0f;
  cloud.points[4].getVector3fMap () <<  4.0f,  0.0f,  0.0f;
  cloud.points[5].getVector3fMap () <<  5.0f,  0.0f,  0.0f;
  // Outliers
  cloud.points[6].getVector3fMap () <<  2.1f,  2.0f,  0.0f;
  cloud.points[7].getVector3fMap () <<  5.0f,  4.1f,  0.0f;
  cloud.points[8].getVector3fMap () <<  0.4f,  1.3f,  0.0f;
  cloud.points[9].getVector3fMap () <<  3.3f,  0.1f,  0.0f;

  // Create a shared line model pointer directly
  SampleConsensusModelLinePtr model (new SampleConsensusModelLine<PointXYZ> (cloud.makeShared ()));

  // Create the RANSAC object
  RandomSampleConsensus<PointXYZ> sac (model, 0.001);

  // Algorithm tests
  bool result = sac.computeModel ();
  ASSERT_TRUE (result);

  std::vector<int> inliers;
  sac.getInliers (inliers);
  EXPECT_EQ (6, inliers.size ());

  Eigen::VectorXf coeff;
  sac.getModelCoefficients (coeff);
  EXPECT_EQ (6, coeff.size ());

  EXPECT_NE (0, coeff[3]);
  EXPECT_NEAR (0, coeff[4], 1e-4);
  EXPECT_NEAR (0, coeff[5], 1e-4);
}
TEST (RANSAC, Base)
{
  // Create a shared plane model pointer directly
  SampleConsensusModelPlanePtr model (new SampleConsensusModelPlane<PointXYZ> (cloud_));

  // Create the RANSAC object
  RandomSampleConsensus<PointXYZ> sac (model, 0.03);

  // Basic tests
  ASSERT_EQ (sac.getDistanceThreshold (), 0.03);
  sac.setDistanceThreshold (0.03);
  ASSERT_EQ (sac.getDistanceThreshold (), 0.03);

  sac.setProbability (0.99);
  ASSERT_EQ (sac.getProbability (), 0.99);

  sac.setMaxIterations (10000);
  ASSERT_EQ (sac.getMaxIterations (), 10000);
}
TEST (RANSAC, SampleConsensusModelLine)
{
  srand (0);

  // Use a custom point cloud for these tests until we need something better
  PointCloud<PointXYZ> cloud;
  cloud.points.resize (10);

  cloud.points[0].x = 1.0f;  cloud.points[0].y = 2.0f;  cloud.points[0].z = 3.0f;
  cloud.points[1].x = 4.0f;  cloud.points[1].y = 5.0f;  cloud.points[1].z = 6.0f;
  cloud.points[2].x = 7.0f;  cloud.points[2].y = 8.0f;  cloud.points[2].z = 9.0f;
  cloud.points[3].x = 10.0f; cloud.points[3].y = 11.0f; cloud.points[3].z = 12.0f;
  cloud.points[4].x = 13.0f; cloud.points[4].y = 14.0f; cloud.points[4].z = 15.0f;
  cloud.points[5].x = 16.0f; cloud.points[5].y = 17.0f; cloud.points[5].z = 18.0f;
  cloud.points[6].x = 19.0f; cloud.points[6].y = 20.0f; cloud.points[6].z = 21.0f;
  cloud.points[7].x = 22.0f; cloud.points[7].y = 23.0f; cloud.points[7].z = 24.0f;
  cloud.points[8].x = -5.0f; cloud.points[8].y = 1.57f; cloud.points[8].z = 0.75f;
  cloud.points[9].x = 4.0f;  cloud.points[9].y = 2.0f;  cloud.points[9].z = 3.0f;

  // Create a shared line model pointer directly
  SampleConsensusModelLinePtr model (new SampleConsensusModelLine<PointXYZ> (cloud.makeShared ()));

  // Create the RANSAC object
  RandomSampleConsensus<PointXYZ> sac (model, 0.001);

  // Algorithm tests
  bool result = sac.computeModel ();
  ASSERT_EQ (result, true);

  std::vector<int> sample;
  sac.getModel (sample);
  EXPECT_EQ (int (sample.size ()), 2);

  std::vector<int> inliers;
  sac.getInliers (inliers);
  EXPECT_EQ (int (inliers.size ()), 8);

  Eigen::VectorXf coeff;
  sac.getModelCoefficients (coeff);
  EXPECT_EQ (int (coeff.size ()), 6);
  EXPECT_NEAR (coeff[4]/coeff[3], 1, 1e-4);
  EXPECT_NEAR (coeff[5]/coeff[3], 1, 1e-4);

  Eigen::VectorXf coeff_refined;
  model->optimizeModelCoefficients (inliers, coeff, coeff_refined);
  EXPECT_EQ (int (coeff_refined.size ()), 6);
  EXPECT_NEAR (coeff[4]/coeff[3], 1, 1e-4);
  EXPECT_NEAR (coeff[5]/coeff[3], 1, 1e-4);

  // Projection tests
  PointCloud<PointXYZ> proj_points;
  model->projectPoints (inliers, coeff_refined, proj_points);

  EXPECT_NEAR (proj_points.points[2].x, 7.0, 1e-4);
  EXPECT_NEAR (proj_points.points[2].y, 8.0, 1e-4);
  EXPECT_NEAR (proj_points.points[2].z, 9.0, 1e-4);

  EXPECT_NEAR (proj_points.points[3].x, 10.0, 1e-4);
  EXPECT_NEAR (proj_points.points[3].y, 11.0, 1e-4);
  EXPECT_NEAR (proj_points.points[3].z, 12.0, 1e-4);

  EXPECT_NEAR (proj_points.points[5].x, 16.0, 1e-4);
  EXPECT_NEAR (proj_points.points[5].y, 17.0, 1e-4);
  EXPECT_NEAR (proj_points.points[5].z, 18.0, 1e-4);
}
TEST (RANSAC, SampleConsensusModelCircle3D)
{
  srand (0);

  // Use a custom point cloud for these tests until we need something better
  PointCloud<PointXYZ> cloud;
  cloud.points.resize (20);

  cloud.points[0].x = 1.0f;  	    cloud.points[0].y = 5.0f;        cloud.points[0].z = -2.9000001f;
  cloud.points[1].x = 1.034202f;    cloud.points[1].y = 5.0f;        cloud.points[1].z = -2.9060307f;
  cloud.points[2].x = 1.0642787f;   cloud.points[2].y = 5.0f;        cloud.points[2].z = -2.9233956f;
  cloud.points[3].x = 1.0866026f;   cloud.points[3].y = 5.0f;  	     cloud.points[3].z = -2.95f;
  cloud.points[4].x = 1.0984808f;   cloud.points[4].y = 5.0f;  	     cloud.points[4].z = -2.9826353f;
  cloud.points[5].x = 1.0984808f;   cloud.points[5].y = 5.0f;        cloud.points[5].z = -3.0173647f;
  cloud.points[6].x = 1.0866026f;   cloud.points[6].y = 5.0f;  	     cloud.points[6].z = -3.05f;
  cloud.points[7].x = 1.0642787f;   cloud.points[7].y = 5.0f;  	     cloud.points[7].z = -3.0766044f;
  cloud.points[8].x = 1.034202f;    cloud.points[8].y = 5.0f;  	     cloud.points[8].z = -3.0939693f;
  cloud.points[9].x = 1.0f;         cloud.points[9].y = 5.0f;  	     cloud.points[9].z = -3.0999999f;
  cloud.points[10].x = 0.96579796f; cloud.points[10].y = 5.0f; 	     cloud.points[10].z = -3.0939693f;
  cloud.points[11].x = 0.93572122f; cloud.points[11].y = 5.0f; 	     cloud.points[11].z = -3.0766044f;
  cloud.points[12].x = 0.91339743f; cloud.points[12].y = 5.0f; 	     cloud.points[12].z = -3.05f;
  cloud.points[13].x = 0.90151924f; cloud.points[13].y = 5.0f; 	     cloud.points[13].z = -3.0173647f;
  cloud.points[14].x = 0.90151924f; cloud.points[14].y = 5.0f; 	     cloud.points[14].z = -2.9826353f;
  cloud.points[15].x = 0.91339743f; cloud.points[15].y = 5.0f; 	     cloud.points[15].z = -2.95f;
  cloud.points[16].x = 0.93572122f; cloud.points[16].y = 5.0f; 	     cloud.points[16].z = -2.9233956f;
  cloud.points[17].x = 0.96579796f; cloud.points[17].y = 5.0;        cloud.points[17].z = -2.9060307f;
  cloud.points[18].x = 0.85000002f; cloud.points[18].y = 4.8499999f; cloud.points[18].z = -3.1500001f;
  cloud.points[19].x = 1.15f; 	    cloud.points[19].y = 5.1500001f; cloud.points[19].z = -2.8499999f;

  // Create a shared 3d circle model pointer directly
  SampleConsensusModelCircle3DPtr model (new SampleConsensusModelCircle3D<PointXYZ> (cloud.makeShared ()));

  // Create the RANSAC object
  RandomSampleConsensus<PointXYZ> sac (model, 0.03);

  // Algorithm tests
  bool result = sac.computeModel ();
  ASSERT_EQ (result, true);

  std::vector<int> sample;
  sac.getModel (sample);
  EXPECT_EQ (int (sample.size ()), 3);

  std::vector<int> inliers;
  sac.getInliers (inliers);
  EXPECT_EQ (int (inliers.size ()), 18);

  Eigen::VectorXf coeff;
  sac.getModelCoefficients (coeff);
  EXPECT_EQ (int (coeff.size ()), 7);
  EXPECT_NEAR (coeff[0],  1, 1e-3);
  EXPECT_NEAR (coeff[1],  5, 1e-3);
  EXPECT_NEAR (coeff[2], -3, 1e-3);
  EXPECT_NEAR (coeff[3],0.1, 1e-3);
  EXPECT_NEAR (coeff[4],  0, 1e-3);
  EXPECT_NEAR (coeff[5], -1, 1e-3);
  EXPECT_NEAR (coeff[6],  0, 1e-3);

  Eigen::VectorXf coeff_refined;
  model->optimizeModelCoefficients (inliers, coeff, coeff_refined);
  EXPECT_EQ (int (coeff_refined.size ()), 7);
  EXPECT_NEAR (coeff_refined[0],  1, 1e-3);
  EXPECT_NEAR (coeff_refined[1],  5, 1e-3);
  EXPECT_NEAR (coeff_refined[2], -3, 1e-3);
  EXPECT_NEAR (coeff_refined[3],0.1, 1e-3);
  EXPECT_NEAR (coeff_refined[4],  0, 1e-3);
  EXPECT_NEAR (coeff_refined[5], -1, 1e-3);
  EXPECT_NEAR (coeff_refined[6],  0, 1e-3);
}
TEST (RANSAC, SampleConsensusModelCylinder)
{
  srand (0);

  // Use a custom point cloud for these tests until we need something better
  PointCloud<PointXYZ> cloud;
  PointCloud<Normal> normals;
  cloud.points.resize (20); normals.points.resize (20);

  cloud.points[0].x =  -0.499902f; cloud.points[0].y =  2.199701f; cloud.points[0].z =  0.000008f;
  cloud.points[1].x =  -0.875397f; cloud.points[1].y =  2.030177f; cloud.points[1].z =  0.050104f;
  cloud.points[2].x =  -0.995875f; cloud.points[2].y =  1.635973f; cloud.points[2].z =  0.099846f;
  cloud.points[3].x =  -0.779523f; cloud.points[3].y =  1.285527f; cloud.points[3].z =  0.149961f;
  cloud.points[4].x =  -0.373285f; cloud.points[4].y =  1.216488f; cloud.points[4].z =  0.199959f;
  cloud.points[5].x =  -0.052893f; cloud.points[5].y =  1.475973f; cloud.points[5].z =  0.250101f;
  cloud.points[6].x =  -0.036558f; cloud.points[6].y =  1.887591f; cloud.points[6].z =  0.299839f;
  cloud.points[7].x =  -0.335048f; cloud.points[7].y =  2.171994f; cloud.points[7].z =  0.350001f;
  cloud.points[8].x =  -0.745456f; cloud.points[8].y =  2.135528f; cloud.points[8].z =  0.400072f;
  cloud.points[9].x =  -0.989282f; cloud.points[9].y =  1.803311f; cloud.points[9].z =  0.449983f;
  cloud.points[10].x = -0.900651f; cloud.points[10].y = 1.400701f; cloud.points[10].z = 0.500126f;
  cloud.points[11].x = -0.539658f; cloud.points[11].y = 1.201468f; cloud.points[11].z = 0.550079f;
  cloud.points[12].x = -0.151875f; cloud.points[12].y = 1.340951f; cloud.points[12].z = 0.599983f;
  cloud.points[13].x = -0.000724f; cloud.points[13].y = 1.724373f; cloud.points[13].z = 0.649882f;
  cloud.points[14].x = -0.188573f; cloud.points[14].y = 2.090983f; cloud.points[14].z = 0.699854f;
  cloud.points[15].x = -0.587925f; cloud.points[15].y = 2.192257f; cloud.points[15].z = 0.749956f;
  cloud.points[16].x = -0.927724f; cloud.points[16].y = 1.958846f; cloud.points[16].z = 0.800008f;
  cloud.points[17].x = -0.976888f; cloud.points[17].y = 1.549655f; cloud.points[17].z = 0.849970f;
  cloud.points[18].x = -0.702003f; cloud.points[18].y = 1.242707f; cloud.points[18].z = 0.899954f;
  cloud.points[19].x = -0.289916f; cloud.points[19].y = 1.246296f; cloud.points[19].z = 0.950075f;

  normals.points[0].normal[0] =   0.000098f; normals.points[0].normal[1] =   1.000098f; normals.points[0].normal[2] =   0.000008f;
  normals.points[1].normal[0] =  -0.750891f; normals.points[1].normal[1] =   0.660413f; normals.points[1].normal[2] =   0.000104f;
  normals.points[2].normal[0] =  -0.991765f; normals.points[2].normal[1] =  -0.127949f; normals.points[2].normal[2] =  -0.000154f;
  normals.points[3].normal[0] =  -0.558918f; normals.points[3].normal[1] =  -0.829439f; normals.points[3].normal[2] =  -0.000039f;
  normals.points[4].normal[0] =   0.253627f; normals.points[4].normal[1] =  -0.967447f; normals.points[4].normal[2] =  -0.000041f;
  normals.points[5].normal[0] =   0.894105f; normals.points[5].normal[1] =  -0.447965f; normals.points[5].normal[2] =   0.000101f;
  normals.points[6].normal[0] =   0.926852f; normals.points[6].normal[1] =   0.375543f; normals.points[6].normal[2] =  -0.000161f;
  normals.points[7].normal[0] =   0.329948f; normals.points[7].normal[1] =   0.943941f; normals.points[7].normal[2] =   0.000001f;
  normals.points[8].normal[0] =  -0.490966f; normals.points[8].normal[1] =   0.871203f; normals.points[8].normal[2] =   0.000072f;
  normals.points[9].normal[0] =  -0.978507f; normals.points[9].normal[1] =   0.206425f; normals.points[9].normal[2] =  -0.000017f;
  normals.points[10].normal[0] = -0.801227f; normals.points[10].normal[1] = -0.598534f; normals.points[10].normal[2] =  0.000126f;
  normals.points[11].normal[0] = -0.079447f; normals.points[11].normal[1] = -0.996697f; normals.points[11].normal[2] =  0.000079f;
  normals.points[12].normal[0] =  0.696154f; normals.points[12].normal[1] = -0.717889f; normals.points[12].normal[2] = -0.000017f;
  normals.points[13].normal[0] =  0.998685f; normals.points[13].normal[1] =  0.048502f; normals.points[13].normal[2] = -0.000118f;
  normals.points[14].normal[0] =  0.622933f; normals.points[14].normal[1] =  0.782133f; normals.points[14].normal[2] = -0.000146f;
  normals.points[15].normal[0] = -0.175948f; normals.points[15].normal[1] =  0.984480f; normals.points[15].normal[2] = -0.000044f;
  normals.points[16].normal[0] = -0.855476f; normals.points[16].normal[1] =  0.517824f; normals.points[16].normal[2] =  0.000008f;
  normals.points[17].normal[0] = -0.953769f; normals.points[17].normal[1] = -0.300571f; normals.points[17].normal[2] = -0.000030f;
  normals.points[18].normal[0] = -0.404035f; normals.points[18].normal[1] = -0.914700f; normals.points[18].normal[2] = -0.000046f;
  normals.points[19].normal[0] =  0.420154f; normals.points[19].normal[1] = -0.907445f; normals.points[19].normal[2] =  0.000075f;

  // Create a shared cylinder model pointer directly
  SampleConsensusModelCylinderPtr model (new SampleConsensusModelCylinder<PointXYZ, Normal> (cloud.makeShared ()));
  model->setInputNormals (normals.makeShared ());

  // Create the RANSAC object
  RandomSampleConsensus<PointXYZ> sac (model, 0.03);

  // Algorithm tests
  bool result = sac.computeModel ();
  ASSERT_EQ (result, true);

  std::vector<int> sample;
  sac.getModel (sample);
  EXPECT_EQ (int (sample.size ()), 2);

  std::vector<int> inliers;
  sac.getInliers (inliers);
  EXPECT_EQ (int (inliers.size ()), 20);

  Eigen::VectorXf coeff;
  sac.getModelCoefficients (coeff);
  EXPECT_EQ (int (coeff.size ()), 7);
  EXPECT_NEAR (coeff[0], -0.5, 1e-3);
  EXPECT_NEAR (coeff[1],  1.7,  1e-3);
  EXPECT_NEAR (coeff[6],  0.5, 1e-3);

  Eigen::VectorXf coeff_refined;
  model->optimizeModelCoefficients (inliers, coeff, coeff_refined);
  EXPECT_EQ (int (coeff_refined.size ()), 7);
  EXPECT_NEAR (coeff_refined[6], 0.5, 1e-3);
}
TEST (RANSAC, SampleConsensusModelCone)
{
  srand (0);

  // Use a custom point cloud for these tests until we need something better
  PointCloud<PointXYZ> cloud;
  PointCloud<Normal> normals;
  cloud.points.resize (31); normals.points.resize (31);

  cloud.points[0].x = -0.011247f; cloud.points[0].y = 0.200000f; cloud.points[0].z = 0.965384f; 
  cloud.points[1].x =  0.000000f; cloud.points[1].y = 0.200000f; cloud.points[1].z = 0.963603f; 
  cloud.points[2].x =  0.011247f; cloud.points[2].y = 0.200000f; cloud.points[2].z = 0.965384f; 
  cloud.points[3].x = -0.016045f; cloud.points[3].y = 0.175000f; cloud.points[3].z = 0.977916f; 
  cloud.points[4].x = -0.008435f; cloud.points[4].y = 0.175000f; cloud.points[4].z = 0.974038f; 
  cloud.points[5].x =  0.004218f; cloud.points[5].y = 0.175000f; cloud.points[5].z = 0.973370f; 
  cloud.points[6].x =  0.016045f; cloud.points[6].y = 0.175000f; cloud.points[6].z = 0.977916f; 
  cloud.points[7].x = -0.025420f; cloud.points[7].y = 0.200000f; cloud.points[7].z = 0.974580f; 
  cloud.points[8].x =  0.025420f; cloud.points[8].y = 0.200000f; cloud.points[8].z = 0.974580f; 
  cloud.points[9].x = -0.012710f; cloud.points[9].y = 0.150000f; cloud.points[9].z = 0.987290f; 
  cloud.points[10].x = -0.005624f; cloud.points[10].y = 0.150000f; cloud.points[10].z = 0.982692f; 
  cloud.points[11].x =  0.002812f; cloud.points[11].y = 0.150000f; cloud.points[11].z = 0.982247f; 
  cloud.points[12].x =  0.012710f; cloud.points[12].y = 0.150000f; cloud.points[12].z = 0.987290f; 
  cloud.points[13].x = -0.022084f; cloud.points[13].y = 0.175000f; cloud.points[13].z = 0.983955f; 
  cloud.points[14].x =  0.022084f; cloud.points[14].y = 0.175000f; cloud.points[14].z = 0.983955f; 
  cloud.points[15].x = -0.034616f; cloud.points[15].y = 0.200000f; cloud.points[15].z = 0.988753f; 
  cloud.points[16].x =  0.034616f; cloud.points[16].y = 0.200000f; cloud.points[16].z = 0.988753f; 
  cloud.points[17].x = -0.006044f; cloud.points[17].y = 0.125000f; cloud.points[17].z = 0.993956f; 
  cloud.points[18].x =  0.004835f; cloud.points[18].y = 0.125000f; cloud.points[18].z = 0.993345f; 
  cloud.points[19].x = -0.017308f; cloud.points[19].y = 0.150000f; cloud.points[19].z = 0.994376f; 
  cloud.points[20].x =  0.017308f; cloud.points[20].y = 0.150000f; cloud.points[20].z = 0.994376f; 
  cloud.points[21].x = -0.025962f; cloud.points[21].y = 0.175000f; cloud.points[21].z = 0.991565f; 
  cloud.points[22].x =  0.025962f; cloud.points[22].y = 0.175000f; cloud.points[22].z = 0.991565f; 
  cloud.points[23].x = -0.009099f; cloud.points[23].y = 0.125000f; cloud.points[23].z = 1.000000f; 
  cloud.points[24].x =  0.009099f; cloud.points[24].y = 0.125000f; cloud.points[24].z = 1.000000f; 
  cloud.points[25].x = -0.018199f; cloud.points[25].y = 0.150000f; cloud.points[25].z = 1.000000f; 
  cloud.points[26].x =  0.018199f; cloud.points[26].y = 0.150000f; cloud.points[26].z = 1.000000f; 
  cloud.points[27].x = -0.027298f; cloud.points[27].y = 0.175000f; cloud.points[27].z = 1.000000f; 
  cloud.points[28].x =  0.027298f; cloud.points[28].y = 0.175000f; cloud.points[28].z = 1.000000f; 
  cloud.points[29].x = -0.036397f; cloud.points[29].y = 0.200000f; cloud.points[29].z = 1.000000f; 
  cloud.points[30].x =  0.036397f; cloud.points[30].y = 0.200000f; cloud.points[30].z = 1.000000f; 

  normals.points[0].normal[0] = -0.290381f; normals.points[0].normal[1] =  -0.342020f; normals.points[0].normal[2] =  -0.893701f; 
  normals.points[1].normal[0] =  0.000000f; normals.points[1].normal[1] =  -0.342020f; normals.points[1].normal[2] =  -0.939693f; 
  normals.points[2].normal[0] =  0.290381f; normals.points[2].normal[1] =  -0.342020f; normals.points[2].normal[2] =  -0.893701f; 
  normals.points[3].normal[0] = -0.552338f; normals.points[3].normal[1] =  -0.342020f; normals.points[3].normal[2] =  -0.760227f; 
  normals.points[4].normal[0] = -0.290381f; normals.points[4].normal[1] =  -0.342020f; normals.points[4].normal[2] =  -0.893701f; 
  normals.points[5].normal[0] =  0.145191f; normals.points[5].normal[1] =  -0.342020f; normals.points[5].normal[2] =  -0.916697f; 
  normals.points[6].normal[0] =  0.552337f; normals.points[6].normal[1] =  -0.342020f; normals.points[6].normal[2] =  -0.760227f; 
  normals.points[7].normal[0] = -0.656282f; normals.points[7].normal[1] =  -0.342020f; normals.points[7].normal[2] =  -0.656283f; 
  normals.points[8].normal[0] =  0.656282f; normals.points[8].normal[1] =  -0.342020f; normals.points[8].normal[2] =  -0.656283f; 
  normals.points[9].normal[0] = -0.656283f; normals.points[9].normal[1] =  -0.342020f; normals.points[9].normal[2] =  -0.656282f; 
  normals.points[10].normal[0] = -0.290381f; normals.points[10].normal[1] =  -0.342020f; normals.points[10].normal[2] =  -0.893701f; 
  normals.points[11].normal[0] =  0.145191f; normals.points[11].normal[1] =  -0.342020f; normals.points[11].normal[2] =  -0.916697f; 
  normals.points[12].normal[0] =  0.656282f; normals.points[12].normal[1] =  -0.342020f; normals.points[12].normal[2] =  -0.656282f; 
  normals.points[13].normal[0] = -0.760228f; normals.points[13].normal[1] =  -0.342020f; normals.points[13].normal[2] =  -0.552337f; 
  normals.points[14].normal[0] =  0.760228f; normals.points[14].normal[1] =  -0.342020f; normals.points[14].normal[2] =  -0.552337f; 
  normals.points[15].normal[0] = -0.893701f; normals.points[15].normal[1] =  -0.342020f; normals.points[15].normal[2] =  -0.290380f; 
  normals.points[16].normal[0] =  0.893701f; normals.points[16].normal[1] =  -0.342020f; normals.points[16].normal[2] =  -0.290380f; 
  normals.points[17].normal[0] = -0.624162f; normals.points[17].normal[1] =  -0.342020f; normals.points[17].normal[2] =  -0.624162f; 
  normals.points[18].normal[0] =  0.499329f; normals.points[18].normal[1] =  -0.342020f; normals.points[18].normal[2] =  -0.687268f; 
  normals.points[19].normal[0] = -0.893701f; normals.points[19].normal[1] =  -0.342020f; normals.points[19].normal[2] =  -0.290380f; 
  normals.points[20].normal[0] =  0.893701f; normals.points[20].normal[1] =  -0.342020f; normals.points[20].normal[2] =  -0.290380f; 
  normals.points[21].normal[0] = -0.893701f; normals.points[21].normal[1] =  -0.342020f; normals.points[21].normal[2] =  -0.290381f; 
  normals.points[22].normal[0] =  0.893701f; normals.points[22].normal[1] =  -0.342020f; normals.points[22].normal[2] =  -0.290381f; 
  normals.points[23].normal[0] = -0.939693f; normals.points[23].normal[1] =  -0.342020f; normals.points[23].normal[2] =  0.000000f; 
  normals.points[24].normal[0] =  0.939693f; normals.points[24].normal[1] =  -0.342020f; normals.points[24].normal[2] =  0.000000f; 
  normals.points[25].normal[0] = -0.939693f; normals.points[25].normal[1] =  -0.342020f; normals.points[25].normal[2] =  0.000000f; 
  normals.points[26].normal[0] =  0.939693f; normals.points[26].normal[1] =  -0.342020f; normals.points[26].normal[2] =  0.000000f; 
  normals.points[27].normal[0] = -0.939693f; normals.points[27].normal[1] =  -0.342020f; normals.points[27].normal[2] =  0.000000f; 
  normals.points[28].normal[0] =  0.939693f; normals.points[28].normal[1] =  -0.342020f; normals.points[28].normal[2] =  0.000000f; 
  normals.points[29].normal[0] = -0.939693f; normals.points[29].normal[1] =  -0.342020f; normals.points[29].normal[2] =  0.000000f; 
  normals.points[30].normal[0] =  0.939693f; normals.points[30].normal[1] =  -0.342020f; normals.points[30].normal[2] =  0.000000f; 


  // Create a shared cylinder model pointer directly
  SampleConsensusModelConePtr model (new SampleConsensusModelCone<PointXYZ, Normal> (cloud.makeShared ()));
  model->setInputNormals (normals.makeShared ());

  // Create the RANSAC object
  RandomSampleConsensus<PointXYZ> sac (model, 0.03);

  // Algorithm tests
  bool result = sac.computeModel ();
  ASSERT_EQ (result, true);

  std::vector<int> sample;
  sac.getModel (sample);
  EXPECT_EQ (int (sample.size ()), 3);

  std::vector<int> inliers;
  sac.getInliers (inliers);
  EXPECT_EQ (int (inliers.size ()), 31);

  Eigen::VectorXf coeff;
  sac.getModelCoefficients (coeff);
  EXPECT_EQ (int (coeff.size ()), 7);
  EXPECT_NEAR (coeff[0],  0, 1e-2);
  EXPECT_NEAR (coeff[1],  0.1,  1e-2);
  EXPECT_NEAR (coeff[6],  0.349066, 1e-2);

  Eigen::VectorXf coeff_refined;
  model->optimizeModelCoefficients (inliers, coeff, coeff_refined);
  EXPECT_EQ (int (coeff_refined.size ()), 7);
  EXPECT_NEAR (coeff_refined[6], 0.349066 , 1e-2);
}
Exemple #14
0
template <typename PointSource, typename PointTarget> void
pcl::IterativeClosestPoint<PointSource, PointTarget>::computeTransformation (PointCloudSource &output, const Eigen::Matrix4f &guess)
{
  // Allocate enough space to hold the results
  std::vector<int> nn_indices (1);
  std::vector<float> nn_dists (1);

  // Point cloud containing the correspondences of each point in <input, indices>
  PointCloudTarget input_corresp;
  input_corresp.points.resize (indices_->size ());

  nr_iterations_ = 0;
  converged_ = false;
  double dist_threshold = corr_dist_threshold_ * corr_dist_threshold_;

  // If the guessed transformation is non identity
  if (guess != Eigen::Matrix4f::Identity ())
  {
    // Initialise final transformation to the guessed one
    final_transformation_ = guess;
    // Apply guessed transformation prior to search for neighbours
    transformPointCloud (output, output, guess);
  }

  // Resize the vector of distances between correspondences 
  std::vector<float> previous_correspondence_distances (indices_->size ());
  correspondence_distances_.resize (indices_->size ());

  while (!converged_)           // repeat until convergence
  {
    // Save the previously estimated transformation
    previous_transformation_ = transformation_;
    // And the previous set of distances
    previous_correspondence_distances = correspondence_distances_;

    int cnt = 0;
    std::vector<int> source_indices (indices_->size ());
    std::vector<int> target_indices (indices_->size ());

    // Iterating over the entire index vector and  find all correspondences
    for (size_t idx = 0; idx < indices_->size (); ++idx)
    {
      if (!this->searchForNeighbors (output, (*indices_)[idx], nn_indices, nn_dists))
      {
        PCL_ERROR ("[pcl::%s::computeTransformation] Unable to find a nearest neighbor in the target dataset for point %d in the source!\n", getClassName ().c_str (), (*indices_)[idx]);
        return;
      }

      // Check if the distance to the nearest neighbor is smaller than the user imposed threshold
      if (nn_dists[0] < dist_threshold)
      {
        source_indices[cnt] = (*indices_)[idx];
        target_indices[cnt] = nn_indices[0];
        cnt++;
      }

      // Save the nn_dists[0] to a global vector of distances
      correspondence_distances_[(*indices_)[idx]] = std::min (nn_dists[0], static_cast<float> (dist_threshold));
    }
    if (cnt < min_number_correspondences_)
    {
      PCL_ERROR ("[pcl::%s::computeTransformation] Not enough correspondences found. Relax your threshold parameters.\n", getClassName ().c_str ());
      converged_ = false;
      return;
    }

    // Resize to the actual number of valid correspondences
    source_indices.resize (cnt); target_indices.resize (cnt);

    std::vector<int> source_indices_good;
    std::vector<int> target_indices_good;
    {
      // From the set of correspondences found, attempt to remove outliers
      // Create the registration model
      typedef typename SampleConsensusModelRegistration<PointSource>::Ptr SampleConsensusModelRegistrationPtr;
      SampleConsensusModelRegistrationPtr model;
      model.reset (new SampleConsensusModelRegistration<PointSource> (output.makeShared (), source_indices));
      // Pass the target_indices
      model->setInputTarget (target_, target_indices);
      // Create a RANSAC model
      RandomSampleConsensus<PointSource> sac (model, inlier_threshold_);
      sac.setMaxIterations (ransac_iterations_);

      // Compute the set of inliers
      if (!sac.computeModel ())
      {
        source_indices_good = source_indices;
        target_indices_good = target_indices;
      }
      else
      {
        std::vector<int> inliers;
        // Get the inliers
        sac.getInliers (inliers);
        source_indices_good.resize (inliers.size ());
        target_indices_good.resize (inliers.size ());

        boost::unordered_map<int, int> source_to_target;
        for (unsigned int i = 0; i < source_indices.size(); ++i)
          source_to_target[source_indices[i]] = target_indices[i];

        // Copy just the inliers
        std::copy(inliers.begin(), inliers.end(), source_indices_good.begin());
        for (size_t i = 0; i < inliers.size (); ++i)
          target_indices_good[i] = source_to_target[inliers[i]];
      }
    }

    // Check whether we have enough correspondences
    cnt = static_cast<int> (source_indices_good.size ());
    if (cnt < min_number_correspondences_)
    {
      PCL_ERROR ("[pcl::%s::computeTransformation] Not enough correspondences found. Relax your threshold parameters.\n", getClassName ().c_str ());
      converged_ = false;
      return;
    }

    PCL_DEBUG ("[pcl::%s::computeTransformation] Number of correspondences %d [%f%%] out of %zu points [100.0%%], RANSAC rejected: %zu [%f%%].\n", 
        getClassName ().c_str (), 
        cnt, 
        (static_cast<float> (cnt) * 100.0f) / static_cast<float> (indices_->size ()), 
        indices_->size (), 
        source_indices.size () - cnt, 
        static_cast<float> (source_indices.size () - cnt) * 100.0f / static_cast<float> (source_indices.size ()));
  
    // Estimate the transform
    //rigid_transformation_estimation_(output, source_indices_good, *target_, target_indices_good, transformation_);
    transformation_estimation_->estimateRigidTransformation (output, source_indices_good, *target_, target_indices_good, transformation_);

    // Tranform the data
    transformPointCloud (output, output, transformation_);

    // Obtain the final transformation    
    final_transformation_ = transformation_ * final_transformation_;

    nr_iterations_++;

    // Update the vizualization of icp convergence
    if (update_visualizer_ != 0)
      update_visualizer_(output, source_indices_good, *target_, target_indices_good );

    // Various/Different convergence termination criteria
    // 1. Number of iterations has reached the maximum user imposed number of iterations (via 
    //    setMaximumIterations)
    // 2. The epsilon (difference) between the previous transformation and the current estimated transformation 
    //    is smaller than an user imposed value (via setTransformationEpsilon)
    // 3. The sum of Euclidean squared errors is smaller than a user defined threshold (via 
    //    setEuclideanFitnessEpsilon)

    if (nr_iterations_ >= max_iterations_ ||
        (transformation_ - previous_transformation_).array ().abs ().sum () < transformation_epsilon_ ||
        fabs (this->getFitnessScore (correspondence_distances_, previous_correspondence_distances)) <= euclidean_fitness_epsilon_
       )
    {
      converged_ = true;
      PCL_DEBUG ("[pcl::%s::computeTransformation] Convergence reached. Number of iterations: %d out of %d. Transformation difference: %f\n",
                 getClassName ().c_str (), nr_iterations_, max_iterations_, (transformation_ - previous_transformation_).array ().abs ().sum ());

      PCL_DEBUG ("nr_iterations_ (%d) >= max_iterations_ (%d)\n", nr_iterations_, max_iterations_);
      PCL_DEBUG ("(transformation_ - previous_transformation_).array ().abs ().sum () (%f) < transformation_epsilon_ (%f)\n",
                 (transformation_ - previous_transformation_).array ().abs ().sum (), transformation_epsilon_);
      PCL_DEBUG ("fabs (getFitnessScore (correspondence_distances_, previous_correspondence_distances)) (%f) <= euclidean_fitness_epsilon_ (%f)\n",
                 fabs (this->getFitnessScore (correspondence_distances_, previous_correspondence_distances)),
                 euclidean_fitness_epsilon_);

    }
  }
}
void
compute (const sensor_msgs::PointCloud2::ConstPtr &input, sensor_msgs::PointCloud2 &output,
         int max_iterations = 1000, double threshold = 0.05, bool negative = false)
{
  // Convert data to PointCloud<T>
  PointCloud<PointXYZ>::Ptr xyz (new PointCloud<PointXYZ>);
  fromROSMsg (*input, *xyz);

  // Estimate
  TicToc tt;
  print_highlight (stderr, "Computing ");
  
  tt.tic ();

  // Refine the plane indices
  typedef SampleConsensusModelPlane<PointXYZ>::Ptr SampleConsensusModelPlanePtr;
  SampleConsensusModelPlanePtr model (new SampleConsensusModelPlane<PointXYZ> (xyz));
  RandomSampleConsensus<PointXYZ> sac (model, threshold);
  sac.setMaxIterations (max_iterations);
  bool res = sac.computeModel ();
  
  vector<int> inliers;
  sac.getInliers (inliers);
  Eigen::VectorXf coefficients;
  sac.getModelCoefficients (coefficients);

  if (!res || inliers.empty ())
  {
    PCL_ERROR ("No planar model found. Relax thresholds and continue.\n");
    return;
  }
  sac.refineModel (2, 50);
  sac.getInliers (inliers);
  sac.getModelCoefficients (coefficients);

  print_info ("[done, "); print_value ("%g", tt.toc ()); print_info (" ms, plane has : "); print_value ("%zu", inliers.size ()); print_info (" points]\n");

  print_info ("Model coefficients: [");
  print_value ("%g %g %g %g", coefficients[0], coefficients[1], coefficients[2], coefficients[3]); print_info ("]\n");

  // Instead of returning the planar model as a set of inliers, return the outliers, but perform a cluster segmentation first
  if (negative)
  {
    // Remove the plane indices from the data
    PointIndices::Ptr everything_but_the_plane (new PointIndices);
    std::vector<int> indices_fullset (xyz->size ());
    for (int p_it = 0; p_it < static_cast<int> (indices_fullset.size ()); ++p_it)
      indices_fullset[p_it] = p_it;
    
    std::sort (inliers.begin (), inliers.end ());
    set_difference (indices_fullset.begin (), indices_fullset.end (),
                    inliers.begin (), inliers.end (),
                    inserter (everything_but_the_plane->indices, everything_but_the_plane->indices.begin ()));

    // Extract largest cluster minus the plane
    vector<PointIndices> cluster_indices;
    EuclideanClusterExtraction<PointXYZ> ec;
    ec.setClusterTolerance (0.02); // 2cm
    ec.setMinClusterSize (100);
    ec.setInputCloud (xyz);
    ec.setIndices (everything_but_the_plane);
    ec.extract (cluster_indices);

    // Convert data back
    copyPointCloud (*input, cluster_indices[0].indices, output);
  }
  else
  {
    // Convert data back
    PointCloud<PointXYZ> output_inliers;
    copyPointCloud (*input, inliers, output);
  }
}
TEST (SampleConsensusModelParallelLine, RANSAC)
{
  PointCloud<PointXYZ> cloud (16, 1);

  // Line 1
  cloud.points[0].getVector3fMap () <<  1.0f,  2.00f,  3.00f;
  cloud.points[1].getVector3fMap () <<  4.0f,  5.00f,  6.00f;
  cloud.points[2].getVector3fMap () <<  7.0f,  8.00f,  9.00f;
  cloud.points[3].getVector3fMap () << 10.0f, 11.00f, 12.00f;
  cloud.points[4].getVector3fMap () << 13.0f, 14.00f, 15.00f;
  cloud.points[5].getVector3fMap () << 16.0f, 17.00f, 18.00f;
  cloud.points[6].getVector3fMap () << 19.0f, 20.00f, 21.00f;
  cloud.points[7].getVector3fMap () << 22.0f, 23.00f, 24.00f;
  // Random points
  cloud.points[8].getVector3fMap () << -5.0f,  1.57f,  0.75f;
  cloud.points[9].getVector3fMap () <<  4.0f,  2.00f,  3.00f;
  // Line 2 (parallel to the Z axis)
  cloud.points[10].getVector3fMap () << -1.00f,  5.00f,  0.0f;
  cloud.points[11].getVector3fMap () << -1.05f,  5.01f,  1.0f;
  cloud.points[12].getVector3fMap () << -1.01f,  5.05f,  2.0f;
  cloud.points[13].getVector3fMap () << -1.05f,  5.01f,  3.0f;
  cloud.points[14].getVector3fMap () << -1.01f,  5.05f,  4.0f;
  cloud.points[15].getVector3fMap () << -1.05f,  5.01f,  5.0f;

  // Create a shared line model pointer directly
  const double eps = 0.1; //angle eps in radians
  const Eigen::Vector3f axis (0, 0, 1);
  SampleConsensusModelParallelLinePtr model (new SampleConsensusModelParallelLine<PointXYZ> (cloud.makeShared ()));
  model->setAxis (axis);
  model->setEpsAngle (eps);

  // Create the RANSAC object
  RandomSampleConsensus<PointXYZ> sac (model, 0.1);

  // Algorithm tests
  bool result = sac.computeModel ();
  ASSERT_TRUE (result);

  std::vector<int> sample;
  sac.getModel (sample);
  EXPECT_EQ (2, sample.size ());

  std::vector<int> inliers;
  sac.getInliers (inliers);
  EXPECT_EQ (6, inliers.size ());

  Eigen::VectorXf coeff;
  sac.getModelCoefficients (coeff);
  EXPECT_EQ (6, coeff.size ());

  // Make sure the returned direction respects the angular constraint
  double angle_diff = getAngle3D (axis, coeff.tail<3> ());
  angle_diff = std::min (angle_diff, M_PI - angle_diff);
  EXPECT_GT (eps, angle_diff);

  // Projection tests
  PointCloud<PointXYZ> proj_points;
  model->projectPoints (inliers, coeff, proj_points);

  EXPECT_XYZ_NEAR (PointXYZ (-1.05, 5.05, 3.0), proj_points.points[13], 0.1);
  EXPECT_XYZ_NEAR (PointXYZ (-1.05, 5.05, 4.0), proj_points.points[14], 0.1);
}
TEST (SampleConsensusModelNormalParallelPlane, RANSAC)
{
  srand (0);

  // Use a custom point cloud for these tests until we need something better
  PointCloud<PointXYZ> cloud;
  PointCloud<Normal> normals;
  cloud.points.resize (10);
  normals.resize (10);

  for (unsigned idx = 0; idx < cloud.size (); ++idx)
  {
    cloud.points[idx].x = static_cast<float> ((rand () % 200) - 100);
    cloud.points[idx].y = static_cast<float> ((rand () % 200) - 100);
    cloud.points[idx].z = 0.0f;

    normals.points[idx].normal_x = 0.0f;
    normals.points[idx].normal_y = 0.0f;
    normals.points[idx].normal_z = 1.0f;
  }

  // Create a shared plane model pointer directly
  SampleConsensusModelNormalParallelPlanePtr model (new SampleConsensusModelNormalParallelPlane<PointXYZ, Normal> (cloud.makeShared ()));
  model->setInputNormals (normals.makeShared ());

  const float max_angle_rad = 0.01f;
  const float angle_eps = 0.001f;
  model->setEpsAngle (max_angle_rad);

  // Test true axis
  {
    model->setAxis (Eigen::Vector3f (0, 0, 1));

    RandomSampleConsensus<PointXYZ> sac (model, 0.03);
    sac.computeModel();

    std::vector<int> inliers;
    sac.getInliers (inliers);
    ASSERT_EQ (cloud.size (), inliers.size ());
  }

  // test axis slightly in valid range
  {
    model->setAxis (Eigen::Vector3f (0, sin (max_angle_rad * (1 - angle_eps)), cos (max_angle_rad * (1 - angle_eps))));
    RandomSampleConsensus<PointXYZ> sac (model, 0.03);
    sac.computeModel ();

    std::vector<int> inliers;
    sac.getInliers (inliers);
    ASSERT_EQ (cloud.size (), inliers.size ());
  }

  // test axis slightly out of valid range
  {
    model->setAxis (Eigen::Vector3f (0, sin (max_angle_rad * (1 + angle_eps)), cos (max_angle_rad * (1 + angle_eps))));
    RandomSampleConsensus<PointXYZ> sac (model, 0.03);
    sac.computeModel ();

    std::vector<int> inliers;
    sac.getInliers (inliers);
    ASSERT_EQ (0, inliers.size ());
  }
}
TEST (RANSAC, SampleConsensusModelNormalSphere)
{
  srand (0);

  // Use a custom point cloud for these tests until we need something better
  PointCloud<PointXYZ> cloud;
  PointCloud<Normal> normals;
  cloud.points.resize (27); normals.points.resize (27);
  cloud.points[0].x = -0.014695f; cloud.points[0].y =  0.009549f; cloud.points[0].z = 0.954775f; 
  cloud.points[1].x =  0.014695f; cloud.points[1].y =  0.009549f; cloud.points[1].z = 0.954775f; 
  cloud.points[2].x = -0.014695f; cloud.points[2].y =  0.040451f; cloud.points[2].z = 0.954775f; 
  cloud.points[3].x =  0.014695f; cloud.points[3].y =  0.040451f; cloud.points[3].z = 0.954775f; 
  cloud.points[4].x = -0.009082f; cloud.points[4].y = -0.015451f; cloud.points[4].z = 0.972049f; 
  cloud.points[5].x =  0.009082f; cloud.points[5].y = -0.015451f; cloud.points[5].z = 0.972049f; 
  cloud.points[6].x = -0.038471f; cloud.points[6].y =  0.009549f; cloud.points[6].z = 0.972049f; 
  cloud.points[7].x =  0.038471f; cloud.points[7].y =  0.009549f; cloud.points[7].z = 0.972049f; 
  cloud.points[8].x = -0.038471f; cloud.points[8].y =  0.040451f; cloud.points[8].z = 0.972049f; 
  cloud.points[9].x =  0.038471f; cloud.points[9].y =  0.040451f; cloud.points[9].z = 0.972049f; 
  cloud.points[10].x = -0.009082f; cloud.points[10].y =  0.065451f; cloud.points[10].z = 0.972049f; 
  cloud.points[11].x =  0.009082f; cloud.points[11].y =  0.065451f; cloud.points[11].z = 0.972049f; 
  cloud.points[12].x = -0.023776f; cloud.points[12].y = -0.015451f; cloud.points[12].z = 0.982725f; 
  cloud.points[13].x =  0.023776f; cloud.points[13].y = -0.015451f; cloud.points[13].z = 0.982725f; 
  cloud.points[14].x = -0.023776f; cloud.points[14].y =  0.065451f; cloud.points[14].z = 0.982725f; 
  cloud.points[15].x =  0.023776f; cloud.points[15].y =  0.065451f; cloud.points[15].z = 0.982725f; 
  cloud.points[16].x = -0.000000f; cloud.points[16].y = -0.025000f; cloud.points[16].z = 1.000000f; 
  cloud.points[17].x =  0.000000f; cloud.points[17].y = -0.025000f; cloud.points[17].z = 1.000000f; 
  cloud.points[18].x = -0.029389f; cloud.points[18].y = -0.015451f; cloud.points[18].z = 1.000000f; 
  cloud.points[19].x =  0.029389f; cloud.points[19].y = -0.015451f; cloud.points[19].z = 1.000000f; 
  cloud.points[20].x = -0.047553f; cloud.points[20].y =  0.009549f; cloud.points[20].z = 1.000000f; 
  cloud.points[21].x =  0.047553f; cloud.points[21].y =  0.009549f; cloud.points[21].z = 1.000000f; 
  cloud.points[22].x = -0.047553f; cloud.points[22].y =  0.040451f; cloud.points[22].z = 1.000000f; 
  cloud.points[23].x =  0.047553f; cloud.points[23].y =  0.040451f; cloud.points[23].z = 1.000000f; 
  cloud.points[24].x = -0.029389f; cloud.points[24].y =  0.065451f; cloud.points[24].z = 1.000000f; 
  cloud.points[25].x =  0.029389f; cloud.points[25].y =  0.065451f; cloud.points[25].z = 1.000000f; 
  cloud.points[26].x =  0.000000f; cloud.points[26].y =  0.075000f; cloud.points[26].z = 1.000000f; 
  
  normals.points[0].normal[0] = -0.293893f; normals.points[0].normal[1] =  -0.309017f; normals.points[0].normal[2] =  -0.904509f; 
  normals.points[1].normal[0] =  0.293893f; normals.points[1].normal[1] =  -0.309017f; normals.points[1].normal[2] =  -0.904508f; 
  normals.points[2].normal[0] = -0.293893f; normals.points[2].normal[1] =   0.309017f; normals.points[2].normal[2] =  -0.904509f; 
  normals.points[3].normal[0] =  0.293893f; normals.points[3].normal[1] =   0.309017f; normals.points[3].normal[2] =  -0.904508f; 
  normals.points[4].normal[0] = -0.181636f; normals.points[4].normal[1] =  -0.809017f; normals.points[4].normal[2] =  -0.559017f; 
  normals.points[5].normal[0] =  0.181636f; normals.points[5].normal[1] =  -0.809017f; normals.points[5].normal[2] =  -0.559017f; 
  normals.points[6].normal[0] = -0.769421f; normals.points[6].normal[1] =  -0.309017f; normals.points[6].normal[2] =  -0.559017f; 
  normals.points[7].normal[0] =  0.769421f; normals.points[7].normal[1] =  -0.309017f; normals.points[7].normal[2] =  -0.559017f; 
  normals.points[8].normal[0] = -0.769421f; normals.points[8].normal[1] =   0.309017f; normals.points[8].normal[2] =  -0.559017f; 
  normals.points[9].normal[0] =  0.769421f; normals.points[9].normal[1] =   0.309017f; normals.points[9].normal[2] =  -0.559017f; 
  normals.points[10].normal[0] = -0.181636f; normals.points[10].normal[1] =  0.809017f; normals.points[10].normal[2] =  -0.559017f; 
  normals.points[11].normal[0] =  0.181636f; normals.points[11].normal[1] =  0.809017f; normals.points[11].normal[2] =  -0.559017f; 
  normals.points[12].normal[0] = -0.475528f; normals.points[12].normal[1] = -0.809017f; normals.points[12].normal[2] =  -0.345491f; 
  normals.points[13].normal[0] =  0.475528f; normals.points[13].normal[1] = -0.809017f; normals.points[13].normal[2] =  -0.345491f; 
  normals.points[14].normal[0] = -0.475528f; normals.points[14].normal[1] =  0.809017f; normals.points[14].normal[2] =  -0.345491f; 
  normals.points[15].normal[0] =  0.475528f; normals.points[15].normal[1] =  0.809017f; normals.points[15].normal[2] =  -0.345491f; 
  normals.points[16].normal[0] = -0.000000f; normals.points[16].normal[1] = -1.000000f; normals.points[16].normal[2] =  0.000000f; 
  normals.points[17].normal[0] =  0.000000f; normals.points[17].normal[1] = -1.000000f; normals.points[17].normal[2] =  0.000000f; 
  normals.points[18].normal[0] = -0.587785f; normals.points[18].normal[1] = -0.809017f; normals.points[18].normal[2] =  0.000000f; 
  normals.points[19].normal[0] =  0.587785f; normals.points[19].normal[1] = -0.809017f; normals.points[19].normal[2] =  0.000000f; 
  normals.points[20].normal[0] = -0.951057f; normals.points[20].normal[1] = -0.309017f; normals.points[20].normal[2] =  0.000000f; 
  normals.points[21].normal[0] =  0.951057f; normals.points[21].normal[1] = -0.309017f; normals.points[21].normal[2] =  0.000000f; 
  normals.points[22].normal[0] = -0.951057f; normals.points[22].normal[1] =  0.309017f; normals.points[22].normal[2] =  0.000000f; 
  normals.points[23].normal[0] =  0.951057f; normals.points[23].normal[1] =  0.309017f; normals.points[23].normal[2] =  0.000000f; 
  normals.points[24].normal[0] = -0.587785f; normals.points[24].normal[1] =  0.809017f; normals.points[24].normal[2] =  0.000000f; 
  normals.points[25].normal[0] =  0.587785f; normals.points[25].normal[1] =  0.809017f; normals.points[25].normal[2] =  0.000000f; 
  normals.points[26].normal[0] =  0.000000f; normals.points[26].normal[1] =  1.000000f; normals.points[26].normal[2] =  0.000000f; 
  
  // Create a shared sphere model pointer directly
  SampleConsensusModelNormalSpherePtr model (new SampleConsensusModelNormalSphere<PointXYZ, Normal> (cloud.makeShared ()));
  model->setInputNormals(normals.makeShared ());

  // Create the RANSAC object
  RandomSampleConsensus<PointXYZ> sac (model, 0.03);

  // Algorithm tests
  bool result = sac.computeModel ();
  ASSERT_EQ (result, true); 

  std::vector<int> sample;
  sac.getModel (sample);
  EXPECT_EQ (int (sample.size ()), 4);

  std::vector<int> inliers;
  sac.getInliers (inliers);
  EXPECT_EQ (int (inliers.size ()), 27);

  Eigen::VectorXf coeff;
  sac.getModelCoefficients (coeff);
  EXPECT_EQ (int (coeff.size ()), 4);
  EXPECT_NEAR (coeff[0], 0.0,   1e-2);
  EXPECT_NEAR (coeff[1], 0.025, 1e-2);
  EXPECT_NEAR (coeff[2], 1.0,   1e-2);
  EXPECT_NEAR (coeff[3], 0.05,  1e-2);
  Eigen::VectorXf coeff_refined;
  model->optimizeModelCoefficients (inliers, coeff, coeff_refined);
  EXPECT_EQ (int (coeff_refined.size ()), 4);
  EXPECT_NEAR (coeff_refined[0], 0.0,   1e-2);
  EXPECT_NEAR (coeff_refined[1], 0.025, 1e-2);
  EXPECT_NEAR (coeff_refined[2], 1.0,   1e-2);
  EXPECT_NEAR (coeff_refined[3], 0.05,  1e-2);	 
}