SIFT(尺度不变特征变换)是过去十多年来最成功的图像局部描述子,SIFT算子具有很强的稳健性,目前SIFT算子通常和其他兴趣点检测器一起结合使用。在这里将不介绍SIFT原理,详细的介绍在可以在其他博主文章里找到,这里推荐一个比较好的文章:传送门
兴趣点检测定位:
SIFT特征使用高斯差分函数来定位兴趣点:
其中是二维高斯核,是使用高斯模糊之后的图像,κ是决定相差尺度的系数,兴趣点是在图像位置和尺度变化下D(x,σ)的最大值和最小值,这些候选位置通过滤波取出不稳定点。
描述子
上面给出的是兴趣点的位置和尺度信息,为了实现旋转不变性,基于每个点周围像素梯度的方向和大小,SIFT算子引入了参考方向。SIFT描述子使用主方向来描述方向,主方向使用方向直方图来度量。
SIFT描述子在每个像素点周围选择子区域网格,每个子区域内计算图像的梯度方向直方图,然后每个子区的直方图拼接起来组成描述子向量。SIFT描述子使用4×4的子区域,每个子区域有8个小区间的方向直方图,所以会产生4×4×8=128维的子向量。
Python代码实现
我们使用VLFeat的开源二进制文件来计算SIFT文件,VLFeat库是使用C语言编写的,我们可以使用该库的命令行接口直接调用,关于VLFeat的下载和配置可以参考这个链接:传送门
# coding=UTF-8
from PIL import Image
from numpy import *
from pylab import *
import os
def process_image(imagename, resultname, params="--edge-thresh 10 --peak-thresh 5"):
'''
调用VLFeat 的外部exe程序,生成.sift文件
:param imagename:
:param resultname:
:param params:
:return:
'''
# 判断图片是否是pgm格式,如果不是则创建一个
if imagename[-3:] != 'pgm':
im = Image.open(imagename).convert('L')
im.save('tmp.pgm')
imagename = 'tmp.pgm'
# 调用外部sift.exe文件(注意必须是sift.exe的目录位置,后面的空格不可少)
cmd = str("E:\PycharmProjects\VLfeat\win64\sift.exe "+imagename+" --output="+resultname+" "+params)
os.system(cmd)
def read_features(filename):
'''
从.sift文件中读取特征属性值
:param filename:
:return:
'''
f = loadtxt(filename)
return f[:, :4], f[:, 4:]
def plot_features(im, locs, circle=True):
'''
显示带有特征的图像
:param im:
:param locs:
:param circle:
:return:
'''
def draw_circle(c, r):
t = arange(0, 1.01, 0.01) * 2 * pi
x = c[0] + r * cos(t)
y = c[1] + r * sin(t)
plot(x, y, 'b', linewidth=1)
imshow(im)
if circle:
for p in locs:
draw_circle(p[:2], p[2])
else:
plot(locs[:, 0], locs[:, 1], 'ob')
axis('off')
if __name__=="__main__":
imname = 'lena.tiff'
im1 = array(Image.open(imname).convert('L'))
process_image(imname, 'lena.sift')
l1, d1 = read_features('lena.sift')
figure()
gray()
plot_features(im1, l1, circle=True)
show()
运行结果如下图: