DICOM coordinate validation#

This is the validation record for #2066: independent test methods, fixture provenance, reproducible checks and known limitations. For the coordinate definition, affected historical calculations, physical tolerances and plotting guidance, read DICOM coordinates and gamma, illustrated. That notebook is the physicist-facing explanation; those examples are not repeated here.

Unless a row states otherwise, the one-off checks below compared the original implementation at 400b61fe8 with this implementation at 658c4b6f0. The limitations section lists the DICOM helpers and gamma search cases that these checks do not cover.

Independence of the checks#

The reference geometry comes from DICOM PS3.3 C.7.6.2.1.1 and C.8.8.3.2, not the production conversion. The checks use both a matrix oracle and a separately tabulated storage encoding, with manually worked voxels to check the oracle itself. Asymmetric nonzero dose values expose incorrect reversals and row/column swaps that equal-shaped or uniform arrays could hide.

The physical grid-equality check is tested against exhaustive voxel positions, including rounded direction cosines, both acceptance boundaries and errors on interior slices. Its compact implementation checks four corners of every slice: displacement is affine in row and column, so its convex Euclidean norm reaches a maximum at an in-plane corner. It retains the original encoded cosines; the cardinal approximation used for separable axis extraction is not substituted into this comparison. The notebook explains the user-facing geometric limits and their distinction from clinical significance.

Evidence and its limits#

Validation combines ongoing local tests with recorded historical checks:

  1. A homogeneous 4x4 matrix implementation in the synthetic fixture, checked against manually worked HFP and FFDL voxels.

  2. A manually tabulated storage encoding in the orientation invariance tests. This starts with known patient axes and dose values and does not call the production geometry code or the matrix oracle to encode them.

  3. The historical real-file orientation metadata and expected_fixed_xyz.json baseline, verified before retiring the download-backed tests as recorded below.

One-off independent checks:

Check

Result

Eight orientations, increasing and decreasing relative slice offsets

All 16 encodings recover exactly the original physical axes and dose

All 64 reference/evaluation orientation pairs, both interpolators

Same results as gamma on the known physical grid, within 1e-12

Identical and discrepant dose fields for every pair

Identical fields give zero; the discrepancy includes failing points and remains orientation invariant

First/last crop on each of three patient axes in all eight orientations

All 48 cropped-reference comparisons give zero gamma against their full grid

Off-grid dose interpolation in all eight orientations

Agrees with independently averaged voxel corners within 1e-12 Gy

Historical metadata files for all eight orientations

Zero difference from the independent DICOM matrix calculation

Historical IEC FIXED baseline and pre-PR implementation

Exactly equal in all eight orientations

800 additional seeded random grids, including unequal spacing and uneven/decreasing slice offsets

Zero coordinate difference from the matrix calculation; every dose value retains its physical voxel

800 seeded perturbed grid pairs

The 0.01 mm equality decision at 658c4b6f0 agrees with exhaustive 3D voxel displacement for exact cardinal directions; the committed regression checks the final comparison, including rounded cosines and both physical limits

IEC FIXED on those 800 grids

Exactly equal to the original implementation

Negative control using the original dose conversion

New physical-grid tests detect all 15 changed encoding cases; ordinary HFS with increasing offsets passes

The randomised probes used seed 2066, 100 grids per orientation, dimensions between 2 and 8, origins within +/-1500 mm, and spacings between 0.2 and 5 mm. The committed seeded matrix regression also checks 100 grids per orientation. These checks are not a statistical estimate of clinical error frequency.

The historical orientation DICOM files have empty pixel data. They verify real metadata and the IEC convention, not nonzero real dose registration. The dose-order and gamma checks deliberately use asymmetric, nonzero synthetic dose data. The downloaded archive matched the repository’s SHA-1 e09f71ebf4d98a58bc3c00e7dd9f59e91e9f8bb6.

Retirement of downloaded test fixtures#

On 26 September 2026 the original tests were rerun before removing their download dependency. The replay used Python 3.12.14, NumPy 1.26.4, pydicom 3.0.2 and pytest 9.1.1, with the existing cached downloads. These are results from the named revisions, not a claim that the old DICOM expectations pass against the corrected implementation.

Revision and test selection

Result

Original code at 400b61fe8, test_coords.py and test_dose.py

7 passed, 1 skipped

PR at 97b086739, the same two files before retiring downloads

14 passed

The original IEC PATIENT test was already unconditionally skipped because its assertions failed; it was not a passing validation. The original DICOM coordinate JSON matched the original implementation in all eight orientations. On the PR, that JSON agrees only for HFS: the other orientations were deliberately checked against the independent DICOM matrix instead. The IEC FIXED JSON, wedge dose baseline, non-square-pixel contour test and patient-position checks passed without changing their expectations.

The precise original tests remain available in Git history: original coordinate tests, original dose tests, and PR coordinate tests before removal. To replay either historical run in a checkout of that revision, use:

uv run -- python -m pytest -q \
  lib/pymedphys/tests/dicom/test_coords.py \
  lib/pymedphys/tests/dicom/test_dose.py -ra

Fixture provenance is retained for that optional replay:

Fixture

Source

Repository SHA-1

dicom_dose_test_data.zip

Zenodo record 3870436

e09f71ebf4d98a58bc3c00e7dd9f59e91e9f8bb6

rtdose_non_square_pixels.dcm

Pinned data-repository file

9d59aafe2f2f2d3b706c092e62ff90bc33430e14

example_structures.dcm

Zenodo record 3576026

3600a63d8f29f2b6b42dff37f280f3d45aff2a32

The roughly 75 MiB dose archive and separate DICOM files are no longer downloaded by these coordinate and dose tests. Their useful coverage is retained locally:

Retired downloaded check

Ongoing local coverage

DICOM axes for eight orientations

Analytic matrix tests, manually encoded physical grids, and seeded voxel checks

IEC FIXED historical JSON

Independent image-axis projection tests for all eight orientations, with the exact historical agreement recorded above

Non-square-pixel dose/contour example

Unequal row/column spacing in voxel-placement and interpolation tests, plus the local structure-mask regression

Wedge dose values, units and coordinates

A generated asymmetric HFS dose written and read locally in implicit and explicit VR little endian; expected dose values and coordinates are defined independently

Patient-position checks using orientation and structure files

Generated datasets covering all eight orientations, with and without Patient Position, missing Image Orientation (Patient), and a conflicting Patient Position

This removes recurring vendor-file regression coverage from this test group; the recorded historical pass is evidence for those revisions, not a substitute for future testing. Local DICOM serialisation retains file-reading coverage, but it cannot represent every vendor-specific encoding. Other repository test groups may still download their own data. The fixture URL/hash registry is retained so historical tests can be replayed.

Remaining limitations#

  • Oblique geometry is outside the three-independent-patient-axes contract. The private IEC PATIENT option of xyz_axes_from_dataset raises NotImplementedError.

  • When this record was written, gamma could miss singleton evaluation planes (#2070) and evaluation grids narrower than one search step, and four strict expected-failure tests recorded this. Both have since been fixed: #2119 gives every analysed reference point a value, and #2126 searches singleton evaluation axes directly. Those four tests now pass as ordinary tests.

Reproducing the committed checks#

After the usual locked development installation, run from the repository root:

uv run -- python -m pytest -q \
  lib/pymedphys/tests/dicom/test_coords.py \
  lib/pymedphys/tests/dicom/test_coords_analytic.py \
  lib/pymedphys/tests/dicom/test_orientation_invariance.py \
  lib/pymedphys/tests/dicom/test_dose.py \
  lib/pymedphys/tests/dicom/structure/test_masking.py \
  lib/pymedphys/tests/gamma/test_gamma_axes.py \
  lib/pymedphys/tests/gamma/test_gamma_shell.py \
  lib/pymedphys/tests/interp/test_interp.py

The selection uses only generated local fixtures, needs no network access or downloaded data, and writes generated DICOM files only to pytest’s temporary directories. The four historical gamma-search expected failures above now run as ordinary passing regression tests.

get_dose_grid_structure_mask and DicomDose.coords follow the pixel array’s (slice, row, column) order in every supported orientation. Their tests cover all eight orientations, both slice orders, and square and rectangular grids, and check that find_dose_within_structure selects the same dose in every orientation. With the previous helpers, which treated rows as y and columns as x, exactly the 32 decubitus cases fail.

Gamma performance is a separate experiment; see the workstation study and upload instructions and the recorded fixed-grid demonstration.