OpenCV Erosion / Dilation erroneous output for empty structuring element? - c++

If one erodes an image by zeros(3,3) structuring element, it should be all 1s, but in case of OpenCV, it returns the image. Similarly, dilation of image by zeros(3,3) structuring element return image itself instead of all 0s.

The documentation says:
element – structuring element used for dilation; if element=Mat() , a 3x3 rectangular structuring element is used
If you take a look at the implementation, you will see that in case of empty kernel the ksize will be 3x3 inside of morphOp() #1683:
Size ksize = !kernel.empty() ? kernel.size() : Size(3,3);
This one also works for me:
cv::Mat input = cv::Mat::eye(10, 10, CV_8UC1);
cv::Mat output;
cv::erode(input, output, cv::Mat());

Related

Do mophological ops have a bug when using a header vs full matrix copy source?

Opencv 2.4.10.
At the end of the code below, a dilation is called with a 9 wide disk structuring element on a matrix, Img2. Originally, Img2 was created from Img1 by a simple header copy (Img2=Img1). Note, Img1 was made without copying data from Img0 via Ranges such that Img1 doesn't have the first and last 3 rows of Img0. The result of the dilation was incorrect.
However, if I used a full copy for Img2 via clone, Img2=Img1.clone(), the dilation worked correctly.
Note that using imwrite, not shown in the code below, on Img2 was the same regardless of which copy method I used. So, shouldn't the morphological operators work the same too?
Mat Tmp;
Mat Img1=Img0(Range(3-1, Img0.rows - 3+1),Range::all());
Img1(Range(0,1), Range::all()) = 0;
Img1(Range(Img1.rows-1,Img1.rows), Range::all()) = 0;
// bad
//Mat Img2 = Img1; // header only copy: the dilation results are wrong on the top and bottom
// good
Mat Img2 = Img1.clone(); // full copy, dilation works right.
Mat Disk4;
// exact replacement for mmatlab strel('disk',4,0), somewhat difference than opencv's ellipse structuring element.
MakeFilledEllipse( 4, 4, Disk4);
// If I use Img2 from clone, this is the same as matlab's.
// If I just do a header copy some areas the top and bottom are different
dilate(Img2, Tmp,Disk4, Point(-1,-1),1,BORDER_CONSTANT, Scalar(0));
EDIT- I subsequently simplified the code so that Img2 replaces img1 and there is no img1 so that I could repeat the problem with only 1 level of Mat header indirection and it still failed (was incorrect) the same way.
Mat Tmp;
Mat Img2=Img0(Range(3-1, Img0.rows - 3+1),Range::all());
Img2(Range(0,1), Range::all()) = 0;
Img2(Range(Img2.rows-1,Img2.rows), Range::all()) = 0;
Mat Disk4;
// exact replacement for mmatlab strel('disk',4,0), somewhat difference than opencv's ellipse structuring element.
MakeFilledEllipse( 4, 4, Disk4);
// bad result
dilate(Img2, Tmp,Disk4, Point(-1,-1),1,BORDER_CONSTANT, Scalar(0));
Mat became non-continuous in effect of selecting a ROI within itself.
I'm not sure that in your case Mat Mat::operator()( Range _rowRange, Range _colRange ) const will set CONTINUOUS_FLAG to false but the SUBMATRIX_FLAG will be set surely which can lead to different operation.
Here, I guess some parts of cv::dilate() (such as the border pixel extrapolation method, or the structuring element that determines the shape of a pixel neighborhood) affect your output if there are pixels in the original image around your ROI.
I suggest to use the following to reorder the memory before calling cv::dilate():
if (!mat.isContinuous() || mat.isSubmatrix())
{
mat = mat.clone();
}

OpenCV apply my own filter to a color image

I'm working in OpenCV C++ to filtering image color. I want to filter the image using my own matrix. See this code:
img= "c:/Test/tes.jpg";
Mat im = imread(img);
And then i want to filtering/multiply with my matrix (this matrix can replaced with another matrix 3x3)
Mat filter = (Mat_<double>(3, 3) <<17.8824, 43.5161, 4.11935,
3.45565, 27.1554, 3.86714,
0.0299566, 0.184309, 1.46709);
How to multiply the img mat matrix with my own matrix? I'm still not understand how to multiply 3 channel (RGB) matrix with another matrix (single channel) and resulted image with new color.
you should take a look at the opencv documentation. You could use this function:
filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int borderType=BORDER_DEFAULT )
which would give you something like this in your code:
Mat output;
filter2D(im, output, -1, filter);
About your question for 3-channel matrix; it is specified in the documentation:
kernel – convolution kernel (or rather a correlation kernel), a single-channel floating point matrix; if you want to apply different kernels to different channels, split the image into separate color planes using split() and process them individually.
So by default your "filter" matrix will be applied equally to each color plane.
EDIT You find a fully functional example on the opencv site: http://docs.opencv.org/doc/tutorials/imgproc/imgtrans/filter_2d/filter_2d.html

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.

