# 编程实现多张图片的自动拼接 姓名:许展风 学号:3210100658 电子邮箱:zhanfeng_xu@outlook.com 联系电话:15224131655 老师:潘纲老师 报告日期:2023年12月4日 ## 一、功能简述及运行说明 ### 1.1 功能简述 对输入的多张彩色图像,通过算法实现多张图片的拼接。 ### 1.2 运行说明 程序运行后,第一步根据提示输入待拼接的图片数量,第二步依次输入图片路径,第三步输入输出图片名称。回车后程序运行,并输出最后一步图片拼接的中间过程,以及最终所有图片的拼接结果。 ## 二、开发与运行环境 编程语言:python 3.10.6 编程环境:VScode+Jupyter Notebook 运行环境:Windows ## 三、算法原理 ### 3.1 算法流程图 ```mermaid graph LR A[彩色图片1] B[彩色图片2] A --> C[特征点提取] B --> C C --> D[图像配准] D --> E[计算透视变换矩阵H] E --> F[图像变形] F --> G[图像融合] ``` ### 3.2 具体原理介绍 #### 1. 特征点提取 通过特殊算法检测输入图像的特征点,这些特征可能包括某些方向的极值点,它们不受图像的尺度缩放、亮度变化所影响,是一个稳定的特征。 #### 2. 特征点匹配 利用图像的特征点建立图像特征点之间的对应,匹配两张图片相同的部分。 #### 3. 计算透视变换矩阵H 利用匹配的特征点,建立图像之间的几何对应关系,使它们可以在一个共同参照系中进行变换、比较和分析。 #### 4. 图像变形 利用变换矩阵H对其中一张图像作透视变换,使得两张图像变为同一参照系。 #### 5. 图像融合 变形后的图像可以直接拼接,也可以通过改变边界附近图像的灰度,使得图像在缝隙处平滑过渡。 ## 四、具体实现 ### 4.1 特征点提取 ```python # 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] ### 4.2 特征点匹配 ```python # 对应特征点配对 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匹配器更快的运算速度,在本实验中没有必要使用。 ### 4.3 计算透视变换矩阵H ```python # 求变换矩阵 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方法。 ### 4.4 图像变形与融合 ```python # 用变换矩阵对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]为了解决这个问题,可以扩大初始图片的画布,  ```python # 由于透视变换将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的中央。此时得到的最终结果将保留大片区域的黑边,因为扩大了画布,因此可以再通过截取的方法出去黑边。算法如下: ```python 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 ``` ## 五、实验结果与分析 ### 5.1 特征点提取结果