From bf80935b80cd303d49b23b74179616f1904943b5 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Mon, 10 Aug 2026 16:38:37 +0200 Subject: [PATCH] Bragg integration: do not let the radial correction outlive its kernel table The kernel table is sized and built only where the correction can ever run - explicitly on, or auto, which is the same condition the GPU allocates its radial buffers under. BackgroundRadial(true) on any other engine therefore asked the CPU to correct with a single CIRCULAR kernel for rings that may be elongated, while the GPU, having no buffers, did not correct at all: a wrong kernel on one engine and silence on the other, from the same call. Only the auto path calls it today, so it was unreachable, but the setter is public and the invariant it depends on is not local to it. Remember whether the table was built and refuse to raise the flag otherwise. Also treat a zero stencil cap as "uncapped" rather than "no growth". The engine always sets max_grow, so this changes nothing that runs; it makes a caller that forgets it fail loudly instead of silently disabling the feature. Co-Authored-By: Claude Opus 5 (1M context) --- .../bragg_integration/BraggIntegrationEngine.cpp | 6 +++--- image_analysis/bragg_integration/BraggIntegrationEngine.h | 7 ++++++- image_analysis/bragg_integration/BraggStencil.h | 7 ++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/image_analysis/bragg_integration/BraggIntegrationEngine.cpp b/image_analysis/bragg_integration/BraggIntegrationEngine.cpp index 3fd804a0..07dbf6d3 100644 --- a/image_analysis/bragg_integration/BraggIntegrationEngine.cpp +++ b/image_analysis/bragg_integration/BraggIntegrationEngine.cpp @@ -109,9 +109,9 @@ BraggIntegrationEngine::BraggIntegrationEngine(const DiffractionExperiment &expe // n_kern is the largest row BraggStencilKernelIndex can select, plus one. r_max = std::hypot(std::max(beam_x, static_cast(xpixel) - beam_x), std::max(beam_y, static_cast(ypixel) - beam_y)); - const float grow_max = (bkg_radial || bkg_radial_auto) - ? BraggStencilGrow_px(static_cast(r_max), stencil) - : 0.0f; + bkg_radial_built = bkg_radial || bkg_radial_auto; + const float grow_max = bkg_radial_built ? BraggStencilGrow_px(static_cast(r_max), stencil) + : 0.0f; n_kern = static_cast(std::lround(grow_max)) + 1; // Every row must fit: the last one is built at grow = n_kern - 1, which rounding can put just // above grow_max. diff --git a/image_analysis/bragg_integration/BraggIntegrationEngine.h b/image_analysis/bragg_integration/BraggIntegrationEngine.h index f5bf14cd..7deca572 100644 --- a/image_analysis/bragg_integration/BraggIntegrationEngine.h +++ b/image_analysis/bragg_integration/BraggIntegrationEngine.h @@ -156,6 +156,7 @@ protected: // radius. What an elongated ring rules out is one kernel for ALL of them, not the average. bool bkg_radial = false; bool bkg_radial_auto = false; // settings left it unset: decide per image from the ice score + bool bkg_radial_built = false; // the kernel table was sized for the rings this engine uses int k_off = 0; // index of offset 0 within one kernel int k_len = 0; // entries per kernel int n_kern = 1; // kernels in the table (1 = circular stencil) @@ -188,6 +189,10 @@ public: // Turn the radial background correction on or off for the images that follow. The caller owns // the decision; in the auto mode the analysis sets it per image from that image's ice score. - void BackgroundRadial(bool on) { bkg_radial = on; } + // + // It cannot be raised on an engine the constructor did not build the kernel table for: that + // table would hold a single CIRCULAR kernel for rings that may be elongated, and on the GPU the + // radial buffers were never allocated, so the CPU would correct and the GPU would not. + void BackgroundRadial(bool on) { bkg_radial = on && bkg_radial_built; } [[nodiscard]] bool IsBackgroundRadialAuto() const { return bkg_radial_auto; } }; diff --git a/image_analysis/bragg_integration/BraggStencil.h b/image_analysis/bragg_integration/BraggStencil.h index 5e60bd85..d4191988 100644 --- a/image_analysis/bragg_integration/BraggStencil.h +++ b/image_analysis/bragg_integration/BraggStencil.h @@ -59,7 +59,7 @@ struct BraggStencilParams { float r2 = 6.0f, r3 = 10.0f; float bw_sigma = 0.0f; // radial streak per pixel of radius (bandwidth sigma, dimensionless) float k_sigma = 0.0f; // radial sigmas to push the ring out by; 0 = the old circular stencil - float max_grow = 0.0f; // hard cap on the radial growth [px]. 0 disables growth, not the cap. + float max_grow = 0.0f; // hard cap on the radial growth [px]; <= 0 means uncapped }; // One reflection's stencil, in its own radial/tangential frame. @@ -76,9 +76,10 @@ struct BraggStencil { // so the stencil and the radial-background kernel table agree on what "grown by this much" means. BRAGG_STENCIL_HD float BraggStencilGrow_px(float Rpx, const BraggStencilParams &p) { if (!(p.k_sigma > 0.0f)) return 0.0f; - // The cap bounds what a mis-declared bandwidth can do to the bounding box. + // The cap bounds what a mis-declared bandwidth can do to the bounding box. A caller that + // forgets to set it gets the uncapped growth rather than silently no growth at all. const float grow = p.k_sigma * p.bw_sigma * Rpx; - return grow > p.max_grow ? p.max_grow : grow; + return (p.max_grow > 0.0f && grow > p.max_grow) ? p.max_grow : grow; } BRAGG_STENCIL_HD BraggStencil MakeBraggStencil(float px_x, float px_y, const BraggStencilParams &p) {