Python 光线与体素求交实现与优化


一、项目场景

相机光线与根据点云得到的体素求交,得到两个交点(近点、远点,或用距离表示)。在接下来的内容中,我会根据我的经历,介绍光线与体素求交的实现与可视化。因为走了一些弯路,所以文章会比较长,重点在于优化过程的思考和尝试,怎么去一步步提高效率,得到更好的结果。

最终效果:
请添加图片描述


二、光线与体素表示

光线与体素表示基于open3d。

1. 根据相机参数生成光线

一条光线可以由一个光源点和一个光线方向向量表示。

光线生成参考NeuS

  1. 针对单影像,可设置下采样分辨率
def gen_rays_at(frame, W, H, resolution_level=1):
    """
    Generate rays at world space from one camera.
    """
    l = resolution_level
    tx = torch.linspace(0, W - 1, W // l)
    ty = torch.linspace(0, H - 1, H // l)
    pixels_x, pixels_y = torch.meshgrid(tx, ty)
    p = torch.stack([pixels_x, pixels_y, torch.ones_like(pixels_y)], dim=-1)  # W, H, 3
    intrinsics = torch.from_numpy(np.array(frame["intrinsics"]).astype(np.float32)).cpu()
    intrinsics_inv = torch.inverse(intrinsics)
    p = torch.matmul(intrinsics_inv[None, None, :3, :3], p[:, :, :, None]).squeeze()  # W, H, 3
    rays_v = p / torch.linalg.norm(p, ord=2, dim=-1, keepdim=True)  # W, H, 3
    pose = torch.from_numpy(np.array(frame["camtoworld"]).astype(np.float32)).cpu()
    rays_v = torch.matmul(pose[None, None, :3, :3], rays_v[:, :, :, None]).squeeze()  # W, H, 3
    rays_o = pose[None, None, :3, 3].expand(rays_v.shape)  # W, H, 3
    return rays_o.transpose(0, 1), rays_v.transpose(0, 1)
  1. 针对单影像,随机采样一定数量光线
def gen_random_rays_at(frame, W, H, batch_size):
    """
    Generate random rays at world space from one camera.
    """
    # 随机取batch_size个像素
    pixels_x = np.random.randint(low=0, high=W, size=[batch_size])
    pixels_y = np.random.randint(low=0, high=H, size=[batch_size])
    p = np.vstack([pixels_x, pixels_y, np.ones_like(pixels_y)]).transpose()
    p = torch.from_numpy(p.astype(np.float32))
    intrinsics = torch.from_numpy(np.array(frame["intrinsics"]).astype(np.float32))
    intrinsics_inv = torch.inverse(intrinsics)
    p = torch.matmul(intrinsics_inv[None, :3, :3], p[:, :, None]).squeeze()  # W, H, 3
    rays_v = p / torch.linalg.norm(p, ord=2, dim=-1, keepdim=True)  # W, H, 3
    pose = torch.from_numpy(np.array(frame["camtoworld"]).astype(np.float32))
    rays_v = torch.matmul(pose[None, :3, :3], rays_v[:, :, None]).squeeze()  # W, H, 3
    rays_o = pose[None, :3, 3].expand(rays_v.shape)  # W, H, 3
    return rays_o, rays_v
  1. 在以上随机采样基础上,实现多影像的随机采样,采样数为batch_size×n。
def gen_random_rays(frames, W, H, batch_size,n):
    """
    Generate random rays at world space from one camera.
    """
    # 随机取batch_size个像素
    pixels_x = np.random.randint(low=0, high=W, size=[batch_size])
    pixels_y = np.random.randint(low=0, high=H, size=[batch_size])

    ray_O=[]
    ray_V=[]
    for i in range(n):
        frame=frames[i]
        p = np.vstack([pixels_x, pixels_y, np.ones_like(pixels_y)]).transpose()
        p = torch.from_numpy(p.astype(np.float32))
        intrinsics = torch.from_numpy(np.array(frame["intrinsics"]).astype(np.float32))
        intrinsics_inv = torch.inverse(intrinsics)
        p = torch.matmul(intrinsics_inv[None, :3, :3], p[:, :, None]).squeeze()  # W, H, 3
        rays_v = p / torch.linalg.norm(p, ord=2, dim=-1, keepdim=True)  # W, H, 3
        pose = torch.from_numpy(np.array(frame["camtoworld"]).astype(np.float32))
        rays_v = torch.matmul(pose[None, :3, :3], rays_v[:, :, None]).squeeze()  # W, H, 3
        rays_o = pose[None, :3, 3].expand(rays_v.shape)  # W, H, 3
        ray_O.append(rays_o)
        ray_V.append(rays_v)

    Rays_V = torch.cat(ray_V, dim=0)
    Rays_O = torch.cat(ray_O, dim=0)
    return Rays_O, Rays_V

2. 光线的可视化

基于open3d对光线进行可视化,光线较多,为了较好的可视化效果,随机赋予颜色。

def get_line_set(rays_o, rays_v):
    """通过光线原点和方向求open3d的光线集"""

    # 定义直线的起点和终点
    end_point = rays_v  + rays_o
    rays_all = np.concatenate((rays_o, end_point), axis=0)

    # lines属性是一个 Nx2 的 numpy 数组,其中每行表示一条直线的起点和终点的索引。
    start_end = np.zeros(shape=(len(rays_v), 2))
    start_end[:, 1] = np.arange(len(rays_v))
    start_end[:, 0] = np.arange(len(rays_v))+len(rays_v)
    # 创建一个 LineSet 对象,并将其添加到场景中
    line_set = o3d.geometry.LineSet(points=o3d.utility.Vector3dVector(rays_all),
                                    lines=o3d.utility.Vector2iVector(start_end))
    colors = np.random.rand(len(start_end), 3)
    line_set.colors = o3d.utility.Vector3dVector(colors)

    o3d.visualization.draw_geometries([line_set])
  1. 单影像下采样
    在这里插入图片描述

  2. 单影像随机采样
    在这里插入图片描述

  3. 多影像随机采样

在这里插入图片描述

3. 根据点云获取体素及可视化

体素可以由一个2×3的数组表示,其中存放体素的边界:[[X_min,Y_min,Z_min],[X_max,Y_max,Z_max]]。
这个自己实现也很简单,但根据可视化的需求,选择用open3d的voxel_grid类来表示。
传入的点云最好是含有RGB颜色信息的,在可视化时效果会好很多,否则默认颜色为黑色,很难看出边界。

def get_voxel_grid(pts, voxel_size=0.5):
    """通过点云求open3d的体素集"""

    # 生成点云数据
    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(pts[:, :3])

    # 加入颜色信息
    if (len(pts[0, :]) > 5):
        color = pts[:, 3:6] / 255.0
        pcd.colors = o3d.utility.Vector3dVector(color)  # 将颜色值归一化到[0, 1]

    # 生成体素集
    voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd,
                                                                voxel_size=voxel_size)

    return voxel_grid
  1. 含有颜色信息的点云生成的体素
    在这里插入图片描述
  2. 没有颜色信息的点云生成的体素

在这里插入图片描述

4. 从voxel_grid对象中拿出体素

根据 voxel_grid 类的属性和方法计算每个体素的真实边界值。

    voxels_grid = get_voxel_grid(pts, voxel_size)
    # 获取所有体素的小边界,这里返回的是从0开始的三个维度的体素索引值,第一个体素就是[0,0,0]
    min_box = voxels_grid.get_min_bound()
    # 获取体素的尺寸
    voxels = voxels_grid.get_voxels()
	# 计算所有体素的真实边界值
    voxels_list = []
    for i in range(len(voxels)):
        voxel_min = min_box + voxels[i].grid_index * voxel_size
        voxel_max = voxel_min + voxel_size
        voxels_list.append(np.array([voxel_min, voxel_max]))

三、光线与体素求交的简单实现

光线=射线,体素=立方体,那么就是求一条线与立方体的交点,一个立方体6个面,分别求射线与这6个面的交点就完事儿了。

由于这个项目场景下,光线是相机光线,体素是相机影像重建出的点云生成的,如果光线与体素相交,那么交点一定在正方向上,也就是射线上,所以在实现的时候,光线是用直线来表达的,没有考虑反方向的情况。(当然,如果要考虑也很简单,交点到光源点的向量与光线方向向量进行比较就OK了。)

先进行单根光线与单个体素的求交运算。在计算的时候,由于体素的6个面是与坐标系对齐的(axis-aligned bounding boxes , AABB),所以计算还是比较简单的,在以下的实现中,先与较小的3个体素边界值所在的面计算得到3个交点,再与较大的3个体素边界值所在的面计算得到3个交点,最后得到6个交点,再判断这些交点是否在体素上。正常情况下,如果直线与体素相交,会有两个交点满足情况,如果不相交,则所有交点都不满足情况。特殊情况(仅与体素的一个顶点相交或与体素的边重合)发生概率太小,不考虑。

def get_intersection_of_ray_and_voxel(line_start,ray_dir, voxel):
    """求一条直线与一个体素的交点"""

    # 计算6个面与直线的交点
    intersection = []
    for i in range(len(voxel)):
        x = voxel[i, 0]
        if(ray_dir[0]!=0):
            y = ((x - line_start[0]) / ray_dir[0]) * ray_dir[1] + line_start[1]
            z = ((x - line_start[0]) / ray_dir[0]) * ray_dir[2] + line_start[2]
        else:
            y=np.inf
            z=np.inf
        intersection.append([x, y, z])
        y = voxel[i, 1]
        if (ray_dir[1] != 0):
            x = ((y - line_start[1]) / ray_dir[1]) * ray_dir[0] + line_start[0]
            z = ((y - line_start[1]) / ray_dir[1]) * ray_dir[2] + line_start[2]
        else:
            x = np.inf
            z = np.inf
        intersection.append([x, y, z])
        z = voxel[i, 2]
        if (ray_dir[2] != 0):
            x = ((z - line_start[2]) / ray_dir[2]) * ray_dir[0] + line_start[0]
            y = ((z - line_start[2]) / ray_dir[2]) * ray_dir[1] + line_start[1]
        else:
            x = np.inf
            y = np.inf
        intersection.append([x, y, z])

	
    intersection=np.array(intersection)
 
    # 筛选可用的交点
    rule = (intersection[:, 0] >= voxel[0, 0]) * (intersection[:, 0] <= voxel[1, 0]) * \
           (intersection[:, 1] >= voxel[0, 1]) * (intersection[:, 1] <= voxel[1, 1]) * \
           (intersection[:, 2] >= voxel[0, 2]) * (intersection[:, 2] <= voxel[1, 2])
   
    intersection = intersection[rule]
    if(len(intersection)==2):
        return intersection
    else:
        return []

要得到所有的交点,需要两层 for 循环。根据一条直线与所有体素的交点来判断直线与体素集是否存在交点,如果与多个体素相交,保留最近的交点和最远的交点。

def get_intersection_of_rays_and_voxels(rays_o,rays_v,voxels):

    """求直线集与像素集的交点"""

    ip_all=[]
    # 记录与体素没有交点的直线数量
    miss=0
    for i in tqdm(range(len(rays_v))):
        ip=[]
        for j in range(len(voxels)):
            intersection=get_intersection_of_ray_and_voxel(rays_o[i],rays_v[i],voxels[j])
            if(len(intersection)!=0):
                ip.extend(intersection)
        if(len(ip)>2):
            distance=np.linalg.norm( ip-rays_o[i],axis=1)
            max_ind=np.argmax(distance)
            min_ind=np.argmin(distance)
            ip_all.append(np.array([ip[min_ind],ip[max_ind]]))
        elif(len(ip)==2):
            ip_all.append(ip)
        else:
            print("no intersection!")
            miss+=1
            
    print(miss)
    return ip_all


好,以上就完成了整个光线与体素的求交运算。在光线数量和体素数量较少的情况下,以上代码是可以满足需求的,但很不幸,由于需要进行深度学习的训练,需要需要进行很多次大量的计算,目前的实现还比较耗时,需要进一步优化。先看看目前的耗时。

设置两个组,分别是10240条光线与0.05大小的体素求交,另一个是1024条光线与0.2大小的体素集求交。

表一 优化前耗时
实验
光线数 1024 10240
体素大小 0.2 0.05
体素个数 153 2238
耗时 3.672s 9m 10.839s

四、优化!!!

1. 优化之减少重复计算

很容易发现,一个个去拿出每个体素,然后用其6个面与直线求交点,会导致有很多面被重复计算,接下来就进行减少重复面计算的优化。

为了减少重复面的计算,这次我们不能一个个去拿出单个体素了,而是要将所有体素的所有面拿出来,一次性的求出一条直线与所有面的交点。

def get_intersection_of_ray_and_voxels(rays_o,rays_v, voxels):

    """求一条直线与体素集的交点,优化,减少重复面的计算"""

    # 拿出体素的边界,构成x,y,z三个面
    vos=np.array(voxels).reshape(-1,3)
	# 在这里对所有的xyz面取唯一,避免后续面的重复计算
    plane_x=np.unique(vos[:,0])
    plane_y=np.unique(vos[:,1])
    plane_z=np.unique(vos[:,2])
	
    y = ((plane_x - rays_o[0]) / rays_v[0]) * rays_v[1] + rays_o[1]
    z = ((plane_x - rays_o[0]) / rays_v[0]) * rays_v[2] + rays_o[2]

    x_intersection = np.stack([plane_x, y, z], axis=1)

    x = ((plane_y - rays_o[1]) / rays_v[1]) * rays_v[0] + rays_o[0]
    z = ((plane_y - rays_o[1]) / rays_v[1]) * rays_v[2] + rays_o[2]

    y_intersection = np.stack([x, plane_y, z], axis=1)

    x = ((plane_z - rays_o[2]) / rays_v[2]) * rays_v[0] + rays_o[0]
    y = ((plane_z - rays_o[2]) / rays_v[2]) * rays_v[1] + rays_o[1]

    z_intersection = np.stack([x, y, plane_z],axis=1)
    
    # 这里得到了一条直线与所有体素面的交点
    intersection = np.vstack([x_intersection, y_intersection, z_intersection])
    
    ips=[]
	# 这里进行交点是否在体素上的判别
    for j in range(len(voxels)):
        rule = (intersection[:, 0] >= voxels[j][0, 0]) * (intersection[:, 0] <= voxels[j][1, 0]) * \
               (intersection[:, 1] >= voxels[j][0, 1]) * (intersection[:, 1] <= voxels[j][1, 1]) * \
               (intersection[:, 2] >= voxels[j][0, 2]) * (intersection[:, 2] <= voxels[j][1, 2])
        ip = intersection[rule]
        if (len(ip) >= 2):
            ips.extend(ip)
    return ips

同样参数下耗时:

表二 优化之减少重复面计算
实验
光线数 1024 10240
体素大小 0.2 0.05
体素个数 153 2238
耗时 1.223s 2m 58.398s

可以看到这部分重复计算还是很多的。但是目前的速度仍满足不了要求,还要进一步优化。

2. 优化之GPU加速

def get_intersection_of_ray_and_voxels_gpu(rays_o, rays_v, voxels):
    """求一条直线与体素集的交点,优化,GPU加速"""

	# 主要就是用cuda上的tensor代替cpu上的array进行计算
	
	voxels=torch.tensor(np.array(voxels).astype(np.float32)).to("cuda:0")  # 实际测速的时候,是先执行这条语句再调用的方法
 
    vos = voxels.reshape(-1, 3)
    
    plane_x = torch.unique(vos[:, 0])
    plane_y = torch.unique(vos[:, 1])
    plane_z = torch.unique(vos[:, 2])


    y = ((plane_x - rays_o[0]) / rays_v[0]) * rays_v[1] + rays_o[1]
    z = ((plane_x - rays_o[0]) / rays_v[0]) * rays_v[2] + rays_o[2]
    x_intersection = torch.stack([plane_x, y, z], dim=1)

    x = ((plane_y - rays_o[1]) / rays_v[1]) * rays_v[0] + rays_o[0]
    z = ((plane_y - rays_o[1]) / rays_v[1]) * rays_v[2] + rays_o[2]
    y_intersection = torch.stack([x, plane_y, z], dim=1)

    x = ((plane_z - rays_o[2]) / rays_v[2]) * rays_v[0] + rays_o[0]
    y = ((plane_z - rays_o[2]) / rays_v[2]) * rays_v[1] + rays_o[1]
    z_intersection = torch.stack([x, y, plane_z], dim=1)
    
    intersection= torch.cat([x_intersection ,y_intersection,z_intersection ], dim=0)
    
    ips = []
    for j in range(len(voxels)):

        rule = (intersection[:, 0] >= voxels[j][0, 0]) * (intersection[:, 0] <= voxels[j][1, 0]) * \
               (intersection[:, 1] >= voxels[j][0, 1]) * (intersection[:, 1] <= voxels[j][1, 1]) * \
               (intersection[:, 2] >= voxels[j][0, 2]) * (intersection[:, 2] <= voxels[j][1, 2])

        ip = intersection[rule]
        if (len(ip) >= 2):
            ips.extend(ip)

    if(len(ips)>0):
        return torch.stack(ips,dim=0)
    else:
        return []

同样参数下耗时:

表三 优化之GPU加速(失败了)
实验
光线数 1024 10240
体素大小 0.2 0.05
体素个数 153 2238
耗时 31.563s >1h

在小参数下,耗时还比完全没有优化的版本大,看来这就是所谓的负优化了。大参数的估算了一下,跑了5%花了3分多,跑完肯定超过1小时了。

显然,这份代码以及数据没有利用到GPU的性能(不是密集型数据,在后半部分逻辑运算耗费太多时间)。

3. 优化之多进程加速

使用多进程来进行优化。刚重新问了一嘴ChatGPT多线程与多进程的使用场景:
在这里插入图片描述

小优化:
由于存在与所有体素都不相交的光线,我们可以给其赋一个默认值,由于不同光线方向不一致,所以不能设定默认三维坐标,可以根据交点到光源点的长度设定。
在这里还想到,既然在判断所有交点中的近点和远点时需要计算交点到光源点的距离,那么不如直接用最短距离和最远距离来表示。在后续需要用到交点进行可视化的时候再计算出交点就完事儿了。

多进程实现:

from multiprocessing import Process,Queue

def single_procee(arrs,rays_o,rays_v,voxels, result_queue):
	'''单个进程要做的事'''
    miss=0
    near_far1=[]
    for i in tqdm(arrs):
        ips1 = get_intersection_of_ray_and_voxels(rays_o[i], rays_v[i], voxels)
        if (len(ips1) > 0):
            distance1 = np.linalg.norm(ips1 - rays_o[i], axis=1)
            near_far1.append([np.min(distance1), np.max(distance1)])
        else:
            near_far1.append([1.2, 3])
            miss += 1
    result_queue.put([near_far1,miss])


def get_intersection_of_rays_and_voxels_multi_process(rays_o,rays_v,voxels):

    """求直线集与体素集的交点,优化,多进程"""

    arr=range(len(rays_v))
    # 索引均分为4组
    arrs=np.array_split(arr,4)
    
    result_queue1=Queue()
    result_queue2=Queue()
    result_queue3=Queue()
    result_queue4=Queue()

    p1 = Process(target=single_procee, args=(arrs[0],rays_o,rays_v,voxels,result_queue1))
    p2 = Process(target=single_procee, args=(arrs[1],rays_o,rays_v,voxels,result_queue2))
    p3 = Process(target=single_procee, args=(arrs[2],rays_o,rays_v,voxels,result_queue3))
    p4 = Process(target=single_procee, args=(arrs[3],rays_o,rays_v,voxels,result_queue4))

    p1.start()
    p2.start()
    p3.start()
    p4.start()

    [near_far1, miss1] = result_queue1.get()
    [near_far2, miss2] = result_queue2.get()
    [near_far3, miss3] = result_queue3.get()
    [near_far4, miss4] = result_queue4.get()

    p1.join()
    p2.join()
    p3.join()
    p4.join()

    miss=miss1+miss2+miss3+miss4
    near_far=near_far1+near_far2+near_far3+near_far4

    return near_far

同样参数下耗时:

表四 优化之多进程
实验
光线数 1024 10240
体素大小 0.2 0.05
体素个数 153 2238
耗时 10.725s 1m 0.392s

可以看到,在计算量较大时,多进程有明显的加速效果,但是计算量较小的时候,多进程会更耗时。但是,这两个情况我都需要处理,而且,这大参数下的加速效果也仍然不尽人意。


4. 优化之减少冗余计算

在GPU加速实验时发现,在判别交点是否在体素上、在哪个体素上是很费时的。所以就在想着换一种思路,先找出与光线相交的体素,再用这些少量的体素去计算交点,减少冗余计算。

说干就干,实现起来也比较简单。计算所有体素中心到光线的距离,如果某个距离小于体素的对角线的一半,那么说明光线是穿过这个体素的。

梳理一下步骤:

  1. 计算所有体素中心坐标
  2. 计算体素中心坐标到单条光线的距离
  3. 根据距离阈值计算与光线相交的体素
  4. 用这些少量的与光线确定相交的体素与光线求交点
  5. 处理多个交点的情况,取最近点和最远点
  6. 循环处理所有光线
  7. 处理与光线所有体素没有交点的情况

第二步中,计算体素中心点到光线的距离代码如下:

def dist_to_ray(pts, ray_o, ray_v):
    """
    计算点集到光线的距离
    """
    ray_o = ray_o.reshape(1, -1)
    line_dir_len2 = np.sum(ray_v ** 2)
    pts_proj = ray_o + np.dot(pts - ray_o, ray_v.reshape(-1, 1)) / line_dir_len2 * ray_v.reshape(1, -1)
    return np.sqrt(np.sum((pts - pts_proj) ** 2, axis=1))

第四步中,由于与光线相交的体素很少,所以不再使用拿出所有面取唯一的方法,参考了一份实时渲染光线追踪中的实现,对每个体素分别处理,其中返回值若满足tNear<tFar,则光线与体素相交。

def ray_aabb(ray_o, ray_v, boxMin, boxMax):
    """
    光线与体素交点到光线原点的距离
    :param ray_o: 光线原点
    :param ray_v: 光线方向
    :param boxMin: 体素小边界
    :param boxMax: 体素大边界
    :return: 
    """
    tMin = (boxMin - ray_o) / ray_v
    tMax = (boxMax - ray_o) / ray_v
    t1 = np.minimum(tMin, tMax)
    t2 = np.maximum(tMin, tMax)
    tNear = np.max([t1[0], t1[1], t1[2]])
    tFar = np.min([t2[0], t2[1], t2[2]])
    return tNear, tFar

第七步中,可以给定一个默认值,但是这样在不同场景下需要调整这个默认值,所以就用相交情况的最值加上一个缓冲值来自动设定。如果光线与所有体素都不相交,那么记录下光线的索引,同时先赋值一个较大的值(近距离给极大值,远距离给极小值)占位,然后计算所有近距离的最小值和所有远距离的最大值,加上缓冲值赋值到相应索引中。

完整步骤如下:

def ray_near_far_by_voxels(rays_o, rays_v, voxels_grid):
    """求光线与体素集的交点的近点和远点"""
    
    # 记录每条光线与体素交点的最近距离和最远距离
    N_F = []
	# 记录与体素不相交的光线数量
    miss = 0
    # 记录与体素不相交的光线索引
    ind_no_intersection=[]

    # 取出voxels_grid相关属性
    min_box = voxels_grid.get_min_bound()
    voxels = voxels_grid.get_voxels()
    voxel_size = voxels_grid.voxel_size
  
    grid_index = np.array([voxel.grid_index for voxel in voxels])
    voxel_min = min_box + grid_index * voxel_size
    voxel_max = voxel_min + voxel_size
    voxels = np.array([[a, b] for a, b in zip(voxel_min, voxel_max)])
    # 1. 计算所有体素中心坐标
    center = voxel_min + voxel_size * 0.5
    # 如果光线与体素相交,那么该体素中心到光线的距离小于体素尺寸的sqrt(3)/2
    dist_limit = voxel_size * np.sqrt(3) * 0.5
	
    for i in tqdm(range(len(rays_v))):
        # 根据体素中心到光线的距离筛选与光线相交的体素
        # 2. 计算体素中心坐标到单条光线的距离
        dist = dist_to_ray(center, rays_o[i].reshape(-1, 3), rays_v[i].reshape(-1, 3))
        index_voxel = np.where(dist <= dist_limit)[0]
        # 3. 根据距离阈值计算与光线相交的体素
        voxels_select = voxels[index_voxel]
        near = []
        far = []
        
        # 4. 用这些少量的与光线确定相交的体素与光线求交点距离最值
        for voxel in voxels_select:
            tNear, tFar = ray_aabb(rays_o[i], rays_v[i], voxel[0], voxel[1])
            if (tNear < tFar):
                near.append(tNear)
                far.append(tFar)
        # 5. 处理多个交点的情况,取最近点和最远点
        if (len(near) > 0):
            N_F.append([min(near), max(far)])
        else:
            miss += 1
            ind_no_intersection.append(i)
            N_F.append([999,-999])
        # 6. 循环处理所有光线
        
    # 7. 处理与光线所有体素没有交点的情况
    N_F=np.array(N_F)
    min_near=np.min(N_F[:,0])-0.1
    max_far=np.max(N_F[:,1])+0.1
    N_F[ind_no_intersection,0]=min_near
    N_F[ind_no_intersection,1]=max_far
    
    return N_F.astype(np.float32)

同样参数下耗时:

表五 优化之减少冗余计算
实验
光线数 1024 10240
体素大小 0.2 0.05
体素个数 153 2238
耗时 0.075s 1.434s

效果喜人!

表六 优化前后耗时比较
实验
光线数 1024 10240
体素大小 0.2 0.05
体素个数 153 2238
优化前耗时 3.672s 9m 10.839s
优化后耗时 0.075s 1.434s

这才是真正的优化嘛,这个速度就可以满足训练需要了。

可视化检查一下:
在这里插入图片描述
不显示体素,检查交点没有在体素上。
在这里插入图片描述
没有问题,与体素相交的光线的交点都在体素表面上,没有相交的直线根据最值和缓冲值计算出了最近最远距离,目标达成。


打完收工。

猜你喜欢

转载自blog.csdn.net/m0_50910915/article/details/130498253