Surface Mesh Segmentation
 All Classes Namespaces Files Functions Typedefs Pages
Expectation_maximization.h
1 #ifndef CGAL_SURFACE_MESH_SEGMENTATION_EXPECTATION_MAXIMIZATION_H
2 #define CGAL_SURFACE_MESH_SEGMENTATION_EXPECTATION_MAXIMIZATION_H
3 
4 #include <vector>
5 #include <cmath>
6 #include <algorithm>
7 #include <limits>
8 
9 #include <CGAL/internal/Surface_mesh_segmentation/K_means_clustering.h>
10 #include <CGAL/assertions.h>
11 
12 #define CGAL_DEFAULT_MAXIMUM_ITERATION 10
13 #define CGAL_DEFAULT_NUMBER_OF_RUN 15
14 #define CGAL_DEFAULT_THRESHOLD 1e-3
15 
16 #define CGAL_DEFAULT_SEED 1340818006
17 
18 namespace CGAL {
20 namespace internal {
21 
26 class Expectation_maximization
27 {
28 private:
33  class Gaussian_center
34  {
35  public:
36  double mean;
37  double deviation;
38  double mixing_coefficient;
39 
40  Gaussian_center(): mean(0), deviation(0), mixing_coefficient(1.0)
41  { }
42  Gaussian_center(double mean, double deviation, double mixing_coefficient)
43  : mean(mean), deviation(deviation), mixing_coefficient(mixing_coefficient)
44  { }
51  double probability(double x) const
52  {
53  double e_over = -0.5 * std::pow((x - mean) / deviation, 2);
54  return exp(e_over) / deviation;
55  }
61  double probability_with_coef(double x) const
62  {
63  return probability(x) * mixing_coefficient;
64  }
66  bool operator < (const Gaussian_center& center) const
67  {
68  return mean < center.mean;
69  }
70  };
71 public:
73  enum Initialization_types
74  {
75  RANDOM_INITIALIZATION,
76  PLUS_INITIALIZATION,
77  K_MEANS_INITIALIZATION
78  };
79 
80  double final_likelihood;
81 private:
82  std::vector<Gaussian_center> centers;
83  std::vector<double> points;
84  std::vector<std::vector<double> > responsibility_matrix;
85 
86  double threshold;
87  int maximum_iteration;
88 
89  Initialization_types init_type;
90 
91 public:
105  Expectation_maximization(int number_of_centers,
106  const std::vector<double>& data,
107  Initialization_types init_type = PLUS_INITIALIZATION,
108  int number_of_runs = CGAL_DEFAULT_NUMBER_OF_RUN,
109  double threshold = CGAL_DEFAULT_THRESHOLD,
110  int maximum_iteration = CGAL_DEFAULT_MAXIMUM_ITERATION )
111  :
112  final_likelihood(-(std::numeric_limits<double>::max)()), points(data),
113  responsibility_matrix(std::vector<std::vector<double> >(number_of_centers, std::vector<double>(points.size()))),
114  threshold(threshold), maximum_iteration(maximum_iteration), init_type(init_type)
115  {
116  // For initialization with k-means, with one run
117  if(init_type == K_MEANS_INITIALIZATION)
118  {
119  K_means_clustering k_means(number_of_centers, data, K_means_clustering::PLUS_INITIALIZATION,
120  number_of_runs, maximum_iteration);
121  std::vector<int> initial_center_ids;
122  k_means.fill_with_center_ids(initial_center_ids);
123 
124  initiate_centers_from_memberships(number_of_centers, initial_center_ids);
125  calculate_clustering();
126  }
127  // For initialization with random center selection, with multiple run
128  else
129  {
130  srand(CGAL_DEFAULT_SEED);
131  calculate_clustering_with_multiple_run(number_of_centers, number_of_runs);
132  }
133  sort(centers.begin(), centers.end());
134  }
135 
140  void fill_with_center_ids(std::vector<int>& data_centers)
141  {
142  data_centers.reserve(points.size());
143  for(std::vector<double>::iterator point_it = points.begin();
144  point_it != points.end(); ++point_it)
145  {
146  double max_likelihood = 0.0;
147  int max_center = -1, center_counter = 0;
148  for(std::vector<Gaussian_center>::iterator center_it = centers.begin();
149  center_it != centers.end(); ++center_it, ++center_counter)
150  {
151  double likelihood = center_it->probability_with_coef(*point_it);
152  if(max_likelihood < likelihood)
153  {
154  max_likelihood = likelihood;
155  max_center = center_counter;
156  }
157  }
158  data_centers.push_back(max_center);
159  }
160  }
161 
166  void fill_with_probabilities(std::vector<std::vector<double> >& probabilities)
167  {
168  probabilities = std::vector<std::vector<double> >
169  (centers.size(), std::vector<double>(points.size()));
170  for(std::size_t point_i = 0; point_i < points.size(); ++point_i)
171  {
172  double total_probability = 0.0;
173  for(std::size_t center_i = 0; center_i < centers.size(); ++center_i)
174  {
175  double probability = centers[center_i].probability_with_coef(points[point_i]);
176  total_probability += probability;
177  probabilities[center_i][point_i] = probability;
178  }
179  for(std::size_t center_i = 0; center_i < centers.size(); ++center_i)
180  {
181  probabilities[center_i][point_i] /= total_probability;
182  }
183  }
184  }
185 
186 private:
191  void calculate_initial_deviations()
192  {
193  std::vector<int> member_count(centers.size(), 0);
194  for(std::vector<double>::iterator it = points.begin(); it!= points.end(); ++it)
195  {
196  int closest_center = 0;
197  double min_distance = std::abs(centers[0].mean - *it);
198  for(std::size_t i = 1; i < centers.size(); ++i)
199  {
200  double distance = std::abs(centers[i].mean - *it);
201  if(distance < min_distance)
202  {
203  min_distance = distance;
204  closest_center = i;
205  }
206  }
207  member_count[closest_center]++;
208  centers[closest_center].deviation += min_distance * min_distance;
209  }
210  for(std::size_t i = 0; i < centers.size(); ++i)
211  {
212  // There shouldn't be such case, unless same point is selected as a center twice (and it is also checked!)
213  CGAL_assertion(member_count[i] != 0);
214  centers[i].deviation = std::sqrt(centers[i].deviation / member_count[i]);
215  }
216  }
217 
222  void initiate_centers_randomly(int number_of_centers)
223  {
224  centers.clear();
225  double initial_mixing_coefficient = 1.0 / number_of_centers;
226  double initial_deviation = 0.0;
227  for(int i = 0; i < number_of_centers; ++i)
228  {
229  int random_index = rand() % points.size();
230  double initial_mean = points[random_index];
231  // if same point is choosen as a center twice, algorithm will not work
232  if(!make_center(initial_mean, initial_deviation, initial_mixing_coefficient))
233  { --i; }
234  }
235  calculate_initial_deviations();
236  }
237 
243  void initiate_centers_plus_plus(int number_of_centers)
244  {
245  centers.clear();
246  double initial_deviation = 0.0;
247  double initial_mixing_coefficient = 1.0 / number_of_centers;
248 
249  std::vector<double> distance_square_cumulative(points.size());
250  std::vector<double> distance_square(points.size(), (std::numeric_limits<double>::max)());
251  // distance_square stores squared distance to closest center for each point.
252  // say, distance_square -> [ 0.1, 0.2, 0.3, 0.4, ... ]
253  // then corresponding distance_square_cumulative -> [ 0.1, 0.3, 0.6, 1, ...]
254  double initial_mean = points[rand() % points.size()];
255  make_center(initial_mean, initial_deviation, initial_mixing_coefficient);
256 
257  for(int i = 1; i < number_of_centers; ++i)
258  {
259  double cumulative_distance_square = 0.0;
260  for(std::size_t j = 0; j < points.size(); ++j)
261  {
262  double new_distance = std::pow(centers.back().mean - points[j], 2);
263  if(new_distance < distance_square[j]) { distance_square[j] = new_distance; }
264  cumulative_distance_square += distance_square[j];
265  distance_square_cumulative[j] = cumulative_distance_square;
266  }
267 
268  double zero_one = rand() / (RAND_MAX + 1.0); // [0,1) random number
269  double random_ds = zero_one * (distance_square_cumulative.back());
270  int selection_index = std::upper_bound(distance_square_cumulative.begin(), distance_square_cumulative.end(), random_ds)
271  - distance_square_cumulative.begin();
272  double initial_mean = points[selection_index];
273  // if same point is choosen as a center twice, algorithm will not work
274  if(!make_center(initial_mean, initial_deviation, initial_mixing_coefficient))
275  { --i; }
276  }
277  calculate_initial_deviations();
278  }
279 
285  void initiate_centers_from_memberships(int number_of_centers, const std::vector<int>& initial_center_ids)
286  {
287  // Calculate mean
288  int number_of_points = initial_center_ids.size();
289  centers = std::vector<Gaussian_center>(number_of_centers);
290  std::vector<int> member_count(number_of_centers, 0);
291 
292  for(int i = 0; i < number_of_points; ++i)
293  {
294  int center_id = initial_center_ids[i];
295  centers[center_id].mean += points[i];
296  member_count[center_id] += 1;
297  }
298  // Assign mean, and mixing coef
299  for(int i = 0; i < number_of_centers; ++i)
300  {
301  centers[i].mean /= member_count[i];
302  centers[i].mixing_coefficient = member_count[i] / static_cast<double>(number_of_points);
303  }
304  // Calculate deviation
305  for(int i = 0; i < number_of_points; ++i)
306  {
307  int center_id = initial_center_ids[i];
308  centers[center_id].deviation += std::pow(points[i] - centers[center_id].mean, 2);
309  }
310  for(int i = 0; i < number_of_centers; ++i)
311  {
312  CGAL_assertion(member_count[i] != 0); // There should be no such case, each center should have at least one member.
313  centers[i].deviation = std::sqrt(centers[i].deviation / member_count[i]);
314  }
315  }
316 
322  bool is_already_center(const Gaussian_center& center) const
323  {
324  for(std::vector<Gaussian_center>::const_iterator it = centers.begin(); it != centers.end(); ++it)
325  {
326  if(it->mean == center.mean) { return true; }
327  }
328  return false;
329  }
330 
338  bool make_center(double mean, double deviation, double mixing_coefficient)
339  {
340  Gaussian_center new_center(mean, deviation, mixing_coefficient);
341  if(is_already_center(new_center)) { return false; }
342  centers.push_back(new_center);
343  return true;
344  }
345  //Main steps of EM algorithm
346 
351  void calculate_parameters()
352  {
353  for(std::size_t center_i = 0; center_i < centers.size(); ++center_i)
354  {
355  // Calculate new mean
356  double new_mean = 0.0, total_membership = 0.0;
357  for(std::size_t point_i = 0; point_i < points.size(); ++point_i)
358  {
359  double membership = responsibility_matrix[center_i][point_i];
360  new_mean += membership * points[point_i];
361  total_membership += membership;
362  }
363  new_mean /= total_membership;
364 
365  // Calculate new deviation
366  double new_deviation = 0.0;
367  for(std::size_t point_i = 0; point_i < points.size(); ++point_i)
368  {
369  double membership = responsibility_matrix[center_i][point_i];
370  new_deviation += membership * std::pow(points[point_i] - new_mean, 2);
371  }
372  new_deviation = std::sqrt(new_deviation/total_membership);
373 
374  // Assign new parameters
375  centers[center_i].mixing_coefficient = total_membership / points.size();
376  centers[center_i].deviation = new_deviation;
377  centers[center_i].mean = new_mean;
378  }
379  }
380 
386  double calculate_likelihood()
387  {
388  // The trick (merely a trick) is while calculating log-likelihood, we also refresh responsibility matrix,
389  // so that in next iteration we do not have to calculate matrix again.
390 
391  double likelihood = 0.0;
392  for(std::size_t point_i = 0; point_i < points.size(); ++point_i)
393  {
394  double total_membership = 0.0;
395  for(std::size_t center_i = 0; center_i < centers.size(); ++center_i)
396  {
397  double membership = centers[center_i].probability_with_coef(points[point_i]);
398  total_membership += membership;
399  responsibility_matrix[center_i][point_i] = membership;
400  }
401  for(std::size_t center_i = 0; center_i < centers.size(); ++center_i)
402  {
403  responsibility_matrix[center_i][point_i] /= total_membership;
404  }
405  likelihood += log(total_membership);
406  }
407  return likelihood;
408  }
409 
416  double iterate(bool first_iteration)
417  {
418  // E-step
419  // we call calculate_likelihood for E-step in first iteration because
420  // at first iteration, E-step is not done since calculate_likelihood() is not called yet.
421  if(first_iteration) { calculate_likelihood(); }
422 
423  // M-step
424  calculate_parameters();
425 
426  // Likelihood step and also E-step for next iteration
427  return calculate_likelihood(); // calculates likelihood and -also- refreshes responsibility matrix,
428  // so that we do not have to calculate it in next iteration.
429  }
430 
437  double calculate_clustering()
438  {
439  double likelihood = -(std::numeric_limits<double>::max)(), prev_likelihood;
440  int iteration_count = 0;
441  double is_converged = false;
442  while(!is_converged && iteration_count++ < maximum_iteration)
443  {
444  prev_likelihood = likelihood;
445  likelihood = iterate(iteration_count == 1);
446  double progress = likelihood - prev_likelihood;
447  is_converged = progress < threshold * std::abs(likelihood);
448  }
449  if(final_likelihood < likelihood) { final_likelihood = likelihood; }
450  return likelihood;
451  }
452 
460  void calculate_clustering_with_multiple_run(int number_of_centers, int number_of_run)
461  {
462  std::vector<Gaussian_center> max_centers;
463 
464  while(number_of_run-- > 0)
465  {
466  init_type == RANDOM_INITIALIZATION ? initiate_centers_randomly(number_of_centers)
467  : initiate_centers_plus_plus(number_of_centers);
468 
469  double likelihood = calculate_clustering();
470  if(likelihood == final_likelihood) { max_centers = centers; }
471  }
472  centers = max_centers;
473  }
474 };
475 }//namespace internal
477 }//namespace CGAL
478 #undef CGAL_DEFAULT_SEED
479 #undef CGAL_DEFAULT_MAXIMUM_ITERATION
480 #undef CGAL_DEFAULT_THRESHOLD
481 #undef CGAL_DEFAULT_NUMBER_OF_RUN
482 #endif //CGAL_SURFACE_MESH_SEGMENTATION_EXPECTATION_MAXIMIZATION_H