基于特征匹配实现任意张图像上下左右方向的融合拼接

功能介绍:

        本代码利用图像特征检测,实现图像拼接,为了进一步提高图像拼接适用范围和鲁棒性,对代码一再改造,逐步实现上下左右拼接、无需自定义图像顺序(自左向右、自下向上)拼接。目前需要相邻图像是待拼接的图像,后面将进一步优化代码,实现乱序图像的拼接,想法是基于特征检测和匹配进行图像筛选,再进行图像拼接。拼接后的图像黑边可以通过腐蚀变换去掉,如果有时间完成再更新同步。
        本代码粗制烂造,可能存在些许问题,伙伴们可以参考,欢迎评论批评指正。

直接上代码:

import cv2
import numpy as np
from imutils import paths
import threading

# 定义加权融合中权重计算
def calWeight(d, k):
    '''
    :param d: 融合重叠部分直径
    :param k: 融合计算权重参数
    :return:
    '''
    x = np.arange(-d / 2, d / 2)
    y = 1 / (1 + np.exp(-k * x))
    return y


# 用透视矩阵(Homography)对图像进行变换,返回拼接图像
def Fusion(image_right, image_left, Homography):
    row1, col1 = image_right.shape[:2]    # 右图,下图——h, w
    row2, col2 = image_left.shape[:2]     # 左图,上图——h, w
    if abs(Homography[0, 2]) > abs(Homography[1, 2]):  # 左右拼接
        # cv.warpPerspective():透视变换函数,用于解决cv2.warpAffine()不能处理视场和图像不平行的问题
        # 作用:就是对图像进行透视变换,可保持直线不变形,但是平行线可能不再平行
        result = cv2.warpPerspective(image_right, Homography, (col1 + col2, row1))
        """
        cv.imwrite("./result.jpg", result)
        gray_image = cv.cvtColor(result, cv.COLOR_BGR2GRAY)
        # 找到所有零像素/非零像素的坐标
        zero_coords = np.argwhere(gray_image == 0)
        non_zero_coords = np.argwhere(gray_image > 0)
        # 找到左侧零像素中 X 轴的最大值
        left_zero_coords = zero_coords[zero_coords[:, 1] < gray_image.shape[1] // 2]  # 左侧坐标筛选
        max_x_coord = np.max(left_zero_coords[:, 1])
        # 找到 x 轴最小坐标
        min_x_coord = np.min(non_zero_coords[:, 1])
        print("图像左侧零像素坐标的 X 轴最大值为:", max_x_coord)
        print("图像中非零像素的 x 轴最小坐标为:", min_x_coord)
        d = max_x_coord - min_x_coord
        """
        cv2.imshow("picture_4", result)  # 扭曲变换后的右图
        cv2.waitKey(0)
        cv2.destroyAllWindows()
        # 将左图加入到变换后的右图像的左端即获得最终图像
        d = int(col1 / 5)        # 可以自定义加权融合区域
        w = calWeight(d, 0.05)
        w_expand = np.tile(w, (row2, 1))
        w_expand = np.repeat(w_expand[:, :, np.newaxis], 3, axis=2)
        result[:, 0:col2 - d] = image_left[:, 0:col2 - d]
        result[:, col2 - d:col2] = (1 - w_expand) * image_left[:, col2 - d:col2] + w_expand * result[:, col2 - d:col2]
    else:  # 上下拼接
        result = cv2.warpPerspective(image_right, Homography, (col1, row1 + row2))
        """
        gray_image = cv.cvtColor(result, cv.COLOR_BGR2GRAY)
        # 找到所有零像素/非零像素的坐标
        zero_coords = np.argwhere(gray_image == 0)
        non_zero_coords = np.argwhere(gray_image > 0)
        # 找到上侧零像素中 X 轴的最大值
        top_zero_coords = zero_coords[zero_coords[:, 0] < gray_image.shape[1] // 2]  # 左侧坐标筛选
        max_y_coord = np.max(top_zero_coords[:, 0])
        # 找到 y 轴最小坐标
        min_y_coord = np.min(non_zero_coords[:, 0])
        print("图像上侧零像素坐标的 Y 轴最大值为:", max_y_coord)
        print("图像中非零像素的 y 轴最小坐标为:", min_y_coord)
        d = max_y_coord - min_y_coord
        """
        cv2.imshow("picture_4", result)  # 扭曲变换后的右图
        cv2.waitKey(0)
        cv2.destroyAllWindows()
        # 将左图加入到变换后的右图像的左端即获得最终图像
        d = int(row1 / 5)     # 可以自定义加权融合区域
        w = calWeight(d, 0.05)
        w_expand = np.tile(w, (col2, 1))
        w_expand = np.repeat(w_expand[:, :, np.newaxis], 3, axis=2)
        w_expand = np.transpose(w_expand, (1, 0, 2))
        result[0:row1 - d, :] = image_left[0:row1 - d, 0:]
        result[row1 - d:row1, :] = (1 - w_expand) * image_left[row1 - d:row1, :] + w_expand * result[row1 - d:row1, :]
    return result


# 想写成回调函数用于图像拼接,未使用
def process_item(i, result):
    src_pts = np.float32([kp_list[i][m.queryIdx].pt for m in good_matches_list[i]]).reshape(-1, 1, 2)
    dst_pts = np.float32([kp_list[i + 1][m.trainIdx].pt for m in good_matches_list[i]]).reshape(-1, 1, 2)
    M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
    result = cv2.warpPerspective(result, M, (images[i + 1].shape[1] + result.shape[1], result.shape[0]))
    result[:, images[i].shape[1]:] = images[i + 1]
    return result


