Python计算机视觉-图像局部描述符
1.SIFT描述子
SIFT(Scale Invariant Feature Transform,尺度不变特征变换)由David Lowe 教授提出,SIFT特征包括兴趣点检测器和描述子,具有非常强的稳健性。它对于尺度、旋转和亮度都具有不变性,因此可以用于三位视角和噪声的可靠匹配。
1.1 特征检测步骤:
(1)尺度空间的极值检测:搜索所有尺度空间上的图像,通过高斯微分函数来识别潜在的对尺度和选择不变的兴趣点。
(2)特征点定位:在每个候选的位置上,通过一个拟合精细模型来确定位置尺度,关键点的选取依据他们的稳定程度。
(3)特征方向赋值:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向,后续的所有操作都是对于关键点的方向、尺度和位置进行变换,从而提供这些特征的不变性。
(4)特征点描述:在每个特征点周围的邻域内,在选定的尺度上测量图像的局部梯度,这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变换。
1.2 兴趣点检测匹配与Harris角点检测器效果对比
为了计算图像的SIFT特征,我们用开源工具包VLFeat,VLFeat可以在www.vlfeat.org上下载。
# -*- 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 = './picture/1.jpg'
im = array(Image.open(imname).convert('L'))
sift.process_image(imname, '1.sift')
l1, d1 = sift.read_features_from_file('1.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文件,需要把VLFeat中的对应系统版本文件夹win64复制到当前项目文件夹中,并在sift.py文件中将命令语句修改,路径中的空格不能忽略:
再次运行提示索引超出维度,之后发现需要更换使用早先版本VLFeat-0.9.20解决报错,并且最新版VLFeat-0.9.21中没有对32位的系统支持,若系统非64位也应考虑更换版本:
因原图分辨率过高,将其大小统一更改为了512*288,兴趣点检测结果如下:
两张不同角度和尺度的照片进行兴趣点匹配:
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 = './picture/test1.jpg'
im2f = './picture/test2.jpg'
# im1f = '../data/climbing_1_small.jpg'
# im2f = '../data/climbing_2_small.jpg'
im1 = array(Image.open(im1f))
im2 = array(Image.open(im2f))
sift.process_image(im1f, 'out_sift_test1.txt')
l1, d1 = sift.read_features_from_file('out_sift_test1.txt')
figure()
gray()
subplot(121)
sift.plot_features(im1, l1, circle=False)
sift.process_image(im2f, 'out_sift_test2.txt')
l2, d2 = sift.read_features_from_file('out_sift_test2.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()
Harris角点检测:人们通常通过在一个小的窗口区域内观察点的灰度值大小来识别角点,如果往任何方向移动窗口都会引起比较大的灰度变换那么往往这就是我们要找的角点。代码如下:
# -*- coding: utf-8 -*-
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.
"""
# Figure 2-2上面的图
#im1 = array(Image.open("../data/crans_1_small.jpg").convert("L"))
#im2= array(Image.open("../data/crans_2_small.jpg").convert("L"))
# Figure 2-2下面的图
im1 = array(Image.open("./picture/6.jpg").convert("L"))
im2 = array(Image.open("./picture/7.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()
代码当中有对于加快匹配速度的优化需要返回整数
在Python 3.0中" / “就一定表示 浮点数除法,返回浮点结果;” // "表示整数除法。
更改代码:
# resize加快匹配速度
im1 = imresize(im1, (im1.shape[1]//2, im1.shape[0]//2))
im2 = imresize(im2, (im2.shape[1]//2, im2.shape[0]//2))
对比分析:最后一组匹配中我用4032*2268分辨率的原图进行匹配,原以为分辨率很大程度上会影响匹配结果正确率,但实验结果表明虽然sift算法删去了很多不必要的特征点,效率和准确度都比Harris角点高很多,但对于当前例子中对称建筑物中高度相似的点依然会有错误识别的现象出现。
2.地理标记图像匹配
从图片中提取SIFT描述子,将两两进行特征匹配后的匹配数保存在matchscores中,对上面匹配后的图像进行连接可视化,要做到这样,我们需要在一个图中用边线表示它们之间是相连的。我们采用pydot工具包进行图像连线,它提供了GraphViz graphing库的Python接口,之后将匹配后对其进行可视化。为了是得到的可视化结果比较好看,我们对每幅图像用100*100的缩略图缩放它们。
直接用pip安装pydot工具包:
pip install pydot
# -*- coding: utf-8 -*-
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 = "panoimages" # set this to the path where you downloaded the panoramio images
#path = "/FULLPATH/panoimages/" # path to save thumbnails (pydot needs the full system path)
download_path = "D:\Study\Python_CV\SIFT\picture" # set this to the path where you downloaded the panoramio images
path = "D:\Study\Python_CV\SIFT\picture" # 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('中山纪念楼.png')
运行时报错,path中找不到dot文件:
此时需要安装Graphviz绘图包
可在官网:https://graphviz.gitlab.io/_pages/Download/Download_windows.html 下载并安装
也可在Anaconda prompt当中用指令安装:conda install graphviz
重要的是安装完成后需要在系统中添加bin文件夹的环境变量,若还无法正常调用尝试重启可得到解决
再用pip安装一次graphviz:
测试dot版本:dot -version
地理标记图片匹配结果:
代码参考:http://yongyuan.name/pcvwithpython/chapter2.html#sec-2-2-1