def onReload(self): EditUtil.EditUtil().setCurrentEffect("DefaultTool") import Isobrush # TODO: this causes a flash of the new widget, but not a big deal since it's only devel mode w = Isobrush.IsobrushWidget() w.onReload() del (w.parent) EditUtil.EditUtil().setCurrentEffect("Isobrush")
def updateSliders(self): r = self.structuresView.currentIndex().row() if (r > -1): ei = slicer.modules.SlicerPathologyWidget.editorWidget.helper.structures.item( r, 3).text() else: ei = EditUtil.EditUtil().getParameterNode().GetParameter( 'SlicerPathology,tilename') + '-label' if ei not in params: params[ei] = cparams.copy() if (r < 0): params[ei][ 'label'] = slicer.modules.SlicerPathologyWidget.editorWidget.helper.editUtil.getLabelName( ) else: params[ei][ 'label'] = slicer.modules.SlicerPathologyWidget.editorWidget.helper.structures.item( r, 2).text() jstr = json.dumps(params, sort_keys=True, indent=4, separators=(',', ': ')) self.parameterNode.SetParameter("QuickTCGAEffect,erich", jstr) self.frameOtsuSlider.value = params[ei]["otsuRatio"] self.frameCurvatureWeightSlider.value = params[ei]["curvatureWeight"] self.frameSizeThldSlider.value = params[ei]["sizeThld"] self.frameSizeUpperThldSlider.value = params[ei]["sizeUpperThld"] self.frameMPPSlider.value = params[ei]["mpp"]
def setDefaultParams(self): """Configure here all the required params for any LabelEffect (Paint, Draw, ect.)""" self.editUtil = EditUtil.EditUtil() self.parameterNode = self.editUtil.getParameterNode() self.parameterNode.SetParameter("LabelEffect,paintOver", "1") # Enable paintOver self.parameterNode.SetParameter("LabelEffect,paintThreshold", "0") # Enable Threshold checkbox
def updateParameterNode(self, caller, event): node = EditUtil.EditUtil().getParameterNode() if node != self.parameterNode: if self.parameterNode: node.RemoveObserver(self.parameterNodeTag) self.parameterNode = node self.parameterNodeTag = node.AddObserver("ModifiedEvent", self.updateGUIFromMRML)
def updateParameterNode(self, caller, event): node = EditUtil.getParameterNode() if node != self.parameterNode: if self.parameterNode: node.RemoveObserver(self.parameterNodeTag) self.parameterNode = node self.parameterNodeTag = node.AddObserver(vtk.vtkCommand.ModifiedEvent, self.updateGUIFromMRML)
def updateParam(self, p, v): r = self.structuresView.currentIndex().row() if (r > -1): ei = slicer.modules.SlicerPathologyWidget.editorWidget.helper.structures.item( r, 3).text() else: ei = EditUtil.EditUtil().getParameterNode().GetParameter( 'SlicerPathology,tilename') + '-label' if ei not in params: params[ei] = cparams.copy() if (r < 0): params[ei][ 'label'] = slicer.modules.SlicerPathologyWidget.editorWidget.helper.editUtil.getLabelName( ) else: params[ei][ 'label'] = slicer.modules.SlicerPathologyWidget.editorWidget.helper.structures.item( r, 2).text() params[ei][p] = v cparams[p] = v jstr = json.dumps(params, sort_keys=True, indent=4, separators=(',', ': ')) self.parameterNode.SetParameter("QuickTCGAEffect,erich", jstr)
def step(self, iterations=1): if self.pendingEvent: if self.pendingEvent.command_execution_status == 0: # TODO: is there an attribute for CL_COMPLETE? # TODO: also catch error codes here # # put data back in node # self.steeredArray[:] = self.labelNext_dev.get() self.steeredNode.GetImageData().Modified() self.steeredNode.Modified() else: return # # get the parameters from MRML # # TODO: handl any real growcut parameters node = EditUtil.EditUtil().getParameterNode() # # initialize candidates if needed # if not self.candidatesInitialized: self.clProgram.clearShort( self.clQueue, self.shape, None, self.candidatesNext_dev.data).wait() print("self.candidatesNext_dev mean ", self.candidatesNext_dev.get().mean()) self.clProgram.initialCandidates( self.clQueue, self.shape, None, self.labelArray_dev.data, self.candidates_dev.data).wait() print("self.labelArray_dev mean ", self.labelArray_dev.get().mean()) print("self.candidates_dev mean ", self.candidates_dev.get().mean()) self.candidatesInitialized = True # # run iterations # - save the pending event from the last call so that we can # know when the execution is finished (so the main event loop # is not blocked on this execution) # for iteration in xrange(iterations): self.clProgram.growCut(self.clQueue, self.work_size, None, self.backgroundArray_dev.data, self.labelArray_dev.data, self.theta_dev.data, self.thetaNext_dev.data, self.labelNext_dev.data, self.candidates_dev.data, self.candidatesNext_dev.data, self.backgroundArrayMax, global_offset=self.work_offset) self.clProgram.copyShort(self.clQueue, self.work_size, None, self.labelNext_dev.data, self.labelArray_dev.data, global_offset=self.work_offset) self.clProgram.copyShort(self.clQueue, self.work_size, None, self.candidatesNext_dev.data, self.candidates_dev.data, global_offset=self.work_offset) self.clProgram.clearShort(self.clQueue, self.work_size, None, self.candidatesNext_dev.data, global_offset=self.work_offset) self.pendingEvent = self.clProgram.copyFloat(self.clQueue, self.work_size, None, self.thetaNext_dev.data, self.theta_dev.data, global_offset=self.work_offset)
def apply(self, xy, mode=0, forced_path=None, forced_point=None): # # get the parameters from MRML # # For sanity purposes, tool can always "paint" over existing labels. If we find some foreseeable reason why we might # not want this in all cases, we can re-add to the GUI. # # get the label and background volume nodes # labelLogic = self.sliceLogic.GetLabelLayer() labelNode = labelLogic.GetVolumeNode() backgroundLogic = self.sliceLogic.GetBackgroundLayer() backgroundNode = backgroundLogic.GetVolumeNode() ##### self.errorMessageFrame.textCursor().insertHtml('Error Detected!') ##### self.errorMessageFrame.setStyleSheet("QTextEdit {color:red}") # # get the ijk location of the clicked point # by projecting through patient space back into index # space of the volume. Result is sub-pixel, so round it # (note: bg and lb will be the same for volumes created # by the editor, but can be different if the use selected # different bg nodes, but that is not handled here). # xyToIJK = labelLogic.GetXYToIJKTransform() ijkFloat = xyToIJK.TransformDoublePoint(xy + (0, )) ijk = [] for element in ijkFloat: try: intElement = int(round(element)) except ValueError: intElement = 0 ijk.append(intElement) ijk.reverse() ijk = tuple(ijk) ### IJK ACTIVE HERE # # Get the numpy array for the bg and label # node = EditUtil.EditUtil().getParameterNode() offset = float(node.GetParameter("TraceAndSelect,offsetvalue")) if offset != 0 and mode == 0: self.progress = qt.QProgressDialog() self.progress.setLabelText("Processing Slices...") self.progress.setCancelButtonText("Abort Fill") self.progress.setMinimum(0) self.progress.setMaximum(abs(offset)) self.progress.setAutoClose(1) self.progress.open() return self.fill(ijk, [], mode, forced_path, forced_point)
def __init__(self,options,preferredDeviceType='GPU'): self.preferredDeviceType = preferredDeviceType self.editUtil = EditUtil.EditUtil() self.sliceWidget = options.tools[0].sliceWidget if hasattr(slicer.modules, 'editorBot'): slicer.modules.editorBot.active = False del(slicer.modules.editorBot) slicer.modules.editorBot = self self.interval = 100 self.active = False self.start()
def __init__(self, options): self.editUtil = EditUtil.EditUtil() #self.sliceWidget = options.tools[0].sliceWidget self.sliceWidget = slicer.app.layoutManager().sliceWidget('Red') if hasattr(slicer.modules, 'TCGAEditorBot'): slicer.modules.TCGAEditorBot.active = False del (slicer.modules.TCGAEditorBot) slicer.modules.TCGAEditorBot = self self.redSliceWidget = options.redSliceWidget self.greenSliceWidget = options.greenSliceWidget self.yellowSliceWidget = options.yellowSliceWidget self.start()
def onloadCTButtonClicked(self): # widge to load file w = qt.QWidget() w.resize(320, 240) w.setWindowTitle("Select CT Image") image_path = qt.QFileDialog.getOpenFileName(w, 'Open File', '/') path = str('/'.join(image_path.split('/')[0:-1])) + '/' print(path) print(image_path) if image_path is not None: self.image_loaded = True print(image_path) filetype = ".nrrd" self.filetype = filetype lm = slicer.app.layoutManager() lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView) view = lm.threeDWidget(0).threeDView() viewNode = view.mrmlViewNode() viewNode.SetBackgroundColor((0, 0, 0)) viewNode.SetBackgroundColor2((0, 0, 0)) viewNode.SetAxisLabelsVisible(0) viewNode.SetBoxVisible(0) # load image (master volume) load, image = slicer.util.loadVolume(image_path, returnNode=True) volumesLogic = slicer.modules.volumes.logic() label_name = image.GetName() + "-rough-label" label = volumesLogic.CreateLabelVolume(slicer.mrmlScene, image, label_name) label_path = str(path + label_name + filetype) print(label_path) self.view = view self.path = path self.image_path = image_path self.label_path = label_path self.image_name = image.GetName() self.label_name = label_name self.labelpostfix1 = '_tumor_label' self.labelpostfix2 = '_lung_label' selectionNode = slicer.app.applicationLogic().GetSelectionNode() selectionNode.SetReferenceActiveLabelVolumeID(label.GetID()) EditUtil.EditUtil().propagateVolumeSelection() # switch to Edit module slicer.util.selectModule('Editor') slicer.modules.EditorWidget.toolsBox.selectEffect('PaintEffect')
def __init__(self, sliceLogic): print("Preparing Growcut interaction") self.attributes = ('MouseTool') self.displayName = 'FastGrowCut Effect' #disconnect all shortcuts that may exist, to allow Fast GrowCut's to work, reconnect once bot is turned off slicer.modules.EditorWidget.removeShortcutKeys() self.sliceLogic = sliceLogic self.editUtil = EditUtil.EditUtil() #initialize Fast GrowCut self.init_fGrowCut() self.fastCrowCutCreated = False
def __init__(self, sliceLogic): print("Preparing CleverSeg interaction") self.attributes = ('MouseTool') self.displayName = 'CleverSeg Effect' # disconnect all shortcuts that may exist, to allow CleverSeg's to work, reconnect once bot is turned off slicer.modules.EditorWidget.removeShortcutKeys() self.sliceLogic = sliceLogic self.editUtil = EditUtil.EditUtil() # initialize CleverSeg self.init_fCleverSeg() self.CleverSegCreated = False
def onloadMRIFLAIRButtonClicked(self): # widge to load file w = qt.QWidget() w.resize(320, 240) w.setWindowTitle("Select MRI FLAIR Image") self.FLAIR_image_path = qt.QFileDialog.getOpenFileName( w, 'Open File', '/') self.FLAIR_path = str('/'.join( self.FLAIR_image_path.split('/')[0:-1])) + '/' lm = slicer.app.layoutManager() lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView) view = lm.threeDWidget(0).threeDView() viewNode = view.mrmlViewNode() viewNode.SetBackgroundColor((0, 0, 0)) viewNode.SetBackgroundColor2((0, 0, 0)) viewNode.SetAxisLabelsVisible(0) viewNode.SetBoxVisible(0) self.view = view load, image = slicer.util.loadVolume(self.FLAIR_image_path, returnNode=True) # adding a automated seed point volumesLogic = slicer.modules.volumes.logic() label_name = image.GetName() + "-rough-label" label = volumesLogic.CreateLabelVolume(slicer.mrmlScene, image, label_name) # label_path = str(path+label_name+filetype) # print(label_path) # # self.view = view # self.path = path # self.image_path = image_path # self.label_path = label_path # self.image_name = image.GetName() self.label_name = label_name # self.labelpostfix1 = '_tumor_confidenceconnected' # self.labelpostfix2 = '_lung_confidenceconnected' selectionNode = slicer.app.applicationLogic().GetSelectionNode() selectionNode.SetReferenceActiveLabelVolumeID(label.GetID()) EditUtil.EditUtil().propagateVolumeSelection() # switch to Edit module slicer.util.selectModule('Editor') slicer.modules.EditorWidget.toolsBox.selectEffect('PaintEffect')
def run_qtcga_segmentation_1(self): """ Original recipe :return: """ if self.bEditTCGA: self.currentMessage = "Quick TCGA: running nucleus segmentation ..." slicer.util.showStatusMessage(self.currentMessage) seedArray = slicer.util.array(self.labelNode.GetName()) self.qTCGASeedArray[:] = seedArray[:] node = EditUtil.EditUtil().getParameterNode() # get the parameters from MRML otsuRatio = float(node.GetParameter("ShortCut,otsuRatio")) print(otsuRatio) curvatureWeight = float(node.GetParameter("ShortCut,curvatureWeight")) / 10 print(curvatureWeight) sizeThld = float(node.GetParameter("ShortCut,sizeThld")) print(sizeThld) sizeUpperThld = float(node.GetParameter("ShortCut,sizeUpperThld")) print(sizeUpperThld) mpp = float(node.GetParameter("ShortCut,mpp")) / 100 print(mpp) self.qTCGAMod.SetotsuRatio(otsuRatio) self.qTCGAMod.SetcurvatureWeight(curvatureWeight) self.qTCGAMod.SetsizeThld(sizeThld) self.qTCGAMod.SetsizeUpperThld(sizeUpperThld) self.qTCGAMod.Setmpp(mpp) self.qTCGAMod.SetSourceVol(self.foregroundNode.GetImageData()) self.qTCGAMod.SetSeedVol(self.labelNode.GetImageData()) self.qTCGASegArray[:] = seedArray[:] self.labelNode.GetImageData().Modified() self.labelNode.Modified() self.bEditTCGA = False self.currentMessage = "Quick TCGA done: nuclei segmentation is done; press 'F' to enable ROI selection for refinement" slicer.util.showStatusMessage(self.currentMessage) else: self.currentMessage = "Quick TCGA: go to seed labels first by pressing 'E'" slicer.util.showStatusMessage(self.currentMessage)
def setErrorMessage(self, errorText, errorColor=0): """Call this to seet the message in the error box. Parameters: errorText: string to be displayed in the box errorColor: int 1 or 0 (DEFAULT IS 0) that sets the color of the text as either green (1) or red (0) Returns: none""" if (errorColor == 1): errorColor = "QTextEdit {color:Green}" elif (errorColor == 0): errorColor = "QTextEdit {color:red}" else: # Why did you do this? Read the function? I'm going to make the text white to punish you errorColor = "QTextEdit {color:white}" node = EditUtil.EditUtil().getParameterNode() node.SetParameter("TraceAndSelect,errorMessage", str(errorText)) node.SetParameter("TraceAndSelect,errorMessageColor", str(errorColor)) return
def runQTCGA_NucleiSegYi(self): self.currentMessage = "Quick TCGA: running nucleus segmentation ..." slicer.util.showStatusMessage(self.currentMessage) seedArray = slicer.util.array(self.labelNode.GetName()) self.qTCGASeedArray[:] = seedArray[:] node = EditUtil.EditUtil().getParameterNode( ) # get the parameters from MRML otsuRatio = float(node.GetParameter("QuickTCGAEffect,otsuRatio")) curvatureWeight = float( node.GetParameter("QuickTCGAEffect,curvatureWeight")) / 10 sizeThld = float(node.GetParameter("QuickTCGAEffect,sizeThld")) sizeUpperThld = float( node.GetParameter("QuickTCGAEffect,sizeUpperThld")) mpp = float(node.GetParameter("QuickTCGAEffect,mpp")) / 100 self.qTCGAMod.SetotsuRatio(otsuRatio) self.qTCGAMod.SetcurvatureWeight(curvatureWeight) self.qTCGAMod.SetsizeThld(sizeThld) self.qTCGAMod.SetsizeUpperThld(sizeUpperThld) self.qTCGAMod.Setmpp(mpp) AA = self.foregroundNode.GetImageData() a1 = 0 b1 = 0 ddd = AA.GetDimensions() a2 = ddd[0] - 1 b2 = ddd[1] - 1 LL = self.labelNode.GetImageData() BB = self.GetTile(AA, a1, b1, a2, b2) LL = self.GetTile(LL, a1, b1, a2, b2) self.qTCGAMod.SetSourceVol(BB) self.qTCGAMod.SetSeedVol(LL) self.qTCGAMod.Run_NucleiSegYi() self.qTCGASegArray[:] = seedArray[:] self.MergeImages(LL, self.labelNode.GetImageData(), a1, b1) self.foregroundNode.GetImageData().Modified() self.foregroundNode.Modified() self.labelNode.GetImageData().Modified() self.labelNode.Modified() self.currentMessage = "Quick TCGA done: nuclei segmetnation is done; press 'F' to enable ROI selection for refinement" slicer.util.showStatusMessage(self.currentMessage)
def __init__(self, sliceLogic): print("Preparing Quick TCGA Interaction") self.attributes = 'MouseTool' self.displayName = 'ShortCut Effect' # disconnect all shortcuts that may exist, to allow ShortCut's to work, reconnect once bot is turned off if hasattr(slicer.modules, 'EditorWidget'): slicer.modules.EditorWidget.removeShortcutKeys() self.sliceLogic = sliceLogic self.editUtil = EditUtil.EditUtil() self.swRed = slicer.app.layoutManager().sliceWidget('Red').sliceLogic() self.sw = slicer.app.layoutManager().sliceWidget('Red') self.interactor = self.sw.sliceView().interactor() # initialize to red slice interactor # self.computeCurrSliceSmarter() # initialize the current slice to something meaningful self.mouse_obs_growcut, self.swLUT_growcut = bind_view_observers(self.updateShortCutROI) # initialize Fast GrowCut self.init_ShortCut() self.ShortCutCreated = False
def onSaveButtonClicked(self): bundle = EditUtil.EditUtil().getParameterNode().GetParameter( 'QuickTCGAEffect,erich') tran = json.loads(bundle) layers = [] for key in tran: nn = tran[key] nn["file"] = key + '.tif' layers.append(tran[key]) j = {} j['layers'] = layers j['username'] = self.setupUserName.text j['sourcetile'] = self.tilename j['generator'] = "3DSlicer-4.5.0 with SlicerPathology v1.0a" j['timestamp'] = datetime.datetime.now().strftime("%Y%m%d%H%M%S") labelNodes = slicer.util.getNodes('vtkMRMLLabelMapVolumeNode*') savedMessage = 'Segmentations for the following series were saved:\n\n' for label in labelNodes.values(): labelName = label.GetName() labelFileName = os.path.join(self.dataDirButton.directory, labelName + '.tif') print "labelFileName : " + labelFileName sNode = slicer.vtkMRMLVolumeArchetypeStorageNode() sNode.SetFileName(labelFileName) sNode.SetWriteFileFormat('tif') sNode.SetURI(None) success = sNode.WriteData(label) if success: print "successful writing " + labelFileName else: print "failed writing " + labelFileName jstr = json.dumps(j, sort_keys=True, indent=4, separators=(',', ': ')) f = open( os.path.join(self.dataDirButton.directory, self.tilename + '.json'), 'w') f.write(jstr) f.close()
def apply(self,xy): # # get the parameters from MRML # node = EditUtil.EditUtil().getParameterNode() tolerance = float(node.GetParameter("WandEffect,tolerance")) maxPixels = float(node.GetParameter("WandEffect,maxPixels")) paintOver = int(node.GetParameter("LabelEffect,paintOver")) # # get the label and background volume nodes # labelLogic = self.sliceLogic.GetLabelLayer() labelNode = labelLogic.GetVolumeNode() backgroundLogic = self.sliceLogic.GetBackgroundLayer() backgroundNode = backgroundLogic.GetVolumeNode() # # get the ijk location of the clicked point # by projecting through patient space back into index # space of the volume. Result is sub-pixel, so round it # (note: bg and lb will be the same for volumes created # by the editor, but can be different if the use selected # different bg nodes, but that is not handled here). # xyToIJK = labelLogic.GetXYToIJKTransform().GetMatrix() ijkFloat = xyToIJK.MultiplyPoint(xy+(0,1))[:3] ijk = [] for element in ijkFloat: try: intElement = int(round(element)) except ValueError: intElement = 0 ijk.append(intElement) ijk.reverse() ijk = tuple(ijk) # # Get the numpy array for the bg and label # import vtk.util.numpy_support backgroundImage = backgroundNode.GetImageData() labelImage = labelNode.GetImageData() shape = list(backgroundImage.GetDimensions()) shape.reverse() backgroundArray = vtk.util.numpy_support.vtk_to_numpy(backgroundImage.GetPointData().GetScalars()).reshape(shape) labelArray = vtk.util.numpy_support.vtk_to_numpy(labelImage.GetPointData().GetScalars()).reshape(shape) if self.fillMode == 'Plane': # select the plane corresponding to current slice orientation # for the input volume ijkPlane = self.sliceIJKPlane() i,j,k = ijk if ijkPlane == 'JK': backgroundDrawArray = backgroundArray[:,:,k] labelDrawArray = labelArray[:,:,k] ijk = (i, j) if ijkPlane == 'IK': backgroundDrawArray = backgroundArray[:,j,:] labelDrawArray = labelArray[:,j,:] ijk = (i, k) if ijkPlane == 'IJ': backgroundDrawArray = backgroundArray[i,:,:] labelDrawArray = labelArray[i,:,:] ijk = (j, k) elif self.fillMode == 'Volume': backgroundDrawArray = backgroundArray labelDrawArray = labelArray # # do a recursive search for pixels to change # self.undoRedo.saveState() value = backgroundDrawArray[ijk] label = EditUtil.EditUtil().getLabel() lo = value - tolerance hi = value + tolerance pixelsSet = 0 toVisit = [ijk,] visited = [] while toVisit != []: location = toVisit.pop(0) try: l = labelDrawArray[location] b = backgroundDrawArray[location] except IndexError: continue if (not paintOver and l != 0): # label filled already and not painting over, leave it alone continue if (paintOver and l == label): # label is the current one, but maybe it was filled with another high/low value, # so we have to visit it once (and only once) in this session, too visitedCurrentLocation = True try: visited.index(location) except ValueError: # not found, so not visited yet visitedCurrentLocation = False if visitedCurrentLocation: continue else: visited.append(location) if b < lo or b > hi: continue labelDrawArray[location] = label if l != label: # only count those pixels that were changed (to allow step-by-step growing by multiple mouse clicks) pixelsSet += 1 if pixelsSet > maxPixels: toVisit = [] else: if self.fillMode == 'Plane': # add the 4 neighbors to the stack toVisit.append((location[0] - 1, location[1] )) toVisit.append((location[0] + 1, location[1] )) toVisit.append((location[0] , location[1] - 1 )) toVisit.append((location[0] , location[1] + 1 )) elif self.fillMode == 'Volume': # add the 6 neighbors to the stack toVisit.append((location[0] - 1, location[1] , location[2] )) toVisit.append((location[0] + 1, location[1] , location[2] )) toVisit.append((location[0] , location[1] - 1, location[2] )) toVisit.append((location[0] , location[1] + 1, location[2] )) toVisit.append((location[0] , location[1] , location[2] - 1)) toVisit.append((location[0] , location[1] , location[2] + 1)) # signal to slicer that the label needs to be updated labelImage.Modified() labelNode.Modified()
def fill(self, ijk, optional_seeds=[], mode=0, forced_path=None, forced_point=None): print("Mode: %d" % mode) paintOver = 1 mean = (0, 0) count = 0 node = EditUtil.EditUtil().getParameterNode() # Max number of pixels to fill in (does not include path) print("@@@MaxPixels:%s" % node.GetParameter("TraceAndSelect,maxPixels")) maxPixels = float(node.GetParameter("TraceAndSelect,maxPixels")) # Minimum intensity value to be detected print("@@@Theshold Min:%s" % node.GetParameter("TraceAndSelect,paintThresholdMin")) thresholdMin = float( node.GetParameter("TraceAndSelect,paintThresholdMin")) # Maximum intensity value to be detected print("@@@Theshold Max:%s" % node.GetParameter("TraceAndSelect,paintThresholdMax")) thresholdMax = float( node.GetParameter("TraceAndSelect,paintThresholdMax")) labelLogic = self.sliceLogic.GetLabelLayer() labelNode = labelLogic.GetVolumeNode() backgroundLogic = self.sliceLogic.GetBackgroundLayer() backgroundNode = backgroundLogic.GetVolumeNode() import vtk.util.numpy_support, numpy backgroundImage = backgroundNode.GetImageData() labelImage = labelNode.GetImageData() shape = list(backgroundImage.GetDimensions()) shape.reverse() backgroundArray = vtk.util.numpy_support.vtk_to_numpy( backgroundImage.GetPointData().GetScalars()).reshape(shape) labelArray = vtk.util.numpy_support.vtk_to_numpy( labelImage.GetPointData().GetScalars()).reshape(shape) ijk_reconstruction_indexes = [] # THIS SHOULD ALWAYS BE TRUE # VOLUME MODE IS DISABLED BECAUSE I HAVE NO CLUE WHAT IT IS original_ijk = list(ijk) if self.fillMode == 'Plane': # select the plane corresponding to current slice orientation # for the input volume ijkPlane = self.sliceIJKPlane() i, j, k = ijk if ijkPlane == 'JK': backgroundDrawArray = backgroundArray[:, :, k] labelDrawArray = labelArray[:, :, k] ijk = (i, j) ijk_reconstruction_indexes = (0, 1) if ijkPlane == 'IK': backgroundDrawArray = backgroundArray[:, j, :] labelDrawArray = labelArray[:, j, :] ijk = (i, k) ijk_reconstruction_indexes = (0, 2) if ijkPlane == 'IJ': backgroundDrawArray = backgroundArray[i, :, :] labelDrawArray = labelArray[i, :, :] ijk = (j, k) ijk_reconstruction_indexes = (1, 2) else: print( "HOW DID YOU DO THAT??? WHAT DID YOU DO TO ACTIVATE VOLUME MODE???" ) self.setErrorMessage("Error: volume mode not supported.") return # Log info about where the user clicked for debugging purposes value = backgroundDrawArray[ijk] print("@@@location=", ijk) print("@@@value=", value) # Get the current label that the user wishes to assign using the tool label = EditUtil.EditUtil().getLabel() # Use lo and hi for threshold checks # Easiest way to do things is check if a pixel is outside the threshold, ie. lo = thresholdMin hi = thresholdMax best_path = [] fill_point = ijk if mode == 0 and forced_path is not None and forced_point is not None: best_path = forced_path fill_point = forced_point else: # Build path best_path, visited, dead_ends = gimme_a_path( ijk, 200, hi, lo, backgroundDrawArray, optional_seeds) print("@@@Dead ends:", dead_ends) attempts = 0 max_attempts = 2 while dead_ends > 150 or dead_ends < 0 and attempts < max_attempts: attempts += 1 lo -= 25 print("Lowering min tolerance to:", lo) node.SetParameter("LabelEffect,paintThresholdMin", str(lo)) best_path, visited, dead_ends = gimme_a_path( ijk, 200, hi, lo, backgroundDrawArray, optional_seeds) print("@@@Dead ends:", dead_ends) if dead_ends < 0: print("@@@No path found? Weird.") self.setErrorMessage( "Error: could not find any suitable path.") return # Save state before doing anything self.undoRedo.saveState() for pixel in visited: labelDrawArray[pixel] = label EditUtil.EditUtil().markVolumeNodeAsModified(labelNode) if mode == 1: # Outline only mode print("Outline made, returning.") self.setErrorMessage( "Preview complete. No errrors detected.\nLeft click to confirm.\nRight click to try a new outline.\nUndo to remove.", 1) return (best_path, ijk) # # Fill path # # Fill the path using a recursive search toVisit = [ fill_point, ] extrema = get_extrema(best_path) # Create a map that contains the location of the pixels # that have been already visited (added or considered to be added). # This is required if paintOver is enabled because then we reconsider # all pixels (not just the ones that have not labelled yet). if paintOver: labelDrawVisitedArray = numpy.zeros(labelDrawArray.shape, dtype='bool') pixelsSet = 0 print("@@@FILLING PATH") while toVisit != []: location = toVisit.pop(0) try: l = fetch_val(labelDrawArray, location) b = fetch_val(backgroundDrawArray, location) except IndexError: continue if (not paintOver and l != 0): # label filled already and not painting over, leave it alone continue try: if (paintOver and l == label): temp1 = mean[0] + location[0] temp2 = mean[1] + location[1] mean = (temp1, temp2) count += 1 # label is the current one, but maybe it was filled with another high/low value, # so we have to visit it once (and only once) in this session, too if labelDrawVisitedArray[location]: # visited already, so don't try to fill it again continue else: # we'll visit this pixel now, so mark it as visited labelDrawVisitedArray[location] = True except ValueError: print("@@@VALUE ERROR!", l) print("@@@Location: ", location) print("@@@fill_point:", fill_point) print("@@@toVisit:", toVisit) continue if location in best_path: continue if not (extrema[0] < location[0] < extrema[1] and extrema[2] < location[1] < extrema[3]): # Went out of bounds for path print("@@@WENT OUT OF BOUNDS FOR PATH!") self.setErrorMessage("Error: Went out of bounds for path.") self.undoRedo.undo() return labelDrawArray[location] = label if l != label: # only count those pixels that were changed (to allow step-by-step growing by multiple mouse clicks) pixelsSet += 1 if pixelsSet > maxPixels: toVisit = [] else: # add the 4 neighbors to the stack toVisit.append((location[0] - 1, location[1])) toVisit.append((location[0] + 1, location[1])) toVisit.append((location[0], location[1] - 1)) toVisit.append((location[0], location[1] + 1)) # signal to slicer that the label needs to be updated ## CHANGE OFFSET print("@@@Offset:|%s|" % node.GetParameter("TraceAndSelect,offsetvalue")) self.offset = float(node.GetParameter("TraceAndSelect,offsetvalue")) print("OFFSET SIGN: %s" % math.copysign(1, self.offset)) if self.offset != 0: slices_seen = self.progress.maximum - abs(self.offset) if self.progress.wasCanceled: self.offset = 0 layoutManager = slicer.app.layoutManager() widget = layoutManager.sliceWidget('Red') rednode = widget.sliceLogic().GetSliceNode() rednode.SetSliceOffset(rednode.GetSliceOffset() + math.copysign(1, self.offset)) node.SetParameter("TraceAndSelect,offsetvalue", str(0)) self.setErrorMessage( "Fill abandoned after {} slice(s)".format( int(slices_seen)), 1) return self.progress.setValue(slices_seen) layoutManager = slicer.app.layoutManager() widget = layoutManager.sliceWidget('Red') rednode = widget.sliceLogic().GetSliceNode() rednode.SetSliceOffset(rednode.GetSliceOffset() + math.copysign(1, self.offset)) node.SetParameter("TraceAndSelect,offsetvalue", str(self.offset - math.copysign(1, self.offset))) print(self.offset) ### Calc centoid mean stuff here recs_mean = (mean[0] / count, mean[1] / count) rec_mean = get_optional_seeds(best_path, recs_mean)[0] print("MEAN:", rec_mean, recs_mean) rec_ijk = list(original_ijk) for i in range(0, 3): rec_ijk[i] = int(rec_ijk[i] + int(math.copysign(1, self.offset))) rec_ijk[ijk_reconstruction_indexes[0]] = rec_mean[0] rec_ijk[ijk_reconstruction_indexes[1]] = rec_mean[1] print("RECURSIVE IJK:", rec_ijk) print("#########RECURSE#########") return self.fill(rec_ijk, optional_seeds=get_optional_seeds( best_path, recs_mean)) ### print("@@@FILL DONE") EditUtil.EditUtil().markVolumeNodeAsModified(labelNode) self.setErrorMessage("Fill complete. No errors detected.", 1) return
def apply(self,xy): # TODO: save the undo state - not yet available for extensions # EditorStoreCheckPoint $_layers(label,node) # # get the parameters from MRML # node = EditUtil.EditUtil().getParameterNode() tolerance = float(node.GetParameter("GooCut,tolerance")) maxPixels = float(node.GetParameter("GooCut,maxPixels")) # # get the label and background volume nodes # labelLogic = self.sliceLogic.GetLabelLayer() labelNode = labelLogic.GetVolumeNode() labelNode.SetModifiedSinceRead(1) backgroundLogic = self.sliceLogic.GetBackgroundLayer() backgroundNode = backgroundLogic.GetVolumeNode() # # get the ijk location of the clicked point # by projecting through patient space back into index # space of the volume. Result is sub-pixel, so round it # (note: bg and lb will be the same for volumes created # by the editor, but can be different if the use selected # different bg nodes, but that is not handled here). # xyToIJK = labelLogic.GetXYToIJKTransform().GetMatrix() ijkFloat = xyToIJK.MultiplyPoint(xy+(0,1))[:3] ijk = [] for element in ijkFloat: try: intElement = int(element) except ValueError: intElement = 0 ijk.append(intElement) ijk.reverse() ijk = tuple(ijk) # # Get the numpy array for the bg and label # import vtk.util.numpy_support backgroundImage = backgroundNode.GetImageData() labelImage = labelNode.GetImageData() shape = list(backgroundImage.GetDimensions()) shape.reverse() backgroundArray = vtk.util.numpy_support.vtk_to_numpy(backgroundImage.GetPointData().GetScalars()).reshape(shape) labelArray = vtk.util.numpy_support.vtk_to_numpy(labelImage.GetPointData().GetScalars()).reshape(shape) # # do a region growing # try: value = backgroundArray[ijk] except IndexError: # outside of volume return label = EditUtil.EditUtil().getLabel() lo = value - tolerance hi = value + tolerance pixelsSet = 0 toVisit = [ijk,] while toVisit != []: location = toVisit.pop() try: l = labelArray[location] b = backgroundArray[location] except IndexError: continue if l != 0 or b < lo or b > hi: continue labelArray[location] = label pixelsSet += 1 if pixelsSet > maxPixels: toVisit = [] else: # add the 6 neighbors to the stack toVisit.append((location[0] - 1, location[1] , location[2] )) toVisit.append((location[0] + 1, location[1] , location[2] )) toVisit.append((location[0] , location[1] - 1, location[2] )) toVisit.append((location[0] , location[1] + 1, location[2] )) toVisit.append((location[0] , location[1] , location[2] - 1)) toVisit.append((location[0] , location[1] , location[2] + 1)) labelImage.Modified() labelNode.Modified()
def processEvent(self, caller=None, event=None): """ handle events from the render window interactor """ node = EditUtil.EditUtil().getParameterNode() preview = int(node.GetParameter("TraceAndSelect,preview")) # Clear any saved outlines if preview has been just disabled if not preview: if self.prevPath != []: self.prevPath = [] self.prevFillPoint = None self.undoRedo.undo() # let the superclass deal with the event if it wants to super(TraceAndSelectTool, self).processEvent(caller, event) # LEFT CLICK if event == "LeftButtonPressEvent": xy = self.interactor.GetEventPosition() sliceLogic = self.sliceWidget.sliceLogic() logic = TraceAndSelectLogic(sliceLogic) logic.undoRedo = self.undoRedo if self.prevPath != []: logic.apply(xy, forced_path=self.prevPath, forced_point=self.prevFillPoint) self.prevPath = [] self.prevFillPoint = None else: logic.apply(xy) print("Got a %s at %s in %s" % (event, str(xy), self.sliceWidget.sliceLogic().GetSliceNode().GetName())) self.abortEvent(event) # RIGHT CLICK elif event == "RightButtonPressEvent" and preview: xy = self.interactor.GetEventPosition() sliceLogic = self.sliceWidget.sliceLogic() logic = TraceAndSelectLogic(sliceLogic) logic.undoRedo = self.undoRedo # Erase stored path and remove from view if self.prevPath != []: self.prevPath = [] self.prevFillPoint = None logic.undoRedo.undo() # Store prevPath and prevFillPoint self.prevPath, self.prevFillPoint = logic.apply(xy, 1) print("Got a %s at %s in %s" % (event, str(xy), self.sliceWidget.sliceLogic().GetSliceNode().GetName())) self.abortEvent(event) # SLICE VIEW HAS CHANGED elif event == "ModifiedEvent": # Offset was changed on one of the viewing panels # Erase stored path and remove from view if self.prevPath != []: self.prevPath = [] self.prevFillPoint = None self.undoRedo.undo() sliceLogic = self.sliceWidget.sliceLogic() logic = TraceAndSelectLogic(sliceLogic) logic.setErrorMessage("Previewed path was discarded.", 1) else: pass # events from the slice node if caller and caller.IsA('vtkMRMLSliceNode'): # here you can respond to pan/zoom or other changes # to the view pass
def init_QuickTCGA(self): self.emergencyStopFunc = None self.dialogBox = qt.QMessageBox( ) #will display messages to draw users attention if he does anything wrong self.dialogBox.setWindowTitle("QuickTCGA Error") self.dialogBox.setWindowModality( qt.Qt.NonModal ) #will allow user to continue interacting with Slicer # TODO: check this claim- might be causing leaks # set the image, label nodes (this will not change although the user can # alter what is bgrnd/frgrnd in editor) # Confused about how info propagates UIarray to UIVol, not the other way, NEEDS AUTO TESTS self.labelNode = self.editUtil.getLabelVolume( ) #labelLogic.GetVolumeNode() self.backgroundNode = self.editUtil.getBackgroundVolume( ) #backgroundLogic.GetVolumeNode() self.foregroundNode = self.swRed.GetForegroundLayer().GetVolumeNode() #perform safety check on right images/labels being selected, #set up images #if red slice doesnt have a label or image, go no further if type(self.backgroundNode) == type(None) or type( self.labelNode) == type(None): self.dialogBox.setText( "Either Image (must be Background Image) or Label not set in slice views." ) self.dialogBox.show() if self.emergencyStopFunc: self.emergencyStopFunc() return volumesLogic = slicer.modules.volumes.logic() self.labelName = self.labelNode.GetName( ) # record name of label so user, cant trick us self.imgBgrdName = self.backgroundNode.GetName() self.imgFgrdName = self.foregroundNode.GetName() if self.sliceViewMatchEditor( self.sliceLogic ) == False: # do nothing, exit function if user has played with images if self.emergencyStopFunc: self.emergencyStopFunc() return # QuickTCGA shortcuts ##resetQTCGAKey = qt.QKeySequence(qt.Qt.Key_R) # reset initialization flag ##runQTCGAClusterKey = qt.QKeySequence(qt.Qt.Key_S) # run fast growcut runNucleiSegKey = qt.QKeySequence(qt.Qt.Key_Y) ##editTCGAKey = qt.QKeySequence(qt.Qt.Key_E) # edit seed labels ##runQTCGATemplateKey = qt.QKeySequence(qt.Qt.Key_T) ##runQTCGARefineCurvatureKey = qt.QKeySequence(qt.Qt.Key_U) ##runQTCGAShortCutKey = qt.QKeySequence(qt.Qt.Key_C) ##runQTCGAShortEditCutKey = qt.QKeySequence(qt.Qt.Key_F) print " key to run QuickTCGA segmentation is Y" self.qtkeyconnections = [] self.qtkeydefsQTCGA = [[runNucleiSegKey, self.runQTCGA_NucleiSegYi]] for keydef in self.qtkeydefsQTCGA: s = qt.QShortcut(keydef[0], slicer.util.mainWindow() ) # connect this qt event to mainWindow focus s.connect('activated()', keydef[1]) self.qtkeyconnections.append(s) self.qTCGALabMod_tag = self.sliceLogic.AddObserver( "ModifiedEvent", self.QTCGAChangeLabelInput ) # put test listener on the whole window # Quick TCGA parameters self.bEditTCGA = True self.bEditShortCut = False self.currentMessage = "" seedArray = slicer.util.array(self.labelName) self.qTCGASeedArray = seedArray.copy() self.qTCGASegArray = seedArray.copy() self.qTCGASeedArray[:] = 0 self.qTCGASegArray[:] = 0 self.SCutROIRad = 50 self.volSize = self.labelNode.GetImageData().GetDimensions() self.qSCutROIArray = seedArray.copy( ) #np.zeros([self.volSize[0],self.volSize[1],1]) self.qSCutROIArray[:] = 0 roiVTK = vtk.vtkImageData() roiVTK.DeepCopy(self.labelNode.GetImageData()) self.roiVTK = roiVTK import vtkSlicerQuickTCGAModuleLogicPython node = EditUtil.EditUtil().getParameterNode( ) # get the parameters from MRML otsuRatio = float(node.GetParameter("QuickTCGAEffect,otsuRatio")) curvatureWeight = float( node.GetParameter("QuickTCGAEffect,curvatureWeight")) / 10 sizeThld = float(node.GetParameter("QuickTCGAEffect,sizeThld")) sizeUpperThld = float( node.GetParameter("QuickTCGAEffect,sizeUpperThld")) mpp = float(node.GetParameter("QuickTCGAEffect,mpp")) / 100 cparams["otsuRatio"] = otsuRatio cparams["curvatureWeight"] = curvatureWeight cparams["sizeThld"] = sizeThld cparams["sizeUpperThld"] = sizeUpperThld cparams["mpp"] = mpp qTCGAMod = vtkSlicerQuickTCGAModuleLogicPython.vtkQuickTCGA() qTCGAMod.SetSourceVol(self.foregroundNode.GetImageData()) qTCGAMod.SetotsuRatio(otsuRatio) qTCGAMod.SetcurvatureWeight(curvatureWeight) qTCGAMod.SetsizeThld(sizeThld) qTCGAMod.SetsizeUpperThld(sizeUpperThld) qTCGAMod.Setmpp(mpp) qTCGAMod.Initialization() self.qTCGAMod = qTCGAMod self.QuickTCGACreated = True #tracks if completed the initializtion (so can do stop correctly) of KSlice
def test_ThresholdThreading(self): """ Replicate the issue reported in bug 1822 where splitting a grow-cut produced volume causes a multi-threading related issue on mac release builds """ self.delayDisplay("Starting the test") # # first, get some sample data # self.delayDisplay("Get some data") import SampleData head = SampleData.downloadSample("MRHead") # # now, define an ROI in it # roi = slicer.vtkMRMLAnnotationROINode() slicer.mrmlScene.AddNode(roi) roi.SetXYZ(-2, 104, -80) roi.SetRadiusXYZ(30, 30, 30) # # apply the cropping to the head # cropLogic = slicer.modules.cropvolume.logic() cvpn = slicer.vtkMRMLCropVolumeParametersNode() cvpn.SetROINodeID( roi.GetID() ) cvpn.SetInputVolumeNodeID( head.GetID() ) cropLogic.Apply( cvpn ) croppedHead = slicer.mrmlScene.GetNodeByID( cvpn.GetOutputVolumeNodeID() ) # # create a label map and set it for editing # volumesLogic = slicer.modules.volumes.logic() croppedHeadLabel = volumesLogic.CreateAndAddLabelVolume( slicer.mrmlScene, croppedHead, croppedHead.GetName() + '-label' ) selectionNode = slicer.app.applicationLogic().GetSelectionNode() selectionNode.SetActiveVolumeID( croppedHead.GetID() ) selectionNode.SetActiveLabelVolumeID( croppedHeadLabel.GetID() ) slicer.app.applicationLogic().PropagateVolumeSelection(0) # # got to the editor and do some drawing # self.delayDisplay("Paint some things") parameterNode = EditUtil.getParameterNode() lm = slicer.app.layoutManager() paintEffect = EditorLib.PaintEffectOptions() paintEffect.setMRMLDefaults() paintEffect.__del__() sliceWidget = lm.sliceWidget('Red') paintTool = EditorLib.PaintEffectTool(sliceWidget) EditUtil.setLabel(1) paintTool.paintAddPoint(100,100) paintTool.paintApply() EditUtil.setLabel(2) paintTool.paintAddPoint(200,200) paintTool.paintApply() paintTool.cleanup() paintTool = None self.delayDisplay("Now grow cut") # # now do GrowCut # growCutLogic = EditorLib.GrowCutEffectLogic(sliceWidget.sliceLogic()) growCutLogic.growCut() # # now split the volume, merge it back, and see if it looks right # preArray = slicer.util.array(croppedHeadLabel.GetName()) slicer.util.selectModule('Editor') slicer.util.findChildren(text='Split Merge Volume')[0].clicked() slicer.util.findChildren(text='Merge All')[0].clicked() postArray = slicer.util.array(croppedHeadLabel.GetName()) if (postArray - preArray).max() != 0: print("!$!$!#!@#!@!@$%! Test Failed!!") else: print("Ahh... test passed.") self.assertEqual((postArray - preArray).max(), 0) self.delayDisplay("Test passed!")
def test_sceneImport24281(self): """ Ideally you should have several levels of tests. At the lowest level tests should exercise the functionality of the logic with different inputs (both valid and invalid). At higher levels your tests should emulate the way the user would interact with your code and confirm that it still works the way you intended. One of the most important features of the tests is that it should alert other developers when their changes will have an impact on the behavior of your module. For example, if a developer removes a feature that you depend on, your test should break so they know that the feature is needed. """ self.delayDisplay("Starting the test") # # first, get some data # self.delayDisplay("Getting Data") import SampleData head = SampleData.downloadSample("MRHead") # # create a label map and set it for editing # self.delayDisplay("Setting up LabelMap") volumesLogic = slicer.modules.volumes.logic() headLabel = volumesLogic.CreateAndAddLabelVolume( slicer.mrmlScene, head, head.GetName() + '-label' ) selectionNode = slicer.app.applicationLogic().GetSelectionNode() selectionNode.SetActiveVolumeID( head.GetID() ) selectionNode.SetActiveLabelVolumeID( headLabel.GetID() ) slicer.app.applicationLogic().PropagateVolumeSelection(0) # # got to the editor and do some drawing # self.delayDisplay("Setting up Editor and drawing") parameterNode = EditUtil.getParameterNode() lm = slicer.app.layoutManager() paintEffectOptions = EditorLib.PaintEffectOptions() paintEffectOptions.setMRMLDefaults() paintEffectOptions.__del__() self.delayDisplay('Paint radius is %s' % parameterNode.GetParameter('PaintEffect,radius')) sliceWidget = lm.sliceWidget('Red') size = min(sliceWidget.width,sliceWidget.height) step = int(size / 12) center = int(size / 2) parameterNode.SetParameter('PaintEffect,radius', '20') paintTool = EditorLib.PaintEffectTool(sliceWidget) self.delayDisplay('Paint radius is %s, tool radius is %d' % (parameterNode.GetParameter('PaintEffect,radius'),paintTool.radius)) for label in range(1,5): EditUtil.setLabel(label) pos = center - 2*step + (step * label) self.delayDisplay('Painting %d, at (%d,%d)' % (label,pos,pos),200) paintTool.paintAddPoint(pos,pos) paintTool.paintApply() paintTool.cleanup() paintTool = None # # now build: # create a model using the command line module # based on the current editor parameters # - make a new hierarchy node # self.delayDisplay( "Building..." ) parameters = {} parameters["InputVolume"] = headLabel.GetID() # create models for all labels parameters["JointSmoothing"] = True parameters["StartLabel"] = -1 parameters["EndLabel"] = -1 outHierarchy = slicer.vtkMRMLModelHierarchyNode() outHierarchy.SetScene( slicer.mrmlScene ) outHierarchy.SetName( "sceneImport2428Hierachy" ) slicer.mrmlScene.AddNode( outHierarchy ) parameters["ModelSceneFile"] = outHierarchy modelMaker = slicer.modules.modelmaker self.CLINode = None self.CLINode = slicer.cli.runSync(modelMaker, self.CLINode, parameters, delete_temporary_files=False) self.delayDisplay("Models built") success = self.verifyModels() success = success and (slicer.mrmlScene.GetNumberOfNodesByClass( "vtkMRMLModelNode" ) > 3) self.delayDisplay("Test finished") if success: self.delayDisplay("Ahh... test passed.") else: self.delayDisplay("!$!$!#!@#!@!@$%! Test Failed!!") self.assertTrue(success)