Example #1
0
def add_to_roi(rtss, roi_name, roi_coordinates, data_set):
    """
        Add new contour image sequence ROI to rtss

        :param rtss: dataset of RTSS
        :param roi_name: ROIName
        :param roi_coordinates: Coordinates of pixels for new ROI
        :param data_set: Data Set of selected DICOM image file
        :return: rtss, with added ROI
    """

    # Creating a new ROIContourSequence, ContourSequence, ContourImageSequence
    contour_sequence = Sequence([Dataset()])
    contour_image_sequence = Sequence([Dataset()])

    number_of_contour_points = len(roi_coordinates) / 3
    referenced_sop_class_uid = data_set.SOPClassUID
    referenced_sop_instance_uid = data_set.SOPInstanceUID

    existing_roi_number = None
    for item in rtss["StructureSetROISequence"]:
        if item.ROIName == roi_name:
            existing_roi_number = item.ROINumber

    position = None

    # Get the index of the ROI
    for index, contour in enumerate(rtss.ROIContourSequence):
        if contour.ReferencedROINumber == existing_roi_number:
            position = index

    new_contour_number = len(rtss.ROIContourSequence[position].ContourSequence) + 1

    # ROI Sequence
    for contour in contour_sequence:
        # if data_set.get("ReferencedImageSequence"):
        contour.add_new(Tag("ContourImageSequence"), "SQ", contour_image_sequence)

        # Contour Sequence
        for contour_image in contour_image_sequence:
            contour_image.add_new(Tag("ReferencedSOPClassUID"), "UI",
                                    referenced_sop_class_uid)  # CT Image Storage
            contour_image.add_new(Tag("ReferencedSOPInstanceUID"), "UI", referenced_sop_instance_uid)

        
        contour.add_new(Tag("ContourNumber"), "IS", new_contour_number)
        if not _is_closed_contour(roi_coordinates):
            contour.add_new(Tag("ContourGeometricType"), "CS", "OPEN_PLANAR")
            contour.add_new(Tag("NumberOfContourPoints"), "IS", number_of_contour_points)                
            contour.add_new(Tag("ContourData"), "DS", roi_coordinates)
        else:
            contour.add_new(Tag("ContourGeometricType"), "CS", "CLOSED_PLANAR")
            contour.add_new(Tag("NumberOfContourPoints"), "IS", number_of_contour_points-1)                
            contour.add_new(Tag("ContourData"), "DS", roi_coordinates[0:-3])

    rtss.ROIContourSequence[position].ContourSequence.extend(contour_sequence)

    return rtss
Example #2
0
File: util.py Project: dcmq/pydcmq
def datasetFromJSON(data: dict):
    #return Dataset.from_json(data)
    ds = Dataset()
    for key in data.keys():
        tag = Tag(key)
        try:
            if 'Value' in data[key].keys():
                if data[key]['vr'] == 'SQ':
                    tempds = []
                    for subdata in data[key]['Value']:
                        tempds.append(datasetFromJSON(subdata))
                    seq = Sequence(tempds)
                    ds[key] = DataElement(tag, data[key]['vr'], seq)
                elif type(
                        data[key]['Value'][0]
                ) == dict and 'Alphabetic' in data[key]['Value'][0].keys():
                    ds[key] = DataElement(tag, data[key]['vr'],
                                          data[key]['Value'][0]['Alphabetic'])
                else:
                    if len(data[key]['Value']) > 1:
                        ds[key] = DataElement(tag, data[key]['vr'],
                                              data[key]['Value'])
                    else:
                        ds[key] = DataElement(tag, data[key]['vr'],
                                              data[key]['Value'][0])
            else:
                ds[key] = DataElement(tag, data[key]['vr'], '')
        except:
            from IPython import embed
            embed()
    return ds
Example #3
0
    def __init__(self, suid, split_num=0):
        # Init dataset list and the callback
        self._datasets = Sequence()
        self._filenames = []

        # Init props
        self._suid = suid
        self._info = None
        self._shape = None
        self._sampling = None
        self._split_num = split_num
Example #4
0
def generate_CRPES_sequence(dcm_files):
    """Helper function to generate a Current Requested Procedure Evidence Sequence 
    with required DICOM attributes from a list of DICOM objects
    Parameters
    ----------
    dcm_files : List of DICOM objects that are to be referenced in the
                Current Requested Procedure Evidence Sequence
    """
    sequence_content = dict()
    for dcm_file in dcm_files:
        ds = read_file(dcm_file)
        if ds.StudyInstanceUID not in sequence_content:
            sequence_content[ds.StudyInstanceUID] = dict()
        if ds.SeriesInstanceUID not in sequence_content[ds.StudyInstanceUID]:
            sequence_content[ds.StudyInstanceUID][
                ds.SeriesInstanceUID] = list()
        sequence_content[ds.StudyInstanceUID][ds.SeriesInstanceUID].append(
            (ds.SOPClassUID, ds.SOPInstanceUID))
    sequence_data = list()
    for study_instance_uid in sequence_content:
        study_dict = dict()
        study_dict["StudyInstanceUID"] = study_instance_uid
        study_dict["ReferencedSeriesSequence"] = list()
        for series_instance_uid in sequence_content[study_instance_uid]:
            series_dict = dict()
            series_dict["SeriesInstanceUID"] = series_instance_uid
            series_dict["ReferencedSOPSequence"] = list()
            for class_instance_uid_tuple in sequence_content[
                    study_instance_uid][series_instance_uid]:
                instance_dict = dict()
                instance_dict[
                    "ReferencedSOPClassUID"] = class_instance_uid_tuple[0]
                instance_dict[
                    "ReferencedSOPInstanceUID"] = class_instance_uid_tuple[1]
                series_dict["ReferencedSOPSequence"].append(instance_dict)
            study_dict["ReferencedSeriesSequence"].append(series_dict)
        sequence_data.append(study_dict)
    sequence = Sequence()
    for study_item in sequence_data:
        study_ds = Dataset()
        study_ds.StudyInstanceUID = study_instance_uid
        study_ds.ReferencedSeriesSequence = generate_sequence(
            "ReferencedSeriesSequence", study_item["ReferencedSeriesSequence"])
        sequence.append(study_ds)
    return sequence
