Having already seen the following posts:
“Don't care” elements in kernel used for binary morphological tree pruning, MATLAB
Structuring Element (Image Thinning).
I am trying to implement thinning algorithm (in C++) as shown here. It is clearly stated in the that the structuring elements in figure 1 (in the link) will be used for thinning:
At each iteration, the image is first thinned by the left hand structuring element, and then by the right hand one, and then with the remaining six 90° rotations of the two elements.
Here are the structuring elements as shown in the link:
(figure 1: Structuring elements as described in the link.)
So I have implemented the structuring elements which is shown in the figure below. (Since the code consists basic code, I do not include it here). For instance, seLeft0 means structuring element on the left with rotation of 0 degrees.
(figure 2: Structuring elements as implemented in my code.)
In order to use that structuring elements, I first load the source file.
// Read original image
Mat src = imread("maze.jpg",IMREAD_GRAYSCALE);
Then I declare another Mat file which is going to be destination file (dst_bin) in binary
Mat dst_bin(src.size(), src.type());
After that I convert the original image into binary using adaptiveThreshold function.
// Convert into binary using Adaptive th.
adaptiveThreshold(src, dst_bin, 1, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 95, -2);
Up to here I everything worked as expected. With this piece of code (because the values in dst_bin is either 0 or 1, I multiply the Mat file with 255 so that I can see the figure.)
imshow("Maze after bin. th. ", dst_bin*255);
I get the following maze:
Now the cumbersome part comes.
I apply the structuring elements like the following: (Because applying structuring elements results in values greater than 1, I threshold after each filter2D.)
Mat dum = dst_bin.clone();
filter2D(dst_bin, dum, dst_bin.depth(), seLeft0);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight0);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seLeft90);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight90);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seLeft180);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight180);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seLeft270);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight270);
threshold(dum, dum, 1, 1, THRESH_BINARY);
imshow("1st iteration", dum * 255);
for (int iii = 0; iii < 10; iii++)
{
filter2D(dum, dum, dst_bin.depth(), seLeft0);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight0);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seLeft90);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight90);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seLeft180);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight180);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seLeft270);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight270);
threshold(dum, dum, 1, 1, THRESH_BINARY);
}
imshow("10th iteration", dum * 255);
But instead of getting a kind of thinning as shown here:
(figure 4: Expected thinning.)
I get the following result after the 1st iteration:
(figure 5: Result of 1st thinning)
And this one as the 10th iteration:
(figure 6: Result of 10th thinning)
As you can see the results are not expected as it is explained in the link that I have been following.
So can you please tell me what I am doing wrong?
First thing that I can think of is about the structuring elements. As seen in figure 1 there are DON'T CARE items. I have implemented them as zeros (as seen in figure 2). So how can I implement the DON'T CARE items in a structuring element in C++ / OpenCV? Should I take them zeros or ones? Or is there another method (like combining them somehow).
Second thing is that i am using filter2D in OpenCV 3.x.x. Is it the right way to do it?
To sum up, how can I implement the thinning algorithm that is described in the link that I have been following.
Yours Sincerely...
PS: You can find the code at the appendix.
--------Appendix--------
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
#include <vector>
#include <array>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
// Read original image
Mat src = imread("maze.png",IMREAD_GRAYSCALE);
Mat dst_bin(src.size(), src.type());
//if fail to read the image
if (!src.data)
{
cout << "Error loading the image" << endl;
return -1;
}
// Convert into binary using Adaptive th.
adaptiveThreshold(src, dst_bin, 1, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 95, -2);
// now dst_bin e^x = {0,1}
imshow("Maze after bin. th. ", dst_bin*255);
double minVal;
double maxVal;
minMaxLoc(dst_bin, &minVal, &maxVal); cout << "dst_bin [" << minVal << ", " << maxVal << "]" << endl;
// seLeft0
Mat seLeft0 = getStructuringElement(MORPH_RECT, Size(3, 3));
array<array<uchar, 3>, 3> data = { { { 0, 0, 0 },{ 0, 1, 0 },{ 1, 1, 1 } } };
seLeft0 = Mat(3, 3, CV_8UC1, &data);
cout << "seLeft0 = " << endl << " " << seLeft0 << endl << endl;
// seLeft90
Mat seLeft90 = getStructuringElement(MORPH_RECT, Size(3, 3));
data = { { { 1, 0, 0 },{ 1, 1, 0 },{ 1, 0, 0 } } };
seLeft90 = Mat(3, 3, CV_8UC1, &data);
cout << "seLeft90 = " << endl << " " << seLeft90 << endl << endl;
// seLeft180
Mat seLeft180 = getStructuringElement(MORPH_RECT, Size(3, 3));
data = { { { 1, 1, 1 },{ 0, 1, 0 },{ 0, 0, 0 } } };
seLeft180 = Mat(3, 3, CV_8UC1, &data);
cout << "seLeft180 = " << endl << " " << seLeft180 << endl << endl;
// seLeft270
Mat seLeft270 = getStructuringElement(MORPH_RECT, Size(3, 3));
data = { { { 0, 0, 1 },{ 0, 1, 1 },{ 0, 0, 1 } } };
seLeft270 = Mat(3, 3, CV_8UC1, &data);
cout << "seLeft270 = " << endl << " " << seLeft270 << endl << endl;
// seRight0
Mat seRight0 = getStructuringElement(MORPH_RECT, Size(3, 3));
data = { { { 0, 0, 0 },{ 1, 1, 0 },{ 0, 1, 0 } } };
seRight0 = Mat(3, 3, CV_8UC1, &data);
cout << "seRight0 = " << endl << " " << seRight0 << endl << endl;
// seRight90
Mat seRight90 = getStructuringElement(MORPH_RECT, Size(3, 3));
data = { { { 0, 1, 0 },{ 1, 1, 0 },{ 0, 0, 0 } } };
seRight90 = Mat(3, 3, CV_8UC1, &data);
cout << "seRight90 = " << endl << " " << seRight90 << endl << endl;
// seRight180
Mat seRight180 = getStructuringElement(MORPH_RECT, Size(3, 3));
data = { { { 0, 1, 0 },{ 0, 1, 1 },{ 0, 0, 0 } } };
seRight180 = Mat(3, 3, CV_8UC1, &data);
cout << "seRight180 = " << endl << " " << seRight180 << endl << endl;
// seRight270
Mat seRight270 = getStructuringElement(MORPH_RECT, Size(3, 3));
data = { { { 0, 0, 0 },{ 0, 1, 1 },{ 0, 1, 0 } } };
seRight270 = Mat(3, 3, CV_8UC1, &data);
cout << "seRight270 = " << endl << " " << seRight270 << endl << endl;
Mat dum = dst_bin.clone();
filter2D(dst_bin, dum, dst_bin.depth(), seLeft0);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight0);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seLeft90);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight90);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seLeft180);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight180);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seLeft270);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight270);
threshold(dum, dum, 1, 1, THRESH_BINARY);
imshow("1st iteration", dum * 255);
for (int iii = 0; iii < 10; iii++)
{
filter2D(dum, dum, dst_bin.depth(), seLeft0);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight0);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seLeft90);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight90);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seLeft180);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight180);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seLeft270);
threshold(dum, dum, 1, 1, THRESH_BINARY);
filter2D(dum, dum, dst_bin.depth(), seRight270);
threshold(dum, dum, 1, 1, THRESH_BINARY);
}
imshow("10th iteration", dum * 255);
minMaxLoc(dum, &minVal, &maxVal); cout << "dum [" << minVal << ", " << maxVal << "]" << endl;
cout << endl;
waitKey();
return 0;
}