示例#1
0
    def __init__(self):
        # Load test DICOM files
        desired_path = Path.cwd().joinpath('test', 'testdata')

        # list of DICOM test files
        selected_files = find_DICOM_files(desired_path)
        # file path of DICOM files
        file_path = os.path.dirname(os.path.commonprefix(selected_files))
        read_data_dict, file_names_dict = \
            ImageLoading.get_datasets(selected_files)

        # Create patient dict container object
        self.patient_dict_container = PatientDictContainer()
        self.patient_dict_container.clear()
        self.patient_dict_container.set_initial_values \
            (file_path, read_data_dict, file_names_dict)

        # Set additional attributes in patient dict container
        # (otherwise program will crash and test will fail)
        if "rtss" in file_names_dict:
            dataset_rtss = dcmread(file_names_dict['rtss'])
            self.rois = ImageLoading.get_roi_info(dataset_rtss)
            dict_raw_contour_data, dict_numpoints = \
                ImageLoading.get_raw_contour_data(dataset_rtss)
            dict_pixluts = ImageLoading.get_pixluts(read_data_dict)

            self.patient_dict_container.set("rois", self.rois)
            self.patient_dict_container.set("raw_contour",
                                            dict_raw_contour_data)
            self.patient_dict_container.set("num_points", dict_numpoints)
            self.patient_dict_container.set("pixluts", dict_pixluts)

            # Set location of rtss file
            file_paths = self.patient_dict_container.filepaths
            self.patient_dict_container.set("file_rtss", file_paths['rtss'])
示例#2
0
    def import_rtdose(self):
        """
        Import DVH data from an RT Dose.
        """
        # Get DVH data
        result = rtdose2dvh()

        # If there is DVH data
        if bool(result):
            incomplete = result["diff"]
            result.pop("diff")
            dvh_x_y = ImageLoading.converge_to_0_dvh(result)
            self.patient_dict_container.set("raw_dvh", result)
            self.patient_dict_container.set("dvh_x_y", dvh_x_y)

            # If incomplete, tell the user about this
            if incomplete:
                self.patient_dict_container.set("dvh_outdated", True)
                self.display_outdated_indicator()

            # Initialise the display
            self.dvh_calculation_finished()
        else:
            result.pop("diff")
            self.init_layout_no_dvh()
示例#3
0
def test_create_rtss(test_object):
    """
    Test for creating an RT Struct file if one does not exist.
    :param test_object: test_object function, for accessing the shared
                        TestIso2Roi object.
    """
    # Create file path
    file_path = test_object.patient_dict_container.filepaths.values()
    file_path = Path(os.path.commonpath(file_path))
    file_path = str(file_path.joinpath("rtss.dcm"))

    # Get CT UID list
    ct_uid_list = ImageLoading.get_image_uid_list(
        test_object.patient_dict_container.dataset)

    # Generate RTSS
    rtss = ROI.create_initial_rtss_from_ct(
        test_object.patient_dict_container.dataset[0], file_path, ct_uid_list)

    # Get test dataset
    test_ds = test_object.patient_dict_container.dataset[0]

    # Assert that the ds exists and is not empty
    assert rtss

    # Assert that certain values are correct
    assert rtss.PatientName == test_ds.PatientName
    assert rtss.PatientID == test_ds.PatientID
    assert rtss.PatientBirthDate == test_ds.PatientBirthDate
    assert rtss.PatientSex == test_ds.PatientSex
    assert rtss.StudyInstanceUID == test_ds.StudyInstanceUID
    assert rtss.Modality == 'RTSTRUCT'
    assert rtss.SOPClassUID == '1.2.840.10008.5.1.4.1.1.481.3'
示例#4
0
    def save_new_rtss_to_moving_image_set(self, event=None):
        """
        Save the current RTSS stored in moving patient dictionary to the
        file system. ROIs modification into moving patient dict is auto
        saved :param event: Not used but will be passed as an argument from
        modified_indicator_widget on mouseReleaseEvent
        """
        if self.moving_dict_container.get("existing_file_rtss") is not None:
            existing_rtss_directory = str(
                Path(self.moving_dict_container.get("existing_file_rtss")))
        else:
            existing_rtss_directory = None
        rtss_directory = str(Path(self.moving_dict_container.get("file_rtss")))

        if existing_rtss_directory is None:
            self.moving_dict_container.get("dataset_rtss").save_as(
                rtss_directory)
        else:
            new_rtss = self.moving_dict_container.get("dataset_rtss")
            old_rtss = pydicom.dcmread(existing_rtss_directory, force=True)
            old_roi_names = \
                set(value["name"] for value in
                    ImageLoading.get_roi_info(old_rtss).values())
            new_roi_names = \
                set(value["name"] for value in
                    self.moving_dict_container.get("rois").values())
            duplicated_names = old_roi_names.intersection(new_roi_names)
            merged_rtss = merge_rtss(old_rtss, new_rtss, duplicated_names)
            merged_rtss.save_as(existing_rtss_directory)
        self.moving_dict_container.set("rtss_modified", False)
示例#5
0
    def __init__(self):
        # Load test DICOM files
        desired_path = Path.cwd().joinpath('test', 'pet-testdata')

        # list of DICOM test files
        selected_files = find_dicom_files(desired_path)
        # file path of DICOM files
        file_path = os.path.dirname(os.path.commonprefix(selected_files))
        read_data_dict, file_names_dict = \
            ImageLoading.get_datasets(selected_files)

        # Create patient dict container object
        self.patient_dict_container = PatientDictContainer()
        self.patient_dict_container.clear()
        self.patient_dict_container.set_initial_values \
            (file_path, read_data_dict, file_names_dict)

        # Create variables to be initialised later
        self.dicom_files = None
        self.suv_data = []

        # Create SUV2ROI object
        self.suv2roi = SUV2ROI()
        # Set patient weight. Not actual weight, just for testing
        # purposes.
        self.suv2roi.patient_weight = 70000
示例#6
0
    def __init__(self, *args, **kwargs):
        super(CalculateDVHProgressWindow, self).__init__(*args, **kwargs)
        layout = QtWidgets.QVBoxLayout()
        text = QtWidgets.QLabel(
            "Calculating DVHs... (This may take several minutes)")
        layout.addWidget(text)
        self.setWindowTitle("Please wait...")
        self.setLayout(layout)

        self.threadpool = QtCore.QThreadPool()
        self.patient_dict_container = PatientDictContainer()

        dataset_rtss = self.patient_dict_container.dataset["rtss"]
        dataset_rtdose = self.patient_dict_container.dataset["rtdose"]
        rois = self.patient_dict_container.get("rois")

        dict_thickness = ImageLoading.get_thickness_dict(
            dataset_rtss, self.patient_dict_container.dataset)

        interrupt_flag = threading.Event()
        fork_safe_platforms = ['Linux']
        if platform.system() in fork_safe_platforms:
            worker = Worker(ImageLoading.multi_calc_dvh, dataset_rtss,
                            dataset_rtdose, rois, dict_thickness)
        else:
            worker = Worker(ImageLoading.calc_dvhs, dataset_rtss,
                            dataset_rtdose, rois, dict_thickness,
                            interrupt_flag)

        worker.signals.result.connect(self.dvh_calculated)

        self.threadpool.start(worker)
示例#7
0
def test_save_radiomics_data():
    """
    Test for saving pyradiomics data to a DICOM SR file.
    """
    # Get test data files
    # Load test DICOM files
    desired_path = Path.cwd().joinpath('test', 'testdata')

    # list of DICOM test files
    selected_files = find_DICOM_files(desired_path)
    # file path of DICOM files
    file_path = os.path.dirname(os.path.commonprefix(selected_files))
    read_data_dict, file_names_dict = \
        ImageLoading.get_datasets(selected_files)

    # Create patient dict container object
    patient_dict_container = PatientDictContainer()
    patient_dict_container.clear()
    patient_dict_container.set_initial_values(file_path, read_data_dict,
                                              file_names_dict)

    file_path = patient_dict_container.path
    file_path = Path(file_path).joinpath("PyRadiomics-SR.dcm")
    ds = patient_dict_container.dataset[0]
    dicom_sr = DICOMStructuredReport.generate_dicom_sr(file_path, ds, "text",
                                                       "PYRADIOMICS")
    dicom_sr.save_as(file_path)

    # Assert that the new SR exists
    assert os.path.isfile(file_path)

    # Delete the created DICOM SR
    os.remove(file_path)
示例#8
0
    def create_new_rtstruct(cls, progress_callback):
        """
        Generates a new RTSS and edits the patient dict container. Used
        for batch processing.
        """
        # Get common directory
        patient_dict_container = PatientDictContainer()
        file_path = patient_dict_container.filepaths.values()
        file_path = Path(os.path.commonpath(file_path))

        # Get new RT Struct file path
        file_path = str(file_path.joinpath("rtss.dcm"))

        # Create RT Struct file
        progress_callback.emit(("Generating RT Structure Set", 60))
        ct_uid_list = ImageLoading.get_image_uid_list(
            patient_dict_container.dataset)
        ds = ROI.create_initial_rtss_from_ct(patient_dict_container.dataset[0],
                                             file_path, ct_uid_list)
        ds.save_as(file_path)

        # Add RT Struct file path to patient dict container
        patient_dict_container.filepaths['rtss'] = file_path
        filepaths = patient_dict_container.filepaths

        # Add RT Struct dataset to patient dict container
        patient_dict_container.dataset['rtss'] = ds
        dataset = patient_dict_container.dataset

        # Set some patient dict container attributes
        patient_dict_container.set("file_rtss", filepaths['rtss'])
        patient_dict_container.set("dataset_rtss", dataset['rtss'])

        dicom_tree_rtss = DicomTree(filepaths['rtss'])
        patient_dict_container.set("dict_dicom_tree_rtss",
                                   dicom_tree_rtss.dict)

        dict_pixluts = ImageLoading.get_pixluts(patient_dict_container.dataset)
        patient_dict_container.set("pixluts", dict_pixluts)

        rois = ImageLoading.get_roi_info(ds)
        patient_dict_container.set("rois", rois)

        patient_dict_container.set("selected_rois", [])
        patient_dict_container.set("dict_polygons_axial", {})

        patient_dict_container.set("rtss_modified", True)
示例#9
0
    def __init__(self):
        # Load test DICOM files
        desired_path = Path.cwd().joinpath('test', 'pet-testdata')

        # List of DICOM test files
        selected_files = find_DICOM_files(desired_path)

        # File path of DICOM files
        file_path = os.path.dirname(os.path.commonprefix(selected_files))
        read_data_dict, file_names_dict = \
            ImageLoading.get_datasets(selected_files)

        # Create patient dict container object
        self.patient_dict_container = PatientDictContainer()
        self.patient_dict_container.clear()
        self.patient_dict_container.set_initial_values \
            (file_path, read_data_dict, file_names_dict)

        # Set additional attributes in patient dict container
        # (otherwise program will crash and test will fail)
        self.patient_dict_container.set("existing_rtss_files", [])
        if "rtss" in file_names_dict:
            dataset_rtss = dcmread(file_names_dict['rtss'])
            self.rois = ImageLoading.get_roi_info(dataset_rtss)
            dict_raw_contour_data, dict_numpoints = \
                ImageLoading.get_raw_contour_data(dataset_rtss)
            dict_pixluts = ImageLoading.get_pixluts(read_data_dict)

            self.patient_dict_container.set("rois", self.rois)
            self.patient_dict_container.set("raw_contour",
                                            dict_raw_contour_data)
            self.patient_dict_container.set("num_points", dict_numpoints)
            self.patient_dict_container.set("pixluts", dict_pixluts)
        else:
            img_loader = ImageLoader(selected_files, None, None)
            img_loader.load_temp_rtss(file_path, DummyProgressWindow,
                                      DummyProgressWindow)

        # Open the main window
        self.main_window = MainWindow()

        # Get the initial structure and ROI count
        self.initial_structure_count = \
            self.main_window.structures_tab.layout_content.count()
        self.initial_roi_count = len(self.main_window.structures_tab.rois)
    def __init__(self):
        # Load test DICOM files
        if platform.system() == "Windows":
            desired_path = "\\testdata\\DICOM-RT-TEST"
        elif platform.system() == "Linux" or platform.system() == "Darwin":
            desired_path = "/testdata/DICOM-RT-TEST"

        desired_path = os.path.dirname(
            os.path.realpath(__file__)) + desired_path

        selected_files = find_DICOM_files(
            desired_path)  # list of DICOM test files
        file_path = os.path.dirname(
            os.path.commonprefix(selected_files))  # file path of DICOM files
        read_data_dict, file_names_dict = ImageLoading.get_datasets(
            selected_files)

        # Create patient dict container object
        patient_dict_container = PatientDictContainer()
        patient_dict_container.clear()
        patient_dict_container.set_initial_values(file_path, read_data_dict,
                                                  file_names_dict)

        # Set additional attributes in patient dict container (otherwise program will crash and test will fail)
        if "rtss" in file_names_dict:
            dataset_rtss = dcmread(file_names_dict['rtss'])
            self.rois = ImageLoading.get_roi_info(dataset_rtss)
            dict_raw_contour_data, dict_numpoints = ImageLoading.get_raw_contour_data(
                dataset_rtss)
            dict_pixluts = ImageLoading.get_pixluts(read_data_dict)

            patient_dict_container.set("rois", self.rois)
            patient_dict_container.set("raw_contour", dict_raw_contour_data)
            patient_dict_container.set("num_points", dict_numpoints)
            patient_dict_container.set("pixluts", dict_pixluts)

        # Open the main window
        self.main_window = MainWindow()
        self.main_window.show()

        self.dicom_view = self.main_window.dicom_view
        self.new_polygons = {}
        slider_id = self.dicom_view.slider.value()
        self.curr_slice = self.dicom_view.patient_dict_container.get(
            "dict_uid")[slider_id]
示例#11
0
    def structure_modified(self, new_dataset):
        """
		Executes when a structure is renamed/deleted. Displays indicator that structure has changed.
		"""
        # TODO there needs to be a way to give the user the option to save the new RTSS file.
        # Currently all changes are discarded when the user exits the program.

        # If this is the first time the RTSS has been modified, create a modified indicator giving the user the option
        # to save their new file.
        if not self.main_window.rtss_modified:

            modified_indicator_widget = QtWidgets.QWidget()
            modified_indicator_widget.setContentsMargins(8, 5, 8, 5)
            modified_indicator_layout = QtWidgets.QHBoxLayout()
            modified_indicator_layout.setAlignment(Qt.AlignLeft)

            modified_indicator_icon = QtWidgets.QLabel()
            modified_indicator_icon.setPixmap(
                QtGui.QPixmap("src/Icon/alert.png"))
            modified_indicator_layout.addWidget(modified_indicator_icon)

            modified_indicator_text = QtWidgets.QLabel(
                "Structures have been modified")
            modified_indicator_text.setStyleSheet("color: red")
            modified_indicator_layout.addWidget(modified_indicator_text)

            modified_indicator_widget.setLayout(modified_indicator_layout)
            modified_indicator_widget.mouseReleaseEvent = self.save_new_rtss  # When the widget is clicked, save the rtss
            self.layout.addWidget(modified_indicator_widget)

        # If this is the first change made to the RTSS file, update the dataset with the new one so that OnkoDICOM
        # starts working off this dataset rather than the original RTSS file.
        self.main_window.rtss_modified = True
        self.main_window.dataset_rtss = new_dataset

        # Refresh ROIs in main page
        self.main_window.rois = ImageLoading.get_roi_info(new_dataset)
        self.main_window.dict_raw_ContourData, self.main_window.dict_NumPoints = ImageLoading.get_raw_contour_data(
            new_dataset)
        self.main_window.list_roi_numbers = self.main_window.ordered_list_rois(
        )

        # Refresh structure tab
        self.update_content()
    def __init__(self):
        # Load test DICOM files
        desired_path = Path.cwd().joinpath('test', 'testdata')
        selected_files = find_DICOM_files(
            desired_path)  # list of DICOM test files
        file_path = os.path.dirname(
            os.path.commonprefix(selected_files))  # file path of DICOM files
        read_data_dict, file_names_dict = ImageLoading.get_datasets(
            selected_files)

        # Create patient dict container object
        patient_dict_container = PatientDictContainer()
        patient_dict_container.clear()
        patient_dict_container.set_initial_values(file_path, read_data_dict,
                                                  file_names_dict)

        # Set additional attributes in patient dict container (otherwise
        # program will crash and test will fail)
        if "rtss" in file_names_dict:
            self.dataset_rtss = dcmread(file_names_dict['rtss'])
            patient_dict_container.set("existing_rtss_files",
                                       [file_names_dict['rtss']])
            self.rois = ImageLoading.get_roi_info(self.dataset_rtss)
            dict_raw_contour_data, dict_numpoints = \
                ImageLoading.get_raw_contour_data(self.dataset_rtss)
            dict_pixluts = ImageLoading.get_pixluts(read_data_dict)

            patient_dict_container.set("rois", self.rois)
            patient_dict_container.set("raw_contour", dict_raw_contour_data)
            patient_dict_container.set("num_points", dict_numpoints)
            patient_dict_container.set("pixluts", dict_pixluts)

        # Open the main window
        self.main_window = MainWindow()
        self.main_window.show()

        self.dicom_view = self.main_window.dicom_single_view
        self.new_polygons = {}
        slider_id = self.dicom_view.slider.value()
        self.curr_slice = self.dicom_view.patient_dict_container.get(
            "dict_uid")[slider_id]
