SIFT全称尺度不变特征转换。它用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量。SIFT算法的实质可以归为在不同 尺度空间 上查找 特征点(关键点) 的问题。
关键点是指一些十分突出的点,这些点不会因光照、尺度、旋转等因素的改变而消失,比如角点、边缘点、暗区域的亮点以及亮区域的暗点。
概念:
尺度空间理论最早于1962年提出,其主要思想是通过对原始图像进行尺度变换,获得图像多尺度下的空间表示。从而实现边缘、角点检测和不同分辨率上的特征提取,以满足特征点的尺度不变性。
表示:
SIFT算法在构建尺度空间时采取高斯核函数进行滤波,使原始图像保存最多的细节特征,经过高斯滤波后细节特征逐渐减少来模拟大尺度情况下的特征表示。利用高斯核函数进行滤波的主要原因有两个:
(1)高斯核函数是唯一的尺度不变核函数。
(2)DoG核函数可以近似为LoG函数,这样可以使特征提取更简单。
一个图像的尺度空间L(x, y, σ) ,定义为原始图像 I(x, y)与一个可变尺度的2维高斯函数G(x, y, σ) 卷积运算。公式如下:
其中*表示卷积运算,(x,y)代表图像的像素位置。是尺度空间因子,值越小表示图像被平滑的越少,相应的尺度也就越小。大尺度对应于图像的概貌特征,小尺度对应于图像的细节特征。
构建:
尺度空间在实现时使用高斯金字塔表示,高斯金字塔的构建分为两步:
(1)对图像做高斯平滑。
(2)对图像做降采样。
为了让尺度体现其连续性,高斯金字塔在简单降采样的基础上加上了高斯滤波。如上图所示,将图像金字塔每层的一张图像使用不同参数做高斯模糊,Octave表示一幅图像可产生的图像组数,Interval表示一组图像包括的图像层数。
表示:
高斯图像金字塔共o组、s层,则有:
σ:尺度空间坐标;s:sub-level层坐标;σ0:初始尺度;S:每组层数(一般为3~5)
最后可将组内和组间尺度归为:
i:金字塔组数;n:每一组的层数
DOG函数:
DOG高斯差分金字塔:
(1)对应DOG算子,需构建DOG金字塔。
可以通过高斯差分图像看出图像上的像素值变化情况。(如果没有变化,也就没有特征。特征必须是变化尽可能多的点。)DOG图像描绘的是目标的轮廓。
(2)DOG局部极值检测
特征点是由DOG空间的局部极值点组成的。为了寻找DoG函数的极值点,每一个像素点要和它所有的相邻点比较,看其是否比它的图像域和尺度域的相邻点大或者小。
中间的检测点和它同尺度的8个相邻点和上下相邻尺度对应的9×2个
点共26个点比较,以确保在尺度空间和二维图像空间都检测到极值点。
去除边缘响应:
由于DoG函数在图像边缘有较强的边缘响应,因此需要排除边缘响应。DoG函数的峰值点在边缘方向有较大的主曲率,而在垂直边缘的方向有较小的主曲率。主曲率可以通过计算在该点位置尺度的2×2的Hessian矩阵得到,导数由采样点相邻差来估计:
Dxx 表示DOG金字塔中某一尺度的图像x方向求导两次。
D的主曲率和H的特征值成正比。令 α ,β为特征值,则
该值在两特征值相等时达最小。Lowe论文中建议阈值T为1.2,即
时保留,反之剔除。
通过尺度不变性求极值点,可以使其具有缩放不变的性质。而利用关键点邻域像素的梯度方向分布特性,可以为每个关键点指定方向参数方向,从而使描述子对图像旋转具有不变性。通过求每个极值点的梯度来为极值点赋予方向。像素点的梯度表示:
对于每一个关键点,都拥有位置、尺度以及方向三个信息。为每个关键点建立一个描述符,用一组向量将这个关键点描述出来,使其不随各种变化而改变,比如光照变化、视角变化等等。这个描述子不但包括关键点,也包含关键点周围对其有贡献的像素点,并且描述符应该有较高的独特性,以便于提高特征点正确匹配的概率。
Lowe实验结果表明:描述子采用4×4×8=128维向量表征,综合效果最优(不变性与独特性)。
关键点的匹配可以采用穷举法来完成,但是这样耗费的时间太多,一
般都采用kd树的数据结构来完成搜索。搜索的内容是以目标图像的关
键点为基准,搜索与目标图像的特征点最邻近的原图像特征点和次邻
近的原图像特征点。Kd树是一个平衡二叉树,如下所示:
# -*- 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 = 'C:/TXX/1.png'
im = array(Image.open(imname).convert('L'))
sift.process_image(imname, 'empire.sift')
l1, d1 = sift.read_features_from_file('empire.sift')
figure()
gray()
subplot(131)
title(u'原始图像', fontproperties=font)
imshow(im)
plt.axis('off')
subplot(132)
sift.plot_features(im, l1, circle=False)
title(u'SIFT特征',fontproperties=font)
subplot(133)
sift.plot_features(im, l1, circle=True)
title(u'用圆圈表示SIFT特征尺度',fontproperties=font)
show()
SIFT算法实现特征匹配主要有三个流程:
1、提取关键点。
2、对关键点附加详细的信息(局部特征),即描述符。
3、通过特征点(附带上特征向量的关键点)的两两比较找出相互匹配的若干对特征点,建立景物间的对应关系。
SIFT算法实现特征匹配步骤图解如下:
在这组测试中,对着同一幅画进行了正面,左侧面和右侧面的拍摄。SIFT算法很好的讲画进行了匹配,而没有受到背后墙纸特征的影响,可能是因为SIFT算法哟拥有高斯差分金字塔的采样和尺度空间DoG函数进行曲线拟合等一系列处理从而提高特征点提取的稳定性。
实验中画和室外的建筑进行匹配,都有大量的匹配成功的点,可能两张图还是有些相似,在下次进行完成不同场景匹配实验时,应挑选差别更大的两张图进行实验,来验证这到底是不是SIFT算法的缺陷。
这次实验采用的三张图分别时两幅画的一部分和两幅画的一部分在一张图中,SIFT算法很好的对其进行了精准的匹配,且没有匹配墙纸的特征点,可能是因为设定的阈值,使得SIFT匹配点数目会减少,但是匹配起来更加精确,更加稳定。
# -*- 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("C:/TXX/3.png").convert("L"))
im2 = array(Image.open("C:/TXX/4.png").convert("L"))
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()
SIFT特征的信息量大,适合在海量数据库中快速准确匹配,准确度也相当高。SIFT特征对旋转、尺度缩放、亮度变化等保持不变性。结果非常准确,是一种非常稳定的局部特征。
for infile in filelist: # 对文件夹下的每张图片进行如下操作
im2 = array(Image.open(infile))
process_image(infile, 'out_sift_2.txt')
l2, d2 = read_features_from_file('out_sift_2.txt')
matches = match_twosided(d1, d2)
num[i] = len(matches.nonzero()[0])
i = i + 1
print '{} matches'.format(num[i - 1]) # 输出匹配值
# -*- 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 = "F:/dropbox/Dropbox/translation/pcv-notebook/data/panoimages" # set this to the path where you downloaded the panoramio images
path = "F:/dropbox/Dropbox/translation/pcv-notebook/data/panoimages/" # 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('whitehouse.png')
RANSAC算法被广泛应用在计算机视觉领域和数学领域,例如在直线拟合、平面拟合、计算图像或点云间的变换矩阵等方面都有应用。下面将介绍RANSAC算法在图像拼接方面的应用。
OpenCV中滤除误匹配对采用RANSAC算法寻找一个最佳单应性矩阵H,RANSAC目的是找到最优的参数矩阵使得满足该矩阵的数据点个数最多。由于单应性矩阵有8个未知参数,至少需要8个线性方程求解,对应到点位置信息上,一组点对可以列出两个方程,则至少包含4组匹配点对。RANSAC算法从匹配数据集中随机抽出4个样本并保证这4个样本之间不共线,计算出单应性矩阵,然后利用这个模型测试所有数据。
RANSAC求解单应性矩阵步骤:
1、随机选择四对匹配特征
2、根据DLT(直接线性变换解法)计算单应矩阵 H (唯一解)
3、对所有匹配点,计算映射误差
4、根据误差阈值,确定inliers
5、重复1-4步,最后针对最大inliers集合,重新计算单应矩阵 H
数据集:
场景一:景深单一
1、相同场景下RANSAC前后对比:
图1:
由图1可看出,ransac前后匹配点数量相差不大,从上图(ransac前)能明显看出错误匹配点(红色圆圈标注处),而在下图(ransac后)中,该匹配点被剔除。
图2:
由图2可看出,ransac前后匹配点数量几乎没有变化,从上图(ransac前)和下图(ransac后)对比中看不出明显差别。
图3:
由图3可看出,ransac前后匹配点数量相差不大,从上图(ransac前)能明显看出错误匹配点(两处红色圆圈标注处),而在下图(ransac后)中,这两处匹配点均被剔除。
2、不同场景下RANSAC前后对比:
图1:
由图1可看出,不同场景下,仍存在少量匹配点,原因可能是图像本身亮度变化不明显,存在些许亮度相似的地方,导致匹配时错误匹配。从对比中可看出上图(ransac前)中错误匹配的点在下图(ransac后)中基本被剔除。
图2:
由图2可看出,不同场景下,匹配点数量较少,会存在匹配点的原因可能是图像中存在一些相近的事物(如左图中的树和右图中的草丛)导致匹配时错误匹配。从对比中可看出上图(ransac前)中错误匹配的点在下图(ransac后)中大部分被剔除,但仍存在明显的错误匹配点。
场景二:景深复杂
1、相同场景下RANSAC前后对比:
图1:
由图1可看出,ransac前后匹配点数量相差不大,从上图(ransac前)能明显看出错误匹配点(红色圆圈标注处),而在下图(ransac后)中,该匹配点被剔除。同时也存在剔除掉了正确匹配点的情况,比如上图蓝色圆圈标记处为正确的匹配点,而在下图中,该点被剔除了。
图2:
由图2可看出,ransac前后匹配点数量相差较大,大量匹配点被剔除(下图中黑色圆圈标注处),其中许多错误匹配点被剔除,而不少正确的匹配点也被剔除掉了。
图3:
由图3可看出,ransac前后匹配点数量几乎没有变化,从上图(ransac前)和下图(ransac后)对比中看不出明显差别。
2、不同场景下RANSAC前后对比:
图1:
由图1可看出,不同场景下,仍存在少量匹配点,原因可能是图像本身景深复杂,存在一些结构相似,颜色相近的地方,导致匹配时错误匹配。从对比中可看出上图(ransac前)中错误匹配的点在下图(ransac后)中基本被剔除,但仍存在错误匹配点。
图2:
由图2可看出,不同场景下,仍存在少量匹配点,原因可能是图像本身景深较复杂,存在一些模糊不清的地方,导致匹配时错误匹配。从对比中可看出上图(ransac前)中错误匹配的点在下图(ransac后)中基本被剔除。
景深单一分析:
景深复杂分析:
问题:opencv-contrib-python无法使用
sift = cv2.xfeatures2d.SIFT_create()函数无法使用,也是opencv-contrib-python无法使用。因为当初安装时是默认版本安装的,所以版本比较高,降低opencv版本后即可。
解决办法:
1、先把原来默认下载的opencv卸载
pip uninstall opencv-python
pip uninstall opencv-contrib-python
2、重装opencv-python、opencv-contrib-python,这次指定版本安装。
pip install opencv_python==3.4.2.16
pip install opencv-contrib-python==3.4.2.16