Beispiel #1
0
    def testAppletDeserialization(self):
        g = Graph()
        op = OpFake(graph=g)
        dataExportApplet = TrackingBaseDataExportApplet(
            op, "Tracking Result Export")
        dataExportSerializer = dataExportApplet.dataSerializers[0]

        with h5py.File(self.TEST_PROJECT_FILE) as testProject:
            # serialize TrackingBaseDataExportApplet's topLevelOperator
            opTrackingBaseDataExport = dataExportApplet.topLevelOperator
            opTrackingBaseDataExport.SelectedPlugin.setValue('Fiji-MaMuT')
            opTrackingBaseDataExport.SelectedExportSource.setValue('Plugin')
            opTrackingBaseDataExport.AdditionalPluginArguments.setValue(
                {'bdvFilePath': '/tmp/bdv.xml'})
            dataExportSerializer.serializeToHdf5(testProject,
                                                 self.TEST_PROJECT_FILE)

            # create new instance of TrackingBaseDataExportApplet and deserialize its topLevelOperator
            dataExportApplet = TrackingBaseDataExportApplet(
                op, "Tracking Result Export")
            dataExportSerializer = dataExportApplet.dataSerializers[0]
            dataExportSerializer.deserializeFromHdf5(testProject,
                                                     self.TEST_PROJECT_FILE)
            # check deserialized values in applet's topLevelOperator
            assert dataExportSerializer.topLevelOperator.SelectedPlugin.value == 'Fiji-MaMuT'
            assert dataExportSerializer.topLevelOperator.SelectedExportSource.value == 'Plugin'
            assert dataExportSerializer.topLevelOperator.AdditionalPluginArguments.value[
                'bdvFilePath'] == '/tmp/bdv.xml'
    def __init__(self, shell, headless, workflow_cmdline_args,
                 project_creation_args, *args, **kwargs):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        super(ManualTrackingWorkflow, self).__init__(shell,
                                                     headless,
                                                     workflow_cmdline_args,
                                                     project_creation_args,
                                                     graph=graph,
                                                     *args,
                                                     **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'\
                            'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'
        ## Create applets
        self.dataSelectionApplet = DataSelectionApplet(
            self,
            "Input Data",
            "Input Data",
            forceAxisOrder=['txyzc'],
            instructionText=data_instructions,
            max_lanes=1)
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        opDataSelection.DatasetRoles.setValue(['Raw Data', 'Prediction Maps'])

        self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet(
            self, "Threshold and Size Filter", "ThresholdTwoLevels")

        self.objectExtractionApplet = ObjectExtractionApplet(
            name="Object Feature Computation",
            workflow=self,
            interactive=False)

        self.trackingApplet = ManualTrackingApplet(workflow=self)
        self.default_export_filename = '{dataset_dir}/{nickname}-exported_data.csv'
        self.dataExportApplet = TrackingBaseDataExportApplet(
            self,
            "Tracking Result Export",
            default_export_filename=self.default_export_filename,
        )

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue(
            ['Manual Tracking', 'Object Identities'])
        opDataExport.WorkingDirectory.connect(opDataSelection.WorkingDirectory)

        # Extra configuration for object export table (as CSV table or HDF5 table)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet.set_exporting_operator(opTracking)
        #self.dataExportApplet.post_process_lane_export = self.post_process_lane_export

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
Beispiel #3
0
    def __init__(self, shell, headless, workflow_cmdline_args,
                 project_creation_args, *args, **kwargs):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        super(ManualTrackingWorkflow, self).__init__(shell,
                                                     headless,
                                                     workflow_cmdline_args,
                                                     project_creation_args,
                                                     graph=graph,
                                                     *args,
                                                     **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'\
                            'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'
        ## Create applets
        self.dataSelectionApplet = DataSelectionApplet(
            self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            force5d=True,
            instructionText=data_instructions,
            max_lanes=1)
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        opDataSelection.DatasetRoles.setValue(['Raw Data', 'Prediction Maps'])

        self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet(
            self, "Threshold and Size Filter", "ThresholdTwoLevels")

        self.objectExtractionApplet = ObjectExtractionApplet(
            name="Object Feature Computation",
            workflow=self,
            interactive=False)

        self.trackingApplet = ManualTrackingApplet(workflow=self)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet = TrackingBaseDataExportApplet(
            self, "Tracking Result Export")
        self.dataExportApplet.set_exporting_operator(opTracking)

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue(
            ['Manual Tracking', 'Object Identities'])
        opDataExport.WorkingDirectory.connect(opDataSelection.WorkingDirectory)

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
Beispiel #4
0
    def testAppletSerialization(self):
        g = Graph()
        op = OpFake(graph=g)
        dataExportApplet = TrackingBaseDataExportApplet(
            op, "Tracking Result Export")
        dataExportSerializer = dataExportApplet.dataSerializers[0]

        with h5py.File(self.TEST_PROJECT_FILE) as testProject:
            opTrackingBaseDataExport = dataExportApplet.topLevelOperator
            opTrackingBaseDataExport.SelectedPlugin.setValue('Fiji-MaMuT')
            opTrackingBaseDataExport.SelectedExportSource.setValue('Plugin')
            opTrackingBaseDataExport.AdditionalPluginArguments.setValue(
                {'bdvFilePath': '/tmp/bdv.xml'})

            dataExportSerializer.serializeToHdf5(testProject,
                                                 self.TEST_PROJECT_FILE)

        # check serialized values
        with h5py.File(self.TEST_PROJECT_FILE) as testProject:
            assert testProject[
                "Tracking Result Export/SelectedPlugin"].value.decode(
                ) == 'Fiji-MaMuT'
            assert testProject[
                "Tracking Result Export/SelectedExportSource"].value.decode(
                ) == 'Plugin'
            assert testProject[
                "Tracking Result Export/AdditionalPluginArguments/bdvFilePath"].value.decode(
                ) == '/tmp/bdv.xml'
def data_export_applet():
    op = Operator(graph=Graph())
    dataExportApplet = TrackingBaseDataExportApplet(op,
                                                    "Tracking Result Export")

    opTrackingBaseDataExport = dataExportApplet.topLevelOperator
    opTrackingBaseDataExport.SelectedPlugin.setValue("Fiji-MaMuT")
    opTrackingBaseDataExport.SelectedExportSource.setValue("Plugin")
    opTrackingBaseDataExport.AdditionalPluginArguments.setValue(
        {"bdvFilePath": "/tmp/bdv.xml"})

    return dataExportApplet
    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        super(ManualTrackingWorkflow, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)
        
        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'\
                            'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'
        ## Create applets 
        self.dataSelectionApplet = DataSelectionApplet(self, 
                                                       "Input Data", 
                                                       "Input Data", 
                                                       forceAxisOrder=['txyzc'],
                                                       instructionText=data_instructions,
                                                       max_lanes=1
                                                       )
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )                
        
        self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self, 
                                                                  "Threshold and Size Filter", 
                                                                  "ThresholdTwoLevels" )
                     
        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",
                                                             workflow=self, interactive=False)
        
        self.trackingApplet = ManualTrackingApplet( workflow=self )
        self.default_export_filename = '{dataset_dir}/{nickname}-exported_data.csv'
        self.dataExportApplet = TrackingBaseDataExportApplet(
            self,
            "Tracking Result Export",
            default_export_filename=self.default_export_filename,
        )
        
        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue( ['Manual Tracking', 'Object Identities'] )
        opDataExport.WorkingDirectory.connect( opDataSelection.WorkingDirectory )

        # Extra configuration for object export table (as CSV table or HDF5 table)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet.set_exporting_operator(opTracking)
        #self.dataExportApplet.post_process_lane_export = self.post_process_lane_export
        
        self._applets = []        
        self._applets.append(self.dataSelectionApplet)        
        self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.objectExtractionApplet)        
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
def test_applet_deserialization(project_path, data_export_applet):
    with h5py.File(project_path, "w") as project_file:
        data_export_applet.dataSerializers[0].serializeToHdf5(
            project_file, project_path)

    op = Operator(graph=Graph())
    data_export_applet = TrackingBaseDataExportApplet(
        op, "Tracking Result Export")

    with h5py.File(project_path, "r") as project_file:
        data_export_applet.dataSerializers[0].deserializeFromHdf5(
            project_file, project_path)

    opTrackingBaseDataExport = data_export_applet.dataSerializers[
        0].topLevelOperator
    assert opTrackingBaseDataExport.SelectedPlugin.value == "Fiji-MaMuT"
    assert opTrackingBaseDataExport.SelectedExportSource.value == "Plugin"
    assert opTrackingBaseDataExport.AdditionalPluginArguments.value[
        "bdvFilePath"] == "/tmp/bdv.xml"
    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        super(ManualTrackingWorkflow, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)
        
        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'\
                            'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'
        ## Create applets 
        self.dataSelectionApplet = DataSelectionApplet(self, 
                                                       "Input Data", 
                                                       "Input Data", 
                                                       batchDataGui=False,
                                                       force5d=True,
                                                       instructionText=data_instructions,
                                                       max_lanes=1
                                                       )
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )                
        
        self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self, 
                                                                  "Threshold and Size Filter", 
                                                                  "ThresholdTwoLevels" )
                     
        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",
                                                             workflow=self, interactive=False)
        
        self.trackingApplet = ManualTrackingApplet( workflow=self )
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet = TrackingBaseDataExportApplet(self, "Tracking Result Export")
        self.dataExportApplet.set_exporting_operator(opTracking)
        
        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue( ['Manual Tracking', 'Object Identities'] )
        opDataExport.WorkingDirectory.connect( opDataSelection.WorkingDirectory )

        self._applets = []        
        self._applets.append(self.dataSelectionApplet)        
        self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.objectExtractionApplet)        
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        # if 'withOptTrans' in kwargs:
        #     self.withOptTrans = kwargs['withOptTrans']
        # if 'fromBinary' in kwargs:
        #     self.fromBinary = kwargs['fromBinary']
        super(ConservationTrackingWorkflowBase, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Variables to store division and cell classifiers to prevent retraining every-time batch processing runs
        self.stored_division_classifier = None
        self.stored_cell_classifier = None

        ## Create applets 
        self.dataSelectionApplet = DataSelectionApplet(self, 
                                                       "Input Data", 
                                                       "Input Data", 
                                                       forceAxisOrder=['txyzc'],
                                                       instructionText=data_instructions,
                                                       max_lanes=None
                                                       )
        
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Segmentation Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )
                
        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self, 
                                                                  "Threshold and Size Filter", 
                                                                  "ThresholdTwoLevels" )
                                                                   
        self.objectExtractionApplet = TrackingFeatureExtractionApplet(workflow=self, interactive=False,
                                                                      name="Object Feature Computation")                                                                     
        
        opObjectExtraction = self.objectExtractionApplet.topLevelOperator

        self.divisionDetectionApplet = self._createDivisionDetectionApplet(configConservation.selectedFeaturesDiv) # Might be None

        if self.divisionDetectionApplet:
            feature_dict_division = {}
            feature_dict_division[config.features_division_name] = { name: {} for name in config.division_features }
            opObjectExtraction.FeatureNamesDivision.setValue(feature_dict_division)
               
            selected_features_div = {}
            for plugin_name in list(config.selected_features_division.keys()):
                selected_features_div[plugin_name] = { name: {} for name in config.selected_features_division[plugin_name] }
            # FIXME: do not hard code this
            for name in [ 'SquaredDistances_' + str(i) for i in range(config.n_best_successors) ]:
                selected_features_div[config.features_division_name][name] = {}

            opDivisionDetection = self.divisionDetectionApplet.topLevelOperator
            opDivisionDetection.SelectedFeatures.setValue(configConservation.selectedFeaturesDiv)
            opDivisionDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])        
            opDivisionDetection.AllowDeleteLabels.setValue(False)
            opDivisionDetection.AllowAddLabel.setValue(False)
            opDivisionDetection.EnableLabelTransfer.setValue(False)
                
        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification",
                                                                     projectFileGroupName="CountClassification",
                                                                     selectedFeatures=configConservation.selectedFeaturesObjectCount)

        selected_features_objectcount = {}
        for plugin_name in list(config.selected_features_objectcount.keys()):
            selected_features_objectcount[plugin_name] = { name: {} for name in config.selected_features_objectcount[plugin_name] }

        opCellClassification = self.cellClassificationApplet.topLevelOperator 
        opCellClassification.SelectedFeatures.setValue(configConservation.selectedFeaturesObjectCount)
        opCellClassification.SuggestedLabelNames.setValue( ['False Detection',] + [str(1) + ' Object'] + [str(i) + ' Objects' for i in range(2,10) ] )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)
                
        self.trackingApplet = ConservationTrackingApplet( workflow=self )

        self.default_export_filename = '{dataset_dir}/{nickname}-exported_data.csv'
        self.dataExportApplet = TrackingBaseDataExportApplet(
            self,
            "Tracking Result Export",
            default_export_filename=self.default_export_filename,
            pluginExportFunc=self._pluginExportFunc,
        )

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue( ['Object-Identities', 'Tracking-Result', 'Merger-Result'] )
        opDataExport.WorkingDirectory.connect( opDataSelection.WorkingDirectory )

        # Extra configuration for object export table (as CSV table or HDF5 table)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet.set_exporting_operator(opTracking)
        self.dataExportApplet.prepare_lane_for_export = self.prepare_lane_for_export

        # configure export settings
        # settings = {'file path': self.default_export_filename, 'compression': {}, 'file type': 'csv'}
        # selected_features = ['Count', 'RegionCenter', 'RegionRadii', 'RegionAxes']                  
        # opTracking.ExportSettings.setValue( (settings, selected_features) )
        
        self._applets = []                
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.objectExtractionApplet)

        if self.divisionDetectionApplet:
            self._applets.append(self.divisionDetectionApplet)
        
        self.batchProcessingApplet = BatchProcessingApplet(self, "Batch Processing", self.dataSelectionApplet, self.dataExportApplet)
            
        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
        self._applets.append(self.batchProcessingApplet)
        
        # Parse export and batch command-line arguments for headless mode
        if workflow_cmdline_args:
            self._data_export_args, unused_args = self.dataExportApplet.parse_known_cmdline_args( workflow_cmdline_args )
            self._batch_input_args, unused_args = self.batchProcessingApplet.parse_known_cmdline_args( workflow_cmdline_args )

        else:
            unused_args = None
            self._data_export_args = None
            self._batch_input_args = None

        if unused_args:
            logger.warning("Unused command-line args: {}".format( unused_args ))
class ManualTrackingWorkflow( Workflow ):
    workflowName = "Manual Tracking Workflow"
    workflowDisplayName = "Manual Tracking Workflow [Inputs: Raw Data, Pixel Prediction Map]"
    workflowDescription = "Manual tracking of objects, based on Prediction Maps or (binary) Segmentation Images"    

    @property
    def applets(self):
        return self._applets
    
    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        super(ManualTrackingWorkflow, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)
        
        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'\
                            'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'
        ## Create applets 
        self.dataSelectionApplet = DataSelectionApplet(self, 
                                                       "Input Data", 
                                                       "Input Data", 
                                                       batchDataGui=False,
                                                       force5d=True,
                                                       instructionText=data_instructions,
                                                       max_lanes=1
                                                       )
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )                
        
        self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self, 
                                                                  "Threshold and Size Filter", 
                                                                  "ThresholdTwoLevels" )
                     
        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",
                                                             workflow=self, interactive=False)
        
        self.trackingApplet = ManualTrackingApplet( workflow=self )
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet = TrackingBaseDataExportApplet(self, "Tracking Result Export")
        self.dataExportApplet.set_exporting_operator(opTracking)
        
        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue( ['Manual Tracking', 'Object Identities'] )
        opDataExport.WorkingDirectory.connect( opDataSelection.WorkingDirectory )

        self._applets = []        
        self._applets.append(self.dataSelectionApplet)        
        self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.objectExtractionApplet)        
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
            
    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)        
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(laneIndex)
        opTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)    
        opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(laneIndex)
        opDataExport = self.dataExportApplet.topLevelOperator.getLane(laneIndex)
                        
        ## Connect operators ##
        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])
        
        opTwoLevelThreshold.InputImage.connect( opData.ImageGroup[1] )
        opTwoLevelThreshold.RawInput.connect( opData.ImageGroup[0] ) # Used for display only
        # Use OpReorderAxis for both input datasets such that they are guaranteed to 
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes( parent=self )        
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect( opTwoLevelThreshold.CachedOutput )        
        
        opObjExtraction.RawImage.connect( op5Raw.Output )
        opObjExtraction.BinaryImage.connect( op5Binary.Output )
        
        opTracking.RawImage.connect( op5Raw.Output )
        opTracking.BinaryImage.connect( op5Binary.Output )
        opTracking.LabelImage.connect( opObjExtraction.LabelImage )
        opTracking.ObjectFeatures.connect( opObjExtraction.RegionFeatures )
        opTracking.ComputedFeatureNames.connect(opObjExtraction.ComputedFeatureNames)

        opDataExport.Inputs.resize(2)
        opDataExport.Inputs[0].connect( opTracking.TrackImage )
        opDataExport.Inputs[1].connect( opTracking.LabelImage )
        opDataExport.RawData.connect( op5Raw.Output )
        opDataExport.RawDatasetInfo.connect( opData.DatasetGroup[0] )
    
    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False

        return input_ready

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.        
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy
        
        opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
        thresholdingOutput = opThresholding.CachedOutput
        thresholding_ready = input_ready and \
                       len(thresholdingOutput) > 0 

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.ComputedFeatureNames
        features_ready = thresholding_ready and \
                         len(objectExtractionOutput) > 0

        opTracking = self.trackingApplet.topLevelOperator
        tracking_ready = features_ready and \
                           len(opTracking.Labels) > 0 and \
                           opTracking.Labels.ready() and \
                           opTracking.TrackImage.ready() 
        
        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.dataExportApplet.busy    
        busy |= self.trackingApplet.busy    
        self._shell.enableProjectChanges( not busy )
        
        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet, input_ready and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet, thresholding_ready and not busy)        
        self._shell.setAppletEnabled(self.trackingApplet, features_ready and not busy)
        self._shell.setAppletEnabled(self.dataExportApplet, tracking_ready and not busy and \
                                        self.dataExportApplet.topLevelOperator.Inputs[0][0].ready() )
Beispiel #11
0
    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']

        super(StructuredTrackingWorkflowBase, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Create applets
        self.dataSelectionApplet = DataSelectionApplet(self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            forceAxisOrder=['txyzc'],
            instructionText=data_instructions,
            max_lanes=1)

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Binary Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self,"Threshold and Size Filter","ThresholdTwoLevels" )

        self.divisionDetectionApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Division Detection (optional)",
                                                                     projectFileGroupName="DivisionDetection",
                                                                     selectedFeatures=configStructured.selectedFeaturesDiv)

        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification",
                                                                     projectFileGroupName="CountClassification",
                                                                     selectedFeatures=configStructured.selectedFeaturesObjectCount)

        self.cropSelectionApplet = CropSelectionApplet(self,"Crop Selection","CropSelection")

        self.trackingFeatureExtractionApplet = TrackingFeatureExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.annotationsApplet = AnnotationsApplet( name="Training", workflow=self )
        opAnnotations = self.annotationsApplet.topLevelOperator

        # self.default_training_export_filename = '{dataset_dir}/{nickname}-training_exported_data.csv'
        # self.dataExportAnnotationsApplet = TrackingBaseDataExportApplet(self, "Training Export",default_export_filename=self.default_training_export_filename)
        # opDataExportAnnotations = self.dataExportAnnotationsApplet.topLevelOperator
        # opDataExportAnnotations.SelectionNames.setValue( ['User Training for Tracking', 'Object Identities'] )
        # opDataExportAnnotations.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        # self.dataExportAnnotationsApplet.set_exporting_operator(opAnnotations)

        self.trackingApplet = StructuredTrackingApplet( name="Tracking - Structured Learning", workflow=self )
        opStructuredTracking = self.trackingApplet.topLevelOperator

        self.default_tracking_export_filename = '{dataset_dir}/{nickname}-tracking_exported_data.csv'
        self.dataExportTrackingApplet = TrackingBaseDataExportApplet(self, "Tracking Result Export",default_export_filename=self.default_tracking_export_filename)
        opDataExportTracking = self.dataExportTrackingApplet.topLevelOperator
        opDataExportTracking.SelectionNames.setValue( ['Tracking Result', 'Merger Result', 'Object Identities'] )
        opDataExportTracking.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        self.dataExportTrackingApplet.set_exporting_operator(opStructuredTracking)
        self.dataExportTrackingApplet.post_process_lane_export = self.post_process_lane_export

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.trackingFeatureExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)
        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.cropSelectionApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.annotationsApplet)
        # self._applets.append(self.dataExportAnnotationsApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportTrackingApplet)

        if self.divisionDetectionApplet:
            opDivDetection = self.divisionDetectionApplet.topLevelOperator
            opDivDetection.SelectedFeatures.setValue(configConservation.selectedFeaturesDiv)
            opDivDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])
            opDivDetection.AllowDeleteLabels.setValue(False)
            opDivDetection.AllowAddLabel.setValue(False)
            opDivDetection.EnableLabelTransfer.setValue(False)

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(configConservation.selectedFeaturesObjectCount )
        opCellClassification.SuggestedLabelNames.setValue( ['False Detection',] + [str(1) + ' Object'] + [str(i) + ' Objects' for i in range(2,10) ] )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)
class ConservationTrackingWorkflowBase( Workflow ):
    workflowName = "Automatic Tracking Workflow (Conservation Tracking) BASE"

    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        # if 'withOptTrans' in kwargs:
        #     self.withOptTrans = kwargs['withOptTrans']
        # if 'fromBinary' in kwargs:
        #     self.fromBinary = kwargs['fromBinary']
        super(ConservationTrackingWorkflowBase, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Variables to store division and cell classifiers to prevent retraining every-time batch processing runs
        self.stored_division_classifier = None
        self.stored_cell_classifier = None

        ## Create applets 
        self.dataSelectionApplet = DataSelectionApplet(self, 
                                                       "Input Data", 
                                                       "Input Data", 
                                                       forceAxisOrder=['txyzc'],
                                                       instructionText=data_instructions,
                                                       max_lanes=None
                                                       )
        
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Segmentation Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )
                
        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self, 
                                                                  "Threshold and Size Filter", 
                                                                  "ThresholdTwoLevels" )
                                                                   
        self.objectExtractionApplet = TrackingFeatureExtractionApplet(workflow=self, interactive=False,
                                                                      name="Object Feature Computation")                                                                     
        
        opObjectExtraction = self.objectExtractionApplet.topLevelOperator

        self.divisionDetectionApplet = self._createDivisionDetectionApplet(configConservation.selectedFeaturesDiv) # Might be None

        if self.divisionDetectionApplet:
            feature_dict_division = {}
            feature_dict_division[config.features_division_name] = { name: {} for name in config.division_features }
            opObjectExtraction.FeatureNamesDivision.setValue(feature_dict_division)
               
            selected_features_div = {}
            for plugin_name in list(config.selected_features_division.keys()):
                selected_features_div[plugin_name] = { name: {} for name in config.selected_features_division[plugin_name] }
            # FIXME: do not hard code this
            for name in [ 'SquaredDistances_' + str(i) for i in range(config.n_best_successors) ]:
                selected_features_div[config.features_division_name][name] = {}

            opDivisionDetection = self.divisionDetectionApplet.topLevelOperator
            opDivisionDetection.SelectedFeatures.setValue(configConservation.selectedFeaturesDiv)
            opDivisionDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])        
            opDivisionDetection.AllowDeleteLabels.setValue(False)
            opDivisionDetection.AllowAddLabel.setValue(False)
            opDivisionDetection.EnableLabelTransfer.setValue(False)
                
        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification",
                                                                     projectFileGroupName="CountClassification",
                                                                     selectedFeatures=configConservation.selectedFeaturesObjectCount)

        selected_features_objectcount = {}
        for plugin_name in list(config.selected_features_objectcount.keys()):
            selected_features_objectcount[plugin_name] = { name: {} for name in config.selected_features_objectcount[plugin_name] }

        opCellClassification = self.cellClassificationApplet.topLevelOperator 
        opCellClassification.SelectedFeatures.setValue(configConservation.selectedFeaturesObjectCount)
        opCellClassification.SuggestedLabelNames.setValue( ['False Detection',] + [str(1) + ' Object'] + [str(i) + ' Objects' for i in range(2,10) ] )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)
                
        self.trackingApplet = ConservationTrackingApplet( workflow=self )

        self.default_export_filename = '{dataset_dir}/{nickname}-exported_data.csv'
        self.dataExportApplet = TrackingBaseDataExportApplet(
            self,
            "Tracking Result Export",
            default_export_filename=self.default_export_filename,
            pluginExportFunc=self._pluginExportFunc,
        )

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue( ['Object-Identities', 'Tracking-Result', 'Merger-Result'] )
        opDataExport.WorkingDirectory.connect( opDataSelection.WorkingDirectory )

        # Extra configuration for object export table (as CSV table or HDF5 table)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet.set_exporting_operator(opTracking)
        self.dataExportApplet.prepare_lane_for_export = self.prepare_lane_for_export

        # configure export settings
        # settings = {'file path': self.default_export_filename, 'compression': {}, 'file type': 'csv'}
        # selected_features = ['Count', 'RegionCenter', 'RegionRadii', 'RegionAxes']                  
        # opTracking.ExportSettings.setValue( (settings, selected_features) )
        
        self._applets = []                
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.objectExtractionApplet)

        if self.divisionDetectionApplet:
            self._applets.append(self.divisionDetectionApplet)
        
        self.batchProcessingApplet = BatchProcessingApplet(self, "Batch Processing", self.dataSelectionApplet, self.dataExportApplet)
            
        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
        self._applets.append(self.batchProcessingApplet)
        
        # Parse export and batch command-line arguments for headless mode
        if workflow_cmdline_args:
            self._data_export_args, unused_args = self.dataExportApplet.parse_known_cmdline_args( workflow_cmdline_args )
            self._batch_input_args, unused_args = self.batchProcessingApplet.parse_known_cmdline_args( workflow_cmdline_args )

        else:
            unused_args = None
            self._data_export_args = None
            self._batch_input_args = None

        if unused_args:
            logger.warning("Unused command-line args: {}".format( unused_args ))
        
    @property
    def applets(self):
        return self._applets

    def _createDivisionDetectionApplet(self,selectedFeatures=dict()):
        return ObjectClassificationApplet(workflow=self,
                                          name="Division Detection (optional)",
                                          projectFileGroupName="DivisionDetection",
                                          selectedFeatures=selectedFeatures)
    
    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def prepareForNewLane(self, laneIndex):
        # Store division and cell classifiers
        if self.divisionDetectionApplet:
            opDivisionClassification = self.divisionDetectionApplet.topLevelOperator
            if opDivisionClassification.classifier_cache.Output.ready() and \
               not opDivisionClassification.classifier_cache._dirty:
                self.stored_division_classifier = opDivisionClassification.classifier_cache.Output.value
            else:
                self.stored_division_classifier = None
                
        opCellClassification = self.cellClassificationApplet.topLevelOperator
        if opCellClassification.classifier_cache.Output.ready() and \
           not opCellClassification.classifier_cache._dirty:
            self.stored_cell_classifier = opCellClassification.classifier_cache.Output.value
        else:
            self.stored_cell_classifier = None

    def handleNewLanesAdded(self):
        """
        If new lanes were added, then we invalidated our classifiers unecessarily.
        Here, we can restore the classifier so it doesn't need to be retrained.
        """
        
        # If we have stored division and cell classifiers, restore them into the workflow now.
        if self.stored_division_classifier:
            opDivisionClassification = self.divisionDetectionApplet.topLevelOperator
            opDivisionClassification.classifier_cache.forceValue(self.stored_division_classifier)
            # Release reference
            self.stored_division_classifier = None
        
        # If we have stored division and cell classifiers, restore them into the workflow now.
        if self.stored_cell_classifier:
            opCellClassification = self.cellClassificationApplet.topLevelOperator
            opCellClassification.classifier_cache.forceValue(self.stored_cell_classifier)
            # Release reference
            self.stored_cell_classifier = None
    
    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        if not self.fromBinary:
            opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(laneIndex)
        opObjExtraction.setDefaultFeatures(configConservation.allFeaturesObjectCount)

        if self.divisionDetectionApplet:
                opDivDetection = self.divisionDetectionApplet.topLevelOperator.getLane(laneIndex)
            
        opCellClassification = self.cellClassificationApplet.topLevelOperator.getLane(laneIndex)
        opTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opDataExport = self.dataExportApplet.topLevelOperator.getLane(laneIndex)
        
        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])
        
        if not self.fromBinary:
            opTwoLevelThreshold.InputImage.connect(opData.ImageGroup[1])
            opTwoLevelThreshold.RawInput.connect(opData.ImageGroup[0])  # Used for display only
            # opTwoLevelThreshold.Channel.setValue(1)
            binarySrc = opTwoLevelThreshold.CachedOutput
        else:
            binarySrc = opData.ImageGroup[1]
        
        # Use Op5ifyers for both input datasets such that they are guaranteed to 
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)         
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(binarySrc)

        # # Connect operators ##       
        opObjExtraction.RawImage.connect(op5Raw.Output)
        opObjExtraction.BinaryImage.connect(op5Binary.Output)

        if self.divisionDetectionApplet:            
            opDivDetection.BinaryImages.connect( op5Binary.Output )
            opDivDetection.RawImages.connect( op5Raw.Output )        
            opDivDetection.SegmentationImages.connect(opObjExtraction.LabelImage)
            opDivDetection.ObjectFeatures.connect(opObjExtraction.RegionFeaturesAll)
            opDivDetection.ComputedFeatureNames.connect(opObjExtraction.ComputedFeatureNamesAll)
        
        opCellClassification.BinaryImages.connect( op5Binary.Output )
        opCellClassification.RawImages.connect( op5Raw.Output )
        opCellClassification.SegmentationImages.connect(opObjExtraction.LabelImage)
        opCellClassification.ObjectFeatures.connect(opObjExtraction.RegionFeaturesVigra)
        opCellClassification.ComputedFeatureNames.connect(opObjExtraction.FeatureNamesVigra)
        
        if self.divisionDetectionApplet: 
            opTracking.ObjectFeaturesWithDivFeatures.connect( opObjExtraction.RegionFeaturesAll)
            opTracking.ComputedFeatureNamesWithDivFeatures.connect( opObjExtraction.ComputedFeatureNamesAll )
            opTracking.DivisionProbabilities.connect( opDivDetection.Probabilities ) 

        opTracking.RawImage.connect( op5Raw.Output )
        opTracking.LabelImage.connect( opObjExtraction.LabelImage )
        opTracking.ObjectFeatures.connect( opObjExtraction.RegionFeaturesVigra )
        opTracking.ComputedFeatureNames.connect( opObjExtraction.FeatureNamesVigra)
        opTracking.DetectionProbabilities.connect( opCellClassification.Probabilities )
        opTracking.NumLabels.connect( opCellClassification.NumLabels )
    
        opDataExport.Inputs.resize(3)
        opDataExport.Inputs[0].connect( opTracking.RelabeledImage )
        opDataExport.Inputs[1].connect( opTracking.Output )
        opDataExport.Inputs[2].connect( opTracking.MergerOutput )
        opDataExport.RawData.connect( op5Raw.Output )
        opDataExport.RawDatasetInfo.connect( opData.DatasetGroup[0] )
         
    def prepare_lane_for_export(self, lane_index):
        # Bypass cache on headless mode and batch processing mode
        self.objectExtractionApplet.topLevelOperator[lane_index].BypassModeEnabled.setValue(True)
        
        if not self.fromBinary:
            self.thresholdTwoLevelsApplet.topLevelOperator[lane_index].opCache.BypassModeEnabled.setValue(True)
            self.thresholdTwoLevelsApplet.topLevelOperator[lane_index].opSmootherCache.BypassModeEnabled.setValue(True)
         
        # Get axes info  
        maxt = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[0] 
        maxx = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[1] 
        maxy = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[2] 
        maxz = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[3] 
        time_enum = list(range(maxt))
        x_range = (0, maxx)
        y_range = (0, maxy)
        z_range = (0, maxz)

        ndim = 2
        if ( z_range[1] - z_range[0] ) > 1:
            ndim = 3
        
        parameters = self.trackingApplet.topLevelOperator.Parameters.value
        
        # Save state of axis ranges
        if 'time_range' in parameters:
            self.prev_time_range = parameters['time_range']
        else:
            self.prev_time_range = time_enum
            
        if 'x_range' in parameters:
            self.prev_x_range = parameters['x_range']
        else:
            self.prev_x_range = x_range
        
        if 'y_range' in parameters:
            self.prev_y_range = parameters['y_range']
        else:
            self.prev_y_range = y_range
            
        if 'z_range' in parameters:
            self.prev_z_range = parameters['z_range']
        else:
            self.prev_z_range = z_range

        if 'numFramesPerSplit' in parameters:
            numFramesPerSplit = parameters['numFramesPerSplit']
        else:
            numFramesPerSplit = 0

        self.trackingApplet.topLevelOperator[lane_index].track(
            time_range = time_enum,
            x_range = x_range,
            y_range = y_range,
            z_range = z_range,
            size_range = parameters['size_range'],
            x_scale = parameters['scales'][0],
            y_scale = parameters['scales'][1],
            z_scale = parameters['scales'][2],
            maxDist=parameters['maxDist'],         
            maxObj = parameters['maxObj'],               
            divThreshold=parameters['divThreshold'],
            avgSize=parameters['avgSize'],                
            withTracklets=parameters['withTracklets'], 
            sizeDependent=parameters['sizeDependent'],
            divWeight=parameters['divWeight'],
            transWeight=parameters['transWeight'],
            withDivisions=parameters['withDivisions'],
            withOpticalCorrection=parameters['withOpticalCorrection'],
            withClassifierPrior=parameters['withClassifierPrior'],
            ndim=ndim,
            withMergerResolution=parameters['withMergerResolution'],
            borderAwareWidth = parameters['borderAwareWidth'],
            withArmaCoordinates = parameters['withArmaCoordinates'],
            cplex_timeout = parameters['cplex_timeout'],
            appearance_cost = parameters['appearanceCost'],
            disappearance_cost = parameters['disappearanceCost'],
            max_nearest_neighbors = parameters['max_nearest_neighbors'],
            numFramesPerSplit = numFramesPerSplit,
            force_build_hypotheses_graph = False,
            withBatchProcessing = True
        )

    def _pluginExportFunc(self, lane_index, filename, exportPlugin, checkOverwriteFiles, plugArgsSlot) -> int:
        return (
            self.trackingApplet
            .topLevelOperator
            .getLane(lane_index)
            .exportPlugin(
                filename,
                exportPlugin,
                checkOverwriteFiles,
                plugArgsSlot
            )
        )

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False

        return input_ready

    def onProjectLoaded(self, projectManager):
        """
        Overridden from Workflow base class.  Called by the Project Manager.
        
        If the user provided command-line arguments, use them to configure 
        the workflow inputs and output settings.
        """

        # Configure the data export operator.
        if self._data_export_args:
            self.dataExportApplet.configure_operator_with_parsed_args( self._data_export_args )

        # Configure headless mode.
        if self._headless and self._batch_input_args and self._data_export_args:
            logger.info("Beginning Batch Processing")
            self.batchProcessingApplet.run_export_from_parsed_args(self._batch_input_args)
            logger.info("Completed Batch Processing")

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        if not self.fromBinary:
            opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
            thresholdingOutput = opThresholding.CachedOutput
            thresholding_ready = input_ready and \
                           len(thresholdingOutput) > 0
        else:
            thresholding_ready = True and input_ready

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.ComputedFeatureNamesAll
        features_ready = thresholding_ready and \
                         len(objectExtractionOutput) > 0

        objectCountClassifier_ready = features_ready

        opTracking = self.trackingApplet.topLevelOperator
        tracking_ready = objectCountClassifier_ready                          

        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.trackingApplet.busy
        busy |= self.dataExportApplet.busy
        busy |= self.batchProcessingApplet.busy
        self._shell.enableProjectChanges( not busy )

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        if not self.fromBinary:
            self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet, input_ready and not busy)
            
        if self.divisionDetectionApplet:    
            self._shell.setAppletEnabled(self.divisionDetectionApplet, features_ready and not busy)
        
        self._shell.setAppletEnabled(self.objectExtractionApplet, thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.cellClassificationApplet, features_ready and not busy)
        self._shell.setAppletEnabled(self.trackingApplet, objectCountClassifier_ready and not busy)
        self._shell.setAppletEnabled(self.dataExportApplet, tracking_ready and not busy and \
                                    self.dataExportApplet.topLevelOperator.Inputs[0][0].ready() )
        self._shell.setAppletEnabled(self.batchProcessingApplet, tracking_ready and not busy and \
                                    self.dataExportApplet.topLevelOperator.Inputs[0][0].ready() )
