目录
蛮力匹配(ORB匹配)
Brute-Force匹配非常简单,首先在第一幅图像中选取一个关键点然后依次与第二幅图像的每个关键点进行(描述符)距离测试,最后返回距离最近的关键点.
对于BF匹配器,首先我们必须使用**cv2.BFMatcher()**创建 BFMatcher 对象。它需要两个可选的参数:
- normType :它指定要使用的距离测量,默认情况下,它是cv2.NORM_L2 ,它适用于SIFT,SURF等(cv2.NORM_L1 也在那里)。对于基于二进制字符串的描述符,如ORB,BRIEF,BRISK等,应使用cv2.NORM_HAMMING ,使用汉明距离作为度量,如果ORB使用WTA_K == 3or4,则应使用cv2.NORM_HAMMING2。
- crossCheck :默认值为
False
。如果设置为True
,匹配条件就会更加严格,只有到A中的第i个特征点与B中的第j个特征点距离最近,并且B中的第j个特征点到A中的第i个特征点也是最近时才会返回最佳匹配,即这两个特征点要互相匹配才行
两个重要的方法是BFMatcher.match()和BFMatcher.knnMatch(), 第一个返回最佳匹配, 第二种方法返回k个最佳匹配,其中k由用户指定.
使用cv2.drawMatches()来绘制匹配的点,它会将两幅图像先水平排列,然后在最佳匹配的点之间绘制直线。如果前面使用的BFMatcher.knnMatch() ,现在可以使用函数cv2.drawMatchsKnn为每个关键点和它的个最佳匹配点绘制匹配线。如果要选择性绘制就要给函数传入一个掩模.
一对一匹配 BFMatcher.match()
python
import numpy as np
import cv2
from matplotlib import pyplot as plt
img1 = cv2.imread('./image/girl1.jpg')
img2 = cv2.imread('./image/girl2.jpg')
sift = cv2.SIFT_create()
# kp代表特征点 des每个点对应特征向量
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
bf = cv2.BFMatcher(crossCheck=True) # 可选对象crossCheck
# 1对1匹配
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance) # 排序,通过距离来度量
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:30], None, flags=2) # matches[:10] 距离前十的给显示出来,即显示十条匹配线
cv2.imshow('img', img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
k对最佳匹配 BFMatcher.knnMatch()
python
import numpy as np
import cv2
from matplotlib import pyplot as plt
img1 = cv2.imread('./image/girl1.jpg')
img2 = cv2.imread('./image/girl2.jpg')
sift = cv2.SIFT_create()
# kp代表特征点 des每个点对应特征向量
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# k对最佳匹配
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)
good = []
for m, n in matches:
# 过滤方法
if m.distance < 0.75 * n.distance:
good.append([m])
img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good, None, flags=2)
cv2.imshow('img', img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
RANSAC算法
蛮力匹配是一种简单而直观的匹配方法,适用于小规模的特征点匹配。通过比较所有可能的特征点对,找到最佳匹配。而RANSAC算法则通过随机采样和一致性检验,从包含噪声的数据中估计出正确的模型参数,对于一些存在噪声和异常值的情况,RANSAC能够更稳健地估计模型。
利用RANSAC算法计算变换矩阵
RANSAC是"RANdom SAmple Consensus"(随机一致采样)的缩写。该方法是用来找到正确模型来拟合带有噪声数据的迭代方法。给定一个模型,例如点集之间的单应性矩阵。基本的思想是:数据中包含正确的点和噪声点,合理的模型应该能够在描述正确数据点的同时摒弃噪声点。
此外还有ORB匹配、SIFT的特征匹配、基于FLANN的匹配器的匹配,等等
全景图像拼接
全景图像拼接是将多张图像拼接成一张全景图的任务。在这个过程中,特征点匹配和单应性矩阵估计是关键的步骤。你提到的使用SIFT找到特征点,并通过单应性矩阵将图像进行变换,是一种常见的方法。这样可以在不同视角或位置拍摄的图像中找到对应的特征点,从而实现拼接。
通过SIFT找特征点
关于单应性矩阵(H矩阵):
利用两个图像中至少四个特征点能够求解一个单应性矩阵(homography matrix),然后用这个单应性矩阵能够将图像1中的某个坐标变换到图像2中对应的位置。然而,矩阵的推导是来自于相机在不同位姿拍摄同一个三维平面,所以使用opencv计算单应性矩阵的时候前提是两个图像对应区域必须是同一平面。
当进行全景图像拼接时,常常需要使用RANSAC算法估计单应性矩阵。下面是一个简单的示例代码,其中包括特征点匹配、RANSAC算法和全景图像拼接的步骤。
python
import cv2
import numpy as np
def find_keypoints_and_descriptors(image):
# 使用SIFT算法找到图像的关键点和描述符
sift = cv2.SIFT_create()
kp, des = sift.detectAndCompute(image, None)
return kp, des
def match_keypoints(des1, des2):
# 使用BFMatcher进行特征点匹配
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)
# 使用比值测试排除不好的匹配
good = []
for m, n in matches:
if m.distance < 0.75 * n.distance:
good.append(m)
return good
def ransac_homography(matches, kp1, kp2, reproj_thresh=4.0):
# 将匹配的关键点转换为numpy数组
src_pts = np.float32([kp1[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2)
# 使用RANSAC算法估计单应性矩阵
H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, reproj_thresh)
return H
def stitch_images(image1, image2, H):
# 将图像1进行透视变换,将其叠加到图像2上
rows1, cols1 = image1.shape[:2]
rows2, cols2 = image2.shape[:2]
warp_img1 = cv2.warpPerspective(image1, H, (cols1 + cols2, rows2))
warp_img1[:rows2, :cols2] = image2
return warp_img1
if __name__ == "__main__":
# 读取两张图像
img1 = cv2.imread('image1.jpg')
img2 = cv2.imread('image2.jpg')
# 找到关键点和描述符
kp1, des1 = find_keypoints_and_descriptors(img1)
kp2, des2 = find_keypoints_and_descriptors(img2)
# 进行特征点匹配
matches = match_keypoints(des1, des2)
# 使用RANSAC估计单应性矩阵
H = ransac_homography(matches, kp1, kp2)
# 进行全景图像拼接
result = stitch_images(img1, img2, H)
# 显示拼接结果
cv2.imshow('Panorama', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
为了达到更好的拼接效果,可能需要使用更复杂的图像配准和融合技术。