Beispiel #1
0
    def __init__(self, filter_implementation, *args, **kwargs):
        super(OpFeatureSelectionNoCache, self).__init__(*args, **kwargs)

        # Create the operator that actually generates the features
        if filter_implementation == 'Original':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Original(parent=self)
            logger.debug("Using ORIGINAL filters")
        elif filter_implementation == 'Refactored':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Refactored(parent=self)
        elif filter_implementation == 'Interpolated':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Interpolated(parent=self)
            self.opPixelFeatures.InterpolationScaleZ.setValue(2)
            logger.debug("Using INTERPOLATED filters")
        else:
            raise RuntimeError("Unknown filter implementation option: {}".format( filter_implementation ))

        # Connect our internal operators to our external inputs 
        self.opPixelFeatures.Scales.connect( self.Scales )
        self.opPixelFeatures.FeatureIds.connect( self.FeatureIds )
        self.opReorderIn = OpReorderAxes(parent=self)
        self.opReorderIn.Input.connect(self.InputImage)
        self.opPixelFeatures.Input.connect(self.opReorderIn.Output)
        self.opReorderOut = OpReorderAxes(parent=self)
        self.opReorderOut.Input.connect(self.opPixelFeatures.Output)
        self.opReorderLayers = OperatorWrapper(OpReorderAxes, parent=self,
                                               broadcastingSlotNames=["AxisOrder"])
        self.opReorderLayers.Input.connect(self.opPixelFeatures.Features)

        # We don't connect SelectionMatrix here because we want to 
        #  check it for errors (See setupOutputs)
        # self.opPixelFeatures.SelectionMatrix.connect( self.SelectionMatrix )

        self.WINDOW_SIZE = self.opPixelFeatures.WINDOW_SIZE
 def aaaNumpyFile(self):
     g =graph.Graph()
     npfile = "/home/akreshuk/data/synapse_small_4d.npy"
     reader = OpInputDataReader(graph=g)
     reader.FilePath.setValue(npfile)
     #out = reader.Output[:].wait()
     #print out.shape
     
     opFeatures = OpPixelFeaturesPresmoothed(graph=g)
     opFeatures.Scales.setValue(self.scales)
     opFeatures.FeatureIds.setValue(self.featureIds)
     opFeatures.Input.connect(reader.Output)
     opFeatures.Matrix.setValue(self.selectedFeatures[5])
     out = opFeatures.Output[:].wait()
     print out.shape
     
     opFeaturesInterp = OpPixelFeaturesInterpPresmoothed(graph=g)
     opFeaturesInterp.Scales.setValue(self.scales)
     opFeaturesInterp.FeatureIds.setValue(self.featureIds)
     opFeaturesInterp.Input.connect(reader.Output)
     opFeaturesInterp.Matrix.setValue(self.selectedFeatures[5])
     opFeaturesInterp.InterpolationScaleZ.setValue(2)
     out = opFeaturesInterp.Output[:].wait()
     
     print out.shape
