Python计算机视觉第二章-局部图像描述子

目录

2.1 Harris角点检测器

2.2 SIFT(尺度不变特征变换)

2.2.1 兴趣点

2.2.2 描述子

2.2.3 检测兴趣点

2.2.4 匹配描述子


2.1 Harris角点检测器

        Harris 角点检测算法(也称 Harris & Stephens 角点检测器)是一个极为简单的角点 检测算法。该算法的主要思想是,如果像素周围显示存在多于一个方向的边,我们 认为该点为兴趣点。该点就称为角点。

arris 算法主要通过以下步骤工作:

  1. 计算梯度:首先计算图像的梯度(x方向和y方向)。

  2. 构建自相关矩阵:利用梯度信息构建自相关矩阵 MM,该矩阵用于描述图像中每个点的局部结构。

    计算响应函数:使用自相关矩阵计算响应函数 RR,这是一种衡量每个点是否为角点的指标。Harris 角点响应函数的计算公式为:
    1.                         ​​​​​​​        R=det(M)-k(trace(M))^{2}​​​​​​​

      其中 det(M)是矩阵 M的行列式,trace(M)是矩阵的迹,k 是一个经验常数,通常取值为 0.04 到 0.06。

  3. 非极大值抑制:对响应函数进行非极大值抑制,确定最终的角点位置。

  4. 应用阈值:根据阈值过滤响应函数,得到最终的角点。

分别为使用阈值 0.01、 0.05 0.1 检测出的角点:
import cv2
import numpy as np
from matplotlib import pyplot as plt

# 读取图像并转换为灰度图像
image = cv2.imread('empire.jpg', cv2.IMREAD_GRAYSCALE)
if image is None:
    raise FileNotFoundError("Image file 'empire.jpg' not found.")

# Harris 角点检测函数
def harris_corner_detection(image, threshold):
    # 使用 OpenCV 的 cornerHarris 方法
    dst = cv2.cornerHarris(image, 2, 3, 0.04)
    dst = cv2.dilate(dst, None)
    
    # 创建一个空白图像以标记角点
    corners = np.zeros_like(image)
    corners[dst > threshold * dst.max()] = 255
    
    return corners

# 设置不同的阈值
thresholds = [0.01, 0.05, 0.1]

# 生成结果图像
fig, axs = plt.subplots(1, len(thresholds) + 1, figsize=(15, 5))
axs[0].imshow(image, cmap='gray')
axs[0].set_title('Original Image')
axs[0].axis('off')

for i, threshold in enumerate(thresholds):
    corners = harris_corner_detection(image, threshold)
    axs[i + 1].imshow(corners, cmap='gray')
    axs[i + 1].set_title(f'Threshold = {threshold}')
    axs[i + 1].axis('off')

plt.show()

分析:

  1. 图像读取和转换:加载并转换图像为灰度图像,以便进行角点检测。
  2. Harris 角点检测函数:使用 cv2.cornerHarris 函数计算 Harris 角点响应,应用阈值来检测角点。
  3. 阈值设置:测试不同的阈值,生成不同的角点检测结果图像。
  4. 结果显示:使用 matplotlib 显示原始图像及不同阈值下的角点检测结果。

结果:

  • 阈值 0.01:较低的阈值会检测到更多的角点,包括许多可能是噪声的点。图像上角点分布可能较为密集。
  • 阈值 0.05:中等阈值能更好地平衡角点的检测数量和准确性。图像上的角点数量适中,主要集中在显著的角落位置。
  • 阈值 0.1:较高的阈值会减少检测到的角点数量,过滤掉一些弱的角点,只保留更显著和稳定的角点。

2.2 SIFT尺度不变特征变换

        SIFT 特征包括兴趣点检测器和描述 子。SIFT 描述子具有非常强的稳健性,这在很大程度上也是 SIFT 特征能够成功和流行的主要原因。

2.2.1 兴趣点

        SIFT 特征使用高斯差分函数来定位兴趣点:

        D(\mathbf{x}, \sigma)=\left[G_{\kappa \sigma}(\mathbf{x})-G_{\sigma}(\mathbf{x})\right] * \boldsymbol{I}(\mathbf{x})=\left[G_{\kappa \sigma}-G_{\sigma}\right] * \boldsymbol{I}=\boldsymbol{I}_{\kappa \sigma}-\boldsymbol{I}_{\sigma}

        其中,G σ 是上一章中介绍的二维高斯核, I σ 是使用 G σ 模糊的灰度图像, κ 是决定相
差尺度的常数。

2.2.2 描述子

        为了实 现旋转不变性,基于每个点周围图像梯度的方向和大小,SIFT 描述子又引入了参考方向。SIFT 描述子使用主方向描述参考方向。主方向使用方向直方图(以大小为权重)来度量。

下面我们基于位置、尺度和方向信息来计算描述子。SIFT 描述子在每个像素点附近选取子区域网格,在每个子区域内计算图像 梯度方向直方图。每个子区域的直方图拼接起来组成描述子向量。SIFT 描述子的标 准设置使用 4×4 的子区域,每个子区域使用 8 个小区间的方向直方图,会产生共 128 个小区间的直方图(4×4×8=128)。图 2-3 所示为描述子的构造过程:

