From ca90d3ec382dc29c0b40bdc826aa9e4dfed2d875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E4=BD=A9=E5=85=B6?= Date: Fri, 8 Mar 2024 14:00:42 +0800 Subject: [PATCH 1/2] faster thinning implement --- modules/ximgproc/src/thinning.cpp | 210 +++++++++++++++++++++++------- 1 file changed, 160 insertions(+), 50 deletions(-) diff --git a/modules/ximgproc/src/thinning.cpp b/modules/ximgproc/src/thinning.cpp index b28784d2894..00017fe0acb 100644 --- a/modules/ximgproc/src/thinning.cpp +++ b/modules/ximgproc/src/thinning.cpp @@ -5,65 +5,175 @@ using namespace std; namespace cv { namespace ximgproc { +// look up table - there is one entry for each of the 2^8=256 possible +// combinations of 8 binary neighbors. +static uint8_t lut_zhang_iter0[] = { + 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, + 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, + 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1}; + +static uint8_t lut_zhang_iter1[] = { + 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, + 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, + 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, + 0, 1, 1, 1}; + +static uint8_t lut_guo_iter0[] = { + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, + 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1}; + +static uint8_t lut_guo_iter1[] = { + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, + 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, + 1, 1, 1, 1}; + // Applies a thinning iteration to a binary image static void thinningIteration(Mat img, int iter, int thinningType){ Mat marker = Mat::zeros(img.size(), CV_8UC1); + int rows = img.rows; + int cols = img.cols; if(thinningType == THINNING_ZHANGSUEN){ - for (int i = 1; i < img.rows-1; i++) - { - for (int j = 1; j < img.cols-1; j++) - { - uchar p2 = img.at(i-1, j); - uchar p3 = img.at(i-1, j+1); - uchar p4 = img.at(i, j+1); - uchar p5 = img.at(i+1, j+1); - uchar p6 = img.at(i+1, j); - uchar p7 = img.at(i+1, j-1); - uchar p8 = img.at(i, j-1); - uchar p9 = img.at(i-1, j-1); - - int A = (p2 == 0 && p3 == 1) + (p3 == 0 && p4 == 1) + - (p4 == 0 && p5 == 1) + (p5 == 0 && p6 == 1) + - (p6 == 0 && p7 == 1) + (p7 == 0 && p8 == 1) + - (p8 == 0 && p9 == 1) + (p9 == 0 && p2 == 1); - int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9; - int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8); - int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8); - - if (A == 1 && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0) - marker.at(i,j) = 1; - } - } + marker.forEach([=](uchar& value, const int postion[]) { + int i = postion[0]; + int j = postion[1]; + if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1) + return; + + auto ptr = img.ptr(i, j); // p1 + + // p9 p2 p3 + // p8 p1 p4 + // p7 p6 p5 + uchar p2 = ptr[-cols]; + uchar p3 = ptr[-cols + 1]; + uchar p4 = ptr[1]; + uchar p5 = ptr[cols + 1]; + uchar p6 = ptr[cols]; + uchar p7 = ptr[cols - 1]; + uchar p8 = ptr[-1]; + uchar p9 = ptr[-cols - 1]; + + int neighbors = p9 | (p2 << 1) | (p3 << 2) | (p4 << 3) | (p5 << 4) | (p6 << 5) | (p7 << 6) | (p8 << 7); + + if (iter == 0) + value = lut_zhang_iter0[neighbors]; + else + value = lut_zhang_iter1[neighbors]; + + //int A = (p2 == 0 && p3 == 1) + (p3 == 0 && p4 == 1) + + // (p4 == 0 && p5 == 1) + (p5 == 0 && p6 == 1) + + // (p6 == 0 && p7 == 1) + (p7 == 0 && p8 == 1) + + // (p8 == 0 && p9 == 1) + (p9 == 0 && p2 == 1); + //int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9; + //int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8); + //int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8); + //if (A == 1 && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0) value = 0; + }); } if(thinningType == THINNING_GUOHALL){ - for (int i = 1; i < img.rows-1; i++) - { - for (int j = 1; j < img.cols-1; j++) - { - uchar p2 = img.at(i-1, j); - uchar p3 = img.at(i-1, j+1); - uchar p4 = img.at(i, j+1); - uchar p5 = img.at(i+1, j+1); - uchar p6 = img.at(i+1, j); - uchar p7 = img.at(i+1, j-1); - uchar p8 = img.at(i, j-1); - uchar p9 = img.at(i-1, j-1); - - int C = ((!p2) & (p3 | p4)) + ((!p4) & (p5 | p6)) + - ((!p6) & (p7 | p8)) + ((!p8) & (p9 | p2)); - int N1 = (p9 | p2) + (p3 | p4) + (p5 | p6) + (p7 | p8); - int N2 = (p2 | p3) + (p4 | p5) + (p6 | p7) + (p8 | p9); - int N = N1 < N2 ? N1 : N2; - int m = iter == 0 ? ((p6 | p7 | (!p9)) & p8) : ((p2 | p3 | (!p5)) & p4); - - if ((C == 1) && ((N >= 2) && ((N <= 3)) & (m == 0))) - marker.at(i,j) = 1; - } - } + marker.forEach([=](uchar& value, const int postion[]) { + int i = postion[0]; + int j = postion[1]; + if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1) + return; + + auto ptr = img.ptr(i, j); // p1 + + // p9 p2 p3 + // p8 p1 p4 + // p7 p6 p5 + uchar p2 = ptr[-cols]; + uchar p3 = ptr[-cols + 1]; + uchar p4 = ptr[1]; + uchar p5 = ptr[cols + 1]; + uchar p6 = ptr[cols]; + uchar p7 = ptr[cols - 1]; + uchar p8 = ptr[-1]; + uchar p9 = ptr[-cols - 1]; + + int neighbors = p9 | (p2 << 1) | (p3 << 2) | (p4 << 3) | (p5 << 4) | (p6 << 5) | (p7 << 6) | (p8 << 7); + + if (iter == 0) + value = lut_guo_iter0[neighbors]; + else + value = lut_guo_iter1[neighbors]; + + //int C = ((!p2) & (p3 | p4)) + ((!p4) & (p5 | p6)) + + // ((!p6) & (p7 | p8)) + ((!p8) & (p9 | p2)); + //int N1 = (p9 | p2) + (p3 | p4) + (p5 | p6) + (p7 | p8); + //int N2 = (p2 | p3) + (p4 | p5) + (p6 | p7) + (p8 | p9); + //int N = N1 < N2 ? N1 : N2; + //int m = iter == 0 ? ((p6 | p7 | (!p9)) & p8) : ((p2 | p3 | (!p5)) & p4); + //if ((C == 1) && ((N >= 2) && ((N <= 3)) & (m == 0))) value = 0; + }); } - img &= ~marker; + img &= marker; } // Apply the thinning procedure to a given image From 85589dd12e95d9a3ce9222d4bafb574857de9ede Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 27 Mar 2024 12:31:04 +0300 Subject: [PATCH 2/2] Added performance test for cv::thining. --- modules/ximgproc/perf/perf_thining.cpp | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 modules/ximgproc/perf/perf_thining.cpp diff --git a/modules/ximgproc/perf/perf_thining.cpp b/modules/ximgproc/perf/perf_thining.cpp new file mode 100644 index 00000000000..6703a01da80 --- /dev/null +++ b/modules/ximgproc/perf/perf_thining.cpp @@ -0,0 +1,36 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "perf_precomp.hpp" + +namespace opencv_test { namespace { + +typedef tuple ThinningPerfParam; +typedef TestBaseWithParam ThinningPerfTest; + +PERF_TEST_P(ThinningPerfTest, perf, + Combine( + Values(sz1080p, sz720p, szVGA), + Values(THINNING_ZHANGSUEN, THINNING_GUOHALL) + ) +) +{ + ThinningPerfParam params = GetParam(); + Size size = get<0>(params); + int type = get<1>(params); + + Mat src = Mat::zeros(size, CV_8UC1); + for (int x = 50; x < src.cols - 50; x += 50) + cv::circle(src, Point(x, x/2), 30 + x/2, Scalar(255), 5); + + Mat dst; + TEST_CYCLE() + { + thinning(src, dst, type); + } + + SANITY_CHECK_NOTHING(); +} + +}} // namespace