SIFT算法:图像拼接的"智能胶水"
下面是完整的SIFT图像拼接Python代码,包含了羽化过渡融合优化。你可以复制这段代码,保存为sift_stitching.py,然后运行它来体验图像拼接的魔力!
💡 提示:确保你已经安装了必要的库:pip install opencv-python numpy matplotlib
import cv2
import numpy as np
import matplotlib.pyplot as plt
def feather_blend(image1, image2_transformed, width, height):
# 创建羽化掩码
# 计算重叠区域宽度
overlap_width = image2_transformed.shape[1] - width
if overlap_width <= 0:
return image2_transformed
# 创建线性渐变掩码
mask = np.ones((height, width + overlap_width, 3), dtype=np.float32)
for col in range(width, width + overlap_width):
alpha = (col - width) / overlap_width
mask[:, col, :] = 1 - alpha
# 应用掩码
image2_transformed[0:height, 0:width] = image1 * mask[0:height, 0:width] + \
image2_transformed[0:height, 0:width] * (1 - mask[0:height, 0:width])
return image2_transformed
def sift_image_stitching(image_path1, image_path2, output_path='./images/panorama_result.jpg'):
# 读取两张待拼接的图像
print(f'读取图像: {image_path1} 和 {image_path2}')
image1 = cv2.imread(image_path1)
image2 = cv2.imread(image_path2)
if image1 is None or image2 is None:
print('错误:无法读取图像,请检查文件路径')
return None
# 转换为RGB格式(因为OpenCV默认读取的是BGR格式)
image1_rgb = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)
image2_rgb = cv2.cvtColor(image2, cv2.COLOR_BGR2RGB)
# 初始化SIFT检测器
print('初始化SIFT检测器...')
sift = cv2.SIFT_create()
# 检测SIFT特征点和计算描述符
print('检测SIFT特征点...')
kp1, des1 = sift.detectAndCompute(image1, None)
kp2, des2 = sift.detectAndCompute(image2, None)
# 使用FLANN匹配器匹配特征点
print('匹配特征点...')
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
# 筛选优质匹配点(根据Lowe的比率测试)
good_matches = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good_matches.append(m)
print(f'找到 {len(good_matches)} 个优质匹配点')
# 绘制匹配点并保存
matched_image = cv2.drawMatches(image1_rgb, kp1, image2_rgb, kp2, good_matches, None, flags=2)
matched_image_path = './images/sift_matching_result.png'
cv2.imwrite(matched_image_path, cv2.cvtColor(matched_image, cv2.COLOR_RGB2BGR))
print(f'匹配点图像已保存至: {matched_image_path}')
# 获取匹配点的坐标
points1 = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
points2 = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
# 计算单应性矩阵(homography matrix)使用RANSAC算法
print('计算单应性矩阵...')
H, mask = cv2.findHomography(points2, points1, cv2.RANSAC, 5.0)
# 使用单应性矩阵变换图像2
height, width = image1.shape[:2]
# 计算输出图像的大小,确保能够容纳变换后的图像2和原图像1
stitched_width = width + image2.shape[1]
stitched_height = max(height, image2.shape[0])
print('变换图像...')
stitched_image = cv2.warpPerspective(image2, H, (stitched_width, stitched_height))
# 使用羽化过渡融合
print('应用羽化过渡融合...')
stitched_image = feather_blend(image1, stitched_image, width, height)
# 优化:裁剪黑边
print('裁剪黑边...')
# 寻找非黑色区域的边界
gray = cv2.cvtColor(stitched_image, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
x, y, w, h = cv2.boundingRect(contours[0])
stitched_image_cropped = stitched_image[y:y+h, x:x+w]
# 转换为RGB格式以便显示
stitched_image_rgb = cv2.cvtColor(stitched_image_cropped, cv2.COLOR_BGR2RGB)
# 保存拼接后的图像
cv2.imwrite(output_path, cv2.cvtColor(stitched_image_cropped, cv2.COLOR_RGB2BGR))
print(f'拼接图像已保存至: {output_path}')
# 显示结果
plt.figure(figsize=(18, 10))
plt.subplot(221)
plt.imshow(image1_rgb)
plt.title('图像1')
plt.axis('off')
plt.subplot(222)
plt.imshow(image2_rgb)
plt.title('图像2')
plt.axis('off')
plt.subplot(223)
plt.imshow(matched_image)
plt.title('特征点匹配')
plt.axis('off')
plt.subplot(224)
plt.imshow(stitched_image_rgb)
plt.title('拼接结果')
plt.axis('off')
plt.tight_layout()
plt.show()
return stitched_image_rgb
if __name__ == '__main__':
# 输入图像路径
image_path1 = './images/image_stitching_1.jpg'
image_path2 = './images/image_stitching_2.jpg'
# 执行图像拼接
print('开始图像拼接...')
result = sift_image_stitching(image_path1, image_path2)
if result is not None:
print('图像拼接完成!')
else:
print('图像拼接失败。')
🔍 小观察:运行代码时,注意观察控制台输出的信息。它会告诉你代码执行到了哪一步,找到了多少个优质匹配点,以及图像保存的路径。这些信息可以帮助你了解代码的执行状态,以及排查可能出现的问题。