diff --git a/rugnux/Rugnux.h b/rugnux/Rugnux.h index 514a2c0bd..3773244c0 100644 --- a/rugnux/Rugnux.h +++ b/rugnux/Rugnux.h @@ -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 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; diff --git a/rugnux/RugnuxPreScan.cpp b/rugnux/RugnuxPreScan.cpp index 95ff6ce0b..8575cdc4c 100644 --- a/rugnux/RugnuxPreScan.cpp +++ b/rugnux/RugnuxPreScan.cpp @@ -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 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 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>(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); diff --git a/rugnux/rugnux_cli.cpp b/rugnux/rugnux_cli.cpp index cfaa47511..2a1409d6d 100644 --- a/rugnux/rugnux_cli.cpp +++ b/rugnux/rugnux_cli.cpp @@ -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 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 diff --git a/viewer/JFJochProcessController.cpp b/viewer/JFJochProcessController.cpp index 4361f3753..e6e6aa98f 100644 --- a/viewer/JFJochProcessController.cpp +++ b/viewer/JFJochProcessController.cpp @@ -86,8 +86,10 @@ void JFJochProcessController::run_(QString file_path, DiffractionExperiment expe const RugnuxMeasuredGain g = RugnuxMeasureGain(reader, static_cast(std::max(1, config.nthreads))); const std::optional gain = g.peaks ? std::optional(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