http://www.cnblogs.com/zhaoweiwei/p/OpenVC_matchTemplate.html
1 理論介紹模板匹配是在一幅圖像中尋找一個特定目標的方法之一,這種方法的原理非常簡單,遍歷圖像中的每一個可能的位置,比較各處與模板是否“相似”,當相似度足夠高時,就認為找到了我們的目標。OpenCV提供了6種模板匹配算法:
- 平方差匹配法CV_TM_SQDIFF
- 歸一化平方差匹配法CV_TM_SQDIFF_NORMED
- 相關匹配法CV_TM_CCORR
- 歸一化相關匹配法CV_TM_CCORR_NORMED
- 相關系數匹配法CV_TM_CCOEFF
- 歸一化相關系數匹配法CV_TM_CCOEFF_NORMED
用T表示模板圖像,I表示待匹配圖像,切模板圖像的寬為w高為h,用R表示匹配結果,匹配過程如下圖所示:

上述6中匹配方法可用以下公式進行描述:

2 示例代碼
下面給出方法6的python代碼
歸一化相關系數匹配法代碼58行中的N就是公式(6)中的w*h,由于python代碼運行速度比較慢,代碼的58、59行相當于對公式(6)的分子分母都進行了平方操作,并且分子分母都乘以了N方,以減小計算量,所以代碼61行的ret相當于公式(6)中的R(x,y)的平方,
為了更快的進行算法驗證,用上述代碼進行驗證時請盡量選用較小的匹配圖像及模板圖像,下圖顯示了我的匹配結果(待匹配圖像295x184模板69x46用了十幾分鐘):

3 OpenCV源碼
較新版本的OpenCV庫中的模板匹配已經進行了較多的算法改進,直接看新版本中的算法需要了解很多相關理論知識,所以我們結合OpenCV0.9.5的源碼進行講解,該版本的源碼基本上是C風格代碼更容易進行理解(如果要對
OpenCV源碼進行研究,建議用該版本進行入門),仍以歸一化相關系數匹配法為例進行分析。

