官网https://docs.opencv.org/3.4.1/d1/de0/tutorial_py_feature_homography.html
什么是单应Homography ?
Homography就是一个变换(3*3矩阵),将一张图中的点映射到另一张图中对应的点
平面的单应性被定义为从一个平面到另一个平面的投影映射。
在上一节我们做了什么呢?
我们先有一副查找图像(queryImage),在图像上找到一些特征点;然后在有一副目标图像(trainImage),也在这个图上找到一些特征点。最后我们在2幅图的特征点之间进行匹配。简而言之,我们在另外一副杂乱的图像中找到了目标对象(的一部分)。这些信息足够在目标图像中准确找到需要查找的对象。
为此,我们需要使用calib3d模块中的cv.findHomography().函数。如果我们把2幅图的特征点传输给这个函数,它就能找到需要查找的对象的透视变化图。然后我们可以用cv.perspectiveTransform() 函数找到这个对象。它需要至少4个正确的点才能找到这个变化。
我们已经看到匹配时候可能会有一些错误,这些错误会影响匹配结果。为了解决这个问题,算法使用了RANSAC或者LEAST_MEDIAN(根据标志位决定)。所以提供正确估计的良好匹配被称为有效数据(inliers),其余部分则被称为无效数据(outliers)。cv.findHomography() 函数返回一个掩膜,它定义了inliers和outliers点。
实现代码
首先,找到SIFT特征点,使用FLANN匹配器,用比率测试找到最佳匹配。这个在上节中已经介绍过https://blog.csdn.net/weixin_42555985/article/details/100651652
# -*- coding: cp936 -*-
import cv2
import numpy as np
from matplotlib import pyplot as plt
MIN_MATCH_COUNT = 10
img1 = cv2.imread('box.png',0) # queryImage
img2 = cv2.imread('box_in_scene.png',0) # trainImage
# Initiate SIFT detector
sift = cv2.xfeatures2d.SIFT_create()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
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)
# store all the good matches as per Lowe's ratio test.
good = []
for m,n in matches:
if m.distance < 0.7*n.distance:
good.append(m)
上面代码上一节中已经介绍过。
在这里增加一个条件,至少存在10个匹配才会去查找目标(MIN_MATCH_COUNT = 10),否则会提示匹配不足。
如果找到足够匹配点,我们提取2幅图中匹配点的坐标,把它们传入函数,获得透视变化。。一旦我们找到3x3 的变换矩阵,就可以使用它将查询图像的角点(四个角)变换到目标图像中去了。然后再绘制出来。
以下这段代码是本文的重点
if len(good)>MIN_MATCH_COUNT:
# 获取关键点的坐标
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)
# 第三个参数 计算单应矩阵所使用的方法
#0 - 利用所有点的常规方法
#CV_RANSAC - RANSAC-based robust method
#CV_LMEDS - Least-Median robust method
# 第四个参数取值范围在1 到10,?将点对视为内点的最大允许重投影错误阈值。
#原图像的点经过变换后点与目标图像上对应点的误差, 超过误差就认为是outlier
# 返回值中M 为变换矩阵。
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
matchesMask = mask.ravel().tolist()
# 获得原图像的高和宽
h,w= img1.shape #官网为 h,w,d = 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,3, cv2.LINE_AA)
else:
print( "Not enough matches are found - {}/{}".format(len(good), MIN_MATCH_COUNT) )
matchesMask = None
最后绘制inliers(如果能成功找到目标图像)或者匹配的关键点(如果失败)。
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, 'gray'),plt.show()