
https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox Index: ChangeLog from Thierry Geraud <thierry.geraud@lrde.epita.fr> New source in ICDAR-HSC to go from input to lines. * icdar/2009/hsc/input_to_lines.cc: Rename as... * icdar/2009/hsc/old_input_to_lines.cc: ...this. * icdar/2009/hsc/input_to_wsl.cc: New. * icdar/2009/hsc/ws_to_wsl.hh: New. input_to_wsl.cc | 130 +++++++++++++++++++ ws_to_wsl.hh | 373 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 503 insertions(+) Index: icdar/2009/hsc/input_to_wsl.cc --- icdar/2009/hsc/input_to_wsl.cc (revision 0) +++ icdar/2009/hsc/input_to_wsl.cc (revision 0) @@ -0,0 +1,130 @@ +#include <set> +#include <vector> + +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image_if.hh> +#include <mln/core/alias/neighb2d.hh> + +#include <mln/pw/all.hh> +#include <mln/data/fill.hh> +#include <mln/level/saturate.hh> +#include <mln/level/convert.hh> +#include <mln/arith/revert.hh> + +#include <mln/value/int_u8.hh> +#include <mln/value/rgb8.hh> +#include <mln/literal/colors.hh> +#include <mln/debug/colorize.hh> + +#include <mln/io/pbm/load.hh> +#include <mln/io/pbm/save.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/ppm/save.hh> + +#include <mln/world/binary_2d/subsample.hh> + +#define MLN_FLOAT double +#include <sandbox/theo/exec/gaussian_directional_2d.hh> + +#include <mln/morpho/closing/structural.hh> +#include <mln/morpho/watershed/flooding.hh> +#include <mln/win/rectangle2d.hh> + +#include <sandbox/icdar/2009/hsc/ws_to_wsl.hh> + + + + + +void usage(char* argv[]) +{ + std::cerr << "usage: " << argv[0] << " input.pbm output.pgm [output.ppm]" << std::endl + << " HSC @ ICDAR'2009" << std::endl + << " input.pbm: input 2D binary image (text is black; background is white)" << std::endl + << " output.pgm: output image where line components are labeled (int_u8)" << std::endl + << " 0 is the background label." << std::endl; + std::abort(); +} + + + +int main(int argc, char* argv[]) +{ + using namespace mln; + using value::int_u8; + using value::rgb8; + + if (argc != 3 && argc != 4) + usage(argv); + + + // Parameters. + + const unsigned + subsampling_factor = 4, + height = 5, + width = 25, + n_min_stats = 1000; + const float + h_sigma = 31, + v_sigma = 1.3; + + // end of Parameters. + + + trace::entering("main"); + + image2d<bool> input; + io::pbm::load(input, argv[1]); + + // Sub-sampling. + image2d<int_u8> + small = world::binary_2d::subsample(input, subsampling_factor), + fuzzy, clo, + ws, + spc; + + +# ifdef LOG + io::pgm::save(small, "tmp_small.pgm"); +# endif + + + // Fuzzifying. + { + image2d<MLN_FLOAT> temp(small.domain()), out; + data::fill(temp, small); + + out = linear::gaussian_directional_2d(temp, 1, h_sigma, 0); + out = linear::gaussian_directional_2d(out, 0, v_sigma, 0); + + fuzzy = level::saturate(int_u8(), out); + +# ifdef LOG + io::pgm::save(fuzzy, "tmp_fuzzy.pgm"); +# endif + } + + + clo = morpho::closing::structural(fuzzy, win::rectangle2d(height, width)); + +# ifdef LOG + io::pgm::save(clo, "tmp_clo.pgm"); +# endif + + int_u8 n_basins; + ws = morpho::watershed::flooding(clo, c4(), n_basins); + + + image2d<int_u8> wsl = ws_to_wslines(input, small, ws, n_basins); + + io::pgm::save(wsl, argv[2]); + + if (argc == 4) + { + io::ppm::save(debug::colorize(rgb8(), wsl, n_basins), + argv[3]); + } + + trace::exiting("main"); +} Index: icdar/2009/hsc/ws_to_wsl.hh --- icdar/2009/hsc/ws_to_wsl.hh (revision 0) +++ icdar/2009/hsc/ws_to_wsl.hh (revision 0) @@ -0,0 +1,373 @@ +#include <mln/core/var.hh> + +#include <mln/core/image/image2d.hh> +#include <mln/core/alias/neighb2d.hh> + +#include <mln/pw/all.hh> + +#include <mln/value/int_u8.hh> + +#include <mln/util/array.hh> +#include <mln/level/transform.hh> + +#include <mln/accu/internal/base.hh> + +#include <mln/algebra/vec.hh> +#include <mln/algebra/mat.hh> + +#include <mln/make/region_adjacency_graph.hh> + +#include <mln/core/site_set/p_edges.hh> +#include <mln/core/site_set/p_vertices.hh> +#include <mln/core/image/graph_elt_neighborhood.hh> + +#include <mln/morpho/elementary/dilation.hh> + + + + + + +namespace mln +{ + + + algebra::mat<1,2,float> + trans(const algebra::vec<2,float>& v) + { + algebra::mat<1,2,float> tmp; + tmp(0,0) = v[0]; + tmp(0,1) = v[1]; + return tmp; + } + + algebra::mat<2,1,float> + to_mat(const algebra::vec<2,float>& v) + { + algebra::mat<2,1,float> tmp; + tmp(0,0) = v[0]; + tmp(1,0) = v[1]; + return tmp; + } + + + struct mahalanobis : public mln::accu::internal::base< unsigned , mahalanobis > + { + typedef point2d argument; + typedef unsigned result; + + mahalanobis() + { + } + + /// Manipulators. + /// \{ + void init() + { + valid_ = false; + n_ = 0; + mean_.set_all(0); + cov_.set_all(0); + } + + void take(const argument& p) + { + mln_invariant(0); + } + + void take(const mahalanobis& other) + { + mln_invariant(0); + } + + void take(unsigned n_times, const argument& p) + { + n_ += n_times; + float + r = p.row(), + c = p.col(); + mean_[0] += r * n_times; + mean_[1] += c * n_times; + cov_(0, 0) += r * r * n_times; + cov_(0, 1) += r * c * n_times; + cov_(1, 0) += c * r * n_times; + cov_(1, 1) += c * c * n_times; + } + + void finalize() + { + mln_invariant(! valid_); + valid_ = true; + if (n_ == 0) + return; + mean_ /= n_; + algebra::mat<2,1,float> mu; + cov_ = cov_ / n_ - to_mat(mean_) * trans(mean_); + + float + a = cov_(0, 0), b = cov_(0, 1), + c = cov_(1, 0), d = cov_(1, 1), + r = a * d - b * c; + if (r > -0.001f && r < 0.001f) // too close to 0 + { + n_ = 0; // invalidation + return; + } + cov_1_(0, 0) = + d / r; + cov_1_(0, 1) = - b / r; + cov_1_(1, 0) = - c / r; + cov_1_(1, 1) = + a / r; + } + + /// Get the accumulator result; here DUMMY !!! + unsigned to_result() const + { + mln_invariant(0); + return n_; + } + + /// Get the mean vector. + const algebra::vec<2,float>& mean() const + { + mln_invariant(valid_); + return mean_; + } + + /// Get the center column. + def::coord col() const + { + mln_invariant(valid_); + return static_cast<def::coord>(0.49999f + mean_[1]); // col = coord #1 + } + + /// Get the covariance matrix. + const algebra::mat<2,2,float>& cov() const + { + mln_invariant(valid_); + return cov_; + } + + /// Get the inverse of the covariance matrix. + const algebra::mat<2,2,float>& cov_1() const + { + mln_invariant(valid_); + return cov_1_; + } + + float dist(const algebra::vec<2,float>& v) const + { + mln_invariant(valid_); + algebra::vec<2,float> v_ = v - mean_; + return (trans(v_) * cov_1_ * v_)[0]; + } + + /// Get the number of items. + unsigned n() const + { + return n_; + } + + /// Check whether this accu is able to return a result. + /// Always true here. + bool is_valid() const + { + return valid_; + } + + protected: + + bool valid_; + unsigned n_; + algebra::vec<2,float> mean_; + algebra::mat<2,2,float> cov_, cov_1_; + }; + + + + template <typename L> + util::array<mahalanobis> + mahalanobis_from_images(const image2d<L>& ws, + const image2d<value::int_u8>& small, + L n_labels) + { + util::array<mahalanobis> arr(n_labels + 1); + for (unsigned l = 0; l <= n_labels; ++l) + arr[l].init(); + mln_piter(box2d) p(ws.domain()); + for_all(p) + { + if (ws(p) == 0 || small(p) == 255) + continue; + arr[ws(p)].take(255 - small(p), p); + } + + for (unsigned l = 0; l <= n_labels; ++l) + arr[l].finalize(); + + return arr; + } + + + float dist(const mahalanobis& m1, const mahalanobis& m2) + { + return std::min(m1.dist(m2.mean()), m2.dist(m1.mean())); + } + + + template <typename L> + inline + L find_root_(util::array<L>& parent, L l) + { + if (parent[l] == l) + return l; + return parent[l] = find_root_(parent, parent[l]); + } + + + + + template <typename L> + image2d<L> + ws_to_wslines(const image2d<bool>& input, + const image2d<value::int_u8>& small, + const image2d<L>& ws, + L n_basins) + { + typedef util::graph G; + + G gr = make::region_adjacency_graph(ws, c8(), n_basins); + + util::array<mahalanobis> m; + m = mahalanobis_from_images(ws, small, n_basins); + + typedef pw::cst_<int> P; + P no_f(0); + p_edges<G,P> pe(gr, no_f); + + + mln_VAR(d, fun::i2v::array<float>(gr.e_nmax()) | pe); + + + // Computing 'd'. + + { + mln_piter_(d_t) e(pe); + for_all(e) + { + unsigned + i1 = e.element().v1(), + i2 = e.element().v2(); + const mahalanobis + & m1 = m[i1], + & m2 = m[i2]; + + if (m1.n() > 1000 && m2.n() > 1000) + d(e) = dist(m1, m2); + else + d(e) = 666.f; + } + } + + + util::array<unsigned> + v_left (n_basins + 1), + v_right(n_basins + 1); + + + // Fill in v_left and v_right. + + { + + mln_vertex_iter_(G) v(gr); + mln_vertex_nbh_edge_iter_(G) e(v); + for_all(v) + { + int v_col = m[v.id()].col(); + float + d_left_min = 666.f, + d_right_min = 666.f; + unsigned + v_left_best = 0, + v_right_best = 0; + for_all(e) + { + unsigned v2 = e.v_other(v); + float d_ = d.function()(e); + if (m[v2].col() < v_col) + { + // v2 <-- v + if (d_ < d_left_min) + { + d_left_min = d_; + v_left_best = v2; + } + } + else + { + // v --> v2 + if (d_ < d_right_min) + { + d_right_min = d_; + v_right_best = v2; + } + } + } + v_left [v.id()] = v_left_best; + v_right[v.id()] = v_right_best; + } + + } + + + + util::array<L> parent(n_basins + 1); + + // Computing parent. + + { + for (L l = 0; l <= n_basins; ++l) + parent[l] = l; + + for (L l = 1; l <= n_basins; ++l) + { + if (v_right[l] == 0) + continue; + unsigned r = v_right[l]; + if (v_left[r] != l) + { + // std::cout << '.' << std::endl; + continue; + } + // std::cout << l << " -> " << r << std::endl; + parent[l] = r; + } + + for (L l = 1; l <= n_basins; ++l) + parent[l] = find_root_(parent, l); + } + + + // Outputing. + + image2d<L> output(input.domain()); + + { + unsigned s = input.nrows() / small.nrows(); + + image2d<L> ws_nol = morpho::elementary::dilation(ws, c8()); + + mln_piter(box2d) p(input.domain()); + for_all(p) + if (input(p)) + output(p) = 0; + else + { + L l = ws_nol.at_(p.row() / s, p.col() / s); + output(p) = parent[l]; + } + } + + return output; + } + +} // ! mln