Skip to content

Commit e0381f0

Browse files
authored
Merge pull request #3689 from LiuPeiqiCN:faster_thinning
Faster thinning implementation
2 parents 0810cfb + 85589dd commit e0381f0

File tree

2 files changed

+196
-50
lines changed

2 files changed

+196
-50
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.
4+
5+
#include "perf_precomp.hpp"
6+
7+
namespace opencv_test { namespace {
8+
9+
typedef tuple<Size, int> ThinningPerfParam;
10+
typedef TestBaseWithParam<ThinningPerfParam> ThinningPerfTest;
11+
12+
PERF_TEST_P(ThinningPerfTest, perf,
13+
Combine(
14+
Values(sz1080p, sz720p, szVGA),
15+
Values(THINNING_ZHANGSUEN, THINNING_GUOHALL)
16+
)
17+
)
18+
{
19+
ThinningPerfParam params = GetParam();
20+
Size size = get<0>(params);
21+
int type = get<1>(params);
22+
23+
Mat src = Mat::zeros(size, CV_8UC1);
24+
for (int x = 50; x < src.cols - 50; x += 50)
25+
cv::circle(src, Point(x, x/2), 30 + x/2, Scalar(255), 5);
26+
27+
Mat dst;
28+
TEST_CYCLE()
29+
{
30+
thinning(src, dst, type);
31+
}
32+
33+
SANITY_CHECK_NOTHING();
34+
}
35+
36+
}} // namespace

modules/ximgproc/src/thinning.cpp

