使用SageMaker Ground Truth注释密集点云数据

源节点: 834877

自动驾驶汽车公司通常使用 激光雷达 传感器,以对车辆周围的环境产生3D理解。 例如,他们将LiDAR传感器安装在车辆上,以连续捕获周围3D环境的时间点快照。 LiDAR传感器的输出是一系列3D点云帧(典型捕获速率是每秒10帧)。 亚马逊SageMaker地面真相 可以轻松地在单个3D框架中或跨一系列3D点云框架标记对象,以构建机器学习(ML)训练数据集。 Ground Truth还支持摄像机和LiDAR数据与多达XNUMX个摄像机输入的传感器融合。

随着LiDAR传感器变得越来越容易访问和更具成本效益,客户越来越多地在新空间中使用点云数据,例如机器人技术,信号映射和增强现实。 一些新的移动设备甚至包括LiDAR传感器,其中之一为这篇文章提供了数据! LiDAR传感器的可用性不断增长,越来越关注用于ML任务的点云数据,例如3D对象检测和跟踪,3D分割,3D对象合成和重建,甚至使用3D数据来验证2D深度估计。

尽管密集点云数据中包含丰富的信息(超过1万个点云),但由于标签工作站的内存通常有限,并且图形功能和注释器往往在地理上分布,这给标签带来了挑战,这可能会增加延迟。 尽管在贴标机的工作站中可以渲染大量点,但是由于在处理数百万个点云时的渲染时间会降低贴标机的吞吐量,从而大大增加了贴标成本并降低了效率。

减少这些成本和时间的一种方法是将点云标记作业转换为更小,更易于呈现的任务,这些任务保留了大多数点云的原始信息以供注释。 我们将这些方法广泛地称为 下采样, 相近 下采样 在信号处理领域。 像在信号处理领域一样,点云下采样方法会尝试在保留原始点云保真度的同时删除点。 注释降采样的点云时,可以使用输出3D 长方体 目标追踪目标检测 直接在全尺寸点云上进行训练或验证的任务,对模型性能的影响很小甚至没有影响,同时节省了标注时间。 对于其他方式,例如 语义分割 其中每个点都有自己的标签,您可以使用降采样的标签来预测原始点云中每个点上的标签,从而可以在标签成本(以及因此标签数据的数量)与少量标签成本之间进行权衡全尺寸点云中点的错误分类。

在本文中,我们将逐步介绍如何执行下采样技术以准备要标记的点云数据,然后演示如何使用简单的ML模型通过一些样本推断来对输出标签进行上采样以应用于原始全尺寸数据集。 为此,我们使用地面真理和 Amazon SageMaker笔记本实例 执行标签以及所有预处理和后处理步骤。

数据

我们在这篇文章中使用的数据是使用iPhone3 Pro上的12D扫描仪应用程序生成的公寓屋顶的扫描图。 该应用程序允许您使用移动设备上的内置LiDAR扫描仪来扫描给定区域并导出点云文件。 在这种情况下,点云数据为xyzrgb格式,这是地面真理点云的可接受格式。 有关地面真理点云中允许的数据类型的更多信息,请参见 接受的原始3D数据格式.

下图显示了我们的3D扫描。

方法

我们首先介绍几种减少标记点云的数据集大小的方法:平铺,固定步长样本和体素均值。 我们演示了为什么下采样技术可以在不显着牺牲标注质量的情况下增加标签的吞吐量,然后我们演示如何使用下采样点云上创建的标签,并通过上采样方法将其应用于原始点云。

下采样方法

下采样是从完整的数据集中获取数据,或者从中选择一个点子集进行标记,或者创建一组代表性的新点,这些点不一定在原始数据集中,但足够接近以进行标记。

平铺

一种幼稚的方法是将点云空间分成3D多维数据集,也称为 体素,(例如)500,000个点,每个点都被并行地独立标记。 这种方法称为 瓦片,有效减小了用于标记的场景大小。

