超大点云处理策略:基于空间分片与4D融合点云的高效加载与渲染
在处理超大规模点云时,如何高效地管理内存、减少网络请求并提升用户体验,是一个非常关键的问题。今天我们介绍一种常见的解决方案:空间分片与4D融合点云,结合前端按需加载和切帧控制,实现高效的可视化与交互。
一、什么是超大点云?
超大点云指的是包含数百万甚至数十亿个点的数据集,常见于自动驾驶、地理信息系统、三维重建等领域。在这种场景下,如何高效处理这些点云数据,尤其是在浏览器或前端渲染时,成为了一个巨大的挑战。
二、方案概述
本方案通过将点云按空间区域分片,以及在每个分片内融合多个时间帧,解决了超大点云的数据传输、内存占用、渲染效率等问题。具体策略如下:
1. 空间分片(Sharding)
当点云数据集非常庞大时,我们通常会将点云按空间区域切分成多个分片。每个分片对应一个空间区域,包含该区域内所有的点云数据。
例:rangeJSON 数据结构
{
"0": {
"range": [x1, y1, x2, y2],
"final_pcds_num": 10000,
"final_extra_pcds_num": 5000,
"num_not_in_overlap": 2000
},
"1": {
"range": [x3, y3, x4, y4],
"final_pcds_num": 15000,
"final_extra_pcds_num": 7000,
"num_not_in_overlap": 3000
},
"frames": 50,
"pcds_ds_mode": "split",
"sam_version": "v1.0",
"psam_version": "v2.0",
"instance_skip_threshold": 10
}
在这个结构中,每个分片(如 0、1)都有一个空间范围(range) ,指定了点云数据的空间坐标范围。每个分片也包含了最终的点云数量、额外预标注点数量以及非重叠区域的点数量等信息。
2. 按需加载分片
根据用户当前视野,前端将按需加载分片。当用户查看某个区域时,后端会加载该区域的融合点云数据(如 0.pcd)。与此同时,后台会通过 Web Worker 预加载其他分片(如 1.pcd、2.pcd等),以便用户切换到其他区域时,能够快速加载数据。
加载流程
- 用户打开任务,前端获取当前区域的分片:
infoShardingId = 0 - 只请求对应区域的点云文件(如
0.pcd) - 后台预加载其他区域的点云数据(如
1.pcd,2.pcd等)到 Worker - 用户切换区域时,从缓存中加载对应的分片,若缓存没有,则重新请求
3. 4D 点云 = 融合点云
每个分片的点云文件(如 0.pcd)包含该区域所有帧的数据,多个帧的数据已经融合。每个点都携带了 frameIndex 属性,表示它属于哪个时间帧。
切帧操作并不需要重新请求新的文件,而是通过改变每个点的可见性来控制渲染哪些帧的数据。
function filterPcd(frameStart, frameEnd) {
for (let i = 0; i < totalPoints; i++) {
const pointFrameIndex = pointArray[i].frameIndex;
if (pointFrameIndex >= frameStart && pointFrameIndex <= frameEnd) {
pointArray[i].visible = 1; // 显示
} else {
pointArray[i].visible = 0; // 隐藏
}
}
}
4. 总结
通过空间分片和4D融合点云的处理策略,我们可以有效解决以下问题:
- 空间分片:通过将点云按区域划分成多个
.pcd文件,每个文件存储该区域内的所有帧数据。这样能减轻每次加载时的内存压力。 - 按需加载:前端根据用户当前查看的区域,动态加载对应的分片数据,避免一次性加载所有点云,减少带宽消耗。
- 后台预缓存:后台使用 Web Worker 预加载其他分片数据,提高区域切换时的响应速度。
- 时间维度:每个分片内包含所有时间帧的数据,通过
frameIndex和visible属性来控制切帧的显示与隐藏,不需要每次切帧时重新请求点云数据。 - 内存优化:同一时间只渲染一个区域的点云,避免显存爆炸。
因此,你在浏览器中看到的只是一开始加载的 0.pcd,但它已经包含了所有的帧数据。切换帧只是修改每个点的可见性,而切换区域才会触发新的 .pcd 文件请求。
三、rangeJSON 的定义和获取
为了实现上述的按需加载和切帧操作,rangeJSON 作为配置信息被用来存储分片的具体信息,并且能够在前端通过 range.json 动态获取。
1. 获取 range.json
const baseURL = 'oss://path/to/pcds_for_label/';
const rangeUrl = baseURL + 'range.json';
const rangeJSON = await fetch(rangeUrl).json();
2. rangeJSON 数据结构
rangeJSON 包含了每个分片的空间范围、点云数量以及帧数等信息:
{
"0": {
"range": [x1, y1, x2, y2],
"final_pcds_num": 10000,
"final_extra_pcds_num": 5000,
"num_not_in_overlap": 2000
},
"1": { ... },
"frames": 50,
"pcds_ds_mode": "split",
"sam_version": "v1.0",
"psam_version": "v2.0",
"instance_skip_threshold": 10
}
3. 判断是否有多个分片
通过解析 rangeJSON 中的分片索引,判断是否有多个区域分片。
const shardIndices = Object.keys(rangeJSON)
.map(key => +key)
.filter(key => !Number.isNaN(key)); // [0, 1, 2, ...]
// 如果只有 0,说明只有一个区域
// 如果有 [0, 1, 2],说明有多个区域分片
4. 你的情况
如果你只看到 0.pcd 的请求,说明整个点云任务只有一个区域:
rangeJSON = {
"0": {
"range": [...],
"final_pcds_num": 10000,
"final_extra_pcds_num": 5000,
"num_not_in_overlap": 2000
},
"frames": 50,
"pcds_ds_mode": "split"
}
这意味着:
- 只有一个分片,没有其他区域分片(如
1.pcd,2.pcd等) - 所有点云数据都在
0.pcd中,包含了所有50帧的数据 - 切帧操作只是通过修改点的
visible属性来控制显示哪些帧的数据 - 不需要切换区域,因此也不会有额外的请求
四、总结
通过空间分片和4D融合点云的策略,我们成功地将超大点云数据切分成多个区域,按需加载分片并进行时间轴控制。通过这种方式,我们既能有效地管理大规模的点云数据,又能提供流畅的用户体验,同时确保内存和网络带宽的高效利用。
在你看到的任务中,只有一个区域(
0.pcd),所有数据都在其中,切换帧时无需重新请求点云文件。