def testBasic(self):
        graph = Graph()
        op1 = OpMultiPassthrough(graph=graph)
    
        opTranspose = OpTransposeSlots(graph=graph)
        opTranspose.Inputs.connect( op1.Outputs )
        opTranspose.OutputLength.setValue( 2 )
        assert len( opTranspose.Outputs ) == 2
    
        op1.Inputs.resize( 3 )
        assert len( op1.Outputs ) == 3
        assert len( opTranspose.Outputs[0] ) == 3
    
        op1.Inputs[0].resize(2)
        op1.Inputs[0][0].setValue( (0,0) )
        
        # Sanity check...
        assert op1.Outputs[0][0].ready()
        assert op1.Outputs[0][0].value == (0,0)

        assert opTranspose.Outputs[0][0].ready()
        assert opTranspose.Outputs[0][0].value == (0,0)
        
        op1.Inputs[0][1].setValue( (0,1) )

        # Sanity check...
        assert op1.Outputs[0][1].ready()
        assert op1.Outputs[0][1].value == (0,1)

        assert opTranspose.Outputs[1][0].ready()
        assert opTranspose.Outputs[1][0].value == (0,1)
    
        op1.Inputs[2].resize(2)
        op1.Inputs[2][0].setValue( (2,0) )

        # Sanity check...
        assert op1.Outputs[2][0].ready()
        assert op1.Outputs[2][0].value == (2,0)

        assert opTranspose.Outputs[0][2].ready()
        assert opTranspose.Outputs[0][2].value == (2,0)
        
        op1.Inputs[2][1].setValue( (2,1) )

        # Sanity check...
        assert op1.Outputs[2][1].ready()
        assert op1.Outputs[2][1].value == (2,1)

        assert opTranspose.Outputs[1][2].ready()
        assert opTranspose.Outputs[1][2].value == (2,1)

        # The middle input multi-slot was never configured.
        # Therefore, the middle slot of each multi-output is not ready.    
        assert not opTranspose.Outputs[0,1].ready()
        assert not opTranspose.Outputs[1,1].ready()
    def _initBatchWorkflow(self):
        """
        Connect the batch-mode top-level operators to the training workflow and to each other.
        """
        # Access applet operators from the training workflow
        opTrainingDataSelection = self.dataSelectionApplet.topLevelOperator
        opTrainingFeatures = self.featureSelectionApplet.topLevelOperator
        opClassify = self.pcApplet.topLevelOperator
        
        # Access the batch operators
        opBatchInputs = self.batchInputApplet.topLevelOperator
        opBatchResults = self.batchResultsApplet.topLevelOperator
        
        opBatchInputs.DatasetRoles.connect( opTrainingDataSelection.DatasetRoles )
        
        opSelectFirstLane = OperatorWrapper( OpSelectSubslot, parent=self )
        opSelectFirstLane.Inputs.connect( opTrainingDataSelection.ImageGroup )
        opSelectFirstLane.SubslotIndex.setValue(0)
        
        opSelectFirstRole = OpSelectSubslot( parent=self )
        opSelectFirstRole.Inputs.connect( opSelectFirstLane.Output )
        opSelectFirstRole.SubslotIndex.setValue(self.DATA_ROLE_RAW)
        
        opBatchResults.ConstraintDataset.connect( opSelectFirstRole.Output )
        
        ## Create additional batch workflow operators
        opBatchFeatures = OperatorWrapper( OpFeatureSelectionNoCache, operator_kwargs={'filter_implementation': self.filter_implementation}, parent=self, promotedSlotNames=['InputImage'] )
        opBatchPredictionPipeline = OperatorWrapper( OpPredictionPipelineNoCache, parent=self )
        
        ## Connect Operators ##
        opTranspose = OpTransposeSlots( parent=self )
        opTranspose.OutputLength.setValue(2) # There are 2 roles
        opTranspose.Inputs.connect( opBatchInputs.DatasetGroup )
        opTranspose.name = "batchTransposeInputs"
        
        # Provide dataset paths from data selection applet to the batch export applet
        opBatchResults.RawDatasetInfo.connect( opTranspose.Outputs[self.DATA_ROLE_RAW] )
        opBatchResults.WorkingDirectory.connect( opBatchInputs.WorkingDirectory )
        
        # Connect (clone) the feature operator inputs from 
        #  the interactive workflow's features operator (which gets them from the GUI)
        opBatchFeatures.Scales.connect( opTrainingFeatures.Scales )
        opBatchFeatures.FeatureIds.connect( opTrainingFeatures.FeatureIds )
        opBatchFeatures.SelectionMatrix.connect( opTrainingFeatures.SelectionMatrix )
        
        # Classifier and NumClasses are provided by the interactive workflow
        opBatchPredictionPipeline.Classifier.connect( opClassify.Classifier )
        opBatchPredictionPipeline.NumClasses.connect( opClassify.NumClasses )
        
        # Provide these for the gui
        opBatchResults.RawData.connect( opBatchInputs.Image )
        opBatchResults.PmapColors.connect( opClassify.PmapColors )
        opBatchResults.LabelNames.connect( opClassify.LabelNames )
        
        # Connect Image pathway:
        # Input Image -> Features Op -> Prediction Op -> Export
        opBatchFeatures.InputImage.connect( opBatchInputs.Image )
        opBatchPredictionPipeline.PredictionMask.connect( opBatchInputs.Image1 )
        opBatchPredictionPipeline.FeatureImages.connect( opBatchFeatures.OutputImage )

        opBatchResults.SelectionNames.setValue( self.EXPORT_NAMES )        
        # opBatchResults.Inputs is indexed by [lane][selection],
        # Use OpTranspose to allow connection.
        opTransposeBatchInputs = OpTransposeSlots( parent=self )
        opTransposeBatchInputs.name = "opTransposeBatchInputs"
        opTransposeBatchInputs.OutputLength.setValue(0)
        opTransposeBatchInputs.Inputs.resize( len(self.EXPORT_NAMES) )
        opTransposeBatchInputs.Inputs[0].connect( opBatchPredictionPipeline.HeadlessPredictionProbabilities ) # selection 0
        opTransposeBatchInputs.Inputs[1].connect( opBatchPredictionPipeline.SimpleSegmentation ) # selection 1
        opTransposeBatchInputs.Inputs[2].connect( opBatchPredictionPipeline.HeadlessUncertaintyEstimate ) # selection 2
        opTransposeBatchInputs.Inputs[3].connect( opBatchPredictionPipeline.FeatureImages ) # selection 3
        for slot in opTransposeBatchInputs.Inputs:
            assert slot.partner is not None
        
        # Now opTransposeBatchInputs.Outputs is level-2 indexed by [lane][selection]
        opBatchResults.Inputs.connect( opTransposeBatchInputs.Outputs )

        # We don't actually need the cached path in the batch pipeline.
        # Just connect the uncached features here to satisfy the operator.
        #opBatchPredictionPipeline.CachedFeatureImages.connect( opBatchFeatures.OutputImage )

        self.opBatchFeatures = opBatchFeatures
        self.opBatchPredictionPipeline = opBatchPredictionPipeline
    def _initBatchWorkflow(self):
        """
        Connect the batch-mode top-level operators to the training workflow and to each other.
        """
        # Access applet operators from the training workflow
        opTrainingDataSelection = self.dataSelectionApplet.topLevelOperator
        opTrainingFeatures = self.featureSelectionApplet.topLevelOperator
        opClassify = self.pcApplet.topLevelOperator

        # Access the batch operators
        opBatchInputs = self.batchInputApplet.topLevelOperator
        opBatchResults = self.batchResultsApplet.topLevelOperator

        opBatchInputs.DatasetRoles.connect(
            opTrainingDataSelection.DatasetRoles)

        opSelectFirstLane = OperatorWrapper(OpSelectSubslot, parent=self)
        opSelectFirstLane.Inputs.connect(opTrainingDataSelection.ImageGroup)
        opSelectFirstLane.SubslotIndex.setValue(0)

        opSelectFirstRole = OpSelectSubslot(parent=self)
        opSelectFirstRole.Inputs.connect(opSelectFirstLane.Output)
        opSelectFirstRole.SubslotIndex.setValue(self.DATA_ROLE_RAW)

        opBatchResults.ConstraintDataset.connect(opSelectFirstRole.Output)

        ## Create additional batch workflow operators
        opBatchFeatures = OperatorWrapper(OpFeatureSelectionNoCache,
                                          operator_kwargs={
                                              'filter_implementation':
                                              self.filter_implementation
                                          },
                                          parent=self,
                                          promotedSlotNames=['InputImage'])
        opBatchPredictionPipeline = OperatorWrapper(
            OpPredictionPipelineNoCache, parent=self)

        ## Connect Operators ##
        opTranspose = OpTransposeSlots(parent=self)
        opTranspose.OutputLength.setValue(2)  # There are 2 roles
        opTranspose.Inputs.connect(opBatchInputs.DatasetGroup)
        opTranspose.name = "batchTransposeInputs"

        # Provide dataset paths from data selection applet to the batch export applet
        opBatchResults.RawDatasetInfo.connect(
            opTranspose.Outputs[self.DATA_ROLE_RAW])
        opBatchResults.WorkingDirectory.connect(opBatchInputs.WorkingDirectory)

        # Connect (clone) the feature operator inputs from
        #  the interactive workflow's features operator (which gets them from the GUI)
        opBatchFeatures.Scales.connect(opTrainingFeatures.Scales)
        opBatchFeatures.FeatureIds.connect(opTrainingFeatures.FeatureIds)
        opBatchFeatures.SelectionMatrix.connect(
            opTrainingFeatures.SelectionMatrix)

        # Classifier and NumClasses are provided by the interactive workflow
        opBatchPredictionPipeline.Classifier.connect(opClassify.Classifier)
        opBatchPredictionPipeline.NumClasses.connect(opClassify.NumClasses)

        # Provide these for the gui
        opBatchResults.RawData.connect(opBatchInputs.Image)
        opBatchResults.PmapColors.connect(opClassify.PmapColors)
        opBatchResults.LabelNames.connect(opClassify.LabelNames)

        # Connect Image pathway:
        # Input Image -> Features Op -> Prediction Op -> Export
        opBatchFeatures.InputImage.connect(opBatchInputs.Image)
        opBatchPredictionPipeline.PredictionMask.connect(opBatchInputs.Image1)
        opBatchPredictionPipeline.FeatureImages.connect(
            opBatchFeatures.OutputImage)

        opBatchResults.SelectionNames.setValue(self.EXPORT_NAMES)
        # opBatchResults.Inputs is indexed by [lane][selection],
        # Use OpTranspose to allow connection.
        opTransposeBatchInputs = OpTransposeSlots(parent=self)
        opTransposeBatchInputs.name = "opTransposeBatchInputs"
        opTransposeBatchInputs.OutputLength.setValue(0)
        opTransposeBatchInputs.Inputs.resize(len(self.EXPORT_NAMES))
        opTransposeBatchInputs.Inputs[0].connect(
            opBatchPredictionPipeline.HeadlessPredictionProbabilities
        )  # selection 0
        opTransposeBatchInputs.Inputs[1].connect(
            opBatchPredictionPipeline.SimpleSegmentation)  # selection 1
        opTransposeBatchInputs.Inputs[2].connect(
            opBatchPredictionPipeline.HeadlessUncertaintyEstimate
        )  # selection 2
        opTransposeBatchInputs.Inputs[3].connect(
            opBatchPredictionPipeline.FeatureImages)  # selection 3
        for slot in opTransposeBatchInputs.Inputs:
            assert slot.partner is not None

        # Now opTransposeBatchInputs.Outputs is level-2 indexed by [lane][selection]
        opBatchResults.Inputs.connect(opTransposeBatchInputs.Outputs)

        # We don't actually need the cached path in the batch pipeline.
        # Just connect the uncached features here to satisfy the operator.
        #opBatchPredictionPipeline.CachedFeatureImages.connect( opBatchFeatures.OutputImage )

        self.opBatchFeatures = opBatchFeatures
        self.opBatchPredictionPipeline = opBatchPredictionPipeline
    def _initBatchWorkflow(self):
        """
        Connect the batch-mode top-level operators to the training workflow and to eachother.
        """
        # Access applet operators from the training workflow
        opTrainingDataSelection = self.dataSelectionApplet.topLevelOperator
        opTrainingFeatures = self.featureSelectionApplet.topLevelOperator
        opClassify = self.countingApplet.topLevelOperator
                
        opSelectFirstLane = OperatorWrapper( OpSelectSubslot, parent=self )
        opSelectFirstLane.Inputs.connect( opTrainingDataSelection.ImageGroup )
        opSelectFirstLane.SubslotIndex.setValue(0)
        
        opSelectFirstRole = OpSelectSubslot( parent=self )
        opSelectFirstRole.Inputs.connect( opSelectFirstLane.Output )
        opSelectFirstRole.SubslotIndex.setValue(0)

        ## Create additional batch workflow operators
        opBatchFeatures = OperatorWrapper( OpFeatureSelection, operator_kwargs={'filter_implementation':'Original'}, parent=self, promotedSlotNames=['InputImage'] )
        opBatchPredictionPipeline = OperatorWrapper( OpPredictionPipeline, parent=self )
        
        # Create applets for batch workflow
        self.batchInputApplet = DataSelectionApplet(self, "Batch Prediction Input Selections", "BatchDataSelection", supportIlastik05Import=False, batchDataGui=True)
        self.batchResultsApplet = CountingDataExportApplet(self, "Batch Prediction Output Locations", opBatchPredictionPipeline, isBatch=True)

        # Expose in shell        
        self._applets.append(self.batchInputApplet)
        self._applets.append(self.batchResultsApplet)

        opBatchInputs = self.batchInputApplet.topLevelOperator
        opBatchResults = self.batchResultsApplet.topLevelOperator
        
        opBatchInputs.DatasetRoles.connect( opTrainingDataSelection.DatasetRoles )
        opBatchResults.ConstraintDataset.connect( opSelectFirstRole.Output )
        
        
        ## Connect Operators ##
        opTranspose = OpTransposeSlots( parent=self )
        opTranspose.OutputLength.setValue(1)
        opTranspose.Inputs.connect( opBatchInputs.DatasetGroup )
        
        # Provide dataset paths from data selection applet to the batch export applet
        opBatchResults.RawDatasetInfo.connect( opTranspose.Outputs[0] )
        opBatchResults.WorkingDirectory.connect( opBatchInputs.WorkingDirectory )
        
        # Connect (clone) the feature operator inputs from 
        #  the interactive workflow's features operator (which gets them from the GUI)
        opBatchFeatures.Scales.connect( opTrainingFeatures.Scales )
        opBatchFeatures.FeatureIds.connect( opTrainingFeatures.FeatureIds )
        opBatchFeatures.SelectionMatrix.connect( opTrainingFeatures.SelectionMatrix )
        
        # Classifier and LabelsCount are provided by the interactive workflow
        opBatchPredictionPipeline.Classifier.connect( opClassify.Classifier )
        opBatchPredictionPipeline.MaxLabel.connect( opClassify.MaxLabelValue )
        opBatchPredictionPipeline.FreezePredictions.setValue( False )
        
        # Provide these for the gui
        opBatchResults.RawData.connect( opBatchInputs.Image )
        opBatchResults.PmapColors.connect( opClassify.PmapColors )
        opBatchResults.LabelNames.connect( opClassify.LabelNames )
        opBatchResults.UpperBound.connect( opClassify.UpperBound )
        
        # Connect Image pathway:
        # Input Image -> Features Op -> Prediction Op -> Export
        opBatchFeatures.InputImage.connect( opBatchInputs.Image )
        opBatchPredictionPipeline.FeatureImages.connect( opBatchFeatures.OutputImage )
        
        opBatchResults.SelectionNames.setValue( ['Probabilities'] )        
        # opBatchResults.Inputs is indexed by [lane][selection],
        # Use OpTranspose to allow connection.
        opTransposeBatchInputs = OpTransposeSlots( parent=self )
        opTransposeBatchInputs.OutputLength.setValue(0)
        opTransposeBatchInputs.Inputs.resize(1)
        opTransposeBatchInputs.Inputs[0].connect( opBatchPredictionPipeline.HeadlessPredictionProbabilities ) # selection 0
        
        # Now opTransposeBatchInputs.Outputs is level-2 indexed by [lane][selection]
        opBatchResults.Inputs.connect( opTransposeBatchInputs.Outputs )

        # We don't actually need the cached path in the batch pipeline.
        # Just connect the uncached features here to satisfy the operator.
        opBatchPredictionPipeline.CachedFeatureImages.connect( opBatchFeatures.OutputImage )

        self.opBatchPredictionPipeline = opBatchPredictionPipeline
