几何视觉的编程实践——相机参数计算——基于ipyvolume的3D可视化——透视投影

这篇具有很好参考价值的文章主要介绍了几何视觉的编程实践——相机参数计算——基于ipyvolume的3D可视化——透视投影。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文主要侧重上手实践,理论部分可以先参考其他文章学习


前言

本次实践是几何视觉的编程实践,是对计算机视觉课程的一次巩固复习,从中查缺补漏完善知识体系。主要实现了相机内外参的计算,标定板的三维可视化,最后还添加新的模型实测透视效果。

使用的是jupterlab的环境,基于ipyvolume实现的3D可视化

图片数据和源码文件在此下载:几何视觉视觉代码+图片

一、环境配置

1.JupyterLab

jupterlab可以在Anaconda Navigator中打开,也可以使用终端打开。
ipyvolume,人工智能,python,3d,python,matplotlib
如图,点击Launch即可启动,界面和jupyter类似。

2.安装包

想要实现3D效果,需要提前导入ipyvolume和matplotlib库;
如图在base环境下搜索安装ipyvolume,选择安装0.6.0a8版本
(旧版本可能会无法运行,尽量升级到最新版)
ipyvolume,人工智能,python,3d,python,matplotlib
matplotlib库也是类似,我使用的版本是matplotlib==3.5.0

3.数据准备

提取准备好带有标定板的十张图片,以及chessboard
ipyvolume,人工智能,python,3d,python,matplotlib

二、代码实现

1.相机校准得到相机内外参

camera_calibration.py 代码如下:

import matplotlib.pyplot as plt
import ipyvolume as ipv
import numpy as np
import cv2

def calibrate_cameras() :
    """ Calibrates cameras from chessboard images. 
    
    Returns: 
        images (list[np.ndarray]): Images containing the chessboard.
        intrinsics (np.ndarray): An upper triangular 4x4 full-rank matrix containing camera intrinsics.
        distortions (np.ndarray): Radial distortion coefficients.
        rotation_vectors (list[np.ndarray]): Rodrigues rotation vectors.
        translation_vectors (list[np.ndarray]): Translation vectors.
        object_points: (np.ndarray): A (4, 54) point array, representing the [x,y,z,w]
            of 54 chessboard points (homogenous coordiantes).
    """    
    
    # Read images
    images = list() 
    for i in range(11): 
        img = cv2.imread(f'./images/{i}.jpg')
        img = cv2.resize(img, None, fx=0.25, fy=0.25)
        images.append(img)     
                
    # chessboard corner search with opencv.
    shape = (6, 9) # The default opencv chessboard has 6 rows, 9 columns 
    object_points_all = [] # 3D world coordinates
    image_points_all = [] # 2D image coordinates
    flags = cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE
    refinement_criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)    
    
    # For each image, store the object points and image points of chessboard corners.对于每个图像,存储棋盘角的对象点和图像点。
    images_filtered = list()    
    object_points = np.zeros((1, shape[0] * shape[1], 3), np.float32)
    object_points[0, :, :2] = np.mgrid[0:shape[0], 0:shape[1]].T.reshape(-1, 2)
    for idx, image in enumerate(images): 
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        succes, corners = cv2.findChessboardCorners(image=gray, 
                                                    patternSize=shape,
                                                    flags=flags) 
        if succes:
            images_filtered.append(image)
            corners = cv2.cornerSubPix(image=gray, 
                                       corners=corners, 
                                       winSize=(11,11), 
                                       zeroZone=(-1,-1), 
                                       criteria=refinement_criteria)
            object_points_all.append(object_points)
            image_points_all.append(corners)    
            
    images = images_filtered
    
    # Calibrate the cameras by using the 3D <-> 2D point correspondences.使用3D<->2D点对应关系校准相机。
    ret, intrinsics, distortions, rotation_vectors, translation_vectors = cv2.calibrateCamera(object_points_all, image_points_all, gray.shape[::-1], None, None)
        
    intrinsics = np.hstack([intrinsics, np.zeros((3, 1))])
    intrinsics = np.vstack([intrinsics, [[0, 0, 0, 1]]])

    # Convert chessboard object points to homogeneous coordinates for later use.将棋盘对象点转换为同质坐标以供以后使用。
    object_points = object_points[0].reshape((-1, 3)).T
    object_points = np.vstack([object_points, np.ones((1, object_points.shape[1]))])
    
    return images, intrinsics, distortions, rotation_vectors, translation_vectors, object_points

if __name__ == "__main__":

    # camera calibration
    images, intrinsics, distortions, rotation_vectors, translation_vectors, object_points = calibrate_cameras()

    print(f'\n\nNumber of calibration images: {len(images)}')
    print(f'Number of calibration points: {object_points.shape[1]}')
    print(f'\n\nSample of imags used')
    plt.figure(figsize=(20,20))
    
    _ = plt.imshow(cv2.hconcat([cv2.cvtColor(im, cv2.COLOR_RGB2BGR) for im in images[:5]]))    

结果如图:
ipyvolume,人工智能,python,3d,python,matplotlib

2.对棋盘标定板和相机中心进行三维可视化

camera_center.py代码:

import matplotlib.pyplot as plt
import ipyvolume as ipv
import numpy as np
import cv2

cam_sphere_size = 1

