
https://svn.lrde.epita.fr/svn/oln/branches/cleanup-2008/milena Index: ChangeLog from Thierry Geraud <thierry.geraud@lrde.epita.fr> Revamp linear convolution. * mln/linear/convolve.hh: Re-vamp. * tests/linear/convolve.cc: Update. * mln/core/routine/primary.hh: New; not completed yet. * mln/core/routine/all.hh: Update. * mln/core/concept/window.hh (todo): New. * mln/morpho/erosion.hh (tests): New. mln/core/concept/window.hh | 3 mln/core/routine/all.hh | 7 - mln/core/routine/primary.hh | 100 ++++++++++++++++++++++ mln/linear/convolve.hh | 198 +++++++++++++++++++++++++------------------- mln/morpho/erosion.hh | 21 ++++ tests/linear/convolve.cc | 8 - 6 files changed, 245 insertions(+), 92 deletions(-) Index: tests/linear/convolve.cc --- tests/linear/convolve.cc (revision 2743) +++ tests/linear/convolve.cc (working copy) @@ -54,7 +54,6 @@ image2d<int_u8> lena; io::pgm::load(lena, MLN_IMG_DIR "/lena.pgm"); - image2d<int_u8> out(lena.domain()); float ws[] = { .04, .04, .04, .04, .04, .04, .04, .04, .04, .04, @@ -63,9 +62,10 @@ .04, .04, .04, .04, .04 }; w_window2d_float w = make::w_window2d(ws); - image2d<float> tmp(lena.domain()); - linear::convolve(lena, w, tmp); - level::transform(tmp, math::round<int_u8>(), out); +// image2d<float> tmp = linear::convolve(lena, w); +// image2d<int_u8> out = level::transform(tmp, math::round<int_u8>()); + + image2d<int_u8> out = linear::convolve(lena, w); io::pgm::save(out, "out.pgm"); } Index: mln/core/routine/primary.hh --- mln/core/routine/primary.hh (revision 0) +++ mln/core/routine/primary.hh (revision 0) @@ -0,0 +1,100 @@ +// 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. + +#ifndef MLN_CORE_ROUTINE_PRIMARY_HH +# define MLN_CORE_ROUTINE_PRIMARY_HH + +/*! \file mln/core/routine/primary.hh + * + * \brief FIXME + * + * \todo We also need to get the extension image to handle border + * routines. + */ + +# include <mln/core/concept/image.hh> + + +namespace mln +{ + + /// FIXME: Doc! + template <typename I> + void primary(const Image<I>& ima); + + +# ifndef MLN_INCLUDE_ONLY + + + namespace internal + { + + // Primary_type. + + template <typename I> struct primary_type; + + template <typename I, typename C> + struct primary_type_helper + { + typedef typename primary_type<mln_delegatee(I)>::ret ret; + }; + + template <typename I> + struct primary_type_helper< I, mln::trait::image::category::primary > + { + typedef I ret; + }; + + template <typename I> + struct primary_type + { + typedef mln_trait_image_category(I) Cat; + typedef typename primary_type_helper<I, Cat>::ret ret; + }; + + + // Routine. + + } // end of namespace mln::internal + + + + // Facade. + + template <typename I> + inline + void primary(const Image<I>&) + { + } + + +# endif // ! MLN_INCLUDE_ONLY + +} // end of namespace mln + + +#endif // ! MLN_CORE_ROUTINE_PRIMARY_HH Index: mln/core/routine/all.hh --- mln/core/routine/all.hh (revision 2743) +++ mln/core/routine/all.hh (working copy) @@ -36,10 +36,11 @@ */ -// # include <mln/core/routine/clone.hh> -// # include <mln/core/routine/exact.hh> +# include <mln/core/routine/clone.hh> +# include <mln/core/routine/exact.hh> # include <mln/core/routine/extend.hh> -// # include <mln/core/routine/initialize.hh> +# include <mln/core/routine/initialize.hh> +# include <mln/core/routine/primary.hh> #endif // ! MLN_CORE_ROUTINE_ALL_HH Index: mln/core/concept/window.hh --- mln/core/concept/window.hh (revision 2743) +++ mln/core/concept/window.hh (working copy) @@ -34,6 +34,9 @@ * \todo Operator== should test if the cmp is possible. * * \todo Add an is_valid() method. + * + * \todo The is_centered() method could also exist when the window is + * not regular... */ # include <mln/core/concept/object.hh> Index: mln/linear/convolve.hh --- mln/linear/convolve.hh (revision 2743) +++ mln/linear/convolve.hh (working copy) @@ -31,6 +31,8 @@ /*! \file mln/linear/convolve.hh * * \brief Convolution. + * + * \todo Introduce an accumulator. */ # include <mln/core/concept/image.hh> @@ -56,107 +58,90 @@ * * \pre output.domain = input.domain */ - template <typename I, typename W, typename O> - void convolve(const Image<I>& input, const Weighted_Window<W>& w_win, - Image<O>& output); + template <typename I, typename W> + mln_concrete(I) + convolve(const Image<I>& input, const Weighted_Window<W>& w_win); + # ifndef MLN_INCLUDE_ONLY - namespace impl + // Tests. + + namespace internal + { + + template <typename I, typename W> + void + convolve_tests(const Image<I>& input, + const Weighted_Window<W>& w_win) { - /* FIXME: We must clean up the interface of - mln::linear::impl::convolve_: + mln_precondition(exact(input).has_data()); + // mln_precondition(exact(w_win).is_valid()); + } - - either allow certain patterns of speed traits (e.g., - any/any, fastest/fastest, fastest/any, etc.). In this - case, the generic version should abort at compile time; + } // end of namespace mln::linear::internal - - or accept all combinations (which is the current case), and - default to the slowest one (presumably any/any). - */ - // Fwd decl. - template <typename I, typename W, typename O> - inline - void convolve_(const I& input, - const Weighted_Window<W>& w_win_, - O& output); - - /// Default version, delegating to the generic version. - template <typename Speed_I, typename I, typename W, - typename Speed_O, typename O> - inline - void convolve_(Speed_I, const I& input, - const Weighted_Window<W>& w_win_, - Speed_O, O& output) - { - /* Don't delegate using such a call: - - \code - impl::convolve_(trait::image::speed::any(), input, - w_win_, - trait::image::speed::any(), output); - \endcode - - since it would end up with infinite recursion. The reason - is that the compiler would select this function (in which - you read this very comment), instead of the next one (with - input and output speed traits set to `any'), to resolve the - call. This is because C++ overloading rules favor the - generic function over the more specialized one. And we - cannot use explicit partial specialization, since it just - doesn't exist for functions. - - Hence the chosen solution: create and call another - overloading for mln::linear::impl::convolve_, with no - unnatural selection behavior. */ - impl::convolve_(input, w_win_, output); - } - - template <typename I, typename W, typename O> - inline - void convolve_(trait::image::speed::any, const I& input, - const Weighted_Window<W>& w_win_, - trait::image::speed::any, O& output) - { - // Delegate the call to the generic version. - impl::convolve_(input, w_win_, output); - } - - /// A factored implementation of the most generic version of - /// mln::linear::impl::convolve_. - template <typename I, typename W, typename O> - inline - void convolve_(const I& input, - const Weighted_Window<W>& w_win_, - O& output) + // Implementation. + + namespace impl + { + + namespace generic { + + template <typename I, typename W> + mln_concrete(I) + convolve(const Image<I>& input_, + const Weighted_Window<W>& w_win_) + { + trace::entering("linear::impl::generic::convolve"); + + const I& input = exact(input_); const W& w_win = exact(w_win_); + internal::convolve_tests(input, w_win); + + // extension::adjust_duplicate(input, w_win); + + typedef mln_concrete(I) O; + O output; + initialize(output, input); mln_piter(I) p(input.domain()); mln_qiter(W) q(w_win, p); for_all(p) { - mln_value(O) v = 0; + mln_value(O) v = literal::zero; for_all(q) if (input.has(q)) v += input(q) * q.w(); output(p) = v; } + + trace::exiting("linear::impl::generic::convolve"); + return output; } - template <typename I, typename W, typename O> - inline - void convolve_(trait::image::speed::fastest, const I& input, - const Weighted_Window<W>& w_win_, - trait::image::speed::fastest, O& output) + } // end of namespace mln::linear::impl::generic + + + template <typename I, typename W> + mln_concrete(I) + convolve_fastest(const Image<I>& input_, + const Weighted_Window<W>& w_win_) { + trace::entering("linear::impl::convolve_fastest"); + + const I& input = exact(input_); const W& w_win = exact(w_win_); + internal::convolve_tests(input, w_win); - border::resize(input, w_win.delta()); - border::duplicate(input); + // extension::adjust_duplicate(input, w_win); + typedef mln_concrete(I) O; + O output; + initialize(output, input); mln_pixter(O) p_out(output); mln_pixter(const I) p(input); @@ -164,28 +149,71 @@ for_all_2(p, p_out) { - mln_value(O) v = 0; + mln_value(O) v = literal::zero; unsigned i = 0; for_all(q) v += w_win.w(i++) * q.val(); p_out.val() = v; } + + trace::exiting("linear::impl::convolve_fastest"); + return output; } } // end of namespace mln::linear::impl + // Dispatch. + + namespace internal + { + + template <typename I, typename W> + mln_concrete(I) + convolve_dispatch(trait::image::speed::any, + const Image<I>& input, + const Weighted_Window<W>& w_win) + { + return impl::generic::convolve(input, w_win); + } + + template <typename I, typename W> + mln_concrete(I) + convolve_dispatch(trait::image::speed::fastest, + const Image<I>& input, + const Weighted_Window<W>& w_win) + { + return impl::convolve_fastest(input, w_win); + } + + template <typename I, typename W> + mln_concrete(I) + convolve_dispatch(const Image<I>& input, + const Weighted_Window<W>& w_win) + { + return convolve_dispatch(mln_trait_image_speed(I)(), + input, w_win); + } + + } // end of namespace mln::linear::internal + + // Facade. - template <typename I, typename W, typename O> - inline - void convolve(const Image<I>& input, const Weighted_Window<W>& w_win, - Image<O>& output) - { - mln_precondition(exact(output).domain() == exact(input).domain()); - impl::convolve_(mln_trait_image_speed(I)(), exact(input), - exact(w_win), - mln_trait_image_speed(O)(), exact(output)); + template <typename I, typename W> + mln_concrete(I) + convolve(const Image<I>& input, const Weighted_Window<W>& w_win) + { + trace::entering("linear::convolve"); + + internal::convolve_tests(input, w_win); + + mln_concrete(I) output; + output = internal::convolve_dispatch(mln_trait_image_speed(I)(), + input, w_win); + + trace::exiting("linear::convolve"); + return output; } # endif // ! MLN_INCLUDE_ONLY Index: mln/morpho/erosion.hh --- mln/morpho/erosion.hh (revision 2743) +++ mln/morpho/erosion.hh (working copy) @@ -52,6 +52,25 @@ # ifndef MLN_INCLUDE_ONLY + + namespace internal + { + + template <typename I, typename W> + inline + void + erosion_tests(const Image<I>& input_, const Window<W>& win_) + { + const I& input = exact(input_); + const W& win = exact(win_); + + mln_precondition(exact(input).has_data()); + mln_precondition(! exact(win).is_empty()); + } + + } // end of mln::morpho::internal + + namespace impl { @@ -69,6 +88,7 @@ const I& input = exact(input_); const W& win = exact(win_); + internal::erosion_tests(input, win); extension::adjust_fill(input, win, mln_max(mln_value(I))); @@ -139,6 +159,7 @@ mln_precondition(exact(input).has_data()); mln_precondition(! exact(win).is_empty()); + internal::erosion_tests(input, win); mln_concrete(I) output = internal::erosion_dispatch(input, win); if (exact(win).is_centered())