Skip to content

Commit c889698

Browse files
committed
Update thinning.cpp
1 parent 70dfb8f commit c889698

File tree

1 file changed

+90
-188
lines changed

1 file changed

+90
-188
lines changed

modules/ximgproc/src/thinning.cpp

+90-188
Original file line numberDiff line numberDiff line change
@@ -5,165 +5,6 @@ using namespace std;
55
namespace cv {
66
namespace ximgproc {
77

8-
#define IMG_XY(img, x, y) img[(x) + (width) * (y)]
9-
10-
int guo_hall_thinning(uint8_t* binary_image, size_t width, size_t height);
11-
int zhang_suen_thinning(uint8_t* binary_image, size_t width, size_t height);
12-
void bitwiseANDInPlace(uint8_t* a, const uint8_t* b, size_t size);
13-
14-
void thinninggh(InputArray input, OutputArray output, int thinningType);
15-
16-
/**
17-
* Perform a logical AND on a memory array (a), ANDing it with another array (b).
18-
* We expect this function to be optimized by the compiler
19-
* specifically for the platform in use.
20-
*/
21-
void bitwiseANDInPlace(uint8_t* a, const uint8_t* b, size_t size) {
22-
for (size_t i = 0; i < size; ++i) {
23-
a[i] &= b[i];
24-
}
25-
}
26-
27-
/**
28-
* Performs a single iteration of the Guo-Hall algorithm.
29-
* See http://opencv-code.com/quick-tips/implementation-of-guo-hall-thinning-algorithm/
30-
* and the original paper http://dx.doi.org/10.1145/62065.62074 for details.
31-
*
32-
* Compared to the opencv-code.com implementation, we also count the number of
33-
* changes during the iteration in order to avoid the cv::absdiff() call and the
34-
* super-expensive whole-image (possibly multi-Mibibyte) copy to prev.
35-
*/
36-
int guo_hall_iteration(uint8_t* img, uint8_t* mask, size_t width, size_t height, bool oddIteration) {
37-
int changed = 0;
38-
for (unsigned int y = 1; y < height - 1; y++) {
39-
for (unsigned int x = 1; x < width - 1; x++) {
40-
if (IMG_XY(img, x, y) == 0) continue;
41-
42-
uchar p2 = IMG_XY(img, x, y - 1);
43-
uchar p3 = IMG_XY(img, x + 1, y - 1);
44-
uchar p4 = IMG_XY(img, x + 1, y);
45-
uchar p5 = IMG_XY(img, x + 1, y + 1);
46-
uchar p6 = IMG_XY(img, x, y + 1);
47-
uchar p7 = IMG_XY(img, x - 1, y + 1);
48-
uchar p8 = IMG_XY(img, x - 1, y);
49-
uchar p9 = IMG_XY(img, x - 1, y - 1);
50-
51-
int C = ((!p2) & (p3 | p4)) + ((!p4) & (p5 | p6)) +
52-
((!p6) & (p7 | p8)) + ((!p8) & (p9 | p2));
53-
int N1 = (p9 | p2) + (p3 | p4) + (p5 | p6) + (p7 | p8);
54-
int N2 = (p2 | p3) + (p4 | p5) + (p6 | p7) + (p8 | p9);
55-
int N = N1 < N2 ? N1 : N2;
56-
int m = oddIteration ? ((p6 | p7 | (!p9)) & p8) : ((p2 | p3 | (!p5)) & p4);
57-
if ((C == 1) && ((N >= 2) && ((N <= 3)) & (m == 0))) {
58-
IMG_XY(mask, x, y) = 0; // Mask is computed in an inverted way
59-
changed++;
60-
}
61-
}
62-
}
63-
64-
bitwiseANDInPlace(img, mask, width * height);
65-
return changed;
66-
}
67-
68-
/**
69-
* Performs a single iteration of the Zhang-Suen algorithm.
70-
* See http://opencv-code.com/quick-tips/implementation-of-thinning-algorithm-in-opencv/
71-
* and the original paper https://dx.doi.org/10.1145/357994.358023 for details.
72-
*/
73-
int zhang_suen_iteration(uint8_t* img, uint8_t* mask, size_t width, size_t height, bool oddIteration) {
74-
int changed = 0;
75-
for (unsigned int y = 1; y < height - 1; y++) {
76-
for (unsigned int x = 1; x < width - 1; x++) {
77-
if (IMG_XY(img, x, y) == 0) continue;
78-
79-
uchar p2 = IMG_XY(img, x, y - 1);
80-
uchar p3 = IMG_XY(img, x + 1, y - 1);
81-
uchar p4 = IMG_XY(img, x + 1, y);
82-
uchar p5 = IMG_XY(img, x + 1, y + 1);
83-
uchar p6 = IMG_XY(img, x, y + 1);
84-
uchar p7 = IMG_XY(img, x - 1, y + 1);
85-
uchar p8 = IMG_XY(img, x - 1, y);
86-
uchar p9 = IMG_XY(img, x - 1, y - 1);
87-
88-
int A = (p2 == 0 && p3 == 1) + (p3 == 0 && p4 == 1) +
89-
(p4 == 0 && p5 == 1) + (p5 == 0 && p6 == 1) +
90-
(p6 == 0 && p7 == 1) + (p7 == 0 && p8 == 1) +
91-
(p8 == 0 && p9 == 1) + (p9 == 0 && p2 == 1);
92-
93-
int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
94-
95-
int m1 = oddIteration ? (p2 * p4 * p8) : (p2 * p4 * p6);
96-
int m2 = oddIteration ? (p2 * p6 * p8) : (p4 * p6 * p8);
97-
98-
if (A == 1 && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0) {
99-
IMG_XY(mask, x, y) = 0; // Inverted mask!
100-
changed++;
101-
}
102-
}
103-
}
104-
105-
bitwiseANDInPlace(img, mask, width * height);
106-
return changed;
107-
}
108-
109-
/**
110-
* Main Guo-Hall thinning function (optimized).
111-
*/
112-
int guo_hall_thinning(uint8_t* binary_image, size_t width, size_t height) {
113-
uint8_t* mask = (uint8_t*)malloc(width * height);
114-
if (mask == NULL) {
115-
return -1;
116-
}
117-
118-
memset(mask, UCHAR_MAX, width * height);
119-
120-
int changed;
121-
do {
122-
changed = guo_hall_iteration(binary_image, mask, width, height, false) +
123-
guo_hall_iteration(binary_image, mask, width, height, true);
124-
} while (changed != 0);
125-
126-
free(mask);
127-
return 0;
128-
}
129-
130-
/**
131-
* Main Zhang-Suen thinning function (optimized).
132-
*/
133-
int zhang_suen_thinning(uint8_t* binary_image, size_t width, size_t height) {
134-
uint8_t* mask = (uint8_t*)malloc(width * height);
135-
if (mask == NULL) {
136-
return -1;
137-
}
138-
139-
memset(mask, UCHAR_MAX, width * height);
140-
141-
int changed;
142-
do {
143-
changed = zhang_suen_iteration(binary_image, mask, width, height, false) +
144-
zhang_suen_iteration(binary_image, mask, width, height, true);
145-
} while (changed != 0);
146-
147-
free(mask);
148-
return 0;
149-
}
150-
151-
// Apply the thinning procedure to a given image
152-
void thinning(InputArray input, OutputArray output, int thinningType){
153-
if(thinningType == THINNING_GUOHALL)
154-
return thinninggh(input, output, thinningType);
155-
Mat processed = input.getMat().clone();
156-
CV_CheckTypeEQ(processed.type(), CV_8UC1, "");
157-
158-
processed /= 255;
159-
160-
if(thinningType == THINNING_ZHANGSUEN)
161-
zhang_suen_thinning(processed.data, processed.cols, processed.rows);
162-
163-
processed *= 255;
164-
output.assign(processed);
165-
}
166-
1678
// look up table - there is one entry for each of the 2^8=256 possible
1689
// combinations of 8 binary neighbors.
16910
static uint8_t lut_zhang_iter0[] = {
@@ -251,56 +92,117 @@ static uint8_t lut_guo_iter1[] = {
25192
1, 1, 1, 1};
25293

25394
// Applies a thinning iteration to a binary image
254-
static void thinningIteration(Mat &img, Mat &marker, const uint8_t* const lut) {
95+
static void thinningIteration(Mat img, int iter, int thinningType){
96+
Mat marker = Mat::zeros(img.size(), CV_8UC1);
25597
int rows = img.rows;
25698
int cols = img.cols;
25799
marker.col(0).setTo(1);
258100
marker.col(cols - 1).setTo(1);
259101
marker.row(0).setTo(1);
260102
marker.row(rows - 1).setTo(1);
261103

262-
marker.forEach<uchar>([=](uchar& value, const int postion[]) {
263-
int i = postion[0];
264-
int j = postion[1];
265-
if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1) { return; }
266-
267-
auto ptr = img.ptr(i, j); // p1
268-
uchar p2 = ptr[-cols];
269-
uchar p3 = ptr[-cols + 1];
270-
uchar p4 = ptr[1];
271-
uchar p5 = ptr[cols + 1];
272-
uchar p6 = ptr[cols];
273-
uchar p7 = ptr[cols - 1];
274-
uchar p8 = ptr[-1];
275-
uchar p9 = ptr[-cols - 1];
276-
277-
int neighbors = p9 | (p2 << 1) | (p3 << 2) | (p4 << 3) | (p5 << 4) | (p6 << 5) | (p7 << 6) | (p8 << 7);
278-
value = lut[neighbors];
279-
});
104+
if(thinningType == THINNING_ZHANGSUEN){
105+
marker.forEach<uchar>([=](uchar& value, const int postion[]) {
106+
int i = postion[0];
107+
int j = postion[1];
108+
if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)
109+
return;
110+
111+
auto ptr = img.ptr(i, j); // p1
112+
113+
// p9 p2 p3
114+
// p8 p1 p4
115+
// p7 p6 p5
116+
uchar p2 = ptr[-cols];
117+
uchar p3 = ptr[-cols + 1];
118+
uchar p4 = ptr[1];
119+
uchar p5 = ptr[cols + 1];
120+
uchar p6 = ptr[cols];
121+
uchar p7 = ptr[cols - 1];
122+
uchar p8 = ptr[-1];
123+
uchar p9 = ptr[-cols - 1];
124+
125+
int neighbors = p9 | (p2 << 1) | (p3 << 2) | (p4 << 3) | (p5 << 4) | (p6 << 5) | (p7 << 6) | (p8 << 7);
126+
127+
if (iter == 0)
128+
value = lut_zhang_iter0[neighbors];
129+
else
130+
value = lut_zhang_iter1[neighbors];
131+
132+
//int A = (p2 == 0 && p3 == 1) + (p3 == 0 && p4 == 1) +
133+
// (p4 == 0 && p5 == 1) + (p5 == 0 && p6 == 1) +
134+
// (p6 == 0 && p7 == 1) + (p7 == 0 && p8 == 1) +
135+
// (p8 == 0 && p9 == 1) + (p9 == 0 && p2 == 1);
136+
//int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
137+
//int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8);
138+
//int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8);
139+
//if (A == 1 && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0) value = 0;
140+
// else value = 1;
141+
});
142+
}
143+
if(thinningType == THINNING_GUOHALL){
144+
marker.forEach<uchar>([=](uchar& value, const int postion[]) {
145+
int i = postion[0];
146+
int j = postion[1];
147+
if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)
148+
return;
149+
150+
auto ptr = img.ptr(i, j); // p1
151+
152+
// p9 p2 p3
153+
// p8 p1 p4
154+
// p7 p6 p5
155+
uchar p2 = ptr[-cols];
156+
uchar p3 = ptr[-cols + 1];
157+
uchar p4 = ptr[1];
158+
uchar p5 = ptr[cols + 1];
159+
uchar p6 = ptr[cols];
160+
uchar p7 = ptr[cols - 1];
161+
uchar p8 = ptr[-1];
162+
uchar p9 = ptr[-cols - 1];
163+
164+
int neighbors = p9 | (p2 << 1) | (p3 << 2) | (p4 << 3) | (p5 << 4) | (p6 << 5) | (p7 << 6) | (p8 << 7);
165+
166+
if (iter == 0)
167+
value = lut_guo_iter0[neighbors];
168+
else
169+
value = lut_guo_iter1[neighbors];
170+
171+
//int C = ((!p2) & (p3 | p4)) + ((!p4) & (p5 | p6)) +
172+
// ((!p6) & (p7 | p8)) + ((!p8) & (p9 | p2));
173+
//int N1 = (p9 | p2) + (p3 | p4) + (p5 | p6) + (p7 | p8);
174+
//int N2 = (p2 | p3) + (p4 | p5) + (p6 | p7) + (p8 | p9);
175+
//int N = N1 < N2 ? N1 : N2;
176+
//int m = iter == 0 ? ((p6 | p7 | (!p9)) & p8) : ((p2 | p3 | (!p5)) & p4);
177+
//if ((C == 1) && ((N >= 2) && ((N <= 3)) & (m == 0))) value = 0;
178+
// else value = 1;
179+
});
180+
}
280181

281182
img &= marker;
282-
marker.setTo(0);
283183
}
284184

285185
// Apply the thinning procedure to a given image
286-
void thinninggh(InputArray input, OutputArray output, int thinningType){
186+
void thinning(InputArray input, OutputArray output, int thinningType){
287187
Mat processed = input.getMat().clone();
288188
CV_CheckTypeEQ(processed.type(), CV_8UC1, "");
289189
// Enforce the range of the input image to be in between 0 - 255
290190
processed /= 255;
191+
291192
Mat prev = processed.clone();
292-
Mat marker = Mat::zeros(processed.size(), CV_8UC1);
293-
const auto lutIter0 = (thinningType == THINNING_GUOHALL) ? lut_guo_iter0 : lut_zhang_iter0;
294-
const auto lutIter1 = (thinningType == THINNING_GUOHALL) ? lut_guo_iter1 : lut_zhang_iter1;
193+
Mat diff;
194+
295195
do {
296-
thinningIteration(processed, marker, lutIter0);
297-
thinningIteration(processed, marker, lutIter1);
298-
const auto res = cv::norm(processed, prev, cv::NORM_L1);
299-
if (res <= 0) { break; }
196+
thinningIteration(processed, 0, thinningType);
197+
thinningIteration(processed, 1, thinningType);
198+
absdiff(processed, prev, diff);
199+
if (!hasNonZero(diff)) break;
300200
processed.copyTo(prev);
301-
} while (true);
201+
}
202+
while (true);
302203

303204
processed *= 255;
205+
304206
output.assign(processed);
305207
}
306208

0 commit comments

Comments
 (0)