コード例 #1
0
    def __init__(self, name, role, parent, hfw_nomag, **kwargs):
        model.Emitter.__init__(self, name, role, parent=parent, **kwargs)
        self.parent = parent

        # Distance between borders if magnification = 1. It should be found out
        # via calibration.
        self._hfw_nomag = hfw_nomag  # m

        self.magnification = model.FloatContinuous(
            self.parent.GetMagnification(),
            unit="",
            readonly=True,
            range=MAGNIFICATION_RANGE)
        fov_range = (self._hfw_nomag / MAGNIFICATION_RANGE[1],
                     self._hfw_nomag / MAGNIFICATION_RANGE[0])
        self.horizontalFoV = model.FloatContinuous(
            self._hfw_nomag / self.magnification.value,
            range=fov_range,
            unit="m",
            setter=self._setHorizontalFoV)
        self.horizontalFoV.subscribe(self._onHorizontalFoV)

        self.blanker = model.VAEnumerated(self.parent.GetBlankBeam(),
                                          choices={True, False},
                                          setter=self._setBlanker)

        self.external = model.VAEnumerated(self.parent.GetExternal(),
                                           choices={True, False},
                                           setter=self._setExternal)

        #self.probeCurrent = model.FloatContinuous(1e-6, range=PC_RANGE, unit="A",
        #                                          setter=self._setProbeCurrent)

        self.accelVoltage = model.FloatContinuous(
            0,
            range=(VOLTAGE_RANGE[0] * 1e3, VOLTAGE_RANGE[1] * 1e3),
            unit="V",
            setter=self._setVoltage)

        # No pixelSize as there is no shape (not a full scanner)

        # To provide some rough idea of the step size when changing focus
        # Depends on the pixelSize, so will be updated whenever the HFW changes
        self.depthOfField = model.FloatContinuous(1e-6,
                                                  range=(0, 1e3),
                                                  unit="m",
                                                  readonly=True)
        self._updateDepthOfField()

        # Refresh regularly the values, from the hardware, starting from now
        self._updateSettings()
        self._va_poll = util.RepeatingTimer(5, self._updateSettings,
                                            "Settings polling")
        self._va_poll.start()
コード例 #2
0
    def __init__(self, name, data):
        """
        name (string)
        data (model.DataArray(Shadow) of shape (YX) or list of such DataArray(Shadow)).
         The metadata MD_POS and MD_AR_POLE should be provided
        """
        if not isinstance(data, collections.Iterable):
            data = [data] # from now it's just a list of DataArray

        # TODO: support DAS, as a "delayed loading" by only calling .getData()
        # when the projection for the particular data needs to be computed (or
        # .raw needs to be accessed?)
        # Ensure all the data is a DataArray, as we don't handle (yet) DAS
        data = [d.getData() if isinstance(d, model.DataArrayShadow) else d for d in data]

        # find positions of each acquisition
        # tuple of 2 floats -> DataArray: position on SEM -> data
        self._sempos = {}
        for d in data:
            try:
                self._sempos[d.metadata[MD_POS]] = img.ensure2DImage(d)
            except KeyError:
                logging.info("Skipping DataArray without known position")

        # Cached conversion of the CCD image to polar representation
        # TODO: automatically fill it in a background thread
        self._polar = {} # dict tuple 2 floats -> DataArray

        # SEM position displayed, (None, None) == no point selected
        self.point = model.VAEnumerated((None, None),
                     choices=frozenset([(None, None)] + list(self._sempos.keys())))

        # The background data (typically, an acquisition without ebeam).
        # It is subtracted from the acquisition data.
        # If set to None, a simple baseline background value is subtracted.
        self.background = model.VigilantAttribute(None,
                                                  setter=self._setBackground)
        self.background.subscribe(self._onBackground)

        if self._sempos:
            # Pick one point, e.g., top-left
            bbtl = (min(x for x, y in self._sempos.keys() if x is not None),
                    min(y for x, y in self._sempos.keys() if y is not None))
            # top-left point is the closest from the bounding-box top-left
            def dis_bbtl(v):
                try:
                    return math.hypot(bbtl[0] - v[0], bbtl[1] - v[1])
                except TypeError:
                    return float("inf") # for None, None
            self.point.value = min(self._sempos.keys(), key=dis_bbtl)

        # no need for init=True, as Stream.__init__ will update the image
        self.point.subscribe(self._onPoint)

        super(StaticARStream, self).__init__(name, list(self._sempos.values()))
コード例 #3
0
    def _createAutoExternal(self, original_external_va):
        """

        """
        self._external = original_external_va
        self.external = model.VAEnumerated(None,
                                           setter=self._setExternal,
                                           choices={
                                               True: 'external',
                                               False: 'acquisition',
                                               None: 'auto'
                                           })
コード例 #4
0
    def _createAutoBlanker(self, original_blanker):
        """

        """
        self._blanker = original_blanker
        self.blanker = model.VAEnumerated(None,
                                          setter=self._setBlanker,
                                          choices={
                                              True: 'blanked',
                                              False: 'unblanked',
                                              None: 'auto'
                                          })
コード例 #5
0
ファイル: comp_stream_test.py プロジェクト: ihebdelmic/odemis
    def __init__(self, name):
        Stream.__init__(self, name, None, None, None)

        # For imitating a FluoStream
        self.excitation = model.VAEnumerated(
            (4.2e-07, 4.3e-07, 4.38e-07, 4.45e-07, 4.55e-07),
            # multiple spectra
            choices={(4.2e-07, 4.3e-07, 4.38e-07, 4.45e-07, 4.55e-07),
                     (3.75e-07, 3.9e-07, 4e-07, 4.02e-07, 4.05e-07),
                     (5.65e-07, 5.7e-07, 5.75e-07, 5.8e-07, 5.95e-07),
                     (5.25e-07, 5.4e-07, 5.5e-07, 5.55e-07, 5.6e-07),
                     (4.95e-07, 5.05e-07, 5.13e-07, 5.2e-07, 5.3e-07)},
            unit="m")
        self.emission = model.VAEnumerated(
            (500e-9, 520e-9),
            # one (fixed) multi-band
            choices={(100e-9, 150e-9), (500e-9, 520e-9), (600e-9, 650e-9)},
            unit="m")
        default_tint = conversion.wave2rgb(488e-9)
        self.tint = model.VigilantAttribute(default_tint, unit="RGB")

        self.histogram._edges = (0, 0)
