Skip to content

Commit

Permalink
Many bug fixes for relative/absolute stage operation, Finalizing the …
Browse files Browse the repository at this point in the history
…Histoscanner
  • Loading branch information
beniroquai committed Nov 2, 2022
1 parent c63db6b commit 5f863eb
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 178 deletions.
2 changes: 1 addition & 1 deletion imswitch/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = '1.2.0'
__version__ = '1.2.9'


# Copyright (C) 2020-2021 ImSwitch developers
Expand Down
222 changes: 70 additions & 152 deletions imswitch/imcontrol/controller/controllers/HistoScanController.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,21 @@ def __init__(self, *args, **kwargs):
self.zStackStep = 0

# store old values
self.Laser1ValueOld = 0
self.Laser2ValueOld = 0
self.LEDValueOld = 0

self.Laser1Value = 0
self.Laser2Value = 0
self.LEDValue = 0

# stage-related variables
self.speed = 10000
self.positionMoveManual = 1000

self.HistoScanFilename = ""

self.updateRate=2

self.pixelsizeZ=10

self.tUnshake = .1



# physical coordinates (temporarily)
self.stepsizeX = 1
self.stepsizeY = 1
Expand Down Expand Up @@ -95,17 +95,13 @@ def __init__(self, *args, **kwargs):
allDetectorNames = self._master.detectorsManager.getAllDeviceNames()
self.detector = self._master.detectorsManager[allDetectorNames[0]]

# select illumination sources
self.leds = []
allLaserNames = self._master.lasersManager.getAllDeviceNames()
for iDevice in allLaserNames:
allIlluNames = self._master.lasersManager.getAllDeviceNames()
for iDevice in allIlluNames:
if iDevice.find("LED")>=0:
self.leds.append(self._master.lasersManager[iDevice])

if len(self._master.LEDMatrixsManager.getAllDeviceNames())>0:
self.illu = self._master.LEDMatrixsManager[self._master.LEDMatrixsManager.getAllDeviceNames()[0]]
else:
self.illu = []

# select stage
self.stages = self._master.positionersManager[self._master.positionersManager.getAllDeviceNames()[0]]

Expand Down Expand Up @@ -162,16 +158,6 @@ def stopHistoScan(self):
self._widget.setInformationLabel("Done wit timelapse...")

def showLast(self):
try:
self._widget.setImage(self.LastStackLaser1ArrayLast, colormap="green", name="GFP",pixelsizeZ=self.pixelsizeZ)
except Exception as e:
self._logger.error(e)

try:
self._widget.setImage(self.LastStackLaser2ArrayLast, colormap="red", name="Red",pixelsizeZ=self.pixelsizeZ)
except Exception as e:
self._logger.error(e)

try:
self._widget.setImage(self.LastStackLEDArrayLast, colormap="gray", name="Brightfield",pixelsizeZ=self.pixelsizeZ)
except Exception as e:
Expand Down Expand Up @@ -217,78 +203,64 @@ def doScan(self):
# compute cordinates of the miroscope stage and export list of coordinates

# 4. Compute coordinates for high-res image / tiles
cordinateList = np.array(np.where(scanRegionMicroscsope==1)).T*(self.camPixNumX*self.camPixelsize,self.camPixNumY*self.camPixelsize) # each row is one FOV
coordinateList = np.array(np.where(scanRegionMicroscsope==1)).T*(self.camPixNumX*self.camPixelsize,self.camPixNumY*self.camPixelsize) # each row is one FOV

if len(coordinateList) <= 0:
self._logger.debug("No selection was made..")
self._widget.HistoScanStartButton.setEnabled(True)
self._widget.setInformationLabel("No selection was made..")
return

# 6. TODO: Sort list for faster acquisition

# 6. iterate over all tiles and take images

# 7. visualize images/current position in canvas

# this should decouple the hardware-related actions from the GUI
self.isHistoScanrunning = True
self.HistoScanThread = threading.Thread(target=self.doScanThread, args=(cordinateList), daemon=True)
self.HistoScanThread = threading.Thread(target=self.doScanThread, args=(coordinateList,), daemon=True)
self.HistoScanThread.start()

def doAutofocus(self, params):
self._logger.info("Autofocusing...")
isRunInBackground = False
self._commChannel.sigAutoFocus.emit(int(params["valueRange"]), int(params["valueSteps"], isRunInBackground))

def doScanThread(self, cordinateList):