#from camera_calibration import calibrate_cameras

def calibrate_cameras() :
    """ Calibrates cameras from chessboard images. 
    
    Returns: 
        images (list[np.ndarray]): Images containing the chessboard.
        intrinsics (np.ndarray): An upper triangular 4x4 full-rank matrix containing camera intrinsics.
        distortions (np.ndarray): Radial distortion coefficients.
        rotation_vectors (list[np.ndarray]): Rodrigues rotation vectors.
        translation_vectors (list[np.ndarray]): Translation vectors.
        object_points: (np.ndarray): A (4, 54) point array, representing the [x,y,z,w]
            of 54 chessboard points (homogenous coordiantes).
    """    
    
    # Read images
    images = list() 
    for i in range(11): 
        img = cv2.imread(f'./images/{i}.jpg')
        img = cv2.resize(img, None, fx=0.25, fy=0.25)
        images.append(img)     
                
    # chessboard corner search with opencv.
    shape = (6, 9) # The default opencv chessboard has 6 rows, 9 columns 
    object_points_all = [] # 3D world coordinates
    image_points_all = [] # 2D image coordinates
    flags = cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE
    refinement_criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)    
    
    # For each image, store the object points and image points of chessboard corners.
    images_filtered = list()    
    object_points = np.zeros((1, shape[0] * shape[1], 3), np.float32)
    object_points[0, :, :2] = np.mgrid[0:shape[0], 0:shape[1]].T.reshape(-1, 2)
    for idx, image in enumerate(images): 
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        succes, corners = cv2.findChessboardCorners(image=gray, 
                                                    patternSize=shape,
                                                    flags=flags) 
        if succes:
            images_filtered.append(image)
            corners = cv2.cornerSubPix(image=gray, 
                                       corners=corners, 
                                       winSize=(11,11), 
                                       zeroZone=(-1,-1), 
                                       criteria=refinement_criteria)
            object_points_all.append(object_points)
            image_points_all.append(corners)    
            
    images = images_filtered
    
    # Calibrate the cameras by using the 3D <-> 2D point correspondences.
    ret, intrinsics, distortions, rotation_vectors, translation_vectors = cv2.calibrateCamera(object_points_all, image_points_all, gray.shape[::-1], None, None)
        
    intrinsics = np.hstack([intrinsics, np.zeros((3, 1))])
    intrinsics = np.vstack([intrinsics, [[0, 0, 0, 1]]])

    # Convert chessboard object points to homogeneous coordinates for later use.
    object_points = object_points[0].reshape((-1, 3)).T
    object_points = np.vstack([object_points, np.ones((1, object_points.shape[1]))])
    
    return images, intrinsics, distortions, rotation_vectors, translation_vectors, object_points


def extrinsics_from_calibration(rotation_vectors, translation_vectors):
    """ Calculates extrinsic matrices from calibration output. 
        
    Args: 
        rotation_vectors (list[np.ndarray]): Rodrigues rotation vectors.
        translation_vectors (list[np.ndarray]): Translation vectors.
    Returns: 
        extrinsics (list[np.ndarray]): A list of camera extrinsic matrices.
            These matrices are 4x4 full-rank.
    """
    
    rotation_matrices = list() 
    for rot in rotation_vectors:
        rotation_matrices.append(cv2.Rodrigues(rot)[0]) 

    extrinsics = list()
    for rot, trans in zip(rotation_matrices, translation_vectors): 
        extrinsic = np.concatenate([rot, trans], axis=1)
        extrinsic = np.vstack([extrinsic, [[0,0,0,1]]]) 
        extrinsics.append(extrinsic)
    
    return extrinsics

def camera_centers_from_extrinsics(extrinsics):
    """ Calculates camera centers from extrinsic matrices. 
    
    Args: 
        extrinsics (list[np.ndarray]):  A list of camera extrinsic matrices.
    Returns: 
        camera_centers (list[np.ndarray]): Homogenous coordinates of camera centers in 
            3D world coordinate frame.
    """
    camera_centers = list() 

    for extrinsic in extrinsics:
        rot = extrinsic[:3, :3]
        trans = extrinsic[:3, 3]
        # compute camera center based on rotation and translation
        center = np.dot(np.linalg.pinv(rot),-trans) # TODO
        center = np.append(center, 1)
        camera_centers.append(center)
    
    return camera_centers

def plot_camera_axis(cam_center, inv_extrinsic, vis_scale):
    """ Plots the x, y, and z basis vectors of the camera coordinate frame.
        This is achieved by  transforming the basis vectors into the world 
        coordinate frame and plotting them. 
    
    Args:
        cam_center (np.ndarray): Coordinates of camera centers in 
            3D world coordinate frame.                                                                                                                                                   :
        inv_extrinsic (np.ndarray): The (pseudo)-inverse of camera extrinsic matrix.
        vis_scale (int): A visualization size multiplyer to make cameras 
            appear larger and easier to see.
    """
    x, y, z, _ = cam_center
    # use inv_extrinsic function to compute a camera's x, y, and z axis
    
    x_arrow = inv_extrinsic @ (1 * vis_scale, 0, 0, 1)
    y_arrow = inv_extrinsic @ (0, 1 * vis_scale, 0, 1) 
    z_arrow =  inv_extrinsic @ (0, 0, 1 * vis_scale,1)
    
    ipv.plot([x, x_arrow[0]], [y, x_arrow[1]], [z, x_arrow[2]], color='red')
    ipv.plot([x, y_arrow[0]], [y, y_arrow[1]], [z, y_arrow[2]], color='blue')
    ipv.plot([x, z_arrow[0]], [y, z_arrow[1]], [z, z_arrow[2]], color='green')
    ipv.scatter(np.array([x]), np.array([y]), np.array([z]), size=cam_sphere_size, marker="sphere", color='blue')

