1649: Fix the computations of the Sobel gradients and norm(s).

https://svn.lrde.epita.fr/svn/oln/trunk/milena Index: ChangeLog from Roland Levillain <roland@lrde.epita.fr> Fix the computations of the Sobel gradients and norm(s). * mln/linear/sobel.hh (mln_gradient_component, mln_gradient): New macros. (mlc_internal_ensure_is_scalar): New local (workaround) macro. (mln::linear::sobel_v, mln::linear::sobel_h, mln::linear::sobel): Revamp these functions. Catch up with the current calling conventions (output passed as returned value). (mln::linear::impl_::sobel_v_, mln::linear::impl_::sobel_h_) (mln::linear::impl_::sobel_): Likewise. (mln::linear::sobel_norm, mln::linear::impl::sobel_norm_): New functions. * tests/linear_sobel._cc: Rename as... * tests/linear/sobel.cc: ...this. Use MLN_IMG_DIR from tests/data.hh to get the path to the img/ directory. Revamp this test. * tests/linear/Makefile.am (check_PROGRAMS, sobel_SOURCES, TESTS): New. mln/linear/sobel.hh | 214 +++++++++++++++++++++++++++++++++++------------ tests/linear/Makefile.am | 7 + tests/linear/sobel.cc | 32 +++++-- 3 files changed, 193 insertions(+), 60 deletions(-) Index: mln/linear/sobel.hh --- mln/linear/sobel.hh (revision 1648) +++ mln/linear/sobel.hh (working copy) @@ -31,12 +31,36 @@ /*! \file mln/linear/sobel.hh * * \brief Sobel filter. + * + * \todo Extends to the case of other dimensions (only the 2-d case + * is handled here). */ +# include <mln/trait/ch_value.hh> +# include <mln/trait/value/nature.hh> + +# include <mln/metal/int.hh> + # include <mln/arith/plus.hh> # include <mln/level/abs.hh> +# include <mln/fun/v2v/norm.hh> +# include <mln/level/transform.hh> # include <mln/linear/line_x2_convolve.hh> +# include <mln/value/stack.hh> + +// FIXME: We might want to move these macros to another file, like +// mln/core/gradient.hh. + + +/// \brief Get the gradient component image type associated to image \a I. +#define mln_gradient_component(I) \ + /* FIXME: Use mln_sum_x (or mln_sum_product) instead of mln_sum. */ \ + mln_ch_value(I, mln_sum(mln_value(I))) + +/// \brief Get the gradient image type associated to image \a I. +#define mln_gradient(I) \ + mln::value::stack_image<I::point::dim, mln_gradient_component(I)> namespace mln @@ -45,68 +69,132 @@ namespace linear { - /*! Convolution of an image \p input by the weighted window \p w_win. - * - * \warning Computation of \p output(p) is performed with the - * value type of \p output. - * - * \warning The weighted window is used as-is, considering that - * its symmetrization is handled by the client. - * - * \pre output.domain = input.domain - * - * \todo Only for 2D so check + generalize. - * - * \todo Suboptimal since it is not point-wise and it costs temp - * images. - */ - template <typename I, typename O> - void sobel_v(const Image<I>& input, Image<O>& output); - - template <typename I, typename O> - void sobel_h(const Image<I>& input, Image<O>& output); - - template <typename I, typename O> - void sobel(const Image<I>& input, Image<O>& output); + /// Sobel gradient components. + /// \{ + /// Compute the vertical component of the Sobel gradient. + template <typename I> + mln_gradient_component(I) sobel_h(const Image<I>& input); + + /// Compute the horizontal component of the Sobel gradient. + template <typename I> + mln_gradient_component(I) sobel_v(const Image<I>& input); + /// \} + + /// Compute the Sobel gradient of an image. + /// + /// \param output A vectorial output image. + /// \param + template <typename I> + mln_gradient(I) sobel(const Image<I>& input); + + /// Compute the norm of the Sobel gradient of an image. + /// + /// \param input a vectorial input image + /// \param output the norm-image of the Sobel gradient of the input image + /// \param norm a norm functor (\see mln/fun/v2v/norm.hh), + /// defaulting to the L1-norm (mln::fun::v2v::l1_norm). + /* FIXME: Add constraints on N (mln::Function_v2v<N>). */ + template <typename I, typename N> + mln_ch_value(I, mln_result(N)) sobel_norm(const Image<I>& input, N norm); + + + /// \brief Compute the L-1 norm of the Sobel gradient of an image. + /// + /// This function is a shortcut for the general + /// mln::linear::sobel_norm function. + template <typename I> + mln_ch_value(I, mln_sum(mln_value(I))) sobel_norm(const Image<I>& input); # ifndef MLN_INCLUDE_ONLY +/// \brief Ensure type \a V is a scalar value. +/// +/// For internal use only (#undef'd before the end of this file). (We +/// might want to export this macro to the user interface; if so, +/// rename it and move it elsewhere.) +/// +/* FIXME: At the moment (rev. 1645), we cannot use mlc_is_a to check + whether V derives from trait::value::nature::scalar like this + + mlc_is_a(trait::value_<V>::nature, trait::value::nature::scalar)::check() + + since `mlc_is_a' doesn't work on non template classes (yet). Hence + the ugly workaround based on comprehensive checks. */ +# define mlc_internal_ensure_is_scalar(V) \ + mlc_or(mlc_equal(typename mln::trait::value_<V>::nature, \ + mln::trait::value::nature::scalar), \ + mlc_or(mlc_equal(typename mln::trait::value_<V>::nature, \ + mln::trait::value::nature::integer), \ + mlc_equal(typename mln::trait::value_<V>::nature, \ + mln::trait::value::nature::floating)))::check() + namespace impl { - template <typename I, typename O> + template <typename I> inline - void sobel_h_(const Image<I>& input, Image<O>& output) + mln_gradient_component(I) + sobel_h_(const Image<I>& input) { - int wrow[] = { -1, 0, 1 }, wcol[] = { 1, + // We don't use const ints here, since it would confuse the + // STL allocators used within the windows used in the + // convolutions. + int wrow[] = { -1, 0, 1 }; + int wcol[] = { 1, 2, 1 }; + // FIXME: It'd probably be nicer to have a "functional" version + // of linear::line_x2_convolve. + mln_gradient_component(I) output(exact(input).domain()); linear::line_x2_convolve(input, wrow, wcol, output); + return output; } - template <typename I, typename O> + template <typename I> inline - void sobel_v_(const Image<I>& input, Image<O>& output) + mln_gradient_component(I) + sobel_v_(const Image<I>& input) { - int wrow[] = { 1, 2, 1 }, wcol[] = { -1, + // We don't use const ints here, since it would confuse the + // STL allocators used within the windows used in the + // convolutions. + int wrow[] = { 1, 2, 1 }; + int wcol[] = { -1, 0, +1 }; + // FIXME: It'd probably be nicer to have a "functional" version + // of linear::line_x2_convolve. + mln_gradient_component(I) output(exact(input).domain()); linear::line_x2_convolve(input, wrow, wcol, output); + return output; } - template <typename I, typename O> + template <typename I> inline - void sobel_(const Image<I>& input, Image<O>& output) + mln_gradient(I) + sobel_(const Image<I>& input) + { + /* FIXME: Not generic w.r.t. the dimension (only works on 2-D + images here). Anyway, we'd need a n-dimensional definition + of the Sobel gradient to. + + Stick with a very simple static assertion for the momemt, + and improve later. */ + mlc_equal(metal::int_<I::point::dim>, metal::int_<2>)::check(); + + mln_gradient_component(I) component_h = linear::sobel_h(input); + mln_gradient_component(I) component_v = linear::sobel_v(input); + return value::stack(component_h, component_v); + } + + /* FIXME: Add constraints on N (mln::Function_v2v<N>). */ + template <typename I, typename N> + mln_ch_value(I, mln_result(N)) + sobel_norm_(const Image<I>& input, N norm) { - // h - sobel_h(input, output); - // v - O temp(exact(input).domain()); - sobel_v(input, temp); - // output - arith::plus_inplace(output, temp); // FIXME: abs before plus!!! - level::abs_inplace(output); + mln_gradient(I) gradient = mln::linear::sobel (input); + return level::transform (gradient, norm); } } // end of namespace mln::linear::impl @@ -114,30 +202,54 @@ // Facades. - template <typename I, typename O> + template <typename I> inline - void sobel_h(const Image<I>& input, Image<O>& output) + mln_gradient_component(I) sobel_h(const Image<I>& input) { - mln_precondition(exact(output).domain() == exact(input).domain()); - impl::sobel_h_(exact(input), exact(output)); + mlc_internal_ensure_is_scalar(mln_value(I)); + return impl::sobel_h_(exact(input)); } - template <typename I, typename O> + template <typename I> inline - void sobel_v(const Image<I>& input, Image<O>& output) + mln_gradient_component(I) sobel_v(const Image<I>& input) { - mln_precondition(exact(output).domain() == exact(input).domain()); - impl::sobel_v_(exact(input), exact(output)); + mlc_internal_ensure_is_scalar(mln_value(I)); + return impl::sobel_v_(exact(input)); } - template <typename I, typename O> + template <typename I> inline - void sobel(const Image<I>& input, Image<O>& output) + mln_gradient(I) sobel(const Image<I>& input) { - mln_precondition(exact(output).domain() == exact(input).domain()); - impl::sobel_(exact(input), exact(output)); + mlc_internal_ensure_is_scalar(mln_value(I)); + return impl::sobel_(exact(input)); } + template <typename I, typename N> + inline + mln_ch_value(I, mln_result(N)) + sobel_norm(const Image<I>& input, N norm) + { + mlc_internal_ensure_is_scalar(mln_value(I)); + return impl::sobel_norm_(exact(input), norm); + } + + template <typename I> + inline + mln_ch_value(I, mln_sum(mln_value(I))) + sobel_norm(const Image<I>& input) + { + // The type of a component of a vector from the gradient. + typedef mln_sum(mln_value(I)) gradient_val_t; + // The type of a vector from the gradient. + typedef mln::metal::vec<I::point::dim, gradient_val_t> gradient_vec_t; + return sobel_norm(input, + fun::v2v::l1_norm<gradient_vec_t, gradient_val_t>()); + } + +# undef mlc_internal_ensure_is_scalar + # endif // ! MLN_INCLUDE_ONLY } // end of namespace mln::linear Index: tests/linear/sobel.cc --- tests/linear/sobel.cc (revision 1643) +++ tests/linear/sobel.cc (working copy) @@ -1,4 +1,4 @@ -// Copyright (C) 2007 EPITA Research and Development Laboratory +// Copyright (C) 2007, 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 @@ -33,6 +33,7 @@ #include <mln/core/image2d.hh> #include <mln/value/int_u8.hh> #include <mln/level/saturate.hh> +#include <mln/level/stretch.hh> #include <mln/io/pgm/load.hh> #include <mln/io/pgm/save.hh> @@ -40,6 +41,10 @@ #include <mln/border/thickness.hh> #include <mln/linear/sobel.hh> +#include <mln/debug/println.hh> + +#include "tests/data.hh" + int main() { @@ -48,13 +53,22 @@ border::thickness = 1; - image2d<int_u8> - lena = io::pgm::load("../img/lena.pgm"), - out(lena.domain()); - - image2d<int> tmp(lena.domain()); - linear::sobel(lena, tmp); + image2d<int_u8> input; + io::pgm::load(input, MLN_IMG_DIR "/tiny.pgm"); - level::saturate(tmp, out); - io::pgm::save(out, "out.pgm"); + // Create unused objects, for test purpose. + linear::sobel_h(input); + linear::sobel_v(input); + linear::sobel(input); + + linear::sobel_norm(input); + + image2d<float> result = linear::sobel_norm(input); + image2d<int_u8> output (result.domain()); + level::stretch (result, output); + debug::println (output); + + image2d<int_u8> output_sat (output.domain()); + level::saturate(output, output_sat); + io::pgm::save(output_sat, "out.pgm"); } Index: tests/linear/Makefile.am --- tests/linear/Makefile.am (revision 1648) +++ tests/linear/Makefile.am (working copy) @@ -1,3 +1,10 @@ ## Process this file through Automake to create Makefile.in -*- Makefile -*- include $(top_srcdir)/milena/tests/tests.mk + +check_PROGRAMS = \ + sobel + +sobel_SOURCES = sobel.cc + +TESTS = $(check_PROGRAMS)
participants (1)
-
Roland Levillain