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" 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