def init_3d_plot():
    """ Initializes a ipyvolume 3d plot and centers the 
        world view around the center of the chessboard.
        
    Returns: 
        fig (ipyvolume.pylab.figure): A 3D plot. 
    """
    chessboard_x_center = 2.5
    chessboard_y_center = 4
    fig = ipv.pylab.figure(figsize=(15, 15), width=800)
    ipv.xlim(2.5 - 30, 2.5 + 30)
    ipv.ylim(4 - 30, 4 + 30)
    ipv.zlim(-50, 10)
    ipv.pylab.view(azimuth=40, elevation=-150)
    return fig

def plot_chessboard(object_points):
    """ Plots a 3D chessboard and highlights the 
        objects points with green spheres. 
        
    Args:  A (4, 54) point array, representing the [x,y,z,w]
           of 54 chessboard points (homogenous coordiantes).
    """
    img = cv2.imread(f'./images/chessboard.jpg')
    img_height, img_width, _ = img.shape
    chessboard_rows, chessboard_cols = 7, 10
    xx, yy = np.meshgrid(np.linspace(0, chessboard_rows, img_height), 
                         np.linspace(0, chessboard_cols, img_width)) 
    zz = np.zeros_like(yy)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) / 255
    # -1 is used as the start of the board images x and y coord, 
    # such that the first inner corner appear as coord (0, 0, 0) 
    ipv.plot_surface(xx-1, yy-1, zz, color=cv2.transpose(img))
    xs, ys, zs, _ = object_points
    ipv.scatter(xs, ys, zs, size=1, marker='sphere', color='lime')


if __name__ == "__main__":
    
    images, intrinsics, distortions, rotation_vectors, translation_vectors, object_points = calibrate_cameras()        

    extrinsics = extrinsics_from_calibration(rotation_vectors, translation_vectors)
    camera_centers = camera_centers_from_extrinsics(extrinsics)

    print(f'Shape of an extrinsic matrix: {extrinsics[0].shape}')
    print(f'Shape of the intrinsic matrix: {intrinsics.shape}')
    print(f'Number of cameras in the scene: {len(camera_centers)}')

    init_3d_plot()

    # Determines the visual scale of the plotted cameras 确定打印相机的视觉比例
    vis_scale = 10

    for cam_center, extrinsic, image in zip(camera_centers, extrinsics, images): 
        height, width, _ = image.shape
        inv_extrinsic = np.linalg.inv(extrinsic)
        plot_camera_axis(cam_center, inv_extrinsic, vis_scale)
    
    plot_chessboard(object_points)

    ipv.show()

结果如下:
ipyvolume,人工智能,python,3d,python,matplotlib

3.将标定板图像画在三维空间中的相机视角图像中

camera_wireframe.py代码如下:

import matplotlib.pyplot as plt
import ipyvolume as ipv
import numpy as np
import cv2

cam_sphere_size = 1
# Visual dimension of the camera in the 3D plot. 

images = list() 
for i in range(11): 
    img = cv2.imread(f'./images/{i}.jpg')
    img = cv2.resize(img, None, fx=0.25, fy=0.25)
    images.append(img)  
     
height, width,_= images[0].shape
camera_aspect_ratio = width / height
# A length of 1 corresponds to the length of 1 chessboard cell.
# Set height of camera viewport to 1.
vis_cam_height = 1 
vis_cam_width = vis_cam_height * camera_aspect_ratio
wire_frame_depth = 1.2


