OpenCV--图像拼接

发布于:2024-12-19 ⋅ 阅读:(16) ⋅ 点赞:(0)

OpenCV--图像拼接

代码和笔记

import cv2
import numpy as np

"""
图像拼接:
1. 读取图片,灰度化
2. 计算各自的特征点和描述子
3. 匹配特征
4. 计算单应性矩阵
5. 透视变换
6. 创建一个大图,放图两张图
"""

img1 = cv2.imread('./img/ca2.jpeg')
img2 = cv2.imread('./img/cat.jpeg')

# 把两张图的尺寸变为同样大小
img1 = cv2.resize(img1, (640, 480))
img2 = cv2.resize(img2, (640, 480))

# 灰度化处理
gary1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gary2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

# 创建特征检测器
sift = cv2.SIFT_create()

# 计算描述子
kp1, des1 = sift.detectAndCompute(gary1, None)
kp2, des2 = sift.detectAndCompute(gary2, None)


# 创建特征匹配器 为了更准确,这里使用KNN算法匹配器进行相应的筛选,k是KNN的参数
bf = cv2.BFMatcher()
match = bf.knnMatch(des1, des2, k=2)

# 筛选后的集合
goods = []
# 开始筛选
# match返回两个index
for m, n in match:
    # 这个阈值一般为0.7-0.8
    # distance为描述子之间的距离,越小越好
    if m.distance < 0.75 * n.distance:
        goods.append(m)

# 计算单应性矩阵至少需要四个点
if len(goods) >= 4:
    # src_points:源平面中点的坐标矩阵。 dst_points:目标平面中点的坐标矩阵
    src_points = np.float32([kp1[m.queryIdx].pt for m in goods]).reshape(-1, 1, 2)
    dst_points = np.float32([kp2[m.trainIdx].pt for m in goods]).reshape(-1, 1, 2)

    # 根据匹配的点计算单应性矩阵
    H, _ = cv2.findHomography(src_points, dst_points, cv2.RANSAC, 5)
else:
    exit()

# 获取原始图的高和宽
h1, w1 = img1.shape[:2]
h2, w2 = img2.shape[:2]
img1_pts = np.float32([[0, 0], [0, h1-1], [w1-1, h1-1], [w1-1, 0]]).reshape(-1, 1, 2)
img2_pts = np.float32([[0, 0], [0, h2-1], [w2-1, h2-1], [w2-1, 0]]).reshape(-1, 1, 2)

# 计算img1的四个角变换之后的坐标
# 变换后img1_transform可能有负数,要平移回正,OpenCV中,x向左或者y向上为负
img1_transform = cv2.perspectiveTransform(img1_pts, H)

# 要把两幅图放在一个大图上,先把坐标聚合,也就是数组上下放在一起,对齐
results_pts = np.concatenate((img2_pts, img1_transform))

# 对齐后,找到每一列的最小值(应该为负数),平移这个最小值为正数,才能显示图像
# results_pts.min(axis=0)是二维的,用ravel直接取到一维的数据
# 因为原来有小数,可以-+1
[x_min, y_min] = np.int32(results_pts.min(axis=0).ravel() - 1)

# 大图需要的大小为两张图最大的宽和高与最小的宽和高的绝对值的和,这里求出最大的值
[x_max, y_max] = np.int32(results_pts.max(axis=0).ravel() + 1)

# 构造平移矩阵(格式固定),平移即仿射变换,原图点乘平移矩阵
move_matrix = np.array([[1, 0, -x_min], [0, 1, -y_min], [0, 0, 1]])

# 将img1平移并透视变换到大图中,如果不平移,我们看不全图片
# 大图的大小:(x_max - x_min, y_max - y_min)
results_img = cv2.warpPerspective(img1, move_matrix.dot(H), (x_max - x_min, y_max - y_min))

# 把第二张图放进来,直接用切片放进来 ndarray和OpenCV的wh是反的
results_img[-y_min: -y_min + h2, -x_min: -x_min + w2] = img2
# 如果中间有线,可以对img2进行透视变换,把图片拉正,大图片也可以拉正

cv2.imshow('results_img', results_img)
cv2.waitKey(0)
cv2.destroyAllWindows()


网站公告

今日签到

点亮在社区的每一天
去签到