Beispiel #13
0
class ManualTrackingWorkflow(Workflow):
    workflowName = "Manual Tracking Workflow"
    workflowDisplayName = "Manual Tracking Workflow [Inputs: Raw Data, Pixel Prediction Map]"
    workflowDescription = "Manual tracking of objects, based on Prediction Maps or (binary) Segmentation Images"

    @property
    def applets(self):
        return self._applets

    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def __init__(self, shell, headless, workflow_cmdline_args,
                 project_creation_args, *args, **kwargs):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        super(ManualTrackingWorkflow, self).__init__(shell,
                                                     headless,
                                                     workflow_cmdline_args,
                                                     project_creation_args,
                                                     graph=graph,
                                                     *args,
                                                     **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'\
                            'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'
        ## Create applets
        self.dataSelectionApplet = DataSelectionApplet(
            self,
            "Input Data",
            "Input Data",
            forceAxisOrder='txyzc',
            instructionText=data_instructions,
            max_lanes=1)
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        opDataSelection.DatasetRoles.setValue(['Raw Data', 'Prediction Maps'])

        self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet(
            self, "Threshold and Size Filter", "ThresholdTwoLevels")

        self.objectExtractionApplet = ObjectExtractionApplet(
            name="Object Feature Computation",
            workflow=self,
            interactive=False)

        self.trackingApplet = ManualTrackingApplet(workflow=self)
        self.default_export_filename = '{dataset_dir}/{nickname}-exported_data.csv'
        self.dataExportApplet = TrackingBaseDataExportApplet(
            self,
            "Tracking Result Export",
            default_export_filename=self.default_export_filename)

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue(
            ['Manual Tracking', 'Object Identities'])
        opDataExport.WorkingDirectory.connect(opDataSelection.WorkingDirectory)

        # Extra configuration for object export table (as CSV table or HDF5 table)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet.set_exporting_operator(opTracking)
        self.dataExportApplet.post_process_lane_export = self.post_process_lane_export

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)

    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(
            laneIndex)
        opTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(
            laneIndex)
        opDataExport = self.dataExportApplet.topLevelOperator.getLane(
            laneIndex)

        ## Connect operators ##
        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])

        opTwoLevelThreshold.InputImage.connect(opData.ImageGroup[1])
        opTwoLevelThreshold.RawInput.connect(
            opData.ImageGroup[0])  # Used for display only
        # Use OpReorderAxis for both input datasets such that they are guaranteed to
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(opTwoLevelThreshold.CachedOutput)

        opObjExtraction.RawImage.connect(op5Raw.Output)
        opObjExtraction.BinaryImage.connect(op5Binary.Output)

        opTracking.RawImage.connect(op5Raw.Output)
        opTracking.BinaryImage.connect(op5Binary.Output)
        opTracking.LabelImage.connect(opObjExtraction.LabelImage)
        opTracking.ObjectFeatures.connect(opObjExtraction.RegionFeatures)
        opTracking.ComputedFeatureNames.connect(opObjExtraction.Features)

        opDataExport.Inputs.resize(2)
        opDataExport.Inputs[0].connect(opTracking.TrackImage)
        opDataExport.Inputs[1].connect(opTracking.LabelImage)
        opDataExport.RawData.connect(op5Raw.Output)
        opDataExport.RawDatasetInfo.connect(opData.DatasetGroup[0])

    def post_process_lane_export(self, lane_index):
        # FIXME: This probably only works for the non-blockwise export slot.
        #        We should assert that the user isn't using the blockwise slot.
        settings, selected_features = self.trackingApplet.topLevelOperator.getLane(
            lane_index).get_table_export_settings()
        if settings:
            raw_dataset_info = self.dataSelectionApplet.topLevelOperator.DatasetGroup[
                lane_index][0].value
            if raw_dataset_info.location == DatasetInfo.Location.FileSystem:
                filename_suffix = raw_dataset_info.nickname
            else:
                filename_suffix = str(lane_index)
            req = self.trackingApplet.topLevelOperator.getLane(
                lane_index
            ).export_object_data(
                lane_index,
                # FIXME: Even in non-headless mode, we can't show the gui because we're running in a non-main thread.
                #        That's not a huge deal, because there's still a progress bar for the overall export.
                show_gui=False,
                filename_suffix=filename_suffix)
            req.wait()

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False

        return input_ready

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
        thresholdingOutput = opThresholding.CachedOutput
        thresholding_ready = input_ready and \
                       len(thresholdingOutput) > 0

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        features_ready = thresholding_ready

        opTracking = self.trackingApplet.topLevelOperator
        tracking_ready = features_ready and \
                           len(opTracking.Labels) > 0 and \
                           opTracking.Labels.ready() and \
                           opTracking.TrackImage.ready()

        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.dataExportApplet.busy
        busy |= self.trackingApplet.busy
        self._shell.enableProjectChanges(not busy)

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet, input_ready
                                     and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet,
                                     thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.trackingApplet, features_ready
                                     and not busy)
        self._shell.setAppletEnabled(self.dataExportApplet, tracking_ready and not busy and \
                                        self.dataExportApplet.topLevelOperator.Inputs[0][0].ready() )
class StructuredTrackingWorkflowBase( Workflow ):
    workflowName = "Structured Learning Tracking Workflow BASE"

    @property
    def applets(self):
        return self._applets

    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']

        super(StructuredTrackingWorkflowBase, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Create applets
        self.dataSelectionApplet = DataSelectionApplet(self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            forceAxisOrder=['txyzc'],
            instructionText=data_instructions,
            max_lanes=1)

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Binary Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self,"Threshold and Size Filter","ThresholdTwoLevels" )

        self.divisionDetectionApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Division Detection (optional)",
                                                                     projectFileGroupName="DivisionDetection",
                                                                     selectedFeatures=configConservation.selectedFeaturesDiv)

        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification",
                                                                     projectFileGroupName="CountClassification",
                                                                     selectedFeatures=configConservation.selectedFeaturesObjectCount)

        self.trackingFeatureExtractionApplet = TrackingFeatureExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.annotationsApplet = AnnotationsApplet( name="Training", workflow=self )
        opAnnotations = self.annotationsApplet.topLevelOperator

        self.trackingApplet = StructuredTrackingApplet( name="Tracking - Structured Learning", workflow=self )
        opStructuredTracking = self.trackingApplet.topLevelOperator

        if SOLVER=="CPLEX" or SOLVER=="GUROBI":
            self._solver="ILP"
        elif SOLVER=="DPCT":
            self._solver="Flow-based"
        else:
            self._solver=None
        opStructuredTracking._solver = self._solver

        self.default_tracking_export_filename = '{dataset_dir}/{nickname}-tracking_exported_data.csv'
        self.dataExportTrackingApplet = TrackingBaseDataExportApplet(
            self,
            "Tracking Result Export",
            default_export_filename=self.default_tracking_export_filename,
            pluginExportFunc=self._pluginExportFunc
        )
        opDataExportTracking = self.dataExportTrackingApplet.topLevelOperator
        opDataExportTracking.SelectionNames.setValue( ['Tracking-Result', 'Merger-Result', 'Object-Identities'] )
        opDataExportTracking.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        self.dataExportTrackingApplet.set_exporting_operator(opStructuredTracking)
        self.dataExportTrackingApplet.prepare_lane_for_export = self.prepare_lane_for_export

        # configure export settings
        settings = {'file path': self.default_tracking_export_filename, 'compression': {}, 'file type': 'h5'}
        selected_features = ['Count', 'RegionCenter', 'RegionRadii', 'RegionAxes']
        opStructuredTracking.ExportSettings.setValue( (settings, selected_features) )

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.trackingFeatureExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)

        self.batchProcessingApplet = BatchProcessingApplet(self, "Batch Processing", self.dataSelectionApplet, self.dataExportTrackingApplet)

        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.annotationsApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportTrackingApplet)

        if self.divisionDetectionApplet:
            opDivDetection = self.divisionDetectionApplet.topLevelOperator
            opDivDetection.SelectedFeatures.setValue(configConservation.selectedFeaturesDiv)
            opDivDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])
            opDivDetection.AllowDeleteLabels.setValue(False)
            opDivDetection.AllowAddLabel.setValue(False)
            opDivDetection.EnableLabelTransfer.setValue(False)

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(configConservation.selectedFeaturesObjectCount )
        opCellClassification.SuggestedLabelNames.setValue( ['False Detection',] + [str(1) + ' Object'] + [str(i) + ' Objects' for i in range(2,10) ] )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

        if workflow_cmdline_args:

            if '--testFullAnnotations' in workflow_cmdline_args:
                self.testFullAnnotations = True
            else:
                self.testFullAnnotations = False

            self._data_export_args, unused_args = self.dataExportTrackingApplet.parse_known_cmdline_args( workflow_cmdline_args )
            self._batch_input_args, unused_args = self.batchProcessingApplet.parse_known_cmdline_args( workflow_cmdline_args )
        else:
            unused_args = None
            self._data_export_args = None
            self._batch_input_args = None
            self.testFullAnnotations = False

        if unused_args:
            logger.warning("Unused command-line args: {}".format( unused_args ))

    def _pluginExportFunc(self, lane_index, filename, exportPlugin, checkOverwriteFiles, plugArgsSlot) -> int:
        return (
            self.trackingApplet
            .topLevelOperator
            .getLane(lane_index)
            .exportPlugin(
                filename,
                exportPlugin,
                checkOverwriteFiles,
                additionalPluginArgumentsSlot
            )
        )

    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(laneIndex)
        opTrackingFeatureExtraction = self.trackingFeatureExtractionApplet.topLevelOperator.getLane(laneIndex)

        opAnnotations = self.annotationsApplet.topLevelOperator.getLane(laneIndex)
        if not self.fromBinary:
            opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(laneIndex)

        opStructuredTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opDataTrackingExport = self.dataExportTrackingApplet.topLevelOperator.getLane(laneIndex)

        ## Connect operators ##
        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])

        opDivDetection = self.divisionDetectionApplet.topLevelOperator.getLane(laneIndex)
        opCellClassification = self.cellClassificationApplet.topLevelOperator.getLane(laneIndex)

        if not self.fromBinary:
            opTwoLevelThreshold.InputImage.connect( opData.ImageGroup[1] )
            opTwoLevelThreshold.RawInput.connect( opData.ImageGroup[0] ) # Used for display only
            binarySrc = opTwoLevelThreshold.CachedOutput
        else:
            binarySrc = opData.ImageGroup[1]
        # Use Op5ifyers for both input datasets such that they are guaranteed to
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(binarySrc)

        opObjExtraction.RawImage.connect( op5Raw.Output )
        opObjExtraction.BinaryImage.connect( op5Binary.Output )

        opTrackingFeatureExtraction.RawImage.connect( op5Raw.Output )
        opTrackingFeatureExtraction.BinaryImage.connect( op5Binary.Output )

        opTrackingFeatureExtraction.setDefaultFeatures(configConservation.allFeaturesObjectCount)
        opTrackingFeatureExtraction.FeatureNamesVigra.setValue(configConservation.allFeaturesObjectCount)
        feature_dict_division = {}
        feature_dict_division[config.features_division_name] = { name: {} for name in config.division_features }
        opTrackingFeatureExtraction.FeatureNamesDivision.setValue(feature_dict_division)

        if self.divisionDetectionApplet:
            opDivDetection.BinaryImages.connect( op5Binary.Output )
            opDivDetection.RawImages.connect( op5Raw.Output )
            opDivDetection.SegmentationImages.connect(opTrackingFeatureExtraction.LabelImage)
            opDivDetection.ObjectFeatures.connect(opTrackingFeatureExtraction.RegionFeaturesAll)
            opDivDetection.ComputedFeatureNames.connect(opTrackingFeatureExtraction.ComputedFeatureNamesAll)

        opCellClassification.BinaryImages.connect( op5Binary.Output )
        opCellClassification.RawImages.connect( op5Raw.Output )
        opCellClassification.SegmentationImages.connect(opTrackingFeatureExtraction.LabelImage)
        opCellClassification.ObjectFeatures.connect(opTrackingFeatureExtraction.RegionFeaturesAll)
        opCellClassification.ComputedFeatureNames.connect(opTrackingFeatureExtraction.ComputedFeatureNamesNoDivisions)

        opAnnotations.RawImage.connect( op5Raw.Output )
        opAnnotations.BinaryImage.connect( op5Binary.Output )
        opAnnotations.LabelImage.connect( opObjExtraction.LabelImage )
        opAnnotations.ObjectFeatures.connect( opObjExtraction.RegionFeatures )
        opAnnotations.ComputedFeatureNames.connect(opObjExtraction.Features)
        opAnnotations.DivisionProbabilities.connect( opDivDetection.Probabilities )
        opAnnotations.DetectionProbabilities.connect( opCellClassification.Probabilities )
        opAnnotations.MaxNumObj.connect (opCellClassification.MaxNumObj)

        opStructuredTracking.RawImage.connect( op5Raw.Output )
        opStructuredTracking.LabelImage.connect( opTrackingFeatureExtraction.LabelImage )
        opStructuredTracking.ObjectFeatures.connect( opTrackingFeatureExtraction.RegionFeaturesVigra )
        opStructuredTracking.ComputedFeatureNames.connect( opTrackingFeatureExtraction.FeatureNamesVigra )

        if self.divisionDetectionApplet:
            opStructuredTracking.ObjectFeaturesWithDivFeatures.connect( opTrackingFeatureExtraction.RegionFeaturesAll)
            opStructuredTracking.ComputedFeatureNamesWithDivFeatures.connect( opTrackingFeatureExtraction.ComputedFeatureNamesAll )
            opStructuredTracking.DivisionProbabilities.connect( opDivDetection.Probabilities )

        opStructuredTracking.DetectionProbabilities.connect( opCellClassification.Probabilities )
        opStructuredTracking.NumLabels.connect( opCellClassification.NumLabels )
        opStructuredTracking.Annotations.connect (opAnnotations.Annotations)
        opStructuredTracking.Labels.connect (opAnnotations.Labels)
        opStructuredTracking.Divisions.connect (opAnnotations.Divisions)
        opStructuredTracking.Appearances.connect (opAnnotations.Appearances)
        opStructuredTracking.Disappearances.connect (opAnnotations.Disappearances)
        opStructuredTracking.MaxNumObj.connect (opCellClassification.MaxNumObj)

        opDataTrackingExport.Inputs.resize(3)
        opDataTrackingExport.Inputs[0].connect( opStructuredTracking.RelabeledImage )
        opDataTrackingExport.Inputs[1].connect( opStructuredTracking.MergerOutput )
        opDataTrackingExport.Inputs[2].connect( opStructuredTracking.LabelImage )
        opDataTrackingExport.RawData.connect( op5Raw.Output )
        opDataTrackingExport.RawDatasetInfo.connect( opData.DatasetGroup[0] )

    def prepare_lane_for_export(self, lane_index):
        import logging
        logger = logging.getLogger(__name__)

        maxt = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[0]
        maxx = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[1]
        maxy = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[2]
        maxz = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[3]
        time_enum = list(range(maxt))
        x_range = (0, maxx)
        y_range = (0, maxy)
        z_range = (0, maxz)

        ndim = 2
        if ( z_range[1] - z_range[0] ) > 1:
            ndim = 3

        parameters = self.trackingApplet.topLevelOperator.Parameters.value
        # Save state of axis ranges
        if 'time_range' in parameters:
            self.prev_time_range = parameters['time_range']
        else:
            self.prev_time_range = time_enum

        if 'x_range' in parameters:
            self.prev_x_range = parameters['x_range']
        else:
            self.prev_x_range = x_range

        if 'y_range' in parameters:
            self.prev_y_range = parameters['y_range']
        else:
            self.prev_y_range = y_range

        if 'z_range' in parameters:
            self.prev_z_range = parameters['z_range']
        else:
            self.prev_z_range = z_range

        # batch processing starts a new lane, so training data needs to be copied from the lane that loaded the project
        loaded_project_lane_index=0
        self.annotationsApplet.topLevelOperator[lane_index].Annotations.setValue(
            self.trackingApplet.topLevelOperator[loaded_project_lane_index].Annotations.value)

        def runLearningAndTracking(withMergerResolution=True):
            if self.testFullAnnotations:
                logger.info("Test: Structured Learning")
                weights = self.trackingApplet.topLevelOperator[lane_index]._runStructuredLearning(
                    z_range,
                    parameters['maxObj'],
                    parameters['max_nearest_neighbors'],
                    parameters['maxDist'],
                    parameters['divThreshold'],
                    [parameters['scales'][0],parameters['scales'][1],parameters['scales'][2]],
                    parameters['size_range'],
                    parameters['withDivisions'],
                    parameters['borderAwareWidth'],
                    parameters['withClassifierPrior'],
                    withBatchProcessing=True)
                logger.info("weights: {}".format(weights))

            logger.info("Test: Tracking")
            result = self.trackingApplet.topLevelOperator[lane_index].track(
                time_range = time_enum,
                x_range = x_range,
                y_range = y_range,
                z_range = z_range,
                size_range = parameters['size_range'],
                x_scale = parameters['scales'][0],
                y_scale = parameters['scales'][1],
                z_scale = parameters['scales'][2],
                maxDist=parameters['maxDist'],
                maxObj = parameters['maxObj'],
                divThreshold=parameters['divThreshold'],
                avgSize=parameters['avgSize'],
                withTracklets=parameters['withTracklets'],
                sizeDependent=parameters['sizeDependent'],
                detWeight=parameters['detWeight'],
                divWeight=parameters['divWeight'],
                transWeight=parameters['transWeight'],
                withDivisions=parameters['withDivisions'],
                withOpticalCorrection=parameters['withOpticalCorrection'],
                withClassifierPrior=parameters['withClassifierPrior'],
                ndim=ndim,
                withMergerResolution=withMergerResolution,
                borderAwareWidth = parameters['borderAwareWidth'],
                withArmaCoordinates = parameters['withArmaCoordinates'],
                cplex_timeout = parameters['cplex_timeout'],
                appearance_cost = parameters['appearanceCost'],
                disappearance_cost = parameters['disappearanceCost'],
                force_build_hypotheses_graph = False,
                withBatchProcessing = True
            )

            return result

        if self.testFullAnnotations:

            self.result = runLearningAndTracking(withMergerResolution=False)

            hypothesesGraph = self.trackingApplet.topLevelOperator[lane_index].LearningHypothesesGraph.value
            hypothesesGraph.insertSolution(self.result)
            hypothesesGraph.computeLineage()
            solution = hypothesesGraph.getSolutionDictionary()
            annotations = self.trackingApplet.topLevelOperator[lane_index].Annotations.value

            self.trackingApplet.topLevelOperator[lane_index].insertAnnotationsToHypothesesGraph(hypothesesGraph,annotations,misdetectionLabel=-1)
            hypothesesGraph.computeLineage()
            solutionFromAnnotations = hypothesesGraph.getSolutionDictionary()

            for key in list(solution.keys()):
                if key == 'detectionResults':
                    detectionFlag = True
                    for i in range(len(solution[key])):
                        flag = False
                        for j in range(len(solutionFromAnnotations[key])):
                            if solution[key][i]['id'] == solutionFromAnnotations[key][j]['id'] and \
                                solution[key][i]['value'] == solutionFromAnnotations[key][j]['value']:
                                flag = True
                                break
                        detectionFlag &= flag
                elif key == 'divisionResults':
                    divisionFlag = True
                    for i in range(len(solution[key])):
                        flag = False
                        for j in range(len(solutionFromAnnotations[key])):
                            if solution[key][i]['id'] == solutionFromAnnotations[key][j]['id'] and \
                                solution[key][i]['value'] == solutionFromAnnotations[key][j]['value']:
                                flag = True
                                break
                        divisionFlag &= flag
                elif key == 'linkingResults':
                    linkingFlag = True
                    for i in range(len(solution[key])):
                        flag = False
                        for j in range(len(solutionFromAnnotations[key])):
                            if solution[key][i]['dest'] == solutionFromAnnotations[key][j]['dest'] and \
                                solution[key][i]['src'] == solutionFromAnnotations[key][j]['src']:
                                if solution[key][i]['gap'] == solutionFromAnnotations[key][j]['gap'] and \
                                    solution[key][i]['value'] == solutionFromAnnotations[key][j]['value']:
                                    flag = True
                                    break
                        linkingFlag &= flag

            assert detectionFlag, "Detection results are NOT correct. They differ from your annotated detections."
            logger.info("Detection results are correct.")
            assert divisionFlag, "Division results are NOT correct. They differ from your annotated divisions."
            logger.info("Division results are correct.")
            assert linkingFlag, "Transition results are NOT correct. They differ from your annotated transitions."
            logger.info("Transition results are correct.")
        self.result = runLearningAndTracking(withMergerResolution=parameters['withMergerResolution'])

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False
        return input_ready

    def onProjectLoaded(self, projectManager):
        """
        Overridden from Workflow base class.  Called by the Project Manager.

        If the user provided command-line arguments, use them to configure
        the workflow inputs and output settings.
        """

        # Configure the data export operator.
        if self._data_export_args:
            self.dataExportTrackingApplet.configure_operator_with_parsed_args( self._data_export_args )

        # Configure headless mode.
        if self._headless and self._batch_input_args and self._data_export_args:
            logger.info("Beginning Batch Processing")
            self.batchProcessingApplet.run_export_from_parsed_args(self._batch_input_args)
            logger.info("Completed Batch Processing")

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        if not self.fromBinary:
            opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
            thresholdingOutput = opThresholding.CachedOutput
            thresholding_ready = input_ready and len(thresholdingOutput) > 0
        else:
            thresholding_ready = input_ready

        opTrackingFeatureExtraction = self.trackingFeatureExtractionApplet.topLevelOperator
        trackingFeatureExtractionOutput = opTrackingFeatureExtraction.ComputedFeatureNamesAll
        tracking_features_ready = thresholding_ready and len(trackingFeatureExtractionOutput) > 0

        objectCountClassifier_ready = tracking_features_ready

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.RegionFeatures
        features_ready = thresholding_ready and \
                         len(objectExtractionOutput) > 0

        opAnnotations = self.annotationsApplet.topLevelOperator
        annotations_ready = features_ready and \
                           len(opAnnotations.Labels) > 0 and \
                           opAnnotations.Labels.ready() and \
                           opAnnotations.TrackImage.ready()

        opStructuredTracking = self.trackingApplet.topLevelOperator
        structured_tracking_ready = objectCountClassifier_ready

        withIlpSolver = (self._solver=="ILP")

        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.annotationsApplet.busy
        # busy |= self.dataExportAnnotationsApplet.busy
        busy |= self.trackingApplet.busy
        busy |= self.dataExportTrackingApplet.busy

        self._shell.enableProjectChanges( not busy )

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        if not self.fromBinary:
            self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet, input_ready and not busy)
        self._shell.setAppletEnabled(self.trackingFeatureExtractionApplet, thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.cellClassificationApplet, tracking_features_ready and not busy)
        self._shell.setAppletEnabled(self.divisionDetectionApplet, tracking_features_ready and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet, not busy)
        self._shell.setAppletEnabled(self.annotationsApplet, features_ready and not busy) # and withIlpSolver)
        # self._shell.setAppletEnabled(self.dataExportAnnotationsApplet, annotations_ready and not busy and \
        #                                 self.dataExportAnnotationsApplet.topLevelOperator.Inputs[0][0].ready() )
        self._shell.setAppletEnabled(self.trackingApplet, objectCountClassifier_ready and not busy)
        self._shell.setAppletEnabled(self.dataExportTrackingApplet, structured_tracking_ready and not busy and \
                                    self.dataExportTrackingApplet.topLevelOperator.Inputs[0][0].ready() )
