2. 编程实现多张图片的自动拼接

姓名:许展风 学号:3210100658

电子邮箱:zhanfeng_xu@outlook.com 联系电话:15224131655

老师:潘纲老师 报告日期:2023年12月4日

2.1. 一、功能简述及运行说明

2.1.1. 1.1 功能简述

对输入的多张彩色图像,通过算法实现多张图片的拼接。

2.1.2. 1.2 运行说明

程序运行后,第一步根据提示输入待拼接的图片数量,第二步依次输入图片路径,第三步输入输出图片名称。回车后程序运行,并输出最后一步图片拼接的中间过程,以及最终所有图片的拼接结果。

2.2. 二、开发与运行环境

编程语言:python 3.10.6

编程环境:VScode+Jupyter Notebook

运行环境:Windows

2.3. 三、算法原理

2.3.1. 3.1 算法流程图

graph LR
    A[彩色图片1]
    B[彩色图片2]
    A --> C[特征点提取]
    B --> C
    C --> D[图像配准]
    D --> E[计算透视变换矩阵H]
    E --> F[图像变形]
    F --> G[图像融合]

2.3.2. 3.2 具体原理介绍

2.3.2.1. 1. 特征点提取

通过特殊算法检测输入图像的特征点,这些特征可能包括某些方向的极值点,它们不受图像的尺度缩放、亮度变化所影响,是一个稳定的特征。

2.3.2.2. 2. 特征点匹配

利用图像的特征点建立图像特征点之间的对应,匹配两张图片相同的部分。

2.3.2.3. 3. 计算透视变换矩阵H

利用匹配的特征点,建立图像之间的几何对应关系,使它们可以在一个共同参照系中进行变换、比较和分析。

2.3.2.4. 4. 图像变形

利用变换矩阵H对其中一张图像作透视变换,使得两张图像变为同一参照系。

2.3.2.5. 5. 图像融合

变形后的图像可以直接拼接,也可以通过改变边界附近图像的灰度,使得图像在缝隙处平滑过渡。

2.4. 四、具体实现

2.4.1. 4.1 特征点提取

    # sift特征点计算
    sift = cv2.SIFT_create()
    kp1, des1 = sift.detectAndCompute(imgGray1, None)
    kp2, des2 = sift.detectAndCompute(imgGray2, None)

使用SIFT算法提取图片特征点,SIFT的全称是Scale Invariant Feature Transform,尺度不变特征变换,SIFT特征对旋转、尺度缩放、亮度变化等保持不变性,是一种非常稳定的局部特征。[2]

2.4.2. 4.2 特征点匹配

    # 对应特征点配对
    bf = cv2.BFMatcher(cv2.NORM_L2)
    matches = bf.knnMatch(des1, des2, k=2)

    goodMatch = []  # 配对点集合,用于画图
    good = []  # 配对点坐标序号集合, 用于后续求变换矩阵
    for m, n in matches:
        if m.distance < 0.75 * n.distance:
            goodMatch.append(m)
            good.append((m.trainIdx, m.queryIdx))

cv2.BFMatcher是openCV库中的一种匹配器,Brute-Force蛮力匹配器,该匹配器将两组中的所有特征点进行匹配,返回距离最近的匹配项。再将蛮力匹配得到的结果进行筛选,当最近距离与次近距离的比值小于ratio值时的配对保留。存储保留配对的索引值,便于后续索引。

另一种匹配器为FLANN匹配器,它利用最近邻搜索的优化算法,可以在大型数据集中拥有比BF匹配器更快的运算速度,在本实验中没有必要使用。

2.4.3. 4.3 计算透视变换矩阵H

    # 求变换矩阵
    pts1 = np.float32([kp1[i].pt for (_, i) in good])
    pts2 = np.float32([kp2[i].pt for (i, _) in good])
    H, status = cv2.findHomography(pts1, pts2, cv2.RANSAC, 4.0)

利用保留匹配点的索引提取匹配点的坐标,利用cv2.findHomography函数计算单应性矩阵,使用RANSAC方法。

2.4.4. 4.4 图像变形与融合

    # 用变换矩阵对imga作透视变换
    tranRes = cv2.warpPerspective(img1, H, (img1.shape[1], img1.shape[0]))
    # 透视变换后的图片拼接上imgb
    tranRes[th:imgb.shape[0] + th, tw:imgb.shape[1] + tw] = imgb

利用计算得到的矩阵H作为透视变换矩阵,使用透视变换函数对图片1作透视变换。

在算法实现过程中,可以发现对图片1作透视变换后,图片1会发生包括平移在内的变换,其结果是图片1中与图片2特征对应的位置得到重合,此时直接将图片2覆盖在图片1,即可直接得到混合图片。当使用Yosemite图片作为样本时,会发现实际上图片1会变换后左移,而移动出画布的范围造成变换后信息的缺失。如下图中绿色的区域被丢失。[3]为了解决这个问题,可以扩大初始图片的画布,

在这里插入图片描述

 # 由于透视变换将imga图片进行平移、旋转等等操作,所以需要扩大图像画布,避免信息丢失
    tw = np.int16(np.max([imga.shape[1], imgb.shape[1]]))  # 确定宽度平移量
    th = np.int16(np.max([imga.shape[0], imgb.shape[0]]))  # 确定高度平移量

    M = np.float32([[1, 0, tw], [0, 1, th]])  # 构造平移变换矩阵
    img1 = cv2.warpAffine(imga, M,
                          (imga.shape[1] + 2 * tw,
                           imga.shape[0] + 2 * th))  # 变换后,保证了图像的最大尺度变换下信息不丢失
    img2 = cv2.warpAffine(imgb, M,
                          (imga.shape[1] + 2 * tw,
                           imga.shape[0] + 2 * th))  # 对imgb作同样处理,便于后续图片直接原位置拼接

