diff --git a/modules/core/src/channels.cpp b/modules/core/src/channels.cpp index bc27918a0f..53f673f7f7 100644 --- a/modules/core/src/channels.cpp +++ b/modules/core/src/channels.cpp @@ -428,11 +428,14 @@ void cv::extractChannel(InputArray _src, OutputArray _dst, int coi) CV_Assert( 0 <= coi && coi < cn ); int ch[] = { coi, 0 }; - _dst.createSameSize(_src, depth); #ifdef HAVE_OPENCL if (ocl::isOpenCLActivated() && _src.dims() <= 2 && _dst.isUMat()) { UMat src = _src.getUMat(); + // NB: fetch the source before (re)allocating the destination so that + // in-place operation (_src and _dst referencing the same array) keeps + // the multi-channel source alive while dst is reshaped to 1 channel. + _dst.createSameSize(_src, depth); UMat dst = _dst.getUMat(); mixChannels(std::vector(1, src), std::vector(1, dst), ch, 1); return; @@ -440,6 +443,7 @@ void cv::extractChannel(InputArray _src, OutputArray _dst, int coi) #endif Mat src = _src.getMat(); + _dst.createSameSize(_src, depth); Mat dst = _dst.getMat(); CV_IPP_RUN_FAST(ipp_extractChannel(src, dst, coi)) diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 85aba95097..5400111957 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -1381,6 +1381,17 @@ TEST(Core_Mat, regression_9507) EXPECT_EQ(25u, m2.total()); } +TEST(Core_Mat, extractChannel_inplace_29568) +{ + // https://github.com/opencv/opencv/issues/29568 + // extractChannel() must support in-place operation (dst aliasing src). + Mat mat(3, 3, CV_8UC3, Scalar(1, 2, 3)); + ASSERT_NO_THROW(cv::extractChannel(mat, mat, 1)); + EXPECT_EQ(1, mat.channels()); + EXPECT_EQ(Size(3, 3), mat.size()); + EXPECT_EQ(0, cv::norm(mat, Mat(3, 3, CV_8UC1, Scalar(2)), NORM_INF)); +} + TEST(Core_Mat, empty) { // Should not crash.