Beispiel #15
0
    def __init__(self, shell, headless, workflow_cmdline_args,
                 project_creation_args, *args, **kwargs):
        graph = kwargs["graph"] if "graph" in kwargs else Graph()
        if "graph" in kwargs:
            del kwargs["graph"]
        # if 'withOptTrans' in kwargs:
        #     self.withOptTrans = kwargs['withOptTrans']
        # if 'fromBinary' in kwargs:
        #     self.fromBinary = kwargs['fromBinary']
        super(ConservationTrackingWorkflowBase,
              self).__init__(shell,
                             headless,
                             workflow_cmdline_args,
                             project_creation_args,
                             graph=graph,
                             *args,
                             **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Variables to store division and cell classifiers to prevent retraining every-time batch processing runs
        self.stored_division_classifier = None
        self.stored_cell_classifier = None

        ## Create applets
        self.dataSelectionApplet = DataSelectionApplet(
            self,
            "Input Data",
            "Input Data",
            forceAxisOrder=["txyzc"],
            instructionText=data_instructions,
            max_lanes=None,
        )

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue(
                ["Raw Data", "Segmentation Image"])
        else:
            opDataSelection.DatasetRoles.setValue(
                ["Raw Data", "Prediction Maps"])

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet(
                self, "Threshold and Size Filter", "ThresholdTwoLevels")

        self.objectExtractionApplet = TrackingFeatureExtractionApplet(
            workflow=self,
            interactive=False,
            name="Object Feature Computation")

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator

        self.divisionDetectionApplet = self._createDivisionDetectionApplet(
            configConservation.selectedFeaturesDiv)  # Might be None

        if self.divisionDetectionApplet:
            feature_dict_division = {}
            feature_dict_division[config.features_division_name] = {
                name: {}
                for name in config.division_features
            }
            opObjectExtraction.FeatureNamesDivision.setValue(
                feature_dict_division)

            selected_features_div = {}
            for plugin_name in list(config.selected_features_division.keys()):
                selected_features_div[plugin_name] = {
                    name: {}
                    for name in config.selected_features_division[plugin_name]
                }
            # FIXME: do not hard code this
            for name in [
                    "SquaredDistances_" + str(i)
                    for i in range(config.n_best_successors)
            ]:
                selected_features_div[config.features_division_name][name] = {}

            opDivisionDetection = self.divisionDetectionApplet.topLevelOperator
            opDivisionDetection.SelectedFeatures.setValue(
                configConservation.selectedFeaturesDiv)
            opDivisionDetection.LabelNames.setValue(
                ["Not Dividing", "Dividing"])
            opDivisionDetection.AllowDeleteLabels.setValue(False)
            opDivisionDetection.AllowAddLabel.setValue(False)
            opDivisionDetection.EnableLabelTransfer.setValue(False)

        self.cellClassificationApplet = ObjectClassificationApplet(
            workflow=self,
            name="Object Count Classification",
            projectFileGroupName="CountClassification",
            selectedFeatures=configConservation.selectedFeaturesObjectCount,
        )

        selected_features_objectcount = {}
        for plugin_name in list(config.selected_features_objectcount.keys()):
            selected_features_objectcount[plugin_name] = {
                name: {}
                for name in config.selected_features_objectcount[plugin_name]
            }

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(
            configConservation.selectedFeaturesObjectCount)
        opCellClassification.SuggestedLabelNames.setValue(
            ["False Detection"] + [str(1) + " Object"] +
            [str(i) + " Objects" for i in range(2, 10)])
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

        self.trackingApplet = ConservationTrackingApplet(workflow=self)

        self.default_export_filename = "{dataset_dir}/{nickname}-exported_data.csv"
        self.dataExportApplet = TrackingBaseDataExportApplet(
            self,
            "Tracking Result Export",
            default_export_filename=self.default_export_filename,
            pluginExportFunc=self._pluginExportFunc,
        )

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue(
            ["Object-Identities", "Tracking-Result", "Merger-Result"])
        opDataExport.WorkingDirectory.connect(opDataSelection.WorkingDirectory)

        # Extra configuration for object export table (as CSV table or HDF5 table)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet.set_exporting_operator(opTracking)
        self.dataExportApplet.prepare_lane_for_export = self.prepare_lane_for_export

        # configure export settings
        # settings = {'file path': self.default_export_filename, 'compression': {}, 'file type': 'csv'}
        # selected_features = ['Count', 'RegionCenter', 'RegionRadii', 'RegionAxes']
        # opTracking.ExportSettings.setValue( (settings, selected_features) )

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.objectExtractionApplet)

        if self.divisionDetectionApplet:
            self._applets.append(self.divisionDetectionApplet)

        self.batchProcessingApplet = BatchProcessingApplet(
            self, "Batch Processing", self.dataSelectionApplet,
            self.dataExportApplet)

        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
        self._applets.append(self.batchProcessingApplet)

        # Parse export and batch command-line arguments for headless mode
        if workflow_cmdline_args:
            self._data_export_args, unused_args = self.dataExportApplet.parse_known_cmdline_args(
                workflow_cmdline_args)
            self._batch_input_args, unused_args = self.batchProcessingApplet.parse_known_cmdline_args(
                workflow_cmdline_args)

        else:
            unused_args = None
            self._data_export_args = None
            self._batch_input_args = None

        if unused_args:
            logger.warning("Unused command-line args: {}".format(unused_args))
Beispiel #16
0
class StructuredTrackingWorkflowBase( Workflow ):
    workflowName = "Structured Learning Tracking Workflow BASE"

    @property
    def applets(self):
        return self._applets

    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']

        super(StructuredTrackingWorkflowBase, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Create applets
        self.dataSelectionApplet = DataSelectionApplet(self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            forceAxisOrder=['txyzc'],
            instructionText=data_instructions,
            max_lanes=1)

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Binary Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self,"Threshold and Size Filter","ThresholdTwoLevels" )

        self.divisionDetectionApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Division Detection (optional)",
                                                                     projectFileGroupName="DivisionDetection",
                                                                     selectedFeatures=configConservation.selectedFeaturesDiv)

        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification",
                                                                     projectFileGroupName="CountClassification",
                                                                     selectedFeatures=configConservation.selectedFeaturesObjectCount)

        self.trackingFeatureExtractionApplet = TrackingFeatureExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.annotationsApplet = AnnotationsApplet( name="Training", workflow=self )
        opAnnotations = self.annotationsApplet.topLevelOperator

        self.trackingApplet = StructuredTrackingApplet( name="Tracking - Structured Learning", workflow=self )
        opStructuredTracking = self.trackingApplet.topLevelOperator

        if SOLVER=="CPLEX" or SOLVER=="GUROBI":
            self._solver="ILP"
        elif SOLVER=="DPCT":
            self._solver="Flow-based"
        else:
            self._solver=None
        opStructuredTracking._solver = self._solver

        self.default_tracking_export_filename = '{dataset_dir}/{nickname}-tracking_exported_data.csv'
        self.dataExportTrackingApplet = TrackingBaseDataExportApplet(self, "Tracking Result Export",default_export_filename=self.default_tracking_export_filename)
        opDataExportTracking = self.dataExportTrackingApplet.topLevelOperator
        opDataExportTracking.SelectionNames.setValue( ['Tracking-Result', 'Merger-Result', 'Object-Identities'] )
        opDataExportTracking.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        self.dataExportTrackingApplet.set_exporting_operator(opStructuredTracking)
        self.dataExportTrackingApplet.prepare_lane_for_export = self.prepare_lane_for_export
        self.dataExportTrackingApplet.post_process_lane_export = self.post_process_lane_export

        # configure export settings
        settings = {'file path': self.default_tracking_export_filename, 'compression': {}, 'file type': 'h5'}
        selected_features = ['Count', 'RegionCenter', 'RegionRadii', 'RegionAxes']
        opStructuredTracking.ExportSettings.setValue( (settings, selected_features) )

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.trackingFeatureExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)

        self.batchProcessingApplet = BatchProcessingApplet(self, "Batch Processing", self.dataSelectionApplet, self.dataExportTrackingApplet)

        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.annotationsApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportTrackingApplet)

        if self.divisionDetectionApplet:
            opDivDetection = self.divisionDetectionApplet.topLevelOperator
            opDivDetection.SelectedFeatures.setValue(configConservation.selectedFeaturesDiv)
            opDivDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])
            opDivDetection.AllowDeleteLabels.setValue(False)
            opDivDetection.AllowAddLabel.setValue(False)
            opDivDetection.EnableLabelTransfer.setValue(False)

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(configConservation.selectedFeaturesObjectCount )
        opCellClassification.SuggestedLabelNames.setValue( ['False Detection',] + [str(1) + ' Object'] + [str(i) + ' Objects' for i in range(2,10) ] )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

        if workflow_cmdline_args:

            if '--testFullAnnotations' in workflow_cmdline_args:
                self.testFullAnnotations = True
            else:
                self.testFullAnnotations = False

            self._data_export_args, unused_args = self.dataExportTrackingApplet.parse_known_cmdline_args( workflow_cmdline_args )
            self._batch_input_args, unused_args = self.batchProcessingApplet.parse_known_cmdline_args( workflow_cmdline_args )
        else:
            unused_args = None
            self._data_export_args = None
            self._batch_input_args = None
            self.testFullAnnotations = False

        if unused_args:
            logger.warning("Unused command-line args: {}".format( unused_args ))

    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(laneIndex)
        opTrackingFeatureExtraction = self.trackingFeatureExtractionApplet.topLevelOperator.getLane(laneIndex)

        opAnnotations = self.annotationsApplet.topLevelOperator.getLane(laneIndex)
        if not self.fromBinary:
            opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(laneIndex)

        opStructuredTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opDataTrackingExport = self.dataExportTrackingApplet.topLevelOperator.getLane(laneIndex)

        ## Connect operators ##
        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])

        opDivDetection = self.divisionDetectionApplet.topLevelOperator.getLane(laneIndex)
        opCellClassification = self.cellClassificationApplet.topLevelOperator.getLane(laneIndex)

        if not self.fromBinary:
            opTwoLevelThreshold.InputImage.connect( opData.ImageGroup[1] )
            opTwoLevelThreshold.RawInput.connect( opData.ImageGroup[0] ) # Used for display only
            binarySrc = opTwoLevelThreshold.CachedOutput
        else:
            binarySrc = opData.ImageGroup[1]
        # Use Op5ifyers for both input datasets such that they are guaranteed to
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(binarySrc)

        opObjExtraction.RawImage.connect( op5Raw.Output )
        opObjExtraction.BinaryImage.connect( op5Binary.Output )

        opTrackingFeatureExtraction.RawImage.connect( op5Raw.Output )
        opTrackingFeatureExtraction.BinaryImage.connect( op5Binary.Output )

        opTrackingFeatureExtraction.setDefaultFeatures(configConservation.allFeaturesObjectCount)
        opTrackingFeatureExtraction.FeatureNamesVigra.setValue(configConservation.allFeaturesObjectCount)
        feature_dict_division = {}
        feature_dict_division[config.features_division_name] = { name: {} for name in config.division_features }
        opTrackingFeatureExtraction.FeatureNamesDivision.setValue(feature_dict_division)

        if self.divisionDetectionApplet:
            opDivDetection.BinaryImages.connect( op5Binary.Output )
            opDivDetection.RawImages.connect( op5Raw.Output )
            opDivDetection.SegmentationImages.connect(opTrackingFeatureExtraction.LabelImage)
            opDivDetection.ObjectFeatures.connect(opTrackingFeatureExtraction.RegionFeaturesAll)
            opDivDetection.ComputedFeatureNames.connect(opTrackingFeatureExtraction.ComputedFeatureNamesAll)

        opCellClassification.BinaryImages.connect( op5Binary.Output )
        opCellClassification.RawImages.connect( op5Raw.Output )
        opCellClassification.SegmentationImages.connect(opTrackingFeatureExtraction.LabelImage)
        opCellClassification.ObjectFeatures.connect(opTrackingFeatureExtraction.RegionFeaturesAll)
        opCellClassification.ComputedFeatureNames.connect(opTrackingFeatureExtraction.ComputedFeatureNamesNoDivisions)

        opAnnotations.RawImage.connect( op5Raw.Output )
        opAnnotations.BinaryImage.connect( op5Binary.Output )
        opAnnotations.LabelImage.connect( opObjExtraction.LabelImage )
        opAnnotations.ObjectFeatures.connect( opObjExtraction.RegionFeatures )
        opAnnotations.ComputedFeatureNames.connect(opObjExtraction.Features)
        opAnnotations.DivisionProbabilities.connect( opDivDetection.Probabilities )
        opAnnotations.DetectionProbabilities.connect( opCellClassification.Probabilities )
        opAnnotations.MaxNumObj.connect (opCellClassification.MaxNumObj)

        opStructuredTracking.RawImage.connect( op5Raw.Output )
        opStructuredTracking.LabelImage.connect( opTrackingFeatureExtraction.LabelImage )
        opStructuredTracking.ObjectFeatures.connect( opTrackingFeatureExtraction.RegionFeaturesVigra )
        opStructuredTracking.ComputedFeatureNames.connect( opTrackingFeatureExtraction.FeatureNamesVigra )

        if self.divisionDetectionApplet:
            opStructuredTracking.ObjectFeaturesWithDivFeatures.connect( opTrackingFeatureExtraction.RegionFeaturesAll)
            opStructuredTracking.ComputedFeatureNamesWithDivFeatures.connect( opTrackingFeatureExtraction.ComputedFeatureNamesAll )
            opStructuredTracking.DivisionProbabilities.connect( opDivDetection.Probabilities )

        opStructuredTracking.DetectionProbabilities.connect( opCellClassification.Probabilities )
        opStructuredTracking.NumLabels.connect( opCellClassification.NumLabels )
        opStructuredTracking.Annotations.connect (opAnnotations.Annotations)
        opStructuredTracking.Labels.connect (opAnnotations.Labels)
        opStructuredTracking.Divisions.connect (opAnnotations.Divisions)
        opStructuredTracking.Appearances.connect (opAnnotations.Appearances)
        opStructuredTracking.Disappearances.connect (opAnnotations.Disappearances)
        opStructuredTracking.MaxNumObj.connect (opCellClassification.MaxNumObj)

        opDataTrackingExport.Inputs.resize(3)
        opDataTrackingExport.Inputs[0].connect( opStructuredTracking.RelabeledImage )
        opDataTrackingExport.Inputs[1].connect( opStructuredTracking.MergerOutput )
        opDataTrackingExport.Inputs[2].connect( opStructuredTracking.LabelImage )
        opDataTrackingExport.RawData.connect( op5Raw.Output )
        opDataTrackingExport.RawDatasetInfo.connect( opData.DatasetGroup[0] )

    def prepare_lane_for_export(self, lane_index):
        import logging
        logger = logging.getLogger(__name__)

        maxt = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[0]
        maxx = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[1]
        maxy = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[2]
        maxz = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[3]
        time_enum = list(range(maxt))
        x_range = (0, maxx)
        y_range = (0, maxy)
        z_range = (0, maxz)

        ndim = 2
        if ( z_range[1] - z_range[0] ) > 1:
            ndim = 3

        parameters = self.trackingApplet.topLevelOperator.Parameters.value
        # Save state of axis ranges
        if 'time_range' in parameters:
            self.prev_time_range = parameters['time_range']
        else:
            self.prev_time_range = time_enum

        if 'x_range' in parameters:
            self.prev_x_range = parameters['x_range']
        else:
            self.prev_x_range = x_range

        if 'y_range' in parameters:
            self.prev_y_range = parameters['y_range']
        else:
            self.prev_y_range = y_range

        if 'z_range' in parameters:
            self.prev_z_range = parameters['z_range']
        else:
            self.prev_z_range = z_range

        # batch processing starts a new lane, so training data needs to be copied from the lane that loaded the project
        loaded_project_lane_index=0
        self.annotationsApplet.topLevelOperator[lane_index].Annotations.setValue(
            self.trackingApplet.topLevelOperator[loaded_project_lane_index].Annotations.value)

        def runLearningAndTracking(withMergerResolution=True):
            if self.testFullAnnotations:
                logger.info("Test: Structured Learning")
                weights = self.trackingApplet.topLevelOperator[lane_index]._runStructuredLearning(
                    z_range,
                    parameters['maxObj'],
                    parameters['max_nearest_neighbors'],
                    parameters['maxDist'],
                    parameters['divThreshold'],
                    [parameters['scales'][0],parameters['scales'][1],parameters['scales'][2]],
                    parameters['size_range'],
                    parameters['withDivisions'],
                    parameters['borderAwareWidth'],
                    parameters['withClassifierPrior'],
                    withBatchProcessing=True)
                logger.info("weights: {}".format(weights))

            logger.info("Test: Tracking")
            result = self.trackingApplet.topLevelOperator[lane_index].track(
                time_range = time_enum,
                x_range = x_range,
                y_range = y_range,
                z_range = z_range,
                size_range = parameters['size_range'],
                x_scale = parameters['scales'][0],
                y_scale = parameters['scales'][1],
                z_scale = parameters['scales'][2],
                maxDist=parameters['maxDist'],
                maxObj = parameters['maxObj'],
                divThreshold=parameters['divThreshold'],
                avgSize=parameters['avgSize'],
                withTracklets=parameters['withTracklets'],
                sizeDependent=parameters['sizeDependent'],
                detWeight=parameters['detWeight'],
                divWeight=parameters['divWeight'],
                transWeight=parameters['transWeight'],
                withDivisions=parameters['withDivisions'],
                withOpticalCorrection=parameters['withOpticalCorrection'],
                withClassifierPrior=parameters['withClassifierPrior'],
                ndim=ndim,
                withMergerResolution=withMergerResolution,
                borderAwareWidth = parameters['borderAwareWidth'],
                withArmaCoordinates = parameters['withArmaCoordinates'],
                cplex_timeout = parameters['cplex_timeout'],
                appearance_cost = parameters['appearanceCost'],
                disappearance_cost = parameters['disappearanceCost'],
                force_build_hypotheses_graph = False,
                withBatchProcessing = True
            )

            return result

        if self.testFullAnnotations:

            self.result = runLearningAndTracking(withMergerResolution=False)

            hypothesesGraph = self.trackingApplet.topLevelOperator[lane_index].LearningHypothesesGraph.value
            hypothesesGraph.insertSolution(self.result)
            hypothesesGraph.computeLineage()
            solution = hypothesesGraph.getSolutionDictionary()
            annotations = self.trackingApplet.topLevelOperator[lane_index].Annotations.value

            self.trackingApplet.topLevelOperator[lane_index].insertAnnotationsToHypothesesGraph(hypothesesGraph,annotations,misdetectionLabel=-1)
            hypothesesGraph.computeLineage()
            solutionFromAnnotations = hypothesesGraph.getSolutionDictionary()

            for key in list(solution.keys()):
                if key == 'detectionResults':
                    detectionFlag = True
                    for i in range(len(solution[key])):
                        flag = False
                        for j in range(len(solutionFromAnnotations[key])):
                            if solution[key][i]['id'] == solutionFromAnnotations[key][j]['id'] and \
                                solution[key][i]['value'] == solutionFromAnnotations[key][j]['value']:
                                flag = True
                                break
                        detectionFlag &= flag
                elif key == 'divisionResults':
                    divisionFlag = True
                    for i in range(len(solution[key])):
                        flag = False
                        for j in range(len(solutionFromAnnotations[key])):
                            if solution[key][i]['id'] == solutionFromAnnotations[key][j]['id'] and \
                                solution[key][i]['value'] == solutionFromAnnotations[key][j]['value']:
                                flag = True
                                break
                        divisionFlag &= flag
                elif key == 'linkingResults':
                    linkingFlag = True
                    for i in range(len(solution[key])):
                        flag = False
                        for j in range(len(solutionFromAnnotations[key])):
                            if solution[key][i]['dest'] == solutionFromAnnotations[key][j]['dest'] and \
                                solution[key][i]['src'] == solutionFromAnnotations[key][j]['src']:
                                if solution[key][i]['gap'] == solutionFromAnnotations[key][j]['gap'] and \
                                    solution[key][i]['value'] == solutionFromAnnotations[key][j]['value']:
                                    flag = True
                                    break
                        linkingFlag &= flag

            assert detectionFlag, "Detection results are NOT correct. They differ from your annotated detections."
            logger.info("Detection results are correct.")
            assert divisionFlag, "Division results are NOT correct. They differ from your annotated divisions."
            logger.info("Division results are correct.")
            assert linkingFlag, "Transition results are NOT correct. They differ from your annotated transitions."
            logger.info("Transition results are correct.")
        self.result = runLearningAndTracking(withMergerResolution=parameters['withMergerResolution'])

    def post_process_lane_export(self, lane_index, checkOverwriteFiles=False):
        # Plugin export if selected
        logger.info("Export source is: " + self.dataExportTrackingApplet.topLevelOperator.SelectedExportSource.value)

        print("in post_process_lane_export")
        if self.dataExportTrackingApplet.topLevelOperator.SelectedExportSource.value == OpTrackingBaseDataExport.PluginOnlyName:
            logger.info("Export source plugin selected!")
            selectedPlugin = self.dataExportTrackingApplet.topLevelOperator.SelectedPlugin.value
            additionalPluginArgumentsSlot = self.dataExportTrackingApplet.topLevelOperator.AdditionalPluginArguments

            exportPluginInfo = pluginManager.getPluginByName(selectedPlugin, category="TrackingExportFormats")
            if exportPluginInfo is None:
                logger.error("Could not find selected plugin %s" % exportPluginInfo)
            else:
                exportPlugin = exportPluginInfo.plugin_object
                logger.info("Exporting tracking result using %s" % selectedPlugin)
                name_format = self.dataExportTrackingApplet.topLevelOperator.getLane(lane_index).OutputFilenameFormat.value
                partially_formatted_name = self.getPartiallyFormattedName(lane_index, name_format)

                if exportPlugin.exportsToFile:
                    filename = partially_formatted_name
                    if os.path.basename(filename) == '':
                        filename = os.path.join(filename, 'pluginExport.txt')
                else:
                    filename = partially_formatted_name

                if filename is None or len(str(filename)) == 0:
                    logger.error("Cannot export from plugin with empty output filename")
                    return True

                self.dataExportTrackingApplet.progressSignal(-1)
                exportStatus = self.trackingApplet.topLevelOperator.getLane(lane_index).exportPlugin(
                    filename, exportPlugin, checkOverwriteFiles, additionalPluginArgumentsSlot)
                self.dataExportTrackingApplet.progressSignal(100)

                if not exportStatus:
                    return False
                logger.info("Export done")

            return True

        return True

    def getPartiallyFormattedName(self, lane_index, path_format_string):
        ''' Takes the format string for the output file, fills in the most important placeholders, and returns it '''
        raw_dataset_info = self.dataSelectionApplet.topLevelOperator.DatasetGroup[lane_index][0].value
        project_path = self.shell.projectManager.currentProjectPath
        project_dir = os.path.dirname(project_path)
        dataset_dir = PathComponents(raw_dataset_info.filePath).externalDirectory
        abs_dataset_dir = make_absolute(dataset_dir, cwd=project_dir)
        known_keys = {}
        known_keys['dataset_dir'] = abs_dataset_dir
        nickname = raw_dataset_info.nickname.replace('*', '')
        if os.path.pathsep in nickname:
            nickname = PathComponents(nickname.split(os.path.pathsep)[0]).fileNameBase
        known_keys['nickname'] = nickname
        known_keys['result_type'] = self.dataExportTrackingApplet.topLevelOperator.SelectedPlugin._value
        # use partial formatting to fill in non-coordinate name fields
        partially_formatted_name = format_known_keys(path_format_string, known_keys)
        return partially_formatted_name

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False
        return input_ready

    def onProjectLoaded(self, projectManager):
        """
        Overridden from Workflow base class.  Called by the Project Manager.

        If the user provided command-line arguments, use them to configure
        the workflow inputs and output settings.
        """

        # Configure the data export operator.
        if self._data_export_args:
            self.dataExportTrackingApplet.configure_operator_with_parsed_args( self._data_export_args )

        # Configure headless mode.
        if self._headless and self._batch_input_args and self._data_export_args:
            logger.info("Beginning Batch Processing")
            self.batchProcessingApplet.run_export_from_parsed_args(self._batch_input_args)
            logger.info("Completed Batch Processing")

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        if not self.fromBinary:
            opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
            thresholdingOutput = opThresholding.CachedOutput
            thresholding_ready = input_ready and len(thresholdingOutput) > 0
        else:
            thresholding_ready = input_ready

        opTrackingFeatureExtraction = self.trackingFeatureExtractionApplet.topLevelOperator
        trackingFeatureExtractionOutput = opTrackingFeatureExtraction.ComputedFeatureNamesAll
        tracking_features_ready = thresholding_ready and len(trackingFeatureExtractionOutput) > 0

        objectCountClassifier_ready = tracking_features_ready

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.RegionFeatures
        features_ready = thresholding_ready and \
                         len(objectExtractionOutput) > 0

        opAnnotations = self.annotationsApplet.topLevelOperator
        annotations_ready = features_ready and \
                           len(opAnnotations.Labels) > 0 and \
                           opAnnotations.Labels.ready() and \
                           opAnnotations.TrackImage.ready()

        opStructuredTracking = self.trackingApplet.topLevelOperator
        structured_tracking_ready = objectCountClassifier_ready

        withIlpSolver = (self._solver=="ILP")

        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.annotationsApplet.busy
        # busy |= self.dataExportAnnotationsApplet.busy
        busy |= self.trackingApplet.busy
        busy |= self.dataExportTrackingApplet.busy

        self._shell.enableProjectChanges( not busy )

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        if not self.fromBinary:
            self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet, input_ready and not busy)
        self._shell.setAppletEnabled(self.trackingFeatureExtractionApplet, thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.cellClassificationApplet, tracking_features_ready and not busy)
        self._shell.setAppletEnabled(self.divisionDetectionApplet, tracking_features_ready and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet, not busy)
        self._shell.setAppletEnabled(self.annotationsApplet, features_ready and not busy) # and withIlpSolver)
        # self._shell.setAppletEnabled(self.dataExportAnnotationsApplet, annotations_ready and not busy and \
        #                                 self.dataExportAnnotationsApplet.topLevelOperator.Inputs[0][0].ready() )
        self._shell.setAppletEnabled(self.trackingApplet, objectCountClassifier_ready and not busy)
        self._shell.setAppletEnabled(self.dataExportTrackingApplet, structured_tracking_ready and not busy and \
                                    self.dataExportTrackingApplet.topLevelOperator.Inputs[0][0].ready() )
