Commit eaebe41d authored by Lars Bittrich's avatar Lars Bittrich
Browse files

Merge commit '33010f81'

parents dedfb64d 33010f81
Loading
Loading
Loading
Loading
+25 −5
Original line number Diff line number Diff line
@@ -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
@@ -249,3 +261,11 @@ 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
+96 −34
Original line number Diff line number Diff line
@@ -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":
@@ -371,6 +380,37 @@ 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):
@@ -436,6 +489,12 @@ class ParticleDetectionView(QtWidgets.QWidget):
            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:
            self.lastcenter = self.lastmove
@@ -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")    
    view = ParticleDetectionView(img, ds, None)    
    view.setDataSet(ds)
    view.show()
    app.exec_()
 No newline at end of file
+38 −29
Original line number Diff line number Diff line
@@ -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