SIFT特征匹配与检索

1.SIFT简介

  SIFT的英文全称叫Scale-invariant feature transform,也叫尺度不变特征变换算法,是由David Lowe 先提出的,也是过去十年中最成功的图像局部描述子之一。SIFT 特征包括兴趣点检测器和描述子。SIFT 描述子具有非常强稳健性,这在很大程度上也是 SIFT 特征能够成功和流行的主要原因。自从 SIFT 特征的出现,许多其他本质上使用相同描述子的方法也相继出现。现在,SIFT 描述符经常和许多不同的兴趣点检测器相结合使用(有些情况下是区域检测器),有时甚至在整幅图像上密集地使用。SIFT 特征对于尺度、旋转和亮度都具有不变性,因此,它可以用于三维视角和噪声的可靠匹配。
 
  SIFT算法的实质是:“不同的尺度空间上查找关键点(特征点),并计算出关键点的方向” ,SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。

2.SIFT算法的特点:

1. SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性;

2. 独特性(Distinctiveness)好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配;

3. 多量性,即使少数的几个物体也可以产生大量的SIFT特征向量;

4. 高速性,经优化的SIFT匹配算法甚至可以达到实时的要求;

5. 可扩展性,可以很方便的与其他形式的特征向量进行联合。

3.四步分解SIFT算法

3.1Lowe将SIFT算法分解为如下四步:

① 尺度空间极值检测:搜索所有尺度上的图像位置。通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点。

② 关键点定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。

③ 方向确定:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性。

④ 关键点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变化。

3.2尺度空间及作用:

  尺度空间就是在多个尺度下观察目标,然后加以综合的分析、理解和应用。尺度空间中各尺度图像的 模糊程度逐渐变大,能够模拟人在距离目标由近到远时目标 在视网膜上的形成过程。 尺度越大图像越模糊。在未知的场景中,计算机视觉并不能提供物体的尺度大小,其中的一种方法是把物体不同尺度下的图像都提供给机器,让机器能够对物体在不同的尺度下有一个统一的认知。在建立统一认知的过程中,要考虑的就是在图像在不同的尺度下都存在的特征点。

3.3关键点(兴趣点)

  SIFT 特征使用高斯差分函数来定位兴趣点:D(x,σ)=[Gkσ(x)Gσ(x)]I(x)=[GkσGσ]I=IkσIσ
  其中,Gσ 是上一章中介绍的二维高斯核,Iσ 是使用 Gσ 模糊的灰度图像,κ 是决定相差尺度的常数。兴趣点是在图像位置和尺度变化下 D(x,σ) 的最大值和最小值点。这些候选位置点通过滤波去除不稳定点。基于一些准则,比如认为低对比度和位于边上的点不是兴趣点,我们可以去除一些候选兴趣点。

3.4检测关键点 

  使用开源工具包 VLFeat 提供的二进制文件来计算图像的 SIFT 特征。VLFeat 库是用 C 语言来写的,但是我们可以使用该库提供的命令行接口。也可以使用 Matlab 接口或者 Python 包装器来计算图像的 SIFT 特征,可以从http://github.com/mmmikael/vlfeat/ 下载相应的版本。

 

4.实验环境

 python+opencv+开源工具包VLFeat0.9.20

5.实验内容与小结

5.1图像数据集

  • 本数据集主要以建筑物为主体,图片都来自不同的场景,构成了一个有20张图片的数据集。