コード例 #6
0
ファイル: auto_align.py プロジェクト: lazem/odemis
    def __init__(self, microscope, main_app):
        super(AutomaticOverlayPlugin, self).__init__(microscope, main_app)
        self.addMenu("Data correction/Add && Align EM...", self.start)

        self._dlg = None

        # Projections of the reference and new data
        self._rem_proj = None
        self._nem_proj = None

        # On-the-fly keypoints and matching keypoints computed
        self._nem_kp = None
        self._nem_mkp = None
        self._rem_kp = None
        self._rem_mkp = None

        # im_ref.choices contains the streams and their name
        self.im_ref = model.VAEnumerated(None, choices={None: ""})
        self.blur_ref = model.IntContinuous(2, range=(0, 20), unit="px")
        self.blur = model.IntContinuous(5, range=(0, 20), unit="px")
        self.crop_top = model.IntContinuous(0, range=(0, 200), unit="px")
        self.crop_top.clip_on_range = True
        self.crop_bottom = model.IntContinuous(0, range=(0, 200), unit="px")
        self.crop_bottom.clip_on_range = True
        self.crop_left = model.IntContinuous(0, range=(0, 200), unit="px")
        self.crop_left.clip_on_range = True
        self.crop_right = model.IntContinuous(0, range=(0, 200), unit="px")
        self.crop_right.clip_on_range = True
        # TODO: inverting the values doesn't seem to really affect the keypoints
        self.invert = model.BooleanVA(False)
        # TODO: ideally, the flip shouldn't be needed, but it seems the matchers
        # in OpenCV are not able to handle "negative" scale
        self.flip_x = model.BooleanVA(False)
        self.flip_y = model.BooleanVA(False)
        self.draw_kp = model.BooleanVA(True)
        #         self.wta = model.IntContinuous(2, range=(2, 4))
        #         self.scaleFactor = model.FloatContinuous(1.2, range=(1.01, 2))
        #         self.nlevels = model.IntContinuous(8, range=(4, 48))
        #         self.patchSize = model.IntContinuous(31, range=(4, 256))

        # Any change on the VAs should update the stream
        self.blur_ref.subscribe(self._on_ref_stream)
        self.blur.subscribe(self._on_new_stream)
        self.crop_top.subscribe(self._on_new_stream)
        self.crop_bottom.subscribe(self._on_new_stream)
        self.crop_left.subscribe(self._on_new_stream)
        self.crop_right.subscribe(self._on_new_stream)
        self.invert.subscribe(self._on_new_stream)
        self.flip_x.subscribe(self._on_new_stream)
        self.flip_y.subscribe(self._on_new_stream)
        self.draw_kp.subscribe(self._on_draw_kp)
コード例 #7
0
    def __init__(self,
                 name,
                 detector,
                 dataflow,
                 emitter,
                 l2=None,
                 analyzer=None,
                 **kwargs):
        """
        See SpectrumSettingsStream for the standard options
        l2 (None or Actuator with "x" axis): to move the lens 2 (aka "lens-switch")
        analyzer (None or Actuator with "pol" axis): the polarization analyzer.
          It should have at least the 7 "standard" positions
        """
        super(LASpectrumSettingsStream,
              self).__init__(name, detector, dataflow, emitter, **kwargs)

        self.l2 = l2
        if l2:
            # Convert the boolean to the actual position.
            # The actuator is expected to have two positions, named "on" and "off"
            self._toLens2Pos = None
            for pos, pos_name in l2.axes["x"].choices.items():
                if pos_name == "on":
                    self._toLens2Pos = pos
            if self._toLens2Pos is None:
                raise ValueError(
                    "Lens 2 actuator should have an 'on' position, but only %s"
                    % (list(l2.axes["x"].choices.values()), ))

        # Polarization stored on the stream.
        # We don't use the standard "global" axes trick, so that it's possible
        # to have multiple streams, each with a different polarization.
        self.analyzer = analyzer
        if analyzer:
            # Hardcode the 6 pol pos + pass-through
            positions = set(POL_POSITIONS) | {MD_POL_NONE}
            # check positions specified in the microscope file are correct
            for pos in positions:
                if pos not in analyzer.axes["pol"].choices:
                    raise ValueError(
                        "Polarization analyzer %s misses position '%s'" %
                        (analyzer, pos))
            self.polarization = model.VAEnumerated(MD_POL_NONE,
                                                   choices=positions)

            # Not used, but the MDStream expects it as well.
            self.acquireAllPol = model.BooleanVA(False)
コード例 #8
0
    def test_enumerated(self):
        prop = model.StringEnumerated("a", set(["a", "c", "bfds"]))
        self.assertEqual(prop.value, "a")
        self.assertEqual(prop.choices, set(["a", "c", "bfds"]))

        self.called = 0
        prop.subscribe(self.callback_test_notify)
        # now count
        prop.value = "c"  # +1
        assert (prop.value == "c")

        try:
            prop.value = "wfds"
            self.fail("Assigning out of bound should not be allowed.")
        except IndexError:
            pass  # as it should be

        prop.choices = set(["a", "c", "b", 5])
        assert (prop.value == "c")
        try:
            prop.choices = set(["a", "b"])
            self.fail(
                "Assigning choices not containing current value should not be allowed."
            )
        except IndexError:
            pass  # as it should be

        try:
            prop.value = 5
            self.fail("Assigning an int to a string should not be allowed.")
        except TypeError:
            pass  # as it should be

        try:
            prop.choices = 5
            self.fail("Choices should be allowed only if it's a set.")
        except TypeError:
            pass  # as it should be

        prop.unsubscribe(self.callback_test_notify)

        self.assertTrue(self.called == 1)

        # It's also allowed to use dict as choices
        prop = model.VAEnumerated((1, 2), {(1, 2): "aaa", (3, 5): "doo"})
        for v in prop.choices:
            prop.value = v  # they all should work
コード例 #9
0
    def test_points_select_overlay(self):
        # Create stuff
        cnvs = miccanvas.DblMicroscopeCanvas(self.panel)

        self.add_control(cnvs, wx.EXPAND, proportion=1, clear=True)

        tab_mod = self.create_simple_tab_model()
        view = tab_mod.focussedView.value
        view.mpp.value = 1e-5
        cnvs.setView(view, tab_mod)

        # Manually add the overlay
        pol = wol.PointsOverlay(cnvs)
        cnvs.add_world_overlay(pol)

        cnvs.current_mode = guimodel.TOOL_POINT
        pol.activate()

        test.gui_loop()

        from itertools import product

        phys_points = product(xrange(-200, 201, 50), xrange(-200, 201, 50))
        phys_points = [(a / 1.0e5, b / 1.0e5) for a, b in phys_points]

        point = model.VAEnumerated(phys_points[0],
                                   choices=frozenset(phys_points))

        pol.set_point(point)
        test.gui_loop()

        cnvs.update_drawing()
        test.gui_loop(0.5)

        point.value = (50 / 1.0e5, 50 / 1.0e5)

        test.gui_loop(0.5)
