Commit 4a04d651 authored by Hackmet's avatar Hackmet

Added second threshold in detectionview.py

parent 961b3253
...@@ -275,7 +275,7 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -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): ...@@ -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): ...@@ -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,18 +338,44 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -334,18 +338,44 @@ 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)
...@@ -384,7 +414,7 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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)
......
...@@ -30,7 +30,7 @@ from random import random ...@@ -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): ...@@ -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): ...@@ -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,11 +318,36 @@ class Segmentation(object): ...@@ -303,11 +318,36 @@ 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
if self.cancelcomputation: 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:
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 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)
...@@ -317,20 +357,20 @@ class Segmentation(object): ...@@ -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,13 +379,11 @@ class Segmentation(object): ...@@ -339,13 +379,11 @@ 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)
sure_bg = self.closeHoles(sure_bg) if self.closeBackground:
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
if len(deletepoints)>0: if len(deletepoints)>0:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment