0

Having already seen the following posts:

“Don't care” elements in kernel used for binary morphological tree pruning, MATLAB

A fast thinning algorithm

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:structuring elements

(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.

structuring elements

(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:

Original maze (figure 3: Original 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:

expected thinning (figure 4: Expected thinning.)

I get the following result after the 1st iteration:

1st iteration result (figure 5: Result of 1st thinning)

And this one as the 10th iteration:

10th iteration result (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;
}
burak akdemir
  • 153
  • 10
  • 1
    Interesting question. My english is poor, maybe I misunderstand you. I think you want to find a path in the maze. Here is my try: https://i.stack.imgur.com/DvBo7.png – Kinght 金 Jan 20 '18 at 15:29
  • 1
    The "don't care" values are explicitly distinct from 0 and 1. You cannot set them to 0 and expect the same result. Look for the hit-and-miss operator. That is what you want to apply. The 1s are "hit" and the 0s are "miss". If you apply the hit-and-miss operator with the given masks, you will be applying a thinning operator as implemented in the links you posted. – Cris Luengo Jan 21 '18 at 05:03

0 Answers0