$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
objects_with_holes.hh
1 // Copyright (C) 2009, 2010, 2011, 2013, 2014 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_OBJECTS_WITH_HOLES_HH
28 # define SCRIBO_FILTER_OBJECTS_WITH_HOLES_HH
29 
33 
34 # include <sstream>
35 
36 # include <mln/core/image/image2d.hh>
37 # include <mln/core/alias/neighb2d.hh>
38 # include <mln/core/routine/extend.hh>
39 # include <mln/core/image/dmorph/extended.hh>
40 
41 # include <mln/extension/duplicate.hh>
42 
43 # include <mln/draw/box_plain.hh>
44 # include <mln/util/array.hh>
45 
46 # include <mln/labeling/blobs_and_compute.hh>
47 
48 # include <mln/accu/math/count.hh>
49 
50 # include <mln/fun/i2v/array.hh>
51 
52 # include <mln/io/pbm/save.hh>
53 # include <mln/io/pgm/save.hh>
54 
55 # include <mln/data/convert.hh>
56 
57 # include <mln/labeling/background.hh>
58 
59 # include <scribo/core/macros.hh>
60 # include <scribo/core/component_set.hh>
61 # include <scribo/filter/internal/compute.hh>
62 
63 # include <mln/data/fill.hh>
64 # include <mln/data/paste.hh>
65 
66 # include <mln/util/timer.hh>
67 
68 # include <mln/value/label_16.hh>
69 
70 
71 #include <mln/debug/filename.hh>
72 
73 namespace scribo
74 {
75 
76  namespace filter
77  {
78 
79  using namespace mln;
80 
95  template <typename L>
96  component_set<L>
97  objects_with_holes(const component_set<L>& components,
98  unsigned min_holes_count,
99  unsigned min_size);
100 
115  template <typename L>
116  inline
117  component_set<L>
118  components_with_two_holes(const component_set<L>& components,
119  unsigned min_size);
120 
121 
122 # ifndef MLN_INCLUDE_ONLY
123 
124  namespace internal
125  {
126 
127  unsigned my_find_root(image2d<unsigned>& parent, unsigned x)
128  {
129  if (parent.element(x) == x)
130  return x;
131  return parent.element(x) = my_find_root(parent,
132  parent.element(x));
133  }
134 
135 
136  template <typename L>
137  mln_concrete(L)
138  compute_bboxes_image(const component_set<L>& components)
139  {
140  typedef mln_psite(L) P;
141  typedef mln_dpsite(P) D;
142 
143  const L& lbl = components.labeled_image();
144  extension::adjust_fill(lbl, 1, 0);
145 
146  mln_concrete(L) output;
147  initialize(output, lbl);
148  data::fill(output, 0);
149 
150  for_all_comps(i, components)
151  if (components(i).is_valid())
152  {
153  mln_box(L) b = components(i).bbox();
154  b.enlarge(1);
155 
156  unsigned
157  nrows = b.pmax().row() - b.pmin().row() + 1,
158  ncols = b.pmax().col() - b.pmin().col() + 1,
159  row_offset = lbl.delta_offset(D(+1, -ncols));
160 
161  mln_value(L) *ptr = &output(b.pmin());
162  for (unsigned row = 0; row < nrows; ++row, ptr += row_offset)
163  for (unsigned col = 0; col < ncols; ++col)
164  *ptr++ = i;
165  }
166 
167  extension::duplicate(output);
168 
169  return output;
170  }
171 
172 
173  } // end of namespace scribo::filter::internal
174 
175 
176 
181  template <typename L>
182  inline
183  component_set<L>
184  objects_with_holes(const component_set<L>& components,
185  unsigned min_holes_count,
186  unsigned min_size)
187  {
188  mln_trace("scribo::filter::objects_with_holes");
189 
190  typedef component_set<L> O;
191  neighb2d nbh = c4();
192 
193  image2d<unsigned> parent, card;
194  L bboxes_ima;
195 
196  util::array<util::set<unsigned> > bg_comps(
197  value::next(components.nelements()));
198 
200  to_keep(value::next(components.nelements()),
201  false);
202 
203  const L& lbl = components.labeled_image();
204 
205  // init
206  {
207  extension::adjust_fill(lbl, nbh, mln_max(mln_value(L)));
208  initialize(parent, lbl);
209  data::fill(parent, 0u);
210 
211  initialize(card, lbl);
212  data::fill(card, 1);
213 
214 
215  // FIXME: Improve.
216  bboxes_ima = internal::compute_bboxes_image(components);
217 
218  to_keep(0) = true;
219  }
220 
221  // 1st pass
222  {
223  util::array<int> dp = positive_offsets_wrt(lbl, nbh);
224  const unsigned n_nbhs = dp.nelements();
225 
226  mln_bkd_pixter(const L) pxl(lbl); // Backward.
227  for_all(pxl)
228  {
229  unsigned p = pxl.offset();
230  if (bboxes_ima.element(p) == 0 || lbl.element(p) != literal::zero)
231  continue;
232 
233  parent.element(p) = p;
234  for (unsigned i = 0; i < n_nbhs; ++i)
235  {
236  unsigned n = p + dp[i];
237  if (bboxes_ima.element(n) == 0 || lbl.element(n) != literal::zero)
238  continue;
239 
240  unsigned r = internal::my_find_root(parent, n);
241  if (r != p)
242  {
243  parent.element(r) = p;
244  card.element(p) += card.element(r);
245  }
246  } // for_all(n)
247 
248  } // for_all(pxl)
249 
250  }
251 
252  // FIXME: Iterate over another label when a label is marked as
253  // "to be kept".
254 
255  // 2nd pass
256  {
257  unsigned kept = 0;
258  mln_fwd_pixter(const L) pxl(bboxes_ima); // Forward.
259  for_all(pxl)
260  {
261  unsigned p = pxl.offset();
262 
263  // Foreground, ignored.
264  if (parent.element(p) == 0 || bboxes_ima.element(p) == literal::zero)
265  continue;
266 
267  unsigned& parent_p = parent.element(p);
268 
269  if (parent_p != p) // Not root => propagation
270  parent_p = parent.element(parent_p);
271  else // Root
272  if (card.element(p) < min_size)
273  {
274  parent_p = 0;
275  continue;
276  }
277 
278 // if (bboxes_ima.element(p) != literal::zero)
279 // {
280  mln_value(L) object_id = bboxes_ima.element(p);
281  if (parent_p != 0 // Check parent again since
282  // parent's may have been set to
283  // 0.
284  && bg_comps(object_id).nelements() < min_holes_count)
285  {
286  if (! bg_comps(object_id).has(parent_p))
287  {
288  bg_comps(object_id).insert(parent_p);
289 
290  if (bg_comps(object_id).nelements() == min_holes_count)
291  {
292  to_keep(object_id) = true;
293  ++kept;
294  }
295  }
296  }
297 // }
298  }
299 
300  if (kept == components.nelements())
301  {
302  return components;
303  }
304 
305  component_set<L> output = components.duplicate();
306  output.update_tags(to_keep, component::Ignored);
307 
308  return output;
309  }
310 
311 
312  }
313 
314 
315  template <typename L>
316  inline
317  component_set<L>
318  components_with_two_holes(const component_set<L>& components,
319  unsigned min_size)
320  {
321  mln_trace("scribo::filter::objects_with_holes");
322 
323  typedef component_set<L> O;
324  neighb2d nbh = c8();
325 
326  image2d<unsigned> parent, card;
327  L bboxes_ima;
328 
329  util::array<unsigned> bg_comps(
330  value::next(components.nelements()), 0);
331  util::array<bool> bg_comps_done(
332  value::next(components.nelements()), false);
333 
335  to_keep(value::next(components.nelements()), false);
336 
337  const L& lbl = components.labeled_image();
338 
339  // init
340  {
341  extension::fill(lbl, mln_max(mln_value(L)));
342 // extension::adjust_fill(components, nbh, mln_max(mln_value(L)));
343  initialize(parent, lbl);
344  data::fill(parent, 0u);
345 
346  initialize(card, lbl);
347  data::fill(card, 1);
348  border::fill(card, 1);
349 
350  bboxes_ima = internal::compute_bboxes_image(components);
351 
352  to_keep(0) = true;
353  }
354 
355  // 1st pass
356  {
357  util::array<int> dp = positive_offsets_wrt(lbl, nbh);
358  const unsigned n_nbhs = dp.nelements();
359 
360  mln_bkd_pixter(const L) pxl(lbl); // Backward.
361  for_all(pxl)
362  {
363  unsigned p = pxl.offset();
364  if (bboxes_ima.element(p) == 0 || lbl.element(p) != literal::zero)
365  continue;
366 
367  parent.element(p) = p;
368  for (unsigned i = 0; i < n_nbhs; ++i)
369  {
370  unsigned n = p + dp[i];
371  if (bboxes_ima.element(n) == 0 || lbl.element(n) != literal::zero)
372  continue;
373 
374  unsigned r = internal::my_find_root(parent, n);
375  if (r != p)
376  {
377  parent.element(r) = p;
378  card.element(p) += card.element(r);
379  }
380  } // for_all(n)
381 
382  } // for_all(pxl)
383 
384  }
385 
386  // 2nd pass
387  {
388  unsigned kept = 0;
389  mln_fwd_pixter(const L) pxl(bboxes_ima); // Forward.
390  for_all(pxl)
391  {
392  unsigned p = pxl.offset();
393  if (parent.element(p) == 0) // Foreground, ignored.
394  continue;
395 
396  unsigned& parent_p = parent.element(p);
397 
398  if (parent_p != p) // Not root => propagation
399  parent_p = parent.element(parent_p);
400  else // Root
401  if (card.element(p) < min_size)
402  {
403  parent_p = 0;
404  continue;
405  }
406 
407  if (parent_p != 0 // Check parent again since
408  // parent's may have been set to
409  // 0.
410  && bboxes_ima.element(p) != literal::zero)
411  {
412  mln_value(L) object_id = bboxes_ima.element(p);
413  if (!bg_comps_done(object_id) && components(object_id).is_valid())
414  {
415  if (bg_comps(object_id) == 0)
416  {
417  bg_comps(object_id) = parent_p;
418  }
419  else if (bg_comps(object_id) != parent_p)
420  {
421  bg_comps_done(object_id) = true;
422  to_keep(object_id) = true;
423  ++kept;
424  }
425  }
426  }
427  }
428 
429  component_set<L> output;
430  if (kept == components.nelements())
431  {
432  return components;
433  }
434 
435  output = components.duplicate();
436  output.update_tags(to_keep, component::Ignored);
437 
438  return output;
439  }
440 
441  }
442 
443 
444 # endif // ! MLN_INCLUDE_ONLY
445 
446  } // end of namespace scribo::filter
447 
448 } // end of namespace scribo
449 
450 
451 #endif // ! SCRIBO_FILTER_OBJECTS_WITH_HOLES_HH