def calibrate_cameras() :
    """ Calibrates cameras from chessboard images. 
    
    Returns: 
        images (list[np.ndarray]): Images containing the chessboard.
        intrinsics (np.ndarray): An upper triangular 4x4 full-rank matrix containing camera intrinsics.
        distortions (np.ndarray): Radial distortion coefficients.
        rotation_vectors (list[np.ndarray]): Rodrigues rotation vectors.
        translation_vectors (list[np.ndarray]): Translation vectors.
        object_points: (np.ndarray): A (4, 54) point array, representing the [x,y,z,w]
            of 54 chessboard points (homogenous coordiantes).
    """    
    
    # Read images
    images = list() 
    for i in range(11): 
        img = cv2.imread(f'./images/{i}.jpg')
        img = cv2.resize(img, None, fx=0.25, fy=0.25)
        images.append(img)     
                
    # chessboard corner search with opencv.
    shape = (6, 9) # The default opencv chessboard has 6 rows, 9 columns 
    object_points_all = [] # 3D world coordinates
    image_points_all = [] # 2D image coordinates
    flags = cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE
    refinement_criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)    
    
    # For each image, store the object points and image points of chessboard corners.
    images_filtered = list()    
    object_points = np.zeros((1, shape[0] * shape[1], 3), np.float32)
    object_points[0, :, :2] = np.mgrid[0:shape[0], 0:shape[1]].T.reshape(-1, 2)
    for idx, image in enumerate(images): 
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        succes, corners = cv2.findChessboardCorners(image=gray, 
                                                    patternSize=shape,
                                                    flags=flags) 
        if succes:
            images_filtered.append(image)
            corners = cv2.cornerSubPix(image=gray, 
                                       corners=corners, 
                                       winSize=(11,11), 
                                       zeroZone=(-1,-1), 
                                       criteria=refinement_criteria)
            object_points_all.append(object_points)
            image_points_all.append(corners)    
            
    images = images_filtered
    
    # Calibrate the cameras by using the 3D <-> 2D point correspondences.
    ret, intrinsics, distortions, rotation_vectors, translation_vectors = cv2.calibrateCamera(object_points_all, image_points_all, gray.shape[::-1], None, None)
        
    intrinsics = np.hstack([intrinsics, np.zeros((3, 1))])
    intrinsics = np.vstack([intrinsics, [[0, 0, 0, 1]]])

    # Convert chessboard object points to homogeneous coordinates for later use.
    object_points = object_points[0].reshape((-1, 3)).T
    object_points = np.vstack([object_points, np.ones((1, object_points.shape[1]))])
    
    return images, intrinsics, distortions, rotation_vectors, translation_vectors, object_points


#from camera_center import extrinsics_from_calibration, camera_centers_from_extrinsics, init_3d_plot, plot_chessboard

def extrinsics_from_calibration(rotation_vectors, translation_vectors):
    """ Calculates extrinsic matrices from calibration output. 
        
    Args: 
        rotation_vectors (list[np.ndarray]): Rodrigues rotation vectors.
        translation_vectors (list[np.ndarray]): Translation vectors.
    Returns: 
        extrinsics (list[np.ndarray]): A list of camera extrinsic matrices.
            These matrices are 4x4 full-rank.
    """
    
    rotation_matrices = list() 
    for rot in rotation_vectors:
        rotation_matrices.append(cv2.Rodrigues(rot)[0]) 

    extrinsics = list()
    for rot, trans in zip(rotation_matrices, translation_vectors): 
        extrinsic = np.concatenate([rot, trans], axis=1)
        extrinsic = np.vstack([extrinsic, [[0,0,0,1]]]) 
        extrinsics.append(extrinsic)
    
    return extrinsics

def camera_centers_from_extrinsics(extrinsics):
    """ Calculates camera centers from extrinsic matrices. 
    
    Args: 
        extrinsics (list[np.ndarray]):  A list of camera extrinsic matrices.
    Returns: 
        camera_centers (list[np.ndarray]): Homogenous coordinates of camera centers in 
            3D world coordinate frame.
    """
    camera_centers = list() 

    for extrinsic in extrinsics:
        rot = extrinsic[:3, :3]
        trans = extrinsic[:3, 3]
        # compute camera center based on rotation and translation
        center = np.dot(np.linalg.pinv(rot),-trans) # TODO
        center = np.append(center, 1)
        camera_centers.append(center)
    
    return camera_centers

def init_3d_plot():
    """ Initializes a ipyvolume 3d plot and centers the 
        world view around the center of the chessboard.
        
    Returns: 
        fig (ipyvolume.pylab.figure): A 3D plot. 
    """
    chessboard_x_center = 2.5
    chessboard_y_center = 4
    fig = ipv.pylab.figure(figsize=(15, 15), width=800)
    ipv.xlim(2.5 - 30, 2.5 + 30)
    ipv.ylim(4 - 30, 4 + 30)
    ipv.zlim(-50, 10)
    ipv.pylab.view(azimuth=40, elevation=-150)
    return fig



def plot_camera_wireframe(cam_center, vis_scale, inv_extrinsic):
    """ Plots the wireframe of a camera's viewport.打印摄影机视口的线框。 """
    x, y, z, _ = cam_center 
    
    # Get left/right top/bottom wireframe coordinates
    # Use the inverse of the camera's extrinsic matrix to convert 
    # coordinates relative to the camera to world coordinates.
    lt = inv_extrinsic @ np.array((-vis_cam_width/2, -vis_cam_height/2, wire_frame_depth, 1/vis_scale)) * vis_scale
    rt = inv_extrinsic @ np.array((vis_cam_width/2,  -vis_cam_height/2, wire_frame_depth, 1/vis_scale)) * vis_scale
    lb = inv_extrinsic @ np.array((-vis_cam_width/2,  vis_cam_height/2, wire_frame_depth, 1/vis_scale)) * vis_scale
    rb = inv_extrinsic @ np.array((vis_cam_width/2,   vis_cam_height/2, wire_frame_depth, 1/vis_scale)) * vis_scale

    # Connect camera projective center to wireframe extremities
    ipv.plot([x, lt[0]], [y, lt[1]], [z, lt[2]], color='blue')
    ipv.plot([x, rt[0]], [y, rt[1]], [z, rt[2]], color='blue')
    ipv.plot([x, lb[0]], [y, lb[1]], [z, lb[2]], color='blue')
    ipv.plot([x, rb[0]], [y, rb[1]], [z, rb[2]], color='blue')
    
    # Connect wireframe corners with a rectangle
    ipv.plot([lt[0], rt[0]], [lt[1], rt[1]], [lt[2], rt[2]], color='blue')
    ipv.plot([rt[0], rb[0]], [rt[1], rb[1]], [rt[2], rb[2]], color='blue')
    ipv.plot([rb[0], lb[0]], [rb[1], lb[1]], [rb[2], lb[2]], color='blue')
    ipv.plot([lb[0], lt[0]], [lb[1], lt[1]], [lb[2], lt[2]], color='blue')
    ipv.scatter(np.array([x]), np.array([y]), np.array([z]), size=cam_sphere_size, marker="sphere", color='blue')

