Commit 5e79c241 authored by Josef Brandt's avatar Josef Brandt

Automated Test until Spectrum Scan

parent 31eb4bb9
...@@ -79,7 +79,7 @@ class GEPARDMainWindow(QtWidgets.QMainWindow): ...@@ -79,7 +79,7 @@ class GEPARDMainWindow(QtWidgets.QMainWindow):
closeAll() closeAll()
@QtCore.pyqtSlot(float) @QtCore.pyqtSlot(float)
def scalingChanged(self, scale): def scalingChanged(self):
self.zoomInAct.setEnabled(self.view.scaleFactor < 20.0) self.zoomInAct.setEnabled(self.view.scaleFactor < 20.0)
self.zoomOutAct.setEnabled(self.view.scaleFactor > .01) self.zoomOutAct.setEnabled(self.view.scaleFactor > .01)
self.normalSizeAct.setEnabled(self.view.scaleFactor != 1.) self.normalSizeAct.setEnabled(self.view.scaleFactor != 1.)
...@@ -91,7 +91,7 @@ class GEPARDMainWindow(QtWidgets.QMainWindow): ...@@ -91,7 +91,7 @@ class GEPARDMainWindow(QtWidgets.QMainWindow):
if fileName: if fileName:
self.fname = str(fileName) self.fname = str(fileName)
self.view.open(self.fname) self.view.open(self.fname)
self.scalingChanged(1.) self.scalingChanged()
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def importProject(self, fileName=False): def importProject(self, fileName=False):
...@@ -101,7 +101,7 @@ class GEPARDMainWindow(QtWidgets.QMainWindow): ...@@ -101,7 +101,7 @@ class GEPARDMainWindow(QtWidgets.QMainWindow):
if fileName: if fileName:
self.fname = str(fileName) self.fname = str(fileName)
self.view.importProject(self.fname) self.view.importProject(self.fname)
self.scalingChanged(1.) self.scalingChanged()
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def new(self, fileName=False): def new(self, fileName=False):
...@@ -113,7 +113,7 @@ class GEPARDMainWindow(QtWidgets.QMainWindow): ...@@ -113,7 +113,7 @@ class GEPARDMainWindow(QtWidgets.QMainWindow):
if isValid: if isValid:
self.fname = str(fileName) self.fname = str(fileName)
self.view.new(self.fname) self.view.new(self.fname)
self.scalingChanged(1.) self.scalingChanged()
else: else:
QtWidgets.QMessageBox.critical(self, "Error", msg) QtWidgets.QMessageBox.critical(self, "Error", msg)
......
This diff is collapsed.
...@@ -299,7 +299,7 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -299,7 +299,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.seg = Segmentation(self.dataset, self) self.seg = Segmentation(self.dataset, self)
self.seg.detectionState.connect(self.updateDetectionState) self.seg.detectionState.connect(self.updateDetectionState)
self.thread = None self.thread = None
self.threadrunning = False self.detectThreadRunning = False
logPath = os.path.join(self.dataset.path, 'detectionlog.txt') logPath = os.path.join(self.dataset.path, 'detectionlog.txt')
self.logger = logging.getLogger('detection') self.logger = logging.getLogger('detection')
...@@ -397,8 +397,7 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -397,8 +397,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
grid.addWidget(paramui, i, 1, QtCore.Qt.AlignLeft) grid.addWidget(paramui, i, 1, QtCore.Qt.AlignLeft)
else: else:
grid.addWidget(paramui, i, 0, 1, 2, QtCore.Qt.AlignLeft) grid.addWidget(paramui, i, 0, 1, 2, QtCore.Qt.AlignLeft)
if label is not None: if label is not None:
grid.addWidget(label, i, 0, QtCore.Qt.AlignLeft) grid.addWidget(label, i, 0, QtCore.Qt.AlignLeft)
...@@ -448,7 +447,7 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -448,7 +447,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
vbox.addWidget(self.hideSeedsInSampleViewBtn) vbox.addWidget(self.hideSeedsInSampleViewBtn)
self.slider = QtWidgets.QSlider(self) self.slider = QtWidgets.QSlider(self)
self.slider.setRange(0,100) self.slider.setRange(0, 100)
self.slider.setValue(80) self.slider.setValue(80)
self.slider.setOrientation(QtCore.Qt.Horizontal) self.slider.setOrientation(QtCore.Qt.Horizontal)
self.slider.sliderMoved.connect(self.imglabel.resetAlpha) self.slider.sliderMoved.connect(self.imglabel.resetAlpha)
...@@ -654,7 +653,7 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -654,7 +653,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.updateImageSeeds() self.updateImageSeeds()
def detectShow(self, showname): def detectShow(self, showname):
if not self.threadrunning: if not self.detectThreadRunning:
self.saveDetectParams(self.dataset) self.saveDetectParams(self.dataset)
img = self.subimg.copy() img = self.subimg.copy()
kwargs = {} kwargs = {}
...@@ -699,7 +698,7 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -699,7 +698,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.seg.cancelcomputation = True self.seg.cancelcomputation = True
self.thread.join() self.thread.join()
self.seg.cancelcomputation = False self.seg.cancelcomputation = False
self.threadrunning = False self.detectThreadRunning = False
def blockUI(self): def blockUI(self):
self.pdetectsub.setEnabled(False) self.pdetectsub.setEnabled(False)
...@@ -731,7 +730,7 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -731,7 +730,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
return return
self.blockUI() self.blockUI()
self.pdetectall.setText("Cancel") self.pdetectall.setText("Cancel")
self.threadrunning = True self.detectThreadRunning = True
self.img = self.pyramid.getFullImage() self.img = self.pyramid.getFullImage()
self.thread = Thread(target=self._worker) self.thread = Thread(target=self._worker)
self.thread.start() self.thread.start()
...@@ -743,7 +742,7 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -743,7 +742,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def checkOnComputation(self): def checkOnComputation(self):
if self.thread is not None: if self.thread is not None:
if not self.threadrunning: if not self.detectThreadRunning:
self.thread = None self.thread = None
self.progressbar.disable() self.progressbar.disable()
self.unBlockUI() self.unBlockUI()
...@@ -794,7 +793,7 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -794,7 +793,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
seedradius = self.seedradiusedit.value() seedradius = self.seedradiusedit.value()
self.seg.setParameters(**kwargs) self.seg.setParameters(**kwargs)
try: try:
measurementPoints, contours= self.seg.apply2Image(self.img, seedpoints, deletepoints, seedradius, self.dataset, detectLogger) measurementPoints, contours = self.seg.apply2Image(self.img, seedpoints, deletepoints, seedradius, self.dataset, detectLogger)
except: except:
showErrorMessageAsWidget('Fatal error in particle detection, see detectionlog for info') showErrorMessageAsWidget('Fatal error in particle detection, see detectionlog for info')
detectLogger.exception('Fatal error in particle detection') detectLogger.exception('Fatal error in particle detection')
...@@ -804,7 +803,7 @@ class ParticleDetectionView(QtWidgets.QWidget): ...@@ -804,7 +803,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
if self.dataset is not None: if self.dataset is not None:
self.applyResultsToDataset(measurementPoints, contours) self.applyResultsToDataset(measurementPoints, contours)
self.threadrunning = False self.detectThreadRunning = False
def applyResultsToDataset(self, measurementPoints, contours): def applyResultsToDataset(self, measurementPoints, contours):
self.dataset.ramanscandone = False self.dataset.ramanscandone = False
......
...@@ -62,7 +62,7 @@ def scan(path, sol, zpositions, grid, controlclass, dataqueue, ...@@ -62,7 +62,7 @@ def scan(path, sol, zpositions, grid, controlclass, dataqueue,
for i, p in enumerate(grid): for i, p in enumerate(grid):
x, y = p x, y = p
z = sol[0]*x + sol[1]*y + sol[2] z = sol[0]*x + sol[1]*y + sol[2]
for k, zk in (zlist if i%2==0 else zlist[::-1]): for k, zk in (zlist if i % 2 == 0 else zlist[::-1]):
name = f"image_{i}_{k}.bmp" name = f"image_{i}_{k}.bmp"
logger.info(f'taking image {name}, time: ' + strftime("%d %b %Y %H:%M:%S", localtime())) logger.info(f'taking image {name}, time: ' + strftime("%d %b %Y %H:%M:%S", localtime()))
zik = z+zk zik = z+zk
...@@ -72,7 +72,7 @@ def scan(path, sol, zpositions, grid, controlclass, dataqueue, ...@@ -72,7 +72,7 @@ def scan(path, sol, zpositions, grid, controlclass, dataqueue,
if ishdr: if ishdr:
img_list = [] img_list = []
fname = os.path.join(path, f"tmp.bmp") fname = os.path.join(path, f"tmp.bmp")
values = [5.,25.,100.] values = [5., 25., 100.]
for j, val in enumerate(values if (i%2+k%2)%2==0 else reversed(values)): for j, val in enumerate(values if (i%2+k%2)%2==0 else reversed(values)):
ramanctrl.setBrightness(val) ramanctrl.setBrightness(val)
logger.info(f'writing hdr image to {fname}') logger.info(f'writing hdr image to {fname}')
...@@ -309,6 +309,7 @@ class OpticalScanUI(QtWidgets.QWidget): ...@@ -309,6 +309,7 @@ class OpticalScanUI(QtWidgets.QWidget):
# def __init__(self, ramanctrl, dataset, logger, parent=None): # def __init__(self, ramanctrl, dataset, logger, parent=None):
def __init__(self, sampleview: 'SampleView'): def __init__(self, sampleview: 'SampleView'):
super().__init__(sampleview, QtCore.Qt.Window) super().__init__(sampleview, QtCore.Qt.Window)
self.timer = QtCore.QTimer(self)
self.logger = sampleview.logger self.logger = sampleview.logger
self.view: QtWidgets.QGraphicsView = sampleview self.view: QtWidgets.QGraphicsView = sampleview
mainLayout = QtWidgets.QVBoxLayout() mainLayout = QtWidgets.QVBoxLayout()
...@@ -715,10 +716,9 @@ class OpticalScanUI(QtWidgets.QWidget): ...@@ -715,10 +716,9 @@ class OpticalScanUI(QtWidgets.QWidget):
self.dataset.maxdim = p0 + p1 self.dataset.maxdim = p0 + p1
self.dataset.mode = "opticalscan" self.dataset.mode = "opticalscan"
self.dataset.save() self.dataset.save()
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.checkOnScan) self.timer.timeout.connect(self.checkOnScan)
self.timer.setSingleShot(True) self.timer.setSingleShot(True)
self.timer.start(1000.) self.timer.start(1000)
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def checkOnScan(self): def checkOnScan(self):
......
...@@ -22,6 +22,8 @@ If not, see <https://www.gnu.org/licenses/>. ...@@ -22,6 +22,8 @@ If not, see <https://www.gnu.org/licenses/>.
import numpy as np import numpy as np
import cv2 import cv2
import os import os
import sys
from PyQt5 import QtWidgets, QtCore
try: try:
from skimage.io import imread as skimread from skimage.io import imread as skimread
...@@ -123,3 +125,7 @@ def hasFTIRControl(sampleview) -> bool: ...@@ -123,3 +125,7 @@ def hasFTIRControl(sampleview) -> bool:
""" """
ramanName: str = sampleview.ramanctrl.name.lower() ramanName: str = sampleview.ramanctrl.name.lower()
return ramanName.find('ftir') != -1 return ramanName.find('ftir') != -1
def getAppFolder() -> str:
return QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppLocalDataLocation)
This diff is collapsed.
...@@ -21,14 +21,13 @@ If not, see <https://www.gnu.org/licenses/>. ...@@ -21,14 +21,13 @@ If not, see <https://www.gnu.org/licenses/>.
Image Generator for fake images for testing with simulated instrument interfaces Image Generator for fake images for testing with simulated instrument interfaces
""" """
import os import os
import sys
from PyQt5 import QtWidgets, QtCore
import cv2 import cv2
import numpy as np import numpy as np
import noise import noise
from copy import deepcopy from copy import deepcopy
import pickle import pickle
from typing import Tuple, List from typing import Tuple, List
from ...helperfunctions import getAppFolder
def particleIsVisible(particle: np.ndarray, full: np.ndarray, pos: Tuple[int, int]) -> bool: def particleIsVisible(particle: np.ndarray, full: np.ndarray, pos: Tuple[int, int]) -> bool:
...@@ -133,8 +132,6 @@ class FakeCamera: ...@@ -133,8 +132,6 @@ class FakeCamera:
def close(self) -> None: def close(self) -> None:
self.fakeFilter.saveToFile() self.fakeFilter.saveToFile()
if self.fakeFilter.app is not None:
self.fakeFilter.app.deleteLater()
def updateImageAtPosition(self, position: List[float]) -> None: def updateImageAtPosition(self, position: List[float]) -> None:
""" """
...@@ -205,7 +202,6 @@ class FakeFilter: ...@@ -205,7 +202,6 @@ class FakeFilter:
self.presetParticles: List[dict] = [] self.presetParticles: List[dict] = []
self.particles: List[FakeParticle] = [] self.particles: List[FakeParticle] = []
self.filePath: str = '' self.filePath: str = ''
self.app: QtWidgets.QApplication = None
self._updateFromFile() self._updateFromFile()
def _updateFromFile(self) -> None: def _updateFromFile(self) -> None:
...@@ -238,20 +234,12 @@ class FakeFilter: ...@@ -238,20 +234,12 @@ class FakeFilter:
def saveToFile(self) -> None: def saveToFile(self) -> None:
fpath: str = self._getFilePath() fpath: str = self._getFilePath()
# QApplication cannot be pickled, so we replace it temporarily with None
origApp = self.app
self.app = None
with open(fpath, "wb") as fp: with open(fpath, "wb") as fp:
pickle.dump(self, fp, protocol=-1) pickle.dump(self, fp, protocol=-1)
self.app = origApp
def _getFilePath(self) -> str: def _getFilePath(self) -> str:
if self.filePath == '': if self.filePath == '':
if self.app is None: self.filePath = os.path.join(getAppFolder(), 'fakeFilter.pkl')
self.app = QtWidgets.QApplication(sys.argv) # has to be an instance attribute :/
self.app.setApplicationName("GEPARD")
path: str = QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppLocalDataLocation)
self.filePath = os.path.join(path, 'fakeFilter.pkl')
return self.filePath return self.filePath
def getParticleImage(self, particle: FakeParticle, blurRadius: int = 0) -> np.ndarray: def getParticleImage(self, particle: FakeParticle, blurRadius: int = 0) -> np.ndarray:
......
...@@ -21,16 +21,13 @@ If not, see <https://www.gnu.org/licenses/>. ...@@ -21,16 +21,13 @@ If not, see <https://www.gnu.org/licenses/>.
Simulates a Microscope Stage with Camera. Simulates a Microscope Stage with Camera.
""" """
import os import os
import sys
from PyQt5 import QtWidgets, QtGui, QtCore from PyQt5 import QtWidgets, QtGui, QtCore
import cv2 import cv2
import json import json
import numpy as np import numpy as np
from typing import List from typing import List
try: from .imageGenerator import FakeCamera
from .imageGenerator import FakeCamera from ...helperfunctions import getAppFolder
except ImportError:
from imageGenerator import FakeCamera
class SimulatedStage(object): class SimulatedStage(object):
...@@ -91,9 +88,7 @@ class SimulatedStage(object): ...@@ -91,9 +88,7 @@ class SimulatedStage(object):
json.dump(self._configToDict(), fp) json.dump(self._configToDict(), fp)
def _getFilePath(self) -> str: def _getFilePath(self) -> str:
self.app: QtWidgets.QApplication = QtWidgets.QApplication(sys.argv) # has to be an instance attribute :/ path: str = getAppFolder()
self.app.setApplicationName("GEPARD")
path: str = QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppLocalDataLocation)
return os.path.join(path, 'simulatedStageConfig.txt') return os.path.join(path, 'simulatedStageConfig.txt')
def _configToDict(self) -> dict: def _configToDict(self) -> dict:
......
# -*- 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, QtWidgets
import numpy as np
from multiprocessing import Process, Queue, Event
import queue
import os
import logging
import logging.handlers
from .external import tsp
from .uielements import TimeEstimateProgressbar
from .gepardlogging import setDefaultLoggingConfig
from .analysis.particleCharacterization import FTIRAperture
from .helperfunctions import getRamanControl
def reorder(points, N=20):
"""
Finds an efficient reordering of scan points in meandering horizontal
stripes.
Parameters
----------
points : list of scan points in 2D
N : integer, optional
The number of horizontal stripes in which the points should be aranged.
The default is 20.
Returns
-------
newind : index array
The new index array to reorder the scan points for efficient travel
between points.
"""
y0, y1 = points[:,1].min(), points[:,1].max()
y = np.linspace(y0,y1+.1,N+1)
allind = np.arange(points.shape[0])
newind = []
for i, yi in enumerate(y[:-1]):
yi1 = y[i+1]
indy = allind[(points[:,1]>=yi)&(points[:,1]<yi1)]
p = points[indy,:]
indx = p[:,0].argsort()
if i%2==1:
newind.append(indy[indx])
else:
newind.append(indy[indx[::-1]])
newind = np.concatenate(newind, axis=0)
assert np.unique(newind).shape[0]==allind.shape[0]
return newind
def scan(ramanSettings, positions, controlclass, dataqueue, stopevent,
logpath=''):
if logpath != '':
logger = logging.getLogger('RamanScanLogger')
logger.addHandler(
logging.handlers.RotatingFileHandler(
logpath, maxBytes=5 * (1 << 20), backupCount=10)
)
setDefaultLoggingConfig(logger)
try:
ramanctrl = getRamanControl(controlclass, logger)
ramanctrl.connect()
if ramanctrl.name == 'ThermoFTIRCom':
ramanSettings['Apertures'] = positions
ramanctrl.initiateMeasurement(ramanSettings)
logger.info(ramanctrl.name)
for i, p in enumerate(positions):
if not ramanctrl.name == 'ThermoFTIRCom':
x, y, z = p
else:
x, y, z = p.centerX, p.centerY, p.centerZ
width, height, angle = p.width, p.height, p.angle
logger.info(f'{width}, {height}, {angle}')
ramanctrl.setAperture(width, height, angle)
logger.info(f"position: {x}, {y}, {z}")
ramanctrl.moveToAbsolutePosition(x, y, z)
logger.info("move done")
ramanctrl.triggerMeasurement(i)
logger.info("trigger done")
if stopevent.is_set():
ramanctrl.disconnect()
return
dataqueue.put(i)
ramanctrl.finishMeasurement()
ramanctrl.disconnect()
except:
logger.exception('Fatal error in ramanscan')
from .errors import showErrorMessageAsWidget
showErrorMessageAsWidget('See ramanscanlog in project directory for information')
class RamanScanUI(QtWidgets.QWidget):
imageUpdate = QtCore.pyqtSignal(str, name='imageUpdate') #str = 'df' (= darkfield) or 'bf' (=bright field)
ramanscanUpdate = QtCore.pyqtSignal()
def __init__(self, ramanctrl, dataset, logger, parent=None):
super().__init__(parent, QtCore.Qt.Window)
self.view = parent
self.logger = logger
self.ramanctrl = ramanctrl
self.dataset = dataset
self.process = None
self.processstopevent = Event()
self.dataqueue = Queue()
self.timer = QtCore.QTimer(self)
vbox = QtWidgets.QVBoxLayout()
hbox = QtWidgets.QHBoxLayout()
self.params = []
self.paramsGroup = QtWidgets.QGroupBox("Raman settings")
self.paramsLayout = QtWidgets.QFormLayout()
self.prun = QtWidgets.QPushButton("Raman scan", self)
self.prun.released.connect(self.run)
self.paramsGroup.setLayout(self.paramsLayout)
self.updateRamanParameters()
self.pexit = QtWidgets.QPushButton("Cancel", self)
self.pexit.released.connect(self.cancelScan)
self.prun.setEnabled(False)
self.progressbar = TimeEstimateProgressbar()
self.progressbar.disable()
hbox.addStretch()
hbox.addWidget(self.pexit)
vbox.addWidget(self.paramsGroup)
vbox.addLayout(hbox)
vbox.addWidget(self.progressbar)
self.setLayout(vbox)
self.setWindowTitle("Raman Scan")
self.setVisible(False)
def updateRamanParameters(self):
"""
Update the raman parameters in the layout
:return:
"""
for index in reversed(range(self.paramsLayout.count())):
widget = self.paramsLayout.itemAt(index).widget()
self.paramsLayout.removeWidget(widget)
widget.setParent(None)
self.params = []
for param in self.ramanctrl.ramanParameters:
if param.dtype == 'int':
self.params.append(QtWidgets.QSpinBox())
self.params[-1].setMinimum(param.minVal)
self.params[-1].setMaximum(param.maxVal)
self.params[-1].setValue(param.value)
if param.dtype == 'double':
self.params.append(QtWidgets.QDoubleSpinBox())
self.params[-1].setMinimum(param.minVal)
self.params[-1].setMaximum(param.maxVal)
self.params[-1].setValue(param.value)
if param.dtype == 'combobox':
self.params.append(QtWidgets.QComboBox())
self.params[-1].addItems([str(i) for i in param.valList])
if param.dtype == 'checkBox':
self.params.append(QtWidgets.QCheckBox())
self.params[-1].setChecked(param.value)
for index, param in enumerate(self.params):
param.setMinimumWidth(70)
self.paramsLayout.addRow(QtWidgets.QLabel(self.ramanctrl.ramanParameters[index].name), param)
self.paramsLayout.addRow(self.prun)
self.paramsGroup.setLayout(self.paramsLayout)
def makeGetFnameLambda(self, msg, path, fileType, btn):
return lambda: self.getFName(msg, path, fileType, btn)
def getFName(self, msg, path, filetype, btn):
fname = QtWidgets.QFileDialog.getOpenFileName(self, msg, path, filetype)[0]
btn.setText(fname.split('\\')[-1])
btn.setMinimumSize(btn.sizeHint())
def resetDataset(self, ds):
self.dataset = ds
numParticles = self.dataset.particleContainer.getNumberOfParticles()
numMeasurements = self.dataset.particleContainer.getNumberOfMeasurements()
if numParticles>0:
self.prun.setEnabled(True)
self.setWindowTitle(f'{numParticles} Particles ({numMeasurements} Measurements)')
@QtCore.pyqtSlot()