geopro/docs/superpowers/specs/2026-06-23-gpr-volume-A-who...

7.4 KiB
Raw Blame History

GPR 三维体 · 方案 A整卷上纹理不用金字塔复用现有管线最简基线

  • 日期2026-06-23
  • 范围:把 GPR探地雷达阵列数据插值成三维体并在 VTK 中渲染/切片,直接复用现有剖面三维体管线,整卷一次性进显存,不做分块/金字塔/核外。
  • 定位:三选一中的最小改动基线。用于评估"现有架构原样接雷达,能做到什么、卡在哪"。
  • ⚠ 评审结论2026-06-23opusA 不应作为独立交付步,建议并入 B。 唯一值得从 A 单独先做的是"三方案共有的地基"§2double+400³+暴力 IDW 三条硬约束使 A 产出的 GPR 体既无业务分辨率(沿线被强制粗化到 5.5m vs 物理 5cm、又无法落盘秒开。详见 §5/§6。
  • 测试数据明星路450MHz 阵列 GPR14 通道,每道 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::SliceToolvtkImagePlaneWidget / vtkImageResliceCPU 重采样)
持久化VolumeBuildParams参数必存+ 可选明细缓存(现内存 mock

现有落点(实证):

  • core::ScalarVolume = std::vector<double>,行优先(src/core/model/Field.hpp:8-26)。
  • render::buildVoxelvtkImageData+vtkDoubleArray+vtkSmartVolumeMapper,整卷上传(src/render/actors/VoxelActor.cpp:41-79)。
  • 体素维度上限 kMaxVolumeDim = 400src/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/appendGridPointsERT 反演帘面,Api3dRepository.cpp:146-171雷达没有现成喂入路径。"复用现有管线"实际仍须新写 GPR→buildVolume 接入§2 已含§1 流程图的"原样复用"措辞偏乐观。

2. 新增工作(雷达接入,三方案共有的地基)

A/B/C 都绕不开这块A 用最朴素实现:

  1. .iprb/.iprh 解析器(新):.iprh 文本头取 SAMPLES/LAST TRACE/CHANNELS/TIMEWINDOW/SOIL VELOCITY/DISTANCE INTERVAL.iprb 读 int16 B-scansamples × traces,校验 samples×traces×2 == 文件大小)。
  2. 地理配准.ord 取 14 通道横向偏移;.gps/.cor 取每道经纬度/RTK深度 = time × soilVelocity / 2
  3. GPR→PointSet 适配器:把"14 通道 × N 道 × 821 采样"摊成 PointSet{x,y,z,v}(局部坐标)。注意横向只有 14 个真实样本,是稀疏维。
  4. 数据集接入:新增 ddCodedd_gpr_volume),在维度分类(Api3dRepository.cpp:30-45LocalSample3dRepository.cpp:43-58)归 3DDTO 解析器放 src/data/dto/

3. 关键约束与后果A 的硬边界)

现有管线是 double + 400³ 上限。这两条直接决定 A 能做什么:

约束 数值 后果
标量 dtype double8 字节/体素) 同样体素数,内存是 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(源数据引用 + 插值模型/参数 + 色阶)+ GridSpecorigin/spacing/dims锚定切片/异常坐标)。
  • 可选明细ScalarVolume(double)。A 阶段仍是内存 mockStoredVolume.cachedGrid未真实落盘——这是 A 与用户"保存插值后体"诉求的主要差距
  • 用户要的两种保存:①参数 ②插值后明细 —— A 的 ②目前只有内存缓存,需补一段最朴素的 double 体落盘raw + sidecar才算满足但 double 全路段落盘巨大,不实用。

5. 评估

优点

  • 改动最小:渲染/切片/异常/详情全部现成,只加雷达解析与适配。
  • 路径已验证,风险低,可最快出"雷达能进三维场景"的可见效果。

缺点/限制

  • double + 400³ → 撑不起全路段原始分辨率,只能粗览或分段小体。
  • 明细落盘不实用double 体积过大),用户"算一次秒开"诉求难真正成立。
  • 单线程 IDW 在雷达量级偏慢。
  • 把结构化的雷达数据(沿线/深度本就规则)当无结构散点做 3D IDW算力浪费(见方案 B 的结构化插值优化)。

适用

  • 单条短测线 / 粗分辨率概览 / 快速打通链路的第一步。
  • 不适合作为全路段完整体验的最终方案。

6. 工作量与落地顺序

  1. .iprb/.iprh 解析 + 地理配准 + GPR→PointSet地基~中)。
  2. 雷达 ddCode 接入维度分类 + DTO~小)。
  3. 直接复用 IdwInterpolator/buildVoxel/SliceTool,按 ≤400³ 降采样建体(~小)。
  4. 可选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。