def plot_chessboard(object_points):
    """ Plots a 3D chessboard and highlights the 
        objects points with green spheres. 
        
    Args:  A (4, 54) point array, representing the [x,y,z,w]
           of 54 chessboard points (homogenous coordiantes).
    """
    img = cv2.imread(f'./images/chessboard.jpg')
    img_height, img_width, _ = img.shape
    chessboard_rows, chessboard_cols = 7, 10
    xx, yy = np.meshgrid(np.linspace(0, chessboard_rows, img_height), 
                         np.linspace(0, chessboard_cols, img_width)) 
    zz = np.zeros_like(yy)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) / 255
    # -1 is used as the start of the board images x and y coord, 
    # such that the first inner corner appear as coord (0, 0, 0) 
    ipv.plot_surface(xx-1, yy-1, zz, color=cv2.transpose(img))
    xs, ys, zs, _ = object_points
    ipv.scatter(xs, ys, zs, size=1, marker='sphere', color='lime')    
    
def plot_picture(image, inv_extrinsic, vis_scale):
    """ Plots a real-world image its respective 3D camera wireframe. 打印真实世界图像及其相应的3D相机线框。""" 
    image = image.copy()
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) 
    image = cv2.resize(image, None, fx=0.1, fy=0.1) / 255
    img_height, img_width, _ = image.shape

    xx, yy = np.meshgrid(np.linspace(-vis_cam_width/2 * vis_scale,  vis_cam_width/2 * vis_scale,  img_width), 
                         np.linspace(-vis_cam_height/2 * vis_scale, vis_cam_height/2 * vis_scale, img_height))
    zz = np.ones_like(yy) * wire_frame_depth * vis_scale
    coords = np.stack([xx, yy, zz, np.ones_like(zz)]) 
    coords = coords.reshape(4, -1) 
     
    # Convert canera relative coordinates to world relative coordinates将canera相对坐标转换为世界相对坐标
    coords = inv_extrinsic @ coords
    xx, yy, zz, ones = coords.reshape(4, img_height, img_width) 
    ipv.plot_surface(xx, yy, zz, color=image)
    
    
def project_points_to_picture(image, object_points, intrinsics, extrinsic):
    """ Perspective projects points to an image and draws them green. """
    image = image.copy()
    # project points to an image with perspective projection使用透视投影指向图像
    
    proj_matrix = np.dot(intrinsics, extrinsic) # TODO
    object_points = np.dot(proj_matrix, object_points)  # TODO
    xs, ys, ones, disparity = object_points / object_points[2]
    
    for idx, (x, y) in enumerate(zip(xs, ys)):
        x = round(x)
        y = round(y)
        if (0 < y < image.shape[0] and
            0 < x < image.shape[1]):
            # Each point occupies a 20x20 pixel area in the image.
            image[y-10:y+10, x-10:x+10] = [0, 255, 0] 
    
    return image

if __name__ == "__main__":
    images, intrinsics, distortions, rotation_vectors, translation_vectors, object_points = calibrate_cameras()    
    extrinsics = extrinsics_from_calibration(rotation_vectors, translation_vectors)
    camera_centers = camera_centers_from_extrinsics(extrinsics)

    init_3d_plot()
    
    plot_chessboard(object_points)    
    
    vis_scale = 10
   
    for cam_center, extrinsic, image in zip(camera_centers, extrinsics, images):
        image = project_points_to_picture(image, object_points, intrinsics, extrinsic)
        inv_extrinsic = np.linalg.pinv(extrinsic)

        # Plot cameras as viewport wireframes
        plot_camera_wireframe(cam_center, vis_scale, inv_extrinsic)

        # Plot images in the camera wireframes
        plot_picture(image, inv_extrinsic, vis_scale)
        
    ipv.show()  

结果如下:
ipyvolume,人工智能,python,3d,python,matplotlib

4.把兔子模型放到场景中并投影到图像

plot_bunny.py 代码:

import matplotlib.pyplot as plt
import ipyvolume as ipv
import numpy as np
import cv2

cam_sphere_size = 1
# Visual dimension of the camera in the 3D plot. 
images = list() 
for i in range(11): 
    img = cv2.imread(f'./images/{i}.jpg')
    img = cv2.resize(img, None, fx=0.25, fy=0.25)
    images.append(img)  
height, width,_= images[0].shape
camera_aspect_ratio = width / height
# A length of 1 corresponds to the length of 1 chessboard cell.
# This is because a chessboard points have been defined as such.
# Set height of camera viewport to 1.
vis_cam_height = 1 
vis_cam_width = vis_cam_height * camera_aspect_ratio
wire_frame_depth = 1.2

