尺度不变特征变换(SIFT)

理论部分

常用应用领域:图像拼接

  • 分别提取待拼接图片的特征点
    分别提取待拼接图片的特征点
  • 拟合透视矩阵得到不同场景下的同一物体,拼接图像
    在这里插入图片描述

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

Sift特征的特点:

  1. 对视角变化、噪声等也存在一定程度的稳定性;
  2. 独特性,信息量丰富,适用于在海量特征数据中进行快速,准确的匹配;
  3. 多量性,即使少数几个物体也可以产生大量的Sfit特征向量;

过程:
Sfit算法的实质是在不同的尺度空间上查找关键点(特征点),计算关键点的大小、方向、尺度信息,利
用这些信息组成关键点对特征点进行描述的问题。Sift所查找的关键点都是一些十分突出,不会因光照,
仿射变换和噪声等因素而变换的“稳定”特征点,如角点、边缘点、暗区的亮点以及亮区的暗点等。
匹配的过程就是对比这些特征点的过程:
在这里插入图片描述
专利问题:
SIFT函数注册了专利,2020年之前在商业用途上是收费的。在opencv > 3.4.3中,不再提供。
解决办法:
安装opencv-python 3.4.2.16
以及opencv-contrib-python 3.4.2.16
使用conda和pip安装
pip install opencv-python == 3.4.2.16
pip install opencv-contrib-python == 3.4.2.16
具体步骤:

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

尺度空间

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

SIFT–图像金字塔

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

在这里插入图片描述

SIFT–高斯金字塔

  • 主要思想是通过对原始图像进行尺度变换,获得图像多尺度下的尺度空间表示序列,对这些序列进行尺度空间主轮廓的提取,并以该主轮廓作为一种特征向量,实现边缘、角点检测不同分辨率上的关键点提取等。
  • 各尺度下图像的模糊度逐渐变大,能够模拟人在距离目标由近到远时目标物体在视网膜上的形成过程。
  • 尺度空间构建的基础是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)就可以唯一确定高斯金字塔中的一幅图像。
    尺度空间的形象表述:
    在这里插入图片描述
    SIFT–DOG金字塔
    尺度空间构建的基础是DOG金字塔,DOG金字塔构建的基础是高斯金字塔。
    差分金字塔,DOG(Difference of Gaussian)金字塔是在高斯金字塔的基础上构建起来的,其实生成高斯金字塔的目的就是为了构建DOG金字塔。
    DOG金字塔的第1组第1层是由高斯金字塔的第1组第2层减第1组第1层得到的。以此类推,逐组逐层生成每一个差分图像,所有差分图像构成差分金字塔。
    概括为DOG金字塔的第o组第l层图像是由高斯金字塔的第o组第l+1层减第o组第l层得到的
    组数和高斯金字塔相同,每组层数少1
    在这里插入图片描述
    包含大量特征点信息,但肉眼难以分辨
    在这里插入图片描述
    归一化之后:显示稳定特征信息
    在这里插入图片描述
2. 空间极值点检测(关键点的初步查探)

特征点是由DOG空间的局部极值点组成的。为了寻找DoG的极值点,每一个像素点要和它所有的相邻点比较,看其是否比它的图像域和尺度域的相邻点大或者小。
如下图,中间的检测点和它同尺度的8个相邻点和上下相邻尺度对应的9×2个点共26个点比较,以确保在尺度空间和二维图像空间都检测到极值点
在这里插入图片描述
注意,局部极值点都是在同一个组当中进行的,所以肯定有这样的问题,某一组当中的第一个图和最后一个图层没有前一张图和下一张图,那该怎么计算? 解决办法是,再用高斯模糊,在高斯金字塔多“模糊”出三张来凑数,所以在DOG中多出两张。
高斯金字塔的K值
在这里插入图片描述
s:每组图像中检测s个尺度的极值点。(实际计算时,s通常在3到5之间)
Sift算法中生成高斯金字塔的规则(M,N为原始图像的行数和列数):
设高斯金字塔共包含O组图像,每组图像由S + 3 层,
在这里插入图片描述
注:s的意思是将来我们在差分高斯金字塔中求极值点的时候,我们要求s层点。

