cleanup-2008 2896: Import skeleton from matthieu's sandbox.

https://svn.lrde.epita.fr/svn/oln/branches/cleanup-2008/milena Index: ChangeLog from Thierry Geraud <thierry.geraud@lrde.epita.fr> Import skeleton from matthieu's sandbox. * mln/topo/is_simple_2d.hh: New. * mln/morpho/skeleton_constrained.hh: New. * tests/morpho/skeleton_constrained.cc: New. * tests/morpho/Makefile.am: Update. * img/small.pbm: New. * img/fly.pbm: New. * mln/win/multiple_size.hh (i_): Change type to signed. (size_): Change return type to int to prevent warning. (is_valid_, invalidate_): Fix the case of the iterator construction is deferred. img/fly.pbm | 5 mln/morpho/skeleton_constrained.hh | 177 +++++++++++------------------------ mln/topo/is_simple_2d.hh | 167 +++++++++++++-------------------- mln/win/multiple_size.hh | 12 +- tests/morpho/Makefile.am | 3 tests/morpho/skeleton_constrained.cc | 104 ++++++++++++++++++++ 6 files changed, 246 insertions(+), 222 deletions(-) Index: tests/morpho/skeleton_constrained.cc --- tests/morpho/skeleton_constrained.cc (revision 0) +++ tests/morpho/skeleton_constrained.cc (revision 0) @@ -0,0 +1,104 @@ +// Copyright (C) 2008 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 +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 51 Franklin Street, Fifth Floor, +// Boston, MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +/// \file tests/morpho/skeleton_constrained.cc +/// +/// Test on mln::morpho::skeleton_constrained. +/// + +#include <mln/core/image/image2d.hh> +#include <mln/core/alias/neighb2d.hh> +#include <mln/core/var.hh> +#include <mln/value/int_u8.hh> + +#include <mln/make/dual_neighb.hh> +#include <mln/topo/is_simple_2d.hh> +#include <mln/morpho/skeleton_constrained.hh> + +#include <mln/io/pbm/load.hh> +#include <mln/io/pbm/save.hh> +#include <mln/level/fill.hh> +#include <mln/debug/println.hh> +#include <mln/debug/println_with_border.hh> + +#include <mln/pw/value.hh> +#include <mln/core/image/image_if.hh> + +#include "tests/data.hh" + + +namespace mln +{ + + template <typename N> + void show_connectivity_numbers(const image2d<bool>& ima, + const N& nbh) + { + extension::adjust_fill(ima, nbh, false); + + image2d<unsigned> when_true(ima.domain()), when_false(ima.domain()); + mln_piter(box2d) p(ima.domain()); + for_all(p) + { + when_true(p) = connectivity_number_2d(ima, nbh.foreground(), p, true); + when_false(p) = connectivity_number_2d(ima, nbh.background(), p, false); + } + debug::println("when true = ", when_true | pw::value(ima)); + debug::println("when false = ", when_false | pw::value(ima)); + } + +} // mln + + +int main() +{ + using namespace mln; + using value::int_u8; + + image2d<bool> pic; + io::pbm::load(pic, MLN_IMG_DIR "/tiny.pbm"); + + mln_VAR( nbh, + make::dual_neighb(pic, c4(), c8()) ); + + show_connectivity_numbers(pic, nbh); + + + image2d<bool> K(pic.domain()); + level::fill(K, false); + + image2d<int_u8> prior(pic.domain()); + level::fill(prior, 1); + + mln_VAR( skl, + morpho::skeleton_constrained(pic, + nbh, is_simple_2d_t(), + K, prior) ); + + debug::println("pic =", pic); + debug::println("skl =", skl); +} Index: tests/morpho/Makefile.am --- tests/morpho/Makefile.am (revision 2895) +++ tests/morpho/Makefile.am (working copy) @@ -31,6 +31,7 @@ opening_area \ opening_height \ opening_volume \ + skeleton_constrained \ thinning # -------------- # @@ -67,6 +68,8 @@ meyer_wst_SOURCES = meyer_wst.cc +skeleton_constrained_SOURCES = skeleton_constrained.cc + combined_SOURCES = combined.cc # --------------- # Index: mln/topo/is_simple_2d.hh --- mln/topo/is_simple_2d.hh (revision 2893) +++ mln/topo/is_simple_2d.hh (working copy) @@ -1,4 +1,4 @@ -// Copyright (C) 2007, 2008 EPITA Research and Development Laboratory +// Copyright (C) 2008 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,47 +25,59 @@ // reasons why the executable file might be covered by the GNU General // Public License. -#ifndef MLN_SIMPLE_POINT_HH -# define MLN_SIMPLE_POINT_HH +#ifndef MLN_TOPO_IS_SIMPLE_2D_HH +# define MLN_TOPO_IS_SIMPLE_2D_HH -/*! \file simple_point.hh - * - * \brief is_simple_point tell if a point is simple or not (Cf - * bertrand.07.chap). - * - */ +/// \file mln/topo/is_simple_2d.hh +/// +/// Define the function is_simple_2d which tests if a point is simple +/// or not (Cf bertrand.07.chap). + +#include <mln/core/concept/image.hh> +#include <mln/core/concept/neighborhood.hh> #include <mln/core/alias/point2d.hh> -#include <mln/core/image/image2d.hh> #include <mln/core/alias/neighb2d.hh> + namespace mln { -/*! Tell if a point is simple or not. A point of an object is simple - * if in its c8 neiborhood, there is exactly one connected component of the - * object, and only one connected component of the background - * Examples : ( | == object, - = background) - * - * - - | - * | P | Here p is simple in the c4 and c8 case. - * | | | - * - * - | - - * | P | Here p is never simple. - * | | | - * - */ + /// Test if a point is simple or not. A point of an object is simple + /// if in its c8 neiborhood, there is exactly one connected component of the + /// object, and only one connected component of the background + /// Examples : ( | == object, - = background) + /// + /// - - | + /// | P | Here p is simple in the c4 and c8 case. + /// | | | + /// + /// - | - + /// | P | Here p is never simple. + /// | | | + + template<typename I, typename N> + bool + is_simple_2d(const Image<I>& ima, const Neighborhood<N>& nbh, const mln_psite(I)& p); + + - bool is_simple_point(const image2d<bool>& ima, const neighb2d& nbh, const point2d& p); + struct is_simple_2d_t + { + template<typename I, typename N> + bool operator()(const Image<I>& ima, + const Neighborhood<N>& nbh, + const mln_psite(I)& p) const + { + return is_simple_2d(ima, nbh, p); + } + }; - unsigned nb_connexity2d(const image2d<bool>& ima, const neighb2d& nbh, const point2d& p); - bool is_curve_extremum(const image2d<bool>& ima, unsigned nbh, const point2d& p); # ifndef MLN_INCLUDE_ONLY - static const unsigned char nb_connexity_c8[256] = + static const unsigned char connectivity_number_c8[256] = { 0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, @@ -88,7 +100,7 @@ 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; - static const unsigned char nb_connexity_c4[256] = + static const unsigned char connectivity_number_c4[256] = { 0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 2, 2, 2, 1, 2, 1, 1, 2, 2, 3, 2, 2, 2, 2, 1, @@ -111,96 +123,57 @@ 1, 2, 2, 2, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1 }; - neighb2d int_to_neighb(unsigned i) - { - if (i == 8) - return c8(); - if (i == 4) - return c4(); - mln_assertion(0); - return c4(); - } - unsigned complement_neighb(unsigned i) + template<typename I, typename N> + inline + unsigned + connectivity_number_2d(const Image<I>& ima_, const Neighborhood<N>& nbh_, + const mln_psite(I)& p, bool b) { - if (i == 8) - return 4; - if (i == 4) - return 8; - mln_assertion(0); - return 0; - } + const I& ima = exact(ima_); + const N& nbh = exact(nbh_); - unsigned nb_connexity2d(const image2d<bool>& ima, unsigned nbh, const point2d& p, bool object) - { unsigned res = 0; - mln_bkd_niter_(neighb2d) n(c8() , p); + mln_bkd_niter(neighb2d) n(c8(), p); for_all(n) { res = (res << 1); - if (ima.domain().has(n) && ima(n) == object) + if (ima.domain().has(n) && ima(n) == b) res = res | 1; } - if (nbh == 8) - return nb_connexity_c8[res]; - else - { - mln_assertion(nbh == 4); - return nb_connexity_c4[res]; - } - } - - bool is_curve_extremum(const image2d<bool>& ima, unsigned nbh_i, const point2d& p_, unsigned deep) - { -// return false; - unsigned cpt = 0; - mln_site_(image2d<bool>) next = p_; - mln_site_(image2d<bool>) p = next; - mln_niter_(neighb2d) n(int_to_neighb(nbh_i) , p); + unsigned number; - p = next; - for_all(n) + switch (nbh.size()) { - if (ima.domain().has(n) && ima(n) == true) - { - next = n; - cpt++; - } + case 4: + number = connectivity_number_c4[res]; + break; + case 8: + number = connectivity_number_c8[res]; + break; + default: + mln_assertion(0); } - if (cpt != 1) - return false; - for (unsigned i = 0; i < deep - 1; i++) - { - cpt = 0; - p = next; - for_all(n) - { - if (ima.domain().has(n) && ima(n) == true) - { - next = n; - cpt++; - } - } - if (cpt != 2) - return false; + return number; } - return true; - } - bool is_simple_point2d(const image2d<bool>& ima, unsigned nbh, const point2d& p) + template<typename I, typename N> + inline + bool + is_simple_2d(const Image<I>& ima, const Neighborhood<N>& nbh_, const mln_psite(I)& p) { - mln_assertion(nbh == 4 || nbh == 8); - - return (nb_connexity2d(ima, nbh, p, true) == 1) && - (nb_connexity2d(ima, complement_neighb(nbh), p, false) == 1); + const N& nbh = exact(nbh_); + return + connectivity_number_2d(ima, nbh.foreground(), p, true ) == 1 && + connectivity_number_2d(ima, nbh.background(), p, false) == 1; } # endif // MLN_INCLUDE_ONLY } // end of namespace mln -#endif // ! MLN_SIMPLE_POINT_HH +#endif // ! MLN_TOPO_IS_SIMPLE_2D_HH Index: mln/win/multiple_size.hh --- mln/win/multiple_size.hh (revision 2895) +++ mln/win/multiple_size.hh (working copy) @@ -159,8 +159,8 @@ mln_psite(W) compute_p_() const; private: - unsigned i_; - unsigned size_() const; + int i_; + int size_() const; }; @@ -321,7 +321,7 @@ bool multiple_size_qiter<n,W,F>::is_valid_() const { - return i_ < size_(); + return i_ != -1 && i_ < size_(); } template <unsigned n, typename W, typename F> @@ -329,7 +329,7 @@ void multiple_size_qiter<n,W,F>::invalidate_() { - i_ = size_(); + i_ = -1; } template <unsigned n, typename W, typename F> @@ -358,10 +358,10 @@ template <unsigned n, typename W, typename F> inline - unsigned + int multiple_size_qiter<n,W,F>::size_() const { - return this->s_->size_around(*this->c_); + return int(this->s_->size_around(*this->c_)); } # endif // ! MLN_INCLUDE_ONLY Index: mln/morpho/skeleton_constrained.hh --- mln/morpho/skeleton_constrained.hh (revision 2893) +++ mln/morpho/skeleton_constrained.hh (working copy) @@ -1,4 +1,4 @@ -// Copyright (C) 2007, 2008 EPITA Research and Development Laboratory +// Copyright (C) 2008 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,137 +25,97 @@ // reasons why the executable file might be covered by the GNU General // Public License. -#ifndef MLN_SKELETON_HH -# define MLN_SKELETON_HH +#ifndef MLN_MORPHO_SKELETON_CONSTRAINED_HH +# define MLN_MORPHO_SKELETON_CONSTRAINED_HH -# include <iomanip> -# include <iostream> -# include <sstream> - -# include <mln/core/var.hh> - -# include <mln/core/image/image2d.hh> -# include <mln/core/image/cast_image.hh> -# include <mln/core/alias/neighb2d.hh> +/// \file mln/morpho/skeleton_constrained.hh +/// +/// Compute a skeleton under constraints. + +# include <mln/core/concept/image.hh> +# include <mln/core/concept/neighborhood.hh> # include <mln/core/site_set/p_queue_fast.hh> # include <mln/core/site_set/p_priority.hh> - -# include <mln/value/int_u8.hh> -# include <mln/arith/revert.hh> -# include <mln/transform/distance.hh> - -# include <mln/make/w_window2d_int.hh> - +# include <mln/extension/adjust_fill.hh> # include <mln/level/fill.hh> -# include <mln/debug/println.hh> - -# include <mln/logical/not.hh> - -# include "simple_point.hh" - -#include <mln/make/w_window2d_int.hh> - -# include <mln/io/pgm/save.hh> -# include <mln/io/pbm/save.hh> namespace mln { - template <typename V> - void save_state(const image2d<V>& ima) + namespace morpho { - static int id = 0; - std::stringstream filename; - std::cout << id << std::endl; - filename << "skel_trace_" << std::setw(5) << std::setfill('0') - << std::right << id++ << ".ppm"; + template <typename I, + typename N, typename F, + typename K, typename R> + mln_ch_value(I, bool) + skeleton_constrained(const Image<I>& input, + const Neighborhood<N>& nbh, const F& is_simple, + const Image<K>& constraint, const Image<R>& priority); - io::pbm::save(ima, filename.str()); - } - image2d<bool> crest(const image2d<bool>& input, - const image2d<value::int_u8>& dist_map, - const neighb2d& nbh) - { - image2d<bool> is_crest; - initialize(is_crest, input); - level::fill(is_crest, false); - mln_piter_(image2d<bool>) p(input.domain()); - mln_niter_(neighb2d) n(nbh, p); - for_all(p) - { - if (!input(p) || dist_map(p) < 0) - continue; +# ifndef MLN_INCLUDE_ONLY - unsigned nb_eq = 0; - unsigned nb_gt = 0; - for_all(n) - if (input.domain().has(n)) + template <typename I, + typename N, typename F, + typename K, typename R> + inline + mln_ch_value(I, bool) + skeleton_constrained(const Image<I>& input_, + const Neighborhood<N>& nbh_, const F& is_simple, + const Image<K>& constraint_, const Image<R>& priority_) { - if (dist_map(n) == dist_map(p)) - nb_eq++; - else if (dist_map(n) > dist_map(p)) - nb_gt++; - } - - if ((nb_gt == 1 && nb_eq == 0) || - (nb_gt == 0)) - is_crest(p) = true; - } - return is_crest; - } + const I& input = exact(input_); + const N& nbh = exact(nbh_); + const K& constraint = exact(constraint_); + const R& priority = exact(priority_); + extension::adjust_fill(input, nbh, false); - image2d<bool> - skeleton_with_constraint(const image2d<bool>& input, - unsigned nbh_i, - const image2d<bool>& K, - const image2d<value::int_u8>& priority) - { - mln_assertion(nbh_i == 4 || nbh_i == 8); + // FIXME: Tests! - neighb2d nbh = int_to_neighb(nbh_i); - image2d<bool> output; - initialize(output, input); + typedef mln_psite(I) P; + typedef p_queue_fast<P> Q; + p_priority<mln_value(R), Q> q; - typedef mln_site_(image2d<bool>) P; - p_priority<value::int_u8, p_queue_fast<P> > q; + mln_ch_value(I, bool) output; // Initialization. { - p_priority<value::int_u8, p_queue_fast<P> > q_tmp; - + initialize(output, input); level::fill(output, input); - mln_piter_(image2d<bool>) p(input.domain()); + + mln_piter(I) p(input.domain()); for_all(p) - if (!input(p) && - is_simple_point2d(input, nbh_i, p)) // p is a simple point of background + if ( input(p) == false ) +// if ( input(p) == false && +// is_simple(input, nbh, p) ) // p is a simple point of the background. + { q.push(priority(p), p); + // std::cout << "push " << p << std::endl; + } } + // std::cout << std::endl << "propagation..." << std::endl; + // Propagation. { P p; - mln_niter_(neighb2d) n(nbh, p); + mln_niter(N) n(nbh, p); while (! q.is_empty()) { p = q.pop_front(); - for_all(n) if (output.domain().has(n) && - output(n) && - K(n) == false && - is_simple_point2d(output, nbh_i, n) - // && // n is simple - // !is_curve_extremum(output, nbh_i, n, 1) - ) + output(n) == true && + constraint(n) == false && + is_simple(output, nbh, n) ) { - output(n) = false; // Remove n from object - // save_state(output); + output(n) = false; // Remove n from object. q.push(priority(n), n); + // std::cout << "push " << n << std::endl; } } } @@ -163,32 +123,11 @@ return output; } +# endif // ! MLN_INCLUDE_ONLY - image2d<bool> - skeleton(const image2d<bool>& input, unsigned nbh_i) - { - mln_assertion(nbh_i == 4 || nbh_i == 8); - neighb2d nbh = int_to_neighb(nbh_i); - - int vals[] = { 0, 9, 0, 9, 0, - 9, 6, 4, 6, 9, - 0, 4, 0, 4, 0, // Values of distances. - 9, 6, 4, 6, 9, - 0, 9, 0, 9, 0 }; - - image2d<value::int_u8> dist_map_n = transform::distance(value::int_u8(), logical::not_(input), nbh, make::w_window2d_int(vals)); - image2d<value::int_u8> dist_map = arith::revert(dist_map_n); - - io::pgm::save(dist_map, "distance.pgm"); - io::pgm::save(dist_map_n, "distance_n.pgm"); - - // Make K - image2d<bool> K = crest(input, dist_map_n, nbh); - io::pbm::save(K, "K.pbm"); - - return skeleton_with_constraint(input, nbh_i, K, dist_map); - } + } // end of namespace mln::morpho } // end of namespace mln -#endif + +#endif // ! MLN_MORPHO_SKELETON_CONSTRAINED_HH Index: img/small.pbm Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: img/small.pbm ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: img/fly.pbm --- img/fly.pbm (revision 0) +++ img/fly.pbm (revision 0) @@ -0,0 +1,5 @@ +P4 +# CREATOR: XV version 3.10a-jumboFix+Enh of 20050501 + +5 6 +����X� \ No newline at end of file
participants (1)
-
Thierry Geraud