121 lines
11 KiB
Markdown
121 lines
11 KiB
Markdown
# GPR 三维体 · 方案 B:全路段 int16 整卷上 GPU(升级现有管线,推荐)
|
||
|
||
- 日期:2026-06-23
|
||
- 范围:把全路段 GPR 按**物理分辨率(5~10cm)**插值成**单个 int16 体(~5~10GB)**,整卷传成 GPU 3D 纹理,切片/体绘制都丝滑。**不做金字塔/核外**,靠"右尺寸 + int16"让单体进显存。
|
||
- 定位(2026-06-23 用户定):**与 C 对等的两条已承诺路线之一,两者都做、用户运行时按需切换**。B 走"整卷进显存"路线(在现有管线上有针对性升级),适合能装进显存的体;超显存的体走 C。经同一 `IVolumeRenderSource` 接口切换。
|
||
- **✅ 评审结论(2026-06-23,opus):Go(条件式)。** 体积测算、int16+结构化插值、渲染/切片复用均成立。**开工前必改 3 项**:①落盘方案(VTKHDF Writer 写不了 ImageData,必须改裸分块,见 §3);②量化贯穿传递函数/色阶/反量化(见 §3.5);③一个 vtkShortArray→GPU 体绘制的小验证 spike。
|
||
- 测试数据(明星路):同方案 A。物理分辨率依据:450MHz、土速 0.1m/ns → 波长 λ≈0.22m、垂向分辨率 ≈5cm;网格细过 ~5cm 即过采样。
|
||
|
||
---
|
||
|
||
## 1. 设计意图
|
||
|
||
A 的瓶颈是 `double` + 400³ 上限,撑不起全路段。B 针对性拆掉这两条,**保持"整卷进显存"这一最省力的渲染架构不变**:
|
||
|
||
```
|
||
.iprb/.iprh → 结构化建体(仅横向插值)→ ScalarVolumeI16(int16)
|
||
→ vtkImageData + vtkShortArray → vtkSmartVolumeMapper(整卷进显存)
|
||
切片:复用 SliceTool(reslice 对 int16 image 同样工作)
|
||
持久化:VolumeBuildParams + int16 明细【真实落盘 + 分块压缩】
|
||
```
|
||
|
||
体积测算(明星路全路段,依据真实头文件):
|
||
|
||
| 网格 (横×纵×深) | 体素数 | int16 体积 | 进显存? |
|
||
|---|---|---|---|
|
||
| 10cm×10cm×2cm | 22230×270×400 ≈ 2.4G | **4.8GB** | 12GB+ 显卡可 |
|
||
| 10cm×10cm×原生821 | ≈ 4.9G | **9.8GB** | 16~24GB 显卡可 |
|
||
| 5cm×5cm×5cm | 44460×540×160 ≈ 3.8G | **7.7GB** | 16GB+ 显卡可 |
|
||
|
||
**关键:5~10GB 全部在单显卡可承载区间——不需要金字塔/核外。** 那个 39TB 是 cm 级横向过采样的产物,物理无意义。
|
||
|
||
---
|
||
|
||
## 2. 三处核心升级(相对 A)
|
||
|
||
### 2.1 dtype:引入 int16 体(4× 内存削减)
|
||
- 现 `ScalarVolume` 全仓库是 `double`(`Field.hpp:8-26`),直接改全局风险大。**方案**:新增并行的 `ScalarVolumeI16`(`std::vector<int16_t>` + 同样行优先布局 + 量化标定 `scale/offset` 把物理值映射到 int16),雷达走 int16 路径,反演剖面仍走 double。
|
||
- 渲染:`buildVoxel` 增加 int16 重载 → `vtkImageData` + `vtkShortArray`。**评审已证实** GPU 体绘制原生支持 short(`vtkSmartVolumeMapper`→`vtkOpenGLGPUVolumeRayCastMapper`→`vtkVolumeTexture` 走 GL 16-bit 整型纹理)。NaN/空值改用 int16 哨兵(如 `INT16_MIN`)+ 不透明度传递函数透明(与现 `VoxelActor.cpp:23-24,68-72` 同构)。
|
||
- 收益:同体素数内存/显存/磁盘 = double 的 1/4,是"让全路段进显存"的关键杠杆。雷达原始本就是 int16,**无精度损失**。
|
||
- **适配面比"加个重载"大(评审 HIGH)**:`ScalarVolume`(double) 被 `VolumeGrid`/`buildVoxel`/`finalizeVolume`/`Api3dRepository`(`StoredVolume.cachedGrid`、`loadVolume` 回调签名、`VolumeInfo` 统计) 一路引用。int16 体需让这些**要么模板化、要么并行一套带量化 meta 的变体**。隔离方向(雷达 int16 / 反演剖面仍 double)对,但工作量按"中"算偏乐观,§6 已上调。
|
||
|
||
### 2.2 维度上限:由物理分辨率决定,拆掉 400³ 死值
|
||
- 移除/放宽 `kMaxVolumeDim=400`(`VolumeBuilder.hpp:8`),改为按 `cellXY/cellZ` 与场景范围算出 dims,并加**显存预算守卫**(建体前估算 `nx·ny·nz·2B`,**并留余量**:实际还要叠加传递函数纹理 + 颜色/深度 FBO,按裸标量算偏紧——评审 MEDIUM)。
|
||
- **显存探测无可靠跨厂商 API(评审 MEDIUM)**:OpenGL 无统一"可用显存"查询。实践只能 try-upload-on-fail 或留保守阈值。
|
||
- **免费兜底(评审发现,spec 原漏报)**:`vtkSmartVolumeMapper` 自带 `MaxMemoryInBytes` + `LowResResample`(`vtkSmartVolumeMapper.h:194-211,373-379`),体超显存时**自动降采样重采样到可容纳**——等于"概览体"免费实现。目标机显存小时优先用它 + 按区域细化,**仍在 B 框架内,不必转 C**。
|
||
- 默认网格由雷达物理分辨率给(横 5~10cm、深 2~5cm),不让用户填出过采样网格。
|
||
|
||
### 2.3 插值:结构化建体,不做 3D 散点 IDW
|
||
- **重要架构洞察**:雷达数据沿测线(X)、深度(Z)**本就是规则密采样**,只有横向(Y)的 14 通道是稀疏的。所以"插值成体"≠ 3D 无结构散点插值,而是:
|
||
- X、Z 方向按道距/采样直接落格(重采样/最近邻,廉价);
|
||
- **只在 Y 方向对 14 通道做 1D 插值**填充横向空隙。
|
||
- 这比 A 复用的全 3D IDW **快一两个数量级**,且单线程可接受;若仍慢,Y 向插值天然可并行(QtConcurrent/std::thread,按 X 切片并行)。
|
||
- 保留 `IInterpolator` 抽象,新增 `GprStructuredBuilder` 实现,与 IDW 并列。
|
||
|
||
---
|
||
|
||
## 3. 持久化(真正满足"算一次、之后秒开")
|
||
|
||
用户要两种保存,B 把第二种做实:
|
||
|
||
- **方式一(参数档)**:`VolumeBuildParams`(源 .iprb 引用 + 建体参数 + 色阶 + 量化 scale/offset)+ `GridSpec`。小、可复算、详情面板展示。
|
||
- **方式二(明细缓存,升级为真实落盘)**:int16 体 **分块写盘 + 逐块压缩**:
|
||
- **⚠ 不能用 `vtkHDFWriter`(评审 CRITICAL,两个评审独立证实)**:VTK 9.6 的 `vtkHDFWriter` **写不了 `vtkImageData`/规则体**——它只支持 PolyData/UnstructuredGrid/Partitioned/MultiBlock(`vtkHDFWriter.h:6-9,232-235`,无 ImageData 写重载)。`vtkHDFReader` 能**读** ImageData,但 Writer 不能**写**,读写不对称。"补个 IOHdf 组件就能 VTKHDF 原生落盘体"的说法**错误**。
|
||
- **首选(改正后)**:**自定义 raw int16 分块 + sidecar(GridSpec/量化 scale·offset/vmin·vmax/分块索引) + 逐块 zlib(VTK 自带 `vtkzlib`,无需新依赖)**。分块布局从一开始就设计好,C 的"切片核外"可几乎免费复用同一格式。
|
||
- 备选:直接用底层 `vtkhdf5` C API 自写 chunked dataset(绕过 `vtkHDFWriter`),获得 HDF5 生态兼容;成本高于 raw 分块。
|
||
- 不引入独立 zstd/blosc(vcpkg 未含;如需更高压缩比再加)。
|
||
- **加载**:有明细 → 读盘(可 mmap)→ 整卷上显存;无明细 → 按参数后台线程重算落缓存(复用现有重算逻辑 `Api3dRepository.cpp:212-225`,从 mock 升级为真实落盘)。
|
||
- **后台重算不阻塞 UI(评审 MEDIUM)**:现 `loadVolume` 回调**在主线程**(mock 同步)。改为工作线程建体/重算后,回调要**跨线程编组**回 UI(Qt 信号 / `QMetaObject::invokeMethod`),这是线程模型改动,需显式设计。
|
||
- **加载耗时别承诺"秒开"(评审 LOW)**:5~10GB 上传 GPU(约 1~5s) + 压缩明细解压,实际**约 10s 量级**。明星路单体压缩后约 2~6GB,读盘+解压秒~十秒级——比每次重算快得多,用户"算一次之后快读"诉求成立。
|
||
|
||
### 3.5 量化贯穿(评审 HIGH,正确性问题,必做)
|
||
int16 渲染标量是量化域 `q = round((v_phys - offset)/scale)`,不是物理值。必须把量化贯穿全链,否则色阶/读数全错:
|
||
- **传递函数 / 不透明度**:现 `VoxelActor.cpp:62-72` 用物理 `vmin/vmax` 加控制点 → int16 路径必须改成在**量化域 `qmin/qmax`** 采样。
|
||
- **切片色阶 LUT**:`buildLut(cs,vmin,vmax)`(`SliceTool.cpp:37`)同理喂量化域。
|
||
- **反量化显示**:取值光标 / 异常详情 / 数据详情面板展示给用户的值必须 `v_phys = q*scale + offset` 反量化回物理量。
|
||
- `scale/offset` 存入 `VolumeBuildParams` 并随 `VolumeInfo` 传递。
|
||
|
||
---
|
||
|
||
## 4. 渲染与交互(基本复用,验证为主)
|
||
|
||
- 整卷 `vtkSmartVolumeMapper`(现有),int16 image 直接喂;确认 9.6 的 GPU ray cast 对 short 标量正常。
|
||
- 切片 `SliceTool`(`vtkImagePlaneWidget`/`vtkImageReslice`)对 int16 image 同样工作(CPU reslice 与 dtype 无关);丝滑度由"整卷已在显存"保证。
|
||
- 异常/详情/色阶:复用现有 3D 分析栏链路。
|
||
|
||
---
|
||
|
||
## 5. 评估
|
||
|
||
**优点**
|
||
- **全路段完整连续体 + 最好体验**,切片/体绘制丝滑,且**不必上金字塔/核外**。
|
||
- 复用现有渲染/切片/异常/详情,主要新增 = int16 路径 + 结构化建体 + 真实落盘。
|
||
- int16 + 结构化插值同时解决"内存/显存/磁盘大"和"插值慢"。
|
||
- 明细真实落盘,"算一次秒开"成立。
|
||
|
||
**缺点/风险(评审分级)**
|
||
- **CRITICAL(已在 §3 修正)**:`vtkHDFWriter` 写不了 ImageData → 落盘改裸 int16 分块+zlib。有现成退路,非方案推翻,但落盘是"自写格式"的中等工程,非"补组件"。
|
||
- **HIGH**:量化未贯穿传递函数/色阶/反量化会导致颜色与读数错(§3.5 已补设计)。
|
||
- **HIGH**:int16 适配面被低估(`VolumeGrid`/`loadVolume` 回调/`StoredVolume`/`VolumeInfo` 均需带量化 meta),非单点重载。
|
||
- **MEDIUM**:后台重算从主线程改工作线程,跨线程回调编组需设计;显存无可靠查询、预算按裸标量偏紧;结构化落格假设道近似等距,GPS 抖动需沿弧长重采样。
|
||
- **LOW**:5~10GB 加载约 10s 级,UX 别承诺"秒开"。
|
||
- 单巨体无部分加载:打开即载全量。
|
||
- int16 路径是对核心类型的扩展,需谨慎不污染 double 主路径(用并行类型隔离)。
|
||
|
||
**适用**
|
||
- 当前明星路这一档(及绝大多数单路段工程)。**这是默认推荐。**
|
||
|
||
---
|
||
|
||
## 6. 工作量与落地顺序
|
||
|
||
0. **【开工前】验证 spike(~半天)**:`vtkShortArray` 填小 `vtkImageData` → `vtkSmartVolumeMapper` 跑通 GPU ray cast + 量化域传递函数,确认 GPU 路径与颜色正确。
|
||
1. 地基(同 A §2):`.iprb`/`.iprh` 解析 + 配准 + 接入(~中)。
|
||
2. `ScalarVolumeI16` + `buildVoxel` int16 重载 + 哨兵透明 + **量化贯穿传递函数/LUT/反量化(§3.5)**(~中大,评审上调:适配面比单点重载大)。
|
||
3. `GprStructuredBuilder`(X/Z 落格 + Y 向插值,可并行;GPS 抖动需沿弧长重采样)替代全 3D IDW(~中)。
|
||
4. 显存预算守卫(留 FBO/传函余量)+ `LowResResample` 概览兜底 + 物理分辨率默认网格(~小)。
|
||
5. 明细真实落盘(**raw int16 分块 + sidecar + zlib,不用 vtkHDFWriter**)+ 后台重算(**含跨线程回调编组**)(~中大,评审上调:自写分块格式 + 线程模型)。
|
||
6. 渲染/切片对 int16 的验证(~小)。
|
||
|
||
**结论(评审:Go 条件式 / 用户定:与 C 都做)**:B 用"右尺寸 + int16 + 结构化插值"在现有架构上拿到全路段完整体验。**前置条件**:§3 落盘章节已从 VTKHDF 改为裸分块、§3.5 量化设计已补、第 0 步 spike 通过——满足后即可进入实现。B 与 C 经同一 `IVolumeRenderSource` 并存,用户按数据规模在两者间切换(能进显存走 B、超显存走 C),落盘格式两者共用(B 的裸分块是 C 分块/金字塔的基座)。
|