前面几节我们做了什么?我们使用queryImage,在其中找到一些特征点,我们使用另一个trainImage,也在该图像中找到特征,并在其中找到最佳匹配。简而言之,我们在另一张杂乱的图像中找到了物体的某些部分的位置。这些信息足以准确地在trainImage上找到对象。
为此,我们可以使用calib3d模块中的函数,即cv2.findHomography()。如果我们从两个图像中传递点的集合,它会找到那个物体的透视变换。然后我们可以使用cv2.perspectiveTransform()来查找对象。它需要至少四个正确的点来找到变换。
我们已经看到,在匹配时可能会有一些错误,这可能会影响结果。为了解决这个问题,算法使用RANSAC或LEAST_MEDIAN(可以由标志来决定)。因此,提供正确估计的良好匹配被称为内值,其余的被称为异常值。cv2.findHomography()返回一个掩码,该掩码指定了靠近点和离群点。
import numpy as np
import cv2
from matplotlib import pyplot as plt
# 读入图像
img1 = cv2.imread("assets/box.png", 0)
img2 = cv2.imread("assets/box_in_scene.png", 0)
# 创建SIFT
sift = cv2.SIFT_create()
# 找关键点和计算特征
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# 寻找匹配的关键点
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
seach_params = dict(checks = 50)
good = []
flann = cv2.FlannBasedMatcher(index_params, seach_params)
matches = flann.knnMatch(des1, des2, k=2)
for m, n in matches:
if m.distance < 0.7*n.distance:
good.append(m)
# 找到映射关系
MIN_MATCH_COUNT = 10
pts__ = None
dst__ = None
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)
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, 3, 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)
# 对应的点发生了偏移
shift_x = np.float32([[w-1, 0], [w-1, 0], [w-1, 0], [w-1, 0]]).reshape(-1, 1, 2)
dst2__ = np.int32(dst__ + shift_x)
# 绘制原始图像图像的位置和查找目标的位置
img3 = cv2.polylines(img3, [np.int32(pts__)], True,
(0, 0, 255), 2, cv2.LINE_AA)
img3 = cv2.polylines(img3, [np.int32(dst2__)], True,
(0, 0, 255), 2, cv2.LINE_AA)
cv2.imshow("result", img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.findHomography( srcPoints, dstPoints[, method[, ransacReprojThreshold[, mask[, maxIters[, confidence]]]]] ) -> retval, mask
cv2.findHomography( srcPoints, dstPoints, params[, mask] ) -> retval, mask
查找平面间的透视变换
- srcPoints: 原始平面里的点,type CV_32FC2 or vector
- dstPoints: 查找目标平面里的点,type CV_32FC2 or vector
- method: 查找方法,可选值为:
- 0: 常规的方法用所有的点,最小二乘法;
- RANSAC: 基于RANSAC的鲁棒方法;
- LMEDS: 最小中值稳健方法;
- RHO: 基于PROSAC鲁棒方法;
- ransacReprojThreshold: 将点对作为内线处理时允许的最大重投影误差(仅用于RANSAC和RHO方法)。如果 ∣ ∣ d s t P o i n t s i − c o n v e r t P o i n t s H o m o g e n e o u s ( H ∗ s r c P o i n t s i ) ∣ ∣ 2 > r a n s a c R e p r o j T h r e s h o l d ||dstPoints_i−convertPointsHomogeneous(H∗srcPoints_i)||_2>ransacReprojThreshold ∣∣dstPointsi−convertPointsHomogeneous(H∗srcPointsi)∣∣2>ransacReprojThreshold , 则点 i i i 为离群点。一般设置为1~10
- mask: 通过鲁棒方法(RANSAC或lmed)设置的可选输出掩码。注意,输入掩码值将被忽略。
- maxIters: RANSAC 最大迭代次数
- confidence: 置信度层级,0~1
【OpenCV-Python】教程:3-2 几何变换(仿射变换,透视变换)_黄金旺铺的博客-CSDN博客_python根据三个点仿射变换