Beispiel #17
0
class StructuredTrackingWorkflowBase(Workflow):
    workflowName = "Structured Learning Tracking Workflow BASE"

    @property
    def applets(self):
        return self._applets

    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def __init__(self, shell, headless, workflow_cmdline_args,
                 project_creation_args, *args, **kwargs):
        graph = kwargs["graph"] if "graph" in kwargs else Graph()
        if "graph" in kwargs:
            del kwargs["graph"]

        super(StructuredTrackingWorkflowBase,
              self).__init__(shell,
                             headless,
                             workflow_cmdline_args,
                             project_creation_args,
                             graph=graph,
                             *args,
                             **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Create applets
        self.dataSelectionApplet = DataSelectionApplet(
            self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            forceAxisOrder=["txyzc"],
            instructionText=data_instructions,
            max_lanes=1,
        )

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue(["Raw Data", "Binary Image"])
        else:
            opDataSelection.DatasetRoles.setValue(
                ["Raw Data", "Prediction Maps"])

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet(
                self, "Threshold and Size Filter", "ThresholdTwoLevels")

        self.divisionDetectionApplet = ObjectClassificationApplet(
            workflow=self,
            name="Division Detection (optional)",
            projectFileGroupName="DivisionDetection",
            selectedFeatures=configConservation.selectedFeaturesDiv,
        )

        self.cellClassificationApplet = ObjectClassificationApplet(
            workflow=self,
            name="Object Count Classification",
            projectFileGroupName="CountClassification",
            selectedFeatures=configConservation.selectedFeaturesObjectCount,
        )

        self.trackingFeatureExtractionApplet = TrackingFeatureExtractionApplet(
            name="Object Feature Computation",
            workflow=self,
            interactive=False)

        self.objectExtractionApplet = ObjectExtractionApplet(
            name="Object Feature Computation",
            workflow=self,
            interactive=False)

        self.annotationsApplet = AnnotationsApplet(name="Training",
                                                   workflow=self)
        opAnnotations = self.annotationsApplet.topLevelOperator

        self.trackingApplet = StructuredTrackingApplet(
            name="Tracking - Structured Learning", workflow=self)
        opStructuredTracking = self.trackingApplet.topLevelOperator

        if SOLVER == "CPLEX" or SOLVER == "GUROBI":
            self._solver = "ILP"
        elif SOLVER == "DPCT":
            self._solver = "Flow-based"
        else:
            self._solver = None
        opStructuredTracking._solver = self._solver

        self.default_tracking_export_filename = "{dataset_dir}/{nickname}-tracking_exported_data.csv"
        self.dataExportTrackingApplet = TrackingBaseDataExportApplet(
            self,
            "Tracking Result Export",
            default_export_filename=self.default_tracking_export_filename,
            pluginExportFunc=self._pluginExportFunc,
        )
        opDataExportTracking = self.dataExportTrackingApplet.topLevelOperator
        opDataExportTracking.SelectionNames.setValue(
            ["Tracking-Result", "Merger-Result", "Object-Identities"])
        opDataExportTracking.WorkingDirectory.connect(
            opDataSelection.WorkingDirectory)
        self.dataExportTrackingApplet.set_exporting_operator(
            opStructuredTracking)
        self.dataExportTrackingApplet.prepare_lane_for_export = self.prepare_lane_for_export

        # configure export settings
        settings = {
            "file path": self.default_tracking_export_filename,
            "compression": {},
            "file type": "h5"
        }
        selected_features = [
            "Count", "RegionCenter", "RegionRadii", "RegionAxes"
        ]
        opStructuredTracking.ExportSettings.setValue(
            (settings, selected_features))

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.trackingFeatureExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)

        self.batchProcessingApplet = BatchProcessingApplet(
            self, "Batch Processing", self.dataSelectionApplet,
            self.dataExportTrackingApplet)

        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.annotationsApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportTrackingApplet)

        if self.divisionDetectionApplet:
            opDivDetection = self.divisionDetectionApplet.topLevelOperator
            opDivDetection.SelectedFeatures.setValue(
                configConservation.selectedFeaturesDiv)
            opDivDetection.LabelNames.setValue(["Not Dividing", "Dividing"])
            opDivDetection.AllowDeleteLabels.setValue(False)
            opDivDetection.AllowAddLabel.setValue(False)
            opDivDetection.EnableLabelTransfer.setValue(False)

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(
            configConservation.selectedFeaturesObjectCount)
        opCellClassification.SuggestedLabelNames.setValue(
            ["False Detection"] + [str(1) + " Object"] +
            [str(i) + " Objects" for i in range(2, 10)])
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

        if workflow_cmdline_args:

            if "--testFullAnnotations" in workflow_cmdline_args:
                self.testFullAnnotations = True
            else:
                self.testFullAnnotations = False

            self._data_export_args, unused_args = self.dataExportTrackingApplet.parse_known_cmdline_args(
                workflow_cmdline_args)
            self._batch_input_args, unused_args = self.batchProcessingApplet.parse_known_cmdline_args(
                workflow_cmdline_args)
        else:
            unused_args = None
            self._data_export_args = None
            self._batch_input_args = None
            self.testFullAnnotations = False

        if unused_args:
            logger.warning("Unused command-line args: {}".format(unused_args))

    def _pluginExportFunc(self, lane_index, filename, exportPlugin,
                          checkOverwriteFiles, plugArgsSlot) -> int:
        return self.trackingApplet.topLevelOperator.getLane(
            lane_index).exportPlugin(filename, exportPlugin,
                                     checkOverwriteFiles, plugArgsSlot)

    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(
            laneIndex)
        opTrackingFeatureExtraction = self.trackingFeatureExtractionApplet.topLevelOperator.getLane(
            laneIndex)

        opAnnotations = self.annotationsApplet.topLevelOperator.getLane(
            laneIndex)
        if not self.fromBinary:
            opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(
                laneIndex)

        opStructuredTracking = self.trackingApplet.topLevelOperator.getLane(
            laneIndex)
        opDataTrackingExport = self.dataExportTrackingApplet.topLevelOperator.getLane(
            laneIndex)

        ## Connect operators ##
        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])

        opDivDetection = self.divisionDetectionApplet.topLevelOperator.getLane(
            laneIndex)
        opCellClassification = self.cellClassificationApplet.topLevelOperator.getLane(
            laneIndex)

        if not self.fromBinary:
            opTwoLevelThreshold.InputImage.connect(opData.ImageGroup[1])
            opTwoLevelThreshold.RawInput.connect(
                opData.ImageGroup[0])  # Used for display only
            binarySrc = opTwoLevelThreshold.CachedOutput
        else:
            binarySrc = opData.ImageGroup[1]
        # Use Op5ifyers for both input datasets such that they are guaranteed to
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(binarySrc)

        opObjExtraction.RawImage.connect(op5Raw.Output)
        opObjExtraction.BinaryImage.connect(op5Binary.Output)

        opTrackingFeatureExtraction.RawImage.connect(op5Raw.Output)
        opTrackingFeatureExtraction.BinaryImage.connect(op5Binary.Output)

        opTrackingFeatureExtraction.setDefaultFeatures(
            configConservation.allFeaturesObjectCount)
        opTrackingFeatureExtraction.FeatureNamesVigra.setValue(
            configConservation.allFeaturesObjectCount)
        feature_dict_division = {}
        feature_dict_division[config.features_division_name] = {
            name: {}
            for name in config.division_features
        }
        opTrackingFeatureExtraction.FeatureNamesDivision.setValue(
            feature_dict_division)

        if self.divisionDetectionApplet:
            opDivDetection.BinaryImages.connect(op5Binary.Output)
            opDivDetection.RawImages.connect(op5Raw.Output)
            opDivDetection.SegmentationImages.connect(
                opTrackingFeatureExtraction.LabelImage)
            opDivDetection.ObjectFeatures.connect(
                opTrackingFeatureExtraction.RegionFeaturesAll)
            opDivDetection.ComputedFeatureNames.connect(
                opTrackingFeatureExtraction.ComputedFeatureNamesAll)

        opCellClassification.BinaryImages.connect(op5Binary.Output)
        opCellClassification.RawImages.connect(op5Raw.Output)
        opCellClassification.SegmentationImages.connect(
            opTrackingFeatureExtraction.LabelImage)
        opCellClassification.ObjectFeatures.connect(
            opTrackingFeatureExtraction.RegionFeaturesAll)
        opCellClassification.ComputedFeatureNames.connect(
            opTrackingFeatureExtraction.ComputedFeatureNamesNoDivisions)

        opAnnotations.RawImage.connect(op5Raw.Output)
        opAnnotations.BinaryImage.connect(op5Binary.Output)
        opAnnotations.LabelImage.connect(opObjExtraction.LabelImage)
        opAnnotations.ObjectFeatures.connect(opObjExtraction.RegionFeatures)
        opAnnotations.ComputedFeatureNames.connect(opObjExtraction.Features)
        opAnnotations.DivisionProbabilities.connect(
            opDivDetection.Probabilities)
        opAnnotations.DetectionProbabilities.connect(
            opCellClassification.Probabilities)
        opAnnotations.MaxNumObj.connect(opCellClassification.MaxNumObj)

        opStructuredTracking.RawImage.connect(op5Raw.Output)
        opStructuredTracking.LabelImage.connect(
            opTrackingFeatureExtraction.LabelImage)
        opStructuredTracking.ObjectFeatures.connect(
            opTrackingFeatureExtraction.RegionFeaturesVigra)
        opStructuredTracking.ComputedFeatureNames.connect(
            opTrackingFeatureExtraction.FeatureNamesVigra)

        if self.divisionDetectionApplet:
            opStructuredTracking.ObjectFeaturesWithDivFeatures.connect(
                opTrackingFeatureExtraction.RegionFeaturesAll)
            opStructuredTracking.ComputedFeatureNamesWithDivFeatures.connect(
                opTrackingFeatureExtraction.ComputedFeatureNamesAll)
            opStructuredTracking.DivisionProbabilities.connect(
                opDivDetection.Probabilities)

        opStructuredTracking.DetectionProbabilities.connect(
            opCellClassification.Probabilities)
        opStructuredTracking.NumLabels.connect(opCellClassification.NumLabels)
        opStructuredTracking.Annotations.connect(opAnnotations.Annotations)
        opStructuredTracking.Labels.connect(opAnnotations.Labels)
        opStructuredTracking.Divisions.connect(opAnnotations.Divisions)
        opStructuredTracking.Appearances.connect(opAnnotations.Appearances)
        opStructuredTracking.Disappearances.connect(
            opAnnotations.Disappearances)
        opStructuredTracking.MaxNumObj.connect(opCellClassification.MaxNumObj)

        opDataTrackingExport.Inputs.resize(3)
        opDataTrackingExport.Inputs[0].connect(opStructuredTracking.Output)
        opDataTrackingExport.Inputs[1].connect(
            opStructuredTracking.MergerOutput)
        opDataTrackingExport.Inputs[2].connect(
            opStructuredTracking.RelabeledImage)
        opDataTrackingExport.RawData.connect(op5Raw.Output)
        opDataTrackingExport.RawDatasetInfo.connect(opData.DatasetGroup[0])

    def prepare_lane_for_export(self, lane_index):
        import logging

        logger = logging.getLogger(__name__)

        maxt = self.trackingApplet.topLevelOperator[
            lane_index].RawImage.meta.shape[0]
        maxx = self.trackingApplet.topLevelOperator[
            lane_index].RawImage.meta.shape[1]
        maxy = self.trackingApplet.topLevelOperator[
            lane_index].RawImage.meta.shape[2]
        maxz = self.trackingApplet.topLevelOperator[
            lane_index].RawImage.meta.shape[3]
        time_enum = list(range(maxt))
        x_range = (0, maxx)
        y_range = (0, maxy)
        z_range = (0, maxz)

        ndim = 2
        if (z_range[1] - z_range[0]) > 1:
            ndim = 3

        parameters = self.trackingApplet.topLevelOperator.Parameters.value
        # Save state of axis ranges
        if "time_range" in parameters:
            self.prev_time_range = parameters["time_range"]
        else:
            self.prev_time_range = time_enum

        if "x_range" in parameters:
            self.prev_x_range = parameters["x_range"]
        else:
            self.prev_x_range = x_range

        if "y_range" in parameters:
            self.prev_y_range = parameters["y_range"]
        else:
            self.prev_y_range = y_range

        if "z_range" in parameters:
            self.prev_z_range = parameters["z_range"]
        else:
            self.prev_z_range = z_range

        # batch processing starts a new lane, so training data needs to be copied from the lane that loaded the project
        loaded_project_lane_index = 0
        self.annotationsApplet.topLevelOperator[
            lane_index].Annotations.setValue(
                self.trackingApplet.
                topLevelOperator[loaded_project_lane_index].Annotations.value)

        def runLearningAndTracking(withMergerResolution=True):
            if self.testFullAnnotations:
                logger.info("Test: Structured Learning")
                weights = self.trackingApplet.topLevelOperator[
                    lane_index]._runStructuredLearning(
                        z_range,
                        parameters["maxObj"],
                        parameters["max_nearest_neighbors"],
                        parameters["maxDist"],
                        parameters["divThreshold"],
                        [
                            parameters["scales"][0], parameters["scales"][1],
                            parameters["scales"][2]
                        ],
                        parameters["size_range"],
                        parameters["withDivisions"],
                        parameters["borderAwareWidth"],
                        parameters["withClassifierPrior"],
                        withBatchProcessing=True,
                    )
                logger.info("weights: {}".format(weights))

            logger.info("Test: Tracking")
            result = self.trackingApplet.topLevelOperator[lane_index].track(
                time_range=time_enum,
                x_range=x_range,
                y_range=y_range,
                z_range=z_range,
                size_range=parameters["size_range"],
                x_scale=parameters["scales"][0],
                y_scale=parameters["scales"][1],
                z_scale=parameters["scales"][2],
                maxDist=parameters["maxDist"],
                maxObj=parameters["maxObj"],
                divThreshold=parameters["divThreshold"],
                avgSize=parameters["avgSize"],
                withTracklets=parameters["withTracklets"],
                sizeDependent=parameters["sizeDependent"],
                detWeight=parameters["detWeight"],
                divWeight=parameters["divWeight"],
                transWeight=parameters["transWeight"],
                withDivisions=parameters["withDivisions"],
                withOpticalCorrection=parameters["withOpticalCorrection"],
                withClassifierPrior=parameters["withClassifierPrior"],
                ndim=ndim,
                withMergerResolution=withMergerResolution,
                borderAwareWidth=parameters["borderAwareWidth"],
                withArmaCoordinates=parameters["withArmaCoordinates"],
                cplex_timeout=parameters["cplex_timeout"],
                appearance_cost=parameters["appearanceCost"],
                disappearance_cost=parameters["disappearanceCost"],
                force_build_hypotheses_graph=False,
                withBatchProcessing=True,
            )

            return result

        if self.testFullAnnotations:

            self.result = runLearningAndTracking(withMergerResolution=False)

            hypothesesGraph = self.trackingApplet.topLevelOperator[
                lane_index].LearningHypothesesGraph.value
            hypothesesGraph.insertSolution(self.result)
            hypothesesGraph.computeLineage()
            solution = hypothesesGraph.getSolutionDictionary()
            annotations = self.trackingApplet.topLevelOperator[
                lane_index].Annotations.value

            self.trackingApplet.topLevelOperator[
                lane_index].insertAnnotationsToHypothesesGraph(
                    hypothesesGraph, annotations, misdetectionLabel=-1)
            hypothesesGraph.computeLineage()
            solutionFromAnnotations = hypothesesGraph.getSolutionDictionary()

            for key in list(solution.keys()):
                if key == "detectionResults":
                    detectionFlag = True
                    for i in range(len(solution[key])):
                        flag = False
                        for j in range(len(solutionFromAnnotations[key])):
                            if (solution[key][i]["id"]
                                    == solutionFromAnnotations[key][j]["id"]
                                    and solution[key][i]["value"] ==
                                    solutionFromAnnotations[key][j]["value"]):
                                flag = True
                                break
                        detectionFlag &= flag
                elif key == "divisionResults":
                    divisionFlag = True
                    for i in range(len(solution[key])):
                        flag = False
                        for j in range(len(solutionFromAnnotations[key])):
                            if (solution[key][i]["id"]
                                    == solutionFromAnnotations[key][j]["id"]
                                    and solution[key][i]["value"] ==
                                    solutionFromAnnotations[key][j]["value"]):
                                flag = True
                                break
                        divisionFlag &= flag
                elif key == "linkingResults":
                    linkingFlag = True
                    for i in range(len(solution[key])):
                        flag = False
                        for j in range(len(solutionFromAnnotations[key])):
                            if (solution[key][i]["dest"]
                                    == solutionFromAnnotations[key][j]["dest"]
                                    and solution[key][i]["src"]
                                    == solutionFromAnnotations[key][j]["src"]):
                                if (solution[key][i]["gap"] ==
                                        solutionFromAnnotations[key][j]["gap"]
                                        and solution[key][i]["value"]
                                        == solutionFromAnnotations[key][j]
                                    ["value"]):
                                    flag = True
                                    break
                        linkingFlag &= flag

            assert detectionFlag, "Detection results are NOT correct. They differ from your annotated detections."
            logger.info("Detection results are correct.")
            assert divisionFlag, "Division results are NOT correct. They differ from your annotated divisions."
            logger.info("Division results are correct.")
            assert linkingFlag, "Transition results are NOT correct. They differ from your annotated transitions."
            logger.info("Transition results are correct.")
        self.result = runLearningAndTracking(
            withMergerResolution=parameters["withMergerResolution"])

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and all(
                    [sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False
        return input_ready

    def onProjectLoaded(self, projectManager):
        """
        Overridden from Workflow base class.  Called by the Project Manager.

        If the user provided command-line arguments, use them to configure
        the workflow inputs and output settings.
        """

        # Configure the data export operator.
        if self._data_export_args:
            self.dataExportTrackingApplet.configure_operator_with_parsed_args(
                self._data_export_args)

        # Configure headless mode.
        if self._headless and self._batch_input_args and self._data_export_args:
            logger.info("Beginning Batch Processing")
            self.batchProcessingApplet.run_export_from_parsed_args(
                self._batch_input_args)
            logger.info("Completed Batch Processing")

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        if not self.fromBinary:
            opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
            thresholdingOutput = opThresholding.CachedOutput
            thresholding_ready = input_ready and len(thresholdingOutput) > 0
        else:
            thresholding_ready = input_ready

        opTrackingFeatureExtraction = self.trackingFeatureExtractionApplet.topLevelOperator
        trackingFeatureExtractionOutput = opTrackingFeatureExtraction.ComputedFeatureNamesAll
        tracking_features_ready = thresholding_ready and len(
            trackingFeatureExtractionOutput) > 0

        objectCountClassifier_ready = tracking_features_ready

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.RegionFeatures
        features_ready = thresholding_ready and len(objectExtractionOutput) > 0

        opAnnotations = self.annotationsApplet.topLevelOperator
        annotations_ready = (features_ready and len(opAnnotations.Labels) > 0
                             and opAnnotations.Labels.ready()
                             and opAnnotations.TrackImage.ready())

        opStructuredTracking = self.trackingApplet.topLevelOperator
        structured_tracking_ready = objectCountClassifier_ready

        withIlpSolver = self._solver == "ILP"

        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.annotationsApplet.busy
        # busy |= self.dataExportAnnotationsApplet.busy
        busy |= self.trackingApplet.busy
        busy |= self.dataExportTrackingApplet.busy

        self._shell.enableProjectChanges(not busy)

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        if not self.fromBinary:
            self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet,
                                         input_ready and not busy)
        self._shell.setAppletEnabled(self.trackingFeatureExtractionApplet,
                                     thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.cellClassificationApplet,
                                     tracking_features_ready and not busy)
        self._shell.setAppletEnabled(self.divisionDetectionApplet,
                                     tracking_features_ready and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet, not busy)
        self._shell.setAppletEnabled(self.annotationsApplet, features_ready
                                     and not busy)  # and withIlpSolver)
        # self._shell.setAppletEnabled(self.dataExportAnnotationsApplet, annotations_ready and not busy and \
        #                                 self.dataExportAnnotationsApplet.topLevelOperator.Inputs[0][0].ready() )
        self._shell.setAppletEnabled(self.trackingApplet,
                                     objectCountClassifier_ready and not busy)
        self._shell.setAppletEnabled(
            self.dataExportTrackingApplet,
            structured_tracking_ready and not busy
            and self.dataExportTrackingApplet.topLevelOperator.Inputs[0]
            [0].ready(),
        )
    def __init__( self, shell, headless, workflow_cmdline_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        # if 'withOptTrans' in kwargs:
        #     self.withOptTrans = kwargs['withOptTrans']
        # if 'fromBinary' in kwargs:
        #     self.fromBinary = kwargs['fromBinary']
        super(ConservationTrackingWorkflowBase, self).__init__(shell, headless, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        ## Create applets 
        self.dataSelectionApplet = DataSelectionApplet(self, 
                                                       "Input Data", 
                                                       "Input Data", 
                                                       forceAxisOrder='txyzc',
                                                       instructionText=data_instructions,
                                                       max_lanes=1
                                                       )
        
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Binary Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )
                
        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self, 
                                                                  "Threshold and Size Filter", 
                                                                  "ThresholdTwoLevels" )        
        if self.withOptTrans:
            self.opticalTranslationApplet = OpticalTranslationApplet(workflow=self)
                                                                   
        self.objectExtractionApplet = TrackingFeatureExtractionApplet(workflow=self, interactive=False,
                                                                      name="Object Feature Computation")                                                                      
        
        self.divisionDetectionApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Division Detection (optional)",
                                                                     projectFileGroupName="DivisionDetection")
        
        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification (optional)",
                                                                     projectFileGroupName="CountClassification")
                
        self.trackingApplet = ConservationTrackingApplet( workflow=self )

        self.default_export_filename = '{dataset_dir}/{nickname}-exported_data.csv'
        self.dataExportApplet = TrackingBaseDataExportApplet(self, 
                                                             "Tracking Result Export", 
                                                             default_export_filename=self.default_export_filename)

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue( ['Object Identities', 'Tracking Result', 'Merger Result'] )
        opDataExport.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        
        # Extra configuration for object export table (as CSV table or HDF5 table)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet.set_exporting_operator(opTracking)
        self.dataExportApplet.post_process_lane_export = self.post_process_lane_export
        
        self._applets = []                
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        if self.withOptTrans:
            self._applets.append(self.opticalTranslationApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)
        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
class StructuredTrackingWorkflowBase( Workflow ):
    workflowName = "Structured Learning Tracking Workflow BASE"

    @property
    def applets(self):
        return self._applets

    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']

        super(StructuredTrackingWorkflowBase, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Create applets
        self.dataSelectionApplet = DataSelectionApplet(self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            forceAxisOrder='txyzc',
            instructionText=data_instructions,
            max_lanes=1)

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Binary Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self,"Threshold and Size Filter","ThresholdTwoLevels" )

        self.divisionDetectionApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Division Detection (optional)",
                                                                     projectFileGroupName="DivisionDetection",
                                                                     selectedFeatures=configStructured.selectedFeaturesDiv)

        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification",
                                                                     projectFileGroupName="CountClassification",
                                                                     selectedFeatures=configStructured.selectedFeaturesObjectCount)

        self.cropSelectionApplet = CropSelectionApplet(self,"Crop Selection","CropSelection")

        self.trackingFeatureExtractionApplet = TrackingFeatureExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.annotationsApplet = AnnotationsApplet( name="Training", workflow=self )
        opAnnotations = self.annotationsApplet.topLevelOperator

        # self.default_training_export_filename = '{dataset_dir}/{nickname}-training_exported_data.csv'
        # self.dataExportAnnotationsApplet = TrackingBaseDataExportApplet(self, "Training Export",default_export_filename=self.default_training_export_filename)
        # opDataExportAnnotations = self.dataExportAnnotationsApplet.topLevelOperator
        # opDataExportAnnotations.SelectionNames.setValue( ['User Training for Tracking', 'Object Identities'] )
        # opDataExportAnnotations.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        # self.dataExportAnnotationsApplet.set_exporting_operator(opAnnotations)

        self.trackingApplet = StructuredTrackingApplet( name="Tracking - Structured Learning", workflow=self )
        opStructuredTracking = self.trackingApplet.topLevelOperator

        self.default_tracking_export_filename = '{dataset_dir}/{nickname}-tracking_exported_data.csv'
        self.dataExportTrackingApplet = TrackingBaseDataExportApplet(self, "Tracking Result Export",default_export_filename=self.default_tracking_export_filename)
        opDataExportTracking = self.dataExportTrackingApplet.topLevelOperator
        opDataExportTracking.SelectionNames.setValue( ['Tracking Result', 'Merger Result', 'Object Identities'] )
        opDataExportTracking.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        self.dataExportTrackingApplet.set_exporting_operator(opStructuredTracking)
        self.dataExportTrackingApplet.post_process_lane_export = self.post_process_lane_export

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.trackingFeatureExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)
        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.cropSelectionApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.annotationsApplet)
        # self._applets.append(self.dataExportAnnotationsApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportTrackingApplet)

        if self.divisionDetectionApplet:
            opDivDetection = self.divisionDetectionApplet.topLevelOperator
            opDivDetection.SelectedFeatures.setValue(configConservation.selectedFeaturesDiv)
            opDivDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])
            opDivDetection.AllowDeleteLabels.setValue(False)
            opDivDetection.AllowAddLabel.setValue(False)
            opDivDetection.EnableLabelTransfer.setValue(False)

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(configConservation.selectedFeaturesObjectCount )
        opCellClassification.SuggestedLabelNames.setValue( ['False Detection',] + [str(1) + ' Object'] + [str(i) + ' Objects' for i in range(2,10) ] )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(laneIndex)
        opTrackingFeatureExtraction = self.trackingFeatureExtractionApplet.topLevelOperator.getLane(laneIndex)

        opAnnotations = self.annotationsApplet.topLevelOperator.getLane(laneIndex)
        if not self.fromBinary:
            opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(laneIndex)
        # opDataAnnotationsExport = self.dataExportAnnotationsApplet.topLevelOperator.getLane(laneIndex)

        opCropSelection = self.cropSelectionApplet.topLevelOperator.getLane(laneIndex)
        opStructuredTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opDataTrackingExport = self.dataExportTrackingApplet.topLevelOperator.getLane(laneIndex)

        ## Connect operators ##
        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])

        opDivDetection = self.divisionDetectionApplet.topLevelOperator.getLane(laneIndex)
        opCellClassification = self.cellClassificationApplet.topLevelOperator.getLane(laneIndex)

        if not self.fromBinary:
            opTwoLevelThreshold.InputImage.connect( opData.ImageGroup[1] )
            opTwoLevelThreshold.RawInput.connect( opData.ImageGroup[0] ) # Used for display only
            binarySrc = opTwoLevelThreshold.CachedOutput
        else:
            binarySrc = opData.ImageGroup[1]
        # Use Op5ifyers for both input datasets such that they are guaranteed to
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(binarySrc)

        opCropSelection.InputImage.connect( opData.ImageGroup[0] )
        opCropSelection.PredictionImage.connect( opData.ImageGroup[1] )

        opObjExtraction.RawImage.connect( op5Raw.Output )
        opObjExtraction.BinaryImage.connect( op5Binary.Output )

        opTrackingFeatureExtraction.RawImage.connect( op5Raw.Output )
        opTrackingFeatureExtraction.BinaryImage.connect( op5Binary.Output )

        # vigra_features = list((set(config.vigra_features)).union(config.selected_features_objectcount[config.features_vigra_name]))
        # feature_names_vigra = {}
        # feature_names_vigra[config.features_vigra_name] = { name: {} for name in vigra_features }

        opTrackingFeatureExtraction.FeatureNamesVigra.setValue(configConservation.allFeaturesObjectCount)
        feature_dict_division = {}
        feature_dict_division[config.features_division_name] = { name: {} for name in config.division_features }
        opTrackingFeatureExtraction.FeatureNamesDivision.setValue(feature_dict_division)

        if self.divisionDetectionApplet:
            opDivDetection.BinaryImages.connect( op5Binary.Output )
            opDivDetection.RawImages.connect( op5Raw.Output )
            opDivDetection.SegmentationImages.connect(opTrackingFeatureExtraction.LabelImage)
            opDivDetection.ObjectFeatures.connect(opTrackingFeatureExtraction.RegionFeaturesAll)
            opDivDetection.ComputedFeatureNames.connect(opTrackingFeatureExtraction.ComputedFeatureNamesAll)

        opCellClassification.BinaryImages.connect( op5Binary.Output )
        opCellClassification.RawImages.connect( op5Raw.Output )
        opCellClassification.SegmentationImages.connect(opTrackingFeatureExtraction.LabelImage)
        opCellClassification.ObjectFeatures.connect(opTrackingFeatureExtraction.RegionFeaturesAll)
        opCellClassification.ComputedFeatureNames.connect(opTrackingFeatureExtraction.ComputedFeatureNamesNoDivisions)

        opAnnotations.RawImage.connect( op5Raw.Output )
        opAnnotations.BinaryImage.connect( op5Binary.Output )
        opAnnotations.LabelImage.connect( opObjExtraction.LabelImage )
        opAnnotations.ObjectFeatures.connect( opObjExtraction.RegionFeatures )
        opAnnotations.ComputedFeatureNames.connect(opObjExtraction.Features)
        opAnnotations.Crops.connect( opCropSelection.Crops)

        # opDataAnnotationsExport.Inputs.resize(2)
        # opDataAnnotationsExport.Inputs[0].connect( opAnnotations.TrackImage )
        # opDataAnnotationsExport.Inputs[1].connect( opAnnotations.LabelImage )
        # opDataAnnotationsExport.RawData.connect( op5Raw.Output )
        # opDataAnnotationsExport.RawDatasetInfo.connect( opData.DatasetGroup[0] )

        opStructuredTracking.RawImage.connect( op5Raw.Output )
        opStructuredTracking.LabelImage.connect( opTrackingFeatureExtraction.LabelImage )
        opStructuredTracking.ObjectFeatures.connect( opTrackingFeatureExtraction.RegionFeaturesVigra )
        opStructuredTracking.ComputedFeatureNames.connect( opTrackingFeatureExtraction.FeatureNamesVigra )

        if self.divisionDetectionApplet:
            opStructuredTracking.ObjectFeaturesWithDivFeatures.connect( opTrackingFeatureExtraction.RegionFeaturesAll)
            opStructuredTracking.ComputedFeatureNamesWithDivFeatures.connect( opTrackingFeatureExtraction.ComputedFeatureNamesAll )
            opStructuredTracking.DivisionProbabilities.connect( opDivDetection.Probabilities )

        # configure tracking export settings
        settings = {'file path': self.default_tracking_export_filename, 'compression': {}, 'file type': 'csv'}
        selected_features = ['Count', 'RegionCenter']
        opStructuredTracking.configure_table_export_settings(settings, selected_features)

        opStructuredTracking.DetectionProbabilities.connect( opCellClassification.Probabilities )
        opStructuredTracking.NumLabels.connect( opCellClassification.NumLabels )
        opStructuredTracking.Crops.connect (opCropSelection.Crops)
        opStructuredTracking.Annotations.connect (opAnnotations.Annotations)
        opStructuredTracking.Labels.connect (opAnnotations.Labels)
        opStructuredTracking.Divisions.connect (opAnnotations.Divisions)
        opStructuredTracking.MaxNumObj.connect (opCellClassification.MaxNumObj)

        opDataTrackingExport.Inputs.resize(3)
        opDataTrackingExport.Inputs[0].connect( opStructuredTracking.Output )
        opDataTrackingExport.Inputs[1].connect( opStructuredTracking.MergerOutput )
        opDataTrackingExport.Inputs[2].connect( opStructuredTracking.LabelImage )
        opDataTrackingExport.RawData.connect( op5Raw.Output )
        opDataTrackingExport.RawDatasetInfo.connect( opData.DatasetGroup[0] )

    def post_process_lane_export(self, lane_index):
        # FIXME: This probably only works for the non-blockwise export slot.
        #        We should assert that the user isn't using the blockwise slot.
        settings, selected_features = self.trackingApplet.topLevelOperator.getLane(lane_index).get_table_export_settings()
        from lazyflow.utility import PathComponents, make_absolute, format_known_keys

        if settings:
            self.dataExportTrackingApplet.progressSignal.emit(-1)
            raw_dataset_info = self.dataSelectionApplet.topLevelOperator.DatasetGroup[lane_index][0].value

            project_path = self.shell.projectManager.currentProjectPath
            project_dir = os.path.dirname(project_path)
            dataset_dir = PathComponents(raw_dataset_info.filePath).externalDirectory
            abs_dataset_dir = make_absolute(dataset_dir, cwd=project_dir)

            known_keys = {}
            known_keys['dataset_dir'] = abs_dataset_dir
            nickname = raw_dataset_info.nickname.replace('*', '')
            if os.path.pathsep in nickname:
                nickname = PathComponents(nickname.split(os.path.pathsep)[0]).fileNameBase
            known_keys['nickname'] = nickname

            # use partial formatting to fill in non-coordinate name fields
            name_format = settings['file path']
            partially_formatted_name = format_known_keys( name_format, known_keys )
            settings['file path'] = partially_formatted_name

            req = self.trackingApplet.topLevelOperator.getLane(lane_index).export_object_data(
                        lane_index,
                        # FIXME: Even in non-headless mode, we can't show the gui because we're running in a non-main thread.
                        #        That's not a huge deal, because there's still a progress bar for the overall export.
                        show_gui=False)

            req.wait()
            self.dataExportTrackingApplet.progressSignal.emit(100)

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False
        return input_ready

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        if not self.fromBinary:
            opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
            thresholdingOutput = opThresholding.CachedOutput
            thresholding_ready = input_ready and len(thresholdingOutput) > 0
        else:
            thresholding_ready = input_ready

        opTrackingFeatureExtraction = self.trackingFeatureExtractionApplet.topLevelOperator
        trackingFeatureExtractionOutput = opTrackingFeatureExtraction.ComputedFeatureNamesAll
        tracking_features_ready = thresholding_ready and len(trackingFeatureExtractionOutput) > 0

        opCropSelection = self.cropSelectionApplet.topLevelOperator
        croppingOutput = opCropSelection.Crops
        cropping_ready = thresholding_ready and len(croppingOutput) > 0

        objectCountClassifier_ready = tracking_features_ready

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.RegionFeatures
        features_ready = thresholding_ready and \
                         len(objectExtractionOutput) > 0

        opAnnotations = self.annotationsApplet.topLevelOperator
        annotations_ready = features_ready and \
                           len(opAnnotations.Labels) > 0 and \
                           opAnnotations.Labels.ready() and \
                           opAnnotations.TrackImage.ready()

        opStructuredTracking = self.trackingApplet.topLevelOperator
        structured_tracking_ready = objectCountClassifier_ready and \
                           len(opStructuredTracking.EventsVector) > 0
        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.annotationsApplet.busy
        # busy |= self.dataExportAnnotationsApplet.busy
        busy |= self.trackingApplet.busy
        busy |= self.dataExportTrackingApplet.busy

        self._shell.enableProjectChanges( not busy )

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        if not self.fromBinary:
            self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet, input_ready and not busy)
        self._shell.setAppletEnabled(self.trackingFeatureExtractionApplet, thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.cellClassificationApplet, tracking_features_ready and not busy)
        self._shell.setAppletEnabled(self.divisionDetectionApplet, tracking_features_ready and not busy)
        self._shell.setAppletEnabled(self.cropSelectionApplet, thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet, not busy)
        self._shell.setAppletEnabled(self.annotationsApplet, features_ready and not busy)
        # self._shell.setAppletEnabled(self.dataExportAnnotationsApplet, annotations_ready and not busy and \
        #                                 self.dataExportAnnotationsApplet.topLevelOperator.Inputs[0][0].ready() )
        self._shell.setAppletEnabled(self.trackingApplet, objectCountClassifier_ready and not busy)
        self._shell.setAppletEnabled(self.dataExportTrackingApplet, structured_tracking_ready and not busy and \
                                    self.dataExportTrackingApplet.topLevelOperator.Inputs[0][0].ready() )
    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']

        super(StructuredTrackingWorkflowBase, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Create applets
        self.dataSelectionApplet = DataSelectionApplet(self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            forceAxisOrder='txyzc',
            instructionText=data_instructions,
            max_lanes=1)

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Binary Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self,"Threshold and Size Filter","ThresholdTwoLevels" )

        self.divisionDetectionApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Division Detection (optional)",
                                                                     projectFileGroupName="DivisionDetection",
                                                                     selectedFeatures=configStructured.selectedFeaturesDiv)

        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification",
                                                                     projectFileGroupName="CountClassification",
                                                                     selectedFeatures=configStructured.selectedFeaturesObjectCount)

        self.cropSelectionApplet = CropSelectionApplet(self,"Crop Selection","CropSelection")

        self.trackingFeatureExtractionApplet = TrackingFeatureExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.annotationsApplet = AnnotationsApplet( name="Training", workflow=self )
        opAnnotations = self.annotationsApplet.topLevelOperator

        # self.default_training_export_filename = '{dataset_dir}/{nickname}-training_exported_data.csv'
        # self.dataExportAnnotationsApplet = TrackingBaseDataExportApplet(self, "Training Export",default_export_filename=self.default_training_export_filename)
        # opDataExportAnnotations = self.dataExportAnnotationsApplet.topLevelOperator
        # opDataExportAnnotations.SelectionNames.setValue( ['User Training for Tracking', 'Object Identities'] )
        # opDataExportAnnotations.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        # self.dataExportAnnotationsApplet.set_exporting_operator(opAnnotations)

        self.trackingApplet = StructuredTrackingApplet( name="Tracking - Structured Learning", workflow=self )
        opStructuredTracking = self.trackingApplet.topLevelOperator

        self.default_tracking_export_filename = '{dataset_dir}/{nickname}-tracking_exported_data.csv'
        self.dataExportTrackingApplet = TrackingBaseDataExportApplet(self, "Tracking Result Export",default_export_filename=self.default_tracking_export_filename)
        opDataExportTracking = self.dataExportTrackingApplet.topLevelOperator
        opDataExportTracking.SelectionNames.setValue( ['Tracking Result', 'Merger Result', 'Object Identities'] )
        opDataExportTracking.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        self.dataExportTrackingApplet.set_exporting_operator(opStructuredTracking)
        self.dataExportTrackingApplet.post_process_lane_export = self.post_process_lane_export

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.trackingFeatureExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)
        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.cropSelectionApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.annotationsApplet)
        # self._applets.append(self.dataExportAnnotationsApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportTrackingApplet)

        if self.divisionDetectionApplet:
            opDivDetection = self.divisionDetectionApplet.topLevelOperator
            opDivDetection.SelectedFeatures.setValue(configConservation.selectedFeaturesDiv)
            opDivDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])
            opDivDetection.AllowDeleteLabels.setValue(False)
            opDivDetection.AllowAddLabel.setValue(False)
            opDivDetection.EnableLabelTransfer.setValue(False)

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(configConservation.selectedFeaturesObjectCount )
        opCellClassification.SuggestedLabelNames.setValue( ['False Detection',] + [str(1) + ' Object'] + [str(i) + ' Objects' for i in range(2,10) ] )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)
    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']

        super(StructuredTrackingWorkflowBase, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Create applets
        self.dataSelectionApplet = DataSelectionApplet(self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            forceAxisOrder=['txyzc'],
            instructionText=data_instructions,
            max_lanes=1)

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Binary Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self,"Threshold and Size Filter","ThresholdTwoLevels" )

        self.divisionDetectionApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Division Detection (optional)",
                                                                     projectFileGroupName="DivisionDetection",
                                                                     selectedFeatures=configConservation.selectedFeaturesDiv)

        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification",
                                                                     projectFileGroupName="CountClassification",
                                                                     selectedFeatures=configConservation.selectedFeaturesObjectCount)

        self.trackingFeatureExtractionApplet = TrackingFeatureExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.annotationsApplet = AnnotationsApplet( name="Training", workflow=self )
        opAnnotations = self.annotationsApplet.topLevelOperator

        self.trackingApplet = StructuredTrackingApplet( name="Tracking - Structured Learning", workflow=self )
        opStructuredTracking = self.trackingApplet.topLevelOperator

        if SOLVER=="CPLEX" or SOLVER=="GUROBI":
            self._solver="ILP"
        elif SOLVER=="DPCT":
            self._solver="Flow-based"
        else:
            self._solver=None
        opStructuredTracking._solver = self._solver

        self.default_tracking_export_filename = '{dataset_dir}/{nickname}-tracking_exported_data.csv'
        self.dataExportTrackingApplet = TrackingBaseDataExportApplet(
            self,
            "Tracking Result Export",
            default_export_filename=self.default_tracking_export_filename,
            pluginExportFunc=self._pluginExportFunc
        )
        opDataExportTracking = self.dataExportTrackingApplet.topLevelOperator
        opDataExportTracking.SelectionNames.setValue( ['Tracking-Result', 'Merger-Result', 'Object-Identities'] )
        opDataExportTracking.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        self.dataExportTrackingApplet.set_exporting_operator(opStructuredTracking)
        self.dataExportTrackingApplet.prepare_lane_for_export = self.prepare_lane_for_export

        # configure export settings
        settings = {'file path': self.default_tracking_export_filename, 'compression': {}, 'file type': 'h5'}
        selected_features = ['Count', 'RegionCenter', 'RegionRadii', 'RegionAxes']
        opStructuredTracking.ExportSettings.setValue( (settings, selected_features) )

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.trackingFeatureExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)

        self.batchProcessingApplet = BatchProcessingApplet(self, "Batch Processing", self.dataSelectionApplet, self.dataExportTrackingApplet)

        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.annotationsApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportTrackingApplet)

        if self.divisionDetectionApplet:
            opDivDetection = self.divisionDetectionApplet.topLevelOperator
            opDivDetection.SelectedFeatures.setValue(configConservation.selectedFeaturesDiv)
            opDivDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])
            opDivDetection.AllowDeleteLabels.setValue(False)
            opDivDetection.AllowAddLabel.setValue(False)
            opDivDetection.EnableLabelTransfer.setValue(False)

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(configConservation.selectedFeaturesObjectCount )
        opCellClassification.SuggestedLabelNames.setValue( ['False Detection',] + [str(1) + ' Object'] + [str(i) + ' Objects' for i in range(2,10) ] )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

        if workflow_cmdline_args:

            if '--testFullAnnotations' in workflow_cmdline_args:
                self.testFullAnnotations = True
            else:
                self.testFullAnnotations = False

            self._data_export_args, unused_args = self.dataExportTrackingApplet.parse_known_cmdline_args( workflow_cmdline_args )
            self._batch_input_args, unused_args = self.batchProcessingApplet.parse_known_cmdline_args( workflow_cmdline_args )
        else:
            unused_args = None
            self._data_export_args = None
            self._batch_input_args = None
            self.testFullAnnotations = False

        if unused_args:
            logger.warning("Unused command-line args: {}".format( unused_args ))
