From 1b844f84132b2a40b031bf49e935a46c59cdd200 Mon Sep 17 00:00:00 2001 From: Danny <33044223+danielenricocahall@users.noreply.github.com> Date: Fri, 30 Apr 2021 13:20:52 -0400 Subject: [PATCH] Merge pull request #19993 from danielenricocahall:fix-compute-ecc-issue Fix unsigned int bug in computeECC * address issue with unsigned ints in computeEcc * remove additional logic checking firstOctave * use swap instead of same src/dst * simplify the unsigned check logic --- modules/video/src/ecc.cpp | 23 ++++++++++++++++++++--- modules/video/test/test_ecc.cpp | 12 ++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/modules/video/src/ecc.cpp b/modules/video/src/ecc.cpp index b7148c22ef..eede0e7071 100644 --- a/modules/video/src/ecc.cpp +++ b/modules/video/src/ecc.cpp @@ -323,17 +323,34 @@ double cv::computeECC(InputArray templateImage, InputArray inputImage, InputArra Scalar meanTemplate, sdTemplate; int active_pixels = inputMask.empty() ? templateImage.size().area() : countNonZero(inputMask); - + int type = templateImage.type(); meanStdDev(templateImage, meanTemplate, sdTemplate, inputMask); Mat templateImage_zeromean = Mat::zeros(templateImage.size(), templateImage.type()); - subtract(templateImage, meanTemplate, templateImage_zeromean, inputMask); + Mat templateMat = templateImage.getMat(); + Mat inputMat = inputImage.getMat(); + + /* + * For unsigned ints, when the mean is computed and subtracted, any values less than the mean + * will be set to 0 (since there are no negatives values). This impacts the norm and dot product, which + * ultimately results in an incorrect ECC. To circumvent this problem, if unsigned ints are provided, + * we convert them to a signed ints with larger resolution for the subtraction step. + */ + if(type == CV_8U || type == CV_16U) { + int newType = type == CV_8U ? CV_16S : CV_32S; + Mat templateMatConverted, inputMatConverted; + templateMat.convertTo(templateMatConverted, newType); + cv::swap(templateMat, templateMatConverted); + inputMat.convertTo(inputMatConverted, newType); + cv::swap(inputMat, inputMatConverted); + } + subtract(templateMat, meanTemplate, templateImage_zeromean, inputMask); double templateImagenorm = std::sqrt(active_pixels*sdTemplate.val[0]*sdTemplate.val[0]); Scalar meanInput, sdInput; Mat inputImage_zeromean = Mat::zeros(inputImage.size(), inputImage.type()); meanStdDev(inputImage, meanInput, sdInput, inputMask); - subtract(inputImage, meanInput, inputImage_zeromean, inputMask); + subtract(inputMat, meanInput, inputImage_zeromean, inputMask); double inputImagenorm = std::sqrt(active_pixels*sdInput.val[0]*sdInput.val[0]); return templateImage_zeromean.dot(inputImage_zeromean)/(templateImagenorm*inputImagenorm); diff --git a/modules/video/test/test_ecc.cpp b/modules/video/test/test_ecc.cpp index 84c5b851f5..21a5ae3915 100644 --- a/modules/video/test/test_ecc.cpp +++ b/modules/video/test/test_ecc.cpp @@ -501,6 +501,18 @@ TEST(Video_ECC_Test_Compute, accuracy) EXPECT_NEAR(ecc, -0.5f, 1e-5f); } +TEST(Video_ECC_Test_Compute, bug_14657) +{ + /* + * Simple test case - a 2 x 2 matrix with 10, 10, 10, 6. When the mean (36 / 4 = 9) is subtracted, + * it results in 1, 1, 1, 0 for the unsigned int case - compare to 1, 1, 1, -3 in the signed case. + * For this reason, when the same matrix was provided as the input and the template, we didn't get 1 as expected. + */ + Mat img = (Mat_(2, 2) << 10, 10, 10, 6); + EXPECT_NEAR(computeECC(img, img), 1.0f, 1e-5f); +} + + TEST(Video_ECC_Translation, accuracy) { CV_ECC_Test_Translation test; test.safe_run();} TEST(Video_ECC_Euclidean, accuracy) { CV_ECC_Test_Euclidean test; test.safe_run(); } TEST(Video_ECC_Affine, accuracy) { CV_ECC_Test_Affine test; test.safe_run(); }