コード例 #10
0
ファイル: _static.py プロジェクト: ihebdelmic/odemis
    def __init__(self, name, data, *args, **kwargs):
        """
        name (string)
        data (model.DataArray(Shadow) of shape (YX) or list of such DataArray(Shadow)).
         The metadata MD_POS, MD_AR_POLE and MD_POL_MODE should be provided
        """
        if not isinstance(data, collections.Iterable):
            data = [data]  # from now it's just a list of DataArray

        # TODO: support DAS, as a "delayed loading" by only calling .getData()
        # when the projection for the particular data needs to be computed (or
        # .raw needs to be accessed?)
        # Ensure all the data is a DataArray, as we don't handle (yet) DAS
        data = [
            d.getData() if isinstance(d, model.DataArrayShadow) else d
            for d in data
        ]

        # find positions of each acquisition
        # (float, float, str or None)) -> DataArray: position on SEM + polarization -> data
        self._pos = {}
        sempositions = set()
        polpositions = set()
        for d in data:
            try:
                sempos_cur = d.metadata[MD_POS]

                # When reading data: floating point error (slightly different keys for same ebeam pos)
                # -> check if there is already a position specified, which is very close by
                # (and therefore the same ebeam pos) and replace with that ebeam position
                # (e.g. all polarization positions for the same ebeam positions will have exactly the same ebeam pos)
                for sempos in sempositions:
                    if almost_equal(sempos_cur[0], sempos[0]) and almost_equal(
                            sempos_cur[1], sempos[1]):
                        sempos_cur = sempos
                        break
                self._pos[sempos_cur + (d.metadata.get(MD_POL_MODE, None),
                                        )] = img.ensure2DImage(d)
                sempositions.add(sempos_cur)
                if MD_POL_MODE in d.metadata:
                    polpositions.add(d.metadata.get(MD_POL_MODE))
            except KeyError:
                logging.info("Skipping DataArray without known position")

        # Cached conversion of the CCD image to polar representation
        # TODO: automatically fill it in a background thread
        self._polar = {}  # dict tuple (float, float, str or None) -> DataArray

        # SEM position VA
        # SEM position displayed, (None, None) == no point selected (x, y)
        self.point = model.VAEnumerated(
            (None, None),
            choices=frozenset([(None, None)] + list(sempositions)))

        if self._pos:
            # Pick one point, e.g., top-left
            bbtl = (min(x for x, y in sempositions if x is not None),
                    min(y for x, y in sempositions if y is not None))

            # top-left point is the closest from the bounding-box top-left
            def dis_bbtl(v):
                try:
                    return math.hypot(bbtl[0] - v[0], bbtl[1] - v[1])
                except TypeError:
                    return float("inf")  # for None, None

            self.point.value = min(sempositions, key=dis_bbtl)

        # no need for init=True, as Stream.__init__ will update the image
        self.point.subscribe(self._onPoint)

        # polarization VA
        # check if any polarization analyzer data, set([]) == no analyzer data (pol)
        if polpositions:
            # use first entry in acquisition to populate VA (acq could have 1 or 6 pol pos)
            self.polarization = model.VAEnumerated(list(polpositions)[0],
                                                   choices=polpositions)
            self.polarization.subscribe(self._onPolarization)

        if "acq_type" not in kwargs:
            kwargs["acq_type"] = model.MD_AT_AR
        super(StaticARStream, self).__init__(name, list(self._pos.values()),
                                             *args, **kwargs)
コード例 #11
0
    def __init__(self, name, role, parent, image, spectrograph=None, daemon=None, **kwargs):
        """ Initializes a fake readout camera.
        :parameter name: (str) as in Odemis
        :parameter role: (str) as in Odemis
        :parameter parent: class streakcamera
        :parameter image: fake input image
        """
        # TODO image focus and operate mode
        # get the fake images
        try:
            image_filename = str(image)
            # ensure relative path is from this file
            if not os.path.isabs(image):
                image_filename = os.path.join(os.path.dirname(__file__), image)
            converter = dataio.find_fittest_converter(image_filename, mode=os.O_RDONLY)
            self._img_list = []
            img_list = converter.read_data(image_filename)
            for img in img_list:
                if img.ndim > 3:  # remove dims of length 1
                    img = numpy.squeeze(img)
                self._img_list.append(img)  # can be RGB or greyscale
        except Exception:
            raise ValueError("Fake image does not fit requirements for temporal spectrum acquisition.")

        super(ReadoutCamera, self).__init__(name, role, parent=parent,
                                            daemon=daemon, **kwargs)  # init HwComponent

        self.parent = parent

        self._metadata[model.MD_HW_VERSION] = 'Simulated readout camera OrcaFlash 4.0 V3, ' \
                                              'Product number: C13440-20C, Serial number: 301730'
        self._metadata[model.MD_SW_VERSION] = 'Firmware: 4.20.B, Version: 4.20.B03-A19-B02-4.02'
        self._metadata[model.MD_DET_TYPE] = model.MD_DT_INTEGRATING

        # sensor size (resolution)
        # x (lambda): horizontal, y (time): vertical
        full_res = (self._img_list[0].shape[1], self._img_list[0].shape[0])
        self._metadata[model.MD_SENSOR_SIZE] = full_res

        # 16-bit
        depth = 2 ** (self._img_list[0].dtype.itemsize * 8)
        self._shape = full_res + (depth,)

        # variable needed to update resolution VA and wavelength list correctly (_updateWavelengthList())
        self._binning = (2, 2)

        # need to be before binning, as it is modified when changing binning
        resolution = (int(full_res[0]/self._binning[0]), int(full_res[1]/self._binning[1]))
        self.resolution = model.ResolutionVA(resolution, ((1, 1), full_res), setter=self._setResolution)

        # variable needed to update wavelength list correctly (_updateWavelengthList())
        self._resolution = self.resolution.value

        choices_bin = {(1, 1), (2, 2), (4, 4)}
        self.binning = model.VAEnumerated(self._binning, choices_bin, setter=self._setBinning)
        self._metadata[model.MD_BINNING] = self.binning.value

        # physical pixel size is 6.5um x 6.5um
        sensor_pixelsize = (6.5e-06, 6.5e-06)
        self._metadata[model.MD_SENSOR_PIXEL_SIZE] = sensor_pixelsize

        # pixelsize VA is the sensor size, it does not include binning or magnification
        self.pixelSize = model.VigilantAttribute(sensor_pixelsize, unit="m", readonly=True)

        range_exp = [0.00001, 1]  # 10us to 1s
        self._exp_time = 0.1  # 100 msec
        self.exposureTime = model.FloatContinuous(self._exp_time, range_exp, unit="s", setter=self._setCamExpTime)
        self._metadata[model.MD_EXP_TIME] = self.exposureTime.value

        self.readoutRate = model.VigilantAttribute(425000000, unit="Hz", readonly=True)  # MHz
        self._metadata[model.MD_READOUT_TIME] = 1 / self.readoutRate.value  # s

        # spectrograph VAs after readout camera VAs
        self._spectrograph = spectrograph
        if self._spectrograph:
            logging.debug("Starting streak camera with spectrograph.")
            self._spectrograph.position.subscribe(self._updateWavelengthList, init=True)
        else:
            logging.warning("No spectrograph specified. No wavelength metadata will be attached.")

        # for synchronized acquisition
        self._sync_event = None
        self.softwareTrigger = model.Event()

        # Simple implementation of the flow: we keep generating images and if
        # there are subscribers, they'll receive it.
        self.data = SimpleStreakCameraDataFlow(self._start, self._stop, self._sync)
        self._generator = None

        self._img_counter = 0  # initialize the image counter
