Related
I would appreciate some help understanding the output of a small educational program I put together. I am new to OpenCV and don't have much C++ experience.
The goal of the script is to perform the following:
Load an image
Perform DFT on the image
Apply a circular binary mask to the spectrum, where the radius of the circle can be increased by hitting a key on the keyboard (essentially applying a very crude filter to the image)
Display the result of the inverse transform of the spectrum after the mask was applied
I have the basic functionality working: I can load the image, perform DFT, view the output image and increase the radius of the circle (advancing through a for-loop with the circle radius following i), and see the result of the inverse transform of the modified spectrum.
However I do not understand why the output is showing a vertically flipped copy of the input superimposed on the image (see example below with Lena.png). This is not my intended result. When I imshow() the inverse DFT result without applying the mask, I get a normal, non-doubled image. But after applying the mask, the IDFT output looks like this:
I am not looking for a quick solution: I would really appreciate if someone more experienced could ask leading questions to help me understand this result so that I can try to fix it myself.
My code:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
void expand_img_to_optimal(Mat &padded, Mat &img);
void fourier_transform(Mat &img);
int main(int argc, char **argv)
{
Mat input_img;
input_img = imread("Lena.png" , IMREAD_GRAYSCALE);
if (input_img.empty())
{
fprintf(stderr, "Could not Open image\n\n");
return -1;
}
fourier_transform(input_img);
return 0;
}
void fourier_transform(Mat &img)
{
Mat padded;
expand_img_to_optimal(padded, img);
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexI;
merge(planes, 2, complexI);
dft(complexI, complexI, DFT_COMPLEX_OUTPUT);
// For-loop to increase mask circle radius incrementally
for (int i=0; i<400; i+=10) {
Mat mask = Mat::ones(complexI.size(), complexI.type());
mask.convertTo(mask, CV_8U);
Mat dest = Mat::ones(complexI.size(), complexI.type());
circle(mask, Point(mask.cols/2, mask.rows/2), i, 0, -1, 8, 0);
complexI.copyTo(dest, mask);
Mat inverseTransform;
idft(dest, inverseTransform, DFT_INVERSE|DFT_REAL_OUTPUT);
normalize(inverseTransform, inverseTransform, 0, 1, NORM_MINMAX);
imshow("Reconstructed", inverseTransform);
waitKey(0);
}
}
void expand_img_to_optimal(Mat &padded, Mat &img) {
int row = getOptimalDFTSize(img.rows);
int col = getOptimalDFTSize(img.cols);
copyMakeBorder(img, padded, 0, row - img.rows, 0, col - img.cols, BORDER_CONSTANT, Scalar::all(0));
}
This happens because you are inverse-transforming a frequency spectrum that is not conjugate-symmetric around the origin.
The origin of the frequency domain image is the top-left pixel. Your disk mask must be centered there. The frequency domain is periodic, so that the part of the mask that extends to the left of the image wraps around and comes in to the right edge, same with top and bottom edges.
The easiest way to generate a proper mask is to
create it with the origin at (mask.cols/2, mask.rows/2), like you already do, and then
apply the ifftshift operation.
OpenCV doesn’t have a ifftshift function, this answer has code that implements the ifftshift correctly.
Firstly I'd like to thank #Cris Luengo for his helpful input on implementing the ifftshift in OpenCV.
In the end, the problem with my code was in this line:
Mat mask = Mat::ones(complexI.size(), complexI.type());
Instead of using the type of complexI, it looks like I should have used the type of img:
Mat mask = Mat::ones(complexI.size(), img.type());
Why? I'm not sure yet. Still trying to understand. Here is my complete code that is working how I intended:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
void expand_img_to_optimal(Mat &padded, Mat &img);
void fourier_transform(Mat &img);
void ifft_shift(Mat &mask);
int main(int argc, char **argv)
{
Mat input_img;
input_img = imread("Lena.png" , IMREAD_GRAYSCALE);
if (input_img.empty())
{
fprintf(stderr, "Could not Open image\n\n");
return -1;
}
fourier_transform(input_img);
return 0;
}
void fourier_transform(Mat &img)
{
Mat padded;
expand_img_to_optimal(padded, img);
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexI;
merge(planes, 2, complexI);
dft(complexI, complexI, DFT_COMPLEX_OUTPUT);
for (float i=0; i<4000; i+=2) {
// Create disk mask matrix
Mat mask = Mat::ones(complexI.size(), CV_8U);
circle(mask, Point(mask.cols/2, mask.rows/2), i, 0, -1, 8, 0);
// Perform ifft shift
ifft_shift(mask);
// Destination matrix for masked spectrum
Mat dest;
complexI.copyTo(dest, mask);
// Perform inverse DFT
Mat inverseTransform;
idft(dest, inverseTransform, DFT_INVERSE|DFT_REAL_OUTPUT);
normalize(inverseTransform, inverseTransform, 0, 1, NORM_MINMAX);
imshow("Reconstructed", inverseTransform);
waitKey(0);
}
}
void expand_img_to_optimal(Mat &padded, Mat &img) {
int row = getOptimalDFTSize(img.rows);
int col = getOptimalDFTSize(img.cols);
copyMakeBorder(img, padded, 0, row - img.rows, 0, col - img.cols, BORDER_CONSTANT, Scalar::all(0));
}
void ifft_shift(Mat &mask) {
// input sizes
int sx = mask.cols;
int sy = mask.rows;
// input origin
int cx = sx / 2;
int cy = sy / 2;
// split the quadrants
Mat top_left(mask, Rect(0, 0, cx, cy));
Mat top_right(mask, Rect(cx, 0, sx - cx, cy));
Mat bottom_left(mask, Rect(0, cy, cx, sy - cy));
Mat bottom_right(mask, Rect(cx, cy, sx - cx, sy - cy));
// merge the quadrants in right order
Mat tmp1, tmp2;
hconcat(bottom_right, bottom_left, tmp1);
hconcat(top_right, top_left, tmp2);
vconcat(tmp1, tmp2, mask);
}
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
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;
}
I'm trying to follow the discreet fourier transform (dft) example here:
http://docs.opencv.org/doc/tutorials/core/discrete_fourier_transform/discrete_fourier_transform.html
I'm running 2.4.8 on Visual Studio 2013 Express in Windows 8.
I've modified the example so that instead of loading a greyscale image I'm using a colour image captured from my webcam (loaded into a Mat variable).
When I run the example above, I get the following error:
"Assertion Failed Tp>::channels == m.channels()) in
cv::Mat::operator"
and a break at the following line:
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
Looking around, I saw that this is the old way of converting between types, so I added these lines to convert everything to CV_32F:
padded.convertTo(padded32, CV_32F);
Mat planes[] = { padded32, Mat::zeros(padded32.size(), CV_32F) };
Now the problem is that I get another assertion fail a few lines down at:
split(complexI, planes);
The Error is:
"Assertion Failed (Type == CV_32FC1 || Type == CV_32FC2 || ... || Type
== CV_64FC2) in cv::dft"
So now it seems like it doesn't like the CV_32F data type. I tried making the data type CV_32FC1, but it had the same error. I suspect it's related to the output data type of complexI from the dft() function but I'm not sure what to do. It may also be related to the number of channels in my input (3 channel colour vs 1 channel greyscale image).
Thanks for the help.
Complete code from the linked example:
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
int main(int argc, char ** argv)
{
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);
waitKey();
return 0;
}
You cannot use dft on an imagine that has more than 2 channels.
Even if the image has 2 channels the second one is interpreted as the imaginary part of a complex number so this probably not what you want anyway.
So you have 2 options: either convert the colour image that you get from your webcam to a single channel image, like a grayscale image, or apply the dft for each channel independently.
You can take a look over mix channels or split, both of them can extract the individual channels from your image and then apply dft on each of them,
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);