class OpLabelPreviewer(Operator):
    name = "LabelPreviewer"
    description = "Provides a Preview of the labels after gaussian smoothing"

    inputSlots = [InputSlot("Labels"), InputSlot("Sigma", stype="object")]
    outputSlots = [OutputSlot("Output")]

    def __init__(self, *args, **kwargs):
        super(OpLabelPreviewer, self).__init__(*args, **kwargs)
        self._svr = SVR()

    def setupOutputs(self):
        self.Output.meta.assignFrom(self.Labels.meta)
        self.Output.meta.dtype = np.float32
        self.Output.meta.drange = (0.0, 1.0)

    @traceLogged(logger,
                 level=logging.INFO,
                 msg="OpTrainCounter: Training Counting Regressor")
    def execute(self, slot, subindex, roi, result):
        progress = 0

        key = roi.toSlice()
        dot = self.Labels[key].wait()
        self._svr.set_params(Sigma=self.Sigma.value)

        result[...] = self._svr.smoothLabels(dot)[0]
        return result

    def propagateDirty(self, slot, subindex, roi):
        self.Output.setDirty((slice(None)))
Example #2
0
    def __init__(self, *args, **kwargs):
        super(OpTrainCounter, self).__init__(*args, **kwargs)
        self.progressSignal = OrderedSignal()
        self._svr = SVR()
        params = self._svr.get_params()
        self.initInputs(params)
        self.Classifier.meta.dtype = object
        self.Classifier.meta.shape = (self.numRegressors, )

        # Normally, lane removal does not trigger a dirty notification.
        # But in this case, if the lane contained any label data whatsoever,
        #  the classifier needs to be marked dirty.
        # We know which slots contain (or contained) label data because they have
        # been 'touched' at some point (they became dirty at some point).
        self._touched_slots = set()

        def handle_new_lane(multislot, index, newlength):
            def handle_dirty_lane(slot, roi):
                self._touched_slots.add(slot)

            multislot[index].notifyDirty(handle_dirty_lane)

        self.ForegroundLabels.notifyInserted(handle_new_lane)
        self.BackgroundLabels.notifyInserted(handle_new_lane)

        def handle_remove_lane(multislot, index, newlength):
            # If the lane we're removing contained
            # label data, then mark the downstream dirty
            if multislot[index] in self._touched_slots:
                self.Classifier.setDirty()
                self._touched_slots.remove(multislot[index])

        self.ForegroundLabels.notifyRemove(handle_remove_lane)
        self.BackgroundLabels.notifyRemove(handle_remove_lane)
class OpLabelPreviewer(Operator):
    name = "LabelPreviewer"
    description = "Provides a Preview of the labels after gaussian smoothing"

    inputSlots = [InputSlot("Labels"),
                 InputSlot("Sigma", stype = "object")]
    outputSlots = [OutputSlot("Output")]

    def __init__(self, *args, **kwargs):
        super(OpLabelPreviewer, self).__init__(*args, **kwargs)
        self._svr = SVR()

    def setupOutputs(self):
        self.Output.meta.assignFrom(self.Labels.meta)
        self.Output.meta.dtype = np.float32
        self.Output.meta.drange = (0.0, 1.0)

    @traceLogged(logger, level=logging.INFO, msg="OpTrainCounter: Training Counting Regressor")
    def execute(self, slot, subindex, roi, result):
        progress = 0
        
        key = roi.toSlice()
        dot=self.Labels[key].wait()
        self._svr.set_params(Sigma = self.Sigma.value)

        result[...] = self._svr.smoothLabels(dot)[0]
        return result
    
    def propagateDirty(self, slot, subindex, roi):
        self.Output.setDirty((slice(None)))