Exemple #5
0
    def _initBatchWorkflow(self):
        
        # If we are in pixel workflow, start from raw data
        # The part below is simply copied from the pixel classification
        
        # Access applet operators from the training workflow
        opPixelTrainingDataSelection = self.dataSelectionApplet.topLevelOperator
        opPixelTrainingFeatures = self.featureSelectionApplet.topLevelOperator
        opPixelClassify = self.pcApplet.topLevelOperator
        
        # Access the batch operators
        opBatchInputs = self.dataSelectionAppletBatch.topLevelOperator
        opBatchExport = self.batchExportApplet.topLevelOperator
        
        opBatchInputs.DatasetRoles.connect( opPixelTrainingDataSelection.DatasetRoles )
        
        opSelectFirstLane = OperatorWrapper( OpSelectSubslot, parent=self )
        opSelectFirstLane.Inputs.connect( opPixelTrainingDataSelection.ImageGroup )
        opSelectFirstLane.SubslotIndex.setValue(0)
        
        opSelectFirstRole = OpSelectSubslot( parent=self )
        opSelectFirstRole.Inputs.connect( opSelectFirstLane.Output )
        opSelectFirstRole.SubslotIndex.setValue(0)
        
        ## Create additional batch workflow operators
        # FIXME: this should take the same filter_implementation as the pixel operator!!!
        opBatchPixelFeatures = OperatorWrapper( OpFeatureSelection, operator_kwargs={'filter_implementation':'Original'}, parent=self, promotedSlotNames=['InputImage'] )
        opBatchPixelPredictionPipeline = OperatorWrapper( OpPredictionPipeline, parent=self )
        
        # Connect (clone) the feature operator inputs from 
        #  the interactive workflow's features operator (which gets them from the GUI)
        opBatchPixelFeatures.Scales.connect( opPixelTrainingFeatures.Scales )
        opBatchPixelFeatures.FeatureIds.connect( opPixelTrainingFeatures.FeatureIds )
        opBatchPixelFeatures.SelectionMatrix.connect( opPixelTrainingFeatures.SelectionMatrix )
        
        # Classifier and LabelsCount are provided by the interactive workflow
        opBatchPixelPredictionPipeline.Classifier.connect( opPixelClassify.Classifier )
        opBatchPixelPredictionPipeline.NumClasses.connect( opPixelClassify.NumClasses )
        opBatchPixelPredictionPipeline.FreezePredictions.setValue( False )
                
        # Connect Image pathway:
        # Input Image -> Features Op -> Prediction Op -> Thresholding
        opBatchPixelFeatures.InputImage.connect( opBatchInputs.Image )
        opBatchPixelPredictionPipeline.FeatureImages.connect( opBatchPixelFeatures.OutputImage )

        # We don't actually need the cached path in the batch pipeline.
        # Just connect the uncached features here to satisfy the operator.
        opBatchPixelPredictionPipeline.CachedFeatureImages.connect( opBatchPixelFeatures.OutputImage ) 
        
        # Now connect the object part
        opObjectTrainingTopLevel = self.objectClassificationApplet.topLevelOperator
        
        opBlockwiseObjectClassification = self.blockwiseObjectClassificationApplet.topLevelOperator

        op5Raw = OperatorWrapper(OpReorderAxes, parent=self)

        if self.fillMissing != 'none':
            opBatchFillMissingSlices = OperatorWrapper(OpFillMissingSlicesNoCache, parent=self)
            opBatchFillMissingSlices.Input.connect(opBatchInputs.Image)
            op5Raw.Input.connect(opBatchFillMissingSlices.Output)
        else:
            op5Raw.Input.connect(opBatchInputs.Image)
        
        opInteractiveThreshold = self.thresholdingApplet.topLevelOperator
        opBatchThreshold = OperatorWrapper(OpThresholdTwoLevels, parent=self)
        opBatchThreshold.MinSize.connect(opInteractiveThreshold.MinSize)
        opBatchThreshold.MaxSize.connect(opInteractiveThreshold.MaxSize)
        opBatchThreshold.HighThreshold.connect(opInteractiveThreshold.HighThreshold)
        opBatchThreshold.LowThreshold.connect(opInteractiveThreshold.LowThreshold)
        opBatchThreshold.SingleThreshold.connect(opInteractiveThreshold.SingleThreshold)
        opBatchThreshold.SmootherSigma.connect(opInteractiveThreshold.SmootherSigma)
        opBatchThreshold.Channel.connect(opInteractiveThreshold.Channel)
        opBatchThreshold.CurOperator.connect(opInteractiveThreshold.CurOperator)
        
        #  Image pathway is from the batch pipeline
        op5Pred = OperatorWrapper(OpReorderAxes, parent=self)
        op5Pred.Input.connect(opBatchPixelPredictionPipeline.HeadlessPredictionProbabilities)
        op5Binary = OperatorWrapper(OpReorderAxes, parent=self)
        opBatchThreshold.RawInput.connect(op5Raw.Output)
        opBatchThreshold.InputImage.connect(op5Pred.Output)
        op5Binary.Input.connect(opBatchThreshold.Output)
        
        # BATCH outputs are computed BLOCKWISE.
        # Connect the blockwise classification operator
        # Parameter inputs are cloned from the interactive workflow,
        opBatchObjectClassify = OperatorWrapper(OpBlockwiseObjectClassification, parent=self,
                                          promotedSlotNames=['RawImage', 'BinaryImage'])
        opBatchObjectClassify.Classifier.connect(opObjectTrainingTopLevel.Classifier)
        opBatchObjectClassify.LabelsCount.connect(opObjectTrainingTopLevel.NumLabels)
        opBatchObjectClassify.SelectedFeatures.connect(opObjectTrainingTopLevel.SelectedFeatures)
        opBatchObjectClassify.BlockShape3dDict.connect(opBlockwiseObjectClassification.BlockShape3dDict)
        opBatchObjectClassify.HaloPadding3dDict.connect(opBlockwiseObjectClassification.HaloPadding3dDict)        
        
        opBatchObjectClassify.RawImage.connect(op5Raw.Output)
        opBatchObjectClassify.BinaryImage.connect(op5Binary.Output)

        self.opBatchClassify = opBatchObjectClassify

        # We need to transpose the dataset group, because it is indexed by [image_index][group_index]
        # But we want it to be indexed by [group_index][image_index] for the RawDatasetInfo connection, below.
        opTransposeDatasetGroup = OpTransposeSlots( parent=self )
        opTransposeDatasetGroup.OutputLength.setValue(1)
        opTransposeDatasetGroup.Inputs.connect( opBatchInputs.DatasetGroup )

        # Connect the batch OUTPUT applet
        opBatchExport.RawData.connect( opBatchInputs.Image )
        opBatchExport.RawDatasetInfo.connect( opTransposeDatasetGroup.Outputs[0] )

        # See EXPORT_SELECTION_PREDICTIONS, EXPORT_SELECTION_PROBABILITIES, and EXPORT_SELECTION_PIXEL_PROBABILITIES, above
        opBatchExport.SelectionNames.setValue( ['Object Predictions', 'Object Probabilities', 'Pixel Probabilities'] )        
        # opBatchResults.Inputs is indexed by [lane][selection],
        # Use OpTranspose to allow connection.
        opTransposeBatchInputs = OpTransposeSlots( parent=self )
        opTransposeBatchInputs.OutputLength.setValue(0)
        opTransposeBatchInputs.Inputs.resize(3)
        opTransposeBatchInputs.Inputs[EXPORT_SELECTION_PREDICTIONS].connect( opBatchObjectClassify.PredictionImage ) # selection 0
        opTransposeBatchInputs.Inputs[EXPORT_SELECTION_PROBABILITIES].connect( opBatchObjectClassify.ProbabilityChannelImage ) # selection 1
        opTransposeBatchInputs.Inputs[EXPORT_SELECTION_PIXEL_PROBABILITIES].connect( opBatchThreshold.InputImage ) # selection 2 (must use op5'd version)
        
        # Now opTransposeBatchInputs.Outputs is level-2 indexed by [lane][selection]
        opBatchExport.Inputs.connect( opTransposeBatchInputs.Outputs )
