Commit 4a04d651 authored by Hackmet's avatar Hackmet
Browse files

Added second threshold in detectionview.py

parent 961b3253
Loading
Loading
Loading
Loading
+40 −7
Original line number Original line Diff line number Diff line
@@ -275,7 +275,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
        self.dataset = self.verifySeedpoints(dataset)
        self.dataset = self.verifySeedpoints(dataset)
        self.img = img
        self.img = img
        self.imgclip = 0,0,0,0
        self.imgclip = 0,0,0,0
        self.seg = Segmentation(self.dataset)
        self.seg = Segmentation(self.dataset, self)
        self.thread = None
        self.thread = None
        self.view = parent
        self.view = parent
        
        
@@ -303,10 +303,12 @@ class ParticleDetectionView(QtWidgets.QWidget):
        group = QtWidgets.QGroupBox("Detection settings", self)
        group = QtWidgets.QGroupBox("Detection settings", self)
        grid = QtWidgets.QGridLayout()
        grid = QtWidgets.QGridLayout()
        self.parameters = []
        self.parameters = []
        checkBoxesToLink = {}
        
        # create editable parameters:
        # create editable parameters:
        for i, p in enumerate(self.seg.parlist):
        for i, p in enumerate(self.seg.parlist):
            label, colstretch = None, 1
            label, colstretch = None, 1
            if p.name == "points":
            if p.name == "contrastCurve":
                paramui = HistWidget(lambda : self.seg.calculateHist(self.seg.convert2Gray(self.subimg)),
                paramui = HistWidget(lambda : self.seg.calculateHist(self.seg.convert2Gray(self.subimg)),
                                     self.seg.calculateHistFunction, self)
                                     self.seg.calculateHistFunction, self)
                valuefunc = makeValueLambda(paramui.value)
                valuefunc = makeValueLambda(paramui.value)
@@ -316,6 +318,8 @@ class ParticleDetectionView(QtWidgets.QWidget):
                paramui.setChecked(p.value)
                paramui.setChecked(p.value)
                valuefunc = makeValueLambda(paramui.isChecked)
                valuefunc = makeValueLambda(paramui.isChecked)
                colstretch = 2
                colstretch = 2
                if p.linkedParameter is not None:
                    checkBoxesToLink[paramui] = p.linkedParameter
            elif p.dtype == int or p.dtype == float:
            elif p.dtype == int or p.dtype == float:
                label = QtWidgets.QLabel(p.helptext, self)
                label = QtWidgets.QLabel(p.helptext, self)
                if p.dtype == int:
                if p.dtype == int:
@@ -334,19 +338,45 @@ class ParticleDetectionView(QtWidgets.QWidget):
            elif p.dtype is None:
            elif p.dtype is None:
                label = QtWidgets.QLabel(p.helptext, self)
                label = QtWidgets.QLabel(p.helptext, self)
                paramui = None
                paramui = None
            
            if paramui is not None:
            if paramui is not None:
                self.parameters.append([paramui, p.name, valuefunc])
                self.parameters.append([paramui, p.name, valuefunc, None])
                if colstretch == 1:
                if colstretch == 1:
                    grid.addWidget(paramui, i, 1, QtCore.Qt.AlignLeft)
                    grid.addWidget(paramui, i, 1, QtCore.Qt.AlignLeft)
                else:
                else:
                    grid.addWidget(paramui, i, 0, 1, 2, QtCore.Qt.AlignLeft) 
                    grid.addWidget(paramui, i, 0, 1, 2, QtCore.Qt.AlignLeft) 
                    
                
            if label is not None:
            if label is not None:
                grid.addWidget(label, i, 0, QtCore.Qt.AlignLeft)
                grid.addWidget(label, i, 0, QtCore.Qt.AlignLeft)
                
            if p.show is True:
            if p.show is True:
                pshow = QtWidgets.QPushButton(">", self)
                pshow = QtWidgets.QPushButton(">", self)
                pshow.released.connect(makeShowLambda(p.name))
                pshow.released.connect(makeShowLambda(p.name))
                grid.addWidget(pshow, i, 2, QtCore.Qt.AlignRight)
                grid.addWidget(pshow, i, 2, QtCore.Qt.AlignRight)
                
                
                if paramui is not None:
                    self.parameters[-1][3] = pshow
                
                    
            
        
        #link checkboxes to other parameters:
        def makeEnableLambda(checkbox, parameter):
            return lambda: parameter.setEnabled(checkbox.isChecked())
        
        for box in checkBoxesToLink:
            linkedName = checkBoxesToLink[box]
            for p in self.parameters:
                if p[1] == linkedName:
                    p[0].setEnabled(box.isChecked())
                    box.stateChanged.connect(makeEnableLambda(box, p[0]))   #the actual control element
                    if p[3] is not None: #the "show" box, if present
                        box.stateChanged.connect(makeEnableLambda(box, p[3]))   
                        p[3].setEnabled(box.isChecked())
        
        
        
        label = QtWidgets.QLabel("Seed radius", self)
        label = QtWidgets.QLabel("Seed radius", self)
        grid.addWidget(label, i+1, 0, QtCore.Qt.AlignLeft)
        grid.addWidget(label, i+1, 0, QtCore.Qt.AlignLeft)
        grid.addWidget(self.seedradiusedit, i+1, 1, QtCore.Qt.AlignLeft)
        grid.addWidget(self.seedradiusedit, i+1, 1, QtCore.Qt.AlignLeft)
