diff --git a/hal/ipp/src/matchtemplate_ipp.cpp b/hal/ipp/src/matchtemplate_ipp.cpp index 7ff0807aa6..6df33706c6 100644 --- a/hal/ipp/src/matchtemplate_ipp.cpp +++ b/hal/ipp/src/matchtemplate_ipp.cpp @@ -132,8 +132,9 @@ int ipp_hal_matchTemplate(const uchar* src_data, size_t src_step, int src_width, else if(method == cv::TM_SQDIFF_NORMED || method == cv::TM_CCORR || method == cv::TM_CCOEFF || method == cv::TM_CCOEFF_NORMED) { - // Raw cross-correlation; caller finishes SQDIFF_NORMED/CCOEFF/CCOEFF_NORMED via - // common_matchTemplate (TM_CCORR is already final). + // Raw cross-correlation only. TM_CCORR is already the final result; the caller + // (cv::matchTemplate) runs common_matchTemplate() to finish TM_SQDIFF_NORMED / + // TM_CCOEFF / TM_CCOEFF_NORMED. if(ipp_crossCorr(img, templ, result, false)) return CV_HAL_ERROR_OK; } diff --git a/modules/imgproc/src/templmatch.cpp b/modules/imgproc/src/templmatch.cpp index 45c38dc4ad..fad936518d 100644 --- a/modules/imgproc/src/templmatch.cpp +++ b/modules/imgproc/src/templmatch.cpp @@ -47,6 +47,12 @@ namespace cv { +static inline Size matchResultSize(Size imageSize, Size templSize) +{ + return Size(imageSize.width - templSize.width + 1, + imageSize.height - templSize.height + 1); +} + #ifdef HAVE_OPENCL /////////////////////////////////////////////////// CCORR ////////////////////////////////////////////////////////////// @@ -234,7 +240,7 @@ static bool convolve_dft(InputArray _image, InputArray _templ, OutputArray _resu static bool convolve_32F(InputArray _image, InputArray _templ, OutputArray _result) { - _result.create(_image.rows() - _templ.rows() + 1, _image.cols() - _templ.cols() + 1, CV_32F); + _result.create(matchResultSize(_image.size(), _templ.size()), CV_32F); if (_image.channels() == 1) return(convolve_dft(_image, _templ, _result)); @@ -280,7 +286,7 @@ static bool matchTemplateNaive_CCORR(InputArray _image, InputArray _templ, Outpu return false; UMat image = _image.getUMat(), templ = _templ.getUMat(); - _result.create(image.rows - templ.rows + 1, image.cols - templ.cols + 1, CV_32FC1); + _result.create(matchResultSize(image.size(), templ.size()), CV_32FC1); UMat result = _result.getUMat(); k.args(ocl::KernelArg::ReadOnlyNoSize(image), ocl::KernelArg::ReadOnly(templ), @@ -313,26 +319,27 @@ static bool matchTemplate_CCORR(InputArray _image, InputArray _templ, OutputArra } } -static bool matchTemplate_CCORR_NORMED(InputArray _image, InputArray _templ, OutputArray _result) +static bool matchTemplatePrepared(InputArray _image, InputArray _templ, OutputArray _result, + const char* kernelName, const char* buildDef) { matchTemplate(_image, _templ, _result, cv::TM_CCORR); int type = _image.type(), cn = CV_MAT_CN(type); - ocl::Kernel k("matchTemplate_CCORR_NORMED", ocl::imgproc::match_template_oclsrc, - format("-D CCORR_NORMED -D T=%s -D cn=%d", ocl::typeToStr(type), cn)); + ocl::Kernel k(kernelName, ocl::imgproc::match_template_oclsrc, + format("-D %s -D T=%s -D cn=%d", buildDef, ocl::typeToStr(type), cn)); if (k.empty()) return false; UMat image = _image.getUMat(), templ = _templ.getUMat(); - _result.create(image.rows - templ.rows + 1, image.cols - templ.cols + 1, CV_32FC1); + _result.create(matchResultSize(image.size(), templ.size()), CV_32F); UMat result = _result.getUMat(); UMat image_sums, image_sqsums; integral(image.reshape(1), image_sums, image_sqsums, CV_32F, CV_32F); UMat templ_sqsum; - if (!sumTemplate(templ, templ_sqsum)) + if (!sumTemplate(_templ, templ_sqsum)) return false; k.args(ocl::KernelArg::ReadOnlyNoSize(image_sqsums), ocl::KernelArg::ReadWrite(result), @@ -342,6 +349,11 @@ static bool matchTemplate_CCORR_NORMED(InputArray _image, InputArray _templ, Out return k.run(2, globalsize, NULL, false); } +static bool matchTemplate_CCORR_NORMED(InputArray _image, InputArray _templ, OutputArray _result) +{ + return matchTemplatePrepared(_image, _templ, _result, "matchTemplate_CCORR_NORMED", "CCORR_NORMED"); +} + ////////////////////////////////////// SQDIFF ////////////////////////////////////////////////////////////// static bool matchTemplateNaive_SQDIFF(InputArray _image, InputArray _templ, OutputArray _result) @@ -357,7 +369,7 @@ static bool matchTemplateNaive_SQDIFF(InputArray _image, InputArray _templ, Outp return false; UMat image = _image.getUMat(), templ = _templ.getUMat(); - _result.create(image.rows - templ.rows + 1, image.cols - templ.cols + 1, CV_32F); + _result.create(matchResultSize(image.size(), templ.size()), CV_32F); UMat result = _result.getUMat(); k.args(ocl::KernelArg::ReadOnlyNoSize(image), ocl::KernelArg::ReadOnly(templ), @@ -372,64 +384,12 @@ static bool matchTemplate_SQDIFF(InputArray _image, InputArray _templ, OutputArr if (useNaive(_templ.size())) return( matchTemplateNaive_SQDIFF(_image, _templ, _result)); else - { - matchTemplate(_image, _templ, _result, cv::TM_CCORR); - - int type = _image.type(), cn = CV_MAT_CN(type); - - ocl::Kernel k("matchTemplate_Prepared_SQDIFF", ocl::imgproc::match_template_oclsrc, - format("-D SQDIFF_PREPARED -D T=%s -D cn=%d", ocl::typeToStr(type), cn)); - if (k.empty()) - return false; - - UMat image = _image.getUMat(), templ = _templ.getUMat(); - _result.create(image.rows - templ.rows + 1, image.cols - templ.cols + 1, CV_32F); - UMat result = _result.getUMat(); - - UMat image_sums, image_sqsums; - integral(image.reshape(1), image_sums, image_sqsums, CV_32F, CV_32F); - - UMat templ_sqsum; - if (!sumTemplate(_templ, templ_sqsum)) - return false; - - k.args(ocl::KernelArg::ReadOnlyNoSize(image_sqsums), ocl::KernelArg::ReadWrite(result), - templ.rows, templ.cols, ocl::KernelArg::PtrReadOnly(templ_sqsum)); - - size_t globalsize[2] = { (size_t)result.cols, (size_t)result.rows }; - - return k.run(2, globalsize, NULL, false); - } + return matchTemplatePrepared(_image, _templ, _result, "matchTemplate_Prepared_SQDIFF", "SQDIFF_PREPARED"); } static bool matchTemplate_SQDIFF_NORMED(InputArray _image, InputArray _templ, OutputArray _result) { - matchTemplate(_image, _templ, _result, cv::TM_CCORR); - - int type = _image.type(), cn = CV_MAT_CN(type); - - ocl::Kernel k("matchTemplate_SQDIFF_NORMED", ocl::imgproc::match_template_oclsrc, - format("-D SQDIFF_NORMED -D T=%s -D cn=%d", ocl::typeToStr(type), cn)); - if (k.empty()) - return false; - - UMat image = _image.getUMat(), templ = _templ.getUMat(); - _result.create(image.rows - templ.rows + 1, image.cols - templ.cols + 1, CV_32F); - UMat result = _result.getUMat(); - - UMat image_sums, image_sqsums; - integral(image.reshape(1), image_sums, image_sqsums, CV_32F, CV_32F); - - UMat templ_sqsum; - if (!sumTemplate(_templ, templ_sqsum)) - return false; - - k.args(ocl::KernelArg::ReadOnlyNoSize(image_sqsums), ocl::KernelArg::ReadWrite(result), - templ.rows, templ.cols, ocl::KernelArg::PtrReadOnly(templ_sqsum)); - - size_t globalsize[2] = { (size_t)result.cols, (size_t)result.rows }; - - return k.run(2, globalsize, NULL, false); + return matchTemplatePrepared(_image, _templ, _result, "matchTemplate_SQDIFF_NORMED", "SQDIFF_NORMED"); } ///////////////////////////////////// CCOEFF ///////////////////////////////////////////////////////////////// @@ -486,7 +446,7 @@ static bool matchTemplate_CCOEFF_NORMED(InputArray _image, InputArray _templ, Ou UMat templ = _templ.getUMat(); Size size = _image.size(), tsize = templ.size(); - _result.create(size.height - templ.rows + 1, size.width - templ.cols + 1, CV_32F); + _result.create(matchResultSize(size, tsize), CV_32F); UMat result = _result.getUMat(); float scale = 1.f / tsize.area(); @@ -758,6 +718,20 @@ void crossCorr( const Mat& img, const Mat& _templ, Mat& corr, } } +static void crossCorrImgSq( const Mat& img, const Mat& mask2, Mat& dst ) +{ + crossCorr(img.mul(img), mask2, dst, Point(0, 0), 0, 0); +} + +static Mat sumChannels( const Mat& e, int rows ) +{ + if (e.channels() == 1) + return e; + Mat r = e.reshape(1, e.rows * e.cols); + reduce(r, r, 1, REDUCE_SUM); + return r.reshape(1, rows); +} + static void matchTemplateMask( InputArray _img, InputArray _templ, OutputArray _result, int method, InputArray _mask ) { CV_Assert(_mask.depth() == CV_8U || _mask.depth() == CV_32F); @@ -783,7 +757,7 @@ static void matchTemplateMask( InputArray _img, InputArray _templ, OutputArray _ maskBin.convertTo(mask, CV_32F); } - Size corrSize(img.cols - templ.cols + 1, img.rows - templ.rows + 1); + Size corrSize = matchResultSize(img.size(), templ.size()); _result.create(corrSize, CV_32F); Mat result = _result.getMat(); @@ -798,12 +772,11 @@ static void matchTemplateMask( InputArray _img, InputArray _templ, OutputArray _ if (method == cv::TM_SQDIFF || method == cv::TM_SQDIFF_NORMED) { Mat temp_result(corrSize, CV_32F); - Mat img2 = img.mul(img); Mat mask2 = mask.mul(mask); // If the mul() is ever unnested, declare MatExpr, *not* Mat, to be more efficient. // NORM_L2SQR calculates sum of squares double templ2_mask2_sum = norm(templ.mul(mask), NORM_L2SQR); - crossCorr(img2, mask2, temp_result, Point(0,0), 0, 0); + crossCorrImgSq(img, mask2, temp_result); crossCorr(img, templ.mul(mask2), result, Point(0,0), 0, 0); // result and temp_result should not be switched, because temp_result is potentially needed // for normalization. @@ -824,11 +797,10 @@ static void matchTemplateMask( InputArray _img, InputArray _templ, OutputArray _ if (method == cv::TM_CCORR_NORMED) { Mat temp_result(corrSize, CV_32F); - Mat img2 = img.mul(img); Mat mask2 = mask.mul(mask); // NORM_L2SQR calculates sum of squares double templ2_mask2_sum = norm(templ.mul(mask), NORM_L2SQR); - crossCorr( img2, mask2, temp_result, Point(0,0), 0, 0 ); + crossCorrImgSq(img, mask2, temp_result); sqrt(templ2_mask2_sum * temp_result, temp_result); result /= temp_result; } @@ -851,19 +823,7 @@ static void matchTemplateMask( InputArray _img, InputArray _templ, OutputArray _ // It does not matter what to use Mat/MatExpr, it should be evaluated to perform assign subtraction Mat temp_res; multiply(img_mask_corr, sum(templx_mask).div(mask_sum), temp_res); - if (img.channels() == 1) - { - result -= temp_res; - } - else - { - // Sum channels of expression - temp_res = temp_res.reshape(1, result.rows * result.cols); - // channels are now columns - reduce(temp_res, temp_res, 1, REDUCE_SUM); - // transform back, but now with only one channel - result -= temp_res.reshape(1, result.rows); - } + result -= sumChannels(temp_res, result.rows); if (method == cv::TM_CCOEFF_NORMED) { // norm(T') @@ -875,31 +835,17 @@ static void matchTemplateMask( InputArray _img, InputArray _templ, OutputArray _ // + CCorr(I, M)/sum(M)*{ sum(M^2) / sum(M) * CCorr(I,M) // - 2 * CCorr(I, M^2) } } Mat norm_imgx(corrSize, CV_32F); - Mat img2 = img.mul(img); Mat mask2 = mask.mul(mask); Scalar mask2_sum = sum(mask2); Mat img_mask2_corr(corrSize, img.type()); - crossCorr(img2, mask2, norm_imgx, Point(0,0), 0, 0); + crossCorrImgSq(img, mask2, norm_imgx); crossCorr(img, mask2, img_mask2_corr, Point(0,0), 0, 0); Mat temp_res1; multiply(img_mask_corr, Scalar(1.0, 1.0, 1.0, 1.0).div(mask_sum), temp_res1); Mat temp_res2; multiply(img_mask_corr, mask2_sum.div(mask_sum), temp_res2); temp_res = temp_res1.mul(temp_res2 - 2 * img_mask2_corr); - if (img.channels() == 1) - { - norm_imgx += temp_res; - } - else - { - // Sum channels of expression - temp_res = temp_res.reshape(1, result.rows*result.cols); - // channels are now columns - // reduce sums columns (= channels) - reduce(temp_res, temp_res, 1, REDUCE_SUM); - // transform back, but now with only one channel - norm_imgx += temp_res.reshape(1, result.rows); - } + norm_imgx += sumChannels(temp_res, result.rows); sqrt(norm_imgx, norm_imgx); result /= norm_imgx * norm_templx; } @@ -1063,7 +1009,7 @@ void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, if (needswap) std::swap(img, templ); - Size corrSize(img.cols - templ.cols + 1, img.rows - templ.rows + 1); + Size corrSize = matchResultSize(img.size(), templ.size()); _result.create(corrSize, CV_32F); Mat result = _result.getMat();