Example #4
0
 def __init__(self, *args, **kwargs):
     super(OpTrainCounter, self).__init__(*args, **kwargs)
     self.progressSignal = OrderedSignal()
     self._svr = SVR()
     params = self._svr.get_params()
     self.initInputs(params)
     self.Classifier.meta.dtype = object
     self.Classifier.meta.shape = (self.numRegressors,)
    def _deserialize(self, classifierGroup, slot):
        # Due to non-shared hdf5 dlls, vigra can't read directly
        # from our open hdf5 group. Instead, we'll copy the
        # classfier data to a temporary file and give it to vigra.
        tmpDir = tempfile.mkdtemp()
        cachePath = os.path.join(tmpDir, 'tmp_classifier_cache.h5').replace('\\', '/')
        with h5py.File(cachePath, 'w') as cacheFile:
            cacheFile.copy(classifierGroup, self.name)

        forests = []
        for name, forestGroup in sorted(classifierGroup.items()):
            targetname = '{0}/{1}'.format(self.name, name)
            #forests.append(vigra.learning.RandomForest(cachePath, targetname))
            from ilastik.applets.counting.countingsvr import SVR
            forests.append(SVR.load(cachePath, targetname))
            

        os.remove(cachePath)
        os.rmdir(tmpDir)

        # Now force the classifier into our classifier cache. The
        # downstream operators (e.g. the prediction operator) can
        # use the classifier without inducing it to be re-trained.
        # (This assumes that the classifier we are loading is
        # consistent with the images and labels that we just
        # loaded. As soon as training input changes, it will be
        # retrained.)
        self.cache.forceValue(numpy.array(forests))
Example #6
0
    def __init__(self, *args, **kwargs):
        super(OpTrainCounter, self).__init__(*args, **kwargs)
        self.progressSignal = OrderedSignal()
        self._svr = SVR()
        params = self._svr.get_params()
        self.initInputs(params)
        self.Classifier.meta.dtype = object
        self.Classifier.meta.shape = (self.numRegressors,)

        # Normally, lane removal does not trigger a dirty notification.
        # But in this case, if the lane contained any label data whatsoever,
        #  the classifier needs to be marked dirty.
        # We know which slots contain (or contained) label data because they have
        # been 'touched' at some point (they became dirty at some point).
        self._touched_slots = set()
        def handle_new_lane( multislot, index, newlength ):
            def handle_dirty_lane( slot, roi ):
                self._touched_slots.add(slot)
            multislot[index].notifyDirty( handle_dirty_lane )
        self.ForegroundLabels.notifyInserted( handle_new_lane )
        self.BackgroundLabels.notifyInserted( handle_new_lane )

        def handle_remove_lane( multislot, index, newlength ):
            # If the lane we're removing contained
            # label data, then mark the downstream dirty
            if multislot[index] in self._touched_slots:
                self.Classifier.setDirty()
                self._touched_slots.remove(multislot[index])
        self.ForegroundLabels.notifyRemove( handle_remove_lane )
        self.BackgroundLabels.notifyRemove( handle_remove_lane )
Example #7
0
    def _deserialize(self, classifierGroup, slot):
        # Due to non-shared hdf5 dlls, vigra can't read directly
        # from our open hdf5 group. Instead, we'll copy the
        # classfier data to a temporary file and give it to vigra.
        tmpDir = tempfile.mkdtemp()
        cachePath = os.path.join(tmpDir, 'tmp_classifier_cache.h5').replace('\\', '/')
        with h5py.File(cachePath, 'w') as cacheFile:
            cacheFile.copy(classifierGroup, self.name)

        try:
            forests = []
            for name, forestGroup in sorted(classifierGroup.items()):
                targetname = '{0}/{1}'.format(self.name, name)
                #forests.append(vigra.learning.RandomForest(cachePath, targetname))
                from ilastik.applets.counting.countingsvr import SVR
                forests.append(SVR.load(cachePath, targetname))
        except:
            warnings.warn( "Wasn't able to deserialize the saved classifier.  "
                           "It will need to be retrainied" )
            return
        finally:
            os.remove(cachePath)
            os.rmdir(tmpDir)

        # Now force the classifier into our classifier cache. The
        # downstream operators (e.g. the prediction operator) can
        # use the classifier without inducing it to be re-trained.
        # (This assumes that the classifier we are loading is
        # consistent with the images and labels that we just
        # loaded. As soon as training input changes, it will be
        # retrained.)
        self.cache.forceValue(numpy.array(forests))
Example #8
0
 def __init__(self, *args, **kwargs):
     super(OpTrainCounter, self).__init__(*args, **kwargs)
     self.progressSignal = OrderedSignal()
     self._svr = SVR()
     self.Classifier.meta.dtype = object
     self.Classifier.meta.shape = (1,)
     self.progressSignal = OrderedSignal()
