前面通过图像直方图反向投影的方式在图像中寻找模版图像,由于直方图不能直接反映图像的纹理,因此,如果两幅不同的模版图像具有相同的直方图分布特性,那么在同一幅图中对着两幅模版图像的直方图进行反向投影,最终结果将不具有参考意义。
在图像中寻找模版图像时,可以直接通过比较图像像素的形式来搜索是否存在相同的内容,这种通过比较像素灰度值来寻找相同内容的方法称作图像的模版匹配。
OpenCV中提供了用于图像模版匹配的函数matchTemplate能够实现模版匹配过程中图像与模版相似性的计算:
void matchTemplate(
InputArray image, // 原图 CV_8U或者CV_32F
InputArray templ, // 模板图
OutputArray result, // 输出图像 CV_32F
int method, // 模板匹配方法标志
InputArray mask = noArray() // 匹配模板的掩码,通常情况下不使用,仅在 TM_SQDIFF和 TM_CCORR_NOrMED两种匹配方法中使用
);
第三个参数为相似性矩阵,滑动窗口与模板的相似性系数存放在滑动窗口左上角第一个像素出,因此输出的相似性矩阵尺寸小于原始图像的尺寸,如果原始图像尺寸为 W × H W \times H W×H ,模板图像尺寸为 w × h w \times h w×h ,那么相似性矩阵尺寸为 ( W − w + 1 ) × ( H − h + 1 ) (W-w+1) \times (H-h+1) (W−w+1)×(H−h+1) 。此外,无论输入的是彩色图像还是灰度图像,函数输出结果都是单通道矩阵。
第4个参数是滑动窗口与模板匹配方法标志也称作相似性系数。
enum TemplateMatchModes {
// 平方差匹配法
TM_SQDIFF = 0,
// 归一化平方差匹配法
TM_SQDIFF_NORMED = 1,
// 相关匹配法
TM_CCORR = 2,
// 归一化相关匹配法
TM_CCORR_NORMED = 3,
// 系数匹配法
TM_CCOEFF = 4,
// 归一化相关系数匹配法
TM_CCOEFF_NORMED = 5
};
相似性系数公式:method公式计算方法
下面公式中, T T T 表示模版图像, I I I 表示原始图像。
0.TM_SQDIFF:两者匹配程度越低,计算数值越大。
R ( x , y ) = ∑ x ′ , y ′ ( T ( x ′ , y ′ ) − I ( x + x ′ , y + y ′ ) ) 2 R ( x , y ) = ∑ x ′ , y ′ ( ( T ( x ′ , y ′ ) − I ( x + x ′ , y + y ′ ) ) ⋅ M ( x ′ , y ′ ) ) 2 R(x,y) = \sum_{x',y'}(T(x', y') - I(x+x', y+y'))^2 \\ R(x,y) = \sum_{x',y'}((T(x', y') - I(x+x', y+y')) \cdot M(x',y'))^2 \\ R(x,y)=x′,y′∑(T(x′,y′)−I(x+x′,y+y′))2R(x,y)=x′,y′∑((T(x′,y′)−I(x+x′,y+y′))⋅M(x′,y′))2
1.TM_SQDIFF_NORMED:两者匹配程度越低,计算数值越大。
R ( x , y ) = ∑ x ′ , y ′ ( T ( x ′ , y ′ ) − I ( x + x ′ , y + y ′ ) ) 2 ∑ x ′ , y ′ T ( x ′ , y ′ ) 2 ⋅ ∑ x ′ , y ′ I ( x + x ′ , y + y ′ ) 2 R ( x , y ) = ∑ x ′ , y ′ ( ( T ( x ′ , y ′ ) − I ( x + x ′ , y + y ′ ) ) ⋅ M ( x ′ , y ′ ) ) 2 ∑ x ′ , y ′ ( T ( x ′ , y ′ ) ⋅ M ( x ′ , y ′ ) ) 2 ⋅ ∑ x ′ , y ′ ( I ( x + x ′ , y + y ′ ) ⋅ M ( x ′ , y ′ ) ) 2 R(x,y) = \frac{\sum_{x',y'}(T(x', y') - I(x+x', y+y'))^2}{\sqrt{\sum_{x',y'}T(x',y')^2 · \sum_{x',y'}I(x+x',y+y')^2}} \\ R(x,y) = \frac{\sum_{x',y'}((T(x', y') - I(x+x', y+y')) \cdot M(x',y'))^2}{\sqrt{\sum_{x',y'}(T(x',y') \cdot M(x',y'))^2 · \sum_{x',y'}(I(x+x',y+y') \cdot M(x',y'))^2}} \\ R(x,y)=∑x′,y′T(x′,y′)2⋅∑x′,y′I(x+x′,y+y′)2∑x′,y′(T(x′,y′)−I(x+x′,y+y′))2R(x,y)=∑x′,y′(T(x′,y′)⋅M(x′,y′))2⋅∑x′,y′(I(x+x′,y+y′)⋅M(x′,y′))2∑x′,y′((T(x′,y′)−I(x+x′,y+y′))⋅M(x′,y′))2
2.TM_CCORR:数值越大匹配效果越好,0表示最坏匹配结果。
R ( x , y ) = ∑ x ′ , y ′ ( T ( x ′ , y ′ ) ⋅ I ( x + x ′ , y + y ′ ) ) R ( x , y ) = ∑ x ′ , y ′ ( ( T ( x ′ , y ′ ) ⋅ I ( x + x ′ , y + y ′ ) ) ⋅ M ( x ′ , y ′ ) 2 ) R(x,y) = \sum_{x',y'}(T(x', y') \cdot I(x+x', y+y')) \\ R(x,y) = \sum_{x',y'}((T(x', y') \cdot I(x+x', y+y')) \cdot M(x',y')^2) \\ R(x,y)=x′,y′∑(T(x′,y′)⋅I(x+x′,y+y′))R(x,y)=x′,y′∑((T(x′,y′)⋅I(x+x′,y+y′))⋅M(x′,y′)2)
3.TM_CCORR_NORMED:完全匹配时结果为1,完全不匹配结果为0
R ( x , y ) = ∑ x ′ , y ′ ( T ( x ′ , y ′ ) ⋅ I ( x + x ′ , y + y ′ ) ) ∑ x ′ , y ′ T ( x ′ , y ′ ) 2 ⋅ ∑ x ′ , y ′ I ( x + x ′ , y + y ′ ) 2 R ( x , y ) = ∑ x ′ , y ′ ( T ( x ′ , y ′ ) ⋅ I ( x + x ′ , y + y ′ ) ⋅ M ( x ′ , y ′ ) 2 ) ∑ x ′ , y ′ ( T ( x ′ , y ′ ) ⋅ M ( x ′ , y ′ ) ) 2 ⋅ ∑ x ′ , y ′ ( I ( x + x ′ , y + y ′ ) ⋅ M ( x ′ , y ′ ) ) 2 R(x,y) = \frac{\sum_{x',y'}(T(x', y') \cdot I(x+x', y+y'))}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'}I(x+x',y+y')^2}} \\ R(x,y) = \frac{\sum_{x',y'}(T(x', y') \cdot I(x+x', y+y') \cdot M(x',y')^2)}{\sqrt{\sum_{x',y'}(T(x',y') \cdot M(x',y'))^2 \cdot \sum_{x',y'}(I(x+x',y+y') \cdot M(x',y'))^2}} \\ R(x,y)=∑x′,y′T(x′,y′)2⋅∑x′,y′I(x+x′,y+y′)2∑x′,y′(T(x′,y′)⋅I(x+x′,y+y′))R(x,y)=∑x′,y′(T(x′,y′)⋅M(x′,y′))2⋅∑x′,y′(I(x+x′,y+y′)⋅M(x′,y′))2∑x′,y′(T(x′,y′)⋅I(x+x′,y+y′)⋅M(x′,y′)2)
4.TM_CCOEFF:这种方法采用相关匹配法对模版减去均值的结果和原始图像减去均值的结果进行匹配,可以很好地解决模版图像和原始图像之间由于亮度不同而产生的影响。匹配程度越高数值越大,匹配程度越低数值越小,结果可以为负值。
R ( x , y ) = ∑ x ′ , y ′ ( T ′ ( x ′ , y ′ ) ⋅ I ′ ( x + x ′ , y + y ′ ) ) 其中 { T ′ ( x ′ , y ′ ) = T ( x ′ , y ′ ) − 1 w ⋅ h ∑ x ′ ′ , y ′ ′ T ( x ′ ′ , y ′ ′ ) I ′ ( x ′ , y ′ ) = I ( x + x ′ , y + y ′ ) − 1 w ⋅ h ∑ x ′ ′ , y ′ ′ I ( x + x ′ ′ , y + y ′ ′ ) R(x,y) = \sum_{x',y'}(T'(x', y') \cdot I'(x+x', y+y')) \\ 其中\left\{ \begin{array}{ll} T'(x',y') = T(x',y') - \frac{1}{w \cdot h}\sum_{x'',y''}T(x'',y'') \\ I'(x',y') = I(x+x',y+y') - \frac{1}{w \cdot h} \sum_{x'',y''}I(x+x'',y+y'') \\ \end{array} \right. R(x,y)=x′,y′∑(T′(x′,y′)⋅I′(x+x′,y+y′))其中{T′(x′,y′)=T(x′,y′)−w⋅h1∑x′′,y′′T(x′′,y′′)I′(x′,y′)=I(x+x′,y+y′)−w⋅h1∑x′′,y′′I(x+x′′,y+y′′)
5.TM_CCOEFF_NORMED:归一化到1~-1之间,完全匹配数值为1,完全不匹配数值为-1。
R ( x , y ) = ∑ x ′ , y ′ ( T ′ ( x ′ , y ′ ) ⋅ I ′ ( x + x ′ , y + y ′ ) ) ∑ x ′ , y ′ T ′ ( x ′ , y ′ ) 2 ⋅ ∑ x ′ , y ′ I ′ ( x + x ′ , y + y ′ ) 2 R(x,y) = \frac{\sum_{x',y'}(T'(x', y') \cdot I'(x+x', y+y'))}{\sqrt{ \sum_{x',y'}T'(x', y')^2 \cdot \sum_{x',y'}I'(x+x', y+y')^2 }} \\ R(x,y)=∑x′,y′T′(x′,y′)2⋅∑x′,y′I′(x+x′,y+y′)2∑x′,y′(T′(x′,y′)⋅I′(x+x′,y+y′))
示例:
#include
#include
using namespace cv;
using namespace std;
int main()
{
cout << "OpenCV Version: " << CV_VERSION << endl;
utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
Mat img = imread("lena.png");
Mat temp = imread("lena_face.png");
if (img.empty() || temp.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
cout << "img.size(): " << img.size() << endl; // [512 x 512]
cout << "img_template.size(): " << temp.size() << endl; // [164 x 172]
Mat result;
matchTemplate(img, temp, result, TM_CCOEFF_NORMED);//模板匹配
cout << "result.size(): " << result.size() << endl; // [349 x 341]
double maxVal, minVal;
Point minLoc, maxLoc;
//寻找匹配结果中的最大值和最小值以及坐标位置
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);
//绘制最佳匹配区域
rectangle(img, cv::Rect(maxLoc.x, maxLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 2);
imshow("原图", img);
imshow("模板图像", temp);
imshow("result", result);
int k = waitKey(0); // Wait for a keystroke in the window
return 0;
}