diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index 4e86be7196..e8cb786e3f 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -1237,8 +1237,13 @@ public: When the operation mask is specified, if the Mat::create call shown above reallocates the matrix, the newly allocated matrix is initialized with all zeros before copying the data. + + If (re)allocation of destination memory is not necessary (e.g. updating ROI), use copyAt() . + @param m Destination matrix. If it does not have a proper size or type before the operation, it is reallocated. + + @sa copyAt */ void copyTo( OutputArray m ) const; @@ -1250,6 +1255,30 @@ public: */ void copyTo( OutputArray m, InputArray mask ) const; + /** @brief Overwrites the existing matrix + + This method writes existing matrix data, just like copyTo(). + But if it does not have a proper size or type before the operation, an exception is thrown. + This function is helpful to update ROI in an existing matrix. + + If (re)allocation of destination memory is necessary, use copyTo() . + + @param m Destination matrix. + If it does not have a proper size or type before the operation, an exception is thrown. + + @sa copyTo + + */ + void copyAt( OutputArray m ) const; + + /** @overload + @param m Destination matrix. + If it does not have a proper size or type before the operation, an exception is thrown. + @param mask Operation mask of the same size as \*this. Its non-zero elements indicate which matrix + elements need to be copied. The mask has to be of type CV_8U and can have 1 or multiple channels. + */ + void copyAt( OutputArray m, InputArray mask ) const; + /** @brief Converts an array to another data type with optional scaling. The method converts source pixel values to the target data type. saturate_cast\<\> is applied at diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index 43f477aeae..271858ad5b 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -615,6 +615,28 @@ void Mat::copyTo( OutputArray _dst, InputArray _mask ) const copymask(ptrs[0], 0, ptrs[2], 0, ptrs[1], 0, sz, &esz); } +/* dst = src */ +void Mat::copyAt( OutputArray _dst ) const +{ + CV_INSTRUMENT_REGION(); + + Mat dst = _dst.getMat(); + CV_CheckTrue( !dst.empty(), "dst must not be empty" ); + CV_CheckTypeEQ(type(), dst.type(), "Make the type of dst the same as src"); + CV_CheckEQ(size(), dst.size(), "Make the size of dst the same as src"); + copyTo(_dst); +} +void Mat::copyAt( OutputArray _dst, InputArray _mask ) const +{ + CV_INSTRUMENT_REGION(); + + Mat dst = _dst.getMat(); + CV_CheckTrue( !dst.empty(), "dst must not be empty" ); + CV_CheckTypeEQ(type(), dst.type(), "Make the type of dst the same as src"); + CV_CheckEQ(size(), dst.size(), "Make the size of dst the same as src"); + copyTo(_dst, _mask); +} + static bool can_apply_memset(const Mat &mat, const Scalar &s, int &fill_value) { diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 9ba47755b4..20f3ce6809 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2713,4 +2713,69 @@ TEST(Mat, Recreate1DMatWithSameMeta) EXPECT_NO_THROW(m.create(dims, depth)); } +// see https://github.com/opencv/opencv/issues/27298 +TEST(Mat, copyAt_regression27298) +{ + cv::Mat src(40/*height*/, 30/*width*/, CV_8UC1, Scalar(255)); + // Normal + { + cv::Mat dst(100, 100, CV_8UC1, Scalar(0)); + cv::Mat roi(dst, cv::Rect(0, 0, 30/*width*/, 40/*height*/)); + void* roiData = roi.data; + EXPECT_NO_THROW(src.copyTo(roi)); + EXPECT_EQ(roi.data, roiData); + EXPECT_EQ(countNonZero(roi), roi.size().width * roi.size().height) << roi; + } + { + cv::Mat dst(100, 100, CV_8UC1, Scalar(0)); + cv::Mat roi(dst, cv::Rect(0, 0, 30/*width*/, 40/*height*/)); + void* roiData = roi.data; + EXPECT_NO_THROW(src.copyAt(roi)); + EXPECT_EQ(roi.data, roiData); + EXPECT_EQ(countNonZero(roi), roi.size().width * roi.size().height) << roi; + } + + // Empty + { + cv::Mat roi; // empty + EXPECT_NO_THROW(src.copyTo(roi)); + EXPECT_NE(roi.data, nullptr); // Allocated + EXPECT_EQ(countNonZero(roi), roi.size().width * roi.size().height) << roi; + } + { + cv::Mat roi; // empty + EXPECT_ANY_THROW(src.copyAt(roi)); + } + + // Different Type + { + cv::Mat dst(100, 100, CV_16UC1, Scalar(0)); + cv::Mat roi(dst, cv::Rect(0, 0, 30/*width*/, 40/*height*/)); + void* roiData = roi.data; + EXPECT_NO_THROW(src.copyTo(roi)); + EXPECT_NE(roi.data, roiData); // Reallocated + EXPECT_EQ(countNonZero(roi), roi.size().width * roi.size().height) << roi; + } + { + cv::Mat dst(100, 100, CV_16UC1, Scalar(0)); + cv::Mat roi(dst, cv::Rect(0, 0, 30/*width*/, 40/*height*/)); + EXPECT_ANY_THROW(src.copyAt(roi)); + } + + // Different Size + { + cv::Mat dst(100, 100, CV_8UC1, Scalar(0)); + cv::Mat roi(dst, cv::Rect(0, 0, 40/*width*/, 30/*height*/)); + void* roiData = roi.data; + EXPECT_NO_THROW(src.copyTo(roi)); + EXPECT_NE(roi.data, roiData); // Reallocated + EXPECT_EQ(countNonZero(roi), roi.size().width * roi.size().height) << roi; + } + { + cv::Mat dst(100, 100, CV_8UC1, Scalar(0)); + cv::Mat roi(dst, cv::Rect(0, 0, 40/*width*/, 30/*height*/)); + EXPECT_ANY_THROW(src.copyAt(roi)); + } +} + }} // namespace diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index f9c5744466..13f467157e 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -212,7 +212,7 @@ bool GifDecoder::readData(Mat &img) { if(!restore.empty()) { Mat roi = Mat(lastImage, cv::Rect(left,top,width,height)); - restore.copyTo(roi); + restore.copyAt(roi); } return hasRead;