特征检测与匹配
这个技术在物体检测,视觉跟踪和三维重建等领域都有应用。
“FAST”—FastFeatureDetector
“STAR”—StarFeatureDetector
“SIFT”—SIFT(nonfree module)
“SURF”—SURF(nonfree module)
“ORB”—ORB
“MSER”—MSER
“GFTT”—GoodFeaturesToTrackDetector
“HARRIS”—GoodFeaturesToTrackDetector(配合Harris detector操作)
“Dense”—DenseFeatureDetector
“SimpleBlob”—SimpleBlobDetector

SURF特征点检测
这个是SIFT算法的加速版,比它快好几倍,并且有更好的稳定性。最大特征是采用了harr特征,以及积分图像概念
1.构建Hessian矩阵构造高斯金字塔尺度空间
Hessian矩阵时Surf算法的核心,通过这个矩阵来进行计算,从而利用判定结果的符号将所有点分类,根据判别式取值正负,来判别这个点是或者不是极值点,先进行高斯平滑,再求二阶导数
2.利用非极大值抑制初步确定特征点
也就是将确定下来的点进行比较,如果这个点是周围这些点中的最大值或者最小值,就保留下来,当做初步的特征点。
3.精确定位极值点
也就是采用三维线性插值法得到亚像素级的特征点,同时也去掉那些值小于一定阈值的点,增加极值使检测到的特征点数量减少,最后就会只有几个特征最强点被检测出来。
4.选取特征点的主方向
通过统计特征点领域内的harr小波特征,也就是在一个领域内,统计60度扇形内所有点的水平和垂直特征总和。
5.构造sur特征点描述算子
surf的描述子相对来说,方向比较少
surFeatureDetecte 和surfDescriptorExtractor等价于surf类,而他继承自Fealture2D类。

绘制关键点的drawKeypoints()函数:
参数分别是输入图像,输出参数和输出图像,再是关键点的颜色,默认(-1)(-1也就是随机的),最后是flags,用来绘制关键点的特征标识符。(默认是0)

再是一个KeyPoint的类,首先是Point2f类型的坐标,再是特征点领域直径和特征点方向,float类型的回应值,再是特征点所在的图像金字塔的组,和用于聚类的id

#include "opencv2/features2d/features2d.hpp"
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>

using namespace cv;
using namespace std;



