Files
Jungfraujoch/image_analysis/ImageAnalysisCPU.cpp
2025-04-22 14:42:14 +02:00

183 lines
6.6 KiB
C++

// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "ImageAnalysisCPU.h"
#include "../common/AzimuthalIntegrationProfile.h"
#include "../common/CUDAWrapper.h"
#include "StrongPixelSet.h"
#include "../compression/JFJochDecompress.h"
ImageAnalysisCPU::ImageAnalysisCPU(const DiffractionGeometry &geom,
const AzimuthalIntegrationSettings &azint_settings,
const SpotFindingSettings &spot_finding_settings,
const ROIMap &rmap,
const std::optional<UnitCell> &uc,
const std::vector<uint32_t> &mask,
size_t width, size_t height)
: geom(geom),
rois(rmap),
integration(geom, azint_settings, mask, width, height),
settings(spot_finding_settings),
npixels(width * height),
xpixels(width),
mask_1byte(npixels, 0),
spotFinder(integration) {
roi_count = rmap.size();
roi_map = rmap.GetROIMap(geom, width, height);
roi_names = rmap.GetROINameMap();
for (int i = 0; i < npixels; i++)
mask_1byte[i] = (mask[i] != 0);
if (uc && (get_gpu_count() > 0)) {
try {
indexer = std::make_unique<IndexerWrapper>();
indexer->Setup(uc.value());
} catch (const std::exception &e) {
throw JFJochException(JFJochExceptionCategory::GPUCUDAError, e.what());
}
}
}
void ImageAnalysisCPU::Analyze(DataMessage &output, std::vector<uint8_t> &image) {
if ((output.image.xpixel != xpixels)
|| (output.image.xpixel * output.image.ypixel != npixels))
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Mismatch in pixel size");
image.resize(output.image.xpixel * output.image.ypixel * output.image.pixel_depth_bytes);
JFJochDecompressPtr(image.data(),
output.image.algorithm,
output.image.data,
output.image.size,
output.image.xpixel * output.image.ypixel,
output.image.pixel_depth_bytes);
if (output.image.pixel_is_signed) {
if (output.image.pixel_depth_bytes == 1)
Analyze<int8_t>(output, image.data(), INT8_MIN, INT8_MAX);
else if (output.image.pixel_depth_bytes == 2)
Analyze<int16_t>(output, image.data(), INT16_MIN, INT16_MAX);
else if (output.image.pixel_depth_bytes == 4)
Analyze<int32_t>(output, image.data(), INT32_MIN, INT32_MAX);
} else {
if (output.image.pixel_depth_bytes == 1)
Analyze<uint8_t>(output, image.data(), UINT8_MAX, UINT8_MAX);
else if (output.image.pixel_depth_bytes == 2)
Analyze<uint16_t>(output, image.data(), UINT16_MAX, UINT16_MAX);
else if (output.image.pixel_depth_bytes == 4)
Analyze<uint32_t>(output, image.data(), UINT32_MAX, UINT32_MAX);
}
}
template <class T>
void ImageAnalysisCPU::Analyze(DataMessage &output,
const uint8_t *in_image,
T err_pixel_val,
T sat_pixel_val) {
auto image = (T *) in_image;
std::vector<ROIMessage> roi(roi_count);
size_t err_pixels = 0;
size_t masked_pixels = 0;
size_t sat_pixels = 0;
int64_t min_value = INT64_MAX;
int64_t max_value = INT64_MIN;
auto &pixel_to_bin = integration.GetPixelToBin();
auto &corrections = integration.Corrections();
auto nbins = integration.GetBinNumber();
std::vector<float> sum(nbins);
std::vector<float> sum2(nbins);
std::vector<uint32_t> count(nbins);
for (int i = 0; i < npixels; i++) {
auto bin = pixel_to_bin[i];
auto value = image[i] * corrections[i];
if (mask_1byte[i] != 0)
++masked_pixels;
else if (image[i] == sat_pixel_val)
++sat_pixels;
else if (image[i] == err_pixel_val)
++err_pixels;
else {
if (image[i] > max_value)
max_value = image[i];
if (image[i] < min_value)
min_value = image[i];
if (roi_count > 0 && (roi_map[i] != 0)) {
int64_t x = i % xpixels;
int64_t y = i / xpixels;
for (int8_t r = 0; r < roi_count; r++) {
if ((roi_map[i] & (1<<r)) != 0) {
roi[r].sum += image[i];
roi[r].sum_square += image[i] * image[i];
roi[r].pixels += 1;
if (image[i] > roi[r].max_count)
roi[r].max_count = image[i];
roi[r].x_weighted += x * image[i];
roi[r].y_weighted += y * image[i];
}
}
}
if (bin < nbins) {
sum[bin] += value;
sum2[bin] += value * value;
count[bin] += 1;
}
}
}
AzimuthalIntegrationProfile azint_profile(integration);
azint_profile.Add(sum, count);
std::vector<DiffractionSpot> spots;
if (settings.enable)
spots = spotFinder.Run(image, GetSpotFindingSettings());
std::vector<DiffractionSpot> spots_out;
FilterSpotsByCount(max_spot_count, spots, spots_out);
for (const auto &spot: spots_out)
output.spots.push_back(spot);
output.indexing_result = false;
if (indexer && settings.indexing)
indexer->Run(output, spots_out, geom, settings.indexing_tolerance);
output.max_viable_pixel_value = max_value;
output.min_viable_pixel_value = min_value;
output.error_pixel_count = err_pixels;
output.saturated_pixel_count = sat_pixels;
output.az_int_profile = azint_profile.GetResult();
output.bkg_estimate = azint_profile.GetBkgEstimate(integration.Settings());
for (const auto &[key, val]: roi_names)
output.roi[key] = roi[val];
}
void ImageAnalysisCPU::FillStartMessage(StartMessage &msg) {
msg.rois = rois.ExportMetadata();
msg.az_int_bin_to_q = integration.GetBinToQ();
msg.az_int_bin_number = integration.GetBinNumber();
msg.max_spot_count = max_spot_count;
}
void ImageAnalysisCPU::SetSpotFindingSettings(const SpotFindingSettings &in_settings) {
std::unique_lock ul(spot_finding_settings_mutex);
settings = in_settings;
}
SpotFindingSettings ImageAnalysisCPU::GetSpotFindingSettings() const {
std::unique_lock ul(spot_finding_settings_mutex);
return settings;
}