smcs
smcs

Reputation: 2004

How to use the OpenCV CUDA Fourier Transform

Instead of OpenCV's normal dft, I'd like to use cuda::dft. As a start I tried performing a forward and inverse transform, but the result doesn't look anything like the input. Here's a minimal example using an OpenCV example image:

// Load 8bit test image (https://raw.githubusercontent.com/opencv/opencv/master/samples/data/basketball1.png)
Mat testImg;
testImg = imread("basketball1.png", CV_LOAD_IMAGE_GRAYSCALE);

// Convert input to complex float image
Mat_<float> imgReal;
testImg.convertTo(imgReal, CV_32F, 1.0/255.0);
Mat imgImag = Mat(imgReal.rows, imgReal.cols, CV_32F, float(0));
vector<Mat> channels;
channels.push_back(imgReal);
channels.push_back(imgImag);
Mat imgComplex;
merge(channels,imgComplex);

imshow("Img real", imgReal);
waitKey(0);

//Perform a Fourier transform
cuda::GpuMat imgGpu, fftGpu;
imgGpu.upload(imgComplex);

cuda::dft(imgGpu, fftGpu, imgGpu.size());

//Performs an inverse Fourier transform
cuda::GpuMat propGpu, convFftGpu;
cuda::dft(fftGpu, propGpu, imgGpu.size(), DFT_REAL_OUTPUT | DFT_SCALE);

Mat output(propGpu);
output.convertTo(output, CV_8U, 255, 0);

imshow("Output", output);
waitKey(0);

I played with the flags but output never looks anything like input. Using the above code I get as output:

enter image description here

While it should look like this:

enter image description here

Upvotes: 1

Views: 2174

Answers (2)

Mahdi
Mahdi

Reputation: 171

To go into Fourier domain using OpenCV Cuda FFT and back into the spatial domain, you can simply follow the below example (to learn more, you can refer to cufft documentation, on which OpenCV Cuda FFT source code is based).

Mat test_im;
test_im = imread("frame.png", IMREAD_GRAYSCALE);

// Convert input input to real value type (CV_64F for double precision)
Mat im_real;
test_im.convertTo(im_real, CV_32F, 1.0/255.0);

imshow("Input Image", im_real);
waitKey(0);

// Perform The Fourier Transform
cuda::GpuMat in_im_gpu, fft_im;
in_im_gpu.upload(im_real);
cuda::dft(in_im_gpu, fft_im, in_im_gpu.size(), 0);

// Performs an inverse Fourier transform
cuda::GpuMat ifft_im_gpu;
//! int odd_size = imgGpu.size().width % 2;
//! cv::Size dest_size((fftGpu.size().width-1)*2 + (odd_size ? 1 : 0), fftGpu.size().height);
cv::Size dest_size = in_im_gpu.size();
int flag = (DFT_SCALE + DFT_REAL_OUTPUT) | DFT_INVERSE;
cuda::dft(fft_im, ifft_im_gpu, dest_size, flag);

Mat ifft_im(ifft_im_gpu);
ifft_im.convertTo(ifft_im, CV_8U, 255, 0);

imshow("Inverse FFT", ifft_im);
waitKey(0);

Upvotes: 0

smcs
smcs

Reputation: 2004

I found the answer here. Apparently, when starting with a complex input image, it's not possible to use the flag DFT_REAL_OUTPUT.

Either you do the forward transform with a one channel float input and then you get the same as an output from the inverse transform, or you start with a two channel complex input image and get that type as output. The upside to using a complex input image is that the forward transform delivers the full sized complex field to work with, e.g. perform a convolution (see linked answer for details).

I'll add that in order to obtain an 8bit image from the inverse transform, compute the magnitude yourself like so:

Mat output(propGpu);
Mat planes[2];
split(output,planes);
Mat mag;
magnitude(planes[0],planes[1],mag);
mag.convertTo(mag, CV_8U, 255, 0);

Upvotes: 2

Related Questions