SIFT算法实现特征匹配与地理标识(python)

1. SIFT算法

1.1 概述
SIFT(Scale-Invariant Feature Transform,尺度不变特征变换),由David G.Lowe提出,是一种计算机视觉的算法。它用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量。SIFT算法可解决的问题有目标的旋转、缩放、平移(RST)、图像仿射/投影变换(视点viewpoint)、弱光照影响(illumination)、部分目标遮挡(occlusion)、杂物场景(clutter)、噪声等。
1.2 SIFT特征检测的步骤
SIFT算法实现特征匹配与地理标识(python)_第1张图片
(1)尺度空间的极值检测:搜索所有尺度空间上的图像,通过高斯微分函数来识别潜在的对尺度和选择不变的兴趣点。
(2)特征点定位 在每个候选的位置上,通过一个拟合精细模型来确定位置尺度,关键点的选取依据他们的稳定程度。
(3)特征方向赋值 基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向,后续的所有操作都是对于关键点的方向、尺度和位置进行变换,从而提供这些特征的不变性。
(4)特征点描述 在每个特征点周围的邻域内,在选定的尺度上测量图像的局部梯度,这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变换。
1.3 SIFT算法原理
sift算法的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。这些是一些十分突出的点,不会因光照、尺度、旋转等因素的改变而消失,比如角点、边缘点、暗区域的亮点以及亮区域的暗点。

1.高斯模糊
SIFT算法是在不同的尺度空间上查找关键点,而尺度空间的获取需要使用高斯模糊来实现。高斯核是唯一可以产生多尺度空间的核,一个图像的尺度空间,L(x, y, σ) ,定义为原始图像 I(x, y)与一个可变尺度的二维高斯函数G(x, y, σ) 卷积运算。
SIFT算法实现特征匹配与地理标识(python)_第2张图片
在二维空间中,这个公式生成的曲面的等高线是从中心开始呈正态分布的同心圆,如下图所示。分布不为零的像素组成的卷积矩阵与原始图像做变换。每个像素的值都是周围相邻像素值的加权平均。原始像素的值有最大的高斯分布值,所以有最大的权重,相邻像素随着距离原始像素越来越远,其权重也越来越小。这样进行模糊处理比其它的均衡模糊滤波器更高地保留了边缘效果。
SIFT算法实现特征匹配与地理标识(python)_第3张图片

2.尺度空间
尺度空间主要思想是通过对原始图像进行尺度变换,获得图像多尺度下的空间表示。从而实现边缘、角点检测和不同分辨率上的特征提取,以满足特征点的尺度不变性。
2.构建高斯金字塔
图像的金字塔模型是指,将原始图像不断降阶采样,得到一系列大小不一的图像,由大到小,从下到上构成的塔状模型。原图像为金子塔的第一层,每次降采样所得到的新图像为金字塔的一层(每层一张图像),每个金字塔共n层。金字塔的层数根据图像的原始大小和塔顶图像的大小共同决定。
(1)构建过程:
a.对图像做高斯平滑(即不同尺度的高斯模糊);
b.对图像做降采样(增加高斯滤波,让尺度具有连续性,一幅图产生几组图像,一组图像包括几层图像)
若金字塔共o组,s层,则有:在这里插入图片描述
SIFT算法实现特征匹配与地理标识(python)_第4张图片
最后可得组内与组间尺度:SIFT算法实现特征匹配与地理标识(python)_第5张图片
i——金字塔组数
n——每一组的层数
SIFT算法实现特征匹配与地理标识(python)_第6张图片
3.高斯差分金字塔
对应DOG算子,需构建DOG金字塔,可以通过高斯差分图像看出图像上的像素值变化情况。
SIFT算法实现特征匹配与地理标识(python)_第7张图片
(1)DOG的局部极值点检测
特征点是由DOG空间的局部极值点组成的。为了寻找DOG函数的极值点,每一个像素点要和它所有的相邻点比较,看其是否比它的图像域和尺度域的相邻点大或者小。中间的检测点和它同尺度的8个相邻点和上下相邻尺度对应的9×2个点共26个点比较,以确保在尺度空间和二维图像空间都检测到极值点。
SIFT算法实现特征匹配与地理标识(python)_第8张图片
(2)删除不好的极值点
由于DOG函数在图像边缘有较强的边缘响应,因此需要排除边缘响应。主曲率可以通过计算在该点位置尺度的2×2的Hessian矩阵得到,导数由采样点相邻差来估计:
SIFT算法实现特征匹配与地理标识(python)_第9张图片D的主曲率和H的特征值成正比。令 α ,β为特征值,则:SIFT算法实现特征匹配与地理标识(python)_第10张图片该值在两特征值相等时达最小
在这里插入图片描述时保留关键点,反之剔除,阈值T为1.2。
(3)求出特征点的主方向
利用关键点邻域像素的梯度方向分布特性,可以为每个关键点指定方向参数方向,从而使描述子对图像旋转具有不变性。通过求每个极值点的梯度来为极值点赋予方向。
像素点的梯度表示:
在这里插入图片描述
梯度幅值:在这里插入图片描述
梯度方向:在这里插入图片描述
计算得到梯度方向后,使用直方图统计特征点邻域内像素对应的梯度方向和幅值。梯度方向的直方图的横轴是梯度方向的角度(梯度方向的范围是0-360度,直方图每36度一个柱,共10个柱,或者每45度一个柱,共8个柱),纵轴是梯度方向对应梯度幅值的累加,在直方图的峰值就是特征点的主方向。
得到特征点的主方向后,对于每个特征点可以得到三个信息(x,y,σ,θ),即位置、尺度和方向。由此可以确定一个SIFT特征区域,一个SIFT特征区域由三个值表示,中心表示特征点位置,半径表示关键点的尺度,箭头表示主方向。具有多个方向的关键点可以被复制成多份,然后将方向值分别赋给复制后的特征点,一个特征点就产生了多个坐标、尺度相等,但是方向不同的特征点。
(4)关键点描述
描述子由2×2×8维向量表征,也即是2×2个8方向的方向直方图组成。左图的种子点由8×8单元组成。每一个小格都代表了特征点邻域所在的尺度空间的一个像素,箭头方向代表了像素梯度方向,箭头长度代表该像素的幅值。然后在4×4的窗口内计算8个方向的梯度方向直方图。绘制每个梯度方向的累加可形成一个种子点,如右图所示:一个特
征点由4个种子点的信息所组成。
SIFT算法实现特征匹配与地理标识(python)_第11张图片
Lowe实验结果表明:描述子采用4×4×8=128维向量表征,综合效果最优(不变性与独特性)。
步骤:
校正旋转主方向,确保旋转不变性。
生成描述子,最终形成一个128维的特征向量
归一化处理,将特征向量长度进行归一化处理,进一步去除光照的影响。