Different result with OPENCV C and C++ API (Border Interpolation difference)

I have performed Closing morphological operation and I am getting different result with the C and C++ API (OpenCV 2.4.2)
Input:
With OpenCV 'C':
//Set ROI
//Perform Gaussian smoothing
//Perform Canny edge analysis
cvMorphologyEx( src, dst, temp, Mat(), MORPH_CLOSE, 5 );
RESULT:
http://i47.tinypic.com/33e0yfb.png
With Opencv C++
//Set ROI
//Perform Gaussian smoothing
//Perform Canny edge analysis
cv::morphologyEx( src, dst, cv::MORPH_CLOSE, cv::Mat(), cv::Point(-1,-1), 5 );
RESULT:
http://i50.tinypic.com/i5vxjo.png
As you can see, the C++ API yields an output with White/Gray border color. Hence, the results are different for both of these APIs.
I have tried different borderType with the C++ API but it always yields the same result.
How can I get the same output as C API in C++? I need it because it affects the detected contours
Thanks in advance
Thank you everybody for answering this question. I have found my error. I am going to describe it in brief below. Hope it helps others facing this problem.
1) I had executed the C and C++ commands on a ROI image. Apparently, the way OpenCV 'C' and 'C++' API treat ROI is different.
2) In 'C', a ROI is treated as a completely different image. Hence, when you execute functions such as cvSmooth, cvDilate, etc, where one needs to mentions border Pixel extrapolation methods, the 'C' API does not refer back to the original image for pixels beyond left/right/top/bottom most pixel. It actually interpolates the pixel values according to the method you mentioned.
3) But in 'C++', I have found that it always refers back to the original image for pixels beyond left/right/top/bottom most pixel. Hence, the border pixel extrapolation method mentioned doesn't affect your output if there are pixels in the original image around your ROI.
I think it applies the order pixel extrapolation method to the original image instead of the ROI unlike the 'C' API. I don't know if this a bug; I haven't completely read the OpenCV 2.4.2 C++ API documentation. (Please correct me if I am wrong)
To claim my support, I have posted input/output images below:
Output for 'C' and C++ API:
INPUT:
<--- input
OpenCV 'C' API:
IplImage *src = cvLoadImage("input.png", 0);
cvSetImageROI( src, cvRect(33,19,250,110));
cvSaveImage( "before_gauss.png", src );
cvSmooth( src, src, CV_GAUSSIAN );
cvSaveImage("after_gauss.png", src);
IplConvKernel *element = cvCreateStructuringElementEx(3,3,1,1,CV_SHAPE_RECT);
cvCanny( src, src, 140, 40 );
cvSaveImage("after_canny.png", src);
cvDilate( src, src, element, 5);
cvSaveImage("dilate.png", src);
OUTPUT:
<-- before_gauss
<--- after_gauss
<--- after_canny
<--- dilate
OpenCV 'C++' API:
cv::Mat src = cv::imread("input.png", 0);
cv::Mat src_ROI = src( cv::Rect(33,19,250,110));
cv::imwrite( "before_gauss.png", src_ROI );
cv::GaussianBlur( src_ROI, src_ROI, cv::Size(3,3),0 );
cv::imwrite( "after_gauss.png", src_ROI );
cv::Mat element = cv::getStructuringElement( cv::MORPH_RECT, cv::Size(3, 3), cv::Point(1,1));
cv::Canny( src_ROI, src_ROI, 140, 40);
cv::imwrite( "after_canny.png", src_ROI );
cv::dilate( src_ROI, src_ROI, element, cv::Point(1,1), 5);
cv::imwrite( "dilate.png", src_ROI );
OUTPUT:
<-- before_gauss
^^^^^ after_gauss (NOTE: the borders are no more completely black, they are grayish)
^^^^^ after_canny
^^^^^ dilate
SOLUTION:
Create a separate ROI copy and use it for further analysis;
src_ROI.copyTo( new_src_ROI );
Use new_src_ROI for further analysis.
If anyone has better solution, please post below
The defaults are not the same between C and C++ - especially the structuring element.
In C: the default structuring element is:
cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT)
whereas in C++, the default structuring element is:
getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2));
You should specify all fields(including the anchor) if you want the same results.
Check out this sample code from the OpenCV v2.4.2 documentation. You might also want to check this code for using the Canny edge detector. These will hopefully help you track down the error :)
Also note that morphological closing is an idempotent operator, so it can be applied multiple times without changing the result beyond the initial application.

