From a56bc599d01a7961a2a3948bf43b6b040fdaf920 Mon Sep 17 00:00:00 2001 From: Francesco Date: Tue, 5 Sep 2023 20:44:38 +0200 Subject: [PATCH] feat: dataprep - run crop routine on separate thread, closes #401 --- cellacdc/dataPrep.py | 119 +++++++++++++++++++++++++++++++++++++++++-- cellacdc/workers.py | 26 ++++++++++ 2 files changed, 141 insertions(+), 4 deletions(-) diff --git a/cellacdc/dataPrep.py b/cellacdc/dataPrep.py index 47242488..7abb64b8 100755 --- a/cellacdc/dataPrep.py +++ b/cellacdc/dataPrep.py @@ -870,13 +870,102 @@ def saveSingleCrop(self, posData, cropROI): self.saveCroppedSegmData(posData, posData.segm_npz_path, cropROI) self.correctAcdcDfCrop(posData, posData.acdc_output_csv_path, cropROI) + def startCropWorker(self, posData): + # Disable clicks on image during alignment + self.img.mousePressEvent = None + + if posData.SizeT > 1: + self.progressWin = apps.QDialogWorkerProgress( + title='Saving cropped data', + parent=self, + pbarDesc=f'Saving cropped data...' + ) + self.progressWin.show(self.app) + self.progressWin.mainPbar.setMaximum(0) + + self._thread = QThread() + + self.cropWorker = workers.DataPrepCropWorker(posData, self) + self.cropWorker.moveToThread(self._thread) + + self.cropWorker.moveToThread(self._thread) + self.cropWorker.signals.finished.connect(self._thread.quit) + self.cropWorker.signals.finished.connect( + self.cropWorker.deleteLater + ) + self._thread.finished.connect(self._thread.deleteLater) + + self.cropWorker.signals.finished.connect( + self.cropWorkerFinished + ) + self.cropWorker.signals.progress.connect(self.workerProgress) + self.cropWorker.signals.initProgressBar.connect( + self.workerInitProgressbar + ) + self.cropWorker.signals.progressBar.connect( + self.workerUpdateProgressbar + ) + self.cropWorker.signals.critical.connect( + self.workerCritical + ) + + self._thread.started.connect(self.cropWorker.run) + self._thread.start() + return self.cropWorker + + def startSaveBkgrDataWorker(self, posData): + # Disable clicks on image during alignment + self.img.mousePressEvent = None + + if posData.SizeT > 1: + self.progressWin = apps.QDialogWorkerProgress( + title='Saving background data', + parent=self, + pbarDesc=f'Saving background data...' + ) + self.progressWin.show(self.app) + self.progressWin.mainPbar.setMaximum(0) + + self._thread = QThread() + + self.saveBkgrDataWorker = workers.DataPrepSaveBkgrDataWorker( + posData, self + ) + self.saveBkgrDataWorker.moveToThread(self._thread) + + self.saveBkgrDataWorker.moveToThread(self._thread) + self.saveBkgrDataWorker.signals.finished.connect(self._thread.quit) + self.saveBkgrDataWorker.signals.finished.connect( + self.saveBkgrDataWorker.deleteLater + ) + self._thread.finished.connect(self._thread.deleteLater) + + self.saveBkgrDataWorker.signals.finished.connect( + self.saveBkgrDataWorkerFinished + ) + self.saveBkgrDataWorker.signals.progress.connect(self.workerProgress) + self.saveBkgrDataWorker.signals.initProgressBar.connect( + self.workerInitProgressbar + ) + self.saveBkgrDataWorker.signals.progressBar.connect( + self.workerUpdateProgressbar + ) + self.saveBkgrDataWorker.signals.critical.connect( + self.workerCritical + ) + + self._thread.started.connect(self.saveBkgrDataWorker.run) + self._thread.start() + return self.saveBkgrDataWorker + def saveCroppedData(self, posData): # Get metadata from tif with TiffFile(posData.tif_path) as tif: metadata = tif.imagej_metadata if len(posData.cropROIs) == 1: - self.saveSingleCrop(posData, posData.cropROIs[0]) + worker = self.startCropWorker(posData) + self.waitWorker(worker) else: self.saveMultiCrops(posData) @@ -1153,9 +1242,11 @@ def crop_cb(self): self.logger.info(f'Cropped data shape:\n{croppedShapesFormat}') self.saveROIcoords(doCrop, posData) - self.logger.info('Saving background data...') - self.saveBkgrData(posData) - + self.logger.info('Starting crop worker...') + + worker = self.startSaveBkgrDataWorker(posData) + self.waitWorker(worker) + self.saveCroppedData(posData) for posData in self.data: @@ -1534,6 +1625,10 @@ def waitAlignDataWorker(self): self.alignDataWorkerLoop = QEventLoop(self) self.alignDataWorkerLoop.exec_() + def waitWorker(self, worker): + worker.loop = QEventLoop(self) + worker.loop.exec_() + def workerProgress(self, text, loggerLevel='INFO'): if self.progressWin is not None: self.progressWin.logConsole.append('-'*60) @@ -1843,6 +1938,22 @@ def alignDataWorkerFinished(self, result): self.alignDataWorkerLoop.exit() self.img.mousePressEvent = self.gui_mousePressEventImg + def saveBkgrDataWorkerFinished(self, result): + if self.progressWin is not None: + self.progressWin.workerFinished = True + self.progressWin.close() + self.progressWin = None + self.saveBkgrDataWorker.loop.exit() + self.img.mousePressEvent = self.gui_mousePressEventImg + + def cropWorkerFinished(self, result): + if self.progressWin is not None: + self.progressWin.workerFinished = True + self.progressWin.close() + self.progressWin = None + self.cropWorker.loop.exit() + self.img.mousePressEvent = self.gui_mousePressEventImg + def workerInitProgressbar(self, totalIter): self.progressWin.mainPbar.setValue(0) if totalIter == 1: diff --git a/cellacdc/workers.py b/cellacdc/workers.py index 49c05087..155ae8a4 100755 --- a/cellacdc/workers.py +++ b/cellacdc/workers.py @@ -1911,6 +1911,32 @@ def emitSelectAcdcOutputFiles( self.mutex.unlock() return self.abort +class DataPrepSaveBkgrDataWorker(QObject): + def __init__(self, posData, dataPrepWin): + QObject.__init__(self) + self.signals = signals() + self.logger = workerLogger(self.signals.progress) + self.posData = posData + self.dataPrepWin = dataPrepWin + + @worker_exception_handler + def run(self): + self.dataPrepWin.saveBkgrData(self.posData) + self.signals.finished.emit(self) + +class DataPrepCropWorker(QObject): + def __init__(self, posData, dataPrepWin): + QObject.__init__(self) + self.signals = signals() + self.logger = workerLogger(self.signals.progress) + self.posData = posData + self.dataPrepWin = dataPrepWin + + @worker_exception_handler + def run(self): + self.dataPrepWin.saveSingleCrop(self.posData, self.posData.cropROIs[0]) + self.signals.finished.emit(self) + class TrackSubCellObjectsWorker(BaseWorkerUtil): sigAskAppendName = Signal(str, list) sigCriticalNotEnoughSegmFiles = Signal(str)