(5)关键点匹配
一般都采用kd树的数据结构来完成搜索。搜索的内容是以目标图像的关键点为基准,搜索与目标图像的特征点最邻近的原图像特征点和次邻近的原图像特征点。
SIFT算法实现特征匹配与地理标识(python)_第12张图片

二、实现特征匹配

1.Harris特征匹配
Harris角点检测基本思想是从图像局部的小窗口观察图像特征(角点:窗口向任意方向的移动都导致图像灰度的明显变化)。
将图像转换为灰度图像,计算响应函数,基于响应值选取角点,最后在原始图像中覆盖绘制检测出的角点。

from pylab import *
from PIL import Image
from PCV.localdescriptors import harris

"""
Example of detecting Harris corner points (Figure 2-1 in the book).
"""
# 读入图像
im = array(Image.open('E:/image/1.jpg').convert('L'))

# 检测harris角点
harrisim = harris.compute_harris_response(im)
# Harris响应函数
harrisim1 = 255 - harrisim


gray()
#画出Harris响应图
subplot(141)
imshow(harrisim1)
print (harrisim1.shape)
axis('off')
axis('equal')

threshold = [0.01, 0.05, 0.1]
for i, thres in enumerate(threshold):
    filtered_coords = harris.get_harris_points(harrisim, 6, thres)
    subplot(1, 4, i+2)
    imshow(im)
    print (im.shape)
    plot([p[1] for p in filtered_coords], [p[0] for p in filtered_coords], '*')
    axis('off')
show()

SIFT算法实现特征匹配与地理标识(python)_第13张图片
1.2 两图片之间寻找匹配点
在每个点上加入描述子信息,获取图像像素块,并使用归一化的互相关矩阵来比较像素块。

from pylab import *
from PIL import Image
from PCV.localdescriptors import harris
from PCV.tools.imtools import imresize

"""
This is the Harris point matching example in Figure 2-2.
"""

im1 = array(Image.open("C:/Users/Administrator/Desktop/image/1.jpg").convert("L"))
im2 = array(Image.open("C:/Users/Administrator/Desktop/image/4.jpg").convert("L"))

