* mln/transform/fft.hh: Here.
---
 milena/ChangeLog            |    6 +
 milena/mln/transform/fft.hh |  722 ++++++++++++++++++++++++++-----------------
 2 files changed, 436 insertions(+), 292 deletions(-)
diff --git a/milena/ChangeLog b/milena/ChangeLog
index 8f96157..72bfb1a 100644
--- a/milena/ChangeLog
+++ b/milena/ChangeLog
@@ -1,5 +1,11 @@
 2012-10-10  Roland Levillain  <roland(a)lrde.epita.fr>
 
+	Split the interface of the FFT off from its implementation.
+
+	* mln/transform/fft.hh: Here.
+
+2012-10-10  Roland Levillain  <roland(a)lrde.epita.fr>
+
 	Stylistic changes in the Fast Fourier Transform.
 
 	* mln/transform/fft.hh: Here.
diff --git a/milena/mln/transform/fft.hh b/milena/mln/transform/fft.hh
index 9b70b4f..36e4b80 100644
--- a/milena/mln/transform/fft.hh
+++ b/milena/mln/transform/fft.hh
@@ -42,9 +42,11 @@ namespace mln
   namespace internal
   {
 
-    /// Dispatch traits for fftw
+    /// Dispatch tags for FFT traits.
     enum fft_dispatch { fft_cplx, fft_real };
 
+
+    /// \brief FFT traits.
     template <typename T>
     struct fft_trait;
 
@@ -68,6 +70,7 @@ namespace mln
       typedef std::complex<T> fftw_input;
     };
 
+
     /// \brief Internal structure containining packing data and
     /// instructions for the (forward) and inverse (backward)
     /// transforms.
@@ -78,16 +81,10 @@ namespace mln
     {
     public:
       /// \brief Accessor to transformed image (const version).
-      const image2d< std::complex<T> >& transformed_image() const
-      {
-	return trans_im;
-      }
+      const image2d< std::complex<T> >& transformed_image() const;
 
       /// \brief Accessor to transformed image (non const version).
-      image2d< std::complex<T> >& transformed_image()
-      {
-	return trans_im;
-      }
+      image2d< std::complex<T> >& transformed_image();
 
       /// \brief Accessor to the transformed image (magnitude).
       ///
@@ -98,28 +95,7 @@ namespace mln
       ///
       /// \param ordered Kind of traversal.
       template <class R>
-      image2d<R> transformed_image_magn(bool ordered = true) const
-      {
-	// FIXME: Ensure R is real.
-
-	const unsigned nrows = trans_im.nrows();
-	const unsigned ncols = trans_im.ncols();
-	image2d<R> new_im(trans_im.domain());
-	if (ordered)
-	  for (unsigned row = 0; row < new_im.nrows(); ++row)
-	    for (unsigned col = 0; col < new_im.ncols(); ++col)
-	      opt::at(new_im, row, col) =
-		std::norm(opt::at(trans_im,
-				  (row + nrows / 2) % nrows,
-				  (col + ncols / 2) % ncols));
-	else
-	  {
-	    mln_piter(image2d< std::complex<T> >) it(trans_im.domain());
-	    for_all(it)
-	      new_im(it) = std::norm(trans_im(it));
-	  }
-	return new_im;
-      }
+      image2d<R> transformed_image_magn(bool ordered = true) const;
 
       /// \brief Accessor to the transformed image (magnitude).
       ///
@@ -127,10 +103,7 @@ namespace mln
       /// an image containing <tt>|T[p]|</tt> is returned.
       ///
       /// \param ordered Kind of traversal.
-      image2d<T> transformed_image_magn(bool ordered = true) const
-      {
-	return transformed_image_magn<T>(ordered);
-      }
+      image2d<T> transformed_image_magn(bool ordered = true) const;
 
       /// \brief Accessor to the transformed image (clipped
       /// magnitude).
@@ -144,43 +117,7 @@ namespace mln
       /// \param ordered Kind of traversal.
       template <class R>
       image2d<R> transformed_image_clipped_magn(double clip,
-						bool ordered = true) const
-      {
-	// FIXME: Ensure R is real.
-	// FIXME: Ensure `clip' is between 0 and 1?
-
-	double max = mln_min(double);
-	mln_piter(image2d<T>) it(trans_im.domain());
-	for_all(it)
-	  if (std::norm(trans_im(it)) > max)
-	    max = std::norm(trans_im(it));
-
-	const unsigned nrows = trans_im.nrows();
-	const unsigned ncols = trans_im.ncols();
-	image2d<R> new_im(trans_im.domain());
-	if (ordered)
-	  for (unsigned row = 0; row < new_im.nrows(); ++row)
-	    for (unsigned col = 0; col < new_im.ncols(); ++col)
-	      if (std::norm(opt::at(trans_im,
-				    (row + nrows / 2) % nrows,
-				    (col + ncols / 2) % ncols))
-		  >= max * clip)
-		opt::at(new_im, row, col) = mln_max(R);
-	      else
-		opt::at(new_im, row, col) =
-		  (double) mln_max(R) *
-		  std::norm(opt::at(trans_im,
-				    (row + nrows / 2) % nrows,
-				    (col + ncols / 2) % ncols)) / (max * clip);
-	else
-	  for_all(it)
-	    if (std::norm(trans_im(it)) >= max * clip)
-	      new_im(it) = mln_max(R);
-	    else
-	      new_im(it) =
-		(double) mln_max(R) * std::norm(trans_im(it)) / (max * clip);
-	return new_im;
-      }
+						bool ordered = true) const;
 
       /// \brief Accessor to the transformed image (clipped
       /// magnitude).
