OpenCV and working with Fourier transform dft() - c++

I am trying to point-wise multiply fourier transforms of two separate images and then convert back to a normal image. I'm not very familiar with using the fourier transform in OpenCV but this is what I have at the moment. The last line where the output is shown causes an exception of type 'System.Runtime.InteropServices.SEHException' but I can't figure out how to fix it. I have tried various different parameters and functions at each stage but all seem either give an exception or an empty output. What am I doing wrong? Thanks for any help you can give me!
Mat dftInput1, dftImage1, dftInput2, dftImage2, multipliedDFT, inverseDFT, inverseDFTconverted;
image1.convertTo(dftInput1, CV_32F);
dft(dftInput1, dftImage1, DFT_COMPLEX_OUTPUT);
image2.convertTo(dftInput2, CV_32F);
dft(dftInput2, dftImage2, DFT_COMPLEX_OUTPUT);
multiply(dftImage1, dftImage2, multipliedDFT);
idft(multipliedDFT, inverseDFT, DFT_SCALE);
inverseDFT.convertTo(inverseDFTconverted, CV_8U);
imshow("Output", inverseDFTconverted);

imshow can't show 2 channel images, only 1,3,4 channel ones.
if you use DFT_COMPLEX_OUTPUT for the dft, you get a 2 channel image, applying the reverse idft again produces a 2channel(complex) Mat
no idea, why you get a 'System.Runtime.InteropServices.SEHException' though ( is that 'managed c++' ? )
convertTo() changes the type of the channels, but not their count (yea, surprise).
so, either restrict it to the real part:
idft(multipliedDFT, inverseDFT, CV_DFT_SCALE | CV_DFT_REAL_OUTPUT );
or split it , and throw only the real part at imshow:
Mat chan[2];
split( inverseDFTconverted, chan );
imshow("lalala", chan[0]);

Related

How to align 2 images based on their content with OpenCV

I am totally new to OpenCV and I have started to dive into it. But I'd need a little bit of help.
So I want to combine these 2 images:
I would like the 2 images to match along their edges (ignoring the very right part of the image for now)
Can anyone please point me into the right direction? I have tried using the findTransformECC function. Here's my implementation:
cv::Mat im1 = [imageArray[1] CVMat3];
cv::Mat im2 = [imageArray[0] CVMat3];
// Convert images to gray scale;
cv::Mat im1_gray, im2_gray;
cvtColor(im1, im1_gray, CV_BGR2GRAY);
cvtColor(im2, im2_gray, CV_BGR2GRAY);
// Define the motion model
const int warp_mode = cv::MOTION_AFFINE;
// Set a 2x3 or 3x3 warp matrix depending on the motion model.
cv::Mat warp_matrix;
// Initialize the matrix to identity
if ( warp_mode == cv::MOTION_HOMOGRAPHY )
warp_matrix = cv::Mat::eye(3, 3, CV_32F);
else
warp_matrix = cv::Mat::eye(2, 3, CV_32F);
// Specify the number of iterations.
int number_of_iterations = 50;
// Specify the threshold of the increment
// in the correlation coefficient between two iterations
double termination_eps = 1e-10;
// Define termination criteria
cv::TermCriteria criteria (cv::TermCriteria::COUNT+cv::TermCriteria::EPS, number_of_iterations, termination_eps);
// Run the ECC algorithm. The results are stored in warp_matrix.
findTransformECC(
im1_gray,
im2_gray,
warp_matrix,
warp_mode,
criteria
);
// Storage for warped image.
cv::Mat im2_aligned;
if (warp_mode != cv::MOTION_HOMOGRAPHY)
// Use warpAffine for Translation, Euclidean and Affine
warpAffine(im2, im2_aligned, warp_matrix, im1.size(), cv::INTER_LINEAR + cv::WARP_INVERSE_MAP);
else
// Use warpPerspective for Homography
warpPerspective (im2, im2_aligned, warp_matrix, im1.size(),cv::INTER_LINEAR + cv::WARP_INVERSE_MAP);
UIImage* result = [UIImage imageWithCVMat:im2_aligned];
return result;
I have tried playing around with the termination_eps and number_of_iterations and increased/decreased those values, but they didn't really make a big difference.
So here's the result:
What can I do to improve my result?
EDIT: I have marked the problematic edges with red circles. The goal is to warp the bottom image and make it match with the lines from the image above:
I did a little bit of research and I'm afraid the findTransformECC function won't give me the result I'd like to have :-(
Something important to add:
I actually have an array of those image "stripes", 8 in this case, they all look similar to the images shown here and they all need to be processed to match the line. I have tried experimenting with the stitch function of OpenCV, but the results were horrible.
EDIT:
Here are the 3 source images:
The result should be something like this:
I transformed every image along the lines that should match. Lines that are too far away from each other can be ignored (the shadow and the piece of road on the right portion of the image)
By your images, it seems that they overlap. Since you said the stitch function didn't get you the desired results, implement your own stitching. I'm trying to do something close to that too. Here is a tutorial on how to implement it in c++: https://ramsrigoutham.com/2012/11/22/panorama-image-stitching-in-opencv/
You can use Hough algorithm with high threshold on two images and then compare the vertical lines on both of them - most of them should be shifted a bit, but keep the angle.
This is what I've got from running this algorithm on one of the pictures:
Filtering out horizontal lines should be easy(as they are represented as Vec4i), and then you can align the remaining lines together.
Here is the example of using it in OpenCV's documentation.
UPDATE: another thought. Aligning the lines together can be done with the concept similar to how cross-correlation function works. Doesn't matter if picture 1 has 10 lines, and picture 2 has 100 lines, position of shift with most lines aligned(which is, mostly, the maximum for CCF) should be pretty close to the answer, though this might require some tweaking - for example giving weight to every line based on its length, angle, etc. Computer vision never has a direct way, huh :)
UPDATE 2: I actually wonder if taking bottom pixels line of top image as an array 1 and top pixels line of bottom image as array 2 and running general CCF over them, then using its maximum as shift could work too... But I think it would be a known method if it worked good.

