Loading .gitignore +8 −2 Original line number Diff line number Diff line __pycache__/ gepard\.cfg analysis/database_config\.txt *.so *.c *.pyd external/build/ gepard\.cfg .idea/ external/__init__.py→__init__.py +0 −0 File moved. gepard.py→__main__.py100755 → 100644 +53 −23 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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): Loading @@ -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) Loading @@ -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) Loading Loading @@ -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": Loading Loading @@ -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) Loading @@ -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)) Loading Loading @@ -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() analysis/DBAssignments.py 0 → 100755 +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 analysis/analysisplots.py 0 → 100644 +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
.gitignore +8 −2 Original line number Diff line number Diff line __pycache__/ gepard\.cfg analysis/database_config\.txt *.so *.c *.pyd external/build/ gepard\.cfg .idea/
gepard.py→__main__.py100755 → 100644 +53 −23 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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): Loading @@ -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) Loading @@ -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) Loading Loading @@ -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": Loading Loading @@ -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) Loading @@ -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)) Loading Loading @@ -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()
analysis/DBAssignments.py 0 → 100755 +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
analysis/analysisplots.py 0 → 100644 +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