https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox
Index: ChangeLog
from Thierry Geraud <thierry.geraud(a)lrde.epita.fr>
Add some useful executable source.
* theo/exec/gaussian_directional_2d.cc: New.
* theo/exec/rank_rectangle.cc: New.
* theo/exec/closing_rectangle.cc: New.
* theo/exec/gaussian_directional_2d.hh: New.
closing_rectangle.cc | 42 +++
gaussian_directional_2d.cc | 99 +++++++++
gaussian_directional_2d.hh | 475 +++++++++++++++++++++++++++++++++++++++++++++
rank_rectangle.cc | 52 ++++
4 files changed, 668 insertions(+)
Index: theo/exec/gaussian_directional_2d.cc
--- theo/exec/gaussian_directional_2d.cc (revision 0)
+++ theo/exec/gaussian_directional_2d.cc (revision 0)
@@ -0,0 +1,99 @@
+
+# define MLN_FLOAT double
+
+#include "filetype.hh"
+#include "./gaussian_directional_2d.hh"
+
+#include <mln/io/pgm/load.hh>
+#include <mln/io/pgm/save.hh>
+
+#include <mln/pw/all.hh>
+
+#include <mln/data/fill.hh>
+#include <mln/level/saturate.hh>
+
+
+
+
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.xxx bdr
dir sigma output.pgm" << std::endl
+ << " xxx is pbm or pgm" << std::endl
+ << " bdr is the outer border value" << std::endl
+ << " dir = 0 (vertical blur) or 1 (horizontal blur)" <<
std::endl
+ << " sigma > 0" << std::endl
+ << " Directional gaussian blur." << std::endl;
+ std::abort();
+}
+
+
+
+int main(int argc, char* argv[])
+{
+
+ using namespace mln;
+ using value::int_u8;
+
+ trace::entering("main");
+
+ if (argc != 6)
+ usage(argv);
+
+
+ int dir = atoi(argv[3]);
+ if (dir != 0 && dir != 1)
+ usage(argv);
+
+ MLN_FLOAT sigma = atof(argv[4]);
+ if (sigma <= 0.f)
+ usage(argv);
+
+
+ switch (get_filetype(argv[1]))
+ {
+ case filetype::pbm:
+ {
+ int bdr = atoi(argv[2]);
+ if (bdr != 0 && bdr != 1)
+ usage(argv);
+
+ image2d<bool> ima;
+ io::pbm::load(ima, argv[1]);
+
+ image2d<MLN_FLOAT> temp(ima.domain()), out;
+ data::fill(temp, ima);
+
+ out = linear::gaussian_directional_2d(temp, dir, sigma, bdr);
+
+ io::pgm::save(level::saturate(int_u8(),
+ (pw::value(out) * pw::cst(255.f)) | out.domain()),
+ argv[5]);
+ }
+ break;
+
+ case filetype::pgm:
+ {
+ int bdr = atoi(argv[2]);
+ if (bdr < 0 || bdr > 255)
+ usage(argv);
+
+ image2d<int_u8> ima;
+ io::pgm::load(ima, argv[1]);
+
+ image2d<MLN_FLOAT> temp(ima.domain()), out;
+ data::fill(temp, ima);
+
+ out = linear::gaussian_directional_2d(temp, dir, sigma, bdr);
+
+ io::pgm::save(level::saturate(int_u8(), out), argv[5]);
+ }
+ break;
+
+ default:
+ std::cerr << "file type not handled!" << std::endl;
+ usage(argv);
+ }
+
+ trace::exiting("main");
+}
Index: theo/exec/rank_rectangle.cc
--- theo/exec/rank_rectangle.cc (revision 0)
+++ theo/exec/rank_rectangle.cc (revision 0)
@@ -0,0 +1,52 @@
+#include <mln/core/image/image2d.hh>
+#include <mln/io/pbm/load.hh>
+#include <mln/io/pbm/save.hh>
+
+#include <mln/win/hline2d.hh>
+#include <mln/win/rectangle2d.hh>
+
+#include <mln/morpho/rank_filter.hh>
+
+
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.pbm
height width k output.pbm" << std::endl
+ << " Rank filter with a 2D rectangle." << std::endl;
+ std::abort();
+}
+
+
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+ using value::int_u8;
+
+ if (argc != 6)
+ usage(argv);
+
+ trace::entering("main");
+
+ image2d<bool> ima, out;
+ io::pbm::load(ima, argv[1]);
+
+ int height = atoi(argv[2]);
+ if (height < 0)
+ usage(argv);
+
+ int width = atoi(argv[3]);
+ if (width < 0)
+ usage(argv);
+
+ int k = atoi(argv[4]);
+ if (k < 1 || k > height * width)
+ usage(argv);
+
+ out = morpho::rank_filter(ima,
+ win::rectangle2d(height, width),
+ k);
+ io::pbm::save(out, argv[5]);
+
+ trace::exiting("main");
+}
Index: theo/exec/closing_rectangle.cc
--- theo/exec/closing_rectangle.cc (revision 0)
+++ theo/exec/closing_rectangle.cc (revision 0)
@@ -0,0 +1,42 @@
+#include "filetype.hh"
+
+#include <mln/morpho/closing/structural.hh>
+
+
+
+void usage(char* argv[])
+{
+ std::cerr << "usage: " << argv[0] << " input.pgm
height width output.pgm" << std::endl
+ << " Height and width are odd positive integers." <<
std::endl
+ << " Rectangle closing." << std::endl;
+ std::abort();
+}
+
+
+
+int main(int argc, char* argv[])
+{
+ using namespace mln;
+ using value::int_u8;
+
+ trace::entering("main");
+
+ if (argc != 5)
+ usage(argv);
+
+ int height = atoi(argv[2]);
+ if (height < 0 || height % 2 == 0)
+ usage(argv);
+
+ int width = atoi(argv[3]);
+ if (width < 0 || width % 2 == 0)
+ usage(argv);
+
+ image2d<int_u8> ima, clo;
+ io::pgm::load(ima, argv[1]);
+
+ clo = morpho::closing::structural(ima, win::rectangle2d(height, width));
+ io::pgm::save(clo, argv[4]);
+
+ trace::exiting("main");
+}
Index: theo/exec/gaussian_directional_2d.hh
--- theo/exec/gaussian_directional_2d.hh (revision 0)
+++ theo/exec/gaussian_directional_2d.hh (revision 0)
@@ -0,0 +1,475 @@
+
+#include <mln/core/image/image2d.hh>
+#include <mln/extension/adjust_fill.hh>
+#include <mln/extension/adjust_duplicate.hh>
+
+
+
+namespace mln
+{
+
+ namespace linear
+ {
+
+ namespace my
+ {
+
+ struct recursivefilter_coef_
+ {
+ enum FilterType { DericheGaussian,
+ DericheGaussianFirstDerivative,
+ DericheGaussianSecondDerivative };
+
+ std::vector<MLN_FLOAT> n, d, nm, dm;
+ MLN_FLOAT sumA, sumC;
+
+ recursivefilter_coef_(MLN_FLOAT a0, MLN_FLOAT a1,
+ MLN_FLOAT b0, MLN_FLOAT b1,
+ MLN_FLOAT c0, MLN_FLOAT c1,
+ MLN_FLOAT w0, MLN_FLOAT w1,
+ MLN_FLOAT s, FilterType filter_type)
+ {
+ n.reserve(5);
+ d.reserve(5);
+ nm.reserve(5);
+ dm.reserve(5);
+
+ b0 /= s;
+ b1 /= s;
+ w0 /= s;
+ w1 /= s;
+
+ MLN_FLOAT sin0 = sin(w0);
+ MLN_FLOAT sin1 = sin(w1);
+ MLN_FLOAT cos0 = cos(w0);
+ MLN_FLOAT cos1 = cos(w1);
+
+ switch (filter_type) {
+
+ case DericheGaussian :
+ {
+ 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);
+ break;
+ }
+
+ case DericheGaussianFirstDerivative :
+ {
+ sumA = -2.f *
+ (a0 * cos0 - a1 * sin0 + a1 * sin0 * exp( 2.0 * b0 )
+ + a0 * cos0 * exp( 2.0 * b0 ) - 2.0 * a0 * exp( b0 ))
+ * exp( b0 )
+ / (exp( 4.0 * b0 ) - 4.0 * cos0 * exp( 3.0 * b0 )
+ + 2.0 * exp( 2.0 * b0 ) + 4.0 * cos0 * cos0 * exp( 2.0 * b0 )
+ + 1.0 - 4.0 * cos0 * exp( b0 ));
+ sumC = - 2.f *
+ (c0 * cos1 - c1 * sin1 + c1 * sin1 * exp( 2.0 * b1 )
+ + c0 * cos1 * exp( 2.0 * b1 ) - 2.0 * c0 * exp( b1 ))
+ * exp( b1 ) /
+ (exp( 4.0 * b1 ) - 4.0 * cos1 * exp( 3.0 * b1 )
+ + 2.0 * exp( 2.0 * b1 ) + 4.0 * cos1 * cos1 * exp( 2.0 * b1 )
+ + 1.0 - 4.0 * cos1 * exp( b1 ));
+ break;
+ }
+
+ case DericheGaussianSecondDerivative :
+ {
+ MLN_FLOAT aux;
+ aux =
+ 12.0 * cos0 * exp( 3.0 * b0 ) - 3.0 * exp( 2.0 * b0 )
+ + 8.0 * cos0 * cos0 * cos0 * exp( 3.0 * b0 ) - 12.0 * cos0 * cos0 *
+ exp( 4.0 * b0 )
+ - (3.0 * exp( 4.0 * b0 ))
+ + 6.0 * cos0 * exp( 5.0 * b0 ) - exp( 6.0 * b0 ) + 6.0 * cos0 * exp
+ ( b0 )
+ - ( 1.0 + 12.0 * cos0 * cos0 * exp( 2.0 * b0 ) );
+ sumA =
+ 4.0 * a0 * sin0 * exp( 3.0 * b0 ) + a1 * cos0 * cos0 * exp( 4.0 * b0 )
+ - ( 4.0 * a0 * sin0 * exp( b0 ) + 6.0 * a1 * cos0 * cos0 * exp( 2.0 * b0 ) )
+ + 2.0 * a1 * cos0 * cos0 * cos0 * exp( b0 ) - 2.0 * a1 * cos0 * exp(b0)
+ + 2.0 * a1 * cos0 * cos0 * cos0 * exp( 3.0 * b0 ) - 2.0 * a1 * cos0
+ * exp( 3.0 * b0 )
+ + a1 * cos0 * cos0 - a1 * exp( 4.0 * b0 )
+ + 2.0 * a0 * sin0 * cos0 * cos0 * exp( b0 ) - 2.0 * a0 * sin0 * cos0
+ * cos0 * exp( 3.0 * b0 )
+ - ( a0 * sin0 * cos0 * exp( 4.0 * b0 ) + a1 )
+ + 6.0 * a1 * exp( 2.0 * b0 ) + a0 * cos0 * sin0
+ * 2.0 * exp( b0 ) / ( aux * sin0 );
+ aux =
+ 12.0 * cos1 * exp( 3.0 * b1 ) - 3.0 * exp( 2.0 * b1 )
+ + 8.0 * cos1 * cos1 * cos1 * exp( 3.0 * b1 ) - 12.0 * cos1 * cos1 *
+ exp( 4.0 * b1 )
+ - 3.0 * exp( 4.0 * b1 )
+ + 6.0 * cos1 * exp( 5.0 * b1 ) - exp( 6.0 * b1 ) + 6.0 * cos1 * exp
+ ( b1 )
+ - ( 1.0 + 12.0 * cos1 * cos1 * exp( 2.0 * b1 ) );
+ sumC = 4.0 * c0 * sin1 * exp( 3.0 * b1 ) + c1 * cos1 * cos1 * exp( 4.0 * b1 )
+ - ( 4.0 * c0 * sin1 * exp( b1 ) + 6.0 * c1 * cos1 * cos1 * exp( 2.0 * b1 ) )
+ + 2.0 * c1 * cos1 * cos1 * cos1 * exp( b1 ) - 2.0 * c1 * cos1 * exp( b1 )
+ + 2.0 * c1 * cos1 * cos1 * cos1 * exp( 3.0 * b1 ) - 2.0 * c1 * cos1
+ * exp( 3.0 * b1 )
+ + c1 * cos1 * cos1 - c1 * exp( 4.0 * b1 )
+ + 2.0 * c0 * sin1 * cos1 * cos1 * exp( b1 ) - 2.0 * c0 * sin1 * cos1
+ * cos1 * exp( 3.0 * b1 )
+ - ( c0 * sin1 * cos1 * exp( 4.0 * b1 ) + c1 )
+ + 6.0 * c1 * exp( 2.0 * b1 ) + c0 * cos1 * sin1
+ * 2.0 * exp( b1 ) / ( aux * sin1 );
+ sumA /= 2;
+ sumC /= 2;
+ break;
+ }
+ }
+
+ 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;
+
+ switch (filter_type) {
+ case DericheGaussian :
+ case DericheGaussianSecondDerivative :
+ {
+ 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];
+ break;
+ }
+ case DericheGaussianFirstDerivative :
+ {
+ 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];
+ break;
+ }
+ }
+ }
+ };
+
+
+ } // end of namespace mln::linear::my
+
+
+
+
+ // FIXME: in the "generic" code below there is no test that we
+ // actually stay in the image domain...
+
+
+ template <typename I, typename C>
+ inline
+ void
+ recursivefilter_directional_2d_generic(I& ima,
+ const C& c,
+ const mln_psite(I)& start,
+ const mln_psite(I)& finish,
+ int len,
+ const mln_deduce(I, psite, delta)& d)
+ {
+ std::vector<MLN_FLOAT> tmp1(len);
+ std::vector<MLN_FLOAT> tmp2(len);
+
+ 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_site(I) current = start + d + d + d + d;
+ for (int 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 <typename I, typename C>
+ inline
+ void
+ recursivefilter_directional_2d_fastest(I& ima,
+ const C& c,
+ const mln_psite(I)& start,
+ const mln_psite(I)& finish,
+ int len,
+ const mln_deduce(I, psite, delta)& d,
+ const mln_value(I)& bdr)
+ {
+// extension::adjust_fill(ima, 5 * int(151 + .50001) + 1, bdr);
+ // extension::fill(ima, bdr);
+
+ std::vector<MLN_FLOAT> tmp1(len);
+ std::vector<MLN_FLOAT> tmp2(len);
+
+ unsigned delta_offset = ima.delta_index(d);
+ unsigned
+ o_start = ima.index_of_point(start),
+ o_start_d = o_start + delta_offset,
+ o_start_dd = o_start + 2 * delta_offset,
+ o_start_ddd = o_start + 3 * delta_offset;
+
+ tmp1[0] =
+ c.n[0] * ima.element(o_start);
+
+ tmp1[1] = 0
+ + c.n[0] * ima.element(o_start_d)
+ + c.n[1] * ima.element(o_start)
+ - c.d[1] * tmp1[0];
+
+ tmp1[2] = 0
+ + c.n[0] * ima.element(o_start_dd)
+ + c.n[1] * ima.element(o_start_d)
+ + c.n[2] * ima.element(o_start)
+ - c.d[1] * tmp1[1]
+ - c.d[2] * tmp1[0];
+
+ tmp1[3] = 0
+ + c.n[0] * ima.element(o_start_ddd)
+ + c.n[1] * ima.element(o_start_dd)
+ + c.n[2] * ima.element(o_start_d)
+ + c.n[3] * ima.element(o_start)
+ - c.d[1] * tmp1[2] - c.d[2] * tmp1[1]
+ - c.d[3] * tmp1[0];
+
+ unsigned
+ o_current = o_start + 4 * delta_offset,
+ o_current_d = o_current - delta_offset,
+ o_current_dd = o_current - 2 * delta_offset,
+ o_current_ddd = o_current - 3 * delta_offset;
+
+ for (int i = 4; i < len; ++i)
+ {
+ tmp1[i] = 0
+ + c.n[0] * ima.element(o_current)
+ + c.n[1] * ima.element(o_current_d)
+ + c.n[2] * ima.element(o_current_dd)
+ + c.n[3] * ima.element(o_current_ddd)
+ - c.d[1] * tmp1[i - 1] - c.d[2] * tmp1[i - 2]
+ - c.d[3] * tmp1[i - 3] - c.d[4] * tmp1[i - 4];
+ o_current += delta_offset;
+ o_current_d += delta_offset;
+ o_current_dd += delta_offset;
+ o_current_ddd += delta_offset;
+ }
+
+ // Non causal part
+
+ // extension::fill(ima, bdr);
+
+ unsigned
+ o_finish = ima.index_of_point(finish),
+ o_finish_d = o_finish - delta_offset,
+ o_finish_dd = o_finish - 2 * delta_offset;
+
+ tmp2[len - 1] = 0;
+
+ tmp2[len - 2] =
+ c.nm[1] * ima.element(o_finish);
+
+ tmp2[len - 3] = 0
+ + c.nm[1] * ima.element(o_finish_d)
+ + c.nm[2] * ima.element(o_finish)
+ - c.dm[1] * tmp2[len-2];
+
+ tmp2[len - 4] = 0
+ + c.nm[1] * ima.element(o_finish_dd)
+ + c.nm[2] * ima.element(o_finish_d)
+ + c.nm[3] * ima.element(o_finish)
+ - c.dm[1] * tmp2[len-3]
+ - c.dm[2] * tmp2[len-2];
+
+ o_current = o_finish - 3 * delta_offset;
+ o_current_d = o_current + delta_offset;
+ o_current_dd = o_current + 2 * delta_offset;
+ o_current_ddd = o_current + 3 * delta_offset;
+
+ for (int i = len - 5; i >= 0; --i)
+ {
+ tmp2[i] = 0
+ + c.nm[1] * ima.element(o_current)
+ + c.nm[2] * ima.element(o_current_d)
+ + c.nm[3] * ima.element(o_current_dd)
+ + c.nm[4] * ima.element(o_current_ddd)
+ - c.dm[1] * tmp2[i+1] - c.dm[2] * tmp2[i+2]
+ - c.dm[3] * tmp2[i+3] - c.dm[4] * tmp2[i+4];
+ o_current -= delta_offset;
+ o_current_d -= delta_offset;
+ o_current_dd -= delta_offset;
+ o_current_ddd -= delta_offset;
+ }
+
+ // Combine results from causal and non-causal parts.
+
+ o_current = o_start;
+ for (int i = 0; i < len; ++i)
+ {
+ ima.element(o_current) = (tmp1[i] + tmp2[i]);
+ o_current += delta_offset;
+ }
+ }
+
+
+ template <typename I>
+ inline
+ mln_concrete(I)
+ gaussian_directional_2d(const Image<I>& input_,
+ unsigned dir, MLN_FLOAT sigma,
+ const mln_value(I)& bdr)
+ {
+ const I& input = exact(input_);
+
+ mln_precondition(dir == 0 || dir == 1);
+ mln_precondition(input.is_valid());
+
+ my::recursivefilter_coef_ coef(1.68f, 3.735f,
+ 1.783f, 1.723f,
+ -0.6803f, -0.2598f,
+ 0.6318f, 1.997f,
+ sigma,
+ my::recursivefilter_coef_::DericheGaussian);
+
+ extension::adjust_fill(input, 5 * int(sigma + .50001) + 1, bdr);
+ mln_concrete(I) output = duplicate(input);
+
+ if (sigma < 0.006)
+ return output;
+
+ int
+ nrows = geom::nrows(input),
+ ncols = geom::ncols(input),
+ b = input.border();
+
+ if (dir == 0)
+ {
+ for (int j = 0; j < ncols; ++j)
+ recursivefilter_directional_2d_fastest(output, coef,
+ point2d(- b, j),
+ point2d(nrows - 1 + b, j),
+ nrows + 2 * b,
+ dpoint2d(1, 0),
+ bdr);
+ }
+
+ if (dir == 1)
+ {
+ for (int i = 0; i < nrows; ++i)
+ recursivefilter_directional_2d_fastest(output, coef,
+ point2d(i, - b),
+ point2d(i, ncols - 1 + b),
+ ncols + 2 * b,
+ dpoint2d(0, 1),
+ bdr);
+ }
+
+ return output;
+ }
+
+
+ } // end of namespace mln::linear
+
+} // end of namespace mln