Source code for pytomography.io.shared.dicom_creation
import datetime
import pytomography
from pydicom.uid import generate_uid
from pydicom.dataset import Dataset, FileDataset, FileMetaDataset
from pydicom.sequence import Sequence
from pydicom.uid import ImplicitVRLittleEndian
from pydicom.uid import PYDICOM_IMPLEMENTATION_UID
[docs]def get_file_meta(SOP_instance_UID: str, SOP_class_UID: str) -> FileMetaDataset:
"""Creates DICOM file metadata given an SOP instance and class UID.
Args:
SOP_instance_UID (str): Identifier unique to each DICOM file
SOP_class_UID (str): Identifier specifying imaging modality
Returns:
FileMetaDataset: Metadata for DICOM file
"""
file_meta = FileMetaDataset()
file_meta.FileMetaInformationGroupLength = 202
file_meta.FileMetaInformationVersion = b"\x00\x01"
file_meta.TransferSyntaxUID = ImplicitVRLittleEndian
#file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.128'
file_meta.MediaStorageSOPClassUID = SOP_class_UID
file_meta.MediaStorageSOPInstanceUID = (SOP_instance_UID)
file_meta.ImplementationClassUID = PYDICOM_IMPLEMENTATION_UID
return file_meta
[docs]def generate_base_dataset(SOP_instance_UID: str, SOP_class_UID: str) -> FileDataset:
"""Generates a base dataset with the minimal number of required parameters
Args:
SOP_instance_UID (str): Identifier unique to each DICOM file
SOP_class_UID (str): Identifier specifying imaging modality
Returns:
FileDataset: DICOM dataset
"""
file_name = "pydicom-reconstruction"
file_meta = get_file_meta(SOP_instance_UID, SOP_class_UID)
ds = FileDataset(file_name, {}, file_meta=file_meta, preamble=b"\0" * 128)
add_required_elements_to_ds(ds)
return ds
[docs]def add_required_elements_to_ds(ds: FileDataset) -> None:
"""Adds elements to dataset including timing and manufacturer details
Args:
ds (FileDataset): DICOM dataset that will be updated
"""
dt = datetime.datetime.now()
# Append data elements required by the DICOM standarad
ds.SpecificCharacterSet = "ISO_IR 100"
ds.InstanceCreationDate = dt.strftime("%Y%m%d")
ds.InstanceCreationTime = dt.strftime("%H%M%S.%f")
ds.Manufacturer = "Qurit"
ds.ManufacturerModelName = f"PyTomography {pytomography.__version__}"
ds.InstitutionName = "Qurit"
# Set the transfer syntax
ds.is_little_endian = True
ds.is_implicit_VR = True
# Set values already defined in the file meta
ds.SOPClassUID = ds.file_meta.MediaStorageSOPClassUID
ds.SOPInstanceUID = ds.file_meta.MediaStorageSOPInstanceUID
ds.ApprovalStatus = "UNAPPROVED"
[docs]def add_study_and_series_information(ds: FileDataset, reference_ds: FileDataset) -> None:
"""Adds study and series information to dataset based on reference dataset
Args:
ds (FileDataset): Dataset for which to add headers
reference_ds (FileDataset): Dataset from which to copy headers
"""
ds.StudyDate = reference_ds.StudyDate
ds.SeriesDate = getattr(reference_ds, "SeriesDate", "")
ds.StudyTime = reference_ds.StudyTime
ds.SeriesTime = getattr(reference_ds, "SeriesTime", "")
ds.StudyDescription = getattr(reference_ds, "StudyDescription", "")
ds.SeriesDescription = getattr(reference_ds, "SeriesDescription", "")
ds.StudyInstanceUID = reference_ds.StudyInstanceUID
ds.SeriesInstanceUID = generate_uid() # TODO: find out if random generation is ok
ds.StudyID = reference_ds.StudyID
ds.SeriesNumber = "1" # TODO: find out if we can just use 1 (Should be fine since its a new series)
ds.FrameOfReferenceUID = getattr(reference_ds, 'FrameOfReferenceUID', generate_uid())
[docs]def add_patient_information(ds: FileDataset, reference_ds):
"""Adds patient information to dataset based on reference dataset
Args:
ds (FileDataset): Dataset for which to add headers
reference_ds (FileDataset): Dataset from which to copy headers
"""
ds.PatientName = getattr(reference_ds, "PatientName", "")
ds.PatientID = getattr(reference_ds, "PatientID", "")
ds.PatientBirthDate = getattr(reference_ds, "PatientBirthDate", "")
ds.PatientSex = getattr(reference_ds, "PatientSex", "")
ds.PatientAge = getattr(reference_ds, "PatientAge", "")
ds.PatientSize = getattr(reference_ds, "PatientSize", "")
ds.PatientWeight = getattr(reference_ds, "PatientWeight", "")
[docs]def create_ds(reference_ds: FileDataset, SOP_instance_UID: str, SOP_class_UID: str, modality: str):
"""Creates a new DICOM dataset based on a reference dataset with all required headers. Because this is potentially used to save images corresponding to different modalities, the UIDs must be input arguments to this function. In addition, since some modalities require saving multiple slices whereby ``SOP_instance_UIDs`` may use some convention to specify slice number, these are also input arguments.
Args:
reference_ds (FileDataset): Dataset from which to copy all important headers such as patient information and study UID.
SOP_instance_UID (str): Unique identifier for the particular instance (this is different for every DICOM file created)
SOP_class_UID (str): Unique identifier for the imaging modality
modality (str): String specifying imaging modality
Returns:
_type_: _description_
"""
ds = generate_base_dataset(SOP_instance_UID, SOP_class_UID)
add_study_and_series_information(ds, reference_ds)
add_patient_information(ds, reference_ds)
ds.Modality = modality
return ds