PixelRefine: Some improvements
Build Packages / Generate python client (push) Successful in 23s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (rocky9) (push) Failing after 4m58s
Build Packages / Build documentation (push) Successful in 1m25s
Build Packages / XDS test (neggia plugin) (push) Successful in 13m57s
Build Packages / XDS test (durin plugin) (push) Successful in 15m44s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 16m9s
Build Packages / DIALS test (push) Successful in 22m45s
Build Packages / Unit tests (push) Successful in 1h44m30s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Failing after 3m0s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Failing after 3m4s
Build Packages / build:rpm (rocky8_nocuda) (push) Failing after 3m12s
Build Packages / build:rpm (rocky9_nocuda) (push) Failing after 3m58s
Build Packages / build:rpm (rocky8_sls9) (push) Failing after 4m20s
Build Packages / build:rpm (rocky8) (push) Failing after 4m18s
Build Packages / build:rpm (rocky9_sls9) (push) Failing after 4m55s
Build Packages / build:rpm (ubuntu2204) (push) Failing after 4m20s
Build Packages / build:rpm (ubuntu2404) (push) Failing after 4m17s
Build Packages / Generate python client (push) Successful in 23s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (rocky9) (push) Failing after 4m58s
Build Packages / Build documentation (push) Successful in 1m25s
Build Packages / XDS test (neggia plugin) (push) Successful in 13m57s
Build Packages / XDS test (durin plugin) (push) Successful in 15m44s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 16m9s
Build Packages / DIALS test (push) Successful in 22m45s
Build Packages / Unit tests (push) Successful in 1h44m30s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Failing after 3m0s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Failing after 3m4s
Build Packages / build:rpm (rocky8_nocuda) (push) Failing after 3m12s
Build Packages / build:rpm (rocky9_nocuda) (push) Failing after 3m58s
Build Packages / build:rpm (rocky8_sls9) (push) Failing after 4m20s
Build Packages / build:rpm (rocky8) (push) Failing after 4m18s
Build Packages / build:rpm (rocky9_sls9) (push) Failing after 4m55s
Build Packages / build:rpm (ubuntu2204) (push) Failing after 4m20s
Build Packages / build:rpm (ubuntu2404) (push) Failing after 4m17s
This commit is contained in:
+2
-1
@@ -25,7 +25,8 @@ struct Reflection {
|
||||
float sigma;
|
||||
float dist_ewald;
|
||||
float rlp;
|
||||
float partiality;
|
||||
float partiality; // fraction of the reflection recorded in the sampled (rocking) slice
|
||||
float completeness = 1.0f; // fraction of the spot footprint on live pixels (1 = not clipped by edge/gap/mask)
|
||||
float zeta;
|
||||
float image_scale_corr; // I_true = scaling_correction * I; scaling_correction = rlp / (partiality * image_scale)
|
||||
bool observed = false;
|
||||
|
||||
@@ -33,6 +33,7 @@ struct ReflGroup {
|
||||
double Itrue; // reference intensity (held fixed)
|
||||
double R_bw_sq; // bandwidth radial-width^2 contribution (0 = monochromatic)
|
||||
double pol; // per-reflection polarization correction (raw = true * pol)
|
||||
double Ibkg; // local flat background (raw counts, constant over the shoebox)
|
||||
double predicted_x, predicted_y;
|
||||
std::vector<PixelObs> pixels;
|
||||
};
|
||||
@@ -83,6 +84,23 @@ std::vector<uint8_t> BuildSpotMask(const std::vector<Reflection> &predicted, int
|
||||
return mask;
|
||||
}
|
||||
|
||||
// Square shoebox bounds (inclusive) around a predicted spot, clamped to the
|
||||
// detector. The centre is rounded to the nearest pixel with std::lround so the
|
||||
// signal box is centred identically to the spot-core mask (BuildSpotMask) and
|
||||
// the local-background ring (EstimateLocalBackground), which also lround. Used by
|
||||
// Run and the diagnostic renderers so all three share one shoebox definition.
|
||||
struct ShoeboxBox { int min_x, max_x, min_y, max_y; };
|
||||
ShoeboxBox ShoeboxBounds(double px, double py, int radius, size_t xpixel, size_t ypixel) {
|
||||
const int cx = static_cast<int>(std::lround(px));
|
||||
const int cy = static_cast<int>(std::lround(py));
|
||||
return {
|
||||
std::max(cx - radius, 0),
|
||||
std::min<int>(cx + radius, static_cast<int>(xpixel) - 1),
|
||||
std::max(cy - radius, 0),
|
||||
std::min<int>(cy + radius, static_cast<int>(ypixel) - 1)
|
||||
};
|
||||
}
|
||||
|
||||
// Local flat background around one shoebox, in raw detector counts. Samples the
|
||||
// square ring shoebox_radius < max(|dx|,|dy|) <= bkg_outer_radius centred on the
|
||||
// spot, dropping pixels that belong to any spot core (spot_mask) or carry a
|
||||
@@ -633,16 +651,14 @@ void PixelRefine::Run(const T *image,
|
||||
g.Itrue = reference_data[hkl];
|
||||
g.R_bw_sq = bandwidth_radial_sq(refl.d);
|
||||
g.pol = polarization(refl.predicted_x, refl.predicted_y);
|
||||
g.Ibkg = Ibkg;
|
||||
g.predicted_x = refl.predicted_x;
|
||||
g.predicted_y = refl.predicted_y;
|
||||
|
||||
const int min_y = std::max<int>(refl.predicted_y - radius, 0);
|
||||
const int max_y = std::min<int>(refl.predicted_y + radius, ypixel - 1);
|
||||
const int min_x = std::max<int>(refl.predicted_x - radius, 0);
|
||||
const int max_x = std::min<int>(refl.predicted_x + radius, xpixel - 1);
|
||||
const auto box = ShoeboxBounds(refl.predicted_x, refl.predicted_y, radius, xpixel, ypixel);
|
||||
|
||||
for (int y = min_y; y <= max_y; ++y) {
|
||||
for (int x = min_x; x <= max_x; ++x) {
|
||||
for (int y = box.min_y; y <= box.max_y; ++y) {
|
||||
for (int x = box.min_x; x <= box.max_x; ++x) {
|
||||
const size_t npixel = xpixel * y + x;
|
||||
|
||||
// Skip sentinel (masked / saturated) pixels. We assume the pixel
|
||||
@@ -826,52 +842,100 @@ void PixelRefine::Run(const T *image,
|
||||
} // predict<->refine iterations
|
||||
|
||||
// ---- Extract integrated reflections ---------------------------------------
|
||||
// Profile fitting gives the recorded amplitude (against the tangential profile
|
||||
// P_t):
|
||||
// J = sum_p[ P_t,p (Iobs_p - Ibkg_p)/v_p ] / sum_p[ P_t,p^2 / v_p ]
|
||||
// Profile fitting gives the recorded amplitude (fitting the tangential profile
|
||||
// P_t against the background-subtracted pixels):
|
||||
// J = sum_p[ P_t,p (Iobs_p - Ibkg)/v_p ] / sum_p[ P_t,p^2 / v_p ]
|
||||
// ~ G * Itrue * B_term * partiality * pol (recorded raw counts)
|
||||
// var(J) = 1 / sum_p[ P_t,p^2 / v_p ]
|
||||
//
|
||||
// Two SEPARATE fractions reduce the full intensity to what these pixels record:
|
||||
//
|
||||
// partiality - the radial / rocking dimension that a still does NOT sample.
|
||||
// Only the slice of the reflection that crosses the Ewald
|
||||
// sphere on this shot is recorded; <= 1. We DIVIDE it out to
|
||||
// recover the full intensity. = profile-weighted P_radial.
|
||||
//
|
||||
// completeness - the fraction of the spot's detector footprint that landed on
|
||||
// live pixels (= profile captured by live pixels / profile over
|
||||
// the whole shoebox). 1.0 when the spot sits fully on the
|
||||
// detector; < 1.0 only when a detector edge, gap or mask clips
|
||||
// it. Profile fitting already extrapolates over the missing
|
||||
// pixels, so this is NOT applied to r.I - it is a quality flag.
|
||||
//
|
||||
// Output split (Merge multiplies r.I * image_scale_corr and weights by
|
||||
// 1/(sigma*image_scale_corr)^2 - see Merge.cpp):
|
||||
// r.I = J / (B_term * partiality * pol) = G * Itrue
|
||||
// r.sigma = sqrt(var(J)) / (B_term * partiality * pol)
|
||||
// r.partiality = profile-weighted peak radial factor in (0,1] (Merge filter only)
|
||||
// r.partiality = profile-weighted P_radial in (0,1] (the rocking fraction)
|
||||
// r.completeness = live/total tangential profile in (0,1] (detector clipping)
|
||||
// r.image_scale_corr = 1/G (per-image scale ONLY)
|
||||
// so r.I * image_scale_corr = Itrue. B, partiality and polarization live on the
|
||||
// intensity, G lives on image_scale_corr - one clean meaning per field.
|
||||
//
|
||||
// We walk the full (unclamped) shoebox once: every grid point feeds the total
|
||||
// tangential profile (completeness denominator); points that are real, live
|
||||
// detector pixels also feed the profile fit and the captured profile.
|
||||
data.reflections.reserve(groups.size());
|
||||
for (const auto &g : groups) {
|
||||
double num = 0.0, den = 0.0, bkg_sum = 0.0;
|
||||
double radial_sum = 0.0, radial_w = 0.0;
|
||||
const int cx = static_cast<int>(std::lround(g.predicted_x));
|
||||
const int cy = static_cast<int>(std::lround(g.predicted_y));
|
||||
|
||||
// Debye-Waller factor for this reflection (constant over its shoebox).
|
||||
const double B_term = std::exp(-data.B_factor / (4.0 * g.d * g.d));
|
||||
|
||||
double num = 0.0, den = 0.0, bkg_sum = 0.0, radial_sum = 0.0;
|
||||
double prof_live = 0.0, prof_full = 0.0; // tangential profile: captured / total
|
||||
size_t n = 0;
|
||||
|
||||
for (const auto &obs : g.pixels) {
|
||||
PixelResidual pr(obs, 1.0, lambda, pixel_size, g.h, g.k, g.l, g.R_bw_sq, g.pol, data.crystal_system);
|
||||
double q_sq, eps_r, eps_t_sq;
|
||||
if (!pr.GeometryTerms(beam, &dist_mm, detector_rot, latt_vec0, latt_vec1, latt_vec2, q_sq,
|
||||
eps_r, eps_t_sq))
|
||||
continue;
|
||||
if (!(data.R[0] > 0.0) || !(data.R[1] > 0.0))
|
||||
continue;
|
||||
for (int y = cy - radius; y <= cy + radius; ++y) {
|
||||
for (int x = cx - radius; x <= cx + radius; ++x) {
|
||||
// Geometry/profile for this grid point (valid even off the detector).
|
||||
PixelObs probe{static_cast<double>(x), static_cast<double>(y), 0.0, g.Ibkg, 1.0};
|
||||
PixelResidual pr(probe, 1.0, lambda, pixel_size, g.h, g.k, g.l,
|
||||
g.R_bw_sq, g.pol, data.crystal_system);
|
||||
double q_sq, eps_r, eps_t_sq;
|
||||
if (!pr.GeometryTerms(beam, &dist_mm, detector_rot,
|
||||
latt_vec0, latt_vec1, latt_vec2, q_sq, eps_r, eps_t_sq))
|
||||
continue;
|
||||
if (!(data.R[0] > 0.0) || !(data.R[1] > 0.0))
|
||||
continue;
|
||||
|
||||
// Tangential profile shape -> fit weight (every pixel counts equally).
|
||||
const double P_t = std::exp(-eps_t_sq / (data.R[1] * data.R[1]))
|
||||
/ (M_PI * data.R[1] * data.R[1]);
|
||||
// Peak-normalized radial factor (the partiality), in (0,1].
|
||||
// Bandwidth-broadened radial width, matching the model in Model().
|
||||
const double R0_eff_sq = data.R[0] * data.R[0] + g.R_bw_sq;
|
||||
const double P_radial = std::exp(-eps_r * eps_r / R0_eff_sq);
|
||||
// Tangential profile shape (area-normalized) -> the fit template.
|
||||
const double P_t = std::exp(-eps_t_sq / (data.R[1] * data.R[1]))
|
||||
/ (M_PI * data.R[1] * data.R[1]);
|
||||
prof_full += P_t; // whole shoebox, on- or off-detector
|
||||
|
||||
const double v = SafeInv(obs.weight * obs.weight, 1.0); // pixel variance
|
||||
const double signal = obs.Iobs - obs.Ibkg;
|
||||
// Only real, unmasked detector pixels carry signal.
|
||||
if (x < 0 || x >= static_cast<int>(xpixel) || y < 0 || y >= static_cast<int>(ypixel))
|
||||
continue;
|
||||
const size_t np = static_cast<size_t>(xpixel) * y + x;
|
||||
if (image[np] == std::numeric_limits<T>::max())
|
||||
continue;
|
||||
if (std::is_signed_v<T> && image[np] == std::numeric_limits<T>::min())
|
||||
continue;
|
||||
|
||||
num += P_t * signal / v;
|
||||
den += P_t * P_t / v;
|
||||
radial_sum += P_radial * P_t; // weight partiality by the spot core
|
||||
radial_w += P_t;
|
||||
bkg_sum += obs.Ibkg;
|
||||
++n;
|
||||
const double Iobs = static_cast<double>(image[np]); // raw counts
|
||||
double v = std::max(Iobs, 0.0); // Poisson variance
|
||||
if (!(v > 1.0))
|
||||
v = 1.0;
|
||||
|
||||
// Peak-normalized radial factor (the partiality), in (0,1]. The
|
||||
// bandwidth-broadened radial width matches the model in Model().
|
||||
const double R0_eff_sq = data.R[0] * data.R[0] + g.R_bw_sq;
|
||||
const double P_radial = std::exp(-eps_r * eps_r / R0_eff_sq);
|
||||
|
||||
// Profile-fit accumulators. The amplitude estimator weights pixels by
|
||||
// P_t^2/v, so the partiality (which de-scales that amplitude) MUST use
|
||||
// the SAME weights - otherwise an R0_eff-dependent (resolution-
|
||||
// dependent) factor is left behind in r.I.
|
||||
const double w = P_t * P_t / v;
|
||||
num += P_t * (Iobs - g.Ibkg) / v;
|
||||
den += w;
|
||||
radial_sum += P_radial * w; // partiality weighted exactly like num/den
|
||||
prof_live += P_t; // captured tangential profile
|
||||
bkg_sum += g.Ibkg;
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
Reflection r{};
|
||||
@@ -884,12 +948,12 @@ void PixelRefine::Run(const T *image,
|
||||
r.observed_x = NAN;
|
||||
r.observed_y = NAN;
|
||||
r.rlp = 1.0f;
|
||||
r.partiality = (radial_w > 0.0) ? static_cast<float>(radial_sum / radial_w) : 1.0f;
|
||||
r.partiality = (den > 0.0) ? static_cast<float>(radial_sum / den) : 1.0f;
|
||||
r.completeness = (prof_full > 0.0) ? static_cast<float>(prof_live / prof_full) : 1.0f;
|
||||
|
||||
if (den > 0.0 && n > 0) {
|
||||
const double I_amp = num / den; // ~ G*Itrue*B_term*partiality*pol
|
||||
const double sigma_amp = std::sqrt(1.0 / den);
|
||||
const double B_term = std::exp(-data.B_factor / (4.0 * g.d * g.d));
|
||||
const double corr = static_cast<double>(r.partiality) * B_term * g.pol; // B, partiality & pol
|
||||
r.bkg = static_cast<float>(bkg_sum / static_cast<double>(n));
|
||||
r.observed = true;
|
||||
@@ -1010,13 +1074,10 @@ std::vector<float> PixelRefine::PredictImage(const T *image,
|
||||
refl.predicted_x, refl.predicted_y,
|
||||
radius, bkg_outer_radius, Ibkg);
|
||||
|
||||
const int min_y = std::max<int>(refl.predicted_y - radius, 0);
|
||||
const int max_y = std::min<int>(refl.predicted_y + radius, ypixel - 1);
|
||||
const int min_x = std::max<int>(refl.predicted_x - radius, 0);
|
||||
const int max_x = std::min<int>(refl.predicted_x + radius, xpixel - 1);
|
||||
const auto box = ShoeboxBounds(refl.predicted_x, refl.predicted_y, radius, xpixel, ypixel);
|
||||
|
||||
for (int y = min_y; y <= max_y; ++y) {
|
||||
for (int x = min_x; x <= max_x; ++x) {
|
||||
for (int y = box.min_y; y <= box.max_y; ++y) {
|
||||
for (int x = box.min_x; x <= box.max_x; ++x) {
|
||||
const size_t npixel = xpixel * y + x;
|
||||
|
||||
PixelObs obs{
|
||||
@@ -1106,13 +1167,10 @@ std::vector<float> PixelRefine::ChiSquaredImage(const T *image,
|
||||
radius, bkg_outer_radius, Ibkg))
|
||||
continue;
|
||||
|
||||
const int min_y = std::max<int>(refl.predicted_y - radius, 0);
|
||||
const int max_y = std::min<int>(refl.predicted_y + radius, ypixel - 1);
|
||||
const int min_x = std::max<int>(refl.predicted_x - radius, 0);
|
||||
const int max_x = std::min<int>(refl.predicted_x + radius, xpixel - 1);
|
||||
const auto box = ShoeboxBounds(refl.predicted_x, refl.predicted_y, radius, xpixel, ypixel);
|
||||
|
||||
for (int y = min_y; y <= max_y; ++y) {
|
||||
for (int x = min_x; x <= max_x; ++x) {
|
||||
for (int y = box.min_y; y <= box.max_y; ++y) {
|
||||
for (int x = box.min_x; x <= box.max_x; ++x) {
|
||||
const size_t npixel = xpixel * y + x;
|
||||
|
||||
// Same gating as Run(): only pixels that actually enter the fit.
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "../bragg_prediction/BraggPrediction.h"
|
||||
#include "../common/DiffractionExperiment.h"
|
||||
#include "../scale_merge/HKLKey.h"
|
||||
@@ -198,4 +200,14 @@ public:
|
||||
std::vector<float> ChiSquaredImage(const T *image,
|
||||
BraggPrediction &prediction,
|
||||
const PixelRefineData &data) const;
|
||||
|
||||
// Reference (merged) intensity used as the fixed hypothesis for a reflection,
|
||||
// or nullopt if this hkl is not in the reference. Lets callers show the fitted
|
||||
// estimate next to the reference it was scaled against.
|
||||
std::optional<double> ReferenceIntensity(const Reflection &r) const {
|
||||
const auto it = reference_data.find(hkl_key_generator(r));
|
||||
if (it == reference_data.end())
|
||||
return std::nullopt;
|
||||
return it->second;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -88,6 +88,8 @@ ADD_EXECUTABLE(jfjoch_viewer jfjoch_viewer.cpp JFJochViewerWindow.cpp JFJochView
|
||||
windows/JFJochViewerReciprocalSpaceWindow.h
|
||||
windows/JFJochPixelRefineWindow.cpp
|
||||
windows/JFJochPixelRefineWindow.h
|
||||
windows/JFJochPixelRefineTableWindow.cpp
|
||||
windows/JFJochPixelRefineTableWindow.h
|
||||
windows/JFJochMagnifierWindow.cpp
|
||||
windows/JFJochMagnifierWindow.h
|
||||
windows/PixelRefineParams.h
|
||||
|
||||
@@ -70,6 +70,7 @@ JFJochImageReadingWorker::JFJochImageReadingWorker(const SpotFindingSettings &se
|
||||
indexing_settings(experiment.GetIndexingSettings()),
|
||||
azint_settings(experiment.GetAzimuthalIntegrationSettings()) {
|
||||
qRegisterMetaType<PixelRefineParams>("PixelRefineParams");
|
||||
qRegisterMetaType<PixelRefineReport>("PixelRefineReport");
|
||||
qRegisterMetaType<QVector<QRect>>("QVector<QRect>");
|
||||
spot_finding_settings = settings;;
|
||||
|
||||
@@ -852,6 +853,41 @@ QVector<QRect> JFJochImageReadingWorker::BuildShoeboxes_i(const PixelRefineData
|
||||
return boxes;
|
||||
}
|
||||
|
||||
PixelRefineReport JFJochImageReadingWorker::BuildReport_i(const PixelRefineData &data) const {
|
||||
PixelRefineReport report;
|
||||
report.pr_G = data.scale_factor;
|
||||
report.pr_B = data.B_factor;
|
||||
report.pr_cc = data.cc;
|
||||
report.pr_cc_n = data.cc_n;
|
||||
|
||||
// Standard ScaleOnTheFly pipeline result for the same image, as a baseline.
|
||||
if (current_image_ptr) {
|
||||
const auto &d = current_image_ptr->ImageData();
|
||||
if (d.image_scale_factor) report.pipe_G = d.image_scale_factor.value();
|
||||
if (d.image_scale_b_factor) report.pipe_B = d.image_scale_b_factor.value();
|
||||
if (d.image_scale_cc) report.pipe_cc = d.image_scale_cc.value();
|
||||
}
|
||||
|
||||
report.rows.reserve(data.reflections.size());
|
||||
for (const auto &r : data.reflections) {
|
||||
if (!r.observed)
|
||||
continue;
|
||||
PixelRefineReport::Row row;
|
||||
row.h = r.h; row.k = r.k; row.l = r.l;
|
||||
row.d = r.d;
|
||||
row.completeness = r.completeness;
|
||||
row.partiality = r.partiality;
|
||||
row.I = r.I;
|
||||
row.sigma = r.sigma;
|
||||
if (std::isfinite(r.image_scale_corr))
|
||||
row.I_true_est = static_cast<double>(r.I) * static_cast<double>(r.image_scale_corr);
|
||||
if (pixel_refine_)
|
||||
row.I_true_ref = pixel_refine_->ReferenceIntensity(r).value_or(NAN);
|
||||
report.rows.push_back(row);
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
std::vector<float> JFJochImageReadingWorker::BuildDisplayImage_i(const PixelRefineData &data,
|
||||
int display_mode) const {
|
||||
const auto &img32 = current_image_ptr->Image();
|
||||
@@ -909,6 +945,7 @@ void JFJochImageReadingWorker::PixelRefinePreview(PixelRefineParams params) {
|
||||
const auto &img32 = current_image_ptr->Image();
|
||||
pixel_refine_->Run<int32_t>(img32.data(), *pixel_pred_, d);
|
||||
emit pixelRefineResidual(d.final_cost, d.cc, static_cast<int64_t>(d.reflections.size()));
|
||||
emit pixelRefineReport(BuildReport_i(d));
|
||||
|
||||
auto display = BuildDisplayImage_i(d, params.display_mode);
|
||||
emit predictedImageReady(WrapFloatImage_i(display));
|
||||
@@ -954,6 +991,7 @@ void JFJochImageReadingWorker::PixelRefineRun(PixelRefineParams params) {
|
||||
out.beam_y = d.geom.GetBeamY_pxl();
|
||||
emit pixelRefineParamsRefined(out);
|
||||
emit pixelRefineResidual(d.final_cost, d.cc, static_cast<int64_t>(d.reflections.size()));
|
||||
emit pixelRefineReport(BuildReport_i(d));
|
||||
|
||||
auto display = BuildDisplayImage_i(d, params.display_mode);
|
||||
emit predictedImageReady(WrapFloatImage_i(display));
|
||||
|
||||
@@ -80,6 +80,8 @@ private:
|
||||
void MaskMeasuredSentinels_i(std::vector<float> &img) const;
|
||||
// Build the per-reflection shoebox rectangles for the last refine/preview.
|
||||
QVector<QRect> BuildShoeboxes_i(const PixelRefineData &data) const;
|
||||
// Assemble the per-reflection table + per-image summary for the table window.
|
||||
PixelRefineReport BuildReport_i(const PixelRefineData &data) const;
|
||||
// Build the float image to display for the given PixelRefineParams::DisplayMode.
|
||||
std::vector<float> BuildDisplayImage_i(const PixelRefineData &data, int display_mode) const;
|
||||
|
||||
@@ -148,6 +150,7 @@ signals:
|
||||
void predictedShoeboxes(QVector<QRect> boxes); // per-reflection optimization windows
|
||||
void pixelRefineResidual(double cost, double cc, int64_t n_reflections);
|
||||
void pixelRefineParamsRefined(PixelRefineParams params);
|
||||
void pixelRefineReport(PixelRefineReport report); // per-reflection table + summary
|
||||
void pixelRefineStatus(QString message);
|
||||
|
||||
public:
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "windows/JFJoch2DAzintImageWindow.h"
|
||||
#include "windows/JFJochAzIntWindow.h"
|
||||
#include "windows/JFJochPixelRefineWindow.h"
|
||||
#include "windows/JFJochPixelRefineTableWindow.h"
|
||||
#include "windows/JFJochMagnifierWindow.h"
|
||||
#include "image_viewer/JFJochImage.h"
|
||||
#include "image_viewer/JFJochSimpleImage.h"
|
||||
@@ -108,6 +109,7 @@ JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString
|
||||
auto azintWindow = new JFJochAzIntWindow(experiment.GetAzimuthalIntegrationSettings(), this);
|
||||
auto azintImageWindow = new JFJoch2DAzintImageWindow(this);
|
||||
auto pixelRefineWindow = new JFJochPixelRefineWindow(this);
|
||||
auto pixelRefineTableWindow = new JFJochPixelRefineTableWindow(this);
|
||||
auto magnifierWindow = new JFJochMagnifierWindow(this);
|
||||
|
||||
menuBar->AddWindowEntry(tableWindow, "Image list");
|
||||
@@ -120,6 +122,7 @@ JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString
|
||||
menuBar->AddWindowEntry(azintWindow, "Azimuthal integration settings");
|
||||
menuBar->AddWindowEntry(azintImageWindow, "Azimuthal integration 2D image");
|
||||
menuBar->AddWindowEntry(pixelRefineWindow, "PixelRefine (experimental)");
|
||||
menuBar->AddWindowEntry(pixelRefineTableWindow, "PixelRefine reflections");
|
||||
menuBar->AddWindowEntry(magnifierWindow, "Magnifier");
|
||||
|
||||
if (dbus) {
|
||||
@@ -361,6 +364,12 @@ JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString
|
||||
connect(reading_worker, &JFJochImageReadingWorker::pixelRefineStatus,
|
||||
pixelRefineWindow, &JFJochPixelRefineWindow::setStatus);
|
||||
|
||||
// Reflection-table window: refreshed on every preview/refine, raised by button.
|
||||
connect(reading_worker, &JFJochImageReadingWorker::pixelRefineReport,
|
||||
pixelRefineTableWindow, &JFJochPixelRefineTableWindow::setReport);
|
||||
connect(pixelRefineWindow, &JFJochPixelRefineWindow::showTableRequested,
|
||||
pixelRefineTableWindow, &JFJochHelperWindow::open);
|
||||
|
||||
// Lock the predicted-image viewport to the original image (both directions).
|
||||
connect(viewer, &JFJochImage::viewportChanged,
|
||||
pixelRefineWindow->imageView(), &JFJochImage::applyViewport);
|
||||
|
||||
@@ -82,8 +82,10 @@ JFJochPixelRefineWindow::JFJochPixelRefineWindow(QWidget *parent)
|
||||
// --- buttons + readouts -------------------------------------------------
|
||||
m_loadRef = new QPushButton(tr("Load reference MTZ…"), this);
|
||||
m_refine = new QPushButton(tr("Refine"), this);
|
||||
m_showTable = new QPushButton(tr("Reflection table…"), this);
|
||||
controlsLayout->addWidget(m_loadRef);
|
||||
controlsLayout->addWidget(m_refine);
|
||||
controlsLayout->addWidget(m_showTable);
|
||||
|
||||
m_residual = new QLabel(tr("Residual: —"), this);
|
||||
m_pipelineCC = new QLabel(tr("Pipeline CC (ref): —"), this);
|
||||
@@ -121,6 +123,10 @@ JFJochPixelRefineWindow::JFJochPixelRefineWindow(QWidget *parent)
|
||||
emit loadReferenceRequested(path);
|
||||
});
|
||||
|
||||
connect(m_showTable, &QPushButton::clicked, this, [this] {
|
||||
emit showTableRequested();
|
||||
});
|
||||
|
||||
connect(m_refine, &QPushButton::clicked, this, [this] {
|
||||
// Cancel any pending live-preview: otherwise a debounce armed by a slider
|
||||
// move just before this click fires after the refine and overwrites the
|
||||
|
||||
@@ -49,6 +49,7 @@ class JFJochPixelRefineWindow : public JFJochHelperWindow {
|
||||
QLabel *m_status;
|
||||
QPushButton *m_loadRef;
|
||||
QPushButton *m_refine;
|
||||
QPushButton *m_showTable;
|
||||
|
||||
QTimer *m_debounce;
|
||||
bool m_suppress = false; // guard while pushing refined params into sliders
|
||||
@@ -68,6 +69,7 @@ signals:
|
||||
void paramsChanged(PixelRefineParams params); // debounced live preview
|
||||
void refineRequested(PixelRefineParams params); // "Refine" button
|
||||
void loadReferenceRequested(QString path); // "Load reference" button
|
||||
void showTableRequested(); // "Reflection table" button
|
||||
|
||||
public slots:
|
||||
void setPredictedImage(std::shared_ptr<const SimpleImage> image);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <QMetaType>
|
||||
|
||||
// Parameters exchanged between the PixelRefine window (sliders/buttons) and the
|
||||
@@ -39,3 +41,26 @@ struct PixelRefineParams {
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(PixelRefineParams)
|
||||
|
||||
// One PixelRefine result, shipped from the worker to the reflection-table window:
|
||||
// a per-image summary that puts PixelRefine's scale/B/CC next to the standard
|
||||
// ScaleOnTheFly pipeline's, plus one row per matched reflection.
|
||||
struct PixelRefineReport {
|
||||
// Per-image summary (NaN = not available).
|
||||
double pr_G = NAN, pr_B = NAN, pr_cc = NAN; // PixelRefine
|
||||
int64_t pr_cc_n = 0;
|
||||
double pipe_G = NAN, pipe_B = NAN, pipe_cc = NAN; // ScaleOnTheFly pipeline baseline
|
||||
|
||||
struct Row {
|
||||
int h = 0, k = 0, l = 0;
|
||||
double d = 0.0;
|
||||
double completeness = 1.0; // spot footprint on live pixels (1 = not clipped)
|
||||
double partiality = 1.0; // recorded rocking fraction
|
||||
double I = 0.0, sigma = 0.0;
|
||||
double I_true_est = NAN; // r.I * image_scale_corr (this image's estimate)
|
||||
double I_true_ref = NAN; // reference (merged) intensity
|
||||
};
|
||||
std::vector<Row> rows;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(PixelRefineReport)
|
||||
|
||||
Reference in New Issue
Block a user