From bc8727786b9ed0ca05a63a8f27d108ccf76acdff Mon Sep 17 00:00:00 2001 From: Qoo Date: Mon, 15 Feb 2021 20:14:07 -0500 Subject: [PATCH 01/12] save allocation of memory for fake image --- modules/dnn/src/layers/proposal_layer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dnn/src/layers/proposal_layer.cpp b/modules/dnn/src/layers/proposal_layer.cpp index 990cfeda30..0e0a9ecdad 100644 --- a/modules/dnn/src/layers/proposal_layer.cpp +++ b/modules/dnn/src/layers/proposal_layer.cpp @@ -286,7 +286,8 @@ public: CV_Assert(imInfo.total() >= 2); // We've chosen the smallest data type because we need just a shape from it. - fakeImageBlob.create(shape(1, 1, imInfo.at(0), imInfo.at(1)), CV_8UC1); + // We don't allocate memory but just need the shape is correct. + Mat fakeImageBlob(shape(1, 1, imInfo.at(0), imInfo.at(1)), CV_8UC1, nullptr); // Generate prior boxes. std::vector layerInputs(2), layerOutputs(1, priorBoxes); @@ -427,7 +428,6 @@ private: Ptr deltasPermute; Ptr scoresPermute; uint32_t keepTopBeforeNMS, keepTopAfterNMS, featStride, baseSize; - Mat fakeImageBlob; float nmsThreshold; DictValue ratios, scales; #ifdef HAVE_OPENCL From 5cf08b0722f274d003679cfedef21de9668c3451 Mon Sep 17 00:00:00 2001 From: Giles Payne Date: Fri, 19 Feb 2021 17:10:11 +0900 Subject: [PATCH 02/12] Fix/optimize Android put/get functions --- modules/core/misc/java/test/MatTest.java | 21 +++ modules/java/generator/src/cpp/Mat.cpp | 177 +++++++----------- .../src/org/opencv/test/OpenCVTestCase.java | 16 +- .../src/org/opencv/test/OpenCVTestCase.java | 15 +- 4 files changed, 104 insertions(+), 125 deletions(-) diff --git a/modules/core/misc/java/test/MatTest.java b/modules/core/misc/java/test/MatTest.java index 039aa39929..00e7b7cb32 100644 --- a/modules/core/misc/java/test/MatTest.java +++ b/modules/core/misc/java/test/MatTest.java @@ -455,6 +455,27 @@ public class MatTest extends OpenCVTestCase { bytesNum = sm.get(1, 1, buff11); assertEquals(4, bytesNum); assertTrue(Arrays.equals(new short[] {340, 341, 0, 0}, buff11)); + + Mat m2 = new Mat(new int[]{ 5, 6, 8 }, CvType.CV_16S); + short[] data = new short[(int)m2.total()]; + for (int i = 0; i < data.length; i++ ) { + data[i] = (short)i; + } + m2.put(new int[] {0, 0, 0}, data); + Mat matNonContinuous = m2.submat(new Range[]{new Range(1,4), new Range(2,5), new Range(3,6)}); + Mat matContinuous = matNonContinuous.clone(); + short[] outNonContinuous = new short[(int)matNonContinuous.total()]; + matNonContinuous.get(new int[] { 0, 0, 0 }, outNonContinuous); + short[] outContinuous = new short[(int)matNonContinuous.total()]; + matContinuous.get(new int[] { 0, 0, 0 }, outContinuous); + assertArrayEquals(outNonContinuous, outContinuous); + Mat subMat2 = m2.submat(new Range[]{new Range(1,4), new Range(1,5), new Range(0,8)}); + Mat subMatClone2 = subMat2.clone(); + short[] outNonContinuous2 = new short[(int)subMat2.total()]; + subMat2.get(new int[] { 0, 1, 1 }, outNonContinuous2); + short[] outContinuous2 = new short[(int)subMat2.total()]; + subMatClone2.get(new int[] { 0, 1, 1 }, outContinuous2); + assertArrayEquals(outNonContinuous2, outContinuous2); } public void testGetNativeObjAddr() { diff --git a/modules/java/generator/src/cpp/Mat.cpp b/modules/java/generator/src/cpp/Mat.cpp index 5203413ae4..d59fe4a506 100644 --- a/modules/java/generator/src/cpp/Mat.cpp +++ b/modules/java/generator/src/cpp/Mat.cpp @@ -2129,80 +2129,83 @@ namespace { #undef JOCvT } -template static int mat_put(cv::Mat* m, int row, int col, int count, int offset, char* buff) -{ - if(! m) return 0; - if(! buff) return 0; - - count *= sizeof(T); - int rest = ((m->rows - row) * m->cols - col) * (int)m->elemSize(); - if(count>rest) count = rest; - int res = count; - - if( m->isContinuous() ) - { - memcpy(m->ptr(row, col), buff + offset, count); - } else { - // row by row - int num = (m->cols - col) * (int)m->elemSize(); // 1st partial row - if(countptr(row++, col); - while(count>0){ - memcpy(data, buff + offset, num); - count -= num; - buff += num; - num = m->cols * (int)m->elemSize(); - if(countptr(row++, 0); - } +static size_t idx2Offset(cv::Mat* mat, std::vector& indices) { + size_t offset = indices[0]; + for (int dim=1; dim < mat->dims; dim++) { + offset = offset*mat->size[dim] + indices[dim]; + } + return offset; +} + +static void offset2Idx(cv::Mat* mat, size_t offset, std::vector& indices) { + for (int dim=mat->dims-1; dim>=0; dim--) { + indices[dim] = offset % mat->size[dim]; + offset = (offset - indices[dim]) / mat->size[dim]; } - return res; } // returns true if final index was reached -static bool updateIdx(cv::Mat* m, std::vector& idx, int inc) { - for (int i=m->dims-1; i>=0; i--) { - if (inc == 0) return false; - idx[i] = (idx[i] + 1) % m->size[i]; - inc--; - } - return true; +static bool updateIdx(cv::Mat* mat, std::vector& indices, size_t inc) { + size_t currentOffset = idx2Offset(mat, indices); + size_t newOffset = currentOffset + inc; + bool reachedEnd = newOffset>=(size_t)mat->total(); + offset2Idx(mat, reachedEnd?0:newOffset, indices); + return reachedEnd; } -template static int mat_put_idx(cv::Mat* m, std::vector& idx, int count, int offset, char* buff) -{ +template static int mat_copy_data(cv::Mat* m, std::vector& idx, int count, char* buff, bool isPut) { if(! m) return 0; if(! buff) return 0; - count *= sizeof(T); - int rest = (int)m->elemSize(); - for (int i = 0; i < m->dims; i++) { - rest *= (m->size[i] - idx[i]); - } - if(count>rest) count = rest; - int res = count; + size_t countBytes = count * sizeof(T); + size_t remainingBytes = (size_t)(m->total() - idx2Offset(m, idx))*m->elemSize(); + countBytes = (countBytes>remainingBytes)?remainingBytes:countBytes; + int res = (int)countBytes; if( m->isContinuous() ) { - memcpy(m->ptr(idx.data()), buff + offset, count); + if (isPut) { + memcpy(m->ptr(idx.data()), buff, countBytes); + } else { + memcpy(buff, m->ptr(idx.data()), countBytes); + } } else { - // dim by dim - int num = (m->size[m->dims-1] - idx[m->dims-1]) * (int)m->elemSize(); // 1st partial row - if(countsize[m->dims-1] * m->elemSize(); + size_t firstPartialBlockSize = (m->size[m->dims-1] - idx[m->dims-1]) * m->step[m->dims-1];; + for (int dim=m->dims-2; dim>=0 && blockSize == m->step[dim]; dim--) { + blockSize *= m->size[dim]; + firstPartialBlockSize += (m->size[dim] - (idx[dim]+1)) * m->step[dim]; + } + size_t copyCount = (countBytesptr(idx.data()); - while(count>0){ - memcpy(data, buff + offset, num); - updateIdx(m, idx, num / (int)m->elemSize()); - count -= num; - buff += num; - num = m->size[m->dims-1] * (int)m->elemSize(); - if(count0){ + if (isPut) { + memcpy(data, buff, copyCount); + } else { + memcpy(buff, data, copyCount); + } + updateIdx(m, idx, copyCount / m->elemSize()); + countBytes -= copyCount; + buff += copyCount; + copyCount = countBytesptr(idx.data()); } } return res; } +template static int mat_put_idx(cv::Mat* m, std::vector& idx, int count, int offset, char* buff) +{ + return mat_copy_data(m, idx, count, buff + offset, true); +} + +template static int mat_put(cv::Mat* m, int row, int col, int count, int offset, char* buff) +{ + int indicesArray[] = { row, col }; + std::vector indices(indicesArray, indicesArray+2); + return mat_put_idx(m, indices, count, offset, buff); +} + template static jint java_mat_put(JNIEnv* env, jlong self, jint row, jint col, jint count, jint offset, ARRAY vals) { static const char *method_name = JavaOpenCVTrait::put; @@ -2455,68 +2458,16 @@ JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutDIdx } // extern "C" -template static int mat_get(cv::Mat* m, int row, int col, int count, char* buff) -{ - if(! m) return 0; - if(! buff) return 0; - - int bytesToCopy = count * sizeof(T); - int bytesRestInMat = ((m->rows - row) * m->cols - col) * (int)m->elemSize(); - if(bytesToCopy > bytesRestInMat) bytesToCopy = bytesRestInMat; - int res = bytesToCopy; - - if( m->isContinuous() ) - { - memcpy(buff, m->ptr(row, col), bytesToCopy); - } else { - // row by row - int bytesInRow = (m->cols - col) * (int)m->elemSize(); // 1st partial row - while(bytesToCopy > 0) - { - int len = std::min(bytesToCopy, bytesInRow); - memcpy(buff, m->ptr(row, col), len); - bytesToCopy -= len; - buff += len; - row++; - col = 0; - bytesInRow = m->cols * (int)m->elemSize(); - } - } - return res; -} - template static int mat_get_idx(cv::Mat* m, std::vector& idx, int count, char* buff) { - if(! m) return 0; - if(! buff) return 0; + return mat_copy_data(m, idx, count, buff, false); +} - count *= sizeof(T); - int rest = (int)m->elemSize(); - for (int i = 0; i < m->dims; i++) { - rest *= (m->size[i] - idx[i]); - } - if(count>rest) count = rest; - int res = count; - - if( m->isContinuous() ) - { - memcpy(buff, m->ptr(idx.data()), count); - } else { - // dim by dim - int num = (m->size[m->dims-1] - idx[m->dims-1]) * (int)m->elemSize(); // 1st partial row - if(countptr(idx.data()); - while(count>0){ - memcpy(buff, data, num); - updateIdx(m, idx, num / (int)m->elemSize()); - count -= num; - buff += num; - num = m->size[m->dims-1] * (int)m->elemSize(); - if(countptr(idx.data()); - } - } - return res; +template static int mat_get(cv::Mat* m, int row, int col, int count, char* buff) +{ + int indicesArray[] = { row, col }; + std::vector indices(indicesArray, indicesArray+2); + return mat_get_idx(m, indices, count, buff); } template static jint java_mat_get(JNIEnv* env, jlong self, jint row, jint col, jint count, ARRAY vals) { diff --git a/modules/java/test/android_test/src/org/opencv/test/OpenCVTestCase.java b/modules/java/test/android_test/src/org/opencv/test/OpenCVTestCase.java index 5c6432c9f2..802bb2daa4 100644 --- a/modules/java/test/android_test/src/org/opencv/test/OpenCVTestCase.java +++ b/modules/java/test/android_test/src/org/opencv/test/OpenCVTestCase.java @@ -279,19 +279,23 @@ public class OpenCVTestCase extends TestCase { } public static void assertArrayEquals(E[] ar1, E[] ar2, double epsilon) { - if (ar1.length != ar2.length) { - fail("Arrays have different sizes."); - } + assertEquals(ar1.length, ar2.length); for (int i = 0; i < ar1.length; i++) assertEquals(ar1[i].doubleValue(), ar2[i].doubleValue(), epsilon); //assertTrue(Math.abs(ar1[i].doubleValue() - ar2[i].doubleValue()) <= epsilon); } + public static void assertArrayEquals(short[] ar1, short[] ar2) { + assertEquals(ar1.length, ar2.length); + + for (int i = 0; i < ar1.length; i++) + assertEquals(ar1[i], ar2[i]); + //assertTrue(Math.abs(ar1[i].doubleValue() - ar2[i].doubleValue()) <= epsilon); + } + public static void assertArrayEquals(double[] ar1, double[] ar2, double epsilon) { - if (ar1.length != ar2.length) { - fail("Arrays have different sizes."); - } + assertEquals(ar1.length, ar2.length); for (int i = 0; i < ar1.length; i++) assertEquals(ar1[i], ar2[i], epsilon); diff --git a/modules/java/test/pure_test/src/org/opencv/test/OpenCVTestCase.java b/modules/java/test/pure_test/src/org/opencv/test/OpenCVTestCase.java index 5b303f3836..3fd918dbfe 100644 --- a/modules/java/test/pure_test/src/org/opencv/test/OpenCVTestCase.java +++ b/modules/java/test/pure_test/src/org/opencv/test/OpenCVTestCase.java @@ -305,19 +305,22 @@ public class OpenCVTestCase extends TestCase { } public static void assertArrayEquals(E[] ar1, E[] ar2, double epsilon) { - if (ar1.length != ar2.length) { - fail("Arrays have different sizes."); - } + assertEquals(ar1.length, ar2.length); for (int i = 0; i < ar1.length; i++) assertEquals(ar1[i].doubleValue(), ar2[i].doubleValue(), epsilon); //assertTrue(Math.abs(ar1[i].doubleValue() - ar2[i].doubleValue()) <= epsilon); } + public static void assertArrayEquals(short[] ar1, short[] ar2) { + assertEquals(ar1.length, ar2.length); + + for (int i = 0; i < ar1.length; i++) + assertEquals(ar1[i], ar2[i]); + } + public static void assertArrayEquals(double[] ar1, double[] ar2, double epsilon) { - if (ar1.length != ar2.length) { - fail("Arrays have different sizes."); - } + assertEquals(ar1.length, ar2.length); for (int i = 0; i < ar1.length; i++) assertEquals(ar1[i], ar2[i], epsilon); From 309e1e2b1dd2c3db33cbc53d5684c070329b2da0 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 15 Feb 2021 21:36:33 +0000 Subject: [PATCH 03/12] core(InputArray): replace STD_ARRAY to MATX - remove duplication kind --- modules/core/include/opencv2/core/cvdef.h | 2 + modules/core/include/opencv2/core/mat.hpp | 8 ++- modules/core/include/opencv2/core/mat.inl.hpp | 18 +++--- modules/core/src/matrix_wrap.cpp | 64 ++++++++++++------- modules/core/test/test_mat.cpp | 1 - 5 files changed, 58 insertions(+), 35 deletions(-) diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index 38f9eed452..7d61e8714b 100644 --- a/modules/core/include/opencv2/core/cvdef.h +++ b/modules/core/include/opencv2/core/cvdef.h @@ -400,7 +400,9 @@ typedef union Cv64suf } Cv64suf; +#ifndef OPENCV_ABI_COMPATIBILITY #define OPENCV_ABI_COMPATIBILITY 300 +#endif #ifdef __OPENCV_BUILD # define DISABLE_OPENCV_24_COMPATIBILITY diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index 98f451cf71..6fedeaa974 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -170,7 +170,9 @@ public: STD_VECTOR = 3 << KIND_SHIFT, STD_VECTOR_VECTOR = 4 << KIND_SHIFT, STD_VECTOR_MAT = 5 << KIND_SHIFT, - EXPR = 6 << KIND_SHIFT, //!< removed +#if OPENCV_ABI_COMPATIBILITY < 500 + EXPR = 6 << KIND_SHIFT, //!< removed: https://github.com/opencv/opencv/pull/17046 +#endif OPENGL_BUFFER = 7 << KIND_SHIFT, CUDA_HOST_MEM = 8 << KIND_SHIFT, CUDA_GPU_MAT = 9 << KIND_SHIFT, @@ -178,7 +180,9 @@ public: STD_VECTOR_UMAT =11 << KIND_SHIFT, STD_BOOL_VECTOR =12 << KIND_SHIFT, STD_VECTOR_CUDA_GPU_MAT = 13 << KIND_SHIFT, - STD_ARRAY =14 << KIND_SHIFT, +#if OPENCV_ABI_COMPATIBILITY < 500 + STD_ARRAY =14 << KIND_SHIFT, //!< removed: https://github.com/opencv/opencv/issues/18897 +#endif STD_ARRAY_MAT =15 << KIND_SHIFT }; diff --git a/modules/core/include/opencv2/core/mat.inl.hpp b/modules/core/include/opencv2/core/mat.inl.hpp index b6ffd81795..c1d5261b8c 100644 --- a/modules/core/include/opencv2/core/mat.inl.hpp +++ b/modules/core/include/opencv2/core/mat.inl.hpp @@ -112,7 +112,7 @@ _InputArray::_InputArray(const std::vector<_Tp>& vec) #ifdef CV_CXX_STD_ARRAY template inline _InputArray::_InputArray(const std::array<_Tp, _Nm>& arr) -{ init(FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_READ, arr.data(), Size(1, _Nm)); } +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_READ, arr.data(), Size(1, _Nm)); } template inline _InputArray::_InputArray(const std::array& arr) @@ -176,7 +176,7 @@ template inline _InputArray _InputArray::rawIn(const std::array<_Tp, _Nm>& arr) { _InputArray v; - v.flags = FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_READ; + v.flags = FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_READ; v.obj = (void*)arr.data(); v.sz = Size(1, _Nm); return v; @@ -199,7 +199,7 @@ inline bool _InputArray::isUMatVector() const { return kind() == _InputArray::S inline bool _InputArray::isMatx() const { return kind() == _InputArray::MATX; } inline bool _InputArray::isVector() const { return kind() == _InputArray::STD_VECTOR || kind() == _InputArray::STD_BOOL_VECTOR || - kind() == _InputArray::STD_ARRAY; } + (kind() == _InputArray::MATX && (sz.width <= 1 || sz.height <= 1)); } inline bool _InputArray::isGpuMat() const { return kind() == _InputArray::CUDA_GPU_MAT; } inline bool _InputArray::isGpuMatVector() const { return kind() == _InputArray::STD_VECTOR_CUDA_GPU_MAT; } @@ -219,7 +219,7 @@ _OutputArray::_OutputArray(std::vector<_Tp>& vec) #ifdef CV_CXX_STD_ARRAY template inline _OutputArray::_OutputArray(std::array<_Tp, _Nm>& arr) -{ init(FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_WRITE, arr.data(), Size(1, _Nm)); } +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE, arr.data(), Size(1, _Nm)); } template inline _OutputArray::_OutputArray(std::array& arr) @@ -261,7 +261,7 @@ _OutputArray::_OutputArray(const std::vector<_Tp>& vec) #ifdef CV_CXX_STD_ARRAY template inline _OutputArray::_OutputArray(const std::array<_Tp, _Nm>& arr) -{ init(FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_WRITE, arr.data(), Size(1, _Nm)); } +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE, arr.data(), Size(1, _Nm)); } template inline _OutputArray::_OutputArray(const std::array& arr) @@ -336,7 +336,7 @@ template inline _OutputArray _OutputArray::rawOut(std::array<_Tp, _Nm>& arr) { _OutputArray v; - v.flags = FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_WRITE; + v.flags = FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_WRITE; v.obj = (void*)arr.data(); v.sz = Size(1, _Nm); return v; @@ -359,7 +359,7 @@ _InputOutputArray::_InputOutputArray(std::vector<_Tp>& vec) #ifdef CV_CXX_STD_ARRAY template inline _InputOutputArray::_InputOutputArray(std::array<_Tp, _Nm>& arr) -{ init(FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_RW, arr.data(), Size(1, _Nm)); } +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW, arr.data(), Size(1, _Nm)); } template inline _InputOutputArray::_InputOutputArray(std::array& arr) @@ -396,7 +396,7 @@ _InputOutputArray::_InputOutputArray(const std::vector<_Tp>& vec) #ifdef CV_CXX_STD_ARRAY template inline _InputOutputArray::_InputOutputArray(const std::array<_Tp, _Nm>& arr) -{ init(FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_RW, arr.data(), Size(1, _Nm)); } +{ init(FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW, arr.data(), Size(1, _Nm)); } template inline _InputOutputArray::_InputOutputArray(const std::array& arr) @@ -473,7 +473,7 @@ template inline _InputOutputArray _InputOutputArray::rawInOut(std::array<_Tp, _Nm>& arr) { _InputOutputArray v; - v.flags = FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_RW; + v.flags = FIXED_TYPE + FIXED_SIZE + MATX + traits::Type<_Tp>::value + ACCESS_RW; v.obj = (void*)arr.data(); v.sz = Size(1, _Nm); return v; diff --git a/modules/core/src/matrix_wrap.cpp b/modules/core/src/matrix_wrap.cpp index ad697d204c..0d439759cc 100644 --- a/modules/core/src/matrix_wrap.cpp +++ b/modules/core/src/matrix_wrap.cpp @@ -32,7 +32,7 @@ Mat _InputArray::getMat_(int i) const return m->getMat(accessFlags).row(i); } - if( k == MATX || k == STD_ARRAY ) + if (k == MATX) { CV_Assert( i < 0 ); return Mat(sz, flags, obj); @@ -172,7 +172,7 @@ void _InputArray::getMatVector(std::vector& mv) const return; } - if( k == MATX || k == STD_ARRAY ) + if (k == MATX) { size_t n = sz.height, esz = CV_ELEM_SIZE(flags); mv.resize(n); @@ -361,7 +361,14 @@ ogl::Buffer _InputArray::getOGlBuffer() const int _InputArray::kind() const { int k = flags & KIND_MASK; +#if CV_VERSION_MAJOR < 5 CV_DbgAssert(k != EXPR); +#if CV_VERSION_MAJOR < 4 + if (k == STD_ARRAY) + k = MATX; +#endif + CV_DbgAssert(k != STD_ARRAY); +#endif return k; } @@ -391,7 +398,7 @@ Size _InputArray::size(int i) const return ((const UMat*)obj)->size(); } - if( k == MATX || k == STD_ARRAY ) + if (k == MATX) { CV_Assert( i < 0 ); return sz; @@ -610,7 +617,7 @@ int _InputArray::dims(int i) const return ((const UMat*)obj)->dims; } - if( k == MATX || k == STD_ARRAY ) + if (k == MATX) { CV_Assert( i < 0 ); return 2; @@ -744,7 +751,7 @@ int _InputArray::type(int i) const if( k == UMAT ) return ((const UMat*)obj)->type(); - if( k == MATX || k == STD_VECTOR || k == STD_ARRAY || k == STD_VECTOR_VECTOR || k == STD_BOOL_VECTOR ) + if( k == MATX || k == STD_VECTOR || k == STD_VECTOR_VECTOR || k == STD_BOOL_VECTOR ) return CV_MAT_TYPE(flags); if( k == NONE ) @@ -830,7 +837,7 @@ bool _InputArray::empty() const if( k == UMAT ) return ((const UMat*)obj)->empty(); - if( k == MATX || k == STD_ARRAY ) + if (k == MATX) return false; if( k == STD_VECTOR ) @@ -899,7 +906,7 @@ bool _InputArray::isContinuous(int i) const if( k == UMAT ) return i < 0 ? ((const UMat*)obj)->isContinuous() : true; - if( k == MATX || k == STD_VECTOR || k == STD_ARRAY || + if( k == MATX || k == STD_VECTOR || k == NONE || k == STD_VECTOR_VECTOR || k == STD_BOOL_VECTOR ) return true; @@ -940,7 +947,7 @@ bool _InputArray::isSubmatrix(int i) const if( k == UMAT ) return i < 0 ? ((const UMat*)obj)->isSubmatrix() : false; - if( k == MATX || k == STD_VECTOR || k == STD_ARRAY || + if( k == MATX || k == STD_VECTOR || k == NONE || k == STD_VECTOR_VECTOR || k == STD_BOOL_VECTOR ) return false; @@ -985,7 +992,7 @@ size_t _InputArray::offset(int i) const return ((const UMat*)obj)->offset; } - if( k == MATX || k == STD_VECTOR || k == STD_ARRAY || + if( k == MATX || k == STD_VECTOR || k == NONE || k == STD_VECTOR_VECTOR || k == STD_BOOL_VECTOR ) return 0; @@ -1044,7 +1051,7 @@ size_t _InputArray::step(int i) const return ((const UMat*)obj)->step; } - if( k == MATX || k == STD_VECTOR || k == STD_ARRAY || + if( k == MATX || k == STD_VECTOR || k == NONE || k == STD_VECTOR_VECTOR || k == STD_BOOL_VECTOR ) return 0; @@ -1090,7 +1097,7 @@ void _InputArray::copyTo(const _OutputArray& arr) const if( k == NONE ) arr.release(); - else if( k == MAT || k == MATX || k == STD_VECTOR || k == STD_ARRAY || k == STD_BOOL_VECTOR ) + else if( k == MAT || k == MATX || k == STD_VECTOR || k == STD_BOOL_VECTOR ) { Mat m = getMat(); m.copyTo(arr); @@ -1111,7 +1118,7 @@ void _InputArray::copyTo(const _OutputArray& arr, const _InputArray & mask) cons if( k == NONE ) arr.release(); - else if( k == MAT || k == MATX || k == STD_VECTOR || k == STD_ARRAY || k == STD_BOOL_VECTOR ) + else if( k == MAT || k == MATX || k == STD_VECTOR || k == STD_BOOL_VECTOR ) { Mat m = getMat(); m.copyTo(arr, mask); @@ -1299,16 +1306,27 @@ void _OutputArray::create(int d, const int* sizes, int mtype, int i, CV_Assert( i < 0 ); int type0 = CV_MAT_TYPE(flags); CV_Assert( mtype == type0 || (CV_MAT_CN(mtype) == 1 && ((1 << type0) & fixedDepthMask) != 0) ); - CV_Assert( d == 2 && ((sizes[0] == sz.height && sizes[1] == sz.width) || - (allowTransposed && sizes[0] == sz.width && sizes[1] == sz.height))); - return; - } - - if( k == STD_ARRAY ) - { - int type0 = CV_MAT_TYPE(flags); - CV_Assert( mtype == type0 || (CV_MAT_CN(mtype) == 1 && ((1 << type0) & fixedDepthMask) != 0) ); - CV_Assert( d == 2 && sz.area() == sizes[0]*sizes[1]); + CV_CheckLE(d, 2, ""); + Size requested_size(d == 2 ? sizes[1] : 1, d >= 1 ? sizes[0] : 1); + if (sz.width == 1 || sz.height == 1) + { + // NB: 1D arrays assume allowTransposed=true (see #4159) + int total_1d = std::max(sz.width, sz.height); + CV_Check(requested_size, std::max(requested_size.width, requested_size.height) == total_1d, ""); + } + else + { + if (!allowTransposed) + { + CV_CheckEQ(requested_size, sz, ""); + } + else + { + CV_Check(requested_size, + (requested_size == sz || (requested_size.height == sz.width && requested_size.width == sz.height)), + ""); + } + } return; } @@ -1770,7 +1788,7 @@ void _OutputArray::setTo(const _InputArray& arr, const _InputArray & mask) const if( k == NONE ) ; - else if( k == MAT || k == MATX || k == STD_VECTOR || k == STD_ARRAY ) + else if (k == MAT || k == MATX || k == STD_VECTOR) { Mat m = getMat(); m.setTo(arr, mask); diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index a2426ad977..2afd926bd1 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -1998,7 +1998,6 @@ class TestInputArrayRangeChecking { C(EXPR); C(MATX); C(STD_VECTOR); - C(STD_ARRAY); C(NONE); C(STD_VECTOR_VECTOR); C(STD_BOOL_VECTOR); From 9b399f39607d249f91a0ae28e7cb67a29d607c5a Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Wed, 17 Feb 2021 12:04:49 +0300 Subject: [PATCH 04/12] Update python samples --- samples/python/houghcircles.py | 1 + .../back_projection/calcBackProject_Demo1.py | 4 ++-- .../back_projection/calcBackProject_Demo2.py | 4 ++-- .../histogram_calculation/calcHist_Demo.py | 12 ++++++------ .../corner_subpixels/cornerSubPix_Demo.py | 2 +- .../goodFeaturesToTrack_Demo.py | 2 +- .../ml/introduction_to_svm/introduction_to_svm.py | 2 +- .../ml/non_linear_svms/non_linear_svms.py | 6 +++--- .../python/tutorial_code/ml/py_svm_opencv/hogsvm.py | 2 +- .../tutorial_code/video/optical_flow/optical_flow.py | 9 +++++---- .../tutorial_code/videoio/video-input-psnr-ssim.py | 4 ++-- 11 files changed, 25 insertions(+), 23 deletions(-) diff --git a/samples/python/houghcircles.py b/samples/python/houghcircles.py index 416309aab0..60a36714fe 100755 --- a/samples/python/houghcircles.py +++ b/samples/python/houghcircles.py @@ -30,6 +30,7 @@ def main(): circles = cv.HoughCircles(img, cv.HOUGH_GRADIENT, 1, 10, np.array([]), 100, 30, 1, 30) if circles is not None: # Check if circles have been found and only then iterate over these and add them to the image + circles = np.uint16(np.around(circles)) _a, b, _c = circles.shape for i in range(b): cv.circle(cimg, (circles[0][i][0], circles[0][i][1]), circles[0][i][2], (0, 0, 255), 3, cv.LINE_AA) diff --git a/samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py b/samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py index fc85aca339..8086b9327e 100644 --- a/samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py +++ b/samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo1.py @@ -38,10 +38,10 @@ def Hist_and_Backproj(val): ## [Read the image] parser = argparse.ArgumentParser(description='Code for Back Projection tutorial.') -parser.add_argument('--input', help='Path to input image.') +parser.add_argument('--input', help='Path to input image.', default='home.jpg') args = parser.parse_args() -src = cv.imread(args.input) +src = cv.imread(cv.samples.findFile(args.input)) if src is None: print('Could not open or find the image:', args.input) exit(0) diff --git a/samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo2.py b/samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo2.py index 24255a2bb8..42d5a9b81f 100644 --- a/samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo2.py +++ b/samples/python/tutorial_code/Histograms_Matching/back_projection/calcBackProject_Demo2.py @@ -54,10 +54,10 @@ def Hist_and_Backproj(mask): # Read the image parser = argparse.ArgumentParser(description='Code for Back Projection tutorial.') -parser.add_argument('--input', help='Path to input image.') +parser.add_argument('--input', help='Path to input image.', default='home.jpg') args = parser.parse_args() -src = cv.imread(args.input) +src = cv.imread(cv.samples.findFile(args.input)) if src is None: print('Could not open or find the image:', args.input) exit(0) diff --git a/samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py b/samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py index 992ca90141..1789d49358 100644 --- a/samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py +++ b/samples/python/tutorial_code/Histograms_Matching/histogram_calculation/calcHist_Demo.py @@ -53,14 +53,14 @@ cv.normalize(r_hist, r_hist, alpha=0, beta=hist_h, norm_type=cv.NORM_MINMAX) ## [Draw for each channel] for i in range(1, histSize): - cv.line(histImage, ( bin_w*(i-1), hist_h - int(round(b_hist[i-1])) ), - ( bin_w*(i), hist_h - int(round(b_hist[i])) ), + cv.line(histImage, ( bin_w*(i-1), hist_h - int(b_hist[i-1]) ), + ( bin_w*(i), hist_h - int(b_hist[i]) ), ( 255, 0, 0), thickness=2) - cv.line(histImage, ( bin_w*(i-1), hist_h - int(round(g_hist[i-1])) ), - ( bin_w*(i), hist_h - int(round(g_hist[i])) ), + cv.line(histImage, ( bin_w*(i-1), hist_h - int(g_hist[i-1]) ), + ( bin_w*(i), hist_h - int(g_hist[i]) ), ( 0, 255, 0), thickness=2) - cv.line(histImage, ( bin_w*(i-1), hist_h - int(round(r_hist[i-1])) ), - ( bin_w*(i), hist_h - int(round(r_hist[i])) ), + cv.line(histImage, ( bin_w*(i-1), hist_h - int(r_hist[i-1]) ), + ( bin_w*(i), hist_h - int(r_hist[i]) ), ( 0, 0, 255), thickness=2) ## [Draw for each channel] diff --git a/samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py b/samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py index 6d8738b310..37e38abd44 100644 --- a/samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py +++ b/samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py @@ -30,7 +30,7 @@ def goodFeaturesToTrack_Demo(val): print('** Number of corners detected:', corners.shape[0]) radius = 4 for i in range(corners.shape[0]): - cv.circle(copy, (corners[i,0,0], corners[i,0,1]), radius, (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)), cv.FILLED) + cv.circle(copy, (int(corners[i,0,0]), int(corners[i,0,1])), radius, (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)), cv.FILLED) # Show what you got cv.namedWindow(source_window) diff --git a/samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py b/samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py index 3fb8441d92..ef082bddc1 100644 --- a/samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py +++ b/samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py @@ -30,7 +30,7 @@ def goodFeaturesToTrack_Demo(val): print('** Number of corners detected:', corners.shape[0]) radius = 4 for i in range(corners.shape[0]): - cv.circle(copy, (corners[i,0,0], corners[i,0,1]), radius, (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)), cv.FILLED) + cv.circle(copy, (int(corners[i,0,0]), int(corners[i,0,1])), radius, (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)), cv.FILLED) # Show what you got cv.namedWindow(source_window) diff --git a/samples/python/tutorial_code/ml/introduction_to_svm/introduction_to_svm.py b/samples/python/tutorial_code/ml/introduction_to_svm/introduction_to_svm.py index 1a5f202420..eeb246bc38 100644 --- a/samples/python/tutorial_code/ml/introduction_to_svm/introduction_to_svm.py +++ b/samples/python/tutorial_code/ml/introduction_to_svm/introduction_to_svm.py @@ -53,7 +53,7 @@ thickness = 2 sv = svm.getUncompressedSupportVectors() for i in range(sv.shape[0]): - cv.circle(image, (sv[i,0], sv[i,1]), 6, (128, 128, 128), thickness) + cv.circle(image, (int(sv[i,0]), int(sv[i,1])), 6, (128, 128, 128), thickness) ## [show_vectors] cv.imwrite('result.png', image) # save the image diff --git a/samples/python/tutorial_code/ml/non_linear_svms/non_linear_svms.py b/samples/python/tutorial_code/ml/non_linear_svms/non_linear_svms.py index fc4b56c454..a88ac4bd1b 100644 --- a/samples/python/tutorial_code/ml/non_linear_svms/non_linear_svms.py +++ b/samples/python/tutorial_code/ml/non_linear_svms/non_linear_svms.py @@ -94,13 +94,13 @@ thick = -1 for i in range(NTRAINING_SAMPLES): px = trainData[i,0] py = trainData[i,1] - cv.circle(I, (px, py), 3, (0, 255, 0), thick) + cv.circle(I, (int(px), int(py)), 3, (0, 255, 0), thick) # Class 2 for i in range(NTRAINING_SAMPLES, 2*NTRAINING_SAMPLES): px = trainData[i,0] py = trainData[i,1] - cv.circle(I, (px, py), 3, (255, 0, 0), thick) + cv.circle(I, (int(px), int(py)), 3, (255, 0, 0), thick) ## [show_data] #------------------------- 6. Show support vectors -------------------------------------------- @@ -109,7 +109,7 @@ thick = 2 sv = svm.getUncompressedSupportVectors() for i in range(sv.shape[0]): - cv.circle(I, (sv[i,0], sv[i,1]), 6, (128, 128, 128), thick) + cv.circle(I, (int(sv[i,0]), int(sv[i,1])), 6, (128, 128, 128), thick) ## [show_vectors] cv.imwrite('result.png', I) # save the Image diff --git a/samples/python/tutorial_code/ml/py_svm_opencv/hogsvm.py b/samples/python/tutorial_code/ml/py_svm_opencv/hogsvm.py index 79333a4f94..898c7dc4d7 100755 --- a/samples/python/tutorial_code/ml/py_svm_opencv/hogsvm.py +++ b/samples/python/tutorial_code/ml/py_svm_opencv/hogsvm.py @@ -33,7 +33,7 @@ def hog(img): return hist ## [hog] -img = cv.imread('digits.png',0) +img = cv.imread(cv.samples.findFile('digits.png'),0) if img is None: raise Exception("we need the digits.png image from samples/data here !") diff --git a/samples/python/tutorial_code/video/optical_flow/optical_flow.py b/samples/python/tutorial_code/video/optical_flow/optical_flow.py index c367407e45..93bb2c421e 100644 --- a/samples/python/tutorial_code/video/optical_flow/optical_flow.py +++ b/samples/python/tutorial_code/video/optical_flow/optical_flow.py @@ -40,15 +40,16 @@ while(1): p1, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params) # Select good points - good_new = p1[st==1] - good_old = p0[st==1] + if p1 is not None: + good_new = p1[st==1] + good_old = p0[st==1] # draw the tracks for i,(new,old) in enumerate(zip(good_new, good_old)): a,b = new.ravel() c,d = old.ravel() - mask = cv.line(mask, (a,b),(c,d), color[i].tolist(), 2) - frame = cv.circle(frame,(a,b),5,color[i].tolist(),-1) + mask = cv.line(mask, (int(a),int(b)),(int(c),int(d)), color[i].tolist(), 2) + frame = cv.circle(frame,(int(a),int(b)),5,color[i].tolist(),-1) img = cv.add(frame,mask) cv.imshow('frame',img) diff --git a/samples/python/tutorial_code/videoio/video-input-psnr-ssim.py b/samples/python/tutorial_code/videoio/video-input-psnr-ssim.py index 84610d4768..973854feb6 100644 --- a/samples/python/tutorial_code/videoio/video-input-psnr-ssim.py +++ b/samples/python/tutorial_code/videoio/video-input-psnr-ssim.py @@ -86,8 +86,8 @@ def main(): framenum = -1 # Frame counter - captRefrnc = cv.VideoCapture(sourceReference) - captUndTst = cv.VideoCapture(sourceCompareWith) + captRefrnc = cv.VideoCapture(cv.samples.findFileOrKeep(sourceReference)) + captUndTst = cv.VideoCapture(cv.samples.findFileOrKeep(sourceCompareWith)) if not captRefrnc.isOpened(): print("Could not open the reference " + sourceReference) From f6bc4fd4c62e42339080478bf8e12ebc42ceed74 Mon Sep 17 00:00:00 2001 From: WeiChungChang Date: Mon, 22 Feb 2021 11:44:00 -0500 Subject: [PATCH 05/12] Merge pull request #19552 from WeiChungChang:partialSort apply partial sort to save computations * apply partial sort * fix typo * fix accroding to CR --- modules/dnn/src/layers/detection_output_layer.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/dnn/src/layers/detection_output_layer.cpp b/modules/dnn/src/layers/detection_output_layer.cpp index 76142181ae..55ef36f95c 100644 --- a/modules/dnn/src/layers/detection_output_layer.cpp +++ b/modules/dnn/src/layers/detection_output_layer.cpp @@ -611,8 +611,13 @@ public: } } // Keep outputs k results per image. - std::sort(scoreIndexPairs.begin(), scoreIndexPairs.end(), - util::SortScorePairDescend >); + if ((_keepTopK * 8) > scoreIndexPairs.size()) { + std::sort(scoreIndexPairs.begin(), scoreIndexPairs.end(), + util::SortScorePairDescend >); + } else { + std::partial_sort(scoreIndexPairs.begin(), scoreIndexPairs.begin() + _keepTopK, scoreIndexPairs.end(), + util::SortScorePairDescend >); + } scoreIndexPairs.resize(_keepTopK); std::map > newIndices; From 86cb435adf8ed62552b2dea401b1d1645f416591 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 23 Feb 2021 03:31:29 +0000 Subject: [PATCH 06/12] dnn: fix build (nullptr issue) --- modules/dnn/src/layers/proposal_layer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dnn/src/layers/proposal_layer.cpp b/modules/dnn/src/layers/proposal_layer.cpp index 060a94db78..69660b8d1b 100644 --- a/modules/dnn/src/layers/proposal_layer.cpp +++ b/modules/dnn/src/layers/proposal_layer.cpp @@ -287,7 +287,7 @@ public: CV_Assert(imInfo.total() >= 2); // We've chosen the smallest data type because we need just a shape from it. // We don't allocate memory but just need the shape is correct. - Mat fakeImageBlob(shape(1, 1, imInfo.at(0), imInfo.at(1)), CV_8UC1, nullptr); + Mat fakeImageBlob(shape(1, 1, imInfo.at(0), imInfo.at(1)), CV_8UC1, NULL); // Generate prior boxes. std::vector layerInputs(2), layerOutputs(1, priorBoxes); From d4d12164aa130fcf4c6e7cf3c003b9285df0ff22 Mon Sep 17 00:00:00 2001 From: WeiChungChang Date: Tue, 23 Feb 2021 16:09:55 -0500 Subject: [PATCH 07/12] Merge pull request #19529 from WeiChungChang:3.4 * improve map allocation check * fix accoring to CR --- modules/dnn/src/layers/detection_output_layer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/dnn/src/layers/detection_output_layer.cpp b/modules/dnn/src/layers/detection_output_layer.cpp index 55ef36f95c..2dd6f5fb73 100644 --- a/modules/dnn/src/layers/detection_output_layer.cpp +++ b/modules/dnn/src/layers/detection_output_layer.cpp @@ -852,16 +852,16 @@ public: for (int i = 0; i < num; ++i, locData += numPredsPerClass * numLocClasses * 4) { LabelBBox& labelBBox = locPreds[i]; + int start = shareLocation ? -1 : 0; + for (int c = 0; c < numLocClasses; ++c) { + labelBBox[start++].resize(numPredsPerClass); + } for (int p = 0; p < numPredsPerClass; ++p) { int startIdx = p * numLocClasses * 4; for (int c = 0; c < numLocClasses; ++c) { int label = shareLocation ? -1 : c; - if (labelBBox.find(label) == labelBBox.end()) - { - labelBBox[label].resize(numPredsPerClass); - } util::NormalizedBBox& bbox = labelBBox[label][p]; if (locPredTransposed) { From b2321576bc0a651cd41a013dbbf074d7bda2918f Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Wed, 24 Feb 2021 20:31:00 +0300 Subject: [PATCH 08/12] Fixed several issues found by static analysis --- modules/calib3d/src/sqpnp.hpp | 1 + modules/dnn/src/layers/pooling_layer.cpp | 2 +- modules/videoio/src/cap_msmf.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/calib3d/src/sqpnp.hpp b/modules/calib3d/src/sqpnp.hpp index f8136324c9..97c10e34e7 100644 --- a/modules/calib3d/src/sqpnp.hpp +++ b/modules/calib3d/src/sqpnp.hpp @@ -72,6 +72,7 @@ private: cv::Matx r_hat; cv::Matx t; double sq_error; + SQPSolution() : sq_error(0) {} }; /* diff --git a/modules/dnn/src/layers/pooling_layer.cpp b/modules/dnn/src/layers/pooling_layer.cpp index 834493966f..ac25bf4dae 100644 --- a/modules/dnn/src/layers/pooling_layer.cpp +++ b/modules/dnn/src/layers/pooling_layer.cpp @@ -750,7 +750,7 @@ virtual Ptr initNgraph(const std::vector >& inp if (max_elem!=last) { dstData[x0] = *max_elem; - if( compMaxIdx ) + if( compMaxIdx && dstMaskData ) { dstMaskData[x0] = std::distance(first, max_elem); } diff --git a/modules/videoio/src/cap_msmf.cpp b/modules/videoio/src/cap_msmf.cpp index 7caa7c1ea0..fe3d261d34 100644 --- a/modules/videoio/src/cap_msmf.cpp +++ b/modules/videoio/src/cap_msmf.cpp @@ -309,7 +309,7 @@ class SourceReaderCB : public IMFSourceReaderCallback { public: SourceReaderCB() : - m_nRefCount(0), m_hEvent(CreateEvent(NULL, FALSE, FALSE, NULL)), m_bEOS(FALSE), m_hrStatus(S_OK), m_reader(NULL), m_dwStreamIndex(0) + m_nRefCount(0), m_hEvent(CreateEvent(NULL, FALSE, FALSE, NULL)), m_bEOS(FALSE), m_hrStatus(S_OK), m_reader(NULL), m_dwStreamIndex(0), m_lastSampleTimestamp(0) { } From fadb25baf86fa23c75bfef807ed498bb179040c4 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 25 Feb 2021 20:16:50 +0000 Subject: [PATCH 09/12] imgproc(warpAffine): avoid buffer indexes overflow in SIMD code --- modules/imgproc/src/imgwarp.cpp | 2 +- modules/imgproc/test/test_imgwarp.cpp | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 1665e0a40c..298f9b3b5b 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -446,7 +446,7 @@ struct RemapVec_8u { int cn = _src.channels(), x = 0, sstep = (int)_src.step; - if( (cn != 1 && cn != 3 && cn != 4) || sstep > 0x8000 ) + if( (cn != 1 && cn != 3 && cn != 4) || sstep >= 0x8000 ) return 0; const uchar *S0 = _src.ptr(), *S1 = _src.ptr(1); diff --git a/modules/imgproc/test/test_imgwarp.cpp b/modules/imgproc/test/test_imgwarp.cpp index 73d513e85f..7d0360dfb1 100644 --- a/modules/imgproc/test/test_imgwarp.cpp +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -1781,6 +1781,26 @@ TEST(Imgproc_Warp, multichannel) } } + +TEST(Imgproc_Warp, regression_19566) // valgrind should detect problem if any +{ + const Size imgSize(8192, 8); + + Mat inMat = Mat::zeros(imgSize, CV_8UC4); + Mat outMat = Mat::zeros(imgSize, CV_8UC4); + + warpAffine( + inMat, + outMat, + getRotationMatrix2D(Point2f(imgSize.width / 2.0f, imgSize.height / 2.0f), 45.0, 1.0), + imgSize, + INTER_LINEAR, + cv::BORDER_CONSTANT, + cv::Scalar(0.0, 0.0, 0.0, 255.0) + ); +} + + TEST(Imgproc_GetAffineTransform, singularity) { Point2f A_sample[3]; From 773262bc098322f3e603dc6abe06de85240f1f66 Mon Sep 17 00:00:00 2001 From: Federico Martinez Date: Tue, 23 Feb 2021 08:44:17 +0100 Subject: [PATCH 10/12] Fix UB in CopyMakeConstBoder_8u Caused by overflow of arithmetic operators conversion rank --- modules/core/src/copy.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index dcd585d834..798fde74d4 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -1325,13 +1325,12 @@ void copyMakeConstBorder_8u( const uchar* src, size_t srcstep, cv::Size srcroi, memcpy( dstInner + srcroi.width, constBuf, right ); } - dst += dststep*top; - for( i = 0; i < top; i++ ) - memcpy(dst + (i - top)*dststep, constBuf, dstroi.width); + memcpy(dst + i * dststep, constBuf, dstroi.width); + dst += (top + srcroi.height) * dststep; for( i = 0; i < bottom; i++ ) - memcpy(dst + (i + srcroi.height)*dststep, constBuf, dstroi.width); + memcpy(dst + i * dststep, constBuf, dstroi.width); } } From 84080c12fd17ee6a42ef210430967b26b5289e51 Mon Sep 17 00:00:00 2001 From: LaurentBerger Date: Sat, 27 Feb 2021 13:18:13 +0100 Subject: [PATCH 11/12] python binding blendLinear --- modules/imgproc/include/opencv2/imgproc.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 601c2e4687..6705b7a046 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -3634,7 +3634,7 @@ CV_EXPORTS_W int floodFill( InputOutputArray image, InputOutputArray mask, //! @param weights1 It has a type of CV_32FC1 and the same size with src1. //! @param weights2 It has a type of CV_32FC1 and the same size with src1. //! @param dst It is created if it does not have the same size and type with src1. -CV_EXPORTS void blendLinear(InputArray src1, InputArray src2, InputArray weights1, InputArray weights2, OutputArray dst); +CV_EXPORTS_W void blendLinear(InputArray src1, InputArray src2, InputArray weights1, InputArray weights2, OutputArray dst); //! @} imgproc_misc From 9695165877aa35f32e588645d888ad136a8039b1 Mon Sep 17 00:00:00 2001 From: Federico Bolelli <6863130+prittt@users.noreply.github.com> Date: Sat, 27 Feb 2021 18:27:24 +0100 Subject: [PATCH 12/12] Merge pull request #19631 from prittt:sota-ccl * Add Spaghetti algorithm for CCL * Add stat tests for new and old algorithms * Switch license header to short version --- doc/opencv.bib | 33 + modules/imgproc/include/opencv2/imgproc.hpp | 10 +- .../imgproc/src/ccl_bolelli_forest.inc.hpp | 1746 +++++++++++++++++ .../src/ccl_bolelli_forest_firstline.inc.hpp | 218 ++ .../src/ccl_bolelli_forest_lastline.inc.hpp | 731 +++++++ .../src/ccl_bolelli_forest_singleline.inc.hpp | 95 + modules/imgproc/src/connectedcomponents.cpp | 596 ++++-- .../imgproc/test/test_connectedcomponents.cpp | 123 +- 8 files changed, 3435 insertions(+), 117 deletions(-) create mode 100644 modules/imgproc/src/ccl_bolelli_forest.inc.hpp create mode 100644 modules/imgproc/src/ccl_bolelli_forest_firstline.inc.hpp create mode 100644 modules/imgproc/src/ccl_bolelli_forest_lastline.inc.hpp create mode 100644 modules/imgproc/src/ccl_bolelli_forest_singleline.inc.hpp diff --git a/doc/opencv.bib b/doc/opencv.bib index 6045a8434d..787cb0c2fb 100644 --- a/doc/opencv.bib +++ b/doc/opencv.bib @@ -110,6 +110,29 @@ year = {2010}, url = {http://ingmec.ual.es/~jlblanco/papers/jlblanco2010geometry3D_techrep.pdf} } +@inproceedings{Bolelli2017, + title = {{Two More Strategies to Speed Up Connected Components Labeling Algorithms}}, + author = {Bolelli, Federico and Cancilla, Michele and Grana, Costantino}, + year = 2017, + booktitle = {Image Analysis and Processing - ICIAP 2017}, + publisher = {Springer}, + volume = 10485, + pages = {48--58}, + doi = {10.1007/978-3-319-68548-9_5}, + isbn = {978-3-319-68547-2} +} +@article{Bolelli2019, + title = {{Spaghetti Labeling: Directed Acyclic Graphs for Block-Based Connected Components Labeling}}, + author = {Bolelli, Federico and Allegretti, Stefano and Baraldi, Lorenzo and Grana, Costantino}, + year = 2019, + journal = {IEEE Transactions on Image Processing}, + publisher = {IEEE}, + volume = 29, + number = 1, + pages = {1999--2012}, + doi = {10.1109/TIP.2019.2946979}, + issn = {1057-7149} +} @article{Borgefors86, author = {Borgefors, Gunilla}, title = {Distance transformations in digital images}, @@ -420,6 +443,16 @@ volume = {51}, pages = {378-384} } +@article{Grana2010, + title = {{Optimized Block-Based Connected Components Labeling With Decision Trees}}, + author = {Grana, Costantino and Borghesani, Daniele and Cucchiara, Rita}, + year = 2010, + journal = {IEEE Transactions on Image Processing}, + volume = 19, + number = 6, + pages = {1596--1609}, + doi = {10.1109/TIP.2010.2044963} +} @article{taubin1991, abstract = {The author addresses the problem of parametric representation and estimation of complex planar curves in 2-D surfaces in 3-D, and nonplanar space curves in 3-D. Curves and surfaces can be defined either parametrically or implicitly, with the latter representation used here. A planar curve is the set of zeros of a smooth function of two variables x-y, a surface is the set of zeros of a smooth function of three variables x-y-z, and a space curve is the intersection of two surfaces, which are the set of zeros of two linearly independent smooth functions of three variables x-y-z For example, the surface of a complex object in 3-D can be represented as a subset of a single implicit surface, with similar results for planar and space curves. It is shown how this unified representation can be used for object recognition, object position estimation, and segmentation of objects into meaningful subobjects, that is, the detection of `interest regions' that are more complex than high curvature regions and, hence, more useful as features for object recognition}, author = {Taubin, Gabriel}, diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 601c2e4687..7e2eeb3761 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -406,9 +406,13 @@ enum ConnectedComponentsTypes { //! connected components algorithm enum ConnectedComponentsAlgorithmsTypes { - CCL_WU = 0, //!< SAUF @cite Wu2009 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity - CCL_DEFAULT = -1, //!< BBDT algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity - CCL_GRANA = 1 //!< BBDT algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity + CCL_DEFAULT = -1, //!< BBDT @cite Grana2010 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity. The parallel implementation described in @cite Bolelli2017 is available for both BBDT and SAUF. + CCL_WU = 0, //!< SAUF @cite Wu2009 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity. The parallel implementation described in @cite Bolelli2017 is available for SAUF. + CCL_GRANA = 1, //!< BBDT @cite Grana2010 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity. The parallel implementation described in @cite Bolelli2017 is available for both BBDT and SAUF. + CCL_BOLELLI = 2, //!< Spaghetti @cite Bolelli2019 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity. + CCL_SAUF = 3, //!< Same as CCL_WU. It is preferable to use the flag with the name of the algorithm (CCL_SAUF) rather than the one with the name of the first author (CCL_WU). + CCL_BBDT = 4, //!< Same as CCL_GRANA. It is preferable to use the flag with the name of the algorithm (CCL_BBDT) rather than the one with the name of the first author (CCL_GRANA). + CCL_SPAGHETTI = 5, //!< Same as CCL_BOLELLI. It is preferable to use the flag with the name of the algorithm (CCL_SPAGHETTI) rather than the one with the name of the first author (CCL_BOLELLI). }; //! mode of the contour retrieval algorithm diff --git a/modules/imgproc/src/ccl_bolelli_forest.inc.hpp b/modules/imgproc/src/ccl_bolelli_forest.inc.hpp new file mode 100644 index 0000000000..1aa17557d9 --- /dev/null +++ b/modules/imgproc/src/ccl_bolelli_forest.inc.hpp @@ -0,0 +1,1746 @@ +// 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. +// +// 2021 Federico Bolelli +// 2021 Stefano Allegretti +// 2021 Costantino Grana +// +// This file has been automatically generated using GRAPHGEN (https://github.com/prittt/GRAPHGEN) +// and taken from the YACCLAB repository (https://github.com/prittt/YACCLAB). +tree_0: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_0; } else { goto break_1_0; } } + if (CONDITION_O) { + NODE_1: + if (CONDITION_J) { + ACTION_4 + goto tree_11; + } + else { + if (CONDITION_P) { + NODE_3: + if (CONDITION_K) { + if (CONDITION_I) { + NODE_5: + if (CONDITION_D) { + ACTION_5 + goto tree_5; + } + else { + ACTION_10 + goto tree_5; + } + } + else { + ACTION_5 + goto tree_5; + } + } + else { + if (CONDITION_I) { + ACTION_4 + goto tree_4; + } + else { + ACTION_2 + goto tree_3; + } + } + } + else { + if (CONDITION_I) { + ACTION_4 + goto tree_10; + } + else { + ACTION_2 + goto tree_9; + } + } + } + } + else { + NODE_8: + if (CONDITION_S) { + if (CONDITION_P) { + NODE_10: + if (CONDITION_J) { + ACTION_4 + goto tree_6; + } + else{ + goto NODE_3; + } + } + else { + ACTION_2 + goto tree_7; + } + } + else { + NODE_11: + if (CONDITION_P){ + goto NODE_10; + } + else { + NODE_12: + if (CONDITION_T) { + ACTION_2 + goto tree_2; + } + else { + ACTION_1 + goto tree_1; + } + } + } + } +tree_1: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_1; } else { goto break_1_1; } } + if (CONDITION_O) { + NODE_13: + if (CONDITION_J) { + if (CONDITION_I) { + ACTION_4 + goto tree_11; + } + else { + if (CONDITION_H) { + NODE_16: + if (CONDITION_C) { + ACTION_4 + goto tree_11; + } + else { + ACTION_7 + goto tree_11; + } + } + else { + ACTION_4 + goto tree_11; + } + } + } + else { + if (CONDITION_P) { + if (CONDITION_K) { + if (CONDITION_I){ + goto NODE_5; + } + else { + if (CONDITION_H) { + NODE_21: + if (CONDITION_D) { + if (CONDITION_C) { + ACTION_5 + goto tree_5; + } + else { + ACTION_8 + goto tree_5; + } + } + else { + ACTION_8 + goto tree_5; + } + } + else { + ACTION_5 + goto tree_5; + } + } + } + else { + if (CONDITION_I) { + ACTION_4 + goto tree_4; + } + else { + if (CONDITION_H) { + ACTION_3 + goto tree_3; + } + else { + ACTION_2 + goto tree_3; + } + } + } + } + else { + if (CONDITION_I) { + ACTION_4 + goto tree_10; + } + else { + if (CONDITION_H) { + ACTION_3 + goto tree_9; + } + else { + ACTION_2 + goto tree_9; + } + } + } + } + } + else{ + goto NODE_8; + } +tree_2: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_2; } else { goto break_1_2; } } + if (CONDITION_O) { + NODE_27: + if (CONDITION_J) { + if (CONDITION_I) { + ACTION_11 + goto tree_11; + } + else { + if (CONDITION_H) { + if (CONDITION_C) { + ACTION_11 + goto tree_11; + } + else { + ACTION_14 + goto tree_11; + } + } + else { + ACTION_11 + goto tree_11; + } + } + } + else { + if (CONDITION_P) { + if (CONDITION_K) { + if (CONDITION_I) { + if (CONDITION_D) { + ACTION_12 + goto tree_5; + } + else { + ACTION_16 + goto tree_5; + } + } + else { + if (CONDITION_H) { + if (CONDITION_D) { + if (CONDITION_C) { + ACTION_12 + goto tree_5; + } + else { + ACTION_15 + goto tree_5; + } + } + else { + ACTION_15 + goto tree_5; + } + } + else { + ACTION_12 + goto tree_5; + } + } + } + else { + if (CONDITION_H) { + ACTION_9 + goto tree_8; + } + else { + NODE_39: + if (CONDITION_I) { + ACTION_11 + goto tree_4; + } + else { + ACTION_6 + goto tree_3; + } + } + } + } + else { + if (CONDITION_H) { + ACTION_9 + goto tree_12; + } + else { + NODE_41: + if (CONDITION_I) { + ACTION_11 + goto tree_10; + } + else { + ACTION_6 + goto tree_9; + } + } + } + } + } + else { + if (CONDITION_S) { + if (CONDITION_P) { + NODE_44: + if (CONDITION_J) { + ACTION_11 + goto tree_6; + } + else { + NODE_45: + if (CONDITION_K) { + if (CONDITION_D) { + ACTION_12 + goto tree_5; + } + else { + if (CONDITION_I) { + ACTION_16 + goto tree_5; + } + else { + ACTION_12 + goto tree_5; + } + } + } + else{ + goto NODE_39; + } + } + } + else { + ACTION_6 + goto tree_7; + } + } + else{ + goto NODE_11; + } + } +tree_3: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_3; } else { goto break_1_3; } } + if (CONDITION_O) { + if (CONDITION_J) { + ACTION_11 + goto tree_11; + } + else { + if (CONDITION_P) { + NODE_50: + if (CONDITION_K) { + ACTION_12 + goto tree_5; + } + else { + ACTION_6 + goto tree_8; + } + } + else { + ACTION_6 + goto tree_12; + } + } + } + else { + if (CONDITION_S) { + if (CONDITION_P) { + if (CONDITION_J) { + ACTION_11 + goto tree_6; + } + else{ + goto NODE_50; + } + } + else { + ACTION_6 + goto tree_7; + } + } + else { + NODE_54: + if (CONDITION_P) { + if (CONDITION_J) { + ACTION_4 + goto tree_6; + } + else { + if (CONDITION_K) { + ACTION_5 + goto tree_5; + } + else { + ACTION_2 + goto tree_3; + } + } + } + else{ + goto NODE_12; + } + } + } +tree_4: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_3; } else { goto break_1_4; } } + if (CONDITION_O) { + if (CONDITION_J) { + if (CONDITION_C) { + NODE_59: + if (CONDITION_B) { + ACTION_4 + goto tree_11; + } + else { + ACTION_11 + goto tree_11; + } + } + else { + ACTION_11 + goto tree_11; + } + } + else { + if (CONDITION_P) { + NODE_61: + if (CONDITION_K) { + if (CONDITION_D) { + if (CONDITION_C) { + NODE_64: + if (CONDITION_B) { + ACTION_5 + goto tree_5; + } + else { + ACTION_12 + goto tree_5; + } + } + else { + ACTION_12 + goto tree_5; + } + } + else { + ACTION_12 + goto tree_5; + } + } + else { + ACTION_6 + goto tree_8; + } + } + else { + ACTION_6 + goto tree_12; + } + } + } + else { + if (CONDITION_S) { + if (CONDITION_P) { + if (CONDITION_J) { + if (CONDITION_C) { + NODE_69: + if (CONDITION_B) { + ACTION_4 + goto tree_6; + } + else { + ACTION_11 + goto tree_6; + } + } + else { + ACTION_11 + goto tree_6; + } + } + else{ + goto NODE_61; + } + } + else { + ACTION_6 + goto tree_7; + } + } + else{ + goto NODE_54; + } + } +tree_5: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_3; } else { goto break_1_5; } } + if (CONDITION_O) { + if (CONDITION_J) { + ACTION_4 + goto tree_11; + } + else { + if (CONDITION_P) { + NODE_72: + if (CONDITION_K) { + if (CONDITION_D) { + ACTION_5 + goto tree_5; + } + else { + ACTION_12 + goto tree_5; + } + } + else { + ACTION_6 + goto tree_8; + } + } + else { + ACTION_6 + goto tree_12; + } + } + } + else { + if (CONDITION_S) { + if (CONDITION_P) { + if (CONDITION_J) { + ACTION_4 + goto tree_6; + } + else{ + goto NODE_72; + } + } + else { + ACTION_6 + goto tree_7; + } + } + else { + if (CONDITION_P) { + if (CONDITION_J) { + ACTION_4 + goto tree_6; + } + else { + if (CONDITION_K){ + goto NODE_5; + } + else { + ACTION_4 + goto tree_4; + } + } + } + else{ + goto NODE_12; + } + } + } +tree_6: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_3; } else { goto break_1_6; } } + if (CONDITION_O) { + NODE_80: + if (CONDITION_J) { + NODE_81: + if (CONDITION_I) { + ACTION_4 + goto tree_11; + } + else { + if (CONDITION_C) { + ACTION_4 + goto tree_11; + } + else { + ACTION_11 + goto tree_11; + } + } + } + else { + if (CONDITION_P) { + NODE_84: + if (CONDITION_K) { + NODE_85: + if (CONDITION_D) { + NODE_86: + if (CONDITION_I) { + ACTION_5 + goto tree_5; + } + else { + if (CONDITION_C) { + ACTION_5 + goto tree_5; + } + else { + ACTION_12 + goto tree_5; + } + } + } + else { + ACTION_12 + goto tree_5; + } + } + else { + ACTION_6 + goto tree_8; + } + } + else { + ACTION_6 + goto tree_12; + } + } + } + else { + if (CONDITION_S) { + if (CONDITION_P) { + NODE_90: + if (CONDITION_J) { + NODE_91: + if (CONDITION_I) { + ACTION_4 + goto tree_6; + } + else { + if (CONDITION_C) { + ACTION_4 + goto tree_6; + } + else { + ACTION_11 + goto tree_6; + } + } + } + else{ + goto NODE_84; + } + } + else { + ACTION_6 + goto tree_7; + } + } + else{ + goto NODE_11; + } + } +tree_7: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_4; } else { goto break_1_7; } } + if (CONDITION_O) { + if (CONDITION_R){ + goto NODE_27; + } + else{ + goto NODE_13; + } + } + else { + NODE_94: + if (CONDITION_S) { + if (CONDITION_P) { + if (CONDITION_R){ + goto NODE_44; + } + else{ + goto NODE_10; + } + } + else { + NODE_97: + if (CONDITION_R) { + ACTION_6 + goto tree_7; + } + else { + ACTION_2 + goto tree_7; + } + } + } + else{ + goto NODE_11; + } + } +tree_8: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_3; } else { goto break_1_8; } } + if (CONDITION_O) { + if (CONDITION_J) { + if (CONDITION_C) { + if (CONDITION_G){ + goto NODE_59; + } + else { + ACTION_11 + goto tree_11; + } + } + else { + ACTION_11 + goto tree_11; + } + } + else { + if (CONDITION_P) { + NODE_102: + if (CONDITION_K) { + if (CONDITION_D) { + if (CONDITION_C) { + if (CONDITION_G){ + goto NODE_64; + } + else { + ACTION_12 + goto tree_5; + } + } + else { + ACTION_12 + goto tree_5; + } + } + else { + ACTION_12 + goto tree_5; + } + } + else { + ACTION_6 + goto tree_8; + } + } + else { + ACTION_6 + goto tree_12; + } + } + } + else { + if (CONDITION_S) { + if (CONDITION_P) { + if (CONDITION_J) { + if (CONDITION_C) { + if (CONDITION_G){ + goto NODE_69; + } + else { + ACTION_11 + goto tree_6; + } + } + else { + ACTION_11 + goto tree_6; + } + } + else{ + goto NODE_102; + } + } + else { + ACTION_6 + goto tree_7; + } + } + else{ + goto NODE_54; + } + } +tree_9: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_5; } else { goto break_1_9; } } + if (CONDITION_O) { + if (CONDITION_R) { + if (CONDITION_J) { + ACTION_11 + goto tree_11; + } + else { + if (CONDITION_P){ + goto NODE_45; + } + else{ + goto NODE_41; + } + } + } + else{ + goto NODE_1; + } + } + else{ + goto NODE_94; + } +tree_10: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_6; } else { goto break_1_10; } } + if (CONDITION_O) { + if (CONDITION_R) { + if (CONDITION_J) { + NODE_116: + if (CONDITION_B){ + goto NODE_81; + } + else { + ACTION_11 + goto tree_11; + } + } + else { + if (CONDITION_P) { + NODE_118: + if (CONDITION_K) { + if (CONDITION_D) { + NODE_120: + if (CONDITION_B){ + goto NODE_86; + } + else { + ACTION_12 + goto tree_5; + } + } + else { + if (CONDITION_I) { + NODE_122: + if (CONDITION_B) { + ACTION_12 + goto tree_5; + } + else { + ACTION_16 + goto tree_5; + } + } + else { + ACTION_12 + goto tree_5; + } + } + } + else { + if (CONDITION_I) { + NODE_124: + if (CONDITION_B) { + ACTION_4 + goto tree_4; + } + else { + ACTION_11 + goto tree_4; + } + } + else { + ACTION_6 + goto tree_3; + } + } + } + else { + if (CONDITION_I) { + NODE_126: + if (CONDITION_B) { + ACTION_4 + goto tree_10; + } + else { + ACTION_11 + goto tree_10; + } + } + else { + ACTION_6 + goto tree_9; + } + } + } + } + else{ + goto NODE_1; + } + } + else { + if (CONDITION_S) { + if (CONDITION_P) { + if (CONDITION_R) { + if (CONDITION_J) { + NODE_131: + if (CONDITION_B){ + goto NODE_91; + } + else { + ACTION_11 + goto tree_6; + } + } + else{ + goto NODE_118; + } + } + else{ + goto NODE_10; + } + } + else{ + goto NODE_97; + } + } + else{ + goto NODE_11; + } + } +tree_11: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_7; } else { goto break_1_11; } } + if (CONDITION_O) { + if (CONDITION_N){ + goto NODE_80; + } + else { + if (CONDITION_R){ + goto NODE_80; + } + else { + if (CONDITION_J) { + if (CONDITION_I) { + ACTION_4 + goto tree_11; + } + else{ + goto NODE_16; + } + } + else { + if (CONDITION_P) { + if (CONDITION_K) { + if (CONDITION_I){ + goto NODE_5; + } + else{ + goto NODE_21; + } + } + else { + if (CONDITION_I) { + ACTION_4 + goto tree_4; + } + else { + ACTION_3 + goto tree_3; + } + } + } + else { + if (CONDITION_I) { + ACTION_4 + goto tree_10; + } + else { + ACTION_3 + goto tree_9; + } + } + } + } + } + } + else { + if (CONDITION_S) { + if (CONDITION_P) { + if (CONDITION_N){ + goto NODE_90; + } + else { + if (CONDITION_R) { + if (CONDITION_J){ + goto NODE_91; + } + else { + if (CONDITION_K){ + goto NODE_85; + } + else { + if (CONDITION_I) { + ACTION_4 + goto tree_4; + } + else { + ACTION_6 + goto tree_3; + } + } + } + } + else{ + goto NODE_10; + } + } + } + else { + if (CONDITION_R) { + ACTION_6 + goto tree_7; + } + else { + if (CONDITION_N) { + ACTION_6 + goto tree_7; + } + else { + ACTION_2 + goto tree_7; + } + } + } + } + else{ + goto NODE_11; + } + } +tree_12: if ((c+=2) >= w - 2) { if (c > w - 2) { goto break_0_8; } else { goto break_1_12; } } + if (CONDITION_O) { + if (CONDITION_R) { + if (CONDITION_J) { + if (CONDITION_G){ + goto NODE_116; + } + else { + ACTION_11 + goto tree_11; + } + } + else { + if (CONDITION_P) { + NODE_154: + if (CONDITION_K) { + if (CONDITION_D) { + if (CONDITION_G){ + goto NODE_120; + } + else { + ACTION_12 + goto tree_5; + } + } + else { + if (CONDITION_I) { + if (CONDITION_G){ + goto NODE_122; + } + else { + ACTION_16 + goto tree_5; + } + } + else { + ACTION_12 + goto tree_5; + } + } + } + else { + if (CONDITION_I) { + if (CONDITION_G){ + goto NODE_124; + } + else { + ACTION_11 + goto tree_4; + } + } + else { + ACTION_6 + goto tree_3; + } + } + } + else { + if (CONDITION_I) { + if (CONDITION_G){ + goto NODE_126; + } + else { + ACTION_11 + goto tree_10; + } + } + else { + ACTION_6 + goto tree_9; + } + } + } + } + else{ + goto NODE_1; + } + } + else { + if (CONDITION_S) { + if (CONDITION_P) { + if (CONDITION_R) { + if (CONDITION_J) { + if (CONDITION_G){ + goto NODE_131; + } + else { + ACTION_11 + goto tree_6; + } + } + else{ + goto NODE_154; + } + } + else{ + goto NODE_10; + } + } + else{ + goto NODE_97; + } + } + else{ + goto NODE_11; + } + } +break_0_0: + if (CONDITION_O) { + NODE_168: + if (CONDITION_I) { + ACTION_4 + } + else { + ACTION_2 + } + } + else { + NODE_169: + if (CONDITION_S) { + ACTION_2 + } + else { + ACTION_1 + } + } + continue; +break_0_1: + if (CONDITION_O) { + NODE_170: + if (CONDITION_I) { + ACTION_4 + } + else { + if (CONDITION_H) { + ACTION_3 + } + else { + ACTION_2 + } + } + } + else{ + goto NODE_169; + } + continue; +break_0_2: + if (CONDITION_O) { + NODE_172: + if (CONDITION_H) { + ACTION_9 + } + else { + NODE_173: + if (CONDITION_I) { + ACTION_11 + } + else { + ACTION_6 + } + } + } + else { + NODE_174: + if (CONDITION_S) { + ACTION_6 + } + else { + ACTION_1 + } + } + continue; +break_0_3: + if (CONDITION_O) { + ACTION_6 + } + else{ + goto NODE_174; + } + continue; +break_0_4: + if (CONDITION_O) { + if (CONDITION_R){ + goto NODE_172; + } + else{ + goto NODE_170; + } + } + else { + NODE_176: + if (CONDITION_S) { + NODE_177: + if (CONDITION_R) { + ACTION_6 + } + else { + ACTION_2 + } + } + else { + ACTION_1 + } + } + continue; +break_0_5: + if (CONDITION_O) { + if (CONDITION_R){ + goto NODE_173; + } + else{ + goto NODE_168; + } + } + else{ + goto NODE_176; + } + continue; +break_0_6: + if (CONDITION_O) { + if (CONDITION_R) { + NODE_180: + if (CONDITION_I) { + NODE_181: + if (CONDITION_B) { + ACTION_4 + } + else { + ACTION_11 + } + } + else { + ACTION_6 + } + } + else{ + goto NODE_168; + } + } + else{ + goto NODE_176; + } + continue; +break_0_7: + if (CONDITION_O) { + if (CONDITION_N) { + ACTION_6 + } + else { + if (CONDITION_R) { + ACTION_6 + } + else { + NODE_184: + if (CONDITION_I) { + ACTION_4 + } + else { + ACTION_3 + } + } + } + } + else { + if (CONDITION_S) { + NODE_186: + if (CONDITION_R) { + ACTION_6 + } + else { + if (CONDITION_N) { + ACTION_6 + } + else { + ACTION_2 + } + } + } + else { + ACTION_1 + } + } + continue; +break_0_8: + if (CONDITION_O) { + if (CONDITION_R) { + NODE_189: + if (CONDITION_I) { + NODE_190: + if (CONDITION_G){ + goto NODE_181; + } + else { + ACTION_11 + } + } + else { + ACTION_6 + } + } + else{ + goto NODE_168; + } + } + else{ + goto NODE_176; + } + continue; +break_1_0: + if (CONDITION_O) { + NODE_191: + if (CONDITION_J) { + ACTION_4 + } + else{ + goto NODE_168; + } + } + else { + NODE_192: + if (CONDITION_S) { + if (CONDITION_P){ + goto NODE_191; + } + else { + ACTION_2 + } + } + else { + NODE_194: + if (CONDITION_P){ + goto NODE_191; + } + else { + NODE_195: + if (CONDITION_T) { + ACTION_2 + } + else { + ACTION_1 + } + } + } + } + continue; +break_1_1: + if (CONDITION_O) { + NODE_196: + if (CONDITION_J) { + if (CONDITION_I) { + ACTION_4 + } + else { + if (CONDITION_H) { + NODE_199: + if (CONDITION_C) { + ACTION_4 + } + else { + ACTION_7 + } + } + else { + ACTION_4 + } + } + } + else{ + goto NODE_170; + } + } + else{ + goto NODE_192; + } + continue; +break_1_2: + if (CONDITION_O) { + NODE_200: + if (CONDITION_J) { + if (CONDITION_I) { + ACTION_11 + } + else { + if (CONDITION_H) { + if (CONDITION_C) { + ACTION_11 + } + else { + ACTION_14 + } + } + else { + ACTION_11 + } + } + } + else{ + goto NODE_172; + } + } + else { + if (CONDITION_S) { + if (CONDITION_P) { + NODE_206: + if (CONDITION_J) { + ACTION_11 + } + else{ + goto NODE_173; + } + } + else { + ACTION_6 + } + } + else{ + goto NODE_194; + } + } + continue; +break_1_3: + if (CONDITION_O) { + NODE_207: + if (CONDITION_J) { + ACTION_11 + } + else { + ACTION_6 + } + } + else { + if (CONDITION_S) { + if (CONDITION_P){ + goto NODE_207; + } + else { + ACTION_6 + } + } + else { + NODE_210: + if (CONDITION_P) { + if (CONDITION_J) { + ACTION_4 + } + else { + ACTION_2 + } + } + else{ + goto NODE_195; + } + } + } + continue; +break_1_4: + if (CONDITION_O) { + NODE_212: + if (CONDITION_J) { + if (CONDITION_C){ + goto NODE_181; + } + else { + ACTION_11 + } + } + else { + ACTION_6 + } + } + else { + if (CONDITION_S) { + if (CONDITION_P){ + goto NODE_212; + } + else { + ACTION_6 + } + } + else{ + goto NODE_210; + } + } + continue; +break_1_5: + if (CONDITION_O) { + NODE_216: + if (CONDITION_J) { + ACTION_4 + } + else { + ACTION_6 + } + } + else { + if (CONDITION_S) { + if (CONDITION_P){ + goto NODE_216; + } + else { + ACTION_6 + } + } + else { + if (CONDITION_P) { + ACTION_4 + } + else{ + goto NODE_195; + } + } + } + continue; +break_1_6: + if (CONDITION_O) { + NODE_220: + if (CONDITION_J) { + NODE_221: + if (CONDITION_I) { + ACTION_4 + } + else { + if (CONDITION_C) { + ACTION_4 + } + else { + ACTION_11 + } + } + } + else { + ACTION_6 + } + } + else { + if (CONDITION_S) { + if (CONDITION_P){ + goto NODE_220; + } + else { + ACTION_6 + } + } + else{ + goto NODE_194; + } + } + continue; +break_1_7: + if (CONDITION_O) { + if (CONDITION_R){ + goto NODE_200; + } + else{ + goto NODE_196; + } + } + else { + NODE_226: + if (CONDITION_S) { + if (CONDITION_P) { + NODE_228: + if (CONDITION_R){ + goto NODE_206; + } + else{ + goto NODE_191; + } + } + else{ + goto NODE_177; + } + } + else{ + goto NODE_194; + } + } + continue; +break_1_8: + if (CONDITION_O) { + NODE_229: + if (CONDITION_J) { + if (CONDITION_C){ + goto NODE_190; + } + else { + ACTION_11 + } + } + else { + ACTION_6 + } + } + else { + if (CONDITION_S) { + if (CONDITION_P){ + goto NODE_229; + } + else { + ACTION_6 + } + } + else{ + goto NODE_210; + } + } + continue; +break_1_9: + if (CONDITION_O){ + goto NODE_228; + } + else{ + goto NODE_226; + } + continue; +break_1_10: + if (CONDITION_O) { + NODE_233: + if (CONDITION_R) { + if (CONDITION_J) { + NODE_235: + if (CONDITION_B){ + goto NODE_221; + } + else { + ACTION_11 + } + } + else{ + goto NODE_180; + } + } + else{ + goto NODE_191; + } + } + else { + if (CONDITION_S) { + if (CONDITION_P){ + goto NODE_233; + } + else{ + goto NODE_177; + } + } + else{ + goto NODE_194; + } + } + continue; +break_1_11: + if (CONDITION_O) { + if (CONDITION_N){ + goto NODE_220; + } + else { + if (CONDITION_R){ + goto NODE_220; + } + else { + if (CONDITION_J) { + if (CONDITION_I) { + ACTION_4 + } + else{ + goto NODE_199; + } + } + else{ + goto NODE_184; + } + } + } + } + else { + if (CONDITION_S) { + if (CONDITION_P) { + if (CONDITION_N){ + goto NODE_220; + } + else { + if (CONDITION_R) { + if (CONDITION_J){ + goto NODE_221; + } + else { + if (CONDITION_I) { + ACTION_4 + } + else { + ACTION_6 + } + } + } + else{ + goto NODE_191; + } + } + } + else{ + goto NODE_186; + } + } + else{ + goto NODE_194; + } + } + continue; +break_1_12: + if (CONDITION_O) { + NODE_248: + if (CONDITION_R) { + if (CONDITION_J) { + if (CONDITION_G){ + goto NODE_235; + } + else { + ACTION_11 + } + } + else{ + goto NODE_189; + } + } + else{ + goto NODE_191; + } + } + else { + if (CONDITION_S) { + if (CONDITION_P){ + goto NODE_248; + } + else{ + goto NODE_177; + } + } + else{ + goto NODE_194; + } + } + continue; diff --git a/modules/imgproc/src/ccl_bolelli_forest_firstline.inc.hpp b/modules/imgproc/src/ccl_bolelli_forest_firstline.inc.hpp new file mode 100644 index 0000000000..b452b9ff4f --- /dev/null +++ b/modules/imgproc/src/ccl_bolelli_forest_firstline.inc.hpp @@ -0,0 +1,218 @@ +// 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. +// +// 2021 Federico Bolelli +// 2021 Stefano Allegretti +// 2021 Costantino Grana +// +// This file has been automatically generated using GRAPHGEN (https://github.com/prittt/GRAPHGEN) +// and taken from the YACCLAB repository (https://github.com/prittt/YACCLAB). +fl_tree_0: if ((c+=2) >= w - 2) { if (c > w - 2) { goto fl_break_0_0; } else { goto fl_break_1_0; } } + if (CONDITION_O) { + NODE_253: + if (CONDITION_P) { + ACTION_2 + goto fl_tree_1; + } + else { + ACTION_2 + goto fl_tree_2; + } + } + else { + if (CONDITION_S){ + goto NODE_253; + } + else { + NODE_255: + if (CONDITION_P) { + ACTION_2 + goto fl_tree_1; + } + else { + if (CONDITION_T) { + ACTION_2 + goto fl_tree_1; + } + else { + ACTION_1 + goto fl_tree_0; + } + } + } + } +fl_tree_1: if ((c+=2) >= w - 2) { if (c > w - 2) { goto fl_break_0_1; } else { goto fl_break_1_1; } } + if (CONDITION_O) { + NODE_257: + if (CONDITION_P) { + ACTION_6 + goto fl_tree_1; + } + else { + ACTION_6 + goto fl_tree_2; + } + } + else { + if (CONDITION_S){ + goto NODE_257; + } + else{ + goto NODE_255; + } + } +fl_tree_2: if ((c+=2) >= w - 2) { if (c > w - 2) { goto fl_break_0_2; } else { goto fl_break_1_2; } } + if (CONDITION_O) { + if (CONDITION_R){ + goto NODE_257; + } + else{ + goto NODE_253; + } + } + else { + if (CONDITION_S) { + if (CONDITION_P) { + if (CONDITION_R) { + ACTION_6 + goto fl_tree_1; + } + else { + ACTION_2 + goto fl_tree_1; + } + } + else { + if (CONDITION_R) { + ACTION_6 + goto fl_tree_2; + } + else { + ACTION_2 + goto fl_tree_2; + } + } + } + else{ + goto NODE_255; + } + } +fl_break_0_0: + if (CONDITION_O) { + ACTION_2 + } + else { + if (CONDITION_S) { + ACTION_2 + } + else { + ACTION_1 + } + } + goto end_fl; +fl_break_0_1: + if (CONDITION_O) { + ACTION_6 + } + else { + if (CONDITION_S) { + ACTION_6 + } + else { + ACTION_1 + } + } + goto end_fl; +fl_break_0_2: + if (CONDITION_O) { + NODE_266: + if (CONDITION_R) { + ACTION_6 + } + else { + ACTION_2 + } + } + else { + if (CONDITION_S){ + goto NODE_266; + } + else { + ACTION_1 + } + } + goto end_fl; +fl_break_1_0: + if (CONDITION_O) { + NODE_268: + if (CONDITION_P) { + ACTION_2 + } + else { + ACTION_2 + } + } + else { + if (CONDITION_S){ + goto NODE_268; + } + else { + NODE_270: + if (CONDITION_P) { + ACTION_2 + } + else { + if (CONDITION_T) { + ACTION_2 + } + else { + ACTION_1 + } + } + } + } + goto end_fl; +fl_break_1_1: + if (CONDITION_O) { + NODE_272: + if (CONDITION_P) { + ACTION_6 + } + else { + ACTION_6 + } + } + else { + if (CONDITION_S){ + goto NODE_272; + } + else{ + goto NODE_270; + } + } + goto end_fl; +fl_break_1_2: + if (CONDITION_O) { + if (CONDITION_R){ + goto NODE_272; + } + else{ + goto NODE_268; + } + } + else { + if (CONDITION_S) { + if (CONDITION_P){ + goto NODE_266; + } + else{ + goto NODE_266; + } + } + else{ + goto NODE_270; + } + } + goto end_fl; +end_fl:; diff --git a/modules/imgproc/src/ccl_bolelli_forest_lastline.inc.hpp b/modules/imgproc/src/ccl_bolelli_forest_lastline.inc.hpp new file mode 100644 index 0000000000..09c1691738 --- /dev/null +++ b/modules/imgproc/src/ccl_bolelli_forest_lastline.inc.hpp @@ -0,0 +1,731 @@ +// 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. +// +// 2021 Federico Bolelli +// 2021 Stefano Allegretti +// 2021 Costantino Grana +// +// This file has been automatically generated using GRAPHGEN (https://github.com/prittt/GRAPHGEN) +// and taken from the YACCLAB repository (https://github.com/prittt/YACCLAB). +ll_tree_0: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_0; } else { goto ll_break_1_0; } } + if (CONDITION_O) { + if (CONDITION_J) { + ACTION_4 + goto ll_tree_6; + } + else { + if (CONDITION_P) { + NODE_277: + if (CONDITION_K) { + if (CONDITION_I) { + NODE_279: + if (CONDITION_D) { + ACTION_5 + goto ll_tree_4; + } + else { + ACTION_10 + goto ll_tree_4; + } + } + else { + ACTION_5 + goto ll_tree_4; + } + } + else { + if (CONDITION_I) { + ACTION_4 + goto ll_tree_3; + } + else { + ACTION_2 + goto ll_tree_2; + } + } + } + else { + if (CONDITION_I) { + ACTION_4 + goto ll_tree_0; + } + else { + ACTION_2 + goto ll_tree_0; + } + } + } + } + else { + NODE_282: + if (CONDITION_P) { + if (CONDITION_J) { + ACTION_4 + goto ll_tree_5; + } + else{ + goto NODE_277; + } + } + else { + ACTION_1 + goto ll_tree_1; + } + } +ll_tree_1: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_1; } else { goto ll_break_1_1; } } + if (CONDITION_O) { + if (CONDITION_J) { + if (CONDITION_I) { + ACTION_4 + goto ll_tree_6; + } + else { + if (CONDITION_H) { + NODE_287: + if (CONDITION_C) { + ACTION_4 + goto ll_tree_6; + } + else { + ACTION_7 + goto ll_tree_6; + } + } + else { + ACTION_4 + goto ll_tree_6; + } + } + } + else { + if (CONDITION_P) { + if (CONDITION_K) { + if (CONDITION_I){ + goto NODE_279; + } + else { + if (CONDITION_H) { + NODE_292: + if (CONDITION_D) { + if (CONDITION_C) { + ACTION_5 + goto ll_tree_4; + } + else { + ACTION_8 + goto ll_tree_4; + } + } + else { + ACTION_8 + goto ll_tree_4; + } + } + else { + ACTION_5 + goto ll_tree_4; + } + } + } + else { + if (CONDITION_I) { + ACTION_4 + goto ll_tree_3; + } + else { + if (CONDITION_H) { + ACTION_3 + goto ll_tree_2; + } + else { + ACTION_2 + goto ll_tree_2; + } + } + } + } + else { + if (CONDITION_I) { + ACTION_4 + goto ll_tree_0; + } + else { + if (CONDITION_H) { + ACTION_3 + goto ll_tree_0; + } + else { + ACTION_2 + goto ll_tree_0; + } + } + } + } + } + else{ + goto NODE_282; + } +ll_tree_2: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_2; } else { goto ll_break_1_2; } } + if (CONDITION_O) { + if (CONDITION_J) { + ACTION_11 + goto ll_tree_6; + } + else { + if (CONDITION_P) { + if (CONDITION_K) { + ACTION_12 + goto ll_tree_4; + } + else { + ACTION_6 + goto ll_tree_7; + } + } + else { + ACTION_6 + goto ll_tree_0; + } + } + } + else { + NODE_301: + if (CONDITION_P) { + if (CONDITION_J) { + ACTION_4 + goto ll_tree_5; + } + else { + if (CONDITION_K) { + ACTION_5 + goto ll_tree_4; + } + else { + ACTION_2 + goto ll_tree_2; + } + } + } + else { + ACTION_1 + goto ll_tree_1; + } + } +ll_tree_3: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_2; } else { goto ll_break_1_3; } } + if (CONDITION_O) { + if (CONDITION_J) { + if (CONDITION_C) { + NODE_306: + if (CONDITION_B) { + ACTION_4 + goto ll_tree_6; + } + else { + ACTION_7 + goto ll_tree_6; + } + } + else { + ACTION_11 + goto ll_tree_6; + } + } + else { + if (CONDITION_P) { + if (CONDITION_K) { + if (CONDITION_D) { + if (CONDITION_C) { + NODE_311: + if (CONDITION_B) { + ACTION_5 + goto ll_tree_4; + } + else { + ACTION_12 + goto ll_tree_4; + } + } + else { + ACTION_12 + goto ll_tree_4; + } + } + else { + ACTION_12 + goto ll_tree_4; + } + } + else { + ACTION_6 + goto ll_tree_7; + } + } + else { + ACTION_6 + goto ll_tree_0; + } + } + } + else{ + goto NODE_301; + } +ll_tree_4: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_2; } else { goto ll_break_1_4; } } + if (CONDITION_O) { + if (CONDITION_J) { + ACTION_4 + goto ll_tree_6; + } + else { + if (CONDITION_P) { + if (CONDITION_K) { + if (CONDITION_D) { + ACTION_5 + goto ll_tree_4; + } + else { + ACTION_12 + goto ll_tree_4; + } + } + else { + ACTION_6 + goto ll_tree_7; + } + } + else { + ACTION_6 + goto ll_tree_0; + } + } + } + else { + if (CONDITION_P) { + if (CONDITION_J) { + ACTION_4 + goto ll_tree_5; + } + else { + if (CONDITION_K){ + goto NODE_279; + } + else { + ACTION_4 + goto ll_tree_3; + } + } + } + else { + ACTION_1 + goto ll_tree_1; + } + } +ll_tree_5: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_2; } else { goto ll_break_1_5; } } + if (CONDITION_O) { + NODE_319: + if (CONDITION_J) { + if (CONDITION_I) { + ACTION_4 + goto ll_tree_6; + } + else { + if (CONDITION_C) { + ACTION_4 + goto ll_tree_6; + } + else { + ACTION_11 + goto ll_tree_6; + } + } + } + else { + if (CONDITION_P) { + if (CONDITION_K) { + if (CONDITION_D) { + if (CONDITION_I) { + ACTION_5 + goto ll_tree_4; + } + else { + if (CONDITION_C) { + ACTION_5 + goto ll_tree_4; + } + else { + ACTION_12 + goto ll_tree_4; + } + } + } + else { + ACTION_12 + goto ll_tree_4; + } + } + else { + ACTION_6 + goto ll_tree_7; + } + } + else { + ACTION_6 + goto ll_tree_0; + } + } + } + else{ + goto NODE_282; + } +ll_tree_6: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_3; } else { goto ll_break_1_6; } } + if (CONDITION_O) { + if (CONDITION_N){ + goto NODE_319; + } + else { + if (CONDITION_J) { + if (CONDITION_I) { + ACTION_4 + goto ll_tree_6; + } + else{ + goto NODE_287; + } + } + else { + if (CONDITION_P) { + if (CONDITION_K) { + if (CONDITION_I){ + goto NODE_279; + } + else{ + goto NODE_292; + } + } + else { + if (CONDITION_I) { + ACTION_4 + goto ll_tree_3; + } + else { + ACTION_3 + goto ll_tree_2; + } + } + } + else { + if (CONDITION_I) { + ACTION_4 + goto ll_tree_0; + } + else { + ACTION_3 + goto ll_tree_0; + } + } + } + } + } + else{ + goto NODE_282; + } +ll_tree_7: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_2; } else { goto ll_break_1_7; } } + if (CONDITION_O) { + if (CONDITION_J) { + if (CONDITION_C) { + if (CONDITION_G){ + goto NODE_306; + } + else { + ACTION_11 + goto ll_tree_6; + } + } + else { + ACTION_11 + goto ll_tree_6; + } + } + else { + if (CONDITION_P) { + if (CONDITION_K) { + if (CONDITION_D) { + if (CONDITION_C) { + if (CONDITION_G){ + goto NODE_311; + } + else { + ACTION_12 + goto ll_tree_4; + } + } + else { + ACTION_12 + goto ll_tree_4; + } + } + else { + ACTION_12 + goto ll_tree_4; + } + } + else { + ACTION_6 + goto ll_tree_7; + } + } + else { + ACTION_6 + goto ll_tree_0; + } + } + } + else{ + goto NODE_301; + } +ll_break_0_0: + if (CONDITION_O) { + NODE_343: + if (CONDITION_I) { + ACTION_4 + } + else { + ACTION_2 + } + } + else { + ACTION_1 + } + goto ll_end; +ll_break_0_1: + if (CONDITION_O) { + NODE_344: + if (CONDITION_I) { + ACTION_4 + } + else { + if (CONDITION_H) { + ACTION_3 + } + else { + ACTION_2 + } + } + } + else { + ACTION_1 + } + goto ll_end; +ll_break_0_2: + if (CONDITION_O) { + ACTION_6 + } + else { + ACTION_1 + } + goto ll_end; +ll_break_0_3: + if (CONDITION_O) { + if (CONDITION_N) { + ACTION_6 + } + else { + NODE_347: + if (CONDITION_I) { + ACTION_4 + } + else { + ACTION_3 + } + } + } + else { + ACTION_1 + } + goto ll_end; +ll_break_1_0: + if (CONDITION_O) { + NODE_348: + if (CONDITION_J) { + ACTION_4 + } + else{ + goto NODE_343; + } + } + else { + NODE_349: + if (CONDITION_P){ + goto NODE_348; + } + else { + ACTION_1 + } + } + goto ll_end; +ll_break_1_1: + if (CONDITION_O) { + if (CONDITION_J) { + if (CONDITION_I) { + ACTION_4 + } + else { + if (CONDITION_H) { + NODE_353: + if (CONDITION_C) { + ACTION_4 + } + else { + ACTION_7 + } + } + else { + ACTION_4 + } + } + } + else{ + goto NODE_344; + } + } + else{ + goto NODE_349; + } + goto ll_end; +ll_break_1_2: + if (CONDITION_O) { + if (CONDITION_J) { + ACTION_11 + } + else { + ACTION_6 + } + } + else { + NODE_355: + if (CONDITION_P) { + if (CONDITION_J) { + ACTION_4 + } + else { + ACTION_2 + } + } + else { + ACTION_1 + } + } + goto ll_end; +ll_break_1_3: + if (CONDITION_O) { + if (CONDITION_J) { + if (CONDITION_C) { + NODE_359: + if (CONDITION_B) { + ACTION_4 + } + else { + ACTION_7 + } + } + else { + ACTION_11 + } + } + else { + ACTION_6 + } + } + else{ + goto NODE_355; + } + goto ll_end; +ll_break_1_4: + if (CONDITION_O) { + if (CONDITION_J) { + ACTION_4 + } + else { + ACTION_6 + } + } + else { + if (CONDITION_P) { + ACTION_4 + } + else { + ACTION_1 + } + } + goto ll_end; +ll_break_1_5: + if (CONDITION_O) { + NODE_362: + if (CONDITION_J) { + if (CONDITION_I) { + ACTION_4 + } + else { + if (CONDITION_C) { + ACTION_4 + } + else { + ACTION_11 + } + } + } + else { + ACTION_6 + } + } + else{ + goto NODE_349; + } + goto ll_end; +ll_break_1_6: + if (CONDITION_O) { + if (CONDITION_N){ + goto NODE_362; + } + else { + if (CONDITION_J) { + if (CONDITION_I) { + ACTION_4 + } + else{ + goto NODE_353; + } + } + else{ + goto NODE_347; + } + } + } + else{ + goto NODE_349; + } + goto ll_end; +ll_break_1_7: + if (CONDITION_O) { + if (CONDITION_J) { + if (CONDITION_C) { + if (CONDITION_G){ + goto NODE_359; + } + else { + ACTION_11 + } + } + else { + ACTION_11 + } + } + else { + ACTION_6 + } + } + else{ + goto NODE_355; + } + goto ll_end; +ll_end:; diff --git a/modules/imgproc/src/ccl_bolelli_forest_singleline.inc.hpp b/modules/imgproc/src/ccl_bolelli_forest_singleline.inc.hpp new file mode 100644 index 0000000000..034b4ff6f7 --- /dev/null +++ b/modules/imgproc/src/ccl_bolelli_forest_singleline.inc.hpp @@ -0,0 +1,95 @@ +// 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. +// +// 2021 Federico Bolelli +// 2021 Stefano Allegretti +// 2021 Costantino Grana +// +// This file has been automatically generated using GRAPHGEN (https://github.com/prittt/GRAPHGEN) +// and taken from the YACCLAB repository (https://github.com/prittt/YACCLAB). +sl_tree_0: if ((c+=2) >= w - 2) { if (c > w - 2) { goto sl_break_0_0; } else { goto sl_break_1_0; } } + if (CONDITION_O) { + if (CONDITION_P) { + ACTION_2 + goto sl_tree_1; + } + else { + ACTION_2 + goto sl_tree_0; + } + } + else { + NODE_372: + if (CONDITION_P) { + ACTION_2 + goto sl_tree_1; + } + else { + ACTION_1 + goto sl_tree_0; + } + } +sl_tree_1: if ((c+=2) >= w - 2) { if (c > w - 2) { goto sl_break_0_1; } else { goto sl_break_1_1; } } + if (CONDITION_O) { + if (CONDITION_P) { + ACTION_6 + goto sl_tree_1; + } + else { + ACTION_6 + goto sl_tree_0; + } + } + else{ + goto NODE_372; + } +sl_break_0_0: + if (CONDITION_O) { + ACTION_2 + } + else { + ACTION_1 + } + goto end_sl; +sl_break_0_1: + if (CONDITION_O) { + ACTION_6 + } + else { + ACTION_1 + } + goto end_sl; +sl_break_1_0: + if (CONDITION_O) { + if (CONDITION_P) { + ACTION_2 + } + else { + ACTION_2 + } + } + else { + NODE_375: + if (CONDITION_P) { + ACTION_2 + } + else { + ACTION_1 + } + } + goto end_sl; +sl_break_1_1: + if (CONDITION_O) { + if (CONDITION_P) { + ACTION_6 + } + else { + ACTION_6 + } + } + else{ + goto NODE_375; + } + goto end_sl; +end_sl:; diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index c3a3f6f75b..e66766c872 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -38,11 +38,12 @@ // the use of this software, even if advised of the possibility of such damage. // // 2011 Jason Newton -// 2016 Costantino Grama -// 2016 Federico Bolelli +// 2016, 2021 Costantino Grana +// 2016, 2021 Federico Bolelli // 2016 Lorenzo Baraldi // 2016 Roberto Vezzani // 2016 Michele Cancilla +// 2021 Stefano Allegretti //M*/ // #include "precomp.hpp" @@ -286,10 +287,366 @@ namespace cv{ return LT((y /*+ 1*/) / 2) * LT((w + 1) / 2) + 1; } + //Implementation of Spaghetti algorithm, as described in "Spaghetti Labeling: Directed Acyclic Graphs for Block-Based + //Connected Components Labeling" (only for 8-connectivity) + //Federico Bolelli et. al. + template + struct LabelingBolelli + { + LabelT operator()(const cv::Mat& img, cv::Mat& imgLabels, int connectivity, StatsOp& sop) + { + CV_Assert(img.rows == imgLabels.rows); + CV_Assert(img.cols == imgLabels.cols); + CV_Assert(connectivity == 8); - //Based on "Two Strategies to Speed up Connected Components Algorithms", the SAUF (Scan array union find) variant - //using decision trees - //Kesheng Wu, et al + const int h = img.rows; + const int w = img.cols; + + const int e_rows = h & -2; + const bool o_rows = h % 2 == 1; + const int e_cols = w & -2; + const bool o_cols = w % 2 == 1; + + // A quick and dirty upper bound for the maximum number of labels. + // Following formula comes from the fact that a 2x2 block in 8-connectivity case + // can never have more than 1 new label and 1 label for background. + // Worst case image example pattern: + // 1 0 1 0 1... + // 0 0 0 0 0... + // 1 0 1 0 1... + // ............ + const size_t Plength = size_t(((h + 1) / 2) * size_t((w + 1) / 2)) + 1; + + std::vector P_(Plength, 0); + LabelT *P = P_.data(); + //P[0] = 0; + LabelT lunique = 1; + + // First scan + + // We work with 2x2 blocks + // +-+-+-+ + // |P|Q|R| + // +-+-+-+ + // |S|X| + // +-+-+ + + // The pixels are named as follows + // +---+---+---+ + // |a b|c d|e f| + // |g h|i j|k l| + // +---+---+---+ + // |m n|o p| + // |q r|s t| + // +---+---+ + + // Pixels a, f, l, q are not needed, since we need to understand the + // the connectivity between these blocks and those pixels only matter + // when considering the outer connectivities + + // A bunch of defines is used to check if the pixels are foreground + // and to define actions to be performed on blocks + { + #define CONDITION_B img_row_prev_prev[c-1]>0 + #define CONDITION_C img_row_prev_prev[c]>0 + #define CONDITION_D img_row_prev_prev[c+1]>0 + #define CONDITION_E img_row_prev_prev[c+2]>0 + + #define CONDITION_G img_row_prev[c-2]>0 + #define CONDITION_H img_row_prev[c-1]>0 + #define CONDITION_I img_row_prev[c]>0 + #define CONDITION_J img_row_prev[c+1]>0 + #define CONDITION_K img_row_prev[c+2]>0 + + #define CONDITION_M img_row[c-2]>0 + #define CONDITION_N img_row[c-1]>0 + #define CONDITION_O img_row[c]>0 + #define CONDITION_P img_row[c+1]>0 + + #define CONDITION_R img_row_fol[c-1]>0 + #define CONDITION_S img_row_fol[c]>0 + #define CONDITION_T img_row_fol[c+1]>0 + + // Action 1: No action + #define ACTION_1 img_labels_row[c] = 0; + // Action 2: New label (the block has foreground pixels and is not connected to anything else) + #define ACTION_2 img_labels_row[c] = lunique; \ + P[lunique] = lunique; \ + lunique = lunique + 1; + //Action 3: Assign label of block P + #define ACTION_3 img_labels_row[c] = img_labels_row_prev_prev[c - 2]; + // Action 4: Assign label of block Q + #define ACTION_4 img_labels_row[c] = img_labels_row_prev_prev[c]; + // Action 5: Assign label of block R + #define ACTION_5 img_labels_row[c] = img_labels_row_prev_prev[c + 2]; + // Action 6: Assign label of block S + #define ACTION_6 img_labels_row[c] = img_labels_row[c - 2]; + // Action 7: Merge labels of block P and Q + #define ACTION_7 img_labels_row[c] = set_union(P, img_labels_row_prev_prev[c - 2], img_labels_row_prev_prev[c]); + //Action 8: Merge labels of block P and R + #define ACTION_8 img_labels_row[c] = set_union(P, img_labels_row_prev_prev[c - 2], img_labels_row_prev_prev[c + 2]); + // Action 9 Merge labels of block P and S + #define ACTION_9 img_labels_row[c] = set_union(P, img_labels_row_prev_prev[c - 2], img_labels_row[c - 2]); + // Action 10 Merge labels of block Q and R + #define ACTION_10 img_labels_row[c] = set_union(P, img_labels_row_prev_prev[c], img_labels_row_prev_prev[c + 2]); + // Action 11: Merge labels of block Q and S + #define ACTION_11 img_labels_row[c] = set_union(P, img_labels_row_prev_prev[c], img_labels_row[c - 2]); + // Action 12: Merge labels of block R and S + #define ACTION_12 img_labels_row[c] = set_union(P, img_labels_row_prev_prev[c + 2], img_labels_row[c - 2]); + // Action 13: Merge labels of block P, Q and R + #define ACTION_13 img_labels_row[c] = set_union(P, set_union(P, img_labels_row_prev_prev[c - 2], img_labels_row_prev_prev[c]), img_labels_row_prev_prev[c + 2]); + // Action 14: Merge labels of block P, Q and S + #define ACTION_14 img_labels_row[c] = set_union(P, set_union(P, img_labels_row_prev_prev[c - 2], img_labels_row_prev_prev[c]), img_labels_row[c - 2]); + //Action 15: Merge labels of block P, R and S + #define ACTION_15 img_labels_row[c] = set_union(P, set_union(P, img_labels_row_prev_prev[c - 2], img_labels_row_prev_prev[c + 2]), img_labels_row[c - 2]); + //Action 16: labels of block Q, R and S + #define ACTION_16 img_labels_row[c] = set_union(P, set_union(P, img_labels_row_prev_prev[c], img_labels_row_prev_prev[c + 2]), img_labels_row[c - 2]); + } + // The following Directed Rooted Acyclic Graphs (DAGs) allow to choose which action to + // perform, checking as few conditions as possible. Special DAGs are used for the first/last + // line of the image and for single line images. Actions: the blocks label are provisionally + // stored in the top left pixel of the block in the labels image. + if (h == 1) { + // Single line + const PixelT * const img_row = img.ptr(0); + LabelT * const img_labels_row = imgLabels.ptr(0); + int c = -2; + #include "ccl_bolelli_forest_singleline.inc.hpp" + } + else { + // More than one line + + // First couple of lines + { + const PixelT * const img_row = img.ptr(0); + const PixelT * const img_row_fol = (PixelT *)(((char*)img_row) + img.step.p[0]); + LabelT * const img_labels_row = imgLabels.ptr(0); + int c = -2; + #include "ccl_bolelli_forest_firstline.inc.hpp" + } + + // Every other line but the last one if image has an odd number of rows + for (int r = 2; r < e_rows; r += 2) { + // Get rows pointer + const PixelT * const img_row = img.ptr(r); + const PixelT * const img_row_prev = (PixelT *)(((char*)img_row) - img.step.p[0]); + const PixelT * const img_row_prev_prev = (PixelT *)(((char*)img_row_prev) - img.step.p[0]); + const PixelT * const img_row_fol = (PixelT *)(((char*)img_row) + img.step.p[0]); + LabelT * const img_labels_row = imgLabels.ptr(r); + LabelT * const img_labels_row_prev_prev = (LabelT *)(((char*)img_labels_row) - imgLabels.step.p[0] - imgLabels.step.p[0]); + + int c = -2; + goto tree_0; + + #include "ccl_bolelli_forest.inc.hpp" + } + + // Last line (in case the rows are odd) + if (o_rows) { + int r = h - 1; + const PixelT * const img_row = img.ptr(r); + const PixelT * const img_row_prev = (PixelT *)(((char*)img_row) - img.step.p[0]); + const PixelT * const img_row_prev_prev = (PixelT *)(((char*)img_row_prev) - img.step.p[0]); + LabelT * const img_labels_row = imgLabels.ptr(r); + LabelT * const img_labels_row_prev_prev = (LabelT *)(((char*)img_labels_row) - imgLabels.step.p[0] - imgLabels.step.p[0]); + int c = -2; + #include "ccl_bolelli_forest_lastline.inc.hpp" + } + } + + // undef conditions and actions + { + #undef ACTION_1 + #undef ACTION_2 + #undef ACTION_3 + #undef ACTION_4 + #undef ACTION_5 + #undef ACTION_6 + #undef ACTION_7 + #undef ACTION_8 + #undef ACTION_9 + #undef ACTION_10 + #undef ACTION_11 + #undef ACTION_12 + #undef ACTION_13 + #undef ACTION_14 + #undef ACTION_15 + #undef ACTION_16 + + #undef CONDITION_B + #undef CONDITION_C + #undef CONDITION_D + #undef CONDITION_E + + #undef CONDITION_G + #undef CONDITION_H + #undef CONDITION_I + #undef CONDITION_J + #undef CONDITION_K + + #undef CONDITION_M + #undef CONDITION_N + #undef CONDITION_O + #undef CONDITION_P + + #undef CONDITION_R + #undef CONDITION_S + #undef CONDITION_T + } + + // Second scan + analysis + LabelT nLabels = flattenL(P, lunique); + sop.init(nLabels); + + int r = 0; + for (; r < e_rows; r += 2) { + // Get rows pointer + const PixelT * const img_row = img.ptr(r); + const PixelT * const img_row_fol = (PixelT *)(((char*)img_row) + img.step.p[0]); + LabelT * const img_labels_row = imgLabels.ptr(r); + LabelT * const img_labels_row_fol = (LabelT *)(((char*)img_labels_row) + imgLabels.step.p[0]); + int c = 0; + for (; c < e_cols; c += 2) { + LabelT iLabel = img_labels_row[c]; + if (iLabel > 0) { + iLabel = P[iLabel]; + if (img_row[c] > 0) { + img_labels_row[c] = iLabel; + sop(r, c, iLabel); + } + else { + img_labels_row[c] = 0; + sop(r, c, 0); + } + if (img_row[c + 1] > 0) { + img_labels_row[c + 1] = iLabel; + sop(r, c + 1, iLabel); + } + else { + img_labels_row[c + 1] = 0; + sop(r, c + 1, 0); + } + if (img_row_fol[c] > 0) { + img_labels_row_fol[c] = iLabel; + sop(r + 1, c, iLabel); + } + else { + img_labels_row_fol[c] = 0; + sop(r + 1, c, 0); + } + if (img_row_fol[c + 1] > 0) { + img_labels_row_fol[c + 1] = iLabel; + sop(r + 1, c + 1, iLabel); + } + else { + img_labels_row_fol[c + 1] = 0; + sop(r + 1, c + 1, 0); + } + } + else { + img_labels_row[c] = 0; + sop(r, c, 0); + img_labels_row[c + 1] = 0; + sop(r, c + 1, 0); + img_labels_row_fol[c] = 0; + sop(r + 1, c, 0); + img_labels_row_fol[c + 1] = 0; + sop(r + 1, c + 1, 0); + } + } + // Last column if the number of columns is odd + if (o_cols) { + LabelT iLabel = img_labels_row[c]; + if (iLabel > 0) { + iLabel = P[iLabel]; + if (img_row[c] > 0) { + img_labels_row[c] = iLabel; + sop(r, c, iLabel); + } + else { + img_labels_row[c] = 0; + sop(r, c, 0); + } + if (img_row_fol[c] > 0) { + img_labels_row_fol[c] = iLabel; + sop(r + 1, c, iLabel); + } + else { + img_labels_row_fol[c] = 0; + sop(r + 1, c, 0); + } + } + else { + img_labels_row[c] = 0; + sop(r, c, 0); + img_labels_row_fol[c] = 0; + sop(r + 1, c, 0); + } + } + } + // Last row if the number of rows is odd + if (o_rows) { + // Get rows pointer + const PixelT * const img_row = img.ptr(r); + LabelT * const img_labels_row = imgLabels.ptr(r); + int c = 0; + for (; c < e_cols; c += 2) { + LabelT iLabel = img_labels_row[c]; + if (iLabel > 0) { + iLabel = P[iLabel]; + if (img_row[c] > 0) { + img_labels_row[c] = iLabel; + sop(r, c, iLabel); + } + else { + img_labels_row[c] = 0; + sop(r, c, 0); + } + if (img_row[c + 1] > 0) { + img_labels_row[c + 1] = iLabel; + sop(r, c + 1, iLabel); + } + else { + img_labels_row[c + 1] = 0; + sop(r, c + 1, 0); + } + } + else { + img_labels_row[c] = 0; + sop(r, c, 0); + img_labels_row[c + 1] = 0; + sop(r, c + 1, 0); + } + } + // Last column if the number of columns is odd + if (o_cols) { + LabelT iLabel = img_labels_row[c]; + if (iLabel > 0) { + iLabel = P[iLabel]; + if (img_row[c] > 0) { + img_labels_row[c] = iLabel; + sop(r, c, iLabel); + } + else { + img_labels_row[c] = 0; + sop(r, c, 0); + } + } + else { + img_labels_row[c] = 0; + sop(r, c, iLabel); + } + } + } + + sop.finish(); + return nLabels; + }//End function LabelingBolelli operator() + };//End struct LabelingBolelli + + //Parallel implementation of Scan Array-based Union Find (SAUF) algorithm, as described in "Two More Strategies to Speed + //Up Connected Components Labeling Algorithms" + //Federico Bolelli et. al. template struct LabelingWuParallel{ @@ -332,11 +689,11 @@ namespace cv{ LabelT * const imgLabels_row_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels_.step.p[0]); for (int c = 0; c < w; ++c) { -#define condition_p c > 0 && r > limitLine && img_row_prev[c - 1] > 0 -#define condition_q r > limitLine && img_row_prev[c] > 0 -#define condition_r c < w - 1 && r > limitLine && img_row_prev[c + 1] > 0 -#define condition_s c > 0 && img_row[c - 1] > 0 -#define condition_x img_row[c] > 0 + #define condition_p c > 0 && r > limitLine && img_row_prev[c - 1] > 0 + #define condition_q r > limitLine && img_row_prev[c] > 0 + #define condition_r c < w - 1 && r > limitLine && img_row_prev[c + 1] > 0 + #define condition_s c > 0 && img_row[c - 1] > 0 + #define condition_x img_row[c] > 0 if (condition_x){ if (condition_q){ @@ -390,11 +747,11 @@ namespace cv{ //write in the follower memory location chunksSizeAndLabels_[startR + 1] = label - firstLabel; } -#undef condition_p -#undef condition_q -#undef condition_r -#undef condition_s -#undef condition_x + #undef condition_p + #undef condition_q + #undef condition_r + #undef condition_s + #undef condition_x }; class FirstScan4Connectivity : public cv::ParallelLoopBody{ @@ -435,9 +792,9 @@ namespace cv{ LabelT * const imgLabels_row_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels_.step.p[0]); for (int c = 0; c < w; ++c) { -#define condition_q r > limitLine && img_row_prev[c] > 0 -#define condition_s c > 0 && img_row[c - 1] > 0 -#define condition_x img_row[c] > 0 + #define condition_q r > limitLine && img_row_prev[c] > 0 + #define condition_s c > 0 && img_row[c - 1] > 0 + #define condition_x img_row[c] > 0 if (condition_x){ if (condition_q){ @@ -471,9 +828,9 @@ namespace cv{ //write in the following memory location chunksSizeAndLabels_[startR + 1] = label - firstLabel; } -#undef condition_q -#undef condition_s -#undef condition_x + #undef condition_q + #undef condition_s + #undef condition_x }; class SecondScan : public cv::ParallelLoopBody{ @@ -541,10 +898,10 @@ namespace cv{ for (int c = 0; c < w; ++c){ -#define condition_p c > 0 && imgLabels_row_prev[c - 1] > 0 -#define condition_q imgLabels_row_prev[c] > 0 -#define condition_r c < w - 1 && imgLabels_row_prev[c + 1] > 0 -#define condition_x imgLabels_row[c] > 0 + #define condition_p c > 0 && imgLabels_row_prev[c - 1] > 0 + #define condition_q imgLabels_row_prev[c] > 0 + #define condition_r c < w - 1 && imgLabels_row_prev[c + 1] > 0 + #define condition_x imgLabels_row[c] > 0 if (condition_x){ if (condition_p){ @@ -562,10 +919,10 @@ namespace cv{ } } } -#undef condition_p -#undef condition_q -#undef condition_r -#undef condition_x + #undef condition_p + #undef condition_q + #undef condition_r + #undef condition_x } inline static @@ -586,8 +943,8 @@ namespace cv{ for (int c = 0; c < w; ++c){ -#define condition_q imgLabels_row_prev[c] > 0 -#define condition_x imgLabels_row[c] > 0 + #define condition_q imgLabels_row_prev[c] > 0 + #define condition_x imgLabels_row[c] > 0 if (condition_x){ if (condition_q){ @@ -597,8 +954,8 @@ namespace cv{ } } } -#undef condition_q -#undef condition_x + #undef condition_q + #undef condition_x } LabelT operator()(const cv::Mat& img, cv::Mat& imgLabels, int connectivity, StatsOp& sop){ @@ -671,10 +1028,9 @@ namespace cv{ } };//End struct LabelingWuParallel - - //Based on "Two Strategies to Speed up Connected Components Algorithms", the SAUF (Scan array union find) variant + //Based on "Two Strategies to Speed up Connected Components Algorithms", the SAUF (Scan Array-based Union Find) variant //using decision trees - //Kesheng Wu, et al + //Kesheng Wu et. al. template struct LabelingWu{ LabelT operator()(const cv::Mat& img, cv::Mat& imgLabels, int connectivity, StatsOp& sop){ @@ -712,11 +1068,11 @@ namespace cv{ for (int c = 0; c < w; ++c){ -#define condition_p c>0 && r>0 && img_row_prev[c - 1]>0 -#define condition_q r>0 && img_row_prev[c]>0 -#define condition_r c < w - 1 && r > 0 && img_row_prev[c + 1] > 0 -#define condition_s c > 0 && img_row[c - 1] > 0 -#define condition_x img_row[c] > 0 + #define condition_p c>0 && r>0 && img_row_prev[c - 1]>0 + #define condition_q r>0 && img_row_prev[c]>0 + #define condition_r c < w - 1 && r > 0 && img_row_prev[c + 1] > 0 + #define condition_s c > 0 && img_row[c - 1] > 0 + #define condition_x img_row[c] > 0 if (condition_x){ if (condition_q){ @@ -770,11 +1126,11 @@ namespace cv{ } } } -#undef condition_p -#undef condition_q -#undef condition_r -#undef condition_s -#undef condition_x + #undef condition_p + #undef condition_q + #undef condition_r + #undef condition_s + #undef condition_x } else{ for (int r = 0; r < h; ++r){ @@ -784,9 +1140,9 @@ namespace cv{ LabelT * const imgLabels_row_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels.step.p[0]); for (int c = 0; c < w; ++c) { -#define condition_q r > 0 && img_row_prev[c] > 0 -#define condition_s c > 0 && img_row[c - 1] > 0 -#define condition_x img_row[c] > 0 + #define condition_q r > 0 && img_row_prev[c] > 0 + #define condition_s c > 0 && img_row[c - 1] > 0 + #define condition_x img_row[c] > 0 if (condition_x){ if (condition_q){ @@ -818,9 +1174,9 @@ namespace cv{ } } } -#undef condition_q -#undef condition_s -#undef condition_x + #undef condition_q + #undef condition_s + #undef condition_x } //analysis @@ -842,9 +1198,9 @@ namespace cv{ }//End function LabelingWu operator() };//End struct LabelingWu - - // Based on "Optimized Block-based Connected Components Labeling with Decision Trees", Costantino Grana et al - // Only for 8-connectivity + //Parallel implementation of BBDT (Block-Based with Decision Tree) algorithm, as described in "Two More Strategies to Speed + //Up Connected Components Labeling Algorithms" + //Federico Bolelli et. al. template struct LabelingGranaParallel{ @@ -901,31 +1257,31 @@ namespace cv{ // +---+---+ // Pixels a, f, l, q are not needed, since we need to understand the - // the connectivity between these blocks and those pixels only metter + // the connectivity between these blocks and those pixels only matter // when considering the outer connectivities // A bunch of defines used to check if the pixels are foreground, // without going outside the image limits. -#define condition_b c-1>=0 && r > limitLine && img_row_prev_prev[c-1]>0 -#define condition_c r > limitLine && img_row_prev_prev[c]>0 -#define condition_d c+1 limitLine && img_row_prev_prev[c+1]>0 -#define condition_e c+2 limitLine && img_row_prev_prev[c+2]>0 + #define condition_b c-1>=0 && r > limitLine && img_row_prev_prev[c-1]>0 + #define condition_c r > limitLine && img_row_prev_prev[c]>0 + #define condition_d c+1 limitLine && img_row_prev_prev[c+1]>0 + #define condition_e c+2 limitLine && img_row_prev_prev[c+2]>0 -#define condition_g c-2>=0 && r > limitLine - 1 && img_row_prev[c-2]>0 -#define condition_h c-1>=0 && r > limitLine - 1 && img_row_prev[c-1]>0 -#define condition_i r > limitLine - 1 && img_row_prev[c]>0 -#define condition_j c+1 limitLine - 1 && img_row_prev[c+1]>0 -#define condition_k c+2 limitLine - 1 && img_row_prev[c+2]>0 + #define condition_g c-2>=0 && r > limitLine - 1 && img_row_prev[c-2]>0 + #define condition_h c-1>=0 && r > limitLine - 1 && img_row_prev[c-1]>0 + #define condition_i r > limitLine - 1 && img_row_prev[c]>0 + #define condition_j c+1 limitLine - 1 && img_row_prev[c+1]>0 + #define condition_k c+2 limitLine - 1 && img_row_prev[c+2]>0 -#define condition_m c-2>=0 && img_row[c-2]>0 -#define condition_n c-1>=0 && img_row[c-1]>0 -#define condition_o img_row[c]>0 -#define condition_p c+10 + #define condition_m c-2>=0 && img_row[c-2]>0 + #define condition_n c-1>=0 && img_row[c-1]>0 + #define condition_o img_row[c]>0 + #define condition_p c+10 -#define condition_r c-1>=0 && r+10 -#define condition_s r+10 -#define condition_t c+10 + #define condition_r c-1>=0 && r+10 + #define condition_s r+10 + #define condition_t c+10 // This is a decision tree which allows to choose which action to // perform, checking as few conditions as possible. @@ -1903,15 +2259,15 @@ namespace cv{ //write in the follower memory location chunksSizeAndLabels_[startR + 1] = label - firstLabel; } -#undef condition_k -#undef condition_j -#undef condition_i -#undef condition_h -#undef condition_g -#undef condition_e -#undef condition_d -#undef condition_c -#undef condition_b + #undef condition_k + #undef condition_j + #undef condition_i + #undef condition_h + #undef condition_g + #undef condition_e + #undef condition_d + #undef condition_c + #undef condition_b }; class SecondScan : public cv::ParallelLoopBody{ @@ -2511,12 +2867,12 @@ namespace cv{ for (int c = 0; c < w; c += 2){ -#define condition_x imgLabels_row[c] > 0 -#define condition_pppr c > 1 && imgLabels_row_prev_prev[c - 2] > 0 -#define condition_qppr imgLabels_row_prev_prev[c] > 0 -#define condition_qppr1 c < w - 1 -#define condition_qppr2 c < w -#define condition_rppr c < w - 2 && imgLabels_row_prev_prev[c + 2] > 0 + #define condition_x imgLabels_row[c] > 0 + #define condition_pppr c > 1 && imgLabels_row_prev_prev[c - 2] > 0 + #define condition_qppr imgLabels_row_prev_prev[c] > 0 + #define condition_qppr1 c < w - 1 + #define condition_qppr2 c < w + #define condition_rppr c < w - 2 && imgLabels_row_prev_prev[c + 2] > 0 if (condition_x){ if (condition_pppr){ @@ -2603,8 +2959,9 @@ namespace cv{ } };//End struct LabelingGranaParallel - // Based on "Optimized Block-based Connected Components Labeling with Decision Trees", Costantino Grana et al - // Only for 8-connectivity + //Implementation of BBDT (Block-Based with Decision Tree) algorithm, as described in "Optimized Block-based Connected + //Components Labeling with Decision Trees" (only for 8-connectivity) + //Costantino Grana et. al. template struct LabelingGrana{ LabelT operator()(const cv::Mat& img, cv::Mat& imgLabels, int connectivity, StatsOp& sop){ @@ -2658,30 +3015,30 @@ namespace cv{ // +---+---+ // Pixels a, f, l, q are not needed, since we need to understand the - // the connectivity between these blocks and those pixels only metter + // the connectivity between these blocks and those pixels only matter // when considering the outer connectivities // A bunch of defines used to check if the pixels are foreground, // without going outside the image limits. -#define condition_b c-1>=0 && r-2>=0 && img_row_prev_prev[c-1]>0 -#define condition_c r-2>=0 && img_row_prev_prev[c]>0 -#define condition_d c+1=0 && img_row_prev_prev[c+1]>0 -#define condition_e c+2=0 && img_row_prev[c-1]>0 + #define condition_b c-1>=0 && r-2>=0 && img_row_prev_prev[c-1]>0 + #define condition_c r-2>=0 && img_row_prev_prev[c]>0 + #define condition_d c+1=0 && img_row_prev_prev[c+1]>0 + #define condition_e c+2=0 && img_row_prev[c-1]>0 -#define condition_g c-2>=0 && r-1>=0 && img_row_prev[c-2]>0 -#define condition_h c-1>=0 && r-1>=0 && img_row_prev[c-1]>0 -#define condition_i r-1>=0 && img_row_prev[c]>0 -#define condition_j c+1=0 && img_row_prev[c+1]>0 -#define condition_k c+2=0 && img_row_prev[c+2]>0 + #define condition_g c-2>=0 && r-1>=0 && img_row_prev[c-2]>0 + #define condition_h c-1>=0 && r-1>=0 && img_row_prev[c-1]>0 + #define condition_i r-1>=0 && img_row_prev[c]>0 + #define condition_j c+1=0 && img_row_prev[c+1]>0 + #define condition_k c+2=0 && img_row_prev[c+2]>0 -#define condition_m c-2>=0 && img_row[c-2]>0 -#define condition_n c-1>=0 && img_row[c-1]>0 -#define condition_o img_row[c]>0 -#define condition_p c+10 + #define condition_m c-2>=0 && img_row[c-2]>0 + #define condition_n c-1>=0 && img_row[c-1]>0 + #define condition_o img_row[c]>0 + #define condition_p c+10 -#define condition_r c-1>=0 && r+10 -#define condition_s r+10 -#define condition_t c+10 + #define condition_r c-1>=0 && r+10 + #define condition_s r+10 + #define condition_t c+10 // This is a decision tree which allows to choose which action to // perform, checking as few conditions as possible. @@ -3948,7 +4305,7 @@ namespace cv{ int connectedComponents_sub1(const cv::Mat& I, cv::Mat& L, int connectivity, int ccltype, StatsOp& sop){ CV_Assert(L.channels() == 1 && I.channels() == 1); CV_Assert(connectivity == 8 || connectivity == 4); - CV_Assert(ccltype == CCL_GRANA || ccltype == CCL_WU || ccltype == CCL_DEFAULT); + CV_Assert(ccltype == CCL_SPAGHETTI || ccltype == CCL_BBDT || ccltype == CCL_SAUF || ccltype == CCL_BOLELLI || ccltype == CCL_GRANA || ccltype == CCL_WU || ccltype == CCL_DEFAULT); int lDepth = L.depth(); int iDepth = I.depth(); @@ -3960,8 +4317,8 @@ namespace cv{ //Run parallel labeling only if the rows of the image are at least twice the number of available threads const bool is_parallel = currentParallelFramework != NULL && nThreads > 1 && L.rows / nThreads >= 2; - if (ccltype == CCL_WU || connectivity == 4){ - // Wu algorithm is used + if (ccltype == CCL_SAUF || ccltype == CCL_WU || connectivity == 4){ + // SAUF algorithm is used using connectedcomponents::LabelingWu; using connectedcomponents::LabelingWuParallel; //warn if L's depth is not sufficient? @@ -3980,8 +4337,8 @@ namespace cv{ return (int)LabelingWuParallel()(I, L, connectivity, sop); } } - else if ((ccltype == CCL_GRANA || ccltype == CCL_DEFAULT) && connectivity == 8){ - // Grana algorithm is used + else if ((ccltype == CCL_BBDT || ccltype == CCL_GRANA || ccltype == CCL_DEFAULT) && connectivity == 8){ + // BBDT algorithm is used using connectedcomponents::LabelingGrana; using connectedcomponents::LabelingGranaParallel; //warn if L's depth is not sufficient? @@ -4000,6 +4357,23 @@ namespace cv{ return (int)LabelingGranaParallel()(I, L, connectivity, sop); } } + else if ((ccltype == CCL_SPAGHETTI || ccltype == CCL_BOLELLI) && connectivity == 8) { + // Spaghetti algorithm is used + using connectedcomponents::LabelingBolelli; + //using connectedcomponents::LabelingBolelliParallel; // Not implemented + //warn if L's depth is not sufficient? + if (lDepth == CV_8U) { + //Not supported yet + } + else if (lDepth == CV_16U) { + return (int)LabelingBolelli()(I, L, connectivity, sop); + } + else if (lDepth == CV_32S) { + //note that signed types don't really make sense here and not being able to use unsigned matters for scientific projects + //OpenCV: how should we proceed? .at typechecks in debug mode + return (int)LabelingBolelli()(I, L, connectivity, sop); + } + } CV_Error(CV_StsUnsupportedFormat, "unsupported label/image type"); } diff --git a/modules/imgproc/test/test_connectedcomponents.cpp b/modules/imgproc/test/test_connectedcomponents.cpp index bc5c9f0a23..e57c24a937 100644 --- a/modules/imgproc/test/test_connectedcomponents.cpp +++ b/modules/imgproc/test/test_connectedcomponents.cpp @@ -74,11 +74,10 @@ void normalizeLabels(Mat1i& imgLabels, int iNumLabels) { } } - void CV_ConnectedComponentsTest::run( int /* start_from */) { - int ccltype[] = { cv::CCL_WU, cv::CCL_DEFAULT, cv::CCL_GRANA }; + int ccltype[] = { cv::CCL_DEFAULT, cv::CCL_WU, cv::CCL_GRANA, cv::CCL_BOLELLI, cv::CCL_SAUF, cv::CCL_BBDT, cv::CCL_SPAGHETTI }; string exp_path = string(ts->get_data_path()) + "connectedcomponents/ccomp_exp.png"; Mat exp = imread(exp_path, 0); @@ -150,7 +149,6 @@ TEST(Imgproc_ConnectedComponents, grana_buffer_overflow) EXPECT_EQ(1, nbComponents); } - static cv::Mat createCrashMat(int numThreads) { const int h = numThreads * 4 * 2 + 8; const double nParallelStripes = std::max(1, std::min(h / 2, numThreads * 4)); @@ -239,5 +237,124 @@ TEST(Imgproc_ConnectedComponents, missing_background_pixels) EXPECT_TRUE(std::isnan(centroids.at(0, 1))); } +TEST(Imgproc_ConnectedComponents, spaghetti_bbdt_sauf_stats) +{ + cv::Mat1b img(16, 16); + img << 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, + 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, + 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1; + + cv::Mat1i labels; + cv::Mat1i stats; + cv::Mat1d centroids; + + int ccltype[] = { cv::CCL_WU, cv::CCL_GRANA, cv::CCL_BOLELLI, cv::CCL_SAUF, cv::CCL_BBDT, cv::CCL_SPAGHETTI }; + + for (uint cclt = 0; cclt < sizeof(ccltype) / sizeof(int); ++cclt) { + + EXPECT_NO_THROW(cv::connectedComponentsWithStats(img, labels, stats, centroids, 8, CV_32S, ccltype[cclt])); + EXPECT_EQ(stats(0, cv::CC_STAT_LEFT), 0); + EXPECT_EQ(stats(0, cv::CC_STAT_TOP), 0); + EXPECT_EQ(stats(0, cv::CC_STAT_WIDTH), 16); + EXPECT_EQ(stats(0, cv::CC_STAT_HEIGHT), 15); + EXPECT_EQ(stats(0, cv::CC_STAT_AREA), 144); + + EXPECT_EQ(stats(1, cv::CC_STAT_LEFT), 1); + EXPECT_EQ(stats(1, cv::CC_STAT_TOP), 1); + EXPECT_EQ(stats(1, cv::CC_STAT_WIDTH), 3); + EXPECT_EQ(stats(1, cv::CC_STAT_HEIGHT), 3); + EXPECT_EQ(stats(1, cv::CC_STAT_AREA), 9); + + EXPECT_EQ(stats(2, cv::CC_STAT_LEFT), 1); + EXPECT_EQ(stats(2, cv::CC_STAT_TOP), 1); + EXPECT_EQ(stats(2, cv::CC_STAT_WIDTH), 8); + EXPECT_EQ(stats(2, cv::CC_STAT_HEIGHT), 7); + EXPECT_EQ(stats(2, cv::CC_STAT_AREA), 40); + + EXPECT_EQ(stats(3, cv::CC_STAT_LEFT), 10); + EXPECT_EQ(stats(3, cv::CC_STAT_TOP), 2); + EXPECT_EQ(stats(3, cv::CC_STAT_WIDTH), 5); + EXPECT_EQ(stats(3, cv::CC_STAT_HEIGHT), 2); + EXPECT_EQ(stats(3, cv::CC_STAT_AREA), 8); + + EXPECT_EQ(stats(4, cv::CC_STAT_LEFT), 11); + EXPECT_EQ(stats(4, cv::CC_STAT_TOP), 5); + EXPECT_EQ(stats(4, cv::CC_STAT_WIDTH), 3); + EXPECT_EQ(stats(4, cv::CC_STAT_HEIGHT), 3); + EXPECT_EQ(stats(4, cv::CC_STAT_AREA), 9); + + EXPECT_EQ(stats(5, cv::CC_STAT_LEFT), 2); + EXPECT_EQ(stats(5, cv::CC_STAT_TOP), 9); + EXPECT_EQ(stats(5, cv::CC_STAT_WIDTH), 1); + EXPECT_EQ(stats(5, cv::CC_STAT_HEIGHT), 1); + EXPECT_EQ(stats(5, cv::CC_STAT_AREA), 1); + + EXPECT_EQ(stats(6, cv::CC_STAT_LEFT), 12); + EXPECT_EQ(stats(6, cv::CC_STAT_TOP), 9); + EXPECT_EQ(stats(6, cv::CC_STAT_WIDTH), 1); + EXPECT_EQ(stats(6, cv::CC_STAT_HEIGHT), 1); + EXPECT_EQ(stats(6, cv::CC_STAT_AREA), 1); + + // Labels' order could be different! + if (cclt == cv::CCL_WU || cclt == cv::CCL_SAUF) { + // CCL_SAUF, CCL_WU + EXPECT_EQ(stats(9, cv::CC_STAT_LEFT), 1); + EXPECT_EQ(stats(9, cv::CC_STAT_TOP), 11); + EXPECT_EQ(stats(9, cv::CC_STAT_WIDTH), 4); + EXPECT_EQ(stats(9, cv::CC_STAT_HEIGHT), 2); + EXPECT_EQ(stats(9, cv::CC_STAT_AREA), 8); + + EXPECT_EQ(stats(7, cv::CC_STAT_LEFT), 6); + EXPECT_EQ(stats(7, cv::CC_STAT_TOP), 10); + EXPECT_EQ(stats(7, cv::CC_STAT_WIDTH), 4); + EXPECT_EQ(stats(7, cv::CC_STAT_HEIGHT), 2); + EXPECT_EQ(stats(7, cv::CC_STAT_AREA), 8); + + EXPECT_EQ(stats(8, cv::CC_STAT_LEFT), 0); + EXPECT_EQ(stats(8, cv::CC_STAT_TOP), 10); + EXPECT_EQ(stats(8, cv::CC_STAT_WIDTH), 16); + EXPECT_EQ(stats(8, cv::CC_STAT_HEIGHT), 6); + EXPECT_EQ(stats(8, cv::CC_STAT_AREA), 21); + } + else { + // CCL_BBDT, CCL_GRANA, CCL_SPAGHETTI, CCL_BOLELLI + EXPECT_EQ(stats(7, cv::CC_STAT_LEFT), 1); + EXPECT_EQ(stats(7, cv::CC_STAT_TOP), 11); + EXPECT_EQ(stats(7, cv::CC_STAT_WIDTH), 4); + EXPECT_EQ(stats(7, cv::CC_STAT_HEIGHT), 2); + EXPECT_EQ(stats(7, cv::CC_STAT_AREA), 8); + + EXPECT_EQ(stats(8, cv::CC_STAT_LEFT), 6); + EXPECT_EQ(stats(8, cv::CC_STAT_TOP), 10); + EXPECT_EQ(stats(8, cv::CC_STAT_WIDTH), 4); + EXPECT_EQ(stats(8, cv::CC_STAT_HEIGHT), 2); + EXPECT_EQ(stats(8, cv::CC_STAT_AREA), 8); + + EXPECT_EQ(stats(9, cv::CC_STAT_LEFT), 0); + EXPECT_EQ(stats(9, cv::CC_STAT_TOP), 10); + EXPECT_EQ(stats(9, cv::CC_STAT_WIDTH), 16); + EXPECT_EQ(stats(9, cv::CC_STAT_HEIGHT), 6); + EXPECT_EQ(stats(9, cv::CC_STAT_AREA), 21); + } + EXPECT_EQ(stats(10, cv::CC_STAT_LEFT), 9); + EXPECT_EQ(stats(10, cv::CC_STAT_TOP), 12); + EXPECT_EQ(stats(10, cv::CC_STAT_WIDTH), 5); + EXPECT_EQ(stats(10, cv::CC_STAT_HEIGHT), 2); + EXPECT_EQ(stats(10, cv::CC_STAT_AREA), 7); + } +} }} // namespace