2019-9-10 opencv特征检测和描述10-特征匹配+单应(Homography,投影映射)查找对象
官网https://docs.opencv.org/3.4.1/d1/de0/tutorial_py_feature_homography.html
补充知识
什么是单应Homography ?
Homography是一种基于3\times 3矩阵的几何转换操作,在图像处理中用于坐标变换;它通过这种转换(基于3\times 3矩阵)将图像上的每个点转换至目标图像的位置。
该几何关系是通过单应性模型来描述两个平面上对应点之间的投影关系。
在上一节我们主要完成了什么工作?
对于给定的查询图像(queryImage),我们从该图像中提取出一系列关键特征点;接着,在另一幅待识别的目标图像(trainImage)中同样地也提取出了一系列关键特征点。随后,在这两组关键特征点之间建立起了对应关系。
换句话说,在这一堆看似混乱的数据中筛选出了我们要找的目标片段;通过这些匹配信息,在目标图像中就可以精确地定位到所需查找的对象。
为此, 必须调用calib3d模块中的cv.findHomography()方法. 当我们将两张图像的关键特征点传递给该函数时, 它能够计算出目标图像在源图像上的透视变换关系. 随后可以通过调用cv.perspectiveTransform()方法来获得所需的结果. 该过程要求至少四个精确匹配的对应点才能准确实现透视变换.
在匹配过程中可能会出现一些误差,从而影响最终的结果。为了改善这一问题,我们采用RANSAC算法或LEAST MEDIAN方法(由标志位决定)。这样能提供可靠估计的有效配对被称作内点(inliers),剩下的则称为外点(outliers)。cv.findHomography() 函数返回一个掩膜矩阵,在该矩阵中明确了内点和外点的位置。
编写代码
第一步:提取关键点
第二步:采用FLANN算法进行匹配
第三步:通过比值测试确定最佳匹配点
这部分内容在上一节中有详细阐述:
# -*- 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个匹配时,在目标位置进行进一步操作;否则将无法执行后续操作
以下这段代码是本文的重点
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()
代码解读
本例中依然使用了上一节的图像
查找图像

目标图像

运行结果

被找到的对象在目标图像中用白色框标注了出来。