#from camera_calibration import calibrate_cameras
#from camera_center import extrinsics_from_calibration, camera_centers_from_extrinsics, init_3d_plot, plot_chessboard
#from camera_wireframe import plot_camera_wireframe, plot_picture, project_points_to_picture

def calibrate_cameras() :
    """ Calibrates cameras from chessboard images. 
    
    Returns: 
        images (list[np.ndarray]): Images containing the chessboard.
        intrinsics (np.ndarray): An upper triangular 4x4 full-rank matrix containing camera intrinsics.
        distortions (np.ndarray): Radial distortion coefficients.
        rotation_vectors (list[np.ndarray]): Rodrigues rotation vectors.
        translation_vectors (list[np.ndarray]): Translation vectors.
        object_points: (np.ndarray): A (4, 54) point array, representing the [x,y,z,w]
            of 54 chessboard points (homogenous coordiantes).
    """    
    
    # Read images
    images = list() 
    for i in range(11): 
        img = cv2.imread(f'./images/{i}.jpg')
        img = cv2.resize(img, None, fx=0.25, fy=0.25)
        images.append(img)     
                
    # chessboard corner search with opencv.
    shape = (6, 9) # The default opencv chessboard has 6 rows, 9 columns 
    object_points_all = [] # 3D world coordinates
    image_points_all = [] # 2D image coordinates
    flags = cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE
    refinement_criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)    
    
    # For each image, store the object points and image points of chessboard corners.
    images_filtered = list()    
    object_points = np.zeros((1, shape[0] * shape[1], 3), np.float32)
    object_points[0, :, :2] = np.mgrid[0:shape[0], 0:shape[1]].T.reshape(-1, 2)
    for idx, image in enumerate(images): 
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        succes, corners = cv2.findChessboardCorners(image=gray, 
                                                    patternSize=shape,
                                                    flags=flags) 
        if succes:
            images_filtered.append(image)
            corners = cv2.cornerSubPix(image=gray, 
                                       corners=corners, 
                                       winSize=(11,11), 
                                       zeroZone=(-1,-1), 
                                       criteria=refinement_criteria)
            object_points_all.append(object_points)
            image_points_all.append(corners)    
            
    images = images_filtered
    
    # Calibrate the cameras by using the 3D <-> 2D point correspondences.
    ret, intrinsics, distortions, rotation_vectors, translation_vectors = cv2.calibrateCamera(object_points_all, image_points_all, gray.shape[::-1], None, None)
        
    intrinsics = np.hstack([intrinsics, np.zeros((3, 1))])
    intrinsics = np.vstack([intrinsics, [[0, 0, 0, 1]]])

    # Convert chessboard object points to homogeneous coordinates for later use.
    object_points = object_points[0].reshape((-1, 3)).T
    object_points = np.vstack([object_points, np.ones((1, object_points.shape[1]))])
    
    return images, intrinsics, distortions, rotation_vectors, translation_vectors, object_points


#from camera_center import extrinsics_from_calibration, camera_centers_from_extrinsics, init_3d_plot, plot_chessboard

def extrinsics_from_calibration(rotation_vectors, translation_vectors):
    """ Calculates extrinsic matrices from calibration output. 
        
    Args: 
        rotation_vectors (list[np.ndarray]): Rodrigues rotation vectors.
        translation_vectors (list[np.ndarray]): Translation vectors.
    Returns: 
        extrinsics (list[np.ndarray]): A list of camera extrinsic matrices.
            These matrices are 4x4 full-rank.
    """
    
    rotation_matrices = list() 
    for rot in rotation_vectors:
        rotation_matrices.append(cv2.Rodrigues(rot)[0]) 

    extrinsics = list()
    for rot, trans in zip(rotation_matrices, translation_vectors): 
        extrinsic = np.concatenate([rot, trans], axis=1)
        extrinsic = np.vstack([extrinsic, [[0,0,0,1]]]) 
        extrinsics.append(extrinsic)
    
    return extrinsics

def camera_centers_from_extrinsics(extrinsics):
    """ Calculates camera centers from extrinsic matrices. 
    
    Args: 
        extrinsics (list[np.ndarray]):  A list of camera extrinsic matrices.
    Returns: 
        camera_centers (list[np.ndarray]): Homogenous coordinates of camera centers in 
            3D world coordinate frame.
    """
    camera_centers = list() 

    for extrinsic in extrinsics:
        rot = extrinsic[:3, :3]
        trans = extrinsic[:3, 3]
        # compute camera center based on rotation and translation
        center = np.dot(np.linalg.pinv(rot),-trans) # TODO# TODO
        center = np.append(center, 1)
        camera_centers.append(center)
    
    return camera_centers

def init_3d_plot():
    """ Initializes a ipyvolume 3d plot and centers the 
        world view around the center of the chessboard.
        
    Returns: 
        fig (ipyvolume.pylab.figure): A 3D plot. 
    """
    chessboard_x_center = 2.5
    chessboard_y_center = 4
    fig = ipv.pylab.figure(figsize=(15, 15), width=800)
    ipv.xlim(2.5 - 30, 2.5 + 30)
    ipv.ylim(4 - 30, 4 + 30)
    ipv.zlim(-50, 10)
    ipv.pylab.view(azimuth=40, elevation=-150)
    return fig