Beispiel #22
0
class ConservationTrackingWorkflowBase(Workflow):
    workflowName = "Automatic Tracking Workflow (Conservation Tracking) BASE"

    def __init__(self, shell, headless, workflow_cmdline_args, *args,
                 **kwargs):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        # if 'withOptTrans' in kwargs:
        #     self.withOptTrans = kwargs['withOptTrans']
        # if 'fromBinary' in kwargs:
        #     self.fromBinary = kwargs['fromBinary']
        super(ConservationTrackingWorkflowBase, self).__init__(shell,
                                                               headless,
                                                               graph=graph,
                                                               *args,
                                                               **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        ## Create applets
        self.dataSelectionApplet = DataSelectionApplet(
            self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            force5d=True,
            instructionText=data_instructions,
            max_lanes=1)

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue(['Raw Data', 'Binary Image'])
        else:
            opDataSelection.DatasetRoles.setValue(
                ['Raw Data', 'Prediction Maps'])

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet(
                self, "Threshold and Size Filter", "ThresholdTwoLevels")
        if self.withOptTrans:
            self.opticalTranslationApplet = OpticalTranslationApplet(
                workflow=self)

        self.objectExtractionApplet = TrackingFeatureExtractionApplet(
            workflow=self,
            interactive=False,
            name="Object Feature Computation")

        self.divisionDetectionApplet = ObjectClassificationApplet(
            workflow=self,
            name="Division Detection (optional)",
            projectFileGroupName="DivisionDetection")

        self.cellClassificationApplet = ObjectClassificationApplet(
            workflow=self,
            name="Object Count Classification (optional)",
            projectFileGroupName="CountClassification")

        self.trackingApplet = ConservationTrackingApplet(workflow=self)
        opTracking = self.trackingApplet.topLevelOperator

        self.dataExportApplet = TrackingBaseDataExportApplet(
            self, "Tracking Result Export")
        self.dataExportApplet.set_exporting_operator(opTracking)

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue(
            ['Tracking Result', 'Merger Result', 'Object Identities'])
        opDataExport.WorkingDirectory.connect(opDataSelection.WorkingDirectory)

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        if self.withOptTrans:
            self._applets.append(self.opticalTranslationApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)
        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)

    @property
    def applets(self):
        return self._applets

    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        if not self.fromBinary:
            opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(
                laneIndex)
        if self.withOptTrans:
            opOptTranslation = self.opticalTranslationApplet.topLevelOperator.getLane(
                laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(
            laneIndex)
        opDivDetection = self.divisionDetectionApplet.topLevelOperator.getLane(
            laneIndex)
        opCellClassification = self.cellClassificationApplet.topLevelOperator.getLane(
            laneIndex)
        opTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opDataExport = self.dataExportApplet.topLevelOperator.getLane(
            laneIndex)

        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])

        if not self.fromBinary:
            opTwoLevelThreshold.InputImage.connect(opData.ImageGroup[1])
            opTwoLevelThreshold.RawInput.connect(
                opData.ImageGroup[0])  # Used for display only
            # opTwoLevelThreshold.Channel.setValue(1)
            binarySrc = opTwoLevelThreshold.CachedOutput
        else:
            binarySrc = opData.ImageGroup[1]

        # Use Op5ifyers for both input datasets such that they are guaranteed to
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(binarySrc)

        if self.withOptTrans:
            opOptTranslation.RawImage.connect(op5Raw.Output)
            opOptTranslation.BinaryImage.connect(op5Binary.Output)

        # # Connect operators ##
        vigra_features = list((set(config.vigra_features)).union(
            config.selected_features_objectcount[config.features_vigra_name]))
        feature_names_vigra = {}
        feature_names_vigra[config.features_vigra_name] = {
            name: {}
            for name in vigra_features
        }
        opObjExtraction.RawImage.connect(op5Raw.Output)
        opObjExtraction.BinaryImage.connect(op5Binary.Output)
        if self.withOptTrans:
            opObjExtraction.TranslationVectors.connect(
                opOptTranslation.TranslationVectors)
        opObjExtraction.FeatureNamesVigra.setValue(feature_names_vigra)
        feature_dict_division = {}
        feature_dict_division[config.features_division_name] = {
            name: {}
            for name in config.division_features
        }
        opObjExtraction.FeatureNamesDivision.setValue(feature_dict_division)

        selected_features_div = {}
        for plugin_name in config.selected_features_division.keys():
            selected_features_div[plugin_name] = {
                name: {}
                for name in config.selected_features_division[plugin_name]
            }
        # FIXME: do not hard code this
        for name in [
                'SquaredDistances_' + str(i)
                for i in range(config.n_best_successors)
        ]:
            selected_features_div[config.features_division_name][name] = {}

        opDivDetection.BinaryImages.connect(op5Binary.Output)
        opDivDetection.RawImages.connect(op5Raw.Output)
        opDivDetection.LabelsAllowedFlags.connect(opData.AllowLabels)
        opDivDetection.SegmentationImages.connect(opObjExtraction.LabelImage)
        opDivDetection.ObjectFeatures.connect(
            opObjExtraction.RegionFeaturesAll)
        opDivDetection.ComputedFeatureNames.connect(
            opObjExtraction.ComputedFeatureNamesAll)
        opDivDetection.SelectedFeatures.setValue(selected_features_div)
        opDivDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])
        opDivDetection.AllowDeleteLabels.setValue(False)
        opDivDetection.AllowAddLabel.setValue(False)
        opDivDetection.EnableLabelTransfer.setValue(False)

        selected_features_objectcount = {}
        for plugin_name in config.selected_features_objectcount.keys():
            selected_features_objectcount[plugin_name] = {
                name: {}
                for name in config.selected_features_objectcount[plugin_name]
            }
        opCellClassification.BinaryImages.connect(op5Binary.Output)
        opCellClassification.RawImages.connect(op5Raw.Output)
        opCellClassification.LabelsAllowedFlags.connect(opData.AllowLabels)
        opCellClassification.SegmentationImages.connect(
            opObjExtraction.LabelImage)
        opCellClassification.ObjectFeatures.connect(
            opObjExtraction.RegionFeaturesVigra)
        opCellClassification.ComputedFeatureNames.connect(
            opObjExtraction.ComputedFeatureNamesVigra)
        opCellClassification.SelectedFeatures.setValue(
            selected_features_objectcount)
        opCellClassification.SuggestedLabelNames.setValue([
            'false detection',
        ] + [str(i) + ' Objects' for i in range(1, 10)])
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

        opTracking.RawImage.connect(op5Raw.Output)
        opTracking.LabelImage.connect(opObjExtraction.LabelImage)
        opTracking.ObjectFeatures.connect(opObjExtraction.RegionFeaturesVigra)
        opTracking.ObjectFeaturesWithDivFeatures.connect(
            opObjExtraction.RegionFeaturesAll)
        opTracking.ComputedFeatureNames.connect(
            opObjExtraction.ComputedFeatureNamesVigra)
        opTracking.ComputedFeatureNamesWithDivFeatures.connect(
            opObjExtraction.ComputedFeatureNamesAll)
        opTracking.DivisionProbabilities.connect(opDivDetection.Probabilities)
        opTracking.DetectionProbabilities.connect(
            opCellClassification.Probabilities)
        opTracking.NumLabels.connect(opCellClassification.NumLabels)

        opDataExport.Inputs.resize(3)
        opDataExport.Inputs[0].connect(opTracking.Output)
        opDataExport.Inputs[1].connect(opTracking.MergerOutput)
        opDataExport.Inputs[2].connect(opTracking.LabelImage)
        opDataExport.RawData.connect(op5Raw.Output)
        opDataExport.RawDatasetInfo.connect(opData.DatasetGroup[0])

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False

        return input_ready

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        if not self.fromBinary:
            opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
            thresholdingOutput = opThresholding.CachedOutput
            thresholding_ready = input_ready and \
                           len(thresholdingOutput) > 0
        else:
            thresholding_ready = True and input_ready

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.ComputedFeatureNamesAll
        features_ready = thresholding_ready and \
                         len(objectExtractionOutput) > 0

        objectCountClassifier_ready = features_ready

        opTracking = self.trackingApplet.topLevelOperator
        tracking_ready = objectCountClassifier_ready and \
                           len(opTracking.EventsVector) > 0

        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.trackingApplet.busy
        busy |= self.dataExportApplet.busy
        self._shell.enableProjectChanges(not busy)

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        if not self.fromBinary:
            self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet,
                                         input_ready and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet,
                                     thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.cellClassificationApplet,
                                     features_ready and not busy)
        self._shell.setAppletEnabled(self.divisionDetectionApplet,
                                     features_ready and not busy)
        self._shell.setAppletEnabled(self.trackingApplet,
                                     objectCountClassifier_ready and not busy)
        self._shell.setAppletEnabled(self.dataExportApplet, tracking_ready and not busy and \
                                    self.dataExportApplet.topLevelOperator.Inputs[0][0].ready() )
Beispiel #23
0
class ManualTrackingWorkflow(Workflow):
    workflowName = "Manual Tracking Workflow"
    workflowDisplayName = "Manual Tracking Workflow [Inputs: Raw Data, Pixel Prediction Map]"
    workflowDescription = "Manual tracking of objects, based on Prediction Maps or (binary) Segmentation Images"

    @property
    def applets(self):
        return self._applets

    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def __init__(self, shell, headless, workflow_cmdline_args,
                 project_creation_args, *args, **kwargs):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        super(ManualTrackingWorkflow, self).__init__(shell,
                                                     headless,
                                                     workflow_cmdline_args,
                                                     project_creation_args,
                                                     graph=graph,
                                                     *args,
                                                     **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'\
                            'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'
        ## Create applets
        self.dataSelectionApplet = DataSelectionApplet(
            self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            force5d=True,
            instructionText=data_instructions,
            max_lanes=1)
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        opDataSelection.DatasetRoles.setValue(['Raw Data', 'Prediction Maps'])

        self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet(
            self, "Threshold and Size Filter", "ThresholdTwoLevels")

        self.objectExtractionApplet = ObjectExtractionApplet(
            name="Object Feature Computation",
            workflow=self,
            interactive=False)

        self.trackingApplet = ManualTrackingApplet(workflow=self)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet = TrackingBaseDataExportApplet(
            self, "Tracking Result Export")
        self.dataExportApplet.set_exporting_operator(opTracking)

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue(
            ['Manual Tracking', 'Object Identities'])
        opDataExport.WorkingDirectory.connect(opDataSelection.WorkingDirectory)

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)

    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(
            laneIndex)
        opTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(
            laneIndex)
        opDataExport = self.dataExportApplet.topLevelOperator.getLane(
            laneIndex)

        ## Connect operators ##
        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])

        opTwoLevelThreshold.InputImage.connect(opData.ImageGroup[1])
        opTwoLevelThreshold.RawInput.connect(
            opData.ImageGroup[0])  # Used for display only
        # Use OpReorderAxis for both input datasets such that they are guaranteed to
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(opTwoLevelThreshold.CachedOutput)

        opObjExtraction.RawImage.connect(op5Raw.Output)
        opObjExtraction.BinaryImage.connect(op5Binary.Output)

        opTracking.RawImage.connect(op5Raw.Output)
        opTracking.BinaryImage.connect(op5Binary.Output)
        opTracking.LabelImage.connect(opObjExtraction.LabelImage)
        opTracking.ObjectFeatures.connect(opObjExtraction.RegionFeatures)
        opTracking.ComputedFeatureNames.connect(
            opObjExtraction.ComputedFeatureNames)

        opDataExport.Inputs.resize(2)
        opDataExport.Inputs[0].connect(opTracking.TrackImage)
        opDataExport.Inputs[1].connect(opTracking.LabelImage)
        opDataExport.RawData.connect(op5Raw.Output)
        opDataExport.RawDatasetInfo.connect(opData.DatasetGroup[0])

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False

        return input_ready

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
        thresholdingOutput = opThresholding.CachedOutput
        thresholding_ready = input_ready and \
                       len(thresholdingOutput) > 0

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.ComputedFeatureNames
        features_ready = thresholding_ready and \
                         len(objectExtractionOutput) > 0

        opTracking = self.trackingApplet.topLevelOperator
        tracking_ready = features_ready and \
                           len(opTracking.Labels) > 0 and \
                           opTracking.Labels.ready() and \
                           opTracking.TrackImage.ready()

        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.dataExportApplet.busy
        busy |= self.trackingApplet.busy
        self._shell.enableProjectChanges(not busy)

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet, input_ready
                                     and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet,
                                     thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.trackingApplet, features_ready
                                     and not busy)
        self._shell.setAppletEnabled(self.dataExportApplet, tracking_ready and not busy and \
                                        self.dataExportApplet.topLevelOperator.Inputs[0][0].ready() )
Beispiel #24
0
    def __init__(self, shell, headless, workflow_cmdline_args, *args,
                 **kwargs):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        # if 'withOptTrans' in kwargs:
        #     self.withOptTrans = kwargs['withOptTrans']
        # if 'fromBinary' in kwargs:
        #     self.fromBinary = kwargs['fromBinary']
        super(ConservationTrackingWorkflowBase, self).__init__(shell,
                                                               headless,
                                                               graph=graph,
                                                               *args,
                                                               **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        ## Create applets
        self.dataSelectionApplet = DataSelectionApplet(
            self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            force5d=True,
            instructionText=data_instructions,
            max_lanes=1)

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue(['Raw Data', 'Binary Image'])
        else:
            opDataSelection.DatasetRoles.setValue(
                ['Raw Data', 'Prediction Maps'])

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet(
                self, "Threshold and Size Filter", "ThresholdTwoLevels")
        if self.withOptTrans:
            self.opticalTranslationApplet = OpticalTranslationApplet(
                workflow=self)

        self.objectExtractionApplet = TrackingFeatureExtractionApplet(
            workflow=self,
            interactive=False,
            name="Object Feature Computation")

        self.divisionDetectionApplet = ObjectClassificationApplet(
            workflow=self,
            name="Division Detection (optional)",
            projectFileGroupName="DivisionDetection")

        self.cellClassificationApplet = ObjectClassificationApplet(
            workflow=self,
            name="Object Count Classification (optional)",
            projectFileGroupName="CountClassification")

        self.trackingApplet = ConservationTrackingApplet(workflow=self)
        opTracking = self.trackingApplet.topLevelOperator

        self.dataExportApplet = TrackingBaseDataExportApplet(
            self, "Tracking Result Export")
        self.dataExportApplet.set_exporting_operator(opTracking)

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue(
            ['Tracking Result', 'Merger Result', 'Object Identities'])
        opDataExport.WorkingDirectory.connect(opDataSelection.WorkingDirectory)

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        if self.withOptTrans:
            self._applets.append(self.opticalTranslationApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)
        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
Beispiel #25
0
class StructuredTrackingWorkflowBase( Workflow ):
    workflowName = "Structured Learning Tracking Workflow BASE"

    @property
    def applets(self):
        return self._applets

    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']

        super(StructuredTrackingWorkflowBase, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Create applets
        self.dataSelectionApplet = DataSelectionApplet(self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            forceAxisOrder=['txyzc'],
            instructionText=data_instructions,
            max_lanes=1)

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Binary Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self,"Threshold and Size Filter","ThresholdTwoLevels" )

        self.divisionDetectionApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Division Detection (optional)",
                                                                     projectFileGroupName="DivisionDetection",
                                                                     selectedFeatures=configStructured.selectedFeaturesDiv)

        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification",
                                                                     projectFileGroupName="CountClassification",
                                                                     selectedFeatures=configStructured.selectedFeaturesObjectCount)

        self.cropSelectionApplet = CropSelectionApplet(self,"Crop Selection","CropSelection")

        self.trackingFeatureExtractionApplet = TrackingFeatureExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.annotationsApplet = AnnotationsApplet( name="Training", workflow=self )
        opAnnotations = self.annotationsApplet.topLevelOperator

        # self.default_training_export_filename = '{dataset_dir}/{nickname}-training_exported_data.csv'
        # self.dataExportAnnotationsApplet = TrackingBaseDataExportApplet(self, "Training Export",default_export_filename=self.default_training_export_filename)
        # opDataExportAnnotations = self.dataExportAnnotationsApplet.topLevelOperator
        # opDataExportAnnotations.SelectionNames.setValue( ['User Training for Tracking', 'Object Identities'] )
        # opDataExportAnnotations.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        # self.dataExportAnnotationsApplet.set_exporting_operator(opAnnotations)

        self.trackingApplet = StructuredTrackingApplet( name="Tracking - Structured Learning", workflow=self )
        opStructuredTracking = self.trackingApplet.topLevelOperator

        self.default_tracking_export_filename = '{dataset_dir}/{nickname}-tracking_exported_data.csv'
        self.dataExportTrackingApplet = TrackingBaseDataExportApplet(self, "Tracking Result Export",default_export_filename=self.default_tracking_export_filename)
        opDataExportTracking = self.dataExportTrackingApplet.topLevelOperator
        opDataExportTracking.SelectionNames.setValue( ['Tracking Result', 'Merger Result', 'Object Identities'] )
        opDataExportTracking.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        self.dataExportTrackingApplet.set_exporting_operator(opStructuredTracking)
        self.dataExportTrackingApplet.post_process_lane_export = self.post_process_lane_export

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.trackingFeatureExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)
        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.cropSelectionApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.annotationsApplet)
        # self._applets.append(self.dataExportAnnotationsApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportTrackingApplet)

        if self.divisionDetectionApplet:
            opDivDetection = self.divisionDetectionApplet.topLevelOperator
            opDivDetection.SelectedFeatures.setValue(configConservation.selectedFeaturesDiv)
            opDivDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])
            opDivDetection.AllowDeleteLabels.setValue(False)
            opDivDetection.AllowAddLabel.setValue(False)
            opDivDetection.EnableLabelTransfer.setValue(False)

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(configConservation.selectedFeaturesObjectCount )
        opCellClassification.SuggestedLabelNames.setValue( ['False Detection',] + [str(1) + ' Object'] + [str(i) + ' Objects' for i in range(2,10) ] )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(laneIndex)
        opTrackingFeatureExtraction = self.trackingFeatureExtractionApplet.topLevelOperator.getLane(laneIndex)

        opAnnotations = self.annotationsApplet.topLevelOperator.getLane(laneIndex)
        if not self.fromBinary:
            opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(laneIndex)
        # opDataAnnotationsExport = self.dataExportAnnotationsApplet.topLevelOperator.getLane(laneIndex)

        opCropSelection = self.cropSelectionApplet.topLevelOperator.getLane(laneIndex)
        opStructuredTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opDataTrackingExport = self.dataExportTrackingApplet.topLevelOperator.getLane(laneIndex)

        ## Connect operators ##
        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])

        opDivDetection = self.divisionDetectionApplet.topLevelOperator.getLane(laneIndex)
        opCellClassification = self.cellClassificationApplet.topLevelOperator.getLane(laneIndex)

        if not self.fromBinary:
            opTwoLevelThreshold.InputImage.connect( opData.ImageGroup[1] )
            opTwoLevelThreshold.RawInput.connect( opData.ImageGroup[0] ) # Used for display only
            binarySrc = opTwoLevelThreshold.CachedOutput
        else:
            binarySrc = opData.ImageGroup[1]
        # Use Op5ifyers for both input datasets such that they are guaranteed to
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(binarySrc)

        opCropSelection.InputImage.connect( opData.ImageGroup[0] )
        opCropSelection.PredictionImage.connect( opData.ImageGroup[1] )

        opObjExtraction.RawImage.connect( op5Raw.Output )
        opObjExtraction.BinaryImage.connect( op5Binary.Output )

        opTrackingFeatureExtraction.RawImage.connect( op5Raw.Output )
        opTrackingFeatureExtraction.BinaryImage.connect( op5Binary.Output )

        # vigra_features = list((set(config.vigra_features)).union(config.selected_features_objectcount[config.features_vigra_name]))
        # feature_names_vigra = {}
        # feature_names_vigra[config.features_vigra_name] = { name: {} for name in vigra_features }

        opTrackingFeatureExtraction.FeatureNamesVigra.setValue(configConservation.allFeaturesObjectCount)
        feature_dict_division = {}
        feature_dict_division[config.features_division_name] = { name: {} for name in config.division_features }
        opTrackingFeatureExtraction.FeatureNamesDivision.setValue(feature_dict_division)

        if self.divisionDetectionApplet:
            opDivDetection.BinaryImages.connect( op5Binary.Output )
            opDivDetection.RawImages.connect( op5Raw.Output )
            opDivDetection.SegmentationImages.connect(opTrackingFeatureExtraction.LabelImage)
            opDivDetection.ObjectFeatures.connect(opTrackingFeatureExtraction.RegionFeaturesAll)
            opDivDetection.ComputedFeatureNames.connect(opTrackingFeatureExtraction.ComputedFeatureNamesAll)

        opCellClassification.BinaryImages.connect( op5Binary.Output )
        opCellClassification.RawImages.connect( op5Raw.Output )
        opCellClassification.SegmentationImages.connect(opTrackingFeatureExtraction.LabelImage)
        opCellClassification.ObjectFeatures.connect(opTrackingFeatureExtraction.RegionFeaturesAll)
        opCellClassification.ComputedFeatureNames.connect(opTrackingFeatureExtraction.ComputedFeatureNamesNoDivisions)

        opAnnotations.RawImage.connect( op5Raw.Output )
        opAnnotations.BinaryImage.connect( op5Binary.Output )
        opAnnotations.LabelImage.connect( opObjExtraction.LabelImage )
        opAnnotations.ObjectFeatures.connect( opObjExtraction.RegionFeatures )
        opAnnotations.ComputedFeatureNames.connect(opObjExtraction.Features)
        opAnnotations.Crops.connect( opCropSelection.Crops)

        # opDataAnnotationsExport.Inputs.resize(2)
        # opDataAnnotationsExport.Inputs[0].connect( opAnnotations.TrackImage )
        # opDataAnnotationsExport.Inputs[1].connect( opAnnotations.LabelImage )
        # opDataAnnotationsExport.RawData.connect( op5Raw.Output )
        # opDataAnnotationsExport.RawDatasetInfo.connect( opData.DatasetGroup[0] )

        opStructuredTracking.RawImage.connect( op5Raw.Output )
        opStructuredTracking.LabelImage.connect( opTrackingFeatureExtraction.LabelImage )
        opStructuredTracking.ObjectFeatures.connect( opTrackingFeatureExtraction.RegionFeaturesVigra )
        opStructuredTracking.ComputedFeatureNames.connect( opTrackingFeatureExtraction.FeatureNamesVigra )

        if self.divisionDetectionApplet:
            opStructuredTracking.ObjectFeaturesWithDivFeatures.connect( opTrackingFeatureExtraction.RegionFeaturesAll)
            opStructuredTracking.ComputedFeatureNamesWithDivFeatures.connect( opTrackingFeatureExtraction.ComputedFeatureNamesAll )
            opStructuredTracking.DivisionProbabilities.connect( opDivDetection.Probabilities )

        # configure tracking export settings
        settings = {'file path': self.default_tracking_export_filename, 'compression': {}, 'file type': 'csv'}
        selected_features = ['Count', 'RegionCenter']
        opStructuredTracking.configure_table_export_settings(settings, selected_features)

        opStructuredTracking.DetectionProbabilities.connect( opCellClassification.Probabilities )
        opStructuredTracking.NumLabels.connect( opCellClassification.NumLabels )
        opStructuredTracking.Crops.connect (opCropSelection.Crops)
        opStructuredTracking.Annotations.connect (opAnnotations.Annotations)
        opStructuredTracking.Labels.connect (opAnnotations.Labels)
        opStructuredTracking.Divisions.connect (opAnnotations.Divisions)
        opStructuredTracking.MaxNumObj.connect (opCellClassification.MaxNumObj)

        opDataTrackingExport.Inputs.resize(3)
        opDataTrackingExport.Inputs[0].connect( opStructuredTracking.Output )
        opDataTrackingExport.Inputs[1].connect( opStructuredTracking.MergerOutput )
        opDataTrackingExport.Inputs[2].connect( opStructuredTracking.LabelImage )
        opDataTrackingExport.RawData.connect( op5Raw.Output )
        opDataTrackingExport.RawDatasetInfo.connect( opData.DatasetGroup[0] )

    def post_process_lane_export(self, lane_index):
        # FIXME: This probably only works for the non-blockwise export slot.
        #        We should assert that the user isn't using the blockwise slot.
        settings, selected_features = self.trackingApplet.topLevelOperator.getLane(lane_index).get_table_export_settings()
        from lazyflow.utility import PathComponents, make_absolute, format_known_keys

        if settings:
            self.dataExportTrackingApplet.progressSignal.emit(-1)
            raw_dataset_info = self.dataSelectionApplet.topLevelOperator.DatasetGroup[lane_index][0].value

            project_path = self.shell.projectManager.currentProjectPath
            project_dir = os.path.dirname(project_path)
            dataset_dir = PathComponents(raw_dataset_info.filePath).externalDirectory
            abs_dataset_dir = make_absolute(dataset_dir, cwd=project_dir)

            known_keys = {}
            known_keys['dataset_dir'] = abs_dataset_dir
            nickname = raw_dataset_info.nickname.replace('*', '')
            if os.path.pathsep in nickname:
                nickname = PathComponents(nickname.split(os.path.pathsep)[0]).fileNameBase
            known_keys['nickname'] = nickname

            # use partial formatting to fill in non-coordinate name fields
            name_format = settings['file path']
            partially_formatted_name = format_known_keys( name_format, known_keys )
            settings['file path'] = partially_formatted_name

            req = self.trackingApplet.topLevelOperator.getLane(lane_index).export_object_data(
                        lane_index,
                        # FIXME: Even in non-headless mode, we can't show the gui because we're running in a non-main thread.
                        #        That's not a huge deal, because there's still a progress bar for the overall export.
                        show_gui=False)

            req.wait()
            self.dataExportTrackingApplet.progressSignal.emit(100)

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False
        return input_ready

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        if not self.fromBinary:
            opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
            thresholdingOutput = opThresholding.CachedOutput
            thresholding_ready = input_ready and len(thresholdingOutput) > 0
        else:
            thresholding_ready = input_ready

        opTrackingFeatureExtraction = self.trackingFeatureExtractionApplet.topLevelOperator
        trackingFeatureExtractionOutput = opTrackingFeatureExtraction.ComputedFeatureNamesAll
        tracking_features_ready = thresholding_ready and len(trackingFeatureExtractionOutput) > 0

        opCropSelection = self.cropSelectionApplet.topLevelOperator
        croppingOutput = opCropSelection.Crops
        cropping_ready = thresholding_ready and len(croppingOutput) > 0

        objectCountClassifier_ready = tracking_features_ready

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.RegionFeatures
        features_ready = thresholding_ready and \
                         len(objectExtractionOutput) > 0

        opAnnotations = self.annotationsApplet.topLevelOperator
        annotations_ready = features_ready and \
                           len(opAnnotations.Labels) > 0 and \
                           opAnnotations.Labels.ready() and \
                           opAnnotations.TrackImage.ready()

        opStructuredTracking = self.trackingApplet.topLevelOperator
        structured_tracking_ready = objectCountClassifier_ready and \
                           len(opStructuredTracking.EventsVector) > 0
        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.annotationsApplet.busy
        # busy |= self.dataExportAnnotationsApplet.busy
        busy |= self.trackingApplet.busy
        busy |= self.dataExportTrackingApplet.busy

        self._shell.enableProjectChanges( not busy )

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        if not self.fromBinary:
            self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet, input_ready and not busy)
        self._shell.setAppletEnabled(self.trackingFeatureExtractionApplet, thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.cellClassificationApplet, tracking_features_ready and not busy)
        self._shell.setAppletEnabled(self.divisionDetectionApplet, tracking_features_ready and not busy)
        self._shell.setAppletEnabled(self.cropSelectionApplet, thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet, not busy)
        self._shell.setAppletEnabled(self.annotationsApplet, features_ready and not busy)
        # self._shell.setAppletEnabled(self.dataExportAnnotationsApplet, annotations_ready and not busy and \
        #                                 self.dataExportAnnotationsApplet.topLevelOperator.Inputs[0][0].ready() )
        self._shell.setAppletEnabled(self.trackingApplet, objectCountClassifier_ready and not busy)
        self._shell.setAppletEnabled(self.dataExportTrackingApplet, structured_tracking_ready and not busy and \
                                    self.dataExportTrackingApplet.topLevelOperator.Inputs[0][0].ready() )