@@ -191,10 +128,7 @@ namespace mln
       /// \param clip    Value used for clipping.
       /// \param ordered Kind of traversal.
       image2d<T> transformed_image_clipped_magn(double clip,
-						bool ordered = true) const
-      {
-	return transformed_image_clipped_magn<T>(clip, ordered);
-      }
+						bool ordered = true) const;
 
       /// \brief Accessor to the transformed image (clipped
       /// magnitude).
@@ -206,10 +140,7 @@ namespace mln
       ///
       /// \param ordered Kind of traversal.
       template <class R>
-      image2d<R> transformed_image_clipped_magn(bool ordered = true) const
-      {
-	return transformed_image_clipped_magn<R>(1, ordered);
-      }
+      image2d<R> transformed_image_clipped_magn(bool ordered = true) const;
 
       /// \brief Accessor to the transformed image (clipped
       /// magnitude).
@@ -218,10 +149,7 @@ namespace mln
       /// containing a clipped value of <tt>|T[p]|</tt> is returned.
       ///
       /// \param ordered Kind of traversal.
-      image2d<T> transformed_image_clipped_magn(bool ordered = true) const
-      {
-	return transformed_image_clipped_magn<T>(1, ordered);
-      }
+      image2d<T> transformed_image_clipped_magn(bool ordered = true) const;
 
       /// \brief Accessor to the transformed image (log of the
       /// magnitude).