コード例 #12
0
ファイル: ar_spectral.py プロジェクト: effting/odemis
    def __init__(self,
                 name,
                 detector,
                 sed,
                 emitter,
                 spectrograph,
                 lens_switch,
                 bigslit,
                 opm,
                 wl_inverted=False):
        """
        name (string): user-friendly name of this stream
        detector (Detector): the 2D CCD which get wavelength on the X axis and angles on the Y axis
        sed (Detector): the se-detector
        emitter (Emitter): the emitter (eg: ebeam scanner)
        spectrograph (Actuator): the spectrograph
        wl_inverted (bool): if True, will swap the wavelength axis of the CCD, in
          order to support hardware where the highest wavelengths are at the smallest
          indices. (The MD_WL_LIST is *not* inverted)
        """
        self.name = model.StringVA(name)

        # Hardware Components
        self._detector = detector
        self._sed = sed
        self._emitter = emitter
        self._sgr = spectrograph
        self._opm = opm
        self._lsw = lens_switch
        self._bigslit = bigslit
        self._wl_inverted = wl_inverted

        wlr = spectrograph.axes["wavelength"].range
        slitw = spectrograph.axes["slit-in"].range
        self.centerWavelength = model.FloatContinuous(500e-9, wlr, unit="m")
        self.slitWidth = model.FloatContinuous(100e-6, slitw, unit="m")
        # dwell time and exposure time are the same thing in this case
        self.dwellTime = model.FloatContinuous(
            1, range=detector.exposureTime.range, unit="s")
        self.emtTranslation = model.TupleContinuous(
            (0, 0),
            range=self._emitter.translation.range,
            cls=(int, long, float),
            unit="px")

        # Distance between the center of each pixel
        self.stepsize = model.FloatContinuous(1e-6, (1e-9, 1e-4), unit="m")

        # Region of acquisition. ROI form is LEFT Top RIGHT Bottom, relative to full field size
        self.roi = model.TupleContinuous((0, 0, 1, 1),
                                         range=((0, 0, 0, 0), (1, 1, 1, 1)),
                                         cls=(int, long, float))

        # For drift correction
        self.dcRegion = model.TupleContinuous(UNDEFINED_ROI,
                                              range=((0, 0, 0, 0), (1, 1, 1,
                                                                    1)),
                                              cls=(int, long, float))
        self.dcDwellTime = model.FloatContinuous(emitter.dwellTime.range[0],
                                                 range=emitter.dwellTime.range,
                                                 unit="s")

        #self.binning = model.VAEnumerated((1,1), choices=set([(1,1), (2,2), (2,3)]))
        # separate binning values because it can useful for experiment
        self.binninghorz = model.VAEnumerated(1, choices={1, 2, 4, 8, 16})
        self.binningvert = model.VAEnumerated(1, choices={1, 2, 4, 8, 16})
        self.nDC = model.IntContinuous(1, (1, 20))

        # For acquisition
        self.ARspectral_data = None
        self.ARspectral_data_received = threading.Event()
        self.sem_data = []
        self.sem_data_received = threading.Event()
        self._hw_settings = None
コード例 #13
0
    def __init__(self, name, image):
        """
        name (string)
        image (model.DataArray(Shadow) of shape (CYX) or (C11YX)). The metadata
        MD_WL_POLYNOMIAL or MD_WL_LIST should be included in order to associate the C to a
        wavelength.
        """
        # Spectrum stream has in addition to normal stream:
        #  * information about the current bandwidth displayed (avg. spectrum)
        #  * coordinates of 1st point (1-point, line)
        #  * coordinates of 2nd point (line)

        # TODO: need to handle DAS properly, in case it's tiled (in XY), to avoid
        # loading too much data in memory.
        # Ensure the data is a DataArray, as we don't handle (yet) DAS
        if isinstance(image, model.DataArrayShadow):
            image = image.getData()

        if len(image.shape) == 3:
            # force 5D
            image = image[:, numpy.newaxis, numpy.newaxis, :, :]
        elif len(image.shape) != 5 or image.shape[1:3] != (1, 1):
            logging.error("Cannot handle data of shape %s", image.shape)
            raise NotImplementedError("SpectrumStream needs a cube data")

        # This is for "average spectrum" projection
        try:
            # cached list of wavelength for each pixel pos
            self._wl_px_values = spectrum.get_wavelength_per_pixel(image)
        except (ValueError, KeyError):
            # useless polynomial => just show pixels values (ex: -50 -> +50 px)
            # TODO: try to make them always int?
            max_bw = image.shape[0] // 2
            min_bw = (max_bw - image.shape[0]) + 1
            self._wl_px_values = range(min_bw, max_bw + 1)
            assert(len(self._wl_px_values) == image.shape[0])
            unit_bw = "px"
            cwl = (max_bw + min_bw) // 2
            width = image.shape[0] // 12
        else:
            min_bw, max_bw = self._wl_px_values[0], self._wl_px_values[-1]
            unit_bw = "m"
            cwl = (max_bw + min_bw) / 2
            width = (max_bw - min_bw) / 12

        # TODO: allow to pass the calibration data as argument to avoid
        # recomputing the data just after init?
        # Spectrum efficiency compensation data: None or a DataArray (cf acq.calibration)
        self.efficiencyCompensation = model.VigilantAttribute(None, setter=self._setEffComp)

        # The background data (typically, an acquisition without e-beam).
        # It is subtracted from the acquisition data.
        # If set to None, a simple baseline background value is subtracted.
        self.background = model.VigilantAttribute(None, setter=self._setBackground)

        # low/high values of the spectrum displayed
        self.spectrumBandwidth = model.TupleContinuous(
                                    (cwl - width, cwl + width),
                                    range=((min_bw, min_bw), (max_bw, max_bw)),
                                    unit=unit_bw,
                                    cls=(int, long, float))

        # Whether the (per bandwidth) display should be split intro 3 sub-bands
        # which are applied to RGB
        self.fitToRGB = model.BooleanVA(False)

        # This attribute is used to keep track of any selected pixel within the
        # data for the display of a spectrum
        self.selected_pixel = model.TupleVA((None, None))  # int, int

        # first point, second point in pixels. It must be 2 elements long.
        self.selected_line = model.ListVA([(None, None), (None, None)], setter=self._setLine)

        # Peak method index, None if spectrum peak fitting curve is not displayed
        self.peak_method = model.VAEnumerated("gaussian", {"gaussian", "lorentzian", None})

        # The thickness of a point or a line (shared).
        # A point of width W leads to the average value between all the pixels
        # which are within W/2 from the center of the point.
        # A line of width W leads to a 1D spectrum taking into account all the
        # pixels which fit on an orthogonal line to the selected line at a
        # distance <= W/2.
        self.selectionWidth = model.IntContinuous(1, [1, 50], unit="px")

        self.fitToRGB.subscribe(self.onFitToRGB)
        self.spectrumBandwidth.subscribe(self.onSpectrumBandwidth)
        self.efficiencyCompensation.subscribe(self._onCalib)
        self.background.subscribe(self._onCalib)
        self.selectionWidth.subscribe(self._onSelectionWidth)

        self._calibrated = image  # the raw data after calibration
        super(StaticSpectrumStream, self).__init__(name, [image])

        # Automatically select point/line if data is small (can only be done
        # after .raw is set)
        if image.shape[-2:] == (1, 1):  # Only one point => select it immediately
            self.selected_pixel.value = (0, 0)
        elif image.shape[-2] == 1:  # Horizontal line => select line immediately
            self.selected_line.value = [(0, 0), (image.shape[-1] - 1, 0)]
        elif image.shape[-1] == 1:  # Vertical line => select line immediately
            self.selected_line.value = [(0, 0), (0, image.shape[-2] - 1)]