3. 稳定关键点的精确定位
4. 稳定关键点方向信息分配

方法:获取关键点所在尺度空间的邻域,然后计算该区域的梯度和方向,根据计算得到的结果创建方向直方图。可以做如下设定:直方图的峰值为主方向的参数,其他高于主方向百分之80的方向被判定为辅助方向。
在这里插入图片描述
在这里插入图片描述
得到特征点的主方向后,对于每个特征点可以得到三个信息(x,y,σ,θ),即位置、尺度和方向。由此可以确定一个SIFT特征区域,一个SIFT特征区域由三个信息表示,中心表示特征点位置,半径表示关键点的尺度,箭头表示主方向。具有多个方向的关键点可以被复制成多份,然后将方向值分别赋给复制后的特征点,一个特征点就产生了多个坐标、尺度相等,但是方向不同的特征点。
在这里插入图片描述

5. 关键点描述

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

6. 特征点匹配

特征点的匹配是通过计算两组特征点的128维的关键点的欧式距离实现的。
欧式距离越小,则相似度越高,当欧式距离小于设定的阈值时,可以判定为匹配成功。
具体步骤:
1、分别对模板图(参考图,reference image)和实时图(观测图,observation image)建立关键点描述子集合。目标的识别是通过两点集内关键点描述子的比对来完成。具有128维的关键点描述子的相似性度量采用欧式距离。
2、匹配可采取穷举法完成。

SIFT算法进行图像分类的思路
通过比较待识别图片与训练图集中的每一张图片的sift关键点的个数,找出匹配度最高的那张图片所在的类别,则该类别就被判定为待识别图片的类别。

如,有40类图片,每类图片5张:首先计算待识别图片的特征点集,然后依次比较每一类中每一张图片与该特征点集的匹配数之和(或平均匹配数),则匹配数之和(或平均匹配数)最大的一类判定为与待识别图片为同一类。

代码部分

图像关键点提取

import cv2
import numpy as np

# 读取图像
img = cv2.imread("shangri-la.jpg")
# 转换到灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 创建SIFT对象,注意这里直接使用cv2.SIFT_create()
sift = cv2.SIFT_create()
# 检测关键点和计算描述符
keypoints, descriptor = sift.detectAndCompute(gray, None)

# 在图像上绘制关键点
img = cv2.drawKeypoints(image=img, outImage=img, keypoints=keypoints,
                        flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS,
                        color=(51, 163, 236))

# 显示图像
cv2.imshow('sift_keypoints', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

特征匹配

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

    # 从goodMatch中提取匹配的关键点索引
    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)

    # 在vis图像中绘制匹配的线条
    for (x1, y1), (x2, y2) in zip(post1, post2):
        cv2.line(vis, (x1, y1), (x2, y2), (0, 255, 0))

    # 显示匹配结果
    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_create()

# 计算两幅图像的关键点和描述符
kp1, des1 = sift.detectAndCompute(img1_gray, None)
kp2, des2 = sift.detectAndCompute(img2_gray, None)

# 创建暴力匹配器,使用L2范数
bf = cv2.BFMatcher(cv2.NORM_L2)

# 使用knn算法匹配描述符,找出每个匹配中的前两个最相似的点
matches = bf.knnMatch(des1, des2, k=2)

# 根据距离选择好的匹配
goodMatch = []
for m, n in matches:
    if m.distance < 0.50 * n.distance:
        goodMatch.append(m)

# 绘制前20个好的匹配结果
drawMatchesKnn_cv2(img1_gray, kp1, img2_gray, kp2, goodMatch[:20])

# 等待用户响应并销毁所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hanley_Yeung

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值