Commit d10f1a65 authored by Guillaume Lazzara's avatar Guillaume Lazzara
Browse files

Add new Sauvola overloads and update examples.

	* scribo/binarization/internal/first_pass_functor.hh,
	* scribo/binarization/local_threshold.hh,
	* scribo/binarization/sauvola.hh,
	* scribo/binarization/sauvola_ms.hh,
	* scribo/binarization/sauvola_ms_split.hh,
	* scribo/binarization/sauvola_threshold_image.hh,
	* scribo/canvas/integral_browsing.hh: Add more parameters to
	Sauvola routines.

	* scribo/binarization/sauvola_threshold_image_debug.hh: New. New
	overload providing debug outputs.

	* scribo/debug/usage.hh: Remove a parameter.

	* scribo/src/binarization/Makefile.am,
	* scribo/src/binarization/pgm_global_threshold_auto.cc,
	* scribo/src/binarization/pgm_sauvola.cc,
	* scribo/src/binarization/pgm_sauvola_ms.cc,
	* scribo/src/binarization/ppm_fg_sauvola_ms.cc,
	* scribo/src/binarization/ppm_sauvola.cc,
	* scribo/src/binarization/ppm_sauvola_ms.cc,
	* scribo/src/binarization/ppm_sauvola_ms_split.cc: Add new
          program arguments.

	* scribo/src/binarization/fg_sauvola_ms.cc,
        * scribo/src/binarization/sauvola.cc,
        * scribo/src/binarization/sauvola_ms.cc,
        * scribo/src/binarization/sauvola_ms_split.cc,
	* scribo/src/binarization/sauvola_debug.cc: New.
