Skip to content

Commit 5a981fa

Browse files
authored
Update thinning.cpp
1 parent 8e0173e commit 5a981fa

File tree

1 file changed

+145
-14
lines changed

1 file changed

+145
-14
lines changed

modules/ximgproc/src/thinning.cpp

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

195-
do {
196-
thinningIteration(processed, 0, thinningType);
197-
thinningIteration(processed, 1, thinningType);
198-
absdiff(processed, prev, diff);
199-
if (!hasNonZero(diff)) break;
200-
processed.copyTo(prev);
201-
}
202-
while (true);
331+
if(thinningType == THINNING_ZHANGSUEN)
332+
zhang_suen_thinning(processed.data, processed.cols, processed.rows);
203333

204-
processed *= 255;
334+
if(thinningType == THINNING_GUOHALL)
335+
guo_hall_thinning(processed.data, processed.cols, processed.rows);
205336

206337
output.assign(processed);
207338
}

0 commit comments

Comments
 (0)