python高斯噪声怎么去除_SIFT图像匹配及其python实现

d9733292b9b53ecb2068906d23394d96.png

1 SIFT算法基本原理

SITF算法,即尺度不变特征变换(Scale-invariant feature transform)算法,是由David G.Lowe于1999年提出,并在2004年加以完善。论文下载地址:

https://www.cs.ubc.ca/~lowe/keypoints/​www.cs.ubc.ca

Lowe将尺度的概念引入到SIFT算法中,利用SIFT方法检测图像特征点的实质就是在不同尺度空间上查找特征点,这些特征点对应的就是不同尺寸的地物。SIFT配准方法主要分为以下几个步骤:

  • DOG尺度空间的建立
  • 尺度空间中提取关键点
  • 生成特征描述子
  • 特征点匹配
  • 之后便可以进行后续的配准流程,如转换模型求解、重采样等。

1.1 DOG尺度空间的构建

图像在计算机中是以二维矩阵的方式存储的,分析图像就需要提取出图像的特征,这些特征应该是具有局部不变性的显著特征,如尺度不变和旋转不变。举个例子,当人们看一个物体时,它在距离人们10米的地方和距离人们100米的地方看到的大小是不一样,这样在视网膜上成像的图像尺度其实就发生了变化。尺度不变性就是不论是在10米还是100米处成像,都能够对图像进行清晰的辨认。图像尺度空间指的就是所有尺度的图像。SIFT算法中首先进行尺度空间的构建,提取不同尺度下的关键点,这样就可以具有尺度不变性。SIFT算法中定义图像自身和尺度空间分别为

,则尺度空间计算公式如下:

其中

指的是在
方向和
方向上进行卷积操作,
表示尺度空间坐标。高斯核是唯一产生多尺度空间的核。使用高斯模糊对图像进行高斯平滑且降低图像细节,对图像进行2*2降采样处理,即图像长宽都降低为原来的一半,如此实现图像尺度的降低。

为了检测稳定特征点的位置,SIFT算法用DOG尺度空间来代替尺度空间函数

。通过对两幅相邻高斯尺度空间图像相减,得到DOG尺度空间。用
函数来表示DOG空间,则该尺度空间的计算公式如下:

通过上式也可以看出 DOG 尺度空间中的图像可以反映图像像素的变化,有变化的地方才有特征的存在,因此利用DOG尺度空间来定位特征点。

DOG 金字塔构建如图1所示。

8866fe5a5174e10fe9f41d69adab3002.png
图1 DOG金字塔构建图

1.2 DOG尺度空间中提取特征点

尺度空间中的所有检测点都与其同尺度相邻的8个点和上下尺度各相邻的9个点进行比较,只保留局部极值点。之后去除其中不好的关键点,通过子像元插值和剔除对比度低的点以及不稳定的边缘响应点来对精化检测到的关键点。图2是极值点检测图。

68819caea9efeebefef4b441733205e9.png
图2 极值点检测

对特征点进行子像元插值,是因为特征点的寻找是在离散空间中进行,找的特征点也是离散的,但是对于连续空间来说,离散的极值点不一定就是函数的最终极值点,所以需要用插值来找到真正的极值点。

寻找到的极值点并不是图像特征点的另一种情况是,极值点过小。图3就是有效极值点和无效极值点的示意图。如果极值点与周围像素点差距过小的话,则该极值点是无效极值点。在图像某区域中,有这样一组数据,像素点的灰度值大小依次为23、25、24,则根据上述内容,会把25当做极值点,但是从视觉上,只单纯看图像来说,这组数据代表的图像区域应该是平滑的,并且25这个点与周围点的差别不是很大,很容易受到噪声的影响,所以对这类点的剔除方法是设定阈值,若检测到的极值点没有超过阈值,则把该极值点去除掉。

02b498835ac84a950431d1869596dc78.png
图3 有效极值点和无效极值点

边缘响应较强的点就是横跨边缘处有较大的主曲率且垂直边缘处有较小主曲率的点,首先边缘上的点容易出现二义性,不能确定到底属于哪一个区域,其次易受噪声影响而变得不稳定,所以边缘响应的点一定要去除掉。

主曲率可以用Hessian矩阵H表示:

fb0590801aad063632bc751f13d567ba.png

H的特征值与主曲率成正比,主曲率的变化公式如下:设

是H的特征值,
,其中
。则

通过计算特征值的比率来表示主曲率的变化。如果要去除边缘响应点的话,就需要垂直和横跨边缘方向上差别不是很大的点,如果差别很大的话,则说明是边缘响应点。因此要求 小于一定值。因为

