Remove the experimental PixelRefine integrator
On the LysozymeJet5 serial stills the default Gaussian profile-fit integrator (ProfileIntegrate2D) + reference scaling matched or beat whole-PixelRefine on every per-shell CC1/2 (overall 95.7% vs 91.9%), ISa (1.6 vs 1.2) and R-meas (98.5% vs 175%), with CCref a tie -- so PixelRefine has no remaining advantage. Reference-based per-image scaling is integrator-agnostic (IndexAndRefine::ReferenceIntensities builds a ScaleOnTheFly(experiment, reference) applied to any integrator's output), so the reference-dataset feature (CCref + reference scaling) is kept. Delete image_analysis/pixel_refinement/, GeomRefinementAlgorithmEnum:: PixelRefine and its gates, BraggIntegrationSettings::ProfileMultiplier (PixelRefine-only; R1 is shared and kept), and the -r pixelrefine / --profile-multiplier CLI. The inherited lessons (mean background, de-biased variance, tight-profile-loses / centroid floor, R-refinement futile) are folded into NEXTGEN_INTEGRATOR.md. NOTE: this transiently breaks the viewer build -- the committed viewer still references the removed enum and ProfileMultiplier. It is fixed in the next commit (the viewer feature work), held separate while the viewer UI is being tested. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -206,10 +206,6 @@ void IndexAndRefine::RefineGeometryIfNeeded(DataMessage &msg, IndexAndRefine::In
|
||||
XtalOptimizerRotationOnly(data, msg.spots, 0.05);
|
||||
break;
|
||||
case GeomRefinementAlgorithmEnum::BeamCenter:
|
||||
case GeomRefinementAlgorithmEnum::PixelRefine:
|
||||
// PixelRefine still benefits from the classical beam-center + cell
|
||||
// refinement as a starting point; the pixel-level refinement runs
|
||||
// later, during integration.
|
||||
if (XtalOptimizer(data, {msg.spots})) {
|
||||
outcome.experiment.BeamX_pxl(data.geom.GetBeamX_pxl())
|
||||
.BeamY_pxl(data.geom.GetBeamY_pxl());
|
||||
@@ -323,34 +319,20 @@ void IndexAndRefine::QuickPredictAndIntegrate(DataMessage &msg,
|
||||
.bandwidth_sigma = experiment.GetBandwidthFWHM().value_or(0.0f) / 2.3548f
|
||||
};
|
||||
|
||||
// Select the integration path: classical 2D integration, or the experimental
|
||||
// PixelRefine joint geometry/profile/scale refinement (which also produces the
|
||||
// integrated reflections that flow into the normal save/merge).
|
||||
const bool use_pixel_refine =
|
||||
experiment.GetIndexingSettings().GetGeomRefinementAlgorithm() == GeomRefinementAlgorithmEnum::PixelRefine
|
||||
&& !pixel_reference_.empty();
|
||||
// Predict, then integrate with the selected integrator (box-sum or profile-fit).
|
||||
auto pred_start_time = std::chrono::steady_clock::now();
|
||||
auto nrefl = prediction.Calc(outcome.experiment, latt, settings_prediction);
|
||||
auto pred_end_time = std::chrono::steady_clock::now();
|
||||
msg.bragg_prediction_time_s = std::chrono::duration<float>(pred_end_time - pred_start_time).count();
|
||||
|
||||
if (use_pixel_refine) {
|
||||
auto integration_start_time = std::chrono::steady_clock::now();
|
||||
PixelRefineIntegrate(msg, image, prediction, outcome, i_outcome);
|
||||
msg.integrated_reflections = i_outcome.reflections.size();
|
||||
auto integration_end_time = std::chrono::steady_clock::now();
|
||||
msg.integration_time_s = std::chrono::duration<float>(integration_end_time - integration_start_time).count();
|
||||
} else {
|
||||
auto pred_start_time = std::chrono::steady_clock::now();
|
||||
auto nrefl = prediction.Calc(outcome.experiment, latt, settings_prediction);
|
||||
auto pred_end_time = std::chrono::steady_clock::now();
|
||||
msg.bragg_prediction_time_s = std::chrono::duration<float>(pred_end_time - pred_start_time).count();
|
||||
|
||||
auto integration_start_time = std::chrono::steady_clock::now();
|
||||
i_outcome.reflections =
|
||||
outcome.experiment.GetBraggIntegrationSettings().GetIntegrator() == IntegratorMode::BoxSum
|
||||
? BraggIntegrate2D(outcome.experiment, image, prediction.GetReflections(), nrefl, msg.number)
|
||||
: ProfileIntegrate2D(outcome.experiment, image, prediction.GetReflections(), nrefl, msg.number);
|
||||
msg.integrated_reflections = i_outcome.reflections.size();
|
||||
auto integration_end_time = std::chrono::steady_clock::now();
|
||||
msg.integration_time_s = std::chrono::duration<float>(integration_end_time - integration_start_time).count();
|
||||
}
|
||||
auto integration_start_time = std::chrono::steady_clock::now();
|
||||
i_outcome.reflections =
|
||||
outcome.experiment.GetBraggIntegrationSettings().GetIntegrator() == IntegratorMode::BoxSum
|
||||
? BraggIntegrate2D(outcome.experiment, image, prediction.GetReflections(), nrefl, msg.number)
|
||||
: ProfileIntegrate2D(outcome.experiment, image, prediction.GetReflections(), nrefl, msg.number);
|
||||
msg.integrated_reflections = i_outcome.reflections.size();
|
||||
auto integration_end_time = std::chrono::steady_clock::now();
|
||||
msg.integration_time_s = std::chrono::duration<float>(integration_end_time - integration_start_time).count();
|
||||
|
||||
constexpr size_t kMaxReflections = 10000;
|
||||
if (i_outcome.reflections.size() > kMaxReflections) {
|
||||
@@ -371,10 +353,7 @@ void IndexAndRefine::QuickPredictAndIntegrate(DataMessage &msg,
|
||||
CalcISigma(msg, i_outcome.reflections);
|
||||
CalcWilsonBFactor(msg, i_outcome.reflections);
|
||||
|
||||
// PixelRefine produces already-scaled reflections; only the classical path
|
||||
// needs the separate ScaleOnTheFly step.
|
||||
if (!use_pixel_refine)
|
||||
ScaleImage(msg, i_outcome);
|
||||
ScaleImage(msg, i_outcome);
|
||||
|
||||
// Copy reflections to outgoing message
|
||||
msg.reflections = i_outcome.reflections;
|
||||
@@ -434,7 +413,6 @@ std::optional<RotationIndexerResult> IndexAndRefine::FinalizeRotationIndexing()
|
||||
|
||||
IndexAndRefine &IndexAndRefine::ReferenceIntensities(std::vector<MergedReflection> &reference) {
|
||||
scaling_engine = std::make_unique<ScaleOnTheFly>(experiment, reference);
|
||||
pixel_reference_ = reference; // kept for the experimental PixelRefine path
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -454,71 +432,6 @@ void IndexAndRefine::ScaleImage(DataMessage &msg, IntegrationOutcome& outcome) {
|
||||
msg.image_scale_time_s = std::chrono::duration<float>(scaling_end_time - scaling_start_time).count();
|
||||
}
|
||||
|
||||
bool IndexAndRefine::PixelRefineIntegrate(DataMessage &msg,
|
||||
const CompressedImage &image,
|
||||
BraggPrediction &prediction,
|
||||
const IndexAndRefine::IndexingOutcome &outcome,
|
||||
IntegrationOutcome &i_outcome) {
|
||||
if (!outcome.lattice_candidate)
|
||||
return false;
|
||||
|
||||
// Build the engine once (lazy).
|
||||
std::call_once(pixel_refine_once_, [&] {
|
||||
pixel_refine_ = std::make_unique<PixelRefine>(experiment, pixel_reference_);
|
||||
});
|
||||
if (!pixel_refine_)
|
||||
return false;
|
||||
|
||||
PixelRefineData prd;
|
||||
prd.geom = outcome.experiment.GetDiffractionGeometry();
|
||||
prd.latt = *outcome.lattice_candidate;
|
||||
prd.centering = outcome.symmetry.centering;
|
||||
if (const auto bw = experiment.GetBandwidthFWHM())
|
||||
prd.bandwidth = bw.value() / 2.3548; // FWHM -> sigma
|
||||
|
||||
// Signal-box radius from the shared integration setting (same knob as BraggIntegrate2D).
|
||||
prd.shoebox_radius = static_cast<int>(std::lround(experiment.GetBraggIntegrationSettings().GetR1()));
|
||||
prd.r1_multiplier = experiment.GetBraggIntegrationSettings().GetProfileMultiplier();
|
||||
|
||||
std::vector<uint8_t> buffer;
|
||||
const uint8_t *ptr = image.GetUncompressedPtr(buffer);
|
||||
switch (image.GetMode()) {
|
||||
case CompressedImageMode::Int8:
|
||||
pixel_refine_->Run(reinterpret_cast<const int8_t *>(ptr), prediction, prd); break;
|
||||
case CompressedImageMode::Int16:
|
||||
pixel_refine_->Run(reinterpret_cast<const int16_t *>(ptr), prediction, prd); break;
|
||||
case CompressedImageMode::Int32:
|
||||
pixel_refine_->Run(reinterpret_cast<const int32_t *>(ptr), prediction, prd); break;
|
||||
case CompressedImageMode::Uint8:
|
||||
pixel_refine_->Run(reinterpret_cast<const uint8_t *>(ptr), prediction, prd); break;
|
||||
case CompressedImageMode::Uint16:
|
||||
pixel_refine_->Run(reinterpret_cast<const uint16_t *>(ptr), prediction, prd); break;
|
||||
case CompressedImageMode::Uint32:
|
||||
pixel_refine_->Run(reinterpret_cast<const uint32_t *>(ptr), prediction, prd); break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// PixelRefine output flows into the normal save/merge path: the refined
|
||||
// geometry/lattice and the already-scaled reflections become the outcome.
|
||||
i_outcome.reflections = std::move(prd.reflections);
|
||||
i_outcome.geom = prd.geom;
|
||||
i_outcome.latt = prd.latt;
|
||||
i_outcome.image_scale_g = static_cast<float>(prd.scale_factor);
|
||||
i_outcome.image_scale_b_factor_Ang2 = static_cast<float>(prd.B_factor);
|
||||
if (std::isfinite(prd.cc)) {
|
||||
i_outcome.image_scale_cc = static_cast<float>(prd.cc);
|
||||
i_outcome.image_scale_cc_n = prd.cc_n;
|
||||
}
|
||||
|
||||
msg.image_scale_factor = static_cast<float>(prd.scale_factor);
|
||||
msg.image_scale_cc = i_outcome.image_scale_cc;
|
||||
if (prd.B_factor != 0.0)
|
||||
msg.image_scale_b_factor = static_cast<float>(prd.B_factor);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ScalingResult IndexAndRefine::ScaleAllImages(const std::vector<MergedReflection> &reference, size_t nthreads) {
|
||||
ScaleOnTheFly scaling(experiment, reference);
|
||||
scaling.Scale(integration_outcome, nthreads);
|
||||
|
||||
Reference in New Issue
Block a user