Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit66cf9b3

Browse files
committed
🚀 Improved image processing speed by optimizing the thinning algorithm
1 parent80f1ca2 commit66cf9b3

File tree

3 files changed

+79
-173
lines changed

3 files changed

+79
-173
lines changed

‎modules/ximgproc/include/opencv2/ximgproc.hpp‎

Lines changed: 18 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,6 @@
1-
/*
2-
* By downloading, copying, installing or using the software you agree to this license.
3-
* If you do not agree to this license, do not download, install,
4-
* copy or use the software.
5-
*
6-
*
7-
* License Agreement
8-
* For Open Source Computer Vision Library
9-
* (3 - clause BSD License)
10-
*
11-
* Redistribution and use in source and binary forms, with or without modification,
12-
* are permitted provided that the following conditions are met :
13-
*
14-
* * Redistributions of source code must retain the above copyright notice,
15-
* this list of conditions and the following disclaimer.
16-
*
17-
* * Redistributions in binary form must reproduce the above copyright notice,
18-
* this list of conditions and the following disclaimer in the documentation
19-
* and / or other materials provided with the distribution.
20-
*
21-
* * Neither the names of the copyright holders nor the names of the contributors
22-
* may be used to endorse or promote products derived from this software
23-
* without specific prior written permission.
24-
*
25-
* This software is provided by the copyright holders and contributors "as is" and
26-
* any express or implied warranties, including, but not limited to, the implied
27-
* warranties of merchantability and fitness for a particular purpose are disclaimed.
28-
* In no event shall copyright holders or contributors be liable for any direct,
29-
* indirect, incidental, special, exemplary, or consequential damages
30-
* (including, but not limited to, procurement of substitute goods or services;
31-
* loss of use, data, or profits; or business interruption) however caused
32-
* and on any theory of liability, whether in contract, strict liability,
33-
* or tort(including negligence or otherwise) arising in any way out of
34-
* the use of this software, even if advised of the possibility of such damage.
35-
*/
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.
364

375
#ifndef __OPENCV_XIMGPROC_HPP__
386
#define__OPENCV_XIMGPROC_HPP__
@@ -68,6 +36,8 @@
6836
/**
6937
@defgroup ximgproc Extended Image Processing
7038
@{
39+
@defgroup ximgproc_binarization Binarization
40+
7141
@defgroup ximgproc_edge Structured forests for fast edge detection
7242
7343
This module contains implementations of modern structured edge detection algorithms,
@@ -124,7 +94,7 @@ namespace cv
12494
namespaceximgproc
12595
{
12696

127-
//! @addtogroupximgproc
97+
//! @addtogroupximgproc_binarization
12898
//! @{
12999

130100
enum ThinningTypes{
@@ -179,15 +149,20 @@ CV_EXPORTS_W void niBlackThreshold( InputArray _src, OutputArray _dst,
179149
int blockSize,double k,int binarizationMethod = BINARIZATION_NIBLACK,
180150
double r =128 );
181151

182-
/** @briefApplies abinaryblob thinningoperation,toachieve askeletization of the input image.
152+
/** @briefPerformsbinaryimage thinning toobtain askeletonized representation of the input image.
183153
184-
The function transforms a binary blob image into a skeletized form using the technique of Zhang-Suen.
154+
This function applies a thinning algorithm, reducing the binary blobs in the input image to a skeletal form.
155+
By default, it uses the Zhang-Suen technique, which iteratively removes pixels from the boundaries of the blobs
156+
while preserving the overall structure and connectivity of the objects.
157+
158+
@param src Source image: an 8-bit, single-channel binary image where the blobs are represented by pixels with a value of 255 (white),
159+
and the background is 0 (black).
160+
@param dst Destination image of the same size and type as src, where the result of the thinning operation will be stored.
161+
This operation can be performed in-place, meaning `src` and `dst` can be the same.
162+
@param thinningType The thinning algorithm to apply. By default, the Zhang-Suen algorithm is used. See cv::ximgproc::ThinningTypes for other options.
163+
*/
164+
CV_EXPORTS_Wvoidthinning(InputArray src, OutputArray dst,int thinningType = THINNING_ZHANGSUEN);
185165

