diff --git a/tools/gpr_poc/main.cpp b/tools/gpr_poc/main.cpp index 41d9da6..cf46e2d 100644 --- a/tools/gpr_poc/main.cpp +++ b/tools/gpr_poc/main.cpp @@ -1164,6 +1164,13 @@ geopro::core::ColorScale makeStructuralColorScale(double vmin, double vmax) { // 地震高对比色阶(seismic 红-白-蓝):两端饱和亮色(强正=亮红、强负=亮蓝), // 零附近白。比 structural 更亮、对比更狠,正负反射一眼分开。 +// 前置声明(实现在 polish 段):梯度幅值分位统计,供 C4 画廊的梯度不透明度标定。 +struct GradStats { + double median = 0, p75 = 0, p90 = 0, p99 = 0, mx = 0; + std::size_t samples = 0; +}; +GradStats sampleGradientMagnitude(vtkImageData* img); + geopro::core::ColorScale makeSeismicColorScale(double vmin, double vmax) { geopro::core::ColorScale cs; const double span = (vmax > vmin) ? (vmax - vmin) : 1.0; @@ -2803,32 +2810,45 @@ struct GalleryVariant { double zoom; // 再 Zoom 填满画面 double bg[3]; // 背景 RGB const char* desc; // 报告用中文说明 + // ---- C4 视觉调优(梯度不透明度 + 光照),默认关 → 不改既有变体行为 ---- + bool useGradientOpacity = false; // SetGradientOpacity:均匀层透明、界面/异常显形 + bool useShade = false; // SetShade:层界面带立体明暗 }; -// 4 组视觉参数。值经离屏实跑挑出(详见报告)。 +// C4 视觉调优:4 组对照(纯标量 / +梯度不透明度 / +光照 / 全开)。 +// 同一局部段、同一 seismic 配色、同一斜穿俯视取景(El45/Az30,视线穿进体内部而非 +// 只看端面)——唯一变量是「梯度不透明度 / 光照」,供肉眼直读"内部结构是否透出"。 +// maxOpacity 字段:纯标量组用低峰值(0.45,避免均匀积分糊成实心);开了梯度门的组用 +// 高峰值(0.6)——均匀区被梯度门压透明后,层界面的净不透明度(标量×梯度)才够高、浮成实面。 +// 末项 var4(全开) = kViewDefaultVariant → 交互窗口默认走全开。 const GalleryVariant kGalleryVariants[] = { - // var1:高不透明度实体感——seismic 亮配色 + V 形包络(中段 0.40/两端 0.85), - // floor 压到 0.04:近零层间隙近透明,亮层面浮出 → 内部层状结构可读。 + // var1:纯标量不透明度(基线)——无梯度门、无光照。GPR 均匀层段沿射线积分糊成 + // 半透明实心块,只外表面/端面可读,内部水平分层难分辨(对照基准)。 {"var1", OpacityProfile::kSolid, ColorChoice::kSeismic, - 0.04, 0.40, 0.85, 8.0, 22.0, 28.0, 1.9, {0.05, 0.05, 0.09}, - "高不透明度实体感:V形包络(floor0.04/mid0.40/max0.85)+seismic 亮配色," - "半透明实心、内部层次可见"}, - // var2:高对比配色——jet 全程高饱和 + 中等不透明度 V 形包络。 - {"var2", OpacityProfile::kSolid, ColorChoice::kJet, - 0.04, 0.32, 0.70, 8.0, 22.0, 28.0, 1.9, {0.06, 0.06, 0.10}, - "高对比配色:jet 蓝青绿黄红全程高饱和 + 中等 V 形包络(mid0.32/max0.70)"}, - // var3:居中正对纵截面——低 Elevation/Azimuth 摆平、正对 X-Z 长侧面(层状反射沿 - // X 延展最清晰)、Zoom2.0 填满 ~70%;floor 压更低让层间隙透明、层面立体。 + 0.04, 0.30, 0.45, 8.0, 45.0, 30.0, 1.5, {0.05, 0.05, 0.09}, + "纯标量不透明度(基线):V形包络(floor0.04/mid0.30/max0.45)+seismic," + "无梯度门/无光照,均匀层积分糊成半透明块、仅端面可读", + /*useGradientOpacity=*/false, /*useShade=*/false}, + // var2:+梯度不透明度——低梯度(均匀层)透明、高梯度(层界面/异常)显形,射线穿透 + // 均匀雾、内部界面浮出。标量峰值提到 0.6(梯度门压透明后层面才够实)。 + {"var2", OpacityProfile::kSolid, ColorChoice::kSeismic, + 0.04, 0.30, 0.60, 8.0, 45.0, 30.0, 1.5, {0.05, 0.05, 0.09}, + "+梯度不透明度:低梯度(均匀层)透明、高梯度(界面/异常)显形,标量峰值0.6," + "射线穿透均匀雾、内部界面浮出", + /*useGradientOpacity=*/true, /*useShade=*/false}, + // var3:+光照——在梯度门基础上 ShadeOn,层界面带立体明暗(非纯平涂)。 {"var3", OpacityProfile::kSolid, ColorChoice::kSeismic, - 0.03, 0.38, 0.82, 9.0, 10.0, 12.0, 2.0, {0.05, 0.05, 0.09}, - "居中正对纵截面:低 El10/Az12 摆平正对 X-Z 长侧面、Zoom2.0 填满视野," - "floor0.03 凸显层面,exagg9 放大薄轴"}, - // var4:最像真实 GPR 三维图——seismic + 略提背景亮 + 微立体角 + 实体包络。 + 0.04, 0.30, 0.60, 8.0, 45.0, 30.0, 1.5, {0.05, 0.05, 0.09}, + "+光照(梯度门+Shade):层界面带立体明暗(Ambient0.3/Diffuse0.7/Specular0.2)," + "界面起伏更有体积感", + /*useGradientOpacity=*/true, /*useShade=*/true}, + // var4:全开——梯度不透明度 + 光照 + seismic 双端斜坡配色 + 略亮冷灰背景。 // 综合最佳,选作交互窗口默认(kViewDefaultVariant)。 {"var4", OpacityProfile::kSolid, ColorChoice::kSeismic, - 0.035, 0.38, 0.84, 8.0, 18.0, 22.0, 2.0, {0.07, 0.08, 0.11}, - "综合最佳:seismic + 实体包络(floor0.035/mid0.38/max0.84) + 微立体取景" - "(El18/Az22/Zoom2.0) + 略亮冷灰背景"}, + 0.04, 0.30, 0.60, 8.0, 45.0, 30.0, 1.5, {0.07, 0.08, 0.11}, + "全开(综合最佳):梯度不透明度+光照+seismic+略亮冷灰背景,内部分层界面/异常" + "从均匀块透出 → 交互窗口默认", + /*useGradientOpacity=*/true, /*useShade=*/true}, }; // 交互窗口(无 flag 的 view)的默认视觉变体 = var4(kGalleryVariants 末项)。 @@ -2847,15 +2867,43 @@ geopro::core::ColorScale pickColor(ColorChoice c, double vmin, double vmax) { // 按变体的不透明度包络建体属性(gallery / 交互默认共用,DRY)。kSolid 走 V 形实体 // 包络(floor/mid/max),kStructural 走双端斜坡(仅 maxOpacity)。 +// +// C4 视觉调优:若变体开了 useGradientOpacity 且传入了实测梯度统计 gs,再叠加 +// 1) 梯度不透明度(SetGradientOpacity):低梯度(均匀层)→透明、高梯度(界面/异常)→显形, +// 让射线穿透均匀雾、内部界面浮出(阈值按 gs 的 median/p90/p99 标定,不靠猜); +// 2) 光照(useShade → SetShade + Ambient/Diffuse/Specular):层界面带立体明暗。 vtkSmartPointer makeVariantProperty( const GalleryVariant& v, const geopro::core::Quant& q, const geopro::core::ColorScale& cs, double vmin, double vmax, - double maxOpacity) { + double maxOpacity, const GradStats* gs = nullptr) { + vtkSmartPointer prop; if (v.profile == OpacityProfile::kSolid) { - return makeSolidVolumeProperty(q, cs, vmin, vmax, v.floorOpacity, + prop = makeSolidVolumeProperty(q, cs, vmin, vmax, v.floorOpacity, v.midOpacity, maxOpacity); + } else { + prop = makeTunedVolumeProperty(q, cs, vmin, vmax, maxOpacity); } - return makeTunedVolumeProperty(q, cs, vmin, vmax, maxOpacity); + + // 梯度不透明度:均匀层(低梯度)透明,层界面/异常边缘(高梯度)显形。阈值按实测分布: + // median 处仍 0(压住均匀积分雾),p90 升到 0.5,p99 到 0.9。无 gs(未测)则跳过。 + if (v.useGradientOpacity && gs != nullptr && gs->samples > 0) { + vtkNew grad; + grad->AddPoint(0.0, 0.0); + grad->AddPoint(std::max(1.0, gs->median), 0.0); + grad->AddPoint(std::max(2.0, gs->p90), 0.5); + grad->AddPoint(std::max(3.0, gs->p99), 0.9); + prop->SetGradientOpacity(grad); + } + + // 光照:ShadeOn + Ambient/Diffuse/Specular,让层界面带立体明暗(区别于纯平涂)。 + if (v.useShade) { + prop->ShadeOn(); + prop->SetAmbient(0.3); + prop->SetDiffuse(0.7); + prop->SetSpecular(0.2); + prop->SetSpecularPower(10.0); + } + return prop; } // ============================================================================ @@ -3066,14 +3114,31 @@ int runGalleryVariant(const std::string& dir, const GalleryVariant& v, const double vmin = m.vminPhys, vmax = m.vmaxPhys; const geopro::core::ColorScale cs = pickColor(v.color, vmin, vmax); - vtkSmartPointer prop = - makeVariantProperty(v, m.quant, cs, vmin, vmax, v.maxOpacity); - auto rw = makeOffscreenWindow(winW, winH); vtkNew ren; ren->SetBackground(v.bg[0], v.bg[1], v.bg[2]); rw->AddRenderer(ren); + // 局部段(沿线中段,同 viewSetupDefaultFrame 的取段法)。先建体 → 实测梯度分布 + // 用于 C4 梯度不透明度标定(仅开了 useGradientOpacity 的变体需要)。 + const int totBx = store.bricksX(0); + const int localBx = std::min(kViewDefaultLocalBricks, totBx); + const int bx0 = std::max(0, totBx / 2 - localBx / 2); + vtkSmartPointer locImg = + buildLocalLevel0Image(store, m, bx0, localBx); + + GradStats gs; + if (v.useGradientOpacity) { + gs = sampleGradientMagnitude(locImg.Get()); + std::cout << "[gallery " << v.name << "] 梯度幅值分布(量化域,样本 " << gs.samples + << "): median=" << gs.median << " p90=" << gs.p90 + << " p99=" << gs.p99 << " max=" << gs.mx << "\n"; + } + + vtkSmartPointer prop = makeVariantProperty( + v, m.quant, cs, vmin, vmax, v.maxOpacity, + v.useGradientOpacity ? &gs : nullptr); + vtkNew mapper; mapper->SetRequestedRenderMode(vtkSmartVolumeMapper::GPURenderMode); auto volume = vtkSmartPointer::New(); @@ -3082,12 +3147,6 @@ int runGalleryVariant(const std::string& dir, const GalleryVariant& v, volume->SetScale(1.0, v.exagg, v.exagg); ren->AddVolume(volume); - // 局部段(沿线中段,同 viewSetupDefaultFrame 的取段法)。 - const int totBx = store.bricksX(0); - const int localBx = std::min(kViewDefaultLocalBricks, totBx); - const int bx0 = std::max(0, totBx / 2 - localBx / 2); - vtkSmartPointer locImg = - buildLocalLevel0Image(store, m, bx0, localBx); std::vector> one{locImg}; auto mb = makeMultiBlock(one); mapper->SetInputDataObject(mb); @@ -3261,8 +3320,17 @@ int cmdView(int argc, char** argv) { const double vmin = m.vminPhys, vmax = m.vmaxPhys; // 配色/不透明度包络取自 var4:seismic + V 形实体包络(floor/mid + opacity 作峰值)。 const geopro::core::ColorScale cs = pickColor(dv.color, vmin, vmax); - vtkSmartPointer prop = - makeVariantProperty(dv, m.quant, cs, vmin, vmax, opacity); + // C4:默认变体(var4)开了梯度不透明度 → 从常驻底图实测梯度分布标定阈值。底图恒非空。 + GradStats dvGs; + if (dv.useGradientOpacity && source.baseImage() != nullptr) { + dvGs = sampleGradientMagnitude(source.baseImage()); + std::cout << "[view] 梯度幅值分布(底图,量化域,样本 " << dvGs.samples + << "): median=" << dvGs.median << " p90=" << dvGs.p90 + << " p99=" << dvGs.p99 << "\n"; + } + vtkSmartPointer prop = makeVariantProperty( + dv, m.quant, cs, vmin, vmax, opacity, + dv.useGradientOpacity ? &dvGs : nullptr); // 渲染窗口:preview/smoke 走离屏,否则真窗口。 vtkSmartPointer rw; @@ -3616,10 +3684,7 @@ constexpr double kPolishMaxOpacityGrad = 0.6; // b/c:梯度门控后可提 // 在 VTK_SHORT 局部体上采样梯度幅值分布(量化域,中心差分),返回有序的若干分位数。 // 跳过 kBlank 体素及其邻居(空值不参与梯度)。返回 {median, p75, p90, p99, max}。 -struct GradStats { - double median = 0, p75 = 0, p90 = 0, p99 = 0, mx = 0; - std::size_t samples = 0; -}; +// (GradStats 已在文件上方前置声明,供 C4 画廊共用。) GradStats sampleGradientMagnitude(vtkImageData* img) { int dims[3]; img->GetDimensions(dims);