# -*- coding: utf-8 -*- """ GEPARD - Gepard-Enabled PARticle Detection Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für Polymerforschung Dresden e. V. 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 . """ from PyQt5 import QtCore, QtWidgets import numpy as np from multiprocessing import Process, Queue, Event import queue from .imagestitch import imageStacking import sys, os import cv2 from .helperfunctions import cv2imread_fix, cv2imwrite_fix from time import time from .opticalbackground import BackGroundManager from .uielements import TimeEstimateProgressbar from .zlevelsetter import ZLevelSetter from .scenePyramid import ScenePyramid def scan(path, sol, zpositions, grid, controlclass, dataqueue, stopevent, logpath='', ishdr=False): if ishdr: merge_mertens = cv2.createMergeMertens() fp = None if logpath != '': try: fp = open(os.path.join(logpath, 'scanlog.txt'), 'a') sys.stderr = fp sys.stdout = fp except IOError: print('separate logging failed', flush=True) pass print('starting new optical scan', flush=True) ramanctrl = controlclass() ramanctrl.connect() zlist = list(enumerate(zpositions)) for i, p in enumerate(grid): x, y = p z = sol[0]*x + sol[1]*y + sol[2] for k, zk in (zlist if i%2==0 else zlist[::-1]): name = f"image_{i}_{k}.bmp" print("time:", time(), flush=True) zik = z+zk assert not np.isnan(zik) print("moving to:", x, y, zik, flush=True) ramanctrl.moveToAbsolutePosition(x, y, zik) if ishdr: img_list = [] fname = os.path.join(path,f"tmp.bmp") values = [5.,25.,100.] for j, val in enumerate(values if (i%2+k%2)%2==0 else reversed(values)): ramanctrl.setBrightness(val) ramanctrl.saveImage(fname) img_list.append(cv2imread_fix(fname)) res_mertens = merge_mertens.process(img_list) res_mertens_8bit = np.clip(res_mertens*255, 0, 255).astype('uint8') cv2imwrite_fix(os.path.join(path,name), res_mertens_8bit) else: ramanctrl.saveImage(os.path.join(path,name)) if stopevent.is_set(): ramanctrl.disconnect() return dataqueue.put(i) ramanctrl.disconnect() if fp is not None: fp.close() def subtractBackground(image, background): avg = np.mean(background) subtracted = np.clip(np.array(image - background + avg, dtype=np.uint8), 0, 255) return subtracted def legacyLoadAndPasteImage(srcnames, fullimage, fullzval, width, height, rotationvalue, p0, p1, p, background=None): colimgs = [] for name in srcnames: curImg = cv2imread_fix(name) if background is not None: curImg = subtractBackground(curImg, background) colimgs.append(curImg) img, zval = imageStacking(colimgs) x, y = p Nx, Ny = int((p1[0] - p0[0] + width) / width * img.shape[1]), int((p0[1] - p1[1] + height) / height * img.shape[ 0]) + 10 # + 10 because of rotation and hopefully it will be small c, s = np.cos(np.radians(rotationvalue)), np.sin(np.radians(rotationvalue)) dx, dy = (x - p0[0]) / width * img.shape[1], (p0[1] - y) / height * img.shape[0] M = np.float32([[c, s, dx], [-s, c, dy]]) if fullimage is not None: cv2.warpAffine(img, M, (Nx, Ny), fullimage, borderMode=cv2.BORDER_TRANSPARENT) cv2.warpAffine(zval, M, (Nx, Ny), fullzval, borderMode=cv2.BORDER_TRANSPARENT) dst = fullimage zval = fullzval else: dst = cv2.warpAffine(img, M, (Nx, Ny)) zval = cv2.warpAffine(zval, M, (Nx, Ny)) return dst, zval def removeSrcTiles(names, path): files = os.listdir(path) for fpath in names: file = os.path.basename(fpath) if file in files: os.remove(fpath) def loadAndPasteImage(srcnames, pyramid, fullzval, width, height, rotationvalue, p0, p1, p, background=None): """ :param list of str srcnames: list of stacked scan files to merge :param ScenePyramid pyramid: the scene pyramid :param numpy.ndarray fullzval: full size zval image :param float width: width of camera :param float height: height camera :param float rotationvalue: angle of camera :param list of float p0: (min x; max y) of scan tile positions :param list of float p1: (max x; min y) of scan tile positions :param list of float p: position of current scan tile :param numpy.ndarray background: :return: """ colimgs = [] for name in srcnames: curImg = cv2imread_fix(name) if background is not None: curImg = subtractBackground(curImg, background) colimgs.append(curImg) img, zval = imageStacking(colimgs) x, y = p Nx, Ny = int((p1[0]-p0[0]+width)/width*img.shape[1]), int((p0[1]-p1[1]+height)/height*img.shape[0]) + 10 # + 10 because of rotation and hopefully it will be small c, s = np.cos(np.radians(rotationvalue)), np.sin(np.radians(rotationvalue)) dx, dy = (x-p0[0])/width*img.shape[1], (p0[1]-y)/height*img.shape[0] M = np.float32([[c, s, dx], [-s, c, dy]]) dst = None if pyramid is not None: if fullzval is not None: cv2.warpAffine(zval, M, (Nx, Ny), fullzval, borderMode=cv2.BORDER_TRANSPARENT) zval = fullzval else: zval = cv2.warpAffine(zval, M, (Nx, Ny)) pyramid.addSrcTile( img, np.float32([[c, s, 0], [-s, c, 0]]), (dx, dy), (Nx, Ny) ) return dst, zval class PointCoordinates(QtWidgets.QGridLayout): readPoint = QtCore.pyqtSignal(float, float, float, name='readPoint') def __init__(self, N, ramanctrl, parent=None, names=None): super().__init__(parent) self.dswidgets = [] self.N = 0 self.ramanctrl = ramanctrl self.names = names self.pimageOnly = QtWidgets.QPushButton("Image") self.addWidget(self.pimageOnly, 0, 6, QtCore.Qt.AlignRight) self.pimageOnly.released.connect(QtCore.pyqtSlot()(lambda : self.read(-1))) self.createWidgets(N) @QtCore.pyqtSlot() def createWidgets(self, N, pointsgiven=[]): self.validpoints = [False]*N points = np.zeros((N,3)) def connect(button, index): button.released.connect(QtCore.pyqtSlot()(lambda : self.read(index))) for i in range(self.N,min(N,len(self.dswidgets))): self.itemAtPosition(i+1,0).setVisible(True) self.itemAtPosition(i+1,1).setVisible(True) self.itemAtPosition(i+1,2).setVisible(True) self.itemAtPosition(i+1,3).setVisible(True) self.itemAtPosition(i+1,4).setVisible(True) self.itemAtPosition(i+1,5).setVisible(True) self.itemAtPosition(i+1,6).setVisible(True) for i in range(self.N, N): if self.names is not None: lx = QtWidgets.QLabel(f"{self.names[i]} -> x:") else: lx = QtWidgets.QLabel(f"{i+1} -> x:") ly = QtWidgets.QLabel("y:") lz = QtWidgets.QLabel("z:") wx = QtWidgets.QDoubleSpinBox() wy = QtWidgets.QDoubleSpinBox() wz = QtWidgets.QDoubleSpinBox() wx.setDecimals(1) wy.setDecimals(1) wz.setDecimals(1) wx.setRange(-500_000, 500_000) wy.setRange(-500_000, 500_000) wz.setRange(-500_000, 500_000) wx.setValue(points[i,0]) wy.setValue(points[i,1]) wz.setValue(points[i,2]) self.addWidget(lx, i+1, 0, QtCore.Qt.AlignLeft) self.addWidget(wx, i+1, 1, QtCore.Qt.AlignRight) self.addWidget(ly, i+1, 2, QtCore.Qt.AlignLeft) self.addWidget(wy, i+1, 3, QtCore.Qt.AlignRight) self.addWidget(lz, i+1, 4, QtCore.Qt.AlignLeft) self.addWidget(wz, i+1, 5, QtCore.Qt.AlignRight) pread = QtWidgets.QPushButton("read") connect(pread, i) self.addWidget(pread, i+1, 6, QtCore.Qt.AlignRight) self.dswidgets.append([wx,wy,wz]) for i in range(N, len(self.dswidgets)): self.itemAtPosition(i+1,0).setVisible(False) self.itemAtPosition(i+1,1).setVisible(False) self.itemAtPosition(i+1,2).setVisible(False) self.itemAtPosition(i+1,3).setVisible(False) self.itemAtPosition(i+1,4).setVisible(False) self.itemAtPosition(i+1,5).setVisible(False) self.itemAtPosition(i+1,6).setVisible(False) self.N = N for i, p in pointsgiven: wx, wy, wz = self.dswidgets[i] x, y, z = p wx.setValue(x) wy.setValue(y) wz.setValue(z) self.validpoints[i] = True for i in range(len(pointsgiven), N): wx, wy, wz = self.dswidgets[i] wx.setValue(0) wy.setValue(0) wz.setValue(0) self.update() def read(self, index): x, y, z = self.ramanctrl.getPosition() z = self.ramanctrl.getUserZ() if index>=0: wx, wy, wz = self.dswidgets[index] wx.setValue(x) wy.setValue(y) wz.setValue(z) self.validpoints[index] = True self.readPoint.emit(x,y,z) def getPoints(self): points = np.zeros((self.N, 3), dtype=np.double) for i in range(self.N): if self.validpoints[i]: wx, wy, wz = self.dswidgets[i] points[i,0] = wx.value() points[i,1] = wy.value() points[i,2] = wz.value() else: points[i,:] = np.nan return points class OpticalScan(QtWidgets.QWidget): imageUpdate = QtCore.pyqtSignal(str, name='imageUpdate') #str = 'df' (= darkfield) or 'bf' (=bright field) boundaryUpdate = QtCore.pyqtSignal() backGroundSavedToPath = QtCore.pyqtSignal(int, str) def __init__(self, ramanctrl, dataset, logpath='', parent=None): super().__init__(parent, QtCore.Qt.Window) self.logpath = logpath self.view: QtWidgets.QGraphicsView = parent mainLayout = QtWidgets.QVBoxLayout() pointgroup = QtWidgets.QGroupBox("Point coordinates [µm]", self) self.ramanctrl = ramanctrl self.dataset = dataset self.pyramid: ScenePyramid = None self.positions = [] self.process = None self.points = PointCoordinates(5, self.ramanctrl, self) pointgroup.setLayout(self.points) self.points.readPoint.connect(self.takePoint) bkggroup = QtWidgets.QGroupBox('Manage Background Images') self.enableBackGround = QtWidgets.QCheckBox('Enable BackgroundSubtraction') self.enableBackGround.setChecked(False) self.enableBackGround.stateChanged.connect(self.enableDisableBackground) self.backGroundManager = BackGroundManager(self) self.backGroundManager.managerClosed.connect(self.managerWasClosed) self.backGroundManager.readBackground.connect(self.readBackground) self.showBgkManagerBtn = QtWidgets.QPushButton('Show Background Manager Window') self.showBgkManagerBtn.setDisabled(True) self.showBgkManagerBtn.clicked.connect(self.showHideBackgroundWindow) bkglayout = QtWidgets.QVBoxLayout() bkglayout.addWidget(self.enableBackGround) bkglayout.addWidget(self.showBgkManagerBtn) bkggroup.setLayout(bkglayout) self.pareaselect = QtWidgets.QPushButton("Area select", self) label = QtWidgets.QLabel("Size increase:", self) self.radiusincreaseedit = QtWidgets.QDoubleSpinBox(self) self.radiusincreaseedit.setMinimum(-1000) self.radiusincreaseedit.setMaximum(1000) self.radiusincreaseedit.setDecimals(0) self.radiusincreaseedit.setSingleStep(20) self.radiusincreaseedit.setMaximumWidth(100) self.radiusincreaseedit.valueChanged.connect(self.areaSelect) self.hdrcheck = QtWidgets.QCheckBox("High dynamic range", self) self.hdrcheck.setChecked(False) self.zLevelSetter = ZLevelSetter() self.zLevelSetter.sizeChanged.connect(self._updateSize) self.prun = QtWidgets.QPushButton("Run", self) self.pexit = QtWidgets.QPushButton("Cancel", self) self.pareaselect.released.connect(self.areaSelect) self.prun.released.connect(self.run) self.pexit.released.connect(self.stopScan) self.prun.setEnabled(False) self.progressbar = TimeEstimateProgressbar() self.progressbar.disable() radioGroup = QtWidgets.QGroupBox('Shape') radioLayout = QtWidgets.QHBoxLayout() self.circlerad = QtWidgets.QRadioButton("Circle") self.circlerad.clicked.connect(self.areaSelect) self.rectanglerad = QtWidgets.QRadioButton("Rectangle") self.rectanglerad.setChecked(True) self.rectanglerad.clicked.connect(self.areaSelect) radioLayout.addWidget(self.circlerad) radioLayout.addWidget(self.rectanglerad) radioGroup.setLayout(radioLayout) micModeGroup = QtWidgets.QGroupBox('Mode for Image Acquisition') micModeLayout = QtWidgets.QHBoxLayout() self.df_btn = QtWidgets.QRadioButton('Darkfield') self.df_btn.setChecked(True) self.df_btn.clicked.connect(self.areaSelect) self.bf_btn = QtWidgets.QRadioButton('Brightfield') self.bf_btn.clicked.connect(self.areaSelect) micModeLayout.addWidget(self.df_btn) micModeLayout.addWidget(self.bf_btn) micModeGroup.setLayout(micModeLayout) self.areaOptionsGroup = QtWidgets.QGroupBox('Area Select Options') areaLayout = QtWidgets.QFormLayout() areaLayout.addRow(radioGroup) areaLayout.addRow(label, self.radiusincreaseedit) areaLayout.addRow(micModeGroup) if not self.view.ramanSwitchNeeded: micModeGroup.setDisabled(True) areaLayout.addRow(self.pareaselect) self.areaOptionsGroup.setLayout(areaLayout) self.areaOptionsGroup.setDisabled(True) furtherOptionsGroup = QtWidgets.QGroupBox('Further Options') furtherOptionsLayout = QtWidgets.QFormLayout() furtherOptionsLayout.addRow(self.hdrcheck) furtherOptionsGroup.setLayout(furtherOptionsLayout) btnLayout = QtWidgets.QHBoxLayout() btnLayout.addWidget(self.prun) btnLayout.addWidget(self.pexit) btnLayout.addStretch() vbox1 = QtWidgets.QVBoxLayout() vbox2 = QtWidgets.QVBoxLayout() vbox1.addWidget(pointgroup) vbox1.addWidget(self.zLevelSetter) vbox1.addStretch() vbox2.addWidget(self.areaOptionsGroup) vbox2.addWidget(bkggroup) vbox2.addWidget(furtherOptionsGroup) vbox2.addStretch() optionsLayout = QtWidgets.QHBoxLayout() optionsLayout.addLayout(vbox1) optionsLayout.addLayout(vbox2) mainLayout.addLayout(optionsLayout) mainLayout.addWidget(self.progressbar) mainLayout.addLayout(btnLayout) self.setLayout(mainLayout) self.setVisible(False) def enableDisableBackground(self): self.showBgkManagerBtn.setEnabled(self.enableBackGround.isChecked()) if self.enableBackGround.isChecked(): self.backGroundManager.calculateAverageImage() else: self.deleteBackGroundImage() def showHideBackgroundWindow(self): if self.backGroundManager.isHidden(): self.backGroundManager.show() self.showBgkManagerBtn.setText('Hide Background Manager Window') else: self.backGroundManager.hide() self.showBgkManagerBtn.setText('Show Background Manager Window') def managerWasClosed(self): self.showBgkManagerBtn.setText('Show Background Manager Window') @QtCore.pyqtSlot() def _updateSize(self): self.adjustSize() @QtCore.pyqtSlot() def stopScan(self): if self.process is not None and self.process.is_alive(): reply = QtWidgets.QMessageBox.question(self, 'Stop optical scan?', "Do you want to terminate the running scan?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: self.progressbar.resetTimerAndCounter() self.timer.stop() self.processstopevent.set() self.process.join() self.dataqueue.close() self.dataqueue.join_thread() self.view.unblockUI() else: return self.close() @QtCore.pyqtSlot() def areaSelect(self): magn = self.ramanctrl.magn if self.circlerad.isChecked() == True: xym, r = cv2.minEnclosingCircle(np.array([p[:2] for p in self.dataset.fitpoints], dtype=np.float32)) r += self.radiusincreaseedit.value() phi = np.linspace(0, 2*np.pi, magn, endpoint=False) self.dataset.boundary = [[xym[0]+r*np.cos(phii), xym[1]+r*np.sin(phii)] for phii in phi] else: da = self.radiusincreaseedit.value() x0, x1 = self.dataset.fitpoints[:,0].min()-da, self.dataset.fitpoints[:,0].max()+da y0, y1 = self.dataset.fitpoints[:,1].min()-da, self.dataset.fitpoints[:,1].max()+da a = 2*(y1-y0 + x1-x0) nx, ny = max(int(np.round((x1-x0)/a*magn)),2), max(int(np.round((y1-y0)/a*magn)),2) x, dx = np.linspace(x0, x1, nx, endpoint=False, retstep=True) y, dy = np.linspace(y0, y1, ny, endpoint=False, retstep=True) self.dataset.boundary = [[xi, yi] for xi, yi in zip(x,y0*np.ones_like(x))] + \ [[xi, yi] for xi, yi in zip(x1*np.ones_like(y),y)] + \ [[xi, yi] for xi, yi in zip(x[::-1]+dx,y1*np.ones_like(x))] + \ [[xi, yi] for xi, yi in zip(x0*np.ones_like(y),y[::-1]+dy)] self.boundaryUpdate.emit() self.prun.setEnabled(True) def setPyramid(self, pyramid): self.pyramid = pyramid def resetDataset(self, ds): self.dataset = ds self.points.createWidgets(5, list(zip(ds.fitindices,ds.fitpoints))) if len(self.dataset.fitindices)>1: self.areaOptionsGroup.setEnabled(True) softwarez = self.ramanctrl.getSoftwareZ() if abs(softwarez) >0.1: reply = QtWidgets.QMessageBox.critical(self, 'Software z position nonzero', "The software z position needs to be set to zero."\ " Moving z for %4.0f µm relative to current position. Counteract manually before proceeding!"%(-softwarez), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Abort, QtWidgets.QMessageBox.Abort) if reply == QtWidgets.QMessageBox.Yes: self.ramanctrl.moveZto(0.0) else: QtWidgets.QMessageBox.information(self, "Information", 'Scan may stop if the software z position gets to high!', QtWidgets.QMessageBox.Ok) @QtCore.pyqtSlot(float, float, float) def takePoint(self, x, y, z): if self.dataset.heightmap is not None: reply = QtWidgets.QMessageBox.critical(self, 'Dataset already contains optical scan data', "Continuation will invalidate all previous results! Continue anyway?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if reply != QtWidgets.QMessageBox.Yes: return self.ramanctrl.saveImage(self.dataset.getTmpImageName()) width, height, rotationvalue = self.ramanctrl.getImageDimensions(self.view.microscopeMode) pshift = self.ramanctrl.getRamanPositionShift() self.dataset.pshift = pshift 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] self.dataset.imagedim_df = self.ramanctrl.getImageDimensions('df') self.dataset.pixelscale_df = self.dataset.imagedim_df[0]/img.shape[1] #=imagedim_width/shape[1] points = self.points.getPoints() ind = np.isfinite(points[:,0]) self.dataset.fitindices = np.arange(points.shape[0])[ind] points = points[ind,:].copy() self.dataset.fitpoints = points if len(points)>1: self.areaOptionsGroup.setEnabled(True) points = np.concatenate(([[x,y,z]], points), axis=0) p0 = [points[:,0].min(), points[:,1].max()] p1 = [points[:,0].max(), points[:,1].min()] if self.dataset.maxdim is not None: p0 = [min(p0[0], self.dataset.maxdim[0]), max(p0[1], self.dataset.maxdim[1])] p1 = [max(p1[0], self.dataset.maxdim[2]), min(p1[1], self.dataset.maxdim[3])] Nx, Ny = int((p1[0]-p0[0]+width)/width*img.shape[1]), int((p0[1]-p1[1]+height)/height*img.shape[0]) + 10 # + 10 because of rotation and hopefully it will be small c, s = np.cos(np.radians(rotationvalue)), np.sin(np.radians(rotationvalue)) dx, dy = (x-p0[0])/width*img.shape[1], (p0[1]-y)/height*img.shape[0] M = np.float32([[c, s, dx], [-s, c, dy]]) #dst = cv2.warpAffine(img, M, (Nx, Ny)) M_rot = np.float32([[c, s, 0], [-s, c, 0]]) img_rot = cv2.warpAffine(img, M_rot, (img.shape[1], img.shape[0] + 10)) # calc new pixel start coords for tile # @see https://stackoverflow.com/a/43166421/9880753 c_dest = cv2.transform(np.float32([[(0, 0)]]), np.float32([[c, s, dx], [-s, c, dy]])) self.pyramid.addSrcTileSimple( img_rot, (c_dest[0][0][0], c_dest[0][0][1]), p0 ) #if self.view.imgdata is not None and self.dataset.lastpos is not None: # lp = self.dataset.lastpos # dx, dy = (lp[0]-p0[0])/width*img.shape[1], (p0[1]-lp[1])/height*img.shape[0] # full = self.view.imgdata # M = np.float32([[1,0,dx],[0,1,dy]]) # try: # full = cv2.warpAffine(full, M, (Nx, Ny)) #fails, if image dimensions are >32767x32767px... # dst = cv2.max(full, dst) # except: # QtWidgets.QMessageBox.critical(self, 'Error', 'Image is too large\nSelect smaller region.') # raise # return #self.view.imgdata = dst self.dataset.lastpos = p0 self.dataset.maxdim = p0 + p1 self.dataset.readin = False self.imageUpdate.emit(self.view.microscopeMode) @QtCore.pyqtSlot(int) def readBackground(self, indexOfCallingImage): tmp_path = self.dataset.getTmpImageName() self.ramanctrl.saveImage(tmp_path) self.backGroundSavedToPath.emit(indexOfCallingImage, tmp_path) def writeBackGroundImage(self, backgroundImg): cv2imwrite_fix(self.dataset.getBackgroundImageName(), cv2.cvtColor(backgroundImg, cv2.COLOR_RGB2BGR)) def deleteBackGroundImage(self): if os.path.exists(self.dataset.getBackgroundImageName()): os.remove(self.dataset.getBackgroundImageName()) @QtCore.pyqtSlot() def run(self): ramanPoints = self.dataset.particleContainer.getMeasurementPixelCoords() if len(ramanPoints) != 0: reply = QtWidgets.QMessageBox.critical(self, 'Dataset already contains raman scan points', "Continuation will invalidate all previous results! Continue anyway?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if reply != QtWidgets.QMessageBox.Yes: return if self.dataset.readin: reply = QtWidgets.QMessageBox.critical(self, 'Dataset is newly read from disk!', "Coordinate systems might have changed since. Do you want to continue with saved coordinates?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: self.dataset.readin = False else: return if os.path.exists(self.dataset.getBackgroundImageName()): reply = QtWidgets.QMessageBox.critical(self, 'Background correction info.', "A background image was saved. All acquired images will be corrected. Continue?\nOtherwise delete images in background manager", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes) if reply != QtWidgets.QMessageBox.Yes: return self.view.imparent.ramanSwitch.df_btn.setChecked(self.df_btn.isChecked()) self.view.imparent.ramanSwitch.setDisabled(True) self.view.setMicroscopeMode() if self.df_btn.isChecked(): self.view.dataset.imagescanMode = 'df' else: self.view.dataset.imagescanMode = 'bf' points = np.float32(self.dataset.fitpoints) # convert z to software z, which is relative to current user z softwarez = self.ramanctrl.getSoftwareZ() # get current software z points[:,2] += softwarez-self.ramanctrl.getUserZ() try: zrange = self.zLevelSetter.getZLevels() except ValueError: return zmaxstack = max(zrange) if len(zrange) == 1: zmaxstack = 0.0 self.dataset.zpositions = np.array([0.0]) else: # self.dataset.zpositions = np.linspace(0, zmaxstack, Nz) self.dataset.zpositions = zrange width, height, rotationvalue = self.dataset.imagedim_df print("Width, height, rotation:", width, height, rotationvalue) print("Points x:", points[:,0].min(), points[:,0].max()) print("Points y:", points[:,1].min(), points[:,1].max()) print("Points z:", points[:,2].min(), points[:,2].max()) A = np.ones((points.shape[0],3)) A[:,:2] = points[:,:2] b = points[:,2] sol = np.linalg.lstsq(A, b, rcond=None)[0] self.dataset.heightmap = sol print("Fit deviation:", sol[0]*points[:,0]+sol[1]*points[:,1]+sol[2] -points[:,2] ) path = self.dataset.getScanPath() # get zmin and zmax in absolut software z coordinates zmin, zmax = None, None for i, p in enumerate(self.dataset.grid): x, y = p z = sol[0]*x + sol[1]*y + sol[2] if i==0: zmin, zmax = z, z else: if zmin>z: zmin = z if zmax= 0: Ngrid = len(self.dataset.grid) names = [] for k in range(len(self.dataset.zpositions)): names.append(os.path.join(self.dataset.getScanPath(), f"image_{i}_{k}.bmp")) width, height, rotationvalue = (self.dataset.imagedim_df if self.view.imparent.ramanSwitch.df_btn.isChecked() else self.dataset.imagedim_bf) p = self.dataset.grid[i] p0, p1 = self.dataset.maxdim[:2], self.dataset.maxdim[2:] if os.path.exists(self.dataset.getBackgroundImageName()): background_img = cv2imread_fix(self.dataset.getBackgroundImageName()) else: background_img = None self.view.imgdata, self.dataset.zvalimg = loadAndPasteImage( names, self.pyramid, self.dataset.zvalimg, width, height, rotationvalue, p0, p1, p, background=background_img ) removeSrcTiles(names, self.dataset.getScanPath()) self.progressbar.setValue(i+1) self.imageUpdate.emit(self.view.microscopeMode) if i==Ngrid-1: # cv2imwrite_fix(self.dataset.getImageName(), cv2.cvtColor(self.view.imgdata, cv2.COLOR_RGB2BGR)) self.dataset.saveZvalImg() self.process.join() self.dataqueue.close() self.dataqueue.join_thread() self.pyramid.toDataset() self.ramanctrl.connect() self.view.saveDataSet() self.view.unblockUI() self.view.switchMode("ParticleDetection") self.progressbar.resetTimerAndCounter() self.progressbar.disable() self.close() return self.timer.start(100.) def closeEvent(self, event): self.backGroundManager.close() event.accept()