def plot_camera_wireframe(cam_center, vis_scale, inv_extrinsic):
    """ Plots the wireframe of a camera's viewport. """
    x, y, z, _ = cam_center 
    
    # Get left/right top/bottom wireframe coordinates
    # Use the inverse of the camera's extrinsic matrix to convert 
    # coordinates relative to the camera to world coordinates.
    lt = inv_extrinsic @ np.array((-vis_cam_width/2, -vis_cam_height/2, wire_frame_depth, 1/vis_scale)) * vis_scale
    rt = inv_extrinsic @ np.array((vis_cam_width/2,  -vis_cam_height/2, wire_frame_depth, 1/vis_scale)) * vis_scale
    lb = inv_extrinsic @ np.array((-vis_cam_width/2,  vis_cam_height/2, wire_frame_depth, 1/vis_scale)) * vis_scale
    rb = inv_extrinsic @ np.array((vis_cam_width/2,   vis_cam_height/2, wire_frame_depth, 1/vis_scale)) * vis_scale

    # Connect camera projective center to wireframe extremities
    ipv.plot([x, lt[0]], [y, lt[1]], [z, lt[2]], color='blue')
    ipv.plot([x, rt[0]], [y, rt[1]], [z, rt[2]], color='blue')
    ipv.plot([x, lb[0]], [y, lb[1]], [z, lb[2]], color='blue')
    ipv.plot([x, rb[0]], [y, rb[1]], [z, rb[2]], color='blue')
    
    # Connect wireframe corners with a rectangle
    ipv.plot([lt[0], rt[0]], [lt[1], rt[1]], [lt[2], rt[2]], color='blue')
    ipv.plot([rt[0], rb[0]], [rt[1], rb[1]], [rt[2], rb[2]], color='blue')
    ipv.plot([rb[0], lb[0]], [rb[1], lb[1]], [rb[2], lb[2]], color='blue')
    ipv.plot([lb[0], lt[0]], [lb[1], lt[1]], [lb[2], lt[2]], color='blue')
    ipv.scatter(np.array([x]), np.array([y]), np.array([z]), size=cam_sphere_size, marker="sphere", color='blue')

def plot_picture(image, inv_extrinsic, vis_scale):
    """ Plots a real-world image its respective 3D camera wireframe. """ 
    image = image.copy()
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) 
    image = cv2.resize(image, None, fx=0.1, fy=0.1) / 255
    img_height, img_width, _ = image.shape

    xx, yy = np.meshgrid(np.linspace(-vis_cam_width/2 * vis_scale,  vis_cam_width/2 * vis_scale,  img_width), 
                         np.linspace(-vis_cam_height/2 * vis_scale, vis_cam_height/2 * vis_scale, img_height))
    zz = np.ones_like(yy) * wire_frame_depth * vis_scale
    coords = np.stack([xx, yy, zz, np.ones_like(zz)]) 
    coords = coords.reshape(4, -1) 
     
    # Convert canera relative coordinates to world relative coordinates
    coords = inv_extrinsic @ coords
    xx, yy, zz, ones = coords.reshape(4, img_height, img_width) 
    ipv.plot_surface(xx, yy, zz, color=image)
    
    
def project_points_to_picture(image, object_points, intrinsics, extrinsic):
    """ Perspective projects points to an image and draws them green. """
    image = image.copy()
    # project points to an image with perspective projection使用透视投影指向图像
    proj_matrix = np.dot(intrinsics , extrinsic) # TODO
    object_points = np.dot(proj_matrix, object_points)  # TODO
    xs, ys, ones, disparity = object_points / object_points[2]
    
    for idx, (x, y) in enumerate(zip(xs, ys)):
        x = round(x)
        y = round(y)
        if (0 < y < image.shape[0] and
            0 < x < image.shape[1]):
            # Each point occupies a 20x20 pixel area in the image.
            image[y-10:y+10, x-10:x+10] = [0, 255, 0] 
    
    return image

def plot_bunny():
    """Plots the Stanford bunny pointcloud and returns its points. 
    
    Returns: 
        bunny_coords (np.ndarray): a 4xn homogenous point cloud array.
    """
    bunny_coords = np.load(open('data/bunny_point_cloud.npy', 'rb'))
    b_xs, b_ys, b_zs = bunny_coords[:3]
    bunny_point_size = 0.5
    ipv.scatter(b_xs, b_ys, b_zs, size=bunny_point_size, marker="sphere", color='lime')
    return bunny_coords

if __name__ == "__main__":
    images, intrinsics, distortions, rotation_vectors, translation_vectors, object_points = calibrate_cameras()    
    extrinsics = extrinsics_from_calibration(rotation_vectors, translation_vectors)
    camera_centers = camera_centers_from_extrinsics(extrinsics)

    init_3d_plot()
    bunny_coords = plot_bunny() 
    
    vis_scale=10
    for cam_center, extrinsic, image in zip(camera_centers, extrinsics, images):
        image = project_points_to_picture(image, object_points, intrinsics, extrinsic)
        # add bunny on the image
        image = project_points_to_picture(image, bunny_coords, intrinsics, extrinsic)
        inv_extrinsic = np.linalg.pinv(extrinsic)
        plot_camera_wireframe(cam_center, vis_scale, inv_extrinsic)
        plot_picture(image, inv_extrinsic, vis_scale)
  
    ipv.show()      

