Skip to content

Commit

Permalink
Add tests for keep_all_tags option.
Browse files Browse the repository at this point in the history
Fix pydicom value represetations to string.
  • Loading branch information
blowekamp committed Oct 2, 2024
1 parent dcf52ac commit 3ba0eb1
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 12 deletions.
22 changes: 21 additions & 1 deletion rap_sitkcore/_dicom_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def convert_mv_ds_to_float_list(rep: str, vm: int = 0) -> List[float]:

def convert_float_list_to_mv_ds(value: List[float]) -> str:
"""
Convert a iterable of float to the DICOM mutli-value representation for decimal string (DS).
Convert a iterable of float to the DICOM multi-value representation for decimal string (DS).
This method is intended to convert the pydicom MV DS data elements to the representation that GDCM produced for
SimpleITK.
Expand All @@ -42,6 +42,26 @@ def convert_float_list_to_mv_ds(value: List[float]) -> str:
return rep


def convert_int_list_to_mv_ds(value: List[float]) -> str:
"""
Convert a iterable of int to the DICOM multi-value representation for (unsigned) integer (US/IS).
This method is intended to convert the pydicom MV DS data elements to the representation that GDCM produced for
SimpleITK.
:param value: an iterable or list like object of convertable to float values.
:returns: The value encode in for DICOM representation.
"""

rep = _vm_delimiter.join([str(int(f)) for f in value])

# DICOM spec
if len(rep) % 2:
rep += " "

return rep


def keyword_to_gdcm_tag(keyword: str) -> str:
"""Converts a DICOM keyword to a DICOM tag formatted as a string to match GDCM representation.
Expand Down
29 changes: 18 additions & 11 deletions rap_sitkcore/read_dcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pydicom
from pathlib import Path
from rap_sitkcore._util import srgb2gray
from rap_sitkcore._dicom_util import convert_float_list_to_mv_ds, keyword_to_gdcm_tag
from rap_sitkcore._dicom_util import convert_float_list_to_mv_ds, convert_int_list_to_mv_ds, keyword_to_gdcm_tag
import logging

_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -37,10 +37,16 @@ def _get_string_representation(de: pydicom.dataelem.DataElement) -> str:
return convert_float_list_to_mv_ds(de.value)
else:
return str(float(de.value))
elif de.VR == "US":
return str(int(de.value))
elif de.VR in ["US", "IS"]:

if de.VM > 1:
return convert_int_list_to_mv_ds(de.value)
else:
assert str(int(de.value)) == str(de.value), f"{de.value} != {int(de.value)}"
return str(int(de.value))

