特征点的检测与匹配(ORB,SIFT,SURFT比较)[opencv-python]

本文详细比较了OpenCV中ORB、SIFT和SURF特征点检测方法,并探讨了暴力匹配、FLANN和RANSAC优化在匹配过程中的应用,展示了它们在精度和效率上的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

特征点的检测与匹配(ORB,SIFT,SURFT比较)

本文旨在总结opencv-python上特征点的检测和匹配。
1、特征点的检测(包括:ORB,SIFT,SURFT)
2、特侦点匹配方法 (包括:暴力法,FLANN,以及随机抽样一致性优化RANSAC算法)
注:由于SURF专利问题,所以opencv官方包目前不支持SURF但支持ORB和SIFT,安装opencv-contrib-python包就可以解决

pip uninstall opencv-python
pip install opencv-contrib-python==3.4.2.17	

一、特征点检测
1、ORB算法
大致步骤:
ORB 的特点是速度超快,而且在一定程度上不受噪点和图像变换的影响,例如旋转和缩放变换等。
原理(步骤:)
(1)对图像进行多个版本的下采样,构建图像金字塔。对每个采样图像进行特征点检测。
(2)采用FAST特征点检测算法来检测特征点。
(3)采用BRIEF描述子来描述每个特征点。注:描述子可以理解为一个用来描述特征点的向量,每个特侦点都有对应的描述子,后面的特征点匹配就是对每个特征点之间描述子是否相似的判断。
关于FAST特征点检测算法:
FAST算法定义:特征点是如果某个像素点和他周围领域足够多的像素点处于不同区域,那么这个像素点就是特征点。
对于灰度图,及特征点处灰度值与周围足够多像素灰度值不同。即设定一个阈值若16个点中有n(取12)个点的灰度与中心点的灰度差超过阈值则判断为特征点。
关于BRIEF描述子
后面再补充—
实现代码如下:

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


def ORB(img):
    """
     ORB角点检测
     实例化ORB对象
    """
    orb = cv2.ORB_create(nfeatures=500)
    """检测关键点,计算特征描述符"""
    kp, des = orb.detectAndCompute(img, None)

    # 将关键点绘制在图像上
    img2 = cv2.drawKeypoints(img, kp, None, color=(0, 255, 0), flags=0)

    cv2.imwrite("1.jpg", img2)

    # 画图
    plt.figure(figsize=(10, 8), dpi=100)
    plt.imshow(img2[:, :, ::-1])
    plt.xticks([]), plt.yticks([])
    plt.show()
    return kp, des


img1 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = ORB(img1)

结果如图:
ORB特征点检测
可以看出ORB可以提取一些关键的特征点

2、SIFT算法
SIFT特征点:尺度不变特征变换(Scale-invariant feature transform):大致方法是首先搜索所有尺度下图像的位置,通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点,然后在候选位置通过拟合精细的模型来确定位置和尺度,再基于图像梯度,分配给关键点一个或多个方向,最后在每个关键点的周围邻域,在选定的尺度下测量图像局部梯度来描述关键点。(总的来说就是根据梯度描述关键点)
图像高斯金字塔就考虑了这两个方面:① 图像的远近程度;② 图像的模糊程度(理解为粗细更好)。前者通过上下采样来实现,后者通过高斯平滑处理来实现。
所谓的高斯金字塔是由原始图像的n层下采样图像组成,然后在每个下采样的层中利用不同核大小的高斯滤波来生成t张图像,t张图像除了第一张和最后一张其余图像在本张、上张、下张一共26个点中看是否为极值。
具体原理可参考
代码如下:

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