Example #5
0
 def __init__(self):
     self.sequence = Sequence()
Example #6
0
def generate_dicom_sr(file_path, img_ds, data, series_description):
    """
    Generates DICOM Structured Report files for the given file path.
    :param file_path: the file name and directory to save the DICOM
                      SR file in.
    :param img_ds: A CT or MR image from the dataset used to pull
                   general information for the DICOM SR.
    :param data: Text data to be written to the DICOM SR file.
    :param series_description: Description of text data written to SR.
    :return: dicom_sr, a dataset for the new DICOM SR file.
    """
    if img_ds is None:
        raise ValueError("No CT data to initialize RT SS")

    # Create file meta
    file_meta = FileMetaDataset()
    file_meta.FileMetaInformationGroupLength = 238
    file_meta.FileMetaInformationVersion = b'\x00\x01'
    file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.88.33'
    file_meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid()
    file_meta.TransferSyntaxUID = ImplicitVRLittleEndian
    validate_file_meta(file_meta)

    # Create dataset
    dicom_sr = pydicom.dataset.FileDataset(file_path, {},
                                           preamble=b"\0" * 128,
                                           file_meta=file_meta)
    dicom_sr.fix_meta_info()

    # Get current date and time
    now = datetime.datetime.now()
    dicom_date = now.strftime("%Y%m%d")
    dicom_time = now.strftime("%H%M")

    # List of tags to copy from CT/MR image
    top_level_tags_to_copy: list = [
        Tag("PatientName"),
        Tag("PatientID"),
        Tag("PatientBirthDate"),
        Tag("PatientSex"),
        Tag("StudyDate"),
        Tag("StudyTime"),
        Tag("ReferringPhysicianName"),
        Tag("StudyDescription"),
        Tag("StudyInstanceUID"),
        Tag("StudyID"),
        Tag("RequestingService"),
        Tag("PatientAge"),
        Tag("PatientSize"),
        Tag("PatientWeight"),
        Tag("MedicalAlerts"),
        Tag("Allergies"),
        Tag("PregnancyStatus"),
        Tag("InstitutionName"),
        Tag("InstitutionAddress")
    ]

    # Copy tags from CT/MR image
    for tag in top_level_tags_to_copy:
        if tag in img_ds:
            dicom_sr[tag] = deepcopy(img_ds[tag])

    dicom_sr.AccessionNumber = ""

    # == SR Document Series Module
    dicom_sr.SeriesDate = dicom_date
    dicom_sr.SeriesTime = dicom_time
    dicom_sr.Modality = "SR"
    dicom_sr.SeriesDescription = series_description
    # Can be empty
    referenced_performed_procedure_step_sequence = Sequence()
    dicom_sr.ReferencedPerformedProcedureStepSequence = \
        referenced_performed_procedure_step_sequence
    dicom_sr.SeriesInstanceUID = pydicom.uid.generate_uid()
    dicom_sr.SeriesNumber = 1

    # == General Equipment Module
    dicom_sr.Manufacturer = "OnkoDICOM"
    dicom_sr.ManufacturersModelName = "OnkoDICOM"
    # TODO: Pull this off build information in some way
    dicom_sr.SoftwareVersions = "2021"

    # == SR Document General Module
    dicom_sr.ContentDate = dicom_date
    dicom_sr.ContentTime = dicom_time

    dicom_sr.InstanceNumber = 1

    # Empty if unknown
    performed_procedure_code_sequence = Sequence()

    dicom_sr.PerformedProcedureCodeSequence = performed_procedure_code_sequence

    # Do not want to mark as complete in case it isn't!
    dicom_sr.CompletionFlag = "PARTIAL"
    dicom_sr.VerificationFlag = "UNVERIFIED"

    # == SR Document Content Module
    referenced_sop_sequence = Sequence([Dataset()])
    referenced_sop_sequence[0].ReferencedSOPClassUID = ''
    referenced_sop_sequence[0].ReferencedSOPInstanceUID = ''

    dicom_sr.ReferencedSOPSequence = referenced_sop_sequence
    dicom_sr.ValueType = "CONTAINER"

    dicom_sr.ContinuityOfContent = "CONTINUOUS"
    dicom_sr.TemporalRangeTime = ""
    dicom_sr.ReferencedTimeOffsets = ""
    dicom_sr.ReferencedDateTime = ""

    dicom_sr.MeasuredValueSequence = Sequence()
    og_frame_of_reference_UID = \
        deepcopy(img_ds[Tag("FrameOfReferenceUID")].value)
    dicom_sr.ReferencedFrameOfReferenceUID = og_frame_of_reference_UID

    # == Content Sequence
    content_sequence = Sequence([Dataset()])
    content_sequence[0].RelationshipType = 'CONTAINS'
    content_sequence[0].ValueType = 'TEXT'

    concept_name_code_sequence = Sequence([Dataset()])
    concept_name_code_sequence[0].CodeValue = ''
    concept_name_code_sequence[0].CodingSchemeDesignator = ''
    concept_name_code_sequence[0].CodeMeaning = ''
    content_sequence[0].ConceptNameCodeSequence = concept_name_code_sequence

    content_sequence[0].TextValue = data

    dicom_sr.ContentSequence = content_sequence

    # == SOP Common Module
    dicom_sr.SOPClassUID = '1.2.840.10008.5.1.4.1.1.88.33'
    dicom_sr.SOPInstanceUID = file_meta.MediaStorageSOPInstanceUID

    dicom_sr.is_little_endian = True
    dicom_sr.is_implicit_VR = True

    return dicom_sr
    def createStructDS(self, planData, ImageInfoUIDs, setupPosition,
                       roiShiftVector):
        print("Creating Data structure")
        # create RS SOPInstanceUID
        structSOPInstanceUID = pydicom.uid.generate_uid()
        structSeriesInstanceUID = pydicom.uid.generate_uid()

        # get image header info from ImageSet_0.ImageInfo
        structFrameUID = ''
        structStudyInstanceUID = ''
        structSeriesUID = ''
        structClassUID = '1.2.840.10008.5.1.4.1.1.481.3'
        if "ImageInfoList" in ImageInfoUIDs:
            structFrameUID = ImageInfoUIDs.ImageInfoList[0].FrameUID
            structStudyInstanceUID = ImageInfoUIDs.ImageInfoList[
                0].StudyInstanceUID
            structSeriesUID = ImageInfoUIDs.ImageInfoList[0].SeriesUID
            # structClassUID = ImageInfoUIDs.ImageInfoList[0].ClassUID

        # Populate required values for file meta information
        file_meta = Dataset()
        # RT Structure Set Storage
        file_meta.MediaStorageSOPClassUID = structClassUID
        file_meta.MediaStorageSOPInstanceUID = structSOPInstanceUID
        structfilename = "RS." + structSOPInstanceUID + ".dcm"
        # this value remains static since implementation for creating file is the same
        file_meta.ImplementationClassUID = '1.2.826.0.1.3680043.8.498.75006884747854523615841001'
        # Create the FileDataset instance (initially no data elements, but file_meta supplied)
        ds = FileDataset(structfilename, {},
                         file_meta=file_meta,
                         preamble=b'\x00' * 128)
        # print(file_meta.preamble)

        # add info_data,basic patientinfo
        # [0008,0005] - [0008,0018]
        ds.SpecificCharacterSet = 'ISO_IR 100'
        ds.InstanceCreationDate = time.strftime("%Y%m%d")
        ds.InstanceCreationTime = time.strftime("%H%M%S")
        ds.SOPClassUID = structClassUID
        ds.SOPInstanceUID = structSOPInstanceUID
        ds.Modality = 'RTSTRUCT'
        ds.AccessionNumber = ""
        ds.Manufacturer = 'Pinnalce3'  # from sample dicom file, maybe should change?
        # not sure where to get information for this element can find this and read in from
        ds.StationName = "adacp3u7"
        # ds.ManufacturersModelName = 'Pinnacle3'
        ds = self.modifyPatientInfo(ds, planData)

        # [0008,1110]
        ds.ReferencedStudySequence = Sequence()
        ReferencedStudy1 = Dataset()
        ds.ReferencedStudySequence.append(ReferencedStudy1)
        # Study Component Management SOP Class (chosen from template)
        ds.ReferencedStudySequence[
            0].ReferencedSOPClassUID = '1.2.840.10008.3.1.2.3.2'
        ds.ReferencedStudySequence[
            0].ReferencedSOPInstanceUID = structStudyInstanceUID
        # ds.StudyInstanceUID = StudyInstanceUID
        print("Setting structure file study instance: " +
              str(structStudyInstanceUID))
        # [0020,000d]
        ds.StudyInstanceUID = structStudyInstanceUID
        # [0020,000e]
        ds.SeriesInstanceUID = structSeriesInstanceUID

        # [3006,0010]
        ds.ReferencedFrameOfReferenceSequence = Sequence()
        ReferencedFrameofReference1 = Dataset()
        ds.ReferencedFrameOfReferenceSequence.append(
            ReferencedFrameofReference1)
        ds.ReferencedFrameOfReferenceSequence[
            0].FrameofReferenceUID = structFrameUID
        # [3006,0012]
        ds.ReferencedFrameOfReferenceSequence[
            0].RTReferencedStudySequence = Sequence()
        RTReferencedStudy1 = Dataset()
        ds.ReferencedFrameOfReferenceSequence[
            0].RTReferencedStudySequence.append(RTReferencedStudy1)
        ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
            0].ReferencedSOPClassUID = '1.2.840.10008.3.1.2.3.2'
        ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
            0].ReferencedSOPInstanceUID = structStudyInstanceUID
        # ds.StudyInstanceUID = StudyInstanceUID
        # [3006,0014]
        ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
            0].RTReferencedSeriesSequence = Sequence()
        RTReferencedSeries1 = Dataset()
        ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
            0].RTReferencedSeriesSequence.append(RTReferencedSeries1)
        ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
            0].RTReferencedSeriesSequence[
                0].SeriesInstanceUID = structSeriesUID

        # [3006,0016]
        ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
            0].RTReferencedSeriesSequence[0].ContourImageSequence = Sequence()

        # [fffe,e000]
        for i, value in enumerate(ImageInfoUIDs.ImageInfoList, 1):
            exec("ContourImage%d = Dataset()" % i)
            exec(
                "ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[0].RTReferencedSeriesSequence[0].ContourImageSequence.append(ContourImage%d)"
                % i)
            ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
                0].RTReferencedSeriesSequence[0].ContourImageSequence[
                    i - 1].ReferencedSOPClassUID = value.ClassUID
            ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
                0].RTReferencedSeriesSequence[0].ContourImageSequence[
                    i - 1].ReferencedSOPInstanceUID = value.InstanceUID
            # exec("del ContourImage%d" % i)

        # [3006,0020]
        roiListData = planData.planROIsRawData.roiList
        ds.StructureSetROISequence = Sequence()
        for i, value in enumerate(roiListData, 1):
            exec("ROISet%d = Dataset()" % i)
            exec("ds.StructureSetROISequence.append(ROISet%d)" % i)
            ds.StructureSetROISequence[i - 1].ROIName = value.name
            ds.StructureSetROISequence[i - 1].ROINumber = i
            ds.StructureSetROISequence[
                i - 1].ReferencedFrameOfReferenceUID = structFrameUID
            if 'volume' in value:
                ds.StructureSetROISequence[i - 1].ROIVolume = value.volume
            ds.StructureSetROISequence[
                i - 1].ROIGenerationAlgorithm = value.roiinterpretedtype

        # [3006,0039]get each ROI
        ds.ROIContourSequence = Sequence()
        for i, value in enumerate(roiListData, 1):
            exec("ContourSequence%d = Dataset()" % i)
            exec("ds.ROIContourSequence.append(ContourSequence%d)" % i)
            ds.ROIContourSequence[i - 1].ROIDisplayColor = [0, 255, 0]
            ds.ROIContourSequence[i - 1].ReferencedROINumber = i

            # get all curves in current ROI
            ds.ROIContourSequence[i - 1].ContourSequence = Sequence()
            planROIsCurvesList = value.num_curve
            # get each ROI_Curvers
            for j, data in enumerate(planROIsCurvesList, 1):
                exec("CurvesPoint%d = Dataset()" % j)
                exec(
                    "ds.ROIContourSequence[i - 1].ContourSequence.append(CurvesPoint%d)"
                    % j)
                # [3006,0040]
                ds.ROIContourSequence[i - 1].ContourSequence[
                    j - 1].ContourImageSequence = Sequence()
                coutourImage1 = Dataset()
                ds.ROIContourSequence[i - 1].ContourSequence[
                    j - 1].ContourImageSequence.append(coutourImage1)
                ds.ROIContourSequence[i - 1].ContourSequence[
                    j - 1].ContourImageSequence[
                        0].ReferencedSOPClassUID = structClassUID
                ds.ROIContourSequence[i - 1].ContourSequence[
                    j - 1].ContourImageSequence[
                        0].ReferencedSOPInstanceUID = self.getCTInstanceUID(
                            data.Points[0], setupPosition, ImageInfoUIDs)

                # [3006,0042]
                ds.ROIContourSequence[i - 1].ContourSequence[
                    j - 1].ContourGeometricType = "CLOSED_PLANAR"
                ds.ROIContourSequence[i - 1].ContourSequence[
                    j - 1].NumberOfContourPoints = data.num_points
                # get each ROI_Curves_Points, using data.Points
                ds.ROIContourSequence[i - 1].ContourSequence[
                    j - 1].ContourData = self.getContourCurvePoints(
                        data.Points, setupPosition, roiShiftVector)

        # [3006,0080]
        ds.RTROIObservationsSequence = Sequence()
        for i, current_roi in enumerate(ds.StructureSetROISequence, 1):
            exec("Observation%d = Dataset()" % i)
            exec("ds.RTROIObservationsSequence.append(Observation%d)" % i)
            ds.RTROIObservationsSequence[
                i - 1].ObservationNumber = current_roi.ROINumber
            ds.RTROIObservationsSequence[
                i - 1].ReferencedROINumber = current_roi.ROINumber
            ds.RTROIObservationsSequence[i - 1].RTROIInterpretedType = 'ORGAN'
            ds.RTROIObservationsSequence[i - 1].ROIInterpreter = ""

        # find out where to get if its been approved or not
        ds.ApprovalStatus = 'UNAPPROVED'
        # Set the transfer syntax
        ds.is_little_endian = True
        ds.is_implicit_VR = True

        # # Create the FileDataset instance (initially no data elements, but file_meta supplied)
        # structds = FileDataset(structfilename, {},
        #                  file_meta=ds, preamble=b'\x00' * 128)

        # structfilepath=outputfolder + patientfolder + "/" + structfilename
        # structds.save_as("structfilepath")
        # print("Structure file being saved\n")
        ds.save_as(os.getenv('HOME') + '/PinnWork/' + structfilename)
        #
        # dcmds = pydicom.dcmread(ds)
        # print(dcmds)
        return ds
