目录
- [1 Harris角点检测器](#1 Harris角点检测器)
- [2 SIFT](#2 SIFT)
-
- [2.1 兴趣点](#2.1 兴趣点)
- [2.2 描述子](#2.2 描述子)
- [2.3 检测兴趣点](#2.3 检测兴趣点)
- [2.4 匹配描述子](#2.4 匹配描述子)
1 Harris角点检测器
Harris角点检测算法是简单的角点检测算法,主要思想是,如果像素周围显示存在多于一个方向的边,认为该点为兴趣点,称为角点。
把图像域中点 x上的对称半正定矩阵 M r = M l ( x ) M_{r}=M_{l}(\mathbf{x}) Mr=Ml(x)定义为:
M 1 = ∇ I ∇ I T = [ I x I y ] [ I x I y ] = [ I x 2 I x I y I x I y I y 2 ] \boldsymbol{M}_1=\nabla\boldsymbol{I}\:\nabla\boldsymbol{I}^T=\begin{bmatrix}\boldsymbol{I}_x\\\boldsymbol{I}_y\end{bmatrix}\begin{bmatrix}\boldsymbol{I}_x&\boldsymbol{I}_y\end{bmatrix}=\begin{bmatrix}\boldsymbol{I}_x^2&\boldsymbol{I}_x\boldsymbol{I}_y\\\boldsymbol{I}_x\boldsymbol{I}_y&\boldsymbol{I}_y^2\end{bmatrix} M1=∇I∇IT=[IxIy][IxIy]=[Ix2IxIyIxIyIy2]
其中 ∇ I \nabla I ∇I 为包含导数 I x I_x Ix 和 I y I_\mathrm{y} Iy的图像梯度 。由于该定义, M ι M_{\iota} Mι的秩为1,特征值为 λ 1 = ∣ ∇ I ∣ 2 \lambda_1=|\nabla I|^2 λ1=∣∇I∣2和 λ 2 = 0 \lambda_2=0 λ2=0。现在对于图像的每一个像素,我们可以计算出该矩阵。
选择权重矩阵 W W W(通常为高斯滤波器 G σ G_\sigma Gσ),我们可以得到卷积:
M ‾ l = W ∗ M l \overline{M}_l=W*M_l Ml=W∗Ml
该卷积的目的是得到 M I M_I MI 在周围像素上的局部平均。计算出的矩阵 M ‾ I \overline{M}_I MI有称为 Harris矩阵。
取决于该区域 ∇ I 的值,Harris 矩阵 M ‾ i 的特征值有三种情况 : ∙ 如果 λ 1 和 λ 2 都是很大的正数,则该 x 点为角点 ; ∙ 如果 λ 1 很大, λ 2 ≈ 0 ,则该区域内存在一个边,该区域内的平均 M i 的特征值不 会变化太大; ∙ 如果 λ 1 ≈ λ 2 ≈ 0 ,该区域内为空。 \begin{aligned}&\text{取决于该区域}\nabla\boldsymbol{I}\text{ 的值,Harris 矩阵}\overline{\boldsymbol{M}}_i\text{的特征值有三种情况}:\\&\bullet\text{ 如果 }\lambda_1\text{ 和 }\lambda_2\text{都是很大的正数,则该 }\mathbf{x}\text{ 点为角点};\\&\bullet\text{ 如果 }\lambda_1\text{ 很大,}\lambda_2\approx0\text{,则该区域内存在一个边,该区域内的平均 }\boldsymbol{M}_i\text{的特征值不}\\&\text{会变化太大;}\\&\bullet\text{ 如果 }\lambda_1\approx\lambda_2\approx0\text{,该区域内为空。}\end{aligned} 取决于该区域∇I 的值,Harris 矩阵Mi的特征值有三种情况:∙ 如果 λ1 和 λ2都是很大的正数,则该 x 点为角点;∙ 如果 λ1 很大,λ2≈0,则该区域内存在一个边,该区域内的平均 Mi的特征值不会变化太大;∙ 如果 λ1≈λ2≈0,该区域内为空。
python
from matplotlib import pyplot as plt
from PIL import Image
from numpy import *
from numpy import random
from scipy.ndimage import filters
def compute_harris_response(img, sigma=3):
imgx = zeros(img.shape)
filters.gaussian_filter(img, (sigma, sigma), (0, 1), imgx)
imgy = zeros(img.shape)
filters.gaussian_filter(img, (sigma, sigma), (1, 0), imgy)
Wxx = filters.gaussian_filter(imgx * imgx, sigma)
Wxy = filters.gaussian_filter(imgx * imgy, sigma)
Wyy = filters.gaussian_filter(imgy * imgy, sigma)
Wdet = Wxx * Wyy - Wxy * Wxy
Wtran = Wxx + Wyy
return Wdet / Wtran
def get_harris_points(harrisimg, min_dist=10, threshold=0.1):
corners = harrisimg.max() * threshold
harrisimg_t = (harrisimg > corners) * 1
coords = array(harrisimg_t.nonzero()).T
candidate_values = [harrisimg[c[0], c[1]] for c in coords]
index = argsort(candidate_values)
allow_locations = zeros(harrisimg.shape)
allow_locations[min_dist:-min_dist, min_dist:-min_dist] = 1
filtered_coords = []
for idx in index:
if allow_locations[coords[idx][0], coords[idx][1]] == 1:
filtered_coords.append(coords[idx])
allow_locations[coords[idx][0] - min_dist:coords[idx][0] + min_dist,
coords[idx][1] - min_dist:coords[idx][1] + min_dist] = 0
return filtered_coords
im = array(Image.open('jimei.jpg').convert('L'))
harrisim = compute_harris_response(im)
filtered_coords = get_harris_points(harrisim,6)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.subplot(121), plt.imshow(im, plt.cm.gray), plt.title('原始图像'), plt.axis('off')
plt.subplot(122), plt.imshow(harrisim, plt.cm.gray), plt.title('角点图像'), plt.axis('off')
plt.show()
- 在图像间寻找对应点
Harris角点检测器仅仅能够检测出图像中的兴趣点,没有给出通过比较图像间的兴趣点来寻找匹配角点的方法。需要在每个点加入描述子信息,给出一个比较描述子的方法。
兴趣点描述子是分配给兴趣点的一个向量,描述该点附近的图像的表观信息。描述子越好,寻找到的对应点越好。
Harris 角点的描述子通常是由周围图像像素块的灰度值,以及用于比较的归一化互
相关矩阵构成的。图像的像素块由以该像素点为中心的周围矩形部分图像构成。
通常,两个(相同大小)像素块 I 1 ( x ) I_1(\mathbf{x}) I1(x) 和 I 2 ( x ) I_2(\mathbf{x}) I2(x)的相关矩阵定义为:
c ( I 1 , I 2 ) = ∑ x f ( I 1 ( x ) , I 2 ( x ) ) c(I_1,I_2)=\sum_xf(I_1(\mathbf{x}),I_2(\mathbf{x})) c(I1,I2)=x∑f(I1(x),I2(x))
其中,函数 f f f随着相关方法的变化而变化。上式取像素块中所有像素位置 x的和。对于互相关矩阵,函数 f ( I 1 , I 2 ) = I 1 I 2 f(\boldsymbol I_1,\boldsymbol{I}_2)=\boldsymbol{I}_1\boldsymbol{I}_2 f(I1,I2)=I1I2,因此, c ( I 1 , I 2 ) = I 1 ⋅ I 2 c(\boldsymbol I_1,\boldsymbol{I}_2)=\boldsymbol{I}_1\cdot\boldsymbol{I}_2 c(I1,I2)=I1⋅I2,其中·表示向量乘法(按照行或者列堆积的像素)。 c ( I 1 , I 2 ) c(I_1,I_2) c(I1,I2)的值越大,像素块 I 1 I_1 I1和 I 2 的相似度越高。 1 I_2\text{的相似度越高。}^1 I2的相似度越高。1
归一化的互相关矩阵是互相关矩阵的一种变形,可以定义为:
n c c ( I 1 , I 2 ) = 1 n − 1 ∑ x ( I 1 ( x ) − μ 1 ) σ 1 ⋅ ( I 2 ( x ) − μ 2 ) σ 2 ncc\left(\boldsymbol{I}_1,\boldsymbol{I}2\right)=\frac{1}{n-1}\sum{x}\frac{\left(\boldsymbol{I}_1(\mathbf{x})-\mu_1\right)}{\sigma_1}\cdot\frac{\left(\boldsymbol{I}_2(\mathbf{x})-\mu_2\right)}{\sigma_2} ncc(I1,I2)=n−11x∑σ1(I1(x)−μ1)⋅σ2(I2(x)−μ2)
其中, n n n为像素块中像素的数目, μ 1 \mu_1 μ1和 μ 2 \mu_2 μ2表示每个像素块中的平均像素值强度, σ l \sigma_{\mathrm{l}} σl 和 σ 2 \sigma_{2} σ2分别表示每个像素块中的标准差。通过减去均值和除以标准差,该方法对图像亮度变化具有稳健性。
可以使用如下两个函数实现:
python
def get_descriptors(image, filtered_coords, wid=5):
desc = []
for coord in filtered_coords:
patch = image[coord[0] - wid:coord[0] + wid + 1, coord[1] - wid:coord[1] + wid + 1].flatten()
desc.append(patch)
return desc
def match(desc1, desc2, threshold=0.5):
n = len(desc1[0])
d = -ones((len(desc1), len(desc2)))
for i in range(len(desc1)):
for j in range(len(desc2)):
d1 = (desc1[i] - mean(desc1[i])) / std(desc1[i])
d2 = (desc2[j] - mean(desc2[j])) / std(desc2[j])
ncc_values = sum(d1 * d2) / (n - 1)
if ncc_values > threshold:
d[i, j] = ncc_values
ndx = argsort(-d)
matchscores = ndx[:, 0]
return matchscores
第一个函数的参数为奇数大小长度的方形灰度图像块,该图像块的中心为处理的像素点。该函数将图像块像素值压平成一个向量,然后添加到描述子列表中。第二个函数使用归一化的互相关矩阵,将每个描述子匹配到另一个图像中的最优的候选点。
2 SIFT
SIFT特征包括兴趣点检测器和描述子。SIFT描述子具有非常强的稳健性,SIFT特征对于尺度,旋转和亮度都具有不变性,可用于三维视角和噪声的可靠匹配。
2.1 兴趣点
SIFT 特征使用高斯差分函数来定位兴趣点:
D ( x , σ ) = [ G κ σ ( x ) − G σ ( x ) ] ∗ I ( x ) = [ G κ σ − G σ ] ∗ I = I κ σ − I σ D(\mathbf{x},\sigma)=[G_{\kappa\sigma}(\mathbf{x})-G_{\sigma}(\mathbf{x})]*I(\mathbf{x})=[G_{\kappa\sigma}-G_{\sigma}]*I=I_{\kappa\sigma}-I_{\sigma} D(x,σ)=[Gκσ(x)−Gσ(x)]∗I(x)=[Gκσ−Gσ]∗I=Iκσ−Iσ
其中, G σ G_\sigma Gσ是上一章中介绍的二维高斯核, I σ I_\sigma Iσ是使用 G σ G_\sigma Gσ模糊的灰度图像, κ \kappa κ是决定相差尺度的常数。兴趣点是在图像位置和尺度变化下 D ( x , σ ) D(\mathbf{x},\sigma) D(x,σ)的最大值和最小值点。
2.2 描述子
为了实现旋转不变性,基于每个点周围图像梯度的方向和大小,SIFT描述子引入了参考方向。SIFT描述子使用主方向描述参考方向。主方向使用方向直方图来度量。
2.3 检测兴趣点
可以使用开源工具包VLFeat提供的二进制文件来计算图像的SIFT特征。
python
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 = 'D:\\Python\\chapter2\\jimei2.jpg'
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)
sift.plot_features(im, l1, circle=False)
title(u'(a)SIFT特征', fontproperties=font)
subplot(132)
sift.plot_features(im, l1, circle=True)
title(u'(b)用圆圈表示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'(c)Harris角点', fontproperties=font)
show()
2.4 匹配描述子
对于将一幅图像中的特征匹配到另一幅图像的特征,一种稳健的准则是使用这两个特征距离和两个最匹配特征距离的比率。
python
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 = 'D:\Data\xiaoqu.jpg'
im2f = 'D:\Data\JiMei.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_twosided(d1, d2)
print '{} matches'.format(len(matches.nonzero()[0]))
figure()
gray()
sift.plot_matches(im1, im2, l1, l2, matches, show_below=True)
show()