1 knnBFmatcher匹配
BFmatcher(Brute-Force Matching)暴力匹配,应用BFMatcher.knnMatch( )函数来进行核心的匹配,knnMatch(k-nearest neighbor classification)k近邻分类算法。
kNN算法则是从训练集中找到和新数据最接近的k条记录,然后根据他们的主要分类来决定新数据的类别。该算法涉及3个主要因素:训练集、距离或相似的衡量、k的大小。kNN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。该方法在确定分类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。
kNN方法在类别决策时,只与极少量的相邻样本有关。由于kNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,kNN方法较其他方法更为适合。
BFMatcher解决匹配
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1,des2, k=2)
2 SURF算法
SURF (Speeded Up Robust Features) 是一种基于 SIFT 算法的改进版本,它在计算效率和描述子的稳健性方面进行了优化。SURF 算法由 Herbert Bay 等人于 2006 年提出.
2.1 主要目的
与 SIFT 算法相比,SURF 算法主要改进了以下两个方面:
尺度空间极值检测:SURF 算法使用快速Hessian矩阵(Fast Hessian)来检测图像中的尺度空间极值点。相比之下,SIFT 算法使用高斯差分金字塔来检测极值点,而快速Hessian矩阵能够更快地计算图像的尺度空间。
描述子计算:SURF 算法使用了一种加速技术称为积分图像(Integral Image),用于快速计算关键点周围区域的梯度和特征描述子。这种技术可以显著加快计算速度。
由于 SURF 算法对尺度、旋转和仿射变换具有较好的不变性,并且计算速度更快,因此在实际应用中得到了广泛的应用。与 SIFT 算法相比,SURF 算法在实时性要求较高的场景下更加适用。
2.2 主要步骤
SURF算法的主要步骤可以概括为以下几个阶段:
尺度空间构建:首先,对输入图像进行尺度空间构建。这通常涉及使用高斯滤波器构建图像的金字塔,通过不同尺度的图像来检测不同尺度的特征。
关键点检测:在每个尺度的图像中,使用Hessian矩阵计算特征点的兴趣值。Hessian矩阵描述了图像局部区域的灰度变化情况,通过检测局部最大值或最小值来确定关键点。
关键点定位:通过在尺度空间中插值,精确定位关键点的位置。这样做是为了提高关键点的精度,并且能够在亚像素级别进行定位。
方向分配:对于每个关键点,计算其主要方向。这通常通过计算特征点周围区域的梯度方向直方图来实现。主要方向用于后续的描述子计算和旋转不变性。
描述子计算:在关键点周围的邻域中,计算描述子来描述关键点的特征。SURF算法使用了一种基于哈尔小波响应的描述子计算方法。描述子捕捉了关键点周围区域的局部特征。
特征匹配:使用描述子比较来进行特征匹配。常见的方法是使用距离度量(如欧氏距离或汉明距离)来衡量描述子之间的相似性。匹配算法可以采用暴力匹配或更高级的方法(如最近邻搜索或KD树)。
异常值剔除:在进行特征匹配后,可能存在一些错误的匹配或异常值。通过应用一些筛选机制(如RANSAC算法)来剔除这些异常值,以获得更准确的匹配结果。
由于OpenCV库中已经有对应的SURF算法接口,直接调用即可,如下python代码:
创建 SURF 对象
surf = cv2.xfeatures2d.SURF_create()
检测关键点和计算描述子
keypoints, descriptors = surf.detectAndCompute(image, None)
参考:
Opencv之全景拼接
应用OpenCV和Python进行SIFT算法的实现
详解SIFT、SURF和ORB特征点检测和描述算法
本实验采取基于BFmatcher的SURF实现,实现对图像(rabbit)的拼接
具体代码如下:
import numpy as np
import cv2
from matplotlib import pyplot as plt
def cv_show(name,img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
#imageA = cv2.imread("/home/lake/teacher.WG/first/104.bmp")
imageA = cv2.imread("/home/lake/图片/rabbit2.png")#左右图像位置相反放置
#imageA = cv2.imread("/home/lake/图片/104.bmp")#左右图像位置相反放置;
imageA=cv2.resize(imageA,(141,180))
#imageA = cv2.resize(imageA,(0,0),fx=0.4,fy=0.4)
imageB = cv2.imread("/home/lake/图片/rabbit1.png")
#imageB = cv2.imread("/home/lake/图片/107.bmp")
#imageB = cv2.imread("/home/lake/teacher.WG/first/107.bmp")
imageB=cv2.resize(imageB,(141,180))
#imageB = cv2.resize(imageB,(0,0),fx=0.4,fy=0.4)
grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)
# 建立SURF生成器
descriptor = cv2.xfeatures2d.SURF_create()
# 检测SURF特征点,并计算描述子
kpsA, featuresA = descriptor.detectAndCompute(grayA, None)
print(np.array(kpsA).shape)
print(np.array(featuresA).shape)
kpsB, featuresB = descriptor.detectAndCompute(grayB, None)
#画出特征点,并显示为红色圆圈
img3 = cv2.drawKeypoints(imageA,kpsA,imageA,color=(255,0,255))
img4 = cv2.drawKeypoints(imageB,kpsB,imageB,color=(255,0,255))
hmerge = np.hstack((img3, img4)) #水平拼接
cv2.imshow("point", hmerge) #拼接显示为gray
cv2.waitKey(0)
# 将结果转换成NumPy数组
kpsA = np.float32([kpA.pt for kpA in kpsA])
kpsB = np.float32([kpB.pt for kpB in kpsB])
print(kpsA.shape)
#重载图像使后续拼接的图像没有红圈显示
imageA = cv2.imread("/home/lake/图片/rabbit2.png")#左右图像位置相反放置
imageB = cv2.imread("/home/lake/图片/rabbit1.png")
#imageA = cv2.imread("/home/lake/图片/104.bmp")
#imageB = cv2.imread("/home/lake/图片/107.bmp")
# 建立暴力匹配器
matcher = cv2.BFMatcher()
# 使用KNN检测来自A、B图的SIFT特征匹配对,K=2
rawMatches = matcher.knnMatch(featuresA, featuresB, k=2)
print(np.array(rawMatches).shape)
matches = []
for m, n in rawMatches:
if m.distance < 0.75 * n.distance:
matches.append((m.trainIdx, m.queryIdx))
print(np.array(matches).shape)
if len(matches) > 4:
# 获取匹配对的点坐标
ptsA = np.float32([kpsA[i] for (_, i) in matches])
print(ptsA.shape)
ptsB = np.float32([kpsB[i] for (i, _) in matches])
# 计算视角变换矩阵
H, status = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, 4.0)
print(H.shape)
print(status.shape)
# 将图片A进行视角变换,result是变换后图片
hA, wA = imageA.shape[:2]
print((hA, wA))
hB, wB = imageB.shape[:2]
print((hB, wB))
result = cv2.warpPerspective(imageA, H, (wA+wB, hA))
# 可视化
vis = np.zeros((hA, wA+wB, 3), dtype=np.uint8)
vis[0:hA, 0:wA] = imageA
vis[0:hB, wA:] = imageB
# 联合遍历,画出匹配对
for ((trainIdx, queryIdx), s) in zip(matches, status):
# 当点对匹配成功时,画到可视化图上
if s == 1:
# 画出匹配对
ptA = (int(kpsA[queryIdx][0]), int(kpsA[queryIdx][1]))
ptB = (int(kpsB[trainIdx][0]) + wA, int(kpsB[trainIdx][1]))
cv2.line(vis, ptA, ptB, (0, 255, 0), 1)
cv_show("Keypoint Matches", vis)
cv2.waitKey(0)
cv_show('result', result)
cv2.waitKey(0)
# 将图片B传入result图片最左端
result[:hB, :wB] = imageB
cv_show('result', result)
#调整拼接后图像显示,宽度为两张拼接之前图像总和
result=cv2.resize(imageB,(1000,418))
else:
print("匹配不足,无法拼接")
实验结果: