diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index e67922eec0..9118d76ae6 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -3330,9 +3330,11 @@ data type. @param result Map of comparison results. It must be single-channel 32-bit floating-point. If image is \f$W \times H\f$ and templ is \f$w \times h\f$ , then result is \f$(W-w+1) \times (H-h+1)\f$ . @param method Parameter specifying the comparison method, see cv::TemplateMatchModes +@param mask Mask of searched template. It must have the same datatype and size with templ. It is +not set by default. */ CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ, - OutputArray result, int method ); + OutputArray result, int method, InputArray mask = noArray() ); //! @} diff --git a/modules/imgproc/src/templmatch.cpp b/modules/imgproc/src/templmatch.cpp index 416917a2fb..8afdba7d10 100644 --- a/modules/imgproc/src/templmatch.cpp +++ b/modules/imgproc/src/templmatch.cpp @@ -814,12 +814,97 @@ void crossCorr( const Mat& img, const Mat& _templ, Mat& corr, } } } + +static void matchTemplateMask( InputArray _img, InputArray _templ, OutputArray _result, int method, InputArray _mask ) +{ + int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); + CV_Assert( CV_TM_SQDIFF <= method && method <= CV_TM_CCOEFF_NORMED ); + CV_Assert( (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2 ); + + Mat img = _img.getMat(), templ = _templ.getMat(), mask = _mask.getMat(); + int ttype = templ.type(), tdepth = CV_MAT_DEPTH(ttype), tcn = CV_MAT_CN(ttype); + int mtype = img.type(), mdepth = CV_MAT_DEPTH(type), mcn = CV_MAT_CN(mtype); + + if (depth == CV_8U) + { + depth = CV_32F; + type = CV_MAKETYPE(CV_32F, cn); + img.convertTo(img, type, 1.0 / 255); + } + + if (tdepth == CV_8U) + { + tdepth = CV_32F; + ttype = CV_MAKETYPE(CV_32F, tcn); + templ.convertTo(templ, ttype, 1.0 / 255); + } + + if (mdepth == CV_8U) + { + mdepth = CV_32F; + mtype = CV_MAKETYPE(CV_32F, mcn); + compare(mask, Scalar::all(0), mask, CMP_NE); + mask.convertTo(mask, mtype, 1.0 / 255); + } + + Size corrSize(img.cols - templ.cols + 1, img.rows - templ.rows + 1); + _result.create(corrSize, CV_32F); + Mat result = _result.getMat(); + + Mat img2 = img.mul(img); + Mat mask2 = mask.mul(mask); + Mat mask_templ = templ.mul(mask); + Scalar templMean, templSdv; + + double templSum2 = 0; + meanStdDev( mask_templ, templMean, templSdv ); + + templSum2 = templSdv[0]*templSdv[0] + templSdv[1]*templSdv[1] + templSdv[2]*templSdv[2] + templSdv[3]*templSdv[3]; + templSum2 += templMean[0]*templMean[0] + templMean[1]*templMean[1] + templMean[2]*templMean[2] + templMean[3]*templMean[3]; + templSum2 *= ((double)templ.rows * templ.cols); + + if (method == CV_TM_SQDIFF) + { + Mat mask2_templ = templ.mul(mask2); + + Mat corr(corrSize, CV_32F); + crossCorr( img, mask2_templ, corr, corr.size(), corr.type(), Point(0,0), 0, 0 ); + crossCorr( img2, mask, result, result.size(), result.type(), Point(0,0), 0, 0 ); + + result -= corr * 2; + result += templSum2; + } + else if (method == CV_TM_CCORR_NORMED) + { + if (templSum2 < DBL_EPSILON) + { + result = Scalar::all(1); + return; + } + + Mat corr(corrSize, CV_32F); + crossCorr( img2, mask2, corr, corr.size(), corr.type(), Point(0,0), 0, 0 ); + crossCorr( img, mask_templ, result, result.size(), result.type(), Point(0,0), 0, 0 ); + + sqrt(corr, corr); + result = result.mul(1/corr); + result /= std::sqrt(templSum2); + } + else + CV_Error(Error::StsNotImplemented, ""); +} } //////////////////////////////////////////////////////////////////////////////////////////////////////// -void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, int method ) +void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, int method, InputArray _mask ) { + if (!_mask.empty()) + { + cv::matchTemplateMask(_img, _templ, _result, method, _mask); + return; + } + int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); CV_Assert( CV_TM_SQDIFF <= method && method <= CV_TM_CCOEFF_NORMED ); CV_Assert( (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2 ); diff --git a/samples/cpp/mask_tmpl.cpp b/samples/cpp/mask_tmpl.cpp new file mode 100644 index 0000000000..2b6bb77bf1 --- /dev/null +++ b/samples/cpp/mask_tmpl.cpp @@ -0,0 +1,72 @@ +#include "opencv2/imgproc.hpp" +#include "opencv2/highgui.hpp" + +#include +#include +#include +#include + +using namespace std; +using namespace cv; + +static void help() +{ + cout << "\nThis program demonstrates template match with mask.\n" + "Usage:\n" + "./mask_tmpl , Default is ../data/lena_tmpl.jpg\n" + << endl; +} + +int main( int argc, const char** argv ) +{ + const char* filename = argc == 4 ? argv[1] : "../data/lena_tmpl.jpg"; + const char* tmplname = argc == 4 ? argv[2] : "../data/tmpl.png"; + const char* maskname = argc == 4 ? argv[3] : "../data/mask.png"; + + Mat img = imread(filename); + Mat tmpl = imread(tmplname); + Mat mask = imread(maskname); + Mat res; + + if(img.empty()) + { + help(); + cout << "can not open " << filename << endl; + return -1; + } + + if(tmpl.empty()) + { + help(); + cout << "can not open " << tmplname << endl; + return -1; + } + + if(mask.empty()) + { + help(); + cout << "can not open " << maskname << endl; + return -1; + } + + //int method = CV_TM_SQDIFF; + int method = CV_TM_CCORR_NORMED; + matchTemplate(img, tmpl, res, method, mask); + + double minVal, maxVal; + Point minLoc, maxLoc; + Rect rect; + minMaxLoc(res, &minVal, &maxVal, &minLoc, &maxLoc); + + if(method == CV_TM_SQDIFF || method == CV_TM_SQDIFF_NORMED) + rect = Rect(minLoc, tmpl.size()); + else + rect = Rect(maxLoc, tmpl.size()); + + rectangle(img, rect, Scalar(0, 255, 0), 2); + + imshow("detected template", img); + waitKey(); + + return 0; +} diff --git a/samples/data/lena_tmpl.jpg b/samples/data/lena_tmpl.jpg new file mode 100644 index 0000000000..0c9fc20de8 Binary files /dev/null and b/samples/data/lena_tmpl.jpg differ diff --git a/samples/data/mask.png b/samples/data/mask.png new file mode 100644 index 0000000000..0666232d49 Binary files /dev/null and b/samples/data/mask.png differ diff --git a/samples/data/tmpl.png b/samples/data/tmpl.png new file mode 100644 index 0000000000..999ac704cc Binary files /dev/null and b/samples/data/tmpl.png differ