コード例 #14
0
ファイル: __init__.py プロジェクト: PierreBizouard/odemis
    def __init__(self, microscope):
        """
        microscope (model.Microscope or None): the root of the HwComponent tree
         provided by the back-end. If None, it means the interface is not
         connected to a microscope (and displays a recorded acquisition).
        """

        self.microscope = microscope
        self.role = None

        # These are either HwComponents or None (if not available)
        self.ccd = None
        self.stage = None
        self.focus = None  # actuator to change the camera focus
        self.aligner = None  # actuator to align ebeam/ccd
        self.mirror = None  # actuator to change the mirror position (on SPARC)
        self.light = None
        self.light_filter = None  # emission light filter for SECOM/output filter for SPARC
        self.lens = None
        self.ebeam = None
        self.ebeam_focus = None  # change the e-beam focus
        self.sed = None  # secondary electron detector
        self.bsd = None  # back-scatter electron detector
        self.spectrometer = None  # spectrometer
        self.spectrograph = None  # actuator to change the wavelength
        self.ar_spec_sel = None  # actuator to select AR/Spectrometer (SPARC)
        self.lens_switch = None  # actuator to (de)activate the lens (SPARC)
        self.chamber = None  # actuator to control the chamber (pressure)
        self.ccd_chamber = None  # view of inside the chamber
        self.ccd_overview = None  # global view from above the sample

        # Indicates whether the microscope is acquiring a high quality image
        self.is_acquiring = model.BooleanVA(False)

        if microscope:
            self.role = microscope.role

            for d in microscope.detectors:
                if d.role == "ccd":
                    self.ccd = d
                elif d.role == "se-detector":
                    self.sed = d
                elif d.role == "bs-detector":
                    self.bsd = d
                elif d.role == "spectrometer":
                    self.spectrometer = d
                elif d.role == "ccd-chamber":
                    self.ccd_chamber = d
                elif d.role == "ccd-overview":
                    self.ccd_overview = d

            for a in microscope.actuators:
                if a.role == "stage":
                    self.stage = a  # most views move this actuator when moving
                elif a.role == "focus":
                    self.focus = a
                elif a.role == "ebeam-focus":
                    self.ebeam_focus = a
                elif a.role == "mirror":
                    self.mirror = a
                elif a.role == "align":
                    self.aligner = a
                elif a.role == "lens-switch":
                    self.lens_switch = a
                elif a.role == "ar-spec-selector":
                    self.ar_spec_sel = a
                elif a.role == "chamber":
                    self.chamber = a

            # Spectrograph is not directly an actuator, but a sub-comp of spectrometer
            if self.spectrometer:
                for child in self.spectrometer.children:
                    if child.role == "spectrograph":
                        self.spectrograph = child

            for e in microscope.emitters:
                if e.role == "light":
                    self.light = e
                    self._light_power_on = None  # None = unknown
                elif e.role == "filter":
                    self.light_filter = e
                elif e.role == "lens":
                    self.lens = e
                elif e.role == "e-beam":
                    self.ebeam = e

            # Do some typical checks on expectations from an actual microscope
            if not any((self.ccd, self.sed, self.bsd, self.spectrometer)):
                raise KeyError("No detector found in the microscope")

            if not self.light and not self.ebeam:
                raise KeyError("No emitter found in the microscope")

        # TODO: all that on/off thing is crazy:
        # * we cannot do it (for now)
        # * we'd better turn on/off the hardware when streams need it
        # * pause and off are the same things but for SEM (blank/off)
        # * optical on in live view means light on, while in lens align it means light off
        # => we'd be better with just one global pause button (and pressure)

        # Handle turning on/off the instruments
        hw_states = set([STATE_OFF, STATE_ON, STATE_PAUSE])
        if self.ccd:
            # not so nice to hard code it here, but that should do it for now...
            if self.role == "sparc":
                self.arState = model.IntEnumerated(STATE_OFF,
                                                   choices=hw_states)
                self.arState.subscribe(self.onARState)
            else:
                self.opticalState = model.IntEnumerated(STATE_OFF,
                                                        choices=hw_states)
                self.opticalState.subscribe(self.onOpticalState)

        if self.ebeam:
            self.emState = model.IntEnumerated(STATE_OFF, choices=hw_states)
            self.emState.subscribe(self.onEMState)

        if self.spectrometer:
            self.specState = model.IntEnumerated(STATE_OFF, choices=hw_states)
            self.specState.subscribe(self.onSpecState)

        # Used when doing fine alignment, based on the value used by the user
        # when doing manual alignment. 0.1s is not too bad value if the user
        # hasn't specified anything (yet).
        self.fineAlignDwellTime = model.FloatContinuous(0.1,
                                                        range=[1e-9, 100],
                                                        unit="s")

        # TODO: should we put also the configuration related stuff?
        # Like path/file format
        # Set to True to request debug info to be displayed
        self.debug = model.BooleanVA(False)

        # Current tab (+ all available tabs in choices as a dict tab -> name)
        # Fully set and managed later by the TabBarController.
        # Not very beautiful because Tab is not part of the model.
        # MicroscopyGUIData would be better in theory, but is less convenient
        # do directly access additional GUI information.
        self.tab = model.VAEnumerated(None, choices={None: ""})
コード例 #15
0
    def __init__(self, name, detector, dataflow, emitter, em_filter, **kwargs):
        """
        name (string): user-friendly name of this stream
        detector (Detector): the detector which has the dataflow
        dataflow (Dataflow): the dataflow from which to get the data
        emitter (Light): the HwComponent to modify the light excitation
        em_filter (Filter): the HwComponent to modify the emission light filtering
        """
        super(FluoStream, self).__init__(name, detector, dataflow, emitter, **kwargs)
        self._em_filter = em_filter

        # Emission and excitation are based on the hardware capacities.
        # For excitation, compared to the hardware, only one band at a time can
        # be selected. The difficulty comes to pick the default value. The best
        # would be to use the current hardware value, but if the light is off
        # there is no default value. In that case, we pick the emission value
        # and try to pick a compatible excitation value: the first excitation
        # wavelength below the emission. However, the emission value might also
        # be difficult to know if there is a multi-band filter. In that case we
        # just pick the lowest value.
        # TODO: once the streams have their own version of the hardware settings
        # and in particular light.power, it should be possible to turn off the
        # light just by stopping the power, and so leaving the emissions as is.

        em_choices = em_filter.axes["band"].choices.copy()
        # convert any list into tuple, as lists cannot be put in a set
        for k, v in em_choices.items():
            em_choices[k] = conversion.ensure_tuple(v)

        # invert the dict, to directly convert the emission to the position value
        self._emission_to_idx = {v: k for k, v in em_choices.items()}

        cur_pos = em_filter.position.value["band"]
        current_em = em_choices[cur_pos]
        if isinstance(current_em[0], collections.Iterable):
            # if multiband => pick the first one
            em_band = current_em[0]
        else:
            em_band = current_em
        center_em = fluo.get_center(em_band)

        exc_choices = set(emitter.spectra.value)
        current_exc = self._get_current_excitation()
        if current_exc is None:
            # pick the closest below the current emission
            current_exc = min(exc_choices, key=lambda b: b[2])  # default to the smallest
            for b in exc_choices:
                # Works because exc_choices only contains 5-float tuples
                if (b[2] < center_em and
                    center_em - b[2] < center_em - current_exc[2]):
                    current_exc = b
            logging.debug("Guessed excitation is %s, based on emission %s",
                          current_exc, current_em)

        self.excitation = model.VAEnumerated(current_exc, choices=exc_choices,
                                             unit="m")
        self.excitation.subscribe(self.onExcitation)

        # The wavelength band on the out path (set when emission changes)
        self.emission = model.VAEnumerated(current_em, choices=set(em_choices.values()),
                                           unit="m")
        self.emission.subscribe(self.onEmission)

        # colouration of the image
        self.tint.value = conversion.wave2rgb(center_em)
