角点的判定:
当窗口位于平坦区时,任意方向移动都没有灰度变化,即窗口内像素值均没有太大变化。
当窗口位于边缘区时,沿垂直方向移动无灰度变化,沿水平方向移动时,灰度会发生变化。
当窗口位于角点时,沿任意方向移动都会有明显的灰度变化。
角点的识别:
和 是矩阵 M的特征值, K 是一个经验常数,在范围 (0.04, 0.06) 之间。
R的值取决于M的特征值:
R很大的时候,属于角点区域;
R<0的时候,属于边缘区域;
|R|很小的时候,属于平坦区域。
特征点匹配:
角点检测:
#角点检测
import cv2
import numpy as np
filename = 'E:\picture-computerview/5.jpg'
img = cv2.imread(filename)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
# 输入图像必须是 float32,最后一个参数在 0.04 到 0.05 之间
dst = cv2.cornerHarris(gray,2,3,0.04)
#result is dilated for marking the corners, not important
dst = cv2.dilate(dst,None)
# Threshold for an optimal value, it may vary depending on the image.
img[dst>0.01*dst.max()]=[0,0,255] #判断角点
cv2.imshow('dst',img)
if cv2.waitKey(0) & 0xff == 27:
cv2.destroyAllWindows()
特征匹配:
# 特征匹配
from pylab import *
from PIL import Image
from PCV.localdescriptors import harris
from PCV.tools.imtools import imresize
im1 = array(Image.open("E:\picture-computerview/4.jpg").convert("L"))
im2 = array(Image.open("E:\picture-computerview/5.jpg").convert("L"))
# resize to make matching faster
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()
suptitle("Harris matching")
gray()
harris.plot_matches(im1, im2, filtered_coords1, filtered_coords2, matches)
show()
Harris 角点检测的结果是一个由角点分数构成的灰度图像。选取适当的阈值对结果图像进行二值化我们就检测到了图像中的角点。
实验是根据阈值与像素值的比较来查找角点,角点周围的角点也较多,可以采用非极大值抑制的方法去除一些多余的角点,从而使更有特征的角点显示出来。
该算法的结果存在一些不正确匹配。这是因为,与现代的一些方法相比,图像像素块的互相关矩阵具有较弱的描述性。实际运用中,我们通常使用更 稳健的方法来处理这些对应匹配。这些描述符还有一个问题,它们不具有尺度不变 性和旋转不变性,而算法中像素块的大小也会影响对应匹配的结果。
SIFT算法特征原理
SIFT即尺度不变特征转换,它用来检测图像的局部性特征,在空间尺度中寻找极值点,提取这点的位置、尺度、旋转不变量。这些关键点是一些十分突出,不会因光照和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等,所以与影像的大小和旋转无关,对光线、噪声、视角改变的容忍度也很高。
SIFT特征检测有四步:
1.尺度空间的极值检测:搜索所有尺度空间上的图像,通过高斯微分函数来识别潜在的对尺度和选择不变的兴趣点。
2.特征点定位:在每个候选的位置上,通过一个拟合精细模型来确定位置尺度,关键点的选取依据他们的稳定程度。
3.特征方向赋值:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向,后续的所有操作都是对于关键点的方向、尺度和位置进行变换,从而提供这些特征的不变性。
4.特征点描述:在每个特征点周围的邻域内,在选定的尺度上测量图像的局部梯度,这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变换。
算法思想:
将一幅图像映射(变换)为一个局部特征向量集;特征向量具有平移、缩放、旋转不变性,同时对光照变化、仿射及投影变换也有一定不变性。
算法实现步骤简述:
SIFT算法的实质可以归为在不同尺度空间上查找特征点(关键点)的问题。
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 = 'E:\picture-computerview\9.jpg'
im2f = 'E:\picture-computerview\8.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()
对于将一幅图像中的特征匹配到另一幅图像的特征,一种稳健的准则(同样是由Lowe提出的)是使用者两个特征距离和两个最匹配特征距离的比率。相比于图像中的其他特征,该准则保证能够找到足够相似的唯一特征。使用该方法可以使错误的匹配数降低。
SIFT可以检测出较多的特征点。通过两种算法的比较可以看出,两种算法所选特征点的位置是不同的,SIFT算法比起Harris算法,提取图像的特征点更加准确全面精准,更具有稳健性,效果上比起Harris算法更好。但是SIFT算法的运行速度相对来说慢很多,实时性不高。
from numpy import zeros
from pylab import *
from PIL import Image
from PCV.localdescriptors import sift
from PCV.tools import imtools
import pydot
import os
os.environ["PATH"] += os.pathsep + 'C:\Program Files\Graphviz/bin'
download_path = "E:\\1\photo" # set this to the path where you downloaded the panoramio images
path = "E:\\1\\photo\\" # path to save thumbnails (pydot needs the full system path)
imlist = imtools.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])
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('22.png')
为了创建显示可能图像组的图,如果匹配的数目高于一个阈值,我们使用边来连接相应的图像节点。为了得到图中的图像,需要使用图像的全路径。为了使图像看起来效果好,需要将每幅图像尺度化为缩略图形式,这里将缩略图的水平边更改为512 像素。