parent b1a4d275
2010-04-13 Guillaume Lazzara <z@lrde.epita.fr>
Add new Sauvola overloads and update examples.
* scribo/binarization/internal/first_pass_functor.hh,
* scribo/binarization/local_threshold.hh,
* scribo/binarization/sauvola.hh,
* scribo/binarization/sauvola_ms.hh,
* scribo/binarization/sauvola_ms_split.hh,
* scribo/binarization/sauvola_threshold_image.hh,
* scribo/canvas/integral_browsing.hh: Add more parameters to
Sauvola routines.
* scribo/binarization/sauvola_threshold_image_debug.hh: New. New
overload providing debug outputs.
* scribo/debug/usage.hh: Remove a parameter.
* scribo/src/binarization/Makefile.am,
* scribo/src/binarization/pgm_global_threshold_auto.cc,
* scribo/src/binarization/pgm_sauvola.cc,
* scribo/src/binarization/pgm_sauvola_ms.cc,
* scribo/src/binarization/ppm_fg_sauvola_ms.cc,
* scribo/src/binarization/ppm_sauvola.cc,
* scribo/src/binarization/ppm_sauvola_ms.cc,
* scribo/src/binarization/ppm_sauvola_ms_split.cc: Add new
program arguments.
* scribo/src/binarization/fg_sauvola_ms.cc,
* scribo/src/binarization/sauvola.cc,
* scribo/src/binarization/sauvola_ms.cc,
* scribo/src/binarization/sauvola_ms_split.cc,
* scribo/src/binarization/sauvola_debug.cc: New.
2010-03-29 Guillaume Lazzara <z@lrde.epita.fr>
Add support for various image types in text_in_photo_fast.
......
......@@ -68,12 +68,12 @@ namespace scribo
unsigned n_nbhs;
util::array<int> dp;
static const double one_k = 1 - 0.34;
static const double k_R = 0.34 / 128.0;
double K_;
first_pass_functor(const I& input)
first_pass_functor(const I& input, double K)
: input(input),
pxl(input)
pxl(input),
K_(K)
{
res = 0;
pxl.start();
......@@ -96,17 +96,9 @@ namespace scribo
unsigned p = pxl.offset();
// Use an inlined and developed version of sauvola's
// threshold formula.
// value::int_u8 t_p = mean * (one_k + k_R * stddev);
// std::cout << t_p << ", ";
// std::cout << input.element(p) << " - " << t_p << std::endl;
value::int_u8 t_p = sauvola_threshold_formula(mean, stddev);
// std::cout << input.point_at_index(p)
// << " - " << sauvola_threshold_formula(mean, stddev);
value::int_u8 t_p = sauvola_threshold_formula(mean, stddev,
K_,
SCRIBO_DEFAULT_SAUVOLA_R);
msk.element(p) = input.element(p) < t_p;
......
......@@ -200,9 +200,6 @@ namespace scribo
internal::local_threshold_tests(input, threshold);
mln_ch_value(I, bool)
output = internal::local_threshold_dispatch(input, threshold);
......
......@@ -53,6 +53,23 @@ namespace scribo
/*! \brief Convert an image into a binary image.
\input[in] input An image.
\input[in] window_size The window size.
\input[in] K Sauvola's formulae constant.
\return A binary image.
*/
template <typename I>
mln_ch_value(I, bool)
sauvola(const Image<I>& input, unsigned window_size, double K);
/*! \brief Convert an image into a binary image.
Sauvola's formulae constant K is set to 0.34.
\input[in] input An image.
\input[in] window_size The window size.
......@@ -84,7 +101,7 @@ namespace scribo
template <typename I>
mln_ch_value(I, bool)
sauvola(const Image<I>& input, unsigned window_size)
sauvola(const Image<I>& input, unsigned window_size, double K)
{
trace::entering("scribo::binarization::impl::generic::sauvola");
mln_precondition(exact(input).is_valid());
......@@ -92,7 +109,8 @@ namespace scribo
mln_ch_value(I, bool)
output = local_threshold(input,
binarization::sauvola_threshold_image(input,
window_size));
window_size,
K));
trace::exiting("scribo::binarization::impl::generic::sauvola");
return output;
......@@ -103,7 +121,7 @@ namespace scribo
template <typename I>
mln_ch_value(I, bool)
sauvola_rgb8(const Image<I>& input, unsigned window_size)
sauvola_rgb8(const Image<I>& input, unsigned window_size, double K)
{
trace::entering("scribo::binarization::impl::generic::sauvola");
mln_precondition(exact(input).is_valid());
......@@ -114,7 +132,8 @@ namespace scribo
mln_ch_value(I, bool)
output = local_threshold(gima,
binarization::sauvola_threshold_image(gima,
window_size));
window_size,
K));
trace::exiting("scribo::binarization::impl::generic::sauvola");
return output;
......@@ -132,26 +151,29 @@ namespace scribo
template <typename I>
mln_ch_value(I, bool)
sauvola_dispatch(const mln_value(I)&,
const Image<I>& input, unsigned window_size)
const Image<I>& input, unsigned window_size,
double K)
{
return impl::generic::sauvola(input, window_size);
return impl::generic::sauvola(input, window_size, K);
}
template <typename I>
mln_ch_value(I, bool)
sauvola_dispatch(const value::rgb8&,
const Image<I>& input, unsigned window_size)
const Image<I>& input, unsigned window_size,
double K)
{
return impl::sauvola_rgb8(input, window_size);
return impl::sauvola_rgb8(input, window_size, K);
}
template <typename I>
mln_ch_value(I, bool)
sauvola_dispatch(const Image<I>& input, unsigned window_size)
sauvola_dispatch(const Image<I>& input, unsigned window_size,
double K)
{
typedef mln_value(I) V;
return sauvola_dispatch(V(), input, window_size);
return sauvola_dispatch(V(), input, window_size, K);
}
} // end of namespace scribo::binarization::internal
......@@ -160,6 +182,22 @@ namespace scribo
// Facades
template <typename I>
mln_ch_value(I, bool)
sauvola(const Image<I>& input, unsigned window_size, double K)
{
trace::entering("scribo::binarization::sauvola");
mln_precondition(exact(input).is_valid());
mln_ch_value(I, bool)
output = internal::sauvola_dispatch(input, window_size, K);
trace::exiting("scribo::binarization::sauvola");
return output;
}
template <typename I>
mln_ch_value(I, bool)
sauvola(const Image<I>& input, unsigned window_size)
......@@ -169,7 +207,8 @@ namespace scribo
mln_precondition(exact(input).is_valid());
mln_ch_value(I, bool)
output = internal::sauvola_dispatch(input, window_size);
output = internal::sauvola_dispatch(input, window_size,
SCRIBO_DEFAULT_SAUVOLA_K);
trace::exiting("scribo::binarization::sauvola");
return output;
......
......@@ -54,6 +54,9 @@
# include <mln/extension/adjust.hh>
// FIXME: to be removed later...
# include <mln/io/pgm/save.hh>
# include <scribo/subsampling/integral_single_image.hh>
# include <scribo/core/macros.hh>
......@@ -81,6 +84,7 @@ namespace scribo
\param[in] w_1 The window size used to compute stats.
\param[in] s The scale factor used for the first subscaling.
\param[in] lambda_min_1 Size of the objects kept at scale 1.
\param[in] K Sauvola's formulae parameter.
\p w_1 and \p lambda_min_1 are expressed according to the image
......@@ -90,6 +94,14 @@ namespace scribo
*/
template <typename I>
mln_ch_value(I,bool)
sauvola_ms(const Image<I>& input_1_, unsigned w_1,
unsigned s, unsigned lambda_min_1, double K);
/// \overload
/// K is set to 0.34.
//
template <typename I>
mln_ch_value(I,bool)
sauvola_ms(const Image<I>& input_1, unsigned w_1,
unsigned s, unsigned lambda_min_1);
......@@ -105,6 +117,11 @@ namespace scribo
using namespace mln;
// FIXME: to be removed later...
char* scale_image_output = 0;
template <typename V>
V my_find_root(image2d<V>& parent, const V& x)
{
......@@ -120,7 +137,8 @@ namespace scribo
unsigned lambda_min, unsigned lambda_max,
unsigned s,
unsigned q, unsigned i, unsigned w,
const image2d<util::couple<double,double> >& integral_sum_sum_2)
const image2d<util::couple<double,double> >& integral_sum_sum_2,
double K)
{
typedef image2d<int_u8> I;
typedef point2d P;
......@@ -133,15 +151,30 @@ namespace scribo
w_local_h = w_local,
w_local_w = w_local;
// Make sure the window fits in the image domain.
if (w >= static_cast<const unsigned>(integral_sum_sum_2.ncols()))
{
w_local_w = std::min(integral_sum_sum_2.ncols(), integral_sum_sum_2.nrows()) - integral_sum_sum_2.border();
w_local_h = w_local_w;
trace::warning("integral_browsing - Adjusting window width since it was larger than image width.");
}
if (w_local_h >= static_cast<const unsigned>(integral_sum_sum_2.nrows()))
{
w_local_h = std::min(integral_sum_sum_2.nrows(), integral_sum_sum_2.ncols()) - integral_sum_sum_2.border();
w_local_w = w_local_h;
trace::warning("integral_browsing - Adjusting window height since it was larger than image height.");
}
if (! (w_local % 2))
{
--w_local_w;
++w_local_h;
}
// 1st pass
scribo::binarization::internal::first_pass_functor< image2d<int_u8> >
f(sub);
f(sub, K);
scribo::canvas::integral_browsing(integral_sum_sum_2,
ratio,
w_local_w, w_local_h,
......@@ -757,7 +790,7 @@ namespace scribo
template <typename I>
mln_ch_value(I,bool)
sauvola_ms(const Image<I>& input_1_, unsigned w_1,
unsigned s, unsigned lambda_min_1)
unsigned s, double K)
{
trace::entering("scribo::binarization::sauvola_ms");
......@@ -768,6 +801,8 @@ namespace scribo
dpoint2d none(0, 0);
unsigned lambda_min_1 = w_1 / 2;
// Number of subscales.
unsigned nb_subscale = 3;
......@@ -820,7 +855,7 @@ namespace scribo
// Subsampling to scale 3 and 4.
for (unsigned i = 3; i <= nb_subscale + 1; ++i)
sub_ima.append(mln::subsampling::antialiased(sub_ima[i - 1], q, none,
sub_ima.append(mln::subsampling::antialiased(sub_ima[i - 1], q,
sub_domains[i].first(),
sub_domains[i].second()));
......@@ -840,7 +875,8 @@ namespace scribo
mln_max(unsigned),
s,
q, i, w_work,
integral_sum_sum_2);
integral_sum_sum_2,
K);
}
// Other scales -> maximum and minimum component size.
......@@ -854,7 +890,8 @@ namespace scribo
lambda_max_2 / ratio,
s,
q, i, w_work,
integral_sum_sum_2);
integral_sum_sum_2,
K);
}
}
......@@ -863,13 +900,17 @@ namespace scribo
t_ima[2] = internal::compute_t_n_and_e_2(sub_ima[2], e_2, 0,
lambda_max_2,
s, 1, 2, w_work,
integral_sum_sum_2);
integral_sum_sum_2,
K);
}
// Propagate scale values.
e_2 = transform::influence_zone_geodesic(e_2, c8());
// FIXME: Remove or make it better...
if (internal::scale_image_output)
io::pgm::save(e_2, internal::scale_image_output);
// Binarize
image2d<bool>
......@@ -885,7 +926,7 @@ namespace scribo
template <typename I>
mln_ch_value(I,bool)
sauvola_ms_rgb8(const Image<I>& input_1_, unsigned w_1,
unsigned s, unsigned lambda_min_1)
unsigned s, double K)
{
const I& input_1 = exact(input_1_);
......@@ -893,54 +934,7 @@ namespace scribo
gima = data::transform(input_1, mln::fun::v2v::rgb_to_int_u<8>());
mln_ch_value(I, bool)
output = generic::sauvola_ms(gima, w_1, s, lambda_min_1);
// typedef mln_ch_value(I,bool) bin_t;
// mln_ch_value(I, value::int_u8) r_i, g_i, b_i;
// // Split the rgb8 image into 3 intensity images.
// mln::data::split(input_1, r_i, g_i, b_i);
// bin_t r_b, g_b, b_b;
// r_b = generic::sauvola_ms(r_i, w_1, s, lambda_min_1);
// g_b = generic::sauvola_ms(g_i, w_1, s, lambda_min_1);
// b_b = generic::sauvola_ms(b_i, w_1, s, lambda_min_1);
// border::resize(r_b, input_1.border());
// border::resize(g_b, input_1.border());
// border::resize(b_b, input_1.border());
// bin_t output;
// initialize(output, input_1);
// typedef bool * b_ptr_t;
// b_ptr_t
// out_ptr = output.buffer(),
// r_ptr = r_b.buffer(),
// g_ptr = g_b.buffer(),
// b_ptr = b_b.buffer();
// unsigned ntrue;
// for (unsigned n = 0; n < output.nelements(); ++n)
// {
// ntrue = 0;
// if (*r_ptr)
// ++ntrue;
// if (*g_ptr)
// ++ntrue;
// if (*b_ptr)
// ++ntrue;
// *out_ptr++ = ntrue > 1;;
// ++r_ptr;
// ++g_ptr;
// ++b_ptr;
// }
output = generic::sauvola_ms(gima, w_1, s, K);
return output;
}
......@@ -959,9 +953,9 @@ namespace scribo
mln_ch_value(I,bool)
sauvola_ms_dispatch(const mln_value(I)&,
const Image<I>& input_1, unsigned w_1,
unsigned s, unsigned lambda_min_1)
unsigned s, double K)
{
return impl::generic::sauvola_ms(input_1, w_1, s, lambda_min_1);
return impl::generic::sauvola_ms(input_1, w_1, s, K);
}
......@@ -970,19 +964,19 @@ namespace scribo
mln_ch_value(I,bool)
sauvola_ms_dispatch(const value::rgb8&,
const Image<I>& input_1, unsigned w_1,
unsigned s, unsigned lambda_min_1)
unsigned s, double K)
{
return impl::sauvola_ms_rgb8(input_1, w_1, s, lambda_min_1);
return impl::sauvola_ms_rgb8(input_1, w_1, s, K);
}
template <typename I>
mln_ch_value(I,bool)
sauvola_ms_dispatch(const Image<I>& input_1, unsigned w_1,
unsigned s, unsigned lambda_min_1)
unsigned s, double K)
{
typedef mln_value(I) V;
return sauvola_ms_dispatch(V(), input_1, w_1, s, lambda_min_1);
return sauvola_ms_dispatch(V(), input_1, w_1, s, K);
}
......@@ -995,20 +989,28 @@ namespace scribo
template <typename I>
mln_ch_value(I,bool)
sauvola_ms(const Image<I>& input_1_, unsigned w_1,
unsigned s, unsigned lambda_min_1)
unsigned s, double K)
{
trace::entering("scribo::binarization::sauvola_ms");
mln_precondition(input_1.is_valid());
mln_precondition(exact(input_1_).is_valid());
mln_ch_value(I,bool)
output = internal::sauvola_ms_dispatch(input_1_, w_1, s, lambda_min_1);
output = internal::sauvola_ms_dispatch(input_1_, w_1, s, K);
trace::exiting("scribo::binarization::sauvola_ms");
return output;
}
template <typename I>
mln_ch_value(I,bool)
sauvola_ms(const Image<I>& input_1, unsigned w_1, unsigned s)
{
return sauvola_ms(input_1, w_1, s, SCRIBO_DEFAULT_SAUVOLA_K);
}
# endif // ! MLN_INCLUDE_ONLY
......
......@@ -61,6 +61,7 @@ namespace scribo
\param[in] min_ntrue A site is set to 'True' in the output if it
is set to 'True' at least \p min_ntrue
components. Possible values: 1, 2, 3.
\param[in] K Sauvola's formula parameter.
\p w_1 and \p lambda_min_1 are expressed according to the image
at scale 0, i.e. the original size.
......@@ -69,17 +70,28 @@ namespace scribo
*/
template <typename I>
mln_ch_value(I, bool)
sauvola_ms_split(const Image<I>& input_1_, unsigned w_1,
unsigned s, unsigned lambda_min_1, unsigned min_ntrue,
double K);
/// \overload
/// K is set to 0.34.
template <typename I>
mln_ch_value(I, bool)
sauvola_ms_split(const Image<I>& input_1_, unsigned w_1,
unsigned s, unsigned lambda_min_1, unsigned min_ntrue);
# ifndef MLN_INCLUDE_ONLY
template <typename I>
mln_ch_value(I, bool)
sauvola_ms_split(const Image<I>& input_1_, unsigned w_1,
unsigned s, unsigned lambda_min_1, unsigned min_ntrue)
unsigned s, unsigned lambda_min_1, unsigned min_ntrue,
double K)
{
trace::entering("scribo::binarization::sauvola_ms_split");
......@@ -97,9 +109,9 @@ namespace scribo
bin_t r_b, g_b, b_b;
r_b = impl::generic::sauvola_ms(r_i, w_1, s, lambda_min_1);
g_b = impl::generic::sauvola_ms(g_i, w_1, s, lambda_min_1);
b_b = impl::generic::sauvola_ms(b_i, w_1, s, lambda_min_1);
r_b = impl::generic::sauvola_ms(r_i, w_1, s, lambda_min_1, K);
g_b = impl::generic::sauvola_ms(g_i, w_1, s, lambda_min_1, K);
b_b = impl::generic::sauvola_ms(b_i, w_1, s, lambda_min_1, K);
border::resize(r_b, input_1.border());
border::resize(g_b, input_1.border());
......@@ -138,6 +150,15 @@ namespace scribo
}
template <typename I>
mln_ch_value(I, bool)
sauvola_ms_split(const Image<I>& input_1, unsigned w_1,
unsigned s, unsigned lambda_min_1, unsigned min_ntrue)
{
return sauvola_ms_split(input_1, w_1, s, lambda_min_1, min_ntrue,
SCRIBO_DEFAULT_SAUVOLA_K);
}
# endif // ! MLN_INCLUDE_ONLY
} // end of namespace scribo::binarization
......
......@@ -47,10 +47,24 @@
# include <mln/fun/v2v/rgb_to_int_u.hh>
# include <mln/debug/println.hh>
# include <scribo/core/init_integral_image.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
{
......@@ -73,11 +87,18 @@ namespace scribo
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);
......@@ -113,9 +134,9 @@ namespace scribo
inline
double
sauvola_threshold_formula(const double m_x_y, const double s_x_y,
const double k, const double R)
const double K, const double R)
{
return m_x_y * (1.0 + k * ((s_x_y / R) - 1.0));
return m_x_y * (1.0 + K * ((s_x_y / R) - 1.0));
}
......@@ -125,13 +146,9 @@ namespace scribo
double
sauvola_threshold_formula(double m_x_y, double s_x_y)
{