From df06d2eac2ed2a33733285ada11a709aac470970 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Fri, 1 Nov 2024 11:37:56 +0300 Subject: [PATCH] Merge pull request #26254 from vpisarev:extra_tests_for_reshape Added extra tests for reshape #26254 Attempt to reproduce problems described in #25174. No success; everything works as expected. Probably, the function has been used improperly. Slightly modified the code of Mat::reshape() to provide better diagnostic. - [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 - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- modules/core/src/copy.cpp | 24 ++-- modules/core/src/matrix.cpp | 57 +++++---- modules/core/src/umatrix.cpp | 59 ++++++--- modules/core/test/test_mat.cpp | 215 +++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+), 52 deletions(-) diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index ccfe6a1f68..ffb61117c4 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -329,17 +329,25 @@ void Mat::copyTo( OutputArray _dst ) const } #endif + int stype = type(); int dtype = _dst.type(); - if( _dst.fixedType() && dtype != type() ) + if( _dst.fixedType() && dtype != stype ) { - CV_Assert( channels() == CV_MAT_CN(dtype) ); + CV_Assert( CV_MAT_CN(stype) == CV_MAT_CN(dtype) ); convertTo( _dst, dtype ); return; } - if( empty() ) + if( dims == 0 && empty() ) { _dst.release(); + void* obj = _dst.getObj(); + if (_dst.isMat()) + reinterpret_cast(obj)->flags = Mat::MAGIC_VAL | Mat::CONTINUOUS_FLAG | stype; + else if (_dst.isUMat()) + reinterpret_cast(obj)->flags = UMat::MAGIC_VAL | UMat::CONTINUOUS_FLAG | stype; + else if (_dst.isGpuMat()) + reinterpret_cast(obj)->flags = stype; return; } @@ -348,10 +356,12 @@ void Mat::copyTo( OutputArray _dst ) const (_dst.fixedSize() && _dst.dims() == 1); if( _dst.isUMat() ) { - _dst.create( dims, size.p, type(), -1, allowTransposed ); + _dst.create( dims, size.p, stype, -1, allowTransposed ); + if (empty()) + return; UMat dst = _dst.getUMat(); CV_Assert(dst.u != NULL); - size_t i, sz[CV_MAX_DIM] = {1}, dstofs[CV_MAX_DIM] = {0}, esz = elemSize(); + size_t i, sz[CV_MAX_DIM] = {1}, dstofs[CV_MAX_DIM] = {0}, esz = CV_ELEM_SIZE(stype); CV_Assert(dims >= 0 && dims < CV_MAX_DIM); for( i = 0; i < (size_t)dims; i++ ) sz[i] = size.p[i]; @@ -365,7 +375,7 @@ void Mat::copyTo( OutputArray _dst ) const if( dims <= 2 ) { - _dst.create( dims, size.p, type(), -1, allowTransposed ); + _dst.create( dims, size.p, stype, -1, allowTransposed ); Mat dst = _dst.getMat(); if( data == dst.data ) return; @@ -389,7 +399,7 @@ void Mat::copyTo( OutputArray _dst ) const return; } - _dst.create( dims, size, type() ); + _dst.create( dims, size, stype ); Mat dst = _dst.getMat(); if( data == dst.data ) return; diff --git a/modules/core/src/matrix.cpp b/modules/core/src/matrix.cpp index af5c31691f..7b6c3f497f 100644 --- a/modules/core/src/matrix.cpp +++ b/modules/core/src/matrix.cpp @@ -1725,27 +1725,26 @@ Mat Mat::reshape(int new_cn, int new_rows) const int cn = channels(); Mat hdr = *this; + if( new_cn == 0 ) + new_cn = cn; + if( dims > 2 ) { - if( new_rows == 0 && new_cn != 0 && size[dims-1]*cn % new_cn == 0 ) + if( new_rows == 0 ) { + // special case: just change the number of channnels; retain the same shape, + // except for the last, innermost dimension + CV_Assert(size[dims-1]*cn % new_cn == 0); hdr.flags = (hdr.flags & ~CV_MAT_CN_MASK) | ((new_cn-1) << CV_CN_SHIFT); hdr.step[dims-1] = CV_ELEM_SIZE(hdr.flags); hdr.size[dims-1] = hdr.size[dims-1]*cn / new_cn; return hdr; } - if( new_rows > 0 ) - { - int sz[] = { new_rows, (int)(total()*cn/new_rows) }; - return reshape(new_cn, 2, sz); - } + CV_Assert( new_rows > 0 ); + int sz[] = { new_rows, (int)(total()*cn/new_rows) }; + return reshape(new_cn, 2, sz); } - CV_Assert( dims <= 2 ); - - if( new_cn == 0 ) - new_cn = cn; - int total_width = cols * cn; if( (new_cn > total_width || total_width % new_cn != 0) && new_rows == 0 ) @@ -1758,9 +1757,6 @@ Mat Mat::reshape(int new_cn, int new_rows) const CV_Error( cv::Error::BadStep, "The matrix is not continuous, thus its number of rows can not be changed" ); - if( (unsigned)new_rows > (unsigned)total_size ) - CV_Error( cv::Error::StsOutOfRange, "Bad new number of rows" ); - total_width = total_size / new_rows; if( total_width * new_rows != total_size ) @@ -1810,22 +1806,33 @@ Mat Mat::reshape(int _cn, int _newndims, const int* _newsz) const AutoBuffer newsz_buf( (size_t)_newndims ); + int m1_idx = -1; + for (int i = 0; i < _newndims; i++) { - CV_Assert(_newsz[i] >= 0); - - if (_newsz[i] > 0) - newsz_buf[i] = _newsz[i]; - else if (i < dims) - newsz_buf[i] = this->size[i]; - else - CV_Error(cv::Error::StsOutOfRange, "Copy dimension (which has zero size) is not present in source matrix"); - - total_elem1 *= (size_t)newsz_buf[i]; + if (_newsz[i] >= 0) { + if (_newsz[i] == 0 && i < dims) + newsz_buf[i] = size.p[i]; + else + newsz_buf[i] = _newsz[i]; + total_elem1 *= (size_t)newsz_buf[i]; + } else { + if (m1_idx >= 0) + CV_Error(cv::Error::StsBadSize, "More than one '-1' occured in the new shape"); + m1_idx = i; + } } - if (total_elem1 != total_elem1_ref) + if (m1_idx >= 0) { + if (total_elem1 == 0) { + CV_Assert(total_elem1_ref == 0); + total_elem1 = 1; + } + CV_Assert(total_elem1_ref % total_elem1 == 0); + newsz_buf[m1_idx] = (int)(total_elem1_ref / total_elem1); + } else if (total_elem1 != total_elem1_ref) { CV_Error(cv::Error::StsUnmatchedSizes, "Requested and source matrices have different count of elements"); + } Mat hdr = *this; hdr.flags = (hdr.flags & ~CV_MAT_CN_MASK) | ((_cn-1) << CV_CN_SHIFT); diff --git a/modules/core/src/umatrix.cpp b/modules/core/src/umatrix.cpp index cc011da4f4..eea94a2f28 100644 --- a/modules/core/src/umatrix.cpp +++ b/modules/core/src/umatrix.cpp @@ -1063,9 +1063,6 @@ UMat UMat::reshape(int new_cn, int new_rows) const CV_Error( cv::Error::BadStep, "The matrix is not continuous, thus its number of rows can not be changed" ); - if( (unsigned)new_rows > (unsigned)total_size ) - CV_Error( cv::Error::StsOutOfRange, "Bad new number of rows" ); - total_width = total_size / new_rows; if( total_width * new_rows != total_size ) @@ -1127,7 +1124,7 @@ UMat UMat::reshape(int _cn, int _newndims, const int* _newsz) const if (isContinuous()) { - CV_Assert(_cn >= 0 && _newndims >= 0 && _newndims <= CV_MAX_DIM && _newsz); + CV_Assert(_cn >= 0 && _newndims >= 0 && _newndims <= CV_MAX_DIM && (_newndims == 0 || _newsz != 0)); if (_cn == 0) _cn = this->channels(); @@ -1139,22 +1136,33 @@ UMat UMat::reshape(int _cn, int _newndims, const int* _newsz) const AutoBuffer newsz_buf( (size_t)std::max(_newndims, 1) ); + int m1_idx = -1; + for (int i = 0; i < _newndims; i++) { - CV_Assert(_newsz[i] >= 0); - - if (_newsz[i] > 0) - newsz_buf[i] = _newsz[i]; - else if (i < dims) - newsz_buf[i] = this->size[i]; - else - CV_Error(cv::Error::StsOutOfRange, "Copy dimension (which has zero size) is not present in source matrix"); - - total_elem1 *= (size_t)newsz_buf[i]; + if (_newsz[i] >= 0) { + if (_newsz[i] == 0 && i < dims) + newsz_buf[i] = size.p[i]; + else + newsz_buf[i] = _newsz[i]; + total_elem1 *= (size_t)newsz_buf[i]; + } else { + if (m1_idx >= 0) + CV_Error(cv::Error::StsBadSize, "More than one '-1' occured in the new shape"); + m1_idx = i; + } } - if (total_elem1 != total_elem1_ref) + if (m1_idx >= 0) { + if (total_elem1 == 0) { + CV_Assert(total_elem1_ref == 0); + total_elem1 = 1; + } + CV_Assert(total_elem1_ref % total_elem1 == 0); + newsz_buf[m1_idx] = (int)(total_elem1_ref / total_elem1); + } else if (total_elem1 != total_elem1_ref) { CV_Error(cv::Error::StsUnmatchedSizes, "Requested and source matrices have different count of elements"); + } UMat hdr = *this; hdr.flags = (hdr.flags & ~CV_MAT_CN_MASK) | ((_cn-1) << CV_CN_SHIFT); @@ -1249,22 +1257,30 @@ void UMat::copyTo(OutputArray _dst) const } #endif + int stype = type(); int dtype = _dst.type(); - if( _dst.fixedType() && dtype != type() ) + if( _dst.fixedType() && dtype != stype ) { - CV_Assert( channels() == CV_MAT_CN(dtype) ); + CV_Assert( CV_MAT_CN(stype) == CV_MAT_CN(dtype) ); convertTo( _dst, dtype ); return; } - if( empty() ) + if( dims == 0 && empty() ) { _dst.release(); + void* obj = _dst.getObj(); + if (_dst.isMat()) + reinterpret_cast(obj)->flags = Mat::MAGIC_VAL | Mat::CONTINUOUS_FLAG | stype; + else if (_dst.isUMat()) + reinterpret_cast(obj)->flags = UMat::MAGIC_VAL | UMat::CONTINUOUS_FLAG | stype; + else if (_dst.isGpuMat()) + reinterpret_cast(obj)->flags = stype; return; } size_t sz[CV_MAX_DIM] = {1}, srcofs[CV_MAX_DIM]={0}, dstofs[CV_MAX_DIM]={0}; - size_t esz = elemSize(); + size_t esz = CV_ELEM_SIZE(stype); int i, d = std::max(dims, 1); for( i = 0; i < d; i++ ) sz[i] = size.p[i]; @@ -1272,7 +1288,10 @@ void UMat::copyTo(OutputArray _dst) const ndoffset(srcofs); srcofs[d-1] *= esz; - _dst.create( dims, size.p, type() ); + _dst.create( dims, size.p, stype ); + if (empty()) + return; + if( _dst.isUMat() ) { UMat dst = _dst.getUMat(); diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 4a697b5b00..9f308b9715 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2669,4 +2669,219 @@ TEST(Mat, regression_26322) EXPECT_EQ(0, (int)mat3.total()); } +TEST(Mat, reshape_1d) +{ + std::vector v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + int newrows = 2; + Mat m = Mat(v).reshape(0, newrows); + + EXPECT_EQ(m.dims, 2); + EXPECT_EQ(m.type(), CV_32S); + EXPECT_EQ(m.ptr(), &v[0]); + EXPECT_EQ(m.rows, newrows); + EXPECT_EQ(m.total(), v.size()); + + int sz[] = {(int)v.size()}; + Mat m1 = m.reshape(0, 1, sz); + EXPECT_EQ(m1.dims, 1); + EXPECT_EQ(m1.type(), CV_32S); + EXPECT_EQ(m1.ptr(), &v[0]); + EXPECT_EQ(m1.rows, 1); + EXPECT_EQ(m1.total(), v.size()); + + int sz3d[] = {2, -1, 3}; + Mat m3 = m1.reshape(0, 3, sz3d); + EXPECT_EQ(m3.dims, 3); + EXPECT_EQ(m3.type(), CV_32S); + EXPECT_EQ(m3.ptr(), &v[0]); + EXPECT_EQ(m3.size[0], 2); + EXPECT_EQ(m3.size[1], 2); + EXPECT_EQ(m3.size[2], 3); + EXPECT_EQ(m3.total(), v.size()); +} + +TEST(UMat, reshape_1d) +{ + std::vector v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + UMat m_, m; + int newrows = 2; + Mat(v).copyTo(m_); + m = m_.reshape(0, newrows); + + EXPECT_EQ(m.dims, 2); + EXPECT_EQ(m.type(), CV_32S); + EXPECT_EQ(m.u->handle, m_.u->handle); + EXPECT_EQ(m.offset, m_.offset); + EXPECT_EQ(m.rows, newrows); + EXPECT_EQ(m.total(), v.size()); + + int sz[] = {(int)v.size()}; + UMat m1 = m.reshape(0, 1, sz); + EXPECT_EQ(m1.dims, 1); + EXPECT_EQ(m1.type(), CV_32S); + EXPECT_EQ(m.u->handle, m_.u->handle); + EXPECT_EQ(m.offset, m_.offset); + EXPECT_EQ(m1.rows, 1); + EXPECT_EQ(m1.total(), v.size()); + + int sz3d[] = {2, -1, 3}; + UMat m3 = m1.reshape(0, 3, sz3d); + EXPECT_EQ(m3.dims, 3); + EXPECT_EQ(m3.type(), CV_32S); + EXPECT_EQ(m.u->handle, m_.u->handle); + EXPECT_EQ(m.offset, m_.offset); + EXPECT_EQ(m3.size[0], 2); + EXPECT_EQ(m3.size[1], 2); + EXPECT_EQ(m3.size[2], 3); + EXPECT_EQ(m3.total(), v.size()); +} + +TEST(Mat, reshape_0d) +{ + int v = 1001; + Mat m0d(0, nullptr, CV_32S, &v); + + EXPECT_EQ(m0d.dims, 0); + EXPECT_EQ(m0d.type(), CV_32S); + EXPECT_EQ(m0d.ptr(), &v); + EXPECT_EQ(m0d.rows, 1); + EXPECT_EQ(m0d.total(), (size_t)1); + EXPECT_EQ(m0d.empty(), false); + + int sz = 1; + Mat m1d = m0d.reshape(0, 1, &sz); + EXPECT_EQ(m1d.dims, 1); + EXPECT_EQ(m1d.type(), CV_32S); + EXPECT_EQ(m1d.ptr(), &v); + EXPECT_EQ(m1d.rows, 1); + EXPECT_EQ(m1d.total(), (size_t)1); + + Mat m2d = m1d.reshape(0, 1); + EXPECT_EQ(m2d.dims, 2); + EXPECT_EQ(m2d.type(), CV_32S); + EXPECT_EQ(m2d.ptr(), &v); + EXPECT_EQ(m2d.size(), Size(1, 1)); + EXPECT_EQ(m2d.total(), (size_t)1); + + Mat m0d_ = m2d.reshape(0, 0, nullptr); + EXPECT_EQ(m0d_.dims, 0); + EXPECT_EQ(m0d_.type(), CV_32S); + EXPECT_EQ(m0d_.ptr(), &v); + EXPECT_EQ(m0d_.size(), Size(1, 1)); + EXPECT_EQ(m0d_.total(), (size_t)1); + EXPECT_EQ(m0d_.empty(), false); +} + +TEST(UMat, reshape_0d) +{ + int v = 1001; + Mat m0dcpu(0, nullptr, CV_32S, &v); + UMat m0d; + m0dcpu.copyTo(m0d); + + EXPECT_EQ(m0d.dims, 0); + EXPECT_EQ(m0d.type(), CV_32S); + EXPECT_EQ(m0d.rows, 1); + EXPECT_EQ(m0d.total(), (size_t)1); + EXPECT_EQ(m0d.empty(), false); + + int sz = 1; + UMat m1d = m0d.reshape(0, 1, &sz); + EXPECT_EQ(m1d.dims, 1); + EXPECT_EQ(m1d.type(), CV_32S); + EXPECT_EQ(m1d.u->handle, m0d.u->handle); + EXPECT_EQ(m1d.offset, m0d.offset); + EXPECT_EQ(m1d.rows, 1); + EXPECT_EQ(m1d.total(), (size_t)1); + + UMat m2d = m1d.reshape(0, 1); + EXPECT_EQ(m2d.dims, 2); + EXPECT_EQ(m2d.type(), CV_32S); + EXPECT_EQ(m2d.u->handle, m0d.u->handle); + EXPECT_EQ(m2d.offset, m0d.offset); + EXPECT_EQ(m2d.size(), Size(1, 1)); + EXPECT_EQ(m2d.total(), (size_t)1); + + UMat m0d_ = m2d.reshape(0, 0, nullptr); + EXPECT_EQ(m0d_.dims, 0); + EXPECT_EQ(m0d_.type(), CV_32S); + EXPECT_EQ(m0d_.u->handle, m0d.u->handle); + EXPECT_EQ(m0d_.offset, m0d.offset); + EXPECT_EQ(m0d_.size(), Size(1, 1)); + EXPECT_EQ(m0d_.total(), (size_t)1); + EXPECT_EQ(m0d_.empty(), false); +} + + +TEST(Mat, reshape_empty) +{ + std::vector v; + int sz = 0; + Mat m = Mat(v).reshape(0, 1, &sz); + + EXPECT_EQ(m.empty(), true); + EXPECT_EQ(m.dims, 1); + EXPECT_EQ(m.type(), CV_32S); + EXPECT_EQ(m.ptr(), nullptr); + EXPECT_EQ(m.rows, 1); + EXPECT_EQ(m.total(), (size_t)0); + + Mat m2d = m.reshape(0, 2); // let's make it 2x0 + + EXPECT_EQ(m2d.empty(), true); + EXPECT_EQ(m2d.dims, 2); + EXPECT_EQ(m2d.type(), CV_32S); + EXPECT_EQ(m2d.ptr(), nullptr); + EXPECT_EQ(m2d.rows, 2); + EXPECT_EQ(m2d.total(), (size_t)0); + + int sz4[] = { 3, 7, 1, 0 }; + Mat m4d = m2d.reshape(0, 4, sz4); // let's make it 2x0 + + EXPECT_EQ(m4d.empty(), true); + EXPECT_EQ(m4d.dims, 4); + EXPECT_EQ(m4d.type(), CV_32S); + EXPECT_EQ(m4d.ptr(), nullptr); + EXPECT_EQ(m4d.size[0], 3); + EXPECT_EQ(m4d.size[1], 7); + EXPECT_EQ(m4d.size[2], 1); + EXPECT_EQ(m4d.size[3], 0); + EXPECT_EQ(m4d.total(), (size_t)0); +} + +TEST(UMat, reshape_empty) +{ + std::vector v; + int sz = 0; + UMat m_, m; + Mat(v).copyTo(m_); + m = m_.reshape(0, 1, &sz); + + EXPECT_EQ(m.empty(), true); + EXPECT_EQ(m.dims, 1); + EXPECT_EQ(m.type(), CV_32S); + EXPECT_EQ(m.rows, 1); + EXPECT_EQ(m.total(), (size_t)0); + + UMat m2d = m.reshape(0, 2); // let's make it 2x0 + + EXPECT_EQ(m2d.empty(), true); + EXPECT_EQ(m2d.dims, 2); + EXPECT_EQ(m2d.type(), CV_32S); + EXPECT_EQ(m2d.rows, 2); + EXPECT_EQ(m2d.total(), (size_t)0); + + int sz4[] = { 3, 7, 1, 0 }; + UMat m4d = m2d.reshape(0, 4, sz4); // let's make it 2x0 + + EXPECT_EQ(m4d.empty(), true); + EXPECT_EQ(m4d.dims, 4); + EXPECT_EQ(m4d.type(), CV_32S); + EXPECT_EQ(m4d.size[0], 3); + EXPECT_EQ(m4d.size[1], 7); + EXPECT_EQ(m4d.size[2], 1); + EXPECT_EQ(m4d.size[3], 0); + EXPECT_EQ(m4d.total(), (size_t)0); +} + }} // namespace