import cv2
import numpy as np
if __name__=='__main__':
#1.读入图片
imageA = cv2.imread('left.png')
imageB = cv2.imread('right.png')
#2.转换为灰度图
gray1 = cv2.cvtColor(imageA,cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)
#3.构造sift对象,求解特征点和sift特征向量
sift=cv2.SIFT_create()
# 有两个必须的参数,第一个是求解特征点和特征向量的图像。第二个参数决定是否对特定区域求解
# 返回值kps是对图像求解的特征点(keypoints),是一个一维向量,其中每一个元素属于keypoint类型。
# dp是与kps对应的特征向量,也是一个列表,其中每一个元素是长度为128的向量.
#求解keypoint和描述子
kpsA, dpA = sift.detectAndCompute(gray1, None)
kpsB, dpB = sift.detectAndCompute(gray2, None)
# print(type(kpsA))
# print(type(kpsA[0]))
# print(type(dpA))
# print(dpA.shape)
#4.构造BFMatcher对象,用暴力匹配的方式寻找匹配点
bf=cv2.BFMatcher()
#5.用KnnMatch方法匹配关键点
#返回的列表元素是一组包含两个特征点匹配的列表,
# 其中第一个特征点是查询图片(dpA)中的,第二个特征点是训练图片(dpB)中的。
# 这里的k值为2,因此每个查询点最多可以与两个训练点进行匹配。
matches=bf.knnMatch(dpA,dpB,2)
# print(matches)
#6.手动去除不可靠匹配
# 用于存储好的匹配点的下标
good_matches=[]
for m in matches:
# print(m)
# print(m[0])
if len(m)==2 and m[0].distance<0.4*m[1].distance:
"""每个匹配对都由两个元素组成:第一个元素是query image中关键点的索引,
第二个元素是train(即reference) image中关键点的索引"""
# print(m[0].queryIdx, m[0].trainIdx)
good_matches.append((m[0].queryIdx,m[0].trainIdx))
# print((good_matches))
#7.将可靠的匹配转换数据类型
"""将在变量kpsA中的所有关键点(Keypoint)的位置(即坐标)
提取出来,并存储为一个浮点数类型的Numpy数组kps1"""
kps1=np.float32([kp.pt for kp in kpsA]) #求出所有关键点的x,y坐标
kps2 = np.float32([kp.pt for kp in kpsB])
# print(kps1)
# print(kps1[592,:])
kps1 = np.float32([kps1[a[0]] for a in good_matches])#求出可靠匹配的x,y坐标
kps2 = np.float32([kps2[a[1]] for a in good_matches])
# print(kps1[0])
# print(kps2[0])
# print(kps1)
# print(kps2)
#8.求解转换矩阵
M,status=cv2.findHomography(kps2,kps1,cv2.RANSAC,4.0)
# print(M)
#9.拼接图像
"""这行代码使用OpenCV库中的函数cv2.warpPerspective()对图像imageB进行透视变换,
使其与另一幅图像imageA对齐,并将结果保存在result变量中。M是一个3×3的变换矩阵,用于
指定图像的变换方式。这个变换可以将imageB上每个点的像素位置映射到result图像的新位置,
从而实现图像的拼接或其他形式的处理。具体地,这里将result图像的宽度设置为imageA和imageB
的宽度之和,高度设置为imageB的高度,以确保两张图像都可以完整地显示在result中。"""
result=cv2.warpPerspective(imageB,M,(imageA.shape[1]+imageB.shape[1],imageB.shape[0]))
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
"""这段代码是将名为imageA的图像复制到名为result的图像的左上角位置。具体来说,它将result
的第一行到imageA的行数和第一列到imageA的列数的区域(即左上角)替换为imageA的像素值。这通
常用于图像拼接、覆盖或合并操作中。"""
result[0:imageA.shape[0],0:imageA.shape[1]]=imageA
cv2.imshow('Result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()