当前位置:   article > 正文

OpenCV基础(23)特征检测与匹配_opencv 匹配特征

opencv 匹配特征

在这里插入图片描述
图像的8x8像素部分被考虑,并将这个 8x8 框进一步划分为 4 个块,每个块为 4x4 维度。在每个 4x4 块内,图像梯度以向量的形式表示。通过搜索最独特或不同的特征在图像中找到关键点。这里,Key point Descriptor是由4个相邻向量组合而成。关键点描述符显示该部分图像中梯度变化的方向和幅度。对关键点周围的区域进行归一化,计算关键点区域的局部描述符。局部描述符是一个数字向量,用于描述关键点的视觉外观。

2D 特征框架的主要部分是:

  • 特征检测
  • 特征匹配

如果您在较低级别看到图像,即没有关注图像的主题,图像的突出特征将是角点、边缘和像素强度变化最小的一致部分。其中,最重要的将是角点。角点是由于相邻边缘而在两个方向上发生严重梯度变化的点。因此,Opencv 提供了一些非常酷的方法来检测图像中的角点。

1.特征检测方法

a) Harris Corner Detection
b) Shi-Tomasi Corner Detection
c) FAST algorithm for corner detection
d) BRIEF(Binary Robust Independent Elementary Features)
e) ORB(Oriented Fast and Rotated Brief)
f) SIFT(Scale Invariant Feature Transform)
g) SURF(Speeded Up Robust Features)

2. Harris角点检测

Harris Corner Detector 是一种角点检测算子,常用于计算机视觉算法中,用于提取角点并推断图像的特征。与之前的角点检测器相比,Harris角点检测器直接考虑了角点分数与方向的差异,而不是每45度角都使用移位的patch,被证明在区分边缘和角点方面更加准确。从那时起,它得到了改进并被许多算法采用来预处理图像以供后续应用程序使用。
在这里插入图片描述

2.1角点

角点是其局部邻域位于两个主要且不同的边缘方向的点。换句话说,角可以解释为两条边的交汇处,其中边是图像亮度的突然变化。角点是图像中的重要特征,通常称为兴趣点,对平移、旋转和光照不变。
在这里插入图片描述
所以让我们了解为什么角被认为是更好的特征或适合补丁映射。在上图中,如果我们取平坦区域,则在任何方向都没有观察到梯度变化。同样,在边缘区域,沿边缘方向没有观察到梯度变化。所以平坦区域和边缘区域都不利于补丁匹配,因为它们不是很明显(边缘区域中沿边缘有许多相似的补丁)。而在角落区域,我们观察到所有方向的显着梯度变化。由于这个角被认为有利于补丁匹配(在任何方向上移动窗口都会产生很大的外观变化)并且通常在视点变化时更稳定。

2.2角点检测

这个想法是在图像中的每个像素 p 周围考虑一个小窗口。我们想要识别所有这些唯一的像素窗口。可以通过在给定方向上将每个窗口移动少量并测量像素值中发生的变化量来测量唯一性。
在这里插入图片描述
更正式地说,我们取平移前后像素值的平方和(SSD),并识别出在所有8个方向平移时SSD都较大的像素窗口。让我们定义变化函数E(u,v)为所有差异平方和(SSD)的**sum **,其中u,v是我们3 × 3窗口中每个像素的x,y坐标,I是像素的强度值。图像中的特征是由某个阈值定义的具有较大E(u,v)值的所有像素。
在这里插入图片描述
为了角点检测,我们必须最大化这个函数 E(u,v)。这意味着,我们必须最大化第二项。将泰勒展开式应用于上述方程并使用一些数学步骤,我们得到最终方程:
在这里插入图片描述
现在,我们将求和矩阵重命名为 M:
在这里插入图片描述
所以方程现在变成:
在这里插入图片描述
记住,我们希望SSD在所有八个方向上都是大的,或者相反,SSD在所有方向上都是小的。通过求解M的特征向量,我们可以得到SSD最大和最小增长的方向。相应的特征值给了我们这些增加的实际值。为每个窗口计算一个分数R:
在这里插入图片描述
λ1 和 λ2 是 M 的特征值。因此这些特征值的值决定了区域是角、边缘还是平面。

  • 当|R|很小,当 λ1 和 λ2 很小时会发生这种情况,该区域是平坦的。
  • 当 R<0 时,即 λ1>>λ2 时发生,反之亦然,则该区域为边缘。
  • 当 R 较大时,即 λ1 和 λ2 较大且 λ1∼λ2 时,该区域为角点。

在这里插入图片描述

2.3伪代码

  • 1.取原图的灰度
  • 2.应用高斯滤波器以消除任何噪声
  • 3.应用 Sobel 算子找到灰度图像中每个像素的 x 和 y 梯度值
  • 4.对于灰度图像中的每个像素 p,考虑它周围的 3×3 窗口并计算角强度函数,称其为哈里斯值。
  • 5.找到所有超过某个阈值并且是某个窗口内的局部最大值的像素(以防止特征的冗余重复)
  • 6.对于满足 5 中标准的每个像素,计算一个特征描述符。

2.4 代码实现

import cv2
import numpy as np

# 读取原始图像
cv_image = cv2.imread("bird.png")
cv2.namedWindow("src", 0)
cv2.imshow("src", cv_image)
# 转换为灰度
gray_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)
# 将图像像素数据转换为 float32,以避免进一步的尺寸不兼容冲突
gray_image = np.float32(gray_image)
# syntax cv2.corenrHarris(input_image, block size for neighborhood pixels to be considered, sobel operator size, border type)
result_image = cv2.cornerHarris(gray_image, blockSize=2, ksize=3, k=0.04)
cv2.namedWindow("result_image", 0)
cv2.imshow("result_image", result_image)
# 膨胀以突出角点
result_image = cv2.dilate(result_image, None)
cv2.namedWindow("dilate", 0)
cv2.imshow("dilate", result_image)
# 使用最佳阈值恢复到原始图像
cv_image[result_image > 0.01 * result_image.max()] = [0, 0, 255]
cv2.namedWindow("haris", 0)
cv2.imshow("haris", cv_image)
cv2.waitKey()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