int main()
{
	//先改变一下颜色
	system("color 2F");
	
	//先载入原图片并显示
	Mat srcImage1 = imread("1.jpg", 1);
	Mat srcImage2 = imread("2.jpg", 1);
	if (!srcImage1.data || !srcImage2.data)
	{
		printf("读取图片错误,请确定目录下是否有指定图片\n");
		return false;
	}
	imshow("原始图1", srcImage1);
	imshow("原始图2", srcImage2);
	//定义需要用的变量和类
	int  minHessian = 400;//定义SURF中的hessian阈值特征点检测算子
	//SurfFeatureDetector detector(minHessian);//顶一个一个特征检测类对象
	//std::vector<KeyPoint>keypoints_1, keypoints_2;//vector模板类是能够存放任意类型的动态数组
	//再是调用detect函数检测数SURF特征关键点,保存在vector容器中
	//detector.delect(srcImage1, keypoints_1);
	//detecor.delect(srcImage2, keypoints_2);
	//但是nonfree模块丢失用不了接口,所以上面的都变成备注

	vector<KeyPoint> keypoints_1, keypoints_2;
	Ptr<FeatureDetector>dector = FastFeatureDetector::create(minHessian);
	dector->detect(srcImage1, keypoints_1);
	dector->detect(srcImage2, keypoints_2);

	//绘制特征关键点
	Mat img_keypoints_1; Mat img_keypoints_2;
	drawKeypoints(srcImage1, keypoints_1, img_keypoints_1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
	drawKeypoints(srcImage2, keypoints_2, img_keypoints_2, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
	
	//显示效果图
	imshow("特征点检测效果图1",img_keypoints_1);
	imshow("特征点检测效果图2", img_keypoints_2);
	waitKey(0);
	return 0;

}

SURF特征提取
主要是用drawMatches方法和BruteForceMatcher类来进行运用的。
drawMatches()函数:顾名思义:绘制出相匹配的两个图像的关键点,参数差不多就是,第一幅原图和根据第一幅原图得到的特征点,再是第二幅原图和第二幅原图得到的特征点。第一幅图到第二幅图的匹配点,也就是每一个图一的特征点都在图二中有一一对应的点和输出图像再是匹配的输出颜色,也就是线和关键点的颜色,默认值是(-1),也就是随机生成的。再是单一特征点的颜色,再确定哪些匹配是会绘制出来的掩膜,最后是int类型的flags,也就是特征绘制的标识符。
*BruteForceMatcher类:*是用来进行暴力匹配相关的操作

写一个示例程序;用SurfDescriptorExtractor类进行特征向量的相关计算。核心思想是:

1.使用DescriptorExtractor接口来寻找关键点对应的特征向量
2.使用SurfDescriptorExtractor以及他的函数compute来完成特定的计算
3.使用BruteForceMatcher来匹配特征向量
4.使用函数drawMatches来绘制检测到的匹配点

#include"opencv2/core/core.hpp"
#include"opencv2/features2d/features2d.hpp"
#include"opencv2/highgui/highgui.hpp"
#include<opencv2/features2d/nonfree.hpp>//sift
#include<opencv2/imgproc/imgproc.hpp>
#include <opencv2/ittnotify.h>//听说是放弃使用legacy模块了
#include<iostream>



using namespace cv;
using namespace std;
using namespace cv::xfeatures2d;//这个对命名很重要



int main()
{
	Mat srcImage1 = imread("1.jpg", 1);
	Mat srcImage2 = imread("2.jpg", 1);
	if (!srcImage1.data || !srcImage2.data)
	{
		printf("读取图片错误,请确定目录下是否有IMread函数指定的图片存在\n");
		return false;
	}
	//使用SURF算子检测关键点
	int minHessian = 700; //SURF算法中的hessian阈值
	//SurfFeatureDetector detector(minHessian);//定义一个
	//前面的版本过老,需要用新版的代码来敲
	Ptr<SURF> detector = SURF::create(minHessian);//定义一个检测类
	vector<KeyPoint>keypoints_object, keypoints_scene;//vector模板类,存放任意类型的动态模板
	//再调用detect函数来检测出SURF特征关键点,保存在vector容器
	detector->detect(srcImage1, keypoints_object);
	detector->detect(srcImage2, keypoints_scene);
	//计算描述符(特征向量)
	Ptr<SURF>EXTRACTOR = SURF::create();

	Mat descriptors_object, descriptors_scene;
	extractor->compute(srcImage1, keypoints_object, descriptors_object);
	extractor->compute(srcImage2, keypoints_scene, descriptors_scene);
	//使用FLANN匹配算子进行匹配
	FlannBasedMatcher matcher;
	vector<DMatch> matches;
	matcher.match(descriptors_object, descriptors_scene, matches);
	double max_dist = 0; double min_dist = 100;//最小距离和最大距离

	//快速计算关键点之间距离最大值和最小值
	for (int i = 0; i < descriptors_object.rows; i++)
	{
		double dist = matches[i].distance;
		if (dist < min_dist)min_dist = dist;
		if (dist > max_dist)max_dist = dist;
	}
	printf(">Max dist 最大距离:%f\n", max_dist);
	printf(">Min dist 最小距离:%f\n", min_dist);
	//存下匹配距离小于3*min_dist的点对
	std::vector<DMatch>good_matches;
	for (int i = 0; i < descriptors_object.rows; i++)
	{
		if (matches[i].distance < 3 * min_dist)
		{
			good_matches.push_back(matches[i]);
		}
	}
	//绘制出匹配到的关键点
	Mat img_matches;
	drawMatches(srcImage1, keypoints_object, srcImage2, keypoints_scene, good_matches, img_matches, Scalar:; all(-1), Scalar::all(-1),vector<char>(),DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
	//定义两个局部变量
	vector<Point2f> obj;
	vector<Point2f> scene;
	//从匹配成功的匹配对中获取关键点
	for (unsigned int i = 0; i < good_matches.size(); i++)
	{
		obj.push_back(keypoints_object[good_matches[i].queryIdx].pt);
		scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);
	}
	vector<unsigned char>listpoints;
	//计算透视变换
	Mat H = findHomography(obj, scene, RANSAC, 3, listpoints);

std:; vector<DMatch>goodgood_matches;
	for (int i = 0; i < listpoints.size(); i++)
	{
		if ((int)listpoints[i])
		{
			goodgood_matches.push_back(good_matches[i]);
			cout << (int)listpoints[i] < , endl;
		}
	}
	cout << "listpoints大小:" << listpoints.size() << endl;
	cout << "goodgood_matches大小:" << goodgood_matches.size() << endl;
	cout << "good_matches大小:" << good_matches.size() << endl;
	Mat Homgimg_matches;
	drawMatches(copysrcImage1, keypoints_object, copysrcImage2, keypoints_scene, goodgood_matches, Homgimg_matches, Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
	//输出相关匹配点信息
	for(int i=0;i<good_matches.size();i++)
	{
		printf(">符合条件的匹配点[%d]特征点1:%d--特征点2:%d\n", i, good_matches[i].queryIdx, good_matches[i].trainIdx);
}
	imshow("匹配效果图", img_matches);
	//按任意键退出程序
	waitKey(0);
	return 0;


}

SURF与SIFT算法的比较:
1.尺度空间:SIFT是DOG与不同尺度的图片卷积,SURF是不同尺度的box filters与原图片卷积
2.特征点检测:SIFT是先进非极大抑制,再去除低对比度的点,再通过Hessian矩阵去除边缘点 SURF是先利用Hessian矩阵确定候选点,然后进行非极大抑制。
3.方向:再正方形区域内统计梯度的幅值的直方图,找max对应的方向,可以有多个方向。
4.特征描述子:1616的采样点划分为44的区域,计算每个区域的采样点的梯度方向和幅值,统计成8bit直方图,一共448=128维
4.2020s的区域划分为44的子区域,每个区域找5*5个采样点,计算采样点的haar小波响应。