class ConservationTrackingWorkflowBase(Workflow):
    workflowName = "Automatic Tracking Workflow (Conservation Tracking) BASE"

    def __init__(self, shell, headless, workflow_cmdline_args,
                 project_creation_args, *args, **kwargs):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        # if 'withOptTrans' in kwargs:
        #     self.withOptTrans = kwargs['withOptTrans']
        # if 'fromBinary' in kwargs:
        #     self.fromBinary = kwargs['fromBinary']
        super(ConservationTrackingWorkflowBase,
              self).__init__(shell,
                             headless,
                             workflow_cmdline_args,
                             project_creation_args,
                             graph=graph,
                             *args,
                             **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Variables to store division and cell classifiers to prevent retraining every-time batch processing runs
        self.stored_division_classifier = None
        self.stored_cell_classifier = None

        ## Create applets
        self.dataSelectionApplet = DataSelectionApplet(
            self,
            "Input Data",
            "Input Data",
            forceAxisOrder=['txyzc'],
            instructionText=data_instructions,
            max_lanes=None)

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue(
                ['Raw Data', 'Segmentation Image'])
        else:
            opDataSelection.DatasetRoles.setValue(
                ['Raw Data', 'Prediction Maps'])

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet(
                self, "Threshold and Size Filter", "ThresholdTwoLevels")
        if self.withOptTrans:
            self.opticalTranslationApplet = OpticalTranslationApplet(
                workflow=self)

        self.objectExtractionApplet = TrackingFeatureExtractionApplet(
            workflow=self,
            interactive=False,
            name="Object Feature Computation")

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        opObjectExtraction.FeatureNamesVigra.setValue(
            configConservation.allFeaturesObjectCount)

        self.divisionDetectionApplet = self._createDivisionDetectionApplet(
            configConservation.selectedFeaturesDiv)  # Might be None

        if self.divisionDetectionApplet:
            feature_dict_division = {}
            feature_dict_division[config.features_division_name] = {
                name: {}
                for name in config.division_features
            }
            opObjectExtraction.FeatureNamesDivision.setValue(
                feature_dict_division)

            selected_features_div = {}
            for plugin_name in config.selected_features_division.keys():
                selected_features_div[plugin_name] = {
                    name: {}
                    for name in config.selected_features_division[plugin_name]
                }
            # FIXME: do not hard code this
            for name in [
                    'SquaredDistances_' + str(i)
                    for i in range(config.n_best_successors)
            ]:
                selected_features_div[config.features_division_name][name] = {}

            opDivisionDetection = self.divisionDetectionApplet.topLevelOperator
            opDivisionDetection.SelectedFeatures.setValue(
                configConservation.selectedFeaturesDiv)
            opDivisionDetection.LabelNames.setValue(
                ['Not Dividing', 'Dividing'])
            opDivisionDetection.AllowDeleteLabels.setValue(False)
            opDivisionDetection.AllowAddLabel.setValue(False)
            opDivisionDetection.EnableLabelTransfer.setValue(False)

        self.cellClassificationApplet = ObjectClassificationApplet(
            workflow=self,
            name="Object Count Classification",
            projectFileGroupName="CountClassification",
            selectedFeatures=configConservation.selectedFeaturesObjectCount)

        selected_features_objectcount = {}
        for plugin_name in config.selected_features_objectcount.keys():
            selected_features_objectcount[plugin_name] = {
                name: {}
                for name in config.selected_features_objectcount[plugin_name]
            }

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(
            configConservation.selectedFeaturesObjectCount)
        opCellClassification.SuggestedLabelNames.setValue([
            'False Detection',
        ] + [str(1) + ' Object'] + [str(i) + ' Objects' for i in range(2, 10)])
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

        self.trackingApplet = ConservationTrackingApplet(workflow=self)

        self.default_export_filename = '{dataset_dir}/{nickname}-exported_data.csv'
        self.dataExportApplet = TrackingBaseDataExportApplet(
            self,
            "Tracking Result Export",
            default_export_filename=self.default_export_filename)

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue(
            ['Object-Identities', 'Tracking-Result', 'Merger-Result'])
        opDataExport.WorkingDirectory.connect(opDataSelection.WorkingDirectory)

        # Extra configuration for object export table (as CSV table or HDF5 table)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet.set_exporting_operator(opTracking)
        self.dataExportApplet.prepare_lane_for_export = self.prepare_lane_for_export
        self.dataExportApplet.post_process_lane_export = self.post_process_lane_export

        # table only export is just available for the pgmlink backend, hytra uses the CSV plugin instead
        try:
            import hytra
        except ImportError:
            self.dataExportApplet.includeTableOnlyOption(
            )  # Export table only, without volumes

        # configure export settings
        settings = {
            'file path': self.default_export_filename,
            'compression': {},
            'file type': 'csv'
        }
        selected_features = [
            'Count', 'RegionCenter', 'RegionRadii', 'RegionAxes'
        ]
        opTracking.ExportSettings.setValue((settings, selected_features))

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        if self.withOptTrans:
            self._applets.append(self.opticalTranslationApplet)
        self._applets.append(self.objectExtractionApplet)

        if self.divisionDetectionApplet:
            self._applets.append(self.divisionDetectionApplet)

        self.batchProcessingApplet = BatchProcessingApplet(
            self, "Batch Processing", self.dataSelectionApplet,
            self.dataExportApplet)

        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
        self._applets.append(self.batchProcessingApplet)

        # Parse export and batch command-line arguments for headless mode
        if workflow_cmdline_args:
            self._data_export_args, unused_args = self.dataExportApplet.parse_known_cmdline_args(
                workflow_cmdline_args)
            self._batch_input_args, unused_args = self.batchProcessingApplet.parse_known_cmdline_args(
                workflow_cmdline_args)
        else:
            unused_args = None
            self._data_export_args = None
            self._batch_input_args = None

        if unused_args:
            logger.warn("Unused command-line args: {}".format(unused_args))

    @property
    def applets(self):
        return self._applets

    def _createDivisionDetectionApplet(self, selectedFeatures=dict()):
        return ObjectClassificationApplet(
            workflow=self,
            name="Division Detection (optional)",
            projectFileGroupName="DivisionDetection",
            selectedFeatures=selectedFeatures)

    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def prepareForNewLane(self, laneIndex):
        # Store division and cell classifiers
        if self.divisionDetectionApplet:
            opDivisionClassification = self.divisionDetectionApplet.topLevelOperator
            if opDivisionClassification.classifier_cache.Output.ready() and \
               not opDivisionClassification.classifier_cache._dirty:
                self.stored_division_classifier = opDivisionClassification.classifier_cache.Output.value
            else:
                self.stored_division_classifier = None

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        if opCellClassification.classifier_cache.Output.ready() and \
           not opCellClassification.classifier_cache._dirty:
            self.stored_cell_classifier = opCellClassification.classifier_cache.Output.value
        else:
            self.stored_cell_classifier = None

    def handleNewLanesAdded(self):
        """
        If new lanes were added, then we invalidated our classifiers unecessarily.
        Here, we can restore the classifier so it doesn't need to be retrained.
        """

        # If we have stored division and cell classifiers, restore them into the workflow now.
        if self.stored_division_classifier:
            opDivisionClassification = self.divisionDetectionApplet.topLevelOperator
            opDivisionClassification.classifier_cache.forceValue(
                self.stored_division_classifier)
            # Release reference
            self.stored_division_classifier = None

        # If we have stored division and cell classifiers, restore them into the workflow now.
        if self.stored_cell_classifier:
            opCellClassification = self.cellClassificationApplet.topLevelOperator
            opCellClassification.classifier_cache.forceValue(
                self.stored_cell_classifier)
            # Release reference
            self.stored_cell_classifier = None

    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        if not self.fromBinary:
            opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(
                laneIndex)
        if self.withOptTrans:
            opOptTranslation = self.opticalTranslationApplet.topLevelOperator.getLane(
                laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(
            laneIndex)

        if self.divisionDetectionApplet:
            opDivDetection = self.divisionDetectionApplet.topLevelOperator.getLane(
                laneIndex)

        opCellClassification = self.cellClassificationApplet.topLevelOperator.getLane(
            laneIndex)
        opTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opDataExport = self.dataExportApplet.topLevelOperator.getLane(
            laneIndex)

        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])

        if not self.fromBinary:
            opTwoLevelThreshold.InputImage.connect(opData.ImageGroup[1])
            opTwoLevelThreshold.RawInput.connect(
                opData.ImageGroup[0])  # Used for display only
            # opTwoLevelThreshold.Channel.setValue(1)
            binarySrc = opTwoLevelThreshold.CachedOutput
        else:
            binarySrc = opData.ImageGroup[1]

        # Use Op5ifyers for both input datasets such that they are guaranteed to
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(binarySrc)

        if self.withOptTrans:
            opOptTranslation.RawImage.connect(op5Raw.Output)
            opOptTranslation.BinaryImage.connect(op5Binary.Output)

        # # Connect operators ##
        opObjExtraction.RawImage.connect(op5Raw.Output)
        opObjExtraction.BinaryImage.connect(op5Binary.Output)
        if self.withOptTrans:
            opObjExtraction.TranslationVectors.connect(
                opOptTranslation.TranslationVectors)

        if self.divisionDetectionApplet:
            opDivDetection.BinaryImages.connect(op5Binary.Output)
            opDivDetection.RawImages.connect(op5Raw.Output)
            opDivDetection.SegmentationImages.connect(
                opObjExtraction.LabelImage)
            opDivDetection.ObjectFeatures.connect(
                opObjExtraction.RegionFeaturesAll)
            opDivDetection.ComputedFeatureNames.connect(
                opObjExtraction.ComputedFeatureNamesAll)

        opCellClassification.BinaryImages.connect(op5Binary.Output)
        opCellClassification.RawImages.connect(op5Raw.Output)
        opCellClassification.SegmentationImages.connect(
            opObjExtraction.LabelImage)
        opCellClassification.ObjectFeatures.connect(
            opObjExtraction.RegionFeaturesVigra)
        opCellClassification.ComputedFeatureNames.connect(
            opObjExtraction.FeatureNamesVigra)

        if self.divisionDetectionApplet:
            opTracking.ObjectFeaturesWithDivFeatures.connect(
                opObjExtraction.RegionFeaturesAll)
            opTracking.ComputedFeatureNamesWithDivFeatures.connect(
                opObjExtraction.ComputedFeatureNamesAll)
            opTracking.DivisionProbabilities.connect(
                opDivDetection.Probabilities)

        opTracking.RawImage.connect(op5Raw.Output)
        opTracking.LabelImage.connect(opObjExtraction.LabelImage)
        opTracking.ObjectFeatures.connect(opObjExtraction.RegionFeaturesVigra)
        opTracking.ComputedFeatureNames.connect(
            opObjExtraction.FeatureNamesVigra)
        opTracking.DetectionProbabilities.connect(
            opCellClassification.Probabilities)
        opTracking.NumLabels.connect(opCellClassification.NumLabels)

        opDataExport.Inputs.resize(3)
        opDataExport.Inputs[0].connect(opTracking.RelabeledImage)
        opDataExport.Inputs[1].connect(opTracking.Output)
        opDataExport.Inputs[2].connect(opTracking.MergerOutput)
        opDataExport.RawData.connect(op5Raw.Output)
        opDataExport.RawDatasetInfo.connect(opData.DatasetGroup[0])

    def prepare_lane_for_export(self, lane_index):
        # Bypass cache on headless mode and batch processing mode
        self.objectExtractionApplet.topLevelOperator[
            lane_index].BypassModeEnabled.setValue(True)

        # Get axes info
        maxt = self.trackingApplet.topLevelOperator[
            lane_index].RawImage.meta.shape[0]
        maxx = self.trackingApplet.topLevelOperator[
            lane_index].RawImage.meta.shape[1]
        maxy = self.trackingApplet.topLevelOperator[
            lane_index].RawImage.meta.shape[2]
        maxz = self.trackingApplet.topLevelOperator[
            lane_index].RawImage.meta.shape[3]
        time_enum = range(maxt)
        x_range = (0, maxx)
        y_range = (0, maxy)
        z_range = (0, maxz)

        ndim = 2
        if (z_range[1] - z_range[0]) > 1:
            ndim = 3

        parameters = self.trackingApplet.topLevelOperator.Parameters.value

        # Save state of axis ranges
        if 'time_range' in parameters:
            self.prev_time_range = parameters['time_range']
        else:
            self.prev_time_range = time_enum

        if 'x_range' in parameters:
            self.prev_x_range = parameters['x_range']
        else:
            self.prev_x_range = x_range

        if 'y_range' in parameters:
            self.prev_y_range = parameters['y_range']
        else:
            self.prev_y_range = y_range

        if 'z_range' in parameters:
            self.prev_z_range = parameters['z_range']
        else:
            self.prev_z_range = z_range

        self.trackingApplet.topLevelOperator[lane_index].track(
            time_range=time_enum,
            x_range=x_range,
            y_range=y_range,
            z_range=z_range,
            size_range=parameters['size_range'],
            x_scale=parameters['scales'][0],
            y_scale=parameters['scales'][1],
            z_scale=parameters['scales'][2],
            maxDist=parameters['maxDist'],
            maxObj=parameters['maxObj'],
            divThreshold=parameters['divThreshold'],
            avgSize=parameters['avgSize'],
            withTracklets=parameters['withTracklets'],
            sizeDependent=parameters['sizeDependent'],
            divWeight=parameters['divWeight'],
            transWeight=parameters['transWeight'],
            withDivisions=parameters['withDivisions'],
            withOpticalCorrection=parameters['withOpticalCorrection'],
            withClassifierPrior=parameters['withClassifierPrior'],
            ndim=ndim,
            withMergerResolution=parameters['withMergerResolution'],
            borderAwareWidth=parameters['borderAwareWidth'],
            withArmaCoordinates=parameters['withArmaCoordinates'],
            cplex_timeout=parameters['cplex_timeout'],
            appearance_cost=parameters['appearanceCost'],
            disappearance_cost=parameters['disappearanceCost'],
            max_nearest_neighbors=parameters['max_nearest_neighbors'],
            numFramesPerSplit=parameters['numFramesPerSplit'],
            force_build_hypotheses_graph=False,
            withBatchProcessing=True)

    def post_process_lane_export(self, lane_index, checkOverwriteFiles=False):
        # `time` parameter ensures we check only once for files that could be overwritten, pop up
        # the MessageBox and then don't export (time=0). For the next round we click the export button,
        # we really want it to export, so time=1. The default parameter is 1, so everything but not 0,
        # in order to ensure writing out even in headless mode.

        # FIXME: This probably only works for the non-blockwise export slot.
        #        We should assert that the user isn't using the blockwise slot.

        # Plugin export if selected
        logger.info(
            "Export source is: " +
            self.dataExportApplet.topLevelOperator.SelectedExportSource.value)

        if self.dataExportApplet.topLevelOperator.SelectedExportSource.value == OpTrackingBaseDataExport.PluginOnlyName:
            logger.info("Export source plugin selected!")
            selectedPlugin = self.dataExportApplet.topLevelOperator.SelectedPlugin.value

            exportPluginInfo = pluginManager.getPluginByName(
                selectedPlugin, category="TrackingExportFormats")
            if exportPluginInfo is None:
                logger.error("Could not find selected plugin %s" %
                             exportPluginInfo)
            else:
                exportPlugin = exportPluginInfo.plugin_object
                logger.info("Exporting tracking result using %s" %
                            selectedPlugin)
                name_format = self.dataExportApplet.topLevelOperator.getLane(
                    lane_index).OutputFilenameFormat.value
                partially_formatted_name = self.getPartiallyFormattedName(
                    lane_index, name_format)

                if exportPlugin.exportsToFile:
                    filename = partially_formatted_name
                    if os.path.basename(filename) == '':
                        filename = os.path.join(filename, 'pluginExport.txt')
                else:
                    filename = os.path.dirname(partially_formatted_name)

                if filename is None or len(str(filename)) == 0:
                    logger.error(
                        "Cannot export from plugin with empty output filename")
                    return

                exportStatus = self.trackingApplet.topLevelOperator.getLane(
                    lane_index).exportPlugin(filename, exportPlugin,
                                             checkOverwriteFiles)
                if not exportStatus:
                    return False
                logger.info("Export done")

            return

        # CSV Table export (only if plugin was not selected)
        settings, selected_features = self.trackingApplet.topLevelOperator.getLane(
            lane_index).get_table_export_settings()
        if settings:
            self.dataExportApplet.progressSignal.emit(0)
            name_format = settings['file path']
            partially_formatted_name = self.getPartiallyFormattedName(
                lane_index, name_format)
            settings['file path'] = partially_formatted_name

            req = self.trackingApplet.topLevelOperator.getLane(
                lane_index
            ).export_object_data(
                lane_index,
                # FIXME: Even in non-headless mode, we can't show the gui because we're running in a non-main thread.
                #        That's not a huge deal, because there's still a progress bar for the overall export.
                show_gui=False)

            req.wait()
            self.dataExportApplet.progressSignal.emit(100)

            # Restore option to bypass cache to false
            self.objectExtractionApplet.topLevelOperator[
                lane_index].BypassModeEnabled.setValue(False)

            # Restore state of axis ranges
            parameters = self.trackingApplet.topLevelOperator.Parameters.value
            parameters['time_range'] = self.prev_time_range
            parameters['x_range'] = self.prev_x_range
            parameters['y_range'] = self.prev_y_range
            parameters['z_range'] = self.prev_z_range

    def getPartiallyFormattedName(self, lane_index, path_format_string):
        ''' Takes the format string for the output file, fills in the most important placeholders, and returns it '''
        raw_dataset_info = self.dataSelectionApplet.topLevelOperator.DatasetGroup[
            lane_index][0].value
        project_path = self.shell.projectManager.currentProjectPath
        project_dir = os.path.dirname(project_path)
        dataset_dir = PathComponents(
            raw_dataset_info.filePath).externalDirectory
        abs_dataset_dir = make_absolute(dataset_dir, cwd=project_dir)
        known_keys = {}
        known_keys['dataset_dir'] = abs_dataset_dir
        nickname = raw_dataset_info.nickname.replace('*', '')
        if os.path.pathsep in nickname:
            nickname = PathComponents(nickname.split(
                os.path.pathsep)[0]).fileNameBase
        known_keys['nickname'] = nickname
        opDataExport = self.dataExportApplet.topLevelOperator.getLane(
            lane_index)
        known_keys[
            'result_type'] = self.dataExportApplet.topLevelOperator.SelectedPlugin._value
        # use partial formatting to fill in non-coordinate name fields
        partially_formatted_name = format_known_keys(path_format_string,
                                                     known_keys)
        return partially_formatted_name

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False

        return input_ready

    def onProjectLoaded(self, projectManager):
        """
        Overridden from Workflow base class.  Called by the Project Manager.
        
        If the user provided command-line arguments, use them to configure 
        the workflow inputs and output settings.
        """

        # Configure the data export operator.
        if self._data_export_args:
            self.dataExportApplet.configure_operator_with_parsed_args(
                self._data_export_args)

        # Configure headless mode.
        if self._headless and self._batch_input_args and self._data_export_args:
            logger.info("Beginning Batch Processing")
            self.batchProcessingApplet.run_export_from_parsed_args(
                self._batch_input_args)
            logger.info("Completed Batch Processing")

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        if not self.fromBinary:
            opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
            thresholdingOutput = opThresholding.CachedOutput
            thresholding_ready = input_ready and \
                           len(thresholdingOutput) > 0
        else:
            thresholding_ready = True and input_ready

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.ComputedFeatureNamesAll
        features_ready = thresholding_ready and \
                         len(objectExtractionOutput) > 0

        objectCountClassifier_ready = features_ready

        opTracking = self.trackingApplet.topLevelOperator
        tracking_ready = objectCountClassifier_ready

        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.trackingApplet.busy
        busy |= self.dataExportApplet.busy
        busy |= self.batchProcessingApplet.busy
        self._shell.enableProjectChanges(not busy)

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        if not self.fromBinary:
            self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet,
                                         input_ready and not busy)

        if self.divisionDetectionApplet:
            self._shell.setAppletEnabled(self.divisionDetectionApplet,
                                         features_ready and not busy)

        self._shell.setAppletEnabled(self.objectExtractionApplet,
                                     thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.cellClassificationApplet,
                                     features_ready and not busy)
        self._shell.setAppletEnabled(self.trackingApplet,
                                     objectCountClassifier_ready and not busy)
        self._shell.setAppletEnabled(self.dataExportApplet, tracking_ready and not busy and \
                                    self.dataExportApplet.topLevelOperator.Inputs[0][0].ready() )
        self._shell.setAppletEnabled(self.batchProcessingApplet, tracking_ready and not busy and \
                                    self.dataExportApplet.topLevelOperator.Inputs[0][0].ready() )
class ConservationTrackingWorkflowBase( Workflow ):
    workflowName = "Automatic Tracking Workflow (Conservation Tracking) BASE"

    def __init__( self, shell, headless, workflow_cmdline_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        # if 'withOptTrans' in kwargs:
        #     self.withOptTrans = kwargs['withOptTrans']
        # if 'fromBinary' in kwargs:
        #     self.fromBinary = kwargs['fromBinary']
        super(ConservationTrackingWorkflowBase, self).__init__(shell, headless, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        ## Create applets 
        self.dataSelectionApplet = DataSelectionApplet(self, 
                                                       "Input Data", 
                                                       "Input Data", 
                                                       forceAxisOrder='txyzc',
                                                       instructionText=data_instructions,
                                                       max_lanes=1
                                                       )
        
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Binary Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )
                
        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self, 
                                                                  "Threshold and Size Filter", 
                                                                  "ThresholdTwoLevels" )        
        if self.withOptTrans:
            self.opticalTranslationApplet = OpticalTranslationApplet(workflow=self)
                                                                   
        self.objectExtractionApplet = TrackingFeatureExtractionApplet(workflow=self, interactive=False,
                                                                      name="Object Feature Computation")                                                                      
        
        self.divisionDetectionApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Division Detection (optional)",
                                                                     projectFileGroupName="DivisionDetection")
        
        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification (optional)",
                                                                     projectFileGroupName="CountClassification")
                
        self.trackingApplet = ConservationTrackingApplet( workflow=self )

        self.dataExportApplet = TrackingBaseDataExportApplet(self, "Tracking Result Export")

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue( ['Tracking Result', 'Merger Result', 'Object Identities'] )
        opDataExport.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        
        # Extra configuration for object export table (as CSV table or HDF5 table)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet.set_exporting_operator(opTracking)
        self.dataExportApplet.post_process_lane_export = self.post_process_lane_export
        
        self._applets = []                
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        if self.withOptTrans:
            self._applets.append(self.opticalTranslationApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)
        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
        
    @property
    def applets(self):
        return self._applets
    
    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName
    
    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        if not self.fromBinary:
            opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(laneIndex)
        if self.withOptTrans:
            opOptTranslation = self.opticalTranslationApplet.topLevelOperator.getLane(laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(laneIndex)    
        opDivDetection = self.divisionDetectionApplet.topLevelOperator.getLane(laneIndex)
        opCellClassification = self.cellClassificationApplet.topLevelOperator.getLane(laneIndex)
        opTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opDataExport = self.dataExportApplet.topLevelOperator.getLane(laneIndex)
        
        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])
        
        if not self.fromBinary:
            opTwoLevelThreshold.InputImage.connect(opData.ImageGroup[1])
            opTwoLevelThreshold.RawInput.connect(opData.ImageGroup[0])  # Used for display only
            # opTwoLevelThreshold.Channel.setValue(1)
            binarySrc = opTwoLevelThreshold.CachedOutput
        else:
            binarySrc = opData.ImageGroup[1]
        
        # Use Op5ifyers for both input datasets such that they are guaranteed to 
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)         
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(binarySrc)
        
        if self.withOptTrans:
            opOptTranslation.RawImage.connect(op5Raw.Output)
            opOptTranslation.BinaryImage.connect(op5Binary.Output)
        
        # # Connect operators ##       
        vigra_features = list((set(config.vigra_features)).union(config.selected_features_objectcount[config.features_vigra_name])) 
        feature_names_vigra = {}
        feature_names_vigra[config.features_vigra_name] = { name: {} for name in vigra_features }
        opObjExtraction.RawImage.connect(op5Raw.Output)
        opObjExtraction.BinaryImage.connect(op5Binary.Output)
        if self.withOptTrans:
            opObjExtraction.TranslationVectors.connect(opOptTranslation.TranslationVectors)
        opObjExtraction.FeatureNamesVigra.setValue(feature_names_vigra)
        feature_dict_division = {}
        feature_dict_division[config.features_division_name] = { name: {} for name in config.division_features }
        opObjExtraction.FeatureNamesDivision.setValue(feature_dict_division)
        
        
        selected_features_div = {}
        for plugin_name in config.selected_features_division.keys():
            selected_features_div[plugin_name] = { name: {} for name in config.selected_features_division[plugin_name] }
        # FIXME: do not hard code this
        for name in [ 'SquaredDistances_' + str(i) for i in range(config.n_best_successors) ]:
            selected_features_div[config.features_division_name][name] = {}
            
        opDivDetection.BinaryImages.connect( op5Binary.Output )
        opDivDetection.RawImages.connect( op5Raw.Output )        
        opDivDetection.LabelsAllowedFlags.connect(opData.AllowLabels)
        opDivDetection.SegmentationImages.connect(opObjExtraction.LabelImage)
        opDivDetection.ObjectFeatures.connect(opObjExtraction.RegionFeaturesAll)
        opDivDetection.ComputedFeatureNames.connect(opObjExtraction.ComputedFeatureNamesAll)
        opDivDetection.SelectedFeatures.setValue(selected_features_div)
        opDivDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])        
        opDivDetection.AllowDeleteLabels.setValue(False)
        opDivDetection.AllowAddLabel.setValue(False)
        opDivDetection.EnableLabelTransfer.setValue(False)
        
        selected_features_objectcount = {}
        for plugin_name in config.selected_features_objectcount.keys():
            selected_features_objectcount[plugin_name] = { name: {} for name in config.selected_features_objectcount[plugin_name] }
        opCellClassification.BinaryImages.connect( op5Binary.Output )
        opCellClassification.RawImages.connect( op5Raw.Output )
        opCellClassification.LabelsAllowedFlags.connect(opData.AllowLabels)
        opCellClassification.SegmentationImages.connect(opObjExtraction.LabelImage)
        opCellClassification.ObjectFeatures.connect(opObjExtraction.RegionFeaturesVigra)
        opCellClassification.ComputedFeatureNames.connect(opObjExtraction.ComputedFeatureNamesVigra)
        opCellClassification.SelectedFeatures.setValue( selected_features_objectcount )        
        opCellClassification.SuggestedLabelNames.setValue( ['false detection',] + [str(i) + ' Objects' for i in range(1,10) ] )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)
        
        opTracking.RawImage.connect( op5Raw.Output )
        opTracking.LabelImage.connect( opObjExtraction.LabelImage )
        opTracking.ObjectFeatures.connect( opObjExtraction.RegionFeaturesVigra )
        opTracking.ObjectFeaturesWithDivFeatures.connect( opObjExtraction.RegionFeaturesAll)
        opTracking.ComputedFeatureNames.connect( opObjExtraction.ComputedFeatureNamesVigra )
        opTracking.ComputedFeatureNamesWithDivFeatures.connect( opObjExtraction.ComputedFeatureNamesAll )
        opTracking.DivisionProbabilities.connect( opDivDetection.Probabilities )
        opTracking.DetectionProbabilities.connect( opCellClassification.Probabilities )
        opTracking.NumLabels.connect( opCellClassification.NumLabels )
    
        opDataExport.Inputs.resize(3)
        opDataExport.Inputs[0].connect( opTracking.Output )
        opDataExport.Inputs[1].connect( opTracking.MergerOutput )
        opDataExport.Inputs[2].connect( opTracking.LabelImage )
        opDataExport.RawData.connect( op5Raw.Output )
        opDataExport.RawDatasetInfo.connect( opData.DatasetGroup[0] )

    def post_process_lane_export(self, lane_index):
        # FIXME: This probably only works for the non-blockwise export slot.
        #        We should assert that the user isn't using the blockwise slot.
        settings, selected_features = self.trackingApplet.topLevelOperator.getLane(lane_index).get_table_export_settings()
        if settings:
            raw_dataset_info = self.dataSelectionApplet.topLevelOperator.DatasetGroup[lane_index][0].value
            if raw_dataset_info.location == DatasetInfo.Location.FileSystem:
                filename_suffix = raw_dataset_info.nickname
            else:
                filename_suffix = str(lane_index)
            req = self.trackingApplet.topLevelOperator.getLane(lane_index).export_object_data(
                        lane_index, 
                        # FIXME: Even in non-headless mode, we can't show the gui because we're running in a non-main thread.
                        #        That's not a huge deal, because there's still a progress bar for the overall export.
                        show_gui=False, 
                        filename_suffix=filename_suffix)
            req.wait()         
    
    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False

        return input_ready

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        if not self.fromBinary:
            opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
            thresholdingOutput = opThresholding.CachedOutput
            thresholding_ready = input_ready and \
                           len(thresholdingOutput) > 0
        else:
            thresholding_ready = True and input_ready

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.ComputedFeatureNamesAll
        features_ready = thresholding_ready and \
                         len(objectExtractionOutput) > 0

        objectCountClassifier_ready = features_ready

        opTracking = self.trackingApplet.topLevelOperator
        tracking_ready = objectCountClassifier_ready and \
                           len(opTracking.EventsVector) > 0
                           

        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.trackingApplet.busy
        busy |= self.dataExportApplet.busy
        self._shell.enableProjectChanges( not busy )

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        if not self.fromBinary:
            self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet, input_ready and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet, thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.cellClassificationApplet, features_ready and not busy)
        self._shell.setAppletEnabled(self.divisionDetectionApplet, features_ready and not busy)
        self._shell.setAppletEnabled(self.trackingApplet, objectCountClassifier_ready and not busy)
        self._shell.setAppletEnabled(self.dataExportApplet, tracking_ready and not busy and \
                                    self.dataExportApplet.topLevelOperator.Inputs[0][0].ready() )
