如何实现两张图拼接为一张图

16人浏览 / 0人评论 / 添加收藏

OpenCV 中的图像拼接,简单说就是把多张有重叠区域的小图合成一张宽视角的大图(全景图)。实现上主要有两种方式:

1、高级 API:cv2.Stitcher 类,一行调用,自动完成所有步骤。

2、底层手动实现:分步完成特征提取、匹配、单应变换和融合,适合深入理解与精细控制。

下面从这两方面展开,并给出可直接运行的代码示例。

一、使用 Stitcher 高级 API(推荐)
OpenCV 自带 cv2.Stitcher 类,封装了完整的拼接流水线。使用时只需传入图像列表,即可得到全景图。

实例代码如下:

import cv2
import os

# 1. 读取多张图像(按拍摄顺序从左到右)
img_paths = ['img1.jpg', 'img2.jpg', 'img3.jpg']
imgs = []
for path in img_paths:
   img = cv2.imread(path)
   if img is not None:
       imgs.append(img)

# 2. 创建 Stitcher 对象,并拼接
#    mode: cv2.Stitcher_PANORAMA(全景) 或 cv2.Stitcher_SCANS(扫描文档)
stitcher = cv2.Stitcher.create(cv2.Stitcher_PANORAMA)
status, stitched = stitcher.stitch(imgs)

if status == cv2.Stitcher_OK:
   cv2.imwrite('panorama.jpg', stitched)
   print('拼接成功,保存为 panorama.jpg')
else:
   print(f'拼接失败,错误码: {status}')

错误码含义:

cv2.Stitcher_OK → 成功

cv2.Stitcher_ERR_NEED_MORE_IMGS → 图像不够

cv2.Stitcher_ERR_HOMOGRAPHY_EST_FAIL → 无法计算单应矩阵,重叠不足或特征太少

cv2.Stitcher_ERR_CAMERA_PARAMS_ADJUST_FAIL → 相机参数调整失败

注意:OpenCV 的 Stitcher 内部默认使用 ORB 特征(或按需切换),如果你的图像特征特别稀疏或纹理重复严重,可能会失败。此时需要手动降级或调参。

二、手动分步实现(理解原理)
手动拼接的经典流水线:特征检测 → 特征匹配 → 计算单应矩阵 → 图像变换 → 融合。

图像拼接的基本流程可以分为以下几个步骤:

图像读取:读取需要拼接的图像。
特征点检测:在每张图像中检测出关键点(特征点)。
特征点匹配:在不同图像之间匹配这些特征点。
计算变换矩阵:根据匹配的特征点计算图像之间的变换矩阵。
图像融合:将图像按照变换矩阵进行拼接,并进行融合处理以消除拼接痕迹。

import cv2
import numpy as np

# 1. 加载图像
image1 = cv2.imread("./images/pinjie2.png")
image2 = cv2.imread("./images/pinjie1.png")
image1 = cv2.resize(image1, (640, 480))
image2 = cv2.resize(image2, (640, 480))

# 2. 转换为灰度图
gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)

# 3. 特征点检测
sift = cv2.SIFT_create()
keypoints1, descriptors1 = sift.detectAndCompute(gray1, None)
keypoints2, descriptors2 = sift.detectAndCompute(gray2, None)

# 4. 特征点匹配
matcher = cv2.BFMatcher()
matches = matcher.knnMatch(descriptors1, descriptors2, k=2)

# 5. 筛选匹配点
good_matches = []
for m, n in matches:
   if m.distance < 0.75 * n.distance:
       good_matches.append(m)

# 6. 计算单应性矩阵
if len(good_matches) > 10:
   src_pts = np.float32([keypoints1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
   dst_pts = np.float32([keypoints2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
   H, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
else:
   print("Not enough matches found.")
   exit()

# 7. 图像变换
height1, width1 = image1.shape[:2]
height2, width2 = image2.shape[:2]
warped_image = cv2.warpPerspective(image1, H, (width1 + width2, height1))

# 8. 图像拼接
warped_image[0:height2, 0:width2] = image2

# 9. 显示结果
cv2.imshow("Stitched Image", warped_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

拼接前的两张图片,有部分重叠部分,且有倾斜角度:

拼接后图片:

全部评论