* scribo/binarization/all.hh: Update includes.
* scribo/binarization/internal/compute_local_threshold.hh,
* scribo/binarization/internal/compute_sauvola_threshold.hh,
* scribo/binarization/internal/first_pass_functor.hh,
* scribo/binarization/internal/local_threshold_debug.hh,
* scribo/binarization/internal/sauvola_debug.hh,
* scribo/binarization/internal/sauvola_formula.hh,
* scribo/binarization/sauvola.hh,
* scribo/binarization/sauvola_ms.hh,
* scribo/binarization/sauvola_threshold.hh,
* scribo/binarization/sauvola_threshold_image.hh: Revamp code in
order to share some parts with Niblack's algorithm.
* scribo/binarization/internal/niblack_formula.hh,
* scribo/binarization/niblack.hh,
* scribo/binarization/niblack_threshold.hh: New.
* src/binarization/Makefile.am
* tests/binarization/Makefile.am: Add new targets.
* src/binarization/niblack.cc: New tool.
* tests/binarization/niblack.cc: New test.
* tests/binarization/niblack.ref.pbm: New test data.
---
scribo/ChangeLog | 31 ++
scribo/scribo/binarization/all.hh | 5 +-
.../internal/compute_local_threshold.hh | 224 +++++++++++++++
.../internal/compute_sauvola_threshold.hh | 285 ------------------
.../binarization/internal/first_pass_functor.hh | 24 +-
.../binarization/internal/local_threshold_debug.hh | 88 ++++++
.../binarization/internal/niblack_formula.hh | 105 +++++++
.../scribo/binarization/internal/sauvola_debug.hh | 87 ------
.../binarization/internal/sauvola_formula.hh | 121 ++++++++
scribo/scribo/binarization/niblack.hh | 218 ++++++++++++++
scribo/scribo/binarization/niblack_threshold.hh | 299 +++++++++++++++++++
scribo/scribo/binarization/sauvola.hh | 24 +-
scribo/scribo/binarization/sauvola_ms.hh | 21 +-
scribo/scribo/binarization/sauvola_threshold.hh | 296 +++++++++++++++++++
.../scribo/binarization/sauvola_threshold_image.hh | 301 --------------------
scribo/src/binarization/Makefile.am | 12 +-
scribo/src/binarization/niblack.cc | 106 +++++++
.../binarization/pgm_sauvola_threshold_image.cc | 7 +-
scribo/tests/binarization/Makefile.am | 3 +
scribo/tests/binarization/niblack.cc | 52 ++++
scribo/tests/binarization/niblack.ref.pbm | Bin 0 -> 32884 bytes
21 files changed, 1597 insertions(+), 712 deletions(-)
create mode 100644 scribo/scribo/binarization/internal/compute_local_threshold.hh
delete mode 100644 scribo/scribo/binarization/internal/compute_sauvola_threshold.hh
create mode 100644 scribo/scribo/binarization/internal/local_threshold_debug.hh
create mode 100644 scribo/scribo/binarization/internal/niblack_formula.hh
delete mode 100644 scribo/scribo/binarization/internal/sauvola_debug.hh
create mode 100644 scribo/scribo/binarization/internal/sauvola_formula.hh
create mode 100644 scribo/scribo/binarization/niblack.hh
create mode 100644 scribo/scribo/binarization/niblack_threshold.hh
create mode 100644 scribo/scribo/binarization/sauvola_threshold.hh
delete mode 100644 scribo/scribo/binarization/sauvola_threshold_image.hh
create mode 100644 scribo/src/binarization/niblack.cc
create mode 100644 scribo/tests/binarization/niblack.cc
create mode 100644 scribo/tests/binarization/niblack.ref.pbm
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index 5aa2a06..0923b28 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,3 +1,34 @@
+2011-10-17 Guillaume Lazzara <z(a)lrde.epita.fr>
+
+ Add Niblack's binarization algorithm.
+
+ * scribo/binarization/all.hh: Update includes.
+
+ * scribo/binarization/internal/compute_local_threshold.hh,
+ * scribo/binarization/internal/compute_sauvola_threshold.hh,
+ * scribo/binarization/internal/first_pass_functor.hh,
+ * scribo/binarization/internal/local_threshold_debug.hh,
+ * scribo/binarization/internal/sauvola_debug.hh,
+ * scribo/binarization/internal/sauvola_formula.hh,
+ * scribo/binarization/sauvola.hh,
+ * scribo/binarization/sauvola_ms.hh,
+ * scribo/binarization/sauvola_threshold.hh,
+ * scribo/binarization/sauvola_threshold_image.hh: Revamp code in
+ order to share some parts with Niblack's algorithm.
+
+ * scribo/binarization/internal/niblack_formula.hh,
+ * scribo/binarization/niblack.hh,
+ * scribo/binarization/niblack_threshold.hh: New.
+
+ * src/binarization/Makefile.am
+ * tests/binarization/Makefile.am: Add new targets.
+
+ * src/binarization/niblack.cc: New tool.
+
+ * tests/binarization/niblack.cc: New test.
+
+ * tests/binarization/niblack.ref.pbm: New test data.
+
2011-10-14 Guillaume Lazzara <z(a)lrde.epita.fr>
Add a new tool for global thresholding.
diff --git a/scribo/scribo/binarization/all.hh b/scribo/scribo/binarization/all.hh
index 6f40505..5530861 100644
--- a/scribo/scribo/binarization/all.hh
+++ b/scribo/scribo/binarization/all.hh
@@ -1,4 +1,5 @@
-// Copyright (C) 2010 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2010, 2011 EPITA Research and Development Laboratory
+// (LRDE)
//
// This file is part of Olena.
//
@@ -50,6 +51,6 @@ namespace scribo
# include <scribo/binarization/sauvola.hh>
# include <scribo/binarization/sauvola_ms.hh>
# include <scribo/binarization/sauvola_ms_split.hh>
-# include <scribo/binarization/sauvola_threshold_image.hh>
+# include <scribo/binarization/sauvola_threshold.hh>
#endif // ! SCRIBO_BINARIZATION_ALL_HH
diff --git a/scribo/scribo/binarization/internal/compute_local_threshold.hh
b/scribo/scribo/binarization/internal/compute_local_threshold.hh
new file mode 100644
index 0000000..147ef0f
--- /dev/null
+++ b/scribo/scribo/binarization/internal/compute_local_threshold.hh
@@ -0,0 +1,224 @@
+// 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;
+
+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));
+
+ 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));
+
+ 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(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/compute_sauvola_threshold.hh
b/scribo/scribo/binarization/internal/compute_sauvola_threshold.hh
deleted file mode 100644
index d3ca07f..0000000
--- a/scribo/scribo/binarization/internal/compute_sauvola_threshold.hh
+++ /dev/null
@@ -1,285 +0,0 @@
-// Copyright (C) 2010 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_SAUVOLA_THRESHOLD_HH
-# define SCRIBO_BINARIZATION_INTERNAL_COMPUTE_SAUVOLA_THRESHOLD_HH
-
-
-/// \file
-///
-/// \brief Compute a threshold with Sauvola's binarization formula.
-
-# include <algorithm>
-# include <cmath>
-
-# include <mln/core/image/image2d.hh>
-# include <mln/value/int_u8.hh>
-
-# include <scribo/binarization/internal/sauvola_debug.hh>
-
-
-// Setup default Sauvola's formulae parameters values.
-// These macros may be used in other variant of Sauvola's algorithm.
-//
-// Values are set according to the following reference: "Automatic
-// Evaluation of Document Binarization Results", Badekas and al, 2005
-//
-// Badekas et al. said 0.34 was best.
-# define SCRIBO_DEFAULT_SAUVOLA_K 0.34
-//
-// 128 is best for grayscale documents.
-# define SCRIBO_DEFAULT_SAUVOLA_R 128
-
-
-namespace scribo
-{
-
- namespace binarization
- {
-
- namespace internal
- {
-
- using namespace mln;
-
-
- /*! \brief Compute a point wise threshold according Sauvola's
- binarization.
-
- \param[in] p A site.
- \param[in] simple An integral image of mean values.
- \param[in] squared An integral image of squared mean values.
- \param[in] win_width Window width.
- \param[in] k Control the threshold value in the local
- window. The higher, the lower the threshold
- form the local mean m(x, y).
- \param[in] R Maximum value of the standard deviation (128
- for grayscale documents).
-
- \return A threshold.
- */
- template <typename P, typename J>
- double
- compute_sauvola_threshold(const P& p,
- const J& simple,
- const J& squared,
- int win_width, double K, double R);
-
- /// \overload
- /// K is set to 0.34 and R to 128.
- //
- template <typename P, typename J>
- double
- compute_sauvola_threshold(const P& p,
- const J& simple,
- const J& squared,
- int win_width);
-
-
-
-# ifndef MLN_INCLUDE_ONLY
-
-
-
- /*! \brief compute Sauvola's threshold applying directly the formula.
-
- \param[in] m_x_y Mean value.
- \param[in] s_x_y Standard deviation.
- \param[in] k Control the threshold value in the local
- window. The higher, the lower the threshold
- form the local mean m(x, y).
- \param[in] R Maximum value of the standard deviation (128
- for grayscale documents).
-
- \return A threshold.
- */
- inline
- double
- sauvola_threshold_formula(const double m_x_y, const double s_x_y,
- const double K, const double R)
- {
- return m_x_y * (1.0 + K * ((s_x_y / R) - 1.0));
- }
-
- /// \overload
- /// K is set to 0.34 and R to 128.
- //
- inline
- double
- sauvola_threshold_formula(double m_x_y, double s_x_y)
- {
- return sauvola_threshold_formula(m_x_y, s_x_y,
- SCRIBO_DEFAULT_SAUVOLA_K,
- SCRIBO_DEFAULT_SAUVOLA_R);
- }
-
-
-
- template <typename P, typename J>
- double
- compute_sauvola_threshold(const P& p,
- const J& simple,
- const J& squared,
- int win_width, double K, double R)
- {
- 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));
-
- double m_x_y = m_x_y_tmp / wh;
-
-# ifdef SCRIBO_SAUVOLA_DEBUG
- // Store local mean
- debug_mean(p) = m_x_y * mean_debug_factor;
-# endif // ! SCRIBO_SAUVOLA_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));
-
- double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f));
-
-# ifdef SCRIBO_SAUVOLA_DEBUG
- // Store local standard deviation
- debug_stddev(p) = s_x_y * stddev_debug_factor;
-# endif // ! SCRIBO_SAUVOLA_DEBUG
-
- // Thresholding.
- double t_x_y = sauvola_threshold_formula(m_x_y, s_x_y, K, R);
-
-# ifdef SCRIBO_SAUVOLA_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_SAUVOLA_DEBUG
-
- return t_x_y;
- }
-
-
- template <typename P, typename J>
- double
- compute_sauvola_threshold_single_image(const P& p,
- const J& integral,
- int win_width,
- double K, double R)
- {
- // 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_SAUVOLA_DEBUG
- // Store local mean
- debug_mean(p) = m_x_y * mean_debug_factor;
-# endif // ! SCRIBO_SAUVOLA_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_SAUVOLA_DEBUG
- // Store local standard deviation
- debug_stddev(p) = s_x_y * stddev_debug_factor;
-# endif // !SCRIBO_SAUVOLA_DEBUG
-
- // Thresholding.
- double t_x_y = sauvola_threshold_formula(m_x_y, s_x_y, K, R);
-
-# ifdef SCRIBO_SAUVOLA_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_SAUVOLA_DEBUG
-
- return t_x_y;
- }
-
-
-
- template <typename P, typename J>
- double
- compute_sauvola_threshold(const P& p,
- const J& simple,
- const J& squared,
- int win_width)
- {
- return compute_sauvola_threshold(p, simple, squared, win_width,
- SCRIBO_DEFAULT_SAUVOLA_K,
- SCRIBO_DEFAULT_SAUVOLA_R);
- }
-
-
-#endif // ! MLN_INCLUDE_ONLY
-
- } // end of namespace scribo::binarization::internal
-
- } // end of namespace scribo::binarization
-
-} // end of namespace scribo
-
-#endif // ! SCRIBO_BINARIZATION_INTERNAL_COMPUTE_SAUVOLA_THRESHOLD_HH
diff --git a/scribo/scribo/binarization/internal/first_pass_functor.hh
b/scribo/scribo/binarization/internal/first_pass_functor.hh
index 0b1a7ac..8da401b 100644
--- a/scribo/scribo/binarization/internal/first_pass_functor.hh
+++ b/scribo/scribo/binarization/internal/first_pass_functor.hh
@@ -37,7 +37,11 @@
# include <mln/value/int_u8.hh>
# include <mln/data/fill.hh>
-# include <scribo/binarization/sauvola_threshold_image.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
@@ -67,8 +71,11 @@ namespace scribo
mln::util::array<int> dp;
double K_;
+ double R_;
+
+ sauvola_formula formula_;
- first_pass_functor(const I& input, double K);
+ first_pass_functor(const I& input, double K, double R);
void exec(double mean, double stddev);
void finalize();
@@ -88,10 +95,11 @@ namespace scribo
template <typename I>
- first_pass_functor<I>::first_pass_functor(const I& input, double K)
+ first_pass_functor<I>::first_pass_functor(const I& input, double K,
double R)
: input(input),
pxl(input),
- K_(K)
+ K_(K),
+ R_(R)
{
res = 0;
pxl.start();
@@ -100,10 +108,10 @@ namespace scribo
initialize(parent, input);
initialize(msk, input);
-# ifdef SCRIBO_SAUVOLA_DEBUG
+# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG
initialize(debug_mean, input);
initialize(debug_stddev, input);
-# endif // ! SCRIBO_SAUVOLA_DEBUG
+# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG
mln::extension::fill(msk, false);
@@ -124,9 +132,7 @@ namespace scribo
unsigned p = pxl.offset();
value::int_u8 t_p;
- mln::convert::from_to(sauvola_threshold_formula(mean, stddev,
- K_,
- SCRIBO_DEFAULT_SAUVOLA_R),
+ mln::convert::from_to(formula_(mean, stddev, K_, R_),
t_p);
msk.element(p) = input.element(p) < t_p;
diff --git a/scribo/scribo/binarization/internal/local_threshold_debug.hh
b/scribo/scribo/binarization/internal/local_threshold_debug.hh
new file mode 100644
index 0000000..a9da06c
--- /dev/null
+++ b/scribo/scribo/binarization/internal/local_threshold_debug.hh
@@ -0,0 +1,88 @@
+// 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_LOCAL_THRESHOLD_DEBUG_HH
+# define SCRIBO_BINARIZATION_INTERNAL_LOCAL_THRESHOLD_DEBUG_HH
+
+/// \file
+///
+/// \brief Declare all debug related variables for local based
+/// algorithms.
+
+
+/// FIXME: A struct may be a bit better...
+
+
+# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+namespace scribo
+{
+
+ namespace binarization
+ {
+
+ using namespace mln;
+
+ namespace internal
+ {
+
+ char* stddev_image_output = 0;
+ char* mean_image_output = 0;
+ char* threshold_image_output = 0;
+
+ char* scale_image_output = 0;
+
+ char* alpham_image_output = 0;
+ char* alphacond_image_output = 0;
+
+ // Declare debug images.
+ image2d<double> debug_stddev;
+ image2d<double> debug_mean;
+ image2d<double> debug_threshold;
+
+ image2d<double> debug_alpham;
+ image2d<bool> debug_alphacond;
+
+ double mean_debug_factor = 1.0;
+ double stddev_debug_factor = 1.0;
+ double alpham_debug_factor = 2.0;
+
+ } // end of namespace scribo::binarization::internal
+
+ } // end of namespace scribo::binarization
+
+} // end of namespace scribo
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG
+
+
+#endif // ! SCRIBO_BINARIZATION_INTERNAL_LOCAL_THRESHOLD_DEBUG_HH
diff --git a/scribo/scribo/binarization/internal/niblack_formula.hh
b/scribo/scribo/binarization/internal/niblack_formula.hh
new file mode 100644
index 0000000..54dbc9b
--- /dev/null
+++ b/scribo/scribo/binarization/internal/niblack_formula.hh
@@ -0,0 +1,105 @@
+// 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_FORMULA_HH
+# define SCRIBO_BINARIZATION_INTERNAL_NIBLACK_FORMULA_HH
+
+
+/// \file
+///
+/// \brief Routines computing a threshold using Niblack's binarization
+/// formula.
+
+// \fixme Having an unused parameter to fulfill the required interface
+// may not be the best solution...
+
+// Setup default Niblack's formula parameters values.
+# define SCRIBO_DEFAULT_NIBLACK_K -0.2
+
+
+
+namespace scribo
+{
+
+ namespace binarization
+ {
+
+ namespace internal
+ {
+
+ struct niblack_formula
+ {
+
+ /*! \brief compute a threshold using Niblack's formula.
+
+ \param[in] m_x_y Mean value.
+ \param[in] s_x_y Standard deviation.
+ \param[in] k Control the threshold value in the local
+ window. The higher, the lower the threshold
+ form the local mean m(x, y).
+ \param[in] R Maximum value of the standard deviation (128
+ for grayscale documents). 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;
+
+ /*!
+ \overload K = 0.34.
+ */
+ double operator()(const double m_x_y, const double s_x_y) const;
+
+ };
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+ inline
+ double
+ niblack_formula::operator()(const double m_x_y, const double s_x_y,
+ const double K, const double /*R*/) const
+ {
+ return m_x_y + K * s_x_y;
+ }
+
+ inline
+ double
+ 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);
+ }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::binarization::internal
+
+ } // end of namespace scribo::binarization
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_BINARIZATION_INTERNAL_NIBLACK_FORMULA_HH
diff --git a/scribo/scribo/binarization/internal/sauvola_debug.hh
b/scribo/scribo/binarization/internal/sauvola_debug.hh
deleted file mode 100644
index 0f8ccf0..0000000
--- a/scribo/scribo/binarization/internal/sauvola_debug.hh
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (C) 2010 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_DEBUG_HH
-# define SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_DEBUG_HH
-
-/// \file
-///
-/// \brief Declare all debug related variables for Sauvola*
-/// algorithms.
-
-
-/// FIXME: A struct may be a bit better...
-
-
-# ifdef SCRIBO_SAUVOLA_DEBUG
-
-# ifndef MLN_INCLUDE_ONLY
-
-
-namespace scribo
-{
-
- namespace binarization
- {
-
- using namespace mln;
-
- namespace internal
- {
-
- char* stddev_image_output = 0;
- char* mean_image_output = 0;
- char* threshold_image_output = 0;
-
- char* scale_image_output = 0;
-
- char* alpham_image_output = 0;
- char* alphacond_image_output = 0;
-
- // Declare debug images.
- image2d<double> debug_stddev;
- image2d<double> debug_mean;
- image2d<double> debug_threshold;
-
- image2d<double> debug_alpham;
- image2d<bool> debug_alphacond;
-
- double mean_debug_factor = 1.0;
- double stddev_debug_factor = 1.0;
- double alpham_debug_factor = 2.0;
-
- } // end of namespace scribo::binarization::internal
-
- } // end of namespace scribo::binarization
-
-} // end of namespace scribo
-
-
-# endif // ! MLN_INCLUDE_ONLY
-
-# endif // ! SCRIBO_SAUVOLA_DEBUG
-
-
-#endif // ! SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_DEBUG_HH
diff --git a/scribo/scribo/binarization/internal/sauvola_formula.hh
b/scribo/scribo/binarization/internal/sauvola_formula.hh
new file mode 100644
index 0000000..3251abd
--- /dev/null
+++ b/scribo/scribo/binarization/internal/sauvola_formula.hh
@@ -0,0 +1,121 @@
+// 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_FORMULA_HH
+# define SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_FORMULA_HH
+
+
+/// \file
+///
+/// \brief Routines computing a threshold using Sauvola's binarization
+/// formula.
+
+
+// Setup default Sauvola's formula parameters values.
+// These macros may be used in other variant of Sauvola's algorithms.
+//
+// Values are set according to the following reference: "Automatic
+// Evaluation of Document Binarization Results", Badekas and al, 2005
+//
+// Badekas et al. said 0.34 was best for Sauvola.
+# define SCRIBO_DEFAULT_SAUVOLA_K 0.34
+//
+// 128 is best for grayscale documents.
+# define SCRIBO_DEFAULT_SAUVOLA_R 128
+
+
+namespace scribo
+{
+
+ namespace binarization
+ {
+
+ namespace internal
+ {
+
+ struct sauvola_formula
+ {
+
+ /*! \brief Compute a threshold using Sauvola's formula.
+
+ \param[in] m_x_y Mean value.
+ \param[in] s_x_y Standard deviation.
+ \param[in] k Control the threshold value in the local
+ window. The higher, the lower the threshold
+ form the local mean m(x, y).
+ \param[in] R Maximum value of the standard deviation (128
+ for grayscale documents).
+
+ \return A threshold.
+ */
+ 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 double m_x_y, const double s_x_y) const;
+
+ };
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+ bool b;
+ double skewness_;
+
+ inline
+ double
+ sauvola_formula::operator()(const double m_x_y, const double s_x_y,
+ const double K, const double R) const
+ {
+ // if (b)
+ // std::cout << skewness_ << " - " << (K * -1 * skewness_)
<< std::endl;
+ // volatile double new_t = ((skewness_ < 0) ? -skewness_ : 1 * m_x_y * (1.0 + K *
((s_x_y / R) - 1.0)));
+ // volatile double old_t = (m_x_y * (1.0 + K * ((s_x_y / R) - 1.0)));
+ // if (skewness_ > 0)
+ // if (new_t != old_t)
+ // std::cout << skewness_ << " - " << new_t <<
" vs " << old_t << std::endl;
+
+ return m_x_y * (1.0 + K * ((s_x_y / R) - 1.0));
+ }
+
+ inline
+ double
+ sauvola_formula::operator()(const double m_x_y, const double s_x_y) const
+ {
+ return (*this)(m_x_y, s_x_y,
+ SCRIBO_DEFAULT_SAUVOLA_K, SCRIBO_DEFAULT_SAUVOLA_R);
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::binarization::internal
+
+ } // end of namespace scribo::binarization
+
+} // end of namespace scribo
+
+#endif // ! SCRIBO_BINARIZATION_INTERNAL_SAUVOLA_FORMULA_HH
diff --git a/scribo/scribo/binarization/niblack.hh
b/scribo/scribo/binarization/niblack.hh
new file mode 100644
index 0000000..e66e7b4
--- /dev/null
+++ b/scribo/scribo/binarization/niblack.hh
@@ -0,0 +1,218 @@
+// 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_NIBLACK_HH
+# define SCRIBO_BINARIZATION_NIBLACK_HH
+
+/// \file
+///
+///
+
+# 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
+
+namespace scribo
+{
+
+ namespace binarization
+ {
+
+ using namespace mln;
+
+
+ /*! \brief Convert an image into a binary image.
+
+ \input[in] input An image.
+ \input[in] window_size The window size.
+ \input[in] K Niblack's formulae constant.
+
+ \return A binary image.
+
+ */
+ template <typename I>
+ mln_ch_value(I, bool)
+ niblack(const Image<I>& input, unsigned window_size, double K);
+
+
+
+ /*! \brief Convert an image into a binary image.
+
+ Niblack's formulae constant K is set to 0.34.
+
+ \input[in] input An image.
+ \input[in] window_size The window size.
+
+ \return A binary image.
+
+ */
+ template <typename I>
+ mln_ch_value(I, bool)
+ niblack(const Image<I>& input, unsigned window_size);
+
+
+ /// \overload
+ /// The window size is set to 11.
+ //
+ template <typename I>
+ mln_ch_value(I, bool)
+ niblack(const Image<I>& input);
+
+
+# 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)
+ {
+ 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
+
+
+ trace::exiting("scribo::binarization::niblack");
+ return output;
+ }
+
+
+ template <typename I>
+ mln_ch_value(I, bool)
+ niblack(const Image<I>& input, unsigned window_size)
+ {
+ return niblack(input, window_size, SCRIBO_DEFAULT_NIBLACK_K);
+ }
+
+
+ template <typename I>
+ mln_ch_value(I, bool)
+ niblack(const Image<I>& input)
+ {
+ return niblack(input, 11);
+ }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+
+ } // end of namespace scribo::binarization
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_BINARIZATION_NIBLACK_HH
diff --git a/scribo/scribo/binarization/niblack_threshold.hh
b/scribo/scribo/binarization/niblack_threshold.hh
new file mode 100644
index 0000000..db4a74e
--- /dev/null
+++ b/scribo/scribo/binarization/niblack_threshold.hh
@@ -0,0 +1,299 @@
+// 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_NIBLACK_THRESHOLD_HH
+# define SCRIBO_BINARIZATION_NIBLACK_THRESHOLD_HH
+
+/// \file
+///
+/// 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>
+
+# include <mln/core/image/image2d.hh>
+# include <mln/value/int_u.hh>
+# include <mln/value/int_u8.hh>
+
+# include <scribo/core/init_integral_image.hh>
+# include <scribo/binarization/internal/compute_local_threshold.hh>
+# include <scribo/binarization/internal/niblack_formula.hh>
+
+
+
+namespace scribo
+{
+
+ namespace binarization
+ {
+
+ using namespace mln;
+
+ /*! \brief Compute an image of local threshold using Niblack algorithm.
+
+ \input[in] input A gray level image.
+ \input[in] window_size The window size.
+ \input[out] simple The sum of all intensities of \p input.
+ \input[out] squared The sum of all squared intensities of \p
+ input.
+
+ \return An image of local thresholds.
+
+ */
+ 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);
+
+ /// \overload
+ /// K is set to 0.34
+ template <typename I>
+ mln_ch_value(I, value::int_u8)
+ niblack_threshold(const Image<I>& input, unsigned window_size);
+
+
+ /// \overload
+ /// The window size is set to 11.
+ //
+ template <typename I>
+ mln_ch_value(I, value::int_u8)
+ niblack_threshold(const Image<I>& input);
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+ // Implementation
+
+
+ namespace impl
+ {
+
+ namespace generic
+ {
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ niblack_threshold(const Image<I>& input_, unsigned window_size,
+ double K,
+ Image<J>& simple_,
+ Image<J>& squared_)
+ {
+ 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
+ }
+
+ trace::exiting("scribo::binarization::impl::generic::niblack_threshold");
+ return output;
+ }
+
+ } // end of namespace scribo::binarization::impl::generic
+
+
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ niblack_threshold_gl(const I& input, unsigned window_size,
+ double K,
+ Image<J>& simple,
+ Image<J>& squared)
+ {
+ return impl::generic::niblack_threshold(input, window_size, K,
+ simple, squared);
+ }
+
+
+ } // end of namespace scribo::binarization::impl
+
+
+
+
+ // Dispatch
+
+ namespace internal
+ {
+
+ template <unsigned n, typename I, typename J>
+ 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)
+ {
+ return impl::niblack_threshold_gl(input, window_size, K,
+ simple, squared);
+ }
+
+
+ template <typename I, typename J>
+ 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)
+ {
+ // No dispatch for this kind of value type.
+ mlc_abort(I)::check();
+
+ typedef mln_ch_value(I,bool) output_t;
+ return output_t();
+ }
+
+
+ } // end of namespace scribo::binarization::internal
+
+
+
+ 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)
+ {
+ trace::entering("scribo::binarization::niblack_threshold");
+
+ mln_precondition(mln_site_(I)::dim == 2);
+ mln_precondition(exact(input).is_valid());
+
+ typedef mln_value(I) value_t;
+ mln_ch_value(I, value::int_u8)
+ output = internal::niblack_threshold_dispatch(value_t(),
+ exact(input),
+ window_size,
+ K,
+ exact(simple),
+ exact(squared));
+
+ trace::exiting("scribo::text::ppm2pbm");
+ return output;
+ }
+
+
+ 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,
+ SCRIBO_DEFAULT_NIBLACK_K);
+ }
+
+
+ template <typename I>
+ inline
+ mln_ch_value(I, value::int_u8)
+ niblack_threshold(const Image<I>& input)
+ {
+ return niblack_threshold(input, 11);
+ }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::binarization
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_BINARIZATION_NIBLACK_THRESHOLD_HH
diff --git a/scribo/scribo/binarization/sauvola.hh
b/scribo/scribo/binarization/sauvola.hh
index 45891c3..fc3e104 100644
--- a/scribo/scribo/binarization/sauvola.hh
+++ b/scribo/scribo/binarization/sauvola.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.
//
@@ -35,15 +35,15 @@
# include <mln/data/transform.hh>
# include <mln/value/int_u8.hh>
-# include <scribo/binarization/sauvola_threshold_image.hh>
+# include <scribo/binarization/sauvola_threshold.hh>
# include <scribo/binarization/local_threshold.hh>
-# include <scribo/binarization/internal/sauvola_debug.hh>
+# include <scribo/binarization/internal/local_threshold_debug.hh>
-# ifdef SCRIBO_SAUVOLA_DEBUG
+# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG
# include <mln/io/pgm/save.hh>
# include <mln/io/pbm/save.hh>
# include <mln/data/saturate.hh>
-# endif // ! SCRIBO_SAUVOLA_DEBUG
+# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG
namespace scribo
{
@@ -110,11 +110,11 @@ namespace scribo
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,
- binarization::sauvola_threshold_image(input,
- window_size,
- K));
+ output = local_threshold(input, threshold_image);
trace::exiting("scribo::binarization::impl::generic::sauvola");
return output;
@@ -168,7 +168,7 @@ namespace scribo
mln_ch_value(I, bool)
output = internal::sauvola_dispatch(input, window_size, K);
-# ifdef SCRIBO_SAUVOLA_DEBUG
+# 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);
@@ -184,7 +184,7 @@ namespace scribo
internal::alpham_image_output);
if (internal::alphacond_image_output)
io::pbm::save(internal::debug_alphacond, internal::alphacond_image_output);
-# endif // ! SCRIBO_SAUVOLA_DEBUG
+# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG
trace::exiting("scribo::binarization::sauvola");
diff --git a/scribo/scribo/binarization/sauvola_ms.hh
b/scribo/scribo/binarization/sauvola_ms.hh
index c1a3414..36629f9 100644
--- a/scribo/scribo/binarization/sauvola_ms.hh
+++ b/scribo/scribo/binarization/sauvola_ms.hh
@@ -60,16 +60,15 @@
# include <scribo/core/macros.hh>
-# include <scribo/binarization/sauvola_threshold_image.hh>
# include <scribo/binarization/internal/first_pass_functor.hh>
# include <scribo/canvas/integral_browsing.hh>
-# ifdef SCRIBO_SAUVOLA_DEBUG
-# include <scribo/binarization/internal/sauvola_debug.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_SAUVOLA_DEBUG
+# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG
@@ -179,7 +178,7 @@ namespace scribo
// 1st pass
scribo::binarization::internal::first_pass_functor< image2d<int_u8> >
- f(sub, K);
+ f(sub, K, SCRIBO_DEFAULT_SAUVOLA_R);
scribo::canvas::integral_browsing(integral_sum_sum_2,
ratio,
w_local_w, w_local_h,
@@ -259,10 +258,10 @@ namespace scribo
} // end of 2nd pass
-# ifdef SCRIBO_SAUVOLA_DEBUG
+# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG
io::pbm::save(f.msk,
scribo::make::debug_filename(internal::threshold_image_output).c_str());
-# endif // ! SCRIBO_SAUVOLA_DEBUG
+# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG
return f.t_sub;
}
@@ -923,18 +922,18 @@ namespace scribo
}
-# ifdef SCRIBO_SAUVOLA_DEBUG
+# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG
if (internal::scale_image_output)
io::pgm::save(e_2, internal::scale_image_output);
-# endif // ! SCRIBO_SAUVOLA_DEBUG
+# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG
// Propagate scale values.
e_2 = transform::influence_zone_geodesic(e_2, c8());
-// # ifdef SCRIBO_SAUVOLA_DEBUG
+// # ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG
// if (internal::scale_image_output)
// io::pgm::save(e_2, internal::scale_image_output);
-// # endif // ! SCRIBO_SAUVOLA_DEBUG
+// # endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG
// Binarize
image2d<bool>
diff --git a/scribo/scribo/binarization/sauvola_threshold.hh
b/scribo/scribo/binarization/sauvola_threshold.hh
new file mode 100644
index 0000000..df46e95
--- /dev/null
+++ b/scribo/scribo/binarization/sauvola_threshold.hh
@@ -0,0 +1,296 @@
+// 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_SAUVOLA_THRESHOLD_HH
+# define SCRIBO_BINARIZATION_SAUVOLA_THRESHOLD_HH
+
+/// \file
+///
+/// Compute an image of local threshold using Sauvola algorithm.
+
+/// \fixme return type too restrictive!
+
+# include <algorithm>
+# include <cmath>
+
+# include <mln/core/image/image2d.hh>
+# include <mln/value/int_u.hh>
+# include <mln/value/int_u8.hh>
+
+# include <scribo/core/init_integral_image.hh>
+# include <scribo/binarization/internal/compute_local_threshold.hh>
+# include <scribo/binarization/internal/sauvola_formula.hh>
+
+
+
+namespace scribo
+{
+
+ namespace binarization
+ {
+
+ using namespace mln;
+
+ /*! \brief Compute an image of local threshold using Sauvola algorithm.
+
+ \input[in] input A gray level image.
+ \input[in] window_size The window size.
+ \input[out] simple The sum of all intensities of \p input.
+ \input[out] squared The sum of all squared intensities of \p
+ input.
+
+ \return An image of local thresholds.
+
+ */
+ 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
+ /// K is set to 0.34
+ template <typename I>
+ mln_ch_value(I, value::int_u8)
+ sauvola_threshold(const Image<I>& input, unsigned window_size);
+
+
+ /// \overload
+ /// The window size is set to 11.
+ //
+ template <typename I>
+ mln_ch_value(I, value::int_u8)
+ sauvola_threshold(const Image<I>& input);
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+ // Implementation
+
+
+ namespace impl
+ {
+
+ namespace generic
+ {
+
+ template <typename I, typename J>
+ inline
+ mln_concrete(I)
+ sauvola_threshold(const Image<I>& input_, unsigned window_size,
+ double K,
+ Image<J>& simple_,
+ Image<J>& squared_)
+ {
+ trace::entering("scribo::binarization::impl::generic::sauvola_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
+
+ // Sauvola Algorithm with I.I.
+
+ 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::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));
+
+# ifdef SCRIBO_LOCAL_THRESHOLD_DEBUG
+ internal::debug_threshold.at_(row, col) = t;
+# endif // ! SCRIBO_LOCAL_THRESHOLD_DEBUG
+ }
+
+ trace::exiting("scribo::binarization::impl::generic::sauvola_threshold");
+ return output;
+ }
+
+ } // end of namespace scribo::binarization::impl::generic
+
+
+
+ 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);
+ }
+
+
+ } // end of namespace scribo::binarization::impl
+
+
+
+
+ // Dispatch
+
+ namespace internal
+ {
+
+ template <unsigned n, typename I, typename J>
+ inline
+ mln_ch_value(I, value::int_u<n>)
+ sauvola_threshold_dispatch(const value::int_u<n>&, const I& input,
+ unsigned window_size,
+ double K,
+ J& simple,
+ J& squared)
+ {
+ return impl::sauvola_threshold_gl(input, window_size, K,
+ simple, squared);
+ }
+
+
+ template <typename I, typename J>
+ inline
+ mln_ch_value(I, value::int_u8)
+ sauvola_threshold_dispatch(const mln_value(I)&, const I& input,
+ unsigned window_size,
+ double K,
+ J& simple,
+ J& squared)
+ {
+ // No dispatch for this kind of value type.
+ mlc_abort(I)::check();
+
+ typedef mln_ch_value(I,bool) output_t;
+ return output_t();
+ }
+
+
+ } // end of namespace scribo::binarization::internal
+
+
+
+ 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)
+ {
+ trace::entering("scribo::binarization::sauvola_threshold");
+
+ mln_precondition(mln_site_(I)::dim == 2);
+ mln_precondition(exact(input).is_valid());
+
+ typedef mln_value(I) value_t;
+ mln_ch_value(I, value::int_u8)
+ output = internal::sauvola_threshold_dispatch(value_t(),
+ exact(input),
+ window_size,
+ K,
+ exact(simple),
+ exact(squared));
+
+ trace::exiting("scribo::text::ppm2pbm");
+ return output;
+ }
+
+
+ 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_);
+
+ 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,
+ SCRIBO_DEFAULT_SAUVOLA_K);
+ }
+
+
+ template <typename I>
+ inline
+ mln_ch_value(I, value::int_u8)
+ sauvola_threshold(const Image<I>& input)
+ {
+ return sauvola_threshold(input, 11);
+ }
+
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::binarization
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_BINARIZATION_SAUVOLA_THRESHOLD_HH
diff --git a/scribo/scribo/binarization/sauvola_threshold_image.hh
b/scribo/scribo/binarization/sauvola_threshold_image.hh
deleted file mode 100644
index 94cd688..0000000
--- a/scribo/scribo/binarization/sauvola_threshold_image.hh
+++ /dev/null
@@ -1,301 +0,0 @@
-// Copyright (C) 2009, 2010 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_SAUVOLA_THRESHOLD_IMAGE_HH
-# define SCRIBO_BINARIZATION_SAUVOLA_THRESHOLD_IMAGE_HH
-
-/// \file
-///
-/// Compute an image of local threshold using Sauvola algorithm.
-
-/// \fixme return type too restrictive!
-
-# include <algorithm>
-# include <cmath>
-
-# include <mln/core/image/image2d.hh>
-# include <mln/value/int_u.hh>
-# include <mln/value/int_u8.hh>
-
-# include <scribo/core/init_integral_image.hh>
-# include <scribo/binarization/internal/compute_sauvola_threshold.hh>
-
-
-
-namespace scribo
-{
-
- namespace binarization
- {
-
- using namespace mln;
-
- /*! \brief Compute an image of local threshold using Sauvola algorithm.
-
- \input[in] input An image.
- \input[in] window_size The window size.
- \input[out] simple The sum of all intensities of \p input.
- \input[out] squared The sum of all squared intensities of \p
- input.
-
- \return An image of local thresholds.
-
- */
- template <typename I, typename J>
- mln_ch_value(I, value::int_u8)
- sauvola_threshold_image(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_image(const Image<I>& input, unsigned window_size,
- double K);
-
- /// \overload
- /// K is set to 0.34
- template <typename I>
- mln_ch_value(I, value::int_u8)
- sauvola_threshold_image(const Image<I>& input, unsigned window_size);
-
-
- /// \overload
- /// The window size is set to 11.
- //
- template <typename I>
- mln_ch_value(I, value::int_u8)
- sauvola_threshold_image(const Image<I>& input);
-
-
-
-# ifndef MLN_INCLUDE_ONLY
-
-
- // Implementation
-
-
- namespace impl
- {
-
- namespace generic
- {
-
- template <typename I, typename J>
- inline
- mln_concrete(I)
- sauvola_threshold_image(const Image<I>& input_, unsigned window_size,
- double K,
- Image<J>& simple_,
- Image<J>& squared_)
- {
-
trace::entering("scribo::binarization::impl::generic::sauvola_threshold_image");
-
- 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_SAUVOLA_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_SAUVOLA_DEBUG
-
- // Sauvola Algorithm with I.I.
-
- 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());
-
-
- for(mln::def::coord row = 0; row < nrows; ++row)
- for(mln::def::coord col = 0; col < ncols; ++col)
- {
-# ifdef SCRIBO_SAUVOLA_DEBUG
-
- double t = internal::compute_sauvola_threshold(P(row, col), simple,
- squared, window_size,
- K,
- SCRIBO_DEFAULT_SAUVOLA_R);
- mln::convert::from_to(t, output.at_(row, col));
- internal::debug_threshold.at_(row, col) = t;
-# else
- mln::convert::from_to(
- internal::compute_sauvola_threshold(P(row, col), simple,
- squared, window_size,
- K,
- SCRIBO_DEFAULT_SAUVOLA_R),
- output.at_(row, col));
-# endif // ! SCRIBO_SAUVOLA_DEBUG
- }
-
- trace::exiting("scribo::binarization::impl::generic::sauvola_threshold");
- return output;
- }
-
- } // end of namespace scribo::binarization::impl::generic
-
-
-
- template <typename I, typename J>
- inline
- mln_concrete(I)
- sauvola_threshold_image_gl(const I& input, unsigned window_size,
- double K,
- Image<J>& simple,
- Image<J>& squared)
- {
- return impl::generic::sauvola_threshold_image(input, window_size, K,
- simple, squared);
- }
-
-
- } // end of namespace scribo::binarization::impl
-
-
-
-
- // Dispatch
-
- namespace internal
- {
-
- template <unsigned n, typename I, typename J>
- inline
- mln_ch_value(I, value::int_u<n>)
- sauvola_threshold_image_dispatch(const value::int_u<n>&, const I&
input,
- unsigned window_size,
- double K,
- J& simple,
- J& squared)
- {
- return impl::sauvola_threshold_image_gl(input, window_size, K,
- simple, squared);
- }
-
-
- template <typename I, typename J>
- inline
- mln_ch_value(I, value::int_u8)
- sauvola_threshold_image_dispatch(const mln_value(I)&, const I& input,
- unsigned window_size,
- double K,
- J& simple,
- J& squared)
- {
- // No dispatch for this kind of value type.
- mlc_abort(I)::check();
-
- typedef mln_ch_value(I,bool) output_t;
- return output_t();
- }
-
-
- } // end of namespace scribo::binarization::internal
-
-
-
- template <typename I, typename J>
- mln_ch_value(I, value::int_u8)
- sauvola_threshold_image(const Image<I>& input, unsigned window_size,
- double K,
- Image<J>& simple,
- Image<J>& squared)
- {
- trace::entering("scribo::binarization::sauvola_threshold_image");
-
- mln_precondition(mln_site_(I)::dim == 2);
- mln_precondition(exact(input).is_valid());
-
- typedef mln_value(I) value_t;
- mln_ch_value(I, value::int_u8)
- output = internal::sauvola_threshold_image_dispatch(value_t(),
- exact(input),
- window_size,
- K,
- exact(simple),
- exact(squared));
-
- trace::exiting("scribo::text::ppm2pbm");
- return output;
- }
-
-
- template <typename I>
- inline
- mln_ch_value(I, value::int_u8)
- sauvola_threshold_image(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 sauvola_threshold_image(input, window_size,
- K, simple, squared);
- }
-
-
- template <typename I>
- inline
- mln_ch_value(I, value::int_u8)
- sauvola_threshold_image(const Image<I>& input, unsigned window_size)
- {
- return sauvola_threshold_image(input, window_size,
- SCRIBO_DEFAULT_SAUVOLA_K);
- }
-
-
- template <typename I>
- inline
- mln_ch_value(I, value::int_u8)
- sauvola_threshold_image(const Image<I>& input)
- {
- return sauvola_threshold_image(input, 11);
- }
-
-
-# endif // ! MLN_INCLUDE_ONLY
-
- } // end of namespace scribo::binarization
-
-} // end of namespace scribo
-
-
-#endif // ! SCRIBO_BINARIZATION_SAUVOLA_THRESHOLD_IMAGE_HH
diff --git a/scribo/src/binarization/Makefile.am b/scribo/src/binarization/Makefile.am
index 315e621..567972a 100644
--- a/scribo/src/binarization/Makefile.am
+++ b/scribo/src/binarization/Makefile.am
@@ -46,6 +46,7 @@ if HAVE_MAGICKXX
sauvola_ms_debug
utilexec_PROGRAMS = \
+ niblack \
otsu \
sauvola \
sauvola_ms \
@@ -60,6 +61,13 @@ if HAVE_MAGICKXX
$(MAGICKXX_LDFLAGS)
+ niblack_SOURCES = niblack.cc
+ niblack_CPPFLAGS = $(AM_CPPFLAGS) \
+ $(MAGICKXX_CPPFLAGS)
+ niblack_LDFLAGS = $(AM_LDFLAGS) \
+ $(MAGICKXX_LDFLAGS)
+
+
otsu_SOURCES = otsu.cc
otsu_CPPFLAGS = $(AM_CPPFLAGS) \
$(MAGICKXX_CPPFLAGS)
@@ -80,7 +88,7 @@ if HAVE_MAGICKXX
sauvola_debug_SOURCES = sauvola_debug.cc
sauvola_debug_CPPFLAGS = $(AM_CPPFLAGS) \
- -DSCRIBO_SAUVOLA_DEBUG \
+ -DSCRIBO_LOCAL_THRESHOLD_DEBUG \
$(MAGICKXX_CPPFLAGS)
sauvola_debug_LDFLAGS = $(AM_LDFLAGS) \
$(MAGICKXX_LDFLAGS)
@@ -94,7 +102,7 @@ if HAVE_MAGICKXX
sauvola_ms_debug_SOURCES = sauvola_ms_debug.cc
sauvola_ms_debug_CPPFLAGS = $(AM_CPPFLAGS) \
- -DSCRIBO_SAUVOLA_DEBUG \
+ -DSCRIBO_LOCAL_THRESHOLD_DEBUG \
$(MAGICKXX_CPPFLAGS)
sauvola_ms_debug_LDFLAGS = $(AM_LDFLAGS) \
$(MAGICKXX_LDFLAGS)
diff --git a/scribo/src/binarization/niblack.cc b/scribo/src/binarization/niblack.cc
new file mode 100644
index 0000000..4b7ed91
--- /dev/null
+++ b/scribo/src/binarization/niblack.cc
@@ -0,0 +1,106 @@
+// 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.
+
+#include <mln/core/image/image2d.hh>
+#include <mln/value/int_u8.hh>
+#include <mln/io/magick/load.hh>
+#include <mln/io/pbm/save.hh>
+#include <mln/data/transform.hh>
+#include <mln/fun/v2v/rgb_to_luma.hh>
+
+#include <scribo/binarization/niblack.hh>
+#include <scribo/debug/option_parser.hh>
+#include <scribo/debug/logger.hh>
+
+static const scribo::debug::arg_data arg_desc[] =
+{
+ { "input.*", "An image." },
+ { "output.pbm", "A binary image." },
+ {0, 0}
+};
+
+
+// --enable/disable-<name>
+static const scribo::debug::toggle_data toggle_desc[] =
+{
+ // name, description, default value
+ {0, 0, false}
+};
+
+
+// --<name> <args>
+static const scribo::debug::opt_data opt_desc[] =
+{
+ // name, description, arguments, check args function, number of args, default arg
+ { "debug-prefix", "Enable debug image outputs. Prefix image name with
that "
+ "given prefix.", "<prefix>", 0, 1, 0 },
+ { "k", "Niblack's formulae parameter",
"<value>", 0, 1, "-0.2" },
+ { "verbose", "Enable verbose mode", 0, 0, 0, 0 },
+ { "win-size", "Window size", "<size>", 0, 1,
"101" },
+ {0, 0, 0, 0, 0, 0}
+};
+
+
+
+int main(int argc, char *argv[])
+{
+ using namespace mln;
+
+ scribo::debug::option_parser options(arg_desc, toggle_desc, opt_desc);
+
+ if (!options.parse(argc, argv))
+ return 1;
+
+ // Enable debug output.
+ if (options.is_set("debug-prefix"))
+ {
+
scribo::debug::logger().set_filename_prefix(options.opt_value("debug-prefix").c_str());
+ scribo::debug::logger().set_level(scribo::debug::All);
+ }
+
+ Magick::InitializeMagick(*argv);
+
+ trace::entering("main");
+
+ bool verbose = options.is_set("verbose");
+ unsigned w = atoi(options.opt_value("win-size").c_str());
+ double k = atof(options.opt_value("k").c_str());
+
+ if (verbose)
+ std::cout << "Using w=" << w << " and k="
<< k << std::endl;
+
+ image2d<value::rgb8> input;
+ io::magick::load(input, options.arg("input.*"));
+
+ // Convert to Gray level image.
+ image2d<value::int_u8>
+ input_1_gl = data::transform(input,
mln::fun::v2v::rgb_to_luma<value::int_u8>());
+
+ image2d<bool> out = scribo::binarization::niblack(input_1_gl, w, k);
+
+ io::pbm::save(out, options.arg("output.pbm"));
+
+ trace::exiting("main");
+}
diff --git a/scribo/src/binarization/pgm_sauvola_threshold_image.cc
b/scribo/src/binarization/pgm_sauvola_threshold_image.cc
index a38784a..69e2e15 100644
--- a/scribo/src/binarization/pgm_sauvola_threshold_image.cc
+++ b/scribo/src/binarization/pgm_sauvola_threshold_image.cc
@@ -1,4 +1,5 @@
-// Copyright (C) 2010 EPITA Research and Development Laboratory (LRDE)
+// Copyright (C) 2010, 2011 EPITA Research and Development Laboratory
+// (LRDE)
//
// This file is part of Olena.
//
@@ -25,7 +26,7 @@
#include <mln/io/pgm/all.hh>
-#include <scribo/binarization/sauvola.hh>
+#include <scribo/binarization/sauvola_threshold.hh>
#include <scribo/debug/usage.hh>
const char *args_desc[][2] =
@@ -67,7 +68,7 @@ int main(int argc, char *argv[])
image2d<value::int_u8> input;
io::pgm::load(input, argv[1]);
- image2d<value::int_u8> out = scribo::binarization::sauvola_threshold_image(input,
w, k);
+ image2d<value::int_u8> out = scribo::binarization::sauvola_threshold(input, w,
k);
io::pgm::save(out, argv[2]);
diff --git a/scribo/tests/binarization/Makefile.am
b/scribo/tests/binarization/Makefile.am
index b8ab2d9..a2962bb 100644
--- a/scribo/tests/binarization/Makefile.am
+++ b/scribo/tests/binarization/Makefile.am
@@ -21,6 +21,7 @@
include $(top_srcdir)/scribo/tests/tests.mk
EXTRA_DIST = \
+ niblack.res.pbm \
sauvola_ms.ref.pbm \
sauvola.ref.pbm \
otsu.ref.pbm
@@ -28,6 +29,7 @@ EXTRA_DIST = \
check_PROGRAMS = \
global_threshold \
local_threshold \
+ niblack \
otsu \
sauvola \
sauvola_ms
@@ -35,6 +37,7 @@ check_PROGRAMS = \
global_threshold_SOURCES = global_threshold.cc
local_threshold_SOURCES = local_threshold.cc
+niblack_SOURCES = niblack.cc
otsu_SOURCES = otsu.cc
sauvola_SOURCES = sauvola.cc
sauvola_ms_SOURCES = sauvola_ms.cc
diff --git a/scribo/tests/binarization/niblack.cc b/scribo/tests/binarization/niblack.cc
new file mode 100644
index 0000000..99a58e2
--- /dev/null
+++ b/scribo/tests/binarization/niblack.cc
@@ -0,0 +1,52 @@
+// Copyright (C) 2011 EPITA Research and Development Laboratory (LRDE)
+//
+// This file is part of Olena.
+//
+// Olena is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation, version 2 of the License.
+//
+// Olena is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Olena. If not, see <http://www.gnu.org/licenses/>.
+//
+// As a special exception, you may use this file as part of a free
+// software project without restriction. Specifically, if other files
+// instantiate templates or use macros or inline functions from this
+// file, or you compile this file and link it with other files to produce
+// an executable, this file does not by itself cause the resulting
+// executable to be covered by the GNU General Public License. This
+// exception does not however invalidate any other reasons why the
+// executable file might be covered by the GNU General Public License.
+
+/// \file
+
+#include <mln/core/image/image2d.hh>
+#include <mln/data/compare.hh>
+#include <mln/value/int_u8.hh>
+#include <mln/io/pgm/load.hh>
+#include <mln/io/pbm/load.hh>
+#include <mln/io/pbm/save.hh>
+
+#include <scribo/binarization/niblack.hh>
+
+#include "tests/data.hh"
+
+int main()
+{
+ using namespace mln;
+
+ image2d<value::int_u8> input;
+ io::pgm::load(input, MILENA_IMG_DIR "/lena.pgm");
+
+ image2d<bool> bin = scribo::binarization::niblack(input, 101);
+
+ image2d<bool> ref;
+ io::pbm::load(ref, SCRIBO_TESTS_DIR "binarization/niblack.ref.pbm");
+
+ mln_assertion(bin == ref);
+}
diff --git a/scribo/tests/binarization/niblack.ref.pbm
b/scribo/tests/binarization/niblack.ref.pbm
new file mode 100644
index 0000000000000000000000000000000000000000..83cf4fb746f4638a7834af48108d7b79fd58fd61
GIT binary patch
literal 32884
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
literal 0
HcmV?d00001
--
1.7.2.5