コード例 #1
0
 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
コード例 #2
0
ファイル: session.py プロジェクト: SmartTemplate/SliceTracker
 def __init__(self):
   StepBasedSession.__init__(self)
   self.registrationLogic = SliceTrackerRegistrationLogic()
   self.seriesTypeManager = SeriesTypeManager()
   self.seriesTypeManager.addEventObserver(self.seriesTypeManager.SeriesTypeManuallyAssignedEvent,
                                           lambda caller, event: self.invokeEvent(self.SeriesTypeManuallyAssignedEvent))
   self.resetAndInitializeMembers()
コード例 #3
0
 def __init__(self):
     StepBasedSession.__init__(self)
     self.seriesTypeManager = SeriesTypeManager()
     self.seriesTypeManager.addEventObserver(
         self.seriesTypeManager.SeriesTypeManuallyAssignedEvent,
         lambda caller, event: self.invokeEvent(
             self.SeriesTypeManuallyAssignedEvent))
     self.targetingPlugin = TargetsDefinitionPlugin(self)
     self.needlePathCaculator = ZFrameGuidanceComputation(self)
     self.segmentationEditor = slicer.qMRMLSegmentEditorWidget()
     self.resetAndInitializeMembers()
     self.resetAndInitializedTargetsAndSegments()
コード例 #4
0
 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()
コード例 #5
0
 def save(self, outputDir):
   seriesTypeManager = SeriesTypeManager()
   dictionary = {
     "name": self.name,
     "seriesType": seriesTypeManager.getSeriesType(self.name)
   }
   savedSuccessfully = []
   failedToSave = []
   success, name = self.saveNodeData(self.transform, outputDir, FileExtension.H5)
   dictionary["transform"] = name + FileExtension.H5
   self.handleSaveNodeDataReturn(success, name, savedSuccessfully, failedToSave)
   success, name = self.saveNodeData(self.volume, outputDir, FileExtension.NRRD)
   dictionary["volume"] = name + FileExtension.NRRD
   self.handleSaveNodeDataReturn(success, name, savedSuccessfully, failedToSave)
   return dictionary
コード例 #6
0
 def asDict(self):
   seriesTypeManager = SeriesTypeManager()
   dictionary = super(RegistrationResult, self).asDict()
   dictionary.update({
     "name": self.name,
     "series":{
       "type": seriesTypeManager.getSeriesType(self.name),
       "receivedTime": self.receivedTime
     }
   })
   if self.approved or self.rejected:
     dictionary["targets"] = self.targets.getAllFileNames()
     dictionary["transforms"] = self.transforms.getAllFileNames()
     dictionary["volumes"] = self.volumes.getAllFileNames()
     dictionary["labels"] = self.labels.getAllFileNames()
     dictionary["suffix"] = self.suffix
     if self.startTime and self.endTime:
       dictionary["registration"] = {
         "startTime": self.startTime,
         "endTime": self.endTime
       }
     if self.approved:
       dictionary["status"]["registrationType"] = self.registrationType
   elif self.skipped:
     dictionary["volumes"] = {
       "fixed": self.volumes.getFileName(self.volumes.fixed)
     }
   if self.score:
     dictionary["score"] = self.score
   if self.approved:
     dictionary["targets"]["approved"] = {
       "userModified": self.getApprovedTargetsModifiedStatus(),
       "fileName": self.targets.getFileNameByAttributeName("approved")
     }
   if self.segmentationData:
     dictionary["segmentation"] = self.segmentationData.toJSON()
   return dictionary
コード例 #7
0
  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 = {}
コード例 #8
0
class SessionData(ModuleLogicMixin):

    NewResultCreatedEvent = vtk.vtkCommand.UserEvent + 901

    DEFAULT_JSON_FILE_NAME = "results.json"

    _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()

    def __init__(self):
        self.resetAndInitializeData()

    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 createZFrameRegistrationResult(self, series):
        self.zFrameRegistrationResult = ZFrameRegistrationResult(series)
        return self.zFrameRegistrationResult

    def readProcedureEvents(self, procedureEvents):
        self.startTimeStamp = 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 load(self, filename):
        directory = os.path.dirname(filename)
        self.resetAndInitializeData()
        self.alreadyLoadedFileNames = {}
        with open(filename) as data_file:
            self.customProgressBar.visible = True
            self.customProgressBar.text = "Reading meta information"
            logging.debug("reading json file %s" % filename)
            data = json.load(data_file)

            self.readProcedureEvents(data["procedureEvents"])

            if "intraOpTargets" in data.keys():
                intraOpTargetsInfo = data["intraOpTargets"]
                if type(intraOpTargetsInfo) == type(dict()):
                    self.intraOpTargets = self._loadOrGetFileData(
                        directory, intraOpTargetsInfo["targetFile"],
                        slicer.util.loadMarkupsFiducialList)
                    self.savedNeedleTypeForTargets = intraOpTargetsInfo.get(
                        "needleType")
                elif type(intraOpTargetsInfo) == type(unicode(
                )):  # ensure backward compatibility to load old data
                    self.intraOpTargets = self._loadOrGetFileData(
                        directory, data["intraOpTargets"],
                        slicer.util.loadMarkupsFiducialList)
                self.intraOpTargets.SetLocked(True)

            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"])

            if "initialVolume" in data.keys():
                self.initialVolume = self._loadOrGetFileData(
                    directory, data["initialVolume"], slicer.util.loadVolume)

            if "tumorSegmentation" in data.keys():
                self.segmentModelNode = self._loadOrGetFileData(
                    directory, data["tumorSegmentation"],
                    slicer.util.loadSegmentation)

            #self.loadResults(data, directory) ## ?? why need to load twice
        return True

    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:
                if self.segmentModelNode.GetSegmentation().GetNumberOfSegments(
                ) > 0:
                    success, name = self.saveNodeData(self.segmentModelNode,
                                                      outputDir,
                                                      ".seg.nrrd",
                                                      overwrite=True)
                    self.handleSaveNodeDataReturn(success, name,
                                                  successfullySavedFileNames,
                                                  failedSaveOfFileNames)
                    return name + ".seg.nrrd"
            return None

        def saveInitialVolume():
            success, name = self.saveNodeData(self.initialVolume,
                                              outputDir,
                                              FileExtension.NRRD,
                                              overwrite=True)
            self.handleSaveNodeDataReturn(success, name,
                                          successfullySavedFileNames,
                                          failedSaveOfFileNames)
            return name + FileExtension.NRRD

        data = {}

        def saveIntraOpTargets():
            success, name = self.saveNodeData(self.intraOpTargets,
                                              outputDir,
                                              FileExtension.FCSV,
                                              name="IntraOpTargets",
                                              overwrite=True)
            self.handleSaveNodeDataReturn(success, name,
                                          successfullySavedFileNames,
                                          failedSaveOfFileNames)
            intraOpTargetsInfo = {"targetFile": name + FileExtension.FCSV}
            intraOpTargetsInfo["needleType"] = self.savedNeedleTypeForTargets
            data["intraOpTargets"] = intraOpTargetsInfo
            return

        def addProcedureEvents():
            procedureEvents = {
                "caseStarted": self.startTimeStamp,
            }
            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.intraOpTargets:
            saveIntraOpTargets()

        if self.initialVolume:
            data["initialVolume"] = saveInitialVolume()

        if self.segmentModelNode:
            data["tumorSegmentation"] = saveManualSegmentation()

        destinationFile = os.path.join(outputDir, self.DEFAULT_JSON_FILE_NAME)
        with open(destinationFile, 'w') as outfile:
            logging.debug("Writing registration results to %s" %
                          destinationFile)
            json.dump(data, outfile, indent=2)

        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 printOutput(self, message, fileNames):
        if not len(fileNames):
            return
        for fileName in fileNames:
            message += fileName + "\n"
        logging.debug(message)
