@@ -25,7 +25,7 @@ void bitwiseANDInPlace(uint8_t* a, const uint8_t* b, size_t size) {
25
25
* Performs a single iteration of the Guo-Hall algorithm.
26
26
* See http://opencv-code.com/quick-tips/implementation-of-guo-hall-thinning-algorithm/
27
27
* and the original paper http://dx.doi.org/10.1145/62065.62074 for details.
28
- *
28
+ *
29
29
* Compared to the opencv-code.com implementation, we also count the number of
30
30
* changes during the iteration in order to avoid the cv::absdiff() call and the
31
31
* super-expensive whole-image (possibly multi-Mibibyte) copy to prev.
@@ -36,23 +36,22 @@ int guo_hall_iteration(uint8_t* img, uint8_t* mask, size_t width, size_t height,
36
36
for (unsigned int x = 1 ; x < width - 1 ; x++) {
37
37
if (IMG_XY (img, x, y) == 0 ) continue ;
38
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 ) {
39
+ uchar p2 = IMG_XY (img, x, y - 1 );
40
+ uchar p3 = IMG_XY (img, x + 1 , y - 1 );
41
+ uchar p4 = IMG_XY (img, x + 1 , y);
42
+ uchar p5 = IMG_XY (img, x + 1 , y + 1 );
43
+ uchar p6 = IMG_XY (img, x, y + 1 );
44
+ uchar p7 = IMG_XY (img, x - 1 , y + 1 );
45
+ uchar p8 = IMG_XY (img, x - 1 , y);
46
+ uchar p9 = IMG_XY (img, x - 1 , y - 1 );
47
+
48
+ int C = ((!p2) & (p3 | p4)) + ((!p4) & (p5 | p6)) +
49
+ ((!p6) & (p7 | p8)) + ((!p8) & (p9 | p2));
50
+ int N1 = (p9 | p2) + (p3 | p4) + (p5 | p6) + (p7 | p8);
51
+ int N2 = (p2 | p3) + (p4 | p5) + (p6 | p7) + (p8 | p9);
52
+ int N = N1 < N2 ? N1 : N2;
53
+ int m = oddIteration ? ((p6 | p7 | (!p9)) & p8) : ((p2 | p3 | (!p5)) & p4);
54
+ if ((C == 1 ) && ((N >= 2 ) && ((N <= 3 )) & (m == 0 ))) {
56
55
IMG_XY (mask, x, y) = 0 ; // Mask is computed in an inverted way
57
56
changed++;
58
57
}
@@ -74,14 +73,14 @@ int zhang_suen_iteration(uint8_t* img, uint8_t* mask, size_t width, size_t heigh
74
73
for (unsigned int x = 1 ; x < width - 1 ; x++) {
75
74
if (IMG_XY (img, x, y) == 0 ) continue ;
76
75
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 );
76
+ uchar p2 = IMG_XY (img, x, y - 1 );
77
+ uchar p3 = IMG_XY (img, x + 1 , y - 1 );
78
+ uchar p4 = IMG_XY (img, x + 1 , y);
79
+ uchar p5 = IMG_XY (img, x + 1 , y + 1 );
80
+ uchar p6 = IMG_XY (img, x, y + 1 );
81
+ uchar p7 = IMG_XY (img, x - 1 , y + 1 );
82
+ uchar p8 = IMG_XY (img, x - 1 , y);
83
+ uchar p9 = IMG_XY (img, x - 1 , y - 1 );
85
84
86
85
int A = (p2 == 0 && p3 == 1 ) + (p3 == 0 && p4 == 1 ) +
87
86
(p4 == 0 && p5 == 1 ) + (p5 == 0 && p6 == 1 ) +
@@ -146,194 +145,20 @@ int zhang_suen_thinning(uint8_t* binary_image, size_t width, size_t height) {
146
145
return 0 ;
147
146
}
148
147
149
- // look up table - there is one entry for each of the 2^8=256 possible
150
- // combinations of 8 binary neighbors.
151
- static uint8_t lut_zhang_iter0[] = {
152
- 1 , 1 , 1 , 0 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 0 , 1 ,
153
- 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 ,
154
- 0 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
155
- 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
156
- 0 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
157
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
158
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 ,
159
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
160
- 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 1 ,
161
- 1 , 1 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 ,
162
- 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
163
- 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
164
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
165
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 0 ,
166
- 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ,
167
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
168
- 0 , 0 , 1 , 0 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 ,
169
- 1 , 1 , 0 , 0 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
170
- 1 , 1 , 1 , 1 };
171
-
172
- static uint8_t lut_zhang_iter1[] = {
173
- 1 , 1 , 1 , 0 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 0 , 1 ,
174
- 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 ,
175
- 0 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
176
- 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
177
- 0 , 1 , 1 , 1 , 0 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 ,
178
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
179
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 ,
180
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
181
- 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 1 ,
182
- 0 , 1 , 1 , 0 , 1 , 0 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 ,
183
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
184
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
185
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
186
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 0 ,
187
- 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
188
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
189
- 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
190
- 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 ,
191
- 0 , 1 , 1 , 1 };
192
-
193
- static uint8_t lut_guo_iter0[] = {
194
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 0 , 0 , 1 , 1 ,
195
- 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 ,
196
- 0 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 ,
197
- 0 , 0 , 0 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
198
- 0 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 ,
199
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
200
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
201
- 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 0 , 1 ,
202
- 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 , 1 , 0 , 1 ,
203
- 0 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 0 ,
204
- 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
205
- 1 , 0 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
206
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
207
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
208
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
209
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
210
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
211
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
212
- 1 , 1 , 1 , 1 };
213
-
214
- static uint8_t lut_guo_iter1[] = {
215
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 , 1 ,
216
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
217
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
218
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
219
- 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
220
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
221
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
222
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
223
- 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 , 1 , 1 , 1 ,
224
- 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 ,
225
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
226
- 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 ,
227
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 ,
228
- 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 ,
229
- 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
230
- 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
231
- 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 ,
232
- 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 , 1 , 1 ,
233
- 1 , 1 , 1 , 1 };
234
-
235
- // Applies a thinning iteration to a binary image
236
- static void thinningIteration (Mat img, int iter, int thinningType){
237
- Mat marker = Mat::zeros (img.size (), CV_8UC1);
238
- int rows = img.rows ;
239
- int cols = img.cols ;
240
- marker.col (0 ).setTo (1 );
241
- marker.col (cols - 1 ).setTo (1 );
242
- marker.row (0 ).setTo (1 );
243
- marker.row (rows - 1 ).setTo (1 );
244
-
245
- if (thinningType == THINNING_ZHANGSUEN){
246
- marker.forEach <uchar>([=](uchar& value, const int postion[]) {
247
- int i = postion[0 ];
248
- int j = postion[1 ];
249
- if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1 )
250
- return ;
251
-
252
- auto ptr = img.ptr (i, j); // p1
253
-
254
- // p9 p2 p3
255
- // p8 p1 p4
256
- // p7 p6 p5
257
- uchar p2 = ptr[-cols];
258
- uchar p3 = ptr[-cols + 1 ];
259
- uchar p4 = ptr[1 ];
260
- uchar p5 = ptr[cols + 1 ];
261
- uchar p6 = ptr[cols];
262
- uchar p7 = ptr[cols - 1 ];
263
- uchar p8 = ptr[-1 ];
264
- uchar p9 = ptr[-cols - 1 ];
265
-
266
- int neighbors = p9 | (p2 << 1 ) | (p3 << 2 ) | (p4 << 3 ) | (p5 << 4 ) | (p6 << 5 ) | (p7 << 6 ) | (p8 << 7 );
267
-
268
- if (iter == 0 )
269
- value = lut_zhang_iter0[neighbors];
270
- else
271
- value = lut_zhang_iter1[neighbors];
272
-
273
- // int A = (p2 == 0 && p3 == 1) + (p3 == 0 && p4 == 1) +
274
- // (p4 == 0 && p5 == 1) + (p5 == 0 && p6 == 1) +
275
- // (p6 == 0 && p7 == 1) + (p7 == 0 && p8 == 1) +
276
- // (p8 == 0 && p9 == 1) + (p9 == 0 && p2 == 1);
277
- // int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
278
- // int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8);
279
- // int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8);
280
- // if (A == 1 && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0) value = 0;
281
- // else value = 1;
282
- });
283
- }
284
- if (thinningType == THINNING_GUOHALL){
285
- marker.forEach <uchar>([=](uchar& value, const int postion[]) {
286
- int i = postion[0 ];
287
- int j = postion[1 ];
288
- if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1 )
289
- return ;
290
-
291
- auto ptr = img.ptr (i, j); // p1
292
-
293
- // p9 p2 p3
294
- // p8 p1 p4
295
- // p7 p6 p5
296
- uchar p2 = ptr[-cols];
297
- uchar p3 = ptr[-cols + 1 ];
298
- uchar p4 = ptr[1 ];
299
- uchar p5 = ptr[cols + 1 ];
300
- uchar p6 = ptr[cols];
301
- uchar p7 = ptr[cols - 1 ];
302
- uchar p8 = ptr[-1 ];
303
- uchar p9 = ptr[-cols - 1 ];
304
-
305
- int neighbors = p9 | (p2 << 1 ) | (p3 << 2 ) | (p4 << 3 ) | (p5 << 4 ) | (p6 << 5 ) | (p7 << 6 ) | (p8 << 7 );
306
-
307
- if (iter == 0 )
308
- value = lut_guo_iter0[neighbors];
309
- else
310
- value = lut_guo_iter1[neighbors];
311
-
312
- // int C = ((!p2) & (p3 | p4)) + ((!p4) & (p5 | p6)) +
313
- // ((!p6) & (p7 | p8)) + ((!p8) & (p9 | p2));
314
- // int N1 = (p9 | p2) + (p3 | p4) + (p5 | p6) + (p7 | p8);
315
- // int N2 = (p2 | p3) + (p4 | p5) + (p6 | p7) + (p8 | p9);
316
- // int N = N1 < N2 ? N1 : N2;
317
- // int m = iter == 0 ? ((p6 | p7 | (!p9)) & p8) : ((p2 | p3 | (!p5)) & p4);
318
- // if ((C == 1) && ((N >= 2) && ((N <= 3)) & (m == 0))) value = 0;
319
- // else value = 1;
320
- });
321
- }
322
-
323
- img &= marker;
324
- }
325
-
326
148
// Apply the thinning procedure to a given image
327
149
void thinning (InputArray input, OutputArray output, int thinningType){
328
150
Mat processed = input.getMat ().clone ();
329
151
CV_CheckTypeEQ (processed.type (), CV_8UC1, " " );
330
152
153
+ processed /= 255 ;
154
+
331
155
if (thinningType == THINNING_ZHANGSUEN)
332
156
zhang_suen_thinning (processed.data , processed.cols , processed.rows );
333
157
334
158
if (thinningType == THINNING_GUOHALL)
335
159
guo_hall_thinning (processed.data , processed.cols , processed.rows );
336
160
161
+ processed *= 255 ;
337
162
output.assign (processed);
338
163
}
339
164
0 commit comments