def SIFT(img):
    # SIFT算法关键点检测
    # 读取图像
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # SIFT关键点检测
    # 1. 实例化sift
    sift = cv2.xfeatures2d.SIFT_create()

    # 2. 利用sift.detectAndCompute()检测关键点并计算
    kp, des = sift.detectAndCompute(gray, None)
    # gray: 进行关键带你检测的图像,注意是灰度图像
    # kp: 关键点信息,包括位置,尺度,方向信息
    # des: 关键点描述符,每个关键点对应128个梯度信息的特征向量

    # 3. 将关键点检测结果绘制在图像上
    # cv2.drawKeypoints(image, keypoints, outputimage, color, flags)
    # image: 原始图像
    # keypoints: 关键点信息,将其绘制在图像上
    # outputimage: 输出图片,可以是原始图像
    # color: 颜色设置,通过修改(b, g, r)的值,更改画笔的颜色,b = 蓝色, g = 绿色, r = 红色
    # flags: 绘图功能的标识设置
    # 1. cv2.DRAW_MATCHES_FLAGS_DEFAULT: 创建输出图像矩阵,使用现存的输出图像绘制匹配对象和特征点,对每一个关键点只绘制中间点
    # 2. cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG: 不创建输出图像矩阵,而是在输出图像上绘制匹配对
    # 3. cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS: 对每一个特征点绘制带大小和方向的关键点图形
    # 4. cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS: 单点的特征点不被绘制
    cv2.drawKeypoints(img, kp, img, (0, 255, 0))
    cv2.imwrite("1.jpg", img)
    # 图像显示
    plt.figure(figsize=(10, 8), dpi=100)
    plt.imshow(img[:, :, ::-1])
    plt.xticks([]), plt.yticks([])
    plt.show()
    return kp, des


img1 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SIFT(img1)

结果如图:
SIFT算法结果
看以看出SIFT算法所求的特征点远远大于ORB

3、SURF算法
SURF特征点:加速稳健特征(Speeded Up Robust Features):加速版的SIFT。通过修改滤波器的尺寸和模糊度从而得到不同层级的影像,别的和SIFT差不多。
Surf是可以说是对Sift算法的改进,该算子在保持 SIFT 算子优良性能特点的基础上,同时解决了 SIFT 计算复杂度高、耗时长的缺点,提升了算法的执行效率,为算法在实时计算机视觉系统中应用提供了可能,SIFT在一般的计算机对于日常使用的图片想做到实时基本上是不可能的。
代码如下:

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


def SURF(img):
    surf = cv2.xfeatures2d.SURF_create()
    kp, des = surf.detectAndCompute(img, None)
    cv2.drawKeypoints(img, kp, img, (0, 255, 0))

    cv2.imwrite("1.jpg", img)
    # 图像显示
    plt.figure(figsize=(10, 8), dpi=100)
    plt.imshow(img[:, :, ::-1])
    plt.xticks([]), plt.yticks([])
    plt.show()
    return kp, des


img1 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SURF(img1)

结果如图
SURF算法
可以看到和SIFT差不多密密麻麻的一大片,密集恐惧症看的都哭了,
可达鸭,你…绿了。 可达鸭:我%#@#¥%#¥#¥¥##¥¥%!你不准丑化我,我可是顶流。
特征点检测还有些其他的算法,就先介绍这么多,以后有时间在总结。

二、特征点匹配算法
1、暴力求解
让前一个图像的每一个特征点直接遍历后一个图像的所有特征点,找到最小值即为匹配,真的是简单粗暴。
代码:暴力求解+SURF

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


def SURF(img):
    surf = cv2.xfeatures2d.SURF_create()
    kp, des = surf.detectAndCompute(img, None)
    cv2.drawKeypoints(img, kp, img, (0, 255, 0))

    # 图像显示
    plt.figure(figsize=(10, 8), dpi=100)
    plt.imshow(img[:, :, ::-1])
    plt.xticks([]), plt.yticks([])
    plt.show()
    return kp, des


