6目摄像头照片球面投影变换拼接全景图片

最近在做全景拼接项目,有有六张来自于六目摄像头的图片,分别来自不同方向上的图片,摄像头示意图如下:
                                                                                在这里插入图片描述

各方向上图片的位置示意图如下:
                                                                 在这里插入图片描述

假设相对应位置的图片为拼接起来后(此处我们用代码进行了拼接,而且需要这样结构拼接起来作为输入):
在这里插入图片描述
球面变换和拼接的代码如下:

from PIL import Image
import math
import numpy as np
import cv2

def spherical_coordinates(i, j, w, h):
    """ Returns spherical coordinates of the pixel from the output image. """
    theta = 2*float(i)/float(w)-1
    phi = 2*float(j)/float(h)-1
    # phi = lat, theta = long
    return phi*(math.pi/2), theta*math.pi


def vector_coordinates(phi, theta):
    """ Returns 3D vector which points to the pixel location inside a sphere. """
    return (math.cos(phi) * math.cos(theta),  # X
            math.sin(phi),                    # Y
            math.cos(phi) * math.sin(theta))  # Z


# Assign identifiers to the faces of the cube
FACE_Z_POS = 1  # Left
FACE_Z_NEG = 2  # Right
FACE_Y_POS = 3  # Top
FACE_Y_NEG = 4  # Bottom
FACE_X_NEG = 5  # Front
FACE_X_POS = 6  # Back


def get_face(x, y, z):
    """ Uses 3D vector to find which cube face the pixel lies on. """
    largest_magnitude = max(abs(x), abs(y), abs(z))
    if largest_magnitude - abs(x) < 0.00001:
        return FACE_X_POS if x < 0 else FACE_X_NEG
    elif largest_magnitude - abs(y) < 0.00001:
        return FACE_Y_POS if y < 0 else FACE_Y_NEG
    elif largest_magnitude - abs(z) < 0.00001:
        return FACE_Z_POS if z < 0 else FACE_Z_NEG


def raw_face_coordinates(face, x, y, z):
    """
    Return coordinates with necessary sign (- or +) depending on which face they lie on.

    From Open-GL specification (chapter 3.8.10) https://www.opengl.org/registry/doc/glspec41.core.20100725.pdf
    """
    if face == FACE_X_NEG:
        xc = z
        yc = y
        ma = x
        return xc, yc, ma
    elif face == FACE_X_POS:
        xc = -z
        yc = y
        ma = x
        return xc, yc, ma
    elif face == FACE_Y_NEG:
        xc = z
        yc = -x
        ma = y
        return xc, yc, ma
    elif face == FACE_Y_POS:
        xc = z
        yc = x
        ma = y
        return xc, yc, ma
    elif face == FACE_Z_POS:
        xc = x
        yc = y
        ma = z
        return xc, yc, ma
    elif face == FACE_Z_NEG:
        xc = -x
        yc = y
        ma = z
        return xc, yc, ma


def raw_coordinates(xc, yc, ma):
    """ Return 2D coordinates on the specified face relative to the bottom-left corner of the face. Also from Open-GL spec."""
    return (float(xc)/abs(float(ma)) + 1) / 2, (float(yc)/abs(float(ma)) + 1) / 2


def face_origin_coordinates(face, n):
    """ Return bottom-left coordinate of specified face in the input image. """
    if face == FACE_X_NEG:
        return n, n
    elif face == FACE_X_POS:
        return 3*n, n
    elif face == FACE_Z_NEG:
        return 2*n, n
    elif face == FACE_Z_POS:
        return 0, n
    elif face == FACE_Y_POS:
        return n, 0
    elif face == FACE_Y_NEG:
        return n, 2*n


def normalized_coordinates(face, x, y, n):
    """ Return pixel coordinates in the input image where the specified pixel lies. """
    face_coords = face_origin_coordinates(face, n)
    normalized_x = math.floor(x*n)
    normalized_y = math.floor(y*n)

    # Stop out of bound behaviour
    if normalized_x < 0:
        normalized_x = 0
    elif normalized_x >= n:
        normalized_x = n-1
    if normalized_y < 0:
        normalized_x = 0
    elif normalized_y >= n:
        normalized_y = n-1

    return face_coords[0] + normalized_x, face_coords[1] + normalized_y