# resize加快匹配速度
im1 = imresize(im1, (im1.shape[1]//2, im1.shape[0]//2))
im2 = imresize(im2, (im2.shape[1]//2, im2.shape[0]//2))

wid = 5
harrisim = harris.compute_harris_response(im1, 5)
filtered_coords1 = harris.get_harris_points(harrisim, wid+1)
d1 = harris.get_descriptors(im1, filtered_coords1, wid)

harrisim = harris.compute_harris_response(im2, 5)
filtered_coords2 = harris.get_harris_points(harrisim, wid+1)
d2 = harris.get_descriptors(im2, filtered_coords2, wid)

print ('starting matching')
matches = harris.match_twosided(d1, d2)

figure()
gray() 
harris.plot_matches(im1, im2, filtered_coords1, filtered_coords2, matches)
show()

SIFT算法实现特征匹配与地理标识(python)_第14张图片
由上图可看出,存在不正确的匹配,如建筑物与地面之间有连线,然而这两处并不是正确的匹配点,这是因为图像像素块的互相关矩阵具有较弱的描述性。除此之外,描述符还具有尺度不变性和旋转不变性,而Harris算法中像素块的大小也会影响对应匹配的结果。
2.sift描述子匹配

from PIL import Image
from pylab import *
import sys
from PCV.localdescriptors import sift

im1f = 'C:/Users/Administrator/Desktop/image/1.jpg'
im2f = 'C:/Users/Administrator/Desktop/image/4.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_twosided(d1, d2)
print ('{} matches'.format(len(matches.nonzero()[0])))

figure()
gray()
sift.plot_matches(im1, im2, l1, l2, matches, show_below=True)
show()

SIFT算法实现特征匹配与地理标识(python)_第15张图片
SIFT算法实现特征匹配与地理标识(python)_第16张图片
sift算法基于位置、尺度和方向信息来计算描述子,由运行结果可知,图中特征点有很多,但匹配的描述子只有一点。
比较Sift算法与Harris算法发现,Sift算法正确匹配率更高。

3.sift算法与Harris算法提取角点对比

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 = 'E:/image/3.jpg'
im = array(Image.open(imname).convert('L'))
sift.process_image(imname, 'test.sift')
l1, d1 = sift.read_features_from_file('test.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()

SIFT算法实现特征匹配与地理标识(python)_第17张图片
由上图可知,sift和harris算法所选择的特征点不相同。

4.匹配地理标记图像
以集美大学尚大楼及其周围建筑为参照物,采用15张图片进行地理标记。

from pylab import *
from PIL import Image
from PCV.localdescriptors import sift
from PCV.tools import imtools
import pydot

""" This is the example graph illustration of matching images from Figure 2-10.
To download the images, see ch2_download_panoramio.py."""

download_path = "E:/image"  # set this to the path where you downloaded the panoramio images
path = "E:/image/"  # path to save thumbnails (pydot needs the full system path)

# list of downloaded filenames
imlist = imtools.get_imlist(download_path)
nbr_images = len(imlist)

# extract features
featlist = [imname[:-3] + 'sift' for imname in imlist]
for i, imname in enumerate(imlist):
    sift.process_image(imname, featlist[i])

matchscores = zeros((nbr_images, nbr_images))

for i in range(nbr_images):
    for j in range(i, nbr_images):  # only compute upper triangle
        print ('comparing ', imlist[i], imlist[j])
        l1, d1 = sift.read_features_from_file(featlist[i])
        l2, d2 = sift.read_features_from_file(featlist[j])
        matches = sift.match_twosided(d1, d2)
        nbr_matches = sum(matches > 0)
        print ('number of matches = ', nbr_matches)
        matchscores[i, j] = nbr_matches
print ("The match scores is: \n", matchscores)

# copy values
for i in range(nbr_images):
    for j in range(i + 1, nbr_images):  # no need to copy diagonal
        matchscores[j, i] = matchscores[i, j]

#可视化

threshold = 2  # min number of matches needed to create link

g = pydot.Dot(graph_type='graph')  # don't want the default directed graph

for i in range(nbr_images):
    for j in range(i + 1, nbr_images):
        if matchscores[i, j] > threshold:
            # first image in pair
            im = Image.open(imlist[i])
            im.thumbnail((100, 100))
            filename = path + str(i) + '.png'
            im.save(filename)  # need temporary files of the right size
            g.add_node(pydot.Node(str(i), fontcolor='transparent', shape='rectangle', image=filename))

            # second image in pair
            im = Image.open(imlist[j])
            im.thumbnail((100, 100))
            filename = path + str(j) + '.png'
            im.save(filename)  # need temporary files of the right size
            g.add_node(pydot.Node(str(j), fontcolor='transparent', shape='rectangle', image=filename))

            g.add_edge(pydot.Edge(str(i), str(j)))
g.write_png('whitehouse.png')

SIFT算法实现特征匹配与地理标识(python)_第18张图片
实验结果表明,SIFT采用henssian矩阵获取图像局部最值还是十分稳定的,如图右部分,大楼在有灯与无灯情况下能够识别出两张图是同一建筑物,说明sift在一定程度上可以减少光照对图片匹配的影响。但是在求主方向阶段太过于依赖局部区域像素的梯度方向,有可能使得找到的主方向不准确,后面的特征向量提取以及匹配都严重依赖于主方向,即使不大偏差角度也可以造成后面特征匹配的放大误差,从而匹配不成功。如图中间部分,将吕振万楼与图书馆匹配在一起,对于相似建筑匹配错误。
最后,同样的景物在不同的照片中可能出现不同的形状、大小、角度、亮度,甚至扭曲;计算机视觉的知识表明通过光学镜头获取的图像,对于平面形状的两个物体它们之间可以建立射影对应,对于像人脸这种曲面物体在不同角度距离不同相机参数下获取的两幅图像,它们之间不是一个线性对应关系,就是说我们即使获得两张图像中的脸上若干匹配好的点对,还是无法从中推导出其他点的对应。

你可能感兴趣的:(SIFT算法实现特征匹配与地理标识(python))