How can I compute the DFT of an image (using EMGU), display it and then compute the reverse to get back to the original?
I'm going to answer my own question here since it took me a while to figure out.
How can I compute the DFT of an image (using EMGU), display it and then compute the reverse to get back to the original?
I'm going to answer my own question here since it took me a while to figure out.
To test that it works here's an image
and here's the expected result after applying DFT.
And without further ado here's the code:
// Load image
Image<Gray, float> image = new Image<Gray, float>(@"C:\Users\me\Desktop\lines.png");
// Transform 1 channel grayscale image into 2 channel image
IntPtr complexImage = CvInvoke.cvCreateImage(image.Size, Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_32F, 2);
CvInvoke.cvSetImageCOI(complexImage, 1); // Select the channel to copy into
CvInvoke.cvCopy(image, complexImage, IntPtr.Zero);
CvInvoke.cvSetImageCOI(complexImage, 0); // Select all channels
// This will hold the DFT data
Matrix<float> forwardDft = new Matrix<float>(image.Rows, image.Cols, 2);
CvInvoke.cvDFT(complexImage, forwardDft, Emgu.CV.CvEnum.CV_DXT.CV_DXT_FORWARD, 0);
CvInvoke.cvReleaseImage(ref complexImage);
// We'll display the magnitude
Matrix<float> forwardDftMagnitude = GetDftMagnitude(forwardDft);
SwitchQuadrants(ref forwardDftMagnitude);
// Now compute the inverse to see if we can get back the original
Matrix<float> reverseDft = new Matrix<float>(forwardDft.Rows, forwardDft.Cols, 2);
CvInvoke.cvDFT(forwardDft, reverseDft, Emgu.CV.CvEnum.CV_DXT.CV_DXT_INV_SCALE, 0);
Matrix<float> reverseDftMagnitude = GetDftMagnitude(reverseDft);
pictureBox1.Image = image.ToBitmap();
pictureBox2.Image = Matrix2Bitmap(forwardDftMagnitude);
pictureBox3.Image = Matrix2Bitmap(reverseDftMagnitude);
private Bitmap Matrix2Bitmap(Matrix<float> matrix)
{
CvInvoke.cvNormalize(matrix, matrix, 0.0, 255.0, Emgu.CV.CvEnum.NORM_TYPE.CV_MINMAX, IntPtr.Zero);
Image<Gray, float> image = new Image<Gray, float>(matrix.Size);
matrix.CopyTo(image);
return image.ToBitmap();
}
// Real part is magnitude, imaginary is phase.
// Here we compute log(sqrt(Re^2 + Im^2) + 1) to get the magnitude and
// rescale it so everything is visible
private Matrix<float> GetDftMagnitude(Matrix<float> fftData)
{
//The Real part of the Fourier Transform
Matrix<float> outReal = new Matrix<float>(fftData.Size);
//The imaginary part of the Fourier Transform
Matrix<float> outIm = new Matrix<float>(fftData.Size);
CvInvoke.cvSplit(fftData, outReal, outIm, IntPtr.Zero, IntPtr.Zero);
CvInvoke.cvPow(outReal, outReal, 2.0);
CvInvoke.cvPow(outIm, outIm, 2.0);
CvInvoke.cvAdd(outReal, outIm, outReal, IntPtr.Zero);
CvInvoke.cvPow(outReal, outReal, 0.5);
CvInvoke.cvAddS(outReal, new MCvScalar(1.0), outReal, IntPtr.Zero); // 1 + Mag
CvInvoke.cvLog(outReal, outReal); // log(1 + Mag)
return outReal;
}
// We have to switch quadrants so that the origin is at the image center
private void SwitchQuadrants(ref Matrix<float> matrix)
{
int cx = matrix.Cols / 2;
int cy = matrix.Rows / 2;
Matrix<float> q0 = matrix.GetSubRect(new Rectangle(0, 0, cx, cy));
Matrix<float> q1 = matrix.GetSubRect(new Rectangle(cx, 0, cx, cy));
Matrix<float> q2 = matrix.GetSubRect(new Rectangle(0, cy, cx, cy));
Matrix<float> q3 = matrix.GetSubRect(new Rectangle(cx, cy, cx, cy));
Matrix<float> tmp = new Matrix<float>(q0.Size);
q0.CopyTo(tmp);
q3.CopyTo(q0);
tmp.CopyTo(q3);
q1.CopyTo(tmp);
q2.CopyTo(q1);
tmp.CopyTo(q2);
}
Most of the information in this answer is from a question on the OpenCV mailing list and Steve Eddins' article on FFT in image processing.