milena r1069: Add fast-gaussian

URL: https://svn.lrde.epita.fr/svn/oln/trunk/milena ChangeLog: 2007-09-05 Simon Nivault <simon.nivault@lrde.epita.fr> Add fast-gaussian * sandbox/nivault/fast_gaussian.hh: New. * sandbox/nivault/tests/test.cc: New. * sandbox/nivault/tests/test: New. * sandbox/nivault/tests: New. fast_gaussian.hh | 288 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test | 4 tests/test.cc | 42 ++++++++ 3 files changed, 334 insertions(+) Index: trunk/milena/sandbox/nivault/tests/test =================================================================== --- trunk/milena/sandbox/nivault/tests/test (revision 0) +++ trunk/milena/sandbox/nivault/tests/test (revision 1069) @@ -0,0 +1,4 @@ +#!/bin/bash + +g++ -I../../.. -I../.. -I.. test.cc -O1 +./a.out Property changes on: trunk/milena/sandbox/nivault/tests/test ___________________________________________________________________ Name: svn:executable + * Index: trunk/milena/sandbox/nivault/tests/test.cc =================================================================== --- trunk/milena/sandbox/nivault/tests/test.cc (revision 0) +++ trunk/milena/sandbox/nivault/tests/test.cc (revision 1069) @@ -0,0 +1,42 @@ +// Copyright (C) 2007 EPITA Research and Development Laboratory +// +// 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. + +#include <mln/core/image2d_b.hh> +#include <mln/io/load_pgm.hh> +#include <mln/io/save_pgm.hh> + +#include <fast_gaussian.hh> + +int main() +{ + mln::image2d_b<mln::value::int_u<8u> > im1 = mln::io::load_pgm("../../../img/lena.pgm"); + mln::image2d_b<float> im2; + + mln::linear::gaussian(im1, 0.2, im2); + + // mln::io::save_pgm(im2, "gausslena.pgm"); + } Index: trunk/milena/sandbox/nivault/fast_gaussian.hh =================================================================== --- trunk/milena/sandbox/nivault/fast_gaussian.hh (revision 0) +++ trunk/milena/sandbox/nivault/fast_gaussian.hh (revision 1069) @@ -0,0 +1,288 @@ +// Copyright (C) 2001, 2002, 2003, 2004 EPITA Research and Development Laboratory +// +// 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, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, 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 OLENA_CONVOL_FAST_GAUSSIAN_FILTER_HH__ +# define OLENA_CONVOL_FAST_GAUSSIAN_FILTER_HH__ + +#include <mln/core/concept/image.hh> +#include <mln/level/paste.hh> +#include <mln/geom/ncols.hh> + + +namespace mln +{ + + namespace linear + { + + + struct recursivefilter_coef_ + { + + /*! + ** \brief Constructor. + */ + recursivefilter_coef_(float a0, float a1, + float b0, float b1, + float c0, float c1, + float w0, float w1, + float s); + std::vector<float> n, d, nm, dm; + float sumA, sumC; + }; + + recursivefilter_coef_::recursivefilter_coef_(float a0, float a1, + float b0, float b1, + float c0, float c1, + float w0, float w1, + float s) + { + n.reserve(5); + d.reserve(5); + nm.reserve(5); + dm.reserve(5); + + b0 /= s; + b1 /= s; + w0 /= s; + w1 /= s; + + float sin0 = sin(w0); + float sin1 = sin(w1); + float cos0 = cos(w0); + float cos1 = cos(w1); + + sumA = + (2.0 * a1 * exp( b0 ) * cos0 * cos0 - a0 * sin0 * exp( 2.0 * b0 ) + + a0 * sin0 - 2.0 * a1 * exp( b0 )) / + (( 2.0 * cos0 * exp( b0 ) - exp( 2.0 * b0 ) - 1 ) * sin0); + + sumC = + (2.0 * c1 * exp( b1 ) * cos1 * cos1 - c0 * sin1 * exp( 2.0 * b1 ) + + c0 * sin1 - 2.0 * c1 * exp( b1 )) + / (( 2.0 * cos1 * exp( b1 ) - exp( 2.0 * b1 ) - 1 ) * sin1); + + a0 /= (sumA + sumC); + a1 /= (sumA + sumC); + c0 /= (sumA + sumC); + c1 /= (sumA + sumC); + + n[3] = + exp( -b1 - 2*b0 ) * (c1 * sin1 - cos1 * c0) + + exp( -b0 - 2*b1 ) * (a1 * sin0 - cos0 * a0); + n[2] = + 2 * exp(-b0 - b1) * ((a0 + c0) * cos1 * cos0 - + cos1 * a1 * sin0 - + cos0 * c1 * sin1) + + c0 * exp(-2 * b0) + a0 * exp(-2 * b1); + n[1] = + exp(-b1) * (c1 * sin1 - (c0 + 2*a0) * cos1) + + exp(-b0) * (a1 * sin0 - (2*c0 + a0) * cos0); + n[0] = + a0 + c0; + + d[4] = exp(-2 * b0 - 2 * b1); + d[3] = + -2 * cos0 * exp(-b0 - 2*b1) - + 2 * cos1 * exp(-b1 - 2*b0); + d[2] = + 4 * cos1 * cos0 * exp(-b0 - b1) + + exp(-2*b1) + exp(-2*b0); + d[1] = + -2*exp(-b1) * cos1 - 2 * exp(-b0) * cos0; + + for (unsigned i = 1; i <= 3; ++i) + { + dm[i] = d[i]; + nm[i] = n[i] - d[i] * n[0]; + } + dm[4] = d[4]; + nm[4] = -d[4] * n[0]; + } + + + + template <class WorkType, class I> + void + recursivefilter_(I& ima, + const recursivefilter_coef_& c, + const mln_point(I)& start, + const mln_point(I)& finish, + int len, + const mln_dpoint(I)& d) + { + std::vector<WorkType> tmp1(len); + std::vector<WorkType> tmp2(len); + + // The fourth degree approximation implies to have a special + // look on the four first points we consider that there is + // no signal before 0 (to be discussed) + + // -- + // Causal part + + tmp1[0] = + c.n[0]*ima(start); + + tmp1[1] = + c.n[0]*ima(start + d) + + c.n[1]*ima(start) + - c.d[1]*tmp1[0]; + + tmp1[2] = + c.n[0]*ima(start + d + d) + + c.n[1]*ima(start + d) + + c.n[2]*ima(start) + - c.d[1]*tmp1[1] + - c.d[2]*tmp1[0]; + + tmp1[3] = + c.n[0]*ima(start + d + d + d) + + c.n[1]*ima(start + d + d) + + c.n[2]*ima(start + d) + + c.n[3]*ima(start) + - c.d[1]*tmp1[2] - c.d[2]*tmp1[1] + - c.d[3]*tmp1[0]; + + mln_point(I) current(start + d + d + d + d); + for (mln_coord(I) i = 4; i < len; ++i) + { + tmp1[i] = + c.n[0]*ima(current) + + c.n[1]*ima(current - d) + + c.n[2]*ima(current - d - d) + + c.n[3]*ima(current - d - d - d) + - c.d[1]*tmp1[i - 1] - c.d[2]*tmp1[i - 2] + - c.d[3]*tmp1[i - 3] - c.d[4]*tmp1[i - 4]; + current += d; + } + + // Non causal part + + tmp2[len - 1] = 0; + + tmp2[len - 2] = + c.nm[1]*ima(finish); + + tmp2[len - 3] = + c.nm[1]*ima(finish - d) + + c.nm[2]*ima(finish) + - c.dm[1]*tmp2[len-2]; + + tmp2[len - 4] = + c.nm[1]*ima(finish - d - d) + + c.nm[2]*ima(finish - d) + + c.nm[3]*ima(finish) + - c.dm[1]*tmp2[len-3] + - c.dm[2]*tmp2[len-2]; + + current = finish - d - d - d ; + + for (int i = len - 5; i >= 0; --i) + { + tmp2[i] = + c.nm[1]*ima(current) + + c.nm[2]*ima(current + d) + + c.nm[3]*ima(current + d + d) + + c.nm[4]*ima(current + d + d + d) + - c.dm[1]*tmp2[i+1] - c.dm[2]*tmp2[i+2] + - c.dm[3]*tmp2[i+3] - c.dm[4]*tmp2[i+4]; + current -= d; + } + + // Combine results from causal and non-causal parts. + + current = start; + for (int i = 0; i < len; ++i) + { + ima(current) = tmp1[i] + tmp2[i]; + current += d; + } + } + + + template <class I, class F> + void + gaussian_(Image<I>& img_, const F& coef) + { + I& img = exact(img_); + typedef mln_point(I) P; + // Apply on columns. + + recursivefilter_<float>(img, coef, + make::point2d(0, - img.border()), // FIXME + make::point2d(0, geom::ncols(img) - 1 + img.border()), + geom::ncols(img) + 2 * img.border(), + make::dpoint2d(0, 1)); + } + + + template <class I, class F, class O> + void + gaussian_common_(const Image<I>& in, + const F& coef, + float sigma, + Image<O>& out) + { + mln_ch_value(O, float) work_img(exact(in).domain()); + level::paste(in, work_img); + + // On tiny sigma, Derich algorithm doesn't work. + // It is the same thing that to convolve with a Dirac. + if (sigma > 0.006) + gaussian_(work_img, coef); + /* Convert the result image to the user-requested datatype. + FIXME: We are making an unnecessary copy in case the + user expects a ntg::float_s image. */ + level::paste(work_img, out); + } + + + + + template <class I, class O> + void + gaussian(const Image<I>& in, float sigma, + Image<O>& out) + { + recursivefilter_coef_ + coef(1.68f, 3.735f, + 1.783f, 1.723f, + -0.6803f, -0.2598f, + 0.6318f, 1.997f, + sigma); + + gaussian_common_(in, coef, sigma, out); + } + + } + +} + + + +#endif // OLENA_CONVOL_FAST_GAUSSIAN_FILTER_HH__
participants (1)
-
Simon Nivault