$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
image3d-skel.hh
1 // Copyright (C) 2011, 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 
29 
30 #ifndef APPS_GENERIC_SKEL_IMAGE3D_SKEL_HH
31 # define APPS_GENERIC_SKEL_IMAGE3D_SKEL_HH
32 
33 #include <mln/version.hh>
34 
35 #include <mln/core/image/image3d.hh>
36 
37 #include <mln/math/sqr.hh>
38 #include <mln/math/sqrt.hh>
39 
40 
41 /*------.
42 | I/O. |
43 `------*/
44 
45 // FIXME: Do not use a dedicated I/O routines. Either convert the data
46 // to another format, or move thess routines into mln/io.
47 
48 // -------------------- //
49 // PGM (3D extension). //
50 // -------------------- //
51 
52 inline
54 load_pgm_3d(const std::string& filename)
55 {
56  std::ifstream file(filename.c_str());
57 
58  std::string magic;
59  std::getline(file, magic);
60  mln_assertion(magic == "P5");
61 
62  const size_t ncomments = 3;
63  std::string comment[ncomments];
64  std::string expected[ncomments] = { "#xdim 1", "#ydim 1", "#zdim 1" };
65  // Avoid a warning when NDEBUG is defined.
66  (void) expected;
67  for (size_t i = 0; i < ncomments; ++i)
68  {
69  std::getline(file, comment[i]);
70  mln_assertion(comment[i] == expected[i]);
71  }
72 
73  // FIXME: What is the right order? Does the number of slices
74  // appears first or last?
75  int nslis, nrows, ncols;
76  file >> nslis >> nrows >> ncols;
77 
78  // FIXME: We just ignore max_val.
79  unsigned max_val;
80  file >> max_val;
81 
82  mln::image3d<bool> ima(nslis, nrows, ncols);
83  char val;
85  for (p.sli() = 0; p.sli() < nslis; ++p.sli())
86  for (p.row() = 0; p.row() < nrows; ++p.row())
87  for (p.col() = 0; p.col() < ncols; ++p.col())
88  {
89  file.read(&val, 1);
90  ima(p) = val;
91  }
92  return ima;
93 }
94 
95 // ------------- //
96 // Raw 3D data. //
97 // ------------- //
98 
99 inline
100 void
101 save_raw_3d(const mln::image3d<bool>& ima, const std::string& filename)
102 {
103  std::ofstream file(filename.c_str());
104  mln_fwd_piter_(mln::image3d<bool>) p(ima.domain());
105  for_all(p)
106  file << static_cast<char>(ima(p) ? 0xff : 0x00);
107 }
108 
109 // ----- //
110 // VTK. //
111 // ----- //
112 
113 // Save a binary 3D image as a VTK file where each voxel is
114 // represented by a cube decomposed in 6 square faces.
115 inline
116 void
117 save_vtk_polygons(const mln::image3d<bool>& ima,
118  const std::string& filename)
119 {
120  std::ofstream file(filename.c_str());
121 
122  // Header.
123  file
124  << "# vtk DataFile Version 2.0" << std::endl
125  << "Generated by " OLN_PACKAGE_STRING " (" OLN_PACKAGE_URL ")" << std::endl
126  << "ASCII" << std::endl
127  << std::endl;
128 
129  // Number of object (foreground) sites to represent as a cube.
130  unsigned ncubes = 0;
131  mln_piter_(mln::image3d<bool>) p(ima.domain());
132  for_all(p)
133  if (ima(p))
134  ++ncubes;
135 
136  /* FIXME: The current approach duplicates some of the vertices (when
137  they are shared by two cubes or more). */
138 
139  // Points (locations).
140  file << "DATASET POLYDATA" << std::endl;
141  // Each site (voxel) is defined by the location of its 8 corners.
142  file << "POINTS " << ncubes * 8 << " float" << std::endl;
143 
144  // ``Radius'' (half-length) of a cube.
145  float r = 0.5;
146 
147  // Cubes (voxels).
148  for_all(p)
149  if (ima(p))
150  file << p[0] - r << ' ' << p[1] - r << ' ' << p[2] - r << " "
151  << p[0] + r << ' ' << p[1] - r << ' ' << p[2] - r << " "
152  << p[0] + r << ' ' << p[1] + r << ' ' << p[2] - r << " "
153  << p[0] - r << ' ' << p[1] + r << ' ' << p[2] - r << " "
154  << p[0] - r << ' ' << p[1] - r << ' ' << p[2] + r << " "
155  << p[0] + r << ' ' << p[1] - r << ' ' << p[2] + r << " "
156  << p[0] + r << ' ' << p[1] + r << ' ' << p[2] + r << " "
157  << p[0] - r << ' ' << p[1] + r << ' ' << p[2] + r
158  << std::endl;
159  file << std::endl;
160 
161  /* FIXME: The current approach duplicates some of the faces (when
162  they are shared by two squares). */
163 
164  // Decompose each cube as a set of squares (polygons).
165  file << "POLYGONS "
166  // Each square has 6 faces
167  << ncubes * 6 << ' '
168  /* Each square face requires 5 parameters: 1 for the number of
169  vertices of the polygon (always `4'), and 4 for the indices of
170  the vertices of this polygon. */
171  << ncubes * 6 * (1 + 4) << std::endl;
172 
173  // ``Draw'' the 6 faces of each cube.
174  for (unsigned i = 0; i < ncubes; ++i)
175  {
176  /* Vertices. The 8 vertices of associated with the `i'-th cube
177  have indices `8 * i' to `8 * i + 7'. */
178  unsigned v[8] =
179  { 8 * i + 0, 8 * i + 1, 8 * i + 2, 8 * i + 3,
180  8 * i + 4, 8 * i + 5, 8 * i + 6, 8 * i + 7 };
181  // Square faces.
182  file
183  << "4 " << v[0] << ' ' << v[1] << ' ' << v[2] << ' ' << v[3] << '\n'
184  << "4 " << v[4] << ' ' << v[5] << ' ' << v[6] << ' ' << v[7] << '\n'
185  << "4 " << v[0] << ' ' << v[1] << ' ' << v[5] << ' ' << v[4] << '\n'
186  << "4 " << v[2] << ' ' << v[3] << ' ' << v[7] << ' ' << v[6] << '\n'
187  << "4 " << v[0] << ' ' << v[4] << ' ' << v[7] << ' ' << v[3] << '\n'
188  << "4 " << v[1] << ' ' << v[2] << ' ' << v[6] << ' ' << v[5] << '\n'
189  << std::flush;
190  }
191 }
192 
193 // Save a binary 3D image as a VTK file where each voxel is
194 // represented by a cube (voxel polyhedron). This routine generates
195 // smaller files than save_vtk_polygons.
196 inline
197 void
198 save_vtk_polyhedrons(const mln::image3d<bool>& ima,
199  const std::string& filename)
200 {
201  std::ofstream file(filename.c_str());
202 
203  // Header.
204  file
205  << "# vtk DataFile Version 2.0" << std::endl
206  << "Generated by " OLN_PACKAGE_STRING " (" OLN_PACKAGE_URL ")" << std::endl
207  << "ASCII" << std::endl
208  << std::endl;
209 
210  // Number of object (foreground) sites to represent as a cube.
211  unsigned ncubes = 0;
212  mln_piter_(mln::image3d<bool>) p(ima.domain());
213  for_all(p)
214  if (ima(p))
215  ++ncubes;
216 
217  /* FIXME: The current approach duplicates some of the vertices (when
218  they are shared by two cubes or more). */
219 
220  // Points (locations).
221  file << "DATASET UNSTRUCTURED_GRID" << std::endl;
222  // Each site (voxel) is defined by the location of its 8 corners.
223  file << "POINTS " << ncubes * 8 << " float" << std::endl;
224 
225  // ``Radius'' (half-length) of a cube.
226  float r = 0.5;
227 
228  // Cubes (voxels).
229  for_all(p)
230  if (ima(p))
231  file << p[0] - r << ' ' << p[1] - r << ' ' << p[2] - r << " "
232  << p[0] + r << ' ' << p[1] - r << ' ' << p[2] - r << " "
233  << p[0] + r << ' ' << p[1] + r << ' ' << p[2] - r << " "
234  << p[0] - r << ' ' << p[1] + r << ' ' << p[2] - r << " "
235  << p[0] - r << ' ' << p[1] - r << ' ' << p[2] + r << " "
236  << p[0] + r << ' ' << p[1] - r << ' ' << p[2] + r << " "
237  << p[0] + r << ' ' << p[1] + r << ' ' << p[2] + r << " "
238  << p[0] - r << ' ' << p[1] + r << ' ' << p[2] + r
239  << std::endl;
240  file << std::endl;
241 
242  // Create a polyhedron for each voxel.
243  file << "CELLS " << ncubes << ' '
244  /* Each cubes (VTK_VOXEL) requires 9 parameters: 1 for the number
245  of vertices of the cube (always `8'), and 8 for the indices of
246  the vertices of this polyhedron. */
247  << ncubes * (1 + 8) << std::endl;
248 
249  // ``Draw'' the 6 faces of each cube.
250  for (unsigned i = 0; i < ncubes; ++i)
251  {
252  /* Vertices. The 8 vertices of associated with the `i'-th cube
253  have indices `8 * i' to `8 * i + 7'. */
254  unsigned v[8] =
255  { 8 * i + 0, 8 * i + 1, 8 * i + 2, 8 * i + 3,
256  8 * i + 4, 8 * i + 5, 8 * i + 6, 8 * i + 7 };
257  // The order of the vertices follows the conventions of the VTK
258  // file format specification.
259  file << "8 "
260  << v[0] << ' ' << v[1] << ' ' << v[3] << ' ' << v[2] << ' '
261  << v[4] << ' ' << v[5] << ' ' << v[7] << ' ' << v[6]
262  << std::endl;
263  }
264  file << std::endl;
265 
266  // Announce the type of cells used.
267  file << "CELL_TYPES " << ncubes << std::endl;
268  for (unsigned i = 0; i < ncubes; ++i)
269  // The VTK_VERTEX cell type has number `11'.
270  file << 11 << std::endl;
271 }
272 
273 
274 /*--------------.
275 | Subsampling. |
276 `--------------*/
277 
278 // FIXME: Again, this is a dedicated routine. Use something from
279 // subsample.
280 inline
282 subsampling_3d(const mln::image3d<bool>& input, unsigned factor)
283 {
284  using namespace mln;
285  typedef image3d<bool> I;
286  /* This subsampling procedure may not take into account pixels at the
287  end of each sloce/row/column if the corresponding dimension is
288  not a multiple of FACTOR. */
289  I output(input.nslis() / factor,
290  input.nrows() / factor,
291  input.ncols() / factor);
292  mln_piter_(I) po(output.domain());
293  for_all(po)
294  {
295  // Bounds of the browsed box.
296  point3d pi_min(po.sli() * factor,
297  po.row() * factor,
298  po.col() * factor);
299  point3d pi_max((po.sli() + 1) * factor - 1,
300  (po.row() + 1) * factor - 1,
301  (po.col() + 1) * factor - 1);
302  box3d sample_box(pi_min, pi_max);
303  size_t sample_size = sample_box.nsites();
304 
305  // Count the number of `true' and `false' values and set OUTPUT(P)
306  // to the prevalent value.
307  unsigned ntrue_vals = 0;
308  unsigned nfalse_vals = 0;
309  mln_piter_(box3d) pi(sample_box);
310  for_all(pi)
311  {
312  if (input(pi))
313  ++ntrue_vals;
314  else
315  ++nfalse_vals;
316  // Optimization: if one of the values (`true' or `false' has
317  // absolute majority, stop here).
318  if ( ntrue_vals > sample_size / 2
319  || nfalse_vals > sample_size / 2)
320  break;
321  }
322  output(po) = (ntrue_vals >= nfalse_vals);
323  }
324  return output;
325 }
326 
334 inline
335 void
336 draw_torus(mln::image3d<bool>& ima, unsigned axis_dim,
337  unsigned int_radius, unsigned ext_radius)
338 {
339  using namespace mln;
340 
341  // Check the axis dimension.
342  mln_precondition(0 <= axis_dim && axis_dim < 3);
343  // Dimensions other than the one of the axis.
344  unsigned dim1 = (axis_dim + 1) % 3;
345  unsigned dim2 = (axis_dim + 2) % 3;
346 
347  // Mid radius.
348  const unsigned mid_radius = (ext_radius + int_radius) / 2;
349  // Radius of the section of the torus.
350  const unsigned torus_radius = (ext_radius - int_radius) / 2 ;
351 
352  point3d bb_min;
353  bb_min[axis_dim] = -torus_radius;
354  bb_min[dim1] = -ext_radius;
355  bb_min[dim2] = -ext_radius;
356 
357  point3d bb_max;
358  bb_max[axis_dim] = torus_radius;
359  bb_max[dim1] = ext_radius;
360  bb_max[dim2] = ext_radius;
361 
362  // Ensure the box of IMA is large enough.
363  mln_assertion(ima.has(bb_min));
364  mln_assertion(ima.has(bb_max));
365 
366  box3d bb(bb_min, bb_max);
367  mln_piter_(box3d) p(bb);
368  for_all(p)
369  {
370  unsigned x = math::sqrt((math::sqr(torus_radius) - math::sqr(p[axis_dim])));
371  unsigned lo_radius = mid_radius - x;
372  unsigned hi_radius = mid_radius + x;
373  unsigned r2 = math::sqr(p[dim1]) + math::sqr(p[dim2]);
374  if (math::sqr(lo_radius) < r2 && r2 < math::sqr(hi_radius))
375  ima(p) = true;
376  }
377 }
378 
380 inline
382 make_triple_torus(mln::def::coord half_len = 100)
383 {
384  using namespace mln;
385 
386  box3d b = make::box3d(-half_len, -half_len, -half_len,
387  +half_len, +half_len, +half_len);
388  image3d<bool> ima(b);
389  data::fill(ima, false);
390  border::fill(ima, false);
391  def::coord ext_radius = half_len;
392  def::coord int_radius = ext_radius * 0.7;
393  draw_torus(ima, 0, int_radius, ext_radius);
394  draw_torus(ima, 1, int_radius, ext_radius);
395  draw_torus(ima, 2, int_radius, ext_radius);
396  return ima;
397 }
398 
399 #endif // ! APPS_GENERIC_SKEL_IMAGE3D_SKEL_HH