Stills keep an overloaded observation as a lower bound instead of dropping it

The stills merge dropped every observation with a saturated pixel. On a still the saturated
observations are the fully recorded ones of the strongest low-resolution reflections, so what
survived were their partial observations, whose partiality correction then set the merged
intensity: on clipped CCD stills the merged low-resolution intensities came out at 0.4-0.6x of
the deposited ones, a ramp no Wilson B absorbs, invisible to CC1/2 and R_split.

The pixels are really clipped (flat-topped plateaus a few per mille wide, a pile-up in the
pixel-value histogram from the per-pixel ADC limit), so the auto ceiling is right; the drop was
the defect. A still now keeps the observation: the box sum with every saturated pixel counted at
the saturation limit, unflagged and marked clipped, counted in BraggIntegrationCounts and
reported as OVERLOADED_REFLECTIONS_KEPT. Rotation data (IsRotationIndexing) are unchanged and
keep the flag for the rocking-event drop.

Measured on a clipped-CCD stills subset (dimple R-free, deposited free set): dropped 0.247,
lower bound 0.230 (same as switching saturation off). A Kabsch fit to the unsaturated wings read
lower than the lower bound (0.237), and inflating the lower bound's sigma lost (0.255).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-10-22 10:36:33 +02:00
1 parent 987e812e23
commit 72cc542d78
15 files changed
+152 -20

No files matched your search

@@ -127,6 +127,8 @@ BraggIntegrationEngine::BraggIntegrationEngine(const DiffractionExperiment &expe
if (overlap == OverlapMode::Exclude && mode == IntegratorMode::BoxSum)
overlap = OverlapMode::Off;
overlap_min_peak = settings.GetOverlapMinPeak();
keep_overloads = !experiment.IsRotationIndexing();
saturation_limit = static_cast<double>(experiment.GetSaturationLimit());
claim = static_cast<float>(R);
inv_claim = 1.0f / claim;
@@ -233,7 +235,7 @@ std::vector<Reflection> BraggIntegrationEngine::Finalize(const std::vector<Refle
size_t npredicted,
const std::vector<BraggPanel> &panels,
const std::vector<BraggFitResult> &results,
int64_t image_number) const {
int64_t image_number) {
// Sized to what is kept, not to what was predicted: this vector is the one every image holds to the
// end of the pass, and a background ring lost to the neighbours drops a reflection here - on a
// dense pattern more than half of them, which a reserve of npredicted would carry as dead capacity.
@@ -258,6 +260,20 @@ std::vector<Reflection> BraggIntegrationEngine::Finalize(const std::vector<Refle
// has_observed is set only for a signal disk every pixel of which was readable.
refl.clipped = !fr.has_observed;
refl.overloaded = fr.overloaded;
// A rotation merge drops the whole rocking event of an overloaded reflection. A still has no
// rocking event: its overloaded observations are the fully recorded ones of the strongest
// reflections, so dropping them leaves those reflections to their partial observations, whose
// partiality correction then sets the merged intensity - and it reads low. Measured on clipped
// CCD stills, the merged low-resolution intensities came out at 0.4-0.6x of the deposited ones.
// So a still keeps the observation as a lower bound: each saturated pixel counted at the
// saturation limit. (A fit to the unsaturated wings was tried and read lower still: the
// profile learned from unsaturated spots does not describe the wings of a clipped one.)
if (fr.overloaded && keep_overloads) {
refl.I = fr.I + static_cast<float>(fr.saturated * (saturation_limit - fr.bkg));
refl.sigma = std::sqrt(fr.var_bkg + std::max(refl.I, 1.0f));
refl.overloaded = false;
++counts.overloads_kept;
}
refl.observed = true;
if (polarization)
refl.prescaling_corr /= geom.CalcAzIntPolarizationCorr(panels[i].plane_x, panels[i].plane_y,