Beispiel #3
0
    def __init__(self, filter_implementation, *args, **kwargs):
        super(OpFeatureSelectionNoCache, self).__init__(*args, **kwargs)

        # Create the operator that actually generates the features
        if filter_implementation == 'Original':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Original(parent=self)
            logger.debug("Using ORIGINAL filters")
        elif filter_implementation == 'Refactored':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Refactored(parent=self)
        elif filter_implementation == 'Interpolated':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Interpolated(parent=self)
            self.opPixelFeatures.InterpolationScaleZ.setValue(2)
            logger.debug("Using INTERPOLATED filters")
        else:
            raise RuntimeError("Unknown filter implementation option: {}".format( filter_implementation ))

        # Connect our internal operators to our external inputs 
        self.opPixelFeatures.Scales.connect( self.Scales )
        self.opPixelFeatures.FeatureIds.connect( self.FeatureIds )
        self.opPixelFeatures.Input.connect( self.InputImage )
 def aaaAssert(self):
     g = graph.Graph()
     data = numpy.zeros((self.nx, self.ny, self.nz), dtype=numpy.float32)
     for i in range(self.data3d.shape[2]):
         data[:, :, i]=i
     data = data.view(vigra.VigraArray)
     data.axistags = vigra.VigraArray.defaultAxistags(3)
     opFeaturesInterp = OpPixelFeaturesInterpPresmoothed(graph=g)
     opFeaturesInterp.Input.setValue(data)
     opFeaturesInterp.Scales.setValue(self.scales)
     opFeaturesInterp.FeatureIds.setValue(self.featureIds)
     opFeaturesInterp.InterpolationScaleZ.setValue(self.scaleZ)
     opFeaturesInterp.Matrix.setValue(self.selectedFeatures[0])
     
     out = opFeaturesInterp.Output[:].wait()
     
     print "passed"
 def runFeatures(self, data, dataInterp):
     g = graph.Graph()
     opFeatures = OpPixelFeaturesPresmoothed(graph=g)
     opFeaturesInterp = OpPixelFeaturesInterpPresmoothed(graph=g)
     
     opFeatures.Input.setValue(dataInterp)
     opFeaturesInterp.Input.setValue(data)
     
     opFeatures.Scales.setValue(self.scales)
     opFeaturesInterp.Scales.setValue(self.scales)
     
     opFeatures.FeatureIds.setValue(self.featureIds)
     opFeaturesInterp.FeatureIds.setValue(self.featureIds)
     
     opFeaturesInterp.InterpolationScaleZ.setValue(self.scaleZ)
     
     #for i, imatrix in enumerate(self.selectedFeatures[0:1]):
     for i, imatrix in enumerate(self.selectedFeatures):
         opFeatures.Matrix.setValue(imatrix)
         opFeaturesInterp.Matrix.setValue(imatrix)
         outputInterpData = opFeatures.Output[:].wait()
         outputInterpFeatures = opFeaturesInterp.Output[:].wait()
         
         for iz in range(self.nz):
         #for iz in range(2, 3):
             #print iz, iz*self.scaleZ
             try:
                 outputInterpDataSlice = opFeatures.Output[:, :, iz*self.scaleZ:iz*self.scaleZ+1, :].wait()
                 outputInterpFeaturesSlice = opFeaturesInterp.Output[:, :, iz, :].wait()
                 assert_array_almost_equal(outputInterpDataSlice, outputInterpFeaturesSlice, 1)
                 assert_array_almost_equal(outputInterpData[:, :, iz*self.scaleZ, 0], outputInterpFeatures[:, :, iz, 0], 1)
                 #assert_array_almost_equal(outputInterpDataSlice[:, :, 0, :], outputInterpData[:, :, iz*self.scaleZ, :], 3)
                 assert_array_almost_equal(outputInterpFeatures[:, :, iz, :], outputInterpFeaturesSlice[:, :, 0, :], 1)
             except AssertionError:
                 print "failed for feature:", imatrix, i
                 print "failed for slice:", iz
                 print "inter data:", outputInterpData[:, :, iz*self.scaleZ, 0]
                 print "inter features:", outputInterpFeatures[:, :, iz, 0]
                 print "inter data slice:", outputInterpDataSlice[:, :, 0, 0]
                 print "inter features:", outputInterpFeaturesSlice[:, :, 0, 0]
                 raise AssertionError
