opencv:SIFT——尺度不变特征变换

SIFT概念:

Sift(尺度不变特征变换),全称是Scale Invariant Feature Transform Sift提取图像的局部特征,在尺度空间寻找极值点,并提取出其位置、尺度、方向信息。
Sfit的应用范围包括 物体辨别、机器人地图感知与导航、影像拼接、3D模型建立、手势识别、影像追踪等。

Sift特征的特点:

1.对旋转、尺度缩放、亮度变化保持不变性,对视角变化、噪声等也存在一定程度的稳定性;
2.独特性,信息量丰富,适用于在海量特征数据中进行快速,准确的匹配;
3.多量性,即使少数几个物体也可以产生大量的Sfit特征向量;
4.可扩展性,可以很方便的与其他形式的特征向量进行联合;

Sfit算法的实质:

Sfit算法的实质是在不同的尺度空间上查找关键点(特征点),计算关键点的大小、方向、尺度信息,利 用这些信息组成关键点对特征点进行描述的问题。Sift所查找的关键点都是一些十分突出,不会因光照, 仿射变换和噪声等因素而变换的“稳定”特征点,如角点、边缘点、暗区的亮点以及亮区的暗点等。 匹配的过程就是对比这些特征点的过程:
在这里插入图片描述

SIFT特征提取和匹配具体步骤:

1.生成高斯差分金字塔(DOG金字塔),尺度空间构建
2.空间极值点检测(关键点的初步查探)
3. 稳定关键点的精确定位
4. 稳定关键点方向信息分配
5. 关键点描述
6. 特征点匹配

1. 生成高斯差分金字塔(DOG金字塔),尺度空间构建:

尺度空间:
尺度空间,在摄像头中,计算机无法分辨一个景物的尺度信息。而人眼不同,除了人大脑里已经对物体 有了基本的概念(例如正常人在十几米外看到苹果,和在近距离看到苹果,都能认出是苹果)以外,人 眼在距离物体近时,能够获得物体足够多的特性,在距离物体远时,能够忽略细节,例如,近距离看一 个人脸能看到毛孔,距离远了看不到毛孔等等。 在图片信息当中,分辨率都是固定的,要想得到类似人眼的效果,就要把图片弄成不同的分辨率,制作 成图像金字塔来模拟人眼的功能,从而在其他图片中进行特征识别时,能够像人眼睛一样,即使要识别 的物体尺寸变大或者变小,也能够识别出来。
尺度空间即试图在图像领域中模拟人眼观察物体的概念与方法。例如:观察一颗树,关键在于我们想要 观察是树叶子还是整棵树:如果是一整棵树(相当于大尺度情况下观察),那么就应该去除图像的细节部分。 如果是树叶(小尺度情况下观察),那么就该观察局部细节特征。

图像金字塔
通俗地说,尺度空间,就相当于一个图片需要获得多少分辨率的量级。如果把一个图片从原始分辨率 不停的对其分辨率进行减少,然后将这些图片摞在一起,可以看成一个四棱锥的样式,这个东西就叫 做图像金字塔。
图像金字塔是一种以多分辨率来解释图像的结构,通过对原始图像进行多尺度像素采样的方式,生成N 个不同分辨率的图像。把具有最高级别分辨率的图像放在底部,以金字塔形状排列,往上是一系列像 素(尺寸)逐渐降低的图像,一直到金字塔的顶部只包含一个像素点的图像,这就构成了传统意义上 的图像金字塔。
在这里插入图片描述
获得图像金字塔一般包括二个步骤:
1.利用低通滤波器平滑图像 (高斯滤波)
2.对平滑图像进行抽样(采样) 有两种采样方式——上采样(分辨率逐级升高)和下采样(分辨率逐级降低)