Example #8
0
def create_roi(rtss, roi_name, roi_coordinates, data_set, rt_roi_interpreted_type="ORGAN"):
    """
        Create new ROI to rtss

        :param rtss: dataset of RTSS
        :param roi_name: ROIName
        :param roi_coordinates: Coordinates of pixels for new ROI
        :param data_set: Data Set of selected DICOM image file
        :return: rtss, with added ROI
        """

    patient_dict_container = PatientDictContainer()
    existing_rois = patient_dict_container.get("rois")
    roi_exists = False

    # This is for adding a new slice to an already existing ROI. For Future Development.
    # Check to see if the ROI already exists
    for key, value in existing_rois.items():
        if value["name"] == roi_name:
            roi_exists = True

    if not roi_exists:
        number_of_contour_points = len(roi_coordinates) / 3
        referenced_sop_class_uid = data_set.SOPClassUID
        referenced_sop_instance_uid = data_set.SOPInstanceUID

        referenced_frame_of_reference_uid = rtss["StructureSetROISequence"].value[0].ReferencedFrameOfReferenceUID
        roi_number = rtss["StructureSetROISequence"].value[-1].ROINumber + 1

        # Colour TBC
        red = random.randint(0, 255)
        green = random.randint(0, 255)
        blue = random.randint(0, 255)
        rgb = [red, green, blue]

        # Saving a new StructureSetROISequence
        structure_set_sequence = Sequence([Dataset()])

        original_structure_set = rtss.StructureSetROISequence

        for structure_set in structure_set_sequence:
            structure_set.add_new(Tag("ROINumber"), 'IS', roi_number)
            structure_set.add_new(Tag("ReferencedFrameOfReferenceUID"), 'UI',
                                  referenced_frame_of_reference_uid)
            structure_set.add_new(Tag("ROIName"), 'LO', roi_name)
            structure_set.add_new(Tag("ROIGenerationAlgorithm"), 'CS', "")

        # Combine old and new structure set
        original_structure_set.extend(structure_set_sequence)
        rtss.add_new(Tag("StructureSetROISequence"), "SQ", original_structure_set)

        # Saving a new ROIContourSequence, ContourSequence, ContourImageSequence
        roi_contour_sequence = Sequence([Dataset()])
        contour_sequence = Sequence([Dataset()])
        contour_image_sequence = Sequence([Dataset()])

        # Original File
        original_ROI_contour = rtss.ROIContourSequence

        # ROI Contour Sequence
        for roi_contour in roi_contour_sequence:
            roi_contour.add_new(Tag("ROIDisplayColor"), "IS", rgb)
            roi_contour.add_new(Tag("ContourSequence"), "SQ", contour_sequence)

            # ROI Sequence
            for contour in contour_sequence:
                # if data_set.get("ReferencedImageSequence"):
                contour.add_new(Tag("ContourImageSequence"), "SQ", contour_image_sequence)

                # Contour Sequence
                for contour_image in contour_image_sequence:
                    contour_image.add_new(Tag("ReferencedSOPClassUID"), "UI",
                                            referenced_sop_class_uid)  # CT Image Storage
                    contour_image.add_new(Tag("ReferencedSOPInstanceUID"), "UI", referenced_sop_instance_uid)

                contour.add_new(Tag("ContourNumber"), "IS", 1)
                if not _is_closed_contour(roi_coordinates):
                    contour.add_new(Tag("ContourGeometricType"), "CS", "OPEN_PLANAR")
                    contour.add_new(Tag("NumberOfContourPoints"), "IS", number_of_contour_points)                
                    contour.add_new(Tag("ContourData"), "DS", roi_coordinates)
                else:
                    contour.add_new(Tag("ContourGeometricType"), "CS", "CLOSED_PLANAR")
                    contour.add_new(Tag("NumberOfContourPoints"), "IS", number_of_contour_points-1)                
                    contour.add_new(Tag("ContourData"), "DS", roi_coordinates[0:-3])

            roi_contour.add_new(Tag("ReferencedROINumber"), "IS", roi_number)

        # Combine original ROIContourSequence with new
        original_ROI_contour.extend(roi_contour_sequence)

        rtss.add_new(Tag("ROIContourSequence"), "SQ", original_ROI_contour)

        # Saving a new RTROIObservationsSequence
        RT_ROI_observations_sequence = Sequence([Dataset()])

        original_ROI_observation_sequence = rtss.RTROIObservationsSequence

        for ROI_observations in RT_ROI_observations_sequence:
            # TODO: Check to make sure that there aren't multiple observations per ROI, e.g. increment from existing Observation Numbers?
            ROI_observations.add_new(Tag("ObservationNumber"), 'IS', roi_number)
            ROI_observations.add_new(Tag("ReferencedROINumber"), 'IS', roi_number)
            ROI_observations.add_new(Tag("RTROIInterpretedType"), 'CS', rt_roi_interpreted_type)

        original_ROI_observation_sequence.extend(RT_ROI_observations_sequence)
        rtss.add_new(Tag("RTROIObservationsSequence"), "SQ", original_ROI_observation_sequence)

    else:
        # Add contour image data to existing ROI
        rtss = add_to_roi(rtss, roi_name, roi_coordinates, data_set)

    return rtss
