The measured-modulation finder of the previous three commits is withdrawn. On a CCD sweep with a holder arm along x it lost the arm: a small rise in the deep pixels' count promoted part of the arm to shadow, which left the rest of the arm too few deep pixels to pass as transmitting hardware, and the mask fell from 2.5 % to 0.4 % of the detector. On a 16-Mpixel stills detector the per-band fit went wrong in the outer bands and masked 3.6 % of the detector instead of 0.03 %. The shadow finder is rc178's again. The stills defect it was meant to fix came from dividing by the factor of the wrong plane: the background of that run is suppressed along the image's y axis, not lab x, and the division left dim lobes over the corners on the arm search's thresholds, so that the third decimal of p moved thousands of pixels. The degree cannot be told from the data, but the plane can. Before the correction the mean projection is compared with the profile a polarized source gives each radial band (rings relative to their upper decile, each sector the brighter of itself and the one opposite, bands seen over three quarters of the azimuth); the stated degree is applied in the plane the background follows when it follows one clearly (correlation > 0.5, at least 0.3 of a fully polarized source's modulation), and as stated otherwise. Over 46 rotation sweeps and 11 stills runs only that one run is turned, so every other mask is unchanged; there the mask is the 5248-pixel stop at p = 0.99, 0.995 and 0.998. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
40 lines
2.0 KiB
C++
40 lines
2.0 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
// Included only under JFJOCH_USE_CUDA.
|
|
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
#include <cuda_runtime.h>
|
|
|
|
// What the mask is drawn about: the detector, the beam centre, and the geometry the rings and the
|
|
// polarization factor are read off (ShadowFinder keeps it as a DiffractionGeometry; the device takes
|
|
// it as numbers).
|
|
struct ShadowMaskSetup {
|
|
int width = 0, height = 0;
|
|
float beam_x = 0.0f, beam_y = 0.0f;
|
|
float det_matrix[9] = {}; // row major
|
|
float pixel_size_mm = 0.0f;
|
|
float distance_mm = 0.0f;
|
|
bool has_polarization = false;
|
|
float polarization = 0.0f;
|
|
};
|
|
|
|
// ShadowFinder::GetMask on the device, from the projection ShadowAccumulatorGPU holds there. Step for
|
|
// step the host's algorithm - the same pooling, ring medians, components, morphology and arm search -
|
|
// and the same answer wherever the arithmetic is exact: every integer, comparison, sort and component
|
|
// is. What is not is the floating point the two compilers evaluate differently - the polarization
|
|
// factor's trigonometry, the Poisson test's logarithm and the azimuth of the arm search - so a pixel
|
|
// within a rounding of one of those thresholds can come out the other way.
|
|
std::vector<uint32_t> ShadowMaskOnDevice(const ShadowMaskSetup &setup, const std::vector<uint32_t> &pixel_mask,
|
|
const int64_t *max_value, const int64_t *sum_value,
|
|
const uint32_t *valid_count, uint32_t frames, cudaStream_t stream);
|
|
|
|
// The mean projection the host's GetMeanProjection makes, computed where the sums are: the same
|
|
// division, so the same bits, and a quarter of the bytes to bring back.
|
|
std::vector<float> MeanProjectionOnDevice(const std::vector<uint32_t> &pixel_mask, const int64_t *sum_value,
|
|
const uint32_t *valid_count, size_t npixels, cudaStream_t stream);
|