Stills scaling: leave an image unscaled when its scale collapses
SolveScaleIRLS returns whatever it converged to and both writers accept any G > 0, so a fit that collapsed to ~1e-3 multiplies that image's intensities by a thousand. Nothing downstream notices, because the sigmas are multiplied by the same factor and the merge's n-sigma outlier test is therefore blind to it - only a total collapse self-heals, by overflowing corr to inf. The rotation path refuses a per-frame scale this far below its neighbours; the stills path had no guard. Judge each image against the median of the images that did scale, and put a collapsed one back to G = 1 - the same state as an image with too few reflections to fit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "ScaleOnTheFly.h"
|
||||
|
||||
#include "../../common/Logger.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
@@ -16,6 +18,10 @@ namespace {
|
||||
// symmetry. Cauchy down-weights residuals beyond ~this many sigma without a hard cut.
|
||||
constexpr double SCALE_ROBUST_K = 3.0;
|
||||
|
||||
// Smallest per-image scale, relative to the run's median, that is still believable as a scale
|
||||
// rather than a failed fit. The same ratio guards the rotation path's per-frame scales.
|
||||
constexpr double MIN_CREDIBLE_SCALE_RATIO = 0.02;
|
||||
|
||||
double SafeInv(double x, double fallback) {
|
||||
if (!std::isfinite(x) || x == 0.0)
|
||||
return fallback;
|
||||
@@ -146,6 +152,45 @@ void ScaleOnTheFly::Scale(IntegrationOutcome &integration_outcome) const {
|
||||
integration_outcome.image_scale_wedge_deg.reset();
|
||||
}
|
||||
|
||||
// A per-image scale that has collapsed toward zero multiplies that image's intensities by 1/G - and its
|
||||
// sigmas by the same factor, so nothing downstream can recognise it: the merge's n-sigma outlier test
|
||||
// scales with the very number that is wrong. The rotation path already refuses a per-frame scale this
|
||||
// far below its neighbours; the stills path had no such guard. An image whose fit collapsed is left
|
||||
// UNSCALED (G = 1, the same state as an image with too few reflections to fit) rather than merged with
|
||||
// its intensities blown up.
|
||||
void ScaleOnTheFly::RejectCollapsedScales(std::vector<IntegrationOutcome> &integration) {
|
||||
std::vector<double> fitted;
|
||||
fitted.reserve(integration.size());
|
||||
for (const auto &i: integration)
|
||||
if (i.image_scale_g && std::isfinite(*i.image_scale_g) && *i.image_scale_g > 0.0)
|
||||
fitted.push_back(*i.image_scale_g);
|
||||
if (fitted.size() < 2)
|
||||
return;
|
||||
|
||||
const size_t mid = fitted.size() / 2;
|
||||
std::nth_element(fitted.begin(), fitted.begin() + mid, fitted.end());
|
||||
const double g_floor = fitted[mid] * MIN_CREDIBLE_SCALE_RATIO;
|
||||
|
||||
int64_t n_rejected = 0;
|
||||
for (auto &i: integration) {
|
||||
if (!i.image_scale_g || !std::isfinite(*i.image_scale_g) || *i.image_scale_g >= g_floor)
|
||||
continue;
|
||||
for (auto &r: i.reflections)
|
||||
r.image_scale_corr = (std::isfinite(r.rlp) && r.partiality > 0.0f)
|
||||
? static_cast<float>(r.rlp / r.partiality)
|
||||
: NAN;
|
||||
i.image_scale_cc.reset();
|
||||
i.image_scale_cc_n.reset();
|
||||
i.image_scale_g.reset();
|
||||
++n_rejected;
|
||||
}
|
||||
if (n_rejected > 0)
|
||||
Logger("ScaleOnTheFly").Warning(
|
||||
"Left {} image(s) unscaled: their per-image scale collapsed more than {:.0f}x below the "
|
||||
"run median, which would have amplified their intensities by the same factor",
|
||||
n_rejected, 1.0 / MIN_CREDIBLE_SCALE_RATIO);
|
||||
}
|
||||
|
||||
void ScaleOnTheFly::Scale(std::vector<IntegrationOutcome> &integration, size_t nthreads) const {
|
||||
if (nthreads == 0)
|
||||
nthreads = std::thread::hardware_concurrency();
|
||||
@@ -171,4 +216,6 @@ void ScaleOnTheFly::Scale(std::vector<IntegrationOutcome> &integration, size_t n
|
||||
for (auto &f: futures)
|
||||
f.get();
|
||||
}
|
||||
|
||||
RejectCollapsedScales(integration);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user