// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "QuickIntegrate.h" #include "BraggPrediction.h" #include #include "IntegrationStats.h" QuickIntegrate::QuickIntegrate(const DiffractionExperiment &in_experiment) : experiment(in_experiment) {} template QuickIntegrateResult QuickIntegrate::IntegrateInternal(const CompressedImage &image, const CrystalLattice &latt, int64_t special_value, int64_t saturation, float high_res_A) { auto start = std::chrono::high_resolution_clock::now(); std::vector buffer; auto ptr = reinterpret_cast(image.GetUncompressedPtr(buffer)); BraggPrediction prediction(experiment,latt, high_res_A, ewald_sphere_angle_cutoff); auto refl = prediction.GetReflections(ewald_sphere_angle_cutoff); QuickIntegrateResult ret; IntegrationStats stats(6.0, high_res_A, 10); // Wilson statistics is only linear in certain resolution range for (const auto &r_iter: refl) { Reflection r = r_iter; if (IntegrateInternal(r, ptr, image.GetWidth(), image.GetHeight(), special_value, saturation)) { ret.reflections.push_back(r); stats.AddReflection(r); } } ret.b_factor = stats.BFactor(); return ret; } QuickIntegrateResult QuickIntegrate::Integrate(const CompressedImage& image, const CrystalLattice &latt, float high_res_A) { if (image.GetCompressedSize() == 0) return {}; switch (image.GetMode()) { case CompressedImageMode::Int8: return IntegrateInternal(image, latt, INT8_MIN, INT8_MAX, high_res_A); case CompressedImageMode::Int16: return IntegrateInternal(image, latt, INT16_MIN, INT16_MAX, high_res_A); case CompressedImageMode::Int32: return IntegrateInternal(image, latt, INT32_MIN, INT32_MAX, high_res_A); case CompressedImageMode::Uint8: return IntegrateInternal(image, latt, UINT8_MAX, UINT8_MAX, high_res_A); case CompressedImageMode::Uint16: return IntegrateInternal(image, latt, UINT16_MAX, UINT16_MAX, high_res_A); case CompressedImageMode::Uint32: return IntegrateInternal(image, latt, UINT32_MAX, UINT32_MAX, high_res_A); default: throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Image mode not supported"); } } template bool QuickIntegrate::IntegrateInternal(Reflection &r, const T *image, size_t xpixel, size_t ypixel, int64_t special_value, int64_t saturation) { r.I = 0; r.bkg = 0; int64_t x0 = std::floor(r.center_x_pxl - r_3 - 1.0); int64_t x1 = std::ceil(r.center_x_pxl + r_3 + 1.0); int64_t y0 = std::floor(r.center_y_pxl - r_3 - 1.0); int64_t y1 = std::ceil(r.center_y_pxl + 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 = 0; int64_t bkg_sum = 0; int64_t bkg_npixel = 0; for (int64_t y = y0; y <= y1; y++) { for (int64_t x = x0; x <= x1; x++) { float dist_sq = (x - r.center_x_pxl) * (x - r.center_x_pxl) + (y - r.center_y_pxl) * (y - r.center_y_pxl); if (image[y * xpixel + x] == special_value || image[y * xpixel + x] == saturation) { continue; } else if (dist_sq < r_1_sq) { I_sum += image[y * xpixel + x]; I_npixel++; } else if (dist_sq >= r_2_sq && dist_sq < r_3_sq) { bkg_sum += image[y * xpixel + x]; bkg_npixel++; } } } if ((I_npixel > 2) && (bkg_npixel > 5)) { r.bkg = static_cast(bkg_sum) / static_cast(bkg_npixel); r.I = static_cast(I_sum) - static_cast(I_npixel) * r.bkg; // minimum sigma is 1! if (I_sum >= 1) r.sigma = std::sqrt(static_cast(I_sum)); else r.sigma = 1; return true; } return false; }