"); //-->
图17只能说类似,并不准确,后面也会具体说明的,这个深度信息可以构建一个伪3D模型(Point Cloud点云模式),类似图15:
图18看着还行,但把这个3D模型转到BEV俯视角下,估计亲娘都认不出来了:
图19拍扁后结合特征Feature再做一次语义识别,形成:
图20这个就是喜闻乐见的BEV图了。以上是对LSS的直观认知,算法层面是如何实现的?先给单个相机可拍摄的范围构建一个立方体模样的铁丝笼子(高8宽22深41),祭出大杀器Blender:
图21这里是示意图,不要纠结于格子的数量和尺寸。这个3D网格代表的是一路相机的视锥体(Frustum),前面贴过视锥体的形状(图9),这里变形成立方体,在相机空间里看这个照片和这个立体网格的关系就是:
图22右边是个正对着网格立方体的相机示意图,相片提取深度后(深度图的实际像素尺寸是高8宽22):
图23把这个深度图按照每个像素的深度沿着红线方向展开(Lift)后:
图24可以看到,部分深度像素已经超出了视锥体的范围,因为LSS一开始就假设了这么个有限范围的笼子,超出部分直接过滤掉。这里必须提醒一下:LSS并不是直接算出每个像素的深度,而是推理出每个像素可能处于笼子里每个格子的概率,图24是已经通过Softmax提取出每个像素最有可能位于哪个格子,然后把它装进对应格子的示意结果,便于理解,更准确的描述如下:
图25在图25中选取深度图的某个像素(红色格子,事实上LSS的深度图分辨率是很小的,默认只有8*22像素,所以这里可以用一个格子当做一个像素),它隶属于笼子下方边沿的一条深度格子(这条格子其实就代表相机沿着深度看向远方的一条视线):
图26图25中的那个红色的深度像素,沿着图26这条视线格子的概率分布就是:
图27黄线的起伏表示2D深度图像素在Lift后沿着视线3D深度的概率分布(Depth Distribution,我这是示意性得画法,不是严格按照实际数据做的)。等价于LSS论文里的这张图:
图28
LSS中构建立方笼子的代码位于:
class LiftSplatShoot(nn.Module):
def __init__(self, grid_conf, data_aug_conf, outC):
self.frustum = self.create_frustum()
def create_frustum(self):
# D x H x W x 3
frustum = torch.stack((xs, ys, ds), -1)
return nn.Parameter(frustum, requires_grad=False)
def get_geometry(self, rots, trans, intrins, post_rots, post_trans):
"""Determine the (x,y,z) locations (in the ego frame)
of the points in the point cloud.
Returns B x N x D x H/downsample x W/downsample x 3
"""
B, N, _ = trans.shape
# undo post-transformation
# B x N x D x H x W x 3
points = self.frustum - post_trans.view(B, N, 1, 1, 1, 3)
points = torch.inverse(post_rots).view(B, N, 1, 1, 1, 3, 3).matmul(points.unsqueeze(-1))
# cam_to_ego
points = torch.cat((points[:, :, :, :, :, :2] * points[:, :, :, :, :, 2:3],
points[:, :, :, :, :, 2:3]
), 5)
combine = rots.matmul(torch.inverse(intrins))
points = combine.view(B, N, 1, 1, 1, 3, 3).matmul(points).squeeze(-1)
points += trans.view(B, N, 1, 1, 1, 3)
return points
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。