/* * Software License Agreement (BSD License) * * Point Cloud Library (PCL) - www.pointclouds.org * Copyright (c) 2010-2012, Willow Garage, Inc. * Copyright (c) 2014-, Open Perception, Inc. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the copyright holder(s) nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace pcl; using namespace pcl::io; using namespace std; typedef SampleConsensusModel::Ptr SampleConsensusModelPtr; typedef SampleConsensusModelPlane::Ptr SampleConsensusModelPlanePtr; typedef SampleConsensusModelSphere::Ptr SampleConsensusModelSpherePtr; typedef SampleConsensusModelCylinder::Ptr SampleConsensusModelCylinderPtr; typedef SampleConsensusModelCone::Ptr SampleConsensusModelConePtr; typedef SampleConsensusModelCircle2D::Ptr SampleConsensusModelCircle2DPtr; typedef SampleConsensusModelCircle3D::Ptr SampleConsensusModelCircle3DPtr; typedef SampleConsensusModelLine::Ptr SampleConsensusModelLinePtr; typedef SampleConsensusModelNormalPlane::Ptr SampleConsensusModelNormalPlanePtr; typedef SampleConsensusModelNormalSphere::Ptr SampleConsensusModelNormalSpherePtr; typedef SampleConsensusModelParallelPlane::Ptr SampleConsensusModelParallelPlanePtr; typedef SampleConsensusModelNormalParallelPlane::Ptr SampleConsensusModelNormalParallelPlanePtr; PointCloud::Ptr cloud_ (new PointCloud ()); PointCloud::Ptr normals_ (new PointCloud ()); vector indices_; float plane_coeffs_[] = {-0.8964f, -0.5868f, -1.208f}; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void verifyPlaneSac (ModelType & model, SacType & sac, unsigned int inlier_number = 2000, float tol = 1e-1f, float refined_tol = 1e-1f, float proj_tol = 1e-3f) { // Algorithm tests bool result = sac.computeModel (); ASSERT_TRUE (result); std::vector sample; sac.getModel (sample); EXPECT_EQ (3, sample.size ()); std::vector inliers; sac.getInliers (inliers); EXPECT_LT (inlier_number, inliers.size ()); Eigen::VectorXf coeff; sac.getModelCoefficients (coeff); EXPECT_EQ (4, coeff.size ()); EXPECT_NEAR (plane_coeffs_[0], coeff[0]/coeff[3], tol); EXPECT_NEAR (plane_coeffs_[1], coeff[1]/coeff[3], tol); EXPECT_NEAR (plane_coeffs_[2], coeff[2]/coeff[3], tol); Eigen::VectorXf coeff_refined; model->optimizeModelCoefficients (inliers, coeff, coeff_refined); EXPECT_EQ (4, coeff_refined.size ()); EXPECT_NEAR (plane_coeffs_[0], coeff_refined[0]/coeff_refined[3], refined_tol); EXPECT_NEAR (plane_coeffs_[1], coeff_refined[1]/coeff_refined[3], refined_tol); // This test fails in Windows (VS 2010) -- not sure why yet -- relaxing the constraint from 1e-2 to 1e-1 // This test fails in MacOS too -- not sure why yet -- disabling //EXPECT_NEAR (coeff_refined[2]/coeff_refined[3], plane_coeffs_[2], refined_tol); // Projection tests PointCloud proj_points; model->projectPoints (inliers, coeff_refined, proj_points); EXPECT_NEAR (1.1266, proj_points.points[20].x, proj_tol); EXPECT_NEAR (0.0152, proj_points.points[20].y, proj_tol); EXPECT_NEAR (-0.0156, proj_points.points[20].z, proj_tol); EXPECT_NEAR (1.1843, proj_points.points[30].x, proj_tol); EXPECT_NEAR (-0.0635, proj_points.points[30].y, proj_tol); EXPECT_NEAR (-0.0201, proj_points.points[30].z, proj_tol); EXPECT_NEAR (1.0749, proj_points.points[50].x, proj_tol); EXPECT_NEAR (-0.0586, proj_points.points[50].y, proj_tol); EXPECT_NEAR (0.0587, proj_points.points[50].z, refined_tol); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (SampleConsensusModelPlane, Base) { // Create a shared plane model pointer directly SampleConsensusModelPlanePtr model (new SampleConsensusModelPlane (cloud_)); // Basic tests PointCloud::ConstPtr cloud = model->getInputCloud (); ASSERT_EQ (cloud_->points.size (), cloud->points.size ()); model->setInputCloud (cloud); cloud = model->getInputCloud (); ASSERT_EQ (cloud_->points.size (), cloud->points.size ()); boost::shared_ptr > indices = model->getIndices (); ASSERT_EQ (indices_.size (), indices->size ()); model->setIndices (indices_); indices = model->getIndices (); ASSERT_EQ (indices_.size (), indices->size ()); model->setIndices (indices); indices = model->getIndices (); ASSERT_EQ (indices_.size (), indices->size ()); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RANSAC, Base) { // Create a shared plane model pointer directly SampleConsensusModelPlanePtr model (new SampleConsensusModelPlane (cloud_)); // Create the RANSAC object RandomSampleConsensus sac (model, 0.03); // Basic tests ASSERT_EQ (0.03, sac.getDistanceThreshold ()); sac.setDistanceThreshold (0.03); ASSERT_EQ (0.03, sac.getDistanceThreshold ()); sac.setProbability (0.99); ASSERT_EQ (0.99, sac.getProbability ()); sac.setMaxIterations (10000); ASSERT_EQ (10000, sac.getMaxIterations ()); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RANSAC, SampleConsensusModelPlane) { srand (0); // Create a shared plane model pointer directly SampleConsensusModelPlanePtr model (new SampleConsensusModelPlane (cloud_)); // Create the RANSAC object RandomSampleConsensus sac (model, 0.03); verifyPlaneSac(model, sac); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (LMedS, SampleConsensusModelPlane) { srand (0); // Create a shared plane model pointer directly SampleConsensusModelPlanePtr model (new SampleConsensusModelPlane (cloud_)); // Create the LMedS object LeastMedianSquares sac (model, 0.03); verifyPlaneSac(model, sac); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (MSAC, SampleConsensusModelPlane) { srand (0); // Create a shared plane model pointer directly SampleConsensusModelPlanePtr model (new SampleConsensusModelPlane (cloud_)); // Create the MSAC object MEstimatorSampleConsensus sac (model, 0.03); verifyPlaneSac (model, sac); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RRANSAC, SampleConsensusModelPlane) { srand (0); // Create a shared plane model pointer directly SampleConsensusModelPlanePtr model (new SampleConsensusModelPlane (cloud_)); // Create the RRANSAC object RandomizedRandomSampleConsensus sac (model, 0.03); sac.setFractionNrPretest (10.0); ASSERT_EQ (10.0, sac.getFractionNrPretest ()); verifyPlaneSac(model, sac, 600, 1.0f, 1.0f, 0.01f); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RMSAC, SampleConsensusModelPlane) { srand (0); // Create a shared plane model pointer directly SampleConsensusModelPlanePtr model (new SampleConsensusModelPlane (cloud_)); // Create the RMSAC object RandomizedMEstimatorSampleConsensus sac (model, 0.03); sac.setFractionNrPretest (10.0); ASSERT_EQ (10.0, sac.getFractionNrPretest ()); verifyPlaneSac(model, sac, 600, 1.0f, 1.0f, 0.01f); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RANSAC, SampleConsensusModelNormalParallelPlane) { srand (0); // Use a custom point cloud for these tests until we need something better PointCloud cloud; PointCloud normals; cloud.points.resize (10); normals.resize (10); for (unsigned idx = 0; idx < cloud.size (); ++idx) { cloud.points[idx].x = static_cast ((rand () % 200) - 100); cloud.points[idx].y = static_cast ((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 (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 sac (model, 0.03); sac.computeModel(); std::vector 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 sac (model, 0.03); sac.computeModel(); std::vector 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 sac (model, 0.03); sac.computeModel(); std::vector inliers; sac.getInliers (inliers); ASSERT_EQ (0, inliers.size ()); } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (MLESAC, SampleConsensusModelPlane) { srand (0); // Create a shared plane model pointer directly SampleConsensusModelPlanePtr model (new SampleConsensusModelPlane (cloud_)); // Create the MSAC object MaximumLikelihoodSampleConsensus sac (model, 0.03); verifyPlaneSac(model, sac, 1000, 0.3f, 0.2f, 0.01f); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RANSAC, SampleConsensusModelSphere) { srand (0); // Use a custom point cloud for these tests until we need something better PointCloud cloud; cloud.points.resize (10); cloud.points[0].getVector3fMap () << 1.7068f, 1.0684f, 2.2147f; cloud.points[1].getVector3fMap () << 2.4708f, 2.3081f, 1.1736f; cloud.points[2].getVector3fMap () << 2.7609f, 1.9095f, 1.3574f; cloud.points[3].getVector3fMap () << 2.8016f, 1.6704f, 1.5009f; cloud.points[4].getVector3fMap () << 1.8517f, 2.0276f, 1.0112f; cloud.points[5].getVector3fMap () << 1.8726f, 1.3539f, 2.7523f; cloud.points[6].getVector3fMap () << 2.5179f, 2.3218f, 1.2074f; cloud.points[7].getVector3fMap () << 2.4026f, 2.5114f, 2.7588f; cloud.points[8].getVector3fMap () << 2.6999f, 2.5606f, 1.5571f; cloud.points[9].getVector3fMap () << 0.0000f, 0.0000f, 0.0000f; // Create a shared sphere model pointer directly SampleConsensusModelSpherePtr model (new SampleConsensusModelSphere (cloud.makeShared ())); // Create the RANSAC object RandomSampleConsensus sac (model, 0.03); // Algorithm tests bool result = sac.computeModel (); ASSERT_TRUE (result); std::vector sample; sac.getModel (sample); EXPECT_EQ (4, sample.size ()); std::vector inliers; sac.getInliers (inliers); EXPECT_EQ (9, inliers.size ()); Eigen::VectorXf coeff; sac.getModelCoefficients (coeff); EXPECT_EQ (4, coeff.size ()); EXPECT_NEAR (2, coeff[0]/coeff[3], 1e-2); EXPECT_NEAR (2, coeff[1]/coeff[3], 1e-2); EXPECT_NEAR (2, coeff[2]/coeff[3], 1e-2); Eigen::VectorXf coeff_refined; model->optimizeModelCoefficients (inliers, coeff, coeff_refined); EXPECT_EQ (4, coeff_refined.size ()); EXPECT_NEAR (2, coeff_refined[0]/coeff_refined[3], 1e-2); EXPECT_NEAR (2, coeff_refined[1]/coeff_refined[3], 1e-2); EXPECT_NEAR (2, coeff_refined[2]/coeff_refined[3], 1e-2); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RANSAC, SampleConsensusModelNormalSphere) { srand (0); // Use a custom point cloud for these tests until we need something better PointCloud cloud; PointCloud normals; cloud.points.resize (27); normals.points.resize (27); cloud.points[ 0].getVector3fMap () << -0.014695f, 0.009549f, 0.954775f; cloud.points[ 1].getVector3fMap () << 0.014695f, 0.009549f, 0.954775f; cloud.points[ 2].getVector3fMap () << -0.014695f, 0.040451f, 0.954775f; cloud.points[ 3].getVector3fMap () << 0.014695f, 0.040451f, 0.954775f; cloud.points[ 4].getVector3fMap () << -0.009082f, -0.015451f, 0.972049f; cloud.points[ 5].getVector3fMap () << 0.009082f, -0.015451f, 0.972049f; cloud.points[ 6].getVector3fMap () << -0.038471f, 0.009549f, 0.972049f; cloud.points[ 7].getVector3fMap () << 0.038471f, 0.009549f, 0.972049f; cloud.points[ 8].getVector3fMap () << -0.038471f, 0.040451f, 0.972049f; cloud.points[ 9].getVector3fMap () << 0.038471f, 0.040451f, 0.972049f; cloud.points[10].getVector3fMap () << -0.009082f, 0.065451f, 0.972049f; cloud.points[11].getVector3fMap () << 0.009082f, 0.065451f, 0.972049f; cloud.points[12].getVector3fMap () << -0.023776f, -0.015451f, 0.982725f; cloud.points[13].getVector3fMap () << 0.023776f, -0.015451f, 0.982725f; cloud.points[14].getVector3fMap () << -0.023776f, 0.065451f, 0.982725f; cloud.points[15].getVector3fMap () << 0.023776f, 0.065451f, 0.982725f; cloud.points[16].getVector3fMap () << -0.000000f, -0.025000f, 1.000000f; cloud.points[17].getVector3fMap () << 0.000000f, -0.025000f, 1.000000f; cloud.points[18].getVector3fMap () << -0.029389f, -0.015451f, 1.000000f; cloud.points[19].getVector3fMap () << 0.029389f, -0.015451f, 1.000000f; cloud.points[20].getVector3fMap () << -0.047553f, 0.009549f, 1.000000f; cloud.points[21].getVector3fMap () << 0.047553f, 0.009549f, 1.000000f; cloud.points[22].getVector3fMap () << -0.047553f, 0.040451f, 1.000000f; cloud.points[23].getVector3fMap () << 0.047553f, 0.040451f, 1.000000f; cloud.points[24].getVector3fMap () << -0.029389f, 0.065451f, 1.000000f; cloud.points[25].getVector3fMap () << 0.029389f, 0.065451f, 1.000000f; cloud.points[26].getVector3fMap () << 0.000000f, 0.075000f, 1.000000f; normals.points[ 0].getNormalVector3fMap () << -0.293893f, -0.309017f, -0.904509f; normals.points[ 1].getNormalVector3fMap () << 0.293893f, -0.309017f, -0.904508f; normals.points[ 2].getNormalVector3fMap () << -0.293893f, 0.309017f, -0.904509f; normals.points[ 3].getNormalVector3fMap () << 0.293893f, 0.309017f, -0.904508f; normals.points[ 4].getNormalVector3fMap () << -0.181636f, -0.809017f, -0.559017f; normals.points[ 5].getNormalVector3fMap () << 0.181636f, -0.809017f, -0.559017f; normals.points[ 6].getNormalVector3fMap () << -0.769421f, -0.309017f, -0.559017f; normals.points[ 7].getNormalVector3fMap () << 0.769421f, -0.309017f, -0.559017f; normals.points[ 8].getNormalVector3fMap () << -0.769421f, 0.309017f, -0.559017f; normals.points[ 9].getNormalVector3fMap () << 0.769421f, 0.309017f, -0.559017f; normals.points[10].getNormalVector3fMap () << -0.181636f, 0.809017f, -0.559017f; normals.points[11].getNormalVector3fMap () << 0.181636f, 0.809017f, -0.559017f; normals.points[12].getNormalVector3fMap () << -0.475528f, -0.809017f, -0.345491f; normals.points[13].getNormalVector3fMap () << 0.475528f, -0.809017f, -0.345491f; normals.points[14].getNormalVector3fMap () << -0.475528f, 0.809017f, -0.345491f; normals.points[15].getNormalVector3fMap () << 0.475528f, 0.809017f, -0.345491f; normals.points[16].getNormalVector3fMap () << -0.000000f, -1.000000f, 0.000000f; normals.points[17].getNormalVector3fMap () << 0.000000f, -1.000000f, 0.000000f; normals.points[18].getNormalVector3fMap () << -0.587785f, -0.809017f, 0.000000f; normals.points[19].getNormalVector3fMap () << 0.587785f, -0.809017f, 0.000000f; normals.points[20].getNormalVector3fMap () << -0.951057f, -0.309017f, 0.000000f; normals.points[21].getNormalVector3fMap () << 0.951057f, -0.309017f, 0.000000f; normals.points[22].getNormalVector3fMap () << -0.951057f, 0.309017f, 0.000000f; normals.points[23].getNormalVector3fMap () << 0.951057f, 0.309017f, 0.000000f; normals.points[24].getNormalVector3fMap () << -0.587785f, 0.809017f, 0.000000f; normals.points[25].getNormalVector3fMap () << 0.587785f, 0.809017f, 0.000000f; normals.points[26].getNormalVector3fMap () << 0.000000f, 1.000000f, 0.000000f; // Create a shared sphere model pointer directly SampleConsensusModelNormalSpherePtr model (new SampleConsensusModelNormalSphere (cloud.makeShared ())); model->setInputNormals(normals.makeShared ()); // Create the RANSAC object RandomSampleConsensus sac (model, 0.03); // Algorithm tests bool result = sac.computeModel (); ASSERT_TRUE (result); std::vector sample; sac.getModel (sample); EXPECT_EQ (4, sample.size ()); std::vector inliers; sac.getInliers (inliers); EXPECT_EQ (27, inliers.size ()); Eigen::VectorXf coeff; sac.getModelCoefficients (coeff); EXPECT_EQ (4, coeff.size ()); EXPECT_NEAR (0.0, coeff[0], 1e-2); EXPECT_NEAR (0.025, coeff[1], 1e-2); EXPECT_NEAR (1.0, coeff[2], 1e-2); EXPECT_NEAR (0.05, coeff[3], 1e-2); Eigen::VectorXf coeff_refined; model->optimizeModelCoefficients (inliers, coeff, coeff_refined); EXPECT_EQ (4, coeff_refined.size ()); EXPECT_NEAR (0.0, coeff_refined[0], 1e-2); EXPECT_NEAR (0.025, coeff_refined[1], 1e-2); EXPECT_NEAR (1.0, coeff_refined[2], 1e-2); EXPECT_NEAR (0.05, coeff_refined[3], 1e-2); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RANSAC, SampleConsensusModelCone) { srand (0); // Use a custom point cloud for these tests until we need something better PointCloud cloud; PointCloud normals; cloud.points.resize (31); normals.points.resize (31); cloud.points[ 0].getVector3fMap () << -0.011247f, 0.200000f, 0.965384f; cloud.points[ 1].getVector3fMap () << 0.000000f, 0.200000f, 0.963603f; cloud.points[ 2].getVector3fMap () << 0.011247f, 0.200000f, 0.965384f; cloud.points[ 3].getVector3fMap () << -0.016045f, 0.175000f, 0.977916f; cloud.points[ 4].getVector3fMap () << -0.008435f, 0.175000f, 0.974038f; cloud.points[ 5].getVector3fMap () << 0.004218f, 0.175000f, 0.973370f; cloud.points[ 6].getVector3fMap () << 0.016045f, 0.175000f, 0.977916f; cloud.points[ 7].getVector3fMap () << -0.025420f, 0.200000f, 0.974580f; cloud.points[ 8].getVector3fMap () << 0.025420f, 0.200000f, 0.974580f; cloud.points[ 9].getVector3fMap () << -0.012710f, 0.150000f, 0.987290f; cloud.points[10].getVector3fMap () << -0.005624f, 0.150000f, 0.982692f; cloud.points[11].getVector3fMap () << 0.002812f, 0.150000f, 0.982247f; cloud.points[12].getVector3fMap () << 0.012710f, 0.150000f, 0.987290f; cloud.points[13].getVector3fMap () << -0.022084f, 0.175000f, 0.983955f; cloud.points[14].getVector3fMap () << 0.022084f, 0.175000f, 0.983955f; cloud.points[15].getVector3fMap () << -0.034616f, 0.200000f, 0.988753f; cloud.points[16].getVector3fMap () << 0.034616f, 0.200000f, 0.988753f; cloud.points[17].getVector3fMap () << -0.006044f, 0.125000f, 0.993956f; cloud.points[18].getVector3fMap () << 0.004835f, 0.125000f, 0.993345f; cloud.points[19].getVector3fMap () << -0.017308f, 0.150000f, 0.994376f; cloud.points[20].getVector3fMap () << 0.017308f, 0.150000f, 0.994376f; cloud.points[21].getVector3fMap () << -0.025962f, 0.175000f, 0.991565f; cloud.points[22].getVector3fMap () << 0.025962f, 0.175000f, 0.991565f; cloud.points[23].getVector3fMap () << -0.009099f, 0.125000f, 1.000000f; cloud.points[24].getVector3fMap () << 0.009099f, 0.125000f, 1.000000f; cloud.points[25].getVector3fMap () << -0.018199f, 0.150000f, 1.000000f; cloud.points[26].getVector3fMap () << 0.018199f, 0.150000f, 1.000000f; cloud.points[27].getVector3fMap () << -0.027298f, 0.175000f, 1.000000f; cloud.points[28].getVector3fMap () << 0.027298f, 0.175000f, 1.000000f; cloud.points[29].getVector3fMap () << -0.036397f, 0.200000f, 1.000000f; cloud.points[30].getVector3fMap () << 0.036397f, 0.200000f, 1.000000f; normals.points[ 0].getNormalVector3fMap () << -0.290381f, -0.342020f, -0.893701f; normals.points[ 1].getNormalVector3fMap () << 0.000000f, -0.342020f, -0.939693f; normals.points[ 2].getNormalVector3fMap () << 0.290381f, -0.342020f, -0.893701f; normals.points[ 3].getNormalVector3fMap () << -0.552338f, -0.342020f, -0.760227f; normals.points[ 4].getNormalVector3fMap () << -0.290381f, -0.342020f, -0.893701f; normals.points[ 5].getNormalVector3fMap () << 0.145191f, -0.342020f, -0.916697f; normals.points[ 6].getNormalVector3fMap () << 0.552337f, -0.342020f, -0.760227f; normals.points[ 7].getNormalVector3fMap () << -0.656282f, -0.342020f, -0.656283f; normals.points[ 8].getNormalVector3fMap () << 0.656282f, -0.342020f, -0.656283f; normals.points[ 9].getNormalVector3fMap () << -0.656283f, -0.342020f, -0.656282f; normals.points[10].getNormalVector3fMap () << -0.290381f, -0.342020f, -0.893701f; normals.points[11].getNormalVector3fMap () << 0.145191f, -0.342020f, -0.916697f; normals.points[12].getNormalVector3fMap () << 0.656282f, -0.342020f, -0.656282f; normals.points[13].getNormalVector3fMap () << -0.760228f, -0.342020f, -0.552337f; normals.points[14].getNormalVector3fMap () << 0.760228f, -0.342020f, -0.552337f; normals.points[15].getNormalVector3fMap () << -0.893701f, -0.342020f, -0.290380f; normals.points[16].getNormalVector3fMap () << 0.893701f, -0.342020f, -0.290380f; normals.points[17].getNormalVector3fMap () << -0.624162f, -0.342020f, -0.624162f; normals.points[18].getNormalVector3fMap () << 0.499329f, -0.342020f, -0.687268f; normals.points[19].getNormalVector3fMap () << -0.893701f, -0.342020f, -0.290380f; normals.points[20].getNormalVector3fMap () << 0.893701f, -0.342020f, -0.290380f; normals.points[21].getNormalVector3fMap () << -0.893701f, -0.342020f, -0.290381f; normals.points[22].getNormalVector3fMap () << 0.893701f, -0.342020f, -0.290381f; normals.points[23].getNormalVector3fMap () << -0.939693f, -0.342020f, 0.000000f; normals.points[24].getNormalVector3fMap () << 0.939693f, -0.342020f, 0.000000f; normals.points[25].getNormalVector3fMap () << -0.939693f, -0.342020f, 0.000000f; normals.points[26].getNormalVector3fMap () << 0.939693f, -0.342020f, 0.000000f; normals.points[27].getNormalVector3fMap () << -0.939693f, -0.342020f, 0.000000f; normals.points[28].getNormalVector3fMap () << 0.939693f, -0.342020f, 0.000000f; normals.points[29].getNormalVector3fMap () << -0.939693f, -0.342020f, 0.000000f; normals.points[30].getNormalVector3fMap () << 0.939693f, -0.342020f, 0.000000f; // Create a shared cylinder model pointer directly SampleConsensusModelConePtr model (new SampleConsensusModelCone (cloud.makeShared ())); model->setInputNormals (normals.makeShared ()); // Create the RANSAC object RandomSampleConsensus sac (model, 0.03); // Algorithm tests bool result = sac.computeModel (); ASSERT_TRUE (result); std::vector sample; sac.getModel (sample); EXPECT_EQ (3, sample.size ()); std::vector inliers; sac.getInliers (inliers); EXPECT_EQ (31, inliers.size ()); Eigen::VectorXf coeff; sac.getModelCoefficients (coeff); EXPECT_EQ (7, coeff.size ()); EXPECT_NEAR (0, coeff[0], 1e-2); EXPECT_NEAR (0.1, coeff[1], 1e-2); EXPECT_NEAR (0.349066, coeff[6], 1e-2); Eigen::VectorXf coeff_refined; model->optimizeModelCoefficients (inliers, coeff, coeff_refined); EXPECT_EQ (7, coeff_refined.size ()); EXPECT_NEAR (0.349066 , coeff_refined[6], 1e-2); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RANSAC, SampleConsensusModelCylinder) { srand (0); // Use a custom point cloud for these tests until we need something better PointCloud cloud; PointCloud normals; cloud.points.resize (20); normals.points.resize (20); cloud.points[ 0].getVector3fMap () << -0.499902f, 2.199701f, 0.000008f; cloud.points[ 1].getVector3fMap () << -0.875397f, 2.030177f, 0.050104f; cloud.points[ 2].getVector3fMap () << -0.995875f, 1.635973f, 0.099846f; cloud.points[ 3].getVector3fMap () << -0.779523f, 1.285527f, 0.149961f; cloud.points[ 4].getVector3fMap () << -0.373285f, 1.216488f, 0.199959f; cloud.points[ 5].getVector3fMap () << -0.052893f, 1.475973f, 0.250101f; cloud.points[ 6].getVector3fMap () << -0.036558f, 1.887591f, 0.299839f; cloud.points[ 7].getVector3fMap () << -0.335048f, 2.171994f, 0.350001f; cloud.points[ 8].getVector3fMap () << -0.745456f, 2.135528f, 0.400072f; cloud.points[ 9].getVector3fMap () << -0.989282f, 1.803311f, 0.449983f; cloud.points[10].getVector3fMap () << -0.900651f, 1.400701f, 0.500126f; cloud.points[11].getVector3fMap () << -0.539658f, 1.201468f, 0.550079f; cloud.points[12].getVector3fMap () << -0.151875f, 1.340951f, 0.599983f; cloud.points[13].getVector3fMap () << -0.000724f, 1.724373f, 0.649882f; cloud.points[14].getVector3fMap () << -0.188573f, 2.090983f, 0.699854f; cloud.points[15].getVector3fMap () << -0.587925f, 2.192257f, 0.749956f; cloud.points[16].getVector3fMap () << -0.927724f, 1.958846f, 0.800008f; cloud.points[17].getVector3fMap () << -0.976888f, 1.549655f, 0.849970f; cloud.points[18].getVector3fMap () << -0.702003f, 1.242707f, 0.899954f; cloud.points[19].getVector3fMap () << -0.289916f, 1.246296f, 0.950075f; normals.points[ 0].getNormalVector3fMap () << 0.000098f, 1.000098f, 0.000008f; normals.points[ 1].getNormalVector3fMap () << -0.750891f, 0.660413f, 0.000104f; normals.points[ 2].getNormalVector3fMap () << -0.991765f, -0.127949f, -0.000154f; normals.points[ 3].getNormalVector3fMap () << -0.558918f, -0.829439f, -0.000039f; normals.points[ 4].getNormalVector3fMap () << 0.253627f, -0.967447f, -0.000041f; normals.points[ 5].getNormalVector3fMap () << 0.894105f, -0.447965f, 0.000101f; normals.points[ 6].getNormalVector3fMap () << 0.926852f, 0.375543f, -0.000161f; normals.points[ 7].getNormalVector3fMap () << 0.329948f, 0.943941f, 0.000001f; normals.points[ 8].getNormalVector3fMap () << -0.490966f, 0.871203f, 0.000072f; normals.points[ 9].getNormalVector3fMap () << -0.978507f, 0.206425f, -0.000017f; normals.points[10].getNormalVector3fMap () << -0.801227f, -0.598534f, 0.000126f; normals.points[11].getNormalVector3fMap () << -0.079447f, -0.996697f, 0.000079f; normals.points[12].getNormalVector3fMap () << 0.696154f, -0.717889f, -0.000017f; normals.points[13].getNormalVector3fMap () << 0.998685f, 0.048502f, -0.000118f; normals.points[14].getNormalVector3fMap () << 0.622933f, 0.782133f, -0.000146f; normals.points[15].getNormalVector3fMap () << -0.175948f, 0.984480f, -0.000044f; normals.points[16].getNormalVector3fMap () << -0.855476f, 0.517824f, 0.000008f; normals.points[17].getNormalVector3fMap () << -0.953769f, -0.300571f, -0.000030f; normals.points[18].getNormalVector3fMap () << -0.404035f, -0.914700f, -0.000046f; normals.points[19].getNormalVector3fMap () << 0.420154f, -0.907445f, 0.000075f; // Create a shared cylinder model pointer directly SampleConsensusModelCylinderPtr model (new SampleConsensusModelCylinder (cloud.makeShared ())); model->setInputNormals (normals.makeShared ()); // Create the RANSAC object RandomSampleConsensus sac (model, 0.03); // Algorithm tests bool result = sac.computeModel (); ASSERT_TRUE (result); std::vector sample; sac.getModel (sample); EXPECT_EQ (2, sample.size ()); std::vector inliers; sac.getInliers (inliers); EXPECT_EQ (20, inliers.size ()); Eigen::VectorXf coeff; sac.getModelCoefficients (coeff); EXPECT_EQ (7, coeff.size ()); EXPECT_NEAR (-0.5, coeff[0], 1e-3); EXPECT_NEAR (1.7, coeff[1], 1e-3); EXPECT_NEAR (0.5, coeff[6], 1e-3); Eigen::VectorXf coeff_refined; model->optimizeModelCoefficients (inliers, coeff, coeff_refined); EXPECT_EQ (7, coeff_refined.size ()); EXPECT_NEAR (0.5, coeff_refined[6], 1e-3); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RANSAC, SampleConsensusModelCircle2D) { srand (0); // Use a custom point cloud for these tests until we need something better PointCloud cloud; cloud.points.resize (18); cloud.points[ 0].getVector3fMap () << 3.587751f, -4.190982f, 0.0f; cloud.points[ 1].getVector3fMap () << 3.808883f, -4.412265f, 0.0f; cloud.points[ 2].getVector3fMap () << 3.587525f, -5.809143f, 0.0f; cloud.points[ 3].getVector3fMap () << 2.999913f, -5.999980f, 0.0f; cloud.points[ 4].getVector3fMap () << 2.412224f, -5.809090f, 0.0f; cloud.points[ 5].getVector3fMap () << 2.191080f, -5.587682f, 0.0f; cloud.points[ 6].getVector3fMap () << 2.048941f, -5.309003f, 0.0f; cloud.points[ 7].getVector3fMap () << 2.000397f, -4.999944f, 0.0f; cloud.points[ 8].getVector3fMap () << 2.999953f, -6.000056f, 0.0f; cloud.points[ 9].getVector3fMap () << 2.691127f, -5.951136f, 0.0f; cloud.points[10].getVector3fMap () << 2.190892f, -5.587838f, 0.0f; cloud.points[11].getVector3fMap () << 2.048874f, -5.309052f, 0.0f; cloud.points[12].getVector3fMap () << 1.999990f, -5.000147f, 0.0f; cloud.points[13].getVector3fMap () << 2.049026f, -4.690918f, 0.0f; cloud.points[14].getVector3fMap () << 2.190956f, -4.412162f, 0.0f; cloud.points[15].getVector3fMap () << 2.412231f, -4.190918f, 0.0f; cloud.points[16].getVector3fMap () << 2.691027f, -4.049060f, 0.0f; cloud.points[17].getVector3fMap () << 2.000000f, -3.000000f, 0.0f; // Create a shared 2d circle model pointer directly SampleConsensusModelCircle2DPtr model (new SampleConsensusModelCircle2D (cloud.makeShared ())); // Create the RANSAC object RandomSampleConsensus sac (model, 0.03); // Algorithm tests bool result = sac.computeModel (); ASSERT_TRUE (result); std::vector sample; sac.getModel (sample); EXPECT_EQ (3, sample.size ()); std::vector inliers; sac.getInliers (inliers); EXPECT_EQ (17, inliers.size ()); Eigen::VectorXf coeff; sac.getModelCoefficients (coeff); EXPECT_EQ (3, coeff.size ()); EXPECT_NEAR (3, coeff[0], 1e-3); EXPECT_NEAR (-5, coeff[1], 1e-3); EXPECT_NEAR (1, coeff[2], 1e-3); Eigen::VectorXf coeff_refined; model->optimizeModelCoefficients (inliers, coeff, coeff_refined); EXPECT_EQ (3, coeff_refined.size ()); EXPECT_NEAR (3, coeff_refined[0], 1e-3); EXPECT_NEAR (-5, coeff_refined[1], 1e-3); EXPECT_NEAR (1, coeff_refined[2], 1e-3); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RANSAC, SampleConsensusModelCircle3D) { srand (0); // Use a custom point cloud for these tests until we need something better PointCloud cloud; cloud.points.resize (20); cloud.points[ 0].getVector3fMap () << 1.00000000f, 5.0000000f, -2.9000001f; cloud.points[ 1].getVector3fMap () << 1.03420200f, 5.0000000f, -2.9060307f; cloud.points[ 2].getVector3fMap () << 1.06427870f, 5.0000000f, -2.9233956f; cloud.points[ 3].getVector3fMap () << 1.08660260f, 5.0000000f, -2.9500000f; cloud.points[ 4].getVector3fMap () << 1.09848080f, 5.0000000f, -2.9826353f; cloud.points[ 5].getVector3fMap () << 1.09848080f, 5.0000000f, -3.0173647f; cloud.points[ 6].getVector3fMap () << 1.08660260f, 5.0000000f, -3.0500000f; cloud.points[ 7].getVector3fMap () << 1.06427870f, 5.0000000f, -3.0766044f; cloud.points[ 8].getVector3fMap () << 1.03420200f, 5.0000000f, -3.0939693f; cloud.points[ 9].getVector3fMap () << 1.00000000f, 5.0000000f, -3.0999999f; cloud.points[10].getVector3fMap () << 0.96579796f, 5.0000000f, -3.0939693f; cloud.points[11].getVector3fMap () << 0.93572122f, 5.0000000f, -3.0766044f; cloud.points[12].getVector3fMap () << 0.91339743f, 5.0000000f, -3.0500000f; cloud.points[13].getVector3fMap () << 0.90151924f, 5.0000000f, -3.0173647f; cloud.points[14].getVector3fMap () << 0.90151924f, 5.0000000f, -2.9826353f; cloud.points[15].getVector3fMap () << 0.91339743f, 5.0000000f, -2.9500000f; cloud.points[16].getVector3fMap () << 0.93572122f, 5.0000000f, -2.9233956f; cloud.points[17].getVector3fMap () << 0.96579796f, 5.0000000f, -2.9060307f; cloud.points[18].getVector3fMap () << 0.85000002f, 4.8499999f, -3.1500001f; cloud.points[19].getVector3fMap () << 1.15000000f, 5.1500001f, -2.8499999f; // Create a shared 3d circle model pointer directly SampleConsensusModelCircle3DPtr model (new SampleConsensusModelCircle3D (cloud.makeShared ())); // Create the RANSAC object RandomSampleConsensus sac (model, 0.03); // Algorithm tests bool result = sac.computeModel (); ASSERT_TRUE (result); std::vector sample; sac.getModel (sample); EXPECT_EQ (3, sample.size ()); std::vector inliers; sac.getInliers (inliers); EXPECT_EQ (18, inliers.size ()); Eigen::VectorXf coeff; sac.getModelCoefficients (coeff); EXPECT_EQ (7, coeff.size ()); EXPECT_NEAR (1, coeff[0], 1e-3); EXPECT_NEAR (5, coeff[1], 1e-3); EXPECT_NEAR (-3, coeff[2], 1e-3); EXPECT_NEAR (0.1,coeff[3], 1e-3); EXPECT_NEAR (0, coeff[4], 1e-3); EXPECT_NEAR (-1, coeff[5], 1e-3); EXPECT_NEAR (0, coeff[6], 1e-3); Eigen::VectorXf coeff_refined; model->optimizeModelCoefficients (inliers, coeff, coeff_refined); EXPECT_EQ (7, coeff_refined.size ()); EXPECT_NEAR (1, coeff_refined[0], 1e-3); EXPECT_NEAR (5, coeff_refined[1], 1e-3); EXPECT_NEAR (-3, coeff_refined[2], 1e-3); EXPECT_NEAR (0.1,coeff_refined[3], 1e-3); EXPECT_NEAR (0, coeff_refined[4], 1e-3); EXPECT_NEAR (-1, coeff_refined[5], 1e-3); EXPECT_NEAR (0, coeff_refined[6], 1e-3); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RANSAC, SampleConsensusModelLine) { srand (0); // Use a custom point cloud for these tests until we need something better PointCloud 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 (cloud.makeShared ())); // Create the RANSAC object RandomSampleConsensus sac (model, 0.001); // Algorithm tests bool result = sac.computeModel (); ASSERT_TRUE (result); std::vector sample; sac.getModel (sample); EXPECT_EQ (2, sample.size ()); std::vector 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 proj_points; model->projectPoints (inliers, coeff_refined, proj_points); EXPECT_NEAR (7.0, proj_points.points[2].x, 1e-4); EXPECT_NEAR (8.0, proj_points.points[2].y, 1e-4); EXPECT_NEAR (9.0, proj_points.points[2].z, 1e-4); EXPECT_NEAR (10.0, proj_points.points[3].x, 1e-4); EXPECT_NEAR (11.0, proj_points.points[3].y, 1e-4); EXPECT_NEAR (12.0, proj_points.points[3].z, 1e-4); EXPECT_NEAR (16.0, proj_points.points[5].x, 1e-4); EXPECT_NEAR (17.0, proj_points.points[5].y, 1e-4); EXPECT_NEAR (18.0, proj_points.points[5].z, 1e-4); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TEST (RANSAC, SampleConsensusModelNormalPlane) { srand (0); // Create a shared plane model pointer directly SampleConsensusModelNormalPlanePtr model (new SampleConsensusModelNormalPlane (cloud_)); model->setInputNormals (normals_); model->setNormalDistanceWeight (0.01); // Create the RANSAC object RandomSampleConsensus sac (model, 0.03); verifyPlaneSac (model, sac); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Test if RANSAC finishes within a second. TEST (SAC, InfiniteLoop) { const unsigned point_count = 100; PointCloud cloud; cloud.points.resize (point_count); for (unsigned pIdx = 0; pIdx < point_count; ++pIdx) { cloud.points[pIdx].x = static_cast (pIdx); cloud.points[pIdx].y = 0.0; cloud.points[pIdx].z = 0.0; } boost::posix_time::time_duration delay(0,0,1,0); boost::function sac_function; SampleConsensusModelSpherePtr model (new SampleConsensusModelSphere (cloud.makeShared ())); // Create the RANSAC object RandomSampleConsensus ransac (model, 0.03); sac_function = boost::bind (&RandomSampleConsensus::computeModel, &ransac, 0); boost::thread thread1 (sac_function); ASSERT_TRUE(thread1.timed_join(delay)); // Create the LMSAC object LeastMedianSquares lmsac (model, 0.03); sac_function = boost::bind (&LeastMedianSquares::computeModel, &lmsac, 0); boost::thread thread2 (sac_function); ASSERT_TRUE(thread2.timed_join(delay)); // Create the MSAC object MEstimatorSampleConsensus mesac (model, 0.03); sac_function = boost::bind (&MEstimatorSampleConsensus::computeModel, &mesac, 0); boost::thread thread3 (sac_function); ASSERT_TRUE(thread3.timed_join(delay)); // Create the RRSAC object RandomizedRandomSampleConsensus rrsac (model, 0.03); sac_function = boost::bind (&RandomizedRandomSampleConsensus::computeModel, &rrsac, 0); boost::thread thread4 (sac_function); ASSERT_TRUE(thread4.timed_join(delay)); // Create the RMSAC object RandomizedMEstimatorSampleConsensus rmsac (model, 0.03); sac_function = boost::bind (&RandomizedMEstimatorSampleConsensus::computeModel, &rmsac, 0); boost::thread thread5 (sac_function); ASSERT_TRUE(thread5.timed_join(delay)); // Create the MLESAC object MaximumLikelihoodSampleConsensus mlesac (model, 0.03); sac_function = boost::bind (&MaximumLikelihoodSampleConsensus::computeModel, &mlesac, 0); boost::thread thread6 (sac_function); ASSERT_TRUE(thread6.timed_join(delay)); } /* ---[ */ int main (int argc, char** argv) { if (argc < 2) { std::cerr << "No test file given. Please download `sac_plane_test.pcd` and pass its path to the test." << std::endl; return (-1); } // Load a standard PCD file from disk pcl::PCLPointCloud2 cloud_blob; if (loadPCDFile (argv[1], cloud_blob) < 0) { std::cerr << "Failed to read test file. Please download `sac_plane_test.pcd` and pass its path to the test." << std::endl; return (-1); } fromPCLPointCloud2 (cloud_blob, *cloud_); indices_.resize (cloud_->points.size ()); for (size_t i = 0; i < indices_.size (); ++i) { indices_[i] = int (i); } // Estimate surface normals NormalEstimation n; search::Search::Ptr tree (new search::KdTree); tree->setInputCloud (cloud_); n.setInputCloud (cloud_); boost::shared_ptr > indicesptr (new vector (indices_)); n.setIndices (indicesptr); n.setSearchMethod (tree); n.setRadiusSearch (0.02); // Use 2cm radius to estimate the normals n.compute (*normals_); testing::InitGoogleTest (&argc, argv); return (RUN_ALL_TESTS ()); } /* ]--- */