Basically I am trying to implement a very basic version of the Wiener filter on a grey scale image, using the a stripped down Wiener equation: (1/(SNR))*DFT(Image) after which I take the IDFT of the whole thing. My problem is that my output image which is supposed to be filtered looks exactly like the input, and therefore it seems that the pixel values aren't changing at all. Can anyone please indicate to me where I am going wrong? Here's the code I am currently using:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv/cv.hpp"
#include "opencv/cxcore.hpp"
#include <iostream>
using namespace cv;
using namespace std;
void updateMag(Mat complex);
Mat updateResult(Mat complex);
Mat computeDFT(Mat image);
Mat DFT2(Mat I);
void shift(Mat magI);
int kernel_size = 0;
int main( int argc, char** argv )
{
Mat result;
String file;
file = " << SAMPLE FILE >>";
Mat image = imread("/Users/John/Desktop/house.png", CV_LOAD_IMAGE_GRAYSCALE);
namedWindow( "Orginal window", CV_WINDOW_AUTOSIZE );// Create a window for display.
imshow( "Orginal window", image ); // Show our image inside it.
float x = 1/0.001;
Mat complex = computeDFT(image); // DFT of image
updateMag(complex); // compute magnitude of complex, switch to logarithmic scale and display...
Mat fourierImage(complex.size(), complex.type());
fourierImage = cv::Scalar::all(x);
//cout<< "Fourier = " << endl << fourierImage << endl;
//Mat complexFourier = computeDFT(fourierImage);
//cout << "1" << endl << complexFourier.type() << endl << complexFourier.type() << endl;
//complex = complex.mul(fourierImage);
//mulSpectrums(complex, fourierImage, complex, DFT_ROWS);
complex = complex.mul(x);
result = updateResult(complex); // do inverse transform and display the result image
waitKey(0);
return 0;
}
Mat updateResult(Mat complex)
{
Mat work;
//work.convertTo(work, CV_32F);
idft(complex, work);
//dft(complex, work, DFT_INVERSE + DFT_SCALE);
Mat planes[] = {Mat::zeros(complex.size(), complex.type()), Mat::zeros(complex.size(), complex.type())};
split(work, planes); // planes[0] = Re(DFT(I)), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], work); // === sqrt(Re(DFT(I))^2 + Im(DFT(I))^2)
normalize(work, work, 1, 0, NORM_MINMAX);
imshow("result", work);
return work;
}
void updateMag(Mat complex )
{
Mat magI;
Mat planes[] = {Mat::zeros(complex.size(), CV_32F), Mat::zeros(complex.size(), CV_32F)};
split(complex, planes); // planes[0] = Re(DFT(I)), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], magI); // sqrt(Re(DFT(I))^2 + Im(DFT(I))^2)
// switch to logarithmic scale: log(1 + magnitude)
magI += Scalar::all(1);
log(magI, magI);
shift(magI);
normalize(magI, magI, 1, 0, NORM_INF); // Transform the matrix with float values into a
// viewable image form (float between values 0 and 1).
imshow("spectrum", magI);
}
Mat computeDFT(Mat image) {
Mat padded; //expand input image to optimal size
int m = getOptimalDFTSize( image.rows );
int n = getOptimalDFTSize( image.cols ); // on the border add zero values
copyMakeBorder(image, padded, 0, m - image.rows, 0, n - image.cols, BORDER_CONSTANT, Scalar::all(0));
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complex;
merge(planes, 2, complex); // Add to the expanded another plane with zeros
dft(complex, complex, DFT_COMPLEX_OUTPUT); // furier transform
return complex;
}
void shift(Mat magI) {
// crop if it has an odd number of rows or columns
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
int cx = magI.cols/2;
int cy = magI.rows/2;
Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right
Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left
Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
Mat tmp; // swap quadrants (Top-Left with Bottom-Right)
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
q2.copyTo(q1);
tmp.copyTo(q2);
}
Mat DFT2(Mat I)
{
Mat padded; //expand input image to optimal size
int m = getOptimalDFTSize( I.rows );
int n = getOptimalDFTSize( I.cols ); // on the border add zero values
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexI;
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
dft(complexI, complexI); // this way the result may fit in the source matrix
// compute the magnitude and switch to logarithmic scale
// => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magI = planes[0];
magI += Scalar::all(1); // switch to logarithmic scale
log(magI, magI);
// crop the spectrum, if it has an odd number of rows or columns
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
// rearrange the quadrants of Fourier image so that the origin is at the image center
int cx = magI.cols/2;
int cy = magI.rows/2;
Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right
Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left
Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
Mat tmp; // swap quadrants (Top-Left with Bottom-Right)
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
q2.copyTo(q1);
tmp.copyTo(q2);
normalize(magI, magI, 0, 1, CV_MINMAX); // Transform the matrix with float values into a
// viewable image form (float between values 0 and 1).
return complexI;
}
Related
I want to do a cross correlation of 2 shifted images. In general I would do it like this:
- Load the 2 images
- make an dft with this 2 images
- multiply this images with each other with mulSpectrum (opencv)
- make an inverse dft of the result of the multipliation
- show the result--in the result image there must be a shift of the frequency, which is the shift of the real images.
I have done this with openCV:
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace std;
using namespace cv;
void fft_shift(Mat &I, Mat &magI) //shift the origin to the center of the image (taken from OpenCV example of dft)
{
Mat padded; //expand input image to optimal size
int m = getOptimalDFTSize(I.rows);
int n = getOptimalDFTSize(I.cols); // on the border add zero values
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
dft(complexI, complexI); // this way the result may fit in the source matrix
// compute the magnitude and switch to logarithmic scale
// => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
magI = planes[0];
magI += Scalar::all(1); // switch to logarithmic scale
log(magI, magI);
// crop the spectrum, if it has an odd number of rows or columns
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
// rearrange the quadrants of Fourier image so that the origin is at the image center
int cx = magI.cols / 2;
int cy = magI.rows / 2;
Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right
Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left
Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
Mat tmp; // swap quadrants (Top-Left with Bottom-Right)
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
q2.copyTo(q1);
tmp.copyTo(q2);
}
int main()
{
//load images and convert them to greyscale
Mat I = imread("original_Image.png");
cv::cvtColor(I, I, CV_BGR2GRAY);
Mat II = imread("shifted_Image.png");
cv::cvtColor(II, II, CV_BGR2GRAY);
if (I.empty())
return -1;
// call the fft_shift function and multiply this to spectrum
Mat mag1, mag1_shift, mag3,mag4;
fft_shift(I,mag1);
fft_shift(II, mag1_shift);
mulSpectrums(mag1, mag1_shift,mag3, 0, 1);
//perform an inverse dft and shift it, then normalize is for displaying
cv::dft(mag3, mag3, cv::DFT_INVERSE | cv::DFT_REAL_OUTPUT);
fft_shift(mag3, mag4);
normalize(mag4, mag4, 0, 1, CV_MINMAX);
imshow("spectrum shift", mag4);
waitKey();
return 0;
}
Here is the result of this calculations: result
And here is the result I expected: expected result
this result was taken out of a python programm from: http://scikit-image.org/docs/0.11.x/auto_examples/plot_register_translation.html I try to translate this code to C++, which is the code above, but it is not working. Does anybody know, what I´m doing wrong here?
I have found a solution from the second post of this page:
http://answers.opencv.org/question/1624/phase-correlation-for-image-registrationimage-stitching/
The result of this code is:
Now I have to normalize this image, to see only the shiftet point.
So before you make an ifft, you have to normalize the result of the mulspectrum (code snipped taken out from the link above):
mulSpectrums(fft1,fft2,fft1,0,true);
fft1 = fft1/abs(fft1) //-->new
idft(fft1,fft1);
After this, you have to swap the quadrants, like in the openCV example:
// crop the spectrum, if it has an odd number of rows or columns
fft1 = fft1(Rect(0, 0, fft1.cols & -2, fft1.rows & -2));
// rearrange the quadrants of Fourier image so that the origin is at the image center
int cx = fft1.cols / 2;
int cy = fft1.rows / 2;
Mat q0(fft1, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
Mat q1(fft1, Rect(cx, 0, cx, cy)); // Top-Right
Mat q2(fft1, Rect(0, cy, cx, cy)); // Bottom-Left
Mat q3(fft1, Rect(cx, cy, cx, cy)); // Bottom-Right
Mat tmp; // swap quadrants (Top-Left with Bottom-Right)
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
q2.copyTo(q1);
tmp.copyTo(q2);
Now the result looks like the one from the python code:
or I can use just:
Point2d phaseCorrelate(InputArray src1, InputArray src2, InputArray window=noArray())
that´s doing all the stuff for me
You may make error on the inverse fft's scale, since you mulSpectrums, you need to divide (width*height)^2 for correct result, other than normalize it.
You may take my recipe:
cv::Mat XCorrelation(cv::Mat const& I, cv::Mat const& I1)
{
int width = cv::getOptimalDFTSize(std::max(I.cols,I1.cols));
int height = cv::getOptimalDFTSize(std::max(I.rows,I1.rows));
cv::Mat fft1;
cv::Mat fft2;
cv::copyMakeBorder(I, fft1, 0, height - I.rows, 0, width - I.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
cv::copyMakeBorder(I1, fft2, 0, height - I.rows, 0, width - I.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
fft1.convertTo(fft1, CV_32F);
fft2.convertTo(fft2, CV_32F);
cv::dft(fft1,fft1,0,I.rows);
cv::dft(fft2,fft2,0,I1.rows);
cv::mulSpectrums(fft1,fft2,fft1,0,true);
// here cv::DFT_SCALE divide `width*height` 1 times
cv::idft(fft1,fft1,cv::DFT_SCALE|cv::DFT_REAL_OUTPUT);
Rearrange(fft1, fft1);
// here divide another times
return cv::abs(fft1)/(width*height);
}
The Rearrange function is the same with your fft_shift as follows:
void Rearrange(cv::Mat& src, cv::Mat& dst)
{
int cx = src.cols / 2;
int cy = src.rows / 2;
cv::Mat tmp;
tmp.create(src.size(), src.type());
src(cv::Rect(0, 0, cx, cy)).copyTo(tmp(cv::Rect(cx, cy, cx, cy)));
src(cv::Rect(cx, cy, cx, cy)).copyTo(tmp(cv::Rect(0, 0, cx, cy)));
src(cv::Rect(cx, 0, cx, cy)).copyTo(tmp(cv::Rect(0, cy, cx, cy)));
src(cv::Rect(0, cy, cx, cy)).copyTo(tmp(cv::Rect(cx, 0, cx, cy)));
dst = tmp;
}
And for famous Lena with a shift (dx=30, dy=20), i got a result image looks similar with your python output:
Lena3020
I am using Visual Studio 2013 and OpenCV library. I display discrete Fourier transform and it works well. I'd like to save the displayed image which is the spectrum, but the saved one is black even when I use JPEG quality=100.
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include <iostream>
#include <cmath>
using namespace cv;
using namespace std;
static void help(char* progName)
{
cout << endl
<< "This program demonstrated the use of the discrete Fourier transform (DFT). " << endl
<< "The dft of an image is taken and it's power spectrum is displayed." << endl
<< "Usage:" << endl
<< progName << " [image_name -- default lena.jpg] " << endl << endl;
}
int main(int argc, char ** argv)
{
help(argv[0]);
const char* filename = argc >= 2 ? argv[1] : "lena.jpg";
Mat I = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
if (I.empty())
return -1;
Mat padded; //expand input image to optimal size
int m = getOptimalDFTSize(I.rows);
int n = getOptimalDFTSize(I.cols); // on the border add zero values
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
dft(complexI, complexI); // this way the result may fit in the source matrix
// compute the magnitude and switch to logarithmic scale
// => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magI = planes[0];
magI += Scalar::all(1); // switch to logarithmic scale
log(magI, magI);
// crop the spectrum, if it has an odd number of rows or columns
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
// rearrange the quadrants of Fourier image so that the origin is at the image center
int cx = magI.cols / 2;
int cy = magI.rows / 2;
Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right
Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left
Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
Mat tmp; // swap quadrants (Top-Left with Bottom-Right)
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
q2.copyTo(q1);
tmp.copyTo(q2);
normalize(magI, magI, 0, 1, CV_MINMAX); // Transform the matrix with float values into a
// viewable image form (float between values 0 and 1).
imshow("Input Image", I); // Show the result
imshow("spectrum magnitude", magI);
cv::Mat gs_bgr(magI.size(), CV_8UC1);
cv::cvtColor(magI, gs_bgr, CV_RGB2GRAY);
imwrite("orig.png", gs_bgr);
waitKey();
return 0;
}
the outcome of the dft (magI) is a float Mat, but you can only save uchar images with imwrite.
since you normalized the image to [0..1] , the resulting uchar - grayscale img only has 0 and 1 values, which will look pretty black indeed.
also, applying cv::cvtColor(magI, gs_bgr, CV_RGB2GRAY); to a 1chan float img seems broken.
instead of that, try:
Mat gray;
magI.convertTo(gray, CV_8U, 255); // upscale to [0..255]
I want to show that phase of an image carries more information than that of its magnitude, so I want to exchange the magnitude of two image and then do the inverse DFT.
here is the code:
void main()
{
Mat I1 = imread("lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);
Mat I2 = imread("peppers.png", CV_LOAD_IMAGE_GRAYSCALE);
Mat padded1,padded2;
//expand input image to optimal size
int m1= getOptimalDFTSize( I1.rows );
int n1 = getOptimalDFTSize( I1.cols );
int m2= getOptimalDFTSize( I2.rows );
int n2 = getOptimalDFTSize( I2.cols );
// on the border add zero values
copyMakeBorder(I1, padded1, 0, m1 - I1.rows, 0, n1 - I1.cols, BORDER_CONSTANT, Scalar::all(0));
copyMakeBorder(I2, padded2, 0, m2 - I2.rows, 0, n2 - I2.cols, BORDER_CONSTANT, Scalar::all(0));
Mat planes1[] = {Mat_<float>(padded1), Mat::zeros(padded1.size(), CV_32F)};
Mat planes2[] = {Mat_<float>(padded2), Mat::zeros(padded2.size(), CV_32F)};
Mat complexI, complexII;
// Add to the expanded another plane with zeros
merge(planes1, 2, complexI);
merge(planes2, 2, complexII);
dft(complexI, complexI);
dft(complexII, complexII);
// compute the magnitude and phase then switch to logarithmic scale
// => magnitude:log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2)), phase:arctan(Im(DFT(I)),Re(DFT(I)))
split(complexI, planes1);// planes[0] = Re(DFT(I)), planes[1] = Im(DFT(I))
Mat ph1, magI1;
phase(planes1[0], planes1[1], ph1);//ph1 = phase
magnitude(planes1[0], planes1[1], magI1);// magI1 = magnitude
magI1 = magI1(Rect(0, 0, magI1.cols & -2, magI1.rows & -2));
ph1 = ph1(Rect(0, 0, ph1.cols & -2, ph1.rows & -2));
split(complexII, planes2);// planes[0] = Re(DFT(I)), planes[1] = Im(DFT(I))
Mat ph2, magI2;
phase(planes2[0], planes2[1], ph2);//ph2 = phase
magnitude(planes2[0], planes2[1], magI2);// mag2 = magnitude
magI2 = magI2(Rect(0, 0, magI2.cols & -2, magI2.rows & -2));
ph2 = ph2(Rect(0, 0, ph2.cols & -2, ph2.rows & -2));
planes1[1] = ph1; planes1[0] = magI2;
planes2[1] = ph2; planes2[0] = magI1;
dft(complexI,complexI,DFT_INVERSE);
dft(complexII,complexII,DFT_INVERSE);
imshow("image", complexI);
waitKey();
}
I just simply merge magnitude and phase together then do the IDFT, seems totally wrong.
I guess from your question that something with your dft is not working.
Try the below Code (after adding your split Planes) and see if it works.
The images have to be the Exact same Size.
If something else is wrong: please show your images and results. Maybe your code is correct and you are just expecting the wrong thing.
Here is the working Example:
// Load an image
Mat I1 = imread("lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);
Mat I2 = imread("peppers.png", CV_LOAD_IMAGE_GRAYSCALE);
Mat fI1;
Mat fI2;
I1.convertTo(fI1, CV_32F);
I2.convertTo(fI2, CV_32F);
//Perform DFT
Mat fourierTransform1;
Mat fourierTransform2;
dft(fI1, fourierTransform1, DFT_SCALE|DFT_COMPLEX_OUTPUT);
dft(fI2, fourierTransform2, DFT_SCALE|DFT_COMPLEX_OUTPUT);
//your split plane and everything else
//Perform IDFT
Mat inverseTransform1;
Mat inverseTransform2;
dft(fourierTransform1, inverseTransform1, DFT_INVERSE|DFT_REAL_OUTPUT);
dft(fourierTransform2, inverseTransform2, DFT_INVERSE|DFT_REAL_OUTPUT);
Mat result1;
Mat result2;
inverseTransform1.convertTo(result1, CV_8U);
inverseTransform2.convertTo(result2, CV_8U);
You can't merge the magnitude and phase by using cv::merge() function, instead you should use cv::polarToCart(). Here's my code to do what you want:
using namespace cv;
// Rearrange the quadrants of a Fourier image so that the origin is at
// the image center
void shiftDFT(Mat &fImage )
{
Mat tmp, q0, q1, q2, q3;
// first crop the image, if it has an odd number of rows or columns
fImage = fImage(Rect(0, 0, fImage.cols & -2, fImage.rows & -2));
int cx = fImage.cols / 2;
int cy = fImage.rows / 2;
// rearrange the quadrants of Fourier image
// so that the origin is at the image center
q0 = fImage(Rect(0, 0, cx, cy));
q1 = fImage(Rect(cx, 0, cx, cy));
q2 = fImage(Rect(0, cy, cx, cy));
q3 = fImage(Rect(cx, cy, cx, cy));
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
}
int main()
{
// Load an image
Mat I1 = imread("lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);
Mat I2 = imread("pepper.jpg", CV_LOAD_IMAGE_GRAYSCALE);
Mat fI1;
Mat fI2;
I1.convertTo(fI1, CV_32F);
I2.convertTo(fI2, CV_32F);
//expand input image to optimal size
int m = getOptimalDFTSize( I1.rows );
int n = getOptimalDFTSize( I1.cols );
Mat padded1, padded2;
// on the border add zero values
copyMakeBorder(fI1, padded1, 0, m - I1.rows, 0, n - I1.cols, BORDER_CONSTANT, Scalar::all(0));
copyMakeBorder(fI2, padded2, 0, m - I2.rows, 0, n - I2.cols, BORDER_CONSTANT, Scalar::all(0));
//Perform DFT
Mat fourierTransform1;
Mat fourierTransform2;
Mat planes1[2], planes2[2];
dft(fI1, fourierTransform1, DFT_SCALE|DFT_COMPLEX_OUTPUT);
dft(fI2, fourierTransform2, DFT_SCALE|DFT_COMPLEX_OUTPUT);
shiftDFT(fourierTransform1);
shiftDFT(fourierTransform2);
split(fourierTransform1, planes1);// planes[0] = Re(DFT(I)), planes[1] = Im(DFT(I))
split(fourierTransform2, planes2);// planes[0] = Re(DFT(I)), planes[1] = Im(DFT(I))
Mat ph1, mag1;
mag1.zeros(planes1[0].rows, planes1[0].cols, CV_32F);
ph1.zeros(planes1[0].rows, planes1[0].cols, CV_32F);
cartToPolar(planes1[0], planes1[1], mag1, ph1);
Mat ph2, mag2;
mag2.zeros(planes2[0].rows, planes2[0].cols, CV_32F);
ph2.zeros(planes2[0].rows, planes2[0].cols, CV_32F);
cartToPolar(planes2[0], planes2[1], mag2, ph2);
polarToCart(mag1, ph2, planes1[0], planes1[1]);
polarToCart(mag2, ph1, planes2[0], planes2[1]);
merge(planes1, 2, fourierTransform1);
merge(planes2, 2, fourierTransform2);
shiftDFT(fourierTransform1);
shiftDFT(fourierTransform2);
//Perform IDFT
Mat inverseTransform1, inverseTransform2;
dft(fourierTransform1, inverseTransform1, DFT_INVERSE|DFT_REAL_OUTPUT);
dft(fourierTransform2, inverseTransform2, DFT_INVERSE|DFT_REAL_OUTPUT);
namedWindow("original image 1");
imshow("original image 1", I1);
namedWindow("original image 2");
imshow("original image 2", I2);
waitKey(0);
cv::Mat out1, out2;
inverseTransform1.convertTo(out1, CV_8U);
inverseTransform2.convertTo(out2, CV_8U);
namedWindow("result image 1");
imshow("result image 1", out1);
namedWindow("result image 2");
imshow("result image 2", out2);
waitKey(0);
}
I'm making a couple of wrapper methods for fourier transform of image and have encountered a problem. Whenever I use the standard methods:
void fft::dfft(Mat* img,Mat* result){
Mat image;
img->convertTo(image, CV_32F);
cv::dft(image, *result, cv::DFT_SCALE|cv::DFT_COMPLEX_OUTPUT);
}
Invert the operations:
void fft::idft(Mat* fourier, Mat* img) {
//invert:
Mat inverseTransform;
dft(*fourier, inverseTransform, cv::DFT_INVERSE|cv::DFT_REAL_OUTPUT);
//restore image to 8 bits (fourier needs to use float at least
Mat res;
inverseTransform.convertTo(res, CV_8U);
res.copyTo(*img);
}
everything is ok. However when I try to use this pair:
void fft::dfft(Mat* img, Mat* mag, Mat* phi, Mat* re, Mat* im) {
/*SPECAL NOTE: dft is faster for images with size being multiple of two three and five. An OCV fmethod getOptrimalDFTSize comes in handy*/
Mat paddedImg; //expand input image to the optimal size
int m = getOptimalDFTSize(img->rows);
int n = getOptimalDFTSize(img->cols);
copyMakeBorder(*img, paddedImg, 0, m - img->rows, 0, n-img->cols, BORDER_CONSTANT, Scalar::all(0));
/*prepare space to store the image*/
Mat planes[] = {Mat_<float>(paddedImg), Mat::zeros(paddedImg.size(), CV_32F)};
Mat complexImg;
merge(planes, 2, complexImg);
/*Actual dft:*/
dft(complexImg, complexImg);
/*Get magnitude:*/
split(complexImg, planes);
Mat magImg, phasImg;
planes[0].copyTo(*re);
planes[1].copyTo(*im);
cout << "intern Real = "<< planes[0] << endl;
magnitude(planes[0], planes[1], magImg); //magnitude will be stored in planes[0]
phase(planes[0], planes[1], phasImg);
magImg.copyTo(*mag);
phasImg.copyTo(*phi);
#ifdef debug
namedWindow("Input Image", 0 );
imshow("Input Image", *img);
namedWindow( "Image magnitude", 0 );
imshow("Image magnitude", magImg);
waitKey(0);
#endif
}
To extract the specific parameters, I can't put it back together. Currently I'm focusing on Real and Imaginary part, because I feel that the Mag/Phi one will work in identical way just converting to real and imaginary back from Mag/Phi first.
void fft::idft(Mat* re, Mat* im, Mat* img, bool dummy) {
Mat inverseTransform;
Mat fourier(re->rows, re->cols, CV_32FC2);
vector<Mat> planes;
planes.push_back(*re);
planes.push_back(*im);
//re and im get inside correctly (verified)
Mat padded;
Mat complex;
Mat pp[] = {Mat_<float>(*re), Mat::zeros(re->size(), CV_32F)};
pp[0] = *re;
pp[1] = *im;
merge(pp, 2, complex); // Add to the expanded another plane with zeros
cv::merge(planes, fourier);
dft(complex, inverseTransform, cv::DFT_INVERSE|cv::DFT_REAL_OUTPUT);
Mat res;
inverseTransform.convertTo(res, CV_8U);
cv::imshow("iFFT1", res);
dft(fourier, inverseTransform, cv::DFT_INVERSE|cv::DFT_REAL_OUTPUT);
inverseTransform.convertTo(res, CV_8U);
cv::imshow("iFFT2", res);
res.copyTo(*img);
}
Both approaches (with vector and with array pp) produce the same white rectangle. To see if it does anything I passed
fft::idft(&re, &re, &img, true);
in such case result was identical in both windows (some random garbage but identical)
I suspect the problem lies in wrong merge procedure, but I have no other ideas how to restore the original form?
Thanks in advance for insights.
Here is my fourier domain filter, I think it should be useful:
#pragma once
#include <string>
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
//----------------------------------------------------------
// Recombinate quadrants
//----------------------------------------------------------
void Recomb(Mat &src,Mat &dst)
{
int cx = src.cols>>1;
int cy = src.rows>>1;
Mat tmp;
tmp.create(src.size(),src.type());
src(Rect(0, 0, cx, cy)).copyTo(tmp(Rect(cx, cy, cx, cy)));
src(Rect(cx, cy, cx, cy)).copyTo(tmp(Rect(0, 0, cx, cy)));
src(Rect(cx, 0, cx, cy)).copyTo(tmp(Rect(0, cy, cx, cy)));
src(Rect(0, cy, cx, cy)).copyTo(tmp(Rect(cx, 0, cx, cy)));
dst=tmp;
}
//----------------------------------------------------------
// Forward fft
//----------------------------------------------------------
void ForwardFFT(Mat &Src, Mat *FImg)
{
int M = getOptimalDFTSize( Src.rows );
int N = getOptimalDFTSize( Src.cols );
Mat padded;
copyMakeBorder(Src, padded, 0, M - Src.rows, 0, N - Src.cols, BORDER_CONSTANT, Scalar::all(0));
// Create complex image
// planes[0] image , planes[1] filled by zeroes
Mat planes[2] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexImg;
merge(planes, 2, complexImg);
dft(complexImg, complexImg,DFT_SCALE);
// After tranform we also get complex image
split(complexImg, planes);
//
planes[0] = planes[0](Rect(0, 0, planes[0].cols & -2, planes[0].rows & -2));
planes[1] = planes[1](Rect(0, 0, planes[1].cols & -2, planes[1].rows & -2));
Recomb(planes[0],planes[0]);
Recomb(planes[1],planes[1]);
FImg[0]=planes[0].clone();
FImg[1]=planes[1].clone();
}
//----------------------------------------------------------
// Inverse FFT
//----------------------------------------------------------
void InverseFFT(Mat *FImg,Mat &Dst)
{
Recomb(FImg[0],FImg[0]);
Recomb(FImg[1],FImg[1]);
Mat complexImg;
merge(FImg, 2, complexImg);
// Inverse transform
dft(complexImg, complexImg, DFT_INVERSE);
split(complexImg, FImg);
FImg[0].copyTo(Dst);
}
//----------------------------------------------------------
// Forward FFT using Magnitude and phase
//----------------------------------------------------------
void ForwardFFT_Mag_Phase(Mat &src, Mat &Mag,Mat &Phase)
{
Mat planes[2];
ForwardFFT(src,planes);
Mag.zeros(planes[0].rows,planes[0].cols,CV_32F);
Phase.zeros(planes[0].rows,planes[0].cols,CV_32F);
cv::cartToPolar(planes[0],planes[1],Mag,Phase);
}
//----------------------------------------------------------
// Inverse FFT using Magnitude and phase
//----------------------------------------------------------
void InverseFFT_Mag_Phase(Mat &Mag,Mat &Phase, Mat &dst)
{
Mat planes[2];
planes[0].create(Mag.rows,Mag.cols,CV_32F);
planes[1].create(Mag.rows,Mag.cols,CV_32F);
cv::polarToCart(Mag,Phase,planes[0],planes[1]);
InverseFFT(planes,dst);
}
//----------------------------------------------------------
// MAIN
//----------------------------------------------------------
int main(int argc, char* argv[])
{
// src image
Mat img;
// Magnitude
Mat Mag;
// Phase
Mat Phase;
// Image loading
img=imread("d:\\ImagesForTest\\lena.jpg",0);
resize(img,img,Size(512,512));
// Image size
cout<<img.size().width<<endl;
cout<<img.size().height<<endl;
//
ForwardFFT_Mag_Phase(img,Mag,Phase);
//----------------------------------------------------------
// Filter
//----------------------------------------------------------
// draw ring
int R=100; // External radius
int r=30; // internal radius
Mat mask;
mask.create(Mag.cols,Mag.rows,CV_32F);
int cx = Mag.cols>>1;
int cy = Mag.rows>>1;
mask=1;
cv::circle(mask,cv::Point(cx,cy),R,CV_RGB(0,0,0),-1);
cv::circle(mask,cv::Point(cx,cy),r,CV_RGB(1,1,1),-1);
//mask=1-mask; // uncomment for inversion
//cv::multiply(Mag,mask,Mag); // uncomment to turn filter on
//cv::multiply(Phase,mask,Phase);
//----------------------------------------------------------
// Inverse transform
//----------------------------------------------------------
InverseFFT_Mag_Phase(Mag,Phase,img);
//----------------------------------------------------------
// Results output
//----------------------------------------------------------
//
Mat LogMag;
LogMag.zeros(Mag.rows,Mag.cols,CV_32F);
LogMag=(Mag+1);
cv::log(LogMag,LogMag);
//---------------------------------------------------
imshow("Magnitude Log", LogMag);
imshow("Phase", Phase);
// img - now in CV_32FC1 format,we need CV_8UC1 or scale it by factor 1.0/255.0
img.convertTo(img,CV_8UC1);
imshow("Filtering result", img);
//----------------------------------------------------------
// Wait key press
//----------------------------------------------------------
waitKey(0);
return 0;
}
Sorry, I can't reproduce you result I've tested the code and it works fine:
#pragma once
#include <string>
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
//----------------------------------------------------------
// Recombinate quadrants
//----------------------------------------------------------
void Recomb(Mat &src,Mat &dst)
{
int cx = src.cols>>1;
int cy = src.rows>>1;
Mat tmp;
tmp.create(src.size(),src.type());
src(Rect(0, 0, cx, cy)).copyTo(tmp(Rect(cx, cy, cx, cy)));
src(Rect(cx, cy, cx, cy)).copyTo(tmp(Rect(0, 0, cx, cy)));
src(Rect(cx, 0, cx, cy)).copyTo(tmp(Rect(0, cy, cx, cy)));
src(Rect(0, cy, cx, cy)).copyTo(tmp(Rect(cx, 0, cx, cy)));
dst=tmp;
}
//----------------------------------------------------------
// Forward fft
//----------------------------------------------------------
void ForwardFFT(Mat &Src, Mat *FImg)
{
int M = getOptimalDFTSize( Src.rows );
int N = getOptimalDFTSize( Src.cols );
Mat padded;
copyMakeBorder(Src, padded, 0, M - Src.rows, 0, N - Src.cols, BORDER_CONSTANT, Scalar::all(0));
// Create complex image
// planes[0] image , planes[1] filled by zeroes
Mat planes[2] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexImg;
merge(planes, 2, complexImg);
dft(complexImg, complexImg,DFT_SCALE);
// After tranform we also get complex image
split(complexImg, planes);
//
planes[0] = planes[0](Rect(0, 0, planes[0].cols & -2, planes[0].rows & -2));
planes[1] = planes[1](Rect(0, 0, planes[1].cols & -2, planes[1].rows & -2));
Recomb(planes[0],planes[0]);
Recomb(planes[1],planes[1]);
FImg[0]=planes[0].clone();
FImg[1]=planes[1].clone();
}
//----------------------------------------------------------
// Inverse FFT
//----------------------------------------------------------
void InverseFFT(Mat *FImg,Mat &Dst)
{
Recomb(FImg[0],FImg[0]);
Recomb(FImg[1],FImg[1]);
Mat complexImg;
merge(FImg, 2, complexImg);
// Inverse transform
dft(complexImg, complexImg, DFT_INVERSE);
split(complexImg, FImg);
FImg[0].copyTo(Dst);
}
//----------------------------------------------------------
// Forward FFT using Magnitude and phase
//----------------------------------------------------------
void ForwardFFT_Mag_Phase(Mat &src, Mat &Mag,Mat &Phase)
{
Mat planes[2];
ForwardFFT(src,planes);
Mag.zeros(planes[0].rows,planes[0].cols,CV_32F);
Phase.zeros(planes[0].rows,planes[0].cols,CV_32F);
cv::cartToPolar(planes[0],planes[1],Mag,Phase);
}
//----------------------------------------------------------
// Inverse FFT using Magnitude and phase
//----------------------------------------------------------
void InverseFFT_Mag_Phase(Mat &Mag,Mat &Phase, Mat &dst)
{
Mat planes[2];
planes[0].create(Mag.rows,Mag.cols,CV_32F);
planes[1].create(Mag.rows,Mag.cols,CV_32F);
cv::polarToCart(Mag,Phase,planes[0],planes[1]);
InverseFFT(planes,dst);
}
//----------------------------------------------------------
// MAIN
//----------------------------------------------------------
int main(int argc, char* argv[])
{
// src image
Mat img;
// Magnitude
Mat Mag;
// Phase
Mat Phase;
// Image loading (grayscale)
img=imread("d:\\ImagesForTest\\lena.jpg",0);
ForwardFFT_Mag_Phase(img,Mag,Phase);
//----------------------------------------------------------
// Inverse transform
//----------------------------------------------------------
InverseFFT_Mag_Phase(Mag,Phase,img);
img.convertTo(img,CV_8UC1);
imshow("Filtering result", img);
//----------------------------------------------------------
// Wait key press
//----------------------------------------------------------
waitKey(0);
return 0;
}
And it get the same image as input.
This question already has an answer here:
Inverse fourier transformation in OpenCV
(1 answer)
Closed 6 years ago.
I'm trying to implement inverse DFT using OpenCV in C++
I downloaded complete dft example in docs.opencv.org and just adjust couple of lines to inverse.
my DFT code is like this
Mat DFT(const char* filename)
{
Mat I = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
if (I.empty())
{
Mat emty(7, 7, CV_32FC2, Scalar(1, 3));
return emty;
}
Mat padded; //expand input image to optimal size
int m = getOptimalDFTSize(I.rows);
int n = getOptimalDFTSize(I.cols); // on the border add zero values
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
dft(complexI, complexI); // this way the result may fit in the source matrix
// compute the magnitude and switch to logarithmic scale
// => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magI = planes[0];
magI += Scalar::all(1); // switch to logarithmic scale
log(magI, magI);
normalize(magI, magI, 0, 1, CV_MINMAX); // Transform the matrix with float values into a
// viewable image form (float between values 0 and 1).
imshow("Input Image", I); // Show the result
imshow(filename, magI);
// waitKey();
return magI;
}
and did IDFT. By just fix dft to idft. But the output was just look like noise. What did I do wrong? I thought the dft and idft is just same....
Mat IDFT(Mat src)
{
Mat I = src;
Mat padded; //expand input image to optimal size
int m = getOptimalDFTSize(I.rows);
int n = getOptimalDFTSize(I.cols); // on the border add zero values
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
dft(complexI, complexI, DFT_INVERSE); // this way the result may fit in the source matrix
// compute the magnitude and switch to logarithmic scale
// => log(1 + sqrt(Re(IDFT(I))^2 + Im(IDFT(I))^2))
split(complexI, planes); // planes[0] = Re(IDFT(I), planes[1] = Im(IDFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magI = planes[0];
magI += Scalar::all(1); // switch to logarithmic scale
log(magI, magI);
normalize(magI, magI, 0, 1, CV_MINMAX);
imshow("forged map", magI);
return magI;
}
you have to rewrite your code like this to get the inverse DFT which is the original image read :
#include "stdafx.h"
#include <opencv2/core/core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat I = imread("test.tif", CV_LOAD_IMAGE_GRAYSCALE);
if( I.empty())
return -1;
Mat padded; //expand input image to optimal size
int m = getOptimalDFTSize( I.rows );
int n = getOptimalDFTSize( I.cols ); // on the border add zero values
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexI;
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
dft(complexI, complexI); // this way the result may fit in the source matrix
// compute the magnitude and switch to logarithmic scale
// => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magI = planes[0];
magI += Scalar::all(1); // switch to logarithmic scale
log(magI, magI);
// crop the spectrum, if it has an odd number of rows or columns
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
// rearrange the quadrants of Fourier image so that the origin is at the image center
int cx = magI.cols/2;
int cy = magI.rows/2;
Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right
Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left
Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
Mat tmp; // swap quadrants (Top-Left with Bottom-Right)
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
q2.copyTo(q1);
tmp.copyTo(q2);
normalize(magI, magI, 0, 1, CV_MINMAX); // Transform the matrix with float values into a
normalize(phaseVals, phaseVals, 0, 1, CV_MINMAX);
// viewable image form (float between values 0 and 1).
imshow("Input Image" , I ); // Show the result
imshow("Spectrum Magnitude", magI);
waitKey();
//calculating the idft
cv::Mat inverseTransform;
cv::dft(complexI, inverseTransform, cv::DFT_INVERSE|cv::DFT_REAL_OUTPUT);
normalize(inverseTransform, inverseTransform, 0, 1, CV_MINMAX);
imshow("Reconstructed", inverseTransform);
waitKey();
return 0;
}
I just added this part to your code :
//calculating the idft
cv::Mat inverseTransform;
cv::dft(complexI, inverseTransform, cv::DFT_INVERSE|cv::DFT_REAL_OUTPUT);
normalize(inverseTransform, inverseTransform, 0, 1, CV_MINMAX);
imshow("Reconstructed", inverseTransform);
waitKey();
for some reason using the normalize function in the given idft block doesn't give exactly the desired original image (contrast wise). Instead use convertTo. To be precise, replace the normalize function with the following line.
inverseTransform.convertTo(inverseTransform, CV_8U);