在这里插入图片描述
在这里插入图片描述

3.Shi-Tomasi角点检测

这种角点检测方法类似于Harris。 Shi-Tomasi 在他的研究论文 Good Features to Track 中提出要找到图像中的 N 个最强角点。评分函数为:
在这里插入图片描述
实际上,Shi-Tomasi比Harris更有效。在图形上,我们可以将此方法可视化为
在这里插入图片描述

3.1代码实现

import numpy as np
import cv2

# 输入图像
cv_img = cv2.imread('bird.png')
cv_gray = cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
# syntax cv2.goodFeaturesToTrack(input_image, max_corner_to_detect, qualityLevel, minDistance)
corners = cv2.goodFeaturesToTrack(cv_gray, maxCorners=25, qualityLevel=0.01, minDistance=10)
corners = np.float32(corners)
for item in corners:
    x, y = item[0].astype("int")
    cv2.circle(cv_img, (x, y), 3, (0, 0, 255), -1)
cv2.imshow("image", cv_img)
cv2.imwrite("shi_result.jpg", cv_img)
cv2.waitKey()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述

4.FAST 角点检测算法

上面讨论的角点检测技术很好,但对于实时应用来说还不够快。作为对此的解决方案,FAST(Features from Accelerated Segment Test)算法是由 Edward Rosten 和 Tom Drummond 在 2006 年的论文“Machine learning for high-speed corner detection”中提出的(后来在 2010 年对其进行了修订)。

4.1使用 FAST 进行特征检测

该算法解释如下:
在这里插入图片描述

  • 在图像中选择一个像素p,检测像素p是否为兴趣点。让它的强度为Ip。
  • 选择合适的阈值 t。
  • 考虑被测像素周围 16 个像素的圆圈。 (这是一个半径为 3 的 Bresenham 圆。)
  • 现在,如果在圆(16 个像素)中存在一组 n 个连续像素,它们都比 Ip + t 亮,或者都比 Ip - t 暗,那么像素*** p*** 是一个角点。 (作者在算法的第一个版本中使用了 n=12)
  • 为了使算法快速,首先将圆的1、5、9、13像素的强度与Ip进行比较。从上图可以看出,这四个像素中至少有三个满足阈值条件,这样兴趣点才会存在。
  • 如果四个像素值中的至少三个——I1、I5、I9、I13 不高于或低于 Ip + t,则 p 不是兴趣点(角点)。在这种情况下,拒绝像素 p 作为可能的兴趣点。否则,如果至少三个像素高于或低于 Ip + t,则检查所有 16 个像素并检查 12 个连续像素是否符合标准。
  • 对图像中的所有像素重复该过程。

该算法有一些限制。首先,对于 n<12,算法在所有情况下都不能很好地工作,因为当 n<12 时,检测到的兴趣点数量非常多。其次,查询16个像素的顺序决定了算法的速度。算法中添加了机器学习方法来处理这些问题。

4.2机器学习方法

  • 选择一组图像进行训练(最好来自目标应用领域)
  • 在每个图像中运行 FAST 算法以查找特征点。
  • 对于每个特征点,将其周围的 16 个像素存储为一个向量。对所有图像执行此操作以获得特征向量*** p***。
  • 这 16 个像素中的每个像素(比如 x)可以具有以下三种状态之一:
    在这里插入图片描述
  • 根据这些状态,特征向量 P 被细分为 3 个子集 Pd、Ps、Pb。
  • 定义一个变量 Kp,如果 ***p *** 是兴趣点则为真,如果 p 不是兴趣点则为假。
  • 使用 ID3 算法(决策树分类器)使用变量 Kp 查询每个子集,以获取有关真实类的知识。
  • ID3 算法的工作原理是熵最小化。以这样一种方式查询 16 个像素,即以最少的查询次数找到真正的类(兴趣点与否)。或者换句话说,选择像素 x,它具有关于像素 p 的最多信息。集合 P 的熵可以用数学表示为:
    在这里插入图片描述
  • 这递归地应用于所有子集,直到其熵为零。
  • 如此创建的决策树用于在其他图像中进行快速检测。

4.3非最大值抑制

检测相邻位置的多个兴趣点是另一个问题。它是通过使用非极大值抑制来解决的。

  • 为所有检测到的特征点计算得分函数 V。 V 是 p 和周围 16 个像素值之间的绝对差之和。
  • 考虑两个相邻的关键点并计算它们的 V 值。
  • 丢弃 V 值较低的那个。

4.4代码实现

简而言之,FAST 比许多现有的特征检测器快,但在存在高水平噪声的情况下表现不佳。主要是因为高水平的噪声会改变像素值。

import cv2

# 输入图像
cv_img = cv2.imread('bird.png')
cv_gray = cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
fast = cv2.FastFeatureDetector_create()
fast.setNonmaxSuppression(False)
keypoint = fast.detect(cv_gray, None)
keypoint_image = cv2.drawKeypoints(cv_img, keypoint, None, color=(0, 0, 255))
cv2.imshow("FAST", keypoint_image)
cv2.imwrite("fast.jpg", keypoint_image)
cv2.waitKey()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在这里插入图片描述

4.5代码比较是否使用NMS

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
#  FAST
import cv2
import matplotlib.pyplot as plt
import numpy as np

# 加载图像
image = cv2.imread('bird.png')

# 将图像转换为 RGB
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# 将图像转换为灰度
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)

# 显示图像和灰度图像
fx, plots = plt.subplots(1, 2, figsize=(20,10))