为1时,
最小,当
增加时,
也会增大,所以只要控制
小于阈值,这样
会小于一定阈值,
也会小于一定的阈值,那么两个方向上的主曲率差别不大,就去除了边缘响应点。

因此,可以将满足以下公式的特征点保留,否则剔除掉。

1.3 生成特征点描述子

特征描述子包含了该描述子所在的尺度、描述子的位置以及描述子的方向。因此生成特征点描述子需要求出描述子的这三个值。尺度是已知的,因此本节主要说明如何求关键点的位置和方向信息。因为关键点还要满足尺度不变性以及一定的光照和视角不变性等,因此在描述关键点描述子的时候,要综合考虑关键点及其周围像素点的信息,这里的像素点应当是会影响关键点信息的像素点,即对关键点描述子生成有贡献的点。

首先计算的就是关键点和它周围像素点的梯度大小和方向;计算关键点的主方向会用到梯度直方图,直方图会统计出各个方向上的像素点的个数,像素点个数最多的那个方向就是关键点的主方向。之后将坐标轴方向和关键点主方向保持一致,便能够满足旋转不变性。

准备工作完成后,计算关键点描述子,因为描述关键点不仅与其自身有关,还与它周围的像素点有关,所以将以该特征点为中心的邻域划分成4×4的区域,即特征点周围形成了16个小区域。然后利用梯度直方图统计每个小区域内各方向像素点的个数,那么此区域中与关键点有关的信息就是各个方向的像素点数。因为共有8个方向,且每个方向的像素点数就是一种信息,因此每个区域内会出现8种信息,关键点周围一共形成了16个区域,每个区域8种信息,一共128种信息,而这些信息都要记录在关键点描述子中,所以描述子一共128维。图4是SIFT特征描述子生成图。

1e110bcff65ea9169bd1364a1b84ac8a.png
图4 SIFT特征描述子生成

1.4 特征点匹配

使用欧氏距离来判断两幅图像特征点的相似性。在匹配过程中,SIFT算法使用的是Kd-tree算法,即找出待配准图像中与参考图像特征点A距离最近的点B和距离次近的点C,则判断特征点A与B是一对匹配点的公式为:

其中d(A,B)为A和距离A最近的点B之间的距离,d(A,C)为A和距离A次近的点C之间的距离。

之后用RANSAC算法消除错误匹配点,至此SIFT特征点提取和匹配工作已经完成。RANSAC是一种随机参数估计算法,它的具体步骤为:

  1. 从初始匹配点对集中随机选取8个匹配点对作为样本初始化模型,确定一个模型的参数;
  2. 用此模型检测初始匹配点对集,找到满足该模型阈值的所有内点(符合模型的数据);
  3. 统计由该模型得到的内点数量;
  4. 重复步骤1-3,当得到的内点数量最多、误差最小时停止;
  5. 输出最多的内点集,该内点集即为经过RANSAC筛选后的匹配点对集。

2 代码实现

代码采用python语言编写,设计到的第三方库包括matplotlib、OpenCV、numpy等。OpenCV可能会遇到无法调用Sift模块问题,请参照:

故梦:python使用Opencv的Sift/Surf算法​zhuanlan.zhihu.com
0af893ce6bcbd1284a1c5c4496d5a53e.png

2.1 加载并显示图像

import matplotlib.image as mpimg
import matplotlib.pyplot as plt

image1 = mpimg.imread("01.jpg")
image2 = mpimg.imread("02.jpg")

plt.figure()
plt.imshow(image1)
plt.savefig('image1.png', dpi = 300)

plt.figure()
plt.imshow(image2)
plt.savefig('image2.png', dpi = 300)

输出:

acc4bbdc082afb24f306b9c9ca0d9ad9.png
image1.png

d9bf7364ae2f5f65be4aebad6589bdc7.png
image2.png

2.2 特征点提取&生成描述

import cv2
import time

#  计算特征点提取&生成描述时间
start = time.time()
sift = cv2.xfeatures2d.SIFT_create()
#  使用SIFT查找关键点key points和描述符descriptors
kp1, des1 = sift.detectAndCompute(image1, None)
kp2, des2 = sift.detectAndCompute(image2, None)
end = time.time()
print("特征点提取&生成描述运行时间:%.2f秒"%(end-start))

kp_image1 = cv2.drawKeypoints(image1, kp1, None)
kp_image2 = cv2.drawKeypoints(image2, kp2, None)

plt.figure()
plt.imshow(kp_image1)
plt.savefig('kp_image1.png', dpi = 300)

