计算机视觉——SIFT特征提取与检索
目录
一、SIFT算法
1.1算法介绍
SIFT特征是基于物体上的一些局部外观的兴趣点而与影像的大小和旋转无关。对于光线、噪声、微视角改变的容忍度也相当高。基于这些特性,它们是高度显著而且相对容易撷取,在母数庞大的特征数据库中,很容易辨识物体而且鲜有误认。使用SIFT特征描述对于部分物体遮蔽的侦测率也相当高,甚至只需要3个以上的SIFT物体特征就足以计算出位置与方位。在现今的电脑硬件速度下和小型的特征数据库条件下,辨识速度可接近即时运算。SIFT特征的信息量大,适合在海量数据库中快速准确匹配。
1.2算法特点
1.SIFT特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性;
2. 区分性好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配;
3. 多量性,即使少数的几个物体也可以产生大量的SIFT特征向量;
4.高速性,经优化的SIFT匹配算法甚至可以达到实时的要求;
5.可扩展性,可以很方便的与其他形式的特征向量进行联合。
1.3特征检测
SIFT特征检测主要包括以下4个基本步骤:
1.尺度空间极值检测:
搜索所有尺度上的图像位置。通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点。
2. 关键点定位
在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。
3. 方向确定
基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性。
4. 关键点描述
在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变化。
1.4特征匹配
SIFT特征匹配主要包括2个阶段:
第一阶段:SIFT特征的生成,即从多幅图像中提取对尺度缩放、旋转、亮度变化无关的特征向量。
第二阶段:SIFT特征向量的匹配。
二、SIFT特征提取与检索实验
2.1实验要求
- 针对自己所处的环境,拍摄多张图片(注意要来自不同场景),构造出一个小的数据集(15张以上)
- 实现数据集中,每张图片的SIFT特征提取,并展示特征点
- 给定两张图片,计算其SIFT特征匹配结果
- 给定一张输入的图片,在数据集内部进行检索,输出与其匹配最多的三张图片
2.2实验准备
1)实验数据

2)环境配置

2.3实验过程
2.3.1图片的SIFT特征提取
1)实验代码
# -*- 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 = 'pc1.jpg'
im = array(Image.open(imname).convert('L'))
sift.process_image(imname, 'picture1.sift')
l1, d1 = sift.read_features_from_file('picture1.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()
2)实验结果



3)实验小结
通过选择三组复杂程度不同的照片进行SIFT特征提取和Harris角点检测,可以看出无论是复杂的图像还是简易的图像,
SIFT算法都有着非常良好的检测效率,而且检测出的特征点也比Harris更加丰富。因此,可以很容易的比较得出SIFT算法具有更加优异性能的结果。
2.3.2计算两张图片其SIFT特征匹配结果
1)实验代码
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 = 'pc3.jpg'
im2f = 'pc4.jpg'
im1 = array(Image.open(im1f))
im2 = array(Image.open(im2f))
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(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()
2)实验结果

3)实验小结
通过计算两张图片其SIFT特征匹配结果,可以看出使用SIFT算法基本上能够找出所有的特征匹配点,而且SIFT算法不会受图像角度或者旋转的影响,也不会受物体远近、光线的干扰。需要注意的是,在进行计算两张图片其SIFT特征匹配结果时需要确保两张图片的大小一致,这样才能保证算法搜索时的匹配性。
2.3.3寻找匹配图像
1)实验代码
第一步需要对所有图片进行处理,先把所有图片都进行SIFT算法特征提取并保存
import cv2
import numpy as np
from os import walk
from os.path import join
def create_descriptors(folder):
files = []
for (dirpath, dirnames, filenames) in walk(folder):
files.extend(filenames)
for f in files:
if '.jpg' in f:
save_descriptor(folder, f, cv2.xfeatures2d.SIFT_create())
def save_descriptor(folder, image_path, feature_detector):
# 判断图片是否为npy格式
if image_path.endswith("npy"):
return
# 读取图片并检查特征
img = cv2.imread(join(folder,image_path), 0)
keypoints, descriptors = feature_detector.detectAndCompute(img, None)
# 设置文件名并将特征数据保存到npy文件
descriptor_file = image_path.replace("jpg", "npy")
np.save(join(folder, descriptor_file), descriptors)
if __name__=='__main__':
path = 'images'
create_descriptors(path)
第二步依次读取所有图片并进行检索匹配度
from os.path import join
from os import walk
import numpy as np
import cv2
query = cv2.imread('pc3.jpg', 0)
folder = 'images'
descriptors = []
# 获取特征数据文件名
for (dirpath, dirnames, filenames) in walk(folder):
for f in filenames:
if f.endswith("npy"):
descriptors.append(f)
print(descriptors)
# 使用SIFT算法检查图像的关键点和描述符
sift = cv2.xfeatures2d.SIFT_create()
query_kp, query_ds = sift.detectAndCompute(query, None)
# 创建FLANN匹配器
index_params = dict(algorithm=0, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
potential_culprits = {}
for d in descriptors:
# 将图像query与特征数据文件的数据进行匹配
matches = flann.knnMatch(query_ds, np.load(join(folder, d)), k=2)
# 清除错误匹配
good = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good.append(m)
# 输出每张图片与目标图片的匹配数目
print("img is %s ! matching rate is (%d)" % (d, len(good)))
potential_culprits[d] = len(good)
# 获取最多匹配数目的图片
max_matches = None
potential_suspect = None
for culprit, matches in potential_culprits.items():
if max_matches == None or matches > max_matches:
max_matches = matches
potential_suspect = culprit
print("potential suspect is %s" % potential_suspect.replace("npy", "").upper())
2)实验结果
目标图片:

