#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
//读取图片
Mat img_1 = imread("1.png");
Mat img_2 = imread("2.png");
if (img_1.empty() || img_2.empty())
{
cout << "Can't open the picture!\n";
return 0;
}
vector<KeyPoint> img_1_keypoints, img_2_keypoints;
Mat img_1_descriptors, img_2_descriptors;
Ptr<ORB> detector=ORB::create(); //采用ORB算法提取特征点
//检测FAST角点位置
detector->detect(img_1, img_1_keypoints);
detector->detect(img_2, img_2_keypoints);
//根据角点位置计算BRIEF描述子
detector->compute(img_1, img_1_keypoints, img_1_descriptors);
detector->compute(img_2, img_2_keypoints, img_2_descriptors);
//汉明距离做为相似度度量
BFMatcher matcher(NORM_HAMMING, true);
vector<DMatch> matches; //注意这里的类型为DMatch
matcher.match(img_1_descriptors, img_2_descriptors, matches);
Mat match_img;
drawMatches(img_1, img_1_keypoints, img_2, img_2_keypoints, matches, match_img);
imshow("所有匹配", match_img);
//采用RANSAC算法优化
//保存匹配对序号
vector<int> queryIdxs(matches.size()), trainIdxs(matches.size());
for (size_t i = 0; i < matches.size(); i++)
{
queryIdxs[i] = matches[i].queryIdx;
trainIdxs[i] = matches[i].trainIdx;
}
Mat homography_matrix; //变换矩阵
vector<Point2f> points1; KeyPoint::convert(img_1_keypoints, points1, queryIdxs);
vector<Point2f> points2; KeyPoint::convert(img_2_keypoints, points2, trainIdxs);
int ransacReprojThreshold = 5; //拒绝阈值
homography_matrix = findHomography(Mat(points1), Mat(points2), CV_RANSAC, ransacReprojThreshold);
vector<char> matchesMask(matches.size(), 0);
Mat points1t;
perspectiveTransform(Mat(points1), points1t, homography_matrix);
for (size_t i = 0; i < points1.size(); i++) //保存‘局内点’
{
if (norm(points2[i] - points1t.at<Point2f>((int)i, 0)) <= ransacReprojThreshold) //给局内点做标记
{
matchesMask[i] = 1;
}
}
Mat match_img2; //滤除‘局外点’后
drawMatches(img_1, img_1_keypoints, img_2, img_2_keypoints, matches, match_img2, Scalar(0, 0, 255), Scalar::all(-1), matchesMask);
imshow("优化后的匹配", match_img2);
waitKey(0);
return 0;
}
随机抽样一致算法(RANdom SAmple Consensus),简称RANSAC算法 ,采用迭代的方式从一组包含离群的被观测数据中估算出数学模型的参数。其广泛应用在计算机视觉领域和数学领域,例如直线拟合、平面拟合、计算图像或点云间的变换矩阵、计算基础矩阵等方面。
RANSAC算法基本思想:从数据集中随机选出一组局内点(其数目要保证能够求解出模型的所有参数),计算出一套模型参数。用得到的模型去测试其他所有的数据点,如果某点的误差在设定的误差阈值之内,就判定其为局内点,否则为局外点,只保留目前为止局内点数目最多的模型,将其记录为最佳模型。重复执行1,2步足够的次数(即达到预设的迭代次数)后,使用最佳模型对应的局内点来最终求解模型参数。最后可以通过估计局内点与模型的错误率来评估模型。
对于上述代码中出现的一些函数进行记录。
一、C++ OpenCV特征提取之BFMatcher匹配
Brute Force匹配是opencv二维特征点匹配常见的办法,BFMatcher总是尝试所有可能的匹配,从而使得它总能够找到最佳匹配,这也是Brute Force(暴力法)的原始含义。
二、OpenCV的 drawMatches() function:
void drawMatches(Mat img1, vector<KeyPoint> keypoints1,
Mat img2, vector<KeyPoint> keypoints2,
vector<DMatch> matches,
Mat outImg) //want keypoints1[i] = keypoints2[matches[i]]
注意匹配是类型向量< DMatch> 。以下是 DMatch 构造函数:
DMatch(int queryIdx,int trainIdx,float distance)
如果您按顺序调用match函数:
match (descriptor_for_keypoints1,descriptor_for_keypoints2,matches)
则 queryIdx 指 keypoints1 和 trainIdx 指 keypoints2
三、keypiont::convert
opencv中对角点检测时需要将vector<KeyPoint>与vector<point2f>之间进行转换
这个在opencv版本里面自带了相关的转换函数
1、KeyPoint 转point2f
CV_WRAP static void convert(const std::vector<KeyPoint>& keypoints,
CV_OUT std::vector<Point2f>& points2f,
const std::vector<int>& keypointIndexes=std::vector<int>());
2、point2f 转KeyPoint
CV_WRAP static void convert(const std::vector<Point2f>& points2f,
CV_OUT std::vector<KeyPoint>& keypoints,
float size=1, float response=1, int octave=0, int class_id=-1);
四、findHomography
Mat cv::findHomography ( InputArray srcPoints,
InputArray dstPoints,
int method = 0,
double ransacReprojThreshold = 3,
OutputArray mask = noArray(),
const int maxIters = 2000,
const double confidence = 0.995
)
//srcPoints 源平面中点的坐标矩阵,可以是CV_32FC2类型,也可以是vector<Point2f>类型
//dstPoints 目标平面中点的坐标矩阵,可以是CV_32FC2类型,也可以是vector<Point2f>类型
/*method 计算单应矩阵所使用的方法。不同的方法对应不同的参数,具体如下:
0 - 利用所有点的常规方法
RANSAC - RANSAC-基于RANSAC的鲁棒算法
LMEDS - 最小中值鲁棒算法
RHO - PROSAC-基于PROSAC的鲁棒算法*/
/*ransacReprojThreshold
将点对视为内点的最大允许重投影错误阈值(仅用于RANSAC和RHO方法)。如果
则点被认为是个外点(即错误匹配点对)。若srcPoints和dstPoints是以像素为单位的,则该参数通常设置在1到10的范围内。*/
/*mask
可选输出掩码矩阵,通常由鲁棒算法(RANSAC或LMEDS)设置。 请注意,输入掩码矩阵是不需要设置的。
maxIters RANSAC 算法的最大迭代次数,默认值为2000。
confidence 可信度值,取值范围为0到1.*/
五、透视变换
void perspectiveTransform(InputArray src, OutputArray dst, InputArray m)
// 坐标、坐标、变换矩阵
注意这里src和dst的输入并不是图像,而是图像对应的坐标。