DataFilters.cpp
Go to the documentation of this file.
00001 #include "../utest.h"
00002 #include <ciso646>
00003 #include <cmath>
00004 
00005 using namespace std;
00006 using namespace PointMatcherSupport;
00007 
00008 //---------------------------
00009 // DataFilter modules
00010 //---------------------------
00011 
00012 // Utility classes
00013 class DataFilterTest: public IcpHelper
00014 {
00015 public:
00016         // Will be called for every tests
00017         virtual void SetUp()
00018         {
00019                 icp.setDefault();
00020                 // Uncomment for console outputs
00021                 //setLogger(PM::get().LoggerRegistrar.create("FileLogger"));
00022                 
00023                 // We'll test the filters on reading point cloud
00024                 icp.readingDataPointsFilters.clear();
00025         }
00026 
00027         // Will be called for every tests
00028         virtual void TearDown() {}
00029 
00030         void addFilter(string name, PM::Parameters params)
00031         {
00032                 std::shared_ptr<PM::DataPointsFilter> testedDataPointFilter =
00033                         PM::get().DataPointsFilterRegistrar.create(name, params);
00034         
00035                 icp.readingDataPointsFilters.push_back(testedDataPointFilter);
00036         }
00037         
00038         void addFilter(string name)
00039         {
00040                 std::shared_ptr<PM::DataPointsFilter> testedDataPointFilter =
00041                         PM::get().DataPointsFilterRegistrar.create(name);
00042                 
00043                 icp.readingDataPointsFilters.push_back(testedDataPointFilter);
00044         }
00045 
00046         DP generateRandomDataPoints(int nbPoints = 100)
00047         {
00048                 const int dimFeatures = 4;
00049                 const int dimDescriptors = 3;
00050                 const int dimTime = 2;
00051 
00052                 PM::Matrix randFeat = PM::Matrix::Random(dimFeatures, nbPoints);
00053                 DP::Labels featLabels;
00054                 featLabels.push_back(DP::Label("x", 1));
00055                 featLabels.push_back(DP::Label("y", 1));
00056                 featLabels.push_back(DP::Label("z", 1));
00057                 featLabels.push_back(DP::Label("pad", 1));
00058 
00059                 PM::Matrix randDesc = PM::Matrix::Random(dimDescriptors, nbPoints);
00060                 DP::Labels descLabels;
00061                 descLabels.push_back(DP::Label("dummyDesc", 3));
00062 
00063                 PM::Int64Matrix randTimes = PM::Int64Matrix::Random(dimTime, nbPoints);
00064                 DP::Labels timeLabels;
00065                 timeLabels.push_back(DP::Label("dummyTime", 2));
00066 
00067                 // Construct the point cloud from the generated matrices
00068                 DP pointCloud = DP(randFeat, featLabels, randDesc, descLabels, randTimes, timeLabels);
00069 
00070                 return pointCloud;
00071         }
00072 };
00073 
00074 TEST_F(DataFilterTest, IdentityDataPointsFilter)
00075 {
00076         // build test cloud
00077         DP ref2DCopy(ref2D);
00078         
00079         // apply and checked
00080         addFilter("IdentityDataPointsFilter");
00081         icp.readingDataPointsFilters.apply(ref2DCopy);
00082         EXPECT_TRUE(ref2D == ref2DCopy);
00083 }
00084 
00085 TEST_F(DataFilterTest, RemoveNaNDataPointsFilter)
00086 {
00087         // build test cloud
00088         DP ref2DCopy(ref2D);
00089         int goodCount(0);
00090         const float nan(std::numeric_limits<float>::quiet_NaN());
00091         for (int i(0); i < ref2DCopy.features.cols(); ++i)
00092         {
00093                 if (rand() % 3 == 0)
00094                 {
00095                         ref2DCopy.features(rand() % ref2DCopy.features.rows(), i) = nan;
00096                 }
00097                 else
00098                         ++goodCount;
00099         }
00100         
00101         // apply and checked
00102         addFilter("RemoveNaNDataPointsFilter");
00103         icp.readingDataPointsFilters.apply(ref2DCopy);
00104         EXPECT_TRUE(ref2DCopy.features.cols() == goodCount);
00105 }
00106 
00107 TEST_F(DataFilterTest, MaxDistDataPointsFilter)
00108 {
00109         // Max dist has been selected to not affect the points
00110         params = PM::Parameters();
00111         params["dim"] = "0";
00112         params["maxDist"] = toParam(6.0);
00113         
00114         // Filter on x axis
00115         params["dim"] = "0";
00116         icp.readingDataPointsFilters.clear();
00117         addFilter("MaxDistDataPointsFilter", params);
00118         validate2dTransformation();
00119         validate3dTransformation();
00120         
00121         // Filter on y axis
00122         params["dim"] = "1";
00123         icp.readingDataPointsFilters.clear();
00124         addFilter("MaxDistDataPointsFilter", params);
00125         validate2dTransformation();
00126         validate3dTransformation();
00127         
00128         // Filter on z axis (not existing)
00129         params["dim"] = "2";
00130         icp.readingDataPointsFilters.clear();
00131         addFilter("MaxDistDataPointsFilter", params);
00132         EXPECT_ANY_THROW(validate2dTransformation());
00133         validate3dTransformation();
00134         
00135         // Filter on a radius
00136         params["dim"] = "-1";
00137         icp.readingDataPointsFilters.clear();
00138         addFilter("MaxDistDataPointsFilter", params);
00139         validate2dTransformation();
00140         validate3dTransformation();
00141         
00142         // Parameter outside valid range
00143         params["dim"] = "3";
00144         //TODO: specify the exception, move that to GenericTest
00145         EXPECT_ANY_THROW(addFilter("MaxDistDataPointsFilter", params));
00146         
00147 }
00148 
00149 TEST_F(DataFilterTest, MinDistDataPointsFilter)
00150 {
00151         // Min dist has been selected to not affect the points too much
00152         params = PM::Parameters();
00153         params["dim"] = "0";
00154         params["minDist"] = toParam(0.05);
00155         
00156         // Filter on x axis
00157         params["dim"] = "0";
00158         icp.readingDataPointsFilters.clear();
00159         addFilter("MinDistDataPointsFilter", params);
00160         validate2dTransformation();
00161         validate3dTransformation();
00162         
00163         // Filter on y axis
00164         params["dim"] = "1";
00165         icp.readingDataPointsFilters.clear();
00166         addFilter("MinDistDataPointsFilter", params);
00167         validate2dTransformation();
00168         validate3dTransformation();
00169         
00170         //TODO: move that to specific 2D test
00171         // Filter on z axis (not existing)
00172         params["dim"] = "2";
00173         icp.readingDataPointsFilters.clear();
00174         addFilter("MinDistDataPointsFilter", params);
00175         EXPECT_ANY_THROW(validate2dTransformation());
00176         validate3dTransformation();
00177         
00178         // Filter on a radius
00179         params["dim"] = "-1";
00180         icp.readingDataPointsFilters.clear();
00181         addFilter("MinDistDataPointsFilter", params);
00182         validate2dTransformation();
00183         validate3dTransformation();
00184                 
00185 }
00186 
00187 TEST_F(DataFilterTest, MaxQuantileOnAxisDataPointsFilter)
00188 {
00189         // Ratio has been selected to not affect the points too much
00190         string ratio = "0.95";
00191         params = PM::Parameters();
00192         params["dim"] = "0";
00193         params["ratio"] = ratio;
00194         
00195         // Filter on x axis
00196         params["dim"] = "0";
00197         icp.readingDataPointsFilters.clear();
00198         addFilter("MaxQuantileOnAxisDataPointsFilter", params);
00199         validate2dTransformation();
00200         validate3dTransformation();
00201         
00202         // Filter on y axis
00203         params["dim"] = "1";
00204         icp.readingDataPointsFilters.clear();
00205         addFilter("MaxQuantileOnAxisDataPointsFilter", params);
00206         validate2dTransformation();
00207         validate3dTransformation();
00208         
00209         // Filter on z axis (not existing)
00210         params["dim"] = "2";
00211         icp.readingDataPointsFilters.clear();
00212         addFilter("MaxQuantileOnAxisDataPointsFilter", params);
00213         EXPECT_ANY_THROW(validate2dTransformation());
00214         validate3dTransformation();
00215 }
00216 
00217 
00218 
00219 TEST_F(DataFilterTest, SurfaceNormalDataPointsFilter)
00220 {
00221         // This filter create descriptor, so parameters should'nt impact results
00222         params = PM::Parameters();
00223         params["knn"] =  "5"; 
00224         params["epsilon"] =  "0.1"; 
00225         params["keepNormals"] =  "1";
00226         params["keepDensities"] =  "1";
00227         params["keepEigenValues"] =  "1";
00228         params["keepEigenVectors"] =  "1" ;
00229         params["keepMatchedIds"] =  "1" ;
00230         // FIXME: the parameter keepMatchedIds seems to do nothing...
00231 
00232         addFilter("SurfaceNormalDataPointsFilter", params);
00233         validate2dTransformation();     
00234         validate3dTransformation();
00235 
00236         // TODO: standardize how filter are tested:
00237         // 1- impact on number of points
00238         // 2- impact on descriptors
00239         // 3- impact on ICP (that's what we test now)
00240 }
00241 
00242 TEST_F(DataFilterTest, MaxDensityDataPointsFilter)
00243 {
00244         // Ratio has been selected to not affect the points too much
00245         vector<double> ratio = {100, 1000, 5000};
00246  
00247         for(unsigned i=0; i < ratio.size(); i++)
00248         {
00249                 icp.readingDataPointsFilters.clear();
00250                 params = PM::Parameters();
00251                 params["knn"] = "5"; 
00252                 params["epsilon"] = "0.1"; 
00253                 params["keepNormals"] = "0";
00254                 params["keepDensities"] = "1";
00255                 params["keepEigenValues"] = "0";
00256                 params["keepEigenVectors"] = "0" ;
00257                 params["keepMatchedIds"] = "0" ;
00258 
00259                 addFilter("SurfaceNormalDataPointsFilter", params);
00260 
00261                 params = PM::Parameters();
00262                 params["maxDensity"] = toParam(ratio[i]);
00263 
00264                 addFilter("MaxDensityDataPointsFilter", params);
00265                 
00266                 // FIXME BUG: the density in 2D is not well computed
00267                 //validate2dTransformation();   
00268  
00269                 //double nbInitPts = data2D.features.cols();
00270                 //double nbRemainingPts = icp.getPrefilteredReadingPtsCount();
00271                 //EXPECT_TRUE(nbRemainingPts < nbInitPts);
00272                 
00273                 validate3dTransformation();
00274 
00275                 double nbInitPts = data3D.features.cols();
00276                 double nbRemainingPts = icp.getPrefilteredReadingPtsCount();
00277                 EXPECT_TRUE(nbRemainingPts < nbInitPts);
00278         }
00279 }
00280 
00281 TEST_F(DataFilterTest, SamplingSurfaceNormalDataPointsFilter)
00282 {
00283         // This filter create descriptor AND subsample
00284         params = PM::Parameters();
00285         params["knn"] = "5";
00286         params["averageExistingDescriptors"] = "1";
00287         params["keepNormals"] = "1";
00288         params["keepDensities"] = "1";
00289         params["keepEigenValues"] = "1";
00290         params["keepEigenVectors"] = "1";
00291         
00292         addFilter("SamplingSurfaceNormalDataPointsFilter", params);
00293         validate2dTransformation();
00294         validate3dTransformation();
00295 
00296 }
00297 
00298 //TODO: this filter is broken, fix it!
00299 /*
00300 TEST_F(DataFilterTest, ElipsoidsDataPointsFilter)
00301 {
00302         // This filter creates descriptor AND subsamples
00303         params = PM::Parameters();
00304         params["knn"] = "5";
00305         params["averageExistingDescriptors"] = "1";
00306         params["keepNormals"] = "1";
00307         params["keepDensities"] = "1";
00308         params["keepEigenValues"] = "1";
00309         params["keepEigenVectors"] = "1";
00310         params["maxBoxDim"] = "inf";
00311         params["maxTimeWindow"] = "10";
00312         params["minPlanarity"] = "0";
00313         params["keepMeans"] = "1";
00314         params["keepCovariances"] = "1";
00315         params["keepWeights"] = "1";
00316         params["keepShapes"] = "1";
00317         params["keepIndices"] = "1";
00318 
00319         addFilter("ElipsoidsDataPointsFilter", params);
00320         validate2dTransformation();
00321         validate3dTransformation();
00322 }
00323 */
00324 
00325 TEST_F(DataFilterTest, GestaltDataPointsFilter)
00326 {
00327         // This filter creates descriptor AND subsamples
00328         params = PM::Parameters();
00329         params["knn"] = "5";
00330         params["averageExistingDescriptors"] = "1";
00331         params["keepNormals"] = "1";
00332         params["keepEigenValues"] = "1";
00333         params["keepEigenVectors"] = "1";
00334         params["maxBoxDim"] = "1";
00335         params["keepMeans"] = "1";
00336         params["keepCovariances"] = "1";
00337         params["keepGestaltFeatures"] = "1";
00338         params["radius"] = "1";
00339         params["ratio"] = "0.5";
00340 
00341         addFilter("GestaltDataPointsFilter", params);
00342         validate3dTransformation();
00343 }
00344 
00345 TEST_F(DataFilterTest, OrientNormalsDataPointsFilter)
00346 {
00347         // Used to create normal for reading point cloud
00348         std::shared_ptr<PM::DataPointsFilter> extraDataPointFilter;
00349         extraDataPointFilter = PM::get().DataPointsFilterRegistrar.create(
00350                         "SurfaceNormalDataPointsFilter");
00351         icp.readingDataPointsFilters.push_back(extraDataPointFilter);
00352         addFilter("ObservationDirectionDataPointsFilter");
00353         addFilter("OrientNormalsDataPointsFilter", {
00354                 {"towardCenter", toParam(false)}
00355         }
00356         );
00357         validate2dTransformation();
00358         validate3dTransformation();
00359 }
00360 
00361 
00362 TEST_F(DataFilterTest, RandomSamplingDataPointsFilter)
00363 {
00364         vector<double> prob = {0.80, 0.85, 0.90, 0.95};
00365         for(unsigned i = 0; i < prob.size(); i++)
00366         {
00367                 // Try to avoid to low value for the reduction to avoid under sampling
00368                 params = PM::Parameters();
00369                 params["prob"] = toParam(prob[i]);
00370 
00371                 icp.readingDataPointsFilters.clear();
00372                 addFilter("RandomSamplingDataPointsFilter", params);
00373                 validate2dTransformation();
00374                 validate3dTransformation();
00375         }
00376 }
00377 
00378 TEST_F(DataFilterTest, FixStepSamplingDataPointsFilter)
00379 {
00380         vector<unsigned> steps = {1, 2, 3};
00381         for(unsigned i=0; i<steps.size(); i++)
00382         {
00383                 // Try to avoid too low value for the reduction to avoid under sampling
00384                 params = PM::Parameters();
00385                 params["startStep"] = toParam(steps[i]);
00386 
00387                 icp.readingDataPointsFilters.clear();
00388                 addFilter("FixStepSamplingDataPointsFilter", params);
00389                 validate2dTransformation();
00390                 validate3dTransformation();
00391         }
00392 }
00393 
00394 TEST_F(DataFilterTest, MaxPointCountDataPointsFilter)
00395 {
00396         DP cloud = ref3D;
00397         
00398         const size_t maxCount = 1000;
00399                 
00400         params = PM::Parameters(); 
00401         params["seed"] = "42";
00402         params["maxCount"] = toParam(maxCount);
00403         
00404         std::shared_ptr<PM::DataPointsFilter> maxPtsFilter =
00405                         PM::get().DataPointsFilterRegistrar.create("MaxPointCountDataPointsFilter", params);
00406 
00407         DP filteredCloud = maxPtsFilter->filter(cloud);
00408         
00409         //Check number of points
00410         EXPECT_GT(cloud.getNbPoints(), filteredCloud.getNbPoints());
00411         EXPECT_EQ(cloud.getDescriptorDim(), filteredCloud.getDescriptorDim());
00412         EXPECT_EQ(cloud.getTimeDim(), filteredCloud.getTimeDim());
00413         
00414         EXPECT_EQ(filteredCloud.getNbPoints(), maxCount);
00415         
00416         //Same seed should result same filtered cloud
00417         DP filteredCloud2 = maxPtsFilter->filter(cloud);
00418         
00419         EXPECT_TRUE(filteredCloud == filteredCloud2);
00420         
00421         //Different seeds should not result same filtered cloud but same number
00422         params.clear();
00423         params["seed"] = "1";
00424         params["maxCount"] = toParam(maxCount);
00425         
00426         std::shared_ptr<PM::DataPointsFilter> maxPtsFilter2 =
00427                         PM::get().DataPointsFilterRegistrar.create("MaxPointCountDataPointsFilter", params);
00428                         
00429         DP filteredCloud3 = maxPtsFilter2->filter(cloud);
00430         
00431         EXPECT_FALSE(filteredCloud3 == filteredCloud2);
00432         
00433         EXPECT_EQ(filteredCloud3.getNbPoints(), maxCount);
00434         
00435         EXPECT_EQ(filteredCloud3.getNbPoints(), filteredCloud2.getNbPoints());
00436         EXPECT_EQ(filteredCloud3.getDescriptorDim(), filteredCloud2.getDescriptorDim());
00437         EXPECT_EQ(filteredCloud3.getTimeDim(), filteredCloud2.getTimeDim());
00438         
00439         //Validate transformation
00440         icp.readingDataPointsFilters.clear();
00441         addFilter("MaxPointCountDataPointsFilter", params);
00442         validate2dTransformation();
00443         validate3dTransformation();
00444 }
00445 
00446 TEST_F(DataFilterTest, OctreeGridDataPointsFilter)
00447 {
00448         const unsigned int nbPts = 60000;
00449         const DP cloud = generateRandomDataPoints(nbPts);       
00450         params = PM::Parameters(); 
00451 
00452         std::shared_ptr<PM::DataPointsFilter> octreeFilter;
00453         
00454         for(const int meth : {0,1,2,3})
00455                 for(const size_t maxData : {1,5})
00456                         for(const float maxSize : {0.,0.05})
00457                         {
00458                                 params.clear();
00459                                 params["maxPointByNode"] = toParam(maxData);
00460                                 params["maxSizeByNode"] = toParam(maxSize);
00461                                 params["samplingMethod"] = toParam(meth);
00462                                 params["buildParallel"] = "1";
00463         
00464                                 octreeFilter = PM::get().DataPointsFilterRegistrar.create("OctreeGridDataPointsFilter", params);
00465 
00466                                 const DP filteredCloud = octreeFilter->filter(cloud);
00467                         
00468                                 if(maxData==1 and maxSize==0.)
00469                                 {
00470                                         // 1/pts by octants + validate parallel build
00471                                         // the number of point should not change
00472                                         // the sampling methods should not change anything
00473                                         //Check number of points
00474                                         EXPECT_EQ(cloud.getNbPoints(), filteredCloud.getNbPoints());
00475                                         EXPECT_EQ(cloud.getDescriptorDim(), filteredCloud.getDescriptorDim());
00476                                         EXPECT_EQ(cloud.getTimeDim(), filteredCloud.getTimeDim());
00477         
00478                                         EXPECT_EQ(filteredCloud.getNbPoints(), nbPts);
00479                                 }
00480                                 else
00481                                 {       
00482                                         //Check number of points
00483                                         EXPECT_GT(cloud.getNbPoints(), filteredCloud.getNbPoints());
00484                                 }
00485                                 //Validate transformation
00486                                 icp.readingDataPointsFilters.clear();
00487                                 addFilter("OctreeGridDataPointsFilter", params);
00488                                 validate2dTransformation();
00489                                 validate3dTransformation();
00490                         }
00491 }
00492 
00493 TEST_F(DataFilterTest, NormalSpaceDataPointsFilter)
00494 {
00495         const size_t nbPts = 60000;
00496         DP cloud = generateRandomDataPoints(nbPts);     
00497         params = PM::Parameters(); 
00498         
00499         //const size_t nbPts2D = ref2D.getNbPoints();
00500         const size_t nbPts3D = ref3D.getNbPoints();
00501         
00502         std::shared_ptr<PM::DataPointsFilter> nssFilter;
00503         
00504         //Compute normals
00505         auto paramsNorm = PM::Parameters();
00506                         paramsNorm["knn"] = "5"; 
00507                         paramsNorm["epsilon"] = "0.1"; 
00508                         paramsNorm["keepNormals"] = "1";
00509         std::shared_ptr<PM::DataPointsFilter> normalFilter = PM::get().DataPointsFilterRegistrar.create("SurfaceNormalDataPointsFilter", paramsNorm);
00510 
00511         normalFilter->inPlaceFilter(cloud);
00512         
00513         //Evaluate filter
00514         std::vector<size_t> samples = {/* 2*nbPts2D/3, nbPts2D,*/ 1500, 5000, nbPts, nbPts3D};
00515         for(const float epsilon : {M_PI/6., M_PI/32., M_PI/64.})
00516                 for(const size_t nbSample : samples)
00517                 {
00518                         icp.readingDataPointsFilters.clear();
00519                         
00520                         params.clear();
00521                         params["epsilon"] = toParam(epsilon);
00522                         params["nbSample"] = toParam(nbSample);
00523 
00524                         nssFilter = PM::get().DataPointsFilterRegistrar.create("NormalSpaceDataPointsFilter", params);
00525 
00526                         addFilter("SurfaceNormalDataPointsFilter", paramsNorm);
00527                         addFilter("NormalSpaceDataPointsFilter", params);
00528                         
00529                         const DP filteredCloud = nssFilter->filter(cloud);
00530                                         
00531                         /*
00532                         if(nbSample <= nbPts2D)
00533                         {
00534                                 validate2dTransformation();
00535                                 EXPECT_LE(filteredCloud.getNbPoints(), nbPts2D);
00536                                 continue;
00537                         }
00538                         else if (nbSample == nbPts3D)
00539                         {
00540                                 EXPECT_EQ(filteredCloud.getNbPoints(), nbPts3D);
00541                         }
00542                         else */ 
00543                         if (nbSample == nbPts)
00544                         {
00545                                 //Check number of points
00546                                 EXPECT_EQ(cloud.getNbPoints(), filteredCloud.getNbPoints());
00547                                 EXPECT_EQ(cloud.getDescriptorDim(), filteredCloud.getDescriptorDim());
00548                                 EXPECT_EQ(cloud.getTimeDim(), filteredCloud.getTimeDim());
00549 
00550                                 EXPECT_EQ(filteredCloud.getNbPoints(), nbPts);
00551                         }
00552                         
00553                         validate3dTransformation();                     
00554                         EXPECT_GE(cloud.getNbPoints(), filteredCloud.getNbPoints());
00555                 }
00556 }
00557 
00558 TEST_F(DataFilterTest, CovarianceSamplingDataPointsFilter)
00559 {
00560         const size_t nbPts = 60000;
00561         DP cloud = generateRandomDataPoints(nbPts);     
00562         params = PM::Parameters(); 
00563         
00564         const size_t nbPts3D = ref3D.getNbPoints();
00565         
00566         std::shared_ptr<PM::DataPointsFilter> covsFilter;
00567         
00568         //Compute normals
00569         auto paramsNorm = PM::Parameters();
00570                         paramsNorm["knn"] = "5"; 
00571                         paramsNorm["epsilon"] = "0.1"; 
00572                         paramsNorm["keepNormals"] = "1";
00573         std::shared_ptr<PM::DataPointsFilter> normalFilter = PM::get().DataPointsFilterRegistrar.create("SurfaceNormalDataPointsFilter", paramsNorm);
00574 
00575         normalFilter->inPlaceFilter(cloud);
00576         
00577         //Evaluate filter
00578         std::vector<size_t> samples = {500, 1500, 5000, nbPts, nbPts3D};
00579         for(const size_t nbSample : samples)
00580         {
00581                 icp.readingDataPointsFilters.clear();
00582                 
00583                 params.clear();
00584                 params["nbSample"] = toParam(nbSample);
00585 
00586                 covsFilter = PM::get().DataPointsFilterRegistrar.create("CovarianceSamplingDataPointsFilter", params);
00587 
00588                 addFilter("SurfaceNormalDataPointsFilter", paramsNorm);
00589                 addFilter("CovarianceSamplingDataPointsFilter", params);
00590                 
00591                 const DP filteredCloud = covsFilter->filter(cloud);
00592                                 
00593                 if (nbSample == nbPts3D)
00594                 {
00595                         EXPECT_EQ(filteredCloud.getNbPoints(), nbPts3D);
00596                 }
00597                 else if (nbSample == nbPts)
00598                 {
00599                         //Check number of points
00600                         EXPECT_EQ(cloud.getNbPoints(), filteredCloud.getNbPoints());
00601                         EXPECT_EQ(cloud.getDescriptorDim(), filteredCloud.getDescriptorDim());
00602                         EXPECT_EQ(cloud.getTimeDim(), filteredCloud.getTimeDim());
00603 
00604                         EXPECT_EQ(filteredCloud.getNbPoints(), nbPts);
00605                 }
00606                 
00607                 validate3dTransformation();                     
00608                 EXPECT_GE(cloud.getNbPoints(), filteredCloud.getNbPoints());
00609         }
00610 }
00611 
00612 TEST_F(DataFilterTest, VoxelGridDataPointsFilter)
00613 {
00614         // Test with point cloud
00615         DP cloud = generateRandomDataPoints();
00616 
00617         params = PM::Parameters(); 
00618         params["vSizeX"] = "0.5";
00619         params["vSizeY"] = "0.5";
00620         params["vSizeZ"] = "0.5";
00621         params["useCentroid"] = toParam(true);
00622         params["averageExistingDescriptors"] = toParam(true);
00623 
00624         std::shared_ptr<PM::DataPointsFilter> voxelFilter =
00625                         PM::get().DataPointsFilterRegistrar.create("VoxelGridDataPointsFilter", params);
00626 
00627         DP filteredCloud = voxelFilter->filter(cloud);
00628 
00629         EXPECT_GT(cloud.getNbPoints(), filteredCloud.getNbPoints());
00630         EXPECT_EQ(cloud.getDescriptorDim(), filteredCloud.getDescriptorDim());
00631         EXPECT_EQ(cloud.getTimeDim(), filteredCloud.getTimeDim());
00632 
00633         // Test with ICP
00634         vector<bool> useCentroid = {false, true};
00635         vector<bool> averageExistingDescriptors = {false, true};
00636         for (unsigned i = 0 ; i < useCentroid.size() ; i++) 
00637         {
00638                 for (unsigned j = 0; j < averageExistingDescriptors.size(); j++) 
00639                 {
00640                         params = PM::Parameters(); 
00641                         params["vSizeX"] = "0.02";
00642                         params["vSizeY"] = "0.02";
00643                         params["vSizeZ"] = "0.02";
00644                         params["useCentroid"] = toParam(true);
00645                         params["averageExistingDescriptors"] = toParam(true);
00646                         
00647                         icp.readingDataPointsFilters.clear();
00648                         addFilter("VoxelGridDataPointsFilter", params);
00649                         validate2dTransformation();
00650                 }
00651         }
00652 
00653         for (unsigned i = 0 ; i < useCentroid.size() ; i++)
00654         {
00655                 for (unsigned j = 0; j < averageExistingDescriptors.size(); j++)
00656                 {
00657                         params = PM::Parameters();
00658                         params["vSizeX"] = "1";
00659                         params["vSizeY"] = "1";
00660                         params["vSizeZ"] = "1";
00661                         params["useCentroid"] = toParam(true);
00662                         params["averageExistingDescriptors"] = toParam(true);
00663                         
00664                         icp.readingDataPointsFilters.clear();
00665                         addFilter("VoxelGridDataPointsFilter", params);
00666                         validate3dTransformation();
00667                 }
00668         }
00669 }
00670 
00671 TEST_F(DataFilterTest, CutAtDescriptorThresholdDataPointsFilter)
00672 {
00673         // Copied from density ratio above
00674         vector<double> thresholds = {100, 1000, 5000};
00675 
00676         DP ref3Ddensities = ref3D;
00677         // Adding descriptor "densities"
00678         icp.readingDataPointsFilters.clear();
00679         params = PM::Parameters();
00680         params["knn"] = "5"; 
00681         params["epsilon"] = "0.1"; 
00682         params["keepNormals"] = "0";
00683         params["keepDensities"] = "1";
00684         params["keepEigenValues"] = "0";
00685         params["keepEigenVectors"] = "0";
00686         params["keepMatchedIds"] = "0";
00687         
00688 
00689         addFilter("SurfaceNormalDataPointsFilter", params);
00690         icp.readingDataPointsFilters.apply(ref3Ddensities);
00691 
00692         for(unsigned i=0; i < thresholds.size(); i++)
00693         {
00694                 int belowCount=0;
00695                 int aboveCount=0;
00696 
00697                 // counting points above and below
00698                 PM::DataPoints::View densities = ref3Ddensities.getDescriptorViewByName("densities");
00699                 for (unsigned j=0; j < densities.cols(); ++j)
00700                 {
00701                         if (densities(0, j) <= thresholds[i])
00702                         {
00703                                 ++belowCount;
00704                         }
00705                         if (densities(0, j) >= thresholds[i])
00706                         {
00707                                 ++aboveCount;
00708                         }
00709                 }
00710 
00711                 for(bool useLargerThan(true); useLargerThan; useLargerThan=false)
00712                 {
00713                         DP ref3DCopy = ref3Ddensities;
00714 
00715                         icp.readingDataPointsFilters.clear();
00716                         params = PM::Parameters(); 
00717                         params["descName"] = toParam("densities");
00718                         params["useLargerThan"] = toParam(useLargerThan);
00719                         params["threshold"] = toParam(thresholds[i]);
00720                         
00721 
00722                         addFilter("CutAtDescriptorThresholdDataPointsFilter", params);
00723                         icp.readingDataPointsFilters.apply(ref3DCopy);
00724                         if (useLargerThan)
00725                         {
00726                                 EXPECT_TRUE(ref3DCopy.features.cols() == belowCount);
00727                         }
00728                         else
00729                         {
00730                                 EXPECT_TRUE(ref3DCopy.features.cols() == aboveCount);
00731                         }
00732                 }
00733         }
00734 }
00735 
00736 TEST_F(DataFilterTest, DistanceLimitDataPointsFilter)
00737 {
00738         params = PM::Parameters();
00739         params["dim"] = "0";
00740         params["dist"] = toParam(6.0);
00741         params["removeInside"] = "0";
00742 
00743         // Filter on x axis
00744         params["dim"] = "0";
00745         icp.readingDataPointsFilters.clear();
00746         addFilter("DistanceLimitDataPointsFilter", params);
00747         validate2dTransformation();
00748         validate3dTransformation();
00749 
00750         // Filter on y axis
00751         params["dim"] = "1";
00752         icp.readingDataPointsFilters.clear();
00753         addFilter("DistanceLimitDataPointsFilter", params);
00754         validate2dTransformation();
00755         validate3dTransformation();
00756 
00757         // Filter on z axis (not existing)
00758         params["dim"] = "2";
00759         icp.readingDataPointsFilters.clear();
00760         addFilter("DistanceLimitDataPointsFilter", params);
00761         EXPECT_ANY_THROW(validate2dTransformation());
00762         validate3dTransformation();
00763 
00764         // Filter on a radius
00765         params["dim"] = "-1";
00766         icp.readingDataPointsFilters.clear();
00767         addFilter("DistanceLimitDataPointsFilter", params);
00768         validate2dTransformation();
00769         validate3dTransformation();
00770 
00771         // Parameter outside valid range
00772         params["dim"] = "3";
00773         //TODO: specify the exception, move that to GenericTest
00774         EXPECT_ANY_THROW(addFilter("DistanceLimitDataPointsFilter", params));
00775 
00776 
00777         params = PM::Parameters();
00778         params["dim"] = "0";
00779         params["dist"] = toParam(0.05);
00780         params["removeInside"] = "1";
00781 
00782         // Filter on x axis
00783         params["dim"] = "0";
00784         icp.readingDataPointsFilters.clear();
00785         addFilter("DistanceLimitDataPointsFilter", params);
00786         validate2dTransformation();
00787         validate3dTransformation();
00788 
00789         // Filter on y axis
00790         params["dim"] = "1";
00791         icp.readingDataPointsFilters.clear();
00792         addFilter("DistanceLimitDataPointsFilter", params);
00793         validate2dTransformation();
00794         validate3dTransformation();
00795 
00796         //TODO: move that to specific 2D test
00797         // Filter on z axis (not existing)
00798         params["dim"] = "2";
00799         icp.readingDataPointsFilters.clear();
00800         addFilter("DistanceLimitDataPointsFilter", params);
00801         EXPECT_ANY_THROW(validate2dTransformation());
00802         validate3dTransformation();
00803 
00804         // Filter on a radius
00805         params["dim"] = "-1";
00806         icp.readingDataPointsFilters.clear();
00807         addFilter("DistanceLimitDataPointsFilter", params);
00808         validate2dTransformation();
00809         validate3dTransformation();
00810 }
00811 
00812 TEST_F(DataFilterTest, SameFilterInstanceTwice)
00813 {
00814         params = PM::Parameters();
00815         
00816         std::shared_ptr<PM::DataPointsFilter> df = PM::get().DataPointsFilterRegistrar.create("MaxPointCountDataPointsFilter", params);
00817         
00818         icp.referenceDataPointsFilters.push_back(df);
00819         icp.readingDataPointsFilters.push_back(df);
00820 }
00821 
00822 TEST_F(DataFilterTest, RemoveSensorBiasDataPointsFilter)
00823 {
00824         const size_t nbPts = 6;
00825         const double expectedErrors_LMS1xx[6] = {0., -0.0015156, -0.059276,
00826                                                  0., -0.002311, -0.163689};
00827         const double expectedErrors_HDL32E[6]  = {0., -0.002945, -0.075866,
00828                                                   0., -0.002998,-0.082777 };
00829         
00830         PM::Matrix points = (PM::Matrix(3,6) << 1, 1, 1, 5, 5, 5,
00831                 0, 0, 0, 0, 0, 0,
00832                 0, 0, 0, 0, 0, 0).finished();
00833         DP::Labels pointsLabels;
00834         pointsLabels.push_back(DP::Label("x", 1));
00835         pointsLabels.push_back(DP::Label("y", 1));
00836         pointsLabels.push_back(DP::Label("z", 1));
00837         pointsLabels.push_back(DP::Label("pad", 1));
00838         
00839         PM::Matrix desc = (PM::Matrix(4,6) << 0., 0.7854, 1.4835, 0., 0.7854, 1.4835, //0,45,85 degrees
00840                 -1, -1, -1, -5, -5, -5,
00841                 0,  0,  0,  0,  0,  0, // observation : point to sensor
00842                 0,  0,  0,  0,  0,  0).finished();
00843         
00844         PM::Matrix desc2 = (PM::Matrix(4,6) << 0., 0.7854, std::nanf(""), 0., 0.7854, M_PI_2, //0,45,90 degrees
00845                 -1, -1, -1, -5, -5, -5,
00846                 0,  0,  0,  0,  0,  0, // observation : point to sensor
00847                 0,  0,  0,  0,  0,  0).finished();
00848         DP::Labels descLabels;
00849         descLabels.push_back(DP::Label("incidenceAngles", 1));
00850         descLabels.push_back(DP::Label("observationDirections", 3));
00851         
00852         PM::Int64Matrix randTimes = PM::Int64Matrix::Random(2, nbPts);
00853         DP::Labels timeLabels;
00854         timeLabels.push_back(DP::Label("dummyTime", 2));
00855         
00856         // Construct the point cloud from the generated matrices
00857         DP pointCloud = DP(points, pointsLabels, desc, descLabels, randTimes, timeLabels);
00858         
00859         
00860         PM::Parameters parameters;
00861         parameters["sensorType"] = toParam(0); //LMS_1xx
00862         std::shared_ptr<PM::DataPointsFilter> removeSensorBiasFilter = PM::get().DataPointsFilterRegistrar.create("RemoveSensorBiasDataPointsFilter", parameters);
00863         DP resultCloud = removeSensorBiasFilter->filter(pointCloud);
00864         EXPECT_EQ(pointCloud.getNbPoints(), resultCloud.getNbPoints());
00865         
00866         for(std::size_t i = 0; i< nbPts; ++i)
00867         {
00868                 const double error = pointCloud.features.col(i).norm() - resultCloud.features.col(i).norm();
00869                 EXPECT_NEAR(expectedErrors_LMS1xx[i], error, 1e-3); // below mm
00870         }
00871         
00872         parameters["sensorType"] = toParam(1); //HDL32E
00873         removeSensorBiasFilter = PM::get().DataPointsFilterRegistrar.create("RemoveSensorBiasDataPointsFilter", parameters);
00874         resultCloud = removeSensorBiasFilter->filter(pointCloud);
00875         
00876         for(std::size_t i = 0; i< nbPts; ++i)
00877         {
00878                 const double error = pointCloud.features.col(i).norm() - resultCloud.features.col(i).norm();
00879                 EXPECT_NEAR(expectedErrors_HDL32E[i], error, 1e-4); // below mm
00880         }
00881 
00882 
00883         //test points rejection
00884         pointCloud = DP(points, pointsLabels, desc2, descLabels, randTimes, timeLabels);
00885 
00886         parameters["sensorType"] = toParam(0); //LMS_1xx
00887         parameters["angleThreshold"] = toParam(30.);
00888         removeSensorBiasFilter = PM::get().DataPointsFilterRegistrar.create("RemoveSensorBiasDataPointsFilter", parameters);
00889         resultCloud = removeSensorBiasFilter->filter(pointCloud);       
00890 
00891         //four points should have been rejected
00892         EXPECT_EQ(pointCloud.getNbPoints()-4, resultCloud.getNbPoints());
00893 }


libpointmatcher
Author(s):
autogenerated on Thu Jun 20 2019 19:51:29