因此在程序中对输入图片使用平移变换,扩大画布的同时平移图片至中央,此时考虑到图片最大尺度的透视变换,只要两个方向都留足原本图像的大小,就能保证图像不丢失。此时图片混合方法需要将原大小的图片2放入变换后图片1的中央。此时得到的最终结果将保留大片区域的黑边,因为扩大了画布,因此可以再通过截取的方法出去黑边。算法如下:

def removeBlack(blackim):
    '''去除图像黑边

    :blackim: 输入图像
    :res_image: 输出图像
    '''
    blackimgGray = cv2.cvtColor(blackim, cv2.COLOR_BGR2GRAY)  # 转为灰度图像
    edges_y, edges_x = np.where(blackimgGray != 0)  # 求非黑的有效区域
    bottom = min(edges_y)
    top = max(edges_y)
    height = top - bottom  # 求有效区域的最大高度

    left = min(edges_x)
    right = max(edges_x)
    width = right - left  # 求有效区域的最小宽度

    res_image = blackim[bottom:bottom + height, left:left + width]  # 裁剪出有效区域
    return res_image

2.5. 五、实验结果与分析

2.5.1. 5.1 特征点提取结果

使用了扩大画布的方法后,提取特征点时计算会收到一定的影响,但是可以看到大部分的特征点是能提取到目标图片内部的,因此对最终结果影响较小。

2.5.2. 5.2 特征点匹配结果

图中显示了前100个匹配点结果,依然能看到有部分的特征点匹配收到影响,但是由于匹配到的有效配对点有很多,依然对最终的变换矩阵影响较小。

2.5.3. 5.3 图片变换与混合

图片1 变换后与图片2叠加,去黑边处理后,可以发现相同的部分能够充分重叠,只是分界处有灰度级的区别,因此有分界线痕迹。

2.5.3.1. 5.4 多张图片拼接

最终得到多张图片拼接结果,可以发现在位置上能够得到充分混合,而在灰度级上还有较明显的区别。

2.5.4. 5.5 使用个人图片效果

待拼接的5张图片原图如上,是在西教用手机拍摄的五张照片。拼接的效果如下。

可以看到拼接结果是正确的,通过墙与地面的接缝的笔直情况判断出拼接的效果是好的,它以第3张图片视角为主视角,将其他图片实现了变换拼接。由于是近景,拍摄时视角的转向比较大,所以拼接时变换的程度也大,对应视角的剧烈变化。同时可以发现,由于手机摄影亮度自动调节的关系,第一张图片与其他图片有明显不一样的亮度,从拼接结果也能明显的看出来。

2.6. 六、结论与心得体会

2.6.1. 结论

该程序能够基本完成图像拼接的任务。

在两方面有一定的瑕疵,其一是扩大画布避免信息丢失的方法,对特征点的检测和匹配存在一定的影响。且处理大图像时将消耗大量的空间存储图像信息。如果对于输入的图像有先验的透视变换的尺度和方向的估计,可以特定的对画布进行合适大小的扩大,一定程度上节省空间。

另一方面是由于图像噪声、光照曝光度、模型匹配误差等因素,直接的图像拼接后,重叠区域的拼接处会出现比较明显的边痕迹[4],这个问题一般通过multi-band bleing算法来解决。采用的方法是直接对带拼接的两个图片进行拉普拉斯金字塔分解,后一半对前一半进行融合。由于时间及复杂度问题没能完成。

2.6.2. 心得体会

  1. 图像拼接原理涉及到的算法很多很复杂,例如包括sirf特征点提取算法、单应性矩阵求解算法等等,在这个程序里都直接用openCV中的函数实现了。但是对于图像拼接的一般流程有了充分的熟悉与掌握。

  2. 实验时最大困难是变换后信息丢失的问题,其实解决这个问题角度考虑了三个,一是从一开始改变画布,也就是程序最终采用的方法;二是在透视变换时增大显示范围,但是这个操作是直接用一个函数实现的,难以直接调整;三是改变矩阵H,但不清楚改变H后,对于重叠部分是否还重叠的影响,也没有采用。

  3. 编程时参考了较多CSDN上的文章与程序,但没有对程序完全照搬,也没有任何程序能够直接搬进来就可以用,都得在理解的基础上进行修改调试,有些文献参考的是原理介绍,有些参考了问题解决的思路,编程的方法与思路。

2.7. 七、参考文献

[1], 纸箱里的猫咪, Opencv实战——图像拼接, [OL], CSDN, 2022-06-06, [2023-12-04], https://blog.csdn.net/Thousand_drive/article/details/125084810

[2], 阿飞大魔王, 图像特征点提取(SIFT,SURF,ORB), [OL], CSDN, 2019-03-26, [2023-12-04], https://blog.csdn.net/lucifer_24/article/details/88823448

[3], 牛牛牛叶, 解决透视变换后图片信息丢失的问题,附程序, [OL], CSDN,2020-08-13,[2023-12-04], https://blog.csdn.net/xiaoyeer666/article/details/107973505

[4], LiaoNanan,Python计算机视觉(三)—— 全景图像拼接, [OL], CSDN,2022-04-27,[2023-12-04], https://blog.csdn.net/LIAO_0312/article/details/124460671