else:
return de.value
return str(de.value)
except (TypeError, ValueError) as e:
raise RuntimeError(
f'"Error parsing data element "{de.name}" with value "{de.value}" '
Expand All @@ -66,6 +72,7 @@ def _read_dcm_pydicom(filename: Path, keep_all_tags: bool = False) -> sitk.Image
elif ds.PhotometricInterpretation in ["YBR_FULL_422", "YBR_FULL", "RGB"]:
if ds.PhotometricInterpretation != "RGB":
from pydicom.pixel_data_handlers.util import convert_color_space

arr = convert_color_space(ds.pixel_array, ds.PhotometricInterpretation, "RGB")

img = sitk.GetImageFromArray(arr, isVector=True)
Expand All @@ -77,6 +84,7 @@ def _read_dcm_pydicom(filename: Path, keep_all_tags: bool = False) -> sitk.Image
for de in ds:
if de.keyword != "PixelData":
key = f"{de.tag.group:04x}|{de.tag.elem:04x}"
# print(f"pydicom tag: {key} = \"{de.value}\" type: {type(de.value)} VR: {de.VR} VM: {de.VM}")
img[key] = _get_string_representation(de)
# iterate through all tags and copy the ones specified in _keyword_to_copy
# to the SimpleITK image
Expand Down Expand Up @@ -152,14 +160,15 @@ def read_dcm(filename: Path, keep_all_tags: bool = False) -> sitk.Image:
if img.GetNumberOfComponentsPerPixel() == 1:
out = img
elif img.GetNumberOfComponentsPerPixel() == 3:

out = srgb2gray(img)

# After converting to grayscale, we need to copy the tags from the original image
# Copy all tags
old_keys = img.GetMetaDataKeys()
if keep_all_tags:
for k in old_keys:
out[k] = img[k]

for k in old_keys:
out[k] = img[k]
else:
raise RuntimeError(f"Unsupported number of components: {img.GetNumberOfComponentsPerPixel()}")

if not keep_all_tags:
old_keys = set(out.GetMetaDataKeys())
Expand All @@ -168,5 +177,3 @@ def read_dcm(filename: Path, keep_all_tags: bool = False) -> sitk.Image:
del out[k]

return out

raise RuntimeError(f"Unsupported number of components: {img.GetNumberOfComponentsPerPixel()}")
68 changes: 68 additions & 0 deletions test/unit/test_read_dcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,74 @@ def test_read_dcm1(test_file, data_paths):
assert k in _white_listed_dicom_tags


@pytest.mark.parametrize(
"test_file,number_of_tags",
[
("1.3.6.1.4.1.25403.163683357445804.11044.20131119114627.12.dcm", 109),
("1.3.6.1.4.1.25403.158515237678667.5060.20130807021253.18.dcm", 33),
("1.2.840.114062.2.192.168.196.13.2015.11.4.13.11.45.13871156.dcm", 37),
("2.25.288816364564751018524666516362407260298.dcm", 15),
("2.25.240995260530147929836761273823046959883.dcm", 15),
("2.25.226263219114459199164755074787420926696.dcm", 15),
("2.25.40537326380965754670062689705190363681.dcm", 15),
("2.25.326714092011492114153708980185182745084.dcm", 15),
("2.25.5871713374023139953558641168991505875.dcm", 15),
("n10.dcm", 30),
("n11.dcm", 30),
("n12.dcm", 30),
("1.2.392.200036.9116.2.5.1.37.2429823676.1495586039.603772.DCM", 94),
("2.25.298570032897489859462791131067889681111.dcm", 15),
("non_square_color.dcm", 15),
("non_square_uint16.dcm", 57),
("square_uint8.dcm", 32),
],
)
def test_read_dcm_pydicom1(test_file, number_of_tags, data_paths):
filename = data_paths[test_file]

required_tags = [
"StudyInstanceUID",
"SeriesInstanceUID",
"Modality",
]

img = _read_dcm_pydicom(Path(filename))
for tag in required_tags:
key = keyword_to_gdcm_tag(tag)
assert key in img

for k in img.GetMetaDataKeys():
assert k in _white_listed_dicom_tags

img = _read_dcm_pydicom(Path(filename), keep_all_tags=True)

img_keys = set(img.GetMetaDataKeys())

for tag in required_tags:
key = keyword_to_gdcm_tag(tag)
assert key in img

# Check that
assert (
len(img_keys - set(_white_listed_dicom_tags)) == number_of_tags
), f"Expected: {number_of_tags} but got {len(img_keys - set(_white_listed_dicom_tags))}"

img = rap_sitkcore.read_dcm(Path(filename), keep_all_tags=True)

img_keys = set(img.GetMetaDataKeys())

for tag in required_tags:
key = keyword_to_gdcm_tag(tag)
assert key in img

# There are 1-4 different number tags between pydicom and ITK GDCM
# assert len (img_keys - set(_white_listed_dicom_tags)) == number_of_tags+2,\
# f"Expected: {number_of_tags} but got {len(img_keys - set(_whihoyte_listed_dicom_tags))}"

# BUG: Not all these file can be written out
# sitk.WriteImage(img, "foo.dcm")


def test_read_dcm2():
"""Test with filename does not exit"""

Expand Down

0 comments on commit 3ba0eb1

Please sign in to comment.