$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
fill_object_holes.hh
1 // Copyright (C) 2010, 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 
26 #ifndef SCRIBO_POSTPROCESSING_FILL_OBJECT_HOLES_HH
27 # define SCRIBO_POSTPROCESSING_FILL_OBJECT_HOLES_HH
28 
32 
36 
37 # include <sstream>
38 
39 # include <mln/core/image/image2d.hh>
40 # include <mln/core/alias/neighb2d.hh>
41 # include <mln/core/routine/extend.hh>
42 # include <mln/core/image/dmorph/extended.hh>
43 
44 # include <mln/data/fill.hh>
45 # include <mln/data/transform.hh>
46 
47 # include <mln/extension/duplicate.hh>
48 # include <mln/extension/adjust_fill.hh>
49 
50 # include <mln/draw/box_plain.hh>
51 # include <mln/util/array.hh>
52 
53 # include <mln/accu/math/count.hh>
54 
55 # include <mln/fun/i2v/array.hh>
56 
57 # include <mln/value/int_u16.hh>
58 
59 # include <mln/labeling/blobs_and_compute.hh>
60 
61 # include <mln/logical/not.hh>
62 
63 # include <mln/transform/influence_zone_geodesic.hh>
64 
65 # include <scribo/core/macros.hh>
66 # include <scribo/core/object_groups.hh>
67 # include <scribo/primitive/group/apply.hh>
68 
69 
70 namespace scribo
71 {
72 
73  namespace postprocessing
74  {
75 
76  using namespace mln;
77 
81  template <typename I>
82  inline
83  mln_concrete(I)
84  fill_object_holes(const Image<I>& input, float ratio);
85 
86 
87  template <typename L>
88  inline
89  object_groups<L>
90  fill_object_holes(const object_groups<L>& groups,
91  unsigned min_size);
92 
93 # ifndef MLN_INCLUDE_ONLY
94 
95 
96  // Utils
97 
98  namespace internal
99  {
100 
101  unsigned my_find_root(image2d<unsigned>& parent, unsigned x)
102  {
103  if (parent.element(x) == x)
104  return x;
105  return parent.element(x) = my_find_root(parent,
106  parent.element(x));
107  }
108 
109 
110  template <typename L>
111  mln_concrete(L)
112  compute_bboxes_image(const component_set<L>& components)
113  {
114  typedef mln_psite(L) P;
115  typedef mln_dpsite(P) D;
116 
117  const L& lbl = components.labeled_image();
118  extension::adjust_fill(lbl, 1, 0);
119 
120  mln_concrete(L) output;
121  initialize(output, lbl);
122  data::fill(output, 0);
123 
124  for_all_comps(i, components)
125  if (components(i).is_valid())
126  {
127  mln_box(L) b = components(i).bbox();
128  b.enlarge(1);
129 
130  unsigned
131  nrows = b.pmax().row() - b.pmin().row() + 1,
132  ncols = b.pmax().col() - b.pmin().col() + 1,
133  row_offset = lbl.delta_offset(D(+1, -ncols));
134 
135  mln_value(L) *ptr = &output(b.pmin());
136  for (unsigned row = 0; row < nrows; ++row, ptr += row_offset)
137  for (unsigned col = 0; col < ncols; ++col)
138  *ptr++ = i;
139  }
140 
141  extension::duplicate(output);
142 
143  return output;
144  }
145 
146 
147  } // end of namespace scribo::postprocessing::internal
148 
149 
150 
151  // Implementations
152 
153  namespace impl
154  {
155 
156  namespace generic
157  {
158 
159  template <typename L>
160  inline
161  object_groups<L>
162  fill_object_holes(const object_groups<L>& groups,
163  unsigned min_size)
164  {
165  mln_trace("scribo::postprocessing::impl::generic::fill_object_holes");
166 
167  // Grouping groups and relabel the underlying labeled image.
168  // Groups are now considered as components.
169  component_set<L> components = primitive::group::apply(groups);
170 
171  neighb2d nbh = c8();
172 
173  image2d<unsigned> parent, card;
174  L bboxes_ima;
175 
177  value::next(components.nelements()), 0);
178  mln::util::array<bool> bg_comps_done(
179  value::next(components.nelements()), false);
180 
182  to_keep(value::next(components.nelements()), false);
183 
184  const L& lbl = components.labeled_image();
185 
186  // init
187  {
188  extension::fill(lbl, mln_max(mln_value(L)));
189 // extension::adjust_fill(components, nbh, mln_max(mln_value(L)));
190  initialize(parent, lbl);
191  data::fill(parent, 0u);
192 
193  initialize(card, lbl);
194  data::fill(card, 1);
195  border::fill(card, 1);
196 
197  // We want to label background components only in the
198  // group bounding boxes. Thus, this image is a labeling
199  // constraint.
200  bboxes_ima = internal::compute_bboxes_image(components);
201 
202  to_keep(0) = true;
203  }
204 
205  // 1st pass
206  {
207  mln::util::array<int> dp = positive_offsets_wrt(lbl, nbh);
208  const unsigned n_nbhs = dp.nelements();
209 
210  mln_bkd_pixter(const L) pxl(lbl); // Backward.
211  for_all(pxl)
212  {
213  unsigned p = pxl.offset();
214  if (bboxes_ima.element(p) == 0 || lbl.element(p) != literal::zero)
215  continue;
216 
217  parent.element(p) = p;
218  for (unsigned i = 0; i < n_nbhs; ++i)
219  {
220  unsigned n = p + dp[i];
221  if (bboxes_ima.element(n) == 0 || lbl.element(n) != literal::zero)
222  continue;
223 
224  unsigned r = internal::my_find_root(parent, n);
225  if (r != p)
226  {
227  parent.element(r) = p;
228  card.element(p) += card.element(r);
229  }
230  } // for_all(n)
231 
232  } // for_all(pxl)
233 
234  }
235 
236  // 2nd pass
237  {
238  unsigned kept = 0;
239  mln_fwd_pixter(const L) pxl(bboxes_ima); // Forward.
240  for_all(pxl)
241  {
242  unsigned p = pxl.offset();
243  if (parent.element(p) == 0) // Foreground, ignored.
244  continue;
245 
246  unsigned& parent_p = parent.element(p);
247 
248  if (parent_p != p) // Not root => propagation
249  parent_p = parent.element(parent_p);
250  else // Root
251  if (card.element(p) < min_size)
252  {
253  parent_p = 0;
254  continue;
255  }
256 
257  if (parent_p != 0 // Check parent again since
258  // parent's may have been set to
259  // 0.
260  && bboxes_ima.element(p) != literal::zero)
261  {
262  mln_value(L) group_id = bboxes_ima.element(p);
263  if (!bg_comps_done(group_id) && components(group_id).is_valid())
264  {
265  if (bg_comps(group_id) == 0)
266  {
267  bg_comps(group_id) = parent_p;
268  }
269  else if (bg_comps(group_id) != parent_p)
270  {
271  bg_comps_done(group_id) = true;
272  to_keep(group_id) = true;
273  ++kept;
274  }
275  }
276  }
277  }
278 
279  if (kept == components.nelements())
280  {
281  return groups.duplicate();
282  }
283 
284  object_groups<L> output = groups.duplicate();
285  for_all_groups(c, groups)
286  if (! to_keep(groups(c)))
287  output(c) = 0;
288 
289 
290  return output;
291  }
292 
293  }
294 
295  } // end of namespace scribo::postprocessing::impl::generic
296 
297  } // end of namespace scribo::postprocessing::impl
298 
299 
300 
301  // Facade
302 
303  template <typename L>
304  inline
305  object_groups<L>
306  fill_object_holes(const object_groups<L>& groups,
307  unsigned min_size)
308  {
309  mln_trace("scribo::postprocessing::fill_object_holes");
310 
311  mln_precondition(groups.is_valid());
312 
313  object_groups<L>
314  output = impl::generic::fill_object_holes(groups, min_size);
315 
316  return output;
317  }
318 
319 
320  template <typename I>
321  inline
322  mln_concrete(I)
323  fill_object_holes(const Image<I>& input_, float ratio)
324  {
325  mln_trace("scribo::postprocessing::fill_object_holes");
326 
327  const I& input = exact(input_);
328 
329  mln_precondition(input.is_valid());
330  mlc_is(mln_value(I), bool)::check();
331 
332  mln_concrete(I) output = duplicate(input);
333 
334  typedef value::int_u16 L;
335  typedef mln_ch_value(I, L) Li;
336  typedef accu::math::count<mln_site(Li)> A;
337 
338  typedef
339  mln::util::couple<Li,
340  mln::util::couple<
341  mln::util::array<unsigned>,
342  mln::util::array<A> > > res_t;
343 
344  // Holes card Image
345 
346  L nlabels;
347 
348  res_t res = labeling::blobs_and_compute(input, c8(), nlabels, A());
349 
350  mln::util::array<unsigned>& holes_card = res.second().first();
351  mln_ch_value(I, unsigned)
352  holes = data::transform(res.first(), holes_card);
353 
354 
355 
356  // Threshold Image
357 
358  I input_i = logical::not_(input);
359  res = labeling::blobs_and_compute(input_i, c8(), nlabels, A());
360 
361  mln::util::array<unsigned>& card = res.second().first();
362  for (unsigned i = 1; i < card.size(); ++i)
363  card(i) = unsigned(round(card(i) * ratio));
364 
365  mln_ch_value(I, unsigned)
366  thres = data::transform(res.first(), card);
367  thres = transform::influence_zone_geodesic(thres, c8());
368 
369 
370 
371  // Thresholding
372 
373  I hole_mask;
374  initialize(hole_mask, holes);
375  data::fill(hole_mask, false);
376  mln_piter(I) p(input.domain());
377  for_all(p)
378  if (holes(p))
379  hole_mask(p) = holes(p) < thres(p);
380 
381 
382  // Cleanup
383 
384  data::fill((output | pw::value(hole_mask)).rw(), false);
385 
386 
387  return output;
388  }
389 
390 
391 # endif // ! MLN_INCLUDE_ONLY
392 
393  } // end of namespace scribo::postprocessing
394 
395 } // end of namespace scribo
396 
397 
398 #endif // ! SCRIBO_POSTPROCESSING_FILL_OBJECT_HOLES_HH