SIFT算法详细原理与应用

news2025/6/9 5:04:49

SIFT算法详细原理与应用

1 SIFT算法由来

1.1 什么是 SIFT?

SIFT,全称为 Scale-Invariant Feature Transform(尺度不变特征变换),是一种用于图像特征检测和描述的经典算法。它通过提取图像中的局部关键点,并为每个关键点生成具有尺度和旋转不变性的描述子,使其能够在不同的图像中进行特征匹配。SIFT 算法尤其适合处理视角变化、尺度变换、部分遮挡和光照变化的问题,因此被广泛应用于计算机视觉领域。通过sift算法可生成图像的特征向量(128维),对图像特征向量可以理解为图像的细节纹理特征,它具有光照,角度,尺寸大小不变性,一个特征向量可能为某个人脸图像的局部一个特征纹理,比如为人脸图像中鼻子的特征,不同人脸图像的鼻子特征是不同的,故,通过收集人脸图像的所有特征向量,可以进行人脸识别,图像匹配等场景。

1.2 SIFT 的发展历程

SIFT 由计算机科学家 David G. Lowe 于 1999 年首次提出,并在 2004 年发表的论文《Distinctive Image Features from Scale-Invariant Keypoints》中进一步完善。其革命性的设计使得 SIFT 成为了特征提取领域的重要里程碑。
虽然 SIFT 曾因专利保护限制了开源使用,但随着专利过期(美国专利于 2020 年到期),SIFT 再次成为开源社区的重要工具,并在许多实际项目中被广泛应用。

此外,SIFT 的思想也启发了许多后续算法的诞生,例如 SURF(Speeded-Up Robust Features)和 ORB(Oriented FAST and Rotated BRIEF),进一步推动了特征提取技术的发展。

2 SIFT算法详细原理

  • 根据高斯函数生成高斯金字塔
    生成高斯金字塔的作用可以理解为 通过相机调整距离物体的距离拍摄的物理,实际上对于物体本身,不管相机距离物体的距离远近,总有一些特征是不变的,比如物体的边缘及轮廓等。
    高斯金字塔底层的图像可以理解为相机近距离拍摄物体的图像,顶层可以理解为相机远距离拍摄物体的图像。故sift具有图像尺寸不变性及旋转不变性(再生成种子时会进行角度旋转变换及归一化处理),而角点检测算法Harris只具有旋转不变性
    在这里插入图片描述
    通过模拟不同距离的图像,尽可能的找到图像的所有特征点

  • 根据高斯金字塔生成差分金字塔(DOG)
    在这里插入图片描述
    通过对高斯金字塔同一组中相邻的图像进行相减,得到DOG差分图像

  • 根据DOG差分图像找到关键点(极值点)
    在这里插入图片描述
    为了寻找尺度空间的极值点,每一个采样点要和它所有的相邻点比较,看其是否比它的图像域和尺度域的相邻点大或者小。如图所示,中间的检测点和它同尺度的8个相邻点和上下相邻尺度对应的9×2个点共26个点比较,以确保在尺度空间和二维图像空间都检测到极值点。 一个点如果在DOG尺度空间本层以及上下两层的26个领域中是最大或最小值时,就认为该点是图像在该尺度下的一个特征点,如图所示。

  • 关键点过滤
    真正的关键点都是灰度值在,x,y方向都有明显变化的,即:一个定义不好的高斯差分算子的极值在横跨边缘的地方有较大的主曲率,而在垂直边缘的方向有较小的主曲率。

  • 根据找到的关键点计算出对应的描述子

    • 计算关键点周围领域的直方图(包括变化尺度,角度(角度可以分为8个方向)),得出当前关键点的主方向(变换尺度最大的为主方向)
      在这里插入图片描述
      通过对每个关键点领域做直方图计算,可以得到该关键点的主方向及大小,由此每个关键点有三个基本的属性:(关键点位置,主方向(与x轴的角度),主方向的大小)
    • 对关键点周围领域进行旋转,将主方向旋转成与x方向一致的方向,以确保旋转不变性
      在这里插入图片描述
    • 将关键点领域分为4x4的 16个子区域,每个子区域分别计算他们的直方图,然后生成由右图组成的16个种子,每个种子包含八个方向的变化尺度。
      在这里插入图片描述
    • 生成描述子
      根据关键点生成的16个种子的八维向量,组成一个4x4x8-128维的向量。再将特征向量进行归一化(就是使的特征向量的值1,成为单位向量)。即每一个关键点就能得到一个128维的向量,也成为一个关键点的描述子。
  • 两个图像描述子的匹配
    通过sift算法分别生成两个图像的各自的多个描述子,分别计算两个图像描述子向量之间的距离,计算两个向量的距离有多种
    计算两个向量的距离有多种方式

    • 曼哈顿距离欧氏距离
    • 欧氏距离
    • 切比雪夫距离
    • 闵可夫斯基距离
    • 标准化欧式距离
    • 余弦距离
    • 汉明距离
    • 杰卡德距离
    • 马氏距离
      具体详见:9个机器学习算法常见距离计算公式

