Commit c438bcca authored by Hackmet's avatar Hackmet
Browse files

Peparation for RenishawCom, inclusion of analysis module

parent bddc18b2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@ Requirements:
  for 64bit as many use cases require a lot of memory (16 GB better 32 GB 
  recommended)
  
* the tsp module in externalmodules can be built with
* the tsp module in external can be built with
  python setuptsp.py
  please note: for this step a valid compiler needs to be installed in the 
  system; Otherwise use the precompiled tsp-module
+108 −0
Original line number Diff line number Diff line
# -*- coding: utf-8 -*-
"""
GEPARD - Gepard-Enabled PARticle Detection
Copyright (C) 2018  Lars Bittrich and Josef Brandt, Leibniz-Institut für 
Polymerforschung Dresden e. V. <bittrich-lars@ipfdd.de>    

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program, see COPYING.  
If not, see <https://www.gnu.org/licenses/>.
"""
from PyQt5 import QtCore, QtWidgets, QtGui
from PIL import ImageFont
import numpy as np


WX, WY = 1024, 200
        
class Legend(QtWidgets.QMdiSubWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        
        self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint|QtCore.Qt.FramelessWindowHint)
        
        self.setFixedSize(200, 800)
        self.wscale = 1
        self.drag = None
        self.items = []     	   #list of items to display (text, color)
        self.tileSize = 12
        self.fontSize = 15
        self.spacer = 10
        
    def mousePressEvent(self, event):
        if event.button()==QtCore.Qt.LeftButton:
            self.drag = event.pos()
            
    def mouseMoveEvent(self, event):
        if self.drag is not None:
            p0 = event.pos()
            self.move(self.mapToParent(p0-self.drag))
            self.parentWidget().update()
        else:
            super().mouseMoveEvent(event)
                        
    def mouseReleaseEvent(self, event):
        self.drag = None
        super().mouseReleaseEvent(event)        

    def paintEvent(self, event):
        numEntries = len(self.items)
        if numEntries > 0:
            
            def getSize(fontsize, text, tileSize, spacer):
#                font = ImageFont.truetype('arial.ttf', fontsize)
#                size = font.getsize(text)
                size = 5*len(text), fontsize+2
                width, height = size[0]*1.5 + tileSize + spacer, numEntries * (tileSize+1*spacer) + 2*spacer
                
                return width, height
            
            
            fontSize, tileSize, spacer = self.fontSize, self.tileSize, self.spacer
            longestEntry = max([i[0] for i in self.items], key=len)
            width, height = getSize(fontSize, longestEntry, tileSize, spacer)
            
            #scale smaller, if necessary
            if height > 1024:
                factor = 1024/height
                #prevent text getting tooo small:
                factor = np.clip(factor, 0.6, 1)        #0.6*15   would be fontSize 9
                
                height, tileSize, fontSize, spacer = height*factor, tileSize*factor, int(fontSize*factor), spacer*factor
                
                width, height = getSize(fontSize, longestEntry, tileSize, spacer)
                
            self.setFixedSize(width, height)
            
            qp = QtGui.QPainter()
            qp.begin(self)
            font = QtGui.QFont()
            font.setPixelSize(fontSize)
            qp.setFont(font)
            
            for index, item in enumerate(self.items):
                #draw Text
                x0 = tileSize+2*spacer
                y0 = index*(fontSize+spacer)
                rect= QtCore.QRectF(x0, y0, width, float(fontSize+spacer))
                qp.drawText(rect, QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter, item[0])
                
                #draw colored Box
                qp.setBrush(item[1])
                x0 = spacer
                y0 = index*(fontSize+spacer) + (fontSize - tileSize)/2 +spacer/2
                qp.drawRect(x0, y0, tileSize, tileSize)
            
            qp.end()
        
        
 No newline at end of file
+953 −0

File added.

Preview size limit exceeded, changes collapsed.

+161 −0
Original line number Diff line number Diff line
# -*- coding: utf-8 -*-
"""
Created on Wed Jan 16 12:43:00 2019

@author: brandt
"""

"""
GEPARD - Gepard-Enabled PARticle Detection
Copyright (C) 2018  Lars Bittrich and Josef Brandt, Leibniz-Institut für 
Polymerforschung Dresden e. V. <bittrich-lars@ipfdd.de>    

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program, see COPYING.  
If not, see <https://www.gnu.org/licenses/>.
"""
import numpy as np
import cv2
#import matplotlib.pyplot as plt

