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

2.1 Harris角点检测器

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

我们把图像域中点x上的对称半正定矩阵定义为:

其中为包含导数 的图像梯度(我们已经在第1章定义了图像的导数和梯度)。由于该定义,的秩为1,特征值为。选择权重矩阵,可以得到卷积:

该卷积的目的是得到在周围像素上的局部平均。在不需要实际计算特征值的情况下,为了把重要的情况和其他情况分开,引入了指示函数

为了去除加权常数,通常改为商数作为指示器,即

下面编写Harris角点检测程序

python 复制代码
import numpy as np
from scipy.ndimage import gaussian_filter
from PIL import Image
import matplotlib.pyplot as plt


def compute_harris_response(im, sigma=3):
    """在一幅灰度图像中,对每一个像素计算Harris角点检测器响应函数"""

    # 计算导数
    imx = np.zeros(im.shape)
    gaussian_filter(im, (sigma, sigma), (0, 1), imx)

    imy = np.zeros(im.shape)
    gaussian_filter(im, (sigma, sigma), (1, 0), imy)

    # 计算 Harris 矩阵的分量
    Wxx = gaussian_filter(imx * imx, sigma)
    Wxy = gaussian_filter(imx * imy, sigma)
    Wyy = gaussian_filter(imy * imy, sigma)

    # 计算特征值和迹
    Wdet = Wxx * Wyy - Wxy * Wxy
    Wtr = Wxx + Wyy

    return Wdet / Wtr


def get_harris_points(harrisim, min_dist=10, threshold=0.1):
    """从一幅Harris响应图像中返回角点,min_dist为分割角点和图像边界的最少像素数目"""

    # 寻找高于阈值的候选角点
    corner_threshold = harrisim.max() * threshold
    harrisim_t = (harrisim > corner_threshold) * 1

    # 得到候选点的坐标
    coords = np.array(harrisim_t.nonzero()).T

    # 以及它们的 Harris 响应值
    candidate_values = [harrisim[c[0], c[1]] for c in coords]

    # 对候选点按照 Harris 响应值进行排序
    index = np.argsort(candidate_values)

    # 将可行点的位置保存到数组中
    allowed_locations = np.zeros(harrisim.shape)
    allowed_locations[min_dist:-min_dist, min_dist:-min_dist] = 1

    # 按照 min_distance 原则,选择最佳 Harris 点
    filtered_coords = []
    for i in index:
        if allowed_locations[coords[i][0], coords[i][1]] == 1:
            filtered_coords.append(coords[i])
            allowed_locations[coords[i][0] - min_dist:coords[i][0] + min_dist,
            coords[i][1] - min_dist:coords[i][1] + min_dist] = 0
    return filtered_coords


if __name__ == "__main__":
    # 读取图像
    im = np.array(Image.open('eg.jpg').convert('L'))

    # 计算 Harris 响应
    harrisim = compute_harris_response(im)
    filtered_coords1 = get_harris_points(harrisim, min_dist=6, threshold=0.1)
    filtered_coords2 = get_harris_points(harrisim, min_dist=6, threshold=0.05)

    # 显示结果并保存图像
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.subplot(151), plt.imshow(im, cmap='gray'), plt.title('原图'), plt.axis('off')
    plt.subplot(152), plt.imshow(harrisim, cmap='gray'), plt.title('角点响应图'), plt.axis('off')
    plt.subplot(153), plt.imshow(im, cmap='gray'), plt.plot([p[1] for p in filtered_coords1],
                                                            [p[0] for p in filtered_coords1], 'r.'),
    plt.title('阈值为0.1'), plt.axis('off')
    plt.subplot(154), plt.imshow(im, cmap='gray'), plt.plot([p[1] for p in filtered_coords2],
                                                            [p[0] for p in filtered_coords2], 'r.'),
    plt.title('阈值为0.05'), plt.axis('off')

    # 保存图像为文件
    plt.savefig('harris.jpg', dpi=300, bbox_inches='tight')

    # 显示图像
    plt.show()

其运行结果如下图所示:

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

SIFT(尺度不变特征变换,Scale-Invariant Feature Transform)是一种用于图像特征提取和描述的计算机视觉算法。它可以有效地检测和描述图像中的局部特征,具有对图像缩放、旋转和光照变化的较强不变性。

2.2.1 兴趣点

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

其中,是二维高斯核,是使用模糊的灰度图像,是决定相差尺度的常数。兴趣点是在图像位置和尺度变化下的最大值和最小值点。

2.2.2 描述子

SIFT描述子在每个像素点附近选取子区域网格,在每个子区域内计算图像梯度方向直方图,每个子区域的直方图拼接起来组成描述子向量。

