* binarization/sauvola.hh: New routine.
* src/binarization/Makefile.am,
* src/binarization/sauvola.cc,
* src/binarization/sauvola_pgm.cc: New examples.
---
scribo/ChangeLog | 10 +
scribo/binarization/sauvola.hh | 330 ++++++++++++++++++++++++++++++++
scribo/src/binarization/Makefile.am | 4 +
scribo/src/binarization/sauvola.cc | 61 ++++++
scribo/src/binarization/sauvola_pgm.cc | 61 ++++++
5 files changed, 466 insertions(+), 0 deletions(-)
create mode 100644 scribo/binarization/sauvola.hh
create mode 100644 scribo/src/binarization/sauvola.cc
create mode 100644 scribo/src/binarization/sauvola_pgm.cc
diff --git a/scribo/ChangeLog b/scribo/ChangeLog
index 296bbd2..5dc8c5e 100644
--- a/scribo/ChangeLog
+++ b/scribo/ChangeLog
@@ -1,3 +1,13 @@
+2009-08-28 Guillaume Lazzara <lazzara(a)lrde.epita.fr>
+
+ Add Sauvola's binarization.
+
+ * binarization/sauvola.hh: New routine.
+
+ * src/binarization/Makefile.am,
+ * src/binarization/sauvola.cc,
+ * src/binarization/sauvola_pgm.cc: New examples.
+
2009-08-25 Guillaume Lazzara <lazzara(a)lrde.epita.fr>
* src/text_in_photo_invert.cc: New example.
diff --git a/scribo/binarization/sauvola.hh b/scribo/binarization/sauvola.hh
new file mode 100644
index 0000000..a726665
--- /dev/null
+++ b/scribo/binarization/sauvola.hh
@@ -0,0 +1,330 @@
+// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+//
+// This file is part of Olena.
+//
+// Olena is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation, version 2 of the License.
+//
+// Olena is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Olena. If not, see <http://www.gnu.org/licenses/>.
+//
+// As a special exception, you may use this file as part of a free
+// software project without restriction. Specifically, if other files
+// instantiate templates or use macros or inline functions from this
+// file, or you compile this file and link it with other files to produce
+// an executable, this file does not by itself cause the resulting
+// executable to be covered by the GNU General Public License. This
+// exception does not however invalidate any other reasons why the
+// executable file might be covered by the GNU General Public License.
+
+#ifndef SCRIBO_BINARIZATION_SAUVOLA_HH
+# define SCRIBO_BINARIZATION_SAUVOLA_HH
+
+/// \file
+///
+/// Convert an image into a binary image.
+
+# include <algorithm>
+# include <cmath>
+
+# include <mln/core/image/image2d.hh>
+# include <mln/value/rgb8.hh>
+# include <mln/value/int_u.hh>
+
+# include <mln/data/transform.hh>
+# include <mln/pw/all.hh>
+# include <mln/core/routine/duplicate.hh>
+
+
+
+
+namespace scribo
+{
+
+ namespace binarization
+ {
+
+ using namespace mln;
+
+ /*! \brief Convert an image into a binary image.
+
+ \input[in] An image.
+
+ \return A binary image.
+
+ */
+ template <typename I>
+ mln_ch_value(I,bool)
+ sauvola(const Image<I>& input);
+
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+
+ namespace internal
+ {
+
+ template <typename T>
+ class integral_image
+ {
+ public:
+
+ template<class F>
+ integral_image(const image2d<T>& i, F func)
+ : img_(0),
+ nrows_(i.nrows()),
+ ncols_(i.ncols())
+ {
+ img_ = (unsigned long long**)malloc(sizeof(unsigned long long*) * nrows_);
+ for (int n = 0; n < nrows_; ++n)
+ img_[n] = (unsigned long long*)malloc(sizeof(unsigned long long) * ncols_);
+
+ img_[0][0] = func(i.at_(0, 0));
+
+ for (int row = 1; row < nrows_; ++row)
+ img_[row][0] = (*this)(row - 1, 0) + func(i.at_(row, 0));
+
+ for (int col = 1; col < ncols_; ++col)
+ img_[0][col] = (*this)(0, col - 1)
+ + func(i.at_(0, col));
+
+ for (int row = 1; row < nrows_; ++row)
+ for (int col = 1; col < ncols_; ++col)
+ img_[row][col] = (*this)(row - 1, col)
+ + (*this)(row, col - 1)
+ - (*this)(row - 1, col - 1)
+ + func(i.at_(row, col));
+ }
+
+ ~integral_image()
+ {
+ for (int n = 0; n < nrows_; ++n)
+ free(img_[n]);
+ free(img_);
+ }
+
+ unsigned long long operator()(int row, int col) const
+ {
+ return img_[row][col];
+ }
+
+ private:
+ unsigned long long** img_;
+ int nrows_;
+ int ncols_;
+ };
+
+
+ struct rgb8_to_int_u8 : Function_v2v< rgb8_to_int_u8 >
+ {
+ typedef value::int_u8 result;
+ result operator()(const value::rgb8& c) const
+ {
+ return (c.red() + c.green() + c.blue()) / 3;
+ }
+ };
+
+
+ unsigned long long square_(const value::int_u8& val)
+ {
+ unsigned long long v = static_cast<unsigned long long>(val);
+ return v * v;
+ }
+
+ unsigned long long identity_(const value::int_u8& val)
+ {
+ return static_cast<unsigned long long>(val);
+ }
+
+ } // end of namespace scribo::binarization::internal
+
+
+
+
+ // Implementation
+
+
+ namespace impl
+ {
+
+ namespace generic
+ {
+
+ template <typename I>
+ inline
+ mln_ch_value(I, bool)
+ sauvola(const I& input)
+ {
+ trace::entering("scribo::binarization::impl::generic::sauvola");
+
+ typedef mln_value(I) V;
+
+ // Value of the window size
+ const unsigned int w = 11;
+
+ // Control the threshold value in the local window
+ // The higher, the lower the threshold form the local
+ // mean m(x, y). Badekas et al. said 0.34 was best.
+ const double k = 0.34;
+
+ // Maximum value of the standard deviation (128 for
+ // grayscale documents).
+ const double R = 128;
+
+ // Compute the sum of all intensities of input
+ internal::integral_image<V> simple(input, internal::identity_);
+
+ // Compute the sum of all squared intensities of input
+ internal::integral_image<V> squared(input, internal::square_);
+
+ int w_2 = w >> 1;
+
+ // Savaula Algorithm with I.I.
+
+ image2d<bool> output;
+ initialize(output, input);
+
+ const def::coord nrows = static_cast<def::coord>(input.nrows());
+ const def::coord ncols = static_cast<def::coord>(input.ncols());
+
+ for(def::coord row = 0; row < nrows; ++row)
+ for(def::coord col = 0; col < ncols; ++col)
+ {
+ int row_min = std::max(0, row - w_2);
+ int col_min = std::max(0, col - w_2);
+ int row_max = std::min(nrows - 1, row + w_2);
+ int col_max = std::min(ncols - 1, col + w_2);
+
+ double wh = (row_max - row_min + 1) * (col_max - col_min + 1);
+
+ // Mean.
+ double m_x_y_tmp = (simple(row_max, col_max)
+ + simple(row_min, col_min)
+ - simple(row_max, col_min)
+ - simple(row_min, col_max));
+
+ double m_x_y = m_x_y_tmp / wh;
+
+ // Standard deviation.
+ double s_x_y_tmp = (squared(row_max, col_max)
+ + squared(row_min, col_min)
+ - squared(row_max, col_min)
+ - squared(row_min, col_max));
+
+ double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh -
1.f));
+
+ // Thresholding.
+ double t_x_y = m_x_y * (1.0 + k * ((s_x_y / R) - 1.0));
+
+ output.at_(row, col) = (input.at_(row, col) < t_x_y);
+ }
+
+ trace::exiting("scribo::binarization::impl::generic::sauvola");
+ return output;
+ }
+
+ } // end of namespace scribo::binarization::impl::generic
+
+
+ template <typename I>
+ inline
+ mln_ch_value(I, bool)
+ sauvola_gl(const I& input)
+ {
+ return impl::generic::sauvola(input);
+ }
+
+
+ template <typename I>
+ inline
+ mln_ch_value(I, bool)
+ sauvola_rgb8(const I& input)
+ {
+ trace::entering("scribo::binarization::impl::sauvola_rgb8");
+
+ mln_ch_value(I, value::int_u8) gima;
+ gima = data::transform(input,
+ internal::rgb8_to_int_u8());
+
+ mln_ch_value(I, bool) output = impl::generic::sauvola(gima);
+
+ trace::exiting("scribo::binarization::impl::sauvola_rgb8");
+ return output;
+ }
+
+
+ } // end of namespace scribo::binarization::impl
+
+
+
+
+ // Dispatch
+
+ namespace internal
+ {
+
+ template <typename I, unsigned n>
+ inline
+ mln_ch_value(I, bool)
+ sauvola_dispatch(const value::int_u<n>&, const I& input)
+ {
+ return impl::sauvola_gl(input);
+ }
+
+ template <typename I>
+ inline
+ mln_ch_value(I, bool)
+ sauvola_dispatch(const value::rgb8&, const I& input)
+ {
+ return impl::sauvola_rgb8(input);
+ }
+
+ template <typename I>
+ inline
+ mln_ch_value(I, bool)
+ sauvola_dispatch(const mln_value(I)&, const I& input)
+ {
+ // 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>
+ inline
+ mln_ch_value(I, bool)
+ sauvola(const Image<I>& input)
+ {
+ trace::entering("scribo::binarization::sauvola");
+
+ mln_precondition(I::dim == 2);
+ mln_precondition(exact(input).is_valid());
+
+ typedef mln_value(I) value_t;
+ mln_ch_value(I, bool)
+ output = internal::sauvola_dispatch(value_t(), exact(input));
+
+ trace::exiting("scribo::text::ppm2pbm");
+ return output;
+ }
+
+# endif // ! MLN_INCLUDE_ONLY
+
+ } // end of namespace scribo::binarization
+
+} // end of namespace scribo
+
+
+#endif // ! SCRIBO_TEXT_PPM2PBM_HH
diff --git a/scribo/src/binarization/Makefile.am b/scribo/src/binarization/Makefile.am
index 5ba07fc..ab982dc 100644
--- a/scribo/src/binarization/Makefile.am
+++ b/scribo/src/binarization/Makefile.am
@@ -20,7 +20,11 @@
include $(top_srcdir)/scribo/scribo.mk
bin_PROGRAMS = \
+ sauvola \
+ sauvola_pgm \
simple
+sauvola_SOURCES = sauvola.cc
+sauvola_pgm_SOURCES = sauvola_pgm.cc
simple_SOURCES = simple.cc
diff --git a/scribo/src/binarization/sauvola.cc b/scribo/src/binarization/sauvola.cc
new file mode 100644
index 0000000..9b34597
--- /dev/null
+++ b/scribo/src/binarization/sauvola.cc
@@ -0,0 +1,61 @@
+// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+//
+// This file is part of Olena.
+//
+// Olena is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation, version 2 of the License.
+//
+// Olena is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Olena. If not, see <http://www.gnu.org/licenses/>.
+//
+// As a special exception, you may use this file as part of a free
+// software project without restriction. Specifically, if other files
+// instantiate templates or use macros or inline functions from this
+// file, or you compile this file and link it with other files to produce
+// an executable, this file does not by itself cause the resulting
+// executable to be covered by the GNU General Public License. This
+// exception does not however invalidate any other reasons why the
+// executable file might be covered by the GNU General Public License.
+
+#include <mln/io/ppm/load.hh>
+#include <mln/io/pbm/save.hh>
+
+#include <scribo/binarization/sauvola.hh>
+#include <scribo/debug/usage.hh>
+
+const char *args_desc[][2] =
+{
+ { "input.ppm", "A color image." },
+ {0, 0}
+};
+
+
+int main(int argc, char *argv[])
+{
+ using namespace mln;
+ using value::rgb8;
+
+ if (argc != 3)
+ return scribo::debug::usage(argv,
+ "Binarization of a color image based on Sauvola's algorithm.",
+ "input.ppm output.pbm",
+ args_desc, "A binary image.");
+
+ trace::entering("main");
+
+ image2d<rgb8> input;
+ io::ppm::load(input, argv[1]);
+
+
+ io::pbm::save(scribo::binarization::sauvola(input),
+ argv[2]);
+
+
+ trace::exiting("main");
+}
diff --git a/scribo/src/binarization/sauvola_pgm.cc
b/scribo/src/binarization/sauvola_pgm.cc
new file mode 100644
index 0000000..cac71e1
--- /dev/null
+++ b/scribo/src/binarization/sauvola_pgm.cc
@@ -0,0 +1,61 @@
+// Copyright (C) 2009 EPITA Research and Development Laboratory (LRDE)
+//
+// This file is part of Olena.
+//
+// Olena is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation, version 2 of the License.
+//
+// Olena is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Olena. If not, see <http://www.gnu.org/licenses/>.
+//
+// As a special exception, you may use this file as part of a free
+// software project without restriction. Specifically, if other files
+// instantiate templates or use macros or inline functions from this
+// file, or you compile this file and link it with other files to produce
+// an executable, this file does not by itself cause the resulting
+// executable to be covered by the GNU General Public License. This
+// exception does not however invalidate any other reasons why the
+// executable file might be covered by the GNU General Public License.
+
+#include <mln/io/pgm/load.hh>
+#include <mln/io/pbm/save.hh>
+
+#include <scribo/binarization/sauvola.hh>
+#include <scribo/debug/usage.hh>
+
+const char *args_desc[][2] =
+{
+ { "input.pgm", "A gray level image." },
+ {0, 0}
+};
+
+
+int main(int argc, char *argv[])
+{
+ using namespace mln;
+ using value::int_u8;
+
+ if (argc != 3)
+ return scribo::debug::usage(argv,
+ "Binarization of a gray level image based on Sauvola's algorithm.",
+ "input.pgm output.pbm",
+ args_desc, "A binary image.");
+
+ trace::entering("main");
+
+ image2d<int_u8> input;
+ io::pgm::load(input, argv[1]);
+
+
+ io::pbm::save(scribo::binarization::sauvola(input),
+ argv[2]);
+
+
+ trace::exiting("main");
+}
--
1.5.6.5