Commit 1c6ae7e5 authored by JosefBrandt's avatar JosefBrandt

Basic plots working, proceeding to contours
parent d1ae9d81
#!/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 os
import numpy as np
from analysis import importSpectra
class ParticleIndicator(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.fillRect(self.rect(), QtCore.Qt.white)
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 TypeHistogramView(QtWidgets.QScrollArea):
indexClicked = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super().__init__(parent)
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.widgets = []
def updateTypeHistogram(self, types):
# print("Updating polymer type view", flush=True)
for pi in self.widgets:
self.indicatorbox.removeWidget(pi)
pi.setParent(None)
pi.destroy()
self.indicatorbox.takeAt(0)
self.widgets = []
numtotal = sum([num for num, text, color in types])
def getIndexFunction(index):
return lambda : self.indexClicked.emit(index)
for index, entry in enumerate(types):
num, text, color = entry
# print("num, text, color:", num, text, color, flush=True)
pi = ParticleIndicator(num, numtotal, color, text)
self.indicatorbox.addWidget(pi)
pi.clicked.connect(getIndexFunction(index))
self.widgets.append(pi)
self.indicatorbox.addStretch()
self.view.update()
class SpectraPlot(QtWidgets.QGroupBox):
def __init__(self, dataset):
super(SpectraPlot, self).__init__()
self.dataset = dataset
self.spectra = None
layout = QtWidgets.QHBoxLayout()
self.canvas = FigureCanvas(Figure())
self.spec_axis = self.canvas.figure.subplots()
self.spec_axis.axis("off")
self.reference_ax = 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): #formerly updateData(self)....
def tryLoadingNumpySpecFile():
specPath = self.dataset.getSpectraFileName()
if os.path.exists(specPath):
return np.load(specPath)
else:
raise ImportError
try:
self.spectra = tryLoadingNumpySpecFile()
except ImportError:
fname = QtWidgets.QFileDialog.getOpenFileName(QtWidgets.QWidget(), 'Select Spectra File', self.dataset.path, 'text file (*.txt)')[0]
try:
self.spectra, spectraNames = importSpectra.importWITecSpectra(fname)
except ImportError:
try:
self.spectra, spectraNames = importSpectra.importRenishawSpectra(fname)
except ImportError:
self.spectra, spectraNames = importSpectra.importPerkinElmerSpectra(fname)
if self.spectra is None:
raise ImportError
else:
np.save(self.dataset.getSpectraFileName(), self.spectra)
self.canvas.draw()
def updateParticleSpectrum(self, specIndex, particleSize, hqi):
if self.spectra is not None:
#draw Sample Spectrum
self.spec_axis.axis("on")
self.spec_axis.clear()
self.spec_axis.plot(self.spectra[:, 0], self.spectra[:, specIndex+1])
self.spec_axis.tick_params(axis='both', which='both', labelsize=15)
self.spec_axis.set_xlabel('Wavenumber (cm-1)', fontsize = 15)
self.spec_axis.set_ylabel('Counts', fontsize = 15)
self.spec_axis.set_title('ScanPoint Number {}, Size = {} µm, HQI = {}'.format(specIndex+1, particleSize, hqi))
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=15)
self.reference_axis.plot(ref_wavenumber, ref_intensity, color = 'r')
self.reference_axis.set_ylabel('Ref. Intensity', fontsize = 15, 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]))
# wavenumber_diff = list(ref[:, 0]-100)
# y_start = wavenumber_diff.index(min(wavenumber_diff))
# y_min = min(ref[y_start:, specIndex+1])
# y_max = max(ref[y_start:, specIndex+1])
self.canvas.draw()
\ No newline at end of file
This diff is collapsed.
...@@ -204,86 +204,5 @@ class AdditiveViewer(QtWidgets.QWidget): ...@@ -204,86 +204,5 @@ class AdditiveViewer(QtWidgets.QWidget):
self.ax.hist(sortedAdditives) self.ax.hist(sortedAdditives)
self.ax.set_ylabel('Number', fontsize = 15) self.ax.set_ylabel('Number', fontsize = 15)
self.ax.tick_params(axis='both', which='both', labelsize=15) self.ax.tick_params(axis='both', which='both', labelsize=15)
class ParticleIndicator(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.fillRect(self.rect(), QtCore.Qt.white)
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 ParticleTypeView(QtWidgets.QScrollArea):
indexClicked = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super().__init__(parent)
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.widgets = []
def updateTypeHistogram(self, types):
# print("Updating polymer type view", flush=True)
for pi in self.widgets:
self.indicatorbox.removeWidget(pi)
pi.setParent(None)
pi.destroy()
self.indicatorbox.takeAt(0)
self.widgets = []
numtotal = sum([num for num, text, color in types])
def getIndexFunction(index):
return lambda : self.indexClicked.emit(index)
for index, entry in enumerate(types):
num, text, color = entry
# print("num, text, color:", num, text, color, flush=True)
pi = ParticleIndicator(num, numtotal, color, text)
self.indicatorbox.addWidget(pi)
pi.clicked.connect(getIndexFunction(index))
self.widgets.append(pi)
self.indicatorbox.addStretch()
self.view.update()
\ No newline at end of file
...@@ -20,6 +20,8 @@ If not, see <https://www.gnu.org/licenses/>. ...@@ -20,6 +20,8 @@ If not, see <https://www.gnu.org/licenses/>.
""" """
from PyQt5 import QtCore, QtWidgets, QtGui from PyQt5 import QtCore, QtWidgets, QtGui
import numpy as np import numpy as np
import random
import colorsys
WX, WY = 1024, 200 WX, WY = 1024, 200
...@@ -111,4 +113,23 @@ class ColorLegend(QtWidgets.QMdiSubWindow): ...@@ -111,4 +113,23 @@ class ColorLegend(QtWidgets.QMdiSubWindow):
qp.end() qp.end()
class ColorHandler(object):
\ No newline at end of file def __init__(self):
pass
#We should implement another method of getting colors, rather than taking the random color generator
#presets and other stuff can be defined here
def getColorFromName(self, name, seed, base255=True):
random.seed(seed + name)
hue = random.random()
random.seed((seed + name)*2)
saturation = random.random()/4 + 0.75 #i.e., between 0.75 and 1
random.seed((seed + 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:
color = list(color)
for i in range(3):
color[i] = np.round(color[i]*255)
color = tuple(color)
return color
\ No newline at end of file
...@@ -18,17 +18,16 @@ You should have received a copy of the GNU General Public License ...@@ -18,17 +18,16 @@ You should have received a copy of the GNU General Public License
along with this program, see COPYING. along with this program, see COPYING.
If not, see <https://www.gnu.org/licenses/>. If not, see <https://www.gnu.org/licenses/>.
""" """
import os #import os
import numpy as np import numpy as np
import operator import operator
from PyQt5 import QtWidgets, QtCore #from PyQt5 import QtWidgets, QtCore
from analysis import importSpectra #try:
try: # from dataset import loadData, recursiveDictCompare
from dataset import loadData, recursiveDictCompare # print('exported dataset methods from datastats')
print('exported dataset methods from datastats') #except:
except: # print('failed exported dataset methods from datastats')
print('failed exported dataset methods from datastats')
class ParticleContainer(object): class ParticleContainer(object):
def __init__(self, parent): def __init__(self, parent):
...@@ -89,7 +88,12 @@ class ParticleContainer(object): ...@@ -89,7 +88,12 @@ class ParticleContainer(object):
meas.setAssignment(hqiList[scanIndex]) meas.setAssignment(hqiList[scanIndex])
indicesOfTransferredAssignments.append(scanIndex) indicesOfTransferredAssignments.append(scanIndex)
assert np.unique(indicesOfTransferredAssignments) == np.unique((range(len(hqiList)))) assert np.unique(indicesOfTransferredAssignments) == np.unique((range(len(hqiList))))
def getParticleOfIndex(self, index):
particle = self.particles[index]
assert particle.index == index, f'particle.index ({particle.index}) does match requested index in particleList ({index})'
return particle
def getNumberOfParticles(self): def getNumberOfParticles(self):
return len(self.particles) return len(self.particles)
...@@ -99,9 +103,9 @@ class ParticleContainer(object): ...@@ -99,9 +103,9 @@ class ParticleContainer(object):
def getParticleContoursByIndex(self, partIndices): def getParticleContoursByIndex(self, partIndices):
contours = [] contours = []
for part in self.particles: for index in partIndices:
if part.index in partIndices: particle = self.getParticleOfIndex(index)
contours.append(part.contour) contours.append(particle.contour)
return contours return contours
def getMeasurementPixelCoords(self): def getMeasurementPixelCoords(self):
...@@ -118,6 +122,14 @@ class ParticleContainer(object): ...@@ -118,6 +122,14 @@ class ParticleContainer(object):
num += 1 num += 1
return num return num
def getNumberOfSpectraOfParticle(self, particleIndex):
particle = self.getParticleOfIndex(particleIndex)
return particle.getNumberOfMeasurements()
def getSpectraIndicesOfParticle(self, particleIndex):
particle = self.getParticleOfIndex(particleIndex)
return particle.getMeasurementIndices()
def getListOfParticleAssignments(self): def getListOfParticleAssignments(self):
particleAssignments = [] particleAssignments = []
for particle in self.particles: for particle in self.particles:
...@@ -130,7 +142,12 @@ class ParticleContainer(object): ...@@ -130,7 +142,12 @@ class ParticleContainer(object):
hqis.append(particle.getHighestHQI()) hqis.append(particle.getHighestHQI())
return hqis return hqis
def getInconsistentParticles(self): #i.e., particles that have multiple measurements with different assignments def getHQIOfSpectrumIndex(self, specIndex):
for particle in self.particles:
if specIndex in particle.getMeasurementIndices():
return particle.getHQIOfMeasurementIndex(specIndex)
def testForInconsistentParticles(self): #i.e., particles that have multiple measurements with different assignments
self.inconsistentParticles = [] self.inconsistentParticles = []
for particle in self.particles: for particle in self.particles:
if not particle.measurementsHaveSameOrigAssignment(): if not particle.measurementsHaveSameOrigAssignment():
...@@ -141,32 +158,45 @@ class ParticleContainer(object): ...@@ -141,32 +158,45 @@ class ParticleContainer(object):
print(f'Particle with index {particle.index} has the following assignments:') print(f'Particle with index {particle.index} has the following assignments:')
for assignment in particle.getOrigMeasurementAssignments(): for assignment in particle.getOrigMeasurementAssignments():
print(assignment) print(assignment)
else:
print('All particles have consistent spectra assignments')
return self.inconsistentParticles
def getSizesOfAllParticles(self): def getSizesOfAllParticles(self):
particleSizes = [] particleSizes = []
for particle in self.particles: for particle in self.particles:
particleSizes.append(particle.getParticleSize()) particleSizes.append(particle.getParticleSize())
return particleSizes
def getShortSizesOfAllParticles(self): def getShortSizesOfAllParticles(self):
shortSizes = [] shortSizes = []
for particle in self.particles: for particle in self.particles:
shortSizes.append(particle.getShortParticleSize()) shortSizes.append(particle.getShortParticleSize())#
return shortSizes
def getSizesOfParticleType(self, assignment): def getSizesOfParticleType(self, assignment):
particleSizes = [] particleSizes = []
for particle in self.particles: for particle in self.particles:
if particle.getParticleAssignment() == assignment: if particle.getParticleAssignment() == assignment:
particleSizes.append(particle.getParticleSize()) particleSizes.append(particle.getParticleSize())
return particleSizes
def getSizeOfParticleByIndex(self, index): def getIndicesOfParticleType(self, assignment):
indices = []
for particle in self.particles: for particle in self.particles:
if particle.index == index: if particle.getParticleAssignment() == assignment:
return particle.getParticleSize() indices.append(particle.index)
return indices
def getSizeOfParticleByIndex(self, index):
particle = self.getParticleOfIndex(index)
return particle.getParticleSize()
def getUniquePolymers(self):
typeHist = self.getTypeHistogram()
return typeHist.keys()
def getTypeHistogram(self): def getTypeHistogram(self):
uniquePolymers = self.getUniquePolymers() uniquePolymers = np.unique(self.getListOfParticleAssignments())
typehistogram = {i: 0 for i in uniquePolymers} typehistogram = {i: 0 for i in uniquePolymers}
for assignment in self.getListOfParticleAssignments(): for assignment in self.getListOfParticleAssignments():
typehistogram[assignment] += 1 typehistogram[assignment] += 1
...@@ -175,18 +205,15 @@ class ParticleContainer(object): ...@@ -175,18 +205,15 @@ class ParticleContainer(object):
#convert back to dict #convert back to dict
final_typehistogram = {i[0]: i[1] for i in sorted_typehistogram} final_typehistogram = {i[0]: i[1] for i in sorted_typehistogram}
return final_typehistogram return final_typehistogram
def getUniquePolymers(self):
return np.unique(self.getListOfParticleAssignments())
class Particle(object): class Particle(object):
def __init__(self, index): def __init__(self, index):
self.index = index self.index = index
self.longSize_ellipse = None self.longSize_ellipse = np.nan
self.shortSize_ellipse = None self.shortSize_ellipse = np.nan
self.longSize_box = None self.longSize_box = np.nan
self.shortSize_box = None self.shortSize_box = np.nan
self.area = None self.area = None
self.contour = None self.contour = None
self.measurements = [] self.measurements = []
...@@ -212,6 +239,17 @@ class Particle(object): ...@@ -212,6 +239,17 @@ class Particle(object):
hqis.append(meas.getHQI()) hqis.append(meas.getHQI())
return max(hqis) return max(hqis)
def getHQIOfMeasurementIndex(self, index):
for meas in self.measurements:
if meas.ramanScanIndex == index:
return meas.getHQI()
def getMeasurementIndices(self):
indices = []
for meas in self.measurements:
indices.append(meas.ramanScanIndex)
return indices
def getMeasurements(self): def getMeasurements(self):
return self.measurements return self.measurements
...@@ -225,23 +263,27 @@ class Particle(object): ...@@ -225,23 +263,27 @@ class Particle(object):
return assignments[indexOfHighestHQI] return assignments[indexOfHighestHQI]
def getParticleSize(self): def getParticleSize(self):
if self.longSize_ellipse is not None: if not np.isnan(self.longSize_ellipse):
return round(self.longSize_ellipse) size = self.longSize_ellipse
elif self.longSize_box is not None: elif not np.isnan(self.longSize_box):
return round(self.longSize_box) size = self.longSize_box
else: else:
print(f'Error, particle size requested, but not yet set.\nParticle Index is {self.index}') print(f'Error, particle size requested, but not yet set.\nParticle Index is {self.index}')
raise ValueError raise ValueError
assert size is not None, f'Error, size or particle {self.index} is None'
return round(size)
def getShortParticleSize(self): def getShortParticleSize(self):
if self.shortSize_ellipse is not None: if not np.isnan(self.shortSize_ellipse):
return round(self.shortSize_ellipse) return round(self.shortSize_ellipse)
elif self.shortSize_box is not None: elif not np.isnan(self.shortSize_box):
return round(self.shortSize_box ) return round(self.shortSize_box)
else: else:
print(f'Error, particle size requested, but not yet set.\nParticle Index is {self.index}') print(f'Error, particle size requested, but not yet set.\nParticle Index is {self.index}')
raise ValueError raise ValueError
def getNumberOfMeasurements(self):
return len(self.measurements)
def measurementsHaveSameOrigAssignment(self):