7.4 KiB
7.4 KiB
GPR 三维体 · 方案 A:整卷上纹理,不用金字塔(复用现有管线,最简基线)
- 日期:2026-06-23
- 范围:把 GPR(探地雷达)阵列数据插值成三维体并在 VTK 中渲染/切片,直接复用现有剖面三维体管线,整卷一次性进显存,不做分块/金字塔/核外。
- 定位:三选一中的最小改动基线。用于评估"现有架构原样接雷达,能做到什么、卡在哪"。
- ⚠ 评审结论(2026-06-23,opus):A 不应作为独立交付步,建议并入 B。 唯一值得从 A 单独先做的是"三方案共有的地基"(§2)。
double+400³+暴力 IDW 三条硬约束使 A 产出的 GPR 体既无业务分辨率(沿线被强制粗化到 5.5m vs 物理 5cm)、又无法落盘秒开。详见 §5/§6。 - 测试数据(明星路):450MHz 阵列 GPR,14 通道,每道 821 采样,单线 ~45306 道,20 线,int16,合计 13.6GB;路长 2223m,测幅 1.37m,深 ~8m。
1. 设计意图
不引入任何新渲染/存储机制。把雷达数据喂进现有体素管线,让它走和反演剖面三维体完全一样的路:
.iprb/.iprh → PointSet → core::IdwInterpolator → ScalarVolume(double)
→ data::VolumeGrid → render::buildVoxel → vtkSmartVolumeMapper(整卷进显存)
切片:render::interact::SliceTool(vtkImagePlaneWidget / vtkImageReslice,CPU 重采样)
持久化:VolumeBuildParams(参数必存)+ 可选明细缓存(现内存 mock)
现有落点(实证):
core::ScalarVolume=std::vector<double>,行优先(src/core/model/Field.hpp:8-26)。render::buildVoxel→vtkImageData+vtkDoubleArray+vtkSmartVolumeMapper,整卷上传(src/render/actors/VoxelActor.cpp:41-79)。- 体素维度上限
kMaxVolumeDim = 400(src/core/algo/VolumeBuilder.hpp:8)。 - 切片 CPU 重采样(
src/render/interact/SliceTool.cpp:24-39)。 - 插值
IdwInterpolator,单线程三重循环,且无空间索引——每体素全点集线性扫描,O(体素数×点数) 暴力(src/core/algo/IdwInterpolator.cpp:15-33,评审实证)。雷达级点集下即便 400³ 也是分钟级甚至卡死,不只是"偏慢"。 - 持久化
Api3dRepository::StoredVolume,纯内存(src/data/api/Api3dRepository.hpp:112-119),重算逻辑已就绪(Api3dRepository.cpp:212-225)。 - 注意(评审):现有
loadVolume的散点来源硬绑loadSection/appendGridPoints(ERT 反演帘面,Api3dRepository.cpp:146-171),雷达没有现成喂入路径。"复用现有管线"实际仍须新写 GPR→buildVolume接入(§2 已含),§1 流程图的"原样复用"措辞偏乐观。
2. 新增工作(雷达接入,三方案共有的地基)
A/B/C 都绕不开这块,A 用最朴素实现:
.iprb/.iprh解析器(新):.iprh文本头取SAMPLES/LAST TRACE/CHANNELS/TIMEWINDOW/SOIL VELOCITY/DISTANCE INTERVAL;.iprb读 int16 B-scan(samples × traces,校验samples×traces×2 == 文件大小)。- 地理配准:
.ord取 14 通道横向偏移;.gps/.cor取每道经纬度/RTK;深度 =time × soilVelocity / 2。 - GPR→PointSet 适配器:把"14 通道 × N 道 × 821 采样"摊成
PointSet{x,y,z,v}(局部坐标)。注意横向只有 14 个真实样本,是稀疏维。 - 数据集接入:新增 ddCode(如
dd_gpr_volume),在维度分类(Api3dRepository.cpp:30-45、LocalSample3dRepository.cpp:43-58)归 3D;DTO 解析器放src/data/dto/。
3. 关键约束与后果(A 的硬边界)
现有管线是 double + 400³ 上限。这两条直接决定 A 能做什么:
| 约束 | 数值 | 后果 |
|---|---|---|
| 标量 dtype | double(8 字节/体素) |
同样体素数,内存是 int16 的 4 倍 |
| 维度上限 | 400³ | 整卷 ≤ 400³×8 ≈ 512MB;放不下全路段 |
| 整卷进显存 | 一次性 | 体大小受限于显存 |
| IDW | 单线程 + 无空间索引暴力 | 大点集插值分钟级/卡死(明星路单线 ~5亿采样点级) |
fitAxis行为(评审实证VolumeBuilder.cpp:16-26):格数超 400 时不裁剪范围,而是outCell=ext/(400-1)把 400 格摊满整个包络。所以 A 不是"丢掉远端",而是"强制粗化"——沿线细节被低分辨率抹平。
全路段在 A 下做不到原始分辨率。 明星路需 ~22000(沿线)×270(横)×400(深),远超 400³。在 A 下只能:
- 重度降采样到 ≤400³:沿线 2223m/400 ≈ 5.5m 网格 → 沿线细节全毁;或
- 按单条测线/短段分别建小体(单线降到 400³ 仍偏粗),多体并排显示(类似现有 2D 足迹平铺)。但(评审):20 体 × 400³ × double ≈ 10GB 整卷同驻显存,比单大体更易爆显存;且各体独立
GridSpec/origin,跨体的全路段连续切片做不到(用户想沿全路一刀切无法实现)。
4. 持久化(沿用 2026-06-17 §7 策略)
- 必存:
VolumeBuildParams(源数据引用 + 插值模型/参数 + 色阶)+GridSpec(origin/spacing/dims,锚定切片/异常坐标)。 - 可选明细:
ScalarVolume(double)。A 阶段仍是内存 mock(StoredVolume.cachedGrid),未真实落盘——这是 A 与用户"保存插值后体"诉求的主要差距。 - 用户要的两种保存:①参数 ②插值后明细 —— A 的 ②目前只有内存缓存,需补一段最朴素的 double 体落盘(raw + sidecar)才算满足;但 double 全路段落盘巨大,不实用。
5. 评估
优点
- 改动最小:渲染/切片/异常/详情全部现成,只加雷达解析与适配。
- 路径已验证,风险低,可最快出"雷达能进三维场景"的可见效果。
缺点/限制
double+ 400³ → 撑不起全路段原始分辨率,只能粗览或分段小体。- 明细落盘不实用(double 体积过大),用户"算一次秒开"诉求难真正成立。
- 单线程 IDW 在雷达量级偏慢。
- 把结构化的雷达数据(沿线/深度本就规则)当无结构散点做 3D IDW,算力浪费(见方案 B 的结构化插值优化)。
适用
- 单条短测线 / 粗分辨率概览 / 快速打通链路的第一步。
- 不适合作为全路段完整体验的最终方案。
6. 工作量与落地顺序
.iprb/.iprh解析 + 地理配准 + GPR→PointSet(地基,~中)。- 雷达 ddCode 接入维度分类 + DTO(~小)。
- 直接复用
IdwInterpolator/buildVoxel/SliceTool,按 ≤400³ 降采样建体(~小)。 - (可选)double 明细落盘最朴素实现(~小,但不推荐用于全路段)。
结论(修订,评审定):no-go(作为独立交付步),并入 B。 A 没有任何 B 不需要的独立资产,渲染/切片/异常/持久化骨架 A、B 共享,地基(§2)也共享。double+400³+暴力 IDW 三条硬约束使 A 的 GPR 产物既无业务分辨率、又无法落盘秒开,连"最小基线该兑现的可用产物"都达不到。
- 唯一抽出先做的独立里程碑 = §2 共有地基(
.iprb/.iprh解析 + 14 通道配准 + GPR→PointSet + ddCode 接入),A/B/C 都要。 - "复用 double+400³ 管线建退化体"这一步不单独交付,直接在 B 的 int16+结构化建体上落地,避免做一遍注定被 B 推翻的降级体。
- 全路段完整体验走 B。