SIFT角点检测
1 知识点
SIFT特征包括兴趣点检测器和描述子。
SIFT算法的特点:
-
SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性,可用于三维视角和噪声的可靠匹配;
-
独特性(Distinctiveness)好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配;
-
多量性,即使少数的几个物体也可以产生大量的SIFT特征向量;
-
高速性,经优化的SIFT匹配算法甚至可以达到实时的要求;
-
可扩展性,可以很方便的与其他形式的特征向量进行联合。
-
具有非常强的稳健性,这是SIFT特征能够成功和流行的主要原因。
兴趣点:
SIFT特征使用高斯差分函数来定位兴趣点:
D
(
x
,
σ
)
=
[
G
κ
σ
(
x
)
−
G
σ
(
x
)
]
∗
I
(
x
)
=
[
G
K
σ
−
G
σ
]
∗
I
=
I
κ
σ
−
I
σ
D(\mathbf{x}, \sigma)=\left[G_{\kappa \sigma}(\mathbf{x})-G_{\sigma}(\mathbf{x})\right]^{*} \mathbf{I}(\mathbf{x})=\left[G_{K \sigma}-G_{\sigma}\right]^{*} \mathbf{I}=\mathbf{I}_{\kappa \sigma}-\mathbf{I}_{\sigma}
D(x,σ)=[Gκσ(x)−Gσ(x)]∗I(x)=[GKσ−Gσ]∗I=Iκσ−Iσ
G
σ
G_\sigma
Gσ是上一章中介绍的二维高斯核,
I
σ
I_\sigma
Iσ是使用
G
σ
G_\sigma
Gσ模糊的灰度图像,
κ
κ
κ是决定相差尺度的常数。兴趣点是在图像位置和尺度变化下
D
(
x
,
σ
)
D(x,\sigma)
D(x,σ) 的最大值和最小值点。这些候选位置点通过滤波去除不稳定点。
描述子:
SIFT描述子使用图像梯度,在每个像素点附近选取子区域网格,在每个子区域内计算图像梯度方向直方图每个子区域的直方图拼接起来组成描述子向量。子区域的大小为4×4,每个子区域使用8个小区间的方向直方图。
2 实验部分
检测兴趣点
a. 图片转为pgm格式并保存文件
import os
def process_image(imagename, resultname, tmpname='tmp.pgm', params="--edge-thresh 10 --peak-thresh 5"):
"""处理一幅图像,然后将结果保存在文件中"""
if imagename[-3:] != 'pgm':
#创建一个pgm文件
im = Image.open(imagename).convert('L')
im.save(tmpname)
imagename = tmpname
cmmd = str("sift.exe " + imagename +" --output=" + resultname+ " " + params)
print(cmmd)
os.system(cmmd)
print('processed',imagename,'to',resultname)
b. 读取特征值
def read_features_from_file(filename):
"""读取特征值属性值,然后将其以矩阵形式返回"""
f = loadtxt(filename)
return f[:,:4],f[:,4:] # 特征位置,描述子
c. 标记特征位置
def plot_features(im, locs, circle=False):
"""显示带有特征的图像
输入:im(数组图像),locs(每个特征的行、列、尺度和方向角度)"""
def draw_circle(c,r):
t = arange(0,1.01,.01)*2*pi
x = r*cos(t) + c[0]
y = r*sin(t) + c[1]
plot(x,y,'b',linewidth=2)
imshow(im)
if circle:
for p in locs:
draw_circle(p[:2],p[2])
else:
plot(locs[:,0],locs[:,1],'ob')
axis('off')
return
d. 运行
from PIL import Image
from pylab import *
import numpy as np
imname = 'a3.jpg'
im1 = array(Image.open(imname).convert('L'))
process_image(imname,'a3.sift')
l1,d1 = read_features_from_file('a3.sift')
figure()
gray()
subplot(121)
plot_features(im1,l1,circle=False)
subplot(122)
plot_features(im1,l1,circle=True)
show()
e. 运行结果
匹配描述子
a. 图片转为pgm格式并保存文件
import os
def process_image(imagename, resultname, tmpname='tmp.pgm', params="--edge-thresh 10 --peak-thresh 5"):
"""处理一幅图像,然后将结果保存在文件中"""
if imagename[-3:] != 'pgm':
#创建一个pgm文件
im = Image.open(imagename).convert('L')
im.save(tmpname)
imagename = tmpname
cmmd = str("sift.exe " + imagename +" --output=" + resultname+ " " + params)
print(cmmd)
os.system(cmmd)
print('processed',imagename,'to',resultname)
b. 读取特征值
def read_features_from_file(filename):
""" read feature properties and return in matrix form"""
f = loadtxt(filename)
return f[:,:4],f[:,4:] # feature locations, descriptors
c. 找匹配点
def match(desc1, desc2):
"""对于第一幅图像的每个描述子,选取其在第二幅图像中的匹配
输入:desc1(第一幅图像中的描述子),desc2(第二幅图像中的描述子)"""
desc1 = array([d/linalg.norm(d) for d in desc1])
desc2 = array([d/linalg.norm(d) for d in desc2])
dist_ratio = 0.6
desc1_size = desc1.shape
matchscores = zeros((desc1_size[0],1), 'int')
desc2t = desc2.T #预先计算矩阵转置
for i in range(desc1_size[0]):
dotprods = dot(desc1[i,:], desc2t) #向量点乘
dotprods = 0.9999*dotprods
# 反余弦和反排序,返回第二幅图像中特征的索引
index = argsort(arccos(dotprods))
# 检查最近邻的角度是否小于dist_ratio乘以第二近邻的角度
if arccos(dotprods)[index[0]] < dist_ratio * arccos(dotprods)[index[1]]:
matchscores[i] = int(index[0])
return matchscores
d. 两幅图进行匹配
def match_twosided(desc1,decs2):
"""双向对称版本的match"""
matches_12 = match(desc1, decs2)
matches_21 = match(decs2, decs2)
ndx_12 = matches_12.nonzero()[0]
# 去除不对称匹配
for n in ndx_12:
if matches_21[int(matches_12[n])] != n:
matches_12[n] = 0
return matches_12
e. 两幅图拼接
def appendimages(im1, im2):
"""返回将两幅图像并排拼接成的一幅新图像"""
# 选取具有最少行数的图像,然后填充足够的空行
row1 = im1.shape[0]
row2 = im2.shape[0]
if row1 < row2:
im1 = concatenate((im1,zeros((row2-row1,im1.shape[1]))), axis=0)
elif row1 > row2:
im2 = concatenate((im2,zeros((row1-row2,im2.shape[1]))), axis=0)
# 如果这些情况都没有,那么他们的行数相同,不需要进行填充
return concatenate((im1,im2), axis=1)
f. 显示两幅图的特征点
def plot_features(im, locs, circle=False):
"""显示带有特征的图像
输入:im(数组图像),locs(每个特征的行、列、尺度和方向角度)"""
def draw_circle(c,r):
t = arange(0,1.01,.01)*2*pi
x = r*cos(t) + c[0]
y = r*sin(t) + c[1]
plot(x,y,'b',linewidth=2)
imshow(im)
if circle:
for p in locs:
draw_circle(p[:2],p[2])
else:
plot(locs[:,0],locs[:,1],'ob')
axis('off')
return
g. 连接两幅图的匹配点
def plot_matches(im1, im2, locs1, locs2, matchscores, show_below=True):
"""显示一幅带有连接匹配之间连线的图片
输入:im1,im2(数组图像),locs1,locs2(特征位置),matchscores(match的输出),
show_below(如果图像应该显示再匹配下方)"""
im3 = appendimages(im1,im2)
if show_below:
im3 = vstack((im3,im3))
imshow(im3)
cols1 = im1.shape[1]
for i in range(len(matchscores)):
if matchscores[i] > 0:
plot([locs1[i, 0], locs2[matchscores[i, 0], 0] + cols1], [locs1[i, 1], locs2[matchscores[i, 0], 1]], 'c')
axis('off')
h. 运行
from PIL import Image
from pylab import *
import os
from numpy import *
im1f = 'test.jpg'
im2f = 'test.jpg'
im1 = array(Image.open(im1f))
im2 = array(Image.open(im2f))
process_image(im1f,'test.sift')
l1,d1 = read_features_from_file('test.sift')
figure()
gray()
subplot(121)
plot_features(im1, l1, circle=False)
process_image(im2f,'test.sift')
l2,d2 = read_features_from_file('test.sift')
subplot(122)
plot_features(im2, l2, circle=False)
matches = match_twosided(d1, d2)
print('{} matches'.format(len(matches.nonzero()[0])))
figure()
gray()
plot_matches(im1,im2,l1,l2,matches, show_below=True)
show()
I. 运行结果
为方便观察运行结果,使用了两幅一样的图进行匹配。
3 遇到的问题及解决方法
问题一: 未找到test.sift文件
解决方法:
def process_image(imagename, resultname, tmpname='tmp.pgm', params="--edge-thresh 10 --peak-thresh 5"):
函数中,
cmmd = str("sift.exe " + imagename +" --output=" + resultname+ " " + params)
sift写成sift.exe
–output 前面要加空格,不然识别不出来文件
问题二: IndexError: only integers, slices (:
), ellipsis (...
), numpy.newaxis (None
) and integer or boolean arrays are valid indices
在匹配描述子时报错
解决方法:
def match(desc1, desc2):
函数中,
matchscores = zeros((desc1_size[0],1), 'int')
末尾需加上’int’进行数据类型转换
提示: sift.exe和vi.dll需和写的代码放进同一个文件里。