示例#13
0
    def __init__(self):
        # Load test DICOM files
        desired_path = Path.cwd().joinpath('test', 'testdata')
        # list of DICOM test files
        selected_files = find_DICOM_files(desired_path)
        # file path of DICOM files
        file_path = os.path.dirname(os.path.commonprefix(selected_files))
        read_data_dict, file_names_dict = ImageLoading.get_datasets(
            selected_files)

        # Create patient dict container object
        patient_dict_container = PatientDictContainer()
        patient_dict_container.clear()
        patient_dict_container.set_initial_values(file_path, read_data_dict,
                                                  file_names_dict)

        # Set additional attributes in patient dict container (otherwise
        # program will crash and test will fail)
        if "rtss" in file_names_dict:
            self.dataset_rtss = dcmread(file_names_dict['rtss'])
            self.rois = ImageLoading.get_roi_info(self.dataset_rtss)
            dict_raw_contour_data, dict_numpoints = \
                ImageLoading.get_raw_contour_data(self.dataset_rtss)
            dict_pixluts = ImageLoading.get_pixluts(read_data_dict)

            patient_dict_container.set("rois", self.rois)
            patient_dict_container.set("raw_contour", dict_raw_contour_data)
            patient_dict_container.set("num_points", dict_numpoints)
            patient_dict_container.set("pixluts", dict_pixluts)

        # Open the main window
        self.main_window = MainWindow()
        self.main_window.show()

        # Open the manipulate ROI window
        self.structures_tab = self.main_window.structures_tab
        color_dict = self.structures_tab.color_dict
        self.roi_manipulate_handler = self.structures_tab.\
            roi_manipulate_handler.show_roi_manipulate_options(color_dict)
        self.manipulate_window = self.structures_tab.\
            roi_manipulate_handler.manipulate_window
示例#14
0
    def load_temp_rtss(self, path, progress_callback, interrupt_flag):
        """
        Generate a temporary rtss and load its data into
        MovingDictContainer
        :param path: str. The common root folder of all DICOM files.
        :param progress_callback: A signal that receives the current
        progress of the loading.
        :param interrupt_flag: A threading.Event() object that tells the
        function to stop loading.
        """
        progress_callback.emit(("Generating temporary rtss...", 20))
        moving_dict_container = MovingDictContainer()
        rtss_path = Path(path).joinpath('rtss.dcm')
        uid_list = ImageLoading.get_image_uid_list(
            moving_dict_container.dataset)
        rtss = create_initial_rtss_from_ct(moving_dict_container.dataset[0],
                                           rtss_path, uid_list)

        if interrupt_flag.is_set():  # Stop loading.
            print("stopped")
            return False

        progress_callback.emit(("Loading temporary rtss...", 50))
        # Set ROIs
        rois = ImageLoading.get_roi_info(rtss)
        moving_dict_container.set("rois", rois)

        # Set pixluts
        dict_pixluts = ImageLoading.get_pixluts(moving_dict_container.dataset)
        moving_dict_container.set("pixluts", dict_pixluts)

        # Add RT Struct file path and dataset to moving dict container
        moving_dict_container.filepaths['rtss'] = rtss_path
        moving_dict_container.dataset['rtss'] = rtss

        # Set some moving dict container attributes
        moving_dict_container.set("file_rtss", rtss_path)
        moving_dict_container.set("dataset_rtss", rtss)
        ordered_dict = DicomTree(None).dataset_to_dict(rtss)
        moving_dict_container.set("dict_dicom_tree_rtss", ordered_dict)
        moving_dict_container.set("selected_rois", [])
示例#15
0
def test_create_initial_rtss_from_ct(qtbot, test_object, init_config):
    # Create a test rtss
    path = test_object.patient_dict_container.path
    rtss_path = Path(path).joinpath('rtss.dcm')
    uid_list = ImageLoading.get_image_uid_list(
        test_object.patient_dict_container.dataset)
    rtss = create_initial_rtss_from_ct(
        test_object.patient_dict_container.dataset[1], rtss_path, uid_list)

    # type 1 tags - must exist and not be empty
    type_1_tags: list = [Tag("StudyInstanceUID"),
                         Tag("Modality"),
                         Tag("SeriesInstanceUID"),
                         Tag("StructureSetLabel"),
                         Tag("SOPClassUID"),
                         Tag("SOPInstanceUID")
                         ]
    # type 2 tags - must exist and be at least an empty string
    type_2_tags: list = [Tag("PatientName"),
                         Tag("PatientBirthDate"),
                         Tag("PatientSex"),
                         Tag("StudyDate"),
                         Tag("StudyTime"),
                         Tag("AccessionNumber"),
                         Tag("ReferringPhysicianName"),
                         Tag("StudyID"),
                         Tag("OperatorsName"),
                         Tag("SeriesNumber"),
                         Tag("Manufacturer"),
                         Tag("StructureSetDate"),
                         Tag("StructureSetTime")
                         ]

    # type 1 sequence tags - must exist
    type_1_sequence_tags: list = [Tag("StructureSetROISequence"),
                                  Tag("ROIContourSequence"),
                                  Tag("RTROIObservationsSequence")
                                  ]

    # Checking type 1 tags
    for tag in type_1_tags:
        assert (tag in rtss) is True
        assert rtss[tag].is_empty is False

    # Checking type 2 tags
    for tag in type_2_tags:
        assert (tag in rtss) is True
        if rtss[tag].value != "":
            assert rtss[tag].is_empty is False

    # Checking type 1 sequence tags
    for tag in type_1_sequence_tags:
        assert (tag in rtss) is True
示例#16
0
    def __init__(self):
        self.dvh_data = None

        # Load test DICOM files
        desired_path = Path.cwd().joinpath('test', 'testdata')
        selected_files = find_DICOM_files(desired_path)
        file_path = os.path.dirname(os.path.commonprefix(selected_files))
        read_data_dict, file_names_dict = \
            ImageLoading.get_datasets(selected_files)

        # Create patient dict container object
        self.patient_dict_container = PatientDictContainer()
        self.patient_dict_container.clear()
        self.patient_dict_container.set_initial_values(file_path,
                                                       read_data_dict,
                                                       file_names_dict)
