$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
mesh-complex-pinv-curv-skel.cc
1 // Copyright (C) 2008-2010, 2012-2013 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 
32 
33 // FIXME: Factor with mesh-complex-max-curv-skel.cc.
34 
35 #include <iostream>
36 
37 #include <mln/core/image/complex_image.hh>
38 #include <mln/core/image/complex_neighborhoods.hh>
39 
40 #include <mln/core/image/dmorph/image_if.hh>
41 #include <mln/core/image/dmorph/mutable_extension_ima.hh>
42 #include <mln/core/routine/mutable_extend.hh>
43 #include <mln/data/paste.hh>
44 
45 #include <mln/value/label_16.hh>
46 
47 #include <mln/labeling/regional_minima.hh>
48 #include <mln/morpho/closing/area.hh>
49 
50 #include <mln/topo/is_n_face.hh>
51 #include <mln/topo/is_simple_cell.hh>
52 #include <mln/topo/detach_cell.hh>
53 #include <mln/topo/skeleton/breadth_first_thinning.hh>
54 
55 #include <mln/math/pi.hh>
56 
57 #include <mln/util/timer.hh>
58 
59 #include <mln/io/off/load.hh>
60 /* FIXME: Remove as soon as mln::io::off::save is able to save a
61  morphed mln::complex_image (i.e., seen through image_if). */
62 #include "save_bin_alt.hh"
63 
64 #include "misc.hh"
65 
66 
67 int
68 main(int argc, char* argv[])
69 {
70  if (argc != 4)
71  {
72  std::cerr << "usage: " << argv[0] << " input.off lambda output.off"
73  << std::endl;
74  std::exit(1);
75  }
76 
77  std::string input_filename = argv[1];
78  unsigned lambda = atoi(argv[2]);
79  std::string output_filename = argv[3];
80 
81  /*----------------.
82  | Complex image. |
83  `----------------*/
84 
85  // Curvature image type.
86  typedef mln::float_2complex_image3df float_ima_t;
87  // Dimension of the image (and therefore of the complex).
88  static const unsigned D = float_ima_t::dim;
89  // Geometry of the image.
90  typedef mln_geom_(float_ima_t) G;
91 
92  mln::bin_2complex_image3df bin_input;
93  mln::io::off::load(bin_input, input_filename);
94  // FIXME: mln::complex_image should provide shorcuts for this.
95  const mln::topo::complex<D>& cplx = bin_input.domain().cplx();
96  for (unsigned n = 0; n <= D; ++n)
97  std::cout << cplx.nfaces_of_dim(n) << ' ' << n << "-faces" << std::endl;
98 
99  std::pair<float_ima_t, float_ima_t> curv =
100  mln::geom::mesh_curvature(bin_input.domain());
101 
102  // Compute the pseudo_inverse curvature at each vertex.
103  float_ima_t float_ima(bin_input.domain());
104  mln::p_n_faces_fwd_piter<D, G> v(float_ima.domain(), 0);
105  for_all(v)
106  {
107  float h = (curv.first(v) + curv.second(v)) / 2;
108  // Pseudo-inverse curvature.
109  float h_inv =
110  1 / float(mln::math::pi) * (atanf(-h) + float(mln::math::pi) / 2);
111  float_ima(v) = h_inv;
112  }
113 
114  // Values on triangles.
115  mln::p_n_faces_fwd_piter<D, G> t(float_ima.domain(), 2);
116  // For each triangle (2-face) T, iterate on the the set of vertices
117  // (0-faces) transitively adjacent to T.
118  typedef mln::complex_m_face_neighborhood<D, G> adj_vertices_nbh_t;
119  adj_vertices_nbh_t adj_vertices_nbh;
120  mln_niter_(adj_vertices_nbh_t) adj_v(adj_vertices_nbh, t);
121  /* FIXME: We should be able to pass this value (m) either at the
122  construction of the neighborhood or at the construction of the
123  iterator. */
124  adj_v.iter().set_m(0);
125  // Iterate on triangles (2-faces).
126  for_all(t)
127  {
128  float s = 0.0f;
129  unsigned n = 0;
130  // Iterate on vertices (0-faces).
131  for_all(adj_v)
132  {
133  s += float_ima(adj_v);
134  ++n;
135  }
136  float_ima(t) = s / n;
137  // A triangle should be adjacent to exactly two vertices.
138  mln_invariant(n <= 3);
139  }
140 
141  // Convert the float image into an unsigned image because some
142  // algorithms do not handle float images well.
143  /* FIXME: This is bad: float images should be handled as-is. At
144  least, we should use a decent conversion, using an optimal affine
145  transformation (stretching/shrinking) or any other kind of
146  interpolation. */
147  typedef mln::unsigned_2complex_image3df ima_t;
148  ima_t ima(float_ima.domain());
149  // Process only triangles, as edges and vertices are set afterwards
150  // (see FIXME below).
151  for_all(t)
152  ima(t) = 1000 * float_ima(t);
153 
154  /* FIXME: Workaround: Set maximal values on vertices and edges to
155  exclude them from the set of minimal values. */
156  for_all(v)
157  ima(v) = mln_max(mln_value_(ima_t));
158  mln::p_n_faces_fwd_piter<D, G> e(float_ima.domain(), 1);
159  for_all(e)
160  ima(e) = mln_max(mln_value_(ima_t));
161 
162  /*-----------------.
163  | Simplification. |
164  `-----------------*/
165 
167  typedef mln::complex_lower_dim_connected_n_face_neighborhood<D, G> nbh_t;
168  nbh_t nbh;
169 
170  ima_t closed_ima = mln::morpho::closing::area(ima, nbh, lambda);
171 
172  /*---------------.
173  | Local minima. |
174  `---------------*/
175 
176  typedef mln::value::label_16 label_t;
177  label_t nminima;
178 
179  /* FIXME: We should use something like `ima_t | p_n_faces(2)' instead
180  of `ima_t' here. Or better: `input' should only associate data
181  to 2-faces. */
182  typedef mln_ch_value_(ima_t, label_t) label_ima_t;
183  label_ima_t minima =
184  mln::labeling::regional_minima(closed_ima, nbh, nminima);
185 
186  typedef mln::complex_higher_neighborhood<D, G> higher_nbh_t;
187  higher_nbh_t higher_nbh;
188 
189  // Propagate minima values from triangles to edges.
190  // FIXME: Factor this inside a function.
191  mln_niter_(higher_nbh_t) adj_t(higher_nbh, e);
192  for_all(e)
193  {
194  label_t ref_adj_minimum = mln::literal::zero;
195  for_all(adj_t)
196  if (minima(adj_t) == mln::literal::zero)
197  {
198  // If E is adjacent to a non-minimal triangle, then it must
199  // not belong to a minima.
200  ref_adj_minimum = mln::literal::zero;
201  break;
202  }
203  else
204  {
205  if (ref_adj_minimum == mln::literal::zero)
206  // If this is the first minimum seen, use it as a reference.
207  ref_adj_minimum = minima(adj_t);
208  else
209  // If this is not the first time a minimum is encountered,
210  // ensure it is REF_ADJ_MINIMUM.
211  mln_assertion(minima(adj_t) == ref_adj_minimum);
212  }
213  minima(e) = ref_adj_minimum;
214  }
215 
216  // Likewise from edges to edges to vertices.
217  mln_niter_(higher_nbh_t) adj_e(higher_nbh, v);
218  for_all(v)
219  {
220  label_t ref_adj_minimum = mln::literal::zero;
221  for_all(adj_e)
222  if (minima(adj_e) == mln::literal::zero)
223  {
224  // If V is adjacent to a non-minimal triangle, then it must
225  // not belong to a minima.
226  ref_adj_minimum = mln::literal::zero;
227  break;
228  }
229  else
230  {
231  if (ref_adj_minimum == mln::literal::zero)
232  // If this is the first minimum seen, use it as a reference.
233  ref_adj_minimum = minima(adj_e);
234  else
235  // If this is not the first time a minimum is encountered,
236  // ensure it is REF_ADJ_MINIMUM.
237  mln_assertion(minima(adj_e) == ref_adj_minimum);
238  }
239  minima(v) = ref_adj_minimum;
240  }
241 
242  /*-----------------------.
243  | Initial binary image. |
244  `-----------------------*/
245 
246  /* Careful: creating ``holes'' in the surface obviously changes its
247  topology, but it may also split a single connected component in
248  two or more components, resulting in a disconnected skeleton. We
249  may want to improve this step either by forbidding any splitting,
250  or by incrementally ``digging'' a regional minima as long as no
251  splitting occurs. */
252 
253  typedef mln_ch_value_(ima_t, bool) bin_ima_t;
254  bin_ima_t surface(minima.domain());
255  mln::data::fill(surface, true);
256  // Dig ``holes'' in the surface surface by setting minima faces to false.
257  // FIXME: Use fill with an image_if instead, when available/working.
258  mln_piter_(bin_ima_t) f(minima.domain());
259  for_all(f)
260  if (minima(f) != mln::literal::zero)
261  surface(f) = false;
262 
263  /*-----------.
264  | Skeleton. |
265  `-----------*/
266 
267  // ---------------- //
268  // Skeleton image. //
269  // ---------------- //
270 
271  // Predicate type: is a face a triangle (2-face)?
272  typedef mln::topo::is_n_face<mln_psite_(bin_ima_t), D> is_a_triangle_t;
273  is_a_triangle_t is_a_triangle;
274  // Surface image type, of which domain is restricted to triangles.
275  typedef mln::image_if<bin_ima_t, is_a_triangle_t> bin_triangle_only_ima_t;
276  // Surface image type, of which iteration (not domain) is restricted
277  // to triangles.
278  typedef mln::mutable_extension_ima<bin_triangle_only_ima_t, bin_ima_t>
279  bin_triangle_ima_t;
280 
281  // ------------------------ //
282  // Simple point predicate. //
283  // ------------------------ //
284 
285  // Neighborhood type returning the set of (n-1)- and (n+1)-faces
286  // adjacent to a an n-face.
287  typedef mln::complex_lower_higher_neighborhood<D, G> adj_nbh_t;
288  // Neighborhood type returning the set of (n-1)-faces adjacent to a
289  // an n-face.
290  typedef mln::complex_lower_neighborhood<D, G> lower_adj_nbh_t;
291  // Neighborhood type returning the set of (n+1)-faces adjacent to a
292  // an n-face.
293  typedef mln::complex_higher_neighborhood<D, G> higher_adj_nbh_t;
294  // Predicate type: is a triangle (2-face) simple?
295  typedef mln::topo::is_simple_cell< bin_triangle_ima_t,
296  adj_nbh_t,
297  lower_adj_nbh_t,
298  higher_adj_nbh_t >
299  is_simple_triangle_t;
300  is_simple_triangle_t is_simple_triangle;
301 
302  // ------------------------------- //
303  // Simple point detach procedure. //
304  // ------------------------------- //
305 
306  // Type of adjacency relationships between faces of immediately
307  // lower and higher dimensions.
308  adj_nbh_t adj_nbh;
309  // Functor detaching a cell.
310  mln::topo::detach_cell<bin_triangle_ima_t, adj_nbh_t> detach(adj_nbh);
311 
312  mln_concrete_(bin_ima_t) skel;
313  mln::initialize(skel, surface);
314 
315  mln::util::timer time;
316  time.start();
317  mln::data::paste
318  (mln::topo::skeleton::breadth_first_thinning
319  (mln::mutable_extend((surface | is_a_triangle).rw(), surface),
320  nbh,
321  is_simple_triangle,
322  detach),
323  skel);
324  time.stop();
325  std::cout << time.read() << " s" << std::endl;
326 
327 
328  /*---------.
329  | Output. |
330  `---------*/
331 
332  /* FIXME: This does not work (yet).
333  Use workaround mln::io::off::save_bin_alt instead (bad!)
334 
335  Moreover, even if it worked, it would not have the same meaning
336  as mln::io::off::save_bin_alt. Maybe the latter is useful, after
337  all. But we need to factor it with the code of
338  mln::io::off::save, anyway. */
339 #if 0
340  mln::io::off::save(skel | mln::pw::value(skel) == mln::pw::cst(true),
341  output_filename);
342 #endif
343  mln::io::off::save_bin_alt(skel, output_filename);
344 }