核心思路:
- 读取匹配点:使用匹配点信息计算图像之间的变换矩阵。
- 图像变换:通过变换矩阵将图像对齐。
- 图像合并:将对齐后的图像拼接到最终大图中。
import cv2
import numpy as npdef stitch_images(image_list, keypoints_list):"""拼接图像的函数。:param image_list: 包含所有图像的列表:param keypoints_list: 每两个相邻图像的匹配点信息,格式为:[((src_pts_1, dst_pts_1), (src_pts_2, dst_pts_2), ...)]其中 src_pts 和 dst_pts 分别是两图的匹配点。:return: 拼接完成的大图。"""# 初始化拼接结果,先以第一张图像为基础stitched_image = image_list[0].copy()h, w = stitched_image.shape[:2]# 初始化最终大图的画布canvas_height = h * 2canvas_width = w * len(image_list)final_canvas = np.zeros((canvas_height, canvas_width, 3), dtype=np.uint8)# 将第一张图像放置在画布中央offset = (canvas_width // 4, canvas_height // 4)final_canvas[offset[1]:offset[1] + h, offset[0]:offset[0] + w] = stitched_image# 当前图像的变换矩阵current_transform = np.eye(3)# 遍历相邻图像,依次拼接for i in range(len(keypoints_list)):src_pts, dst_pts = keypoints_list[i]# 计算变换矩阵(透视变换或仿射变换)if len(src_pts) >= 4:transform_matrix, _ = cv2.findHomography(np.array(src_pts), np.array(dst_pts), cv2.RANSAC)else:transform_matrix = cv2.estimateAffine2D(np.array(src_pts), np.array(dst_pts))[0]transform_matrix = np.vstack([transform_matrix, [0, 0, 1]])# 更新当前变换矩阵current_transform = current_transform @ transform_matrix# 对下一张图像进行变换next_image = image_list[i + 1]warped_image = cv2.warpPerspective(next_image, current_transform, (canvas_width, canvas_height))# 将变换后的图像叠加到画布mask = (warped_image > 0).astype(np.uint8) # 避免覆盖已有内容final_canvas = cv2.add(final_canvas, warped_image * mask)return final_canvas# 示例用法
if __name__ == "__main__":# 示例图像和匹配点images = [cv2.imread(f"image_{i}.jpg") for i in range(4)] # 替换为实际的图像路径keypoints = [([(50, 50), (200, 50), (50, 200)], [(10, 10), (160, 10), (10, 160)]),([(10, 10), (160, 10), (10, 160)], [(20, 20), (180, 20), (20, 180)]),# 添加更多相邻图像的匹配点]result = stitch_images(images, keypoints)cv2.imwrite("stitched_image.jpg", result)cv2.imshow("Stitched Image", result)cv2.waitKey(0)cv2.destroyAllWindows()
1. 计算变换矩阵的两种方法
在图像拼接中,使用匹配点计算变换矩阵是关键步骤。常用的方法有透视变换(Homography)和仿射变换(Affine Transformation)。选择哪种变换方法,取决于匹配点的数量和图像内容的几何关系。
1.1 透视变换(Homography)
函数:cv2.findHomography()
适用场景:两张图像为投影变换;匹配点数量不少于4;能处理平面旋转、缩放、平移以及透视失真。
公式:
其中H为3×3矩阵
代码解释:
transform_matrix, _ = cv2.findHomography(np.array(src_pts), np.array(dst_pts), cv2.RANSAC)
src_pts
: 源图像中的点集合(形状为 n×2)。dst_pts
: 目标图像中的点集合。cv2.RANSAC
: 使用随机抽样一致性算法来剔除错误的匹配点。transform_matrix
: 计算出的3×3 变换矩阵。
优点
- 能处理复杂的透视变换关系。
缺点
- 需要至少 4 个匹配点,匹配点数量较少时可能会不稳定。
1.2 仿射变换
函数:cv2.estimateAffine2D
适用场景:
- 当两张图像之间的关系是仿射变换。
- 匹配点数量不少于 3。
- 仿射变换仅支持缩放、旋转、平移,不支持透视失真。
公式:
其中 A 是 2×2 的旋转缩放矩阵,t 是平移向量。
代码解释:
transform_matrix = cv2.estimateAffine2D(np.array(src_pts), np.array(dst_pts))[0]
src_pts
: 源图像中的点集合。dst_pts
: 目标图像中的点集合。- 返回的矩阵是 2×32 \times 32×3 仿射变换矩阵。
transform_matrix = np.vstack([transform_matrix, [0, 0, 1]])
仿射变换矩阵需要扩展为 3×3 矩阵,方便后续统一处理
2.图像变换
warped_image = cv2.warpPerspective(next_image, current_transform, (canvas_width, canvas_height))
next_image
: 需要变换的源图像。current_transform
: 当前的 3×3变换矩阵。(canvas_width, canvas_height)
: 输出图像的尺寸。