def ByBFMatcher(img1, img2, kp1, kp2, des1, des2, flag="ORB"):
    """
    (1)暴力法
    :param img1: 匹配图像1
    :param img2: 匹配图像2
    :param kp1: 匹配图像1的特征点
    :param kp2: 匹配图像2的特征点
    :param des1: 匹配图像1的描述子
    :param des2: 匹配图像2的描述子
    :return:
    """
    if (flag == "SIFT" or flag == "sift"):
        # SIFT方法或SURF
        bf = cv2.BFMatcher_create(cv2.NORM_L1, crossCheck=False)
    else:
        # ORB方法
        bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=False)
    ms = bf.match(des1, des2)
    # ms = sorted(ms, key=lambda x: x.distance)
    img3 = cv2.drawMatches(img1, kp1, img2, kp2, ms, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
    cv2.imwrite("1.jpg", img3)
    cv2.imshow("Matches", img3)
    cv2.waitKey(0)
    return ms


img1 = cv2.imread("../asset/image/4.jpg")
img2 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SURF(img1)
kp2, des2 = SURF(img2)
matches = ByBFMatcher(img1, img2, kp1, kp2, des1, des2, "SIFT")

结果如下
匹配结果
可以看得出来,什么也看不出来,一团乱麻,而且一看就有很多误匹配的,不急后文讲优化的时候会解决这一问题。‘
可达鸭:喂喂喂,我人都没了,你这可恶的人类。
如果想使用ORB就可以把上面的ORB函数粘贴进来,然后修改这几行,SIFT同理。

kp1, des1 = ORB(img1)
kp2, des2 = ORB(img2)
matches = ByBFMatcher(img1, img2, kp1, kp2, des1, des2, "ORB")

1、FLANN求解
使用快速近似最近邻搜索算法寻找。
代码:FLANN + SURF

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


def SURF(img):
    surf = cv2.xfeatures2d.SURF_create()
    kp, des = surf.detectAndCompute(img, None)
    cv2.drawKeypoints(img, kp, img, (0, 255, 0))

    # 图像显示
    plt.figure(figsize=(10, 8), dpi=100)
    plt.imshow(img[:, :, ::-1])
    plt.xticks([]), plt.yticks([])
    plt.show()
    return kp, des


def ByFlann(img1, img2, kp1, kp2, des1, des2, flag="ORB"):
    """
        (1)FLANN匹配器
        :param img1: 匹配图像1
        :param img2: 匹配图像2
        :param kp1: 匹配图像1的特征点
        :param kp2: 匹配图像2的特征点
        :param des1: 匹配图像1的描述子
        :param des2: 匹配图像2的描述子
        :return:
        """
    if (flag == "SIFT" or flag == "sift"):
        # SIFT方法
        FLANN_INDEX_KDTREE = 1
        index_params = dict(algorithm=FLANN_INDEX_KDTREE,
                            trees=5)
        search_params = dict(check=50)
    else:
        # ORB方法
        FLANN_INDEX_LSH = 6
        index_params = dict(algorithm=FLANN_INDEX_LSH,
                            table_number=6,
                            key_size=12,
                            multi_probe_level=1)
        search_params = dict(check=50)
    # 定义FLANN参数
    flann = cv2.FlannBasedMatcher(index_params, search_params)
    matches = flann.match(des1, des2)
    img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
    cv2.imshow("Matches", img3)
    cv2.imwrite("1.jpg", img3)
    cv2.waitKey(0)
    return matches


img1 = cv2.imread("../asset/image/4.jpg")
img2 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SURF(img1)
kp2, des2 = SURF(img2)
matches = ByFlann(img1, img2, kp1, kp2, des1, des2, "SIFT")

结果如下
FLANN
依旧看不见鸭鸭。

三、优化
由上图匹配结果可以看出存在较多误匹配现象所以必须采取手段来实现减少误匹配。
1、RANSAC算法
RANSAC算法,是随机抽样一致。
代码如下:FLANN + SURF + RANSAC

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


def SURF(img):
    surf = cv2.xfeatures2d.SURF_create()
    kp, des = surf.detectAndCompute(img, None)
    cv2.drawKeypoints(img, kp, img, (0, 255, 0))

    # 图像显示
    plt.figure(figsize=(10, 8), dpi=100)
    plt.imshow(img[:, :, ::-1])
    plt.xticks([]), plt.yticks([])
    plt.show()
    return kp, des


def ByFlann(img1, img2, kp1, kp2, des1, des2, flag="ORB"):
    """
        (1)FLANN匹配器
        :param img1: 匹配图像1
        :param img2: 匹配图像2
        :param kp1: 匹配图像1的特征点
        :param kp2: 匹配图像2的特征点
        :param des1: 匹配图像1的描述子
        :param des2: 匹配图像2的描述子
        :return:
        """
    if (flag == "SIFT" or flag == "sift"):
        # SIFT方法
        FLANN_INDEX_KDTREE = 1
        index_params = dict(algorithm=FLANN_INDEX_KDTREE,
                            trees=5)
        search_params = dict(check=50)
    else:
        # ORB方法
        FLANN_INDEX_LSH = 6
        index_params = dict(algorithm=FLANN_INDEX_LSH,
                            table_number=6,
                            key_size=12,
                            multi_probe_level=1)
        search_params = dict(check=50)
    # 定义FLANN参数
    flann = cv2.FlannBasedMatcher(index_params, search_params)
    matches = flann.match(des1, des2)
    # img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
    # cv2.imshow("Matches", img3)
    # cv2.imwrite("1.jpg", img3)
    # cv2.waitKey(0)
    return matches

def RANSAC(img1, img2, kp1, kp2, matches):
    MIN_MATCH_COUNT = 10
    # store all the good matches as per Lowe's ratio test.
    matchType = type(matches[0])
    good = []
    print(matchType)
    if isinstance(matches[0], cv2.DMatch):
        # 搜索使用的是match
        good = matches
    else:
        # 搜索使用的是knnMatch
        for m, n in matches:
            if m.distance < 0.7 * n.distance:
                good.append(m)

    if len(good) > MIN_MATCH_COUNT:
        src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
        dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)

        # M: 3x3 变换矩阵.
        M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
        matchesMask = mask.ravel().tolist()

        # h, w = img1.shape
        # pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
        # dst = cv2.perspectiveTransform(pts, M)
        #
        # img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
    else:
        print
        "Not enough matches are found - %d/%d" % (len(good), MIN_MATCH_COUNT)
        matchesMask = None

    draw_params = dict(matchColor=(0, 255, 0),  # draw matches in green color
                       singlePointColor=None,
                       matchesMask=matchesMask,  # draw only inliers
                       flags=2)

    img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params)

    draw_params1 = dict(matchColor=(0, 255, 0),  # draw matches in green color
                        singlePointColor=None,
                        matchesMask=None,  # draw only inliers
                        flags=2)

    img33 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params1)
    cv2.imwrite("1.jpg", img33)
    cv2.imwrite("2.jpg", img3)
    cv2.imshow("before", img33)
    cv2.imshow("now", img3)
    cv2.waitKey(0)

