...
 
Commits (5)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
"""
GEPARD - Gepard-Enabled PARticle Detection
Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für
......@@ -20,11 +20,9 @@ along with this program, see COPYING.
If not, see <https://www.gnu.org/licenses/>.
"""
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets
import numpy as np
import sys
import operator
import os
import random
import colorsys
......@@ -32,20 +30,21 @@ from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import pandas as pd
from analysis.loadresults import LoadWITecResults
from analysis.editParticles import ParticleEditor
from analysis.database import DataBaseWindow
from .analysiswidgets import ExpExcelDialog, AdditiveViewer
from .loadresults import LoadWITecResults
from .particleeditor import ParticleEditor
from .database import DataBaseWindow
from .datastats import DataStats
try:
from analysis.sqlexport import SQLExport
from .sqlexport import SQLExport
sqlEnabled = True
except:
sqlEnabled = False
class ParticleAnalysis(QtWidgets.QMainWindow):
def __init__(self, parent):
super(ParticleAnalysis, self).__init__()
def __init__(self, dataset, parent=None):
super(ParticleAnalysis, self).__init__(parent)
self.setGeometry(100, 100, 1680, 1050)
self.setWindowTitle('Results of polymer analysis')
self.layout = QtWidgets.QHBoxLayout()
......@@ -54,29 +53,13 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.setCentralWidget(self.widget)
self.parent = parent
if self.parent is not None:
self.config = self.parent.dataset.resultParams
self.editor = ParticleEditor(self)
self.expWin = None
self.spectraResults = None #entire List of all spectra assignments
self.additiveResults = None #entire List of all additives
self.particlestats = None
self.particleResults = None #final assignment for each particle
self.currentPolymers = None #list of polymers after setting entries with low hqi to unknown
self.currentAdditives = None #same thing for the additives
self.uniquePolymers = None #list of present polymer types
self.spectra = None #acquired spectra
self.indices = None #assignment of what spectra-indices belong to what substance
self.expWindow = None
self.datastats = DataStats(dataset)
self.editor = ParticleEditor(self.datastats, self)
self.additivePlot = None
self.importWindow = None
self.directory = None
self.particles2spectra = None
self.manualPolymers = {}
self.manualAdditives = {}
self.polymerCheckBoxes = []
self.lastSelectedCheckBoxNames = []
......@@ -237,11 +220,9 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.layout.addLayout(self.menuLayout)
self.layout.addLayout(viewLayout)
# update config, if present:
if self.parent is not None:
if self.config['minHQI'] is not None:
self.hqiSpinBox.setValue(self.config['minHQI'])
self.compHqiSpinBox.setValue(self.config['compHQI'])
if self.datastats.config['minHQI'] is not None:
self.hqiSpinBox.setValue(self.datastats.config['minHQI'])
self.compHqiSpinBox.setValue(self.datastats.config['compHQI'])
self.createActions()
self.createMenus()
......@@ -329,97 +310,27 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.refSelector.addItem('')
self.refSelector.addItems(self.dbWin.activeDatabase.spectraNames)
self.refSelector.setDisabled(False)
def loadSpectra(self, fname):
import time
t0 = time.time()
specfilename = self.parent.dataset.fname.split('.pkl')[0] + '_spectra.npy'
if os.path.exists(specfilename):
return np.load(specfilename)
else:
try:
specs = np.loadtxt(fname)
#if spectra are already in correct format (WITec, first column: wavenumbers, other columns, intensities),
#we take them, otherwise we have to convert from Renishaw export format...
if not len(np.unique(specs[:, 0])) == len(specs[:, 0]): #--> only unique numbers -> this is the wavenumber column, we have the witec format
#Renishaw Convert
#columns 0 and 1 are x and y coordinates. We dont need them...
startWavenumber = specs[0, 2]
startIndices = np.where(specs[:, 2] == startWavenumber)[0]
spectra = np.zeros((startIndices[1], len(startIndices)+1)) #create array with shape (numWavenumbers, numSpectra+1) (first column holds wavenumbers)
spectra[:, 0] = specs[startIndices[0]:startIndices[1], 2]
for i in range(len(startIndices)-1):
spectra[:, i+1] = specs[startIndices[i]:startIndices[i+1], 3]
#aaand the last spectrum:
spectra[:, -1] = specs[startIndices[-1]:, 3]
specs = np.flip(spectra, 0) #Renishaw goes from highest to lowest wavenumber, out of whatever reason...
#write spectra to binary file, that makes reloading them in future significantly faster
np.save(specfilename, specs)
print('loading specs:', time.time()-t0)
return specs
except:
return None
def updateData(self):
print('updating data from', self.parent.dataset.name)
self.spectraResults = self.parent.dataset.results['polymers']
self.additiveResults = self.parent.dataset.results['additives']
self.hqis = self.parent.dataset.results['hqis']
self.addhqis = self.parent.dataset.results['additive_hqis']
self.colorSeed = self.parent.dataset.colorSeed
if type(self.colorSeed) != str:
self.colorSeed = 'default'
#load Spectra
if self.parent.dataset.spectraPath is None:
fname = os.path.join(self.parent.dataset.path, self.parent.dataset.name + '_000_Spec.Data 1.txt')
else:
fname = self.parent.dataset.spectraPath
self.spectra = self.loadSpectra(fname)
if self.spectra is None:
fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Select Spectra File', self.parent.dataset.path, 'text file (*.txt)')[0]
self.spectra = self.loadSpectra(fname)
if self.spectra is None:
spectra = self.datastats.update()
if spectra is None:
fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Select Spectra File', self.datastats.dataset.path, 'text file (*.txt)')[0]
spectra = self.datastats.loadSpectra(fname)
if spectra is None:
QtWidgets.QMessageBox.critical(self, 'ERROR!', 'spectra file could not be opened with np.loadtxt...')
return
self.parent.dataset.spectraPath = fname
self.specCanvas.draw()
self.loadParticleData()
def loadParticleData(self):
self.particlestats = np.array(self.parent.dataset.particlestats)
pixelscale = (self.parent.dataset.pixelscale_df if self.parent.dataset.imagescanMode == 'df' else self.parent.dataset.pixelscale_bf)
#convert to mikrometer scale
for index in range(len(self.particlestats)):
for subindex in range(5):
self.particlestats[index][subindex] = self.particlestats[index][subindex] * pixelscale #multiply by pixelscale
if subindex == 4:
self.particlestats[index][subindex] = self.particlestats[index][subindex] * pixelscale #again for the area...
self.particles2spectra = self.parent.dataset.particles2spectra
sortindices = self.parent.dataset.ramanscansortindex
if self.particles2spectra is None:
print('creating default particles2spectra list')
#no assignment found, so we assume one measurement per particle and use ramanscansortindex for assignment
self.particles2spectra = [[int(np.where(sortindices == i)[0])] for i in range(len(sortindices))]
#check, if dataset already contains results. Otherwise load them...
if self.spectraResults is None or (len(self.spectraResults) != len(sortindices)):
if not self.datastats.loadParticleData():
self.show()
answer = QtWidgets.QMessageBox.question(self, 'Warning', 'No (or inconsistent) spectra results found, please run import dialog.\nPress OK to import or cancel to set to empty.', QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
if answer == QtWidgets.QMessageBox.Ok:
self.importTrueMatchResults()
elif answer == QtWidgets.QMessageBox.Cancel:
self.spectraResults = ['empty']*(self.spectra.shape[1]-1)
self.hqis = [100]*(self.spectra.shape[1]-1)
self.datastats.invalidateSpectra()
self.updateBtn.clicked.connect(self.formatResults)
self.formatResults()
else:
......@@ -427,79 +338,36 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.formatResults()
def importTrueMatchResults(self):
self.importWindow = LoadWITecResults(self)
self.importWindow.show()
self.importWindow = LoadWITecResults(self.datastats, self)
self.importWindow.exec()
def getAdditivePlot(self, event):
clickedindex = int(np.round(event.xdata))
polymer = self.typehistogram[clickedindex][0] #get the polymer name, that was clicked on
polymer = self.datastats.typehistogram[clickedindex][0] #get the polymer name, that was clicked on
if len(self.sorted_additives[clickedindex]) > 0:
self.additivePlot = AdditiveViewer(polymer, self.sorted_additives[clickedindex])
if len(self.datastats.sorted_additives[clickedindex]) > 0:
self.additivePlot = AdditiveViewer(polymer, self.datastats.sorted_additives[clickedindex])
self.additivePlot.show()
@QtCore.pyqtSlot()
def formatResults(self):
if self.spectraResults is not None:
if self.datastats.spectraResults is not None:
print('formatResults')
self.updateBtn.setDisabled(False)
self.optionsGroup.setDisabled(False)
del self.currentPolymers, self.currentAdditives
#convert to arrays (makes indexing easier...)
self.currentPolymers, self.hqis = np.array(self.spectraResults), np.array(self.hqis)
self.datastats.formatResults(self.hqiSpinBox.value(), self.compHqiSpinBox.value())
if self.additiveResults is not None:
self.currentAdditives, self.addhqis = np.array(self.additiveResults), np.array(self.addhqis)
if self.datastats.additiveResults is not None:
self.compHqiSpinBox.setDisabled(False)
else:
self.currentAdditives = None
#set poor HQI results to unknown
self.currentPolymers[self.hqis < self.hqiSpinBox.value()] = 'unknown'
if self.currentAdditives is not None:
self.currentAdditives[self.addhqis < self.compHqiSpinBox.value()] = 'unknown'
self.createHistogramData()
def createHistogramData(self):
self.uniquePolymers = np.unique(self.currentPolymers)
self.particleResults = [None]*len(self.particlestats)
self.typehistogram = {i: 0 for i in self.uniquePolymers}
if len(self.particles2spectra) != len(self.particlestats):
if not self.datastats.createHistogramData():
QtWidgets.QMessageBox.critical(self, 'Error', 'Inconsistent particle data. Please restore backup!')
return
for particleID, specList in enumerate(self.particles2spectra):
assignment = self.currentPolymers[specList[0]] #we take the first result as particle result. Hence, all spectra per particle have to have the same result
self.particleResults[particleID] = assignment
self.typehistogram[assignment] += 1
self.particleResults = np.array(self.particleResults)
##sort typehistogram, it will be converted into a list!!
self.typehistogram = sorted(self.typehistogram.items(), key = operator.itemgetter(1), reverse = True)
self.uniquePolymers = [i[0] for i in self.typehistogram]
self.indices = [] #what particles belong to which polymer type?
for polymer in self.uniquePolymers:
self.indices.append(list(np.where(self.particleResults == polymer)[0]))
###generate additive array for each type in typehistogram:
if self.currentAdditives is None:
self.sorted_additives = None
else:
self.sorted_additives = []
for polymer in self.typehistogram: #get additives of each polymer type
self.sorted_additives.append(self.currentAdditives[np.where(self.currentPolymers == polymer[0])])
for i in range(len(self.sorted_additives)): #sort out 'none' entries
nonentries = np.where(self.sorted_additives[i] == 'none')
self.sorted_additives[i] = np.delete(self.sorted_additives[i], nonentries)
###Handle Checkboxes for all polymers...
self.menuLayout.removeWidget(self.resultScrollarea)
for i in [self.resultCheckBoxes, self.resultCheckBoxesLayout, self.resultScrollarea, self.layout_SArea]:
......@@ -531,7 +399,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.resultCheckBoxesLayout.addWidget(self.showTotalSelector)
#generate new checkboxes
self.polymerCheckBoxes = []
for index, polymer in enumerate(self.uniquePolymers):
for index, polymer in enumerate(self.datastats.uniquePolymers):
self.polymerCheckBoxes.append(QtWidgets.QCheckBox(self))
self.polymerCheckBoxes[index].setText(polymer)
self.resultCheckBoxesLayout.addWidget(self.polymerCheckBoxes[index])
......@@ -548,7 +416,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.menuLayout.addWidget(self.resultScrollarea)
if self.currentAdditives is not None:
if self.datastats.currentAdditives is not None:
self.typeHistogramCanvas.setCursor(QtGui.QCursor(QtCore.Qt.WhatsThisCursor))
self.typeHistogramCanvas.mpl_connect('button_press_event', self.getAdditivePlot)
......@@ -559,7 +427,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.navigationGroup.setEnabled(True)
self.polymerComboBox.currentIndexChanged.disconnect()
self.polymerComboBox.clear()
self.polymerComboBox.addItems(self.uniquePolymers)
self.polymerComboBox.addItems(self.datastats.uniquePolymers)
self.polymerComboBox.currentIndexChanged.connect(self.displayNewPolymerType)
self.polymerIndex = self.polymerComboBox.currentIndex()
......@@ -573,34 +441,31 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.createPolymerOverlay()
def exportToExcel(self):
if self.expWin is not None:
self.expWin.close()
self.expWin.destroy()
self.expWin = ExpExcelDialog(self)
self.expWin.show()
expWin = ExpExcelDialog(self.datastats, self)
expWin.exec()
def exportToSQL(self):
if self.expWin is not None:
self.expWin.close()
self.expWin.destroy()
self.expWin = SQLExport(self)
self.expWin.show()
sqlexp = SQLExport(self.datastats, self)
sqlexp.exec()
def updateSpecPlot(self, centerOn=True, highlightContour=True):
#draw Sample Spectrum
specIndex = self.currentSpectrumIndex
spectra = self.datastats.spectra
self.spec_ax.axis("on")
self.spec_ax.clear()
self.spec_ax.plot(self.spectra[:, 0], self.spectra[:, specIndex+1])
self.spec_ax.plot(spectra[:, 0], spectra[:, specIndex+1])
self.spec_ax.tick_params(axis='both', which='both', labelsize=15)
self.spec_ax.set_xlabel('Wavenumber (cm-1)', fontsize = 15)
self.spec_ax.set_ylabel('Counts', fontsize = 15)
self.spec_ax.set_title('ScanPoint Number {}, Size = {} µm'.format(specIndex+1, np.round(self.particlestats[self.currentParticleIndex][2], 1)))
self.spec_ax.set_xbound(100, (3400 if self.spectra[-1, 0] > 3400 else self.spectra[-1, 0]))
wavenumber_diff = list(self.spectra[:, 0]-100)
self.spec_ax.set_title('ScanPoint Number {}, Size = {} µm'.format(specIndex+1,
np.round(self.datastats.particlestats[self.currentParticleIndex][2], 1)))
self.spec_ax.set_xbound(100, (3400 if spectra[-1, 0] > 3400 else spectra[-1, 0]))
wavenumber_diff = list(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])
y_min = min(spectra[y_start:, specIndex+1])
y_max = max(spectra[y_start:, specIndex+1])
self.spec_ax.set_ybound(0.9*y_min, 1.1*y_max)
#draw Reference
......@@ -612,7 +477,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.ref_ax.plot(ref[:, 0], ref[:, 1], color = 'r')
self.ref_ax.set_ylabel('Ref. Intensity', fontsize = 15, color = 'r')
self.ref_ax.tick_params('y', colors = 'r')
self.ref_ax.set_xbound(100, (3400 if self.spectra[-1, 0] > 3400 else self.spectra[-1, 0]))
self.ref_ax.set_xbound(100, (3400 if spectra[-1, 0] > 3400 else spectra[-1, 0]))
# wavenumber_diff = list(ref[:, 0]-100)
# y_start = wavenumber_diff.index(min(wavenumber_diff))
# y_min = min(ref[y_start:, specIndex+1])
......@@ -627,33 +492,33 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
def displayNewPolymerType(self, resetCurrentIndex=True):
self.polymerIndex = self.polymerComboBox.currentIndex()
self.particleSelector.setMaximum(len(self.indices[self.polymerIndex]))
self.particleSelector.setMaximum(len(self.datastats.indices[self.polymerIndex]))
if resetCurrentIndex:
self.particleSelector.setValue(1)
self.spectrumSelector.setValue(1)
self.spectrumSelector.setMaximum(len(self.particles2spectra[self.currentParticleIndex]))
self.currentParticleIndex = self.indices[self.polymerIndex][self.particleSelector.value()-1]
self.currentSpectrumIndex = self.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
self.spectrumSelector.setMaximum(len(self.datastats.particles2spectra[self.currentParticleIndex]))
self.currentParticleIndex = self.datastats.indices[self.polymerIndex][self.particleSelector.value()-1]
self.currentSpectrumIndex = self.datastats.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
self.updateSpecPlot(centerOn=True)
else:
self.currentParticleIndex = self.indices[self.polymerIndex][self.particleSelector.value()-1]
self.currentSpectrumIndex = self.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
self.currentParticleIndex = self.datastats.indices[self.polymerIndex][self.particleSelector.value()-1]
self.currentSpectrumIndex = self.datastats.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
self.updateSpecPlot(centerOn=False)
def selectParticle(self, resetSpectrumCount=True):
if self.particles2spectra is not None:
self.currentParticleIndex = self.indices[self.polymerIndex][self.particleSelector.value()-1]
self.spectrumSelector.setMaximum(len(self.particles2spectra[self.currentParticleIndex]))
if self.datastats.particles2spectra is not None:
self.currentParticleIndex = self.datastats.indices[self.polymerIndex][self.particleSelector.value()-1]
self.spectrumSelector.setMaximum(len(self.datastats.particles2spectra[self.currentParticleIndex]))
if resetSpectrumCount:
self.spectrumSelector.setValue(1)
self.currentSpectrumIndex = self.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
self.currentSpectrumIndex = self.datastats.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
self.updateSpecPlot()
else:
print('no spectrum assignment found...')
def selectSpectrum(self):
if self.particles2spectra is not None:
self.currentSpectrumIndex = self.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
if self.datastats.particles2spectra is not None:
self.currentSpectrumIndex = self.datastats.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
self.updateSpecPlot()
def updateHistogram(self):
......@@ -669,13 +534,13 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
abundancyList = []
for index, checkbox in enumerate(self.polymerCheckBoxes):
if checkbox.isChecked():
abundancyList.append(self.typehistogram[index][1])
curColor = self.getColorFromName(self.typehistogram[index][0], base255 = False)
abundancyList.append(self.datastats.typehistogram[index][1])
curColor = self.getColorFromName(self.datastats.typehistogram[index][0], base255 = False)
colorList.append(curColor)
else:
abundancyList = [i[1] for i in self.typehistogram]
for polymer in self.typehistogram:
abundancyList = [i[1] for i in self.datastats.typehistogram]
for polymer in self.datastats.typehistogram:
curColor = self.getColorFromName(polymer[0], base255 = False)
colorList.append(curColor)
......@@ -686,18 +551,18 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
###add text labels
self.histPlotTextLabels = []
y_label_position = 0
for index, i in enumerate(self.typehistogram):
for index, i in enumerate(self.datastats.typehistogram):
if not self.selOverlayAct.isChecked() or self.polymerCheckBoxes[index].isChecked():
if self.sorted_additives is None:
if self.datastats.sorted_additives is None:
numads = ''
else:
numads = len(np.unique(self.sorted_additives[index]))
numads = len(np.unique(self.datastats.sorted_additives[index]))
if numads == 0:
numads = ''
else:
numads = '(' + str(numads) + ')'
numpolymers = i[1]
label = ('{} x ' + self.typehistogram[index][0] + ' {}').format(numpolymers, numads)
label = ('{} x ' + self.datastats.typehistogram[index][0] + ' {}').format(numpolymers, numads)
x_label_position = self.typeHist_ax.get_xlim()[1]*0.05
self.histPlotTextLabels.append(self.typeHist_ax.text(x_label_position, y_label_position, label, fontsize = 15, rotation = 0, verticalalignment = 'bottom'))
y_label_position += 1
......@@ -716,7 +581,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.typeHist_ax.set_ylabel('Polymer Type', fontsize = 15)
self.typeHist_ax.set_xlabel('Number', fontsize = 15)
if len(self.typehistogram) > self.dispResultSpinBox.value():
if len(self.datastats.typehistogram) > self.dispResultSpinBox.value():
def wheelScroll(event):
step = -0.05*event.step*self.dispResultSpinBox.value()
......@@ -739,7 +604,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
#general size histogram
self.bins = np.logspace(0.1, 3, 20)
self.sizes = [i[0] if np.isnan(i[2]) else i[2] for i in self.particlestats] #extract long size (if ellipse fit is nan -> box fit)
self.sizes = [i[0] if np.isnan(i[2]) else i[2] for i in self.datastats.particlestats] #extract long size (if ellipse fit is nan -> box fit)
sizehist = np.histogram(self.sizes, self.bins)
self.totalhistx = []
for i in range(19):
......@@ -762,7 +627,8 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
selected.append(i.text())
for i in selected:
sizes = [self.sizes[index] for index in range(len(self.sizes)) if self.currentPolymers[index] == i]
sizes = [self.sizes[index] for index in range(len(self.sizes))
if self.datastats.currentPolymers[index] == i]
sizehist = np.histogram(sizes, self.bins)
self.sizeHist_ax.semilogx(self.totalhistx, sizehist[0], label = i, color = self.getColorFromName(i, base255 = False))
......@@ -787,19 +653,19 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.parent.item.setOpacity(1)
def updateColorSeed(self):
text, ok = QtWidgets.QInputDialog.getText(self, 'Color Seed', 'Enter New Seed here', text=self.colorSeed)
text, ok = QtWidgets.QInputDialog.getText(self, 'Color Seed', 'Enter New Seed here', text=self.datastats.colorSeed)
if ok:
self.colorSeed = text
self.parent.dataset.colorSeed = text
self.datastats.colorSeed = text
self.datastats.dataset.colorSeed = text
self.updateHistogram()
self.createPolymerOverlay()
def getColorFromName(self, name, base255=True):
random.seed(self.colorSeed + name)
random.seed(self.datastats.colorSeed + name)
hue = random.random()
random.seed((self.colorSeed + name)*2)
random.seed((self.datastats.colorSeed + name)*2)
saturation = random.random()/4 + 0.75 #i.e., between 0.75 and 1
random.seed((self.colorSeed + name)*3)
random.seed((self.datastats.colorSeed + name)*3)
value = random.random()/5 + 0.8 #i.e., between 0.8 and 1
color = colorsys.hsv_to_rgb(hue, saturation, value)
if base255:
......@@ -810,34 +676,32 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
return color
def createPolymerOverlay(self):
if not self.noOverlayAct.isChecked() and self.indices is not None:
if len(self.indices) > 0:
if not self.noOverlayAct.isChecked() and self.datastats.indices is not None:
if len(self.datastats.indices) > 0:
alpha = (128 if self.transpAct.isChecked() else 255)
#get colors for each polymer type
colorList = [QtGui.QColor(255, 255, 255, alpha=50)]*len(self.particleResults)
colorList = [QtGui.QColor(255, 255, 255, alpha=50)]*len(self.datastats.particleResults)
legendItems = []
for index, indexList in enumerate(self.indices):
for index, indexList in enumerate(self.datastats.indices):
if self.fullOverlayAct.isChecked() or (self.selOverlayAct.isChecked() and self.polymerCheckBoxes[index].isChecked()):
color = self.getColorFromName(self.uniquePolymers[index], base255=True)
color = self.getColorFromName(self.datastats.uniquePolymers[index], base255=True)
color = QtGui.QColor(color[0], color[1], color[2], alpha=alpha)
legendItems.append((self.uniquePolymers[index], color))
legendItems.append((self.datastats.uniquePolymers[index], color))
for i in indexList:
colorList[i] = color
self.parent.contouritem.colorList = colorList
self.parent.contouritem.update()
self.parent.imparent.legend.items = legendItems
self.parent.imparent.legend.update()
self.parent.imparent.legend.setTextColorItems(legendItems)
self.parent.imparent.legend.show()
else:
self.parent.contouritem.colorList = []
self.parent.contouritem.update()
self.parent.imparent.legend.items = []
self.parent.imparent.legend.update()
self.parent.imparent.legend.setTextColorItems([])
self.parent.imparent.legend.hide()
def show_hide_labels(self):
......@@ -845,227 +709,22 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
for scanIndicator in self.parent.ramanscanitems:
scanIndicator.hidden = hidden
scanIndicator.update()
def saveAnalysisResults(self):
self.parent.dataset.results = {'polymers': self.spectraResults,
'hqis': self.hqis,
'additives': self.additiveResults,
'additive_hqis': self.addhqis}
self.parent.dataset.resultParams = {'minHQI': self.hqiSpinBox.value(),
'compHQI': self.compHqiSpinBox.value()}
self.parent.dataset.save()
print('saved dataset')
def closeEvent(self, event):
for window in [self.expWindow, self.additivePlot, self.importWindow, self.dbWin]:
for window in [self.additivePlot, self.importWindow, self.dbWin]:
try: window.close()
except: pass
self.parent.imparent.particelAnalysisAct.setChecked(False)
event.accept()
class ExpExcelDialog(QtWidgets.QWidget):
def __init__(self, parent):
super(ExpExcelDialog, self).__init__()
self.setWindowTitle('Export Options')
self.setGeometry(200,200, 300, 300)
self.parent = parent
self.particles = self.parent.particlestats
self.polymers = self.parent.particleResults
self.additives = self.parent.currentAdditives
self.hqis = self.parent.hqis
self.layout = QtWidgets.QHBoxLayout()
self.setLayout(self.layout)
excelvbox = QtWidgets.QVBoxLayout()
excelvbox.addWidget(QtWidgets.QLabel('Select Parameters for Export'))
excelgroup = QtWidgets.QGroupBox("Export to Excel", self)
self.exportOptions = ['Polymer Type (mandatory)', 'Additives', 'Long Size (µm)', 'Short Size (µm)', 'Area (µm²)', 'HQI', 'Size Classes']
self.checkBoxes = []
self.sizeClasses = [5, 10, 20, 50, 100, 1e6]
self.directory = self.parent.parent.dataset.path
for index, option in enumerate(self.exportOptions):
self.checkBoxes.append(QtWidgets.QCheckBox(self))
self.checkBoxes[-1].setText(option)
self.checkBoxes[-1].setChecked(True)
if option == 'Polymer Type (mandatory)':
self.checkBoxes[-1].setEnabled(False) #is mandatory!!!
if option == 'Additives':
if self.additives is None:
self.checkBoxes[-1].setEnabled(False)
self.checkBoxes[-1].setChecked(False)
excelvbox.addWidget(self.checkBoxes[-1])
self.xlsFileName = QtWidgets.QLineEdit()
self.xlsFileName.setText('{}_Particle_List'.format(self.parent.parent.dataset.name))
excelvbox.addWidget(QtWidgets.QLabel('Filename:'))
excelvbox.addWidget(self.xlsFileName)
self.exlbtn = QtWidgets.QPushButton('Export to Excel')
self.exlbtn.resize(self.exlbtn.sizeHint())
self.exlbtn.clicked.connect(self.toExcel)
excelvbox.addWidget(self.exlbtn)
excelgroup.setLayout(excelvbox)
self.layout.addWidget(excelgroup)
# sqlGroup = QtWidgets.QGroupBox('Export to SQL')
# sqlLayout = QtWidgets.QVBoxLayout()
#
# self.sqlbtn = QtWidgets.QPushButton('Export to SQL Database')
# self.sqlbtn.resize(self.sqlbtn.sizeHint())
# if sqlEnabled:
# self.sqlbtn.clicked.connect(self.toSQL)
# else:
# self.sqlbtn.setDisabled(True)
#
# self.sqlExport = None
#
# sqlLayout.addWidget(self.sqlbtn)
# sqlGroup.setLayout(sqlLayout)
#
# self.layout.addWidget(sqlGroup)
self.show()
def toExcel(self):
requiredcolumns = []
self.sizes = np.round(np.array([i[0] if np.isnan(i[2]) else i[2] for i in self.particles]), 1)
for box in self.checkBoxes:
if box.isChecked() == True:
if box.text() != 'Size Classes':
requiredcolumns.append(box.text())
if box.text() == 'Long Size (µm)':
longSize = self.sizes
elif box.text() == 'Short Size (µm)':
shortSize = np.round(np.array([i[1] if np.isnan(i[3]) else i[3] for i in self.particles]), 1)
elif box.text() == 'Area (µm²)':
area = np.array([np.round(float(entry[4]), 1) for entry in self.particles])
else:
requiredcolumns.append('0 - 5 µm')
requiredcolumns.append('5 - 10 µm')
requiredcolumns.append('10 - 20 µm')
requiredcolumns.append('20 - 50 µm')
requiredcolumns.append('50 - 100 µm')
requiredcolumns.append('> 100 µm')
self.finalData = np.array(['nothing to see here, iam a boring placeholder']*((self.polymers.shape[0]+1)*len(requiredcolumns))).reshape(((self.polymers.shape[0]+1), len(requiredcolumns)))
#create header:
self.finalData[0, :] = np.array(requiredcolumns)
self.finalData[0, 0] = 'Polymer Type'
rowindex = 1
for polymer in np.unique(self.polymers):
indices = np.where(self.polymers == polymer)[0]
numentries = int(len(indices))
for colindex, column in enumerate(requiredcolumns):
if column == 'Polymer Type (mandatory)':
self.finalData[rowindex:rowindex+numentries, colindex] = self.polymers[indices]
if column == 'Additives':
self.finalData[rowindex:rowindex+numentries, colindex] = self.additives[indices]
if column == 'Long Size (µm)':
self.finalData[rowindex:rowindex+numentries, colindex] = longSize[indices]
if column == 'Short Size (µm)':
self.finalData[rowindex:rowindex+numentries, colindex] = shortSize[indices]
if column == 'Area (µm²)':
self.finalData[rowindex:rowindex+numentries, colindex] = area[indices]
if column == 'HQI':
self.finalData[rowindex:rowindex+numentries, colindex] = self.hqis[indices]
if '> 100 µm' in requiredcolumns:
##append size classes
numPrevCols = len(requiredcolumns) - 6 #number of previous columns
for tableindex, dataindex in enumerate(indices):
for classindex in range(len(self.sizeClasses)):
upLimit = self.sizeClasses[classindex]
if classindex == 0: lowLimit = 0
else: lowLimit = self.sizeClasses[classindex-1]
curSize = self.sizes[dataindex]
if curSize > lowLimit and curSize <= upLimit:
self.finalData[rowindex+tableindex, numPrevCols + classindex] = np.int(1)
else:
self.finalData[rowindex+tableindex, numPrevCols + classindex] = np.int(0)
rowindex = rowindex + numentries
#dump into excel file
xlsname = self.directory + '//' + self.xlsFileName.text() + '.xlsx'
print('exporting excel to:\n file name: {} in directory: {}'.format(self.xlsFileName.text(), self.directory))
validFileName = False
incr = 1
while not validFileName:
if not os.path.exists(xlsname):
validFileName = True
else:
xlsname = self.directory + self.xlsFileName.text() + ' {}.xlsx'.format(incr)
incr += 1
writer = pd.ExcelWriter(xlsname, engine = 'xlsxwriter')
df = pd.DataFrame(self.finalData)
df.to_excel(writer, sheet_name = 'Individual Particles', header = False, index = False)
if '> 100 µm' in requiredcolumns:
#generate particle statistics report
report = [['Polymer Type', '0 - 5 µm', '5 - 10 µm', '10 - 20 µm', '20 - 50 µm', '50 - 100 µm', '> 100 µm']]
for polymer in np.unique(self.polymers):
indices = np.where(self.polymers == polymer)[0]
classes = np.array([0, 0, 0, 0, 0, 0])
for size in self.sizes[indices]:
if size < 5: classes[0] += 1
elif size < 10: classes[1] += 1
elif size < 20: classes[2] += 1
elif size < 50: classes[3] += 1
elif size < 100: classes[4] += 1
else: classes[5] += 1
report.append([polymer, classes[0], classes[1], classes[2], classes[3], classes[4], classes[5]])
report = pd.DataFrame(np.array(report))
report.to_excel(writer, sheet_name = 'Particle Statistics', header = False, index = False)
QtWidgets.QMessageBox.about(self, 'Done!', 'Particle Data exported')
def closeEvent(self, event):
if self.expWin is not None:
self.expWin.close()
event.accept()
class AdditiveViewer(QtWidgets.QWidget):
def __init__(self, polymername, sortedAdditives):
super(AdditiveViewer, self).__init__()
self.setGeometry(200,200, 800, 600)
self.setWindowTitle('Additives of {}'.format(polymername))
self.layout = QtWidgets.QGridLayout()
self.setLayout(self.layout)
self.canvas = FigureCanvas(Figure(figsize=(5, 3)))
self.ax = self.canvas.figure.subplots()
self.layout.addWidget(self.canvas, 0, 0)
self.ax.hist(sortedAdditives)
self.ax.set_ylabel('Number', fontsize = 15)
self.ax.tick_params(axis='both', which='both', labelsize=15)
if __name__ == '__main__':
from ..dataset import DataSet
def run():
app = QtWidgets.QApplication(sys.argv)
meas = ParticleAnalysis(None)
meas = ParticleAnalysis(DataSet("dummydata"))
meas.showMaximized()
ret = app.exec_()
return app.exec_()
run()
\ No newline at end of file
# -*- 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
import numpy as np
import pandas as pd
import os
import sys
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
class ExpExcelDialog(QtWidgets.QDialog):
def __init__(self, datastats, parent):
super(ExpExcelDialog, self).__init__(parent)
self.setWindowTitle('Export Options')
self.setGeometry(200,200, 300, 300)
self.datastats = datastats
self.particles = self.datastats.particlestats
self.polymers = self.datastats.particleResults
self.additives = self.datastats.currentAdditives
self.hqis = self.datastats.hqis
self.layout = QtWidgets.QHBoxLayout()
self.setLayout(self.layout)
excelvbox = QtWidgets.QVBoxLayout()
excelvbox.addWidget(QtWidgets.QLabel('Select Parameters for Export'))
excelgroup = QtWidgets.QGroupBox("Export to Excel", self)
self.exportOptions = ['Polymer Type (mandatory)', 'Additives', 'Long Size (µm)', 'Short Size (µm)', 'Area (µm²)', 'HQI', 'Size Classes']
self.checkBoxes = []
self.sizeClasses = [5, 10, 20, 50, 100, 1e6]
self.directory = self.datastats.dataset.path
for index, option in enumerate(self.exportOptions):
self.checkBoxes.append(QtWidgets.QCheckBox(self))
self.checkBoxes[-1].setText(option)
self.checkBoxes[-1].setChecked(True)
if option == 'Polymer Type (mandatory)':
self.checkBoxes[-1].setEnabled(False) #is mandatory!!!
if option == 'Additives':
if self.additives is None:
self.checkBoxes[-1].setEnabled(False)
self.checkBoxes[-1].setChecked(False)
excelvbox.addWidget(self.checkBoxes[-1])
self.xlsFileName = QtWidgets.QLineEdit()
self.xlsFileName.setText('{}_Particle_List'.format(self.datastats.dataset.name))
excelvbox.addWidget(QtWidgets.QLabel('Filename:'))
excelvbox.addWidget(self.xlsFileName)
self.exlbtn = QtWidgets.QPushButton('Export to Excel')
self.exlbtn.resize(self.exlbtn.sizeHint())
self.exlbtn.clicked.connect(self.toExcel)
excelvbox.addWidget(self.exlbtn)
excelgroup.setLayout(excelvbox)
self.layout.addWidget(excelgroup)
self.show()
def toExcel(self):
requiredcolumns = []
self.sizes = np.round(np.array([i[0] if np.isnan(i[2]) else i[2] for i in self.particles]), 1)
for box in self.checkBoxes:
if box.isChecked() == True:
if box.text() != 'Size Classes':
requiredcolumns.append(box.text())
if box.text() == 'Long Size (µm)':
longSize = self.sizes
elif box.text() == 'Short Size (µm)':
shortSize = np.round(np.array([i[1] if np.isnan(i[3]) else i[3] for i in self.particles]), 1)
elif box.text() == 'Area (µm²)':
area = np.array([np.round(float(entry[4]), 1) for entry in self.particles])
else:
requiredcolumns.append('0 - 5 µm')
requiredcolumns.append('5 - 10 µm')
requiredcolumns.append('10 - 20 µm')
requiredcolumns.append('20 - 50 µm')
requiredcolumns.append('50 - 100 µm')
requiredcolumns.append('> 100 µm')
finalData = np.zeros((self.polymers.shape[0],len(requiredcolumns)-1))
polymertypes = [""]*self.polymers.shape[0]
rowindex = 0
for polymer in np.unique(self.polymers):
indices = self.polymers == polymer
numentries = int(np.sum(indices))
print("Num:", numentries)
sys.stdout.flush()
for colindex, column in enumerate(requiredcolumns):
if column == 'Polymer Type (mandatory)':
polymertypes[rowindex:rowindex+numentries] = self.polymers[indices]
if column == 'Additives':
finalData[rowindex:rowindex+numentries, colindex-1] = self.additives[indices]
if column == 'Long Size (µm)':
finalData[rowindex:rowindex+numentries, colindex-1] = longSize[indices]
if column == 'Short Size (µm)':
finalData[rowindex:rowindex+numentries, colindex-1] = shortSize[indices]
if column == 'Area (µm²)':
finalData[rowindex:rowindex+numentries, colindex-1] = area[indices]
# hit quality index array does not match the data size if particles have been combined
#if column == 'HQI':
# finalData[rowindex:rowindex+numentries, colindex-1] = self.hqis[indices]
if '> 100 µm' in requiredcolumns:
##append size classes
numPrevCols = len(requiredcolumns) - 1 - len(self.sizeClasses) #number of previous columns
for tableindex, dataindex in enumerate(np.arange(len(indices))[indices]):
for classindex in range(len(self.sizeClasses)):
upLimit = self.sizeClasses[classindex]
if classindex == 0: lowLimit = 0
else: lowLimit = self.sizeClasses[classindex-1]
curSize = self.sizes[dataindex]
if curSize > lowLimit and curSize <= upLimit:
finalData[rowindex+tableindex, numPrevCols + classindex] = np.int(1)
else:
finalData[rowindex+tableindex, numPrevCols + classindex] = np.int(0)
rowindex = rowindex + numentries
#dump into excel file
xlsname = self.directory + '//' + self.xlsFileName.text() + '.xlsx'
print('exporting excel to:\n file name: {} in directory: {}'.format(self.xlsFileName.text(), self.directory))
validFileName = False
incr = 1
while not validFileName:
if not os.path.exists(xlsname):
validFileName = True
else:
xlsname = self.directory + self.xlsFileName.text() + ' {}.xlsx'.format(incr)
incr += 1
writer = pd.ExcelWriter(xlsname, engine = 'xlsxwriter')
df = pd.DataFrame(finalData, columns=requiredcolumns[1:])
df.insert(0, 'Polymer Type', polymertypes)
df.to_excel(writer, sheet_name = 'Individual Particles', index = False)
if '> 100 µm' in requiredcolumns:
#generate particle statistics report
header = ['0 - 5 µm', '5 - 10 µm', '10 - 20 µm', '20 - 50 µm', '50 - 100 µm', '> 100 µm']
index = np.unique(self.polymers)
particleclasses = []
for polymer in index:
indices = np.where(self.polymers == polymer)[0]
sortind = np.searchsorted([5,10,20,50,100], self.sizes[indices], 'right')
classes = np.bincount(sortind, minlength=6)
particleclasses.append(classes)
particleclasses = np.array(particleclasses)
report = pd.DataFrame(np.array(particleclasses), columns=header,
dtype=int)
report.insert(0, 'Polymer Type', index)
report.insert(len(report.columns), 'Sum total', particleclasses.sum(axis=1))
report.to_excel(writer, sheet_name = 'Particle Statistics', index=False)
writer.save()
self.accept()
class AdditiveViewer(QtWidgets.QWidget):
def __init__(self, polymername, sortedAdditives):
super(AdditiveViewer, self).__init__()
self.setGeometry(200,200, 800, 600)
self.setWindowTitle('Additives of {}'.format(polymername))
self.layout = QtWidgets.QGridLayout()
self.setLayout(self.layout)
self.canvas = FigureCanvas(Figure(figsize=(5, 3)))
self.ax = self.canvas.figure.subplots()
self.layout.addWidget(self.canvas, 0, 0)
self.ax.hist(sortedAdditives)
self.ax.set_ylabel('Number', fontsize = 15)
self.ax.tick_params(axis='both', which='both', labelsize=15)
\ No newline at end of file
# -*- 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 os
import numpy as np
import operator
class DataStats(object):
def __init__(self, dataset):
self.dataset = dataset
self.config = dataset.resultParams
self.spectraResults = None #entire List of all spectra assignments
self.additiveResults = None #entire List of all additives
self.particlestats = None
self.particleResults = None #final assignment for each particle
self.currentPolymers = None #list of polymers after setting entries with low hqi to unknown
self.currentAdditives = None #same thing for the additives
self.uniquePolymers = None #list of present polymer types
self.spectra = None #acquired spectra
self.indices = None #assignment of what spectra-indices belong to what substance
self.particles2spectra = None
self.manualPolymers = {}
self.manualAdditives = {}
def resetResults(self, spectraResults, additiveResults, hqis, addhqis):
self.spectraResults = spectraResults
self.additiveResults = additiveResults
self.hqis = hqis
self.addhqis = addhqis
def update(self):
print('updating data from', self.dataset.name)
self.spectraResults = self.dataset.results['polymers']
self.additiveResults = self.dataset.results['additives']
self.hqis = self.dataset.results['hqis']
self.addhqis = self.dataset.results['additive_hqis']
self.colorSeed = self.dataset.colorSeed
if type(self.colorSeed) != str:
self.colorSeed = 'default'
#load Spectra
if self.dataset.spectraPath is None:
fname = os.path.join(self.dataset.path, self.dataset.name + '_000_Spec.Data 1.txt')
else:
fname = self.dataset.spectraPath
return self.loadSpectra(fname)
def loadSpectra(self, fname):
import time
t0 = time.time()
specfilename = self.dataset.fname.split('.pkl')[0] + '_spectra.npy'
specs = None
if os.path.exists(specfilename):
specs = np.load(specfilename)
else:
try:
specs = np.loadtxt(fname)
#if spectra are already in correct format (WITec, first column: wavenumbers, other columns, intensities),
#we take them, otherwise we have to convert from Renishaw export format...
if not len(np.unique(specs[:, 0])) == len(specs[:, 0]): #--> only unique numbers -> this is the wavenumber column, we have the witec format
#Renishaw Convert
#columns 0 and 1 are x and y coordinates. We dont need them...
startWavenumber = specs[0, 2]
startIndices = np.where(specs[:, 2] == startWavenumber)[0]
spectra = np.zeros((startIndices[1], len(startIndices)+1)) #create array with shape (numWavenumbers, numSpectra+1) (first column holds wavenumbers)
spectra[:, 0] = specs[startIndices[0]:startIndices[1], 2]
for i in range(len(startIndices)-1):
spectra[:, i+1] = specs[startIndices[i]:startIndices[i+1], 3]
#aaand the last spectrum:
spectra[:, -1] = specs[startIndices[-1]:, 3]
specs = np.flip(spectra, 0) #Renishaw goes from highest to lowest wavenumber, out of whatever reason...
#write spectra to binary file, that makes reloading them in future significantly faster
np.save(specfilename, specs)
print('loading specs:', time.time()-t0)
self.dataset.spectraPath = fname
except:
pass
self.spectra = specs
return specs
def loadParticleData(self):
self.particlestats = np.array(self.dataset.particlestats)
pixelscale = (self.dataset.pixelscale_df if self.dataset.imagescanMode == 'df' else self.dataset.pixelscale_bf)
#convert to mikrometer scale
for index in range(len(self.particlestats)):
for subindex in range(5):
self.particlestats[index][subindex] = self.particlestats[index][subindex] * pixelscale #multiply by pixelscale
if subindex == 4:
self.particlestats[index][subindex] = self.particlestats[index][subindex] * pixelscale #again for the area...
self.particles2spectra = self.dataset.particles2spectra
sortindices = self.dataset.ramanscansortindex
if self.particles2spectra is None:
print('creating default particles2spectra list')
#no assignment found, so we assume one measurement per particle and use ramanscansortindex for assignment
self.particles2spectra = [[int(np.where(sortindices == i)[0])] for i in range(len(sortindices))]
#check, if dataset already contains results. Otherwise load them...
return not (self.spectraResults is None or (len(self.spectraResults) != len(sortindices)))
def invalidateSpectra(self):
self.spectraResults = ['empty']*(self.spectra.shape[1]-1)
self.hqis = [100]*(self.spectra.shape[1]-1)
def formatResults(self, hqi, compHqi):
if self.spectraResults is not None:
del self.currentPolymers, self.currentAdditives
#convert to arrays (makes indexing easier...)
self.currentPolymers, self.hqis = np.array(self.spectraResults), np.array(self.hqis)
if self.additiveResults is not None:
self.currentAdditives, self.addhqis = np.array(self.additiveResults), np.array(self.addhqis)
self.compHqiSpinBox.setDisabled(False)
else:
self.currentAdditives = None
#set poor HQI results to unknown
self.currentPolymers[self.hqis < hqi] = 'unknown'
if self.currentAdditives is not None:
self.currentAdditives[self.addhqis < compHqi] = 'unknown'
def createHistogramData(self):
self.uniquePolymers = np.unique(self.currentPolymers)
self.particleResults = [None]*len(self.particlestats)
self.typehistogram = {i: 0 for i in self.uniquePolymers}
if len(self.particles2spectra) != len(self.particlestats):
return False
for particleID, specList in enumerate(self.particles2spectra):
assignment = self.currentPolymers[specList[0]] #we take the first result as particle result. Hence, all spectra per particle have to have the same result
self.particleResults[particleID] = assignment
self.typehistogram[assignment] += 1
self.particleResults = np.array(self.particleResults)
##sort typehistogram, it will be converted into a list!!
self.typehistogram = sorted(self.typehistogram.items(), key = operator.itemgetter(1), reverse = True)
self.uniquePolymers = [i[0] for i in self.typehistogram]
self.indices = [] #what particles belong to which polymer type?
for polymer in self.uniquePolymers:
self.indices.append(list(np.where(self.particleResults == polymer)[0]))
###generate additive array for each type in typehistogram:
if self.currentAdditives is None:
self.sorted_additives = None
else:
self.sorted_additives = []
for polymer in self.typehistogram: #get additives of each polymer type
self.sorted_additives.append(self.currentAdditives[np.where(self.currentPolymers == polymer[0])])
for i in range(len(self.sorted_additives)): #sort out 'none' entries
nonentries = np.where(self.sorted_additives[i] == 'none')
self.sorted_additives[i] = np.delete(self.sorted_additives[i], nonentries)
return True
def saveAnalysisResults(self, minHQI, compHQI):
self.dataset.results = {'polymers': self.spectraResults,
'hqis': self.hqis,
'additives': self.additiveResults,
'additive_hqis': self.addhqis}
self.dataset.resultParams = {'minHQI': minHQI,
'compHQI': compHQI}
self.dataset.save()
print('saved dataset')
\ No newline at end of file
......@@ -12,13 +12,14 @@ import sys
from os import chdir, getcwd
class LoadWITecResults(QtWidgets.QDialog):
def __init__(self, parent):
def __init__(self, datastats, 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.datastats = datastats
self.parent = parent
self.parent.setDisabled(True)
self.trueMatchResults = None
......@@ -99,7 +100,7 @@ class LoadWITecResults(QtWidgets.QDialog):
self.editEntryWindow.show()
def loadFileManually(self):
dsetpath = self.parent.parent.dataset.path
dsetpath = self.datastats.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])))
......@@ -247,22 +248,16 @@ class LoadWITecResults(QtWidgets.QDialog):
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.datastats.resetResults(self.polymertypes, self.additives,
self.hqis, 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.datastats.resetResults(self.polymertypes, self.additives,
self.hqis, self.addhqis)
self.parent.updateBtn.clicked.connect(self.parent.formatResults)
self.parent.formatResults()
self.parent.show_hide_labels()
......
......@@ -27,10 +27,10 @@ If not, see <https://www.gnu.org/licenses/>.
import numpy as np
import cv2
from PyQt5 import QtWidgets
#import matplotlib.pyplot as plt
class ParticleEditor(object):
def __init__(self, parent):
def __init__(self, datastats, parent):
self.datastats = datastats
self.parent = parent #the assigned analysis widget
self.backupFreq = 3 #save a backup every n actions
self.neverBackedUp = True
......@@ -39,7 +39,7 @@ class ParticleEditor(object):
def createSafetyBackup(self):
self.actionCounter += 1
if self.actionCounter == self.backupFreq-1 or self.neverBackedUp:
backupname = self.parent.parent.dataset.saveBackup()
backupname = self.datastats.dataset.saveBackup()
print('backing up as', backupname)
self.neverBackedUp = False
self.actionCounter = 0
......@@ -52,12 +52,14 @@ class ParticleEditor(object):
def combineParticles(self, contourIndices, new_assignment):
if new_assignment == 'other':
new_assignment = self.getNewEntry()
if new_assignment is None:
return
contourIndices = sorted(contourIndices) #we want to keep the contour with lowest index
print('merging contours:', contourIndices)
self.createSafetyBackup()
#get contours:
contours = [self.parent.parent.dataset.particlecontours[i] for i in contourIndices]
contours = [self.datastats.dataset.particlecontours[i] for i in contourIndices]
cnt = np.vstack(tuple(contours)) #combine contous
#draw contours
......@@ -70,7 +72,7 @@ class ParticleEditor(object):
img = np.zeros((rangey, rangex))
for i in contourIndices:
curCnt = self.parent.parent.dataset.particlecontours[i]
curCnt = self.datastats.dataset.particlecontours[i]
for i in range(len(curCnt)):
curCnt[i][0][0] -= xmin-padding
curCnt[i][0][1] -= ymin-padding
......@@ -80,7 +82,10 @@ class ParticleEditor(object):
img = np.uint8(cv2.morphologyEx(img, cv2.MORPH_CLOSE, np.ones((3, 3))))
temp, contours, hierarchy = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
if cv2.__version__ > '3.5':
contours, hierarchy = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
else:
temp, contours, hierarchy = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
newContour = contours[0]
stats = self.characterizeParticle(newContour)
......@@ -91,65 +96,67 @@ class ParticleEditor(object):
#check, if dataset contains (already modified) particle2spectra, otherwise create new.
if self.parent.parent.dataset.particles2spectra is None: #create default assignment
if self.datastats.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))]
sortindices = self.datastats.dataset.ramanscansortindex
self.datastats.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
self.datastats.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:]]
self.datastats.dataset.particlestats = [i for ind, i in enumerate(self.datastats.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:]]
self.datastats.dataset.particlecontours[contourIndices[0]] = newContour
self.datastats.dataset.particlecontours = [i for ind, i in enumerate(self.datastats.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])
specIndices.append(self.datastats.particles2spectra[index])
#flatten index list (in case, that a nested list was created...)
specIndices = list(np.concatenate(specIndices))
for i in specIndices:
self.parent.spectraResults[i] = new_assignment
self.parent.hqis[i] = 100 #avoid sorting them out again by hqi-filter...
self.datastats.spectraResults[i] = new_assignment
self.datastats.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
self.datastats.dataset.particles2spectra[contourIndices[0]] = specIndices
for index in reversed(contourIndices[1:]):
print('removing index from particles2spectra:', index)
del self.parent.parent.dataset.particles2spectra[index]
del self.datastats.dataset.particles2spectra[index]
#save data
self.parent.saveAnalysisResults()
self.datastats.saveAnalysisResults()
#update contours in sampleview
self.parent.parent.contouritem.resetContours(self.parent.parent.dataset.particlecontours)
self.parent.parent.contouritem.resetContours(self.datastats.dataset.particlecontours)
self.parent.loadParticleData()