【中级】图像匹配技术 -- SIFT

英文原文:
https://www.analyticsvidhya.com/blog/2019/10/detailed-guide-powerful-sift-technique-image-matching-python/
边翻译边学习

前言

查看如下的图片集,思考它们之间的共同点:


是的,金碧辉煌的埃菲尔铁塔,每张图像都有不同的背景,从不同的角度拍摄,前景中也有不同的物体。我相信你可以轻而易举的弄清除每张图之间的差异。无论图像是以任何角度旋转、放大到仅显示一半的铁塔都没有关系。这主要是因为您见过埃菲尔铁塔的图像(甚至亲身见过)并且对它的特征有所了解。我们自然明白图像的比例或角度可能会改变,但图像所描述的对象是保持不变的。

但是机器却很难做到这一点。如果我们改变拍摄角度或比例,机器是很难识别图像中对象的。但我们可以教它们以近乎人类的水平识别图像。

本文中,我们将讨论一种图像匹配算法,该算法可识别图像中的关键特征,并将这些特征与同一对象的新图像进行匹配。

SIFT简介

SIFT(Scale Invariant Feature Transform),是计算机视觉中的一种特征检测的算法。SIFT 有助于定位图像中的局部特征,通常称为图像的 Keypoints。这些 关键点(Keypoints)是缩放和旋转不变的,可用于各种计算机视觉应用,如图像匹配、对象检测、场景检测等。我们还可以在模型训练期间使用 SIFT 生成的关键点作为图像的特征。 SIFT 特征相对于边缘特征或 hog 特征,它的主要优势在于它们不受图像大小或方向的影响

例如,这是埃菲尔铁塔及其较小版本的另一张图片。第一张图像中对象的关键点与第二张图像中发现的关键点相匹配。当另一个图像中的对象稍微旋转时,两个图像仍然拥有匹配的关键点:


让我们开始了解这些关键点是如何识别的,以及用于确保尺度和旋转不变性的技术是什么。广义上讲,整个过程可以分为4个部分:

  • Constructing a Scale Space:确保特征与尺度无关
  • Keypoint Localization:确定合适的特征或关键点
  • Orientation Assignment:确保关键点是旋转不变的
  • Keypoint Descriptor:为每个关键点分配一个唯一的指纹

最后,我们可以使用这些关键点进行特征匹配!

Constructing the Scale Space

我们需要在忽略任何噪声的同时识别给定图像中最明显的特征。此外,我们还需要确保特征不依赖于尺度。

我们使用高斯模糊技术(Gaussian Blurring technique)来减少图像中的噪声。

对于图像中的每个像素,Gaussian Blurring 都会根据其相邻像素计算一个值。下面是应用 Gaussian Blurring 前后的图像示例。如您所见,纹理和次要细节已从图像中删除,仅保留形状和边缘等相关信息:


Gaussian Blurring 成功去除了图像中的大多数噪声,从而突出了图像的重要特征。现在,我们需要确保这些特征不能依赖于尺度。这意味着我们将通过创建 scale space 在多个尺度上搜索这些特征。

尺度空间是从单个图像生成的具有不同尺度的图像的集合

因此,这些模糊图像是为多个尺度创建的。要创建一组不同比例的新图像,我们将采用原始图像并将比例缩小一半。对于每个新图像,我们将创建如上所示的模糊版本。

这是一个可以更好地理解它的示例。我们有尺寸为 (275, 183) 的原始图像和尺寸为 (138, 92) 的缩放图像。对于这两个图像,创建了两个模糊图像:


您可能会好奇,我们需要缩放图像多少次,以及需要为每个缩放图像创建多少个模糊图像呢?理想的阶数应该是四个,对于每阶,模糊图像的数量应该是五个。


Difference of Gaussian

到目前为止,我们已经创建了多个尺度(通常用 σ 表示)的图像,并对每个尺度使用高斯模糊来减少图像中的噪声。接下来,我们将尝试使用一种称为Difference of Gaussians (高斯差分或 DoG) 的技术来增强特征。

高斯差分是一种特征增强算法,涉及从原始图像的一个模糊版本减去另一个原始图像的模糊版本。DoG 为每个尺度创建另一组图像,方法是从相同比例的前一个图像中减去每个图像。以下是 DoG 实现方式的直观解释:


图片取自原论文。

让我们为尺度空间中的图像创建 DoG。看看下面的图表。在左侧,我们有 5 个图像,全部来自第一个 尺度(因此具有相同的比例)。每个后续图像都是通过在前一个图像上应用高斯模糊来创建的。


我们为这些图像中的每一个都提供了增强的功能

这里仅针对第一个尺度实施它,但所有尺度都会发生相同的过程。

现在我们有了一组新的图像,我们将使用它来找到重要的关键点。

Keypoint Localization

创建图像后,下一步是从图像中找到可用于特征匹配的重要关键点。这个想法是找到图像的局部最大值和最小值。这部分分为两步:

  • 找到局部最大值和最小值
    *(关键点选择)删除低对比度关键点