示例#17
0
    def __init__(self):
        # Load test DICOM files
        desired_path = Path.cwd().joinpath('test', 'testdata')
        selected_files = find_DICOM_files(
            desired_path)  # list of DICOM test files
        file_path = os.path.dirname(
            os.path.commonprefix(selected_files))  # file path of DICOM files
        read_data_dict, file_names_dict = ImageLoading.get_datasets(
            selected_files)

        # Create patient dict container object
        patient_dict_container = PatientDictContainer()
        patient_dict_container.clear()
        patient_dict_container.set_initial_values(file_path, read_data_dict,
                                                  file_names_dict)

        # Set additional attributes in patient dict container (otherwise program will crash and test will fail)
        if "rtss" in file_names_dict:
            dataset_rtss = dcmread(file_names_dict['rtss'])
            self.rois = ImageLoading.get_roi_info(dataset_rtss)
            dict_raw_contour_data, dict_numpoints = ImageLoading.get_raw_contour_data(
                dataset_rtss)
            dict_pixluts = ImageLoading.get_pixluts(read_data_dict)

            patient_dict_container.set("rois", self.rois)
            patient_dict_container.set("raw_contour", dict_raw_contour_data)
            patient_dict_container.set("num_points", dict_numpoints)
            patient_dict_container.set("pixluts", dict_pixluts)
            dict_thickness = ImageLoading.get_thickness_dict(
                dataset_rtss, read_data_dict)

        if 'rtdose' in file_names_dict:
            dataset_rtdose = dcmread(file_names_dict['rtdose'])

        # Open the main window
        self.main_window = MainWindow()
        self.dvh_tab = self.main_window.dvh_tab
        self.new_polygons = {}
        self.raw_dvh = ImageLoading.multi_calc_dvh(dataset_rtss,
                                                   dataset_rtdose, self.rois,
                                                   dict_thickness)
        self.dvh_x_y = ImageLoading.converge_to_0_dvh(self.raw_dvh)
    def __init__(self):
        # Load test DICOM files
        desired_path = Path.cwd().joinpath('test', 'testdata')

        # list of DICOM test files
        selected_files = find_DICOM_files(desired_path)
        # file path of DICOM files
        self.file_path = os.path.dirname(os.path.commonprefix(selected_files))
        read_data_dict, file_names_dict = \
            ImageLoading.get_datasets(selected_files)

        # Create patient dict container object
        self.patient_dict_container = PatientDictContainer()
        self.patient_dict_container.clear()
        self.patient_dict_container.set_initial_values(self.file_path,
                                                       read_data_dict,
                                                       file_names_dict)

        self.file_path = self.patient_dict_container.path
        self.file_path = Path(self.file_path).joinpath("Clinical-Data-SR.dcm")

        # Test data to write
        self.data = [['123456789', 'Jim', 'Jimson']]
示例#19
0
    def load(self, interrupt_flag, progress_callback):
        """
        :param interrupt_flag: A threading.Event() object that tells the 
        function to stop loading.

        :param progress_callback: A signal that receives the current 
        progress of the loading.

        :return: PatientDictContainer object containing all values related 
        to the loaded DICOM files.
        """
        progress_callback.emit(("Creating datasets...", 0))
        try:
            # Gets the common root folder.
            path = os.path.dirname(os.path.commonprefix(self.selected_files))
            read_data_dict, file_names_dict = ImageLoading.get_datasets(
                self.selected_files)
        except ImageLoading.NotAllowedClassError:
            raise ImageLoading.NotAllowedClassError

        # Populate the initial values in the PatientDictContainer singleton.
        moving_dict_container = MovingDictContainer()
        moving_dict_container.clear()
        moving_dict_container.set_initial_values(
            path,
            read_data_dict,
            file_names_dict,
            existing_rtss_files=self.existing_rtss)

        if interrupt_flag.is_set():
            print("stopped")
            return False

        if 'rtss' in file_names_dict and 'rtdose' in file_names_dict:
            self.parent_window.signal_advise_calc_dvh.connect(
                self.update_calc_dvh)
            self.signal_request_calc_dvh.emit()

            while not self.advised_calc_dvh:
                pass

        if 'rtss' in file_names_dict:
            dataset_rtss = dcmread(file_names_dict['rtss'])

            progress_callback.emit(("Getting ROI info...", 10))
            rois = ImageLoading.get_roi_info(dataset_rtss)

            if interrupt_flag.is_set():  # Stop loading.
                print("stopped")
                return False

            progress_callback.emit(("Getting contour data...", 30))
            dict_raw_contour_data, dict_numpoints = \
                ImageLoading.get_raw_contour_data(dataset_rtss)

            # Determine which ROIs are one slice thick
            dict_thickness = ImageLoading.get_thickness_dict(
                dataset_rtss, read_data_dict)

            if interrupt_flag.is_set():  # Stop loading.
                print("stopped")
                return False

            progress_callback.emit(("Getting pixel LUTs...", 50))
            dict_pixluts = ImageLoading.get_pixluts(read_data_dict)

            if interrupt_flag.is_set():  # Stop loading.
                print("stopped")
                return False

            # Add RTSS values to MovingDictContainer
            moving_dict_container.set("rois", rois)
            moving_dict_container.set("raw_contour", dict_raw_contour_data)
            moving_dict_container.set("num_points", dict_numpoints)
            moving_dict_container.set("pixluts", dict_pixluts)

            if 'rtdose' in file_names_dict and self.calc_dvh:
                dataset_rtdose = dcmread(file_names_dict['rtdose'])

                # Spawn-based platforms (i.e Windows and MacOS) have a large
                # overhead when creating a new process, which ends up making
                # multiprocessing on these platforms more expensive than linear
                # calculation. As such, multiprocessing is only available on
                # Linux until a better solution is found.
                fork_safe_platforms = ['Linux']
                if platform.system() in fork_safe_platforms:
                    progress_callback.emit(("Calculating DVHs...", 60))
                    raw_dvh = ImageLoading.multi_calc_dvh(
                        dataset_rtss, dataset_rtdose, rois, dict_thickness)
                else:
                    progress_callback.emit(
                        ("Calculating DVHs... (This may take a while)", 60))
                    raw_dvh = ImageLoading.calc_dvhs(dataset_rtss,
                                                     dataset_rtdose, rois,
                                                     dict_thickness,
                                                     interrupt_flag)

                if interrupt_flag.is_set():  # Stop loading.
                    print("stopped")
                    return False

                progress_callback.emit(("Converging to zero...", 80))
                dvh_x_y = ImageLoading.converge_to_0_dvh(raw_dvh)

                if interrupt_flag.is_set():  # Stop loading.
                    print("stopped")
                    return False

                # Add DVH values to MovingDictContainer
                moving_dict_container.set("raw_dvh", raw_dvh)
                moving_dict_container.set("dvh_x_y", dvh_x_y)
                moving_dict_container.set("dvh_outdated", False)
            create_moving_model()
        else:
            create_moving_model()
            self.load_temp_rtss(path, progress_callback, interrupt_flag)
        progress_callback.emit(("Loading Moving Model", 85))

        if interrupt_flag.is_set():  # Stop loading.
            progress_callback.emit(("Stopping", 85))
            return False

        return True
