文章目录

一、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:///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;
}
}

OpenCV + CPP 系列(卌一)图像特征匹配( FLANN 匹配)_scala

二、单应性矩阵

从场景图像上得到特征匹配图像的四点坐标:​​详情请查看​

函数介绍: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);
}

OpenCV + CPP 系列(卌一)图像特征匹配( FLANN 匹配)_i++_02