0%

特征匹配

ubuntu下opencv CMakeList文件

通过terminal直接apt安装opencv,一般挂载在/usr/local/lib下;此时find_package即可找到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# cmake needs this line
cmake_minimum_required(VERSION 3.1)

# Define project name
project(FeatureMapping)

# Find OpenCV, you may need to set OpenCV_DIR variable
# to the absolute path to the directory containing OpenCVConfig.cmake file
# via the command line or GUI
find_package(OpenCV REQUIRED)

# Enable C++11
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)

# Declare the executable target built from your sources
add_executable(main main.cpp)

# Link your application with OpenCV libraries
target_link_libraries(main PRIVATE ${OpenCV_LIBS})

两RGB图间的特征匹配

具体可参考官方教程https://docs.opencv.org/4.x/d7/dff/tutorial_feature_homography.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <opencv4/opencv2/highgui.hpp>
#include <opencv4/opencv2/opencv.hpp>
#include <opencv4/opencv2/imgproc.hpp>
#include <opencv4/opencv2/features2d.hpp>
#include <opencv4/opencv2/core.hpp>
#include <opencv4/opencv2/calib3d.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char **argv)
{
cout << CV_VERSION << endl;

Mat img1 = imread("001.JPG");
Mat img2 = imread("002.JPG");

Ptr<SIFT> detector = SIFT::create();
vector<KeyPoint> keypoints1, keypoints2;
Mat descriptors1, descriptors2;
detector->detectAndCompute(img1, noArray(), keypoints1, descriptors1);
detector->detectAndCompute(img2, noArray(), keypoints2, descriptors2);

Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED);

vector<vector<DMatch>> knn_matches;
matcher->knnMatch(descriptors1, descriptors2, knn_matches, 2);

const float ratio_thresh = 0.5f;
vector<DMatch> good_matches;
for(size_t i=0; i<knn_matches.size();i++)
{
if(knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
{
good_matches.push_back(knn_matches[i][0]);
}
}

Mat img_matches;
drawMatches(img1, keypoints1, img2, keypoints2, good_matches, img_matches);


//-- Localize the object
std::vector<Point2f> obj;
std::vector<Point2f> scene;
for( size_t i = 0; i < good_matches.size(); i++ )
{
//-- Get the keypoints from the good matches
obj.push_back( keypoints1[ good_matches[i].queryIdx ].pt );
scene.push_back( keypoints2[ good_matches[i].trainIdx ].pt );
}
Mat H = findHomography( obj, scene, RANSAC );
cout << H << endl;
//-- Get the corners from the image_1 ( the object to be "detected" )
std::vector<Point2f> obj_corners(4);
obj_corners[0] = Point2f(0, 0);
obj_corners[1] = Point2f( (float)img1.cols, 0 );
obj_corners[2] = Point2f( (float)img1.cols, (float)img1.rows );
obj_corners[3] = Point2f( 0, (float)img1.rows );
std::vector<Point2f> scene_corners(4);
perspectiveTransform( obj_corners, scene_corners, H);
//-- Draw lines between the corners (the mapped object in the scene - image_2 )
line( img_matches, scene_corners[0] + Point2f((float)img1.cols, 0),
scene_corners[1] + Point2f((float)img1.cols, 0), Scalar(0, 255, 0), 4 );
line( img_matches, scene_corners[1] + Point2f((float)img1.cols, 0),
scene_corners[2] + Point2f((float)img1.cols, 0), Scalar( 0, 255, 0), 4 );
line( img_matches, scene_corners[2] + Point2f((float)img1.cols, 0),
scene_corners[3] + Point2f((float)img1.cols, 0), Scalar( 0, 255, 0), 4 );
line( img_matches, scene_corners[3] + Point2f((float)img1.cols, 0),
scene_corners[0] + Point2f((float)img1.cols, 0), Scalar( 0, 255, 0), 4 );
//-- Show detected matches

namedWindow("Matches", WINDOW_NORMAL);
imshow("Matches", img_matches );
// imwrite("good_res.jpg", img_matches);


waitKey();
return 0;
}

/*
输出H:
[0.9771928708495958, -0.008932567941541457, 129.4806431662592;
0.0342771681684575, 0.9114878090650101, 163.0011063784843;
3.322036688562577e-05, -1.090291446290035e-05, 1]

*/

值得注意的是,本版本是opencv 4.5,与Opencv2/3有很多语法不一样了;关于特征点和描述子最大的区别:特征点是特征坐标KeyPoint,描述子是描述邻域信息的Mat。

特征匹配总体的流程:
1.首先读取图片,创建检测特征SIFT,ORB,SURF(xfeature)等
2.如果检测子包含描述子,则直接detectAndCompute
3.一般用BFMatcher直接对描述子点对vector 进行暴力匹配,这里是优化加速版用FLANN的knn,注意的是ORB可用HAMMING距离,其他则不可。
4.接着对KNN点对进行基于距离的筛选
5.对置信度高的点对进行RANSAC的单应变换矩阵求解

至此基本完成特征匹配的功能,得到的单应矩阵描述了图片间的变换关系;这是很多CV高级应用的基础,如校正,拼接,SLAM等。