示例#20
0
    def get_datasets(cls, file_path_list):
        """
        Gets datasets in the passed-in file path.
        :param file_path_list: list of file paths to load datasets from.
        """
        read_data_dict = {}
        file_names_dict = {}
        rt_structs = {}

        # For getting the correct PET files in a dataset that contains
        # multiple image sets
        ctac_count = 0
        pt_nac = {}
        nac_count = 0

        slice_count = 0
        # For each file in the file path list
        for file in ImageLoading.natural_sort(file_path_list):
            # Try to open it
            try:
                read_file = dcmread(file)
            except InvalidDicomError:
                continue

            # Update relevant data
            if read_file.SOPClassUID in cls.allowed_classes:
                allowed_class = cls.allowed_classes[read_file.SOPClassUID]
                if allowed_class["sliceable"]:
                    slice_name = slice_count
                    slice_count += 1
                else:
                    # Add one (the first) clinical data SR
                    if allowed_class["name"] == 'sr':
                        sr_type = read_file.SeriesDescription
                        if sr_type == "CLINICAL-DATA" \
                                and 'sr' not in list(read_data_dict.keys()):
                            read_data_dict['sr'] = read_file
                            file_names_dict['sr'] = file
                            continue
                        else:
                            continue

                    slice_name = allowed_class["name"]

                # Here we keep track of what PET images we have found,
                # as if we have 2 sets and one is CTAC, we want to use the
                # one that is CTAC.
                if read_file.SOPClassUID == '1.2.840.10008.5.1.4.1.1.128':
                    if 'CorrectedImage' in read_file:
                        # If it is a CTAC, add it straight into the
                        # dictionaries
                        if "ATTN" in read_file.CorrectedImage:
                            read_data_dict[ctac_count] = read_file
                            file_names_dict[ctac_count] = file
                            ctac_count += 1
                            continue
                        # If it is a NAC, store it in case we need it
                        else:
                            pt_nac[file] = read_file
                            nac_count += 1
                            continue

                if slice_name == 'rtss':
                    rt_structs[file] = read_file
                    continue

                # Skip this file if it does not match files already in the
                # dataset, and if this file is an image.
                # TODO: allow selection of which set of images (CT, PET,
                #       etc.) to open (by description). Currently only
                #       opens first set of each found + matching RTSS
                if len(read_data_dict) > 0 \
                        and isinstance(slice_name, int) \
                        and 0 in list(read_data_dict.keys()) \
                        and read_file.SeriesInstanceUID \
                        != read_data_dict[0].SeriesInstanceUID:
                    continue

                read_data_dict[slice_name] = read_file
                file_names_dict[slice_name] = file

        # If we have PET NAC files and no PET CTAC files,
        # add them to the data dictionaries
        if nac_count > 0 and ctac_count == 0:
            for i, path in enumerate(pt_nac):
                read_data_dict[i] = pt_nac[path]
                file_names_dict[i] = path

        # Here we need to compare DICOM files to get the correct
        # RTSS for the selected images, i.e., the RTSS that
        # references the images we have. Once we have found the
        # right RTSS we break.
        for rtss in rt_structs:
            # Try get the referenced series instance UID from the
            # rtss. It is an optional tag and therefore may not
            # exist.
            ref_image_series_uid = None
            try:
                ref_frame = rt_structs[rtss].ReferencedFrameOfReferenceSequence
                ref_study = ref_frame[0].RTReferencedStudySequence[0]
                ref_series = ref_study.RTReferencedSeriesSequence[0]
                ref_image_series_uid = ref_series.SeriesInstanceUID
            except AttributeError:
                pass

            # If we have no images
            if len(read_data_dict) <= 0:
                read_data_dict['rtss'] = rt_structs[rtss]
                file_names_dict['rtss'] = rtss
                break
            # If we have images
            elif 0 in list(read_data_dict.keys()):
                # If the RTSTRUCT matches the image, add it
                if ref_image_series_uid \
                        == read_data_dict[0].SeriesInstanceUID:
                    read_data_dict['rtss'] = rt_structs[rtss]
                    file_names_dict['rtss'] = rtss
                    break
                # If it doesn't continue
                else:
                    continue
            # If we have other datasets that aren't images (eg rtdose, rtplan),
            # add the RTSTRUCT. This will only be reached if there are other
            # items in the read_data_dict, but they are not images.
            else:
                read_data_dict['rtss'] = rt_structs[rtss]
                file_names_dict['rtss'] = rtss
                break

        # Get and return read data dict and file names dict
        sorted_read_data_dict, sorted_file_names_dict = \
            ImageLoading.image_stack_sort(read_data_dict, file_names_dict)
        return sorted_read_data_dict, sorted_file_names_dict
示例#21
0
    def save_new_rtss_to_fixed_image_set(self, event=None, auto=False):
        """
        Save the current RTSS stored in fixed patient dictionary to the file
        system. :param event: Not used but will be passed as an argument
        from modified_indicator_widget on mouseReleaseEvent :param auto:
        Used for auto save without user confirmation
        """
        existing_rtss_files = self.patient_dict_container.get(
            "existing_rtss_files")
        if len(existing_rtss_files) == 1:
            if isinstance(existing_rtss_files[0], Series):
                existing_rtss_directory = str(
                    Path(existing_rtss_files[0].get_files()[0]))
            else:
                # This "else" is used by iso2roi gui and structure tab tests to
                # quickly set existing_rtss_directory
                existing_rtss_directory = existing_rtss_files[0]
        elif len(existing_rtss_files) > 1:
            self.display_select_rtss_window()
            # This function will be called again when a RTSS is selected
            return
        else:
            existing_rtss_directory = None

        rtss_directory = str(Path(
            self.patient_dict_container.get("file_rtss")))

        if auto:
            confirm_save = QtWidgets.QMessageBox.Yes
        else:
            confirm_save = \
                QtWidgets.QMessageBox.information(self, "Confirmation",
                                                  "Are you sure you want to "
                                                  "save the modified RTSTRUCT "
                                                  "file? This will overwrite "
                                                  "the existing file. This is "
                                                  "not reversible.",
                                                  QtWidgets.QMessageBox.Yes,
                                                  QtWidgets.QMessageBox.No)

        if confirm_save == QtWidgets.QMessageBox.Yes:
            if existing_rtss_directory is None:
                self.patient_dict_container.get("dataset_rtss").save_as(
                    rtss_directory)
            else:
                new_rtss = self.patient_dict_container.get("dataset_rtss")
                old_rtss = pydicom.dcmread(existing_rtss_directory, force=True)
                old_roi_names = \
                    set(value["name"] for value in
                        ImageLoading.get_roi_info(old_rtss).values())
                new_roi_names = \
                    set(value["name"] for value in
                        self.patient_dict_container.get("rois").values())
                duplicated_names = old_roi_names.intersection(new_roi_names)

                # stop if there are conflicting roi names and user do not
                # wish to proceed.
                if duplicated_names and not self.display_confirm_merge(
                        duplicated_names):
                    return

                merged_rtss = merge_rtss(old_rtss, new_rtss, duplicated_names)
                merged_rtss.save_as(existing_rtss_directory)

            if not auto:
                QtWidgets.QMessageBox.about(
                    self.parentWidget(), "File saved",
                    "The RTSTRUCT file has been saved.")
            self.patient_dict_container.set("rtss_modified", False)
            # Hide the modified indicator
            self.modified_indicator_widget.setVisible(False)
