@@ -5,165 +5,6 @@ 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
- 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
-
167
8
// look up table - there is one entry for each of the 2^8=256 possible
168
9
// combinations of 8 binary neighbors.
169
10
static uint8_t lut_zhang_iter0[] = {
@@ -251,56 +92,117 @@ static uint8_t lut_guo_iter1[] = {
251
92
1 , 1 , 1 , 1 };
252
93
253
94
// 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);
255
97
int rows = img.rows ;
256
98
int cols = img.cols ;
257
99
marker.col (0 ).setTo (1 );
258
100
marker.col (cols - 1 ).setTo (1 );
259
101
marker.row (0 ).setTo (1 );
260
102
marker.row (rows - 1 ).setTo (1 );
261
103
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
+ }
280
181
281
182
img &= marker;
282
- marker.setTo (0 );
283
183
}
284
184
285
185
// 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){
287
187
Mat processed = input.getMat ().clone ();
288
188
CV_CheckTypeEQ (processed.type (), CV_8UC1, " " );
289
189
// Enforce the range of the input image to be in between 0 - 255
290
190
processed /= 255 ;
191
+
291
192
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
+
295
195
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 ;
300
200
processed.copyTo (prev);
301
- } while (true );
201
+ }
202
+ while (true );
302
203
303
204
processed *= 255 ;
205
+
304
206
output.assign (processed);
305
207
}
306
208
0 commit comments