5.2SIFT特征提取

  • 实现对数据集中每张图片的SIFT特征进行提取,并展示特征点
  •    数据集中1.jpg

       数据集中3.jpg

       数据集中 5.jpg

  •    数据集中6.jpg

  •   数据集中10.jpg

  •  数据集中12.jpg

  • 分析:由上图可以看出,SIFT可以检测出较多的特征点。通过两种算法的比较可以看出,两种算法所选特征点的位置是不同的,SIFT算法比Harris算法提取图像的特征点更加准确全面精准,更具有稳健性,效果上比起Harris算法更好。但是SIFT算法的运行速度相对来说慢很多,实时性不高。
  • 代码展示:
  • # -*- coding: utf-8 -*-
    from PIL import Image
    from pylab import *
    from PCV.localdescriptors import sift
    from PCV.localdescriptors import harris
    
    # 添加中文字体支持
    from matplotlib.font_manager import FontProperties
    font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
    
    imname = 'D:/计算机视觉/SIFT特征匹配图片库/12.jpg'
    im = array(Image.open(imname).convert('L'))
    sift.process_image(imname, '12.sift')
    l1, d1 = sift.read_features_from_file('12.sift')
    
    figure()
    gray()
    subplot(131)
    sift.plot_features(im, l1, circle=False)
    title(u'SIFT特征',fontproperties=font)
    subplot(132)
    sift.plot_features(im, l1, circle=True)
    title(u'用圆圈表示SIFT特征尺度',fontproperties=font)
    
    # 检测harris角点
    harrisim = harris.compute_harris_response(im)
    
    subplot(133)
    filtered_coords = harris.get_harris_points(harrisim, 6, 0.1)
    imshow(im)
    plot([p[1] for p in filtered_coords], [p[0] for p in filtered_coords], '*')
    axis('off')
    title(u'Harris角点',fontproperties=font)
    
    show()

5.3SIFT特征匹配

  •  给定两张图片,计算其SIFT特征匹配结果
  • 分析:上图是已经将图片灰度化,实现过程是在读取两张图片的特征属性值后,进行双向匹配,即进行对于第一幅图像中的每个描述子,选取其在第二幅图像中的匹配后,再反过来匹配一次。最后将匹配结果用连线的图片展示出来。当两幅图像的SIFT特征向量生成后,用关键点特征向量的欧式距离来作为两幅图像中关键点的相似性判定度量。取图像1中的某个关键点,并找出其与图像2中欧式距离最近的前两个关键点。由上面匹配结果图可知,SIFT算法匹配出的特征点更多,这是因为SIFT算法具有尺度和旋转不变性,具有准确性和稳定性,即使两张图大小不一样、角度不一致也不会影响匹配结果。
  • 代码展示:

    from PIL import Image
    from pylab import *
    import sys
    from PCV.localdescriptors import sift
    
    
    if len(sys.argv) >= 3:
      im1f, im2f = sys.argv[1], sys.argv[2]
    else:
    #  im1f = '../data/sf_view1.jpg'
    #  im2f = '../data/sf_view2.jpg'
      im1f = 'D:/计算机视觉/SIFT特征匹配图片库/19.jpg'
      im2f = 'D:/计算机视觉/SIFT特征匹配图片库/20.jpg'
    #  im1f = '../data/climbing_1_small.jpg'
    #  im2f = '../data/climbing_2_small.jpg'
    im1 = array(Image.open(im1f).convert('L'))
    im2 = array(Image.open(im2f).convert('L'))
    
    sift.process_image(im1f, 'out_sift_1.txt')
    l1, d1 = sift.read_features_from_file('out_sift_1.txt')
    figure()
    gray()
    subplot(121)
    sift.plot_features(im1, l1, circle=False)
    
    sift.process_image(im2f, 'out_sift_2.txt')
    l2, d2 = sift.read_features_from_file('out_sift_2.txt')
    subplot(122)
    sift.plot_features(im2, l2, circle=False)
    
    #matches = sift.match(d1, d2)
    matches = sift.match_twosided(d1, d2)
    print ('{} matches'.format(len(matches.nonzero()[0])))
    
    figure()
    gray()
    sift.plot_matches(im1, im2, l1, l2, matches, show_below=True)
    show()