img1 = cv2.imread("../asset/image/4.jpg")
img2 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SURF(img1)
kp2, des2 = SURF(img2)
matches = ByFlann(img1, img2, kp1, kp2, des1, des2, "SIFT")
RANSAC(img1, img2, kp1, kp2, matches)

结果:
不使用RANSAC
不使用RANSAC
使用RANSAC
使用RANSAC
可以看到匹配的特征点减少了很多,但是还是存在误匹配的现象,效果不够理想。

2、使用knnMatch
就是将flann.knnMatch改为flann.match
使用knnMatch得到的matches会是一个tuple,里面储存两个match。
match结构为:
queryIdx:当前帧特征点序号
trainIdx:下一帧最为匹配的特征点序号
distance:距离
一个match存最接近,一个match存次最接近。
代码如下:

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


def SURF(img):
    surf = cv2.xfeatures2d.SURF_create()
    kp, des = surf.detectAndCompute(img, None)
    cv2.drawKeypoints(img, kp, img, (0, 255, 0))

    # 图像显示
    plt.figure(figsize=(10, 8), dpi=100)
    plt.imshow(img[:, :, ::-1])
    plt.xticks([]), plt.yticks([])
    plt.show()
    return kp, des


def ByFlann(img1, img2, kp1, kp2, des1, des2, flag="ORB"):
    """
        (1)FLANN匹配器
        :param img1: 匹配图像1
        :param img2: 匹配图像2
        :param kp1: 匹配图像1的特征点
        :param kp2: 匹配图像2的特征点
        :param des1: 匹配图像1的描述子
        :param des2: 匹配图像2的描述子
        :return:
        """
    if (flag == "SIFT" or flag == "sift"):
        # SIFT方法
        FLANN_INDEX_KDTREE = 1
        index_params = dict(algorithm=FLANN_INDEX_KDTREE,
                            trees=5)
        search_params = dict(check=50)
    else:
        # ORB方法
        FLANN_INDEX_LSH = 6
        index_params = dict(algorithm=FLANN_INDEX_LSH,
                            table_number=6,
                            key_size=12,
                            multi_probe_level=1)
        search_params = dict(check=50)
    # 定义FLANN参数
    flann = cv2.FlannBasedMatcher(index_params, search_params)
    matches = flann.knnMatch(des1, des2, k=2)
    # img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
    # cv2.imshow("Matches", img3)
    # cv2.imwrite("1.jpg", img3)
    # cv2.waitKey(0)
    return matches

