def initFeatSelDlg(self): if self.topLevelOperatorView.name == "OpPixelClassification": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplet.topLevelOperator.innerOperators[ 0] elif self.topLevelOperatorView.name == "OpPixelClassification0": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[ 0].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name == "OpPixelClassification1": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[ 1].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name == "OpPixelClassification2": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[ 2].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name == "OpPixelClassification3": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[ 3].topLevelOperator.innerOperators[0] else: raise NotImplementedError self.featSelDlg = FeatureSelectionDialog(thisOpFeatureSelection, self.topLevelOperatorView)
def initFeatSelDlg(self): if self.topLevelOperatorView.name=="OpPixelClassification": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplet.topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name=="OpPixelClassification0": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[0].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name=="OpPixelClassification1": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[1].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name=="OpPixelClassification2": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[2].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name=="OpPixelClassification3": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[3].topLevelOperator.innerOperators[0] else: raise NotImplementedError self.featSelDlg = FeatureSelectionDialog(thisOpFeatureSelection, self.topLevelOperatorView)
class PixelClassificationGui(LabelingGui): ########################################### ### AppletGuiInterface Concrete Methods ### ########################################### def centralWidget(self): return self def stopAndCleanUp(self): for fn in self.__cleanup_fns: fn() # Base class super(PixelClassificationGui, self).stopAndCleanUp() def viewerControlWidget(self): return self._viewerControlUi def menus(self): menus = super(PixelClassificationGui, self).menus() advanced_menu = QMenu("Advanced", parent=self) def handleClassifierAction(): dlg = ClassifierSelectionDlg(self.topLevelOperatorView, parent=self) dlg.exec_() classifier_action = advanced_menu.addAction("Classifier...") classifier_action.triggered.connect(handleClassifierAction) def showVarImpDlg(): varImpDlg = VariableImportanceDialog( self.topLevelOperatorView.Classifier.value.named_importances, parent=self) varImpDlg.exec_() advanced_menu.addAction("Variable Importance Table").triggered.connect( showVarImpDlg) def handleImportLabelsAction(): # Find the directory of the most recently opened image file mostRecentImageFile = PreferencesManager().get( 'DataSelection', 'recent image') if mostRecentImageFile is not None: defaultDirectory = os.path.split(mostRecentImageFile)[0] else: defaultDirectory = os.path.expanduser('~') fileNames = DataSelectionGui.getImageFileNamesToOpen( self, defaultDirectory) fileNames = map(str, fileNames) # For now, we require a single hdf5 file if len(fileNames) > 1: QMessageBox.critical( self, "Too many files", "Labels must be contained in a single hdf5 volume.") return if len(fileNames) == 0: # user cancelled return file_path = fileNames[0] internal_paths = DataSelectionGui.getPossibleInternalPaths( file_path) if len(internal_paths) == 0: QMessageBox.critical( self, "No volumes in file", "Couldn't find a suitable dataset in your hdf5 file.") return if len(internal_paths) == 1: internal_path = internal_paths[0] else: dlg = H5VolumeSelectionDlg(internal_paths, self) if dlg.exec_() == QDialog.Rejected: return selected_index = dlg.combo.currentIndex() internal_path = str(internal_paths[selected_index]) path_components = PathComponents(file_path) path_components.internalPath = str(internal_path) try: top_op = self.topLevelOperatorView opReader = OpInputDataReader(parent=top_op.parent) opReader.FilePath.setValue(path_components.totalPath()) # Reorder the axes op5 = OpReorderAxes(parent=top_op.parent) op5.AxisOrder.setValue(top_op.LabelInputs.meta.getAxisKeys()) op5.Input.connect(opReader.Output) # Finally, import the labels top_op.importLabels(top_op.current_view_index(), op5.Output) finally: op5.cleanUp() opReader.cleanUp() def print_label_blocks(sorted_axis): sorted_column = self.topLevelOperatorView.InputImages.meta.getAxisKeys( ).index(sorted_axis) input_shape = self.topLevelOperatorView.InputImages.meta.shape label_block_slicings = self.topLevelOperatorView.NonzeroLabelBlocks.value sorted_block_slicings = sorted(label_block_slicings, key=lambda s: s[sorted_column]) for slicing in sorted_block_slicings: # Omit channel order = "".join( self.topLevelOperatorView.InputImages.meta.getAxisKeys()) line = order[:-1].upper() + ": " line += slicing_to_string(slicing[:-1], input_shape) print line labels_submenu = QMenu("Labels") self.labels_submenu = labels_submenu # Must retain this reference or else it gets auto-deleted. import_labels_action = labels_submenu.addAction("Import Labels...") import_labels_action.triggered.connect(handleImportLabelsAction) self.print_labels_submenu = QMenu("Print Label Blocks") labels_submenu.addMenu(self.print_labels_submenu) for axis in self.topLevelOperatorView.InputImages.meta.getAxisKeys( )[:-1]: self.print_labels_submenu\ .addAction("Sort by {}".format( axis.upper() ))\ .triggered.connect( partial(print_label_blocks, axis) ) advanced_menu.addMenu(labels_submenu) menus += [advanced_menu] return menus ########################################### ########################################### def __init__(self, parentApplet, topLevelOperatorView, labelingDrawerUiPath=None): self.parentApplet = parentApplet # Tell our base class which slots to monitor labelSlots = LabelingGui.LabelingSlots() labelSlots.labelInput = topLevelOperatorView.LabelInputs labelSlots.labelOutput = topLevelOperatorView.LabelImages labelSlots.labelEraserValue = topLevelOperatorView.opLabelPipeline.opLabelArray.eraser labelSlots.labelDelete = topLevelOperatorView.opLabelPipeline.DeleteLabel labelSlots.labelNames = topLevelOperatorView.LabelNames self.__cleanup_fns = [] # We provide our own UI file (which adds an extra control for interactive mode) if labelingDrawerUiPath is None: labelingDrawerUiPath = os.path.split( __file__)[0] + '/labelingDrawer.ui' # Base class init super(PixelClassificationGui, self).__init__(parentApplet, labelSlots, topLevelOperatorView, labelingDrawerUiPath) self.topLevelOperatorView = topLevelOperatorView self.interactiveModeActive = False # Immediately update our interactive state self.toggleInteractive( not self.topLevelOperatorView.FreezePredictions.value) self._currentlySavingPredictions = False self.labelingDrawerUi.labelListView.support_merges = True self.labelingDrawerUi.liveUpdateButton.setEnabled(False) self.labelingDrawerUi.liveUpdateButton.setIcon(QIcon( ilastikIcons.Play)) self.labelingDrawerUi.liveUpdateButton.setToolButtonStyle( Qt.ToolButtonTextBesideIcon) self.labelingDrawerUi.liveUpdateButton.toggled.connect( self.toggleInteractive) self.initFeatSelDlg() self.labelingDrawerUi.suggestFeaturesButton.clicked.connect( self.show_feature_selection_dialog) self.featSelDlg.accepted.connect(self.update_features_from_dialog) self.labelingDrawerUi.suggestFeaturesButton.setEnabled(False) self.topLevelOperatorView.LabelNames.notifyDirty( bind(self.handleLabelSelectionChange)) self.__cleanup_fns.append( partial(self.topLevelOperatorView.LabelNames.unregisterDirty, bind(self.handleLabelSelectionChange))) self._initShortcuts() # FIXME: We MUST NOT enable the render manager by default, # since it will drastically slow down the app for large volumes. # For now, we leave it off by default. # To re-enable rendering, we need to allow the user to render a segmentation # and then initialize the render manager on-the-fly. # (We might want to warn the user if her volume is not small.) self.render = False self._renderMgr = None self._renderedLayers = {} # (layer name, label number) # Always off for now (see note above) if self.render: try: self._renderMgr = RenderingManager(self.editor.view3d) except: self.render = False # toggle interactive mode according to freezePredictions.value self.toggleInteractive( not self.topLevelOperatorView.FreezePredictions.value) def FreezePredDirty(): self.toggleInteractive( not self.topLevelOperatorView.FreezePredictions.value) # listen to freezePrediction changes self.topLevelOperatorView.FreezePredictions.notifyDirty( bind(FreezePredDirty)) self.__cleanup_fns.append( partial( self.topLevelOperatorView.FreezePredictions.unregisterDirty, bind(FreezePredDirty))) def initFeatSelDlg(self): if self.topLevelOperatorView.name == "OpPixelClassification": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplet.topLevelOperator.innerOperators[ 0] elif self.topLevelOperatorView.name == "OpPixelClassification0": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[ 0].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name == "OpPixelClassification1": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[ 1].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name == "OpPixelClassification2": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[ 2].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name == "OpPixelClassification3": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[ 3].topLevelOperator.innerOperators[0] else: raise NotImplementedError self.featSelDlg = FeatureSelectionDialog(thisOpFeatureSelection, self.topLevelOperatorView) def show_feature_selection_dialog(self): self.featSelDlg.exec_() def update_features_from_dialog(self): if self.topLevelOperatorView.name == "OpPixelClassification": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplet.topLevelOperator.innerOperators[ 0] elif self.topLevelOperatorView.name == "OpPixelClassification0": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[ 0].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name == "OpPixelClassification1": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[ 1].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name == "OpPixelClassification2": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[ 2].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name == "OpPixelClassification3": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[ 3].topLevelOperator.innerOperators[0] else: raise NotImplementedError thisOpFeatureSelection.SelectionMatrix.setValue( self.featSelDlg.selected_features_matrix) thisOpFeatureSelection.SelectionMatrix.setDirty() thisOpFeatureSelection.setupOutputs() def initViewerControlUi(self): localDir = os.path.split(__file__)[0] self._viewerControlUi = uic.loadUi( os.path.join(localDir, "viewerControls.ui")) # Connect checkboxes def nextCheckState(checkbox): checkbox.setChecked(not checkbox.isChecked()) self._viewerControlUi.checkShowPredictions.nextCheckState = partial( nextCheckState, self._viewerControlUi.checkShowPredictions) self._viewerControlUi.checkShowSegmentation.nextCheckState = partial( nextCheckState, self._viewerControlUi.checkShowSegmentation) self._viewerControlUi.checkShowPredictions.clicked.connect( self.handleShowPredictionsClicked) self._viewerControlUi.checkShowSegmentation.clicked.connect( self.handleShowSegmentationClicked) # The editor's layerstack is in charge of which layer movement buttons are enabled model = self.editor.layerStack self._viewerControlUi.viewerControls.setupConnections(model) def _initShortcuts(self): mgr = ShortcutManager() ActionInfo = ShortcutManager.ActionInfo shortcutGroupName = "Predictions" mgr.register( "p", ActionInfo(shortcutGroupName, "Toggle Prediction", "Toggle Prediction Layer Visibility", self._viewerControlUi.checkShowPredictions.click, self._viewerControlUi.checkShowPredictions, self._viewerControlUi.checkShowPredictions)) mgr.register( "s", ActionInfo(shortcutGroupName, "Toggle Segmentaton", "Toggle Segmentaton Layer Visibility", self._viewerControlUi.checkShowSegmentation.click, self._viewerControlUi.checkShowSegmentation, self._viewerControlUi.checkShowSegmentation)) mgr.register( "l", ActionInfo(shortcutGroupName, "Live Prediction", "Toggle Live Prediction Mode", self.labelingDrawerUi.liveUpdateButton.toggle, self.labelingDrawerUi.liveUpdateButton, self.labelingDrawerUi.liveUpdateButton)) def _setup_contexts(self, layer): def callback(pos, clayer=layer): name = clayer.name if name in self._renderedLayers: label = self._renderedLayers.pop(name) self._renderMgr.removeObject(label) self._update_rendering() else: label = self._renderMgr.addObject() self._renderedLayers[clayer.name] = label self._update_rendering() if self.render: layer.contexts.append( QAction('Toggle 3D rendering', None, triggered=callback)) def setupLayers(self): """ Called by our base class when one of our data slots has changed. This function creates a layer for each slot we want displayed in the volume editor. """ # Base class provides the label layer. layers = super(PixelClassificationGui, self).setupLayers() ActionInfo = ShortcutManager.ActionInfo if ilastik_config.getboolean('ilastik', 'debug'): # Add the label projection layer. labelProjectionSlot = self.topLevelOperatorView.opLabelPipeline.opLabelArray.Projection2D if labelProjectionSlot.ready(): projectionSrc = LazyflowSource(labelProjectionSlot) try: # This colortable requires matplotlib from volumina.colortables import jet projectionLayer = ColortableLayer( projectionSrc, colorTable=[QColor(0, 0, 0, 128).rgba()] + jet(N=255), normalize=(0.0, 1.0)) except (ImportError, RuntimeError): pass else: projectionLayer.name = "Label Projection" projectionLayer.visible = False projectionLayer.opacity = 1.0 layers.append(projectionLayer) # Show the mask over everything except labels maskSlot = self.topLevelOperatorView.PredictionMasks if maskSlot.ready(): maskLayer = self._create_binary_mask_layer_from_slot(maskSlot) maskLayer.name = "Mask" maskLayer.visible = True maskLayer.opacity = 1.0 layers.append(maskLayer) # Add the uncertainty estimate layer uncertaintySlot = self.topLevelOperatorView.UncertaintyEstimate if uncertaintySlot.ready(): uncertaintySrc = LazyflowSource(uncertaintySlot) uncertaintyLayer = AlphaModulatedLayer(uncertaintySrc, tintColor=QColor(Qt.cyan), range=(0.0, 1.0), normalize=(0.0, 1.0)) uncertaintyLayer.name = "Uncertainty" uncertaintyLayer.visible = False uncertaintyLayer.opacity = 1.0 uncertaintyLayer.shortcutRegistration = ( "u", ActionInfo("Prediction Layers", "Uncertainty", "Show/Hide Uncertainty", uncertaintyLayer.toggleVisible, self.viewerControlWidget(), uncertaintyLayer)) layers.append(uncertaintyLayer) labels = self.labelListData # Add each of the segmentations for channel, segmentationSlot in enumerate( self.topLevelOperatorView.SegmentationChannels): if segmentationSlot.ready() and channel < len(labels): ref_label = labels[channel] segsrc = LazyflowSource(segmentationSlot) segLayer = AlphaModulatedLayer(segsrc, tintColor=ref_label.pmapColor(), range=(0.0, 1.0), normalize=(0.0, 1.0)) segLayer.opacity = 1 segLayer.visible = False #self.labelingDrawerUi.liveUpdateButton.isChecked() segLayer.visibleChanged.connect( self.updateShowSegmentationCheckbox) def setLayerColor(c, segLayer_=segLayer, initializing=False): if not initializing and segLayer_ not in self.layerstack: # This layer has been removed from the layerstack already. # Don't touch it. return segLayer_.tintColor = c self._update_rendering() def setSegLayerName(n, segLayer_=segLayer, initializing=False): if not initializing and segLayer_ not in self.layerstack: # This layer has been removed from the layerstack already. # Don't touch it. return oldname = segLayer_.name newName = "Segmentation (%s)" % n segLayer_.name = newName if not self.render: return if oldname in self._renderedLayers: label = self._renderedLayers.pop(oldname) self._renderedLayers[newName] = label setSegLayerName(ref_label.name, initializing=True) ref_label.pmapColorChanged.connect(setLayerColor) ref_label.nameChanged.connect(setSegLayerName) #check if layer is 3d before adding the "Toggle 3D" option #this check is done this way to match the VolumeRenderer, in #case different 3d-axistags should be rendered like t-x-y #_axiskeys = segmentationSlot.meta.getAxisKeys() if len(segmentationSlot.meta.shape) == 4: #the Renderer will cut out the last shape-dimension, so #we're checking for 4 dimensions self._setup_contexts(segLayer) layers.append(segLayer) # Add each of the predictions for channel, predictionSlot in enumerate( self.topLevelOperatorView.PredictionProbabilityChannels): if predictionSlot.ready() and channel < len(labels): ref_label = labels[channel] predictsrc = LazyflowSource(predictionSlot) predictLayer = AlphaModulatedLayer( predictsrc, tintColor=ref_label.pmapColor(), range=(0.0, 1.0), normalize=(0.0, 1.0)) predictLayer.opacity = 0.25 predictLayer.visible = self.labelingDrawerUi.liveUpdateButton.isChecked( ) predictLayer.visibleChanged.connect( self.updateShowPredictionCheckbox) def setLayerColor(c, predictLayer_=predictLayer, initializing=False): if not initializing and predictLayer_ not in self.layerstack: # This layer has been removed from the layerstack already. # Don't touch it. return predictLayer_.tintColor = c def setPredLayerName(n, predictLayer_=predictLayer, initializing=False): if not initializing and predictLayer_ not in self.layerstack: # This layer has been removed from the layerstack already. # Don't touch it. return newName = "Prediction for %s" % n predictLayer_.name = newName setPredLayerName(ref_label.name, initializing=True) ref_label.pmapColorChanged.connect(setLayerColor) ref_label.nameChanged.connect(setPredLayerName) layers.append(predictLayer) # Add the raw data last (on the bottom) inputDataSlot = self.topLevelOperatorView.InputImages if inputDataSlot.ready(): inputLayer = self.createStandardLayerFromSlot(inputDataSlot) inputLayer.name = "Input Data" inputLayer.visible = True inputLayer.opacity = 1.0 # the flag window_leveling is used to determine if the contrast # of the layer is adjustable if isinstance(inputLayer, GrayscaleLayer): inputLayer.window_leveling = True else: inputLayer.window_leveling = False def toggleTopToBottom(): index = self.layerstack.layerIndex(inputLayer) self.layerstack.selectRow(index) if index == 0: self.layerstack.moveSelectedToBottom() else: self.layerstack.moveSelectedToTop() inputLayer.shortcutRegistration = ("i", ActionInfo( "Prediction Layers", "Bring Input To Top/Bottom", "Bring Input To Top/Bottom", toggleTopToBottom, self.viewerControlWidget(), inputLayer)) layers.append(inputLayer) # The thresholding button can only be used if the data is displayed as grayscale. if inputLayer.window_leveling: self.labelingDrawerUi.thresToolButton.show() else: self.labelingDrawerUi.thresToolButton.hide() self.handleLabelSelectionChange() return layers def toggleInteractive(self, checked): logger.debug("toggling interactive mode to '%r'" % checked) if checked == True: if not self.topLevelOperatorView.FeatureImages.ready() \ or self.topLevelOperatorView.FeatureImages.meta.shape==None: self.labelingDrawerUi.liveUpdateButton.setChecked(False) self.labelingDrawerUi.suggestFeaturesButton.setEnabled(False) mexBox = QMessageBox() mexBox.setText("There are no features selected ") mexBox.exec_() return # If we're changing modes, enable/disable our controls and other applets accordingly if self.interactiveModeActive != checked: if checked: self.labelingDrawerUi.suggestFeaturesButton.setEnabled(False) self.labelingDrawerUi.labelListView.allowDelete = False self.labelingDrawerUi.AddLabelButton.setEnabled(False) else: num_label_classes = self._labelControlUi.labelListModel.rowCount( ) self.labelingDrawerUi.labelListView.allowDelete = ( num_label_classes > self.minLabelNumber) self.labelingDrawerUi.AddLabelButton.setEnabled( (num_label_classes < self.maxLabelNumber)) self.labelingDrawerUi.suggestFeaturesButton.setEnabled(True) self.interactiveModeActive = checked self.topLevelOperatorView.FreezePredictions.setValue(not checked) self.labelingDrawerUi.liveUpdateButton.setChecked(checked) #self.labelingDrawerUi.suggestFeaturesButton.setEnabled(checked) # Auto-set the "show predictions" state according to what the user just clicked. if checked: self._viewerControlUi.checkShowPredictions.setChecked(True) self.handleShowPredictionsClicked() # Notify the workflow that some applets may have changed state now. # (For example, the downstream pixel classification applet can # be used now that there are features selected) self.parentApplet.appletStateUpdateRequested.emit() @pyqtSlot() def handleShowPredictionsClicked(self): checked = self._viewerControlUi.checkShowPredictions.isChecked() for layer in self.layerstack: if "Prediction" in layer.name: layer.visible = checked @pyqtSlot() def handleShowSegmentationClicked(self): checked = self._viewerControlUi.checkShowSegmentation.isChecked() for layer in self.layerstack: if "Segmentation" in layer.name: layer.visible = checked @pyqtSlot() def updateShowPredictionCheckbox(self): predictLayerCount = 0 visibleCount = 0 for layer in self.layerstack: if "Prediction" in layer.name: predictLayerCount += 1 if layer.visible: visibleCount += 1 if visibleCount == 0: self._viewerControlUi.checkShowPredictions.setCheckState( Qt.Unchecked) elif predictLayerCount == visibleCount: self._viewerControlUi.checkShowPredictions.setCheckState( Qt.Checked) else: self._viewerControlUi.checkShowPredictions.setCheckState( Qt.PartiallyChecked) @pyqtSlot() def updateShowSegmentationCheckbox(self): segLayerCount = 0 visibleCount = 0 for layer in self.layerstack: if "Segmentation" in layer.name: segLayerCount += 1 if layer.visible: visibleCount += 1 if visibleCount == 0: self._viewerControlUi.checkShowSegmentation.setCheckState( Qt.Unchecked) elif segLayerCount == visibleCount: self._viewerControlUi.checkShowSegmentation.setCheckState( Qt.Checked) else: self._viewerControlUi.checkShowSegmentation.setCheckState( Qt.PartiallyChecked) @pyqtSlot() @threadRouted def handleLabelSelectionChange(self): enabled = False if self.topLevelOperatorView.LabelNames.ready(): enabled = True enabled &= len(self.topLevelOperatorView.LabelNames.value) >= 2 enabled &= numpy.all( numpy.asarray(self.topLevelOperatorView.CachedFeatureImages. meta.shape) > 0) # FIXME: also check that each label has scribbles? if not enabled: self.labelingDrawerUi.liveUpdateButton.setChecked(False) self._viewerControlUi.checkShowPredictions.setChecked(False) self._viewerControlUi.checkShowSegmentation.setChecked(False) self.handleShowPredictionsClicked() self.handleShowSegmentationClicked() self.labelingDrawerUi.liveUpdateButton.setEnabled(enabled) self.labelingDrawerUi.suggestFeaturesButton.setEnabled(enabled) self._viewerControlUi.checkShowPredictions.setEnabled(enabled) self._viewerControlUi.checkShowSegmentation.setEnabled(enabled) def _getNext(self, slot, parentFun, transform=None): numLabels = self.labelListData.rowCount() value = slot.value if numLabels < len(value): result = value[numLabels] if transform is not None: result = transform(result) return result else: return parentFun() def _onLabelChanged(self, parentFun, mapf, slot): parentFun() new = map(mapf, self.labelListData) old = slot.value slot.setValue(_listReplace(old, new)) def _onLabelRemoved(self, parent, start, end): # Call the base class to update the operator. super(PixelClassificationGui, self)._onLabelRemoved(parent, start, end) # Keep colors in sync with names # (If we deleted a name, delete its corresponding colors, too.) op = self.topLevelOperatorView if len(op.PmapColors.value) > len(op.LabelNames.value): for slot in (op.LabelColors, op.PmapColors): value = slot.value value.pop(start) # Force dirty propagation even though the list id is unchanged. slot.setValue(value, check_changed=False) def getNextLabelName(self): return self._getNext( self.topLevelOperatorView.LabelNames, super(PixelClassificationGui, self).getNextLabelName) def getNextLabelColor(self): return self._getNext( self.topLevelOperatorView.LabelColors, super(PixelClassificationGui, self).getNextLabelColor, lambda x: QColor(*x)) def getNextPmapColor(self): return self._getNext( self.topLevelOperatorView.PmapColors, super(PixelClassificationGui, self).getNextPmapColor, lambda x: QColor(*x)) def onLabelNameChanged(self): self._onLabelChanged( super(PixelClassificationGui, self).onLabelNameChanged, lambda l: l.name, self.topLevelOperatorView.LabelNames) def onLabelColorChanged(self): self._onLabelChanged( super(PixelClassificationGui, self).onLabelColorChanged, lambda l: (l.brushColor().red(), l.brushColor().green(), l.brushColor().blue( )), self.topLevelOperatorView.LabelColors) def onPmapColorChanged(self): self._onLabelChanged( super(PixelClassificationGui, self).onPmapColorChanged, lambda l: (l.pmapColor().red(), l.pmapColor().green(), l.pmapColor().blue()), self.topLevelOperatorView.PmapColors) def _update_rendering(self): if not self.render: return shape = self.topLevelOperatorView.InputImages.meta.shape[1:4] if len(shape) != 5: #this might be a 2D image, no need for updating any 3D stuff return time = self.editor.posModel.slicingPos5D[0] if not self._renderMgr.ready: self._renderMgr.setup(shape) layernames = set(layer.name for layer in self.layerstack) self._renderedLayers = dict( (k, v) for k, v in self._renderedLayers.iteritems() if k in layernames) newvolume = numpy.zeros(shape, dtype=numpy.uint8) for layer in self.layerstack: try: label = self._renderedLayers[layer.name] except KeyError: continue for ds in layer.datasources: vol = ds.dataSlot.value[time, ..., 0] indices = numpy.where(vol != 0) newvolume[indices] = label self._renderMgr.volume = newvolume self._update_colors() self._renderMgr.update() def _update_colors(self): for layer in self.layerstack: try: label = self._renderedLayers[layer.name] except KeyError: continue color = layer.tintColor color = (color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0) self._renderMgr.setColor(label, color)
class PixelClassificationGui(LabelingGui): ########################################### ### AppletGuiInterface Concrete Methods ### ########################################### def centralWidget( self ): return self def stopAndCleanUp(self): for fn in self.__cleanup_fns: fn() # Base class super(PixelClassificationGui, self).stopAndCleanUp() def viewerControlWidget(self): return self._viewerControlUi def menus( self ): menus = super( PixelClassificationGui, self ).menus() advanced_menu = QMenu("Advanced", parent=self) def handleClassifierAction(): dlg = ClassifierSelectionDlg(self.topLevelOperatorView, parent=self) dlg.exec_() classifier_action = advanced_menu.addAction("Classifier...") classifier_action.triggered.connect( handleClassifierAction ) def showVarImpDlg(): varImpDlg = VariableImportanceDialog(self.topLevelOperatorView.Classifier.value.named_importances, parent=self) varImpDlg.exec_() advanced_menu.addAction("Variable Importance Table").triggered.connect(showVarImpDlg) def handleImportLabelsAction(): # Find the directory of the most recently opened image file mostRecentImageFile = PreferencesManager().get( 'DataSelection', 'recent image' ) if mostRecentImageFile is not None: defaultDirectory = os.path.split(mostRecentImageFile)[0] else: defaultDirectory = os.path.expanduser('~') fileNames = DataSelectionGui.getImageFileNamesToOpen(self, defaultDirectory) fileNames = map(str, fileNames) # For now, we require a single hdf5 file if len(fileNames) > 1: QMessageBox.critical(self, "Too many files", "Labels must be contained in a single hdf5 volume.") return if len(fileNames) == 0: # user cancelled return file_path = fileNames[0] internal_paths = DataSelectionGui.getPossibleInternalPaths(file_path) if len(internal_paths) == 0: QMessageBox.critical(self, "No volumes in file", "Couldn't find a suitable dataset in your hdf5 file.") return if len(internal_paths) == 1: internal_path = internal_paths[0] else: dlg = H5VolumeSelectionDlg(internal_paths, self) if dlg.exec_() == QDialog.Rejected: return selected_index = dlg.combo.currentIndex() internal_path = str(internal_paths[selected_index]) path_components = PathComponents(file_path) path_components.internalPath = str(internal_path) try: top_op = self.topLevelOperatorView opReader = OpInputDataReader(parent=top_op.parent) opReader.FilePath.setValue( path_components.totalPath() ) # Reorder the axes op5 = OpReorderAxes(parent=top_op.parent) op5.AxisOrder.setValue( top_op.LabelInputs.meta.getAxisKeys() ) op5.Input.connect( opReader.Output ) # Finally, import the labels top_op.importLabels( top_op.current_view_index(), op5.Output ) finally: op5.cleanUp() opReader.cleanUp() def print_label_blocks(sorted_axis): sorted_column = self.topLevelOperatorView.InputImages.meta.getAxisKeys().index(sorted_axis) input_shape = self.topLevelOperatorView.InputImages.meta.shape label_block_slicings = self.topLevelOperatorView.NonzeroLabelBlocks.value sorted_block_slicings = sorted(label_block_slicings, key=lambda s: s[sorted_column]) for slicing in sorted_block_slicings: # Omit channel order = "".join( self.topLevelOperatorView.InputImages.meta.getAxisKeys() ) line = order[:-1].upper() + ": " line += slicing_to_string( slicing[:-1], input_shape ) print line labels_submenu = QMenu("Labels") self.labels_submenu = labels_submenu # Must retain this reference or else it gets auto-deleted. import_labels_action = labels_submenu.addAction("Import Labels...") import_labels_action.triggered.connect( handleImportLabelsAction ) self.print_labels_submenu = QMenu("Print Label Blocks") labels_submenu.addMenu(self.print_labels_submenu) for axis in self.topLevelOperatorView.InputImages.meta.getAxisKeys()[:-1]: self.print_labels_submenu\ .addAction("Sort by {}".format( axis.upper() ))\ .triggered.connect( partial(print_label_blocks, axis) ) advanced_menu.addMenu(labels_submenu) if ilastik_config.getboolean('ilastik', 'debug'): def showBookmarksWindow(): self._bookmarks_window.show() advanced_menu.addAction("Bookmarks...").triggered.connect(showBookmarksWindow) menus += [advanced_menu] return menus ########################################### ########################################### def __init__(self, parentApplet, topLevelOperatorView, labelingDrawerUiPath=None ): self.parentApplet = parentApplet # Tell our base class which slots to monitor labelSlots = LabelingGui.LabelingSlots() labelSlots.labelInput = topLevelOperatorView.LabelInputs labelSlots.labelOutput = topLevelOperatorView.LabelImages labelSlots.labelEraserValue = topLevelOperatorView.opLabelPipeline.opLabelArray.eraser labelSlots.labelDelete = topLevelOperatorView.opLabelPipeline.DeleteLabel labelSlots.labelNames = topLevelOperatorView.LabelNames self.__cleanup_fns = [] # We provide our own UI file (which adds an extra control for interactive mode) if labelingDrawerUiPath is None: labelingDrawerUiPath = os.path.split(__file__)[0] + '/labelingDrawer.ui' # Base class init super(PixelClassificationGui, self).__init__( parentApplet, labelSlots, topLevelOperatorView, labelingDrawerUiPath ) self.topLevelOperatorView = topLevelOperatorView self.interactiveModeActive = False # Immediately update our interactive state self.toggleInteractive( not self.topLevelOperatorView.FreezePredictions.value ) self._currentlySavingPredictions = False self.labelingDrawerUi.labelListView.support_merges = True self.labelingDrawerUi.liveUpdateButton.setEnabled(False) self.labelingDrawerUi.liveUpdateButton.setIcon( QIcon(ilastikIcons.Play) ) self.labelingDrawerUi.liveUpdateButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.labelingDrawerUi.liveUpdateButton.toggled.connect( self.toggleInteractive ) self.initFeatSelDlg() self.labelingDrawerUi.suggestFeaturesButton.clicked.connect(self.show_feature_selection_dialog) self.featSelDlg.accepted.connect(self.update_features_from_dialog) self.labelingDrawerUi.suggestFeaturesButton.setEnabled(False) self.topLevelOperatorView.LabelNames.notifyDirty( bind(self.handleLabelSelectionChange) ) self.__cleanup_fns.append( partial( self.topLevelOperatorView.LabelNames.unregisterDirty, bind(self.handleLabelSelectionChange) ) ) self._initShortcuts() self._bookmarks_window = BookmarksWindow(self, self.topLevelOperatorView) # FIXME: We MUST NOT enable the render manager by default, # since it will drastically slow down the app for large volumes. # For now, we leave it off by default. # To re-enable rendering, we need to allow the user to render a segmentation # and then initialize the render manager on-the-fly. # (We might want to warn the user if her volume is not small.) self.render = False self._renderMgr = None self._renderedLayers = {} # (layer name, label number) # Always off for now (see note above) if self.render: try: self._renderMgr = RenderingManager( self.editor.view3d ) except: self.render = False # toggle interactive mode according to freezePredictions.value self.toggleInteractive(not self.topLevelOperatorView.FreezePredictions.value) def FreezePredDirty(): self.toggleInteractive(not self.topLevelOperatorView.FreezePredictions.value) # listen to freezePrediction changes self.topLevelOperatorView.FreezePredictions.notifyDirty( bind(FreezePredDirty) ) self.__cleanup_fns.append( partial( self.topLevelOperatorView.FreezePredictions.unregisterDirty, bind(FreezePredDirty) ) ) def initFeatSelDlg(self): if self.topLevelOperatorView.name=="OpPixelClassification": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplet.topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name=="OpPixelClassification0": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[0].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name=="OpPixelClassification1": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[1].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name=="OpPixelClassification2": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[2].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name=="OpPixelClassification3": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[3].topLevelOperator.innerOperators[0] else: raise NotImplementedError self.featSelDlg = FeatureSelectionDialog(thisOpFeatureSelection, self.topLevelOperatorView) def show_feature_selection_dialog(self): self.featSelDlg.exec_() def update_features_from_dialog(self): if self.topLevelOperatorView.name=="OpPixelClassification": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplet.topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name=="OpPixelClassification0": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[0].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name=="OpPixelClassification1": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[1].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name=="OpPixelClassification2": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[2].topLevelOperator.innerOperators[0] elif self.topLevelOperatorView.name=="OpPixelClassification3": thisOpFeatureSelection = self.topLevelOperatorView.parent.featureSelectionApplets[3].topLevelOperator.innerOperators[0] else: raise NotImplementedError thisOpFeatureSelection.SelectionMatrix.setValue(self.featSelDlg.selected_features_matrix) thisOpFeatureSelection.SelectionMatrix.setDirty() thisOpFeatureSelection.setupOutputs() def initViewerControlUi(self): localDir = os.path.split(__file__)[0] self._viewerControlUi = uic.loadUi( os.path.join( localDir, "viewerControls.ui" ) ) # Connect checkboxes def nextCheckState(checkbox): checkbox.setChecked( not checkbox.isChecked() ) self._viewerControlUi.checkShowPredictions.nextCheckState = partial(nextCheckState, self._viewerControlUi.checkShowPredictions) self._viewerControlUi.checkShowSegmentation.nextCheckState = partial(nextCheckState, self._viewerControlUi.checkShowSegmentation) self._viewerControlUi.checkShowPredictions.clicked.connect( self.handleShowPredictionsClicked ) self._viewerControlUi.checkShowSegmentation.clicked.connect( self.handleShowSegmentationClicked ) # The editor's layerstack is in charge of which layer movement buttons are enabled model = self.editor.layerStack self._viewerControlUi.viewerControls.setupConnections(model) def _initShortcuts(self): mgr = ShortcutManager() ActionInfo = ShortcutManager.ActionInfo shortcutGroupName = "Predictions" mgr.register( "p", ActionInfo( shortcutGroupName, "Toggle Prediction", "Toggle Prediction Layer Visibility", self._viewerControlUi.checkShowPredictions.click, self._viewerControlUi.checkShowPredictions, self._viewerControlUi.checkShowPredictions ) ) mgr.register( "s", ActionInfo( shortcutGroupName, "Toggle Segmentaton", "Toggle Segmentaton Layer Visibility", self._viewerControlUi.checkShowSegmentation.click, self._viewerControlUi.checkShowSegmentation, self._viewerControlUi.checkShowSegmentation ) ) mgr.register( "l", ActionInfo( shortcutGroupName, "Live Prediction", "Toggle Live Prediction Mode", self.labelingDrawerUi.liveUpdateButton.toggle, self.labelingDrawerUi.liveUpdateButton, self.labelingDrawerUi.liveUpdateButton ) ) def _setup_contexts(self, layer): def callback(pos, clayer=layer): name = clayer.name if name in self._renderedLayers: label = self._renderedLayers.pop(name) self._renderMgr.removeObject(label) self._update_rendering() else: label = self._renderMgr.addObject() self._renderedLayers[clayer.name] = label self._update_rendering() if self.render: layer.contexts.append( QAction('Toggle 3D rendering', None, triggered=callback) ) def setupLayers(self): """ Called by our base class when one of our data slots has changed. This function creates a layer for each slot we want displayed in the volume editor. """ # Base class provides the label layer. layers = super(PixelClassificationGui, self).setupLayers() ActionInfo = ShortcutManager.ActionInfo if ilastik_config.getboolean('ilastik', 'debug'): # Add the label projection layer. labelProjectionSlot = self.topLevelOperatorView.opLabelPipeline.opLabelArray.Projection2D if labelProjectionSlot.ready(): projectionSrc = LazyflowSource(labelProjectionSlot) try: # This colortable requires matplotlib from volumina.colortables import jet projectionLayer = ColortableLayer( projectionSrc, colorTable=[QColor(0,0,0,128).rgba()]+jet(N=255), normalize=(0.0, 1.0) ) except (ImportError, RuntimeError): pass else: projectionLayer.name = "Label Projection" projectionLayer.visible = False projectionLayer.opacity = 1.0 layers.append(projectionLayer) # Show the mask over everything except labels maskSlot = self.topLevelOperatorView.PredictionMasks if maskSlot.ready(): maskLayer = self._create_binary_mask_layer_from_slot( maskSlot ) maskLayer.name = "Mask" maskLayer.visible = True maskLayer.opacity = 1.0 layers.append( maskLayer ) # Add the uncertainty estimate layer uncertaintySlot = self.topLevelOperatorView.UncertaintyEstimate if uncertaintySlot.ready(): uncertaintySrc = LazyflowSource(uncertaintySlot) uncertaintyLayer = AlphaModulatedLayer( uncertaintySrc, tintColor=QColor( Qt.cyan ), range=(0.0, 1.0), normalize=(0.0, 1.0) ) uncertaintyLayer.name = "Uncertainty" uncertaintyLayer.visible = False uncertaintyLayer.opacity = 1.0 uncertaintyLayer.shortcutRegistration = ( "u", ActionInfo( "Prediction Layers", "Uncertainty", "Show/Hide Uncertainty", uncertaintyLayer.toggleVisible, self.viewerControlWidget(), uncertaintyLayer ) ) layers.append(uncertaintyLayer) labels = self.labelListData # Add each of the segmentations for channel, segmentationSlot in enumerate(self.topLevelOperatorView.SegmentationChannels): if segmentationSlot.ready() and channel < len(labels): ref_label = labels[channel] segsrc = LazyflowSource(segmentationSlot) segLayer = AlphaModulatedLayer( segsrc, tintColor=ref_label.pmapColor(), range=(0.0, 1.0), normalize=(0.0, 1.0) ) segLayer.opacity = 1 segLayer.visible = False #self.labelingDrawerUi.liveUpdateButton.isChecked() segLayer.visibleChanged.connect(self.updateShowSegmentationCheckbox) def setLayerColor(c, segLayer_=segLayer, initializing=False): if not initializing and segLayer_ not in self.layerstack: # This layer has been removed from the layerstack already. # Don't touch it. return segLayer_.tintColor = c self._update_rendering() def setSegLayerName(n, segLayer_=segLayer, initializing=False): if not initializing and segLayer_ not in self.layerstack: # This layer has been removed from the layerstack already. # Don't touch it. return oldname = segLayer_.name newName = "Segmentation (%s)" % n segLayer_.name = newName if not self.render: return if oldname in self._renderedLayers: label = self._renderedLayers.pop(oldname) self._renderedLayers[newName] = label setSegLayerName(ref_label.name, initializing=True) ref_label.pmapColorChanged.connect(setLayerColor) ref_label.nameChanged.connect(setSegLayerName) #check if layer is 3d before adding the "Toggle 3D" option #this check is done this way to match the VolumeRenderer, in #case different 3d-axistags should be rendered like t-x-y #_axiskeys = segmentationSlot.meta.getAxisKeys() if len(segmentationSlot.meta.shape) == 4: #the Renderer will cut out the last shape-dimension, so #we're checking for 4 dimensions self._setup_contexts(segLayer) layers.append(segLayer) # Add each of the predictions for channel, predictionSlot in enumerate(self.topLevelOperatorView.PredictionProbabilityChannels): if predictionSlot.ready() and channel < len(labels): ref_label = labels[channel] predictsrc = LazyflowSource(predictionSlot) predictLayer = AlphaModulatedLayer( predictsrc, tintColor=ref_label.pmapColor(), range=(0.0, 1.0), normalize=(0.0, 1.0) ) predictLayer.opacity = 0.25 predictLayer.visible = self.labelingDrawerUi.liveUpdateButton.isChecked() predictLayer.visibleChanged.connect(self.updateShowPredictionCheckbox) def setLayerColor(c, predictLayer_=predictLayer, initializing=False): if not initializing and predictLayer_ not in self.layerstack: # This layer has been removed from the layerstack already. # Don't touch it. return predictLayer_.tintColor = c def setPredLayerName(n, predictLayer_=predictLayer, initializing=False): if not initializing and predictLayer_ not in self.layerstack: # This layer has been removed from the layerstack already. # Don't touch it. return newName = "Prediction for %s" % n predictLayer_.name = newName setPredLayerName(ref_label.name, initializing=True) ref_label.pmapColorChanged.connect(setLayerColor) ref_label.nameChanged.connect(setPredLayerName) layers.append(predictLayer) # Add the raw data last (on the bottom) inputDataSlot = self.topLevelOperatorView.InputImages if inputDataSlot.ready(): inputLayer = self.createStandardLayerFromSlot( inputDataSlot ) inputLayer.name = "Input Data" inputLayer.visible = True inputLayer.opacity = 1.0 # the flag window_leveling is used to determine if the contrast # of the layer is adjustable if isinstance( inputLayer, GrayscaleLayer ): inputLayer.window_leveling = True else: inputLayer.window_leveling = False def toggleTopToBottom(): index = self.layerstack.layerIndex( inputLayer ) self.layerstack.selectRow( index ) if index == 0: self.layerstack.moveSelectedToBottom() else: self.layerstack.moveSelectedToTop() inputLayer.shortcutRegistration = ( "i", ActionInfo( "Prediction Layers", "Bring Input To Top/Bottom", "Bring Input To Top/Bottom", toggleTopToBottom, self.viewerControlWidget(), inputLayer ) ) layers.append(inputLayer) # The thresholding button can only be used if the data is displayed as grayscale. if inputLayer.window_leveling: self.labelingDrawerUi.thresToolButton.show() else: self.labelingDrawerUi.thresToolButton.hide() self.handleLabelSelectionChange() return layers def toggleInteractive(self, checked): logger.debug("toggling interactive mode to '%r'" % checked) if checked==True: if not self.topLevelOperatorView.FeatureImages.ready() \ or self.topLevelOperatorView.FeatureImages.meta.shape==None: self.labelingDrawerUi.liveUpdateButton.setChecked(False) self.labelingDrawerUi.suggestFeaturesButton.setEnabled(False) mexBox=QMessageBox() mexBox.setText("There are no features selected ") mexBox.exec_() return # If we're changing modes, enable/disable our controls and other applets accordingly if self.interactiveModeActive != checked: if checked: self.labelingDrawerUi.suggestFeaturesButton.setEnabled(False) self.labelingDrawerUi.labelListView.allowDelete = False self.labelingDrawerUi.AddLabelButton.setEnabled( False ) else: num_label_classes = self._labelControlUi.labelListModel.rowCount() self.labelingDrawerUi.labelListView.allowDelete = ( num_label_classes > self.minLabelNumber ) self.labelingDrawerUi.AddLabelButton.setEnabled( ( num_label_classes < self.maxLabelNumber ) ) self.labelingDrawerUi.suggestFeaturesButton.setEnabled(True) self.interactiveModeActive = checked self.topLevelOperatorView.FreezePredictions.setValue( not checked ) self.labelingDrawerUi.liveUpdateButton.setChecked(checked) #self.labelingDrawerUi.suggestFeaturesButton.setEnabled(checked) # Auto-set the "show predictions" state according to what the user just clicked. if checked: self._viewerControlUi.checkShowPredictions.setChecked( True ) self.handleShowPredictionsClicked() # Notify the workflow that some applets may have changed state now. # (For example, the downstream pixel classification applet can # be used now that there are features selected) self.parentApplet.appletStateUpdateRequested.emit() @pyqtSlot() def handleShowPredictionsClicked(self): checked = self._viewerControlUi.checkShowPredictions.isChecked() for layer in self.layerstack: if "Prediction" in layer.name: layer.visible = checked @pyqtSlot() def handleShowSegmentationClicked(self): checked = self._viewerControlUi.checkShowSegmentation.isChecked() for layer in self.layerstack: if "Segmentation" in layer.name: layer.visible = checked @pyqtSlot() def updateShowPredictionCheckbox(self): predictLayerCount = 0 visibleCount = 0 for layer in self.layerstack: if "Prediction" in layer.name: predictLayerCount += 1 if layer.visible: visibleCount += 1 if visibleCount == 0: self._viewerControlUi.checkShowPredictions.setCheckState(Qt.Unchecked) elif predictLayerCount == visibleCount: self._viewerControlUi.checkShowPredictions.setCheckState(Qt.Checked) else: self._viewerControlUi.checkShowPredictions.setCheckState(Qt.PartiallyChecked) @pyqtSlot() def updateShowSegmentationCheckbox(self): segLayerCount = 0 visibleCount = 0 for layer in self.layerstack: if "Segmentation" in layer.name: segLayerCount += 1 if layer.visible: visibleCount += 1 if visibleCount == 0: self._viewerControlUi.checkShowSegmentation.setCheckState(Qt.Unchecked) elif segLayerCount == visibleCount: self._viewerControlUi.checkShowSegmentation.setCheckState(Qt.Checked) else: self._viewerControlUi.checkShowSegmentation.setCheckState(Qt.PartiallyChecked) @pyqtSlot() @threadRouted def handleLabelSelectionChange(self): enabled = False if self.topLevelOperatorView.LabelNames.ready(): enabled = True enabled &= len(self.topLevelOperatorView.LabelNames.value) >= 2 enabled &= numpy.all(numpy.asarray(self.topLevelOperatorView.CachedFeatureImages.meta.shape) > 0) # FIXME: also check that each label has scribbles? if not enabled: self.labelingDrawerUi.liveUpdateButton.setChecked(False) self._viewerControlUi.checkShowPredictions.setChecked(False) self._viewerControlUi.checkShowSegmentation.setChecked(False) self.handleShowPredictionsClicked() self.handleShowSegmentationClicked() self.labelingDrawerUi.liveUpdateButton.setEnabled(enabled) self.labelingDrawerUi.suggestFeaturesButton.setEnabled(enabled) self._viewerControlUi.checkShowPredictions.setEnabled(enabled) self._viewerControlUi.checkShowSegmentation.setEnabled(enabled) def _getNext(self, slot, parentFun, transform=None): numLabels = self.labelListData.rowCount() value = slot.value if numLabels < len(value): result = value[numLabels] if transform is not None: result = transform(result) return result else: return parentFun() def _onLabelChanged(self, parentFun, mapf, slot): parentFun() new = map(mapf, self.labelListData) old = slot.value slot.setValue(_listReplace(old, new)) def _onLabelRemoved(self, parent, start, end): # Call the base class to update the operator. super(PixelClassificationGui, self)._onLabelRemoved(parent, start, end) # Keep colors in sync with names # (If we deleted a name, delete its corresponding colors, too.) op = self.topLevelOperatorView if len(op.PmapColors.value) > len(op.LabelNames.value): for slot in (op.LabelColors, op.PmapColors): value = slot.value value.pop(start) # Force dirty propagation even though the list id is unchanged. slot.setValue(value, check_changed=False) def getNextLabelName(self): return self._getNext(self.topLevelOperatorView.LabelNames, super(PixelClassificationGui, self).getNextLabelName) def getNextLabelColor(self): return self._getNext( self.topLevelOperatorView.LabelColors, super(PixelClassificationGui, self).getNextLabelColor, lambda x: QColor(*x) ) def getNextPmapColor(self): return self._getNext( self.topLevelOperatorView.PmapColors, super(PixelClassificationGui, self).getNextPmapColor, lambda x: QColor(*x) ) def onLabelNameChanged(self): self._onLabelChanged(super(PixelClassificationGui, self).onLabelNameChanged, lambda l: l.name, self.topLevelOperatorView.LabelNames) def onLabelColorChanged(self): self._onLabelChanged(super(PixelClassificationGui, self).onLabelColorChanged, lambda l: (l.brushColor().red(), l.brushColor().green(), l.brushColor().blue()), self.topLevelOperatorView.LabelColors) def onPmapColorChanged(self): self._onLabelChanged(super(PixelClassificationGui, self).onPmapColorChanged, lambda l: (l.pmapColor().red(), l.pmapColor().green(), l.pmapColor().blue()), self.topLevelOperatorView.PmapColors) def _update_rendering(self): if not self.render: return shape = self.topLevelOperatorView.InputImages.meta.shape[1:4] if len(shape) != 5: #this might be a 2D image, no need for updating any 3D stuff return time = self.editor.posModel.slicingPos5D[0] if not self._renderMgr.ready: self._renderMgr.setup(shape) layernames = set(layer.name for layer in self.layerstack) self._renderedLayers = dict((k, v) for k, v in self._renderedLayers.iteritems() if k in layernames) newvolume = numpy.zeros(shape, dtype=numpy.uint8) for layer in self.layerstack: try: label = self._renderedLayers[layer.name] except KeyError: continue for ds in layer.datasources: vol = ds.dataSlot.value[time, ..., 0] indices = numpy.where(vol != 0) newvolume[indices] = label self._renderMgr.volume = newvolume self._update_colors() self._renderMgr.update() def _update_colors(self): for layer in self.layerstack: try: label = self._renderedLayers[layer.name] except KeyError: continue color = layer.tintColor color = (color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0) self._renderMgr.setColor(label, color)