diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp index e757b0fa91..e8566faa3a 100644 --- a/modules/video/include/opencv2/video/tracking.hpp +++ b/modules/video/include/opencv2/video/tracking.hpp @@ -265,6 +265,19 @@ enum MOTION_HOMOGRAPHY = 3 }; +/** @brief Computes the Enhanced Correlation Coefficient value between two images @cite EP08 . + +@param templateImage single-channel template image; CV_8U or CV_32F array. +@param inputImage single-channel input image to be warped to provide an image similar to + templateImage, same type as templateImage. +@param inputMask An optional mask to indicate valid values of inputImage. + +@sa +findTransformECC + */ + +CV_EXPORTS_W double computeECC(InputArray templateImage, InputArray inputImage, InputArray inputMask = noArray()); + /** @example samples/cpp/image_alignment.cpp An example using the image alignment ECC algorithm */ @@ -273,7 +286,7 @@ An example using the image alignment ECC algorithm @param templateImage single-channel template image; CV_8U or CV_32F array. @param inputImage single-channel input image which should be warped with the final warpMatrix in -order to provide an image similar to templateImage, same type as temlateImage. +order to provide an image similar to templateImage, same type as templateImage. @param warpMatrix floating-point \f$2\times 3\f$ or \f$3\times 3\f$ mapping matrix (warp). @param motionType parameter, specifying the type of motion: - **MOTION_TRANSLATION** sets a translational motion model; warpMatrix is \f$2\times 3\f$ with @@ -290,6 +303,7 @@ criteria.epsilon defines the threshold of the increment in the correlation coeff iterations (a negative criteria.epsilon makes criteria.maxcount the only termination criterion). Default values are shown in the declaration above. @param inputMask An optional mask to indicate valid values of inputImage. +@param gaussFiltSize An optional value indicating size of gaussian blur filter; (DEFAULT: 5) The function estimates the optimum transformation (warpMatrix) with respect to ECC criterion (@cite EP08), that is @@ -317,12 +331,19 @@ sample image_alignment.cpp that demonstrates the use of the function. Note that an exception if algorithm does not converges. @sa -estimateAffine2D, estimateAffinePartial2D, findHomography +computeECC, estimateAffine2D, estimateAffinePartial2D, findHomography */ CV_EXPORTS_W double findTransformECC( InputArray templateImage, InputArray inputImage, - InputOutputArray warpMatrix, int motionType = MOTION_AFFINE, - TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 50, 0.001), - InputArray inputMask = noArray()); + InputOutputArray warpMatrix, int motionType, + TermCriteria criteria, + InputArray inputMask, int gaussFiltSize); + +/** @overload */ +CV_EXPORTS +double findTransformECC(InputArray templateImage, InputArray inputImage, + InputOutputArray warpMatrix, int motionType = MOTION_AFFINE, + TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 50, 0.001), + InputArray inputMask = noArray()); /** @example samples/cpp/kalman.cpp An example using the standard Kalman filter diff --git a/modules/video/src/ecc.cpp b/modules/video/src/ecc.cpp index aabb317b65..cd189566c7 100644 --- a/modules/video/src/ecc.cpp +++ b/modules/video/src/ecc.cpp @@ -309,16 +309,48 @@ static void update_warping_matrix_ECC (Mat& map_matrix, const Mat& update, const } +/** Function that computes enhanced corelation coefficient from Georgios et.al. 2008 +* See https://github.com/opencv/opencv/issues/12432 +*/ +double cv::computeECC(InputArray templateImage, InputArray inputImage, InputArray inputMask) +{ + CV_Assert(!templateImage.empty()); + CV_Assert(!inputImage.empty()); + + if( ! (templateImage.type()==inputImage.type())) + CV_Error( Error::StsUnmatchedFormats, "Both input images must have the same data type" ); + + Scalar meanTemplate, sdTemplate; + + int active_pixels = inputMask.empty() ? templateImage.size().area() : countNonZero(inputMask); + + meanStdDev(templateImage, meanTemplate, sdTemplate, inputMask); + Mat templateImage_zeromean = Mat::zeros(templateImage.size(), templateImage.type()); + subtract(templateImage, 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); + double inputImagenorm = std::sqrt(active_pixels*sdInput.val[0]*sdInput.val[0]); + + return templateImage_zeromean.dot(inputImage_zeromean)/(templateImagenorm*inputImagenorm); +} + + double cv::findTransformECC(InputArray templateImage, InputArray inputImage, InputOutputArray warpMatrix, int motionType, TermCriteria criteria, - InputArray inputMask) + InputArray inputMask, + int gaussFiltSize) { - Mat src = templateImage.getMat();//template iamge + Mat src = templateImage.getMat();//template image Mat dst = inputImage.getMat(); //input image (to be warped) Mat map = warpMatrix.getMat(); //warp (transformation) @@ -416,11 +448,11 @@ double cv::findTransformECC(InputArray templateImage, //gaussian filtering is optional src.convertTo(templateFloat, templateFloat.type()); - GaussianBlur(templateFloat, templateFloat, Size(5, 5), 0, 0); + GaussianBlur(templateFloat, templateFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0); Mat preMaskFloat; preMask.convertTo(preMaskFloat, CV_32F); - GaussianBlur(preMaskFloat, preMaskFloat, Size(5, 5), 0, 0); + GaussianBlur(preMaskFloat, preMaskFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0); // Change threshold. preMaskFloat *= (0.5/0.95); // Rounding conversion. @@ -428,7 +460,7 @@ double cv::findTransformECC(InputArray templateImage, preMask.convertTo(preMaskFloat, preMaskFloat.type()); dst.convertTo(imageFloat, imageFloat.type()); - GaussianBlur(imageFloat, imageFloat, Size(5, 5), 0, 0); + GaussianBlur(imageFloat, imageFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0); // needed matrices for gradients and warped gradients Mat gradientX = Mat::zeros(hd, wd, CV_32FC1); @@ -557,5 +589,13 @@ double cv::findTransformECC(InputArray templateImage, return rho; } +double cv::findTransformECC(InputArray templateImage, InputArray inputImage, + InputOutputArray warpMatrix, int motionType, + TermCriteria criteria, + InputArray inputMask) +{ + // Use default value of 5 for gaussFiltSize to maintain backward compatibility. + return findTransformECC(templateImage, inputImage, warpMatrix, motionType, criteria, inputMask, 5); +} /* End of file. */ diff --git a/modules/video/test/test_ecc.cpp b/modules/video/test/test_ecc.cpp index 913cd59b87..84c5b851f5 100644 --- a/modules/video/test/test_ecc.cpp +++ b/modules/video/test/test_ecc.cpp @@ -95,7 +95,6 @@ double CV_ECC_BaseTest::computeRMS(const Mat& mat1, const Mat& mat2){ return sqrt(errorMat.dot(errorMat)/(mat1.rows*mat1.cols)); } - class CV_ECC_Test_Translation : public CV_ECC_BaseTest { public: @@ -464,6 +463,22 @@ bool CV_ECC_Test_Mask::testMask(int from) return false; } + // Test with non-default gaussian blur. + findTransformECC(warpedImage, testImg, mapTranslation, 0, + TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon), mask, 1); + + if (!isMapCorrect(mapTranslation)){ + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + return false; + } + + if (computeRMS(mapTranslation, translationGround)>MAX_RMS_ECC){ + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + ts->printf( ts->LOG, "RMS = %f", + computeRMS(mapTranslation, translationGround)); + return false; + } + } return true; } @@ -476,6 +491,16 @@ void CV_ECC_Test_Mask::run(int from) ts->set_failed_test_info(cvtest::TS::OK); } +TEST(Video_ECC_Test_Compute, accuracy) +{ + Mat testImg = (Mat_(3, 3) << 1, 0, 0, 1, 0, 0, 1, 0, 0); + Mat warpedImage = (Mat_(3, 3) << 0, 1, 0, 0, 1, 0, 0, 1, 0); + Mat_ mask = Mat_::ones(testImg.rows, testImg.cols); + double ecc = computeECC(warpedImage, testImg, mask); + + EXPECT_NEAR(ecc, -0.5f, 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(); }