@@ -237,43 +165,7 @@ namespace mln
       /// \param ordered Kind of traversal.
       template <class R>
       image2d<R> transformed_image_log_magn(double a, double b,
-					    bool ordered = true) const
-      {
-	/* FIXME: Find a more elegant way to fix range interval on a
-	   and b (Note from Roland: what does it mean?).  */
-
-	// FIXME: Ensure R is real.
-	// FIXME: Ensure 0 <= a <= 1000.
-	// FIXME: Ensure 0 <= b <= 1000.
-
-	image2d<R> new_im(trans_im.domain());
-
-	double max = mln_min(double);
-	mln_piter(image2d<R>) it(trans_im.domain());
-	for_all(it)
-	  if (std::norm(trans_im(it)) > max)
-	    max = std::norm(trans_im(it));
-
-	const unsigned nrows = trans_im.nrows();
-	const unsigned ncols = trans_im.ncols();
-	if (ordered)
-	  for (unsigned row = 0; row < new_im.nrows(); ++row)
-	    for (unsigned col = 0; col < new_im.ncols(); ++col)
-	      opt::at(new_im, row, col) =
-		log(a + b * std::norm(opt::at(trans_im,
-					      (row + nrows / 2) % nrows,
-					      (col + ncols / 2) % ncols)))
-		/ log (a + b * max) * mln_max(R);
-	else
-	  {
-	    mln_piter(image2d< std::complex<T> >) it(trans_im.domain());
-	    for_all(it)
-	      new_im(it) =
-	      log(a + b * std::norm(trans_im(it)))
-	      / log (a + b * max) * mln_max(R);
-	  }
-	return new_im;
-      }
+					    bool ordered = true) const;
 
       /// \brief Accessor to the transformed image (log of the
       /// magnitude).
@@ -286,10 +178,7 @@ namespace mln
       /// \param b       Upper bound.
       /// \param ordered Kind of traversal.
       image2d<T> transformed_image_log_magn(double a, double b,
-					    bool ordered = true) const
-      {
-	return transformed_image_log_magn<T>(a, b, ordered);
-      }
+					    bool ordered = true) const;
 
       /// \brief Accessor to the transformed image (log of the
       /// magnitude).
@@ -302,10 +191,7 @@ namespace mln
       ///
       /// \param ordered Kind of traversal.
       template <class R>
-      image2d<R> transformed_image_log_magn(bool ordered = true) const
-      {
-	return transformed_image_log_magn<R>(1, 100, ordered);
-      }
+      image2d<R> transformed_image_log_magn(bool ordered = true) const;
 
       /// \brief Accessor to the transformed image (log of the
       /// magnitude).
@@ -315,18 +201,9 @@ namespace mln
       /// interval [1, 100].
       ///
       /// \param ordered Kind of traversal.
-      image2d<T> transformed_image_log_magn(bool ordered = true) const
-      {
-	return transformed_image_log_magn<T>(1, 100, ordered);
-      }
-
-      ~fft()
-      {
-	fftw_free(in);
-	fftw_free(out);
-	fftw_destroy_plan(p);
-	fftw_destroy_plan(p_inv);
-      }
+      image2d<T> transformed_image_log_magn(bool ordered = true) const;
+
+      ~fft();
 
     protected:
       /// Input image (used internally by FFTW).
@@ -359,7 +236,7 @@ namespace mln
     class fft;
 
 
-    /// \brief FFT engine (specialization for images of real values).
+    /// \brief oFFT engine (specialization for images of real values).
     ///
     /// \tparam T Data type.
     template <class T>
@@ -372,96 +249,20 @@ namespace mln
       ///
       /// \param original_im Image to process.
       template <typename D>
-      fft(const image2d<D>& original_im)
-      {
-	const unsigned nrows = original_im.nrows();
-	const unsigned ncols = original_im.ncols();
-
-	this->in  = (T*) fftw_malloc(nrows * ncols * sizeof(T));
-	this->out = (std::complex<T>*) fftw_malloc(nrows
-						   * (ncols / 2 + 1)
-						   * sizeof(std::complex<T>));
-
-	for (unsigned row = 0; row < nrows; ++row)
-	  for (unsigned col = 0; col < ncols; ++col)
-	    this->in[row * ncols + col] =
-	      opt::at(original_im, row, col);
-
-	this->p =
-	  fftw_plan_dft_r2c_2d (nrows, ncols,
-				this->in,
-				reinterpret_cast<fftw_complex*>(this->out),
-				FFTW_ESTIMATE);
-	this->p_inv =
-	  fftw_plan_dft_c2r_2d (nrows, ncols,
-				reinterpret_cast<fftw_complex*>(this->out),
-				this->in,
-				FFTW_ESTIMATE);
-	this->trans_im = image2d< std::complex<T> >(original_im.domain());
-      }
+      fft(const image2d<D>& original_im);
 
       /// \brief Compute and return the transform (as a complex image).
