文章目录
- 一、FLANN简介
- 演示SURF--Flann
- 二、单应性矩阵
一、FLANN简介
FLANN库全称是Fast Library for Approximate Nearest Neighbors(快速最近邻逼进搜索库),是一个在高维空间快速搜索近邻的库,它是目前最完整的(近似)最近邻开源库。不但实现了一系列查找算法,还包含了一种自动选取最快算法的机制。
主要优势:实现了包含kd-tree等在内的搜索算法集合,并且可以根据数据集本身特点,自动选取最适合的算法来完成k近邻搜索任务(更加确切的说是高维特征向量的匹配任务),提供了c++、c、python、matlab的接口。
FLANN 算法 官网:https://www.cs.ubc.ca/paper/flann FLANN github地址:https://github.com/flann-lib/flann
关于算法详情查看:javascript:void(0) 关于特征存储查看:OpenCV—python 角点特征检测之三(FLANN匹配) 关于构造函数查看:https://www.jianshu.com/p/d70d9c8b2bec
主要介绍一下indexParams的参数
Ptr<flann::IndexParams> indexParams
; 索引参数
Ptr<flann::SearchParams> searchParams
; 搜索参数
Ptr<flann::Index> flannIndex
; 创建索引
头文件 image_feature_all.h
:声明类与公共函数
#pragma once
#include <opencv2/opencv.hpp>
#include <iostream>
#include <opencv2/xfeatures2d.hpp> //新增引入库
using namespace cv;
using namespace std;
class ImageFeature {
public:
void flann_demo(Mat& image, Mat& image2);
};
主函数main.cpp
调用该类的公共成员函数
#include "image_feature_all.h"
int main(int argc, char** argv) {
const char* img_path = "D:\\Desktop\\match_dst.jpg";
const char* img_path2 = "D:\\Desktop\\match_raw.jpg";
Mat image = imread(img_path, IMREAD_GRAYSCALE);
Mat image2 = imread(img_path2, IMREAD_GRAYSCALE);
if (image.empty() || image2.empty()) {
cout << "图像数据为空,读取文件失败!" << endl;
}
ImageFeature imgfeature;
imgfeature.flann_demo(image, image2);
waitKey(0);
destroyAllWindows();
return 0;
}
演示SURF–Flann
源文件 feature_extract.cpp
:实现类与公共函数
void ImageFeature::flann_demo(Mat& image, Mat& image2) {
//SURF 特征提取
int minHessian = 400;
Ptr<xfeatures2d::SURF> detector = xfeatures2d::SURF::create(minHessian);
vector<KeyPoint> keypoint_obj, keypoint_scene;
Mat descriptor_obj, descriptor_scens;
detector->detectAndCompute(image, Mat(), keypoint_obj, descriptor_obj);
detector->detectAndCompute(image2, Mat(), keypoint_scene, descriptor_scens);
//SURF 特征匹配
FlannBasedMatcher matcher;
vector<DMatch> matches;
matcher.match(descriptor_obj, descriptor_scens, matches);
//查找最大最小距离
double minDist = 1000;
double maxDist = 0;
for (size_t i = 0; i < descriptor_obj.rows; i++) {
double dist = matches[i].distance;
if (dist > maxDist) { maxDist = dist; }
if (dist < minDist) { minDist = dist; }
}
cout << "minDist = " << minDist << endl;
cout << "maxDist = " << maxDist << endl;
// 筛选最佳匹配(小于最小距离的3倍)
vector<DMatch> goodMatchs;
for (size_t i = 0; i < descriptor_obj.rows; i++) {
double dist = matches[i].distance;
if (dist < max(3 * minDist, 0.02)) {
goodMatchs.push_back(matches[i]);
}
}
//绘制特征点
Mat matchImg;
drawMatches(
image, keypoint_obj,
image2, keypoint_scene,
goodMatchs, matchImg,
Scalar::all(-1), Scalar::all(-1),
vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS
);
imshow("matchImg", matchImg);
//查看goodMatchs
for (size_t i = 0; i < goodMatchs.size(); i++){
cout << "goodMatchs "<<to_string(i) << "\tKeypoint1, Keypoint2 = " << goodMatchs[i].queryIdx <<", " << goodMatchs[i].trainIdx << endl;
}
}
二、单应性矩阵
从场景图像上得到特征匹配图像的四点坐标:详情请查看
函数介绍:findHomography有三个重载函数,这里只说明一个
Mat
InputArray srcPoints, 特征点集合,一般是来自目标图像
InputArray dstPoints, 特征点集合,一般是来自场景图像
int method = 0, 配准方法,支持有四种方法(如下)
double ransacReprojThreshold = 3,
OutputArray mask=noArray(), 掩码
const int maxIters = 2000, 最大迭代次数,当使用RANSAC方法
const double confidence = 0.995 置信参数
);
0 – 使用所有的点,比如最小二乘
RANSAC – 基于随机样本一致性
LMEDS – 最小中值
RHO –基于渐近样本一致性
InputArray src, 输入四个顶点
OutputArray dst, 输出四个顶点(变换后)
InputArray m 仿射变换矩阵
)
void ImageFeature::flann_demo(Mat& image, Mat& image2) {
//SURF 特征提取
int minHessian = 400;
Ptr<xfeatures2d::SURF> detector = xfeatures2d::SURF::create(minHessian);
vector<KeyPoint> keypoint_obj, keypoint_scene;
Mat descriptor_obj, descriptor_scens;
detector->detectAndCompute(image, Mat(), keypoint_obj, descriptor_obj);
detector->detectAndCompute(image2, Mat(), keypoint_scene, descriptor_scens);
//SURF 特征匹配
FlannBasedMatcher matcher;
vector<DMatch> matches;
matcher.match(descriptor_obj, descriptor_scens, matches);
//查找最大最小距离
double minDist = 1000;
double maxDist = 0;
for (size_t i = 0; i < descriptor_obj.rows; i++) {
double dist = matches[i].distance;
if (dist > maxDist) { maxDist = dist; }
if (dist < minDist) { minDist = dist; }
}
cout << "minDist = " << minDist << endl;
cout << "maxDist = " << maxDist << endl;
// 筛选最佳匹配(小于最小距离的3倍)
vector<DMatch> goodMatchs;
for (size_t i = 0; i < descriptor_obj.rows; i++) {
double dist = matches[i].distance;
if (dist < max(3 * minDist, 0.02)) {
goodMatchs.push_back(matches[i]);
}
}
//绘制特征点
Mat matchImg;
drawMatches(
image, keypoint_obj,
image2, keypoint_scene,
goodMatchs, matchImg,
Scalar::all(-1), Scalar::all(-1),
vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS
);
imshow("matchImg", matchImg);
//查看goodMatchs
for (size_t i = 0; i < goodMatchs.size(); i++){
cout << "goodMatchs "<<to_string(i) << "\tKeypoint1, Keypoint2 = " << goodMatchs[i].queryIdx <<", " << goodMatchs[i].trainIdx << endl;
}
//获取goodMatchs坐标,并获取变换矩阵
vector<Point2f> obj;
vector<Point2f> scens;
for (size_t i = 0; i < goodMatchs.size(); i++) {
obj.push_back(keypoint_obj[goodMatchs[i].queryIdx].pt);
scens.push_back(keypoint_scene[goodMatchs[i].trainIdx].pt);
}
Mat H = findHomography(obj, scens, RANSAC);
vector<Point2f> obj_corners(4);
vector<Point2f> scens_corners(4);
obj_corners[0] = Point(0, 0);
obj_corners[1] = Point(image.cols, 0);
obj_corners[2] = Point(image.cols,image.rows);
obj_corners[3] = Point(0, image.rows);
perspectiveTransform(obj_corners, scens_corners, H);
//可视化
line(matchImg, scens_corners[0] + obj_corners[1], scens_corners[1] + obj_corners[1], Scalar(0, 0, 255));
line(matchImg, scens_corners[1] + obj_corners[1], scens_corners[2] + obj_corners[1], Scalar(0, 0, 255));
line(matchImg, scens_corners[2] + obj_corners[1], scens_corners[3] + obj_corners[1], Scalar(0, 0, 255));
line(matchImg, scens_corners[3] + obj_corners[1], scens_corners[0] + obj_corners[1], Scalar(0, 0, 255));
imshow("matchImg2", matchImg);
//绘制到背景图上
Mat image2_BGR;
cvtColor(image2, image2_BGR, COLOR_GRAY2BGR);
line(image2_BGR, scens_corners[0], scens_corners[1], Scalar(0, 0, 255));
line(image2_BGR, scens_corners[1], scens_corners[2], Scalar(0, 0, 255));
line(image2_BGR, scens_corners[2], scens_corners[3], Scalar(0, 0, 255));
line(image2_BGR, scens_corners[3], scens_corners[0], Scalar(0, 0, 255));
imshow("image2_BGR", image2_BGR);
}