Commit eaebe41d authored by Lars Bittrich's avatar Lars Bittrich

Merge commit '33010f81'

parents dedfb64d 33010f81
......@@ -74,6 +74,18 @@ class DataSet(object):
self.pshift = None # shift of raman scan position relative to image center
self.seedpoints = np.array([])
self.seeddeletepoints = np.array([])
self.detectParams = {'points': np.array([[50,0],[100,200],[200,255]]),
'contrastcurve': True,
'blurRadius': 9,
'threshold': 0.2,
'maxholebrightness': 0.5,
'erodeconvexdefects': 0,
'minparticlearea': 20,
'minparticledistance': 20,
'measurefrac': 1,
'compactness': 0.1,
'seedRad': 3}
self.ramanpoints = []
self.particlecontours = []
self.particlestats = []
......@@ -104,8 +116,8 @@ class DataSet(object):
from opticalscan import loadAndPasteImage
# try to load png and check for detection contours
recreatefullimage = recreatefullimage or not os.path.exists(self.getLegacyImageName())
if not recreatefullimage:
buggyimage = recreatefullimage
if not buggyimage and os.path.exists(self.getLegacyImageName()):
img = cv2imread_fix(self.getLegacyImageName())
Nc = len(self.particlecontours)
if Nc>0:
......@@ -113,12 +125,12 @@ class DataSet(object):
contpixels = img[contour[:,0,1],contour[:,0,0]]
if np.all(contpixels[:,1]==255) and np.all(contpixels[:,2]==0) \
and np.all(contpixels[:,0]==0):
recreatefullimage = True
if not recreatefullimage:
buggyimage = True
if not buggyimage:
cv2imwrite_fix(self.getImageName(), img)
del img
if recreatefullimage:
if buggyimage:
print("recreating fullimage from grid data")
imgdata = None
zvalimg = None
......@@ -248,4 +260,12 @@ class DataSet(object):
def save(self):
saveData(self, self.fname)
if __name__ == '__main__':
dset = loadData(r'C:\Users\brandt\Desktop\20180723DemoTZW\20180723DemoTZW.pkl')
print(dset.detectParams)
# dset.seedpoints = np.array([])
# dset.seeddeletepoints = np.array([])
# dset.save()
\ No newline at end of file
......@@ -32,7 +32,10 @@ class HistWidget(QtWidgets.QWidget):
super().__init__(parent)
self.updateCallbacks(histcallback, curvecallback)
self.points = np.array([[50,0],[100,150],[200,255]])
if parent.dataset is None:
self.points = np.array([[50,0],[100,220],[200,255]])
else:
self.points = parent.dataset.detectParams['points']
self.fig = plt.Figure()
self.canvas = FigureCanvasQTAgg(self.fig)
......@@ -151,12 +154,12 @@ class ImageView(QtWidgets.QLabel):
p0 = np.array([p0])
if len(self.seedpoints)>0:
arr = np.array(self.seedpoints)
d = np.linalg.norm(arr-p0, axis=1)
d = np.linalg.norm(arr[:, :2]-p0, axis=1)
ind = d>self.seedradius
self.seedpoints = arr[ind,:].tolist()
if len(self.seeddeletepoints)>0:
arr = np.array(self.seeddeletepoints)
d = np.linalg.norm(arr-p0, axis=1)
d = np.linalg.norm(arr[:, :2]-p0, axis=1)
ind = d>self.seedradius
self.seeddeletepoints = arr[ind,:].tolist()
......@@ -170,9 +173,9 @@ class ImageView(QtWidgets.QLabel):
self.drag = "add"
p0 = event.pos()
if self.drag=="add":
self.seedpoints.append([p0.x(),p0.y()])
self.seedpoints.append([p0.x(),p0.y(),self.seedradius])
elif self.drag=="delete":
self.seeddeletepoints.append([p0.x(),p0.y()])
self.seeddeletepoints.append([p0.x(),p0.y(),self.seedradius])
else:
self.removeSeeds([p0.x(),p0.y()])
......@@ -183,9 +186,9 @@ class ImageView(QtWidgets.QLabel):
if self.drag:
p0 = event.pos()
if self.drag=="add":
self.seedpoints.append([p0.x(),p0.y()])
self.seedpoints.append([p0.x(),p0.y(),self.seedradius])
elif self.drag=="delete":
self.seeddeletepoints.append([p0.x(),p0.y()])
self.seeddeletepoints.append([p0.x(),p0.y(),self.seedradius])
else:
self.removeSeeds([p0.x(),p0.y()])
self.update()
......@@ -205,6 +208,10 @@ class ImageView(QtWidgets.QLabel):
self.overlay = None
def updateSeedPoints(self, seedpoints=[], seeddeletepoints=[]):
# if len(seedpoints) > 0 and len(self.seedpoints) > 0:
# print(seedpoints[0, :], self.seedpoints[0, :])
# else:
# print('else...', len(seedpoints), len(self.seedpoints))
self.seedpoints = seedpoints
self.seeddeletepoints = seeddeletepoints
......@@ -231,6 +238,7 @@ class ImageView(QtWidgets.QLabel):
self.overlay = pix
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawPixmap(0, 0, self.pixmap())
painter.setOpacity(self.alpha)
......@@ -247,16 +255,16 @@ class ImageView(QtWidgets.QLabel):
painter.drawEllipse(p[0]-2, p[1]-2, 5, 5)
if self.showseedpoints:
r = self.seedradius
painter.setPen(QtCore.Qt.magenta)
painter.setBrush(QtCore.Qt.magenta)
for p in self.seeddeletepoints:
painter.drawEllipse(p[0]-r, p[1]-r, 2*r, 2*r)
painter.setPen(QtCore.Qt.white)
painter.setBrush(QtCore.Qt.white)
for p in self.seedpoints:
painter.drawEllipse(p[0]-r, p[1]-r, 2*r, 2*r)
painter.drawEllipse(p[0]-p[2], p[1]-p[2], 2*p[2], 2*p[2])
painter.setPen(QtCore.Qt.magenta) #I think it is more useful when the deletpoints override the seedpoints
painter.setBrush(QtCore.Qt.magenta)
for p in self.seeddeletepoints:
painter.drawEllipse(p[0]-p[2], p[1]-p[2], 2*p[2], 2*p[2])
class ParticleDetectionView(QtWidgets.QWidget):
imageUpdate = QtCore.pyqtSignal(name='imageUpdate')
......@@ -264,10 +272,10 @@ class ParticleDetectionView(QtWidgets.QWidget):
def __init__(self, img, dataset, parent=None):
super().__init__(parent, QtCore.Qt.Window)
self.dataset = dataset
self.dataset = self.verifySeedpoints(dataset)
self.img = img
self.imgclip = 0,0,0,0
self.seg = Segmentation()
self.seg = Segmentation(self.dataset)
self.thread = None
vbox = QtWidgets.QVBoxLayout()
......@@ -294,6 +302,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
group = QtWidgets.QGroupBox("Detection settings", self)
grid = QtWidgets.QGridLayout()
self.parameters = []
#create editable parameters:
for i, p in enumerate(self.seg.parlist):
label, colstretch = None, 1
if p.name == "points":
......@@ -370,7 +379,38 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.setLayout(hbox)
self.setWindowTitle("Particle Detection")
def saveDetectParams(self, ds=None):
if ds is not None:
for param in self.parameters:
if param[1] == 'points':
print(param[0].value())
try: #is it a spinbox or the histWidget? Read out the value
ds.detectParams[param[1]] = param[0].value()
except: #otherwise checkbox -> take its state
ds.detectParams[param[1]] = param[0].isChecked()
ds.detectParams['seedRad'] = self.seedradiusedit.value()
ds.save()
def verifySeedpoints(self, dataset):
seedpoints = dataset.seedpoints
seeddeletepoints = dataset.seeddeletepoints
if len(seedpoints) > 0: #points are present
if seedpoints.shape[1] == 2: #old entries with only x,y coordinates
radii = np.ones((seedpoints.shape[0], 1))*3
dataset.seedpoints = np.hstack((seedpoints, radii))
else:
dataset.seedpoints = np.array([])
if len(seeddeletepoints) > 0: #points are present
if seeddeletepoints.shape[1] == 2: #old entries with only x,y coordinates
radii = np.ones((seeddeletepoints.shape[0], 1))*3
dataset.seeddeletepoints = np.hstack((seeddeletepoints, radii))
else:
dataset.seeddeletepoints = np.array([])
return dataset
@QtCore.pyqtSlot()
def seedChanged(self):
seedradius = self.seedradiusedit.value()
......@@ -380,20 +420,21 @@ class ParticleDetectionView(QtWidgets.QWidget):
ind &= (arr0[:,1]>n1-seedradius)&(arr0[:,1]<=n2+seedradius)
arr0 = arr0[~ind,:]
else:
arr0 = arr0.reshape(0,2)
arr0 = arr0.reshape(0,3)
if arr1.shape[0]>0:
arr1 += p0
arr1[:,:2] += p0
else:
arr1 = arr1.reshape(0,2)
arr1 = arr1.reshape(0,3)
return np.concatenate((arr0, arr1), axis=0)
if self.dataset is not None:
n1,n2,m1,m2 = self.imgclip
p0 = np.array([[m1,n1]], dtype=np.int32)
self.dataset.seedpoints = clipdata(self.dataset.seedpoints,
np.array(self.imglabel.seedpoints, dtype=np.int32),
p0, n1, n2, m1, m2)
self.dataset.seeddeletepoints = clipdata(self.dataset.seeddeletepoints,
np.array(self.imglabel.seeddeletepoints, dtype=np.int32),
p0, n1, n2, m1, m2)
......@@ -402,23 +443,35 @@ class ParticleDetectionView(QtWidgets.QWidget):
@QtCore.pyqtSlot()
def updateImageSeeds(self):
if self.dataset is not None:
seedradius = self.seedradiusedit.value()
# seedradius = self.seedradiusedit.value()
n1,n2,m1,m2 = self.imgclip
p0 = np.array([[m1,n1]], dtype=np.int32)
seedpoints = []
seeddeletepoints = []
arr1 = self.dataset.seedpoints
if arr1.shape[0]>0:
ind = (arr1[:,0]>m1-seedradius)&(arr1[:,0]<=m2+seedradius)
ind &= (arr1[:,1]>n1-seedradius)&(arr1[:,1]<=n2+seedradius)
if np.any(ind):
seedpoints = (arr1[ind,:]-p0).tolist()
#what seeds are actually in image view?
for point in arr1: #Josef says: I replaced the commented logic with the one right here below, as the old one somehow did not work.... The for-loop might become slow at some point??
if point[0] > (m1-point[2]) and point[0] <= (m2+point[2]) and point[1] > (n1-point[2]) and point[1] <= (n2+point[2]):
seedpoints.append([point[0] - p0[0][0], point[1] - p0[0][1], point[2]])
# if arr1.shape[0]>0:
# ind = (arr1[:,0]>m1-seedradius)&(arr1[:,0]<=m2+seedradius)
# ind &= (arr1[:,1]>n1-seedradius)&(arr1[:,1]<=n2+seedradius)
# if np.any(ind):
# arr1[ind, :2] -= p0
# seedpoints = arr1.tolist()
#
arr2 = self.dataset.seeddeletepoints
if arr2.shape[0]>0:
ind = (arr2[:,0]>m1-seedradius)&(arr2[:,0]<=m2+seedradius)
ind &= (arr2[:,1]>n1-seedradius)&(arr2[:,1]<=n2+seedradius)
if np.any(ind):
seeddeletepoints = (arr2[ind,:]-p0).tolist()
for point in arr2: #Josef says: I replaced the commented logic with the one right here below, as the old one somehow did not work.... The for-loop might become slow at some point??
if point[0] > (m1-point[2]) and point[0] <= (m2+point[2]) and point[1] > (n1-point[2]) and point[1] <= (n2+point[2]):
seeddeletepoints.append([point[0] - p0[0][0], point[1] - p0[0][1], point[2]])
# if arr2.shape[0]>0:
# ind = (arr2[:,0]>m1-seedradius)&(arr2[:,0]<=m2+seedradius)
# ind &= (arr2[:,1]>n1-seedradius)&(arr2[:,1]<=n2+seedradius)
# if np.any(ind):
# arr2[ind, :2] -= p0
# seeddeletepoints = arr2.tolist()
self.imglabel.updateSeedPoints(seedpoints, seeddeletepoints)
def closeEvent(self, event):
......@@ -435,6 +488,12 @@ class ParticleDetectionView(QtWidgets.QWidget):
dp = self.p0-event.pos()
self.lastmove = (self.lastcenter[0]+dp.x(),self.lastcenter[1]+dp.y())
self.setImageCenter(self.lastmove)
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
self.seedradiusedit.setValue(self.seedradiusedit.value()+1)
else:
self.seedradiusedit.setValue(self.seedradiusedit.value()-1)
def mouseReleaseEvent(self, event):
if self.drag:
......@@ -486,6 +545,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.updateImageSeeds()
def detectShow(self, showname):
self.saveDetectParams(self.dataset)
img = self.subimg.copy()
kwargs = {}
for ui, name, valuefunc in self.parameters:
......@@ -534,6 +594,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
@QtCore.pyqtSlot()
def detectParticles(self):
self.saveDetectParams(self.dataset)
if self.thread is not None and self.thread.is_alive():
self.cancelThread()
return
......@@ -596,7 +657,8 @@ if __name__ == "__main__":
from dataset import DataSet
from time import time
fname = r"D:\Projekte\Mikroplastik\Waferreinigung_Kerzenfilter\WaferMilliQH118\fullimage.tif"
# fname = r"D:\Projekte\Mikroplastik\Waferreinigung_Kerzenfilter\WaferMilliQH118\fullimage.tif"
fname = r"C:\Users\brandt\Desktop\20180723DemoTZW\fullimage.tif"
app = QtWidgets.QApplication(sys.argv)
t2 = time()
......@@ -605,8 +667,8 @@ if __name__ == "__main__":
print("OpenCV read:", t3-t2)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
view = ParticleDetectionView(img, None)
ds = DataSet("dummy")
ds = DataSet("dummy")
view = ParticleDetectionView(img, ds, None)
view.setDataSet(ds)
view.show()
app.exec_()
\ No newline at end of file
......@@ -41,22 +41,36 @@ class Parameter(object):
self.show = show
class Segmentation(object):
def __init__(self):
def __init__(self, dataset=None):
self.cancelcomputation = False
if dataset is not None:
self.detectParams = dataset.detectParams
else:
self.detectParams = {'points': np.array([[50,0],[100,200],[200,255]]),
'contrastcurve': True,
'blurRadius': 9,
'threshold': 0.2,
'maxholebrightness': 0.5,
'erodeconvexdefects': 0,
'minparticlearea': 20,
'minparticledistance': 20,
'measurefrac': 1,
'compactness': 0.1,
'seedRad': 3}
self.initialParameters()
def initialParameters(self):
parlist = [Parameter("points", np.ndarray, np.array([[20,0],[50,100],[200,255]]), helptext="Curve contrast"),
Parameter("contrastcurve", np.bool, True, helptext="Contrast curve", show=True),
Parameter("blurRadius", int, 9, 3, 99, 1, 2, helptext="Blur radius", show=True),
Parameter("threshold", float, .2, .01, .9, 2, .02, helptext="Basic threshold", show=True),
Parameter("maxholebrightness", float, 0.5, 0, 1, 2, 0.02, helptext="Close holes brighter than..", show = True),
Parameter("erodeconvexdefects", int, 0, 0, 20, helptext="Erode convex defects", show=True),
Parameter("minparticlearea", int, 20, 10, 1000, 0, 50, helptext="Min. particle pixel area", show=False),
Parameter("minparticledistance", int, 20, 10, 1000, 0, 5, helptext="Min. distance between particles", show=False),
Parameter("measurefrac", float, 1, 0, 1, 2, stepsize = 0.05, helptext="measure fraction of particles", show=False),
parlist = [Parameter("points", np.ndarray, self.detectParams['points'], helptext="Curve contrast"),
Parameter("contrastcurve", np.bool, self.detectParams['contrastcurve'], helptext="Contrast curve", 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("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("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("measurefrac", float, self.detectParams['measurefrac'], 0, 1, 2, stepsize = 0.05, helptext="measure fraction of particles", show=False),
Parameter("sure_fg", None, helptext="Show sure foreground", show=True),
Parameter("compactness", float, 0.1, 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),
]
# make each parameter accessible via self.name
......@@ -279,10 +293,6 @@ class Segmentation(object):
if return_step=="contrastcurve": return gray, 0
# image blur for noise-reduction
if self.blurRadius%2 != 1:
self.blurRadius += 1
print('blur Radius was an even number, incremented blur Radius by 1')
blur = cv2.medianBlur(gray, self.blurRadius)
blur = np.uint8(blur*(255/blur.max()))
if return_step=="blurRadius": return blur, 0
......@@ -330,27 +340,26 @@ class Segmentation(object):
sure_fg = self.getSureForeground(erthresh, self.minparticledistance, self.minparticlearea)
# modify sure_fg with seedpoints and deletepoints
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 = self.closeHoles(sure_bg)
# modify sure_fg and sure_bg with seedpoints and deletepoints
if len(deletepoints)>0:
h, w = sure_fg.shape[:2]
mask = np.zeros((h+2, w+2), np.uint8)
for p in np.int32(deletepoints):
if p[0] >= 0 and p[1] >= 0:
cv2.floodFill(sure_fg, mask, tuple(p), 0)
else:
print('skipped del point at {}'.format(p))
for p in np.int32(deletepoints):
cv2.circle(sure_fg, tuple(p), int(seedradius), 0, -1)
if p[0] > 0 and p[1] > 0:
cv2.floodFill(sure_fg, mask, tuple([p[0], p[1]]), 0)
for p in np.int32(seedpoints):
cv2.circle(sure_fg, tuple(p), int(seedradius), 1, -1)
sure_bg = cv2.dilate(erthresh, np.ones((5, 5)), iterations = 1)
sure_bg = self.closeHoles(sure_bg)
print("sure_fg, sure_bg")
cv2.circle(sure_fg, tuple([p[0], p[1]]), int(p[2]), 1, -1)
for p in np.int32(deletepoints):
cv2.circle(sure_fg, tuple([p[0], p[1]]), int(p[2]), 0, -1)
cv2.circle(sure_bg, tuple([p[0], p[1]]), int(p[2]), 0, -1)
sure_bg = cv2.dilate(thresh, np.ones((5, 5)), iterations = 1)
sure_bg = self.closeHoles(sure_bg)
print("sure_fg, sure_bg")
if self.cancelcomputation:
return None, None, None
......
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