5.4SIFT数据集匹配

  •  给定一张输入的图片,在数据集内部进行检索,输出与其匹配最多的三张图片
  • 原图:
  •  
  • 检索结果最高匹配前三张图:
  • 分析:匹配思路是输入图片与数据集中图片两两进行特征匹配后的匹配数保存在数组中,找到与输入图片匹配特征点最多的图,最后将矩阵中数值最大的三个位置输出。通过测试可知SIFT在大量数据库中速度也非常快。匹配的结果非常精确,SIFT特征对旋转、尺度缩放、亮度变化等保持不变性,是一种非常稳定的局部特征。SIFT算法在用于图片匹配检索,具有一定的优势,它不会受图片大小和拍摄角度的影响,具备较好的稳健性,准确性高。
  • 代码展示:

    from PIL import Image
    from pylab import *
    from PCV.localdescriptors import sift
    import matplotlib.pyplot as plt


    im1f = 'D:/计算机视觉/SIFT特征匹配图片库/3.jpg'
    im1 = array(Image.open(im1f))
    sift.process_image(im1f, 'out_sift_1.txt')
    l1, d1 = sift.read_features_from_file('out_sift_1.txt')

    arr=[]
    arrHash = {}
    for i in range(1, 10):
    im2f = (r'D:/PyCharm Community Edition 2018.2.3/siftpictures/'+'B'+str(i)+'.jpg')
    im2 = array(Image.open(im2f))
    sift.process_image(im2f, 'out_sift_2.txt')
    l2, d2 = sift.read_features_from_file('out_sift_2.txt')
    matches = sift.match_twosided(d1, d2)
    length=len(matches.nonzero()[0])
    length=int(length)
    arr.append(length)
    arrHash[length]=im2f

    arr.sort()
    arr=arr[::-1]
    arr=arr[:3]
    i=0
    plt.figure(figsize=(5,12))
    for item in arr:
    if(arrHash.get(item)!=None):
    img=arrHash.get(item)
    im1 = array(Image.open(img))
    ax=plt.subplot(511 + i)
    ax.set_title('{} matches'.format(item))
    plt.axis('off')
    imshow(im1)
    i = i + 1

    plt.show()

5.5匹配地理标记图像

  • 本小节实验内容主要是在数据集中找到并匹配这些图像对之间的特征,然后使用局部描述子来匹配带有地理标记的图像。

      5.5.1提取SIFT特征

  • 先对数据集中的图像使用SIFT特征提取代码进行提取SIFT特征,并且将特征保存在和图像同名的文件路径下,但文件名是.sift,而不是.jpg。

      

  • 成功结果展示:

    

      5.5.2使用局部描述子匹配

  • 对所有的图像进行逐个匹配,将每对图像间的匹配特征数保存在matchscores数组中。

   

      5.5.3可视化连接图像

      

  •      运行结果图
  •  

     5.5.4实验分析与小结

  1. 拍摄的图像最好先压缩一下,不然要运行很久。
  2. 左边起第一类归类成功;第二类归类错误,前三张图为一类,后两张图为一类;第三类归类错误,前两张图为一类,后两张图为一类;第四类和第五类以及第六类都归类成功。可见SIFT算法在用于图片匹配时,虽然它不会受图片大小和拍摄角度的影响,准确性高。但不够稳健,仍然存在错误的匹配。
  3. 所拍摄的建筑物图像特征点周围纹理多且复杂,导致分类效果不佳。
  4. 拍摄的建筑物图像特征点周围纹理相近并且颜色相近也会导致分类错误。
  5. 由实验结果也可以看出SIFT算法虽然能够较为准确地匹配出相同场景,但仍然存在错误的匹配,某些相同场景的图片无法匹配到。

5.6 RANSAC应用

    5.6.1RANSAC算法简介

     RANSAC算法是使用尽可能少的初始数据去拟合出一个模型,通过迭代次数,逐渐扩大内点数量,找到一个更好的模型。比如说三个点就足够定义一个圆,一开始我们可以从数据集中随机选取三个点,并假设连接它们的构成的圆就是正确的模型。需要注意的是RANSAC算法第一次拟合模型-圆时,用的数据只有n=3个,但是第二次、第三次…..拟合所用的数据会更多n>3。

    5.6.2RANSAC算法讲解

      1、容限误差的确定

           样本偏差可以通过以下几个方面确定:噪声、计算出的模型、测量隐藏误差。容限误差=平均偏差+1~2个标准差。

      2、迭代次数k的确定

         E(k)=1/b=wn;  k=[log(1z)/log(1b)] ;其中,k:寻找合适的拟合模型时需要算法的迭代次数; E(k):k的期望值;b:算法迭代中,某一次拟合模型所用的点,所有点(n个点)都是符合容限误差的概率; ω:从n个点中选出来的一点是符合容限误差的点的概率( ω 是一个先验概率,可以实现确定的); a=1-b 表示所有点(n个点)中至少有一个不符合容限误差的概率; z:算法迭代中,至少有一次拟合模型所用的点都是符合容县误差的概率;

      3、阈值t的确定

         阈值t应该足够大,当内点的数量大于t时,判定拟合出的那个模型合理,否则不合理;(内点就是组成模型参数的数据,相反外点就是不适合模型的数据)。

      4、最小二乘法

         它是一种数学优化技术,原理是通过最小化误差的平方和寻找数据的最佳模型。使用尽可能多得数据去拟合出一个模型,使得尽可能多的点在模型当中,期望通过“平均”消除偏差,然而在很多实际情况下(数据中带有重大误差点),不能够得到一个好的结果。

    5.6.3RANSAC算法步骤

      1、假设我们要将P个数据点 {x1, x2,… , xn}拟合一个由至少n个点决定的模型(P≥n ,对于圆来说n=2)。

      2、设迭代计数k=1。

      3、从P中随机选取n个点拟合一个模型,记为M1。

      4、给定容限误差 ε,计算数据点{x1, x2,… , xn}中相对于模型的残差在偏差ε内的元素个数,如果内点个数大于阈值t,根据内点集合重新拟合模型(可以利用最小二乘或其变种),算法终止。

       5、设k=k+1,如果k小于预先设定的K,跳至第3步,新的内点集合和模型分别记为S1*和M1*。 否则采用具有当前内点最多的点集的模型,或者算法失败。

    5.6.4RANSAC算法应用

  • 数据集如下

  

  

     1、针对景深单一的场景(利用SIFT+RANSAC算法进行特征匹配分析,实验如下:)

  • SIFT特征匹配

  • SIFT+RANSAC特征匹配

  • 全景拼接

     2、针对景深丰富的场景

  • SIFT特征匹配

  • SIFT+RANSAC特征匹配

  • 全景拼接

    3、实验小结

       通过以上在景深单一和景深丰富两个不同的场景进行的实验中,在特征匹配过程中经过RANSAC算法比没有RANSAC算法匹配得更加准确,对比可以发现RANSAC算法可以去除大部分的错误的匹配点,但是仍然存在错误匹配点,并且也有删掉正确匹配点的可能。

另外,通过图片拼接可以看出图片拼接得很生硬,分割明显、照片有所歪斜并且物体也有所变形!

原因分析:

1、建筑物相似

2、拍照角度选取不佳

3、图像光照强度不同

4、没有得到可信的模型,迭代次数不够

    4、代码展示

# -*- coding: utf-8
from pylab import *
from numpy import *
from PIL import Image
from scipy.spatial import Delaunay
# If you have PCV installed, these imports should work
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift


featname = ['D:/计算机视觉/SIFT特征匹配图片库/4/' + str(i + 1) + '.sift' for i in range(5)]
imname = ['D:/计算机视觉/SIFT特征匹配图片库/4/' + str(i + 1) + '.jpg' for i in range(5)]

# extract features and match
l = {}
d = {}
for i in range(5):
    sift.process_image(imname[i], featname[i])
    l[i], d[i] = sift.read_features_from_file(featname[i])

matches = {}
for i in range(4):
    matches[i] = sift.match(d[i + 1], d[i])

# visualize the matches (Figure 3-11 in the book)

for i in range(4):
    im1 = array(Image.open(imname[i]))
    im2 = array(Image.open(imname[i + 1]))
    figure()
    sift.plot_matches(im2, im1, l[i + 1], l[i], matches[i], show_below=True)


# function to convert the matches to hom. points

def convert_points(j):
    ndx = matches[j].nonzero()[0]
    fp = homography.make_homog(l[j + 1][ndx, :2].T)
    ndx2 = [int(matches[j][i]) for i in ndx]
    tp = homography.make_homog(l[j][ndx2, :2].T)

    # switch x and y - TODO this should move elsewhere
    fp = vstack([fp[1], fp[0], fp[2]])
    tp = vstack([tp[1], tp[0], tp[2]])
    return fp, tp


# estimate the homographies

model = homography.RansacModel()

fp, tp = convert_points(1)
H_12 = homography.H_from_ransac(fp, tp, model)[0]  # im 1 to 2  # im1 到 im2 的单应性矩阵


fp, tp = convert_points(0)
H_01 = homography.H_from_ransac(fp, tp, model)[0]  # im 0 to 1

tp, fp = convert_points(2)  # NB: reverse order
H_32 = homography.H_from_ransac(fp, tp, model)[0]  # im 3 to 2

tp, fp = convert_points(3)  # NB: reverse order
H_43 = homography.H_from_ransac(fp, tp, model)[0]  # im 4 to 3

# warp the images
# 扭曲图像

delta = 2000  # for padding and translation 用于填充和平移

im1 = array(Image.open(imname[1]), "uint8")
im2 = array(Image.open(imname[2]), "uint8")
im_12 = warp.panorama(H_12, im1, im2, delta, delta)

im1 = array(Image.open(imname[0]), "f")
im_02 = warp.panorama(dot(H_12, H_01), im1, im_12, delta, delta)

im1 = array(Image.open(imname[3]), "f")
im_32 = warp.panorama(H_32, im1, im_02, delta, delta)

im1 = array(Image.open(imname[4]), "f")
im_42 = warp.panorama(dot(H_32, H_43), im1, im_32, delta, 2 * delta)
imsave('jmu2.jpg', array(im_42, "uint8"))
figure()
imshow(array(im_42, "uint8"))
axis('off')
show()

6.实验中遇到的问题与解决方案

  1. 运行过程中提示print错误!!原因是3.5的python的print用法需要加括号,必须修改print语法。根据提示的路径找到最终文件打开进行改正后就可以运行了。

             

             根据提示的路径找到最终文件打开进行改正后就可以运行了。

         

       2. SIFT特征进行Harris角点检测时出现empire.sift not found!! 解决方案是下载开源工具包VLFeat。

       3. 图片像素过大时,容易发生报错;可调整图片的大小或者将图片的jpg格式改为png格式,这样程序的运行速度可加快。缺点是会降低匹配值!!

       4.匹配地理标记图像时出现import pydot的错误

           解决方案:4.1 在Anaconda Prompt中先使用安装pydot:pip install pydot
                             4.2然后再下载Graphviz:graphviz-2.38.msi ,进入官网下载https://graphviz.gitlab.io/_pages/Download/Download_windows.html 下载graphviz的时候,一定要选择后缀是.msi的文件。

                            4.3下载后一直安装即可

                            4.4 然后根据安装的路径配置环境变量即可

      5.出现"dot" not found in path.的错误

        解决方案:修改self.prog = 'dot’为self.prog = ‘dot.exe’ 即可

7.实验总结

  • SIFT算法与Harris算法相比,提取图像的特征点更加准确全面精准,更具有稳健性,效果上比起Harris算法更好。但是SIFT算法的运行速度相对来说慢很多。
  • SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性。对视角变化、仿射变换、噪声也保持一定程度的稳定性。信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配。匹配速度快。
  • 图片像素过大时,容易发生报错;可调整图片的大小或者将图片的jpg格式改为png格式,这样程序的运行速度可加快,但会降低匹配值。
  • SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。
  • SIFT算法匹配出的特征点多,即使两张图大小不一样、角度不一致也不会影响匹配结果,具有准确性和稳定性。SIFT在提取方面拥有一定的优势,但是并不完美。存在实时性不高及对边缘光滑的目标无法准确提取特征点等缺点。
  • 网络不好的时候千万别暴躁,也别太自责,冷静!

 

posted @ 2020-03-08 14:49  积极废人  阅读(1661)  评论(0编辑  收藏  举报