示例#22
0
    def fixed_container_structure_modified(self, changes):
        """
        Executes when a structure of fixed patient container is modified
        Displays indicator that structure has changed.
        Changes is a tuple of (new_dataset,
        description_of_changes)
        description_of_changes follows the format
        {"type_of_change": value_of_change}.
        Examples:
        {"rename": ["TOOTH", "TEETH"]} represents that the TOOTH structure has
            been renamed to TEETH.
        {"delete": ["TEETH", "MAXILLA"]} represents that the TEETH and MAXILLA
            structures have been deleted.
        {"draw": "AORTA"} represents that a new structure AORTA has been drawn.
        Note: Use {"draw": None} after multiple ROIs are generated
        (E.g., from ISO2ROI functionality), and use {"transfer":None} for
         ROI Transfer instead of calling this function
        multiple times. This will trigger auto save.
        """

        new_dataset = changes[0]
        change_description = changes[1]

        # Only show the modified indicator if description_of_changes is
        # not {"draw": None}, as this description means that the RTSS
        # is autosaved, and therefore there is no need to tell the user
        # that the RTSS has been modified
        if not("draw" in change_description
               and change_description["draw"] is None) and \
                not ("transfer" in change_description):
            self.show_modified_indicator()

        # If this is the first change made to the RTSS file, update the
        # dataset with the new one so that OnkoDICOM starts working off this
        # dataset rather than the original RTSS file.
        self.patient_dict_container.set("rtss_modified", True)
        self.patient_dict_container.set("dataset_rtss", new_dataset)

        # Refresh ROIs in main page
        self.patient_dict_container.set("rois",
                                        ImageLoading.get_roi_info(new_dataset))
        self.rois = self.patient_dict_container.get("rois")
        contour_data = ImageLoading.get_raw_contour_data(new_dataset)
        self.patient_dict_container.set("raw_contour", contour_data[0])
        self.patient_dict_container.set("num_points", contour_data[1])
        pixluts = ImageLoading.get_pixluts(self.patient_dict_container.dataset)
        self.patient_dict_container.set("pixluts", pixluts)
        self.patient_dict_container.set(
            "list_roi_numbers",
            ordered_list_rois(self.patient_dict_container.get("rois")))
        self.patient_dict_container.set("selected_rois", [])
        self.patient_dict_container.set("dict_polygons_axial", {})
        self.patient_dict_container.set("dict_polygons_sagittal", {})
        self.patient_dict_container.set("dict_polygons_coronal", {})

        if "draw" in change_description or "transfer" in change_description:
            dicom_tree_rtss = DicomTree(None)
            dicom_tree_rtss.dataset = new_dataset
            dicom_tree_rtss.dict = dicom_tree_rtss.dataset_to_dict(
                dicom_tree_rtss.dataset)
            self.patient_dict_container.set("dict_dicom_tree_rtss",
                                            dicom_tree_rtss.dict)
            self.color_dict = self.init_color_roi(self.patient_dict_container)
            self.patient_dict_container.set("roi_color_dict", self.color_dict)
            if self.patient_dict_container.has_attribute("raw_dvh"):
                # DVH will be outdated once changes to it are made, and
                # recalculation will be required.
                self.patient_dict_container.set("dvh_outdated", True)

        if self.patient_dict_container.has_attribute("raw_dvh"):
            # Rename structures in DVH list
            if "rename" in change_description:
                new_raw_dvh = self.patient_dict_container.get("raw_dvh")
                for key, dvh in new_raw_dvh.items():
                    if dvh.name == change_description["rename"][0]:
                        dvh.name = change_description["rename"][1]
                        break

                self.patient_dict_container.set("raw_dvh", new_raw_dvh)
                dvh2rtdose(new_raw_dvh)

            # Remove structures from DVH list - the only visible effect of
            # this section is the exported DVH csv
            if "delete" in change_description:
                list_of_deleted = []
                new_raw_dvh = self.patient_dict_container.get("raw_dvh")
                for key, dvh in new_raw_dvh.items():
                    if dvh.name in change_description["delete"]:
                        list_of_deleted.append(key)
                for key in list_of_deleted:
                    new_raw_dvh.pop(key)
                self.patient_dict_container.set("raw_dvh", new_raw_dvh)
                dvh2rtdose(new_raw_dvh)

        # Refresh ROIs in DVH tab and DICOM View
        self.request_update_structures.emit()

        # Refresh structure tab
        self.update_content()

        if "draw" in change_description and change_description["draw"] is None:
            self.save_new_rtss_to_fixed_image_set(auto=True)
        elif "transfer" in change_description \
                and change_description["transfer"] is None:
            self.save_new_rtss_to_fixed_image_set(auto=True)
示例#23
0
 def dvh_calculated(self, result):
     dvh_x_y = ImageLoading.converge_to_0_dvh(result)
     self.patient_dict_container.set("raw_dvh", result)
     self.patient_dict_container.set("dvh_x_y", dvh_x_y)
     self.signal_dvh_calculated.emit()
     self.close()
示例#24
0
    def generate_ROI(self, contours, progress_callback):
        """
        Generates new ROIs based on contour data.
        :param contours: dictionary of contours to turn into ROIs.
        :param progress_callback: signal that receives the current
                                  progress of the loading.
        """
        # Initialise variables needed for function
        patient_dict_container = PatientDictContainer()
        dataset_rtss = patient_dict_container.get("dataset_rtss")

        # Get existing ROIs
        existing_rois = []
        rois = patient_dict_container.get("dataset_rtss")
        if rois:
            for roi in rois.StructureSetROISequence:
                existing_rois.append(roi.ROIName)

        # Loop through each SUV level
        item_count = len(contours)
        current_progress = 60
        progress_increment = round((95 - 60) / item_count)
        for item in contours:
            # Delete ROI if it already exists to recreate it
            if item in existing_rois:
                dataset_rtss = ROI.delete_roi(dataset_rtss, item)

                # Update patient dict container
                current_rois = patient_dict_container.get("rois")
                keys = []
                for key, value in current_rois.items():
                    if value["name"] == item:
                        keys.append(key)
                for key in keys:
                    del current_rois[key]
                patient_dict_container.set("rois", current_rois)

            progress_callback.emit(("Generating ROIs", current_progress))
            current_progress += progress_increment

            # Loop through each slice
            for i in range(len(contours[item])):
                slider_id = contours[item][i][0]
                dataset = patient_dict_container.dataset[slider_id]
                pixlut = patient_dict_container.get("pixluts")
                pixlut = pixlut[dataset.SOPInstanceUID]
                z_coord = dataset.SliceLocation

                # List storing lists that contain all points for a
                # contour.
                single_array = []

                # Loop through each contour
                for j in range(len(contours[item][i][1])):
                    single_array.append([])
                    # Loop through every point in the contour
                    for point in contours[item][i][1][j]:
                        # Convert pixel coordinates to RCS points
                        rcs_pixels = ROI.pixel_to_rcs(pixlut, round(point[1]),
                                                      round(point[0]))
                        # Append RCS points to the single array
                        single_array[j].append(rcs_pixels[0])
                        single_array[j].append(rcs_pixels[1])
                        single_array[j].append(z_coord)

                # Create the ROI(s)
                for array in single_array:
                    rtss = ROI.create_roi(dataset_rtss, item, [{
                        'coords': array,
                        'ds': dataset
                    }], "")

                    # Save the updated rtss
                    patient_dict_container.set("dataset_rtss", rtss)
                    patient_dict_container.set("rois",
                                               ImageLoading.get_roi_info(rtss))
示例#25
0
    def structure_modified(self, changes):
        """
		Executes when a structure is renamed/deleted. Displays indicator that structure has changed.
		changes is a tuple of (new_dataset, description_of_changes)
		description_of_changes follows the format {"type_of_change": value_of_change}.
		Examples: {"rename": ["TOOTH", "TEETH"]} represents that the TOOTH structure has been renamed to TEETH.
		{"delete": ["TEETH", "MAXILLA"]} represents that the TEETH and MAXILLA structures have been deleted.
		"""

        new_dataset = changes[0]
        change_description = changes[1]

        # If this is the first time the RTSS has been modified, create a modified indicator giving the user the option
        # to save their new file.
        if not self.main_window.rtss_modified:

            modified_indicator_widget = QtWidgets.QWidget()
            modified_indicator_widget.setContentsMargins(8, 5, 8, 5)
            modified_indicator_layout = QtWidgets.QHBoxLayout()
            modified_indicator_layout.setAlignment(Qt.AlignLeft)

            modified_indicator_icon = QtWidgets.QLabel()
            modified_indicator_icon.setPixmap(
                QtGui.QPixmap("src/Icon/alert.png"))
            modified_indicator_layout.addWidget(modified_indicator_icon)

            modified_indicator_text = QtWidgets.QLabel(
                "Structures have been modified")
            modified_indicator_text.setStyleSheet("color: red")
            modified_indicator_layout.addWidget(modified_indicator_text)

            modified_indicator_widget.setLayout(modified_indicator_layout)
            modified_indicator_widget.mouseReleaseEvent = self.save_new_rtss  # When the widget is clicked, save the rtss
            self.layout.addWidget(modified_indicator_widget)

        # If this is the first change made to the RTSS file, update the dataset with the new one so that OnkoDICOM
        # starts working off this dataset rather than the original RTSS file.
        self.main_window.rtss_modified = True
        self.main_window.dataset_rtss = new_dataset

        # Refresh ROIs in main page
        self.main_window.rois = ImageLoading.get_roi_info(new_dataset)
        self.main_window.dict_raw_ContourData, self.main_window.dict_NumPoints = ImageLoading.get_raw_contour_data(
            new_dataset)
        self.main_window.list_roi_numbers = self.main_window.ordered_list_rois(
        )
        self.main_window.selected_rois = []

        if self.main_window.raw_dvh is not None:
            # Rename structures in DVH list
            if "rename" in changes[1]:
                for key, dvh in self.main_window.raw_dvh.items():
                    if dvh.name == change_description["rename"][0]:
                        dvh.name = change_description["rename"][1]
                        break

            # Remove structures from DVH list - the only visible effect of this section is the exported DVH csv
            if "delete" in changes[1]:
                list_of_deleted = []
                for key, dvh in self.main_window.raw_dvh.items():
                    if dvh.name in change_description["delete"]:
                        list_of_deleted.append(key)
                for key in list_of_deleted:
                    self.main_window.raw_dvh.pop(key)

        # Refresh ROIs in DVH tab and DICOM View
        if hasattr(self.main_window, 'dvh'):
            self.main_window.dvh.update_plot(self.main_window)
        self.main_window.dicom_view.update_view()

        # Refresh structure tab
        self.update_content()
