OpenCV 提供了完整的图像拼接(Image Stitching)工具链,可以实现将两张图像拼接为全景图。下面分别介绍 C++ 和 Python 实现方式。


一、拼接流程步骤

拼接两张照片通常包括以下步骤:

  1. 特征检测(如 SIFT, ORB)
  2. 特征匹配(BFMatcher / FLANN)
  3. 匹配筛选(如 Lowe’s Ratio Test)
  4. 计算单应矩阵 H(RANSAC)
  5. 图像透视变换(warp)
  6. 图像合并(拼接)

二、C++ 示例代码(OpenCV)

需要开启 OpenCV 的 xfeatures2d 模块(如 SIFT)。

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <iostream>

using namespace cv;
using namespace cv::xfeatures2d;
using namespace std;

int main() {
    Mat img1 = imread("left.jpg");
    Mat img2 = imread("right.jpg");

    // 1. 检测特征点与描述子
    Ptr<SIFT> detector = SIFT::create();
    vector<KeyPoint> keypoints1, keypoints2;
    Mat descriptors1, descriptors2;
    detector->detectAndCompute(img1, noArray(), keypoints1, descriptors1);
    detector->detectAndCompute(img2, noArray(), keypoints2, descriptors2);

    // 2. 匹配特征
    BFMatcher matcher(NORM_L2);
    vector<vector<DMatch>> knn_matches;
    matcher.knnMatch(descriptors1, descriptors2, knn_matches, 2);

    // 3. 筛选匹配点(Lowe's Ratio Test)
    vector<DMatch> good_matches;
    for (auto& m : knn_matches)
        if (m[0].distance < 0.75 * m[1].distance)
            good_matches.push_back(m[0]);

    // 4. 提取匹配点
    vector<Point2f> pts1, pts2;
    for (auto& m : good_matches) {
        pts1.push_back(keypoints1[m.queryIdx].pt);
        pts2.push_back(keypoints2[m.trainIdx].pt);
    }

    // 5. 计算单应矩阵
    Mat H = findHomography(pts2, pts1, RANSAC);

    // 6. 图像变换与拼接
    Mat result;
    warpPerspective(img2, result, H, Size(img1.cols + img2.cols, img1.rows));
    img1.copyTo(result(Rect(0, 0, img1.cols, img1.rows)));

    imwrite("stitched.jpg", result);
    imshow("Stitched", result);
    waitKey(0);
    return 0;
}

三、Python 示例代码(OpenCV)

import cv2
import numpy as np

# 1. 读取图像
img1 = cv2.imread('left.jpg')
img2 = cv2.imread('right.jpg')

# 2. 检测特征点与描述子
sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)

# 3. 匹配特征点
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)

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

# 5. 提取点
src_pts = np.float32([kp1[m.queryIdx].pt for m in good])
dst_pts = np.float32([kp2[m.trainIdx].pt for m in good])

# 6. 计算单应矩阵
H, mask = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC, 5.0)

# 7. 图像变换与拼接
height, width = img1.shape[:2]
result = cv2.warpPerspective(img2, H, (width + img2.shape[1], height))
result[0:height, 0:width] = img1

cv2.imwrite("stitched.jpg", result)
cv2.imshow("Stitched Image", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

可选项:使用 OpenCV 官方拼接器(高级封装)

Python(OpenCV ≥ 4.0):

stitcher = cv2.Stitcher_create()
(status, stitched) = stitcher.stitch([img1, img2])

C++:

Ptr<Stitcher> stitcher = Stitcher::create();
Stitcher::Status status = stitcher->stitch({img1, img2}, result);

总结

步骤 方法
特征提取 SIFT / ORB(推荐 SIFT)
特征匹配 BFMatcher + ratio test
配准 cv::findHomography(RANSAC)
变换 warpPerspective()
拼接 直接覆盖或渐变融合

扩张补充

为了让两张图像拼接后在接缝处更加自然,可以引入**图像融合(seamless blending)**技术来进行缝隙平滑处理。下面是两种主流融合方式的实现:


图像融合方案一:线性融合(Multi-band Blending 简化版)

原理:使用加权平均的方式,让两幅图像在重叠区域逐渐过渡。

Python 线性融合版本
import cv2
import numpy as np

def blend_images(img1, img2, H):
    # 图像尺寸
    height1, width1 = img1.shape[:2]
    height2, width2 = img2.shape[:2]

    # 变换 img2
    warped_img2 = cv2.warpPerspective(img2, H, (width1 + width2, height1))

    # 创建融合图像
    blended = np.zeros_like(warped_img2)
    blended[0:height1, 0:width1] = img1

    # 融合区域 mask
    mask1 = (blended > 0).astype(np.uint8)
    mask2 = (warped_img2 > 0).astype(np.uint8)

    # 叠加区域
    overlap_mask = (mask1 * mask2) > 0

    alpha = np.linspace(1, 0, num=overlap_mask.shape[1])
    alpha = np.tile(alpha, (overlap_mask.shape[0], 1))

    for c in range(3):  # 对 RGB 三个通道分别融合
        blended[..., c] = warped_img2[..., c] * (1 - alpha) + blended[..., c] * alpha

    # 将剩余区域填充
    mask_combined = (blended == 0)
    blended[mask_combined] = warped_img2[mask_combined]

    return blended

# 示例拼接流程
img1 = cv2.imread("left.jpg")
img2 = cv2.imread("right.jpg")
sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)

# Lowe's ratio test
good = [m[0] for m in matches if len(m) == 2 and m[0].distance < 0.75 * m[1].distance]
src_pts = np.float32([kp1[m.queryIdx].pt for m in good])
dst_pts = np.float32([kp2[m.trainIdx].pt for m in good])
H, _ = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC)

result = blend_images(img1, img2, H)
cv2.imwrite("stitched_blended.jpg", result)
cv2.imshow("Blended Stitch", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

图像融合方案二:Poisson Blending / SeamlessClone(OpenCV 高级融合)

OpenCV 提供了 cv2.seamlessClone(),可以将图像无缝克隆融合到另一张图上,非常适合图像拼接缝合。

示例(SeamlessClone):
# 基于已 warp 变换后的 img2
warped_img2 = cv2.warpPerspective(img2, H, (img1.shape[1] + img2.shape[1], img1.shape[0]))

# 选取 img1 插入中心
center = (img1.shape[1] // 2, img1.shape[0] // 2)
mask = 255 * np.ones_like(img2, dtype=np.uint8)

# 执行无缝克隆
result = cv2.seamlessClone(img2, warped_img2, mask, center, cv2.MIXED_CLONE)

cv2.imshow("Seamless Clone", result)
cv2.waitKey(0)

seamlessClone 适合于图像局部融合或视觉对齐已经较好的场景,不推荐用于整幅图像拼接,建议用于小 patch 处理或 logo 替换。


小结

方法 原理 优点 适用
线性加权融合 重叠区域按权值平均 快速简单 图像对齐良好
SeamlessClone 基于泊松图像融合 结果自然无缝 局部融合、补丁插入
Multi-band blending 多尺度金字塔融合 最平滑自然 OpenCV 无内建,需手动实现

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