def restoreStateBeforeExitingModule(self): """Load the last state of the module when the user exited (labelmap, opacity, contrast window, etc.) """ try: if self.savedVolumeID: # There is a previously saved valid state. SlicerUtil.setActiveVolumeIds(self.savedVolumeID) SlicerUtil.changeContrastWindow(self.savedContrastLevel[0], self.savedContrastLevel[1]) # if self.savedLabelmapID: # print "Restoring active labelmap: " + self.savedLabelmapID # # There was a valid labelmap. Restore it # SlicerUtil.displayLabelmapVolume(self.savedLabelmapID) # # Restore previous opacity # SlicerUtil.changeLabelmapOpacity(self.savedLabelmapOpacity) # else: # # Hide labelmap # print "No labelmap saved. Hide all" # SlicerUtil.displayLabelmapVolume(None) # else: # # Hide labelmap # print "No volume saved. Hide labelmap" # SlicerUtil.displayLabelmapVolume(None) # Restore layout SlicerUtil.changeLayout(self.savedLayout) except: Util.print_last_exception() pass
def loadFiducials(self, volumeNode, fileName): """ Load from disk a list of fiducials for a particular volume node :param volumeNode: Volume (scalar node) :param fileName: full path of the file to load the fiducials where """ with open(fileName, "r") as f: xml = f.read() geom = GTD.GeometryTopologyData.from_xml(xml) for point in geom.points: subtype = point.chest_type if subtype in self.params.mainTypes.keys(): # Main type. The subtype will be "Any" mainType = subtype subtype = 0 else: mainType = self.params.getMainTypeForSubtype(subtype) # Activate the current fiducials list based on the main type fidList = self.setActiveFiducialsListNode(volumeNode, mainType, subtype, point.feature_type) # Check if the coordinate system is RAS (and make the corresponding transform otherwise) if geom.coordinate_system == geom.LPS: coord = Util.lps_to_ras(point.coordinate) elif geom.coordinate_system == geom.IJK: coord = Util.ijk_to_ras(volumeNode, point.coordinate) else: # Try default mode (RAS) coord = point.coordinate # Add the fiducial fidList.AddFiducial(coord[0], coord[1], coord[2], self.getMarkupLabel(mainType, subtype, point.feature_type))
def saveResults(self): try: if SlicerUtil.isSlicerACILLoaded(): saveResultsRemotely = qt.QMessageBox.question( slicer.util.mainWindow(), "Save volume remotely?", "Do you want to save the results remotely?", qt.QMessageBox.Yes | qt.QMessageBox.No) == qt.QMessageBox.Yes else: saveResultsRemotely = False # First, save locally to the results directory (as a backup) labelmap = self.getCurrentLabelMapNode() localPath = os.path.join(self.saveResultsDirectoryButton.directory, labelmap.GetName() + ".nrrd") if saveResultsRemotely: self.caseNavigatorWidget.uploadVolume( labelmap, callbackFunction=self._uploadFileCallback_, localPath=localPath) else: slicer.util.saveNode(labelmap, localPath) slicer.util.infoDisplay( "Results saved to '{}'".format(localPath)) except Exception as ex: Util.print_last_exception() slicer.util.errorDisplay(ex.message)
def loadFiducialsXml(self, volumeNode, fileName): """ Load from disk a list of fiducials for a particular volume node :param volumeNode: Volume (scalar node) :param fileName: full path of the file to load the fiducials where """ with open(fileName, "r") as f: xml = f.read() self.currentGeometryTopologyData = gtd.GeometryTopologyData.from_xml( xml) for point in self.currentGeometryTopologyData.points: # Activate the current fiducials list based on the type list typesList = self.getTypesListFromXmlPoint(point) fidListNode = self.setActiveFiducialsListNode( volumeNode, typesList) # Check if the coordinate system is RAS (and make the corresponding transform otherwise) if self.currentGeometryTopologyData.coordinate_system == self.currentGeometryTopologyData.LPS: coord = Util.lps_to_ras(point.coordinate) elif self.currentGeometryTopologyData.coordinate_system == self.currentGeometryTopologyData.IJK: coord = Util.ijk_to_ras(volumeNode, point.coordinate) else: # Try default mode (RAS) coord = point.coordinate # Add the fiducial fidListNode.AddFiducial(coord[0], coord[1], coord[2], self.getMarkupLabel(typesList))
def loadFiducialsXml(self, volumeNode, fileName): """ Load from disk a list of fiducials for a particular volume node :param volumeNode: Volume (scalar node) :param fileName: full path of the file to load the fiducials where """ with open(fileName, "r") as f: xml = f.read() self.currentGeometryTopologyData = GTD.GeometryTopologyData.from_xml( xml) for point in self.currentGeometryTopologyData.points: subtype = point.chest_type if subtype in self.params.mainTypes.keys(): # Main type. The subtype will be "Any" mainType = subtype subtype = 0 else: mainType = self.params.getMainTypeForSubtype(subtype) # Activate the current fiducials list based on the main type fidListNode = self.setActiveFiducialsListNode( volumeNode, mainType, subtype, point.feature_type) # Check if the coordinate system is RAS (and make the corresponding transform otherwise) if self.currentGeometryTopologyData.coordinate_system == self.currentGeometryTopologyData.LPS: coord = Util.lps_to_ras(point.coordinate) elif self.currentGeometryTopologyData.coordinate_system == self.currentGeometryTopologyData.IJK: coord = Util.ijk_to_ras(volumeNode, point.coordinate) else: # Try default mode (RAS) coord = point.coordinate # Add the fiducial fidListNode.AddFiducial( coord[0], coord[1], coord[2], self.getMarkupLabel(mainType, subtype, point.feature_type))
def saveResultsCurrentNode(self): """ Get current active node and save the xml fiducials file """ try: d = self.saveResultsDirectoryButton.directory if not os.path.isdir(d): # Ask the user if he wants to create the folder if qt.QMessageBox.question( slicer.util.mainWindow(), "Create directory?", "The directory '{0}' does not exist. Do you want to create it?" .format(d), qt.QMessageBox.Yes | qt.QMessageBox.No) == qt.QMessageBox.Yes: try: os.makedirs(d) # Make sure that everybody has write permissions (sometimes there are problems because of umask) os.chmod(d, 0o777) except: qt.QMessageBox.warning( slicer.util.mainWindow(), 'Directory incorrect', 'The folder "{0}" could not be created. Please select a valid directory' .format(d)) return else: # Abort process SlicerUtil.logDevelop("Saving results process aborted", includePythonConsole=True) return # self.logic.saveCurrentFiducials(d, self.caseNavigatorWidget, self.uploadFileResult) # qt.QMessageBox.information(slicer.util.mainWindow(), 'Results saved', # "The results have been saved succesfully") # else: if SlicerUtil.isSlicerACILLoaded(): question = qt.QMessageBox.question( slicer.util.mainWindow(), "Save results remotely?", "Your results will be saved locally. Do you also want to save your results in your remote server? (MAD, etc.)", qt.QMessageBox.Yes | qt.QMessageBox.No | qt.QMessageBox.Cancel) if question == qt.QMessageBox.Cancel: return saveInRemoteRepo = question == qt.QMessageBox.Yes else: saveInRemoteRepo = False self.logic.saveCurrentFiducials( d, caseNavigatorWidget=self.caseNavigatorWidget, callbackFunction=self.uploadFileResult, saveInRemoteRepo=saveInRemoteRepo) qt.QMessageBox.information( slicer.util.mainWindow(), 'Results saved', "The results have been saved succesfully") except: Util.print_last_exception() qt.QMessageBox.critical( slicer.util.mainWindow(), "Error when saving the results", "Error when saving the results. Please review the console for additional info" )
def refreshTextboxes(self, reset=False): """ Update the information of the textboxes that give information about the measurements """ self.aortaTextBox.setText("0") self.paTextBox.setText("0") self.ratioTextBox.setText("0") self.ratioTextBox.setStyleSheet( " QLineEdit { background-color: white; color: black}") volumeId = self.volumeSelector.currentNodeID # if volumeId not in self.logic.currentVolumesLoaded: # return if volumeId: self.logic.changeActiveRulersColor(volumeId, self.logic.defaultColor) aorta = None pa = None if not reset: rulerAorta, newAorta = self.logic.getRulerNodeForVolumeAndStructure( self.volumeSelector.currentNodeID, self.logic.AORTA, createIfNotExist=False) rulerPA, newPA = self.logic.getRulerNodeForVolumeAndStructure( self.volumeSelector.currentNodeID, self.logic.PA, createIfNotExist=False) if rulerAorta: aorta = rulerAorta.GetDistanceMeasurement() self.aortaTextBox.setText(str(aorta)) if rulerPA: pa = rulerPA.GetDistanceMeasurement() self.paTextBox.setText(str(pa)) if pa is not None and aorta is not None and aorta != 0: try: ratio = pa / aorta self.ratioTextBox.setText(str(ratio)) if ratio > 1.0: # Switch colors ("alarm") st = " QLineEdit {{ background-color: rgb({0}, {1}, {2}); color: white }}". \ format(int(self.logic.defaultWarningColor[0]*255), int(self.logic.defaultWarningColor[1]*255), int(self.logic.defaultWarningColor[2]*255)) self.ratioTextBox.setStyleSheet(st) self.logic.changeActiveRulersColor( volumeId, self.logic.defaultWarningColor) except Exception: Util.print_last_exception()
def refreshTextboxes(self, reset=False): """ Update the information of the textboxes that give information about the measurements """ self.rvTextBox.setText("0") self.lvTextBox.setText("0") self.ratioTextBox.setText("0") self.ratioTextBox.setStyleSheet( " QLineEdit { background-color: white; color: black}") volumeId = self.volumeSelector.currentNodeID # if volumeId not in self.logic.currentVolumesLoaded: # return rv = None lv = None if not reset: rulerRV, newRV = self.logic.getRulerNodeForVolumeAndStructure( self.volumeSelector.currentNodeID, self.logic.RV, createIfNotExist=False) rulerLV, newLV = self.logic.getRulerNodeForVolumeAndStructure( self.volumeSelector.currentNodeID, self.logic.LV, createIfNotExist=False) if rulerRV: rv = rulerRV.GetDistanceMeasurement() self.rvTextBox.setText(str(rv)) if rulerLV: lv = rulerLV.GetDistanceMeasurement() self.lvTextBox.setText(str(lv)) if lv is not None and rv is not None and rv != 0: try: ratio = lv / rv self.ratioTextBox.setText(str(ratio)) if ratio > 1.0: # Switch colors ("alarm") st = " QLineEdit {{ background-color: rgb({0}, {1}, {2}); color: white }}". \ format(int(self.logic.defaultWarningColor[0]*255), int(self.logic.defaultWarningColor[1]*255), int(self.logic.defaultWarningColor[2]*255)) self.ratioTextBox.setStyleSheet(st) except Exception: Util.print_last_exception()
def saveResultsCurrentNode(self): """ Get current active node and save the xml fiducials file """ try: d = self.saveResultsDirectoryButton.directory if not os.path.isdir(d): # Ask the user if he wants to create the folder if qt.QMessageBox.question( slicer.util.mainWindow(), "Create directory?", "The directory '{0}' does not exist. Do you want to create it?" .format(d), qt.QMessageBox.Yes | qt.QMessageBox.No) == qt.QMessageBox.Yes: try: os.makedirs(d) # Make sure that everybody has write permissions (sometimes there are problems because of umask) os.chmod(d, 0777) except: qt.QMessageBox.warning( slicer.util.mainWindow(), 'Directory incorrect', 'The folder "{0}" could not be created. Please select a valid directory' .format(d)) return self.logic.saveCurrentFiducials(d, self.caseNavigatorWidget, self.uploadFileResult) qt.QMessageBox.information( slicer.util.mainWindow(), 'Results saved', "The results have been saved succesfully") else: self.logic.saveCurrentFiducials(d, self.caseNavigatorWidget, self.uploadFileResult) qt.QMessageBox.information( slicer.util.mainWindow(), 'Results saved', "The results have been saved succesfully") except: Util.print_last_exception() qt.QMessageBox.critical( slicer.util.mainWindow(), "Error when saving the results", "Error when saving the results. Please review the console for additional info" )
def saveCurrentFiducials(self, directory): """ Save all the fiducials for the current volume. The name of the file will be VolumeName_parenchymaTraining.xml" :param volume: scalar node :param directory: destiny directory :return: """ volume = slicer.util.getNode(self.currentVolumeId) print("DEBUG: saving the fidcuals for volume " + volume.GetName()) # Iterate over all the fiducials list nodes pos = [0,0,0] topology = GTD.GeometryTopologyData() topology.coordinate_system = topology.LPS # Get the transformation matrix LPS-->IJK matrix = Util.get_lps_to_ijk_transformation_matrix(volume) topology.lps_to_ijk_transformation_matrix = Util.convert_vtk_matrix_to_list(matrix) for fidListNode in slicer.util.getNodes("{0}_fiducials_*".format(volume.GetID())).itervalues(): # Get all the markups for i in range(fidListNode.GetNumberOfMarkups()): fidListNode.GetNthFiducialPosition(i, pos) # Get the type from the description (region will always be 0) desc = fidListNode.GetNthMarkupDescription(i) typeId = int(desc.split("_")[0]) artifactId = int(desc.split("_")[1]) # Switch coordinates from RAS to LPS lps_coords = Util.ras_to_lps(list(pos)) p = GTD.Point(0, typeId, artifactId, lps_coords) topology.add_point(p) # Get the xml content file xml = topology.to_xml() # Save the file fileName = os.path.join(directory, "{0}_parenchymaTraining.xml".format(volume.GetName())) with open(fileName, 'w') as f: f.write(xml) # Mark the current volume as saved #self.savedVolumes.add(volume.GetID()) self.savedVolumes[volume.GetID()] = True
def saveStateBeforeEnteringModule(self): """Save the state of the module regarding labelmap, etc. This state will be saved/loaded when exiting/entering the module """ if self.preventSavingState: # Avoid that the first time that the module loads, the state is saved twice self.preventSavingState = False return # Save existing layout self.savedLayout = None if slicer.app.layoutManager() is not None: self.savedLayout = slicer.app.layoutManager().layout # Get the active volume (it it exists) activeVolumeId = SlicerUtil.getFirstActiveVolumeId() if activeVolumeId is None: # Reset state self.resetModuleState() else: # There is a Volume loaded. Save state try: self.savedVolumeID = activeVolumeId displayNode = SlicerUtil.getNode( activeVolumeId).GetDisplayNode() self.savedContrastLevel = (displayNode.GetWindow(), displayNode.GetLevel()) # activeLabelmapId = SlicerUtil.getFirstActiveLabelmapId() # self.savedLabelmapID = activeLabelmapId # if activeLabelmapId is None: # self.savedLabelmapOpacity = None # else: # self.savedLabelmapOpacity = SlicerUtil.getLabelmapOpacity() # # Hide any labelmap # SlicerUtil.displayLabelmapVolume(None) except: Util.print_last_exception() # Not action really needed pass
def refreshTextboxes(self, reset=False): """ Update the information of the textboxes that give information about the measurements """ self.aortaTextBox.setText("0") self.paTextBox.setText("0") self.ratioTextBox.setText("0") self.ratioTextBox.setStyleSheet(" QLineEdit { background-color: white; color: black}") volumeId = self.volumeSelector.currentNodeId if volumeId not in self.logic.currentVolumesLoaded: return if volumeId: self.logic.changeColor(volumeId, self.logic.defaultColor) aorta = None pa = None if not reset: rulerAorta, newAorta = self.logic.getRulerNodeForVolumeAndStructure( self.volumeSelector.currentNodeId, self.logic.AORTA, createIfNotExist=False ) rulerPA, newPA = self.logic.getRulerNodeForVolumeAndStructure( self.volumeSelector.currentNodeId, self.logic.PA, createIfNotExist=False ) if rulerAorta: aorta = rulerAorta.GetDistanceMeasurement() self.aortaTextBox.setText(str(aorta)) if rulerPA: pa = rulerPA.GetDistanceMeasurement() self.paTextBox.setText(str(pa)) if aorta is not None and aorta != 0: try: ratio = pa / aorta self.ratioTextBox.setText(str(ratio)) if ratio > 1: # Switch colors ("alarm") self.ratioTextBox.setStyleSheet(" QLineEdit { background-color: rgb(255, 0, 0); color: white}") self.logic.changeColor(volumeId, self.logic.defaultWarningColor) except Exception: Util.print_last_exception()
if not path.isdir(results_dir): # Create the results folder if it doesn't exist os.makedirs(results_dir) files = os.listdir(files_dir) types_delete = [97, 98, 99, 100] types_replace = { 94: 84, 96: 85, 101: 34, 102: 86, 103: 88 } for file_name in files: if Util.get_file_extension(file_name) != ".xml": continue p = path.join(files_dir, file_name) print ("Processing {0}...".format(file_name)) with open(p, "r+b") as f: xml = f.read() geom = gtd.GeometryTopologyData.from_xml(xml) geom.num_dimensions = 3 points = [] change_coords = geom.coordinate_system != geom.LPS for point in geom.points: if point.chest_type not in types_delete: if types_replace.has_key(point.chest_type): # Replace type point.chest_type = types_replace[point.chest_type] if change_coords:
sys.path.append("/Users/jonieva/Projects/SlicerCIP/Scripted/CIP_Common/") from CIP.logic import geometry_topology_data as gtd from CIP.logic import Util import os import os.path import re wrong_cases_path = "/Volumes/Mac500/Dropbox/rola-jorge/LH ROI failure/" xmls_path = "/Volumes/Mac500/Dropbox/ACIL-Biomarkers/parenchyma training 2015-10-02/" results_path = "/Volumes/Mac500/Data/tempdata/parenchyma training 2015-10-02/Removed Points/LH ROI failure" current_case = "" caseIds = dict() for file_name in os.listdir(wrong_cases_path): if Util.get_file_extension(file_name) != ".png": continue expr = "(?P<caseid>(^(.*?)_(.*?)_(.*?)_(.*?)_(.*?)))_(.*?)_p(?P<point>(.*?))_(.*)" r = re.match(expr, file_name) caseId = r.group("caseid") point = int(r.group("point")) if not caseIds.has_key(caseId): caseIds[caseId] = [] caseIds[caseId].append(point) for caseId, excludedPoints in caseIds.iteritems(): # Load the Geometry object geom = gtd.GeometryTopologyData.from_xml_file(os.path.join(xmls_path, caseId + "_parenchymaTraining.xml")) newPoints=[] for i in range(len(geom.points)): if i not in excludedPoints:
def saveCurrentFiducials(self, localFilePath, caseNavigatorWidget=None, callbackFunction=None, saveInRemoteRepo=False): """ Save all the fiducials for the current volume. The name of the file will be VolumeName_parenchymaTraining.xml" :param filePath: destination file (local) :param caseNavigatorWidget: case navigator widget (optional) :param callbackFunction: function to invoke when the file has been uploaded to the server (optional) """ volume = slicer.mrmlScene.GetNodeByID(self.currentVolumeId) #fileName = volume.GetName() + Util.file_conventions_extensions[self._xmlFileExtensionKey_] # If there is already a xml file in the results directory, make a copy. #localFilePath = os.path.join(directory, fileName) if os.path.isfile(localFilePath): # Make a copy of the file for history purposes copyfile(localFilePath, localFilePath + "." + time.strftime("%Y%m%d.%H%M%S")) # Iterate over all the fiducials list nodes pos = [0, 0, 0] geometryTopologyData = gtd.GeometryTopologyData() geometryTopologyData.coordinate_system = geometryTopologyData.LPS # Get the transformation matrix LPS-->IJK matrix = Util.get_lps_to_ijk_transformation_matrix(volume) geometryTopologyData.lps_to_ijk_transformation_matrix = Util.convert_vtk_matrix_to_list( matrix) # Save spacing and origin of the volume geometryTopologyData.origin = volume.GetOrigin() geometryTopologyData.spacing = volume.GetSpacing() geometryTopologyData.dimensions = volume.GetImageData().GetDimensions() # Get the hashtable and seed from previously loaded GeometryTopologyData object (if available) if self.currentGeometryTopologyData is None: hashTable = {} else: hashTable = self.currentGeometryTopologyData.get_hashtable() geometryTopologyData.seed_id = self.currentGeometryTopologyData.seed_id # Get a timestamp that will be used for all the points timestamp = gtd.GeometryTopologyData.get_timestamp() for fidListNode in slicer.util.getNodes("{0}_fiducials_*".format( volume.GetName())).itervalues(): # Get all the markups for i in range(fidListNode.GetNumberOfMarkups()): fidListNode.GetNthFiducialPosition(i, pos) # Get the type from the description (region will always be 0) desc = fidListNode.GetNthMarkupDescription(i) # Switch coordinates from RAS to LPS lps_coords = Util.ras_to_lps(list(pos)) pointMetadata = self.getPointMetadataFromFiducialDescription( desc) p = gtd.Point(pointMetadata[0], pointMetadata[1], pointMetadata[2], lps_coords, description=pointMetadata[3]) key = p.get_hash() if hashTable.has_key(key): # Add previously existing point geometryTopologyData.add_point(hashTable[key], fill_auto_fields=False) else: # Add a new point with a precalculated timestamp geometryTopologyData.add_point(p, fill_auto_fields=True) p.timestamp = timestamp # Get the xml content file xml = geometryTopologyData.to_xml() # Save the file with open(localFilePath, 'w') as f: f.write(xml) # Use the new object as the current GeometryTopologyData self.currentGeometryTopologyData = geometryTopologyData # Upload to MAD if we are using the ACIL case navigator if saveInRemoteRepo: caseNavigatorWidget.uploadFile(localFilePath, callbackFunction=callbackFunction) # Mark the current volume as saved self.savedVolumes[volume.GetName()] = True
sys.path.append("/Users/jonieva/Projects/SlicerCIP/Scripted/CIP_Common/") from CIP.logic import geometry_topology_data as gtd from CIP.logic import Util import os import os.path import re wrong_cases_path = "/Volumes/Mac500/Dropbox/rola-jorge/LH ROI failure/" xmls_path = "/Volumes/Mac500/Dropbox/ACIL-Biomarkers/parenchyma training 2015-10-02/" results_path = "/Volumes/Mac500/Data/tempdata/parenchyma training 2015-10-02/Removed Points/LH ROI failure" current_case = "" caseIds = dict() for file_name in os.listdir(wrong_cases_path): if Util.get_file_extension(file_name) != ".png": continue expr = "(?P<caseid>(^(.*?)_(.*?)_(.*?)_(.*?)_(.*?)))_(.*?)_p(?P<point>(.*?))_(.*)" r = re.match(expr, file_name) caseId = r.group("caseid") point = int(r.group("point")) if not caseIds.has_key(caseId): caseIds[caseId] = [] caseIds[caseId].append(point) for caseId, excludedPoints in caseIds.iteritems(): # Load the Geometry object geom = gtd.GeometryTopologyData.from_xml_file( os.path.join(xmls_path, caseId + "_parenchymaTraining.xml")) newPoints = [] for i in range(len(geom.points)):
if not path.isdir(results_dir): # Create the results folder if it doesn't exist os.makedirs(results_dir) files = os.listdir(files_dir) types_delete = [97, 98, 99, 100] types_replace = { 94: 84, 96: 85, 101: 34, 102: 86, 103: 88 } for file_name in files: if Util.get_file_extension(file_name) != ".xml": continue p = path.join(files_dir, file_name) print ("Processing {0}...".format(file_name)) with open(p, "r+b") as f: xml = f.read() geom = gtd.GeometryTopologyData.from_xml(xml) geom.num_dimensions = 3 points = [] change_coords = geom.coordinate_system != geom.LPS for point[ in geom.points: if point.chest_type not in types_delete: if types_replace.has_key(point.chest_type): # Replace type point.chest_type = types_replace[point.chest_type] if change_coords:
def saveCurrentFiducials(self, directory, caseNavigatorWidget=None, callbackFunction=None): """ Save all the fiducials for the current volume. The name of the file will be VolumeName_parenchymaTraining.xml" :param volume: scalar node :param directory: destination directory """ volume = slicer.mrmlScene.GetNodeByID(self.currentVolumeId) fileName = volume.GetName( ) + Util.file_conventions_extensions["ParenchymaTrainingFiducialsXml"] # If there is already a xml file in the results directory, make a copy. fiducialsLocalFilePath = os.path.join(directory, fileName) if os.path.isfile(fiducialsLocalFilePath): # Make a copy of the file for history purposes copyfile( fiducialsLocalFilePath, fiducialsLocalFilePath + "." + time.strftime("%Y%m%d.%H%M%S")) # Iterate over all the fiducials list nodes pos = [0, 0, 0] geometryTopologyData = GTD.GeometryTopologyData() geometryTopologyData.coordinate_system = geometryTopologyData.LPS # Get the transformation matrix LPS-->IJK matrix = Util.get_lps_to_ijk_transformation_matrix(volume) geometryTopologyData.lps_to_ijk_transformation_matrix = Util.convert_vtk_matrix_to_list( matrix) # Get the hashtable and seed from previously loaded GeometryTopologyData object (if available) if self.currentGeometryTopologyData is None: hashTable = {} else: hashTable = self.currentGeometryTopologyData.get_hashtable() geometryTopologyData.id_seed = self.currentGeometryTopologyData.id_seed # Get a timestamp that will be used for all the points timestamp = GTD.GeometryTopologyData.get_timestamp() for fidListNode in slicer.util.getNodes("{0}_fiducials_*".format( volume.GetName())).itervalues(): # Get all the markups for i in range(fidListNode.GetNumberOfMarkups()): fidListNode.GetNthFiducialPosition(i, pos) # Get the type from the description (region will always be 0) desc = fidListNode.GetNthMarkupDescription(i) typeId = int(desc.split("_")[0]) artifactId = int(desc.split("_")[1]) # Switch coordinates from RAS to LPS lps_coords = Util.ras_to_lps(list(pos)) p = GTD.Point(0, typeId, artifactId, lps_coords) key = p.get_hash() if hashTable.has_key(key): # Add previously existing point geometryTopologyData.add_point(hashTable[key], fill_auto_fields=False) else: # Add a new point with a precalculated timestamp geometryTopologyData.add_point(p, fill_auto_fields=True) p.timestamp = timestamp # Get the xml content file xml = geometryTopologyData.to_xml() # Save the file with open(fiducialsLocalFilePath, 'w') as f: f.write(xml) # Use the new object as the current GeometryTopologyData self.currentGeometryTopologyData = geometryTopologyData # Upload to MAD if we are using the ACIL case navigator if caseNavigatorWidget is not None: caseNavigatorWidget.uploadFile(fiducialsLocalFilePath, callbackFunction=callbackFunction) # Mark the current volume as saved self.savedVolumes[volume.GetName()] = True