@@ -5,6 +5,147 @@ using namespace std;
5
5
namespace cv {
6
6
namespace ximgproc {
7
7
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
+
8
149
// look up table - there is one entry for each of the 2^8=256 possible
9
150
// combinations of 8 binary neighbors.
10
151
static uint8_t lut_zhang_iter0[] = {
@@ -186,22 +327,12 @@ static void thinningIteration(Mat img, int iter, int thinningType){
186
327
void thinning (InputArray input, OutputArray output, int thinningType){
187
328
Mat processed = input.getMat ().clone ();
188
329
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;
194
330
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 );
203
333
204
- processed *= 255 ;
334
+ if (thinningType == THINNING_GUOHALL)
335
+ guo_hall_thinning (processed.data , processed.cols , processed.rows );
205
336
206
337
output.assign (processed);
207
338
}
0 commit comments