Commit 542fcf95 authored by Guillaume Lazzara's avatar Guillaume Lazzara
Browse files

Improve stats computation in Sauvola Multi-scale.

	* binarization/internal/first_pass_functor.hh: Use
	sauvola_threshold routine.

	* binarization/sauvola_threshold.hh: Remove debug and fix invalid
	read in integral image.

	* canvas/integral_browsing.hh,
	* subsampling/integral_single_image.hh: Fix stats computation.

	* src/binarization/sauvola_ms.cc: Fix window parameter and make
	debug output optional.
parent 32e461a6
2009-12-11 Guillaume Lazzara <z@lrde.epita.fr>
Improve Sauvola Multi-scale.
* binarization/internal/first_pass_functor.hh: Use
sauvola_threshold routine.
* binarization/sauvola_threshold.hh: Remove debug and fix invalid
read in integral image.
* canvas/integral_browsing.hh,
* subsampling/integral_single_image.hh: Fix stats computation.
* src/binarization/sauvola_ms.cc: Fix window parameter and make
debug output optional.
2009-12-04 Guillaume Lazzara <z@lrde.epita.fr> 2009-12-04 Guillaume Lazzara <z@lrde.epita.fr>
Optimize Sauvola's multiscale binarization. Optimize Sauvola's multiscale binarization.
......
...@@ -98,8 +98,16 @@ namespace scribo ...@@ -98,8 +98,16 @@ namespace scribo
// Use an inlined and developed version of sauvola's // Use an inlined and developed version of sauvola's
// threshold formula. // threshold formula.
value::int_u8 t_p = mean * (one_k + k_R * stddev); // value::int_u8 t_p = mean * (one_k + k_R * stddev);
// value::int_u8 t_p = sauvola_threshold_formula(mean, 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);
msk.element(p) = input.element(p) < t_p; msk.element(p) = input.element(p) < t_p;
t_sub.element(p) = t_p; t_sub.element(p) = t_p;
...@@ -128,6 +136,8 @@ namespace scribo ...@@ -128,6 +136,8 @@ namespace scribo
void finalize() void finalize()
{ {
mln_assertion(! pxl.is_valid()); mln_assertion(! pxl.is_valid());
// std::cout << std::endl << " ------- " << std::endl;
} }
}; };
......
...@@ -46,6 +46,9 @@ ...@@ -46,6 +46,9 @@
# include <scribo/core/init_integral_image.hh> # include <scribo/core/init_integral_image.hh>
#include <mln/io/pgm/save.hh>
namespace scribo namespace scribo
{ {
...@@ -168,8 +171,8 @@ namespace scribo ...@@ -168,8 +171,8 @@ namespace scribo
// Window half width. // Window half width.
int w_2 = win_width >> 1; int w_2 = win_width >> 1;
int row_min = std::max(0, p.row() - w_2); int row_min = std::max(0, p.row() - w_2 - 1);
int col_min = std::max(0, p.col() - w_2); int col_min = std::max(0, p.col() - w_2 - 1);
int row_max = std::min(static_cast<int>(simple.nrows()) - 1, int row_max = std::min(static_cast<int>(simple.nrows()) - 1,
p.row() + w_2); p.row() + w_2);
...@@ -177,12 +180,7 @@ namespace scribo ...@@ -177,12 +180,7 @@ namespace scribo
p.col() + w_2); p.col() + w_2);
// std::cout << "sauvola threshold : " double wh = (row_max - row_min) * (col_max - col_min);
// << simple.domain() << " - "
// << row_max << " - "
// << col_max << std::endl;
double wh = (row_max - row_min + 1) * (col_max - col_min + 1);
// Mean. // Mean.
double m_x_y_tmp = (simple.at_(row_max, col_max) double m_x_y_tmp = (simple.at_(row_max, col_max)
...@@ -200,9 +198,38 @@ namespace scribo ...@@ -200,9 +198,38 @@ namespace scribo
double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f)); double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f));
// if (p == point2d(3,3))// || p == point2d(4,4) || p == point2d(1,1))
// {
// // std::cout << "p" << p << " - A(" << row_min << ", " << col_min
// // << ") = " << simple.at_(row_min, col_min)
// << " - B(" << row_min << ", " << col_max
// << ") = " << simple.at_(row_min, col_max)
// << " - C(" << row_max << ", " << col_min
// << ") = " << simple.at_(row_max, col_min)
// << " - D(" << row_max << ", " << col_max
// << ") = " << simple.at_(row_max, col_max)
// << " - n = " << wh
// << std::endl;
// << std::endl;
// }
// Thresholding. // Thresholding.
double t_x_y = sauvola_threshold_formula(m_x_y, s_x_y, k, R); double t_x_y = sauvola_threshold_formula(m_x_y, s_x_y, k, R);
// std::cout << p
// << " - m = " << m_x_y
// << " - s = " << s_x_y
// << " - t = " << t_x_y
// << " - sum = " << m_x_y_tmp
// << " - sum_2 = " << s_x_y_tmp
// << std::endl;
return t_x_y; return t_x_y;
} }
...@@ -219,19 +246,12 @@ namespace scribo ...@@ -219,19 +246,12 @@ namespace scribo
int row_min = std::max(0, p.row() - w_2); int row_min = std::max(0, p.row() - w_2);
int col_min = std::max(0, p.col() - w_2); int col_min = std::max(0, p.col() - w_2);
//FIXME: offset (-4) should be replaced by the number of
//padding pixels.
int row_max = std::min(static_cast<int>(integral.nrows()) - 1, int row_max = std::min(static_cast<int>(integral.nrows()) - 1,
p.row() + w_2); p.row() + w_2);
int col_max = std::min(static_cast<int>(integral.ncols()) - 1, int col_max = std::min(static_cast<int>(integral.ncols()) - 1,
p.col() + w_2); p.col() + w_2);
// std::cout << "sauvola threshold : "
// << simple.domain() << " - "
// << row_max << " - "
// << col_max << std::endl;
double wh = (row_max - row_min + 1) * (col_max - col_min + 1); double wh = (row_max - row_min + 1) * (col_max - col_min + 1);
// Mean. // Mean.
...@@ -251,7 +271,7 @@ namespace scribo ...@@ -251,7 +271,7 @@ namespace scribo
double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f)); double s_x_y = std::sqrt((s_x_y_tmp - (m_x_y_tmp * m_x_y_tmp) / wh) / (wh - 1.f));
// Thresholding. // Thresholding.
double t_x_y = m_x_y * (1.0 + 0.14 * ((s_x_y / 128) - 1.0)); double t_x_y = m_x_y * (1.0 + 0.34 * ((s_x_y / 128) - 1.0));
return t_x_y; return t_x_y;
} }
...@@ -438,6 +458,25 @@ namespace scribo ...@@ -438,6 +458,25 @@ namespace scribo
mln_precondition(mln_site_(I)::dim == 2); mln_precondition(mln_site_(I)::dim == 2);
mln_precondition(exact(input).is_valid()); mln_precondition(exact(input).is_valid());
// {
// J& simple_ = exact(simple);
// J& squared_ = exact(squared);
// mln_piter(J) p(simple_.domain());
// for_all(p)
// {
// std::cout << simple_(p) << ", ";
// }
// std::cout << std::endl << " ------- " << std::endl;
// for_all(p)
// {
// std::cout << squared_(p) << ", ";
// }
// std::cout << std::endl << " ------- " << std::endl;
// }
typedef mln_value(I) value_t; typedef mln_value(I) value_t;
mln_ch_value(I, value::int_u8) mln_ch_value(I, value::int_u8)
output = internal::sauvola_threshold_dispatch(value_t(), exact(input), output = internal::sauvola_threshold_dispatch(value_t(), exact(input),
...@@ -445,6 +484,8 @@ namespace scribo ...@@ -445,6 +484,8 @@ namespace scribo
exact(simple), exact(simple),
exact(squared)); exact(squared));
// std::cout << std::endl << " ------- " << std::endl;
io::pgm::save(output, "ref_2_t.pgm");
trace::exiting("scribo::text::ppm2pbm"); trace::exiting("scribo::text::ppm2pbm");
return output; return output;
} }
......
...@@ -58,9 +58,13 @@ namespace scribo ...@@ -58,9 +58,13 @@ namespace scribo
double& mean, double& stddev) double& mean, double& stddev)
{ {
mean = sum / n; mean = sum / n;
stddev = std::sqrt(sum_2 / n - mean * mean); // stddev = std::sqrt(sum_2 / n - mean * mean);
// std::cout << "(" << mean << " - " << stddev << " - " << n << "),";
// unbias version: // unbias version:
// stddev = std::sqrt((sum_2 - n * mean * mean) / (n - 1)); stddev = std::sqrt((sum_2 - sum * sum / n) / (n - 1));
} }
} // end of namespace scribo::canvas::internal } // end of namespace scribo::canvas::internal
...@@ -72,6 +76,7 @@ namespace scribo ...@@ -72,6 +76,7 @@ namespace scribo
void integral_browsing(const image2d<util::couple<double, double> >& ima, void integral_browsing(const image2d<util::couple<double, double> >& ima,
unsigned step, unsigned step,
unsigned w, unsigned h, unsigned w, unsigned h,
unsigned s,
F& functor) F& functor)
{ {
typedef util::couple<double, double> V; typedef util::couple<double, double> V;
...@@ -112,6 +117,8 @@ namespace scribo ...@@ -112,6 +117,8 @@ namespace scribo
double mean, stddev; double mean, stddev;
unsigned s_2 = s * s;
// ------------------------------- // -------------------------------
// T (top) // T (top)
...@@ -146,7 +153,7 @@ namespace scribo ...@@ -146,7 +153,7 @@ namespace scribo
// D // D
internal::compute_stats(d_ima->first(), internal::compute_stats(d_ima->first(),
d_ima->second(), d_ima->second(),
size_tl, size_tl * s_2,
mean, stddev); mean, stddev);
functor.exec(mean, stddev); functor.exec(mean, stddev);
d_ima += step; d_ima += step;
...@@ -166,7 +173,7 @@ namespace scribo ...@@ -166,7 +173,7 @@ namespace scribo
// D - C // D - C
internal::compute_stats(d_ima->first() - c_ima->first(), internal::compute_stats(d_ima->first() - c_ima->first(),
d_ima->second() - c_ima->second(), d_ima->second() - c_ima->second(),
size_tc, size_tc * s_2,
mean, stddev); mean, stddev);
functor.exec(mean, stddev); functor.exec(mean, stddev);
c_ima += step; c_ima += step;
...@@ -188,7 +195,7 @@ namespace scribo ...@@ -188,7 +195,7 @@ namespace scribo
// D* - C // D* - C
internal::compute_stats(d_sum - c_ima->first(), internal::compute_stats(d_sum - c_ima->first(),
d_sum_2 - c_ima->second(), d_sum_2 - c_ima->second(),
size_tr, size_tr * s_2,
mean, stddev); mean, stddev);
functor.exec(mean, stddev); functor.exec(mean, stddev);
c_ima += step; c_ima += step;
...@@ -239,7 +246,7 @@ namespace scribo ...@@ -239,7 +246,7 @@ namespace scribo
// D - B // D - B
internal::compute_stats(d_ima->first() - b_ima->first(), internal::compute_stats(d_ima->first() - b_ima->first(),
d_ima->second() - b_ima->second(), d_ima->second() - b_ima->second(),
size_ml, size_ml * s_2,
mean, stddev); mean, stddev);
functor.exec(mean, stddev); functor.exec(mean, stddev);
b_ima += step; b_ima += step;
...@@ -258,11 +265,40 @@ namespace scribo ...@@ -258,11 +265,40 @@ namespace scribo
for (; col <= max_col_mid; col += step) for (; col <= max_col_mid; col += step)
{ {
// D + A - B - C // D + A - B - C
// if (row == 3 && col == 3)
// std::cout << "p(" << row << "," << col << ") - "
// << "A" << ima.point_at_index(a_ima - ima.buffer())
// << "=" << a_ima->first() << " - "
// << "B" << ima.point_at_index(b_ima - ima.buffer())
// << "=" << b_ima->first() << " - "
// << "C" << ima.point_at_index(c_ima - ima.buffer())
// << "=" << c_ima->first() << " - "
// << "D" << ima.point_at_index(d_ima - ima.buffer())
// << "=" << d_ima->first() << " - "
// << "n =" << size_mc << " - "
// << "n*s_2 =" << size_mc * s_2
// << std::endl;
internal::compute_stats((d_ima->first() - b_ima->first()) + (a_ima->first() - c_ima->first()), internal::compute_stats((d_ima->first() - b_ima->first()) + (a_ima->first() - c_ima->first()),
(d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()), (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()),
size_mc, size_mc * s_2,
mean, stddev); mean, stddev);
functor.exec(mean, stddev); functor.exec(mean, stddev);
// std::cout << " - " << mean
// << " - " << stddev
// << " - " << (d_ima->first() - b_ima->first()) + (a_ima->first() - c_ima->first())
// << " - " << (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second())
// << std::endl;
a_ima += step; a_ima += step;
b_ima += step; b_ima += step;
c_ima += step; c_ima += step;
...@@ -283,7 +319,7 @@ namespace scribo ...@@ -283,7 +319,7 @@ namespace scribo
// D* + A - B* - C // D* + A - B* - C
internal::compute_stats(d_b_sum + (a_ima->first() - c_ima->first()), internal::compute_stats(d_b_sum + (a_ima->first() - c_ima->first()),
d_b_sum_2 + (a_ima->second() - c_ima->second()), d_b_sum_2 + (a_ima->second() - c_ima->second()),
size_mr, size_mr * s_2,
mean, stddev); mean, stddev);
functor.exec(mean, stddev); functor.exec(mean, stddev);
a_ima += step; a_ima += step;
...@@ -332,7 +368,7 @@ namespace scribo ...@@ -332,7 +368,7 @@ namespace scribo
// D* - B // D* - B
internal::compute_stats(d_ima->first() - b_ima->first(), internal::compute_stats(d_ima->first() - b_ima->first(),
d_ima->second() - b_ima->second(), d_ima->second() - b_ima->second(),
size_bl, size_bl * s_2,
mean, stddev); mean, stddev);
functor.exec(mean, stddev); functor.exec(mean, stddev);
b_ima += step; b_ima += step;
...@@ -354,7 +390,7 @@ namespace scribo ...@@ -354,7 +390,7 @@ namespace scribo
// D* + A - B - C* // D* + A - B - C*
internal::compute_stats((d_ima->first() - b_ima->first()) + (a_ima->first() - c_ima->first()), internal::compute_stats((d_ima->first() - b_ima->first()) + (a_ima->first() - c_ima->first()),
(d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()), (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()),
size_bc, size_bc * s_2,
mean, stddev); mean, stddev);
// std::cout << (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()) << std::endl; // std::cout << (d_ima->second() - b_ima->second()) + (a_ima->second() - c_ima->second()) << std::endl;
...@@ -385,7 +421,7 @@ namespace scribo ...@@ -385,7 +421,7 @@ namespace scribo
// D* + A - B* - C* // D* + A - B* - C*
internal::compute_stats(d_b_sum + (a_ima->first() - c_ima->first()), internal::compute_stats(d_b_sum + (a_ima->first() - c_ima->first()),
d_b_sum_2 + (a_ima->second() - c_ima->second()), d_b_sum_2 + (a_ima->second() - c_ima->second()),
size_br, size_br * s_2,
mean, stddev); mean, stddev);
functor.exec(mean, stddev); functor.exec(mean, stddev);
a_ima += step; a_ima += step;
......
This diff is collapsed.
...@@ -107,7 +107,6 @@ namespace scribo ...@@ -107,7 +107,6 @@ namespace scribo
const I& input = exact(input_); const I& input = exact(input_);
J& integral_sum_sum_2 = exact(integral_sum_sum_2_); J& integral_sum_sum_2 = exact(integral_sum_sum_2_);
const unsigned area = scale * scale;
mln_precondition(input.is_valid()); mln_precondition(input.is_valid());
mln_precondition(input.domain().pmin() == literal::origin); mln_precondition(input.domain().pmin() == literal::origin);
...@@ -142,19 +141,22 @@ namespace scribo ...@@ -142,19 +141,22 @@ namespace scribo
const V* ptr3 = & input.at_(row + 2, 0); const V* ptr3 = & input.at_(row + 2, 0);
for (unsigned col = 0; col < ncols; col += scale) for (unsigned col = 0; col < ncols; col += scale)
{ {
S sum; V v11 = *ptr1, v12 = *(ptr1 + 1), v13 = *(ptr1 + 2),
sum = *ptr1 + *(ptr1 + 1) + *(ptr1 + 2); v21 = *ptr2, v22 = *(ptr2 + 1), v23 = *(ptr2 + 2),
sum += *ptr2 + *(ptr2 + 1) + *(ptr2 + 2); v31 = *ptr3, v32 = *(ptr3 + 1), v33 = *(ptr3 + 2);
sum += *ptr3 + *(ptr3 + 1) + *(ptr3 + 2);
ptr1 += 3; ptr1 += 3;
ptr2 += 3; ptr2 += 3;
ptr3 += 3; ptr3 += 3;
S local_sum = v11 + v12 + v13
+ v21 + v22 + v23
+ v31 + v32 + v33,
local_sum_2 = v11*v11 + v12*v12 + v13*v13
+ v21*v21 + v22*v22 + v23*v23
+ v31*v31 + v32*v32 + v33*v33;
S val = sum / area; *p_sub++ = local_sum / 9;
*p_sub++ = val; h_sum += local_sum;
h_sum_2 += local_sum_2;
h_sum += val;
h_sum_2 += val * val;
// exception // exception
p_integ->first() = h_sum; p_integ->first() = h_sum;
...@@ -172,24 +174,27 @@ namespace scribo ...@@ -172,24 +174,27 @@ namespace scribo
for (row += scale; row < nrows; row += scale) for (row += scale; row < nrows; row += scale)
{ {
S h_sum = 0, h_sum_2 = 0; S h_sum = 0, h_sum_2 = 0;
const V* ptr1 = & input.at_(row, 0); const V* ptr1 = & input.at_(row, 0);
const V* ptr2 = & input.at_(row + 1, 0); const V* ptr2 = & input.at_(row + 1, 0);
const V* ptr3 = & input.at_(row + 2, 0); const V* ptr3 = & input.at_(row + 2, 0);
for (unsigned col = 0; col < ncols; col += scale) for (unsigned col = 0; col < ncols; col += scale)
{ {
S sum; V v11 = *ptr1, v12 = *(ptr1 + 1), v13 = *(ptr1 + 2),
sum = *ptr1 + *(ptr1 + 1) + *(ptr1 + 2); v21 = *ptr2, v22 = *(ptr2 + 1), v23 = *(ptr2 + 2),
sum += *ptr2 + *(ptr2 + 1) + *(ptr2 + 2); v31 = *ptr3, v32 = *(ptr3 + 1), v33 = *(ptr3 + 2);
sum += *ptr3 + *(ptr3 + 1) + *(ptr3 + 2);
ptr1 += 3; ptr1 += 3;
ptr2 += 3; ptr2 += 3;
ptr3 += 3; ptr3 += 3;
S local_sum = v11 + v12 + v13
+ v21 + v22 + v23
+ v31 + v32 + v33,
local_sum_2 = v11*v11 + v12*v12 + v13*v13
+ v21*v21 + v22*v22 + v23*v23
+ v31*v31 + v32*v32 + v33*v33;
S val = sum / area; *p_sub++ = local_sum / 9;
*p_sub++ = val; h_sum += local_sum;
h_sum_2 += local_sum_2;
h_sum += val;
h_sum_2 += val * val;
p_integ->first() = h_sum + (p_integ + up)->first(); p_integ->first() = h_sum + (p_integ + up)->first();
p_integ->second() = h_sum_2 + (p_integ + up)->second(); p_integ->second() = h_sum_2 + (p_integ + up)->second();
...@@ -220,7 +225,6 @@ namespace scribo ...@@ -220,7 +225,6 @@ namespace scribo
const I& input = exact(input_); const I& input = exact(input_);
J& integral_sum_sum_2 = exact(integral_sum_sum_2_); J& integral_sum_sum_2 = exact(integral_sum_sum_2_);
const unsigned area = scale * scale;
typedef mln_value(I) V; typedef mln_value(I) V;
typedef mln_sum(V) S; typedef mln_sum(V) S;
...@@ -257,6 +261,7 @@ namespace scribo ...@@ -257,6 +261,7 @@ namespace scribo
const V* ptr2 = & input.at_(row + 1, 0); const V* ptr2 = & input.at_(row + 1, 0);
for (unsigned col = 0; col < ncols; col += scale) for (unsigned col = 0; col < ncols; col += scale)
{ {
/*
S sum; S sum;
sum = *ptr1 + *(ptr1 + 1); sum = *ptr1 + *(ptr1 + 1);
sum += *ptr2 + *(ptr2 + 1); sum += *ptr2 + *(ptr2 + 1);
...@@ -268,6 +273,22 @@ namespace scribo ...@@ -268,6 +273,22 @@ namespace scribo
h_sum += val; h_sum += val;
h_sum_2 += val * val; h_sum_2 += val * val;
*/
// NEW:
V v11 = *ptr1, v12 = *(ptr1 + 1),
v21 = *ptr2, v22 = *(ptr2 + 1);
ptr1 += 2;
ptr2 += 2;
S local_sum = v11 + v12 + v21 + v22,
local_sum_2 = v11*v11 + v12*v12 + v21*v21 + v22*v22;
*p_sub++ = local_sum / 4;
h_sum += local_sum;
h_sum_2 += local_sum_2;
// end of NEW.
// exception // exception
p_integ->first() = h_sum; p_integ->first() = h_sum;
...@@ -289,6 +310,29 @@ namespace scribo ...@@ -289,6 +310,29 @@ namespace scribo
const V* ptr2 = & input.at_(row + 1, 0); const V* ptr2 = & input.at_(row + 1, 0);
for (unsigned col = 0; col < ncols; col += scale) for (unsigned col = 0; col < ncols; col += scale)
{ {
// NEW:
V v11 = *ptr1, v12 = *(ptr1 + 1),