高斯金字塔
主要思想是通过对原始图像进行尺度变换,获得图像多尺度下的尺度空间表示序列,对这些序列进 行尺度空间主轮廓的提取,并以该主轮廓作为一种特征向量,实现边缘、角点检测不同分辨率上的 关键点提取等。 各尺度下图像的模糊度逐渐变大,能够模拟人在距离目标由近到远时目标物体在视网膜上的形成过 程。尺度空间构建的基础是DOG金字塔,DOG金字塔构建的基础是高斯金字塔。
高斯金字塔式在Sift算子中提出来的概念,首先高斯金字塔并不是一个金字塔,而是有很多组(Octave) 金字塔构成,并且每组金字塔都包含若干层(Interval)。
高斯金字塔构建过程:
1.先将原图像扩大一倍之后作为高斯金字塔的第1组第1层,将第1组第1层图像经高斯卷积(其实就 是高斯平滑或称高斯滤波)之后作为第1组金字塔的第2层。
2. 将σ乘以一个比例系数k,得到一个新的平滑因子σ=kσ,用它来平滑第1组第2层图像,结果图像作为 第3层。
3. 如此这般重复,最后得到L层图像,在同一组中,每一层图像的尺寸都是一样的,只是平滑系数不 一样。它们对应的平滑系数分别为:0,σ,kσ,k2σ,k3σ……k^(L-2)σ。
4. 将第1组倒数第三层图像作比例因子为2的降采样,得到的图像作为第2组的第1层,然后对第2组的 第1层图像做平滑因子为σ的高斯平滑,得到第2组的第2层,就像步骤2中一样,如此得到第2组的L 层图像,同组内它们的尺寸是一样的,对应的平滑系数分别为:0,σ,kσ,k2σ,k3σ……k^(L-2)σ。 但是在尺寸方面第2组是第1组图像的一半。
在这里插入图片描述(高斯公式)
反复执行,就可以得到一共O组,每组L层,共计O
L个图像,这些图像一起就构成了高斯金字塔:
注意:
• 在同一组内,不同层图像的尺寸是一样的,后一层图像的 高斯平滑因子σ是前一层图像平滑因子的k倍;
• 在不同组内,后一组第一个图像是前一组倒数第三个图像 的二分之一采样,图像大小是前一组的一半;
在这里插入图片描述
在这里插入图片描述
从上图的实现结果可以发现:图像越往金字塔上层,越模糊

构建尺度空间
在高斯金字塔中一共生成O组L层不同尺度的图像,这两个量合起来(O,L)就构成了高斯金字塔的尺 度空间,也就是说以高斯金字塔的组O作为二维坐标系的一个坐标,不同层L作为另一个坐标,则给定 的一组坐标(O,L)就可以唯一确定高斯金字塔中的一幅图像。 尺度空间的形象表述:
• 图中尺度空间中k前的系数n表示的是第一组 图像尺寸是当前组图像尺寸的n倍。
在这里插入图片描述
SIFT算法在构建尺度空间时候采取高斯核函数进行滤波,使原始图像保存最多的细节特征,经过高斯滤波 后细节特征逐渐减少来模拟大尺度情况下的特征表示。 利用高斯核函数进行滤波的主要原因有两个:
(1)高斯核函数是唯一的尺度不变核函数。
(2)DoG核函数可以近似为LoG函数,这样可以使特征提取更加简单。 其实尺度空间图像生成就是当前图像与不同尺度核参数σ进行卷积运算后产生的图像。 L(x, y, σ) ,定义为原始图像I(x, y)与一个可变尺度的2维高斯函数G(x, y, σ) 卷积运算。
在这里插入图片描述
尺度空间构建的基础是DOG金字塔,DOG金字塔构建的基础是高斯金字塔。

差分金字塔,DOG(Difference of Gaussian)金字塔是在高斯金字塔的基础上构建起来的, 其实生成高斯金字塔的目的就是为了构建DOG金字塔。 DOG金字塔的第1组第1层是由高斯金字塔的第1组第2层减第1组第1层得到的。
以此类推, 逐组逐层生成每一个差分图像,所有差分图像构成差分金字塔。概括为DOG金字塔的第o组 第l层图像是由高斯金字塔的第o组第l+1层减第o组第l层得到的。
在这里插入图片描述
但是实际结果:在这里插入图片描述
原因就是差值很小,肉眼很难看见。
归一化之后:
在这里插入图片描述

2.空间极值点检测(关键点的初步查探)

特征点是由DOG空间的局部极值点组成的。为了寻找DoG函数的极值点,每一个像素点要和它所有的 相邻点比较,看其是否比它的图像域和尺度域的相邻点大或者小。、 如下图,中间的检测点和它同尺度的8个相邻点和上下相邻尺度对应的9×2个点共26个点比较,以确保 在尺度空间和二维图像空间都检测到极值点。
在这里插入图片描述
注意,局部极值点都是在同一个组当中进行的,所以肯定有这样的问题,某一组当中的第一个图和 最后一个图层没有前一张图和下一张图,那该怎么计算? 解决办法是,在用高斯模糊,在高斯金字塔多“模糊”出三张来凑数,所以在DOG中多出两张。

高斯金字塔的k值计算:
在这里插入图片描述
s:每组图像中检测s个尺度的极值点。
(实际计算时,s通常在3到5之间) Sift算法中生成高斯金字塔的规则(M,N为原始图像的行数和列数):
在这里插入图片描述
+3就是因为在高斯金字塔要多“模糊”出三张来凑数

3. 稳定关键点的精确定位

DOG值对噪声和边缘比较敏感,所以在第2步的尺度空间中检测到的局部极值点还要经过进一步的筛选,去除不稳定和错误检测出的极值点。
利用阈值的方法来限制,在opencv中为contrastThreshold。

4. 稳定关键点方向信息分配