# 5. move to first tile and take image
self._widget.setInformationLabel("Moving to first tile: " + str(cordinateList[0])+ " µm ")
self.stages.move(value=cordinateList[0,0], axis="X", is_absolute=True, is_blocking=True)
self.stages.move(value=cordinateList[0,1], axis="Y", is_absolute=True, is_blocking=True)

for i in range(len(cordinateList)):

coordX = cordinateList[i,0]
coordY = cordinateList[i,1]
self._widget.setInformationLabel("Moving to : " + (coordX,coordY)+ " µm ")
def doScanThread(self, coordinateList):
# store initial stage position
initialPosition = (self.stages.get_abs(axis=1),self.stages.get_abs(axis=2))

# reserve and free space for displayed stacks
self.LastStackLED = []

self.stages.move(value=coordX, axis="X", is_absolute=True, is_blocking=True)
self.stages.move(value=coordY, axis="Y", is_absolute=True, is_blocking=True)


# this wil run i nthe background
self._logger.debug("Take image")
zstackParams = self._widget.getZStackValues()
# reserve and free space for displayed stacks
self.LastStackLaser1 = []
self.LastStackLaser2 = []
self.LastStackLED = []
for iPos in range(len(coordinateList)):
# move to location
self._widget.setInformationLabel("Moving to : " + str(coordinateList[iPos,:]) + " µm ")
self.stages.move(value=coordinateList[iPos,:], axis="XY", speed=(self.speed,self.speed), is_absolute=True, is_blocking=True)
#self.stages.move(value=coordinateList[iPos,0], axis="X", speed=(self.speed), is_absolute=True, is_blocking=True)
#self.stages.move(value=coordinateList[iPos,1], axis="Y", speed=(self.speed), is_absolute=True, is_blocking=True)

# want to do autofocus?
autofocusParams = self._widget.getAutofocusValues()
if self._widget.isAutofocus() and np.mod(self.nImages, int(autofocusParams['valuePeriod'])) == 0:
self._widget.setInformationLabel("Autofocusing...")
self.doAutofocus(autofocusParams)



if self.Laser1Value>0:
self.takeImageIllu(illuMode = "Laser1", intensity=self.Laser1Value, timestamp=self.nImages, zstackParams=zstackParams)
if self.Laser2Value>0:
self.takeImageIllu(illuMode = "Laser2", intensity=self.Laser2Value, timestamp=self.nImages, zstackParams=zstackParams)
if self.LEDValue>0:
self.takeImageIllu(illuMode = "Brightfield", intensity=self.LEDValue, timestamp=self.nImages, zstackParams=zstackParams)

self.nImages += 1
self._widget.setInformationLabel(self.nImages)

self.isHistoScanrunning = False

self.LastStackLaser1ArrayLast = np.array(self.LastStackLaser1)
self.LastStackLaser2ArrayLast = np.array(self.LastStackLaser2)
self.LastStackLEDArrayLast = np.array(self.LastStackLED)

# turn on illumination # TODO: ensure it's the right light source!
zstackParams = self._widget.getZStackValues()
self._logger.debug("Take image")
self.takeImageIlluStack(xycoords = coordinateList[iPos,:], intensity=self.LEDValue, zstackParams=zstackParams)

self._widget.HistoScanShowLastButton.setEnabled(True)
# move stage back to origine
self.stages.move(value=initialPosition, axis="XY", speed=(self.speed,self.speed), is_absolute=True, is_blocking=True)

# done with scan
self._widget.setInformationLabel("Done")
self._widget.HistoScanStartButton.setEnabled(True)
self.isHistoScanrunning = False
self.LastStackLEDArrayLast = np.array(self.LastStackLED)
self._widget.HistoScanShowLastButton.setEnabled(True)


def takeImageIllu(self, illuMode, intensity, timestamp=0, zstackParams=None):
self._logger.debug("Take image: " + illuMode + " - " + str(intensity))
def takeImageIlluStack(self, xycoords, intensity, zstackParams=None):
self._logger.debug("Take image: " + str(xycoords) + " - " + str(intensity))
fileExtension = 'tif'

# sync with camera frame
Expand All @@ -307,100 +279,45 @@ def takeImageIllu(self, illuMode, intensity, timestamp=0, zstackParams=None):
for iZ in np.arange(zstackParams[0], zstackParams[1], zstackParams[2]):
stepsCounter += zstackParams[2]
self.stages.move(value=zstackParams[2], axis="Z", is_absolute=False, is_blocking=True)
filePath = self.getSaveFilePath(date=self.HistoScanDate, filename=f'{self.HistoScanFilename}_{illuMode}_t_{timestamp}_Z_{stepsCounter}', extension=fileExtension)
filePath = self.getSaveFilePath(date=self.HistoScanDate, filename=f'{self.HistoScanFilename}_X{xycoords[0]}_Y{xycoords[1]}_Z_{stepsCounter}', extension=fileExtension)