plots[0].set_title("Orignal Image")
plots[0].imshow(image)

plots[1].set_title("Gray Image")
plots[1].imshow(gray, cmap="gray")
plot.show()

fast = cv2.FastFeatureDetector_create()

# 用非最大抑制检测关键点
keypoints_with_nonmax = fast.detect(gray, None)

# 禁用非最大抑制
fast.setNonmaxSuppression(False)

# 在没有非最大抑制的情况下检测关键点
keypoints_without_nonmax = fast.detect(gray, None)

image_with_nonmax = np.copy(image)
image_without_nonmax = np.copy(image)

# 在输入图像上绘制关键点
cv2.drawKeypoints(image, keypoints_with_nonmax, image_with_nonmax, color=(0,255,0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.drawKeypoints(image, keypoints_without_nonmax, image_without_nonmax, color=(0,255,0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# 显示有和没有非最大抑制的图像
fx, plots = plt.subplots(1, 2, figsize=(20,10))

plots[0].set_title("With non max suppression")
plots[0].imshow(image_with_nonmax)

plots[1].set_title("Without non max suppression")
plots[1].imshow(image_without_nonmax)

# 打印使用NMS在图像中检测到的关键点数
print("Number of Keypoints Detected In The Image With Non Max Suppression: ", len(keypoints_with_nonmax))

# 打印不使用NMS在图像中检测到的关键点数
print("Number of Keypoints Detected In The Image Without Non Max Suppression: ", len(keypoints_without_nonmax))
plt.show()
# Number of Keypoints Detected In The Image With Non Max Suppression:  1496
# Number of Keypoints Detected In The Image Without Non Max Suppression:  5893
  • 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

在这里插入图片描述
在这里插入图片描述

5.BRIEF(Binary Robust Independent Elementary Features)

特征点描述符是目前许多计算机视觉技术的核心,如物体识别、三维重建、图像检索和摄像机定位等。由于这些技术的应用程序必须处理更多的数据,或者在具有有限计算资源的移动设备上运行,因此对计算快、匹配快、内存高效的本地描述符的需求越来越大。

在本文中,我们使用二进制字符串作为有效的特征点描述符,称为BRIEF。在很多情况下,BRIEF 的构建和匹配速度都非常快。BRIEF 在速度和识别率方面轻松胜过其他快速描述符,例如 SURF 和 SIFT。

5.1背景

在检测到关键点之后,我们继续计算每一个关键点的描述符。特征描述符将感兴趣的信息编码成一系列数字,作为一种数字指纹,可以用来区分不同的特征。在像素(关键点)周围定义的邻域被称为一个patch,它是一个像素宽度和高度的正方形。
在这里插入图片描述
基于相对少量的成对强度比较,可以有效地对图像块进行分类。Brief将图像小块转换为二值特征向量,使它们一起可以表示一个对象。二值特征向量又称二值特征描述符,是只包含1和0的特征向量。简而言之,每个关键点由一个128-512位字符串的特征向量描述。

5.2代码实现

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
"""
@File    : BRIEF.py
@Time    : 2021/10/13 11:07
@Author  : David
@Software: PyCharm
"""
# BRIEF(Binary Robust Independent Elementary Features)

# 导入库,显示图像

import cv2
import matplotlib.pyplot as plt
import numpy as np

# 加载图像
image1 = cv2.imread('bird.png')

# 将训练图像转换为RGB
training_image = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)

# 将训练图像转换为灰度
training_gray = cv2.cvtColor(training_image, cv2.COLOR_RGB2GRAY)

# 通过添加缩放不变性和旋转不变性来创建测试图像
test_image = cv2.pyrDown(training_image)
test_image = cv2.pyrDown(test_image)
num_rows, num_cols = test_image.shape[:2]

rotation_matrix = cv2.getRotationMatrix2D((num_cols / 2, num_rows / 2), 30, 1)
test_image = cv2.warpAffine(test_image, rotation_matrix, (num_cols, num_rows))

test_gray = cv2.cvtColor(test_image, cv2.COLOR_RGB2GRAY)

# 显示训练图像和测试图像
fx, plots = plt.subplots(1, 2, figsize=(20, 10))

plots[0].set_title("Training Image")
plots[0].imshow(training_image)

plots[1].set_title("Testing Image")
plots[1].imshow(test_image)

# 检测关键点并创建描述符

fast = cv2.FastFeatureDetector_create()
brief = cv2.xfeatures2d.BriefDescriptorExtractor_create()

train_keypoints = fast.detect(training_gray, None)
test_keypoints = fast.detect(test_gray, None)

train_keypoints, train_descriptor = brief.compute(training_gray, train_keypoints)
test_keypoints, test_descriptor = brief.compute(test_gray, test_keypoints)

keypoints_without_size = np.copy(training_image)
keypoints_with_size = np.copy(training_image)

cv2.drawKeypoints(training_image, train_keypoints, keypoints_without_size, color=(0, 255, 0))

cv2.drawKeypoints(training_image, train_keypoints, keypoints_with_size,
                  flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# 显示图像有和没有关键点大小
fx, plots = plt.subplots(1, 2, figsize=(20, 10))

plots[0].set_title("Train keypoints With Size")
plots[0].imshow(keypoints_with_size, cmap='gray')

plots[1].set_title("Train keypoints Without Size")
plots[1].imshow(keypoints_without_size, cmap='gray')

# 打印训练图像中检测到的关键点数量
print("Number of Keypoints Detected In The Training Image: ", len(train_keypoints))

# 打印查询图像中检测到的关键点数量
print("Number of Keypoints Detected In The Query Image: ", len(test_keypoints))

# 匹配关键点

# 创建一个Brute Force Matcher对象。
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)

# 对训练图像和测试图像的BRIEF描述符进行匹配
matches = bf.match(train_descriptor, test_descriptor)

# 距离较短的是我们想要的。
matches = sorted(matches, key=lambda x: x.distance)

result = cv2.drawMatches(training_image, train_keypoints, test_gray, test_keypoints, matches, test_gray, flags=2)

# 显示最佳匹配点
plt.rcParams['figure.figsize'] = [14.0, 7.0]
plt.title('Best Matching Points')
plt.imshow(result)
plt.show()

# 打印训练图像和查询图像之间的匹配点总数
print("\nNumber of Matching Keypoints Between The Training and Query Images: ", len(matches))
# Number of Keypoints Detected In The Training Image:  1495
# Number of Keypoints Detected In The Query Image:  138

# Number of Matching Keypoints Between The Training and Query Images:  59
  • 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
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

在这里插入图片描述
在这里插入图片描述

5.3总结

Brief 依靠相对少量的强度差异测试来将图像块表示为二进制字符串。这个描述符的构建和匹配不仅比其他最先进的描述符快得多,而且它也往往会产生更高的识别率,只要不需要对大的平面内旋转保持不变。

6.ORB(Oriented Fast and Rotated Brief)

Oriented Fast and Rotated Brief (ORB)是OpenCV实验室在2011年由Ethan Rublee、Vincent Rabaud、Kurt Konolige和Gary R. Bradski开发的,作为SIFT和SURF的一种有效可行的替代方案。ORB的构想主要是因为SIFT和SURF是专利算法。然而,ORB是免费使用的。

ORB在特征检测任务上的表现与SIFT一样好(而且比SURF更好),同时几乎快了两个数量级。ORB构建于众所周知的FAST关键点检测器和BRIEF描述符之上。这两种技术都因其良好的性能和低成本而具有吸引力。ORB的主要贡献如下:

  • 在 FAST 中添加了快速准确的定向组件
  • 定向BRIEF特征的高效计算
  • 定向BRIEF特征的方差和相关性分析
  • 一种在旋转不变性下对BRIEF特征去相关的学习方法,从而在最近邻应用中获得更好的性能

6.1相关术语

关键点 - 图像中特别独特的小区域。示例:像素值从亮到暗急剧变化的角点。
描述符——特征描述符是一种算法,它采用图像并输出特征描述符/特征向量。特征描述符将感兴趣的信息编码成一系列数字,并充当一种数字“指纹”,可用于区分一个特征与另一个特征。理想情况下,这些信息在图像变换下是不变的,因此即使图像以某种方式进行了变换,我们也可以再次找到该特征。

6.2代码实现

import cv2
import matplotlib.pyplot as plt
import numpy as np

# 加载图像
image1 = cv2.imread('bird.png')

# 将训练图像转换为RGB
training_image = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)

# 将训练图像转换为灰度
training_gray = cv2.cvtColor(training_image, cv2.COLOR_RGB2GRAY)

# 通过添加缩放不变性和旋转不变性来创建测试图像
test_image = cv2.pyrDown(training_image)
test_image = cv2.pyrDown(test_image)
num_rows, num_cols = test_image.shape[:2]

rotation_matrix = cv2.getRotationMatrix2D((num_cols/2, num_rows/2), 30, 1)
test_image = cv2.warpAffine(test_image, rotation_matrix, (num_cols, num_rows))

test_gray = cv2.cvtColor(test_image, cv2.COLOR_RGB2GRAY)

# 显示训练图像和测试图像
fx, plots = plt.subplots(1, 2, figsize=(20,10))

plots[0].set_title("Training Image")
plots[0].imshow(training_image)

plots[1].set_title("Testing Image")
plots[1].imshow(test_image)


orb = cv2.ORB_create()

train_keypoints, train_descriptor = orb.detectAndCompute(training_gray, None)
test_keypoints, test_descriptor = orb.detectAndCompute(test_gray, None)

keypoints_without_size = np.copy(training_image)
keypoints_with_size = np.copy(training_image)

cv2.drawKeypoints(training_image, train_keypoints, keypoints_without_size, color = (0, 255, 0))

cv2.drawKeypoints(training_image, train_keypoints, keypoints_with_size, flags = cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# 显示图像有和没有关键点大小
fx, plots = plt.subplots(1, 2, figsize=(20,10))

plots[0].set_title("Train keypoints With Size")
plots[0].imshow(keypoints_with_size, cmap='gray')

plots[1].set_title("Train keypoints Without Size")
plots[1].imshow(keypoints_without_size, cmap='gray')

# 打印训练图像中检测到的关键点个数
print("Number of Keypoints Detected In The Training Image: ", len(train_keypoints))

# 打印查询图像中检测到的关键点数量
print("Number of Keypoints Detected In The Query Image: ", len(test_keypoints))


# 创建一个Brute Force Matcher对象。
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck = True)

# 执行训练图像和测试图像的ORB描述符之间的匹配
matches = bf.match(train_descriptor, test_descriptor)

# 距离较短的是我们想要的。
matches = sorted(matches, key = lambda x : x.distance)

result = cv2.drawMatches(training_image, train_keypoints, test_gray, test_keypoints, matches, test_gray, flags = 2)

# 显示最佳匹配点
plt.rcParams['figure.figsize'] = [14.0, 7.0]
plt.title('Best Matching Points')
plt.imshow(result)
plt.show()

# 打印训练图像和查询图像之间的匹配点总数
print("\nNumber of Matching Keypoints Between The Training and Query Images: ", len(matches))
# Number of Keypoints Detected In The Training Image:  500
# Number of Keypoints Detected In The Query Image:  108
# 
# Number of Matching Keypoints Between The Training and Query Images:  47
  • 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

在这里插入图片描述
在这里插入图片描述

7.SIFT(Scale Invariant Feature Transform)

SIFT是尺度不变特征变换(Scale-Invariant Feature Transform)的缩写,2004年由英属哥伦比亚大学(University of British Columbia)的D.Lowe首次提出。SIFT对图像的尺度和旋转具有不变性。该算法是专利算法,因此该算法包含在OpenCV的Non-free模块中。
在这里插入图片描述
SIFT的主要优点是:

  • **局部性:**特征是局部的,因此对遮挡和噪音非常健壮(无先验分割)
  • 独特性:单个特征可以与大型对象数据库相匹配
  • 数量:即使是小物体也可以生成许多特征
  • 效率:接近实时性能
  • **可扩展性:**可以很容易地扩展到广泛的不同特性类型,每一种类型都增加了健壮性

7.1算法思想

SIFT 是一个相当复杂的算法。 SIFT算法主要涉及四个步骤。我们将一一看到它们。

  • 尺度空间峰值选择:寻找特征的潜在位置。
  • 关键点定位:精确定位特征关键点。
  • 方向分配:为关键点分配方向。
  • 关键点描述符:将关键点描述为一个高维向量。
  • 关键点匹配

7.2尺度空间峰值选择

7.2.1尺度空间

现实世界中的物体只有在一定的尺度下才有意义。你可能会在桌子上看到一块方糖。但如果观察整个银河系,它根本不存在。物体的多尺度特性在自然界中是很常见的。一个尺度空间尝试在数字图像上复制这个概念。
在这里插入图片描述
图像的尺度空间是一个函数L(x,y,σ),它是由不同尺度的高斯核(blur)与输入图像的卷积产生的。尺度空间被划分为多个octaves,而octaves和尺度的数量取决于原始图像的大小。所以我们生成了原始图像的几个octaves。每个octave的图像大小是前一个的一半。

7.2.2模糊

在一个octave内,图像使用高斯模糊运算符逐步模糊。数学上,“模糊”被称为高斯算子和图像的卷积。高斯模糊有一个特定的表达式或“运算符”,应用于每个像素。结果是图像模糊。
在这里插入图片描述
G 是高斯模糊算子,I 是图像。 x,y 是位置坐标,σ 是“尺度”参数。将其视为模糊量。值越大,模糊越大。
在这里插入图片描述

7.2.3DOG(高斯核差分)

现在我们使用这些模糊的图像来生成另一组图像,即高斯差分 (DoG)。这些 DoG 图像非常适合找出图像中有趣的关键点。 DOG是作为具有两个不同 σ 的图像的高斯模糊的差分而获得的,设它是 σ 和 kσ。这个过程是针对高斯金字塔中图像的不同octave完成的。它如下图所示:
在这里插入图片描述

7.2.4寻找关键点

到目前为止,我们已经生成了一个尺度空间并使用该尺度空间来计算高斯差分。然后将它们用于计算尺度不变的高斯近似的拉普拉斯算子。
在这里插入图片描述
图像中的一个像素与其 8 个相邻像素以及下一尺度中的 9 个像素和先前尺度中的 9 个像素进行比较。这样,总共进行了 26 次检查。如果是局部极值,则是潜在的关键点。这基本上意味着关键点在该尺度中得到了最好的表示。

7.3关键点定位

上一步生成了很多关键点。其中一些位于边缘,或者它们没有足够的对比度。在这两种情况下,它们都没有特征那么有用。所以我们丢弃它们。该方法类似于 Harris Corner Detector 中用于去除边缘特征的方法。对于低对比度特征,我们只需检查它们的强度。 他们使用尺度空间的泰勒级数展开来获得更准确的极值位置,如果该极值处的强度小于阈值(论文中为 0.03),则拒绝。 DoG 对边缘有较高的响应,因此边缘也需要去除。他们使用 2x2 Hessian 矩阵 (H) 来计算主曲率。
在这里插入图片描述

7.4方向分配

现在我们有了合理的关键点。它们经过测试是稳定的。我们已经知道关键点被检测到的尺度(与模糊图像的尺度相同)。我们有尺度不变性。下一件事是给每个关键点分配一个方向,使它旋转不变性。
在这里插入图片描述
根据尺度在关键点位置周围取一个邻域,并在该区域计算梯度大小和方向。创建一个方向直方图,其中 36 个 bin 覆盖 360 度。假设某个点(在“方向收集区域”中)的梯度方向为 18.759 度,那么它将进入 10-19 度 bin。添加到 bin 中的“数量”与该点的梯度大小成正比。一旦您对关键点周围的所有像素完成此操作,直方图将在某个点出现峰值。
在这里插入图片描述
取直方图中的最高峰值,任何超过 80% 的峰值也被视为计算方向。它创建具有相同位置和尺度但方向不同的关键点。它有助于匹配的稳定性。

7.5关键点描述符

此时,每个关键点都有一个位置、尺度、方向。接下来是为每个关键点的局部图像区域计算一个描述符,该描述符对于诸如视点和光照变化等变化尽可能具有高度独特性和不变性。 为此,需要在关键点周围创建一个 16x16 的窗口。它被分成 16 个 4x4 大小的子块。
在这里插入图片描述
对于每个子块,创建 8 个 bin 方向直方图。
在这里插入图片描述
4 X 4 X 8 方向给出 128 个 bin 值。它被表示为特征向量以形成关键点描述符。这个特征向量引入了一些复杂性。我们需要在最终确定指纹之前摆脱它们。

  • 旋转依赖:特征向量使用梯度方向。显然,如果你旋转图像,一切都会改变。所有的渐变方向也会改变。为了实现旋转独立性,关键点的旋转要从每个方向上减去。因此,每个梯度方向都是相对于关键点的方向。
  • 光照依赖性:如果我们阈值很大,我们就可以实现光照独立。因此,任何大于 0.2 的数(128 个)都更改为 0.2。这个合成特征向量再次被归一化。现在你有了一个与光照无关的特征向量!

7.6关键点匹配

通过识别它们最近的邻居来匹配两个图像之间的关键点。但在某些情况下,第二个最接近的匹配可能与第一个非常接近。它可能由于噪音或其他一些原因而发生。在这种情况下,采用最近距离与第二最近距离的比率。如果大于 0.8,则拒绝。根据论文,它消除了大约 90% 的错误匹配,而仅丢弃了 5% 的正确匹配。

7.7代码实现

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
"""
@File    : sift.py
@Time    : 2021/10/13 13:01
@Author  : David
@Software: PyCharm
"""
# SIFT (Scale-Invariant Feature Transform)

# 导入库函数与显示图像

import cv2
import matplotlib.pyplot as plt
import numpy as np

# 导入图像
image1 = cv2.imread('bird.png')

# 将训练图像转换为RGB
training_image = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)

# 将训练图像转换为灰度
training_gray = cv2.cvtColor(training_image, cv2.COLOR_RGB2GRAY)

# 通过添加缩放不变性和旋转不变性来创建测试图像
test_image = cv2.pyrDown(training_image)
test_image = cv2.pyrDown(test_image)
num_rows, num_cols = test_image.shape[:2]

rotation_matrix = cv2.getRotationMatrix2D((num_cols / 2, num_rows / 2), 30, 1)
test_image = cv2.warpAffine(test_image, rotation_matrix, (num_cols, num_rows))

test_gray = cv2.cvtColor(test_image, cv2.COLOR_RGB2GRAY)

# 显示训练图像和测试图像
fx, plots = plt.subplots(1, 2, figsize=(20, 10))

plots[0].set_title("Training Image")
plots[0].imshow(training_image)

plots[1].set_title("Testing Image")
plots[1].imshow(test_image)
# 检测关键点并创建描述符

sift = cv2.xfeatures2d.SIFT_create()
train_keypoints, train_descriptor = sift.detectAndCompute(training_gray, None)
test_keypoints, test_descriptor = sift.detectAndCompute(test_gray, None)

keypoints_without_size = np.copy(training_image)
keypoints_with_size = np.copy(training_image)

cv2.drawKeypoints(training_image, train_keypoints, keypoints_without_size, color=(0, 255, 0))

cv2.drawKeypoints(training_image, train_keypoints, keypoints_with_size,
                  flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# 显示图像有和没有关键点大小
fx, plots = plt.subplots(1, 2, figsize=(20, 10))

plots[0].set_title("Train keypoints With Size")
plots[0].imshow(keypoints_with_size, cmap='gray')

plots[1].set_title("Train keypoints Without Size")
plots[1].imshow(keypoints_without_size, cmap='gray')

# 打印训练图像中检测到的关键点个数
print("Number of Keypoints Detected In The Training Image: ", len(train_keypoints))

# 打印查询图像中检测到的关键点数量
print("Number of Keypoints Detected In The Query Image: ", len(test_keypoints))

# 关键点匹配


# 创建一个Brute Force Matcher对象。
bf = cv2.BFMatcher(cv2.NORM_L1, crossCheck=False)

# 对训练图像和测试图像的SIFT描述子进行匹配
matches = bf.match(train_descriptor, test_descriptor)

# 距离较短的是我们想要的。
matches = sorted(matches, key=lambda x: x.distance)

result = cv2.drawMatches(training_image, train_keypoints, test_gray, test_keypoints, matches, test_gray, flags=2)

# 显示最佳匹配点
plt.rcParams['figure.figsize'] = [14.0, 7.0]
plt.title('Best Matching Points')
plt.imshow(result)
plt.show()

# 打印训练图像和查询图像之间的匹配点总数
print("\nNumber of Matching Keypoints Between The Training and Query Images: ", len(matches))
# Number of Keypoints Detected In The Training Image:  500
# Number of Keypoints Detected In The Query Image:  108
#
# Number of Matching Keypoints Between The Training and Query Images:  47

  • 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
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99

在这里插入图片描述
在这里插入图片描述

8.SURF(Speeded Up Robust Features)

SURF 方法(加速鲁棒特征)是一种快速且鲁棒的算法,用于图像的局部、相似性不变表示和比较。 SURF 方法的主要在于它使用盒式滤波器快速计算算子,从而实现跟踪和对象识别等实时应用。本文中描述的 SURF 框架是基于 Ph.D. H. Bay [ETH Zurich, 2009] 的论文,更具体地说是 H. Bay、A. Ess、T. Tuytelaars 和 L. Van Gool 的论文。
SURF由两个步骤组成:

  • 特征提取
  • 特征描述

8.1特征提取

兴趣点检测方法使用非常基本的 Hessian 矩阵近似。

8.1.1积分图像

积分图像或 * 总面积表 * 于 1984 年推出。 积分图像用作计算给定图像中的值(像素值)总和的一种快速有效的方法 - 或网格的矩形子集(给定的图像)。它也可以或主要用于计算给定图像内的平均强度。

它们允许快速计算盒型卷积滤波器。积分图像 I ∑ ( x ) I_{∑}(x) I(x)表示在位置 x = (x,y)ᵀ 处输入图像I在原点与x形成的矩形区域内的所有像素之和。
在这里插入图片描述
计算 I ∑ I_{∑} I后,只需四次操作即可计算任何矩形区域的强度总和,而与其大小无关。

8.1.2基于 Hessian 矩阵的兴趣点

Surf 之所以使用 Hessian 矩阵,是因为它在计算时间和精度方面表现良好。不同于使用不同的度量来选择位置和尺度(Hessian-Laplace 检测器),surf 依赖于 Hessian 矩阵的行列式。给定一个像素,该像素的 Hessian 类似于:
在这里插入图片描述
为了适应任何尺度,我们通过高斯核对图像进行滤波,因此给定点 X = (x, y),x 在尺度 σ 处的 Hessian 矩阵 H(x, σ) 定义为:
在这里插入图片描述
其中 L x x ( x , σ ) L_{xx}(x, σ) Lxx(x,σ) 是高斯二阶导数与图像 I 在点 x 的卷积,对于 L x y ( x , σ ) L_{xy}(x, σ) Lxy(x,σ) L y y ( x , σ ) L_{yy}(x, σ) Lyy(x,σ)也是如此。高斯分布是尺度空间分析的最佳选择,但在实践中,它们必须被离散化和裁剪。这导致在图像围绕 π /4 的奇数倍旋转时的可重复性损失。这种弱点通常适用于基于 Hessian 的检测器。尽管如此,检测器仍然表现良好,性能的轻微下降并没有超过离散化和裁剪带来的快速卷积的优势。

为了计算 Hessian 矩阵的行列式,首先我们需要应用高斯核的卷积,然后是二阶导数。Lowe 在 LoG 逼近 (SIFT) 方面取得成功之后,SURF 使用盒式滤波器进一步推动了逼近(卷积和二阶导数)。这些近似的二阶高斯导数可以使用积分图像以非常低的计算成本进行评估,并且与大小无关,这也是 SURF 快速的部分原因。

在这里插入图片描述
上图中的 9 × 9 盒式滤波器是 σ = 1.2 的高斯二阶导数的近似值。我们用 Dxx、Dyy 和 Dxy 表示这些近似值。现在我们可以将 Hessian(近似值)的行列式表示为:
在这里插入图片描述

8.1.3尺度空间表示

尺度空间通常实现为图像金字塔。图像用高斯函数反复平滑并随后进行下采样,以实现更高级别的金字塔。由于使用了盒式过滤器和积分图像,surf 不必将相同的过滤器迭代应用于先前过滤层的输出,而是可以直接在原始图像上以完全相同的速度应用任何大小的过滤器,并且甚至是并行的。因此,通过放大过滤器尺寸(9×9 → 15×15 → 21×21 → 27×27 等)而不是迭代地减小图像尺寸来分析尺度空间。因此,对于每个新octave,滤波器大小增加一倍,同时用于提取兴趣点 (σ) 的采样间隔也可以加倍,这允许以恒代价放大滤波器。为了定位图像和尺度上的兴趣点,应用了 3 × 3 × 3 邻域中的非极大值抑制。
在这里插入图片描述

8.2特征描述

SURF 描述符的创建分两步进行。第一步包括根据关键点周围圆形区域的信息确定可重现的方向。然后,我们构建一个与所选方向对齐的方形区域,并从中提取 SURF 描述符。

8.2.1方向分配

为了对旋转保持不变,surf 尝试为兴趣点确定可重复的方向。为了实现这一点:

  • Surf 首先计算 x 和 y 方向上的 Haar 小波响应,这是在关键点周围半径为 6s 的圆形邻域中,s 是检测到关键点的尺度。此外,采样步骤与尺度相关并选择为 s,并且在当前尺度 s 计算小波响应。因此,在高尺度下,小波的大小很大。因此再次使用积分图像进行快速过滤。
  • 然后计算一个扫描区域的垂直和水平小波响应的和,然后改变扫描方向(加上π/3),重新计算,直到找到和值最大的方向,这个方向就是特征描述子的主要方向。
    在这里插入图片描述
8.2.1描述符组件

现在是提取描述符的时候了。

  • 第一步包括构建一个以关键点为中心并沿着我们上面已经得到的方向定向的方形区域。这个窗口的大小是20s。
  • 然后将该区域有规律地分成较小的 4 × 4 方形子区域。对于每个子区域,我们在 5×5 规则间隔的样本点上计算一些简单的特征。为简单起见,我们称 dx 为水平方向的 Haar 小波响应,称 dy 为垂直方向的 Haar 小波响应(滤波器大小为 2s)。为了增加对几何变形和定位误差的鲁棒性,响应 dx 和 dy 首先用以关键点为中心的高斯 (σ = 3.3s) 加权。
    在这里插入图片描述
  • 然后,小波响应 dx 和 dy 在每个子区域上相加,形成特征向量的第一组。为了引入关于强度变化极性的信息,我们还提取了响应绝对值的总和,|dx|和|dy|。因此,每个子区域都有一个四维描述符向量 v 用于其底层强度结构 V = ( ∑ d x , ∑ d y , ∑ ∣ d x ∣ , ∑ ∣ d y ∣ ) V = (∑ dx, ∑ dy, ∑|dx|, ∑|dy|) V=(dx,dy,dx,dy)。这导致所有长度为 64 的 4×4 子区域的描述符向量(在 Sift 中,我们的描述符是 128-D 向量,所以这是 SURF 比 Sift 更快的部分原因)。

8.3代码实现

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
"""
@File    : surf.py
# SURF (Speeded-Up Robust Features)
# 新版OpenCV不能使用Surf了,我使用了opencv-contrib-python==3.4.2.16

# 导入库和显示图像

import cv2
import matplotlib.pyplot as plt
import numpy as np

# 加载图像
image1 = cv2.imread('bird.png')

# 将训练图像转换为RGB
training_image = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)

# 将训练图像转换为灰度
training_gray = cv2.cvtColor(training_image, cv2.COLOR_RGB2GRAY)

# 通过添加缩放不变性和旋转不变性来创建测试图像
test_image = cv2.pyrDown(training_image)
test_image = cv2.pyrDown(test_image)
num_rows, num_cols = test_image.shape[:2]

rotation_matrix = cv2.getRotationMatrix2D((num_cols / 2, num_rows / 2), 30, 1)
test_image = cv2.warpAffine(test_image, rotation_matrix, (num_cols, num_rows))

test_gray = cv2.cvtColor(test_image, cv2.COLOR_RGB2GRAY)

# 显示训练图像和测试图像
fx, plots = plt.subplots(1, 2, figsize=(20, 10))

plots[0].set_title("Training Image")
plots[0].imshow(training_image)

plots[1].set_title("Testing Image")
plots[1].imshow(test_image)

# 检测关键点并创建描述符

surf = cv2.xfeatures2d.SURF_create(800)

train_keypoints, train_descriptor = surf.detectAndCompute(training_gray, None)
test_keypoints, test_descriptor = surf.detectAndCompute(test_gray, None)

keypoints_without_size = np.copy(training_image)
keypoints_with_size = np.copy(training_image)

cv2.drawKeypoints(training_image, train_keypoints, keypoints_without_size, color=(0, 255, 0))

cv2.drawKeypoints(training_image, train_keypoints, keypoints_with_size,
                  flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# 显示图像有和没有关键点大小
fx, plots = plt.subplots(1, 2, figsize=(20, 10))

plots[0].set_title("Train keypoints With Size")
plots[0].imshow(keypoints_with_size, cmap='gray')

plots[1].set_title("Train keypoints Without Size")
plots[1].imshow(keypoints_without_size, cmap='gray')

# 打印训练图像中检测到的关键点个数
print("Number of Keypoints Detected In The Training Image: ", len(train_keypoints))

# 打印查询图像中检测到的关键点数量
print("Number of Keypoints Detected In The Query Image: ", len(test_keypoints))

# 关键点匹配

# 创建一个Brute Force Matcher对象。
bf = cv2.BFMatcher(cv2.NORM_L1, crossCheck=True)

# 对训练图像和测试图像的SURF描述符进行匹配
matches = bf.match(train_descriptor, test_descriptor)

# 距离较短的是我们想要的。
matches = sorted(matches, key=lambda x: x.distance)

result = cv2.drawMatches(training_image, train_keypoints, test_gray, test_keypoints, matches, test_gray, flags=2)

# 显示最佳匹配点
plt.rcParams['figure.figsize'] = [14.0, 7.0]
plt.title('Best Matching Points')
plt.imshow(result)
plt.show()

# 打印训练图像和查询图像之间的匹配点总数
print("\nNumber of Matching Keypoints Between The Training and Query Images: ", len(matches))
# Number of Keypoints Detected In The Training Image:  242
# Number of Keypoints Detected In The Query Image:  29
# 
# Number of Matching Keypoints Between The Training and Query Images:  21

  • 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
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97

在这里插入图片描述
在这里插入图片描述

9.特征匹配

Opencv 文档中提到了两种特征匹配方法。即 Brute-Force Matcher 和 FLANN Matcher。

9.1 Brute-Force Matcher

Brute-Force很简单。它采用第一组中一个特征的描述符,并使用一些距离计算(距离计算的多种方式)与第二组中的所有其他特征匹配。并返回最近的一个。

import cv2

cv_img1 = cv2.imread('bird.png', 1)
cv_img2 = cv2.imread('bird_rotated.png', 1)
orb = cv2.ORB_create(nfeatures=500)
kp1, des1 = orb.detectAndCompute(cv_img1, None)
kp2, des2 = orb.detectAndCompute(cv_img2, None)
# matcher接受normType,它被设置为cv2.NORM_L2用于SIFT和SURF, cv2.NORM_HAMMING用于ORB, FAST and BRIEF
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance)  # 前50匹配点
match_img = cv2.drawMatches(cv_img1, kp1, cv_img2, kp2, matches[:50], None)
cv2.imshow('Matches', match_img)
cv2.imwrite("Match.jpg", match_img)
cv2.waitKey()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述

在这里插入图片描述

9.2 FLANN Matcher

FLANN 我们将通过使用 ORB 检测器和校正失真图像来实现匹配操作。

import cv2
import numpy as np


def get_corrected_img(cv_img1, cv_img2):
    MIN_MATCHES = 10
    orb = cv2.ORB_create(nfeatures=500)
    kp1, des1 = orb.detectAndCompute(cv_img1, None)
    kp2, des2 = orb.detectAndCompute(cv_img2, None)
    index_params = dict(algorithm=6,
                        table_number=6,
                        key_size=12,
                        multi_probe_level=2)
    search_params = {}
    flann = cv2.FlannBasedMatcher(index_params, search_params)
    matches = flann.knnMatch(des1, des2, k=2)
    # 根据 Lowe 的比率测试来过滤良好的匹配
    good_matches = []
    for m, n in matches:
        if m.distance < 0.8 * n.distance:
            good_matches.append(m)
    if len(good_matches) > MIN_MATCHES:
        src_points = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
        dst_points = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
        print(src_points.shape)
        good_matches = sorted(good_matches, key=lambda x: x.distance)  # 前50匹配点
        match_img = cv2.drawMatches(cv_img1, kp1, cv_img2, kp2, good_matches[:50], None)
        cv2.imshow('flannMatches', match_img)
        cv2.imwrite("flannMatch.jpg", match_img)
        cv2.waitKey()
        m, mask = cv2.findHomography(src_points, dst_points, cv2.RANSAC, 5.0)
        corrected_img = cv2.warpPerspective(cv_img1, m, (cv_img2.shape[1], cv_img2.shape[0]))
        return corrected_img
    return None


if __name__ == "__main__":
    cv_im1 = cv2.imread('bird.png')
    cv_im2 = cv2.imread('bird_rotated.png')
    img = get_corrected_img(cv_im2, cv_im1)
    if img is not None:
        cv2.imshow('Corrected image', img)
        cv2.imwrite("corrected_image.jpg", img)

        cv2.waitKey()
  • 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

在这里插入图片描述
在这里插入图片描述

参考目录

https://medium.com/analytics-vidhya/computer-vision-and-deep-learning-part-4-dbaf250b8c54
https://github.com/deepanshut041/feature-detection

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/356034
推荐阅读
相关标签
  

闽ICP备14008679号