3678: Grouping ICDAR files into appropriate directories.

https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox Index: ChangeLog from Thierry Geraud <thierry.geraud@lrde.epita.fr> Grouping ICDAR files into appropriate directories. Dibco stuff. * icdar/2009/dibco: New directory. * icdar/2009/README: New. * theo/icdar/dibco/main.cc: Remove, obsolete. This method can be found in... * scribo/binarization/simple.hh: ...this file. * theo/icdar/dibco/wst-based.sh, * lazzara/icdar/quasi.cc, * lazzara/icdar/binarization.cc: Rename as... * icdar/2009/dibco/wst-based.sh, * icdar/2009/dibco/quasi.cc, * icdar/2009/dibco/binarization.cc: ...these. * icdar/2009/dibco/svg.1.binarization.cc: New. * icdar/2009/dibco/svg.2.binarization.cc: New. * icdar/2009/dibco/svg.3.binarization.cc: New. Hsc stuff. * icdar/2009/hsc/first_attempts: New directory. * theo/icdar/hsc/fuzzy.sh, * theo/icdar/hsc/reduce.sh, * theo/icdar/hsc/exec/iz_lines.cc, * theo/icdar/hsc/exec/relabel_lines.cc, * theo/icdar/hsc/exec/show_lines_pbm.cc, * theo/icdar/hsc/exec/show_lines.cc, * theo/icdar/hsc/boxes.cc, * theo/icdar/hsc/words.sh, * theo/icdar/hsc/main.cc, * theo/icdar/hsc/dist.sh: Rename as... * icdar/2009/hsc/first_attempts/fuzzy.sh, * icdar/2009/hsc/first_attempts/reduce.sh, * icdar/2009/hsc/first_attempts/boxes.cc, * icdar/2009/hsc/first_attempts/exec, * icdar/2009/hsc/first_attempts/words.sh, * icdar/2009/hsc/first_attempts/main.cc, * icdar/2009/hsc/first_attempts/dist.sh: ...these. Get rid of private stuff. * theo/icdar/hsc, * theo/icdar/hsc/exec, * theo/icdar/dibco, * theo/icdar, * lazzara/icdar: Remove directories. README | 13 dibco/svg.1.binarization.cc | 464 +++++++++++++++++++++++ dibco/svg.2.binarization.cc | 737 +++++++++++++++++++++++++++++++++++++ dibco/svg.3.binarization.cc | 858 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 2072 insertions(+) Property changes on: icdar/2009/dibco/wst-based.sh ___________________________________________________________________ Added: svn:mergeinfo Property changes on: icdar/2009/dibco/quasi.cc ___________________________________________________________________ Added: svn:mergeinfo Index: icdar/2009/dibco/svg.1.binarization.cc --- icdar/2009/dibco/svg.1.binarization.cc (revision 0) +++ icdar/2009/dibco/svg.1.binarization.cc (revision 0) @@ -0,0 +1,464 @@ +#include <mln/core/var.hh> + +#include <mln/core/image/image1d.hh> +#include <mln/core/alias/neighb1d.hh> + +#include <mln/core/image/image2d.hh> +#include <mln/core/alias/neighb2d.hh> +#include <mln/core/site_set/p_queue.hh> + +#include <mln/pw/all.hh> +#include <mln/core/image/image_if.hh> + +#include <mln/data/fill.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/io/essential.hh> +#include <mln/value/int_u8.hh> +#include <mln/value/int_u16.hh> +#include <mln/value/label_8.hh> +#include <mln/value/label_16.hh> +#include <mln/value/rgb8.hh> +#include <mln/histo/compute.hh> + +#include <mln/morpho/elementary/gradient_internal.hh> +#include <mln/morpho/closing/volume.hh> +#include <mln/morpho/closing/height.hh> +#include <mln/morpho/watershed/flooding.hh> + +#include <mln/make/rag_and_labeled_wsl.hh> +#include <mln/util/graph.hh> +#include <mln/util/couple.hh> +#include <mln/debug/colorize.hh> +#include <mln/fun/i2v/array.hh> + +#include <mln/labeling/compute.hh> +#include <mln/labeling/regional_minima.hh> + +# include <mln/linear/gaussian_1d.hh> + +#include <mln/accu/center.hh> +#include <mln/accu/median_h.hh> +#include <mln/accu/mean.hh> + +#include <mln/math/abs.hh> +#include <mln/literal/colors.hh> +#include <mln/debug/draw_graph.hh> + + + + + +namespace mln +{ + + struct edge_values : Function_vv2v<edge_values> + { + typedef value::int_u8 result; + typedef void mutable_result; + + value::int_u8 operator()(const value::int_u8& v1, + const value::int_u8& v2) const + { return math::abs(v1 - v2); } + + }; + + + template <typename I, typename V> + class edge_to_color : public Function_p2v< edge_to_color<I,V> > + { + public: + typedef V result; + + edge_to_color(const I& ima_e) : ima_e_(ima_e) + {} + + V + operator()(const unsigned& e) const + { return convert::to<V>(ima_e_.function()(e)); } + + private: + const I& ima_e_; + }; + + + namespace make + { + + template <typename G, typename P> + p_edges<G,pw::cst_<P> > + dummy_pedges(const Graph<G>& g_, const P& dummy_value) + { + trace::entering("dummy_pedges"); + + const G& g = exact(g_); + mln_precondition(g.is_valid()); + p_edges<G,pw::cst_<P> > pe(g, pw::cst(dummy_value)); + + trace::exiting("dummy_pedges"); + return pe; + } + + template <typename FVV, typename G, typename V2P, typename E2P, typename FVE> + pw::image<fun::i2v::array<mln_result(FVE)>,p_edges<G,E2P> > + edge_image(const pw::image<FVV,p_vertices<G,V2P> >& vertex_image, + const p_edges<G,E2P>& pe, + const Function_vv2v<FVE>& edge_value_) + { + trace::entering("edge_image"); + + const FVE& edge_value = exact(edge_value_); + mln_precondition(vertex_image.is_valid()); + mln_precondition(pe.is_valid()); + + typedef fun::i2v::array<mln_result(FVE)> edge_values_t; + edge_values_t edge_values(pe.nsites()); + + // image on graph edges + typedef pw::image<edge_values_t, p_edges<G,E2P> > ima_e_t; + ima_e_t ima_e = (edge_values | pe); + + mln_piter(ima_e_t) e(ima_e.domain()); + for_all(e) + ima_e(e) = edge_value(vertex_image.function()(e.element().v1()), + vertex_image.function()(e.element().v2())); + + trace::exiting("edge_image"); + return ima_e; + } + + + template <typename W, typename G> + inline + p_vertices<util::graph, fun::i2v::array<mln_site(W)> > + common_pvertices(const Image<W>& wst, const mln_value(W)& nbasins, + const Graph<G>& g_) + { + trace::entering("common_pvertices"); + + const G& g = exact(g_); + mln_precondition(g.is_valid()); + + typedef fun::i2v::array<mln_site(W)> vertex_sites_t; + vertex_sites_t vertex_sites; + convert::from_to(labeling::compute(accu::center<mln_site(W)>(), wst, nbasins), + vertex_sites); + + p_vertices<util::graph, vertex_sites_t> pv(g, vertex_sites); + + trace::exiting("common_pvertices"); + return pv; + } + + + template <typename A, typename G, typename F, typename I, typename W> + pw::image<fun::i2v::array<mln_value(I)>, p_vertices<G,F> > + vertex_image(const Accumulator<A>& accu, + const p_vertices<G,F>& pv, + const Image<I>& input_, + const Image<W>& wst_, + const mln_value(W)& nbasins) + { + trace::entering("vertex_image"); + + const I& input = exact(input_); + const W& wst = exact(wst_); + mln_precondition(input.is_valid()); + mln_precondition(wst.is_valid()); + mln_precondition(pv.is_valid()); + + typedef fun::i2v::array<mln_value(I)> vertex_values_t; + vertex_values_t vertex_values; + convert::from_to(labeling::compute(accu, input, wst, nbasins), + vertex_values); + + pw::image<fun::i2v::array<mln_value(I)>, + p_vertices<util::graph, fun::i2v::array<mln_site(I)> > > + ima_vertex = vertex_values | pv; + + trace::exiting("vertex_image"); + return ima_vertex; + } + + + template <typename I, typename V, typename E> + inline + mln_concrete(I) + debug_graph_image(const Image<I>& input_, + const Image<V>& ima_v_, const Image<E>& ima_e_, + unsigned box_size, const mln_value(I)& bg) + { + trace::entering("debug_graph_image"); + + const I& input = exact(input_); + const V& ima_v = exact(ima_v_); + const E& ima_e = exact(ima_e_); + + mln_precondition(input.is_valid()); + mln_precondition(ima_v.is_valid()); + mln_precondition(ima_e.is_valid()); + + mln_concrete(I) output; + initialize(output, input); + + data::fill(output, bg); + debug::draw_graph(output, ima_v.domain(), + pw::cst(150u), + edge_to_color<E, mln_value(I)>(ima_e)); + + dpoint2d tl(-box_size,-box_size); + dpoint2d br(box_size,box_size); + mln_piter(V) p(ima_v.domain()); + for_all(p) + { + box<mln_site(I)> b(p + tl, p + br); + b.crop_wrt(output.domain()); + data::fill((output | b).rw(), convert::to<mln_value(I)>(ima_v(p))); + } + + trace::exiting("debug_graph_image"); + return output; + } + + } // end of namespace mln::make + + int + find_threshold(const histo::array<value::int_u8>& h) + { + + const float sigma = 5; // FIXME: hard-coded! + + util::array<point1d> c; + value::label_8 n; + + { + image1d<unsigned> h_, hs_; + image1d<value::label_8> l; + + convert::from_to(h, h_); + hs_ = linear::gaussian_1d(h_, sigma, 0); + l = labeling::regional_minima(hs_, c2(), n); + + { +// debug::println("l", l); +// debug::histo(h, "tmp_h.txt"); + +// std::ofstream file("tmp_hs.txt"); +// mln_piter_(box1d) p(h_.domain()); +// for_all(p) +// file << p.ind() << ' ' << hs_(p) << std::endl; +// file.close(); + } + + accu::center<point1d, point1d> a; + c = labeling::compute(a, l, n); + + c[0] = point1d(0); // Force a neutral value for the non-label value (0). + + // std::cout << "c = " << c << std::endl; + } + + + int threshold; + + { + std::vector<int> v; + for (unsigned i = 0; i <= n; ++i) + v.push_back(c[i].ind()); + std::sort(v.begin(), v.end()); + + for (unsigned i = 0; i <= n; ++i) + if (v[i] != 0) + { + threshold = v[i]; + break; + } + + std::cout << "threshold = " << threshold << std::endl; + } + + return threshold; + } + + + image2d<bool> contours(const image2d<value::int_u8>& f, unsigned& threshold) + { + using value::int_u8; + typedef int_u8 V; + histo::array<V> h; + + image2d<int_u8> g; + g = morpho::elementary::gradient_internal(f, c4()); + g = morpho::closing::height(g, c4(), 3); // FIXME: hard-coded! + + unsigned nbasins; + image2d<unsigned> w = morpho::watershed::flooding(g, c4(), nbasins); + + h = histo::compute(g | (pw::value(w) == pw::cst(0))); + + threshold = find_threshold(h); + + return duplicate((pw::value(g) > pw::cst(threshold)) | f.domain()); + } + +} // end of namespace mln + + + +int main(int argc, char *argv[]) +{ + using namespace mln; + using namespace value; + + if (argc != 4) + { + std::cout << argv[0] << " input.pgm lambda_grad lambda_graph" << std::endl; + return 1; + } + + int lambda_grad = std::atoi(argv[2]); + int lambda_graph = std::atoi(argv[3]); + + + image2d<int_u8> input; + io::pgm::load(input, argv[1]); + + image2d<int_u8> grad = morpho::elementary::gradient_internal(input, c4()); +// io::pgm::save(grad, "binarization_grad.pgm"); + + image2d<int_u8> clo_vol = morpho::closing::volume(grad, c4(), lambda_grad); +// io::pgm::save(clo_vol, "binarization_clo.pgm"); + + label_16 nbasins; + image2d<label_16> wst = morpho::watershed::flooding(clo_vol, c4(), nbasins); +// io::ppm::save(debug::colorize(rgb8(), wst, nbasins), "binarization_wst.ppm"); + + + util::couple<util::graph, image2d<label_16> > + rag_data = make::rag_and_labeled_wsl(wst, c4(), nbasins); + +// io::pgm::save(rag_data.second(), "binarization_lwsl.pgm"); + + + unsigned threshold; + contours(input, threshold); + + + typedef accu::median_h<int_u8> A; + typedef fun::i2v::array<point2d> F; + + + util::array<int_u8> basin_med = labeling::compute(A(), input, wst, nbasins); + basin_med[0] = 0; + + { + util::graph& gr = rag_data.first(); + + fun::i2v::array<int_u8> f_med; + convert::from_to(basin_med, f_med); + + // io::pgm::save(level::transform(wst, f_med), "basin_med.pgm"); + + p_vertices<util::graph, fun::i2v::array<point2d> > + pv = make::common_pvertices(wst, nbasins, rag_data.first()); + + mln_VAR( med, f_med | pv ); + + int_u8 + object = 255, + background = 0, + unknown = 128; + + med_t out; + initialize(out, med); + data::fill(out, unknown); + + typedef graph_elt_neighborhood<util::graph, F> N; + N nbh; + + // Initialization. + + mln_piter_(med_t) p(med.domain()); + mln_niter_(N) n(nbh, p); + for_all(p) + for_all(n) + { + int d = math::diff_abs(med(p), med(n)); +// if (2 * d > 3 * threshold) + if (d > threshold) + { + if (med(p) < med(n)) + { + out(p) = object; + out(n) = background; + } + else + { + out(n) = object; + out(p) = background; + } + break; + } + } + + io::pgm::save(level::transform(wst, out.function()), + "temp_out.pgm"); + + // Enqueuing. + + typedef mln_psite_(med_t) P; + p_queue<P> q; + + for_all(p) + { + if (out(p) != unknown) + continue; + for_all(n) + if (out(n) != unknown) + { + q.insert(p); + break; + } + } + + // Propagation. + + P p_; + mln_niter_(N) n_(nbh, p_); + while (! q.is_empty()) + { + p_ = q.pop_front(); + + if (out(p_) != unknown) + continue; // std::cerr << "oops! (1)" << std::endl; + + unsigned d = 256, id; + for_all(n_) + { + if (out(n_) == unknown) + { + q.insert(n_); + continue; + } + unsigned d_ = math::diff_abs(med(p_), med(n_)); + if (d_ < d) + { + d = d_; + id = out(n_); + } + } + if (d == 256) + std::cerr << "oops! (2)" << std::endl; + + out(p_) = id; // object or background. + } + + io::pgm::save(level::transform(wst, out.function()), + "temp_out_2.pgm"); + + } + +} Index: icdar/2009/dibco/svg.2.binarization.cc --- icdar/2009/dibco/svg.2.binarization.cc (revision 0) +++ icdar/2009/dibco/svg.2.binarization.cc (revision 0) @@ -0,0 +1,737 @@ +#include <mln/core/var.hh> + +#include <mln/core/image/image1d.hh> +#include <mln/core/alias/neighb1d.hh> + +#include <mln/core/image/image2d.hh> +#include <mln/core/alias/neighb2d.hh> +#include <mln/make/double_neighb2d.hh> + +#include <mln/pw/all.hh> +#include <mln/core/image/image_if.hh> +#include <mln/core/site_set/p_queue.hh> + +#include <mln/core/routine/extend.hh> +#include <mln/core/routine/duplicate.hh> +#include <mln/data/fill.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/io/essential.hh> +#include <mln/value/int_u8.hh> +#include <mln/value/int_u16.hh> +#include <mln/value/label_8.hh> +#include <mln/value/label_16.hh> +#include <mln/value/rgb8.hh> +#include <mln/literal/colors.hh> + +#include <mln/histo/compute.hh> + +#include <mln/morpho/erosion.hh> +#include <mln/morpho/dilation.hh> +#include <mln/morpho/elementary/gradient_internal.hh> +#include <mln/morpho/closing/volume.hh> +#include <mln/morpho/closing/height.hh> +#include <mln/morpho/watershed/flooding.hh> + +#include <mln/make/rag_and_labeled_wsl.hh> +#include <mln/util/graph.hh> +#include <mln/util/couple.hh> +#include <mln/debug/colorize.hh> +#include <mln/fun/i2v/array.hh> + +#include <mln/labeling/compute.hh> +#include <mln/labeling/regional_minima.hh> +#include <mln/labeling/wrap.hh> + +#include <mln/level/transform.hh> +#include <mln/level/convert.hh> + +#include <mln/linear/gaussian_1d.hh> + +#include <mln/accu/center.hh> +#include <mln/accu/median_h.hh> +#include <mln/accu/mean.hh> + +#include <mln/math/abs.hh> +#include <mln/debug/draw_graph.hh> + + + + + +namespace mln +{ + + struct edge_values : Function_vv2v<edge_values> + { + typedef value::int_u8 result; + typedef void mutable_result; + + value::int_u8 operator()(const value::int_u8& v1, + const value::int_u8& v2) const + { return math::abs(v1 - v2); } + + }; + + + template <typename I, typename V> + class edge_to_color : public Function_p2v< edge_to_color<I,V> > + { + public: + typedef V result; + + edge_to_color(const I& ima_e) : ima_e_(ima_e) + {} + + V + operator()(const unsigned& e) const + { return convert::to<V>(ima_e_.function()(e)); } + + private: + const I& ima_e_; + }; + + + namespace make + { + + template <typename G, typename P> + p_edges<G,pw::cst_<P> > + dummy_pedges(const Graph<G>& g_, const P& dummy_value) + { + trace::entering("dummy_pedges"); + + const G& g = exact(g_); + mln_precondition(g.is_valid()); + p_edges<G,pw::cst_<P> > pe(g, pw::cst(dummy_value)); + + trace::exiting("dummy_pedges"); + return pe; + } + + template <typename FVV, typename G, typename V2P, typename E2P, typename FVE> + pw::image<fun::i2v::array<mln_result(FVE)>,p_edges<G,E2P> > + edge_image(const pw::image<FVV,p_vertices<G,V2P> >& vertex_image, + const p_edges<G,E2P>& pe, + const Function_vv2v<FVE>& edge_value_) + { + trace::entering("edge_image"); + + const FVE& edge_value = exact(edge_value_); + mln_precondition(vertex_image.is_valid()); + mln_precondition(pe.is_valid()); + + typedef fun::i2v::array<mln_result(FVE)> edge_values_t; + edge_values_t edge_values(pe.nsites()); + + // image on graph edges + typedef pw::image<edge_values_t, p_edges<G,E2P> > ima_e_t; + ima_e_t ima_e = (edge_values | pe); + + mln_piter(ima_e_t) e(ima_e.domain()); + for_all(e) + ima_e(e) = edge_value(vertex_image.function()(e.element().v1()), + vertex_image.function()(e.element().v2())); + + trace::exiting("edge_image"); + return ima_e; + } + + + template <typename W, typename G> + inline + p_vertices<util::graph, fun::i2v::array<mln_site(W)> > + common_pvertices(const Image<W>& wst, const mln_value(W)& nbasins, + const Graph<G>& g_) + { + trace::entering("common_pvertices"); + + const G& g = exact(g_); + mln_precondition(g.is_valid()); + + typedef fun::i2v::array<mln_site(W)> vertex_sites_t; + vertex_sites_t vertex_sites; + convert::from_to(labeling::compute(accu::center<mln_site(W)>(), wst, nbasins), + vertex_sites); + + p_vertices<util::graph, vertex_sites_t> pv(g, vertex_sites); + + trace::exiting("common_pvertices"); + return pv; + } + + + template <typename A, typename G, typename F, typename I, typename W> + pw::image<fun::i2v::array<mln_value(I)>, p_vertices<G,F> > + vertex_image(const Accumulator<A>& accu, + const p_vertices<G,F>& pv, + const Image<I>& input_, + const Image<W>& wst_, + const mln_value(W)& nbasins) + { + trace::entering("vertex_image"); + + const I& input = exact(input_); + const W& wst = exact(wst_); + mln_precondition(input.is_valid()); + mln_precondition(wst.is_valid()); + mln_precondition(pv.is_valid()); + + typedef fun::i2v::array<mln_value(I)> vertex_values_t; + vertex_values_t vertex_values; + convert::from_to(labeling::compute(accu, input, wst, nbasins), + vertex_values); + + pw::image<fun::i2v::array<mln_value(I)>, + p_vertices<util::graph, fun::i2v::array<mln_site(I)> > > + ima_vertex = vertex_values | pv; + + trace::exiting("vertex_image"); + return ima_vertex; + } + + + template <typename I, typename V, typename E> + inline + mln_concrete(I) + debug_graph_image(const Image<I>& input_, + const Image<V>& ima_v_, const Image<E>& ima_e_, + unsigned box_size, const mln_value(I)& bg) + { + trace::entering("debug_graph_image"); + + const I& input = exact(input_); + const V& ima_v = exact(ima_v_); + const E& ima_e = exact(ima_e_); + + mln_precondition(input.is_valid()); + mln_precondition(ima_v.is_valid()); + mln_precondition(ima_e.is_valid()); + + mln_concrete(I) output; + initialize(output, input); + + data::fill(output, bg); + debug::draw_graph(output, ima_v.domain(), + pw::cst(150u), + edge_to_color<E, mln_value(I)>(ima_e)); + + dpoint2d tl(-box_size,-box_size); + dpoint2d br(box_size,box_size); + mln_piter(V) p(ima_v.domain()); + for_all(p) + { + box<mln_site(I)> b(p + tl, p + br); + b.crop_wrt(output.domain()); + data::fill((output | b).rw(), convert::to<mln_value(I)>(ima_v(p))); + } + + trace::exiting("debug_graph_image"); + return output; + } + + } // end of namespace mln::make + + int + find_threshold(const histo::array<value::int_u8>& h) + { + + const float sigma = 5; // FIXME: hard-coded! + + util::array<point1d> c; + value::label_8 n; + + { + image1d<unsigned> h_, hs_; + image1d<value::label_8> l; + + convert::from_to(h, h_); + hs_ = linear::gaussian_1d(h_, sigma, 0); + l = labeling::regional_minima(hs_, c2(), n); + + { +// debug::println("l", l); +// debug::histo(h, "tmp_h.txt"); + +// std::ofstream file("tmp_hs.txt"); +// mln_piter_(box1d) p(h_.domain()); +// for_all(p) +// file << p.ind() << ' ' << hs_(p) << std::endl; +// file.close(); + } + + accu::center<point1d, point1d> a; + c = labeling::compute(a, l, n); + + c[0] = point1d(0); // Force a neutral value for the non-label value (0). + + // std::cout << "c = " << c << std::endl; + } + + + int threshold; + + { + std::vector<int> v; + for (unsigned i = 0; i <= n; ++i) + v.push_back(c[i].ind()); + std::sort(v.begin(), v.end()); + + for (unsigned i = 0; i <= n; ++i) + if (v[i] != 0) + { + threshold = v[i]; + break; + } + + std::cout << "threshold = " << threshold << std::endl; + } + + return threshold; + } + + + image2d<bool> contours(const image2d<value::int_u8>& f, unsigned& threshold) + { + using value::int_u8; + typedef int_u8 V; + histo::array<V> h; + + image2d<int_u8> g; + g = morpho::elementary::gradient_internal(f, c4()); + g = morpho::closing::height(g, c4(), 3); // FIXME: hard-coded! + + unsigned nbasins; + image2d<unsigned> w = morpho::watershed::flooding(g, c4(), nbasins); + + h = histo::compute(g | (pw::value(w) == pw::cst(0))); + + threshold = find_threshold(h); + + return duplicate((pw::value(g) > pw::cst(threshold)) | f.domain()); + } + + + // Distances. + + value::int_u8 dist(const value::int_u8& g1, const value::int_u8& g2) + { + return math::diff_abs(g1, g2); + } + + value::int_u8 dist(const value::rgb8& c1, const value::rgb8& c2) + { + unsigned d = math::diff_abs(c1.red(), c2.red()); + unsigned d_; + d_ = math::diff_abs(c1.green(), c2.green()); + if (d_ > d) + d = d_; + d_ = math::diff_abs(c1.blue(), c2.blue()); + if (d_ > d) + d = d_; + return d; + } + + + // Functions. + + inline + bool is_row_odd(const point2d& p) + { + return p.row() % 2; + } + + inline + bool is_square(const point2d& p) + { + return p.row() % 2 == 0 && p.col() % 2 == 0; + } + + inline + bool is_edge(const point2d& p) + { + return p.row() % 2 + p.col() % 2 == 1; + } + + inline + bool is_point(const point2d& p) + { + return p.row() % 2 && p.col() % 2; + } + + + // Neighborhoods. + + typedef neighb< win::multiple<window2d, bool(*)(const point2d&)> > dbl_neighb2d; + + const dbl_neighb2d& e2c() + { + static bool e2c_h[] = { 0, 1, 0, + 0, 0, 0, + 0, 1, 0 }; + static bool e2c_v[] = { 0, 0, 0, + 1, 0, 1, + 0, 0, 0 }; + static dbl_neighb2d nbh = make::double_neighb2d(is_row_odd, e2c_h, e2c_v); + return nbh; + } + + const dbl_neighb2d& e2e() + { + static bool e2e_h[] = { 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0 }; + static bool e2e_v[] = { 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0 }; + static dbl_neighb2d nbh = make::double_neighb2d(is_row_odd, e2e_h, e2e_v); + return nbh; + } + + + // Transforms. + + template <typename T> + image2d<T> + image2full(const image2d<T>& input) + { + image2d<T> output(2 * input.nrows() - 1, + 2 * input.ncols() - 1); + for (int row = 0; row < input.nrows(); ++row) + for (int col = 0; col < input.ncols(); ++col) + opt::at(output, 2 * row, 2 * col) = opt::at(input, row, col); + return output; + } + + template <typename T> + image2d<T> + full2image(const image2d<T>& input) + { + image2d<T> output((input.nrows() + 1) / 2, + (input.ncols() + 1) / 2); + for (int row = 0; row < input.nrows(); row += 2) + for (int col = 0; col < input.ncols(); col += 2) + opt::at(output, row / 2, col / 2) = + opt::at(input, row, col); + return output; + } + + + // Display. + + template <typename I> + I display_edge(const I& ima, mln_value(I) bg, unsigned zoom) + { + unsigned nrows = ima.nrows() / 2 + 1; + unsigned ncols = ima.ncols() / 2 + 1; + I output(nrows * (zoom + 1) - 1, + ncols * (zoom + 1) - 1); + data::fill(output, bg); + mln_VAR( edge, ima | is_edge ); + mln_piter(edge_t) p(edge.domain()); + for_all(p) + if (p.row() % 2) // horizontal edge + { + unsigned row = (p.row() / 2 + 1) * (zoom + 1) - 1; + unsigned col = (p.col() / 2) * (zoom + 1); + for (unsigned i = 0; i < zoom; ++i) + opt::at(output, row, col + i) = ima(p); + } + else // vertical edge + { + unsigned row = (p.row() / 2) * (zoom + 1); + unsigned col = (p.col() / 2 + 1) * (zoom + 1) - 1; + for (unsigned i = 0; i < zoom; ++i) + opt::at(output, row + i, col) = ima(p); + } + return output; + } + + + + // Distance, stored on edges, of a couple of colors. + + template <typename I, typename N> + image2d<value::int_u8> + dist_on_edges(const I& input, const N& nbh) + { + image2d<value::int_u8> output; + initialize(output, input); + data::fill(output, 0); + + mln_piter(I) p(input.domain()); + mln_niter(N) n(nbh, p); + for_all(p) + { + n.start(); + mln_value(I) c1 = input(n); + n.next(); + mln_value(I) c2 = input(n); + output(p) = dist(c1, c2); + } + + // io::pgm::save(output, "temp_dist.pgm"); + + return output; + } + + + value::int_u8 L_to_int_u8(unsigned l) + { + return l == 0 ? + 0 : // wshed line + 1 + (l - 1) % 255; // basin + } + + +} // end of namespace mln + + + +int main(int argc, char *argv[]) +{ + using namespace mln; + using namespace value; + + if (argc != 5) + { + std::cout << argv[0] << " input.pgm lambda echo output.pbm" << std::endl; + return 1; + } + + int lambda_grad = std::atoi(argv[2]); + int echo = std::atoi(argv[3]); + + + if (echo != 0 && echo != 1) + { + std::cout << argv[0] << " input.pgm lambda echo output.pbm" << std::endl; + return 1; + } + + + image2d<int_u8> input; + io::pgm::load(input, argv[1]); + + image2d<int_u8> input_ = image2full(input); // Larger to make room to edges. + + image2d<int_u8> grad_; + { + grad_ = dist_on_edges(extend(input_ | is_edge, pw::value(input_)), + e2c()); + } + + // io::pgm::save(grad_, "grad_.pgm"); + + mln_VAR(grad, grad_ | is_edge); + typedef grad_t E; + + grad = morpho::closing::volume(grad, + e2e(), + lambda_grad); + + // io::pgm::save(grad.unmorph_(), "grad.pgm"); + + typedef label_16 L; // Label type. + + L nbasins; + + typedef mln_ch_value_(E, L) EL; + + EL wst = morpho::watershed::flooding(grad, + e2e(), + nbasins); + + std::cout << "n basins = " << nbasins << std::endl; + + + if (echo) + { + io::ppm::save(display_edge(debug::colorize(rgb8(), + wst, + nbasins).unmorph_(), + literal::black, + 3), + "temp_wst_edges.ppm"); + } + + + image2d<L> w_all = wst.unmorph_(); + + // edges -> squares + mln_VAR(w_squares, w_all | is_square); + data::paste(morpho::dilation(extend(w_squares, pw::value(w_all)), + c4().win()), + w_all); + // edges -> points + mln_VAR(w_points, w_all | is_point); + data::paste(morpho::erosion(extend(w_points, pw::value(w_all)), + c4().win()), + w_all); + + image2d<L> ws = full2image(w_all); + + if (echo) + { + io::ppm::save(debug::colorize(rgb8(), ws, nbasins), + "temp_ws.ppm"); + } + + util::couple<util::graph, image2d<L> > + rag_data = make::rag_and_labeled_wsl(w_all, c8(), nbasins); + + if (echo) + io::pgm::save(labeling::wrap(int_u8(), + rag_data.second()), + "temp_lwsl.pgm"); + + + unsigned threshold; + contours(input, threshold); + + + typedef accu::median_h<int_u8> A; + typedef fun::i2v::array<point2d> F; + + + util::array<int_u8> basin_med = labeling::compute(A(), input, ws, nbasins); + basin_med[0] = 0; + + + + if (echo) + io::ppm::save(debug::colorize(rgb8(), w_all, nbasins), + "temp_w_all.ppm"); + + + + // ----------------------------- R A G -------------------------------- + + + util::graph& gr = rag_data.first(); + + fun::i2v::array<int_u8> f_med; + convert::from_to(basin_med, f_med); + + if (echo) + { + io::pgm::save(level::transform(w_all, f_med), + "temp_w_all.pgm"); + io::pgm::save(level::transform(ws, f_med), "temp_basin_med.pgm"); + } + + p_vertices<util::graph, fun::i2v::array<point2d> > + pv = make::common_pvertices(ws, nbasins, rag_data.first()); + + mln_VAR( med, f_med | pv ); + + int_u8 + object = 255, + background = 0, + unknown = 128; + + med_t out; + initialize(out, med); + data::fill(out, unknown); + + typedef graph_elt_neighborhood<util::graph, F> N; + N nbh; + + // Initialization. + + mln_piter_(med_t) p(med.domain()); + mln_niter_(N) n(nbh, p); + for_all(p) + for_all(n) + { + int d = math::diff_abs(med(p), med(n)); + // if (2 * d > 3 * threshold) + if (d > threshold) + { + if (med(p) < med(n)) + { + out(p) = object; + out(n) = background; + } + else + { + out(n) = object; + out(p) = background; + } + break; + } + } + + if (echo) + { + io::pgm::save(level::transform(ws, out.function()), + "temp_out.pgm"); + } + + // Enqueuing. + + typedef mln_psite_(med_t) P; + p_queue<P> q; + + for_all(p) + { + if (out(p) != unknown) + continue; + for_all(n) + if (out(n) != unknown) + { + q.insert(p); + break; + } + } + + // Propagation. + + P p_; + mln_niter_(N) n_(nbh, p_); + while (! q.is_empty()) + { + p_ = q.pop_front(); + + if (out(p_) != unknown) + continue; // std::cerr << "oops! (1)" << std::endl; + + unsigned d = 256, id; + for_all(n_) + { + if (out(n_) == unknown) + { + q.insert(n_); + continue; + } + unsigned d_ = math::diff_abs(med(p_), med(n_)); + if (d_ < d) + { + d = d_; + id = out(n_); + } + } + if (d == 256) + std::cerr << "oops! (2)" << std::endl; + + out(p_) = id; // object or background. + } + + if (echo) + { + io::pgm::save(level::transform(ws, out.function()), + "temp_out_2.pgm"); + } + + io::pbm::save((pw::value(level::transform(ws, + out.function())) == pw::cst(255)) + | input.domain(), + argv[4]); +} Index: icdar/2009/dibco/svg.3.binarization.cc --- icdar/2009/dibco/svg.3.binarization.cc (revision 0) +++ icdar/2009/dibco/svg.3.binarization.cc (revision 0) @@ -0,0 +1,858 @@ +#include <mln/core/var.hh> + +#include <mln/core/image/image1d.hh> +#include <mln/core/alias/neighb1d.hh> + +#include <mln/core/image/image2d.hh> +#include <mln/core/alias/neighb2d.hh> +#include <mln/make/double_neighb2d.hh> + +#include <mln/pw/all.hh> +#include <mln/core/image/image_if.hh> +#include <mln/core/site_set/p_queue.hh> + +#include <mln/core/routine/extend.hh> +#include <mln/core/routine/duplicate.hh> +#include <mln/data/fill.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/io/essential.hh> +#include <mln/value/int_u8.hh> +#include <mln/value/int_u16.hh> +#include <mln/value/label_8.hh> +#include <mln/value/label_16.hh> +#include <mln/value/rgb8.hh> +#include <mln/literal/colors.hh> + +#include <mln/histo/compute.hh> + +#include <mln/morpho/erosion.hh> +#include <mln/morpho/dilation.hh> +#include <mln/morpho/elementary/gradient_internal.hh> +#include <mln/morpho/closing/volume.hh> +#include <mln/morpho/closing/height.hh> +#include <mln/morpho/watershed/flooding.hh> + +#include <mln/make/rag_and_labeled_wsl.hh> +#include <mln/util/graph.hh> +#include <mln/util/couple.hh> +#include <mln/debug/colorize.hh> +#include <mln/fun/i2v/array.hh> + +#include <mln/labeling/compute.hh> +#include <mln/labeling/regional_minima.hh> +#include <mln/labeling/wrap.hh> + +#include <mln/level/transform.hh> +#include <mln/level/convert.hh> + +#include <mln/linear/gaussian_1d.hh> + +#include <mln/accu/center.hh> +#include <mln/accu/median_h.hh> +#include <mln/accu/mean.hh> + +#include <mln/math/abs.hh> +#include <mln/debug/draw_graph.hh> + + + + + +namespace mln +{ + + struct edge_values : Function_vv2v<edge_values> + { + typedef value::int_u8 result; + typedef void mutable_result; + + value::int_u8 operator()(const value::int_u8& v1, + const value::int_u8& v2) const + { return math::abs(v1 - v2); } + + }; + + + template <typename I, typename V> + class edge_to_color : public Function_p2v< edge_to_color<I,V> > + { + public: + typedef V result; + + edge_to_color(const I& ima_e) : ima_e_(ima_e) + {} + + V + operator()(const unsigned& e) const + { return convert::to<V>(ima_e_.function()(e)); } + + private: + const I& ima_e_; + }; + + + namespace make + { + + template <typename G, typename P> + p_edges<G,pw::cst_<P> > + dummy_pedges(const Graph<G>& g_, const P& dummy_value) + { + trace::entering("dummy_pedges"); + + const G& g = exact(g_); + mln_precondition(g.is_valid()); + p_edges<G,pw::cst_<P> > pe(g, pw::cst(dummy_value)); + + trace::exiting("dummy_pedges"); + return pe; + } + + template <typename FVV, typename G, typename V2P, typename E2P, typename FVE> + pw::image<fun::i2v::array<mln_result(FVE)>,p_edges<G,E2P> > + edge_image(const pw::image<FVV,p_vertices<G,V2P> >& vertex_image, + const p_edges<G,E2P>& pe, + const Function_vv2v<FVE>& edge_value_) + { + trace::entering("edge_image"); + + const FVE& edge_value = exact(edge_value_); + mln_precondition(vertex_image.is_valid()); + mln_precondition(pe.is_valid()); + + typedef fun::i2v::array<mln_result(FVE)> edge_values_t; + edge_values_t edge_values(pe.nsites()); + + // image on graph edges + typedef pw::image<edge_values_t, p_edges<G,E2P> > ima_e_t; + ima_e_t ima_e = (edge_values | pe); + + mln_piter(ima_e_t) e(ima_e.domain()); + for_all(e) + ima_e(e) = edge_value(vertex_image.function()(e.element().v1()), + vertex_image.function()(e.element().v2())); + + trace::exiting("edge_image"); + return ima_e; + } + + + template <typename W, typename G> + inline + p_vertices<util::graph, fun::i2v::array<mln_site(W)> > + common_pvertices(const Image<W>& wst, const mln_value(W)& nbasins, + const Graph<G>& g_) + { + trace::entering("common_pvertices"); + + const G& g = exact(g_); + mln_precondition(g.is_valid()); + + typedef fun::i2v::array<mln_site(W)> vertex_sites_t; + vertex_sites_t vertex_sites; + convert::from_to(labeling::compute(accu::center<mln_site(W)>(), wst, nbasins), + vertex_sites); + + p_vertices<util::graph, vertex_sites_t> pv(g, vertex_sites); + + trace::exiting("common_pvertices"); + return pv; + } + + + template <typename A, typename G, typename F, typename I, typename W> + pw::image<fun::i2v::array<mln_value(I)>, p_vertices<G,F> > + vertex_image(const Accumulator<A>& accu, + const p_vertices<G,F>& pv, + const Image<I>& input_, + const Image<W>& wst_, + const mln_value(W)& nbasins) + { + trace::entering("vertex_image"); + + const I& input = exact(input_); + const W& wst = exact(wst_); + mln_precondition(input.is_valid()); + mln_precondition(wst.is_valid()); + mln_precondition(pv.is_valid()); + + typedef fun::i2v::array<mln_value(I)> vertex_values_t; + vertex_values_t vertex_values; + convert::from_to(labeling::compute(accu, input, wst, nbasins), + vertex_values); + + pw::image<fun::i2v::array<mln_value(I)>, + p_vertices<util::graph, fun::i2v::array<mln_site(I)> > > + ima_vertex = vertex_values | pv; + + trace::exiting("vertex_image"); + return ima_vertex; + } + + + template <typename I, typename V, typename E> + inline + mln_concrete(I) + debug_graph_image(const Image<I>& input_, + const Image<V>& ima_v_, const Image<E>& ima_e_, + unsigned box_size, const mln_value(I)& bg) + { + trace::entering("debug_graph_image"); + + const I& input = exact(input_); + const V& ima_v = exact(ima_v_); + const E& ima_e = exact(ima_e_); + + mln_precondition(input.is_valid()); + mln_precondition(ima_v.is_valid()); + mln_precondition(ima_e.is_valid()); + + mln_concrete(I) output; + initialize(output, input); + + data::fill(output, bg); + debug::draw_graph(output, ima_v.domain(), + pw::cst(150u), + edge_to_color<E, mln_value(I)>(ima_e)); + + dpoint2d tl(-box_size,-box_size); + dpoint2d br(box_size,box_size); + mln_piter(V) p(ima_v.domain()); + for_all(p) + { + box<mln_site(I)> b(p + tl, p + br); + b.crop_wrt(output.domain()); + data::fill((output | b).rw(), convert::to<mln_value(I)>(ima_v(p))); + } + + trace::exiting("debug_graph_image"); + return output; + } + + } // end of namespace mln::make + + int + find_threshold(const histo::array<value::int_u8>& h) + { + + const float sigma = 5; // FIXME: hard-coded! + + util::array<point1d> c; + value::label_8 n; + + { + image1d<unsigned> h_, hs_; + image1d<value::label_8> l; + + convert::from_to(h, h_); + hs_ = linear::gaussian_1d(h_, sigma, 0); + l = labeling::regional_minima(hs_, c2(), n); + + { +// debug::println("l", l); +// debug::histo(h, "tmp_h.txt"); + +// std::ofstream file("tmp_hs.txt"); +// mln_piter_(box1d) p(h_.domain()); +// for_all(p) +// file << p.ind() << ' ' << hs_(p) << std::endl; +// file.close(); + } + + accu::center<point1d, point1d> a; + c = labeling::compute(a, l, n); + + c[0] = point1d(0); // Force a neutral value for the non-label value (0). + + // std::cout << "c = " << c << std::endl; + } + + + int threshold; + + { + std::vector<int> v; + for (unsigned i = 0; i <= n; ++i) + v.push_back(c[i].ind()); + std::sort(v.begin(), v.end()); + + for (unsigned i = 0; i <= n; ++i) + if (v[i] != 0) + { + threshold = v[i]; + break; + } + + std::cout << "threshold = " << threshold << std::endl; + } + + return threshold; + } + + + image2d<bool> contours(const image2d<value::int_u8>& f, unsigned& threshold) + { + using value::int_u8; + typedef int_u8 V; + histo::array<V> h; + + image2d<int_u8> g; + g = morpho::elementary::gradient_internal(f, c4()); + g = morpho::closing::height(g, c4(), 3); // FIXME: hard-coded! + + unsigned nbasins; + image2d<unsigned> w = morpho::watershed::flooding(g, c4(), nbasins); + + h = histo::compute(g | (pw::value(w) == pw::cst(0))); + + threshold = find_threshold(h); + + return duplicate((pw::value(g) > pw::cst(threshold)) | f.domain()); + } + + + // Distances. + + value::int_u8 dist(const value::int_u8& g1, const value::int_u8& g2) + { + return math::diff_abs(g1, g2); + } + + value::int_u8 dist(const value::rgb8& c1, const value::rgb8& c2) + { + unsigned d = math::diff_abs(c1.red(), c2.red()); + unsigned d_; + d_ = math::diff_abs(c1.green(), c2.green()); + if (d_ > d) + d = d_; + d_ = math::diff_abs(c1.blue(), c2.blue()); + if (d_ > d) + d = d_; + return d; + } + + + // Functions. + + inline + bool is_row_odd(const point2d& p) + { + return p.row() % 2; + } + + inline + bool is_square(const point2d& p) + { + return p.row() % 2 == 0 && p.col() % 2 == 0; + } + + inline + bool is_edge(const point2d& p) + { + return p.row() % 2 + p.col() % 2 == 1; + } + + inline + bool is_point(const point2d& p) + { + return p.row() % 2 && p.col() % 2; + } + + + // Neighborhoods. + + typedef neighb< win::multiple<window2d, bool(*)(const point2d&)> > dbl_neighb2d; + + const dbl_neighb2d& e2c() + { + static bool e2c_h[] = { 0, 1, 0, + 0, 0, 0, + 0, 1, 0 }; + static bool e2c_v[] = { 0, 0, 0, + 1, 0, 1, + 0, 0, 0 }; + static dbl_neighb2d nbh = make::double_neighb2d(is_row_odd, e2c_h, e2c_v); + return nbh; + } + + const dbl_neighb2d& e2e() + { + static bool e2e_h[] = { 0, 0, 1, 0, 0, + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + 0, 0, 1, 0, 0 }; + static bool e2e_v[] = { 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, + 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0 }; + static dbl_neighb2d nbh = make::double_neighb2d(is_row_odd, e2e_h, e2e_v); + return nbh; + } + + + // Transforms. + + template <typename T> + image2d<T> + image2full(const image2d<T>& input) + { + image2d<T> output(2 * input.nrows() - 1, + 2 * input.ncols() - 1); + for (int row = 0; row < input.nrows(); ++row) + for (int col = 0; col < input.ncols(); ++col) + opt::at(output, 2 * row, 2 * col) = opt::at(input, row, col); + return output; + } + + template <typename T> + image2d<T> + full2image(const image2d<T>& input) + { + image2d<T> output((input.nrows() + 1) / 2, + (input.ncols() + 1) / 2); + for (int row = 0; row < input.nrows(); row += 2) + for (int col = 0; col < input.ncols(); col += 2) + opt::at(output, row / 2, col / 2) = + opt::at(input, row, col); + return output; + } + + + // Display. + + template <typename I> + I display_edge(const I& ima, mln_value(I) bg, unsigned zoom) + { + unsigned nrows = ima.nrows() / 2 + 1; + unsigned ncols = ima.ncols() / 2 + 1; + I output(nrows * (zoom + 1) - 1, + ncols * (zoom + 1) - 1); + data::fill(output, bg); + mln_VAR( edge, ima | is_edge ); + mln_piter(edge_t) p(edge.domain()); + for_all(p) + if (p.row() % 2) // horizontal edge + { + unsigned row = (p.row() / 2 + 1) * (zoom + 1) - 1; + unsigned col = (p.col() / 2) * (zoom + 1); + for (unsigned i = 0; i < zoom; ++i) + opt::at(output, row, col + i) = ima(p); + } + else // vertical edge + { + unsigned row = (p.row() / 2) * (zoom + 1); + unsigned col = (p.col() / 2 + 1) * (zoom + 1) - 1; + for (unsigned i = 0; i < zoom; ++i) + opt::at(output, row + i, col) = ima(p); + } + return output; + } + + + + // Distance, stored on edges, of a couple of colors. + + template <typename I, typename N> + image2d<value::int_u8> + dist_on_edges(const I& input, const N& nbh) + { + image2d<value::int_u8> output; + initialize(output, input); + data::fill(output, 0); + + mln_piter(I) p(input.domain()); + mln_niter(N) n(nbh, p); + for_all(p) + { + n.start(); + mln_value(I) c1 = input(n); + n.next(); + mln_value(I) c2 = input(n); + output(p) = dist(c1, c2); + } + + // io::pgm::save(output, "temp_dist.pgm"); + + return output; + } + + + value::int_u8 L_to_int_u8(unsigned l) + { + return l == 0 ? + 0 : // wshed line + 1 + (l - 1) % 255; // basin + } + + + + // Quasi-Regional Minima Labeling. + + template <typename I> + struct quasi_regional_minima_functor + { + typedef mln_psite(I) P; + + const I& input; + unsigned threshold; + + void init() + { + data::fill(attr, true); + } + + bool handles(const P&) const { return true; } + bool labels(const P& p) const { return attr(p); } + + bool equiv(const P& n, const P& p) const + { + if (attr(p) == false) + return false; + + mln_invariant(input(n) <= input(p)); + if (input(n) > input(p)) + std::cout << "oups!" << std::endl; + + std::cout << "UNION: n=" << n << " [" << input(n) + << "] p=" << p << " [" << input(p) + << "] equiv=" << (input(p) < input(n) + threshold) + << " " << threshold + << std::endl; + +// return input(p) == input(n); + return input(p) < input(n) + threshold; + } + + void do_no_union(const P& n, const P& p) + { + if (input(n) > input(p)) + std::cout << "oups!" << std::endl; + + if (input(p) < input(n) + threshold) + std::cout << "oups!" << std::endl; + + mln_invariant(input(p) >= input(n) + threshold); + (void)n; + attr(p) = false; + + std::cout << "INVALID: n=" << n << " (" << input(n) + << ") p=" << p << " (" << input(p) + << std::endl; + + } + + void init_attr(const P&) + {} + + void merge_attr(const P& r, const P& p) + { + attr(p) = attr(p) && attr(r); + } + + mln_ch_value(I, bool) attr; + + quasi_regional_minima_functor(const I& input, unsigned threshold) + : input(input), + threshold(threshold) + { + initialize(attr, input); + } + }; + + + template <typename I, typename N, typename L> + mln_ch_value(I, L) + labeling__quasi_regional_minima(const Image<I>& input_, const Neighborhood<N>& nbh_, + unsigned threshold, + L& nlabels) + { + trace::entering("labeling__quasi_regional_minima"); + + const I& input = exact(input_); + const N& nbh = exact(nbh_); + mln_precondition(input.is_valid()); + + // FIXME: abort if L is not wide enough to encode the set of + // minima. + + typedef quasi_regional_minima_functor<I> F; + F f(exact(input), threshold); + mln_ch_value(I, L) output = canvas::labeling_sorted(input, nbh, nlabels, + f, false); + + trace::exiting("labeling__quasi_regional_minima"); + return output; + } + + +} // end of namespace mln + + + +int main(int argc, char *argv[]) +{ + using namespace mln; + using namespace value; + + // trace::quiet = false; + + if (argc != 5) + { + std::cout << argv[0] << " input.pgm lambda echo output.pbm" << std::endl; + return 1; + } + + int lambda_grad = std::atoi(argv[2]); + int echo = std::atoi(argv[3]); + + + if (echo != 0 && echo != 1) + { + std::cout << argv[0] << " input.pgm lambda echo output.pbm" << std::endl; + return 1; + } + + + image2d<int_u8> input; + io::pgm::load(input, argv[1]); + + image2d<int_u8> input_ = image2full(input); // Larger to make room to edges. + + image2d<int_u8> grad_; + { + grad_ = dist_on_edges(extend(input_ | is_edge, pw::value(input_)), + e2c()); + } + + // io::pgm::save(grad_, "grad_.pgm"); + + mln_VAR(grad, grad_ | is_edge); + typedef grad_t E; + + grad = morpho::closing::volume(grad, + e2e(), + lambda_grad); + + // io::pgm::save(grad.unmorph_(), "grad.pgm"); + + typedef label_16 L; // Label type. + + L nbasins; + + typedef mln_ch_value_(E, L) EL; + + EL wst = morpho::watershed::flooding(grad, + e2e(), + nbasins); + + std::cout << "n basins = " << nbasins << std::endl; + + + if (echo) + { + io::ppm::save(display_edge(debug::colorize(rgb8(), + wst, + nbasins).unmorph_(), + literal::black, + 3), + "temp_wst_edges.ppm"); + } + + + image2d<L> w_all = wst.unmorph_(); + + // edges -> squares + mln_VAR(w_squares, w_all | is_square); + data::paste(morpho::dilation(extend(w_squares, pw::value(w_all)), + c4().win()), + w_all); + // edges -> points + mln_VAR(w_points, w_all | is_point); + data::paste(morpho::erosion(extend(w_points, pw::value(w_all)), + c4().win()), + w_all); + + image2d<L> ws = full2image(w_all); + + if (echo) + { + io::ppm::save(debug::colorize(rgb8(), ws, nbasins), + "temp_ws.ppm"); + } + + util::couple<util::graph, image2d<L> > + rag_data = make::rag_and_labeled_wsl(w_all, c8(), nbasins); + + if (echo) + io::pgm::save(labeling::wrap(int_u8(), + rag_data.second()), + "temp_lwsl.pgm"); + + + unsigned threshold; + contours(input, threshold); + + + typedef accu::median_h<int_u8> A; + typedef fun::i2v::array<point2d> F; + + + util::array<int_u8> basin_med = labeling::compute(A(), input, ws, nbasins); + basin_med[0] = 0; + + + + if (echo) + io::ppm::save(debug::colorize(rgb8(), w_all, nbasins), + "temp_w_all.ppm"); + + + + // ----------------------------- R A G -------------------------------- + + + util::graph& gr = rag_data.first(); + + fun::i2v::array<int_u8> f_med; + convert::from_to(basin_med, f_med); + + if (echo) + { + io::pgm::save(level::transform(w_all, f_med), + "temp_w_all.pgm"); + io::pgm::save(level::transform(ws, f_med), "temp_basin_med.pgm"); + } + + p_vertices<util::graph, fun::i2v::array<point2d> > + pv = make::common_pvertices(ws, nbasins, rag_data.first()); + + mln_VAR( med, f_med | pv ); + + + typedef graph_elt_neighborhood<util::graph, F> N; + N nbh; + + threshold = 25; // FIXME + + L n_objects; + mln_VAR( lab, labeling__quasi_regional_minima(med, nbh, threshold, n_objects) ); + + + io::pbm::save((pw::value(level::transform(ws, + lab.function())) != pw::cst(0)) + | input.domain(), + argv[4]); + + + /* + + int_u8 + object = 255, + background = 0, + unknown = 128; + + med_t out; + initialize(out, med); + data::fill(out, unknown); + + // Initialization. + + mln_piter_(med_t) p(med.domain()); + mln_niter_(N) n(nbh, p); + for_all(p) + for_all(n) + { + int d = math::diff_abs(med(p), med(n)); + // if (2 * d > 3 * threshold) + if (d > threshold) + { + if (med(p) < med(n)) + { + out(p) = object; + out(n) = background; + } + else + { + out(n) = object; + out(p) = background; + } + break; + } + } + + if (echo) + { + io::pgm::save(level::transform(ws, out.function()), + "temp_out.pgm"); + } + + + // Enqueuing. + + typedef mln_psite_(med_t) P; + p_queue<P> q; + + for_all(p) + { + if (out(p) != unknown) + continue; + for_all(n) + if (out(n) != unknown) + { + q.insert(p); + break; + } + } + + // Propagation. + + P p_; + mln_niter_(N) n_(nbh, p_); + while (! q.is_empty()) + { + p_ = q.pop_front(); + + if (out(p_) != unknown) + continue; // std::cerr << "oops! (1)" << std::endl; + + unsigned d = 256, id; + for_all(n_) + { + if (out(n_) == unknown) + { + q.insert(n_); + continue; + } + unsigned d_ = math::diff_abs(med(p_), med(n_)); + if (d_ < d) + { + d = d_; + id = out(n_); + } + } + if (d == 256) + std::cerr << "oops! (2)" << std::endl; + + out(p_) = id; // object or background. + } + + if (echo) + { + io::pgm::save(level::transform(ws, out.function()), + "temp_out_2.pgm"); + } + + io::pbm::save((pw::value(level::transform(ws, + out.function())) == pw::cst(255)) + | input.domain(), + argv[4]); + + */ + +} Property changes on: icdar/2009/dibco/binarization.cc ___________________________________________________________________ Added: svn:mergeinfo Property changes on: icdar/2009/hsc/first_attempts/fuzzy.sh ___________________________________________________________________ Added: svn:mergeinfo Property changes on: icdar/2009/hsc/first_attempts/reduce.sh ___________________________________________________________________ Added: svn:mergeinfo Property changes on: icdar/2009/hsc/first_attempts/boxes.cc ___________________________________________________________________ Added: svn:mergeinfo Property changes on: icdar/2009/hsc/first_attempts/words.sh ___________________________________________________________________ Added: svn:mergeinfo Property changes on: icdar/2009/hsc/first_attempts/main.cc ___________________________________________________________________ Added: svn:mergeinfo Property changes on: icdar/2009/hsc/first_attempts/dist.sh ___________________________________________________________________ Added: svn:mergeinfo Index: icdar/2009/README --- icdar/2009/README (revision 0) +++ icdar/2009/README (revision 0) @@ -0,0 +1,13 @@ + -*- outline -*- + +* hsc +Handwriting Segmentation Contest +http://www.iit.demokritos.gr/~bgat/HandSegmCont2009/index.html + +* dibco +Document Image Binarization Contest +http://www.iit.demokritos.gr/~bgat/DIBCO2009/ + +* pscomp +Page Segmentation Competition +http://www.cse.salford.ac.uk/prima/ICDAR2009_pscomp/
participants (1)
-
Thierry Geraud