开源节流 2019-11-20
全文共6081字,预计学习时长18分钟
图源:pexels
综述
强大的SIFT技术初学者指南;
如何使用SIFT进行特征匹配;
在Python中通过动手编码展示SIFT。
简介
观察下面选取的图片,并思考他们的共同元素:
对了,就是美丽的埃菲尔铁塔!眼尖的你们一定也发现了每张图片都有一个不同的背景,是从不同的角度捕捉到的,不同的位置还有不同的物体。
相信你只需要一两秒的时间就能找出这些不同。即使图片以奇怪的角度或是只拉近了一半镜头,也能看得出来。这主要是因为你已经看过埃菲尔铁塔的图片许多次了,并且你的记忆很容易使你想起它的特征。所以说,图片的大小和角度可能会改变,但是展示的物体永远不变。
对于相同的想法,机器也进行了全方位的探索。但如果我们改变了某些东西(例如角度或比例),对它们来说识别图像中的物体却是一个挑战。好消息是——机器超级灵活,我们可以教会它们亦人的水平识别图像。
这是使用计算机视觉工作的最激动人心的部分!
本文将会讨论图像搜索算法,即定义图像上的关键特征并且能够根据这些特征搜索到一张新图片。让我们开始吧!
目录
1. SIFT介绍
2. 创建尺度空间
1. 高斯模糊软件
2. 高斯软件的不同
3. 关键信息本地化
1. 极值
2. 关键信息筛选
4. 关键信息描述符
1. 计算幅度和方向
2. 创建幅度和方向柱状图
5. 关键点描述符
6. 特征匹配
1. SIFT介绍
SIFT(Scale Invariant Feature Transform),又称尺度不变特征转换匹配算法,是在计算机视觉任务中的特征提取算法。
SIFT可以帮助定位图像中的局部特征,通常称为图像的“关键点”。这些关键点是比例尺和旋转不变量,可用于各种计算机视觉应用,例如图像匹配,物体检测,场景检测等。
还可以将通过SIFT生成的关键点用作模型训练期间的图像特征。与边缘特征或单一特征相比,SIFT特征的主要优势在于它们不受图像大小或方向的影响。
例如,这是埃菲尔铁塔的另一张图片以及它的较小版本。第一张图像的关键点与第二图像中找到的关键点相匹配。当另一幅图像中的对象稍微旋转时,两幅图像也是如此。是不是很厉害?
了解如何识别这些关键点,以及用于确保比例和旋转角度不变的技术是什么。广义上讲,整个过程可以分为四个部分:
最后,用这些关键信息进行特征匹配!
2. 创建尺度空间(Scale Space)
识别给定图像中最鲜明的特征,同时忽略任何噪点。另外,确保特征不依赖于比例。这些是关键概念,我们将逐一讨论。
2.1(Gaussian Blur)
使用高斯模糊技术(Gaussian Blur)来降低图像中的噪点。
因此,对于图像中的每个像素,高斯模糊技术会基于其相邻像素计算一个值。以下是应用高斯模糊之前和之后的图像示例。如图所示,纹理和次要细节将从图像中删除,并且仅保留诸如形状和边缘之类的相关信息:
高斯模糊成功地去除了图像中的噪点,强调了图像的重要特征。现在,需要确保这些功能一定不能与比例相关。这意味着通过创建“比例空间”来在多个比例上搜索这些功能。
比例空间是从单个图像生成的具有不同比例的图像的集合。
因此,这些模糊图像是针对多个比例创建的。要创建一组不同比例的新图像,我们需要拍摄原始图像并将其比例缩小一半。对于每个新图像,我们都会创建模糊版本。
这是一个以更好的方式理解它的例子。我们有尺寸为(275,183)的原始图像和尺寸为(138,92)的缩放图像。对于这两个图像,将创建两个模糊图像:
你可能在想——需要缩放图像多少次,并且每个缩放图像需要创建多少后续的模糊图像?理想的数量应为四个,并且对于每个均,模糊图像的数目应为五个。
2.2 高斯的差异
到目前为止,我们已经创建了多个比例的图像(通常由σ表示),并对每个图像都使用了高斯模糊,以减少图像中的噪点。接下来,尝试使用称为高斯差异(DoG)的技术来增强特征。
高斯差异是一种特征增强算法,涉及从原始图像的另一个模糊版本中减去原始图像的一个模糊版本。
DoG通过从前一个图像中以相同比例尺减去每个图像,为每个octave创建另一组图像。这是如何实现DoG的直观说明:
注:图像取自原始纸张。octave现在以垂直形式表示,以使视图更清晰
为比例空间中的图像创建DoG。看下图。在左侧,有5张图像,全部来自第一个均(因此具有相同的比例)。通过在前一个图像上应用高斯模糊来创建每个后续图像。
在右侧,通过减去连续的高斯图像生成了四个图像。结果令人惊讶!
我们已经增强了每个图像的特征。请注意,这里仅对第一个均执行此操作,但是所有均都重复了相同的操作。
现在我们有了一组新的图像,使用它来找到重要的关键点。
3. 关键信息本地化
创建图像后,下一步就是从图像中找到可用于特征匹配的重要关键点。即找到图像的局部最大值和最小值。分为两个步骤:
1.找到局部最大值和最小值
2.删除低对比度的关键点(关键点选择)
3.1 局部极值
为了定位局部最大值和最小值,仔细检查图像中的每个像素,并将其与相邻像素进行比较。
当我说“邻近”时,它不仅包括该图像的周围像素(像素所在的像素),还包括八度中上一张和下一张图像的九个像素。
这意味着将每个像素值与其他26个像素值进行比较,以确定是否为局部最大值/最小值。例如,在下图中,我们从第一个八度获得了三个图像。将标记为x的像素与相邻像素(绿色)进行比较,如果它是相邻像素中最高或最低的像素,则将其选择为关键点:
现在,我们有代表图像的潜在关键点,并且尺度不变。对选中的关键点进行最后检查,以确保这些是代表图像的最准确的关键点。
3.2 关键点筛选
哇哦!到目前为止,我们已经成功地生成了尺度不变的关键点。但是这些关键点中的一些可能对噪声没有鲁棒性。这就是为什么需要进行最终检查以确保我们拥有最准确的关键点来表示图像特征的原因。
因此,将消除对比度低或非常靠近边缘的关键点。
为了处理低对比度关键点,将为每个关键点计算二阶泰勒展开(second-order Taylor expansion)。如果结果值小于0.03(大小),则剔除该关键点。
那么,如何处理其余关键点呢?再次检查以确定位置不佳的关键点。这些是具有高边缘度但对少量噪点无鲁棒性的关键点。使用二阶Hessian矩阵来识别此类关键点。可以在这里了解其背后的数学原理。
现在我们已经执行了对比测试和边缘测试来剔除不稳定的关键点,现在为每个关键点分配一个方向值以使旋转角度不变。
4. 定位任务
在此阶段,为图像提供了一组稳定的关键点。现在,为每个关键点指定一个方以使它们不变。再次将该步骤分为两个较小的步骤:
1.计算幅度和方向
2.创建大小和方向的柱状图
4.1 计算幅度和方向
看下面的例图:
假设要找到红色像素值的大小和方向。为此,通过提取55和46与56和42之间的差值来计算x和y方向上的梯度。得出的分别是Gx = 9和Gy = 14。
一旦有了梯度,就可以使用以下公式找到幅度和方向:
Magnitude = √[(Gx)2+(Gy)2] = 16.64
Φ = atan(Gy / Gx) = atan(1.55) =57.17
大小表示像素的强度,方向表示像素的方向。
现在,假设我们具有这些大小和方向值,就可以创建柱状图。
4.2 创建大小和方向的柱状图
在x轴上,有一个角度值的区间,例如0-9、10 – 19、20-29,最大为360。由角度值为57,它会落在第6个区间中。第6个bin值与像素的大小成正比,即16.64。我们将对关键点周围的所有像素执行此操作。
这样就得到了下面的柱状图:
您可以参考本文以获得有关计算梯度,幅度,方向和绘制柱状图的详细说明:A Valuable Introduction to the Histogram of Oriented Gradients
该柱状图将在某个点达到峰值。可以看出峰值的位置将是关键点的方向。此外,如果存在另一个显着的峰值(在80 – 100%之间),则将生成另一个关键点,其大小和比例与用于生成直方图的关键点相同。角度或方向将等于具有峰值的新bin。
在这一点上,关键点的数量可能会有所增加。
5. 关键信息描述符
这是SIFT的最后一步。到目前为止,我们有稳定的关键点——不变的比例以及旋转角度。在本部分中,我们将使用相邻像素,它们的方向和大小为该关键点生成一个唯一的指纹,称为“描述符”。
另外,由于我们使用周围的像素,因此描述符对于图像的照度或亮度部分不变。
首先在关键点周围采用16×16的邻域。将该16×16区域进一步划分为4×4子块,对于这些子块中的每一个小块,使用幅度和方向生成柱状图。
在此阶段,bin的大小增加,只占用8个bin(不是36个)。每一个箭头代表8个bin,箭头的长度定义了幅度。因此,每个关键点总共有128个bin值。
例如:
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
#reading image
img1 = cv2.imread('eiffel_2.jpeg')
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
#keypoints
sift = cv2.xfeatures2d.SIFT_create()
keypoints_1, descriptors_1 = sift.detectAndCompute(img1,None)
img_1 = cv2.drawKeypoints(gray1,keypoints_1,img1)
plt.imshow(img_1)
6. 特征匹配
现在,使用SIFT功能进行特征匹配。为此,本人下载了两张埃菲尔铁塔的图像,它们是从不同位置拍摄的。你可以尝试使用任意两个图像。
如下为本人所用的两张图像:
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
# read images
img1 = cv2.imread('eiffel_2.jpeg')
img2 = cv2.imread('eiffel_1.jpg')
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
figure, ax = plt.subplots(1, 2, figsize=(16, 8))
ax[0].imshow(img1, cmap='gray')
ax[1].imshow(img2, cmap='gray')
现在,对于这两个图像,生成SIFT特征。首先,必须创建一个SIFT对象,然后使用函数detectAndCompute来获取关键点。它将返回两个值——关键点和描述符。
确定关键点并得到每个图像中找到的关键点总数:
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
# read images
img1 = cv2.imread('eiffel_2.jpeg')
img2 = cv2.imread('eiffel_1.jpg')
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
#sift
sift = cv2.xfeatures2d.SIFT_create()
keypoints_1, descriptors_1 = sift.detectAndCompute(img1,None)
keypoints_2, descriptors_2 = sift.detectAndCompute(img2,None)
len(keypoints_1), len(keypoints_2)
viewrawkeypoints_shape.py hostedwith by GitHub
283, 540
接下来,尝试将图片1中的特征与图片2中的特征进行匹配。使用BFmatcher(强力匹配)模块中的match()功能。同样,在两个图像中都匹配的要素之间画线。可以使用OpenCV中的drawMatches函数来完成。
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
# read images
img1 = cv2.imread('eiffel_2.jpeg')
img2 = cv2.imread('eiffel_1.jpg')
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
#sift
sift = cv2.xfeatures2d.SIFT_create()
keypoints_1, descriptors_1 = sift.detectAndCompute(img1,None)
keypoints_2, descriptors_2 = sift.detectAndCompute(img2,None)
#feature matching
bf = cv2.BFMatcher(cv2.NORM_L1, crossCheck=True)
matches = bf.match(descriptors_1,descriptors_2)
matches =sorted(matches, key=lambda x:x.distance)
img3 = cv2.drawMatches(img1, keypoints_1, img2, keypoints_2, matches[:50], img2, flags=2)
plt.imshow(img3),plt.show()
viewrawfeature_matching.py hostedwith by GitHub
为了清楚起见,本人在此次只绘制了50个match。你可以根据喜好增加数量要找出匹配的关键点数,可以打印出匹配变量的长度。在这种情况下,答案将是190。
尾注
本文详细讨论了SIFT特征匹配算法。这里提供了一个SIFT操作的优秀的可视化网页。你可以添加自己的图像,它也能够为这张图像创造关键信息。
留言点赞关注
我们一起分享AI学习与发展的干货
如转载,请后台留言,遵守转载规范