if __name__ == '__main__':
    # imagePaths = sorted(list(paths.list_images("images/newimages")))                          # 图像顺序依次从右向左
    imagePaths = sorted(list(paths.list_images("images/newimages")), reverse=True)     # 验证图像顺序不影响拼接
    print(imagePaths)
    num_images = len(imagePaths)

    images = []
    for imagePath in imagePaths:
        image = cv2.imread(imagePath)
        images.append(image)

    # 特征检测
    kp_list = []
    des_list = []
    sift = cv2.SIFT_create()
    for image in images:
        kp, des = sift.detectAndCompute(image, None)
        kp_list.append(kp)
        des_list.append(des)
    # 特征匹配
    matcher = cv2.DescriptorMatcher_create(cv2.DescriptorMatcher_FLANNBASED)
    matches_list = []
    for i in range(num_images - 1):
        matches = matcher.knnMatch(des_list[i], des_list[i+1], k=2)
        matches_list.append(matches)
    # 特征筛选
    good_matches_list = []
    for matches in matches_list:
        good_matches = []
        for m, n in matches:
            if m.distance < 0.7 * n.distance:
                good_matches.append(m)
        good_matches_list.append(good_matches)


    result = images[0]
    for i in range(num_images - 1):
        src_pts = np.float32([kp_list[i][m.queryIdx].pt for m in good_matches_list[i]]).reshape(-1, 1, 2)
        dst_pts = np.float32([kp_list[i + 1][m.trainIdx].pt for m in good_matches_list[i]]).reshape(-1, 1, 2)
        M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
        # print(M)
        if M[0, 2] < 0 or M[1, 2] < 0:   # 根据平移元素的正负值,判断图像的顺序位置
            M, mask = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC, 5.0)
            # print(M)
            result = Fusion(images[i + 1], result, M)
        else:
            result = Fusion(result, images[i + 1], M)
        cv2.imshow("Stitched", result)
        cv2.waitKey(0)
        """
        result = cv2.warpPerspective(result, M, (images[i + 1].shape[1] + result.shape[1], result.shape[0]))
        cv2.imshow("result", result)
        cv2.waitKey(0)
        # result[:, result.shape[1] - images[i + 1].shape[1]:] = images[i + 1]
        result[:, :images[i + 1].shape[1]] = images[i + 1]
        print(result.shape)
        cv2.imshow("Stitched", result)
        cv2.waitKey(0)
        """

    cv2.imwrite('result.jpg', result)

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一种实现图像特征提取、特征匹配图像融合图像拼接的流程: 1. 读取多图像并转换为灰度图像。 2. 对每图像提取关键点和特征描述符,可以使用SIFT、SURF、ORB等算法。 3. 对所有图像中的关键点进行特征匹配。可以使用FLANN或BFMatcher等算法。 4. 根据特征匹配结果计算单应性矩阵,可以使用RANSAC算法进行鲁棒估计。 5. 对所有图像进行透视变换,将它们对齐到同一平面。 6. 图像融合,可以使用泊松融合等算法,将各图像拼接起来形成一全景图像。 7. 可以对全景图像进行进一步的处理,比如去除重叠区域的伪影等。 代码实现的话,可以使用OpenCV图像处理库。以下是伪代码示例: ```python import cv2 # 读取多图像 img1 = cv2.imread('image1.jpg') img2 = cv2.imread('image2.jpg') img3 = cv2.imread('image3.jpg') # 转换为灰度图像 gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) gray3 = cv2.cvtColor(img3, cv2.COLOR_BGR2GRAY) # 提取特征点和描述符 sift = cv2.xfeatures2d.SIFT_create() kp1, des1 = sift.detectAndCompute(gray1, None) kp2, des2 = sift.detectAndCompute(gray2, None) kp3, des3 = sift.detectAndCompute(gray3, None) # 特征匹配 matcher = cv2.BFMatcher() matches1_2 = matcher.match(des1, des2) matches2_3 = matcher.match(des2, des3) # 计算单应性矩阵 src_pts1 = np.float32([kp1[m.queryIdx].pt for m in matches1_2]).reshape(-1, 1, 2) dst_pts1 = np.float32([kp2[m.trainIdx].pt for m in matches1_2]).reshape(-1, 1, 2) H1, _ = cv2.findHomography(src_pts1, dst_pts1, cv2.RANSAC, 5.0) src_pts2 = np.float32([kp2[m.queryIdx].pt for m in matches2_3]).reshape(-1, 1, 2) dst_pts2 = np.float32([kp3[m.trainIdx].pt for m in matches2_3]).reshape(-1, 1, 2) H2, _ = cv2.findHomography(src_pts2, dst_pts2, cv2.RANSAC, 5.0) # 透视变换 result1 = cv2.warpPerspective(img1, H1, (img1.shape[1], img1.shape[0])) result2 = cv2.warpPerspective(img2, H1.dot(H2), (img2.shape[1], img2.shape[0])) # 图像融合 mask = np.zeros_like(result1) mask[:, :img1.shape[1]//2, :] = 1 result = cv2.seamlessClone(result1, result2, mask, (img1.shape[1]//2, img1.shape[0]//2), cv2.NORMAL_CLONE) # 显示结果 cv2.imshow('result', result) cv2.waitKey(0) cv2.destroyAllWindows() ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值