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);
}
Related
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'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,
So I've been working on recognizing a yoga ball with Hough Circles. Now, when converted to grayscale, it works straight away. Unfortunately, I have to take a more complicated procedure due to there being multiple of these coloured balls and only wanted to detect the blue.
Unfiltered ball:
Filtered ball:
Steps of my algorithm:
convert from BGR to HSV
blur the image
filter HSV for only select values (in my case dark blue to light blue due to lighting)
invert the image
use morphology to fill in the part that was lighted
blur again
filter the blur so it's a solid shape instead of unrecognisable blurry grayscale
detect with hough-circles. The MAT is still Grayscale so that isn't the problem.
Code:
#include <iostream>
#include <string>
#include <iomanip>
#include <sstream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
// Morphology stuff
Mat element5(30, 30, CV_8U, Scalar(1));
int morph_elem = 1; // 2
int morph_size = 33;// 30
int morph_operator = 2; // 2
Mat element = getStructuringElement(morph_elem, Size(2 * morph_size + 1, 2 * morph_size + 1), Point(morph_size, morph_size));
int const max_operator = 4;
int const max_elem = 2;
int const max_kernel_size = 21;
Mat kernel;
// Display Windows Name
namedWindow("Testing Purposes", CV_WINDOW_AUTOSIZE);
Mat src; // loaded image
Mat hsv; // changed src into HSV
Mat Filtered; // filtered w/ inRange for blue ball
Mat Gray; // gray filter for src
Mat dst; // destination for canny edge
Mat detected_edges; // matrix of edges w/ canny
// thresholds for canny
int edgeThresh = 45;
int lowThreshold;
int const max_lowThreshold = 100;
src = imread(argv[1]);
cvtColor(src, Gray, CV_BGR2GRAY);
cvtColor(src, hsv, CV_BGR2HSV);
/*
// CannyEdge Testing
blur(Gray, detected_edges, Size(3, 3)); // blur the grayimage
Canny(detected_edges, detected_edges, lowThreshold, lowThreshold * ratio, kernel_size);
dst = Scalar::all(0);
src.copyTo( dst, detected_edges);
imshow(window_name,dst);
*/
// hsv blur and then thresholds
blur(hsv,hsv,Size(4, 4), Point(-1, -1));
inRange(hsv, Scalar(100, 100, 0), Scalar(200, 200, 255), Filtered); //filtering after blur
vector<Vec3f> circles; //vector for holding info on circles
// houghcircles - attempts to detect circles in the Filtered image we passed it
// morphology defintion for Kernel
bitwise_not(Filtered, Filtered);
// imwrite("/home/bjacobs/Desktop/Testing.jpg", Filtered);
imwrite("/home/bjacobs/Desktop/Testingg.jpg", Filtered);
morphologyEx(Filtered, dst, MORPH_OPEN, element);
blur(dst, dst, Size(20, 20), Point(-1, -1));
Mat baw = dst > 128;
HoughCircles(baw ,circles, CV_HOUGH_GRADIENT, 1, baw.rows/8,200,100,0,0);
imwrite("/home/bjacobs/Desktop/Testing.jpg", baw);
// Draw the circles detected onto the SRC file
for(size_t i = 0; i < circles.size(); i++)
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][3]));
int radius = cvRound(circles[i][2]);
// circle center
circle(src, center, 3, Scalar(0, 255, 0), -1, 8, 0);
// circle outline
circle(src, center, radius, Scalar(0, 0, 255), 3, 8, 0);
}
imwrite("/home/bjacobs/Desktop/Test.jpg", hsv);
imshow("Testing Purposes", src);
waitKey(0);
}
I've already read as much as I possibly could online on this matter, and nothing I've found so far has helped. Forgive the sloppy commenting, and there are some failed algorithms included with using Canny Edge detection, so don't pay too much mind to them. Does anyone know of a solution to this detection issue?
Instead of using houghcircle you can do the following.
Segment the blue color.
Find contours(largest).
Minimum enclosing circle for contour.
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.
I am new in OpenCV and image processing algorithms. I need to do inverse discrete fourier transformation in OpenCV in C++, but I don't know how. I searched over internet and I didn't find answer. I am doing fourier transformation in my program with this code from this page: http://opencv.itseez.com/doc/tutorials/core/discrete_fourier_transform/discrete_fourier_transform.html. I have tried to do inverse to that code, but I don't know where I am doing wrong. My code is here (I think that whole code is wrong):
void doFourierInverse(const Mat &src, Mat &dst) {
normalize(src, dst, 0, -1, CV_MINMAX);
int cx = dst.cols/2;
int cy = dst.rows/2;
Mat q0(dst, Rect(0, 0, cx, cy));
Mat q1(dst, Rect(cx, 0, cx, cy));
Mat q2(dst, Rect(0, cy, cx, cy));
Mat q3(dst, Rect(cx, cy, cx, cy));
Mat tmp;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
dst = dst(Rect(0, 0, dst.cols & -2, dst.rows & -2));
exp(dst, dst);
dst -= Scalar::all(1);
Mat planes[2];
polarToCart(dst, Mat::zeros(dst.rows, dst.cols, dst.type()), planes[0], planes[1]);
merge(planes, 2, dst);
idft(dst, dst, DFT_INVERSE | DFT_SCALE);
split(dst, planes);
dst = planes[0];
}
Actually, you don't have to swap the different quadrants, it's needed only if you're a human and want a more natural looking visualization of the FFT result (i.e. with the 0 frequency in the middle, negative frequencies left/bottom and positive frequencies up/right).
To invert the FFT, you need to pass the result of the forward transform "as is" (or after the frequency filtering you wanted) to the same dft() function, only adding the flag DFT_INVERSE. If you remember your math about FFT, the forward and backward transforms have very tight kinks in the formulation...
--- EDIT ---
What exactly doesn't work ?
The following code does perform forward-then-backward FFT, and everything works just fine as expected.
// Load an image
cv::Mat inputImage = cv::imread(argv[argc-1], 0);
// Go float
cv::Mat fImage;
inputImage.convertTo(fImage, CV_32F);
// FFT
std::cout << "Direct transform...\n";
cv::Mat fourierTransform;
cv::dft(fImage, fourierTransform, cv::DFT_SCALE|cv::DFT_COMPLEX_OUTPUT);
// Some processing
doSomethingWithTheSpectrum();
// IFFT
std::cout << "Inverse transform...\n";
cv::Mat inverseTransform;
cv::dft(fourierTransform, inverseTransform, cv::DFT_INVERSE|cv::DFT_REAL_OUTPUT);
// Back to 8-bits
cv::Mat finalImage;
inverseTransform.convertTo(finalImage, CV_8U);