OpenCV - Filling empty spaces in object using c++ - c++

How can I fill empty space of object in OpenCV ?
Let me clarify my question.
I have an image below
Now I want to fill all the gaps in the image like this :
in Matlab I have done it by convex hull, but I don't know how to do it in C++.
Thanks.

Try morphological operations. If you go this way, note, that you may vary either kernel size (increase to decrease number of iterations), or iterations (more iterations will eliminate empty space even if kernel is small), or both.
cv::Mat img = cv::imread("cwyX5.jpeg");
cv::imshow("image", img);
cv::Size kernelSize(5, 5);
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, kernelSize);
cv::Mat result;
int iterations = 3;
cv::morphologyEx(img, result, cv::MORPH_OPEN, kernel, cv::Point(-1,-1), iterations);
cv::imshow("result", result);
cv::waitKey();

Related

OpenCV C++, trouble understanding starter code for DFT

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.

How to use the OpenCV CUDA Fourier Transform

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);

Pad a Mat kernel with zeros to the size of Image

Using OpenCv in cpp
i need to increase the size of the given Mat kernel to the size of any given image by padding it with extra zeroes.
Mat kernel = (Mat_<double>(3,3) << 1.36, 0.062, -0.921,
-0.644198, 1.10, -0.17,
-0.072951, -1.81485, 2.806);
I found the way in this post to pad the whole array with zeroes but i want to save the values in kernel as well.
it is as simple as below.
// this is the input image
cv::Mat img(100, 100, CV_64FC1);
cv::Mat kernel = (cv::Mat_<double>(3, 3) << 1.36, 0.062, -0.921,
-0.644198, 1.10, -0.17,
-0.072951, -1.81485, 2.806);
cv::Mat kernelNew = cv::Mat::zeros(img.size(), CV_64FC1);
kernel.copyTo(kernelNew(cv::Rect(0, 0, kernel.cols, kernel.rows)));
kernel = kernelNew;

How to get clean edge without broken lines?

I'm working on a binary image to get its edge. I used cannyedge function from opencv but the result is less desirable.
Click for the Images
int edgeThresh = 1;
int lowThreshold = 100;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
blur(binaryImage, detected_edges, Size(3, 3));
Canny(binaryImage, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size);
dst = Scalar::all(0);
src.copyTo(dst, detected_edges);
imwrite(defaultPath + "edge_" + filename, dst);
I did a dirty workaround which works but again added to processing time:
Canny(detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size);
blur(detected_edges, detected_edges, Size(3, 3));
Canny(detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size);
I am new to opencv and image processing so most likely I am missing something.
Please enlighten me. Thanks!
FIRSTLY: for the image like this, where it just white and black pixels, use findContours function, it would be faster and more precise. If you need to draw contours you just found (draw your result) use drawContours function (draw on new Mat, the same size of the one you found the contours on).
Documentation about this HERE.
SECONDLY: it may be a problem that your values of kernel size or thresholds are incorrect. The second threshold is reccomended to be 3 times bigger than the first one. I think that the problem may be with your kernel size (last argument in Canny function). Try not using this function overload at all, use the one without this argument or lower your kernel size.

OpenCV filter2D: Filtering only part of the matrix/image

I encountered the following problem.
I need to filter the matrix/image with linear filter, but I want to filter only those pixels that have sufficient number of neighbors around itself (according to the kernel size). To be concretely the result of filtering 32x32 image with 5x5 kernel should be of the 28x28 size.
Is it possible to do such a processing in relatively simple way with OpenCV built-in functions?
int kernel_size = 3;
cv::Mat in_img, out_img;
cv::Mat kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size);
cv::filter2D(in_img, out_img, -1 , kernel); //filtering
cv::Size size = in_img.size();
cv::Rect roi(kernel_size, kernel_size,size.width - 2*kernel_size, size.height - 2*kernel_size);
cv::Mat cropped = in_img(roi).clone(); //cropping
there is a function called cv::filter2D in opencv, but the output image will be of the same size as the input image (with zero padings during the filtering). There is another image/mathematical library called vxl, there you can find a convolution operator suitable for your requirements.