示例#26
0
    def generate_roi(self, contours, progress_callback):
        """
        Generates new ROIs based on contour data.
        :param contours: dictionary of contours to turn into ROIs.
        :param progress_callback: signal to update loading progress
        """
        # Initialise variables needed for function
        patient_dict_container = PatientDictContainer()
        dataset_rtss = patient_dict_container.get("dataset_rtss")
        pixmaps = patient_dict_container.get("pixmaps_axial")
        slider_min = 0
        slider_max = len(pixmaps) - 1

        # Get existing ROIs
        existing_rois = []
        rois = patient_dict_container.get("dataset_rtss")
        if rois:
            for roi in rois.StructureSetROISequence:
                existing_rois.append(roi.ROIName)

        # Loop through each isodose level
        for item in contours:
            # Delete ROI if it already exists to recreate it
            if item in existing_rois:
                dataset_rtss = ROI.delete_roi(dataset_rtss, item)

                # Update patient dict container
                current_rois = patient_dict_container.get("rois")
                keys = []
                for key, value in current_rois.items():
                    if value["name"] == item:
                        keys.append(key)
                for key in keys:
                    del current_rois[key]
                patient_dict_container.set("rois", current_rois)

            # Calculate isodose ROI for each slice, skip if slice has no
            # contour data
            for i in range(slider_min, slider_max):
                if not len(contours[item][i]):
                    continue

                # Get required data for calculating ROI
                dataset = patient_dict_container.dataset[i]
                pixlut = patient_dict_container.get("pixluts")
                pixlut = pixlut[dataset.SOPInstanceUID]
                z_coord = dataset.SliceLocation
                curr_slice_uid = patient_dict_container.get("dict_uid")[i]
                dose_pixluts = patient_dict_container.get("dose_pixluts")
                dose_pixluts = dose_pixluts[curr_slice_uid]

                # Loop through each contour for each slice.
                # Convert the pixel points to RCS points, append z value
                single_array = []

                # Loop through each contour
                for j in range(len(contours[item][i])):
                    single_array.append([])
                    # Loop through every second point in the contour
                    for point in contours[item][i][j][::2]:
                        # Transform into dose pixel
                        dose_pixels = [
                            dose_pixluts[0][int(point[1])],
                            dose_pixluts[1][int(point[0])]
                        ]
                        # Transform into RCS pixel
                        rcs_pixels = ROI.pixel_to_rcs(pixlut,
                                                      round(dose_pixels[0]),
                                                      round(dose_pixels[1]))
                        # Append point coordinates to the single array
                        single_array[j].append(rcs_pixels[0])
                        single_array[j].append(rcs_pixels[1])
                        single_array[j].append(z_coord)

                # Create the ROI(s)
                for array in single_array:
                    rtss = ROI.create_roi(dataset_rtss, item, [{
                        'coords': array,
                        'ds': dataset
                    }], "DOSE_REGION")

                    # Save the updated rtss
                    patient_dict_container.set("dataset_rtss", rtss)
                    patient_dict_container.set("rois",
                                               ImageLoading.get_roi_info(rtss))

        progress_callback.emit(("Writing to RT Structure Set", 85))
示例#27
0
    def load(self, interrupt_flag, progress_callback):
        """
        :param interrupt_flag: A threading.Event() object that tells the function to stop loading.
        :param progress_callback: A signal that receives the current progress of the loading.
        :return: PatientDictContainer object containing all values related to the loaded DICOM files.
        """
        progress_callback.emit(("Creating datasets...", 0))
        try:
            path = os.path.dirname(os.path.commonprefix(self.selected_files))  # Gets the common root folder.
            read_data_dict, file_names_dict = ImageLoading.get_datasets(self.selected_files)
        except ImageLoading.NotAllowedClassError:
            raise ImageLoading.NotAllowedClassError

        # Populate the initial values in the PatientDictContainer singleton.
        patient_dict_container = PatientDictContainer()
        patient_dict_container.clear()
        patient_dict_container.set_initial_values(path, read_data_dict, file_names_dict)

        # As there is no way to interrupt a QRunnable, this method must check after every step whether or not the
        # interrupt flag has been set, in which case it will interrupt this method after the currently processing
        # function has finished running. It's not very pretty, and the thread will still run some functions for, in some
        # cases, up to a couple seconds after the close button on the Progress Window has been clicked, however it's
        # the best solution I could come up with. If you have a cleaner alternative, please make your contribution.
        if interrupt_flag.is_set():
            print("stopped")
            return False

        if 'rtss' in file_names_dict and 'rtdose' in file_names_dict:
            self.parent_window.signal_advise_calc_dvh.connect(self.update_calc_dvh)
            self.signal_request_calc_dvh.emit()

            while not self.advised_calc_dvh:
                pass

        if 'rtss' in file_names_dict:
            dataset_rtss = dcmread(file_names_dict['rtss'])

            progress_callback.emit(("Getting ROI info...", 10))
            rois = ImageLoading.get_roi_info(dataset_rtss)

            if interrupt_flag.is_set():  # Stop loading.
                print("stopped")
                return False

            progress_callback.emit(("Getting contour data...", 30))
            dict_raw_contour_data, dict_numpoints = ImageLoading.get_raw_contour_data(dataset_rtss)

            # Determine which ROIs are one slice thick
            dict_thickness = ImageLoading.get_thickness_dict(dataset_rtss, read_data_dict)

            if interrupt_flag.is_set():  # Stop loading.
                print("stopped")
                return False

            progress_callback.emit(("Getting pixel LUTs...", 50))
            dict_pixluts = ImageLoading.get_pixluts(read_data_dict)

            if interrupt_flag.is_set():  # Stop loading.
                print("stopped")
                return False

            # Add RTSS values to PatientDictContainer
            patient_dict_container.set("rois", rois)
            patient_dict_container.set("raw_contour", dict_raw_contour_data)
            patient_dict_container.set("num_points", dict_numpoints)
            patient_dict_container.set("pixluts", dict_pixluts)

            if 'rtdose' in file_names_dict and self.calc_dvh:
                dataset_rtdose = dcmread(file_names_dict['rtdose'])

                # Spawn-based platforms (i.e Windows and MacOS) have a large overhead when creating a new process, which
                # ends up making multiprocessing on these platforms more expensive than linear calculation. As such,
                # multiprocessing is only available on Linux until a better solution is found.
                fork_safe_platforms = ['Linux']
                if platform.system() in fork_safe_platforms:
                    progress_callback.emit(("Calculating DVHs...", 60))
                    raw_dvh = ImageLoading.multi_calc_dvh(dataset_rtss, dataset_rtdose, rois, dict_thickness)
                else:
                    progress_callback.emit(("Calculating DVHs... (This may take a while)", 60))
                    raw_dvh = ImageLoading.calc_dvhs(dataset_rtss, dataset_rtdose, rois, dict_thickness, interrupt_flag)

                if interrupt_flag.is_set():  # Stop loading.
                    print("stopped")
                    return False

                progress_callback.emit(("Converging to zero...", 80))
                dvh_x_y = ImageLoading.converge_to_0_dvh(raw_dvh)

                if interrupt_flag.is_set():  # Stop loading.
                    print("stopped")
                    return False

                # Add DVH values to PatientDictContainer
                patient_dict_container.set("raw_dvh", raw_dvh)
                patient_dict_container.set("dvh_x_y", dvh_x_y)
                patient_dict_container.set("dvh_outdated", False)

                return True
            else:
                return True

        return True
