Пример #1
0
    def _onNewImage(self, _):
        # update overview whenever the streams change, limited to a frequency of 1 Hz
        if self.curr_s and self.curr_s.image.value is not None:
            s = self.curr_s
            img = s.image.value
            logging.debug("Updating overview using image at %s", getBoundingBox(img))
            if isinstance(s, acqstream.OpticalStream):
                insert_tile_to_image(img, self.im_opt)
            elif isinstance(s, acqstream.EMStream):
                insert_tile_to_image(img, self.im_sem)
            else:
                logging.info("%s not added to overview image as it's not optical nor EM", s)

            self._update_ovv()
Пример #2
0
    def test_overview_acquisition(self):
        s = stream.SEMStream(
            "Single beam",
            self.sed,
            self.sed.data,
            self.ebeam,
            focuser=self.
            efocuser,  # Not used during acquisition, but done by the GUI
            hwemtvas={"scale", "dwellTime", "horizontalFoV"})
        # This should be used by the acquisition
        s.dwellTime.value = 1e-6  # s

        # Use random settings and check they are overridden by the overview acquisition
        s.scale.value = (2, 2)
        s.horizontalFoV.value = 20e-6  # m

        # Known position of the center scintillator
        scintillator5_area = (-0.007, -0.007, 0.007, 0.007)  # l, b, r, t
        # Small area for DEBUG (3x3)
        # scintillator5_area = (-0.002, -0.002, 0.002, 0.002)  # l, b, r, t

        est_time = fastem.estimateTiledAcquisitionTime(s, self.stage,
                                                       scintillator5_area)
        # don't use for DEBUG example
        self.assertGreater(est_time,
                           10)  # It should take more than 10s! (expect ~5 min)

        before_start_t = time.time()
        f = fastem.acquireTiledArea(s, self.stage, scintillator5_area)
        time.sleep(1)
        start_t, end_t = f.get_progress()
        self.assertGreater(start_t, before_start_t)
        # don't use for DEBUG example
        self.assertGreater(end_t,
                           time.time() +
                           10)  # Should report still more than 10s

        overview_da = f.result()
        self.assertGreater(overview_da.shape[0], 2000)
        self.assertGreater(overview_da.shape[1], 2000)

        # Check the final area fits the requested area, with possibly a little bit of margin
        bbox = img.getBoundingBox(overview_da)
        fov = bbox[2] - bbox[0], bbox[3] - bbox[1]
        logging.debug("Got image of size %s, with FoV %s = %s",
                      overview_da.shape, fov, bbox)
        self.assertLessEqual(bbox[0], scintillator5_area[0])  # Left
        self.assertLessEqual(bbox[1], scintillator5_area[1])  # Bottom
        self.assertGreaterEqual(bbox[2], scintillator5_area[2])  # Right
        self.assertGreaterEqual(bbox[3], scintillator5_area[3])  # Top
Пример #3
0
 def _getFov(self, sd):
     """
     sd (Stream or DataArray): If it's a stream, it must be a live stream,
       and the FoV will be estimated based on the settings.
     return (float, float): width, height in m
     """
     if isinstance(sd, model.DataArray):
         # The actual FoV, as the data recorded it
         im_bbox = img.getBoundingBox(sd)
         logging.debug("Bounding box of stream data: %s", im_bbox)
         return im_bbox[2] - im_bbox[0], im_bbox[3] - im_bbox[1]
     elif isinstance(sd, Stream):
         # Ask the stream, which estimates based on the emitter/detector settings
         try:
             return sd.guessFoV()
         except (NotImplementedError, AttributeError):
             raise TypeError(
                 "Unsupported Stream %s, it doesn't have a .guessFoV()" %
                 (sd, ))
     else:
         raise TypeError("Unsupported object")
Пример #4
0
    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)