-      image2d< std::complex<T> > transform()
-      {
-	fftw_execute(this->p);
-
-	const unsigned nrows = this->trans_im.nrows();
-	const unsigned ncols = this->trans_im.ncols();
-	unsigned denom = nrows * ncols;
-	int i = 0;
-	for (unsigned row = 0; row < nrows; ++row)
-	  for (unsigned col = 0; col <= ncols / 2; ++col)
-	    {
-	      this->out[i] = std::complex<T> (this->out[i].real() / denom,
-					      this->out[i].imag() / denom);
-	      opt::at(this->trans_im, row, col) = this->out[i];
-	      ++i;
-	    }
-	for (unsigned row = 0; row < nrows; ++row)
-	  for (unsigned col = ncols - 1; col > ncols / 2; --col)
-	    opt::at(this->trans_im, row, col) =
-	      opt::at(this->trans_im, nrows - row - 1, ncols - col - 1);
-	return this->trans_im;
-      }
+      image2d< std::complex<T> > transform();
 
       /// \brief Compute and return the inverse transform (as a real
       /// image) of the FFT.
       ///
       /// \tparam R Value type of output image.
       template <class R>
-      image2d<R> transform_inv()
-      {
-	// FIXME: Ensure R is real.
-
-	const unsigned nrows = this->trans_im.nrows();
-	const unsigned ncols = this->trans_im.ncols();
-	for (unsigned row = 0; row < nrows; ++row)
-	  for (unsigned col = 0; col <= ncols / 2; ++col)
-	    this->out[row * (ncols / 2 + 1) + col] =
-	      opt::at(this->trans_im, row, col);
-
-	fftw_execute(this->p_inv);
-
-	image2d<R> new_im(this->trans_im.domain());
-	int i = 0;
-	for (unsigned row = 0; row < nrows; ++row)
-	  for (unsigned col = 0; col < ncols; ++col)
-	    {
-	      opt::at(new_im, row, col) = (this->in[i] >= mln_min(R)
-					   ? (this->in[i] <= mln_max(R)
-					      ? (R) this->in [i]
-					      : mln_min(R))
-					   : mln_max(R));
-	      ++i;
-	    }
-	return new_im;
-      }
+      image2d<R> transform_inv();
 
       /// \brief Compute and return the inverse transform of the FFT.
-      image2d<T> transform_inv()
-      {
-	return transform_inv<T>();
-      }
+      image2d<T> transform_inv();
     };
 
 
@@ -477,48 +278,10 @@ namespace mln
       /// Initialization of data for the computation of the FFT.
       ///
       /// \param original_im Image to process.
-      fft(const image2d< std::complex<T> >& original_im)
-      {
-	const unsigned nrows = original_im.nrows();
-	const unsigned ncols = original_im.ncols();
-
-	this->in  = fftw_malloc(sizeof(std::complex<T>) * nrows * ncols);
-	this->out = fftw_malloc(sizeof(std::complex<T>) * nrows * ncols);
-
-	for (unsigned row = 0; row < nrows; ++row)
-	  for (unsigned col = 0; col < ncols; ++col)
-	    {
-	      this->in[row * ncols + col].re = original_im(row, col).real();
-	      this->in[row * ncols + col].im = original_im(row, col).imag();
-	    }
-
-	this->p     = fftw_plan_dft_2d(nrows, ncols, this->in, this->out,
-				       FFTW_FORWARD, FFTW_ESTIMATE);
-	this->p_inv = fftw_plan_dft_2d(nrows, ncols, this->out, this->in,
-				       FFTW_BACKWARD, FFTW_ESTIMATE);
-	this->trans_im = image2d< std::complex<T> >(original_im.domain());
-      }
+      fft(const image2d< std::complex<T> >& original_im);
 
       /// \brief Compute and return the transform (as a complex image).
