Opencv实现Surf特征点和Homography匹配物体

另一篇博客已经初步了解单映性的原理和计算,这篇就借助Opencv库来实战。

**目的:**在一张背景图中寻找目标物体,并标注出来。

思路:

  • 1.通过SurfFeatureDetector分别提取出Object图片和Scene图片各自的特征点(Surf特征点)。
  • 2.通过SurfDescriptorExtractor分别计算出Object图片和Scene图片对特征点的描述。
  • 3.采用FlannBasedMatcher匹配器将Object和Scene各自的特征点匹配起来。(FLANN - Fast Library for Approximate Nearest Neighbors)
  • 4.提取出一定条件下的Good_Matches,比如小于(3 * 最小匹配距离)的Matches。
  • 5.通过findHomography函数来计算出Object的特征点(Good_Matches)和Scene的特征点(Good_Matches)之间的单映性矩阵H
  • 6.通过perspectiveTransform(对二维或者三维矢量进行透射变换,也就是对输入二维坐标点或者三维坐标点进行投射变换)函数对Object图像的四个交点进行投影映射到Scene图像上,并且用线段标识出来。

源码如下:

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
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/nonfree/nonfree.hpp>
#include <opencv2/calib3d/calib3d.hpp>
using namespace cv;
using namespace std;

int main()
{
Mat img_object = imread("./res/box.png", CV_LOAD_IMAGE_GRAYSCALE);
Mat img_scene = imread("./res/box_in_scene.png", CV_LOAD_IMAGE_GRAYSCALE);

int minHessian = 400;

SurfFeatureDetector detector(minHessian);
vector<KeyPoint> kp_object, kp_scene;

detector.detect(img_object,kp_object);
detector.detect(img_scene, kp_scene);

SurfDescriptorExtractor extractor;
Mat descriptor_object, descriptor_scene;
extractor.compute(img_object, kp_object, descriptor_object);
extractor.compute(img_scene, kp_scene, descriptor_scene);

FlannBasedMatcher matcher;
vector<DMatch> matches;
matcher.match(descriptor_object, descriptor_scene, matches);

double min_dist = 100, max_dist = img_object.rows > img_object.cols ? img_object.rows:img_object.cols;
for (int i = 0; i < descriptor_object.rows;i++)
{
double dist = matches[i].distance;
if (dist < min_dist) min_dist = dist;
if (dist > max_dist) max_dist = dist;
}

vector<DMatch> good_matchs;

for (int i = 0; i < descriptor_object.rows;i++)
{
if (matches[i].distance < 3 * min_dist )
{
good_matchs.push_back(matches[i]);
}
}

Mat img_matches;
drawMatches(img_object, kp_object, img_scene, kp_scene,
good_matchs,img_matches, Scalar::all(-1), Scalar::all(-1),
vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

//-- Localize the object
vector<Point2f> obj;
vector<Point2f> scene;
for (int i = 0; i < good_matchs.size();i++)
{
obj.push_back(kp_object[good_matchs[i].queryIdx].pt);
scene.push_back(kp_scene[good_matchs[i].trainIdx].pt);
}

Mat H = findHomography(obj, scene, CV_RANSAC);
vector<Point2f> obj_corners(4);
obj_corners[0] = Point(0, 0);
obj_corners[1] = Point(img_object.cols, 0);
obj_corners[2] = Point(img_object.cols, img_object.rows);
obj_corners[3] = Point(0, img_object.rows);
vector<Point2f> scene_corners(4);
perspectiveTransform(obj_corners, scene_corners, H);

line(img_matches, scene_corners[0] + Point2f(img_object.cols, 0), scene_corners[1] + Point2f(img_object.cols, 0), Scalar(0, 255, 0), 2);
line(img_matches, scene_corners[1] + Point2f(img_object.cols, 0), scene_corners[2] + Point2f(img_object.cols, 0), Scalar(0, 255, 0), 2);
line(img_matches, scene_corners[2] + Point2f(img_object.cols, 0), scene_corners[3] + Point2f(img_object.cols, 0), Scalar(0, 255, 0), 2);
line(img_matches, scene_corners[3] + Point2f(img_object.cols, 0), scene_corners[0] + Point2f(img_object.cols, 0), Scalar(0, 255, 0), 2);

imwrite("./res/Img_matches.jpg", img_matches);
imshow("Img_Matches", img_matches);
waitKey();
return 0;
}

效果图:
DetectObject

这里也同时贴出计算出来的H(Homography矩阵):
DetectObject

贴出Opencv中Homography相关的函数Api及功能:

  • estimateRigidTransform():计算多个二维点对或者图像之间的最优仿射变换矩阵 (2行x3列),H可以是部分自由度,比如各向一致的切变。
  • getAffineTransform():计算3个二维点对之间的仿射变换矩阵H(2行x3列),自由度为6.
  • warpAffine():对输入图像进行仿射变换
  • findHomography: 计算多个二维点对之间的最优单映射变换矩阵 H(3行x3列) ,使用最小均方误差或者RANSAC方法 。
  • getPerspectiveTransform():计算4个二维点对之间的透射变换矩阵 H(3行x3列)
  • warpPerspective(): 对输入图像进行透射变换
  • perspectiveTransform():对二维或者三维矢量进行透射变换,也就是对输入二维坐标点或者三维坐标点进行投射变换。
  • estimateAffine3D:计算多个三维点对之间的最优三维仿射变换矩阵H (3行x4列)
  • transform():对输入的N维矢量进行变换,可用于进行仿射变换、图像色彩变换.
  • findFundamentalMat:计算多个点对之间的基矩阵H
  • 如何计算3个二维点对之间的仿射变换矩阵?
    使用getAffineTransform()。
  • 如何计算多个二维点对之间的仿射变换矩阵(使用误差最小准则 )?
    使用estimateRigidTransform()或者findHomography。
  • 如何计算4个二维点对之间的透射变换?
    使用getPerspectiveTransform()。
  • 如何计算多个三维点对之间的仿射变换?
    使用estimateAffine3D。
  • 如何对输入图像进行仿射变换?
    使用warpAffine()。
  • 如何对输入图像进行透射变换?
    使用perspectiveTransform()。
  • 如何对输入的二维点对进行仿射变换?
    使用transform()。
  • 如何对输入的三维点对进行投射变换?
    使用perspectiveTransform()。