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