点云_pointcloud_to_camera_可视化

点云和图像

 像高 = EFL*tan (半FOV);EFL为焦距;FOV为视场角
 视场角:Field of View 以光学仪器的镜头为顶点,以被测目标的物像可通过镜头的最大范围的两条边缘构成的夹角,称为视场角
      FOV又分为 HFOV(水平)/VFOV(垂直)/DFOV(对角)
	    HFOV由Focal Length和senor的宽度(W)决定, VFOV由Focal Length和sensor的高度(H)决定。DFOV由Focal Length, W, H共同决定。
 光学畸变(Opt distortion)和TV畸变(TV distortion):

点云的变换-原理和概念介绍

rotx(t) roty(t)  rotz(t) 
def transform_from_rot_trans(R, t):
    """Transforation matrix from rotation matrix and translation vector."""
    R = R.reshape(3, 3)
    t = t.reshape(3, 1)
    return np.vstack((np.hstack([R, t]), [0, 0, 0, 1]))	

### 图像和雷达
   减去主点坐标,从公制坐标(m)转换为像素坐标
   相机校准(camera calibration)
   相机内参和畸变参数的过程是畸变校准(calibration), 拿着相机内参和畸变参数去消除相机图像失真的才叫校正(rectification
     cv2.findChessboardCorners   cv2.drawChessboardCorners  cv2.calibrateCamera  cv2.undistort
 实现3D投影需要从完成这些坐标系的变换:
    激光雷达世界坐标系->
	    相机坐标系(激光雷达和相机都在车辆坐标系中进行位置校准, 从而互相关联)->
	     像平面坐标系->像素坐标系.
	
    像素平面坐标系(u,v)、  像平面坐标系(图像物理坐标第(x,y)、相机坐标系(Xc,Yc,Zc) 和世界坐标系(Xw,Yw,Zw)	
	 
	深度图像的点m(u,v,z)转换到深度相机坐标系下的坐标点M(x,y,z)
	
    欧几里得坐标系->齐次坐标系->欧几里得坐标系.	

摄像机内外参

 摄像机外参:决定摄像机坐标与世界坐标系之间相对位置关系-- 变换矩阵
 摄像机内参:确定摄像机从三维空间到二维图像的投影关系

点云映射

 np.delete(array,obj,axis)
   axis 如果输入为0:按行删除 如果输入为1:按列删除
 np.logical_and
 
# choose illegal index
    bound_x = np.logical_and(index[:, 0] >= 0, index[:, 0] < image_size[0])
    bound_y = np.logical_and(index[:, 1] >= 0, index[:, 1] < image_size[1])
    bound_box = np.logical_and(bound_x, bound_y)
	
# filter point out of canvas
   u,v,z = cam
   u_out = np.logical_or(u<0, u>IMG_W)
   v_out = np.logical_or(v<0, v>IMG_H)
   outlier = np.logical_or(u_out, v_out)
   cam = np.delete(cam,np.where(outlier),axis=1)	

散点图

# 过滤了激光雷达背后深度为负的点云,保留图像宽高内点

plt.scatter()函数用于生成一个scatter散点图
matplotlib.pyplot.scatter(x, y, s=None, c=None, marker=None, 
   cmap=None, norm=None, vmin=None, vmax=None, alpha=None, 
   linewidths=None, verts=None, edgecolors=None, *, data=None, **kwargs)
其中  
   x, y → 散点的坐标,float or array-like, shape (n, )
   s → 散点的面积,float or array-like, shape (n, ), optional
   c → 散点的颜色(默认值为蓝色,'b',其余颜色同plt.plot( ))
   marker → 散点样式(默认值为实心圆,'o',其余样式同plt.plot( ))
   alpha → 散点透明度([0, 1]之间的数,0表示完全透明,1则表示完全不透明)
   linewidths →散点的边缘线宽
   edgecolors → 散点的边缘颜色

参考相机_坐标系中, 再进行图像共面对齐修正, 
然后投影到相机0的像素坐标系中.	
   
 ###说明
 #以列为基准, 删除投影图像点中深度z<0(在投影图像后方)的点 #3xN   
 cam = np.delete(proj_lidar,np.where(proj_lidar[2,:]<0),axis=1)  
 
 cam[:2,:] /= cam[2,:]   # 等价写法 cam[:2] /= cam[2] # 
 前两行元素分布除以第三行元素(归一化到相机坐标系z=1平面)(x=x/z, y =y/z)

代码示例

import open3d as o3d
import numpy as np
import matplotlib.pyplot as plt
import json


if __name__ == "__main__":
    ##############################################################
    calib_path = r"./config.json"
    with open(calib_path,"r") as f:
        calib_config = json.loads(f.read())
        img_base_calib = calib_config.get("config")
		# Extrinsics
        img_Tx = img_base_calib.get("img_Tx")
		# Intrinsics 
        img_intrinsics = img_base_calib.get("img_intrinsics")
        img_TX_cam_m = img_base_calib.get("img_TX_cam_m")
    ##############################################################
    pcd_path =  r"./123.pcd"
    pcd = o3d.io.read_point_cloud(pcd_path)
    pcd_points = np.asarray(pcd.points)
    # reshape to homogeneous 4*N shape
    point_hom = np.vstack([pcd_points[:,:3].T, [1]*len(pcd_points)])
    print("Ppint_in_homo",point_hom.shape)

    ##############################################################
    img_path =  r"./123.jpg"
    #读入的 图像形状是(H,W,C),读入的顺序是RGB
    png = plt.imread(img_path)
    IMG_H,IMG_W,_ = png.shape
 
    # ##############################################################
    # ###转换
    # # # 世界坐标系 --> 相机坐标系内的坐标
    camera_array = np.dot(img_Tx, point_hom)
    # #删除深度x=0的点避免前后覆盖
    camera_array = np.delete(camera_array,np.where(camera_array[2,:]<0),axis=1) 
    # 转到相机上 
    ## 归一化到相机坐标系z=1平面 --> 像平面坐标系
    point_Came_temp = camera_array / camera_array[2, :]
    deep_color = camera_array[2,:]
    ## -->像素平面坐标系
    points_pixel = np.dot(img_intrinsics, point_Came_temp[0:3,:])

    # filter point out of canvas
    u,v,z = points_pixel[0:3,]
    u_out = np.logical_or(u<0, u>IMG_W)
    v_out = np.logical_or(v<0, v>IMG_H)
    outlier = np.logical_or(u_out, v_out)
    points_pixel_limit = np.delete(points_pixel,np.where(outlier),axis=1)
    u,v,z = points_pixel_limit[0:3,]
    # to plot format
    points_pixel = points_pixel.T
    points_pixel = points_pixel.astype(np.int64)
    
    #plot the data
    plt.axis([0,IMG_W,IMG_H,0])
    plt.imshow(png)
    plt.scatter(points_pixel[:, 0], points_pixel[:, 1], s=0.5, c=deep_color , cmap='rainbow_points', alpha=0.7)
    # imshow需要的是RGB顺序
    plt.show()

图像像素和点云点之间的匹配

准确描述和检测 2D 和 3D 关键点对于建立跨图像和点云的对应关系至关重要
建立粗粒度匹配-- 建立细粒度的对应关系

联合特征描述和关键点检测

示例

def points_lidar2image(points, tr_velo_to_cam, P2):
    '''
    points: shape=(N, 8, 3) 
    tr_velo_to_cam: shape=(4, 4)
    P2: shape=(4, 4)
    return: shape=(N, 8, 2)
    '''
    # points = points[:, :, [1, 2, 0]]
    extended_points = np.pad(points, ((0, 0), (0, 0), (0, 1)), 'constant', constant_values=1.0) # (N, 8, 4)
	# (N, 8, 4)
    camera_points = np.dot(extended_points , tr_velo_to_cam.T)
    # (N, 8, 4)
    image_points = np.dot(camera_points , P2.T)   
    #  删除投影图像点中深度z<0(在投影图像后方)的点
    image_points = np.delete(image_points,np.where(image_points[:, :, 2:3]<0),axis=0)
    # 归一化到相机坐标系z=1平面 --> 像平面坐标系	
    image_points = image_points[:, :, :2] / image_points[:, :, 2:3]  
    return image_points

if __name__ == "__main__":
     bboxes = bbox3d2corners(gt_3d_box)
	 image_points =  points_lidar2image(bboxes, RT,P2=K)
	 gt_img = vis_img_3d(img_png, image_points,  rt=True)   	

参考

https://ksimek.github.io/perspective_camera_toy.html	
pykitti工具 KITTI 数据集oxts 文件读取 https://blog.csdn.net/qq_26623879/article/details/103595531	
https://github.com/azureology/kitti-velo2cam
Lidar to camera projection of KITTI https://github.com/utiasSTARS/pykitti/blob/master/pykitti/utils.py
根据标定的相机和激光雷达的外参,使用python将3D激光雷达点云映射到图像上 https://juejin.cn/post/7094064714778411021
参数功能可视化 https://ksimek.github.io/perspective_camera_toy.html
 点云投影 https://blog.csdn.net/shyjhyp11/article/details/113180123
   3D点云 (Lidar)检测入门篇 - PointPillars PyTorch实现 https://zhuanlan.zhihu.com/p/521277176
 【ROS】利用ROS将KITTI数据集点云数据投影到2D图像 https://copyfuture.com/blogs-details/202207150551110747
posted @ 2022-11-15 18:07  辰令  阅读(774)  评论(0编辑  收藏  举报