def RANSAC(img1, img2, kp1, kp2, matches):
    MIN_MATCH_COUNT = 10
    # store all the good matches as per Lowe's ratio test.
    matchType = type(matches[0])
    good = []
    print(matchType)
    if isinstance(matches[0], cv2.DMatch):
        # 搜索使用的是match
        good = matches
    else:
        # 搜索使用的是knnMatch
        for m, n in matches:
            if m.distance < 0.7 * n.distance:
                good.append(m)

    if len(good) > MIN_MATCH_COUNT:
        src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
        dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)

        # M: 3x3 变换矩阵.
        M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
        matchesMask = mask.ravel().tolist()

        # h, w = img1.shape
        # pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
        # dst = cv2.perspectiveTransform(pts, M)
        #
        # img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
    else:
        print
        "Not enough matches are found - %d/%d" % (len(good), MIN_MATCH_COUNT)
        matchesMask = None

    draw_params = dict(matchColor=(0, 255, 0),  # draw matches in green color
                       singlePointColor=None,
                       matchesMask=matchesMask,  # draw only inliers
                       flags=2)

    img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params)

    draw_params1 = dict(matchColor=(0, 255, 0),  # draw matches in green color
                        singlePointColor=None,
                        matchesMask=None,  # draw only inliers
                        flags=2)

    img33 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params1)
    cv2.imwrite("1.jpg", img33)
    cv2.imwrite("2.jpg", img3)
    cv2.imshow("before", img33)
    cv2.imshow("now", img3)
    cv2.waitKey(0)

img1 = cv2.imread("../asset/image/4.jpg")
img2 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SURF(img1)
kp2, des2 = SURF(img2)
matches = ByFlann(img1, img2, kp1, kp2, des1, des2, "SIFT")
RANSAC(img1, img2, kp1, kp2, matches)

结果:
使用FLANN + SURF(match) + RANSAC
在这里插入图片描述
使用FLANN + SURF(knnMatch) + 不使用RANSAC
在这里插入图片描述
使用FLANN + SURF(knnMatch) + RANSAC
在这里插入图片描述
由上面可以看出,就算不使用RANSAC,单单就FLANN + SURF(knnMatch)的效果就已经比FLANN + SURF(match) + RANSAC好很多,当然效果最好的还是FLANN + SURF(knnMatch) + RANSAC。
可达鸭:我就一直绿呗。

四、总结
1、总的来说就时效性应该是ORB>SURF>SIFT, FLANN>暴力求解。具体对比可以参考这篇文章
2、从效果来看FLANN + SURF/SURF(knnMatch) + RANSAC是最好的。ORB好像不支持knnMatch,我后面再测测看。
3、整个程序源码放在最后,大家可以依据自己的需要组合成不的方式。

程序代码:
要想使用knnMatch,把SURT,SIFT函数里面的

matches = flann.match(des1, des2)

改为

matches = flann.knnMatch(des1, des2, k=2)
"""
图像特征点的检测与匹配
主要涉及:
1、ORB
2、SIFT
3、SURF
"""

"""
一、图像特征点的检测
"""
import numpy as np
import cv2
from matplotlib import pyplot as plt


def ORB(img):
    """
     ORB角点检测
     实例化ORB对象
    """
    orb = cv2.ORB_create(nfeatures=500)
    """检测关键点,计算特征描述符"""
    kp, des = orb.detectAndCompute(img, None)

    # 将关键点绘制在图像上
    img2 = cv2.drawKeypoints(img, kp, None, color=(0, 255, 0), flags=0)

    # 画图
    plt.figure(figsize=(10, 8), dpi=100)
    plt.imshow(img2[:, :, ::-1])
    plt.xticks([]), plt.yticks([])
    plt.show()
    return kp, des


