OpenCV中实现两张照片拼接详解和代码示例(C++代码和Python代码)
步骤方法特征提取SIFT / ORB(推荐 SIFT)特征匹配配准变换拼接直接覆盖或渐变融合。
·
OpenCV 提供了完整的图像拼接(Image Stitching)工具链,可以实现将两张图像拼接为全景图。下面分别介绍 C++ 和 Python 实现方式。
一、拼接流程步骤
拼接两张照片通常包括以下步骤:
- 特征检测(如 SIFT, ORB)
- 特征匹配(BFMatcher / FLANN)
- 匹配筛选(如 Lowe’s Ratio Test)
- 计算单应矩阵 H(RANSAC)
- 图像透视变换(warp)
- 图像合并(拼接)
二、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 无内建,需手动实现 |
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)