From 1c748ba41efb711ed5699eb99b11ba79d2c91978 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Wed, 14 Oct 2026 19:42:31 +0200 Subject: [PATCH] BraggIntegrationEngineCPU: background clip reads the ring pixels pass A gathered, output unchanged The one-pass sigma clip of the background ring walked the reflection's whole box a second time, recomputing every pixel's stencil distances and neighbour mask. Pass A now keeps the ring pixels the clip reads (readable, not a neighbour's flux), with their radial offset, in pixel order, and the clip sums those - the same values in the same order. Measured on cytochrome C (CPU-only, Apple M6): the clip loop was 8.4% of CPU; the run is 4-5% faster with this and the ring-histogram change together. Merged reflections and maps byte-identical. Co-Authored-By: Claude Opus 5.5 --- .../BraggIntegrationEngineCPU.cpp | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/image_analysis/bragg_integration/BraggIntegrationEngineCPU.cpp b/image_analysis/bragg_integration/BraggIntegrationEngineCPU.cpp index 146e850e6..b62ab2aa9 100644 --- a/image_analysis/bragg_integration/BraggIntegrationEngineCPU.cpp +++ b/image_analysis/bragg_integration/BraggIntegrationEngineCPU.cpp @@ -208,6 +208,10 @@ std::vector BraggIntegrationEngineCPU::RunImpl(const Sampler &img, std::vector rough(npredicted); double inv_d2_min = std::numeric_limits::max(), inv_d2_max = 0.0; std::vector bkg_vals; // reused per reflection for the trimmed-mean background (idea 1) + // The ring pixels the background clip below reads, with their radial offsets, gathered while pass + // A walks the box anyway - in the order the clip sums them, so it sums the same values. + struct ClipPixel { int32_t px; float rad; }; + std::vector clip_ring; // Radial background curve, accumulated from the annulus pixels this pass already reads. A pixel's // radius is the reflection's radius plus the pixel's projection on the beam->reflection direction, @@ -266,10 +270,13 @@ std::vector BraggIntegrationEngineCPU::RunImpl(const Sampler &img, // separable from what the detector took. int n_ring = 0, n_clean = 0; bkg_vals.clear(); + clip_ring.clear(); for (int y = y0; y <= y1; ++y) for (int x = x0; x <= x1; ++x) { const auto d = BraggStencilDistances(st, x - r.predicted_x, y - r.predicted_y); const int32_t px = img[y * W + x]; + if (do_clip && d.inner >= r2_sq && d.outer < r3_sq && valid(px) && refl_mask.Get(x, y) != MASK_FLUX) + clip_ring.push_back({px, d.rad}); if (BraggStencilInSignal(st, x - r.predicted_x, y - r.predicted_y, d.signal, r1_sq)) { // A pixel a nearer neighbour owns carries that neighbour's flux, so in Exclude // mode it leaves the disk entirely - the sum, the pixel count the background is @@ -353,23 +360,16 @@ std::vector BraggIntegrationEngineCPU::RunImpl(const Sampler &img, * std::max(out.bkg, 1.0)); double s = 0.0; int n = 0; - for (int y = y0; y <= y1; ++y) - for (int x = x0; x <= x1; ++x) { - const auto d = BraggStencilDistances(st, x - r.predicted_x, y - r.predicted_y); - if (!(d.inner >= r2_sq && d.outer < r3_sq)) continue; - if (refl_mask.Get(x, y) == MASK_FLUX) continue; - const int32_t px = img[y * W + x]; - if (!valid(px)) continue; - if (static_cast(px) <= thr) { - s += px; - ++n; - if (bkg_radial) { - // The radial curve is binned on the TRUE detector radius, so the - // offset here is the unshrunk radial projection. - const int b = std::clamp(static_cast(std::lround(st.r0 + d.rad)), 0, n_rad - 1); - rad_sum[b] += static_cast(px); - ++rad_cnt[b]; - } + for (const auto &[px, rad] : clip_ring) + if (static_cast(px) <= thr) { + s += px; + ++n; + if (bkg_radial) { + // The radial curve is binned on the TRUE detector radius, so the + // offset here is the unshrunk radial projection. + const int b = std::clamp(static_cast(std::lround(st.r0 + rad)), 0, n_rad - 1); + rad_sum[b] += static_cast(px); + ++rad_cnt[b]; } } if (n > 5) { out.bkg = s / n; n_bkg_used = n; }