$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
mesh-complex-skel.cc
1 // Copyright (C) 2008, 2009, 2010 EPITA Research and Development
2 // Laboratory (LRDE)
3 //
4 // This file is part of Olena.
5 //
6 // Olena is free software: you can redistribute it and/or modify it under
7 // the terms of the GNU General Public License as published by the Free
8 // Software Foundation, version 2 of the License.
9 //
10 // Olena is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Olena. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // As a special exception, you may use this file as part of a free
19 // software project without restriction. Specifically, if other files
20 // instantiate templates or use macros or inline functions from this
21 // file, or you compile this file and link it with other files to produce
22 // an executable, this file does not by itself cause the resulting
23 // executable to be covered by the GNU General Public License. This
24 // exception does not however invalidate any other reasons why the
25 // executable file might be covered by the GNU General Public License.
26 
31 
32 #include <iostream>
33 
34 #include <mln/core/image/complex_image.hh>
35 #include <mln/core/image/complex_neighborhoods.hh>
36 
37 #include <mln/core/image/dmorph/image_if.hh>
38 #include <mln/core/image/dmorph/mutable_extension_ima.hh>
39 #include <mln/core/routine/mutable_extend.hh>
40 #include <mln/data/paste.hh>
41 
42 #include <mln/value/label_16.hh>
43 
44 #include <mln/labeling/regional_minima.hh>
45 #include <mln/morpho/closing/area.hh>
46 
47 #include <mln/topo/is_n_face.hh>
48 #include <mln/topo/is_simple_cell.hh>
49 #include <mln/topo/detach_cell.hh>
50 #include <mln/topo/skeleton/breadth_first_thinning.hh>
51 
52 #include <mln/io/off/load.hh>
53 /* FIXME: Remove as soon as mln::io::off::save is able to save a
54  morphed mln::complex_image (i.e., seen through image_if). */
55 #include "save_bin_alt.hh"
56 
57 
58 int
59 main(int argc, char* argv[])
60 {
61  if (argc != 4)
62  {
63  std::cerr << "usage: " << argv[0] << " input.off lambda output.off"
64  << std::endl;
65  std::exit(1);
66  }
67 
68  std::string input_filename = argv[1];
69  unsigned lambda = atoi(argv[2]);
70  std::string output_filename = argv[3];
71 
72  /*----------------.
73  | Complex image. |
74  `----------------*/
75 
76  // Image type.
77  typedef mln::float_2complex_image3df float_input_t;
78  // Dimension of the image (and therefore of the complex).
79  static const unsigned D = float_input_t::dim;
80  // Geometry of the image.
81  typedef mln_geom_(float_input_t) G;
82 
83  float_input_t float_input;
84  mln::io::off::load(float_input, input_filename);
85 
86  // Convert the float image into an unsigned image because some
87  // algorithms do not handle float images well.
88  /* FIXME: This is bad: float images should be handled as-is. At
89  least, we should use a decent conversion, using an optimal affine
90  transformation (stretching/shrinking) or any other kind of
91  interpolation. */
92  typedef mln::unsigned_2complex_image3df ima_t;
93  ima_t input(float_input.domain());
94  // Process only triangles, as edges and vertices are set afterwards
95  // (see FIXME below).
96  mln::p_n_faces_fwd_piter<D, G> t(input.domain(), 2);
97  for_all(t)
98  input(t) = 1000 * float_input(t);
99 
100  /* FIXME: Workaround: Set maximal values on vertices and edges to
101  exclude them from the set of minimal values. */
102  mln::p_n_faces_fwd_piter<D, G> v(input.domain(), 0);
103  for_all(v)
104  input(v) = mln_max(mln_value_(ima_t));
105  mln::p_n_faces_fwd_piter<D, G> e(input.domain(), 1);
106  for_all(e)
107  input(e) = mln_max(mln_value_(ima_t));
108 
109  /*-----------------.
110  | Simplification. |
111  `-----------------*/
112 
114  typedef mln::complex_lower_dim_connected_n_face_neighborhood<D, G> nbh_t;
115  nbh_t nbh;
116 
117  ima_t closed_input = mln::morpho::closing::area(input, nbh, lambda);
118 
119  /*---------------.
120  | Local minima. |
121  `---------------*/
122 
123  typedef mln::value::label_16 label_t;
124  label_t nminima;
125 
126  /* FIXME: We should use something like `ima_t | p_n_faces(2)' instead
127  of `ima_t' here. Or better: `input' should only associate data
128  to 2-faces. */
129  typedef mln_ch_value_(ima_t, label_t) label_ima_t;
130  label_ima_t minima =
131  mln::labeling::regional_minima(closed_input, nbh, nminima);
132 
133  typedef mln::complex_higher_neighborhood<D, G> higher_nbh_t;
134  higher_nbh_t higher_nbh;
135 
136  // Propagate minima values from triangles to edges.
137  // FIXME: Factor this inside a function.
138  mln_niter_(higher_nbh_t) adj_t(higher_nbh, e);
139  for_all(e)
140  {
141  label_t ref_adj_minimum = mln::literal::zero;
142  for_all(adj_t)
143  if (minima(adj_t) == mln::literal::zero)
144  {
145  // If E is adjacent to a non-minimal triangle, then it must
146  // not belong to a minima.
147  ref_adj_minimum = mln::literal::zero;
148  break;
149  }
150  else
151  {
152  if (ref_adj_minimum == mln::literal::zero)
153  // If this is the first minimum seen, use it as a reference.
154  ref_adj_minimum = minima(adj_t);
155  else
156  // If this is not the first time a minimum is encountered,
157  // ensure it is REF_ADJ_MINIMUM.
158  mln_assertion(minima(adj_t) == ref_adj_minimum);
159  }
160  minima(e) = ref_adj_minimum;
161  }
162 
163  // Likewise from edges to edges to vertices.
164  mln_niter_(higher_nbh_t) adj_e(higher_nbh, v);
165  for_all(v)
166  {
167  label_t ref_adj_minimum = mln::literal::zero;
168  for_all(adj_e)
169  if (minima(adj_e) == mln::literal::zero)
170  {
171  // If V is adjacent to a non-minimal triangle, then it must
172  // not belong to a minima.
173  ref_adj_minimum = mln::literal::zero;
174  break;
175  }
176  else
177  {
178  if (ref_adj_minimum == mln::literal::zero)
179  // If this is the first minimum seen, use it as a reference.
180  ref_adj_minimum = minima(adj_e);
181  else
182  // If this is not the first time a minimum is encountered,
183  // ensure it is REF_ADJ_MINIMUM.
184  mln_assertion(minima(adj_e) == ref_adj_minimum);
185  }
186  minima(v) = ref_adj_minimum;
187  }
188 
189  /*-----------------------.
190  | Initial binary image. |
191  `-----------------------*/
192 
193  /* Careful: creating ``holes'' in the surface obviously changes its
194  topology, but it may also split a single connected component in
195  two or more components, resulting in a disconnected skeleton. We
196  may want to improve this step either by forbidding any splitting,
197  or by incrementally ``digging'' a regional minima as long as no
198  splitting occurs. */
199 
200  typedef mln_ch_value_(ima_t, bool) bin_ima_t;
201  bin_ima_t surface(minima.domain());
202  mln::data::fill(surface, true);
203  // Dig ``holes'' in the surface surface by setting minima faces to false.
204  // FIXME: Use fill with an image_if instead, when available/working.
205  mln_piter_(bin_ima_t) f(minima.domain());
206  for_all(f)
207  if (minima(f) != mln::literal::zero)
208  surface(f) = false;
209 
210  /*-----------.
211  | Skeleton. |
212  `-----------*/
213 
214  // ---------------- //
215  // Skeleton image. //
216  // ---------------- //
217 
218  // Predicate type: is a face a triangle (2-face)?
219  typedef mln::topo::is_n_face<mln_psite_(bin_ima_t), D> is_a_triangle_t;
220  is_a_triangle_t is_a_triangle;
221  // Surface image type, of which domain is restricted to triangles.
222  typedef mln::image_if<bin_ima_t, is_a_triangle_t> bin_triangle_only_ima_t;
223  // Surface image type, of which iteration (not domain) is restricted
224  // to triangles.
225  typedef mln::mutable_extension_ima<bin_triangle_only_ima_t, bin_ima_t>
226  bin_triangle_ima_t;
227 
228  // ------------------------ //
229  // Simple point predicate. //
230  // ------------------------ //
231 
232  // Neighborhood type returning the set of (n-1)- and (n+1)-faces
233  // adjacent to a an n-face.
234  typedef mln::complex_lower_higher_neighborhood<D, G> adj_nbh_t;
235  // Neighborhood type returning the set of (n-1)-faces adjacent to a
236  // an n-face.
237  typedef mln::complex_lower_neighborhood<D, G> lower_adj_nbh_t;
238  // Neighborhood type returning the set of (n+1)-faces adjacent to a
239  // an n-face.
240  typedef mln::complex_higher_neighborhood<D, G> higher_adj_nbh_t;
241  // Predicate type: is a triangle (2-face) simple?
242  typedef mln::topo::is_simple_cell< bin_triangle_ima_t,
243  adj_nbh_t,
244  lower_adj_nbh_t,
245  higher_adj_nbh_t >
246  is_simple_triangle_t;
247  is_simple_triangle_t is_simple_triangle;
248 
249  // ------------------------------- //
250  // Simple point detach procedure. //
251  // ------------------------------- //
252 
253  // Type of adjacency relationships between faces of immediately
254  // lower and higher dimensions.
255  adj_nbh_t adj_nbh;
256  // Functor detaching a cell.
257  mln::topo::detach_cell<bin_triangle_ima_t, adj_nbh_t> detach(adj_nbh);
258 
259  mln_concrete_(bin_ima_t) skel;
260  mln::initialize(skel, surface);
261  mln::data::paste
262  (mln::topo::skeleton::breadth_first_thinning
263  (mln::mutable_extend((surface | is_a_triangle).rw(), surface),
264  nbh,
265  is_simple_triangle,
266  detach),
267  skel);
268 
269 
270  /*---------.
271  | Output. |
272  `---------*/
273 
274  /* FIXME: This does not work (yet).
275  Use workaround mln::io::off::save_bin_alt instead (bad!)
276 
277  Moreover, even if it worked, it would not have the same meaning
278  as mln::io::off::save_bin_alt. Maybe the latter is useful, after
279  all. But we need to factor it with the code of
280  mln::io::off::save, anyway. */
281 #if 0
282  mln::io::off::save(skel | mln::pw::value(skel) == mln::pw::cst(true),
283  output_filename);
284 #endif
285  mln::io::off::save_bin_alt(skel, output_filename);
286 }