详见:
经典算法研究(1):SIFT算法1
SIFT算法详解
SIFT 全面解析:原理、实现与应用
经典的图像匹配算法----SIFT

3 SIFT算法应用

详见:OpenCV入门学习笔记之Harris角点检测与SIFT特征匹配算法

3.1 cv2中sift算法的使用

""" SIFT,全称为 Scale-Invariant Feature Transform(尺度不变特征变换)"""
src_img = cv2.imread('resources/sift_2.jpg')

img = copy.deepcopy(src_img)
gray1 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

gray = copy.deepcopy(gray1)

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

# 找出图像中的关键点
kp = sift.detect(gray, None)

# 在图中画出关键点
sift_src_img = cv2.drawKeypoints(img, kp, img, color=(255, 0, 0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

sift_gray_img = cv2.drawKeypoints(gray, kp, gray, color=(255, 0, 0),
                                 flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# 计算关键点对应的SIFT特征向量
# kp, des = sift.compute(gray, kp)

# 调整子图间距
plt.subplot(1, 2, 1), plt.imshow(src_img), plt.title('Origin')
plt.subplot(1, 2, 2), plt.imshow(sift_src_img), plt.title('Sift_Origin')
# plt.subplot(2, 2, 3), plt.imshow(gray, cmap='gray'), plt.title('Gray')
# plt.subplot(2, 2, 4), plt.imshow(sift_gray_img, cmap='gray'), plt.title('Sift_Gray')
plt.show()

在这里插入图片描述
其中右图中的每个关键点是由位置,方向,方向大小组成,通过断点调试可以看到这些信息均包含在kp数组里
在这里插入图片描述

3.2 cv2中sift算法进行图像拼接

如下图有不同角度拍摄的书面,两个图片有共同部分,也有不同部分,现在要对这两个图片进行拼接,共同组成一个完整的图片。

请添加图片描述

左图
请添加图片描述

右图

拼接的过程:
1, 通过sift求出两个图片的特征向量
2,通过knn匹配算法找到两个图片的多对(四队以上)相互匹配的关键点坐标
3,通过四对关键点坐标对B图像进行投射变换
4,A图像与变换后的B图像进行拼接得到原图
下面是完整的

import copy
import time
from datetime import datetime
from typing import Any

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

#  modify backend to TkAgg
matplotlib.use('TkAgg')

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

#  modify backend to TkAgg
matplotlib.use('TkAgg')

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题


class SIFT():
    def __init__(self):
        pass


    def sift_keypoints_draw(self):
        """ SIFT,全称为 Scale-Invariant Feature Transform(尺度不变特征变换)"""
        src_img = cv2.imread('resources/sift_2.jpg')

        img = copy.deepcopy(src_img)
        gray1 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        gray = copy.deepcopy(gray1)

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

        # 找出图像中的关键点
        kp = sift.detect(gray, None)

        # 在图中画出关键点
        sift_src_img = cv2.drawKeypoints(img, kp, img, color=(255, 0, 0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

        sift_gray_img = cv2.drawKeypoints(gray, kp, gray, color=(255, 0, 0),
                                         flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

        # 计算关键点对应的SIFT特征向量
        # kp, des = sift.compute(gray, kp)

        # 调整子图间距
        plt.subplot(1, 2, 1), plt.imshow(src_img), plt.title('Origin')
        plt.subplot(1, 2, 2), plt.imshow(sift_src_img), plt.title('Sift_Origin')
        # plt.subplot(2, 2, 3), plt.imshow(gray, cmap='gray'), plt.title('Gray')
        # plt.subplot(2, 2, 4), plt.imshow(sift_gray_img, cmap='gray'), plt.title('Sift_Gray')
        plt.show()


    def image_sift(self, src_image_a, src_image_b):
        # 对两个图像通过sift 分别求出各自的特征向量,根据特征向量求出两个图像的所有相同的部分的点坐标信息,并返回
        """ SIFT,全称为 Scale-Invariant Feature Transform(尺度不变特征变换)"""
        gray_a = cv2.cvtColor(src_image_a, cv2.COLOR_BGR2GRAY)
        gray_b = cv2.cvtColor(src_image_b, cv2.COLOR_BGR2GRAY)

        # generate kps and feature vector
        sift = cv2.SIFT_create()
        (kps_a, features_a) = sift.detectAndCompute(gray_a, None)
        (kps_b, features_b) = sift.detectAndCompute(gray_b, None)

        n_kps_a = np.float32([kp.pt for kp in kps_a])
        n_kps_b = np.float32([kp.pt for kp in kps_b])

        # feature vector match
        ratio = 0.95
        n = 2
        matcher = cv2.BFMatcher()

        # 获取两个图像之间所有的匹配点(通过knn算法 进行匹配 对于A图像的每个匹配点,都在B中找到两个最近的匹配点)
        raw_matches = matcher.knnMatch(features_a, features_b, n)

        good_matches = []
        # 通过第一匹配点与第二匹配点的距离差异大小,过滤掉可能为不真实的匹配点
        for match in raw_matches:
            if match[0].distance < ratio * match[1].distance:
                good_matches.append(match[0])

        if len(good_matches) < 4:
            print("numbers of match points is less than 4")

        # 求透视矩阵(用RANSAC方法)
        # 关于重投影阈值的取值,1:可以根据应用场景来取
        """
           应用场景	                    推荐阈值范围	说明
           高精度匹配(如标定板、AR 标记)	1.0~3.0	    适用于特征点位置非常准确的场景
           图像拼接(SIFT/SURF/ORB 匹配)	3.0~5.0	    平衡噪声和精度
           动态场景或低质量图像	            5.0~10.0	容忍更大的匹配误差
        """

        src_pts = np.float32([n_kps_b[match.trainIdx] for match in good_matches])
        dst_pts = np.float32([n_kps_a[match.queryIdx] for match in good_matches])
        return src_pts, dst_pts


    def image_perspective_transformation(self, src_image_a, src_image_b, src_pts, dst_pts):
        # 图像透视变换
        reproj_threshold = 4.0
        H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, reproj_threshold)

        distance = 700

        # 对图像B进行透视变换,变换成与A图像相同角度的平面图像,以便于与图像A进行拼接
        pert_src_image_b = cv2.warpPerspective(src_image_b, H, (src_image_b.shape[1] + distance, src_image_b.shape[0]))

        # 图像变形和拼接
        h1, w1 = src_image_a.shape[:2]
        h2, w2 = src_image_b.shape[:2]

        # 计算拼接后图像的尺寸
        corners1 = np.float32([[0, 0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)
        corners2 = np.float32([[0, 0], [0, h2], [w2, h2], [w2, 0]]).reshape(-1, 1, 2)
        warped_corners2 = cv2.perspectiveTransform(corners2, H)

        all_corners = np.concatenate((corners1, warped_corners2), axis=0)
        [xmin, ymin] = np.int32(all_corners.min(axis=0).ravel() - 0.5)
        [xmax, ymax] = np.int32(all_corners.max(axis=0).ravel() + 0.5)

        # 计算平移变换
        translation_dist = [-xmin, -ymin]
        H_translation = np.array([[1, 0, translation_dist[0]],
                                  [0, 1, translation_dist[1]],
                                  [0, 0, 1]])

        # 应用变换,图像拼接
        result = cv2.warpPerspective(src_image_b, H_translation.dot(H), (xmax - xmin, ymax - ymin))
        result[translation_dist[1]:h1 + translation_dist[1],
        translation_dist[0]:w1 + translation_dist[0]] = src_image_a

        return result


    def image_mosaicking(self):
        # 图像拼接
        src_image_a = cv2.imread('resources/sift_1.jpg')
        src_image_b = cv2.imread('resources/sift_2.jpg')

        src_pts, dst_pts = self.image_sift(src_image_a, src_image_b)


        dst_image_b = self.image_perspective_transformation(src_image_a, src_image_b, src_pts, dst_pts)

        plt.subplot(1, 3, 1), plt.imshow(src_image_a), plt.title('src_image_a')
        plt.subplot(1, 3, 2), plt.imshow(src_image_b), plt.title('src_image_b')
        plt.subplot(1, 3, 3), plt.imshow(dst_image_b), plt.title('Origin_perspective')
        plt.show()





if __name__ == '__main__':
    sift = SIFT()
    sift.image_mosaicking()

下面是拼接后的效果图:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2404981.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AlphaDrive:通过强化学习和推理释放自动驾驶中 VLM 的力量

AlphaDrive: Unleashing the Power of VLMs in Autonomous Driving via Reinforcement Learning and Reasoning 25年3月来自华中科技大学和地平线的论文 OpenAI 的 o1 和 DeepSeek R1 在数学和科学等复杂领域达到甚至超越了人类专家水平&#xff0c;其中强化学习&#xff08;R…

【八股消消乐】如何解决SQL线上死锁事故

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本专栏《八股消消乐》旨在记录个人所背的八股文&#xff0c;包括Java/Go开发、Vue开发、系统架构、大模型开发、具身智能、机器学习、深度学习、力扣算法等相关知识点&#xff…

如何使用 HTML、CSS 和 JavaScript 随机更改图片颜色

原文&#xff1a;如何使用 HTML、CSS 和 JavaScript 随机更改图片颜色 | w3cschool笔记 &#xff08;请勿标记为付费&#xff01;&#xff01;&#xff01;&#xff01;&#xff09; 在网页开发中&#xff0c;为图片添加动态效果可以显著提升用户体验。今天&#xff0c;我将向…

CAN通信收发测试(USB2CAN模块测试实验)

1.搭建测试环境 电脑&#xff1a;安装 USB 驱动&#xff0c;安装原厂调试工具&#xff0c;安装cangaroo&#xff08;参考安装包的入门教程即可&#xff09; USB驱动路径&#xff1a;~\CAN分析仪资料20230701_Linux\硬件驱动程序 原厂调试工具路径&#xff1a;~\CAN分析仪资料2…

OSCP备战-BSides-Vancouver-2018-Workshop靶机详细步骤

一、靶机介绍 靶机地址&#xff1a;https://www.vulnhub.com/entry/bsides-vancouver-2018-workshop%2C231/ 靶机难度&#xff1a;中级&#xff08;CTF&#xff09; 靶机发布日期&#xff1a;2018年3月21日 靶机描述&#xff1a; Boot2root挑战旨在创建一个安全的环境&…

PDF转Markdown/JSON软件MinerU最新1.3.12版整合包下载

MinerU发布至今我已经更新多版整合包了&#xff0c;5天前MinerU发布了第一个正式版1.0.1&#xff0c;并且看到在18小时之前有更新模型文件&#xff0c;我就做了个最新版的一键启动整合包。 2025年02月21日更新v1.1.0版整合包 2025年02月27日更新v1.2.0版整合包 2025-06-05 更…

【深入学习Linux】System V共享内存

目录 前言 一、共享内存是什么&#xff1f; 共享内存实现原理 共享内存细节理解 二、接口认识 1.shmget函数——申请共享内存 2.ftok函数——生成key值 再次理解ftok和shmget 1&#xff09;key与shmid的区别与联系 2&#xff09;再理解key 3&#xff09;通过指令查看/释放系统中…

编程基础:执行流

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 执行流同步&#xff1a;顺序执行&#xff0c;只有一个执行流异步&#xff1a;新开后台(次)执行流&#xff0c;后台执行流要确保不能影响主执行流。共有两个执行流。 阻塞&#xff1a;任务阻塞执行流&#xff0c;导致…

理解非结构化文档:将 Reducto 解析与 Elasticsearch 结合使用

作者&#xff1a;来自 Elastic Adel Wu 演示如何将 Reducto 的文档处理与 Elasticsearch 集成以实现语义搜索。 Elasticsearch 与业界领先的生成式 AI 工具和提供商有原生集成。欢迎观看我们的网络研讨会&#xff0c;了解如何超越 RAG 基础&#xff0c;或使用 Elastic 向量数据…

[面试精选] 0094. 二叉树的中序遍历

文章目录 1. 题目链接2. 题目描述3. 题目示例4. 解题思路5. 题解代码6. 复杂度分析 1. 题目链接 94. 二叉树的中序遍历 - 力扣&#xff08;LeetCode&#xff09; 2. 题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 3. 题目示例 示例 1 : 输入&…

学习笔记(23): 机器学习之数据预处理Pandas和转换成张量格式[1]

学习笔记(23): 机器学习之数据预处理Pandas和转换成张量格式[1] 学习机器学习&#xff0c;需要学习如何预处理原始数据&#xff0c;这里用到pandas&#xff0c;将原始数据转换为张量格式的数据。 1、安装pandas pip install pandas 2、写入和读取数据 >>创建一个人工…

2025年6月6日第一轮

2025年6月6日 The rapid in Chiese industdy is developnig e,and it is From be in a enjoy a deep is developing The drone industry in China is developing The drone industy in china develops rapidly and is in a leading position in in the world. The dro…

12-Oracle 23ai Vector 使用ONNX模型生成向量嵌入

一、Oracle 23ai Vector Embeddings 核心概念​ 向量嵌入&#xff08;Vector Embeddings&#xff09;​​ -- 将非结构化数据&#xff08;文本/图像&#xff09;转换为数值向量 - - 捕获数据的语义含义而非原始内容 - 示例&#xff1a;"数据库" → [0.24, -0.78, 0.5…

pytorch 与 张量的处理

系列文章目录 文章目录 系列文章目录一、Tensor 的裁剪二、Tensor 的索引与数据筛选torch.wheretorch.indicestorch.gathertorch.masked_selecttorch.taketorch.nonzero&#xff08;省略&#xff09; 三、Tensor 的组合与拼接torch.cattorch.stack 四、Tensor的切片chunksplit …

layer norm和 rms norm 对比

Layer norm # Layer Norm 公式 mean x.mean(dim-1, keepdimTrue) var x.var(dim-1, keepdimTrue) output (x - mean) / sqrt(var eps) * gamma beta特点&#xff1a; 减去均值&#xff08;去中心化&#xff09;除以标准差&#xff08;标准化&#xff09;包含可学习参数 …

Java高级 | 【实验六】Springboot文件上传和下载

隶属文章&#xff1a;Java高级 | &#xff08;二十二&#xff09;Java常用类库-CSDN博客 系列文章&#xff1a;Java高级 | 【实验一】Springboot安装及测试 |最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot 静…

VBA进度条ProgressForm1

上一章《VBA如何使用ProgressBar进度条控件》介绍了ProgressBar控件的使用方法&#xff0c;今天我给大家介绍ProgressForm1进度条的使用方法&#xff0c;ProgressForm1是集成ProgressBar控件和Label控件的窗体&#xff0c;可以同时显示进度条和百分比&#xff0c;如下图&#x…

行为型设计模式之Interpreter(解释器)

行为型设计模式之Interpreter&#xff08;解释器&#xff09; 前言&#xff1a; 自己的话理解&#xff1a;自定义一个解释器用来校验参数或数据是否合法。 1&#xff09;意图 给定一个语言&#xff0c;定义它的文法的一种表示&#xff0c;并定义一个解释器&#xff0c;这个解…

深入解析 CAS 操作

一、CAS 的本质&#xff1a;硬件级别的乐观锁 CAS&#xff08;Compare-And-Swap&#xff0c;比较并交换&#xff09; 是一种原子操作指令&#xff0c;用于实现对共享变量的无锁并发修改。它是现代多核处理器支持的底层硬件指令&#xff0c;也是构建高效并发数据结构&#xff0…

vue3+TS+eslint9配置

记录eslint升级到9.x的版本之后遇到的坑 在 ESLint 9 中&#xff0c;配置方式发生了变化。Flat Config 格式&#xff08;eslint.config.js 或 .ts&#xff09;不再支持 extensions 选项。所以vscode编辑器中的 extensions 需要注释掉&#xff0c;要不然保存的时候不会格式化。…