@@ -5,6 +5,147 @@ using namespace std;
55namespace cv {
66namespace 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.
10151static uint8_t lut_zhang_iter0[] = {
@@ -186,22 +327,12 @@ static void thinningIteration(Mat img, int iter, int thinningType){
186327void 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