plt.figure()
plt.imshow(kp_image2)
plt.savefig('kp_image2.png', dpi = 300)

输出:

特征点提取&生成描述运行时间:2.60秒

e19c24923002960c1a2463e13dc045aa.png
kp_image1.png

f414d6445621720753ba550f05e6c831.png
kp_image2.png
#  查看关键点
print("关键点数目:", len(kp1))

for i in range(2):
    print("关键点", i)
    print("数据类型:", type(kp1[i]))
    print("关键点坐标:", kp1[i].pt)
    print("邻域直径:", kp1[i].size)
    print("方向:", kp1[i].angle)
    print("所在的图像金字塔的组:", kp1[i].octave)
    print("================")

#  查看描述
print("描述的shape:", des1.shape)
for i in range(2):
    print("描述", i)
    print(des1[i])

输出:

关键点数目: 19158
关键点 0
数据类型: <class 'cv2.KeyPoint'>
关键点坐标: (3.075269937515259, 1546.7645263671875)
邻域直径: 2.406801223754883
方向: 77.8153076171875
所在的图像金字塔的组: 4457215
================
关键点 1
数据类型: <class 'cv2.KeyPoint'>
关键点坐标: (3.701429605484009, 1531.2259521484375)
邻域直径: 2.497684955596924
方向: 212.90887451171875
所在的图像金字塔的组: 7144191
================
描述的shape: (19158, 128)
描述 0
[  1.   0.   1.   1.   0.   7.  12.   7.  49.   1.   1.   0.   0.   0.
   8.  68.  28.   0.   0.   0.   1.  76. 106.  69.  16.   9.   0.   0.
   0.  79.  46.   2.   0.   0.   0.   0.   2.  83.  82.   6. 151.   4.
   0.   0.   0.   6.  58.  75. 151.  29.   0.   0.   0.  93. 124. 109.
  15.   3.   0.   0.   0. 151. 134.   8.   0.   0.   0.   0.   7.  53.
  50.   0.  54.   2.   0.   0.   0.   9.  87.  50. 151.  41.   0.   0.
   0.   8.  12.  54.  79.   8.   0.   0.   4. 151.  29.  14.   0.   0.
   0.   0.   0.   0.   0.   0.   2.   0.   0.   0.   0.   0.   1.   2.
  31.   0.   0.   0.   0.   0.   0.   7.  15.   0.   0.   0.   2.  17.
   1.   5.]
描述 1
[ 76.  38.   0.   0.   0.   0.   0.   0. 151.  66.   1.   1.   1.   0.
   0.   5.  31.   8.   1.   8.   3.   0.   0.   1.   0.   0.   0.   0.
   0.   0.   0.   0.  86.  14.   0.   0.   1.   2.   1.   4. 151.  41.
   9.  18.  13.   0.   0.  22.  43.   8.   9. 137. 101.   0.   0.   5.
   0.   0.   0.  15.   7.   0.   0.   0.  59.  13.   3.   1.   0.   2.
   3.   8. 151.  98. 116.  51.   3.   0.   0.   7.  14.  11.  88. 151.
  43.   1.   9.  77.   1.   0.   0.  18.   8.   0.  24. 120.   9.   8.
   5.   3.   0.   0.   8.   4.  24.  14.  38.  21.   1.   0.   0.  51.
  59.   2.  18.  11.   1.   0.   9. 151.   5.   0.   2.  21.  12.   2.
  27. 151.]

2.3 画出对应匹配

ratio = 0.85

#  计算匹配点匹配时间
start = time.time()

#  K近邻算法求取在空间中距离最近的K个数据点,并将这些数据点归为一类
matcher = cv2.BFMatcher()
raw_matches = matcher.knnMatch(des1, des2, k = 2)
good_matches = []
for m1, m2 in raw_matches:
    #  如果最接近和次接近的比值大于一个既定的值,那么我们保留这个最接近的值,认为它和其匹配的点为good_match
    if m1.distance < ratio * m2.distance:
        good_matches.append([m1])
end = time.time()
print("匹配点匹配运行时间:%.2f秒"%(end-start))

matches = cv2.drawMatchesKnn(image1, kp1, image2, kp2, good_matches, None, flags = 2)

plt.figure()
plt.imshow(matches)
plt.savefig('matches.png', dpi = 300)

输出:

匹配点匹配运行时间:0.74秒

