输入输出:以带有实例分割标签(但不引入具体的语义类别)的点云数据作为输入,输出该场景对应的场景图,包括每个实例的类别标签和类别间关系的标签组成的图拓扑结构。
点云的特征提取:利用 PointNet 全局特征提取模块 考虑欧式空间中点云数据的特点:无序性、点间存在联系、变换无关性。
- 无序性:聚合所有点特征的函数应具有对称的特点,这里引入 max pooling 层进行解决
- 点间存在联系:利用一维卷积来对输入特征进行处理
- 变换无关性:引入一个 Tranformation Network (Mini PointNet),用来回归一个 3 × 3 的矩阵作用在点云上。这里的思路是在特征提取之前将所有输入对齐到一个规范空间内
物体间关系特征学习:图卷积网络,考虑输入节点数的不一致性,利用节点间的连接关系来对节点和边的特征进行优化。 图卷积网络中每一层的更新方式:
- 将输入的三元组 <主,谓,宾>特征向量输入多层感知机(MLP)进行信息传递。这里‘谓语’的特征得到保留。
- 对每一个节点,所有与该节点存在连接关系的节点,都应该作为该节点特征优化过程中的输入,所以这里需要根据图拓扑中节点间的连接关系,利用求和取均值的方式来更新每个节点的特征。
- 将聚合后的节点特征输入另一个 MLP,利用该模型中的参数来学习用于后续任务的节点特征,为了克服图结构中可能存在的拉普拉斯平滑(Laplacian smoothing),引入一个跳跃连接,在 MLP 输出的特征上加上原节点特征向量作为节点更新后的特征。
经过每一层网络的更新,节点的特征可以被传递到更远的邻居。这里,图卷积的层数等于网络可以学习到的邻居范围。
节点和边类别的预测:两个 MLP。
Loss:物体 object 分类的 Loss 使用通用的交叉熵损失。由于物体间的关系 predicate 可能多于一种,因而这里 Loss 项采用各类二元交叉熵损失,即对 predicate 可能所属每一个类别分别考虑是否属于该类别,即可看做多个二分类问题。
对每一个 scan split 的数据做如下预处理:
- [ 建立segment和point的映射关系 ] 从 mesh.refined.obj 文件读取预处理后的点云,利用 mesh.refined.0.010000.segs.json 文件建立 segments 字典,该字典的键值为 segment 的 id,value 为该 segment 包含的所有点。
- [ 建立object和point的映射关系] 读取 semseg.json 文件,建立 object id 和点云间的映射关系。
- [ farthest point sampling 和点云的归一化 ] 利用最远采样来减少输入网络的数据负担,采样数可在 config.py 中进行修改。然后对每个物体的点云进行归一化,来解决 PointNet 学习过程中的尺度相关性。
- [ 三元组的建立和 union_point_cloud 的建立 ] 在所有 object 间建立三元组(relationship.json中不存在的关系即为 0 -> none )同时将两个 object 的 bounding box 包含的所有点组合成一个新的点云集合 union_point_cloud ,为该点云再添加一列特征,指向每个点所属的类别,1为subject,2为object,0为neither。
- [ farthest point sampling 和点云的归一化 ] 和前面类似,对关系点云进行采样和归一化。
- [ 建立 data_dict 字典项并写入json文件 ] 这里由于在 GCN 索引节点间关系的 edge 参数包含的是节点索引,而不是 object 的 id,所以这里要做一步转化,存入 triples 的是主谓宾的 id,存入 edge 的主谓是在 objects_id 中的下标。 将所有 id,category, point cloud, point number, edges, triples 保存在 data_dict 中并存入json文件中方便训练时对数据的读取。
3RScan数据集:
-
labels.instances.annotated.ply 场景的实例分割演示,该文件中包含原始场景中的所有点和实例分割标签。
-
mesh.refined.0.010000.segs.json
{ "params": { "kThresh": 0.01, "segMinVerts": 20 }, "sceneId": "/mesh.refined", "segIndices": [ // 存储的是obj文件下每个点所属的segment序号 59944, 180 ] }
-
mesh.refined.mtl 网格的材质信息,包括光照参数和纹理信息
-
mesh.refined.obj 优化后(压缩)的点云文件,包含每个点的坐标、法向量、纹理坐标和三角面片信息
-
semseg.json
{ "sceneId": "tangonet.00d42bed-778d-2ac6-86a7-0e0e5f5f5660", "appId": "stk.v1", "annId": "6215", "segGroups": [ { "id": 1, "objectId": 1, "label": "floor", "segments": [ // 该object包含的所有segment,每个segment又包含很多点 72934, 5405, ], "obb": { // 该object的方向包围盒 "centroid": [ // obb中心坐标 0.07841286214739862, 0.5989804343002418, -1.5308050264238195 ], "axesLengths": [ // obb三个轴方向上的长度 8.722671943831202, 0.32160996671892733, 4.4991274130680505 ], "normalizedAxes": [ // obb三个轴的单位向量 -0.3248705267906189, 0.9457585215568542, 2.100005772735734e-16, 0, -2.220446049250313e-16, 1, 0.9457585215568542, 0.3248705267906189, 7.213574777300977e-17 ] }, "dominantNormal": [ 0, -2.220446049250313e-16, 1 ], "partId": 1, "index": 0 }, ] }
-
sequence.zip 用于重建的图像序列
为减少一次加入网络中场景的大小,原论文作者对 3RScan 数据集进行了划分,将一个实际场景下采集到的点云数据划分为多组(split)。
3DSSG数据集:
-
classes.txt 包含 object 所有可能所属的类别
-
relationships.txt 包含 predicate 所有可能所属的类别
-
relationships_train.json / relationships_validation.json
{ "scans": [ { "scan": "f62fd5fd-9a3f-2f44-883a-1e5cf819608e", "objects": { // 该scan split下包含的所有object id和对应的类别标签,这里的id和其类别间不存在映射关系 "1": "floor", "42": "stool" }, "relationships": [ // 该scan split下包含的所有三元组,其中predicate的序号和对应的标签间为一一映射关系 [ 4, 1, 14, "attached to" ], [ 13, 1, 15, "standing on" ] ], "split": 1 } ] }
数据集处理过程中存在的问题:
- classes.txt 和 relationships.txt 最后一行需添加回车方便文件处理
- 部分 semseg.json 文件中 label 标注拼写错误或者 label 在 classes.txt 中未出现,多了一些括号的注释部分,直接进行删除
- relationships_validation.json 文件中存在subject 和object 的id 相同的情况,这种情况也需要进行忽略处理,无意义