$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
object_groups_with_holes.hh
1 // Copyright (C) 2010, 2011, 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 
27 #ifndef SCRIBO_FILTER_OBJECT_GROUPS_WITH_HOLES_HH
28 # define SCRIBO_FILTER_OBJECT_GROUPS_WITH_HOLES_HH
29 
34 
35 # include <sstream>
36 
37 # include <mln/core/image/image2d.hh>
38 # include <mln/core/concept/image.hh>
39 # include <mln/core/alias/neighb2d.hh>
40 # include <mln/core/routine/extend.hh>
41 # include <mln/core/image/dmorph/extended.hh>
42 
43 # include <mln/geom/ncols.hh>
44 # include <mln/geom/nrows.hh>
45 
46 # include <mln/extension/duplicate.hh>
47 # include <mln/extension/adjust_fill.hh>
48 
49 # include <mln/draw/box_plain.hh>
50 # include <mln/util/array.hh>
51 
52 # include <mln/accu/math/count.hh>
53 
54 # include <mln/fun/i2v/array.hh>
55 
56 
57 # include <scribo/core/macros.hh>
58 # include <scribo/core/component_set.hh>
59 # include <scribo/core/object_groups.hh>
60 
61 # include <scribo/primitive/group/apply.hh>
62 
63 
64 namespace scribo
65 {
66 
67  namespace filter
68  {
69 
70  using namespace mln;
71 
84  template <typename L>
85  inline
86  object_groups<L>
87  object_groups_with_holes(const object_groups<L>& components,
88  unsigned min_size);
89 
90 
91 # ifndef MLN_INCLUDE_ONLY
92 
93 
94  // Utils
95 
96  namespace internal
97  {
98 
99  unsigned my_find_root(image2d<unsigned>& parent, unsigned x)
100  {
101  if (parent.element(x) == x)
102  return x;
103  return parent.element(x) = my_find_root(parent,
104  parent.element(x));
105  }
106 
107 
108  template <typename L>
109  mln_concrete(L)
110  compute_bboxes_image(const component_set<L>& components)
111  {
112  typedef mln_psite(L) P;
113  typedef mln_dpsite(P) D;
114 
115  const L& lbl = components.labeled_image();
116  mln::extension::adjust_fill(lbl, 1, 0);
117 
118  mln_concrete(L) output;
119  initialize(output, lbl);
120  data::fill(output, 0);
121 
122  for_all_comps(i, components)
123  if (components(i).is_valid())
124  {
125  mln_box(L) b = components(i).bbox();
126  b.enlarge(1);
127 
128  unsigned
129  nrows = geom::nrows(b),
130  ncols = geom::ncols(b),
131  row_offset = lbl.delta_offset(D(+1, -ncols));
132 
133  mln_value(L) *ptr = &output(b.pmin());
134  for (unsigned row = 0; row < nrows; ++row, ptr += row_offset)
135  for (unsigned col = 0; col < ncols; ++col)
136  *ptr++ = i;
137  }
138 
139  extension::duplicate(output);
140 
141  return output;
142  }
143 
144 
145  } // end of namespace scribo::filter::internal
146 
147 
148 
149  // Implementations
150 
151  namespace impl
152  {
153 
154  namespace generic
155  {
156 
157  template <typename L>
158  inline
159  object_groups<L>
160  object_groups_with_holes(const object_groups<L>& groups,
161  unsigned min_size)
162  {
163  mln_trace("scribo::filter::impl::generic::object_groups_with_holes");
164 
165  // Grouping groups and relabel the underlying labeled image.
166  // Groups are now considered as components.
167  //
168  // The relabeling function used in group::apply is kept in
169  // order to map properly the groups and their new component
170  // id.
171  //
173  component_set<L>
174  components = primitive::group::apply(groups, group_2_comp);
175 
176  neighb2d nbh = c8();
177 
178  mln_ch_value(L,unsigned) parent, card;
179  L bboxes_ima;
180 
181  // Will store the first background component id associated
182  // to a group.
183  mln::util::array<unsigned> bg_comps(
184  value::next(components.nelements()), 0);
185 
186  // Will 'True' if a group has at least two background
187  // components.
188  mln::fun::i2v::array<bool>
189  to_keep(value::next(components.nelements()), false);
190 
191  const L& lbl = components.labeled_image();
192 
193  // init
194  {
195  // The border should be considered as the background.
196  extension::fill(lbl, literal::zero);
197 
198  initialize(parent, lbl);
199  data::fill(parent, 0u);
200  border::fill(parent, 0u);
201 
202  initialize(card, lbl);
203  data::fill(card, 1);
204  border::fill(card, 1);
205 
206  // We want to label background components only in the
207  // group/component bounding boxes. This image is a
208  // labeling constraint.
209  bboxes_ima = internal::compute_bboxes_image(components);
210 
211 // to_keep(0) = true;
212 
213  } // end of init
214 
215 
216  // 1st pass
217  {
218  mln::util::array<int> dp = positive_offsets_wrt(lbl, nbh);
219  const unsigned n_nbhs = dp.nelements();
220 
221  mln_bkd_pixter(const L) pxl(lbl); // Backward.
222  for_all(pxl)
223  {
224  unsigned p = pxl.offset();
225  if (bboxes_ima.element(p) == 0 || lbl.element(p) != literal::zero)
226  continue;
227 
228  parent.element(p) = p;
229  for (unsigned i = 0; i < n_nbhs; ++i)
230  {
231  unsigned n = p + dp[i];
232  if (bboxes_ima.element(n) == 0 || lbl.element(n) != literal::zero)
233  continue;
234 
235  unsigned r = internal::my_find_root(parent, n);
236  if (r != p)
237  {
238  parent.element(r) = p;
239  card.element(p) += card.element(r);
240  }
241  } // for_all(n)
242 
243  } // for_all(pxl)
244 
245  } // end of 1st pass
246 
247  unsigned kept = 0;
248 
249 
250  // 2nd pass
251  {
252  mln_fwd_pixter(const L) pxl(bboxes_ima); // Forward.
253  for_all(pxl)
254  {
255  unsigned p = pxl.offset();
256  if (parent.element(p) == 0) // Foreground, ignored.
257  continue;
258 
259  unsigned& parent_p = parent.element(p);
260 
261  if (parent_p != p) // Not root => propagation
262  parent_p = parent.element(parent_p);
263  else // Root
264  if (card.element(p) < min_size)
265  {
266  parent_p = 0;
267  continue;
268  }
269 
270  if (parent_p != 0 // Check parent again since
271  // parent's may have been set to
272  // 0.
273  && bboxes_ima.element(p) != literal::zero)
274  {
275  mln_value(L) group_id = bboxes_ima.element(p);
276  if (!to_keep(group_id))
277  {
278  // The group must have at least 2 different
279  // background components.
280  //
281  // This is the first background component found.
282  if (bg_comps(group_id) == 0)
283  {
284  bg_comps(group_id) = parent_p;
285  }
286  // This is the second background component found.
287  else if (bg_comps(group_id) != parent_p)
288  {
289  to_keep(group_id) = true;
290  ++kept;
291  }
292  }
293  }
294  } // for_all(pxl)
295 
296  } // end of 2nd pass
297 
298 
299  // No groups need to be invalidated.
300  if (kept == components.nelements())
301  {
302  return groups.duplicate();
303  }
304 
305  // Invalidate groups when necessary.
306  object_groups<L> output = groups.duplicate();
307  for_all_groups(c, groups)
308  if (! to_keep(group_2_comp(c)))
309  output(c).invalidate();
310 
311  return output;
312 
313  }
314 
315  } // end of namespace scribo::filter::impl::generic
316 
317  } // end of namespace scribo::filter::impl
318 
319 
320 
321  // Facade
322 
323  template <typename L>
324  inline
325  object_groups<L>
326  object_groups_with_holes(const object_groups<L>& groups,
327  unsigned min_size)
328  {
329  mln_trace("scribo::filter::object_groups_with_holes");
330 
331  mln_precondition(groups.is_valid());
332 
333  object_groups<L>
334  output = impl::generic::object_groups_with_holes(groups, min_size);
335 
336  return output;
337  }
338 
339 # endif // ! MLN_INCLUDE_ONLY
340 
341  } // end of namespace scribo::filter
342 
343 } // end of namespace scribo
344 
345 
346 #endif // ! SCRIBO_FILTER_OBJECT_GROUPS_WITH_HOLES_HH