Commit ac123cc1 authored by Lars Bittrich's avatar Lars Bittrich

Initial commit of current project status. Some modification will be needed...

Initial commit of current project status. Some modification will be needed before this will work without manual adaptations…
parent b20a8281
# GEPARD
GEPARD - Gepard-Enabled PARticle Detection for Raman microscopes.
Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für
Polymerforschung Dresden e. V. <bittrich-lars@ipfdd.de>
Requirements:
* python 3.6, PyQt5, OpenCV 3.4.1, numpy 1.14, scikit-image 0.13.1, scipy 1.1.0,
win32com, pythoncom, cython 0.28
* we advise the use of Anaconda (python 3.6): https://www.anaconda.com/download
this package contains most of the python libraries
however, opencv and scikit-image are missing
start anaconda prompt and install opencv:
pip install opencv-python
conda install scikit-image
* we recommend working with a 64bit OS and also a python interpreter compiled
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
python setuptsp.py
please note: for this step a valid compiler needs to be installed in the
system; Otherwise use the precompiled tsp-module
If you plan on using the WITec Raman interface to control your device, please
note: You use this interface at your OWN RISK! Make sure, that no obstacles
block the objective and that you UNDERSTAND and VALIDATE the code, that controls
the microscope! Start with "witectesting.py", which should read and move within
small margins.
At the moment the program is an executable python script. Copy the folder with
all its content to some place and run (e.g. using anaconda prompt):
python gepard.py
It is possible to create a windows link file, that executes python with the
gepard script as an argument and the working directory pointing to the folder
containing gepard for convenience.
# -*- coding: utf-8 -*-
"""
GEPARD - Gepard-Enabled PARticle Detection
Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für
Polymerforschung Dresden e. V. <bittrich-lars@ipfdd.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program, see COPYING.
If not, see <https://www.gnu.org/licenses/>.
"""
import os
import sys
try:
import pythoncom
import win32com.client
except ImportError:
os.environ["NO_WITEC_CONTROL"] = "True"
from time import sleep, time
from ramanbase import RamanBase
from socket import gethostname
class WITecCOM(RamanBase):
CLSID = "{C45E77CE-3D66-489A-B5E2-159F443BD1AA}"
hostname = "unknown"
def __init__(self, hostname=None):
super().__init__()
if hostname is None:
hostname = gethostname()
self.IBUCSAccess = win32com.client.DispatchEx(self.CLSID, machine=hostname,
clsctx=pythoncom.CLSCTX_REMOTE_SERVER)
def connect(self):
if not self.IBUCSAccess.RequestWriteAccess(True):
self.connected = False
return False
IBUCSCore = win32com.client.CastTo(self.IBUCSAccess, 'IBUCSCore')
self.PosXInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SamplePositioning|AbsolutePositionX")
self.PosYInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SamplePositioning|AbsolutePositionY")
self.PosZInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|ScanTable|PositionMicroscopeZ")
self.GoToInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SamplePositioning|GoToPosition")
self.PosXCurInterface = IBUCSCore.GetSubSystemDefaultInterface("Status|Software|SamplePositioner|CurrentPositionX")
self.PosYCurInterface = IBUCSCore.GetSubSystemDefaultInterface("Status|Software|SamplePositioner|CurrentPositionY")
self.PosZCurInterface = IBUCSCore.GetSubSystemDefaultInterface("Status|Software|SamplePositioner|CurrentPositionZ")
self.PosZCurUserInterface = IBUCSCore.GetSubSystemDefaultInterface("Status|Hardware|Controller|DataChannels|StepperMotorPosition")
# Get Float Manipulators
self.PosXFloatMan = win32com.client.CastTo(self.PosXInterface, 'IBUCSFloat')
self.PosYFloatMan = win32com.client.CastTo(self.PosYInterface, 'IBUCSFloat')
self.PosZFloatMan = win32com.client.CastTo(self.PosZInterface, 'IBUCSFloat')
self.PosXCurFloatMan = win32com.client.CastTo(self.PosXCurInterface, 'IBUCSStatusContainer')
self.PosYCurFloatMan = win32com.client.CastTo(self.PosYCurInterface, 'IBUCSStatusContainer')
self.PosZCurFloatMan = win32com.client.CastTo(self.PosZCurInterface, 'IBUCSStatusContainer')
self.PosZCurUserFloatMan = win32com.client.CastTo(self.PosZCurUserInterface, 'IBUCSStatusContainer')
self.GoToTrigger = win32com.client.CastTo(self.GoToInterface, 'IBUCSTrigger')
self.ImageNameInterface = IBUCSCore.GetSubSystemDefaultInterface("MultiComm|MultiCommVideoSystem|BitmapFileName")
self.ImageNameMan = win32com.client.CastTo(self.ImageNameInterface, 'IBUCSString')
self.ImageSaveInterface = IBUCSCore.GetSubSystemDefaultInterface("MultiComm|MultiCommVideoSystem|SaveColorBitmapToFile")
self.ImageSaveMan = win32com.client.CastTo(self.ImageSaveInterface, 'IBUCSTrigger')
self.ImageHeightInterface = IBUCSCore.GetSubSystemDefaultInterface("MultiComm|MicroscopeControl|Video|Calibration|ImageHeightMicrons")
self.ImageHeightMan = win32com.client.CastTo(self.ImageHeightInterface, 'IBUCSFloat')
self.ImageWidthInterface = IBUCSCore.GetSubSystemDefaultInterface("MultiComm|MicroscopeControl|Video|Calibration|ImageWidthMicrons")
self.ImageWidthMan = win32com.client.CastTo(self.ImageWidthInterface, 'IBUCSFloat')
self.ImageRotationInterface = IBUCSCore.GetSubSystemDefaultInterface("MultiComm|MicroscopeControl|Video|Calibration|RotationDegrees")
self.ImageRotationMan = win32com.client.CastTo(self.ImageRotationInterface, 'IBUCSFloat')
self.SequencerStartInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SequencerSingleSpectrum|Start")
self.SequencerStartTrigger = win32com.client.CastTo(self.SequencerStartInterface, 'IBUCSTrigger')
self.SequencerBusyInterface = IBUCSCore.GetSubSystemDefaultInterface("Status|Software|Sequencers|IsASequencerActive")
self.SequencerBusyStatus = win32com.client.CastTo(self.SequencerBusyInterface, 'IBUCSStatusContainer')
self.TimeSeriesSlowNumMeasurementsInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SequencerTimeSeriesSlow|AmountOfMeasurements")
self.TimeSeriesSlowNumMeasurementsMan = win32com.client.CastTo(self.TimeSeriesSlowNumMeasurementsInterface, 'IBUCSInt')
self.TimeSeriesSlowNameInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SequencerTimeSeriesSlow|Naming|DataName")
self.TimeSeriesSlowNameMan = win32com.client.CastTo(self.TimeSeriesSlowNameInterface, 'IBUCSString')
self.TimeSeriesSlowNumAccumulationsInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SequencerTimeSeriesSlow|SpectrumAcquisition|Accumulations")
self.TimeSeriesSlowNumAccumulationsMan = win32com.client.CastTo(self.TimeSeriesSlowNumAccumulationsInterface, 'IBUCSInt')
self.TimeSeriesSlowIntTimeInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SequencerTimeSeriesSlow|SpectrumAcquisition|IntegrationTime")
self.TimeSeriesSlowIntTimeMan = win32com.client.CastTo(self.TimeSeriesSlowIntTimeInterface, 'IBUCSFloat')
self.TimeSeriesSlowNumInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SequencerTimeSeriesSlow|Naming|DataNumber")
self.TimeSeriesSlowNumMan = win32com.client.CastTo(self.TimeSeriesSlowNumInterface, 'IBUCSInt')
self.TimeSeriesSlowModeInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SequencerTimeSeriesSlow|MeasurementMode")
self.TimeSeriesSlowModeMan = win32com.client.CastTo(self.TimeSeriesSlowModeInterface, 'IBUCSEnum')
self.TimeSeriesSlowNextInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SequencerTimeSeriesSlow|NextMeasurement")
self.TimeSeriesSlowNextMan = win32com.client.CastTo(self.TimeSeriesSlowNextInterface, 'IBUCSTrigger')
self.TimeSeriesSlowStartInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SequencerTimeSeriesSlow|Start")
self.TimeSeriesSlowStartMan = win32com.client.CastTo(self.TimeSeriesSlowStartInterface, 'IBUCSTrigger')
self.TimeSeriesSlowStopInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|StopSequencer")
self.TimeSeriesSlowStopMan = win32com.client.CastTo(self.TimeSeriesSlowStopInterface, 'IBUCSTrigger')
self.TimeSeriesSlowIndNextInterface = IBUCSCore.GetSubSystemDefaultInterface("UserParameters|SequencerTimeSeriesSlow|IndexOfNextMeasurement")
self.TimeSeriesSlowIndNextMan = win32com.client.CastTo(self.TimeSeriesSlowIndNextInterface, 'IBUCSInt')
self.TimeSeriesSlowActivityInterface = IBUCSCore.GetSubSystemDefaultInterface("Status|Software|Sequencers|ActiveSequencer|CurrentActivity")
self.TimeSeriesSlowActivityMan = win32com.client.CastTo(self.TimeSeriesSlowActivityInterface, 'IBUCSStatusContainer')
self.RamanRelativeXInterface = IBUCSCore.GetSubSystemDefaultInterface("MultiComm|MicroscopeControl|Video|ProbePosition|RelativeX")
self.RamanRelativeXMan = win32com.client.CastTo(self.RamanRelativeXInterface, 'IBUCSFloat')
self.RamanRelativeYInterface = IBUCSCore.GetSubSystemDefaultInterface("MultiComm|MicroscopeControl|Video|ProbePosition|RelativeY")
self.RamanRelativeYMan = win32com.client.CastTo(self.RamanRelativeYInterface, 'IBUCSFloat')
self.BrightnessInterface = IBUCSCore.GetSubSystemDefaultInterface("MultiComm|MicroscopeControl|WhiteLight|Top|BrightnessPercentage")
self.BrightnessMan = win32com.client.CastTo(self.BrightnessInterface, 'IBUCSFloat')
self.connected = True
return True
def getBrightness(self):
assert self.connected
return self.BrightnessMan.GetValue()
def setBrightness(self, newval):
if newval<0:
newval = 0.0
elif newval>100.:
newval = 100.0
self.BrightnessMan.SetValue(newval)
sleep(.1)
def getRamanPositionShift(self):
rx = .5-self.RamanRelativeXMan.GetValue() # current assumption is, that image center is current position
ry = self.RamanRelativeYMan.GetValue()-.5
width, height = self.ImageWidthMan.GetValue(), self.ImageHeightMan.GetValue()
return rx*width, ry*height
def disconnect(self):
if self.connected:
self.IBUCSAccess.RequestWriteAccess(False)
self.connected = False
def getPosition(self):
assert self.connected
self.PosXCurFloatMan.Update()
x = self.PosXCurFloatMan.GetSingleValueAsDouble()[1]
self.PosYCurFloatMan.Update()
y = self.PosYCurFloatMan.GetSingleValueAsDouble()[1]
self.PosZCurFloatMan.Update()
z = self.PosZCurFloatMan.GetSingleValueAsDouble()[1]
return x, y, z
def getSoftwareZ(self):
self.PosZCurFloatMan.Update()
z = self.PosZCurFloatMan.GetSingleValueAsDouble()[1]
return z
def getUserZ(self):
assert self.connected
self.PosZCurUserFloatMan.Update()
z = self.PosZCurUserFloatMan.GetSingleValueAsDouble()[1]
return z
def moveToAbsolutePosition(self, x, y, z=None, epsxy=0.11, epsz=0.011):
assert self.connected
initpos = self.getPosition()
# move only if new position is really different; repeat if new position is ignored (happens some times)
while max(abs(initpos[0]-x), abs(initpos[1]-y))>epsxy:
t0 = time()
self.PosXFloatMan.SetValue(x)
self.PosYFloatMan.SetValue(y)
self.GoToTrigger.OperateTrigger()
# wait till position is found within accuracy of epsxy; check if position changes at all
distance = 2*epsxy
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.):
print("WARNING: signal ignored:", time()-t0, x, y, curpos, initpos)
sys.stdout.flush()
break
sleep(.01)
sleep(.1)
initpos = self.getPosition()
if z is not None:
self.moveZto(z, epsz)
def moveZto(self, z, epsz=0.011):
assert self.connected
#z = round(z,2)
while abs(self.PosZCurFloatMan.GetSingleValueAsDouble()[1]-z)>epsz:
initpos = self.PosZCurFloatMan.GetSingleValueAsDouble()[1]
t0 = time()
self.PosZFloatMan.SetValue(z)
distance = 2*epsz
while distance > epsz:
self.PosZCurFloatMan.Update()
curpos = self.PosZCurFloatMan.GetSingleValueAsDouble()[1]
distance = abs(curpos-z)
if ((time()-t0>0.5) and abs(curpos-initpos)<epsz) or (time()-t0>10.):
print("WARNING: signal z ignored:", time()-t0)
sys.stdout.flush()
break
sleep(.01)
sleep(.1)
def saveImage(self, fname):
assert self.connected
self.ImageNameMan.SetValue(fname)
self.ImageSaveMan.OperateTrigger()
sleep(.1)
def getImageDimensions(self):
""" Get the image width and height in um and the orientation angle in degrees.
"""
assert self.connected
width, height = self.ImageWidthMan.GetValue(), self.ImageHeightMan.GetValue()
angle = self.ImageRotationMan.GetValue()
return width, height, angle
def startSinglePointScan(self):
assert self.connected
self.SequencerStartTrigger.OperateTrigger()
# Wait until sequencer has finished
while True:
self.SequencerBusyStatus.Update()
Busy = self.SequencerBusyStatus.GetSingleValueAsInt()[1]
if not Busy:
break
def initiateTimeSeriesScan(self, label, numberofscans, accumulations, integrtime):
assert self.connected
self.timeseries = numberofscans
self.TimeSeriesSlowNameMan.SetValue(label)
self.TimeSeriesSlowNumMeasurementsMan.SetValue(numberofscans)
self.TimeSeriesSlowNumAccumulationsMan.SetValue(accumulations)
self.TimeSeriesSlowIntTimeMan.SetValue(integrtime)
self.TimeSeriesSlowModeMan.SetValueNumeric(0)
self.TimeSeriesSlowNumMan.SetValue(0)
self.TimeSeriesSlowStartMan.OperateTrigger()
sleep(0.1)
t1 = time()
while True:
self.TimeSeriesSlowActivityMan.Update()
valid, act = self.TimeSeriesSlowActivityMan.GetSingleValueAsString()
if act=="Waiting for next Measurement":
break
else:
sleep(0.02)
if time()-t1>3.:
print("Waiting for measurement ready...")
t1 = time()
def nextTimeSeriesScan(self, num):
assert self.timeseries
self.TimeSeriesSlowNextMan.OperateTrigger()
# Wait until sequencer has finished
sleep(0.1)
t1 = time()
while True:
ind = self.TimeSeriesSlowIndNextMan.GetValue()
if ind>num:
break
else:
sleep(0.02)
if time()-t1>3.:
print("Waiting for next index...")
t1 = time()
while True:
valid, act = self.TimeSeriesSlowActivityMan.GetSingleValueAsString()
if act=="Waiting for next Measurement":
break
else:
sleep(0.02)
if time()-t1>3.:
print("Waiting for measurement ready...")
t1 = time()
if num==self.timeseries-1:
self.timeseries = 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/>.
"""
import os
import pickle
import numpy as np
import cv2
from helperfunctions import cv2imread_fix, cv2imwrite_fix
from copy import copy
currentversion = 1
def loadData(fname):
retds = None
with open(fname, "rb") as fp:
ds = pickle.load(fp)
ds.fname = fname
ds.readin = True
ds.updatePath()
retds = DataSet(fname)
retds.version = 0
retds.__dict__.update(ds.__dict__)
if retds.version < currentversion:
retds.legacyConversion()
elif retds.zvalimg=="saved":
retds.loadZvalImg()
return retds
def saveData(dataset, fname):
with open(fname, "wb") as fp:
# zvalimg is rather large and thus it is saved separately in a tif file
# only onces after its creation
zvalimg = dataset.zvalimg
if zvalimg is not None:
dataset.zvalimg = "saved"
pickle.dump(dataset, fp, protocol=-1)
dataset.zvalimg = zvalimg
class DataSet(object):
def __init__(self, fname, newProject=False):
self.fname = fname
# parameters specifically for optical scan
self.version = currentversion
self.lastpos = None
self.maxdim = None
self.pixelscale = None # µm / pixel
self.imagedim = None # width, height, angle
self.fitpoints = [] # manually adjusted positions aquired to define the specimen geometry
self.fitindices = [] # which of the five positions in the ui are already known
self.boundary = [] # scan boundary computed by a circle around the fitpoints + manual adjustments
self.grid = [] # scan grid positions for optical scan
self.zpositions = [] # z-positions for optical scan
self.heightmap = None
self.zvalimg = None
# parameters specifically for raman scan
self.pshift = None # shift of raman scan position relative to image center
self.seedpoints = np.array([])
self.seeddeletepoints = np.array([])
self.ramanpoints = []
self.particlecontours = []
self.particlestats = []
self.ramanscansortindex = None
self.ramanscandone = False
self.readin = True # a value that is always set to True at loadData
# and mark that the coordinate system might be changed in the meantime
self.mode = "prepare"
if newProject:
self.fname = self.newProject(fname)
self.updatePath()
def saveZvalImg(self):
if self.zvalimg is not None:
cv2imwrite_fix(self.getZvalImageName(), self.zvalimg)
def loadZvalImg(self):
if os.path.exists(self.getZvalImageName()):
self.zvalimg = cv2imread_fix(self.getZvalImageName(), cv2.IMREAD_GRAYSCALE)
def legacyConversion(self, recreatefullimage=False):
if self.version==0:
print("Converting legacy version 0 to 1")
print("This may take some time")
# local imports as these functions are only needed for the rare occasion of legacy conversion
from opticalscan import loadAndPasteImage
# try to load png and check for detection contours
buggyimage = recreatefullimage
if not buggyimage and os.path.exists(self.getLegacyImageName()):
img = cv2imread_fix(self.getLegacyImageName())
Nc = len(self.particlecontours)
if Nc>0:
contour = self.particlecontours[Nc//2]
contpixels = img[contour[:,0,1],contour[:,0,0]]
if np.all(contpixels[:,1]==255) and np.all(contpixels[:,2]==0) \
and np.all(contpixels[:,0]==0):
buggyimage = True
if not buggyimage:
cv2imwrite_fix(self.getImageName(), img)
del img
if buggyimage:
print("recreating fullimage from grid data")
imgdata = None
zvalimg = None
Ngrid = len(self.grid)
width, height, rotationvalue = self.imagedim
p0, p1 = self.maxdim[:2], self.maxdim[2:]
for i in range(Ngrid):
print(f"Processing image {i+1} of {Ngrid}")
names = []
for k in range(len(self.zpositions)):
names.append(os.path.join(self.getScanPath(), f"image_{i}_{k}.bmp"))
p = self.grid[i]
imgdata, zvalimg = loadAndPasteImage(names, imgdata, zvalimg, width,
height, rotationvalue, p0, p1, p)
self.zvalimg = zvalimg
cv2imwrite_fix(self.getImageName(), cv2.cvtColor(imgdata, cv2.COLOR_RGB2BGR))
del imgdata
self.saveZvalImg()
if "particleimgs" in self.__dict__:
del self.particleimgs
self.version = 1
#os.remove(self.getLegacyImageName())
#os.remove(self.getLegacyDetectImageName())
#self.save()
# add later conversion for higher version numbers here
def getSubImage(self, img, index, draw=True):
contour = self.particlecontours[index]
x0, x1 = contour[:,0,0].min(), contour[:,0,0].max()
y0, y1 = contour[:,0,1].min(), contour[:,0,1].max()
subimg = img[y0:y1+1,x0:x1+1].copy()
if draw:
cv2.drawContours(subimg, [contour], -1, (0,255,0), 1)
return subimg
def getZval(self, pixelpos):
assert self.zvalimg is not None
zp = self.zvalimg[round(pixelpos[1]), round(pixelpos[0])]
z0, z1 = self.zpositions.min(), self.zpositions.max()
return zp/255.*(z1-z0) + z0
def mapHeight(self, x, y):
assert not self.readin
assert self.heightmap is not None
return self.heightmap[0]*x + self.heightmap[1]*y + self.heightmap[2]
def mapToPixel(self, p, force=False):
if not force:
assert not self.readin
p0 = copy(self.lastpos)
p0[0] -= self.imagedim[0]/2
p0[1] += self.imagedim[1]/2
return (p[0] - p0[0])/self.pixelscale, (p0[1] - p[1])/self.pixelscale
def mapToLength(self, pixelpos, force=False):
if not force:
assert not self.readin
p0 = copy(self.lastpos)
p0[0] -= self.imagedim[0]/2
p0[1] += self.imagedim[1]/2
return (pixelpos[0]*self.pixelscale + p0[0]), (p0[1] - pixelpos[1]*self.pixelscale)
def mapToLengthRaman(self, pixelpos, noz=False):
p0x, p0y = self.mapToLength(pixelpos)
x, y = p0x + self.pshift[0], p0y + self.pshift[1]
z = None
if not noz:
z = self.mapHeight(x, y)
z += self.getZval(pixelpos)
return x, y, z
def newProject(self, fname):
path = os.path.split(fname)[0]
name = os.path.splitext(os.path.basename(fname))[0]
newpath = os.path.join(path, name)
fname = os.path.join(newpath, name + ".pkl")
if not os.path.exists(newpath):
os.mkdir(newpath) # for new projects a directory will be created
elif os.path.exists(fname): # if this project is already there, load it instead
self.__dict__.update(loadData(fname).__dict__)
return fname
def getScanPath(self):
scandir = os.path.join(self.path, "scanimages")
if not os.path.exists(scandir):
os.mkdir(scandir)
return scandir
def updatePath(self):
self.path = os.path.split(self.fname)[0]
self.name = os.path.splitext(os.path.basename(self.fname))[0]
def getImageName(self):
return os.path.join(self.path, "fullimage.tif")
def getZvalImageName(self):
return os.path.join(self.path, "zvalues.tif")
def getLegacyImageName(self):
return os.path.join(self.path, "fullimage.png")
def getLegacyDetectImageName(self):
return os.path.join(self.path, "detectimage.png")
def getDetectImageName(self):
raise NotImplementedError("No longer implemented due to change in API")
def getTmpImageName(self):
return os.path.join(self.path, "tmp.bmp")
def saveParticleData(self):
if len(self.ramanscansortindex)>0:
data = []
for i in self.ramanscansortindex:
data.append(list(self.ramanpoints[i])+list(self.particlestats[i]))
data = np.array(data)
data[:,0], data[:,1], z = self.mapToLengthRaman((data[:,0], data[:,1]), noz=True)
data[:,2:7] *= self.pixelscale
header = "x [µm], y [µm], length [µm], height [µm], length_ellipse [µm], height_ellipse [µm]"
if data.shape[1]>6:
header = header + ", area [µm^2]"
data[:,6] *= self.pixelscale
np.savetxt(os.path.join(self.path, "particledata.txt"), data,
header=header)
def save(self):
saveData(self, self.fname)
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
GEPARD - Gepard-Enabled PARticle Detection
Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für
Polymerforschung Dresden e. V. <bittrich-lars@ipfdd.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program, see COPYING.
If not, see <https://www.gnu.org/licenses/>.
"""
import numpy as np
from PyQt5 import QtCore, QtWidgets, QtGui
from segmentation import Segmentation
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
import matplotlib.pyplot as plt
from threading import Thread
Nscreen = 1000
class HistWidget(QtWidgets.QWidget):
def __init__(self, histcallback, curvecallback, parent=None):
super().__init__(parent)
self.updateCallbacks(histcallback, curvecallback)
self.points = np.array([[50,0],[100,150],[200,255]])
self.fig = plt.Figure()
self.canvas = FigureCanvasQTAgg(self.fig)
self.canvas.setParent(self)
self.ax = self.fig.add_subplot(111, autoscale_on=False)
self.ax.axis("off")
self.fig.subplots_adjust(left=0.05, top=0.98, bottom=0.05, right=0.995)
self.canvas.mpl_connect('pick_event', self.moveControlPoint)
self.canvas.mpl_connect('button_release_event', self.releaseAxes)
self.canvas.mpl_connect('motion_notify_event', self.moveAxes)
self.moveind = None
self.plot()
self.setMaximumSize(200,200)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(self.canvas)
self.setLayout(vbox)
def value(self):
return self.points
def moveAxes(self, event):
if (event.inaxes==self.ax) and self.moveind is not None:
x = event.xdata
y = event.ydata
if self.moveind == 0:
self.points[0,0] = max(0,min(x, self.points[1,0]))
elif self.moveind == 1:
self.points[1,0] = max(self.points[0,0]+1, min(x, self.points[2,0]-1))
self.points[1,1] = max(0, min(y, 255))
else:
self.points[2,0] = max(self.points[1,0]+1, min(x, 255))
self.plot()
def releaseAxes(self, event):
self.moveind = None
def plot(self):
self.ax.lines = []
self.ax.collections = []
self.ax.artists = []
hist = self.histcallback()[:,0]
hist /= hist.max()
hist *= 255
hist[0] = 0
hist[-1] = 0
xarr, arr = self.curvecallback(self.points)
self.ax.fill(xarr, hist, color=(.7,.7,.7))
self.ax.plot([0,0], [255,255], "k-")
self.ax.plot(xarr, arr, "b-")
self.ax.plot(self.points[:,0], self.points[:,1], "go", picker=5)
self.ax.set_xlim(0,255)
self.ax.set_ylim(0,255)
self.canvas.draw()
self.repaint()
def updateCallbacks(self, histcallback, curvecallback):
self.histcallback = histcallback
self.curvecallback = curvecallback
def moveControlPoint(self, event):
if self.moveind is None:
self.moveind = event.ind
class ImageView(QtWidgets.QLabel):
seedChanged = QtCore.pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.overlay = None
self.drag = False
self.alpha = .8
self.contours = []
self.seedradius = 3
self.showseedpoints = True
self.measpoints = []
self.seedpoints = []
self.seeddeletepoints = []
@QtCore.pyqtSlot(int)
def resetAlpha(self, num):
self.alpha = num/100.
self.update()
@QtCore.pyqtSlot(int)
def setSeedRadius(self, num):