// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #pragma once #include #include #include #include "JFJochReader.h" #include "CrystFELGeometry.h" class HDF5ReadOnlyFile; // Reads serial (stills) frames from HDF5 files laid out the way CrystFEL reads them, placed on the // detector by a CrystFEL geometry file: Cheetah CXI files with the frames stacked along the first // dimension, files holding one frame each, or files with one group per shot (data = /%/data). The // panels are put on one flat image at their geometry-file positions, rounded to whole pixels, with // everything between them and every bad region masked. Pixel values are converted to photons with the // geometry's gain where it states one, and are otherwise left in detector units. // // One wavelength and one camera length hold for the whole run: where the geometry takes either from // the image files, it is the mean over the first file's frames. // // A path is one file, or a directory, every .h5/.cxi file of which is read in name order. class JFJochCrystFELReader : public JFJochReader { struct Frame { size_t file; std::string dataset; int64_t index; // along the first dimension of a 3D dataset; -1 for a 2D one std::string group; // the shot group the geometry's % stands for, if any }; crystfel::Geometry geom_; std::vector files_; std::vector> open_files_; std::vector frames_; std::shared_ptr dataset_; size_t frame_width_ = 0, frame_height_ = 0; // the stored frame size_t width_ = 0, height_ = 0; // the flat image // For every stored pixel, where it lands on the flat image (-1: nowhere) and what one photon reads. std::vector destination_; std::vector adu_per_photon_; bool counts_in_photons_ = false; // Where every frame is stored as one deflated (and optionally byte-shuffled) chunk of a plain // number type - the Cheetah outputs - the chunk is read raw under the HDF5 lock and decoded // outside it, so decoding runs on every reading thread instead of one at a time. bool direct_chunks_ = false; bool shuffled_ = false; bool stored_float_ = false, stored_signed_ = false; size_t stored_size_ = 0; void CheckDirectChunks(); double MeanOverFirstFile(const std::string &path); bool LoadImage_i(std::shared_ptr &dataset, DataMessage &message, std::vector &buffer, int64_t image_number, bool update_dataset) override; template void DecodeInto(int64_t image_number, Buffer &buffer) const; public: JFJochCrystFELReader(); ~JFJochCrystFELReader() override; void ReadFiles(const std::string &path, const std::string &geometry_file); // False where the geometry states no gain, so the images are in detector units, not photons. [[nodiscard]] bool CountsInPhotons() const; [[nodiscard]] uint64_t GetNumberOfImages() const override; void Close() override; bool ReadRawImage(int64_t image_number, JFJochReaderRawImage &image) override; [[nodiscard]] std::vector ReadSpots(int64_t image) const override; };