图像特征点匹配

本文主要介绍图像特征点匹配众多方法中的sift算法。这是几年前写的代码,现在再回看发现很多内容我不记得了,记忆被删除了一样。但是,当时记录好了,也把代码、数据备份了,现在一边运行、一边查资料,很快就把这段代码捡起来了。话不多说,直接进入今天主题。

航片拼接、点云生成、SFM不可绕过的是特征点匹配。而特征点匹配中最经典、有效、稳定的方法是sift算法。我们有必要去学习一下这个sift算法。

下面是一个利用python语言调用opencv中的算法的示例。

输入图像1:

2

输入图像2:

1

具体代码如下:

# -*- coding: utf-8 -*-
# @Time    : 2021/3/10 20:11
import numpy as np
import cv2

'''
sift practice code 1
'''

class SIFT():
    def img_sift(self, file1, file2):
        sift = cv2.xfeatures2d.SIFT_create()

        img1 = cv2.imread(file1)
        gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)  # 灰度处理图像

        img2 = cv2.imread(file2)
        gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)  # 灰度处理图像

        data1 = self.match_2d(gray1, gray2)
        gray1 = data1[:, :, 0] / 255
        gray2 = data1[:, :, 1] / 255

        hmerge = np.hstack((gray1, gray2))  # 水平拼接
        cv2.imshow("gray", hmerge)  # 拼接显示为gray
        cv2.waitKey(0)

        data2 = self.match_3d(img1, img2)
        img1 = np.array(data2[:, :, :, 0]).astype('uint8')
        img2 = np.array(data2[:, :, :, 1]).astype('uint8')

        kp1, des1 = sift.detectAndCompute(img1, None)  # des是描述子
        kp2, des2 = sift.detectAndCompute(img2, None)  # des是描述子

        img3 = cv2.drawKeypoints(img1, kp1, img1, color=(255, 0, 255))  # 画出特征点,并显示为红色圆圈
        img4 = cv2.drawKeypoints(img2, kp2, img2, color=(255, 0, 255))  # 画出特征点,并显示为红色圆圈
        hmerge = np.hstack((img3, img4))  # 水平拼接
        cv2.imshow("point", hmerge)  # 拼接显示为gray
        cv2.waitKey(0)
        # BFMatcher解决匹配
        bf = cv2.BFMatcher()
        matches = bf.knnMatch(des1, des2, k=2)
        # 调整ratio
        good = []
        for m, n in matches:
            if m.distance < 0.75 * n.distance:
                good.append([m])

        img5 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good, None, flags=2)
        cv2.imshow("BFmatch", img5)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    def match_2d(self, d1, d2):
        x1, y1 = np.shape(d1)
        x2, y2 = np.shape(d2)
        if x1 > x2 and y1 > y2:

            d3 = np.zeros((x1, y1))
            d3[d3 == 0] = 255
            d3[5:5 + x2, 5:5 + y2] = d2
            x3, y3 = np.shape(d3)



        else:
            if y1 < y2:
                d3 = np.zeros((x2, y2))
                d3[d3 == 0] = 255
                d3[5:5 + x1, 5:5 + y1] = d1
                x3, y3 = np.shape(d3)

        d = []
        x3, y3 = np.shape(d3)
        datagray = np.zeros(shape=(x3, y3, 2))
        datagray[:, :, 0] = d3
        if x3 == x1:
            datagray[:, :, 1] = d1
        elif x3 == x2:
            datagray[:, :, 1] = d2

        return datagray

    def match_3d(self, d1, d2):
        x1, y1, z1 = np.shape(d1)
        x2, y2, z2 = np.shape(d2)

        if x1 > x2 and y1 > y2:
                d3 = np.zeros((x1, y1, 3))
                for i in range(z1):
                    print(i)
                    d3[5:5 + x2, 5:5 + y2, i] = d2[:, :, i]
        else:

                if y1 < y2:
                    d3 = np.zeros((x2, y2, 3))

                    for i in range(z1):
                        d3[5:5 + x1, 5:5 + y1,i] = d1[:, :, i]

        d=[]
        x3, y3, z3 = np.shape(d3)
        datagray = np.zeros(shape=(x3, y3, z3, 2))
        datagray[:, :, :, 0] = d3
        if x3 == x1:
            datagray[:, :, :, 1] = d1
        elif x3 == x2:
            datagray[:, :, :, 1] = d2

        return datagray


if __name__ == '__main__':
    imgname2 = r'.\1.jpg'
    imgname1 = r'.\2.jpg'
    a = SIFT()
    a.img_sift(imgname1, imgname2)

图像1和图像2的尺寸不一样,match_3d函数的目的是为了让这两张图像的尺寸一样。

尺寸一样才能顺利地进行可视化。小图使用零值进行填充,最终小图的尺寸与大图保持一种。实际上,在图像特征点匹配中不需要对图像进行尺寸一致处理,这样的处理只是为了图像可视化而已。

代码中结果可视化如下,零值在图像中显示为黑色。

image-20240806144317483

下一步就是查找特征点,通过opencv的sift算法,我们轻松找到这两幅图像的特征点,可视化结果如下:

image-20240806144812507

此时,特征点是无序的,需要给它们配对起来。下一步是特征点匹配。

每个特征点是128维的向量,目标的识别是通过两点集内特征点描述子的比对来完成。具有128维的特征点描述子的相似性度量采用欧式距离。

image-20240806145209624

特征点匹配结果如下:

image-20240806145304438

以上具体操作请看代码,在运行代码的时候,多打断点,多debug,多查看变量的数据结构。