https://svn.lrde.epita.fr/svn/oln/trunk/milena
Index: ChangeLog
from Roland Levillain <roland(a)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)