OpenCv, get image information

I am playing around with an open source openCv application. With the provided image sets, it works great, but when I attempt to pass it a live camera stream, or even recorded frames from that camera stream, it crashes. I assume that this is to do with the cv::Mat type, differing image channels, or some conversion that i am not doing.
The provided dataset is grey-scale, 8 bit, and so are my images.
The application expects grayscale (CV_8U).
My question is:
Given one of the (working) provided images, and one of my recorded (not working) images, what is the best way to compare them using opencv, to find out what the difference might be that is causing my crashes?
Thank you.
I have tried:
Commenting out this code (Which gave assertion errors)
if(mImGray.channels()==3)
{
cvtColor(mImGray,mImGray,CV_BGR2GRAY);
cvtColor(imGrayRight,imGrayRight,CV_BGR2GRAY);
}
else if(mImGray.channels()==4)
{
cvtColor(mImGray,mImGray,CV_BGRA2GRAY);
cvtColor(imGrayRight,imGrayRight,CV_BGRA2GRAY);
}
And replacing it with:
cv::Mat TempL;
mImGray.convertTo(TempL, CV_8U);
cvtColor(TempL, mImGray, CV_BayerGR2BGR);
cvtColor(mImGray, mImGray, CV_BGR2GRAY);
And the program crashes with no error...
You can try this code:
if ( mImGray.depth() != CV_8U )
mImGray.convertTo(mImGray, CV_8U);
if (mImGray.channels() == 3 )
{
cvtColor(mImGray, mImGray, COLOR_BGR2GRAY);
}
Or you can define a new Mat with create function and use that.

Why does setTo not work (assertion failed)?

I am just learning OpenCV and, since I have some experience with Matlab's logical indexing, I was really interested to see the matrix method setTo. My initial attempt doesn't work though, and I can't work out why, so I'd be very grateful for your help!
I have a Mat containing image data, and want to set all values greater than 10 to zero. So, I did:
Mat not_relevant = abs(difference - frame2) > 10;
difference = difference.setTo(0, not_relevant);
This however gives me:
OpenCV Error: Assertion failed (mask.empty() || mask.type() == CV_8U) in
cv::Mat::setTo, file
C:\builds\2_4_PackSlave-win32-vc12-shared\opencv\modules\core\src\copy.cpp, line 347
I have tried converting not_relevant, difference and frame2 before doing this using, e.g.:
frame2.convertTo(frame2, CV_8UC1);
but that did not fix the error, so I'm not sure what else I could try. Does anyone have any idea what might be wrong?
Thank you for your help!
I think the error is pretty clear.type of your mask image should be CV_8U.
so you need to convert not_relevent to grayscale.
Mat not_relevant = abs(difference - frame2) > 10;
cv::cvtColor(not_relevant, not_relevant, CV_BGR2GRAY);
difference = difference.setTo(0, not_relevant);
Why convertTo does not work here ?
CV_8U(or CV_8UC1) is type of image with one channel of uchar values.
convertTo can not change number of channels in image.
So converting image with more than one channel to CV_8U using convertTo does not work .
check this answer for more detailed explanations.