@@ -384,7 +414,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
    def saveDetectParams(self, ds=None):
    def saveDetectParams(self, ds=None):
        if ds is not None:
        if ds is not None:
            for param in self.parameters:
            for param in self.parameters:
                if param[1] == 'points':
                if param[1] == 'contrastCurve':
                    print(param[0].value())
                    print(param[0].value())
                try:                # is it a spinbox or the histWidget? Read out the value
                try:                # is it a spinbox or the histWidget? Read out the value
                    ds.detectParams[param[1]] = param[0].value()
                    ds.detectParams[param[1]] = param[0].value()
@@ -535,7 +565,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
        self.saveDetectParams(self.dataset)
        self.saveDetectParams(self.dataset)
        img = self.subimg.copy()
        img = self.subimg.copy()
        kwargs = {}
        kwargs = {}
        for ui, name, valuefunc in self.parameters:
        for ui, name, valuefunc, showbtn in self.parameters:
            kwargs[name] = valuefunc()
            kwargs[name] = valuefunc()
        self.seg.setParameters(**kwargs)
        self.seg.setParameters(**kwargs)
        seedradius = self.seedradiusedit.value()
        seedradius = self.seedradiusedit.value()
@@ -579,6 +609,9 @@ class ParticleDetectionView(QtWidgets.QWidget):
        self.pdetectsub.setEnabled(True)
        self.pdetectsub.setEnabled(True)
        self.pclear.setEnabled(True)
        self.pclear.setEnabled(True)
    
    
    def raiseWarning(self, warning):
        QtWidgets.QMessageBox.critical(self, "Warning", warning)
    
    @QtCore.pyqtSlot()
    @QtCore.pyqtSlot()
    def detectParticles(self):
    def detectParticles(self):
        self.saveDetectParams(self.dataset)
        self.saveDetectParams(self.dataset)
@@ -615,7 +648,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
        if self.dataset is not None:
        if self.dataset is not None:
            seedpoints = self.dataset.seedpoints
            seedpoints = self.dataset.seedpoints
            deletepoints = self.dataset.seeddeletepoints
            deletepoints = self.dataset.seeddeletepoints
        for ui, name, valuefunc in self.parameters:
        for ui, name, valuefunc, showbtn in self.parameters:
            kwargs[name] = valuefunc()
            kwargs[name] = valuefunc()
        seedradius = self.seedradiusedit.value()
        seedradius = self.seedradiusedit.value()
        self.seg.setParameters(**kwargs)
        self.seg.setParameters(**kwargs)
+76 −38
Original line number Original line Diff line number Diff line
@@ -30,7 +30,7 @@ from random import random


