00001 #include <pcl/point_types.h>
00002 #include <pcl/conversions.h>
00003 #include <pcl/io/pcd_io.h>
00004 #include <pcl/filters/filter_indices.h>
00005 #include <pcl_conversions/pcl_conversions.h>
00006 #include <sensor_msgs/Image.h>
00007 #include <sensor_msgs/PointCloud2.h>
00008
00009 #include <ros/ros.h>
00010 #include <rosbag/bag.h>
00011 #include <rosbag/view.h>
00012
00013 #include <sstream>
00014 #include <fstream>
00015 #include <boost/filesystem.hpp>
00016 #include <boost/foreach.hpp>
00017
00018 #include <shape_reconstruction/SRUtils.h>
00019
00020 #include <opencv2/highgui/highgui.hpp>
00021
00022 #include <regex>
00023
00024 using namespace std;
00025 using namespace boost::filesystem;
00026
00027 namespace omip {
00028
00029
00030 void compareAlignedDensePointClouds(SRPointCloud::Ptr& gt_pc, SRPointCloud::Ptr& seg_pc, int& tp, int& tn, int& fp, int& fn, int& error) {
00031 tp=0, tn=0, fp=0, fn=0, error=0;
00032
00033
00034 if (gt_pc->size() != seg_pc->size()) {
00035 ROS_ERROR(" Cannot compare; sizes differ!");
00036 return;
00037 } else {
00038 for (int i = 0; i < gt_pc->size(); i++) {
00039 SRPoint& p_gt = gt_pc->points[i];
00040 SRPoint& p_seg = seg_pc->points[i];
00041
00042 bool gt_nan = !(!isnan(p_gt.x) && !isnan(p_gt.y) && !isnan(p_gt.z));
00043 bool seg_nan = !(!isnan(p_seg.x) && !isnan(p_seg.y) && !isnan(p_seg.z));
00044
00045 if (gt_nan && seg_nan)
00046 tn++;
00047 else if (gt_nan && !seg_nan)
00048 fp++;
00049 else if (!gt_nan && seg_nan)
00050 fn++;
00051 else if ( p_gt.x == p_seg.x && p_gt.y == p_seg.y && p_gt.z == p_seg.z )
00052 tp++;
00053 else
00054 error++;
00055 }
00056 }
00057
00058 if (error>0){
00059 ROS_ERROR_STREAM(endl << " ERROR non matching points: " << error << " - are the point clouds really aligned?");
00060 }
00061 }
00062
00063 std::string getTimeStampString(const ros::Time& t) {
00064 double sec = t.toSec();
00065
00066 char c[255];
00067 sprintf(c, "%.3f\n", sec);
00068
00069 return string(c);
00070 }
00071
00072 void writeCSV( std::ofstream& out, std::vector<std::string>& labels, std::vector<std::vector<double> >& values ) {
00073 assert (out.is_open());
00074
00075 out.precision(25);
00076
00077 out << "#";
00078 for (vector<string >::iterator it = labels.begin(); it != labels.end(); it++) {
00079 out << *it << " ";
00080 }
00081 out << endl;
00082
00083 for (vector<vector<double> >::iterator it = values.begin(); it != values.end(); it++) {
00084 for (vector<double>::iterator it2 = it->begin(); it2 != it->end(); it2++) {
00085 out << *it2 << " ";
00086 }
00087 out << endl;
00088 }
00089 }
00090
00091 void get_ochs_result_file_list(const string& ochs_result_dir, vector<string>& dense_recons) {
00092 cout << "Listing Ochs results: " << ochs_result_dir << endl;
00093 path ochs_dir(ochs_result_dir);
00094 directory_iterator end_iter;
00095
00096
00097 dense_recons.clear();
00098
00099 if ( !exists(ochs_dir) || !is_directory(ochs_dir)) {
00100 cout << "ERROR: directory does not exist" << endl;
00101 return;
00102 }
00103 for( directory_iterator dir_iter(ochs_dir) ; dir_iter != end_iter ; ++dir_iter) {
00104 if (!is_regular_file(dir_iter->status()) ) {
00105 continue;
00106 }
00107
00108 const path& p = dir_iter->path();
00109 if (p.leaf().string().find("dense") != string::npos) {
00110 dense_recons.push_back(p.string());
00111 }
00112 }
00113
00114 std::sort(dense_recons.begin(), dense_recons.end());
00115
00116 for (int i = 0; i < dense_recons.size(); i++) {
00117 cout << " " << dense_recons[i] << endl;
00118 }
00119
00120 }
00121
00122
00123 void getOchsSegments(const cv::Mat img, SRPointCloud::ConstPtr orig_pc, vector<SRPointCloud::Ptr>& segments) {
00124
00125 segments.clear();
00126
00127 map<string,SRPointCloud::Ptr> colors;
00128 int idx=0;
00129 for (int h=0; h<img.rows; h++) {
00130 for (int w=0; w<img.cols; w++) {
00131 cv::Vec3b c = img.at<cv::Vec3b>(h,w);
00132 ostringstream hash;
00133 hash << static_cast<int>(c[0]) << static_cast<int>(c[1]) << static_cast<int>(c[2]);
00134 map<string,SRPointCloud::Ptr>::iterator it = colors.find(hash.str());
00135 if (it == colors.end()) {
00136 cout << "create new point cloud #" << colors.size() << " for hash " << hash.str() << endl;
00137 SRPointCloud::Ptr pc(new SRPointCloud);
00138 pc->width = img.cols;
00139 pc->height = img.rows;
00140 for (int h=0; h<img.rows; h++) {
00141 for (int w=0; w<img.cols; w++) {
00142 SRPoint po; po.data[0] = po.data[1] = po.data[2] = std::numeric_limits<float>::quiet_NaN();
00143 pc->points.push_back(po);
00144 }
00145 }
00146
00147
00148
00149
00150
00151
00152 colors[hash.str()] = pc;
00153 }
00154 SRPointCloud::Ptr pc = colors[hash.str()];
00155 SRPoint &p = pc->points.at(idx);
00156 p = orig_pc->points.at(idx);
00157
00158 idx++;
00159 }
00160 }
00161
00162 for (map<string,SRPointCloud::Ptr>::iterator it = colors.begin(); it != colors.end(); it++) {
00163 segments.push_back(it->second);
00164 }
00165
00166 }
00167
00168 void process(rosbag::Bag& results_bag, rosbag::Bag& gt_bag, const path& output_dir, const string& ochs_result_dir="") {
00169 static const int rb_max = 5;
00170
00171 vector<string> labels;
00172 labels.push_back("time");
00173 labels.push_back("tp");
00174 labels.push_back("tn");
00175 labels.push_back("fp");
00176 labels.push_back("fn");
00177
00178 map<string, vector<vector<double> > > results;
00179
00180
00181 std::vector<std::string> topics;
00182
00183 topics.push_back("/camera/depth_registered/points");
00184 topics.push_back("/real_selected_points");
00185
00186 std::vector<std::string> segment_topics;
00187 segment_topics.push_back("/shape_recons/segment_rb");
00188 segment_topics.push_back("/shape_recons/segment_depth_rb");
00189 segment_topics.push_back("/shape_recons/segment_color_rb");
00190 segment_topics.push_back("/shape_recons/segment_ext_d_and_c_rb");
00191 segment_topics.push_back("/shape_recons/segment_ext_d_rb");
00192 segment_topics.push_back("/shape_recons/segment_ext_c_rb");
00193
00194 segment_topics.push_back("/shape_recons/segment_naive_rb");
00195 segment_topics.push_back("/shape_recons/segment_naive_depth_rb");
00196 segment_topics.push_back("/shape_recons/segment_naive_color_rb");
00197
00198 for (vector<string>::iterator it = segment_topics.begin(); it != segment_topics.end(); it++) {
00199 for (int i = 2; i < rb_max; i++) {
00200 stringstream ss;
00201 ss << *it << i;
00202 topics.push_back(ss.str());
00203
00204 cout << " adding topic " << ss.str() << endl;
00205 }
00206 }
00207 cout << endl;
00208
00209 rosbag::View view_results(results_bag, rosbag::TopicQuery(topics));
00210 rosbag::View view_gt(gt_bag, rosbag::TopicQuery(topics));
00211
00212
00213 vector<rosbag::View::const_iterator> gt_pc_list;
00214 vector<rosbag::View::const_iterator> res_full_pc_list;
00215 map< string, vector<rosbag::View::const_iterator> > res_seg_pc_map;
00216
00217
00218 cout << "COLLECTING ground truth " << endl;
00219 rosbag::View::const_iterator git = view_gt.begin();
00220 for (; git != view_gt.end(); git++) {
00221
00222 rosbag::MessageInstance const& m = *git;
00223 assert (m.getTopic() == "/real_selected_points");
00224
00225 sensor_msgs::PointCloud2ConstPtr gt_msg = m.instantiate<sensor_msgs::PointCloud2>();
00226 cout << " found gt at time: " << gt_msg->header.stamp << endl;
00227 gt_pc_list.push_back(git);
00228 }
00229 cout << endl;
00230
00231 cout << "COLLECTING segmentation results" << endl;
00232 rosbag::View::const_iterator rit = view_results.begin();
00233 for (; rit != view_results.end(); rit++) {
00234
00235 rosbag::MessageInstance const& m = *rit;
00236 if (m.getTopic() == "/camera/depth_registered/points") {
00237 sensor_msgs::PointCloud2ConstPtr pc_msg = m.instantiate<sensor_msgs::PointCloud2>();
00238 res_full_pc_list.push_back(rit);
00239 cout << " Found PC at time " << pc_msg->header.stamp << endl;
00240 } else {
00241 sensor_msgs::PointCloud2ConstPtr pc_msg = m.instantiate<sensor_msgs::PointCloud2>();
00242 res_seg_pc_map[getTimeStampString(pc_msg->header.stamp)].push_back(rit);
00243 cout << " Found segment " << m.getTopic() << " at time " << pc_msg->header.stamp << endl;
00244
00245 SRPointCloud::Ptr seg_pc(new SRPointCloud);
00246 rosbag::MessageInstance const& m = *rit;
00247 sensor_msgs::PointCloud2ConstPtr seg_msg = m.instantiate<sensor_msgs::PointCloud2>();
00248 pcl::fromROSMsg(*seg_msg, *seg_pc);
00249
00250
00251
00252
00253
00254
00255 }
00256 }
00257
00258 cout << "SUMMARY" << endl;
00259 cout << " ground truth: " << gt_pc_list.size() << " full point clouds " << endl;
00260 cout << " segments: " << res_full_pc_list.size() << " full point clouds " << endl;
00261 cout << " " << res_seg_pc_map.size() << " entries " << endl;
00262
00263 cout << "---------------------------" << endl << endl;
00264
00265 if (gt_pc_list.size() != res_full_pc_list.size()) {
00266 ROS_WARN_STREAM("Ground truth and result bag have different sizes!");
00267 if (gt_pc_list.size() > res_full_pc_list.size())
00268 ROS_WARN_STREAM("Assuming results contain a subset, but start at the same time step");
00269 else {
00270 ROS_ERROR_STREAM("Results bag has more than ground truth! aborting");
00271 return;
00272 }
00273 }
00274
00275
00276
00277 vector<string> ochs_results;
00278 if (ochs_result_dir != "") {
00279 get_ochs_result_file_list(ochs_result_dir, ochs_results);
00280 }
00281
00282
00284
00285 vector<rosbag::View::const_iterator>::iterator gvit = gt_pc_list.begin();
00286 vector<rosbag::View::const_iterator>::iterator rvit = res_full_pc_list.begin();
00287 int idx=0;
00288
00289 for (; gvit != gt_pc_list.end() && rvit != res_full_pc_list.end(); gvit++, rvit++, idx++) {
00290
00291 rosbag::MessageInstance const& m_gt = **gvit;
00292 SRPointCloud::Ptr gt_pc_with_nan(new SRPointCloud);
00293 sensor_msgs::PointCloud2ConstPtr gt_msg = m_gt.instantiate<sensor_msgs::PointCloud2>();
00294 pcl::fromROSMsg(*gt_msg, *gt_pc_with_nan);
00295
00296
00297
00298
00299
00300
00301
00302
00303 rosbag::MessageInstance const& m_full = **rvit;
00304 sensor_msgs::PointCloud2ConstPtr full_msg = m_full.instantiate<sensor_msgs::PointCloud2>();
00305
00306 cout << "TIME: " << full_msg->header.stamp << endl;
00307 cout << " time ground truth: " << gt_msg->header.stamp << endl;
00308
00309
00310 if (!ochs_results.empty()) {
00311 cout << " " << "Looking at Ochs result: " << idx << endl;
00312
00313 SRPointCloud::Ptr full_pc(new SRPointCloud);
00314 pcl::fromROSMsg(*full_msg, *full_pc);
00315
00316 cv::Mat ochs_result_current = cv::imread(ochs_results[idx]);
00317 vector<SRPointCloud::Ptr> ochs_segments;
00318 getOchsSegments(ochs_result_current, full_pc, ochs_segments);
00319
00320
00321 int best_ochs_segment = -1;
00322 double best_f_score = 0.;
00323 vector<double> res;
00324 for (int i= 0; i < ochs_segments.size(); i++) {
00325 int tp, tn, fp, fn, error;
00326 compareAlignedDensePointClouds(gt_pc_with_nan, ochs_segments[i], tp, tn, fp, fn, error);
00327 double precision=0.0, recall=0.0, fscore=0.0;
00328
00329 precision = ((double)tp) / (tp+fp);
00330 recall = ((double)tp) / (tp+fn);
00331 fscore = 2* ((double) precision*recall)/(precision+recall);
00332
00333 cout << " " << " ochs segment " << i << " p=" << precision << ", r=" << recall << ", f=" << fscore << endl;
00334 if (fscore>best_f_score) {
00335 best_ochs_segment = i;
00336 best_f_score = fscore;
00337 cout << " " << i << " is best so far" << endl;
00338 res.clear();
00339 res.push_back(full_msg->header.stamp.toSec());
00340 res.push_back(tp); res.push_back(tn); res.push_back(fp); res.push_back(fn);
00341 }
00342 }
00343 results[ "ochs" ].push_back(res);
00344
00345
00346 cv::imshow("ochs segmentation", ochs_result_current);
00347 for (int i= 0; i < ochs_segments.size(); i++) {
00348 stringstream nm;
00349 nm << "segment " << i;
00350 SRPointCloud::Ptr sgm_pc = ochs_segments[i];
00351 cv::Mat sgm_dm(480,640,CV_32FC1);
00352 OrganizedPC2DepthMap(sgm_pc, sgm_dm);
00353 cv::imshow(nm.str(), sgm_dm);
00354 }
00355
00356
00357
00358 }
00359
00360
00361
00362 vector<rosbag::View::const_iterator>& segment_list = res_seg_pc_map[getTimeStampString(full_msg->header.stamp)];
00363 cout << " Segment list contains " << segment_list.size() << " points" << endl;
00364 if (segment_list.empty()) {
00365 ROS_WARN_STREAM("No segments at time " << full_msg->header.stamp);
00366 continue;
00367 }
00368
00369 vector<rosbag::View::const_iterator>::iterator svit = segment_list.begin();
00370 for (; svit != segment_list.end(); svit++) {
00371
00372 SRPointCloud::Ptr seg_pc(new SRPointCloud);
00373 rosbag::MessageInstance const& m_res = **svit;
00374 sensor_msgs::PointCloud2ConstPtr seg_msg = m_res.instantiate<sensor_msgs::PointCloud2>();
00375 pcl::fromROSMsg(*seg_msg, *seg_pc);
00376
00377
00378
00379
00380
00381
00382 int tp,fp,tn,fn,error;
00383 compareAlignedDensePointClouds(gt_pc_with_nan, seg_pc, tp, tn, fp, fn, error);
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404 cout << m_res.getTopic() << endl;
00405 cout << " tp= " << tp;
00406 cout << " tn= " << tn;
00407 cout << " fp= " << fp;
00408 cout << " fn= " << fn << endl;
00409
00410 vector<double> res;
00411 res.push_back(seg_msg->header.stamp.toSec());
00412 res.push_back(tp); res.push_back(tn); res.push_back(fp); res.push_back(fn);
00413 results[ m_res.getTopic() ].push_back(res);
00414 }
00415 }
00416
00417
00418 for (map<string, vector<vector<double> > >::iterator it = results.begin(); it != results.end(); it++) {
00419 cout << "Writing out results for " << it->first << endl;
00420 cout << " has " << it->second.size() << " entries" << endl;
00421
00422 vector<string> strs;
00423 boost::split(strs, it->first,boost::is_any_of("/"));
00424
00425 path output_path = output_dir;;
00426 output_path /= strs[strs.size()-1] + ".txt";
00427
00428 cout <<" Output path: " << output_path.string() << endl;
00429
00430 ofstream output;
00431 output.open(output_path.string().c_str());
00432 writeCSV(output, labels, it->second);
00433 output.close();
00434 }
00435 }
00436
00437 }
00438
00439
00440 int main(int argc, char** argv)
00441 {
00442 if (argc < 3) {
00443 cout << "Usage: rosrun statistics <full_pc_and_segmentation> <ground_truth> [ochs_results_dir]" << endl;
00444 return 0;
00445 }
00446
00447 path results_bag_path(argv[1]);
00448 if (!exists(results_bag_path.string())) {
00449 cerr << " results bag does not exist: " << results_bag_path.string() << endl;
00450 return -1;
00451 }
00452 path gt_bag_path(argv[2]);
00453 if (!exists(gt_bag_path.string())) {
00454 cerr << " ground truth bag does not exist: " << gt_bag_path.string() << endl;
00455 return -1;
00456 }
00457
00458 string ochs_result_dir("");
00459 if (argc > 3) {
00460 ochs_result_dir = argv[3];
00461 }
00462
00463
00464 path output_dir = results_bag_path.parent_path();
00465 output_dir /= results_bag_path.stem().string();
00466
00467 if (!boost::filesystem::exists(output_dir)) {
00468 if (!boost::filesystem::create_directories(output_dir)) {
00469 ROS_ERROR_NAMED("statistics", "Output directory for statistics does not exist and cannot be created!");
00470 return -1;
00471 }
00472 }
00473
00474
00475 rosbag::Bag results_bag;
00476 results_bag.open(results_bag_path.string(), rosbag::bagmode::Read);
00477
00478 rosbag::Bag gt_bag;
00479 gt_bag.open(gt_bag_path.string(), rosbag::bagmode::Read);
00480
00481 omip::process(results_bag, gt_bag, output_dir, ochs_result_dir);
00482 results_bag.close();
00483 gt_bag.close();
00484
00485 return (0);
00486 }