// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include #include "../common/AzimuthalIntegrationMapping.h" #include "../image_analysis/spot_finding/AdaptiveSpotFinderCPU.h" namespace { SpotFindingSettings AdaptiveSettings() { SpotFindingSettings s{}; s.adaptive_threshold = true; s.false_pixels_per_frame = 100.0f; s.min_pix_per_spot = 1; s.max_pix_per_spot = 50; s.high_resolution_limit = 0.0f; s.low_resolution_limit = 1.0e6f; s.high_res_gap_Q_recipA = std::nullopt; return s; } } // namespace // Raw (untransformed) geometry: the mapping is built over the raw module layout, which is smaller // than the converted image. The finder has to walk the raw image, so a spot planted at a raw pixel // comes back at that pixel. TEST_CASE("AdaptiveSpotFinderCPU_RawGeometry", "[AdaptiveSpotFinder]") { DiffractionExperiment x(DetJF4M()); x.DetectorDistance_mm(80).BeamX_pxl(1030).BeamY_pxl(1080); x.QSpacingForAzimInt_recipA(0.05).QRangeForAzimInt_recipA(0.05, 5.0); x.GeometryTransformation(false); PixelMask pixel_mask(x); AzimuthalIntegrationMapping mapping(x, pixel_mask); const size_t w = x.GetXPixelsNum(); const size_t h = x.GetYPixelsNum(); REQUIRE(w * h == mapping.GetPixelToBin().size()); REQUIRE(w * h < static_cast(x.GetPixelsNumConv())); ImagePreprocessorBuffer buffer(x.GetPixelsNum()); for (size_t i = 0; i < w * h; i++) buffer[i] = 8 + static_cast(i % 5); // background 8..12 // A 3x3 blob on a pixel that has a ring - with all of its neighbours on one too. const auto &pixel_to_bin = mapping.GetPixelToBin(); size_t spot_row = 0, spot_col = 0; for (size_t row = 100; row < h - 100 && spot_row == 0; row++) { for (size_t col = 100; col < w - 100; col++) { bool all_binned = true; for (int dr = -1; dr <= 1; dr++) for (int dc = -1; dc <= 1; dc++) all_binned &= pixel_to_bin[(row + dr) * w + col + dc] != UINT16_MAX; if (all_binned) { spot_row = row; spot_col = col; break; } } } REQUIRE(spot_row > 0); for (int dr = -1; dr <= 1; dr++) for (int dc = -1; dc <= 1; dc++) buffer[(spot_row + dr) * w + spot_col + dc] = 200; std::vector res_mask(x.GetPixelsNum(), false); AdaptiveSpotFinderCPU finder(mapping); const auto spots = finder.Run(buffer, AdaptiveSettings(), res_mask); REQUIRE(spots.size() == 1); CHECK(std::lround(spots[0].RawCoord().x) == static_cast(spot_col)); CHECK(std::lround(spots[0].RawCoord().y) == static_cast(spot_row)); }