diff --git a/modules/imgproc/test/test_drawing.cpp b/modules/imgproc/test/test_drawing.cpp index dc227e8b54..e421d697e6 100644 --- a/modules/imgproc/test/test_drawing.cpp +++ b/modules/imgproc/test/test_drawing.cpp @@ -1139,7 +1139,7 @@ PARAM_TEST_CASE(FillPolyFully, unsigned, unsigned, int, int, Point, cv::LineType } }; -TEST_P(FillPolyFully, DISABLED_fillpoly_fully) +TEST_P(FillPolyFully, fillpoly_fully) { int imageSizeOffset = 15; diff --git a/modules/imgproc/test/test_imgproc_umat.cpp b/modules/imgproc/test/test_imgproc_umat.cpp index 74bfdac621..f1de3d2317 100644 --- a/modules/imgproc/test/test_imgproc_umat.cpp +++ b/modules/imgproc/test/test_imgproc_umat.cpp @@ -44,41 +44,30 @@ namespace opencv_test { namespace { -class CV_ImgprocUMatTest : public cvtest::BaseTest +TEST(Imgproc_UMat, regression) { -public: - CV_ImgprocUMatTest() {} - ~CV_ImgprocUMatTest() {} -protected: - void run(int) - { - string imgpath = string(ts->get_data_path()) + "shared/lena.png"; - Mat img = imread(imgpath, IMREAD_COLOR), gray, smallimg, result; - UMat uimg = img.getUMat(ACCESS_READ), ugray, usmallimg, uresult; + const string imgpath = cvtest::findDataFile("shared/lena.png"); + Mat img = imread(imgpath, IMREAD_COLOR); + ASSERT_FALSE(img.empty()); - cvtColor(img, gray, COLOR_BGR2GRAY); - resize(gray, smallimg, Size(), 0.75, 0.75, INTER_LINEAR_EXACT); - equalizeHist(smallimg, result); + Mat gray, smallimg, result; + cvtColor(img, gray, COLOR_BGR2GRAY); + resize(gray, smallimg, Size(), 0.75, 0.75, INTER_LINEAR_EXACT); + equalizeHist(smallimg, result); - cvtColor(uimg, ugray, COLOR_BGR2GRAY); - resize(ugray, usmallimg, Size(), 0.75, 0.75, INTER_LINEAR_EXACT); - equalizeHist(usmallimg, uresult); + // Deliberately getUMat() rather than copyTo(): the ocl/ tests always upload + // by copy (UMAT_UPLOAD_INPUT_PARAMETER), so this is the only coverage of an + // imgproc chain fed by a UMat view over a Mat that stays alive alongside it. + UMat uimg = img.getUMat(ACCESS_READ), ugray, usmallimg, uresult; + cvtColor(uimg, ugray, COLOR_BGR2GRAY); + resize(ugray, usmallimg, Size(), 0.75, 0.75, INTER_LINEAR_EXACT); + equalizeHist(usmallimg, uresult); -#if 0 - imshow("orig", uimg); - imshow("small", usmallimg); - imshow("equalized gray", uresult); - waitKey(); - destroyWindow("orig"); - destroyWindow("small"); - destroyWindow("equalized gray"); -#endif - ts->set_failed_test_info(cvtest::TS::OK); - - (void)uresult.getMat(ACCESS_READ); - } -}; - -TEST(Imgproc_UMat, regression) { CV_ImgprocUMatTest test; test.safe_run(); } + // Measured identical here; the tolerance of 1 matches OCL_TEST_P(EqualizeHist, Mat), + // since bit-exactness was only confirmed on one device. + Mat uresult_host = uresult.getMat(ACCESS_READ); + EXPECT_LE(cv::norm(result, uresult_host, NORM_INF), 1) + << "Mat and UMat imgproc pipelines disagree"; +} }} // namespace diff --git a/modules/imgproc/test/test_resize_bitexact.cpp b/modules/imgproc/test/test_resize_bitexact.cpp index def5ceb0f0..8b7d082886 100644 --- a/modules/imgproc/test/test_resize_bitexact.cpp +++ b/modules/imgproc/test/test_resize_bitexact.cpp @@ -183,8 +183,9 @@ TEST_P(Resize_Bitexact, Nearest8U_vsNonExact) EXPECT_EQ(CountDiff(mat_gray), 0) << "gray, type: " << depth; } -// Now INTER_NEAREST's convention and INTER_NEAREST_EXACT's one are different. -INSTANTIATE_TEST_CASE_P(DISABLED_Imgproc, Resize_Bitexact, +// At an integer upscale INTER_NEAREST and INTER_NEAREST_EXACT both map dst i to src i/k, so they +// agree here. They diverge on downscale and fractional factors, where EXACT follows Pillow. +INSTANTIATE_TEST_CASE_P(Imgproc, Resize_Bitexact, testing::Values(CV_8U, CV_16U, CV_32F, CV_64F) ); diff --git a/modules/imgproc/test/test_watershed.cpp b/modules/imgproc/test/test_watershed.cpp index 0fb6521809..14cbb27c0d 100644 --- a/modules/imgproc/test/test_watershed.cpp +++ b/modules/imgproc/test/test_watershed.cpp @@ -41,3 +41,70 @@ //M*/ #include "test_precomp.hpp" + +namespace opencv_test { namespace { + +// Seeds are eroded copies of the stored reference regions, so watershed has to re-grow the +// boundaries. The neighbouring watershed/comp.xml is not used: it is an +// "opencv-sequence-tree" holding CvSeq contours and needs the removed C API to read. +TEST(Imgproc_Watershed, regression) +{ + const string folder = string(cvtest::TS::ptr()->get_data_path()); + const string expPath = folder + "watershed/wshed_exp.png"; + + Mat image = imread(folder + "inpaint/orig.png", IMREAD_COLOR); + Mat expLabels8 = imread(expPath, IMREAD_GRAYSCALE); + ASSERT_FALSE(image.empty()) << "Could not read " << folder << "inpaint/orig.png"; + ASSERT_FALSE(expLabels8.empty()) << "Could not read " << expPath; + ASSERT_EQ(image.size(), expLabels8.size()); + + // wshed_exp.png stores the expected labels offset by +1, so that the -1 used for + // watershed boundaries survives being written to an 8-bit PNG. + Mat expected; + expLabels8.convertTo(expected, CV_32S); + expected -= 1; // -1 = boundary, 1..nLabels = regions + + double maxLabel = 0; + minMaxLoc(expected, 0, &maxLabel); + const int nLabels = cvRound(maxLabel); + ASSERT_GT(nLabels, 1) << "reference image does not contain several regions"; + + Mat markers = Mat::zeros(expected.size(), CV_32S); + Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(15, 15)); + for (int label = 1; label <= nLabels; label++) + { + Mat seed; + erode(expected == label, seed, kernel); + ASSERT_GT(countNonZero(seed), 0) << "region " << label << " vanished when eroded"; + markers.setTo(label, seed); + } + + Mat result = markers.clone(); + watershed(image, result); + + ASSERT_EQ(expected.size(), result.size()); + ASSERT_EQ(CV_32S, result.type()); + + EXPECT_EQ(0, countNonZero(result == 0)) << "watershed left pixels unlabelled"; + + double minVal = 0, maxVal = 0; + minMaxLoc(result, &minVal, &maxVal); + EXPECT_GE(minVal, -1); + EXPECT_LE(maxVal, nLabels) << "watershed produced a label that was never seeded"; + + // Measured: ~99.6% for a correct implementation, ~78.7% if watershed does nothing. + Mat interior = expected > 0; + const int totalPx = countNonZero(interior); + ASSERT_GT(totalPx, 0); + Mat agreeMask; + bitwise_and(result == expected, interior, agreeMask); + const double agreement = (double)countNonZero(agreeMask) / totalPx; + EXPECT_GT(agreement, 0.95) + << "only " << agreement * 100 << "% of interior pixels match " << expPath; + + Mat again = markers.clone(); + watershed(image, again); + EXPECT_EQ(0, countNonZero(again != result)) << "watershed is not deterministic"; +} + +}} // namespace diff --git a/modules/python/test/test_watershed.py b/modules/python/test/test_watershed.py index b3f72c11ea..e88318327a 100644 --- a/modules/python/test/test_watershed.py +++ b/modules/python/test/test_watershed.py @@ -22,8 +22,17 @@ class watershed_test(NewOpenCVTests): if img is None or markers is None: self.assertEqual(0, 1, 'Missing test data') + # cv.watershed() writes into markers in place, so the CV_32S array must be bound + # to a name: np.int32(markers) inline would hand over a temporary. + markers = np.int32(markers) + before = markers.copy() + colors = np.int32( list(np.ndindex(3, 3, 3)) ) * 122 - cv.watershed(img, np.int32(markers)) + cv.watershed(img, markers) + + self.assertFalse(np.array_equal(before, markers), + 'cv.watershed() did not modify the markers in place') + segments = colors[np.maximum(markers, 0)] if refSegments is None: