diff --git a/.gitignore b/.gitignore index 2a5bd19c1193a363a4f5ac6fb5626fdc9b0fa3ca..fd4eb085eb2f9064b0474d29c0d1ff32b80a6bc1 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,7 @@ external/build/ .idea/ *.pyd + +ramancom/renishawcom.py + +ramancom/renishawtesting.py diff --git a/__main__.py b/__main__.py index e1c173a60717bc09e33348970b0886ab9ef37733..059a8bc3026342c14fb255c1d1e01325a274f050 100644 --- a/__main__.py +++ b/__main__.py @@ -205,12 +205,17 @@ class GEPARDMainWindow(QtWidgets.QMainWindow): self.configRamanCtrlAct.setDisabled(True) self.noOverlayAct = QtWidgets.QAction("&No Overlay", self) + self.noOverlayAct.setShortcut("1") self.selOverlayAct = QtWidgets.QAction("&Selected Overlay", self) + self.selOverlayAct.setShortcut("2") self.fullOverlayAct = QtWidgets.QAction("&Full Overlay", self) + self.fullOverlayAct.setShortcut("3") self.transpAct = QtWidgets.QAction("&Transparent Overlay", self) + self.transpAct.setShortcut("T") self.hideLabelAct = QtWidgets.QAction('&Hide Spectra Numbers', self) self.hideLabelAct.setShortcut("H") self.darkenAct = QtWidgets.QAction("&Darken Image", self) + self.darkenAct.setShortcut("D") self.seedAct = QtWidgets.QAction("&Set Color Seed", self) for act in [self.noOverlayAct, self.selOverlayAct, self.fullOverlayAct, self.hideLabelAct, self.transpAct, self.darkenAct, self.seedAct]: diff --git a/analysis/analysiswidgets.py b/analysis/analysiswidgets.py index d574c291b57c90837c2852e160d98e4cd033e47c..4987fc046d4ed61a808434492acd5f9627d7b076 100644 --- a/analysis/analysiswidgets.py +++ b/analysis/analysiswidgets.py @@ -209,13 +209,19 @@ class PolymerNavigationToolbar(QtWidgets.QGroupBox): self.particleSelector.setValue(partIndices.index(particleIndex)+1) specIndices = self.particleContainer.getSpectraIndicesOfParticle(self.currentParticleIndex) - self.spectrumSelector.setValue(1) - self.spectrumSelector.setMaximum(len(specIndices)) + if len(specIndices) > 0: + self.spectrumSelector.setDisabled(False) + self.spectrumSelector.setValue(1) + self.spectrumSelector.setMaximum(len(specIndices)) + self.currentSpectrumIndex = specIndices[self.spectrumSelector.value()-1] + self.specNumberSelector.setValue(self.currentSpectrumIndex+1) + else: + self.spectrumSelector.setDisabled(True) + self.spectrumSelector.setValue(0) + self.spectrumSelector.setMaximum(0) + self.spectrumNumberLabel.setText(f'of {len(specIndices)} spectra') - - self.currentSpectrumIndex = specIndices[self.spectrumSelector.value()-1] - self.specNumberSelector.setValue(self.currentSpectrumIndex+1) - + self.typeSelectorCombo.currentIndexChanged.connect(self.setTypeSelector) self.spectrumSelector.valueChanged.connect(self.setSpecSelector) self.particleSelector.valueChanged.connect(self.setParticleSelector) @@ -232,10 +238,6 @@ class PolymerNavigationToolbar(QtWidgets.QGroupBox): def setParticleSelector(self): assignment = self.typeSelectorCombo.currentText() if assignment != '': -# if self.lastParticleIndex is not None: -# self.currentParticleIndex = self.lastParticleIndex -# self.lastParticleIndex = None -# else: partIndices = self.particleContainer.getIndicesOfParticleType(assignment) self.currentParticleIndex = partIndices[self.particleSelector.value()-1] @@ -246,22 +248,22 @@ class PolymerNavigationToolbar(QtWidgets.QGroupBox): self.setSpecSelector() def setSpecSelector(self): -# if self.lastSpecIndex is not None: -# self.currentSpectrumIndex = self.lastSpecIndex -# self.lastSpecIndex = None -# else: specIndices = self.particleContainer.getSpectraIndicesOfParticle(self.currentParticleIndex) - self.currentSpectrumIndex = specIndices[self.spectrumSelector.value()-1] - - self.specNumberSelector.setValue(self.currentSpectrumIndex+1) - - self.WidgetsUpdated.emit() - self.JumpToIndicatedSpec.emit() - + if len(specIndices) == 0: + self.spectrumSelector.setDisabled(True) + else: + self.spectrumSelector.setDisabled(False) + + self.currentSpectrumIndex = specIndices[self.spectrumSelector.value()-1] + self.specNumberSelector.setValue(self.currentSpectrumIndex+1) + if self.lastSpecIndex is not None: self.currentParticleIndex = self.particleContainer.getParticleIndexContainingSpecIndex(self.lastSpecIndex) self.lastSpecIndex = None self.setWidgetsToNewParticleIndex(self.currentParticleIndex) + + self.JumpToIndicatedSpec.emit() + self.WidgetsUpdated.emit() class FindColoredParticleWindow(QtWidgets.QWidget): diff --git a/analysis/excelexport.py b/analysis/excelexport.py index e09e96979e9ab3e9e5062c830360ee3b9f5b9218..8996e06f4b0a27ce8c770670c555b991935c6c51 100644 --- a/analysis/excelexport.py +++ b/analysis/excelexport.py @@ -76,7 +76,6 @@ class ExpExcelDialog(QtWidgets.QDialog): polymers = np.array(self.particleContainer.getListOfParticleAssignments()) sizes = np.array(self.particleContainer.getSizesOfAllParticles()) - for box in self.checkBoxes: if box.isChecked() == True: if box.text() != 'Size Classes': diff --git a/analysis/loadresults.py b/analysis/loadresults.py index c002be2da0c53fed2f2fecbe9eef2c4cf7e86463..102616bbb0accf96af5022ed52901cd2ee639734 100644 --- a/analysis/loadresults.py +++ b/analysis/loadresults.py @@ -121,7 +121,6 @@ class LoadTrueMatchResults(QtWidgets.QWidget): print('spectra in batch:', len(resBatch)) for result in resBatch: specIndex, polymertype, additive, hqi, addhqi = self.interpretEntry(resBatchIndex, numSpectraInPreviousBatches, result, numhits, numcomps) - print('assigning currentSpecIndex', specIndex) self.polymertypes[specIndex] = polymertype self.hqis[specIndex] = hqi if numcomps > 1: diff --git a/analysis/particleAndMeasurement.py b/analysis/particleAndMeasurement.py index 51348bad4a83a6cac50ab74c09684fb3e0201e60..de6ce7947d380fc69b38a7fa8a0a8b4b7ee145ca 100644 --- a/analysis/particleAndMeasurement.py +++ b/analysis/particleAndMeasurement.py @@ -44,13 +44,19 @@ class Particle(object): meas.setHQI(100) def getParticleAssignment(self): - return self.getMeasAssignmentWithHighestHQI() #probably another method could be more suitable... + assignment = 'NotYetMeasured' + if len(self.measurements) > 0: + assignment = self.getMeasAssignmentWithHighestHQI() #probably another method could be more suitable... + return assignment def getHighestHQI(self): - hqis = [] - for meas in self.measurements: - hqis.append(meas.getHQI()) - return max(hqis) + maxHQI = None + if len(self.measurements) > 0: + hqis = [] + for meas in self.measurements: + hqis.append(meas.getHQI()) + maxHQI = max(hqis) + return maxHQI def getHQIOfMeasurementIndex(self, index): for meas in self.measurements: diff --git a/analysis/particleCharacterization.py b/analysis/particleCharacterization.py index cfcc9d50ddb0e86557bd62bd0fc047608a8ef333..3b52afa96ba6ea6faea7095fad0a9b9233db6af1 100644 --- a/analysis/particleCharacterization.py +++ b/analysis/particleCharacterization.py @@ -239,7 +239,7 @@ def getParticleCenterPoint(contour): return x, y def loadFullimageFromDataset(dataset): - return cv2imread_fix(dataset.getImageName(), cv2.COLOR_BGR2RGB) + return cv2imread_fix(dataset.getImageName()) def loadZValImageFromDataset(dataset): return cv2imread_fix(dataset.getZvalImageName(), cv2.IMREAD_GRAYSCALE) \ No newline at end of file diff --git a/analysis/particleContainer.py b/analysis/particleContainer.py index 89ea0bed412371088da60e59f87a5e72d382ef94..b61ca9bddceaec5ee199b75b2ecd70c7722ff811 100644 --- a/analysis/particleContainer.py +++ b/analysis/particleContainer.py @@ -44,6 +44,8 @@ class ParticleContainer(object): def clearMeasurements(self): self.measurements = [] + for particle in self.particles: + particle.measurements = [] def setMeasurementScanIndex(self, indexOfMeasurment, scanIndex): self.measurements[indexOfMeasurment].ramanScanIndex = scanIndex @@ -94,7 +96,7 @@ class ParticleContainer(object): particle.contour = contours[index] def setParticleStats(self, particlestats): - assert len(self.particles) == len(particlestats) + assert len(self.particles) == len(particlestats), f'numParticles = {len(self.particles)}, len partStats = {len(particlestats)}' for index, particle in enumerate(self.particles): particle.__dict__.update(particlestats[index].__dict__) diff --git a/dataset.py b/dataset.py index 45adde007478956cd622681ff92e0a9a7fa96ddc..64678137b21820d2a19869c1685ece051af6be19 100644 --- a/dataset.py +++ b/dataset.py @@ -148,7 +148,7 @@ class DataSet(object): 'minparticlearea': 20, 'minparticledistance': 20, 'measurefrac': 1, - 'compactness': 0.1, + 'compactness': 0.0, 'seedRad': 3} self.particleContainer = ParticleContainer(self) diff --git a/detectionview.py b/detectionview.py index cd564f1b8c096fa29f791f10cbdbe7ec772e1a3e..55b8dc9da95aa157ee4a53c315a34b4f966839e7 100644 --- a/detectionview.py +++ b/detectionview.py @@ -20,11 +20,14 @@ If not, see . """ 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 +from .segmentation import Segmentation +from .analysis.particleCharacterization import getParticleStatsWithPixelScale, loadZValImageFromDataset +from .errors import InvalidParticleError + Nscreen = 1000 class HistWidget(QtWidgets.QWidget): @@ -377,8 +380,8 @@ class ParticleDetectionView(QtWidgets.QWidget): grid.addWidget(label, i+1, 0, QtCore.Qt.AlignLeft) grid.addWidget(self.seedradiusedit, i+1, 1, QtCore.Qt.AlignLeft) grid.addWidget(self.showseedpoints, i+2, 0, 1, 2, QtCore.Qt.AlignLeft) - grid.addWidget(QtWidgets.QLabel("Use Ctrl for seeds and Ctrl+Shift for delete points"), i+3, 0, 1, 2, QtCore.Qt.AlignLeft) - grid.addWidget(QtWidgets.QLabel("Ctrl+Alt removes seeds near cursor"), i+4, 0, 1, 2, QtCore.Qt.AlignLeft) + grid.addWidget(QtWidgets.QLabel("Click mouse to add seeds, Click+Shift to add deletepoints"), i+3, 0, 1, 2, QtCore.Qt.AlignLeft) + grid.addWidget(QtWidgets.QLabel("Click+Alt removes seeds near cursor"), i+4, 0, 1, 2, QtCore.Qt.AlignLeft) group.setLayout(grid) vbox.addWidget(group) @@ -565,18 +568,12 @@ class ParticleDetectionView(QtWidgets.QWidget): self.seg.setParameters(**kwargs) seedradius = self.seedradiusedit.value() if showname is not None: - stepImg, imgtype = self.seg.apply2Image(img, self.imglabel.seedpoints, - self.imglabel.seeddeletepoints, - seedradius, - self.dataset, - return_step=showname) + stepImg, imgtype = self.seg.apply2Image(img, self.imglabel.seedpoints, self.imglabel.seeddeletepoints, + seedradius, self.dataset, return_step=showname) self.imglabel.showStep(stepImg, imgtype) else: - measurementpoints, contours, particlestats = self.seg.apply2Image(img, - self.imglabel.seedpoints, - self.imglabel.seeddeletepoints, - seedradius, - self.dataset) + measurementpoints, contours = self.seg.apply2Image(img, self.imglabel.seedpoints, self.imglabel.seeddeletepoints, + seedradius, self.dataset) self.imglabel.updateDetectionResults(contours, measurementpoints) @QtCore.pyqtSlot() @@ -628,15 +625,16 @@ class ParticleDetectionView(QtWidgets.QWidget): def checkOnComputation(self): if self.thread is not None: if not self.threadrunning: - #self.thread.join() self.thread = None self.unBlockUI() self.pdetectall.setText("Detect all") self.imageUpdate.emit(self.view.microscopeMode) if self.dataset is not None: - self.setWindowTitle(str(self.dataset.particleContainer.getNumberOfParticles()) + " Particles") + numParticles = self.dataset.particleContainer.getNumberOfParticles() + numMeasurements = self.dataset.particleContainer.getNumberOfMeasurements() + self.setWindowTitle(f'{numParticles} Particles ({numMeasurements} Measurements)') else: - self.timer.start(100.) + self.timer.start(100.) def _worker(self): kwargs = {} @@ -648,27 +646,27 @@ class ParticleDetectionView(QtWidgets.QWidget): kwargs[name] = valuefunc() seedradius = self.seedradiusedit.value() self.seg.setParameters(**kwargs) - measurementPoints, contours, particlestats = self.seg.apply2Image(self.img, - seedpoints, - deletepoints, - seedradius, - self.dataset) + + measurementPoints, contours= self.seg.apply2Image(self.img, seedpoints, deletepoints, seedradius, self.dataset) if measurementPoints is None: # computation was canceled return if self.dataset is not None: - self.applyResultsToDataset(measurementPoints, contours, particlestats) + self.applyResultsToDataset(measurementPoints, contours) self.threadrunning = False - def applyResultsToDataset(self, measurementPoints, contours, particlestats): + def applyResultsToDataset(self, measurementPoints, contours): self.dataset.ramanscandone = False + particlestats = self.getParticleStats(contours) + particleContainer = self.dataset.particleContainer numParticles = len(contours) particleContainer.initializeParticles(numParticles) particleContainer.setParticleContours(contours) particleContainer.setParticleStats(particlestats) + particleContainer.clearMeasurements() for particleIndex in measurementPoints.keys(): measPoints = measurementPoints[particleIndex] for index, point in enumerate(measPoints): @@ -680,3 +678,16 @@ class ParticleDetectionView(QtWidgets.QWidget): self.dataset.particleDetectionDone = True self.dataset.mode = "prepareraman" self.dataset.save() + + def getParticleStats(self, contours): + particlestats = [] + zvalimg = loadZValImageFromDataset(self.dataset) + for contour in contours: + try: + stats = getParticleStatsWithPixelScale(contour, self.dataset, fullimage=self.img, zimg=zvalimg) + particlestats.append(stats) + except InvalidParticleError: + print('invalid contour in detection, skipping partile. Contour is:', contour) + continue + + return particlestats \ No newline at end of file diff --git a/legacyConvert.py b/legacyConvert.py index 6621563486f4cb7b8a90dea370fba913a8b7628f..34c5d59ece79aeddd88186159531fd362b4a68d0 100644 --- a/legacyConvert.py +++ b/legacyConvert.py @@ -77,6 +77,7 @@ def legacyConversion(dset, recreatefullimage=False): del dset.particleimgs dset.version = 1 + dset.save() if dset.version == 1: print("Converting legacy version 1 to 2") @@ -95,17 +96,20 @@ def legacyConversion(dset, recreatefullimage=False): dset.particles2spectra = [[int(np.where(dset.ramanscansortindex == i)[0])] for i in range(len(dset.ramanscansortindex))] dset.version = 2 + dset.save() if dset.version == 2: print("Converting legacy version 2 to 3") transferParticleStatsToParticleContainer(dset) dset.version = 3 + dset.save() if dset.version == 3: print("Converting legacy version 3 to 4") updateParticleStats(dset) removeLegacyAttributes(dset) dset.version = 4 + dset.save() # add later conversion for higher version numbers here diff --git a/opticalbackground.py b/opticalbackground.py index 47635c0b6c9c952d8032a76a9e19bcd544f2d4db..6cb9d4dba0e49cf5b13bc3013f946e9c999e9043 100644 --- a/opticalbackground.py +++ b/opticalbackground.py @@ -93,7 +93,7 @@ class BackGroundManager(QtWidgets.QWidget): QtWidgets.QMessageBox.about(self, 'Warning', 'No Background Image is aquired') return else: - from opticalscan import loadAndPasteImage + from .opticalscan import loadAndPasteImage self.dataset = self.parentOSwidget.dataset #acquire images in 3x3 area to preview quality of background subtraction diff --git a/opticalscan.py b/opticalscan.py index 6ab990f4f28f08a773629847deb092fadd5171fb..20c94c9f819a6fecf8a49cd78ef6975c18b70ce6 100644 --- a/opticalscan.py +++ b/opticalscan.py @@ -94,7 +94,7 @@ def loadAndPasteImage(srcnames, fullimage, fullzval, width, height, curImg = cv2imread_fix(name) if background is not None: curImg = subtractBackground(curImg, background) - colimgs.append(cv2.cvtColor(curImg, cv2.COLOR_BGR2RGB)) + colimgs.append(curImg) img, zval = imageStacking(colimgs) x, y = p @@ -276,7 +276,7 @@ class OpticalScan(QtWidgets.QWidget): self.zmaxedit.setMaximumWidth(100) label3 = QtWidgets.QLabel("Focus steps:", self) self.nzedit = QtWidgets.QSpinBox(self) - self.nzedit.setRange(2,20) + self.nzedit.setRange(1,20) self.nzedit.setValue(3) self.nzedit.setMaximumWidth(100) self.hdrcheck = QtWidgets.QCheckBox("High dynamic range", self) @@ -451,7 +451,7 @@ class OpticalScan(QtWidgets.QWidget): width, height, rotationvalue = self.ramanctrl.getImageDimensions(self.view.microscopeMode) pshift = self.ramanctrl.getRamanPositionShift() self.dataset.pshift = pshift - img = cv2.cvtColor(cv2imread_fix(self.dataset.getTmpImageName()), cv2.COLOR_BGR2RGB) + img = cv2imread_fix(self.dataset.getTmpImageName()) self.dataset.imagedim_bf = self.ramanctrl.getImageDimensions('bf') self.dataset.pixelscale_bf = self.dataset.imagedim_bf[0]/img.shape[1] #=imagedim_width/shape[1] diff --git a/ramancom/WITecCOM.py b/ramancom/WITecCOM.py index 9eb857fe7e3923fd1a04e8ad2f103e9e585fa9c8..e6eccdde8d8d1d4253887250b41eb17a90ddf857 100644 --- a/ramancom/WITecCOM.py +++ b/ramancom/WITecCOM.py @@ -205,7 +205,7 @@ class WITecCOM(RamanBase): z = self.PosZCurUserFloatMan.GetSingleValueAsDouble()[1] return z - def moveToAbsolutePosition(self, x, y, z=None, epsxy=0.11, epsz=0.011, debugReturn=False, measurementRunning=False): + 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) diff --git a/ramancom/ramanbase.py b/ramancom/ramanbase.py index b1ac2e8491bff2228698c4b24cc0a6d7b1de149c..84cb9bf59ac5d3fe4bb6b1d0d84e766f4720e04a 100644 --- a/ramancom/ramanbase.py +++ b/ramancom/ramanbase.py @@ -45,7 +45,7 @@ class RamanBase(object): def getUserZ(self): raise NotImplementedError - def moveToAbsolutePosition(self, x, y, z=None, epsxy=0.11, epsz=0.011, debugReturn=False, measurementRunning=False): + def moveToAbsolutePosition(self, x, y, z=None, epsxy=0.11, epsz=0.011): raise NotImplementedError def moveZto(self, z, epsz=0.011): diff --git a/ramancom/simulatedraman.py b/ramancom/simulatedraman.py index d31b7e6d5526817317d97beb74c07352658debec..c1bcfa088b9c8639542decd2fd8e95fde80150cf 100644 --- a/ramancom/simulatedraman.py +++ b/ramancom/simulatedraman.py @@ -39,10 +39,10 @@ class SimulatedRaman(RamanBase): self.currentZ = 0. # some plausible data to simulate consecutively changing positions self.positionlist = np.array([[ -1201, 1376, -1290], - [ -1195, -9200, -1279], - [ 1097, -9254, -1297], - [ 2704.1, -1788.2, -138.1], - [ 3884. , -2650.8, -138.1]]) + [ -1195, -1200, -1279], + [ 1097, -1254, -1297], + [ 2704.1, 1288.2, -1381], + [ 1884. , -1500.8, -1381]]) self.znum = 4 self.gridnum = 36 self.positionindex = 0 @@ -78,7 +78,7 @@ class SimulatedRaman(RamanBase): else: return self.currentZ - def moveToAbsolutePosition(self, x, y, z=None, epsxy=0.11, epsz=0.011, debugReturn=False, measurementRunning=False): + def moveToAbsolutePosition(self, x, y, z=None, epsxy=0.11, epsz=0.011): assert self.connected print('moving to:', x, y, z, file=stdout) if z is None: diff --git a/ramanscanui.py b/ramanscanui.py index 3444665e3fa7a147733d3b514354dbcb46b64fc7..62dedb26aa3746fa74405a13928991de34c27441 100644 --- a/ramanscanui.py +++ b/ramanscanui.py @@ -67,7 +67,7 @@ def scan(ramanSettings, positions, controlclass, dataqueue, stopevent, x, y, z = p print("time:", time(), flush=True) print("position:", x, y, z, flush=True) - ramanctrl.moveToAbsolutePosition(x, y, z, measurementRunning=True) + ramanctrl.moveToAbsolutePosition(x, y, z) print("move done", flush=True) ramanctrl.triggerMeasurement(i) print("trigger done", flush=True) @@ -145,7 +145,6 @@ class RamanScanUI(QtWidgets.QWidget): self.setLayout(vbox) self.setWindowTitle("Raman Scan") - #self.show() self.setVisible(False) def makeGetFnameLambda(self, msg, path, fileType, btn): @@ -159,9 +158,10 @@ class RamanScanUI(QtWidgets.QWidget): 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(str(numParticles) + " Particles") + self.setWindowTitle(f'{numParticles} Particles ({numMeasurements} Measurements)') @QtCore.pyqtSlot() def stopScan(self): @@ -234,27 +234,38 @@ class RamanScanUI(QtWidgets.QWidget): self.view.saveDataSet() self.view.prepareAnalysis() self.view.zoomDisplay(2.0) - self.view.highLightRamanIndex(0) - self.view.blockUI() - self.group2.setEnabled(False) - self.progresstime.setEnabled(True) - self.progressbar.setEnabled(True) - self.progressbar.setRange(0, len(scanpoints)) - self.progressbar.setValue(0) - self.ramanctrl.disconnect() - self.processstopevent = Event() - self.dataqueue = Queue() - self.process = Process(target=scan, args=(ramanSettings, scanpoints, - self.ramanctrl.__class__, - self.dataqueue, - self.processstopevent, - self.logpath)) - self.process.start() - self.starttime = time() - self.timer = QtCore.QTimer(self) - self.timer.timeout.connect(self.checkOnScan) - self.timer.setSingleShot(True) - self.timer.start(10000.) + if self.ramanctrl.name == 'RenishawCOM': + QtWidgets.QMessageBox.about(self, "Info", "Control is headed over to Renishaw Instrument, Gepard is stopping here") + ramanSettings['Points'] = scanpoints + self.ramanctrl.initiateMeasurement(ramanSettings) + self.ramanctrl.disconnect() + self.dataset.ramanscandone = True + self.view.saveDataSet() + self.close() + return + + else: + self.view.highLightRamanIndex(0) + self.view.blockUI() + self.group2.setEnabled(False) + self.progresstime.setEnabled(True) + self.progressbar.setEnabled(True) + self.progressbar.setRange(0, len(scanpoints)) + self.progressbar.setValue(0) + self.ramanctrl.disconnect() + self.processstopevent = Event() + self.dataqueue = Queue() + self.process = Process(target=scan, args=(ramanSettings, scanpoints, + self.ramanctrl.__class__, + self.dataqueue, + self.processstopevent, + self.logpath)) + self.process.start() + self.starttime = time() + self.timer = QtCore.QTimer(self) + self.timer.timeout.connect(self.checkOnScan) + self.timer.setSingleShot(True) + self.timer.start(10000.) @QtCore.pyqtSlot() def checkOnScan(self): diff --git a/sampleview.py b/sampleview.py index 5fdbffdf90c4a4af06a4c749a0cc37cf1dc9907f..d9548a2b8922763848fb64bd4843843da71e996f 100644 --- a/sampleview.py +++ b/sampleview.py @@ -193,7 +193,6 @@ class SampleView(QtWidgets.QGraphicsView): self.oscanwidget.setVisible(False) if self.detectionwidget is not None: self.detectionwidget.close() - self.detectionwidget.destroy() self.detectionwidget = None self.ramanwidget.setVisible(False) self.mode = mode @@ -530,7 +529,6 @@ class SampleView(QtWidgets.QGraphicsView): if self.mode == "ParticleDetection" or self.mode == "ParticleAnalysis": self.resetParticleContours() if data is None and os.path.exists(fname): -# data = cv2.cvtColor(cv2imread_fix(fname), cv2.COLOR_BGR2RGB) ##With this line the B and R channel are swapped, which leads to a wrong presentation of the image in gepard..... Why was this line here? data = cv2imread_fix(fname) self.imgdata = data if data is not None: @@ -579,10 +577,17 @@ class SampleView(QtWidgets.QGraphicsView): Removes items from the GraphicsScene. ContourItems remain, however.. :return: """ - for itemList in [self.fititems, self.scanitems, self.ramanscanitems]: - for item in itemList: - self.scene().removeItem(item) - itemList = [] + for item in self.fititems: + self.scene().removeItem(item) + self.fititems = [] + + for item in self.scanitems: + self.scene().removeItem(item) + self.scanitems = [] + + for item in self.ramanscanitems: + self.scene().removeItem(item) + self.ramanscanitems = [] edges, nodes = self.boundaryitems for item in edges: @@ -590,7 +595,6 @@ class SampleView(QtWidgets.QGraphicsView): for item in nodes: self.scene().removeItem(item) self.boundaryitems = [], [] - @QtCore.pyqtSlot() def resetScanPositions(self): diff --git a/segmentation.py b/segmentation.py index c55decb13b907e37190590accaf3201c5301720d..e3a4c1e0d7ebb2b30c2105cd8f2d0f13f3a4fb0b 100644 --- a/segmentation.py +++ b/segmentation.py @@ -26,25 +26,15 @@ from scipy.interpolate import InterpolatedUnivariateSpline from scipy import ndimage as ndi from skimage.feature import peak_local_max from skimage.morphology import watershed -from random import random - -from .errors import InvalidParticleError - +import random def closeHolesOfSubImage(subimg): - #Add padding to TrehsholdImage subimg = cv2.copyMakeBorder(subimg, 1, 1, 1, 1, 0) - # Copy the thresholded image. im_floodfill = subimg.copy() - # Mask used to flood filling. - # Notice the size needs to be 2 pixels than the image. h, w = subimg.shape[:2] mask = np.zeros((h+2, w+2), np.uint8) - # Floodfill from point (0, 0) cv2.floodFill(im_floodfill, mask, (0,0), 255); - # Invert floodfilled image im_floodfill_inv = cv2.bitwise_not(im_floodfill) - # Combine the two images to get the foreground. im_out = subimg | im_floodfill_inv return im_out[1:-1, 1:-1] @@ -83,11 +73,12 @@ class Segmentation(object): 'invertThresh': False, 'maxholebrightness': 0.5, 'minparticlearea': 20, - 'maxparticlearea': 10000, + 'enableMaxArea': False, + 'maxparticlearea': 100000, 'minparticledistance': 20, 'closeBackground': True, 'measurefrac': 1, - 'compactness': 0., + 'compactness': 0.0, 'seedRad': 3} if dataset is not None: self.detectParams = dataset.detectParams @@ -111,6 +102,7 @@ class Segmentation(object): Parameter("upThresh", float, self.detectParams['upThresh'], .01, 1.0, 2, .02, helptext="Upper threshold", show=True), Parameter("maxholebrightness", float, self.detectParams['maxholebrightness'], 0, 1, 2, 0.02, helptext="Close holes brighter than..", show = True), Parameter("minparticlearea", int, self.detectParams['minparticlearea'], 1, 1000, 0, 50, helptext="Min. particle pixel area", show=False), + Parameter("enableMaxArea", np.bool, self.detectParams['enableMaxArea'], helptext="enable filtering for maximal pixel area", show=False, linkedParameter='maxparticlearea'), Parameter("maxparticlearea", int, self.detectParams['maxparticlearea'], 10, 1E9, 0, 50, helptext="Max. particle pixel area", show=True), Parameter("minparticledistance", int, self.detectParams['minparticledistance'], 5, 1000, 0, 5, helptext="Min. distance between particles", show=False), Parameter("measurefrac", float, self.detectParams['measurefrac'], 0, 1, 2, stepsize = 0.05, helptext="measure fraction of particles", show=False), @@ -256,10 +248,18 @@ class Segmentation(object): subdist = cv2.distanceTransform(subimg, cv2.DIST_L2,3) subfg = np.uint8(peak_local_max(subdist, mindistance, indices = False)) - if subfg.max() > 0 and random() < self.measurefrac: #i.e., at least one maximum value was added +# if subfg.max() > 0 and random() < self.measurefrac: #i.e., at least one maximum value was added +# sure_fg[up:(up+height), left:(left+width)] += subfg +# +# elif random() < self.measurefrac: +# #simply get maximum of subdist +# submax = np.where(subdist == subdist.max()) +# sure_fg[up+submax[0][0], left+submax[1][0]] = 1 + + if subfg.max() > 0: #i.e., at least one maximum value was added sure_fg[up:(up+height), left:(left+width)] += subfg - elif random() < self.measurefrac: + else: #simply get maximum of subdist submax = np.where(subdist == subdist.max()) sure_fg[up+submax[0][0], left+submax[1][0]] = 1 @@ -360,7 +360,11 @@ class Segmentation(object): if self.cancelcomputation: return None, None, None - thresh = self.filterThresholdByAreas(thresh, self.minparticlearea, self.maxparticlearea) + if self.enableMaxArea: + maxArea = self.maxparticlearea + else: + maxArea = np.inf + thresh = self.filterThresholdByAreas(thresh, self.minparticlearea, maxArea) print('filter threshold by areas') if return_step == 'maxparticlearea': return thresh, 0 if self.cancelcomputation: @@ -386,6 +390,7 @@ class Segmentation(object): for p in np.int32(seedpoints): cv2.circle(sure_fg, tuple([p[0], p[1]]), int(p[2]), 1, -1) + cv2.circle(sure_bg, tuple([p[0], p[1]]), int(p[2]), 1, -1) for p in np.int32(deletepoints): cv2.circle(sure_fg, tuple([p[0], p[1]]), int(p[2]), 0, -1) cv2.circle(sure_bg, tuple([p[0], p[1]]), int(p[2]), 0, -1) @@ -413,43 +418,38 @@ class Segmentation(object): print("distanceTransform") if self.cancelcomputation: return None, None, None - + twatershed = time() #ich habe jetzt nur noch den Skimage Watershed integriert. Oben auskommentiert der opencv watershed, falls wir ihn doch nochmal für irgendwas brauchen... markers = ndi.label(sure_fg)[0] markers = watershed(-dist_transform, markers, mask=sure_bg, compactness = self.compactness, watershed_line = True) #labels = 0 for background, 1... for particles - print("watershed") + print("watershed, elapsed time:", time()-twatershed, "seconds") if self.cancelcomputation: return None, None, None if return_step=="watershed": return np.uint8(255*(markers!=0)), 0 + tcont = time() if cv2.__version__ > '3.5': contours, hierarchy = cv2.findContours(markers, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE) else: temp, contours, hierarchy = cv2.findContours(markers, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE) - print("contours") + print("contour detection, elapsed time:", time()-tcont, "seconds") if self.cancelcomputation: return None, None, None - from .analysis.particleCharacterization import getParticleStatsWithPixelScale #TODO: AH, this should be imported at beginning of the file, but there it always chrashes.. - particlestats = [] + tstats = time() measurementPoints = {} - tmpcontours = [contours[i] for i in range(len(contours)) if hierarchy[0,i,3]<0] contours = [] particleIndex = 0 + for cnt in tmpcontours: label = markers[cnt[0,0,1],cnt[0,0,0]] if label==0: continue - try: - stats = getParticleStatsWithPixelScale(cnt, dataset, fullimage=img) - except InvalidParticleError: - print('invalid contour in detection, skipping partile. Contour is:', cnt) - continue - particlestats.append(stats) + x0, x1 = cnt[:,0,0].min(), cnt[:,0,0].max() y0, y1 = cnt[:,0,1].min(), cnt[:,0,1].max() subimg = (markers[y0:y1+1,x0:x1+1]).copy() @@ -461,10 +461,21 @@ class Segmentation(object): newMeasPoint = MeasurementPoint(particleIndex, x[index] + x0, y[index] + y0) measurementPoints[particleIndex].append(newMeasPoint) particleIndex += 1 + print("contour stats, elapsed time:", time()-tstats, "seconds") if return_step is not None: raise NotImplementedError(f"this particular return_step: {return_step} is not implemented yet") tf = time() print("particle detection took:", tf-t0, "seconds") - return measurementPoints, contours, particlestats + + if self.measurefrac < 1.0: + nMeasurementsDesired = int(np.round(self.measurefrac * len(measurementPoints))) + print(f'selecting {nMeasurementsDesired} of {len(measurementPoints)} measuring spots') + partIndicesToMeasure = random.sample(measurementPoints.keys(), nMeasurementsDesired) + newMeasPoints = {} + for index in partIndicesToMeasure: + newMeasPoints[index] = measurementPoints[index] + measurementPoints = newMeasPoints + + return measurementPoints, contours