OpenCV-Python——第31章:特征匹配与单应性查找对象

目录

0 基础           1 代码


0 基础

先解释一下单应性,在计算机视觉中:平面的单应性被定义为从一个平面到另一个平面的投影映射。比如,一个二维平面上的点映射到摄像机成像仪上的映射就是平面单应性的例子。

之前我们使用一个查询图像,在其中找到一些 特征点(关键点),我们又在另一幅图像中也找到了一些特征点,最后对这两幅 图像之间的特征点进行匹配。简单来说就是:我们在一张杂乱的图像中找到了 一个对象(的某些部分)的位置。这些信息足以帮助我们在目标图像中准确的 找到(查询图像)对象。

为了达到这个目的我们可以使用calib3d模块中的cv2.findHomography()函数。如果将这两幅图像中的特征点集传给这个函数,他就会找到这个对象的透视图变换。然后我们就可以使用函数 cv2.perspectiveTransform() 找到这个对象了。至少要 4 个正确的点才能找到这种变换。

我们已经知道在匹配过程可能会有一些错误,而这些错误会影响最终结果。为了解决这个问题,算法使用 RANSAC 、 LMEDSPROSAC(可以通过参数来设定)。所以好的匹配提供的正确的估计被称为 inliers,剩下的被称为 outliers。cv2.findHomography() 返回一个掩模,这个掩模确定了 inlier 和 outlier 点。

RANSAC(Random Sample Consensus)随机抽样一致算法,具体可以参考:

http://www.cnblogs.com/xingshansi/p/6763668.html

LMEDS(Least Median Of Squares)最小中值平方法,具体可以参考:

https://blog.****.net/billbliss/article/details/78592216

http://lanbing510.info/2015/10/15/RANSAC.html

PROSAC (The Progressive Sample Consensus)渐进抽样一致算法,具体可以参考:

https://blog.****.net/Rotating_Sky/article/details/87929195

 

 1 代码

基本步骤

1)使用SHIT检测特征点

2)使用FLANN匹配器进行匹配

3)选取好的匹配

4)根据原图像和目标图像中对应的特征点,使用上述其中一种算法求变换矩阵

5)最后将原图像的边界经变换矩阵变换后画到目标图像上

获取变换矩阵的函数:

retval, mask = cv2.findHomography(srcPoints, dstPoints, method, ransacReprojThreshold, maxIters, confidence)

  • retval:输出的矩阵
  • srcPoints:原图像中对应的特征点坐标
  • dstPoints:目标图像中对应的特征点坐标
  • method:计算单应矩阵的方法,具体算法可参考上面的博客

                       0:使用所有点的常规方法,即最小二乘法

                       RANSAC:基于RANSAC的方法

                       LMEDS:最小中值稳健方法

                       RHO:基于PROSAC的方法

  • ransacReprojThreshold:将点对视为内点的最大允许重投影错误(仅用于RANSAC和RHO方法)。也就是说,如果

                OpenCV-Python——第31章:特征匹配与单应性查找对象

              那么点被认为是异常。如果以像素为单位测量srcPoints和dstPoints,则将此参数设置在1到10的范围内通常是有意义的

  • mask:可选输出掩码由稳健方法(RANSAC或LMEDS)设置。
  • maxIters:RANSAC迭代的最大数量。
  • confidence:置信水平,介于0和1之间。

 举个例子:

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

MIN_MATCH_COUNT = 10
img1 = cv2.imread('test37_0.jpg', 0)  # 原图像
img2 = cv2.imread('test37_1.jpg', 0)  # 待搜索图像,下面称目标图像
# 启动SIFT检测器
sift = cv2.xfeatures2d.SIFT_create()
# 使用SIFT查找关键点和描述符
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)

# 使用FLANN匹配器进行匹配
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)  # 获得匹配结果

# 按照Lowe的比率存储所有好的匹配。
good = []
for m, n in matches:
    if m.distance < 0.7*n.distance:
        good.append(m)

# 只有好的匹配点多于10个才查找目标,否则显示匹配不足
if len(good) > MIN_MATCH_COUNT:
    # 获取匹配点在原图像和目标图像中的的位置
    # kp1:原图像的特征点
    # m.queryIdx:匹配点在原图像特征点中的索引
    # .pt:特征点的坐标
    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)
    # 获取变换矩阵,采用RANSAC算法
    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, 5, cv2.LINE_AA)
else:
    print("Not enough matches are found - {}/{}".format(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)
plt.imshow(img3), plt.title('Result'), 
plt.axis('off')
plt.show()

结果如下:

OpenCV-Python——第31章:特征匹配与单应性查找对象