11 KiB
11 KiB
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 的"切片核外"可几乎免费复用同一格式。 - 备选:直接用底层
vtkhdf5C 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. 工作量与落地顺序
- 【开工前】验证 spike(~半天):
vtkShortArray填小vtkImageData→vtkSmartVolumeMapper跑通 GPU ray cast + 量化域传递函数,确认 GPU 路径与颜色正确。 - 地基(同 A §2):
.iprb/.iprh解析 + 配准 + 接入(~中)。 ScalarVolumeI16+buildVoxelint16 重载 + 哨兵透明 + 量化贯穿传递函数/LUT/反量化(§3.5)(~中大,评审上调:适配面比单点重载大)。GprStructuredBuilder(X/Z 落格 + Y 向插值,可并行;GPS 抖动需沿弧长重采样)替代全 3D IDW(~中)。- 显存预算守卫(留 FBO/传函余量)+
LowResResample概览兜底 + 物理分辨率默认网格(~小)。 - 明细真实落盘(raw int16 分块 + sidecar + zlib,不用 vtkHDFWriter)+ 后台重算(含跨线程回调编组)(~中大,评审上调:自写分块格式 + 线程模型)。
- 渲染/切片对 int16 的验证(~小)。
结论(评审:Go 条件式 / 用户定:与 C 都做):B 用"右尺寸 + int16 + 结构化插值"在现有架构上拿到全路段完整体验。前置条件:§3 落盘章节已从 VTKHDF 改为裸分块、§3.5 量化设计已补、第 0 步 spike 通过——满足后即可进入实现。B 与 C 经同一 IVolumeRenderSource 并存,用户按数据规模在两者间切换(能进显存走 B、超显存走 C),落盘格式两者共用(B 的裸分块是 C 分块/金字塔的基座)。