class ParticleEditor(object):
    def __init__(self, parent):
        self.parent = parent    #the assigned analysis widget

    def createSafetyBackup(self):
        self.parent.parent.dataset.saveBackup()

    def combineParticles(self, contourIndices, new_assignment):
        contourIndices = sorted(contourIndices)   #we want to keep the contour with lowest index
        print('selected contours:', contourIndices)
        self.createSafetyBackup()
        #get contours:
        contours = [self.parent.parent.dataset.particlecontours[i] for i in contourIndices]
        cnt = np.vstack(tuple(contours))  #combine contous
        
        #draw contours
        xmin, xmax = cnt[:,0,:][:, 0].min(), cnt[:,0,:][:, 0].max()
        ymin, ymax = cnt[:,0,:][:, 1].min(), cnt[:,0,:][:, 1].max()        
        
        padding = 2    #pixel in each direction
        rangex = int(np.round((xmax-xmin)+2*padding))
        rangey = int(np.round((ymax-ymin)+2*padding))

        for i in range(len(cnt)):
            cnt[i][0][0] -= xmin-padding
            cnt[i][0][1] -= ymin-padding
            
        img = np.zeros((rangey, rangex))
        cv2.drawContours(img, [cnt], 0, 1, -1)
        cv2.drawContours(img, [cnt], 0, 1, 1)
        img = np.uint8(cv2.morphologyEx(img, cv2.MORPH_CLOSE, np.ones((3, 3))))
        
        temp, contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

        newContour = contours[0]
        stats = self.characterizeParticle(newContour)

        for i in range(len(newContour)):
            newContour[i][0][0] += xmin-padding
            newContour[i][0][1] += ymin-padding
        
        
        #check, if dataset contains (already modified) particle2spectra, otherwise create new.
        if self.parent.parent.dataset.particles2spectra is None:  #create default assignment
            print('recreating particles2spectra from within edit particles...')
            sortindices = self.parent.parent.dataset.ramanscansortindex
            self.parent.parent.dataset.particles2spectra = [[int(np.where(sortindices == i)[0])] for i in range(len(sortindices))]
        
        
        #Contour indices are the same as the original particlestats, which are contained in the dataset. 
        #We have to modify that and reload in the analysisview
        #first, overwrite first index with new particlestats
        self.parent.parent.dataset.particlestats[contourIndices[0]] = stats
        
        #now, delete the rest...
        self.parent.parent.dataset.particlestats = [i for ind, i in enumerate(self.parent.parent.dataset.particlestats) if ind not in contourIndices[1:]]
        
        #same with the contours
        self.parent.parent.dataset.particlecontours[contourIndices[0]] = newContour
        self.parent.parent.dataset.particlecontours = [i for ind, i in enumerate(self.parent.parent.dataset.particlecontours) if ind not in contourIndices[1:]]
        
        
        #update particle2spectra_list
        #what is the current particle index??
        specIndices = []
        #other spectra indices:
        for index in contourIndices:
            specIndices.append(self.parent.particles2spectra[index])
        
        #flatten index list (in case, that a nested list was created...)
        specIndices = list(np.unique(np.array(specIndices)))
        for i in specIndices:
            self.parent.spectraResults[i] = new_assignment
            self.parent.hqis[i] = 100   #avoid sorting them out again by hqi-filter...
            print(f'spectrum {i} of particle{contourIndices[0]} is now {new_assignment}')
        
        
        #modify particles2spectra..         
        self.parent.parent.dataset.particles2spectra[contourIndices[0]] = specIndices
        for index in reversed(contourIndices[1:]):
            print('removing index from particles2spectra:', index)
            del self.parent.parent.dataset.particles2spectra[index]

        #save dataset
        self.parent.parent.dataset.save()        
        
        #update contours in sampleview
        self.parent.parent.contouritem.resetContours(self.parent.parent.dataset.particlecontours)
        
        self.parent.loadParticleData()
       
    
    def reassignParticles(self, contourindices, new_assignment):
        self.createSafetyBackup()
        for partIndex in contourindices:
            for specIndex in self.parent.particles2spectra[partIndex]:
                self.parent.currentPolymers[specIndex] = new_assignment
                self.parent.spectraResults[specIndex] = new_assignment
                self.parent.hqis[specIndex] = 100

        self.parent.createHistogramData()
            
    
    def deleteParticles(self):
        self.createSafetyBackup()
        pass
    
    def splitParticles(self):
        self.createSafetyBackup()
        pass
    
    def characterizeParticle(self, contours):
        ##characterize particle
        longellipse, shortellipse = np.nan, np.nan
        
        cnt = contours
        
        if cnt.shape[0] >= 5:       ##at least 5 points required for ellipse fitting...
            ellipse = cv2.fitEllipse(cnt)
            shortellipse, longellipse = ellipse[1]

        rect = cv2.minAreaRect(cnt)
        long, short = rect[1]
        if short>long:
            long, short = short, long
    
        return long, short, longellipse, shortellipse, cv2.contourArea(cnt)
    
    
