OpenCV image processing - image stitching (Python)

1. Principle of picture stitching

For image stitching, it is mainly divided into two parts: 1. Feature point matching, to determine the positional relationship between the two images; 2. Transform all image projections to the same coordinate system, and complete docking and fusion.

2. Feature point matching

First create a feature conversion object, then calculate the feature points and descriptors of the two images, then create a feature matcher, find the matchers of the two images according to the descriptors, and then filter out some invalid matchers, and finally calculate the homography matrix according to the coordinates of the two images, and get the positional relationship between the two images.

def get_homo(img1, img2):

    #1. 创建特征转换对象
    #2. 通过特征转换对象获得特征点和描述子
    #3. 创建特征匹配器
    #4. 进行特征匹配
    #5. 过滤特征,找出有效的特征匹配点

    sift = cv2.xfeatures2d.SIFT_create()

    k1, d1 = sift.detectAndCompute(img1, None)
    k2, d2 = sift.detectAndCompute(img2, None)

    #创建特征匹配器
    bf = cv2.BFMatcher()
    matches = bf.knnMatch(d1, d2, k=2)

    #过滤特征,找出有效的特征匹配点
    verify_ratio = 0.8
    verify_matches = []
    for m1, m2 in matches:
        if m1.distance < 0.8 * m2.distance:
            verify_matches.append(m1)
    
	# 符合一定数量的特征点才进行求单应性矩阵的工作
    min_matches = 8
    if len(verify_matches) > min_matches:

        img1_pts = []
        img2_pts = []

        for m in verify_matches:
        	# 记录每个描述子的坐标
            img1_pts.append(k1[m.queryIdx].pt)
            img2_pts.append(k2[m.trainIdx].pt)
        #[(x1, y1), (x2, y2), ...]
        #[[x1, y1], [x2, y2], ...]

        img1_pts = np.float32(img1_pts).reshape(-1, 1, 2)
        img2_pts = np.float32(img2_pts).reshape(-1, 1, 2)
        H, mask = cv2.findHomography(img1_pts, img2_pts, cv2.RANSAC, 5.0)
        return H
    
    else:
        print('err: Not enough matches!')
        exit()

3. Image docking

The essence of image docking is to project an image into the coordinate system of another image, then find a suitable position through translation, and finally paste another image.

