All checks were successful
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 10m14s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 10m4s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 12m0s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 10m31s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 10m0s
Build Packages / Generate python client (push) Successful in 45s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 11m17s
Build Packages / Create release (push) Has been skipped
Build Packages / Build documentation (push) Successful in 42s
Build Packages / build:rpm (rocky8) (push) Successful in 10m56s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 10m8s
Build Packages / build:rpm (rocky9) (push) Successful in 11m27s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 10m10s
Build Packages / Unit tests (push) Successful in 1h14m45s
This is an UNSTABLE release. The release has significant modifications and bug fixes, if things go wrong, it is better to revert to 1.0.0-rc.124. * jfjoch_broker: Rotation indexer has two retries if failes * jfjoch_broker: Rotation indexer handles small number of rotation images (like test shot) * jfjoch_broker: Integration calculates background mask based on R2 radius * jfjoch_process: HDF5 files are not saved by default Reviewed-on: #37
203 lines
7.7 KiB
C++
203 lines
7.7 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include "BraggIntegrate2D.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
template<class T>
|
|
float Median(std::vector<T> &values) {
|
|
if (values.empty())
|
|
return 0.0f;
|
|
|
|
const size_t middle = values.size() / 2;
|
|
std::nth_element(values.begin(), values.begin() + middle, values.end());
|
|
|
|
if (values.size() % 2 == 1)
|
|
return static_cast<float>(values[middle]);
|
|
|
|
const T upper = values[middle];
|
|
std::nth_element(values.begin(), values.begin() + middle - 1, values.begin() + middle);
|
|
const T lower = values[middle - 1];
|
|
|
|
return 0.5f * static_cast<float>(lower + upper);
|
|
}
|
|
|
|
void MarkReflectionMask(std::vector<uint8_t> &mask,
|
|
size_t xpixel, size_t ypixel,
|
|
const Reflection &r, float r_2, float r_2_sq) {
|
|
|
|
int64_t x0 = std::floor(r.predicted_x - r_2 - 1.0f);
|
|
int64_t x1 = std::ceil(r.predicted_x + r_2 + 1.0f);
|
|
int64_t y0 = std::floor(r.predicted_y - r_2 - 1.0f);
|
|
int64_t y1 = std::ceil(r.predicted_y + r_2 + 1.0f);
|
|
|
|
if (x0 < 0)
|
|
x0 = 0;
|
|
if (y0 < 0)
|
|
y0 = 0;
|
|
if (x1 >= static_cast<int64_t>(xpixel))
|
|
x1 = static_cast<int64_t>(xpixel) - 1;
|
|
if (y1 >= static_cast<int64_t>(ypixel))
|
|
y1 = static_cast<int64_t>(ypixel) - 1;
|
|
|
|
for (int64_t y = y0; y <= y1; y++) {
|
|
for (int64_t x = x0; x <= x1; x++) {
|
|
const float dist_sq = (x - r.predicted_x) * (x - r.predicted_x)
|
|
+ (y - r.predicted_y) * (y - r.predicted_y);
|
|
if (dist_sq < r_2_sq)
|
|
mask[y * xpixel + x] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<uint8_t> BuildReflectionMask(const std::vector<Reflection> &predicted,
|
|
size_t npredicted,
|
|
size_t xpixel, size_t ypixel,
|
|
float r_2, float r_2_sq) {
|
|
std::vector<uint8_t> mask(xpixel * ypixel, 0);
|
|
|
|
for (size_t i = 0; i < npredicted; i++)
|
|
MarkReflectionMask(mask, xpixel, ypixel, predicted.at(i), r_2, r_2_sq);
|
|
|
|
return mask;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
template<class T>
|
|
void IntegrateReflection(Reflection &r, const T *image, const std::vector<uint8_t> &reflection_mask,
|
|
size_t xpixel, size_t ypixel,
|
|
int64_t special_value, int64_t saturation,
|
|
float r_3, float r_1_sq, float r_2_sq, float r_3_sq) {
|
|
|
|
int64_t x0 = std::floor(r.predicted_x - r_3 - 1.0);
|
|
int64_t x1 = std::ceil(r.predicted_x + r_3 + 1.0);
|
|
int64_t y0 = std::floor(r.predicted_y - r_3 - 1.0);
|
|
int64_t y1 = std::ceil(r.predicted_y + r_3 + 1.0);
|
|
if (x0 < 0)
|
|
x0 = 0;
|
|
if (y0 < 0)
|
|
y0 = 0;
|
|
if (x1 >= xpixel)
|
|
x1 = xpixel - 1;
|
|
if (y1 >= ypixel)
|
|
y1 = ypixel - 1;
|
|
|
|
int64_t I_sum = 0;
|
|
int64_t I_npixel_inner = 0;
|
|
int64_t I_npixel_integrated = 0;
|
|
std::vector<T> bkg_values;
|
|
bkg_values.reserve(static_cast<size_t>((x1 - x0 + 1) * (y1 - y0 + 1)));
|
|
|
|
for (int64_t y = y0; y <= y1; y++) {
|
|
for (int64_t x = x0; x <= x1; x++) {
|
|
const float dist_sq = (x - r.predicted_x) * (x - r.predicted_x) + (y - r.predicted_y) * (y - r.predicted_y);
|
|
const auto pixel = image[y * xpixel + x];
|
|
|
|
if (dist_sq < r_1_sq)
|
|
I_npixel_inner++;
|
|
|
|
if (pixel == special_value || pixel == saturation)
|
|
continue;
|
|
|
|
if (dist_sq < r_1_sq) {
|
|
I_sum += pixel;
|
|
I_npixel_integrated++;
|
|
} else if (dist_sq >= r_2_sq && dist_sq < r_3_sq) {
|
|
if (reflection_mask[y * xpixel + x] != 0)
|
|
continue;
|
|
bkg_values.emplace_back(pixel);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((I_npixel_integrated == I_npixel_inner) && (bkg_values.size() > 5)) {
|
|
r.bkg = Median(bkg_values);
|
|
r.I = static_cast<float>(I_sum) - static_cast<float>(I_npixel_integrated) * r.bkg;
|
|
// minimum sigma is 1!
|
|
if (I_sum >= 1)
|
|
r.sigma = std::sqrt(static_cast<float>(I_sum));
|
|
else
|
|
r.sigma = 1;
|
|
r.observed = true;
|
|
} else {
|
|
r.I = 0;
|
|
r.bkg = 0;
|
|
r.observed = false;
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
std::vector<Reflection> IntegrateInternal(const DiffractionExperiment &experiment,
|
|
const CompressedImage &image,
|
|
const std::vector<Reflection> &predicted,
|
|
size_t npredicted,
|
|
int64_t special_value,
|
|
int64_t saturation,
|
|
int64_t image_number) {
|
|
|
|
std::vector<Reflection> ret;
|
|
ret.reserve(npredicted);
|
|
|
|
auto settings = experiment.GetBraggIntegrationSettings();
|
|
auto geom = experiment.GetDiffractionGeometry();
|
|
|
|
std::vector<uint8_t> buffer;
|
|
auto ptr = reinterpret_cast<const T *>(image.GetUncompressedPtr(buffer));
|
|
|
|
const float r_3 = settings.GetR3();
|
|
const float r_1_sq = settings.GetR1() * settings.GetR1();
|
|
const float r_2 = settings.GetR2();
|
|
const float r_2_sq = settings.GetR2() * settings.GetR2();
|
|
const float r_3_sq = settings.GetR3() * settings.GetR3();
|
|
|
|
const auto reflection_mask = BuildReflectionMask(predicted, npredicted,
|
|
image.GetWidth(), image.GetHeight(),
|
|
r_2, r_2_sq);
|
|
|
|
for (int i = 0; i < npredicted; i++) {
|
|
auto r = predicted.at(i);
|
|
IntegrateReflection(r, ptr, reflection_mask, image.GetWidth(), image.GetHeight(), special_value, saturation,
|
|
r_3, r_1_sq, r_2_sq, r_3_sq);
|
|
if (r.observed) {
|
|
if (experiment.GetPolarizationFactor())
|
|
r.rlp /= geom.CalcAzIntPolarizationCorr(r.predicted_x, r.predicted_y, experiment.GetPolarizationFactor().value());
|
|
r.image_number = static_cast<float>(image_number);
|
|
ret.emplace_back(r);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::vector<Reflection> BraggIntegrate2D(const DiffractionExperiment &experiment,
|
|
const CompressedImage &image,
|
|
const std::vector<Reflection> &predicted,
|
|
size_t npredicted,
|
|
int64_t image_number) {
|
|
if (image.GetCompressedSize() == 0 || predicted.empty())
|
|
return {};
|
|
|
|
switch (image.GetMode()) {
|
|
case CompressedImageMode::Int8:
|
|
return IntegrateInternal<int8_t>(experiment, image, predicted, npredicted, INT8_MIN, INT8_MAX, image_number);
|
|
case CompressedImageMode::Int16:
|
|
return IntegrateInternal<int16_t>(experiment, image, predicted, npredicted, INT16_MIN, INT16_MAX, image_number);
|
|
case CompressedImageMode::Int32:
|
|
return IntegrateInternal<int32_t>(experiment, image, predicted, npredicted, INT32_MIN, INT32_MAX, image_number);
|
|
case CompressedImageMode::Uint8:
|
|
return IntegrateInternal<uint8_t>(experiment, image, predicted, npredicted, UINT8_MAX, UINT8_MAX, image_number);
|
|
case CompressedImageMode::Uint16:
|
|
return IntegrateInternal<uint16_t>(experiment, image, predicted, npredicted, UINT16_MAX, UINT16_MAX, image_number);
|
|
case CompressedImageMode::Uint32:
|
|
return IntegrateInternal<uint32_t>(experiment, image, predicted, npredicted, UINT32_MAX, UINT32_MAX, image_number);
|
|
default:
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Image mode not supported");
|
|
}
|
|
}
|