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
This commit is contained in:
Danny 2021-04-30 13:20:52 -04:00 committed by GitHub
parent baa22fa808
commit 1b844f8413
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 3 deletions

View File

@ -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);

View File

@ -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_<uint8_t>(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(); }