$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
mesh-complex-max-curv-2-collapse.cc
1 // Copyright (C) 2008-2013 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 // FIXME: Factor with mesh-complex-max-curv-skel.cc and
32 // mesh-complex-pinv-curv-skel.cc, mesh-complex-max-curv-1-collapse.cc
33 
34 #include <iostream>
35 
36 #include <mln/core/image/complex_image.hh>
37 #include <mln/core/image/complex_neighborhoods.hh>
38 
39 #include <mln/core/image/dmorph/image_if.hh>
40 #include <mln/core/image/dmorph/sub_image.hh>
41 #include <mln/core/routine/extend.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 #include <mln/morpho/dilation.hh>
50 
51 #include <mln/topo/is_n_face.hh>
52 #include <mln/topo/is_simple_pair.hh>
53 #include <mln/topo/detach_pair.hh>
54 #include <mln/topo/skeleton/priority_driven_thinning.hh>
55 
56 #include <mln/arith/revert.hh>
57 
58 #include <mln/io/vtk/load.hh>
59 #include <mln/io/vtk/save.hh>
60 
61 #include <mln/util/timer.hh>
62 
63 #include "misc.hh"
64 
65 
66 int
67 main(int argc, char* argv[])
68 {
69  if (argc != 4)
70  {
71  std::cerr << "usage: " << argv[0] << " input.vtk lambda output.vtk"
72  << std::endl;
73  std::exit(1);
74  }
75 
76  std::string input_filename = argv[1];
77  unsigned lambda = atoi(argv[2]);
78  std::string output_filename = argv[3];
79 
80  /*----------------.
81  | Complex image. |
82  `----------------*/
83 
84  // Curvature image type.
85  typedef mln::float_2complex_image3df float_ima_t;
86  // Dimension of the image (and therefore of the complex).
87  static const unsigned D = float_ima_t::dim;
88  // Geometry of the image.
89  typedef mln_geom_(float_ima_t) G;
90 
91  mln::bin_2complex_image3df bin_input;
92  mln::io::vtk::load(bin_input, input_filename);
93  // FIXME: mln::complex_image should provide shorcuts for this.
94  const mln::topo::complex<D>& cplx = bin_input.domain().cplx();
95  for (unsigned n = 0; n <= D; ++n)
96  std::cout << cplx.nfaces_of_dim(n) << ' ' << n << "-faces" << std::endl;
97 
98  std::pair<float_ima_t, float_ima_t> curv =
99  mln::geom::mesh_curvature(bin_input.domain());
100 
101  // Compute the max curvature at each vertex.
102  float_ima_t float_ima(bin_input.domain());
103  mln::p_n_faces_fwd_piter<D, G> v(float_ima.domain(), 0);
104  for_all(v)
105  {
106  // Max curvature.
107  float_ima(v) = mln::math::max(mln::math::sqr(curv.first(v)),
108  mln::math::sqr(curv.second(v)));
109  }
110 
111  // Neighborhood type returning the set of (n-1)-faces adjacent to a
112  // an n-face.
113  typedef mln::complex_lower_neighborhood<D, G> lower_adj_nbh_t;
114  lower_adj_nbh_t lower_adj_nbh;
115 
116  // Values on edges.
117  /* FIXME: We could probably simplify this by using a
118  convolution-like operator and morphers (see
119  apps/graph-morpho). */
120  mln::p_n_faces_fwd_piter<D, G> e(float_ima.domain(), 1);
121  // For each edge (1-face) E, iterate on the the set of vertices
122  // (0-faces) adjacent to E.
123  mln_niter_(lower_adj_nbh_t) adj_v(lower_adj_nbh, e);
124  // Iterate on edges (1-faces).
125  for_all(e)
126  {
127  float s = 0.0f;
128  unsigned n = 0;
129  // Iterate on vertices (0-faces).
130  for_all(adj_v)
131  {
132  s += float_ima(adj_v);
133  ++n;
134  }
135  float_ima(e) = s / n;
136  // An edge should be adjacent to exactly two vertices.
137  mln_invariant(n == 2);
138  }
139 
140  // Values on triangles.
141  /* FIXME: We could probably simplify this by using a
142  convolution-like operator and morphers (see
143  apps/graph-morpho). */
144  mln::p_n_faces_fwd_piter<D, G> t(float_ima.domain(), 2);
145  // For each triangle (2-face) T, iterate on the the set of edges
146  // (1-faces) adjacent to T.
147  mln_niter_(lower_adj_nbh_t) adj_e(lower_adj_nbh, t);
148  // Iterate on triangles (2-faces).
149  for_all(t)
150  {
151  float s = 0.0f;
152  unsigned n = 0;
153  // Iterate on edges (1-faces).
154  for_all(adj_e)
155  {
156  s += float_ima(adj_e);
157  ++n;
158  }
159  float_ima(t) = s / n;
160  // A triangle should be adjacent to exactly three edges.
161  mln_invariant(n == 3);
162  }
163 
164  // Convert the float image into an unsigned image because some
165  // algorithms do not handle float images well.
166  /* FIXME: This is bad: float images should be handled as-is. At
167  least, we should use a decent conversion, using an optimal affine
168  transformation (stretching/shrinking) or any other kind of
169  interpolation. */
170  typedef mln::unsigned_2complex_image3df ima_t;
171  ima_t ima(float_ima.domain());
172  // Process only triangles, as edges and vertices are set afterwards
173  // (see FIXME below).
174  for_all(t)
175  ima(t) = 1000 * float_ima(t);
176 
177  /*-----------------.
178  | Simplification. |
179  `-----------------*/
180 
182  typedef mln::complex_lower_dim_connected_n_face_neighborhood<D, G> nbh_t;
183  nbh_t nbh;
184 
185  // Predicate type: is a face a triangle (2-face)?
186  typedef mln::topo::is_n_face<mln_psite_(ima_t), 2> is_a_triangle_t;
187  is_a_triangle_t is_a_triangle;
188 
189  // Consider only triangles.
190  ima_t closed_ima = mln::duplicate(ima);
191  mln::data::paste(mln::morpho::closing::area(ima | is_a_triangle,
192  nbh, lambda),
193  closed_ima);
194 
195  /*---------------.
196  | Local minima. |
197  `---------------*/
198 
199  typedef mln::value::label_16 label_t;
200  label_t nminima;
201 
202  // Consider only triangles.
203  typedef mln_ch_value_(ima_t, label_t) label_ima_t;
204  label_ima_t minima;
205  mln::initialize(minima, closed_ima);
206  mln::data::paste(mln::labeling::regional_minima(closed_ima | is_a_triangle,
207  nbh, nminima),
208  minima);
209 
210  /*-----------------------.
211  | Initial binary image. |
212  `-----------------------*/
213 
214  /* Careful: creating ``holes'' in the surface obviously changes its
215  topology, but it may also split a single connected component in
216  two or more components, resulting in a disconnected skeleton. We
217  may want to improve this step either by forbidding any splitting,
218  or by incrementally ``digging'' a regional minima as long as no
219  splitting occurs. */
220 
221  typedef mln_ch_value_(ima_t, bool) bin_ima_t;
222  bin_ima_t surface(minima.domain());
223 
224  // Predicate type: is a face an edge (1-face)?
225  typedef mln::topo::is_n_face<mln_psite_(ima_t), 1> is_an_edge_t;
226  is_an_edge_t is_an_edge;
227  // Predicate type: is a face a vertex (0-face)?
228  typedef mln::topo::is_n_face<mln_psite_(ima_t), 0> is_a_vertex_t;
229  is_a_vertex_t is_a_vertex;
230 
231  // Neighborhood type returning the set of (n+1)-faces adjacent to a
232  // an n-face.
233  typedef mln::complex_higher_neighborhood<D, G> higher_adj_nbh_t;
234  higher_adj_nbh_t higher_adj_nbh;
235 
236  mln::data::fill(surface, false);
237  // Set non minima triangles to true;
238  mln::data::fill
239  ((surface |
240  (mln::pw::value(minima) == mln::pw::cst(mln::literal::zero))).rw(),
241  true);
242  // Extend non minima values from triangles to edges.
243  mln::data::paste (mln::morpho::dilation(mln::extend(surface | is_an_edge,
244  surface),
245  /* Dilations require windows,
246  not neighborhoods. */
247  higher_adj_nbh.win()),
248  surface);
249  // Extend non minima values from edges to vertices.
250  mln::data::paste(mln::morpho::dilation(mln::extend(surface | is_a_vertex,
251  surface),
252  /* Dilations require windows,
253  not neighborhoods. */
254  higher_adj_nbh.win()),
255  surface);
256 
257  /*-------------.
258  | 2-collapse. |
259  `-------------*/
260 
261  // ------------------------------- //
262  // Image restricted to triangles. //
263  // ------------------------------- //
264 
265  // Surface image type, of which domain is restricted to triangles.
266  typedef mln::image_if<bin_ima_t, is_a_triangle_t> bin_triangle_only_ima_t;
267  // Surface image type, of which iteration (not domain) is restricted
268  // to triangles.
269  typedef mln::mutable_extension_ima<bin_triangle_only_ima_t, bin_ima_t>
270  bin_triangle_ima_t;
271 
272  // ------------------------ //
273  // Simple point predicate. //
274  // ------------------------ //
275 
276  // Predicate type: is a triangle (2-face) simple?
277  typedef mln::topo::is_simple_pair< bin_triangle_ima_t,
278  lower_adj_nbh_t,
279  higher_adj_nbh_t >
280  is_simple_triangle_t;
281  is_simple_triangle_t is_simple_triangle(lower_adj_nbh, higher_adj_nbh);
282 
283  // ------------------------------- //
284  // Simple point detach procedure. //
285  // ------------------------------- //
286 
287  // Functor detaching a cell.
288  typedef mln::topo::detach_pair< bin_triangle_ima_t,
289  lower_adj_nbh_t,
290  higher_adj_nbh_t > detach_triangle_t;
291  detach_triangle_t detach_triangle(lower_adj_nbh, higher_adj_nbh);
292 
293  // ------------------------ //
294  // Thinning by 2-collapse. //
295  // ------------------------ //
296 
297  // Create a priority function (actually, an image) using the inverse
298  // of the curvature image.
299  ima_t priority = mln::arith::revert(closed_ima);
300 
301  mln_concrete_(bin_ima_t) surface_2_collapse;
302  mln::initialize(surface_2_collapse, surface);
303 
304  mln::util::timer time;
305  time.start();
306  mln::data::paste
307  (mln::topo::skeleton::priority_driven_thinning
308  (mln::mutable_extend((surface | is_a_triangle).rw(), surface),
309  nbh,
310  is_simple_triangle,
311  detach_triangle,
312  priority)
313  /* Before pasting the result of the computation into
314  SURFACE_2_COLLAPSE, re-expand its domain to the initial site
315  set, to ensure data from all faces (i.e., both the 2-faces,
316  directly processed; and the 1-faces from the extension,
317  undirectly processed). */
318  | surface.domain(),
319  surface_2_collapse);
320  time.stop();
321  std::cout << time.read() << " s" << std::endl;
322 
323  /*---------.
324  | Output. |
325  `---------*/
326 
327  mln::io::vtk::save(surface_2_collapse, output_filename);
328 }