07b9fef12f802c022d2746a9244d66dc.png
matches.png
#  匹配对的数目
print("匹配对的数目:", len(good_matches))
for i in range(2):
    print("匹配", i)
    print("数据类型:", type(good_matches[i][0]))
    print("描述符之间的距离:", good_matches[i][0].distance)
    print("查询图像中描述符的索引:", good_matches[i][0].queryIdx)
    print("目标图像中描述符的索引:", good_matches[i][0].trainIdx)
    print("================")

输出:

匹配对的数目: 2594
匹配 0
数据类型: <class 'cv2.DMatch'>
描述符之间的距离: 248.94175720214844
查询图像中描述符的索引: 6
目标图像中描述符的索引: 627
================
匹配 1
数据类型: <class 'cv2.DMatch'>
描述符之间的距离: 43.931766510009766
查询图像中描述符的索引: 18
目标图像中描述符的索引: 815
================

2.4 图像匹配

import numpy as np

#  单应性矩阵有八个参数,每一个对应的像素点可以产生2个方程(x一个,y一个),那么需要四个像素点就能解出单应性矩阵
if len(good_matches) > 4:
    #  计算匹配时间
    start = time.time()
    ptsA= np.float32([kp1[m[0].queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
    ptsB = np.float32([kp2[m[0].trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
    ransacReprojThreshold = 4
    #  单应性矩阵可以将一张图通过旋转、变换等方式与另一张图对齐
    H, status =cv2.findHomography(ptsA,ptsB,cv2.RANSAC,ransacReprojThreshold);
    imgOut = cv2.warpPerspective(image2, H, (image1.shape[1],image1.shape[0]),flags=cv2.INTER_LINEAR + cv2.WARP_INVERSE_MAP)
    end = time.time()
    print("匹配运行时间:%.2f秒"%(end-start))
    
    plt.figure()
    plt.imshow(image1)
    plt.figure()
    plt.imshow(image2)
    plt.figure()
    plt.imshow(imgOut)
    plt.savefig('imgOut.png', dpi = 300)

输出:

匹配运行时间:0.06秒

392b0770788c818d02eb298c9c03dc8c.png
imgOut.png

参考文献

  1. Lowe D G. Distinctive image features from scale-invariant keypoints[J]. International Journal of Computer Vision, 2004, 60(2): 91–110.
  2. 祁曦. 基于改进SIFT和改进K-means的遥感图像配准算法[D].华东师范大学,2018.
  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
未使用包,python源码实现SIFT 尺度不变特征转换(Scale-invariant feature transform或SIFT)是一种电脑视觉的算法用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量,此算法由 David Lowe在1999年所发表,2004年完善总结。 其应用范围包含物体辨识、机器人地图感知与导航、影像缝合、3D模型建立、手势辨识、影像追踪和动作比对。 此算法有其专利,专利拥有者为英属哥伦比亚大学。 局部影像特征的描述与侦测可以帮助辨识物体,SIFT 特征是基于物体上的一些局部外观的兴趣点而与影像的大小和旋转无关。对于光线、噪声、些微视角改变的容忍度也相当高。基于这些特性,它们是高度显著而且相对容易撷取,在母数庞大的特征数据库中,很容易辨识物体而且鲜有误认。使用 SIFT特征描述对于部分物体遮蔽的侦测率也相当高,甚至只需要3个以上的SIFT物体特征就足以计算出位置与方位。在现今的电脑硬件速度下和小型的特征数据库条件下,辨识速度可接近即运算。SIFT特征的信息量大,适合在海量数据库中快速准确匹配。 SIFT算法的特点有: 1. SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性; 2. 独特性(Distinctiveness)好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配; 3. 多量性,即使少数的几个物体也可以产生大量的SIFT特征向量; 4. 高速性,经优化的SIFT匹配算法甚至可以达到实的要求; 5. 可扩展性,可以很方便的与其他形式的特征向量进行联合。 SIFT算法可以解决的问题: 目标的自身状态、场景所处的环境和成像器材的成像特性等因素影响图像配准/目标识别跟踪的性能。而SIFT算法在一定程度上可解决: 1. 目标的旋转、缩放、平移(RST) 2. 图像仿射/投影变换(视点viewpoint) 3. 光照影响(illumination) 4. 目标遮挡(occlusion) 5. 杂物场景(clutter) 6. 噪声 SIFT算法的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。 Lowe将SIFT算法分解为如下四步: 1. 尺度空间极值检测:搜索所有尺度上的图像位置。通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点。 2. 关键点定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。 3. 方向确定:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性。 4. 关键点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变化。 本文沿着Lowe的步骤,参考Rob Hess及Andrea Vedaldi源码,详解SIFT算法实现过程。 未使用包,python源码实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值