GMS图像特征匹配算法(学习笔记)

一、算法原理

当前各种特征匹配算法往往不能兼顾运行速度和鲁棒性。鲁棒性好的算法往往运行较慢,而运行较快的算法又不够稳定。GMS(Grid-based Motion Statistics) 算法希望在能够保证匹配效果的同时提高运行的效率。

GMS算法受到BF匹配的启发,认为缺乏明显的正确匹配不是因为匹配对数量太少,而是因为难以区分正确和错误的匹配。由于正确匹配的邻域内的特征点之间往往都保持了几何一致性,通过评估待判断的匹配对特征点邻区域内含有的匹配对的点的数量可以区分正确和错误的匹配。如图所示,如果一对匹配点的邻区域内还有其他的匹配对,则这一对匹配对是正确的可能性就比较大。

python 多幅特征匹配融合 特征匹配算法_特征点

GMS 算法中的一些关键点:
①运动平滑性使得,在正确匹配对特征点的附近一个范围内拥有相似的几何关系;而错误匹配对之间往往不会有这种一致性关系。
②增加原始待匹配特征点数量也将对匹配质量带来好的影响。
③一个区域具有运动平滑性的话,正确匹配对附近小区域匹配对的预测得到的位置往往相同,而用错误匹配来预测得到的结果位置往往不同

所以GMS算法通过对待判断匹配周围区域内的存在的其他匹配对数量来判断匹配的正确性。

二、源代码理解

①默认将图像分割成 20×20 的网格,1-400 编号。

②旋转不变性由这样的旋转图案实现。

③预匹配集 all_matches 、剔除错误匹配后的匹配集 gms_matches

输入 all_matches 然后最终输出正确匹配集 gms_matches

python 多幅特征匹配融合 特征匹配算法_python 多幅特征匹配融合_02


1.run 函数

关键的函数,利用各种数据得出最终的 inlier_mask

def run(self, rotation_type):
        self.inlier_mask = [False for _ in range(self.matches_number)]
        # Initialize motion statistics
        self.motion_statistics = np.zeros((int(self.grid_number_left), int(self.grid_number_right)))
        self.match_pairs = [[0, 0] for _ in range(self.matches_number)]

        for GridType in range(1, 5):
            self.motion_statistics = np.zeros((int(self.grid_number_left), int(self.grid_number_right)))
            self.cell_pairs = [-1 for _ in range(self.grid_number_left)]
            self.number_of_points_per_cell_left = [0 for _ in range(self.grid_number_left)]

            self.assign_match_pairs(GridType)
            self.verify_cell_pairs(rotation_type)

            # Mark inliers
            for i in range(self.matches_number):
                if self.cell_pairs[int(self.match_pairs[i][0])] == self.match_pairs[i][1]:
                    self.inlier_mask[i] = True

        return sum(self.inlier_mask)

①self.motion_statistics
大小:左图网格数 × 右图网格数
记录了左图网格 i 和右图网格 j 之间的匹配对数。
②match_pairs
大小:all_matches × 2
将预匹配集中所有匹配对的左右两点对应网格编号记录下来。
后面的 assign_match_pairs 函数会更改该数组的数据。
③cell_pairs
大小:左图网格数 × 1
记录与左边网格共同持有匹配对的数量最多的右边网格的编号。
例:第 i 个数据记录与左边第 i 个网格有最多匹配对的右边网格的编号。
④number_of_points_per_cell_left
大小:左图网格数 × 1
记录左图每个网格存在的匹配点的数量。

2.verify_celll_pairs 函数
对左图每个网格,遍历所有右图网格,找出与左图网格存在共同匹配对数量最多的右图网格编号,记录在 cell_psirs 中。
(和周围8个网格,一共9个中通过点数量计算得分值 score 和阈值 thresh,如果最终 score < thresh,对应 cell_pairs 元素记为-2)

def verify_cell_pairs(self, rotation_type):
        current_rotation_pattern = ROTATION_PATTERNS[rotation_type - 1]
        for i in range(self.grid_number_left):
            if sum(self.motion_statistics[i]) == 0:
                self.cell_pairs[i] = -1
                continue
            max_number = 0
            for j in range(int(self.grid_number_right)):
                value = self.motion_statistics[i]
                if value[j] > max_number:
                    self.cell_pairs[i] = j
                    max_number = value[j]

            idx_grid_rt = self.cell_pairs[i]
            nb9_lt = self.grid_neighbor_left[i]
            nb9_rt = self.grid_neighbor_right[idx_grid_rt]
            score = 0
            thresh = 0
            numpair = 0

            for j in range(9):
                ll = nb9_lt[j]
                rr = nb9_rt[current_rotation_pattern[j] - 1]
                if ll == -1 or rr == -1:
                    continue

                score += self.motion_statistics[int(ll), int(rr)]
                thresh += self.number_of_points_per_cell_left[int(ll)]
                numpair += 1

            thresh = THRESHOLD_FACTOR * math.sqrt(thresh/numpair)
            if score < thresh:
                self.cell_pairs[i] = -2

3.assign_match_pairs 函数
将预匹配集的所有匹配的两个点按几何位置转换成网格编号,记录在match_pairs 数组中。

def assign_match_pairs(self, grid_type):
        for i in range(self.matches_number):
            lp = self.normalized_points1[self.matches[i][0]]
            rp = self.normalized_points2[self.matches[i][1]]
            lgidx = self.match_pairs[i][0] = self.get_grid_index_left(lp, grid_type)

            if grid_type == 1:
                rgidx = self.match_pairs[i][1] = self.get_grid_index_right(rp)
            else:
                rgidx = self.match_pairs[i][1]

            if lgidx < 0 or rgidx < 0:
                continue
            self.motion_statistics[int(lgidx)][int(rgidx)] += 1
            self.number_of_points_per_cell_left[int(lgidx)] += 1

4.inlier_mask 生成过程(run 函数最后)

上述 run 函数中得到的 match_pairs 和 cell_pairs 分别存储了用两种方法得到的网格对应信息。

match_pairs:根据预匹配集中匹配对两点在图片中的位置对应网格

cell_pairs:根据与左边网格共同拥有匹配数量最多来找出右边的网格

python 多幅特征匹配融合 特征匹配算法_计算机视觉_03


对预匹配集中所有匹配对,用左点根据上述两种方法得到的右点如果相同,将此匹配对认为为正确匹配对。

参考资料:
[1] Bian J , Lin W Y , Matsushita Y , et al. GMS: Grid-Based Motion Statistics for Fast, Ultra-Robust Feature Correspondence[C]// 2017 IEEE Conference on Computer Vision and Pattern Recognition (CVPR). IEEE, 2017.