# 反演剖面三维体:客户目标方法(Surfer)对照与客户端差距 — 2026-06-27 > 来源:客户提供的演示视频 `ScreenShot/9f67e80cb823170bb9374d779ec4c0cb.mp4`(Golden Software Surfer,13min) > 与参考成果图 `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.7:Grid Data 导入对话框,Data Type=**XYZC** | | 2. 三维网格化 | Grid Data → 方法 **Inverse Distance to a Power(IDW,幂次)** → XYZC 直接生成 **3D 网格体**;注脚明示"does not extrapolate beyond the range of data" | t48.7:Gridding Method 选中 IDW;t97.5:Gridding 进度;t195:`.grd has been created` | | 3. 边界裁剪 | 数字化**测区边界多边形**(Base vector / Polyline)→ **Blanking** 把体裁到测区真实足迹 | t292.5:Polygon 工具数字化边界 | | 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) | t585:Volume render 属性;t682:Isosurface(isovalue=1794.39);t633:Image 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-28,branch `fix/3d-volume-blanking-mask`) > 本节记录 §1–6 之后这一轮的来龙去脉、技术取舍与权威佐证,供后续决策不再反复。 ### 7.1 前因后果(时间线) 客户原话:"**我们这个白化确实有问题,填色了,填了蓝色**"——无数据区被填成蓝色,而非 Surfer 那种透明白化。排查分三层、逐个修: 1. **切片填蓝**:`vtkImagePlaneWidget` 会按【输入标量范围】(含哨兵)自动 window/level,把哨兵顶到 LUT 最低色格(蓝)且不透明。 修复:`SliceTool` 钉死 `SetWindowLevel([vmin,vmax])` + `ColorLutBuilder` 预留 0 号"白化槽"(全透明),哨兵( - **GDAL 官方**:正确做法是把 masked NoData 的 "**weights of contributing source pixels are set to zero to ignore them**" / "will not be used in interpolation"。 - **rasterio**:bilinear 重采样在 NoData 边界产生 invalid 值(已知 issue #1721)。 - **Golden Software Surfer**(客户参照工具):NoData "**removed from the neighborhood**",不跨它插值。(定义 ) - **凸包外 = 外推**:"**Extrapolated data is usually meaningless and misleading.**" **结论:二值 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` 线性键)。