Example #9
0
 def train_and_store(i):
     result[i] = SVR(minmax=normalizationFactors, **params)
     result[i].fitPrepared(fullFeatMatrix,
                           fullLabelsMatrix,
                           tags=fullTags,
                           boxConstraints=boxConstraints,
                           numRegressors=self.numRegressors,
                           trainAll=False)
Example #10
0
class OpTrainCounter(Operator):
    name = "TrainCounter"
    description = "Train a random forest on multiple images"
    category = "Learning"

    inputSlots = [InputSlot("Images", level=1),
                  InputSlot("ForegroundLabels", level=1), 
                  InputSlot("BackgroundLabels", level=1),
                  InputSlot("fixClassifier", stype="bool"),
                  InputSlot("nonzeroLabelBlocks", level=1),
                  InputSlot("Sigma", stype = "float", value=2.0), 
                  InputSlot("Epsilon",  stype = "float"), 
                  InputSlot("C",  stype = "float"), 
                  InputSlot("SelectedOption", stype = "object"),
                  InputSlot("Ntrees", stype = "int"), #RF parameter
                  InputSlot("MaxDepth", stype = "object"), #RF parameter, None means grow until purity
                  InputSlot("BoxConstraintRois", level = 1, stype = "list", value = []),
                  InputSlot("BoxConstraintValues", level = 1, stype = "list", value = []),
                  InputSlot("UpperBound")
                 ]
    outputSlots = [OutputSlot("Classifier")]
    options = SVR.options
    availableOptions = [checkOption(option["req"]) for option in SVR.options]
    numRegressors = 4

    def __init__(self, *args, **kwargs):
        super(OpTrainCounter, self).__init__(*args, **kwargs)
        self.progressSignal = OrderedSignal()
        self._svr = SVR()
        params = self._svr.get_params()
        self.initInputs(params)
        self.Classifier.meta.dtype = object
        self.Classifier.meta.shape = (self.numRegressors,)

    def initInputs(self, params):
        fix = False
        if self.fixClassifier.ready():
            fix = self.fixClassifier.value
        self.fixClassifier.setValue(True)
        self.Sigma.setValue(params["Sigma"])
        self.Epsilon.setValue(params["epsilon"])
        self.C.setValue(params["C"])
        self.Ntrees.setValue(params["ntrees"])
        self.MaxDepth.setValue(params["maxdepth"])
        self.SelectedOption.setValue(params["method"])

        self.fixClassifier.setValue(fix)

    def setupOutputs(self):
        if self.inputs["fixClassifier"].value == False:
            method = self.SelectedOption.value
            if type(method) is dict:
                method = method["method"]
            
            params = {"method" : method,
                      "Sigma": self.Sigma.value,
                      "epsilon" : self.Epsilon.value,
                      "C" : self.C.value,
                      "ntrees" : self.Ntrees.value,
                      "maxdepth" :self.MaxDepth.value
                     }
            self._svr.set_params(**params)
            #self.Classifier.setValue(self._svr)
            #self.outputs["Classifier"].meta.dtype = object
            #self.outputs["Classifier"].meta.shape = (self._forest_count,)



    #@traceLogged(logger, level=logging.INFO, msg="OpTrainCounter: Training Counting Regressor")
    def execute(self, slot, subindex, roi, result):

        progress = 0
        numImages = len(self.Images)
        self.progressSignal(progress)
        featMatrix=[]
        labelsMatrix=[]
        tagList = []

        
        #result[0] = self._svr

        for i,labels in enumerate(self.inputs["ForegroundLabels"]):
            if labels.meta.shape is not None:
                opGaussian = OpGaussianSmoothing(parent = self, graph = self.graph)
                opGaussian.Sigma.setValue(self.Sigma.value)
                opGaussian.Input.connect(self.ForegroundLabels[i])
                blocks = self.inputs["nonzeroLabelBlocks"][i][0].wait()
                
                reqlistlabels = []
                reqlistbg = []
                reqlistfeat = []
                progress += 10 / numImages
                self.progressSignal(progress)
                
                for b in blocks[0]:
                    request = opGaussian.Output[b]
                    #request = labels[b]
                    featurekey = list(b)
                    featurekey[-1] = slice(None, None, None)
                    request2 = self.Images[i][featurekey]
                    request3 = self.inputs["BackgroundLabels"][i][b]
                    reqlistlabels.append(request)
                    reqlistfeat.append(request2)
                    reqlistbg.append(request3)

                traceLogger.debug("Requests prepared")

                numLabelBlocks = len(reqlistlabels)
                progress_outer = [progress]
                if numLabelBlocks > 0:
                    progressInc = (80 - 10)/(numLabelBlocks * numImages)

                def progressNotify(req):
                    progress_outer[0] += progressInc/2
                    self.progressSignal(progress_outer[0])

                for ir, req in enumerate(reqlistfeat):
                    req.notify_finished(progressNotify)
                    req.submit()

                for ir, req in enumerate(reqlistlabels):
                    req.notify_finished(progressNotify)
                    req.submit()

                for ir, req in enumerate(reqlistbg):
                    req.notify_finished(progressNotify)
                    req.submit()
                
                traceLogger.debug("Requests fired")
                

                #Fixme: Maybe later request only part of the region?

                #image=self.inputs["Images"][i][:].wait()
                for ir, req in enumerate(reqlistlabels):
                    
                    labblock = req.wait()
                    
                    image = reqlistfeat[ir].wait()
                    labbgblock = reqlistbg[ir].wait()
                    labblock = labblock.reshape((image.shape[:-1]))
                    image = image.reshape((-1, image.shape[-1]))
                    labbgindices = np.where(labbgblock == 2)            
                    labbgindices = np.ravel_multi_index(labbgindices, labbgblock.shape)
                    
                    newDot, mapping, tags = \
                    self._svr.prepareDataRefactored(labblock, labbgindices)
                    #self._svr.prepareData(labblock, smooth = True)

                    labels   = newDot[mapping]
                    features = image[mapping]

                    featMatrix.append(features)
                    labelsMatrix.append(labels)
                    tagList.append(tags)
                
                progress = progress_outer[0]

                traceLogger.debug("Requests processed")


        self.progressSignal(80 / numImages)
        if len(featMatrix) == 0 or len(labelsMatrix) == 0:
            result[:] = None

        else:
            posTags = [tag[0] for tag in tagList]
            negTags = [tag[1] for tag in tagList]
            numPosTags = np.sum(posTags)
            numTags = np.sum(posTags) + np.sum(negTags)
            fullFeatMatrix = np.ndarray((numTags, self.Images[0].meta.shape[-1]), dtype = np.float64)
            fullLabelsMatrix = np.ndarray((numTags), dtype = np.float64)
            fullFeatMatrix[:] = np.NAN
            fullLabelsMatrix[:] = np.NAN
            currPosCount = 0
            currNegCount = numPosTags
            for i, posCount in enumerate(posTags):
                fullFeatMatrix[currPosCount:currPosCount + posTags[i],:] = featMatrix[i][:posCount,:]
                fullLabelsMatrix[currPosCount:currPosCount + posTags[i]] = labelsMatrix[i][:posCount]
                fullFeatMatrix[currNegCount:currNegCount + negTags[i],:] = featMatrix[i][posCount:,:]
                fullLabelsMatrix[currNegCount:currNegCount + negTags[i]] = labelsMatrix[i][posCount:]
                currPosCount += posTags[i]
                currNegCount += negTags[i]


            assert(not np.isnan(np.sum(fullFeatMatrix)))

            fullTags = [np.sum(posTags), np.sum(negTags)]
            #pool = RequestPool()

            maxima = np.max(fullFeatMatrix, axis=0)
            minima = np.min(fullFeatMatrix, axis=0)
            normalizationFactors = (minima,maxima)
            



            boxConstraintList = []
            boxConstraints = None
            if self.BoxConstraintRois.ready() and self.BoxConstraintValues.ready():
                for i, slot in enumerate(zip(self.BoxConstraintRois,self.BoxConstraintValues)):
                    for constr, val in zip(slot[0].value, slot[1].value):
                        boxConstraintList.append((i, constr, val))
                if len(boxConstraintList) > 0:
                    boxConstraints = self.constructBoxConstraints(boxConstraintList)

            params = self._svr.get_params() 
            try:
                pool = RequestPool()
                def train_and_store(i):
                    result[i] = SVR(minmax = normalizationFactors, **params)
                    result[i].fitPrepared(fullFeatMatrix, fullLabelsMatrix, tags = fullTags, boxConstraints = boxConstraints, numRegressors
                         = self.numRegressors, trainAll = False)
                for i in range(self.numRegressors):
                    req = pool.request(partial(train_and_store, i))
                
                pool.wait()
                pool.clean()
            
            except:
                logger.error("ERROR: could not learn regressor")
                logger.error("fullFeatMatrix shape = {}, dtype = {}".format(fullFeatMatrix.shape, fullFeatMatrix.dtype) )
                logger.error("fullLabelsMatrix shape = {}, dtype = {}".format(fullLabelsMatrix.shape, fullLabelsMatrix.dtype) )
                raise
            finally:
                self.progressSignal(100) 

        return result

    def propagateDirty(self, slot, subindex, roi):
        if slot is not self.inputs["fixClassifier"] and self.inputs["fixClassifier"].value == False:
            self.outputs["Classifier"].setDirty((slice(None),))
    
    def constructBoxConstraints(self, constraints):
        
        try:
            shape = np.array([[stop - start for start, stop in zip(constr[0][1:-2], constr[1][1:-2])] for _, constr,_ in
                       constraints])
            taggedShape = self.Images[0].meta.getTaggedShape()
            numcols = taggedShape['c']
            shape = shape[:,0] * shape[:,1]
            shape = np.sum(shape,axis = 0)
            constraintmatrix = np.ndarray(shape = (shape, numcols))
            constraintindices = []
            constraintvalues =  []
            offset = 0
            for imagenumber, constr, value in constraints:
                    slicing = [slice(start,stop) for start, stop in zip(constr[0][1:-2], constr[1][1:-2])]
                    numrows = (slicing[0].stop - slicing[0].start) * (slicing[1].stop - slicing[1].start)
                    slicing.append(slice(None)) 
                    slicing = tuple(slicing)

                    constraintmatrix[offset:offset + numrows,:] = self.Images[imagenumber][slicing].wait().reshape((numrows,
                                                                                                          -1))
                    constraintindices.append(offset)
                    constraintvalues.append(value)
                    offset = offset + numrows
            constraintindices.append(offset)

            constraintvalues = np.array(constraintvalues, np.float64)
            constraintindices = np.array(constraintindices, np.int)

            boxConstraints = {"boxFeatures" : constraintmatrix, "boxValues" : constraintvalues, "boxIndices" :
                              constraintindices}
        except:
            boxConstraints = None
            logger.error("An error has occured with the box Constraints: {} ".format(constraints))
        
        return boxConstraints
 def __init__(self, *args, **kwargs):
     super(OpLabelPreviewer, self).__init__(*args, **kwargs)
     self._svr = SVR()
 def __init__(self, *args, **kwargs):
     super(OpLabelPreviewer, self).__init__(*args, **kwargs)
     self._svr = SVR()