Exemple #6
0
    def _initBatchWorkflow(self):
        # Access applet operators from the training workflow
        opObjectTrainingTopLevel = self.objectClassificationApplet.topLevelOperator
        opBlockwiseObjectClassification = self.blockwiseObjectClassificationApplet.topLevelOperator

        # If we are not in the binary workflow, connect the thresholding operator.
        # Parameter inputs are cloned from the interactive workflow,
        if isinstance(self, ObjectClassificationWorkflowBinary):
            #FIXME
            pass
        else:
            opInteractiveThreshold = self.thresholdingApplet.topLevelOperator
            opBatchThreshold = OperatorWrapper(OpThresholdTwoLevels, parent=self)
            opBatchThreshold.MinSize.connect(opInteractiveThreshold.MinSize)
            opBatchThreshold.MaxSize.connect(opInteractiveThreshold.MaxSize)
            opBatchThreshold.HighThreshold.connect(opInteractiveThreshold.HighThreshold)
            opBatchThreshold.LowThreshold.connect(opInteractiveThreshold.LowThreshold)
            opBatchThreshold.SingleThreshold.connect(opInteractiveThreshold.SingleThreshold)
            opBatchThreshold.SmootherSigma.connect(opInteractiveThreshold.SmootherSigma)
            opBatchThreshold.Channel.connect(opInteractiveThreshold.Channel)
            opBatchThreshold.CurOperator.connect(opInteractiveThreshold.CurOperator)

        # OpDataSelectionGroup.ImageGroup is indexed by [laneIndex][roleIndex],
        # but we need a slot that is indexed by [roleIndex][laneIndex]
        # so we can pass each role to the appropriate slots.
        # We use OpTransposeSlots to do this.
        opBatchInputByRole = OpTransposeSlots( parent=self )
        opBatchInputByRole.Inputs.connect( self.opDataSelectionBatch.ImageGroup )
        opBatchInputByRole.OutputLength.setValue(2)
        
        # Lane-indexed multislot for raw data
        batchInputsRaw = opBatchInputByRole.Outputs[0]
        # Lane-indexed multislot for binary/prediction-map data
        batchInputsOther = opBatchInputByRole.Outputs[1]

        # Connect the blockwise classification operator
        # Parameter inputs are cloned from the interactive workflow,
        opBatchClassify = OperatorWrapper(OpBlockwiseObjectClassification, parent=self,
                                          promotedSlotNames=['RawImage', 'BinaryImage'])
        opBatchClassify.Classifier.connect(opObjectTrainingTopLevel.Classifier)
        opBatchClassify.LabelsCount.connect(opObjectTrainingTopLevel.NumLabels)
        opBatchClassify.SelectedFeatures.connect(opObjectTrainingTopLevel.SelectedFeatures)
        opBatchClassify.BlockShape3dDict.connect(opBlockwiseObjectClassification.BlockShape3dDict)
        opBatchClassify.HaloPadding3dDict.connect(opBlockwiseObjectClassification.HaloPadding3dDict)

        #  but image pathway is from the batch pipeline
        op5Raw = OperatorWrapper(OpReorderAxes, parent=self)
        
        if self.fillMissing != 'none':
            opBatchFillMissingSlices = OperatorWrapper(OpFillMissingSlicesNoCache, parent=self)
            opBatchFillMissingSlices.Input.connect(batchInputsRaw)
            op5Raw.Input.connect(opBatchFillMissingSlices.Output)
        else:
            op5Raw.Input.connect(batchInputsRaw)
        
        
        op5Binary = OperatorWrapper(OpReorderAxes, parent=self)
        if self.input_types != 'raw+binary':
            op5Pred = OperatorWrapper(OpReorderAxes, parent=self)
            op5Pred.Input.connect(batchInputsOther)
            opBatchThreshold.RawInput.connect(op5Raw.Output)
            opBatchThreshold.InputImage.connect(op5Pred.Output)
            op5Binary.Input.connect(opBatchThreshold.Output)
        else:
            op5Binary.Input.connect(batchInputsOther)

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

        self.opBatchClassify = opBatchClassify

        # We need to transpose the dataset group, because it is indexed by [image_index][group_index]
        # But we want it to be indexed by [group_index][image_index] for the RawDatasetInfo connection, below.
        opTransposeDatasetGroup = OpTransposeSlots( parent=self )
        opTransposeDatasetGroup.OutputLength.setValue(1)
        opTransposeDatasetGroup.Inputs.connect( self.opDataSelectionBatch.DatasetGroup )

        # Connect the batch OUTPUT applet
        opBatchExport = self.batchExportApplet.topLevelOperator
        opBatchExport.RawData.connect( batchInputsRaw )
        opBatchExport.RawDatasetInfo.connect( opTransposeDatasetGroup.Outputs[0] )
        
        # See EXPORT_SELECTION_PREDICTIONS and EXPORT_SELECTION_PROBABILITIES, above
        opBatchExport.SelectionNames.setValue( ['Object Predictions', 'Object Probabilities'] )        
        # opBatchResults.Inputs is indexed by [lane][selection],
        # Use OpTranspose to allow connection.
        opTransposeBatchInputs = OpTransposeSlots( parent=self )
        opTransposeBatchInputs.OutputLength.setValue(0)
        opTransposeBatchInputs.Inputs.resize(2)
        opTransposeBatchInputs.Inputs[EXPORT_SELECTION_PREDICTIONS].connect( opBatchClassify.PredictionImage ) # selection 0
        opTransposeBatchInputs.Inputs[EXPORT_SELECTION_PROBABILITIES].connect( opBatchClassify.ProbabilityChannelImage ) # selection 1
        
        # Now opTransposeBatchInputs.Outputs is level-2 indexed by [lane][selection]
        opBatchExport.Inputs.connect( opTransposeBatchInputs.Outputs )