2.2.3 检测兴趣点

我们使用书上的示例代码运行不出来,故使用opencv库来检测兴趣点,运行代码如下所示:

python 复制代码
import cv2
import numpy as np
import matplotlib.pyplot as plt

def detect_and_plot_features(image_path):
    # 读取图像并转换为灰度
    image = cv2.imread(image_path)
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

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

    # 检测 SIFT 特征点
    keypoints, descriptors = sift.detectAndCompute(gray_image, None)

    # 绘制特征点
    image_with_keypoints = cv2.drawKeypoints(image, keypoints, None)

    # 显示图像
    plt.figure(figsize=(10, 10))
    plt.imshow(cv2.cvtColor(image_with_keypoints, cv2.COLOR_BGR2RGB))
    plt.axis('off')
    plt.title('SIFT KeyPoints')
    plt.show()

# 调用函数
detect_and_plot_features('touxiang.jpg')  # 替换为您自己的图像路径

绘制出的SIFT特征位置图像如下所示:

2.2.4 匹配描述子

在计算机视觉中,描述子匹配是特征匹样本分析中的一个重要步骤,特别是在图像匹配和物体识别任务中。描述子匹配的核心思想是找到两个图像之间相似的特征点,并用相应的描述子进行匹配。以下是使用 OpenCV 进行描述子匹配的示例代码,采用 SIFT 特征。

python 复制代码
import cv2
import numpy as np
import matplotlib.pyplot as plt


def match_descriptors(image1_path, image2_path):
    # 读取图像并转换为灰度
    img1 = cv2.imread(image1_path)
    img2 = cv2.imread(image2_path)

    gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
    gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

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

    # 检测特征点及计算描述子
    keypoints1, descriptors1 = sift.detectAndCompute(gray1, None)
    keypoints2, descriptors2 = sift.detectAndCompute(gray2, None)

    # 使用 BFMatcher 进行匹配
    bf = cv2.BFMatcher(cv2.NORM_L2)  # 使用 L2 距离
    matches = bf.knnMatch(descriptors1, descriptors2, k=2)  # 找到每个描述子的 2 个最佳匹配

    # 筛选出良好的匹配(比率测试)
    good_matches = []
    for m, n in matches:
        if m.distance < 0.75 * n.distance:  # Lowe's ratio test
            good_matches.append(m)

            # 绘制匹配结果
    match_image = cv2.drawMatches(img1, keypoints1, img2, keypoints2, good_matches, None,
                                  flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)

    plt.figure(figsize=(10, 10))
    plt.imshow(cv2.cvtColor(match_image, cv2.COLOR_BGR2RGB))
    plt.axis('off')
    plt.title('Matching Keypoints')
    plt.show()


# 调用函数,替换图像路径
match_descriptors('touxiang.jpg', 'touxiang.jpg')  # 替换为您自己的图像路径

匹配结果如下图

2.3 匹配地理标记图像

我们使用局部描述子来匹 配带有地理标记的图像。

2.3.1 从百度地图下载地理标记图像

从百度地图下载地理标记图像的过程涉及 API 的使用,但百度地图的 API 并不直接支持下载图像。通常情况下,我们可以从地图 API 获取地理标记、位置信息及其相关图像的链接,然后手动下载或使用 Python 下载图像。

1.获取百度地图 API 密钥

在使用百度地图 API 之前,您需要先注册一个百度开发者账户并申请一个 API 密钥。可以访问https://lbsyun.baidu.com来申请

2.使用 Python 获取相关数据

以下是一个基本的示例,展示如何使用 Python 和百度地图 API 查找某个地点的位置信息,并下载该地点的静态地图图像。

python 复制代码
import requests  
import os  

# 百度地图 API 配置  
AK = 'YOUR_BAIDU_MAP_API_KEY'  # 替换为您的 API 密钥  
location = '39.915,116.404'  # 替换为目标地理位置的经纬度,例如北京的经纬度  

# 构建百度地图静态地图 API 请求 URL  
map_url = f'http://api.map.baidu.com/staticimage/v2?ak={AK}&center={location}&width=600&height=400&markers={location}&markerStyles=l,red&zoom=12'  

# 发起请求并下载图像  
response = requests.get(map_url)  

# 创建用于保存图像的目录  
os.makedirs('baidu_maps', exist_ok=True)  

# 指定保存的文件名  
image_path = os.path.join('baidu_maps', 'map_image.jpg')  

# 保存图像  
with open(image_path, 'wb') as file:  
    file.write(response.content)  

print(f"地图图像已下载到: {image_path}")  