def find_corresponding_pixel(i, j, w, h, n):
    """
    :param i: X coordinate of output image pixel
    :param j: Y coordinate of output image pixel
    :param w: Width of output image
    :param h: Height of output image
    :param n: Height/Width of each square face
    :return: Pixel coordinates for the input image that a specified pixel in the output image maps to.
    """

    spherical = spherical_coordinates(i, j, w, h)
    vector_coords = vector_coordinates(spherical[0], spherical[1])
    face = get_face(vector_coords[0], vector_coords[1], vector_coords[2])
    raw_face_coords = raw_face_coordinates(face, vector_coords[0], vector_coords[1], vector_coords[2])

    cube_coords = raw_coordinates(raw_face_coords[0], raw_face_coords[1], raw_face_coords[2])

    return normalized_coordinates(face, cube_coords[0], cube_coords[1], n)

def get_six_pics_stitching(val_resize):
    size_t = val_resize
    # generate a (512,512) black image
    target = np.zeros((size_t, size_t), dtype=np.uint8) 
    ret = cv2.cvtColor(target, cv2.COLOR_GRAY2BGR)

    imgs_index = ["top", "left", "front", "right", "back", "down"]
    imgs_set = []

    # read all the images from target file
    for i in range(6):
        img = cv2.imread("./pics/{}.jpg".format(imgs_index[i]))
        img_res = cv2.resize(img, (size_t, size_t))
        imgs_set.append(img_res)


    # first row stitching
    ret_pano1 = np.concatenate((ret, imgs_set[0]), axis=1)
    for i in range(2):
        ret_pano1 = np.concatenate((ret_pano1, ret), axis=1)

    # Second row stitching
    imgs_mid_four = imgs_set[1:5]
    ret_pano2 = imgs_mid_four[0]
    for i in range(3):
        ret_pano2 = np.concatenate((ret_pano2, imgs_mid_four[i + 1]), axis=1)

    # Third row stitching
    ret_pano3 = np.concatenate((ret, imgs_set[5]), axis=1)
    for i in range(2):
        ret_pano3 = np.concatenate((ret_pano3, ret), axis=1)

    # whole image sitiching
    ret_pano = np.concatenate((ret_pano1, ret_pano2), axis=0)
    ret_pano = np.concatenate((ret_pano, ret_pano3), axis=0)
    cv2.imwrite('six_pics_stitching.jpg',ret_pano)


def convert_img(infile, outfile):

    inimg = Image.open(infile)

    wo, ho = inimg.size

    # Calculate height and width of output image, and size of each square face
    h = int(wo/3)
    w = 2*h
    n = int(ho/3)

    # Create new image with width w, and height h
    outimg = Image.new('RGB', (w, h))

    # For each pixel in output image find colour value from input image
    for ycoord in range(0, h):
        for xcoord in range(0, w):
            corrx, corry = find_corresponding_pixel(xcoord, ycoord, w, h, n)

            outimg.putpixel((xcoord, ycoord), inimg.getpixel((corrx, corry)))
        # Print progress percentage
        print(str(round((float(ycoord)/float(h))*100, 2)) + '%')
    outimg.save(outfile, 'PNG')


if __name__ == '__main__':
    val_resize = 512
    # you need put your images in the file"./pics/" or you can define by yourself.
    get_six_pics_stitching(val_resize)
    convert_img('six_pics_stitching.jpg', 'output.jpg')
    # out_img = cv2.imread('output.jpg')
    # cv2.imshow('out_img', out_img)
    # cv2.waitKey()

拼接后的效果如图:
在这里插入图片描述

参考链接:https://github.com/adamb70/Python-Spherical-Projection

发布了62 篇原创文章 · 获赞 83 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/zhou4411781/article/details/100553733