def test_compressed_stack(self): """ Test the whole procedure (acquire compressed zstack + stitch) of acquireTiledArea function """ # With fm streams settings_obs = SettingsObserver([self.stage]) fm_fov = compute_camera_fov(self.ccd) # Using "songbird-sim-ccd.h5" in simcam with tile max_res: (260, 348) area = (0, 0, fm_fov[0] * 2, fm_fov[1] * 2) # left, top, right, bottom overlap = 0.2 focus_value = self.focus.position.value['z'] zsteps = 3 # Create focus zlevels from the given zsteps number zlevels = numpy.linspace(focus_value - (zsteps / 2 * 1e-6), focus_value + (zsteps / 2 * 1e-6), zsteps).tolist() future = acquireTiledArea(self.fm_streams, self.stage, area=area, overlap=overlap, settings_obs=settings_obs, zlevels=zlevels) data = future.result() self.assertTrue(future.done()) self.assertEqual(len(data), 2) self.assertIsInstance(data[0], model.DataArray) self.assertEqual(len(data[0].shape), 2)
def _run_overview_acquisition(f, stream, stage, area, live_stream): """ :returns: (DataArray) """ _configure_overview_hw(stream.emitter) # The stage movement precision is quite good (just a few pixels). The stage's # position reading is much better, and we can assume it's below a pixel. # So as long as we are sure there is some overlap, the tiles will be positioned # correctly and without gap. overlap = STAGE_PRECISION / stream.emitter.horizontalFoV.value logging.debug("Overlap is %s%%", overlap * 100) # normally < 1% def _pass_future_progress(sub_f, start, end): f.set_progress(start, end) # Note, for debugging, it's possible to keep the intermediary tiles with log_path="./tile.ome.tiff" sf = stitching.acquireTiledArea([stream], stage, area, overlap, registrar=REGISTER_IDENTITY) # Connect the progress of the underlying future to the main future sf.add_update_callback(_pass_future_progress) das = sf.result() if len(das) != 1: logging.warning("Expected 1 DataArray, but got %d: %r", len(das), das) return das[0]
def test_cancel(self): """ Test cancelling of acquireTiledArea function """ self.start = None self.end = None self.updates = 0 self.done = False # Get area from stage metadata area = (-0.0001, -0.0001, 0.0001, 0.0001) overlap = 0.2 f = acquireTiledArea(self.fm_streams, self.stage, area=area, overlap=overlap) f.add_update_callback(self.on_progress_update) f.add_done_callback(self.on_done) time.sleep(1) # make sure it's started self.assertTrue(f.running()) f.cancel() self.assertRaises(CancelledError, f.result, 1) self.assertGreaterEqual(self.updates, 1) # at least one update at cancellation self.assertLessEqual(self.end, time.time()) self.assertTrue(self.done) self.assertTrue(f.cancelled())
def on_acquire(self, evt): """ Start the actual acquisition """ logging.info("Acquire button clicked, starting acquisition") acq_streams = self.get_acq_streams() if not acq_streams: logging.info("No stream to acquire, ending immediately") self.on_close( evt) # Nothing to do, so it's the same as closing the window self.acquiring = True # Adjust view FoV to the whole area, so that it's possible to follow the acquisition self._fit_view_to_area() self.btn_secom_acquire.Disable() # Freeze all the settings so that it's not possible to change anything self._pause_settings() self.gauge_acq.Show() self.Layout() # to put the gauge at the right place # For now, always indicate the best quality if self._main_data_model.opm: self._main_data_model.opm.setAcqQuality(path.ACQ_QUALITY_BEST) zlevels = self._get_zstack_levels() focus_mtd = FocusingMethod.MAX_INTENSITY_PROJECTION if zlevels else FocusingMethod.NONE if self.filename_tiles: logging.info("Acquisition tiles logged at %s", self.filename_tiles) os.makedirs(os.path.dirname(self.filename_tiles)) self.acq_future = stitching.acquireTiledArea( acq_streams, self._main_data_model.stage, area=self.area, overlap=self.overlap, settings_obs=self._main_data_model.settings_obs, log_path=self.filename_tiles, weaver=WEAVER_COLLAGE_REVERSE, zlevels=zlevels, focusing_method=focus_mtd) self._acq_future_connector = ProgressiveFutureConnector( self.acq_future, self.gauge_acq, self.lbl_acqestimate) # TODO: Build-up the complete image during the acquisition, so that the # progress can be followed live. self.acq_future.add_done_callback(self.on_acquisition_done) self.btn_cancel.SetLabel("Cancel") self.btn_cancel.Bind(wx.EVT_BUTTON, self.on_cancel)
def _run_overview_acquisition(f, stream, stage, area, live_stream): """ Runs the acquisition of one overview image (typically one scintillator). :param f: (ProgressiveFuture) Acquisition future object. :param stream: (SEMstream) The stream used for the acquisition. :param stage: (actuator.MultiplexActuator) The stage in the sample carrier coordinate system. The x and y axes are aligned with the x and y axes of the ebeam scanner. :param area: (float, float, float, float) xmin, ymin, xmax, ymax coordinates of the overview region. :param live_stream: (StaticStream or None): StaticStream to be updated with each tile acquired, to build up live the whole acquisition. NOT SUPPORTED YET. :returns: (DataArray) The complete overview image. """ fastem_conf.configure_scanner(stream.emitter, fastem_conf.OVERVIEW_MODE) # The stage movement precision is quite good (just a few pixels). The stage's # position reading is much better, and we can assume it's below a pixel. # So as long as we are sure there is some overlap, the tiles will be positioned # correctly and without gap. overlap = STAGE_PRECISION / stream.emitter.horizontalFoV.value logging.debug("Overlap is %s%%", overlap * 100) # normally < 1% def _pass_future_progress(sub_f, start, end): f.set_progress(start, end) # Note, for debugging, it's possible to keep the intermediary tiles with log_path="./tile.ome.tiff" sf = stitching.acquireTiledArea([stream], stage, area, overlap, registrar=REGISTER_IDENTITY, focusing_method=FocusingMethod.NONE) # Connect the progress of the underlying future to the main future # FIXME removed to provide better progress update in GUI # When _tiledacq.py has proper time update implemented, add this line here again. # sf.add_update_callback(_pass_future_progress) das = sf.result() if len(das) != 1: logging.warning("Expected 1 DataArray, but got %d: %r", len(das), das) # Switch immersion mode back on, so we can focus the SEM from the TFS GUI. stream.emitter.immersion.value = True # FIXME auto blanking not working properly, so force beam blanking after image acquisition for now. stream.emitter.blanker.value = True return das[0]
def test_whole_procedure(self): """ Test the whole procedure (acquire + stitch) of acquireTiledArea function """ # With fm streams settings_obs = SettingsObserver([self.stage]) fm_fov = compute_camera_fov(self.ccd) # Using "songbird-sim-ccd.h5" in simcam with tile max_res: (260, 348) area = (0, 0, fm_fov[0] * 4, fm_fov[1] * 4) # left, bottom, right, top overlap = 0.2 self.stage.moveAbs({'x': 0, 'y': 0}).result() future = acquireTiledArea(self.fm_streams, self.stage, area=area, overlap=overlap, settings_obs=settings_obs, weaver=WEAVER_COLLAGE_REVERSE) data = future.result() self.assertEqual(future._state, FINISHED) self.assertEqual(len(data), 2) self.assertIsInstance(data[0], model.DataArray) self.assertEqual(len(data[0].shape), 2) # With sem stream area = (0, 0, 0.00001, 0.00001) self.stage.moveAbs({'x': 0, 'y': 0}).result() future = acquireTiledArea(self.sem_streams, self.stage, area=area, overlap=overlap) data = future.result() self.assertEqual(future._state, FINISHED) self.assertEqual(len(data), 1) self.assertIsInstance(data[0], model.DataArray) self.assertEqual(len(data[0].shape), 2)
def test_registrar_weaver(self): overlap = 0.05 # Little overlap, no registration sem_fov = compute_scanner_fov(self.ebeam) area = (0, 0, sem_fov[0], sem_fov[1]) self.stage.moveAbs({'x': 0, 'y': 0}).result() future = acquireTiledArea(self.sem_streams, self.stage, area=area, overlap=overlap, registrar=REGISTER_IDENTITY, weaver=WEAVER_MEAN) data = future.result() self.assertEqual(future._state, FINISHED) self.assertEqual(len(data), 1) self.assertIsInstance(data[0], model.DataArray) self.assertEqual(len(data[0].shape), 2)
def test_progress(self): """ Test progress update of acquireTiledArea function """ self.start = None self.end = None self.updates = 0 area = (0, 0, 0.00001, 0.00001) # left, top, right, bottom overlap = 0.2 self.stage.moveAbs({'x': 0, 'y': 0}).result() f = acquireTiledArea(self.sem_streams, self.stage, area=area, overlap=overlap) f.add_update_callback(self.on_progress_update) data = f.result() self.assertIsInstance(data[0], model.DataArray) self.assertGreaterEqual(self.updates, 2) # at least one update per stream
def test_area(self): """ Test the acquired area matches the requested area """ fm_fov = compute_camera_fov(self.ccd) # Using "songbird-sim-ccd.h5" in simcam with tile max_res: (260, 348) area = (0, 0, fm_fov[0] * 2, fm_fov[1] * 3) # left, bottom, right, top overlap = 0.2 # No focuser, to make it faster, and it doesn't affect the FoV fs = stream.FluoStream("fluo1", self.ccd, self.ccd.data, self.light, self.light_filter) future = acquireTiledArea([fs], self.stage, area=area, overlap=overlap, registrar=REGISTER_IDENTITY, weaver=WEAVER_MEAN) data = future.result() self.assertIsInstance(data[0], model.DataArray) self.assertEqual(len(data[0].shape), 2) # The center should be almost precisely at the center of the request RoA, # modulo the stage precision (which is very good on the simulator). # The size can be a little bit bigger (but never smaller), as it's # rounded up to a tile. bbox = img.getBoundingBox(data[0]) data_center = data[0].metadata[model.MD_POS] area_center = (area[0] + area[2]) / 2, (area[1] + area[3]) / 2 logging.debug("Expected area: %s %s, actual area: %s %s", area, area_center, bbox, data_center) self.assertAlmostEqual(data_center[0], area_center[0], delta=1e-6) # +- 1µm self.assertAlmostEqual(data_center[1], area_center[1], delta=1e-6) # +- 1µm self.assertTrue(area[0] - fm_fov[0] / 2 <= bbox[0] <= area[0]) self.assertTrue(area[1] - fm_fov[1] / 2 <= bbox[1] <= area[1]) self.assertTrue(area[2] <= bbox[2] <= area[2] + fm_fov[0] / 2) self.assertTrue(area[3] <= bbox[3] <= area[3] + fm_fov[1] / 2)
def _run_overview_acquisition(f, stream, stage, area, live_stream): """ :returns: (DataArray) """ fastem_conf.configure_scanner(stream.emitter, fastem_conf.OVERVIEW_MODE) # The stage movement precision is quite good (just a few pixels). The stage's # position reading is much better, and we can assume it's below a pixel. # So as long as we are sure there is some overlap, the tiles will be positioned # correctly and without gap. overlap = STAGE_PRECISION / stream.emitter.horizontalFoV.value logging.debug("Overlap is %s%%", overlap * 100) # normally < 1% def _pass_future_progress(sub_f, start, end): f.set_progress(start, end) # Note, for debugging, it's possible to keep the intermediary tiles with log_path="./tile.ome.tiff" sf = stitching.acquireTiledArea([stream], stage, area, overlap, registrar=REGISTER_IDENTITY, focusing_method=FocusingMethod.NONE) # Connect the progress of the underlying future to the main future sf.add_update_callback(_pass_future_progress) das = sf.result() if len(das) != 1: logging.warning("Expected 1 DataArray, but got %d: %r", len(das), das) # Switch immersion mode back on, so we can focus the SEM from the TFS GUI. stream.emitter.immersion.value = True # FIXME auto blanking not working properly, so force beam blanking after image acquisition for now. stream.emitter.blanker.value = True return das[0]