SIFT(尺度不变特征变换)
SIFT特征包括兴趣点检测器和描述子,SIFT特征对于尺度、旋转和亮度都具有不变性
SIFT特征使用高斯差分函数定位兴趣点,兴趣点是在图像位置和尺度变化下高斯差分函数的最大值和最小值点
兴趣点位置描述子给出了兴趣点的位置和尺度信息
如何构造SIFT描述子?
建立一个围绕兴趣点的网格结构,在网格的一个子区域构造梯度方向的8-bin直方图,在网格的每个子区域内提取直方图,拼接直方图,得到一个长的特征向量
process_image()方法可以将图像转换为二进制文件需要的图像格式——灰度.pgm,转换结果保存在文本文件中。格式为兴趣点坐标、尺度、方向角度和对应描述符的128维向量
def process_image(imagename,resultname,params="--edge-thresh 10 --peak-thresh 5"):
"""处理一幅图像,然后将结果保存在文件中"""
if imagename[-3:] != 'pgm':
#创建一个pgm文件
im = Image.open(imagename).convert('L')
im.save('tmp.pgm')
imagename = 'tmp.pgm'
cmmd = str("sift "+imagename+" --output="+resultname+
" "+params)
os.system(cmmd)
print 'processed', imagename, 'to',resultname
read_features_from_file()方法可以从输出文件中将特征读取到NumPy数组中
def read_features_from_file(filename):
"""读取特征属性值,然后将其以矩阵的形式返回"""
f = loadtxt(filename)
return f[:,:4],f[:,4:] #特征位置,描述子
write_features_to_file()方法可以修改描述子,将输出结果保存到特征文件
def write_features_to_file(filename,locs,decs):
"""将特征位置和描述子保存到文件中"""
savetxt(filename,hstack((locs,decs)))
hstack()方法可以拼接不同的行向量来实现水平堆叠两个向量
plot_features()方法可以在图像上绘制特征的位置,将其可视化
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')
绘制SIFT特征位置的实例:
imname = 'C:/Users/0AQZ0/Documents/exercisecode/Python/PyCV/Images/001.jpg'
im1 = array(Image.open(imname).convert('L'))
process_image(imname,'001.sift')
l1,d1 = read_features_from_file('001.sift')
figure()
gray()
plot_features(im1,l1,circle=True)
show()
如何将一幅图像中的特征匹配到另一幅图像的特征?
使用这两个特征距离和两个最匹配特征距离的比率
match()方法使用描述子向量间的夹角作为距离度量,实现了匹配
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
#反余弦和反排序,返回第二幅图像中的特征的索引
indx = argsort(arccos(dotprods))
#检查最近的角度是否小于dist_ratio乘以第二近邻的角度
if arccos(dotprods)[indx[0]] < dist_ratio * arccos(dotprods)[indx[1]]:
matchscores[i] = int(indx[0])
return matchscores
match_twosided()方法仅保留同时满足两种匹配准则的对应,进一步增加匹配的稳健性
def match_twosided(desc1,desc2):
"""双向对称版本的match()"""
matches_12 = match(desc1,desc2)
matches_21 = match(desc2,desc1)
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