2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU Lesser General Public License as
4 * published by the Free Software Foundation, either version 3 of the
5 * License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * @author Roland Denis (\c roland.denis@univ-smb.fr )
20 * LAboratory of MAthematics - LAMA (CNRS, UMR 5127), University of Savoie, France
24 * Implementation of inline methods defined in RealFFT.h
26 * This file is part of the DGtal library.
30 //////////////////////////////////////////////////////////////////////////////
32 #include <stdexcept> // Exceptions
33 #include <new> // std::bad_alloc exception
36 //////////////////////////////////////////////////////////////////////////////
38 ///////////////////////////////////////////////////////////////////////////////
39 // IMPLEMENTATION of inline methods.
40 ///////////////////////////////////////////////////////////////////////////////
42 ///////////////////////////////////////////////////////////////////////////////
43 // ----------------------- Standard services ------------------------------
46 ///////////////////////////////////////////////////////////////////////////////
47 // Interface - public :
49 // Constructor from a domain and scaled lower bound and extent.
50 template <typename TSpace, typename T>
52 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
53 RealFFT( Domain const& aDomain,
54 RealPoint const& aLowerBound,
55 RealPoint const& anExtent
57 : mySpatialDomain{ aDomain }
58 , mySpatialExtent{ mySpatialDomain.upperBound() - mySpatialDomain.lowerBound() + Point::diagonal(1) }
59 , myFreqExtent{ mySpatialExtent / (Point::diagonal(1) + Point::base(0)) + Point::base(0) }
60 , myFreqDomain{ Point::diagonal(0), myFreqExtent - Point::diagonal(1) }
61 , myFullSpatialDomain{ mySpatialDomain.lowerBound(), mySpatialDomain.lowerBound() + myFreqExtent + Point::base(0, myFreqExtent[0]) - Point::diagonal(1) }
62 , myStorage( FFTW::malloc( sizeof(Complex) * myFreqDomain.size() ) )
64 if ( myStorage == nullptr ) throw std::bad_alloc{};
66 setScaledSpatialLowerBound( aLowerBound );
67 setScaledSpatialExtent( anExtent );
70 // Constructor from a domain.
71 template <typename TSpace, typename T>
73 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
74 RealFFT( Domain const& aDomain )
76 , aDomain.lowerBound()
77 , aDomain.upperBound() - aDomain.lowerBound() + Point::diagonal(1)
83 template <typename TSpace, typename T>
85 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
88 FFTW::free( myStorage );
91 // Padding when using real datas.
92 template <typename TSpace, typename T>
95 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
96 getPadding() const noexcept
98 return 2*myFreqExtent[0] - mySpatialExtent[0];
101 // Gets mutable spatial storage.
102 template <typename TSpace, typename T>
104 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Real*
105 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
106 getSpatialStorage() noexcept
108 return reinterpret_cast<Real*>(myStorage);
111 // Gets non-mutable spatial storage.
112 template <typename TSpace, typename T>
114 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Real const*
115 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
116 getSpatialStorage() const noexcept
120 // Gets mutable spatial image.
121 template <typename TSpace, typename T>
123 DGtal::ArrayImageAdapter<
124 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Real*,
125 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Domain
127 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
128 getSpatialImage() noexcept
130 return { getSpatialStorage(), myFullSpatialDomain, mySpatialDomain };
133 // Gets non-mutable spatial image.
134 template <typename TSpace, typename T>
136 DGtal::ArrayImageAdapter<
137 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Real const*,
138 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Domain
140 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
141 getSpatialImage() const noexcept
143 return { getSpatialStorage(), myFullSpatialDomain, mySpatialDomain };
146 // Gets mutable frequential raw storage.
147 template <typename TSpace, typename T>
149 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Complex*
150 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
151 getFreqStorage() noexcept
153 return reinterpret_cast<Complex*>(myStorage);
156 // Gets non-mutable frequential storage.
157 template <typename TSpace, typename T>
159 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Complex const*
160 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
161 getFreqStorage() const noexcept
163 return reinterpret_cast<Complex const*>(myStorage);
166 // Gets mutable frequential image.
167 template <typename TSpace, typename T>
169 DGtal::ArrayImageAdapter<
170 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Complex*,
171 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Domain
173 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
174 getFreqImage() noexcept
176 return { getFreqStorage(), getFreqDomain() };
179 // Get non-mutable frequential image.
180 template <typename TSpace, typename T>
182 DGtal::ArrayImageAdapter<
183 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Complex const*,
184 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Domain
186 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
187 getFreqImage() const noexcept
189 return { getFreqStorage(), getFreqDomain() };
192 // Get spatial domain.
193 template <typename TSpace, typename T>
195 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Domain const&
196 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
197 getSpatialDomain() const noexcept
199 return mySpatialDomain;
202 // Get frequential domain.
203 template <typename TSpace, typename T>
205 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Domain const&
206 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
207 getFreqDomain() const noexcept
212 // Get spatial domain extent.
213 template <typename TSpace, typename T>
215 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Point const&
216 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
217 getSpatialExtent() const noexcept
219 return mySpatialExtent;
222 // Get frequential domain extent.
223 template <typename TSpace, typename T>
225 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Point const&
226 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
227 getFreqExtent() const noexcept
232 // Gets the extent of the scaled spatial domain.
233 template <typename TSpace, typename T>
235 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::RealPoint
236 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
237 getScaledSpatialExtent() const noexcept
239 return myScaledSpatialExtent;
242 // Sets the extent of the scaled spatial domain.
243 template <typename TSpace, typename T>
246 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
247 setScaledSpatialExtent( RealPoint const& anExtent ) noexcept
249 myScaledSpatialExtent = anExtent;
251 myScaledFreqMag = 1.;
252 for ( Dimension i = 0; i < dimension; ++i )
253 myScaledFreqMag *= mySpatialExtent[ i ] / myScaledSpatialExtent[ i ];
256 // Gets the lower bound of the scaled spatial domain.
257 template <typename TSpace, typename T>
259 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::RealPoint
260 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
261 getScaledSpatialLowerBound() const noexcept
263 return myScaledSpatialLowerBound;
266 // Sets the lower bound of the scaled spatial domain.
267 template <typename TSpace, typename T>
270 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
271 setScaledSpatialLowerBound( RealPoint const& aPoint ) noexcept
273 myScaledSpatialLowerBound = aPoint;
276 // Converts coordinates from the spatial image into scaled coordinates.
277 template <typename TSpace, typename T>
279 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::RealPoint
280 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
281 calcScaledSpatialCoords( Point const& aPoint ) const noexcept
283 RealPoint scaledCoords;
284 for ( Dimension i = 0; i < dimension; ++i )
285 scaledCoords[ i ] = myScaledSpatialLowerBound[ i ]
286 + ( myScaledSpatialExtent[ i ] / mySpatialExtent[ i ] )
287 * ( aPoint[ i ] - mySpatialDomain.lowerBound()[ i ] );
292 // Converts scaled coordinates from the spatial image into native spatial coords.
293 template <typename TSpace, typename T>
295 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Point
296 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
297 calcNativeSpatialCoords( RealPoint const& aScaledPoint ) const noexcept
300 for ( Dimension i = 0; i < dimension; ++i )
301 nativeCoords[ i ] = std::round(
302 mySpatialDomain.lowerBound()[ i ]
303 + ( mySpatialExtent[ i ] / myScaledSpatialExtent[ i ] )
304 * ( aScaledPoint[ i ] - myScaledSpatialLowerBound[ i ] )
310 // Converts coordinates from the frequency image into scaled frequencies.
311 template <typename TSpace, typename T>
313 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::RealPoint
314 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
315 calcScaledFreqCoords( Point const& aPoint ) const noexcept
317 RealPoint frequencies;
318 for ( Dimension i = 0; i < dimension; ++i )
320 if ( aPoint[ i ] <= mySpatialExtent[ i ] / 2 )
321 frequencies[ i ] = aPoint[ i ] / myScaledSpatialExtent[ i ];
323 frequencies[ i ] = ( aPoint[ i ] - mySpatialExtent[ i ] ) / myScaledSpatialExtent[ i ];
329 // From the scaled frequency coordinates, calculates the nearest corresponding
330 // coordinates in the native frequency image.
331 template <typename TSpace, typename T>
333 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Point
334 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
335 calcNativeFreqCoords( RealPoint const& aScaledPoint, bool & applyConj ) const noexcept
338 for ( Dimension i = 0; i < dimension; ++i )
340 nativeCoords[ i ] = std::round( aScaledPoint[ i ] * myScaledSpatialExtent[ i ] );
342 if ( aScaledPoint[ i ] < 0 )
343 nativeCoords[ i ] += mySpatialExtent[ i ];
346 // Checking if conjugate is needed.
347 if ( nativeCoords[ 0 ] > mySpatialExtent[ 0 ] / 2 )
349 for ( Dimension i = 0; i < dimension; ++i )
350 if ( nativeCoords[ i ] > 0 )
351 nativeCoords[ i ] = mySpatialExtent[ i ] - nativeCoords[ i ];
362 // Converts a complex value from the frequency image to the scaled frequency image.
363 template <typename TSpace, typename T>
365 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Complex
366 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
367 calcScaledFreqValue( RealPoint const& aScaledPoint, Complex const& aValue ) const noexcept
370 for ( Dimension i = 0; i < dimension; ++i )
371 phase -= Real( myScaledSpatialLowerBound[ i ] ) * aScaledPoint[ i ];
375 return aValue * std::polar( myScaledFreqMag, phase );
378 // From a complex value of the scaled frequency image, calculates the
379 // corresponding value (undoing scaling and rotation) in the native
381 template <typename TSpace, typename T>
383 typename DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::Complex
384 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
385 calcNativeFreqValue( RealPoint const& aScaledPoint, Complex const& aScaledValue ) const noexcept
388 for ( Dimension i = 0; i < dimension; ++i )
389 phase += Real( myScaledSpatialLowerBound[ i ] ) * aScaledPoint[ i ];
393 return aScaledValue * std::polar( Real(1)/myScaledFreqMag, phase );
396 // Creates a transformation plan for the specified transformation direction.
397 template <typename TSpace, typename T>
400 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
401 createPlan( unsigned flags, int way )
403 // Transform dimensions
405 for (size_t i = 0; i < dimension; ++i)
406 n[dimension-i-1] = mySpatialExtent[i];
408 // Creates the plan for this transformation
409 typename FFTW::plan p = FFTW::plan_dft( dimension, n, getSpatialStorage(), getFreqStorage(), way, flags );
412 FFTW::destroy_plan( p );
415 // Fast Fourier Transformation.
416 template <typename TSpace, typename T>
419 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
420 doFFT( unsigned flags, int way, bool normalized )
422 typename FFTW::plan p;
424 // Transform dimensions
426 for (size_t i = 0; i < dimension; ++i)
427 n[dimension-i-1] = mySpatialExtent[i];
429 // Creates the plan for this transformation
430 // Only FFTW_ESTIMATE flag preserves input.
431 if ( flags & FFTW_ESTIMATE )
433 p = FFTW::plan_dft( dimension, n, getSpatialStorage(), getFreqStorage(), way, FFTW_ESTIMATE );
437 // Strategy to preserve input datas while creating DFT plan:
438 // - Firstly, checks if a plan already exists for this dimensions.
439 p = FFTW::plan_dft( dimension, n, getSpatialStorage(), getFreqStorage(), way, FFTW_WISDOM_ONLY | flags );
441 // - Otherwise, create fake input to create the plan.
444 void* tmp = FFTW::malloc( sizeof(Complex) * myFreqDomain.size() );
445 if ( tmp == nullptr ) throw std::bad_alloc{};
446 p = FFTW::plan_dft( dimension, n, reinterpret_cast<Real*>(tmp), reinterpret_cast<Complex*>(tmp), way, flags );
451 // We must have a valid plan now ...
452 if ( p == NULL ) throw std::runtime_error("No valid DFT plan founded.");
455 FFTW::execute_dft( p, getSpatialStorage(), getFreqStorage(), way );
458 FFTW::destroy_plan( p );
461 if ( way == FFTW_BACKWARD && normalized )
463 const std::size_t N = getSpatialDomain().size();
465 for ( auto & v : getSpatialImage() )
470 // In-place forward FFT transformation (spatial -> frequential)
471 template <typename TSpace, typename T>
474 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
475 forwardFFT( unsigned flags )
477 doFFT( flags, FFTW_FORWARD, false );
480 // In-place backward FFT transformation (frequential -> spatial)
481 template <typename TSpace, typename T>
484 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::
485 backwardFFT( unsigned flags, bool normalized )
487 doFFT( flags, FFTW_BACKWARD, normalized );
491 * Writes/Displays the object on an output stream.
492 * @param out the output stream where the object is written.
494 template <typename TSpace, typename T>
497 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::selfDisplay ( std::ostream & out ) const
503 * Checks the validity/consistency of the object.
504 * @return 'true' if the object is valid, 'false' otherwise.
506 template <typename TSpace, typename T>
509 DGtal::RealFFT<DGtal::HyperRectDomain<TSpace>, T>::isValid() const noexcept
511 return myStorage != nullptr;
516 ///////////////////////////////////////////////////////////////////////////////
517 // Implementation of inline functions //
525 DGtal::operator<< ( std::ostream & out,
526 const RealFFT<TDomain, T> & object )
528 object.selfDisplay( out );
533 ///////////////////////////////////////////////////////////////////////////////