Example #9
0
    def _convert_to_parametric_map_iod(self):

        if self.metadata is None:
            return None

        # Create a copy of the metadata.
        old_dcm: FileDataset = copy.deepcopy(self.metadata)

        # Update the SOP class to that of a parametric map image
        self.set_metadata(tag=(0x0008, 0x0016), value="1.2.840.10008.5.1.4.1.1.30")

        # Update the image type attribute
        image_type = self.get_metadata(tag=(0x0008, 0x0008), tag_type="mult_str", default=[])
        image_type = [image_type[ii] if ii < len(image_type) else "" for ii in range(4)]
        image_type[0] = "DERIVED"
        image_type[1] = "PRIMARY"
        image_type[2] = image_type[2] if not image_type[2] == "" else "STATIC"
        image_type[3] = "MIXED" if self.spat_transform == "base" else "FILTERED"

        self.set_metadata(tag=(0x0008, 0x0008), value=image_type)

        # Parametric Map Image module attributes that may be missing.
        self.cond_set_metadata(tag=(0x2050, 0x0020), value="IDENTITY")  # Presentation LUT shape
        self.cond_set_metadata(tag=(0x0018, 0x9004), value="RESEARCH")  # Content qualification
        self.cond_set_metadata(tag=(0x0028, 0x0301), value="NO")  # Burned-in Annotation
        self.cond_set_metadata(tag=(0x0028, 0x0302), value="YES")  # Recognisable facial features
        self.cond_set_metadata(tag=(0x0070, 0x0080), value=self.get_export_descriptor().upper().strip()[:15])  # Content label
        self.cond_set_metadata(tag=(0x0070, 0x0081), value=self.get_export_descriptor()[:63])  # Content description
        self.cond_set_metadata(tag=(0x0070, 0x0084), value="Doe^John")

        # Set the source instance sequence
        source_instance_list = []
        for reference_instance_sop_uid in self.slice_table.sop_instance_uid:
            ref_inst = Dataset()
            set_pydicom_meta_tag(dcm_seq=ref_inst, tag=(0x0008, 0x1150), value=get_pydicom_meta_tag(dcm_seq=old_dcm, tag=(0x0008, 0x0016), tag_type="str"))
            set_pydicom_meta_tag(dcm_seq=ref_inst, tag=(0x0008, 0x1155), value=reference_instance_sop_uid)

            source_instance_list += [ref_inst]

        self.set_metadata(tag=(0x0008, 0x2112), value=Sequence(source_instance_list))

        # Attributes from the enhanced general equipment module may be missing.
        self.cond_set_metadata(tag=(0x0008, 0x0070), value="unknown")  # Manufacturer
        self.cond_set_metadata(tag=(0x0008, 0x1090), value="unknown")  # Model name
        self.cond_set_metadata(tag=(0x0018, 0x1000), value="unknown")  # Device Serial Number
        self.set_metadata(tag=(0x0018, 0x1020), value="MIRP " + get_version())

        # Items from multi-frame function groups may be missing. We currently only use a single frame.
        self.set_metadata(tag=(0x5200, 0x9229), value=Sequence())  # Shared functional groups sequence
        self.set_metadata(tag=(0x5200, 0x9230), value=Sequence())  # Per-frame functional groups sequence

        # Multi-frame Dimension module

        # Dimension organisation sequence. We copy the frame of reference as UID.
        dim_org_seq_elem = Dataset()
        set_pydicom_meta_tag(dim_org_seq_elem, tag=(0x0020, 0x9164), value=self.get_metadata(tag=(0x0020, 0x0052), tag_type="str"))  # Dimension organisation UID
        self.set_metadata(tag=(0x0020, 0x9221), value=Sequence([dim_org_seq_elem]))

        # Dimension Index sequence. We point to the instance number.
        dim_index_seq_elem = Dataset()
        set_pydicom_meta_tag(dim_index_seq_elem, tag=(0x0020, 0x9165), value=(0x0020, 0x0013))  # Dimension index pointer
        set_pydicom_meta_tag(dim_index_seq_elem, tag=(0x0020, 0x9164), value=self.get_metadata(tag=(0x00200052), tag_type="str"))  # Dimension organisation UID
        self.set_metadata(tag=(0x0020, 0x9222), value=Sequence([dim_index_seq_elem]))