-      image2d< std::complex<T> > transform()
-      {
-	fftw_execute(this->p);
-
-	const unsigned nrows = this->trans_im.nrows();
-	const unsigned ncols = this->trans_im.ncols();
-	unsigned denom = nrows * ncols;
-	int i = 0;
-	for (unsigned row = 0; row < nrows; ++row)
-	  for (unsigned col = 0; col < ncols; ++col)
-	    {
-	      this->out[i].re /= denom;
-	      this->out[i].im /= denom;
-	      this->trans_im(row, col) =
-		std::complex<double>(this->out[i].re, this->out[i].im);
-	      ++i;
-	    }
-	return this->trans_im;
-      }
+      image2d< std::complex<T> > transform();
 
       /// \brief Compute and return the inverse transform (as a
       /// complex image).
@@ -526,42 +289,417 @@ namespace mln
       /// \tparam R Component type of the value type of the output
       /// image.
       template <class R>
-      image2d< std::complex<R> > transform_inv()
-      {
-	const unsigned nrows = this->trans_im.nrows();
-	const unsigned ncols = this->trans_im.ncols();
-	for (unsigned row = 0; row < nrows; ++row)
-	  for (unsigned col = 0; col < ncols; ++col)
-	    {
-	      this->out[row * ncols + col].re =
-		this->trans_im(row, col).real();
-	      this->out[row * ncols + col].im =
-		this->trans_im(row, col).imag();
-	    }
-
-	fftw_execute(this->p_inv);
-
-	image2d< std::complex<R> > new_im(this->trans_im.size());
-	int i = 0;
-	for (unsigned row = 0; row < nrows; ++row)
-	  for (unsigned col = 0; col < ncols; ++col)
-	    {
-	      new_im(row, col) = this->in[i];
-	      ++i;
-	    }
-	return new_im;
-      }
+      image2d< std::complex<R> > transform_inv();
 
       /// \brief Compute and return the inverse transform of the FFT.
-      image2d< std::complex<T> > transform_inv()
-      {
-	return transform_inv<T>();
-      }
-
+      image2d< std::complex<T> > transform_inv();
     };
 
   } // end of namespace mln::transform
 
