目录
一、基于FLANN的匹配
FLANN匹配流程:
代码编写
二、基于FLANN进行单应性匹配
什么是单应性?
FLANN进行单应性匹配流程
代码编写
FLANN库全称是Fast Library for Approximate Nearest Neighbors,它是目前最完整的(近似)最近邻开源库。不但实现了一系列查找算法,还包含了一种自动选取最快算法的机制,FLANN使用C++写成,它能够很容易地通过C,MTALAB和Python等绑定提供的库,用在很多环境中。
sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
index_params = dict(algorithm=1, tree=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
FLANN匹配器接收两个参数:indexindexParams对象和searchParams对象,这些参数以Python中字典(和C++中结构体)的形式传递。
我们使用了5棵树的核密度树索引算法,FLANN可以并行处理此算法。
我们对每棵树执行50次检查或者遍历,检查次数越多,可以提供的精度也越高,但是,计算成本也就更高。
例如,如果mask_matches=[[0,0],[1,0]],这意味着有两个匹配的关键点:对于第一个关键点,最优和次优匹配都是糟糕的,对于第二个关键点,最佳匹配是好的,次优匹配是糟糕的。注意,我们假设了所有次优匹配都是糟糕的:
# 准备一个空的掩膜来绘制好的匹配
mask_matches = [[0, 0] for i in range(len(matches))]
# 向掩膜中添加数据
for i, (m, n) in enumerate(matches):
if m.distance < 0.7 * n.distance:
mask_matches[i] = [1, 0]
绘制并显示良好匹配,把mask_matches 列表传递给cv2.drawMatchesKnn 作为可选参数,cv2.drawMatchesKnn 只绘制掩膜中标记为好的匹配(值为1)
img_matches = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches, None,
matchColor=(0, 255, 0), singlePointColor=(255, 0, 0),
matchesMask=mask_matches, flags=0)
import numpy as np
import cv2
from matplotlib import pyplot as plt
# 读入图像
imgname1 = 'E:/qi.png'
imgname2 = 'E:/qiqiqi.png'
img1 = cv2.imread(imgname1)
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) # 灰度处理图像
img2 = cv2.imread(imgname2)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) # 灰度处理图像
sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
index_params = dict(algorithm=1, tree=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
# 准备一个空的掩膜来绘制好的匹配
mask_matches = [[0, 0] for i in range(len(matches))]
# 向掩膜中添加数据
for i, (m, n) in enumerate(matches):
if m.distance < 0.7 * n.distance:
mask_matches[i] = [1, 0]
img_matches = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches, None,
matchColor=(0, 255, 0), singlePointColor=(255, 0, 0),
matchesMask=mask_matches, flags=0)
cv2.imshow("FLANN", img_matches)
cv2.waitKey(0)
cv2.destroyAllWindows()
可以看到,效果比较理想,几乎所有的匹配项都处于正确的位置。接下来,我们将把这种类型的结果简化为更简洁的几何表示——单应性,他将描述整个匹配对象的姿态,而不是一堆不连续的点。
平面的单应性被定义为从一个平面到另一个平面的投影映射。
书中的作者(见参考)给出这样的解释:单应性是当一张图是另一张图的一个透视畸变时,在两张图中寻找彼此的一种情况。
good_matches = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good_matches.append(m)
MIN_NUM_GOOD_MATCHES = 10
if len(good_matches) >= MIN_NUM_GOOD_MATCHES:
src_pts = np.float32(
[kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
dst_pts = np.float32(
[kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
寻找单应性:
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
mask_matches = mask.ravel().tolist()
cv2.findHomography参数:
cv2.findHomography(srcPoints, dstPoints, method, ransacReprojThreshold, mask, maxIters, confidence)
· srcPoints:源平面中点的坐标矩阵
· dstPoints:目标平面中点的坐标矩阵
· method:计算单应矩阵所使用的方法。不同的方法对应不同的参数,具体如下:
· 0: 利用所有点的常规方法
· RANSAC:基于RANSAC的鲁棒算法
` LMEDS:最小中值鲁棒算法
· RHO:基于PROSAC的鲁棒算法
· ransacReprojThreshold:将点对视为内点的最大允许重投影错误阈值(仅用于RANSAC和RHO方法)
· mask:可选输出掩码矩阵,通常由鲁棒算法(RANSAC或LMEDS)设置。 请注意,输入掩码矩阵是不需要设置的
· maxIters:RANSAC算法的最大迭代次数,默认值为2000
· confidence:可信度值,取值范围为0到1
执行透射转换,取查询图像的矩形角点,并将其投影到场景中,从而画出边界
h, w = img1.shape[:2]
src_corners = np.float32(
[[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
dst_corners = cv2.perspectiveTransform(src_corners, M)
img2 = cv2.polylines(img2, [np.int32(dst_corners)], True, (255, 0, 0), 3, cv2.LINE_AA)
然后,继续绘制关键点并显示可视化效果
import numpy as np
import cv2
from matplotlib import pyplot as plt
# 读入图像
imgname1 = 'E:/qiqiqi.png'
imgname2 = 'E:/train1.jpg'
img1 = cv2.imread(imgname1)
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) # 灰度处理图像
img2 = cv2.imread(imgname2)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) # 灰度处理图像
sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
index_params = dict(algorithm=1, tree=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
good_matches = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good_matches.append(m)
MIN_NUM_GOOD_MATCHES = 10
if len(good_matches) >= MIN_NUM_GOOD_MATCHES:
src_pts = np.float32(
[kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
dst_pts = np.float32(
[kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
mask_matches = mask.ravel().tolist()
h, w = img1.shape[:2]
src_corners = np.float32(
[[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
dst_corners = cv2.perspectiveTransform(src_corners, M)
img2 = cv2.polylines(img2, [np.int32(dst_corners)], True, (255, 0, 0), 3, cv2.LINE_AA)
else:
mask_matches = None
draw_params = dict(matchColor=(0, 255, 0), singlePointColor=None, matchesMask=mask_matches, flags=2)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, **draw_params)
cv2.imshow("FLANN", img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
【参考】:OpenCV 4计算机视觉 Python语言实现(原书第三版) 作者:Joseph Howse