$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
connectivity_numbers_3d.hh
1 // Copyright (C) 2008, 2009, 2011 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 
28 
29 #ifndef TOOLS_CONNECTIVITY_NUMBERS_3D_HH
30 # define TOOLS_CONNECTIVITY_NUMBERS_3D_HH
31 
32 # include <cstdlib>
33 
34 # include <string>
35 # include <vector>
36 
37 # include <iostream>
38 # include <iomanip>
39 
40 # include <mln/core/image/dmorph/image_if.hh>
41 # include <mln/core/image/image3d.hh>
42 # include <mln/core/alias/neighb3d.hh>
43 # include <mln/value/int_u8.hh>
44 # include <mln/value/int_u32.hh>
45 
46 # include <mln/labeling/blobs.hh>
47 # include <mln/data/fill.hh>
48 # include <mln/debug/println.hh>
49 
50 
51 /* Connectivity numbers in 3D.
52 
53  N_26^*(A) =
54  0 1 2 9 10 11 18 19 20
55  3 4 5 12 * 14 21 22 23
56  6 7 8 15 16 17 24 25 26
57 
58  N_18^*(A) =
59  __ 1 __ 9 10 11 __ 19 __
60  3 4 5 12 * 14 21 22 23
61  __ 7 __ 15 16 17 __ 25 __
62 
63  N_6^*(A) =
64  __ __ __ __ 10 __ __ __ __
65  __ 4 __ 12 * 14 __ 22 __
66  __ __ __ __ 16 __ __ __ __
67 
68 
69  Note: The 6+-connectivity is the 6-connectivity associated to the
70  18-connectivity, while the 6-connectivity (with no `+') is the
71  6-connectivity associated to the 26-connectivity
72  (bertrand.07.chap).
73 
74 
75  Geodesic neighborhoods
76  Careful, when N_26^*(A) is not included in X, this result is
77  probably wrong (because all paths may not be valid). In
78  particular, this means that connectivity numbers computed on the
79  borders of a 3D image (where a point has less than 26 neighbors)
80  are wrong! How can we deal with that?
81 
82  G_6(A,X)
83  = N_6^2(A,X)
84  = union(N_6(B) inter N_26^*(A) inter X s.t. B in N_6^1(A,X))
85  = union(N_6(B) inter N_26^*(A) inter X s.t. B in N_6^*(A) inter X)
86  = union(N_6(B) inter N_26^*(A) inter X s.t. B in {4, 10, 12, 14, 16, 22})
87  = union( N_6(4) inter N_26^*(A) inter X,
88  N_6(10) inter N_26^*(A) inter X,
89  N_6(12) inter N_26^*(A) inter X,
90  N_6(14) inter N_26^*(A) inter X,
91  N_6(16) inter N_26^*(A) inter X,
92  N_6(22) inter N_26^*(A) inter X)
93  = union( {1, 3, 4, 5, 7} inter X,
94  {1, 9, 10 11, 19} inter X,
95  {3, 9, 12, 15, 21} inter X,
96  {5, 11, 14, 17, 23 inter X,
97  {7, 15, 16, 17, 25} inter X,
98  {19, 21, 22, 23, 25} inter X)
99  = {1, 3, 4, 5, 7, 9, 10, 11, 12, 14, 15, 16, 17, 19, 21, 22, 23, 25}
100  inter X
101  = N_18^*(A) inter X
102 
103  Bottom line:
104  G_6(A,X) = N_18^*(A) inter X when N_26^*(A) in included in X
105  G_6+(A,X) = N_26^*(A) inter X when N_26^*(A) in included in X
106  G_18(A,X) = N_26^*(A) inter X when N_26^*(A) in included in X
107  G_26(A,X) = N_26^*(A) inter X when N_26^*(A) in included in X
108 */
109 
110 
119 typedef std::vector<conn_number_t> conn_numbers_t;
120 
123 
124 /* A function stating whether a point of the box (-1,-1-1)-(+1,+1,+1)
125  is within the 18-c neighborhood of (0, 0, 0). */
126 bool
127 f_within_c18(const mln::point3d& p)
128 {
129  return !(mln::math::abs(p.sli()) == 1
130  && mln::math::abs(p.row()) == 1
131  && mln::math::abs(p.col()) == 1);
132 }
133 
134 // Associated functor.
135 struct within_c18 : mln::Function_v2b<within_c18>
136 {
137  typedef bool result;
138  bool operator()(const mln::point3d& p) const { return f_within_c18(p); }
139 };
140 
141 /* FIXME: Concurrent accesses to the neighborhood (reference) returned
142  by routines such as mln::c26() are not thread-safe.
143 
144  Short explanation
145 
146  This neighborhood is not thread-safe, because
147  mln::util::set<T>::freeze_() is not thread-safe.
148 
149  Long explanation
150 
151  The neighborhood is built on a window object containing an
152  mln::util::set of delta-points. However, this set is implemented
153  as a kind of variant containing an array and a set, with actual
154  data in one of these containers or the other at any time.
155  mln::util:set changes its internal representation when the nature
156  of the current operation (read or write) changes with respect to
157  the previously performed operation. Alas, with concurrent
158  accesses, this does not work as expected.
159 
160  The issue here is that mln::c26() (an similar routines) creates a
161  neighborhood containing an mln::util::set where only write
162  operations are performed at the beginning. The next read
163  operation (occurring when the neighborhood is browsed) may be
164  done currently, and mln::util::set<T>::freeze_() may then be
165  called concurrently. As it has not been designed to support
166  concurrent calls, it generally causes run-time errors (double
167  deallocations, deallocation before allocation, etc.).
168 
169  Workaround
170 
171  A simple workaround is to prevent concurrent accesses, by copying
172  the neighborhood returned by mln::c26() and other routines,
173  instead of using it directly (i.e., as a shared reference).
174 
175  Another option would be to force-freeze the mln::util::set
176  contained in the neighborhood `nbh', e.g. by calling
177  `nbh.win().std_vector()'. (We have to peform an indirect
178  invocation, as mln::util::set<T>::freeze_() is a private method
179  and cannot be called directly.)
180 
181  To do
182 
183  We must improve mln::util::set<T>::freeze_() and make it thread
184  safe. And maybe get rid of mln::c26() and others, or change
185  their implementations. */
186 
187 /*----------------------------------------------------.
188 | Sequential computation of 3D connectivity numbers. |
189 `----------------------------------------------------*/
190 
195 template <typename F>
196 conn_numbers_t
197 connectivity_numbers_3d(F f)
198 {
199  using namespace mln;
200 
201  typedef image3d<bool> I;
202  // B must be a model of mln::Box.
203  typedef mln_domain(I) B;
204  typedef mln_psite(I) P;
205 
206  B b = make::box3d(-1,-1,-1, 1,1,1);
207  I ima(b, 0);
208  P p(0, 0, 0);
209 
210  const unsigned dim = 3;
211  const unsigned max_nneighbs = mlc_pow_int(3, dim) - 1;
212  const unsigned nconfigs = mlc_pow_int(2, max_nneighbs);
213 
214  conn_numbers_t numbers(nconfigs, 0);
215 
216  typedef neighb3d N;
217  N nbh = c26();
218 
219  for (config_3d_t i = 0; i < nconfigs; ++i)
220  {
221  /* Create the local i-th configuration around P.
222 
223  Note that the value corresponding to P is always `false', to
224  prevent the connection of two components through P. */
225  data::fill(ima, false);
226  config_3d_t tmp = i;
227  mln_fwd_niter_(N) n(nbh, p);
228  for_all(n)
229  {
230  if (tmp % 2)
231  ima(n) = true;
232  tmp = tmp >> 1;
233  }
234  numbers[i] = f(ima);
235  }
236  return numbers;
237 }
238 
239 
240 // FIXME: Try to factor common parts among the 4 cases of
241 // 3D configurations below.
242 
243 /*-------------------------------------------------------------.
244 | (6, 26) configurations: 6-connected foreground, 26-connected |
245 | background. |
246 `-------------------------------------------------------------*/
247 
248 // FIXME: Factor these using mln/topo/connectivity_number_3d.hh
249 
251 connectivity_number_3d__6_26_one(const mln::image3d<bool>& ima)
252 {
253  using namespace mln;
254  typedef image3d<bool> I;
255  typedef neighb3d N;
256  typedef mln_psite_(I) P;
257  P p(0, 0, 0);
258 
259  // Create a copy of mln::c6()'s own neighborhood to avoid
260  // thread-unsafe accesses to this neighborhood (see the long
261  // explanation above).
262  mln::neighb3d nbh = c6();
263  conn_number_t unused_nl;
264  // Restrict the image to the 18-c neighborhood of P.
266  labeling::blobs(ima | within_c18(), nbh, unused_nl);
267  std::set<conn_number_t> s;
268  mln_niter_(N) n(nbh, p);
269  for_all(n)
270  if (lab(n) != 0)
271  s.insert(lab(n));
272  return s.size();
273 }
274 
275 conn_numbers_t
276 connectivity_numbers_3d__6_26()
277 {
278  return connectivity_numbers_3d(connectivity_number_3d__6_26_one);
279 }
280 
281 
282 /*-------------------------------------------------------------.
283 | (26, 6) configurations: 26-connected foreground, 6-connected |
284 | background. |
285 `-------------------------------------------------------------*/
286 
288 connectivity_number_3d__26_6_one(const mln::image3d<bool>& ima)
289 {
290  using namespace mln;
291 
292  // Create a copy of mln::c26()'s own neighborhood to avoid
293  // thread-unsafe accesses to this neighborhood (see the long
294  // explanation above).
295  mln::neighb3d nbh = c26();
296  conn_number_t n;
297  labeling::blobs(ima, nbh, n);
298  return n;
299 }
300 
301 conn_numbers_t
302 connectivity_numbers_3d__26_6()
303 {
304  return connectivity_numbers_3d(connectivity_number_3d__26_6_one);
305 }
306 
307 
308 /*---------------------------------------------------------------.
309 | (6+, 18) configurations: 6+-connected foreground, 18-connected |
310 | background. |
311 `---------------------------------------------------------------*/
312 
314 connectivity_number_3d__6p_18_one(const mln::image3d<bool>& ima)
315 {
316  using namespace mln;
317  typedef image3d<bool> I;
318  typedef neighb3d N;
319  typedef mln_psite_(I) P;
320  P p(0, 0, 0);
321 
322  // Create a copy of mln::c6()'s own neighborhood to avoid
323  // thread-unsafe accesses to this neighborhood (see the long
324  // explanation above).
325  mln::neighb3d nbh = c6();
326  conn_number_t unused_nl;
327  image3d<conn_number_t> lab = labeling::blobs(ima, nbh, unused_nl);
328  std::set<conn_number_t> s;
329  mln_niter_(N) n(nbh, p);
330  for_all(n)
331  if (lab(n) != 0)
332  s.insert(lab(n));
333  return s.size();
334 }
335 
336 conn_numbers_t
337 connectivity_numbers_3d__6p_18()
338 {
339  return connectivity_numbers_3d(connectivity_number_3d__6p_18_one);
340 }
341 
342 
343 /*---------------------------------------------------------------.
344 | (18, 6+) configurations: 18-connected foreground, 6+-connected |
345 | background. |
346 `---------------------------------------------------------------*/
347 
349 connectivity_number_3d__18_6p_one(const mln::image3d<bool>& ima)
350 {
351  using namespace mln;
352  typedef image3d<bool> I;
353  typedef neighb3d N;
354  typedef mln_psite_(I) P;
355  P p(0, 0, 0);
356 
357  // Create a copy of mln::c18()'s own neighborhood to avoid
358  // thread-unsafe accesses to this neighborhood (see the long
359  // explanation above).
360  mln::neighb3d nbh = c18();
361  conn_number_t unused_nl;
362  image3d<conn_number_t> lab = labeling::blobs(ima, nbh, unused_nl);
363  std::set<conn_number_t> s;
364  mln_niter_(N) n(nbh, p);
365  for_all(n)
366  if (lab(n) != 0)
367  s.insert(lab(n));
368  return s.size();
369 }
370 
371 conn_numbers_t
372 connectivity_numbers_3d__18_6p()
373 {
374  return connectivity_numbers_3d(connectivity_number_3d__18_6p_one);
375 }
376 
377 
378 /*----------------------.
379 | Helpers for drivers. |
380 `----------------------*/
381 
382 void
383 usage(const std::string& program)
384 {
385  std::cerr <<
386  "usage: " << program << " <nbhs>" << std::endl <<
387  "where <nbhs> is one of these values:\n\n"
388  " `6_26' : 6-c foreground, 26-c background\n"
389  " `26_6' : 26-c foreground, 6-c background\n"
390  " `6p_18' : 6+-c foreground, 18-c background\n"
391  " `18_6p' : 18-c foreground, 6+-c background\n" << std::endl;
392  std::exit(1);
393 }
394 
395 void
396 display_connectivity_numbers(const conn_numbers_t& conn_numbers)
397 {
398  for (size_t i = 0; i < conn_numbers.size(); ++i)
399  {
400  std::cout << std::setw(2) << conn_numbers[i] << ", ";
401  if (! ((i + 1) % 4)) std::cout << " ";
402  if (! ((i + 1) % 16)) std::cout << std::endl;
403  if (! ((i + 1) % 64)) std::cout << std::endl;
404  }
405 }
406 
407 #endif // ! TOOLS_CONNECTIVITY_NUMBERS_3D_HH