...
 
Commits (21)
......@@ -21,7 +21,7 @@ Requirements:
for 64bit as many use cases require a lot of memory (16 GB better 32 GB
recommended)
* the tsp module in externalmodules can be built with
* the tsp module in external can be built with
python setuptsp.py
please note: for this step a valid compiler needs to be installed in the
system; Otherwise use the precompiled tsp-module
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# -*- coding: utf-8 -*-
"""
Created on Wed Jan 16 12:43:00 2019
@author: brandt
"""
"""
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 numpy as np
import cv2
from PyQt5 import QtWidgets
class ParticleEditor(object):
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
self.actionCounter = 0
def createSafetyBackup(self):
self.actionCounter += 1
if self.actionCounter == self.backupFreq-1 or self.neverBackedUp:
backupname = self.datastats.dataset.saveBackup()
print('backing up as', backupname)
self.neverBackedUp = False
self.actionCounter = 0
def getNewEntry(self):
text, okClicked = QtWidgets.QInputDialog.getText(self.parent.parent, "Custom assignment", "Enter new assignment")
if okClicked and text != '':
return text
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
#get contours:
contours = [self.datastats.dataset.particlecontours[i] for i in contourIndices]
cnt = np.vstack(tuple(contours)) #combine contous
#draw contours
xmin, xmax = cnt[:,0,:][:, 0].min(), cnt[:,0,:][:, 0].max()
ymin, ymax = cnt[:,0,:][:, 1].min(), cnt[:,0,:][:, 1].max()
padding = 2 #pixel in each direction
rangex = int(np.round((xmax-xmin)+2*padding))
rangey = int(np.round((ymax-ymin)+2*padding))
img = np.zeros((rangey, rangex))
for i in contourIndices:
curCnt = self.datastats.dataset.particlecontours[i].copy()
for i in range(len(curCnt)):
curCnt[i][0][0] -= xmin-padding
curCnt[i][0][1] -= ymin-padding
cv2.drawContours(img, [curCnt], -1, 1, -1)
cv2.drawContours(img, [curCnt], -1, 1, 1)
img = np.uint8(cv2.morphologyEx(img, cv2.MORPH_CLOSE, np.ones((3, 3))))
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)
if len(contours)>1:
QtWidgets.QMessageBox.critical(self.parent, 'ERROR!',
'Particle contours are not connected and cannot be combined!')
return
newContour = contours[0]
stats = self.characterizeParticle(newContour)
for i in range(len(newContour)):
newContour[i][0][0] += xmin-padding
newContour[i][0][1] += ymin-padding
print('merging contours:', contourIndices)
self.createSafetyBackup()
#check, if dataset contains (already modified) particle2spectra, otherwise create new.
if self.datastats.dataset.particles2spectra is None: #create default assignment
print('recreating particles2spectra from within edit particles...')
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.datastats.dataset.particlestats[contourIndices[0]] = stats
#now, delete the rest...
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.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.datastats.particles2spectra[index])
#flatten index list (in case, that a nested list was created...)
specIndices = list(np.concatenate(specIndices))
for i in specIndices:
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.datastats.dataset.particles2spectra[contourIndices[0]] = specIndices
for index in reversed(contourIndices[1:]):
print('removing index from particles2spectra:', index)
del self.datastats.dataset.particles2spectra[index]
#update contours in sampleview
self.parent.parent.contouritem.resetContours(self.datastats.dataset.particlecontours)
self.parent.loadParticleData()
#save data
minHQI = self.parent.hqiSpinBox.value()
compHQI = self.parent.compHqiSpinBox.value()
if not self.datastats.saveAnalysisResults(minHQI, compHQI):
QtWidgets.QMessageBox.warning(self.parent, 'Error!',
'Data inconsistency after saving!', QtWidgets.QMessageBox.Ok,
QtWidgets.QMessageBox.Ok)
def reassignParticles(self, contourindices, new_assignment):
if new_assignment == 'other':
new_assignment = self.getNewEntry()
if new_assignment is None:
return
self.createSafetyBackup()
print(f'reassigning indices {contourindices} into {new_assignment}')
for partIndex in contourindices:
for specIndex in self.datastats.particles2spectra[partIndex]:
self.datastats.currentPolymers[specIndex] = new_assignment
self.datastats.spectraResults[specIndex] = new_assignment
self.datastats.hqis[specIndex] = 100
#update contours in sampleview
self.parent.parent.contouritem.resetContours(self.datastats.dataset.particlecontours)
self.parent.loadParticleData()
#save data
minHQI = self.parent.hqiSpinBox.value()
compHQI = self.parent.compHqiSpinBox.value()
if not self.datastats.saveAnalysisResults(minHQI, compHQI):
QtWidgets.QMessageBox.warning(self.parent, 'Error!',
'Data inconsistency after saving!',
QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.Ok)
def deleteParticles(self):
self.createSafetyBackup()
pass
def splitParticles(self):
self.createSafetyBackup()
pass
def characterizeParticle(self, contours):
##characterize particle
longellipse, shortellipse = np.nan, np.nan
cnt = contours
if cnt.shape[0] >= 5: ##at least 5 points required for ellipse fitting...
ellipse = cv2.fitEllipse(cnt)
shortellipse, longellipse = ellipse[1]
rect = cv2.minAreaRect(cnt)
long, short = rect[1]
if short>long:
long, short = short, long
return long, short, longellipse, shortellipse, cv2.contourArea(cnt)
\ No newline at end of file
This diff is collapsed.
# -*- 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, QtGui
import numpy as np
WX, WY = 1024, 200
class ColorLegend(QtWidgets.QMdiSubWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint|QtCore.Qt.FramelessWindowHint)
self.setFixedSize(200, 800)
self.wscale = 1
self.drag = None
self.items = [] #list of items to display (text, color)
self.tileSize = 12
self.fontSize = 15
self.spacer = 10
def setTextColorItems(self, items):
for text, color in items:
print(text, color)
assert type(text)==str or type(text)==np.str_, "items must be tuples of text and QColor"
assert type(color)==QtGui.QColor or type(color)==QtCore.Qt.GlobalColor, "items must be tuples of text and QColor"
self.items = items
self.update()
def mousePressEvent(self, event):
if event.button()==QtCore.Qt.LeftButton:
self.drag = event.pos()
def mouseMoveEvent(self, event):
if self.drag is not None:
p0 = event.pos()
self.move(self.mapToParent(p0-self.drag))
self.parentWidget().update()
else:
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self.drag = None
super().mouseReleaseEvent(event)
def paintEvent(self, event):
numEntries = len(self.items)
if numEntries > 0:
def getSize(fontsize, text, tileSize, spacer):
font = QtGui.QFont()
font.setPixelSize(fontSize)
fm = QtGui.QFontMetrics(font)
pixelwidth = fm.width(text)
width, height = pixelwidth + tileSize + 4*spacer, numEntries * (fontSize+1*spacer) + spacer
return width, height
fontSize, tileSize, spacer = self.fontSize, self.tileSize, self.spacer
longestEntry = max([i[0] for i in self.items], key=len)
width, height = getSize(fontSize, longestEntry, tileSize, spacer)
# scale smaller, if necessary
if height > 1024:
factor = 1024/height
#prevent text getting tooo small:
factor = np.clip(factor, 0.6, 1) #0.6*15 would be fontSize 9
height, tileSize, fontSize, spacer = height*factor, tileSize*factor, int(fontSize*factor), spacer*factor
width, height = getSize(fontSize, longestEntry, tileSize, spacer)
self.setFixedSize(width, height)
qp = QtGui.QPainter()
qp.begin(self)
font = QtGui.QFont()
font.setPixelSize(fontSize)
qp.setFont(font)
for index, item in enumerate(self.items):
#draw Text
x0 = tileSize+2*spacer
y0 = index*(fontSize+spacer)
rect= QtCore.QRectF(x0, y0, width, float(fontSize+spacer))
qp.drawText(rect, QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter, item[0])
#draw colored Box
qp.setBrush(item[1])
x0 = spacer
y0 = index*(fontSize+spacer) + (fontSize - tileSize)/2 +spacer/2
qp.drawRect(x0, y0, tileSize, tileSize)
qp.end()
\ No newline at end of file
This diff is collapsed.
......@@ -267,7 +267,7 @@ class ImageView(QtWidgets.QLabel):
painter.drawEllipse(p[0]-p[2], p[1]-p[2], 2*p[2], 2*p[2])
class ParticleDetectionView(QtWidgets.QWidget):
imageUpdate = QtCore.pyqtSignal(name='imageUpdate')
imageUpdate = QtCore.pyqtSignal(str, name='imageUpdate') #str = 'df' (= darkfield) or 'bf' (=bright field)
detectionFinished = QtCore.pyqtSignal(name='detectionFinished')
def __init__(self, img, dataset, parent=None):
......@@ -275,8 +275,9 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.dataset = self.verifySeedpoints(dataset)
self.img = img
self.imgclip = 0,0,0,0
self.seg = Segmentation(self.dataset)
self.seg = Segmentation(self.dataset, self)
self.thread = None
self.view = parent
vbox = QtWidgets.QVBoxLayout()
hbox = QtWidgets.QHBoxLayout()
......@@ -302,10 +303,12 @@ class ParticleDetectionView(QtWidgets.QWidget):
group = QtWidgets.QGroupBox("Detection settings", self)
grid = QtWidgets.QGridLayout()
self.parameters = []
checkBoxesToLink = {}
# create editable parameters:
for i, p in enumerate(self.seg.parlist):
label, colstretch = None, 1
if p.name == "points":
if p.name == "contrastCurve":
paramui = HistWidget(lambda : self.seg.calculateHist(self.seg.convert2Gray(self.subimg)),
self.seg.calculateHistFunction, self)
valuefunc = makeValueLambda(paramui.value)
......@@ -315,6 +318,8 @@ class ParticleDetectionView(QtWidgets.QWidget):
paramui.setChecked(p.value)
valuefunc = makeValueLambda(paramui.isChecked)
colstretch = 2
if p.linkedParameter is not None:
checkBoxesToLink[paramui] = p.linkedParameter
elif p.dtype == int or p.dtype == float:
label = QtWidgets.QLabel(p.helptext, self)
if p.dtype == int:
......@@ -333,18 +338,44 @@ class ParticleDetectionView(QtWidgets.QWidget):
elif p.dtype is None:
label = QtWidgets.QLabel(p.helptext, self)
paramui = None
if paramui is not None:
self.parameters.append([paramui, p.name, valuefunc])
self.parameters.append([paramui, p.name, valuefunc, None])
if colstretch == 1:
grid.addWidget(paramui, i, 1, QtCore.Qt.AlignLeft)
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:
grid.addWidget(label, i, 0, QtCore.Qt.AlignLeft)
if p.show is True:
pshow = QtWidgets.QPushButton(">", self)
pshow.released.connect(makeShowLambda(p.name))
grid.addWidget(pshow, i, 2, QtCore.Qt.AlignRight)
if paramui is not None:
self.parameters[-1][3] = pshow
#link checkboxes to other parameters:
def makeEnableLambda(checkbox, parameter):
return lambda: parameter.setEnabled(checkbox.isChecked())
for box in checkBoxesToLink:
linkedName = checkBoxesToLink[box]
for p in self.parameters:
if p[1] == linkedName:
p[0].setEnabled(box.isChecked())
box.stateChanged.connect(makeEnableLambda(box, p[0])) #the actual control element
if p[3] is not None: #the "show" box, if present
box.stateChanged.connect(makeEnableLambda(box, p[3]))
p[3].setEnabled(box.isChecked())
label = QtWidgets.QLabel("Seed radius", self)
grid.addWidget(label, i+1, 0, QtCore.Qt.AlignLeft)
......@@ -383,7 +414,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
def saveDetectParams(self, ds=None):
if ds is not None:
for param in self.parameters:
if param[1] == 'points':
if param[1] == 'contrastCurve':
print(param[0].value())
try: # is it a spinbox or the histWidget? Read out the value
ds.detectParams[param[1]] = param[0].value()
......@@ -462,7 +493,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
def closeEvent(self, event):
self.detectionFinished.emit()
self.destroy()
# self.destroy()
def mousePressEvent(self, event):
if event.button()==QtCore.Qt.RightButton:
......@@ -534,7 +565,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.saveDetectParams(self.dataset)
img = self.subimg.copy()
kwargs = {}
for ui, name, valuefunc in self.parameters:
for ui, name, valuefunc, showbtn in self.parameters:
kwargs[name] = valuefunc()
self.seg.setParameters(**kwargs)
seedradius = self.seedradiusedit.value()
......@@ -561,7 +592,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.dataset.ramanscandone = False
self.dataset.mode = "opticalscan"
self.dataset.save()
self.imageUpdate.emit()
self.imageUpdate.emit(self.view.microscopeMode)
@QtCore.pyqtSlot()
def cancelThread(self):
......@@ -578,6 +609,9 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.pdetectsub.setEnabled(True)
self.pclear.setEnabled(True)
def raiseWarning(self, warning):
QtWidgets.QMessageBox.critical(self, "Warning", warning)
@QtCore.pyqtSlot()
def detectParticles(self):
self.saveDetectParams(self.dataset)
......@@ -602,7 +636,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
self.thread = None
self.unBlockUI()
self.pdetectall.setText("Detect all")
self.imageUpdate.emit()
self.imageUpdate.emit(self.view.microscopeMode)
if self.dataset is not None:
self.setWindowTitle(str(len(self.dataset.ramanpoints)) + " Particles")
else:
......@@ -614,7 +648,7 @@ class ParticleDetectionView(QtWidgets.QWidget):
if self.dataset is not None:
seedpoints = self.dataset.seedpoints
deletepoints = self.dataset.seeddeletepoints
for ui, name, valuefunc in self.parameters:
for ui, name, valuefunc, showbtn in self.parameters:
kwargs[name] = valuefunc()
seedradius = self.seedradiusedit.value()
self.seg.setParameters(**kwargs)
......
......@@ -21,25 +21,34 @@ If not, see <https://www.gnu.org/licenses/>.
from PyQt5 import QtCore, QtWidgets, QtGui
from sampleview import SampleView
from scalebar import ScaleBar
from ramancontrol import defaultPath
from ramancom.ramancontrol import defaultPath
from ramancom.ramanSwitch import RamanSwitch
from colorlegend import ColorLegend
import os
class MeasureParticleWindow(QtWidgets.QMainWindow):
def __init__(self):
def __init__(self, logpath):
super(MeasureParticleWindow, self).__init__()
self.setWindowTitle("GEPARD")
self.resize(900, 700)
self.view = SampleView()
self.view = SampleView(logpath)
self.view.imparent = self
self.view.ScalingChanged.connect(self.scalingChanged)
self.scalebar = ScaleBar(self)
self.legend = ColorLegend(self)
self.ramanSwitch = RamanSwitch(self)
self.view.ScalingChanged.connect(self.scalebar.updateScale)
mdiarea = QtWidgets.QMdiArea(self)
mdiarea.addSubWindow(self.scalebar)
mdiarea.addSubWindow(self.legend)
mdiarea.addSubWindow(self.ramanSwitch)
self.legend.hide()
self.ramanSwitch.hide()
subview = mdiarea.addSubWindow(self.view)
subview.showMaximized()
subview.setWindowFlags(QtCore.Qt.FramelessWindowHint)
......@@ -53,7 +62,9 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
self.updateModes()
def resizeEvent(self, event):
self.scalebar.move(0,self.height()-self.scalebar.height())
self.scalebar.move(0,self.height()-self.scalebar.height()-30)
self.legend.move(self.width()-self.legend.width()-130, 10)
self.ramanSwitch.move(5, 5)
def closeEvent(self, event):
self.view.closeEvent(event)
......@@ -73,6 +84,16 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
self.fname = str(fileName)
self.view.open(self.fname)
self.scalingChanged(1.)
@QtCore.pyqtSlot()
def importProject(self, fileName=False):
if fileName is False:
fileName = QtWidgets.QFileDialog.getOpenFileName(self, "Import Zeiss Zen Project",
defaultPath, "*.xml")[0]
if fileName:
self.fname = str(fileName)
self.view.importProject(self.fname)
self.scalingChanged(1.)
@QtCore.pyqtSlot()
def new(self, fileName=False):
......@@ -80,9 +101,12 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
fileName = QtWidgets.QFileDialog.getSaveFileName(self, "Create New Project",
defaultPath, "*.pkl")[0]
if fileName:
self.fname = str(fileName)
self.view.new(self.fname)
self.scalingChanged(1.)
if fileName.find(' ') < 0:
self.fname = str(fileName)
self.view.new(self.fname)
self.scalingChanged(1.)
else:
QtWidgets.QMessageBox.critical(self, "Error", "File path must not contain spaces.")
@QtCore.pyqtSlot()
def about(self):
......@@ -101,6 +125,10 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
self.openAct.setShortcut("Ctrl+O")
self.openAct.triggered.connect(self.open)
self.importAct = QtWidgets.QAction("&Import Project...", self)
self.importAct.setShortcut("Ctrl+I")
self.importAct.triggered.connect(self.importProject)
self.newAct = QtWidgets.QAction("&New Measurement...", self)
self.newAct.setShortcut("Ctrl+N")
self.newAct.triggered.connect(self.new)
......@@ -152,13 +180,20 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
self.ramanScanAct.setCheckable(True)
self.ramanScanAct.triggered.connect(QtCore.pyqtSlot()(lambda :self.view.switchMode("RamanScan")))
self.particelAnalysisAct = QtWidgets.QAction("Particle analysis", self)
self.particelAnalysisAct.setEnabled(False)
self.particelAnalysisAct.setCheckable(True)
self.particelAnalysisAct.triggered.connect(QtCore.pyqtSlot()(lambda :self.view.switchMode("ParticleAnalysis")))
self.snapshotAct = QtWidgets.QAction("Save Screenshot", self)
self.snapshotAct.triggered.connect(self.view.takeScreenshot)
self.snapshotAct.setDisabled(True)
self.configRamanCtrlAct = QtWidgets.QAction("Configure Raman Control", self)
self.configRamanCtrlAct.triggered.connect(self.view.configureRamanControl)
if self.view.simulatedRaman:
self.configRamanCtrlAct.setDisabled(True)
def updateModes(self, active=None, maxenabled=None):
ose, osc, pde, pdc, rse, rsc, pae, pac = [False]*8
if maxenabled=="OpticalScan":
......@@ -190,12 +225,14 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
def unblockUI(self, connected):
self.openAct.setEnabled(True)
self.importAct.setEnabled(True)
self.newAct.setEnabled(True)
self.updateConnected(connected)
self.exitAct.setEnabled(True)
def blockUI(self):
self.openAct.setEnabled(False)
self.importAct.setEnabled(False)
self.newAct.setEnabled(False)
self.connectRamanAct.setEnabled(False)
self.disconnectRamanAct.setEnabled(False)
......@@ -216,10 +253,11 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
def createMenus(self):
self.fileMenu = QtWidgets.QMenu("&File", self)
self.fileMenu.addAction(self.newAct)
self.fileMenu.addAction(self.importAct)
self.fileMenu.addAction(self.openAct)
self.fileMenu.addSeparator()
self.fileMenu.addAction(self.exitAct)
self.viewMenu = QtWidgets.QMenu("&View", self)
self.viewMenu.addAction(self.zoomInAct)
self.viewMenu.addAction(self.zoomOutAct)
......@@ -232,19 +270,26 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
self.modeMenu.addAction(self.detectParticleAct)
self.modeMenu.addAction(self.particelAnalysisAct)
self.toolsMenu = QtWidgets.QMenu("&Tools")
self.toolsMenu.addAction(self.snapshotAct)
self.toolsMenu.addAction(self.configRamanCtrlAct)
self.helpMenu = QtWidgets.QMenu("&Help", self)
self.helpMenu.addAction(self.aboutAct)
self.menuBar().addMenu(self.fileMenu)
self.menuBar().addMenu(self.viewMenu)
self.menuBar().addMenu(self.modeMenu)
self.menuBar().addMenu(self.toolsMenu)
self.menuBar().addMenu(self.helpMenu)
def createToolBar(self):
self.toolbar = QtWidgets.QToolBar("Tools")
self.toolbar.setIconSize(QtCore.QSize(100,50))
self.toolbar.addAction(self.aboutAct)
self.toolbar.addAction(self.newAct)
self.toolbar.addAction(self.importAct)
self.toolbar.addAction(self.openAct)
self.toolbar.addSeparator()
self.toolbar.addAction(self.connectRamanAct)
......@@ -260,16 +305,29 @@ class MeasureParticleWindow(QtWidgets.QMainWindow):
self.addToolBar(QtCore.Qt.LeftToolBarArea, self.toolbar)
if __name__ == '__main__':
import sys
from time import localtime, strftime
logname = os.path.join(os.path.split(__file__)[0], os.path.join("logfile.txt"))
fp = open(logname, "a")
sys.stderr = fp
sys.stdout = fp
print("starting GEPARD at: " + strftime("%d %b %Y %H:%M:%S", localtime()))
app = QtWidgets.QApplication(sys.argv)
meas = MeasureParticleWindow()
app.setApplicationName("GEPARD") # appname needed for logpath
logpath = QtCore.QStandardPaths.writableLocation(
QtCore.QStandardPaths.AppLocalDataLocation)
fp = None
if logpath != "":
if not os.path.exists(logpath):
os.mkdir(logpath)
logname = os.path.join(logpath, 'logfile.txt')
fp = open(logname, "a")
sys.stderr = fp
sys.stdout = fp
print("starting GEPARD at: " + strftime("%d %b %Y %H:%M:%S", localtime()),
flush=True)
meas = MeasureParticleWindow(logpath)
meas.showMaximized()
ret = app.exec_()
\ No newline at end of file
ret = app.exec_()
if fp is not None:
fp.close()
\ No newline at end of file
......@@ -24,15 +24,11 @@ import cv2
import os
def cv2imread_fix(fname, flags=cv2.IMREAD_COLOR):
if not os.path.exists(fname): #This seems to be a source of potential errors. Having these lines here probably aids finding errors for other groups?
print('Error, image file not found\Please check if the used save-Image command returns before the image was fully written to disk.')
return
with open(fname, "rb") as fp:
cont = fp.read()
img = cv2.imdecode(np.fromstring(cont, dtype=np.uint8), flags)
return img
return None
with open(fname, "rb") as fp:
cont = fp.read()
img = cv2.imdecode(np.fromstring(cont, dtype=np.uint8), flags)
return img
return None
def cv2imwrite_fix(fname, img, params=None):
pathname, ext = os.path.splitext(fname)
......
This diff is collapsed.
......@@ -28,12 +28,23 @@ except ImportError:
os.environ["NO_WITEC_CONTROL"] = "True"
from time import sleep, time
from ramanbase import RamanBase
try: #when running the witectesting, the paths have to be differently, as it is in the same directory as the other raman com modules
from ramancom.ramanbase import RamanBase
from ramancom.configRaman import RamanSettingParam
except:
from ramanbase import RamanBase
from configRaman import RamanSettingParam
from socket import gethostname
class WITecCOM(RamanBase):
CLSID = "{C45E77CE-3D66-489A-B5E2-159F443BD1AA}"
magn = 20
ramanParameters = [RamanSettingParam('IntegrationTime (s)', 'double', default=0.5, minVal=0.01, maxVal=100),
RamanSettingParam('Accumulations', 'int', default=5, minVal=1, maxVal=100)]
def __init__(self, hostname=None):
super().__init__()
if hostname is None:
......@@ -193,7 +204,7 @@ class WITecCOM(RamanBase):
z = self.PosZCurUserFloatMan.GetSingleValueAsDouble()[1]
return z
def moveToAbsolutePosition(self, x, y, z=None, epsxy=0.11, epsz=0.011):
def moveToAbsolutePosition(self, x, y, z=None, epsxy=0.11, epsz=0.011, debugReturn=False, measurementRunning=False):
assert self.connected
initpos = self.getPosition()
# move only if new position is really different; repeat if new position is ignored (happens some times)
......@@ -208,9 +219,8 @@ class WITecCOM(RamanBase):
while distance > epsxy:# and (lastpos is None or lastpos!=curpos):
curpos = self.getPosition()
distance = max(abs(curpos[0]-x), abs(curpos[1]-y))
# if ((time()-t0>0.5) and max(abs(curpos[0]-initpos[0]), abs(curpos[1]-initpos[1]))<epsxy) or (time()-t0>10.):
if ((time()-t0>2) and max(abs(curpos[0]-initpos[0]), abs(curpos[1]-initpos[1]))<epsxy) or (time()-t0>10.):
print("WARNING: signal ignored; time: {} s, x, y: {}, {}, curPos: {}, initPos: {}".format((time()-t0), x, y, curpos, initpos))
if ((time()-t0>0.5) and max(abs(curpos[0]-initpos[0]), abs(curpos[1]-initpos[1]))<epsxy) or (time()-t0>10.):
print("WARNING: signal ignored:", time()-t0, x, y, curpos, initpos)
sys.stdout.flush()
break
sleep(.01)
......@@ -244,7 +254,7 @@ class WITecCOM(RamanBase):
self.ImageSaveMan.OperateTrigger()
sleep(.1)
def getImageDimensions(self):
def getImageDimensions(self, mode = 'df'):
""" Get the image width and height in um and the orientation angle in degrees.
"""
assert self.connected
......@@ -262,16 +272,17 @@ class WITecCOM(RamanBase):
if not Busy:
break
def initiateTimeSeriesScan(self, label, numberofscans, accumulations, integrtime):
def initiateMeasurement(self, ramanSettings):
assert self.connected
self.timeseries = numberofscans
self.TimeSeriesSlowNameMan.SetValue(label)
self.TimeSeriesSlowNumMeasurementsMan.SetValue(numberofscans)
self.TimeSeriesSlowNumAccumulationsMan.SetValue(accumulations)
self.TimeSeriesSlowIntTimeMan.SetValue(integrtime)
self.timeseries = ramanSettings['numPoints']
self.TimeSeriesSlowNameMan.SetValue(ramanSettings['filename'])
self.TimeSeriesSlowNumMeasurementsMan.SetValue(ramanSettings['numPoints'])
self.TimeSeriesSlowNumAccumulationsMan.SetValue(ramanSettings['Accumulations'])
self.TimeSeriesSlowIntTimeMan.SetValue(ramanSettings['IntegrationTime (s)'])
self.TimeSeriesSlowModeMan.SetValueNumeric(0)
self.TimeSeriesSlowNumMan.SetValue(0)
self.TimeSeriesSlowStartMan.OperateTrigger()
sleep(0.1)
t1 = time()
while True:
......@@ -286,7 +297,7 @@ class WITecCOM(RamanBase):
t1 = time()
def nextTimeSeriesScan(self, num):
def triggerMeasurement(self, num):
assert self.timeseries
self.TimeSeriesSlowNextMan.OperateTrigger()
# Wait until sequencer has finished
......@@ -313,4 +324,5 @@ class WITecCOM(RamanBase):
if num==self.timeseries-1:
self.timeseries = False
\ No newline at end of file
def finishMeasurement(self):
pass
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Sep 21 12:23:20 2018
@author: brandt
"""
from PyQt5 import QtWidgets
class RamanConfigWin(QtWidgets.QWidget):
def __init__(self, parent=None):
super(RamanConfigWin, self).__init__()
self.parent = parent
if parent is not None:
self.ramancontrol = self.parent.ramanctrl
self.setGeometry(200, 200, 400, 300)
self.setWindowTitle('Configure Raman Control')
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
generalGroup = QtWidgets.QGroupBox('General settings')
witecGroup = QtWidgets.QGroupBox('WITec settings')
renishawGroup = QtWidgets.QGroupBox('Renishaw settings')
generalLayout = QtWidgets.QFormLayout()
self.magnSpinBox = QtWidgets.QSpinBox()
if parent is not None:
self.magnSpinBox.setValue(self.ramancontrol.magn)
else:
self.magnSpinBox.setValue(20)
generalLayout.addRow(QtWidgets.QLabel('Magnification (x)'), self.magnSpinBox)
generalGroup.setLayout(generalLayout)
witecLayout = QtWidgets.QFormLayout()
witecLayout.addRow(QtWidgets.QLabel('Nothing yet implemented...'))
witecGroup.setLayout(witecLayout)
renishawLayout = QtWidgets.QFormLayout()
self.ren_BFwidth = QtWidgets.QDoubleSpinBox()
self.ren_BFheight = QtWidgets.QDoubleSpinBox()
self.ren_DFwidth = QtWidgets.QDoubleSpinBox()
self.ren_DFheight = QtWidgets.QDoubleSpinBox()
for spinbox in [self.ren_BFheight, self.ren_BFwidth, self.ren_DFheight, self.ren_DFwidth]:
spinbox.setMinimum(0)
spinbox.setMaximum(1e7)
if self.parent is not None:
self.ren_BFwidth.setValue(self.ramancontrol.cam_bf_dims[0])
self.ren_BFheight.setValue(self.ramancontrol.cam_bf_dims[1])
self.ren_DFwidth.setValue(self.ramancontrol.cam_df_dims[0])
self.ren_DFheight.setValue(self.ramancontrol.cam_df_dims[1])
self.getBFImgDimBtn = QtWidgets.QPushButton('Read dimensions from File')
self.getBFImgDimBtn.released.connect(self.makeReadImgDimsLambda(self.ren_BFwidth, self.ren_BFheight))
self.getDFImgDimBtn = QtWidgets.QPushButton('Read dimensions from File')
self.getDFImgDimBtn.released.connect(self.makeReadImgDimsLambda(self.ren_DFwidth, self.ren_DFheight))
renishawLayout.addRow(QtWidgets.QLabel('Bright Field Camera Settings'))
renishawLayout.addRow(QtWidgets.QLabel('Image widht (um)'), self.ren_BFwidth)
renishawLayout.addRow(QtWidgets.QLabel('Image height (um)'), self.ren_BFheight)
renishawLayout.addRow(self.getBFImgDimBtn)
renishawLayout.addRow(QtWidgets.QLabel())
renishawLayout.addRow(QtWidgets.QLabel('Dark Field Camera Settings'))
renishawLayout.addRow(QtWidgets.QLabel('Image widht (um)'), self.ren_DFwidth)
renishawLayout.addRow(QtWidgets.QLabel('Image height (um)'), self.ren_DFheight)
renishawLayout.addRow(self.getDFImgDimBtn)
renishawGroup.setLayout(renishawLayout)
layout.addWidget(generalGroup, 0, 0, 1, 2)
layout.addWidget(witecGroup, 1, 0)
layout.addWidget(renishawGroup, 1, 1)
closeBtn = QtWidgets.QPushButton('Save and Exit')
closeBtn.released.connect(self.close)
layout.addWidget(closeBtn, 2, 0)
def makeReadImgDimsLambda(self, width_spinbox, height_spinbox):
return lambda: self.readImgDims(width_spinbox, height_spinbox)
def readImgDims(self, width_spinbox, height_spinbox):
try:
import exifread
except:
QtWidgets.QMessageBox.critical(self, 'Error', 'Failed to load exifread module!\nPlease install exifread module to use this function')
return
magn, ok = QtWidgets.QInputDialog.getInt(self, 'Enter Magnification', 'Magnification = ', 20)
if not ok:
return
fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Select jpeg file', 'C://', 'Image file (*.jpg *.jpeg)')[0]
if len(fname) == 0:
return
def stringDivision(string):
a, b = string.split('/')
a, b = float(a), float(b)
return a/b
try:
file = open(fname, 'rb')
tags = exifread.process_file(file)
for index, key in enumerate(tags):
if index == 3:
startVals = tags[key].printable
if index == 4:
ranges = tags[key].printable
startVals = startVals.replace('[', '').replace(']', '').replace(' ', '') #remove brackets and space
ranges = ranges.replace('[', '').replace(']', '').replace(' ', '') #remove brackets and space
startX, startY = startVals.split(',')
rangeX, rangeY = ranges.split(',')
startX, startY = stringDivision(startX), stringDivision(startY)
rangeX, rangeY = stringDivision(rangeX), stringDivision(rangeY)
width_spinbox.setValue(rangeX*magn)
height_spinbox.setValue(rangeY*magn)
except:
QtWidgets.QMessageBox.critical(self, 'Error', 'Reading dimensions from jpeg failed')
def closeEvent(self, event):
ret = QtWidgets.QMessageBox.question(self, '', 'Save Data to RamanControl?', QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes)
if ret == QtWidgets.QMessageBox.Yes:
if self.parent is not None:
if self.ramancontrol.hostname == 'AN-Raman-Renishaw':
self.ramancontrol.cam_bf_dims = [self.ren_BFwidth.value(), self.ren_BFheight.value()]
self.ramancontrol.cam_df_dims = [self.ren_DFwidth.value(), self.ren_DFheight.value()]
self.ramancontrol.magn = self.magnSpinBox.value()
else:
print('fake saved')
class RamanSettingParam(object):
def __init__(self, name, dtype, default=None, minVal=None, maxVal=None, valList=None, openFileType=None):
self.name = name
self.dtype = dtype
self.value = default
self.minVal = minVal
self.maxVal = maxVal
self.valList = valList
self.openFileType = openFileType
if not self.hasValidType():
print('erroreneous type in setting parameter:', self.dtype)
def hasValidType(self):
if self.dtype in ['int', 'double', 'checkBox', 'combobox', 'selectBtn']:
return True
else:
return False
def value_of(self, obj):
if self.dtype in ['int', 'double']:
return obj.value()
elif self.dtype == 'checkBox':
return obj.isChecked()
elif self.dtype == 'combobox':
return obj.currentText()
elif self.dtype == 'selectBtn':
return obj.text()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
setWin = RamanConfigWin()
setWin.show()
ret = app.exec_()
\ No newline at end of file
import sys, os
from PyQt5 import QtWidgets, QtCore
class RamanSwitch(QtWidgets.QMdiSubWindow):
def __init__(self, parent=None):
super(RamanSwitch, self).__init__()
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint|QtCore.Qt.FramelessWindowHint)
self.setFixedSize(220, 60)
self.parent = parent
self.sampleview = None
layout = self.layout()
self.df_btn = QtWidgets.QRadioButton('Darkfield')
self.bf_btn = QtWidgets.QRadioButton('Brightfield')
self.bf_btn.setChecked(True)
self.df_btn.clicked.connect(self.updateMode)
self.bf_btn.clicked.connect(self.updateMode)
groupLayout = QtWidgets.QHBoxLayout()
group = QtWidgets.QGroupBox('Micropscope Mode')
groupLayout.addWidget(self.bf_btn)
groupLayout.addWidget(self.df_btn)
group.setLayout(groupLayout)
layout.addWidget(group)
def connectToSampleView(self):
if self.parent is not None:
self.sampleview = self.parent.view
if os.path.exists(self.sampleview.dataset.getImageName()):
mode = self.sampleview.dataset.imagescanMode
print('fullimage was acquired in', mode)
if mode == 'df':
self.df_btn.setChecked(True)
else:
self.df_btn.setChecked(False)
self.setDisabled(True)
def updateMode(self):
if self.sampleview is not None:
if self.df_btn.isChecked():
self.sampleview.microscopeMode = 'df'
self.sampleview.announceScaling()
else:
self.sampleview.microscopeMode = 'bf'
self.sampleview.announceScaling()
print('mic-mode is now', self.sampleview.microscopeMode)
#class QSlideSwitchPrivate(QtCore.QObject):
#
# def __init__(self, q):
# super(QSlideSwitchPrivate, self).__init__()
#
# self._position = 0
# self._sliderShape = QtCore.QRectF()
# self._gradient = QtGui.QLinearGradient()
# self._gradient.setSpread(QtGui.QGradient.PadSpread)
# self._qPointer = q
#
# self.animation = QtCore.QPropertyAnimation(self)
# self.animation.setTargetObject(self)
# self.animation.setPropertyName(b"position")
# self.animation.setStartValue(0)
# self.animation.setEndValue(1)
# self.animation.setDuration(300)
# self.animation.setEasingCurve(QtCore.QEasingCurve.InOutExpo)
#
# def __del__(self):
# del self.animation
#
# @QtCore.pyqtProperty(float)
# def position(self):
# return self._position
#
# @position.setter
# def position(self, value):
# self._position = value
# self._qPointer.repaint()
#
# def drawSlider(self, painter):
# margin = 3
# r = self._qPointer.rect().adjusted(0,0,-1,-1)
# dx = (r.width() - self._sliderShape.width()) * self._position
# sliderRect = self._sliderShape.translated(dx, 0)
# painter.setPen(QtCore.Qt.NoPen)
#
# # basic settings
# shadow = self._qPointer.palette().color(QtGui.QPalette.Dark)
# light = self._qPointer.palette().color(QtGui.QPalette.Light)
# button = self._qPointer.palette().color(QtGui.QPalette.Button)
#
# # draw background
# # draw outer background
# self._gradient.setColorAt(0, shadow.darker(130))
# self._gradient.setColorAt(1, light.darker(130))
# self._gradient.setStart(0, r.height())
# self._gradient.setFinalStop(0, 0)
# painter.setBrush(self._gradient)
# painter.drawRoundedRect(r, 15, 15)
#
# # draw background
# # draw inner background
# self._gradient.setColorAt(0, shadow.darker(140))
# self._gradient.setColorAt(1, light.darker(160))
# self._gradient.setStart(0, 0)
# self._gradient.setFinalStop(0, r.height())
# painter.setBrush(self._gradient)
# painter.drawRoundedRect(r.adjusted(margin, margin, -margin, -margin), 15, 15)
#
# # draw slider
# self._gradient.setColorAt(0, button.darker(130))
# self._gradient.setColorAt(1, button)
#
# # draw outer slider
# self._gradient.setStart(0, r.height())
# self._gradient.setFinalStop(0, 0)
# painter.setBrush(self._gradient)
# painter.drawRoundedRect(sliderRect.adjusted(margin, margin, -margin, -margin), 10, 15)
#
# # draw inner slider
# self._gradient.setStart(0, 0)
# self._gradient.setFinalStop(0, r.height())
# painter.setBrush(self._gradient)
# painter.drawRoundedRect(sliderRect.adjusted(2.5 * margin, 2.5 * margin, -2.5 * margin, - 2.5 * margin), 5, 15)
#
# font = self._qPointer.font()
# self._gradient.setColorAt(0, light)
# self._gradient.setColorAt(1, shadow)
# self._gradient.setStart(0, r.height() / 2.0 + font.pointSizeF())
# self._gradient.setFinalStop(0, r.height() / 2.0 - font.pointSizeF())
# painter.setFont(font)
# painter.setPen(QtCore.Qt.black)
# painter.drawText(0, 0, r.width() / 2, r.height()-1, QtCore.Qt.AlignCenter, "Brightfield")
# painter.drawText( r.width() / 2, 0, r.width() / 2, r.height() - 1, QtCore.Qt.AlignCenter, "Darkfield")
#
# def updateSliderRect(self, size):
# self._sliderShape.setWidth(size.width() / 2.0)
# self._sliderShape.setHeight(size.height() - 1.0)
#
# @QtCore.pyqtSlot(bool, name='animate')
# def animate(self, checked):
# self.animation.setDirection(QtCore.QPropertyAnimation.Forward if checked else QtCore.QPropertyAnimation.Backward)
# self.animation.start()
#
#
#class QSlideSwitch(QtWidgets.QAbstractButton):
# def __init__(self, parent = None):
# super(QSlideSwitch, self).__init__(parent)
#
# self.d_ptr = QSlideSwitchPrivate( self )
# self.clicked.connect( self.d_ptr.animate )
# self.d_ptr.animation.finished.connect( self.update )
#
# def __del__(self):
# del self.d_ptr
#
# def sizeHint(self):
# return QtCore.QSize(48, 28)
#
# def hitButton(self, point):
# return self.rect().contains(point)
#
# def paintEvent(self, event):
# painter = QtGui.QPainter(self)
# painter.setRenderHint(QtGui.QPainter.Antialiasing)
# self.d_ptr.drawSlider(painter)
#
# def resizeEvent(self, event):
# self.d_ptr.updateSliderRect(event.size())
# self.repaint()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
win = RamanSwitch()
win.show()
sys.exit(app.exec_())
\ No newline at end of file
......@@ -44,7 +44,7 @@ class RamanBase(object):
def getUserZ(self):
raise NotImplementedError
def moveToAbsolutePosition(self, x, y, z=None, epsxy=0.11, epsz=0.011):
def moveToAbsolutePosition(self, x, y, z=None, epsxy=0.11, epsz=0.011, debugReturn=False, measurementRunning=False):
raise NotImplementedError
def moveZto(self, z, epsz=0.011):
......@@ -53,7 +53,7 @@ class RamanBase(object):
def saveImage(self, fname):
raise NotImplementedError
def getImageDimensions(self):
def getImageDimensions(self, mode = 'df'):
""" Get the image width and height in um and the orientation angle in degrees.
"""
raise NotImplementedError
......@@ -61,8 +61,11 @@ class RamanBase(object):
def startSinglePointScan(self):
raise NotImplementedError
def initiateTimeSeriesScan(self, label, numberofscans, accumulations, integrtime):
def initiateMeasurement(self, ramanSettings):
raise NotImplementedError
def nextTimeSeriesScan(self, num):
def triggerMeasurement(self, num):
raise NotImplementedError
def finishMeasurement(self):
raise NotImplementedError
\ No newline at end of file
......@@ -23,12 +23,14 @@ import configparser
__all__ = ["RamanControl", "defaultPath", "simulatedRaman"]
defaultPath = os.path.split(__file__)[0]
defaultPath = os.path.dirname(os.path.split(__file__)[0])
config = configparser.ConfigParser()
config.read(os.path.join(defaultPath, 'gepard.cfg'))
interface = "SIMULATED_RAMAN_CONTROL"
interface = "RENISHAW_CONTROL"
try:
defaultPath = config["Defaults"]["file_path"]
......@@ -41,15 +43,33 @@ except KeyError:
pass
if interface == "SIMULATED_RAMAN_CONTROL":
from simulatedraman import SimulatedRaman
from ramancom.simulatedraman import SimulatedRaman
RamanControl = SimulatedRaman
print("WARNING: using only simulated raman control!")
simulatedRaman = True
elif interface == "WITEC_CONTROL":
from WITecCOM import WITecCOM
from ramancom.WITecCOM import WITecCOM
RamanControl = WITecCOM
RamanControl.magn = int(config["General Microscope Setup"]["magnification"]) # not yet implemented in WITecCOM, but would probably be a good idea...
simulatedRaman = False
elif interface == "RENISHAW_CONTROL":
raise NotImplementedError
from ramancom.renishawcom import RenishawCOM
RamanControl = RenishawCOM
RamanControl.magn = int(config["General Microscope Setup"]["magnification"])
try:
bf_dims = config["Renishaw"]["img_size_BF"].split('*')
df_dims = config["Renishaw"]["img_size_DF"].split('*')
RamanControl.cam_bf_dims = [float(bf_dims[0]), float(bf_dims[1])]
RamanControl.cam_df_dims = [float(df_dims[0]), float(df_dims[1])]
except:
print('Invalid image dimensions in config file!')
# RamanControl.defaultMeasTemplate = config["Renishaw"]["defaultMeasTemplate"]
RamanControl.measTemplatePath = config["Renishaw"]["measTemplatePath"]
print(RamanControl.measTemplatePath)
RamanControl.ramanParameters = RamanControl.updateRamanParameters(RamanControl, RamanControl.measTemplatePath)
simulatedRaman = False
\ 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/>.
Simualted Raman interface module for testing without actual raman system connected
"""
from time import sleep
import numpy as np
from shutil import copyfile
from ramanbase import RamanBase
class SimulatedRaman(RamanBase):
def __init__(self):
super().__init__()
self.currentpos = None, 0., 0.
self.currentZ = 0.
# some plausible data to simulate consecutively changing positions
self.positionlist = np.array([[ 1526. , -1379.9, -131. ],
[ 3762.5, -1197.7, -138.1],
[ 2313.7, -2627.2, -138.1],
[ 2704.1, -1788.2, -138.1],
[ 3884. , -2650.8, -138.1]])
self.znum = 4
self.gridnum = 36
self.positionindex = 0
self.imageindex = 0
def getRamanPositionShift(self):
return 0., 0.
def connect(self):
self.connected = True
self.imageindex = 0
return True
def disconnect(self):
self.connected = False
def getPosition(self):
if self.currentpos[0] is None:
pos = self.positionlist[self.positionindex]
self.positionindex = (self.positionindex+1)%len(self.positionlist)
else:
pos = self.currentpos
return pos
def getSoftwareZ(self):
return self.currentpos[2]
def getUserZ(self):
assert self.connected
if self.currentpos[0] is None:
return self.positionlist[self.positionindex][2]
else:
return self.currentZ
def moveToAbsolutePosition(self, x, y, z=None, epsxy=0.11, epsz=0.011):
assert self.connected
if z is None:
self.currentpos = x, y, self.currentpos[2]
else:
self.currentpos = x, y, z
sleep(1.)
def moveZto(self, z, epsz=0.011):
assert self.connected
self.currentpos = self.currentpos[0], self.currentpos[1], z
def saveImage(self, fname):
assert self.connected
copyfile(f"fakeData/image.bmp", fname)
self.imageindex = (self.imageindex+1)%(self.znum*self.gridnum)
sleep(.01)
def getImageDimensions(self):
""" Get the image width and height in um and the orientation angle in degrees.
"""
assert self.connected
width, height, angle = 463.78607177734375, 296.0336608886719, -0.04330849274992943
return width, height, angle
def startSinglePointScan(self):
assert self.connected
print("Fake scan")
sleep(.3)
def initiateTimeSeriesScan(self, label, numberofscans, accumulations, integrtime):
assert self.connected
print("Scanning ",numberofscans, "particle positions")
self.timeseries = numberofscans
sleep(.3)
def nextTimeSeriesScan(self, num):
assert self.timeseries
print("Scan number:", num)
sleep(.3)
if num==self.timeseries-1:
# -*- 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