Beispiel #6
0
class OpFeatureSelectionNoCache(Operator):
    """
    The top-level operator for the feature selection applet for headless workflows.
    """
    name = "OpFeatureSelection"
    category = "Top-level"

    ScalesList = ScalesList
    FeatureGroups = FeatureGroups
    FeatureNames = FeatureNames

    MinimalFeatures = numpy.zeros( (len(FeatureNames), len(ScalesList)), dtype=bool )
    MinimalFeatures[0,0] = True

    # Multiple input images
    InputImage = InputSlot()

    # The following input slots are applied uniformly to all input images
    Scales = InputSlot( value=ScalesList ) # The list of possible scales to use when computing features
    FeatureIds = InputSlot( value=getFeatureIdOrder() ) # The list of features to compute
    SelectionMatrix = InputSlot( value=MinimalFeatures ) # A matrix of bools indicating which features to output.
                                                       # The matrix rows correspond to feature types in the order specified by the FeatureIds input.
                                                       #  (See OpPixelFeaturesPresmoothed for the available feature types.)
                                                       # The matrix columns correspond to the scales provided in the Scales input,
                                                       #  which requires that the number of matrix columns must match len(Scales.value)

    FeatureListFilename = InputSlot(stype="str", optional=True)
    
    # Features are presented in the channels of the output image
    # Output can be optionally accessed via an internal cache.
    # (Training a classifier benefits from caching, but predicting with an existing classifier does not.)
    OutputImage = OutputSlot()

    FeatureLayers = OutputSlot(level=1) # For the GUI, we also provide each feature as a separate slot in this multislot

    # For ease of development and testing, the underlying feature computation implementation 
    #  can be switched via a constructor argument.  These are the possible choices.
    FilterImplementations = ['Original', 'Refactored', 'Interpolated']
    
    def __init__(self, filter_implementation, *args, **kwargs):
        super(OpFeatureSelectionNoCache, self).__init__(*args, **kwargs)

        # Create the operator that actually generates the features
        if filter_implementation == 'Original':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Original(parent=self)
            logger.debug("Using ORIGINAL filters")
        elif filter_implementation == 'Refactored':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Refactored(parent=self)
        elif filter_implementation == 'Interpolated':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Interpolated(parent=self)
            self.opPixelFeatures.InterpolationScaleZ.setValue(2)
            logger.debug("Using INTERPOLATED filters")
        else:
            raise RuntimeError("Unknown filter implementation option: {}".format( filter_implementation ))

        # Connect our internal operators to our external inputs 
        self.opPixelFeatures.Scales.connect( self.Scales )
        self.opPixelFeatures.FeatureIds.connect( self.FeatureIds )
        self.opReorderIn = OpReorderAxes(parent=self)
        self.opReorderIn.Input.connect(self.InputImage)
        self.opPixelFeatures.Input.connect(self.opReorderIn.Output)
        self.opReorderOut = OpReorderAxes(parent=self)
        self.opReorderOut.Input.connect(self.opPixelFeatures.Output)
        self.opReorderLayers = OperatorWrapper(OpReorderAxes, parent=self,
                                               broadcastingSlotNames=["AxisOrder"])
        self.opReorderLayers.Input.connect(self.opPixelFeatures.Features)

        # We don't connect SelectionMatrix here because we want to 
        #  check it for errors (See setupOutputs)
        # self.opPixelFeatures.SelectionMatrix.connect( self.SelectionMatrix )

        self.WINDOW_SIZE = self.opPixelFeatures.WINDOW_SIZE

    def setupOutputs(self):
        # drop non-channel singleton axes
        allAxes = 'txyzc'
        ts = self.InputImage.meta.getTaggedShape()
        oldAxes = "".join(ts.keys())
        newAxes = "".join([a for a in allAxes
                           if a in ts and ts[a] > 1 or a == 'c'])
        self.opReorderIn.AxisOrder.setValue(newAxes)
        self.opReorderOut.AxisOrder.setValue(oldAxes)
        self.opReorderLayers.AxisOrder.setValue(oldAxes)
        
        # Get features from external file
        if self.FeatureListFilename.ready() and len(self.FeatureListFilename.value) > 0:
                  
            self.OutputImage.disconnect()
            self.FeatureLayers.disconnect()
            
            axistags = self.InputImage.meta.axistags
                
            with h5py.File(self.FeatureListFilename.value,'r') as f:
                dset_names = []
                f.visit(dset_names.append)
                if len(dset_names) != 1:
                    sys.stderr.write("Input external features HDF5 file should have exactly 1 dataset.\n")
                    sys.exit(1)                
                
                dset = f[dset_names[0]]
                chnum = dset.shape[-1]
                shape = dset.shape
                dtype = dset.dtype.type
            
            # Set the metadata for FeatureLayers. Unlike OutputImage and CachedOutputImage, 
            # FeatureLayers has one slot per channel and therefore the channel dimension must be 1.
            self.FeatureLayers.resize(chnum)
            for i in range(chnum):
                self.FeatureLayers[i].meta.shape    = shape[:-1]+(1,)
                self.FeatureLayers[i].meta.dtype    = dtype
                self.FeatureLayers[i].meta.axistags = axistags 
                self.FeatureLayers[i].meta.display_mode = 'default' 
                self.FeatureLayers[i].meta.description = "feature_channel_"+str(i)
            
            self.OutputImage.meta.shape    = shape
            self.OutputImage.meta.dtype    = dtype 
            self.OutputImage.meta.axistags = axistags
            
        else:
            # Set the new selection matrix and check if it creates an error.
            selections = self.SelectionMatrix.value
            self.opPixelFeatures.Matrix.setValue( selections )
            invalid_scales = self.opPixelFeatures.getInvalidScales()
            if invalid_scales:
                msg = "Some of your selected feature scales are too large for your dataset.\n"\
                      "Choose smaller scales (sigma) or use a larger dataset.\n"\
                      "The invalid scales are: {}".format( invalid_scales )                      
                raise DatasetConstraintError( "Feature Selection", msg )
            
            # Connect our external outputs to our internal operators
            self.OutputImage.connect( self.opReorderOut.Output )
            self.FeatureLayers.connect( self.opReorderLayers.Output )

    def propagateDirty(self, slot, subindex, roi):
        # Output slots are directly connected to internal operators
        pass
    
    def execute(self, slot, subindex, rroi, result):
        if len(self.FeatureListFilename.value) == 0:
            return
        
        # Set the channel corresponding to the slot(subindex) of the feature layers
        if slot == self.FeatureLayers:
            rroi.start[-1] = subindex[0]
            rroi.stop[-1] = subindex[0] + 1 
            
        key = roiToSlice(rroi.start, rroi.stop)
        
        # Read features from external file
        with h5py.File(self.FeatureListFilename.value, 'r') as f:
            dset_names = []
            f.visit(dset_names.append)
            
            if len(dset_names) != 1:
                sys.stderr.write("Input external features HDF5 file should have exactly 1 dataset.")
                return 
                
            dset = f[dset_names[0]]              
            result[...] = dset[key]
                        
        return result
class OpFeatureSelectionNoCache(Operator):
    """
    The top-level operator for the feature selection applet for headless workflows.
    """
    name = "OpFeatureSelection"
    category = "Top-level"

    # Multiple input images
    InputImage = InputSlot()

    # The following input slots are applied uniformly to all input images
    Scales = InputSlot(
    )  # The list of possible scales to use when computing features
    FeatureIds = InputSlot()  # The list of features to compute
    SelectionMatrix = InputSlot(
    )  # A matrix of bools indicating which features to output.
    # The matrix rows correspond to feature types in the order specified by the FeatureIds input.
    #  (See OpPixelFeaturesPresmoothed for the available feature types.)
    # The matrix columns correspond to the scales provided in the Scales input,
    #  which requires that the number of matrix columns must match len(Scales.value)
    FeatureListFilename = InputSlot(stype="str", optional=True)

    # Features are presented in the channels of the output image
    # Output can be optionally accessed via an internal cache.
    # (Training a classifier benefits from caching, but predicting with an existing classifier does not.)
    OutputImage = OutputSlot()

    FeatureLayers = OutputSlot(
        level=1
    )  # For the GUI, we also provide each feature as a separate slot in this multislot

    # For ease of development and testing, the underlying feature computation implementation
    #  can be switched via a constructor argument.  These are the possible choices.
    FilterImplementations = ['Original', 'Refactored', 'Interpolated']

    def __init__(self, filter_implementation, *args, **kwargs):
        super(OpFeatureSelectionNoCache, self).__init__(*args, **kwargs)

        # Create the operator that actually generates the features
        if filter_implementation == 'Original':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Original(
                parent=self)
            logger.debug("Using ORIGINAL filters")
        elif filter_implementation == 'Refactored':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Refactored(
                parent=self)
        elif filter_implementation == 'Interpolated':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Interpolated(
                parent=self)
            self.opPixelFeatures.InterpolationScaleZ.setValue(2)
            logger.debug("Using INTERPOLATED filters")
        else:
            raise RuntimeError(
                "Unknown filter implementation option: {}".format(
                    filter_implementation))

        # Connect our internal operators to our external inputs
        self.opPixelFeatures.Scales.connect(self.Scales)
        self.opPixelFeatures.FeatureIds.connect(self.FeatureIds)
        self.opReorderIn = OpReorderAxes(parent=self)
        self.opReorderIn.Input.connect(self.InputImage)
        self.opPixelFeatures.Input.connect(self.opReorderIn.Output)
        self.opReorderOut = OpReorderAxes(parent=self)
        self.opReorderOut.Input.connect(self.opPixelFeatures.Output)
        self.opReorderLayers = OperatorWrapper(
            OpReorderAxes, parent=self, broadcastingSlotNames=["AxisOrder"])
        self.opReorderLayers.Input.connect(self.opPixelFeatures.Features)

        # We don't connect SelectionMatrix here because we want to
        #  check it for errors (See setupOutputs)
        # self.opPixelFeatures.SelectionMatrix.connect( self.SelectionMatrix )

        self.WINDOW_SIZE = self.opPixelFeatures.WINDOW_SIZE

    def setupOutputs(self):
        # drop non-channel singleton axes
        allAxes = 'txyzc'
        ts = self.InputImage.meta.getTaggedShape()
        oldAxes = "".join(ts.keys())
        newAxes = "".join(
            [a for a in allAxes if a in ts and ts[a] > 1 or a == 'c'])
        self.opReorderIn.AxisOrder.setValue(newAxes)
        self.opReorderOut.AxisOrder.setValue(oldAxes)
        self.opReorderLayers.AxisOrder.setValue(oldAxes)
        if self.FeatureListFilename.ready() and len(
                self.FeatureListFilename.value) > 0:
            f = open(self.FeatureListFilename.value, 'r')
            self._files = []
            for line in f:
                line = line.strip()
                if len(line) > 0:
                    self._files.append(line)
            f.close()

            self.OutputImage.disconnect()
            self.FeatureLayers.disconnect()

            axistags = self.InputImage.meta.axistags

            self.FeatureLayers.resize(len(self._files))
            for i in range(len(self._files)):
                f = h5py.File(self._files[i], 'r')
                shape = f["data"].shape
                assert len(shape) == 3
                dtype = f["data"].dtype.type
                f.close()
                self.FeatureLayers[i].meta.shape = shape + (1, )
                self.FeatureLayers[i].meta.dtype = dtype
                self.FeatureLayers[i].meta.axistags = axistags
                self.FeatureLayers[i].meta.description = os.path.basename(
                    self._files[i])

            self.OutputImage.meta.shape = (shape) + (len(self._files), )
            self.OutputImage.meta.dtype = dtype
            self.OutputImage.meta.axistags = axistags

            self.CachedOutputImage.meta.shape = (shape) + (len(self._files), )
            self.CachedOutputImage.meta.axistags = axistags
        else:
            # Set the new selection matrix and check if it creates an error.
            selections = self.SelectionMatrix.value
            self.opPixelFeatures.Matrix.setValue(selections,
                                                 check_changed=False)
            invalid_scales = self.opPixelFeatures.getInvalidScales()
            if invalid_scales:
                msg = "Some of your selected feature scales are too large for your dataset.\n"\
                      "Choose smaller scales (sigma) or use a larger dataset.\n"\
                      "The invalid scales are: {}".format( invalid_scales )
                raise DatasetConstraintError("Feature Selection", msg)

            # Connect our external outputs to our internal operators
            self.OutputImage.connect(self.opReorderOut.Output)
            self.FeatureLayers.connect(self.opReorderLayers.Output)

    def propagateDirty(self, slot, subindex, roi):
        # Output slots are directly connected to internal operators
        pass

    def execute(self, slot, subindex, rroi, result):
        if len(self.FeatureListFilename.value) == 0:
            return

        assert result.dtype == numpy.float32

        key = roiToSlice(rroi.start, rroi.stop)

        if slot == self.FeatureLayers:
            index = subindex[0]
            f = h5py.File(self._files[index], 'r')
            result[..., 0] = f["data"][key[0:3]]
            return result
        elif slot == self.OutputImage:
            assert result.ndim == 4
            assert result.shape[-1] == len(
                self._files), "result.shape = %r" % result.shape
            assert rroi.start == 0, "rroi = %r" % (rroi, )
            assert rroi.stop == len(self._files), "rroi = %r" % (rroi, )

            j = 0
            for i in range(key[3].start, key[3].stop):
                f = h5py.File(self._files[i], 'r')
                r = f["data"][key[0:3]]
                assert r.dtype == numpy.float32
                assert r.ndim == 3
                f.close()
                result[:, :, :, j] = r
                j += 1
            return result