Example #10
0
def add_new_roi(rtss, roi_name, roi_coordinates, data_set,
                rt_roi_interpreted_type):
    """
    Add the information of a new ROI to the rtss
    :param rtss: dataset of RTSS
    :param roi_name: ROIName
    :param roi_coordinates: Coordinates of pixels for new ROI
    :param data_set: data set of selected DICOM image file
    :param rt_roi_interpreted_type: the interpreted type of the new ROI
    :return: rtss, with added ROI
    """
    number_of_contour_points = len(roi_coordinates) / 3
    referenced_sop_class_uid = data_set.SOPClassUID
    referenced_sop_instance_uid = data_set.SOPInstanceUID

    # Check if there is any ROIs in rtss
    if not len(rtss["StructureSetROISequence"].value):
        referenced_frame_of_reference_uid = data_set.FrameOfReferenceUID
        roi_number = 1
    else:
        first_roi_sequence = rtss["StructureSetROISequence"].value[0]
        referenced_frame_of_reference_uid = \
            first_roi_sequence.ReferencedFrameOfReferenceUID
        roi_number = \
            rtss["StructureSetROISequence"].value[-1].ROINumber + 1

    # Colour TBC
    red = random.randint(0, 255)
    green = random.randint(0, 255)
    blue = random.randint(0, 255)
    rgb = [red, green, blue]

    # Saving a new StructureSetROISequence
    structure_set_sequence = Sequence([Dataset()])

    original_structure_set = rtss.StructureSetROISequence

    for structure_set in structure_set_sequence:
        structure_set.add_new(Tag("ROINumber"), 'IS', roi_number)
        structure_set.add_new(Tag("ReferencedFrameOfReferenceUID"), 'UI',
                              referenced_frame_of_reference_uid)
        structure_set.add_new(Tag("ROIName"), 'LO', roi_name)
        structure_set.add_new(Tag("ROIGenerationAlgorithm"), 'CS', "")

    # Combine old and new structure set
    original_structure_set.extend(structure_set_sequence)
    rtss.add_new(Tag("StructureSetROISequence"), "SQ", original_structure_set)

    # Saving a new ROIContourSequence, ContourSequence,
    # ContourImageSequence
    roi_contour_sequence = Sequence([Dataset()])
    contour_sequence = Sequence([Dataset()])
    contour_image_sequence = Sequence([Dataset()])

    # Original File
    original_roi_contour = rtss.ROIContourSequence

    # ROI Contour Sequence
    for roi_contour in roi_contour_sequence:
        roi_contour.add_new(Tag("ROIDisplayColor"), "IS", rgb)
        roi_contour.add_new(Tag("ContourSequence"), "SQ", contour_sequence)

        # ROI Sequence
        for contour in contour_sequence:
            # if data_set.get("ReferencedImageSequence"):
            contour.add_new(Tag("ContourImageSequence"), "SQ",
                            contour_image_sequence)

            # Contour Sequence
            for contour_image in contour_image_sequence:
                contour_image.add_new(
                    Tag("ReferencedSOPClassUID"), "UI",
                    referenced_sop_class_uid)  # CT Image Storage
                contour_image.add_new(Tag("ReferencedSOPInstanceUID"), "UI",
                                      referenced_sop_instance_uid)

            contour.add_new(Tag("ContourNumber"), "IS", 1)
            if not _is_closed_contour(roi_coordinates):
                contour.add_new(Tag("ContourGeometricType"), "CS",
                                "OPEN_PLANAR")
                contour.add_new(Tag("NumberOfContourPoints"), "IS",
                                number_of_contour_points)
                contour.add_new(Tag("ContourData"), "DS", roi_coordinates)
            else:
                contour.add_new(Tag("ContourGeometricType"), "CS",
                                "CLOSED_PLANAR")
                contour.add_new(Tag("NumberOfContourPoints"), "IS",
                                number_of_contour_points - 1)
                contour.add_new(Tag("ContourData"), "DS",
                                roi_coordinates[0:-3])

        roi_contour.add_new(Tag("ReferencedROINumber"), "IS", roi_number)

    # Combine original ROIContourSequence with new
    original_roi_contour.extend(roi_contour_sequence)

    rtss.add_new(Tag("ROIContourSequence"), "SQ", original_roi_contour)

    # Saving a new RTROIObservationsSequence
    rt_roi_observations_sequence = Sequence([Dataset()])

    original_roi_observation_sequence = rtss.RTROIObservationsSequence

    for ROI_observations in rt_roi_observations_sequence:
        # TODO: Check to make sure that there aren't multiple
        #  observations per ROI, e.g. increment from existing
        #  Observation Numbers?
        ROI_observations.add_new(Tag("ObservationNumber"), 'IS', roi_number)
        ROI_observations.add_new(Tag("ReferencedROINumber"), 'IS', roi_number)
        ROI_observations.add_new(Tag("RTROIInterpretedType"), 'CS',
                                 rt_roi_interpreted_type)
        ROI_observations.add_new(Tag("ROIInterpreter"), 'CS', "")

    original_roi_observation_sequence.extend(rt_roi_observations_sequence)
    rtss.add_new(Tag("RTROIObservationsSequence"), "SQ",
                 original_roi_observation_sequence)
    return rtss
