import greenlet from PyQt5 import Qt import sys from acquisition.pedals.pedals import Pedals class WaitDialog(Qt.QDialog): def __init__(self, dialog, pedalIdx, mainGt): super().__init__(dialog) self._dialog = dialog self._pedalIdx = pedalIdx self._mainGt = mainGt self.setWindowModality(Qt.Qt.NonModal) self.setWindowTitle('Green Qt Test') self.setLayout(Qt.QVBoxLayout()) self.layout().addWidget(Qt.QLabel('Waiting for pedal {} up...'.format(self._pedalIdx))) self._cancelBtn = Qt.QPushButton('cancel') self.layout().addWidget(self._cancelBtn) self._cancelBtn.clicked.connect(self.cancel) self.waitForPedalGt = greenlet.greenlet(self.waitForPedal) def waitForPedal(self): self._dialog.pedalWaitBtns[self._pedalIdx].setEnabled(False) if self._mainGt.switch(): Qt.QMessageBox.information(self, 'Green Qt Test', 'Pedal {} up received.'.format(self._pedalIdx)) self._dialog.pedalWaitBtns[self._pedalIdx].setEnabled(True) self._dialog.pedalWaitDlgs[self._pedalIdx] = None self.close() self.destroy() def cancel(self): self.waitForPedalGt.switch(False) class Dialog(Qt.QDialog): def __init__(self, mainGt): super().__init__() self._mainGt = mainGt self.setWindowTitle('Green Qt Test') layout = Qt.QVBoxLayout() self.setLayout(layout) self.quitButton = Qt.QPushButton('quit') layout.addWidget(self.quitButton) self.quitButton.clicked.connect(self.close) self.pedalWaitBtns = [None, None] self.pedalWaitDlgs = [None, None] self.pedalWaitBtns[0] = Qt.QPushButton('wait for pedal 0 up') layout.addWidget(self.pedalWaitBtns[0]) self.pedalWaitBtns[0].clicked.connect(lambda: self.pedalWaitBtnClickedSlot(0)) self.pedalWaitBtns[1] = Qt.QPushButton('wait for pedal 1 up') layout.addWidget(self.pedalWaitBtns[1]) self.pedalWaitBtns[1].clicked.connect(lambda: self.pedalWaitBtnClickedSlot(1)) self.pedals = Pedals() self.pedals.pedalUpChanged.connect(self.pedalUpChangedSlot) def pedalWaitBtnClickedSlot(self, pedalIdx): if self.pedalWaitDlgs[pedalIdx] is None: self.pedalWaitDlgs[pedalIdx] = WaitDialog(self, pedalIdx, self._mainGt) self.pedalWaitDlgs[pedalIdx].show() self.pedalWaitDlgs[pedalIdx].waitForPedalGt.switch() def pedalUpChangedSlot(self, pedalIdx, isUp): if isUp and self.pedalWaitDlgs[pedalIdx] is not None: self.pedalWaitDlgs[pedalIdx].waitForPedalGt.switch(True) if __name__ == '__main__': app = Qt.QApplication(sys.argv) def eventLoop(): app.exec_() eventLoopGt = greenlet.greenlet(eventLoop) dialog = Dialog(eventLoopGt) dialog.show() eventLoopGt.switch()
import greenlet import numpy from PyQt5 import Qt import sys import time def _brenner(im, direction): if direction == 'h': xo = 2 yo = 0 elif direction == 'v': xo = 0 yo = 2 else: raise ValueError('direction must be h or v.') iml = numpy.pad(im[0:im.shape[0]-yo, 0:im.shape[1]-xo], ((yo, 0), (xo, 0)), mode='constant') imr = im.copy() if direction == 'h': imr[:, :xo] = 0 else: imr[:yo, :] = 0 return iml - imr def brennerFocusMeasure(im): imh = _brenner(im, 'h') imv = _brenner(im, 'v') return numpy.sqrt(imh**2 + imv**2) class LinearSearchAutofocuser: def __init__(self, camera, zDrive, minZ, maxZ, stepsPerRound, numberOfRounds, focusMeasure=brennerFocusMeasure, rw=None): self._running = False self._camera = camera self._zDrive = zDrive self._zRange = (minZ, maxZ) self._stepsPerRound = stepsPerRound self._numberOfRounds = numberOfRounds self._focusMeasure = focusMeasure self._rw = rw self.idleGt = greenlet.getcurrent() self.runGt = greenlet.greenlet(self._run) self.bestZ = None def abort(self): if self._running: self.runGt.switch(False) def _run(self): t0 = time.time() self.bestZ = None self._zDrive.posChanged.connect(self._zDrivePosChanged) self._running = True curZRange = self._zRange buffers = [self._camera.makeAcquisitionBuffer() for i in range(self._stepsPerRound)] for roundIdx in range(0, self._numberOfRounds): print('Starting autofocus round {}.'.format(roundIdx)) fmvs = [] stepIdxsDone = [] if roundIdx == 0: # The first round computes focus measures for every step in the Z range, including endpoints steps = numpy.linspace(*curZRange, num=self._stepsPerRound) else: # Every subsequent round's Z range is the interval between the previous round's Z steps bracketing the Z step # with the highest focus measure value: # # ----- Previous Z step above best previous Z # | --- Current Z step # | --- " # | --- " # ----- Previous Z step with best focus measure value # | --- Current Z step # | --- " # | --- " # ----- Previous Z step below best previous Z # # So, if a subsequent round's step sequence is computed in the same manner as the first round's step sequence, # it will include the previous round's bracketing Z step positions as endpoints, repeating the focus measure # computation for those positions, increasing the computational expense of the linear search. This is most clearly # illustrated by the case where stepsPerRound is 3 and first round best Z step is the middle position: in this case, # the linear search will repeat the same calculations at the same Z step positions for an arbitrary number of rounds, # failing to refine the best Z step position. # # This is avoided while maintaining uniform step size by treating subsequent Z ranges as an open interval bounded # by bracketing Z step positions. If stepsPerRound is odd, the best previous Z step position focus measure is # still recomputed, but this is considered acceptable. steps = numpy.linspace(*curZRange, num=self._stepsPerRound+2)[1:-1] self._camera._camera.AT_Flush() self._camera.shutter = self._camera.Shutter.Rolling self._camera.triggerMode = self._camera.TriggerMode.Software self._camera.cycleMode = self._camera.CycleMode.Fixed self._camera.frameCount = self._stepsPerRound print(self._camera.frameCount) for buffer in buffers: self._camera._camera.AT_QueueBuffer(buffer) self._camera._camera.AT_Command(self._camera._camera.Feature.AcquisitionStart) for stepIdx, z in enumerate(steps): self._zDrive.pos = z # Return to main event loop (or whatever was the enclosing greenlet when this class was instantiated) until # the stage stops moving, aborting and cleaning up if resumed with switch(False) if not self.idleGt.switch(): print('Autofocus aborted.') self._camera._camera.AT_Command(self._camera._camera.Feature.AcquisitionStop) self._camera._camera.AT_Flush() self._running = False self._zDrive.posChanged.disconnect(self._zDrivePosChanged) raise greenlet.GreenletExit() # Resuming after stage has moved actualZ = self._zDrive.pos if abs(actualZ - z) > self._zDrive._factor - sys.float_info.epsilon: w = 'Current Z position ({0}) does not match requested Z position ({1}). ' w+= 'The autofocus step for {1} is being skipped. This can occur if the requested Z position ' w+= 'is out of range or if the scope\'s Z position controller has been moved during the ' w+= 'auto-focus operation.' print(w.format(actualZ, z)) continue # Stage Z position is actually what we requested. Command exposure. self._camera.commandSoftwareTrigger() print('exposed ', stepIdx) stepIdxsDone.append(stepIdx) # Retrieve, show, and process resulting exposures for bufferIdx, stepIdx in enumerate(stepIdxsDone): self._camera._camera.AT_WaitBuffer(1000) print('got buffer', bufferIdx) buffer = buffers[bufferIdx] if self._rw is not None: self._rw.showImage(buffer) im = buffer.astype(numpy.float32) / 65535 fmv = (self._focusMeasure(im)**2).sum() print('round={:02}, z={:<10}, focus_measure={}'.format(roundIdx, steps[stepIdx], fmv)) fmvs.append((steps[stepIdx], fmv)) self._camera._camera.AT_Command(self._camera._camera.Feature.AcquisitionStop) if len(fmvs) == 0: print('Failed to move to any of the Z step positions.') self._running = False self._zDrive.posChanged.disconnect(self._zDrivePosChanged) raise greenlet.GreenletExit() if len(fmvs) == 1: print('Successfully moved to only one of the Z step positions, making that position the best found.') self.bestZ = fmvs[0][0] self._running = False self._zDrive.posChanged.disconnect(self._zDrivePosChanged) raise greenlet.GreenletExit() bestZIdx = numpy.array([fmv[1] for fmv in fmvs], dtype=numpy.float64).argmax() if roundIdx + 1 < self._numberOfRounds: # Next round, search the range between the steps adjacent to the best step curZRange = ( steps[max(bestZIdx - 1, 0)], steps[min(bestZIdx + 1, len(fmvs) - 1)] ) else: # There is no next round. Store result. self.bestZ = steps[bestZIdx] print('Autofocus completed ({}s).'.format(time.time() - t0)) self._running = False self._zDrive.posChanged.disconnect(self._zDrivePosChanged) def _zDrivePosChanged(self, _): if self._running and not self._zDrive.moving: self.runGt.switch(True) def linearSearchAutofocus(camera, zDrive, minZ, maxZ, stepsPerRound, numberOfRounds, focusMeasure=brennerFocusMeasure, rw=None): autofocuser = _Autofocuser(camera, zDrive, minZ, maxZ, stepsPerRound, numberOfRounds, rw) # greenlet.run(..) blocks until the greenlet is dead and is the correct call to use when blocking behavior is desired autofocuser.runGt.run() return autofocuser.bestZ
July 2009 August 2009 September 2009 October 2009 November 2009 December 2009 January 2010 September 2010 December 2010 January 2011 February 2011 April 2011 June 2011 August 2011 February 2012 June 2012 July 2012 August 2012 October 2012 November 2012 January 2014 April 2014 June 2014 August 2014 September 2014 October 2014 January 2015 March 2015 April 2015 June 2015 November 2015 December 2015 January 2016 June 2016 August 2016 January 2017 March 2017 April 2018 April 2019 June 2019 January 2020
Subscribe to Posts [Atom]
Post a Comment