00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include <gtest/gtest.h>
00039 #include <pcl/search/brute_force.h>
00040 #include <pcl/search/kdtree.h>
00041 #include <pcl/search/organized.h>
00042 #include <pcl/search/octree.h>
00043 #include <pcl/io/pcd_io.h>
00044 #include <boost/smart_ptr/shared_array.hpp>
00045 #include <boost/random/mersenne_twister.hpp>
00046 #include <boost/random/uniform_int.hpp>
00047 #include <boost/random/uniform_real.hpp>
00048 #include <boost/random/variate_generator.hpp>
00049
00050 #include <pcl/common/time.h>
00051
00052 using namespace pcl;
00053 using namespace std;
00054
00056 #define DEBUG_OUT 1
00057 #define EXCESSIVE_TESTING 0
00058
00059 #define TEST_unorganized_dense_cloud_COMPLETE_KNN 1
00060 #define TEST_unorganized_dense_cloud_VIEW_KNN 1
00061 #define TEST_unorganized_sparse_cloud_COMPLETE_KNN 1
00062 #define TEST_unorganized_sparse_cloud_VIEW_KNN 1
00063 #define TEST_unorganized_grid_cloud_COMPLETE_RADIUS 1
00064 #define TEST_unorganized_dense_cloud_COMPLETE_RADIUS 1
00065 #define TEST_unorganized_dense_cloud_VIEW_RADIUS 1
00066 #define TEST_unorganized_sparse_cloud_COMPLETE_RADIUS 1
00067 #define TEST_unorganized_sparse_cloud_VIEW_RADIUS 1
00068 #define TEST_ORGANIZED_SPARSE_COMPLETE_KNN 1
00069 #define TEST_ORGANIZED_SPARSE_VIEW_KNN 1
00070 #define TEST_ORGANIZED_SPARSE_COMPLETE_RADIUS 1
00071 #define TEST_ORGANIZED_SPARSE_VIEW_RADIUS 1
00072
00073 #if EXCESSIVE_TESTING
00074
00075 const unsigned int unorganized_point_count = 100000;
00076
00078 const unsigned int query_count = 5000;
00079 #else
00080
00081 const unsigned int unorganized_point_count = 1200;
00082
00084 const unsigned int query_count = 100;
00085 #endif
00086
00088 PointCloud<PointXYZ>::Ptr organized_sparse_cloud (new PointCloud<PointXYZ>);
00089
00091 PointCloud<PointXYZ>::Ptr unorganized_dense_cloud (new PointCloud<PointXYZ>);
00092
00094 PointCloud<PointXYZ>::Ptr unorganized_sparse_cloud (new PointCloud<PointXYZ>);
00095
00097 PointCloud<PointXYZ>::Ptr unorganized_grid_cloud (new PointCloud<PointXYZ>);
00098
00100 boost::variate_generator< boost::mt19937, boost::uniform_int<unsigned> > rand_uint(boost::mt19937 (), boost::uniform_int<unsigned> (0, 10));
00102 boost::variate_generator< boost::mt19937, boost::uniform_real<float> > rand_float(boost::mt19937 (), boost::uniform_real<float> (0, 1));
00103
00105 std::vector<int> unorganized_input_indices;
00106
00108 std::vector<int> organized_input_indices;
00109
00111 pcl::search::BruteForce<pcl::PointXYZ> brute_force;
00112
00114 pcl::search::KdTree<pcl::PointXYZ> KDTree;
00115
00117 pcl::search::Octree<pcl::PointXYZ> octree_search (0.1);
00118
00120 pcl::search::OrganizedNeighbor<pcl::PointXYZ> organized;
00121
00123 vector<search::Search<PointXYZ>* > unorganized_search_methods;
00124
00126 vector<search::Search<PointXYZ>* > organized_search_methods;
00127
00129 vector<int> unorganized_dense_cloud_query_indices;
00130 vector<int> unorganized_sparse_cloud_query_indices;
00131 vector<int> organized_sparse_query_indices;
00132
00138 bool testUniqueness (const vector<int>& indices, const string& name)
00139 {
00140 bool uniqueness = true;
00141 for (unsigned idx1 = 1; idx1 < indices.size () && uniqueness; ++idx1)
00142 {
00143
00144 for (unsigned idx2 = 0; idx2 < idx1; ++idx2)
00145 {
00146 if (indices [idx1] == indices [idx2])
00147 {
00148 #if DEBUG_OUT
00149 std::cout << name << " search: index is twice at positions: " << idx1 << " (" << indices [idx1] << ") , " << idx2 << " (" << indices [idx2] << ")" << std::endl;
00150 #endif
00151
00152 uniqueness = false;
00153 break;
00154 }
00155 }
00156 }
00157 return uniqueness;
00158 }
00159
00160
00166 bool testOrder (const vector<float>& distances, const string& name)
00167 {
00168 bool ordered = true;
00169 for (unsigned idx1 = 1; idx1 < distances.size (); ++idx1)
00170 {
00171 if (distances [idx1-1] > distances [idx1])
00172 {
00173 #if DEBUG_OUT
00174 std::cout << name << " search: not properly sorted: " << idx1 - 1 << "(" << distances [idx1-1] << ") > "
00175 << idx1 << "(" << distances [idx1] << ")"<< std::endl;
00176 #endif
00177 ordered = false;
00178 break;
00179 }
00180 }
00181
00182 return ordered;
00183 }
00184
00192 template<typename PointT> bool
00193 testResultValidity (const typename PointCloud<PointT>::ConstPtr point_cloud, const vector<bool>& indices_mask, const vector<bool>& nan_mask, const vector<int>& indices, const vector<int>& , const string& name)
00194 {
00195 bool validness = true;
00196 for (vector<int>::const_iterator iIt = indices.begin (); iIt != indices.end (); ++iIt)
00197 {
00198 if (!indices_mask [*iIt])
00199 {
00200 #if DEBUG_OUT
00201 cerr << name << ": result contains an invalid point: " << *iIt << " not in indices list.\n";
00202
00203
00204
00205
00206 #endif
00207 validness = false;
00208 break;
00209 }
00210 else if (!nan_mask [*iIt])
00211 {
00212 #if DEBUG_OUT
00213 cerr << name << ": result contains an invalid point: " << *iIt << " = NaN (" << point_cloud->points [*iIt].x << " , "
00214 << point_cloud->points [*iIt].y << " , "
00215 << point_cloud->points [*iIt].z << ")\n";
00216 #endif
00217 validness = false;
00218 break;
00219 }
00220 }
00221
00222 return validness;
00223 }
00224
00235 bool compareResults (const std::vector<int>& indices1, const::vector<float>& distances1, const std::string& name1,
00236 const std::vector<int>& indices2, const::vector<float>& distances2, const std::string& name2, float eps)
00237 {
00238 bool equal = true;
00239 if (indices1.size () != indices2.size ())
00240 {
00241 #if DEBUG_OUT
00242 cerr << "size of results between " << name1 << " search and " << name2 << " search do not match " <<indices1.size () << " vs. " << indices2.size () << endl;
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254 #endif
00255 equal = false;
00256 }
00257 else
00258 {
00259 for (unsigned idx = 0; idx < indices1.size (); ++idx)
00260 {
00261 if (indices1[idx] != indices2[idx] && fabs (distances1[idx] - distances2[idx]) > eps)
00262 {
00263 #if DEBUG_OUT
00264 cerr << "results between " << name1 << " search and " << name2 << " search do not match: " << idx << " nearest neighbor: "
00265 << indices1[idx] << " with distance: " << distances1[idx] << " vs. "
00266 << indices2[idx] << " with distance: " << distances2[idx] << endl;
00267 #endif
00268 equal = false;
00269 break;
00270 }
00271 }
00272 }
00273 return equal;
00274 }
00275
00282 template<typename PointT> void
00283 testKNNSearch (typename PointCloud<PointT>::ConstPtr point_cloud, vector<search::Search<PointT>*> search_methods,
00284 const vector<int>& query_indices, const vector<int>& input_indices = vector<int> () )
00285 {
00286 vector< vector<int> >indices (search_methods.size ());
00287 vector< vector<float> >distances (search_methods.size ());
00288 vector<bool> passed (search_methods.size (), true);
00289
00290 vector<bool> indices_mask (point_cloud->size (), true);
00291 vector<bool> nan_mask (point_cloud->size (), true);
00292
00293 if (input_indices.size () != 0)
00294 {
00295 indices_mask.assign (point_cloud->size (), false);
00296 for (vector<int>::const_iterator iIt = input_indices.begin (); iIt != input_indices.end (); ++iIt)
00297 indices_mask [*iIt] = true;
00298 }
00299
00300
00301 #pragma omp parallel for
00302 for (int pIdx = 0; pIdx < int (point_cloud->size ()); ++pIdx)
00303 {
00304 if (!isFinite (point_cloud->points [pIdx]))
00305 nan_mask [pIdx] = false;
00306 }
00307
00308 boost::shared_ptr<vector<int> > input_indices_;
00309 if (input_indices.size ())
00310 input_indices_.reset (new vector<int> (input_indices));
00311
00312 #pragma omp parallel for
00313 for (int sIdx = 0; sIdx < int (search_methods.size ()); ++sIdx)
00314 search_methods [sIdx]->setInputCloud (point_cloud, input_indices_);
00315
00316
00317 for (unsigned knn = 1; knn <= 512; knn <<= 3)
00318 {
00319
00320 for (vector<int>::const_iterator qIt = query_indices.begin (); qIt != query_indices.end (); ++qIt)
00321 {
00322 #pragma omp parallel for
00323 for (int sIdx = 0; sIdx < int (search_methods.size ()); ++sIdx)
00324 {
00325 search_methods [sIdx]->nearestKSearch (point_cloud->points[*qIt], knn, indices [sIdx], distances [sIdx]);
00326 passed [sIdx] = passed [sIdx] && testUniqueness (indices [sIdx], search_methods [sIdx]->getName ());
00327 passed [sIdx] = passed [sIdx] && testOrder (distances [sIdx], search_methods [sIdx]->getName ());
00328 passed [sIdx] = passed [sIdx] && testResultValidity<PointT>(point_cloud, indices_mask, nan_mask, indices [sIdx], input_indices, search_methods [sIdx]->getName ());
00329 }
00330
00331
00332 #pragma omp parallel for
00333 for (int sIdx = 1; sIdx < int (search_methods.size ()); ++sIdx)
00334 {
00335 passed [sIdx] = passed [sIdx] && compareResults (indices [0], distances [0], search_methods [0]->getName (),
00336 indices [sIdx], distances [sIdx], search_methods [sIdx]->getName (), 1e-6f);
00337 }
00338 }
00339 }
00340 for (size_t sIdx = 0; sIdx < search_methods.size (); ++sIdx)
00341 {
00342 cout << search_methods [sIdx]->getName () << ": " << (passed[sIdx]?"passed":"failed") << endl;
00343 EXPECT_TRUE (passed [sIdx]);
00344 }
00345 }
00346
00353 template<typename PointT> void
00354 testRadiusSearch (typename PointCloud<PointT>::ConstPtr point_cloud, vector<search::Search<PointT>*> search_methods,
00355 const vector<int>& query_indices, const vector<int>& input_indices = vector<int> ())
00356 {
00357 vector< vector<int> >indices (search_methods.size ());
00358 vector< vector<float> >distances (search_methods.size ());
00359 vector <bool> passed (search_methods.size (), true);
00360 vector<bool> indices_mask (point_cloud->size (), true);
00361 vector<bool> nan_mask (point_cloud->size (), true);
00362
00363 if (input_indices.size () != 0)
00364 {
00365 indices_mask.assign (point_cloud->size (), false);
00366 for (vector<int>::const_iterator iIt = input_indices.begin (); iIt != input_indices.end (); ++iIt)
00367 indices_mask [*iIt] = true;
00368 }
00369
00370
00371 #pragma omp parallel for
00372 for (int pIdx = 0; pIdx < int (point_cloud->size ()); ++pIdx)
00373 {
00374 if (!isFinite (point_cloud->points [pIdx]))
00375 nan_mask [pIdx] = false;
00376 }
00377
00378 boost::shared_ptr<vector<int> > input_indices_;
00379 if (input_indices.size ())
00380 input_indices_.reset (new vector<int> (input_indices));
00381
00382 #pragma omp parallel for
00383 for (int sIdx = 0; sIdx < int (search_methods.size ()); ++sIdx)
00384 search_methods [sIdx]->setInputCloud (point_cloud, input_indices_);
00385
00386
00387 for (float radius = 0.01f; radius < 0.1f; radius *= 2.0f)
00388 {
00389
00390
00391 for (vector<int>::const_iterator qIt = query_indices.begin (); qIt != query_indices.end (); ++qIt)
00392 {
00393 #pragma omp parallel for
00394 for (int sIdx = 0; sIdx < static_cast<int> (search_methods.size ()); ++sIdx)
00395 {
00396 search_methods [sIdx]->radiusSearch (point_cloud->points[*qIt], radius, indices [sIdx], distances [sIdx], 0);
00397 passed [sIdx] = passed [sIdx] && testUniqueness (indices [sIdx], search_methods [sIdx]->getName ());
00398 passed [sIdx] = passed [sIdx] && testOrder (distances [sIdx], search_methods [sIdx]->getName ());
00399 passed [sIdx] = passed [sIdx] && testResultValidity<PointT>(point_cloud, indices_mask, nan_mask, indices [sIdx], input_indices, search_methods [sIdx]->getName ());
00400 }
00401
00402
00403 #pragma omp parallel for
00404 for (int sIdx = 1; sIdx < static_cast<int> (search_methods.size ()); ++sIdx)
00405 {
00406 passed [sIdx] = passed [sIdx] && compareResults (indices [0], distances [0], search_methods [0]->getName (),
00407 indices [sIdx], distances [sIdx], search_methods [sIdx]->getName (), 1e-6f);
00408 }
00409 }
00410 }
00411 for (unsigned sIdx = 0; sIdx < search_methods.size (); ++sIdx)
00412 {
00413 cout << search_methods [sIdx]->getName () << ": " << (passed[sIdx]?"passed":"failed") << endl;
00414 EXPECT_TRUE (passed [sIdx]);
00415 }
00416 }
00417
00418 #if TEST_unorganized_dense_cloud_COMPLETE_KNN
00419
00420 TEST (PCL, unorganized_dense_cloud_Complete_KNN)
00421 {
00422 testKNNSearch (unorganized_dense_cloud, unorganized_search_methods, unorganized_dense_cloud_query_indices);
00423 }
00424 #endif
00425
00426 #if TEST_unorganized_dense_cloud_VIEW_KNN
00427
00428 TEST (PCL, unorganized_dense_cloud_View_KNN)
00429 {
00430 testKNNSearch (unorganized_dense_cloud, unorganized_search_methods, unorganized_dense_cloud_query_indices, unorganized_input_indices);
00431 }
00432 #endif
00433
00434 #if TEST_unorganized_sparse_cloud_COMPLETE_KNN
00435
00436 TEST (PCL, unorganized_sparse_cloud_Complete_KNN)
00437 {
00438 testKNNSearch (unorganized_sparse_cloud, unorganized_search_methods, unorganized_sparse_cloud_query_indices);
00439 }
00440 #endif
00441
00442 #if TEST_unorganized_sparse_cloud_VIEW_KNN
00443 TEST (PCL, unorganized_sparse_cloud_View_KNN)
00444 {
00445 testKNNSearch (unorganized_sparse_cloud, unorganized_search_methods, unorganized_sparse_cloud_query_indices, unorganized_input_indices);
00446 }
00447 #endif
00448
00449 #if TEST_unorganized_dense_cloud_COMPLETE_RADIUS
00450
00451 TEST (PCL, unorganized_dense_cloud_Complete_Radius)
00452 {
00453 testRadiusSearch (unorganized_dense_cloud, unorganized_search_methods, unorganized_dense_cloud_query_indices);
00454 }
00455 #endif
00456
00457 #if TEST_unorganized_grid_cloud_COMPLETE_RADIUS
00458
00459 TEST (PCL, unorganized_grid_cloud_Complete_Radius)
00460 {
00461 vector<int> query_indices;
00462 query_indices.reserve (query_count);
00463
00464 unsigned skip = static_cast<unsigned> (unorganized_grid_cloud->size ()) / query_count;
00465 for (unsigned idx = 0; idx < unorganized_grid_cloud->size () && query_indices.size () < query_count; ++idx)
00466 if ((rand () % skip) == 0 && isFinite (unorganized_grid_cloud->points [idx]))
00467 query_indices.push_back (idx);
00468
00469 testRadiusSearch (unorganized_grid_cloud, unorganized_search_methods, query_indices);
00470 }
00471 #endif
00472
00473 #if TEST_unorganized_dense_cloud_VIEW_RADIUS
00474
00475 TEST (PCL, unorganized_dense_cloud_View_Radius)
00476 {
00477 testRadiusSearch (unorganized_dense_cloud, unorganized_search_methods, unorganized_dense_cloud_query_indices, unorganized_input_indices);
00478 }
00479 #endif
00480
00481 #if TEST_unorganized_sparse_cloud_COMPLETE_RADIUS
00482
00483 TEST (PCL, unorganized_sparse_cloud_Complete_Radius)
00484 {
00485 testRadiusSearch (unorganized_sparse_cloud, unorganized_search_methods, unorganized_sparse_cloud_query_indices);
00486 }
00487 #endif
00488
00489 #if TEST_unorganized_sparse_cloud_VIEW_RADIUS
00490 TEST (PCL, unorganized_sparse_cloud_View_Radius)
00491 {
00492 testRadiusSearch (unorganized_sparse_cloud, unorganized_search_methods, unorganized_sparse_cloud_query_indices, unorganized_input_indices);
00493 }
00494 #endif
00495
00496 #if TEST_ORGANIZED_SPARSE_COMPLETE_KNN
00497 TEST (PCL, Organized_Sparse_Complete_KNN)
00498 {
00499 testKNNSearch (organized_sparse_cloud, organized_search_methods, organized_sparse_query_indices);
00500 }
00501 #endif
00502
00503 #if TEST_ORGANIZED_SPARSE_VIEW_KNN
00504 TEST (PCL, Organized_Sparse_View_KNN)
00505 {
00506 testKNNSearch (organized_sparse_cloud, organized_search_methods, organized_sparse_query_indices, organized_input_indices);
00507 }
00508 #endif
00509
00510 #if TEST_ORGANIZED_SPARSE_COMPLETE_RADIUS
00511 TEST (PCL, Organized_Sparse_Complete_Radius)
00512 {
00513 testRadiusSearch (organized_sparse_cloud, organized_search_methods, organized_sparse_query_indices);
00514 }
00515 #endif
00516
00517 #if TEST_ORGANIZED_SPARSE_VIEW_RADIUS
00518 TEST (PCL, Organized_Sparse_View_Radius)
00519 {
00520 testRadiusSearch (organized_sparse_cloud, organized_search_methods, organized_sparse_query_indices, organized_input_indices);
00521 }
00522 #endif
00523
00529 void createQueryIndices (std::vector<int>& query_indices, PointCloud<PointXYZ>::ConstPtr point_cloud, unsigned query_count)
00530 {
00531 query_indices.clear ();
00532 query_indices.reserve (query_count);
00533
00534 unsigned skip = static_cast<unsigned> (point_cloud->size ()) / query_count;
00535 for (unsigned idx = 0; idx < point_cloud->size () && query_indices.size () < query_count; ++idx)
00536 if ((rand () % skip) == 0 && isFinite (point_cloud->points [idx]))
00537 query_indices.push_back (idx);
00538 }
00539
00544 void createIndices (std::vector<int>& indices, unsigned max_index)
00545 {
00546
00547 for (unsigned idx = 0; idx <= max_index; ++idx)
00548 if (rand_uint () == 0)
00549 indices.push_back (idx);
00550
00551 boost::variate_generator< boost::mt19937, boost::uniform_int<> > rand_indices(boost::mt19937 (), boost::uniform_int<> (0, static_cast<int> (indices.size ()) - 1));
00552
00553 for (unsigned idx = 0; idx < max_index; ++idx)
00554 {
00555 unsigned idx1 = rand_indices ();
00556 unsigned idx2 = rand_indices ();
00557
00558 std::swap (indices[idx1], indices[idx2]);
00559 }
00560 }
00561
00562
00563 int
00564 main (int argc, char** argv)
00565 {
00566 if (argc < 2)
00567 {
00568 std::cout << "need path to table_scene_mug_stereo_textured.pcd file\n";
00569 return (-1);
00570 }
00571
00572 pcl::io::loadPCDFile (argv [1], *organized_sparse_cloud);
00573
00574
00575 unorganized_dense_cloud->resize (unorganized_point_count);
00576 unorganized_dense_cloud->height = 1;
00577 unorganized_dense_cloud->width = unorganized_point_count;
00578 unorganized_dense_cloud->is_dense = true;
00579
00580 unorganized_sparse_cloud->resize (unorganized_point_count);
00581 unorganized_sparse_cloud->height = 1;
00582 unorganized_sparse_cloud->width = unorganized_point_count;
00583 unorganized_sparse_cloud->is_dense = false;
00584
00585 PointXYZ point;
00586 for (unsigned pIdx = 0; pIdx < unorganized_point_count; ++pIdx)
00587 {
00588 point.x = rand_float ();
00589 point.y = rand_float ();
00590 point.z = rand_float ();
00591
00592 unorganized_dense_cloud->points [pIdx] = point;
00593
00594 if (rand_uint () == 0)
00595 unorganized_sparse_cloud->points [pIdx].x = unorganized_sparse_cloud->points [pIdx].y = unorganized_sparse_cloud->points [pIdx].z = std::numeric_limits<float>::quiet_NaN ();
00596 else
00597 unorganized_sparse_cloud->points [pIdx] = point;
00598 }
00599
00600 unorganized_grid_cloud->reserve (1000);
00601 unorganized_grid_cloud->height = 1;
00602 unorganized_grid_cloud->width = 1000;
00603 unorganized_grid_cloud->is_dense = true;
00604
00605
00606 for (unsigned xIdx = 0; xIdx < 10; ++xIdx)
00607 {
00608 for (unsigned yIdx = 0; yIdx < 10; ++yIdx)
00609 {
00610 for (unsigned zIdx = 0; zIdx < 10; ++zIdx)
00611 {
00612 point.x = 0.1f * static_cast<float>(xIdx);
00613 point.y = 0.1f * static_cast<float>(yIdx);
00614 point.z = 0.1f * static_cast<float>(zIdx);
00615 unorganized_grid_cloud->push_back (point);
00616 }
00617 }
00618 }
00619
00620 createIndices (organized_input_indices, static_cast<unsigned> (organized_sparse_cloud->size () - 1));
00621 createIndices (unorganized_input_indices, unorganized_point_count - 1);
00622
00623 brute_force.setSortedResults (true);
00624 KDTree.setSortedResults (true);
00625 octree_search.setSortedResults (true);
00626 organized.setSortedResults (true);
00627
00628 unorganized_search_methods.push_back (&brute_force);
00629 unorganized_search_methods.push_back (&KDTree);
00630 unorganized_search_methods.push_back (&octree_search);
00631
00632 organized_search_methods.push_back (&brute_force);
00633 organized_search_methods.push_back (&KDTree);
00634 organized_search_methods.push_back (&octree_search);
00635 organized_search_methods.push_back (&organized);
00636
00637 createQueryIndices (unorganized_dense_cloud_query_indices, unorganized_dense_cloud, query_count);
00638 createQueryIndices (unorganized_sparse_cloud_query_indices, unorganized_sparse_cloud, query_count);
00639 createQueryIndices (organized_sparse_query_indices, organized_sparse_cloud, query_count);
00640
00641 testing::InitGoogleTest (&argc, argv);
00642 return (RUN_ALL_TESTS ());
00643 }
00644