Merge branch 'shadow-6jgh-dimfield' into r4-integration

# Conflicts:
#	image_analysis/beam_stop/ShadowFinder.cpp
This commit is contained in:
leonarski_f committed 2026-09-27 10:10:07 +02:00
commit cff5fa0fdb
3 files changed
+140 -3

No files matched your search

+41
View File
@@ -383,6 +383,47 @@ TEST_CASE("ShadowFinder_APolarizedBackgroundIsNotAShadow", "[ShadowFinder]") {
CHECK(mask_for(std::nullopt) > static_cast<long>(W) * H / 20);
}
// The other side of that correction: a polarization factor that is not the beam's. Here the
// background is unpolarized and the experiment states 0.99, so after the division every outer ring is
// bright along x and dim along y - a smooth lobe below PENUMBRA_RATIO across the top and bottom of the
// detector with nothing in the beam. On its own the lobe is never deep, but a small absorbing speck
// inside it is, and the transmitting-hardware step joined the two into one piece "dim everywhere and
// deep in places" and masked the whole lobe. The step compares a pixel with its ring's own second
// harmonic, so the lobe is explained away and the speck, far below a shadow's size, stays unmasked.
TEST_CASE("ShadowFinder_AWrongPolarizationFactorIsNotAnArm", "[ShadowFinder]") {
constexpr float DISTANCE_MM = 6.0f;
constexpr int LEVEL = 24;
constexpr int SPECK_X = C, SPECK_Y = 20, SPECK_R = 11;
DiffractionExperiment x = TestExperiment();
x.DetectorDistance_mm(DISTANCE_MM).PolarizationFactor(0.99f);
const float pixel_mm = x.GetPixelSize_mm();
std::vector<int32_t> frame(static_cast<size_t>(W) * H);
for (int y = 0; y < H; y++)
for (int xi = 0; xi < W; xi++) {
const double rho = std::hypot(xi - C, y - C) * pixel_mm;
const double cos_2theta_sq = DISTANCE_MM * DISTANCE_MM / (DISTANCE_MM * DISTANCE_MM + rho * rho);
frame[I(xi, y)] = static_cast<int32_t>(std::lround(LEVEL * 0.5 * (1.0 + cos_2theta_sq)));
const int sx = xi - SPECK_X, sy = y - SPECK_Y;
if (sx * sx + sy * sy <= SPECK_R * SPECK_R)
frame[I(xi, y)] /= 10;
}
const PixelMask pixel_mask(x);
ShadowFinder finder(x, pixel_mask);
std::vector<uint8_t> buffer;
for (int f = 0; f < NFRAMES; f++) {
DataMessage msg{};
msg.image = CompressedImage(frame, W, H);
finder.AddImage(msg, buffer);
}
const auto mask = finder.GetMask();
CHECK(mask[I(C, 5)] == 0); // the dim lobe along y
CHECK(mask[I(SPECK_X, SPECK_Y)] == 0); // the speck, too small to be a shadow
CHECK(std::count(mask.begin(), mask.end(), 1u) == 0);
}
// Inside the stop a whole ring is blocked, so its own median is blocked too and the per-pixel
// comparison has nothing to work with; the walk that finds those rings is what covers the disk.
// It decides by comparing a ring against what this detector's background typically is - and NOT