2.2.3 检测兴趣点

        我们使用开源工具包 VLFeat 提供的二进制文件来计算图像的 SIFT 特征 :
​​​​​​​

import numpy as np
import cv2
import ctypes
from ctypes import POINTER, c_int, c_double, c_void_p

# 加载 VLFeat 的共享库
vlfeat = ctypes.CDLL('/path/to/vlfeat/bin/generic/libvl.so')  # 根据实际路径修改

# 定义 SIFT 相关函数的 ctypes 接口
vl_sift_new = vlfeat.vl_sift_new
vl_sift_new.argtypes = [c_int, c_int, c_int, c_int, c_int]
vl_sift_new.restype = c_void_p

vl_sift_process = vlfeat.vl_sift_process
vl_sift_process.argtypes = [c_void_p, POINTER(c_double)]
vl_sift_process.restype = None

vl_sift_get_keypoints = vlfeat.vl_sift_get_keypoints
vl_sift_get_keypoints.argtypes = [c_void_p, POINTER(c_int)]
vl_sift_get_keypoints.restype = None

vl_sift_get_descriptors = vlfeat.vl_sift_get_descriptors
vl_sift_get_descriptors.argtypes = [c_void_p, POINTER(c_double)]
vl_sift_get_descriptors.restype = None

vl_sift_delete = vlfeat.vl_sift_delete
vl_sift_delete.argtypes = [c_void_p]
vl_sift_delete.restype = None

# 读取图像并转换为灰度图像
def load_image(image_path):
    img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
    if img is None:
        raise FileNotFoundError("Image not found.")
    return img

# 计算 SIFT 特征
def compute_sift(image):
    # 转换图像为浮点数类型
    image_float = image.astype(np.float64)

    # 获取图像尺寸
    height, width = image.shape

    # 创建 SIFT 计算器
    sift = vl_sift_new(width, height, 3, 4, 2)
    
    if not sift:
        raise MemoryError("Failed to create SIFT object.")
    
    # 处理图像
    image_data = image_float.flatten()
    vl_sift_process(sift, image_data.ctypes.data_as(POINTER(c_double)))
    
    # 获取特征点和描述子
    num_keypoints = c_int()
    keypoints = np.zeros((0, 4))  # Initialize empty array to hold keypoints
    descriptors = np.zeros((0, 128))  # Initialize empty array to hold descriptors

    vl_sift_get_keypoints(sift, keypoints.ctypes.data_as(POINTER(c_int)))
    vl_sift_get_descriptors(sift, descriptors.ctypes.data_as(POINTER(c_double)))
    
    # 清理
    vl_sift_delete(sift)
    
    return keypoints, descriptors

# 主程序
if __name__ == "__main__":
    image_path = 'path_to_your_image.jpg'
    image = load_image(image_path)
    keypoints, descriptors = compute_sift(image)
    print(f"Number of keypoints: {len(keypoints)}")
    print(f"Descriptors shape: {descriptors.shape}")

结果:

特征点数量:可以用于了解图像中检测到的特征点数量。特征点数量较多通常意味着图像中有更多的可区分的特征。

描述符形状:每个特征点的描述符通常是一个 128 维的向量。描述符的形状为 (num_keypoints, 128),其中 num_keypoints 是特征点的数量。

2.2.4 匹配描述子

        对于将一幅图像中的特征匹配到另一幅图像的特征,一种稳健的准则(同样是由 Lowe 提出的)是使用这两个特征距离和两个最匹配特征距离的比率。

import cv2
import numpy as np

# 加载图像
img1 = cv2.imread('image1.jpg', cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread('image2.jpg', cv2.IMREAD_GRAYSCALE)

# 创建SIFT特征检测器
sift = cv2.SIFT_create()

# 检测关键点和计算描述子
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)

# 使用BFMatcher进行特征匹配
bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=False)

# 进行KNN匹配,k=2意味着每个描述子会找到两个最接近的匹配
matches = bf.knnMatch(des1, des2, k=2)

# 应用比率测试进行过滤
good_matches = []
for m, n in matches:
    if m.distance < 0.75 * n.distance:
        good_matches.append(m)

# 绘制匹配结果
img_matches = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)

# 显示结果
cv2.imshow('Matches', img_matches)
cv2.waitKey(0)
cv2.destroyAllWindows()

分析:

  1. 特征检测与描述:使用SIFT检测图像中的特征点并计算其描述子,SIFT能够提供旋转和尺度不变性。

  2. 特征匹配:使用暴力匹配器(BFMatcher)进行描述子的匹配,knnMatch方法找出每个描述子最近的两个匹配。

  3. 比率测试:根据Lowe的比率测试法,只保留那些第一个匹配距离明显小于第二个匹配距离的特征点对。此方法通过设置比率阈值(通常为0.75),降低了错误匹配的数量,确保了匹配的准确性。

  4. 结果可视化:通过cv2.drawMatches函数显示匹配结果,帮助直观评估匹配质量。

结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值