但是,这可能会大大增加标记时间和成本,因为可能需要将典型的8万点场景分解为16个以上的子场景。 这种方法产生的大量独立任务意味着在任务之间进行上下文切换时会花费更多的注释时间,并且当场景太小时,工作人员可能会丢失上下文,从而导致数据标签错误。

固定步骤样本

另一种方法是通过线性子样本(称为a)选择或减少点的数量。 固定步长样本。 假设您要达到500,000点的目标(我们观察到这通常可以在消费类笔记本电脑上呈现-请参见 接受的原始3D数据格式),但您拥有一个拥有10万点的点云。 您可以将步长计算为 step = 10,000,000 / 500,000 = 20。 确定步长后,可以选择数据集中的每20个点,从而创建一个新的点云。 如果您的点云数据具有足够高的密度,那么即使原始场景中每1个点中只有20个点,标注者仍应能够找出与标注有关的所有特征。

这种方法的缺点是,并非所有点都会对最终的降采样结果有所贡献,这意味着,如果某个点是少数几个重要点之一,而不是样本的一部分,则注释者可能会完全忽略该特征。

体素均值

使用所有点生成下采样点云的另一种下采样形式是执行 网格过滤。 网格过滤意味着您可以将输入空间划分为点云上的常规3D框(或体素),并用单个代表点(例如平均点)替换体素内的所有点。 下图显示了一个示例体素红色框。

如果给定体素中输入数据集中不存在任何点,则不会将任何点添加到该体素的降采样点云中。 网格过滤不同于固定步骤样本,因为您可以使用它来减少噪声,并通过调整内核大小和求平均函数来进一步调整它,以产生稍微不同的最终点云。 以下点云显示了简单(固定步骤样本)和高级(体素均值)下采样的结果。 使用高级方法下采样的点云更平滑,这在比较两个场景后面的红砖墙时尤为明显。

升采样方法