Beispiel #28
0
class ConservationTrackingWorkflowBase(Workflow):
    workflowName = "Automatic Tracking Workflow (Conservation Tracking) BASE"

    def __init__(self, shell, headless, workflow_cmdline_args,
                 project_creation_args, *args, **kwargs):
        graph = kwargs["graph"] if "graph" in kwargs else Graph()
        if "graph" in kwargs:
            del kwargs["graph"]
        # if 'withOptTrans' in kwargs:
        #     self.withOptTrans = kwargs['withOptTrans']
        # if 'fromBinary' in kwargs:
        #     self.fromBinary = kwargs['fromBinary']
        super(ConservationTrackingWorkflowBase,
              self).__init__(shell,
                             headless,
                             workflow_cmdline_args,
                             project_creation_args,
                             graph=graph,
                             *args,
                             **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Variables to store division and cell classifiers to prevent retraining every-time batch processing runs
        self.stored_division_classifier = None
        self.stored_cell_classifier = None

        ## Create applets
        self.dataSelectionApplet = DataSelectionApplet(
            self,
            "Input Data",
            "Input Data",
            forceAxisOrder=["txyzc"],
            instructionText=data_instructions,
            max_lanes=None,
        )

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue(
                ["Raw Data", "Segmentation Image"])
        else:
            opDataSelection.DatasetRoles.setValue(
                ["Raw Data", "Prediction Maps"])

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet(
                self, "Threshold and Size Filter", "ThresholdTwoLevels")

        self.objectExtractionApplet = TrackingFeatureExtractionApplet(
            workflow=self,
            interactive=False,
            name="Object Feature Computation")

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator

        self.divisionDetectionApplet = self._createDivisionDetectionApplet(
            configConservation.selectedFeaturesDiv)  # Might be None

        if self.divisionDetectionApplet:
            feature_dict_division = {}
            feature_dict_division[config.features_division_name] = {
                name: {}
                for name in config.division_features
            }
            opObjectExtraction.FeatureNamesDivision.setValue(
                feature_dict_division)

            selected_features_div = {}
            for plugin_name in list(config.selected_features_division.keys()):
                selected_features_div[plugin_name] = {
                    name: {}
                    for name in config.selected_features_division[plugin_name]
                }
            # FIXME: do not hard code this
            for name in [
                    "SquaredDistances_" + str(i)
                    for i in range(config.n_best_successors)
            ]:
                selected_features_div[config.features_division_name][name] = {}

            opDivisionDetection = self.divisionDetectionApplet.topLevelOperator
            opDivisionDetection.SelectedFeatures.setValue(
                configConservation.selectedFeaturesDiv)
            opDivisionDetection.LabelNames.setValue(
                ["Not Dividing", "Dividing"])
            opDivisionDetection.AllowDeleteLabels.setValue(False)
            opDivisionDetection.AllowAddLabel.setValue(False)
            opDivisionDetection.EnableLabelTransfer.setValue(False)

        self.cellClassificationApplet = ObjectClassificationApplet(
            workflow=self,
            name="Object Count Classification",
            projectFileGroupName="CountClassification",
            selectedFeatures=configConservation.selectedFeaturesObjectCount,
        )

        selected_features_objectcount = {}
        for plugin_name in list(config.selected_features_objectcount.keys()):
            selected_features_objectcount[plugin_name] = {
                name: {}
                for name in config.selected_features_objectcount[plugin_name]
            }

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(
            configConservation.selectedFeaturesObjectCount)
        opCellClassification.SuggestedLabelNames.setValue(
            ["False Detection"] + [str(1) + " Object"] +
            [str(i) + " Objects" for i in range(2, 10)])
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

        self.trackingApplet = ConservationTrackingApplet(workflow=self)

        self.default_export_filename = "{dataset_dir}/{nickname}-exported_data.csv"
        self.dataExportApplet = TrackingBaseDataExportApplet(
            self,
            "Tracking Result Export",
            default_export_filename=self.default_export_filename,
            pluginExportFunc=self._pluginExportFunc,
        )

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue(
            ["Object-Identities", "Tracking-Result", "Merger-Result"])
        opDataExport.WorkingDirectory.connect(opDataSelection.WorkingDirectory)

        # Extra configuration for object export table (as CSV table or HDF5 table)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet.set_exporting_operator(opTracking)
        self.dataExportApplet.prepare_lane_for_export = self.prepare_lane_for_export

        # configure export settings
        # settings = {'file path': self.default_export_filename, 'compression': {}, 'file type': 'csv'}
        # selected_features = ['Count', 'RegionCenter', 'RegionRadii', 'RegionAxes']
        # opTracking.ExportSettings.setValue( (settings, selected_features) )

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.objectExtractionApplet)

        if self.divisionDetectionApplet:
            self._applets.append(self.divisionDetectionApplet)

        self.batchProcessingApplet = BatchProcessingApplet(
            self, "Batch Processing", self.dataSelectionApplet,
            self.dataExportApplet)

        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
        self._applets.append(self.batchProcessingApplet)

        # Parse export and batch command-line arguments for headless mode
        if workflow_cmdline_args:
            self._data_export_args, unused_args = self.dataExportApplet.parse_known_cmdline_args(
                workflow_cmdline_args)
            self._batch_input_args, unused_args = self.batchProcessingApplet.parse_known_cmdline_args(
                workflow_cmdline_args)

        else:
            unused_args = None
            self._data_export_args = None
            self._batch_input_args = None

        if unused_args:
            logger.warning("Unused command-line args: {}".format(unused_args))

    @property
    def applets(self):
        return self._applets

    def _createDivisionDetectionApplet(self, selectedFeatures=dict()):
        return ObjectClassificationApplet(
            workflow=self,
            name="Division Detection (optional)",
            projectFileGroupName="DivisionDetection",
            selectedFeatures=selectedFeatures,
        )

    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def prepareForNewLane(self, laneIndex):
        # Store division and cell classifiers
        if self.divisionDetectionApplet:
            opDivisionClassification = self.divisionDetectionApplet.topLevelOperator
            if (opDivisionClassification.classifier_cache.Output.ready()
                    and not opDivisionClassification.classifier_cache._dirty):
                self.stored_division_classifier = opDivisionClassification.classifier_cache.Output.value
            else:
                self.stored_division_classifier = None

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        if opCellClassification.classifier_cache.Output.ready(
        ) and not opCellClassification.classifier_cache._dirty:
            self.stored_cell_classifier = opCellClassification.classifier_cache.Output.value
        else:
            self.stored_cell_classifier = None

    def handleNewLanesAdded(self):
        """
        If new lanes were added, then we invalidated our classifiers unecessarily.
        Here, we can restore the classifier so it doesn't need to be retrained.
        """

        # If we have stored division and cell classifiers, restore them into the workflow now.
        if self.stored_division_classifier:
            opDivisionClassification = self.divisionDetectionApplet.topLevelOperator
            opDivisionClassification.classifier_cache.forceValue(
                self.stored_division_classifier)
            # Release reference
            self.stored_division_classifier = None

        # If we have stored division and cell classifiers, restore them into the workflow now.
        if self.stored_cell_classifier:
            opCellClassification = self.cellClassificationApplet.topLevelOperator
            opCellClassification.classifier_cache.forceValue(
                self.stored_cell_classifier)
            # Release reference
            self.stored_cell_classifier = None

    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        if not self.fromBinary:
            opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(
                laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(
            laneIndex)
        opObjExtraction.setDefaultFeatures(
            configConservation.allFeaturesObjectCount)

        if self.divisionDetectionApplet:
            opDivDetection = self.divisionDetectionApplet.topLevelOperator.getLane(
                laneIndex)

        opCellClassification = self.cellClassificationApplet.topLevelOperator.getLane(
            laneIndex)
        opTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opDataExport = self.dataExportApplet.topLevelOperator.getLane(
            laneIndex)

        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])

        if not self.fromBinary:
            opTwoLevelThreshold.InputImage.connect(opData.ImageGroup[1])
            opTwoLevelThreshold.RawInput.connect(
                opData.ImageGroup[0])  # Used for display only
            # opTwoLevelThreshold.Channel.setValue(1)
            binarySrc = opTwoLevelThreshold.CachedOutput
        else:
            binarySrc = opData.ImageGroup[1]

        # Use Op5ifyers for both input datasets such that they are guaranteed to
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(binarySrc)

        # # Connect operators ##
        opObjExtraction.RawImage.connect(op5Raw.Output)
        opObjExtraction.BinaryImage.connect(op5Binary.Output)

        if self.divisionDetectionApplet:
            opDivDetection.BinaryImages.connect(op5Binary.Output)
            opDivDetection.RawImages.connect(op5Raw.Output)
            opDivDetection.SegmentationImages.connect(
                opObjExtraction.LabelImage)
            opDivDetection.ObjectFeatures.connect(
                opObjExtraction.RegionFeaturesAll)
            opDivDetection.ComputedFeatureNames.connect(
                opObjExtraction.ComputedFeatureNamesAll)

        opCellClassification.BinaryImages.connect(op5Binary.Output)
        opCellClassification.RawImages.connect(op5Raw.Output)
        opCellClassification.SegmentationImages.connect(
            opObjExtraction.LabelImage)
        opCellClassification.ObjectFeatures.connect(
            opObjExtraction.RegionFeaturesVigra)
        opCellClassification.ComputedFeatureNames.connect(
            opObjExtraction.FeatureNamesVigra)

        if self.divisionDetectionApplet:
            opTracking.ObjectFeaturesWithDivFeatures.connect(
                opObjExtraction.RegionFeaturesAll)
            opTracking.ComputedFeatureNamesWithDivFeatures.connect(
                opObjExtraction.ComputedFeatureNamesAll)
            opTracking.DivisionProbabilities.connect(
                opDivDetection.Probabilities)

        opTracking.RawImage.connect(op5Raw.Output)
        opTracking.LabelImage.connect(opObjExtraction.LabelImage)
        opTracking.ObjectFeatures.connect(opObjExtraction.RegionFeaturesVigra)
        opTracking.ComputedFeatureNames.connect(
            opObjExtraction.FeatureNamesVigra)
        opTracking.DetectionProbabilities.connect(
            opCellClassification.Probabilities)
        opTracking.NumLabels.connect(opCellClassification.NumLabels)

        opDataExport.Inputs.resize(3)
        opDataExport.Inputs[0].connect(opTracking.RelabeledImage)
        opDataExport.Inputs[1].connect(opTracking.Output)
        opDataExport.Inputs[2].connect(opTracking.MergerOutput)
        opDataExport.RawData.connect(op5Raw.Output)
        opDataExport.RawDatasetInfo.connect(opData.DatasetGroup[0])

    def prepare_lane_for_export(self, lane_index):
        # Bypass cache on headless mode and batch processing mode
        self.objectExtractionApplet.topLevelOperator[
            lane_index].BypassModeEnabled.setValue(True)

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet.topLevelOperator[
                lane_index].opCache.BypassModeEnabled.setValue(True)
            self.thresholdTwoLevelsApplet.topLevelOperator[
                lane_index].opSmootherCache.BypassModeEnabled.setValue(True)

        # Get axes info
        maxt = self.trackingApplet.topLevelOperator[
            lane_index].RawImage.meta.shape[0]
        maxx = self.trackingApplet.topLevelOperator[
            lane_index].RawImage.meta.shape[1]
        maxy = self.trackingApplet.topLevelOperator[
            lane_index].RawImage.meta.shape[2]
        maxz = self.trackingApplet.topLevelOperator[
            lane_index].RawImage.meta.shape[3]
        time_enum = list(range(maxt))
        x_range = (0, maxx)
        y_range = (0, maxy)
        z_range = (0, maxz)

        ndim = 2
        if (z_range[1] - z_range[0]) > 1:
            ndim = 3

        parameters = self.trackingApplet.topLevelOperator.Parameters.value

        # Save state of axis ranges
        if "time_range" in parameters:
            self.prev_time_range = parameters["time_range"]
        else:
            self.prev_time_range = time_enum

        if "x_range" in parameters:
            self.prev_x_range = parameters["x_range"]
        else:
            self.prev_x_range = x_range

        if "y_range" in parameters:
            self.prev_y_range = parameters["y_range"]
        else:
            self.prev_y_range = y_range

        if "z_range" in parameters:
            self.prev_z_range = parameters["z_range"]
        else:
            self.prev_z_range = z_range

        if "numFramesPerSplit" in parameters:
            numFramesPerSplit = parameters["numFramesPerSplit"]
        else:
            numFramesPerSplit = 0

        self.trackingApplet.topLevelOperator[lane_index].track(
            time_range=time_enum,
            x_range=x_range,
            y_range=y_range,
            z_range=z_range,
            size_range=parameters["size_range"],
            x_scale=parameters["scales"][0],
            y_scale=parameters["scales"][1],
            z_scale=parameters["scales"][2],
            maxDist=parameters["maxDist"],
            maxObj=parameters["maxObj"],
            divThreshold=parameters["divThreshold"],
            avgSize=parameters["avgSize"],
            withTracklets=parameters["withTracklets"],
            sizeDependent=parameters["sizeDependent"],
            divWeight=parameters["divWeight"],
            transWeight=parameters["transWeight"],
            withDivisions=parameters["withDivisions"],
            withOpticalCorrection=parameters["withOpticalCorrection"],
            withClassifierPrior=parameters["withClassifierPrior"],
            ndim=ndim,
            withMergerResolution=parameters["withMergerResolution"],
            borderAwareWidth=parameters["borderAwareWidth"],
            withArmaCoordinates=parameters["withArmaCoordinates"],
            cplex_timeout=parameters["cplex_timeout"],
            appearance_cost=parameters["appearanceCost"],
            disappearance_cost=parameters["disappearanceCost"],
            max_nearest_neighbors=parameters["max_nearest_neighbors"],
            numFramesPerSplit=numFramesPerSplit,
            force_build_hypotheses_graph=False,
            withBatchProcessing=True,
        )

    def _pluginExportFunc(self, lane_index, filename, exportPlugin,
                          checkOverwriteFiles, plugArgsSlot) -> int:
        return self.trackingApplet.topLevelOperator.getLane(
            lane_index).exportPlugin(filename, exportPlugin,
                                     checkOverwriteFiles, plugArgsSlot)

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and all(
                    [sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False

        return input_ready

    def onProjectLoaded(self, projectManager):
        """
        Overridden from Workflow base class.  Called by the Project Manager.

        If the user provided command-line arguments, use them to configure
        the workflow inputs and output settings.
        """

        # Configure the data export operator.
        if self._data_export_args:
            self.dataExportApplet.configure_operator_with_parsed_args(
                self._data_export_args)

        # Configure headless mode.
        if self._headless and self._batch_input_args and self._data_export_args:
            logger.info("Beginning Batch Processing")
            self.batchProcessingApplet.run_export_from_parsed_args(
                self._batch_input_args)
            logger.info("Completed Batch Processing")

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        if not self.fromBinary:
            opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
            thresholdingOutput = opThresholding.CachedOutput
            thresholding_ready = input_ready and len(thresholdingOutput) > 0
        else:
            thresholding_ready = True and input_ready

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.ComputedFeatureNamesAll
        features_ready = thresholding_ready and len(objectExtractionOutput) > 0

        objectCountClassifier_ready = features_ready

        opTracking = self.trackingApplet.topLevelOperator
        tracking_ready = objectCountClassifier_ready

        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.trackingApplet.busy
        busy |= self.dataExportApplet.busy
        busy |= self.batchProcessingApplet.busy
        self._shell.enableProjectChanges(not busy)

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        if not self.fromBinary:
            self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet,
                                         input_ready and not busy)

        if self.divisionDetectionApplet:
            self._shell.setAppletEnabled(self.divisionDetectionApplet,
                                         features_ready and not busy)

        self._shell.setAppletEnabled(self.objectExtractionApplet,
                                     thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.cellClassificationApplet,
                                     features_ready and not busy)
        self._shell.setAppletEnabled(self.trackingApplet,
                                     objectCountClassifier_ready and not busy)
        self._shell.setAppletEnabled(
            self.dataExportApplet,
            tracking_ready and not busy
            and self.dataExportApplet.topLevelOperator.Inputs[0][0].ready(),
        )
        self._shell.setAppletEnabled(
            self.batchProcessingApplet,
            tracking_ready and not busy
            and self.dataExportApplet.topLevelOperator.Inputs[0][0].ready(),
        )
class ConservationTrackingWorkflowBase(Workflow):
    workflowName = "Automatic Tracking Workflow (Conservation Tracking) BASE"

    def __init__(self, shell, headless, workflow_cmdline_args, *args, **kwargs):
        graph = kwargs["graph"] if "graph" in kwargs else Graph()
        if "graph" in kwargs:
            del kwargs["graph"]
        # if 'withOptTrans' in kwargs:
        #     self.withOptTrans = kwargs['withOptTrans']
        # if 'fromBinary' in kwargs:
        #     self.fromBinary = kwargs['fromBinary']
        super(ConservationTrackingWorkflowBase, self).__init__(shell, headless, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        ## Create applets
        self.dataSelectionApplet = DataSelectionApplet(
            self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            force5d=True,
            instructionText=data_instructions,
            max_lanes=1,
        )

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue(["Raw Data", "Binary Image"])
        else:
            opDataSelection.DatasetRoles.setValue(["Raw Data", "Prediction Maps"])

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet(
                self, "Threshold and Size Filter", "ThresholdTwoLevels"
            )
        if self.withOptTrans:
            self.opticalTranslationApplet = OpticalTranslationApplet(workflow=self)

        self.objectExtractionApplet = TrackingFeatureExtractionApplet(
            workflow=self, interactive=False, name="Object Feature Computation"
        )

        self.divisionDetectionApplet = ObjectClassificationApplet(
            workflow=self, name="Division Detection (optional)", projectFileGroupName="DivisionDetection"
        )

        self.cellClassificationApplet = ObjectClassificationApplet(
            workflow=self, name="Object Count Classification (optional)", projectFileGroupName="CountClassification"
        )

        self.trackingApplet = ConservationTrackingApplet(workflow=self)
        opTracking = self.trackingApplet.topLevelOperator

        self.dataExportApplet = TrackingBaseDataExportApplet(self, "Tracking Result Export")
        self.dataExportApplet.set_exporting_operator(opTracking)

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue(["Tracking Result", "Merger Result", "Object Identities"])
        opDataExport.WorkingDirectory.connect(opDataSelection.WorkingDirectory)

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        if self.withOptTrans:
            self._applets.append(self.opticalTranslationApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)
        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)

    @property
    def applets(self):
        return self._applets

    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        if not self.fromBinary:
            opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(laneIndex)
        if self.withOptTrans:
            opOptTranslation = self.opticalTranslationApplet.topLevelOperator.getLane(laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(laneIndex)
        opDivDetection = self.divisionDetectionApplet.topLevelOperator.getLane(laneIndex)
        opCellClassification = self.cellClassificationApplet.topLevelOperator.getLane(laneIndex)
        opTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opDataExport = self.dataExportApplet.topLevelOperator.getLane(laneIndex)

        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])

        if not self.fromBinary:
            opTwoLevelThreshold.InputImage.connect(opData.ImageGroup[1])
            opTwoLevelThreshold.RawInput.connect(opData.ImageGroup[0])  # Used for display only
            # opTwoLevelThreshold.Channel.setValue(1)
            binarySrc = opTwoLevelThreshold.CachedOutput
        else:
            binarySrc = opData.ImageGroup[1]

        # Use Op5ifyers for both input datasets such that they are guaranteed to
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(binarySrc)

        if self.withOptTrans:
            opOptTranslation.RawImage.connect(op5Raw.Output)
            opOptTranslation.BinaryImage.connect(op5Binary.Output)

        # # Connect operators ##
        vigra_features = list(
            (set(config.vigra_features)).union(config.selected_features_objectcount[config.features_vigra_name])
        )
        feature_names_vigra = {}
        feature_names_vigra[config.features_vigra_name] = {name: {} for name in vigra_features}
        opObjExtraction.RawImage.connect(op5Raw.Output)
        opObjExtraction.BinaryImage.connect(op5Binary.Output)
        if self.withOptTrans:
            opObjExtraction.TranslationVectors.connect(opOptTranslation.TranslationVectors)
        opObjExtraction.FeatureNamesVigra.setValue(feature_names_vigra)
        feature_dict_division = {}
        feature_dict_division[config.features_division_name] = {name: {} for name in config.division_features}
        opObjExtraction.FeatureNamesDivision.setValue(feature_dict_division)

        selected_features_div = {}
        for plugin_name in config.selected_features_division.keys():
            selected_features_div[plugin_name] = {name: {} for name in config.selected_features_division[plugin_name]}
        # FIXME: do not hard code this
        for name in ["SquaredDistances_" + str(i) for i in range(config.n_best_successors)]:
            selected_features_div[config.features_division_name][name] = {}

        opDivDetection.BinaryImages.connect(op5Binary.Output)
        opDivDetection.RawImages.connect(op5Raw.Output)
        opDivDetection.LabelsAllowedFlags.connect(opData.AllowLabels)
        opDivDetection.SegmentationImages.connect(opObjExtraction.LabelImage)
        opDivDetection.ObjectFeatures.connect(opObjExtraction.RegionFeaturesAll)
        opDivDetection.ComputedFeatureNames.connect(opObjExtraction.ComputedFeatureNamesAll)
        opDivDetection.SelectedFeatures.setValue(selected_features_div)
        opDivDetection.LabelNames.setValue(["Not Dividing", "Dividing"])
        opDivDetection.AllowDeleteLabels.setValue(False)
        opDivDetection.AllowAddLabel.setValue(False)
        opDivDetection.EnableLabelTransfer.setValue(False)

        selected_features_objectcount = {}
        for plugin_name in config.selected_features_objectcount.keys():
            selected_features_objectcount[plugin_name] = {
                name: {} for name in config.selected_features_objectcount[plugin_name]
            }
        opCellClassification.BinaryImages.connect(op5Binary.Output)
        opCellClassification.RawImages.connect(op5Raw.Output)
        opCellClassification.LabelsAllowedFlags.connect(opData.AllowLabels)
        opCellClassification.SegmentationImages.connect(opObjExtraction.LabelImage)
        opCellClassification.ObjectFeatures.connect(opObjExtraction.RegionFeaturesVigra)
        opCellClassification.ComputedFeatureNames.connect(opObjExtraction.ComputedFeatureNamesVigra)
        opCellClassification.SelectedFeatures.setValue(selected_features_objectcount)
        opCellClassification.SuggestedLabelNames.setValue(
            ["false detection"] + [str(i) + " Objects" for i in range(1, 10)]
        )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

        opTracking.RawImage.connect(op5Raw.Output)
        opTracking.LabelImage.connect(opObjExtraction.LabelImage)
        opTracking.ObjectFeatures.connect(opObjExtraction.RegionFeaturesVigra)
        opTracking.ObjectFeaturesWithDivFeatures.connect(opObjExtraction.RegionFeaturesAll)
        opTracking.ComputedFeatureNames.connect(opObjExtraction.ComputedFeatureNamesVigra)
        opTracking.ComputedFeatureNamesWithDivFeatures.connect(opObjExtraction.ComputedFeatureNamesAll)
        opTracking.DivisionProbabilities.connect(opDivDetection.Probabilities)
        opTracking.DetectionProbabilities.connect(opCellClassification.Probabilities)
        opTracking.NumLabels.connect(opCellClassification.NumLabels)

        opDataExport.Inputs.resize(3)
        opDataExport.Inputs[0].connect(opTracking.Output)
        opDataExport.Inputs[1].connect(opTracking.MergerOutput)
        opDataExport.Inputs[2].connect(opTracking.LabelImage)
        opDataExport.RawData.connect(op5Raw.Output)
        opDataExport.RawDatasetInfo.connect(opData.DatasetGroup[0])

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False

        return input_ready

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        if not self.fromBinary:
            opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
            thresholdingOutput = opThresholding.CachedOutput
            thresholding_ready = input_ready and len(thresholdingOutput) > 0
        else:
            thresholding_ready = True and input_ready

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.ComputedFeatureNamesAll
        features_ready = thresholding_ready and len(objectExtractionOutput) > 0

        objectCountClassifier_ready = features_ready

        opTracking = self.trackingApplet.topLevelOperator
        tracking_ready = objectCountClassifier_ready and len(opTracking.EventsVector) > 0

        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.trackingApplet.busy
        busy |= self.dataExportApplet.busy
        self._shell.enableProjectChanges(not busy)

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        if not self.fromBinary:
            self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet, input_ready and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet, thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.cellClassificationApplet, features_ready and not busy)
        self._shell.setAppletEnabled(self.divisionDetectionApplet, features_ready and not busy)
        self._shell.setAppletEnabled(self.trackingApplet, objectCountClassifier_ready and not busy)
        self._shell.setAppletEnabled(
            self.dataExportApplet,
            tracking_ready and not busy and self.dataExportApplet.topLevelOperator.Inputs[0][0].ready(),
        )
