Choose min-pix-per-spot adaptively per image for serial-stills indexing

For stills indexing the minimum-pixels-per-spot filter is now chosen per image
instead of being fixed: the frame is indexed at min-pix 3/2/1 and the setting that
maximises indexed-spot count weighted by indexed fraction (n_indexed^2 / n_total)
is kept, then integrated once at that min-pix. The fraction factor keeps a smaller
min-pix's extra spots only when the lattice actually explains them, so strong frames
retain their real weak spots (extending resolution) while noise-flooded frames stay
strict.

The mode is selected by the presence of --min-pix-per-spot, now optional
(SpotFindingSettings::min_pix_per_spot is std::optional<int64_t>): omit it for the
adaptive per-image path, give a value to force a fixed min-pix. It applies only to
the stills indexing path -- rotation indexing builds one global lattice and keeps a
fixed min-pix, and the online receiver and the FPGA host path always carry a concrete
value, so neither changes. IndexAndRefine::ProcessImage now returns whether the frame
indexed, to drive the per-image selection.

Exposed in the jfjoch_viewer spot-finding settings (adaptive-threshold and
adaptive-min-pix checkboxes, each greying out the control it overrides); the broker
uses neither.

Validated on the full rotation regression battery (no regression) and the whole
serial-stills target battery at full image count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 18:31:35 +02:00
co-authored by Claude Opus 4.8
parent b65691f312
commit 427f4b3fff
13 changed files with 115 additions and 31 deletions
@@ -89,7 +89,7 @@ void StrongPixelSet::FindSpotsImage(const SpotFindingSettings &settings, std::ve
if (!pixels.empty() && (strong_pixel_count < UINT16_MAX)) {
for (const auto &spot: sparseccl()) {
if ((spot.PixelCount() <= settings.max_pix_per_spot)
&& (spot.PixelCount() >= settings.min_pix_per_spot)) {
&& (spot.PixelCount() >= settings.min_pix_per_spot.value_or(2))) {
spots.push_back(spot);
}
}
@@ -102,7 +102,7 @@ void StrongPixelSet::FindSpots(const DiffractionExperiment &experiment, const Sp
if (!pixels.empty() && (strong_pixel_count < UINT16_MAX)) {
for (const auto &spot: sparseccl()) {
if ((spot.PixelCount() <= settings.max_pix_per_spot)
&& (spot.PixelCount() >= settings.min_pix_per_spot)) {
&& (spot.PixelCount() >= settings.min_pix_per_spot.value_or(2))) {
auto s = spot;
s.ConvertToImageCoordinates(experiment, module_number);
spots.push_back(s);