3607: Make a common room for ICDAR.

https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox Index: ChangeLog from Thierry Geraud <thierry.geraud@lrde.epita.fr> Make a common room for ICDAR. * icdar: New directory. * icdar/2009: New directory. * icdar/2009/hsc: New directory. * theo/icdar/hsc/main.cc (tmp_clo): New debug image. Copy to... * icdar/2009/hsc/main.cc: ...this new file. (output): Update. * theo/exec/diff_abs.cc: New. Propagate Edwin's modifications. * theo/esiee/laurent/presentation/wst_edge.cc: Update. * theo/color/change_attributes.hh: Update. * theo/color/segment.hh: Update. * theo/color/segment_rgb_pixels.cc: Update. * theo/color/filter_rgb_pixels.cc: New. * theo/color/filter_blen_rgb_edges.cc: New. * lazzara/icdar/quasi.cc: New. * lazzara/icdar/binarization.cc: Augment. icdar/2009/hsc/main.cc | 25 - lazzara/icdar/binarization.cc | 664 +++++++++++++++++++++++++--- lazzara/icdar/quasi.cc | 180 +++++++ theo/color/change_attributes.hh | 10 theo/color/filter_blen_rgb_edges.cc | 387 ++++++++++++++++ theo/color/filter_rgb_pixels.cc | 227 +++++++++ theo/color/segment.hh | 78 ++- theo/color/segment_rgb_pixels.cc | 28 - theo/esiee/laurent/presentation/wst_edge.cc | 4 theo/exec/diff_abs.cc | 38 + theo/icdar/hsc/main.cc | 4 11 files changed, 1552 insertions(+), 93 deletions(-) Index: icdar/2009/hsc/main.cc --- icdar/2009/hsc/main.cc (revision 0) +++ icdar/2009/hsc/main.cc (working copy) @@ -264,8 +264,11 @@ void usage(char* argv[]) { - std::cerr << "usage: " << argv[0] << " input.pbm output.pgm [output.ppm]" << std::endl - << " ICDAR'2009: HSC." << std::endl; + std::cerr << "usage: " << argv[0] << " input.pbm output.pgm" << std::endl + << " HSC @ ICDAR'2009" << std::endl + << " input.pbm: input 2D binary image (text is black; background is white)" << std::endl + << " output.dump: output image where line components are labeled (int_u8)" << std::endl + << " 0 is the background label." << std::endl; std::abort(); } @@ -277,7 +280,7 @@ using value::int_u8; using value::rgb8; - if (argc != 3 && argc != 4) + if (argc != 3) usage(argv); @@ -331,6 +334,10 @@ 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); @@ -358,26 +365,14 @@ { image2d<int_u8> output(input.domain()); - image2d<rgb8> - cool = debug::colorize(rgb8(), spc, n_basins), - lab(input.domain()); - mln_piter_(box2d) p(input.domain()); for_all(p) if (input(p)) - { output(p) = 0; - lab(p) = literal::black; - } else - { output(p) = spc.at_(p.row() / subsampling_factor, p.col() / subsampling_factor); - lab(p) = cool.at_(p.row() / subsampling_factor, p.col() / subsampling_factor); - } io::pgm::save(output, argv[2]); - if (argc == 4) - io::ppm::save(lab, argv[3]); } Property changes on: icdar/2009/hsc/main.cc ___________________________________________________________________ Added: svn:mergeinfo Index: theo/icdar/hsc/main.cc --- theo/icdar/hsc/main.cc (revision 3606) +++ theo/icdar/hsc/main.cc (working copy) @@ -331,6 +331,10 @@ 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); Index: theo/exec/diff_abs.cc --- theo/exec/diff_abs.cc (revision 0) +++ theo/exec/diff_abs.cc (revision 0) @@ -0,0 +1,38 @@ +#include "filetype.hh" + +#include <mln/fun/vv2v/diff_abs.hh> +#include <mln/level/transform.hh> + + + +void usage(char* argv[]) +{ + std::cerr << "usage: " << argv[0] << " input1.pgm input2.pgm output.pgm" << std::endl + << " Point-wise diff abs." << std::endl; + std::abort(); +} + + + +int main(int argc, char* argv[]) +{ + using namespace mln; + using value::int_u8; + + if (argc != 4) + usage(argv); + + + trace::entering("main"); + + image2d<int_u8> input1, input2; + io::pgm::load(input1, argv[1]); + io::pgm::load(input2, argv[2]); + + io::pgm::save(level::transform(input1, + input2, + fun::vv2v::diff_abs<int_u8>()), + argv[3]); + + trace::exiting("main"); +} Index: theo/esiee/laurent/presentation/wst_edge.cc --- theo/esiee/laurent/presentation/wst_edge.cc (revision 3606) +++ theo/esiee/laurent/presentation/wst_edge.cc (working copy) @@ -33,7 +33,7 @@ # include <mln/extension/fill.hh> # include <mln/morpho/meyer_wst.hh> -# include <mln/morpho/closing_volume.hh> +# include <mln/morpho/closing/volume.hh> # include <mln/opt/at.hh> @@ -318,7 +318,7 @@ } - data::paste( morpho::closing_volume(edge, e2e, lambda), edge ); + data::paste( morpho::closing::volume(edge, e2e, lambda), edge ); image2d<unsigned> label(ima.bbox(), 0); Index: theo/color/segment.hh --- theo/color/segment.hh (revision 3606) +++ theo/color/segment.hh (working copy) @@ -16,7 +16,7 @@ #include <mln/pw/all.hh> #include <mln/morpho/tree/data.hh> -#include <mln/morpho/closing_attribute.hh> +#include <mln/morpho/closing/leveling.hh> #include "change_attributes.hh" @@ -120,7 +120,7 @@ // Propagate attribute from a site to its parent. { - mln_fwd_piter(T) p(t.domain()); + mln_up_site_piter(T) p(t); // WAS: mln_fwd_piter(T) p(t.domain()); for_all(p) if (! t.is_root(p)) acc(t.parent(p)).take(acc(p)); @@ -131,12 +131,11 @@ // Change accumulator into its result. { initialize(attr, acc); - typedef typename T::nodes_t N; - mln_piter(N) p(t.nodes()); - for_all(p) + mln_node_piter(T) n(t); + for_all(n) { - mln_invariant(t.is_a_node(p)); - attr(p) = acc(p).to_result(); + mln_invariant(t.is_a_node(n)); + attr(n) = acc(n).to_result(); } } @@ -213,14 +212,22 @@ data::fill(nchildren, 0); { - mln_fwd_piter(T) p(t.domain()); - // Propagate attribute from a site to its parent. - for_all(p) - if (t.is_a_non_root_node(p)) - { - mln_invariant(t.is_a_node(t.parent(p))); - ++nchildren(t.parent(p)); // so parent(p) is a node - } + mln_up_node_piter(T) n(t); + for_all(n) + if (! t.is_root(n)) + { + mln_invariant(t.is_a_node(t.parent(n))); + ++nchildren(t.parent(n)); // so parent(n) is a node + } + + // WAS: +// mln_fwd_piter(T) p(t.domain()); +// for_all(p) +// if (t.is_a_non_root_node(p)) +// { +// mln_invariant(t.is_a_node(t.parent(p))); +// ++nchildren(t.parent(p)); // so parent(p) is a node +// } } return nchildren; @@ -325,7 +332,10 @@ mln_concrete(I) g; { initialize(g, t.f()); - mln_bkd_piter(T) p(t.domain()); + +// mln_bkd_piter(T) p(t.domain()); + + mln_dn_site_piter(T) p(t); for_all(p) if (t.is_a_node(p) && a(p) >= lambda) g(p) = t.f(p); @@ -357,7 +367,7 @@ unsigned n_objects, unsigned less, bool echo = false) { - mln_concrete(I) g_ref = morpho::closing_attribute<A>(f, nbh, lambda); + mln_concrete(I) g_ref = morpho::closing::leveling(f, nbh, A(), lambda); unsigned n_regmins_g_ref; mln_ch_value(I, unsigned) regmin_g_ref = labeling::regional_minima(g_ref, nbh, n_regmins_g_ref); @@ -402,6 +412,40 @@ + + + + template <typename A, typename T, typename N> + inline + mln_concrete(typename T::function) + do_filter(A& a, const T& t, const N& nbh, + const mln_value(A)& lambda) + { + typedef typename T::function I; + + const typename T::parent_t& par = t.parent_image(); + + // No need for attributes back propagation since we process the + // tree downwards: nodes are set *before* their children nodes and + // their related (non-nodes) sites. + + mln_concrete(I) g; + initialize(g, t.f()); + + mln_dn_site_piter(T) p(t); + for_all(p) + if (t.is_a_node(p) && a(p) >= lambda) + g(p) = t.f(p); + else + g(p) = g(par(p)); + + return g; + } + + + + + // Filter facade. template <typename F, typename N, typename A> Index: theo/color/segment_rgb_pixels.cc --- theo/color/segment_rgb_pixels.cc (revision 3606) +++ theo/color/segment_rgb_pixels.cc (working copy) @@ -14,7 +14,8 @@ #include <mln/io/pbm/save.hh> #include <mln/level/convert.hh> -#include <mln/morpho/meyer_wst.hh> +#include <mln/morpho/watershed/flooding.hh> +#include <mln/morpho/attribute/card.hh> #include "mean_rgb_pix.hh" #include "segment.hh" @@ -75,8 +76,7 @@ mln_ch_value(A, value::int_u8) d; initialize(d, a); - typedef typename T::nodes_t N; - mln_fwd_piter(N) n(t.nodes()); + mln_up_node_piter(T) n(t); for_all(n) d(n) = dist(a(n), a(t.parent(n))); @@ -98,6 +98,7 @@ { std::cerr << "usage: " << argv[0] << " input.ppm n echo output.ppm" << std::endl; std::cerr << "color version on pixels (without edges)" << std::endl; + std::cerr << "attribute is 'card'" << std::endl; std::cerr << " n >= 2" << std::endl; std::cerr << " echo = 0 (mute) or 1 (verbose)" << std::endl; abort(); @@ -147,11 +148,22 @@ typedef morpho::tree::data<F,S> tree_t; tree_t t(f, s, c4()); + + morpho::attribute::card<F> a_; + mln_VAR(a, compute_attribute_on_nodes(a_, t)); + + unsigned lambda; + unsigned less; + image2d<int_u8> g = run_filter(a, t, c4(), n_objects, // input + less, lambda, // output + echo); + + /* + accu::rgb_image_ = input; accu::mean_rgb_pix< util::pix<F> > a_; mln_VAR(a, compute_attribute_on_nodes(a_, t)); - mln_VAR(d, dists_from_means(t, a, echo)); make_attribute_grow(t, d); if (echo) @@ -160,19 +172,21 @@ display_tree_attributes(t, d); } - // BAD: extinct_attributes(t, d, echo); - int_u8 lambda; unsigned less; image2d<int_u8> g = run_filter(d, t, c4(), n_objects, // input less, lambda, // output echo); + */ + + io::pbm::save( (pw::value(g) != pw::value(f)) | f.domain(), "temp_activity.pbm" ); + // if (echo) // debug::println("activity (g != f) = ", (pw::value(g) != pw::value(f)) | f.domain()); @@ -181,7 +195,7 @@ typedef value::label_16 L; L nbasins; - mln_ch_value_(I, L) w = morpho::meyer_wst(g, c4(), nbasins); + mln_ch_value_(I, L) w = morpho::watershed::flooding(g, c4(), nbasins); std::cout << "n basins = " << nbasins << std::endl; image2d<rgb8> output = duplicate(input); Index: theo/color/filter_rgb_pixels.cc --- theo/color/filter_rgb_pixels.cc (revision 0) +++ theo/color/filter_rgb_pixels.cc (revision 0) @@ -0,0 +1,227 @@ + +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image_if.hh> +#include <mln/core/alias/neighb2d.hh> + +#include <mln/value/int_u8.hh> +#include <mln/value/label_16.hh> +#include <mln/value/rgb8.hh> +#include <mln/literal/colors.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/pbm/save.hh> +#include <mln/level/convert.hh> + +#include <mln/morpho/watershed/flooding.hh> + +#include "mean_rgb_pix.hh" +#include "segment.hh" + + + +namespace mln +{ + + + // Distance, stored on pixels, of neighboring colors. + + template <typename N> + image2d<value::int_u8> + dist_on_pixels(const image2d<value::rgb8>& input, const N& nbh) + { + using value::int_u8; + image2d<int_u8> output(input.domain()); + + mln_piter(box2d) p(input.domain()); + mln_niter(N) n(nbh, p); + for_all(p) + { + int_u8 d = 0u; + for_all(n) if (input.domain().has(n)) + { + int_u8 d_ = dist(input(p), input(n)); + if (d_ > d) + d = d_; + } + output(p) = d; + } + + io::pgm::save(output, "temp_dist.pgm"); + + return output; + } + + + + // Distance between 2 rgb8 colors. + // ----------------------------------------------------------- + + + value::int_u8 dist_mean(const value::rgb8& c1, const value::rgb8& c2) + { + unsigned d = 0; + d += (math::diff_abs(c1.red(), c2.red()) + 2) / 3; + d += (math::diff_abs(c1.green(), c2.green()) + 2) / 3; + d += (math::diff_abs(c1.blue(), c2.blue()) + 2) / 3; + if (d > 255) + d = 255; + return d; + } + + value::int_u8 dist_sat(const value::rgb8& c1, const value::rgb8& c2) + { + unsigned d = 0; + d += math::diff_abs(c1.red(), c2.red()); + d += math::diff_abs(c1.green(), c2.green()); + d += math::diff_abs(c1.blue(), c2.blue()); + if (d > 255) + d = 255; + return d; + } + + value::int_u8 dist_max(const value::rgb8& c1, const value::rgb8& c2) + { + unsigned d = 0, d_; + d_ = math::diff_abs(c1.red(), c2.red()); + if (d_ > d) d = 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; + } + + + + // From 'mean color' attributes to 'mean difference p/par(p)'. + // ----------------------------------------------------------- + + + template <typename T, typename A> + inline + mln_ch_value(A, value::int_u8) + dists_from_means(const T& t, const A& a, bool echo = false) + { + if (echo) + { + std::cout << "means:" << std::endl; + display_tree_attributes(t, a); + } + + mln_ch_value(A, value::int_u8) d; + initialize(d, a); + + mln_up_node_piter(T) n(t); + for_all(n) + d(n) = dist_max(a(n), a(t.parent(n))); + + if (echo) + { + std::cout << "dists:" << std::endl; + display_tree_attributes(t, d); + } + + return d; + } + + + + + +} // mln + + + +void usage(char* argv[]) +{ + std::cerr << "usage: " << argv[0] << " input.ppm lambda echo output.ppm" << std::endl; + std::cerr << "color version on pixels (without edges)" << std::endl; + std::cerr << "attribute is 'distance of color means'" << std::endl; + std::cerr << " lambda >= 0 and <= 255" << std::endl; + std::cerr << " echo = 0 (mute) or 1 (verbose)" << std::endl; + abort(); +} + + + +int main(int argc, char* argv[]) +{ + using namespace mln; + + using value::int_u8; + using value::rgb8; + + + if (argc != 5) + usage(argv); + + + // Color debase (on pixels, not edges) version. + // -------------------------------------------- + + + typedef image2d<rgb8> I; + + I input; + io::ppm::load(input, argv[1]); + + int lambda = atoi(argv[2]); + if (lambda < 0 || lambda > 255) + usage(argv); + + bool echo = atoi(argv[3]); + + + // Neighborhood. + mln_VAR(nbh, c4()); + + + // Changing input into 'f'. + + typedef image2d<int_u8> F; + F f = dist_on_pixels(input, c4()); + + + typedef p_array<mln_psite_(F)> S; + S s = level::sort_psites_decreasing(f); + + typedef morpho::tree::data<F,S> tree_t; + tree_t t(f, s, c4()); + + + accu::rgb_image_ = input; + accu::mean_rgb_pix< util::pix<F> > a_; + mln_VAR(a, compute_attribute_on_nodes(a_, t)); + + mln_VAR(d, dists_from_means(t, a, echo)); + make_attribute_grow(t, d); + if (echo) + { + std::cout << "dists growing:" << std::endl; + display_tree_attributes(t, d); + } + + image2d<int_u8> g = do_filter(d, t, c4(), lambda); + + + io::pbm::save( (pw::value(g) != pw::value(f)) | f.domain(), + "temp_activity.pbm" ); + + +// debug::println("activity (g != f) = ", (pw::value(g) != pw::value(f)) | f.domain()); + + + // Watershed transform. + + typedef value::label_16 L; + L nbasins; + mln_ch_value_(I, L) w = morpho::watershed::flooding(g, c4(), nbasins); + std::cout << "n basins = " << nbasins << std::endl; + + image2d<rgb8> output = duplicate(input); + data::fill((output | (pw::value(w) == 0)).rw(), literal::black); + + io::ppm::save(output, argv[4]); +} Index: theo/color/change_attributes.hh --- theo/color/change_attributes.hh (revision 3606) +++ theo/color/change_attributes.hh (working copy) @@ -16,8 +16,11 @@ initialize(deja_vu, a); data::fill(deja_vu, false); - typedef typename T::nodes_t nodes_t; - mln_fwd_piter(nodes_t) p(t.nodes()); + // WAS: + // typedef typename T::nodes_t nodes_t; + // mln_fwd_piter(nodes_t) p(t.nodes()); + + mln_up_node_piter(T) p(t); for_all(p) { if (deja_vu(p)) @@ -91,7 +94,8 @@ void back_propagate(const T& t, A& a) { - mln_fwd_piter(T) p(t.domain()); + // WAS: mln_fwd_piter(T) p(t.domain()); + mln_site_piter(T) p(t); for_all(p) if (! t.is_a_node(p)) { Index: theo/color/filter_blen_rgb_edges.cc --- theo/color/filter_blen_rgb_edges.cc (revision 0) +++ theo/color/filter_blen_rgb_edges.cc (revision 0) @@ -0,0 +1,387 @@ + +#include <mln/core/image/image2d.hh> +#include <mln/core/alias/box3d.hh> + +#include <mln/core/alias/neighb2d.hh> +#include <mln/make/double_neighb2d.hh> + +#include <mln/value/label_8.hh> +#include <mln/value/label_16.hh> + +#include <mln/core/image/image_if.hh> +#include <mln/core/routine/extend.hh> +#include <mln/core/routine/duplicate.hh> +#include <mln/data/paste.hh> + +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> + +#include <mln/debug/colorize.hh> + +#include <mln/morpho/dilation.hh> +#include <mln/morpho/erosion.hh> +#include <mln/morpho/watershed/flooding.hh> +#include <mln/labeling/compute.hh> + +#include <mln/accu/count.hh> +#include <mln/accu/volume.hh> +#include <mln/accu/mean.hh> + +#include "sum_pix.hh" +#include "segment.hh" + + + +namespace mln +{ + + point3d color2point(const value::rgb8& c) + { + point3d p(c.red(), c.green(), c.blue()); + return p; + } + + + image2d<value::rgb8> blen_image; + + + template <typename B> + void accu_blen_take(B& b, const point2d& e) + { + if (e.row() % 2) + { + b.take(color2point(blen_image(e + up))); + b.take(color2point(blen_image(e + down))); + } + else + { + b.take(color2point(blen_image(e + left))); + b.take(color2point(blen_image(e + right))); + } + } + +} + +#include "blen_pix.hh" + + + + +namespace mln +{ + + // 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(); + value::rgb8 c1 = input(n); + n.next(); + value::rgb8 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 + } + + +} // mln + + + +void usage(char* argv[]) +{ + std::cerr << "usage: " << argv[0] << " input.ppm lambda echo output.ppm" << std::endl; + std::cerr << " lambda >= 0" << std::endl; + std::cerr << " echo = 0 (mute) or 1 (verbose)" << std::endl; + abort(); +} + + + +int main(int argc, char* argv[]) +{ + using namespace mln; + using value::int_u8; + using value::rgb8; + + if (argc != 5) + usage(argv); + + + // Color version. + // -------------- + + + image2d<rgb8> input; + io::ppm::load(input, argv[1]); + + + int lambda = atoi(argv[2]); + if (lambda < 0) + usage(argv); + + bool echo = atoi(argv[3]); + + + // Changing input into 'f on edges'. + + image2d<int_u8> f_; + image2d<rgb8> input_ = image2full(input); + { + f_ = dist_on_edges(extend(input_ | is_edge, pw::value(input_)), + e2c()); + } + mln_VAR(f, f_ | is_edge); + typedef f_t I; + + + typedef p_array<mln_psite_(I)> S; + S s = level::sort_psites_decreasing(f); + + typedef morpho::tree::data<I,S> tree_t; + tree_t t(f, s, e2e()); + + + blen_image = input_; + accu::blen_pix<I> a_; + mln_VAR(a, compute_attribute_on_nodes(a_, t)); + + + f_t g = do_filter(a, t, e2e(), lambda); + + + if (echo) + debug::println("activity (g != f) = ", (pw::value(g) != pw::value(f)) | f.domain()); + + // Watershed transform. + + typedef value::label_16 L; + L nbasins; + mln_ch_value_(f_t, L) w = morpho::watershed::flooding(g, e2e(), nbasins); + + std::cout << "n basins = " << nbasins << std::endl; + + io::pgm::save(display_edge(w.unmorph_(), 0, 3), "temp_w.pgm"); + + if (echo) + { + image2d<int_u8> g_(f_.domain()); + + data::fill(g_, 0); + data::paste(g | (pw::value(w) != 0), g_); + debug::println("g | basins = ", g_ | is_edge); + + data::fill(g_, 0); + data::paste(g | (pw::value(w) == 0), g_); + debug::println("g | w line = ", g_ | is_edge); + } + + image2d<L> w_all = w.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); + + + io::ppm::save(debug::colorize(rgb8(), + w_all, + nbasins), + "temp_w_all.ppm"); + + + // Outputing. + // ---------- + + image2d<L> out_w = full2image(w_all); + +// io::ppm::save(debug::colorize(rgb8(), +// out_w, +// nbasins), +// argv[4]); + + + typedef algebra::vec<3,float> vec3f; + + util::array<vec3f> m_3f = labeling::compute(accu::mean<rgb8>(), + input, + full2image(w_all), + nbasins); + m_3f[0] = vec3f::zero; + + util::array<rgb8> m(m_3f.nelements()); + // convert::from_to(m_3f, m); // FIXME: Do not work! + for (unsigned i = 0; i < m_3f.nelements(); ++i) + m[i] = rgb8(m_3f[i][0], m_3f[i][1], m_3f[i][2]); + + + io::ppm::save( level::transform(out_w, + convert::to< fun::i2v::array<rgb8> >(m) ), + argv[4] ); + + +// io::ppm::save( level::transform(w_all, +// convert::to< fun::i2v::array<rgb8> >(m) ), +// argv[4] ); + + +// debug::println("input_", input_); +// debug::println("w_squares", w_squares); + +// FIXME: The code below does not work! + +// util::array<vec3f> m_3f = labeling::compute(accu::mean<rgb8>(), +// input_, +// w_squares, +// nbasins); + +} Index: lazzara/icdar/quasi.cc --- lazzara/icdar/quasi.cc (revision 0) +++ lazzara/icdar/quasi.cc (revision 0) @@ -0,0 +1,180 @@ +#include <mln/core/image/image2d.hh> +#include <mln/core/alias/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/site_set/p_array.hh> + +#include <mln/data/fill.hh> + +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/value/int_u8.hh> +#include <mln/io/pbm/save.hh> + +#include <mln/debug/colorize.hh> +#include <mln/fun/i2v/array.hh> + +#include <mln/labeling/regional_minima.hh> +#include <mln/labeling/wrap.hh> + +#include <mln/level/transform.hh> +#include <mln/level/convert.hh> + +#include <mln/math/diff_abs.hh> + + + + + +namespace mln +{ + + float + sim(unsigned v1, unsigned v2) + { + if (v1 == 0 && v2 == 0) + return 1.f; + return v1 < v2 ? float(v1) / float(v2) : float(v2) / float(v1); // min / max + } + + + template <typename I, typename N, typename L> + mln_ch_value(I, bool) + labeling__quasi_regional_minima(const Image<I>& input_, const Neighborhood<N>& nbh_, + unsigned threshold, + L& cur_lab) + { + trace::entering("labeling__quasi_regional_minima"); + + const I& input = exact(input_); + const N& nbh = exact(nbh_); + mln_precondition(input.is_valid()); + + typedef mln_psite(I) P; + p_array<P> s = level::sort_psites_increasing(input); + + std::vector<bool> valid; + valid.push_back(false); // For valid[0] where 0 is the non-label value. + + mln_ch_value(I, L) lab; + initialize(lab, input); + data::fill(lab, 0); + + cur_lab = 0; + unsigned + n_stop_unknown, + n_stop_object, + n_stop_background; + + p_queue<P> q; + P p_; + mln_niter(N) n(nbh, p_); + + mln_piter(p_array<P>) p(s); + for_all(p) + { + if (lab(p) != 0) + continue; + + mln_invariant(q.is_empty()); + + // Initialization. + ++cur_lab; + n_stop_unknown = 0; + n_stop_object = 0; + n_stop_background = 0; + + mln_value(I) input_p = input(p); + lab(p) = cur_lab; + q.push(p); + + while (! q.is_empty()) + { + p_ = q.pop_front(); + mln_invariant(lab(p_) != 0); // Already seen. + for_all(n) if (input.domain().has(n)) + { + if (lab(n) == cur_lab) + continue; + if (lab(n) == 0) + { +// if (sim(input(n), input(p_)) > 0.7 +// && +// sim(input(n), input_p) > 0.8) + if (math::diff_abs(input(n), input(p_)) < threshold + && + math::diff_abs(input(n), input_p) < 2 * threshold) + { + lab(n) = cur_lab; + q.push(n); + } + else + ++n_stop_unknown; // Stop because of threshold. + continue; + } + mln_invariant(lab(n) != 0 && lab(n) != cur_lab); // Another component. + + if (valid[lab(n)]) + ++n_stop_object; + else + ++n_stop_background; + } + } + + if (n_stop_object + n_stop_background <= n_stop_unknown / 4) + { + valid.push_back(true); + continue; + } + valid.push_back(false); + } + + fun::i2v::array<bool> f_valid; + convert::from_to(valid, f_valid); + + mln_ch_value(I, bool) out = level::transform(lab, f_valid); + + io::pgm::save(labeling::wrap(value::int_u8(), lab), + "tmp_lab.pgm"); + + trace::exiting("labeling__quasi_regional_minima"); + return out; + } + + +} // end of namespace mln + + + +int main(int argc, char *argv[]) +{ + using namespace mln; + using namespace value; + + // trace::quiet = false; + + if (argc != 4) + { + std::cout << argv[0] << " input.pgm threshold output.pbm" << std::endl; + return 1; + } + + int threshold = std::atoi(argv[2]); + + + image2d<int_u8> input; + io::pgm::load(input, argv[1]); + + unsigned n_regions; + image2d<bool> output = labeling__quasi_regional_minima(input, + c4(), + threshold, + n_regions); + + std::cout << n_regions << std::endl; + + io::pbm::save(output, argv[3]); +} Index: lazzara/icdar/binarization.cc --- lazzara/icdar/binarization.cc (revision 3606) +++ lazzara/icdar/binarization.cc (working copy) @@ -1,34 +1,67 @@ +#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/site_set/box.hh> -#include <mln/data/fill.hh> -#include <mln/core/alias/dpoint2d.hh> -#include <mln/core/macros.hh> -#include <mln/core/concept/function.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/pw/cst.hh> + #include <mln/math/abs.hh> -#include <mln/literal/colors.hh> #include <mln/debug/draw_graph.hh> + + + + namespace mln { @@ -202,6 +235,339 @@ } // 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 + } + + + + + template <typename I, typename N, typename L> + mln_ch_value(I, bool) + 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()); + + typedef mln_psite(I) P; + p_array<P> s = level::sort_psites_increasing(input); + + std::vector<bool> valid; + valid.push_back(false); // For valid[0] where 0 is the non-label value. + + mln_ch_value(I, L) lab; + initialize(lab, input); + data::fill(lab, 0); + + L cur_lab = 0; + + p_queue<P> q; + P p_; + mln_niter(N) n(nbh, p_); + + mln_piter(p_array<P>) p(s); + for_all(p) + { + if (lab(p) != 0) + continue; + + mln_invariant(q.is_empty()); + + // Initialization. + ++cur_lab; + valid.push_back(true); + mln_invariant(valid[cur_lab] == true); + lab(p) = cur_lab; + q.push(p); + + while (! q.is_empty()) + { + p_ = q.pop_front(); + mln_invariant(lab(p_) != 0); // Already seen. + for_all(n) + { + if (lab(n) == cur_lab) + continue; + if (lab(n) == 0) + if (math::diff_abs(input(n), input(p_)) < threshold) + { + lab(n) = cur_lab; + q.push(n); + continue; + } + mln_invariant(lab(n) != 0 && lab(n) != cur_lab); + if (valid[cur_lab]) + valid[cur_lab] = false; // Invalidation. + } + } + } + + fun::i2v::array<bool> f_valid; + convert::from_to(valid, f_valid); + + mln_ch_value(I, bool) out = level::transform(lab, f_valid); + + trace::exiting("labeling__quasi_regional_minima"); + return out; + } + + } // end of namespace mln @@ -211,63 +577,263 @@ using namespace mln; using namespace value; - if (argc < 2) + // 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> " << std::endl; + 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> grad = morpho::elementary::gradient_internal(input, c4()); - io::pgm::save(grad, "binarization_grad.pgm"); + 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"); + - image2d<int_u8> clo_vol = morpho::closing::volume(grad, c4(), 500); - io::pgm::save(clo_vol, "binarization_clo.pgm"); + unsigned threshold; + contours(input, threshold); - 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"); + typedef accu::median_h<int_u8> A; + typedef fun::i2v::array<point2d> F; - 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"); - io::ppm::save(debug::colorize(rgb8(), rag_data.second(), nbasins), "binarization_lwsl.ppm"); + 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(wst, nbasins, rag_data.first()); + 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())) + | input.domain(), + argv[4]); + +// 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"); + } - pw::image<fun::i2v::array<int_u8>, - p_vertices<util::graph, fun::i2v::array<point2d> > > - ima_v = make::vertex_image(accu::mean<int_u8>(), pv, input, wst, nbasins); - - pw::image<fun::i2v::array<int_u8>, - p_edges<util::graph,pw::cst_<literal::origin_t> > > - ima_e = make::edge_image(ima_v, - make::dummy_pedges(rag_data.first(),literal::origin), - edge_values()); - - - io::pgm::save(make::debug_graph_image(input,ima_v, ima_e, 4, 0), "binarization_graph.ppm"); - - // FIXME: we should use the following line instead. We cannot since currently - // graph edges are numbered from 0 and we must use mln_max() - 1 as - // background id. - // - //label_16 nelts = rag.first().e_nmax(); - label_16 nelts = mln_max(label_16).prev(); - - util::array<int_u8> median_grad = labeling::compute(accu::meta::median_h(), - grad, - rag_data.second(), - nelts); - fun::i2v::array<int_u8> f; - convert::from_to(median_grad, f); + io::pbm::save((pw::value(level::transform(ws, + out.function())) == pw::cst(255)) + | input.domain(), + argv[4]); - image2d<int_u8> med = level::transform(rag_data.second(), f); + */ - io::pgm::save(med, "binarization_median_grad.pgm"); }
participants (1)
-
Thierry Geraud