结果如下ipyvolume,人工智能,python,3d,python,matplotlib

总结

以上就是本次实践的内容,小伙伴还有什么问题可以关注私信我哦!文章来源地址https://www.toymoban.com/news/detail-796628.html

到了这里,关于几何视觉的编程实践——相机参数计算——基于ipyvolume的3D可视化——透视投影的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【计算机视觉】对极几何

    我的《计算机视觉》系列参考UC Berkeley的CS180课程,PPT可以在课程主页看到。 在上一篇文章3D视觉中我们介绍了在两个照相机像平面共面的情况下如何计算深度:深度与景物在图片中的位移成反比。这篇文章我们讨论更一般的情形,像平面不必共面,甚至不必平行。假设两个相

    2024年02月06日
    浏览(32)
  • 计算机视觉之三维重建(一)(摄像机几何)

    针孔摄像机 添加屏障: 使用针孔( o =光圈=针孔=摄像机中心),实现现实与成像一对一映射,减少模糊。其中针孔与像平面的距离为 f (焦距);虚拟像平面位于针孔与真实物体之间,与像平面互为倒立关系。 位置映射:利用相似三角形得到现实坐标在像平面上的映射坐标。 光

    2024年02月12日
    浏览(40)
  • 《计算机视觉中的多视图几何》笔记(2)

    本章主要介绍本书必要的几何知识与符号。 简要介绍了平面几何,本书将以代数和几何混合的方式来讲解。 行向量与列向量 本书默认所有向量的都是列向量,比如 x x x ,那么 x T x^T x T 就是行向量。对于一个行向量 ( x , y ) (x,y) ( x , y ) ,我们就有 x = ( x , y ) T x=(x,y)^T x = ( x

    2024年02月09日
    浏览(34)
  • 计算机视觉 图像形成 几何图形和变换 3D到2D投影

            现在我们知道如何表示2D和3D几何图元以及如何在空间上转换它们,我们需要指定如何将 3D图元投影到图像平面上。 我们可以使用线性3D到2D投影矩阵来做到这一点。最简单的模型是正交法,它不需要除法就可以得到最终的(不均匀的)结果。更常用的模型是透视,

    2023年04月08日
    浏览(33)
  • 机器视觉海康工业相机SDK参数设置获取

    视觉人机器视觉培训-缺陷检测项目-食品行业草鸡蛋外观检测 相机参数类型可分为六类,除 command 参数外,每一类都有其对应的设置与获取函数接口。 表 1 参数类型及对应函数接口介绍 *详细函数接口可参考 SDK 手册: ​C:Program Files (x86)MVSDevelopmentDocumentations 相机参数类型

    2024年02月07日
    浏览(73)
  • 深度学习·理论篇(2023版)·第002篇深度学习和计算机视觉中的基础数学知识01:线性变换的定义+基于角度的线性变换案例(坐标变换)+点积和投影+矩阵乘法的几何意义+图形化精讲

    💕 恭喜本博客浏览量达到两百万,CSDN内容合伙人,CSDN人工智能领域实力新星~ 🧡 本文章为2021版本迭代更新版本,在结合有效知识的基础上对文章进行合理的增加,使得整个文章时刻顺应时代需要 🧡 本专栏将通过系统的深度学习实例,从可解释性的角度对深度学习的原理

    2023年04月08日
    浏览(43)
  • 视觉SLAM理论到实践系列(四)——相机模型

    下面是《视觉SLAM十四讲》学习笔记的系列记录的总链接,本人发表这个系列的文章链接均收录于此 下面是专栏地址: 高翔博士的《视觉SLAM14讲》学习笔记的系列记录 相机将三维世界中的坐标点(单位为米)映射到二维图像平面(单位为像素)的过程能够用一个几何模型进行

    2024年02月04日
    浏览(29)
  • 【计算机视觉|人脸建模】PanoHead:360度几何感知的3D全头合成

    本系列博文为深度学习/计算机视觉论文笔记,转载请注明出处 标题: PanoHead: Geometry-Aware 3D Full-Head Synthesis in 360 ∘ ^{circ} ∘ 链接:[2303.13071] PanoHead: Geometry-Aware 3D Full-Head Synthesis in 360 ∘ ^{circ} ∘ (arxiv.org) 最近,在计算机视觉和计算机图形领域,对3D人头的合成和重建引起了

    2024年02月07日
    浏览(41)
  • 【计算机视觉】相机

    我的《计算机视觉》系列参考UC Berkeley的CS180课程,PPT可以在课程主页看到。 成像原理 想要拍一张相片,直接拿胶片对着景物肯定是不行的,因为物体的每一点发出的光线都会到达胶片上的每一点,从而导致胶片上的影像非常模糊,甚至什么都看不出来。因此,我们想建立景

    2024年02月08日
    浏览(29)
  • 【计算机视觉----相机标定】

    相机标定 是计算机视觉中的一个重要问题,它的目的是确定相机的内部参数和外部参数,以建立从相机到图像的映射关系。相机标定的算法通常 分为两个步骤 :第一步是检测棋盘格角点并计算相机的内部参数,第二步是计算相机的外部参数。 第一步:检测棋盘格角点并计算

    2024年02月02日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包