def register_images(image_1, image_2): """ Registers the moving and fixed image. Args: image_1 (Image Matrix) image_2 (Image Matrix) Return: img_ct (Array) tfm (sitk.CompositeTransform) """ store_object_into_dcm = False # Check to see if the imageWindowing.csv file exists if os.path.exists(data_path('imageFusion.json')): # If it exists, read data from file into the dictionary with open(data_path("imageFusion.json"), "r") as file_input: dict_fusion = json.load(file_input) img_ct, tfm = linear_registration( image_1, image_2, reg_method=dict_fusion["reg_method"], metric=dict_fusion["metric"], optimiser=dict_fusion["optimiser"], shrink_factors=dict_fusion["shrink_factors"], smooth_sigmas=dict_fusion["smooth_sigmas"], sampling_rate=dict_fusion["sampling_rate"], final_interp=dict_fusion["final_interp"], number_of_iterations=dict_fusion["number_of_iterations"], default_value=dict_fusion["default_value"], verbose=False) # Flag for when a given registration method is rigid. # As DICOM Frame of Reference Transformation Matrix allows # RIGID, RIGID_SCALE or AFFINE if dict_fusion["reg_method"] == 'rigid': store_object_into_dcm = True else: # If csv does not exist, initialize registration normally img_ct, tfm = linear_registration(image_1, image_2, shrink_factors=[8], smooth_sigmas=[10], reg_method='rigid', verbose=False) store_object_into_dcm = True return img_ct, tfm, store_object_into_dcm
def find_roi_names(self): """ Return a list of ROI names in the RTSS that are standard organ names. :return: list of ROI names. """ # Get organ names and FMA IDs if they have not been populated if not self.organ_names: # Get standard organ names with open(data_path('organName.csv'), 'r') as f: csv_input = csv.reader(f) header = next(f) # Ignore the "header" of the column for row in csv_input: self.organ_names.append(row[0]) self.fma_ids[row[0]] = row[1] f.close() rtss = self.patient_dict_container.dataset['rtss'] rois = [] # Loop through each ROI in the RT Struct for i in range(len(rtss.StructureSetROISequence)): # Get the ROI name roi_name = rtss.StructureSetROISequence[i].ROIName # Add ROI name to the list if roi_name in self.organ_names: rois.append(roi_name) return rois
def draw_roi_polygons(self, roi_id, polygons, roi_color=None): """ Draw ROI polygons on the image slice :param roi_id: ROI number :param polygons: List of ROI polygons :param roi_color: colors for ROIs used when displaying selected rois in manipulate ROI window """ if roi_color is None: color = self.roi_color[roi_id] else: color = roi_color[roi_id] with open(data_path('line&fill_configuration'), 'r') as stream: elements = stream.readlines() if len(elements) > 0: roi_line = int(elements[0].replace('\n', '')) roi_opacity = int(elements[1].replace('\n', '')) line_width = float(elements[4].replace('\n', '')) else: roi_line = 1 roi_opacity = 10 line_width = 2.0 stream.close() roi_opacity = int((roi_opacity / 100) * 255) color.setAlpha(roi_opacity) pen_color = QtGui.QColor(color.red(), color.green(), color.blue()) pen = self.get_qpen(pen_color, roi_line, line_width) for i in range(len(polygons)): self.scene.addPolygon(polygons[i], pen, QtGui.QBrush(color))
def init_standard_names(self): """ Create two lists containing standard organ and standard volume names as set by the Add-On options. """ with open(data_path('organName.csv'), 'r') as f: self.standard_organ_names = [] csv_input = csv.reader(f) header = next(f) # Ignore the "header" of the column for row in csv_input: self.standard_organ_names.append(row[0]) with open(data_path('volumeName.csv'), 'r') as f: self.standard_volume_names = [] csv_input = csv.reader(f) header = next(f) # Ignore the "header" of the column for row in csv_input: self.standard_volume_names.append(row[1])
def get_standard_names(self): """ Get standard organ names and prefix types. """ # Get standard organ names with open(data_path('organName.csv'), 'r') as f: csv_input = csv.reader(f) header = next(f) # Ignore the "header" of the column for row in csv_input: self.organ_names.append(row[0]) self.organ_names_lowercase.append(row[0].lower()) f.close() # Get standard volume prefixes with open(data_path('volumeName.csv'), 'r') as f: csv_input = csv.reader(f) header = next(f) # Ignore the "header" of the column for row in csv_input: self.volume_prefixes.append(row[1]) f.close()
def save_isodoses(self): """ Called when batch conversion process starts, to save any changes that may have been made to the table. """ with open(data_path('batch_isodoseRoi.csv'), 'w', newline="") \ as stream: writer = csv.writer(stream) for row in range(self.table_roi.rowCount()): rowdata = [] for column in range(self.table_roi.columnCount()): item = self.table_roi.item(row, column) if item is not None: rowdata.append(item.text()) else: rowdata.append('') writer.writerow(rowdata)
def isodose_display(self): """ Display isodoses on the DICOM Image. """ slider_id = self.slider.value() curr_slice_uid = self.patient_dict_container.get("dict_uid")[slider_id] z = self.patient_dict_container.dataset[ slider_id].ImagePositionPatient[2] dataset_rtdose = self.patient_dict_container.dataset['rtdose'] grid = get_dose_grid(dataset_rtdose, float(z)) if not (grid == []): # sort selected_doses in ascending order so that the high dose isodose washes # paint over the lower dose isodose washes for sd in sorted( self.patient_dict_container.get("selected_doses")): dose_level = sd * self.patient_dict_container.get("rx_dose_in_cgray") / \ (dataset_rtdose.DoseGridScaling * 10000) contours = measure.find_contours(grid, dose_level) polygons = self.calc_dose_polygon( self.patient_dict_container.get("dose_pixluts") [curr_slice_uid], contours) brush_color = self.iso_color[sd] with open(data_path('line&fill_configuration'), 'r') as stream: elements = stream.readlines() if len(elements) > 0: iso_line = int(elements[2].replace('\n', '')) iso_opacity = int(elements[3].replace('\n', '')) line_width = float(elements[4].replace('\n', '')) else: iso_line = 2 iso_opacity = 5 line_width = 2.0 stream.close() iso_opacity = int((iso_opacity / 100) * 255) brush_color.setAlpha(iso_opacity) pen_color = QtGui.QColor(brush_color.red(), brush_color.green(), brush_color.blue()) pen = self.get_qpen(pen_color, iso_line, line_width) for i in range(len(polygons)): self.scene.addPolygon(polygons[i], pen, QtGui.QBrush(brush_color))
def start_conversion(self, interrupt_flag, progress_callback): """ Goes the the steps of the iso2roi conversion. :param interrupt_flag: interrupt flag to stop process :param progress_callback: signal that receives the current progress of the loading. """ progress_callback.emit(("Validating Datasets", 0)) # Stop loading if interrupt_flag.is_set(): # TODO: convert print to logging print("Stopped ISO2ROI") return False # Get isodose levels to turn into ROIs isodose_levels = self.get_iso_levels(data_path('isodoseRoi.csv')) # Stop loading if interrupt_flag.is_set(): # TODO: convert print to logging print("Stopped ISO2ROI") return False # Calculate dose boundaries progress_callback.emit(("Calculating Boundaries", 50)) boundaries = self.calculate_isodose_boundaries(isodose_levels) # Return if boundaries could not be calculated if not boundaries: # TODO: convert print to logging print("Boundaries could not be calculated.") return # Stop loading if interrupt_flag.is_set(): # TODO: convert print to logging print("Stopped ISO2ROI") return False progress_callback.emit(("Generating ROIs", 75)) self.generate_roi(boundaries, progress_callback) progress_callback.emit(("Reloading Window. Please Wait...", 95))
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 create_initial_model_batch(): """ This function initializes all the attributes in the PatientDictContainer required for the operation of batch processing. It is a modified version of create_initial_model. This function only sets RTSS values in the PatientDictContainer if an RTSS exists. If one does not exist it will only be created if needed, whereas the original create_initial_model assumes that one is always created. This function also does not set SR attributes in the PatientDictContainer, as SRs are only needed for SR2CSV functions, which do not require the use of the PatientDictContainer. """ ############################## # LOAD PATIENT INFORMATION # ############################## patient_dict_container = PatientDictContainer() dataset = patient_dict_container.dataset filepaths = patient_dict_container.filepaths patient_dict_container.set("rtss_modified", False) if 'WindowWidth' in dataset[0]: if isinstance(dataset[0].WindowWidth, pydicom.valuerep.DSfloat): window = int(dataset[0].WindowWidth) elif isinstance(dataset[0].WindowWidth, pydicom.multival.MultiValue): window = int(dataset[0].WindowWidth[1]) else: window = int(400) if 'WindowCenter' in dataset[0]: if isinstance(dataset[0].WindowCenter, pydicom.valuerep.DSfloat): level = int(dataset[0].WindowCenter) elif isinstance(dataset[0].WindowCenter, pydicom.multival.MultiValue): level = int(dataset[0].WindowCenter[1]) else: level = int(800) patient_dict_container.set("window", window) patient_dict_container.set("level", level) # Check to see if the imageWindowing.csv file exists if os.path.exists(data_path('imageWindowing.csv')): # If it exists, read data from file into the self.dict_windowing # variable dict_windowing = {} with open(data_path('imageWindowing.csv'), "r") \ as fileInput: next(fileInput) dict_windowing["Normal"] = [window, level] for row in fileInput: # Format: Organ - Scan - Window - Level items = [item for item in row.split(',')] dict_windowing[items[0]] = [int(items[2]), int(items[3])] else: # If csv does not exist, initialize dictionary with default values dict_windowing = { "Normal": [window, level], "Lung": [1600, -300], "Bone": [1400, 700], "Brain": [160, 950], "Soft Tissue": [400, 800], "Head and Neck": [275, 900] } patient_dict_container.set("dict_windowing", dict_windowing) pixel_values = convert_raw_data(dataset) # Calculate the ratio between x axis and y axis of 3 views pixmap_aspect = {} pixel_spacing = dataset[0].PixelSpacing slice_thickness = dataset[0].SliceThickness pixmap_aspect["axial"] = pixel_spacing[1] / pixel_spacing[0] pixmap_aspect["sagittal"] = pixel_spacing[1] / slice_thickness pixmap_aspect["coronal"] = slice_thickness / pixel_spacing[0] pixmaps_axial, pixmaps_coronal, pixmaps_sagittal = \ get_pixmaps(pixel_values, window, level, pixmap_aspect) patient_dict_container.set("pixmaps_axial", pixmaps_axial) patient_dict_container.set("pixmaps_coronal", pixmaps_coronal) patient_dict_container.set("pixmaps_sagittal", pixmaps_sagittal) patient_dict_container.set("pixel_values", pixel_values) patient_dict_container.set("pixmap_aspect", pixmap_aspect) basic_info = get_basic_info(dataset[0]) patient_dict_container.set("basic_info", basic_info) patient_dict_container.set("dict_uid", dict_instance_uid(dataset)) # Set RTSS attributes if patient_dict_container.has_modality("rtss"): patient_dict_container.set("file_rtss", filepaths['rtss']) patient_dict_container.set("dataset_rtss", dataset['rtss']) dict_raw_contour_data, dict_numpoints = \ ImageLoading.get_raw_contour_data(dataset['rtss']) patient_dict_container.set("raw_contour", dict_raw_contour_data) dicom_tree_rtss = DicomTree(filepaths['rtss']) patient_dict_container.set("dict_dicom_tree_rtss", dicom_tree_rtss.dict) patient_dict_container.set( "list_roi_numbers", ordered_list_rois(patient_dict_container.get("rois"))) patient_dict_container.set("selected_rois", []) patient_dict_container.set("dict_polygons_axial", {}) patient_dict_container.set("dict_polygons_sagittal", {}) patient_dict_container.set("dict_polygons_coronal", {}) # Set RTDOSE attributes if patient_dict_container.has_modality("rtdose"): dicom_tree_rtdose = DicomTree(filepaths['rtdose']) patient_dict_container.set("dict_dicom_tree_rtdose", dicom_tree_rtdose.dict) patient_dict_container.set("dose_pixluts", get_dose_pixluts(dataset)) patient_dict_container.set("selected_doses", []) # overwritten if RTPLAN is present. patient_dict_container.set("rx_dose_in_cgray", 1) # Set RTPLAN attributes if patient_dict_container.has_modality("rtplan"): # the TargetPrescriptionDose is type 3 (optional), so it may not be # there However, it is preferable to the sum of the beam doses # DoseReferenceStructureType is type 1 (value is mandatory), but it # can have a value of ORGAN_AT_RISK rather than TARGET in which case # there will *not* be a TargetPrescriptionDose and even if it is # TARGET, that's no guarantee that TargetPrescriptionDose will be # encoded and have a value rx_dose_in_cgray = calculate_rx_dose_in_cgray(dataset["rtplan"]) patient_dict_container.set("rx_dose_in_cgray", rx_dose_in_cgray) dicom_tree_rtplan = DicomTree(filepaths['rtplan']) patient_dict_container.set("dict_dicom_tree_rtplan", dicom_tree_rtplan.dict)
def create_table_view(self): """ Create a table to hold all the isodose levels for ISO2ROI. """ # Create table self.table_roi = QtWidgets.QTableWidget(self) self.table_roi.setStyleSheet("background-color: rgb(255, 255, 255);") self.table_roi.setColumnCount(4) self.table_roi.verticalHeader().hide() self.table_roi.setHorizontalHeaderLabels( [" Isodose Level ", " Unit ", " ROI Name ", " Notes "]) self.table_roi.horizontalHeaderItem(0).setTextAlignment( QtCore.Qt.AlignLeft) self.table_roi.horizontalHeaderItem(1).setTextAlignment( QtCore.Qt.AlignLeft) self.table_roi.horizontalHeaderItem(2).setTextAlignment( QtCore.Qt.AlignLeft) self.table_roi.horizontalHeaderItem(3).setTextAlignment( QtCore.Qt.AlignLeft) roi_from_isodose_header = self.table_roi.horizontalHeader() roi_from_isodose_header.setSectionResizeMode( 0, QtWidgets.QHeaderView.Stretch) roi_from_isodose_header.setSectionResizeMode( 1, QtWidgets.QHeaderView.Stretch) roi_from_isodose_header.setSectionResizeMode( 2, QtWidgets.QHeaderView.Stretch) roi_from_isodose_header.setSectionResizeMode( 3, QtWidgets.QHeaderView.Stretch) # Removing the ability to edit tables with immediate click self.table_roi.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers | QtWidgets.QTreeView.NoEditTriggers) # Add table to the main layout self.main_layout.addWidget(self.table_roi) # Add right click options self.table_roi.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.table_roi.customContextMenuRequested.connect( self.on_custom_context_menu_requested_roi) # Populate the table with data from batch_isodoseRoi.csv with open(data_path('batch_isodoseRoi.csv'), "r") as fileInput: # Clear table to prevent displaying data multiple times self.table_roi.setRowCount(0) # Loop through each row for i, row in enumerate(fileInput): items = [ QtWidgets.QTableWidgetItem(str(item.replace('\n', ''))) for item in row.split(',') ] # Add row to table self.table_roi.insertRow(i) self.table_roi.setItem(i, 0, items[0]) self.table_roi.setItem(i, 1, items[1]) self.table_roi.setItem(i, 2, items[2]) if len(items) > 3: self.table_roi.setItem(i, 3, items[3])
def fill_tables(self): # Fill the Windowing table with open(data_path("imageWindowing.csv"), "r") as file_input: next(file_input) i = 0 for row in file_input: items = [ QTableWidgetItem(str(item.replace("\n", ""))) for item in row.split(",") ] if i >= self.table_view.rowCount(): self.table_view.setRowCount(self.table_view.rowCount() + 1) self.table_view.setItem(i, 0, items[0]) self.table_view.setItem(i, 1, items[1]) self.table_view.setItem(i, 2, items[2]) self.table_view.setItem(i, 3, items[3]) i += 1 # organ names table with open(data_path("organName.csv"), "r") as file_input: next(file_input) i = 0 for row in file_input: items = [ QTableWidgetItem(str(item.replace("\n", ""))) for item in row.split(",") ] if i >= self.table_organ.rowCount(): self.table_organ.setRowCount(self.table_organ.rowCount() + 1) self.table_organ.setItem(i, 0, items[0]) self.table_organ.setItem(i, 1, items[1]) self.table_organ.setItem(i, 2, items[2]) if len(items) > 3: self.table_organ.setItem(i, 3, items[3]) i += 1 # volume name table with open(data_path("volumeName.csv"), "r") as file_input: i = 0 for row in file_input: items = [ QTableWidgetItem(str(item.replace("\n", ""))) for item in row.split(",") ] if i >= self.table_volume.rowCount(): self.table_volume.setRowCount( self.table_volume.rowCount() + 1) self.table_volume.setItem(i, 0, items[0]) self.table_volume.setItem(i, 1, items[1]) i += 1 # roi isodose table with open(data_path('isodoseRoi.csv'), "r") as fileInput: # Clear table to prevent displaying data multiple times self.table_roi.setRowCount(0) # Loop through each row for i, row in enumerate(fileInput): items = [ QTableWidgetItem(str(item.replace('\n', ''))) for item in row.split(',') ] # Add row to table self.table_roi.insertRow(i) self.table_roi.setItem(i, 0, items[0]) self.table_roi.setItem(i, 1, items[1]) self.table_roi.setItem(i, 2, items[2]) if len(items) > 3: self.table_roi.setItem(i, 3, items[3]) # patient hash ID table, which is just for displaying all the # patients anonymized byt the software since intallation with open(data_path("patientHash.csv"), "r") as file_input: next(file_input, None) i = 0 for row in file_input: items = [ QTableWidgetItem(str(item.replace("\n", ""))) for item in row.split(",") ] if len(items) >= 2: if i >= self.table_ids.rowCount(): self.table_ids.setRowCount(self.table_ids.rowCount() + 1) self.table_ids.setItem(i, 0, items[0]) self.table_ids.setItem(i, 1, items[1]) i += 1 # Image Fusion try: with open(data_path("imageFusion.json"), "r") as file_input: data = json.load(file_input) for key in data: self.image_fusion_add_on_options.set_value(key, data[key]) self.image_fusion_add_on_options.set_gridLayout() except FileNotFoundError: self.image_fusion_add_on_options.set_fast_mode() QMessageBox.critical( self, "File Not Found!", "Could not find imageFusion.json file for\nimage fusion " "add-ons.")
def accepting(self): """ If APPLY is clicked, save the contents of each option and table into their corresponding files. """ save_flag = True # starting save # Saving the Windowing options with open(data_path("imageWindowing.csv"), "w", newline="") as stream: writer = csv.writer(stream) writer.writerow(["Organ", "Scan", "Window", "Level"]) for row in range(self.table_view.rowCount()): rowdata = [] for column in range(self.table_view.columnCount()): item = self.table_view.item(row, column) if item is not None: rowdata.append(item.text()) else: rowdata.append("") writer.writerow(rowdata) # saving the Standard Organ names with open(data_path("organName.csv"), "w", newline="") as stream: writer = csv.writer(stream) writer.writerow(["Standard Name", "FMA ID", "Organ", "Url"]) for row in range(self.table_organ.rowCount()): rowdata = [] for column in range(self.table_organ.columnCount()): item = self.table_organ.item(row, column) if item is not None: rowdata.append(item.text()) else: rowdata.append("") writer.writerow(rowdata) # Saving the Standard Volume Names with open(data_path("volumeName.csv"), "w", newline="") as stream: writer = csv.writer(stream) for row in range(self.table_volume.rowCount()): rowdata = [] for column in range(self.table_volume.columnCount()): item = self.table_volume.item(row, column) if item is not None: rowdata.append(item.text()) else: rowdata.append("") writer.writerow(rowdata) # saves the new ROI from Isodoses with open(data_path('isodoseRoi.csv'), 'w', newline="") as stream: writer = csv.writer(stream) for row in range(self.table_roi.rowCount()): rowdata = [] for column in range(self.table_roi.columnCount()): item = self.table_roi.item(row, column) if item is not None: rowdata.append(item.text()) else: rowdata.append('') writer.writerow(rowdata) # save configuration file with open(data_path("line&fill_configuration"), "w") as stream: stream.write(str(self.line_style_ROI.currentIndex())) stream.write("\n") stream.write(str(self.opacity_ROI.value())) stream.write("\n") stream.write(str(self.line_style_ISO.currentIndex())) stream.write("\n") stream.write(str(self.opacity_ISO.value())) stream.write("\n") stream.write(str(self.line_width.currentText())) stream.write("\n") stream.close() # Save the default directory and clinical data CSV directory configuration = Configuration() try: new_dir = self.change_default_directory. \ change_default_directory_input_box.text() configuration.update_default_directory(new_dir) new_clinical_data_csv_dir = \ self.clinical_data_csv_dir_options. \ clinical_data_csv_dir_input_box.text() configuration.update_clinical_data_csv_dir( new_clinical_data_csv_dir) except SqlError: configuration.set_up_config_db() QMessageBox.critical( self, "Config file error", "Failed to update default directory.\nPlease try again.") save_flag = False # Image Fusion try: image_fusion_add_on_options_values = \ self.image_fusion_add_on_options.get_values_from_UI() self.image_fusion_add_on_options.check_parameter() self.image_fusion_add_on_options.warning_label.setText("") json_file = open(data_path("imageFusion.json"), "w", newline="") json.dump(image_fusion_add_on_options_values, json_file) except ValueError: QMessageBox.critical( self, "Image Fusion Error", "The number of parameters for 'Smooth_Sigmas' and " "'Shrink_Factors' do not match.\nPlease try again.") save_flag = False else: json_file.close() if save_flag: QMessageBox.about(self, "Success", "Changes were successfully applied") # Close the Add-On Options Window after saving if hasattr(self.window, 'structures_tab'): self.window.structures_tab.init_standard_names() self.window.structures_tab.update_content() self.close()
def __init__(self, window): # initialization function super(AddOnOptions, self).__init__() # read configuration file for line and fill options with open(data_path("line&fill_configuration"), "r") \ as stream: elements = stream.readlines() # if file is not empty, each line represents the last saved # configuration in the given order if len(elements) > 0: roi_line = int(elements[0].replace("\n", "")) roi_opacity = int(elements[1].replace("\n", "")) iso_line = int(elements[2].replace("\n", "")) iso_opacity = int(elements[3].replace("\n", "")) line_width = float(elements[4].replace("\n", "")) else: # if file is empty for some reason, use the default # measures below roi_line = 1 roi_opacity = 10 iso_line = 2 iso_opacity = 5 line_width = 2.0 stream.close() # initialise the UI self.window = window self.setup_ui(self, roi_line, roi_opacity, iso_line, iso_opacity, line_width) # This data is used to create the tree view of functionalities # on the left of the window. Each entry will be used as a button # to change the view on the right accordingly. data = [ { "level": 0, "dbID": 442, "parent_ID": 6, "short_name": "User Options" }, { "level": 1, "dbID": 522, "parent_ID": 442, "short_name": "Image Windowing", }, { "level": 1, "dbID": 556, "parent_ID": 442, "short_name": "Standard Organ Names", }, { "level": 1, "dbID": 527, "parent_ID": 442, "short_name": "Standard Volume Names", }, { 'level': 1, 'dbID': 528, 'parent_ID': 442, 'short_name': 'Create ROIs from Isodoses' }, { "level": 1, "dbID": 520, "parent_ID": 442, "short_name": "Patient ID - Hash ID", }, { "level": 1, "dbID": 523, "parent_ID": 442, "short_name": "Line & Fill configuration", }, { "level": 0, "dbID": 446, "parent_ID": 6, "short_name": "Configuration" }, { "level": 1, "dbID": 521, "parent_ID": 446, "short_name": "Default directory", }, { "level": 1, "dbID": 530, "parent_ID": 446, "short_name": "Clinical Data CSV File", }, { "level": 0, "dbID": 600, "parent_ID": 6, "short_name": "Image Fusion", }, { "level": 1, "dbID": 601, "parent_ID": 600, "short_name": "Auto-Registration", }, ] # create a model for the tree view of options and attach the # data self.model = QtGui.QStandardItemModel() self.treeList.setModel(self.model) self.import_data(data) self.treeList.expandAll() # fill the corresponding tables with the corresponding data from # the csv files self.fill_tables() self.treeList.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers ) # make the tree entries uneditable # Functionalities of the Apply and Cancel button self.cancel_button.clicked.connect( self.close) # Close the window by discarding all changes self.apply_button.clicked.connect(self.accepting) # Connecting the functionalities of the view dependant buttons self.add_new_window.clicked.connect(self.new_windowing) self.delete_window.clicked.connect(self.remove_windowing) self.add_standard_organ_name.clicked.connect(self.new_organ) self.add_standard_volume_name.clicked.connect(self.new_volume) self.add_new_roi.clicked.connect(self.new_isodose) self.delete_roi.clicked.connect(self.remove_isodose) self.import_organ_csv.clicked.connect(self.import_organs) # adding the right click menus for each table self.table_view.setContextMenuPolicy( QtCore.Qt.ContextMenuPolicy.CustomContextMenu) self.table_view.customContextMenuRequested.connect( self.onCustomContextMenuRequestedWindow) self.table_organ.setContextMenuPolicy( QtCore.Qt.ContextMenuPolicy.CustomContextMenu) self.table_organ.customContextMenuRequested.connect( self.onCustomContextMenuRequestedOrgan) self.table_volume.setContextMenuPolicy( QtCore.Qt.ContextMenuPolicy.CustomContextMenu) self.table_volume.customContextMenuRequested.connect( self.onCustomContextMenuRequestedVolume) self.table_roi.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.table_roi.customContextMenuRequested.connect( self.onCustomContextMenuRequestedRoi) # making the URL column a double clicked link self.table_organ.itemDoubleClicked.connect(self.open_link)
matplotlib.cbook.handle_exceptions = "print" # default matplotlib.cbook.handle_exceptions = "raise" matplotlib.cbook.handle_exceptions = "ignore" # matplotlib.cobook.handle_exceptions = my_logger # will be called with exception as argument # The following code are global functions and data/variables used by both # Clinical Data form and Display classes # This variable holds the errors messages of the Clinical data form message = "" # reading the csv files containing the available diseases with open(data_path('ICD10_Topography.csv'), 'r') as f: reader = csv.reader(f) icd = list(reader) icd.pop(0) with open(data_path('ICD10_Topography_C.csv'), 'r') as f: reader = csv.reader(f) icdc = list(reader) icdc.pop(0) with open(data_path('ICD10_Morphology.csv'), 'r') as f: reader = csv.reader(f) hist = list(reader) hist.pop(0) # Creating the arrays containing the above data and formatting them # appropriately new_icd = []
def create_moving_model(): """ This function initializes all the attributes in the MovingDictContainer model required for the operation of the main window. This should be called before the main window's components are constructed, but after the initial values of the MovingDictContainer instance are set (i.e. dataset and filepaths). """ ############################## # LOAD PATIENT INFORMATION # ############################## moving_dict_container = MovingDictContainer() dataset = moving_dict_container.dataset filepaths = moving_dict_container.filepaths moving_dict_container.set("rtss_modified_moving", False) # Determine if dataset is CT for aditional rescaling is_ct = False if dataset[0].Modality == "CT": is_ct = True if 'WindowWidth' in dataset[0]: if isinstance(dataset[0].WindowWidth, pydicom.valuerep.DSfloat): window = int(dataset[0].WindowWidth) elif isinstance(dataset[0].WindowWidth, pydicom.multival.MultiValue): window = int(dataset[0].WindowWidth[1]) else: window = int(400) if 'WindowCenter' in dataset[0]: if isinstance(dataset[0].WindowCenter, pydicom.valuerep.DSfloat): level = int(dataset[0].WindowCenter) - window / 2 elif isinstance(dataset[0].WindowCenter, pydicom.multival.MultiValue): level = int(dataset[0].WindowCenter[1]) - window / 2 if is_ct: level += CT_RESCALE_INTERCEPT else: level = int(800) moving_dict_container.set("window", window) moving_dict_container.set("level", level) # Check to see if the imageWindowing.csv file exists if os.path.exists(data_path('imageWindowing.csv')): # If it exists, read data from file into the self.dict_windowing # variable dict_windowing = {} with open(data_path('imageWindowing.csv'), "r") \ as fileInput: next(fileInput) dict_windowing["Normal"] = [window, level] for row in fileInput: # Format: Organ - Scan - Window - Level items = [item for item in row.split(',')] dict_windowing[items[0]] = [int(items[2]), int(items[3])] else: # If csv does not exist, initialize dictionary with default # values dict_windowing = { "Normal": [window, level], "Lung": [1600, -300], "Bone": [1400, 700], "Brain": [160, 950], "Soft Tissue": [400, 800], "Head and Neck": [275, 900] } moving_dict_container.set("dict_windowing_moving", dict_windowing) if not moving_dict_container.has_attribute("scaled"): moving_dict_container.set("scaled", True) pixel_values = convert_raw_data(dataset, False, is_ct) else: pixel_values = convert_raw_data(dataset, True) # Calculate the ratio between x axis and y axis of 3 views pixmap_aspect = {} pixel_spacing = dataset[0].PixelSpacing slice_thickness = dataset[0].SliceThickness pixmap_aspect["axial"] = pixel_spacing[1] / pixel_spacing[0] pixmap_aspect["sagittal"] = pixel_spacing[1] / slice_thickness pixmap_aspect["coronal"] = slice_thickness / pixel_spacing[0] pixmaps_axial, pixmaps_coronal, pixmaps_sagittal = \ get_pixmaps(pixel_values, window, level, pixmap_aspect) moving_dict_container.set("pixmaps_axial", pixmaps_axial) moving_dict_container.set("pixmaps_coronal", pixmaps_coronal) moving_dict_container.set("pixmaps_sagittal", pixmaps_sagittal) moving_dict_container.set("pixel_values", pixel_values) moving_dict_container.set("pixmap_aspect", pixmap_aspect) basic_info = get_basic_info(dataset[0]) moving_dict_container.set("basic_info", basic_info) moving_dict_container.set("dict_uid", dict_instance_uid(dataset)) # Set RTSS attributes if moving_dict_container.has_modality("rtss"): moving_dict_container.set("file_rtss", filepaths['rtss']) moving_dict_container.set("dataset_rtss", dataset['rtss']) dicom_tree_rtss = DicomTree(filepaths['rtss']) moving_dict_container.set("dict_dicom_tree_rtss", dicom_tree_rtss.dict) moving_dict_container.set( "list_roi_numbers", ordered_list_rois(moving_dict_container.get("rois"))) moving_dict_container.set("selected_rois", []) moving_dict_container.set("dict_polygons", {}) # Set RTDOSE attributes if moving_dict_container.has_modality("rtdose"): dicom_tree_rtdose = DicomTree(filepaths['rtdose']) moving_dict_container.set("dict_dicom_tree_rtdose", dicom_tree_rtdose.dict) moving_dict_container.set("dose_pixluts", get_dose_pixluts(dataset)) moving_dict_container.set("selected_doses", []) # This will be overwritten if an RTPLAN is present. moving_dict_container.set("rx_dose_in_cgray", 1) # Set RTPLAN attributes if moving_dict_container.has_modality("rtplan"): rx_dose_in_cgray = calculate_rx_dose_in_cgray(dataset["rtplan"]) moving_dict_container.set("rx_dose_in_cgray", rx_dose_in_cgray) dicom_tree_rtplan = DicomTree(filepaths['rtplan']) moving_dict_container.set("dict_dicom_tree_rtplan", dicom_tree_rtplan.dict)
def create_initial_model(): """ This function initializes all the attributes in the PatientDictContainer model required for the operation of the main window. This should be called before the main window's components are constructed, but after the initial values of the PatientDictContainer instance are set (i.e. dataset and filepaths). """ ############################## # LOAD PATIENT INFORMATION # ############################## patient_dict_container = PatientDictContainer() dataset = patient_dict_container.dataset filepaths = patient_dict_container.filepaths patient_dict_container.set("rtss_modified", False) # Determine if dataset is CT for aditional rescaling is_ct = False if dataset[0].Modality == "CT": is_ct = True if 'WindowWidth' in dataset[0]: if isinstance(dataset[0].WindowWidth, pydicom.valuerep.DSfloat): window = int(dataset[0].WindowWidth) elif isinstance(dataset[0].WindowWidth, pydicom.multival.MultiValue): window = int(dataset[0].WindowWidth[1]) else: window = int(400) if 'WindowCenter' in dataset[0]: if isinstance(dataset[0].WindowCenter, pydicom.valuerep.DSfloat): level = int(dataset[0].WindowCenter) - window / 2 elif isinstance(dataset[0].WindowCenter, pydicom.multival.MultiValue): level = int(dataset[0].WindowCenter[1]) - window / 2 if is_ct: level += CT_RESCALE_INTERCEPT else: level = int(800) patient_dict_container.set("window", window) patient_dict_container.set("level", level) # Check to see if the imageWindowing.csv file exists if os.path.exists(data_path('imageWindowing.csv')): # If it exists, read data from file into the self.dict_windowing # variable dict_windowing = {} with open(data_path('imageWindowing.csv'), "r") \ as fileInput: next(fileInput) dict_windowing["Normal"] = [window, level] for row in fileInput: # Format: Organ - Scan - Window - Level items = [item for item in row.split(',')] dict_windowing[items[0]] = [int(items[2]), int(items[3])] else: # If csv does not exist, initialize dictionary with default values dict_windowing = { "Normal": [window, level], "Lung": [1600, -300], "Bone": [1400, 700], "Brain": [160, 950], "Soft Tissue": [400, 800], "Head and Neck": [275, 900] } patient_dict_container.set("dict_windowing", dict_windowing) if not patient_dict_container.has_attribute("scaled"): patient_dict_container.set("scaled", True) pixel_values = convert_raw_data(dataset, False, is_ct) else: pixel_values = convert_raw_data(dataset, True) # Calculate the ratio between x axis and y axis of 3 views pixmap_aspect = {} pixel_spacing = dataset[0].PixelSpacing slice_thickness = dataset[0].SliceThickness pixmap_aspect["axial"] = pixel_spacing[1] / pixel_spacing[0] pixmap_aspect["sagittal"] = pixel_spacing[1] / slice_thickness pixmap_aspect["coronal"] = slice_thickness / pixel_spacing[0] pixmaps_axial, pixmaps_coronal, pixmaps_sagittal = \ get_pixmaps(pixel_values, window, level, pixmap_aspect) patient_dict_container.set("pixmaps_axial", pixmaps_axial) patient_dict_container.set("pixmaps_coronal", pixmaps_coronal) patient_dict_container.set("pixmaps_sagittal", pixmaps_sagittal) patient_dict_container.set("pixel_values", pixel_values) patient_dict_container.set("pixmap_aspect", pixmap_aspect) basic_info = get_basic_info(dataset[0]) patient_dict_container.set("basic_info", basic_info) patient_dict_container.set("dict_uid", dict_instance_uid(dataset)) # Set RTSS attributes patient_dict_container.set("file_rtss", filepaths['rtss']) patient_dict_container.set("dataset_rtss", dataset['rtss']) dict_raw_contour_data, dict_numpoints = \ ImageLoading.get_raw_contour_data(dataset['rtss']) patient_dict_container.set("raw_contour", dict_raw_contour_data) # dict_dicom_tree_rtss will be set in advance if the program # generates a new rtss through the execution of # ROI.create_initial_rtss_from_ct(...) if patient_dict_container.get("dict_dicom_tree_rtss") is None: dicom_tree_rtss = DicomTree(filepaths['rtss']) patient_dict_container.set("dict_dicom_tree_rtss", dicom_tree_rtss.dict) patient_dict_container.set( "list_roi_numbers", ordered_list_rois(patient_dict_container.get("rois"))) patient_dict_container.set("selected_rois", []) patient_dict_container.set("dict_polygons_axial", {}) patient_dict_container.set("dict_polygons_sagittal", {}) patient_dict_container.set("dict_polygons_coronal", {}) # Set RTDOSE attributes if patient_dict_container.has_modality("rtdose"): dicom_tree_rtdose = DicomTree(filepaths['rtdose']) patient_dict_container.set("dict_dicom_tree_rtdose", dicom_tree_rtdose.dict) patient_dict_container.set("dose_pixluts", get_dose_pixluts(dataset)) patient_dict_container.set("selected_doses", []) # overwritten if RTPLAN is present. patient_dict_container.set("rx_dose_in_cgray", 1) # Set RTPLAN attributes if patient_dict_container.has_modality("rtplan"): # the TargetPrescriptionDose is type 3 (optional), so it may not be # there However, it is preferable to the sum of the beam doses # DoseReferenceStructureType is type 1 (value is mandatory), but it # can have a value of ORGAN_AT_RISK rather than TARGET in which case # there will *not* be a TargetPrescriptionDose and even if it is # TARGET, that's no guarantee that TargetPrescriptionDose will be # encoded and have a value rx_dose_in_cgray = calculate_rx_dose_in_cgray(dataset["rtplan"]) patient_dict_container.set("rx_dose_in_cgray", rx_dose_in_cgray) dicom_tree_rtplan = DicomTree(filepaths['rtplan']) patient_dict_container.set("dict_dicom_tree_rtplan", dicom_tree_rtplan.dict) # Set SR attributes if patient_dict_container.has_modality("sr-cd"): dicom_tree_sr_clinical_data = DicomTree(filepaths['sr-cd']) patient_dict_container.set("dict_dicom_tree_sr_cd", dicom_tree_sr_clinical_data.dict) if patient_dict_container.has_modality("sr-rad"): dicom_tree_sr_pyrad = DicomTree(filepaths['sr-rad']) patient_dict_container.set("dict_dicom_tree_sr_pyrad", dicom_tree_sr_pyrad.dict)