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

11 KiB
Raw Permalink Blame History

反演剖面三维体客户目标方法Surfer对照与客户端差距 — 2026-06-27

来源:客户提供的演示视频 ScreenShot/9f67e80cb823170bb9374d779ec4c0cb.mp4Golden 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 / PolylineBlanking 把体裁到测区真实足迹 t292.5Polygon 工具数字化边界
4. 三维渲染 3D View → 3D Grid Volumeout-BAIHUA.vtkVolume 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::createVolumesrc/data/api/Api3dRepository.cpp → 把所有选中 ds 的反演单元按测线真实几何配准合并成 PointSetbuildVolume(pts, cellXY, cellZ, power, maxDist)src/core/algo/VolumeBuilder.cpp三维 IDWsrc/core/algo/IdwInterpolator.cppmaxDist 外置 NaN 留空。

即:「合并点云 + 3D IDW」内核与 Surfer 一致。参数见 src/data/repo/VolumeBuildParams.hpp cellXY=1.0, cellZ=0.5, power=2.0, maxDist=4.0

渲染:VoxelActorsrc/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::Krigingcore 未实现),本期不依赖。

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三线性插值在"哨兵↔真值"交界处插出低值(蓝)且不透明度非零 → 渗一圈蓝。为消除,给体绘制加二值 maskVoxelActor::makeMaskLike + assembleVolumevtkGPUVolumeRayCastMapper+SetMaskInput+SetMaskTypeToBinarymask=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 排除:

结论:二值 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)、细采样距离+UseJitteringScalarOpacityUnitDistance=对角/10、去 kMaxOpacity(改由色阶「不透明度」单一控制、100%=实心)、移除工具条「透」滑块。
  • 附带缺陷(待修)VoxelGenerateRequest::maxDist 结构体默认 4.0src/data/dto/Vtk3dRequests.hpp:18)与对话框 kDefMaxDist=0.0VolumeParamsDialog.cpp:34)不一致——绕过对话框直建会拿到 4m → 退回"四块板/线间空隙"老问题,应统一为 0。
  • 抽稀空间哈希 (ix*p1)^(iy*p2)^(iz*p3)VolumeBuilder.cpp ~146-151为 XOR 非单射、有碰撞风险(与本症无关,但宜换 (iz*ny+iy)*nx+ix 线性键)。