#if __name__ == '__main__':
#    import 
 No newline at end of file
+347 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu May 31 10:07:45 2018

@author: brandt
"""

import numpy as np
from PyQt5 import QtWidgets
import sys
from os import chdir, getcwd

class LoadWITecResults(QtWidgets.QDialog):
    def __init__(self, parent):
        super(LoadWITecResults, self).__init__()
        self.setGeometry(400, 400, 200, 300)
        self.setWindowTitle('Get Truematch Results')
        self.layout = QtWidgets.QGridLayout()
        self.setLayout(self.layout)

        self.parent = parent
        self.parent.setDisabled(True)
        self.trueMatchResults = None
        self.polymertypes = None
        self.additives = None
        self.hqis = None
        self.addhqis = None
        
        self.btn1 = QtWidgets.QPushButton('LoadTrueMatchResults')
        self.btn1.resize(self.btn1.sizeHint()) 
        self.btn1.clicked.connect(self.loadFileManually)

        optionsLayout = QtWidgets.QFormLayout()
        self.optionsGroup = QtWidgets.QGroupBox('Compute Options')
        self.optionsGroup.setDisabled(True)
        self.btn3 = QtWidgets.QPushButton('Compute')
        self.btn3.clicked.connect(self.runCalculations)
        
        self.label1 = QtWidgets.QLabel('HQI-Threshold:')
        self.spinbox1 = QtWidgets.QDoubleSpinBox(self)
        self.spinbox1.valueChanged.connect(self.updateParentSpinboxes)
        optionsLayout.addRow(self.label1, self.spinbox1)
        
        self.label2 = QtWidgets.QLabel('ComponentThreshold:')
        self.spinbox2 = QtWidgets.QDoubleSpinBox(self)
        self.spinbox2.valueChanged.connect(self.updateParentSpinboxes)
        optionsLayout.addRow(self.label2, self.spinbox2)
        self.label3 = QtWidgets.QLabel('Max Items in Display:')
        self.numDispSpinbox = QtWidgets.QSpinBox(self)
        self.numDispSpinbox.setValue(20)
        self.numDispSpinbox.setMinimum(1)
        self.numDispSpinbox.setMaximum(40)
        self.numDispSpinbox.valueChanged.connect(self.updateParentSpinboxes)
        optionsLayout.addRow(self.label3, self.numDispSpinbox)
        optionsLayout.addRow(self.btn3)
        
        for box in [self.spinbox1, self.spinbox2]:
            box.setValue(50)
            box.setDecimals(1)
            box.setSingleStep(5)
            box.setMinimum(0)
            box.setMaximum(100)
        self.spinbox2.setValue(30)          #more reasonable...
        
        self.optionsGroup.setLayout(optionsLayout)
        
        self.reviewGroup = QtWidgets.QGroupBox('Review Changes')
        self.reviewGroup.setDisabled(True)
        reviewLayout = QtWidgets.QVBoxLayout()
        
        self.otherBtn = QtWidgets.QPushButton('manual overwrite')
        self.otherBtn.clicked.connect(self.show3FlagsReview)
        reviewLayout.addWidget(self.otherBtn)
        reviewLayout.addStretch()
        self.reviewGroup.setLayout(reviewLayout)

        self.layout.addWidget(self.btn1, 0, 0)
        self.layout.addWidget(self.optionsGroup, 1, 0)
        self.layout.addWidget(self.reviewGroup, 2, 0)
        
        self.manualPolymers = {}
        self.manualAdditives = {}
        self.expWindow = None
        self.additivePlot = None
        self.editEntryWindow = None

        self.numFlagForTakeSpectrum = 1
        self.numFlagForSetUnknown = 2
        self.numFlagForPrompt = 3
    
    def updateParentSpinboxes(self):
        self.parent.hqiSpinBox.setValue(self.spinbox1.value())
        self.parent.compHqiSpinBox.setValue(self.spinbox2.value())
        self.parent.dispResultSpinBox.setValue(self.numDispSpinbox.value())
    
    def show3FlagsReview(self):
        self.editEntryWindow = ModifyManualEdits(self, self.manualPolymers, self.manualAdditives)        
        self.editEntryWindow.show()

    def loadFileManually(self):
        dsetpath = self.parent.parent.dataset.path
        fnames =QtWidgets.QFileDialog.getOpenFileNames(self, 'Select TrueMatch result file', dsetpath, 'text file (*.txt)')[0]
        if len(fnames) > 1:
            QtWidgets.QMessageBox.about(self, 'Info', 'The following order of files was loaded. If incorrect, please call a coder!\n{}'.format('\n'.join([fname for fname in fnames])))
        self.trueMatchResults = []
        for fileindex, fname in enumerate(fnames):
            with open(fname) as file:
                if fileindex == 0:
                    for line in file:
                        self.trueMatchResults.append(line)
                else:       ##for additional files skip first line (header..)
                    for lineindex, line in enumerate(file):
                        if lineindex > 0:
                            self.trueMatchResults.append(line)

        self.btn1.setText('Data loaded')
        self.optionsGroup.setDisabled(False)
    
    def formatResults(self, rawResults):   #get rid of header line, first data interpretation
        results = []
        for index,line in enumerate(rawResults):
            if index == 0:
                line = line.strip().split(';')[:-1]         #disregard the last entry of each line, as each line ends with ; <- that produces an empty entry...
                if line[0] != 'Search Spectrum Name':
                    print('incompatible data format')
                    break 
                #detect, whether one- or multicomponent-search was done
                if line[-1] == 'IsMarked':
                    numhits = np.int(line[-2].split(' ')[-1])
                    numcomps = 1
                else:
                    numhits = np.int(line[-1].split(' ')[-1])
                    numcomps = np.int(line[-1].split(' ')[-3])
            else:
                results.append(line)
        
        numspectra = len(results)
        if numspectra > 0:
            print('{} components, {} hits per sample, {} spectra'.format(numcomps, numhits, numspectra))
        
        return results, numspectra, numcomps, numhits
    
    def interpretEntry(self, index, entry, numhits, numcomps):
        entry = entry.split(';')
        polymertype, additive, hqi, addhqi = None, None, None, None   #assign default None

        #find yes-flags
        flags = np.where(np.array(entry) == 'yes')[0]
        if len(flags) == 0:
            #take highest HQI entry
            if numcomps == 1:
                if float(entry[1]) > self.spinbox1.value():
                    polymertype = entry[2]
                    hqi = entry[1]
                else:
                    polymertype = 'unknown'
                    hqi = 0
            else:
                if float(entry[1]) > self.spinbox1.value():
                    polymertype = entry[5]
                    hqi = entry[1]
                    if float(entry[6]) > self.spinbox2.value(): 
                        additive = entry[7]
                        addhqi = entry[6]
                    else: 
                        additive = 'none'
                        addhqi = 0
                        
                else:
                    polymertype = 'unknown'
                    additive = 'none'
                    hqi = 0
                    addhqi = 0
                    
        elif len(flags) == 1:
            #exactly one flag was placed, take this entry
            if numcomps == 1:
                polymertype = entry[int(flags[0])-1]
                hqi = 100
            else:
                polymertype = entry[int(flags[0])+2]
                additive = entry[int(flags[0])+4]
                hqi = 100
                addhqi = 100
            
        elif len(flags) == 2:
            polymertype = 'unknown'
            hqi = 0
            if numcomps > 1:
                additive = 'none'
                addhqi = 0
                
        elif len(flags) == 3:
            
            hqi = 100
            if index not in self.manualPolymers:
                polymertype, ok = QtWidgets.QInputDialog.getText(self, 'Name of main component', 'Spectrum at index {} is:'.format(index))
                self.manualPolymers[index] = polymertype
            else:
                polymertype = self.manualPolymers[index]
            if numcomps > 1:
                addhqi = 100
                if entry[0] not in self.manualAdditives:
                    additive, ok = QtWidgets.QInputDialog.getText(self, 'Name of additive', 'Additive at index {} is:'.format(index))
                    self.manualAdditives[index] = additive
                else:
                    additive = self.manualAdditives[index]
        else: 
            QtWidgets.QMessageBox.about(self, 'Error!', 'No rule for {} flags, found at spectrum index {}'.format(len(flags), index))
            
        if addhqi is None:
            addhqi = 0
        
        return polymertype, additive, float(hqi), float(addhqi)

    def runCalculations(self):        
        self.resultList, numspectra, numcomps, numhits = self.formatResults(self.trueMatchResults)
        self.dispresults = self.numDispSpinbox.value()
        
        self.polymertypes =[]
        self.hqis = []        
        
        if numcomps == 1:               #####SINGLE COMPONENT SEARCH
            self.additives = None
            self.addhqis = None
            self.spinbox2.setEnabled(False)
            for index, entry in enumerate(self.resultList):
                if len(entry) == 0:
                    del self.resultList[index]
                else:
                    polymertype, additive, hqi, addhqi = self.interpretEntry(index, entry, numhits, numcomps)
                    self.polymertypes.append(polymertype)
                    self.hqis.append(hqi)

        else:                   #####MULTI-COMPONENT SEARCH
            self.additives = []
            self.addhqis = []

            for index, entry in enumerate(self.resultList):
                if len(entry) > 0:
                    polymertype, additive, hqi = self.interpretEntry(index, entry, numhits, numcomps)
                    self.polymertypes.append(polymertype)
                    self.hqis.append(hqi)
                    self.additives.append(additive)
                    self.addhqis.append(addhqi)
    
        assert len(self.polymertypes) == len(self.resultList), 'incorrect number of polymer types added...'

        del self.parent.spectraResults, self.parent.additiveResults, self.parent.hqis, self.parent.addhqis
        self.parent.spectraResults = self.polymertypes
        self.parent.additiveResults = self.additives
        self.parent.hqis = self.hqis
        self.parent.addhqis = self.addhqis
        self.parent.formatResults()
        
        if len(self.manualPolymers) > 0:
            self.reviewGroup.setDisabled(False)

    def closeEvent(self, event):
        del self.parent.spectraResults, self.parent.additiveResults, self.parent.hqis, self.parent.addhqis
        self.parent.spectraResults = self.polymertypes
        self.parent.additiveResults = self.additives
        self.parent.hqis = self.hqis
        self.parent.addhqis = self.addhqis
        self.parent.updateBtn.clicked.connect(self.parent.formatResults)
        self.parent.formatResults()
        self.parent.show_hide_labels()
        self.parent.saveAnalysisResults()
        self.parent.setEnabled(True)
        event.accept()


class ModifyManualEdits(QtWidgets.QWidget):
    def __init__(self, parentWindow, polymerEdits, additiveEdits = None):
        super(ModifyManualEdits, self).__init__()
        self.setWindowTitle('Edit Manual Changes')
        self.setGeometry(200, 200, 800, 500)
        layout = QtWidgets.QVBoxLayout()
        self.setLayout(layout)
        
        self.parent = parentWindow
        self.polymerEdits = polymerEdits
        self.additiveEdits = additiveEdits
        self.labels = []
        self.polymerLineEdits = []
        self.additiveLineEdits = []
        
        groupBox = QtWidgets.QGroupBox('Manually entered edits')
        groupLayout = QtWidgets.QGridLayout()

        #create Labels and LineEdits        
        for index in self.polymerEdits:
            self.labels.append(QtWidgets.QLabel('Spectrum index:' + str(index)))
            print(self.labels[-1].text())
            self.polymerLineEdits.append(QtWidgets.QLineEdit())
            self.polymerLineEdits[-1].setText(self.polymerEdits[index])
            if len(self.additiveEdits) > 0:
               self.additiveLineEdits.append(QtWidgets.QLineEdit()) 
               self.additiveLineEdits[-1].setText(self.additiveEdits[index])
        
        for i in range(len(self.labels)):
            groupLayout.addWidget(self.labels[i], i, 0)
            groupLayout.addWidget(self.polymerLineEdits[i], i, 1)
            if len(self.additiveEdits) > 0:
                groupLayout.addWidget(self.additiveLineEdits[i], i, 2)
        
        groupBox.setLayout(groupLayout)
        
        scrollarea = QtWidgets.QScrollArea()
        scrollarea.setWidget(groupBox)
        scrollarea.setMaximumHeight(600)
        layout.addWidget(scrollarea)
        
        saveAndCloseBtn = QtWidgets.QPushButton('Save and Exit')
        saveAndCloseBtn.clicked.connect(self.closeEvent)
        layout.addWidget(saveAndCloseBtn)

    def closeEvent(self, event):
        #read out LineEdits:
        for index, key in enumerate(self.polymerEdits):
            self.polymerEdits[key] = self.polymerLineEdits[index].text()
            if len(self.additiveEdits) > 0:
                self.additiveEdits[key] = self.additiveLineEdits[index].text()
        
        #copy values to parent Window
        self.parent.manualPolymers, self.parent.manualAdditives = self.polymerEdits, self.additiveEdits
        self.parent.runCalculations()
        self.close()


if __name__ == "__main__":

    wd=getcwd()
    chdir(wd)

    try:
        del(app)
    except:
        pass
    
    app = QtWidgets.QApplication(sys.argv)
    mainWin = LoadWITecResults(None)
    mainWin.show()
    app.exec_()
        
    
Loading