diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index 065e871c5a..3e6c8c9000 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -5659,7 +5659,7 @@ namespace cv{ const char *currentParallelFramework = cv::currentParallelFramework(); const int nThreads = cv::getNumThreads(); - CV_Assert(iDepth == CV_8U || iDepth == CV_8S); + CV_Assert(iDepth == CV_8U || iDepth == CV_8S || iDepth == CV_Bool); //Run parallel labeling only if the rows of the image are at least twice the number of available threads const bool is_parallel = currentParallelFramework != NULL && nThreads > 1 && L.rows / nThreads >= 2; diff --git a/modules/imgproc/test/test_connectedcomponents.cpp b/modules/imgproc/test/test_connectedcomponents.cpp index 42ee000497..bbe285168b 100644 --- a/modules/imgproc/test/test_connectedcomponents.cpp +++ b/modules/imgproc/test/test_connectedcomponents.cpp @@ -743,6 +743,73 @@ TEST(Imgproc_ConnectedComponents, single_column) } +// See https://github.com/opencv/opencv/issues/29593 +// cv::Mat_::depth() returns CV_Bool in 5.0, where it was CV_8U in 4.x, so +// connected components should keep accepting boolean masks as binary input. +TEST(Imgproc_ConnectedComponents, bool_mask_29593) +{ + Mat_ input_bool(5, 6, false); + input_bool(0, 0) = true; + input_bool(0, 1) = true; + input_bool(1, 1) = true; + input_bool(2, 4) = true; + input_bool(3, 4) = true; + input_bool(3, 5) = true; + input_bool(4, 2) = true; + + Mat1b input_uchar(input_bool.size(), uchar(0)); + for (int r = 0; r < input_bool.rows; ++r) + { + for (int c = 0; c < input_bool.cols; ++c) + { + input_uchar(r, c) = input_bool(r, c) ? uchar(255) : uchar(0); + } + } + + ASSERT_EQ(input_bool.depth(), CV_Bool); + + const int ccltype[] = { cv::CCL_DEFAULT, cv::CCL_WU, cv::CCL_GRANA, cv::CCL_BOLELLI, + cv::CCL_SAUF, cv::CCL_BBDT, cv::CCL_SPAGHETTI }; + + for (int connectivity : {4, 8}) + { + for (size_t cclt = 0; cclt < sizeof(ccltype) / sizeof(ccltype[0]); ++cclt) + { + Mat1i labels_bool, labels_uchar; + int nlabels_bool = 0; + ASSERT_NO_THROW(nlabels_bool = connectedComponents( + input_bool, labels_bool, connectivity, CV_32S, ccltype[cclt])); + const int nlabels_uchar = connectedComponents( + input_uchar, labels_uchar, connectivity, CV_32S, ccltype[cclt]); + + normalizeLabels(labels_bool, nlabels_bool); + normalizeLabels(labels_uchar, nlabels_uchar); + + EXPECT_EQ(nlabels_bool, nlabels_uchar) + << "connectivity = " << connectivity << ", ccltype = " << ccltype[cclt]; + EXPECT_EQ(countNonZero(labels_bool != labels_uchar), 0) + << "connectivity = " << connectivity << ", ccltype = " << ccltype[cclt]; + + Mat stats_bool, centroids_bool, stats_uchar, centroids_uchar; + ASSERT_NO_THROW(nlabels_bool = connectedComponentsWithStats( + input_bool, labels_bool, stats_bool, centroids_bool, connectivity, CV_32S, ccltype[cclt])); + const int nlabels_uchar_with_stats = connectedComponentsWithStats( + input_uchar, labels_uchar, stats_uchar, centroids_uchar, connectivity, CV_32S, ccltype[cclt]); + + normalizeLabels(labels_bool, nlabels_bool); + normalizeLabels(labels_uchar, nlabels_uchar_with_stats); + + EXPECT_EQ(nlabels_bool, nlabels_uchar_with_stats) + << "connectivity = " << connectivity << ", ccltype = " << ccltype[cclt]; + EXPECT_EQ(countNonZero(labels_bool != labels_uchar), 0) + << "connectivity = " << connectivity << ", ccltype = " << ccltype[cclt]; + EXPECT_EQ(countNonZero(stats_bool != stats_uchar), 0) + << "connectivity = " << connectivity << ", ccltype = " << ccltype[cclt]; + EXPECT_EQ(cv::norm(centroids_bool, centroids_uchar, NORM_INF), 0) + << "connectivity = " << connectivity << ", ccltype = " << ccltype[cclt]; + } + } +} TEST(Imgproc_ConnectedComponents, 4conn_regression_21366) {