geopro/docs/superpowers/specs/2026-06-27-inversion-3d-vol...

145 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 反演剖面三维体客户目标方法Surfer对照与客户端差距 — 2026-06-27
> 来源:客户提供的演示视频 `ScreenShot/9f67e80cb823170bb9374d779ec4c0cb.mp4`Golden Software Surfer13min
> 与参考成果图 `ScreenShot/Weixin Image_20260627080012_429_2117.png`。
> 结论一句话:**客户用的就是"合并点云 + 3D 反距离加权(IDW) + 边界裁剪(Blanking) + 体绘制/等值面"。**
> 客户端插值内核已与之一致,差距只在「搜索半径 / 边界裁剪 / 等值面」三点。
---
## 0. 背景与一次认知纠偏
用户反馈:"选多个 dd_inversion_data江西理工四条井字相交测线做三维体得到的是四个剖面各自左右
拉伸的薄板,不连成完整体。"
排查中曾推荐"逐深度层各向异性插值"——看了客户演示视频后**确认是过度设计**。客户没有逐层做,
就是**朴素的三维 IDW**(对合并后的整团散点云一次性 3D 网格化)。本文以视频实证为准。
---
## 1. 客户目标方法Surfer 实测,逐帧为证)
| 步骤 | 操作 | 视频帧(时间戳)证据 |
|---|---|---|
| 1. 合并点云 | 所有测线/剖面反演单元合并成**一个 XYZC 文件** `combine-e_m_m.xyz`,列= **X, Y, Elevation, Resistivity, Conductivity, Sensitivity** | t48.7Grid Data 导入对话框Data Type=**XYZC** |
| 2. 三维网格化 | Grid Data → 方法 **Inverse Distance to a PowerIDW幂次** → XYZC 直接生成 **3D 网格体**;注脚明示"does not extrapolate beyond the range of data" | t48.7Gridding Method 选中 IDWt97.5Gridding 进度t195`.grd has been created` |
| 3. 边界裁剪 | 数字化**测区边界多边形**Base vector / Polyline**Blanking** 把体裁到测区真实足迹 | t292.5Polygon 工具数字化边界 |
| 4. 三维渲染 | 3D View → 3D Grid Volume`out-BAIHUA.vtk`**Volume render**(Sliced / Slice count 500 / Tri-linear / Alpha blending / Opacity 80%) + **Isosurface**(阈值 isovalue) + **Image slice**(YZ/Z) | t585Volume render 属性t682Isosurface(isovalue=1794.39)t633Image slice(YZ) |
要点:
- **真三维 IDW**,对合并点云一次成体(非逐层 2D
- IDW **不外推到数据范围外**;测区足迹靠 **Blanking 多边形**裁出(参考图那个不规则边界即来源于此)。
- 红色异常体 = **等值面抽取**Isosurface 按阈值)。
- 坐标为真实投影坐标 + 高程,可叠地形/影像底图。
---
## 2. 客户端现状(已实现部分)
生产路径:`Api3dRepository::createVolume``src/data/api/Api3dRepository.cpp`
→ 把所有选中 ds 的反演单元按测线真实几何配准合并成 `PointSet`
`buildVolume(pts, cellXY, cellZ, power, maxDist)``src/core/algo/VolumeBuilder.cpp`
**三维 IDW**`src/core/algo/IdwInterpolator.cpp``maxDist` 外置 NaN 留空。
即:**「合并点云 + 3D IDW」内核与 Surfer 一致**。参数见 `src/data/repo/VolumeBuildParams.hpp`
`cellXY=1.0, cellZ=0.5, power=2.0, maxDist=4.0`
渲染:`VoxelActor``src/render/actors/VoxelActor.hpp`)仅 GPU 体绘制NaN→透明**无等值面**
`GridContourActor`/`ContourBands` 是 2D 网格等值线,非 3D 等值面)。地形/影像/坐标轴/电极点已有
`TerrainActor`/`TileBasemap`/`AxesActor`/`ElectrodeActor`)。
---
## 3. 差距与修复(共 3 点)
> 不需改插值算法(内核已对);改的是搜索域、裁剪、与等值面。
### G1. 搜索半径 maxDist 太小 → "四块板"
`maxDist=4m` 远小于井字测线间距 → IDW 只填测线 ±4m 管套,线间留空 → 四块薄板。
**修复**:把搜索半径放大到覆盖测区(或提供"覆盖全域"选项),对齐 Surfer 默认搜索域行为。
### G2. 缺边界裁剪(Blanking) → 单纯放大半径只会"变粗"
**这是用户观察到"调大 maxDist 只是让体看起来更粗"的真正原因**:没有足迹裁剪,放大半径会把体
鼓满整个外接盒 → 粗大臃肿。Surfer 不粗,是因为 Blanking 把体裁到了测区真实多边形足迹。
**修复**:加**足迹掩膜**——
- 自动:散点平面**凸包 / alpha-shape**(或沿测线 buffer 并集);
- 或手动:支持用户数字化/导入边界多边形(对齐 Surfer Blanking
掩膜外体素整列置空NaN/透明)。
### G3. 缺 3D 等值面 → 出不来红色异常体
`VoxelActor` 只有体绘制。
**修复**:在体上加 **`vtkFlyingEdges3D` / `vtkContourFilter`** 抽等值面,阈值可调(对齐 Surfer Isosurface
---
## 4. 修复落地顺序
1. **G1+G2 一起做**(插值搜索域放大 + 足迹掩膜)→ 出满铺、裁到测区足迹的体。这是核心,先做。
2. **G3 等值面**(阈值可调)→ 出红色异常体。参考图第二主角,紧接着做。
3. 影像底图/坐标轴/电极点复用现有。
---
## 5. 必须先和客户对齐的预期(避免"又看起来不对"
参考图是**密集测网**(顶面可见很多条测线点阵);江西项目只有**四条井字线**。
- **形态可复刻**(满铺体 + 等值面 + 影像底图);
- 但**框内细节出不来**——参考图的细碎红异常源于密集采样,四条线之间只能给出平滑趋势,
等值面会是几个光滑大团。要那种精度需加密测线。
---
## 6. 非目标 / 说明
- 不改 IDW 内核算法本身(已与 Surfer 一致),不引入逐层各向异性(客户未用)。
- 各向异性搜索椭球可作为后续可选增强Surfer 亦为可选项,非默认),本期不做。
- Kriging 仍为占位(`VolumeBuildParams::Model::Kriging`core 未实现),本期不依赖。
---
## 7. 续:白化方式之争 + 体绘制边界「梯田」2026-06-28branch `fix/3d-volume-blanking-mask`
> 本节记录 §16 之后这一轮的来龙去脉、技术取舍与权威佐证,供后续决策不再反复。
### 7.1 前因后果(时间线)
客户原话:"**我们这个白化确实有问题,填色了,填了蓝色**"——无数据区被填成蓝色,而非 Surfer 那种透明白化。排查分三层、逐个修:
1. **切片填蓝**`vtkImagePlaneWidget` 会按【输入标量范围】(含哨兵)自动 window/level把哨兵顶到 LUT 最低色格(蓝)且不透明。
修复:`SliceTool` 钉死 `SetWindowLevel([vmin,vmax])` + `ColorLutBuilder` 预留 0 号"白化槽"(全透明),哨兵(<vmin)钳到该槽即透明实测证据`tests/spike/slice_alpha_probe.cpp` widget 离屏渲染 + 回读像素背板透出=透明成功)。**这条同时纠正了切片颜色映射**(之前 colorbar 被错误拉伸到切片局部范围)。
2. **早期"满屏蓝"是误判**实为 `maxDist=0`自动覆盖测区`kDefMaxDist=0.0`)把整个凸包**填实**=真实低值数据(蓝)**不是空值**(见 §3 G1)。日志实证请求体 `"maxDist":0`
3. **三维体渗蓝**无数据格设哨兵 `vmin-1`不透明度 0三线性插值在"哨兵真值"交界处插出低值()且不透明度非零 渗一圈蓝为消除给体绘制加**二值 mask**`VoxelActor::makeMaskLike` + `assembleVolume` `vtkGPUVolumeRayCastMapper`+`SetMaskInput`+`SetMaskTypeToBinary`mask=0 体素被光线投射**硬跳过**)。
**mask 的副作用 = 用户 2026-06-28 截图的「竖条/梳齿/底边锯齿」**硬跳过=边界不再三线性插值/羽化。`maxDist=0` 下体填满**凸包足迹**带斜边多边形斜边在 1m 规则网格上离散成**体素阶梯(staircase)**。以前(SmartVolumeMapper+哨兵不透明度0 软消隐)斜边被羽化抹圆看不出 mask 后变成逐体素硬台阶。**即使色阶不透明度=100% 也可见**—— mask 在不透明度传函之前就把体素从光线上删了两个并行子代理渲染侧 + 数据/建体侧共同确诊)。
### 7.2 取舍的本质
| 方案 | 边界观感 | 数据诚实度 | 误判风险 |
|---|---|---|---|
| **二值 mask当前** | 体素梯田难看 | 只画真数据零假值 | 梯田明显是网格对齐的足迹边界不像地质体 |
| 软消隐哨兵不透明度0 mask | 平滑 | 边界是插值出的假值 | **高** |
### 7.3 「细蓝边」是什么 + 为何【不能】回退软消隐(权威佐证)
软消隐回退后的"细蓝边" = 真数据格与哨兵格(`vmin-1`)三线性插值出的、**数据里根本不存在的假低阻值**。低阻在物探通常指含水/导电体这圈蓝出现在测区最外缘会被**误读成"边界存在真实低阻(导电)异常带"**——真有数据分析歧义
多个权威来源一致认定"插值进 NoData 产生边缘伪影"是错误做法 mask 排除
- **ESRI 官方**双线性/三次插值 "**interpolate incorrectly into the NoData** and background areas... producing **artifacts or black ridges**" —— 我们的"蓝脊"与之同源哨兵钳到最低色=蓝)。<https://support.esri.com/en-us/knowledge-base/faq-why-do-bilinear-interpolation-and-cubic-convolution-000003271>
- **GDAL 官方**:正确做法是把 masked NoData 的 "**weights of contributing source pixels are set to zero to ignore them**" / "will not be used in interpolation"。<https://gdal.org/en/stable/programs/gdalwarp.html>
- **rasterio**bilinear 重采样在 NoData 边界产生 invalid 值(已知 issue #1721)。<https://github.com/rasterio/rasterio/issues/1721>
- **Golden Software Surfer**客户参照工具NoData "**removed from the neighborhood**",不跨它插值。<https://surferhelp.goldensoftware.com/gridmisc/Blanked_Nodes_Grid_Filter.htm>(定义 <https://surferhelp.goldensoftware.com/glossary/def_blanking.htm>
- **凸包外 = 外推**"**Extrapolated data is usually meaningless and misleading.**" <https://github.com/fatiando/fatiando/pull/44><https://www.spatialanalysisonline.com/HTML/gridding_and_interpolation_met.htm>
**结论:二值 mask = 业界标准的"把 NoData 排除出插值"做法,是对的**;梯田只是 mask 在斜足迹边界上的网格离散观感(诚实、不误导)。**不应回退软消隐**=让哨兵参与插值=以上权威明确反对的造假值做法)。
### 7.4 决策与待办(截至 2026-06-28本分支未提交
-**保留二值 mask**(数据诚实/合规,符合 ESRI/GDAL/Surfer 标准)。
- 梯田若要压平,走**不造假值**的路(二选一,**待用户/客户拍板**
- (a) **细化 XY 网格**`cellXY` 1m→0.5m阶梯缩到亚像素代价体素×4、耗时 ~3.5s→~14s、内存×4。
- (b) **接受梯田**:它诚实、且明显是足迹边界,不会被当成地质体。
- 渲染侧本轮其它已落地修复(排查"分层/稠密"时做、确认非主因但保留GPU 探测+CPU 回退(`setVolumeGpuSupported`)、细采样距离+`UseJittering`、`ScalarOpacityUnitDistance=对角/10`、去 `kMaxOpacity`(改由色阶「不透明度」单一控制、100%=实心)、移除工具条「透」滑块。
- **附带缺陷(待修)**`VoxelGenerateRequest::maxDist` 结构体默认 `4.0``src/data/dto/Vtk3dRequests.hpp:18`)与对话框 `kDefMaxDist=0.0``VolumeParamsDialog.cpp:34`)不一致——绕过对话框直建会拿到 4m → 退回"四块板/线间空隙"老问题,应统一为 0。
- 抽稀空间哈希 `(ix*p1)^(iy*p2)^(iz*p3)``VolumeBuilder.cpp` ~146-151为 XOR 非单射、有碰撞风险(与本症无关,但宜换 `(iz*ny+iy)*nx+ix` 线性键)。