This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Olena, a generic and efficient image processing platform".
The branch unstable/scribo has been updated
discards 48ccb0ee1492695efa215095edfe715af8ab5d78 (commit)
discards 83eb6772d566edfd0825912255955dfabbba50e0 (commit)
discards db8e0805c02f6fa0a06df902501fad5298a9d3ea (commit)
discards 027bd1b24532d2f6bba717dafdc201a6834ef376 (commit)
discards b14a6dbe8c39dc12ef0aa238111f7fa19d91cbb0 (commit)
via b926dc165dc73387115470edbbba0b6b2e0964d1 (commit)
via 0c44c4c4c9b5e9112cde2b58e0131d09153e046f (commit)
via 91bcd8b415ad22099a16cd941b54f459968b348b (commit)
via 19c45fe85a90403b06454eab85b2a1e628f8d617 (commit)
via 93ca680bd5b2d8e1291f4a542c24548828e92e7c (commit)
via 812809e6cc8a720e6e22cdc3c6cc82f74638bead (commit)
via 8f46b1c939d1f110f4ad55ba3c2fdb51c5c1d709 (commit)
via 0c903e4c6d40d4f86ebda65250c26274854f34cc (commit)
via 00537669dbd854328126aa97cb7e7658129de3da (commit)
via 943c281c2157825dac2f7ff7c64f79dec21a0830 (commit)
This update added new revisions after undoing existing revisions. That is
to say, the old revision is not a strict subset of the new revision. This
situation occurs when you --force push a change and generate a repository
containing something like this:
* -- * -- B -- O -- O -- O (48ccb0ee1492695efa215095edfe715af8ab5d78)
\
N -- N -- N (b926dc165dc73387115470edbbba0b6b2e0964d1)
When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
b926dc1 Merge branch 'next' into unstable/scribo
0c44c4c Fix test of Niblack's algorithm.
91bcd8b Update use of the threshold function.
19c45fe Add two variants of the threshold function.
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 4 +
README | 11 ++-
milena/ChangeLog | 85 +++++++++-----------
milena/mln/border/fill.hh | 31 ++++---
milena/mln/core/box_runstart_piter.hh | 2 +
milena/tests/border/Makefile.am | 4 +-
.../niblack.cc => milena/tests/border/fill_0.cc | 42 ++++++----
scribo/ChangeLog | 27 ++++++
scribo/README | 34 +++++++-
scribo/src/README | 9 +-
10 files changed, 160 insertions(+), 89 deletions(-)
copy scribo/tests/binarization/niblack.cc => milena/tests/border/fill_0.cc (66%)
hooks/post-receive
--
Olena, a generic and efficient image processing platform
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Olena, a generic and efficient image processing platform".
The branch llvm-support has been deleted
was 887dfe212525fea8f438c84b490ebd7ae1287119
-----------------------------------------------------------------------
887dfe212525fea8f438c84b490ebd7ae1287119 Fix more warnings with Clang 3.0
-----------------------------------------------------------------------
hooks/post-receive
--
Olena, a generic and efficient image processing platform
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Olena, a generic and efficient image processing platform".
The branch gimpplugin has been deleted
was 6c0b45ce7368a98b667f55af8c63c750931b822c
-----------------------------------------------------------------------
6c0b45ce7368a98b667f55af8c63c750931b822c backup
-----------------------------------------------------------------------
hooks/post-receive
--
Olena, a generic and efficient image processing platform
* 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