def runAutomaticSegmentation(self): logic = AutomaticSegmentationLogic() logic.addEventObserver(logic.DeepLearningFinishedEvent, self.onSegmentationFinished) logic.addEventObserver( logic.DeepLearningFailedEvent, lambda c, e: self. onPreopLoadingFailed("Automatic segmentation failed.")) customStatusProgressBar = CustomStatusProgressbar() customStatusProgressBar.text = "Running DeepInfer for automatic prostate segmentation" from mpReview import mpReviewLogic mpReviewColorNode, _ = mpReviewLogic.loadColorTable( self.getSetting("Color_File_Name", moduleName=self.MODULE_NAME)) domain = 'BWH_WITHOUT_ERC' prompt = SliceWidgetConfirmYesNoDialog( self.data.initialVolume, "Was an endorectal coil used during preop acquisition?").exec_() self.preopData.usedERC = False if prompt == qt.QDialogButtonBox.Yes: self.preopData.usedERC = True domain = 'BWH_WITH_ERC' elif prompt == qt.QDialogButtonBox.Cancel: raise PreProcessedDataError self.segmentationData.startTime = self.getTime() logic.run(self.data.initialVolume, domain, mpReviewColorNode)
def onNewImageSeriesReceived(self, caller, event, callData): if not self.session.isBusy(): customStatusProgressBar = CustomStatusProgressbar() customStatusProgressBar.text = "New image data has been received." if self.session.intraopDICOMReceiver: self.session.intraopDICOMReceiver.forceStatusChangeEventUpdate( ) self.updateIntraopSeriesSelectorTable() if not self.active or self.session.isBusy(): return selectedSeries = self.intraopSeriesSelector.currentText if selectedSeries != "" and self.session.isTrackingPossible( selectedSeries): selectedSeriesNumber = RegistrationResult.getSeriesNumberFromString( selectedSeries) newImageSeries = ast.literal_eval(callData) newImageSeriesNumbers = [ RegistrationResult.getSeriesNumberFromString(s) for s in newImageSeries ] if selectedSeriesNumber in newImageSeriesNumbers: self.takeActionOnSelectedSeries()
def promptUserAndApplyBiasCorrectionIfNeeded(self): if self.session.data.resumed or self.session.data.completed: return autoPreopSeg = self.session.data.usedAutomaticPreopSegmentation usedERC = (autoPreopSeg and self.session.data.preopData.usedERC is True) or \ (not autoPreopSeg and slicer.util.confirmYesNoDisplay("Was an endorectal coil used for preop image " "acquisition?", windowTitle="SliceTracker")) if usedERC: customProgressbar = CustomStatusProgressbar() customProgressbar.busy = True currentModule = slicer.util.getModuleGui(self.MODULE_NAME) if currentModule: currentModule.parent().enabled = False try: customProgressbar.updateStatus("Bias correction running!") slicer.app.processEvents() self.logic.applyBiasCorrection() except AttributeError: pass finally: customProgressbar.busy = False if currentModule: currentModule.parent().enabled = True customProgressbar.updateStatus("Bias correction done!") self.session.data.preopData.usedERC = usedERC
def setup(self): ScriptedLoadableModuleWidget.setup(self) for step in [SliceTrackerOverviewStep, SliceTrackerZFrameRegistrationStep, SliceTrackerSegmentationStep, SliceTrackerEvaluationStep]: self.session.registerStep(step()) self.customStatusProgressBar = CustomStatusProgressbar() self.setupPatientWatchBox() self.setupViewSettingGroupBox() self.setupTabBarNavigation() self.setupConnections() self.setupSessionObservers() self.layout.addStretch(1)
def onSegmentationValidated(self, caller, event, labelNode): if "_modified" in labelNode.GetName(): self.segmentationData.userModified["endTime"] = self.getTime() self.segmentationData._modifiedLabel = labelNode else: self.segmentationData.userModified = None if not self.preopSegmentationPath: self.createDirectory(self.preopSegmentationPath) segmentedColorName = self.getSetting("Segmentation_Color_Name", moduleName=self.MODULE_NAME) timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") filename = getpass.getuser() + '-' + segmentedColorName + '-' + timestamp self.saveNodeData(labelNode, self.preopSegmentationPath, extension=FileExtension.NRRD, name=filename) self.loadPreProcessedData() customStatusProgressBar = CustomStatusProgressbar() customStatusProgressBar.text = "Done automatic prostate segmentation"
def setup(self): ScriptedLoadableModuleWidget.setup(self) for step in [ ProstateAblationOverviewStep, ProstateAblationZFrameRegistrationStep, ProstateAblationTargetingStep, ProstateAblationGuidanceStep ]: registeredStep = step(self.session) self.session.registerStep(registeredStep) self.customStatusProgressBar = CustomStatusProgressbar() self.setupPatientWatchBox() self.setupViewSettingGroupBox() self.setupTabBarNavigation() self.setupSessionObservers()
def onNewImageSeriesReceived(self, caller, event, callData): if not self.session.isLoading(): customStatusProgressBar = CustomStatusProgressbar() customStatusProgressBar.text = "New image data has been received." self.updateIntraopSeriesSelectorTable() if not self.active or self.session.isLoading(): return selectedSeries = self.intraopSeriesSelector.currentText if selectedSeries != "" and self.session.isTrackingPossible(selectedSeries): selectedSeriesNumber = int(selectedSeries.split(": ")[0]) newImageSeries = ast.literal_eval(callData) newImageSeriesNumbers = [int(s.split(": ")[0]) for s in newImageSeries] if selectedSeriesNumber in newImageSeriesNumbers: self.takeActionOnSelectedSeries()
def resetAndInitializeData(self): self.seriesTypeManager = SeriesTypeManager() self.startTimeStamp = self.getTime() self.resumeTimeStamps = [] self.closedLogTimeStamps = [] self.savedNeedleTypeForTargets = dict() self.savedNeedleTypeForTargets.clear() self.completed = False self.segmentModelNode = None self.initialVolume = None self.initialLabel = None self.intraOpTargets = None self.zFrameRegistrationResult = None self.customProgressBar = CustomStatusProgressbar()
def resetAndInitializeData(self): self.seriesTypeManager = SeriesTypeManager() self.startTime = self.getTime() self.resumeTimeStamps = [] self.closedLogTimeStamps = [] self.completed = False self.usePreopData = False self.segmentModelNode = None self.inputMarkupNode = None self.initialVolume = None self.initialLabel = None self.initialTargets = None self.initialTargetsPath = None self.zFrameRegistrationResult = None self._savedRegistrationResults = [] self.initializeRegistrationResults() self.customProgressBar = CustomStatusProgressbar() self.alreadyLoadedFileNames = {}
def onDICOMReceiverStatusChanged(self, caller, event, callData): customStatusProgressBar = CustomStatusProgressbar() customStatusProgressBar.text = callData customStatusProgressBar.busy = "Waiting" in callData
class SliceTrackerWidget(ModuleWidgetMixin, SliceTrackerConstants, ScriptedLoadableModuleWidget): def __init__(self, parent=None): ScriptedLoadableModuleWidget.__init__(self, parent) self.modulePath = os.path.dirname(slicer.util.modulePath(self.moduleName)) SliceTrackerConfiguration(self.moduleName, os.path.join(self.modulePath, 'Resources', "default.cfg")) self.logic = SliceTrackerLogic() self.session = SliceTrackerSession() self.session.steps = [] self.session.removeEventObservers() self.session.addEventObserver(self.session.CloseCaseEvent, lambda caller, event: self.cleanup()) self.session.addEventObserver(SlicerDevelopmentToolboxEvents.NewFileIndexedEvent, self.onNewFileIndexed) self.demoMode = str(self.getSetting("Demo_Mode", moduleName=self.moduleName)).lower() == 'true' def enter(self): if not slicer.dicomDatabase: slicer.util.errorDisplay("Slicer DICOMDatabase was not found. In order to be able to use SliceTracker, you will " "need to set a proper location for the Slicer DICOMDatabase.") self.layout.parent().enabled = slicer.dicomDatabase is not None def exit(self): pass def onReload(self): ScriptedLoadableModuleWidget.onReload(self) @logmethod(logging.DEBUG) def cleanup(self): ScriptedLoadableModuleWidget.cleanup(self) self.patientWatchBox.sourceFile = None self.preopWatchBox.sourceFile = None self.intraopWatchBox.sourceFile = None def setup(self): ScriptedLoadableModuleWidget.setup(self) for step in [SliceTrackerOverviewStep, SliceTrackerZFrameRegistrationStep, SliceTrackerSegmentationStep, SliceTrackerEvaluationStep]: self.session.registerStep(step()) self.customStatusProgressBar = CustomStatusProgressbar() self.setupPatientWatchBox() self.setupViewSettingGroupBox() self.setupTabBarNavigation() self.setupConnections() self.setupSessionObservers() self.layout.addStretch(1) def setupPatientWatchBox(self): WatchBoxAttribute.TRUNCATE_LENGTH = 20 patientWatchBoxInformation = [WatchBoxAttribute('PatientName', "Patient's Name: ", DICOMTAGS.PATIENT_NAME, masked=self.demoMode), WatchBoxAttribute('PatientID', 'Patient ID: ', DICOMTAGS.PATIENT_ID, masked=self.demoMode), WatchBoxAttribute('DOB', "Patient's Birth Date: ", DICOMTAGS.PATIENT_BIRTH_DATE, masked=self.demoMode)] self.patientWatchBox = DICOMBasedInformationWatchBox(patientWatchBoxInformation, title="Patient Information", columns=2) self.layout.addWidget(self.patientWatchBox) preopWatchBoxInformation = [WatchBoxAttribute('StudyDate', 'Study Date: ', DICOMTAGS.STUDY_DATE)] self.preopWatchBox = DICOMBasedInformationWatchBox(preopWatchBoxInformation, title="Preop Information", columns=2) self.layout.addWidget(self.preopWatchBox) intraopWatchBoxInformation = [WatchBoxAttribute('CurrentSeries', 'Current Series: ', [DICOMTAGS.SERIES_NUMBER, DICOMTAGS.SERIES_DESCRIPTION]), WatchBoxAttribute('StudyDate', 'Study Date: ', DICOMTAGS.STUDY_DATE)] self.intraopWatchBox = DICOMBasedInformationWatchBox(intraopWatchBoxInformation, title="Intraop Information", columns=2) self.registrationDetailsButton = self.createButton("", icon=Icons.settings, styleSheet="border:none;", maximumWidth=16) self.layout.addWidget(self.intraopWatchBox) def setupViewSettingGroupBox(self): iconSize = qt.QSize(24, 24) self.infoButton = self.createButton("", icon=Icons.info, iconSize=iconSize, checkable=True, toolTip="Display Patient/Study Information", checked=True) self.redOnlyLayoutButton = RedSliceLayoutButton() self.sideBySideLayoutButton = SideBySideLayoutButton() self.fourUpLayoutButton = FourUpLayoutButton() self.fourUpPlotLayoutButton = FourUpPlotViewLayoutButton() self.layoutButtons = [self.redOnlyLayoutButton, self.sideBySideLayoutButton, self.fourUpLayoutButton, self.fourUpPlotLayoutButton] self.crosshairButton = CrosshairButton() self.settingsButton = ModuleSettingsButton(self.moduleName) self.dicomConnectionTestButton = DICOMConnectionTestButton() self.dicomConnectionTestButton.setToolTip("Test DICOM connection") self.showAnnotationsButton = self.createButton("", icon=Icons.text_info, iconSize=iconSize, checkable=True, toolTip="Display annotations", checked=True) viewSettingButtons = [self.redOnlyLayoutButton, self.sideBySideLayoutButton, self.fourUpPlotLayoutButton, self.fourUpLayoutButton, self.infoButton, self.crosshairButton, self.settingsButton, self.dicomConnectionTestButton] for step in self.session.steps: viewSettingButtons += step.viewSettingButtons self.layout.addWidget(self.createHLayout(viewSettingButtons)) self.resetViewSettingButtons() def resetViewSettingButtons(self): for step in self.session.steps: step.resetViewSettingButtons() self.crosshairButton.checked = False def setupTabBarNavigation(self): self.tabWidget = SliceTrackerTabWidget() self.tabWidget.addEventObserver(self.tabWidget.AvailableLayoutsChangedEvent, self.onAvailableLayoutsChanged) self.layout.addWidget(self.tabWidget) self.tabWidget.hideTabs() def setupConnections(self): self.infoButton.connect('toggled(bool)', self.onShowInformationToggled) self.showAnnotationsButton.connect('toggled(bool)', self.onShowAnnotationsToggled) def setupSessionObservers(self): self.session.addEventObserver(self.session.PreprocessingSuccessfulEvent, self.onSuccessfulPreProcessing) self.session.addEventObserver(self.session.CurrentSeriesChangedEvent, self.onCurrentSeriesChanged) self.session.addEventObserver(self.session.CloseCaseEvent, self.onCaseClosed) def removeSessionObservers(self): self.session.removeEventObserver(self.session.PreprocessingSuccessfulEvent, self.onSuccessfulPreProcessing) self.session.removeEventObserver(self.session.CurrentSeriesChangedEvent, self.onCurrentSeriesChanged) self.session.removeEventObserver(self.session.CloseCaseEvent, self.onCaseClosed) def onSuccessfulPreProcessing(self, caller, event): dicomFileName = self.logic.getFileList(self.session.preopDICOMDirectory)[0] filename = os.path.join(self.session.preopDICOMDirectory, dicomFileName) self.patientWatchBox.sourceFile = filename self.preopWatchBox.sourceFile = filename def onShowAnnotationsToggled(self, checked): allSliceAnnotations = self.sliceAnnotations[:] def onShowInformationToggled(self, checked): self.patientWatchBox.visible = checked self.preopWatchBox.visible = checked self.intraopWatchBox.visible = checked @vtk.calldata_type(vtk.VTK_STRING) def onNewFileIndexed(self, caller, event, callData): text, size, currentIndex = ast.literal_eval(callData) if not self.customStatusProgressBar.visible: self.customStatusProgressBar.show() self.customStatusProgressBar.maximum = size self.customStatusProgressBar.updateStatus(text, currentIndex) @vtk.calldata_type(vtk.VTK_STRING) def onCurrentSeriesChanged(self, caller, event, callData=None): receivedFile = self.session.loadableList[callData][0] if callData else None if not self.session.data.usePreopData and self.patientWatchBox.sourceFile is None: self.patientWatchBox.sourceFile = receivedFile self.preopWatchBox.setInformation("StudyDate", "NA") self.intraopWatchBox.sourceFile = receivedFile @vtk.calldata_type(vtk.VTK_STRING) def onAvailableLayoutsChanged(self, caller, event, callData): layouts = ast.literal_eval(callData) for layoutButton in self.layoutButtons: layoutButton.enabled = layoutButton.LAYOUT in layouts @vtk.calldata_type(vtk.VTK_STRING) def onCaseClosed(self, caller, event, callData): self.customStatusProgressBar.reset()
class SessionData(ModuleLogicMixin): NewResultCreatedEvent = vtk.vtkCommand.UserEvent + 901 _completed = False _resumed = False @property def completed(self): return self._completed @completed.setter def completed(self, value): self._completed = value self.completedLogTimeStamp = self.generateLogfileTimeStampDict( ) if self._completed else None @property def resumed(self): return self._resumed @resumed.setter def resumed(self, value): if value and self.completed: raise ValueError("Completed case is not supposed to be resumed.") if value and not self.completed: self.resumeTimeStamps.append(self.getTime()) self._resumed = value @staticmethod def wasSessionCompleted(filename): with open(filename) as data_file: data = json.load(data_file) procedureEvents = data["procedureEvents"] return "caseCompleted" in procedureEvents.keys() @property def usedAutomaticPreopSegmentation(self): return self.preopData is not None and self.preopData.segmentation.algorithm == "Automatic" @property def usePreopData(self): self.preopData = getattr(self, "preopData", None) return self.preopData is not None @usePreopData.setter def usePreopData(self, value): self._usePreopData = value self.preopData = PreopData() if self._usePreopData else None def __init__(self): self.resetAndInitializeData() def resetAndInitializeData(self): self.seriesTypeManager = SeriesTypeManager() self.startTime = self.getTime() self.resumeTimeStamps = [] self.closedLogTimeStamps = [] self.completed = False self.usePreopData = False self.segmentModelNode = None self.inputMarkupNode = None self.initialVolume = None self.initialLabel = None self.initialTargets = None self.initialTargetsPath = None self.zFrameRegistrationResult = None self._savedRegistrationResults = [] self.initializeRegistrationResults() self.customProgressBar = CustomStatusProgressbar() self.alreadyLoadedFileNames = {} def initializeRegistrationResults(self): self.registrationResults = OrderedDict() def createZFrameRegistrationResult(self, series): self.zFrameRegistrationResult = ZFrameRegistrationResult(series) return self.zFrameRegistrationResult def createResult(self, series, invokeEvent=True): assert series not in self.registrationResults.keys() self.registrationResults[series] = RegistrationResult(series) if invokeEvent is True: self.invokeEvent(self.NewResultCreatedEvent, series) return self.registrationResults[series] def load(self, filename): directory = os.path.dirname(filename) self.resetAndInitializeData() with open(filename) as dataFile: self.customProgressBar.visible = True self.customProgressBar.text = "Reading meta information" logging.debug("reading json file %s" % filename) data = json.load(dataFile) self.readInitialTargetsAndVolume(data, directory) self.loadZFrameRegistrationData(data, directory) self.loadProcedureEvents(data) self.loadPreopData(data) self.loadResults(data, directory) self.registrationResults = OrderedDict( sorted(self.registrationResults.items())) return True def readInitialTargetsAndVolume(self, data, directory): if "initialTargets" in data.keys(): self.initialTargets = self._loadOrGetFileData( directory, data["initialTargets"], slicer.util.loadMarkupsFiducialList) self.initialTargets.SetLocked(True) self.initialTargetsPath = os.path.join(directory, data["initialTargets"]) if "initialVolume" in data.keys(): self.initialVolume = self._loadOrGetFileData( directory, data["initialVolume"], slicer.util.loadVolume) def loadZFrameRegistrationData(self, data, directory): if "zFrameRegistration" in data.keys(): zFrameRegistration = data["zFrameRegistration"] volume = self._loadOrGetFileData(directory, zFrameRegistration["volume"], slicer.util.loadVolume) transform = self._loadOrGetFileData( directory, zFrameRegistration["transform"], slicer.util.loadTransform) name = zFrameRegistration["name"] if zFrameRegistration.has_key( "name") else volume.GetName() self.zFrameRegistrationResult = ZFrameRegistrationResult(name) self.zFrameRegistrationResult.volume = volume self.zFrameRegistrationResult.transform = transform if zFrameRegistration["seriesType"]: self.seriesTypeManager.assign( self.zFrameRegistrationResult.name, zFrameRegistration["seriesType"]) def loadProcedureEvents(self, data): procedureEvents = data["procedureEvents"] self.startTime = procedureEvents["caseStarted"] self.completed = "caseCompleted" in procedureEvents.keys() if self.completed: self.completedLogTimeStamp = procedureEvents["caseCompleted"] if "caseClosed" in procedureEvents.keys(): self.closedLogTimeStamps = procedureEvents["caseClosed"] if "caseResumed" in procedureEvents.keys(): self.resumeTimeStamps = procedureEvents["caseResumed"] def loadPreopData(self, data): if data.has_key("preop"): self.preopData = PreopData.createFromJSON(data["preop"]) def loadResults(self, data, directory): if len(data["results"]): self.customProgressBar.maximum = len(data["results"]) for index, jsonResult in enumerate(data["results"], start=1): name = jsonResult["name"] logging.debug("processing %s" % name) result = self.createResult(name, invokeEvent=False) self.customProgressBar.updateStatus( "Loading series registration result %s" % result.name, index) slicer.app.processEvents() for attribute, value in jsonResult.items(): logging.debug("found %s: %s" % (attribute, value)) if attribute == 'volumes': self._loadResultFileData(value, directory, slicer.util.loadVolume, result.setVolume) elif attribute == 'transforms': self._loadResultFileData(value, directory, slicer.util.loadTransform, result.setTransform) elif attribute == 'targets': approved = value.pop('approved', None) original = value.pop('original', None) self._loadResultFileData( value, directory, slicer.util.loadMarkupsFiducialList, result.setTargets) if approved: approvedTargets = self._loadOrGetFileData( directory, approved["fileName"], slicer.util.loadMarkupsFiducialList) setattr(result.targets, 'approved', approvedTargets) result.targets.modifiedTargets[jsonResult["status"][ "registrationType"]] = approved["userModified"] if original: originalTargets = self._loadOrGetFileData( directory, original, slicer.util.loadMarkupsFiducialList) setattr(result.targets, 'original', originalTargets) elif attribute == 'labels': self._loadResultFileData(value, directory, slicer.util.loadLabelVolume, result.setLabel) elif attribute == 'status': result.status = value["state"] result.timestamp = value["time"] result.registrationType = value[ "registrationType"] if value.has_key( "registrationType") else None result.consentGivenBy = value[ "consentGivenBy"] if value.has_key( "consentGivenBy") else None elif attribute == 'series': result.receivedTime = value['receivedTime'] seriesType = value["type"] if value.has_key( "type") else None self.seriesTypeManager.assign(name, seriesType) elif attribute == 'registration': result.startTime = value['startTime'] result.endTime = value['endTime'] elif attribute == 'segmentation': result.segmentationData = SegmentationData.createFromJSON( value) else: setattr(result, attribute, value) self.customProgressBar.text = "Finished loading registration results" def _loadResultFileData(self, dictionary, directory, loadFunction, setFunction): for regType, filename in dictionary.items(): data = self._loadOrGetFileData(directory, filename, loadFunction) setFunction(regType, data) def _loadOrGetFileData(self, directory, filename, loadFunction): if not filename: return None try: data = self.alreadyLoadedFileNames[filename] except KeyError: _, data = loadFunction(os.path.join(directory, filename), returnNode=True) self.alreadyLoadedFileNames[filename] = data return data def generateLogfileTimeStampDict(self): return { "time": self.getTime(), "logfile": os.path.basename(self.getSlicerErrorLogPath()) } def close(self, outputDir): if not self.completed: self.closedLogTimeStamps.append( self.generateLogfileTimeStampDict()) return self.save(outputDir) def save(self, outputDir): if not os.path.exists(outputDir): self.createDirectory(outputDir) successfullySavedFileNames = [] failedSaveOfFileNames = [] logFilePath = self.getSlicerErrorLogPath() shutil.copy(logFilePath, os.path.join(outputDir, os.path.basename(logFilePath))) successfullySavedFileNames.append( os.path.join(outputDir, os.path.basename(logFilePath))) def saveManualSegmentation(): if self.segmentModelNode: success, name = self.saveNodeData(self.segmentModelNode, outputDir, FileExtension.VTK) self.handleSaveNodeDataReturn(success, name, successfullySavedFileNames, failedSaveOfFileNames) if self.inputMarkupNode: success, name = self.saveNodeData(self.inputMarkupNode, outputDir, FileExtension.FCSV) self.handleSaveNodeDataReturn(success, name, successfullySavedFileNames, failedSaveOfFileNames) def saveInitialTargets(): success, name = self.saveNodeData(self.initialTargets, outputDir, FileExtension.FCSV, name="Initial_Targets") self.handleSaveNodeDataReturn(success, name, successfullySavedFileNames, failedSaveOfFileNames) return name + FileExtension.FCSV def saveInitialVolume(): success, name = self.saveNodeData(self.initialVolume, outputDir, FileExtension.NRRD) self.handleSaveNodeDataReturn(success, name, successfullySavedFileNames, failedSaveOfFileNames) return name + FileExtension.NRRD def createResultsList(): results = [] for result in sorted(self.getResultsAsList(), key=lambda r: r.seriesNumber): results.append(result.asDict()) return results saveManualSegmentation() data = {"results": createResultsList()} if self.preopData: self.preopData.save(outputDir) data["preop"] = self.preopData.toJSON() data.update(self.getGITRevisionInformation()) def addProcedureEvents(): procedureEvents = { "caseStarted": self.startTime, } if len(self.closedLogTimeStamps): procedureEvents["caseClosed"] = self.closedLogTimeStamps if len(self.resumeTimeStamps): procedureEvents["caseResumed"] = self.resumeTimeStamps if self.completed: procedureEvents["caseCompleted"] = self.completedLogTimeStamp data["procedureEvents"] = procedureEvents addProcedureEvents() if self.zFrameRegistrationResult: data["zFrameRegistration"] = self.zFrameRegistrationResult.save( outputDir) if self.initialTargets: data["initialTargets"] = saveInitialTargets() if self.initialVolume: data["initialVolume"] = saveInitialVolume() destinationFile = os.path.join(outputDir, SliceTrackerConstants.JSON_FILENAME) with open(destinationFile, 'w') as outfile: logging.debug("Writing registration results to %s" % destinationFile) json.dump(data, outfile, indent=2) failedSaveOfFileNames += self.saveRegistrationResults(outputDir) self.printOutput("The following data was successfully saved:\n", successfullySavedFileNames) self.printOutput("The following data failed to saved:\n", failedSaveOfFileNames) return len(failedSaveOfFileNames) == 0, failedSaveOfFileNames def getGITRevisionInformation(self): import inspect dirname = os.path.dirname(inspect.getfile(self.__class__)) def getLocalGITRevisionInformation(): try: from git import Repo, InvalidGitRepositoryError repo = Repo(dirname, search_parent_directories=True) branch = repo.active_branch print(branch.name) return { "GIT_WC_URL": repo.remote().url, "GIT_COMMIT_HASH": repo.head.object.hexsha } except (ImportError, InvalidGitRepositoryError): return {} filename = os.path.join(dirname, "..", "Resources/version.json") with open(filename) as data_file: logging.debug("reading version json file %s" % filename) data = json.load(data_file) if not data["GIT_COMMIT_HASH"]: data = getLocalGITRevisionInformation() return data def printOutput(self, message, fileNames): if not len(fileNames): return for fileName in fileNames: message += fileName + "\n" logging.debug(message) @logmethod(level=logging.DEBUG) def saveRegistrationResults(self, outputDir): failedToSave = [] self.customProgressBar.visible = True for index, result in enumerate(self.getResultsAsList(), start=1): self.customProgressBar.maximum = len(self.registrationResults) self.customProgressBar.updateStatus( "Saving registration result for series %s" % result.name, index) slicer.app.processEvents() if result not in self._savedRegistrationResults: successfulList, failedList = result.save(outputDir) failedToSave += failedList self._savedRegistrationResults.append(result) self.customProgressBar.text = "Registration data successfully saved" if len( failedToSave) == 0 else "Error/s occurred during saving" return failedToSave def _registrationResultHasStatus(self, series, status, method=all): if not type(series) is int: series = RegistrationResult.getSeriesNumberFromString(series) results = self.getResultsBySeriesNumber(series) return method(result.status == status for result in results) if len(results) else False def registrationResultWasApproved(self, series): return self._registrationResultHasStatus( series, RegistrationStatus.APPROVED_STATUS, method=any) def registrationResultWasSkipped(self, series): return self._registrationResultHasStatus( series, RegistrationStatus.SKIPPED_STATUS) def registrationResultWasRejected(self, series): return self._registrationResultHasStatus( series, RegistrationStatus.REJECTED_STATUS) def registrationResultWasApprovedOrRejected(self, series): return self._registrationResultHasStatus(series, RegistrationStatus.REJECTED_STATUS) or \ self._registrationResultHasStatus(series, RegistrationStatus.APPROVED_STATUS) def getResultsAsList(self): return self.registrationResults.values() def getMostRecentApprovedCoverProstateRegistration(self): seriesTypeManager = SeriesTypeManager() for result in self.registrationResults.values(): if seriesTypeManager.isCoverProstate( result.name) and result.approved: return result return None def getLastApprovedRigidTransformation(self): if sum([ 1 for result in self.registrationResults.values() if result.approved ]) == 1: lastRigidTfm = None else: lastRigidTfm = self.getMostRecentApprovedResult().transforms.rigid if not lastRigidTfm: lastRigidTfm = slicer.vtkMRMLLinearTransformNode() slicer.mrmlScene.AddNode(lastRigidTfm) return lastRigidTfm @onExceptionReturnNone def getMostRecentApprovedTransform(self): seriesTypeManager = SeriesTypeManager() results = sorted(self.registrationResults.values(), key=lambda s: s.seriesNumber) for result in reversed(results): if result.approved and not seriesTypeManager.isCoverProstate( result.name): return result.getTransform(result.registrationType) return None @onExceptionReturnNone def getResult(self, series): return self.registrationResults[series] def getResultsBySeries(self, series): seriesNumber = RegistrationResult.getSeriesNumberFromString(series) return self.getResultsBySeriesNumber(seriesNumber) def getResultsBySeriesNumber(self, seriesNumber): return [ result for result in self.getResultsAsList() if seriesNumber == result.seriesNumber ] def removeResult(self, series): # TODO: is this method ever used? try: del self.registrationResults[series] except KeyError: pass def exists(self, series): return series in self.registrationResults.keys() @onExceptionReturnNone def getMostRecentApprovedResult(self, priorToSeriesNumber=None): results = sorted(self.registrationResults.values(), key=lambda s: s.seriesNumber) if priorToSeriesNumber: results = [ result for result in results if result.seriesNumber < priorToSeriesNumber ] for result in reversed(results): if result.approved: return result return None def getApprovedOrLastResultForSeries(self, series): results = self.getResultsBySeries(series) for result in results: if result.approved: return result return results[-1]
class ProstateAblationWidget(ModuleWidgetMixin, ScriptedLoadableModuleWidget): def __init__(self, parent=None): ScriptedLoadableModuleWidget.__init__(self, parent) self.modulePath = os.path.dirname( slicer.util.modulePath(self.moduleName)) ConfigurationParser( os.path.join(self.modulePath, 'Resources', "default.cfg")) self.logic = ProstateAblationLogic() self.session = ProstateAblationSession() self.session.steps = [] self.session.removeEventObservers() self.session.addEventObserver(self.session.CloseCaseEvent, lambda caller, event: self.cleanup()) self.session.addEventObserver( SlicerDevelopmentToolboxEvents.NewFileIndexedEvent, self.onNewFileIndexed) self.demoMode = False self.developerMode = True def enter(self): if not slicer.dicomDatabase: slicer.util.errorDisplay( "Slicer DICOMDatabase was not found. In order to be able to use ProstateAblation, you will " "need to set a proper location for the Slicer DICOMDatabase.") self.layout.parent().enabled = slicer.dicomDatabase is not None def exit(self): pass def onReload(self): ScriptedLoadableModuleWidget.onReload(self) @logmethod(logging.DEBUG) def cleanup(self): ScriptedLoadableModuleWidget.cleanup(self) #self.patientWatchBox.sourceFile = None #self.intraopWatchBox.sourceFile = None def setup(self): ScriptedLoadableModuleWidget.setup(self) for step in [ ProstateAblationOverviewStep, ProstateAblationZFrameRegistrationStep, ProstateAblationTargetingStep, ProstateAblationGuidanceStep ]: registeredStep = step(self.session) self.session.registerStep(registeredStep) self.customStatusProgressBar = CustomStatusProgressbar() self.setupPatientWatchBox() self.setupViewSettingGroupBox() self.setupTabBarNavigation() self.setupSessionObservers() def setupPatientWatchBox(self): WatchBoxAttribute.TRUNCATE_LENGTH = 20 self.patientWatchBoxInformation = [ WatchBoxAttribute('PatientName', 'Name: ', DICOMTAGS.PATIENT_NAME, masked=self.demoMode), WatchBoxAttribute('PatientID', 'PID: ', DICOMTAGS.PATIENT_ID, masked=self.demoMode), WatchBoxAttribute('DOB', 'DOB: ', DICOMTAGS.PATIENT_BIRTH_DATE, masked=self.demoMode), WatchBoxAttribute('StudyDate', 'StudyDate: ', DICOMTAGS.STUDY_DATE) ] self.patientWatchBox = DICOMBasedInformationWatchBox( self.patientWatchBoxInformation, title="Patient Information", columns=2) self.layout.addWidget(self.patientWatchBox) intraopWatchBoxInformation = [ WatchBoxAttribute( 'CurrentSeries', 'Current Series: ', [DICOMTAGS.SERIES_NUMBER, DICOMTAGS.SERIES_DESCRIPTION]), WatchBoxAttribute('StudyDate', 'Intraop Date: ', DICOMTAGS.STUDY_DATE) ] self.intraopWatchBox = DICOMBasedInformationWatchBox( intraopWatchBoxInformation, columns=2) self.layout.addWidget(self.intraopWatchBox) def setupViewSettingGroupBox(self): iconSize = qt.QSize(24, 24) self.redOnlyLayoutButton = RedSliceLayoutButton() self.sideBySideLayoutButton = SideBySideLayoutButton() self.fourUpLayoutButton = FourUpLayoutButton() self.layoutButtons = [ self.redOnlyLayoutButton, self.sideBySideLayoutButton, self.fourUpLayoutButton ] self.screenShotButton = ScreenShotButton() self.screenShotButton.caseResultDir = "" self.settingsButton = ModuleSettingsButton(self.moduleName) self.affectiveZoneIcon = self.createIcon('icon-needle.png') self.showAffectiveZoneButton = self.createButton( "", icon=self.affectiveZoneIcon, iconSize=iconSize, checkable=True, toolTip="Display the effective ablation zone") self.showAffectiveZoneButton.connect( 'toggled(bool)', self.session.onShowAffectiveZoneToggled) viewSettingButtons = [ self.redOnlyLayoutButton, self.fourUpLayoutButton, self.settingsButton, self.screenShotButton, self.showAffectiveZoneButton ] for step in self.session.steps: viewSettingButtons += step.viewSettingButtons self.layout.addWidget(self.createHLayout(viewSettingButtons)) self.setupAdditionalViewSettingButtons() self.resetViewSettingButtons() def setupAdditionalViewSettingButtons(self): for step in self.session.steps: step.setupIcons() step.setupAdditionalViewSettingButtons() def resetViewSettingButtons(self): for step in self.session.steps: step.resetViewSettingButtons() #self.crosshairButton.checked = False def setupTabBarNavigation(self): self.tabWidget = ProstateAblationTabWidget(self.session) self.tabWidget.addEventObserver( self.tabWidget.AvailableLayoutsChangedEvent, self.onAvailableLayoutsChanged) self.layout.addWidget(self.tabWidget) self.tabWidget.hideTabs() def setupSessionObservers(self): self.session.addEventObserver(self.session.CurrentSeriesChangedEvent, self.onCurrentSeriesChanged) self.session.addEventObserver(self.session.NewCaseStartedEvent, self.onUpdateScreenShotDir) self.session.addEventObserver(self.session.CaseOpenedEvent, self.onUpdateScreenShotDir) def removeSessionObservers(self): self.session.removeEventObserver( self.session.CurrentSeriesChangedEvent, self.onCurrentSeriesChanged) self.session.removeEventObserver(self.session.NewCaseStartedEvent, self.onUpdateScreenShotDir) self.session.removeEventObserver(self.session.CaseOpenedEvent, self.onUpdateScreenShotDir) def onUpdateScreenShotDir(self, caller, event): self.screenShotButton.caseResultDir = self.session.outputDirectory @vtk.calldata_type(vtk.VTK_STRING) def onNewFileIndexed(self, caller, event, callData): text, size, currentIndex = ast.literal_eval(callData) if not self.customStatusProgressBar.visible: self.customStatusProgressBar.show() self.customStatusProgressBar.maximum = size self.customStatusProgressBar.updateStatus(text, currentIndex) @vtk.calldata_type(vtk.VTK_STRING) def onCurrentSeriesChanged(self, caller, event, callData): receivedFile = self.session.loadableList[callData][ 0] if callData else None if self.patientWatchBox.sourceFile is None: self.patientWatchBox.sourceFile = receivedFile self.intraopWatchBox.sourceFile = receivedFile @vtk.calldata_type(vtk.VTK_STRING) def onAvailableLayoutsChanged(self, caller, event, callData): layouts = ast.literal_eval(callData) for layoutButton in self.layoutButtons: layoutButton.enabled = layoutButton.LAYOUT in layouts