class OpFeatureSelectionNoCache(Operator):
    """
    The top-level operator for the feature selection applet for headless workflows.
    """
    name = "OpFeatureSelection"
    category = "Top-level"

    # Multiple input images
    InputImage = InputSlot()

    # The following input slots are applied uniformly to all input images
    Scales = InputSlot() # The list of possible scales to use when computing features
    FeatureIds = InputSlot() # The list of features to compute
    SelectionMatrix = InputSlot() # A matrix of bools indicating which features to output.
                         # The matrix rows correspond to feature types in the order specified by the FeatureIds input.
                         #  (See OpPixelFeaturesPresmoothed for the available feature types.)
                         # The matrix columns correspond to the scales provided in the Scales input,
                         #  which requires that the number of matrix columns must match len(Scales.value)
    FeatureListFilename = InputSlot(stype="str", optional=True)
    
    # Features are presented in the channels of the output image
    # Output can be optionally accessed via an internal cache.
    # (Training a classifier benefits from caching, but predicting with an existing classifier does not.)
    OutputImage = OutputSlot()

    FeatureLayers = OutputSlot(level=1) # For the GUI, we also provide each feature as a separate slot in this multislot

    # For ease of development and testing, the underlying feature computation implementation 
    #  can be switched via a constructor argument.  These are the possible choices.
    FilterImplementations = ['Original', 'Refactored', 'Interpolated']
    
    def __init__(self, filter_implementation, *args, **kwargs):
        super(OpFeatureSelectionNoCache, self).__init__(*args, **kwargs)

        # Create the operator that actually generates the features
        if filter_implementation == 'Original':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Original(parent=self)
            logger.debug("Using ORIGINAL filters")
        elif filter_implementation == 'Refactored':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Refactored(parent=self)
        elif filter_implementation == 'Interpolated':
            self.opPixelFeatures = OpPixelFeaturesPresmoothed_Interpolated(parent=self)
            self.opPixelFeatures.InterpolationScaleZ.setValue(2)
            logger.debug("Using INTERPOLATED filters")
        else:
            raise RuntimeError("Unknown filter implementation option: {}".format( filter_implementation ))

        # Connect our internal operators to our external inputs 
        self.opPixelFeatures.Scales.connect( self.Scales )
        self.opPixelFeatures.FeatureIds.connect( self.FeatureIds )
        self.opReorderIn = OpReorderAxes(parent=self)
        self.opReorderIn.Input.connect(self.InputImage)
        self.opPixelFeatures.Input.connect(self.opReorderIn.Output)
        self.opReorderOut = OpReorderAxes(parent=self)
        self.opReorderOut.Input.connect(self.opPixelFeatures.Output)
        self.opReorderLayers = OperatorWrapper(OpReorderAxes, parent=self,
                                               broadcastingSlotNames=["AxisOrder"])
        self.opReorderLayers.Input.connect(self.opPixelFeatures.Features)

        # We don't connect SelectionMatrix here because we want to 
        #  check it for errors (See setupOutputs)
        # self.opPixelFeatures.SelectionMatrix.connect( self.SelectionMatrix )

        self.WINDOW_SIZE = self.opPixelFeatures.WINDOW_SIZE

    def setupOutputs(self):
        # drop non-channel singleton axes
        allAxes = 'txyzc'
        ts = self.InputImage.meta.getTaggedShape()
        oldAxes = "".join(ts.keys())
        newAxes = "".join([a for a in allAxes
                           if a in ts and ts[a] > 1 or a == 'c'])
        self.opReorderIn.AxisOrder.setValue(newAxes)
        self.opReorderOut.AxisOrder.setValue(oldAxes)
        self.opReorderLayers.AxisOrder.setValue(oldAxes)
        if self.FeatureListFilename.ready() and len(self.FeatureListFilename.value) > 0:
            f = open(self.FeatureListFilename.value, 'r')
            self._files = []
            for line in f:
                line = line.strip()
                if len(line) > 0:
                    self._files.append(line)
            f.close()
            
            self.OutputImage.disconnect()
            self.FeatureLayers.disconnect()
            
            axistags = self.InputImage.meta.axistags
            
            self.FeatureLayers.resize(len(self._files))
            for i in range(len(self._files)):
                f = h5py.File(self._files[i], 'r')
                shape = f["data"].shape
                assert len(shape) == 3
                dtype = f["data"].dtype.type
                f.close()
                self.FeatureLayers[i].meta.shape    = shape+(1,)
                self.FeatureLayers[i].meta.dtype    = dtype
                self.FeatureLayers[i].meta.axistags = axistags 
                self.FeatureLayers[i].meta.description = os.path.basename(self._files[i]) 
            
            self.OutputImage.meta.shape    = (shape) + (len(self._files),)
            self.OutputImage.meta.dtype    = dtype 
            self.OutputImage.meta.axistags = axistags 
            
            self.CachedOutputImage.meta.shape    = (shape) + (len(self._files),)
            self.CachedOutputImage.meta.axistags = axistags 
        else:
            # Set the new selection matrix and check if it creates an error.
            selections = self.SelectionMatrix.value
            self.opPixelFeatures.Matrix.setValue( selections, check_changed=False )
            invalid_scales = self.opPixelFeatures.getInvalidScales()
            if invalid_scales:
                msg = "Some of your selected feature scales are too large for your dataset.\n"\
                      "Choose smaller scales (sigma) or use a larger dataset.\n"\
                      "The invalid scales are: {}".format( invalid_scales )                      
                raise DatasetConstraintError( "Feature Selection", msg )
            
            # Connect our external outputs to our internal operators
            self.OutputImage.connect( self.opReorderOut.Output )
            self.FeatureLayers.connect( self.opReorderLayers.Output )

    def propagateDirty(self, slot, subindex, roi):
        # Output slots are directly connected to internal operators
        pass
    
    def execute(self, slot, subindex, rroi, result):
        if len(self.FeatureListFilename.value) == 0:
            return
       
        assert result.dtype == numpy.float32
        
        key = roiToSlice(rroi.start, rroi.stop)
            
        if slot == self.FeatureLayers:
            index = subindex[0]
            f = h5py.File(self._files[index], 'r')
            result[...,0] = f["data"][key[0:3]]
            return result
        elif slot == self.OutputImage:
            assert result.ndim == 4
            assert result.shape[-1] == len(self._files), "result.shape = %r" % result.shape 
            assert rroi.start == 0, "rroi = %r" % (rroi,)
            assert rroi.stop  == len(self._files), "rroi = %r" % (rroi,)
            
            j = 0
            for i in range(key[3].start, key[3].stop):
                f = h5py.File(self._files[i], 'r')
                r = f["data"][key[0:3]]
                assert r.dtype == numpy.float32
                assert r.ndim == 3
                f.close()
                result[:,:,:,j] = r 
                j += 1
            return result