---
scribo/ChangeLog | 4 +
scribo/filter/objects_with_holes.hh | 575 +++++++++++++++++++++++++++++++++++
2 files changed, 579 insertions(+), 0 deletions(-)
create mode 100644 scribo/filter/objects_with_holes.hh
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index bfde10a..891cd30 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,5 +1,9 @@
2010-02-19 Guillaume Lazzara <z(a)lrde.epita.fr>
+ * scribo/filter/objects_with_holes.hh: New component filter.
+
+2010-02-19 Guillaume Lazzara <z(a)lrde.epita.fr>
+
* scribo/draw/bounding_boxes.hh: Do not draw box centers anymore.
2010-02-19 Guillaume Lazzara <z(a)lrde.epita.fr>
diff --git a/scribo/filter/objects_with_holes.hh b/scribo/filter/objects_with_holes.hh
new file mode 100644
index 0000000..c448e58
--- /dev/null
+++ b/scribo/filter/objects_with_holes.hh
@@ -0,0 +1,575 @@
+// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+//
+// This file is part of Olena.
+//
+// Olena is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation, version 2 of the License.
+//
+// Olena is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Olena. If not, see <http://www.gnu.org/licenses/>.
+//
+// As a special exception, you may use this file as part of a free
+// software project without restriction. Specifically, if other files
+// instantiate templates or use macros or inline functions from this
+// file, or you compile this file and link it with other files to produce
+// an executable, this file does not by itself cause the resulting
+// executable to be covered by the GNU General Public License. This
+// exception does not however invalidate any other reasons why the
+// executable file might be covered by the GNU General Public License.
+
+#ifndef SCRIBO_FILTER_OBJECTS_WITH_HOLES_HH
+# define SCRIBO_FILTER_OBJECTS_WITH_HOLES_HH
+
+/// \file
+///
+/// \brief Remove objects having a minimum number of holes.
+
+# include <sstream>
+
+# include <mln/core/concept/image.hh>
+# include <mln/core/alias/neighb2d.hh>
+# include <mln/core/routine/extend.hh>
+# include <mln/core/image/dmorph/extended.hh>
+
+# include <mln/extension/duplicate.hh>
+
+# include <mln/draw/box_plain.hh>
+# include <mln/util/array.hh>
+
+# include <mln/labeling/blobs_and_compute.hh>
+
+# include <mln/accu/math/count.hh>
+
+# include <mln/fun/i2v/array.hh>
+
+# include <mln/io/pbm/save.hh>
+# include <mln/io/pgm/save.hh>
+
+# include <mln/data/convert.hh>
+
+# include <mln/labeling/background.hh>
+
+# include <scribo/core/macros.hh>
+# include <scribo/core/object_image.hh>
+# include <scribo/filter/internal/compute.hh>
+
+# include <mln/data/fill.hh>
+# include <mln/data/paste.hh>
+
+# include <mln/util/timer.hh>
+
+# include <mln/value/label_16.hh>
+# include <mln/core/var.hh>
+
+
+#include <mln/debug/filename.hh>
+
+namespace scribo
+{
+
+ namespace filter
+ {
+
+ using namespace mln;
+
+ /*! \brief Remove objects having a minimum number of holes.
+
+
+
+ */
+ template <typename L>
+ object_image(L)
+ objects_with_holes(const object_image(L)& objects,
+ unsigned min_holes_count,
+ unsigned min_size);
+
+
+ template <typename L>
+ inline
+ object_image(L)
+ objects_with_two_holes(const object_image(L)& objects,
+ unsigned min_size);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+ namespace internal
+ {
+
+ unsigned my_find_root(image2d<unsigned>& parent, unsigned x)
+ {
+ if (parent.element(x) == x)
+ return x;
+ return parent.element(x) = my_find_root(parent,
+ parent.element(x));
+ }
+
+
+ template <typename L>
+ mln_concrete(L)
+ compute_bboxes_image(const object_image(L)& objects)
+ {
+ typedef mln_psite(L) P;
+ typedef mln_dpsite(P) D;
+
+ extension::adjust_fill(objects, 1, 0);
+
+ mln_concrete(L) output;
+ initialize(output, objects);
+ data::fill(output, 0);
+
+ for_all_components(i, objects.bboxes())
+ {
+ mln_box(L) b = objects.bbox(i);
+ b.enlarge(1);
+
+ unsigned
+ nrows = b.pmax().row() - b.pmin().row() + 1,
+ ncols = b.pmax().col() - b.pmin().col() + 1,
+ row_offset = objects.labeled_image_().delta_index(D(+1, -ncols));
+
+ mln_value(L) *ptr = &output(b.pmin());
+ for (unsigned row = 0; row < nrows; ++row, ptr += row_offset)
+ for (unsigned col = 0; col < ncols; ++col)
+ *ptr++ = i;
+ }
+
+ extension::duplicate(output);
+
+ return output;
+ }
+
+
+ } // end of namespace scribo::filter::internal
+
+
+
+ /*!
+ Label the background and count the number of background
+ components in an object bounding box.
+ */
+ template <typename L>
+ inline
+ object_image(L)
+ objects_with_holes(const object_image(L)& objects,
+ unsigned min_holes_count,
+ unsigned min_size)
+ {
+ trace::entering("scribo::filter::objects_with_holes");
+
+ typedef object_image(L) O;
+ neighb2d nbh = c4();
+
+ image2d<unsigned> parent, card;
+ L bboxes_ima;
+
+ util::array<util::set<unsigned> > bg_comps(
+ static_cast<unsigned>(objects.nlabels()) + 1);
+
+ fun::i2v::array<bool>
+ to_keep(static_cast<unsigned>(objects.nlabels()) + 1,
+ false);
+
+ const L& lbl = objects.labeled_image_();
+
+ std::cout << "objects.nlabels = " << objects.nlabels()
<< std::endl;
+
+ util::timer timer_;
+ timer_.start();
+
+ // init
+ {
+ extension::adjust_fill(objects, nbh, mln_max(mln_value(L)));
+ initialize(parent, objects);
+ data::fill(parent, 0u);
+
+ initialize(card, objects);
+ data::fill(card, 1);
+
+
+ // FIXME: Improve.
+ util::timer t2;
+ t2.start();
+ bboxes_ima = internal::compute_bboxes_image(objects);
+ float t2_ = t2;
+ std::cout << "compute bboxes image " << t2_ << std::endl;
+
+ to_keep(0) = true;
+ }
+ float t_ = timer_;
+ std::cout << "init = " << t_ << std::endl;
+
+ // 1st pass
+ timer_.restart();
+ {
+ util::array<int> dp = positive_offsets_wrt(lbl, nbh);
+ const unsigned n_nbhs = dp.nelements();
+
+ mln_bkd_pixter(const L) pxl(lbl); // Backward.
+ for_all(pxl)
+ {
+ unsigned p = pxl.offset();
+ if (bboxes_ima.element(p) == 0 || lbl.element(p) != literal::zero)
+ continue;
+
+ parent.element(p) = p;
+ for (unsigned i = 0; i < n_nbhs; ++i)
+ {
+ unsigned n = p + dp[i];
+ if (bboxes_ima.element(n) == 0 || lbl.element(n) != literal::zero)
+ continue;
+
+ unsigned r = internal::my_find_root(parent, n);
+ if (r != p)
+ {
+ parent.element(r) = p;
+ card.element(p) += card.element(r);
+ }
+ } // for_all(n)
+
+ } // for_all(pxl)
+
+ }
+ t_ = timer_;
+ std::cout << "1st pass = " << t_ << std::endl;
+
+ // FIXME: Iterate over another label when a label is marked as
+ // "to be kept".
+
+ // 2nd pass
+ timer_.restart();
+ {
+ unsigned kept = 0;
+ mln_fwd_pixter(const L) pxl(bboxes_ima); // Forward.
+ for_all(pxl)
+ {
+ unsigned p = pxl.offset();
+
+ // Foreground, ignored.
+ if (parent.element(p) == 0 || bboxes_ima.element(p) == literal::zero)
+ continue;
+
+ unsigned& parent_p = parent.element(p);
+
+ if (parent_p != p) // Not root => propagation
+ parent_p = parent.element(parent_p);
+ else // Root
+ if (card.element(p) < min_size)
+ {
+ parent_p = 0;
+ continue;
+ }
+
+// if (bboxes_ima.element(p) != literal::zero)
+// {
+ mln_value(L) object_id = bboxes_ima.element(p);
+ if (parent_p != 0 // Check parent again since
+ // parent's may have been set to
+ // 0.
+ && bg_comps(object_id).nelements() < min_holes_count)
+ {
+ if (! bg_comps(object_id).has(parent_p))
+ {
+ bg_comps(object_id).insert(parent_p);
+
+ if (bg_comps(object_id).nelements() == min_holes_count)
+ {
+ to_keep(object_id) = true;
+ ++kept;
+ }
+ }
+ }
+// }
+ }
+
+ float t_ = timer_;
+ std::cout << "2nd pass = " << t_ << std::endl;
+
+ std::cout << "kept = " << kept << std::endl;
+// debug::println(parent);
+// std::cout << bg_comps << std::endl;
+// std::cout << to_keep << std::endl;
+
+ timer_.restart();
+ object_image(L) output;
+
+ if (kept == objects.nlabels())
+ {
+ trace::exiting("scribo::filter::objects_with_holes");
+ return objects;
+ }
+
+ output.init_from_(objects);
+ output.relabel(to_keep);
+ t_ = timer_;
+ std::cout << "init output = " << t_ << std::endl;
+
+ trace::exiting("scribo::filter::objects_with_holes");
+ return output;
+ }
+
+
+ }
+
+
+ template <typename L>
+ inline
+ object_image(L)
+ objects_with_two_holes(const object_image(L)& objects,
+ unsigned min_size)
+ {
+ trace::entering("scribo::filter::objects_with_holes");
+
+ std::cout << objects.nlabels() << std::endl;
+
+ typedef object_image(L) O;
+ neighb2d nbh = c8();
+
+ image2d<unsigned> parent, card;
+ L bboxes_ima;
+
+ util::array<unsigned> bg_comps(
+ static_cast<unsigned>(objects.nlabels()) + 1, 0);
+ util::array<bool> bg_comps_done(
+ static_cast<unsigned>(objects.nlabels()) + 1, false);
+
+ fun::i2v::array<bool>
+ to_keep(static_cast<unsigned>(objects.nlabels()) + 1,
+ false);
+
+ const L& lbl = objects.labeled_image_();
+
+ // init
+ {
+ extension::fill(objects, mln_max(mln_value(L)));
+// extension::adjust_fill(objects, nbh, mln_max(mln_value(L)));
+ initialize(parent, objects);
+ data::fill(parent, 0u);
+
+ initialize(card, objects);
+ data::fill(card, 1);
+ border::fill(card, 1);
+
+ bboxes_ima = internal::compute_bboxes_image(objects);
+
+ to_keep(0) = true;
+ }
+
+ // 1st pass
+ std::cout << "1st pass" << std::endl;
+ {
+ util::array<int> dp = positive_offsets_wrt(lbl, nbh);
+ const unsigned n_nbhs = dp.nelements();
+
+ mln_bkd_pixter(const L) pxl(lbl); // Backward.
+ for_all(pxl)
+ {
+ unsigned p = pxl.offset();
+ if (bboxes_ima.element(p) == 0 || lbl.element(p) != literal::zero)
+ continue;
+
+ parent.element(p) = p;
+ for (unsigned i = 0; i < n_nbhs; ++i)
+ {
+ unsigned n = p + dp[i];
+ if (bboxes_ima.element(n) == 0 || lbl.element(n) != literal::zero)
+ continue;
+
+ unsigned r = internal::my_find_root(parent, n);
+ if (r != p)
+ {
+ parent.element(r) = p;
+ card.element(p) += card.element(r);
+ }
+ } // for_all(n)
+
+ } // for_all(pxl)
+
+ }
+
+ // 2nd pass
+ std::cout << "2nd pass" << std::endl;
+ {
+ unsigned kept = 0;
+ mln_fwd_pixter(const L) pxl(bboxes_ima); // Forward.
+ for_all(pxl)
+ {
+ unsigned p = pxl.offset();
+ if (parent.element(p) == 0) // Foreground, ignored.
+ continue;
+
+ unsigned& parent_p = parent.element(p);
+
+ if (parent_p != p) // Not root => propagation
+ parent_p = parent.element(parent_p);
+ else // Root
+ if (card.element(p) < min_size)
+ {
+ parent_p = 0;
+ continue;
+ }
+
+ if (parent_p != 0 // Check parent again since
+ // parent's may have been set to
+ // 0.
+ && bboxes_ima.element(p) != literal::zero)
+ {
+ mln_value(L) object_id = bboxes_ima.element(p);
+ if (!bg_comps_done(object_id))
+ {
+ if (bg_comps(object_id) == 0)
+ {
+ bg_comps(object_id) = parent_p;
+ }
+ else if (bg_comps(object_id) != parent_p)
+ {
+ bg_comps_done(object_id) = true;
+ to_keep(object_id) = true;
+ ++kept;
+ }
+ }
+ }
+ }
+
+ object_image(L) output;
+ if (kept == objects.nlabels())
+ {
+ trace::exiting("scribo::filter::objects_with_holes");
+ return objects;
+ }
+
+ output.init_from_(objects);
+ output.relabel(to_keep);
+
+ trace::exiting("scribo::filter::objects_with_holes");
+ return output;
+ }
+
+ }
+
+
+
+// template <typename L>
+// inline
+// object_image(L)
+// objects_with_holes(const object_image(L)& objects,
+// unsigned min_holes_count)
+// {
+// trace::entering("scribo::filter::objects_with_holes");
+
+// mln_precondition(objects.is_valid());
+
+
+// L bboxes_ima;
+// initialize(bboxes_ima, objects);
+// data::fill(bboxes_ima, literal::zero);
+
+// for_all_components(i, objects.bboxes())
+// mln::draw::box(bboxes_ima, objects.bbox(i), i);
+
+// util::array<util::set<mln_value(L)> > first_bg_comp(
+// static_cast<unsigned>(objects.nlabels()) + 1);
+
+// fun::i2v::array<bool>
+// to_keep(static_cast<unsigned>(objects.nlabels()) + 1,
+// false);
+// to_keep(0) = true;
+
+// mln_value(L) nbglabels;
+// L bg_lbl = labeling::background(objects, c8(), nbglabels);
+
+// unsigned kept;
+// mln_piter(L) p(bboxes_ima.domain());
+// for_all(p)
+// {
+// if (bboxes_ima(p) == literal::zero)
+// continue;
+
+// if (bg_lbl(p) != 0)
+// if (! first_bg_comp(bboxes_ima(p)).has(bg_lbl(p)))
+// if (first_bg_comp(bboxes_ima(p)).nelements() < min_holes_count - 1)
+// first_bg_comp(bboxes_ima(p)).insert(bg_lbl(p));
+// else
+// {
+// to_keep(bboxes_ima(p)) == true;
+// ++kept;
+// }
+// }
+
+// object_image(L) output;
+// if (kept == objects.nlabels())
+// output = objects;
+// else
+// output = internal::compute(objects, to_keep);
+
+// trace::exiting("scribo::filter::objects_with_holes");
+// return output;
+// }
+
+
+ template <typename L>
+ inline
+ object_image(L)
+ objects_with_holes_slow(const object_image(L)& objects,
+ unsigned min_holes_count)
+ {
+ trace::entering("scribo::filter::objects_with_holes");
+
+ mln_precondition(objects.is_valid());
+
+ fun::i2v::array<bool>
+ to_keep(static_cast<unsigned>(objects.nlabels()) + 1,
+ true);
+
+ bool to_remove = false;
+ for_all_components(i, objects.bboxes())
+ {
+ mln_domain(L) b = objects.bbox(i);
+ b.enlarge(1);
+
+ mln_ch_value(L, bool) tmp(b);
+ data::fill(tmp, true);
+ data::fill((tmp | ((objects | objects.bbox(i)) | (pw::value(objects) ==
pw::cst(i))).domain()).rw(), false);
+
+ typedef accu::math::count<mln_value(L)> accu_t;
+ mln_value(L) nlabels;
+ util::array<unsigned> counts
+ = labeling::blobs_and_compute(tmp,
+ c8(), nlabels,
+ accu_t()).second();
+ unsigned nholes = 0;
+ for_all_components(j, counts)
+ if (counts(j) > 4u)
+ ++nholes;
+
+ if (nholes < min_holes_count)
+ {
+ to_keep(i) = false;
+ to_remove = true;
+ }
+ }
+
+ object_image(L) output;
+ if (! to_remove)
+ output = objects;
+ else
+ output = internal::compute(objects, to_keep);
+
+ trace::exiting("scribo::filter::objects_with_holes");
+ return output;
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::filter
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_FILTER_OBJECTS_WITH_HOLES_HH
--
1.5.6.5