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'])
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()
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'
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)
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
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)
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)
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)
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]
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]
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
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", [])
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
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)
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']]
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
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
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)
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)
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()
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))
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()
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))
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
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
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))