
https://svn.lrde.epita.fr/svn/oln/trunk/milena Index: ChangeLog from Thierry Geraud <thierry.geraud@lrde.epita.fr> Have all labeling routines be consistent. * mln/core/concept/window.hh, * mln/core/concept/neighborhood.hh (positive_offsets_wrt): New. * mln/morpho/tree/compute_parent.hh (todo): New. (doc): Augment. * mln/morpho/tree/data.hh (todo): New. * mln/canvas/labeling.hh: Swap fwd and bkd between both passes. (todo): New. Now it is consistent with labeling::blobs. * tests/level/sort_psites.cc: Test reversibility. * tests/morpho/tree/data.cc: Precise fwd when needed. * tests/labeling/foreground.cc: Upgrade doc style. Augment test. mln/canvas/labeling.hh | 26 +++++------- mln/core/concept/neighborhood.hh | 18 ++++++++ mln/core/concept/window.hh | 30 ++++++++++++++ mln/morpho/tree/compute_parent.hh | 78 ++++++++++++++++++++++++++++++++++---- mln/morpho/tree/data.hh | 7 ++- tests/labeling/foreground.cc | 38 +++++++++++++++--- tests/level/sort_psites.cc | 19 ++++++--- tests/morpho/tree/data.cc | 15 +++++-- 8 files changed, 193 insertions(+), 38 deletions(-) Index: mln/core/concept/window.hh --- mln/core/concept/window.hh (revision 3494) +++ mln/core/concept/window.hh (working copy) @@ -121,6 +121,10 @@ template <typename I, typename W> util::array<int> + positive_offsets_wrt(const Image<I>& ima, const Window<W>& win); + + template <typename I, typename W> + util::array<int> negative_offsets_wrt(const Image<I>& ima, const Window<W>& win); @@ -346,6 +350,32 @@ template <typename I, typename W> inline util::array<int> + positive_offsets_wrt(const Image<I>& ima_, const Window<W>& win_) + { + mln_is_simple_window(W)::check(); + + const I& ima = exact(ima_); + const W& win = exact(win_); + mln_precondition(ima.is_valid()); + mln_precondition(win.is_valid()); + + util::array<int> arr; + unsigned n = win.size(); + + for (unsigned i = 0; i < n; ++i) + { + int offset = ima.delta_index(win.dp(i)); + if (offset > 0) + arr.append(offset); + } + + return arr; + } + + + template <typename I, typename W> + inline + util::array<int> negative_offsets_wrt(const Image<I>& ima_, const Window<W>& win_) { mln_is_simple_window(W)::check(); Index: mln/core/concept/neighborhood.hh --- mln/core/concept/neighborhood.hh (revision 3494) +++ mln/core/concept/neighborhood.hh (working copy) @@ -104,6 +104,10 @@ template <typename I, typename N> util::array<int> + positive_offsets_wrt(const Image<I>& ima, const Neighborhood<N>& nbh); + + template <typename I, typename N> + util::array<int> negative_offsets_wrt(const Image<I>& ima, const Neighborhood<N>& nbh); @@ -166,6 +170,20 @@ template <typename I, typename N> util::array<int> + positive_offsets_wrt(const Image<I>& ima_, const Neighborhood<N>& nbh_) + { + mln_is_simple_neighborhood(N)::check(); + + const I& ima = exact(ima_); + const N& nbh = exact(nbh_); + mln_precondition(ima.is_valid()); + mln_precondition(nbh.is_valid()); + + return positive_offsets_wrt(ima, nbh.win()); + } + + template <typename I, typename N> + util::array<int> negative_offsets_wrt(const Image<I>& ima_, const Neighborhood<N>& nbh_) { mln_is_simple_neighborhood(N)::check(); Index: mln/morpho/tree/compute_parent.hh --- mln/morpho/tree/compute_parent.hh (revision 3494) +++ mln/morpho/tree/compute_parent.hh (working copy) @@ -1,4 +1,5 @@ -// Copyright (C) 2008 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2008, 2009 EPITA Research and Development Laboratory +// (LRDE) // // This file is part of the Olena Library. This library is free // software; you can redistribute it and/or modify it under the terms @@ -33,6 +34,11 @@ /// Compute a canonized tree from an image. /// /// \todo Specialize for low quant (and try fastest). +/// +/// \todo Augment and improve documentation. +/// +/// \todo Change level::sort so that the explanations below are valid +/// whatever the choice 'increasing or decreasing'. # include <mln/core/concept/image.hh> # include <mln/core/concept/neighborhood.hh> @@ -55,16 +61,72 @@ /// "natural" childhood relationship. The parenthood is thus /// inverted w.r.t. to \p s. /// - /// It is very convenient since all processing upon the parent - /// tree are performed following \p s (in the default "forward" - /// way). + /// It is very convenient since most processing routines upon + /// the parent tree are performed following \p s (in the default + /// "forward" way). Indeed that is the way to propagate + /// information from parents to children. /// - /// FIXME: Put it more clearly... /// /// The parent result image verifies: \n /// - p is root iff parent(p) == p \n /// - p is a node iff either p is root or f(parent(p)) != f(p). - + /// + /// + /// + /// The choice "s means childhood" is consistent with labeling + /// in binary images. In that particular case, while browsing + /// the image in forward scan (video), we expect to find first a + /// tree root (a first point, representative of a component) and + /// then the other component points. Please note that it leads + /// to increasing values of labels in the "natural" video scan. + /// + /// Since mathematical morphology on functions is related to + /// morphology on sets, we clearly want to keep the equivalence + /// between "component labeling" and "component filtering" using + /// trees. + /// + /// + /// FIXME: Put it more clearly... Insert pictures! + /// + /// A binary image: + /// + /// - | | - - \n + /// - | | - | \n + /// - - - - - \n + /// - - | | - \n + /// + /// where '|' means true and '-' means false. + /// + /// Its labeling: + /// + /// 0 1 1 0 0 \n + /// 0 1 1 0 2 \n + /// 0 0 0 0 0 \n + /// 0 0 3 3 0 \n + /// + /// The corresponding forest: + /// + /// x o . x x \n + /// x . . x o \n + /// x x x x x \n + /// x x o . x \n + /// + /// where 'x' means "no data", 'o' is a tree root + /// (representative point for a component), and '.' is a tree + /// regular (non-root) point (in a component by not its + /// representative point). + /// + /// + /// The forest, with the parent relationship looks like: + /// + /// o < . \n + /// ^ r \n + /// . . o \n + /// \n + /// \n + /// o < . \n + /// + /// template <typename I, typename N, typename S> mln_ch_value(I, mln_psite(I)) compute_parent(const Image<I>& f, const Neighborhood<N>& nbh, @@ -159,7 +221,7 @@ data::fill(deja_vu, false); // Body. - mln_bkd_piter(S) p(s); + mln_bkd_piter(S) p(s); // Backward. mln_niter(N) n(nbh, p); for_all(p) { @@ -183,7 +245,7 @@ // Canonization. { - mln_fwd_piter(S) p(s); + mln_fwd_piter(S) p(s); // Forward. for_all(p) { P q = parent(p); Index: mln/morpho/tree/data.hh --- mln/morpho/tree/data.hh (revision 3494) +++ mln/morpho/tree/data.hh (working copy) @@ -1,4 +1,5 @@ -// Copyright (C) 2008 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2008, 2009 EPITA Research and Development Laboratory +// (LRDE) // // This file is part of the Olena Library. This library is free // software; you can redistribute it and/or modify it under the terms @@ -31,6 +32,10 @@ /// \file mln/morpho/tree/data.hh /// /// FIXME: First Attempt. +/// +/// \todo Fix the issue pointed to by Edwin without modifying the way +/// sites are browsed (see the documentation of compute_parent to +/// learn why we want the 1st pass to be in forward scan of s). # include <mln/morpho/tree/compute_parent.hh> # include <mln/core/image/sub_image.hh> Index: mln/canvas/labeling.hh --- mln/canvas/labeling.hh (revision 3494) +++ mln/canvas/labeling.hh (working copy) @@ -1,5 +1,5 @@ -// Copyright (C) 2007, 2008, 2009 EPITA Research and Development Laboratory -// (LRDE) +// Copyright (C) 2007, 2008, 2009 EPITA Research and Development +// Laboratory (LRDE) // // This file is part of the Olena Library. This library is free // software; you can redistribute it and/or modify it under the terms @@ -33,8 +33,8 @@ /// /// Connected component labeling of the object part in a binary image. /// -/// \todo Make the fastest version work. -/// FIXME: is 'status' useful? +/// \todo Can we get rid of 'deja_vu' (while playing with the border) +/// in the fastest video version? # include <mln/core/concept/image.hh> # include <mln/data/fill.hh> @@ -139,7 +139,7 @@ // Output. mln_ch_value(I, L) output; - bool status; + bool status; // FIXME: Is-it useful? // Initialization. { @@ -157,7 +157,7 @@ // First Pass. { - mln_fwd_piter(S) p(s); + mln_bkd_piter(S) p(s); // Backward. mln_niter(N) n(nbh, p); for_all(p) if (f.handles(p)) { @@ -187,7 +187,7 @@ // Second Pass. { - mln_bkd_piter(S) p(s); + mln_fwd_piter(S) p(s); // Forward. for_all(p) if (f.handles(p)) { if (parent(p) == p) // if p is root @@ -274,10 +274,10 @@ // First Pass. { - util::array<int> dp = negative_offsets_wrt(input, nbh); + util::array<int> dp = positive_offsets_wrt(input, nbh); const unsigned n_nbhs = dp.nelements(); - mln_pixter(const I) px(input); + mln_bkd_pixter(const I) px(input); // Backward. for_all(px) { unsigned p = px.offset(); @@ -311,7 +311,7 @@ // Second Pass. { - mln_bkd_pixter(const I) px(input); + mln_fwd_pixter(const I) px(input); // Forward. for_all(px) { unsigned p = px.offset(); @@ -394,8 +394,7 @@ // First Pass. { - - for (unsigned i = 0; i < n_points; ++i) + for (int i = n_points - 1; i >=0; --i) // Backward. { unsigned p = s[i]; if (! f.handles_(p)) @@ -425,13 +424,12 @@ f.do_no_union_(n, p); } deja_vu.element(p) = true; - } } // Second Pass. { - for (int i = n_points - 1; i >=0; --i) + for (unsigned i = 0; i < n_points; ++i) // Forward. { unsigned p = s[i]; if (! f.handles_(p)) Index: tests/level/sort_psites.cc --- tests/level/sort_psites.cc (revision 3494) +++ tests/level/sort_psites.cc (working copy) @@ -1,5 +1,5 @@ -// Copyright (C) 2007, 2008 EPITA Research and Development Laboratory -// (LRDE) +// Copyright (C) 2007, 2008, 2009 EPITA Research and Development +// Laboratory (LRDE) // // This file is part of the Olena Library. This library is free // software; you can redistribute it and/or modify it under the terms @@ -31,7 +31,7 @@ /// Tests on mln::level::sort_psites. #include <mln/core/image/image2d.hh> -#include <mln/debug/iota.hh> +#include <mln/make/image2d.hh> #include <mln/level/sort_psites.hh> #include <mln/core/site_set/p_array.hh> @@ -40,11 +40,20 @@ { using namespace mln; - image2d<int> ima(3, 3); - debug::iota (ima); + int vals[] = { 0, 3, 4, + 2, 2, 2, + 0, 1, 4 }; + image2d<int> ima = make::image2d(vals); p_array<point2d> array_inc = level::sort_psites_increasing(ima); p_array<point2d> array_dec = level::sort_psites_decreasing(ima); + { + p_array<point2d>::fwd_piter p1(array_inc); + p_array<point2d>::bkd_piter p2(array_dec); + for_all_2(p1, p2) + mln_assertion(ima(p1) == ima(p2)); + } + p_array<point2d> array_inc_ref; p_array<point2d> array_dec_ref; Index: tests/morpho/tree/data.cc --- tests/morpho/tree/data.cc (revision 3494) +++ tests/morpho/tree/data.cc (working copy) @@ -1,4 +1,5 @@ -// Copyright (C) 2008 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2008, 2009 EPITA Research and Development Laboratory +// (LRDE) // // This file is part of the Olena Library. This library is free // software; you can redistribute it and/or modify it under the terms @@ -62,9 +63,17 @@ { std::cout << "nodes = "; - tree_t::nodes_t::piter p(t.nodes()); + tree_t::nodes_t::fwd_piter p(t.nodes()); for_all(p) std::cout << p << ' '; + std::cout << std::endl; + } + { + std::cout << "nodes = "; + tree_t::fwd_piter p(t.domain()); + for_all(p) + if (t.is_a_node(p)) + std::cout << p << ' '; std::cout << std::endl << std::endl; } @@ -73,7 +82,7 @@ { image2d<unsigned> area(ima.domain()); data::fill(area, 1); - tree_t::piter p(t.domain()); + tree_t::fwd_piter p(t.domain()); for_all(p) if (! t.is_root(p)) area(t.parent(p)) += area(p); Index: tests/labeling/foreground.cc --- tests/labeling/foreground.cc (revision 3494) +++ tests/labeling/foreground.cc (working copy) @@ -1,4 +1,5 @@ -// Copyright (C) 2007, 2008 EPITA Research and Development Laboratory (LRDE) +// Copyright (C) 2007, 2008, 2009 EPITA Research and Development +// Laboratory (LRDE) // // This file is part of the Olena Library. This library is free // software; you can redistribute it and/or modify it under the terms @@ -25,14 +26,15 @@ // reasons why the executable file might be covered by the GNU General // Public License. -/*! \file tests/labeling/foreground.cc - * - * \brief Test on mln::labeling::foreground. - */ +/// \file tests/labeling/foreground.cc +/// +/// Test on mln::labeling::foreground. #include <mln/core/image/image2d.hh> +#include <mln/core/var.hh> #include <mln/io/pbm/load.hh> #include <mln/core/alias/neighb2d.hh> +#include <mln/level/compare.hh> #include <mln/labeling/foreground.hh> #include "tests/data.hh" @@ -42,8 +44,30 @@ { using namespace mln; - image2d<bool> pic = io::pbm::load(MLN_IMG_DIR "/picasso.pbm"); + typedef image2d<bool> I; + mln_VAR(nbh, c4()); + + I pic = io::pbm::load(MLN_IMG_DIR "/picasso.pbm"); + image2d<unsigned> out, ref; + unsigned n; - labeling::foreground(pic, c4(), n); + out = labeling::foreground(pic, nbh, n); // Calls the fastest 'video' + // version. mln_assertion(n == 33); + + { + // Note that labeling::foreground actually is labeling::level + // which calls canvas::labeling_video and its generic dispatch + // leads to canvas::impl::generic::labeling. + + labeling::impl::level_functor<I> f(pic, true); + + unsigned n_; + ref = canvas::impl::generic::labeling(pic, nbh, n_, + pic.domain(), + f); + mln_invariant(n_ == n); + mln_invariant(ref == out); + } + }