2.3.2 使用局部描述子匹配

我们刚才已经下载了这些图像,下面需要对这些图像提取局部描述子。在这种情 况下,我们将使用前面部分讲述的SIFT特征描述子。

python 复制代码
import sift
nbr_images = len(imlist)
matchscores = zeros((nbr_images,nbr_images))
for i in range(nbr_images):
    for j in range(i,nbr_images): # 仅仅计算上三角
        print('comparing', imlist[i], imlist[j])
        l1, d1 = sift.read_features_from_file(featlist[i])
        l2, d2 = sift.read_features_from_file(featlist[j])
        matches = sift.match_twosided(d1, d2)
        nbr_matches = sum(matches > 0)
        print('number of matches = ', nbr_matches)
        matchscores[i, j] = nbr_matches
# 复制值
for i in range(nbr_images):
    for j in range(i + 1, nbr_images):  # 不需要复制对角线
        matchscores[j, i] = matchscores[i, j]

2.3.3 可视化连接图像

为了可视化连接通过局部特征匹配找到的图像,您可以使用 OpenCV 将匹配的特征绘制在一起。下述步骤将展示如何使用 Python 和 OpenCV 可视化连接匹配的图像。

python 复制代码
import cv2  
import numpy as np  

# 加载图像  
img1 = cv2.imread('baidu_maps/map_image.jpg')  # 百度地图图像  
img2 = cv2.imread('your_other_image.jpg')  # 要匹配的另一张图像  

# 转换为灰度图  
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)  
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)  

# 创建 ORB 检测器  
orb = cv2.ORB_create()  

# 找到关键点和描述子  
keypoints1, descriptors1 = orb.detectAndCompute(gray1, None)  
keypoints2, descriptors2 = orb.detectAndCompute(gray2, None)  

# 创建 BFMatcher 对象  
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)  

# 匹配描述子  
matches = bf.match(descriptors1, descriptors2)  

# 排序匹配结果  
matches = sorted(matches, key=lambda x: x.distance)  

# 获取关键点的位置  
points1 = np.zeros((len(matches), 2), dtype=np.float32)  
points2 = np.zeros((len(matches), 2), dtype=np.float32)  

for i, match in enumerate(matches):  
    points1[i, :] = keypoints1[match.queryIdx].pt  # 图像1的关键点  
    points2[i, :] = keypoints2[match.trainIdx].pt  # 图像2的关键点  

# 连接图像  
# 创建一个新图像,宽度为两幅图像的宽度之和  
height = max(img1.shape[0], img2.shape[0])  
new_width = img1.shape[1] + img2.shape[1]  
result = np.zeros((height, new_width, 3), dtype=np.uint8)  
result[:img1.shape[0], :img1.shape[1]] = img1  
result[:img2.shape[0], img1.shape[1]:] = img2  

# 绘制匹配结果  
for i in range(len(matches)):  
    pt1 = (int(points1[i, 0]), int(points1[i, 1]))  
    pt2 = (int(points2[i, 0]) + img1.shape[1], int(points2[i, 1]))  
    cv2.line(result, pt1, pt2, (0, 255, 0), 1)  # 绘制线条,颜色为绿色  

# 显示结果  
cv2.imshow("Matched Image", result)  
cv2.waitKey(0)  
cv2.destroyAllWindows()  
相关推荐
youcans_20 分钟前
OpenAI全新发布o1模型:开启 AGI 的新时代
人工智能·chatgpt·agi
黑色叉腰丶大魔王27 分钟前
《自然语言处理 Transformer 模型详解》
人工智能·自然语言处理·transformer
jianglq1 小时前
C++20 协程:异步编程的新纪元
算法·c++20
wniuniu_2 小时前
动态规划前---选----
算法·动态规划
lzb_kkk3 小时前
【Redis】redis5种数据类型(哈希)
开发语言·redis·算法·缓存·哈希算法
鱼跃鹰飞3 小时前
Leetcode面试经典150题-202.快乐数
算法·leetcode·面试
鱼跃鹰飞3 小时前
Leetcode面试经典150题-82.删除排序链表中的重复元素II
算法·leetcode·面试
ersaijun3 小时前
【Obsidian】当笔记接入AI,Copilot插件推荐
人工智能·笔记·copilot
格林威4 小时前
Baumer工业相机堡盟工业相机如何通过BGAPISDK使用短曝光功能(曝光可设置1微秒)(C语言)
c语言·开发语言·人工智能·数码相机·计算机视觉
仰望大佬0074 小时前
HalconDotNet中的图像视频采集
数码相机·计算机视觉·c#·音视频·halcon