Commit 7d28e523 authored by JosefBrandt's avatar JosefBrandt
Browse files

Merge branch 'RefactoringAnalysisModules'

parents 8fc15b1e 582761bd
Loading
Loading
Loading
Loading

.gitignore

0 → 100644
+14 −0
Original line number Diff line number Diff line

__pycache__/

gepard\.cfg

analysis/database_config\.txt

*.so

*.c

external/build/

.idea/
+0 −0

File moved.

gepard.py__main__.py

100755 → 100644
+53 −23
Original line number Diff line number Diff line
@@ -18,18 +18,17 @@ 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 sampleview import SampleView
from scalebar import ScaleBar
from ramancom.ramancontrol import defaultPath
from ramancom.ramanSwitch import RamanSwitch
from colorlegend import ColorLegend
import os
from PyQt5 import QtCore, QtWidgets, QtGui
from .sampleview import SampleView
from .scalebar import ScaleBar
from .ramancom.ramancontrol import defaultPath
from .ramancom.ramanSwitch import RamanSwitch
from .analysis.colorlegend import ColorLegend
            
class MeasureParticleWindow(QtWidgets.QMainWindow):
    
class GEPARDMainWindow(QtWidgets.QMainWindow):
    def __init__(self, logpath):
        super(MeasureParticleWindow, self).__init__()
        super(GEPARDMainWindow, self).__init__()

        self.setWindowTitle("GEPARD")
        self.resize(900, 700)
@@ -101,12 +100,23 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
            fileName = QtWidgets.QFileDialog.getSaveFileName(self, "Create New Project",
                                        defaultPath, "*.pkl")[0]
        if fileName:
            if fileName.find(' ') < 0:
            isValid, msg = self.testFilename(fileName)
            if isValid:
                self.fname = str(fileName)
                self.view.new(self.fname)  
                self.scalingChanged(1.)
            else:
                QtWidgets.QMessageBox.critical(self, "Error", "File path must not contain spaces.")
                QtWidgets.QMessageBox.critical(self, "Error", msg)
            
    @QtCore.pyqtSlot()  
    def testFilename(self, fileName):
        if self.view.ramanctrl.name == 'RenishawCOM':   #the renishawCom does not allow Spaces within filePath
            if fileName.find(' ') == 0:
                return False, "File path must not contain spaces."
            else:
                return True, ""
        else:
            return True, ""
    
    @QtCore.pyqtSlot() 
    def about(self):
@@ -116,7 +126,7 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):

    def createActions(self):
        fname = os.path.join(os.path.split(__file__)[0],
                             os.path.join("data","brand.png"))
                             os.path.join('data', 'brand.png'))
        self.aboutAct = QtWidgets.QAction(QtGui.QIcon(fname),
                                          "About Particle Measurment", self)
        self.aboutAct.triggered.connect(self.about)
@@ -125,7 +135,7 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
        self.openAct.setShortcut("Ctrl+O")
        self.openAct.triggered.connect(self.open)
        
        self.importAct = QtWidgets.QAction("&Import Project...", self)
        self.importAct = QtWidgets.QAction("&Import Zeiss Project...", self)
        self.importAct.setShortcut("Ctrl+I")
        self.importAct.triggered.connect(self.importProject)
        
@@ -194,6 +204,17 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
        if self.view.simulatedRaman:
            self.configRamanCtrlAct.setDisabled(True)
            
        self.noOverlayAct = QtWidgets.QAction("&No Overlay", self)
        self.selOverlayAct = QtWidgets.QAction("&Selected Overlay", self)
        self.fullOverlayAct = QtWidgets.QAction("&Full Overlay", self)
        self.transpAct = QtWidgets.QAction("&Transparent Overlay", self)
        self.hideLabelAct = QtWidgets.QAction('&Hide Spectra Numbers', self)
        self.darkenAct = QtWidgets.QAction("&Darken Image", self)
        self.seedAct = QtWidgets.QAction("&Set Color Seed", self)
        
        for act in [self.noOverlayAct, self.selOverlayAct, self.fullOverlayAct, self.hideLabelAct, self.transpAct, self.darkenAct, self.seedAct]:
            act.setDisabled(True)
            
    def updateModes(self, active=None, maxenabled=None):
        ose, osc, pde, pdc, rse, rsc, pae, pac = [False]*8
        if maxenabled=="OpticalScan":
