1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 07:13:02 +04:00

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
This commit is contained in:
Vadim Pisarevsky
2024-11-01 11:37:56 +03:00
committed by GitHub
parent a95658f106
commit df06d2eac2
4 changed files with 303 additions and 52 deletions
+17 -7
View File
@@ -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<Mat*>(obj)->flags = Mat::MAGIC_VAL | Mat::CONTINUOUS_FLAG | stype;
else if (_dst.isUMat())
reinterpret_cast<UMat*>(obj)->flags = UMat::MAGIC_VAL | UMat::CONTINUOUS_FLAG | stype;
else if (_dst.isGpuMat())
reinterpret_cast<cuda::GpuMat*>(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;
+32 -25
View File
@@ -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<int, 4> 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);
+39 -20
View File
@@ -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<int, 4> 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<Mat*>(obj)->flags = Mat::MAGIC_VAL | Mat::CONTINUOUS_FLAG | stype;
else if (_dst.isUMat())
reinterpret_cast<UMat*>(obj)->flags = UMat::MAGIC_VAL | UMat::CONTINUOUS_FLAG | stype;
else if (_dst.isGpuMat())
reinterpret_cast<cuda::GpuMat*>(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();
+215
View File
@@ -2669,4 +2669,219 @@ TEST(Mat, regression_26322)
EXPECT_EQ(0, (int)mat3.total());
}
TEST(Mat, reshape_1d)
{
std::vector<int> 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<int>(), &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<int>(), &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<int>(), &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<int> 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<int>(), &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<int>(), &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<int>(), &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<int>(), &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<int> 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<int>(), 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<int>(), 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<int>(), 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<int> 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