#ifndef DLIB_MKL_FFT_H #define DLIB_MKL_FFT_H #include <type_traits> #include <mkl_dfti.h> #include "fft_size.h" #define DLIB_DFTI_CHECK_STATUS(s) \ if((s) != 0 && !DftiErrorClass((s), DFTI_NO_ERROR)) \ { \ throw dlib::error(DftiErrorMessage((s))); \ } namespace dlib { template<typename T> void mkl_fft(const fft_size& dims, const std::complex<T>* in, std::complex<T>* out, bool is_inverse) /*! requires - T must be either float or double - dims represents the dimensions of both `in` and `out` - dims.num_dims() > 0 - dims.num_dims() < 3 ensures - performs an FFT on `in` and stores the result in `out`. - if `is_inverse` is true, a backward FFT is performed, otherwise a forward FFT is performed. !*/ { static_assert(std::is_floating_point<T>::value, "template parameter needs to be a floatint point type"); DLIB_ASSERT(dims.num_dims() > 0, "dims can't be empty"); DLIB_ASSERT(dims.num_dims() < 3, "we currently only support up to 2D FFT. Please submit an issue on github if 3D or above is required."); constexpr DFTI_CONFIG_VALUE dfti_type = std::is_same<T,float>::value ? DFTI_SINGLE : DFTI_DOUBLE; DFTI_DESCRIPTOR_HANDLE h; MKL_LONG status; if (dims.num_dims() == 1) { status = DftiCreateDescriptor(&h, dfti_type, DFTI_COMPLEX, 1, dims[0]); DLIB_DFTI_CHECK_STATUS(status); } else { MKL_LONG size[] = {dims[0], dims[1]}; status = DftiCreateDescriptor(&h, dfti_type, DFTI_COMPLEX, 2, size); DLIB_DFTI_CHECK_STATUS(status); MKL_LONG strides[3]; strides[0] = 0; strides[1] = size[1]; strides[2] = 1; status = DftiSetValue(h, DFTI_INPUT_STRIDES, strides); DLIB_DFTI_CHECK_STATUS(status); status = DftiSetValue(h, DFTI_OUTPUT_STRIDES, strides); DLIB_DFTI_CHECK_STATUS(status); } const DFTI_CONFIG_VALUE inplacefft = in == out ? DFTI_INPLACE : DFTI_NOT_INPLACE; status = DftiSetValue(h, DFTI_PLACEMENT, inplacefft); DLIB_DFTI_CHECK_STATUS(status); // Unless we use sequential mode, the fft results are not correct. status = DftiSetValue(h, DFTI_THREAD_LIMIT, 1); DLIB_DFTI_CHECK_STATUS(status); status = DftiCommitDescriptor(h); DLIB_DFTI_CHECK_STATUS(status); if (is_inverse) status = DftiComputeBackward(h, (void*)in, (void*)out); else status = DftiComputeForward(h, (void*)in, (void*)out); DLIB_DFTI_CHECK_STATUS(status); status = DftiFreeDescriptor(&h); DLIB_DFTI_CHECK_STATUS(status); } /* * in has dims[0] * dims[1] * ... * dims[-2] * dims[-1] points * out has dims[0] * dims[1] * ... * dims[-2] * (dims[-1]/2+1) points */ template<typename T> void mkl_fftr(const fft_size& dims, const T* in, std::complex<T>* out) /*! requires - T must be either float or double - dims represent the dimensions of `in` - `out` has dimensions {dims[0], dims[1], ..., dims[-2], dims[-1]/2+1} - dims.num_dims() > 0 - dims.num_dims() <= 3 - dims.back() must be even ensures - performs a real FFT on `in` and stores the result in `out`. !*/ { static_assert(std::is_floating_point<T>::value, "template parameter needs to be a floatint point type"); DLIB_ASSERT(dims.num_dims() > 0, "dims can't be empty"); DLIB_ASSERT(dims.num_dims() < 3, "we currently only support up to 2D FFT. Please submit an issue on github if 3D or above is required."); DLIB_ASSERT(dims.back() % 2 == 0, "last dimension needs to be even"); constexpr DFTI_CONFIG_VALUE dfti_type = std::is_same<T,float>::value ? DFTI_SINGLE : DFTI_DOUBLE; DFTI_DESCRIPTOR_HANDLE h; MKL_LONG status; if (dims.num_dims() == 1) { status = DftiCreateDescriptor(&h, dfti_type, DFTI_REAL, 1, dims[0]); DLIB_DFTI_CHECK_STATUS(status); } else { const long lastdim = dims[1]/2+1; MKL_LONG size[] = {dims[0], dims[1]}; status = DftiCreateDescriptor(&h, dfti_type, DFTI_REAL, 2, size); DLIB_DFTI_CHECK_STATUS(status); { MKL_LONG strides[3]; strides[0] = 0; strides[1] = size[1]; strides[2] = 1; status = DftiSetValue(h, DFTI_INPUT_STRIDES, strides); DLIB_DFTI_CHECK_STATUS(status); } { MKL_LONG strides[3]; strides[0] = 0; strides[1] = lastdim; strides[2] = 1; status = DftiSetValue(h, DFTI_OUTPUT_STRIDES, strides); DLIB_DFTI_CHECK_STATUS(status); } } const DFTI_CONFIG_VALUE inplacefft = (void*)in == (void*)out ? DFTI_INPLACE : DFTI_NOT_INPLACE; status = DftiSetValue(h, DFTI_PLACEMENT, inplacefft); DLIB_DFTI_CHECK_STATUS(status); status = DftiSetValue(h, DFTI_CONJUGATE_EVEN_STORAGE, DFTI_COMPLEX_COMPLEX); DLIB_DFTI_CHECK_STATUS(status); // Unless we use sequential mode, the fft results are not correct. status = DftiSetValue(h, DFTI_THREAD_LIMIT, 1); DLIB_DFTI_CHECK_STATUS(status); status = DftiCommitDescriptor(h); DLIB_DFTI_CHECK_STATUS(status); status = DftiComputeForward(h, (void*)in, (void*)out); DLIB_DFTI_CHECK_STATUS(status); status = DftiFreeDescriptor(&h); DLIB_DFTI_CHECK_STATUS(status); } /* * in has dims[0] * dims[1] * ... * dims[-2] * (dims[-1]/2+1) points * out has dims[0] * dims[1] * ... * dims[-2] * dims[-1] points */ template<typename T> void mkl_ifftr(const fft_size& dims, const std::complex<T>* in, T* out) /*! requires - T must be either float or double - dims represent the dimensions of `out` - `in` has dimensions {dims[0], dims[1], ..., dims[-2], dims[-1]/2+1} - dims.num_dims() > 0 - dims.num_dims() <= 3 - dims.back() must be even ensures - performs an inverse real FFT on `in` and stores the result in `out`. !*/ { static_assert(std::is_floating_point<T>::value, "template parameter needs to be a floatint point type"); DLIB_ASSERT(dims.num_dims() > 0, "dims can't be empty"); DLIB_ASSERT(dims.num_dims() < 3, "we currently only support up to 2D FFT. Please submit an issue on github if 3D or above is required."); DLIB_ASSERT(dims.back() % 2 == 0, "last dimension needs to be even"); constexpr DFTI_CONFIG_VALUE dfti_type = std::is_same<T,float>::value ? DFTI_SINGLE : DFTI_DOUBLE; DFTI_DESCRIPTOR_HANDLE h; MKL_LONG status; if (dims.num_dims() == 1) { status = DftiCreateDescriptor(&h, dfti_type, DFTI_REAL, 1, dims[0]); DLIB_DFTI_CHECK_STATUS(status); } else { const long lastdim = dims[1]/2+1; MKL_LONG size[] = {dims[0], dims[1]}; status = DftiCreateDescriptor(&h, dfti_type, DFTI_REAL, 2, size); DLIB_DFTI_CHECK_STATUS(status); { MKL_LONG strides[3]; strides[0] = 0; strides[1] = lastdim; strides[2] = 1; status = DftiSetValue(h, DFTI_INPUT_STRIDES, strides); DLIB_DFTI_CHECK_STATUS(status); } { MKL_LONG strides[3]; strides[0] = 0; strides[1] = dims[1]; strides[2] = 1; status = DftiSetValue(h, DFTI_OUTPUT_STRIDES, strides); DLIB_DFTI_CHECK_STATUS(status); } } const DFTI_CONFIG_VALUE inplacefft = (void*)in == (void*)out ? DFTI_INPLACE : DFTI_NOT_INPLACE; status = DftiSetValue(h, DFTI_PLACEMENT, inplacefft); DLIB_DFTI_CHECK_STATUS(status); status = DftiSetValue(h, DFTI_CONJUGATE_EVEN_STORAGE, DFTI_COMPLEX_COMPLEX); DLIB_DFTI_CHECK_STATUS(status); // Unless we use sequential mode, the fft results are not correct. status = DftiSetValue(h, DFTI_THREAD_LIMIT, 1); DLIB_DFTI_CHECK_STATUS(status); status = DftiCommitDescriptor(h); DLIB_DFTI_CHECK_STATUS(status); status = DftiComputeBackward(h, (void*)in, (void*)out); DLIB_DFTI_CHECK_STATUS(status); status = DftiFreeDescriptor(&h); DLIB_DFTI_CHECK_STATUS(status); } } #endif // DLIB_MKL_FFT_H