1 /* 2 * pImage: 待匹配圖像 3 * image: 待匹配圖像寬(width*depth并已4字節對齊) 4 * roiSize: 待匹配圖像尺寸 5 * pTemplate: 模板圖像 6 * templStep: 模板圖像寬 7 * templSize: 模板圖像尺寸 8 * pResult: 匹配結果 9 * resultStep: 匹配結果寬 10 * pBuffer: 中間結果數據緩存 11 */ 12 IPCVAPI_IMPL( CvStatus, icvMatchTemplate_CoeffNormed_32f_C1R, 13 (const float *pImage, int imageStep, CvSize roiSize, 14 const float *pTemplate, int templStep, CvSize templSize, 15 float *pResult, int resultStep, void *pBuffer) ) 16 { 17 float *imgBuf = 0; // 待匹配圖像相關數據 18 float *templBuf = 0; // 模板圖像數據 19 double *sumBuf = 0; // 待匹配圖像遍歷塊單行和 20 double *sqsumBuf = 0; // 待匹配圖像遍歷塊單行平方和 21 double *resNum = 0; // 模板圖像和待匹配圖像遍歷塊內積 22 double *resDenom = 0; // 待匹配圖像遍歷塊累加和及待匹配圖像遍歷塊平方累加和 23 double templCoeff = 0; // 模板圖像均分差倒數 24 double templSum = 0; // 模板圖像累加和 25 26 int winLen = templSize.width * templSize.height; 27 double winCoeff = 1. / (winLen + DBL_EPSILON); // + DBL_EPSILON 加一個小整數防止分母為零 28 29 CvSize resultSize = cvSize( roiSize.width - templSize.width + 1, 30 roiSize.height - templSize.height + 1 ); 31 int x, y; 32 33 // 計算并為imgBuf、templBuf、sumBuf、sqsumBuf、resNum、resDenom分配存儲空間 34 CvStatus result = icvMatchTemplateEntry( pImage, imageStep, roiSize, 35 pTemplate, templStep, templSize, 36 pResult, resultStep, pBuffer, 37 cv32f, 1, 1, 38 (void **) &imgBuf, (void **) &templBuf, 39 (void **) &sumBuf, (void **) &sqsumBuf, 40 (void **) &resNum, (void **) &resDenom ); 41 42 if( result != CV_OK ) 43 return result; 44 45 imageStep /= sizeof_float; 46 templStep /= sizeof_float; 47 resultStep /= sizeof_float; 48 49 /* calc common statistics for template and image */ 50 { 51 const float *rowPtr = (const float *) imgBuf; 52 double templSqsum = icvCrossCorr_32f_C1( templBuf, templBuf, winLen ); // 模板圖像平方累加和 53 54 templSum = icvSumPixels_32f_C1( templBuf, winLen ); // 模板圖像累加和 55 templCoeff = (double) templSqsum - ((double) templSum) * templSum * winCoeff; // 模板圖像均方差的平方 56 templCoeff = icvInvSqrt64d( fabs( templCoeff ) + FLT_EPSILON ); // 模板圖像均方差倒數 57 58 for( y = 0; y < roiSize.height; y++, rowPtr += templSize.width ) 59 { 60 sumBuf[y] = icvSumPixels_32f_C1( rowPtr, templSize.width ); // 待匹配圖像按模板圖像寬度求每行之和(遍歷位置第一列) 61 sqsumBuf[y] = icvCrossCorr_32f_C1( rowPtr, rowPtr, templSize.width ); // 待匹配圖像按模板圖像寬度求每行平方之和(遍歷位置第一列) 62 } 63 } 64 65 /* main loop - through x coordinate of the result */ 66 for( x = 0; x < resultSize.width; x++ ) 67 { 68 double sum = 0; 69 double sqsum = 0; 70 float *imgPtr = imgBuf + x; // 待匹配圖像起始位置 71 72 /* update sums and image band buffer */ // 如果不是第1列需重新更新sumBuf,更新后sumBuf為遍歷位置第x列每行之和(行寬為模板圖像寬) 73 if( x > 0 ) 74 { 75 const float *src = pImage + x + templSize.width - 1; 76 float *dst = imgPtr - 1; 77 float out_val = dst[0]; 78 79 dst += templSize.width; 80 81 for( y = 0; y < roiSize.height; y++, src += imageStep, dst += templSize.width ) 82 { 83 float in_val = src[0]; 84 85 sumBuf[y] += in_val - out_val; 86 sqsumBuf[y] += (in_val - out_val) * (in_val + out_val); 87 out_val = dst[0]; 88 dst[0] = (float) in_val; 89 } 90 } 91 92 for( y = 0; y < templSize.height; y++ ) // 求遍歷位置第x列,第1行處遍歷塊累加和sum及平方累加和sqsum 93 { 94 sum += sumBuf[y]; 95 sqsum += sqsumBuf[y]; 96 } 97 98 for( y = 0; y < resultSize.height; y++, imgPtr += templSize.width ) 99 { 100 double res = icvCrossCorr_32f_C1( imgPtr, templBuf, winLen ); // 求模板圖像和待匹配圖像y行x列處遍歷塊的內積 101 102 if( y > 0 ) // 如果不是第1行需更新遍歷塊累加和sum及平方累加和sqsum 103 { 104 sum -= sumBuf[y - 1]; 105 sum += sumBuf[y + templSize.height - 1]; 106 sqsum -= sqsumBuf[y - 1]; 107 sqsum += sqsumBuf[y + templSize.height - 1]; 108 } 109 resNum[y] = res; 110 resDenom[y] = sum; 111 resDenom[y + resultSize.height] = sqsum; 112 } 113 114 for( y = 0; y < resultSize.height; y++ ) 115 { 116 double sum = ((double) resDenom[y]); 117 double wsum = winCoeff * sum; 118 double res = ((double) resNum[y]) - wsum * templSum; 119 double nrm_s = ((double) resDenom[y + resultSize.height]) - wsum * sum; 120 121 res *= templCoeff * icvInvSqrt64d( fabs( nrm_s ) + FLT_EPSILON ); 122 pResult[x + y * resultStep] = (float) res; 123 } 124 } 125 126 return CV_OK; 127 }

以上代碼是歸一化相關系數法核心函數icvMatchTemplate_CoeffNormed_32f_C1R的源碼,我已經在源碼中進行了詳細的注釋,讀者需自己再進行理解,需要進一步說明的是:
代碼118行res就是計算公式(6)的分子部分,代碼56行templCoeff就是計算公式(6)分母的左半部分,代碼121行icvInvSqrt64d函數就是在計算公式(6)分母的右半部分,該行res的最終結果正是公式(6)中的R(x,y)。
4 結束語
OpenCV0.9.5源碼下載:http://download.csdn.net/detail/weiwei22844/9547820
參考文章:http://blog.sina.com.cn/s/blog_4ae371970101aejw.html
http://blog.csdn.net/liyuanbhu/article/details/49837661