class ManualTrackingWorkflow( Workflow ):
    workflowName = "Manual Tracking Workflow"
    workflowDisplayName = "Manual Tracking Workflow [Inputs: Raw Data, Pixel Prediction Map]"
    workflowDescription = "Manual tracking of objects, based on Prediction Maps or (binary) Segmentation Images"    

    @property
    def applets(self):
        return self._applets
    
    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']
        super(ManualTrackingWorkflow, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)
        
        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'\
                            'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'
        ## Create applets 
        self.dataSelectionApplet = DataSelectionApplet(self, 
                                                       "Input Data", 
                                                       "Input Data", 
                                                       forceAxisOrder='txyzc',
                                                       instructionText=data_instructions,
                                                       max_lanes=1
                                                       )
        opDataSelection = self.dataSelectionApplet.topLevelOperator
        opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )                
        
        self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self, 
                                                                  "Threshold and Size Filter", 
                                                                  "ThresholdTwoLevels" )
                     
        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",
                                                             workflow=self, interactive=False)
        
        self.trackingApplet = ManualTrackingApplet( workflow=self )
        self.default_export_filename = '{dataset_dir}/{nickname}-exported_data.csv'
        self.dataExportApplet = TrackingBaseDataExportApplet(self, 
                                                             "Tracking Result Export", 
                                                             default_export_filename=self.default_export_filename)
        
        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue( ['Manual Tracking', 'Object Identities'] )
        opDataExport.WorkingDirectory.connect( opDataSelection.WorkingDirectory )

        # Extra configuration for object export table (as CSV table or HDF5 table)
        opTracking = self.trackingApplet.topLevelOperator
        self.dataExportApplet.set_exporting_operator(opTracking)
        self.dataExportApplet.post_process_lane_export = self.post_process_lane_export
        
        self._applets = []        
        self._applets.append(self.dataSelectionApplet)        
        self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.objectExtractionApplet)        
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
            
    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)        
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(laneIndex)
        opTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)    
        opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(laneIndex)
        opDataExport = self.dataExportApplet.topLevelOperator.getLane(laneIndex)
                        
        ## Connect operators ##
        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])
        
        opTwoLevelThreshold.InputImage.connect( opData.ImageGroup[1] )
        opTwoLevelThreshold.RawInput.connect( opData.ImageGroup[0] ) # Used for display only
        # Use OpReorderAxis for both input datasets such that they are guaranteed to 
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes( parent=self )        
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect( opTwoLevelThreshold.CachedOutput )        
        
        opObjExtraction.RawImage.connect( op5Raw.Output )
        opObjExtraction.BinaryImage.connect( op5Binary.Output )
        
        opTracking.RawImage.connect( op5Raw.Output )
        opTracking.BinaryImage.connect( op5Binary.Output )
        opTracking.LabelImage.connect( opObjExtraction.LabelImage )
        opTracking.ObjectFeatures.connect( opObjExtraction.RegionFeatures )
        opTracking.ComputedFeatureNames.connect(opObjExtraction.Features)

        opDataExport.Inputs.resize(2)
        opDataExport.Inputs[0].connect( opTracking.TrackImage )
        opDataExport.Inputs[1].connect( opTracking.LabelImage )
        opDataExport.RawData.connect( op5Raw.Output )
        opDataExport.RawDatasetInfo.connect( opData.DatasetGroup[0] )

    def post_process_lane_export(self, lane_index):
        # FIXME: This probably only works for the non-blockwise export slot.
        #        We should assert that the user isn't using the blockwise slot.
        settings, selected_features = self.trackingApplet.topLevelOperator.getLane(lane_index).get_table_export_settings()
        if settings:
            raw_dataset_info = self.dataSelectionApplet.topLevelOperator.DatasetGroup[lane_index][0].value
            if raw_dataset_info.location == DatasetInfo.Location.FileSystem:
                filename_suffix = raw_dataset_info.nickname
            else:
                filename_suffix = str(lane_index)
            req = self.trackingApplet.topLevelOperator.getLane(lane_index).export_object_data(
                        lane_index, 
                        # FIXME: Even in non-headless mode, we can't show the gui because we're running in a non-main thread.
                        #        That's not a huge deal, because there's still a progress bar for the overall export.
                        show_gui=False, 
                        filename_suffix=filename_suffix)
            req.wait()         
    
    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False

        return input_ready

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.        
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy
        
        opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
        thresholdingOutput = opThresholding.CachedOutput
        thresholding_ready = input_ready and \
                       len(thresholdingOutput) > 0 

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        features_ready = thresholding_ready

        opTracking = self.trackingApplet.topLevelOperator
        tracking_ready = features_ready and \
                           len(opTracking.Labels) > 0 and \
                           opTracking.Labels.ready() and \
                           opTracking.TrackImage.ready() 
        
        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.dataExportApplet.busy    
        busy |= self.trackingApplet.busy    
        self._shell.enableProjectChanges( not busy )
        
        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet, input_ready and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet, thresholding_ready and not busy)        
        self._shell.setAppletEnabled(self.trackingApplet, features_ready and not busy)
        self._shell.setAppletEnabled(self.dataExportApplet, tracking_ready and not busy and \
                                        self.dataExportApplet.topLevelOperator.Inputs[0][0].ready() )
    def __init__(self, shell, headless, workflow_cmdline_args, *args, **kwargs):
        graph = kwargs["graph"] if "graph" in kwargs else Graph()
        if "graph" in kwargs:
            del kwargs["graph"]
        # if 'withOptTrans' in kwargs:
        #     self.withOptTrans = kwargs['withOptTrans']
        # if 'fromBinary' in kwargs:
        #     self.fromBinary = kwargs['fromBinary']
        super(ConservationTrackingWorkflowBase, self).__init__(shell, headless, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        ## Create applets
        self.dataSelectionApplet = DataSelectionApplet(
            self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            force5d=True,
            instructionText=data_instructions,
            max_lanes=1,
        )

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue(["Raw Data", "Binary Image"])
        else:
            opDataSelection.DatasetRoles.setValue(["Raw Data", "Prediction Maps"])

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet(
                self, "Threshold and Size Filter", "ThresholdTwoLevels"
            )
        if self.withOptTrans:
            self.opticalTranslationApplet = OpticalTranslationApplet(workflow=self)

        self.objectExtractionApplet = TrackingFeatureExtractionApplet(
            workflow=self, interactive=False, name="Object Feature Computation"
        )

        self.divisionDetectionApplet = ObjectClassificationApplet(
            workflow=self, name="Division Detection (optional)", projectFileGroupName="DivisionDetection"
        )

        self.cellClassificationApplet = ObjectClassificationApplet(
            workflow=self, name="Object Count Classification (optional)", projectFileGroupName="CountClassification"
        )

        self.trackingApplet = ConservationTrackingApplet(workflow=self)
        opTracking = self.trackingApplet.topLevelOperator

        self.dataExportApplet = TrackingBaseDataExportApplet(self, "Tracking Result Export")
        self.dataExportApplet.set_exporting_operator(opTracking)

        opDataExport = self.dataExportApplet.topLevelOperator
        opDataExport.SelectionNames.setValue(["Tracking Result", "Merger Result", "Object Identities"])
        opDataExport.WorkingDirectory.connect(opDataSelection.WorkingDirectory)

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        if self.withOptTrans:
            self._applets.append(self.opticalTranslationApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)
        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportApplet)
Beispiel #32
0
    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']

        super(StructuredTrackingWorkflowBase, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Create applets
        self.dataSelectionApplet = DataSelectionApplet(self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            forceAxisOrder=['txyzc'],
            instructionText=data_instructions,
            max_lanes=1)

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Binary Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self,"Threshold and Size Filter","ThresholdTwoLevels" )

        self.divisionDetectionApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Division Detection (optional)",
                                                                     projectFileGroupName="DivisionDetection",
                                                                     selectedFeatures=configConservation.selectedFeaturesDiv)

        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification",
                                                                     projectFileGroupName="CountClassification",
                                                                     selectedFeatures=configConservation.selectedFeaturesObjectCount)

        self.trackingFeatureExtractionApplet = TrackingFeatureExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.annotationsApplet = AnnotationsApplet( name="Training", workflow=self )
        opAnnotations = self.annotationsApplet.topLevelOperator

        self.trackingApplet = StructuredTrackingApplet( name="Tracking - Structured Learning", workflow=self )
        opStructuredTracking = self.trackingApplet.topLevelOperator

        if SOLVER=="CPLEX" or SOLVER=="GUROBI":
            self._solver="ILP"
        elif SOLVER=="DPCT":
            self._solver="Flow-based"
        else:
            self._solver=None
        opStructuredTracking._solver = self._solver

        self.default_tracking_export_filename = '{dataset_dir}/{nickname}-tracking_exported_data.csv'
        self.dataExportTrackingApplet = TrackingBaseDataExportApplet(self, "Tracking Result Export",default_export_filename=self.default_tracking_export_filename)
        opDataExportTracking = self.dataExportTrackingApplet.topLevelOperator
        opDataExportTracking.SelectionNames.setValue( ['Tracking-Result', 'Merger-Result', 'Object-Identities'] )
        opDataExportTracking.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        self.dataExportTrackingApplet.set_exporting_operator(opStructuredTracking)
        self.dataExportTrackingApplet.prepare_lane_for_export = self.prepare_lane_for_export
        self.dataExportTrackingApplet.post_process_lane_export = self.post_process_lane_export

        # configure export settings
        settings = {'file path': self.default_tracking_export_filename, 'compression': {}, 'file type': 'h5'}
        selected_features = ['Count', 'RegionCenter', 'RegionRadii', 'RegionAxes']
        opStructuredTracking.ExportSettings.setValue( (settings, selected_features) )

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.trackingFeatureExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)

        self.batchProcessingApplet = BatchProcessingApplet(self, "Batch Processing", self.dataSelectionApplet, self.dataExportTrackingApplet)

        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.annotationsApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportTrackingApplet)

        if self.divisionDetectionApplet:
            opDivDetection = self.divisionDetectionApplet.topLevelOperator
            opDivDetection.SelectedFeatures.setValue(configConservation.selectedFeaturesDiv)
            opDivDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])
            opDivDetection.AllowDeleteLabels.setValue(False)
            opDivDetection.AllowAddLabel.setValue(False)
            opDivDetection.EnableLabelTransfer.setValue(False)

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(configConservation.selectedFeaturesObjectCount )
        opCellClassification.SuggestedLabelNames.setValue( ['False Detection',] + [str(1) + ' Object'] + [str(i) + ' Objects' for i in range(2,10) ] )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

        if workflow_cmdline_args:

            if '--testFullAnnotations' in workflow_cmdline_args:
                self.testFullAnnotations = True
            else:
                self.testFullAnnotations = False

            self._data_export_args, unused_args = self.dataExportTrackingApplet.parse_known_cmdline_args( workflow_cmdline_args )
            self._batch_input_args, unused_args = self.batchProcessingApplet.parse_known_cmdline_args( workflow_cmdline_args )
        else:
            unused_args = None
            self._data_export_args = None
            self._batch_input_args = None
            self.testFullAnnotations = False

        if unused_args:
            logger.warning("Unused command-line args: {}".format( unused_args ))
Beispiel #33
0
class StructuredTrackingWorkflowBase( Workflow ):
    workflowName = "Structured Learning Tracking Workflow BASE"

    @property
    def applets(self):
        return self._applets

    @property
    def imageNameListSlot(self):
        return self.dataSelectionApplet.topLevelOperator.ImageName

    def __init__( self, shell, headless, workflow_cmdline_args, project_creation_args, *args, **kwargs ):
        graph = kwargs['graph'] if 'graph' in kwargs else Graph()
        if 'graph' in kwargs: del kwargs['graph']

        super(StructuredTrackingWorkflowBase, self).__init__(shell, headless, workflow_cmdline_args, project_creation_args, graph=graph, *args, **kwargs)

        data_instructions = 'Use the "Raw Data" tab to load your intensity image(s).\n\n'
        if self.fromBinary:
            data_instructions += 'Use the "Binary Image" tab to load your segmentation image(s).'
        else:
            data_instructions += 'Use the "Prediction Maps" tab to load your pixel-wise probability image(s).'

        # Create applets
        self.dataSelectionApplet = DataSelectionApplet(self,
            "Input Data",
            "Input Data",
            batchDataGui=False,
            forceAxisOrder=['txyzc'],
            instructionText=data_instructions,
            max_lanes=1)

        opDataSelection = self.dataSelectionApplet.topLevelOperator
        if self.fromBinary:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Binary Image'] )
        else:
            opDataSelection.DatasetRoles.setValue( ['Raw Data', 'Prediction Maps'] )

        if not self.fromBinary:
            self.thresholdTwoLevelsApplet = ThresholdTwoLevelsApplet( self,"Threshold and Size Filter","ThresholdTwoLevels" )

        self.divisionDetectionApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Division Detection (optional)",
                                                                     projectFileGroupName="DivisionDetection",
                                                                     selectedFeatures=configStructured.selectedFeaturesDiv)

        self.cellClassificationApplet = ObjectClassificationApplet(workflow=self,
                                                                     name="Object Count Classification",
                                                                     projectFileGroupName="CountClassification",
                                                                     selectedFeatures=configStructured.selectedFeaturesObjectCount)

        self.cropSelectionApplet = CropSelectionApplet(self,"Crop Selection","CropSelection")

        self.trackingFeatureExtractionApplet = TrackingFeatureExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.objectExtractionApplet = ObjectExtractionApplet(name="Object Feature Computation",workflow=self, interactive=False)

        self.annotationsApplet = AnnotationsApplet( name="Training", workflow=self )
        opAnnotations = self.annotationsApplet.topLevelOperator

        # self.default_training_export_filename = '{dataset_dir}/{nickname}-training_exported_data.csv'
        # self.dataExportAnnotationsApplet = TrackingBaseDataExportApplet(self, "Training Export",default_export_filename=self.default_training_export_filename)
        # opDataExportAnnotations = self.dataExportAnnotationsApplet.topLevelOperator
        # opDataExportAnnotations.SelectionNames.setValue( ['User Training for Tracking', 'Object Identities'] )
        # opDataExportAnnotations.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        # self.dataExportAnnotationsApplet.set_exporting_operator(opAnnotations)

        self.trackingApplet = StructuredTrackingApplet( name="Tracking - Structured Learning", workflow=self )
        opStructuredTracking = self.trackingApplet.topLevelOperator

        self.default_tracking_export_filename = '{dataset_dir}/{nickname}-tracking_exported_data.csv'
        self.dataExportTrackingApplet = TrackingBaseDataExportApplet(self, "Tracking Result Export",default_export_filename=self.default_tracking_export_filename)
        opDataExportTracking = self.dataExportTrackingApplet.topLevelOperator
        opDataExportTracking.SelectionNames.setValue( ['Tracking-Result', 'Merger-Result', 'Object-Identities'] )
        opDataExportTracking.WorkingDirectory.connect( opDataSelection.WorkingDirectory )
        self.dataExportTrackingApplet.set_exporting_operator(opStructuredTracking)
        self.dataExportTrackingApplet.prepare_lane_for_export = self.prepare_lane_for_export
        self.dataExportTrackingApplet.post_process_lane_export = self.post_process_lane_export

        # configure export settings
        settings = {'file path': self.default_tracking_export_filename, 'compression': {}, 'file type': 'h5'}
        selected_features = ['Count', 'RegionCenter', 'RegionRadii', 'RegionAxes']
        opStructuredTracking.ExportSettings.setValue( (settings, selected_features) )

        self._applets = []
        self._applets.append(self.dataSelectionApplet)
        if not self.fromBinary:
            self._applets.append(self.thresholdTwoLevelsApplet)
        self._applets.append(self.trackingFeatureExtractionApplet)
        self._applets.append(self.divisionDetectionApplet)

        self.batchProcessingApplet = BatchProcessingApplet(self, "Batch Processing", self.dataSelectionApplet, self.dataExportTrackingApplet)

        self._applets.append(self.cellClassificationApplet)
        self._applets.append(self.cropSelectionApplet)
        self._applets.append(self.objectExtractionApplet)
        self._applets.append(self.annotationsApplet)
        # self._applets.append(self.dataExportAnnotationsApplet)
        self._applets.append(self.trackingApplet)
        self._applets.append(self.dataExportTrackingApplet)

        if self.divisionDetectionApplet:
            opDivDetection = self.divisionDetectionApplet.topLevelOperator
            opDivDetection.SelectedFeatures.setValue(configConservation.selectedFeaturesDiv)
            opDivDetection.LabelNames.setValue(['Not Dividing', 'Dividing'])
            opDivDetection.AllowDeleteLabels.setValue(False)
            opDivDetection.AllowAddLabel.setValue(False)
            opDivDetection.EnableLabelTransfer.setValue(False)

        opCellClassification = self.cellClassificationApplet.topLevelOperator
        opCellClassification.SelectedFeatures.setValue(configConservation.selectedFeaturesObjectCount )
        opCellClassification.SuggestedLabelNames.setValue( ['False Detection',] + [str(1) + ' Object'] + [str(i) + ' Objects' for i in range(2,10) ] )
        opCellClassification.AllowDeleteLastLabelOnly.setValue(True)
        opCellClassification.EnableLabelTransfer.setValue(False)

        if workflow_cmdline_args:
            self._data_export_args, unused_args = self.dataExportTrackingApplet.parse_known_cmdline_args( workflow_cmdline_args )
            self._batch_input_args, unused_args = self.batchProcessingApplet.parse_known_cmdline_args( workflow_cmdline_args )
        else:
            unused_args = None
            self._data_export_args = None
            self._batch_input_args = None

        if unused_args:
            logger.warn("Unused command-line args: {}".format( unused_args ))

    def connectLane(self, laneIndex):
        opData = self.dataSelectionApplet.topLevelOperator.getLane(laneIndex)
        opObjExtraction = self.objectExtractionApplet.topLevelOperator.getLane(laneIndex)
        opTrackingFeatureExtraction = self.trackingFeatureExtractionApplet.topLevelOperator.getLane(laneIndex)

        opAnnotations = self.annotationsApplet.topLevelOperator.getLane(laneIndex)
        if not self.fromBinary:
            opTwoLevelThreshold = self.thresholdTwoLevelsApplet.topLevelOperator.getLane(laneIndex)
        # opDataAnnotationsExport = self.dataExportAnnotationsApplet.topLevelOperator.getLane(laneIndex)

        opCropSelection = self.cropSelectionApplet.topLevelOperator.getLane(laneIndex)
        opStructuredTracking = self.trackingApplet.topLevelOperator.getLane(laneIndex)
        opDataTrackingExport = self.dataExportTrackingApplet.topLevelOperator.getLane(laneIndex)

        ## Connect operators ##
        op5Raw = OpReorderAxes(parent=self)
        op5Raw.AxisOrder.setValue("txyzc")
        op5Raw.Input.connect(opData.ImageGroup[0])

        opDivDetection = self.divisionDetectionApplet.topLevelOperator.getLane(laneIndex)
        opCellClassification = self.cellClassificationApplet.topLevelOperator.getLane(laneIndex)

        if not self.fromBinary:
            opTwoLevelThreshold.InputImage.connect( opData.ImageGroup[1] )
            opTwoLevelThreshold.RawInput.connect( opData.ImageGroup[0] ) # Used for display only
            binarySrc = opTwoLevelThreshold.CachedOutput
        else:
            binarySrc = opData.ImageGroup[1]
        # Use Op5ifyers for both input datasets such that they are guaranteed to
        # have the same axis order after thresholding
        op5Binary = OpReorderAxes(parent=self)
        op5Binary.AxisOrder.setValue("txyzc")
        op5Binary.Input.connect(binarySrc)

        opCropSelection.InputImage.connect( opData.ImageGroup[0] )
        opCropSelection.PredictionImage.connect( opData.ImageGroup[1] )

        opObjExtraction.RawImage.connect( op5Raw.Output )
        opObjExtraction.BinaryImage.connect( op5Binary.Output )

        opTrackingFeatureExtraction.RawImage.connect( op5Raw.Output )
        opTrackingFeatureExtraction.BinaryImage.connect( op5Binary.Output )

        # vigra_features = list((set(config.vigra_features)).union(config.selected_features_objectcount[config.features_vigra_name]))
        # feature_names_vigra = {}
        # feature_names_vigra[config.features_vigra_name] = { name: {} for name in vigra_features }

        opTrackingFeatureExtraction.FeatureNamesVigra.setValue(configConservation.allFeaturesObjectCount)
        feature_dict_division = {}
        feature_dict_division[config.features_division_name] = { name: {} for name in config.division_features }
        opTrackingFeatureExtraction.FeatureNamesDivision.setValue(feature_dict_division)

        if self.divisionDetectionApplet:
            opDivDetection.BinaryImages.connect( op5Binary.Output )
            opDivDetection.RawImages.connect( op5Raw.Output )
            opDivDetection.SegmentationImages.connect(opTrackingFeatureExtraction.LabelImage)
            opDivDetection.ObjectFeatures.connect(opTrackingFeatureExtraction.RegionFeaturesAll)
            opDivDetection.ComputedFeatureNames.connect(opTrackingFeatureExtraction.ComputedFeatureNamesAll)

        opCellClassification.BinaryImages.connect( op5Binary.Output )
        opCellClassification.RawImages.connect( op5Raw.Output )
        opCellClassification.SegmentationImages.connect(opTrackingFeatureExtraction.LabelImage)
        opCellClassification.ObjectFeatures.connect(opTrackingFeatureExtraction.RegionFeaturesAll)
        opCellClassification.ComputedFeatureNames.connect(opTrackingFeatureExtraction.ComputedFeatureNamesNoDivisions)

        opAnnotations.RawImage.connect( op5Raw.Output )
        opAnnotations.BinaryImage.connect( op5Binary.Output )
        opAnnotations.LabelImage.connect( opObjExtraction.LabelImage )
        opAnnotations.ObjectFeatures.connect( opObjExtraction.RegionFeatures )
        opAnnotations.ComputedFeatureNames.connect(opObjExtraction.Features)
        opAnnotations.Crops.connect( opCropSelection.Crops)
        opAnnotations.DivisionProbabilities.connect( opDivDetection.Probabilities )
        opAnnotations.DetectionProbabilities.connect( opCellClassification.Probabilities )
        opAnnotations.MaxNumObj.connect (opCellClassification.MaxNumObj)

        # opDataAnnotationsExport.Inputs.resize(2)
        # opDataAnnotationsExport.Inputs[0].connect( opAnnotations.TrackImage )
        # opDataAnnotationsExport.Inputs[1].connect( opAnnotations.LabelImage )
        # opDataAnnotationsExport.RawData.connect( op5Raw.Output )
        # opDataAnnotationsExport.RawDatasetInfo.connect( opData.DatasetGroup[0] )

        opStructuredTracking.RawImage.connect( op5Raw.Output )
        opStructuredTracking.LabelImage.connect( opTrackingFeatureExtraction.LabelImage )
        opStructuredTracking.ObjectFeatures.connect( opTrackingFeatureExtraction.RegionFeaturesVigra )
        opStructuredTracking.ComputedFeatureNames.connect( opTrackingFeatureExtraction.FeatureNamesVigra )

        if self.divisionDetectionApplet:
            opStructuredTracking.ObjectFeaturesWithDivFeatures.connect( opTrackingFeatureExtraction.RegionFeaturesAll)
            opStructuredTracking.ComputedFeatureNamesWithDivFeatures.connect( opTrackingFeatureExtraction.ComputedFeatureNamesAll )
            opStructuredTracking.DivisionProbabilities.connect( opDivDetection.Probabilities )

        # configure tracking export settings
        settings = {'file path': self.default_tracking_export_filename, 'compression': {}, 'file type': 'csv'}
        selected_features = ['Count', 'RegionCenter']
        opStructuredTracking.configure_table_export_settings(settings, selected_features)

        opStructuredTracking.DetectionProbabilities.connect( opCellClassification.Probabilities )
        opStructuredTracking.NumLabels.connect( opCellClassification.NumLabels )
        opStructuredTracking.Crops.connect (opCropSelection.Crops)
        opStructuredTracking.Annotations.connect (opAnnotations.Annotations)
        opStructuredTracking.Labels.connect (opAnnotations.Labels)
        opStructuredTracking.Divisions.connect (opAnnotations.Divisions)
        opStructuredTracking.MaxNumObj.connect (opCellClassification.MaxNumObj)

        opDataTrackingExport.Inputs.resize(3)
        opDataTrackingExport.Inputs[0].connect( opStructuredTracking.RelabeledImage )
        opDataTrackingExport.Inputs[1].connect( opStructuredTracking.MergerOutput )
        opDataTrackingExport.Inputs[2].connect( opStructuredTracking.LabelImage )
        opDataTrackingExport.RawData.connect( op5Raw.Output )
        opDataTrackingExport.RawDatasetInfo.connect( opData.DatasetGroup[0] )

    def prepare_lane_for_export(self, lane_index):
        import logging
        logger = logging.getLogger(__name__)

        maxt = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[0]
        maxx = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[1]
        maxy = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[2]
        maxz = self.trackingApplet.topLevelOperator[lane_index].RawImage.meta.shape[3]
        time_enum = range(maxt)
        x_range = (0, maxx)
        y_range = (0, maxy)
        z_range = (0, maxz)

        ndim = 2
        if ( z_range[1] - z_range[0] ) > 1:
            ndim = 3

        parameters = self.trackingApplet.topLevelOperator.Parameters.value
        # Save state of axis ranges
        if 'time_range' in parameters:
            self.prev_time_range = parameters['time_range']
        else:
            self.prev_time_range = time_enum

        if 'x_range' in parameters:
            self.prev_x_range = parameters['x_range']
        else:
            self.prev_x_range = x_range

        if 'y_range' in parameters:
            self.prev_y_range = parameters['y_range']
        else:
            self.prev_y_range = y_range

        if 'z_range' in parameters:
            self.prev_z_range = parameters['z_range']
        else:
            self.prev_z_range = z_range

        # batch processing starts a new lane, so training data needs to be copied from the lane that loaded the project
        loaded_project_lane_index=0
        self.annotationsApplet.topLevelOperator[lane_index].Annotations.setValue(
            self.trackingApplet.topLevelOperator[loaded_project_lane_index].Annotations.value)

        self.cropSelectionApplet.topLevelOperator[lane_index].Crops.setValue(
            self.trackingApplet.topLevelOperator[loaded_project_lane_index].Crops.value)

        logger.info("Test: Structured Learning")
        weights = self.trackingApplet.topLevelOperator[lane_index]._runStructuredLearning(
            z_range,
            parameters['maxObj'],
            parameters['max_nearest_neighbors'],
            parameters['maxDist'],
            parameters['divThreshold'],
            [parameters['scales'][0],parameters['scales'][1],parameters['scales'][2]],
            parameters['size_range'],
            parameters['withDivisions'],
            parameters['borderAwareWidth'],
            parameters['withClassifierPrior'],
            withBatchProcessing=True)
        logger.info("weights: {}".format(weights))

        logger.info("Test: Tracking")
        self.trackingApplet.topLevelOperator[lane_index].track(
            time_range = time_enum,
            x_range = x_range,
            y_range = y_range,
            z_range = z_range,
            size_range = parameters['size_range'],
            x_scale = parameters['scales'][0],
            y_scale = parameters['scales'][1],
            z_scale = parameters['scales'][2],
            maxDist=parameters['maxDist'],
            maxObj = parameters['maxObj'],
            divThreshold=parameters['divThreshold'],
            avgSize=parameters['avgSize'],
            withTracklets=parameters['withTracklets'],
            sizeDependent=parameters['sizeDependent'],
            detWeight=parameters['detWeight'],
            divWeight=parameters['divWeight'],
            transWeight=parameters['transWeight'],
            withDivisions=parameters['withDivisions'],
            withOpticalCorrection=parameters['withOpticalCorrection'],
            withClassifierPrior=parameters['withClassifierPrior'],
            ndim=ndim,
            withMergerResolution=parameters['withMergerResolution'],
            borderAwareWidth = parameters['borderAwareWidth'],
            withArmaCoordinates = parameters['withArmaCoordinates'],
            cplex_timeout = parameters['cplex_timeout'],
            appearance_cost = parameters['appearanceCost'],
            disappearance_cost = parameters['disappearanceCost'],
            force_build_hypotheses_graph = False,
            withBatchProcessing = True
        )

    def post_process_lane_export(self, lane_index):
        # FIXME: This probably only works for the non-blockwise export slot.
        #        We should assert that the user isn't using the blockwise slot.
        settings, selected_features = self.trackingApplet.topLevelOperator.getLane(lane_index).get_table_export_settings()
        from lazyflow.utility import PathComponents, make_absolute, format_known_keys

        if settings:
            self.dataExportTrackingApplet.progressSignal.emit(-1)
            raw_dataset_info = self.dataSelectionApplet.topLevelOperator.DatasetGroup[lane_index][0].value

            project_path = self.shell.projectManager.currentProjectPath
            project_dir = os.path.dirname(project_path)
            dataset_dir = PathComponents(raw_dataset_info.filePath).externalDirectory
            abs_dataset_dir = make_absolute(dataset_dir, cwd=project_dir)

            known_keys = {}
            known_keys['dataset_dir'] = abs_dataset_dir
            nickname = raw_dataset_info.nickname.replace('*', '')
            if os.path.pathsep in nickname:
                nickname = PathComponents(nickname.split(os.path.pathsep)[0]).fileNameBase
            known_keys['nickname'] = nickname

            # use partial formatting to fill in non-coordinate name fields
            name_format = settings['file path']
            partially_formatted_name = format_known_keys( name_format, known_keys )
            settings['file path'] = partially_formatted_name

            req = self.trackingApplet.topLevelOperator.getLane(lane_index).export_object_data(
                        lane_index,
                        # FIXME: Even in non-headless mode, we can't show the gui because we're running in a non-main thread.
                        #        That's not a huge deal, because there's still a progress bar for the overall export.
                        show_gui=False)

            req.wait()
            self.dataExportTrackingApplet.progressSignal.emit(100)

    def _inputReady(self, nRoles):
        slot = self.dataSelectionApplet.topLevelOperator.ImageGroup
        if len(slot) > 0:
            input_ready = True
            for sub in slot:
                input_ready = input_ready and \
                    all([sub[i].ready() for i in range(nRoles)])
        else:
            input_ready = False
        return input_ready

    def onProjectLoaded(self, projectManager):
        """
        Overridden from Workflow base class.  Called by the Project Manager.

        If the user provided command-line arguments, use them to configure
        the workflow inputs and output settings.
        """

        # Configure the data export operator.
        if self._data_export_args:
            self.dataExportTrackingApplet.configure_operator_with_parsed_args( self._data_export_args )

        # Configure headless mode.
        if self._headless and self._batch_input_args and self._data_export_args:
            logger.info("Beginning Batch Processing")
            self.batchProcessingApplet.run_export_from_parsed_args(self._batch_input_args)
            logger.info("Completed Batch Processing")

    def handleAppletStateUpdateRequested(self):
        """
        Overridden from Workflow base class
        Called when an applet has fired the :py:attr:`Applet.statusUpdateSignal`
        """
        # If no data, nothing else is ready.
        input_ready = self._inputReady(2) and not self.dataSelectionApplet.busy

        if not self.fromBinary:
            opThresholding = self.thresholdTwoLevelsApplet.topLevelOperator
            thresholdingOutput = opThresholding.CachedOutput
            thresholding_ready = input_ready and len(thresholdingOutput) > 0
        else:
            thresholding_ready = input_ready

        opTrackingFeatureExtraction = self.trackingFeatureExtractionApplet.topLevelOperator
        trackingFeatureExtractionOutput = opTrackingFeatureExtraction.ComputedFeatureNamesAll
        tracking_features_ready = thresholding_ready and len(trackingFeatureExtractionOutput) > 0

        opCropSelection = self.cropSelectionApplet.topLevelOperator
        croppingOutput = opCropSelection.Crops
        cropping_ready = thresholding_ready and len(croppingOutput) > 0

        objectCountClassifier_ready = tracking_features_ready

        opObjectExtraction = self.objectExtractionApplet.topLevelOperator
        objectExtractionOutput = opObjectExtraction.RegionFeatures
        features_ready = thresholding_ready and \
                         len(objectExtractionOutput) > 0

        opAnnotations = self.annotationsApplet.topLevelOperator
        annotations_ready = features_ready and \
                           len(opAnnotations.Labels) > 0 and \
                           opAnnotations.Labels.ready() and \
                           opAnnotations.TrackImage.ready()

        opStructuredTracking = self.trackingApplet.topLevelOperator
        structured_tracking_ready = objectCountClassifier_ready and \
                           len(opStructuredTracking.EventsVector) > 0
        busy = False
        busy |= self.dataSelectionApplet.busy
        busy |= self.annotationsApplet.busy
        # busy |= self.dataExportAnnotationsApplet.busy
        busy |= self.trackingApplet.busy
        busy |= self.dataExportTrackingApplet.busy

        self._shell.enableProjectChanges( not busy )

        self._shell.setAppletEnabled(self.dataSelectionApplet, not busy)
        if not self.fromBinary:
            self._shell.setAppletEnabled(self.thresholdTwoLevelsApplet, input_ready and not busy)
        self._shell.setAppletEnabled(self.trackingFeatureExtractionApplet, thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.cellClassificationApplet, tracking_features_ready and not busy)
        self._shell.setAppletEnabled(self.divisionDetectionApplet, tracking_features_ready and not busy)
        self._shell.setAppletEnabled(self.cropSelectionApplet, thresholding_ready and not busy)
        self._shell.setAppletEnabled(self.objectExtractionApplet, not busy)
        self._shell.setAppletEnabled(self.annotationsApplet, features_ready and not busy)
        # self._shell.setAppletEnabled(self.dataExportAnnotationsApplet, annotations_ready and not busy and \
        #                                 self.dataExportAnnotationsApplet.topLevelOperator.Inputs[0][0].ready() )
        self._shell.setAppletEnabled(self.trackingApplet, objectCountClassifier_ready and not busy)
        self._shell.setAppletEnabled(self.dataExportTrackingApplet, structured_tracking_ready and not busy and \
                                    self.dataExportTrackingApplet.topLevelOperator.Inputs[0][0].ready() )