コード例 #9
0
ファイル: session.py プロジェクト: courins/SliceTracker
class SliceTrackerSession(StepBasedSession):

    IncomingDataSkippedEvent = SlicerDevelopmentToolboxEvents.SkippedEvent

    IncomingIntraopDataReceiveFinishedEvent = SlicerDevelopmentToolboxEvents.FinishedEvent + 111
    NewImageSeriesReceivedEvent = SlicerDevelopmentToolboxEvents.NewImageDataReceivedEvent

    ZFrameRegistrationSuccessfulEvent = vtk.vtkCommand.UserEvent + 140
    PreprocessingSuccessfulEvent = vtk.vtkCommand.UserEvent + 141
    LoadingMetadataSuccessfulEvent = vtk.vtkCommand.UserEvent + 143
    SegmentationCancelledEvent = vtk.vtkCommand.UserEvent + 144

    CurrentSeriesChangedEvent = vtk.vtkCommand.UserEvent + 151
    CurrentResultChangedEvent = vtk.vtkCommand.UserEvent + 152
    RegistrationStatusChangedEvent = vtk.vtkCommand.UserEvent + 153
    TargetSelectionEvent = vtk.vtkCommand.UserEvent + 154

    InitiateZFrameCalibrationEvent = vtk.vtkCommand.UserEvent + 160
    InitiateSegmentationEvent = vtk.vtkCommand.UserEvent + 161
    InitiateRegistrationEvent = vtk.vtkCommand.UserEvent + 162
    InitiateEvaluationEvent = vtk.vtkCommand.UserEvent + 163

    SeriesTypeManuallyAssignedEvent = SeriesTypeManager.SeriesTypeManuallyAssignedEvent

    MODULE_NAME = SliceTrackerConstants.MODULE_NAME

    @property
    def preprocessedDirectory(self):
        return os.path.join(self.directory,
                            "mpReviewPreprocessed") if self.directory else None

    @property
    def preopDICOMDirectory(self):
        return os.path.join(self.directory, "DICOM",
                            "Preop") if self.directory else None

    @property
    def intraopDICOMDirectory(self):
        return os.path.join(self.directory, "DICOM",
                            "Intraop") if self.directory else None

    @property
    def outputDirectory(self):
        return os.path.join(self.directory, "SliceTrackerOutputs")

    @property
    def approvedCoverTemplate(self):
        try:
            return self.data.zFrameRegistrationResult.volume
        except AttributeError:
            return None

    @approvedCoverTemplate.setter
    def approvedCoverTemplate(self, volume):
        self.data.zFrameRegistrationResult.volume = volume
        self.zFrameRegistrationSuccessful = volume is not None

    @property
    def zFrameRegistrationSuccessful(self):
        self._zFrameRegistrationSuccessful = getattr(
            self, "_zFrameRegistrationSuccessful", None)
        return self.data.zFrameRegistrationResult is not None and self._zFrameRegistrationSuccessful

    @zFrameRegistrationSuccessful.setter
    def zFrameRegistrationSuccessful(self, value):
        self._zFrameRegistrationSuccessful = value
        if self._zFrameRegistrationSuccessful:
            self.save()
            self.invokeEvent(self.ZFrameRegistrationSuccessfulEvent)

    @property
    def currentResult(self):
        return self._getCurrentResult()

    @onExceptionReturnNone
    def _getCurrentResult(self):
        return self.data.registrationResults[self._currentResult]

    @currentResult.setter
    def currentResult(self, series):
        if self.currentResult and self.currentResult.name == series:
            return
        if self.currentResult is not None:
            self.currentResult.removeEventObservers()
        self._currentResult = series
        if self.currentResult:
            for event in RegistrationResult.StatusEvents.values():
                self.currentResult.addEventObserver(
                    event, self.onRegistrationResultStatusChanged)
        self.invokeEvent(self.CurrentResultChangedEvent)

    @property
    def currentSeries(self):
        self._currentSeries = getattr(self, "_currentSeries", None)
        return self._currentSeries

    @currentSeries.setter
    def currentSeries(self, series):
        if series == self.currentSeries:
            return
        if series and series not in self.seriesList:
            raise UnknownSeriesError("Series %s is unknown" % series)
        self._currentSeries = series
        self.invokeEvent(self.CurrentSeriesChangedEvent, series)

    @property
    def currentSeriesVolume(self):
        if not self.currentSeries:
            return None
        else:
            return self.getOrCreateVolumeForSeries(self.currentSeries)

    @property
    def movingVolume(self):
        self._movingVolume = getattr(self, "_movingVolume", None)
        return self._movingVolume

    @movingVolume.setter
    def movingVolume(self, value):
        self._movingVolume = value

    @property
    def movingLabel(self):
        self._movingLabel = getattr(self, "_movingLabel", None)
        return self._movingLabel

    @movingLabel.setter
    def movingLabel(self, value):
        self._movingLabel = value

    @property
    def movingTargets(self):
        self._movingTargets = getattr(self, "_movingTargets", None)
        if self.isCurrentSeriesCoverProstateInNonPreopMode():
            return self.data.initialTargets
        return self._movingTargets

    @movingTargets.setter
    def movingTargets(self, value):
        if self.isCurrentSeriesCoverProstateInNonPreopMode():
            self.data.initialTargets = value
        self._movingTargets = value

    @property
    def fixedVolume(self):
        self._fixedVolume = getattr(self, "_fixedVolume", None)
        if self.isCurrentSeriesCoverProstateInNonPreopMode():
            return self.data.initialVolume
        return self._fixedVolume

    @fixedVolume.setter
    def fixedVolume(self, value):
        if self.isCurrentSeriesCoverProstateInNonPreopMode():
            self.data.initialVolume = value
        self._fixedVolume = value

    @property
    def fixedLabel(self):
        self._fixedLabel = getattr(self, "_fixedLabel", None)
        if self.isCurrentSeriesCoverProstateInNonPreopMode():
            return self.data.initialLabel
        return self._fixedLabel

    @fixedLabel.setter
    def fixedLabel(self, value):
        if self.isCurrentSeriesCoverProstateInNonPreopMode():
            self.data.initialLabel = value
        self._fixedLabel = value

    def setSelectedTarget(self, info):
        self.invokeEvent(self.TargetSelectionEvent, str(info))

    def __init__(self):
        StepBasedSession.__init__(self)
        self.registrationLogic = SliceTrackerRegistrationLogic()
        self.seriesTypeManager = SeriesTypeManager()
        self.seriesTypeManager.addEventObserver(
            self.seriesTypeManager.SeriesTypeManuallyAssignedEvent,
            lambda caller, event: self.invokeEvent(
                self.SeriesTypeManuallyAssignedEvent))
        self.resetAndInitializeMembers()

    def resetAndInitializeMembers(self):
        self._busy = False
        self.seriesTypeManager.clear()
        self.initializeColorNodes()
        self.directory = None
        self.data = SessionData()
        self.data.addEventObserver(self.data.NewResultCreatedEvent,
                                   self.onNewRegistrationResultCreated)
        self.trainingMode = False
        self.resetPreopDICOMReceiver()
        self.resetIntraopDICOMReceiver()
        self.loadableList = {}
        self.seriesList = []
        self.seriesTimeStamps = dict()
        self.alreadyLoadedSeries = {}
        self._currentResult = None
        self._currentSeries = None
        self.retryMode = False
        self.previousStep = None
        self.temporaryIntraopTargets = None

    def initializeColorNodes(self):
        from mpReview import mpReviewLogic
        self.mpReviewColorNode, self.structureNames = mpReviewLogic.loadColorTable(
            self.getSetting("Color_File_Name"))
        self.segmentedColorName = self.getSetting("Segmentation_Color_Name")
        self.segmentedLabelValue = self.mpReviewColorNode.GetColorIndexByName(
            self.segmentedColorName)

    def __del__(self):
        super(SliceTrackerSession, self).__del__()
        self.clearData()

    def clearData(self):
        self.resetAndInitializeMembers()

    def onMrmlSceneCleared(self, caller, event):
        self.initializeColorNodes()

    @onExceptionReturnFalse
    def isCurrentSeriesCoverProstateInNonPreopMode(self):
        return self.seriesTypeManager.isCoverProstate(
            self.currentSeries) and not self.data.usePreopData

    def isBusy(self):
        return self.isPreProcessing() or self._busy

    def isPreProcessing(self):
        return slicer.util.selectedModule() != self.MODULE_NAME

    def isCaseDirectoryValid(self):
        return all(
            os.path.exists(p)
            for p in [self.preopDICOMDirectory, self.intraopDICOMDirectory])

    def isRunning(self):
        return not self.directory in [None, '']

    def processDirectory(self):
        self.newCaseCreated = getattr(self, "newCaseCreated", False)
        if self.newCaseCreated:
            return
        if self.isCaseDirectoryValid():
            self.loadCaseData()
            self.invokeEvent(self.CaseOpenedEvent)
        else:
            slicer.util.warningDisplay(
                "The selected case directory seems not to be valid",
                windowTitle="SliceTracker")
            self.close(save=False)

    def createNewCase(self, destination):
        self.newCaseCreated = True
        self.resetAndInitializeMembers()
        self.directory = destination
        self.createDirectory(self.preopDICOMDirectory)
        self.createDirectory(self.intraopDICOMDirectory)
        self.createDirectory(self.preprocessedDirectory)
        self.createDirectory(self.outputDirectory)
        self.startPreopDICOMReceiver()
        self.newCaseCreated = False
        self.invokeEvent(self.NewCaseStartedEvent)

    def close(self, save=False):
        if not self.isRunning():
            return
        message = None
        if save:
            success, failedFileNames = self.data.close(self.outputDirectory)
            message = "Case data has been saved successfully." if success else \
              "The following data failed to saved:\n %s" % failedFileNames
        self.resetAndInitializeMembers()
        self.invokeEvent(self.CloseCaseEvent, str(message))

    def save(self):
        success, failedFileNames = self.data.save(self.outputDirectory)
        return success and not len(
            failedFileNames
        ), "The following data failed to saved:\n %s" % failedFileNames

    def complete(self):
        self.data.completed = True
        self.close(save=True)

    def load(self):
        filename = os.path.join(self.outputDirectory,
                                SliceTrackerConstants.JSON_FILENAME)
        completed = self.data.wasSessionCompleted(filename)
        if slicer.util.confirmYesNoDisplay("A %s session has been found for the selected case. Do you want to %s?" \
                                            % ("completed" if completed else "started",
                                               "open it" if completed else "continue this session")):
            slicer.app.layoutManager().blockSignals(True)
            self._busy = True
            self.data.load(filename)
            self.postProcessLoadedSessionData()
            slicer.app.layoutManager().blockSignals(False)
            self.invokeEvent(self.LoadingMetadataSuccessfulEvent)
        else:
            self.clearData()

    def postProcessLoadedSessionData(self):
        coverProstate = self.data.getMostRecentApprovedCoverProstateRegistration(
        )
        if coverProstate:
            if not self.data.initialVolume:
                self.data.initialVolume = coverProstate.volumes.moving if self.data.usePreopData else coverProstate.volumes.fixed
            self.data.initialTargets = coverProstate.targets.original
            if self.data.usePreopData:  # TODO: makes sense?
                self.data.preopLabel = coverProstate.labels.moving
        if self.data.zFrameRegistrationResult:
            self._zFrameRegistrationSuccessful = True
        self.data.resumed = not self.data.completed
        if self.data.usePreopData:
            preopDataManager = self.createPreopHandler()
            preopDataManager.loadPreProcessedData()
        else:
            if self.data.initialTargets:
                self.setupPreopLoadedTargets()
            self.startIntraopDICOMReceiver()

    def createPreopHandler(self):
        preopDataManager = PreopDataHandler(self.preopDICOMDirectory,
                                            self.preprocessedDirectory,
                                            self.data)
        preopDataManager.addEventObserver(
            preopDataManager.PreprocessedDataErrorEvent,
            lambda caller, event: self.close(save=False))
        preopDataManager.addEventObserver(
            preopDataManager.PreprocessingStartedEvent,
            self.onPreprocessingStarted)
        preopDataManager.addEventObserver(
            preopDataManager.PreprocessingFinishedEvent,
            self.onPreprocessingSuccessful)
        return preopDataManager

    def startPreopDICOMReceiver(self):
        self.resetPreopDICOMReceiver()
        self.preopDICOMReceiver = IncomingDataWindow(
            incomingDataDirectory=self.preopDICOMDirectory,
            incomingPort=self.getSetting("Incoming_DICOM_Port"),
            skipText="No preoperative images available")
        self.preopDICOMReceiver.addEventObserver(
            SlicerDevelopmentToolboxEvents.SkippedEvent,
            self.onSkippingPreopDataReception)
        self.preopDICOMReceiver.addEventObserver(
            SlicerDevelopmentToolboxEvents.CanceledEvent,
            lambda caller, event: self.close())
        self.preopDICOMReceiver.addEventObserver(
            SlicerDevelopmentToolboxEvents.FinishedEvent,
            self.onPreopDataReceptionFinished)
        self.preopDICOMReceiver.show()

    def onSkippingPreopDataReception(self, caller, event):
        self.data.usePreopData = False
        self.startIntraopDICOMReceiver()
        self.invokeEvent(self.IncomingDataSkippedEvent)

    def onPreopDataReceptionFinished(self, caller=None, event=None):
        self.data.usePreopData = True
        preopDataManager = self.createPreopHandler()
        preopDataManager.handle()

    def onPreprocessingStarted(self, caller, event):
        self._busy = True
        self.startIntraopDICOMReceiver()

    def resetPreopDICOMReceiver(self):
        self.preopDICOMReceiver = getattr(self, "preopDICOMReceiver", None)
        if self.preopDICOMReceiver:
            self.preopDICOMReceiver.hide()
            self.preopDICOMReceiver.removeEventObservers()
            self.preopDICOMReceiver = None

    def startIntraopDICOMReceiver(self):
        self.resetPreopDICOMReceiver()
        logging.debug("Starting DICOM Receiver for intra-procedural data")
        if not self.data.completed:
            self.resetIntraopDICOMReceiver()
            self.intraopDICOMReceiver = SmartDICOMReceiver(
                self.intraopDICOMDirectory,
                self.getSetting("Incoming_DICOM_Port"))
            self._observeIntraopDICOMReceiverEvents()
            self.intraopDICOMReceiver.start(not (
                self.trainingMode or self.data.completed))
        else:
            self.invokeEvent(SlicerDevelopmentToolboxEvents.StoppedEvent)
        self.importDICOMSeries(self.getFileList(self.intraopDICOMDirectory))
        if self.intraopDICOMReceiver:
            self.intraopDICOMReceiver.forceStatusChangeEventUpdate()

    def resetIntraopDICOMReceiver(self):
        self.intraopDICOMReceiver = getattr(self, "intraopDICOMReceiver", None)
        if self.intraopDICOMReceiver:
            self.intraopDICOMReceiver.stop()
            self.intraopDICOMReceiver.removeEventObservers()

    def _observeIntraopDICOMReceiverEvents(self):
        self.intraopDICOMReceiver.addEventObserver(
            self.intraopDICOMReceiver.IncomingDataReceiveFinishedEvent,
            self.onDICOMSeriesReceived)
        self.intraopDICOMReceiver.addEventObserver(
            SlicerDevelopmentToolboxEvents.StatusChangedEvent,
            self.onDICOMReceiverStatusChanged)

    @vtk.calldata_type(vtk.VTK_STRING)
    def onDICOMSeriesReceived(self, caller, event, callData):
        self.importDICOMSeries(ast.literal_eval(callData))
        if self.trainingMode is True:
            self.resetIntraopDICOMReceiver()

    @vtk.calldata_type(vtk.VTK_STRING)
    def onDICOMReceiverStatusChanged(self, caller, event, callData):
        customStatusProgressBar = CustomStatusProgressbar()
        customStatusProgressBar.text = callData
        customStatusProgressBar.busy = "Waiting" in callData

    def importDICOMSeries(self, newFileList):
        indexer = ctk.ctkDICOMIndexer()

        newSeries = []
        for currentIndex, currentFile in enumerate(newFileList, start=1):
            self.invokeEvent(
                SlicerDevelopmentToolboxEvents.NewFileIndexedEvent, [
                    "Indexing file %s" % currentFile,
                    len(newFileList), currentIndex
                ].__str__())
            slicer.app.processEvents()
            currentFile = os.path.join(self.intraopDICOMDirectory, currentFile)
            indexer.addFile(slicer.dicomDatabase, currentFile, None)
            series = self.makeSeriesNumberDescription(currentFile)
            if series not in self.seriesList:
                self.seriesTimeStamps[series] = self.getTime()
                self.seriesList.append(series)
                newSeries.append(series)
                self.loadableList[
                    series] = self.createLoadableFileListForSeries(series)
        self.seriesList = sorted(
            self.seriesList,
            key=lambda s: RegistrationResult.getSeriesNumberFromString(s))

        if len(newFileList):
            self.verifyPatientIDEquality(newFileList)
            self.invokeEvent(self.NewImageSeriesReceivedEvent,
                             newSeries.__str__())

    def verifyPatientIDEquality(self, receivedFiles):
        seriesNumberPatientID = self.getAdditionalInformationForReceivedSeries(
            receivedFiles)
        dicomFileName = self.getPatientIDValidationSource()
        if not dicomFileName:
            return
        currentInfo = self.getPatientInformation(dicomFileName)
        currentID = currentInfo["PatientID"]
        patientName = currentInfo["PatientName"]
        for seriesNumber, receivedInfo in seriesNumberPatientID.iteritems():
            patientID = receivedInfo["PatientID"]
            if patientID is not None and patientID != currentID:
                m = 'WARNING:\n' \
                    'Current case:\n' \
                    '  Patient ID: {0}\n' \
                    '  Patient Name: {1}\n' \
                    'Received image\n' \
                    '  Patient ID: {2}\n' \
                    '  Patient Name : {3}\n\n' \
                    'Do you want to keep this series? '.format(currentID, patientName, patientID, receivedInfo["PatientName"])
                if not slicer.util.confirmYesNoDisplay(
                        m,
                        title="Patient IDs Not Matching",
                        windowTitle="SliceTracker"):
                    self.deleteSeriesFromSeriesList(seriesNumber)

    def getPatientIDValidationSource(self):
        if len(self.loadableList.keys()) > 1:
            keys = self.loadableList.keys()
            keys.sort(
                key=lambda x: RegistrationResult.getSeriesNumberFromString(x))
            return self.loadableList[keys[0]][0]
        else:
            return None

    def getOrCreateVolumeForSeries(self, series):
        try:
            volume = self.alreadyLoadedSeries[series]
        except KeyError:
            files = self.loadableList[series]
            loadables = self.scalarVolumePlugin.examine([files])
            assert len(loadables)
            volume = self.scalarVolumePlugin.load(loadables[0])
            volume.SetName(loadables[0].name)
            self.alreadyLoadedSeries[series] = volume
        slicer.app.processEvents()
        return volume

    def createLoadableFileListForSeries(self, series):
        seriesNumber = RegistrationResult.getSeriesNumberFromString(series)
        loadableList = []
        for dcm in self.getFileList(self.intraopDICOMDirectory):
            currentFile = os.path.join(self.intraopDICOMDirectory, dcm)
            currentSeriesNumber = int(
                self.getDICOMValue(currentFile, DICOMTAGS.SERIES_NUMBER))
            if currentSeriesNumber and currentSeriesNumber == seriesNumber:
                loadableList.append(currentFile)
        return loadableList

    def deleteSeriesFromSeriesList(self, seriesNumber):
        for series in self.seriesList:
            currentSeriesNumber = RegistrationResult.getSeriesNumberFromString(
                series)
            if currentSeriesNumber == seriesNumber:
                self.seriesList.remove(series)
                for seriesFile in self.loadableList[series]:
                    logging.debug(
                        "removing {} from filesystem".format(seriesFile))
                    os.remove(seriesFile)
                del self.loadableList[series]

    def makeSeriesNumberDescription(self, dcmFile):
        seriesDescription = self.getDICOMValue(dcmFile,
                                               DICOMTAGS.SERIES_DESCRIPTION)
        seriesNumber = self.getDICOMValue(dcmFile, DICOMTAGS.SERIES_NUMBER)
        if not (seriesNumber and seriesDescription):
            raise DICOMValueError(
                "Missing Attribute(s):\nFile: {}\nseriesNumber: {}\nseriesDescription: {}"
                .format(dcmFile, seriesNumber, seriesDescription))
        return "{}: {}".format(seriesNumber, seriesDescription)

    def getAdditionalInformationForReceivedSeries(self, fileList):
        seriesNumberPatientID = {}
        for currentFile in [
                os.path.join(self.intraopDICOMDirectory, f) for f in fileList
        ]:
            seriesNumber = int(
                self.getDICOMValue(currentFile, DICOMTAGS.SERIES_NUMBER))
            if seriesNumber not in seriesNumberPatientID.keys():
                seriesNumberPatientID[
                    seriesNumber] = self.getPatientInformation(currentFile)
        return seriesNumberPatientID

    def getPatientInformation(self, currentFile):
        return {
            "PatientID":
            self.getDICOMValue(currentFile, DICOMTAGS.PATIENT_ID),
            "PatientName":
            self.getDICOMValue(currentFile, DICOMTAGS.PATIENT_NAME),
            "SeriesDescription":
            self.getDICOMValue(currentFile, DICOMTAGS.SERIES_DESCRIPTION)
        }

    def getSeriesForSubstring(self, substring):
        for series in reversed(self.seriesList):
            if substring in series:
                return series
        return None

    def loadCaseData(self):
        self._busy = True
        if self.hasJSONResults():
            self.load()
        else:
            if PreopDataHandler.wasDirectoryPreprocessed(
                    self.preprocessedDirectory):
                self.startIntraopDICOMReceiver()
                preopDataManager = self.createPreopHandler()
                preopDataManager.loadPreProcessedData()
            else:
                self.continueWithUnprocessedData()

    def continueWithUnprocessedData(self):
        if os.listdir(self.preopDICOMDirectory):
            self.onPreopDataReceptionFinished()
        elif os.listdir(self.intraopDICOMDirectory):
            self.data.usePreopData = False
            self.startIntraopDICOMReceiver()
        else:
            self.startPreopDICOMReceiver()

    def hasJSONResults(self):
        return os.path.exists(
            os.path.join(self.outputDirectory,
                         SliceTrackerConstants.JSON_FILENAME))

    def onPreprocessingSuccessful(self, caller, event):
        self.setupPreopLoadedTargets()
        self._busy = False
        self.invokeEvent(self.PreprocessingSuccessfulEvent)
        self.startIntraopDICOMReceiver()

    def setupPreopLoadedTargets(self):
        targets = self.data.initialTargets
        ModuleWidgetMixin.setFiducialNodeVisibility(targets, show=True)
        self.applyDefaultTargetDisplayNode(targets)
        self.markupsLogic.JumpSlicesToNthPointInMarkup(targets.GetID(), 0)

    def applyDefaultTargetDisplayNode(self, targetNode, new=False):
        displayNode = None if new else targetNode.GetDisplayNode()
        modifiedDisplayNode = self.setupDisplayNode(displayNode, True)
        targetNode.SetAndObserveDisplayNodeID(modifiedDisplayNode.GetID())

    def setupDisplayNode(self, displayNode=None, starBurst=False):
        if not displayNode:
            displayNode = slicer.vtkMRMLMarkupsDisplayNode()
            slicer.mrmlScene.AddNode(displayNode)
        displayNode.SetTextScale(0)
        displayNode.SetGlyphScale(2.5)
        if starBurst:
            displayNode.SetGlyphType(
                slicer.vtkMRMLAnnotationPointDisplayNode.StarBurst2D)
        return displayNode

    def getColorForSelectedSeries(self, series=None):
        series = series if series else self.currentSeries
        if series in [None, '']:
            return STYLE.WHITE_BACKGROUND
        style = STYLE.YELLOW_BACKGROUND
        if not self.isTrackingPossible(series):
            if self.data.registrationResultWasApproved(series) or \
                    (self.zFrameRegistrationSuccessful and self.seriesTypeManager.isCoverTemplate(series)):
                style = STYLE.GREEN_BACKGROUND
            elif self.data.registrationResultWasSkipped(series):
                style = STYLE.RED_BACKGROUND
            elif self.data.registrationResultWasRejected(series):
                style = STYLE.GRAY_BACKGROUND
        return style

    def isTrackingPossible(self, series):
        if self.data.completed:
            logging.debug(
                "No tracking possible. Case has been marked as completed!")
            return False
        if self.isInGeneralTrackable(
                series) and self.resultHasNotBeenProcessed(series):
            if self.seriesTypeManager.isGuidance(series):
                return self.data.getMostRecentApprovedCoverProstateRegistration(
                )
            elif self.seriesTypeManager.isCoverProstate(series):
                return self.zFrameRegistrationSuccessful
            elif self.isCoverTemplateTrackable(series):
                return True
        return False

    def isCoverTemplateTrackable(self, series):
        if not self.seriesTypeManager.isCoverTemplate(series):
            return False
        if not self.approvedCoverTemplate:
            return True
        currentSeriesNumber = RegistrationResult.getSeriesNumberFromString(
            series)
        vName = self.approvedCoverTemplate.GetName()
        approvedSeriesNumber = int(
            vName.split(":" if vName.find(":") > -1 else "-")[0])
        return currentSeriesNumber > approvedSeriesNumber

    def isInGeneralTrackable(self, series):
        seriesType = self.seriesTypeManager.getSeriesType(series)
        return self.isAnyListItemInString(seriesType, [
            self.getSetting("COVER_TEMPLATE_PATTERN"),
            self.getSetting("COVER_PROSTATE_PATTERN"),
            self.getSetting("NEEDLE_IMAGE_PATTERN")
        ])

    def resultHasNotBeenProcessed(self, series):
        return not (self.data.registrationResultWasApproved(series)
                    or self.data.registrationResultWasSkipped(series)
                    or self.data.registrationResultWasRejected(series))

    def isEligibleForSkipping(self, series):
        seriesType = self.seriesTypeManager.getSeriesType(series)
        return not self.isAnyListItemInString(seriesType, [
            self.getSetting("COVER_PROSTATE_PATTERN"),
            self.getSetting("COVER_TEMPLATE_PATTERN")
        ])

    def takeActionForCurrentSeries(self):
        event = None
        callData = None
        if self.seriesTypeManager.isCoverProstate(self.currentSeries):
            event = self.InitiateSegmentationEvent
            callData = str(False)
        elif self.seriesTypeManager.isCoverTemplate(self.currentSeries):
            event = self.InitiateZFrameCalibrationEvent
        elif self.seriesTypeManager.isGuidance(self.currentSeries):
            self.onInvokeRegistration(initial=False)
            return
        if event:
            self.invokeEvent(event, callData)
        else:
            raise UnknownSeriesError(
                "Action for currently selected series unknown")

    def retryRegistration(self):
        self.invokeEvent(self.InitiateSegmentationEvent, str(True))

    def getRegistrationResultNameAndGeneratedSuffix(self, name):
        nOccurrences = sum([
            1 for result in self.data.getResultsAsList() if name in result.name
        ])
        suffix = ""
        if nOccurrences:
            suffix = "_Retry_" + str(nOccurrences)
        return name, suffix

    def onInvokeRegistration(self,
                             initial=True,
                             retryMode=False,
                             segmentationData=None):
        self.progress = ModuleWidgetMixin.createProgressDialog(
            maximum=4,
            value=1,
            windowFlags=qt.Qt.CustomizeWindowHint | qt.Qt.WindowTitleHint)
        self.progress.setCancelButton(None)
        if initial:
            self.applyInitialRegistration(
                retryMode,
                segmentationData,
                progressCallback=self.updateProgressBar)
        else:
            self.applyRegistration(progressCallback=self.updateProgressBar)
        self.progress.close()
        self.progress = None
        logging.debug('Re-Registration is done')

    @onReturnProcessEvents
    def updateProgressBar(self, **kwargs):
        if self.progress:
            for key, value in kwargs.iteritems():
                if hasattr(self.progress, key):
                    setattr(self.progress, key, value)

    def generateNameAndCreateRegistrationResult(self, fixedVolume):
        name, suffix = self.getRegistrationResultNameAndGeneratedSuffix(
            fixedVolume.GetName())
        result = self.data.createResult(name + suffix)
        result.suffix = suffix
        self.registrationLogic.registrationResult = result
        return result

    def applyInitialRegistration(self,
                                 retryMode,
                                 segmentationData,
                                 progressCallback=None):
        if not retryMode:
            self.data.initializeRegistrationResults()

        self.runBRAINSResample(inputVolume=self.fixedLabel,
                               referenceVolume=self.fixedVolume,
                               outputVolume=self.fixedLabel)
        self._runRegistration(self.fixedVolume, self.fixedLabel,
                              self.movingVolume, self.movingLabel,
                              self.movingTargets, segmentationData,
                              progressCallback)

    def applyRegistration(self, progressCallback=None):

        coverProstateRegResult = self.data.getMostRecentApprovedCoverProstateRegistration(
        )
        lastRigidTfm = self.data.getLastApprovedRigidTransformation()
        lastApprovedTfm = self.data.getMostRecentApprovedTransform()
        initialTransform = lastApprovedTfm if lastApprovedTfm else lastRigidTfm

        fixedLabel = self.volumesLogic.CreateAndAddLabelVolume(
            slicer.mrmlScene, self.currentSeriesVolume,
            self.currentSeriesVolume.GetName() + '-label')

        self.runBRAINSResample(inputVolume=coverProstateRegResult.labels.fixed,
                               referenceVolume=self.currentSeriesVolume,
                               outputVolume=fixedLabel,
                               warpTransform=initialTransform)

        self.dilateMask(fixedLabel, dilateValue=self.segmentedLabelValue)
        self._runRegistration(self.currentSeriesVolume, fixedLabel,
                              coverProstateRegResult.volumes.fixed,
                              coverProstateRegResult.labels.fixed,
                              coverProstateRegResult.targets.approved, None,
                              progressCallback)

    def _runRegistration(self, fixedVolume, fixedLabel, movingVolume,
                         movingLabel, targets, segmentationData,
                         progressCallback):
        result = self.generateNameAndCreateRegistrationResult(fixedVolume)
        result.receivedTime = self.seriesTimeStamps[result.name.replace(
            result.suffix, "")]
        if segmentationData:
            result.segmentationData = segmentationData
        parameterNode = slicer.vtkMRMLScriptedModuleNode()
        parameterNode.SetAttribute('FixedImageNodeID', fixedVolume.GetID())
        parameterNode.SetAttribute('FixedLabelNodeID', fixedLabel.GetID())
        parameterNode.SetAttribute('MovingImageNodeID', movingVolume.GetID())
        parameterNode.SetAttribute('MovingLabelNodeID', movingLabel.GetID())
        parameterNode.SetAttribute('TargetsNodeID', targets.GetID())
        result.startTime = self.getTime()
        self.registrationLogic.run(parameterNode,
                                   progressCallback=progressCallback)
        result.endTime = self.getTime()
        self.addTargetsToMRMLScene(result)
        if self.seriesTypeManager.isCoverProstate(
                self.currentSeries) and self.temporaryIntraopTargets:
            self.addTemporaryTargetsToResult(result)
        self.invokeEvent(self.InitiateEvaluationEvent)

    def addTargetsToMRMLScene(self, result):
        targetNodes = result.targets.asDict()
        for regType in RegistrationTypeData.RegistrationTypes:
            if targetNodes[regType]:
                slicer.mrmlScene.AddNode(targetNodes[regType])

    def addTemporaryTargetsToResult(self, result):
        length = self.temporaryIntraopTargets.GetNumberOfFiducials()
        targetNodes = result.targets.asDict()
        for targetList in [
                targetNodes[r] for r in RegistrationTypeData.RegistrationTypes
                if targetNodes[r]
        ]:
            for i in range(length):
                targetList.AddFiducialFromArray(
                    self.getTargetPosition(self.temporaryIntraopTargets, i),
                    self.temporaryIntraopTargets.GetNthFiducialLabel(i))

    def onRegistrationResultStatusChanged(self, caller, event):
        self.skipAllUnregisteredPreviousSeries(self.currentResult.name)
        if self._busy:
            return
        self.save()
        self.invokeEvent(self.RegistrationStatusChangedEvent)

    @vtk.calldata_type(vtk.VTK_STRING)
    def onNewRegistrationResultCreated(self, caller, event, callData):
        self.currentResult = callData

    def skipAllUnregisteredPreviousSeries(self, series):
        selectedSeriesNumber = RegistrationResult.getSeriesNumberFromString(
            series)
        for series in [
                series for series in self.seriesList
                if not self.seriesTypeManager.isCoverTemplate(series)
        ]:
            currentSeriesNumber = RegistrationResult.getSeriesNumberFromString(
                series)
            if currentSeriesNumber < selectedSeriesNumber and self.isTrackingPossible(
                    series):
                results = self.data.getResultsBySeriesNumber(
                    currentSeriesNumber)
                if len(results) == 0:
                    self.skipSeries(series)
            elif currentSeriesNumber >= selectedSeriesNumber:
                break

    def skipSeries(self, series):
        volume = self.getOrCreateVolumeForSeries(series)
        name, suffix = self.getRegistrationResultNameAndGeneratedSuffix(
            volume.GetName())
        result = self.data.createResult(name + suffix)
        result.volumes.fixed = volume
        result.receivedTime = self.seriesTimeStamps[result.name.replace(
            result.suffix, "")]
        result.skip()

    def skip(self, series):
        self.skipAllUnregisteredPreviousSeries(series)
        self.skipSeries(series)
        self.save()

    def _getConsent(self):
        return RadioButtonChoiceMessageBox("Who gave consent?",
                                           options=["Clinician",
                                                    "Operator"]).exec_()