检索匹配度:

匹配度最高的三张图片:



3)实验小结
从实验结果来看,SIFT算法很好的检索到了匹配度较高的图片,由此可见SIFT特征对旋转、尺度缩放、亮度变化保持不变性,对视角变化、也保持一定程度的稳定性。而且SIFT也具有非常快速的匹配速度,这对于一些复杂的、信息量大的图片的特征匹配有着优异的解决能力。
三、实验总结
1)SIFT特征是基于物体上的一些局部外观的兴趣点而与影像的大小和旋转无关。
2)SIFT特征的信息量大,适合在海量数据库中快速准确匹配。
3)SIFT算法对于Harris角点检测算法有着非常良好的检测能力,无论是在速度上还是准确度上,SIFT算法都有着无可挑剔的能力。
4)但是SIFT同时也用着对平滑边缘检测不灵敏的缺点,以及缺乏实时性。
地理标记图像匹配
一、 实验描述
利用之前的SIFT检测收集的数据图片,通过特征提取,使用局部描述子匹配,将存在关联性的图片进行连接,从而更加直观的看到相似图片特征。
二、环境配置
在实验前需要安装Graphviz和pydot两个库,在安装时一定要先先安装graphviz,再安装pydot
2.1安装Graphviz
法一:
在cmd中输入“conda install graphviz”。
(不过按照这样安装了之后,我使用 from graphviz import Digraph时候仍然报错,表示找不到模块graphviz)
法二:
在cmd中输入“pip install graphviz”。
(按照这样安装了之后,我使用 from graphviz import Digraph时候仍然报错,表示找不到模块graphviz)
法三:
到官网下载graphviz的安装包(可选择msi格式),这里我提供个链接, 一直按next(记住安装路径,比如在我的电脑上它的安装路径是C:\Program Files (x86)\Graphviz2.38),然后将该目录下的bin文件夹添加到系统的环境变量中(即把C:\Program Files (x86)\Graphviz2.38\bin添加进环境变量中), 最后在cmd输入“dot-version”并按回车,若显示出graphviz的相关版本信息,则安装配置成功。
2.2安装pydot
第一种:
在cmd输入“pip install pydot==1.1.0”。
(但是在我的环境里输入这条命令后报错 SyntaxError: invalid syntax,好像行不通)
第二种:
在cmd输入“pip install pydot-ng”。
(我成功安装了pydot-ng,但是并没有解决find_graphviz的报错问题,依然报错)
第三种:
果断手工安装pydot1.1.0!
到网上找到并下载pydot1.1.0的安装包(可以是zip格式),这里我提供个链接,名字为pydot-1.1.0.zip。然后将其置于一个特定的目录下(比如D:\pydot-1.1.0.zip),然后在cmd中进入D盘,输入 “pip install pydot1.1.0.zip”,即可安装成功!
三、实验数据准备

