编译环境:python3.8.3,OpenCV4.5.2,
项目代码:https://download.csdn.net/download/zao_chao/20554332
采用尺度不变特征变换(SIFT)进行图像匹配和拼接。基于 SIFT 点特征的图像配准过程包括特征提取、特征描述、特征匹配、求解变换模型参数以及图像变换配准。其中基准图如下图1,待配准图为图2。
图1 基准图
图2 待配准图
其配准步骤为;
1、SIFT特征提取、特征描述。一种基于尺度空间、对图像缩放、旋转甚至仿射变换保持不变的图像局部不变描述算子。其代码如下:
def detect(image):
# 转化为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 创建SIFT生成器
# descriptor是一个对象,这里使用的是SIFT算法
descriptor = cv2.xfeatures2d.SIFT_create()
# 检测特征点及其描述子(128维向量)
kps, features = descriptor.detectAndCompute(image, None)
return (kps,features)
2、SIFT粗匹配是通过欧氏距离测度来定义的,Lowe通过比较最近邻欧氏距离与次近邻欧氏距离的比值来进行匹配,当最近邻欧氏距离与次近邻欧氏距离的比值小于某个阈值时为匹配点,阈值一般取0.8,SIFT粗匹配简单代码如下:
# 建立暴力匹配器
matcher = cv2.DescriptorMatcher_create("BruteForce")
# 使用knn检测,匹配left,right图的特征点
raw_matches = matcher.knnMatch(features_left, features_right, 2)
print('粗匹配点数;',len(raw_matches))
matches = [] # 存坐标,为了后面
good = [] # 存对象,为了后面的演示
# 筛选匹配点
for m in raw_matches:
# 筛选条件
# print(m[0].distance,m[1].distance)
if len(m) == 2 and m[0].distance < m[1].distance * ratio:
good.append([m[0]])
matches.append((m[0].queryIdx, m[0].trainIdx))
"""
queryIdx:测试图像的特征点描述符的下标==>img_keft
trainIdx:样本图像的特征点描述符下标==>img_right
distance:代表这怡翠匹配的特征点描述符的欧式距离,数值越小也就说明俩个特征点越相近。
"""
# 特征点对数大于4就够用来构建变换矩阵了
kps_left = np.float32([kp.pt for kp in kps_left])
kps_right = np.float32([kp.pt for kp in kps_right])
print('精匹配点数:',len(matches))
其结果为:
图3 特征点数
3、由于简单基于欧氏距离测度的SIFT特征匹配中存在许多误匹配的点,这些误匹配点将严重影响后面变换模型参数的解算,因此需要利用鲁棒估计的方法将这些误匹配的点剔除,在这里鲁棒估计我们使用最常规的RANSAC方法。
if len(matches) > 4:
# 获取匹配点坐标
pts_left = np.float32([kps_left[i] for (i, _) in matches])
pts_right = np.float32([kps_right[i] for (_, i) in matches])
# 计算变换矩阵(采用ransac算法从pts中选择一部分点)
H, status = cv2.findHomography(pts_right, pts_left, cv2.RANSAC, threshold)
return (matches, H, good)
return None
其结果为:
图4 图片匹配拼接
4、图像变换。在获取图像变换参数矩阵之,利用该仿射变换参数以及图像插值,便可以将待配准图像转化到基准图像的坐标系下。代码如下:
def drawMatches(img_left, img_right, kps_left, kps_right, matches, H):
# 获取图片宽度和高度
h_left, w_left = img_left.shape[:2]
h_right, w_right = img_right.shape[:2]
"""对imgB进行透视变换
由于透视变换会改变图片场景的大小,导致部分图片内容看不到
所以对图片进行扩展:高度取最高的,宽度为两者相加"""
image = np.zeros((max(h_left, h_right), w_left+w_right, 3), dtype='uint8')
# 初始化
image[0:h_left, 0:w_left] = img_right
"""利用以获得的单应性矩阵进行变透视换"""
image = cv2.warpPerspective(image, H, (image.shape[1], image.shape[0]))#(w,h
"""将透视变换后的图片与另一张图片进行拼接"""
image[0:h_left, 0:w_left] = img_left
return image
最后将待配准图像配准后与基准图进行拼接: