FFTW gives wrong results in comparison to MATLAB [duplicate] - c++

I aim to get the DFT of an image in OpenCV.
Using dft function, I'm able to calculate it, and then paint it by calculating its magnitude (then, apply the log and finally normalize it in order to paint values between 0 and 1).
My result is, for the following image, the result I show you (with swap in order to have lower frequencies in the center of the image):
However, if I compare it to the result I obtain using other tools like Halcon, It seems incorrect to my since It seems to have really "high" values (the OpenCV DFT magnitude I mean):
I thought it might be for these reasons:
The difference between DFT (at OpenCV) and FFT (Halcon)
The operations I'm performing in order to show the magnitude in OpenCV.
The first one have as problem that it's quite hard for me to analyze, and OpenCV doesn't have a FFT function, as well as Halcon doesn't have a DFT function (if I'm not wrong of course), so I can't compare it directly.
The second one is in which I've been working the most time, but I still don't find the reason if it's there.
There's the code I'm using to paint the magnitude of img (which is my DFT image):
// 1.- To split the image in Re | Im values
Mat planes[] = {Mat_<float>(img), Mat::zeros(img.size(), CV_32F)};
// 2.- To magnitude + phase
split(img, planes);
// Calculate magnitude. I overwrite it, I know, but this is inside a function so it will be never used again, doesn't matter
magnitude(planes[0], planes[1], planes[0]);
// Magnitude Mat
Mat magI = planes[0];
// 3.- We add 1 to all them in order to perform the log
magI += Scalar::all(1); // switch to logarithmic scale
log(magI, magI);
// 4.- Swap the quadrants to center frequency
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
// swap quadrants (Top-Left with Bottom-Right)
Mat tmp;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
// swap quadrant (Top-Right with Bottom-Left)
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
// 5.- Normalize
// Transform the matrix with float values into a
// viewable image form (float between values 0 and 1).
normalize(magI, magI, 0, 1, CV_MINMAX);
// Paint it
imshow( "Magnitud DFT", magI);
So summarizing: any idea about why do I have this difference between these two magnitudes?

I'll summarize my comments into an answer.
When one thinks of doing a Fourier transform to work in the inverse domain, the assumption is that doing the inverse transform will return the same function/vector/whatever. In other words, we assume
This is the case with many programs and libraries (e.g. Mathematica, Matlab/octave, Eigen/unsupported/FFT, etc.). However, with many libraries (FFTW, KissFFT, etc.) this is not the case and there tends to be a scale
where s is usually the number of elements (m) in the array to the power of something (should be 1 if not scaled in a mismatched fashion in both the transform and the inverse). This is done in order to refrain from iterating over all m elements multiplying by a scale, which is often not important.
That being said, when looking at the scale in the inverse domain, various libraries that do scale the transforms have the liberty to use different scales for the transform and inverse transform. Common scaling pairs for the transform/inverse include {m^-1, m} and {m^-0.5, m^0.5}. Therefore, when comparing results from different libraries, we should be prepared to factors of m (scaled by m^-1 vs. not scaled), m^0.5 (scaled by m^-0.5 vs. not scaled and scaled by m^-1 vs. scaled by m^-0.5) or even other scales if other scaling factors were used.
Note: This scaling factor is not related to normalizing an array, such that all values are [0,1] or that the norm of the array is equal to 1.

Related

How to calculate the distance of two circles in a image by opencv

