本文所有代码,只使用C++实现,Python类似,甚至更加简单,暂不做实现。

特征检测

特征检测常用的算法有:

  1. SIFT(尺度不变特征变换)
  2. SURF(加速鲁棒特征)
  3. ORB

SIFTSURF已经申请了专利,在OpenCV中使用需要付费,所以不做讨论。

以下为ORB的基本用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Mat src = imread("./img/blox.jpg"), gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
// 创建 orb 检测器
auto orb = ORB::create();

// 使用 orb 查找特征点
vector<KeyPoint> keyPoints;
orb->detect(gray, keyPoints);

// 使用 orb 计算描述符
Mat desc;
orb->compute(gray, keyPoints, desc);

// 查找特征点和计算描述符也可以归为一步
// orb->detectAndCompute(src, Mat(), keyPoints, desc);

// 绘制特征点
drawKeypoints(src, keyPoints, src);

imshow("src", src);
waitKey();
destroyAllWindows();

orb

以上几乎为固定写法,如果是多图,detectcompute可以传多个数据进行匹配。得到多个keyPointsdesc

特征匹配之BFMatcher

Brute-Force Matcher(BF Matcher)可以翻译成野蛮匹配器,它的步骤是先选取第一个图片特征集合的一个特征描述符,然后匹配另一个集合的所有特征,通过一些距离算法得到一个最近的距离distance。

查找特征点和描述符

1
2
3
4
5
6
7
8
9
10
11
12
13
Mat img1 = imread("./img/box.png"), gray1;
Mat img2 = imread("./img/box_in_scene.png"), gray2;
cvtColor(img1, gray1, COLOR_BGR2GRAY);
cvtColor(img2, gray2, COLOR_BGR2GRAY);

// 创建 orb 检测器
auto orb = ORB::create();

// 使用 orb 查找特征点和计算描述符
vector<KeyPoint> keyPoints1, keyPoints2;
Mat desc1, desc2;
orb->detectAndCompute(img1, Mat(), keyPoints1, desc1);
orb->detectAndCompute(img2, Mat(), keyPoints2, desc2);

创建并使用BFMatcher

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
 // 创建 BFMatcher,开启crossCheck获取更精确的结果
auto matcher = BFMatcher::create(NORM_HAMMING, true);
// BFMatcher::create 函数需要两个参数,第一个是normType,第二个为crossCheck
// 1. normType
// 指定距离测量的规则,默认值是NORM_L2,该值一般与NORM_L1用在SIFT和SURF检测中。
// 如果我们使用ORB检测,我们需要将该值设置为 NORM_HAMMING2。
// 2. crossCheck
// 交叉检查,默认为 false,设置该值为 true 来获取更精确的结果。

// 对比两张图的特征,进行匹配
vector<DMatch> matches;
matcher->match(desc1, desc2, matches);

// 根据评分排序,从小到大,因为越小越精确
sort(matches.begin(), matches.end());

// 移除较差的匹配结果,只保留前百分之15的结果
double percent = 0.15;
int num = matches.size() * percent;
// 至少保留十个结果
num = max(num, 10);

// 对 matches 进行裁剪
matches.assign(matches.begin(), matches.begin() + num);

// 绘制图形
Mat dst;
drawMatches(img1, keyPoints1, img2, keyPoints2, matches, dst);
// 绘制图形的时候,可以将最后一个参数,flags
// 设置为 DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS 从而只显示匹配成功的点。

DMatch类的一些参数

  1. distance:描述符之间的距离,值越小越好
  2. trainIdx:描述符在train图片中的索引
  3. queryIdx:描述符在query图片中的索引
  4. imgIdxtrain图片的索引

bf

特征匹配之FLANN

FLANN是Fast Library for Approximate Nearest Neighbors.的缩写。快速近似最近邻搜索库。 它包含一组算法,这些算法针对大型数据集中的快速最近邻搜索和高维特征进行了优化。对于大型数据集,它的运行速度比BFMatcher更快。

查找特征点和描述符

1
2
3
4
5
6
7
8
9
10
11
12
13
Mat img1 = imread("./img/box.png"), gray1;
Mat img2 = imread("./img/box_in_scene.png"), gray2;
cvtColor(img1, gray1, COLOR_BGR2GRAY);
cvtColor(img2, gray2, COLOR_BGR2GRAY);

// 创建 orb 检测器
auto orb = ORB::create();

// 使用 orb 查找特征点和计算描述符
vector<KeyPoint> keyPoints1, keyPoints2;
Mat desc1, desc2;
orb->detectAndCompute(img1, Mat(), keyPoints1, desc1);
orb->detectAndCompute(img2, Mat(), keyPoints2, desc2);

创建并使用FLANN

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
// 创建 indexPar 和 searchPar
// 当前使用 ORB 检测器,indexPar 使用LshIndexParams 参数默认给6,12,1
auto indexPar = makePtr<flann::LshIndexParams>(6, 12, 1);
// 检索参数,数值越大越准确,但是也越耗时
auto searchPar = makePtr<flann::SearchParams>(100);
// 使用 indexpar 和 searchPar 创建 flannMatcher
FlannBasedMatcher flannMatcher(indexPar, searchPar);
vector<vector<DMatch>> matches;
vector<vector<char>> matchesMask;
// 本例子使用 knnMatch, k 设置为 2
flannMatcher.knnMatch(desc1, desc2, matches, 2);

for (int i = 0; i < matches.size(); i++) {
DMatch first = matches[i][0], last = matches[i][1];
// 第一个小于第二个的百分之80,舍去该值。这个比例根据不同的图片要进行一些微调。
if (first.distance < 0.8 * last.distance) {
matchesMask.push_back({1, 0});
} else {
matchesMask.push_back({0, 0});
}
}

Mat dst;
// 使用 Mask 掩码来输出图像
drawMatches(img1, keyPoints1, img2, keyPoints2, matches, dst,
Scalar::all(-1), Scalar::all(-1), matchesMask,
DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

flann

评论