From eaccbe24b2ccea4d17b489302b3aa231e5df514a Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 23 Dec 2025 14:25:07 +0300 Subject: [PATCH] Merge pull request #28242 from vpisarev:fix_input_array_std_vector modified Input/OutputArray methods to handle 'std::vector' or 'std::vector>' properly #28242 This is port of #26408 with some further improvements (all switch-by-vector-type statements are consolidated in a single macro) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/core/include/opencv2/core/mat.hpp | 1 + modules/core/src/matrix_wrap.cpp | 349 +++++++++++----------- modules/core/test/test_mat.cpp | 60 ++++ 3 files changed, 235 insertions(+), 175 deletions(-) diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index 7a260abe04..dc9d526b97 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -246,6 +246,7 @@ public: bool isContinuous(int i=-1) const; bool isSubmatrix(int i=-1) const; bool empty() const; + bool empty(int i) const; void copyTo(const _OutputArray& arr) const; void copyTo(const _OutputArray& arr, const _InputArray & mask) const; size_t offset(int i=-1) const; diff --git a/modules/core/src/matrix_wrap.cpp b/modules/core/src/matrix_wrap.cpp index a8366a5f6c..8c4a09c3ac 100644 --- a/modules/core/src/matrix_wrap.cpp +++ b/modules/core/src/matrix_wrap.cpp @@ -7,6 +7,51 @@ namespace cv { +typedef Vec Vec5i; +typedef Vec Vec7i; +typedef Vec Vec9i; +typedef Vec Vec10i; +typedef Vec Vec11i; +typedef Vec Vec12i; +typedef Vec Vec13i; +typedef Vec Vec14i; +typedef Vec Vec15i; +typedef Vec Vec16i; +typedef Vec Vec32i; +typedef Vec Vec64i; +typedef Vec Vec128i; + +#undef STD_VECTOR_SWITCH +#define STD_VECTOR_SWITCH(esz, func) \ + switch( esz ) \ + { \ + case 1: func(uchar); \ + case 2: func(Vec2b); \ + case 3: func(Vec3b); \ + case 4: func(int); \ + case 6: func(Vec3s); \ + case 8: func(Vec2i); \ + case 12: func(Vec3i); \ + case 16: func(Vec4i); \ + case 20: func(Vec5i); \ + case 24: func(Vec6i); \ + case 28: func(Vec7i); \ + case 32: func(Vec8i); \ + case 36: func(Vec9i); \ + case 40: func(Vec10i); \ + case 44: func(Vec11i); \ + case 48: func(Vec12i); \ + case 52: func(Vec13i); \ + case 56: func(Vec14i); \ + case 60: func(Vec15i); \ + case 64: func(Vec16i); \ + case 128: func(Vec32i); \ + case 256: func(Vec64i); \ + case 512: func(Vec128i); \ + default: \ + CV_Error_(cv::Error::StsBadArg, ("Vectors of vectors with element size %d are not supported. Please, modify STD_VECTOR_SWITCH() macro\n", esz)); \ + } + /*************************************************************************************************\ Input/Output Array \*************************************************************************************************/ @@ -38,15 +83,6 @@ Mat _InputArray::getMat_(int i) const return Mat(sz, flags, obj); } - if( k == STD_VECTOR ) - { - CV_Assert( i < 0 ); - int t = CV_MAT_TYPE(flags); - const std::vector& v = *(const std::vector*)obj; - - return !v.empty() ? Mat(size(), t, (void*)&v[0]) : Mat(); - } - if( k == STD_BOOL_VECTOR ) { CV_Assert( i < 0 ); @@ -65,14 +101,27 @@ Mat _InputArray::getMat_(int i) const if( k == NONE ) return Mat(); - if( k == STD_VECTOR_VECTOR ) + if( k == STD_VECTOR || k == STD_VECTOR_VECTOR ) { - int t = type(i); - const std::vector >& vv = *(const std::vector >*)obj; - CV_Assert( 0 <= i && i < (int)vv.size() ); - const std::vector& v = vv[i]; + int t = CV_MAT_TYPE(flags); + int esz = CV_ELEM_SIZE(t); - return !v.empty() ? Mat(size(i), t, (void*)&v[0]) : Mat(); + #undef GET_MAT + #define GET_MAT(T) \ + { \ + std::vector* v; \ + if (k == STD_VECTOR_VECTOR) { \ + std::vector >* vv = \ + reinterpret_cast >*>(obj); \ + CV_Assert(size_t(i) < vv->size()); \ + v = &vv->at(i); \ + } else { \ + v = reinterpret_cast*>(obj); \ + } \ + return v->empty() ? Mat() : Mat(1, int(v->size()), t, v->data()); \ + } + + STD_VECTOR_SWITCH(esz, GET_MAT) } if( k == STD_VECTOR_MAT ) @@ -192,7 +241,7 @@ void _InputArray::getMatVector(std::vector& mv) const { const std::vector& v = *(const std::vector*)obj; - size_t n = size().width, esz = CV_ELEM_SIZE(flags); + size_t n = total(-1), esz = CV_ELEM_SIZE(flags); int t = CV_MAT_DEPTH(flags), cn = CV_MAT_CN(flags); mv.resize(n); @@ -209,16 +258,25 @@ void _InputArray::getMatVector(std::vector& mv) const if( k == STD_VECTOR_VECTOR ) { - const std::vector >& vv = *(const std::vector >*)obj; - int n = (int)vv.size(); - int t = CV_MAT_TYPE(flags); - mv.resize(n); + int typ = CV_MAT_TYPE(flags); + int esz = CV_ELEM_SIZE(typ); - for( int i = 0; i < n; i++ ) - { - const std::vector& v = vv[i]; - mv[i] = Mat(size(i), t, (void*)&v[0]); - } + #undef GET_VEC_VEC_MAT_VEC + #define GET_VEC_VEC_MAT_VEC(T) \ + { \ + const std::vector >* vv = (const std::vector >*)obj; \ + size_t n = vv->size(); \ + mv.resize(n); \ + for (size_t i = 0; i < n; i++) { \ + const std::vector& vi = vv->at(i); \ + CV_Assert(vi.size() <= (size_t)INT_MAX); \ + int ni = (int)vi.size(); \ + mv[i] = ni > 0 ? Mat(1, ni, typ, (void*)&vi[0]) : Mat(); \ + } \ + } \ + break + + STD_VECTOR_SWITCH(esz, GET_VEC_VEC_MAT_VEC) return; } @@ -449,35 +507,14 @@ Size _InputArray::size(int i) const return sz; } - if( k == STD_VECTOR ) - { - CV_Assert( i < 0 ); - const std::vector& v = *(const std::vector*)obj; - const std::vector& iv = *(const std::vector*)obj; - size_t szb = v.size(), szi = iv.size(); - return szb == szi ? Size((int)szb, 1) : Size((int)(szb/CV_ELEM_SIZE(flags)), 1); - } - - if( k == STD_BOOL_VECTOR ) - { - CV_Assert( i < 0 ); - const std::vector& v = *(const std::vector*)obj; - return Size((int)v.size(), 1); - } - if( k == NONE ) return Size(); - if( k == STD_VECTOR_VECTOR ) + if( k == STD_VECTOR || k == STD_VECTOR_VECTOR || k == STD_BOOL_VECTOR ) { - const std::vector >& vv = *(const std::vector >*)obj; - if( i < 0 ) - return vv.empty() ? Size() : Size((int)vv.size(), 1); - CV_Assert( i < (int)vv.size() ); - const std::vector >& ivv = *(const std::vector >*)obj; - - size_t szb = vv[i].size(), szi = ivv[i].size(); - return szb == szi ? Size((int)szb, 1) : Size((int)(szb/CV_ELEM_SIZE(flags)), 1); + size_t n = total(i); + CV_Assert(n <= (size_t)INT_MAX); + return Size((int)n, 1); } if( k == STD_VECTOR_MAT ) @@ -617,6 +654,30 @@ int _InputArray::sizend(int* arrsz, int i) const return d; } +bool _InputArray::empty(int i) const +{ + _InputArray::KindFlag k = kind(); + if (i >= 0) { + if (k == STD_VECTOR_MAT) { + auto mv = reinterpret_cast*>(obj); + CV_Assert((size_t)i < mv->size()); + return mv->at(i).empty(); + } + else if (k == STD_VECTOR_UMAT) { + auto umv = reinterpret_cast*>(obj); + CV_Assert((size_t)i < umv->size()); + return umv->at(i).empty(); + } + else if (k == STD_VECTOR || k == STD_VECTOR_VECTOR || k == STD_BOOL_VECTOR) { + size_t n = total(i); + return n == 0; + } else { + CV_Error(Error::StsNotImplemented, ""); + } + } + return empty(); +} + bool _InputArray::sameSize(const _InputArray& arr) const { _InputArray::KindFlag k1 = kind(), k2 = arr.kind(); @@ -688,13 +749,7 @@ int _InputArray::dims(int i) const return 0; if( k == STD_VECTOR_VECTOR ) - { - const std::vector >& vv = *(const std::vector >*)obj; - if( i < 0 ) - return 1; - CV_Assert( i < (int)vv.size() ); - return 2; - } + return 1; if( k == STD_VECTOR_MAT ) { @@ -814,6 +869,32 @@ size_t _InputArray::total(int i) const } + if (k == STD_VECTOR || k == STD_VECTOR_VECTOR) + { + CV_Assert(i < 0 || k == STD_VECTOR_VECTOR); + int esz = CV_ELEM_SIZE(flags); + + #undef GET_VEC_SIZE + #define GET_VEC_SIZE(T) \ + if (k == STD_VECTOR_VECTOR) { \ + const std::vector >* vv = (const std::vector >*)obj; \ + size_t n = vv->size(); \ + if (i < 0) \ + return n; \ + CV_Assert((size_t)i < n); \ + return vv->at(i).size(); \ + } else \ + return ((const std::vector*)obj)->size() + + STD_VECTOR_SWITCH(esz, GET_VEC_SIZE) + } + + if (k == STD_BOOL_VECTOR) + { + CV_Assert(i < 0); + return ((const std::vector*)obj)->size(); + } + return size(i).area(); } @@ -923,27 +1004,15 @@ bool _InputArray::empty() const if (k == MATX) return false; - if( k == STD_VECTOR ) + if( k == STD_VECTOR || k == STD_VECTOR_VECTOR || k == STD_BOOL_VECTOR) { - const std::vector& v = *(const std::vector*)obj; - return v.empty(); - } - - if( k == STD_BOOL_VECTOR ) - { - const std::vector& v = *(const std::vector*)obj; - return v.empty(); + size_t n = total(-1); + return n == 0; } if( k == NONE ) return true; - if( k == STD_VECTOR_VECTOR ) - { - const std::vector >& vv = *(const std::vector >*)obj; - return vv.empty(); - } - if( k == STD_VECTOR_MAT ) { const std::vector& vv = *(const std::vector*)obj; @@ -952,7 +1021,7 @@ bool _InputArray::empty() const if( k == STD_ARRAY_MAT ) { - return sz.height == 0; + return sz.area() == 0; } if( k == STD_VECTOR_UMAT ) @@ -1471,8 +1540,8 @@ void _OutputArray::create(int d, const int* sizes, int mtype, int i, else { CV_Check(requested_size, - (requested_size == sz || (requested_size.height == sz.width && requested_size.width == sz.height)), - ""); + (requested_size == sz || (requested_size.height == sz.width && requested_size.width == sz.height)), + ""); } } return; @@ -1480,104 +1549,40 @@ void _OutputArray::create(int d, const int* sizes, int mtype, int i, if( k == STD_VECTOR || k == STD_VECTOR_VECTOR ) { - CV_Assert( d == 2 && (sizes[0] == 1 || sizes[1] == 1 || sizes[0]*sizes[1] == 0) ); + CV_Assert(!fixedSize()); + CV_Assert(k == STD_VECTOR_VECTOR || i < 0); + CV_Assert(d == 2 && (sizes[0] == 1 || sizes[1] == 1 || sizes[0]*sizes[1] == 0)); size_t len = sizes[0]*sizes[1] > 0 ? sizes[0] + sizes[1] - 1 : 0; - std::vector* v = (std::vector*)obj; + int esz = CV_ELEM_SIZE(flags); - if( k == STD_VECTOR_VECTOR ) + if( k == STD_VECTOR || (k == STD_VECTOR_VECTOR && i >= 0) ) { - std::vector >& vv = *(std::vector >*)obj; - if( i < 0 ) - { - CV_Assert(!fixedSize() || len == vv.size()); - vv.resize(len); - return; - } - CV_Assert( i < (int)vv.size() ); - v = &vv[i]; + int type0 = CV_MAT_TYPE(flags); + CV_Assert( mtype == type0 || (CV_MAT_CN(mtype) == CV_MAT_CN(type0) && ((1 << type0) & fixedDepthMask) != 0) ); } - else - CV_Assert( i < 0 ); - int type0 = CV_MAT_TYPE(flags); - CV_Assert( mtype == type0 || (CV_MAT_CN(mtype) == CV_MAT_CN(type0) && ((1 << type0) & fixedDepthMask) != 0) ); + #undef RESIZE_VEC + #define RESIZE_VEC(T) \ + { \ + std::vector* v; \ + if (k == STD_VECTOR_VECTOR) { \ + std::vector >* vv = reinterpret_cast >*>(obj); \ + if (i < 0) { \ + CV_Assert(!fixedSize() || len == vv->size()); \ + vv->resize(len); \ + return; \ + } \ + CV_Assert(size_t(i) < vv->size()); \ + v = &vv->at(i); \ + } else { \ + v = reinterpret_cast*>(obj); \ + } \ + CV_Assert(!fixedSize() || len == v->size()); \ + v->resize(len); \ + } \ + break - int esz = CV_ELEM_SIZE(type0); - CV_Assert(!fixedSize() || len == ((std::vector*)v)->size() / esz); - switch( esz ) - { - case 1: - ((std::vector*)v)->resize(len); - break; - case 2: - ((std::vector*)v)->resize(len); - break; - case 3: - ((std::vector*)v)->resize(len); - break; - case 4: - ((std::vector*)v)->resize(len); - break; - case 6: - ((std::vector*)v)->resize(len); - break; - case 8: - ((std::vector*)v)->resize(len); - break; - case 12: - ((std::vector*)v)->resize(len); - break; - case 16: - ((std::vector*)v)->resize(len); - break; - case 20: - ((std::vector >*)v)->resize(len); - break; - case 24: - ((std::vector*)v)->resize(len); - break; - case 28: - ((std::vector >*)v)->resize(len); - break; - case 32: - ((std::vector*)v)->resize(len); - break; - case 36: - ((std::vector >*)v)->resize(len); - break; - case 40: - ((std::vector >*)v)->resize(len); - break; - case 44: - ((std::vector >*)v)->resize(len); - break; - case 48: - ((std::vector >*)v)->resize(len); - break; - case 52: - ((std::vector >*)v)->resize(len); - break; - case 56: - ((std::vector >*)v)->resize(len); - break; - case 60: - ((std::vector >*)v)->resize(len); - break; - case 64: - ((std::vector >*)v)->resize(len); - break; - case 128: - ((std::vector >*)v)->resize(len); - break; - case 256: - ((std::vector >*)v)->resize(len); - break; - case 512: - ((std::vector >*)v)->resize(len); - break; - default: - CV_Error_(cv::Error::StsBadArg, ("Vectors with element size %d are not supported. Please, modify OutputArray::create()\n", esz)); - } + STD_VECTOR_SWITCH(esz, RESIZE_VEC) return; } @@ -1851,15 +1856,9 @@ void _OutputArray::release() const if( k == NONE ) return; - if( k == STD_VECTOR ) + if( k == STD_VECTOR || k == STD_VECTOR_VECTOR ) { - create(Size(), CV_MAT_TYPE(flags)); - return; - } - - if( k == STD_VECTOR_VECTOR ) - { - ((std::vector >*)obj)->clear(); + create(Size(), CV_MAT_TYPE(flags), -1); return; } diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 20f3ce6809..b0fcea60c6 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2778,4 +2778,64 @@ TEST(Mat, copyAt_regression27298) } } +template static void make_vector(std::vector >& v, int n) +{ + v.clear(); + v.resize(n); + _Tp* data = &v[0][0]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < cn; j++) { + int k = j % 4; + int val = (k == 0 ? 1 : k == 1 ? -1 : k == 2 ? (i+1) : -(i+1))*(i+1); + data[i*cn + j] = (_Tp)val; + } + } +} + +TEST(Core_InputOutputArray, std_vector_vector) +{ + std::vector vv0_s, vv1_s; + std::vector > cn_s; + make_vector(vv0_s, 100); + + split(vv0_s, cn_s); + merge(cn_s, vv1_s); + + double err0 = cvtest::norm(vv0_s, vv1_s, NORM_INF); + EXPECT_EQ(0, err0); + + _InputArray iarr_s(cn_s); + _OutputArray oarr_s(cn_s); + EXPECT_EQ(3u, iarr_s.total(-1)); + size_t newsize_s = vv0_s.size()*2; + oarr_s.create(Size((int)newsize_s, 1), CV_16S, 2); + EXPECT_EQ(newsize_s, cn_s[2].size()); + cn_s[1].clear(); + EXPECT_EQ(true, oarr_s.empty(1)); + + std::vector vv0_d, vv1_d; + std::vector > cn_d; + make_vector(vv0_d, 1000); + + split(vv0_d, cn_d); + merge(cn_d, vv1_d); + + double err1 = cvtest::norm(vv0_d, vv1_d, NORM_INF); + EXPECT_EQ(0., err1); + + _InputArray iarr_d(cn_d); + _OutputArray oarr_d(cn_d); + EXPECT_EQ(4u, iarr_d.total(-1)); + size_t newsize_d = vv0_d.size()*3; + oarr_d.create(Size((int)newsize_d, 1), CV_64F, 3); + EXPECT_EQ(newsize_d, cn_d[3].size()); + cn_d[1].clear(); + EXPECT_EQ(true, oarr_d.empty(1)); + Mat m2 = oarr_d.getMat(2); + + double err2 = cvtest::norm(m2, Mat(cn_d[2]).t(), NORM_INF); + EXPECT_EQ(m2.ptr(), &cn_d[2][0]); + EXPECT_EQ(0., err2); +} + }} // namespace