186-
@param src Source 8-bit single-channel image, containing binary blobs, with blobs having 255 pixel values.
187-
@param dst Destination image of the same size and the same type as src. The function can work in-place.
188-
@param thinningType Value that defines which thinning algorithm should be used. See cv::ximgproc::ThinningTypes
189-
*/
190-
CV_EXPORTS_Wvoidthinning( InputArray src, OutputArray dst,int thinningType = THINNING_ZHANGSUEN);
191166

192167
/** @brief Performs anisotropic diffusion on an image.
193168

‎modules/ximgproc/src/thinning.cpp‎

Lines changed: 39 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -92,117 +92,60 @@ static uint8_t lut_guo_iter1[] = {
9292
1,1,1,1};
9393

9494
// Applies a thinning iteration to a binary image
95-
staticvoidthinningIteration(Mat img,int iter,int thinningType){
96-
Mat marker =Mat::zeros(img.size(), CV_8UC1);
95+
staticvoidthinningIteration(Mat &img, Mat &marker,constuint8_t*const lut) {
9796
int rows = img.rows;
9897
int cols = img.cols;
99-
marker.col(0).setTo(1);
100-
marker.col(cols -1).setTo(1);
101-
marker.row(0).setTo(1);
102-
marker.row(rows -1).setTo(1);
103-
104-
if(thinningType == THINNING_ZHANGSUEN){
105-
marker.forEach<uchar>([=](uchar& value,constint 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,constint 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-
}
98+
99+
// Parallelized iteration over pixels excluding the boundary
100+
parallel_for_(Range(1, rows -1), [&](const Range& range) {
101+
for (int i = range.start; i < range.end; i++) {
102+
const uchar* imgRow = img.ptr(i);
103+
uchar* markerRow = marker.ptr(i);
104+
for (int j =1; j < cols -1; j++) {
105+
if (imgRow[j]) {
106+
uchar p2 = imgRow[j - cols] !=0;
107+
uchar p3 = imgRow[j - cols +1] !=0;
108+
uchar p4 = imgRow[j +1] !=0;
109+
uchar p5 = imgRow[j + cols +1] !=0;
110+
uchar p6 = imgRow[j + cols] !=0;
111+
uchar p7 = imgRow[j + cols -1] !=0;
112+
uchar p8 = imgRow[j -1] !=0;
113+
uchar p9 = imgRow[j - cols -1] !=0;
114+
115+
int neighbors = p9 | (p2 <<1) | (p3 <<2) | (p4 <<3) | (p5 <<4) | (p6 <<5) | (p7 <<6) | (p8 <<7);
116+
markerRow[j] = lut[neighbors];
117+
}
118+
}
119+
}
120+
});
181121

182122
img &= marker;
183123
}
184124

185125
// Apply the thinning procedure to a given image
186126
voidthinning(InputArray input, OutputArray output,int thinningType){
187-
Mat processed = input.getMat().clone();
188-
CV_CheckTypeEQ(processed.type(), CV_8UC1,"");
189-
// Enforce the range of the input image to be in between 0 - 255
190-
processed /=255;
127+
Mat input_ = input.getMat();
128+
CV_Assert(!input_.empty());
129+
CV_CheckTypeEQ(input_.type(), CV_8UC1,"");
191130

131+
Mat processed = input_ /255;
192132
Mat prev = processed.clone();
193-
Mat diff;
194133

134+
Mat marker;
135+
Mat marker_inner =processed(Rect(1,1, processed.cols -2, processed.rows -2));
136+
copyMakeBorder(marker_inner, marker,1,1,1,1, BORDER_ISOLATED | BORDER_CONSTANT,Scalar(255));
137+
138+
constauto lutIter0 = (thinningType == THINNING_GUOHALL) ? lut_guo_iter0 : lut_zhang_iter0;
139+
constauto lutIter1 = (thinningType == THINNING_GUOHALL) ? lut_guo_iter1 : lut_zhang_iter1;
195140
do {
196-
thinningIteration(processed,0, thinningType);
197-
thinningIteration(processed,1, thinningType);
198-
absdiff(processed, prev,diff);
199-
if (!hasNonZero(diff))break;
141+
thinningIteration(processed,marker, lutIter0);
142+
thinningIteration(processed,marker, lutIter1);
143+
constauto res =cv::norm(processed, prev,cv::NORM_L1);
144+
if (res <=0) {break; }
200145
processed.copyTo(prev);
201-
}
202-
while (true);
146+
}while (true);
203147

204148
processed *=255;
205-
206149
output.assign(processed);
207150
}
208151

‎modules/ximgproc/test/test_thinning.cpp‎

Lines changed: 22 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,52 +6,40 @@
66

77
namespaceopencv_test {namespace {
88

9-
staticintcreateTestImage(Mat1b& src)
10-
{
11-
src =Mat1b::zeros(Size(256,256));
12-
// Create a corner point that should not be affected.
13-
src(0,0) =255;
14-
15-
for (int x =50; x < src.cols -50; x +=50)
16-
{
17-
cv::circle(src,Point(x, x/2),30 + x/2,Scalar(255),5);
18-
}
19-
int src_pixels =countNonZero(src);
20-
EXPECT_GT(src_pixels,0);
21-
return src_pixels;
22-
}
23-
249
TEST(ximgproc_Thinning, simple_ZHANGSUEN)
2510
{
26-
Mat1b src;
27-
int src_pixels =createTestImage(src);
11+
string dir =cvtest::TS::ptr()->get_data_path();
12+
Mat src =imread(dir +"cv/ximgproc/sources/08.png", IMREAD_GRAYSCALE);
13+
Mat dst,check_img;
2814

29-
Mat1b dst;
3015
thinning(src, dst, THINNING_ZHANGSUEN);
31-
int dst_pixels =countNonZero(dst);
32-
EXPECT_LE(dst_pixels, src_pixels);
33-
EXPECT_EQ(dst(0,0),255);
3416

35-
#if0
36-
imshow("src", src); imshow("dst", dst); waitKey();
37-
#endif
17+
check_img =imread(dir +"cv/ximgproc/results/Thinning_ZHANGSUEN.png", IMREAD_GRAYSCALE);
18+
EXPECT_EQ(0,cvtest::norm(check_img, dst, NORM_INF));
19+
20+
dst = ~src;
21+
thinning(dst, dst, THINNING_ZHANGSUEN);
22+
23+
check_img =imread(dir +"cv/ximgproc/results/Thinning_inv_ZHANGSUEN.png", IMREAD_GRAYSCALE);
24+
EXPECT_EQ(0,cvtest::norm(check_img, dst, NORM_INF));
3825
}
3926

4027
TEST(ximgproc_Thinning, simple_GUOHALL)
4128
{
42-
Mat1b src;
43-
int src_pixels =createTestImage(src);
29+
string dir =cvtest::TS::ptr()->get_data_path();
30+
Mat src =imread(dir +"cv/ximgproc/sources/08.png", IMREAD_GRAYSCALE);
31+
Mat dst,check_img;
4432

45-
Mat1b dst;
4633
thinning(src, dst, THINNING_GUOHALL);
47-
int dst_pixels =countNonZero(dst);
48-
EXPECT_LE(dst_pixels, src_pixels);
49-
EXPECT_EQ(dst(0,0),255);
5034

51-
#if0
52-
imshow("src", src); imshow("dst", dst); waitKey();
53-
#endif
54-
}
35+
check_img =imread(dir +"cv/ximgproc/results/Thinning_GUOHALL.png", IMREAD_GRAYSCALE);
36+
EXPECT_EQ(0,cvtest::norm(check_img, dst, NORM_INF));
5537

38+
dst = ~src;
39+
thinning(dst, dst, THINNING_GUOHALL);
40+
41+
check_img =imread(dir +"cv/ximgproc/results/Thinning_inv_GUOHALL.png", IMREAD_GRAYSCALE);
42+
EXPECT_EQ(0,cvtest::norm(check_img, dst, NORM_INF));
43+
}
5644

5745
}}// namespace

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp