// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #pragma once #include #include "../common/JFJochMessages.h" #include "../common/DiffractionExperiment.h" #include "../common/AzimuthalIntegrationMapping.h" #include "../common/PixelMask.h" #include "../common/AzimuthalIntegrationProfile.h" #include "bragg_prediction/BraggPrediction.h" #include "bragg_integration/BraggIntegrationEngine.h" #include "spot_finding/ImageSpotFinder.h" #include "spot_finding/AdaptiveSpotFinderCPU.h" #include "indexing/IndexerThreadPool.h" #include "azint/AzIntEngine.h" #include "roi/ROIIntegration.h" #include "IndexAndRefine.h" #include "image_preprocessing/ImagePreprocessor.h" #include "image_preprocessing/ImagePreprocessorBuffer.h" class CudaStream; class AdaptiveSpotFinderGPU; // MXAnalysisWithoutFPGA is not thread safe - it has to owned by a single thread class MXAnalysisWithoutFPGA { const DiffractionExperiment &experiment; const AzimuthalIntegrationMapping &integration; std::vector decompression_buffer; std::unique_ptr preprocessor; size_t npixels; size_t xpixels; std::unique_ptr azint; std::unique_ptr roi; std::unique_ptr spotFinder; // Self-calibrating finder, used when spot settings request adaptive detection. Kept alongside the // default finder because the choice arrives with the per-image settings, not at construction. It is // an AdaptiveSpotFinderCPU by default; on the GPU path, when the fused engine is enabled (rugnux // offline only), it is instead an AdaptiveSpotFinderGPU that also computes the azimuthal profile, // aliased through fused_adaptive so Analyze() can take that profile and skip the separate azint pass. std::unique_ptr adaptiveSpotFinder; AdaptiveSpotFinderGPU *fused_adaptive = nullptr; const bool enable_fused_adaptive_gpu; IndexAndRefine &indexer; std::unique_ptr prediction; std::unique_ptr bragg_engine; std::unique_ptr preprocessor_buffer; const PixelMask &mask; // Decompress the image into decompression_buffer (or read it straight from the message, when it is // not compressed) and return where it landed. const uint8_t *Decompress(const CompressedImage &image); std::vector mask_resolution; // The limits mask_resolution was built for. Kept as the OPTIONAL the caller passed, so an unset // high-resolution limit compares equal to itself and the mask is not rebuilt on every image. std::optional mask_high_res; float mask_low_res; void UpdateMaskResolution(const SpotFindingSettings& settings); #ifdef JFJOCH_USE_CUDA std::shared_ptr stream; // kept so RebuildROI() can recreate the GPU ROI engine #endif public: // enable_fused_adaptive_gpu turns on the fused GPU azint+adaptive spot finder (only takes effect on // the GPU path with adaptive detection). The rugnux offline path and the interactive viewer enable // it by default, as does the online receiver. It only changes performance - the fused engine // reproduces the CPU finder's spots. Note it also decides whether the preprocessed image is copied // back to the host each frame: that copy exists only for a CPU engine to read, and with the flag on // no CPU engine is built, so the copy is skipped. MXAnalysisWithoutFPGA(const DiffractionExperiment &experiment, const AzimuthalIntegrationMapping &integration, const PixelMask &mask, IndexAndRefine &indexer, bool enable_fused_adaptive_gpu = false); void Analyze(DataMessage &output, AzimuthalIntegrationProfile &profile, const SpotFindingSettings &spot_finding_settings); // Surgical ROI-only paths used when a full re-analysis is not wanted: rebuild the // ROI engine after the ROI set changes, recompute ROIs after preprocessing a new // image (reanalyze off), or just rerun ROIs on the current preprocessed image (an // interactive ROI move). A full Analyze() already computes ROIs, so needs nothing. void RebuildROI(); void AnalyzeROIOnly(DataMessage &output); void RunROIOnly(DataMessage &output); };