Olena-patches
Threads by month
- ----- 2025 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
March 2010
- 4 participants
- 277 discussions
* mln/fun/v2v/all.hh: Include the new header.
* mln/fun/v2v/rgb_to_int_u.hh: New.
---
milena/ChangeLog | 8 +++++
milena/mln/fun/v2v/all.hh | 1 +
milena/mln/fun/v2v/{id.hh => rgb_to_int_u.hh} | 38 ++++++++----------------
3 files changed, 22 insertions(+), 25 deletions(-)
copy milena/mln/fun/v2v/{id.hh => rgb_to_int_u.hh} (72%)
diff --git a/milena/ChangeLog b/milena/ChangeLog
index c2335e8..73b9647 100644
--- a/milena/ChangeLog
+++ b/milena/ChangeLog
@@ -1,5 +1,13 @@
2009-12-14 Guillaume Lazzara <z(a)lrde.epita.fr>
+ Add rbg_to_int_u function.
+
+ * mln/fun/v2v/all.hh: Include the new header.
+
+ * mln/fun/v2v/rgb_to_int_u.hh: New.
+
+2009-12-14 Guillaume Lazzara <z(a)lrde.epita.fr>
+
Add an antialiased subsampling.
* mln/subsampling/antialiased.hh: New.
diff --git a/milena/mln/fun/v2v/all.hh b/milena/mln/fun/v2v/all.hh
index 425d24e..2c455a5 100644
--- a/milena/mln/fun/v2v/all.hh
+++ b/milena/mln/fun/v2v/all.hh
@@ -60,6 +60,7 @@ namespace mln
# include <mln/fun/v2v/linear.hh>
# include <mln/fun/v2v/norm.hh>
# include <mln/fun/v2v/projection.hh>
+# include <mln/fun/v2v/rgb_to_int_u.hh>
//<<lrde
# include <mln/fun/v2v/rgb_to_hsi.hh>
# include <mln/fun/v2v/rgb_to_hsl.hh>
diff --git a/milena/mln/fun/v2v/id.hh b/milena/mln/fun/v2v/rgb_to_int_u.hh
similarity index 72%
copy from milena/mln/fun/v2v/id.hh
copy to milena/mln/fun/v2v/rgb_to_int_u.hh
index 21398c7..d04dbf0 100644
--- a/milena/mln/fun/v2v/id.hh
+++ b/milena/mln/fun/v2v/rgb_to_int_u.hh
@@ -1,4 +1,4 @@
-// Copyright (C) 2007, 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -23,16 +23,9 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
-#ifndef MLN_FUN_V2V_ID_HH
-# define MLN_FUN_V2V_ID_HH
-
-/*! \file
- *
- * \brief Identity function.
- */
-
-# include <mln/fun/internal/selector.hh>
+#ifndef MLN_FUN_V2V_RGB_TO_INT_U_HH
+# define MLN_FUN_V2V_RGB_TO_INT_U_HH
namespace mln
{
@@ -43,25 +36,21 @@ namespace mln
namespace v2v
{
- // FIXME: Doc!
-
- template <typename T>
- struct id
- : fun::internal::selector_<T, T, id<T> >::ret
+ template <unsigned n>
+ struct rgb_to_int_u : Function_v2v< rgb_to_int_u<n> >
{
- typedef T result;
- T operator()(const T& t) const;
- };
+ typedef value::int_u<n> result;
+ result operator()(const value::rgb<n>& c) const;
+ };
# ifndef MLN_INCLUDE_ONLY
- template <typename T>
- inline
- T
- id<T>::operator()(const T& t) const
+ template <unsigned n>
+ typename rgb_to_int_u<n>::result
+ rgb_to_int_u<n>::operator()(const value::rgb<n>& c) const
{
- return t;
+ return (c.red() + c.green() + c.blue()) / 3;
}
# endif // ! MLN_INCLUDE_ONLY
@@ -72,5 +61,4 @@ namespace mln
} // end of namespace mln
-
-#endif // ! MLN_FUN_V2V_ID_HH
+#endif // ! MLN_FUN_V2V_RGB_TO_INT_U_HH
--
1.5.6.5
1
0
11 Mar '10
* mln/subsampling/antialiased.hh: New.
---
milena/ChangeLog | 6 +
milena/mln/subsampling/antialiased.hh | 421 +++++++++++++++++++++++++++++++++
2 files changed, 427 insertions(+), 0 deletions(-)
create mode 100644 milena/mln/subsampling/antialiased.hh
diff --git a/milena/ChangeLog b/milena/ChangeLog
index 39c2d80..c2335e8 100644
--- a/milena/ChangeLog
+++ b/milena/ChangeLog
@@ -1,5 +1,11 @@
2009-12-14 Guillaume Lazzara <z(a)lrde.epita.fr>
+ Add an antialiased subsampling.
+
+ * mln/subsampling/antialiased.hh: New.
+
+2009-12-14 Guillaume Lazzara <z(a)lrde.epita.fr>
+
Add hooks for subclasses in labeled_image_base.
* mln/core/internal/labeled_image_base.hh: New methods which can
diff --git a/milena/mln/subsampling/antialiased.hh b/milena/mln/subsampling/antialiased.hh
new file mode 100644
index 0000000..ccb947e
--- /dev/null
+++ b/milena/mln/subsampling/antialiased.hh
@@ -0,0 +1,421 @@
+// 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 MLN_SUBSAMPLING_ANTIALIASED_HH
+# define MLN_SUBSAMPLING_ANTIALIASED_HH
+
+/// \file
+///
+/// Antialiased subsampling.
+
+/// \fixme: shift is not used for the moment......
+
+#include <mln/core/concept/image.hh>
+#include <mln/border/thickness.hh>
+
+
+namespace mln
+{
+
+ namespace subsampling
+ {
+
+ /*! FIXME: Doc.
+ */
+ template <typename I>
+ inline
+ mln_concrete(I)
+ antialiased(const Image<I>& input,
+ unsigned gap,
+ const mln_deduce(I, site, delta)& shift,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness);
+
+
+ /*! \overload
+ */
+ template <typename I>
+ mln_concrete(I)
+ antialiased(const Image<I>& input,
+ unsigned gap,
+ const mln_deduce(I, site, delta)& shift); // FIXME: Add round_up_size.
+
+
+ /*! \overload
+ */
+ template <typename I>
+ mln_concrete(I)
+ antialiased(const Image<I>& input, unsigned gap);
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+ // Tests
+
+ namespace internal
+ {
+
+ template <typename I>
+ inline
+ void
+ antialiased_tests(const Image<I>& input,
+ unsigned gap,
+ const mln_deduce(I, site, delta)& shift,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ typedef mln_site(I) P;
+
+ mlc_is_a(mln_domain(I), Box)::check();
+ mln_precondition(exact(input).is_valid());
+ mln_precondition(exact(input).domain().pmin() == literal::origin);
+ mln_precondition(gap > 1);
+ for (unsigned i = 0; i < P::dim; ++i)
+ mln_precondition(shift[i] < static_cast<int>(gap));
+
+ (void) input;
+ (void) gap;
+ (void) shift;
+ (void) output_domain;
+ (void) border_thickness;
+ }
+
+
+ } // end of namespace mln::subsampling::internal
+
+
+
+
+ // Implementations.
+
+ namespace impl
+ {
+
+ namespace generic
+ {
+
+ template <typename I>
+ inline
+ mln_concrete(I)
+ antialiased(const Image<I>& input_,
+ unsigned gap,
+ const mln_deduce(I, site, delta)& shift,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ // To be written...
+ mlc_abort(I)::check();
+
+ mln_concrete(I) output;
+ return output;
+ }
+
+ } // end of namespace mln::subsampling::impl::generic
+
+
+ template <typename I>
+ inline
+ mln_concrete(I)
+ antialiased_2d_antialias_fastest_2(const Image<I>& input_,
+ const mln_deduce(I, site, delta)& shift,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ trace::entering("subsampling::impl::antialiased_2d_antialias_fastest_2");
+
+ internal::antialiased_tests(input_, 2, shift,
+ output_domain, border_thickness);
+
+ const I& input = exact(input_);
+
+ typedef mln_value(I) V;
+ typedef mln_sum(V) S;
+
+ typedef mln_site(I) P;
+ box<P> b = output_domain;
+ if (!b.is_valid())
+ {
+ P pmin = input.domain().pmin() / 2,
+ pmax = input.domain().pmax() / 2;
+ b = box<P>(pmin, pmax);
+ }
+
+ typedef mln_concrete(I) O;
+ O output(b, border_thickness);
+
+ const V* ptr1 = & input.at_(0, 0);
+ const V* ptr2 = & input.at_(1, 0);
+
+ mln_box_runstart_piter(O) s(output.domain());
+ const unsigned n = s.run_length();
+
+ unsigned offset = input.delta_index(point2d(2,0) - point2d(0,2*n));
+
+ for_all(s)
+ {
+ mln_value(O)* po = & output(s);
+ for (unsigned i = 0; i < n; ++i)
+ {
+ S s;
+ s = *ptr1 + *(ptr1 + 1);
+ s += *ptr2 + *(ptr2 + 1);
+ ptr1 += 2;
+ ptr2 += 2;
+
+ *po++ = (s + 2) / 4;
+ }
+ ptr1 += offset;
+ ptr2 += offset;
+ }
+
+ trace::exiting("subsampling::impl::antialiased_2d_antialias_fastest_2");
+ return output;
+ }
+
+
+
+ template <typename I>
+ inline
+ mln_concrete(I)
+ antialiased_2d_antialias_fastest_3(const Image<I>& input_,
+ const mln_deduce(I, site, delta)& shift,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ trace::entering("subsampling::impl::antialiased_2d_antialias_fastest_3");
+
+ internal::antialiased_tests(input_, 3, shift,
+ output_domain, border_thickness);
+
+ const I& input = exact(input_);
+
+ typedef mln_value(I) V;
+ typedef mln_sum(V) S;
+
+ typedef mln_site(I) P;
+ box<P> b = output_domain;
+ if (!b.is_valid())
+ {
+ P pmin = input.domain().pmin() / 3,
+ pmax = input.domain().pmax() / 3;
+ b = box<P>(pmin, pmax);
+ }
+ typedef mln_concrete(I) O;
+ O output(b, border_thickness);
+
+ const V* ptr1 = & input.at_(0, 0);
+ const V* ptr2 = & input.at_(1, 0);
+ const V* ptr3 = & input.at_(2, 0);
+
+ mln_box_runstart_piter(O) s(output.domain());
+ const unsigned n = s.run_length();
+
+ unsigned offset = input.delta_index(point2d(3,0) - point2d(0,3*n));
+
+ for_all(s)
+ {
+ mln_value(O)* po = & output(s);
+ for (unsigned i = 0; i < n; ++i)
+ {
+ S s;
+ s = *ptr1 + *(ptr1 + 1) + *(ptr1 + 2);
+ s += *ptr2 + *(ptr2 + 1) + *(ptr2 + 2);
+ s += *ptr3 + *(ptr3 + 1) + *(ptr3 + 2);
+ ptr1 += 3;
+ ptr2 += 3;
+ ptr3 += 3;
+
+ *po++ = (s + 4) / 9;
+ }
+ ptr1 += offset;
+ ptr2 += offset;
+ ptr3 += offset;
+ }
+
+ trace::exiting("subsampling::impl::antialiased_2d_antialias_fastest_3");
+ return output;
+ }
+
+
+
+ } // end of namespace mln::subsampling::impl
+
+
+
+ // Dispatch.
+
+ namespace internal
+ {
+
+ template <unsigned dim, typename I>
+ inline
+ mln_concrete(I)
+ antialiased_dispatch(trait::image::value_alignment::any,
+ trait::image::value_storage::any,
+ trait::image::value_access::any,
+ const Image<I>& input,
+ unsigned gap,
+ const mln_deduce(I, site, delta)& shift,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ // Not implemented yet.
+ mlc_abort(I)::check();
+ }
+
+ template <typename I>
+ inline
+ mln_concrete(I)
+ antialiased_2d_antialias_fastest_dispatch(
+ const Image<I>& input,
+ unsigned gap,
+ const mln_deduce(I, site, delta)& shift,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ if (gap == 2)
+ return impl::antialiased_2d_antialias_fastest_2(input, shift,
+ output_domain,
+ border_thickness);
+ else if (gap == 3)
+ return impl::antialiased_2d_antialias_fastest_3(input, shift,
+ output_domain,
+ border_thickness);
+ else
+ trace::warning("Not implemented yet!");
+
+ mln_concrete(I) output;
+ return output;
+ }
+
+
+ template <typename I>
+ inline
+ mln_concrete(I)
+ antialiased_dispatch_2d(trait::image::value_alignment::with_grid,
+ trait::image::value_storage::one_block,
+ trait::image::value_access::direct,
+ const Image<I>& input,
+ unsigned gap,
+ const mln_deduce(I, site, delta)& shift,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ return antialiased_2d_antialias_fastest_dispatch(input, gap,
+ shift,
+ output_domain,
+ border_thickness);
+ }
+
+
+ template <typename I>
+ inline
+ mln_concrete(I)
+ antialiased_dispatch(const Image<I>& input,
+ unsigned gap,
+ const mln_deduce(I, site, delta)& shift,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ unsigned dim = mln_site_(I)::dim;
+
+ if (dim == 2)
+ return antialiased_dispatch_2d(
+ mln_trait_image_value_alignment(I)(),
+ mln_trait_image_value_storage(I)(),
+ mln_trait_image_value_access(I)(),
+ input,
+ gap,
+ shift,
+ output_domain,
+ border_thickness);
+ else
+ trace::warning("Not implemented yet.");
+
+ mln_concrete(I) output;
+ return output;
+ }
+
+ } // end of namespace mln::subsampling::internal
+
+
+
+ // Facades.
+
+ template <typename I>
+ inline
+ mln_concrete(I)
+ antialiased(const Image<I>& input,
+ unsigned gap,
+ const mln_deduce(I, site, delta)& shift,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ trace::entering("subsampling::antialiased");
+
+ typedef mln_site(I) P;
+
+ internal::antialiased_tests(input, gap, shift,
+ output_domain, border_thickness);
+
+ mln_concrete(I) output;
+ output = internal::antialiased_dispatch(input, gap, shift,
+ output_domain, border_thickness);
+
+ trace::exiting("subsampling::antialiased");
+ return output;
+ }
+
+
+ template <typename I>
+ inline
+ mln_concrete(I)
+ antialiased(const Image<I>& input,
+ unsigned gap,
+ const mln_deduce(I, site, delta)& shift)
+ {
+ mln_domain(I) domain;
+ return antialiased(input, gap, shift, domain, border::thickness);
+ }
+
+
+ template <typename I>
+ inline
+ mln_concrete(I)
+ antialiased(const Image<I>& input, unsigned gap)
+ {
+ return antialiased(input, gap, literal::zero);
+ }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace mln::subsampling
+
+} // end of namespace mln
+
+
+#endif // ! MLN_SUBSAMPLING_ANTIALIASED_HH
--
1.5.6.5
1
0
last-svn-commit-21-g495dd2d Add hooks for subclasses in labeled_image_base.
by Guillaume Lazzara 11 Mar '10
by Guillaume Lazzara 11 Mar '10
11 Mar '10
* mln/core/internal/labeled_image_base.hh: New methods which can
be overridden in subclasses.
* mln/core/image/imorph/labeled_image.hh: Add a missing header.
---
milena/ChangeLog | 9 +++++++
milena/mln/core/image/imorph/labeled_image.hh | 2 +
milena/mln/core/internal/labeled_image_base.hh | 29 ++++++++++++++++++++++++
3 files changed, 40 insertions(+), 0 deletions(-)
diff --git a/milena/ChangeLog b/milena/ChangeLog
index bcaf644..39c2d80 100644
--- a/milena/ChangeLog
+++ b/milena/ChangeLog
@@ -1,3 +1,12 @@
+2009-12-14 Guillaume Lazzara <z(a)lrde.epita.fr>
+
+ Add hooks for subclasses in labeled_image_base.
+
+ * mln/core/internal/labeled_image_base.hh: New methods which can
+ be overridden in subclasses.
+
+ * mln/core/image/imorph/labeled_image.hh: Add a missing header.
+
2010-03-11 Guillaume Lazzara <z(a)lrde.epita.fr>
* mln/draw/dashed_line.hh: New draw line routine.
diff --git a/milena/mln/core/image/imorph/labeled_image.hh b/milena/mln/core/image/imorph/labeled_image.hh
index 438f6ae..fc151e8 100644
--- a/milena/mln/core/image/imorph/labeled_image.hh
+++ b/milena/mln/core/image/imorph/labeled_image.hh
@@ -34,6 +34,8 @@
# include <mln/core/routine/duplicate.hh>
+# include <mln/data/compute.hh>
+
# include <mln/util/array.hh>
# include <mln/accu/center.hh>
diff --git a/milena/mln/core/internal/labeled_image_base.hh b/milena/mln/core/internal/labeled_image_base.hh
index e79bb12..9e2d314 100644
--- a/milena/mln/core/internal/labeled_image_base.hh
+++ b/milena/mln/core/internal/labeled_image_base.hh
@@ -168,7 +168,13 @@ namespace mln
template <typename F>
void relabel_(const Function_v2b<F>& f);
+ /// Called during relabeling.
+ /// @{
+ void init_update_data_();
+ void prepare_update_data_(const mln_value(I)& lbl,
+ const mln_value(I)& new_lbl);
void update_data_(const fun::i2v::array<mln_value(I)>& relabel_fun);
+ /// @}
};
@@ -275,9 +281,14 @@ namespace mln
util::array<accu::shape::bbox<mln_psite(I)> >
new_bboxes(static_cast<unsigned>(this->data_->nlabels_) + 1);
+ exact(this)->init_update_data_();
+
for (unsigned i = 1; i < this->data_->bboxes_.size(); ++i)
if (relabel_fun(i) != 0)
+ {
new_bboxes[relabel_fun(i)].take(this->data_->bboxes_[i]);
+ exact(this)->prepare_update_data_(i, relabel_fun(i));
+ }
convert::from_to(new_bboxes, this->data_->bboxes_);
@@ -341,6 +352,24 @@ namespace mln
// No-Op.
}
+ template <typename I, typename E>
+ void
+ labeled_image_base<I,E>::prepare_update_data_(const mln_value(I)& lbl,
+ const mln_value(I)& new_lbl)
+ {
+ (void) lbl;
+ (void) new_lbl;
+ // No-Op.
+ }
+
+ template <typename I, typename E>
+ void
+ labeled_image_base<I,E>::init_update_data_()
+ {
+ // No-Op.
+ }
+
+
# endif // ! MLN_INCLUDE_ONLY
} // end of namespace mln
--
1.5.6.5
1
0
last-svn-commit-20-g27ae9e6 Improve stats computation in Sauvola Multi-scale.
by Guillaume Lazzara 11 Mar '10
by Guillaume Lazzara 11 Mar '10
11 Mar '10
* binarization/internal/first_pass_functor.hh: Use
sauvola_threshold routine.
* binarization/sauvola_threshold.hh: Remove debug and fix invalid
read in integral image.
* canvas/integral_browsing.hh,
* subsampling/integral_single_image.hh: Fix stats computation.
* src/binarization/sauvola_ms.cc: Fix window parameter and make
debug output optional.
---
scribo/ChangeLog | 16 +
scribo/binarization/internal/first_pass_functor.hh | 14 +-
scribo/binarization/sauvola_threshold.hh | 73 ++-
scribo/canvas/integral_browsing.hh | 58 ++-
scribo/src/binarization/sauvola_ms.cc | 740 ++++++++++++++++++--
scribo/subsampling/integral_single_image.hh | 100 ++-
6 files changed, 904 insertions(+), 97 deletions(-)
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index 9eac19c..0fcd35f 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,3 +1,19 @@
+2009-12-11 Guillaume Lazzara <z(a)lrde.epita.fr>
+
+ Improve Sauvola Multi-scale.
+
+ * binarization/internal/first_pass_functor.hh: Use
+ sauvola_threshold routine.
+
+ * binarization/sauvola_threshold.hh: Remove debug and fix invalid
+ read in integral image.
+
+ * canvas/integral_browsing.hh,
+ * subsampling/integral_single_image.hh: Fix stats computation.
+
+ * src/binarization/sauvola_ms.cc: Fix window parameter and make
+ debug output optional.
+
2009-12-04 Guillaume Lazzara <z(a)lrde.epita.fr>
Optimize Sauvola's multiscale binarization.
diff --git a/scribo/binarization/internal/first_pass_functor.hh b/scribo/binarization/internal/first_pass_functor.hh
index 13fc62a..6a9bbb9 100644
--- a/scribo/binarization/internal/first_pass_functor.hh
+++ b/scribo/binarization/internal/first_pass_functor.hh
@@ -98,8 +98,16 @@ namespace scribo
// Use an inlined and developed version of sauvola's
// threshold formula.
- value::int_u8 t_p = mean * (one_k + k_R * stddev);
-// value::int_u8 t_p = sauvola_threshold_formula(mean, stddev);
+// value::int_u8 t_p = mean * (one_k + k_R * stddev);
+
+// std::cout << t_p << ", ";
+
+// std::cout << input.element(p) << " - " << t_p << std::endl;
+ value::int_u8 t_p = sauvola_threshold_formula(mean, stddev);
+
+// std::cout << input.point_at_index(p)
+// << " - " << sauvola_threshold_formula(mean, stddev);
+
msk.element(p) = input.element(p) < t_p;
t_sub.element(p) = t_p;
@@ -128,6 +136,8 @@ namespace scribo
void finalize()
{
mln_assertion(! pxl.is_valid());
+
+// std::cout << std::endl << " ------- " << std::endl;
}
};
diff --git a/scribo/binarization/sauvola_threshold.hh b/scribo/binarization/sauvola_threshold.hh
index 857de1f..b1f9a81 100644
--- a/scribo/binarization/sauvola_threshold.hh
+++ b/scribo/binarization/sauvola_threshold.hh
@@ -46,6 +46,9 @@
# include <scribo/core/init_integral_image.hh>
+
+#include <mln/io/pgm/save.hh>
+
namespace scribo
{
@@ -168,8 +171,8 @@ namespace scribo
// Window half width.
int w_2 = win_width >> 1;
- int row_min = std::max(0, p.row() - w_2);
- int col_min = std::max(0, p.col() - w_2);
+ int row_min = std::max(0, p.row() - w_2 - 1);
+ int col_min = std::max(0, p.col() - w_2 - 1);
int row_max = std::min(static_cast<int>(simple.nrows()) - 1,
p.row() + w_2);
@@ -177,12 +180,7 @@ namespace scribo
p.col() + w_2);
-// std::cout << "sauvola threshold : "
-// << simple.domain() << " - "
-// << row_max << " - "
-// << col_max << std::endl;
-
- double wh = (row_max - row_min + 1) * (col_max - col_min + 1);
+ double wh = (row_max - row_min) * (col_max - col_min);
// Mean.
double m_x_y_tmp = (simple.at_(row_max, col_max)
@@ -200,9 +198,38 @@ namespace scribo
double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f));
+
+// if (p == point2d(3,3))// || p == point2d(4,4) || p == point2d(1,1))
+// {
+// // std::cout << "p" << p << " - A(" << row_min << ", " << col_min
+// // << ") = " << simple.at_(row_min, col_min)
+
+// << " - B(" << row_min << ", " << col_max
+// << ") = " << simple.at_(row_min, col_max)
+
+// << " - C(" << row_max << ", " << col_min
+// << ") = " << simple.at_(row_max, col_min)
+
+// << " - D(" << row_max << ", " << col_max
+// << ") = " << simple.at_(row_max, col_max)
+// << " - n = " << wh
+// << std::endl;
+
+// << std::endl;
+// }
+
// Thresholding.
double t_x_y = sauvola_threshold_formula(m_x_y, s_x_y, k, R);
+
+// std::cout << p
+// << " - m = " << m_x_y
+// << " - s = " << s_x_y
+// << " - t = " << t_x_y
+// << " - sum = " << m_x_y_tmp
+// << " - sum_2 = " << s_x_y_tmp
+// << std::endl;
+
return t_x_y;
}
@@ -219,19 +246,12 @@ namespace scribo
int row_min = std::max(0, p.row() - w_2);
int col_min = std::max(0, p.col() - w_2);
- //FIXME: offset (-4) should be replaced by the number of
- //padding pixels.
int row_max = std::min(static_cast<int>(integral.nrows()) - 1,
p.row() + w_2);
int col_max = std::min(static_cast<int>(integral.ncols()) - 1,
p.col() + w_2);
-// std::cout << "sauvola threshold : "
-// << simple.domain() << " - "
-// << row_max << " - "
-// << col_max << std::endl;
-
double wh = (row_max - row_min + 1) * (col_max - col_min + 1);
// Mean.
@@ -251,7 +271,7 @@ namespace scribo
double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f));
// Thresholding.
- double t_x_y = m_x_y * (1.0 + 0.14 * ((s_x_y / 128) - 1.0));
+ double t_x_y = m_x_y * (1.0 + 0.34 * ((s_x_y / 128) - 1.0));
return t_x_y;
}
@@ -438,6 +458,25 @@ namespace scribo
mln_precondition(mln_site_(I)::dim == 2);
mln_precondition(exact(input).is_valid());
+
+// {
+// J& simple_ = exact(simple);
+// J& squared_ = exact(squared);
+// mln_piter(J) p(simple_.domain());
+// for_all(p)
+// {
+// std::cout << simple_(p) << ", ";
+// }
+// std::cout << std::endl << " ------- " << std::endl;
+// for_all(p)
+// {
+// std::cout << squared_(p) << ", ";
+// }
+// std::cout << std::endl << " ------- " << std::endl;
+// }
+
+
+
typedef mln_value(I) value_t;
mln_ch_value(I, value::int_u8)
output = internal::sauvola_threshold_dispatch(value_t(), exact(input),
@@ -445,6 +484,8 @@ namespace scribo
exact(simple),
exact(squared));
+// std::cout << std::endl << " ------- " << std::endl;
+ io::pgm::save(output, "ref_2_t.pgm");
trace::exiting("scribo::text::ppm2pbm");
return output;
}
diff --git a/scribo/canvas/integral_browsing.hh b/scribo/canvas/integral_browsing.hh
index 814733f..44d73a0 100644
--- a/scribo/canvas/integral_browsing.hh
+++ b/scribo/canvas/integral_browsing.hh
@@ -58,9 +58,13 @@ namespace scribo
double& mean, double& stddev)
{
mean = sum / n;
- stddev = std::sqrt(sum_2 / n - mean * mean);
+// stddev = std::sqrt(sum_2 / n - mean * mean);
+
+// std::cout << "(" << mean << " - " << stddev << " - " << n << "),";
+
// unbias version:
- // stddev = std::sqrt((sum_2 - n * mean * mean) / (n - 1));
+ stddev = std::sqrt((sum_2 - sum * sum / n) / (n - 1));
+
}
} // end of namespace scribo::canvas::internal
@@ -72,6 +76,7 @@ namespace scribo
void integral_browsing(const image2d<util::couple<double, double> >& ima,
unsigned step,
unsigned w, unsigned h,
+ unsigned s,
F& functor)
{
typedef util::couple<double, double> V;
@@ -112,6 +117,8 @@ namespace scribo
double mean, stddev;
+ unsigned s_2 = s * s;
+
// -------------------------------
// T (top)
@@ -146,7 +153,7 @@ namespace scribo
// D
internal::compute_stats(d_ima->first(),
d_ima->second(),
- size_tl,
+ size_tl * s_2,
mean, stddev);
functor.exec(mean, stddev);
d_ima += step;
@@ -166,7 +173,7 @@ namespace scribo
// D - C
internal::compute_stats(d_ima->first() - c_ima->first(),
d_ima->second() - c_ima->second(),
- size_tc,
+ size_tc * s_2,
mean, stddev);
functor.exec(mean, stddev);
c_ima += step;
@@ -188,7 +195,7 @@ namespace scribo
// D* - C
internal::compute_stats(d_sum - c_ima->first(),
d_sum_2 - c_ima->second(),
- size_tr,
+ size_tr * s_2,
mean, stddev);
functor.exec(mean, stddev);
c_ima += step;
@@ -239,7 +246,7 @@ namespace scribo
// D - B
internal::compute_stats(d_ima->first() - b_ima->first(),
d_ima->second() - b_ima->second(),
- size_ml,
+ size_ml * s_2,
mean, stddev);
functor.exec(mean, stddev);
b_ima += step;
@@ -258,11 +265,40 @@ namespace scribo
for (; col <= max_col_mid; col += step)
{
// D + A - B - C
+
+// if (row == 3 && col == 3)
+// std::cout << "p(" << row << "," << col << ") - "
+
+// << "A" << ima.point_at_index(a_ima - ima.buffer())
+// << "=" << a_ima->first() << " - "
+
+// << "B" << ima.point_at_index(b_ima - ima.buffer())
+// << "=" << b_ima->first() << " - "
+
+// << "C" << ima.point_at_index(c_ima - ima.buffer())
+// << "=" << c_ima->first() << " - "
+
+// << "D" << ima.point_at_index(d_ima - ima.buffer())
+// << "=" << d_ima->first() << " - "
+
+// << "n =" << size_mc << " - "
+// << "n*s_2 =" << size_mc * s_2
+// << std::endl;
+
internal::compute_stats((d_ima->first() - b_ima->first()) + (a_ima->first() - c_ima->first()),
(d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()),
- size_mc,
+ size_mc * s_2,
mean, stddev);
+
functor.exec(mean, stddev);
+
+// std::cout << " - " << mean
+// << " - " << stddev
+// << " - " << (d_ima->first() - b_ima->first()) + (a_ima->first() - c_ima->first())
+// << " - " << (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second())
+// << std::endl;
+
+
a_ima += step;
b_ima += step;
c_ima += step;
@@ -283,7 +319,7 @@ namespace scribo
// D* + A - B* - C
internal::compute_stats(d_b_sum + (a_ima->first() - c_ima->first()),
d_b_sum_2 + (a_ima->second() - c_ima->second()),
- size_mr,
+ size_mr * s_2,
mean, stddev);
functor.exec(mean, stddev);
a_ima += step;
@@ -332,7 +368,7 @@ namespace scribo
// D* - B
internal::compute_stats(d_ima->first() - b_ima->first(),
d_ima->second() - b_ima->second(),
- size_bl,
+ size_bl * s_2,
mean, stddev);
functor.exec(mean, stddev);
b_ima += step;
@@ -354,7 +390,7 @@ namespace scribo
// D* + A - B - C*
internal::compute_stats((d_ima->first() - b_ima->first()) + (a_ima->first() - c_ima->first()),
(d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()),
- size_bc,
+ size_bc * s_2,
mean, stddev);
// std::cout << (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()) << std::endl;
@@ -385,7 +421,7 @@ namespace scribo
// D* + A - B* - C*
internal::compute_stats(d_b_sum + (a_ima->first() - c_ima->first()),
d_b_sum_2 + (a_ima->second() - c_ima->second()),
- size_br,
+ size_br * s_2,
mean, stddev);
functor.exec(mean, stddev);
a_ima += step;
diff --git a/scribo/src/binarization/sauvola_ms.cc b/scribo/src/binarization/sauvola_ms.cc
index 8a07dcc..ff5a99d 100644
--- a/scribo/src/binarization/sauvola_ms.cc
+++ b/scribo/src/binarization/sauvola_ms.cc
@@ -26,10 +26,10 @@
#include <mln/core/alias/neighb2d.hh>
#include <mln/data/stretch.hh>
#include <mln/data/paste.hh>
-// #include <mln/debug/iota.hh>
-// #include <mln/debug/quiet.hh>
-// #include <mln/debug/println.hh>
-// #include <mln/debug/println_with_border.hh>
+#include <mln/debug/iota.hh>
+#include <mln/debug/quiet.hh>
+#include <mln/debug/println.hh>
+#include <mln/debug/println_with_border.hh>
#include <mln/debug/filename.hh>
#include <mln/fun/i2v/array.hh>
#include <mln/io/pbm/all.hh>
@@ -96,6 +96,7 @@ namespace mln
image2d<int_u8>
compute_t_n_and_e_2(const image2d<int_u8>& sub, image2d<int_u8>& e_2,
unsigned lambda_min, unsigned lambda_max,
+ unsigned s,
unsigned q, unsigned i, unsigned w,
const image2d<util::couple<double,double> >& integral_sum_sum_2)
// lambdas: limits of component cardinality at this scale
@@ -111,22 +112,38 @@ namespace mln
tt.restart();
- unsigned w_2 = w / 2 * ratio;
+ unsigned
+ w_local = w * ratio,
+ w_local_h = w_local,
+ w_local_w = w_local;
+
+ if (! (w_local % 2))
+ {
+ --w_local_w;
+ ++w_local_h;
+ }
+
+// std::cout << "Echelle " << i
+// << " - w_local_h = " << w_local_h
+// << " - w_local_w = " << w_local_w
+// << " - w_1 = " << (w_local - 1) * s + 1
+// << std::endl;
+
+// std::cout << "Ratio = " << ratio << std::endl;
+// std::cout << " -----------" << std::endl;
// 1st pass
scribo::binarization::internal::first_pass_functor< image2d<int_u8> >
f(sub);
scribo::canvas::integral_browsing(integral_sum_sum_2,
ratio,
- w_2, w_2,
+ w_local_w, w_local_h,
+ s,
f);
-// debug::println("mask", f.msk);
-// debug::println("parent", f.parent);
-// debug::println("card", f.card);
t_ = tt;
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "1st pass - " << t_ << std::endl;
tt.restart();
@@ -175,7 +192,6 @@ namespace mln
{
for (unsigned l = 0; l < ptr.size(); ++l)
std::memset(ptr(l), i, ratio * sizeof(mln_value_(I)));
-// debug(sq) = i;
}
}
@@ -188,7 +204,6 @@ namespace mln
{
for (unsigned l = 0; l < ptr.size(); ++l)
std::memset(ptr(l), i, ratio * sizeof(mln_value_(I)));
-// debug(sq) = i;
}
}
@@ -204,12 +219,10 @@ namespace mln
}
t_ = tt;
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "2nd pass - " << t_ << std::endl;
// io::pgm::save(e_2, mln::debug::filename("e.pgm", i));
-// io::pgm::save(debug, mln::debug::filename("debug.pgm", i));
-// debug::println(msk);
// io::pbm::save(f.msk, mln::debug::filename("mask.pbm", i));
// io::pgm::save(data::stretch(int_u8(), card), mln::debug::filename("card.pgm"));
} // end of 2nd pass
@@ -245,9 +258,9 @@ namespace mln
// image at scale 1 does not always have a size which can be
// divided by (4*s), some sites in the border may not be processed
// and we must skip them.
- unsigned more_offset = in.border() - ((4 * s) - in.ncols() % (4 * s));
+ int more_offset = - ((4 * s) - in.ncols() % (4 * s));
- if (more_offset == (4 * s))
+ if (more_offset == - (static_cast<int>(4*s)))
more_offset = 0; // No offset needed.
const int
@@ -267,7 +280,7 @@ namespace mln
delta3 = t_ima[3].delta_index(dpoint2d(+1, -1)),
- eor1 = in.delta_index(dpoint2d(+4 * s, - in.ncols() - in.border())) + more_offset,
+ eor1 = in.delta_index(dpoint2d(+4 * s, - in.ncols())) + more_offset,
eor2 = t_ima[2].delta_index(dpoint2d(+4,- t_ima[2].ncols())),
eor3 = t_ima[3].delta_index(dpoint2d(+2,- t_ima[3].ncols())),
eor4 = t_ima[4].delta_index(dpoint2d(+1,- t_ima[4].ncols()));
@@ -700,6 +713,499 @@ namespace mln
+// template <typename I, typename J, typename K>
+// mln_ch_value(I, unsigned)
+// binarize_generic_debug(const I& in, const J& e2, const util::array<K>& t_ima,
+// unsigned s)
+// {
+// mln_ch_value(I,unsigned) out;
+// initialize(out, in);
+// data::fill(out, 0);
+
+// typedef const mln_value(K)* ptr_type;
+
+// ptr_type ptr_t[5];
+// ptr_t[2] = & t_ima[2].at_(0, 0);
+// ptr_t[3] = & t_ima[3].at_(0, 0);
+// ptr_t[4] = & t_ima[4].at_(0, 0);
+
+
+// const mln_value(J)* ptr_e2 = & e2.at_(0, 0);
+// const mln_value(I)* ptr__in = & in.at_(0, 0);
+// unsigned* ptr__out = & out.at_(0, 0);
+
+
+// // Since we iterate from a smaller image in the largest ones and
+// // image at scale 1 does not always have a size which can be
+// // divided by (4*s), some sites in the border may not be processed
+// // and we must skip them.
+
+// std::cout << in.ncols() << std::endl;
+// std::cout << in.ncols() % (4 * s) << std::endl;
+// int more_offset = - ((4 * s) - in.ncols() % (4 * s));
+
+// if (more_offset == - (4*s))
+// more_offset = 0; // No offset needed.
+
+// std::cout << "more_offset == " << more_offset << std::endl;
+// std::cout << "- b1 = " << in.border()
+// << "- b2 = " << t_ima[2].border()
+// << "- b3 = " << t_ima[3].border()
+// << "- b4 = " << t_ima[4].border()
+// << std::endl;
+
+// const int
+// nrows4 = t_ima[4].nrows(), ncols4 = t_ima[4].ncols(),
+
+
+// delta1 = in.delta_index(dpoint2d(+1, -(s - 1))),
+// delta1b = in.delta_index(dpoint2d(+1, -(s + s - 1))),
+// delta1c = in.delta_index(dpoint2d(-(s + s - 1), +1)),
+// delta1d = in.delta_index(dpoint2d(+1, -(s * 4 - 1))),
+// delta1e = in.delta_index(dpoint2d(-(s * 4 - 1), +1)),
+// delta1f = in.delta_index(dpoint2d(-(s - 1), +1)),
+
+// delta2 = t_ima[2].delta_index(dpoint2d(+1, -1)),
+// delta2b = t_ima[2].delta_index(dpoint2d(+1, -3)),
+// delta2c = t_ima[2].delta_index(dpoint2d(-3, +1)),
+
+// delta3 = t_ima[3].delta_index(dpoint2d(+1, -1)),
+
+// eor1 = in.delta_index(dpoint2d(+4 * s, - in.ncols())) + more_offset,
+// eor2 = t_ima[2].delta_index(dpoint2d(+4,- t_ima[2].ncols())),
+// eor3 = t_ima[3].delta_index(dpoint2d(+2,- t_ima[3].ncols())),
+// eor4 = t_ima[4].delta_index(dpoint2d(+1,- t_ima[4].ncols()));
+
+// unsigned pid = 0;
+
+// mln_value(J) threshold;
+// for (int row4 = 0; row4 < nrows4; ++row4)
+// {
+// for (int col4 = 0; col4 < ncols4; ++col4)
+// {
+// // top left 1
+// {
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1f; ptr__in += delta1f;
+// }
+
+// ++ptr_t[2]; ++ptr_e2;
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1b; ptr__in += delta1b;
+// }
+
+// ptr_t[2] += delta2; ptr_e2 += delta2;
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1f; ptr__in += delta1f;
+
+// }
+
+// ++ptr_t[2]; ++ptr_e2;
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1c; ptr__in += delta1c;
+// }
+
+// ptr_t[2] -= delta2; ptr_e2 -= delta2;
+// }
+
+// // top right 1
+// ptr_t[3] += 1;
+// {
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1f; ptr__in += delta1f;
+// }
+
+// ++ptr_t[2]; ++ptr_e2;
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1b; ptr__in += delta1b;
+// }
+
+// ptr_t[2] += delta2; ptr_e2 += delta2;
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1f; ptr__in += delta1f;
+// }
+
+// ++ptr_t[2]; ++ptr_e2;
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1d; ptr__in += delta1d;
+// }
+
+// ptr_t[2] += delta2b; ptr_e2 += delta2b;
+// }
+
+// // bot left 1
+// ptr_t[3] += delta3;
+// {
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1f; ptr__in += delta1f;
+// }
+
+// ++ptr_t[2]; ++ptr_e2;
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1b; ptr__in += delta1b;
+// }
+
+// ptr_t[2] += delta2; ptr_e2 += delta2;
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1f; ptr__in += delta1f;
+// }
+
+// ++ptr_t[2]; ++ptr_e2;
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1c; ptr__in += delta1c;
+// }
+
+// ptr_t[2] -= delta2; ptr_e2 -= delta2;
+// }
+
+// // bot right 1
+// ptr_t[3] += 1;
+// {
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1f; ptr__in += delta1f;
+// }
+
+// ++ptr_t[2]; ++ptr_e2;
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1b; ptr__in += delta1b;
+// }
+
+// ptr_t[2] += delta2; ptr_e2 += delta2;
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1f; ptr__in += delta1f;
+// }
+
+// ++ptr_t[2]; ++ptr_e2;
+// threshold = *ptr_t[*ptr_e2];
+// {
+// for (unsigned i = 1; i < s; ++i)
+// {
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+
+// *ptr__out = pid++;
+// ptr__out += delta1; ptr__in += delta1;
+// }
+
+// for (unsigned j = 1; j < s; ++j)
+// {
+// *ptr__out = pid++;
+// ++ptr__out; ++ptr__in;
+// }
+// *ptr__out = pid++;
+// ptr__out += delta1e; ptr__in += delta1e;
+// }
+// }
+
+// // bot right -> next top left
+// ptr_t[2] += delta2c; ptr_e2 += delta2c;
+// ptr_t[3] = ptr_t[3] - delta3;
+// ptr_t[4] += 1;
+// }
+
+// // eof -> next bof
+// ptr__out += eor1; ptr__in += eor1;
+// ptr_t[2] += eor2; ptr_e2 += eor2;
+// ptr_t[3] += eor3;
+// ptr_t[4] += eor4;
+// }
+
+
+// // mln::debug::println(out);
+
+// return out;
+// }
+
+
+
unsigned sub(unsigned nbr, unsigned down_scaling)
{
@@ -807,9 +1313,9 @@ namespace mln
const char *args_desc[][2] =
{
{ "input.pgm", "A graylevel image." },
- { "w", "Window size. (Common value: 51)" },
+ { "w", "Window size at scale 1. (Common value: 101)" },
{ "s", "First subsampling ratio (Common value: 2)." },
- { "min_area", "Minimum object area (relative to scale 2) (Common value: 200)" },
+ { "min_area", "Minimum object area (at scale 2) (Common value: 200)" },
{ "debug", "Display debug/bench data if set to 1" },
{0, 0}
};
@@ -838,12 +1344,14 @@ int main(int argc, char *argv[])
mln::debug::internal::filename_prefix = argv[1];
- // Window size.
- unsigned w = atoi(argv[2]);
-
// First subsampling scale.
unsigned s = atoi(argv[3]);
+ // Window size.
+ unsigned
+ w_1 = atoi(argv[2]), // Scale 1
+ w_work = w_1 / s; // Scale 2
+
// Number of subscales.
unsigned nb_subscale = 3;//atoi(argv[3]);
@@ -856,8 +1364,8 @@ int main(int argc, char *argv[])
mln::debug::quiet = ! atoi(argv[6]);
- if (mln::debug::quiet)
- std::cout << "Running Sauvola_ms with w = " << w
+ if (! mln::debug::quiet)
+ std::cout << "Running Sauvola_ms with w_1 = " << w_1
<< ", s = " << s
<< ", nb_subscale = " << nb_subscale
<< ", q = " << q
@@ -877,13 +1385,13 @@ int main(int argc, char *argv[])
io::pgm::load(input_full, argv[1]);
{
- unsigned max_dim = math::max(input_full.ncols() / 2,
- input_full.nrows() / 2);
- if (w > max_dim)
+ unsigned max_dim = math::max(input_full.ncols() / s,
+ input_full.nrows() / s);
+ if (w_work > max_dim)
{
std::cout << "------------------" << std::endl;
std::cout << "The window is too large! Image size is only "
- << input_full.nrows() << "x" << input_full.ncols()
+ << input_full.nrows() / s << "x" << input_full.ncols() / s
<< std::endl
<< "Window size must not exceed " << max_dim
<< std::endl;
@@ -927,7 +1435,7 @@ int main(int argc, char *argv[])
util::array<util::couple<box2d, unsigned> >
sub_domains = compute_sub_domains(input_full, nb_subscale, s);
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "adjusting input border to " << sub_domains(1).second()
<< std::endl;
@@ -937,15 +1445,16 @@ int main(int argc, char *argv[])
// mln::debug::println_with_border(input_full);
t_ = timer_;
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "sub domains computed and adjust input border size - "
<< t_ << std::endl;
// Resize input and compute integral images.
timer_.restart();
- image2d<util::couple<double,double> > integral_sum_sum_2;
+ typedef image2d<util::couple<double,double> > integral_t;
+ integral_t integral_sum_sum_2;
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "sub_domain(2).domain() == " << sub_domains(2).first() << std::endl;
sub_ima.append(scribo::subsampling::integral(input_full, s,
@@ -953,10 +1462,26 @@ int main(int argc, char *argv[])
sub_domains[2].first(),
sub_domains[2].second()));
+// {
+// io::pgm::save(sub_ima[2], "in_50p.pgm");
+// mln_piter_(integral_t) p(integral_sum_sum_2.domain());
+// for_all(p)
+// {
+// std::cout << integral_sum_sum_2(p).first() << ", ";
+// }
+// std::cout << std::endl << " ------- " << std::endl;
+
+// for_all(p)
+// {
+// std::cout << integral_sum_sum_2(p).second() << ", ";
+// }
+// std::cout << std::endl << " ------- " << std::endl;
+// }
+
// mln::debug::println(integral_sum_sum_2);
t_ = timer_;
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "subsampling 1 -> 2 And integral images - " << t_
<< " - nsites = "
<< input_full.domain().nsites() << " -> "
@@ -972,7 +1497,7 @@ int main(int argc, char *argv[])
sub_domains[i].first(),
sub_domains[i].second()));
t_ = timer_;
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "subsampling " << (i - 1) << " -> " << i
<< " - " << t_
<< " - nsites = "
@@ -995,10 +1520,11 @@ int main(int argc, char *argv[])
t_ima[i] = compute_t_n_and_e_2(sub_ima[i], e_2,
lambda_min / ratio,
mln_max(unsigned),
- q, i, w, integral_sum_sum_2);
+ s,
+ q, i, w_work, integral_sum_sum_2);
t_ = timer_;
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "Scale " << i
<< " - 1/" << s * ratio
<< " compute t_n and update e - " << t_ << std::endl;
@@ -1013,10 +1539,11 @@ int main(int argc, char *argv[])
t_ima[i] = compute_t_n_and_e_2(sub_ima[i], e_2,
lambda_min / ratio,
lambda_max / ratio,
- q, i, w, integral_sum_sum_2);
+ s,
+ q, i, w_work, integral_sum_sum_2);
t_ = timer_;
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "Scale " << i
<< " - 1/" << s * ratio
<< " compute t_n and update e - " << t_ << std::endl;
@@ -1027,16 +1554,16 @@ int main(int argc, char *argv[])
{
timer_.restart();
t_ima[2] = compute_t_n_and_e_2(sub_ima[2], e_2, 0, lambda_max,
- 1, 2, w, integral_sum_sum_2);
+ s, 1, 2, w_work, integral_sum_sum_2);
t_ = timer_;
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "Scale " << 2
<< " - 1/" << s
<< " compute t_n and update e - " << t_ << std::endl;
}
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "--------" << std::endl;
// io::pgm::save(e_2, mln::debug::filename("e.pgm"));
@@ -1045,7 +1572,7 @@ int main(int argc, char *argv[])
timer_.restart();
e_2 = transform::influence_zone_geodesic(e_2, c8());
t_ = timer_;
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "influence zone - " << t_ << std::endl;
@@ -1058,17 +1585,138 @@ int main(int argc, char *argv[])
// for (unsigned i = 2; i < t_ima.size(); ++i)
// io::pgm::save(t_ima[i], mln::debug::filename("t.pgm", i));
+// {
+// image2d<bool> out_2;
+// initialize(out_2, e_2);
+// mln_piter_(image2d<int_u8>) p(e_2.domain());
+// for_all(p)
+// {
+// out_2(p) = sub_ima[2](p) < t_ima[2](p);
+// }
+// io::pbm::save(out_2, argv[5]);
+// }
+
timer_.restart();
image2d<bool> out_new = binarize_generic(input_full, e_2, t_ima, s);
// image2d<bool> out_new = binarize(input_full, e_2, t_ima);
t_ = timer_;
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "Compute bin - " << t_ << std::endl;
t_ = sauvola_timer_;
- if (mln::debug::quiet)
+ if (! mln::debug::quiet)
std::cout << "Sauvola : " << t_ << std::endl;
- io::pbm::save(out_new, argv[5]);
+
+ io::pbm::save(out_new, argv[5]);
+// abort();
}
+
+
+
+// int main(int argc, char *argv[])
+// {
+// using namespace mln;
+// using namespace scribo;
+// using value::rgb8;
+// using value::int_u8;
+// using value::int_u16;
+// using value::label_16;
+
+// typedef image2d<label_16> L;
+
+// unsigned s = atoi(argv[3]);
+
+
+// typedef image2d<value::int_u8> I;
+// dpoint2d none(0, 0);
+
+// I input_full(atoi(argv[1]),atoi(argv[2]));
+// // I input_full(30,20); // Cas pourri
+// // I input_full(30,24); // Cas 'row <<'
+// // I input_full(36,24); // Cas ideal
+// // I input_full(36,20); // Cas 'col <<'
+// mln::debug::iota(input_full);
+
+// mln::debug::println(input_full);
+
+
+// util::array<I> sub_ima;
+// util::array<I> t_ima;
+
+
+// // Make sure t_ima indexes start from 2.
+// {
+// I dummy(1,1);
+// for (unsigned i = 0; i < 3 + 2; ++i)
+// t_ima.append(dummy);
+// }
+
+// // Make sure sub_ima indexes start from 2.
+// {
+// I dummy(1,1);
+// sub_ima.append(dummy);
+// sub_ima.append(dummy);
+// }
+
+
+
+// util::array<util::couple<box2d, unsigned> >
+// sub_domains = compute_sub_domains(input_full, 3, s);
+
+// border::adjust(input_full, sub_domains(1).second());
+// border::mirror(input_full);
+
+// // Resize input and compute integral images.
+// typedef image2d<util::couple<double,double> > integral_t;
+// integral_t integral_sum_sum_2;
+
+// sub_ima.append(scribo::subsampling::integral(input_full, s,
+// integral_sum_sum_2,
+// sub_domains[2].first(),
+// sub_domains[2].second()));
+
+// std::cout << "input border = " << input_full.border() << std::endl;
+// std::cout << "subsampling 1 -> 2 And integral images - "
+// << " - nsites = "
+// << input_full.domain().nsites() << " -> "
+// << sub_ima[2].domain().nsites() << " - "
+// << input_full.domain() << " -> "
+// << sub_ima[2].domain() << std::endl;
+
+
+// for (unsigned i = 3; i <= 3 + 1; ++i)
+// {
+// sub_ima.append(mln::subsampling::antialiased(sub_ima[i - 1], 2, none,
+// sub_domains[i].first(),
+// sub_domains[i].second()));
+// std::cout << "subsampling " << (i - 1) << " -> " << i
+// << " - "
+// << " - nsites = "
+// << sub_ima[i].domain().nsites() << " - "
+// << sub_ima[i].domain()
+// << std::endl;
+// t_ima[i] = I(sub_ima[i].domain());
+// }
+
+
+
+
+
+// image2d<int_u8> e_2;
+// initialize(e_2, sub_ima[2]);
+// data::fill(e_2, 2);
+
+// data::fill(t_ima[2], 30);
+
+// image2d<unsigned> out_new = binarize_generic_debug(input_full, e_2, t_ima, s);
+
+// mln::debug::println_with_border(out_new);
+// // io::pgm::save(out_new, "out.pgm");
+
+// std::cout << "------------" << std::endl;
+
+// // out_new = binarize_generic_debug(input_full, e_2, t_ima, 2);
+// // mln::debug::println(out_new);
+// }
diff --git a/scribo/subsampling/integral_single_image.hh b/scribo/subsampling/integral_single_image.hh
index 07b391d..6ed1cc6 100644
--- a/scribo/subsampling/integral_single_image.hh
+++ b/scribo/subsampling/integral_single_image.hh
@@ -107,7 +107,6 @@ namespace scribo
const I& input = exact(input_);
J& integral_sum_sum_2 = exact(integral_sum_sum_2_);
- const unsigned area = scale * scale;
mln_precondition(input.is_valid());
mln_precondition(input.domain().pmin() == literal::origin);
@@ -142,19 +141,22 @@ namespace scribo
const V* ptr3 = & input.at_(row + 2, 0);
for (unsigned col = 0; col < ncols; col += scale)
{
- S sum;
- sum = *ptr1 + *(ptr1 + 1) + *(ptr1 + 2);
- sum += *ptr2 + *(ptr2 + 1) + *(ptr2 + 2);
- sum += *ptr3 + *(ptr3 + 1) + *(ptr3 + 2);
+ V v11 = *ptr1, v12 = *(ptr1 + 1), v13 = *(ptr1 + 2),
+ v21 = *ptr2, v22 = *(ptr2 + 1), v23 = *(ptr2 + 2),
+ v31 = *ptr3, v32 = *(ptr3 + 1), v33 = *(ptr3 + 2);
ptr1 += 3;
ptr2 += 3;
ptr3 += 3;
+ S local_sum = v11 + v12 + v13
+ + v21 + v22 + v23
+ + v31 + v32 + v33,
+ local_sum_2 = v11*v11 + v12*v12 + v13*v13
+ + v21*v21 + v22*v22 + v23*v23
+ + v31*v31 + v32*v32 + v33*v33;
- S val = sum / area;
- *p_sub++ = val;
-
- h_sum += val;
- h_sum_2 += val * val;
+ *p_sub++ = local_sum / 9;
+ h_sum += local_sum;
+ h_sum_2 += local_sum_2;
// exception
p_integ->first() = h_sum;
@@ -172,24 +174,27 @@ namespace scribo
for (row += scale; row < nrows; row += scale)
{
S h_sum = 0, h_sum_2 = 0;
- const V* ptr1 = & input.at_(row, 0);
+ const V* ptr1 = & input.at_(row, 0);
const V* ptr2 = & input.at_(row + 1, 0);
const V* ptr3 = & input.at_(row + 2, 0);
for (unsigned col = 0; col < ncols; col += scale)
{
- S sum;
- sum = *ptr1 + *(ptr1 + 1) + *(ptr1 + 2);
- sum += *ptr2 + *(ptr2 + 1) + *(ptr2 + 2);
- sum += *ptr3 + *(ptr3 + 1) + *(ptr3 + 2);
+ V v11 = *ptr1, v12 = *(ptr1 + 1), v13 = *(ptr1 + 2),
+ v21 = *ptr2, v22 = *(ptr2 + 1), v23 = *(ptr2 + 2),
+ v31 = *ptr3, v32 = *(ptr3 + 1), v33 = *(ptr3 + 2);
ptr1 += 3;
ptr2 += 3;
ptr3 += 3;
+ S local_sum = v11 + v12 + v13
+ + v21 + v22 + v23
+ + v31 + v32 + v33,
+ local_sum_2 = v11*v11 + v12*v12 + v13*v13
+ + v21*v21 + v22*v22 + v23*v23
+ + v31*v31 + v32*v32 + v33*v33;
- S val = sum / area;
- *p_sub++ = val;
-
- h_sum += val;
- h_sum_2 += val * val;
+ *p_sub++ = local_sum / 9;
+ h_sum += local_sum;
+ h_sum_2 += local_sum_2;
p_integ->first() = h_sum + (p_integ + up)->first();
p_integ->second() = h_sum_2 + (p_integ + up)->second();
@@ -220,7 +225,6 @@ namespace scribo
const I& input = exact(input_);
J& integral_sum_sum_2 = exact(integral_sum_sum_2_);
- const unsigned area = scale * scale;
typedef mln_value(I) V;
typedef mln_sum(V) S;
@@ -257,6 +261,7 @@ namespace scribo
const V* ptr2 = & input.at_(row + 1, 0);
for (unsigned col = 0; col < ncols; col += scale)
{
+/*
S sum;
sum = *ptr1 + *(ptr1 + 1);
sum += *ptr2 + *(ptr2 + 1);
@@ -268,6 +273,22 @@ namespace scribo
h_sum += val;
h_sum_2 += val * val;
+*/
+
+ // NEW:
+
+ V v11 = *ptr1, v12 = *(ptr1 + 1),
+ v21 = *ptr2, v22 = *(ptr2 + 1);
+ ptr1 += 2;
+ ptr2 += 2;
+ S local_sum = v11 + v12 + v21 + v22,
+ local_sum_2 = v11*v11 + v12*v12 + v21*v21 + v22*v22;
+ *p_sub++ = local_sum / 4;
+ h_sum += local_sum;
+ h_sum_2 += local_sum_2;
+
+ // end of NEW.
+
// exception
p_integ->first() = h_sum;
@@ -289,6 +310,29 @@ namespace scribo
const V* ptr2 = & input.at_(row + 1, 0);
for (unsigned col = 0; col < ncols; col += scale)
{
+ // NEW:
+
+ V v11 = *ptr1, v12 = *(ptr1 + 1),
+ v21 = *ptr2, v22 = *(ptr2 + 1);
+ ptr1 += 2;
+ ptr2 += 2;
+ S local_sum = v11 + v12 + v21 + v22,
+ local_sum_2 = v11*v11 + v12*v12 + v21*v21 + v22*v22;
+ *p_sub++ = local_sum / 4;
+ h_sum += local_sum;
+ h_sum_2 += local_sum_2;
+
+ // end of NEW.
+
+
+ /*
+
+ // To get the strict equivalent to the integral image
+ // computed at working scale (scale (2)), we need the code
+ // below. In addition, the integral_browsing shall call
+ // the threshold formula with (..size..) and NOT with
+ // (..size * s_2..).
+
S sum;
sum = *ptr1 + *(ptr1 + 1);
sum += *ptr2 + *(ptr2 + 1);
@@ -297,10 +341,22 @@ namespace scribo
S val = sum / area;
*p_sub++ = val;
-
h_sum += val;
h_sum_2 += val * val;
+ */
+
+
+ // Never write something like this:
+ // *p_sub++ = sum / area;
+ // h_sum += sum;
+ // h_sum_2 += sum * sum;
+ // because the product 'sum * sum' is not
+ // equivalent to the sum of the value^2. E.g.
+ // we have (v1 + v2 + v3 + v4)^2 + etc. instead
+ // of having the correct sum_2 being v1^2 + v2^2 etc.
+
+
p_integ->first() = h_sum + (p_integ + up)->first();
p_integ->second() = h_sum_2 + (p_integ + up)->second();
--
1.5.6.5
1
0
11 Mar '10
* mln/fun/all.hh: Include a new file.
* mln/fun/n2v/all.hh,
* mln/fun/n2v/white_gaussian.hh: New.
---
milena/ChangeLog | 9 +++
milena/mln/fun/all.hh | 2 +
milena/mln/fun/{stat => n2v}/all.hh | 17 ++---
.../fun/{v2w2v/cos.hh => n2v/white_gaussian.hh} | 75 +++++++++++--------
4 files changed, 62 insertions(+), 41 deletions(-)
copy milena/mln/fun/{stat => n2v}/all.hh (79%)
copy milena/mln/fun/{v2w2v/cos.hh => n2v/white_gaussian.hh} (61%)
diff --git a/milena/ChangeLog b/milena/ChangeLog
index 9a870bf..fae42e1 100644
--- a/milena/ChangeLog
+++ b/milena/ChangeLog
@@ -1,3 +1,12 @@
+2010-02-24 Guillaume Lazzara <z(a)lrde.epita.fr>
+
+ Add white gaussian noise function.
+
+ * mln/fun/all.hh: Include a new file.
+
+ * mln/fun/n2v/all.hh,
+ * mln/fun/n2v/white_gaussian.hh: New.
+
2010-02-16 Guillaume Lazzara <z(a)lrde.epita.fr>
Cleanup to_qimage convertion routines.
diff --git a/milena/mln/fun/all.hh b/milena/mln/fun/all.hh
index b353f38..b4f3ebb 100644
--- a/milena/mln/fun/all.hh
+++ b/milena/mln/fun/all.hh
@@ -57,6 +57,8 @@ namespace mln
// Sub-directories.
+# include <mln/fun/n2v/all.hh>
+
# include <mln/fun/i2v/all.hh>
//<<lrde
# include <mln/fun/meta/all.hh>
diff --git a/milena/mln/fun/stat/all.hh b/milena/mln/fun/n2v/all.hh
similarity index 79%
copy from milena/mln/fun/stat/all.hh
copy to milena/mln/fun/n2v/all.hh
index d207047..0e0e55c 100644
--- a/milena/mln/fun/stat/all.hh
+++ b/milena/mln/fun/n2v/all.hh
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2010 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -23,12 +23,12 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
-#ifndef MLN_FUN_STAT_ALL_HH
-# define MLN_FUN_STAT_ALL_HH
+#ifndef MLN_FUN_N2V_ALL_HH
+# define MLN_FUN_N2V_ALL_HH
/// \file
///
-/// File that includes all statistical functions.
+/// File that includes all functions from nil to value.
namespace mln
@@ -37,16 +37,15 @@ namespace mln
namespace fun
{
- /// \brief Namespace of statistical functions.
+ /// \brief Namespace of functions from nil to value.
///
/// \ingroup modfun
- namespace stat {}
+ namespace n2v {}
}
}
-# include <mln/fun/stat/mahalanobis.hh>
+# include <mln/fun/n2v/white_gaussian.hh>
-
-#endif // ! MLN_FUN_STAT_ALL_HH
+#endif // ! MLN_FUN_N2V_ALL_HH
diff --git a/milena/mln/fun/v2w2v/cos.hh b/milena/mln/fun/n2v/white_gaussian.hh
similarity index 61%
copy from milena/mln/fun/v2w2v/cos.hh
copy to milena/mln/fun/n2v/white_gaussian.hh
index 7616e05..53afbf6 100644
--- a/milena/mln/fun/v2w2v/cos.hh
+++ b/milena/mln/fun/n2v/white_gaussian.hh
@@ -1,4 +1,4 @@
-// Copyright (C) 2007, 2008, 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2010 EPITA Research and Development Laboratory (LRDE)
//
// This file is part of Olena.
//
@@ -23,21 +23,18 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
-#ifndef MLN_FUN_V2W2V_COS_HH
-# define MLN_FUN_V2W2V_COS_HH
+#ifndef MLN_FUN_N2V_WHITE_GAUSSIAN_HH
+# define MLN_FUN_N2V_WHITE_GAUSSIAN_HH
-/*! \file
- *
- * \brief Cos functor.
- *
- * \see mln/math/.
- */
+/// \file
+///
+/// White Gaussian noise.
+
+# include <cstdlib>
+# include <ctime>
# include <mln/core/concept/function.hh>
-# include <mln/trait/value_.hh>
-# include <mln/math/cos.hh>
-# include <mln/math/acos.hh>
namespace mln
@@ -46,48 +43,62 @@ namespace mln
namespace fun
{
- namespace v2w2v
+ namespace n2v
{
- /*! \brief Cosinus bijective functor.
- *
- * \c V is the type of input values and the result type.
- *
- * \see mln::math::cos.
+ /*! \brief Generate a White Gaussian Noise.
+
+
+ Reference:
+ http://www.dspguru.com/dsp/howtos/how-to-generate-white-gaussian-noise
*/
template <typename V>
- struct cos : public Function_v2v< cos<V> >
+ struct white_gaussian : Function_n2v< white_gaussian<V> >
{
typedef V result;
- V operator()(const V& v) const;
- V f_1 (const V& v) const;
+
+ white_gaussian(double sigma_);
+ V operator()() const;
+
+ double sigma;
};
+
# ifndef MLN_INCLUDE_ONLY
+
template <typename V>
- inline
- V
- cos<V>::operator()(const V& v) const
+ white_gaussian<V>::white_gaussian(double sigma_)
+ : sigma(sigma_)
{
- return mln::math::cos (v);
+ srand(time(0));
}
+
template <typename V>
- inline
- V
- cos<V>::f_1(const V& v) const
+ V white_gaussian<V>::operator()() const
{
- return mln::math::acos (v);
- }
+ double X = 0;
+ for (int i = 1; i < 50; ++i)
+ {
+ double U = rand() / (float)RAND_MAX;
+ X = X + U;
+ }
+
+ X = X - 25.f; /* set mean to 0 */
+ X = X * sqrt(12 / 50.f); /* adjust variance to 1 */
+
+ return sigma * X;
+ };
+
# endif // ! MLN_INCLUDE_ONLY
- } // end of namespace mln::fun::v2w2v
+ } // end of namespace mln::fun::n2v
} // end of namespace mln::fun
} // end of namespace mln
-#endif // ! MLN_FUN_V2W2V_COS_HH
+#endif // ! MLN_FUN_N2V_WHITE_GAUSSIAN_HH
--
1.5.6.5
1
0
11 Mar '10
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Olena, a generic and efficient image processing platform".
The branch exp/milena-z has been updated
via ce6a4248b4d98c34ba5ec5022ae68cc240553dd4 (commit)
via 706148bd7bd2a1138c2aee91f0e1463540deaa52 (commit)
from 78149d2acd630f6988e0cbca5682f91213540416 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
No new revisions were added by this update.
-----------------------------------------------------------------------
Summary of changes:
milena/ChangeLog | 13 ++++
milena/mln/draw/{line.hh => dashed_line.hh} | 60 +++++++++++-----
milena/mln/fun/all.hh | 2 +
milena/mln/fun/{stat => n2v}/all.hh | 17 ++---
.../fun/{v2w2v/cos.hh => n2v/white_gaussian.hh} | 75 +++++++++++--------
5 files changed, 106 insertions(+), 61 deletions(-)
copy milena/mln/draw/{line.hh => dashed_line.hh} (61%)
copy milena/mln/fun/{stat => n2v}/all.hh (79%)
copy milena/mln/fun/{v2w2v/cos.hh => n2v/white_gaussian.hh} (61%)
hooks/post-receive
--
Olena, a generic and efficient image processing platform
1
0
11 Mar '10
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Olena, a generic and efficient image processing platform".
The branch exp/scribo-z has been created
at fd14f7491b6b1e1fd681245831975fb2b0cbf444 (commit)
- Log -----------------------------------------------------------------
fd14f74 Add a new example for finding text in pictures.
3395e22 First draft of the dematerialization wizard.
04e5e93 Update examples.
765dcd0 Improve text line merging algorithm.
f334432 Add a reconstruction algorithm.
908bb22 Small fixes.
c79f409 Add new preprocessing routines.
a8aeee1 Update code according to last changes in Scribo core classes.
2773488 Improve debug routines in Scribo.
3ddc051 Improve core classes in Scribo.
83e3ae6 Share internal data in groups and links structures.
5fe933b text/merging.hh (scribo): Update code with lastest Theo's changes.
56ab608 Add a small tool to recognize text in a binary image.
d957e47 mln/labeling/colorize.hh: Add support for value::qt::rgb32.
466ad51 Add a subsampling algorithm.
9d7ad31 Add a first draft of a text line merging algorithm.
65c1cd1 Add and update examples.
d7f2adc Small fixes.
ad5f900 Keep updating code according to last changed in core classes.
7c750d4 Improve core classes.
27b5f71 configure.ac: Configure scribo/demo/review.
800a27b Move Scribo demo.
b41ec08 Small fixes in Milena.
41b6112 io/xml/save_text_lines.hh: New. Add partial support for PageContent XML format.
b3822e8 Introduce new Scribo core classes and start using them.
c24feb9 Add anchor support in debug routines.
33d6df3 Add new link filters.
17faab3 Improve object linking backend.
55f2abf scribo/filter/objects_with_holes.hh: New component filter.
db3dc85 scribo/draw/bounding_boxes.hh: Do not draw box centers anymore.
7681799 Add dedicated routines for AFP's use case.
22cac49 Add new tools in Scribo.
e7ca877 scribo/src/text_in_photo_ppm.cc: Improve output.
750fe21 Cleanup and avoid warnings in Sauvola related files.
c24686e configure.ac: Configure scribo/tests/core.
4026193 Add a new test for object_image.
80b330e primitive/extract/objects.hh: Compute and store mass centers.
0686d02 Fix object links filters.
6db6d75 Cleanup Sauvola's binarization code.
8d114a2 core/object_image.hh: Make use of mln::labeled_image_base.
c7f2301 Improve linking routines.
3395979 Add rbg_to_int_u function.
567ca25 Add an antialiased subsampling.
495dd2d Add hooks for subclasses in labeled_image_base.
27ae9e6 Improve stats computation in Sauvola Multi-scale.
c6851fd Optimize Sauvola's multiscale binarization.
ce6a424 mln/draw/dashed_line.hh: New draw line routine.
706148b Add white gaussian noise function.
-----------------------------------------------------------------------
hooks/post-receive
--
Olena, a generic and efficient image processing platform
1
0
* src/text_in_photo_ppm_fast.cc,
* src/debug/show_links_bottom_aligned.cc,
* src/debug/show_links_top_aligned.cc: Update calls to routines.
* src/preprocessing/trash.cc,
* src/text_in_photo_ppm_fast_2.cc,
* src/preprocessing/split_bg_fg_ms.cc,
* src/preprocessing/denoising.cc: Removed.
* src/preprocessing/denoise.cc,
* src/preprocessing/homogeneous_contrast.ccm,
* src/preprocessing/subsample.cc: New.
* src/preprocessing/Makefile.am: Add new files as target.
* src/preprocessing/split_bg_fg.cc: Fix usage.
* src/text_in_article.cc: Update processing chain.
---
scribo/ChangeLog | 23 +
scribo/src/debug/show_links_bottom_aligned.cc | 24 +-
scribo/src/debug/show_links_top_aligned.cc | 22 +-
scribo/src/preprocessing/Makefile.am | 13 +-
.../denoise.cc} | 31 +-
scribo/src/preprocessing/denoising.cc | 115 -----
scribo/src/preprocessing/homogeneous_contrast.cc | 44 ++
scribo/src/preprocessing/split_bg_fg.cc | 7 +-
scribo/src/preprocessing/split_bg_fg_ms.cc | 109 ----
.../subsample.cc} | 34 +-
scribo/src/text_in_article.cc | 289 +++--------
scribo/src/text_in_photo_ppm_fast.cc | 55 +-
scribo/src/text_in_photo_ppm_fast_2.cc | 541 --------------------
13 files changed, 242 insertions(+), 1065 deletions(-)
copy scribo/src/{filter/objects_with_holes.cc => preprocessing/denoise.cc} (70%)
delete mode 100644 scribo/src/preprocessing/denoising.cc
create mode 100644 scribo/src/preprocessing/homogeneous_contrast.cc
delete mode 100644 scribo/src/preprocessing/split_bg_fg_ms.cc
copy scribo/src/{text/pbm_recognition.cc => preprocessing/subsample.cc} (74%)
delete mode 100644 scribo/src/text_in_photo_ppm_fast_2.cc
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index f5deefc..87985ff 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,5 +1,28 @@
2010-03-11 Guillaume Lazzara <z(a)lrde.epita.fr>
+ Update examples.
+
+ * src/text_in_photo_ppm_fast.cc,
+ * src/debug/show_links_bottom_aligned.cc,
+ * src/debug/show_links_top_aligned.cc: Update calls to routines.
+
+ * src/preprocessing/trash.cc,
+ * src/text_in_photo_ppm_fast_2.cc,
+ * src/preprocessing/split_bg_fg_ms.cc,
+ * src/preprocessing/denoising.cc: Removed.
+
+ * src/preprocessing/denoise.cc,
+ * src/preprocessing/homogeneous_contrast.ccm,
+ * src/preprocessing/subsample.cc: New.
+
+ * src/preprocessing/Makefile.am: Add new files as target.
+
+ * src/preprocessing/split_bg_fg.cc: Fix usage.
+
+ * src/text_in_article.cc: Update processing chain.
+
+2010-03-11 Guillaume Lazzara <z(a)lrde.epita.fr>
+
Improve text line merging algorithm.
* text/merging.hh: Change merge rules and improve integration with
diff --git a/scribo/src/debug/show_links_bottom_aligned.cc b/scribo/src/debug/show_links_bottom_aligned.cc
index 4751308..337032a 100644
--- a/scribo/src/debug/show_links_bottom_aligned.cc
+++ b/scribo/src/debug/show_links_bottom_aligned.cc
@@ -37,7 +37,9 @@
#include <mln/io/pbm/load.hh>
#include <mln/io/ppm/save.hh>
-#include <scribo/primitive/extract/objects.hh>
+#include <scribo/core/component_set.hh>
+
+#include <scribo/primitive/extract/components.hh>
#include <scribo/primitive/link/with_single_right_link_bottom.hh>
#include <scribo/filter/object_links_bottom_aligned.hh>
@@ -49,6 +51,7 @@
const char *args_desc[][2] =
{
{ "input.pbm", "A binary image. True for objects and False for the background." },
+ { "max_dist", "Maximum distance lookup (common value 45)" },
{ "max_alpha", "Max angle between two object bottoms. (common value : 5)" },
{0, 0}
};
@@ -60,37 +63,38 @@ int main(int argc, char* argv[])
using namespace scribo::primitive::internal;
using namespace mln;
- if (argc != 4)
+ if (argc != 5)
return scribo::debug::usage(argv,
"Show valid or invalid links according the horizontal alignment (based on bottom line).",
- "input.pbm max_alpha output.ppm",
+ "input.pbm max_dist max_alpha output.ppm",
args_desc,
"A color image. Valid links are drawn in green, invalid ones in red.");
image2d<bool> input;
io::pbm::load(input, argv[1]);
- // Finding objects.
+ // Finding components.
value::label_16 nbboxes;
typedef image2d<value::label_16> L;
- object_image(L) objects
- = scribo::primitive::extract::objects(input, c8(), nbboxes);
+ component_set<L> components
+ = scribo::primitive::extract::components(input, c8(), nbboxes);
// Finding right links.
object_links<L> right_links
- = primitive::link::with_single_right_link_bottom(objects);
+ = primitive::link::with_single_right_link_bottom(components, atoi(argv[2]));
// Filtering.
object_links<L> filtered_links
- = filter::object_links_bottom_aligned(objects, right_links, atof(argv[2]));
+ = filter::object_links_bottom_aligned(right_links, atof(argv[3]));
// Debug image.
image2d<value::rgb8> decision_image
= scribo::debug::alignment_decision_image(input,
right_links,
filtered_links,
- scribo::debug::bottom);
- io::ppm::save(decision_image, argv[3]);
+ scribo::debug::bottom,
+ atoi(argv[2]));
+ io::ppm::save(decision_image, argv[4]);
}
diff --git a/scribo/src/debug/show_links_top_aligned.cc b/scribo/src/debug/show_links_top_aligned.cc
index 97b4a3b..ff01221 100644
--- a/scribo/src/debug/show_links_top_aligned.cc
+++ b/scribo/src/debug/show_links_top_aligned.cc
@@ -37,7 +37,7 @@
#include <mln/io/pbm/load.hh>
#include <mln/io/ppm/save.hh>
-#include <scribo/primitive/extract/objects.hh>
+#include <scribo/primitive/extract/components.hh>
#include <scribo/primitive/link/with_single_right_link_top.hh>
#include <scribo/filter/object_links_top_aligned.hh>
@@ -49,6 +49,7 @@
const char *args_desc[][2] =
{
{ "input.pbm", "A binary image. True for objects and False for the background." },
+ { "max_dist", "Maximum distance lookup (common value 45)" },
{ "max_alpha", "Max angle between two object tops. (common value : 5)" },
{0, 0}
};
@@ -60,30 +61,30 @@ int main(int argc, char* argv[])
using namespace scribo::primitive::internal;
using namespace mln;
- if (argc != 4)
+ if (argc != 5)
return scribo::debug::usage(argv,
"Show valid or invalid links according the horizontal alignment (based on top line).",
- "input.pbm max_alpha output.ppm",
+ "input.pbm max_dist max_alpha output.ppm",
args_desc,
"A color image. Valid links are drawn in green, invalid ones in red.");
image2d<bool> input;
io::pbm::load(input, argv[1]);
- // Finding objects.
+ // Finding components.
value::label_16 nbboxes;
typedef image2d<value::label_16> L;
- object_image(L) objects
- = scribo::primitive::extract::objects(input, c8(), nbboxes);
+ component_set<L> components
+ = scribo::primitive::extract::components(input, c8(), nbboxes);
// Finding right links.
object_links<L> right_links
- = primitive::link::with_single_right_link_top(objects);
+ = primitive::link::with_single_right_link_top(components, atoi(argv[2]));
// Filtering.
object_links<L> filtered_links
- = filter::object_links_top_aligned(objects, right_links, atof(argv[2]));
+ = filter::object_links_top_aligned(right_links, atof(argv[3]));
// Debug image.
@@ -91,7 +92,8 @@ int main(int argc, char* argv[])
= scribo::debug::alignment_decision_image(input,
right_links,
filtered_links,
- scribo::debug::top);
- io::ppm::save(decision_image, argv[3]);
+ scribo::debug::top,
+ atoi(argv[2]));
+ io::ppm::save(decision_image, argv[4]);
}
diff --git a/scribo/src/preprocessing/Makefile.am b/scribo/src/preprocessing/Makefile.am
index 2422d87..501233a 100644
--- a/scribo/src/preprocessing/Makefile.am
+++ b/scribo/src/preprocessing/Makefile.am
@@ -1,4 +1,5 @@
-# Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE).
+# Copyright (C) 2009, 2010 EPITA Research and Development Laboratory
+# (LRDE).
#
# This file is part of Olena.
#
@@ -20,14 +21,16 @@
include $(top_srcdir)/scribo/scribo.mk
bin_PROGRAMS = \
- denoising \
+ denoise \
+ homogeneous_contrast \
preprocess \
split_bg_fg \
- split_bg_fg_ms \
+ subsample \
unskew
-denoising_SOURCES = denoising.cc
+denoise_SOURCES = denoise.cc
+homogeneous_contrast_SOURCES = homogeneous_contrast.cc
preprocess_SOURCES = preprocess.cc
split_bg_fg_SOURCES = split_bg_fg.cc
-split_bg_fg_ms_SOURCES = split_bg_fg_ms.cc
+subsample_SOURCES = subsample.cc
unskew_SOURCES = unskew.cc
diff --git a/scribo/src/filter/objects_with_holes.cc b/scribo/src/preprocessing/denoise.cc
similarity index 70%
copy from scribo/src/filter/objects_with_holes.cc
copy to scribo/src/preprocessing/denoise.cc
index 0ebf89f..0eb567b 100644
--- a/scribo/src/filter/objects_with_holes.cc
+++ b/scribo/src/preprocessing/denoise.cc
@@ -1,4 +1,5 @@
-// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory
+// (LRDE)
//
// This file is part of Olena.
//
@@ -24,30 +25,31 @@
// executable file might be covered by the GNU General Public License.
#include <mln/core/image/image2d.hh>
-#include <mln/core/alias/neighb2d.hh>
-#include <mln/data/convert.hh>
#include <mln/io/pbm/all.hh>
-#include <mln/value/label_16.hh>
-#include <scribo/filter/objects_with_holes.hh>
#include <scribo/debug/usage.hh>
+#include <scribo/preprocessing/denoise.hh>
+
+
const char *args_desc[][2] =
{
{ "input.pbm", "A binary image. 'True' for objects, 'False'\
for the background." },
- { "min_holes_count", "The minimum holes per objects." },
+ { "fg_min_card", "The minimum neighbor count to be set to true." },
+ { "bg_min_card", "The minimum neighbor count to be set to false." },
{0, 0}
};
+
int main(int argc, char *argv[])
{
using namespace mln;
- if (argc != 4)
+ if (argc != 5)
return scribo::debug::usage(argv,
- "Filter objects with holes",
- "input.pbm min_holes_count output.pbm",
+ "Remove noise.",
+ "input.pbm fg_min_card bg_min_card output.pbm",
args_desc,
"A binary image.");
@@ -57,14 +59,11 @@ int main(int argc, char *argv[])
I input;
io::pbm::load(input, argv[1]);
- value::label_16 nobjects;
- typedef object_image(mln_ch_value_(I,value::label_16)) obj_ima_t;
- obj_ima_t objects
- = scribo::primitive::extract::objects(input, c8(), nobjects);
+ unsigned fg_min_card = atoi(argv[2]);
+ unsigned bg_min_card = atoi(argv[3]);
- obj_ima_t filtered = scribo::filter::objects_with_holes(objects, atoi(argv[2]));
- io::pbm::save(data::convert(bool(), filtered), argv[3]);
+ io::pbm::save(scribo::preprocessing::denoise(input, fg_min_card, bg_min_card),
+ argv[4]);
trace::exiting("main");
-
}
diff --git a/scribo/src/preprocessing/denoising.cc b/scribo/src/preprocessing/denoising.cc
deleted file mode 100644
index 769f75b..0000000
--- a/scribo/src/preprocessing/denoising.cc
+++ /dev/null
@@ -1,115 +0,0 @@
-// 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.
-
-#include <mln/core/image/image2d.hh>
-#include <mln/core/alias/neighb2d.hh>
-#include <mln/data/convert.hh>
-#include <mln/io/pbm/all.hh>
-#include <mln/value/label.hh>
-#include <mln/accu/math/count.hh>
-#include <mln/labeling/relabel.hh>
-#include <mln/labeling/compute.hh>
-#include <mln/labeling/foreground.hh>
-
-#include <scribo/debug/usage.hh>
-
-const char *args_desc[][2] =
-{
- { "input.pbm", "A binary image. 'True' for objects, 'False'\
-for the background." },
- { "nbh", "Select the neighborhood used for checking neighbors. '4' will use C4, '8' will use C8." },
- { "n_nbh", "The minimum neighbor count to be set to true." },
- {0, 0}
-};
-
-int main(int argc, char *argv[])
-{
- using namespace mln;
-
- if (argc != 5)
- return scribo::debug::usage(argv,
- "Remove noise.",
- "input.pbm nbh min_nbh output.pbm",
- args_desc,
- "A binary image.");
-
- trace::entering("main");
-
- typedef image2d<bool> I;
- I input;
- io::pbm::load(input, argv[1]);
-
- typedef value::label<30> V;
-
- neighb2d nbh;
- if (atoi(argv[2]) == 4)
- nbh = c4();
- else if (atoi(argv[2]) == 8)
- nbh = c8();
- else
- {
- std::cout << "Error: nbh must be set to '4' or '8'!" << std::endl;
- return 1;
- }
-
- unsigned min_nbh = atoi(argv[3]);
-
-
- V nlabels;
- image2d<V> lbl = labeling::foreground(input, nbh, nlabels);
- std::cout << nlabels << std::endl;
-
-
- util::array<unsigned> result = labeling::compute(accu::meta::math::count(), lbl, nlabels);
-
- fun::i2v::array<bool> f(static_cast<unsigned>(nlabels) + 1, true);
- for (unsigned i = 1; i <= nlabels; ++i)
- if (result(i) < min_nbh)
- f(i) = false;
-
- labeling::relabel_inplace(lbl, nlabels, f);
- std::cout << nlabels << std::endl;
-
-
-// mln_piter_(I) p(input.domain());
-// mln_niter_(neighb2d) n(nbh, p);
-// unsigned n_nbh;
-// for_all(p)
-// if (input(p))
-// {
-// n_nbh = 0;
-// for_all(n)
-// if (input(n))
-// ++n_nbh;
-
-// if (n_nbh < min_nbh)
-// input(p) = false;
-// }
-
- io::pbm::save(data::convert(bool(), lbl), argv[4]);
-
- trace::exiting("main");
-
-}
diff --git a/scribo/src/preprocessing/homogeneous_contrast.cc b/scribo/src/preprocessing/homogeneous_contrast.cc
new file mode 100644
index 0000000..4930923
--- /dev/null
+++ b/scribo/src/preprocessing/homogeneous_contrast.cc
@@ -0,0 +1,44 @@
+#include <mln/value/int_u8.hh>
+#include <mln/io/pgm/load.hh>
+#include <mln/io/pgm/save.hh>
+
+#include <scribo/preprocessing/homogeneous_contrast.hh>
+#include <scribo/debug/usage.hh>
+
+const char *args_desc[][2] =
+{
+ { "input.pgm", "A gray-level image." },
+ { "h", "The height attribute value for the leveling closing." },
+ {0, 0}
+};
+
+
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.pgm h output.pgm" << std::endl;
+ abort();
+}
+
+
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+ using value::int_u8;
+
+ if (argc != 4)
+ return scribo::debug::usage(argv,
+ "Remove noise.",
+ "input.pgm h output.pgm",
+ args_desc);
+
+
+ image2d<int_u8> input;
+ io::pgm::load(input, argv[1]);
+
+ int h = std::atoi(argv[2]);
+
+ io::pgm::save(scribo::preprocessing::homogeneous_contrast(input, h),
+ argv[3]);
+}
diff --git a/scribo/src/preprocessing/split_bg_fg.cc b/scribo/src/preprocessing/split_bg_fg.cc
index 0d7ba8a..de7bd02 100644
--- a/scribo/src/preprocessing/split_bg_fg.cc
+++ b/scribo/src/preprocessing/split_bg_fg.cc
@@ -35,6 +35,7 @@ const char *args_desc[][2] =
{ "input.pbm", "A color image." },
{ "lambda", "Lambda value. (FIX Description)" },
{ "delta", "Delta value. (FIX Description)" },
+ { "fg.ppm", "The foreground image (1st output)." },
{ "bg.ppm", "The background image (2nd output)." },
{0, 0}
};
@@ -49,7 +50,7 @@ int main(int argc, char *argv[])
if (argc != 6)
return scribo::debug::usage(argv,
"Split background and foreground.",
- "input.pbm lambda delta bg.ppm fg.ppm",
+ "input.pbm lambda delta fg.ppm bg.ppm",
args_desc, "The foreground image.");
typedef image2d<value::rgb8> I;
@@ -60,8 +61,8 @@ int main(int argc, char *argv[])
bg_fg = scribo::preprocessing::split_bg_fg(input,
atoi(argv[2]),
atoi(argv[3]));
- io::ppm::save(bg_fg.first(), argv[4]);
- io::ppm::save(bg_fg.second(), argv[5]);
+ io::ppm::save(bg_fg.first(), argv[5]);
+ io::ppm::save(bg_fg.second(), argv[4]);
mln::trace::exiting("main");
}
diff --git a/scribo/src/preprocessing/split_bg_fg_ms.cc b/scribo/src/preprocessing/split_bg_fg_ms.cc
deleted file mode 100644
index 4c0180a..0000000
--- a/scribo/src/preprocessing/split_bg_fg_ms.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// 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.
-
-#include <mln/core/image/image2d.hh>
-#include <mln/io/ppm/all.hh>
-
-#include <mln/debug/filename.hh>
-
-#include <mln/subsampling/subsampling.hh>
-
-#include <scribo/preprocessing/split_bg_fg.hh>
-#include <scribo/debug/usage.hh>
-
-
-const char *args_desc[][2] =
-{
- { "input.pbm", "A color image." },
- { "lambda", "Lambda value. (FIX Description)" },
- { "delta", "Delta value. (FIX Description)" },
- { "lambda_sub2", "Lambda value. (FIX Description)" },
- { "delta_sub2", "Delta value. (FIX Description)" },
- { "lambda_sub4", "Lambda value. (FIX Description)" },
- { "delta_sub4", "Delta value. (FIX Description)" },
- {0, 0}
-};
-
-
-
-int main(int argc, char *argv[])
-{
- mln::trace::entering("main");
- using namespace mln;
- dpoint2d none(0, 0);
-
- if (argc != 9)
- return scribo::debug::usage(argv,
- "Split background and foreground.",
- "input.pbm lambda delta lambda_sub2 delta_sub2 lambda_sub3 delta_sub3 output_prefix",
- args_desc, "The background and foreground images.");
-
- mln::debug::internal::filename_prefix = argv[8];
-
- typedef image2d<value::rgb8> I;
- I input;
- io::ppm::load(input, argv[1]);
-
- util::couple<I,I>
- bg_fg = scribo::preprocessing::split_bg_fg(input,
- atoi(argv[2]),
- atoi(argv[3]));
-
- io::ppm::save(bg_fg.first(), mln::debug::filename("out_bg.ppm"));
- io::ppm::save(bg_fg.second(), mln::debug::filename("out_fg.ppm"));
-
-
- {
- // 1/2
- std::cout << "1/2" << std::endl;
- I input_sub2x = mln::subsampling::subsampling(input, none, 2);
-
- util::couple<I,I>
- bg_fg = scribo::preprocessing::split_bg_fg(input_sub2x,
- atoi(argv[4]),
- atoi(argv[5]));
-
-
- io::ppm::save(bg_fg.first(), mln::debug::filename("out_bg_sub2x.ppm"));
- io::ppm::save(bg_fg.second(), mln::debug::filename("out_fg_sub2x.ppm"));
- }
-
- {
- // 1/4
- std::cout << "1/4" << std::endl;
- I input_sub4x = mln::subsampling::subsampling(input, none, 4);
-
- util::couple<I,I>
- bg_fg = scribo::preprocessing::split_bg_fg(input_sub4x,
- atoi(argv[6]),
- atoi(argv[7]));
-
-
- io::ppm::save(bg_fg.first(), mln::debug::filename("out_bg_sub4x.ppm"));
- io::ppm::save(bg_fg.second(), mln::debug::filename("out_fg_sub4x.ppm"));
- }
-
- mln::trace::exiting("main");
-}
diff --git a/scribo/src/text/pbm_recognition.cc b/scribo/src/preprocessing/subsample.cc
similarity index 74%
copy from scribo/src/text/pbm_recognition.cc
copy to scribo/src/preprocessing/subsample.cc
index 70ceca1..388d4d9 100644
--- a/scribo/src/text/pbm_recognition.cc
+++ b/scribo/src/preprocessing/subsample.cc
@@ -23,43 +23,43 @@
// exception does not however invalidate any other reasons why the
// executable file might be covered by the GNU General Public License.
-#include <iostream>
-
#include <mln/core/image/image2d.hh>
-#include <mln/io/pbm/load.hh>
+#include <mln/core/alias/neighb2d.hh>
+#include <mln/io/pgm/all.hh>
+
+#include <mln/subsampling/antialiased.hh>
-#include <scribo/text/recognition.hh>
+#include <mln/value/int_u8.hh>
#include <scribo/debug/usage.hh>
const char *args_desc[][2] =
{
- { "input.pbm", "A binary image. 'True' for objects, 'False'\
-for the background." },
+ { "input.pgm", "A gray-scale image." },
+ { "ratio", "Scale ratio." },
{0, 0}
};
-
-int main(int argc, char* argv[])
+int main(int argc, char *argv[])
{
- using namespace scribo;
using namespace mln;
- if (argc != 2)
+ if (argc != 4)
return scribo::debug::usage(argv,
- "Text recognition",
- "input.pbm",
- args_desc,
- "The text is printed on the standard output");
+ "Subsample.",
+ "input.pgm ratio output.pgm",
+ args_desc);
trace::entering("main");
- image2d<bool> input;
- io::pbm::load(input, argv[1]);
+ typedef image2d<value::int_u8> I;
+ I input;
+ io::pgm::load(input, argv[1]);
- scribo::text::recognition(input, "fra");
+ unsigned ratio = atoi(argv[2]);
+ io::pgm::save(mln::subsampling::antialiased(input, ratio), argv[3]);
trace::exiting("main");
}
diff --git a/scribo/src/text_in_article.cc b/scribo/src/text_in_article.cc
index 876eba5..2e6b1a6 100644
--- a/scribo/src/text_in_article.cc
+++ b/scribo/src/text_in_article.cc
@@ -36,64 +36,45 @@
#include <mln/io/ppm/save.hh>
#include <mln/io/dump/save.hh>
-#include <mln/math/min.hh>
-
#include <mln/literal/colors.hh>
#include <mln/value/rgb8.hh>
#include <mln/value/label_16.hh>
-#include <mln/value/int_u16.hh>
-
-#include <mln/draw/box.hh>
-#include <mln/draw/line.hh>
-
-#include <mln/data/convert.hh>
-
-#include <mln/extension/adjust.hh>
-
-#include <mln/accu/stat/median_h.hh>
-
-#include <mln/labeling/colorize.hh>
-#include <mln/labeling/relabel.hh>
-//#include <scribo/draw/bounding_boxes.hh>
-#include <scribo/draw/bounding_box_links.hh>
+#include <scribo/core/line_set.hh>
#include <scribo/primitive/extract/components.hh>
+#include <scribo/primitive/extract/vertical_separators.hh>
+
+#include <scribo/primitive/remove/separators.hh>
#include <scribo/primitive/link/merge_double_link.hh>
#include <scribo/primitive/link/with_single_left_link_dmax_ratio.hh>
#include <scribo/primitive/link/with_single_right_link_dmax_ratio.hh>
-#include <scribo/primitive/group/apply.hh>
-// #include <scribo/primitive/group/from_double_link.hh>
#include <scribo/primitive/group/from_single_link.hh>
#include <scribo/filter/object_links_bbox_h_ratio.hh>
-#include <scribo/filter/objects_small.hh>
-// #include <scribo/filter/objects_thin.hh>
-// #include <scribo/filter/objects_thick.hh>
-
-// #include <scribo/filter/object_groups_small.hh>
-
-// #include <scribo/debug/decision_image.hh>
-// #include <scribo/debug/save_bboxes_image.hh>
-// #include <scribo/debug/save_bboxes_image.hh>
-// #include <scribo/debug/save_linked_bboxes_image.hh>
-
-#include <scribo/core/line_set.hh>
-#include <scribo/io/xml/save_text_lines.hh>
-
#include <scribo/debug/usage.hh>
+#include <scribo/debug/save_bboxes_image.hh>
+#include <scribo/debug/bboxes_enlarged_image.hh>
+#include <scribo/debug/mean_and_base_lines_image.hh>
+#include <scribo/debug/looks_like_a_text_line_image.hh>
#include <scribo/make/debug_filename.hh>
-//#include <scribo/text/recognition.hh>
+#include <scribo/text/recognition.hh>
#include <scribo/text/merging.hh>
+#include <scribo/preprocessing/denoise_fg.hh>
+
+
+// #include <mln/morpho/closing/structural.hh>
+// #include <mln/win/rectangle2d.hh>
+
const char *args_desc[][2] =
{
@@ -128,82 +109,60 @@ int main(int argc, char* argv[])
typedef value::label_16 V;
typedef image2d<V> L;
+
+ // Add whitespace separators.
+// win::rectangle2d win = win::rectangle2d(151, 41);
+// image2d<bool> whitespaces = morpho::closing::structural(input, win);
+// logical::not_inplace(whitespaces);
+
+ // Remove separators
+ std::cout << "Find vertical separators..." << std::endl;
+ image2d<bool> separators = primitive::extract::vertical_separators(input, 81);
+
+ std::cout << "Remove separators..." << std::endl;
+ image2d<bool> input_cleaned = primitive::remove::separators(input,
+ separators);
+
+// whitespaces += separators;
+
+ mln::io::pbm::save(separators, "vseparators.pbm");
+// mln::io::pbm::save(whitespaces, "separators.pbm");
+
+// mln::io::pbm::save(input_cleaned, "input_no_separators.pbm");
+
+ // Denoise
+ std::cout << "Denoise..." << std::endl;
+ input_cleaned = preprocessing::denoise_fg(input_cleaned, c8(), 3);
+
+// mln::io::pbm::save(input_cleaned, "input_denoised.pbm");
+
/// Finding components.
std::cout << "Finding components..." << std::endl;
V ncomponents;
component_set<L>
- components = scribo::primitive::extract::components(input,
- c8(),
+ components = scribo::primitive::extract::components(input_cleaned, c8(),
ncomponents);
- /// First filtering.
- std::cout << "Filtering components..." << std::endl;
- component_set<L> filtered_components
- = scribo::filter::components_small(components, 6);
-
- {
- unsigned none = 0, ignored = 0;
- for_all_comps(i, filtered_components)
- {
- if (filtered_components.info(i).tag() == component::Ignored)
- ++ignored;
- else
- ++none;
- }
- std::cout << "stats - none = " << none << " - ignored = " << ignored << std::endl;
+ /// Set separator components.
+ components.add_separators(separators);
+// components.add_separators(whitespaces);
- }
/// Linking potential objects
std::cout << "Linking objects..." << std::endl;
object_links<L> left_link
- = primitive::link::with_single_left_link_dmax_ratio(filtered_components, 2);
+ = primitive::link::with_single_left_link_dmax_ratio(components, 2);
object_links<L> right_link
- = primitive::link::with_single_right_link_dmax_ratio(filtered_components, 2);
+ = primitive::link::with_single_right_link_dmax_ratio(components, 2);
// Validating left and right links.
object_links<L>
- merged_links = primitive::link::merge_double_link(filtered_components,
- left_link,
- right_link);
-
-// #ifndef NOUT
-// if (argc == 4)
-// {
-// image2d<value::rgb8> output = data::convert(value::rgb8(), input);
-// scribo::draw::bounding_box_links(output,
-// merged_links,
-// literal::green);
-
-// mln::util::array<bool> drawn(static_cast<unsigned>(filtered_components.nelements()) + 1, 0);
-// for_all_comps(i, filtered_components)
-// if (filtered_components(i).tag() != component::Ignored)
-// {
-// if (merged_links[i] == i && ! drawn(i))
-// {
-// mln::draw::box(output, filtered_components(i).bbox(), literal::orange);
-// drawn[i] = true;
-// }
-// else
-// {
-// mln::draw::box(output, filtered_components(i).bbox(), literal::blue);
-// mln::draw::box(output, filtered_components(merged_links[i]).bbox(), literal::blue);
-// drawn[i] = true;
-// drawn[merged_links[i]] = true;
-// }
-// }
+ merged_links = primitive::link::merge_double_link(left_link, right_link);
-// mln::io::ppm::save(output, scribo::make::debug_filename("links.ppm"));
-// }
-// #endif
// Remove links if bboxes have too different sizes.
object_links<L> hratio_filtered_links
- = filter::object_links_bbox_h_ratio(filtered_components,
- merged_links,
- 2.5f);
-
-
+ = filter::object_links_bbox_h_ratio(merged_links, 2.5f);
// #ifndef NOUT
@@ -224,8 +183,7 @@ int main(int argc, char* argv[])
// //
// //######
object_groups<L>
- groups = primitive::group::from_single_link(filtered_components,
- hratio_filtered_links);
+ groups = primitive::group::from_single_link(hratio_filtered_links);
// value::label_16 n_groups;
// mln::fun::i2v::array<value::label_16>
// groups_packed = mln::make::relabelfun(groups,
@@ -235,64 +193,25 @@ int main(int argc, char* argv[])
-// mln::util::array<line_stats_extra> line_stats;
+ // Construct a line set.
line_set<L>
- lines = scribo::make::line_set(hratio_filtered_links, groups);
+ lines = scribo::make::line_set(groups);
+ //===== DEBUG =====
// Bboxes image.
- {
- image2d<value::rgb8> output = data::convert(value::rgb8(), input);
-
- for_all_lines(l, lines)
- if (lines(l).tag() != line::Ignored)
- mln::draw::box(output, lines(l).bbox(), literal::red);
-
- mln::io::ppm::save(output,
- scribo::make::debug_filename("step1_bboxes.ppm"));
- }
+ scribo::debug::save_bboxes_image(input, lines,
+ scribo::make::debug_filename("step1_bboxes.ppm"));
// Bboxes enlarged
- {
- image2d<value::rgb8> output = data::convert(value::rgb8(), input);
-
- for_all_lines(l, lines)
- if (lines(l).tag() != line::Ignored)
- {
- if (text::internal::looks_like_a_text_line(lines(l)))
- {
- box2d
- b = text::internal::enlarge(lines(l).bbox(),
- text::internal::delta_of_line(lines(l)));
- b.crop_wrt(input.domain());
- mln::draw::box(output, b, literal::green);
- }
- else
- mln::draw::box(output, lines(l).bbox(), literal::red);
- }
-
- mln::io::ppm::save(output,
- scribo::make::debug_filename("step1_bboxes_enlarged.ppm"));
- }
+ mln::io::ppm::save(scribo::debug::bboxes_enlarged_image(input, lines),
+ scribo::make::debug_filename("step1_bboxes_enlarged.ppm"));
// Looks like a text line
- {
- image2d<value::rgb8> output = data::convert(value::rgb8(), input);
-
- for_all_lines(l, lines)
- if (lines(l).tag() != line::Ignored)
- {
- if (text::internal::looks_like_a_text_line(lines(l)))
- mln::draw::box(output, lines(l).bbox(), literal::green);
- else
- mln::draw::box(output, lines(l).bbox(), literal::red);
- }
-
- mln::io::ppm::save(output,
- scribo::make::debug_filename("step1_looks_like_a_text_line.ppm"));
- }
+ mln::io::ppm::save(scribo::debug::looks_like_a_text_line_image(input, lines),
+ scribo::make::debug_filename("step1_looks_like_a_text_line.ppm"));
// Bboxes + line infos
@@ -312,7 +231,7 @@ int main(int argc, char* argv[])
<< lines(l).card() << " "
<< lines(l).baseline() << " "
<< lines(l).x_height() << " "
- << lines(l).median() << " "
+ << lines(l).meanline() << " "
<< lines(l).d_height() << " "
<< lines(l).a_height() << " "
<< lines(l).char_space() << " "
@@ -329,88 +248,38 @@ int main(int argc, char* argv[])
}
- std::cout << "Merging lines..." << std::endl;
- lines = scribo::text::merging(input.domain(), lines);
+ // mean and base lines.
+ mln::io::ppm::save(scribo::debug::mean_and_base_lines_image(input, lines),
+ scribo::make::debug_filename("step1_x_height.ppm"));
- // Median and base lines.
- {
- image2d<value::rgb8> output = data::convert(value::rgb8(), input);
+ //===== END OF DEBUG =====
- for_all_lines(l, lines)
- {
- if (lines(l).tag() == line::Pathological)
- mln::draw::box(output, lines(l).bbox(), literal::orange);
- else if (lines(l).tag() != line::Merged)
- {
- if (lines(l).card() > 1)
- {
- point2d
- b_top(lines(l).baseline() - lines(l).median(), lines(l).bbox().pmin().col()),
- e_top(lines(l).baseline() - lines(l).median(), lines(l).bbox().pmax().col()),
- b_bot(lines(l).baseline(), lines(l).bbox().pmin().col()),
- e_bot(lines(l).baseline(), lines(l).bbox().pmax().col());
-
- mln::draw::line(output, b_top, e_top, literal::blue);
- mln::draw::line(output, b_bot, e_bot, literal::cyan);
-
-// std::cout << lines(l) << std::endl;
- }
- mln::draw::box(output, lines(l).bbox(), literal::purple);
- }
- }
- mln::io::ppm::save(output, scribo::make::debug_filename("step2_x_height.ppm"));
- }
-
-
- // Bboxes image.
- {
- image2d<value::rgb8> output = data::convert(value::rgb8(), input);
-
- for_all_lines(l, lines)
- if (lines(l).tag() != line::Merged
- && lines(l).tag() != line::Ignored
- && lines(l).tag() != line::Pathological)
- {
- mln::draw::box(output, lines(l).bbox(), literal::red);
- }
-
- mln::io::ppm::save(output, argv[2]);
- }
-// // line label image.
-// {
-// util::array<V>
-// f(static_cast<unsigned>(filtered_components.nelements()) + 1, 0);
-// for_all_lines(l, lines)
-// if (lines(l).tag() != line::Merged
-// && lines(l).tag() != line::Ignored
-// && lines(l).tag() != line::Pathological)
-// {
-// for_all_elements(c, lines(l).components())
-// f(lines(l).components()[c]) = l;
-// }
-// L line_label = duplicate(filtered_components.labeled_image());
-// V nlbl = filtered_components.nelements();
-// mln::labeling::relabel_inplace(line_label, nlbl, f);
-
-// mln::io::ppm::save(labeling::colorize(value::rgb8(), line_label),
-// scribo::make::debug_filename("labeled_lines.ppm"));
-// mln::io::dump::save(line_label, scribo::make::debug_filename("labeled_lines.dump"));
-// }
+ std::cout << "Merging lines..." << std::endl;
+ lines = scribo::text::merging(lines);
-// scribo::io::xml::save_text_lines(argv[1], lines, "out.xml");
+ //===== DEBUG =====
+ // mean and base lines.
+ mln::io::ppm::save(scribo::debug::mean_and_base_lines_image(input, lines),
+ scribo::make::debug_filename("step2_x_height.ppm"));
+ // Looks like a text line
+ mln::io::ppm::save(scribo::debug::looks_like_a_text_line_image(input, lines),
+ scribo::make::debug_filename("step2_looks_like_a_text_line.ppm"));
+ // Bboxes image.
+ scribo::debug::save_bboxes_image(input, lines, argv[2]);
+ //===== END OF DEBUG =====
-// scribo::text::recognition(lines, "fra", "out.txt");
+ scribo::text::recognition(lines, "fra", "out.txt");
// // Display median character space.
@@ -458,5 +327,5 @@ int main(int argc, char* argv[])
// }
-// trace::exiting("main");
+ trace::exiting("main");
}
diff --git a/scribo/src/text_in_photo_ppm_fast.cc b/scribo/src/text_in_photo_ppm_fast.cc
index e4ddd48..0e35487 100644
--- a/scribo/src/text_in_photo_ppm_fast.cc
+++ b/scribo/src/text_in_photo_ppm_fast.cc
@@ -1,4 +1,5 @@
-// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory
+// (LRDE)
//
// This file is part of Olena.
//
@@ -153,7 +154,7 @@ namespace mln
mask_non_text f(mask);
mln_concrete(I) output = data::transform(input_rgb, f);
- for_all_components(i, components)
+ for_all_comps(i, components)
if (components(i).is_valid())
mln::draw::box(output, components(i).bbox(), literal::red);
@@ -255,9 +256,7 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
timer_.start();
std::cout << "** Using split_bg_fg" << std::endl;
image2d<value::rgb8>
- fg = preprocessing::split_bg_fg(input_rgb,
- lambda,
- 32).second();
+ fg = preprocessing::split_bg_fg(input_rgb, lambda, 32).second();
intensity_ima = data::transform(fg, mln::fun::v2v::rgb_to_int_u<8>());
t_ = timer_;
std::cout << "Foreground extracted. " << t_ << std::endl;
@@ -379,9 +378,7 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
// Validating left and right links.
timer_.restart();
object_links<L>
- merged_links = primitive::link::merge_double_link(filtered_components,
- left_link,
- right_link);
+ merged_links = primitive::link::merge_double_link(left_link, right_link);
t_ = timer_;
std::cout << "Right/Left Validation. " << t_ << std::endl;
@@ -389,8 +386,7 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
// Remove links if bboxes have too different sizes.
timer_.restart();
object_links<L>
- hratio_filtered_links = filter::object_links_bbox_h_ratio(filtered_components,
- merged_links,
+ hratio_filtered_links = filter::object_links_bbox_h_ratio(merged_links,
1.50f);
@@ -413,9 +409,7 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
//Remove links if bboxes overlap too much.
object_links<L> overlap_filtered_links
- = filter::object_links_bbox_overlap(filtered_components,
- hratio_filtered_links,
- 0.80f);
+ = filter::object_links_bbox_overlap(hratio_filtered_links, 0.80f);
@@ -438,16 +432,14 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
timer_.restart();
object_groups<L>
- groups = primitive::group::from_single_link(filtered_components,
- overlap_filtered_links);
+ groups = primitive::group::from_single_link(overlap_filtered_links);
// Apply grouping in a temporary image (for debug purpose).
#ifndef NOUT
component_set<L>
- raw_group_image = primitive::group::apply(filtered_components,
- groups);
+ raw_group_image = primitive::group::apply(groups);
#endif // !NOUT
t_ = timer_;
@@ -505,10 +497,9 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
{
decision_image = data::convert(value::rgb8(), input);
component_set<L>
- grouped_objects = primitive::group::apply(filtered_components,
- filtered_small_groups);
+ grouped_objects = primitive::group::apply(filtered_small_groups);
- for_all_components(i, filtered_components)
+ for_all_comps(i, filtered_components)
if (filtered_components(i).is_valid()
&& filtered_small_groups(i) != 0)
mln::draw::box(decision_image, filtered_components(i).bbox(), literal::green);
@@ -544,10 +535,9 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
decision_image = data::convert(value::rgb8(), input);
component_set<L>
- grouped_components = primitive::group::apply(filtered_components,
- filtered_thin_groups);
+ grouped_components = primitive::group::apply(filtered_thin_groups);
- for_all_components(i, filtered_components)
+ for_all_comps(i, filtered_components)
if (filtered_components(i).is_valid()
&& filtered_thin_groups(i) != 0)
mln::draw::box(decision_image, filtered_components(i).bbox(), literal::green);
@@ -571,8 +561,7 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
// 30);
component_set<L>
- grouped_components = primitive::group::apply(filtered_components,
- filtered_thin_groups);
+ grouped_components = primitive::group::apply(filtered_thin_groups);
t_ = g_timer;
std::cout << "Group applied to object image " << t_ << std::endl;
@@ -588,10 +577,10 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
/// Grouping groups.
- groups = primitive::group::from_single_link(grouped_components, left_link);
+ groups = primitive::group::from_single_link(left_link);
-// component_set<L>
- grouped_components = primitive::group::apply(grouped_components, groups);
+// component_set<L>
+ grouped_components = primitive::group::apply(groups);
t_ = g_timer;
std::cout << "Link and group again " << t_ << std::endl;
@@ -663,9 +652,17 @@ Common usage: ./ppm_text_in_photo input.ppm output.ppm 1 1 1 1 1",
std::cout << "# objects = " << grouped_components.nelements() << std::endl;
+ std::cout << filtered_components << std::endl;
+
+ for_all_comps(c, filtered_components)
+ if (filtered_components(c).is_valid())
+ std::cout << filtered_components(c) << ", ";
+ std::cout << std::endl;
+
+ std::cout << filtered_thin_groups << std::endl;
scribo::line_set<L>
- lines = scribo::line_set<L>(filtered_components, filtered_thin_groups);
+ lines = scribo::make::line_set(filtered_thin_groups);
text::recognition(lines, "fra", "out.txt");
trace::exiting("main");
diff --git a/scribo/src/text_in_photo_ppm_fast_2.cc b/scribo/src/text_in_photo_ppm_fast_2.cc
deleted file mode 100644
index bb24a11..0000000
--- a/scribo/src/text_in_photo_ppm_fast_2.cc
+++ /dev/null
@@ -1,541 +0,0 @@
-// 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.
-
-#include <libgen.h>
-#include <iostream>
-
-#include <mln/core/image/image2d.hh>
-#include <mln/core/image/imorph/tr_image.hh>
-#include <mln/core/alias/neighb2d.hh>
-
-#include <mln/labeling/colorize.hh>
-
-#include <mln/data/stretch.hh>
-
-#include <mln/io/pbm/all.hh>
-#include <mln/io/ppm/save.hh>
-
-#include <mln/math/min.hh>
-
-#include <mln/logical/not.hh>
-
-#include <mln/literal/colors.hh>
-#include <mln/value/rgb8.hh>
-#include <mln/value/label_16.hh>
-
-#include <mln/fun/v2v/rgb_to_int_u.hh>
-
-#include <mln/data/wrap.hh>
-
-#include <mln/draw/box.hh>
-
-#include <mln/geom/translate.hh>
-
-#include <scribo/draw/bounding_boxes.hh>
-
-#include <scribo/binarization/sauvola_ms.hh>
-#include <scribo/binarization/sauvola.hh>
-
-#include <scribo/primitive/extract/objects.hh>
-
-#include <scribo/primitive/link/merge_double_link.hh>
-#include <scribo/primitive/link/with_single_left_link.hh>
-#include <scribo/primitive/link/with_single_right_link.hh>
-
-#include <scribo/primitive/group/apply.hh>
-#include <scribo/primitive/group/from_double_link.hh>
-#include <scribo/primitive/group/from_single_link.hh>
-
-#include <scribo/filter/objects_with_holes.hh>
-#include <scribo/filter/object_links_bbox_h_ratio.hh>
-#include <scribo/filter/object_links_bbox_overlap.hh>
-
-#include <scribo/filter/common/objects_photo.hh>
-
-#include <scribo/filter/object_groups_small.hh>
-#include <scribo/filter/object_groups_v_thickness.hh>
-
-#include <scribo/debug/decision_image.hh>
-#include <scribo/debug/save_bboxes_image.hh>
-#include <scribo/debug/save_linked_bboxes_image.hh>
-
-#include <scribo/debug/usage.hh>
-
-#include <scribo/preprocessing/split_bg_fg.hh>
-
-#include <scribo/make/debug_filename.hh>
-
-#include <mln/util/timer.hh>
-#include <mln/core/var.hh>
-
-
-#include <scribo/src/afp/components.hh>
-#include <scribo/src/afp/link.hh>
-#include <scribo/src/afp/regroup.hh>
-
-const char *args_desc[][2] =
-{
- { "input.ppm", "A color image." },
- { "debug_output_dir", "Directory were debug images will be saved" },
- { "lambda", "Lambda value used for foreground extraction" },
- {0, 0}
-};
-
-
-namespace mln
-{
-
- struct mask_non_text : Function_v2v<mask_non_text>
- {
- typedef value::rgb8 result;
- typedef image2d<bool> I;
-
- mask_non_text(const image2d<bool>& mask)
- : mask_(mask), p_(mask_)
- {
- p_.start();
- }
-
- result operator()(const result& v) const
- {
- bool b = p_.val();
- p_.next();
- if (!b)
- return v / 2;
- else
- return v;
-
- }
-
- I mask_;
- mutable mln_pixter_(I) p_;
- };
-
-
- template <typename I, typename L>
- mln_concrete(I)
- compute_highlight_image(const I& input_rgb,
- const object_image<L>& objects)
- {
- mln_ch_value(I, bool) mask;
- initialize(mask, input_rgb);
- data::fill(mask, false);
-
- for_all_components(i, objects.bboxes())
- data::fill((mask | objects.bbox(i)).rw(), true);
-
- mask_non_text f(mask);
- mln_concrete(I) output = data::transform(input_rgb, f);
-
- for_all_components(i, objects.bboxes())
- mln::draw::box(output, objects.bbox(i), literal::red);
-
- return output;
- }
-
- template <typename I, typename L>
- mln_concrete(I)
- compute_text_image(const I& input_rgb,
- const object_image<L>& grouped_objects)
- {
- const util::array<mln_domain(L)>& bboxes = grouped_objects.bboxes();
-
- unsigned shift = 5;
- float height = 1, width = 0;
- for_all_components(i, bboxes)
- {
- height += bboxes(i).nrows() + shift;
- width = math::max(static_cast<float>(bboxes(i).ncols()), width);
- }
- if (width == 0)
- width = 1;
-
- I output(height, width);
- data::fill(output, literal::black);
-
- algebra::vec<2, float> dv;
- dv[0] = 0;
- dv[1] = 0;
- for_all_ncomponents(i, grouped_objects.nlabels())
- {
- mln_VAR(tmp, duplicate(input_rgb | grouped_objects.bbox(i)));
-
- typedef fun::x2x::translation<mln_site_(I)::dim, float> trans_t;
- trans_t trans(dv - grouped_objects.bbox(i).pmin().to_vec());
-
- mln_domain(I) tr_box(grouped_objects.bbox(i).pmin().to_vec() + trans.t(),
- grouped_objects.bbox(i).pmax().to_vec() + trans.t());
-
- tr_image<mln_domain(I), tmp_t, trans_t> tr_ima(tr_box, tmp, trans);
-
- data::paste(tr_ima, output);
- dv[0] += grouped_objects.bbox(i).nrows() + shift;
- }
-
- return output;
- }
-
-} // end of namespace mln
-
-
-int main(int argc, char* argv[])
-{
- using namespace scribo;
- using namespace mln;
-
- if (argc < 3 || argc > 10)
- return scribo::debug::usage(argv,
- "Find text in a photo.",
- "input.ppm output.ppm [bg/fg enabled] [sauvola_ms enabled] [Bg comp filter enabled] [small group filter enabled] [thin group filter enabled] [debug_output_dir] [lambda]",
- args_desc,
- "A color image where the text is highlighted.");
-
- bool debug = false;
- if (argc > 8)
- {
- scribo::make::internal::debug_filename_prefix = argv[8];
- debug = true;
- }
-
- trace::entering("main");
-
- image2d<value::rgb8> input_rgb;
- io::ppm::load(input_rgb, argv[1]);
-
-
- unsigned lambda;
- if (argc == 6)
- lambda = atoi(argv[9]);
- else
- lambda = 1.2 * (input_rgb.nrows() + input_rgb.ncols());
-
-
- image2d<value::int_u8> intensity_ima;
- util::timer timer_, global_t_;
- float t_;
-
- global_t_.start();
-
- if (argc > 3 && atoi(argv[3]) != 0)
- {
- // Extract foreground
- timer_.start();
- std::cout << "** Using split_bg_fg" << std::endl;
- image2d<value::rgb8>
- fg = preprocessing::split_bg_fg(input_rgb,
- lambda,
- 32).second();
- intensity_ima = data::transform(fg, mln::fun::v2v::rgb_to_int_u<8>());
- t_ = timer_;
- std::cout << "Foreground extracted. " << t_ << std::endl;
- }
- else
- {
- timer_.start();
- std::cout << "** Using data::transform(intensity)" << std::endl;
- intensity_ima = data::transform(input_rgb,
- mln::fun::v2v::rgb_to_int_u<8>());
- t_ = timer_;
- std::cout << "Intensity image " << t_ << std::endl;
- }
-
-
- // Binarize foreground to use it in the processing chain.
- timer_.restart();
- image2d<bool> input;
- unsigned w = std::min(intensity_ima.nrows() / 3, intensity_ima.ncols() / 3);
- if (! w % 2)
- ++w;
- w = std::min(w, 101u);
- if (argc > 4 && atoi(argv[4]) != 0)
- {
- std::cout << "** Using sauvola_ms with w_1 = " << w << std::endl;
- input = binarization::sauvola_ms(intensity_ima, w, 3, 67);
- }
- else
- {
- std::cout << "** Using sauvola with w_1 = " << w << std::endl;
- input = binarization::sauvola(intensity_ima, w);
- }
-
-
-// if (debug)
-// io::pbm::save(input, "input.pbm");
- t_ = timer_;
- std::cout << "Foreground binarized. " << t_ << std::endl;
-
-
-
- typedef image2d<value::label_16> L;
-
- /// Finding objects.
- timer_.restart();
-
- typedef object_image(L) Obj;
- Obj filtered_objects;
-
- {
- util::array<box2d> bboxes;
- util::array<point2d> mass_centers;
-
- value::label_16 nobjects;
- L components = extract_components(input, nobjects, bboxes, mass_centers);
-
- filtered_objects = Obj(components, nobjects, bboxes, mass_centers);
- }
-
- t_ = timer_;
- std::cout << "Object extracted " << t_ << " - " << filtered_objects.nlabels() << " objects" << std::endl;
-
-// if (debug)
-// io::pgm::save(data::stretch(value::int_u8(), filtered_objects), "ref_objects.pgm");
-
-
- /// linking potential objects
- timer_.restart();
- util::couple<object_links<L>, object_links<L> >
- links = primitive::link::left_right(filtered_objects);
-
- object_links<L>& left_link = links.first();
- object_links<L>& right_link = links.second();
-
-// object_links<L> left_link
-// = primitive::link::with_single_left_link(filtered_objects, 30);
-// t_ = timer_;
-// std::cout << "Left Link done " << t_ << std::endl;
-
-// timer_.restart();
-// object_links<L> right_link
-// = primitive::link::with_single_right_link(filtered_objects, 30);
- t_ = timer_;
- std::cout << "Link done " << t_ << std::endl;
-
-
-
-// #ifndef NOUT
-// if (argc > 4)
-// {
-// std::cerr << "BEFORE - nobjects = " << filtered_objects.nlabels() << std::endl;
-// scribo::debug::save_linked_bboxes_image(input,
-// filtered_objects,
-// left_link, right_link,
-// literal::red, literal::cyan,
-// literal::yellow,
-// literal::green,
-// scribo::make::debug_filename("links.ppm"));
-// }
-// #endif
-
-
-
- // Validating left and right links.
- timer_.restart();
- object_links<L>
- merged_links = primitive::link::merge_double_link(filtered_objects,
- left_link,
- right_link);
- t_ = timer_;
- std::cout << "Right/Left Validation. " << t_ << std::endl;
-
-
- // Remove links if bboxes have too different sizes.
- timer_.restart();
- object_links<L>
- hratio_filtered_links = filter::object_links_bbox_h_ratio(filtered_objects,
- merged_links,
- 1.50f);
-
-
-
-
-// #ifndef NOUT
-// if (argc > 4)
-// {
-// image2d<value::rgb8>
-// hratio_decision_image = scribo::debug::decision_image(input,
-// merged_links,
-// hratio_filtered_links);
-// io::ppm::save(hratio_decision_image,
-// scribo::make::debug_filename("hratio_links_decision_image.ppm"));
-// }
-// #endif
-
-
-
-
- //Remove links if bboxes overlap too much.
- object_links<L> overlap_filtered_links
- = filter::object_links_bbox_overlap(filtered_objects,
- hratio_filtered_links,
- 0.80f);
-
-
-
-
-// #ifndef NOUT
-// if (argc > 4)
-// {
-// image2d<value::rgb8> overlap_decision_image
-// = scribo::debug::decision_image(input,
-// hratio_filtered_links,
-// overlap_filtered_links);
-// io::ppm::save(overlap_decision_image,
-// scribo::make::debug_filename("overlap_links_decision_image.ppm"));
-// }
-// #endif
-
-
- t_ = timer_;
- std::cout << "Objects links filtered. " << t_ << std::endl;
-
- timer_.restart();
- object_groups<L>
- groups = primitive::group::from_single_link(filtered_objects,
- overlap_filtered_links);
-
-
-
- /// Apply grouping in a temporary image (for debug purpose).
-// #ifndef NOUT
-// object_image(L)
-// raw_group_image = primitive::group::apply(filtered_objects,
-// filter::object_groups_small(groups, 2));
-// #endif // !NOUT
-
- t_ = timer_;
- std::cout << "Objects grouped. " << t_ << std::endl;
-
-
- util::timer g_timer;
-
- object_image(L)
- grouped_objects = primitive::group::apply(filtered_objects,
- groups);
-
- t_ = g_timer;
- std::cout << "Group applied to object image " << t_ << std::endl;
-
- /// Objects have been grouped. We try to link groups together.
- /// This time a single link is enough since non-wanted objects have
- /// been removed.
- g_timer.restart();
-
- left_link = primitive::link::left(grouped_objects, 30);
-// left_link
-// = primitive::link::with_single_left_link(grouped_objects, 30);
-
-
- /// Grouping groups.
- groups = primitive::group::from_single_link(grouped_objects, left_link);
-
-// object_image(L)
- grouped_objects = primitive::group::apply(grouped_objects, groups);
-
-
-
- std::cout << "Filtering groups..." << std::endl;
-
- timer_.restart();
- // Remove objects part of groups with strictly less than 3 objects.
-
- g_timer.start();
- object_groups<L> filtered_small_groups;
- if (argc > 6 && atoi(argv[6]) != 0)
- {
- std::cout << "** Using group too small" << std::endl;
- filtered_small_groups = filter::object_groups_small(groups, 3);
- }
- else
- filtered_small_groups = groups;
- t_ = g_timer;
- std::cout << "Small groups removed " << t_ << std::endl;
-
-
-
- // Remove objects part of groups having a mean thickness lower than 8.
- g_timer.restart();
- object_groups<L> filtered_thin_groups;
- if (argc > 7 && atoi(argv[7]) != 0)
- {
- std::cout << "** Using group too thin" << std::endl;
- filtered_thin_groups
- = filter::object_groups_v_thickness(filtered_small_groups, 8);
- }
- else
- filtered_thin_groups = filtered_small_groups;
- t_ = g_timer;
- std::cout << "Groups too thin " << t_ << std::endl;
-
-
-
- t_ = g_timer;
- std::cout << "Link and group again " << t_ << std::endl;
-
- timer_.stop();
-
-
- timer_.resume();
-
- g_timer.restart();
- /// Filter grouped objects not having enough background components.
- if (argc > 5 && atoi(argv[5]) != 0)
- {
- std::cout << "** Using objects_with_two_holes" << std::endl;
- grouped_objects = scribo::filter::objects_with_two_holes(grouped_objects, 5);
- t_ = g_timer;
- std::cout << "Objects_with_holes " << t_ << std::endl;
- }
-
- t_ = timer_;
- std::cout << "Objects groups filtered. " << t_ << std::endl;
-
-// object_image(L)
- grouped_objects = primitive::group::apply(grouped_objects, groups);
-
-
- t_ = global_t_;
- std::cout << "*** Result computed " << t_ << std::endl;
-
- timer_.restart();
- std::cout << "Saving result..." << std::endl;
- io::ppm::save(mln::labeling::colorize(value::rgb8(),
- grouped_objects,
- grouped_objects.nlabels()),
- argv[2]);
-
-
- io::ppm::save(compute_highlight_image(input_rgb, grouped_objects),
- scribo::make::debug_filename("orig_with_bboxes.ppm"));
- io::ppm::save(compute_text_image(input_rgb, grouped_objects),
- scribo::make::debug_filename("out_text.ppm"));
-
- t_ = timer_;
- std::cout << "Output saved " << t_ << std::endl;
-
- std::cout << "# objects = " << grouped_objects.nlabels() << std::endl;
-
- trace::exiting("main");
- return grouped_objects.nlabels() != 0;
-}
--
1.5.6.5
1
0
last-svn-commit-64-gfd14f74 Add a new example for finding text in pictures.
by Guillaume Lazzara 11 Mar '10
by Guillaume Lazzara 11 Mar '10
11 Mar '10
* src/Makefile.am: Add new target.
* src/text_in_photo_pbm_fast.cc: New.
---
scribo/ChangeLog | 8 +
scribo/src/Makefile.am | 5 +-
scribo/src/text_in_photo_pbm_fast.cc | 441 ++++++++++++++++++++++++++++++++++
3 files changed, 452 insertions(+), 2 deletions(-)
create mode 100644 scribo/src/text_in_photo_pbm_fast.cc
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index 49df10a..6fce0bc 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,5 +1,13 @@
2010-03-11 Guillaume Lazzara <z(a)lrde.epita.fr>
+ Add a new example for finding text in pictures.
+
+ * src/Makefile.am: Add new target.
+
+ * src/text_in_photo_pbm_fast.cc: New.
+
+2010-03-11 Guillaume Lazzara <z(a)lrde.epita.fr>
+
First draft of the dematerialization wizard.
* demo/shared/icons/Symbols-Delete-icon.png,
diff --git a/scribo/src/Makefile.am b/scribo/src/Makefile.am
index a206e04..60c60f7 100644
--- a/scribo/src/Makefile.am
+++ b/scribo/src/Makefile.am
@@ -35,7 +35,7 @@ bin_PROGRAMS = \
text_in_photo \
text_in_photo_ppm \
text_in_photo_ppm_fast \
- text_in_photo_ppm_fast_2 \
+ text_in_photo_pbm_fast \
text_in_photo_invert
@@ -43,7 +43,7 @@ pbm_lines_in_doc_SOURCES = pbm_lines_in_doc.cc
text_in_photo_SOURCES = text_in_photo.cc
text_in_photo_ppm_SOURCES = text_in_photo_ppm.cc
text_in_photo_ppm_fast_SOURCES = text_in_photo_ppm_fast.cc
-text_in_photo_ppm_fast_2_SOURCES = text_in_photo_ppm_fast_2.cc
+text_in_photo_pbm_fast_SOURCES = text_in_photo_pbm_fast.cc
text_in_photo_invert_SOURCES = text_in_photo_invert.cc
@@ -71,6 +71,7 @@ if HAVE_TIFF
$(TIFF_LDFLAGS) \
-lpthread -lhpdf
+
bin_PROGRAMS += text_in_article
text_in_article_CPPFLAGS = $(AM_CPPFLAGS) \
$(TESSERACT_CPPFLAGS) \
diff --git a/scribo/src/text_in_photo_pbm_fast.cc b/scribo/src/text_in_photo_pbm_fast.cc
new file mode 100644
index 0000000..6b27bbc
--- /dev/null
+++ b/scribo/src/text_in_photo_pbm_fast.cc
@@ -0,0 +1,441 @@
+// 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.
+
+#include <libgen.h>
+#include <iostream>
+
+#include <mln/core/image/image2d.hh>
+#include <mln/core/image/imorph/tr_image.hh>
+#include <mln/core/alias/neighb2d.hh>
+
+#include <mln/labeling/colorize.hh>
+
+#include <mln/data/stretch.hh>
+
+#include <mln/io/pbm/all.hh>
+#include <mln/io/ppm/save.hh>
+
+#include <mln/math/min.hh>
+
+#include <mln/logical/not.hh>
+
+#include <mln/literal/colors.hh>
+#include <mln/value/rgb8.hh>
+#include <mln/value/label_16.hh>
+
+#include <mln/fun/v2v/rgb_to_int_u.hh>
+
+#include <mln/data/wrap.hh>
+
+#include <mln/draw/box.hh>
+
+#include <mln/geom/translate.hh>
+
+#include <scribo/draw/bounding_boxes.hh>
+
+#include <scribo/binarization/sauvola_ms.hh>
+#include <scribo/binarization/sauvola.hh>
+
+#include <scribo/primitive/extract/components.hh>
+
+#include <scribo/primitive/link/merge_double_link.hh>
+#include <scribo/primitive/link/with_single_left_link.hh>
+#include <scribo/primitive/link/with_single_right_link.hh>
+
+#include <scribo/primitive/group/apply.hh>
+#include <scribo/primitive/group/from_double_link.hh>
+#include <scribo/primitive/group/from_single_link.hh>
+
+#include <scribo/filter/objects_with_holes.hh>
+#include <scribo/filter/object_links_bbox_h_ratio.hh>
+#include <scribo/filter/object_links_bbox_overlap.hh>
+
+#include <scribo/filter/common/objects_photo.hh>
+
+#include <scribo/filter/object_groups_small.hh>
+#include <scribo/filter/object_groups_v_thickness.hh>
+
+#include <scribo/debug/decision_image.hh>
+#include <scribo/debug/save_bboxes_image.hh>
+#include <scribo/debug/save_linked_bboxes_image.hh>
+
+#include <scribo/debug/usage.hh>
+
+#include <scribo/preprocessing/split_bg_fg.hh>
+
+#include <scribo/make/debug_filename.hh>
+
+#include <mln/util/timer.hh>
+#include <mln/core/var.hh>
+
+
+#include <scribo/src/afp/components.hh>
+#include <scribo/src/afp/link.hh>
+#include <scribo/src/afp/regroup.hh>
+
+#include <scribo/core/line_set.hh>
+
+const char *args_desc[][2] =
+{
+ { "input.ppm", "A color image." },
+ { "debug_output_dir", "Directory were debug images will be saved" },
+ { "lambda", "Lambda value used for foreground extraction" },
+ {0, 0}
+};
+
+
+namespace mln
+{
+
+ struct mask_non_text : Function_v2v<mask_non_text>
+ {
+ typedef value::rgb8 result;
+ typedef image2d<bool> I;
+
+ mask_non_text(const image2d<bool>& mask)
+ : mask_(mask), p_(mask_)
+ {
+ p_.start();
+ }
+
+ result operator()(const result& v) const
+ {
+ bool b = p_.val();
+ p_.next();
+ if (!b)
+ return v / 2;
+ else
+ return v;
+
+ }
+
+ I mask_;
+ mutable mln_pixter_(I) p_;
+ };
+
+
+ template <typename I, typename L>
+ mln_concrete(I)
+ compute_highlight_image(const I& input_rgb,
+ const scribo::component_set<L>& components)
+ {
+ mln_ch_value(I, bool) mask;
+ initialize(mask, input_rgb);
+ data::fill(mask, false);
+
+ for_all_comps(i, components)
+ if (components(i).is_valid())
+ data::fill((mask | components(i).bbox()).rw(), true);
+
+ mask_non_text f(mask);
+ mln_concrete(I) output = data::transform(input_rgb, f);
+
+ for_all_comps(i, components)
+ if (components(i).is_valid())
+ mln::draw::box(output, components(i).bbox(), literal::red);
+
+ return output;
+ }
+
+ template <typename I, typename L>
+ mln_concrete(I)
+ compute_text_image(const I& input_rgb,
+ const scribo::component_set<L>& grouped_objects)
+ {
+ unsigned shift = 5;
+ float height = 1, width = 0;
+ for_all_comps(i, grouped_objects)
+ if (grouped_objects(i).is_valid())
+ {
+ height += grouped_objects(i).bbox().nrows() + shift;
+ width = math::max(static_cast<float>(grouped_objects(i).bbox().ncols()),
+ width);
+ }
+ if (width == 0)
+ width = 1;
+
+ I output(height, width);
+ data::fill(output, literal::black);
+
+ algebra::vec<2, float> dv;
+ dv[0] = 0;
+ dv[1] = 0;
+ for_all_comps(i, grouped_objects)
+ if (grouped_objects(i).is_valid())
+ {
+ mln_VAR(tmp, duplicate(input_rgb | grouped_objects(i).bbox()));
+
+ typedef fun::x2x::translation<mln_site_(I)::dim, float> trans_t;
+ trans_t trans(dv - grouped_objects(i).bbox().pmin().to_vec());
+
+ mln_domain(I)
+ tr_box(grouped_objects(i).bbox().pmin().to_vec() + trans.t(),
+ grouped_objects(i).bbox().pmax().to_vec() + trans.t());
+
+ tr_image<mln_domain(I), tmp_t, trans_t> tr_ima(tr_box, tmp, trans);
+
+ data::paste(tr_ima, output);
+ dv[0] += grouped_objects(i).bbox().nrows() + shift;
+ }
+
+ return output;
+ }
+
+} // end of namespace mln
+
+
+int main(int argc, char* argv[])
+{
+ using namespace scribo;
+ using namespace mln;
+
+ if (argc < 3 || argc > 8)
+ return scribo::debug::usage(argv,
+ "Find text in a photo.\n\n\
+Common usage: ./pbm_text_in_photo input.pbm output.ppm 1 1 1",
+ "input.pbm output.ppm <Bg comp filter enabled> <small group filter enabled> <thin group filter enabled> [debug_output_dir] [lambda]",
+ args_desc,
+ "A color image where the text is highlighted.");
+
+ std::string out_base_dir;
+ bool debug = false;
+ if (argc > 6)
+ {
+ scribo::make::internal::debug_filename_prefix = argv[6];
+ debug = true;
+ out_base_dir = argv[6];
+ }
+
+ trace::entering("main");
+
+ image2d<bool> input;
+ io::pbm::load(input, argv[1]);
+
+
+ mln::util::timer timer_, global_t_;
+ float t_;
+
+
+
+ typedef image2d<value::label_16> L;
+
+ /// Finding components.
+ timer_.restart();
+
+ typedef component_set<L> Obj;
+ Obj filtered_components;
+
+ {
+ mln::util::array<std::pair<box2d, std::pair<point2d, unsigned> > > attribs;
+
+ value::label_16 ncomponents;
+ L components = extract_components(input, ncomponents, attribs);
+
+ filtered_components = Obj(components, ncomponents, attribs);
+ }
+
+ t_ = timer_;
+ std::cout << "Object extracted " << t_ << " - " << filtered_components.nelements() << " components" << std::endl;
+
+ /// linking potential components
+ timer_.restart();
+ mln::util::couple<object_links<L>, object_links<L> >
+ links = primitive::link::left_right(filtered_components);
+
+ object_links<L>& left_link = links.first();
+ object_links<L>& right_link = links.second();
+
+ t_ = timer_;
+ std::cout << "Link done " << t_ << std::endl;
+
+
+
+ // Validating left and right links.
+ timer_.restart();
+ object_links<L>
+ merged_links = primitive::link::merge_double_link(left_link, right_link);
+ t_ = timer_;
+ std::cout << "Right/Left Validation. " << t_ << std::endl;
+
+
+ // Remove links if bboxes have too different sizes.
+ timer_.restart();
+ object_links<L>
+ hratio_filtered_links = filter::object_links_bbox_h_ratio(merged_links,
+ 1.50f);
+
+ //Remove links if bboxes overlap too much.
+ object_links<L> overlap_filtered_links
+ = filter::object_links_bbox_overlap(hratio_filtered_links, 0.80f);
+
+ t_ = timer_;
+ std::cout << "Components links filtered. " << t_ << std::endl;
+
+ timer_.restart();
+ object_groups<L>
+ groups = primitive::group::from_single_link(overlap_filtered_links);
+
+
+
+// Apply grouping in a temporary image (for debug purpose).
+
+ t_ = timer_;
+ std::cout << "Components grouped. " << t_ << std::endl;
+
+ std::cout << "Filtering groups..." << std::endl;
+
+ mln::util::timer g_timer;
+
+ timer_.restart();
+ // Remove components part of groups with strictly less than 3 components.
+
+ g_timer.start();
+ object_groups<L> filtered_small_groups;
+ if (argc > 4 && atoi(argv[4]) != 0)
+ {
+ std::cout << "** Using group too small" << std::endl;
+ filtered_small_groups = filter::object_groups_small(groups, 3);
+ }
+ else
+ filtered_small_groups = groups;
+ t_ = g_timer;
+ std::cout << "Small groups removed " << t_ << std::endl;
+
+
+
+ // Remove components part of groups having a mean thickness lower than 8.
+ g_timer.restart();
+ object_groups<L> filtered_thin_groups;
+ if (argc > 5 && atoi(argv[5]) != 0)
+ {
+ std::cout << "** Using group too thin" << std::endl;
+ filtered_thin_groups
+ = filter::object_groups_v_thickness(filtered_small_groups, 8);
+ }
+ else
+ filtered_thin_groups = filtered_small_groups;
+ t_ = g_timer;
+ std::cout << "Groups too thin " << t_ << std::endl;
+
+
+
+ /// Apply grouping in the object image.
+ g_timer.restart();
+
+// groups = primitive::group::regroup_left(filtered_components,
+// filtered_thin_groups,
+// 30);
+
+ component_set<L>
+ grouped_components = primitive::group::apply(filtered_thin_groups);
+
+ t_ = g_timer;
+ std::cout << "Group applied to object image " << t_ << std::endl;
+
+ /// Components have been grouped. We try to link groups together.
+ /// This time a single link is enough since non-wanted components have
+ /// been removed.
+ g_timer.restart();
+
+ left_link = primitive::link::left(grouped_components, 30);
+
+
+ /// Grouping groups.
+ groups = primitive::group::from_single_link(left_link);
+
+
+ grouped_components = primitive::group::apply(groups);
+
+ t_ = g_timer;
+ std::cout << "Link and group again " << t_ << std::endl;
+
+ timer_.stop();
+
+ timer_.resume();
+
+ g_timer.restart();
+
+
+ /// Filter grouped objects not having enough background components.
+ if (argc > 3 && atoi(argv[3]) != 0)
+ {
+ std::cout << "** Using objects_with_two_holes" << std::endl;
+ grouped_components = scribo::filter::components_with_two_holes(grouped_components, 5);
+ t_ = g_timer;
+ std::cout << "Objects_with_holes " << t_ << std::endl;
+ }
+
+
+ t_ = timer_;
+ std::cout << "Objects groups filtered. " << t_ << std::endl;
+
+
+ t_ = global_t_;
+ std::cout << "*** Result computed " << t_ << std::endl;
+
+ timer_.restart();
+ std::cout << "Saving result..." << std::endl;
+ io::ppm::save(mln::labeling::colorize(value::rgb8(),
+ grouped_components.labeled_image(),
+ grouped_components.nelements()),
+ argv[2]);
+
+
+ scribo::line_set<L>
+ lines = scribo::line_set<L>(filtered_thin_groups);
+
+
+ // Bboxes + line infos
+ {
+ std::ofstream file(scribo::make::debug_filename("text_bboxes.txt").c_str());
+
+ for_all_lines(l, lines)
+ if (lines(l).tag() != line::Merged
+ && lines(l).tag() != line::Ignored
+ && lines(l).tag() != line::Pathological)
+ {
+ file << lines(l).bbox().pmin().row() << " "
+ << lines(l).bbox().pmin().col() << " "
+ << lines(l).bbox().pmax().row() << " "
+ << lines(l).bbox().pmax().col()
+// << " "
+// << lines(l).card() << " "
+// << lines(l).baseline() << " "
+// << lines(l).x_height() << " "
+// << lines(l).median() << " "
+// << lines(l).d_height() << " "
+// << lines(l).a_height() << " "
+// << lines(l).char_space() << " "
+// << lines(l).char_width()
+ << std::endl;
+ }
+
+ file.close();
+ }
+
+
+ trace::exiting("main");
+ return grouped_components.nelements() != 0;
+}
--
1.5.6.5
1
0
last-svn-commit-19-gc6851fd Optimize Sauvola's multiscale binarization.
by Guillaume Lazzara 11 Mar '10
by Guillaume Lazzara 11 Mar '10
11 Mar '10
* core/init_integral_image.hh: New.
* src/binarization/sauvola_ms.cc: Optimize and make it more
robust.
* canvas/integral_browsing.hh: New canvas to browse and compute
data in an integral image.
* binarization/internal/first_pass_functor.hh: New functor to be
used in the integral browsing.
* binarization/sauvola_threshold.hh: Add new overloads.
* subsampling/integral_single_image.hh: Subsample an image and
compute integral images at the same time.
---
scribo/ChangeLog | 20 +
scribo/binarization/internal/first_pass_functor.hh | 141 +++
scribo/binarization/sauvola_threshold.hh | 318 +++---
scribo/canvas/integral_browsing.hh | 412 ++++++++
scribo/core/init_integral_image.hh | 96 ++
scribo/src/binarization/sauvola_ms.cc | 1072 ++++++++++++++++----
scribo/subsampling/integral_single_image.hh | 403 ++++++++
7 files changed, 2103 insertions(+), 359 deletions(-)
create mode 100644 scribo/binarization/internal/first_pass_functor.hh
create mode 100644 scribo/canvas/integral_browsing.hh
create mode 100644 scribo/core/init_integral_image.hh
create mode 100644 scribo/subsampling/integral_single_image.hh
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index 4fed85c..9eac19c 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,3 +1,23 @@
+2009-12-04 Guillaume Lazzara <z(a)lrde.epita.fr>
+
+ Optimize Sauvola's multiscale binarization.
+
+ * core/init_integral_image.hh: New.
+
+ * src/binarization/sauvola_ms.cc: Optimize and make it more
+ robust.
+
+ * canvas/integral_browsing.hh: New canvas to browse and compute
+ data in an integral image.
+
+ * binarization/internal/first_pass_functor.hh: New functor to be
+ used in the integral browsing.
+
+ * binarization/sauvola_threshold.hh: Add new overloads.
+
+ * subsampling/integral_single_image.hh: Subsample an image and
+ compute integral images at the same time.
+
2009-11-03 Guillaume Lazzara <z(a)lrde.epita.fr>
Add a new example in Scribo.
diff --git a/scribo/binarization/internal/first_pass_functor.hh b/scribo/binarization/internal/first_pass_functor.hh
new file mode 100644
index 0000000..13fc62a
--- /dev/null
+++ b/scribo/binarization/internal/first_pass_functor.hh
@@ -0,0 +1,141 @@
+// 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_BINARIZATION_INTERNAL_FIRST_PASS_FUNCTOR_HH
+# define SCRIBO_BINARIZATION_INTERNAL_FIRST_PASS_FUNCTOR_HH
+
+# include <mln/value/int_u8.hh>
+
+# include <scribo/binarization/sauvola_threshold.hh>
+
+
+// #include <mln/border/adjust.hh>
+
+namespace scribo
+{
+
+ namespace binarization
+ {
+
+ namespace internal
+ {
+
+ using namespace mln;
+
+
+ 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 I>
+ struct first_pass_functor
+ {
+ const I& input;
+ mln_fwd_pixter(const I) pxl;
+ double res;
+ image2d<unsigned> parent;
+ image2d<unsigned> card;
+ image2d<bool> msk;
+ image2d<value::int_u8> t_sub;
+
+ unsigned n_nbhs;
+ util::array<int> dp;
+
+ static const double one_k = 1 - 0.34;
+ static const double k_R = 0.34 / 128.0;
+
+ first_pass_functor(const I& input)
+ : input(input),
+ pxl(input)
+ {
+ res = 0;
+ pxl.start();
+
+ initialize(t_sub, input);
+ initialize(parent, input);
+ initialize(msk, input);
+ extension::fill(msk, false);
+
+ initialize(card, input);
+ data::fill(card, 1);
+
+ dp = negative_offsets_wrt(input, c4());
+ n_nbhs = dp.nelements();
+ }
+
+ void exec(double mean, double stddev)
+ {
+ mln_precondition(pxl.is_valid());
+
+ unsigned p = pxl.offset();
+
+ // Use an inlined and developed version of sauvola's
+ // threshold formula.
+ value::int_u8 t_p = mean * (one_k + k_R * stddev);
+// value::int_u8 t_p = sauvola_threshold_formula(mean, stddev);
+
+ msk.element(p) = input.element(p) < t_p;
+ t_sub.element(p) = t_p;
+ if (! msk.element(p))
+ {
+ pxl.next();
+ return;
+ }
+ parent.element(p) = p;
+ for (unsigned i = 0; i < n_nbhs; ++i)
+ {
+ unsigned n = p + dp[i];
+ if (! msk.element(n))
+ continue;
+ unsigned r = my_find_root(parent, n);
+ if (r != p)
+ {
+ parent.element(r) = p;
+ card.element(p) += card.element(r);
+ }
+ }
+
+ pxl.next(); // next pixel
+ }
+
+ void finalize()
+ {
+ mln_assertion(! pxl.is_valid());
+ }
+ };
+
+
+ } // end of namespace scribo::binarization::internal
+
+ } // end of namespace scribo::binarization
+
+} // end of namespace scribo
+
+#endif // SCRIBO_BINARIZATION_INTERNAL_FIRST_PASS_FUNCTOR_HH
diff --git a/scribo/binarization/sauvola_threshold.hh b/scribo/binarization/sauvola_threshold.hh
index 9a9e439..857de1f 100644
--- a/scribo/binarization/sauvola_threshold.hh
+++ b/scribo/binarization/sauvola_threshold.hh
@@ -44,10 +44,7 @@
# include <mln/pw/all.hh>
# include <mln/core/routine/duplicate.hh>
-
-// Forward declaration.
-namespace mln { template <typename T> class integral_image; }
-
+# include <scribo/core/init_integral_image.hh>
namespace scribo
{
@@ -60,7 +57,7 @@ namespace scribo
/*! \brief Compute a threshold image with Sauvola's algorithm.
\input[in] input An image.
- \input[in] window_size The window size.x
+ \input[in] window_size The window size.
\input[out] simple The sum of all intensities of \p input.
\input[out] squared The sum of all squared intensities of \p
input.
@@ -68,11 +65,11 @@ namespace scribo
\return An image of local thresholds.
*/
- template <typename I, typename T>
+ template <typename I, typename J>
mln_ch_value(I, value::int_u8)
sauvola_threshold(const Image<I>& input, unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared);
+ Image<J>& simple,
+ Image<J>& squared);
/// \overload
@@ -106,36 +103,63 @@ namespace scribo
};
- unsigned long long square_(const value::int_u8& val)
+ /*! \brief compute Sauvola's threshold applying directly the formula.
+
+ \param[in] m_x_y Mean value.
+ \param[in] s_x_y Standard deviation.
+ \param[in] k Control the threshold value in the local
+ window. The higher, the lower the threshold
+ form the local mean m(x, y).
+ \param[in] R Maximum value of the standard deviation (128
+ for grayscale documents).
+
+ \return A threshold.
+ */
+ inline
+ double
+ sauvola_threshold_formula(const double m_x_y, const double s_x_y,
+ const double k, const double R)
{
- unsigned long long v = static_cast<unsigned long long>(val);
- return v * v;
+ return m_x_y * (1.0 + k * ((s_x_y / R) - 1.0));
}
- unsigned long long identity_(const value::int_u8& val)
+
+ /// \overload
+ //
+ inline
+ double
+ sauvola_threshold_formula(double m_x_y, double s_x_y)
{
- return static_cast<unsigned long long>(val);
+ // Badekas et al. said 0.34 was best.
+ const double k = 0.34;
+
+ // 128 is best for grayscale documents.
+ const double R = 128;
+
+ return sauvola_threshold_formula(m_x_y, s_x_y, k, R);
}
- /// \brief Compute a point wise threshold according Sauvola's
- /// binarization.
- ///
- /// \param[in] p A site.
- /// \param[in] simple An integral image of mean values.
- /// \param[in] squared An integral image of squared mean values.
- /// \param[in] win_width Window width.
- /// \param[in] k Control the threshold value in the local
- /// window. The higher, the lower the threshold
- /// form the local mean m(x, y).
- /// \param[in] R Maximum value of the standard deviation (128
- /// for grayscale documents).
-
- template <typename P, typename T>
+ /*! \brief Compute a point wise threshold according Sauvola's
+ binarization.
+
+ \param[in] p A site.
+ \param[in] simple An integral image of mean values.
+ \param[in] squared An integral image of squared mean values.
+ \param[in] win_width Window width.
+ \param[in] k Control the threshold value in the local
+ window. The higher, the lower the threshold
+ form the local mean m(x, y).
+ \param[in] R Maximum value of the standard deviation (128
+ for grayscale documents).
+
+ \return A threshold.
+ */
+ template <typename P, typename J>
double
compute_sauvola_threshold(const P& p,
- const integral_image<T>& simple,
- const integral_image<T>& squared,
+ const J& simple,
+ const J& squared,
int win_width, double k, double R)
{
mln_precondition(simple.nrows() == squared.nrows());
@@ -146,142 +170,116 @@ namespace scribo
int row_min = std::max(0, p.row() - w_2);
int col_min = std::max(0, p.col() - w_2);
- int row_max = std::min(simple.nrows() - 1, p.row() + w_2);
- int col_max = std::min(simple.ncols() - 1, p.col() + w_2);
+
+ int row_max = std::min(static_cast<int>(simple.nrows()) - 1,
+ p.row() + w_2);
+ int col_max = std::min(static_cast<int>(simple.ncols()) - 1,
+ p.col() + w_2);
+
+
+// std::cout << "sauvola threshold : "
+// << simple.domain() << " - "
+// << row_max << " - "
+// << col_max << std::endl;
double wh = (row_max - row_min + 1) * (col_max - col_min + 1);
// Mean.
- double m_x_y_tmp = (simple(row_max, col_max)
- + simple(row_min, col_min)
- - simple(row_max, col_min)
- - simple(row_min, col_max));
+ double m_x_y_tmp = (simple.at_(row_max, col_max)
+ + simple.at_(row_min, col_min)
+ - simple.at_(row_max, col_min)
+ - simple.at_(row_min, col_max));
double m_x_y = m_x_y_tmp / wh;
// Standard deviation.
- double s_x_y_tmp = (squared(row_max, col_max)
- + squared(row_min, col_min)
- - squared(row_max, col_min)
- - squared(row_min, col_max));
+ double s_x_y_tmp = (squared.at_(row_max, col_max)
+ + squared.at_(row_min, col_min)
+ - squared.at_(row_max, col_min)
+ - squared.at_(row_min, col_max));
double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f));
// Thresholding.
- double t_x_y = m_x_y * (1.0 + k * ((s_x_y / R) - 1.0));
+ double t_x_y = sauvola_threshold_formula(m_x_y, s_x_y, k, R);
return t_x_y;
}
- template <typename P, typename T>
+ template <typename P, typename J>
double
- compute_sauvola_threshold(const P& p,
- const integral_image<T>& simple,
- const integral_image<T>& squared,
- int win_width)
+ compute_sauvola_threshold_single_image(const P& p,
+ const J& integral,
+ int win_width)
{
- // Badekas et al. said 0.34 was best.
- const double k = 0.34;
-
- // 128 is best for grayscale documents.
- const double R = 128;
-
- return compute_sauvola_threshold(p, simple, squared, win_width, k, R);
- }
-
-
- } // end of namespace scribo::binarization::internal
-
- } // end of namespace scribo::binarization
+ // Window half width.
+ int w_2 = win_width >> 1;
-} // end of namespace scribo
+ int row_min = std::max(0, p.row() - w_2);
+ int col_min = std::max(0, p.col() - w_2);
+ //FIXME: offset (-4) should be replaced by the number of
+ //padding pixels.
+ int row_max = std::min(static_cast<int>(integral.nrows()) - 1,
+ p.row() + w_2);
+ int col_max = std::min(static_cast<int>(integral.ncols()) - 1,
+ p.col() + w_2);
-namespace mln
-{
- template <typename T>
- class integral_image
- {
- public:
+// std::cout << "sauvola threshold : "
+// << simple.domain() << " - "
+// << row_max << " - "
+// << col_max << std::endl;
- integral_image()
- : img_(0), nrows_(0), ncols_(0)
- {}
-
- template<class F>
- integral_image(const image2d<T>& i, F func)
- : img_(0),
- nrows_(0),
- ncols_(0)
- {
- init_(i, func);
- }
+ double wh = (row_max - row_min + 1) * (col_max - col_min + 1);
- template<class F>
- void init_(const image2d<T>& i, F func)
- {
- nrows_ = i.nrows();
- ncols_ = i.ncols();
+ // Mean.
+ double m_x_y_tmp = (integral.at_(row_max, col_max).first()
+ + integral.at_(row_min, col_min).first()
+ - integral.at_(row_max, col_min).first()
+ - integral.at_(row_min, col_max).first());
- img_ = (unsigned long long**)malloc(sizeof(unsigned long long*) * nrows_);
- for (int n = 0; n < nrows_; ++n)
- img_[n] = (unsigned long long*)malloc(sizeof(unsigned long long) * ncols_);
+ double m_x_y = m_x_y_tmp / wh;
- img_[0][0] = func(i.at_(0, 0));
+ // Standard deviation.
+ double s_x_y_tmp = (integral.at_(row_max, col_max).second()
+ + integral.at_(row_min, col_min).second()
+ - integral.at_(row_max, col_min).second()
+ - integral.at_(row_min, col_max).second());
- for (int row = 1; row < nrows_; ++row)
- img_[row][0] = (*this)(row - 1, 0) + func(i.at_(row, 0));
+ double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f));
- for (int col = 1; col < ncols_; ++col)
- img_[0][col] = (*this)(0, col - 1)
- + func(i.at_(0, col));
+ // Thresholding.
+ double t_x_y = m_x_y * (1.0 + 0.14 * ((s_x_y / 128) - 1.0));
- for (int row = 1; row < nrows_; ++row)
- for (int col = 1; col < ncols_; ++col)
- img_[row][col] = (*this)(row - 1, col)
- + (*this)(row, col - 1)
- - (*this)(row - 1, col - 1)
- + func(i.at_(row, col));
- }
+ return t_x_y;
+ }
- ~integral_image()
- {
- for (int n = 0; n < nrows_; ++n)
- free(img_[n]);
- free(img_);
- }
- bool is_valid() const
- {
- return img_ != 0;
- }
+ template <typename P, typename J>
+ double
+ compute_sauvola_threshold(const P& p,
+ const J& simple,
+ const J& squared,
+ int win_width)
+ {
+ // Badekas et al. said 0.34 was best.
+ const double k = 0.34;
- unsigned long long operator()(int row, int col) const
- {
- return img_[row][col];
- }
+ // 128 is best for grayscale documents.
+ const double R = 128;
- int nrows() const
- {
- return nrows_;
- }
+ return compute_sauvola_threshold(p, simple, squared, win_width, k, R);
+ }
- int ncols() const
- {
- return ncols_;
- }
+ } // end of namespace scribo::binarization::internal
- private:
- unsigned long long** img_;
- int nrows_;
- int ncols_;
- };
+ } // end of namespace scribo::binarization
-} // end of namespace mln
+} // end of namespace scribo
@@ -300,23 +298,25 @@ namespace scribo
namespace generic
{
- template <typename I, typename T>
+ template <typename I, typename J>
inline
mln_concrete(I)
- sauvola_threshold(const I& input, unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ sauvola_threshold(const Image<I>& input_, unsigned window_size,
+ Image<J>& simple_,
+ Image<J>& squared_)
{
trace::entering("scribo::binarization::impl::generic::sauvola_threshold");
- typedef mln_value(I) V;
- typedef mln_site(I) P;
+ const I& input = exact(input_);
+ J& simple = exact(simple_);
+ J& squared = exact(squared_);
- // Compute the sum of all intensities of input
- simple.init_(input, internal::identity_);
+ mln_assertion(input.is_valid());
+ mln_assertion(simple.is_valid());
+ mln_assertion(squared.is_valid());
- // Compute the sum of all squared intensities of input
- squared.init_(input, internal::square_);
+ typedef mln_value(I) V;
+ typedef mln_site(I) P;
// Savaula Algorithm with I.I.
@@ -340,24 +340,24 @@ namespace scribo
- template <typename I, typename T>
+ template <typename I, typename J>
inline
mln_concrete(I)
sauvola_threshold_gl(const I& input, unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ Image<J>& simple,
+ Image<J>& squared)
{
return impl::generic::sauvola_threshold(input, window_size,
simple, squared);
}
- template <typename I, typename T>
+ template <typename I, typename J>
inline
mln_ch_value(I, value::int_u8)
sauvola_threshold_rgb8(const I& input, unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ Image<J>& simple,
+ Image<J>& squared)
{
trace::entering("scribo::binarization::impl::sauvola_threshold_rgb8");
@@ -384,36 +384,36 @@ namespace scribo
namespace internal
{
- template <unsigned n, typename I, typename T>
+ template <unsigned n, typename I, typename J>
inline
mln_ch_value(I, value::int_u<n>)
sauvola_threshold_dispatch(const value::int_u<n>&, const I& input,
unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ J& simple,
+ J& squared)
{
return impl::sauvola_threshold_gl(input, window_size, simple, squared);
}
- template <typename I, typename T>
+ template <typename I, typename J>
inline
mln_ch_value(I, value::int_u8)
sauvola_threshold_dispatch(const value::rgb8&, const I& input,
unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ J& simple,
+ J& squared)
{
return impl::sauvola_threshold_rgb8(input, window_size,
simple, squared);
}
- template <typename I, typename T>
+ template <typename I, typename J>
inline
mln_ch_value(I, value::int_u8)
sauvola_threshold_dispatch(const mln_value(I)&, const I& input,
unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ J& simple,
+ J& squared)
{
// No dispatch for this kind of value type.
mlc_abort(I)::check();
@@ -427,11 +427,11 @@ namespace scribo
- template <typename I, typename T>
+ template <typename I, typename J>
mln_ch_value(I, value::int_u8)
sauvola_threshold(const Image<I>& input, unsigned window_size,
- integral_image<T>& simple,
- integral_image<T>& squared)
+ Image<J>& simple,
+ Image<J>& squared)
{
trace::entering("scribo::binarization::sauvola_threshold");
@@ -441,8 +441,9 @@ namespace scribo
typedef mln_value(I) value_t;
mln_ch_value(I, value::int_u8)
output = internal::sauvola_threshold_dispatch(value_t(), exact(input),
- window_size, simple,
- squared);
+ window_size,
+ exact(simple),
+ exact(squared));
trace::exiting("scribo::text::ppm2pbm");
return output;
@@ -454,7 +455,10 @@ namespace scribo
mln_ch_value(I, value::int_u8)
sauvola_threshold(const Image<I>& input, unsigned window_size)
{
- mln::integral_image<value::int_u8> simple, squared;
+ mln_ch_value(I, double)
+ simple = init_integral_image(input, scribo::internal::identity_),
+ squared = init_integral_image(input, scribo::internal::square_);
+
return sauvola_threshold(input, window_size, simple, squared);
}
diff --git a/scribo/canvas/integral_browsing.hh b/scribo/canvas/integral_browsing.hh
new file mode 100644
index 0000000..814733f
--- /dev/null
+++ b/scribo/canvas/integral_browsing.hh
@@ -0,0 +1,412 @@
+// 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_CANVAS_INTEGRAL_BROWSING_HH
+# define SCRIBO_CANVAS_INTEGRAL_BROWSING_HH
+
+# include <mln/core/image/image2d.hh>
+# include <mln/util/couple.hh>
+
+namespace scribo
+{
+
+ namespace canvas
+ {
+ using namespace mln;
+
+
+ template <typename F>
+ void integral_browsing(const image2d<util::couple<double, double> >& ima,
+ unsigned step,
+ unsigned w, unsigned h,
+ F& functor);
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+ namespace internal
+ {
+
+ inline
+ void compute_stats(// in
+ double sum, double sum_2, unsigned n,
+ // out
+ double& mean, double& stddev)
+ {
+ mean = sum / n;
+ stddev = std::sqrt(sum_2 / n - mean * mean);
+ // unbias version:
+ // stddev = std::sqrt((sum_2 - n * mean * mean) / (n - 1));
+ }
+
+ } // end of namespace scribo::canvas::internal
+
+
+
+
+ template <typename F>
+ void integral_browsing(const image2d<util::couple<double, double> >& ima,
+ unsigned step,
+ unsigned w, unsigned h,
+ F& functor)
+ {
+ typedef util::couple<double, double> V;
+ typedef const V* Ptr;
+ Ptr a_ima, b_ima, c_ima, d_ima;
+
+ const unsigned
+ nrows = ima.nrows(),
+ ncols = ima.ncols(),
+ row_0 = step / 2,
+ col_0 = step / 2;
+
+ const unsigned
+ offset_down = ima.delta_index(dpoint2d(step, 0)),
+ offset_ante = ima.delta_index(dpoint2d(0, -w)),
+ offset_below = ima.delta_index(dpoint2d(+h, 0));
+
+ const unsigned
+ max_row_top = h/2,
+ max_row_mid = nrows - 1 - h/2,
+ max_col_left = w/2,
+ max_col_mid = ncols - 1 - w/2,
+
+ step_2 = step * step,
+
+ h_top = row_0 + h/2 + 1,
+ w_left = col_0 + w/2 + 1;
+
+ unsigned row, col;
+
+ for (col = col_0; col <= max_col_mid; col += step) ;
+ unsigned w_right = ncols - col + w/2;
+
+ Ptr
+ d_tl_start, d_tr_start,
+ b_ml_start = 0, d_ml_start = 0, b_mr_start = 0, d_mr_start = 0,
+ b_bl_start = 0, d_bl_start = 0, b_br_start = 0, d_br = 0;
+
+ double mean, stddev;
+
+
+ // -------------------------------
+ // T (top)
+ // -------------------------------
+
+ const unsigned
+ delta_start_left = step * w_left,
+ delta_start_right = step * w_right,
+ step_w = step * w;
+ unsigned
+ size_tl_start = h_top * w_left,
+ size_tl,
+ delta_size_tl = h_top * step,
+ size_tc = h_top * w,
+ delta_size_tr = h_top * step,
+ size_tr_start = h_top * w_right,
+ size_tr;
+
+ d_tl_start = & ima.at_(row_0 + h/2, col_0 + w/2);
+ d_tr_start = & ima.at_(row_0 + h/2, ncols - 1);
+
+ for (row = row_0; row <= max_row_top; row += step)
+ {
+
+ // TL (top left)
+
+ d_ima = d_tl_start;
+ size_tl = size_tl_start;
+
+ for (col = col_0; col <= max_col_left; col += step)
+ {
+ // D
+ internal::compute_stats(d_ima->first(),
+ d_ima->second(),
+ size_tl,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ d_ima += step;
+ size_tl += delta_size_tl;
+ }
+
+ delta_size_tl += step_2;
+ size_tl_start += delta_start_left;
+ d_tl_start += offset_down;
+
+ // TC (top center)
+
+ c_ima = d_ima + offset_ante;
+
+ for (; col <= max_col_mid; col += step)
+ {
+ // D - C
+ internal::compute_stats(d_ima->first() - c_ima->first(),
+ d_ima->second() - c_ima->second(),
+ size_tc,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ c_ima += step;
+ d_ima += step;
+ }
+
+ size_tc += step_w;
+
+ // TR (top right)
+
+ d_ima = d_tr_start;
+ double
+ d_sum = d_ima->first(),
+ d_sum_2 = d_ima->second();
+ size_tr = size_tr_start;
+
+ for (; col < ncols; col += step)
+ {
+ // D* - C
+ internal::compute_stats(d_sum - c_ima->first(),
+ d_sum_2 - c_ima->second(),
+ size_tr,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ c_ima += step;
+ size_tr -= delta_size_tr;
+ }
+
+ delta_size_tr += step_2;
+ size_tr_start += delta_start_right;
+ d_tr_start += offset_down;
+
+ }
+
+
+
+ // -------------------------------
+ // (M) middle
+ // -------------------------------
+
+
+ const unsigned
+ size_ml_start = h * w_left,
+ h_step = h * step,
+ size_mc = w * h;
+ unsigned
+ size_ml,
+ size_mr_start = h * w_right,
+ size_mr;
+
+ if (row <= max_row_mid)
+ {
+ b_ml_start = & ima.at_(row - h/2 - 1, col_0 + w/2);
+ d_ml_start = b_ml_start + offset_below;
+ b_mr_start = & ima.at_(row - h/2 - 1, ncols - 1);
+ d_mr_start = b_mr_start + offset_below;
+ }
+
+ for (; row <= max_row_mid; row += step)
+ {
+
+ // ML (middle left)
+
+ size_ml = size_ml_start;
+ b_ima = b_ml_start;
+ d_ima = d_ml_start;
+
+ for (col = col_0; col <= max_col_left; col += step)
+ {
+ // D - B
+ internal::compute_stats(d_ima->first() - b_ima->first(),
+ d_ima->second() - b_ima->second(),
+ size_ml,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ b_ima += step;
+ d_ima += step;
+ size_ml += h_step;
+ }
+
+ b_ml_start += offset_down;
+ d_ml_start += offset_down;
+
+ // MC (middle center)
+
+ a_ima = b_ima + offset_ante;
+ c_ima = d_ima + offset_ante;
+
+ for (; col <= max_col_mid; col += step)
+ {
+ // D + A - B - C
+ internal::compute_stats((d_ima->first() - b_ima->first()) + (a_ima->first() - c_ima->first()),
+ (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()),
+ size_mc,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ a_ima += step;
+ b_ima += step;
+ c_ima += step;
+ d_ima += step;
+ }
+
+ // MR (middle right)
+
+ size_mr = size_mr_start;
+ b_ima = b_mr_start;
+ d_ima = d_mr_start;
+ double
+ d_b_sum = d_ima->first() - b_ima->first(),
+ d_b_sum_2 = d_ima->second() - b_ima->second();
+
+ for (; col < ncols; col += step)
+ {
+ // D* + A - B* - C
+ internal::compute_stats(d_b_sum + (a_ima->first() - c_ima->first()),
+ d_b_sum_2 + (a_ima->second() - c_ima->second()),
+ size_mr,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ a_ima += step;
+ c_ima += step;
+ size_mr -= h_step;
+ }
+
+ b_mr_start += offset_down;
+ d_mr_start += offset_down;
+ }
+
+
+
+ // -------------------------------
+ // B (bottom)
+ // -------------------------------
+
+ unsigned
+ size_bl_start = (nrows - row + h/2) * w_left,
+ size_bl,
+ delta_size_bl = (nrows - row + h/2) * step,
+ size_bc = (nrows - row + h/2) * w,
+ size_br_start = (nrows - row + h/2) * w_right,
+ delta_size_br = (nrows - row + h/2) * step,
+ size_br;
+
+ if (row < nrows)
+ {
+ b_bl_start = & ima.at_(row - h/2 - 1, col_0 + w/2);
+ d_bl_start = & ima.at_(nrows - 1, col_0 + w/2);
+ b_br_start = & ima.at_(row - h/2 - 1, ncols - 1);
+ d_br = & ima.at_(nrows - 1, ncols - 1);
+ }
+
+ for (; row < nrows; row += step)
+ {
+
+ // BL (bottom left)
+
+ size_bl = size_bl_start;
+ b_ima = b_bl_start;
+ d_ima = d_bl_start;
+
+ for (col = col_0; col <= max_col_left; col += step)
+ {
+ // D* - B
+ internal::compute_stats(d_ima->first() - b_ima->first(),
+ d_ima->second() - b_ima->second(),
+ size_bl,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ b_ima += step;
+ d_ima += step;
+ size_bl += delta_size_bl;
+ }
+
+ delta_size_bl -= step_2;
+ size_bl_start -= delta_start_left;
+ b_bl_start += offset_down;
+
+ // BC (bottom center)
+
+ a_ima = b_ima + offset_ante;
+ c_ima = d_ima + offset_ante;
+
+ for (; col <= max_col_mid; col += step)
+ {
+ // D* + A - B - C*
+ internal::compute_stats((d_ima->first() - b_ima->first()) + (a_ima->first() - c_ima->first()),
+ (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()),
+ size_bc,
+ mean, stddev);
+// std::cout << (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()) << std::endl;
+
+// std::cout << d_ima->second() << " - " << b_ima->second() << " - "
+// << a_ima->second() << " - " << c_ima->second() << std::endl;
+// std::cout << d_ima->first() << " - " << b_ima->first() << " - "
+// << a_ima->first() << " - " << c_ima->first() << std::endl;
+ functor.exec(mean, stddev);
+ a_ima += step;
+ b_ima += step;
+ c_ima += step;
+ d_ima += step;
+ }
+
+ size_bc -= step_w;
+
+ // BR (bottom right)
+
+ size_br = size_br_start;
+ b_ima = b_br_start;
+ d_ima = d_br;
+ double
+ d_b_sum = d_ima->first() - b_ima->first(),
+ d_b_sum_2 = d_ima->second() - b_ima->second();
+
+ for (; col < ncols; col += step)
+ {
+ // D* + A - B* - C*
+ internal::compute_stats(d_b_sum + (a_ima->first() - c_ima->first()),
+ d_b_sum_2 + (a_ima->second() - c_ima->second()),
+ size_br,
+ mean, stddev);
+ functor.exec(mean, stddev);
+ a_ima += step;
+ c_ima += step;
+ size_br -= delta_size_br;
+ }
+
+ delta_size_br -= step_2;
+ size_br_start -= delta_start_right;
+ b_br_start += offset_down;
+ }
+
+ functor.finalize();
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+ } // end of namespace scribo::canvas
+
+} // end of namespace mln
+
+
+#endif // ! SCRIBO_CANVAS_INTEGRAL_BROWSING_HH
diff --git a/scribo/core/init_integral_image.hh b/scribo/core/init_integral_image.hh
new file mode 100644
index 0000000..ec19070
--- /dev/null
+++ b/scribo/core/init_integral_image.hh
@@ -0,0 +1,96 @@
+// 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_CORE_INIT_INTEGRAL_IMAGE_HH
+# define SCRIBO_CORE_INIT_INTEGRAL_IMAGE_HH
+
+/// \file
+///
+///
+
+# include <mln/core/image/image2d.hh>
+
+namespace scribo
+{
+ using namespace mln;
+
+
+ namespace internal
+ {
+
+ double square_(const double& val)
+ {
+ double v = static_cast<double>(val);
+ return v * v;
+ }
+
+ double identity_(const double& val)
+ {
+ return static_cast<double>(val);
+ }
+
+ } // end of namespace scribo::internal
+
+
+ template <typename I, typename F>
+ mln_ch_value(I,double)
+ init_integral_image(const Image<I>& input_, F& func)
+ {
+ trace::entering("scribo::init_integral_image");
+
+ const I& input = exact(input_);
+ mln_precondition(input.is_valid());
+ mln_precondition(input.domain().pmin() == literal::origin);
+
+ mln_ch_value(I,double) output;
+ initialize(output, input);
+
+ unsigned
+ nrows_ = input.nrows(),
+ ncols_ = input.ncols();
+
+ output.at_(0,0) = func(input.at_(0, 0));
+
+ for (unsigned row = 1; row < nrows_; ++row)
+ output.at_(row,0) = output.at_(row - 1, 0) + func(input.at_(row, 0));
+
+ for (unsigned col = 1; col < ncols_; ++col)
+ output.at_(0,col) = output.at_(0, col - 1)
+ + func(input.at_(0, col));
+
+ for (unsigned row = 1; row < nrows_; ++row)
+ for (unsigned col = 1; col < ncols_; ++col)
+ output.at_(row, col) = output.at_(row - 1, col)
+ + output.at_(row, col - 1)
+ - output.at_(row - 1, col - 1)
+ + func(input.at_(row, col));
+
+ trace::exiting("scribo::init_integral_image");
+ return output;
+ }
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_CORE_INIT_INTEGRAL_IMAGE_HH
diff --git a/scribo/src/binarization/sauvola_ms.cc b/scribo/src/binarization/sauvola_ms.cc
index 747bc11..8a07dcc 100644
--- a/scribo/src/binarization/sauvola_ms.cc
+++ b/scribo/src/binarization/sauvola_ms.cc
@@ -25,6 +25,11 @@
#include <mln/core/alias/neighb2d.hh>
#include <mln/data/stretch.hh>
+#include <mln/data/paste.hh>
+// #include <mln/debug/iota.hh>
+// #include <mln/debug/quiet.hh>
+// #include <mln/debug/println.hh>
+// #include <mln/debug/println_with_border.hh>
#include <mln/debug/filename.hh>
#include <mln/fun/i2v/array.hh>
#include <mln/io/pbm/all.hh>
@@ -32,17 +37,30 @@
#include <mln/io/ppm/all.hh>
#include <mln/literal/colors.hh>
#include <mln/math/sqr.hh>
-#include <mln/subsampling/subsampling.hh>
+#include <mln/math/abs.hh>
+
+#include <mln/subsampling/antialiased.hh>
+
#include <mln/transform/influence_zone_geodesic.hh>
#include <mln/util/timer.hh>
#include <mln/value/int_u16.hh>
#include <mln/value/int_u8.hh>
#include <mln/value/label_16.hh>
#include <mln/value/rgb8.hh>
+#include <mln/border/equalize.hh>
+#include <mln/border/mirror.hh>
+#include <mln/border/adjust.hh>
#include <mln/debug/filename.hh>
+#include <mln/core/box_runend_piter.hh>
+#include <mln/core/box_runstart_piter.hh>
+
+#include <scribo/subsampling/integral_single_image.hh>
+//#include <scribo/subsampling/integral.hh>
+
#include <scribo/core/macros.hh>
#include <scribo/core/object_image.hh>
+
#include <scribo/filter/objects_small.hh>
#include <scribo/filter/objects_large.hh>
@@ -50,155 +68,737 @@
#include <scribo/filter/objects_thick.hh>
#include <scribo/primitive/extract/objects.hh>
+
#include <scribo/binarization/sauvola_threshold.hh>
+#include <scribo/binarization/internal/first_pass_functor.hh>
+
+#include <scribo/canvas/integral_browsing.hh>
+
#include <scribo/debug/usage.hh>
#include <scribo/debug/save_object_diff.hh>
+
namespace mln
{
+ using value::int_u8;
-// FIXME: do not use scribo's filters since they use object images.
-// Object images computes object bounding boxes which are not needed here.
-//
-// template <typename L>
-// mln_concrete(L)
-// filter_small_objects(const Image<L>& objects,
-// const mln_value(L)& nobjects, unsigned min_size)
-// {
-// card = labeling::compute(card_t(), objects, objects.nlabels());
-
-// mln_concrete(L) output;
-// initialize(output, objects);
-// fun::i2v::array<bool> f(nobjects.next(), 0);
-// for (unsigned i = 0; i < card.size(); ++i)
+ 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 I, typename T>
- mln_ch_value(I, bool)
- apply_sauvola(const I& intensity, const T& threshold, unsigned s)
+ image2d<int_u8>
+ compute_t_n_and_e_2(const image2d<int_u8>& sub, image2d<int_u8>& e_2,
+ unsigned lambda_min, unsigned lambda_max,
+ unsigned q, unsigned i, unsigned w,
+ const image2d<util::couple<double,double> >& integral_sum_sum_2)
+ // lambdas: limits of component cardinality at this scale
{
- mln_precondition(intensity.domain() == threshold.domain());
+ typedef image2d<int_u8> I;
+ typedef point2d P;
- mln_ch_value(I, bool) output;
- initialize(output, intensity);
+ util::timer tt;
+ float t_;
- mln_piter(I) p(intensity.domain());
- for_all(p)
- output(p) = (intensity(p) <= threshold(p / s));
+ unsigned ratio = std::pow(q, i - 2); // Ratio in comparison to e_2
+
+
+ tt.restart();
+
+ unsigned w_2 = w / 2 * ratio;
+
+ // 1st pass
+ scribo::binarization::internal::first_pass_functor< image2d<int_u8> >
+ f(sub);
+ scribo::canvas::integral_browsing(integral_sum_sum_2,
+ ratio,
+ w_2, w_2,
+ f);
+
+// debug::println("mask", f.msk);
+// debug::println("parent", f.parent);
+// debug::println("card", f.card);
+
+ t_ = tt;
+ if (mln::debug::quiet)
+ std::cout << "1st pass - " << t_ << std::endl;
+
+ tt.restart();
+
+ {
+ util::array<mln_value_(I) *> ptr(ratio);
+ unsigned nrows = geom::nrows(e_2);
+
+ mln_box_runend_piter_(I) sp(sub.domain()); // Backward.
+ unsigned ncols = sp.run_length();
+ for_all(sp)
+ {
+ unsigned p = &sub(sp) - sub.buffer(); // Offset
+ P site = sp;
+
+ {
+ P tmp = site * ratio;
+
+ // FIXME: to be removed!
+ if (tmp.row() + ratio >= nrows)
+ ptr.resize(nrows - tmp.row());
+
+ ptr(0) = &e_2(tmp);
+ // FIXME: pointers could just be updated with an offset.
+ for (unsigned j = 1; j < ptr.size(); ++j)
+ {
+ tmp[0] += 1;
+ ptr(j) = & e_2(tmp);
+ }
+ }
+
+ for (unsigned j = 0; j < ncols; ++j)
+ {
+ if (f.msk.element(p))
+ {
+
+ mln_site_(I) sq = site * ratio;
+
+ if (f.parent.element(p) == p)
+ {
+ // test over the component cardinality
+ f.msk.element(p) = f.card.element(p) > lambda_min
+ && f.card.element(p) < lambda_max;
+
+ if (f.msk.element(p) && e_2(sq) == 0u)
+ {
+ for (unsigned l = 0; l < ptr.size(); ++l)
+ std::memset(ptr(l), i, ratio * sizeof(mln_value_(I)));
+// debug(sq) = i;
+ }
+
+ }
+ else
+ {
+ // Propagation
+ f.msk.element(p) = f.msk.element(f.parent.element(p));
+
+ if (f.msk.element(p) && e_2(sq) == 0u)
+ {
+ for (unsigned l = 0; l < ptr.size(); ++l)
+ std::memset(ptr(l), i, ratio * sizeof(mln_value_(I)));
+// debug(sq) = i;
+ }
+
+ }
+ }
+
+ for (unsigned l = 0; l < ptr.size(); ++l)
+ ptr(l) -= ratio;
+
+ --site[1];
+ --p;
+ }
+
+ }
- return output;
+ t_ = tt;
+ if (mln::debug::quiet)
+ std::cout << "2nd pass - " << t_ << std::endl;
+
+// io::pgm::save(e_2, mln::debug::filename("e.pgm", i));
+// io::pgm::save(debug, mln::debug::filename("debug.pgm", i));
+// debug::println(msk);
+// io::pbm::save(f.msk, mln::debug::filename("mask.pbm", i));
+// io::pgm::save(data::stretch(int_u8(), card), mln::debug::filename("card.pgm"));
+ } // end of 2nd pass
+
+// io::pgm::save(f.t_sub, mln::debug::filename("t.pgm", i));
+ return f.t_sub;
}
- template <typename I>
- mln_concrete(I)
- enlarge(const Image<I>& input_, unsigned ratio)
+
+ template <typename I, typename J, typename K>
+ mln_ch_value(I, bool)
+ binarize_generic(const I& in, const J& e2, const util::array<K>& t_ima,
+ unsigned s)
{
- const I& input = exact(input_);
- mln_precondition(input.is_valid());
+ mln_ch_value(I,bool) out;
+ initialize(out, in);
- mln_domain(I) bbox(input.domain().pmin() * ratio,
- input.domain().pmax() * ratio
- + (ratio - 1) * mln::down_right);
+ typedef const mln_value(K)* ptr_type;
- mln_concrete(I) output(bbox);
+ ptr_type ptr_t[5];
+ ptr_t[2] = & t_ima[2].at_(0, 0);
+ ptr_t[3] = & t_ima[3].at_(0, 0);
+ ptr_t[4] = & t_ima[4].at_(0, 0);
- mln_site(I) first_p = input.domain().pmin();
- for (def::coord j = geom::min_col(input);
- j <= geom::max_col(input); ++j)
- for (def::coord i = geom::min_row(input);
- i <= geom::max_row(input); ++i)
- {
- point2d p1(i, j);
- point2d p2(first_p[0] + i * ratio, first_p[1] + j * ratio);
- output(p2) = input(p1);
- output(p2 + mln::right) = input(p1);
- output(p2 + mln::down_right) = input(p1);
- output(p2 + mln::down) = input(p1);
+ const mln_value(J)* ptr_e2 = & e2.at_(0, 0);
+ const mln_value(I)* ptr__in = & in.at_(0, 0);
+ bool* ptr__out = & out.at_(0, 0);
+
+
+ // Since we iterate from a smaller image in the largest ones and
+ // image at scale 1 does not always have a size which can be
+ // divided by (4*s), some sites in the border may not be processed
+ // and we must skip them.
+ unsigned more_offset = in.border() - ((4 * s) - in.ncols() % (4 * s));
+
+ if (more_offset == (4 * s))
+ more_offset = 0; // No offset needed.
+
+ const int
+ nrows4 = t_ima[4].nrows(), ncols4 = t_ima[4].ncols(),
+
+
+ delta1 = in.delta_index(dpoint2d(+1, -(s - 1))),
+ delta1b = in.delta_index(dpoint2d(+1, -(s + s - 1))),
+ delta1c = in.delta_index(dpoint2d(-(s + s - 1), +1)),
+ delta1d = in.delta_index(dpoint2d(+1, -(s * 4 - 1))),
+ delta1e = in.delta_index(dpoint2d(-(s * 4 - 1), +1)),
+ delta1f = in.delta_index(dpoint2d(-(s - 1), +1)),
+
+ delta2 = t_ima[2].delta_index(dpoint2d(+1, -1)),
+ delta2b = t_ima[2].delta_index(dpoint2d(+1, -3)),
+ delta2c = t_ima[2].delta_index(dpoint2d(-3, +1)),
+
+ delta3 = t_ima[3].delta_index(dpoint2d(+1, -1)),
+
+ eor1 = in.delta_index(dpoint2d(+4 * s, - in.ncols() - in.border())) + more_offset,
+ eor2 = t_ima[2].delta_index(dpoint2d(+4,- t_ima[2].ncols())),
+ eor3 = t_ima[3].delta_index(dpoint2d(+2,- t_ima[3].ncols())),
+ eor4 = t_ima[4].delta_index(dpoint2d(+1,- t_ima[4].ncols()));
+
+ mln_value(J) threshold;
+ for (int row4 = 0; row4 < nrows4; ++row4)
+ {
+ for (int col4 = 0; col4 < ncols4; ++col4)
+ {
+ // top left 1
+ {
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1b; ptr__in += delta1b;
+ }
+
+ ptr_t[2] += delta2; ptr_e2 += delta2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1c; ptr__in += delta1c;
+ }
+
+ ptr_t[2] -= delta2; ptr_e2 -= delta2;
+ }
+
+ // top right 1
+ ptr_t[3] += 1;
+ {
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1b; ptr__in += delta1b;
+ }
+
+ ptr_t[2] += delta2; ptr_e2 += delta2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1d; ptr__in += delta1d;
+ }
+
+ ptr_t[2] += delta2b; ptr_e2 += delta2b;
+ }
+
+ // bot left 1
+ ptr_t[3] += delta3;
+ {
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1b; ptr__in += delta1b;
+ }
+
+ ptr_t[2] += delta2; ptr_e2 += delta2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1c; ptr__in += delta1c;
+ }
+
+ ptr_t[2] -= delta2; ptr_e2 -= delta2;
+ }
+
+ // bot right 1
+ ptr_t[3] += 1;
+ {
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1b; ptr__in += delta1b;
+ }
+
+ ptr_t[2] += delta2; ptr_e2 += delta2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1f; ptr__in += delta1f;
+ }
+
+ ++ptr_t[2]; ++ptr_e2;
+ threshold = *ptr_t[*ptr_e2];
+ {
+ for (unsigned i = 1; i < s; ++i)
+ {
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1; ptr__in += delta1;
+ }
+
+ for (unsigned j = 1; j < s; ++j)
+ {
+ *ptr__out = *ptr__in < threshold;
+ ++ptr__out; ++ptr__in;
+ }
+ *ptr__out = *ptr__in < threshold;
+ ptr__out += delta1e; ptr__in += delta1e;
+ }
+ }
+
+ // bot right -> next top left
+ ptr_t[2] += delta2c; ptr_e2 += delta2c;
+ ptr_t[3] = ptr_t[3] - delta3;
+ ptr_t[4] += 1;
}
- return output;
+ // eof -> next bof
+ ptr__out += eor1; ptr__in += eor1;
+ ptr_t[2] += eor2; ptr_e2 += eor2;
+ ptr_t[3] += eor3;
+ ptr_t[4] += eor4;
+ }
+
+
+// mln::debug::println(out);
+
+ return out;
+ }
+
+
+
+
+ unsigned sub(unsigned nbr, unsigned down_scaling)
+ {
+ return (nbr + down_scaling - 1) / down_scaling;
}
template <typename I>
- object_image(mln_ch_value(I, value::label_16))
- get_objects(const Image<I>& input_,
- unsigned min_size, unsigned max_size,
- unsigned ratio, unsigned w, unsigned scale)
+ util::array<util::couple<mln_domain(I), unsigned> >
+ compute_sub_domains(const I& ima, unsigned n_scales, unsigned s)
{
- const I& input = exact(input_);
+ util::array<util::couple<unsigned, unsigned> > n(n_scales + 2);
- mln_precondition(input.is_valid());
+ n(1) = make::couple(ima.nrows(), ima.ncols());
+ n(2) = make::couple(sub(n(1).first(), s),
+ sub(n(1).second(), s));
+ for (unsigned i = 3; i <= n_scales + 1; ++i)
+ n(i) = make::couple(sub(n(i - 1).first(), 2),
+ sub(n(i - 1).second(), 2));
- typedef mln_ch_value(I, value::label_16) L;
- dpoint2d none(0, 0);
- I input_sub = mln::subsampling::subsampling(input, none, ratio);
+ util::array<util::couple<mln_domain(I), unsigned> > out(n.size());
+ out(0) = make::couple(make::box2d(1,1), 1u);
+ out(1) = make::couple(make::box2d(ima.nrows(), ima.ncols()), 2u);
+ out(n_scales + 1) = make::couple(make::box2d(n(n_scales + 1).first(),
+ n(n_scales + 1).second()),
+ 1u);
- image2d<value::int_u8>
- t = scribo::binarization::sauvola_threshold(input_sub, w);
+ for (unsigned i = n_scales; i > 1; --i)
+ out(i) = make::couple(make::box2d(2 * out(i + 1).first().nrows(),
+ 2 * out(i + 1).first().ncols()),
+ 2 * out(i + 1).second());
- image2d<bool> b = apply_sauvola(input_sub, t, 1);
+ out(1).second() = std::max(out(2).first().ncols() * s - ima.ncols(),
+ out(2).first().nrows() * s - ima.nrows());
- value::label_16 nb;
- object_image(L) lbl = scribo::primitive::extract::objects(b, c8(), nb);
- object_image(L) lbl_raw(lbl);
+// out(1).second() = std::max(ima.ncols() % (4 * s),
+// ima.nrows() % (4 * s));
- if (min_size > 0)
- lbl = scribo::filter::objects_small(lbl, min_size);
- if (max_size > 0)
- lbl = scribo::filter::objects_large(lbl, max_size);
- scribo::debug::save_object_diff(lbl_raw, lbl,
- mln::debug::filename("filter_diff.ppm", scale + 2));
+// out(2).first().ncols() * s - ima.ncols(),
+// out(2).first().nrows() * s - ima.nrows() );
- return lbl;
+ return out;
}
+
bool
check_args(int argc, char * argv[])
{
- if (argc < 7)
+ if (argc < 5 || argc > 7)
return false;
- int nb_scale = atoi(argv[3]);
- int s = atoi(argv[4]);
- int q = atoi(argv[5]);
+// int nb_scale = atoi(argv[3]);
+ int s = atoi(argv[3]);
+// int q = atoi(argv[5]);
- if (q < 2)
- {
- std::cout << "q must be greater than 2." << std::endl;
- return false;
- }
- if (s < 1 || s < q)
+// if (q < 2)
+// {
+// std::cout << "q must be greater than 2." << std::endl;
+// return false;
+// }
+ if (s < 1 || s > 3)// || s < q)
{
- std::cout << "s must be greater or equal to 1 and greater than q."
+ std::cout << "s must be set to 2 or 3."
<< std::endl;
return false;
}
- if (nb_scale < 1)
- {
- std::cout << "Not enough scales." << std::endl;
- return false;
- }
- if ((argc - 7) != (nb_scale - 1))
- {
- std::cout << "Not enough area threshold."
- << "There must be nb_scale - 1 thresholds."
- << std::endl;
- return false;
- }
+// if (nb_scale < 1)
+// {
+// std::cout << "Not enough scales." << std::endl;
+// return false;
+// }
+
+// if ((argc - 7) != (nb_scale - 1))
+// {
+// std::cout << "Not enough area threshold."
+// << "There must be nb_scale - 1 thresholds."
+// << std::endl;
+// return false;
+// }
return true;
}
+ void data_rand(image2d<unsigned>& e2)
+ {
+ unsigned v = 2;
+ mln_piter_(box2d) p(e2.domain());
+ for_all(p)
+ {
+ e2(p) = v++;
+ if (v == 5) v = 2;
+ }
+ }
+
+
} // end of namespace mln;
@@ -207,11 +807,10 @@ namespace mln
const char *args_desc[][2] =
{
{ "input.pgm", "A graylevel image." },
- { "w", "Window size." },
- { "nb_scale", "Number of scales (Common value: 3)." },
+ { "w", "Window size. (Common value: 51)" },
{ "s", "First subsampling ratio (Common value: 2)." },
- { "q", "Next subsampling ratio (Common value: 2)." },
- { "<area threshold> (Common values: 200 800)", "Area threshold" },
+ { "min_area", "Minimum object area (relative to scale 2) (Common value: 200)" },
+ { "debug", "Display debug/bench data if set to 1" },
{0, 0}
};
@@ -229,11 +828,10 @@ int main(int argc, char *argv[])
typedef image2d<label_16> L;
-
if (!check_args(argc, argv))
return scribo::debug::usage(argv,
- "Binarization of a color image based on Sauvola's algorithm.",
- "input.pgm w nb_scale s q <area thresholds>",
+ "Multi-Scale Binarization of a color image based on Sauvola's algorithm.",
+ "input.pgm w s area_thresholds output.pbm [debug]",
args_desc, "A binary image.");
trace::entering("main");
@@ -244,163 +842,233 @@ int main(int argc, char *argv[])
unsigned w = atoi(argv[2]);
// First subsampling scale.
- unsigned s = atoi(argv[4]);
+ unsigned s = atoi(argv[3]);
// Number of subscales.
- unsigned nb_subscale = atoi(argv[3]);
+ unsigned nb_subscale = 3;//atoi(argv[3]);
// Subscale step.
- unsigned q = atoi(argv[5]);
+ unsigned q = 2;//atoi(argv[5]);
+
+ mln::debug::quiet = true;
- std::cout << "Running Sauvola_ms with w = " << w
- << ", s = " << s
- << ", nb_subscale = " << nb_subscale
- << ", q = " << q
- << std::endl;
+ if (argc == 7)
+ mln::debug::quiet = ! atoi(argv[6]);
+
+
+ if (mln::debug::quiet)
+ std::cout << "Running Sauvola_ms with w = " << w
+ << ", s = " << s
+ << ", nb_subscale = " << nb_subscale
+ << ", q = " << q
+ << std::endl;
typedef image2d<value::int_u8> I;
dpoint2d none(0, 0);
- mln::util::timer timer_;
+ mln::util::timer
+ timer_,
+ sauvola_timer_;;
+
+ // Tmp variable used for timer results;
+ float t_;
- timer_.start();
I input_full;
io::pgm::load(input_full, argv[1]);
- std::cout << "Image loaded - " << timer_ << std::endl;
- timer_.restart();
+ {
+ unsigned max_dim = math::max(input_full.ncols() / 2,
+ input_full.nrows() / 2);
+ if (w > max_dim)
+ {
+ std::cout << "------------------" << std::endl;
+ std::cout << "The window is too large! Image size is only "
+ << input_full.nrows() << "x" << input_full.ncols()
+ << std::endl
+ << "Window size must not exceed " << max_dim
+ << std::endl;
+ return 1;
+ }
+ }
- util::array<object_image(L)> lbls;
+// I input_full(9,9);
+// mln::debug::iota(input_full);
+ sauvola_timer_.start();
+ unsigned lambda_min = atoi(argv[4]);
+ unsigned lambda_max = lambda_min * q; // * atoi(argv[7])
- // First subsampling 1/s
-// std::cout << "1/" << s << std::endl;
- timer_.start();
- I input = mln::subsampling::subsampling(input_full, none, s);
+ util::array<I> t_ima;
+
+ // Make sure t_ima indexes start from 2.
+ {
+ I dummy(1,1);
+ for (unsigned i = 0; i < nb_subscale + 2; ++i)
+ t_ima.append(dummy);
+ }
- integral_image<int_u8> simple, squared;
- image2d<int_u8>
- t_1 = scribo::binarization::sauvola_threshold(input, w, simple, squared);
- label_16 nb1;
- image2d<bool> b_1 = apply_sauvola(input, t_1, 1);
+ image2d<int_u8> e_2;
+ util::array<I> sub_ima;
+
+ // Make sure sub_ima indexes start from 2.
{
- lbls.append(primitive::extract::objects(b_1, c8(), nb1));
- object_image(L) lbl_raw(lbls[0]);
- lbls[0] = filter::objects_large(lbls[0], atoi(argv[6]));
- scribo::debug::save_object_diff(lbl_raw, lbls[0],
- mln::debug::filename("filter_diff.ppm", 2));
+ I dummy(1,1);
+ sub_ima.append(dummy);
+ sub_ima.append(dummy);
}
- std::cout << "Scale 2 - 1/" << s << " Done - " << timer_ << std::endl;
+ timer_.restart();
+ util::array<util::couple<box2d, unsigned> >
+ sub_domains = compute_sub_domains(input_full, nb_subscale, s);
+ if (mln::debug::quiet)
+ std::cout << "adjusting input border to " << sub_domains(1).second()
+ << std::endl;
- // Additional subscales.
- for (unsigned i = 1; i < nb_subscale; ++i)
- {
- unsigned ratio = std::pow(q, i);
-// std::cout << "Scale " << 2 + i << " - 1/" << s * ratio << std::endl;
- timer_.restart();
- unsigned
- min_size = atoi(argv[5 + i]) / ratio,
- max_size;
- if ((3 + i) == static_cast<unsigned>(argc))
- max_size = 0; // Last subscale, so not max size limit.
- else
- max_size = atoi(argv[6 + i]) / ratio;
-
- lbls.append(get_objects(input, min_size, max_size, ratio, w, i));
- std::cout << "Scale " << 2 + i
- << " - 1/" << s * ratio
- << " Done - " << timer_ << std::endl;
- }
+ border::adjust(input_full, sub_domains(1).second());
+ border::mirror(input_full);
- std::cout << "--------" << std::endl;
+// mln::debug::println_with_border(input_full);
- // Constructing "scale image".
- image2d<int_u8> e;
- initialize(e, input);
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "sub domains computed and adjust input border size - "
+ << t_ << std::endl;
- data::fill(e, 0);
+ // Resize input and compute integral images.
+ timer_.restart();
+ image2d<util::couple<double,double> > integral_sum_sum_2;
- typedef object_image(L) obj_t;
+ if (mln::debug::quiet)
+ std::cout << "sub_domain(2).domain() == " << sub_domains(2).first() << std::endl;
- for (int i = nb_subscale - 1; i >= 0; --i)
- {
- unsigned ratio = std::pow(q, i);
+ sub_ima.append(scribo::subsampling::integral(input_full, s,
+ integral_sum_sum_2,
+ sub_domains[2].first(),
+ sub_domains[2].second()));
- std::cout << "Scale " << 2 + i << " - 1/" << s * ratio << " merged" << std::endl;
+// mln::debug::println(integral_sum_sum_2);
- mln_piter_(obj_t) p(lbls[i].domain());
- for_all(p)
- if (lbls[i](p) != 0 && e(p * ratio) == 0)
- {
- box2d b(p * ratio, p * ratio + mln::down_right * (ratio - 1));
- data::fill((e | b).rw(), i + 1);
- }
- }
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "subsampling 1 -> 2 And integral images - " << t_
+ << " - nsites = "
+ << input_full.domain().nsites() << " -> "
+ << sub_ima[2].domain().nsites() << " - "
+ << input_full.domain() << " -> "
+ << sub_ima[2].domain() << std::endl;
- std::cout << "--------" << std::endl;
- /// Saving "scale image".
+ for (unsigned i = 3; i <= nb_subscale + 1; ++i)
{
- mln::fun::i2v::array<value::int_u8> f(nb_subscale + 1, 0);
- for (unsigned i = 1; i <= nb_subscale; ++i)
- f(i) = 255 / nb_subscale * i;
-
- io::pgm::save(e, "e_raw.pgm");
- io::pgm::save(data::transform(e, f), "e.pgm");
+ timer_.restart();
+ sub_ima.append(mln::subsampling::antialiased(sub_ima[i - 1], q, none,
+ sub_domains[i].first(),
+ sub_domains[i].second()));
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "subsampling " << (i - 1) << " -> " << i
+ << " - " << t_
+ << " - nsites = "
+ << sub_ima[i].domain().nsites() << " - "
+ << sub_ima[i].domain()
+ << std::endl;
}
- /// Saving influence zone scale image.
- image2d<int_u8>
- e_ext = transform::influence_zone_geodesic(e, c8(), mln_max(unsigned));
- io::pgm::save(e_ext, "e_ext.pgm");
+ initialize(e_2, sub_ima[2]);
+ data::fill(e_2, 0u);
+ // Compute threshold image.
- /// Creating window size image
- std::cout << "Creating window size image..." << std::endl;
- mln::fun::i2v::array<value::int_u16> f(nb_subscale, 0);
- for (unsigned i = 0; i < nb_subscale; ++i)
+ // Highest scale -> no maximum component size.
{
- unsigned ratio = std::pow(q, i);
- f(i) = ratio * w;
+ timer_.restart();
+ int i = sub_ima.size() - 1;
+ unsigned ratio = std::pow(q, i - 2); // Ratio compared to e_2
+ t_ima[i] = compute_t_n_and_e_2(sub_ima[i], e_2,
+ lambda_min / ratio,
+ mln_max(unsigned),
+ q, i, w, integral_sum_sum_2);
+
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "Scale " << i
+ << " - 1/" << s * ratio
+ << " compute t_n and update e - " << t_ << std::endl;
}
- image2d<int_u16> wsize = data::transform(e_ext, f);
- io::pgm::save(data::stretch(int_u8(), wsize), "wsize.pgm");
+ // Other scales -> maximum and minimum component size.
+ {
+ for (int i = sub_ima.size() - 2; i > 2; --i)
+ {
+ timer_.restart();
+ unsigned ratio = std::pow(q, i - 2); // Ratio compared to e_2
+ t_ima[i] = compute_t_n_and_e_2(sub_ima[i], e_2,
+ lambda_min / ratio,
+ lambda_max / ratio,
+ q, i, w, integral_sum_sum_2);
+
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "Scale " << i
+ << " - 1/" << s * ratio
+ << " compute t_n and update e - " << t_ << std::endl;
+ }
+ }
+ // Lowest scale -> no minimum component size.
+ {
+ timer_.restart();
+ t_ima[2] = compute_t_n_and_e_2(sub_ima[2], e_2, 0, lambda_max,
+ 1, 2, w, integral_sum_sum_2);
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "Scale " << 2
+ << " - 1/" << s
+ << " compute t_n and update e - " << t_ << std::endl;
+
+ }
+
+ if (mln::debug::quiet)
+ std::cout << "--------" << std::endl;
+// io::pgm::save(e_2, mln::debug::filename("e.pgm"));
- /// Constructing threshold image
- image2d<int_u8> t;
- initialize(t, input);
- std::cout << "Computing threshold image..." << std::endl;
timer_.restart();
- mln_piter_(L) p(input.domain());
- for_all(p)
- t(p) = binarization::internal::compute_sauvola_threshold(p,
- simple,
- squared,
- wsize(p));
- std::cout << "Compute t Done - " << timer_ << std::endl;
+ e_2 = transform::influence_zone_geodesic(e_2, c8());
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "influence zone - " << t_ << std::endl;
- io::pgm::save(t, "t.pgm");
- timer_.restart();
+// Saving influence zone scale image.
+// io::pgm::save(e_2, mln::debug::filename("e_ext.pgm"));
+// io::pbm::save(bin, argv[8]);
+// io::pgm::save(t, mln::debug::filename("t.pgm"));
- /// Applying threshold image and save.
- io::pbm::save(apply_sauvola(input_full, t, s), argv[argc - 1]);
- std::cout << "sauvola applied and saved Done - " << timer_ << std::endl;
+// for (unsigned i = 2; i < t_ima.size(); ++i)
+// io::pgm::save(t_ima[i], mln::debug::filename("t.pgm", i));
- trace::exiting("main");
+ timer_.restart();
+ image2d<bool> out_new = binarize_generic(input_full, e_2, t_ima, s);
+// image2d<bool> out_new = binarize(input_full, e_2, t_ima);
+ t_ = timer_;
+ if (mln::debug::quiet)
+ std::cout << "Compute bin - " << t_ << std::endl;
+
+ t_ = sauvola_timer_;
+ if (mln::debug::quiet)
+ std::cout << "Sauvola : " << t_ << std::endl;
+ io::pbm::save(out_new, argv[5]);
}
+
diff --git a/scribo/subsampling/integral_single_image.hh b/scribo/subsampling/integral_single_image.hh
new file mode 100644
index 0000000..07b391d
--- /dev/null
+++ b/scribo/subsampling/integral_single_image.hh
@@ -0,0 +1,403 @@
+// 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_SUBSAMPLING_INTEGRAL_HH
+# define SCRIBO_SUBSAMPLING_INTEGRAL_HH
+
+/// \file
+///
+/// Both subsampling and integral image computation.
+
+#include <mln/core/concept/image.hh>
+#include <mln/metal/equal.hh>
+#include <mln/extension/fill.hh>
+#include <mln/debug/println.hh>
+#include <mln/debug/println_with_border.hh>
+
+
+
+namespace scribo
+{
+
+ namespace subsampling
+ {
+ using namespace mln;
+
+
+ /*! \brief Subsample an image and compute tow integral images.
+
+ \param[in] input An image of Scalar.
+ \param[in] scale The scale factor.
+ \param[in] integral_sum Integral image of mean values.
+ \param[in] integral_sum_2 Integral image of squared mean values.
+ \param[in] output_domain The domain of the subscaled image.
+ \param[in] border_thickness Border of the integral and subscaled images.
+
+ \p integral_sum, \p integral_sum_2 and output image have the same domain.
+ The output domain is set with \p output_domain.
+
+ */
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral(const Image<I>& input, unsigned scale,
+ Image<J>& integral_sum, Image<J>& integral_sum_2,
+ const mln_domain(I)& output_domain, unsigned border_thickness);
+
+ /*! \overload
+
+ The output domain is defined as follows :
+
+ for_each(dimension)
+ input.domain()[dimension] = (N + scale - 1) / scale
+
+ where N is the number of elements in one run in a given dimension.
+
+ The border thickness is set to mln::border::thickness.
+ */
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral(const Image<I>& input, unsigned scale,
+ Image<J>& integral_sum, Image<J>& integral_sum_2);
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+ // Implementation.
+
+ namespace impl
+ {
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral_3(const Image<I>& input_,
+ Image<J>& integral_sum_sum_2_,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ trace::entering("subsampling::impl::integral_3");
+
+ const unsigned scale = 3;
+
+ const I& input = exact(input_);
+ J& integral_sum_sum_2 = exact(integral_sum_sum_2_);
+ const unsigned area = scale * scale;
+
+ mln_precondition(input.is_valid());
+ mln_precondition(input.domain().pmin() == literal::origin);
+ mln_precondition(scale > 1);
+
+ typedef mln_value(I) V;
+ typedef mln_sum(V) S;
+ typedef mln_value(J) V2;
+ typedef mln_site(I) P;
+
+ mln_concrete(I) sub(output_domain, border_thickness);
+ V* p_sub = sub.buffer();
+
+ integral_sum_sum_2.init_(output_domain, border_thickness);
+ V2* p_integ = integral_sum_sum_2.buffer();
+
+ const unsigned up = sub.delta_index(dpoint2d(-1, 0));
+
+ const unsigned nrows = 3 * output_domain.nrows();
+ const unsigned ncols = 3 * output_domain.ncols();
+
+ unsigned row = 0;
+
+ unsigned b_offset = sub.delta_index(dpoint2d(border_thickness,
+ border_thickness));
+ p_sub += b_offset;
+ p_integ += b_offset;
+ {
+ S h_sum = 0, h_sum_2 = 0;
+ const V* ptr1 = & input.at_(row, 0);
+ const V* ptr2 = & input.at_(row + 1, 0);
+ const V* ptr3 = & input.at_(row + 2, 0);
+ for (unsigned col = 0; col < ncols; col += scale)
+ {
+ S sum;
+ sum = *ptr1 + *(ptr1 + 1) + *(ptr1 + 2);
+ sum += *ptr2 + *(ptr2 + 1) + *(ptr2 + 2);
+ sum += *ptr3 + *(ptr3 + 1) + *(ptr3 + 2);
+ ptr1 += 3;
+ ptr2 += 3;
+ ptr3 += 3;
+
+ S val = sum / area;
+ *p_sub++ = val;
+
+ h_sum += val;
+ h_sum_2 += val * val;
+
+ // exception
+ p_integ->first() = h_sum;
+ p_integ->second() = h_sum_2;
+
+ p_integ += 1;
+ }
+ }
+
+ unsigned b_next = 2 * border_thickness;
+
+ p_sub += b_next;
+ p_integ += b_next;
+
+ for (row += scale; row < nrows; row += scale)
+ {
+ S h_sum = 0, h_sum_2 = 0;
+ const V* ptr1 = & input.at_(row, 0);
+ const V* ptr2 = & input.at_(row + 1, 0);
+ const V* ptr3 = & input.at_(row + 2, 0);
+ for (unsigned col = 0; col < ncols; col += scale)
+ {
+ S sum;
+ sum = *ptr1 + *(ptr1 + 1) + *(ptr1 + 2);
+ sum += *ptr2 + *(ptr2 + 1) + *(ptr2 + 2);
+ sum += *ptr3 + *(ptr3 + 1) + *(ptr3 + 2);
+ ptr1 += 3;
+ ptr2 += 3;
+ ptr3 += 3;
+
+ S val = sum / area;
+ *p_sub++ = val;
+
+ h_sum += val;
+ h_sum_2 += val * val;
+
+ p_integ->first() = h_sum + (p_integ + up)->first();
+ p_integ->second() = h_sum_2 + (p_integ + up)->second();
+
+ p_integ += 1;
+ }
+
+ p_sub += b_next;
+ p_integ += b_next;
+ }
+
+ trace::exiting("subsampling::impl::integral_3");
+ return sub;
+ }
+
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral_2(const Image<I>& input_,
+ Image<J>& integral_sum_sum_2_,
+ const mln_domain(I)& output_domain,
+ unsigned border_thickness)
+ {
+ trace::entering("subsampling::impl::integral_2");
+
+ const unsigned scale = 2;
+
+ const I& input = exact(input_);
+ J& integral_sum_sum_2 = exact(integral_sum_sum_2_);
+ const unsigned area = scale * scale;
+
+ typedef mln_value(I) V;
+ typedef mln_sum(V) S;
+ typedef mln_site(I) P;
+ typedef mln_value(J) V2;
+
+ mlc_bool(P::dim == 2)::check();
+ mln_precondition(input.is_valid());
+ mln_precondition(input.domain().pmin() == literal::origin);
+ mln_precondition(scale > 1);
+
+ mln_concrete(I) sub(output_domain, border_thickness);
+ V* p_sub = sub.buffer();
+
+ integral_sum_sum_2.init_(output_domain, border_thickness);
+ V2* p_integ = integral_sum_sum_2.buffer();
+
+ const unsigned up = sub.delta_index(dpoint2d(-1, 0));
+
+ const unsigned nrows = 2 * output_domain.nrows();
+ const unsigned ncols = 2 * output_domain.ncols();
+
+ extension::fill(sub, 0);
+
+ unsigned b_offset = sub.delta_index(dpoint2d(border_thickness,
+ border_thickness));
+ p_sub += b_offset;
+ p_integ += b_offset;
+
+ unsigned row = 0;
+ {
+ S h_sum = 0, h_sum_2 = 0;
+ const V* ptr1 = & input.at_(row, 0);
+ const V* ptr2 = & input.at_(row + 1, 0);
+ for (unsigned col = 0; col < ncols; col += scale)
+ {
+ S sum;
+ sum = *ptr1 + *(ptr1 + 1);
+ sum += *ptr2 + *(ptr2 + 1);
+ ptr1 += 2;
+ ptr2 += 2;
+
+ S val = sum / area;
+ *p_sub++ = val;
+
+ h_sum += val;
+ h_sum_2 += val * val;
+
+ // exception
+ p_integ->first() = h_sum;
+ p_integ->second() = h_sum_2;
+
+ p_integ += 1;
+ }
+ }
+
+ unsigned b_next = 2 * border_thickness;
+
+ p_sub += b_next;
+ p_integ += b_next;
+
+ for (row += scale; row < nrows; row += scale)
+ {
+ S h_sum = 0, h_sum_2 = 0;
+ const V* ptr1 = & input.at_(row, 0);
+ const V* ptr2 = & input.at_(row + 1, 0);
+ for (unsigned col = 0; col < ncols; col += scale)
+ {
+ S sum;
+ sum = *ptr1 + *(ptr1 + 1);
+ sum += *ptr2 + *(ptr2 + 1);
+ ptr1 += 2;
+ ptr2 += 2;
+
+ S val = sum / area;
+ *p_sub++ = val;
+
+ h_sum += val;
+ h_sum_2 += val * val;
+
+ p_integ->first() = h_sum + (p_integ + up)->first();
+ p_integ->second() = h_sum_2 + (p_integ + up)->second();
+
+ p_integ += 1;
+ }
+
+ p_sub += b_next;
+ p_integ += b_next;
+ }
+
+ trace::exiting("subsampling::impl::integral_2");
+ return sub;
+ }
+
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral(const Image<I>& input, unsigned scale,
+ Image<J>& integral_sum_sum_2,
+ const mln_domain(I)& output_domain, unsigned border_thickness)
+ {
+ // mln_precondition(input.nrows() % scale == 0);
+ // mln_precondition(input.ncols() % scale == 0);
+ if (scale == 3)
+ return integral_3(input, integral_sum_sum_2,
+ output_domain, border_thickness);
+ else if (scale == 2)
+ return integral_2(input, integral_sum_sum_2,
+ output_domain, border_thickness);
+ else
+ std::cerr << "NYI!" << std::endl;
+
+ typedef mln_concrete(I) output_t;
+ return output_t();
+ }
+
+
+ } // end of namespace mln::subsampling::impl
+
+
+
+
+ // Facades.
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral(const Image<I>& input_, unsigned scale,
+ Image<J>& integral_sum_sum_2,
+ const mln_domain(I)& output_domain, unsigned border_thickness)
+ {
+ trace::entering("subsampling::integral");
+
+ const I& input = exact(input_);
+
+ mln_precondition(input.is_valid());
+ mln_precondition(input.domain().pmin() == literal::origin);
+ mln_precondition(scale > 1);
+
+ mln_concrete(I)
+ output = impl::integral(input, scale, integral_sum_sum_2,
+ output_domain, border_thickness);
+
+ trace::exiting("subsampling::integral");
+ return output;
+ }
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ integral(const Image<I>& input_, unsigned scale,
+ Image<J>& integral_sum_sum_2)
+ {
+ trace::entering("subsampling::integral");
+
+ const I& input = exact(input_);
+
+ mln_precondition(input.is_valid());
+ mln_precondition(input.domain().pmin() == literal::origin);
+ mln_precondition(scale > 1);
+
+ box<mln_site(I)> b = make::box2d((input.nrows() + scale - 1) / scale,
+ (input.ncols() + scale - 1) / scale);
+ mln_concrete(I) output;
+ output = integral(input_, scale, integral_sum_sum_2,
+ b, mln::border::thickness);
+
+ trace::exiting("subsampling::integral");
+ return output;
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::subsampling
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_SUBSAMPLING_INTEGRAL_HH
--
1.5.6.5
1
0