赞
踩
图像的8x8像素部分被考虑,并将这个 8x8 框进一步划分为 4 个块,每个块为 4x4 维度。在每个 4x4 块内,图像梯度以向量的形式表示。通过搜索最独特或不同的特征在图像中找到关键点。这里,Key point Descriptor是由4个相邻向量组合而成。关键点描述符显示该部分图像中梯度变化的方向和幅度。对关键点周围的区域进行归一化,计算关键点区域的局部描述符。局部描述符是一个数字向量,用于描述关键点的视觉外观。
2D 特征框架的主要部分是:
如果您在较低级别看到图像,即没有关注图像的主题,图像的突出特征将是角点、边缘和像素强度变化最小的一致部分。其中,最重要的将是角点。角点是由于相邻边缘而在两个方向上发生严重梯度变化的点。因此,Opencv 提供了一些非常酷的方法来检测图像中的角点。
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)
Harris Corner Detector 是一种角点检测算子,常用于计算机视觉算法中,用于提取角点并推断图像的特征。与之前的角点检测器相比,Harris角点检测器直接考虑了角点分数与方向的差异,而不是每45度角都使用移位的patch,被证明在区分边缘和角点方面更加准确。从那时起,它得到了改进并被许多算法采用来预处理图像以供后续应用程序使用。
角点是其局部邻域位于两个主要且不同的边缘方向的点。换句话说,角可以解释为两条边的交汇处,其中边是图像亮度的突然变化。角点是图像中的重要特征,通常称为兴趣点,对平移、旋转和光照不变。
所以让我们了解为什么角被认为是更好的特征或适合补丁映射。在上图中,如果我们取平坦区域,则在任何方向都没有观察到梯度变化。同样,在边缘区域,沿边缘方向没有观察到梯度变化。所以平坦区域和边缘区域都不利于补丁匹配,因为它们不是很明显(边缘区域中沿边缘有许多相似的补丁)。而在角落区域,我们观察到所有方向的显着梯度变化。由于这个角被认为有利于补丁匹配(在任何方向上移动窗口都会产生很大的外观变化)并且通常在视点变化时更稳定。
这个想法是在图像中的每个像素 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 的特征值。因此这些特征值的值决定了区域是角、边缘还是平面。
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()
这种角点检测方法类似于Harris。 Shi-Tomasi 在他的研究论文 Good Features to Track 中提出要找到图像中的 N 个最强角点。评分函数为:
实际上,Shi-Tomasi比Harris更有效。在图形上,我们可以将此方法可视化为
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()
上面讨论的角点检测技术很好,但对于实时应用来说还不够快。作为对此的解决方案,FAST(Features from Accelerated Segment Test)算法是由 Edward Rosten 和 Tom Drummond 在 2006 年的论文“Machine learning for high-speed corner detection”中提出的(后来在 2010 年对其进行了修订)。
该算法解释如下:
该算法有一些限制。首先,对于 n<12,算法在所有情况下都不能很好地工作,因为当 n<12 时,检测到的兴趣点数量非常多。其次,查询16个像素的顺序决定了算法的速度。算法中添加了机器学习方法来处理这些问题。
检测相邻位置的多个兴趣点是另一个问题。它是通过使用非极大值抑制来解决的。
简而言之,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()
#!/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
特征点描述符是目前许多计算机视觉技术的核心,如物体识别、三维重建、图像检索和摄像机定位等。由于这些技术的应用程序必须处理更多的数据,或者在具有有限计算资源的移动设备上运行,因此对计算快、匹配快、内存高效的本地描述符的需求越来越大。
在本文中,我们使用二进制字符串作为有效的特征点描述符,称为BRIEF。在很多情况下,BRIEF 的构建和匹配速度都非常快。BRIEF 在速度和识别率方面轻松胜过其他快速描述符,例如 SURF 和 SIFT。
在检测到关键点之后,我们继续计算每一个关键点的描述符。特征描述符将感兴趣的信息编码成一系列数字,作为一种数字指纹,可以用来区分不同的特征。在像素(关键点)周围定义的邻域被称为一个patch,它是一个像素宽度和高度的正方形。
基于相对少量的成对强度比较,可以有效地对图像块进行分类。Brief将图像小块转换为二值特征向量,使它们一起可以表示一个对象。二值特征向量又称二值特征描述符,是只包含1和0的特征向量。简而言之,每个关键点由一个128-512位字符串的特征向量描述。
#!/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
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的主要贡献如下:
关键点 - 图像中特别独特的小区域。示例:像素值从亮到暗急剧变化的角点。
描述符——特征描述符是一种算法,它采用图像并输出特征描述符/特征向量。特征描述符将感兴趣的信息编码成一系列数字,并充当一种数字“指纹”,可用于区分一个特征与另一个特征。理想情况下,这些信息在图像变换下是不变的,因此即使图像以某种方式进行了变换,我们也可以再次找到该特征。
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
SIFT是尺度不变特征变换(Scale-Invariant Feature Transform)的缩写,2004年由英属哥伦比亚大学(University of British Columbia)的D.Lowe首次提出。SIFT对图像的尺度和旋转具有不变性。该算法是专利算法,因此该算法包含在OpenCV的Non-free模块中。
SIFT的主要优点是:
SIFT 是一个相当复杂的算法。 SIFT算法主要涉及四个步骤。我们将一一看到它们。
现实世界中的物体只有在一定的尺度下才有意义。你可能会在桌子上看到一块方糖。但如果观察整个银河系,它根本不存在。物体的多尺度特性在自然界中是很常见的。一个尺度空间尝试在数字图像上复制这个概念。
图像的尺度空间是一个函数L(x,y,σ),它是由不同尺度的高斯核(blur)与输入图像的卷积产生的。尺度空间被划分为多个octaves,而octaves和尺度的数量取决于原始图像的大小。所以我们生成了原始图像的几个octaves。每个octave的图像大小是前一个的一半。
在一个octave内,图像使用高斯模糊运算符逐步模糊。数学上,“模糊”被称为高斯算子和图像的卷积。高斯模糊有一个特定的表达式或“运算符”,应用于每个像素。结果是图像模糊。
G 是高斯模糊算子,I 是图像。 x,y 是位置坐标,σ 是“尺度”参数。将其视为模糊量。值越大,模糊越大。
现在我们使用这些模糊的图像来生成另一组图像,即高斯差分 (DoG)。这些 DoG 图像非常适合找出图像中有趣的关键点。 DOG是作为具有两个不同 σ 的图像的高斯模糊的差分而获得的,设它是 σ 和 kσ。这个过程是针对高斯金字塔中图像的不同octave完成的。它如下图所示:
到目前为止,我们已经生成了一个尺度空间并使用该尺度空间来计算高斯差分。然后将它们用于计算尺度不变的高斯近似的拉普拉斯算子。
图像中的一个像素与其 8 个相邻像素以及下一尺度中的 9 个像素和先前尺度中的 9 个像素进行比较。这样,总共进行了 26 次检查。如果是局部极值,则是潜在的关键点。这基本上意味着关键点在该尺度中得到了最好的表示。
上一步生成了很多关键点。其中一些位于边缘,或者它们没有足够的对比度。在这两种情况下,它们都没有特征那么有用。所以我们丢弃它们。该方法类似于 Harris Corner Detector 中用于去除边缘特征的方法。对于低对比度特征,我们只需检查它们的强度。 他们使用尺度空间的泰勒级数展开来获得更准确的极值位置,如果该极值处的强度小于阈值(论文中为 0.03),则拒绝。 DoG 对边缘有较高的响应,因此边缘也需要去除。他们使用 2x2 Hessian 矩阵 (H) 来计算主曲率。
现在我们有了合理的关键点。它们经过测试是稳定的。我们已经知道关键点被检测到的尺度(与模糊图像的尺度相同)。我们有尺度不变性。下一件事是给每个关键点分配一个方向,使它旋转不变性。
根据尺度在关键点位置周围取一个邻域,并在该区域计算梯度大小和方向。创建一个方向直方图,其中 36 个 bin 覆盖 360 度。假设某个点(在“方向收集区域”中)的梯度方向为 18.759 度,那么它将进入 10-19 度 bin。添加到 bin 中的“数量”与该点的梯度大小成正比。一旦您对关键点周围的所有像素完成此操作,直方图将在某个点出现峰值。
取直方图中的最高峰值,任何超过 80% 的峰值也被视为计算方向。它创建具有相同位置和尺度但方向不同的关键点。它有助于匹配的稳定性。
此时,每个关键点都有一个位置、尺度、方向。接下来是为每个关键点的局部图像区域计算一个描述符,该描述符对于诸如视点和光照变化等变化尽可能具有高度独特性和不变性。 为此,需要在关键点周围创建一个 16x16 的窗口。它被分成 16 个 4x4 大小的子块。
对于每个子块,创建 8 个 bin 方向直方图。
4 X 4 X 8 方向给出 128 个 bin 值。它被表示为特征向量以形成关键点描述符。这个特征向量引入了一些复杂性。我们需要在最终确定指纹之前摆脱它们。
通过识别它们最近的邻居来匹配两个图像之间的关键点。但在某些情况下,第二个最接近的匹配可能与第一个非常接近。它可能由于噪音或其他一些原因而发生。在这种情况下,采用最近距离与第二最近距离的比率。如果大于 0.8,则拒绝。根据论文,它消除了大约 90% 的错误匹配,而仅丢弃了 5% 的正确匹配。
#!/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
SURF 方法(加速鲁棒特征)是一种快速且鲁棒的算法,用于图像的局部、相似性不变表示和比较。 SURF 方法的主要在于它使用盒式滤波器快速计算算子,从而实现跟踪和对象识别等实时应用。本文中描述的 SURF 框架是基于 Ph.D. H. Bay [ETH Zurich, 2009] 的论文,更具体地说是 H. Bay、A. Ess、T. Tuytelaars 和 L. Van Gool 的论文。
SURF由两个步骤组成:
兴趣点检测方法使用非常基本的 Hessian 矩阵近似。
积分图像或 * 总面积表 * 于 1984 年推出。 积分图像用作计算给定图像中的值(像素值)总和的一种快速有效的方法 - 或网格的矩形子集(给定的图像)。它也可以或主要用于计算给定图像内的平均强度。
它们允许快速计算盒型卷积滤波器。积分图像
I
∑
(
x
)
I_{∑}(x)
I∑(x)表示在位置 x = (x,y)ᵀ 处输入图像I在原点与x形成的矩形区域内的所有像素之和。
计算
I
∑
I_{∑}
I∑后,只需四次操作即可计算任何矩形区域的强度总和,而与其大小无关。
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(近似值)的行列式表示为:
尺度空间通常实现为图像金字塔。图像用高斯函数反复平滑并随后进行下采样,以实现更高级别的金字塔。由于使用了盒式过滤器和积分图像,surf 不必将相同的过滤器迭代应用于先前过滤层的输出,而是可以直接在原始图像上以完全相同的速度应用任何大小的过滤器,并且甚至是并行的。因此,通过放大过滤器尺寸(9×9 → 15×15 → 21×21 → 27×27 等)而不是迭代地减小图像尺寸来分析尺度空间。因此,对于每个新octave,滤波器大小增加一倍,同时用于提取兴趣点 (σ) 的采样间隔也可以加倍,这允许以恒代价放大滤波器。为了定位图像和尺度上的兴趣点,应用了 3 × 3 × 3 邻域中的非极大值抑制。
SURF 描述符的创建分两步进行。第一步包括根据关键点周围圆形区域的信息确定可重现的方向。然后,我们构建一个与所选方向对齐的方形区域,并从中提取 SURF 描述符。
为了对旋转保持不变,surf 尝试为兴趣点确定可重复的方向。为了实现这一点:
现在是提取描述符的时候了。
#!/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
Opencv 文档中提到了两种特征匹配方法。即 Brute-Force Matcher 和 FLANN 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()
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()
https://medium.com/analytics-vidhya/computer-vision-and-deep-learning-part-4-dbaf250b8c54
https://github.com/deepanshut041/feature-detection
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。