SITF特征提取匹配

学习

Posted by simplex on June 7, 2021

SIFT 特征提取匹配

一、任务

输入两张图像,输出两张图像的特征点,并且将两张图中相同的点找出来

二、SIFT

SIFT: The scale-invariant feature transform 尺度不变特征变换,用于检测和表述图像中的局部特征。

特点

  • 即使在有比较大的噪声和部分遮挡下也可以识别对象,因为 SIFT 特征描述对均匀缩放、方向、光照变化是不变的,并且对仿射失真部分不变
  • 模仿人的感受野的变化
  • 区分性好,能够在海量特征数据库中进行快速准确的区分信息进行匹配
  • 多量性,就算只有单个物体,也能产生大量特征向量
  • 高速性,能够快速的进行特征向量匹配
  • 可扩展性,能够与其它形式的特征向量进行联合

2.1 SIFT算法

  • 尺度空间极值检测

  • 特征点定位
  • 方向确定
  • 特征点描述

2.1.1尺度空间极值检测

首先检测感兴趣的点,这些点在 SIFT 框架中被称为特征点。SIFT使用高斯差分金字塔(Difference of Gaussians DoG)来的极大或极小值作为特征点。

DoG图像定义\(D(x, y, k_i\sigma) = L(x, y, k_{i + 1}\sigma) - L(x, y, k_i\sigma)\),

\(L(x, y, k_i\sigma)\)是原图像\(I(x, y)\)高斯模糊之后的结果

最后一个参数\(k_i\sigma\),是尺度空间的参数

高斯金字塔和DoG有以下特征点:

  • \(k\sigma\)这个参数,由所在组(octave)以及所在组中的层数决定

  • DoG由原本高斯金字塔的组(octave),通过在组内相邻层做差分得到

极值检测

每个像素点\(D(x, y, \sigma)\)要和其同一\(\sigma\)的像素以及相邻层的像素进行比较。如果像素值是所有比较像素中的最大值或最小值,则将其选为候选特征点。

特征点定位

主要就是剔除一些不好的特征点。最初的方法是在候选特征点的位置和尺度上定位每个特征点。新方法计算极值的插值位置,提高来匹配性和稳定性。

剔除不好点的关键

  • 丢弃低对比度的特征点
  • 消除边缘效应

方向确定

对于极值点\(D(x, y, \sigma)\),对应的高斯金字塔中的位置的\(L(x, y, \sigma)\)

由公式 \(m(x, y) = \sqrt{ {(L(x + 1, y) - L(x - 1, y))}^2 + {(L(x , y + 1) - L(x, y - 1))}^2}\\ \theta (x, y) = arctan((L(x , y + 1) - L(x, y - 1)), (L(x + 1, y) - L(x - 1, y)))\) 得到特征点的方向和模

然后使用直方图统计特征点邻域(\(3\times 1.5\sigma\)范围)内像素对应的梯度方向和幅值。梯度方向的直方图的横轴是梯度方向的角度(梯度方向的范围是0到360度,8柱,10柱,36柱都可以),纵轴是梯度方向对应梯度幅值的累加,在直方图的峰值就是特征点的主方向,保留峰值大于主方向峰值80%的方向作为该特征点的辅方向。

得到特征点的主方向后,对于每个特征点可以得到三个信息\((x,y,\sigma,θ)(x,y,σ,θ\)),即位置、尺度和方向。由此可以确定一个SIFT特征区域,中心表示特征点位置,半径表示关键点的尺度,箭头表示主方向,以及辅方向。

特征点描述

下面就需要使用一组向量(KeyPoint Descriptor)来描述特征点

对于特征点\(F(x,y,\sigma,θ)\),要以特征点为中心,在附近邻域内将坐标轴旋转\(θ\),旋转后以主方向为中心取 \(16×16\)的窗口,分成\(4×4\)个子区域。在最后在每个\(4×4\)的子区域上绘制8个方向的梯度直方图,计算每个梯度方向的累加值,即可形成一个种子点。最终,得到总共有\(16\times8=128\)维的特征向量。

SIFT特征的匹配

  • 假设,输入的两幅图像分别为图像1和图像2,使用上述算法从输入图像中获得 SIFT 特征。
  • 对于图像2的特征点,使用基于欧几里德距离的最近邻方法来匹配图1中的两个特征点点。对于最近邻距离与第二近邻距离之比大于 d的那些关键点,将拒绝匹配,其中d通常取0.8。然后就完成了匹配。

​ 流程图

lex=>start: 输入图片1,图片2
sift=>operation: 提取sift特征
knn=>operation: 使用最近邻方法进行匹配
match=>operation: 将匹配的特征点连线
e=>end: 结束

lex->sift->knn->match->e

三 实验结果

原图  
1 2
特征图 匹配图
feature b

四 源代码以及关键变量解释

import cv2
import numpy as np

MIN_MATCH_COUNT = 4
file_path = r"D:\pycharm_project\datasets\pic/"
img1 = cv2.imread(file_path + "1.jpg")
img2 = cv2.imread(file_path + "2.jpg")
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
sift = cv2.xfeatures2d.SIFT_create()
match = cv2.FlannBasedMatcher(dict(algorithm=2, trees=1), {})
# 计算sift特征点和特征值
kp1, de1 = sift.detectAndCompute(g1, None)
kp2, de2 = sift.detectAndCompute(g2, None)
print(de2)
"""
kp表示关键点的结构体包括
pt: 坐标
size: 特征点邻域直径,尺度
angle: 特征点的方向
response: 特征点的模
octave: 特征点所在的组(octave)
class_id: 聚类的编号

de: 表示特征向量的值是一个n*128的数组
"""
# 画出特征点
winp = cv2.namedWindow("point", 0)
cv2.resizeWindow(winp, 600, 800)
img3 = cv2.drawKeypoints(img1, kp1, img1, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
img4 = cv2.drawKeypoints(img2, kp2, img2, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
hmerge = np.hstack((img3, img4))  # 水平拼接
# cv2.imshow("point", hmerge)  # 拼接显示为gray
# cv2.imwrite(r"D:\pycharm_project\datasets\pic/feature.jpg", hmerge)
# 匹配,使用knn算法,找出最近的两个匹配点
m = match.knnMatch(de1, de2, 2)
"""
m是长度为2的数组的的list,其中元素m[i][0], m[i][1]表示的是 Dmatch结构体
其中包括
queryIdx : 查询点的索引(当前要寻找匹配结果的点在它所在图片上的索引).
trainIdx : 被查询到的点的索引(存储库中的点的在存储库上的索引)
imgIdx :
distance: 两点之间的距离
"""

m = sorted(m, key=lambda x: x[0].distance)
ok = [m1 for (m1, m2) in m if m1.distance < 0.8 * m2.distance]



med = cv2.drawMatches(img1, kp1, img2, kp2, ok, None)

cv2.imshow("b", med)
cv2.imwrite(file_path + "b.jpg", med)
cv2.waitKey(0)