【计算机视觉三】图像拼接

摘要:
使用基于python的opencv中的sift算法检测图像中的特征点。通过knn匹配,每个关键点两个match,即最近邻与次近邻。 采用SIFT作者提出的比较最近邻距离与次近邻距离的SIFT匹配方式来筛选出最近邻远优于次近邻的匹配作为good matches。最后,根据投影映射关系,使用计算出来的单应性矩阵H进行透视变换,再进行拼接。

准备:

import cv2
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

查看图片:

plt.subplot(121)
plt.imshow(cv2.cvtColor(img_left, cv2.COLOR_BGR2RGB))
plt.subplot(122)
plt.imshow(cv2.cvtColor(img_right, cv2.COLOR_BGR2RGB))
plt.show()


 

 注:cv2.cvtColor(imgA, cv2.COLOR_BGR2RGB)的作用是对cv2读取的图片进行通道变换,cv2读取的通道顺序是BGR,而matplotlib.pyplot对应的是RGB。

# 下面是没有进行通道变换的显示结果
plt.subplot(121)
plt.imshow(img_left)
plt.subplot(122)
plt.imshow(img_right)
plt.show()

 第一步:检测关键点

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)

# 可以看下特征点(若果发现几次运行特征点数目不同,要重新读取图片)
def show_points(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    descriptor = cv2.xfeatures2d.SIFT_create()
    kps, features = descriptor.detectAndCompute(image, None)
    print(f"特征点数:{len(kps)}")
    img_left_points = cv2.drawKeypoints(img_left, kps, img_left)
    plt.figure(figsize=(9,9)) 
    plt.imshow(img_left_points)
show_points(img_left)

 第二步:匹配所有特征点

使用knnMatch最近邻匹配:取一幅图像中的一个sift关键点,并找出另一幅图像中最近的前两个关键点。在这两个关键点中,如果最近的距离除以次近的距离得到的ratio少于某个阀值T,则接受这对匹配点。

def match_keypoints(kps_left,kps_right,features_left,features_right,ratio,threshold):
    """
    kpsA,kpsB,featureA,featureB: 两张图的特征点坐标及特征向量
    threshold: 阀值
    
    """
    # 建立暴力匹配器
    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))
    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
img_left = cv2.imread('1.png', 1)
img_right = cv2.imread('2.png', 1)
kps_left, features_left = detect(img_left)
kps_right, features_right = detect(img_right)
matches, H, good = match_keypoints(kps_left,kps_right,features_left,features_right,0.5,0.99)
img = cv2.drawMatchesKnn(img_left,kps_left,img_right,kps_right,good[:30],None,flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
plt.figure(figsize=(20,20))
plt.imshow(img)

print(H) # 单应性矩阵
[[ 3.66077113e-01 -2.46664063e-02  4.23006873e+02]
 [-2.46890577e-01  8.17757134e-01  6.32946430e+01]
 [-8.24224575e-04 -7.01709596e-05  1.00000000e+00]]

 

第三步:变换,拼接

获取左边图像到右边图像的投影映射关系,透视变换将左图象放在相应的位置,将图像拷贝到特定位置完成拼接。

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
vis = drawMatches(img_left, img_right, kps_left, kps_right, matches, H)
plt.xticks([]), plt.yticks([])
plt.imshow(cv2.cvtColor(vis, cv2.COLOR_BGR2RGB))
plt.show()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值