Every dense map that needs geometry now evaluates each image pixel at its place on the detector plane (DetectorModules), so it is right for a stacked panel layout and picks up refined module placements: - AzimuthalIntegrationMapping: bins, resolution, solid-angle/polarisation corrections built per pixel through the table; a pixel on no panel is left as a masked one. The geometry cache key now includes the panel table, so a mapping built after the stills module placement is rebuilt rather than served from the cache. ResolutionMaskBits follows (it is a lookup). - ROIMap / ExportROIMap: azimuthal ROIs on the plane (box and circle ROIs stay in image pixels). - ROIIntegration CPU+GPU: the weighted sums are taken per ROI and panel in image pixels and placed on the plane at the end (exact; the affine commutes with a weighted mean). - HotPixelFinder: ring/sector keys and the zeta of the persistence rule on the plane. - Background beam-centre walk CPU+GPU: pixels binned at their plane position; host and device share BackgroundPlanePoint so they stay bit-identical. New common/PanelRows.h: ForEachPixelInRows, ForEachPixel cut to a band of rows in index order, for the row-parallel builders. An empty table takes the old arithmetic in the old order: rotation sets and every single-panel input are unchanged. [PanelLayout] AzintMapping and ResolutionMask enabled (+ azimuthal ROI map and ROI centroid on the plane); new BeamCenterFromBackground_BinsOnThePlane and ROIIntegrationGPU_MatchesCPU_Panels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
40 lines
1.7 KiB
C++
40 lines
1.7 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
#include "DetectorModules.h"
|
|
|
|
// DetectorModules::ForEachPixel cut to the image rows [row0, row1), so that a dense map can be built a band
|
|
// of rows per thread. Calls f(panel, index, px, py) for every pixel of those rows that is on a panel, in
|
|
// index order: row by row, and within a row panel by panel from left to right. Pixels on no panel are not
|
|
// visited. Empty table: every pixel of the rows, panel -1, plane == image.
|
|
template<class F>
|
|
void ForEachPixelInRows(const DetectorModules &modules, int64_t width, int64_t row0, int64_t row1, F &&f) {
|
|
if (modules.empty()) {
|
|
for (int64_t y = row0; y < row1; y++)
|
|
for (int64_t x = 0; x < width; x++)
|
|
f(int64_t(-1), static_cast<size_t>(y * width + x), static_cast<double>(x), static_cast<double>(y));
|
|
return;
|
|
}
|
|
const auto &m = modules.Get();
|
|
std::vector<int64_t> on_row;
|
|
for (int64_t y = row0; y < row1; y++) {
|
|
on_row.clear();
|
|
for (size_t p = 0; p < m.size(); p++)
|
|
if (y >= m[p].y0 && y < m[p].y0 + m[p].height)
|
|
on_row.push_back(static_cast<int64_t>(p));
|
|
std::sort(on_row.begin(), on_row.end(), [&](int64_t a, int64_t b) { return m[a].x0 < m[b].x0; });
|
|
for (const int64_t p: on_row)
|
|
for (int64_t x = m[p].x0; x < m[p].x0 + m[p].width; x++) {
|
|
const auto [px, py] = m[p].ImageToPlane(static_cast<double>(x), static_cast<double>(y));
|
|
f(p, static_cast<size_t>(y * width + x), px, py);
|
|
}
|
|
}
|
|
}
|