Milena (Olena)  User documentation 2.0a Id
 All Classes Namespaces Functions Variables Typedefs Enumerator Groups Pages
mesh-complex-skel.cc
1 // Copyright (C) 2008, 2009 EPITA Research and Development Laboratory (LRDE)
2 //
3 // This file is part of Olena.
4 //
5 // Olena is free software: you can redistribute it and/or modify it under
6 // the terms of the GNU General Public License as published by the Free
7 // Software Foundation, version 2 of the License.
8 //
9 // Olena is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Olena. If not, see <http://www.gnu.org/licenses/>.
16 //
17 // As a special exception, you may use this file as part of a free
18 // software project without restriction. Specifically, if other files
19 // instantiate templates or use macros or inline functions from this
20 // file, or you compile this file and link it with other files to produce
21 // an executable, this file does not by itself cause the resulting
22 // executable to be covered by the GNU General Public License. This
23 // exception does not however invalidate any other reasons why the
24 // executable file might be covered by the GNU General Public License.
25 
30 
31 #include <iostream>
32 
33 #include <mln/core/image/complex_image.hh>
34 #include <mln/core/image/complex_neighborhoods.hh>
35 
36 #include <mln/core/site_set/p_set.hh>
37 
38 #include <mln/value/label_16.hh>
39 
40 #include <mln/labeling/regional_minima.hh>
41 #include <mln/morpho/closing/area.hh>
42 
43 #include <mln/topo/is_n_face.hh>
44 #include <mln/topo/is_simple_cell.hh>
45 #include <mln/topo/detach.hh>
46 #include <mln/topo/skeleton/breadth_first_thinning.hh>
47 
48 #include <mln/io/off/load.hh>
49 /* FIXME: Remove as soon as mln::io::off::save is able to save a
50  morphed mln::complex_image (i.e., seen through image_if). */
51 #include "save_bin_alt.hh"
52 
53 
54 int
55 main(int argc, char* argv[])
56 {
57  if (argc != 4)
58  {
59  std::cerr << "usage: " << argv[0] << " input.off lambda output.off"
60  << std::endl;
61  std::exit(1);
62  }
63 
64  std::string input_filename = argv[1];
65  unsigned lambda = atoi(argv[2]);
66  std::string output_filename = argv[3];
67 
68  /*----------------.
69  | Complex image. |
70  `----------------*/
71 
72  // Image type.
73  typedef mln::float_2complex_image3df ima_t;
74  // Dimension of the image (and therefore of the complex).
75  static const unsigned D = ima_t::dim;
76  // Geometry of the image.
77  typedef mln_geom_(ima_t) G;
78 
79  ima_t input;
80  mln::io::off::load(input, input_filename);
81 
82  /* FIXME: Workaround: Set maximal values on vertices and edges to
83  exclude them from the set of minimal values. */
84  mln::p_n_faces_fwd_piter<D, G> v(input.domain(), 0);
85  for_all(v)
86  input(v) = mln_max(float);
87  mln::p_n_faces_fwd_piter<D, G> e(input.domain(), 1);
88  for_all(e)
89  input(e) = mln_max(float);
90 
91  /*-----------------.
92  | Simplification. |
93  `-----------------*/
94 
96  typedef mln::complex_lower_dim_connected_n_face_neighborhood<D, G> nbh_t;
97  nbh_t nbh;
98 
99  ima_t closed_input = mln::morpho::closing::area(input, nbh, lambda);
100 
101  /*---------------.
102  | Local minima. |
103  `---------------*/
104 
105  typedef mln::value::label_16 label_t;
106  label_t nminima;
107 
108  /* FIXME: We should use something like `ima_t | p_n_faces(2)' instead
109  of `ima_t' here. Or better: `input' should only associate data
110  to 2-faces. */
111  typedef mln_ch_value_(ima_t, label_t) label_ima_t;
112  label_ima_t minima =
113  mln::labeling::regional_minima(closed_input, nbh, nminima);
114 
115  typedef mln::complex_higher_neighborhood<D, G> higher_nbh_t;
116  higher_nbh_t higher_nbh;
117 
118  // Propagate minima values from triangles to edges.
119  // FIXME: Factor this inside a function.
120  mln_niter_(higher_nbh_t) adj_t(higher_nbh, e);
121  for_all(e)
122  {
123  label_t ref_adj_minimum = mln::literal::zero;
124  for_all(adj_t)
125  if (minima(adj_t) == mln::literal::zero)
126  {
127  // If E is adjcent to a non-minimal triangle, then it must
128  // not belong to a minima.
129  ref_adj_minimum = mln::literal::zero;
130  break;
131  }
132  else
133  {
134  if (ref_adj_minimum == mln::literal::zero)
135  // If this is the first minimum seen, use it as a reference.
136  ref_adj_minimum = minima(adj_t);
137  else
138  // If this is not the first time a minimum is encountered,
139  // ensure it is REF_ADJ_MINIMUM.
140  mln_assertion(minima(adj_t) == ref_adj_minimum);
141  }
142  minima(e) = ref_adj_minimum;
143  }
144 
145  // Likewise from edges to edges to vertices.
146  mln_niter_(higher_nbh_t) adj_e(higher_nbh, v);
147  for_all(v)
148  {
149  label_t ref_adj_minimum = mln::literal::zero;
150  for_all(adj_e)
151  if (minima(adj_e) == mln::literal::zero)
152  {
153  // If V is adjcent to a non-minimal triangle, then it must
154  // not belong to a minima.
155  ref_adj_minimum = mln::literal::zero;
156  break;
157  }
158  else
159  {
160  if (ref_adj_minimum == mln::literal::zero)
161  // If this is the first minimum seen, use it as a reference.
162  ref_adj_minimum = minima(adj_e);
163  else
164  // If this is not the first time a minimum is encountered,
165  // ensure it is REF_ADJ_MINIMUM.
166  mln_assertion(minima(adj_e) == ref_adj_minimum);
167  }
168  minima(v) = ref_adj_minimum;
169  }
170 
171  /*-----------------------.
172  | Initial binary image. |
173  `-----------------------*/
174 
175  typedef mln_ch_value_(ima_t, bool) bin_ima_t;
176  bin_ima_t surface(minima.domain());
177  mln::data::fill(surface, true);
178  // Dig ``holes'' in the surface surface by setting minima faces to false.
179  // FIXME: Use fill with an image_if instead, when available/working.
180  mln_piter_(bin_ima_t) f(minima.domain());
181  for_all(f)
182  if (minima(f) != mln::literal::zero)
183  surface(f) = false;
184 
185  /*-----------.
186  | Skeleton. |
187  `-----------*/
188 
189  mln::topo::is_simple_cell<bin_ima_t> is_simple_p;
190  /* FIXME: Cheat! We'd like to iterate on cells of highest
191  dimension (2-cells) only, but we cannot constrain the domain of
192  the input using image_if (yet) like this
193 
194  breadth_first_thinning(surface | is_n_face<2>, nbh, is_simple_p);
195 
196  As a workaround, we use the constraint predicate of the
197  skeleton routine to restrict the iteration to 2-cells. */
198  mln::topo::is_n_face<bin_ima_t::dim> constraint_p;
199  bin_ima_t skel =
200  mln::topo::skeleton::breadth_first_thinning(surface, nbh,
201  is_simple_p,
202  mln::topo::detach<D, G>,
203  constraint_p);
204 
205  /*---------.
206  | Output. |
207  `---------*/
208 
209  /* FIXME: This does not work (yet).
210  Use workaround mln::io::off::save_bin_salt instead (bad!) */
211 #if 0
212  mln::io::off::save(skel | mln::pw::value(skel) == mln::pw::cst(true),
213  output_filename);
214 #endif
215  mln::io::off::save_bin_alt(skel, output_filename);
216 }