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算法,至于我们之前讲到的其他的算法,大家自行实验。
我要赚赏金