@@ -274,6 +295,17 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
        self.toolsMenu.addAction(self.snapshotAct)
        self.toolsMenu.addAction(self.configRamanCtrlAct)

        self.dispMenu = QtWidgets.QMenu("&Display", self)
        self.overlayActGroup = QtWidgets.QActionGroup(self.dispMenu)
        self.overlayActGroup.setExclusive(True)
        
        for act in [self.noOverlayAct, self.selOverlayAct, self.fullOverlayAct]:
            self.dispMenu.addAction(act)
            self.overlayActGroup.addAction(act)
        
        self.dispMenu.addSeparator()
        self.dispMenu.addActions([self.transpAct, self.hideLabelAct, self.darkenAct, self.seedAct])
        
        self.helpMenu = QtWidgets.QMenu("&Help", self)
        self.helpMenu.addAction(self.aboutAct)

@@ -281,9 +313,9 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
        self.menuBar().addMenu(self.viewMenu)
        self.menuBar().addMenu(self.modeMenu)
        self.menuBar().addMenu(self.toolsMenu)
        self.menuBar().addMenu(self.dispMenu)
        self.menuBar().addMenu(self.helpMenu)


    def createToolBar(self):
        self.toolbar = QtWidgets.QToolBar("Tools")
        self.toolbar.setIconSize(QtCore.QSize(100,50))
@@ -322,12 +354,10 @@ if __name__ == '__main__':
        fp = open(logname, "a")
        sys.stderr = fp
        sys.stdout = fp
    print("starting GEPARD at: " + strftime("%d %b %Y %H:%M:%S", localtime()), flush=True)
    
    print("starting GEPARD at: " + strftime("%d %b %Y %H:%M:%S", localtime()), 
          flush=True)
    
    meas = MeasureParticleWindow(logpath)
    meas.showMaximized()
    gepard = GEPARDMainWindow(logpath)
    gepard.showMaximized()
    ret = app.exec_()
    if fp is not None:
        fp.close()
