class CarvingGui(LabelingGui): def __init__(self, parentApplet, topLevelOperatorView, drawerUiPath=None): self.topLevelOperatorView = topLevelOperatorView self.isInitialized = ( False ) # Need this flag in carvingApplet where initialization is terminated with label selection # members self._doneSegmentationLayer = None self._showSegmentationIn3D = False # self._showUncertaintyLayer = False # end: members labelingSlots = LabelingGui.LabelingSlots() labelingSlots.labelInput = topLevelOperatorView.WriteSeeds labelingSlots.labelOutput = topLevelOperatorView.opLabelArray.Output labelingSlots.labelEraserValue = topLevelOperatorView.opLabelArray.EraserLabelValue labelingSlots.labelNames = topLevelOperatorView.LabelNames labelingSlots.labelDelete = topLevelOperatorView.opLabelArray.DeleteLabel labelingSlots.maxLabelValue = topLevelOperatorView.opLabelArray.MaxLabelValue # We provide our own UI file (which adds an extra control for interactive mode) directory = os.path.split(__file__)[0] if drawerUiPath is None: drawerUiPath = os.path.join(directory, "carvingDrawer.ui") self.dialogdirCOM = os.path.join(directory, "carvingObjectManagement.ui") self.dialogdirSAD = os.path.join(directory, "saveAsDialog.ui") # Add 3DWidget only if the data is 3D is_3d = self._is_3d() super(CarvingGui, self).__init__(parentApplet, labelingSlots, topLevelOperatorView, drawerUiPath, is_3d_widget_visible=is_3d) self.parentApplet = parentApplet self.labelingDrawerUi.currentObjectLabel.setText("<not saved yet>") # Init special base class members self.minLabelNumber = 2 self.maxLabelNumber = 2 mgr = ShortcutManager() ActionInfo = ShortcutManager.ActionInfo # set up keyboard shortcuts mgr.register( "3", ActionInfo( "Carving", "Run interactive segmentation", "Run interactive segmentation", self.labelingDrawerUi.segment.click, self.labelingDrawerUi.segment, self.labelingDrawerUi.segment, ), ) # Disable 3D view by default self.render = False if is_3d: try: self._renderMgr = RenderingManager(self.editor.view3d) self._shownObjects3D = {} self.render = True except: self.render = False # Segmentation is toggled on by default in _after_init, below. # (We can't enable it until the layers are all present.) self._showSegmentationIn3D = False self._segmentation_3d_label = None self.labelingDrawerUi.segment.clicked.connect(self.onSegmentButton) self.labelingDrawerUi.segment.setEnabled(True) self.topLevelOperatorView.Segmentation.notifyDirty( bind(self._segmentation_dirty)) self.topLevelOperatorView.HasSegmentation.notifyValueChanged( bind(self._updateGui)) ## uncertainty # self.labelingDrawerUi.pushButtonUncertaintyFG.setEnabled(False) # self.labelingDrawerUi.pushButtonUncertaintyBG.setEnabled(False) # def onUncertaintyFGButton(): # logger.debug( "uncertFG button clicked" ) # pos = self.topLevelOperatorView.getMaxUncertaintyPos(label=2) # self.editor.posModel.slicingPos = (pos[0], pos[1], pos[2]) # self.labelingDrawerUi.pushButtonUncertaintyFG.clicked.connect(onUncertaintyFGButton) # def onUncertaintyBGButton(): # logger.debug( "uncertBG button clicked" ) # pos = self.topLevelOperatorView.getMaxUncertaintyPos(label=1) # self.editor.posModel.slicingPos = (pos[0], pos[1], pos[2]) # self.labelingDrawerUi.pushButtonUncertaintyBG.clicked.connect(onUncertaintyBGButton) # def onUncertaintyCombo(value): # if value == 0: # value = "none" # self.labelingDrawerUi.pushButtonUncertaintyFG.setEnabled(False) # self.labelingDrawerUi.pushButtonUncertaintyBG.setEnabled(False) # self._showUncertaintyLayer = False # else: # if value == 1: # value = "localMargin" # elif value == 2: # value = "exchangeCount" # elif value == 3: # value = "gabow" # else: # raise RuntimeError("unhandled case '%r'" % value) # self.labelingDrawerUi.pushButtonUncertaintyFG.setEnabled(True) # self.labelingDrawerUi.pushButtonUncertaintyBG.setEnabled(True) # self._showUncertaintyLayer = True # logger.debug( "uncertainty changed to %r" % value ) # self.topLevelOperatorView.UncertaintyType.setValue(value) # self.updateAllLayers() #make sure that an added/deleted uncertainty layer is recognized # self.labelingDrawerUi.uncertaintyCombo.currentIndexChanged.connect(onUncertaintyCombo) self.labelingDrawerUi.objPrefix.setText(self.objectPrefix) self.labelingDrawerUi.objPrefix.textChanged.connect( self.setObjectPrefix) ## save self.labelingDrawerUi.save.clicked.connect(self.onSaveButton) ## clear self.labelingDrawerUi.clear.clicked.connect(self._onClearAction) ## object names self.labelingDrawerUi.namesButton.clicked.connect( self.onShowObjectNames) if hasattr(self.labelingDrawerUi, "exportAllMeshesButton"): self.labelingDrawerUi.exportAllMeshesButton.clicked.connect( self._exportAllObjectMeshes) self.labelingDrawerUi.labelListView.allowDelete = False self._labelControlUi.labelListModel.allowRemove(False) def layerIndexForName(name): return self.layerstack.findMatchingIndex(lambda x: x.name == name) def addLayerToggleShortcut(layername, shortcut): def toggle(): row = layerIndexForName(layername) self.layerstack.selectRow(row) layer = self.layerstack[row] layer.visible = not layer.visible self.viewerControlWidget().layerWidget.setFocus() mgr.register( shortcut, ActionInfo( "Carving", "Toggle layer %s" % layername, "Toggle layer %s" % layername, toggle, self.viewerControlWidget(), None, ), ) # TODO addLayerToggleShortcut("Completed segments (unicolor)", "d") addLayerToggleShortcut("Segmentation", "s") addLayerToggleShortcut("Input Data", "r") def makeColortable(): self._doneSegmentationColortable = [QColor(0, 0, 0, 0).rgba()] for i in range(254): r, g, b = numpy.random.randint(0, 255), numpy.random.randint( 0, 255), numpy.random.randint(0, 255) # ensure colors have sufficient distance to pure red and pure green while (255 - r) + g + b < 128 or r + (255 - g) + b < 128: r, g, b = numpy.random.randint( 0, 255), numpy.random.randint( 0, 255), numpy.random.randint(0, 255) self._doneSegmentationColortable.append(QColor(r, g, b).rgba()) self._doneSegmentationColortable.append(QColor(0, 255, 0).rgba()) makeColortable() self._updateGui() @property def objectPrefix(self): return self.topLevelOperatorView.ObjectPrefix.value def setObjectPrefix(self, value): self.topLevelOperatorView.ObjectPrefix.setValue(value) def _is_3d(self): tagged_shape = defaultdict(lambda: 1) tagged_shape.update( self.topLevelOperatorView.InputData.meta.getTaggedShape()) is_3d = tagged_shape["x"] > 1 and tagged_shape[ "y"] > 1 and tagged_shape["z"] > 1 return is_3d def _after_init(self): super(CarvingGui, self)._after_init() if self.render: self._toggleSegmentation3D() def _updateGui(self): self.labelingDrawerUi.save.setEnabled( self.topLevelOperatorView.dataIsStorable()) def onSegmentButton(self): logger.debug("segment button clicked") bkPriorityValue = self.labelingDrawerUi.backgroundPrioritySpin.value() self.topLevelOperatorView.BackgroundPriority.setValue(bkPriorityValue) biasValue = self.labelingDrawerUi.noBiasBelowSpin.value() self.topLevelOperatorView.NoBiasBelow.setValue(biasValue) self.topLevelOperatorView.Trigger.setDirty(slice(None)) def getObjectNames(self): return self.topLevelOperatorView.AllObjectNames[:].wait() def findNextPrefixNumber(self): names = self.getObjectNames() last = 0 for n in names: match = re.match(f"^{self.objectPrefix}(?P<suffix>\d+)", n) if match: val = int(match.group("suffix")) if val > last: last = val return last + 1 def saveAsDialog(self, name=""): """special functionality: reject names given to other objects""" namesInUse = self.getObjectNames() def generateObjectName(): return f"{self.objectPrefix}{self.findNextPrefixNumber()}" name = name or generateObjectName() dialog = uic.loadUi(self.dialogdirSAD) dialog.lineEdit.setText(name) dialog.lineEdit.selectAll() dialog.warning.setVisible(False) dialog.Ok.clicked.connect(dialog.accept) dialog.Cancel.clicked.connect(dialog.reject) dialog.isDisabled = False def validate(): name = dialog.lineEdit.text() if name in namesInUse: dialog.Ok.setEnabled(False) dialog.warning.setVisible(True) dialog.isDisabled = True elif dialog.isDisabled: dialog.Ok.setEnabled(True) dialog.warning.setVisible(False) dialog.isDisabled = False dialog.lineEdit.textChanged.connect(validate) result = dialog.exec_() if result: return str(dialog.lineEdit.text()) def onSaveButton(self): logger.info("save object as?") prevName = self.topLevelOperatorView.currentObjectName() if self.topLevelOperatorView.dataIsStorable(): prevName = "" if self.topLevelOperatorView.hasCurrentObject(): prevName = self.topLevelOperatorView.currentObjectName() if prevName == "<not saved yet>": prevName = "" name = self.saveAsDialog(name=prevName) if name is None: return namesInUse = self.getObjectNames() if name in namesInUse and name != prevName: QMessageBox.critical( self, "Save Object As", "An object with name '%s' already exists.\nPlease choose a different name." % name, ) return self.topLevelOperatorView.saveObjectAs(name) logger.info("save object as %s" % name) if prevName != name and prevName != "": self.topLevelOperatorView.deleteObject(prevName) elif prevName == name: self._renderMgr.removeObject(prevName) self._renderMgr.invalidateObject(prevName) self._shownObjects3D.pop(prevName, None) else: msgBox = QMessageBox(self) msgBox.setText("The data does not seem fit to be stored.") msgBox.setWindowTitle("Problem with Data") msgBox.setIcon(2) msgBox.exec_() logger.error("object not saved due to faulty data.") def onShowObjectNames(self): """show object names and allow user to load/delete them""" dialog = uic.loadUi(self.dialogdirCOM) names = self.getObjectNames() dialog.objectNames.addItems(sorted(names, key=_humansort_key)) def loadSelection(): selected = [ str(name.text()) for name in dialog.objectNames.selectedItems() ] dialog.close() for objectname in selected: self.topLevelOperatorView.loadObject(objectname) def deleteSelection(): items = dialog.objectNames.selectedItems() if self.confirmAndDelete([str(name.text()) for name in items]): for name in items: name.setHidden(True) dialog.close() dialog.loadButton.clicked.connect(loadSelection) dialog.deleteButton.clicked.connect(deleteSelection) dialog.cancelButton.clicked.connect(dialog.close) dialog.exec_() def confirmAndDelete(self, namelist): logger.info("confirmAndDelete: {}".format(namelist)) objectlist = "".join("\n " + str(i) for i in namelist) confirmed = QMessageBox.question( self, "Delete Object", "Do you want to delete these objects?" + objectlist, QMessageBox.Yes | QMessageBox.Cancel, defaultButton=QMessageBox.Yes, ) if confirmed == QMessageBox.Yes: for name in namelist: self.topLevelOperatorView.deleteObject(name) return True return False def labelingContextMenu(self, names, op, position5d): menu = QMenu(self) menu.setObjectName("carving_context_menu") posItem = menu.addAction("position %d %d %d" % (position5d[1], position5d[2], position5d[3])) posItem.setEnabled(False) menu.addSeparator() for name in names: submenu = QMenu(name, menu) # Load loadAction = submenu.addAction("Load %s" % name) loadAction.triggered.connect(partial(op.loadObject, name)) # Delete def onDelAction(_name): self.confirmAndDelete([_name]) if self.render and self._renderMgr.ready: self._update_rendering() delAction = submenu.addAction("Delete %s" % name) delAction.triggered.connect(partial(onDelAction, name)) if self.render: if name in self._shownObjects3D: # Remove def onRemove3D(_name): label = self._shownObjects3D.pop(_name) self._renderMgr.removeObject(label) self._update_rendering() removeAction = submenu.addAction("Remove %s from 3D view" % name) removeAction.triggered.connect(partial(onRemove3D, name)) else: # Show def onShow3D(_name): label = self._renderMgr.addObject() self._shownObjects3D[_name] = label self._update_rendering() showAction = submenu.addAction("Show 3D %s" % name) showAction.triggered.connect(partial(onShow3D, name)) # Export mesh exportAction = submenu.addAction("Export mesh for %s" % name) exportAction.triggered.connect( partial(self._onContextMenuExportMesh, name)) menu.addMenu(submenu) if names: menu.addSeparator() menu.addSeparator() if self.render: showSeg3DAction = menu.addAction("Show Editing Segmentation in 3D") showSeg3DAction.setCheckable(True) showSeg3DAction.setChecked(self._showSegmentationIn3D) showSeg3DAction.triggered.connect(self._toggleSegmentation3D) if op.dataIsStorable(): menu.addAction("Save object").triggered.connect(self.onSaveButton) menu.addAction("Browse objects").triggered.connect( self.onShowObjectNames) menu.addAction("Segment").triggered.connect(self.onSegmentButton) menu.addAction("Clear").triggered.connect(self._onClearAction) return menu def _onClearAction(self): confirm = QMessageBox.warning(self, "Really Clear?", "Clear all brushtrokes?", QMessageBox.Ok | QMessageBox.Cancel) if confirm == QMessageBox.Ok: self.topLevelOperatorView.clearCurrentLabeling() def _clearLabelListGui(self): # Remove rows until we have the right number while self._labelControlUi.labelListModel.rowCount() > 2: self._removeLastLabel() def _onContextMenuExportMesh(self, _name): """ Export a single object mesh to a user-specified filename. """ recent_dir = preferences.get("carving", "recent export mesh directory") if recent_dir is None: defaultPath = os.path.join(os.path.expanduser("~"), "{}obj".format(_name)) else: defaultPath = os.path.join(recent_dir, "{}.obj".format(_name)) filepath, _filter = QFileDialog.getSaveFileName( self, "Save meshes for object '{}'".format(_name), defaultPath, "OBJ Files (*.obj)") if not filepath: return obj_filepath = str(filepath) preferences.set("carving", "recent export mesh directory", os.path.split(obj_filepath)[0]) self._exportMeshes([_name], [obj_filepath]) def _exportAllObjectMeshes(self): """ Export all objects in the project as separate .obj files, stored to a user-specified directory. """ mst = self.topLevelOperatorView.MST.value if not list(mst.object_lut.keys()): QMessageBox.critical( self, "Can't Export", "You have no saved objets, so there are no meshes to export.") return recent_dir = preferences.get("carving", "recent export mesh directory") if recent_dir is None: defaultPath = os.path.join(os.path.expanduser("~")) else: defaultPath = os.path.join(recent_dir) export_dir = QFileDialog.getExistingDirectory( self, "Select export directory for mesh files", defaultPath) if not export_dir: return export_dir = str(export_dir) preferences.set("carving", "recent export mesh directory", export_dir) # Get the list of all object names object_names = [] obj_filepaths = [] for object_name in list(mst.object_lut.keys()): object_names.append(object_name) obj_filepaths.append( os.path.join(export_dir, "{}.obj".format(object_name))) if object_names: self._exportMeshes(object_names, obj_filepaths) def _exportMeshes(self, object_names: List[str], obj_filepaths: List[str]) -> Request: """Save objects in the mst to .obj files Args: object_names: Names of the objects in the mst obj_filepaths: One path for each object in object_names Returns: Returns the request object, used in testing """ def get_label_volume_from_mst(mst, object_name): object_supervoxels = mst.object_lut[object_name] object_lut = numpy.zeros(mst.nodeNum + 1, dtype=numpy.int32) object_lut[object_supervoxels] = 1 supervoxel_volume = mst.supervoxelUint32 object_volume = object_lut[supervoxel_volume] return object_volume mst = self.topLevelOperatorView.MST.value def exportMeshes(object_names, obj_filepaths): n_objects = len(object_names) progress_update = 100 / n_objects try: for obj, obj_path, obj_n in zip(object_names, obj_filepaths, range(n_objects)): object_volume = get_label_volume_from_mst(mst, obj) unique_ids = len(numpy.unique(object_volume)) if unique_ids <= 1: logger.info(f"No voxels found for {obj}, skipping") continue elif unique_ids > 2: logger.info( f"Supervoxel segmentation not unique for {obj}, skipping, got {unique_ids}" ) continue logger.info(f"Generating mesh for {obj}") _, mesh_data = list(labeling_to_mesh(object_volume, [1]))[0] self.parentApplet.progressSignal( (obj_n + 0.5) * progress_update) logger.info(f"Mesh generation for {obj} complete.") logger.info(f"Saving mesh for {obj} to {obj_path}") mesh_to_obj(mesh_data, obj_path, obj) self.parentApplet.progressSignal( (obj_n + 1) * progress_update) finally: self.parentApplet.busy = False self.parentApplet.progressSignal(100) self.parentApplet.appletStateUpdateRequested() self.parentApplet.busy = True self.parentApplet.progressSignal(-1) self.parentApplet.appletStateUpdateRequested() req = Request(partial(exportMeshes, object_names, obj_filepaths)) req.submit() return req def handleEditorRightClick(self, position5d, globalWindowCoordinate): names = self.topLevelOperatorView.doneObjectNamesForPosition( position5d[1:4]) op = self.topLevelOperatorView # (Subclasses may override menu) menu = self.labelingContextMenu(names, op, position5d) if menu is not None: menu.exec_(globalWindowCoordinate) def _toggleSegmentation3D(self): self._showSegmentationIn3D = not self._showSegmentationIn3D if self._showSegmentationIn3D: self._segmentation_3d_label = self._renderMgr.addObject() else: self._renderMgr.removeObject(self._segmentation_3d_label) self._segmentation_3d_label = None self._update_rendering() def _segmentation_dirty(self): if self.render: self._renderMgr.invalidateObject(CURRENT_SEGMENTATION_NAME) self._renderMgr.removeObject(CURRENT_SEGMENTATION_NAME) self._update_rendering() def _update_rendering(self): if not self.render: return op = self.topLevelOperatorView if not self._renderMgr.ready: shape = op.InputData.meta.shape[1:4] self._renderMgr.setup(op.InputData.meta.shape[1:4]) # remove nonexistent objects self._shownObjects3D = dict( (k, v) for k, v in self._shownObjects3D.items() if k in list(op.MST.value.object_lut.keys())) lut = numpy.zeros(op.MST.value.nodeNum + 1, dtype=numpy.int32) label_name_map = {} for name, label in self._shownObjects3D.items(): objectSupervoxels = op.MST.value.object_lut[name] lut[objectSupervoxels] = label label_name_map[label] = name label_name_map[name] = label if self._showSegmentationIn3D: # Add segmentation as label, which is green label_name_map[ self._segmentation_3d_label] = CURRENT_SEGMENTATION_NAME label_name_map[ CURRENT_SEGMENTATION_NAME] = self._segmentation_3d_label lut[:] = numpy.where(op.MST.value.getSuperVoxelSeg() == 2, self._segmentation_3d_label, lut) self._renderMgr.volume = lut[ op.MST.value. supervoxelUint32], label_name_map # (Advanced indexing) self._update_colors() self._renderMgr.update() def _update_colors(self): """Update colors of objects in 3D viewport""" op = self.topLevelOperatorView ctable = self._doneSegmentationLayer.colorTable for name, label in self._shownObjects3D.items(): color = QColor(ctable[op.MST.value.object_names[name]]) color = (color.red() / 255, color.green() / 255, color.blue() / 255) self._renderMgr.setColor(label, color) if self._showSegmentationIn3D and self._segmentation_3d_label is not None: # color of the foreground label from label list data labels = self.labelListData assert len(labels) == 2 fg_label = labels[1] color = fg_label.pmapColor() # 2 is the foreground index self._renderMgr.setColor( self._segmentation_3d_label, (color.red() / 255, color.green() / 255, color.blue() / 255)) 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 getNextLabelName(self): return self._getNext(self.topLevelOperatorView.LabelNames, super(CarvingGui, self).getNextLabelName) def appletDrawers(self): return [("Carving", self._labelControlUi)] def setupLayers(self): logger.debug("setupLayers") layers = [] def onButtonsEnabled(slot, roi): currObj = self.topLevelOperatorView.CurrentObjectName.value hasSeg = self.topLevelOperatorView.HasSegmentation.value self.labelingDrawerUi.currentObjectLabel.setText(currObj) self.labelingDrawerUi.save.setEnabled(hasSeg) self.topLevelOperatorView.CurrentObjectName.notifyDirty( onButtonsEnabled) self.topLevelOperatorView.HasSegmentation.notifyDirty(onButtonsEnabled) self.topLevelOperatorView.opLabelArray.NonzeroBlocks.notifyDirty( onButtonsEnabled) # Labels labellayer, labelsrc = self.createLabelLayer(direct=True) if labellayer is not None: labellayer._allowToggleVisible = False layers.append(labellayer) # Tell the editor where to draw label data self.editor.setLabelSink(labelsrc) # uncertainty # if self._showUncertaintyLayer: # uncert = self.topLevelOperatorView.Uncertainty # if uncert.ready(): # colortable = [] # for i in range(256-len(colortable)): # r,g,b,a = i,0,0,i # colortable.append(QColor(r,g,b,a).rgba()) # layer = ColortableLayer(createDataSource(uncert), colortable, direct=True) # layer.name = "Uncertainty" # layer.visible = True # layer.opacity = 0.3 # layers.append(layer) # segmentation seg = self.topLevelOperatorView.Segmentation # seg = self.topLevelOperatorView.MST.value.segmentation # temp = self._done_lut[self.MST.value.supervoxelUint32[sl[1:4]]] if seg.ready(): # source = RelabelingArraySource(seg) # source.setRelabeling(numpy.arange(256, dtype=numpy.uint8)) # assign to the object label color, 0 is transparent, 1 is background colortable = [ QColor(0, 0, 0, 0).rgba(), QColor(0, 0, 0, 0).rgba(), labellayer._colorTable[2] ] for i in range(256 - len(colortable)): r, g, b = numpy.random.randint(0, 255), numpy.random.randint( 0, 255), numpy.random.randint(0, 255) colortable.append(QColor(r, g, b).rgba()) layer = ColortableLayer(createDataSource(seg), colortable, direct=True) layer.name = "Segmentation" layer.setToolTip( "This layer displays the <i>current</i> segmentation. Simply add foreground and background " "labels, then press <i>Segment</i>.") layer.visible = True layer.opacity = 0.3 layers.append(layer) # done doneSeg = self.topLevelOperatorView.DoneSegmentation if doneSeg.ready(): # FIXME: if the user segments more than 255 objects, those with indices that divide by 255 will be shown as transparent # both here and in the _doneSegmentationColortable colortable = 254 * [QColor(230, 25, 75).rgba()] colortable.insert(0, QColor(0, 0, 0, 0).rgba()) # have to use lazyflow because it provides dirty signals layer = ColortableLayer(createDataSource(doneSeg), colortable, direct=True) layer.name = "Completed segments (unicolor)" layer.setToolTip( "In order to keep track of which objects you have already completed, this layer " "shows <b>all completed object</b> in one color (<b>blue</b>). " "The reason for only one color is that for finding out which " "objects to label next, the identity of already completed objects is unimportant " "and destracting.") layer.visible = False layer.opacity = 0.5 layers.append(layer) layer = ColortableLayer(createDataSource(doneSeg), self._doneSegmentationColortable, direct=True) layer.name = "Completed segments (one color per object)" layer.setToolTip( "<html>In order to keep track of which objects you have already completed, this layer " "shows <b>all completed object</b>, each with a random color.</html>" ) layer.visible = False layer.opacity = 0.5 layer.colortableIsRandom = True self._doneSegmentationLayer = layer layers.append(layer) # supervoxel sv = self.topLevelOperatorView.Supervoxels if sv.ready(): colortable = [] for i in range(256): r, g, b = numpy.random.randint(0, 255), numpy.random.randint( 0, 255), numpy.random.randint(0, 255) colortable.append(QColor(r, g, b).rgba()) layer = ColortableLayer(createDataSource(sv), colortable, direct=True) layer.name = "Supervoxels" layer.setToolTip( "<html>This layer shows the partitioning of the input image into <b>supervoxels</b>. The carving " "algorithm uses these tiny puzzle-piceces to piece together the segmentation of an " "object. Sometimes, supervoxels are too large and straddle two distinct objects " "(undersegmentation). In this case, it will be impossible to achieve the desired " "segmentation. This layer helps you to understand these cases.</html>" ) layer.visible = False layer.colortableIsRandom = True layer.opacity = 0.5 layers.append(layer) # Visual overlay (just for easier labeling) overlaySlot = self.topLevelOperatorView.OverlayData if overlaySlot.ready(): overlay5D = self.topLevelOperatorView.OverlayData.value layer = GrayscaleLayer(ArraySource(overlay5D), direct=True) layer.visible = True layer.name = "Overlay" layer.opacity = 1.0 # if the flag window_leveling is set the contrast # of the layer is adjustable layer.window_leveling = True self.labelingDrawerUi.thresToolButton.show() layers.append(layer) del layer inputSlot = self.topLevelOperatorView.InputData if inputSlot.ready(): layer = GrayscaleLayer(createDataSource(inputSlot), direct=True) layer.name = "Input Data" layer.setToolTip( "<html>The data originally loaded into ilastik (unprocessed).</html>" ) # layer.visible = not rawSlot.ready() layer.visible = True layer.opacity = 1.0 # Window leveling is already active on the Overlay, # but if no overlay was provided, then activate window_leveling on the raw data instead. if not overlaySlot.ready(): # if the flag window_leveling is set the contrast # of the layer is adjustable layer.window_leveling = True self.labelingDrawerUi.thresToolButton.show() layers.append(layer) del layer filteredSlot = self.topLevelOperatorView.FilteredInputData if filteredSlot.ready(): layer = GrayscaleLayer(createDataSource(filteredSlot)) layer.name = "Filtered Input" layer.visible = False layer.opacity = 1.0 layers.append(layer) return layers
class CarvingGui(LabelingGui): def __init__(self, parentApplet, topLevelOperatorView, drawerUiPath=None ): self.topLevelOperatorView = topLevelOperatorView #members self._doneSegmentationLayer = None self._showSegmentationIn3D = False #self._showUncertaintyLayer = False #end: members labelingSlots = LabelingGui.LabelingSlots() labelingSlots.labelInput = topLevelOperatorView.WriteSeeds labelingSlots.labelOutput = topLevelOperatorView.opLabelArray.Output labelingSlots.labelEraserValue = topLevelOperatorView.opLabelArray.EraserLabelValue labelingSlots.labelNames = topLevelOperatorView.LabelNames labelingSlots.labelDelete = topLevelOperatorView.opLabelArray.DeleteLabel labelingSlots.maxLabelValue = topLevelOperatorView.opLabelArray.MaxLabelValue # We provide our own UI file (which adds an extra control for interactive mode) directory = os.path.split(__file__)[0] if drawerUiPath is None: drawerUiPath = os.path.join(directory, 'carvingDrawer.ui') self.dialogdirCOM = os.path.join(directory, 'carvingObjectManagement.ui') self.dialogdirSAD = os.path.join(directory, 'saveAsDialog.ui') # Add 3DWidget only if the data is 3D is_3d = self._is_3d() super(CarvingGui, self).__init__(parentApplet, labelingSlots, topLevelOperatorView, drawerUiPath, is_3d_widget_visible=is_3d) self.labelingDrawerUi.currentObjectLabel.setText("<not saved yet>") # Init special base class members self.minLabelNumber = 2 self.maxLabelNumber = 2 mgr = ShortcutManager() ActionInfo = ShortcutManager.ActionInfo #set up keyboard shortcuts mgr.register( "3", ActionInfo( "Carving", "Run interactive segmentation", "Run interactive segmentation", self.labelingDrawerUi.segment.click, self.labelingDrawerUi.segment, self.labelingDrawerUi.segment ) ) # Disable 3D view by default self.render = False if is_3d: try: self._renderMgr = RenderingManager( self.editor.view3d ) self._shownObjects3D = {} self.render = True except: self.render = False # Segmentation is toggled on by default in _after_init, below. # (We can't enable it until the layers are all present.) self._showSegmentationIn3D = False self._segmentation_3d_label = None self.labelingDrawerUi.segment.clicked.connect(self.onSegmentButton) self.labelingDrawerUi.segment.setEnabled(True) self.topLevelOperatorView.Segmentation.notifyDirty( bind( self._segmentation_dirty ) ) self.topLevelOperatorView.HasSegmentation.notifyValueChanged( bind( self._updateGui ) ) ## uncertainty #self.labelingDrawerUi.pushButtonUncertaintyFG.setEnabled(False) #self.labelingDrawerUi.pushButtonUncertaintyBG.setEnabled(False) #def onUncertaintyFGButton(): # logger.debug( "uncertFG button clicked" ) # pos = self.topLevelOperatorView.getMaxUncertaintyPos(label=2) # self.editor.posModel.slicingPos = (pos[0], pos[1], pos[2]) #self.labelingDrawerUi.pushButtonUncertaintyFG.clicked.connect(onUncertaintyFGButton) #def onUncertaintyBGButton(): # logger.debug( "uncertBG button clicked" ) # pos = self.topLevelOperatorView.getMaxUncertaintyPos(label=1) # self.editor.posModel.slicingPos = (pos[0], pos[1], pos[2]) #self.labelingDrawerUi.pushButtonUncertaintyBG.clicked.connect(onUncertaintyBGButton) #def onUncertaintyCombo(value): # if value == 0: # value = "none" # self.labelingDrawerUi.pushButtonUncertaintyFG.setEnabled(False) # self.labelingDrawerUi.pushButtonUncertaintyBG.setEnabled(False) # self._showUncertaintyLayer = False # else: # if value == 1: # value = "localMargin" # elif value == 2: # value = "exchangeCount" # elif value == 3: # value = "gabow" # else: # raise RuntimeError("unhandled case '%r'" % value) # self.labelingDrawerUi.pushButtonUncertaintyFG.setEnabled(True) # self.labelingDrawerUi.pushButtonUncertaintyBG.setEnabled(True) # self._showUncertaintyLayer = True # logger.debug( "uncertainty changed to %r" % value ) # self.topLevelOperatorView.UncertaintyType.setValue(value) # self.updateAllLayers() #make sure that an added/deleted uncertainty layer is recognized #self.labelingDrawerUi.uncertaintyCombo.currentIndexChanged.connect(onUncertaintyCombo) ## background priority def onBackgroundPrioritySpin(value): logger.debug( "background priority changed to %f" % value ) self.topLevelOperatorView.BackgroundPriority.setValue(value) self.labelingDrawerUi.backgroundPrioritySpin.valueChanged.connect(onBackgroundPrioritySpin) def onBackgroundPriorityDirty(slot, roi): oldValue = self.labelingDrawerUi.backgroundPrioritySpin.value() newValue = self.topLevelOperatorView.BackgroundPriority.value if newValue != oldValue: self.labelingDrawerUi.backgroundPrioritySpin.setValue(newValue) self.topLevelOperatorView.BackgroundPriority.notifyDirty(onBackgroundPriorityDirty) ## bias def onNoBiasBelowDirty(slot, roi): oldValue = self.labelingDrawerUi.noBiasBelowSpin.value() newValue = self.topLevelOperatorView.NoBiasBelow.value if newValue != oldValue: self.labelingDrawerUi.noBiasBelowSpin.setValue(newValue) self.topLevelOperatorView.NoBiasBelow.notifyDirty(onNoBiasBelowDirty) def onNoBiasBelowSpin(value): logger.debug( "background priority changed to %f" % value ) self.topLevelOperatorView.NoBiasBelow.setValue(value) self.labelingDrawerUi.noBiasBelowSpin.valueChanged.connect(onNoBiasBelowSpin) ## save self.labelingDrawerUi.save.clicked.connect(self.onSaveButton) ## clear self.labelingDrawerUi.clear.clicked.connect(self._onClearAction) ## object names self.labelingDrawerUi.namesButton.clicked.connect(self.onShowObjectNames) if hasattr( self.labelingDrawerUi, 'exportAllMeshesButton' ): self.labelingDrawerUi.exportAllMeshesButton.clicked.connect(self._exportAllObjectMeshes) self.labelingDrawerUi.labelListView.allowDelete = False self._labelControlUi.labelListModel.allowRemove(False) def layerIndexForName(name): return self.layerstack.findMatchingIndex(lambda x: x.name == name) def addLayerToggleShortcut(layername, shortcut): def toggle(): row = layerIndexForName(layername) self.layerstack.selectRow(row) layer = self.layerstack[row] layer.visible = not layer.visible self.viewerControlWidget().layerWidget.setFocus() mgr.register(shortcut, ActionInfo( "Carving", "Toggle layer %s" % layername, "Toggle layer %s" % layername, toggle, self.viewerControlWidget(), None ) ) #TODO addLayerToggleShortcut("Completed segments (unicolor)", "d") addLayerToggleShortcut("Segmentation", "s") addLayerToggleShortcut("Input Data", "r") def makeColortable(): self._doneSegmentationColortable = [QColor(0,0,0,0).rgba()] for i in range(254): r,g,b = numpy.random.randint(0,255), numpy.random.randint(0,255), numpy.random.randint(0,255) # ensure colors have sufficient distance to pure red and pure green while (255 - r)+g+b<128 or r+(255-g)+b<128: r,g,b = numpy.random.randint(0,255), numpy.random.randint(0,255), numpy.random.randint(0,255) self._doneSegmentationColortable.append(QColor(r,g,b).rgba()) self._doneSegmentationColortable.append(QColor(0,255,0).rgba()) makeColortable() self._updateGui() def _is_3d(self): tagged_shape = defaultdict(lambda: 1) tagged_shape.update(self.topLevelOperatorView.InputData.meta.getTaggedShape()) is_3d = tagged_shape['x'] > 1 and tagged_shape['y'] > 1 and tagged_shape['z'] > 1 return is_3d def _after_init(self): super(CarvingGui, self)._after_init() if self.render: self._toggleSegmentation3D() def _updateGui(self): self.labelingDrawerUi.save.setEnabled( self.topLevelOperatorView.dataIsStorable() ) def onSegmentButton(self): logger.debug( "segment button clicked" ) self.topLevelOperatorView.Trigger.setDirty(slice(None)) def saveAsDialog(self, name=""): '''special functionality: reject names given to other objects''' dialog = uic.loadUi(self.dialogdirSAD) dialog.lineEdit.setText(name) dialog.warning.setVisible(False) dialog.Ok.clicked.connect(dialog.accept) dialog.Cancel.clicked.connect(dialog.reject) listOfItems = self.topLevelOperatorView.AllObjectNames[:].wait() dialog.isDisabled = False def validate(): name = dialog.lineEdit.text() if name in listOfItems: dialog.Ok.setEnabled(False) dialog.warning.setVisible(True) dialog.isDisabled = True elif dialog.isDisabled: dialog.Ok.setEnabled(True) dialog.warning.setVisible(False) dialog.isDisabled = False dialog.lineEdit.textChanged.connect(validate) result = dialog.exec_() if result: return str(dialog.lineEdit.text()) def onSaveButton(self): logger.info( "save object as?" ) if self.topLevelOperatorView.dataIsStorable(): prevName = "" if self.topLevelOperatorView.hasCurrentObject(): prevName = self.topLevelOperatorView.currentObjectName() if prevName == "<not saved yet>": prevName = "" name = self.saveAsDialog(name=prevName) if name is None: return objects = self.topLevelOperatorView.AllObjectNames[:].wait() if name in objects and name != prevName: QMessageBox.critical(self, "Save Object As", "An object with name '%s' already exists.\nPlease choose a different name." % name) return self.topLevelOperatorView.saveObjectAs(name) logger.info( "save object as %s" % name ) if prevName != name and prevName != "": self.topLevelOperatorView.deleteObject(prevName) elif prevName == name: self._renderMgr.removeObject(prevName) self._renderMgr.invalidateObject(prevName) self._shownObjects3D.pop(prevName, None) else: msgBox = QMessageBox(self) msgBox.setText("The data does not seem fit to be stored.") msgBox.setWindowTitle("Problem with Data") msgBox.setIcon(2) msgBox.exec_() logger.error( "object not saved due to faulty data." ) def onShowObjectNames(self): '''show object names and allow user to load/delete them''' dialog = uic.loadUi(self.dialogdirCOM) listOfItems = self.topLevelOperatorView.AllObjectNames[:].wait() dialog.objectNames.addItems(sorted(listOfItems)) def loadSelection(): selected = [str(name.text()) for name in dialog.objectNames.selectedItems()] dialog.close() for objectname in selected: self.topLevelOperatorView.loadObject(objectname) def deleteSelection(): items = dialog.objectNames.selectedItems() if self.confirmAndDelete([str(name.text()) for name in items]): for name in items: name.setHidden(True) dialog.close() dialog.loadButton.clicked.connect(loadSelection) dialog.deleteButton.clicked.connect(deleteSelection) dialog.cancelButton.clicked.connect(dialog.close) dialog.exec_() def confirmAndDelete(self,namelist): logger.info( "confirmAndDelete: {}".format( namelist ) ) objectlist = "".join("\n "+str(i) for i in namelist) confirmed = QMessageBox.question(self, "Delete Object", \ "Do you want to delete these objects?"+objectlist, \ QMessageBox.Yes | QMessageBox.Cancel, \ defaultButton=QMessageBox.Yes) if confirmed == QMessageBox.Yes: for name in namelist: self.topLevelOperatorView.deleteObject(name) return True return False def labelingContextMenu(self,names,op,position5d): menu = QMenu(self) menu.setObjectName("carving_context_menu") posItem = menu.addAction("position %d %d %d" % (position5d[1], position5d[2], position5d[3])) posItem.setEnabled(False) menu.addSeparator() for name in names: submenu = QMenu(name,menu) # Load loadAction = submenu.addAction("Load %s" % name) loadAction.triggered.connect( partial(op.loadObject, name) ) # Delete def onDelAction(_name): self.confirmAndDelete([_name]) if self.render and self._renderMgr.ready: self._update_rendering() delAction = submenu.addAction("Delete %s" % name) delAction.triggered.connect( partial(onDelAction, name) ) if self.render: if name in self._shownObjects3D: # Remove def onRemove3D(_name): label = self._shownObjects3D.pop(_name) self._renderMgr.removeObject(label) self._update_rendering() removeAction = submenu.addAction("Remove %s from 3D view" % name) removeAction.triggered.connect( partial(onRemove3D, name) ) else: # Show def onShow3D(_name): label = self._renderMgr.addObject() self._shownObjects3D[_name] = label self._update_rendering() showAction = submenu.addAction("Show 3D %s" % name) showAction.triggered.connect( partial(onShow3D, name ) ) # Export mesh exportAction = submenu.addAction("Export mesh for %s" % name) exportAction.triggered.connect( partial(self._onContextMenuExportMesh, name) ) menu.addMenu(submenu) if names: menu.addSeparator() menu.addSeparator() if self.render: showSeg3DAction = menu.addAction( "Show Editing Segmentation in 3D" ) showSeg3DAction.setCheckable(True) showSeg3DAction.setChecked( self._showSegmentationIn3D ) showSeg3DAction.triggered.connect( self._toggleSegmentation3D ) if op.dataIsStorable(): menu.addAction("Save object").triggered.connect( self.onSaveButton ) menu.addAction("Browse objects").triggered.connect( self.onShowObjectNames ) menu.addAction("Segment").triggered.connect( self.onSegmentButton ) menu.addAction("Clear").triggered.connect( self._onClearAction ) return menu def _onClearAction(self): confirm = QMessageBox.warning(self, "Really Clear?", "Clear all brushtrokes?", QMessageBox.Ok | QMessageBox.Cancel) if confirm == QMessageBox.Ok: self.topLevelOperatorView.clearCurrentLabeling() def _onContextMenuExportMesh(self, _name): """ Export a single object mesh to a user-specified filename. """ recent_dir = PreferencesManager().get( 'carving', 'recent export mesh directory' ) if recent_dir is None: defaultPath = os.path.join( os.path.expanduser('~'), '{}obj'.format(_name) ) else: defaultPath = os.path.join( recent_dir, '{}.obj'.format(_name) ) filepath, _filter = QFileDialog.getSaveFileName(self, "Save meshes for object '{}'".format(_name), defaultPath, "OBJ Files (*.obj)") if not filepath: return obj_filepath = str(filepath) PreferencesManager().set( 'carving', 'recent export mesh directory', os.path.split(obj_filepath)[0] ) self._exportMeshes([_name], [obj_filepath]) def _exportAllObjectMeshes(self): """ Export all objects in the project as separate .obj files, stored to a user-specified directory. """ mst = self.topLevelOperatorView.MST.value if not list(mst.object_lut.keys()): QMessageBox.critical(self, "Can't Export", "You have no saved objets, so there are no meshes to export.") return recent_dir = PreferencesManager().get( 'carving', 'recent export mesh directory' ) if recent_dir is None: defaultPath = os.path.join( os.path.expanduser('~') ) else: defaultPath = os.path.join( recent_dir ) export_dir = QFileDialog.getExistingDirectory( self, "Select export directory for mesh files", defaultPath) if not export_dir: return export_dir = str(export_dir) PreferencesManager().set( 'carving', 'recent export mesh directory', export_dir ) # Get the list of all object names object_names = [] obj_filepaths = [] for object_name in list(mst.object_lut.keys()): object_names.append( object_name ) obj_filepaths.append( os.path.join( export_dir, "{}.obj".format( object_name ) ) ) if object_names: self._exportMeshes( object_names, obj_filepaths ) def _exportMeshes(self, object_names, obj_filepaths): """ Export a mesh .obj file for each object in the object_names list to the corresponding file name from the obj_filepaths list. This function is pseudo-recursive. It works like this: 1) Pop the first name/file from the args 2) Kick off the export by launching the export mesh dlg 3) return from this function to allow the eventloop to resume while the export is running 4) When the export dlg is finished, create the mesh file 5) If there are still more items in the object_names list to process, repeat this function. """ # Pop the first object off the list object_name = object_names.pop(0) obj_filepath = obj_filepaths.pop(0) # Construct a volume with only this object. # We might be tempted to get the object directly from opCarving.DoneObjects, # but that won't be correct for overlapping objects. mst = self.topLevelOperatorView.MST.value object_supervoxels = mst.object_lut[object_name] object_lut = numpy.zeros(mst.nodeNum+1, dtype=numpy.int32) object_lut[object_supervoxels] = 1 supervoxel_volume = mst.supervoxelUint32 object_volume = object_lut[supervoxel_volume] if len(numpy.unique(object_volume)) <= 1: if object_names: self._exportMeshes(object_names, obj_filepaths) return # Run the mesh extractor window = MeshGeneratorDialog(self) def onMeshesComplete(mesh): """ Called when mesh extraction is complete. Writes the extracted mesh to an .obj file """ logger.info( "Mesh generation complete." ) # FIXME: the old comment: Mesh count can sometimes be 0 for the '<not saved yet>' object... # FIXME: is this still relevant??? ''' mesh_count = len( window.extractor.meshes ) if mesh_count > 0: assert mesh_count == 1, \ "Found {} meshes processing object '{}',"\ "(only expected 1)".format( mesh_count, object_name ) mesh = list(window.extractor.meshes.values())[0] logger.info( "Saving meshes to {}".format( obj_filepath ) ) # Use VTK to write to a temporary .vtk file tmpdir = tempfile.mkdtemp() vtkpoly_path = os.path.join(tmpdir, 'meshes.vtk') w = vtkPolyDataWriter() w.SetFileTypeToASCII() w.SetInput(mesh) w.SetFileName(vtkpoly_path) w.Write() # Now convert the file to .obj format. convertVTPtoOBJ(vtkpoly_path, obj_filepath) ''' logger.info("Saving meshes to {}".format(obj_filepath)) mesh_to_obj(mesh, obj_filepath, object_name) # Cleanup: We don't need the window anymore. window.setParent(None) # If there are still objects left to process, # start again with the remainder of the list. if object_names: self._exportMeshes(object_names, obj_filepaths) window.finished.connect( onMeshesComplete ) # Kick off the save process and exit to the event loop window.show() QTimer.singleShot(0, partial(window.run, object_volume)) def handleEditorRightClick(self, position5d, globalWindowCoordinate): names = self.topLevelOperatorView.doneObjectNamesForPosition(position5d[1:4]) op = self.topLevelOperatorView # (Subclasses may override menu) menu = self.labelingContextMenu(names,op,position5d) if menu is not None: menu.exec_(globalWindowCoordinate) def _toggleSegmentation3D(self): self._showSegmentationIn3D = not self._showSegmentationIn3D if self._showSegmentationIn3D: self._segmentation_3d_label = self._renderMgr.addObject() else: self._renderMgr.removeObject(self._segmentation_3d_label) self._segmentation_3d_label = None self._update_rendering() def _segmentation_dirty(self): if self.render: self._renderMgr.invalidateObject(CURRENT_SEGMENTATION_NAME) self._renderMgr.removeObject(CURRENT_SEGMENTATION_NAME) self._update_rendering() def _update_rendering(self): if not self.render: return op = self.topLevelOperatorView if not self._renderMgr.ready: shape = op.InputData.meta.shape[1:4] self._renderMgr.setup(op.InputData.meta.shape[1:4]) # remove nonexistent objects self._shownObjects3D = dict((k, v) for k, v in self._shownObjects3D.items() if k in list(op.MST.value.object_lut.keys())) lut = numpy.zeros(op.MST.value.nodeNum+1, dtype=numpy.int32) label_name_map = {} for name, label in self._shownObjects3D.items(): objectSupervoxels = op.MST.value.object_lut[name] lut[objectSupervoxels] = label label_name_map[label] = name label_name_map[name] = label if self._showSegmentationIn3D: # Add segmentation as label, which is green label_name_map[self._segmentation_3d_label] = CURRENT_SEGMENTATION_NAME label_name_map[CURRENT_SEGMENTATION_NAME] = self._segmentation_3d_label lut[:] = numpy.where( op.MST.value.getSuperVoxelSeg() == 2, self._segmentation_3d_label, lut ) self._renderMgr.volume = lut[op.MST.value.supervoxelUint32], label_name_map # (Advanced indexing) self._update_colors() self._renderMgr.update() def _update_colors(self): op = self.topLevelOperatorView ctable = self._doneSegmentationLayer.colorTable for name, label in self._shownObjects3D.items(): color = QColor(ctable[op.MST.value.object_names[name]]) color = (old_div(color.red(), 255.0), old_div(color.green(), 255.0), old_div(color.blue(), 255.0)) self._renderMgr.setColor(label, color) if self._showSegmentationIn3D and self._segmentation_3d_label is not None: self._renderMgr.setColor(self._segmentation_3d_label, (0.0, 1.0, 0.0)) # Green 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 getNextLabelName(self): return self._getNext(self.topLevelOperatorView.LabelNames, super(CarvingGui, self).getNextLabelName) def appletDrawers(self): return [ ("Carving", self._labelControlUi) ] def setupLayers( self ): logger.debug( "setupLayers" ) layers = [] def onButtonsEnabled(slot, roi): currObj = self.topLevelOperatorView.CurrentObjectName.value hasSeg = self.topLevelOperatorView.HasSegmentation.value self.labelingDrawerUi.currentObjectLabel.setText(currObj) self.labelingDrawerUi.save.setEnabled(hasSeg) self.topLevelOperatorView.CurrentObjectName.notifyDirty(onButtonsEnabled) self.topLevelOperatorView.HasSegmentation.notifyDirty(onButtonsEnabled) self.topLevelOperatorView.opLabelArray.NonzeroBlocks.notifyDirty(onButtonsEnabled) # Labels labellayer, labelsrc = self.createLabelLayer(direct=True) if labellayer is not None: labellayer._allowToggleVisible = False layers.append(labellayer) # Tell the editor where to draw label data self.editor.setLabelSink(labelsrc) #uncertainty #if self._showUncertaintyLayer: # uncert = self.topLevelOperatorView.Uncertainty # if uncert.ready(): # colortable = [] # for i in range(256-len(colortable)): # r,g,b,a = i,0,0,i # colortable.append(QColor(r,g,b,a).rgba()) # layer = ColortableLayer(LazyflowSource(uncert), colortable, direct=True) # layer.name = "Uncertainty" # layer.visible = True # layer.opacity = 0.3 # layers.append(layer) #segmentation seg = self.topLevelOperatorView.Segmentation #seg = self.topLevelOperatorView.MST.value.segmentation #temp = self._done_lut[self.MST.value.supervoxelUint32[sl[1:4]]] if seg.ready(): #source = RelabelingArraySource(seg) #source.setRelabeling(numpy.arange(256, dtype=numpy.uint8)) colortable = [QColor(0,0,0,0).rgba(), QColor(0,0,0,0).rgba(), QColor(0,255,0).rgba()] for i in range(256-len(colortable)): r,g,b = numpy.random.randint(0,255), numpy.random.randint(0,255), numpy.random.randint(0,255) colortable.append(QColor(r,g,b).rgba()) layer = ColortableLayer(LazyflowSource(seg), colortable, direct=True) layer.name = "Segmentation" layer.setToolTip("This layer displays the <i>current</i> segmentation. Simply add foreground and background " \ "labels, then press <i>Segment</i>.") layer.visible = True layer.opacity = 0.3 layers.append(layer) #done done = self.topLevelOperatorView.DoneObjects if done.ready(): colortable = [QColor(0,0,0,0).rgba(), QColor(0,0,255).rgba()] #have to use lazyflow because it provides dirty signals layer = ColortableLayer(LazyflowSource(done), colortable, direct=True) layer.name = "Completed segments (unicolor)" layer.setToolTip("In order to keep track of which objects you have already completed, this layer " \ "shows <b>all completed object</b> in one color (<b>blue</b>). " \ "The reason for only one color is that for finding out which " \ "objects to label next, the identity of already completed objects is unimportant " \ "and destracting.") layer.visible = False layer.opacity = 0.5 layers.append(layer) #done seg doneSeg = self.topLevelOperatorView.DoneSegmentation if doneSeg.ready(): layer = ColortableLayer(LazyflowSource(doneSeg), self._doneSegmentationColortable, direct=True) layer.name = "Completed segments (one color per object)" layer.setToolTip("<html>In order to keep track of which objects you have already completed, this layer " \ "shows <b>all completed object</b>, each with a random color.</html>") layer.visible = False layer.opacity = 0.5 self._doneSegmentationLayer = layer layers.append(layer) #supervoxel sv = self.topLevelOperatorView.Supervoxels if sv.ready(): colortable = [] for i in range(256): r,g,b = numpy.random.randint(0,255), numpy.random.randint(0,255), numpy.random.randint(0,255) colortable.append(QColor(r,g,b).rgba()) layer = ColortableLayer(LazyflowSource(sv), colortable, direct=True) layer.name = "Supervoxels" layer.setToolTip("<html>This layer shows the partitioning of the input image into <b>supervoxels</b>. The carving " \ "algorithm uses these tiny puzzle-piceces to piece together the segmentation of an " \ "object. Sometimes, supervoxels are too large and straddle two distinct objects " \ "(undersegmentation). In this case, it will be impossible to achieve the desired " \ "segmentation. This layer helps you to understand these cases.</html>") layer.visible = False layer.opacity = 1.0 layers.append(layer) # Visual overlay (just for easier labeling) overlaySlot = self.topLevelOperatorView.OverlayData if overlaySlot.ready(): overlay5D = self.topLevelOperatorView.OverlayData.value layer = GrayscaleLayer(ArraySource(overlay5D), direct=True) layer.visible = True layer.name = 'Overlay' layer.opacity = 1.0 # if the flag window_leveling is set the contrast # of the layer is adjustable layer.window_leveling = True self.labelingDrawerUi.thresToolButton.show() layers.append(layer) del layer inputSlot = self.topLevelOperatorView.InputData if inputSlot.ready(): layer = GrayscaleLayer( LazyflowSource(inputSlot), direct=True ) layer.name = "Input Data" layer.setToolTip("<html>The data originally loaded into ilastik (unprocessed).</html>") #layer.visible = not rawSlot.ready() layer.visible = True layer.opacity = 1.0 # Window leveling is already active on the Overlay, # but if no overlay was provided, then activate window_leveling on the raw data instead. if not overlaySlot.ready(): # if the flag window_leveling is set the contrast # of the layer is adjustable layer.window_leveling = True self.labelingDrawerUi.thresToolButton.show() layers.append(layer) del layer filteredSlot = self.topLevelOperatorView.FilteredInputData if filteredSlot.ready(): layer = GrayscaleLayer( LazyflowSource(filteredSlot) ) layer.name = "Filtered Input" layer.visible = False layer.opacity = 1.0 layers.append(layer) return layers