专栏文章 基于 opencv 实现 SIFT 特征提取与匹配实践

欲野法师 · 2021年06月25日 · 1407 次阅读

SIFT

sift 的定义

SIFT,即尺度不变特征变换(Scale-invariant feature transform,SIFT),是用于图像处理领域的一种描述。这种描述具有尺度不变性,可在图像中检测出关键点,是一种局部特征描述子。

sift 算法介绍

SIFT 特征是基于物体上的一些局部外观的兴趣点而与影像的大小和旋转无关。对于光线、噪声、微视角改变的容忍度也相当高。基于这些特性,它们是高度显著而且相对容易撷取,在母数庞大的特征数据库中,很容易辨识物体而且鲜有误认。使用 SIFT 特征描述对于部分物体遮蔽的侦测率也相当高,甚至只需要 3 个以上的 SIFT 物体特征就足以计算出位置与方位。在现今的电脑硬件速度下和小型的特征数据库条件下,辨识速度可接近即时运算。SIFT 特征的信息量大,适合在海量数据库中快速准确匹配

SIFT 算法特点:

  • SIFT 特征是图像的局部特征,其对旋转、尺度缩放、亮度变化保持不变性,对视角变化、仿射变换、噪声也保持一定程度的稳定性
  • 区分性(Distinctiveness)好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配
  • 多量性,即使少数的几个物体也可以产生大量的 SIFT 特征向量
  • 高速性,经优化的 SIFT 匹配算法甚至可以达到实时的要求
  • 可扩展性,可以很方便的与其他形式的特征向量进行联合

特征检测

SIFT 特征检测主要包括以下 4 个基本步骤:

  • 尺度空间极值检测:搜索所有尺度上的图像位置。通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点。
  • 关键点定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。
  • 方向确定:基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性
  • 关键点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度。这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变化

特征匹配

SIFT 特征匹配主要包括 2 个阶段

  • 第一阶段:SIFT 特征的生成,即从多幅图像中提取对尺度缩放、旋转、亮度变化无关的特征向量。
  • 第二阶段:SIFT 特征向量的匹配。

SIFT 特征的生成步骤:

  • 构建尺度空间,检测极值点,获得尺度不变性。
  • 特征点过滤并进行精确定位。
  • 为特征点分配方向值。
  • 生成特征描述子 以特征点为中心取 16×16 的邻域作为采样窗口,将采样点与特征点的相对方向通过高斯加权后归入包含 8 个 bin 的方向直方图,最后获得 4×4×8 的 128 维特征描述子。当两幅图像的 SIFT 特征向量生成以后,下一步就可以采用关键点特征向量的欧式距离来作为两幅图像中关键点的相似性判定度量。取图 1 的某个关键点,通过遍历找到图像 2 中的距离最近的两个关键点。在这两个关键点中,如果最近距离除以次近距离小于某个阈值,则判定为一对匹配点

环境准备

踩坑记录踩坑记录

  • 由于 SIFT 已经申请了专利,所以在高版本的 opencv 中,会出现错误,以前是 opencv4.0.1,然后安装版本为 opencv3.4.2.16
  • 由于 python3 没有 cPickle
  • 所以需要修改 python 源码中的 detect_eval.py 文件的导包方式

  • 无法安装 opencv-python 3.4.2.16
    版本回退到 python3.7
    解决方式

    pip uninstall opencv-python
    pip uninstall opencv-contrib-python
    

    使用怕 pip 在 cmd 中安装

    pip install opencv_python==3.4.2.16
    pip install opencv-contrib-python==3.4.2.16
    

具体实现

import cv2
from matplotlib import pyplot as plt
from imagedt.decorator import time_cost
print('cv version: ', cv2.__version__)

def bgr_rgb(img):
 (r, g, b) = cv2.split(img)
 return cv2.merge([b, g, r])

def orb_detect(image_a, image_b):
 orb = cv2.ORB_create()
 kp1, des1 = orb.detectAndCompute(image_a, None)
 kp2, des2 = orb.detectAndCompute(image_b, None)
 bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
 matches = bf.match(des1, des2)
 matches = sorted(matches, key=lambda x: x.distance)
 img3 = cv2.drawMatches(image_a, kp1, image_b, kp2,
    matches[:100], None, flags=2)
 return bgr_rgb(img3)

@time_cost
def sift_detect(img1, img2, detector='surf'):
 if detector.startswith('si'):
 print("sift detector......")
 sift = cv2.xfeatures2d.SURF_create()
 else:
 print("surf detector......")
 sift = cv2.xfeatures2d.SURF_create()
 kp1, des1 = sift.detectAndCompute(img1, None)
 kp2, des2 = sift.detectAndCompute(img2, None)
 bf = cv2.BFMatcher()
 matches = bf.knnMatch(des1, des2, k=2)
 good = [[m] for m, n in matches if m.distance < 0.5 * n.distance]
 img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good, None, flags=2)
 return bgr_rgb(img3)

if __name__ == "__main__":
 image_a = cv2.imread('../images/a/dm.png')
 image_b = cv2.imread('../images/b/cg.jpg')
 img = sift_detect(image_a, image_b)
 plt.imshow(img)
 plt.show()

运行 dome

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册