@@ -25,7 +25,7 @@ void bitwiseANDInPlace(uint8_t* a, const uint8_t* b, size_t size) {
2525 * Performs a single iteration of the Guo-Hall algorithm.
2626 * See http://opencv-code.com/quick-tips/implementation-of-guo-hall-thinning-algorithm/
2727 * and the original paper http://dx.doi.org/10.1145/62065.62074 for details.
28- *
28+ *
2929 * Compared to the opencv-code.com implementation, we also count the number of
3030 * changes during the iteration in order to avoid the cv::absdiff() call and the
3131 * 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,
3636 for (unsigned int x = 1 ; x < width - 1 ; x++) {
3737 if (IMG_XY (img, x, y) == 0 ) continue ;
3838
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 ))) {
5655 IMG_XY (mask, x, y) = 0 ; // Mask is computed in an inverted way
5756 changed++;
5857 }
@@ -74,14 +73,14 @@ int zhang_suen_iteration(uint8_t* img, uint8_t* mask, size_t width, size_t heigh
7473 for (unsigned int x = 1 ; x < width - 1 ; x++) {
7574 if (IMG_XY (img, x, y) == 0 ) continue ;
7675
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 );
8584
8685 int A = (p2 == 0 && p3 == 1 ) + (p3 == 0 && p4 == 1 ) +
8786 (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) {
146145 return 0 ;
147146}
148147
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-
326148// Apply the thinning procedure to a given image
327149void thinning (InputArray input, OutputArray output, int thinningType){
328150 Mat processed = input.getMat ().clone ();
329151 CV_CheckTypeEQ (processed.type (), CV_8UC1, " " );
330152
153+ processed /= 255 ;
154+
331155 if (thinningType == THINNING_ZHANGSUEN)
332156 zhang_suen_thinning (processed.data , processed.cols , processed.rows );
333157
334158 if (thinningType == THINNING_GUOHALL)
335159 guo_hall_thinning (processed.data , processed.cols , processed.rows );
336160
161+ processed *= 255 ;
337162 output.assign (processed);
338163}
339164
0 commit comments