• 基于特征点匹配的图像相似度算法之SIFT特征(一)


    导读

    在之前的文章图像处理中常用的相似度评估指标中,我们介绍了通过MSEPSNRSSIM以及UQI等指标来计算图像之间的相似度。但是,在使用这些算法计算图像相似的时候两张图像的size必须一致,而且这些算法对于图像的旋转缩放平移仿射变换以及光照强度等都是不鲁棒的。

    这篇文章我们来介绍几个更加鲁棒的图像相似度计算的算法,SIFTSURF以及ORB三种算法,它们都是基于特征点的提取来计算图像之间的相似度。

    注意:因为需要用到SHIFTSURF以及ORB算法,所以需要安装 opencv-python==3.4.2.16opencv-contrib-python==3.4.2.16,如果是高本版opencv由于license的问题可能会无法使用到这些算法

    环境

    • Python:3.7
    • opencv:3.4.2.16
    • opencv-contrib:3.4.2.16

    SIFT

    SIFT (Scale-Invariant Feature Transform):尺度不变特征变换,是用于图像处理中的一种描述。这种描述具有尺度不变性,可在图像中检测出关键点,是一种局部特征的描述子。在图像的特征匹配和特征提取中,经常使用到。

    特点
    • 不变性:SIFT特征是提取图像的局部特征,它对于旋转尺度缩放亮度变换保持不变性,对于噪声视角变换仿射变换也能保持一定的不变性
    • 差异性:提取的特征信息量丰富,适合在海量数据中快速找到目标
    • 实时性:优化版的SIFT算法提取特征的速度可以达到实时
    • 扩展性:提取的特征向量可以和其他的特征向量进行融合
    提取特征的步骤
    1. 尺度空间的极值检测:通过高斯微分函数来寻找图像在尺度变换和旋转不变的关键点
    2. 关键点定位:在每个候选关键点上,通过拟合精细的模型来确定尺度和位置,最终的选择依据它们的稳定程度
    3. 方向的确定:基于图像局部的梯度方向,分配给每个关键点一个或多个方向
    4. 关键点描述:在关键点的领域内,选定的尺度上测量图像的局部梯度,这些梯度被变换成一种表示,这种表示允许比较大的局部变形和光照的变化

    代码实现

    下面我们用python来提取图像的SIFT特征

    • 导包
    import cv2
    import numpy as np
    from matplotlib import pyplot as plt
    
    • 1
    • 2
    • 3
    • 提取图像的SIFT关键点
    def extract_sift_feature(img):
        #创建SIFT检测器
        sift = cv2.xfeatures2d.SIFT_create()
        #提取图像的特征点和描述子信息
        keypoints, descriptors = sift.detectAndCompute(img, None)
        return keypoints,descriptors
    
    def show_img_sift_dot(img):
        gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        #提取图像的SIFT特征点信息
        keypoints, descriptors = extract_sift_feature(img)
        #在图像上绘制特征点的位置
        img1 = cv2.drawKeypoints(rgb_img, keypoints, img)
        plt.imshow(img1)
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述
    图像上面的绘制出来的圆圈就是使用SIFT算法提取的特征点

    • 图像的关键点匹配
    def draw_match_image(img1,img2):
        rgb_img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
        rgb_img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
    
        #提取图像的SIFT关键点信息
        keypoints1, descriptors1 = extract_sift_feature(img1)
        keypoints2, descriptors2 = extract_sift_feature(img2)
    
        #创建一个关键点匹配器,采用L1距离
        bf = cv2.BFMatcher(cv2.NORM_L1, crossCheck=True)
        #关键点匹配,通过计算两张图像提取的描述子之间的距离
        matches = bf.match(descriptors1, descriptors2)
        #根据描述子之间的距离进行排序
        matches = sorted(matches, key=lambda x: x.distance)
    
        #只匹配前50个关键点
        img3 = cv2.drawMatches(rgb_img1, keypoints1, rgb_img2, keypoints2, matches[:50], rgb_img2, flags=2)
        plt.imshow(img3)
        plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述
    上面是两张图片,左图是原图,右图是在原图的基础上增加了高斯噪声,直线连接了两张图像的相似的关键点

    • 通过匹配的关键点个数来计算相似度
      通过计算两张图之间SIFT关键点的匹配个数占总匹配数量的比例来计算相似度,这个算法计算出来的上面的两张图片的相似度只有28%,比较低
    def cal_SIFT_sim(img1,img2):
        #将图片转换为灰度图
        img1 = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
        img2 = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
        #提取图片的SIFT特征
        keypoints1,descriptors1 = extract_sift_feature(img1)
        keypoints2,descriptors2 = extract_sift_feature(img2)
    
        #创建一个匹配器
        bf = cv2.BFMatcher()
        #记录图1和图2的匹配的关键点
        matches1 = bf.knnMatch(descriptors1,descriptors2,k=2)
        top_results1 = []
        for m,n in matches1:
            if m.distance < 0.7 * n.distance:
                top_results1.append(m)
        #记录图2和图1匹配的关键点
        matches2 = bf.knnMatch(descriptors2,descriptors1,k=2)
        top_results2 = []
        for m,n in matches2:
            if m.distance < 0.7 * n.distance:
                top_results2.append(m)
        #从匹配的关键点中选择出有效的匹配
        #确保匹配的关键点信息在图1和图2以及图2和图1是一致的
        top_results = []
        for m1 in top_results1:
            m1_query_idx = m1.queryIdx
            m1_train_idx = m1.trainIdx
    
            for m2 in top_results2:
                m2_query_idx = m2.queryIdx
                m2_train_idx = m2.trainIdx
    
                if m1_query_idx == m2_train_idx and m1_train_idx == m2_query_idx:
                    top_results.append(m1)
    
        #计算图像之间的相似度
        #通过计算两张图片之间的匹配的关键点的个数来计算相似度
        image_sim = len(top_results) / min(len(keypoints1),len(keypoints2))
        return image_sim
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 提取图片SIFT特征的向量计算相似度
      将图片中的每个SIFT特征点转换称为一个128维的特征向量,通过控制提取特征的个数来控制特征向量的长度,理论上来说组合特征向量用到的特征越多计算出来的图片相似度越高。
    def extract_SIFT_vector(img,vector_size):
        gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
        sift = cv2.xfeatures2d.SIFT_create()
        keypoints = sift.detect(gray_img, None)
        # 根据关键点的返回值进行排序,越大越好
        keypoints = sorted(keypoints, key=lambda x: -x.response)
        img_kps = keypoints[:vector_size]
        # 根据关键点来计算描述子向量
        kps, des = sift.compute(gray_img, img_kps)
        # 将向量展开为1维的
        vector = des.flatten()
        # 检查提取特征的长度,SIFT特征一个关键点的描述子是128维
        vector_len = vector_size * 128
        # 对于提取图片特征向量长度不够的使用0进行补充
        if vector.size < vector_len:
            vector = np.concatenate(vector, np.zeros(vector_len - vector.size))
        return vector
    
    
     
     import scipy.spatial as T
    
    #设置提取特征向量特征的个数
    vector_size = 30
    img_path1 = "demo.png"
    img_path2 = "demo_gauss_noise.png"
    img1 = cv2.imread(img_path1)
    img2 = cv2.imread(img_path2)
    #提取图片的特征向量
    img1_vector = extract_SIFT_vector(img1,vector_size).reshape(-1,128*vector_size)
    img2_vector = extract_SIFT_vector(img2,vector_size).reshape(-1,128*vector_size)
    #计算图片之间的相似度
    sim = T.distance.cdist(img1_vector, img2_vector, 'cosine')
    print(sim)#0.411
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    总结

    这篇文章我们主要介绍了如何使用opencv来提取图片的SIFT特征,以及如何来使用SIFT特征点来进行图像匹配和图像的相似度计算

    参考

    1. https://medium.com/@shehan.a.perera/a-comparison-of-sift-surf-and-orb-333d64bcaaea
    2. https://docs.opencv.org/4.5.5/df/dd2/tutorial_py_surf_intro.html
    3. https://github.com/ShehanPerera/Research
    4. https://stackoverflow.com/questions/64525121/sift-surf-set-opencv-enable-nonfree-cmake-solution-opencv-3-opencv-4
    5. https://docs.opencv.org/4.x/dc/dc3/tutorial_py_matcher.html
  • 相关阅读:
    Alibaba官方上线,SpringBoot+SpringCloud全彩指南(第五版)
    C++内存管理 (new、delete)知识点+完整思维导图+实操图+深入细节通俗易懂建议收藏
    idea 模板参数注释 {@link}
    使用U-Net方法对航空图像进行语义分割
    Lua中如何实现类似gdb的断点调试—09支持动态添加和删除断点
    第一行代码Android 第九章9.4-9.5(解析JSON格式,网络编程最佳实践:发送HTTP请求的代码)
    C++ 炼气期之变量的生命周期和作用域
    概统 | 秒杀方法及注意点
    Genesis公链:夯实Web 3.0发展底座
    基于Matlab实现多个图像压缩案例(附上源码+数据集)
  • 原文地址:https://blog.csdn.net/sinat_29957455/article/details/125136620