scaling: robust (Cauchy) loss on the per-image scale fit
The per-image G/B/mosaicity fit used a plain L2 loss, so a few outlier reflections occasionally dragged a frame into a bad optimum - a stochastic (~15% of runs) per-frame mis-scaling that elevated R-meas and collapsed CC1/2 at low symmetry (the image-level CC1/2 half-split makes the damage look patchy across shells, while the data is genuinely noisier). A Cauchy loss (3 sigma) soft-downweights those outliers without a hard cut: MyoB 0/10 catastrophic (was ~2/10), R-meas stable, and ISa improves on every test crystal (EP0210 9.2->12.4, MyoB 12.5->14.6, lysoC 10.4->11.2, cytC 11.5->12.6), most on low symmetry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,12 @@
|
||||
#include <ceres/rotation.h>
|
||||
|
||||
namespace {
|
||||
// Robust loss scale (in sigma units) for the per-image scale fit: a few outlier reflections
|
||||
// (zingers, overlaps, a mis-predicted spot) must not drag a frame's G/B into a bad optimum -
|
||||
// that is the stochastic per-frame mis-scaling that elevates R-meas and collapses CC1/2 at low
|
||||
// symmetry. Cauchy down-weights residuals beyond ~this many sigma without a hard cut.
|
||||
constexpr double SCALE_ROBUST_K = 3.0;
|
||||
|
||||
double SafeInv(double x, double fallback) {
|
||||
if (!std::isfinite(x) || x == 0.0)
|
||||
return fallback;
|
||||
@@ -248,19 +254,19 @@ void ScaleOnTheFly::Scale(IntegrationOutcome &integration_outcome) const {
|
||||
case PartialityModel::Fixed: {
|
||||
auto *cost = new ceres::AutoDiffCostFunction<IntensityFixedResidual, 1, 1, 1>(
|
||||
new IntensityFixedResidual(r, Itrue, sigma, r.partiality));
|
||||
problem.AddResidualBlock(cost, nullptr, &result.G, &result.B);
|
||||
problem.AddResidualBlock(cost, new ceres::CauchyLoss(SCALE_ROBUST_K), &result.G, &result.B);
|
||||
}
|
||||
break;
|
||||
case PartialityModel::Unity: {
|
||||
auto *cost = new ceres::AutoDiffCostFunction<IntensityFixedResidual, 1, 1, 1>(
|
||||
new IntensityFixedResidual(r, Itrue, sigma, 1.0));
|
||||
problem.AddResidualBlock(cost, nullptr, &result.G, &result.B);
|
||||
problem.AddResidualBlock(cost, new ceres::CauchyLoss(SCALE_ROBUST_K), &result.G, &result.B);
|
||||
}
|
||||
break;
|
||||
case PartialityModel::Rotation: {
|
||||
auto *cost = new ceres::AutoDiffCostFunction<ScalingRotationResidual, 1, 1, 1, 1, 1>(
|
||||
new ScalingRotationResidual(r, Itrue, sigma));
|
||||
problem.AddResidualBlock(cost, nullptr, &result.G, &result.B, &result.mos,
|
||||
problem.AddResidualBlock(cost, new ceres::CauchyLoss(SCALE_ROBUST_K), &result.G, &result.B, &result.mos,
|
||||
&result.wedge);
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user