// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "ImagePreprocessorCPU.h" ImagePreprocessorCPU::ImagePreprocessorCPU(const DiffractionExperiment &experiment, const PixelMask &mask) : ImagePreprocessor(experiment), mask_1bit(npixels, false) { for (int i = 0; i < npixels; i++) mask_1bit[i] = (mask.GetMask().at(i) != 0); } ImageStatistics ImagePreprocessorCPU::Analyze(ImagePreprocessorBuffer &processed_image, const uint8_t *image_ptr, CompressedImageMode image_mode) { if (processed_image.size() != npixels) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Processed image size mismatch"); ImageStatistics ret{}; AnalyzeBlock(processed_image, 0, image_ptr, npixels, image_mode, ret); return ret; } void ImagePreprocessorCPU::AnalyzeBlock(ImagePreprocessorBuffer &processed_image, size_t first, const uint8_t *input, size_t n, CompressedImageMode image_mode, ImageStatistics &stats) { switch (image_mode) { case CompressedImageMode::Int8: return AnalyzeBlock(processed_image, first, input, n, INT8_MIN, INT8_MAX, stats); case CompressedImageMode::Int16: return AnalyzeBlock(processed_image, first, input, n, INT16_MIN, INT16_MAX, stats); case CompressedImageMode::Int32: return AnalyzeBlock(processed_image, first, input, n, INT32_MIN, INT32_MAX, stats); case CompressedImageMode::Uint8: return AnalyzeBlock(processed_image, first, input, n, UINT8_MAX, UINT8_MAX, stats); case CompressedImageMode::Uint16: return AnalyzeBlock(processed_image, first, input, n, UINT16_MAX, UINT16_MAX, stats); case CompressedImageMode::Uint32: return AnalyzeBlock(processed_image, first, input, n, UINT32_MAX, UINT32_MAX, stats); default: throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "RGB/float mode not supported"); } } template void ImagePreprocessorCPU::AnalyzeBlock(ImagePreprocessorBuffer &processed_image, size_t first, const uint8_t *input, size_t n, T err_pixel_val, T sat_pixel_val, ImageStatistics &ret) { auto image = reinterpret_cast(input); int32_t *out = processed_image.data() + first; if (sat_pixel_val > saturation_limit) sat_pixel_val = static_cast(saturation_limit); for (size_t i = 0; i < n; i++) { if (mask_1bit[first + i] != 0) { out[i] = INT32_MIN; ++ret.masked_pixel_count; } else if (image[i] == err_pixel_val) { // Error/invalid marker = the pixel type's extreme value (0xFFFFFFFF for EIGER uint32). // Tested before saturation, since for unsigned types the marker also exceeds sat_pixel_val // (which is clipped above to the HDF5 saturation_value). out[i] = INT32_MIN; ++ret.error_pixel_count; } else if (image[i] >= sat_pixel_val) { out[i] = INT32_MAX; ++ret.saturated_pixel_count; } else { out[i] = static_cast(image[i]); if (image[i] > ret.max_value) ret.max_value = image[i]; if (image[i] < ret.min_value) ret.min_value = image[i]; } } }