Example #11
0
def rtDicomFromPreviousFolder(input_folder,
                              ctr_folder_names,
                              ctr_names,
                              base_img,
                              masks_np,
                              output_file_name,
                              prefix_name=''):
    '''

    :param input_folder: Input folder where the ORIGINAL DICOMS are stored
    :param ctr_folder_names:  Name of folders where to search for contours
    :param ctr_names:  Original contour names (this should have the same order as the contours in masks_np)
    :param base_img:  Itk base image to use for obtaining the positions
    :param masks_np:  This is the numpy 4D binary array with the masks for each contour (ctr, slice, w, h)
    :param prefix_name:  Additional prefix string to use in the new names of the contours
    :return:
    '''

    print('Getting pydicom DataSequence from previous data...')

    # Gets the proper rt_folder to look for contours
    cont_file = getLatestContourFolder(ctr_folder_names, input_folder)
    lstFilesContour = io_com.get_dicom_files_in_folder(
        join(input_folder, cont_file))

    # ===================== Contours ==================
    ds = pydicom.read_file(lstFilesContour[0])  # Reads original dataset

    final_ROIContourSequence_values = [
    ]  # This should be an array of datasets and will be replaced from the original FileDataset

    # Iterate over the ROIContourSequences (ak the contours) Type: Sequence
    for new_idx_ctr, c_ctr_name in enumerate(ctr_names):
        old_idx_ctr = [
            i for i, x in enumerate(ds.StructureSetROISequence)
            if x.ROIName == c_ctr_name
        ]

        if len(old_idx_ctr) == 0:  # If the ROI was not found
            print(
                F'ERROR the contour {c_ctr_name} was not found on previous RTStructure'
            )
        else:
            # If the ROI is found, the we use it as a template and just modify the positions of the contours
            old_idx_ctr = old_idx_ctr[0]  # Get just the index
            ds.StructureSetROISequence[
                new_idx_ctr].ROIName == c_ctr_name  # Use the new name for this index
            ds.StructureSetROISequence[
                new_idx_ctr].ROINumber == new_idx_ctr + 1  # Use the new name for this index
            ds.Manufacturer = 'Deep Learning Biomarkers Group'
            ds.SeriesDate = str(datetime.date.today().strftime('%Y%m%d'))
            ds.SeriesDescription = 'NN Prediction'
            ds.StructureSetName = 'NN Prediction'

            print(F'\t Found {c_ctr_name} with idx {old_idx_ctr}!!!')

            # Copy the ROIContourSequence of the OLD contour (VERY IMPORTANT STEP, we initalize with everything that was there before)
            old_ROIContour_ds = ds.ROIContourSequence[old_idx_ctr]
            ds.StructureSetROISequence[
                old_idx_ctr].ROIName = F'{prefix_name}{c_ctr_name}'

            cur_mask = masks_np[
                new_idx_ctr, :, :, :]  # Get the proper mask data

            slices_w_data = np.unique(np.where(cur_mask > 0)[0])
            cur_ContourSequence_values = []

            # Use the first contour sequence as template (IMPORTANT)
            old_ContourSequence_ds = old_ROIContour_ds.ContourSequence[0]
            # Iterate slices with some contour inside. Transform the mask to contour and add it to the sequence
            for idx_slice, slice in enumerate(slices_w_data):
                # for idx_slice,slice in enumerate([82]):

                # Get a contour from the 2D mask using OpenCV
                ctr_pts = getContourMinPosFrom2DMask(cur_mask[slice, :, :])
                # --- Use to visualize the contoured points for each slice and contour -----------
                # import matplotlib.pyplot as plt
                # plt.imshow(cur_mask[slice,:,:])
                # plt.title(slice)
                # for c_ctr in ctr_pts:
                #     plt.scatter(c_ctr[:,0,0],c_ctr[:,0,1],s=1)
                # plt.show()

                tot_ctrs = len(ctr_pts)
                # Iterate over EACH contour and create a dataset for each of them
                for idx_ctr in range(tot_ctrs):
                    cur_contourdata_ds = Dataset(
                    )  # This is the Contour data for this ContourSequence
                    # Copy the desired values from the OLD ContourSequence
                    cur_contourdata_ds.ContourGeometricType = old_ContourSequence_ds.ContourGeometricType

                    contourdata_values = []

                    tot_pts = ctr_pts[idx_ctr].shape[0]
                    this_slice_indx_pos = []

                    # Add the 'desired' format of (x,y,z) for each contour point
                    cur_ctr_pts = ctr_pts[idx_ctr]
                    for cur_pt in cur_ctr_pts:
                        this_slice_indx_pos.append(
                            [int(cur_pt[0][0]),
                             int(cur_pt[0][1]),
                             int(slice)])
                    # We add again the first point of each contour at the end (trying to 'close' the contour)
                    this_slice_indx_pos.append([
                        int(cur_ctr_pts[0][0][0]),
                        int(cur_ctr_pts[0][0][1]),
                        int(slice)
                    ])

                    # Transform each point into a physical position
                    for c_pos in this_slice_indx_pos:
                        phys_pos = base_img.TransformIndexToPhysicalPoint(
                            c_pos)
                        contourdata_values.append(phys_pos[0])
                        contourdata_values.append(phys_pos[1])
                        contourdata_values.append(phys_pos[2])

                    # Copy the list of physical positions in the variable ContourData of the dataset
                    cur_contourdata_ds.ContourData = MultiValue(
                        pydicom.valuerep.DSfloat, contourdata_values)
                    cur_contourdata_ds.NumberOfContourPoints = tot_pts

                    # Append this Dataset into the list of slices with date
                    cur_ContourSequence_values.append(cur_contourdata_ds)

            old_ROIContour_ds.ContourSequence = Sequence(
                cur_ContourSequence_values
            )  # Replace the desired sequence with the new values
            final_ROIContourSequence_values.append(
                old_ROIContour_ds)  # Append it to the ROIContourSquence values

    # Replace the original Sequence of ROIContours with the new ones. These were initialized with the previous values
    # and only the Contour Data was replaced
    new_ROIContourSequence = Sequence(final_ROIContourSequence_values)
    ds.ROIContourSequence = new_ROIContourSequence  # Replace old ROIContourSequence with new one

    pydicom.dcmwrite(output_file_name, ds)