+
+
+# ifndef MLN_INCLUDE_ONLY
+
+  namespace internal
+  {
+
+    template <class T>
+    inline
+    const image2d< std::complex<T> >&
+    fft<T>::transformed_image() const
+    {
+      return trans_im;
+    }
+
+    template <class T>
+    inline
+    image2d< std::complex<T> >&
+    fft<T>::transformed_image()
+    {
+      return trans_im;
+    }
+
+    template <class T>
+    template <class R>
+    inline
+    image2d<R>
+    fft<T>::transformed_image_magn(bool ordered) const
+    {
+      // FIXME: Ensure R is real.
+
+      const unsigned nrows = trans_im.nrows();
+      const unsigned ncols = trans_im.ncols();
+      image2d<R> new_im(trans_im.domain());
+      if (ordered)
+	for (unsigned row = 0; row < new_im.nrows(); ++row)
+	  for (unsigned col = 0; col < new_im.ncols(); ++col)
+	    opt::at(new_im, row, col) =
+	      std::norm(opt::at(trans_im,
+				(row + nrows / 2) % nrows,
+				(col + ncols / 2) % ncols));
+      else
+	{
+	  mln_piter(image2d< std::complex<T> >) it(trans_im.domain());
+	  for_all(it)
+	    new_im(it) = std::norm(trans_im(it));
+	}
+      return new_im;
+    }
+
+    template <class T>
+    inline
+    image2d<T>
+    fft<T>::transformed_image_magn(bool ordered) const
+    {
+      return transformed_image_magn<T>(ordered);
+    }
+
+    template <class T>
+    template <class R>
+    inline
+    image2d<R>
+    fft<T>::transformed_image_clipped_magn(double clip, bool ordered) const
+    {
+      // FIXME: Ensure R is real.
+      // FIXME: Ensure `clip' is between 0 and 1?
+
+      double max = mln_min(double);
+      mln_piter(image2d<T>) it(trans_im.domain());
+      for_all(it)
+	if (std::norm(trans_im(it)) > max)
+	  max = std::norm(trans_im(it));
+
+      const unsigned nrows = trans_im.nrows();
+      const unsigned ncols = trans_im.ncols();
+      image2d<R> new_im(trans_im.domain());
+      if (ordered)
+	for (unsigned row = 0; row < new_im.nrows(); ++row)
+	  for (unsigned col = 0; col < new_im.ncols(); ++col)
+	    if (std::norm(opt::at(trans_im,
+				  (row + nrows / 2) % nrows,
+				  (col + ncols / 2) % ncols))
+		>= max * clip)
+	      opt::at(new_im, row, col) = mln_max(R);
+	    else
+	      opt::at(new_im, row, col) =
+		(double) mln_max(R) *
+		std::norm(opt::at(trans_im,
+				  (row + nrows / 2) % nrows,
+				  (col + ncols / 2) % ncols)) / (max * clip);
+      else
+	for_all(it)
+	  if (std::norm(trans_im(it)) >= max * clip)
+	    new_im(it) = mln_max(R);
+	  else
+	    new_im(it) =
+	      (double) mln_max(R) * std::norm(trans_im(it)) / (max * clip);
+      return new_im;
+    }
+
+    template <class T>
+    inline
+    image2d<T>
+    fft<T>::transformed_image_clipped_magn(double clip, bool ordered) const
+    {
+      return transformed_image_clipped_magn<T>(clip, ordered);
+    }
+
+    template <class T>
+    template <class R>
+    inline
+    image2d<R>
+    fft<T>::transformed_image_clipped_magn(bool ordered) const
+    {
+      return transformed_image_clipped_magn<R>(1, ordered);
+    }
+
+    template <class T>
+    inline
+    image2d<T>
+    fft<T>::transformed_image_clipped_magn(bool ordered) const
+    {
+      return transformed_image_clipped_magn<T>(1, ordered);
+    }
+
+    template <class T>
+    template <class R>
+    inline
+    image2d<R>
+    fft<T>::transformed_image_log_magn(double a, double b, bool ordered) const
+    {
+      /* FIXME: Find a more elegant way to fix range interval on a
+	 and b (Note from Roland: what does it mean?).  */
+
+      // FIXME: Ensure R is real.
+      // FIXME: Ensure 0 <= a <= 1000.
+      // FIXME: Ensure 0 <= b <= 1000.
+
+      image2d<R> new_im(trans_im.domain());
+
+      double max = mln_min(double);
+      mln_piter(image2d<R>) it(trans_im.domain());
+      for_all(it)
+	if (std::norm(trans_im(it)) > max)
+	  max = std::norm(trans_im(it));
+
+      const unsigned nrows = trans_im.nrows();
+      const unsigned ncols = trans_im.ncols();
+      if (ordered)
+	for (unsigned row = 0; row < new_im.nrows(); ++row)
+	  for (unsigned col = 0; col < new_im.ncols(); ++col)
+	    opt::at(new_im, row, col) =
+	      log(a + b * std::norm(opt::at(trans_im,
+					    (row + nrows / 2) % nrows,
+					    (col + ncols / 2) % ncols)))
+	      / log (a + b * max) * mln_max(R);
+      else
+	{
+	  mln_piter(image2d< std::complex<T> >) it(trans_im.domain());
+	  for_all(it)
+	    new_im(it) =
+	    log(a + b * std::norm(trans_im(it)))
+	    / log (a + b * max) * mln_max(R);
+	}
+      return new_im;
+    }
+
+    template <class T>
+    inline
+    image2d<T>
+    fft<T>::transformed_image_log_magn(double a, double b, bool ordered) const
+    {
+      return transformed_image_log_magn<T>(a, b, ordered);
+    }
+
+    template <class T>
+    template <class R>
+    inline
+    image2d<R>
+    fft<T>::transformed_image_log_magn(bool ordered) const
+    {
+      return transformed_image_log_magn<R>(1, 100, ordered);
+    }
+
+    template <class T>
+    inline
+    image2d<T>
+    fft<T>::transformed_image_log_magn(bool ordered) const
+    {
+      return transformed_image_log_magn<T>(1, 100, ordered);
+    }
+
+    template <class T>
+    inline
+    fft<T>::~fft()
+    {
+      fftw_free(in);
+      fftw_free(out);
+      fftw_destroy_plan(p);
+      fftw_destroy_plan(p_inv);
+    }
+
+  } // end of namespace mln::internal
+
+
+  namespace transform
+  {
+
+    /*--------------------------------------------------------.
+    | FFT engine (specialization for images of real values).  |
+    `--------------------------------------------------------*/
+
+    template <class T>
+    template <typename D>
+    inline
+    fft<T, internal::fft_real>::fft(const image2d<D>& original_im)
+    {
+      const unsigned nrows = original_im.nrows();
+      const unsigned ncols = original_im.ncols();
+
+      this->in  = (T*) fftw_malloc(nrows * ncols * sizeof(T));
+      this->out = (std::complex<T>*) fftw_malloc(nrows
+						 * (ncols / 2 + 1)
+						 * sizeof(std::complex<T>));
+
+      for (unsigned row = 0; row < nrows; ++row)
+	for (unsigned col = 0; col < ncols; ++col)
+	  this->in[row * ncols + col] =
+	    opt::at(original_im, row, col);
+
+      this->p =
+	fftw_plan_dft_r2c_2d (nrows, ncols,
+			      this->in,
+			      reinterpret_cast<fftw_complex*>(this->out),
+			      FFTW_ESTIMATE);
+      this->p_inv =
+	fftw_plan_dft_c2r_2d (nrows, ncols,
+			      reinterpret_cast<fftw_complex*>(this->out),
+			      this->in,
+			      FFTW_ESTIMATE);
+      this->trans_im = image2d< std::complex<T> >(original_im.domain());
+    }
+
+    template <class T>
+    inline
+    image2d< std::complex<T> >
+    fft<T, internal::fft_real>::transform()
+    {
+      fftw_execute(this->p);
+
+      const unsigned nrows = this->trans_im.nrows();
+      const unsigned ncols = this->trans_im.ncols();
+      unsigned denom = nrows * ncols;
+      int i = 0;
+      for (unsigned row = 0; row < nrows; ++row)
+	for (unsigned col = 0; col <= ncols / 2; ++col)
+	  {
+	    this->out[i] = std::complex<T> (this->out[i].real() / denom,
+					    this->out[i].imag() / denom);
+	    opt::at(this->trans_im, row, col) = this->out[i];
+	    ++i;
+	  }
+      for (unsigned row = 0; row < nrows; ++row)
+	for (unsigned col = ncols - 1; col > ncols / 2; --col)
+	  opt::at(this->trans_im, row, col) =
+	    opt::at(this->trans_im, nrows - row - 1, ncols - col - 1);
+      return this->trans_im;
+    }
+
+    template <class T>
+    template <class R>
+    inline
+    image2d<R>
+    fft<T, internal::fft_real>::transform_inv()
+    {
+      // FIXME: Ensure R is real.
+
+      const unsigned nrows = this->trans_im.nrows();
+      const unsigned ncols = this->trans_im.ncols();
+      for (unsigned row = 0; row < nrows; ++row)
+	for (unsigned col = 0; col <= ncols / 2; ++col)
+	  this->out[row * (ncols / 2 + 1) + col] =
+	    opt::at(this->trans_im, row, col);
+
+      fftw_execute(this->p_inv);
+
+      image2d<R> new_im(this->trans_im.domain());
+      int i = 0;
+      for (unsigned row = 0; row < nrows; ++row)
+	for (unsigned col = 0; col < ncols; ++col)
+	  {
+	    opt::at(new_im, row, col) = (this->in[i] >= mln_min(R)
+					 ? (this->in[i] <= mln_max(R)
+					    ? (R) this->in [i]
+					    : mln_min(R))
+					 : mln_max(R));
+	    ++i;
+	  }
+      return new_im;
+    }
+
+    template <class T>
+    inline
+    image2d<T>
+    fft<T, internal::fft_real>::transform_inv()
+    {
+      return transform_inv<T>();
+    }
+
+
+    /*-----------------------------------------------------------.
+    | FFT engine (specialization for images of complex values).  |
+    `-----------------------------------------------------------*/
+
+    template <class T>
+    inline
+    fft<T, internal::fft_cplx>::fft(const image2d< std::complex<T> >& original_im)
+    {
+      const unsigned nrows = original_im.nrows();
+      const unsigned ncols = original_im.ncols();
+
+      this->in  = fftw_malloc(sizeof(std::complex<T>) * nrows * ncols);
+      this->out = fftw_malloc(sizeof(std::complex<T>) * nrows * ncols);
+
+      for (unsigned row = 0; row < nrows; ++row)
+	for (unsigned col = 0; col < ncols; ++col)
+	  {
+	    this->in[row * ncols + col].re = original_im(row, col).real();
+	    this->in[row * ncols + col].im = original_im(row, col).imag();
+	  }
+
+      this->p     = fftw_plan_dft_2d(nrows, ncols, this->in, this->out,
+				     FFTW_FORWARD, FFTW_ESTIMATE);
+      this->p_inv = fftw_plan_dft_2d(nrows, ncols, this->out, this->in,
+				     FFTW_BACKWARD, FFTW_ESTIMATE);
+      this->trans_im = image2d< std::complex<T> >(original_im.domain());
+    }
+
+    template <class T>
+    inline
+    image2d< std::complex<T> >
+    fft<T, internal::fft_cplx>::transform()
+    {
+      fftw_execute(this->p);
+
+      const unsigned nrows = this->trans_im.nrows();
+      const unsigned ncols = this->trans_im.ncols();
+      unsigned denom = nrows * ncols;
+      int i = 0;
+      for (unsigned row = 0; row < nrows; ++row)
+	for (unsigned col = 0; col < ncols; ++col)
+	  {
+	    this->out[i].re /= denom;
+	    this->out[i].im /= denom;
+	    this->trans_im(row, col) =
+	      std::complex<double>(this->out[i].re, this->out[i].im);
+	    ++i;
+	  }
+      return this->trans_im;
+    }
+
+    template <class T>
+    template <class R>
+    inline
+    image2d< std::complex<R> >
+    fft<T, internal::fft_cplx>::transform_inv()
+    {
+      const unsigned nrows = this->trans_im.nrows();
+      const unsigned ncols = this->trans_im.ncols();
+      for (unsigned row = 0; row < nrows; ++row)
+	for (unsigned col = 0; col < ncols; ++col)
+	  {
+	    this->out[row * ncols + col].re =
+	      this->trans_im(row, col).real();
+	    this->out[row * ncols + col].im =
+	      this->trans_im(row, col).imag();
+	  }
+
+      fftw_execute(this->p_inv);
+
+      image2d< std::complex<R> > new_im(this->trans_im.size());
+      int i = 0;
+      for (unsigned row = 0; row < nrows; ++row)
+	for (unsigned col = 0; col < ncols; ++col)
+	  {
+	    new_im(row, col) = this->in[i];
+	    ++i;
+	  }
+      return new_im;
+    }
+
+    template <class T>
+    inline
+    image2d< std::complex<T> >
+    fft<T, internal::fft_cplx>::transform_inv()
+    {
+      return transform_inv<T>();
+    }
+
+  } // end of namespace mln::transform
+
+# endif // ! MLN_INCLUDE_ONLY
+
 } // end of namespace mln
 
 #endif // ! MLN_TRANSFORM_FFT_HH
-- 
1.7.10.4