# turn on illuminationn
if illuMode == "Laser1" and len(self.lasers)>0:
self.lasers[0].setValue(intensity)
self.lasers[0].setEnabled(True)
elif illuMode == "Laser2" and len(self.lasers)>1:
self.lasers[1].setValue(intensity)
self.lasers[1].setEnabled(True)
elif illuMode == "Brightfield":
try:
if intensity > 255: intensity=255
if intensity < 0: intensity=0
if len(self.leds)>0:
self.leds[0].setValue(intensity)
self.leds[0].setEnabled(True)
except:
pass
self.leds[0].setValue(intensity)
self.leds[0].setEnabled(True)
time.sleep(self.tUnshake) # unshake
lastFrame = self.detector.getLatestFrame()
self.switchOffIllumination()
self.leds[0].setEnabled(False)

# write out filepath
self._logger.debug(filePath)
tif.imwrite(filePath, lastFrame, append=True)

# store frames for displaying
if illuMode == "Laser1":
self.LastStackLaser1.append(lastFrame.copy())
if illuMode == "Laser2":
self.LastStackLaser2.append(lastFrame.copy())
if illuMode == "Brightfield":
self.LastStackLED.append(lastFrame.copy())
self.LastStackLED.append(lastFrame.copy())
self.stages.setEnabled(is_enabled=False)
self.stages.move(value=-(zstackParams[1]+backlash), axis="Z", is_absolute=False, is_blocking=True)

else:
# turn on illuminationn
if illuMode == "Laser1" and len(self.lasers)>0:
self.lasers[0].setValue(intensity)
self.lasers[0].setEnabled(True)
elif illuMode == "Laser2" and len(self.lasers)>1:
self.lasers[1].setValue(intensity)
self.lasers[1].setEnabled(True)
elif illuMode == "Brightfield":
try:
if intensity > 255: intensity=255
if intensity < 0: intensity=0
if len(self.leds)>0:
self.leds[0].setValue(intensity)
self.leds[0].setEnabled(True)
except:
pass
filePath = self.getSaveFilePath(date=self.HistoScanDate, filename=f'{self.HistoScanFilename}_{illuMode}_t_{timestamp}', extension=fileExtension)
self.leds[0].setValue(intensity)
self.leds[0].setEnabled(True)

filePath = self.getSaveFilePath(date=self.HistoScanDate, filename=f'{self.HistoScanFilename}_X{xycoords[0]}_Y{xycoords[1]}', extension=fileExtension)
lastFrame = self.detector.getLatestFrame()
self._logger.debug(filePath)
tif.imwrite(filePath, lastFrame)
# store frames for displaying
if illuMode == "Laser1":
self.LastStackLaser1.append(lastFrame.copy())
if illuMode == "Laser2":
self.LastStackLaser2.append(lastFrame.copy())
if illuMode == "Brightfield":
self.LastStackLED.append(lastFrame.copy())

self.switchOffIllumination()


def switchOffIllumination(self):
# switch off all illu sources
for lasers in self.lasers:
lasers.setEnabled(False)
lasers.setValue(0)
time.sleep(0.1)
if len(self.leds)>0:
self.illu.setAll((0,0,0))


def valueLaser1Changed(self, value):
self.Laser1Value= value
self._widget.HistoScanLabelLaser1.setText('Intensity (Laser 1):'+str(value))
if len(self.lasers)>0:
self.lasers[0].setValue(self.Laser1Value)
self.LastStackLED.append(lastFrame.copy())
self.leds[0].setEnabled(False)

def valueLaser2Changed(self, value):
self.Laser2Value = value
self._widget.HistoScanLabelLaser2.setText('Intensity (Laser 2):'+str(value))
if len(self.lasers)>1:
self.lasers[1].setValue(self.Laser2Value)

def valueLEDChanged(self, value):
self.LEDValue= value
self.LEDValue = value
self._widget.HistoScanLabelLED.setText('Intensity (LED):'+str(value))
if len(self.leds):
self.illu.setAll(state=(1,1,1), intensity=(self.LEDValue,self.LEDValue,self.LEDValue))

