Skip to content

Add preprocessing sensitivity script and tests; fix non-RAS scan hand… - #54

Merged
cclaess merged 1 commit into
mainfrom
dev
Jul 28, 2026
Merged

Add preprocessing sensitivity script and tests; fix non-RAS scan hand…#54
cclaess merged 1 commit into
mainfrom
dev

Conversation

@cclaess

@cclaess cclaess commented Jul 28, 2026

Copy link
Copy Markdown
Owner

…ling

Copilot AI review requested due to automatic review settings July 28, 2026 07:46
@cclaess
cclaess merged commit c05af47 into main Jul 28, 2026
1 check passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

The new sensitivity script’s reference-detection/labeling logic can misidentify the reference configuration (and the parity test volume size risks slowing CI), so the output correctness/perf should be fixed before approval.

Pull request overview

This PR adds an end-to-end parity test to ensure spectre.io.load_and_window matches the MONAI IO+preprocessing pipeline, introduces an evaluation script to quantify embedding sensitivity to preprocessing choices, and fixes a regression where non-RAS reorientation could produce negative-stride arrays that torch.from_numpy rejects.

Changes:

  • Add tests/test_io_pipeline_parity.py to validate bit-exact parity vs a MONAI reference pipeline across on-disk orientations and optional resampling, and to pin the intentional short-axis padding divergence.
  • Fix load_ct() to make reoriented arrays contiguous before converting to a Torch tensor (avoids negative-stride failures).
  • Add eval/preprocessing_sensitivity.py to sweep voxel spacings / HU windows and report embedding drift (CSV + optional plot).
File summaries
File Description
tests/test_io_pipeline_parity.py New end-to-end IO+orientation+resampling parity coverage against the MONAI reference pipeline.
src/spectre/io.py Make load_ct() robust to non-RAS reorientation by ensuring contiguous NumPy arrays before torch.from_numpy.
eval/preprocessing_sensitivity.py New analysis script to measure and visualize embedding sensitivity to preprocessing knobs.

Review details

Comments suppressed due to low confidence (2)

eval/preprocessing_sensitivity.py:295

  • loudest() excludes the reference by filtering on zero distance; that can drop the wrong config(s) (or keep the reference) for the same reasons as above. Exclude by the explicit reference label for the axis instead.
    def loudest(axis):
        cands = [s for s in summary if s["axis"] == axis and not (s["cos_mean"] == 0.0 and s["cos_std"] == 0.0)]
        return max(cands, key=lambda s: s["cos_mean"]) if cands else None

eval/preprocessing_sensitivity.py:331

  • The plot greys the reference bar using a zero-distance check, which can grey additional bars (or fail to grey the reference) if embeddings are identical or slightly nondeterministic. Grey by matching the reference config label for the axis instead.
        entries = [s for s in summary if s["axis"] == axis]
        labels = [s["config"] for s in entries]
        means = [s[mean_k] for s in entries]
        stds = [s[std_k] for s in entries]
        colors = [ref_bar if (s["cos_mean"] == 0.0 and s["cos_std"] == 0.0) else bar for s in entries]
        x = range(len(entries))
        ax.bar(x, means, yerr=stds, color=colors, ecolor=err, capsize=3, width=0.7, zorder=3)
  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Low

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +188 to +189
for i, scan in enumerate(scans, 1):
name = scan.name
Comment on lines +285 to +288
cos = f"{s['cos_mean']:.4f} ± {s['cos_std']:.4f}"
l2 = f"{s['l2_mean']:.4f} ± {s['l2_std']:.4f}"
tag = " (ref)" if s["cos_mean"] == 0.0 and s["cos_std"] == 0.0 else ""
print(f" {s['config']:<16}{cos:<26}{l2:<24}{s['cos_max']:>8.4f}{tag}")
Comment on lines +50 to +53
# Every axis large enough that even coarse (1.0 mm) resampling stays above the crop on every axis
# and orientation: min physical extent is 170 * 0.9 = 153 mm > 128.
PARITY_SHAPE = (190, 200, 170)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants