opencv-097-基于描述子匹配的已知对象定位

知识点

图像特征点检测、描述子生成以后,就可以通过OpenCV提供的描述子匹配算法,得到描述子直接的距离,距离越小的说明是匹配越好的,设定一个距离阈值,一般是最大匹配距离的1/5~1/4左右作为阈值,得到所有小于阈值的匹配点,作为输入,通过单应性矩阵,获得这两个点所在平面的变换关系H,根据H使用透视变换就可以根据输入的对象图像获得场景图像中对象位置,最终绘制位置即可。

代码(c++,python)

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
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
#define RATIO 0.4
using namespace cv;
using namespace std;

int main(int argc, char** argv) {
Mat box = imread("D:/images/box.png");
Mat scene = imread("D:/images/box_in_scene.png");
if (scene.empty()) {
printf("could not load image...\n");
return -1;
}
imshow("input image", scene);
vector<KeyPoint> keypoints_obj, keypoints_sence;
Mat descriptors_box, descriptors_sence;
Ptr<ORB> detector = ORB::create();
detector->detectAndCompute(scene, Mat(), keypoints_sence, descriptors_sence);
detector->detectAndCompute(box, Mat(), keypoints_obj, descriptors_box);

vector<DMatch> matches;
// 初始化flann匹配
// Ptr<FlannBasedMatcher> matcher = FlannBasedMatcher::create(); // default is bad, using local sensitive hash(LSH)
Ptr<DescriptorMatcher> matcher = makePtr<FlannBasedMatcher>(makePtr<flann::LshIndexParams>(12, 20, 2));
matcher->match(descriptors_box, descriptors_sence, matches);

// 发现匹配
vector<DMatch> goodMatches;
printf("total match points : %d\n", matches.size());
float maxdist = 0;
for (unsigned int i = 0; i < matches.size(); ++i) {
printf("dist : %.2f \n", matches[i].distance);
maxdist = max(maxdist, matches[i].distance);
}
for (unsigned int i = 0; i < matches.size(); ++i) {
if (matches[i].distance < maxdist*RATIO)
goodMatches.push_back(matches[i]);
}

Mat dst;
drawMatches(box, keypoints_obj, scene, keypoints_sence, goodMatches, dst);
imshow("output", dst);

//-- Localize the object
std::vector<Point2f> obj_pts;
std::vector<Point2f> scene_pts;
for (size_t i = 0; i < goodMatches.size(); i++)
{
//-- Get the keypoints from the good matches
obj_pts.push_back(keypoints_obj[goodMatches[i].queryIdx].pt);
scene_pts.push_back(keypoints_sence[goodMatches[i].trainIdx].pt);
}
Mat H = findHomography(obj_pts, scene_pts, RHO);
// 无法配准
// Mat H = findHomography(obj, scene, RANSAC);

//-- Get the corners from the image_1 ( the object to be "detected" )
std::vector<Point2f> obj_corners(4);
obj_corners[0] = Point(0, 0); obj_corners[1] = Point(box.cols, 0);
obj_corners[2] = Point(box.cols, box.rows); obj_corners[3] = Point(0, box.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(dst, scene_corners[0] + Point2f(box.cols, 0), scene_corners[1] + Point2f(box.cols, 0), Scalar(0, 255, 0), 4);
line(dst, scene_corners[1] + Point2f(box.cols, 0), scene_corners[2] + Point2f(box.cols, 0), Scalar(0, 255, 0), 4);
line(dst, scene_corners[2] + Point2f(box.cols, 0), scene_corners[3] + Point2f(box.cols, 0), Scalar(0, 255, 0), 4);
line(dst, scene_corners[3] + Point2f(box.cols, 0), scene_corners[0] + Point2f(box.cols, 0), Scalar(0, 255, 0), 4);

//-- Show detected matches
imshow("Good Matches & Object detection", dst);
imwrite("D:/result.png", dst);
waitKey(0);

waitKey(0);
return 0;
}
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
"""
基于描述子匹配的已知对象定位
"""

import cv2 as cv
import numpy as np

box = cv.imread("images/box.png")
box_in_scene = cv.imread("images/box_in_scene.png")
cv.imshow("box", box)
cv.imshow("box_in_scene", box_in_scene)

# 创建ORB特征检测器
orb = cv.ORB_create()

# 得到特征关键点和描述子
kp1, des1 = orb.detectAndCompute(box, None)
kp2, des2 = orb.detectAndCompute(box_in_scene, None)

# 暴力匹配
bf = cv.BFMatcher(cv.NORM_HAMMING, crossCheck=True)
matchers = bf.match(des1, des2)

# 发现匹配
maxdist = 0
goodMatches = []
for m in matchers:
maxdist = max(maxdist, m.distance)
for m in matchers:
if m.distance < 0.4 * maxdist:
goodMatches.append(m)


# 找到本地化对象
obj_pts = np.float32([kp1[m.queryIdx].pt for m in goodMatches]).reshape(-1, 1, 2)
scene_pts = np.float32([kp2[m.trainIdx].pt for m in goodMatches]).reshape(-1, 1, 2)

# findHomography 函数是计算变换矩阵
# 参数cv.RANSAC / cv.RHO是使用RANSAC算法寻找一个最佳单应性矩阵H,即返回值M
# 返回值:M 为变换矩阵,mask是掩模
M, mask = cv.findHomography(obj_pts, scene_pts, cv.RANSAC)

# 获取box的图像尺寸
h, w, _ = box.shape
# obj_corners是图像box的四个顶点
obj_corners = np.float32([[0, 0], [w, 0], [w, h], [0, h]]).reshape(-1, 1, 2)
# 计算变换后的四个顶点坐标位置,透视变换
scene_corners = cv.perspectiveTransform(obj_corners, M)

# 根据四个顶点坐标位置在img2图像画出变换后的边框
box_in_scene = cv.polylines(box_in_scene, [np.int32(scene_corners)], True, (0, 0, 255), 3, cv.LINE_AA)

# 绘制
result = cv.drawMatches(box, kp1, box_in_scene, kp2, goodMatches, None)
cv.imshow("orb-match", result)

cv.waitKey(0)
cv.destroyAllWindows()

结果

代码地址

github