前言
- 本文主要介绍opencv中图像匹配函数
- 并用示例解释和演示6中匹配方式
- imshow的说明(为后续部分结果归一化后再显示说明原因)
1,如果原始图片是8位无符号整数,就按照原来的数字进行显示。也就是数字范围是[0,255]
2,如果原始图片是16位无符号整数或者32位整数,就除以256进行显示。也就是说0到256*256的范围被压缩到0到255。
3,如果图片是32位或者64位的浮点类型数据,那么像素值就会乘以255。也就是说,0到1的范围被映射到0到255.
函数讲解
- opencv实现了一部分通过模板与目标图像进行寻找最佳匹配的方面matchTemplat() , 函数声明如下所示
void cv::matchTemplate(
cv::InputArray image, // 用于搜索的输入图像
cv::InputArray teml, // 用于匹配的模板,和image类型相同
cv::OutputArray result, // 匹配结果图像, 类型 32F
int method // 用于比较的方法,opencv提供6种
}
- 函数调用方式为
- source_image是原图
- template_image是选取的需要匹配的特征图
- target_image是存储匹配值的结果图
matchTemplate(source_image, template_image, target_image, TM_CCOEFF_NORMED);//匹配函数调用
double minVal, maxVal; //用于存储匹配值的最大值和最小值
Point minLoc, maxLoc; //用于存储匹配最大值的位置和最小值的位置
//寻找最佳匹配位置
minMaxLoc(target_image, &minVal, &maxVal, &minLoc, &maxLoc);
//画圈标出匹配点,这里使用maxLoc还是minLoc和选择的匹配method有关
circle(source_image, Point(maxLoc.x + template_image.cols / 2, maxLoc.y + template_image.rows / 2), 10, Scalar(0, 0, 255), 2, 8, 0);
我们将尝试用不同方式来匹配图片中的黄色物品
opencv支持的比较方法有6种
TM_SQDIFF
- 基本概念:该方法使用平方差进行匹配,因此最佳的匹配结果在结果为0处,值越大匹配结果越差。
- 该方法使用基本的平方差,就是将要匹配的template图片放在一张图上扫描,最好的匹配结果就是0
- 右图是将匹配矩阵中的值映射到0 ---- 1之间显示的,因为需要使用 imshow 对其进行显示
- 可以看到,左图显示,用该方法能够较好实现匹配。匹配的值越大,图像越亮。从右图中,我们可以看到匹配结果,手中物品匹配结果呈黑色,因此差距很小。
TM_SQDIFF_NORMED
- 基本概念:该方法使用归一化的平方差进行匹配,最佳匹配也在结果为0处。
- 这个公式中可以将 的每个值排列起来看作一个向量,公式可以转化为
- 因为匹配的结果矩阵已经进行了归一化,因此我们可以直接进行显示
- 可以看到,这种方法也能很好实现匹配
- 这种标准化操作可以保证当模板和图像各个像素的亮度都乘上了同一个系数时,相关度不发生变化。也就是说当 和 变为 和 时,
TM_CCORR
- 基本概念:相关性匹配方法,该方法使用源图像与模板图像的卷积结果进行匹配,因此,最佳匹配位置在值最大处,值越小匹配结果越差。
- 这个公式中可以将 的每个值排列起来看作一个向量,公式可以转化为 。我认为这并不能用来代表相似性,因为亮度大的区域,得分自然就高
- 右图是将匹配的结果矩阵归一化的显示
- 由左图看出,该方法匹配并不准确,从公式上看,该匹配方法容易受到亮度的影响
- 原图中,左边比右边更亮。可以看出,匹配结果中,左边的匹配值也比右边大
TM_CCORR_NORMED
- 基本概念:归一化的相关性匹配方法,与相关性匹配方法类似,最佳匹配位置也是在值最大处。
- 这个公式中可以将 的每个值排列起来看作一个向量,公式可以转化为 。这里的公式就是求 与 的
- 结果可知,该方法能够较好得实现图像匹配
- 这个方法和 标准化差值平方和匹配 类似,都是去除了亮度线性变化对相似度计算的影响。可以保证图像和模板同时变亮或变暗k倍时结果不变。
TM_CCOEFF
- 基本概念:相关性系数匹配方法,该方法使用源图像与其均值的差、模板与其均值的差二者之间的相关性进行匹配。
- 由匹配结果图片可以看出,人手中的东西是一个白点,证明匹配效果最好
- 从左图中我们也可以看出,匹配结果较好
TM_CCOEFF_NORMED
- 基本概念:在减去了各自的平均值之外,还要各自除以各自的方差。经过减去平均值和除以方差这么两步操作之后,无论是我们的待检图像还是模板都被标准化了,这样可以保证图像和模板分别改变光照亮不影响计算结果。计算出的相关系数被限制在了 -1 到 1 之间,1 表示完全相同,-1 表示两幅图像的亮度正好相反,0 表示两幅图像之间没有线性关系。
- 也可以看做利用 求相似度
- 由匹配结果图片可以看出,人手中的东西是一个白点,证明匹配效果最好
- 从左图中我们也可以看出,匹配结果较好
对比 TM_SQDIFF 和 TM_CCOEFF_NORMED
对template图片的亮度进行改变
三通道像素值均增加 80
- TM_CCOEFF_NORMED匹配结果
- 可以看到,匹配结果依然正确
- 通过匹配结果也就是右图可以看出,匹配的结果和template图像亮度增加之前无明显变化
- TM_SQDIFF匹配结果
- 可以发现的是,当我们增大template图片的亮度后,最好的匹配结果落在了地上
- 从匹配结果图,也就是右图中我们可以看出,图像中右边的树林匹配结果不好,左边的底面匹配结果都不错
- 这是很容易理解的。因为本放大使用的是平方差,因此当增大匹配图像亮度后,匹配的结果会更倾向于寻找亮度更大的区域,因为这样的区域计算出来的平方差才比较小。
对template图片的大小进行调整(变大)
- TM_CCOEFF_NORMED匹配结果
- 从右图可以看出,匹配的结果较图像大小调整之前有明显变化
- 匹配结果略有下降
- 当匹配图像大小调整后,在匹配过程中,检测框的大小发生了变化,框中的像素发生了变化,因此结果也会发生变化
- TM_SQDIFF匹配结果
- 从右图可以看出,匹配结果较图像大小调整之前有明显变化
- 匹配结果略有下降
- 当匹配图像大小调整后,在匹配过程中,检测框的大小发生了变化,框中的像素发生了变化,因此结果也会发生变化
完整代码如下
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include"opencv2/imgproc/imgproc.hpp"
#include"opencv2/opencv.hpp"
using namespace std;
using namespace cv;
Mat source_image;
Mat template_image;
Mat target_image;
int main()
{
source_image = imread("D:\\cLion\\project\\02.jpg");
template_image = imread("D:\\cLion\\project\\02_temp.jpg");
//调整大小时使用
// for(int i = 0 ; i < template_image.rows ; i++)
// for(int j = 0 ; j < template_image.cols ; j++)
// for(int c = 0 ; c < 3 ; c++)
// template_image.at<Vec3b>(i,j)[c] = saturate_cast<uchar>(template_image.at<Vec3b>(i,j)[c] + 80);
matchTemplate(source_image, template_image, target_image, TM_SQDIFF);
double minVal, maxVal; //存储匹配的最大值和最小值
Point minLoc, maxLoc; //存储匹配最大值的位置和最小值的位置0
//寻找最佳匹配位置
minMaxLoc(target_image, &minVal, &maxVal, &minLoc, &maxLoc);
//Point中使用max或min根据具体方法确定
circle(source_image, Point(minLoc.x + template_image.cols / 2, minLoc.y + template_image.rows / 2), 10, Scalar(0, 0, 255), 2, 8, 0);
normalize(target_image, target_image, 0, 1, NORM_MINMAX);
imshow("Source Window" , source_image);
imshow("Template Window" , template_image);
imshow("Target Window" , target_image);
waitKey(0);
}