diff --git a/image_analysis/MXAnalysisWithoutFPGA.cpp b/image_analysis/MXAnalysisWithoutFPGA.cpp index 094f1762..4c06723c 100644 --- a/image_analysis/MXAnalysisWithoutFPGA.cpp +++ b/image_analysis/MXAnalysisWithoutFPGA.cpp @@ -9,7 +9,7 @@ #include "spot_finding/SpotUtils.h" #include "spot_finding/ImageSpotFinderFactory.h" #include "bragg_prediction/BraggPredictionFactory.h" -#include "image_preprocessing/ImagePreprocessorCPU.h" +#include "image_preprocessing/ImagePreprocessor.h" MXAnalysisWithoutFPGA::MXAnalysisWithoutFPGA(const DiffractionExperiment &in_experiment, const AzimuthalIntegration &in_integration, @@ -26,7 +26,7 @@ MXAnalysisWithoutFPGA::MXAnalysisWithoutFPGA(const DiffractionExperiment &in_exp mask_resolution(experiment.GetPixelsNum(), false), mask_high_res(-1), mask_low_res(-1) { - preprocessor = std::make_unique(in_experiment, in_integration, in_mask); + preprocessor = std::make_unique(in_experiment, in_integration, in_mask, spotFinder->GetInputBuffer()); } void MXAnalysisWithoutFPGA::Analyze(DataMessage &output, @@ -45,7 +45,6 @@ void MXAnalysisWithoutFPGA::Analyze(DataMessage &output, const auto preprocessing_start_time = std::chrono::steady_clock::now(); auto ret = preprocessor->Analyze(image_ptr, output.image.GetMode()); - const auto preprocessing_end_time = std::chrono::steady_clock::now(); output.preprocessing_time_s = std::chrono::duration(preprocessing_end_time - preprocessing_start_time).count(); @@ -56,8 +55,6 @@ void MXAnalysisWithoutFPGA::Analyze(DataMessage &output, UpdateMaskResolution(spot_finding_settings); const auto spot_finding_start_time = std::chrono::steady_clock::now(); - - memcpy(spotFinder->GetInputBuffer().data(), preprocessor->GetProcessedImage().data(), npixels * sizeof(int32_t)); const std::vector spots = spotFinder->Run(spot_finding_settings, mask_resolution); SpotAnalyze(experiment, spot_finding_settings, spots, output); const auto spot_finding_end_time = std::chrono::steady_clock::now(); @@ -65,7 +62,7 @@ void MXAnalysisWithoutFPGA::Analyze(DataMessage &output, if (spot_finding_settings.indexing) indexer.ProcessImage(output, spot_finding_settings, - CompressedImage(preprocessor->GetProcessedImage(), experiment.GetXPixelsNum(), experiment.GetYPixelsNum()), + CompressedImage(spotFinder->GetInputBuffer(), experiment.GetXPixelsNum(), experiment.GetYPixelsNum()), *prediction); } diff --git a/image_analysis/image_preprocessing/CMakeLists.txt b/image_analysis/image_preprocessing/CMakeLists.txt index fd796a7e..55a3e7cb 100644 --- a/image_analysis/image_preprocessing/CMakeLists.txt +++ b/image_analysis/image_preprocessing/CMakeLists.txt @@ -1,5 +1,3 @@ -ADD_LIBRARY(JFJochImagePreprocessing - STATIC ImagePreprocessorCPU.cpp ImagePreprocessorCPU.h - ImagePreprocessor.cpp ImagePreprocessor.h) +ADD_LIBRARY(JFJochImagePreprocessing STATIC ImagePreprocessor.cpp ImagePreprocessor.h) TARGET_LINK_LIBRARIES(JFJochImagePreprocessing JFJochCommon) \ No newline at end of file diff --git a/image_analysis/image_preprocessing/ImagePreprocessor.cpp b/image_analysis/image_preprocessing/ImagePreprocessor.cpp index dd4413de..5992004b 100644 --- a/image_analysis/image_preprocessing/ImagePreprocessor.cpp +++ b/image_analysis/image_preprocessing/ImagePreprocessor.cpp @@ -6,26 +6,94 @@ ImagePreprocessor::ImagePreprocessor(const DiffractionExperiment &experiment, const AzimuthalIntegration &integration, - const PixelMask &mask) + const PixelMask &mask, + std::vector &processed_image) : npixels(experiment.GetPixelsNum()), experiment(experiment), integration(integration), azint_sum(integration.GetBinNumber(), 0.0), azint_sum2(integration.GetBinNumber(), 0.0), azint_count(integration.GetBinNumber(), 0), - processed_image(npixels, INT32_MIN), + processed_image(processed_image), mask_1bit(npixels, false), azint_bins(integration.GetBinNumber()), saturation_limit(experiment.GetSaturationLimit()) { + + if (processed_image.size() != npixels) + throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Processed image size mismatch"); + for (int i = 0; i < npixels; i++) mask_1bit[i] = (mask.GetMask().at(i) != 0); } -const std::vector &ImagePreprocessor::GetProcessedImage() const { - return processed_image; -} - void ImagePreprocessor::Update(AzimuthalIntegrationProfile &profile) const { profile.Clear(integration); profile.Add(azint_sum, azint_count); } + +ImageStatistics ImagePreprocessor::Analyze(const uint8_t *image_ptr, CompressedImageMode image_mode) { + switch (image_mode) { + case CompressedImageMode::Int8: + return Analyze(image_ptr, INT8_MIN, INT8_MAX); + case CompressedImageMode::Int16: + return Analyze(image_ptr, INT16_MIN, INT16_MAX); + case CompressedImageMode::Int32: + return Analyze(image_ptr, INT32_MIN, INT32_MAX); + case CompressedImageMode::Uint8: + return Analyze(image_ptr, UINT8_MAX, UINT8_MAX); + case CompressedImageMode::Uint16: + return Analyze(image_ptr, UINT16_MAX, UINT16_MAX); + case CompressedImageMode::Uint32: + return Analyze(image_ptr, UINT32_MAX, UINT32_MAX); + default: + throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "RGB/float mode not supported"); + } +} + +template +ImageStatistics ImagePreprocessor::Analyze(const uint8_t *input, T err_pixel_val, T sat_pixel_val) { + auto image = reinterpret_cast(input); + + for (int i = 0; i < azint_count.size(); i++) { + azint_sum[i] = 0.0f; + azint_sum2[i] = 0.0f; + azint_count[i] = 0; + } + + ImageStatistics ret{}; + + if (sat_pixel_val > saturation_limit) + sat_pixel_val = static_cast(saturation_limit); + + auto &pixel_to_bin = integration.GetPixelToBin(); + auto &corrections = integration.Corrections(); + + for (int i = 0; i < npixels; i++) { + if (mask_1bit[i] != 0) { + processed_image[i] = INT32_MIN; + ++ret.masked_pixel_count; + } else if (image[i] >= sat_pixel_val) { + processed_image[i] = INT32_MAX; + ++ret.saturated_pixel_count; + } else if (std::is_signed::value && (image[i] == err_pixel_val)) { + // Error pixels are possible only for signed types + processed_image[i] = INT32_MIN; + ++ret.error_pixel_count; + } else { + processed_image[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]; + + const uint16_t bin = pixel_to_bin[i]; + if (bin < azint_bins) { + float val = image[i] * corrections[i]; + azint_sum[bin] += val; + ++azint_count[bin]; + } + } + } + return ret; +} \ No newline at end of file diff --git a/image_analysis/image_preprocessing/ImagePreprocessor.h b/image_analysis/image_preprocessing/ImagePreprocessor.h index 61891a66..ae737ef7 100644 --- a/image_analysis/image_preprocessing/ImagePreprocessor.h +++ b/image_analysis/image_preprocessing/ImagePreprocessor.h @@ -28,20 +28,22 @@ protected: std::vector azint_sum; std::vector azint_sum2; std::vector azint_count; - std::vector processed_image; + std::vector &processed_image; std::vector mask_1bit; uint16_t azint_bins; const int64_t saturation_limit; + template ImageStatistics Analyze(const uint8_t *input, T err_value, T sat_value); + public: - ImagePreprocessor(const DiffractionExperiment &experiment, const AzimuthalIntegration &integration, const PixelMask &mask); + ImagePreprocessor(const DiffractionExperiment &experiment, + const AzimuthalIntegration &integration, + const PixelMask &mask, + std::vector &processed_image); - virtual ~ImagePreprocessor() = default; - virtual ImageStatistics Analyze(const uint8_t *decompressed_image, CompressedImageMode image_mode) = 0; - - [[nodiscard]] const std::vector &GetProcessedImage() const; + ImageStatistics Analyze(const uint8_t *decompressed_image, CompressedImageMode image_mode); void Update(AzimuthalIntegrationProfile &profile) const; }; diff --git a/image_analysis/image_preprocessing/ImagePreprocessorCPU.cpp b/image_analysis/image_preprocessing/ImagePreprocessorCPU.cpp deleted file mode 100644 index af513d0c..00000000 --- a/image_analysis/image_preprocessing/ImagePreprocessorCPU.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute -// SPDX-License-Identifier: GPL-3.0-only - -#include "ImagePreprocessorCPU.h" - -ImagePreprocessorCPU::ImagePreprocessorCPU(const DiffractionExperiment &in_experiment, - const AzimuthalIntegration &in_integration, - const PixelMask &in_mask) - : ImagePreprocessor(in_experiment, in_integration, in_mask) {} - -ImageStatistics ImagePreprocessorCPU::Analyze(const uint8_t *image_ptr, CompressedImageMode image_mode) { - switch (image_mode) { - case CompressedImageMode::Int8: - return Analyze(image_ptr, INT8_MIN, INT8_MAX); - case CompressedImageMode::Int16: - return Analyze(image_ptr, INT16_MIN, INT16_MAX); - case CompressedImageMode::Int32: - return Analyze(image_ptr, INT32_MIN, INT32_MAX); - case CompressedImageMode::Uint8: - return Analyze(image_ptr, UINT8_MAX, UINT8_MAX); - case CompressedImageMode::Uint16: - return Analyze(image_ptr, UINT16_MAX, UINT16_MAX); - case CompressedImageMode::Uint32: - return Analyze(image_ptr, UINT32_MAX, UINT32_MAX); - default: - throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "RGB/float mode not supported"); - } -} - -template -ImageStatistics ImagePreprocessorCPU::Analyze(const uint8_t *input, T err_pixel_val, T sat_pixel_val) { - auto image = reinterpret_cast(input); - - for (int i = 0; i < azint_count.size(); i++) { - azint_sum[i] = 0.0f; - azint_sum2[i] = 0.0f; - azint_count[i] = 0; - } - - ImageStatistics ret{}; - - if (sat_pixel_val > saturation_limit) - sat_pixel_val = static_cast(saturation_limit); - - auto &pixel_to_bin = integration.GetPixelToBin(); - auto &corrections = integration.Corrections(); - - for (int i = 0; i < npixels; i++) { - if (mask_1bit[i] != 0) { - processed_image[i] = INT32_MIN; - ++ret.masked_pixel_count; - } else if (image[i] >= sat_pixel_val) { - processed_image[i] = INT32_MAX; - ++ret.saturated_pixel_count; - } else if (std::is_signed::value && (image[i] == err_pixel_val)) { - // Error pixels are possible only for signed types - processed_image[i] = INT32_MIN; - ++ret.error_pixel_count; - } else { - processed_image[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]; - - const uint16_t bin = pixel_to_bin[i]; - if (bin < azint_bins) { - float val = image[i] * corrections[i]; - azint_sum[bin] += val; - azint_sum2[bin] += val * val; - ++azint_count[bin]; - } - } - } - return ret; -} diff --git a/image_analysis/image_preprocessing/ImagePreprocessorCPU.h b/image_analysis/image_preprocessing/ImagePreprocessorCPU.h deleted file mode 100644 index 4f8d5b7c..00000000 --- a/image_analysis/image_preprocessing/ImagePreprocessorCPU.h +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute -// SPDX-License-Identifier: GPL-3.0-only - -#pragma once - -#include - -#include "ImagePreprocessor.h" -#include "../common/JFJochMessages.h" -#include "../common/DiffractionExperiment.h" -#include "../common/AzimuthalIntegration.h" -#include "../common/PixelMask.h" -#include "../common/AzimuthalIntegrationProfile.h" - -class ImagePreprocessorCPU : public ImagePreprocessor { - template - ImageStatistics Analyze(const uint8_t *input, T err_value, T sat_value); -public: - ImagePreprocessorCPU(const DiffractionExperiment &in_experiment, - const AzimuthalIntegration &in_integration, - const PixelMask &in_mask); - - ImageStatistics Analyze(const uint8_t *decompressed_image, CompressedImageMode image_mode) override; -};