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
How can I divide a complex Mat with a real Mat in OpenCV? I want to calculate cross-power spectrum for phase correlation but I get a runtime error when using divide.
Update
I figured out a way to calculate cross-power spectrum but i don't get the appropriate result to find the translation of an image. Below is the code.
I split the result of inverse dft because it has two channels. Then, I was able to divide but the result is not good for translation only in the horizontal axis. There should be only one max value, but there are a lot of max values.
Image
void computeFFTMag(Mat&,Mat&,Mat&);
string getImgType(int );
int main( int argc, char** argv )
{
Mat ref,sens,refMag,sensMag,refFFT,sensFFT;
ref = imread("lena1.jpg",CV_LOAD_IMAGE_GRAYSCALE);
sens = imread("lena3.jpg",CV_LOAD_IMAGE_GRAYSCALE);
namedWindow( "Sensed Image", CV_WINDOW_AUTOSIZE );
imshow( "Sensed Image", sens );
computeFFTMag(ref,refMag,refFFT);
computeFFTMag(sens,sensMag,sensFFT);
Mat R1,R2,R,r,rf[2],rff;
mulSpectrums(refFFT,sensFFT,R1,0,true);
multiply(refMag,sensMag,R2);
dft(R1,r,DFT_REAL_OUTPUT);
split(r,rf);
divide(rf[0],R2,r);
normalize(r, r, 0, 1, CV_MINMAX);
namedWindow( "Reference Image", CV_WINDOW_AUTOSIZE );
imshow("Reference Image" , r );
}
void computeFFTMag(Mat& input,Mat& fftMag,Mat& complexFFT){
Mat inputPadded;
/*int r=getOptimalDFTSize(input.rows);
int c=getOptimalDFTSize(input.cols);
copyMakeBorder(input,inputPadded,0,r-input.rows,0,c-input.cols,BORDER_CONSTANT, Scalar::all(0));*/
Mat fftPlanes[] = {Mat_<float>(input), Mat::zeros(input.size(), CV_32F)};
//Mat complexFFT;
merge(fftPlanes, 2, complexFFT);
dft(complexFFT,complexFFT);
split(complexFFT,fftPlanes);
magnitude(fftPlanes[0],fftPlanes[1],fftPlanes[0]);
fftMag=fftPlanes[0];
//fftMag = fftMag(Rect(0, 0, fftMag.cols & -2, fftMag.rows & -2));
int cx = fftMag.cols/2;
int cy = fftMag.rows/2;
Mat q0(fftMag, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
Mat q1(fftMag, Rect(cx, 0, cx, cy)); // Top-Right
Mat q2(fftMag, Rect(0, cy, cx, cy)); // Bottom-Left
Mat q3(fftMag, 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);
}
A complex number can be divided by a scalar by dividing the real and complex terms by the same scalar. So, (2 + 3i) / 4 is equal to 2/4 + (3/4)i.
OpenCV doesn't let you do elementwise division of complex mats with scalar mats, but it does let you do elementwise division of complex mats with complex mats. You can get the division to work by creating a complex matrix where both the real and imaginary components are the same as the real values in your real mat that you want to divide by.
// Goal: Divide 'complex' by 'real'
Mat complex = Mat(rows, cols, CV_32FC2);
Mat real = Mat(rows, cols, CV_32FC1);
Mat channels[2] = {real, real};
Mat divisor = Mat(rows, cols, CV_32FC2);
merge(channels, 2, divisor);
Mat result = Mat(rows, cols, CV_32FC2);
divide(complex, divisor, result);
I want to subtract the mean value of an image to this image.
I am using a DFT and then I would like to do some process to see my image clearer.
I used meanStdDev function but the image doesn't change. What function should I use instead?
my code
dft(complexI, complexI);
split(complexI, planes);
magnitude(planes[0], planes[1], planes[0]);
Mat magI = planes[0];
magI += Scalar::all(1);
log(magI, magI);
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));
Mat q1(magI, Rect(cx, 0, cx, cy));
Mat q2(magI, Rect(0, cy, cx, cy));
Mat q3(magI, 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);
Mat mean;
Mat stddev;
meanStdDev(magI, mean, stddev);
Mat dst;
subtract( magI, mean, dst);
normalize(dst, dst, 0, 1, CV_MINMAX);
namedWindow("spectrum magnitude", WINDOW_AUTOSIZE);
imshow("spectrum magnitude", dst);
I'm trying to write a code for homomorphic filtering using Gaussian LPF, but as a result I'm getiing a total black image at the end. the written filter part of the code works perfect on other applications !
#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()
{
// Variables ========================================================================================
int D0_GHPF = 80; // Gaussian HPF cut-off deviation
// ==================================================================================================
// Getting the frequency and magnitude of image =====================================================
cv::Mat image = cv::imread("test2.tif", CV_LOAD_IMAGE_GRAYSCALE);
if( image.empty())
return -1;
image.convertTo(image, CV_32F);
image += 1;
log(image,image);
cv::Mat padded1;
int m1 = cv::getOptimalDFTSize( image.rows );
int n1 = cv::getOptimalDFTSize( image.cols );
cv::copyMakeBorder(image, padded1, 0, m1 - image.rows, 0, n1 - image.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
cv::Mat image_planes[] = {cv::Mat_<float>(padded1), cv::Mat::zeros(padded1.size(), CV_32F)};
cv::Mat image_complex;
cv::merge(image_planes, 2, image_complex);
cv::dft(image_complex, image_complex);
cv::split(image_complex, image_planes);
// starting with this part we have the real part of the image in planes[0] and the imaginary in planes[1]
cv::Mat image_phase;
cv::phase(image_planes[0], image_planes[1], image_phase);
cv::Mat image_mag;
cv::magnitude(image_planes[0], image_planes[1], image_mag);
// Shifting the DFT
image_mag = image_mag(cv::Rect(0, 0, image_mag.cols & -2, image_mag.rows & -2));
int cx = image_mag.cols/2;
int cy = image_mag.rows/2;
cv::Mat q0(image_mag, cv::Rect(0, 0, cx, cy));
cv::Mat q1(image_mag, cv::Rect(cx, 0, cx, cy));
cv::Mat q2(image_mag, cv::Rect(0, cy, cx, cy));
cv::Mat q3(image_mag, cv::Rect(cx, cy, cx, cy));
cv::Mat tmp;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
// Creating GHPF ====================================================================================
cv::Mat GHPF(image_mag.size(), CV_32F, 255);
float tempVal = float((-1.0)/float(pow(float(D0_GHPF),2)));
for (int i=0; i < GHPF.rows; i++)
for (int j=0; j < GHPF.cols; j++)
{
float dummy2 = float(pow(float(i - cy), 2) + pow(float(j - cx), 2));
dummy2 = (2.0 - 0.25) * (1.0 - float(exp(float(dummy2 * tempVal)))) + 0.25;
GHPF.at<float>(i,j) = 255 * dummy2;
}
cv::normalize(GHPF, GHPF, 0, 1, CV_MINMAX);
cv::imshow("test", GHPF);
cv::waitKey(0);
// Applying GHPF filter ==================================================================================
cv::Mat GHPF_result(image_mag.size(), CV_32F);
cv::multiply(image_mag, GHPF, GHPF_result);
// reversing the shift ==============================================================================
cv::Mat q0_GHPF(GHPF_result, cv::Rect(0, 0, cx, cy));
cv::Mat q1_GHPF(GHPF_result, cv::Rect(cx, 0, cx, cy));
cv::Mat q2_GHPF(GHPF_result, cv::Rect(0, cy, cx, cy));
cv::Mat q3_GHPF(GHPF_result, cv::Rect(cx, cy, cx, cy));
cv::Mat tmp_GHPF;
q0_GHPF.copyTo(tmp_GHPF);
q3_GHPF.copyTo(q0_GHPF);
tmp_GHPF.copyTo(q3_GHPF);
q1_GHPF.copyTo(tmp_GHPF);
q2_GHPF.copyTo(q1_GHPF);
tmp_GHPF.copyTo(q2_GHPF);
// Reconstructing the image with new GHPF filter ====================================================
cv::Mat GHPFresult_planes[2];
cv::polarToCart(GHPF_result, image_phase,GHPFresult_planes[0], GHPFresult_planes[1]);
cv::Mat GHPFresult_complex;
cv::merge(GHPFresult_planes,2,GHPFresult_complex);
//calculating the iDFT for GHPF
cv::Mat GHPF_inverseTransform;
cv::dft(GHPFresult_complex, GHPF_inverseTransform, cv::DFT_INVERSE|cv::DFT_REAL_OUTPUT);
exp(GHPF_inverseTransform,GHPF_inverseTransform);
cv::normalize(GHPF_inverseTransform, GHPF_inverseTransform, 0, 1, CV_MINMAX);
cv::imshow("GHPF Reconstructed", GHPF_inverseTransform);
cv::waitKey(0);
}
The theory is based on chapter of Gonzalez 3rd edition, digital image processing
well, i'm having a hard time with Homomorphic filtering, too.
and i find there is something wrong with your code when it comes to exp:#INF
cv::normalize(GHPF_inverseTransform, GHPF_inverseTransform, 0, 1, CV_MINMAX);
cv::exp(GHPF_inverseTransform, GHPF_inverseTransform);
cv::normalize(GHPF_inverseTransform, GHPF_inverseTransform, 0,255, CV_MINMAX);
like this,something different happens.
then
cv::normalize(GHPF_inverseTransform, GHPF_inverseTransform, 0, 0.000001, CV_MINMAX);
cv::exp(GHPF_inverseTransform, GHPF_inverseTransform);
cv::normalize(GHPF_inverseTransform, GHPF_inverseTransform, 100,255, CV_MINMAX);
this time i can see the image clearly (well, but it looks like an image from minecraft)
so i copied another GHPF and it worked perfectly.
so i guess there is still something wrong in your GHPF.
(sorry for my poor English:p)
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);