def SIFT(img):
    # SIFT算法关键点检测
    # 读取图像
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # SIFT关键点检测
    # 1. 实例化sift
    sift = cv2.xfeatures2d.SIFT_create()

    # 2. 利用sift.detectAndCompute()检测关键点并计算
    kp, des = sift.detectAndCompute(gray, None)
    # gray: 进行关键带你检测的图像,注意是灰度图像
    # kp: 关键点信息,包括位置,尺度,方向信息
    # des: 关键点描述符,每个关键点对应128个梯度信息的特征向量

    # 3. 将关键点检测结果绘制在图像上
    # cv2.drawKeypoints(image, keypoints, outputimage, color, flags)
    # image: 原始图像
    # keypoints: 关键点信息,将其绘制在图像上
    # outputimage: 输出图片,可以是原始图像
    # color: 颜色设置,通过修改(b, g, r)的值,更改画笔的颜色,b = 蓝色, g = 绿色, r = 红色
    # flags: 绘图功能的标识设置
    # 1. cv2.DRAW_MATCHES_FLAGS_DEFAULT: 创建输出图像矩阵,使用现存的输出图像绘制匹配对象和特征点,对每一个关键点只绘制中间点
    # 2. cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG: 不创建输出图像矩阵,而是在输出图像上绘制匹配对
    # 3. cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS: 对每一个特征点绘制带大小和方向的关键点图形
    # 4. cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS: 单点的特征点不被绘制
    cv2.drawKeypoints(img, kp, img, (0, 255, 0))

    # 图像显示
    plt.figure(figsize=(10, 8), dpi=100)
    plt.imshow(img[:, :, ::-1])
    plt.xticks([]), plt.yticks([])
    plt.show()
    return kp, des


def SURF(img):
    surf = cv2.xfeatures2d.SURF_create()
    kp, des = surf.detectAndCompute(img, None)
    cv2.drawKeypoints(img, kp, img, (0, 255, 0))

    # 图像显示
    plt.figure(figsize=(10, 8), dpi=100)
    plt.imshow(img[:, :, ::-1])
    plt.xticks([]), plt.yticks([])
    plt.show()
    return kp, des


"""
2.图像特征点匹配方法
(1)暴力法
(2)FLANN匹配器
"""


def ByBFMatcher(img1, img2, kp1, kp2, des1, des2, flag="ORB"):
    """
    (1)暴力法
    :param img1: 匹配图像1
    :param img2: 匹配图像2
    :param kp1: 匹配图像1的特征点
    :param kp2: 匹配图像2的特征点
    :param des1: 匹配图像1的描述子
    :param des2: 匹配图像2的描述子
    :return:
    """
    if (flag == "SIFT" or flag == "sift"):
        # SIFT方法
        bf = cv2.BFMatcher_create(cv2.NORM_L1, crossCheck=False)
    else:
        # ORB方法
        bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=False)
    ms = bf.knnMatch(des1, des2, k=2)
    # ms = sorted(ms, key=lambda x: x.distance)
    # img3 = cv2.drawMatches(img1, kp1, img2, kp2, ms, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
    # cv2.imshow("Matches", img3)
    # cv2.waitKey(0)
    return ms


def ByFlann(img1, img2, kp1, kp2, des1, des2, flag="ORB"):
    """
        (1)FLANN匹配器
        :param img1: 匹配图像1
        :param img2: 匹配图像2
        :param kp1: 匹配图像1的特征点
        :param kp2: 匹配图像2的特征点
        :param des1: 匹配图像1的描述子
        :param des2: 匹配图像2的描述子
        :return:
        """
    if (flag == "SIFT" or flag == "sift"):
        # SIFT方法
        FLANN_INDEX_KDTREE = 1
        index_params = dict(algorithm=FLANN_INDEX_KDTREE,
                            trees=5)
        search_params = dict(check=50)
    else:
        # ORB方法
        FLANN_INDEX_LSH = 6
        index_params = dict(algorithm=FLANN_INDEX_LSH,
                            table_number=6,
                            key_size=12,
                            multi_probe_level=1)
        search_params = dict(check=50)
    # 定义FLANN参数
    flann = cv2.FlannBasedMatcher(index_params, search_params)
    matches = flann.knnMatch(des1, des2, k=2)
    return matches


"""
优化匹配结果
RANSAC算法是RANdom SAmple Consensus的缩写,意为随机抽样一致
"""


def RANSAC(img1, img2, kp1, kp2, matches):
    MIN_MATCH_COUNT = 10
    # store all the good matches as per Lowe's ratio test.
    matchType = type(matches[0])
    good = []
    print(matchType)
    if isinstance(matches[0], cv2.DMatch):
        # 搜索使用的是match
        good = matches
    else:
        # 搜索使用的是knnMatch
        for m, n in matches:
            if m.distance < 0.7 * n.distance:
                good.append(m)

    if len(good) > MIN_MATCH_COUNT:
        src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
        dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)

        # M: 3x3 变换矩阵.
        M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
        matchesMask = mask.ravel().tolist()

        # h, w = img1.shape
        # pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
        # dst = cv2.perspectiveTransform(pts, M)
        #
        # img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
    else:
        print
        "Not enough matches are found - %d/%d" % (len(good), MIN_MATCH_COUNT)
        matchesMask = None

    draw_params = dict(matchColor=(0, 255, 0),  # draw matches in green color
                       singlePointColor=None,
                       matchesMask=matchesMask,  # draw only inliers
                       flags=2)

    img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params)

    draw_params1 = dict(matchColor=(0, 255, 0),  # draw matches in green color
                        singlePointColor=None,
                        matchesMask=None,  # draw only inliers
                        flags=2)

    img33 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params1)

    cv2.imshow("before", img33)
    cv2.imshow("now", img3)
    cv2.waitKey(0)