コード例 #10
0
 def seriesType(self):
   seriesTypeManager = SeriesTypeManager()
   return seriesTypeManager.getSeriesType(self.name)
コード例 #11
0
 def getMostRecentApprovedCoverProstateRegistration(self):
   seriesTypeManager = SeriesTypeManager()
   for result in self.registrationResults.values():
     if seriesTypeManager.isCoverProstate(result.name) and result.approved:
       return result
   return None
コード例 #12
0
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.iteritems():
        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.iteritems():
      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]
コード例 #13
0
class ProstateAblationSession(StepBasedSession):

    IncomingDataSkippedEvent = SlicerDevelopmentToolboxEvents.SkippedEvent
    IncomingIntraopDataReceiveFinishedEvent = SlicerDevelopmentToolboxEvents.FinishedEvent + 111
    NewImageSeriesReceivedEvent = SlicerDevelopmentToolboxEvents.NewImageDataReceivedEvent

    DICOMReceiverStatusChanged = SlicerDevelopmentToolboxEvents.StatusChangedEvent
    DICOMReceiverStoppedEvent = SlicerDevelopmentToolboxEvents.StoppedEvent

    ZFrameRegistrationSuccessfulEvent = vtk.vtkCommand.UserEvent + 140
    LoadingMetadataSuccessfulEvent = vtk.vtkCommand.UserEvent + 143
    SegmentationCancelledEvent = vtk.vtkCommand.UserEvent + 144

    CurrentSeriesChangedEvent = vtk.vtkCommand.UserEvent + 151

    InitiateZFrameCalibrationEvent = vtk.vtkCommand.UserEvent + 160
    InitiateTargetingEvent = vtk.vtkCommand.UserEvent + 161
    NeedleTipLocateEvent = vtk.vtkCommand.UserEvent + 162

    NeedleGuidanceEvent = vtk.vtkCommand.UserEvent + 164

    AffectedAreaDisplayChangedEvent = vtk.vtkCommand.UserEvent + 165

    SeriesTypeManuallyAssignedEvent = SeriesTypeManager.SeriesTypeManuallyAssignedEvent

    MODULE_NAME = constants.MODULE_NAME

    NEEDLE_NAME = 'NeedlePath'

    AFFECTEDAREA_NAME = "AffectedArea"

    ISSEEDTYPE = "IceSeed"
    ISRODTYPE = "IceRod"

    @property
    def intraopDICOMDirectory(self):
        return os.path.join(self.directory, "DICOM",
                            "Intraop") if self.directory else None

    @property
    def outputDirectory(self):
        # was outputDir
        return os.path.join(self.directory, "ProstateAblationOutputs")

    @property
    def approvedCoverTemplate(self):
        try:
            return self.data.zFrameRegistrationResult.volume
        except AttributeError:
            return None

    @approvedCoverTemplate.setter
    def approvedCoverTemplate(self, volume):
        self.data.zFrameRegistrationResult.volume = volume
        self.zFrameRegistrationSuccessful = volume is not None

    @property
    def zFrameRegistrationSuccessful(self):
        self._zFrameRegistrationSuccessful = getattr(
            self, "_zFrameRegistrationSuccessful", None)
        return self.data.zFrameRegistrationResult is not None and self._zFrameRegistrationSuccessful

    @zFrameRegistrationSuccessful.setter
    def zFrameRegistrationSuccessful(self, value):
        self._zFrameRegistrationSuccessful = value
        if self._zFrameRegistrationSuccessful:
            self.save()
            self.invokeEvent(self.ZFrameRegistrationSuccessfulEvent)

    @property
    def currentSeries(self):
        self._currentSeries = getattr(self, "_currentSeries", None)
        return self._currentSeries

    @currentSeries.setter
    def currentSeries(self, series):
        if series == self.currentSeries:
            return
        print "set current Series on session"
        if series and series not in self.seriesList:
            raise UnknownSeriesError("Series %s is unknown" % series)
        self._currentSeries = series
        self.invokeEvent(self.CurrentSeriesChangedEvent, series)

    @property
    def currentSeriesVolume(self):
        if not self.currentSeries:
            return None
        else:
            return self.getOrCreateVolumeForSeries(self.currentSeries)

    def __init__(self):
        StepBasedSession.__init__(self)
        self.seriesTypeManager = SeriesTypeManager()
        self.seriesTypeManager.addEventObserver(
            self.seriesTypeManager.SeriesTypeManuallyAssignedEvent,
            lambda caller, event: self.invokeEvent(
                self.SeriesTypeManuallyAssignedEvent))
        self.targetingPlugin = TargetsDefinitionPlugin(self)
        self.needlePathCaculator = ZFrameGuidanceComputation(self)
        self.segmentationEditor = slicer.qMRMLSegmentEditorWidget()
        self.resetAndInitializeMembers()
        self.resetAndInitializedTargetsAndSegments()

    def resetAndInitializeMembers(self):
        self.seriesTypeManager.clear()
        self.initializeColorNodes()
        self.directory = None
        self.data = SessionData()
        self.trainingMode = False
        self.resetIntraopDICOMReceiver()
        self.loadableList = {}
        self.seriesList = []
        self.alreadyLoadedSeries = {}
        self._currentSeries = None
        self.retryMode = False
        self.lastSelectedModelIndex = None
        self.previousStep = None

    def resetAndInitializedTargetsAndSegments(self):
        self.displayForTargets = dict()
        self.needleTypeForTargets = dict()
        self.targetingPlugin.cleanup()
        self.needleModelNode = None
        self.affectedAreaModelNode = None
        self.segmentationEditorNoneButton = None
        self.segmentationEditorShow3DButton = None
        self.segmentationEditorMaskOverWriteCombox = None
        self.segmentEditorNode = None
        self.setupNeedleAndSegModelNode()

    def initializeColorNodes(self):
        self.segmentedColorName = self.getSetting("Segmentation_Color_Name")

    def __del__(self):
        super(ProstateAblationSession, self).__del__()
        self.clearData()

    def clearData(self):
        slicer.mrmlScene.Clear(0)
        self.resetAndInitializeMembers()
        self.resetAndInitializedTargetsAndSegments()
        self.resetSteps()

    def resetSteps(self):
        for step in self.steps:
            step.cleanup()

    def onMrmlSceneCleared(self, caller, event):
        self.initializeColorNodes()

    @onExceptionReturnFalse
    def isCurrentSeriesCoverProstate(self):
        return self.seriesTypeManager.isCoverProstate(self.currentSeries)

    def isCaseDirectoryValid(self):
        return os.path.exists(self.intraopDICOMDirectory)

    def isRunning(self):
        return not self.directory in [None, '']

    def setupFiducialWidgetAndTableWidget(self):
        self.targetingPlugin.fiducialsWidget.addEventObserver(
            slicer.vtkMRMLMarkupsNode().MarkupAddedEvent,
            self.updateAffectiveZoneAndDistance)
        self.targetingPlugin.fiducialsWidget.addEventObserver(
            slicer.vtkMRMLMarkupsNode().MarkupRemovedEvent,
            self.updateAffectiveZoneAndDistance)
        self.targetingPlugin.targetTablePlugin.addEventObserver(
            self.targetingPlugin.targetTablePlugin.TargetPosUpdatedEvent,
            self.updateAffectiveZoneAndDistance)

    def processDirectory(self):
        self.newCaseCreated = getattr(self, "newCaseCreated", False)
        if self.newCaseCreated:
            return
        if not self.directory or not self.isCaseDirectoryValid():
            slicer.util.warningDisplay(
                "The selected case directory seems not to be valid",
                windowTitle="ProstateAblation")
            self.close(save=False)
        else:
            self.loadCaseData()
            self.invokeEvent(self.CaseOpenedEvent)

    def createNewCase(self, destination):
        self.newCaseCreated = True
        self.clearData()
        self.directory = destination
        self.createDirectory(self.intraopDICOMDirectory)
        self.createDirectory(self.outputDirectory)
        self.startIntraopDICOMReceiver()
        self.invokeEvent(self.IncomingDataSkippedEvent)
        self.newCaseCreated = False
        self.invokeEvent(self.NewCaseStartedEvent)

    def close(self, save=False):
        if not self.isRunning():
            return
        message = None
        if save:
            self.data.savedNeedleTypeForTargets = self.needleTypeForTargets.copy(
            )
            success, failedFileNames = self.data.close(self.outputDirectory)
            message = "Case data has been saved successfully." if success else \
              "The following data failed to saved:\n %s" % failedFileNames
        self.invokeEvent(self.CloseCaseEvent, str(message))
        self.clearData()

    def save(self):
        self.data.savedNeedleTypeForTargets = self.needleTypeForTargets.copy()
        success, failedFileNames = self.data.save(self.outputDirectory)
        return success and not len(
            failedFileNames
        ), "The following data failed to saved:\n %s" % failedFileNames

    def complete(self):
        self.data.completed = True
        self.close(save=True)

    def load(self):
        filename = os.path.join(self.outputDirectory, constants.JSON_FILENAME)
        completed = self.data.wasSessionCompleted(filename)
        if slicer.util.confirmYesNoDisplay("A %s session has been found for the selected case. Do you want to %s?" \
                                            % ("completed" if completed else "started",
                                               "open it" if completed else "continue this session")):
            slicer.app.layoutManager().blockSignals(True)
            self._loading = True
            self.data.load(filename)
            self.postProcessLoadedSessionData()
            self._loading = False
            slicer.app.layoutManager().blockSignals(False)
            self.invokeEvent(self.LoadingMetadataSuccessfulEvent)
        else:
            self.clearData()

    def postProcessLoadedSessionData(self):
        for step in self.steps:
            step.resetAndInitialize()
        if self.data.zFrameRegistrationResult:
            self.setupLoadedTransform()
        self.data.resumed = not self.data.completed
        if self.data.intraOpTargets:
            for fiducialIndex in range(
                    self.data.intraOpTargets.GetNumberOfFiducials()):
                self.displayForTargets[self.data.intraOpTargets.GetNthMarkupID(
                    fiducialIndex)] = qt.Qt.Unchecked
            self.needleTypeForTargets = self.data.savedNeedleTypeForTargets.copy(
            )
            self.targetingPlugin.targetTablePlugin.currentTargets = self.data.intraOpTargets
            self.targetingPlugin.targetTablePlugin.visible = True
            self.targetingPlugin.calculateTargetsDistance()
            self.targetingPlugin.targetDistanceWidget.visible = True
            self.setupLoadedTargets()
        self.startIntraopDICOMReceiver()

    def setupSegmentationWidget(self):
        for child in self.segmentationEditor.children():
            if child.className() == 'QGroupBox':
                if child.title == 'Effects':
                    self.segmentationEditorNoneButton = child.children()[1]
            if child.className() == 'ctkMenuButton':
                if child.text == ' Show 3D':
                    self.segmentationEditorShow3DButton = child
            if child.className() == 'ctkCollapsibleGroupBox':
                if child.title == 'Masking':
                    for grandchild in child.children():
                        if grandchild.className() == 'QComboBox':
                            if grandchild.findText('All segments') > -1 and \
                               grandchild.findText('Visible segments') > -1 and \
                               grandchild.findText('None') > -1:
                                self.segmentationEditorMaskOverWriteCombox = grandchild

    def clearOldNodesByName(self, name):
        collection = slicer.mrmlScene.GetNodesByName(name)
        for index in range(collection.GetNumberOfItems()):
            slicer.mrmlScene.RemoveNode(collection.GetItemAsObject(index))

    def setupNeedleAndSegModelNode(self):
        self.clearOldNodesByName(self.NEEDLE_NAME)
        self.setupFiducialWidgetAndTableWidget()
        self.setupSegmentationWidget()
        if self.needleModelNode is None:
            self.needleModelNode = ModuleLogicMixin.createModelNode(
                self.NEEDLE_NAME)
        if (self.needleModelNode.GetScene() is None) or (
                not self.needleModelNode.GetScene() == slicer.mrmlScene):
            slicer.mrmlScene.AddNode(self.needleModelNode)
        if self.needleModelNode.GetDisplayNode() is None:
            ModuleLogicMixin.createAndObserveDisplayNode(
                self.needleModelNode,
                displayNodeClass=slicer.vtkMRMLModelDisplayNode)
            self.needleModelNode.GetDisplayNode().SetColor(1.0, 0.0, 0.0)

        if self.affectedAreaModelNode is None:
            self.affectedAreaModelNode = ModuleLogicMixin.createModelNode(
                self.AFFECTEDAREA_NAME)
        if (self.affectedAreaModelNode.GetScene() is None) or (
                not self.affectedAreaModelNode.GetScene() == slicer.mrmlScene):
            slicer.mrmlScene.AddNode(self.affectedAreaModelNode)
        if self.affectedAreaModelNode.GetDisplayNode() is None:
            ModuleLogicMixin.createAndObserveDisplayNode(
                self.affectedAreaModelNode,
                displayNodeClass=slicer.vtkMRMLModelDisplayNode)
            self.affectedAreaModelNode.GetDisplayNode().SetOpacity(0.5)
            self.affectedAreaModelNode.GetDisplayNode().SetColor(0.0, 1.0, 0.0)

        if self.data.segmentModelNode is None:
            # Create segmentation
            self.data.segmentModelNode = slicer.vtkMRMLSegmentationNode()
            slicer.mrmlScene.AddNode(self.data.segmentModelNode)
            self.data.segmentModelNode.CreateDefaultDisplayNodes(
            )  # only needed for display
            self.data.segmentModelNode.CreateDefaultStorageNode()
            self.data.segmentModelNode.SetName("IntraOpSegmentation")
        if (self.data.segmentModelNode.GetScene() is None) or (
                not self.data.segmentModelNode.GetScene() == slicer.mrmlScene):
            slicer.mrmlScene.AddNode(self.data.segmentModelNode)
        if self.data.segmentModelNode.GetDisplayNode() is None:
            ModuleLogicMixin.createAndObserveDisplayNode(
                self.data.segmentModelNode,
                displayNodeClass=slicer.vtkMRMLSegmentationDisplayNode)
        if self.segmentEditorNode is None:
            self.segmentEditorNode = slicer.vtkMRMLSegmentEditorNode()
            slicer.mrmlScene.AddNode(self.segmentEditorNode)
        if (self.segmentEditorNode.GetScene() is None) or (
                not self.segmentEditorNode.GetScene() == slicer.mrmlScene):
            slicer.mrmlScene.AddNode(self.segmentEditorNode)
        self.segmentationEditor.setMRMLScene(slicer.mrmlScene)
        self.segmentationEditor.setMRMLSegmentEditorNode(
            self.segmentEditorNode)
        self.segmentationEditorMaskOverWriteCombox.setCurrentIndex(
            self.segmentationEditorMaskOverWriteCombox.findText('None'))

    def updateAffectiveZoneAndDistance(self, caller=None, event=None):
        self.updateAffectiveZone()
        self.targetingPlugin.calculateTargetsDistance()

    def onShowAffectiveZoneToggled(self, checked):
        targetingNode = self.targetingPlugin.targetTablePlugin.currentTargets
        if targetingNode is not None:
            for targetIndex in range(targetingNode.GetNumberOfFiducials()):
                checkboxStatus = qt.Qt.Checked if checked else qt.Qt.Unchecked
                self.displayForTargets[targetingNode.GetNthMarkupID(
                    targetIndex)] = checkboxStatus
                if self.targetingPlugin.targetTablePlugin.checkBoxList.get(
                        targetingNode.GetNthMarkupID(targetIndex)):
                    self.targetingPlugin.targetTablePlugin.checkBoxList[
                        targetingNode.GetNthMarkupID(targetIndex)].setChecked(
                            checkboxStatus)
            self.updateAffectiveZone()
            if not self.segmentationEditorShow3DButton.isChecked() == checked:
                self.segmentationEditorShow3DButton.checked = checked
            if self.data.segmentModelNode:
                if not self.data.segmentModelNode.GetDisplayNode(
                ).GetVisibility() == checked:
                    self.data.segmentModelNode.GetDisplayNode().SetVisibility(
                        checked)

    def updateAffectiveZone(self, caller=None, event=None):
        targetingNode = self.targetingPlugin.targetTablePlugin.currentTargets
        if self.targetingPlugin.fiducialsWidget.visible:
            targetingNode = self.targetingPlugin.fiducialsWidget.currentNode
        if self.needleModelNode and self.affectedAreaModelNode and self.approvedCoverTemplate and targetingNode.GetNumberOfFiducials(
        ):
            needleModelAppend = vtk.vtkAppendPolyData()
            affectedBallAreaAppend = vtk.vtkAppendPolyData()
            zFrameTransformMatrix = self.data.zFrameRegistrationResult.transform.GetMatrixTransformToParent(
            )
            # The offset and ellipsoid parameters are taken from the following source code
            # http://viewvc.slicer.org/viewvc.cgi/NAMICSandBox/trunk/IGTLoadableModules/ProstateNav/TransPerinealProstateCryoTemplate/vtkMRMLTransPerinealProstateCryoTemplateNode.cxx?revision=8043&view=markup
            offsetFromTip = 5.0  #unit mm
            coneHeight = 5.0
            for targetIndex in range(targetingNode.GetNumberOfFiducials()):
                if self.displayForTargets.get(
                        targetingNode.GetNthMarkupID(
                            targetIndex)) == qt.Qt.Checked:
                    affectedBallAreaRadius = self.GetIceBallRadius(
                        self.needleTypeForTargets.get(
                            targetingNode.GetNthMarkupID(
                                targetIndex)))  # unit mm
                    targetPosition = [0.0, 0.0, 0.0]
                    targetingNode.GetNthFiducialPosition(
                        targetIndex, targetPosition)
                    (start, end, indexX, indexY, depth,
                     inRange) = self.needlePathCaculator.computeNearestPath(
                         targetPosition)
                    needleDirection = (numpy.array(end) -
                                       numpy.array(start)) / numpy.linalg.norm(
                                           numpy.array(end) -
                                           numpy.array(start))
                    cone = vtk.vtkConeSource()
                    cone.SetRadius(1.5)
                    cone.SetResolution(6)
                    cone.SetHeight(coneHeight)
                    cone.CappingOff()
                    cone.Update()
                    transform = vtk.vtkTransform()
                    transform.RotateY(-90)
                    transform.RotateX(30)
                    transform.Translate(-coneHeight / 2, 0.0, 0.0)
                    tFilter0 = vtk.vtkTransformPolyDataFilter()
                    tFilter0.SetInputData(cone.GetOutput())
                    tFilter0.SetTransform(transform)
                    tFilter0.Update()
                    translatePart = start + depth * needleDirection
                    for index, posElement in enumerate(translatePart):
                        zFrameTransformMatrix.SetElement(index, 3, posElement)
                    transform.SetMatrix(zFrameTransformMatrix)
                    tFilter1 = vtk.vtkTransformPolyDataFilter()
                    tFilter1.SetTransform(transform)
                    tFilter1.SetInputData(tFilter0.GetOutput())
                    tFilter1.Update()
                    needleModelAppend.AddInputData(tFilter1.GetOutput())
                    needleModelAppend.Update()
                    pathTubeFilter = ModuleLogicMixin.createVTKTubeFilter(
                        start,
                        start + (depth - coneHeight) * needleDirection,
                        radius=1.5,
                        numSides=6)
                    needleModelAppend.AddInputData(pathTubeFilter.GetOutput())
                    needleModelAppend.Update()
                    #End of needle model
                    #--------------
                    #--------------
                    #Begin of affectedBallArea
                    affectedBallArea = vtk.vtkParametricEllipsoid()
                    affectedBallArea.SetXRadius(
                        float(affectedBallAreaRadius[0]))
                    affectedBallArea.SetYRadius(
                        float(affectedBallAreaRadius[1]))
                    affectedBallArea.SetZRadius(
                        float(affectedBallAreaRadius[2]))
                    affectedBallAreaSource = vtk.vtkParametricFunctionSource()
                    affectedBallAreaSource.SetParametricFunction(
                        affectedBallArea)
                    affectedBallAreaSource.SetScalarModeToV()
                    affectedBallAreaSource.Update()
                    translatePart = start + (depth + offsetFromTip - float(
                        affectedBallAreaRadius[2])) * needleDirection
                    for index, posElement in enumerate(translatePart):
                        zFrameTransformMatrix.SetElement(index, 3, posElement)
                    transform.SetMatrix(zFrameTransformMatrix)
                    tFilter2 = vtk.vtkTransformPolyDataFilter()
                    tFilter2.SetTransform(transform)
                    tFilter2.SetInputData(affectedBallAreaSource.GetOutput())
                    tFilter2.Update()
                    affectedBallAreaAppend.AddInputData(tFilter2.GetOutput())
                    affectedBallAreaAppend.Update()

            self.needleModelNode.SetAndObservePolyData(
                needleModelAppend.GetOutput())
            self.affectedAreaModelNode.SetAndObservePolyData(
                affectedBallAreaAppend.GetOutput())
            ModuleLogicMixin.setNodeVisibility(self.needleModelNode, True)
            ModuleLogicMixin.setNodeVisibility(self.affectedAreaModelNode,
                                               True)
            ModuleLogicMixin.setNodeSliceIntersectionVisibility(
                self.needleModelNode, True)
            ModuleLogicMixin.setNodeSliceIntersectionVisibility(
                self.affectedAreaModelNode, True)
        pass

    def setupLoadedTransform(self):
        self._zFrameRegistrationSuccessful = True
        self.steps[1].applyZFrameTransform()

    def setupLoadedTargets(self):
        if self.data.intraOpTargets:
            targets = self.data.intraOpTargets
            ModuleWidgetMixin.setFiducialNodeVisibility(targets, show=True)
            self.applyDefaultTargetDisplayNode(targets)
            self.markupsLogic.JumpSlicesToNthPointInMarkup(targets.GetID(), 0)

    def startIntraopDICOMReceiver(self):
        logging.info("Starting DICOM Receiver for intra-procedural data")
        if not self.data.completed:
            self.resetIntraopDICOMReceiver()
            self.intraopDICOMReceiver = SmartDICOMReceiver(
                self.intraopDICOMDirectory)
            self._observeIntraopDICOMReceiverEvents()
            self.intraopDICOMReceiver.start(not (
                self.trainingMode or self.data.completed))
        else:
            self.invokeEvent(SlicerDevelopmentToolboxEvents.StoppedEvent)
        self.importDICOMSeries(self.getFileList(self.intraopDICOMDirectory))
        if self.intraopDICOMReceiver:
            self.intraopDICOMReceiver.forceStatusChangeEventUpdate()

    def resetIntraopDICOMReceiver(self):
        self.intraopDICOMReceiver = getattr(self, "intraopDICOMReceiver", None)
        if self.intraopDICOMReceiver:
            self.intraopDICOMReceiver.stop()
            self.intraopDICOMReceiver.removeEventObservers()

    def _observeIntraopDICOMReceiverEvents(self):
        self.intraopDICOMReceiver.addEventObserver(
            self.intraopDICOMReceiver.IncomingDataReceiveFinishedEvent,
            self.onDICOMSeriesReceived)
        self.intraopDICOMReceiver.addEventObserver(
            SlicerDevelopmentToolboxEvents.StatusChangedEvent,
            self.onDICOMReceiverStatusChanged)

    @vtk.calldata_type(vtk.VTK_STRING)
    def onDICOMReceiverStatusChanged(self, caller, event, callData):
        customStatusProgressBar = CustomStatusProgressbar()
        customStatusProgressBar.text = callData
        if "Waiting" in callData:
            customStatusProgressBar.busy = True

    @vtk.calldata_type(vtk.VTK_STRING)
    def onDICOMSeriesReceived(self, caller, event, callData):
        self.importDICOMSeries(ast.literal_eval(callData))
        if self.trainingMode is True:
            self.resetIntraopDICOMReceiver()

    def importDICOMSeries(self, newFileList):
        indexer = ctk.ctkDICOMIndexer()

        newSeries = []
        for currentIndex, currentFile in enumerate(newFileList, start=1):
            self.invokeEvent(
                SlicerDevelopmentToolboxEvents.NewFileIndexedEvent, [
                    "Indexing file %s" % currentFile,
                    len(newFileList), currentIndex
                ].__str__())
            slicer.app.processEvents()
            currentFile = os.path.join(self.intraopDICOMDirectory, currentFile)
            indexer.addFile(slicer.dicomDatabase, currentFile, None)
            series = self.makeSeriesNumberDescription(currentFile)
            if series not in self.seriesList:
                if not series.split(": ")[0] == '__TAG_NOT_IN_INSTANCE__':
                    self.seriesList.append(series)
                    newSeries.append(series)
                    self.loadableList[
                        series] = self.createLoadableFileListForSeries(series)
        self.seriesList = sorted(self.seriesList,
                                 key=lambda s: int(s.split(": ")[0]))

        if len(newFileList):
            self.verifyPatientIDEquality(newFileList)
            self.invokeEvent(self.NewImageSeriesReceivedEvent,
                             newSeries.__str__())

    def verifyPatientIDEquality(self, receivedFiles):
        seriesNumberPatientID = self.getAdditionalInformationForReceivedSeries(
            receivedFiles)
        dicomFileName = self.getPatientIDValidationSource()
        if not dicomFileName:
            return
        currentInfo = self.getPatientInformation(dicomFileName)
        currentID = currentInfo["PatientID"]
        patientName = currentInfo["PatientName"]
        for seriesNumber, receivedInfo in seriesNumberPatientID.iteritems():
            patientID = receivedInfo["PatientID"]
            if patientID is not None and patientID != currentID:
                m = 'WARNING:\n' \
                    'Current case:\n' \
                    '  Patient ID: {0}\n' \
                    '  Patient Name: {1}\n' \
                    'Received image\n' \
                    '  Patient ID: {2}\n' \
                    '  Patient Name : {3}\n\n' \
                    'Do you want to keep this series? '.format(currentID, patientName, patientID, receivedInfo["PatientName"])
                if not slicer.util.confirmYesNoDisplay(
                        m,
                        title="Patient IDs Not Matching",
                        windowTitle="ProstateAblation"):
                    self.deleteSeriesFromSeriesList(seriesNumber)

    def getPatientIDValidationSource(self):
        # TODO: For loading case purposes it would be nice to keep track which series were accepted
        if len(self.loadableList.keys()) > 1:
            keylist = self.loadableList.keys()
            keylist.sort(key=lambda x: int(x.split(": ")[0]))
            return self.loadableList[keylist[0]][0]
        else:
            return None

    def getOrCreateVolumeForSeries(self, series):
        try:
            volume = self.alreadyLoadedSeries[series]
        except KeyError:
            logging.info("Need to load volume")
            files = self.loadableList[series]
            loadables = self.scalarVolumePlugin.examine([files])
            success, volume = slicer.util.loadVolume(files[0], returnNode=True)
            volume.SetName(loadables[0].name)
            self.alreadyLoadedSeries[series] = volume
        slicer.app.processEvents()
        return volume

    def createLoadableFileListForSeries(self, series):
        seriesNumber = int(series.split(": ")[0])
        loadableList = []
        for dcm in self.getFileList(self.intraopDICOMDirectory):
            currentFile = os.path.join(self.intraopDICOMDirectory, dcm)
            if self.getDICOMValue(
                    currentFile,
                    DICOMTAGS.SERIES_NUMBER) and (not self.getDICOMValue(
                        currentFile,
                        DICOMTAGS.SERIES_NUMBER) == '__TAG_NOT_IN_INSTANCE__'):
                try:
                    int(
                        self.getDICOMValue(currentFile,
                                           DICOMTAGS.SERIES_NUMBER))
                except:
                    print "nothing: ", self.getDICOMValue(
                        currentFile, DICOMTAGS.SERIES_NUMBER)
                currentSeriesNumber = int(
                    self.getDICOMValue(currentFile, DICOMTAGS.SERIES_NUMBER))
                if currentSeriesNumber and currentSeriesNumber == seriesNumber:
                    loadableList.append(currentFile)
        return loadableList

    def deleteSeriesFromSeriesList(self, seriesNumber):
        for series in self.seriesList:
            currentSeriesNumber = int(series.split(": ")[0])
            if currentSeriesNumber == seriesNumber:
                self.seriesList.remove(series)
                for seriesFile in self.loadableList[series]:
                    logging.debug(
                        "removing {} from filesystem".format(seriesFile))
                    os.remove(seriesFile)
                del self.loadableList[series]

    def makeSeriesNumberDescription(self, dcmFile):
        seriesDescription = self.getDICOMValue(dcmFile,
                                               DICOMTAGS.SERIES_DESCRIPTION)
        seriesNumber = self.getDICOMValue(dcmFile, DICOMTAGS.SERIES_NUMBER)
        if not (seriesNumber and seriesDescription):
            raise DICOMValueError(
                "Missing Attribute(s):\nFile: {}\nseriesNumber: {}\nseriesDescription: {}"
                .format(dcmFile, seriesNumber, seriesDescription))
        return "{}: {}".format(seriesNumber, seriesDescription)

    def getAdditionalInformationForReceivedSeries(self, fileList):
        seriesNumberPatientID = {}
        for currentFile in [
                os.path.join(self.intraopDICOMDirectory, f) for f in fileList
        ]:
            seriesNumber = int(
                self.getDICOMValue(currentFile, DICOMTAGS.SERIES_NUMBER))
            if seriesNumber not in seriesNumberPatientID.keys():
                seriesNumberPatientID[
                    seriesNumber] = self.getPatientInformation(currentFile)
        return seriesNumberPatientID

    def getPatientInformation(self, currentFile):
        return {
            "PatientID":
            self.getDICOMValue(currentFile, DICOMTAGS.PATIENT_ID),
            "PatientName":
            self.getDICOMValue(currentFile, DICOMTAGS.PATIENT_NAME),
            "SeriesDescription":
            self.getDICOMValue(currentFile, DICOMTAGS.SERIES_DESCRIPTION)
        }

    def getSeriesForSubstring(self, substring):
        for series in reversed(self.seriesList):
            if substring in series:
                return series
        return None

    def loadCaseData(self):
        if not os.path.exists(
                os.path.join(self.outputDirectory, constants.JSON_FILENAME)):
            if len(os.listdir(self.intraopDICOMDirectory)):
                self.startIntraopDICOMReceiver()
        else:
            self.openSavedSession()

    def openSavedSession(self):
        self.load()

    def applyDefaultTargetDisplayNode(self, targetNode, new=False):
        displayNode = None if new else targetNode.GetDisplayNode()
        modifiedDisplayNode = self.setupDisplayNode(displayNode, True)
        targetNode.SetAndObserveDisplayNodeID(modifiedDisplayNode.GetID())

    def setupDisplayNode(self, displayNode=None, starBurst=False):
        if not displayNode:
            displayNode = slicer.vtkMRMLMarkupsDisplayNode()
            slicer.mrmlScene.AddNode(displayNode)
        displayNode.SetTextScale(0)
        displayNode.SetGlyphScale(2.5)
        if starBurst:
            displayNode.SetGlyphType(
                slicer.vtkMRMLAnnotationPointDisplayNode.StarBurst2D)
        return displayNode

    def loadProcessedData(self, directory):
        resourcesDir = os.path.join(directory, 'RESOURCES')
        logging.debug(resourcesDir)
        if not os.path.exists(resourcesDir):
            message = "The selected directory does not fit the directory structure. Make sure that you select the " \
                      "study root directory which includes directories RESOURCES"
            return message
        seriesMap = []
        self.loadImageAndLabel(seriesMap)
        if self.segmentationPath is None:
            message = "No segmentations found.\nMake sure that you used segment editor for segmenting the prostate first and using " \
                      "its output as the data input here."
            return message
        return None

    def loadImageAndLabel(self, seriesMap):
        self.imagePath = None
        self.segmentationPath = None
        segmentedColorName = self.getSetting("Segmentation_Color_Name")

        for series in seriesMap:
            seriesName = str(seriesMap[series]['LongName'])
            logging.debug('series Number ' + series + ' ' + seriesName)

            imagePath = os.path.join(seriesMap[series]['NRRDLocation'])
            segmentationPath = os.path.dirname(os.path.dirname(imagePath))
            segmentationPath = os.path.join(segmentationPath, 'Segmentations')

            if not os.path.exists(segmentationPath):
                continue
            else:
                if any(segmentedColorName in name
                       for name in os.listdir(segmentationPath)):
                    logging.debug(' FOUND THE SERIES OF INTEREST, ITS ' +
                                  seriesName)
                    logging.debug(' LOCATION OF VOLUME : ' +
                                  str(seriesMap[series]['NRRDLocation']))
                    logging.debug(' LOCATION OF IMAGE path : ' +
                                  str(imagePath))

                    logging.debug(' LOCATION OF SEGMENTATION path : ' +
                                  segmentationPath)

                    self.imagePath = seriesMap[series]['NRRDLocation']
                    self.segmentationPath = segmentationPath
                    break

    def isTrackingPossible(self, series):
        if self.data.completed:
            logging.debug(
                "No tracking possible. Case has been marked as completed!")
            return False
        else:
            return True

    def isEligibleForDistanceMeasure(self, series):
        seriesType = self.seriesTypeManager.getSeriesType(series)
        listItems = [
            str(item) for item in self.getSetting("COVER_PROSTATE") +
            self.getSetting("COVER_TEMPLATE") + self.getSetting("VIBE_IMAGE")
        ]
        return self.isAnyListItemInString(seriesType, listItems)

    def isLoading(self):
        self._loading = getattr(self, "_loading", False)
        return self._loading

    def GetIceBallRadius(self, type):
        if type == self.ISRODTYPE:
            return numpy.array(self.getSetting("NeedleRadius_ICEROD").split())
        elif type == self.ISSEEDTYPE:
            return numpy.array(self.getSetting("NeedleRadius_ICESEED").split())
        else:
            needleRadius = numpy.array([0, 0, 0])
            return needleRadius

    def takeActionForCurrentSeries(self, event=None):
        callData = None
        if self.seriesTypeManager.isCoverTemplate(self.currentSeries):
            event = self.InitiateZFrameCalibrationEvent
        else:
            if self.zFrameRegistrationSuccessful:
                if self.seriesTypeManager.isCoverProstate(self.currentSeries):
                    event = self.InitiateTargetingEvent
                elif self.seriesTypeManager.isGuidance(self.currentSeries):
                    event = self.NeedleGuidanceEvent
                else:
                    event = self.NeedleGuidanceEvent
            else:
                slicer.util.warningDisplay(
                    "ZFrame registration was not performed yet, it is required!"
                )
        if event:
            self.invokeEvent(event, callData)
        else:
            raise UnknownSeriesError(
                "Action for currently selected series unknown")

    @onReturnProcessEvents
    def updateProgressBar(self, **kwargs):
        if self.progress:
            for key, value in kwargs.iteritems():
                if hasattr(self.progress, key):
                    setattr(self.progress, key, value)