コード例 #16
0
ファイル: __init__.py プロジェクト: arijitxx/odemis
    def __init__(self, microscope):
        """
        :param microscope: (model.Microscope or None): the root of the HwComponent tree
            provided by the back-end. If None, it means the interface is not
            connected to a microscope (and displays a recorded acquisition).

        """

        self.microscope = microscope
        self.role = None

        # The following attributes are either HwComponents or None (if not available)
        self.ccd = None
        self.stage = None
        self.focus = None  # actuator to change the camera focus
        self.aligner = None  # actuator to align ebeam/ccd
        self.mirror = None  # actuator to change the mirror position (on SPARC)
        self.light = None
        self.light_filter = None  # emission light filter for SECOM/output filter for SPARC
        self.lens = None
        self.ebeam = None
        self.ebeam_focus = None  # change the e-beam focus
        self.sed = None  # secondary electron detector
        self.bsd = None  # backscattered electron detector
        self.spectrometer = None  # spectrometer
        self.spectrograph = None  # actuator to change the wavelength
        self.ar_spec_sel = None  # actuator to select AR/Spectrometer (SPARC)
        self.lens_switch = None  # actuator to (de)activate the lens (SPARC)
        self.chamber = None  # actuator to control the chamber (has vacuum, pumping etc.)
        self.chamber_ccd = None  # view of inside the chamber
        self.chamber_light = None  # Light illuminating the chamber
        self.overview_ccd = None  # global view from above the sample
        self.overview_focus = None  # focus of the overview CCD
        self.overview_light = None  # light of the overview CCD

        # Indicates whether the microscope is acquiring a high quality image
        self.is_acquiring = model.BooleanVA(False)

        # The microscope object will be probed for common detectors, actuators, emitters etc.
        if microscope:
            self.role = microscope.role

            for c in microscope.children.value:
                if c.role == "ccd":
                    self.ccd = c
                elif c.role == "se-detector":
                    self.sed = c
                elif c.role == "bs-detector":
                    self.bsd = c
                elif c.role == "spectrometer":
                    self.spectrometer = c
                elif c.role == "chamber-ccd":
                    self.chamber_ccd = c
                elif c.role == "overview-ccd":
                    self.overview_ccd = c
                elif c.role == "stage":
                    self.stage = c  # most views move this actuator when moving
                elif c.role == "focus":
                    self.focus = c
                elif c.role == "ebeam-focus":
                    self.ebeam_focus = c
                elif c.role == "overview-focus":
                    self.overview_focus = c
                elif c.role == "mirror":
                    self.mirror = c
                elif c.role == "align":
                    self.aligner = c
                elif c.role == "lens-switch":
                    self.lens_switch = c
                elif c.role == "ar-spec-selector":
                    self.ar_spec_sel = c
                elif c.role == "chamber":
                    self.chamber = c
                elif c.role == "light":
                    self.light = c
                elif c.role == "filter":
                    self.light_filter = c
                elif c.role == "lens":
                    self.lens = c
                elif c.role == "e-beam":
                    self.ebeam = c
                elif c.role == "chamber-light":
                    self.chamber_light = c
                elif c.role == "overview-light":
                    self.overview_light = c

            # Spectrograph is not directly an actuator, but a sub-comp of spectrometer
            if self.spectrometer:
                for child in self.spectrometer.children.value:
                    if child.role == "spectrograph":
                        self.spectrograph = child

            # Check that the components that can be expected to be present on an actual microscope
            # have been correctly detected.

            if not any((self.ccd, self.sed, self.bsd, self.spectrometer)):
                raise KeyError("No detector found in the microscope")

            if not self.light and not self.ebeam:
                raise KeyError("No emitter found in the microscope")

        # Chamber is complex so we provide a "simplified state"
        # It's managed by the ChamberController. Setting to PUMPING or VENTING
        # state will request a pressure change.
        chamber_states = {
            CHAMBER_UNKNOWN, CHAMBER_VENTED, CHAMBER_PUMPING, CHAMBER_VACUUM,
            CHAMBER_VENTING
        }
        self.chamberState = model.IntEnumerated(CHAMBER_UNKNOWN,
                                                chamber_states)

        # Used when doing fine alignment, based on the value used by the user
        # when doing manual alignment. 0.1s is not too bad value if the user
        # hasn't specified anything (yet).
        self.fineAlignDwellTime = FloatContinuous(0.1,
                                                  range=[1e-9, 100],
                                                  unit="s")

        # TODO: should we put also the configuration related stuff?
        # Like path/file format
        # Set to True to request debug info to be displayed
        self.debug = model.BooleanVA(False)

        # Current tab (+ all available tabs in choices as a dict tab -> name)
        # Fully set and managed later by the TabBarController.
        # Not very beautiful because Tab is not part of the model.
        # MicroscopyGUIData would be better in theory, but is less convenient
        # do directly access additional GUI information.
        self.tab = model.VAEnumerated(None, choices={None: ""})
