• 计算机视觉与模式识别实验2-2 SIFT特征提取与匹配


    🧡🧡实验流程🧡🧡

    SIFT算法原理总结:

    1.创建尺度空间:
    高斯模糊去除噪声,强调了图像的重要特征
    在这里插入图片描述
    根据原图创建不同比例的新图像
    在这里插入图片描述

    2.采用高斯差异DOG增强特征
    在这里插入图片描述
    如下,对于某一比例的5张不同模糊程度的图像,进行差分
    在这里插入图片描述

    3.关键点定位(尺度不变性)
    找出局部最大值和最小值(这里“局部”的含义:它不仅包括该图像的周围像素(像素所在的像素),还包括八度中上一张和下一张图像的九个像素)
    这意味着将每个像素值与其他26个像素值进行比较,以确定是否为局部最大值/最小值。例如,在下图中,从第一个八度获得了三个图像。将标记为x的像素与相邻像素(绿色)进行比较,如果它是相邻像素中最高或最低的像素,则将其选择为关键点:
    在这里插入图片描述
    关键点的筛选
    消除对比度低或非常靠近边缘的关键点:
    采用二阶泰勒展开消除对比度低或非常靠近边缘的关键点、采用二阶Hessian矩阵来识别具有高边缘度但对少量噪点无鲁棒性的关键点

    4.关键点方向分配(旋转不变性)
    对于每个关键点和其周围的像素,都执行如下操作:
    根据梯度计算幅度和方向,如下Gx=9,Gy=14则
    在这里插入图片描述
    在这里插入图片描述
    随后创建大小和方向的柱状图
    在这里插入图片描述

    5.生成描述符
    已经通过3、4生成了具有尺度不变性和旋转不变性的关键点,对于每个关键点,使用相邻像素,它们的方向和大小为该关键点生成一个唯一的指纹,称为“描述符”。

    6.关键点匹配
    使用描述子之间的距离或相似度度量来匹配不同图像中的关键点,通常采用最近邻或 k近邻方法来进行匹配。
    (在opencv中,BFMatcher.match() 和BFMatcher.knnMatch(),第一个返回最佳匹配,第二个返回前k个最佳的匹配,k值由用户指定。)




    实现SIFT特征检测和匹配

    原始图像如下:
    在这里插入图片描述

    截出两个图像(分别截取前宽度的4/5和后4/5部分)
    在这里插入图片描述

    画出关键点
    在这里插入图片描述

    SIFT匹配(总共529个匹配,按连线长度升序,画出全部线)
    在这里插入图片描述

    为方便观察,画出按连线长度前100匹配的连线
    在这里插入图片描述

    将右侧图片旋转90度,重复上述步骤
    在这里插入图片描述
    在这里插入图片描述

    将右侧图片旋转15度,并缩放到原图0.8倍,重复上述步骤
    在这里插入图片描述
    在这里插入图片描述




    通过RANSAC 实现图片拼接

    右侧图片正放
    在这里插入图片描述
    拼接结果:
    在这里插入图片描述


    右侧图片旋转90度
    在这里插入图片描述
    拼接结果:
    在这里插入图片描述


    右侧图片旋转15度并缩放到0.8倍
    在这里插入图片描述
    拼接结果:
    在这里插入图片描述




    更换其他图片再次测试效果(依次进行SIFT特征提取、RANSAC 拼接)

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    🧡🧡全部代码🧡🧡

    import numpy as np
    import cv2
    from matplotlib import pyplot as plt
    def cv_show(title,img):
        cv2.imshow(title, img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    
    """
        SIFT 图像特征连接+ RANSAC拼接
    """
    
    def check_and_draw_KeyPoint(img1,img2):
        img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
        img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
        # Sift
        sift = cv2.SIFT_create()
        kp1, des1 = sift.detectAndCompute(img1,None)
        kp2, des2 = sift.detectAndCompute(img2,None)
        # len(kp1), len(kp2)
    
        # Draw KeyPoint
        imgShow1 = cv2.drawKeypoints(img1,kp1,None,color=(255,0,255)) #画出特征点,并显示为红色圆圈
        imgShow2 = cv2.drawKeypoints(img2,kp2,None,color=(255,0,255)) #画出特征点,并显示为红色圆圈
        cv_show("KeyPoint", np.hstack((imgShow1, imgShow2)))
        return img1,img2,kp1,kp2,des1,des2
    
    def match_KeyPoint(img1,img2,kp1,kp2,des1,des2,show_line_num=100):
        # Feature Matching
        bf = cv2.BFMatcher(cv2.NORM_L1, crossCheck=True)
        matches = bf.match(des1,des2)
        matches =sorted(matches, key=lambda x:x.distance)
        # print(len(matches))
        imgShow = cv2.drawMatches(img1, kp1, img2, kp2, matches[0:show_line_num], None, flags=2)
        cv_show("Match",imgShow)
    
    def concat_Image(img1,img2,kp1,kp2,des1,des2):
        # 匹配特征,并返回透视变换矩阵
        matcher = cv2.BFMatcher()
        rawMatches = matcher.knnMatch(des2, des1, 2)
        matches = []
        for m in rawMatches:
            if len(m) == 2 and m[0].distance < m[1].distance * 0.75:
                matches.append((m[0].trainIdx, m[0].queryIdx))
        kp1 = np.float32([kp.pt for kp in kp1])
        kp2 = np.float32([kp.pt for kp in kp2])
        if len(matches) > 4:
            ptsA = np.float32([kp2[i] for (_, i) in matches])
            ptsB = np.float32([kp1[i] for (i, _) in matches])
            (H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, 4.0)
    
        result = cv2.warpPerspective(img2, H, (img2.shape[1] + img1.shape[1], img2.shape[0]))
        result[0:img1.shape[0], 0:img1.shape[1]] = img1
        cv_show("Concat Image",result)
    
    
    if __name__=="__main__":
        # Read Original Image
        image = cv2.imread('img/test2_Sift.jpg')
        height, width, _ = image.shape
        image = cv2.resize(image,(int(width*4/6),int(height*4/6))) # 图片有点宽,缩放一下
        height, width, _ = image.shape
    
        # 截取前4/5部分和后4/5部分
        img1 = image[:, 0 : int(width * 4 / 5)]
        img2 = image[:, int(width / 5) : width]
    
        img1 = cv2.imread("img/test2_river1.png")
        img2 = cv2.imread("img/test2_river2.png")
        # 可注释)图像转变2:img2旋转90度
    #     img2 = cv2.rotate(img2, cv2.ROTATE_90_CLOCKWISE) # 旋转90度
    #     img2 = cv2.resize(img2,(img2.shape[1], img1.shape[0]))
    
        # 可注释)图像转变3:img2旋转15度,并且缩放到0.9倍,同时img1设置跟img2同样高度,并且宽度按比例变换
    #     center=(width/2,height/2)
    #     angle=15
    #     scale=0.8
    #     M=cv2.getRotationMatrix2D(center,angle,scale)
    #     img2=cv2.warpAffine(img2,M,(int(width),int(1.1*height)))
    #     img1=cv2.resize(img1,(int(img1.shape[1]*img2.shape[0]/img1.shape[0]), img2.shape[0]))
        
        cv_show("spilt", np.hstack((img1, img2))) #拼接显示原图
    
        # 调用自定义函数
        img1,img2,kp1,kp2,des1,des2=check_and_draw_KeyPoint(img1,img2) # 检测并画出关键点
        match_KeyPoint(img1,img2,kp1,kp2,des1,des2, show_line_num=100) # 连接关键点
        concat_Image(img1,img2,kp1,kp2,des1,des2) # 拼接图像
    
  • 相关阅读:
    开源 RPA 和并行处理的力量
    Hexagon_V65_Programmers_Reference_Manual(38)
    电子行业MES管理系统的主要功能与用途
    YOLOv5s-ShuffleNetV2
    C# Modbus 通讯
    C++学习第二十九天----引用变量和c语言之register关键字
    VUE条件渲染
    Rust标准库-实现一个TCP服务、Rust使用套接字
    c++day2
    Python基本功
  • 原文地址:https://blog.csdn.net/luohaojia123/article/details/139426185