python计算机视觉编程第二章 局部图像描述子

局部图像描述子

本章将介绍两种局部图像描述子:Harris与SIFT。介绍完二者之后还会有一个实验。

Harris角点检测器

首先我们看到这一小节的名字的时候,可能会疑惑角点是什么?下面是其定义:

  • 角点是图像中具有显著角度变化的位置或特征点

说人话就是图像中那些看起来像是物体边缘相交或拐角的地方。这些点有助于进行下一步的处理,比方说确定物体边缘之类的任务。

由于opencv已经提供了封装好的模块以供使用。因此我们此处只简单介绍其原理,代码方面使用opencv来测试,就不跟着书上走了。

原理

我们先口头描述下要怎么做:

  • 利用矩形窗在图像上移动,若窗内包含有角点,则窗口向各个方向移动时,窗内的灰度值都会发生变化。从而达到检测图像角点的目的。如果像素周围显示存在多于一个方向的边,我们认为该点为兴趣点。该点就称为角点。

更具体的原理的话可以参见下面这篇文章,我自认为不可能讲的比这位作者更好:Harris特征点检测

代码

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 读取输入图像
img = cv2.imread('img.png', 0)

# 执行Harris角点检测
blockSize = 2  # 角点检测中考虑的邻域大小
ksize = 3  # Sobel算子的孔径大小
k = 0.04  # Harris角点检测方程中的参数
threshold = 0.01  # 角点标记的阈值

dst = cv2.cornerHarris(img, blockSize, ksize, k)

# 标记角点
dst = cv2.dilate(dst, None)
corners = np.argwhere(dst > threshold * dst.max())  # 获取角点的坐标

# 将OpenCV图像转换为适用于plt.imshow()的格式
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 显示结果
plt.imshow(img_rgb)
plt.title('Harris Corners')
plt.axis('off')

# 在图像上绘制红色的角点
for corner in corners:
    x, y = corner[1], corner[0]  # OpenCV坐标与plt.imshow()坐标的顺序不同
    plt.plot(x, y, 'ro', markersize=3)

plt.show()

运行结果:
请添加图片描述
点比较少是因为我们设置的阈值比较高,修改阈值可以得到更多的角点(下图为threshold = 0.001 ):
请添加图片描述

Harris角点检测算法优点:

  1. 旋转不变性,旋转转过一定角度但是其形状保持不变(特征值保持不变)。
  2. 对于图像灰度的仿射变化具有部分的不变性,但是由于仅仅使用了图像的一介导数,对于图像灰度平移变化不变;对于图像灰度尺度变化不变。

既然说具有旋转不变性,那么让我们来测试一下:
在这里插入图片描述
可以看到检测的角点基本没有变化,这应该是因为这个算子是通过计算图像中每个像素点的自相关矩阵,然后通过特征值分析来确定角点。然而自相关矩阵的特征值表示了局部图像区域的亮度变化情况。由于旋转操作不会改变图像的亮度变化情况,因此即使图像被旋转了,自相关矩阵的特征值仍然保持不变。

Harris角点检测算法缺点:
3. 它对尺度很敏感,不具备几何尺度不变性。
4. 提取的角点是像素级的。

这个没必要测试了,我们前面的参数有规定核的大小,缩放图像等于改变核的大小,因此肯定会对尺度敏感。

SIFT

描绘子

SITF中文名称为尺度不变特征变换。

SIFT描述子的主要优点是具有较好的尺度不变性(这就要比前面的这个描述子要好了)和旋转不变性,且对光照变化和噪声具有一定的鲁棒性。

SIFT的主要计算步骤如下:

  1. 尺度空间构建:使用高斯滤波器构建尺度空间金字塔,得到不同尺度的图像。
  2. 关键点检测:在尺度空间中检测稳定的关键点,通常使用高斯差分金字塔和极值点检测方法。
  3. 方向分配:对于每个关键点,计算其主导方向,以便后续的旋转不变性。
  4. 局部特征计算:以关键点为中心,将其周围划分为小的子区域(例如16x16或者4x4的小区域),计算每个子区域内的梯度方向和大小。
  5. 特征描述:将局部特征组织成一个向量表示,通常使用直方图统计梯度方向的分布。向量的维度可以根据需求设定。
  6. 描述子归一化:对特征向量进行归一化,使其对光照变化具有一定的鲁棒性。

需要特别说明的是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,σ)的最大值和最小值点。这些候选位置点通过滤波去除不稳定点。基于一些准则,比如认为低对比度和位于边上的点不是兴趣点,我们可以去除一些候选兴趣点。

上面讨论的兴趣点(关键点)位置描述子给出了兴趣点的位置和尺度信息。为了实现旋转不变性,基于每个点周围图像梯度的方向和大小,SIFT 描述子又引入了参考方向。SIFT 描述子使用主方向描述参考方向。主方向使用方向直方图(以大小为权重)来度量。

代码

import cv2
import matplotlib.pyplot as plt


def sift_demo():
    # 读取图片
    img = cv2.imread("img.jpg")
    # 转换为灰度图
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 创建sift对象
    sift = cv2.xfeatures2d.SIFT_create()
    # 检测关键点
    kp = sift.detect(img, None)
    # 计算描述符
    kp, des = sift.compute(img, kp)
    # 可视化,由于是远程,所以用matplotlib
    img = cv2.drawKeypoints(img, kp, img)
    plt.imshow(img)
    plt.show()
    
    
if __name__ == "__main__":
    sift_demo()

在这里插入图片描述
从这张图可能看不出来,看一下原始的图片
请添加图片描述
SIFT算法的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。

匹配

前文提到,SIFT具有一定的不变性,那么用它来做不同视角的匹配自然是极好的。
直接上代码:

import cv2
import matplotlib.pyplot as plt

def sift_kp(image):
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    sift = cv2.xfeatures2d.SIFT_create()
    kp, des = sift.detectAndCompute(gray_image, None)
    img = cv2.drawKeypoints(image, kp, image, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
    return img, kp, des

def sift_match(img1, img2):
    img1, kp1, des1 = sift_kp(img1)
    img2, kp2, des2 = sift_kp(img2)
    bf = cv2.BFMatcher()
    matches = bf.knnMatch(des1, des2, k=2)
    good = []
    for m, n in matches:
        if m.distance < 0.75 * n.distance:
            good.append([m])
    img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good, None, flags=2)
    plt.imshow(img3)
    plt.show()
    
img1 = cv2.imread('img.png')
img2 = cv2.imread('img.png')
# 转为RGB
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)

# img2旋转90度
img2 = cv2.rotate(img2, cv2.ROTATE_90_CLOCKWISE)
sift_match(img1, img2)

在这里插入图片描述

结果分析:可以看到每个点都匹配正确了,但是这是为什么呢?我们可以看到sift_kp(image)函数返回了三个变量,img不必多说,另外两个变量分别是点的位置、点的特征。其中点的特征包含了特定区域的信息(前面有提到过)。也正是因为这个,我们才可以在sift_match(img1, img2)中去匹配点,进而得到最后的结果。

实验

课本上原本是以一个实验结束,可惜接口已经关闭再加上最近比较忙,因此实验暂时搁置,如果后面遇到了合适的数据集再来补全。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值