def stitch_image(img1, img2, H):
    # 1. 获得每张图片的四个角点
    # 2. 对图片进行变换(单应性矩阵使图进行旋转,平移)
    # 3. 创建一张大图,将两张图拼接到一起
    # 4. 将结果输出

    #获得原始图的高/宽
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]
	
	# 获取图片的四个角点
    img1_dims = np.float32([[0, 0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)
    img2_dims = np.float32([[0, 0], [0, h2], [w2, h2], [w2, 0]]).reshape(-1, 1, 2)

	# 将图1的原始四个点,根据单应性矩阵,获得投影坐标
    img1_transform = cv2.perspectiveTransform(img1_dims, H)

    # print(img1_dims)
    # print(img2_dims)
    # print(img1_transform)
	
	# 将两个图像的角点拼接起来。
    result_dims = np.concatenate((img2_dims, img1_transform), axis=0)
    #print(result_dims)
	
	# 获取图像中的最小点,最大点,防止有些信息显示不到
    [x_min, y_min] = np.int32(result_dims.min(axis=0).ravel()-0.5)
    [x_max, y_max ] = np.int32(result_dims.max(axis=0).ravel()+0.5)

    #平移的距离(左加右减,上加下减)
    transform_dist = [-x_min, -y_min]

    #[1, 0, dx]
    #[0, 1, dy]         
    #[0, 0, 1 ]
    # 创建好平移矩阵
    transform_array = np.array([[1, 0, transform_dist[0]],
                                [0, 1, transform_dist[1]],
                                [0, 0, 1]])
	# 透视变换,得到结果矩阵(只是img1来进行变换),但是图片是img1+img2的大小
    result_img = cv2.warpPerspective(img1, transform_array.dot(H), (x_max-x_min, y_max-y_min))


# 将img2贴到结果贴到原图中
    result_img[transform_dist[1]:transform_dist[1]+h2, 
                transform_dist[0]:transform_dist[0]+w2] = img2

    return result_img

4. Complete code

import cv2
import numpy  as np

def stitch_image(img1, img2, H):
    # 1. 获得每张图片的四个角点
    # 2. 对图片进行变换(单应性矩阵使图进行旋转,平移)
    # 3. 创建一张大图,将两张图拼接到一起
    # 4. 将结果输出

    #获得原始图的高/宽
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]

    img1_dims = np.float32([[0, 0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)
    img2_dims = np.float32([[0, 0], [0, h2], [w2, h2], [w2, 0]]).reshape(-1, 1, 2)


    img1_transform = cv2.perspectiveTransform(img1_dims, H)

    # print(img1_dims)
    # print(img2_dims)
    # print(img1_transform)

    result_dims = np.concatenate((img2_dims, img1_transform), axis=0)
    #print(result_dims)

    [x_min, y_min] = np.int32(result_dims.min(axis=0).ravel()-0.5)
    [x_max, y_max ] = np.int32(result_dims.max(axis=0).ravel()+0.5)

    #平移的距离
    transform_dist = [-x_min, -y_min]

    #[1, 0, dx]
    #[0, 1, dy]         
    #[0, 0, 1 ]
    transform_array = np.array([[1, 0, transform_dist[0]],
                                [0, 1, transform_dist[1]],
                                [0, 0, 1]])

    result_img = cv2.warpPerspective(img1, transform_array.dot(H), (x_max-x_min, y_max-y_min))

    # result_img[transform_dist[1]:transform_dist[1]+h2,
    #             transform_dist[0]:transform_dist[0]+w2] = img2

    return result_img


  
def get_homo(img1, img2):

    #1. 创建特征转换对象
    #2. 通过特征转换对象获得特征点和描述子
    #3. 创建特征匹配器
    #4. 进行特征匹配
    #5. 过滤特征,找出有效的特征匹配点

    sift = cv2.xfeatures2d.SIFT_create()

    k1, d1 = sift.detectAndCompute(img1, None)
    k2, d2 = sift.detectAndCompute(img2, None)

    #创建特征匹配器
    bf = cv2.BFMatcher()
    matches = bf.knnMatch(d1, d2, k=2)

    #过滤特征,找出有效的特征匹配点
    verify_ratio = 0.8
    verify_matches = []
    for m1, m2 in matches:
        if m1.distance < 0.8 * m2.distance:
            verify_matches.append(m1)
    
    min_matches = 8
    if len(verify_matches) > min_matches:

        img1_pts = []
        img2_pts = []

        for m in verify_matches:
            img1_pts.append(k1[m.queryIdx].pt)
            img2_pts.append(k2[m.trainIdx].pt)
        #[(x1, y1), (x2, y2), ...]
        #[[x1, y1], [x2, y2], ...]

        img1_pts = np.float32(img1_pts).reshape(-1, 1, 2)
        img2_pts = np.float32(img2_pts).reshape(-1, 1, 2)
        H, mask = cv2.findHomography(img1_pts, img2_pts, cv2.RANSAC, 5.0)
        return H
    
    else:
        print('err: Not enough matches!')
        exit()


#第一步,读取文件,将图片设置成一样大小640x480
#第二步,找特征点,描述子,计算单应性矩阵
#第三步,根据单应性矩阵对图像进行变换,然后平移
#第四步,拼接并输出最终结果

# 读取两张图片
img1 = cv2.imread('map1.png')
img2 = cv2.imread('map2.png')

# 将两张图片设置成同样大小
img1 = cv2.resize(img1, (640, 480))
img2 = cv2.resize(img2, (640, 480))

inputs = np.hstack((img1, img2))

# 获得单应性矩阵
H = get_homo(img1, img2)

# 进行图像拼接
result_image = stitch_image(img1, img2, H)



cv2.imshow('input img', result_image)
cv2.waitKey()

The image link used by the code
The above code is for learning and reference only.

Guess you like

Origin blog.csdn.net/weixin_45153969/article/details/131823322