From e0d463db1a500764ce57b815a1b94e23d9d62ced Mon Sep 17 00:00:00 2001 From: Tamino Huxohl <thuxohl@techfak.uni-bielefeld.de> Date: Wed, 14 Dec 2022 13:55:12 +0100 Subject: [PATCH] move function of data/dicom_util to file/dicom --- mu_map/data/dicom_util.py | 50 ----------------------- mu_map/data/prepare.py | 2 +- mu_map/file/dicom.py | 85 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 81 insertions(+), 56 deletions(-) delete mode 100644 mu_map/data/dicom_util.py diff --git a/mu_map/data/dicom_util.py b/mu_map/data/dicom_util.py deleted file mode 100644 index a627857..0000000 --- a/mu_map/data/dicom_util.py +++ /dev/null @@ -1,50 +0,0 @@ -from datetime import datetime -from enum import Enum - -import pydicom - - -class DICOMTime(Enum): - Study = 1 - Series = 2 - Acquisition = 3 - Content = 4 - - def date_field(self): - return f"{self.name}Date" - - def time_field(self): - return f"{self.name}Time" - - def to_datetime(self, dicom: pydicom.dataset.FileDataset) -> datetime: - _date = dicom[self.date_field()].value - _time = dicom[self.time_field()].value - return datetime( - year=int(_date[0:4]), - month=int(_date[4:6]), - day=int(_date[6:8]), - hour=int(_time[0:2]), - minute=int(_time[2:4]), - second=int(_time[4:6]), - # microsecond=int(_time.split(".")[1]), - ) - - -def parse_age(patient_age: str) -> int: - """ - Parse an age string as defined in the DICOM standard into an integer representing the age in years. - - :param patient_age: age string as defined in the DICOM standard - :return: the age in years as a number - """ - assert ( - type(patient_age) == str - ), f"patient age needs to be a string and not {type(patient_age)}" - assert ( - len(patient_age) == 4 - ), f"patient age [{patient_age}] has to be four characters long" - _num, _format = patient_age[:3], patient_age[3] - assert ( - _format == "Y" - ), f"currently, only patient ages in years [Y] is supported, not [{_format}]" - return int(_num) diff --git a/mu_map/data/prepare.py b/mu_map/data/prepare.py index 702769e..58b89a8 100644 --- a/mu_map/data/prepare.py +++ b/mu_map/data/prepare.py @@ -8,7 +8,7 @@ import numpy as np import pandas as pd import pydicom -from mu_map.data.dicom_util import DICOMTime, parse_age +from mu_map.file.dicom import DICOMTime, parse_age from mu_map.logging import add_logging_args, get_logger_by_args diff --git a/mu_map/file/dicom.py b/mu_map/file/dicom.py index 10f4084..2fed122 100644 --- a/mu_map/file/dicom.py +++ b/mu_map/file/dicom.py @@ -1,3 +1,5 @@ +from datetime import datetime +from enum import Enum import random from typing import Tuple @@ -29,6 +31,69 @@ Default extension of dicom files. EXTENSION_DICOM = ".dcm" +class DICOMTime(Enum): + """ + Class for parsing dates and times defined in a DICOM header + into a python datetime type. It maps the four datetime fields + [Study, Series, Acquisition, Content] of the DICOM header into + an enumeration type. + + Usage: DICOMTime.Series.to_datetime(dcm) + """ + + Study = 1 + Series = 2 + Acquisition = 3 + Content = 4 + + def date_field(self) -> str: + """ + Get the name of the date field according to this DICOMTime type. + """ + return f"{self.name}Date" + + def time_field(self) -> str: + """ + Get the name of the time field according to this DICOMTime type. + """ + return f"{self.name}Time" + + def to_datetime(self, dicom: dcm_type) -> datetime: + """ + Get the datetime according to this DICOMTime type. + """ + _date = dicom[self.date_field()].value + _time = dicom[self.time_field()].value + return datetime( + year=int(_date[0:4]), + month=int(_date[4:6]), + day=int(_date[6:8]), + hour=int(_time[0:2]), + minute=int(_time[2:4]), + second=int(_time[4:6]), + ) + + +def parse_age(patient_age: str) -> int: + """ + Parse an age string as defined in the DICOM standard into an integer representing the age in years. + + :param patient_age: age string as defined in the DICOM standard + :return: the age in years as a number + """ + assert ( + type(patient_age) == str + ), f"patient age needs to be a string and not {type(patient_age)}" + assert ( + len(patient_age) == 4 + ), f"patient age [{patient_age}] has to be four characters long" + _num, _format = patient_age[:3], patient_age[3] + assert ( + _format == "Y" + ), f"currently, only patient ages in years [Y] is supported, not [{_format}]" + return int(_num) + + def load_dcm(filename: str) -> Tuple[pydicom.dataset.FileDataset, np.ndarray]: """ Load a DICOM image, the data as a numpy array and apply normalization of the Siemens SPECT/CT @@ -69,7 +134,7 @@ def scale_image(image: np.ndarray, initial_scale=10000000) -> Tuple[np.ndarray, is smaller than the maximum uint16 number :return: the image scaled and converted to uint16 as well as the used scaling factor """ - if image.min() < 0.0: + if image.min() < 0.0: raise ValueError("Cannot scale images with negative values!") scale = initial_scale @@ -121,15 +186,25 @@ def change_uid(dcm: pydicom.dataset.FileDataset) -> pydicom.dataset.FileDataset: :param dcm: the DICOM file to be udpated :return: the DICOM file with updated UIDs """ - dcm.SeriesInstanceUID = UID_PREFIX + str(random.randint(10000000000000, 99999999999999)) - dcm.SOPInstanceUID = UID_PREFIX + str(random.randint(10000000000000, 99999999999999)) + dcm.SeriesInstanceUID = UID_PREFIX + str( + random.randint(10000000000000, 99999999999999) + ) + dcm.SOPInstanceUID = UID_PREFIX + str( + random.randint(10000000000000, 99999999999999) + ) return dcm + if __name__ == "__main__": import argparse - parser = argparse.ArgumentParser(description="Dump the header of a DICOM image", formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument("file", type=str, help="the DICOM file of which the header is dumped") + parser = argparse.ArgumentParser( + description="Dump the header of a DICOM image", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "file", type=str, help="the DICOM file of which the header is dumped" + ) args = parser.parse_args() dcm = pydicom.dcmread(args.file) -- GitLab