
https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox Index: ChangeLog from Thierry Geraud <thierry.geraud@lrde.epita.fr> Add a color segmentation on pixels (not on edges). * theo/color/segment.hh: Update. * theo/color/segment_rgb_pixels.cc: New. * theo/color/segment_rgb_edges.cc: Update. * theo/color/mean_rgb_pix.hh: New. * theo/color/change_attributes.hh: Update. change_attributes.hh | 55 +++++++++++++++-- mean_rgb_pix.hh | 105 ++++++++++++++------------------ segment.hh | 88 ++++++++++++++++++++++++++- segment_rgb_edges.cc | 24 +------ segment_rgb_pixels.cc | 160 ++++++++++++++++++++++++++++++++++++-------------- 5 files changed, 305 insertions(+), 127 deletions(-) Index: theo/color/segment.hh --- theo/color/segment.hh (revision 3313) +++ theo/color/segment.hh (working copy) @@ -8,6 +8,9 @@ #include <mln/make/pix.hh> #include <mln/debug/println.hh> +#include <mln/value/rgb8.hh> +#include <mln/math/diff_abs.hh> + #include <mln/level/sort_psites.hh> #include <mln/labeling/regional_minima.hh> #include <mln/pw/all.hh> @@ -23,6 +26,31 @@ { + // Distance between 2 rgb8 colors. + + value::int_u8 dist(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_(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; + } + + // Sorting. @@ -118,6 +146,58 @@ + + + // Tree -> attributes on every pixel. + // Warning : it is not an attribute per flat zone! + // ----------------------------------------------- + + template <typename A, typename T> + inline + mln_ch_value(typename T::function, mln_result(A)) + compute_attribute_on_pixels__not_on_flat_zones(const A& a, const T& t) + { + typedef typename T::function I; + + mln_ch_value(I, A) acc; + mln_ch_value(I, mln_result(A)) attr; + + // Initialization of 'acc'. + { + initialize(acc, t.f()); + data::fill(acc, a); // Transfer "dynamic data" (state) of 'a'. + } + + // Initialize every attribute with the corresponding pixel. + { + mln_piter(I) p(t.f().domain()); + for_all(p) + acc(p).take_as_init(make::pix(t.f(), p)); + } + + // Propagate attribute from a site to its parent. + { + mln_fwd_piter(T) p(t.domain()); + for_all(p) + if (! t.is_root(p)) + acc(t.parent(p)).take(acc(p)); + } + + + // Change accumulator into its result. + { + initialize(attr, acc); + mln_piter(I) p(t.f().domain()); // The main difference is here! + for_all(p) + attr(p) = acc(p).to_result(); + } + + return attr; + } + + + + // Tree -> nchildren (on nodes). // ----------------------------- @@ -228,7 +308,7 @@ if (echo) { - back_propagate(filtered, t); + back_propagate(t, filtered); debug::println("filtered =", filtered); debug::println("a < lambda = ", (pw::value(a) < pw::cst(lambda)) | a.domain()); } @@ -344,7 +424,7 @@ tree_t t(f, s, nbh); mln_VAR(a, compute_attribute_on_nodes(a_, t)); - back_propagate(a, t); + back_propagate(t, a); if (echo) { @@ -363,7 +443,7 @@ break; case 2: std::cout << "fuse up attributes" << std::endl; - fuse_down_attributes(t, a, echo); + fuse_up_attributes(t, a, echo); break; } @@ -409,7 +489,7 @@ T t(f, s, nbh); mln_VAR(a, compute_attribute_on_nodes(a_, t)); - back_propagate(a, t); + back_propagate(t, a); unsigned n_regmins_f; // This value can be obtained while computing the attributes! Index: theo/color/segment_rgb_pixels.cc --- theo/color/segment_rgb_pixels.cc (revision 3313) +++ theo/color/segment_rgb_pixels.cc (working copy) @@ -4,34 +4,101 @@ #include <mln/core/alias/neighb2d.hh> #include <mln/value/int_u8.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/io/pgm/load.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/elementary/gradient.hh> #include <mln/morpho/meyer_wst.hh> -#include <mln/accu/height.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; + } + + + + + + // 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); + + typedef typename T::nodes_t N; + mln_fwd_piter(N) n(t.nodes()); + for_all(n) + d(n) = dist(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.pgm n change extinction echo output.ppm" << std::endl; - std::cerr << "gray-level version on pixels (without edges)" << std::endl; + 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 << " n >= 2" << std::endl; - std::cerr << " change = 0 (none), 1 (move-down), or 2 (fuse)" << std::endl; - std::cerr << " extinction = 0 (none) or 1 (effective)" << std::endl; std::cerr << " echo = 0 (mute) or 1 (verbose)" << std::endl; abort(); } @@ -46,30 +113,22 @@ using value::rgb8; - if (argc != 7) + if (argc != 5) usage(argv); - // Gray-level debase version. - // -------------------------- + // Color debase (on pixels, not edges) version. + // -------------------------------------------- - typedef image2d<int_u8> I; + typedef image2d<rgb8> I; I input; - io::pgm::load(input, argv[1]); + io::ppm::load(input, argv[1]); unsigned n_objects = atoi(argv[2]); - int change_kind = atoi(argv[3]); - if (change_kind < 0 || change_kind > 2) - usage(argv); - - int do_extinction = atoi(argv[4]); - if (do_extinction != 0 && do_extinction != 1) - usage(argv); - - bool echo = atoi(argv[5]); + bool echo = atoi(argv[3]); // Neighborhood. @@ -78,40 +137,55 @@ // Changing input into 'f'. - I f = morpho::elementary::gradient(input, c4()); + 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()); - // // granulometry: - // gran_filter(f, c4(), a_); + 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); + } - // accu::count< util::pix<I> > a_; - // accu::sum_pix< util::pix<I> > a_; - // accu::volume<I> a_; - accu::height<I> a_; - // It seems that both extinction and changing attributes only - // properly works for the 'height' attribute. + // BAD: extinct_attributes(t, d, echo); - I g = filter(f, c4(), - a_, - change_kind, do_extinction, - n_objects, + + int_u8 lambda; + unsigned less; + image2d<int_u8> g = run_filter(d, t, c4(), n_objects, // input + less, lambda, // output echo); - if (echo) - debug::println("activity (g != f) = ", (pw::value(g) != pw::value(f)) | f.domain()); + 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()); // Watershed transform. - typedef value::label_8 L; + typedef value::label_16 L; L nbasins; mln_ch_value_(I, L) w = morpho::meyer_wst(g, c4(), nbasins); + std::cout << "n basins = " << nbasins << std::endl; - image2d<rgb8> output = level::convert(rgb8(), input); - data::fill((output | (pw::value(w) == 0)).rw(), literal::red); + image2d<rgb8> output = duplicate(input); + data::fill((output | (pw::value(w) == 0)).rw(), literal::black); - io::ppm::save(output, argv[6]); + io::ppm::save(output, argv[4]); } Index: theo/color/segment_rgb_edges.cc --- theo/color/segment_rgb_edges.cc (revision 3313) +++ theo/color/segment_rgb_edges.cc (working copy) @@ -17,8 +17,6 @@ #include <mln/io/ppm/load.hh> #include <mln/io/ppm/save.hh> -#include <mln/math/diff_abs.hh> - #include <mln/morpho/dilation.hh> #include <mln/morpho/erosion.hh> #include <mln/morpho/meyer_wst.hh> @@ -29,11 +27,10 @@ #include <mln/accu/mean.hh> #include "sum_pix.hh" - - #include "segment.hh" + namespace mln { @@ -192,22 +189,11 @@ - // Distance. - - value::int_u8 dist(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; - } + // Distance, stored on edges, of a couple of colors. template <typename I, typename N> image2d<value::int_u8> - dist(const I& input, const N& nbh) + dist_on_edges(const I& input, const N& nbh) { image2d<value::int_u8> output; initialize(output, input); @@ -224,6 +210,8 @@ output(p) = dist(c1, c2); } + io::pgm::save(output, "temp_dist.pgm"); + return output; } @@ -288,7 +276,7 @@ image2d<int_u8> f_; image2d<rgb8> input_ = image2full(input); { - f_ = dist(extend(input_ | is_edge, pw::value(input_)), + f_ = dist_on_edges(extend(input_ | is_edge, pw::value(input_)), e2c()); } mln_VAR(f, f_ | is_edge); Index: theo/color/mean_rgb_pix.hh --- theo/color/mean_rgb_pix.hh (revision 3313) +++ theo/color/mean_rgb_pix.hh (working copy) @@ -26,57 +26,57 @@ // reasons why the executable file might be covered by the GNU General // Public License. -#ifndef MLN_ACCU_SUM_PIX_HH -# define MLN_ACCU_SUM_PIX_HH +#ifndef MLN_ACCU_MEAN_RGB_PIX_HH +# define MLN_ACCU_MEAN_RGB_PIX_HH -/// \file mln/accu/sum_pix.hh +/// \file mln/accu/mean_rgb_pix.hh /// -/// Define an accumulator that computes a sum. +/// Define an accumulator that computes an rgb8 mean. # include <mln/core/concept/meta_accumulator.hh> # include <mln/accu/internal/base.hh> -# include <mln/util/pix.hh> // To prevent accu::sum_pix to work on pixels (ambiguous). +# include <mln/value/rgb8.hh> +# include <mln/algebra/vec.hh> -# include <mln/trait/value_.hh> // For mln_sum_pix. -# include <mln/value/builtin/all.hh> // In the case of summing builtin values. -# include <mln/literal/zero.hh> // For initialization. +# include <mln/util/pix.hh> +# include <mln/core/image/image2d.hh> namespace mln { + namespace accu { - /// Generic sum_pix accumulator class. - /*! - * Parameter \c T is the type of values that we sum. Parameter \c - * S is the type to store the value sum; the default type of - * \c S is the summation type (property) of \c T. - */ - template <typename P, typename S = mln_sum(mln_value(P))> - struct sum_pix : public mln::accu::internal::base< const S&, sum_pix<P,S> > + image2d<value::rgb8> rgb_image_; + + + /// Generic mean_rgb_pix accumulator class. + /// + template <typename Pix> + struct mean_rgb_pix : public mln::accu::internal::base< value::rgb8, mean_rgb_pix<Pix> > { - typedef P argument; + typedef Pix argument; - sum_pix(); + mean_rgb_pix(); /// Manipulators. /// \{ void init(); void take(const argument& t); - void take(const sum_pix<P,S>& other); + void take(const mean_rgb_pix<Pix>& other); /// \} - void set_value(S v) - { - s_ = v; - } +// void set_value(S v) +// { +// s_ = v; +// } /// Get the value of the accumulator. - const S& to_result() const; + value::rgb8 to_result() const; /// Check whether this accu is able to return a result. /// Always true here. @@ -84,70 +84,59 @@ protected: - S s_; + typedef algebra::vec<3,float> vec_t; + vec_t s_; + unsigned n_; }; - namespace meta - { - - /// Meta accumulator for sum_pix. - struct sum_pix : public Meta_Accumulator< sum_pix > - { - template <typename P, typename S = mln_sum(mln_value(P))> - struct with - { - typedef accu::sum_pix<P, S> ret; - }; - }; - - } // end of namespace mln::accu::meta - - # ifndef MLN_INCLUDE_ONLY - template <typename P, typename S> + template <typename Pix> inline - sum_pix<P,S>::sum_pix() + mean_rgb_pix<Pix>::mean_rgb_pix() { init(); } - template <typename P, typename S> + template <typename Pix> inline void - sum_pix<P,S>::init() + mean_rgb_pix<Pix>::init() { - s_ = literal::zero; + s_ = vec_t::zero; + n_ = 0; } - template <typename P, typename S> + template <typename Pix> inline - void sum_pix<P,S>::take(const argument& p) + void mean_rgb_pix<Pix>::take(const argument& px) { - s_ += /* 1 + */ p.v(); + s_ += vec_t(rgb_image_(px.p())); + ++n_; } - template <typename P, typename S> + template <typename Pix> inline void - sum_pix<P,S>::take(const sum_pix<P,S>& other) + mean_rgb_pix<Pix>::take(const mean_rgb_pix<Pix>& other) { s_ += other.s_; + n_ += other.n_; } - template <typename P, typename S> + template <typename Pix> inline - const S& - sum_pix<P,S>::to_result() const + value::rgb8 + mean_rgb_pix<Pix>::to_result() const { - return s_; + return algebra::vec<3, unsigned>(s_ / n_); } - template <typename P, typename S> + template <typename Pix> inline bool - sum_pix<P,S>::is_valid() const + mean_rgb_pix<Pix>::is_valid() const { return true; } @@ -159,4 +148,4 @@ } // end of namespace mln -#endif // ! MLN_ACCU_SUM_PIX_HH +#endif // ! MLN_ACCU_MEAN_RGB_PIX_HH Index: theo/color/change_attributes.hh --- theo/color/change_attributes.hh (revision 3313) +++ theo/color/change_attributes.hh (working copy) @@ -38,12 +38,58 @@ + // Test attribute growing property. + // -------------------------------- + + template <typename T, typename A> + inline + bool + test_attribute_growing_property(const T& t, const A& a) + { + typedef typename T::nodes_t N; + mln_fwd_piter(N) n(t.nodes()); + for_all(n) + if (a(t.parent(n)) < a(n)) + return false; + return true; + } + + template <typename T, typename A> + inline + void + invariant__attribute_growing_property(const T& t, const A& a) + { + mln_invariant(test_attribute_growing_property(t, a)); + } + + + + + // Make attributes grow. + // -------------------------------- + + template <typename T, typename A> + inline + void + make_attribute_grow(const T& t, A& a) + { + typedef typename T::nodes_t N; + mln_fwd_piter(N) n(t.nodes()); + for_all(n) + if (a(t.parent(n)) < a(n)) + a(t.parent(n)) = a(n); + + mln_postcondition(test_attribute_growing_property(t, a)); + } + + + // Back-propagate values from nodes to component sites. // ---------------------------------------------------- - template <typename A, typename T> + template <typename T, typename A> void - back_propagate(A& a, const T& t) + back_propagate(const T& t, A& a) { mln_fwd_piter(T) p(t.domain()); for_all(p) @@ -243,13 +289,13 @@ - // Fuse down. + // Fuse up. // ---------- template <typename T, typename A> inline void - fuse_down_attributes(const T& t, A& a, bool echo = false) + fuse_up_attributes(const T& t, A& a, bool echo = false) { if (echo) { @@ -273,6 +319,7 @@ + // Rand. // -----