def resetCams(self, curTime, cameras, table): resetEndTime = curTime for camera in cameras: exposureStart = max(curTime, self.getTimeWhenCameraCanExpose(table, camera)) # Cameras that have a pre-set exposure time can only use that # exposure time for clearing the sensor, hence why we take the # maximum of the min exposure time and the current exposure time. # \todo Is it possible for getExposureTime() to be less than # getMinExposureTime()? That would be a bug, right? minExposureTime = max(decimal.Decimal('.1'), camera.getMinExposureTime(isExact=True), camera.getExposureTime(isExact=True)) exposureMode = camera.getExposureMode() if exposureMode == cockpit.handlers.camera.TRIGGER_AFTER: table.addToggle(exposureStart + minExposureTime, camera) elif exposureMode == cockpit.handlers.camera.TRIGGER_DURATION: table.addAction(exposureStart, camera, True) table.addAction(exposureStart + minExposureTime, camera, False) else: # TRIGGER_BEFORE case table.addToggle(exposureStart, camera) resetEndTime = max(resetEndTime, exposureStart + minExposureTime) self.cameraToImageCount[camera] += 1 self.cameraToIgnoredImageIndices[camera].add( self.cameraToImageCount[camera]) self.cameraToIsReady[camera] = True return resetEndTime + decimal.Decimal('1e-6')
def run(self): # For debugging purposes experiment.lastExperiment = self self.sanityCheckEnvironment() self.prepareHandlers() self.cameraToReadoutTime = dict([(c, c.getTimeBetweenExposures(isExact = True)) for c in self.cameras]) for camera, readTime in self.cameraToReadoutTime.items(): if type(readTime) is not decimal.Decimal: raise RuntimeError("Camera %s did not provide an exact (decimal.Decimal) readout time" % camera.name) for camera, func in self.camToFunc.items(): events.subscribe(events.NEW_IMAGE % camera.name, func) for exposureTime in self.exposureTimes: if self.shouldAbort: break self.camToImages = {} self.camToNumImagesReceived = {} self.camToLock = {} for camera in self.cameras: # Prepare a memory buffer to store images in. width, height = camera.getImageSize() self.camToImages[camera] = numpy.zeros((self.numExposures, height, width)) self.camToNumImagesReceived[camera] = 0 self.camToLock[camera] = threading.Lock() # Indicate any frame transfer cameras for reset at start of # table. if camera.getExposureMode() == cockpit.handlers.camera.TRIGGER_AFTER: self.cameraToIsReady[camera] = False self.table = self.generateActions(exposureTime) self.table.sort() self.examineActions() self.table.sort() self.table.enforcePositiveTimepoints() self.lastMinuteActions() self.doneReceivingThread = threading.Thread(target = self.waiter) self.doneReceivingThread.start() self.execute() if self.shouldAbort: break # Wait until it's been a short time after the last received image. self.doneReceivingThread.join() progress = cockpit.gui.progressDialog.ProgressDialog("Processing images", "Processing images for exposure time %.4f" % exposureTime, parent = None) self.processImages(exposureTime) progress.Destroy() for camera, func in self.camToFunc.items(): events.unsubscribe(events.NEW_IMAGE % camera.name, func) self.save() self.showResults() self.cleanup()
def getTimeWhenCameraCanExpose(self, table, camera): lastUseTime, action = table.getLastActionFor(camera) if lastUseTime is None: # No actions yet; assume camera is ready at the start of the # experiment. return 0 nextUseTime = lastUseTime if camera.getExposureMode() == cockpit.handlers.camera.TRIGGER_BEFORE: # The camera actually finished exposing (and started reading # out) some time after lastUseTime, depending on its declared # exposure time. nextUseTime += camera.getExposureTime(isExact=True) nextUseTime += self.cameraToReadoutTime[camera] + decimal.Decimal(0.1) return nextUseTime
def run(self): # For debugging purposes experiment.lastExperiment = self self.sanityCheckEnvironment() self.prepareHandlers() self.cameraToReadoutTime = dict([ (c, c.getTimeBetweenExposures(isExact=True)) for c in self.cameras ]) for camera, readTime in self.cameraToReadoutTime.items(): if type(readTime) is not decimal.Decimal: raise RuntimeError( "Camera %s did not provide an exact (decimal.Decimal) readout time" % camera.name) # Start out with no-exposure-time images to get a measured offset. multiplier = 0 for camera, func in self.camToFunc.items(): events.subscribe(events.NEW_IMAGE % camera.name, func) activeCameras = set(self.cameras) for i in range(self.numCollections): if not activeCameras or self.shouldAbort: break print("Running with cams", activeCameras) self.camToImages = {} self.camToNumImagesReceived = {} self.camToLock = {} for camera in activeCameras: # Prepare a memory buffer to store images in. width, height = camera.getImageSize() self.camToImages[camera] = numpy.zeros( (self.numExposures, height, width)) self.camToNumImagesReceived[camera] = 0 self.camToLock[camera] = threading.Lock() # Indicate any frame transfer cameras for reset at start of # table. if camera.getExposureMode( ) == cockpit.handlers.camera.TRIGGER_AFTER: self.cameraToIsReady[camera] = False self.table = self.generateActions(multiplier, activeCameras) self.table.sort() self.examineActions() self.table.sort() self.table.enforcePositiveTimepoints() self.lastMinuteActions() self.doneReceivingThread = threading.Thread(target=self.waiter) self.doneReceivingThread.start() self.execute() # Wait until it's been a short time after the last received image. self.doneReceivingThread.join() if multiplier == 0: multiplier = decimal.Decimal(1) else: multiplier *= self.exposureMultiplier activeCameras = self.processImages(multiplier) print("Came out with active cams", activeCameras) for camera, func in self.camToFunc.items(): events.unsubscribe(events.NEW_IMAGE % camera.name, func) if self.shouldAbort: # Don't bother processing images. self.cleanup() return results = [] for camera in self.cameras: results.append(self.makeFit(self.camToAverages[camera])) results = numpy.array(results, dtype=numpy.float32) results.shape = len( self.cameras), 1, 2, results.shape[-2], results.shape[-1] # Construct a header for the image data. pixel_size = wx.GetApp().Objectives.GetPixelSize() wavelengths = [c.wavelength for c in self.cameras] header = cockpit.util.datadoc.makeHeaderFor(results, XYSize=pixel_size, ZSize=0, wavelengths=wavelengths) filehandle = open(self.savePath, 'wb') cockpit.util.datadoc.writeMrcHeader(header, filehandle) filehandle.write(results) filehandle.close() self.cleanup()
def expose(self, curTime, cameras, lightTimePairs, table, pseudoGlobalExposure=False, previousMovementTime=0): # First, determine which cameras are not ready to be exposed, because # they may have seen light they weren't supposed to see (due to # bleedthrough from other cameras' exposures). These need # to be triggered (and we need to record that we want to throw away # those images) before we can proceed with the real exposure. camsToReset = set() for camera in cameras: if not self.cameraToIsReady[camera]: camsToReset.add(camera) if camsToReset: curTime = self.resetCams(curTime, camsToReset, table) # Figure out when we can start the exposure, based on the cameras # involved: their exposure modes, readout times, and last trigger # times determine how soon we can next trigger them (see # getTimeWhenCameraCanExpose() for more information). exposureStartTime = curTime # Adjust the exposure start based on when the cameras are ready. for camera in cameras: camExposureReadyTime = self.getTimeWhenCameraCanExpose( table, camera) # we add the readout time to get when the light should be trigger to # obtain pseudo global exposure camPseudoGlobalReadyTime = (camExposureReadyTime + self.cameraToReadoutTime[camera]) exposureStartTime = max(exposureStartTime, camExposureReadyTime) # Determine the maximum exposure time, which depends on our light # sources as well as how long we have to wait for the cameras to be # ready to be triggered. maxExposureTime = 0 if lightTimePairs: maxExposureTime = max(lightTimePairs, key=lambda a: a[1])[1] # Check cameras to see if they have minimum exposure times; take them # into account for when the exposure can end. Additionally, if they # are frame-transfer cameras, then we need to adjust maxExposureTime # to ensure that our triggering of the camera does not come too soon # (while it is still reading out the previous frame). for camera in cameras: maxExposureTime = max(maxExposureTime, camera.getMinExposureTime(isExact=True)) if camera.getExposureMode( ) == cockpit.handlers.camera.TRIGGER_AFTER: nextReadyTime = self.getTimeWhenCameraCanExpose(table, camera) # Ensure camera is exposing for long enough to finish reading # out the last frame. maxExposureTime = max(maxExposureTime, nextReadyTime - exposureStartTime) # Open the shutters for the specified exposure times, centered within # the max exposure time. # Note that a None value here means the user wanted to expose the # cameras without any special light. exposureEndTime = exposureStartTime + maxExposureTime for light, exposureTime, in lightTimePairs: if light is not None and light.name is not 'ambient': # i.e. not ambient light # Center the light exposure. timeSlop = maxExposureTime - exposureTime offset = timeSlop / 2 table.addAction(exposureEndTime - exposureTime - offset, light, True) table.addAction(exposureEndTime - offset, light, False) # Record this exposure time. if exposureTime not in self.lightToExposureTime[light]: self.lightToExposureTime[light].add(exposureTime) # Trigger the cameras. Keep track of which cameras we *aren't* using # here; if they are continuous-exposure cameras, then they may have # seen light that they shouldn't have, and need to be invalidated. usedCams = set() for camera in cameras: usedCams.add(camera) mode = camera.getExposureMode() if mode == cockpit.handlers.camera.TRIGGER_AFTER: table.addToggle(exposureEndTime, camera) elif mode == cockpit.handlers.camera.TRIGGER_DURATION: table.addAction(exposureStartTime, camera, True) table.addAction(exposureEndTime, camera, False) elif mode == cockpit.handlers.camera.TRIGGER_DURATION_PSEUDOGLOBAL: # We added some security time to the readout time that # we have to remove now cameraExposureStartTime = (exposureStartTime - self.cameraToReadoutTime[camera] - decimal.Decimal(0.005)) table.addAction(cameraExposureStartTime, camera, True) table.addAction(exposureEndTime, camera, False) elif mode == cockpit.handlers.camera.TRIGGER_BEFORE: table.addToggle(exposureStartTime, camera) elif mode == cockpit.handlers.camera.TRIGGER_SOFT: table.addAction(exposureStartTime, camera, True) else: raise Exception('%s has no trigger mode set.' % camera) self.cameraToImageCount[camera] += 1 for camera in self.cameras: if (camera not in usedCams and camera.getExposureMode() == cockpit.handlers.camera.TRIGGER_AFTER): # Camera is a continuous-exposure/frame-transfer camera # and therefore saw light it shouldn't have; invalidate it. self.cameraToIsReady[camera] = False return exposureEndTime
def run(self): # Returns True to close config dialog box, False or None otherwise. # Check if the user is set to save to an already-existing file. if self.savePath and os.path.exists(self.savePath): if not guiUtils.getUserPermission( ("The file:\n%s\nalready exists. " % self.savePath) + "Are you sure you want to overwrite it?"): return False global lastExperiment lastExperiment = self self.sanityCheckEnvironment() self.prepareHandlers() self.cameraToReadoutTime = { c: c.getTimeBetweenExposures(isExact=True) for c in self.cameras } for camera, readTime in self.cameraToReadoutTime.items(): if type(readTime) is not decimal.Decimal: raise RuntimeError( "Camera %s did not provide an exact (decimal.Decimal) readout time" % camera.name) # Indicate any frame transfer cameras for reset at start of table. for camera in self.cameras: if camera.getExposureMode( ) == cockpit.handlers.camera.TRIGGER_AFTER: self.cameraToIsReady[camera] = False self.createValidActionTable() if self.numReps > 1 and self.repDuration < self.table.lastActionTime / 1000: warning = "Repeat duration is less than the time required to run " \ "one repeat. Choose:" \ "\n 'OK' to run repeats as fast as possible;" \ "\n 'Cancel' to go back and change parameters." if not guiUtils.getUserPermission(warning): return False # ToDo: check duration of action table against timelapse settings # display appropriate warnings. self.lastMinuteActions() self._run_thread = threading.Thread(target=self.execute, name="Experiment-execute") self._run_thread.start() saveThread = None if self.savePath and max(self.cameraToImageCount.values()): cameraToExcitation = {c: 0.0 for c in self.cameras} for cameras, lightTimePairs in self.exposureSettings: ## If there's multiple light sources for the same ## camera pick the one with highest wavelength. Main ## case of multiple light sources is single molecule ## localisation where a lower wavelength is doing the ## pumping while but excitation for fluorescence is ## with the higher wavelength. if lightTimePairs: max_wavelength = max( [l.wavelength for l, t in lightTimePairs]) else: max_wavelength = 0.0 for camera in cameras: if camera not in self.cameras: continue cameraToExcitation[camera] = max( cameraToExcitation[camera], max_wavelength) saver = dataSaver.DataSaver(self.cameras, self.numReps, self.cameraToImageCount, self.cameraToIgnoredImageIndices, self._run_thread, self.savePath, self.sliceHeight, self.generateTitles(), cameraToExcitation) saver.startCollecting() saveThread = threading.Thread(target=saver.executeAndSave, name="Experiment-execute-save") saveThread.start() generatedFilenames.append(saver.getFilenames()) cleanup_thread = threading.Thread(target=self.cleanup, args=[self._run_thread, saveThread], name="Experiment-cleanup") cleanup_thread.start() return True