局部最大值和最小值

为了定位其局部最大值和最小值,我们遍历图像中的每个像素并将其与其相邻像素进行比较。此处的“相邻”不仅包括该图像的周围像素(像素所在),还包括同尺度中上一张和下一张图像的九个像素。

这意味着将每个像素值与其他 26(9+9+8) 个像素值进行比较,以确定它是否是局部最大值/最小值。例如下图所示,我们有来自第一个尺度的三个图像。标记为 x 的像素与相邻像素(绿色)进行比较,如果它是相邻像素中最高或最低的,则将其选为关键点:


我们现在有潜在的关键点来表示图像并且是尺度不变的。我们将对选定的关键点做一次检查,以确保这些关键点是表示图像的最准确的关键点。

Keypoint Selection

到目前为止,我们已经成功地生成了尺度不变的关键点。但其中一些关键点可能对噪声鲁棒性不好。这就是为什么我们需要执行最终检查以确保我们拥有最准确的关键点来表示图像特征。

因此,我们将消除对比度低或非常靠近边缘的关键点

为了处理低对比度关键点,对每个关键点计算二阶泰勒展开。如果结果值小于 0.03(幅度),则去除此关键点。

接下来,我们执行检查以识别位置不佳的关键点。这些是靠近边缘并具有高边缘响应但可能对少量噪声鲁棒性不好的关键点。二阶 Hessian 矩阵用于识别此类关键点。

现在我们已经执行了对比度测试和边缘测试来去除不稳定的关键点,我们现在将为每个关键点分配一个方向值以使旋转不变。

Orientation Assignment

在这个阶段,我们已经有一组稳定的图像关键点。我们现在将为这些关键点中的每一个分配一个方向,以便它们对旋转保持不变。我们可以再次将这一步分为两个较小的步骤:

  • 计算大小和方向
  • 创建幅度和方向的直方图

计算幅度和方向

考虑下面显示的示例图像:


假设我们想要找到红色像素值的大小和方向。为此,我们将通过取 55 & 46 和 56 & 42 之间的差来计算 x 和 y 方向的梯度。结果分别为 G_x = 9G_y = 14

一旦我们有了梯度,我们就可以使用以下公式找到大小和方向:
Magnitude = \sqrt {(G_x)2+(G_y)2} = 16.64 \\ Φ = atan(\frac {G_y}{G_x}) = atan(1.55) = 57.17

我们现在可以创建一个直方图,因为我们有这些像素的大小和方向值。

为幅度和方向创建直方图

x 轴上代表角度值的桶,例如 0-9、10 – 19、20-29,直到 360。由于我们的角度值为 57,它将落在第 6 个 桶。第 6 个 桶 值将与像素的大小成正比,即 16.64。我们将对关键点周围的所有像素执行此操作。

这就是我们如何得到下面的直方图:


该直方图会在某个点达到峰值。我们看到峰值的 bin 是关键点的方向。此外,如果有另一个在显著的峰值(在 80-100% 之间看到),则会生成另一个关键点。

Keypoint Descriptor

这是 SIFT 的最后一步。到目前为止,我们已经有了尺度不变和旋转不变的稳定关键点。在本节中,我们将使用相邻像素、它们的方向以及大小,为该关键点生成“描述符”。

此外,由于我们使用周围的像素,描述符将基本不受图像的照明或亮度影响。

我们将首先在关键点周围取一个 16×16 的邻域。这个 16×16 块被进一步划分为 4×4 子块,对于这些子块中的每一个,我们根据幅度和方向生成直方图。


此处我们设定桶的大小为8(不是 36 )。这些箭头中的每一个都代表 一个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)

特征匹配

我们现在将使用 SIFT 特征进行特征匹配。为此,我下载了两张从不同位置拍摄的埃菲尔铁塔图像。您可以尝试使用任何您想要的两个图像。

这是我使用的两个图像:

import cv2 
import matplotlib.pyplot as plt
%matplotlib inline

# read images
img1 = cv2.imread('eiffel_2.png')  
img2 = cv2.imread('eiffel_1.png') 

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.png')  
img2 = cv2.imread('eiffel_1.png') 

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) # 283, 540

接下来,让我们尝试将图像 1 中的特征与图像 2 中的特征进行匹配。 我们将使用 BFmatcher 模块中的函数 match()。此外,我们将在两个图像中匹配的特征之间画线。这可以使用 OpenCV 中的 drawMatches 函数来完成:

import cv2 
import matplotlib.pyplot as plt
%matplotlib inline

# read images
img1 = cv2.imread('eiffel_2.png')  
img2 = cv2.imread('eiffel_1.png') 

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()

为了清楚起见,我在这里只绘制了 50 个匹配项。您可以根据自己的喜好增加数量。要找出匹配了多少关键点,我们可以打印变量匹配的长度。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容