img1 = cv2.imread("../asset/image/4.jpg")
img2 = cv2.imread("../asset/image/5.jpg")
kp1, des1 = SURF(img1)
kp2, des2 = SURF(img2)
matches = ByFlann(img1, img2, kp1, kp2, des1, des2, "SIFT")
RANSAC(img1, img2, kp1, kp2, matches)

<think>好的,用户需要关于SIFT特征点匹配Python示例代码。首先,我应该回忆一下SIFT的基本流程,包括关键点检测、描述子计算和特征匹配。然后,用户提到了Python实现,所以需要确保使用OpenCV库中的相关函数。 根据之前的引用内容,用户提供的参考中有三个引用提到了SIFT检测和计算,比如使用sift.detectAndCompute函数。此外,引用[2]还提到了最近邻匹配和距离比匹配的方法,以及错误匹配的问题。所以示例代码应该包括这些步骤。 接下来,我需要确认OpenCV的版本是否支持SIFT。由于SIFT专利已经过期,现在OpenCV中可以直接使用cv2.SIFT_create(),但用户可能需要安装contrib版本,比如opencv-contrib-python。这点需要在代码中提醒用户。 然后,步骤分解:读取图像、初始化SIFT检测关键点和计算描述子、使用匹配器(比如BFMatcher或FLANN)进行匹配,最后应用比例测试来筛选好的匹配点。同时,需要绘制匹配结果并显示。 还需要注意代码的格式是否正确,比如使用正确的缩进和函数参数。例如,detectAndCompute的参数是图像和掩膜,这里可能为None。匹配部分,knnMatch返回k个最近邻,然后根据Lowe的比率测试来过滤。 此外,用户可能遇到的问题包括安装依赖项、图像路径错误、匹配效果不佳时的参数调整,比如调整ratio_threshold的值。这些应该在注意事项中提到。 最后,生成相关的问题,比如性能优化、错误匹配处理和应用场景,这些可以引导用户进一步学习。</think>以下是使用Python实现SIFT特征点匹配的示例代码及分步说明: ### 1. 代码实现 ```python 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) # [^1][^3] kp2, des2 = sift.detectAndCompute(img2, None) # 创建暴力匹配器 bf = cv2.BFMatcher() matches = bf.knnMatch(des1, des2, k=2) # 返回k个最佳匹配[^2] # 应用Lowe's比率测试(筛选优质匹配) good = [] ratio_threshold = 0.75 for m, n in matches: if m.distance < ratio_threshold * n.distance: good.append([m]) # 绘制匹配结果(前30个优质匹配) result = cv2.drawMatchesKnn( img1, kp1, img2, kp2, good[:30], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS ) cv2.imshow('SIFT Matches', result) cv2.waitKey(0) cv2.destroyAllWindows() ``` ### 2. 关键步骤说明 1. **特征检测**:使用$cv2.SIFT\_create()$创建检测器,通过$detectAndCompute$方法同时获取关键点坐标(kp)和特征描述子(des) 2. **特征匹配**:采用暴力匹配器$BFMatcher$进行最近邻搜索,返回每个特征的2个最近邻匹配[^2] 3. **筛选策略**:应用Lowe提出的距离比率测试,过滤掉距离差异不明显的不可靠匹配 4. **结果可视化**:使用$drawMatchesKnn$函数绘制匹配连线,限制显示数量以避免图像过于密集 ### 3. 注意事项 - 需要安装opencv-contrib-python>=4.5.0 - 图像路径需替换为实际文件路径 - 调整ratio_threshold值(0.6-0.8)可平衡匹配数量和准确性 - 对于大尺寸图像建议先进行降采样处理
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值