+79 −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/>.
"""

import pickle

def loadAssigments(fname):
    with open(fname, "rb") as fp:
        assignment = pickle.load(fp)
    return assignment

def saveAssignments(assignment, fname):
    with open(fname, "wb") as fp:
        pickle.dump(assignment, fp, protocol=-1)


class DBAssignment(object):
    def __init__(self):
        self.filename = None
        self.assignments = []
    
    def setFileName(self, fname):
        self.filename = fname
    
    def save(self):
        saveAssignments(self, self.filename)
        
    def hasAssignment(self, polymerName):
        polymIsPresent = False
        for assignment in self.assignments:
            if assignment.polymerName == polymerName:
                polymIsPresent = True
                break
        return polymIsPresent
    
    def getAssignment(self, polymerName):
        for assignment in self.assignments:
            if assignment.polymerName == polymerName:
                return assignment
    
    def createNewAssignment(self, polymerName):
        self.assignments.append(Assignment(polymerName))
    
    def updateAssignment(self, polymerName, result, catRes, indic_paint):
        for assignment in self.assignments:
            if assignment.polymerName == polymerName:
                assignment.update(result, catRes, indic_paint)
                return


class Assignment(object):
    def __init__(self, polymerName):
        self.polymerName = polymerName
        self.result = None
        self.categorizedResult = None
        self.indication_paint = None
    
    def update(self, result, catRes, indic_paint):
        self.result = result
        self.categorizedResult = catRes
        self.indication_paint = indic_paint
    
+278 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3
# -*- 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 QtWidgets, QtGui, QtCore
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import numpy as np

from .colorlegend import getColorFromNameWithSeed

class SizeHistogramPlot(QtWidgets.QGroupBox):
    def __init__(self, dataset):
        super(SizeHistogramPlot, self).__init__()
        self.dataset = dataset
        
        self.minX = 3
        self.maxX = 1E4
        self.fontsize = 15
        
        layout = QtWidgets.QHBoxLayout()
        self.setLayout(layout)
        self.sizeHistogramCanvas = FigureCanvas(Figure())
        self.sizeHist_ax = self.sizeHistogramCanvas.figure.subplots()
        self.sizeHist_ax.axis('off')
        self.sizeHistogramCanvas.figure.subplots_adjust(left=0.1, top=0.93, bottom=0.15, right=0.995)
        histNavigation = NavigationToolbar(self.sizeHistogramCanvas, self)
        histNavigation.setOrientation(QtCore.Qt.Vertical)
        histNavigation.setFixedWidth(50)
        layout.addWidget(histNavigation)
        layout.addWidget(self.sizeHistogramCanvas)
    
    def drawHistograms(self, listOfHistograms):
        if type(listOfHistograms) != list:
            assert type(listOfHistograms) == SizeHistogramData
            listOfHistograms = [listOfHistograms]
        
        self.resetPlotFormat()
        self.adjustPlotLimits(listOfHistograms)
        
        for histData in listOfHistograms:
            self.drawHistogram(histData)
        
        self.drawLegend()
        self.sizeHistogramCanvas.draw()
        
    def resetPlotFormat(self):
        self.sizeHist_ax.clear()
        self.sizeHist_ax.axis('on')
        self.sizeHist_ax.tick_params(axis='both', which='both', labelsize = self.fontsize)
        self.sizeHist_ax.set_xlabel('Size (µm)', fontsize = self.fontsize)
        self.sizeHist_ax.set_ylabel('Number', fontsize = self.fontsize)
        self.sizeHist_ax.set_title('Size Distribution', fontsize=self.fontsize)
        
    def adjustPlotLimits(self, listOfHistograms):
        xmin, xmax = None, None
        for histData in listOfHistograms:
            if xmin is None:
                xmin = np.clip(10**histData.minSizeExponent, self.minX, self.maxX)
                xmax = np.clip(10**histData.maxSizeExponent, self.minX, self.maxX)
            else:
                currentXMin = np.clip(10**histData.minSizeExponent, self.minX, self.maxX)
                currentXMax = np.clip(10**histData.maxSizeExponent, self.minX, self.maxX)
                xmin = min(xmin, currentXMin)
                xmax = max(xmax, currentXMax)
        
        self.sizeHist_ax.set_xlim(xmin, xmax)
    
    def drawHistogram(self, histData):
        label = histData.label
        x_values = histData.sizeHist_binCenters
        y_values = histData.sizeHist_abundancies
        color = getColorFromNameWithSeed(label, self.dataset.colorSeed, base255=False)
        self.sizeHist_ax.semilogx(x_values, y_values, label=label, color=color)    
        
    def drawLegend(self):
        self.sizeHist_ax.legend(prop = {'size': self.fontsize})


class SizeHistogramData(object):
    def __init__(self, label, listOfSizes):
        self.label = label
        self.listOfSizes = listOfSizes
        
        self.minSizeExponent = 0.1
        self.maxSizeExponent = 3
        self.numBins = 20
        
        self.sizeHist_binCenters = None
        self.sizeHist_abundancies = None
        
        self.createSizeHistogramData()
    
    def createSizeHistogramData(self):
        assert self.listOfSizes is not None
        bins = np.logspace(self.minSizeExponent, self.maxSizeExponent, self.numBins)
        self.sizeHist_abundancies, binEdges = np.histogram(self.listOfSizes, bins)
        self.sizeHist_binCenters = self.getBinCenters(binEdges)
    
    def getBinCenters(self, binEdges):
        binCenters = []
        for i in range(self.numBins-1):
            currentCenter = np.mean((binEdges[i], binEdges[i+1]))
            binCenters.append(currentCenter)
        return binCenters
    
                
class TypeHistogramPlot(QtWidgets.QScrollArea):
    indexClicked = QtCore.pyqtSignal(int)
    def __init__(self, dataset):
        super(TypeHistogramPlot, self).__init__()
        self.dataset = dataset
        
        self.view = QtWidgets.QWidget(self)
        self.view.setCursor(QtGui.QCursor(QtCore.Qt.WhatsThisCursor))
        self.view.setMinimumWidth(250)
        
        group = QtWidgets.QGroupBox('Polymer Type Distribution', self.view)
        self.indicatorbox = QtWidgets.QVBoxLayout()
        self.indicatorbox.setContentsMargins(5,5,5,5)
        group.setLayout(self.indicatorbox)
        
        hbox = QtWidgets.QHBoxLayout()
        hbox.addWidget(group)
        self.view.setLayout(hbox)
        self.setWidgetResizable(True)
        self.setWidget(self.view)
        self.setAlignment(QtCore.Qt.AlignHCenter)
        self.abundancyIndicators = []
        
    def updateTypeHistogram(self, typeHistogram):
        self.resetPlot()
        
        colorList = []
        abundancyList = []
        labelList = []
        for polymType in typeHistogram:
            text = f'{typeHistogram[polymType]} x {polymType}'
            labelList.append(text)
            abundancyList.append(typeHistogram[polymType])
            curColor = getColorFromNameWithSeed(polymType, self.dataset.colorSeed)
            colorList.append(QtGui.QColor(*curColor))
        
        types = list(zip(abundancyList, labelList, colorList))
        
        numtotal= sum(abundancyList)
        
        for index, entry in enumerate(types):
            num, text, color = entry
            indicator = AbundancyIndicator(num, numtotal, color, text)
            self.indicatorbox.addWidget(indicator)
            indicator.clicked.connect(self._getIndexFunction(index))
            self.abundancyIndicators.append(indicator)
            
        self.indicatorbox.addStretch()
        self.view.update()
        
    def resetPlot(self):
        for indicator in self.abundancyIndicators:
            self.indicatorbox.removeWidget(indicator)
            indicator.setParent(None)
            indicator.destroy()
        self.indicatorbox.takeAt(0)
        self.abundancyIndicators = []
    
    def _getIndexFunction(self, index):
            return lambda : self.indexClicked.emit(index)

class AbundancyIndicator(QtWidgets.QPushButton):
    def __init__(self, number, numtotal, color, text, parent=None):
        super().__init__(parent)
        self.number = number
        self.numtotal = numtotal
        self.color = color
        self.text = text
        self.setFixedHeight(30)
        
    def paintEvent(self, event):
        r = self.number/self.numtotal
        width = self.width()
        height = self.height()
        
        qp = QtGui.QPainter()
        qp.begin(self)
        qp.setBrush(QtCore.Qt.white)
        qp.drawRoundedRect(0, 0, width, height, 5. ,5.)
        qp.setPen(self.color)
        qp.setBrush(self.color)
        qp.drawRoundedRect(0, 0, int(width*r), height, 5. ,5.)
        qp.setPen(QtCore.Qt.black)
        qp.setBrush(QtCore.Qt.NoBrush)
        qp.drawRoundedRect(0, 0, width, height, 5. ,5.)
        font = qp.font()
        font.setPointSize(13)
        font.setStyleStrategy(QtGui.QFont.NoAntialias)
        font.setWeight(0)
        qp.setFont(font)
        qp.setCompositionMode(QtGui.QPainter.RasterOp_SourceXorDestination)
        qp.setPen(QtCore.Qt.white)
        qp.drawText(5, 0, width-10, height, QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter, 
                    self.text)
        qp.end()
        

class SpectraPlot(QtWidgets.QGroupBox):
    def __init__(self, dataset):
        super(SpectraPlot, self).__init__()
        self.dataset = dataset
        self.spectra = None
        self.fontsize = 15
        
        layout = QtWidgets.QHBoxLayout()
        self.canvas = FigureCanvas(Figure())
        self.spec_axis = self.canvas.figure.subplots()
        self.reference_axis = self.spec_axis.twinx()
        self.canvas.figure.subplots_adjust(left=0.1, top=0.93, bottom=0.15, right=0.9)
        specNavigation = NavigationToolbar(self.canvas, self)
        specNavigation.setOrientation(QtCore.Qt.Vertical)
        specNavigation.setFixedWidth(50)
        
        layout.addWidget(specNavigation)
        layout.addWidget(self.canvas)
        self.setLayout(layout)
        
    def loadSpectraAndInitializeSpecPlot(self):
        self.spectra = self.dataset.particleContainer.getSpectraFromDisk()
        self.canvas.draw()
        
    def updateParticleSpectrum(self, specIndex, assignment, hqi):
        #draw Sample Spectrum
        self.spec_axis.axis("on")
        self.spec_axis.clear()
        self.spec_axis.tick_params(axis='both', which='both', labelsize = self.fontsize)
        self.spec_axis.set_xlabel('Wavenumber (cm-1)', fontsize = self.fontsize)
        self.spec_axis.set_ylabel('Counts', fontsize = self.fontsize)
        
        if self.spectra is not None:
            specInfo = f'ScanPoint Number {specIndex+1}, with assignment {assignment} (hqi = {hqi})'
            self.spec_axis.plot(self.spectra[:, 0], self.spectra[:, specIndex+1])
            self.spec_axis.set_title(specInfo, fontsize = self.fontsize)
            self.spec_axis.set_xbound(100, (3400 if self.spectra[-1, 0] > 3400 else self.spectra[-1, 0]))
            
            wavenumber_diff = list(self.spectra[:, 0]-100)
            y_start = wavenumber_diff.index(min(wavenumber_diff))
            y_min = min(self.spectra[y_start:, specIndex+1])
            y_max = max(self.spectra[y_start:, specIndex+1])
            self.spec_axis.set_ybound(0.9*y_min, 1.1*y_max)
            
        self.canvas.draw()
    
    def updateReferenceSpectrum(self, ref_wavenumber, ref_intensity):
        #draw Reference
        self.reference_axis.clear()
        self.reference_axis.tick_params(axis='both', which='both', labelsize = self.fontsize)
        self.reference_axis.plot(ref_wavenumber, ref_intensity, color = 'r')
        self.reference_axis.set_ylabel('Ref. Intensity', fontsize = self.fontsize, color = 'r')
        self.reference_axis.tick_params('y', colors = 'r')
        self.reference_axis.set_xbound(100, (3400 if self.spectra[-1, 0] > 3400 else self.spectra[-1, 0]))
        
        self.canvas.draw()
 No newline at end of file
Loading