Commit 15c7be2e authored by Lars Bittrich's avatar Lars Bittrich

further restructuring of code in respect to analysisview; pixelscale modes are...

further restructuring of code in respect to analysisview; pixelscale modes are now handled in dataset; dataset object are now able to be compared for consistency checks later;
parent 58e2489c
......@@ -399,7 +399,8 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.resultCheckBoxesLayout.addWidget(self.showTotalSelector)
#generate new checkboxes
self.polymerCheckBoxes = []
for index, polymer in enumerate(self.datastats.uniquePolymers):
uniquePolymers = self.datastats.getUniquePolymers()
for index, polymer in enumerate(uniquePolymers):
self.polymerCheckBoxes.append(QtWidgets.QCheckBox(self))
self.polymerCheckBoxes[index].setText(polymer)
self.resultCheckBoxesLayout.addWidget(self.polymerCheckBoxes[index])
......@@ -427,7 +428,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.navigationGroup.setEnabled(True)
self.polymerComboBox.currentIndexChanged.disconnect()
self.polymerComboBox.clear()
self.polymerComboBox.addItems(self.datastats.uniquePolymers)
self.polymerComboBox.addItems(uniquePolymers)
self.polymerComboBox.currentIndexChanged.connect(self.displayNewPolymerType)
self.polymerIndex = self.polymerComboBox.currentIndex()
......@@ -489,6 +490,48 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.parent.centerOnRamanIndex(specIndex, centerOn=centerOn, highlightContour=highlightContour)
self.parent.highLightRamanIndex(specIndex)
self.lastSpectrumInFocus = specIndex
def selectContour(self, index, centerOn=True):
uniquePolymers = self.datastats.getUniquePolymers()
if uniquePolymers is not None:
#the index is the contour index, find particle index:
specIndex = self.datastats.particles2spectra[index][0] #select first spectrum of partoicle
self.datastats.currentParticleIndex = index
self.datastats.currentSpectrumIndex = specIndex
selectedPolymer = self.datastats.currentPolymers[specIndex]
try:
self.polymerIndex = uniquePolymers.index(selectedPolymer)
except:
print(selectedPolymer)
raise
#subparticleIndex
partIndicesOfThatPolymer = self.datastats.indices[self.polymerIndex]
subPartInd = partIndicesOfThatPolymer.index(index)
#disconnect analysis widgets:
self.particleSelector.valueChanged.disconnect()
self.spectrumSelector.valueChanged.disconnect()
self.polymerComboBox.currentIndexChanged.disconnect()
#set widgets...
self.particleSelector.setValue(subPartInd+1)
self.particleSelector.setMaximum(len(partIndicesOfThatPolymer))
self.spectrumSelector.setValue(1)
self.spectrumSelector.setMaximum(len(self.datastats.particles2spectra[index]))
selectedPolymer = self.datastats.currentPolymers[specIndex]
self.polymerIndex = uniquePolymers.index(selectedPolymer)
self.polymerComboBox.setCurrentIndex(self.polymerIndex)
#reconnect all widgets:
self.particleSelector.valueChanged.connect(self.selectParticle)
self.spectrumSelector.valueChanged.connect(self.selectSpectrum)
self.polymerComboBox.currentIndexChanged.connect(self.displayNewPolymerType)
self.updateSpecPlot(centerOn=centerOn)
def displayNewPolymerType(self, resetCurrentIndex=True):
self.polymerIndex = self.polymerComboBox.currentIndex()
......@@ -676,6 +719,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
return color
def createPolymerOverlay(self):
uniquePolymers = self.datastats.getUniquePolymers()
if not self.noOverlayAct.isChecked() and self.datastats.indices is not None:
if len(self.datastats.indices) > 0:
......@@ -686,9 +730,9 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
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.datastats.uniquePolymers[index], base255=True)
color = self.getColorFromName(uniquePolymers[index], base255=True)
color = QtGui.QColor(color[0], color[1], color[2], alpha=alpha)
legendItems.append((self.datastats.uniquePolymers[index], color))
legendItems.append((uniquePolymers[index], color))
for i in indexList:
colorList[i] = color
......
......@@ -21,6 +21,19 @@ If not, see <https://www.gnu.org/licenses/>.
import os
import numpy as np
import operator
from dataset import loadData
def readDataStats(fname):
ds = loadData(fname)
datastats = DataStats(ds)
datastats.update()
datastats.loadParticleData()
minHQI = datastats.dataset.resultParams['minHQI']
compHQI = datastats.dataset.resultParams['compHQI']
datastats.formatResults(minHQI, compHQI)
datastats.createHistogramData()
return datastats
class DataStats(object):
def __init__(self, dataset):
......@@ -34,7 +47,6 @@ class DataStats(object):
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
......@@ -103,7 +115,7 @@ class DataStats(object):
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)
pixelscale = self.dataset.getPixelScale()
#convert to mikrometer scale
for index in range(len(self.particlestats)):
for subindex in range(5):
......@@ -145,6 +157,11 @@ class DataStats(object):
if self.currentAdditives is not None:
self.currentAdditives[self.addhqis < compHqi] = 'unknown'
def getUniquePolymers(self):
if self.currentPolymers is None:
return None
return self.uniquePolymers
def createHistogramData(self):
self.uniquePolymers = np.unique(self.currentPolymers)
self.particleResults = [None]*len(self.particlestats)
......@@ -192,4 +209,11 @@ class DataStats(object):
self.dataset.resultParams = {'minHQI': minHQI,
'compHQI': compHQI}
self.dataset.save()
print('saved dataset')
\ No newline at end of file
print('saved dataset; Valid:', self.testRead())
def testRead(self):
statsread = readDataStats(self.dataset.fname)
return statsread.__dict__ == self.__dict__
\ No newline at end of file
......@@ -53,6 +53,52 @@ def saveData(dataset, fname):
pickle.dump(dataset, fp, protocol=-1)
dataset.zvalimg = zvalimg
def arrayCompare(a1, a2):
print("array compare")
ind = np.isnan(a1)
if not np.any(ind):
return np.all(a1==a2)
if a1.shape!=a2.shape:
return False
return np.all(a1[~ind]==a2[~ind])
def listCompare(l1, l2):
print("list compare")
if len(l1)!=len(l2):
return False
for l1i, l2i in zip(l1, l2):
if isinstance(l1i, np.ndarray):
if not isinstance(l2i, np.ndarray) or not arrayCompare(l1i, l2i):
return False
elif isinstance(l1i, (list, tuple)):
if not isinstance(l2i, (list, tuple)) or not listCompare(l1i, l2i):
return False
elif l1i!=l2i and ((~np.isnan(l1i)) or (~np.isnan(l2i))):
return False
return True
def recursiveDictCompare(d1, d2):
for key in d1:
if not key in d2:
return False
a = d1[key]
b = d2[key]
print(key, type(a), type(b))
if isinstance(a, np.ndarray):
if not isinstance(b, np.ndarray) or not arrayCompare(a, b):
return False
elif isinstance(a, dict):
if not isinstance(b, dict):
return False
if not recursiveDictCompare(a, b):
return False
elif isinstance(a, (list, tuple)):
if not isinstance(b, (list, tuple)) or not listCompare(a, b):
return False
elif a != b:
return False
return True
class DataSet(object):
def __init__(self, fname, newProject=False):
self.fname = fname
......@@ -115,6 +161,14 @@ class DataSet(object):
self.fname = self.newProject(fname)
self.updatePath()
def __eq__(self, other):
return recursiveDictCompare(self.__dict__, other.__dict__)
def getPixelScale(self, mode=None):
if mode is None:
mode = self.imagescanMode
return (self.pixelscale_df if mode == 'df' else self.pixelscale_bf)
def saveZvalImg(self):
if self.zvalimg is not None:
cv2imwrite_fix(self.getZvalImageName(), self.zvalimg)
......@@ -243,8 +297,7 @@ class DataSet(object):
p0[1] += self.imagedim_bf[1]/2
return (pixelpos[0]*self.pixelscale_bf + p0[0]), (p0[1] - pixelpos[1]*self.pixelscale_bf)
else:
print('mapToRamanMode not understood')
return
raise ValueError(f'mapToLength mode: {mode} not understood')
def mapToLengthRaman(self, pixelpos, microscopeMode='df', noz=False):
p0x, p0y = self.mapToLength(pixelpos, mode = microscopeMode)
......@@ -315,9 +368,7 @@ class DataSet(object):
saveData(self, self.fname)
def saveBackup(self):
# backupNameNotFound = True
inc = 0
# while backupNameNotFound:
while True:
directory = os.path.dirname(self.fname)
filename = self.name + '_backup_' + str(inc) + '.pkl'
......@@ -327,5 +378,4 @@ class DataSet(object):
else:
saveData(self, path)
return filename
# backupNameNotFound = False
# -*- coding: utf-8 -*-
"""
GEPARD - Gepard-Enabled PARticle Detection
Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für
Polymerforschung Dresden e. V. <bittrich-lars@ipfdd.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program, see COPYING.
If not, see <https://www.gnu.org/licenses/>.
"""
from PyQt5 import QtCore, QtGui, QtWidgets
import numpy as np
import os
from dataset import DataSet, loadData
from ramancom.ramancontrol import RamanControl, simulatedRaman
from opticalscan import OpticalScan
from ramanscanui import RamanScanUI
from detectionview import ParticleDetectionView
from analysis.analysisview import ParticleAnalysis
from zeissimporter import ZeissImporter
from viewitems import FitPosIndicator, Node, Edge, ScanIndicator, RamanScanIndicator, SegmentationContours
from helperfunctions import polygoncovering, cv2imread_fix
import cv2
from ramancom.configRaman import RamanConfigWin
class SampleView(QtWidgets.QGraphicsView):
ScalingChanged = QtCore.pyqtSignal(float)
def __init__(self, logpath):
super(SampleView, self).__init__()
self.logpath = logpath
self.item = QtWidgets.QGraphicsPixmapItem()
self.item.setPos(0, 0)
self.item.setAcceptedMouseButtons(QtCore.Qt.NoButton)
self.scaleFactor = 1.0
scene = QtWidgets.QGraphicsScene(self)
scene.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex)
scene.addItem(self.item)
scene.setBackgroundBrush(QtCore.Qt.darkGray)
self.setScene(scene)
self.setCacheMode(QtWidgets.QGraphicsView.CacheBackground)
self.setViewportUpdateMode(QtWidgets.QGraphicsView.BoundingRectViewportUpdate)
self.setRenderHint(QtGui.QPainter.Antialiasing)
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorViewCenter)
self.ramanctrl = RamanControl()
self.simulatedRaman = simulatedRaman
#determine, if ramanSwitch is needed:
self.ramanctrl.connect()
if not self.ramanctrl.connected:
QtWidgets.QMessageBox.warning(self, 'Error', 'Please enable Raman Connection')
return
if self.ramanctrl.getImageDimensions(mode='bf')[0] == self.ramanctrl.getImageDimensions(mode='df')[0]:
self.ramanSwitchNeeded = False
else:
self.ramanSwitchNeeded = True
self.ramanctrl.disconnect()
self.drag = None
self.mode = None
self.dataset = None
self.fititems = []
self.boundaryitems = [[],[]]
self.scanitems = []
self.ramanscanitems = []
self.imgdata = None
self.isblocked = False
self.contouritem = SegmentationContours(self)
scene.addItem(self.contouritem)
self.detectionwidget = None
self.ramanwidget = RamanScanUI(self.ramanctrl, None, self.logpath, self)
self.ramanwidget.imageUpdate.connect(self.loadPixmap)
self.oscanwidget = OpticalScan(self.ramanctrl, None, self.logpath, self)
self.oscanwidget.imageUpdate.connect(self.loadPixmap)
self.oscanwidget.boundaryUpdate.connect(self.resetBoundary)
self.analysiswidget = None
self.setMinimumSize(600, 600)
self.darkenPixmap = False
self.microscopeMode = None
self.coordTestMode = False
def takeScreenshot(self):
#TODO:
#LIMIT SCREENSHOT TO ACTUAL VIEWSIZE OF LOADED IMAGE...
#hide scrollbars
self.setHorizontalScrollBarPolicy(1)
self.setVerticalScrollBarPolicy(1)
#capture screen
screen = QtWidgets.QApplication.primaryScreen()
self.repaint()
screenshot = screen.grabWindow(self.winId())
#unhide scrollbars
self.setHorizontalScrollBarPolicy(0)
self.setVerticalScrollBarPolicy(0)
fname = self.dataset.path + '/screenshot.png'
validFileName = False
incr = 1
while not validFileName:
if not os.path.exists(fname):
validFileName = True
else:
fname = self.dataset.path + '/screenshot ({}).png'.format(incr)
incr += 1
screenshot.save(fname , 'png')
QtWidgets.QMessageBox.about(self, 'Message', 'Saved as {} to project directory.'.format(fname.split('/')[-1]))
def closeEvent(self, event):
reply = QtWidgets.QMessageBox.question(self, 'Message',
"Are you sure to quit?", QtWidgets.QMessageBox.Yes |
QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes:
self.disconnectRaman()
self.saveDataSet()
event.accept()
self.oscanwidget.close()
if self.detectionwidget is not None:
self.detectionwidget.close()
if self.analysiswidget is not None:
self.analysiswidget.close()
self.ramanwidget.close()
else:
event.ignore()
def configureRamanControl(self):
self.configWin = RamanConfigWin(self)
self.configWin.show()
def saveDataSet(self):
if self.dataset is not None:
self.dataset.save()
@QtCore.pyqtSlot()
def zoomIn(self):
self.scaleImage(1.25)
@QtCore.pyqtSlot()
def zoomOut(self):
self.scaleImage(0.8)
@QtCore.pyqtSlot()
def normalSize(self):
self.scaleFactor = 1.0
self.setTransform(QtGui.QTransform.fromScale(1., 1.))
self.announceScaling()
@QtCore.pyqtSlot()
def fitToWindow(self):
# print("fitting to Window")
brect = self.item.sceneBoundingRect()
self.fitInView(0, 0, brect.width(), brect.height(), QtCore.Qt.KeepAspectRatio)
self.scaleFactor = self.transform().m11()
self.announceScaling()
def switchMode(self, mode, loadnew=False):
if mode is None:
return
assert mode in ["OpticalScan", "ParticleDetection", "RamanScan", "ParticleAnalysis"]
self.oscanwidget.setVisible(False)
if self.detectionwidget is not None:
self.detectionwidget.close()
self.detectionwidget.destroy()
self.ramanwidget.setVisible(False)
self.contouritem.resetContours([])
self.mode = mode
self.loadPixmap(self.microscopeMode)
if mode == "OpticalScan":
self.oscanwidget.setVisible(True)
self.oscanwidget.resetDataset(self.dataset)
elif mode == "ParticleDetection":
if self.detectionwidget is None:
self.detectionwidget = ParticleDetectionView(self.imgdata, self.dataset, self)
self.detectionwidget.show()
self.detectionwidget.imageUpdate.connect(self.detectionUpdate)
self.detectionwidget.detectionFinished.connect(self.activateMaxMode)
elif mode == "RamanScan":
self.ramanwidget.resetDataset(self.dataset)
self.ramanwidget.setVisible(True)
elif mode == "ParticleAnalysis":
if self.ramanwidget.isVisible():
self.ramanwidget.setVisible(False)
if self.analysiswidget is None:
print('creating new analysiswidget')
self.analysiswidget = ParticleAnalysis(self.dataset, self)
self.analysiswidget.showMaximized()
else:
print('show maximized already exisiting analysiswidget')
self.analysiswidget.showMaximized()
# self.ramanwidget.setVisible(False)
if self.detectionwidget is not None:
self.detectionwidget.setVisible(False)
#show legend:
self.imparent.legend.show()
if loadnew:
self.fitToWindow()
self.imparent.updateModes(mode, self.getMaxMode())
def open(self, fname):
self.saveDataSet()
#close all widgets
for widget in [self.detectionwidget, self.ramanwidget, self.oscanwidget, self.analysiswidget]:
if widget is not None:
widget.close()
widget.destroy()
del widget
self.contouritem.resetContours()
# if self.dataset is not None:
# del self.dataset
# self.scene().removeItem(self.contouritem)
# del self.contouritem
# self.contouritem = SegmentationContours(self)
# self.scene().addItem(self.contouritem)
self.dataset = loadData(fname)
self.setMicroscopeMode()
self.imparent.setWindowTitle(self.dataset.name + (" SIMULATION" if simulatedRaman else ""))
self.imgdata = None
self.activateMaxMode(loadnew=True)
self.imparent.snapshotAct.setEnabled(True)
def importProject(self, fname):
zimp = ZeissImporter(fname, self.ramanctrl, self)
if zimp.validimport:
zimp.exec()
if zimp.result() == QtWidgets.QDialog.Accepted:
self.open(zimp.gepardname)
def new(self, fname):
self.saveDataSet()
if self.dataset is not None:
self.dataset.save()
self.dataset = DataSet(fname, newProject=True)
self.setMicroscopeMode()
self.imparent.setWindowTitle(self.dataset.name + (" SIMULATION" if simulatedRaman else ""))
self.imgdata = None
self.activateMaxMode(loadnew=True)
self.imparent.snapshotAct.setEnabled(True)
def setMicroscopeMode(self):
if self.ramanSwitchNeeded:
self.imparent.ramanSwitch.connectToSampleView()
self.imparent.ramanSwitch.show()
self.microscopeMode = ('df' if self.imparent.ramanSwitch.df_btn.isChecked() else 'bf')
@QtCore.pyqtSlot()
def activateMaxMode(self, loadnew=False):
mode = self.getMaxMode()
self.imparent.updateModes(self.mode, self.getMaxMode())
self.switchMode(mode, loadnew=loadnew)
def blockUI(self):
self.isblocked = True
self.imparent.blockUI()
def unblockUI(self):
self.isblocked = False
self.imparent.unblockUI(self.ramanctrl.connected)
self.imparent.updateModes(self.mode, self.getMaxMode())
def getMaxMode(self):
if self.dataset is None:
return None
if not self.ramanctrl.connected:
self.connectRaman()
if not self.ramanctrl.connected:
return None
maxmode = "OpticalScan"
if os.path.exists(self.dataset.getImageName()):
maxmode = "ParticleDetection"
if len(self.dataset.ramanpoints)>0:
maxmode = "RamanScan"
if self.dataset.ramanscandone: #uncomment!!
maxmode = "ParticleAnalysis"
return maxmode
def mousePressEvent(self, event):
# if event.button()==QtCore.Qt.RightButton:
if event.button()==QtCore.Qt.MiddleButton:
self.drag = event.pos()
elif event.button()==QtCore.Qt.LeftButton and self.mode in ["OpticalScan", "RamanScan"] \
and event.modifiers()==QtCore.Qt.ControlModifier:
p0 = self.mapToScene(event.pos())
if self.dataset is not None and self.dataset.pshift is not None:
if self.dataset.readin:
reply = QtWidgets.QMessageBox.critical(self, 'Dataset is newly read from disk!',
"Coordinate systems might have changed since. Do you want to continue with saved coordinates?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes:
self.dataset.readin = False
else:
return
x, y, z = self.dataset.mapToLengthRaman([p0.x(), p0.y()],
microscopeMode=self.microscopeMode,
noz=(False if self.mode=="RamanScan" else True))
if z is not None:
assert z>-100.
self.ramanctrl.moveToAbsolutePosition(x, y, z)
elif event.button()==QtCore.Qt.LeftButton and self.mode=="ParticleDetection":
p0 = self.mapToScene(event.pos())
self.detectionwidget.setImageCenter([p0.x(), p0.y()])
# elif event.button()==QtCore.Qt.LeftButton and self.mode=="ParticleAnalysis":
# p0 = self.mapToScene(event.pos())
else:
p0 = self.mapToScene(event.pos())
super(SampleView, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.drag is not None:
p0 = event.pos()
move = self.drag-p0
self.horizontalScrollBar().setValue(move.x() + self.horizontalScrollBar().value())
self.verticalScrollBar().setValue(move.y() + self.verticalScrollBar().value())
self.drag = p0
else:
super(SampleView, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.drag = None
super(SampleView, self).mouseReleaseEvent(event)
def wheelEvent(self, event):
factor = 1.01**(event.angleDelta().y()/8)
self.scaleImage(factor)
def scaleImage(self, factor):
if factor<1 and not self.imparent.zoomOutAct.isEnabled():
return
if factor>1 and not self.imparent.zoomInAct.isEnabled():
return
self.scaleFactor *= factor
self.scale(factor, factor)
self.announceScaling()
def announceScaling(self):
pixelscale = (self.dataset.pixelscale_df if self.microscopeMode == 'df' else self.dataset.pixelscale_bf)
if self.dataset is None or pixelscale is None:
self.ScalingChanged.emit(-1.0)
else:
self.ScalingChanged.emit(pixelscale/self.scaleFactor) ##CURRENTLY ONLY DARKFIELD!!! FIX NEEDED!!!
def connectRaman(self):
if not self.ramanctrl.connect():
msg = QtWidgets.QMessageBox()
msg.setText("Connection failed! Please enable remote control.")
msg.exec()
else:
mode = self.getMaxMode()
self.switchMode(mode)
self.imparent.updateConnected(self.ramanctrl.connected)
def disconnectRaman(self):
self.ramanctrl.disconnect()
self.imparent.updateConnected(self.ramanctrl.connected)
@QtCore.pyqtSlot(str)
def detectionUpdate(self):
self.contouritem.resetContours(self.dataset.particlecontours)
self.prepareAnalysis()
self.update()
@QtCore.pyqtSlot(str)
def loadPixmap(self, microscope_mode='df'):
self.clearItems()
if self.dataset is None:
self.item.setPixmap(QtGui.QPixmap())
else:
data = self.imgdata
fname = self.dataset.getImageName()
if self.mode == "ParticleDetection" or self.mode == "ParticleAnalysis":