稳定的极值点是在不同尺度空间下提取的,这保证了关键点的尺度不变性。 为关键点分配方向信息所要解决的问题是使得关键点对图像角度和旋转具有不变性。
方法:
获取关键点所在尺度空间的邻域,然后计算该区域的梯度和方向,根据计算得到的结果创建 方向直方图,直方图的峰值为主方向的参数,其他高于主方向百分之80的方向被判定为辅助方向, 这样设定对稳定性有很大帮助。
在这里插入图片描述
这里讲解一下:每个小方格为一个像素点,通过求其周围一圈8个像素所得的梯度找其最大的梯度,为主方向,达到主方向百分之80的及以上的方向被判定为辅助方向,例如蓝色图片中,←为最大峰值,↗和↘为辅助方向。

对于任一关键点,其梯度幅值表述为:
在这里插入图片描述
梯度方向为:
在这里插入图片描述

5. 关键点描述

对于每一个关键点,都拥有位置、尺度以及方向三个信息。所以具备平移、缩放、和旋转不变性。 为每个关键点建立一个描述符,用一组向量将这个关键点描述出来,使其不随各种变化而改变,比 如光照变化、视角变化等等描述子不但包含关键点,也包括关键点周围对其有贡献的邻域点。
**描述的思路是:**对关键点周围像素区域分块,计算块内梯度直方图,生成具有独特性的向量,这个 向量是该区域图像信息的一种抽象表述。
如下图,对于22块,每块的所有像素点的灰度做高斯加权,每块最终取8个方向,即可以生成 228维度的向量,以这22*8维向量作为中心关键点的数学描述。
在这里插入图片描述
注意:Lowe实验结果表明:对于每个关键点采用4×4×8=128维向量表征,综合效果最优(不变性与独特性)。
在这里插入图片描述

6. 特征点匹配

特征点的匹配是通过计算两组特征点的128维的关键点的欧式距离实现的。 欧式距离越小,则相似度越高,当欧式距离小于设定的阈值时,可以判定为匹配成功。

具体步骤:
1、分别对模板图(参考图,reference image)和实时图(观测图,observation image)建立关键点描述 子集合。目标的识别是通过两点集内关键点描述子的比对来完成。具有128维的关键点描述子的相似性度 量采用欧式距离。
2、匹配可采取穷举法完成。

代码实现:

注意:(所以我也没有运行出来=。=)
在这里插入图片描述

完成SIFT1-5步的实现代码

import cv2
import numpy as np

img = cv2.imread("lenna.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

sift = cv2.xfeatures2d.SIFT_create()
keypoints, descriptor = sift.detectAndCompute(gray, None)

# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS对图像的每个关键点都绘制了圆圈和方向。
img = cv2.drawKeypoints(image=img, outImage=img, keypoints=keypoints,
                        flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS,
                        color=(51, 163, 236))
                        
#img=cv2.drawKeypoints(gray,keypoints,img)

cv2.imshow('sift_keypoints', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

因为我也运行不了,所以就不给结果展示了,理解代码就好。

完成SIFT全部的实现代码

import cv2
import numpy as np

#画图函数部分
def drawMatchesKnn_cv2(img1_gray,kp1,img2_gray,kp2,goodMatch):
    h1, w1 = img1_gray.shape[:2]
    h2, w2 = img2_gray.shape[:2]
 
    vis = np.zeros((max(h1, h2), w1 + w2, 3), np.uint8)
    vis[:h1, :w1] = img1_gray
    vis[:h2, w1:w1 + w2] = img2_gray
 
    p1 = [kpp.queryIdx for kpp in goodMatch]
    p2 = [kpp.trainIdx for kpp in goodMatch]
 
    post1 = np.int32([kp1[pp].pt for pp in p1])
    post2 = np.int32([kp2[pp].pt for pp in p2]) + (w1, 0)
 
    for (x1, y1), (x2, y2) in zip(post1, post2):
        cv2.line(vis, (x1, y1), (x2, y2), (0,0,255))
 
    cv2.namedWindow("match",cv2.WINDOW_NORMAL)
    cv2.imshow("match", vis)

#图像输入
img1_gray = cv2.imread("iphone1.png")
img2_gray = cv2.imread("iphone2.png")

#SIFT特征计算
sift = cv2.SIFT()
#sift = cv2.xfeatures2d.SIFT_create()
#sift = cv2.SURF()
 
kp1, des1 = sift.detectAndCompute(img1_gray, None)
kp2, des2 = sift.detectAndCompute(img2_gray, None)

#BFMatcher解决匹配
bf = cv2.BFMatcher(cv2.NORM_L2)
matches = bf.knnMatch(des1, des2, k = 2)

#阈值对比,小于阈值时才被认为是匹配
goodMatch = []
for m,n in matches:
    if m.distance < 0.50*n.distance:
        goodMatch.append(m)
 
drawMatchesKnn_cv2(img1_gray,kp1,img2_gray,kp2,goodMatch[:20])
 
cv2.waitKey(0)
cv2.destroyAllWindows()

因为我也运行不了,所以就不给结果展示了,理解代码就好。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值