image with two circles
I have an image that include two fibers (presenting as two circles in the image). How can I calculate the distance of two fibers?
I find it hard to detect the position of the fiber. I have tried to use the HoughCircles function, but the parameters are hard to optimize and it cannot locate the circle precisely in most times. Should I subtract the background first or is there any other methods? MANY Thanks!
Unfortunately, you haven't shown your preprocessing steps. In my approach, I'll do the following:
Convert input image to grayscale (see cvtColor).
Median blurring, maintains the "edges" (see medianBlur).
Adaptive thresholding (see adaptiveTreshold).
Morphological opening to get rid of small noise (see morphologyEx).
Find circles by HoughCircles.
Not done here: Possible refinements of the found circles. Exclude too small or too large circles. Use all prior information you have on that! For example, how large can the circles be at all?
Here's my whole code:
// Read image.
cv::Mat img = cv::imread("images/i7aJJ.jpg", cv::IMREAD_COLOR);
// Convert to grayscale for processing.
cv::Mat blk;
cv::cvtColor(img, blk, cv::COLOR_BGR2GRAY);
// Median blurring to improve following thresholding.
cv::medianBlur(blk, blk, 11);
// Adaptive thresholding.
cv::adaptiveThreshold(blk, blk, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 51, -2);
// Morphological opening to get rid of small noise.
cv::morphologyEx(blk, blk, cv::MORPH_OPEN, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3)));
// Find circles using Hough transform.
std::vector<cv::Vec4f> circles;
cv::HoughCircles(blk, circles, cv::HOUGH_GRADIENT, 1.0, 300, 50, 25, 100);
// TODO: Refinement of found circles, if there are more than two.
// For example, calculate areas: Neglect too small or too large areas.
// Compare all areas, and keep the two with nearly matching areas and
// suitable areas.
// Draw circles in input image.
for (Vec4f& circle : circles) {
cv::circle(img, cv::Point(circle[0], circle[1]), circle[2], cv::Scalar(0, 0, 255), 4);
cv::circle(img, cv::Point(circle[0], circle[1]), 5, cv::Scalar(0, 255, 0), cv::FILLED);
}
// --- Assuming there are only the two right circles left from here. --- //
// Draw some debug output in input image.
const cv::Point c1 = cv::Point(circles[0][0], circles[0][1]);
const cv::Point c2 = cv::Point(circles[1][0], circles[1][1]);
cv::line(img, c1, c2, cv::Scalar(255, 0, 0), 2);
// Calculate distance, and put in input image.
double dist = cv::norm(c1 - c2);
cv::putText(img, std::to_string(dist), cv::Point((c1.x + c2.x) / 2 + 20, (c1.y + c2.y) / 2 + 20), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255, 0, 0));
The final output looks like this:
The intermediate image right before the HoughCircles operation looke like this:
In general, I'm not that skeptical about HoughCircles. You "just" have to pay attention to your preprocessing.
Hope that helps!
It's possible using hough circle detection but you should provide more images if you want a more stable detection. I just do denoising and go straight to circle detection. Using a non-local means denoising is pretty good at preserving edges which is in turn good for the canny edge algorithm included in the hough circle algorithm.
My code is written in Python but can easily be translated into C++.
import cv2
from matplotlib import pyplot as plt
IM_PATH = 'your image path'
DS = 2 # downsample the image
orig = cv2.imread(IM_PATH, cv2.IMREAD_GRAYSCALE)
orig = cv2.resize(orig, (orig.shape[1] // DS, orig.shape[0] // DS))
img = cv2.fastNlMeansDenoising(orig, h=3, templateWindowSize=20 // DS + 1, searchWindowSize=40 // DS + 1)
plt.imshow(orig, cmap='gray')
circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, dp=1, minDist=200 // DS, param1=40 // DS, param2=40 // DS, minRadius=210 // DS, maxRadius=270 // DS)
if circles is not None:
for x, y, r in circles[0]:
c = plt.Circle((x, y), r, fill=False, lw=1, ec='C1')
plt.gca().add_patch(c)
plt.gcf().set_size_inches((12, 8))
plt.show()
Important
Doing a bit of image processing is only the first step in a good (and stable!) object detection. You have to leverage every detail and property that you can get your hands on and apply some statistics to improve your results. For example:
Use Yves' approach as an addition and filter all detected circles that do not intersect the joints.
Is one circle always underneath the other? Filter out horizontally aligned pairs.
Can you reduce the ROI (are the circles always in a specific area in your image or can they be everywhere)?
Are both circles always the same size? Filter out pairs with different sizes.
...
If you can use multiple metrics you can apply a statistical model (ex. majority voting or knn) to find the best pair of circles.
Again: always think of what you know about your object, the environment and its behavior and take advantage of that knowledge.

Implementing Structured Tensor

I am trying to implement a paper called Structured Tensor Based Image Interpolation. In the paper what it does is the use structure tensor to classify each pixel in an image into three different classes (uniform, corners and edges) based on eigen values of a structured tensor.
To a achieve this I have written the following code:
void tensorComputation(Mat dx, Mat dy, Mat magnitude)
{
Mat dx2, dy2, dxy;
GaussianBlur(magnitude, magnitude, Size(3, 3), 0, 0, BORDER_DEFAULT);
// Calculate image derivatives
multiply(dx, dx, dx2);
multiply(dy, dy, dy2);
multiply(dx, dy, dxy);
Mat t(2, 2, CV_32F); // tensor matrix
// Insert values to the tensor matrix.
t.at<float>(0, 0) = sum(dx2)[0];
t.at<float>(0, 1) = sum(dxy)[0];
t.at<float>(1, 0) = sum(dxy)[0];
t.at<float>(1, 1) = sum(dy2)[0];
// eigen decomposition to get the main gradient direction.
Mat eigVal, eigVec;
eigen(t, eigVal, eigVec);
// This should compute the angle of the gradient direction based on the first eigenvector.
float* eVec1 = eigVec.ptr<float>(0);
float* eVec2 = eigVec.ptr<float>(1);
cout << fastAtan2(eVec1[0], eVec1[1]) << endl;
cout << fastAtan2(eVec2[0], eVec2[1]) << endl;
}
Here dx, dy, magnitude are derivative in x-axis, derivative in y- axis and magnitude of an image respectively.
What I know is I have found structured tensor for the entire image. But my problem is that I need to compute structured tensor for each pixel in an image. How to achieve this?
In your code you blur magnitude, but then don't use it. You don't need this magnitude at all.
You build the structure tensor correctly, but you average over the whole image. What you want to do is apply local averaging. For each pixel, the structure tensor is the average of your matrix over the pixels in the neighborhood. You compute this by applying a Gaussian blur to each of the components of the tensor: dx2, dy2, and dxy.
The larger the sigma of the Gaussian, the larger the neighborhood you average over. You get more regularization (less sensitive to noise) but also less resolution (less sensitive to small variations and short edges). Play around with the parameter until you get what you need. Sigma between 2 and 5 are quite common.
Next, you need to compute the eigendecomposition per pixel. I don't know if OpenCV makes this easy. I recommend you use DIPlib 3 instead. It has the right infrastructure to compute and use the structure tensor. See here how easy it can be.

strange gaussianBlur result offset of kernel multiplication unwantedly padded.

You can see the result in the image below. The original image is just a grey pixel, the result should be that but blurred.
Opencv is not using the immediate neighboring pixels for the Gaussian Blur, I'm guessing it's doing some sort of internal padding. Why it is doing so I have no idea, my initial guess would be that it assumes that the vector has more than one channel, which is not the case. Here is how i create the cv::Mats for calculation and how i call cv::gausianBlurr
std::vector<float> sobelCopy (sobel);
cv::Mat sobel_mat_copy(height,
width,
CV_32F,
sobelCopy.data());
cv::Mat sobel_mat(height,
width,
CV_32F,
sobel.data());
cv::GaussianBlur(sobel_mat_copy, sobel_mat, cv::Size(3,3), 0.0, 0.0, cv::BORDER_CONSTANT);
Image
Fixed, it has all to with my how i ordered my vector, i had column major, cv::Mat assumes it is row major ordering.

Matching small grayscale images

I want to test whether two images match. Partial matches also interest me.
The problem is that the images suffer from strong noise. Another problem is that the images might be rotated with an unknown angle. The objects shown in the images will roughly always have the same scale!
The images show area scans from a top-shot perspective. "Lines" are mostly walls and other objects are mostly trees and different kinds of plants.
Another problem was, that the left image was very blurry and the right one's lines were very thin.
To compensate for this difference I used dilation. The resulting images are the ones I uploaded.
Although It can easily be seen that these images match almost perfectly I cannot convince my algorithm of this fact.
My first idea was a feature based matching, but the matches are horrible. It only worked for a rotation angle of -90°, 0° and 90°. Although most descriptors are rotation invariant (in past projects they really were), the rotation invariance seems to fail for this example.
My second idea was to split the images into several smaller segments and to use template matching. So I segmented the images and, again, for the human eye they are pretty easy to match. The goal of this step was to segment the different walls and trees/plants.
The upper row are parts of the left, and the lower are parts of the right image. After the segmentation the segments were dilated again.
As already mentioned: Template matching failed, as did contour based template matching and contour matching.
I think the dilation of the images was very important, because it was nearly impossible for the human eye to match the segments without dilation before the segmentation. Another dilation after the segmentation made this even less difficult.
Your first job should be to fix the orientation. I am not sure what is the best algorithm to do that but here is an approach I would use: fix one of the images and start rotating the other. For each rotation compute a histogram for the color intense on each of the rows/columns. Compute some distance between the resulting vectors(e.g. use cross product). Choose the rotation that results in smallest cross product. It may be good idea to combine this approach with hill climbing.
Once you have the images aligned in approximately the same direction, I believe matching should be easier. As the two images are supposed to be at the same scale, compute something analogous to the geometrical center for both images: compute weighted sum of all pixels - a completely white pixel would have a weight of 1, and a completely black - weight 0, the sum should be a vector of size 2(x and y coordinate). After that divide those values by the dimensions of the image and call this "geometrical center of the image". Overlay the two images in a way that the two centers coincide and then once more compute cross product for the difference between the images. I would say this should be their difference.
You can also try following methods to find rotation and similarity.
Use image moments to get the rotation as shown here.
Once you rotate the image, use cross-correlation to evaluate the similarity.
EDIT
I tried this with OpenCV and C++ for the two sample images. I'm posting the code and results below as it seems to work well at least for the given samples.
Here's the function to calculate the orientation vector using image moments:
Mat orientVec(Mat& im)
{
Moments m = moments(im);
double cov[4] = {m.mu20/m.m00, m.mu11/m.m00, m.mu11/m.m00, m.mu02/m.m00};
Mat covMat(2, 2, CV_64F, cov);
Mat evals, evecs;
eigen(covMat, evals, evecs);
return evecs.row(0);
}
Rotate and match sample images:
Mat im1 = imread(INPUT_FOLDER_PATH + string("WojUi.png"), 0);
Mat im2 = imread(INPUT_FOLDER_PATH + string("XbrsV.png"), 0);
// get the orientation vector
Mat v1 = orientVec(im1);
Mat v2 = orientVec(im2);
double angle = acos(v1.dot(v2))*180/CV_PI;
// rotate im2. try rotating with -angle and +angle. here using -angle
Mat rot = getRotationMatrix2D(Point(im2.cols/2, im2.rows/2), -angle, 1.0);
Mat im2Rot;
warpAffine(im2, im2Rot, rot, Size(im2.rows, im2.cols));
// add a border to rotated image
int borderSize = im1.rows > im2.cols ? im1.rows/2 + 1 : im1.cols/2 + 1;
Mat im2RotBorder;
copyMakeBorder(im2Rot, im2RotBorder, borderSize, borderSize, borderSize, borderSize,
BORDER_CONSTANT, Scalar(0, 0, 0));
// normalized cross-correlation
Mat& image = im2RotBorder;
Mat& templ = im1;
Mat nxcor;
matchTemplate(image, templ, nxcor, CV_TM_CCOEFF_NORMED);
// take the max
double max;
Point maxPt;
minMaxLoc(nxcor, NULL, &max, NULL, &maxPt);
// draw the match
Mat rgb;
cvtColor(image, rgb, CV_GRAY2BGR);
rectangle(rgb, maxPt, Point(maxPt.x+templ.cols-1, maxPt.y+templ.rows-1), Scalar(0, 255, 255), 2);
cout << "max: " << max << endl;
With -angle rotation in code, I get max = 0.758. Below is the rotated image in this case with the matching region.
Otherwise max = 0.293

Differences between Matlab FFT and Opencv DFT c++ code [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I'm trying to convert code from Matlab to OpenCV,
I apply FFT\DFT on top of an image, multiply by amplitude (scale it) than bring the image back using dft.
I receive different results using matlab fft and Opencv DFT, which is exacly what am I trying to solve here.
What am I trying to solve here is the following:
I'd like to receive an identical results to the matlab, but still using an optimal DFT size in the opencv command.
In matlab I pad the image for the FFT usage, 2*N + 1 .
In opencv I pad the image in the size given to me by the function called:
getOptimalDFTSize
What's really bother me is the fact that if I discard the getOptimalDFTSize command in opencv, and use 2*N +1 I receive an identical result to the Matlab.
But if I use the getOptimalDFTSize command, the results are totally different.
I insist to use the getOptimalDFTSize because it should make my code faster.
The matlab code:
building the filter
[m,n] = size(img);
%M = 2*m+1; N = n*2+1;
M = 2*m; N = n*2;
[X, Y] = meshgrid(1:N,1:M);
CentX = ceil(N/2);
CentY = ceil(M/2);
dist = (X-CentX).*(X-CentX)+(Y-CentY).*(Y-CentY);
H = (GammaH-GammaL)*(1-exp(-dist/(2*D0.^2)))+GammaL;
% applying the filter
IMG =fft2(Limg,M,N);
H = fftshift(H);
Iret = real(ifft2(IMG.*H));
Iret = Iret(1:m,1:n);
Iend = exp(Iret)-1;
My Opencv c++ code
//--------------------------UserInputStart
double m_GammaL = 0.02;
double m_GammaH = 1.2;
double m_D0 = 30;
//--------------------------UserInputEnd
int imageType = /*CV_64FC1 */CV_32F;
// Convert the input image into float
Mat inputImage32f;
inputImage8uchar->convertTo(inputImage32f,imageType);
// Convert the image from 0 to 1 scale , matlab corespondance : Limg = log(im2double(img)+1);
inputImage32f = inputImage32f / 255;
inputImage32f = inputImage32f + 1;
// Create a log image
Mat logImage;
cv::log(inputImage32f,logImage);
// Apply the filter
// Transform the image into the special domain
Mat paddedLogImage; //expand input image to optimal size
int m = getOptimalDFTSize( logImage.rows );
int n = getOptimalDFTSize( logImage.cols ); // on the border add zero pixels
copyMakeBorder(logImage, paddedLogImage, 0, m - logImage.rows, 0, n - logImage.cols,
BORDER_CONSTANT, Scalar::all(0));
Mat homoFilter(paddedLogImage.rows,paddedLogImage.cols,imageType);
Mat* p_homoFilter = &homoFilter;
// Handle the homomorphic filter
CreateHomomorphFilter(p_homoFilter,paddedLogImage.cols,paddedLogImage.rows,m_GammaL, m_GammaH , m_D0);
// Shift the filter
(*p_homoFilter) = (*p_homoFilter)(Rect(0, 0, (*p_homoFilter).cols & -2, (*p_homoFilter).rows & -2));
int cx = (*p_homoFilter).cols/2;
int cy = (*p_homoFilter).rows/2;
Mat q0((*p_homoFilter), Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
Mat q1((*p_homoFilter), Rect(cx, 0, cx, cy)); // Top-Right
Mat q2((*p_homoFilter), Rect(0, cy, cx, cy)); // Bottom-Left
Mat q3((*p_homoFilter), Rect(cx, cy, cx, cy)); // Bottom-Right
// swap quadrants (Top-Left with Bottom-Right)
Mat tmp;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
// swap quadrant (Top-Right with Bottom-Left)
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
Mat complexI;
Mat planes[] = {Mat_<float>(paddedLogImage), Mat::zeros(paddedLogImage.size(), imageType)};
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
// this way the result may fit in the source matrix
dft(paddedLogImage, complexI,/*cv::DFT_SCALE|*/cv::DFT_COMPLEX_OUTPUT);
split(complexI, planes);
//planes[0] = planes[0].mul((*p_homoFilter));
multiply(planes[0],(*p_homoFilter),planes[0],1,imageType);
multiply(planes[1],(*p_homoFilter),planes[1],1,imageType);
merge(planes, 2, complexI);
// Back to image scope
idft(complexI, complexI,cv::DFT_SCALE|cv::DFT_INVERSE/*|cv::DFT_REAL_OUTPUT*/);
split(complexI, planes);
// Use only the planes[0] which is the real number
complexI = planes[0];
// Crop the relevant image values
Mat outputImage32f(complexI, Rect(0, 0, inputImage8uchar->cols,inputImage8uchar->rows));
What am I trying to solve here is the following: I'd like to receive an identical results to the matlab, but still using an optimal DFT size in the opencv command.
Regarding to the opencv command , Get optimal DFT size
getOptimalDFTSize
Returns the optimal DFT size for a given vector size.
DFT performance is not a monotonic function of a vector size. Therefore, when you calculate convolution of two arrays or perform the spectral analysis of an array, it usually makes sense to pad the input data with zeros to get a bit larger array that can be transformed much faster than the original one. Arrays whose size is a power-of-two (2, 4, 8, 16, 32, ...) are the fastest to process. Though, the arrays whose size is a product of 2’s, 3’s, and 5’s (for example, 300 = 5*5*3*2*2) are also processed quite efficiently.
The function getOptimalDFTSize returns the minimum number N that is greater than or equal to vecsize so that the DFT of a vector of size N can be processed efficiently. In the current implementation N = 2 p * 3 q * 5 r for some integer p, q, r.
Solved
It took me a while to solve this problem , eventually there was no differences in the code, however there was a difference in the sampling rate between Matlab and OpenCV.
If I'd change the sampling rate in OpenCV things would have worked out but I'd have to pay in higher running time
I guess Opencv supported DFT vs Matlab with FFT standard