Files
leonarski_fandClaude Opus 5.5 8241869f09 Mask: pixels outside a CCD's active area, found as constant over the gain frames
An input in detector units whose background shows no photon peaks (a CCD, image plate
or other integrating detector read through an amplifier) has read noise on every live
pixel, so no live pixel reads the same value on all 16 frames of the gain measurement.
A pixel that does is outside the active area - the corners of a round CCD, a border or
seam the detector's corrections leave at zero - and is now masked as dead (NXmx bit 1)
before the run starts. The frames are already read for the gain, so this costs one
comparison per pixel and frame.

The existing dead-pixel test compares a pixel with its own resolution ring and cannot
see an area that covers whole rings: on a round CCD every pixel beyond the circle's
radius is zero, so its ring reads zero too. Integrated there, reflections straddling
the edge read as strong, and the high-resolution corners set a bogus resolution limit.
On PDB 7BGU (round marCCD, 22% of the frame outside the circle): d_min 1.78 -> 2.42 A
(deposited 2.43), completeness 55 -> 98%, ISa 16.9 -> 23.1.

Not applied to photon-resolving inputs (their pixels may read zero photons on every
frame) or to the XFEL formats (a file cut at a threshold reads zero on live pixels).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UqXYdL4qBGYRiSXSGXfCLu
2026-11-04 21:53:52 +01:00

70 lines
2.7 KiB
C++

// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#ifndef JFJOCHIMAGEREADER_H
#define JFJOCHIMAGEREADER_H
#include <unordered_set>
#include <map>
#include <mutex>
#include "../common/JFJochMessages.h"
#include "../common/Plot.h"
#include "../common/ROIBox.h"
#include "../common/DiffractionExperiment.h"
#include "JFJochReaderDataset.h"
#include "JFJochReaderImage.h"
#include "JFJochReaderSpots.h"
class JFJochReader {
// Reads each of its inputs' frames through them (JFJochMultiReader.h).
friend class JFJochMultiReader;
mutable std::mutex m;
std::shared_ptr<JFJochReaderDataset> dataset;
virtual bool LoadImage_i(std::shared_ptr<JFJochReaderDataset> &dataset,
DataMessage& message,
std::vector<uint8_t> &buffer,
int64_t image_number,
bool update_dataset) = 0;
mutable std::mutex summation_mutex;
void SummationThread(int64_t image0,
int64_t n_image,
int64_t image_jump,
JFJochReaderImage &image);
protected:
void SetStartMessage(const std::shared_ptr<JFJochReaderDataset> &val);
DiffractionExperiment default_experiment;
public:
virtual ~JFJochReader() = default;
JFJochReader& Experiment(const DiffractionExperiment& experiment);
std::shared_ptr<const JFJochReaderDataset> GetDataset() const;
[[nodiscard]] virtual uint64_t GetNumberOfImages() const = 0;
virtual void Close() = 0;
std::shared_ptr<JFJochReaderImage> LoadImage(int64_t image_number, int64_t summation_factor = 1);
void UpdateGeomMetadata(const DiffractionExperiment& experiment);
void UpdateUserMask(const std::vector<uint32_t>& mask);
void AddDeadPixelMask(const std::vector<uint32_t>& mask);
// Read one image into a raw image the caller owns, false where there is nothing to read. A
// worker that walks a whole sweep hands the same object back on every frame and so keeps its
// buffer: GetRawImage() allocates a fresh one per call, which on a 4-byte 16 Mpx frame is 72 MB
// of untouched pages the decoder then faults in one by one, measurably more than the decode.
virtual bool ReadRawImage(int64_t image_number, JFJochReaderRawImage &image) = 0;
// The same image in a freshly allocated raw image.
std::shared_ptr<JFJochReaderRawImage> GetRawImage(int64_t image_number);
virtual std::vector<SpotToSave> ReadSpots(int64_t image) const = 0;
std::shared_ptr<JFJochReaderSpots> ReadAllSpots(int64_t start_image, int64_t end_image, int64_t stride = 1) const;
};
#endif //JFJOCHIMAGEREADER_H