self.leds[0].setEnabled(True)
self.leds[0].setValue(self.LEDValue)

def __del__(self):
self.imageComputationThread.quit()
self.imageComputationThread.wait()
Expand All @@ -417,18 +334,19 @@ def getSaveFilePath(self, date, filename, extension):

return newPath


def moveUp(self):
self._logger.info("Moving up...")
# move stage in Y direction
self.stages.move(value=self.positionMoveManual, axis="Y", is_absolute=False, is_blocking=True)

def moveDown(self):
self._logger.info("Moving down...")

self.stages.move(value=-self.positionMoveManual, axis="Y", is_absolute=False, is_blocking=True)
def moveLeft(self):
self._logger.info("Moving left...")
self.stages.move(value=self.positionMoveManual, axis="X", is_absolute=False, is_blocking=True)

def moveRight(self):
self._logger.info("Moving right...")
self.stages.move(value=-self.positionMoveManual, axis="X", is_absolute=False, is_blocking=True)

def snapPreview(self):
self._logger.info("Snap preview...")
Expand Down
38 changes: 27 additions & 11 deletions imswitch/imcontrol/model/managers/positioners/ESP32StageManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,21 +117,37 @@ def __init__(self, positionerInfo, name, **lowLevelManagers):
def setupMotor(self, minPos, maxPos, stepSize, backlash, axis):
self._motor.setup_motor(axis=axis, minPos=minPos, maxPos=maxPos, stepSize=stepSize, backlash=backlash)

def move(self, value=0, axis="X", is_absolute=False, is_blocking=False):
def move(self, value=0, axis="X", speed=None, is_absolute=False, is_blocking=False):
if speed is None:
if axis == "X": speed = self.speed["X"]
if axis == "Y": speed = self.speed["Y"]
if axis == "Z": speed = self.speed["Z"]
if axis == "XY": speed = (self.speed["X"], self.speed["Y"])
if axis == "XYZ": speed = (self.speed["X"], self.speed["Y"], self.speed["Z"])

if axis == 'X':
self._motor.move_x(value, self.speed["X"], is_absolute=is_absolute, is_enabled=self.is_enabled, is_blocking=is_blocking)
self._position[axis] = self._position[axis] + value
self._motor.move_x(value, speed, is_absolute=is_absolute, is_enabled=self.is_enabled, is_blocking=is_blocking)
if is_absolute: self._position[axis] = self._position[axis] + value
else: self._position[axis] = value
elif axis == 'Y':
self._motor.move_y(value, self.speed["Y"], is_absolute=is_absolute, is_enabled=self.is_enabled, is_blocking=is_blocking)
self._position[axis] = self._position[axis] + value
self._motor.move_y(value, speed, is_absolute=is_absolute, is_enabled=self.is_enabled, is_blocking=is_blocking)
if is_absolute: self._position[axis] = self._position[axis] + value
else: self._position[axis] = value
elif axis == 'Z':
self._motor.move_z(value, self.speed["Z"], is_absolute=is_absolute, is_enabled=self.is_enabled, is_blocking=is_blocking)
self._position[axis] = self._position[axis] + value
self._motor.move_z(value, speed, is_absolute=is_absolute, is_enabled=self.is_enabled, is_blocking=is_blocking)
if is_absolute: self._position[axis] = self._position[axis] + value
else: self._position[axis] = value
elif axis == 'XY':
self._motor.move_xy(value, speed, is_absolute=is_absolute, is_enabled=self.is_enabled, is_blocking=is_blocking)
for i, iaxis in enumerate(("X", "Y")):
if is_absolute: self._position[iaxis] = self._position[iaxis] + value[i]
else: self._position[iaxis] = value[i]
elif axis == 'XYZ':
self._motor.move_xyz(value, self.speed, is_absolute=is_absolute, is_enabled=self.is_enabled, is_blocking=is_blocking)
self._position["X"] = self._position["X"] + value[0]
self._position["Y"] = self._position["Y"] + value[1]
self._position["Z"] = self._position["Z"] + value[2]
self._motor.move_xyz(value, speed, is_absolute=is_absolute, is_enabled=self.is_enabled, is_blocking=is_blocking)
for i, iaxis in enumerate(("X", "Y")):
if is_absolute: self._position[iaxis] = self._position[iaxis] + value[i]
else: self._position[iaxis] = value[i]

else:
print('Wrong axis, has to be "X" "Y" or "Z".')
return
Expand Down
Loading

0 comments on commit 5f863eb

Please sign in to comment.