1 #ifndef CLUSTER_CLASSIFICATION_H 2 #define CLUSTER_CLASSIFICATION_H 3 4 //#define CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE 5 #define CGAL_CLASSIFICATION_VERBOSE 6 7 #include <CGAL/Three/Scene_item.h> 8 9 #include "Scene_points_with_normal_item.h" 10 #include "Item_classification_base.h" 11 #include "Kernel_type.h" 12 #include "Point_set_3.h" 13 14 #include <CGAL/Classification.h> 15 16 #include <iostream> 17 18 typedef CGAL::Parallel_if_available_tag Concurrency_tag; 19 20 class Cluster_classification : public Item_classification_base 21 { 22 public: 23 typedef Kernel::Point_3 Point_3; 24 typedef Kernel::Vector_3 Vector_3; 25 26 typedef Point_set::Point_map Point_map; 27 typedef Point_set::Vector_map Vector_map; 28 29 typedef CGAL::Classification::Point_set_feature_generator<Kernel, Point_set, Point_map> Generator; 30 typedef CGAL::Classification::Local_eigen_analysis Local_eigen_analysis; 31 typedef CGAL::Classification::Feature::Cluster_mean_of_feature Mean_of_feature; 32 typedef CGAL::Classification::Feature::Cluster_variance_of_feature Variance_of_feature; 33 34 typedef CGAL::Classification::Cluster<Point_set, Point_map> Cluster; 35 36 struct Point_set_with_cluster_info 37 : public CGAL::cpp98::unary_function<const Point_set::Index&, 38 std::pair<Kernel::Point_3, int> > 39 { 40 Point_set* point_set; 41 Point_set::Property_map<int>* cluster_id; 42 Point_set_with_cluster_infoPoint_set_with_cluster_info43 Point_set_with_cluster_info (Point_set* point_set, 44 Point_set::Property_map<int>& cluster_id) 45 : point_set (point_set) 46 , cluster_id (&cluster_id) 47 { } 48 operatorPoint_set_with_cluster_info49 std::pair<Kernel::Point_3, int> operator() (const Point_set::Index& idx) const 50 { 51 return std::make_pair (point_set->point(idx), (*cluster_id)[idx]); 52 } 53 54 }; 55 56 public: 57 58 Cluster_classification(Scene_points_with_normal_item* points); 59 ~Cluster_classification(); 60 item()61 CGAL::Three::Scene_item* item() { return m_points; } erase_item()62 void erase_item() { m_points = nullptr; } 63 bbox()64 CGAL::Bbox_3 bbox() 65 { 66 if (m_points->point_set()->nb_selected_points() == 0) 67 return m_points->bbox(); 68 69 CGAL::Bbox_3 bb = CGAL::bbox_3 (boost::make_transform_iterator 70 (m_points->point_set()->first_selected(), 71 CGAL::Property_map_to_unary_function<Point_set::Point_map> 72 (m_points->point_set()->point_map())), 73 boost::make_transform_iterator 74 (m_points->point_set()->end(), 75 CGAL::Property_map_to_unary_function<Point_set::Point_map> 76 (m_points->point_set()->point_map()))); 77 78 double xcenter = (bb.xmax() + bb.xmin()) / 2.; 79 double ycenter = (bb.ymax() + bb.ymin()) / 2.; 80 double zcenter = (bb.zmax() + bb.zmin()) / 2.; 81 82 double dx = bb.xmax() - bb.xmin(); 83 double dy = bb.ymax() - bb.ymin(); 84 double dz = bb.zmax() - bb.zmin(); 85 86 dx *= 10.; 87 dy *= 10.; 88 dz *= 10.; 89 90 return CGAL::Bbox_3 (xcenter - dx, ycenter - dy, zcenter - dz, 91 xcenter + dx, ycenter + dy, zcenter + dz); 92 } 93 94 void compute_features (std::size_t nb_scales, float voxel_size); 95 void add_remaining_point_set_properties_as_features(Feature_set& feature_set); 96 97 void select_random_region(); 98 add_cluster_features()99 void add_cluster_features () 100 { 101 m_eigen = boost::make_shared<Local_eigen_analysis> 102 (Local_eigen_analysis::create_from_point_clusters(m_clusters, 103 Concurrency_tag())); 104 105 m_features.template add<CGAL::Classification::Feature::Cluster_size> (m_clusters); 106 m_features.template add<CGAL::Classification::Feature::Cluster_vertical_extent> (m_clusters); 107 108 m_features.template add<CGAL::Classification::Feature::Eigenvalue> (m_clusters, *m_eigen, 0); 109 m_features.template add<CGAL::Classification::Feature::Eigenvalue> (m_clusters, *m_eigen, 1); 110 m_features.template add<CGAL::Classification::Feature::Eigenvalue> (m_clusters, *m_eigen, 2); 111 112 // m_features.template add<CGAL::Classification::Feature::Linearity> (m_clusters, *m_eigen); 113 // m_features.template add<CGAL::Classification::Feature::Planarity> (m_clusters, *m_eigen); 114 // m_features.template add<CGAL::Classification::Feature::Sphericity> (m_clusters, *m_eigen); 115 // m_features.template add<CGAL::Classification::Feature::Omnivariance> (m_clusters, *m_eigen); 116 // m_features.template add<CGAL::Classification::Feature::Anisotropy> (m_clusters, *m_eigen); 117 // m_features.template add<CGAL::Classification::Feature::Eigentropy> (m_clusters, *m_eigen); 118 // m_features.template add<CGAL::Classification::Feature::Sum_eigenvalues> (m_clusters, *m_eigen); 119 // m_features.template add<CGAL::Classification::Feature::Surface_variation> (m_clusters, *m_eigen); 120 // m_features.template add<CGAL::Classification::Feature::Verticality<Kernel> > (m_clusters, *m_eigen); 121 } 122 123 template <typename Type> try_adding_simple_feature(Feature_set & feature_set,const std::string & name)124 bool try_adding_simple_feature (Feature_set& feature_set, const std::string& name) 125 { 126 typedef typename Point_set::template Property_map<Type> Pmap; 127 bool okay = false; 128 Pmap pmap; 129 boost::tie (pmap, okay) = m_points->point_set()->template property_map<Type>(name.c_str()); 130 if (okay) 131 feature_set.template add<CGAL::Classification::Feature::Simple_feature <Point_set, Pmap> > 132 (*(m_points->point_set()), pmap, name.c_str()); 133 134 return okay; 135 } 136 add_selection_to_training_set(std::size_t label)137 void add_selection_to_training_set (std::size_t label) 138 { 139 for (Point_set::const_iterator it = m_points->point_set()->first_selected(); 140 it != m_points->point_set()->end(); ++ it) 141 { 142 int cid = m_cluster_id[*it]; 143 if (cid != -1) 144 { 145 m_clusters[cid].training() = int(label); 146 m_clusters[cid].label() = int(label); 147 } 148 } 149 150 m_points->resetSelection(); 151 if (m_index_color == 1 || m_index_color == 2) 152 change_color (m_index_color); 153 } reset_training_set(std::size_t label)154 void reset_training_set(std::size_t label) 155 { 156 for (std::size_t i = 0; i < m_clusters.size(); ++ i) 157 if (m_clusters[i].training() == int(label)) 158 m_clusters[i].training() = -1; 159 160 if (m_index_color == 1 || m_index_color == 2) 161 change_color (m_index_color); 162 } reset_training_set_of_selection()163 void reset_training_set_of_selection() 164 { 165 for (Point_set::const_iterator it = m_points->point_set()->first_selected(); 166 it != m_points->point_set()->end(); ++ it) 167 { 168 int cid = m_cluster_id[*it]; 169 if (cid != -1) 170 { 171 m_clusters[cid].training() = -1; 172 m_clusters[cid].label() = -1; 173 } 174 } 175 if (m_index_color == 1 || m_index_color == 2) 176 change_color (m_index_color); 177 } reset_training_sets()178 void reset_training_sets() 179 { 180 for (std::size_t i = 0; i < m_clusters.size(); ++ i) 181 m_clusters[i].training() = -1; 182 if (m_index_color == 1 || m_index_color == 2) 183 change_color (m_index_color); 184 } validate_selection()185 void validate_selection () 186 { 187 for (Point_set::const_iterator it = m_points->point_set()->first_selected(); 188 it != m_points->point_set()->end(); ++ it) 189 { 190 int cid = m_cluster_id[*it]; 191 if (cid != -1) 192 m_clusters[cid].training() = m_clusters[cid].label(); 193 } 194 195 m_points->resetSelection(); 196 if (m_index_color == 1 || m_index_color == 2) 197 change_color (m_index_color); 198 } 199 void train(int classifier, const QMultipleInputDialog& dialog); 200 bool run (int method, int classifier, std::size_t subdivisions, double smoothing); 201 update_color()202 void update_color () { change_color (m_index_color); } 203 void change_color (int index, float* vmin = nullptr, float* vmax = nullptr); generate_one_item(const char * name,int label)204 CGAL::Three::Scene_item* generate_one_item (const char* name, 205 int label) const 206 { 207 Scene_points_with_normal_item* points_item 208 = new Scene_points_with_normal_item; 209 210 points_item->setName (QString("%1 (%2)").arg(name).arg(m_labels[label]->name().c_str())); 211 points_item->setColor (label_qcolor (m_labels[label])); 212 for (Point_set::const_iterator it = m_points->point_set()->begin(); 213 it != m_points->point_set()->end(); ++ it) 214 { 215 int cid = m_cluster_id[*it]; 216 if (cid != -1) 217 { 218 int c = m_clusters[cid].label(); 219 if (c == label) 220 points_item->point_set()->insert (m_points->point_set()->point(*it)); 221 } 222 } 223 return points_item; 224 } generate_one_item_per_label(std::vector<CGAL::Three::Scene_item * > & items,const char * name)225 void generate_one_item_per_label(std::vector<CGAL::Three::Scene_item*>& items, 226 const char* name) const 227 { 228 std::vector<Scene_points_with_normal_item*> points_item 229 (m_labels.size(), nullptr); 230 for (std::size_t i = 0; i < m_labels.size(); ++ i) 231 { 232 points_item[i] = new Scene_points_with_normal_item; 233 points_item[i]->setName (QString("%1 (%2)").arg(name).arg(m_labels[i]->name().c_str())); 234 points_item[i]->setColor (label_qcolor (m_labels[i])); 235 items.push_back (points_item[i]); 236 } 237 238 for (Point_set::const_iterator it = m_points->point_set()->begin(); 239 it != m_points->point_set()->end(); ++ it) 240 { 241 int cid = m_cluster_id[*it]; 242 if (cid != -1) 243 { 244 int c = m_clusters[cid].label(); 245 points_item[c]->point_set()->insert (m_points->point_set()->point(*it)); 246 } 247 } 248 249 } 250 add_new_label(const char * name)251 QColor add_new_label (const char* name) 252 { 253 QColor out = Item_classification_base::add_new_label (name); 254 update_comments_of_point_set_item(); 255 return out; 256 } 257 remove_label(std::size_t position)258 void remove_label (std::size_t position) 259 { 260 Item_classification_base::remove_label (position); 261 262 for (std::size_t i = 0; i < m_clusters.size(); ++ i) 263 { 264 if (m_clusters[i].training() == int(position)) 265 m_clusters[i].training() = -1; 266 else if (m_clusters[i].training() > int(position)) 267 m_clusters[i].training() --; 268 269 if (m_clusters[i].label() == int(position)) 270 m_clusters[i].label() = -1; 271 else if (m_clusters[i].label() > int(position)) 272 m_clusters[i].label() --; 273 } 274 275 update_comments_of_point_set_item(); 276 } 277 fill_display_combo_box(QComboBox * cb,QComboBox * cb1)278 void fill_display_combo_box (QComboBox* cb, QComboBox* cb1) const 279 { 280 cb->addItem ("Clusters"); 281 Item_classification_base::fill_display_combo_box(cb, cb1); 282 } 283 284 int real_index_color() const; 285 void reset_indices(); 286 void backup_existing_colors_and_add_new(); 287 void reset_colors(); 288 289 private: 290 update_comments_of_point_set_item()291 void update_comments_of_point_set_item() 292 { 293 std::string& comments = m_points->comments(); 294 295 // Remove previously registered labels from comments 296 std::string new_comment; 297 298 std::istringstream stream (comments); 299 std::string line; 300 while (getline(stream, line)) 301 { 302 std::stringstream iss (line); 303 std::string tag; 304 if (iss >> tag && tag == "label") 305 continue; 306 new_comment += line + "\n"; 307 } 308 comments = new_comment; 309 310 comments += "label -1 unclassified\n"; 311 for (std::size_t i = 0; i < m_labels.size(); ++ i) 312 { 313 std::ostringstream oss; 314 oss << "label " << i << " " << m_labels[i]->name() << std::endl; 315 comments += oss.str(); 316 } 317 } 318 319 template <typename Classifier> run(int method,const Classifier & classifier,std::size_t subdivisions,double smoothing)320 bool run (int method, const Classifier& classifier, 321 std::size_t subdivisions, double smoothing) 322 { 323 std::vector<int> indices (m_clusters.size(), -1); 324 325 if (method == 0) 326 CGAL::Classification::classify<Concurrency_tag> (m_clusters, 327 m_labels, classifier, 328 indices); 329 else if (method == 1) 330 CGAL::Classification::classify_with_local_smoothing<Concurrency_tag> 331 (m_clusters, 332 CGAL::Identity_property_map<Cluster>(), 333 m_labels, classifier, 334 Cluster::Neighbor_query(), 335 indices); 336 else if (method == 2) 337 CGAL::Classification::classify_with_graphcut<Concurrency_tag> 338 (m_clusters, 339 CGAL::Identity_property_map<Cluster>(), 340 m_labels, classifier, 341 Cluster::Neighbor_query(), 342 smoothing, subdivisions, indices); 343 344 std::vector<int> ground_truth(m_clusters.size(), -1); 345 for (std::size_t i = 0; i < m_clusters.size(); ++ i) 346 { 347 m_clusters[i].label() = indices[i]; 348 ground_truth[i] = m_clusters[i].training(); 349 } 350 351 if (m_index_color == 1 || m_index_color == 2) 352 change_color (m_index_color); 353 354 std::cerr << "Precision, recall, F1 scores and IoU:" << std::endl; 355 356 CGAL::Classification::Evaluation eval (m_labels, ground_truth, indices); 357 358 for (std::size_t i = 0; i < m_labels.size(); ++ i) 359 { 360 std::cerr << " * " << m_labels[i]->name() << ": " 361 << eval.precision(m_labels[i]) << " ; " 362 << eval.recall(m_labels[i]) << " ; " 363 << eval.f1_score(m_labels[i]) << " ; " 364 << eval.intersection_over_union(m_labels[i]) << std::endl; 365 } 366 367 std::cerr << "Accuracy = " << eval.accuracy() << std::endl 368 << "Mean F1 score = " << eval.mean_f1_score() << std::endl 369 << "Mean IoU = " << eval.mean_intersection_over_union() << std::endl; 370 371 372 return true; 373 } 374 375 Scene_points_with_normal_item* m_points; 376 377 std::vector<Cluster> m_clusters; 378 379 Point_set::Property_map<unsigned char> m_red; 380 Point_set::Property_map<unsigned char> m_green; 381 Point_set::Property_map<unsigned char> m_blue; 382 Point_set::Property_map<CGAL::IO::Color> m_color; 383 Point_set::Property_map<int> m_cluster_id; 384 Point_set::Property_map<int> m_training; 385 Point_set::Property_map<int> m_classif; 386 387 std::vector<std::vector<float> > m_label_probabilities; 388 389 int m_index_color; 390 391 boost::shared_ptr<Local_eigen_analysis> m_eigen; 392 393 bool m_input_is_las; 394 395 }; // end class Cluster_classification 396 397 398 399 400 #endif // CLUSTER_CLASSIFICATION_H 401