Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions monai/data/image_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ def _get_affine(self, img, lps_to_ras: bool = True):
affine: np.ndarray = np.eye(sr + 1)
affine[:sr, :sr] = direction[:sr, :sr] @ np.diag(spacing[:sr])
affine[:sr, -1] = origin[:sr]

if lps_to_ras:
affine = orientation_ras_lps(affine)
return affine
Expand Down Expand Up @@ -752,13 +753,25 @@ def _get_affine(self, metadata: dict, lps_to_ras: bool = True):
stacklevel=2,
)
return affine

def _raise_if_not_finite(values: Sequence[Any], tag: str) -> None:
if not np.isfinite(tuple(values)).all():
raise ValueError(
f"PydicomReader: cannot derive affine matrix because DICOM tag {tag} "
f"has a non-finite value: {values}."
)

# "00200037" is the tag of `ImageOrientationPatient`
rx, ry, rz, cx, cy, cz = metadata["00200037"]["Value"]
_raise_if_not_finite((rx, ry, rz, cx, cy, cz), "ImageOrientationPatient (0020,0037)")
# "00200032" is the tag of `ImagePositionPatient`
sx, sy, sz = metadata["00200032"]["Value"]
_raise_if_not_finite((sx, sy, sz), "ImagePositionPatient (0020,0032)")
# "00280030" is the tag of `PixelSpacing`
spacing = metadata["00280030"]["Value"] if "00280030" in metadata else (1.0, 1.0)
_raise_if_not_finite(tuple(spacing), "PixelSpacing (0028,0030)")
dr, dc = metadata.get("spacing", spacing)[:2]
_raise_if_not_finite((dr, dc), "spacing")
affine[0, 0] = cx * dr
affine[0, 1] = rx * dc
affine[0, 3] = sx
Expand All @@ -773,12 +786,16 @@ def _get_affine(self, metadata: dict, lps_to_ras: bool = True):
# 3d
if "lastImagePositionPatient" in metadata:
t1n, t2n, t3n = metadata["lastImagePositionPatient"]
_raise_if_not_finite((t1n, t2n, t3n), "lastImagePositionPatient")
n = metadata[MetaKeys.SPATIAL_SHAPE][-1]
if n > 1:
affine[0, 2] = (t1n - sx) / (n - 1)
affine[1, 2] = (t2n - sy) / (n - 1)
affine[2, 2] = (t3n - sz) / (n - 1)

if not np.isfinite(affine).all():
raise ValueError("PydicomReader: affine matrix not finite after composition.")

if lps_to_ras:
affine = orientation_ras_lps(affine)
return affine
Expand Down
68 changes: 68 additions & 0 deletions tests/data/test_pydicom_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import numpy as np

from monai.data import PydicomReader
from monai.utils import MetaKeys
from tests.test_utils import SkipIfNoModule


Expand All @@ -39,6 +40,73 @@ def test_partial_orientation_tags_warns(self):
affine = reader._get_affine(metadata)
np.testing.assert_array_equal(affine, np.eye(4))

def test_non_finite_pixel_spacing_raises(self):
Comment thread
ericspod marked this conversation as resolved.
reader = PydicomReader()
metadata = {
"00200037": {"Value": [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]},
"00200032": {"Value": [0.0, 0.0, 0.0]},
"00280030": {"Value": [np.nan, 1.0]},
}
with self.assertRaisesRegex(ValueError, "PixelSpacing"):
reader._get_affine(metadata, lps_to_ras=False)

def test_non_finite_image_position_raises(self):
reader = PydicomReader()
metadata = {
"00200037": {"Value": [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]},
"00200032": {"Value": [np.inf, 0.0, 0.0]},
"00280030": {"Value": [1.0, 1.0]},
}
with self.assertRaisesRegex(ValueError, "ImagePositionPatient"):
reader._get_affine(metadata, lps_to_ras=False)

def test_finite_values_return_affine(self):
reader = PydicomReader()
metadata = {
"00200037": {"Value": [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]},
"00200032": {"Value": [10.0, 20.0, 30.0]},
"00280030": {"Value": [0.5, 0.25]},
}
affine = reader._get_affine(metadata, lps_to_ras=False)
self.assertEqual(affine.shape, (4, 4))
self.assertTrue(np.all(np.isfinite(affine)))
np.testing.assert_allclose(affine[0, 3], 10.0)
np.testing.assert_allclose(affine[1, 3], 20.0)
np.testing.assert_allclose(affine[2, 3], 30.0)

def test_non_finite_orientation_raises(self):
reader = PydicomReader()
metadata = {
"00200037": {"Value": [np.nan, 0.0, 0.0, 0.0, 1.0, 0.0]},
"00200032": {"Value": [0.0, 0.0, 0.0]},
"00280030": {"Value": [1.0, 1.0]},
}
with self.assertRaisesRegex(ValueError, "ImageOrientationPatient"):
reader._get_affine(metadata, lps_to_ras=False)

def test_non_finite_last_image_position_raises(self):
reader = PydicomReader()
metadata = {
"00200037": {"Value": [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]},
"00200032": {"Value": [0.0, 0.0, 0.0]},
"00280030": {"Value": [1.0, 1.0]},
"lastImagePositionPatient": [0.0, 0.0, np.inf],
MetaKeys.SPATIAL_SHAPE: [1, 1, 2],
}
with self.assertRaisesRegex(ValueError, "lastImagePositionPatient"):
reader._get_affine(metadata, lps_to_ras=False)

def test_overflow_from_finite_inputs_raises(self):
# Finite inputs whose product overflows produce a non-finite affine.
reader = PydicomReader()
metadata = {
"00200037": {"Value": [1e308, 0.0, 0.0, 1e308, 0.0, 0.0]},
"00200032": {"Value": [0.0, 0.0, 0.0]},
"00280030": {"Value": [1e308, 1e308]},
}
with self.assertRaisesRegex(ValueError, "not finite"):
reader._get_affine(metadata, lps_to_ras=False)


if __name__ == "__main__":
unittest.main()
Loading