00001
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069 #include <ros/console.h>
00070 #include <ros/assert.h>
00071 #include <gtest/gtest.h>
00072
00073 #include <pcl/io/pcd_io.h>
00074 #include <rosbag/bag.h>
00075
00076
00077 #include <cob_3d_segmentation/general_segmentation.h>
00078 #include <cob_3d_segmentation/quad_regression/quad_regression.h>
00079 #include <cob_3d_segmentation/ransac/ransac.h>
00080 #include <cob_3d_segmentation/marching_cubes/marching_cubes.h>
00081 #if PCL_MINOR_VERSION >= 6
00082 #include <cob_3d_segmentation/multi_plane/multi_plane.h>
00083 #endif
00084
00085
00086
00090 class Testing_PCDLoader
00091 {
00092 std::vector<std::string> pc2s_fn_;
00093
00094 static bool _load(const std::string &fn, sensor_msgs::PointCloud2 &pc2) {
00095 pc2.header.frame_id = fn;
00096
00097 if(!pcl::io::loadPCDFile(fn,pc2))
00098 return true;
00099 else
00100 ROS_ERROR("failed to load pcd file %s",fn.c_str());
00101
00102 return false;
00103 }
00104
00105 Testing_PCDLoader()
00106 {
00107 load("test/kitchen01_far_raw.pcd");
00108 load("test/office01_close_raw.pcd");
00109 load("test/office01_far_raw.pcd");
00110 load("test/shelves01_close_raw.pcd");
00111 load("test/shelves01_far_raw.pcd");
00112 load("test/table01_close1m_raw.pcd");
00113 load("test/table01_far_raw.pcd");
00114 }
00115
00116 public:
00117
00119 static Testing_PCDLoader &get() {
00120 static Testing_PCDLoader t;
00121 return t;
00122 }
00123
00124 void load(const std::string &fn) {
00125 pc2s_fn_.push_back(fn);
00126 }
00127
00128 template <typename Point>
00129 bool getPC(const size_t ind, typename pcl::PointCloud<Point>::Ptr pc, std::string &fn) const
00130 {
00131 pc->clear();
00132 if(ind<pc2s_fn_.size())
00133 {
00134 sensor_msgs::PointCloud2 pc2;
00135 if(_load(pc2s_fn_[ind],pc2))
00136 {
00137 pcl::fromROSMsg(pc2,*pc);
00138 fn = pc2.header.frame_id;
00139 }
00140 return true;
00141 }
00142 return false;
00143 }
00144
00145 template <typename Point>
00146 void writePC(const std::string &pc_fn, typename pcl::PointCloud<Point>::ConstPtr pc) const
00147 {
00148 const ::testing::TestInfo* const test_info =
00149 ::testing::UnitTest::GetInstance()->current_test_info();
00150
00151 if(pc->size()<1) {
00152 ROS_ERROR("cannot save empty pc (%s_%s_%s)", test_info->test_case_name(), test_info->name(), pc_fn.c_str());
00153 return;
00154 }
00155
00156 char fn[512];
00157 sprintf(fn,"test/labeled/pc_%s_%s_%s.pcd",test_info->test_case_name(), test_info->name(), pc_fn.c_str());
00158 pcl::io::savePCDFile(fn,*pc);
00159 }
00160 };
00161
00166 class PrecisionStopWatch {
00167 struct timeval precisionClock;
00168
00169 public:
00170 PrecisionStopWatch() {
00171 gettimeofday(&precisionClock, NULL);
00172 };
00173
00174 void precisionStart() {
00175 gettimeofday(&precisionClock, NULL);
00176 };
00177
00178 double precisionStop() {
00179 struct timeval precisionClockEnd;
00180 gettimeofday(&precisionClockEnd, NULL);
00181 double d= ((double)precisionClockEnd.tv_sec + 1.0e-6 * (double)precisionClockEnd.tv_usec) - ((double)precisionClock.tv_sec + 1.0e-6 * (double)precisionClock.tv_usec);
00182 precisionStart();
00183 return d;
00184 };
00185 };
00186
00190 class Testing_CSV
00191 {
00192 FILE *fp;
00193 int row, col;
00194 public:
00195 Testing_CSV(std::string fn):row(0),col(0)
00196 {
00197 const ::testing::TestInfo* const test_info =
00198 ::testing::UnitTest::GetInstance()->current_test_info();
00199 fn = "test/results/csv_"+std::string(test_info->test_case_name())+"_"+std::string(test_info->name())+"_"+fn+".csv";
00200 fp = fopen(fn.c_str(),"w");
00201 }
00202
00203 ~Testing_CSV()
00204 {
00205 if(fp) fclose(fp);
00206 }
00207
00208 void add(const std::string &str)
00209 {
00210 if(col>0) fputc('\t',fp);
00211 fwrite(str.c_str(),str.size(),1,fp);
00212 ++col;
00213 }
00214
00215 template<typename T>
00216 void add(const T v)
00217 {
00218 std::stringstream ss(std::stringstream::out);
00219 ss<<v;
00220 add(ss.str());
00221 }
00222
00223 void next()
00224 {
00225 if(col>0)
00226 {
00227 fputc('\n',fp);
00228 col=0;
00229 ++row;
00230 }
00231 fflush(fp);
00232 }
00233
00234 static Testing_CSV create_table(const std::string &fn, std::string cols)
00235 {
00236 Testing_CSV csv(fn);
00237 size_t pos;
00238 while(cols.size()>0)
00239 {
00240 pos=cols.find(",");
00241 if(pos!=std::string::npos)
00242 {
00243 csv.add(std::string(cols.begin(),cols.begin()+pos));
00244 cols.erase(cols.begin(), cols.begin()+(pos+1));
00245 }
00246 else
00247 {
00248 csv.add(cols);
00249 break;
00250 }
00251 }
00252 csv.next();
00253 return csv;
00254 }
00255 };
00256
00257
00258
00259
00260
00261
00262 template <typename Point, typename PointLabel>
00263 void segment_pointcloud(GeneralSegmentation<Point, PointLabel> *seg, typename pcl::PointCloud<Point>::Ptr &pc, const std::string &fn)
00264 {
00265 EXPECT_TRUE(seg!=NULL);
00266 EXPECT_TRUE(pc->size()>0);
00267
00268
00269 static Testing_CSV csv = Testing_CSV::create_table("execution_time","filename,execution time in seconds");
00270 double took=0.;
00271
00272 seg->setInputCloud(pc);
00273
00274 PrecisionStopWatch psw;
00275 EXPECT_TRUE(
00276 seg->compute()
00277 );
00278 ROS_INFO("segmentation took %f", took=psw.precisionStop());
00279 csv.add(fn);
00280 csv.add(took);
00281 csv.next();
00282
00283 Testing_PCDLoader::get().writePC<PointLabel>(fn, seg->getOutputCloud());
00284 }
00285
00286 template <int Degree>
00287 void test_QPPF()
00288 {
00289 typedef pcl::PointXYZ Point;
00290 typedef PointXYZRGBLabel PointL;
00291
00292 pcl::PointCloud<Point>::Ptr pc(new pcl::PointCloud<Point>);
00293 static Segmentation::Segmentation_QuadRegression<Point, PointL, Segmentation::QPPF::QuadRegression<Degree, Point, Segmentation::QPPF::CameraModel_Kinect<Point> > > seg;
00294
00295 static Testing_CSV csv = Testing_CSV::create_table("accuracy","filename,mean,variance,weighted mean,weighted variance,average distance,used points, memory for representation,points,true positive rate, false positive rate,execution time: quadtree,execution time: growing,execution time: extraction,number of segments");
00296
00297 ROS_INFO("starting segmentation");
00298 size_t ind=0;
00299 std::string fn;
00300 while(Testing_PCDLoader::get().getPC<Point>(ind++, pc, fn))
00301 {
00302 if(pc->size()<1) continue;
00303
00304 pcl::PointCloud<PointL>::Ptr labeled_pc(new pcl::PointCloud<PointL>);
00305 bool loaded = false;
00306 try {
00307 loaded = Testing_PCDLoader::get().getPC<PointL>(ind-1, labeled_pc, fn);
00308 } catch(...) {
00309 }
00310 if(!loaded) {
00311 ROS_INFO("will not evaluate segmentation");
00312 labeled_pc.reset();}
00313
00314 ROS_INFO("processing pc %d ...",(int)ind-1);
00315 std::string fn_short(fn.begin()+(fn.find_last_of("/")+1),fn.end());
00316
00317
00318
00319 segment_pointcloud<Point,PointL>(&seg,pc, fn_short);
00320
00321 seg.extractImages();
00322
00323 Testing_PCDLoader::get().writePC<PointL>("reconstructed_"+fn_short, seg.getReconstructedOutputCloud());
00324
00325 float mean, var, mean_weighted, var_weighted, dist;
00326 double et_quadtree, et_growing, et_extraction;
00327 double true_positive, false_positive;
00328 size_t used, mem, points;
00329 seg.compute_accuracy(mean, var, mean_weighted, var_weighted, used, mem, points, dist, labeled_pc, true_positive, false_positive);
00330 #ifdef STOP_TIME
00331 seg.getExecutionTimes(et_quadtree, et_growing, et_extraction);
00332 #endif
00333
00334 csv.add(fn_short);
00335 csv.add(mean);
00336 csv.add(var_weighted);
00337 csv.add(mean_weighted);
00338 csv.add(var);
00339 csv.add(dist);
00340 csv.add(used);
00341 csv.add(mem);
00342 csv.add(points);
00343 csv.add(true_positive);
00344 csv.add(false_positive);
00345 #ifdef STOP_TIME
00346 csv.add(et_quadtree);
00347 csv.add(et_growing);
00348 csv.add(et_extraction);
00349 #endif
00350 csv.add(seg.getPolygons().size());
00351 csv.next();
00352
00353
00354 rosbag::Bag bag_out;
00355 bag_out.open("test/labeled/"+fn_short+".bag", rosbag::bagmode::Write);
00356 bag_out.write("shapes_array", ros::Time(1342850029.582334425+0.1), (cob_3d_mapping_msgs::ShapeArray)seg);
00357 bag_out.write("shapes_array", ros::Time(1342850029.582334425+0.2), (cob_3d_mapping_msgs::ShapeArray)seg);
00358 bag_out.write("shapes_array", ros::Time(1342850029.582334425+0.4), (cob_3d_mapping_msgs::ShapeArray)seg);
00359 }
00360 }
00361
00362 TEST(Segmentation, quad_regression1)
00363 { test_QPPF<1>(); }
00364 TEST(Segmentation, quad_regression2)
00365 { test_QPPF<2>(); }
00366 TEST(Segmentation, quad_regression3)
00367 { test_QPPF<3>(); }
00368 TEST(Segmentation, quad_regression4)
00369 { test_QPPF<4>(); }
00370
00371
00372
00373
00374
00375 TEST(Segmentation, ransac)
00376 {
00377 return;
00378 typedef pcl::PointXYZRGB Point;
00379 typedef PointXYZRGBLabel PointL;
00380
00381 pcl::PointCloud<Point>::Ptr pc(new pcl::PointCloud<Point>);
00382 Segmentation::Segmentation_RANSAC<Point,PointL> seg;
00383
00384 static Testing_CSV csv = Testing_CSV::create_table("accuracy","filename,mean,variance,average distance,used points, memory for representation,points,true positive rate, false positive rate");
00385
00386 ROS_INFO("starting segmentation");
00387 size_t ind=0;
00388 std::string fn;
00389 while(Testing_PCDLoader::get().getPC<Point>(ind++, pc, fn))
00390 {
00391 if(pc->size()<1) continue;
00392 ROS_INFO("processing pc %d ...",(int)ind-1);
00393 std::string fn_short(fn.begin()+(fn.find_last_of("/")+1),fn.end());
00394 segment_pointcloud<Point,PointL>(&seg,pc, fn_short);
00395
00396 pcl::PointCloud<PointL>::Ptr labeled_pc(new pcl::PointCloud<PointL>);
00397 try {
00398 Testing_PCDLoader::get().getPC<PointL>(ind-1, labeled_pc, fn);
00399 } catch(...) {}
00400
00401 Testing_PCDLoader::get().writePC<PointL>("reconstructed_"+fn_short, seg.getReconstructedOutputCloud());
00402
00403 float mean, var, mean_weighted, var_weighted, dist;
00404 double true_positive, false_positive;
00405 size_t used, mem, points;
00406 seg.compute_accuracy(mean, var, mean_weighted, var_weighted, used, mem, points, dist, labeled_pc, true_positive, false_positive);
00407
00408 csv.add(fn_short);
00409 csv.add(mean);
00410 csv.add(var);
00411 csv.add(dist);
00412 csv.add(used);
00413 csv.add(mem);
00414 csv.add(points);
00415 csv.add(true_positive);
00416 csv.add(false_positive);
00417 csv.next();
00418 }
00419 }
00420
00421 #if PCL_MINOR_VERSION >= 6
00422 TEST(Segmentation, multi_plane)
00423 {
00424 typedef pcl::PointXYZRGB Point;
00425 typedef pcl::PointXYZRGBNormal PointN;
00426 typedef PointXYZRGBLabel PointL;
00427
00428 pcl::PointCloud<Point>::Ptr pc(new pcl::PointCloud<Point>);
00429 Segmentation::Segmentation_MultiPlane<Point,PointN,PointL> seg;
00430
00431 static Testing_CSV csv = Testing_CSV::create_table("accuracy","filename,mean,variance,average distance,used points, memory for representation,points,true positive rate, false positive rate");
00432
00433 ROS_INFO("starting segmentation");
00434 size_t ind=0;
00435 std::string fn;
00436 while(Testing_PCDLoader::get().getPC<Point>(ind++, pc, fn))
00437 {
00438 if(pc->size()<1) continue;
00439 ROS_INFO("processing pc %d ...",(int)ind-1);
00440 std::string fn_short(fn.begin()+(fn.find_last_of("/")+1),fn.end());
00441 segment_pointcloud<Point,PointL>(&seg,pc, fn_short);
00442
00443 pcl::PointCloud<PointL>::Ptr labeled_pc(new pcl::PointCloud<PointL>);
00444 try {
00445 Testing_PCDLoader::get().getPC<PointL>(ind-1, labeled_pc, fn);
00446 } catch(...) {}
00447
00448 Testing_PCDLoader::get().writePC<PointL>("reconstructed_"+fn_short, seg.getReconstructedOutputCloud());
00449
00450 float mean, var, mean_weighted, var_weighted, dist;
00451 double true_positive, false_positive;
00452 size_t used, mem, points;
00453 seg.compute_accuracy(mean, var, mean_weighted, var_weighted, used, mem, points, dist, labeled_pc, true_positive, false_positive);
00454
00455 csv.add(fn_short);
00456 csv.add(mean);
00457 csv.add(var);
00458 csv.add(dist);
00459 csv.add(used);
00460 csv.add(mem);
00461 csv.add(points);
00462 csv.add(true_positive);
00463 csv.add(false_positive);
00464 csv.next();
00465 }
00466 }
00467 #endif
00468
00469 TEST(Segmentation, marching_cubes)
00470 {
00471 typedef pcl::PointXYZRGB Point;
00472 typedef pcl::PointXYZRGBNormal PointN;
00473 typedef pcl::PointXYZRGB PointL;
00474
00475 pcl::PointCloud<Point>::Ptr pc(new pcl::PointCloud<Point>);
00476 Segmentation::Segmentation_MarchingCubes<Point,PointN,PointL> seg;
00477
00478 static Testing_CSV csv = Testing_CSV::create_table("accuracy","filename,mean,variance,average distance,used points, memory for representation,points");
00479
00480 ROS_INFO("starting segmentation");
00481 size_t ind=0;
00482 std::string fn;
00483 while(Testing_PCDLoader::get().getPC<Point>(ind++, pc, fn))
00484 {
00485 if(pc->size()<1) continue;
00486 ROS_INFO("processing pc %d ...",(int)ind-1);
00487 std::string fn_short(fn.begin()+(fn.find_last_of("/")+1),fn.end());
00488 segment_pointcloud<Point,PointL>(&seg,pc, fn_short);
00489
00490 Testing_PCDLoader::get().writePC<PointL>("reconstructed_"+fn_short, seg.getReconstructedOutputCloud());
00491
00492 float mean, var, dist;
00493 size_t used, mem, points;
00494 seg.compute_accuracy(mean, var, used, mem, points, dist);
00495
00496 csv.add(fn_short);
00497 csv.add(mean);
00498 csv.add(var);
00499 csv.add(dist);
00500 csv.add(used);
00501 csv.add(mem);
00502 csv.add(points);
00503 csv.next();
00504 }
00505 }
00506
00507
00508 int main(int argc, char **argv){
00509 ros::Time::init();
00510
00511 if(argc>1 && strcmp(argv[1],"--help")==0)
00512 {
00513 printf("usage:\n\t- without any options tests segmentation\n\t- with additional pcd files\n");
00514 return 0;
00515 }
00516
00517 for(int i=1; i<argc; i++)
00518 Testing_PCDLoader::get().load(argv[i]);
00519
00520 testing::InitGoogleTest(&argc, argv);
00521 return RUN_ALL_TESTS();
00522 }