Beam stop: looked for with the CCD's inactive pixels read as data
The pixels outside a CCD's active area, constant over the gain frames, are masked as dead before the run starts. A CCD reads the deep shadow of a beam stop at one value too, so that mask took the shadow's core, and the shadow finder - which reads only unmasked pixels and grows the shadow from where it is deep - then found a fraction of it or nothing. Of the 48 open-arm CCD rotation sets, about half lost most of their shadow: 9.35% -> 1.11% of the detector on PDB 3MEB, 10.79% -> 2.60% on 3R6O, 1.54% -> none on 9UPT; the CCD stills sets lost theirs too. What went unmasked was the penumbra about the core, which is not constant and was integrated as if exposed, and the shadow bit the integration's test of clipped reflections reads. On 7BGU the background centre the finder draws its rings about moved 56 px. The beam-stop step - the shadow finder and that centre - now reads the inactive pixels as the input has them (ProcessConfig::inactive_pixels, passed by the CLI and the viewer where they mask them); everything else keeps them masked as dead. On 11 CCD rotation sets the shadow is again exactly what it was before the inactive mask, the mask's gain on 7BGU stays (d_min 2.42 A, R-free 0.190), 9UPT R-free 0.225 -> 0.219, 2WNN ISa 14.5 -> 15.4, 3MEB d_min 1.65 -> 1.56 A; the CCD stills sets get their shadow back (R-free within 0.001). Photon-counting sets are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UqXYdL4qBGYRiSXSGXfCLu
This commit is contained in:
1 parent
d3d426aa8a
commit
c5fc3fa1d6
4 files changed
+24
-3
No files matched your search
@@ -202,6 +202,9 @@ struct ProcessConfig {
|
||||
// Neither the input nor the command line states the distance (an XTC): the input's value is a
|
||||
// placeholder, which the stills search does not hold against the others and nothing refines from.
|
||||
bool detector_distance_unknown = false;
|
||||
// The pixels outside the detector's active area (RugnuxMeasuredGain::constant, non-zero = inactive), which the
|
||||
// caller has masked as dead; empty where there are none. The beam-stop search reads them as data (PreScan).
|
||||
std::vector<uint32_t> inactive_pixels;
|
||||
// Whether a saturation level the frames show below the file's limit replaces it (DetectSaturation):
|
||||
// for every input but a Jungfraujoch-written file, whose stated limit is trusted.
|
||||
bool detect_saturation = false;
|
||||
|
||||
@@ -339,9 +339,22 @@ void Rugnux::PreScan(int start_image, int images_to_process, int frame_count, Ru
|
||||
const bool measure_width = want_width && !(measured && measured->spots);
|
||||
const bool measure_hot = want_hot && !(measured && measured->defects);
|
||||
|
||||
// The beam stop is looked for on the detector as the input has it: the pixels outside the active area
|
||||
// (ProcessConfig::inactive_pixels) are read as data here, and by the centre its rings are drawn about. A CCD
|
||||
// reads the deep shadow of a beam stop at one value as well; masked, the finder sees only the shadow's rim
|
||||
// or nothing, and leaves the penumbra about it unmasked. What it finds there is a shadow, for the
|
||||
// integration's test of the reflections it clips. Everything else keeps them masked as dead.
|
||||
PixelMask beam_stop_mask = pixel_mask_;
|
||||
if (!config_.inactive_pixels.empty()) {
|
||||
std::vector<uint32_t> m = pixel_mask_.GetMask();
|
||||
for (size_t i = 0; i < m.size(); i++)
|
||||
if (config_.inactive_pixels[i] != 0)
|
||||
m[i] &= ~(1u << PixelMask::ErrorPixelBit);
|
||||
beam_stop_mask = PixelMask(m);
|
||||
}
|
||||
std::optional<ShadowFinder> finder;
|
||||
if (find_shadow)
|
||||
finder.emplace(experiment_, pixel_mask_);
|
||||
finder.emplace(experiment_, beam_stop_mask);
|
||||
|
||||
// The spot width is read in the same pass as the projection rather than in a pre-pass of its own.
|
||||
// The image is preprocessed a second time for it: the shadow is accumulated from raw counts and
|
||||
@@ -1049,7 +1062,7 @@ void Rugnux::PreScan(int start_image, int images_to_process, int frame_count, Ru
|
||||
TimingMark mark(logger, "pre-scan beam stop");
|
||||
std::string centre_line;
|
||||
projection = std::make_shared<const std::vector<float>>(finder->GetMeanProjection());
|
||||
if (const auto ring_center = FindBeamCenterFromBackground(experiment_, pixel_mask_,
|
||||
if (const auto ring_center = FindBeamCenterFromBackground(experiment_, beam_stop_mask,
|
||||
*projection)) {
|
||||
finder->BeamCenter(ring_center->beam_x_pxl, ring_center->beam_y_pxl);
|
||||
ring_centre = std::make_pair(ring_center->beam_x_pxl, ring_center->beam_y_pxl);
|
||||
|
||||
@@ -2151,6 +2151,7 @@ int RugnuxInputRun::PrepareRugnuxInput() {
|
||||
logger.Error("--gain is for input in detector units; {} is a CBF of counted photons", input_file);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
std::vector<uint32_t> inactive_pixels; // masked as dead below (RugnuxMeasuredGain::constant)
|
||||
if (counts_in_adu) {
|
||||
const bool float_frames = xfel_input && xfel_reader.StoredFloat();
|
||||
const float unit = float_frames ? 1.0f / 16.0f : 1.0f;
|
||||
@@ -2181,6 +2182,7 @@ int RugnuxInputRun::PrepareRugnuxInput() {
|
||||
// Outside the active area (RugnuxMeasuredGain::constant).
|
||||
if (g.constant_pixels > 0) {
|
||||
reader.AddDeadPixelMask(g.constant);
|
||||
inactive_pixels = g.constant;
|
||||
logger.Info("Inactive pixels: {} masked, holding one value on all {} frames of the gain measurement",
|
||||
g.constant_pixels, background_gain::GAIN_MAX_FRAMES);
|
||||
}
|
||||
@@ -3627,6 +3629,7 @@ int RugnuxInputRun::PrepareRugnuxInput() {
|
||||
config.beam_center_check = beam_center_check;
|
||||
config.detector_distance_given = detector_distance_mm.has_value();
|
||||
config.detector_distance_unknown = detector_distance_unknown;
|
||||
config.inactive_pixels = std::move(inactive_pixels);
|
||||
config.detector_tilt_given = rot1_rad.has_value() || rot2_rad.has_value();
|
||||
config.calibration_refine_tilt = calibration_refine_tilt;
|
||||
config.detect_saturation = !saturation_value
|
||||
|
||||
@@ -86,8 +86,10 @@ void JFJochProcessController::run_(QString file_path, DiffractionExperiment expe
|
||||
const RugnuxMeasuredGain g = RugnuxMeasureGain(reader, static_cast<size_t>(std::max(1, config.nthreads)));
|
||||
const std::optional<float> gain = g.peaks ? std::optional<float>(g.peaks->gain) : g.variance;
|
||||
ApplyRugnuxPixelGain(experiment, config.spot_finding, gain.value_or(1.0f));
|
||||
if (g.constant_pixels > 0)
|
||||
if (g.constant_pixels > 0) {
|
||||
pixel_mask.AddDeadPixelMask(experiment, g.constant);
|
||||
config.inactive_pixels = g.constant;
|
||||
}
|
||||
}
|
||||
|
||||
// Seed the live dataset with the experiment so the chart has geometry context; per-image
|
||||
|
||||
Reference in new issue
Block a user