class Parameter(object):
class Parameter(object):
    def __init__(self, name, dtype, value=None, minval=None, maxval=None, 
    def __init__(self, name, dtype, value=None, minval=None, maxval=None, 
                 decimals=0, stepsize=1, helptext=None, show=False):
                 decimals=0, stepsize=1, helptext=None, show=False, linkedParameter=None):
        self.name = name
        self.name = name
        self.dtype = dtype
        self.dtype = dtype
        self.value = value
        self.value = value
@@ -39,36 +39,50 @@ class Parameter(object):
        self.stepsize = stepsize
        self.stepsize = stepsize
        self.helptext = helptext
        self.helptext = helptext
        self.show = show
        self.show = show
        self.linkedParameter = linkedParameter


class Segmentation(object):
class Segmentation(object):
    def __init__(self, dataset=None):
    def __init__(self, dataset=None, parent=None):        
        self.cancelcomputation = False
        self.cancelcomputation = False
        if dataset is not None:
        self.parent = parent
            self.detectParams = dataset.detectParams
        self.defaultParams = {'contrastCurve': np.array([[50,0],[100,200],[200,255]]),
        else:
                                 'activateContrastCurve': True,
            self.detectParams = {'points': np.array([[50,0],[100,200],[200,255]]),
                                 'contrastcurve': True,
                                'blurRadius': 9,
                                'blurRadius': 9,
                                'threshold': 0.2,
                                'activateLowThresh': True,
                                'lowThresh': 0.2,
                                'activateUpThresh': False,
                                'upThresh': 0.5,
                                'maxholebrightness': 0.5,
                                'maxholebrightness': 0.5,
                                'erodeconvexdefects': 0,
#                                'erodeconvexdefects': 0,
                                'minparticlearea': 20,
                                'minparticlearea': 20,
                                'minparticledistance': 20,
                                'minparticledistance': 20,
                                'closeBackground': True,
                                'measurefrac': 1,
                                'measurefrac': 1,
                                'compactness': 0.,
                                'compactness': 0.,
                                'seedRad': 3}
                                'seedRad': 3}
        self.initialParameters()
        if dataset is not None:
            self.detectParams = dataset.detectParams
            for key in self.defaultParams:
                if key not in self.detectParams:
                    self.detectParams[key] = self.defaultParams[key]
        else:
            self.detectParams = self.defaultParams        
        self.initializeParameters()
        
        
    def initialParameters(self):
    def initializeParameters(self):
        parlist = [Parameter("points", np.ndarray, self.detectParams['points'], helptext="Curve contrast"),
        parlist = [Parameter("contrastCurve", np.ndarray, self.detectParams['contrastCurve'], helptext="Curve contrast"),
                   Parameter("contrastcurve", np.bool, self.detectParams['contrastcurve'], helptext="Contrast curve", show=True),
                   Parameter("activateContrastCurve", np.bool, self.detectParams['activateContrastCurve'], helptext="activate Contrast curve", show=True, linkedParameter='contrastCurve'),
                   Parameter("blurRadius", int, self.detectParams['blurRadius'], 3, 99, 1, 2, helptext="Blur radius", show=True),
                   Parameter("blurRadius", int, self.detectParams['blurRadius'], 3, 99, 1, 2, helptext="Blur radius", show=True),
                   Parameter("threshold", float, self.detectParams['threshold'], .01, .9, 2, .02, helptext="Basic threshold", show=True),
                   Parameter("activateLowThresh", np.bool, self.detectParams['activateThresh2'], helptext="activate lower threshold", show=False, linkedParameter='lowThresh'),
                   Parameter("lowThresh", float, self.detectParams['lowThresh'], .01, .9, 2, .02, helptext="Lower threshold", show=True),
                   Parameter("activateUpThresh", np.bool, self.detectParams['activateThresh2'], helptext="activate upper threshold", show=False, linkedParameter='upThresh'),
                   Parameter("upThresh", float, self.detectParams['upThresh'], .01, 1.0, 2, .02, helptext="Upper threshold", show=True),
                   Parameter("maxholebrightness", float, self.detectParams['maxholebrightness'], 0, 1, 2, 0.02, helptext="Close holes brighter than..", show = True),
                   Parameter("maxholebrightness", float, self.detectParams['maxholebrightness'], 0, 1, 2, 0.02, helptext="Close holes brighter than..", show = True),
                   Parameter("erodeconvexdefects", int, self.detectParams['erodeconvexdefects'], 0, 20, helptext="Erode convex defects", show=True),
#                   Parameter("erodeconvexdefects", int, self.detectParams['erodeconvexdefects'], 0, 20, helptext="Erode convex defects", show=True),     #TODO: Consider removing it entirely. It is usually not used...
                   Parameter("minparticlearea", int, self.detectParams['minparticlearea'], 10, 1000, 0, 50, helptext="Min. particle pixel area", show=False),
                   Parameter("minparticlearea", int, self.detectParams['minparticlearea'], 10, 1000, 0, 50, helptext="Min. particle pixel area", show=False),
                   Parameter("minparticledistance", int, self.detectParams['minparticledistance'], 10, 1000, 0, 5, helptext="Min. distance between particles", show=False),
                   Parameter("minparticledistance", int, self.detectParams['minparticledistance'], 10, 1000, 0, 5, helptext="Min. distance between particles", show=False),
                   Parameter("measurefrac", float, self.detectParams['measurefrac'], 0, 1, 2, stepsize = 0.05, helptext="measure fraction of particles", show=False),
                   Parameter("measurefrac", float, self.detectParams['measurefrac'], 0, 1, 2, stepsize = 0.05, helptext="measure fraction of particles", show=False),
                   Parameter("closeBackground", np.bool, self.detectParams['closeBackground'], helptext="close holes in sure background", show=False),
                   Parameter("sure_fg", None, helptext="Show sure foreground", show=True),
                   Parameter("sure_fg", None, helptext="Show sure foreground", show=True),
                   Parameter("compactness", float, self.detectParams['compactness'], 0, 1, 2, 0.05, helptext="watershed compactness", show=False),
                   Parameter("compactness", float, self.detectParams['compactness'], 0, 1, 2, 0.05, helptext="watershed compactness", show=False),
                   Parameter("watershed", None, helptext="Show watershed markers", show=True),
                   Parameter("watershed", None, helptext="Show watershed markers", show=True),
@@ -284,15 +298,16 @@ class Segmentation(object):
        # convert to gray image and do histrogram normalization        
        # convert to gray image and do histrogram normalization        
        gray = self.convert2Gray(img)
        gray = self.convert2Gray(img)
        print("gray")
        print("gray")
        if self.contrastcurve:
        
            xi, arr = self.calculateHistFunction(self.points)
        if self.activateContrastCurve:
            xi, arr = self.calculateHistFunction(self.contrastCurve)
            gray = arr[gray]
            gray = arr[gray]
        print("contrast curve")
        print("contrast curve")
        if self.cancelcomputation:
        if self.cancelcomputation:
            return None, None, None
            return None, None, None
            
            
        # return even if inactive!
        # return even if inactive!
        if return_step=="contrastcurve": return gray, 0
        if return_step=="activateContrastCurve": return gray, 0
        
        
        # image blur for noise-reduction
        # image blur for noise-reduction
        blur = cv2.medianBlur(gray, self.blurRadius)
        blur = cv2.medianBlur(gray, self.blurRadius)
@@ -303,12 +318,37 @@ class Segmentation(object):
            return None, None, None
            return None, None, None
        
        
        # thresholding
        # thresholding
        thresh = cv2.threshold(blur, int(255*self.threshold), 255, cv2.THRESH_BINARY)[1]
        if self.activateLowThresh and not self.activateUpThresh:
        if return_step=="threshold": return thresh, 0
            thresh = cv2.threshold(blur, int(255*self.lowThresh), 255, cv2.THRESH_BINARY)[1]
        print("threshold")
            if return_step=="lowThresh": return thresh, 0
            print("lower threshold")
            if self.cancelcomputation:
                return None, None, None
            
        elif self.activateLowThresh and self.activateUpThresh:
            lowerLimit, upperLimit = np.round(self.lowThresh*255), np.round(self.upThresh*255)
            thresh = np.zeros_like(blur)
            thresh[np.where(np.logical_and(blur >= lowerLimit, blur <= upperLimit))] = 255
            
            if return_step=="lowThresh" or return_step=="upThresh": return thresh, 0
            print("between threshold")
            if self.cancelcomputation:
            if self.cancelcomputation:
                return None, None, None
                return None, None, None
            
            
        elif not self.activateLowThresh and self.activateUpThresh:
            thresh = np.zeros_like(blur)
            thresh[np.where(blur <= np.round(self.upThresh*255))] = 255
            if return_step=="upThresh": return thresh, 0
            print("upper threshold")
            if self.cancelcomputation:
                return None, None, None
        else:   #no checkbox checked
            if self.parent is not None:
                self.parent.raiseWarning('No thresholding method selected!\nAborted detection..')
            print('NO THRESHOLDING SELECTED!')
            return None, None, None
            
        
        #close holes darkter than self.max_brightness
        #close holes darkter than self.max_brightness
        self.closeBrightHoles(thresh, blur, self.maxholebrightness)
        self.closeBrightHoles(thresh, blur, self.maxholebrightness)
        print("closed holes")
        print("closed holes")
@@ -317,20 +357,20 @@ class Segmentation(object):
        if self.cancelcomputation:
        if self.cancelcomputation:
            return None, None, None
            return None, None, None
                
                
        if self.erodeconvexdefects>0:
#        if self.erodeconvexdefects>0:     #TODO: Consider removing
            erthresh = self.erodeConvexDefects(thresh, self.erodeconvexdefects)         ##ist erthresh hier eigentlich notwendig? Können wir bei Bedarf nicht einfach "thresh" überschreiben, anstatt noch ein großes Bild in den Speicher zu laden?
#            thresh = self.erodeConvexDefects(thresh, self.erodeconvexdefects)         ##ist erthresh hier eigentlich notwendig? Können wir bei Bedarf nicht einfach "thresh" überschreiben, anstatt noch ein großes Bild in den Speicher zu laden?
        else:
#        else:
            erthresh = thresh
#            erthresh = thresh
        print("erodeconvexdefects")
#        print("erodeconvexdefects")
        if self.cancelcomputation:
#        if self.cancelcomputation:
            return None, None, None
#            return None, None, None
        # return even if inactive!
        # return even if inactive!
        
        
        if return_step=="erodeconvexdefects": 
#        if return_step=="erodeconvexdefects": 
            if self.erodeconvexdefects > 0: return erthresh, 0
#            if self.erodeconvexdefects > 0: return thresh, 0
            else: return thresh, 0
#            else: return thresh, 0
        
        
        dist_transform = cv2.distanceTransform(erthresh, cv2.DIST_L2,5)
        dist_transform = cv2.distanceTransform(thresh, cv2.DIST_L2,5)
        print("distanceTransform")
        print("distanceTransform")
        if self.cancelcomputation:
        if self.cancelcomputation:
            return None, None, None
            return None, None, None
@@ -339,12 +379,10 @@ class Segmentation(object):
        '''the peak_local_max function takes the min distance between peaks. Unfortunately, that means that individual 
        '''the peak_local_max function takes the min distance between peaks. Unfortunately, that means that individual 
        particles smaller than that distance are consequently disregarded. Hence, we need a connectec_components approach'''
        particles smaller than that distance are consequently disregarded. Hence, we need a connectec_components approach'''
        
        
        sure_fg = self.getSureForeground(erthresh, self.minparticledistance, self.minparticlearea)
        sure_fg = self.getSureForeground(thresh, self.minparticledistance, self.minparticlearea)
        
        sure_bg = cv2.dilate(erthresh, np.ones((5, 5)), iterations = 1)
        sure_bg = self.closeHoles(sure_bg)
        
        
        sure_bg = cv2.dilate(thresh, np.ones((5, 5)), iterations = 1)
        sure_bg = cv2.dilate(thresh, np.ones((5, 5)), iterations = 1)
        if self.closeBackground:
            sure_bg = self.closeHoles(sure_bg)
            sure_bg = self.closeHoles(sure_bg)
        
        
        # modify sure_fg and sure_bg with seedpoints and deletepoints
        # modify sure_fg and sure_bg with seedpoints and deletepoints