
URL: https://svn.lrde.epita.fr/svn/oln/trunk/milena ChangeLog: 2009-03-24 Edwin Carlinet <carlinet@lrde.epita.fr> Test geodesic distance with component tree and try to apply. * mln/morpho/attribute/sharpness.hh: Fix bugs related to height computation. * sandbox/edwin/attributes/bbox.hh: Add bbox morpho attribute. * sandbox/edwin/tree/run.hh: Correct bugs and delete run_while_treshold which can be computed throught pointwise. * sandbox/edwin/tree/test.cc: Test file. --- mln/morpho/attribute/sharpness.hh | 5 sandbox/edwin/attributes/bbox.hh | 203 ++++++++++++++++++++++++++++++++++++++ sandbox/edwin/tree/Makefile | 10 - sandbox/edwin/tree/configure | 14 ++ sandbox/edwin/tree/run.hh | 67 ------------ sandbox/edwin/tree/test.cc | 143 ++++++++++++++++++++++++-- 6 files changed, 362 insertions(+), 80 deletions(-) Index: trunk/milena/mln/morpho/attribute/sharpness.hh =================================================================== --- trunk/milena/mln/morpho/attribute/sharpness.hh (revision 3571) +++ trunk/milena/mln/morpho/attribute/sharpness.hh (revision 3572) @@ -177,7 +177,10 @@ double sharpness<I>::to_result() const { - return (double)volume_.to_result() / (double)(volume_.area() * height_.to_result()); + double d = (double) volume_.to_result() / + (double)(volume_.area() * (height_.to_result() + 1)); + mln_postcondition(d >= 0 && d <= 1); + return d; } template <typename I> Index: trunk/milena/sandbox/edwin/tree/configure =================================================================== --- trunk/milena/sandbox/edwin/tree/configure (revision 0) +++ trunk/milena/sandbox/edwin/tree/configure (revision 3572) @@ -0,0 +1,14 @@ +#! /bin/bash + + +for arg in "$@"; do + if [ "$arg" == "--debug" ]; then + debug=1; + fi +done + +rm -f makefile.rules +touch makefile.rules +if [ "$debug" ]; then + echo 'DEBUG=1' > makefile.rules +fi \ No newline at end of file Property changes on: trunk/milena/sandbox/edwin/tree/configure ___________________________________________________________________ Name: svn:executable + * Index: trunk/milena/sandbox/edwin/tree/run.hh =================================================================== --- trunk/milena/sandbox/edwin/tree/run.hh (revision 3571) +++ trunk/milena/sandbox/edwin/tree/run.hh (revision 3572) @@ -91,7 +91,7 @@ ** ** @param tree Component tree used for propagation. ** @param a Attributed image where values are propagated. - ** @param accu_ Accumulator to apply on tree. + ** @param acc Accumulator to apply on tree. ** @param n The repetition number. ** ** @return Array of propagated nodes. @@ -104,28 +104,6 @@ Accumulator<ACC>& acc, unsigned n); - /** - ** Apply accumulator \accu on tree nodes value until the value - ** accumulated by \p acc get lesser than the treshold \p n. - ** Each time, the result of accumulator is inserted - ** into the returned array, then ascendant and descendant zero-fill - ** propagations are performed from the node. - ** (This function is a shorcut of run_while with a treshold predicate). - ** - ** @param tree Component tree used for propagation. - ** @param a Attributed image where values are propagated. - ** @param accu_ Accumulator to apply on tree. - ** @param n Treshold. - ** - ** @return Array of propagated nodes. - */ - template <typename T, typename A, typename ACC> - inline - p_array< mln_psite(A) > - run_while_treshold(const T& tree, - Image<A>& a, - Accumulator<ACC>& acc, - mln_value(A) n); # ifndef MLN_INCLUDE_ONLY @@ -143,16 +121,16 @@ p_array< mln_psite(A) > arr_sites; util::array< mln_value(A) > arr_values; - do { p = morpho::tree::run(tree, a, accu); - if (a(p) == 0) //there's no more objects. - break; + while (pred(p) && a(p) != 0) + { arr_sites.insert(p); arr_values.append(a(p)); morpho::tree::propagate_node_to_descendants(p, tree, a, 0); morpho::tree::propagate_node_to_ancestors(p, tree, a, 0); a(p) = 0; - } while (pred(accu.to_result())); + p = morpho::tree::run(tree, a, accu); + } for (unsigned i = 0; i < arr_sites.nsites(); i++) a(arr_sites[i]) = arr_values[i]; return arr_sites; @@ -178,28 +156,6 @@ unsigned n_; }; - template <typename I> - struct treshold : Function_p2b< treshold<I> > - { - typedef bool result; - - treshold(const Image<I>& ima, - const mln_value(I)& treshold) - : ima_ (exact(ima)), - treshold_ (treshold) - { - } - - bool operator()(const mln_psite(I)& p) const - { - return (ima_(p) > treshold_); - } - - private: - const I& ima_; - const mln_value(I) treshold_; - }; - } // end of namespace mln::morpho::tree::internal @@ -231,19 +187,6 @@ return run_while(tree, a, acc, predicate); } - - template <typename T, typename A, typename ACC> - inline - p_array< mln_psite(A) > - run_while_treshold(const T& tree, - Image<A>& a, - Accumulator<ACC>& acc, - mln_value(A) n) - { - internal::treshold<A> predicate(a, n); - return run_while(tree, a, acc, predicate); - } - template <typename T, typename A, typename I> mln_result(A) run(const T& tree, Index: trunk/milena/sandbox/edwin/tree/test.cc =================================================================== --- trunk/milena/sandbox/edwin/tree/test.cc (revision 3571) +++ trunk/milena/sandbox/edwin/tree/test.cc (revision 3572) @@ -1,6 +1,8 @@ /* mln core */ #include <mln/core/image/image2d.hh> +#include <mln/core/image/image_if.hh> #include <mln/core/alias/neighb2d.hh> +#include <mln/core/routine/duplicate.hh> #include <mln/core/var.hh> /* Site set */ @@ -15,15 +17,34 @@ #include "accumulator/arg_max.hh" /* Attributes */ -#include <mln/morpho/attribute/sharpness.hh> +#include <mln/transform/distance_geodesic.hh> +#include <mln/morpho/attribute/card.hh> +#include "../attributes/bbox.hh" /* io */ -#include <mln/io/pgm/load.hh> +#include <mln/io/pbm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/ppm/save.hh> #include <../../theo/color/change_attributes.hh> -#include <iostream> + +/* data & pw */ +#include <mln/core/concept/function.hh> +#include <mln/fun/p2v/ternary.hh> +#include <mln/data/fill.hh> +#include <mln/data/paste.hh> +#include <mln/pw/all.hh> + +/* labeling */ +#include <mln/value/label.hh> +#include <mln/labeling/blobs.hh> +#include <mln/debug/colorize.hh> + +/* Draw debug */ +#include <mln/draw/box.hh> /* std */ #include <string> +#include <iostream> bool mydebug = false; @@ -42,6 +63,35 @@ << "*********************" << std::endl; } +template <typename P2V> +struct ratio_ : public mln::Function_p2v< ratio_<P2V> > +{ + typedef double result; + + ratio_(const P2V& f) : + f_ (f) + { + } + + template <typename P> + double operator() (const P& p) const + { + return (double) (f_(p).len(1)) / (double)(f_(p).len(0)); + } + +protected: + const P2V& f_; +}; + +template <typename P2V> +ratio_<P2V> ratio(const mln::Function_p2v<P2V>& f) +{ + return ratio_<P2V>(exact(f)); +} + + + + int main(int argc, char* argv[]) { using namespace mln; @@ -56,8 +106,16 @@ /* Image loadin' */ typedef image2d<int_u8> I; - I input; - io::pgm::load(input, argv[1]); + image2d<bool> input_; + io::pbm::load(input_, argv[1]); + + /* Work on geodesic distance image */ + I input = transform::distance_geodesic(input_, c8(), mln_max(int_u8)); + + if (mydebug) + dsp("Distance geodesic"); + + io::pgm::save(input, "distance.pgm"); /* Component tree creation */ typedef p_array< mln_site_(I) > S; @@ -67,20 +125,44 @@ tree_t tree(input, sorted_sites, c4()); /* Compute Attribute On Image */ - typedef morpho::attribute::sharpness<I> accu_t; - typedef mln_ch_value_(tree_t::function, mln_result_(accu_t)) A; + typedef morpho::attribute::bbox<I> bbox_t; + typedef mln_ch_value_(I, double) A; - A a = morpho::tree::compute_attribute_image(accu_t (), tree); + mln_VAR(attr_image, morpho::tree::compute_attribute_image(bbox_t (), tree)); + A a = duplicate(ratio(pw::value(attr_image)) | attr_image.domain()); morpho::tree::propagate_representant(tree, a); if (mydebug) { - dsp("Image attribute"); display_tree_attributes(tree, a); + dsp("Image sharp attribute"); display_tree_attributes(tree, a); } - /* Run max accumulator, looking for 5 objects */ + /* We don't want little components */ + + // So we compute card attribute and we filter big components + // FIXME: some attributes are compositions of attributes, here + // sharpness can give area so, it would be fine if we could give an + // optional extra argument to compute_attribute where the + // accumulators image will be stored. + +// typedef morpho::attribute::card<I> card_t; +// typedef mln_ch_value_(tree_t::function, mln_result_(card_t)) B; + +// B b = morpho::tree::compute_attribute_image(card_t (), tree); +// morpho::tree::propagate_representant(tree, b); + +// if (mydebug) { +// dsp("Image card attribute"); display_tree_attributes(tree, b); +// } + +// a = duplicate((fun::p2v::ternary(pw::value(b) > pw::cst(2), pw::value(a), pw::cst(0.0))) | a.domain()); + + + /* Run max accumulator */ accumulator::arg_max<A> argmax(a); p_array< mln_psite_(A) > obj_array; // Array of object components. - obj_array = morpho::tree::run_ntimes(tree, a, argmax, 5); + + mln_VAR(predicate, pw::value(a) > pw::cst(0.5)); + obj_array = morpho::tree::run_while(tree, a, argmax, predicate); if (mydebug) { dsp("Run max accumulator, lk 4 5 objs"); display_tree_attributes(tree, a); @@ -94,5 +176,44 @@ std::cout << c; } + /* Now Back Propagate to component */ + typedef mln_ch_value_(I, bool) M; + M mask; + initialize(mask, a); + data::fill(mask, false); + + mln_fwd_piter_(p_array< mln_psite_(I) >) c(obj_array); + for_all(c) + { + mask(c) = true; + propagate_node_to_descendants(c, tree, mask); + } + morpho::tree::propagate_representant(tree, mask); + + // mask now contains all nodes related to objects + + if (mydebug) { + dsp("Create mask and propagate"); display_tree_attributes(tree, mask); + } + + /* Labeling */ + typedef mln_ch_value_(I, value::label<8>) L; + value::label<8> nlabel; + L label = labeling::blobs(mask, c4(), nlabel); + io::ppm::save(debug::colorize(value::rgb8(), label, nlabel), "label.pgm"); + + /* Now store output image image */ + I out; + initialize(out, input); + data::fill(out, 0); + data::paste(input | pw::value(mask), out); + + if (mydebug) { + mln_fwd_piter_(p_array< mln_psite_(I) >) c(obj_array); + for_all(c) + draw::box(out, attr_image(c), mln_max(int_u8)); + dsp("Mask input"); display_tree_attributes(tree, out); + } + io::pgm::save(out, "output.pgm"); } Index: trunk/milena/sandbox/edwin/tree/Makefile =================================================================== --- trunk/milena/sandbox/edwin/tree/Makefile (revision 3571) +++ trunk/milena/sandbox/edwin/tree/Makefile (revision 3572) @@ -1,3 +1,4 @@ +include makefile.rules TARGET=test SRC=test.cc OBJS=${SRC:.cc=.o} @@ -7,16 +8,15 @@ CXXFLAGS=-I$(MILENADIR) -I./ -W -Wall -CXXFLAGS += -g -ggdb -#CXXFLAGS += -DNDEBUG -O1 + +CXXFLAGS += $(if $(DEBUG), -g -ggdb, -DNDEBUG -O1) CXX=g++ LD=g++ LDFLAGS= all: clean $(TARGET) - #chmod +x exo2.sh - #./exo2.sh + $(TARGET): $(OBJS) $(SRC) $(LD) $(LDFLAGS) -o $@ $(OBJS) @@ -29,5 +29,5 @@ clean: rm -f *.o $(TARGET) - rm -f *.pbm - find -name "*.pgm" \! -regex ".*/affiche2?.pgm" -delete \ No newline at end of file + #rm -f *.pbm + #find -name "*.pgm" \! -regex ".*/affiche2?.pgm" -delete Index: trunk/milena/sandbox/edwin/attributes/bbox.hh =================================================================== --- trunk/milena/sandbox/edwin/attributes/bbox.hh (revision 0) +++ trunk/milena/sandbox/edwin/attributes/bbox.hh (revision 3572) @@ -0,0 +1,203 @@ +// 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 +// 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. + +#ifndef MLN_MORPHO_ATTRIBUTE_BBOX_HH_ +# define MLN_MORPHO_ATTRIBUTE_BBOX_HH_ + +/// \file mln/morpho/attribute/bbox.hh +/// +/// Define an accumulator that computes bounding box of a +/// component. + +# include <mln/core/concept/box.hh> + + +namespace mln +{ + + // Forward declaration. + + namespace morpho { + namespace attribute { + template <typename I> class bbox; + } + } + + // Traits. + + namespace trait + { + + template <typename I> + struct accumulator_< morpho::attribute::bbox<I> > + { + typedef accumulator::has_untake::no has_untake; + typedef accumulator::has_set_value::no has_set_value; + typedef accumulator::has_stop::no has_stop; + typedef accumulator::when_pix::use_p when_pix; + }; + + } // end of namespace mln::trait + + namespace morpho { + namespace attribute { + + template <typename I> + struct bbox : public mln::accu::internal::base<const box<mln_psite(I)>&, bbox<I> > + { + typedef mln::accu::internal::base<const box<mln_psite(I)>&, bbox<I> > super_; + + public: + typedef mln_psite(I) P; + typedef mln_psite(I) argument; + + bbox(); + + /// Manipulators. + /// \{ + void init(); + void take(const argument& p); + void take(const bbox<I>& other); + void take_as_init(const argument& p); + + /// \} + + /// Get the value of the accumulator. + const box<P>& to_result() const; + + + /// Check whether this accu is able to return a result. + /// Always true here. + bool is_valid() const; + + protected: + + bool is_valid_; + box<mln_psite(I)> b_; + }; + +# ifndef MLN_INCLUDE_ONLY + + template <typename I> + inline + bbox<I>::bbox() + { + init(); + } + + template <typename I> + inline + void + bbox<I>::init() + { + is_valid_ = false; + } + + template <typename I> + inline + void + bbox<I>::take_as_init(const mln_psite(I)& p) + { + b_.pmin() = p; + b_.pmax() = p; + is_valid_ = true; + } + + template <typename I> + inline + void + bbox<I>::take(const mln_psite(I)& p) + { + if (!is_valid_) + { + b_.pmin() = p; + b_.pmax() = p; + is_valid_ = true; + return; + } + for (unsigned i = 0; i < mln_psite_(I)::dim; ++i) + if (p[i] < b_.pmin()[i]) + b_.pmin()[i] = p[i]; + else if (p[i] > b_.pmax()[i]) + b_.pmax()[i] = p[i]; + } + + template <typename I> + inline + void + bbox<I>::take(const bbox<I>& other) + { + if (! other.is_valid_) + { + // no-op + return; + } + if (! this->is_valid_) + { + // 'other' makes '*this' valid + *this = other; + is_valid_ = true; + return; + } + // both are valids so: + const box<mln_psite(I)>& o_b = other.b_; + for (unsigned i = 0; i < mln_psite_(I)::dim; ++i) + { + if (o_b.pmin()[i] < b_.pmin()[i]) + b_.pmin()[i] = o_b.pmin()[i]; + if (o_b.pmax()[i] > b_.pmax()[i]) + b_.pmax()[i] = o_b.pmax()[i]; + } + } + + template <typename I> + inline + const box<mln_psite(I)>& + bbox<I>::to_result() const + { + mln_precondition(is_valid_); + return b_; + } + + template <typename I> + inline + bool + bbox<I>::is_valid() const + { + return is_valid_; + } + + +# endif /* !MLN_INCLUDE_ONLY */ + } // end of namespace mln::morpho::attribute + } // end of namespace mln::morpho +} // end of namespace mln + + + +#endif /* !MLN_MORPHO_ATTRIBUTE_BBOX_HH_ */