示例#28
0
    def load_images(cls, patient_files, required_classes):
        """
        Loads required datasets for the selected patient.
        :param patient_files: dictionary of classes and patient files.
        :param required_classes: list of classes required for the
                                 selected/current process.
        :return: True if all required datasets found, false otherwise.
        """
        files = []
        found_classes = set()

        # Loop through each item in patient_files
        for key, value in patient_files.items():
            # If the item is an allowed class
            if key in cls.allowed_classes:
                for i in range(len(value)):
                    # Add item's files to the files list
                    files.extend(value[i].get_files())

                # Get the modality name
                modality_name = cls.allowed_classes.get(key).get('name')

                # If the modality name is not found_classes, add it
                if modality_name not in found_classes \
                        and modality_name in required_classes:
                    found_classes.add(modality_name)

        # Get the difference between required classes and found classes
        class_diff = set(required_classes).difference(found_classes)

        # If the dataset is missing required files, pass on it
        if len(class_diff) > 0:
            print("Skipping dataset. Missing required file(s) {}".format(
                class_diff))
            return False

        # Try to get the datasets from the selected files
        try:
            # Convert paths to a common file system representation
            for i, file in enumerate(files):
                files[i] = Path(file).as_posix()
            read_data_dict, file_names_dict = cls.get_datasets(files)
            path = os.path.dirname(
                os.path.commonprefix(list(file_names_dict.values())))
        # Otherwise raise an exception (OnkoDICOM does not support the
        # selected file type)
        except ImageLoading.NotAllowedClassError:
            raise ImageLoading.NotAllowedClassError

        # Populate the initial values in the PatientDictContainer
        patient_dict_container = PatientDictContainer()
        patient_dict_container.clear()
        patient_dict_container.set_initial_values(path, read_data_dict,
                                                  file_names_dict)

        # If an RT Struct is included, set relevant values in the
        # PatientDictContainer
        if 'rtss' in file_names_dict:
            dataset_rtss = dcmread(file_names_dict['rtss'])
            rois = ImageLoading.get_roi_info(dataset_rtss)
            dict_raw_contour_data, dict_numpoints = \
                ImageLoading.get_raw_contour_data(dataset_rtss)
            dict_pixluts = ImageLoading.get_pixluts(read_data_dict)

            # Add RT Struct values to PatientDictContainer
            patient_dict_container.set("rois", rois)
            patient_dict_container.set("raw_contour", dict_raw_contour_data)
            patient_dict_container.set("num_points", dict_numpoints)
            patient_dict_container.set("pixluts", dict_pixluts)

        return True
示例#29
0
    def start(self):
        """
        Goes through the steps of the ISO2ROI conversion.
        :return: True if successful, False if not.
        """
        # Stop loading
        if self.interrupt_flag.is_set():
            # TODO: convert print to logging
            print("Stopped ISO2ROI")
            self.patient_dict_container.clear()
            self.summary = "INTERRUPT"
            return False

        if not self.ready:
            self.summary = "SKIP"
            return False

        # Update progress
        self.progress_callback.emit(("Setting up...", 30))

        # Initialise
        InitialModel.create_initial_model_batch()

        # Stop loading
        if self.interrupt_flag.is_set():
            # TODO: convert print to logging
            print("Stopped ISO2ROI")
            self.patient_dict_container.clear()
            self.summary = "INTERRUPT"
            return False

        # Check if the dataset is complete
        self.progress_callback.emit(("Checking dataset...", 40))
        dataset_complete = ImageLoading.is_dataset_dicom_rt(
            self.patient_dict_container.dataset)

        # Create ISO2ROI object
        iso2roi = ISO2ROI()
        self.progress_callback.emit(("Performing ISO2ROI... ", 50))

        # Stop loading
        if self.interrupt_flag.is_set():
            # TODO: convert print to logging
            print("Stopped ISO2ROI")
            self.patient_dict_container.clear()
            self.summary = "INTERRUPT"
            return False

        if not dataset_complete:
            # Check if RT struct file is missing. If yes, create one and
            # add its data to the patient dict container. Otherwise
            # return
            if not self.patient_dict_container.get("file_rtss"):
                self.progress_callback.emit(("Generating RT Struct", 55))
                self.create_new_rtstruct(self.progress_callback)

        # Get isodose levels to turn into ROIs
        isodose_levels = \
            iso2roi.get_iso_levels(data_path('batch_isodoseRoi.csv'))

        # Stop loading
        if self.interrupt_flag.is_set():
            # TODO: convert print to logging
            print("Stopped ISO2ROI")
            self.patient_dict_container.clear()
            self.summary = "INTERRUPT"
            return False

        # Calculate boundaries
        self.progress_callback.emit(("Calculating boundaries...", 60))
        boundaries = iso2roi.calculate_isodose_boundaries(isodose_levels)

        # Return if boundaries could not be calculated
        if not boundaries:
            print("Boundaries could not be calculated.")
            self.summary = "ISO_NO_RX_DOSE"
            return False

        # Generate ROIs
        self.progress_callback.emit(("Generating ROIs...", 80))
        iso2roi.generate_roi(boundaries, self.progress_callback)

        # Save new RTSS
        self.progress_callback.emit(("Saving RT Struct...", 90))
        self.save_rtss()
        return True
def test_merge_rtss(qtbot, test_object):
    """Test merging rtss. This function creates a new rtss, then merges
    the new rtss with the old rtss and asserts that duplicated ROIs
    will be overwritten when the other being merged.

    :param test_object: test_object function, for accessing the shared
    TestStructureTab object.
    """
    patient_dict_container = PatientDictContainer()

    # Create a new rtss
    dataset = patient_dict_container.dataset[0]
    rtss_path = Path(patient_dict_container.path).joinpath('rtss.dcm')
    new_rtss = create_initial_rtss_from_ct(
        dataset, rtss_path,
        ImageLoading.get_image_uid_list(patient_dict_container.dataset))

    # Set ROIs
    rois = ImageLoading.get_roi_info(new_rtss)
    patient_dict_container.set("rois", rois)

    # Add a new ROI into the new rtss with the name of the first ROI in
    # the old rtss
    roi_name = test_object.rois.get(1)["name"]
    roi_coordinates = [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0]
    new_rtss = create_roi(new_rtss, roi_name, [{
        'coords': roi_coordinates,
        'ds': dataset
    }])

    # Add a new ROI with a new name
    roi_name = "NewTestROI"
    new_rtss = create_roi(new_rtss, roi_name, [{
        'coords': roi_coordinates,
        'ds': dataset
    }])

    # Set ROIs
    rois = ImageLoading.get_roi_info(new_rtss)
    patient_dict_container.set("rois", rois)
    patient_dict_container.set("existing_file_rtss",
                               patient_dict_container.get("file_rtss"))
    patient_dict_container.set("dataset_rtss", new_rtss)

    # Merge the old and new rtss
    structure_tab = StructureTab()
    structure_tab.show_modified_indicator()
    qtbot.addWidget(structure_tab)

    def test_message_window():
        messagebox = structure_tab.findChild(QtWidgets.QMessageBox)
        assert messagebox is not None
        yes_button = messagebox.buttons()[1]
        qtbot.mouseClick(yes_button, QtCore.Qt.LeftButton, delay=1)

    QtCore.QTimer.singleShot(1000, test_message_window)

    structure_tab.save_new_rtss_to_fixed_image_set(auto=True)

    merged_rtss = pydicom.read_file(patient_dict_container.get("file_rtss"))
    merged_rois = ImageLoading.get_roi_info(merged_rtss)
    assert (len(test_object.rois) + 1 == len(merged_rois))