
--- milena/mln/core/internal/image_base.hh | 33 ++++--- scribo/scribo/binarization/sauvola_ms.hh | 43 ++++++++- scribo/scribo/canvas/integral_browsing.hh | 8 ++- scribo/scribo/estim/font_color.hh | 4 +- scribo/src/binarization/Makefile.am | 7 ++ scribo/src/binarization/kim.cc | 143 +++++++++++++---------------- scribo/src/binarization/niblack.cc | 5 +- scribo/src/binarization/sauvola_ms.cc | 17 +++- 8 files changed, 156 insertions(+), 104 deletions(-) diff --git a/milena/mln/core/internal/image_base.hh b/milena/mln/core/internal/image_base.hh index f1ba4f9..6b60121 100644 --- a/milena/mln/core/internal/image_base.hh +++ b/milena/mln/core/internal/image_base.hh @@ -1,4 +1,4 @@ -// Copyright (C) 2007, 2008, 2009 EPITA Research and Development +// Copyright (C) 2007, 2008, 2009, 2012 EPITA Research and Development // Laboratory (LRDE) // // This file is part of Olena. @@ -43,18 +43,6 @@ # include <mln/value/set.hh> # include <mln/value/super_value.hh> -// image_base -// ^ -// | -// --------------------------- -// | | -// image_primary image_morpher -// ^ ^ -// | | -// | ----------------------------------------- -// | | | | -// pw_image_base image_domain_morpher image_value_morpher image_identity - namespace mln { @@ -73,10 +61,27 @@ namespace mln - /// A base class for images. + /// \brief A base class for images. /// /// Parameter \p T is the image value type. /// Parameter \p S is the image site set type. + /// + /// \internal + /// + /// \verbatim + /// image_base + /// ^ + /// | + /// --------------------------- + /// | | + /// image_primary image_morpher + /// ^ ^ + /// | | + /// | ----------------------------------------- + /// | | | | + /// pw_image_base image_domain_morpher image_value_morpher image_identity + /// + /// \endverbatim // template <typename T, typename S, typename E> struct image_base diff --git a/scribo/scribo/binarization/sauvola_ms.hh b/scribo/scribo/binarization/sauvola_ms.hh index 96064e9..ba58207 100644 --- a/scribo/scribo/binarization/sauvola_ms.hh +++ b/scribo/scribo/binarization/sauvola_ms.hh @@ -98,7 +98,8 @@ namespace scribo \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. - + \param[out] integral_sum_sum_2 Integral image of sum and squared + sum. \p w_1 and \p lambda_min_1 are expressed according to the image at scale 0, i.e. the original size. @@ -107,6 +108,15 @@ namespace scribo */ template <typename I> mln_ch_value(I,bool) + sauvola_ms(const Image<I>& input_1_, unsigned w_1, + unsigned s, double K, + image2d<mln::util::couple<double,double> >& integral_sum_sum_2); + + /// \overload + /// The integral image is not returned. + // + template <typename I> + mln_ch_value(I,bool) sauvola_ms(const Image<I>& input_1_, unsigned w_1, unsigned s, double K); /// \overload @@ -802,7 +812,8 @@ namespace scribo template <typename I> mln_ch_value(I,bool) sauvola_ms(const Image<I>& input_1_, unsigned w_1, - unsigned s, double K) + unsigned s, double K, + image2d<mln::util::couple<double,double> >& integral_sum_sum_2) { trace::entering("scribo::binarization::sauvola_ms"); @@ -853,7 +864,7 @@ namespace scribo // Resize input and compute integral images. typedef image2d<mln::util::couple<double,double> > integral_t; - integral_t integral_sum_sum_2; +// integral_t integral_sum_sum_2; mln::util::timer t; t.start(); @@ -1030,6 +1041,27 @@ namespace scribo template <typename I> mln_ch_value(I,bool) sauvola_ms(const Image<I>& input_1_, unsigned w_1, + unsigned s, double K, + image2d<mln::util::couple<double,double> >& integral_sum_sum_2) + { + trace::entering("scribo::binarization::sauvola_ms"); + + mln_precondition(exact(input_1_).is_valid()); + // Gray level images ONLY. + mlc_is_not_a(mln_value(I), value::Vectorial)::check(); + mlc_is_not(mln_value(I), bool)::check(); + + mln_ch_value(I,bool) + output = impl::generic::sauvola_ms(exact(input_1_), w_1, s, K, + integral_sum_sum_2); + + 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, double K) { trace::entering("scribo::binarization::sauvola_ms"); @@ -1039,8 +1071,11 @@ namespace scribo mlc_is_not_a(mln_value(I), value::Vectorial)::check(); mlc_is_not(mln_value(I), bool)::check(); + typedef image2d<mln::util::couple<double,double> > integral_t; + integral_t integral_sum_sum_2; + mln_ch_value(I,bool) - output = impl::generic::sauvola_ms(exact(input_1_), w_1, s, K); + output = sauvola_ms(exact(input_1_), w_1, s, K, integral_sum_sum_2); trace::exiting("scribo::binarization::sauvola_ms"); return output; diff --git a/scribo/scribo/canvas/integral_browsing.hh b/scribo/scribo/canvas/integral_browsing.hh index 89e0019..da13fe7 100644 --- a/scribo/scribo/canvas/integral_browsing.hh +++ b/scribo/scribo/canvas/integral_browsing.hh @@ -116,6 +116,12 @@ namespace scribo for (col = col_0; col <= max_col_mid; col += step) ; int w_right = ncols - col + w/2; + // tl: top left + // tr: top right + // ml: middle left + // mr: middle right + // bl: bottom left + // br: bottom right Ptr d_tl_start, d_tr_start, b_ml_start = 0, d_ml_start = 0, b_mr_start = 0, d_mr_start = 0, @@ -176,7 +182,7 @@ namespace scribo for (; col <= max_col_mid; col += step) { // 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(), size_tc * s_2, mean, stddev); diff --git a/scribo/scribo/estim/font_color.hh b/scribo/scribo/estim/font_color.hh index 9a3c429..78e8c83 100644 --- a/scribo/scribo/estim/font_color.hh +++ b/scribo/scribo/estim/font_color.hh @@ -83,7 +83,7 @@ namespace scribo mln_ch_value(I,value::int_u8) lbl = labeling::blobs(skel, c8(), nlabels); - util::array<algebra::vec<3u, float> > res = + mln::util::array<algebra::vec<3u, float> > res = labeling::compute(accu::meta::stat::mean(), text_ima, lbl, nlabels); accu::stat::median_h<value::int_u12> m_red; @@ -131,7 +131,7 @@ namespace scribo value::int_u8 nlabels = 0; mln_ch_value(J,value::int_u8) lbl = labeling::blobs(skel, c8(), nlabels); - util::array<algebra::vec<3u, float> > res = + mln::util::array<algebra::vec<3u, float> > res = labeling::compute(accu::meta::stat::mean(), text_ima, lbl, nlabels); accu::stat::median_h<value::int_u12> m_val; diff --git a/scribo/src/binarization/Makefile.am b/scribo/src/binarization/Makefile.am index b29ac30..db168fa 100644 --- a/scribo/src/binarization/Makefile.am +++ b/scribo/src/binarization/Makefile.am @@ -30,6 +30,7 @@ if HAVE_MAGICKXX sauvola_ms_debug utilexec_PROGRAMS = \ + kim \ niblack \ otsu \ sauvola \ @@ -66,6 +67,12 @@ if HAVE_MAGICKXX sauvola_ms_fg_LDFLAGS = $(AM_LDFLAGS) \ $(MAGICKXX_LDFLAGS) + kim_SOURCES = kim.cc + kim_CPPFLAGS = $(AM_CPPFLAGS) \ + $(MAGICKXX_CPPFLAGS) + kim_LDFLAGS = $(AM_LDFLAGS) \ + $(MAGICKXX_LDFLAGS) + sauvola_SOURCES = sauvola.cc sauvola_CPPFLAGS = $(AM_CPPFLAGS) \ $(MAGICKXX_CPPFLAGS) diff --git a/scribo/src/binarization/kim.cc b/scribo/src/binarization/kim.cc index 7294a9c..aa81f50 100644 --- a/scribo/src/binarization/kim.cc +++ b/scribo/src/binarization/kim.cc @@ -77,6 +77,47 @@ static const scribo::debug::opt_data opt_desc[] = +double +compute_thres(const mln::image2d<mln::util::couple<double,double> >& integral_sum_sum_2, + int row, int col, unsigned win_size, + const scribo::binarization::internal::sauvola_formula& formula) +{ + point2d + tl(row - win_size - 1, + col - win_size - 1), + br(row + win_size, + col + win_size); + + box2d b(tl, br); + b.crop_wrt(integral_sum_sum_2.domain()); + + point2d tr = b.pmax(); + tr.row() = b.pmin().row(); + point2d bl = b.pmin(); + bl.row() = b.pmax().row(); + + unsigned card_min = b.nsites() - b.height() - b.width() + 1; + + const mln::util::couple<double,double>& + D = integral_sum_sum_2(b.pmax()), + B = integral_sum_sum_2(tr), + C = integral_sum_sum_2(bl), + A = integral_sum_sum_2(b.pmin()); + + double sum = D.first() - B.first() - C.first() + A.first(); + double sum_2 = D.second() - B.second() - C.second() + A.second(); + double mean = sum / card_min; + + double num = (sum_2 - sum * sum / card_min); + double stddev; + if (num > 0) + stddev = std::sqrt(num / (card_min - 1)); + else + stddev = 0; + + return formula(mean, stddev); +} + int main(int argc, char *argv[]) @@ -202,97 +243,43 @@ int main(int argc, char *argv[]) if (!lines(i).is_textline()) continue; - math::round<double> round; double win_min = thickness(lines(i).id()), - win_max = lines(i).bbox().height(), - card_min, - card_max; + win_max = lines(i).bbox().height();; mln_assertion(win_min != 0); mln_assertion(win_max != 0); - binarization::internal::sauvola_formula compute_thres; - point2d tl, br; + scribo::binarization::internal::sauvola_formula formula; - mln_piter(L) p(lines(i).bbox()); - for_all(p) + for (int row = lines(i).bbox().pmin().row(); + row <= lines(i).bbox().pmax().row(); + ++row) { + bool* out_ptr = &output.at_(row, lines(i).bbox().pmin().col()); + value::int_u8* in_ptr = &input_1_gl.at_(row, lines(i).bbox().pmin().col()); + for (int col = lines(i).bbox().pmin().col(); + col <= lines(i).bbox().pmax().col(); + ++col) + { + // Min case + double T_min = compute_thres(integral_sum_sum_2, row, col, win_min, formula); - // Min case - tl.row() = (p.row() - win_min - 1); - tl.col() = (p.col() - win_min - 1); - - br.row() = (p.row() + win_min); - br.col() = (p.col() + win_min); - - box2d b(tl, br); - b.crop_wrt(integral_sum_sum_2.domain()); - - point2d tr = b.pmax(); - tr.row() = b.pmin().row(); - point2d bl = b.pmin(); - bl.row() = b.pmax().row(); - - card_min = b.nsites() - b.height() - b.width() + 1; - - double sum = integral_sum_sum_2(b.pmax()).first() - integral_sum_sum_2(tr).first() - integral_sum_sum_2(bl).first() + integral_sum_sum_2(b.pmin()).first(); - double sum_2 = integral_sum_sum_2(b.pmax()).second() - integral_sum_sum_2(tr).second() - integral_sum_sum_2(bl).second() + integral_sum_sum_2(b.pmin()).second(); - double mean = sum / card_min; - - double num = (sum_2 - sum * sum / card_min); - double stddev = 0; - if (num > 0) - stddev = std::sqrt(num / (card_min - 1)); - else - stddev = 0; - - - double T_min = compute_thres(mean, stddev); - - // Max case - tl.row() = (p.row() - win_max - 1); - tl.col() = (p.col() - win_max - 1); - - br.row() = (p.row() + win_max); - br.col() = (p.col() + win_max); - - b = box2d(tl, br); - b.crop_wrt(integral_sum_sum_2.domain()); - - tr = b.pmax(); - tr.row() = b.pmin().row(); - bl = b.pmin(); - bl.row() = b.pmax().row(); - - card_max = b.nsites() - b.height() - b.width() + 1; - - sum = integral_sum_sum_2(b.pmax()).first() - integral_sum_sum_2(tr).first() - integral_sum_sum_2(bl).first() + integral_sum_sum_2(b.pmin()).first(); - sum_2 = integral_sum_sum_2(b.pmax()).second() - integral_sum_sum_2(tr).second() - integral_sum_sum_2(bl).second() + integral_sum_sum_2(b.pmin()).second(); - mean = sum / card_max; - - num = (sum_2 - sum * sum / card_max); - stddev = 0; - if (num > 0) - stddev = std::sqrt(num / (card_max - 1)); - else - stddev = 0; - - double T_max = compute_thres(mean, stddev); - - - // Final threshold + // Max case + double T_max = compute_thres(integral_sum_sum_2, row, col, win_max, formula); - double teta = 0.3; - double T = teta * T_max + (1 - teta) * T_min; + // Final threshold + double teta = 0.3; // Good results from 0.1 to 0.3 according + // to the paper. + double T = teta * T_max + (1 - teta) * T_min; - mln_assertion(T_min <= 255); - mln_assertion(T_max <= 255); - mln_assertion(T <= 255); + mln_assertion(T_min <= 255); + mln_assertion(T_max <= 255); + mln_assertion(T <= 255); - output(p) = input_1_gl(p) <= T; + *out_ptr++ = *in_ptr++ <= T; + } } - } lt.stop(); diff --git a/scribo/src/binarization/niblack.cc b/scribo/src/binarization/niblack.cc index 4b7ed91..58074fc 100644 --- a/scribo/src/binarization/niblack.cc +++ b/scribo/src/binarization/niblack.cc @@ -29,6 +29,8 @@ #include <mln/io/pbm/save.hh> #include <mln/data/transform.hh> #include <mln/fun/v2v/rgb_to_luma.hh> +#include <mln/arith/revert.hh> +#include <mln/logical/not.hh> #include <scribo/binarization/niblack.hh> #include <scribo/debug/option_parser.hh> @@ -98,8 +100,9 @@ int main(int argc, char *argv[]) image2d<value::int_u8> input_1_gl = data::transform(input, mln::fun::v2v::rgb_to_luma<value::int_u8>()); + arith::revert_inplace(input_1_gl); image2d<bool> out = scribo::binarization::niblack(input_1_gl, w, k); - + logical::not_inplace(out); io::pbm::save(out, options.arg("output.pbm")); trace::exiting("main"); diff --git a/scribo/src/binarization/sauvola_ms.cc b/scribo/src/binarization/sauvola_ms.cc index 483a35b..d07de72 100644 --- a/scribo/src/binarization/sauvola_ms.cc +++ b/scribo/src/binarization/sauvola_ms.cc @@ -109,15 +109,24 @@ int main(int argc, char *argv[]) unsigned s = atoi(options.opt_value("s").c_str()); double k = atof(options.opt_value("k").c_str()); + if (options.is_set("k")) + { + binarization::internal::k2 = k; + binarization::internal::k3 = k; + binarization::internal::k4 = k; + } + else + { + binarization::internal::k2 = atof(options.opt_value("k2").c_str()); + binarization::internal::k3 = atof(options.opt_value("k3").c_str()); + binarization::internal::k4 = atof(options.opt_value("k4").c_str()); + } + if (verbose) std::cout << "Using w_1=" << w_1 << " - s=" << s << " - k=" << k << std::endl; - binarization::internal::k2 = atof(options.opt_value("k2").c_str()); - binarization::internal::k3 = atof(options.opt_value("k3").c_str()); - binarization::internal::k4 = atof(options.opt_value("k4").c_str()); - // Load image2d<value::rgb8> input_1; -- 1.7.2.5
participants (1)
-
Guillaume Lazzara