OpenCV: findContours exception

my matlab code is:
h = fspecial('average', filterSize);
imageData = imfilter(imageData, h, 'replicate');
bwImg = im2bw(imageData, grayThresh);
cDist=regionprops(bwImg, 'Area');
cDist=[cDist.Area];
opencv code is:
cv::blur(dst, dst,cv::Size(filterSize,filterSize));
dst = im2bw(dst, grayThresh);
cv::vector<cv::vector<cv::Point> > contours;
cv::vector<cv::Vec4i> hierarchy;
cv::findContours(dst,contours,hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
here is my image2blackand white function
cv::Mat AutomaticMacbethDetection::im2bw(cv::Mat src, double grayThresh)
{
cv::Mat dst;
cv::threshold(src, dst, grayThresh, 1, CV_THRESH_BINARY);
return dst;
}
I'm getting an exception in findContours() C++ exception: cv::Exception at memory location 0x0000003F6E09E0A0.
Can you please explain what am I doing wrong.
dst is cv::Mat and I used it all along it has my original values.
Update here is my matrix written into *.txt file:
http://www.filedropper.com/gili
UPDATE 2:
I have added dst.convertTo(dst,CV_8U); like Micka suggested, I no longer have an exception. however values are nothing like expected.
Take a look at this question which has a similar problem to what you're encountering: Matlab and OpenCV calculate different image moment m00 for the same image.
Basically, the OP in the linked post is trying to find the zeroth image moment for both x and y of all closed contours - which is actually just the area, by using findContours in OpenCV and regionprops in MATLAB. In MATLAB, that can be accessed by the Area property from regionprops, and judging from your MATLAB code, you wish to find the same quantity.
From the post, there is most certainly a difference between how OpenCV and MATLAB finds contours in an image. This boils down to the way both platforms consider what is a "connected pixel". OpenCV only uses a four-pixel neighbourhood while MATLAB uses an eight-pixel neighbourhood.
As such, there is nothing wrong with your implementation, and converting to 8UC1 is good. However, the areas (and ultimately the total number of connected components and contours themselves) between both contours found with MATLAB and OpenCV are not the same. The only way for you to get exactly the same result is if you manually draw the contours found by findContours on a black image, and using the cv::moments function directly on this image.
However, because of the differing implementations of cv::blur() in comparison to fspecial with an averaging mask that is even, you still may not be able to get the same results along the borders of the image. If there are no important contours around the borders of your image, then hopefully this will give you the right result.
Good luck!

Why does openCV's convertto function not work?

I have an image which has 4 channels and is in 4 * UINT8 format.
I am trying to convert it to 3 channel float and I am using this code:
images.convertTo(images,CV_32FC3,1/255.0);
After the conversion, the image is in a float format but still has 4 channels. How can I get rid of 4th (alpha) channel in OpenCV?
As #AldurDisciple said, Mat::convertTo() is intended to be used for changing the data type of a Mat, not for changing the number of channels.
To work out, you should split it into two steps:
cvtColor(image, image, CV_BGRA2BGR); // 1. change the number of channels
image.convertTo(image, CV_32FC3, 1/255.0); // 2. change type to float and scale
The function convertTo is intended to be used to change the data type of a Mat, exclusively. As mentionned in the documentation (link), the number of channels of the output image is always the same as the input image.
If you want to change the datatype and reduce the number of channels, you should use a combination of split, merge, and convertTo:
cv::Mat img_8UC4;
cv::Mat chans[4];
cv::split(img_8UC4,chans);
cv::Mat img_8UC3;
cv::merge(chans,3,img_8UC3);
cv::Mat img_32FC3;
img_8UC3.convertTo(img_32FC3);
Another approach may be to recode the algorithm yourself, which is quite easy and probably more efficient.
OpenCV's cvtColor function allows you to convert the type and number of channels of a Mat.
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0 )
So something like this would convert a colored 4 channel to a colored 3 channel:
cvtColor(image, image, CV_BGRA2BGR, 3);
Or it is probably more efficient to use the mixChannels function, if you check the documentation its example shows how to split a channel out.
Then if you really want to change it to a specific type:
image.convertTo(image,CV_32F);