OpenCV-Python系列之单应性查找对象实战篇
之前我们讨论到cv2.findHomography()函数里有四个算法,这次我们来简述最后两个算法。
LMEDS
LMEDS又称最小中值平方法,LMedS的经典步骤是:
1. 随机采样
2. 计算模型参数
3. 计算相对模型的点集偏差err,并求出偏差中值Med(err)
4. 迭代2. 3.步直至获得符合阈值的最优解:Med(err)最小
5. 精确优化模型参数(LM算法迭代优化)
LMedS的函数接口 参照OpenCV来说主要需要2-3个参数(第三个不是必须的)
1. 置信度confidence:设置之后代表RANSAC采样n次过程中会出现(至少一次)采样点数据集中的点都为内点的概率,这个值设置的太大,会增加采样次数。太小,会使结果不太理想。一般取0.95-0.99
2. 最大采样迭代次数maxIters:为了防止一直在采样计算
3.最大精细迭代次数refineIters:在采样之后,选取最优解。可以增加精确优化,比如使用LM算法获得更优解。
RANSAC的阈值在具有物理意义或者几何意义的时候比较容易确定,但是当阈值不具有这些特征的时候,就成了一个不太好调整的参数了。这时LMedS可以自适应迭代获得最优解。
PROSAC
渐进一致采样法(PROSAC) 是对经典的 RANSAC中采样的一种优化。相比经典的 RANSAC 方法均匀地从整个集合中采样,PROSAC 方法是从不断增大的最佳对应点集合中进行采样的。所以这种方法可以节省计算量,提高运行速度。由于涉及到机器学习相关的知识,在这里并不过多的详述,我们看一下原论文作者的算法:
OpenCV中的使用
基本步骤
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方法)。也就是说,如果
那么点被认为是异常。如果以像素为单位测量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('box.png', 0) # 原图像 img2 = cv2.imread('box_in_scene.png', 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, 0, 255), # 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()
结果:
可以看到,即便是在诸多的干扰性的图像中,特征匹配仍然相当的准确。目前此代码只实验了RANSAC算法,至于我们之前讲到的其他的算法,大家自行实验。