四、实验代码
4.1提取特征
先对图像使用SIFT特征提取代码进行处理,并且将特征保存在和图像同名(但文件名是.sift,而不是.jpg)的文件路径下
download_path = r"F:\PCdata\images"
path = r"F:\PCdata\SDL"
imlist = sift.get_imlist(download_path)
nbr_images = len(imlist)
featlist = [imname[:-3] + 'sift' for imname in imlist]
for i, imname in enumerate(imlist):
sift.process_image(imname, featlist[i])
4.2使用局部描述子匹配
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
# 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]
4.3可视化连接图像
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')
为了创建显示可能图像组的图,如果匹配的数目高于一个阈值,我们使用边来连接相应的图像节点。为了得到图中的图像,需要使用图像的全路径(使用path变量表示)
4.4实验结果图
全图像特征提取后结果:

运行结果图:

五、实验总结
- 实验的结果图中会看到,原本应该一共有15张图片的数据集,呈现出来后却只有12张关联图片,而另外的3张图片是因为其特征与大部分的特征是截然不同的,也就是匹配度近乎为0, 因此最后的匹配结果只保留了存在相似特征点的图片。
- 通过对每幅图的观察可以看出,数据集图片中那些包含有较高的电梯楼地理特征的图片都被用连线关联起来了,还有在马路边房屋都依靠马路的地理标记出来,说明SIFT算法能够很好的标记出地理特征, 从而实现匹配查找
- 但是在运行代码时, 很明显的感受到耗时很长,15张图片的数据集其需要比较的次数是14的阶层, 即14*13*12*.....*1次
- 有些图片中间只存在较少的匹配度, 也就是匹配值为1的话也会将两张图片连线, 无法看出关联性的强弱。
RANSAC算法
一、算法介绍及原理
RANSAC通过反复选择数据中的一组随机子集,来达成目标。在OpenCV中滤除误匹配对,采用RANSAC算法寻找一个最佳单应性矩阵H,矩阵大小为3×3。RANSAC目的是找到最优的参数矩阵使得满足该矩阵的数据点个数最多,通常令h3×3=1来归一化矩阵。由于单应性矩阵有8个未知参数,至少需要8个线性方程求解,对应到点位置信息上,一组点对可以列出两个方程,则至少包含4组匹配点对。

其中(x,y)表示目标图像角点位置,(x’,y’)为场景图像角点位置,s为尺度参数。
RANSAC算法从匹配数据集中随机抽出4个样本并保证这4个样本之间不共线,计算出单应性矩阵,然后利用这个模型测试所有数据,并计算满足这个模型数据点的个数与投影误差(即代价函数),若此模型为最优模型,则对应的代价函数最小。

RANSAC算法的输入是一组观测数据,一个可以解释或者适应于观测数据的参数化模型,一些可信的参数。
RANSAC通过反复选择数据中的一组随机子集来达成目标。被选取的子集被假设为局内点,并用下述方法进行验证:
1.有一个模型适应于假设的局内点,即所有的未知参数都能从假设的局内点计算得出。
2.用1中得到的模型去测试所有的其它数据,如果某个点适用于估计的模型,认为它也是局内点。
3.如果有足够多的点被归类为假设的局内点,那么估计的模型就足够合理。
4.然后,用所有假设的局内点去重新估计模型,因为它仅仅被初始的假设局内点估计过。
5.最后,通过估计局内点与模型的错误率来评估模型。
这个过程被重复执行固定的次数,每次产生的模型要么因为局内点太少而被舍弃,要么因为比现有的模型更好而被选用。
二、实验过程
实验分成两组,分别是景深单一和景深丰富的图片,通过进行SIFT特征匹配结果,分析没有RANSAC算法和有RANSAC算法的区别,比较两者的特点。
景深单一且无RANSAC:

景深单一且有RANSAC:

小结:景深单一的图片其内容较少,所以匹配点数也会更少。通过比较两幅有无RANSAC算法的检测图片,可以很明显的看到,使用RANSAC算法后删除掉了很多特征匹配点,观察可以发现,删除的特征匹配点大多都是错误的检测匹配点。但是注意到下面那根绿色的匹配线,很明显这是个错误的匹配点应该被删除,而RANSAC并没有把它删除掉,所以可以知道RANSAC算法也会存在着一定的误差。
景深复杂且没有RANSAC:

景深复杂且有RANSAC:

小结:景深复杂的图片特征匹配点较多, 匹配线较为密集,因此我们主要观察图片下方的匹配线。可以看到,使用RANSAC算法后可以将很多不匹配的点删去,但是因为误差的存在依旧残留一些错误的匹配点。而且,图片中的“比阳”两个字并没有被匹配进去,还有一些类似的房屋屋顶也是同样的情况。但是,总的来说RANSAC还是有较强的能力可以删除很多错配,增强了鲁棒性。