the problem is to fourie transform ( cv::dft ) a signal with fourie descriptors. So the mat should be complex numbers :(
But my problem is how can make a mat with complex numbers ?
Please help me to find an example or any other that show me how to store a complex number(RE + IM) to a mat ?
Is there a way to use merge ?
merge()
I found an answer saying:
I think you can use merge() function here, See the Documentation
It says : Composes a multi-channel array from several single-channel arrays.
Reference: How to store complex numbers in OpenCV matrix?
look at the nice dft sample in the opencv repo, also at the dft tutorial
so, if you have a Mat real, and a Mat imag (both of type CV_32FC1):
Mat planes[] = {real,imag};
Mat complexImg;
merge(planes, 2, complexImg); // complexImg is of type CV_32FC2 now
dft(complexImg, complexImg);
split(complexImg, planes);
// real=planes[0], imag=planes[1];
Related
While reading the image with IMREAD_COLOR, 'dft' function throws the error:
DFT function works just fine when reading an image with IMREAD_GRAYSCALE. But I want to read the image with IMREAD_COLOR.
main function
const char* filename = "face.jpg";
Mat I = imread(filename, IMREAD_COLOR);
if(I.empty()) return 0;
Mat padded;
I.convertTo(padded, CV_32F);
Mat fft;
Mat planes[2];
dft(padded, fft, DFT_SCALE|DFT_COMPLEX_OUTPUT);
Mat fftBlur = fft.clone();
fftBlur *= 0.5;
split(fftBlur, planes);
Mat ph, mag;
mag.zeros(planes[0].rows, planes[0].cols, CV_32F);
ph.zeros(planes[0].rows, planes[0].cols, CV_32F);
cartToPolar(planes[0], planes[1], mag, ph);
merge(planes, 2, fftBlur);
//inverse
Mat invfft;
dft(fftBlur, invfft, DFT_INVERSE|DFT_REAL_OUTPUT);
Mat result;
invfft.convertTo(result, CV_8U);
Mat image;
cvtColor(result, image, COLOR_GRAY2RGB);
imshow("Output", result);
imshow("Image", image);
waitKey();
The message you receive is an assertion it tells you DFT function only takes single precision floating point image with one or two channels (CV_32FC1, CV_32FC2, the letter C at the end of the flag mean channel) or double precision floating point images with one or two channels (CV_64FC1, CV_64FC2).
The two channel case is actually the representation of complex image in OpenCV data storage.
If you want you can split you image to std::vector<cv::Mat> where each element does represent one channel, using cv::split apply the DFT on each channels do the processing you want on it and recreate an multichannel image thanks to cv::merge.
From Learning OpenCV (about dft function):
The input array must be of floating-point type and may be single- or double-channel. In the single-channel case, the entries are assumed to be real numbers, and the output will be packed in a special space-saving format called complex conjugate symmetrical.
The same question is mentioned here in terms of matlab image processing.
You can check out cv::split function if you want to separate channels of your initial image.
Not quite understanding why this code works:
cv::Mat img = cv::imread('pic.jpg', -1);
cv::Mat padded;
std::uint16_t m = cv::getOptimalDFTSize(img.rows); // This will be 256
std::uint16_t n = cv::getOptimalDFTSize(img.cols); // This will be 256
cv::copyMakeBorder(img, padded, 0, m - img.rows, 0, n - img.cols,
cv::BORDER_CONSTANT, cv::Scalar::all(0)); // With my inputs, this effectively just copies img into padded
cv::Mat planes[] = { cv::Mat_<float>(padded),cv:: Mat::zeros(padded.size(), CV_32F) };
cv::Mat dft_img;
cv::merge(planes, 2, dft_img);
cv::dft(dft_img, dft_img);
cv::split(dft_img, planes);
But this breaks with an exception in memory:
cv::Mat img = cv::imread('pic.jpg', -1); // I know this image is 256x256
cv::Mat dft_img = cv::Mat::zeros(256,256,CV_32F); // Hard coding for simplicity atm
cv::dft(img,dft_img);
I'm having trouble understanding the documentation for dft() https://docs.opencv.org/2.4/modules/core/doc/operations_on_arrays.html#dft,
and other functions and classes for that matter.
I think it has something to do with dft_img not being a multichannel array in the second segment, but I'm lost on how to initialize such an array short of copying the first segment of code.
Secondly, when trying to access either planes[0] or planes[1] and modify their values with:
planes[0].at<double>(indexi,indexj) = 0;
I get another exception in memory, though I also see a new page that says mat.inl.hpp not found. Using Visual Studio, OpenCV 3.4.3, a novice with C++ but intermediate with signal processing, any help is appreciated.
You did not specify what exception you got, but an important point is that input of the dft function must be a floating point number, either 32 bits or 64 bits floating point number. Another point is that try not to use raw arrays if you are not comfortable with c++. I would even suggest that if using c++ is not mandotary, prefer python for OpenCV. Here is a working example dft code:
// read your image
cv::Mat img = cv::imread("a2.jpg", CV_LOAD_IMAGE_GRAYSCALE); // I know this image is 256x256
// convert it to floating point
//normalization is optional(depends on library and you I guess?)
cv::Mat floatImage;
img.convertTo(floatImage, CV_32FC1, 1.0/255.0);
// create a placeholder Mat variable to hold output of dft
std::vector<cv::Mat> dftOutputs;
dftOutputs.push_back(floatImage);
dftOutputs.push_back(cv::Mat::zeros(floatImage.size(), CV_32F));
cv::Mat dftOutput;
cv::merge(dftOutputs, dftOutput);
// perform dft
cv::dft(dftOutput, dftOutput);
// separate real and complex outputs back
cv::split(dftOutput, dftOutputs);
I changed code from the tutorial a little to make it easier to understand. If you would like to obtain magnitude image and such, you can follow the tutorial after split function.
Instead of OpenCV's normal dft, I'd like to use cuda::dft. As a start I tried performing a forward and inverse transform, but the result doesn't look anything like the input. Here's a minimal example using an OpenCV example image:
// Load 8bit test image (https://raw.githubusercontent.com/opencv/opencv/master/samples/data/basketball1.png)
Mat testImg;
testImg = imread("basketball1.png", CV_LOAD_IMAGE_GRAYSCALE);
// Convert input to complex float image
Mat_<float> imgReal;
testImg.convertTo(imgReal, CV_32F, 1.0/255.0);
Mat imgImag = Mat(imgReal.rows, imgReal.cols, CV_32F, float(0));
vector<Mat> channels;
channels.push_back(imgReal);
channels.push_back(imgImag);
Mat imgComplex;
merge(channels,imgComplex);
imshow("Img real", imgReal);
waitKey(0);
//Perform a Fourier transform
cuda::GpuMat imgGpu, fftGpu;
imgGpu.upload(imgComplex);
cuda::dft(imgGpu, fftGpu, imgGpu.size());
//Performs an inverse Fourier transform
cuda::GpuMat propGpu, convFftGpu;
cuda::dft(fftGpu, propGpu, imgGpu.size(), DFT_REAL_OUTPUT | DFT_SCALE);
Mat output(propGpu);
output.convertTo(output, CV_8U, 255, 0);
imshow("Output", output);
waitKey(0);
I played with the flags but output never looks anything like input. Using the above code I get as output:
While it should look like this:
I found the answer here. Apparently, when starting with a complex input image, it's not possible to use the flag DFT_REAL_OUTPUT.
Either you do the forward transform with a one channel float input and then you get the same as an output from the inverse transform, or you start with a two channel complex input image and get that type as output. The upside to using a complex input image is that the forward transform delivers the full sized complex field to work with, e.g. perform a convolution (see linked answer for details).
I'll add that in order to obtain an 8bit image from the inverse transform, compute the magnitude yourself like so:
Mat output(propGpu);
Mat planes[2];
split(output,planes);
Mat mag;
magnitude(planes[0],planes[1],mag);
mag.convertTo(mag, CV_8U, 255, 0);
To go into Fourier domain using OpenCV Cuda FFT and back into the spatial domain, you can simply follow the below example (to learn more, you can refer to cufft documentation, on which OpenCV Cuda FFT source code is based).
Mat test_im;
test_im = imread("frame.png", IMREAD_GRAYSCALE);
// Convert input input to real value type (CV_64F for double precision)
Mat im_real;
test_im.convertTo(im_real, CV_32F, 1.0/255.0);
imshow("Input Image", im_real);
waitKey(0);
// Perform The Fourier Transform
cuda::GpuMat in_im_gpu, fft_im;
in_im_gpu.upload(im_real);
cuda::dft(in_im_gpu, fft_im, in_im_gpu.size(), 0);
// Performs an inverse Fourier transform
cuda::GpuMat ifft_im_gpu;
//! int odd_size = imgGpu.size().width % 2;
//! cv::Size dest_size((fftGpu.size().width-1)*2 + (odd_size ? 1 : 0), fftGpu.size().height);
cv::Size dest_size = in_im_gpu.size();
int flag = (DFT_SCALE + DFT_REAL_OUTPUT) | DFT_INVERSE;
cuda::dft(fft_im, ifft_im_gpu, dest_size, flag);
Mat ifft_im(ifft_im_gpu);
ifft_im.convertTo(ifft_im, CV_8U, 255, 0);
imshow("Inverse FFT", ifft_im);
waitKey(0);
I am computing the mean image of two images and don't know the correct method to use the function mean() in OpenCV.
Mat img1,img2,img3;
img1=imread("picture1.jpg");
img2=imread("picture2.jpg");
img3=mean(img1,img2);
However it says
R6010
-abort() has been recalled
How can I get the average of img1 & img2?
Thanks.
You could use cv::accumulate :
Mat img3 = Mat::zeros(img1.size(), CV_32F); //larger depth to avoid saturation
cv::accumulate(img1, img3);
cv::accumulate(img2, img3);
img3 = img3/2;
According to opencv documentation :
"The function mean calculates the mean value M of array elements, independently for each channel, and return it:"
This mean it should return you a scalar for each layer of you image, and the second parameter is a mask of pixels to where to perform computation
have you simply tried to do something like this ?
img3 = (img1+img2) * 0.5;
[EDIT] to avoid some losses if values are > 255, you probably should convert your images to CV_32F, before performing computations, then cast the result of you operation into CV_8U using the cv::convertTo opencv documentation on ConvertTo
In OpenCV, if I have a Mat img that contains uchar data, how do I convert the data into float? Is there a function available? Thank you.
If you meant c++ then you have
#include<opencv2/opencv.hpp>
using namespace cv;
Mat img;
img.create(2,2,CV_8UC1);
Mat img2;
img.convertTo(img2, CV_32FC1); // or CV_32F works (too)
details in opencv2refman.pdf.
UPDATE:
CV_32FC1 is for 1-channel (C1, i.e. grey image) float valued (32F) pixels
CV_8UC1 is for 1-channel (C1, i.e. grey image) unsigned char (8UC) valued ones.
UPDATE 2:
According to Arthur Tacca, only CV_32F is correct (or presumably CV_8U), since convertTo should not change the number of channels. It sounds logical right? Nevertheless, when I have checked opencv reference manual, I could not find any info about this, but I agree with him.
Use cvConvert function. In Python:
import cv
m = cv.CreateMat(2, 2, cv.CV_8UC1)
m1 = cv.CreateMat(2, 2, cv.CV_32FC1)
cv.Convert(m, m1)