在对数据进行下采样和标记后,您可能希望看到在较小的,下采样的点云上生成的标签投影到全尺寸点云上,我们称之为 上采样。 对象检测或跟踪作业不需要后处理即可完成。 降采样后的点云中的标签(如长方体)可直接应用于较大的点云,因为它们是在全尺寸点云(x,y,z,高度,宽度,长度)共享的世界坐标空间中定义的。 当边界点不在降采样的数据集中时,这些标签极易受到沿对象边界的很小误差的影响,但长方体中额外的,正确标记的点(也可以是训练有素。

但是,对于3D点云语义分割作业,标签并不直接适用于完整数据集。 我们只有标签的一个子集,但是我们希望基于此子集来预测整个数据集标签的其余部分。 为此,我们可以使用一个简单的 K最近邻(K-NN)分类器 以已经标记的点作为训练集。 K-NN是一种简单的监督ML算法,它使用“ K”个最近的标记点和加权投票来预测点的标记。 使用K-NN,我们可以基于三个最接近(按欧氏距离)点的多数类来预测完整数据集中其余未标记点的点类。 我们可以通过更改K-NN分类器的超参数来进一步完善此方法,例如要考虑的最近点数以及点的距离度量和加权方案。

将样本标签映射到完整数据集后,您可以可视化完整数据集中的图块,以查看上采样策略的效果。

现在,我们已经回顾了本文中使用的方法,我们将在一个SageMaker笔记本上的示例语义分割点云场景中演示这些技术。

先决条件

要遍历此解决方案,您需要以下内容:

  • An AWS账户.
  • 笔记本 AWS身份和访问管理 (IAM)角色,并具有完成此演练所需的权限。 您的IAM角色必须附加以下AWS托管策略:
    • AmazonS3FullAccess
    • AmazonSageMakerFullAccess
  • An 亚马逊简单存储服务 (Amazon S3)存储笔记本工件(输入数据和标签)的存储桶。
  • 一个SageMaker工作团队。 对于此职位,我们使用一个私人工作团队。 您可以在SageMaker控制台上创建工作团队。

笔记本设置

我们用笔记本 ground_truth_annotation_dense_point_cloud_tutorial.ipynb ,在 SageMaker示例 笔记本实例的第二部分,以演示这些下采样和上采样方法。 本笔记本包含执行预处理,加标签和后处理所需的所有代码。

要访问笔记本,请完成以下步骤:

  1. 创建一个笔记本实例。 您可以使用实例类型ml.t2.xlarge启动笔记本实例。 请选择一个至少具有16 GB RAM的实例。
    1. 您需要使用您之前创建的笔记本IAM角色。 该角色允许您的笔记本将数据集上传到Amazon S3并调用解决方案API。
  2. 打开Jupyter Lab或Jupyter以 访问您的笔记本实例.
  3. 在Jupyter中,选择 SageMaker示例 在Jupyter Lab中,选择SageMaker图标。
  4. 地面真相贴标工作 然后选择 ipynb 笔记本电脑。
  5. 如果您使用的是Jupyter,请选择 使用 将笔记本复制到您的实例并运行它。 如果您在Jupyter实验室中,请选择 建立副本.

提供笔记本输入

首先,我们修改笔记本以添加我们的私人工作团队ARN和用于存储数据集以及标签的存储桶位置。

第1节:检索数据集并可视化点云

我们通过运行笔记本的第1部分下载数据,该部分从Amazon S3下载我们的数据集并将点云加载到我们的笔记本实例中。 我们从AWS拥有的存储桶下载定制的准备数据。 一个叫做 rooftop_12_49_41.xyz 应该在S3存储桶的根目录中。 此数据是对在移动设备上生成的自定义公寓屋顶的扫描。 在这种情况下,点云数据为xyzrgb格式。

我们可以使用Matplotlib可视化我们的点云 散布3d 功能。 点云文件包含所有正确的点,但是旋转不正确。 我们可以通过将点云乘以旋转矩阵来绕其轴旋转对象。 我们可以使用 科学的 并使用来指定我们要对每个轴进行的度数更改 from_euler 方法:

!aws s3 cp s3://smgt-downsampling-us-east-1-322552456788/rooftop_12_49_41.xyz pointcloud.xyz # Let's read our dataset into a numpy file pc = np.loadtxt("pointcloud.xyz", delimiter=",") print(f"Loaded points of shape {pc.shape}") # playing with view of 3D scene from scipy.spatial.transform import Rotation def plot_pointcloud(pc, rot = [[30,90,60]], color=True, title="Simple Downsampling 1", figsize=(50,25), verbose=False): if rot: rot1 = Rotation.from_euler('zyx', [[30,90,60]], degrees=True) R1 = rot1.as_matrix() if verbose: print('Rotation matrix:','n',R1) # matrix multiplication between our rotation matrix and pointcloud pc_show = np.matmul(R1, pc.copy()[:,:3].transpose() ).transpose() if color: try: rot_color1 = np.matmul(R1, pc.copy()[:,3:].transpose() ).transpose().squeeze() except: rot_color1 = np.matmul(R1, np.tile(pc.copy()[:,3],(3,1))).transpose().squeeze() else: pc_show = pc fig = plt.figure( figsize=figsize) ax = fig.add_subplot(111, projection="3d") ax.set_title(title, fontdict={'fontsize':20}) if color: ax.scatter(pc_show[:,0], pc_show[:,1], pc_show[:,2], c=rot_color1[:,0], s=0.05) else: ax.scatter(pc_show[:,0], pc_show[:,1], pc_show[:,2], c='blue', s=0.05) # rotate in z direction 30 degrees, y direction 90 degrees, and x direction 60 degrees rot1 = Rotation.from_euler('zyx', [[30,90,60]], degrees=True) print('Rotation matrix:','n', rot1.as_matrix()) plot_pointcloud(pc, rot = [[30,90,60]], color=True, title="Full pointcloud", figsize=(50,30)) 

第2节:对数据集进行下采样

接下来,我们将数据集下采样到少于500,000个点,这是可视化和标记的理想点数。 有关更多信息,请参见 点云分辨率限制 in 接受的原始3D数据格式。 然后,通过运行第2节来绘制下采样的结果。

如前所述,下采样的最简单形式是根据我们希望得到的点云的大小,使用固定的步长选择值。

一种更高级的方法是将输入空间分解为多个多维数据集(也称为体素),并使用平均功能为每个框选择一个点。 下面的代码显示了一个简单的实现。

您可以调整目标点的数量和框的大小,以查看随着执行更积极的下采样而降低的点云清晰度。

#Basic Approach target_num_pts = 500_000 subsample = int(np.ceil(len(pc) / target_num_pts)) pc_downsample_simple = pc[::subsample] print(f"We've subsampled to {len(pc_downsample_simple)} points") #Advanced Approach boxsize = 0.013 # 1.3 cm box size. mins = pc[:,:3].min(axis=0) maxes = pc[:,:3].max(axis=0) volume = maxes - mins num_boxes_per_axis = np.ceil(volume / boxsize).astype('int32').tolist() num_boxes_per_axis.extend([1]) print(num_boxes_per_axis) # For each voxel or "box", use the mean of the box to chose which points are in the box. means, _, _ = scipy.stats.binned_statistic_dd( pc[:,:4], [pc[:,0], pc[:,1], pc[:,2], pc[:,3]], statistic="mean", bins=num_boxes_per_axis, ) x_means = means[0,~np.isnan(means[0])].flatten() y_means = means[1,~np.isnan(means[1])].flatten() z_means = means[2,~np.isnan(means[2])].flatten() c_means = means[3,~np.isnan(means[3])].flatten() pc_downsample_adv = np.column_stack([x_means, y_means, z_means, c_means]) print(pc_downsample_adv.shape) 

第3节:可视化3D渲染

我们可以使用点的3D散点图来可视化点云。 尽管我们的点云具有颜色,但是我们的变换对颜色有不同的影响,因此在单一颜色中对它们进行比较可以提供更好的比较。 我们可以看到,先进的体素均值方法可以创建更平滑的点云,因为求平均值具有降噪效果。 在以下代码中,我们可以通过将点云乘以不同的旋转矩阵来从两个单独的角度看待点云。

当您在笔记本中运行第3节时,您还会看到线性步进方法与盒式网格方法的比较,特别是盒式网格过滤器如何对整个点云产生轻微的平滑效果。 根据数据集的噪声水平,此平滑处理可能很重要。 将网格过滤函数从均值修改为中值或其他某种平均函数也可以提高最终点云的清晰度。 仔细查看简单(固定步长)和高级(体素均值)下采样示例的后壁,请注意,体素均值方法与固定步长方法相比具有平滑效果。

rot1 = Rotation.from_euler('zyx', [[30,90,60]], degrees=True) R1 = rot1.as_matrix() simple_rot1 = pc_downsample_simple.copy() simple_rot1 = np.matmul(R1, simple_rot1[:,:3].transpose() ).transpose() advanced_rot1 = pc_downsample_adv.copy() advanced_rot1 = np.matmul(R1, advanced_rot1[:,:3].transpose() ).transpose() fig = plt.figure( figsize=(50, 30)) ax = fig.add_subplot(121, projection="3d") ax.set_title("Simple Downsampling 1", fontdict={'fontsize':20}) ax.scatter(simple_rot1[:,0], simple_rot1[:,1], simple_rot1[:,2], c='blue', s=0.05) ax = fig.add_subplot(122, projection="3d") ax.set_title("Voxel Mean Downsampling 1", fontdict={'fontsize':20}) ax.scatter(advanced_rot1[:,0], advanced_rot1[:,1], advanced_rot1[:,2], c='blue', s=0.05) # to look at any of the individual pointclouds or rotate the pointcloud, use the following function plot_pointcloud(pc_downsample_adv, rot = [[30,90,60]], color=True, title="Advanced Downsampling", figsize=(50,30)) 

第4节:启动语义细分作业

运行笔记本中的第4节以获取该点云,并使用它来启动Ground Truth点云语义分段标记作业。 这些单元生成所需的输入清单文件,并以与地面真理兼容的表示形式格式化点云。

要了解有关与点云数据相关的地面真理的输入格式的更多信息,请参阅 输入数据接受的原始3D数据格式.

在本节中,我们还将在工作人员门户中执行标签。 我们标记点云的子集,以使用一些注释进行上采样。 作业完成后,我们将来自Amazon S3的注释加载到NumPy数组中以进行后期处理。 以下是“地面真相”点云语义分割工具的屏幕截图。

第5节:执行标签升采样

现在我们有了降采样的标签,我们通过将带注释的点作为训练数据并对全尺寸点云中未标记点的其余部分进行推断,训练了来自SKLearn的K-NN分类器来预测完整的数据集标签。

您可以调整使用的点数以及距离度量和权重方案,以影响标签推断的执行方式。 如果在全尺寸数据集中标记了一些图块,则可以将这些标记的图块用作基本事实,以评估K-NN预测的准确性。 然后,您可以将该精度度量用于K-NN的超参数调整,或者尝试使用不同的推理算法来减少对象边界之间的错误分类点数,从而使样本内错误率最低。 请参见以下代码:

# There's a lot of possibility to tune KNN further # 1) Prevent classification of points far away from all other points (random unfiltered ground point) # 2) Perform a non-uniform weighted vote # 3) Tweak number of neighbors knn = KNeighborsClassifier(n_neighbors=3) print(f"Training on {len(pc_downsample_adv)} labeled points") knn.fit(pc_downsample_adv[:,:3], annotations) print(f"Upsampled to {len(pc)} labeled points") annotations_full = knn.predict(pc[:,:3]) 

第6节:可视化上采样的标签

现在我们已经完成了对标记数据的上采样,我们可以可视化原始全尺寸点云的图块。 我们不会渲染所有全尺寸的点云,因为这可能会阻止我们的可视化工具渲染。 请参见以下代码:

pc_downsample_annotated = np.column_stack((pc_downsample_adv[:,:3], annotations)) pc_annotated = np.column_stack((pc[:,:3], annotations_full)) labeled_area = pc_downsample_annotated[pc_downsample_annotated[:,3] != 255] min_bounds = np.min(labeled_area, axis=0) max_bounds = np.max(labeled_area, axis=0) min_bounds = [-2, -2, -4.5, -1] max_bounds = [2, 2, -1, 256] def extract_tile(point_cloud, min_bounds, max_bounds): return point_cloud[ (point_cloud[:,0] > min_bounds[0]) & (point_cloud[:,1] > min_bounds[1]) & (point_cloud[:,2] > min_bounds[2]) & (point_cloud[:,0] < max_bounds[0]) & (point_cloud[:,1] < max_bounds[1]) & (point_cloud[:,2] < max_bounds[2]) ] tile_downsample_annotated = extract_tile(pc_downsample_annotated, min_bounds, max_bounds) tile_annotated = extract_tile(pc_annotated, min_bounds, max_bounds) rot1 = Rotation.from_euler('zyx', [[30,90,60]], degrees=True) R1 = rot1.as_matrix() down_rot = tile_downsample_annotated.copy() down_rot = np.matmul(R1, down_rot[:,:3].transpose() ).transpose() down_rot_color = np.matmul(R1, np.tile(tile_downsample_annotated.copy()[:,3],(3,1))).transpose().squeeze() full_rot = tile_annotated.copy() full_rot = np.matmul(R1, full_rot[:,:3].transpose() ).transpose() full_rot_color = np.matmul(R1, np.tile(tile_annotated.copy()[:,3],(3,1))).transpose().squeeze() fig = plt.figure(figsize=(50, 20)) ax = fig.add_subplot(121, projection="3d") ax.set_title("Downsampled Annotations", fontdict={'fontsize':20}) ax.scatter(down_rot[:,0], down_rot[:,1], down_rot[:,2], c=down_rot_color[:,0], s=0.05) ax = fig.add_subplot(122, projection="3d") ax.set_title("Upsampled Annotations", fontdict={'fontsize':20}) ax.scatter(full_rot[:,0], full_rot[:,1], full_rot[:,2], c=full_rot_color[:,0], s=0.05) 

因为我们的数据集很密集,所以我们可以可视化图块中的上采样标签,以查看下采样标签被上采样到全尺寸点云。 尽管在对象之间的边界区域可能会出现少量错误分类,但是在全尺寸点云中,与初始点云相比,在正确标注的点上也要多得多,这意味着您的整体ML准确性可能会提高。

净化

笔记本实例:如果不想保持创建的笔记本实例运行,则有两个选择。 如果您想保存它以备后用,可以停止而不是删除它。

  • 停止 笔记本实例:单击 笔记本实例 SageMaker控制台主页左窗格中的链接。 接下来,点击 Stop 停止 笔记本实例名称左侧的“操作”列下的“链接”。 笔记本实例停止后,可以通过单击重新启动笔记本实例。 Start 开始 关联。 请记住,如果您停止而不是删除它,则需要为与之关联的存储付费。
  • 删除 笔记本实例:首先按照上述说明将其停止。 接下来,单击笔记本实例旁边的单选按钮,然后选择 删除 来自 行动 下拉式菜单。

结论

在对数据进行预处理以进行对象检测和对象跟踪标记时,降采样点云可能是一种可行的方法。 它可以降低标签成本,同时仍可以生成高质量的输出标签,特别是对于3D对象检测和跟踪任务。 在本文中,我们演示了下采样方法如何影响工作人员的点云的清晰度,并展示了一些基于数据集噪声水平进行权衡的方法。

最后,我们证明了您可以对下采样的数据集执行3D点云语义分割作业,并通过样本内预测将标签映射到全尺寸点云。 我们通过训练分类器以使用已经标记的点作为训练数据,对剩余的完整数据集大小点进行推断来实现这一点。 这种方法可以对高密度的点云场景进行具有成本效益的标记,同时仍保持良好的整体标记质量。

检验出 这个笔记本 利用您在Ground Truth中的密集点云场景,尝试新的下采样技术,甚至尝试使用K-NN以外的新模型进行最终样本内预测,以查看下采样和上采样技术是否可以降低标签成本。


作者简介

 维迪亚·萨加尔·拉维帕蒂(Vidya Sagar Ravipati) 是位于纽约的深度学习架构师 亚马逊机器学习解决方案实验室,他利用在大型分布式系统方面的丰富经验以及对机器学习的热情,帮助跨不同行业的AWS客户加速其AI和云技术的采用。 之前,他是Amazon Connectivity Services的机器学习工程师,曾帮助构建个性化和预测性维护平台。

艾萨克·普里维特拉(Isaac Privitera) 是一名机器学习专家解决方案架构师,帮助客户在AWS上设计和构建企业级计算机视觉解决方案。 Isaac具有使用机器学习和加速计算进行计算机视觉和信号分析的背景。 艾萨克(Isaac)业余时间也喜欢烹饪,远足和紧跟机器学习的最新进展。

杰里米·费特拉科(Jeremy Feltracco) 是Amazon Web Services的Amazon ML解决方案实验室的软件开发工程师。 他利用其在计算机视觉,机器人技术和机器学习方面的背景知识来帮助AWS客户加速其AI的采用。

资料来源:https://aws.amazon.com/blogs/machine-learning/annotate-dense-point-cloud-data-using-sagemaker-ground-truth/

时间戳记:

更多来自 AWS机器学习博客