olena-2.0-84-g0e2a2c0 Refactor local threshold binarization algorithms.

* scribo/binarization/internal/compute_local_threshold.hh, * scribo/binarization/internal/first_pass_functor.hh: Remove. * scribo/binarization/internal/niblack_functor.hh, * scribo/binarization/internal/niblack_threshold_functor.hh, * scribo/binarization/internal/sauvola_core.hh, * scribo/binarization/internal/sauvola_functor.hh, * scribo/binarization/internal/sauvola_ms_functor.hh, * scribo/binarization/internal/sauvola_threshold_functor.hh, * scribo/util/compute_sub_domains.hh, * scribo/util/init_integral_image.hh, * scribo/util/integral_sub_sum_sum2_functor.hh, * scribo/util/integral_sum_sum2_functor.hh, * src/binarization/pgm_niblack_threshold_image.cc: New. * scribo/binarization/internal/local_threshold_debug.hh, * scribo/binarization/internal/niblack_formula.hh, * scribo/binarization/internal/sauvola_formula.hh, * scribo/binarization/local_threshold.hh, * scribo/binarization/niblack.hh, * scribo/binarization/niblack_threshold.hh, * scribo/binarization/sauvola.hh, * scribo/binarization/sauvola_ms.hh, * scribo/binarization/sauvola_threshold.hh, * scribo/binarization/trash/compute_local_threshold.hh, * scribo/canvas/integral_browsing.hh: Cleanup and make use of functors. * src/binarization/sauvola_ms.cc, * src/binarization/sauvola_ms_debug.cc: Handle verbose/debug modes. --- scribo/ChangeLog | 36 +++ .../internal/compute_local_threshold.hh | 235 ----------------- .../binarization/internal/first_pass_functor.hh | 177 ------------- .../binarization/internal/local_threshold_debug.hh | 13 +- .../binarization/internal/niblack_formula.hh | 8 +- .../binarization/internal/niblack_functor.hh | 150 +++++++++++ .../internal/niblack_threshold_functor.hh | 149 +++++++++++ .../scribo/binarization/internal/sauvola_core.hh | 257 +++++++++++++++++++ .../binarization/internal/sauvola_formula.hh | 18 +- .../binarization/internal/sauvola_functor.hh | 157 ++++++++++++ .../binarization/internal/sauvola_ms_functor.hh | 185 ++++++++++++++ .../internal/sauvola_threshold_functor.hh | 156 ++++++++++++ scribo/scribo/binarization/local_threshold.hh | 18 +- scribo/scribo/binarization/niblack.hh | 110 +-------- scribo/scribo/binarization/niblack_threshold.hh | 170 +++++-------- scribo/scribo/binarization/sauvola.hh | 100 +------- scribo/scribo/binarization/sauvola_ms.hh | 107 ++++----- scribo/scribo/binarization/sauvola_threshold.hh | 162 +++++-------- scribo/scribo/canvas/integral_browsing.hh | 4 +- scribo/scribo/util/compute_sub_domains.hh | 110 ++++++++ scribo/scribo/util/init_integral_image.hh | 264 ++++++++++++++++++++ .../scribo/util/integral_sub_sum_sum2_functor.hh | 193 ++++++++++++++ scribo/scribo/util/integral_sum_sum2_functor.hh | 140 +++++++++++ .../binarization/pgm_niblack_threshold_image.cc | 78 ++++++ scribo/src/binarization/sauvola_ms.cc | 11 +- scribo/src/binarization/sauvola_ms_debug.cc | 5 + 26 files changed, 2109 insertions(+), 904 deletions(-) delete mode 100644 scribo/scribo/binarization/internal/compute_local_threshold.hh delete mode 100644 scribo/scribo/binarization/internal/first_pass_functor.hh create mode 100644 scribo/scribo/binarization/internal/niblack_functor.hh create mode 100644 scribo/scribo/binarization/internal/niblack_threshold_functor.hh create mode 100644 scribo/scribo/binarization/internal/sauvola_core.hh create mode 100644 scribo/scribo/binarization/internal/sauvola_functor.hh create mode 100644 scribo/scribo/binarization/internal/sauvola_ms_functor.hh create mode 100644 scribo/scribo/binarization/internal/sauvola_threshold_functor.hh create mode 100644 scribo/scribo/util/compute_sub_domains.hh create mode 100644 scribo/scribo/util/init_integral_image.hh create mode 100644 scribo/scribo/util/integral_sub_sum_sum2_functor.hh create mode 100644 scribo/scribo/util/integral_sum_sum2_functor.hh create mode 100644 scribo/src/binarization/pgm_niblack_threshold_image.cc diff --git a/scribo/ChangeLog b/scribo/ChangeLog index 43931c6..758b824 100644 --- a/scribo/ChangeLog +++ b/scribo/ChangeLog @@ -1,5 +1,41 @@ 2012-08-23 Guillaume Lazzara <z@lrde.epita.fr> + Refactor local threshold binarization algorithms. + + * scribo/binarization/internal/compute_local_threshold.hh, + * scribo/binarization/internal/first_pass_functor.hh: Remove. + + * scribo/binarization/internal/niblack_functor.hh, + * scribo/binarization/internal/niblack_threshold_functor.hh, + * scribo/binarization/internal/sauvola_core.hh, + * scribo/binarization/internal/sauvola_functor.hh, + * scribo/binarization/internal/sauvola_ms_functor.hh, + * scribo/binarization/internal/sauvola_threshold_functor.hh, + * scribo/util/compute_sub_domains.hh, + * scribo/util/init_integral_image.hh, + * scribo/util/integral_sub_sum_sum2_functor.hh, + * scribo/util/integral_sum_sum2_functor.hh, + * src/binarization/pgm_niblack_threshold_image.cc: New. + + * scribo/binarization/internal/local_threshold_debug.hh, + * scribo/binarization/internal/niblack_formula.hh, + * scribo/binarization/internal/sauvola_formula.hh, + * scribo/binarization/local_threshold.hh, + * scribo/binarization/niblack.hh, + * scribo/binarization/niblack_threshold.hh, + * scribo/binarization/sauvola.hh, + * scribo/binarization/sauvola_ms.hh, + * scribo/binarization/sauvola_threshold.hh, + * scribo/binarization/trash/compute_local_threshold.hh, + * scribo/canvas/integral_browsing.hh: Cleanup and make use of + functors. + + * src/binarization/sauvola_ms.cc, + * src/binarization/sauvola_ms_debug.cc: Handle verbose/debug + modes. + +2012-08-23 Guillaume Lazzara <z@lrde.epita.fr> + * sauvola_fast.cc: Cleanup and add some debug. 2012-08-23 Guillaume Lazzara <z@lrde.epita.fr> diff --git a/scribo/scribo/binarization/internal/compute_local_threshold.hh b/scribo/scribo/binarization/internal/compute_local_threshold.hh deleted file mode 100644 index 4c25e17..0000000 --- a/scribo/scribo/binarization/internal/compute_local_threshold.hh +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (C) 2010, 2011 EPITA Research and Development Laboratory -// (LRDE) -// -// This file is part of Olena. -// -// Olena is free software: you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation, version 2 of the License. -// -// Olena is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Olena. If not, see <http://www.gnu.org/licenses/>. -// -// As a special exception, you may use this file as part of a free -// software project without restriction. Specifically, if other files -// instantiate templates or use macros or inline functions from this -// file, or you compile this file and link it with other files to produce -// an executable, this file does not by itself cause the resulting -// executable to be covered by the GNU General Public License. This -// exception does not however invalidate any other reasons why the -// executable file might be covered by the GNU General Public License. - -#ifndef SCRIBO_BINARIZATION_INTERNAL_COMPUTE_LOCAL_THRESHOLD_HH -# define SCRIBO_BINARIZATION_INTERNAL_COMPUTE_LOCAL_THRESHOLD_HH - - -/// \file -/// -/// \brief Compute a threshold with Local's binarization formula. - -# include <algorithm> -# include <cmath> - -# include <mln/core/image/image2d.hh> -# include <mln/value/int_u8.hh> - -# include <scribo/binarization/internal/local_threshold_debug.hh> - - - -extern mln::image2d<double> skewness; -extern mln::image2d<bool> skewness_pbm; - -namespace scribo -{ - - namespace binarization - { - - namespace internal - { - - using namespace mln; - - - /*! \brief Compute a point wise threshold according to a local - binarization formula. - - \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). - \param[in] formula The function to use to compute the local - threshold. - - \return A threshold. - */ - template <typename P, typename J, typename F> - double - compute_local_threshold(const P& p, - const J& simple, - const J& squared, - int win_width, double K, double R, - const F& formula); - - - -# ifndef MLN_INCLUDE_ONLY - - - template <typename P, typename J, typename F> - double - compute_local_threshold(const P& p, - const J& simple, - const J& squared, - int win_width, double K, double R, - const F& formula) - { - mln_precondition(simple.nrows() == squared.nrows()); - mln_precondition(simple.ncols() == squared.ncols()); - - // Window half width. - int w_2 = win_width >> 1; - - 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); - int col_max = std::min(static_cast<int>(simple.ncols()) - 1, - p.col() + w_2); - - - double wh = (row_max - row_min) * (col_max - col_min); - - // Mean. - 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)); - - // if (!skewness_pbm(p)) - // { - // m_x_y_tmp = 255 * wh - m_x_y_tmp; - // } - - double m_x_y = m_x_y_tmp / wh; - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - // Store local mean - debug_mean(p) = m_x_y * mean_debug_factor; -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG - - // Standard deviation. - 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)); - - // if (!skewness_pbm(p)) - // { - // s_x_y_tmp = std::pow(255, 2) * wh - s_x_y_tmp; - // } - - double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f)); - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - // Store local standard deviation - debug_stddev(p) = s_x_y * stddev_debug_factor; -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG - - // Thresholding. - // skewness_ = skewness(p); - // b = (p == point2d(5,5)); - double t_x_y = formula(p, m_x_y, s_x_y, K, R); - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - double alpha = K * (1 - s_x_y / R); - debug_alpham(p) = alpha * m_x_y * alpham_debug_factor; - debug_alphacond(p) = (s_x_y < (alpha * m_x_y / 2.)); -# endif // !SCRIBO_LOCAL_THRESHOLD_DEBUG - - return t_x_y; - } - - - template <typename P, typename J, typename F> - double - compute_local_threshold_single_image(const P& p, - const J& integral, - int win_width, - double K, double R, - const F& formula) - { - // 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_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); - - - double wh = (row_max - row_min + 1) * (col_max - col_min + 1); - - // 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()); - - double m_x_y = m_x_y_tmp / wh; - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - // Store local mean - debug_mean(p) = m_x_y * mean_debug_factor; -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG - - // 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()); - - double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f)); - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - // Store local standard deviation - debug_stddev(p) = s_x_y * stddev_debug_factor; -# endif // !SCRIBO_LOCAL_THRESHOLD_DEBUG - - // Thresholding. - double t_x_y = formula(m_x_y, s_x_y, K, R); - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - double alpha = K * (1 - s_x_y / R); - debug_alpham(p) = alpha * m_x_y * alpham_debug_factor; - debug_alphacond(p) = (s_x_y < (alpha * m_x_y / 2.)); -# endif // !SCRIBO_LOCAL_THRESHOLD_DEBUG - - return t_x_y; - } - - -#endif // ! MLN_INCLUDE_ONLY - - } // end of namespace scribo::binarization::internal - - } // end of namespace scribo::binarization - -} // end of namespace scribo - -#endif // ! SCRIBO_BINARIZATION_INTERNAL_COMPUTE_LOCAL_THRESHOLD_HH diff --git a/scribo/scribo/binarization/internal/first_pass_functor.hh b/scribo/scribo/binarization/internal/first_pass_functor.hh deleted file mode 100644 index f0ae5a9..0000000 --- a/scribo/scribo/binarization/internal/first_pass_functor.hh +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (C) 2009, 2010, 2011 EPITA Research and Development -// Laboratory (LRDE) -// -// This file is part of Olena. -// -// Olena is free software: you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation, version 2 of the License. -// -// Olena is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Olena. If not, see <http://www.gnu.org/licenses/>. -// -// As a special exception, you may use this file as part of a free -// software project without restriction. Specifically, if other files -// instantiate templates or use macros or inline functions from this -// file, or you compile this file and link it with other files to produce -// an executable, this file does not by itself cause the resulting -// executable to be covered by the GNU General Public License. This -// exception does not however invalidate any other reasons why the -// executable file might be covered by the GNU General Public License. - -#ifndef SCRIBO_BINARIZATION_INTERNAL_FIRST_PASS_FUNCTOR_HH -# define SCRIBO_BINARIZATION_INTERNAL_FIRST_PASS_FUNCTOR_HH - -/// \file -/// -/// - -# include <mln/core/image/image2d.hh> -# include <mln/core/alias/neighb2d.hh> -# include <mln/extension/fill.hh> -# include <mln/value/int_u8.hh> -# include <mln/data/fill.hh> - -# include <scribo/binarization/internal/sauvola_formula.hh> - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG -# include <scribo/binarization/internal/local_threshold_debug.hh> -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG - - -namespace scribo -{ - - namespace binarization - { - - namespace internal - { - - using namespace mln; - - - 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; - mln::util::array<int> dp; - - double K_; - double R_; - - sauvola_formula formula_; - - first_pass_functor(const I& input, double K, double R); - - void exec(double mean, double stddev); - void finalize(); - }; - - -# ifndef MLN_INCLUDE_ONLY - - inline - 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> - first_pass_functor<I>::first_pass_functor(const I& input, double K, double R) - : input(input), - pxl(input), - K_(K), - R_(R) - { - res = 0; - pxl.start(); - - initialize(t_sub, input); - initialize(parent, input); - initialize(msk, input); - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - initialize(debug_mean, input); - initialize(debug_stddev, input); -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG - - mln::extension::fill(msk, false); - - initialize(card, input); - data::fill(card, 1); - - dp = negative_offsets_wrt(input, c4()); - n_nbhs = dp.nelements(); - } - - - template <typename I> - void - first_pass_functor<I>::exec(double mean, double stddev) - { - mln_precondition(pxl.is_valid()); - - unsigned p = pxl.offset(); - - value::int_u8 t_p; - mln::convert::from_to(formula_(input.point_at_index(p), mean, stddev, K_, R_), - t_p); - - 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 - } - - - template <typename I> - void first_pass_functor<I>::finalize() - { - mln_assertion(! pxl.is_valid()); - } - -#endif // ! MLN_INCLUDE_ONLY - - } // 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/scribo/binarization/internal/local_threshold_debug.hh b/scribo/scribo/binarization/internal/local_threshold_debug.hh index a9da06c..f728df3 100644 --- a/scribo/scribo/binarization/internal/local_threshold_debug.hh +++ b/scribo/scribo/binarization/internal/local_threshold_debug.hh @@ -52,14 +52,15 @@ namespace scribo namespace internal { - char* stddev_image_output = 0; - char* mean_image_output = 0; - char* threshold_image_output = 0; + const char* stddev_image_output = 0; + const char* mean_image_output = 0; + const char* threshold_image_output = 0; - char* scale_image_output = 0; + const char* scale_image_output = 0; + const char* scale_iz_image_output = 0; - char* alpham_image_output = 0; - char* alphacond_image_output = 0; + const char* alpham_image_output = 0; + const char* alphacond_image_output = 0; // Declare debug images. image2d<double> debug_stddev; diff --git a/scribo/scribo/binarization/internal/niblack_formula.hh b/scribo/scribo/binarization/internal/niblack_formula.hh index 54dbc9b..d7d7580 100644 --- a/scribo/scribo/binarization/internal/niblack_formula.hh +++ b/scribo/scribo/binarization/internal/niblack_formula.hh @@ -59,13 +59,11 @@ namespace scribo \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). Unused in this formula. \return A threshold. */ double operator()(const double m_x_y, const double s_x_y, - const double K, const double R) const; + const double K) const; /*! \overload K = 0.34. @@ -80,7 +78,7 @@ namespace scribo inline double niblack_formula::operator()(const double m_x_y, const double s_x_y, - const double K, const double /*R*/) const + const double K) const { return m_x_y + K * s_x_y; } @@ -90,7 +88,7 @@ namespace scribo niblack_formula::operator()(const double m_x_y, const double s_x_y) const { return (*this)(m_x_y, s_x_y, - SCRIBO_DEFAULT_NIBLACK_K, 128); + SCRIBO_DEFAULT_NIBLACK_K); } diff --git a/scribo/scribo/binarization/internal/niblack_functor.hh b/scribo/scribo/binarization/internal/niblack_functor.hh new file mode 100644 index 0000000..83d5bbd --- /dev/null +++ b/scribo/scribo/binarization/internal/niblack_functor.hh @@ -0,0 +1,150 @@ +// Copyright (C) 2011 EPITA Research and Development Laboratory (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#ifndef SCRIBO_BINARIZATION_INTERNAL_NIBLACK_FUNCTOR_HH +# define SCRIBO_BINARIZATION_INTERNAL_NIBLACK_FUNCTOR_HH + +/// \file +/// +/// \todo Refactor with sauvola_functor. + +# include <mln/core/image/image2d.hh> +# include <mln/core/alias/neighb2d.hh> +# include <mln/extension/fill.hh> + +# include <scribo/binarization/internal/niblack_formula.hh> + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG +# include <scribo/binarization/internal/local_threshold_debug.hh> +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + +namespace scribo +{ + + namespace binarization + { + + namespace internal + { + + using namespace mln; + + + template <typename I> + struct niblack_functor + { + niblack_functor(const Image<I>& input, double K); + + // Run every 4 pixels. + void exec(double mean, double stddev); + + void end_of_row(int); + + void finalize(); + + + const I input; + mln_ch_value(I,bool) output; + + const mln_value(I)* pi; + bool* po; + + double K_; + + scribo::binarization::internal::niblack_formula formula_; + + int step_; + unsigned next_line3; + unsigned offset1; + unsigned offset2; + }; + +#ifndef MLN_INCLUDE_ONLY + + template <typename I> + niblack_functor<I>::niblack_functor(const Image<I>& input_, + double K) + : input(exact(input_)), + pi(&input(input.domain().pmin())), + K_(K) + { + step_ = 3; + next_line3 = input.delta_index(dpoint2d(+2,0)) + 2 * input.border() - 1; + + offset1 = input.delta_index(dpoint2d(+1,0)); + offset2 = input.delta_index(dpoint2d(+2,0)); + + initialize(output, input); + po = &output(output.domain().pmin()); + } + + template <typename I> + void + niblack_functor<I>::exec(double mean, double stddev) + { + double th = formula_(mean, stddev, K_); + + for (int i = 0; i < step_; ++i, ++po, ++pi) + { + *po = (*pi <= th); + *(po + offset1) = (*(pi + offset1) <= th); + *(po + offset2) = (*(pi + offset2) <= th); + } + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG + // Store local mean + unsigned index = pi - input.buffer(); + + debug_mean.element(index) = mean * mean_debug_factor; + debug_stddev.element(index) = stddev * stddev_debug_factor; + debug_threshold.element(index) = th; +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + } + + template <typename I> + void + niblack_functor<I>::end_of_row(int) + { + po += next_line3; + pi += next_line3; + } + + template <typename I> + void + niblack_functor<I>::finalize() + { + } + +#endif // ! MLN_INCLUDE_ONLY + + } // end of namespace scribo::binarization::internal + + } // end of namespace scribo::binarization + +} // end of namespace scribo + +#endif // SCRIBO_BINARIZATION_INTERNAL_NIBLACK_FUNCTOR_HH diff --git a/scribo/scribo/binarization/internal/niblack_threshold_functor.hh b/scribo/scribo/binarization/internal/niblack_threshold_functor.hh new file mode 100644 index 0000000..b6d6aae --- /dev/null +++ b/scribo/scribo/binarization/internal/niblack_threshold_functor.hh @@ -0,0 +1,149 @@ +// Copyright (C) 2011 EPITA Research and Development Laboratory (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#ifndef SCRIBO_BINARIZATION_INTERNAL_NIBLACK_THRESHOLD_FUNCTOR_HH +# define SCRIBO_BINARIZATION_INTERNAL_NIBLACK_THRESHOLD_FUNCTOR_HH + +/// \file +/// +/// \todo Refactor with sauvola_threshold_functor. + +# include <mln/core/image/image2d.hh> +# include <mln/core/alias/neighb2d.hh> +# include <mln/extension/fill.hh> + +# include <scribo/binarization/internal/niblack_formula.hh> + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG +# include <scribo/binarization/internal/local_threshold_debug.hh> +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + +namespace scribo +{ + + namespace binarization + { + + namespace internal + { + + using namespace mln; + + + template <typename I> + struct niblack_threshold_functor + { + niblack_threshold_functor(const I& input, + double K); + + // Run every 4 pixels. + void exec(double mean, double stddev); + + void end_of_row(int); + + void finalize(); + + typedef mln_concrete(I) th_t; + th_t output; + + mln_value(I)* po; + + double K_; + + scribo::binarization::internal::niblack_formula formula_; + + int step_; + unsigned next_line3; + unsigned offset1; + unsigned offset2; + }; + +#ifndef MLN_INCLUDE_ONLY + + template <typename I> + niblack_threshold_functor<I>::niblack_threshold_functor(const I& input, + double K) + : K_(K) + { + step_ = 3; + next_line3 = input.delta_index(dpoint2d(+2,0)) + 2 * input.border() - 1; + + offset1 = input.delta_index(dpoint2d(+1,0)); + offset2 = input.delta_index(dpoint2d(+2,0)); + + initialize(output, input); + po = &output(output.domain().pmin()); + } + + template <typename I> + void + niblack_threshold_functor<I>::exec(double mean, double stddev) + { + static point2d p(0,0); + + typedef mln_value(I) V; + V th = static_cast<V>(formula_(mean, stddev, K_)); + + for (int i = 0; i < step_; ++i, ++po) + { + *po = th; + *(po + offset1) = th; + *(po + offset2) = th; + } + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG + // Store local mean + unsigned index = po - output.buffer(); + + internal::debug_mean.element(index) = mean * internal::mean_debug_factor; + internal::debug_stddev.element(index) = stddev * internal::stddev_debug_factor; + internal::debug_threshold.element(index) = t; +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + } + + template <typename I> + void + niblack_threshold_functor<I>::end_of_row(int) + { + po += next_line3; + } + + template <typename I> + void + niblack_threshold_functor<I>::finalize() + { + } + +#endif // ! MLN_INCLUDE_ONLY + + } // end of namespace scribo::binarization::internal + + } // end of namespace scribo::binarization + +} // end of namespace scribo + +#endif // SCRIBO_BINARIZATION_INTERNAL_NIBLACK_THRESHOLD_FUNCTOR_HH diff --git a/scribo/scribo/binarization/internal/sauvola_core.hh b/scribo/scribo/binarization/internal/sauvola_core.hh new file mode 100644 index 0000000..9e3592f --- /dev/null +++ b/scribo/scribo/binarization/internal/sauvola_core.hh @@ -0,0 +1,257 @@ +// Copyright (C) 2011 EPITA Research and Development Laboratory (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#ifndef SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_CORE_HH +# define SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_CORE_HH + +/// \file +/// +/// + +# include <mln/core/concept/image.hh> +# include <mln/data/transform.hh> +# include <mln/value/int_u8.hh> + +# include <mln/border/mirror.hh> +# include <mln/border/adjust.hh> + +# include <scribo/canvas/integral_browsing.hh> + +# include <scribo/util/init_integral_image.hh> +# include <scribo/util/integral_sum_sum2_functor.hh> +# include <scribo/util/compute_sub_domains.hh> + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG +# include <scribo/binarization/internal/local_threshold_debug.hh> +# include <mln/io/pgm/save.hh> +# include <mln/io/pbm/save.hh> +# include <mln/data/saturate.hh> +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + +# include <mln/util/timer.hh> + +namespace scribo +{ + + namespace binarization + { + + namespace internal + { + + using namespace mln; + + + /*! \brief Run Sauvola core algorithm. Use functor \p f to + compute the threshold and perform computation. + + \input[in] input An image. + \input[in] f Functor computing Sauvola's formula. + \input[in] window_size The window size. + + */ + template <typename I, typename F> + void + sauvola_core(const Image<I>& input, F& f, + unsigned window_size); + + + /// \overload + /// The window size is set to 11. + // + template <typename I, typename F> + void + sauvola_core(const Image<I>& input, F& f); + + +# ifndef MLN_INCLUDE_ONLY + + + // Implementations. + + namespace impl + { + + namespace generic + { + + using namespace mln; + + template <typename I, typename F> + void + sauvola_core(const Image<I>& input, unsigned window_size) + { + trace::entering("scribo::binarization::internal::impl::generic::sauvola_core"); + + // Not implemented + mlc_abort(I)::check(); + + trace::exiting("scribo::binarization::internal::impl::generic::sauvola_core"); + } + + } // end of namespace scribo::binarization::internal::impl::generic + + + template <typename I, typename F> + void + sauvola_core_fastest(const Image<I>& input, F& f, unsigned window_size) + { + trace::entering("scribo::binarization::internal::impl::generic::sauvola_core"); + mln_precondition(exact(input).is_valid()); + + mln::util::timer t; + t.start(); + + // Make sure the image sizes are a multiple of 3 in each + // dimension. (browsing while binarizing relies on that + // property). + mln::util::array<mln::util::couple<box2d, unsigned> > + sub_domains = scribo::util::compute_sub_domains(input, 1, 3); + + border::adjust(input, sub_domains(1).second()); + border::mirror(input); + + scribo::util::integral_sum_sum2_functor<value::int_u8, double> fi; + image2d<mln::util::couple<double,double> > + integral = scribo::util::init_integral_image(input, 3, fi, + sub_domains[2].first(), + sub_domains[2].second()); + + t.stop(); + std::cout << "image integrale - " << t << std::endl; + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG + initialize(internal::debug_mean, input); + initialize(internal::debug_stddev, input); + + initialize(internal::debug_threshold, input); + initialize(internal::debug_alpham, input); + initialize(internal::debug_alphacond, input); +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + t.restart(); + scribo::canvas::integral_browsing(integral, 1, window_size / 3, + window_size / 3, 3, f); + t.stop(); + std::cout << "Binarization - " << t << std::endl; + + trace::exiting("scribo::binarization::internal::impl::generic::sauvola_core"); + } + + + } // end of namespace scribo::binarization::internal::impl + + + + // Dispatch + + namespace internal + { + + template <typename I, typename F> + void + sauvola_core_dispatch(const mln_value(I)&, + const Image<I>& input, F& f, + unsigned window_size) + { + impl::generic::sauvola_core(input, f, window_size); + } + + template <typename I, typename F> + void + sauvola_core_dispatch(mln::trait::image::speed::fastest, + const mln_value(I)&, + const Image<I>& input, F& f, + unsigned window_size) + { + impl::sauvola_core_fastest(input, f, window_size); + } + + + template <typename I, typename F> + void + sauvola_core_dispatch(const Image<I>& input, F& f, + unsigned window_size) + { + typedef mln_value(I) V; + sauvola_core_dispatch(mln_trait_image_speed(I)(), + V(), input, f, window_size); + } + + } // end of namespace scribo::binarization::internal::internal + + + + // Facades + + template <typename I, typename F> + void + sauvola_core(const Image<I>& input, F& f, unsigned window_size) + { + trace::entering("scribo::binarization::internal::sauvola_core"); + + mln_precondition(exact(input).is_valid()); + + internal::sauvola_core_dispatch(input, f, window_size); + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG + if (stddev_image_output) + io::pgm::save(data::saturate(value::int_u8(), debug_stddev), + stddev_image_output); + if (mean_image_output) + io::pgm::save(data::saturate(value::int_u8(), debug_mean), + mean_image_output); + if (threshold_image_output) + io::pgm::save(data::saturate(value::int_u8(), debug_threshold), + threshold_image_output); + + if (alpham_image_output) + io::pgm::save(data::saturate(value::int_u8(), debug_alpham), + alpham_image_output); + if (alphacond_image_output) + io::pbm::save(debug_alphacond, alphacond_image_output); +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + trace::exiting("scribo::binarization::internal::sauvola_core"); + } + + template <typename I, typename F> + void + sauvola_core(const Image<I>& input, F& f) + { + sauvola_core(input, f, 11); + } + + +# endif // ! MLN_INCLUDE_ONLY + + } // end of namespace scribo::binarization::internal + + } // end of namespace scribo::binarization + +} // end of namespace scribo + + +#endif // ! SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_CORE_HH diff --git a/scribo/scribo/binarization/internal/sauvola_formula.hh b/scribo/scribo/binarization/internal/sauvola_formula.hh index e010e85..a06d6f9 100644 --- a/scribo/scribo/binarization/internal/sauvola_formula.hh +++ b/scribo/scribo/binarization/internal/sauvola_formula.hh @@ -48,7 +48,7 @@ #include <mln/core/alias/point2d.hh> -extern mln::image2d<bool> skewness_pbm; +// extern mln::image2d<bool> skewness_pbm; namespace scribo { @@ -76,13 +76,13 @@ namespace scribo \return A threshold. */ - double operator()(const point2d& p, const double m_x_y, const double s_x_y, + double operator()(const double m_x_y, const double s_x_y, const double K, const double R) const; /*! \overload K = 0.34 and R = 128. */ - double operator()(const point2d& p, const double m_x_y, const double s_x_y) const; + double operator()(const double m_x_y, const double s_x_y) const; }; @@ -94,7 +94,7 @@ namespace scribo inline double - sauvola_formula::operator()(const point2d& p, const double m_x_y, const double s_x_y, + sauvola_formula::operator()(const double m_x_y, const double s_x_y, const double K, const double R) const { // if (b) @@ -104,17 +104,17 @@ namespace scribo // if (skewness_ > 0) // if (new_t != old_t) // std::cout << skewness_ << " - " << new_t << " vs " << old_t << std::endl; - if (skewness_pbm(p)) + // if (skewness_pbm(p)) return m_x_y * (1.0 + K * ((s_x_y / R) - 1.0)); - else - return (255 - m_x_y) * (1.0 + K * ((s_x_y / R) - 1.0)); + // else + // return (255 - m_x_y) * (1.0 + K * ((s_x_y / R) - 1.0)); } inline double - sauvola_formula::operator()(const point2d& p, const double m_x_y, const double s_x_y) const + sauvola_formula::operator()(const double m_x_y, const double s_x_y) const { - return (*this)(p, m_x_y, s_x_y, + return (*this)(m_x_y, s_x_y, SCRIBO_DEFAULT_SAUVOLA_K, SCRIBO_DEFAULT_SAUVOLA_R); } diff --git a/scribo/scribo/binarization/internal/sauvola_functor.hh b/scribo/scribo/binarization/internal/sauvola_functor.hh new file mode 100644 index 0000000..bff8b6f --- /dev/null +++ b/scribo/scribo/binarization/internal/sauvola_functor.hh @@ -0,0 +1,157 @@ +// Copyright (C) 2009, 2010, 2011 EPITA Research and Development +// Laboratory (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#ifndef SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_FUNCTOR_HH +# define SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_FUNCTOR_HH + +/// \file +/// +/// + +# include <mln/core/image/image2d.hh> +# include <mln/core/alias/neighb2d.hh> +# include <mln/extension/fill.hh> + +# include <scribo/binarization/internal/sauvola_formula.hh> + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG +# include <scribo/binarization/internal/local_threshold_debug.hh> +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + +namespace scribo +{ + + namespace binarization + { + + namespace internal + { + + using namespace mln; + + + template <typename I> + struct sauvola_functor + { + sauvola_functor(const Image<I>& input, double K, double R); + + // Run every 4 pixels. + void exec(double mean, double stddev); + + void end_of_row(int); + + void finalize(); + + + const I input; + mln_ch_value(I,bool) output; + + const mln_value(I)* pi; + bool* po; + + double K_; + double R_; + + scribo::binarization::internal::sauvola_formula formula_; + + int step_; + unsigned next_line3; + unsigned offset1; + unsigned offset2; + }; + +#ifndef MLN_INCLUDE_ONLY + + template <typename I> + sauvola_functor<I>::sauvola_functor(const Image<I>& input_, + double K, double R) + : input(exact(input_)), + pi(&input(input.domain().pmin())), + K_(K), + R_(R) + { + step_ = 3; + next_line3 = input.delta_index(dpoint2d(+2,0)) + 2 * input.border() - 1; + + offset1 = input.delta_index(dpoint2d(+1,0)); + offset2 = input.delta_index(dpoint2d(+2,0)); + + initialize(output, input); + po = &output(output.domain().pmin()); + } + + template <typename I> + void + sauvola_functor<I>::exec(double mean, double stddev) + { + double th = formula_(mean, stddev, K_, R_); + + for (int i = 0; i < step_; ++i, ++po, ++pi) + { + *po = (*pi <= th); + *(po + offset1) = (*(pi + offset1) <= th); + *(po + offset2) = (*(pi + offset2) <= th); + } + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG + // Store local mean + unsigned index = pi - input.buffer(); + + debug_mean.element(index) = mean * mean_debug_factor; + debug_stddev.element(index) = stddev * stddev_debug_factor; + debug_threshold.element(index) = th; + + double alpha = K_ * (1 - stddev / R_); + debug_alpham.element(index) = alpha * mean * alpham_debug_factor; + debug_alphacond.element(index) = (stddev < (alpha * mean / 2.)); +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + } + + template <typename I> + void + sauvola_functor<I>::end_of_row(int) + { + po += next_line3; + pi += next_line3; + } + + template <typename I> + void + sauvola_functor<I>::finalize() + { + } + +#endif // ! MLN_INCLUDE_ONLY + + } // end of namespace scribo::binarization::internal + + } // end of namespace scribo::binarization + +} // end of namespace scribo + +#endif // SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_FUNCTOR_HH diff --git a/scribo/scribo/binarization/internal/sauvola_ms_functor.hh b/scribo/scribo/binarization/internal/sauvola_ms_functor.hh new file mode 100644 index 0000000..9a23aa4 --- /dev/null +++ b/scribo/scribo/binarization/internal/sauvola_ms_functor.hh @@ -0,0 +1,185 @@ +// Copyright (C) 2009, 2010, 2011 EPITA Research and Development +// Laboratory (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#ifndef SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_MS_FUNCTOR_HH +# define SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_MS_FUNCTOR_HH + +/// \file +/// +/// + +# include <mln/core/image/image2d.hh> +# include <mln/core/alias/neighb2d.hh> +# include <mln/extension/fill.hh> +# include <mln/value/int_u8.hh> +# include <mln/data/fill.hh> + +# include <scribo/binarization/internal/sauvola_formula.hh> + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG +# include <scribo/binarization/internal/local_threshold_debug.hh> +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + +namespace scribo +{ + + namespace binarization + { + + namespace internal + { + + using namespace mln; + + + template <typename I> + struct sauvola_ms_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; + mln::util::array<int> dp; + + double K_; + double R_; + + sauvola_formula formula_; + + sauvola_ms_functor(const I& input, double K, double R); + + void exec(double mean, double stddev); + void end_of_row(int row); + void finalize(); + + }; + + +# ifndef MLN_INCLUDE_ONLY + + inline + 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> + sauvola_ms_functor<I>::sauvola_ms_functor(const I& input, double K, double R) + : input(input), + pxl(input), + K_(K), + R_(R) + { + res = 0; + pxl.start(); + + initialize(t_sub, input); + initialize(parent, input); + initialize(msk, input); + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG + initialize(debug_mean, input); + initialize(debug_stddev, input); +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + mln::extension::fill(msk, false); + + initialize(card, input); + data::fill(card, 1); + + dp = negative_offsets_wrt(input, c4()); + n_nbhs = dp.nelements(); + } + + + template <typename I> + void + sauvola_ms_functor<I>::exec(double mean, double stddev) + { + mln_precondition(pxl.is_valid()); + + unsigned p = pxl.offset(); + + value::int_u8 t_p; + mln::convert::from_to(formula_(mean, stddev, K_, R_), t_p); + + 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 + } + + + template <typename I> + void + sauvola_ms_functor<I>::end_of_row(int row) + { + (void) row; + } + + template <typename I> + void sauvola_ms_functor<I>::finalize() + { + mln_assertion(! pxl.is_valid()); + } + +#endif // ! MLN_INCLUDE_ONLY + + } // end of namespace scribo::binarization::internal + + } // end of namespace scribo::binarization + +} // end of namespace scribo + +#endif // SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_MS_FUNCTOR_HH diff --git a/scribo/scribo/binarization/internal/sauvola_threshold_functor.hh b/scribo/scribo/binarization/internal/sauvola_threshold_functor.hh new file mode 100644 index 0000000..d7eaf4a --- /dev/null +++ b/scribo/scribo/binarization/internal/sauvola_threshold_functor.hh @@ -0,0 +1,156 @@ +// Copyright (C) 2009, 2010, 2011 EPITA Research and Development +// Laboratory (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#ifndef SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_THRESHOLD_FUNCTOR_HH +# define SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_THRESHOLD_FUNCTOR_HH + +/// \file +/// +/// + +# include <mln/core/image/image2d.hh> +# include <mln/core/alias/neighb2d.hh> +# include <mln/extension/fill.hh> + +# include <scribo/binarization/internal/sauvola_formula.hh> + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG +# include <scribo/binarization/internal/local_threshold_debug.hh> +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + +namespace scribo +{ + + namespace binarization + { + + namespace internal + { + + using namespace mln; + + + template <typename I> + struct sauvola_threshold_functor + { + sauvola_threshold_functor(const I& input, + double K, double R); + + // Run every 4 pixels. + void exec(double mean, double stddev); + + void end_of_row(int); + + void finalize(); + + typedef mln_concrete(I) th_t; + th_t output; + + mln_value(I)* po; + + double K_; + double R_; + + scribo::binarization::internal::sauvola_formula formula_; + + int step_; + unsigned next_line3; + unsigned offset1; + unsigned offset2; + }; + +#ifndef MLN_INCLUDE_ONLY + + template <typename I> + sauvola_threshold_functor<I>::sauvola_threshold_functor(const I& input, + double K, double R) + : K_(K), + R_(R) + { + step_ = 3; + next_line3 = input.delta_index(dpoint2d(+2,0)) + 2 * input.border() - 1; + + offset1 = input.delta_index(dpoint2d(+1,0)); + offset2 = input.delta_index(dpoint2d(+2,0)); + + initialize(output, input); + po = &output(output.domain().pmin()); + } + + template <typename I> + void + sauvola_threshold_functor<I>::exec(double mean, double stddev) + { + static point2d p(0,0); + + typedef mln_value(I) V; + V th = static_cast<V>(formula_(mean, stddev, K_, R_)); + + for (int i = 0; i < step_; ++i, ++po) + { + *po = th; + *(po + offset1) = th; + *(po + offset2) = th; + } + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG + // Store local mean + unsigned index = po - output.buffer(); + + internal::debug_mean.element(index) = mean * internal::mean_debug_factor; + internal::debug_stddev.element(index) = stddev * internal::stddev_debug_factor; + internal::debug_threshold.element(index) = t; + + double alpha = K * (1 - stddev / R); + internal::debug_alpham.element(index) = alpha * mean * internal::alpham_debug_factor; + internal::debug_alphacond.element(index) = (stddev < (alpha * mean / 2.)); +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + } + + template <typename I> + void + sauvola_threshold_functor<I>::end_of_row(int) + { + po += next_line3; + } + + template <typename I> + void + sauvola_threshold_functor<I>::finalize() + { + } + +#endif // ! MLN_INCLUDE_ONLY + + } // end of namespace scribo::binarization::internal + + } // end of namespace scribo::binarization + +} // end of namespace scribo + +#endif // SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_THRESHOLD_FUNCTOR_HH diff --git a/scribo/scribo/binarization/local_threshold.hh b/scribo/scribo/binarization/local_threshold.hh index ab589e2..e7b0f5d 100644 --- a/scribo/scribo/binarization/local_threshold.hh +++ b/scribo/scribo/binarization/local_threshold.hh @@ -1,5 +1,5 @@ -// Copyright (C) 2009, 2010 EPITA Research and Development Laboratory -// (LRDE) +// Copyright (C) 2009, 2010, 2011 EPITA Research and Development +// Laboratory (LRDE) // // This file is part of Olena. // @@ -34,8 +34,6 @@ /// /// \brief Binarize an image using a threshold image. -extern mln::image2d<bool> skewness_pbm; - namespace scribo { @@ -111,10 +109,7 @@ namespace scribo mln_piter(I) p(input.domain()); for_all(p) - if (skewness_pbm(p)) - output(p) = (input(p) <= threshold(p)); - else - output(p) = ((255 - input(p)) <= threshold(p)); + output(p) = (input(p) <= threshold(p)); trace::exiting("scribo::binarization::impl::generic::local_threshold"); return output; @@ -135,8 +130,6 @@ namespace scribo const I& input = exact(input_); const T& threshold = exact(threshold_); - border::resize(::skewness_pbm, input.border()); - typedef mln_ch_value(I, bool) O; O output; initialize(output, input); @@ -146,10 +139,7 @@ namespace scribo mln_pixter(O) po(output); for_all_3(pi, pt, po) - if (skewness_pbm.element(pi.offset())) - po.val() = (pi.val() <= pt.val()); - else - po.val() = ((255 - pi.val()) <= pt.val()); + po.val() = (pi.val() <= pt.val()); trace::exiting("scribo::binarization::impl::generic::local_threshold_fastest"); return output; diff --git a/scribo/scribo/binarization/niblack.hh b/scribo/scribo/binarization/niblack.hh index e66e7b4..00e6ab6 100644 --- a/scribo/scribo/binarization/niblack.hh +++ b/scribo/scribo/binarization/niblack.hh @@ -1,4 +1,5 @@ -// Copyright (C) 2011 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2009, 2010, 2011 EPITA Research and Development +// Laboratory (LRDE) // // This file is part of Olena. // @@ -31,18 +32,8 @@ /// # include <mln/core/concept/image.hh> -# include <mln/data/transform.hh> -# include <mln/value/int_u8.hh> - -# include <scribo/binarization/niblack_threshold.hh> -# include <scribo/binarization/local_threshold.hh> -# include <scribo/binarization/internal/local_threshold_debug.hh> - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG -# include <mln/io/pgm/save.hh> -# include <mln/io/pbm/save.hh> -# include <mln/data/saturate.hh> -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG +# include <scribo/binarization/internal/niblack_functor.hh> +# include <scribo/binarization/internal/sauvola_core.hh> namespace scribo { @@ -57,7 +48,7 @@ namespace scribo \input[in] input An image. \input[in] window_size The window size. - \input[in] K Niblack's formulae constant. + \input[in] K Sauvola's formulae constant. \return A binary image. @@ -70,7 +61,8 @@ namespace scribo /*! \brief Convert an image into a binary image. - Niblack's formulae constant K is set to 0.34. + Sauvola's formulae constant K is set to + SCRIBO_DEFAULT_NIBLACK_K. \input[in] input An image. \input[in] window_size The window size. @@ -93,101 +85,21 @@ namespace scribo # ifndef MLN_INCLUDE_ONLY - - // Implementations. - - namespace impl - { - - namespace generic - { - - template <typename I> - mln_ch_value(I, bool) - niblack(const Image<I>& input, unsigned window_size, double K) - { - trace::entering("scribo::binarization::impl::generic::niblack"); - mln_precondition(exact(input).is_valid()); - - mln_ch_value(I,value::int_u8) - threshold_image = binarization::niblack_threshold(input, window_size, K); - - mln_ch_value(I, bool) - output = local_threshold(input, threshold_image); - - trace::exiting("scribo::binarization::impl::generic::niblack"); - return output; - } - - } // end of namespace scribo::binarization::impl::generic - - - } // end of namespace scribo::binarization::impl - - - - // Dispatch - - namespace internal - { - - template <typename I> - mln_ch_value(I, bool) - niblack_dispatch(const mln_value(I)&, - const Image<I>& input, unsigned window_size, - double K) - { - return impl::generic::niblack(input, window_size, K); - } - - - template <typename I> - mln_ch_value(I, bool) - niblack_dispatch(const Image<I>& input, unsigned window_size, - double K) - { - typedef mln_value(I) V; - return niblack_dispatch(V(), input, window_size, K); - } - - } // end of namespace scribo::binarization::internal - - - // Facades template <typename I> mln_ch_value(I, bool) - niblack(const Image<I>& input, unsigned window_size, double K) + niblack(const Image<I>& input, unsigned window_size, double K) { trace::entering("scribo::binarization::niblack"); mln_precondition(exact(input).is_valid()); - mln_ch_value(I, bool) - output = internal::niblack_dispatch(input, window_size, K); - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - if (internal::stddev_image_output) - io::pgm::save(data::saturate(value::int_u8(), internal::debug_stddev), - internal::stddev_image_output); - if (internal::mean_image_output) - io::pgm::save(data::saturate(value::int_u8(), internal::debug_mean), - internal::mean_image_output); - if (internal::threshold_image_output) - io::pgm::save(data::saturate(value::int_u8(), internal::debug_threshold), - internal::threshold_image_output); - - if (internal::alpham_image_output) - io::pgm::save(data::saturate(value::int_u8(), internal::debug_alpham), - internal::alpham_image_output); - if (internal::alphacond_image_output) - io::pbm::save(internal::debug_alphacond, internal::alphacond_image_output); -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG - + internal::niblack_functor<I> f(input, K); + internal::sauvola_core(input, f, window_size); trace::exiting("scribo::binarization::niblack"); - return output; + return f.output; } diff --git a/scribo/scribo/binarization/niblack_threshold.hh b/scribo/scribo/binarization/niblack_threshold.hh index db4a74e..34517cb 100644 --- a/scribo/scribo/binarization/niblack_threshold.hh +++ b/scribo/scribo/binarization/niblack_threshold.hh @@ -1,4 +1,5 @@ -// Copyright (C) 2011 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2009, 2010, 2011 EPITA Research and Development +// Laboratory (LRDE) // // This file is part of Olena. // @@ -31,7 +32,6 @@ /// Compute an image of local threshold using Niblack algorithm. /// \fixme return type too restrictive! -/// \fixme Revamp code and merge with sauvola_threshold.hh. # include <algorithm> # include <cmath> @@ -40,9 +40,18 @@ # include <mln/value/int_u.hh> # include <mln/value/int_u8.hh> +# include <mln/border/mirror.hh> +# include <mln/border/adjust.hh> + # include <scribo/core/init_integral_image.hh> -# include <scribo/binarization/internal/compute_local_threshold.hh> # include <scribo/binarization/internal/niblack_formula.hh> +# include <scribo/binarization/internal/niblack_threshold_functor.hh> + +# include <scribo/util/init_integral_image.hh> +# include <scribo/util/integral_sum_sum2_functor.hh> +# include <scribo/util/compute_sub_domains.hh> + +# include <scribo/canvas/integral_browsing.hh> @@ -68,15 +77,7 @@ namespace scribo template <typename I, typename J> mln_ch_value(I, value::int_u8) niblack_threshold(const Image<I>& input, unsigned window_size, - double K, - Image<J>& simple, - Image<J>& squared); - - /// \overload - template <typename I> - mln_ch_value(I, value::int_u8) - niblack_threshold(const Image<I>& input, unsigned window_size, - double K); + double K); /// \overload /// K is set to 0.34 @@ -106,81 +107,66 @@ namespace scribo namespace generic { - template <typename I, typename J> + template <typename I> inline mln_concrete(I) niblack_threshold(const Image<I>& input_, unsigned window_size, - double K, - Image<J>& simple_, - Image<J>& squared_) + double K) { trace::entering("scribo::binarization::impl::generic::niblack_threshold"); - const I& input = exact(input_); - J& simple = exact(simple_); - J& squared = exact(squared_); - - mln_assertion(input.is_valid()); - mln_assertion(simple.is_valid()); - mln_assertion(squared.is_valid()); - - typedef mln_value(I) V; - typedef mln_site(I) P; - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - initialize(internal::debug_mean, input); - initialize(internal::debug_stddev, input); - initialize(internal::debug_threshold, input); - initialize(internal::debug_alpham, input); - initialize(internal::debug_alphacond, input); -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG - - mln_concrete(I) output; - initialize(output, input); - - const mln::def::coord - nrows = static_cast<mln::def::coord>(input.nrows()), - ncols = static_cast<mln::def::coord>(input.ncols()); - - - internal::niblack_formula formula; - for(mln::def::coord row = 0; row < nrows; ++row) - for(mln::def::coord col = 0; col < ncols; ++col) - { - // FIXME: Setting R parameter to 128 should not be - // hard-coded. Even though it is not used in Niblack's - // formula, this parameter is used for debug images and - // should be adapted to the data range values. - double t = internal::compute_local_threshold(P(row, col), simple, - squared, window_size, - K, - 128, - formula); - mln::convert::from_to(t, output.at_(row, col)); - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - internal::debug_threshold.at_(row, col) = t; -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG - } + // Not implemented + mlc_abort(I)::check(); + typedef mln_concrete(I) output_t; trace::exiting("scribo::binarization::impl::generic::niblack_threshold"); - return output; + return output_t(); } } // end of namespace scribo::binarization::impl::generic - - template <typename I, typename J> + template <typename I> inline mln_concrete(I) - niblack_threshold_gl(const I& input, unsigned window_size, - double K, - Image<J>& simple, - Image<J>& squared) + niblack_threshold_gl(const Image<I>& input_, unsigned window_size, + double K) { - return impl::generic::niblack_threshold(input, window_size, K, - simple, squared); + trace::entering("scribo::binarization::impl::niblack_threshold_gl"); + + const I& input = exact(input_); + + mln_precondition(exact(input).is_valid()); + + // Make sure the image sizes are a multiple of 3 in each + // dimension. (browsing while binarizing relies on that + // property). + mln::util::array<mln::util::couple<box2d, unsigned> > + sub_domains = scribo::util::compute_sub_domains(input, 1, 3); + + border::adjust(input, sub_domains(1).second()); + border::mirror(input); + + typedef mln::util::couple<double,double> V_i; + scribo::util::integral_sum_sum2_functor<mln_value(I), double> fi; + mln_ch_value(I,V_i) + integral = scribo::util::init_integral_image(input, 3, fi); + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG + initialize(internal::debug_mean, input); + initialize(internal::debug_stddev, input); + + initialize(internal::debug_threshold, input); + initialize(internal::debug_alpham, input); + initialize(internal::debug_alphacond, input); +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + + internal::niblack_threshold_functor<I> f(input, K); + scribo::canvas::integral_browsing(integral, 1, window_size / 3, + window_size / 3, 3, f); + + trace::exiting("scribo::binarization::impl::niblack_threshold_gl"); + return f.output; } @@ -194,28 +180,23 @@ namespace scribo namespace internal { - template <unsigned n, typename I, typename J> + template <unsigned n, typename I> inline mln_ch_value(I, value::int_u<n>) niblack_threshold_dispatch(const value::int_u<n>&, const I& input, unsigned window_size, - double K, - J& simple, - J& squared) + double K) { - return impl::niblack_threshold_gl(input, window_size, K, - simple, squared); + return impl::niblack_threshold_gl(input, window_size, K); } - template <typename I, typename J> + template <typename I> inline mln_ch_value(I, value::int_u8) niblack_threshold_dispatch(const mln_value(I)&, const I& input, unsigned window_size, - double K, - J& simple, - J& squared) + double K) { // No dispatch for this kind of value type. mlc_abort(I)::check(); @@ -229,12 +210,10 @@ namespace scribo - template <typename I, typename J> + template <typename I> mln_ch_value(I, value::int_u8) niblack_threshold(const Image<I>& input, unsigned window_size, - double K, - Image<J>& simple, - Image<J>& squared) + double K) { trace::entering("scribo::binarization::niblack_threshold"); @@ -246,11 +225,9 @@ namespace scribo output = internal::niblack_threshold_dispatch(value_t(), exact(input), window_size, - K, - exact(simple), - exact(squared)); + K); - trace::exiting("scribo::text::ppm2pbm"); + trace::exiting("scribo::binarization::niblack_threshold"); return output; } @@ -258,21 +235,6 @@ namespace scribo template <typename I> inline mln_ch_value(I, value::int_u8) - niblack_threshold(const Image<I>& input, unsigned window_size, - double K) - { - mln_ch_value(I, double) - simple = init_integral_image(input, scribo::internal::identity_), - squared = init_integral_image(input, scribo::internal::square_); - - return niblack_threshold(input, window_size, - K, simple, squared); - } - - - template <typename I> - inline - mln_ch_value(I, value::int_u8) niblack_threshold(const Image<I>& input, unsigned window_size) { return niblack_threshold(input, window_size, diff --git a/scribo/scribo/binarization/sauvola.hh b/scribo/scribo/binarization/sauvola.hh index fc3e104..ce95526 100644 --- a/scribo/scribo/binarization/sauvola.hh +++ b/scribo/scribo/binarization/sauvola.hh @@ -32,18 +32,8 @@ /// # include <mln/core/concept/image.hh> -# include <mln/data/transform.hh> -# include <mln/value/int_u8.hh> - -# include <scribo/binarization/sauvola_threshold.hh> -# include <scribo/binarization/local_threshold.hh> -# include <scribo/binarization/internal/local_threshold_debug.hh> - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG -# include <mln/io/pgm/save.hh> -# include <mln/io/pbm/save.hh> -# include <mln/data/saturate.hh> -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG +# include <scribo/binarization/internal/sauvola_functor.hh> +# include <scribo/binarization/internal/sauvola_core.hh> namespace scribo { @@ -94,67 +84,6 @@ namespace scribo # ifndef MLN_INCLUDE_ONLY - - // Implementations. - - namespace impl - { - - namespace generic - { - - template <typename I> - mln_ch_value(I, bool) - sauvola(const Image<I>& input, unsigned window_size, double K) - { - trace::entering("scribo::binarization::impl::generic::sauvola"); - mln_precondition(exact(input).is_valid()); - - mln_ch_value(I,value::int_u8) - threshold_image = binarization::sauvola_threshold(input, window_size, K); - - mln_ch_value(I, bool) - output = local_threshold(input, threshold_image); - - trace::exiting("scribo::binarization::impl::generic::sauvola"); - return output; - } - - } // end of namespace scribo::binarization::impl::generic - - - } // end of namespace scribo::binarization::impl - - - - // Dispatch - - namespace internal - { - - template <typename I> - mln_ch_value(I, bool) - sauvola_dispatch(const mln_value(I)&, - const Image<I>& input, unsigned window_size, - double K) - { - return impl::generic::sauvola(input, window_size, K); - } - - - template <typename I> - mln_ch_value(I, bool) - sauvola_dispatch(const Image<I>& input, unsigned window_size, - double K) - { - typedef mln_value(I) V; - return sauvola_dispatch(V(), input, window_size, K); - } - - } // end of namespace scribo::binarization::internal - - - // Facades template <typename I> @@ -165,30 +94,11 @@ namespace scribo mln_precondition(exact(input).is_valid()); - mln_ch_value(I, bool) - output = internal::sauvola_dispatch(input, window_size, K); - -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - if (internal::stddev_image_output) - io::pgm::save(data::saturate(value::int_u8(), internal::debug_stddev), - internal::stddev_image_output); - if (internal::mean_image_output) - io::pgm::save(data::saturate(value::int_u8(), internal::debug_mean), - internal::mean_image_output); - if (internal::threshold_image_output) - io::pgm::save(data::saturate(value::int_u8(), internal::debug_threshold), - internal::threshold_image_output); - - if (internal::alpham_image_output) - io::pgm::save(data::saturate(value::int_u8(), internal::debug_alpham), - internal::alpham_image_output); - if (internal::alphacond_image_output) - io::pbm::save(internal::debug_alphacond, internal::alphacond_image_output); -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG - + internal::sauvola_functor<I> f(input, K, SCRIBO_DEFAULT_SAUVOLA_R); + internal::sauvola_core(input, f, window_size); trace::exiting("scribo::binarization::sauvola"); - return output; + return f.output; } diff --git a/scribo/scribo/binarization/sauvola_ms.hh b/scribo/scribo/binarization/sauvola_ms.hh index 36629f9..fe9f1f0 100644 --- a/scribo/scribo/binarization/sauvola_ms.hh +++ b/scribo/scribo/binarization/sauvola_ms.hh @@ -38,6 +38,7 @@ # include <mln/core/alias/neighb2d.hh> # include <mln/data/fill.hh> +# include <mln/data/compare.hh> # include <mln/subsampling/antialiased.hh> @@ -60,17 +61,21 @@ # include <scribo/core/macros.hh> -# include <scribo/binarization/internal/first_pass_functor.hh> +# include <scribo/binarization/internal/sauvola_ms_functor.hh> # include <scribo/canvas/integral_browsing.hh> +# include <scribo/util/init_integral_image.hh> +# include <scribo/util/integral_sub_sum_sum2_functor.hh> +# include <scribo/util/compute_sub_domains.hh> + # ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG # include <scribo/binarization/internal/local_threshold_debug.hh> # include <mln/io/pgm/save.hh> # include <scribo/make/debug_filename.hh> # endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG - +# include <mln/util/timer.hh> namespace scribo { @@ -177,7 +182,7 @@ namespace scribo // 1st pass - scribo::binarization::internal::first_pass_functor< image2d<int_u8> > + scribo::binarization::internal::sauvola_ms_functor< image2d<int_u8> > f(sub, K, SCRIBO_DEFAULT_SAUVOLA_R); scribo::canvas::integral_browsing(integral_sum_sum_2, ratio, @@ -259,8 +264,9 @@ namespace scribo # ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - io::pbm::save(f.msk, - scribo::make::debug_filename(internal::threshold_image_output).c_str()); + if (internal::threshold_image_output) + io::pbm::save(f.msk, + scribo::make::debug_filename(internal::threshold_image_output).c_str()); # endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG return f.t_sub; @@ -746,50 +752,6 @@ namespace scribo return out; } - - - inline - unsigned sub(unsigned nbr, unsigned down_scaling) - { - return (nbr + down_scaling - 1) / down_scaling; - } - - // Compute domains of subsampled images and make sure they can be - // divided by 2. - template <typename I> - mln::util::array<mln::util::couple<mln_domain(I), unsigned> > - compute_sub_domains(const I& ima, unsigned n_scales, unsigned s) - { - mln::util::array<mln::util::couple<unsigned, unsigned> > n(n_scales + 2); - - n(1) = mln::make::couple(ima.nrows(), ima.ncols()); - n(2) = mln::make::couple(sub(n(1).first(), s), - sub(n(1).second(), s)); - for (unsigned i = 3; i <= n_scales + 1; ++i) - n(i) = mln::make::couple(sub(n(i - 1).first(), 2), - sub(n(i - 1).second(), 2)); - - - mln::util::array<mln::util::couple<mln_domain(I), unsigned> > out(n.size()); - out(0) = mln::make::couple(mln::make::box2d(1,1), 1u); - out(1) = mln::make::couple(mln::make::box2d(ima.nrows(), - ima.ncols()), 2u); - out(n_scales + 1) = mln::make::couple( - mln::make::box2d(n(n_scales + 1).first(), - n(n_scales + 1).second()), 1u); - - for (unsigned i = n_scales; i > 1; --i) - out(i) = mln::make::couple( - mln::make::box2d(2 * out(i + 1).first().nrows(), - 2 * out(i + 1).first().ncols()), - 2 * out(i + 1).second()); - - out(1).second() = std::max(out(2).first().ncols() * s - ima.ncols(), - out(2).first().nrows() * s - ima.nrows()); - - return out; - } - } // end of namespace scribo::binarization::internal @@ -851,7 +813,8 @@ namespace scribo } mln::util::array<mln::util::couple<box2d, unsigned> > - sub_domains = internal::compute_sub_domains(input_1, nb_subscale, s); + sub_domains = scribo::util::compute_sub_domains(input_1, + nb_subscale, s); border::adjust(input_1, sub_domains(1).second()); border::mirror(input_1); @@ -861,11 +824,23 @@ namespace scribo typedef image2d<mln::util::couple<double,double> > integral_t; integral_t integral_sum_sum_2; + mln::util::timer t; + t.start(); + // Subsampling from scale 1 to 2. - sub_ima.append(scribo::subsampling::integral(input_1, s, - integral_sum_sum_2, - sub_domains[2].first(), - sub_domains[2].second())); + { + scribo::util::integral_sub_sum_sum2_functor<I, double> + fi(s, sub_domains[2].first(), sub_domains[2].second()); + + integral_sum_sum_2 = scribo::util::init_integral_image(input_1, s, fi, + sub_domains[2].first(), + sub_domains[2].second()); + sub_ima.append(fi.sub); + } + + t.stop(); + std::cout << "1. subsampling and integral - " << t << std::endl; + t.restart(); // Subsampling to scale 3 and 4. // @@ -876,6 +851,9 @@ namespace scribo sub_domains[i].first(), sub_domains[i].second())); + t.stop(); + std::cout << "2. More subsampling - " << t << std::endl; + t.restart(); // Compute threshold images. image2d<int_u8> e_2; @@ -921,6 +899,10 @@ namespace scribo K); } + t.stop(); + std::cout << "3. Multi-scale processing - " << t << std::endl; + t.restart(); + # ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG if (internal::scale_image_output) @@ -930,15 +912,24 @@ namespace scribo // Propagate scale values. e_2 = transform::influence_zone_geodesic(e_2, c8()); -// # ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG -// if (internal::scale_image_output) -// io::pgm::save(e_2, internal::scale_image_output); -// # endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + t.stop(); + std::cout << "4. Influence Zone on Scale image - " << t << std::endl; + t.restart(); + + +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG + if (internal::scale_iz_image_output) + io::pgm::save(e_2, internal::scale_iz_image_output); +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG // Binarize image2d<bool> output = internal::multi_scale_binarization(input_1, e_2, t_ima, s); + t.stop(); + std::cout << "5. Final binarization - " << t << std::endl; + t.restart(); + trace::exiting("scribo::binarization::sauvola_ms"); return output; } diff --git a/scribo/scribo/binarization/sauvola_threshold.hh b/scribo/scribo/binarization/sauvola_threshold.hh index 7950c13..f93edae 100644 --- a/scribo/scribo/binarization/sauvola_threshold.hh +++ b/scribo/scribo/binarization/sauvola_threshold.hh @@ -40,11 +40,20 @@ # include <mln/value/int_u.hh> # include <mln/value/int_u8.hh> +# include <mln/border/mirror.hh> +# include <mln/border/adjust.hh> + # include <scribo/core/init_integral_image.hh> -# include <scribo/binarization/internal/compute_local_threshold.hh> # include <scribo/binarization/internal/sauvola_formula.hh> +# include <scribo/binarization/internal/sauvola_threshold_functor.hh> + +# include <scribo/util/init_integral_image.hh> +# include <scribo/util/integral_sum_sum2_functor.hh> +# include <scribo/util/compute_sub_domains.hh> + +# include <scribo/canvas/integral_browsing.hh> + -extern std::string prefix; namespace scribo { @@ -68,14 +77,6 @@ namespace scribo template <typename I, typename J> mln_ch_value(I, value::int_u8) sauvola_threshold(const Image<I>& input, unsigned window_size, - double K, - Image<J>& simple, - Image<J>& squared); - - /// \overload - template <typename I> - mln_ch_value(I, value::int_u8) - sauvola_threshold(const Image<I>& input, unsigned window_size, double K); /// \overload @@ -106,79 +107,67 @@ namespace scribo namespace generic { - template <typename I, typename J> + template <typename I> inline mln_concrete(I) sauvola_threshold(const Image<I>& input_, unsigned window_size, - double K, - Image<J>& simple_, - Image<J>& squared_) + double K) { trace::entering("scribo::binarization::impl::generic::sauvola_threshold"); - const I& input = exact(input_); - J& simple = exact(simple_); - J& squared = exact(squared_); + // Not implemented + mlc_abort(I)::check(); - mln_assertion(input.is_valid()); - mln_assertion(simple.is_valid()); - mln_assertion(squared.is_valid()); + typedef mln_concrete(I) output_t; + trace::exiting("scribo::binarization::impl::generic::sauvola_threshold"); + return output_t(); + } - typedef mln_value(I) V; - typedef mln_site(I) P; + } // end of namespace scribo::binarization::impl::generic -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - initialize(internal::debug_mean, input); - initialize(internal::debug_stddev, input); - initialize(internal::debug_threshold, input); - initialize(internal::debug_alpham, input); - initialize(internal::debug_alphacond, input); -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + template <typename I> + inline + mln_concrete(I) + sauvola_threshold_gl(const Image<I>& input_, unsigned window_size, + double K) + { + trace::entering("scribo::binarization::impl::sauvola_threshold_gl"); - // Sauvola Algorithm with I.I. + const I& input = exact(input_); - mln_concrete(I) output; - initialize(output, input); + mln_precondition(exact(input).is_valid()); - const mln::def::coord - nrows = static_cast<mln::def::coord>(input.nrows()), - ncols = static_cast<mln::def::coord>(input.ncols()); + // Make sure the image sizes are a multiple of 3 in each + // dimension. (browsing while binarizing relies on that + // property). + mln::util::array<mln::util::couple<box2d, unsigned> > + sub_domains = scribo::util::compute_sub_domains(input, 1, 3); - internal::sauvola_formula formula; - for(mln::def::coord row = 0; row < nrows; ++row) - for(mln::def::coord col = 0; col < ncols; ++col) - { - double t = internal::compute_local_threshold(P(row, col), simple, - squared, window_size, - K, - SCRIBO_DEFAULT_SAUVOLA_R, - formula); - mln::convert::from_to(t, output.at_(row, col)); + border::adjust(input, sub_domains(1).second()); + border::mirror(input); -# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG - internal::debug_threshold.at_(row, col) = t; -# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG - } + typedef mln::util::couple<double,double> V_i; + scribo::util::integral_sum_sum2_functor<mln_value(I), double> fi; + mln_ch_value(I,V_i) + integral = scribo::util::init_integral_image(input, 3, fi); - trace::exiting("scribo::binarization::impl::generic::sauvola_threshold"); - return output; - } - - } // end of namespace scribo::binarization::impl::generic +# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG + initialize(internal::debug_mean, input); + initialize(internal::debug_stddev, input); + initialize(internal::debug_threshold, input); + initialize(internal::debug_alpham, input); + initialize(internal::debug_alphacond, input); +# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG + internal::sauvola_threshold_functor<I> f(input, K, + SCRIBO_DEFAULT_SAUVOLA_R); + scribo::canvas::integral_browsing(integral, 1, window_size / 3, + window_size / 3, 3, f); - template <typename I, typename J> - inline - mln_concrete(I) - sauvola_threshold_gl(const I& input, unsigned window_size, - double K, - Image<J>& simple, - Image<J>& squared) - { - return impl::generic::sauvola_threshold(input, window_size, K, - simple, squared); + trace::exiting("scribo::binarization::impl::sauvola_threshold_gl"); + return f.output; } @@ -192,28 +181,23 @@ namespace scribo namespace internal { - template <unsigned n, typename I, typename J> + template <unsigned n, typename I> inline mln_ch_value(I, value::int_u<n>) sauvola_threshold_dispatch(const value::int_u<n>&, const I& input, unsigned window_size, - double K, - J& simple, - J& squared) + double K) { - return impl::sauvola_threshold_gl(input, window_size, K, - simple, squared); + return impl::sauvola_threshold_gl(input, window_size, K); } - template <typename I, typename J> + template <typename I> inline mln_ch_value(I, value::int_u8) sauvola_threshold_dispatch(const mln_value(I)&, const I& input, unsigned window_size, - double K, - J& simple, - J& squared) + double K) { // No dispatch for this kind of value type. mlc_abort(I)::check(); @@ -227,12 +211,10 @@ namespace scribo - template <typename I, typename J> + template <typename I> mln_ch_value(I, value::int_u8) sauvola_threshold(const Image<I>& input, unsigned window_size, - double K, - Image<J>& simple, - Image<J>& squared) + double K) { trace::entering("scribo::binarization::sauvola_threshold"); @@ -244,11 +226,9 @@ namespace scribo output = internal::sauvola_threshold_dispatch(value_t(), exact(input), window_size, - K, - exact(simple), - exact(squared)); + K); - trace::exiting("scribo::text::ppm2pbm"); + trace::exiting("scribo::binarization::sauvola_threshold"); return output; } @@ -256,24 +236,6 @@ namespace scribo template <typename I> inline mln_ch_value(I, value::int_u8) - sauvola_threshold(const Image<I>& input, unsigned window_size, - double K) - { - mln_ch_value(I, double) - simple = init_integral_image(input, scribo::internal::identity_), - squared = init_integral_image(input, scribo::internal::square_); - - io::dump::save(simple, prefix + "simple.dump"); - io::dump::save(squared, prefix + "squared.dump"); - - return sauvola_threshold(input, window_size, - K, simple, squared); - } - - - template <typename I> - inline - mln_ch_value(I, value::int_u8) sauvola_threshold(const Image<I>& input, unsigned window_size) { return sauvola_threshold(input, window_size, diff --git a/scribo/scribo/canvas/integral_browsing.hh b/scribo/scribo/canvas/integral_browsing.hh index 0ec3d83..89e0019 100644 --- a/scribo/scribo/canvas/integral_browsing.hh +++ b/scribo/scribo/canvas/integral_browsing.hh @@ -210,7 +210,7 @@ namespace scribo delta_size_tr += step_2; size_tr_start += delta_start_right; d_tr_start += offset_down; - + functor.end_of_row(row); } @@ -307,6 +307,7 @@ namespace scribo b_mr_start += offset_down; d_mr_start += offset_down; + functor.end_of_row(row); } @@ -404,6 +405,7 @@ namespace scribo delta_size_br -= step_2; size_br_start -= delta_start_right; b_br_start += offset_down; + functor.end_of_row(row); } functor.finalize(); diff --git a/scribo/scribo/util/compute_sub_domains.hh b/scribo/scribo/util/compute_sub_domains.hh new file mode 100644 index 0000000..e81a88f --- /dev/null +++ b/scribo/scribo/util/compute_sub_domains.hh @@ -0,0 +1,110 @@ +// Copyright (C) 2011 EPITA Research and Development Laboratory (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#ifndef SCRIBO_UTIL_COMPUTE_SUB_DOMAINS_HH +# define SCRIBO_UTIL_COMPUTE_SUB_DOMAINS_HH + +/// \file +/// +/// + +# include <mln/core/image/image2d.hh> + +namespace scribo +{ + + namespace util + { + + using namespace mln; + + template <typename I> + mln::util::array<mln::util::couple<mln_domain(I), unsigned> > + compute_sub_domains(const Image<I>& ima, unsigned n_scales, unsigned s); + + +# ifndef MLN_INCLUDE_ONLY + + namespace internal + { + + inline + unsigned sub(unsigned nbr, unsigned down_scaling) + { + return (nbr + down_scaling - 1) / down_scaling; + } + + } // end of namespace scribo::util::internal + + + + template <typename I> + mln::util::array<mln::util::couple<mln_domain(I), unsigned> > + compute_sub_domains(const Image<I>& ima_, unsigned n_scales, unsigned s) + { + trace::entering("scribo::util::compute_sub_domains"); + + const I& ima = exact(ima_); + mln_precondition(ima.is_valid()); + + mln::util::array<mln::util::couple<unsigned, unsigned> > n(n_scales + 2); + + n(1) = mln::make::couple(ima.nrows(), ima.ncols()); + n(2) = mln::make::couple(internal::sub(n(1).first(), s), + internal::sub(n(1).second(), s)); + for (unsigned i = 3; i <= n_scales + 1; ++i) + n(i) = mln::make::couple(internal::sub(n(i - 1).first(), 2), + internal::sub(n(i - 1).second(), 2)); + + + mln::util::array<mln::util::couple<mln_domain(I), unsigned> > out(n.size()); + out(0) = mln::make::couple(mln::make::box2d(1,1), 1u); + out(1) = mln::make::couple(mln::make::box2d(ima.nrows(), + ima.ncols()), 2u); + out(n_scales + 1) = mln::make::couple( + mln::make::box2d(n(n_scales + 1).first(), + n(n_scales + 1).second()), 1u); + + for (unsigned i = n_scales; i > 1; --i) + out(i) = mln::make::couple( + mln::make::box2d(2 * out(i + 1).first().nrows(), + 2 * out(i + 1).first().ncols()), + 2 * out(i + 1).second()); + + out(1).second() = std::max(out(2).first().ncols() * s - ima.ncols(), + out(2).first().nrows() * s - ima.nrows()); + + trace::exiting("scribo::util::compute_sub_domains"); + return out; + } + +# endif // ! MLN_INCLUDE_ONLY + + } // end of namespace scribo::util + +} // end of namespace scribo + +#endif // ! SCRIBO_UTIL_COMPUTE_SUB_DOMAINS_HH + diff --git a/scribo/scribo/util/init_integral_image.hh b/scribo/scribo/util/init_integral_image.hh new file mode 100644 index 0000000..19d914d --- /dev/null +++ b/scribo/scribo/util/init_integral_image.hh @@ -0,0 +1,264 @@ +// Copyright (C) 2009, 2010, 2011 EPITA Research and Development +// Laboratory (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#ifndef SCRIBO_UTIL_INIT_INTEGRAL_IMAGE_HH +# define SCRIBO_UTIL_INIT_INTEGRAL_IMAGE_HH + +/// \file +/// +/// + +# include <mln/core/image/image2d.hh> + +namespace scribo +{ + + namespace util + { + + using namespace mln; + + + template <typename I, typename F> + mln_ch_value(I, mln_result(F)) + init_integral_image(const Image<I>& input, unsigned scale, F& func); + + template <typename I, typename F> + mln_ch_value(I, mln_result(F)) + init_integral_image(const Image<I>& input, unsigned scale, F& func, + unsigned border); + + +# ifndef MLN_INCLUDE_ONLY + + namespace impl + { + + namespace generic + { + + template <typename I, typename F> + mln_ch_value(I, mln_result(F)) + init_integral_image(const Image<I>& input, unsigned scale, F& f, + const mln_box(I)& output_domain, unsigned border) + { + trace::entering("scribo::util::impl::generic::init_integral_image"); + typedef mln_ch_value(I, mln_result(F)) J; + + // Not implemented + mlc_abort(I)::check(); + + trace::exiting("scribo::util::impl::generic::init_integral_image"); + return J(); + } + + } // end of namespace scribo::util::impl::generic + + + template <typename I, typename F> + mln_ch_value(I, mln_result(F)) + init_integral_image_fastest(const Image<I>& input_, unsigned scale, F& f, + const mln_box(I)& output_domain, unsigned border) + { + trace::entering("scribo::util::impl::init_integral_image_fastest"); + + const I& input = exact(input_); + typedef mln_ch_value(I, mln_result(F)) J; + + mln_precondition(input.is_valid()); + mln_precondition(input.domain().pmin() == literal::origin); + + typedef mln_value(I) V; + typedef mln_sum(V) S; + typedef mln_value(J) V2; + typedef mln_site(I) P; + + J integral_sum_sum_2(output_domain, border); + V2* p_integ = integral_sum_sum_2.buffer(); + + const int up = integral_sum_sum_2.delta_index(dpoint2d(-1, 0)); + + const unsigned nrows = scale * integral_sum_sum_2.nrows(); + const unsigned ncols = scale * integral_sum_sum_2.ncols(); + + unsigned row = 0; + + unsigned b_offset = integral_sum_sum_2.delta_index(dpoint2d(border, + border)); + + // First row (special case) + p_integ += b_offset; + { + f.begin_of_first_row(); // <---- begin_of_first_row() + + mln::util::array<const V*> ptr(scale); + for (unsigned s = 0; s < scale; ++s) + ptr[s] = & input.at_(row + s, 0); + + for (unsigned col = 0; col < ncols; col += scale) + { + f.begin_of_col(); // <---- begin_of_col() + + for (unsigned s = 0; s < scale; ++s) + for (unsigned i = 0; i < scale; ++i) + f.take(*(ptr[s]++)); // <---- take() + + // exception + *p_integ = f.to_result_first_row(); // <---- to_result_first_row() + + f.end_of_col(); // <---- end_of_col() + ++p_integ; + } + f.end_of_row(); // <---- end_of_row() + } + + unsigned b_next = 2 * border; + + // Other rows (general case) + p_integ += b_next; + for (row += scale; row < nrows; row += scale) + { + f.begin_of_row(); // <---- begin_of_row() + + mln::util::array<const V*> ptr(scale); + for (unsigned s = 0; s < scale; ++s) + ptr[s] = & input.at_(row + s, 0); + + for (unsigned col = 0; col < ncols; col += scale) + { + f.begin_of_col(); // <---- begin_of_col() + + for (unsigned s = 0; s < scale; ++s) + for (unsigned i = 0; i < scale; ++i) + f.take(*(ptr[s]++)); // <---- take() + + *p_integ = f.to_result(*(p_integ + up)); // <---- to_result() + + f.end_of_col(); // <---- end_of_col() + ++p_integ; + } + f.end_of_row(); // <---- end_of_row() + p_integ += b_next; + } + + trace::exiting("scribo::util::impl::init_integral_fastest_image"); + return integral_sum_sum_2; + } + + } // end of namespace scribo::util::impl + + + // Dispatch + + namespace internal + { + + template <typename I, typename F> + mln_ch_value(I,mln_result(F)) + init_integral_image_dispatch(mln::trait::image::speed::fastest, + const Image<I>& input, unsigned scale, + F& func, const mln_box(I)& output_domain, + unsigned border) + { + return impl::init_integral_image_fastest(input, scale, func, + output_domain, border); + } + + + template <typename I, typename F> + mln_ch_value(I,mln_result(F)) + init_integral_image_dispatch(mln::trait::image::speed::any, + const Image<I>& input, unsigned scale, + F& func, const mln_box(I)& output_domain, + unsigned border) + { + return impl::generic::init_integral_image(input, scale, func, + output_domain, border); + } + + + template <typename I, typename F> + mln_ch_value(I,mln_result(F)) + init_integral_image_dispatch(const Image<I>& input, unsigned scale, F& func, + const mln_box(I)& output_domain, unsigned border) + { + return init_integral_image_dispatch(mln_trait_image_speed(I)(), + input, scale, func, output_domain, border); + } + + } // end of namespace scribo::util::internal + + + // Facade + + template <typename I, typename F> + mln_ch_value(I,mln_result(F)) + init_integral_image(const Image<I>& input_, unsigned scale, F& func, + const mln_box(I)& output_domain, unsigned border) + { + trace::entering("scribo::util::init_integral_image"); + + const I& input = exact(input_); + mln_precondition(input.is_valid()); + mln_precondition(input.domain().pmin() == literal::origin); + + mln_ch_value(I,mln_result(F)) + output = internal::init_integral_image_dispatch(input, scale, func, + output_domain, border); + + trace::exiting("scribo::util::init_integral_image"); + return output; + } + + template <typename I, typename F> + mln_ch_value(I,mln_result(F)) + init_integral_image(const Image<I>& input_, unsigned scale, F& func) + { + trace::entering("scribo::util::init_integral_image"); + + const I& input = exact(input_); + mln_precondition(input.is_valid()); + mln_precondition(input.domain().pmin() == literal::origin); + + box<mln_site(I)> + output_domain = mln::make::box2d((input.nrows() + scale - 1) / scale, + (input.ncols() + scale - 1) / scale); + + mln_ch_value(I,mln_result(F)) + output = init_integral_image(input, scale, func, + output_domain, exact(input).border()); + + trace::exiting("scribo::util::init_integral_image"); + return output; + } + +#endif // ! MLN_INCLUDE_ONLY + + } // end of namespace scribo::util + +} // end of namespace scribo + +#endif // ! SCRIBO_UTIL_INIT_INTEGRAL_IMAGE_HH diff --git a/scribo/scribo/util/integral_sub_sum_sum2_functor.hh b/scribo/scribo/util/integral_sub_sum_sum2_functor.hh new file mode 100644 index 0000000..1c312b7 --- /dev/null +++ b/scribo/scribo/util/integral_sub_sum_sum2_functor.hh @@ -0,0 +1,193 @@ +// Copyright (C) 2011 EPITA Research and Development Laboratory (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#ifndef SCRIBO_UTIL_INTEGRAL_SUB_SUM_SUM2_FUNCTOR_HH +# define SCRIBO_UTIL_INTEGRAL_SUB_SUM_SUM2_FUNCTOR_HH + +/// \file +/// +/// + +# include <mln/util/couple.hh> + +namespace scribo +{ + + namespace util + { + + template <typename I, typename S = mln_sum(mln_value(I))> + class integral_sub_sum_sum2_functor + { + typedef mln_concrete(I) J; + typedef mln_value(I) V; + + public: + typedef mln::util::couple<S, S> result; + + integral_sub_sum_sum2_functor(const I& ima, unsigned scale); + integral_sub_sum_sum2_functor(unsigned scale, + const mln_box(I)& output_domain, + unsigned border); + + + void take(const V& v); + + result to_result_first_row() const; + result to_result(const result& up_result) const; + + void begin_of_col(); + void end_of_col(); + + void begin_of_row(); + void begin_of_first_row(); + void end_of_row(); + + J sub; + + private: + void init_(unsigned scale, const mln_box(I)& b, unsigned border); + + S local_sum; + S h_sum; + S h_sum_2; + + V *p_sub; + unsigned b_next; + unsigned area; + }; + + +# ifndef MLN_INCLUDE_ONLY + + template <typename I, typename S> + integral_sub_sum_sum2_functor<I,S>::integral_sub_sum_sum2_functor(unsigned scale, + const mln_box(I)& output_domain, + unsigned border) + { + init_(scale, output_domain, border); + } + + template <typename I, typename S> + integral_sub_sum_sum2_functor<I,S>::integral_sub_sum_sum2_functor(const I& ima, + unsigned scale) + { + box<mln_site(I)> + b = mln::make::box2d((ima.nrows() + scale - 1) / scale, + (ima.ncols() + scale - 1) / scale); + init_(scale, b, ima.border()); + } + + template <typename I, typename S> + void + integral_sub_sum_sum2_functor<I,S>::init_(unsigned scale, const mln_box(I)& b, + unsigned border) + { + sub = J(b, border); + + area = scale * scale; + + b_next = 2 * border; + unsigned b_offset = sub.delta_index(dpoint2d(border, border)); + p_sub = sub.buffer() + b_offset; + } + + template <typename I, typename S> + inline + void + integral_sub_sum_sum2_functor<I,S>::begin_of_first_row() + { + h_sum = 0; + h_sum_2 = 0; + } + + template <typename I, typename S> + inline + void + integral_sub_sum_sum2_functor<I,S>::begin_of_row() + { + h_sum = 0; + h_sum_2 = 0; + } + + template <typename I, typename S> + inline + void + integral_sub_sum_sum2_functor<I,S>::take(const V& v) + { + local_sum += v; + h_sum_2 += v * v; + } + + template <typename I, typename S> + inline + typename integral_sub_sum_sum2_functor<I,S>::result + integral_sub_sum_sum2_functor<I,S>::to_result_first_row() const + { + return result(h_sum + local_sum, h_sum_2); + } + + template <typename I, typename S> + inline + typename integral_sub_sum_sum2_functor<I,S>::result + integral_sub_sum_sum2_functor<I,S>::to_result(const result& up_result) const + { + return result(h_sum + local_sum + up_result.first(), + h_sum_2 + up_result.second()); + } + + template <typename I, typename S> + inline + void + integral_sub_sum_sum2_functor<I,S>::begin_of_col() + { + local_sum = 0; + } + + template <typename I, typename S> + inline + void + integral_sub_sum_sum2_functor<I,S>::end_of_col() + { + mln::convert::from_to(local_sum / area, *p_sub++); + h_sum += local_sum; + } + + template <typename I, typename S> + inline + void + integral_sub_sum_sum2_functor<I,S>::end_of_row() + { + p_sub += b_next; + } + + +#endif // ! MLN_INCLUDE_ONLY + + } // end of namespace scribo::util + +} // end of namespace scribo + +#endif // ! SCRIBO_UTIL_INTEGRAL_SUB_SUM_SUM2_FUNCTOR_HH diff --git a/scribo/scribo/util/integral_sum_sum2_functor.hh b/scribo/scribo/util/integral_sum_sum2_functor.hh new file mode 100644 index 0000000..c166532 --- /dev/null +++ b/scribo/scribo/util/integral_sum_sum2_functor.hh @@ -0,0 +1,140 @@ +// Copyright (C) 2011 EPITA Research and Development Laboratory (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#ifndef SCRIBO_UTIL_INTEGRAL_SUM_SUM2_FUNCTOR_HH +# define SCRIBO_UTIL_INTEGRAL_SUM_SUM2_FUNCTOR_HH + +/// \file +/// +/// + +# include <mln/util/couple.hh> + +namespace scribo +{ + + namespace util + { + + template <typename V, typename S = mln_sum(V)> + class integral_sum_sum2_functor + { + public: + typedef mln::util::couple<S, S> result; + + void begin_of_first_row(); + void begin_of_row(); + void end_of_row(); + + void begin_of_col(); + void end_of_col(); + + + void take(const V& v); + + result to_result_first_row() const; + result to_result(const result& up_result) const; + + private: + S h_sum; + S h_sum_2; + }; + + +# ifndef MLN_INCLUDE_ONLY + + template <typename V, typename S> + inline + void + integral_sum_sum2_functor<V,S>::begin_of_first_row() + { + h_sum = 0; + h_sum_2 = 0; + } + + template <typename V, typename S> + inline + void + integral_sum_sum2_functor<V,S>::begin_of_row() + { + h_sum = 0; + h_sum_2 = 0; + } + + template <typename V, typename S> + inline + void + integral_sum_sum2_functor<V,S>::take(const V& v) + { + h_sum += v; + h_sum_2 += v * v; + } + + template <typename V, typename S> + inline + typename integral_sum_sum2_functor<V,S>::result + integral_sum_sum2_functor<V,S>::to_result_first_row() const + { + return result(h_sum, h_sum_2); + } + + template <typename V, typename S> + inline + typename integral_sum_sum2_functor<V,S>::result + integral_sum_sum2_functor<V,S>::to_result(const result& up_result) const + { + return result(h_sum + up_result.first(), + h_sum_2 + up_result.second()); + } + + template <typename V, typename S> + inline + void + integral_sum_sum2_functor<V,S>::begin_of_col() + { + } + + template <typename V, typename S> + inline + void + integral_sum_sum2_functor<V,S>::end_of_col() + { + } + + + template <typename V, typename S> + inline + void + integral_sum_sum2_functor<V,S>::end_of_row() + { + } + +#endif // ! MLN_INCLUDE_ONLY + + } // end of namespace scribo::util + +} // end of namespace scribo + +#endif // ! SCRIBO_UTIL_INTEGRAL_SUM_SUM2_FUNCTOR_HH diff --git a/scribo/src/binarization/pgm_niblack_threshold_image.cc b/scribo/src/binarization/pgm_niblack_threshold_image.cc new file mode 100644 index 0000000..4abd862 --- /dev/null +++ b/scribo/src/binarization/pgm_niblack_threshold_image.cc @@ -0,0 +1,78 @@ +// Copyright (C) 2010, 2011 EPITA Research and Development Laboratory +// (LRDE) +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +#include <mln/io/pgm/all.hh> + +#include <scribo/binarization/niblack_threshold.hh> +#include <scribo/debug/usage.hh> + +const char *args_desc[][2] = +{ + { "input.pgm", "A graylevel image." }, + { "output.pgm", "A threshold image." }, + { "w", "Window size (default 101)." }, + { "k", "Niblack's formulae parameter (default -0.2)." }, + {0, 0} +}; + + +int main(int argc, char *argv[]) +{ + using namespace mln; + + if (argc != 5 && argc != 4 && argc != 3) + return scribo::debug::usage(argv, + "Compute a threshold image based on Niblack's algorithm.", + "input.pgm output.pgm <w> <k>", + args_desc); + + trace::entering("main"); + + unsigned w; + if (argc >= 4) + w = atoi(argv[3]); + else + w = 101; + + double k; + if (argc >= 5) + k = atof(argv[4]); + else + k = -0.2; + + std::cout << "Using w=" << w << " and k=" << k << std::endl; + + image2d<value::int_u8> input; + io::pgm::load(input, argv[1]); + + image2d<value::int_u8> out = scribo::binarization::niblack_threshold(input, w, k); + + + io::pgm::save(out, argv[2]); + + + trace::exiting("main"); +} diff --git a/scribo/src/binarization/sauvola_ms.cc b/scribo/src/binarization/sauvola_ms.cc index d98b83f..77f839d 100644 --- a/scribo/src/binarization/sauvola_ms.cc +++ b/scribo/src/binarization/sauvola_ms.cc @@ -32,12 +32,12 @@ #include <mln/io/pbm/save.hh> #include <mln/data/transform.hh> #include <mln/fun/v2v/rgb_to_luma.hh> +#include <mln/util/timer.hh> #include <scribo/binarization/sauvola_ms.hh> #include <scribo/debug/option_parser.hh> #include <scribo/debug/logger.hh> - static const scribo::debug::arg_data arg_desc[] = { { "input.*", "An image." }, @@ -115,9 +115,18 @@ int main(int argc, char *argv[]) input_1_gl = data::transform(input_1, mln::fun::v2v::rgb_to_luma<value::int_u8>()); + mln::util::timer t; + t.start(); + // Binarize image2d<bool> output = scribo::binarization::sauvola_ms(input_1_gl, w_1, s, k); + if (verbose) + { + t.stop(); + std::cout << "binarized in " << t << "s" << std::endl; + } + io::pbm::save(output, options.arg("output.pbm")); } diff --git a/scribo/src/binarization/sauvola_ms_debug.cc b/scribo/src/binarization/sauvola_ms_debug.cc index 70c1a9a..839a3f7 100644 --- a/scribo/src/binarization/sauvola_ms_debug.cc +++ b/scribo/src/binarization/sauvola_ms_debug.cc @@ -91,6 +91,8 @@ int main(int argc, char *argv[]) "input.* output.pbm <scale.pgm> <w> <s> <k>", args_desc); + Magick::InitializeMagick(0); + trace::entering("main"); // Window size @@ -116,6 +118,9 @@ int main(int argc, char *argv[]) if (argc >= 4) scribo::binarization::internal::scale_image_output = argv[3]; + scribo::binarization::internal::scale_image_output = "scale_image.pgm"; + scribo::binarization::internal::threshold_image_output = "threshold_image.pbm"; + scribo::binarization::internal::scale_iz_image_output = "scale_iz.pgm"; // Load image2d<value::rgb8> input_1; -- 1.7.2.5
participants (1)
-
Guillaume Lazzara