Example #13
0
class OpTrainCounter(Operator):
    name = "TrainCounter"
    description = "Train a random forest on multiple images"
    category = "Learning"

    inputSlots = [InputSlot("Images", level=1),InputSlot("Labels", level=1), InputSlot("fixClassifier", stype="bool"),
                  InputSlot("nonzeroLabelBlocks", level=1),
                  InputSlot("Sigma", value = [2.5], stype = "object"), 
                  InputSlot("Epsilon", value = 1E-3, stype = "float"), 
                  #InputSlot("UnderMult", value = 100, stype = "float"),
                  #InputSlot("OverMult", value = 100, stype = "float"), 
                  InputSlot("C", value = 1, stype = "float"), 
                  InputSlot("SelectedOption", 
                            value = SVR.options[0],
                            stype = "object"),
                  InputSlot("Ntrees", value = 10, stype = "int"), #RF parameter
                  InputSlot("MaxDepth", value =50, stype = "object"), #RF parameter, None means grow until purity
                  InputSlot("BoxConstraints", stype = "list", optional = True)
                 ]
    outputSlots = [OutputSlot("Classifier")]
    options = SVR.options

    def __init__(self, *args, **kwargs):
        super(OpTrainCounter, self).__init__(*args, **kwargs)
        self.progressSignal = OrderedSignal()
        self._svr = SVR()
        self.Classifier.meta.dtype = object
        self.Classifier.meta.shape = (1,)
        self.progressSignal = OrderedSignal()

    def setupOutputs(self):
        if self.inputs["fixClassifier"].value == False:
            params = {"method" : self.SelectedOption.value["method"],
                      "Sigma": self.Sigma.value,
                      "epsilon" : self.Epsilon.value,
                      "C" : self.C.value,
                      "ntrees" : self.Ntrees.value,
                      "maxdepth" :self.MaxDepth.value
                     }
            self._svr.set_params(**params)
            #self.Classifier.setValue(self._svr)
            #self.outputs["Classifier"].meta.dtype = object
            #self.outputs["Classifier"].meta.shape = (self._forest_count,)


    @traceLogged(logger, level=logging.INFO, msg="OpTrainCounter: Training Counting Regressor")
    def execute(self, slot, subindex, roi, result):
        progress = 0
        self.progressSignal(progress)
        featMatrix=[]
        labelsMatrix=[]
        tagList = []
        
        result[0] = self._svr

        for i,labels in enumerate(self.inputs["Labels"]):
            if labels.meta.shape is not None:
                labels=labels[:].wait()

                #Fixme: Maybe later request only part of the region?

                image=self.inputs["Images"][i][:].wait()

                newImg, newDot, mapping, tags = \
                result[0].prepareData(image, labels, smooth = True, normalize = False)
                features=newImg[mapping]
                labels=newDot[mapping]

                featMatrix.append(features)
                labelsMatrix.append(labels)
                tagList.append(tags)
                #tagsMatrix.append(tags)



        posTags = [tag[0] for tag in tagList]
        negTags = [tag[1] for tag in tagList]
        numPosTags = np.sum(posTags)
        numTags = np.sum(posTags) + np.sum(negTags)
        fullFeatMatrix = np.ndarray((numTags, self.Images[0].meta.shape[-1]))
        fullLabelsMatrix = np.ndarray((numTags))
        fullFeatMatrix[:] = np.NAN
        fullLabelsMatrix[:] = np.NAN
        currPosCount = 0
        currNegCount = numPosTags
        for i, posCount in enumerate(posTags):
            fullFeatMatrix[currPosCount:currPosCount + posTags[i],:] = featMatrix[i][:posCount,:]
            fullLabelsMatrix[currPosCount:currPosCount + posTags[i]] = labelsMatrix[i][:posCount]
            fullFeatMatrix[currNegCount:currNegCount + negTags[i],:] = featMatrix[i][posCount:,:]
            fullLabelsMatrix[currNegCount:currNegCount + negTags[i]] = labelsMatrix[i][posCount:]
            currPosCount += posTags[i]
            currNegCount += negTags[i]


        if np.isnan(np.sum(fullFeatMatrix)):
            raise Exception("NAN NAN NAN NAN BATMAN")
        #featMatrix=np.concatenate(featMatrix,axis=0)
        #labelsMatrix=np.concatenate(labelsMatrix,axis=0)
        #tagsMatrix=np.concatenate(tagsMatrix,axis=0)

        # train and store self._forest_count forests in parallel

        fullTags = [np.sum(posTags), np.sum(negTags)]
        #pool = RequestPool()

        self.progressSignal(30)
        boxConstraints = []
        if self.BoxConstraints.ready():
            constraints = self.BoxConstraints.value
            for constr in constraints:

                value = float(constr[2].toDouble()[0])
                slicing = [slice(start,stop) for start, stop in zip(constr[0][1:-2], constr[1][1:-2])]
                slicing.append(slice(None)) 
                slicing = tuple(slicing)
                features = self.Images[0][slicing].wait() 
                features = features.reshape((-1, features.shape[-1]))
                constraint = (value, features)
                boxConstraints.append(constraint)

        self.progressSignal(50)
        try:
            result[0].fitPrepared(fullFeatMatrix, fullLabelsMatrix, tags = fullTags, boxConstraints = boxConstraints)
        #req = pool.request(partial(result[0].fitPrepared, featMatrix, labelsMatrix, tagsMatrix, self.Epsilon.value))
        #pool.wait()
        #pool.clean()
        except:
            logger.error("ERROR: could not learn regressor")
            logger.error("fullFeatMatrix shape = {}, dtype = {}".format(fullFeatMatrix.shape, fullFeatMatrix.dtype) )
            logger.error("fullLabelsMatrix shape = {}, dtype = {}".format(fullLabelsMatrix.shape, fullLabelsMatrix.dtype) )
        self.progressSignal(100) 
        return result

    def propagateDirty(self, slot, subindex, roi):
        if slot is not self.inputs["fixClassifier"] and self.inputs["fixClassifier"].value == False:
            self.outputs["Classifier"].setDirty((slice(None),))