Lines changed: 160 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,65 +5,175 @@ using namespace std;
55
namespace cv {
66
namespace ximgproc {
77

8+
// look up table - there is one entry for each of the 2^8=256 possible
9+
// combinations of 8 binary neighbors.
10+
static uint8_t lut_zhang_iter0[] = {
11+
1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1,
12+
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
13+
0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
14+
1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
15+
0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
16+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
17+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
18+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
19+
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
20+
1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
21+
1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
22+
1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
23+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
24+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0,
25+
1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
26+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
27+
0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
28+
1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
29+
1, 1, 1, 1};
30+
31+
static uint8_t lut_zhang_iter1[] = {
32+
1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1,
33+
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
34+
0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
35+
1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
36+
0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1,
37+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
38+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
39+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
40+
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
41+
0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
42+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
43+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
44+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
45+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0,
46+
1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
47+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
48+
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
49+
1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1,
50+
0, 1, 1, 1};
51+
52+
static uint8_t lut_guo_iter0[] = {
53+
1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
54+
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
55+
0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
56+
0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
57+
0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1,
58+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
59+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
60+
1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
61+
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1,
62+
0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
63+
1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
64+
1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
65+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
66+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
67+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
68+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
69+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
70+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
71+
1, 1, 1, 1};
72+
73+
static uint8_t lut_guo_iter1[] = {
74+
1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
75+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
76+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
77+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
78+
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
79+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
80+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
81+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
82+
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
83+
1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1,
84+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
85+
1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
86+
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
87+
1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
88+
1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
89+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
90+
0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
91+
1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
92+
1, 1, 1, 1};
93+
894
// Applies a thinning iteration to a binary image
995
static void thinningIteration(Mat img, int iter, int thinningType){
1096
Mat marker = Mat::zeros(img.size(), CV_8UC1);
97+
int rows = img.rows;
98+
int cols = img.cols;
1199

12100
if(thinningType == THINNING_ZHANGSUEN){
13-
for (int i = 1; i < img.rows-1; i++)
14-
{
15-
for (int j = 1; j < img.cols-1; j++)
16-
{
17-
uchar p2 = img.at<uchar>(i-1, j);
18-
uchar p3 = img.at<uchar>(i-1, j+1);
19-
uchar p4 = img.at<uchar>(i, j+1);
20-
uchar p5 = img.at<uchar>(i+1, j+1);
21-
uchar p6 = img.at<uchar>(i+1, j);
22-
uchar p7 = img.at<uchar>(i+1, j-1);
23-
uchar p8 = img.at<uchar>(i, j-1);
24-
uchar p9 = img.at<uchar>(i-1, j-1);
25-
26-
int A = (p2 == 0 && p3 == 1) + (p3 == 0 && p4 == 1) +
27-
(p4 == 0 && p5 == 1) + (p5 == 0 && p6 == 1) +
28-
(p6 == 0 && p7 == 1) + (p7 == 0 && p8 == 1) +
29-
(p8 == 0 && p9 == 1) + (p9 == 0 && p2 == 1);
30-
int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
31-
int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8);
32-
int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8);
33-
34-
if (A == 1 && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0)
35-
marker.at<uchar>(i,j) = 1;
36-
}
37-
}
101+
marker.forEach<uchar>([=](uchar& value, const int postion[]) {
102+
int i = postion[0];
103+
int j = postion[1];
104+
if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)
105+
return;
106+
107+
auto ptr = img.ptr(i, j); // p1
108+
109+
// p9 p2 p3
110+
// p8 p1 p4
111+
// p7 p6 p5
112+
uchar p2 = ptr[-cols];
113+
uchar p3 = ptr[-cols + 1];
114+
uchar p4 = ptr[1];
115+
uchar p5 = ptr[cols + 1];
116+
uchar p6 = ptr[cols];
117+
uchar p7 = ptr[cols - 1];
118+
uchar p8 = ptr[-1];
119+
uchar p9 = ptr[-cols - 1];
120+
121+
int neighbors = p9 | (p2 << 1) | (p3 << 2) | (p4 << 3) | (p5 << 4) | (p6 << 5) | (p7 << 6) | (p8 << 7);
122+
123+
if (iter == 0)
124+
value = lut_zhang_iter0[neighbors];
125+
else
126+
value = lut_zhang_iter1[neighbors];
127+
128+
//int A = (p2 == 0 && p3 == 1) + (p3 == 0 && p4 == 1) +
129+
// (p4 == 0 && p5 == 1) + (p5 == 0 && p6 == 1) +
130+
// (p6 == 0 && p7 == 1) + (p7 == 0 && p8 == 1) +
131+
// (p8 == 0 && p9 == 1) + (p9 == 0 && p2 == 1);
132+
//int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
133+
//int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8);
134+
//int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8);
135+
//if (A == 1 && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0) value = 0;
136+
});
38137
}
39138
if(thinningType == THINNING_GUOHALL){
40-
for (int i = 1; i < img.rows-1; i++)
41-
{
42-
for (int j = 1; j < img.cols-1; j++)
43-
{
44-
uchar p2 = img.at<uchar>(i-1, j);
45-
uchar p3 = img.at<uchar>(i-1, j+1);
46-
uchar p4 = img.at<uchar>(i, j+1);
47-
uchar p5 = img.at<uchar>(i+1, j+1);
48-
uchar p6 = img.at<uchar>(i+1, j);
49-
uchar p7 = img.at<uchar>(i+1, j-1);
50-
uchar p8 = img.at<uchar>(i, j-1);
51-
uchar p9 = img.at<uchar>(i-1, j-1);
52-
53-
int C = ((!p2) & (p3 | p4)) + ((!p4) & (p5 | p6)) +
54-
((!p6) & (p7 | p8)) + ((!p8) & (p9 | p2));
55-
int N1 = (p9 | p2) + (p3 | p4) + (p5 | p6) + (p7 | p8);
56-
int N2 = (p2 | p3) + (p4 | p5) + (p6 | p7) + (p8 | p9);
57-
int N = N1 < N2 ? N1 : N2;
58-
int m = iter == 0 ? ((p6 | p7 | (!p9)) & p8) : ((p2 | p3 | (!p5)) & p4);
59-
60-
if ((C == 1) && ((N >= 2) && ((N <= 3)) & (m == 0)))
61-
marker.at<uchar>(i,j) = 1;
62-
}
63-
}
139+
marker.forEach<uchar>([=](uchar& value, const int postion[]) {
140+
int i = postion[0];
141+
int j = postion[1];
142+
if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)
143+
return;
144+
145+
auto ptr = img.ptr(i, j); // p1
146+
147+
// p9 p2 p3
148+
// p8 p1 p4
149+
// p7 p6 p5
150+
uchar p2 = ptr[-cols];
151+
uchar p3 = ptr[-cols + 1];
152+
uchar p4 = ptr[1];
153+
uchar p5 = ptr[cols + 1];
154+
uchar p6 = ptr[cols];
155+
uchar p7 = ptr[cols - 1];
156+
uchar p8 = ptr[-1];
157+
uchar p9 = ptr[-cols - 1];
158+
159+
int neighbors = p9 | (p2 << 1) | (p3 << 2) | (p4 << 3) | (p5 << 4) | (p6 << 5) | (p7 << 6) | (p8 << 7);
160+
161+
if (iter == 0)
162+
value = lut_guo_iter0[neighbors];
163+
else
164+
value = lut_guo_iter1[neighbors];
165+
166+
//int C = ((!p2) & (p3 | p4)) + ((!p4) & (p5 | p6)) +
167+
// ((!p6) & (p7 | p8)) + ((!p8) & (p9 | p2));
168+
//int N1 = (p9 | p2) + (p3 | p4) + (p5 | p6) + (p7 | p8);
169+
//int N2 = (p2 | p3) + (p4 | p5) + (p6 | p7) + (p8 | p9);
170+
//int N = N1 < N2 ? N1 : N2;
171+
//int m = iter == 0 ? ((p6 | p7 | (!p9)) & p8) : ((p2 | p3 | (!p5)) & p4);
172+
//if ((C == 1) && ((N >= 2) && ((N <= 3)) & (m == 0))) value = 0;
173+
});
64174
}
65175

66-
img &= ~marker;
176+
img &= marker;
67177
}
68178

69179
// Apply the thinning procedure to a given image

0 commit comments

Comments
 (0)