コード例 #17
0
    def __init__(self, name, role, parent, aperture=100e-6, wd=10e-3, **kwargs):
        """
        aperture (0 < float): aperture diameter of the electron lens
        wd (0 < float): working distance
        """
        # It will set up ._shape and .parent
        model.Emitter.__init__(self, name, role, parent=parent, **kwargs)
        self._aperture = aperture
        self._working_distance = wd

        fake_img = self.parent.fake_img
        if parent._drift_period:
            # half the size, to keep some margin for the drift
            self._shape = tuple(v // 2 for v in fake_img.shape[::-1])
        else:
            self._shape = fake_img.shape[::-1]

        # next two values are just to determine the pixel size
        # Distance between borders if magnification = 1. It should be found out
        # via calibration. We assume that image is square, i.e., VFV = HFV
        self._hfw_nomag = 0.25  # m

        # pixelSize is the same as MD_PIXEL_SIZE, with scale == 1
        # == smallest size/ between two different ebeam positions
        pxs = fake_img.metadata[model.MD_PIXEL_SIZE]
        self.pixelSize = model.VigilantAttribute(pxs, unit="m", readonly=True)

        # the horizontalFoV VA indicates that it's possible to control the zoom
        hfv = pxs[0] * self._shape[0]
        self.horizontalFoV = model.FloatContinuous(hfv, range=[10e-9, 10e-3],
                                                   unit="m")
        self.magnification = model.VigilantAttribute(self._hfw_nomag / hfv,
                                                     unit="", readonly=True)
        self.horizontalFoV.subscribe(self._onHFV)

        # To provide some rough idea of the step size when changing focus
        # Depends on the pixelSize, so will be updated whenever the HFW changes
        self.depthOfField = model.FloatContinuous(1e-6, range=(0, 1e9),
                                                  unit="m", readonly=True)
        self._updateDepthOfField()  # needs .pixelSize

        # (.resolution), .translation, .rotation, and .scaling are used to
        # define the conversion from coordinates to a region of interest.

        # (float, float) in m => physically moves the e-beam.
        shift_rng = ((-50e-06, -50e-06),
                    (50e-06, 50e-06))
        self.shift = model.TupleContinuous((0, 0), shift_rng,
                                              cls=(int, long, float), unit="m")

        # (float, float) in m => moves center of acquisition by this amount
        # independent of scale and rotation.
        tran_rng = [(-self._shape[0] / 2, -self._shape[1] / 2),
                    (self._shape[0] / 2, self._shape[1] / 2)]
        self.translation = model.TupleContinuous((0, 0), tran_rng,
                                              cls=(int, long, float), unit="px",
                                              setter=self._setTranslation)

        # .resolution is the number of pixels actually scanned. If it's less than
        # the whole possible area, it's centered.
        resolution = (self._shape[0] // 4, self._shape[1] // 4)
        self.resolution = model.ResolutionVA(resolution, [(1, 1), self._shape],
                                             setter=self._setResolution)
        self._resolution = resolution

        # (float, float) as a ratio => how big is a pixel, compared to pixelSize
        # it basically works the same as binning, but can be float
        # (Default to scan the whole area)
        self._scale = (self._shape[0] / resolution[0], self._shape[1] / resolution[1])
        self.scale = model.TupleContinuous(self._scale, [(1, 1), self._shape],
                                           cls=(int, long, float),
                                           unit="", setter=self._setScale)
        self.scale.subscribe(self._onScale, init=True) # to update metadata

        # (float) in rad => rotation of the image compared to the original axes
        self.rotation = model.FloatContinuous(0, [0, 2 * math.pi], unit="rad")

        self.dwellTime = model.FloatContinuous(1e-06, (1e-06, 1000), unit="s")

        # VAs to control the ebeam, purely fake
        self.probeCurrent = model.FloatEnumerated(1.3e-9,
                          {0.1e-9, 1.3e-9, 2.6e-9, 3.4e-9, 11.564e-9, 23e-9},
                          unit="A")
        self.accelVoltage = model.FloatContinuous(10e3, (1e3, 30e3), unit="V")

        # Pretend it's ready to acquire an image
        self.power = model.BooleanVA(True)
        # Blanker has a None = "auto" mode which automatically blanks when not scanning
        self.blanker = model.VAEnumerated(None, choices={True: 'blanked', False: 'unblanked', None: 'auto'})
コード例 #18
0
ファイル: _static.py プロジェクト: lanery/odemis
    def __init__(self, name, data, *args, **kwargs):
        """
        :param name: (string)
        :param data: (model.DataArray(Shadow) of shape (YX) or list of such DataArray(Shadow)).
        The metadata MD_POS, MD_AR_POLE and MD_POL_MODE should be provided
        """
        if not isinstance(data, collections.Iterable):
            data = [data]  # from now it's just a list of DataArray

        # TODO: support DAS, as a "delayed loading" by only calling .getData()
        # when the projection for the particular data needs to be computed (or
        # .raw needs to be accessed?)
        # Ensure all the data is a DataArray, as we don't handle (yet) DAS
        data = [d.getData() if isinstance(d, model.DataArrayShadow) else d for d in data]

        # find positions of each acquisition
        # (float, float, str or None)) -> DataArray: position on SEM + polarization -> data
        self._pos = {}

        sempositions = set()
        polpositions = set()

        for d in data:
            try:
                sempos_cur = d.metadata[MD_POS]

                # When reading data: floating point error (slightly different keys for same ebeam pos)
                # -> check if there is already a position specified, which is very close by
                # (and therefore the same ebeam pos) and replace with that ebeam position
                # (e.g. all polarization positions for the same ebeam positions will have exactly the same ebeam pos)
                for sempos in sempositions:
                    if almost_equal(sempos_cur[0], sempos[0]) and almost_equal(sempos_cur[1], sempos[1]):
                        sempos_cur = sempos
                        break
                self._pos[sempos_cur + (d.metadata.get(MD_POL_MODE, None),)] = img.ensure2DImage(d)

                sempositions.add(sempos_cur)
                if MD_POL_MODE in d.metadata:
                    polpositions.add(d.metadata[MD_POL_MODE])

            except KeyError:
                logging.info("Skipping DataArray without known position")

        # SEM position VA
        # SEM position displayed, (None, None) == no point selected (x, y)
        self.point = model.VAEnumerated((None, None),
                                        choices=frozenset([(None, None)] + list(sempositions)))

        if self._pos:
            # Pick one point, e.g., top-left
            bbtl = (min(x for x, y in sempositions if x is not None),
                    min(y for x, y in sempositions if y is not None))

            # top-left point is the closest from the bounding-box top-left
            def dis_bbtl(v):
                try:
                    return math.hypot(bbtl[0] - v[0], bbtl[1] - v[1])
                except TypeError:
                    return float("inf")  # for None, None
            self.point.value = min(sempositions, key=dis_bbtl)

        # check if any polarization analyzer data, (None) == no analyzer data (pol)
        if polpositions:
            # Check that for every position, all the polarizations are available,
            # as the GUI expects all the combinations possible, and weird errors
            # will happen when one is missing.
            for pos in sempositions:
                for pol in polpositions:
                    if pos + (pol,) not in self._pos:
                        logging.warning("Polarization data is not complete: missing %s,%s/%s",
                                        pos[0], pos[1], pol)

            # use first entry in acquisition to populate VA (acq could have 1 or 6 pol pos)
            current_pol = util.sorted_according_to(polpositions, POL_POSITIONS)[0]
            self.polarization = model.VAEnumerated(current_pol, choices=polpositions)

            # Add a polarimetry VA containing the polarimetry image results.
            # Note: Polarimetry analysis are only possible if all 6 images per ebeam pos exist.
            # Also check if arpolarimetry package can be imported as might not be installed.
            if polpositions >= set(POL_POSITIONS) and arpolarimetry:
                self.polarimetry = model.VAEnumerated(MD_POL_S0, choices=set(POL_POSITIONS_RESULTS))

        if "acq_type" not in kwargs:
            kwargs["acq_type"] = model.MD_AT_AR

        super(StaticARStream, self).__init__(name, list(self._pos.values()), *args, **kwargs)
コード例 #19
0
ファイル: vattributes_test.py プロジェクト: effting/odemis
    def test_enumerated(self):
        prop = model.StringEnumerated("a", {"a", "c", "bfds"})
        self.assertEqual(prop.value, "a")
        self.assertEqual(prop.choices, {"a", "c", "bfds"})

        self.called = 0
        prop.subscribe(self.callback_test_notify)
        # now count
        prop.value = "c"  # +1
        assert (prop.value == "c")

        try:
            prop.value = "wfds"
            self.fail("Assigning out of bound should not be allowed.")
        except IndexError:
            pass  # as it should be

        prop.choices = {"a", "c", "b", 5}
        assert (prop.value == "c")
        try:
            prop.choices = {"a", "b"}
            self.fail(
                "Assigning choices not containing current value should not be allowed."
            )
        except IndexError:
            pass  # as it should be

        try:
            prop.value = 5
            self.fail("Assigning an int to a string should not be allowed.")
        except TypeError:
            pass  # as it should be

        try:
            prop.choices = 5
            self.fail("Choices should be allowed only if it's a set.")
        except TypeError:
            pass  # as it should be

        prop.unsubscribe(self.callback_test_notify)

        self.assertTrue(self.called == 1)

        # It's also allowed to use dict as choices
        prop = model.VAEnumerated((1, 2), {(1, 2): "aaa", (3, 5): "doo"})
        for v in prop.choices:
            prop.value = v  # they all should work

        # It's also allowed to use tuples as choices, which contain no numbers
        prop = model.VAEnumerated((1, 1), {(1, 1), (4, 4), (5, "aaa")})
        prop.value = prop.clip((3, 3))
        # should find the closest value
        self.assertEqual(prop.value, (4, 4))
        for v in prop.choices:
            prop.value = v  # they all should work

        prop = model.VAEnumerated((1, "aaa"), {(1, "aaa"), (3, "bbb"),
                                               (4, "ccc")})
        prev_value = prop.value
        prop.value = prop.clip((3, 3))
        # should not find a closest value, but return old value
        self.assertEqual(prop.value, prev_value)
        for v in prop.choices:
            prop.value = v  # they all should work

        prop = model.VAEnumerated((1, "aaa"), {(1, "aaa"), (3, "bbb"),
                                               (4, "ccc")})
        prev_value = prop.value
        prop.value = prop.clip((5, "ddd"))
        # should not find a closest value as not all values in tuple are numbers, but return old value
        self.assertEqual(prop.value, prev_value)
        for v in prop.choices:
            prop.value = v  # they all should work
コード例 #20
0
ファイル: _static.py プロジェクト: ihebdelmic/odemis
    def __init__(self, name, image, *args, **kwargs):
        """
        name (string)
        image (model.DataArray(Shadow) of shape (CYX), (C11YX), (CTYX), (CT1YX), (1T1YX)).
        The metadata MD_WL_POLYNOMIAL or MD_WL_LIST should be included in order to
        associate the C to a wavelength.
        The metadata MD_TIME_LIST should be included to associate the T to a timestamp

        .background is a DataArray of shape (CT111), where C & T have the same length as in the data.
        .efficiencyCompensation is always DataArray of shape C1111.

        """
        # Spectrum stream has in addition to normal stream:
        #  * information about the current bandwidth displayed (avg. spectrum)
        #  * coordinates of 1st point (1-point, line)
        #  * coordinates of 2nd point (line)

        # TODO: need to handle DAS properly, in case it's tiled (in XY), to avoid
        # loading too much data in memory.
        # Ensure the data is a DataArray, as we don't handle (yet) DAS
        if isinstance(image, model.DataArrayShadow):
            image = image.getData()

        if len(image.shape) == 3:
            # force 5D for CYX
            image = image[:, numpy.newaxis, numpy.newaxis, :, :]
        elif len(image.shape) == 4:
            # force 5D for CTYX
            image = image[:, :, numpy.newaxis, :, :]
        elif len(image.shape) != 5 or image.shape[2] != 1:
            logging.error("Cannot handle data of shape %s", image.shape)
            raise NotImplementedError(
                "StaticSpectrumStream needs 3D or 4D data")

        # This is for "average spectrum" projection
        # cached list of wavelength for each pixel pos
        self._wl_px_values, unit_bw = spectrum.get_spectrum_range(image)
        min_bw, max_bw = self._wl_px_values[0], self._wl_px_values[-1]
        cwl = (max_bw + min_bw) / 2
        width = (max_bw - min_bw) / 12

        # The selected wavelength for a temporal spectrum display
        self.selected_wavelength = model.FloatContinuous(
            self._wl_px_values[0],
            range=(min_bw, max_bw),
            unit=unit_bw,
            setter=self._setWavelength)

        # Is there time data?
        if image.shape[1] > 1:
            # cached list of timestamps for each position in the time dimension
            self._tl_px_values, unit_t = spectrum.get_time_range(image)
            min_t, max_t = self._tl_px_values[0], self._tl_px_values[-1]

            # Allow the select the time as any value within the range, and the
            # setter will automatically "snap" it to the closest existing timestamp
            self.selected_time = model.FloatContinuous(self._tl_px_values[0],
                                                       range=(min_t, max_t),
                                                       unit=unit_t,
                                                       setter=self._setTime)

        # This attribute is used to keep track of any selected pixel within the
        # data for the display of a spectrum
        self.selected_pixel = model.TupleVA((None, None))  # int, int

        # first point, second point in pixels. It must be 2 elements long.
        self.selected_line = model.ListVA([(None, None), (None, None)],
                                          setter=self._setLine)

        # The thickness of a point or a line (shared).
        # A point of width W leads to the average value between all the pixels
        # which are within W/2 from the center of the point.
        # A line of width W leads to a 1D spectrum taking into account all the
        # pixels which fit on an orthogonal line to the selected line at a
        # distance <= W/2.
        self.selectionWidth = model.IntContinuous(1, [1, 50], unit="px")
        self.selectionWidth.subscribe(self._onSelectionWidth)

        # Peak method index, None if spectrum peak fitting curve is not displayed
        self.peak_method = model.VAEnumerated("gaussian",
                                              {"gaussian", "lorentzian", None})

        # TODO: allow to pass the calibration data as argument to avoid
        # recomputing the data just after init?
        # Spectrum efficiency compensation data: None or a DataArray (cf acq.calibration)
        self.efficiencyCompensation = model.VigilantAttribute(
            None, setter=self._setEffComp)
        self.efficiencyCompensation.subscribe(self._onCalib)

        # Is there spectrum data?
        if image.shape[0] > 1:
            # low/high values of the spectrum displayed
            self.spectrumBandwidth = model.TupleContinuous(
                (cwl - width, cwl + width),
                range=((min_bw, min_bw), (max_bw, max_bw)),
                unit=unit_bw,
                cls=(int, long, float))
            self.spectrumBandwidth.subscribe(self.onSpectrumBandwidth)

            # Whether the (per bandwidth) display should be split intro 3 sub-bands
            # which are applied to RGB
            self.fitToRGB = model.BooleanVA(False)
            self.fitToRGB.subscribe(self.onFitToRGB)

        # the raw data after calibration
        self.calibrated = model.VigilantAttribute(image)

        if "acq_type" not in kwargs:
            if image.shape[0] > 1 and image.shape[1] > 1:
                kwargs["acq_type"] = model.MD_AT_TEMPSPECTRUM
            elif image.shape[0] > 1:
                kwargs["acq_type"] = model.MD_AT_SPECTRUM
            elif image.shape[1] > 1:
                kwargs["acq_type"] = model.MD_AT_TEMPORAL
            else:
                logging.warning(
                    "SpectrumStream data has no spectrum or time dimension, shape = %s",
                    image.shape)

        super(StaticSpectrumStream, self).__init__(name, [image], *args,
                                                   **kwargs)

        # Automatically select point/line if data is small (can only be done
        # after .raw is set)
        if image.shape[-2:] == (1,
                                1):  # Only one point => select it immediately
            self.selected_pixel.value = (0, 0)
        elif image.shape[
                -2] == 1:  # Horizontal line => select line immediately
            self.selected_line.value = [(0, 0), (image.shape[-1] - 1, 0)]
        elif image.shape[-1] == 1:  # Vertical line => select line immediately
            self.selected_line.value = [(0, 0), (0, image.shape[-2] - 1)]