Пример #5
0
    def _on_overview_acquire(self, evt):
        # Disable direct image update, as it would duplicate the overview image, but less pretty.
        self.main_data.stage.position.unsubscribe(self.on_stage_pos_change)
        self._on_current_stream([])

        try:
            das = self._acquisition_controller.open_acquisition_dialog()
        finally:
            self.main_data.stage.position.subscribe(self.on_stage_pos_change)
            self._on_current_stream(self._data_model.streams.value)

        if not das:
            return

        for da in das:
            logging.debug("Acquired overview image %s FoV: %s",
                          da.metadata.get(MD_DESCRIPTION, ""),
                          getBoundingBox(da))

        # Store the data somewhere, so that it's possible to open it full size later
        self._save_overview(das)

        # Convert each DataArray to a Stream + Projection, so that we can display it
        streams = udataio.data_to_static_streams(das)

        # Only reset the channels which have a new data
        opt = [s for s in streams if isinstance(s, stream.OpticalStream)]
        if opt:
            self._bkg_opt[:] = 0
        em = [s for s in streams if isinstance(s, stream.EMStream)]
        if em:
            self._bkg_sem[:] = 0

        # Compute the projection, this is done asynchronously (and for now,
        # all at the same time, which might be clever... or not, if the
        # data is really large and the memory is limited)
        projs = [stream.RGBSpatialProjection(s) for s in opt + em]
        logging.debug("Adding %s streams to the overview", len(projs))

        for p in projs:

            def add_bkg_ovv(im, proj=p):
                """
                Receive the projected image (RGB) and add it to the overview
                """
                # To handle cases where the projection was faster than subscribing,
                # we get called at subscription. If we receive None, we just need
                # to be a little bit more patient.
                if im is None:
                    return

                if isinstance(proj.stream, stream.OpticalStream):
                    bkg = self._bkg_opt
                else:
                    bkg = self._bkg_sem
                insert_tile_to_image(im, bkg)
                logging.debug("Added overview projection %s", proj.name.value)

                # Normally not necessary as the image will not change, and the
                # projection + stream will go out of scope, which will cause
                # the VA to be unsubscribed automatically. But it feels cleaner.
                proj.image.unsubscribe(add_bkg_ovv)
                del self._bkg_ovv_subs[proj]

                # We could only do it when _bkg_ovv_subs is empty, as a sign it's
                # the last one... but it could delay quite a bit, and could easily
                # break if for some reason projection fails.
                self._update_ovv()

            # Keep a reference
            self._bkg_ovv_subs[p] = add_bkg_ovv
            p.image.subscribe(add_bkg_ovv, init=True)
Пример #6
0
    def __init__(self, main_data, tab, overview_canvas, m_view, stream_bar):
        self.main_data = main_data
        self._tab = tab
        self._data_model = tab.tab_data_model
        self.canvas = overview_canvas
        self.m_view = m_view
        self._stream_bar = stream_bar
        self.conf = get_acqui_conf()

        self.curr_s = None

        # Timer to detect when the stage ends moving
        self._timer_pos = wx.PyTimer(self.add_pos_to_history)

        if hasattr(m_view, "merge_ratio"):
            m_view.merge_ratio.subscribe(self._on_merge_ratio_change)

        # Global overview image (Delphi)
        if main_data.overview_ccd:
            # Overview camera can be RGB => in that case len(shape) == 4
            if len(main_data.overview_ccd.shape) == 4:
                overview_stream = acqstream.RGBCameraStream(
                    "Overview",
                    main_data.overview_ccd,
                    main_data.overview_ccd.data,
                    None,
                    acq_type=MD_AT_OVV_FULL)
            else:
                overview_stream = acqstream.BrightfieldStream(
                    "Overview",
                    main_data.overview_ccd,
                    main_data.overview_ccd.data,
                    None,
                    acq_type=MD_AT_OVV_FULL)
            self.m_view.addStream(overview_stream)
            # TODO: add it to self.tab_data_model.streams?
        else:
            # black image to display history overlay separately from built-up ovv image
            # controlled by merge slider
            da, _ = self._initialize_ovv_im(OVV_SHAPE)
            history_stream = acqstream.RGBUpdatableStream(
                "History Stream", da, acq_type=MD_AT_HISTORY)
            self.m_view.addStream(history_stream)

        # Built-up overview image
        self.ovv_im, self.m_view.mpp.value = self._initialize_ovv_im(OVV_SHAPE)
        logging.debug("Overview image FoV: %s", getBoundingBox(self.ovv_im))

        # Initialize individual ovv images for optical and sem stream
        self.im_opt = copy.deepcopy(self.ovv_im)
        self.im_sem = copy.deepcopy(self.ovv_im)
        # Extra images to be used for complete overviews, shown behind the build-up images
        self._bkg_opt = copy.deepcopy(self.ovv_im)
        self._bkg_sem = copy.deepcopy(self.ovv_im)

        # Add stream to view
        self.upd_stream = acqstream.RGBUpdatableStream(
            "Overview Stream", self.ovv_im, acq_type=MD_AT_OVV_TILES)
        self.m_view.addStream(self.upd_stream)

        self._data_model.focussedView.subscribe(self._on_focused_view)

        if main_data.stage:
            # Update the image when the stage move
            main_data.stage.position.subscribe(self.on_stage_pos_change,
                                               init=True)
            main_data.chamberState.subscribe(self._on_chamber_state)
            self._data_model.streams.subscribe(self._on_current_stream)

            # Add a "acquire overview" button.
            self._stream_bar.btn_add_overview.Bind(wx.EVT_BUTTON,
                                                   self._on_overview_acquire)
            self._acquisition_controller = acqcont.OverviewStreamAcquiController(
                self._data_model, tab)
            self._bkg_ovv_subs = {
            }  # Just used temporarily when background overview is projected