How to use opencv flann::Index?

I have some problems with opencv flann::Index -
I'm creating index
Mat samples = Mat::zeros(vfv_net_quie.size(),24,CV_32F);
for (int i =0; i < vfv_net_quie.size();i++)
{
for (int j = 0;j<24;j++)
{
samples.at<float>(i,j)=(float)vfv_net_quie[i].vfv[j];
}
}
cv::flann::Index flann_index(
samples,
cv::flann::KDTreeIndexParams(4),
cvflann::FLANN_DIST_EUCLIDEAN
);
flann_index.save("c:\\index.fln");
A fter that I'm tryin to load it and find nearest neiborhoods
cv::flann::Index flann_index(Mat(),
cv::flann::SavedIndexParams("c:\\index.fln"),
cvflann::FLANN_DIST_EUCLIDEAN
);
cv::Mat resps(vfv_reg_quie.size(), K, CV_32F);
cv::Mat nresps(vfv_reg_quie.size(), K, CV_32S);
cv::Mat dists(vfv_reg_quie.size(), K, CV_32F);
flann_index.knnSearch(sample,nresps,dists,K,cv::flann::SearchParams(64));
And have access violation in miniflann.cpp in line
((IndexType*)index)->knnSearch(_query, _indices, _dists, knn,
(const ::cvflann::SearchParams&)get_params(params));
Please help
You should not load the flann-file into a Mat(), as it is the place where the index is stored. It is a temporary object destroyed after the constructor was called. That's why the index isn't pointing anywhere useful when you call knnSearch().
I tried following:
cv::Mat indexMat;
cv::flann::Index flann_index(
indexMat,
cv::flann::SavedIndexParams("c:\\index.fln"),
cvflann::FLANN_DIST_EUCLIDEAN
);
resulting in:
Reading FLANN index error: the saved data size (100, 64) or type (5) is different from the passed one (0, 0), 0
which means, that the matrix has to be initialized with the correct dimensions (seems very stupid to me, as I don't necessarily know, how many elements are stored in my index).
cv::Mat indexMat(samples.size(), CV_32FC1);
cv::flann::Index flann_index(
indexMat,
cv::flann::SavedIndexParams("c:\\index.fln"),
cvflann::FLANN_DIST_EUCLIDEAN
);
does the trick.
In the accepted answer is somehow not clear and misleading why the input matrix in the cv::flann::Index constructor must have the same dimension as the matrix used for generating the saved Index. I'll elaborate on #Sau's comment with an example.
KDTreeIndex was generated using as input a cv::Mat sample, and then saved. When you load it, you must provide the same sample matrix to generate it, something like (using the templated GenericIndex interface):
cv::Mat sample(sample_num, sample_size, ... /* other params */);
cv::flann::SavedIndexParams index_params("c:\\index.fln");
cv::flann::GenericIndex<cvflann::L2<float>> flann_index(sample, index_params);
L2 is the usual Euclidean distance (other types can be found in opencv2/flann/dist.h).
Now the index can be used as shown the find the K nearest neighbours of a query point:
std::vector<float> query(sample_size);
std::vector<int> indices(K);
std::vector<float> distances(K);
flann_index.knnSearch(query, indices, distances, K, cv::flann::SearchParams(64));
The matrix indices will contain the locations of the nearest neighbours in the matrix sample, which was used at first to generate the index. That's why you need to load the saved index with the very matrix used to generate the index, otherwise the returned vector will contain indices pointing to meaningless "nearest neighbours".
In addition you get a distances matrix containing how far are the found neighbours from your query point, which you can later use to perform some inverse distance weighting, for example.
Please also note that sample_size has to match across sample matrix and query point.