* scribo/make/text_blocks_image.hh,
* scribo/make/text_components_image.hh,
* scribo/primitive/extract/internal/union.hh: New.
* scribo/primitive/extract/non_text.hh: New implementation.
* scribo/primitive/extract/non_text_kmean.hh: New. Old
implementatino is saved here.
---
scribo/ChangeLog | 13 +
scribo/scribo/make/text_blocks_image.hh | 136 ++++++++
scribo/scribo/make/text_components_image.hh | 101 ++++++
scribo/scribo/primitive/extract/internal/union.hh | 246 +++++++++++++
scribo/scribo/primitive/extract/non_text.hh | 366 ++++++++++++++------
.../extract/{non_text.hh => non_text_kmean.hh} | 17 +-
6 files changed, 761 insertions(+), 118 deletions(-)
create mode 100644 scribo/scribo/make/text_blocks_image.hh
create mode 100644 scribo/scribo/make/text_components_image.hh
create mode 100644 scribo/scribo/primitive/extract/internal/union.hh
copy scribo/scribo/primitive/extract/{non_text.hh => non_text_kmean.hh} (91%)
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index 43041fe..32dda87 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,5 +1,18 @@
2011-03-14 Guillaume Lazzara <z(a)lrde.epita.fr>
+ New non-text components extraction routine.
+
+ * scribo/make/text_blocks_image.hh,
+ * scribo/make/text_components_image.hh,
+ * scribo/primitive/extract/internal/union.hh: New.
+
+ * scribo/primitive/extract/non_text.hh: New implementation.
+
+ * scribo/primitive/extract/non_text_kmean.hh: New. Old
+ implementatino is saved here.
+
+2011-03-14 Guillaume Lazzara <z(a)lrde.epita.fr>
+
Introduce result image output.
* scribo/fun/v2v/highlight.hh,
diff --git a/scribo/scribo/make/text_blocks_image.hh
b/scribo/scribo/make/text_blocks_image.hh
new file mode 100644
index 0000000..fbc16df
--- /dev/null
+++ b/scribo/scribo/make/text_blocks_image.hh
@@ -0,0 +1,136 @@
+// Copyright (C) 2011 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_MAKE_TEXT_BLOCKS_IMAGE_HH
+# define SCRIBO_MAKE_TEXT_BLOCKS_IMAGE_HH
+
+/// \file
+///
+/// \brief Create a mask of paragraph blocks.
+
+# include <mln/core/image/image2d.hh>
+# include <mln/draw/box_plain.hh>
+
+# include <scribo/core/document.hh>
+# include <scribo/core/paragraph_set.hh>
+# include <scribo/core/line_info.hh>
+
+
+namespace scribo
+{
+
+ namespace make
+ {
+ using namespace mln;
+
+
+ /// \brief Create a mask of paragraph blocks.
+ /*!
+ \pre \p doc has_text() methods MUST return True.
+ */
+ template <typename L>
+ mln_ch_value(L,bool)
+ text_blocks_image(const document<L>& doc, unsigned min_nlines);
+
+
+ # ifndef MLN_INCLUDE_ONLY
+
+ template <typename L>
+ mln_ch_value(L,bool)
+ text_blocks_image(const document<L>& doc, unsigned min_nlines)
+ {
+ trace::entering("scribo::make::text_blocks_image");
+
+ mln_precondition(doc.is_open());
+
+ image2d<bool> output;
+ initialize(output, doc.image());
+ data::fill(output, false);
+
+ // image2d<value::int_u8> log;
+ // initialize(log, output);
+ // data::fill(log, 0);
+
+ if (doc.has_text())
+ {
+ const paragraph_set<L>& parset = doc.paragraphs();
+ for_all_paragraphs(p, parset)
+ if (parset(p).nlines() >= min_nlines)
+ {
+ box2d last_box;
+
+ // For each line in this paragraph.
+ for_all_elements(l, parset(p).line_ids())
+ {
+ const line_info<L>&
+ line = parset.lines()(parset(p).line_ids()(l));
+
+ // Avoid invalid case:
+ //
+ // =======
+ // ======
+ if (last_box.is_valid()
+ && last_box.pmax().row() < line.bbox().pmin().row()
+ && last_box.pmin().col() < line.bbox().pmax().col())
+ {
+ point2d
+ pmin(std::min(last_box.pmax().row(),
+ line.bbox().pmin().row()),
+ std::max(last_box.pmin().col(),
+ line.bbox().pmin().col())),
+ pmax(std::max(last_box.pmax().row(),
+ line.bbox().pmin().row()),
+ std::min(last_box.pmax().col(),
+ line.bbox().pmax().col()));
+
+ box2d new_box(pmin, pmax);
+ mln::draw::box_plain(output, new_box, true);
+ // mln::draw::box_plain(log, new_box, 1);
+
+ // mln::io::pgm::save(log, mln::debug::filename("log.pgm", i++));
+ }
+
+ mln::draw::box_plain(output, line.bbox(), true);
+ // mln::draw::box_plain(log, line.bbox(), 255);
+ // mln::io::pgm::save(log, mln::debug::filename("log.pgm", i++));
+ last_box = line.bbox();
+ }
+ }
+ }
+
+
+ trace::exiting("scribo::make::text_blocks_image");
+ return output;
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::make
+
+} // end of namespace scribo
+
+
+
+#endif // ! SCRIBO_MAKE_TEXT_BLOCKS_IMAGE_HH
diff --git a/scribo/scribo/make/text_components_image.hh
b/scribo/scribo/make/text_components_image.hh
new file mode 100644
index 0000000..e7c892e
--- /dev/null
+++ b/scribo/scribo/make/text_components_image.hh
@@ -0,0 +1,101 @@
+// Copyright (C) 2011 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_MAKE_TEXT_COMPONENTS_IMAGE_HH
+# define SCRIBO_MAKE_TEXT_COMPONENTS_IMAGE_HH
+
+/// \file
+///
+/// Create a binary image with text components only.
+
+#include <mln/pw/all.hh>
+#include <mln/core/image/dmorph/image_if.hh>
+#include <mln/util/array.hh>
+#include <mln/data/fill.hh>
+
+#include <scribo/core/document.hh>
+#include <scribo/core/line_set.hh>
+
+
+namespace scribo
+{
+
+ namespace make
+ {
+ using namespace mln;
+
+
+ /// \brief Create a binary image with text components only.
+ /*!
+ \pre \p doc has_text() methods MUST return True.
+ */
+ template <typename L>
+ mln_ch_value(L,bool)
+ text_components_image(const document<L>& doc);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+ template <typename L>
+ mln_ch_value(L,bool)
+ text_components_image(const document<L>& doc)
+ {
+ trace::entering("scribo::make::text_components_image");
+
+ mln_precondition(doc.is_open());
+
+ mln_ch_value(L,bool) output;
+ initialize(output, doc.image());
+ data::fill(output, false);
+
+ if (doc.has_text())
+ {
+ const scribo::line_set<L>& lines = doc.lines();
+ for_all_lines(l, doc.lines())
+ if (lines(l).is_textline())
+ {
+ const util::array<component_id_t>&
+ comp_ids = lines(l).component_ids();
+ const L& lbl = lines.components().labeled_image();
+ for_all_elements(c, comp_ids)
+ data::fill((output | lines.components()(comp_ids(c)).bbox()).rw(),
+ ((doc.binary_image() | lines.components()(comp_ids(c)).bbox())
+ | (pw::value(lbl) == comp_ids(c))));
+ }
+ }
+
+ trace::exiting("scribo::make::text_components_image");
+ return output;
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::make
+
+} // end of namespace scribo
+
+
+
+#endif // ! SCRIBO_MAKE_TEXT_COMPONENTS_IMAGE_HH
diff --git a/scribo/scribo/primitive/extract/internal/union.hh
b/scribo/scribo/primitive/extract/internal/union.hh
new file mode 100644
index 0000000..509a7e4
--- /dev/null
+++ b/scribo/scribo/primitive/extract/internal/union.hh
@@ -0,0 +1,246 @@
+// Copyright (C) 2011 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.
+
+/// \file
+///
+/// \brief Various utilities for image extraction
+///
+/// \fixme To be cleanup
+
+#ifndef SCRIBO_PRIMITIVE_EXTRACT_INTERNAL_UNION_HH
+# define SCRIBO_PRIMITIVE_EXTRACT_INTERNAL_UNION_HH
+
+# include <mln/core/image/image2d.hh>
+# include <mln/border/fill.hh>
+
+
+namespace scribo
+{
+
+ namespace primitive
+ {
+
+ namespace extract
+ {
+
+ namespace internal
+ {
+
+ using namespace mln;
+
+
+ unsigned find_root(image2d<unsigned>& parent, unsigned x);
+
+
+ void union_find(const image2d<bool>& input, bool lab,
+ image2d<unsigned>& parent, image2d<unsigned>& area,
+ unsigned& max_area);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+ inline
+ unsigned find_root(image2d<unsigned>& parent, unsigned x)
+ {
+ if (parent.element(x) == x)
+ return x;
+ return parent.element(x) = find_root(parent, parent.element(x));
+ }
+
+
+ inline
+ void union_find(const image2d<bool>& input,
+ bool lab,
+ // output:
+ image2d<unsigned>& parent,
+ image2d<unsigned>& area,
+ unsigned& max_area)
+ {
+ const unsigned nrows = input.nrows(), ncols = input.ncols();
+
+ unsigned op, on, delta = input.delta_index(dpoint2d(1, 0));
+
+ data::fill(parent, 0);
+ max_area = 0;
+
+ {
+
+ // row == 0 and col == 0
+
+ op = input.index_of_point(point2d(0,0));
+ if (input.element(op) == lab)
+ {
+ area.element(op) = 1;
+ parent.element(op) = op;
+ }
+
+
+ // row = 0
+
+ for (unsigned col = 1; col < ncols; ++col)
+ {
+ on = op;
+ ++op;
+
+ if (input.element(op) != lab)
+ continue;
+
+ if (input.element(on) == lab)
+ {
+ unsigned& par_p = parent.element(op);
+ par_p = find_root(parent, on);
+ ++area.element(par_p);
+ if (area.element(par_p) > max_area)
+ max_area = area.element(par_p);
+ }
+ else
+ {
+ area.element(op) = 1;
+ parent.element(op) = op;
+ }
+
+ }
+ }
+
+
+ for (unsigned row = 1; row < nrows; ++row)
+ {
+
+ {
+ // col == 0
+
+ op = input.index_of_point(point2d(row, 0));
+ on = op - delta;
+
+ if (input.element(op) == lab)
+ {
+ if (input.element(on) == lab)
+ {
+ unsigned& par_p = parent.element(op);
+ par_p = find_root(parent, on);
+ ++area.element(par_p);
+ if (area.element(par_p) > max_area)
+ max_area = area.element(par_p);
+ }
+ else
+ {
+ area.element(op) = 1;
+ parent.element(op) = op;
+ }
+ }
+
+ }
+
+ for (unsigned col = 1; col < ncols; ++col)
+ {
+ ++op;
+ ++on;
+
+ if (input.element(op) != lab)
+ continue;
+
+ bool merge_ = false;
+
+ // up
+
+ if (input.element(on) == lab)
+ {
+ unsigned& par_p = parent.element(op);
+ par_p = find_root(parent, on);
+ ++area.element(par_p);
+ if (area.element(par_p) > max_area)
+ max_area = area.element(par_p);
+ merge_ = true;
+ }
+
+
+ // left
+
+ unsigned ol = op - 1;
+
+ if (input.element(ol) == lab)
+ {
+ if (merge_)
+ {
+ if (input.element(on - 1) != lab) // not already merged
+ {
+ unsigned r_op = parent.element(op), r_ol = find_root(parent, ol);
+ if (r_op != r_ol)
+ {
+ // do-union
+ if (r_op < r_ol)
+ {
+ parent.element(r_ol) = r_op;
+ area.element(r_op) += area.element(r_ol);
+ if (area.element(r_op) > max_area)
+ max_area = area.element(r_op);
+ }
+ else
+ {
+ parent.element(r_op) = r_ol;
+ area.element(r_ol) += area.element(r_op);
+ if (area.element(r_ol) > max_area)
+ max_area = area.element(r_ol);
+ }
+ }
+ }
+ } // end of "if (merge)
+ else
+ {
+ unsigned& par_p = parent.element(op);
+ par_p = find_root(parent, ol);
+ ++area.element(par_p);
+ if (area.element(par_p) > max_area)
+ max_area = area.element(par_p);
+ merge_ = true;
+ }
+ }
+
+
+ // finalization
+
+ if (merge_ == false)
+ {
+ parent.element(op) = op;
+ area.element(op) = 1;
+ }
+
+ }
+ }
+
+ } // end of 'union_find'
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::primivite::extract::internal
+
+ } // end of namespace scribo::primitive::extract
+
+ } // end of namespace scribo::primitive
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_PRIMITIVE_EXTRACT_INTERNAL_UNION_HH
diff --git a/scribo/scribo/primitive/extract/non_text.hh
b/scribo/scribo/primitive/extract/non_text.hh
index 8528782..a456270 100644
--- a/scribo/scribo/primitive/extract/non_text.hh
+++ b/scribo/scribo/primitive/extract/non_text.hh
@@ -1,4 +1,4 @@
-// Copyright (C) 2010, 2011 EPITA Research and Development Laboratory
+// Copyright (C) 2011 EPITA Research and Development Laboratory
// (LRDE)
//
// This file is part of Olena.
@@ -33,30 +33,19 @@
#ifndef SCRIBO_PRIMITIVE_EXTRACT_NON_TEXT_HH
# define SCRIBO_PRIMITIVE_EXTRACT_NON_TEXT_HH
-# include <mln/core/image/image2d.hh>
-# include <mln/core/alias/neighb2d.hh>
-# include <mln/data/fill.hh>
-# include <mln/util/array.hh>
-# include <mln/labeling/compute.hh>
-# include <mln/labeling/relabel.hh>
-# include <mln/accu/math/count.hh>
-# include <mln/pw/all.hh>
+# include <mln/morpho/elementary/dilation.hh>
-# include <mln/draw/box_plain.hh>
-# include <mln/value/label_8.hh>
-# include <mln/value/rgb.hh>
-# include <mln/value/rgb8.hh>
+# include <scribo/make/text_components_image.hh>
+# include <scribo/make/text_blocks_image.hh>
-# include <scribo/core/macros.hh>
-# include <scribo/core/component_set.hh>
-# include <scribo/core/document.hh>
-# include <scribo/core/line_set.hh>
-# include <scribo/core/def/lbl_type.hh>
-# include <scribo/filter/objects_small.hh>
+# include <scribo/primitive/extract/internal/union.hh>
+# include <scribo/debug/logger.hh>
+
+//DEBUG
+#include <mln/util/timer.hh>
+#include <mln/io/pbm/save.hh>
-# include <mln/clustering/kmean_rgb.hh>
-# include <mln/fun/v2v/rgb8_to_rgbn.hh>
namespace scribo
{
@@ -69,10 +58,24 @@ namespace scribo
using namespace mln;
+ /*! \brief Extract non text components.
+
+ This method takes text localization into account and tries to
+ learn the background colors to deduce the relevant non text
+ components.
+
+ \param[in] doc A document structure. Its must have paragraph
+ information.
+
+ \param[in] nlines The number of lines needed in a paragraph to
+ consider the latter during the background color learning.
+
- template <typename L, typename I>
+ \return A component set of non text components.
+ */
+ template <typename L>
component_set<L>
- non_text(const document<L>& doc, const Image<I>& input);
+ non_text(const document<L>& doc, unsigned nlines);
# ifndef MLN_INCLUDE_ONLY
@@ -82,133 +85,276 @@ namespace scribo
{
template <typename L>
- struct order_bbox
+ image2d<bool>
+ learn(const document<L>& doc,
+ const image2d<bool>& txt,
+ const image2d<bool>& txtblocks,
+ unsigned nbits,
+ float p_cover)
{
- order_bbox(const scribo::component_set<L>& comps)
- : comps_(comps)
+ const image2d<value::rgb8>& input = doc.image();
+ const image2d<bool>&
+ seps = doc.paragraphs().lines().components().separators();
+
+ if (txt.border() != input.border()
+ || txtblocks.border() != input.border()
+ || seps.border() != input.border())
{
+ std::cerr << " txt.border() = " << txt.border()
+ << " - txtblocks.border() = " << txtblocks.border()
+ << " - input.border() = " << input.border()
+ << " - seps.border() = " << seps.border()
+ << std::endl;
+ std::cerr << "different sizes for borders! Resizing..." <<
std::endl;
+
+
+ border::resize(txt, border::thickness);
+ border::resize(input, border::thickness);
+ border::resize(txtblocks, border::thickness);
+ border::resize(seps, border::thickness);
+ // std::abort();
}
- bool operator()(const unsigned& c1, const unsigned& c2) const
+
+ const unsigned q_div = std::pow(2, 8 - nbits);
+ const unsigned q = unsigned(std::pow(2, nbits));
+ const unsigned nelements = input.nelements();
+
+
+ image3d<unsigned> h_bg(q, q, q);
+ data::fill(h_bg, 0);
+
+ border::fill(txtblocks, false); // so h_bg is not updated by border pixels!
+
+ unsigned n_bg = 0;
{
- if (comps_(c1).bbox().nsites() == comps_(c2).bbox().nsites())
- return c1 > c2;
- return comps_(c1).bbox().nsites() > comps_(c2).bbox().nsites();
+ // compute h_bg
+ for (unsigned i = 0; i < nelements; ++i)
+ if (txtblocks.element(i) == true)
+ {
+ ++n_bg;
+ const value::rgb8& c = input.element(i);
+ ++h_bg.at_(c.red() / q_div, c.green() / q_div, c.blue() / q_div);
+ }
}
- scribo::component_set<L> comps_;
- };
+ typedef std::map<unsigned, unsigned> map_t;
+ map_t ncells_with_nitems;
+ {
+ mln_piter_(box3d) c(h_bg.domain());
+ for_all(c)
+ {
+ unsigned nitems_in_c = h_bg(c);
+ ++ncells_with_nitems[ nitems_in_c ];
+ }
+ }
- } // end of namespace scribo::primitive::extract::internal
+ unsigned n_items_min = 0;
+ {
+ map_t::const_reverse_iterator i;
+ unsigned N = 0;
+ for (i = ncells_with_nitems.rbegin(); i != ncells_with_nitems.rend(); ++i)
+ {
+ unsigned nitems = i->first, ncells = i->second;
+ N += nitems * ncells;
+ if (float(N) > p_cover * float(n_bg))
+ {
+ n_items_min = nitems;
+ break;
+ }
+ }
+ }
+ if (n_items_min == 0)
+ n_items_min = 1; // safety
- // FACADE
+ image3d<bool> bg(q, q, q);
+ {
+ mln_piter_(box3d) c(h_bg.domain());
+ for_all(c)
+ bg(c) = (h_bg(c) >= n_items_min);
+ }
- template <typename L, typename I>
- component_set<L>
- non_text(const document<L>& doc, const Image<I>& input_)
- {
- trace::entering("scribo::primitive::extract::non_text");
- const I& input = exact(input_);
- mln_precondition(doc.is_valid());
- mln_precondition(input.is_valid());
+ // outputing
- const line_set<L>& lines = doc.lines();
+ image2d<bool> output;
+ initialize(output, input);
+ {
+ for (unsigned i = 0; i < nelements; ++i)
+ if (txt.element(i) == true || seps.element(i) == true)
+ output.element(i) = false;
+ else
+ {
+ const value::rgb8& c = input.element(i);
+ output.element(i) = ! bg.at_(c.red() / q_div, c.green() / q_div, c.blue() / q_div);
+ }
+ }
- // Element extraction
+ return output;
+ }
- image2d<value::label_8> img_lbl8;
+
+
+
+
+ inline
+ image2d<bool>
+ cleaning(const image2d<bool>& input, unsigned lambda)
{
- image2d<bool> content;
- initialize(content, input);
- data::fill(content, true);
+ const box2d& dom = input.domain();
+
+ image2d<unsigned> area(dom);
+ image2d<unsigned> parent(dom);
+ image2d<bool> output(dom);
+
+ unsigned max_area = 0;
+
+
+ // 1st pass = bg union-find
+
+ {
+ union_find(input, false, // in
+ parent, area, max_area // out
+ );
+ }
+
+
+ // echo
+ // std::cout << "max_area = " << max_area << std::endl;
+
+
+ // 2nd pass = bg biggest component selection
+
+ {
+ const unsigned nelements = input.nelements();
+ const bool* p_i = input.buffer();
+ bool* p_o = output.buffer();
+ const unsigned* p_a = area.buffer();
+ const unsigned* p_par = parent.buffer();
+
+ for (unsigned i = 0; i < nelements; ++i)
+ {
+ if (*p_i == true)
+ *p_o = true;
+ else
+ {
+ if (*p_par == i)
+ *p_o = (*p_a != max_area);
+ else
+ *p_o = output.element(*p_par);
+ }
+ ++p_i;
+ ++p_o;
+ ++p_a;
+ ++p_par;
+ }
+ }
+
+
+
+ // 3rd pass = fg union-find
- for_all_lines(l, lines)
- if (lines(l).type() == line::Text)
- data::fill((content | lines(l).bbox()).rw(), false);
+ {
+ union_find(output, true, // in
+ parent, area, max_area // out
+ );
+ }
- typedef mln::value::rgb<5> t_rgb5;
- typedef mln::fun::v2v::rgb8_to_rgbn<5> t_rgb8_to_rgb5;
- image2d<t_rgb5>
- img_rgb5 = mln::data::transform(doc.image(), t_rgb8_to_rgb5());
- img_lbl8 =
- mln::clustering::kmean_rgb<double,5>((img_rgb5 | pw::value(content)), 3, 10,
10).unmorph_();
- data::fill((img_lbl8 | !pw::value(content)).rw(), 0u);
+ // 4th pass = cleaning fg
- mln::util::array<unsigned>
- card = mln::labeling::compute(accu::math::count<value::label_8>(),
- img_lbl8, img_lbl8, 3);
+ {
+ const unsigned nelements = input.nelements();
+ bool* p_o = output.buffer();
+ const unsigned* p_a = area.buffer();
+ const unsigned* p_par = parent.buffer();
- unsigned max = 0, bg_id = 0;
- for_all_ncomponents(c, 3)
- if (card(c) > max)
+ for (unsigned i = 0; i < nelements; ++i)
{
- max = card(c);
- bg_id = c;
+ if (*p_o == true)
+ {
+ if (*p_par == i)
+ *p_o = (*p_a > lambda);
+ else
+ *p_o = output.element(*p_par);
+ }
+ ++p_o;
+ ++p_a;
+ ++p_par;
}
+ }
+
- mln::fun::i2v::array<bool> f(4, true);
- f(0) = false;
- f(bg_id) = false;
- labeling::relabel_inplace(img_lbl8, 4, f);
+ return output;
}
+ } // end of namespace scribo::primitive::extract::internal
- component_set<L> output;
- std::cout << "Removing small elements" << std::endl;
- {
- image2d<bool> elts;
- initialize(elts, img_lbl8);
- data::fill(elts, false);
- data::fill((elts | (pw::value(img_lbl8) != pw::cst(0))).rw(), true);
- scribo::def::lbl_type nlabels;
- elts = filter::components_small(elts, c8(), nlabels, 40);
+ // FACADE
+
+ template <typename L>
+ component_set<L>
+ non_text(const document<L>& doc, unsigned nlines)
+ {
+ trace::entering("scribo::primitive::extract::non_text");
+
- output = primitive::extract::components(elts, c8(), nlabels);
+ util::timer t;
+ t.start();
+
+ mln_precondition(doc.is_valid());
+
+ mln_precondition(doc.has_line_seps());
+ mln_precondition(doc.has_text());
+
+ // FIXME: Do these images exist elsewhere?
+ image2d<bool>
+ txt = make::text_components_image(doc),
+ txtblocks = make::text_blocks_image(doc, nlines);
+
+ unsigned nbits = 5;
+ float p = 0.9998; // 0.80 <= x < 1.0
+ unsigned lambda = 1000;
+
+ // enlarge the text mask so that "not txt" does not include
+ // any text pixel
+ txt = morpho::elementary::dilation(txt, c8());
+ txt = morpho::elementary::dilation(txt, c4());
+
+ // FIXME: Make it faster?
+ data::fill((txtblocks | pw::value(txt)).rw(), false);
+
+ // Debug
+ {
+ debug::logger().log_image(debug::Special,//debug::AuxiliaryResults,
+ txt, "txt_components");
+ debug::logger().log_image(debug::Special,//debug::AuxiliaryResults,
+ txtblocks, "txt_blocks");
}
+ image2d<bool>
+ element_image = internal::learn(doc, txt, txtblocks, nbits, p);
+ element_image = internal::cleaning(element_image, lambda);
- std::cout << "Ignoring inner elements" << std::endl;
+ mln_value(L) ncomps;
+ component_set<L>
+ elements = primitive::extract::components(element_image,
+ c8(), ncomps);
+ // Debug
{
- // FIXME: We would like to use the convex hull instead of the bbox.
- internal::order_bbox<L> func(output);
- util::array<unsigned> box_ordered_comps;
- for (unsigned i = 1; i < output.nelements(); ++i)
- box_ordered_comps.append(i);
- std::sort(box_ordered_comps.hook_std_vector_().begin(),
- box_ordered_comps.hook_std_vector_().end(), func);
-
- image2d<bool> merged_elts;
- initialize(merged_elts, img_lbl8);
- data::fill(merged_elts, false);
- for (unsigned i = 0; i < box_ordered_comps.nelements(); ++i)
- {
- unsigned c = box_ordered_comps(i);
- point2d
- pminright = output(c).bbox().pmin(),
- pmaxleft = output(c).bbox().pmax();
- pminright.col() = output(c).bbox().pmax().col();
- pmaxleft.col() = output(c).bbox().pmin().col();
-
- if (merged_elts(output(c).bbox().pmin())
- && merged_elts(output(c).bbox().pmax())
- && merged_elts(pminright)
- && merged_elts(pmaxleft))
- output(c).update_tag(component::Ignored);
- else
- mln::draw::box_plain(merged_elts, output(c).bbox(), true);
- }
+ debug::logger().log_image(debug::Results,
+ elements.labeled_image(),
+ "non_text_components");
}
trace::exiting("scribo::primitive::extract::non_text");
- return output;
+ return elements;
}
# endif // ! MLN_INCLUDE_ONLY
diff --git a/scribo/scribo/primitive/extract/non_text.hh
b/scribo/scribo/primitive/extract/non_text_kmean.hh
similarity index 91%
copy from scribo/scribo/primitive/extract/non_text.hh
copy to scribo/scribo/primitive/extract/non_text_kmean.hh
index 8528782..c76ce11 100644
--- a/scribo/scribo/primitive/extract/non_text.hh
+++ b/scribo/scribo/primitive/extract/non_text_kmean.hh
@@ -30,8 +30,8 @@
///
/// \fixme To be optimized!
-#ifndef SCRIBO_PRIMITIVE_EXTRACT_NON_TEXT_HH
-# define SCRIBO_PRIMITIVE_EXTRACT_NON_TEXT_HH
+#ifndef SCRIBO_PRIMITIVE_EXTRACT_NON_TEXT_KMEAN_HH
+# define SCRIBO_PRIMITIVE_EXTRACT_NON_TEXT_KMEAN_HH
# include <mln/core/image/image2d.hh>
# include <mln/core/alias/neighb2d.hh>
@@ -72,7 +72,7 @@ namespace scribo
template <typename L, typename I>
component_set<L>
- non_text(const document<L>& doc, const Image<I>& input);
+ non_text_kmean(const document<L>& doc, const Image<I>& input);
# ifndef MLN_INCLUDE_ONLY
@@ -107,9 +107,9 @@ namespace scribo
template <typename L, typename I>
component_set<L>
- non_text(const document<L>& doc, const Image<I>& input_)
+ non_text_kmean(const document<L>& doc, const Image<I>& input_)
{
- trace::entering("scribo::primitive::extract::non_text");
+ trace::entering("scribo::primitive::extract::non_text_kmean");
const I& input = exact(input_);
mln_precondition(doc.is_valid());
@@ -136,7 +136,8 @@ namespace scribo
img_rgb5 = mln::data::transform(doc.image(), t_rgb8_to_rgb5());
img_lbl8 =
- mln::clustering::kmean_rgb<double,5>((img_rgb5 | pw::value(content)), 3, 10,
10).unmorph_();
+ mln::clustering::kmean_rgb<double,5>(
+ (img_rgb5 | pw::value(content)), 3, 10, 10).unmorph_();
data::fill((img_lbl8 | !pw::value(content)).rw(), 0u);
mln::util::array<unsigned>
@@ -207,7 +208,7 @@ namespace scribo
}
}
- trace::exiting("scribo::primitive::extract::non_text");
+ trace::exiting("scribo::primitive::extract::non_text_kmean");
return output;
}
@@ -220,4 +221,4 @@ namespace scribo
} // end of namespace scribo
-#endif // ! SCRIBO_PRIMITIVE_EXTRACT_NON_TEXT_HH
+#endif // ! SCRIBO_PRIMITIVE_EXTRACT_NON_TEXT_KMEAN_HH
--
1.5.6.5