class CameraImage(traits.HasTraits):

    #Traits view definitions:
    traits_view = traitsui.View(traitsui.Group(
        traitsui.HGroup(traitsui.Item('pixelsX', label="Pixels X"),
                        traitsui.Item('pixelsY', label="Pixels Y"))),
                                buttons=["OK", "Cancel"])

    pixelsX = traits.CInt(768)
    pixelsY = traits.CInt(512)

    xs = traits.Array
    ys = traits.Array
    zs = traits.Array

    minZ = traits.Float
    maxZ = traits.Float

    scale = traits.Float(1.)
    offset = traits.Float(0.)

    ODCorrectionBool = traits.Bool(
        False,
        desc=
        "if true will correct the image to account for the maximum OD parameter"
    )
    ODSaturationValue = traits.Float(
        3.0, desc="the value of the saturated optical density")

    model_changed = traits.Event

    def __init__(self, *args, **kwargs):
        super(CameraImage, self).__init__(*args, **kwargs)

    def _xs_default(self):
        return scipy.linspace(0.0, self.pixelsX - 1, self.pixelsX)

    def _ys_default(self):
        return scipy.linspace(0.0, self.pixelsY - 1, self.pixelsY)

    def _zs_default(self):
        #return scipy.zeros((self.pixelsY, self.pixelsX))
        return scipy.random.random_sample((self.pixelsY, self.pixelsX))

    def _scale_changed(self):
        """update zs data when scale or offset changed """
        logger.info("model scale changed")
        #self.getImageData(self.imageFile)
        self.zs = scipy.random.random_sample((self.pixelsY, self.pixelsX))
        self.model_changed = True

    def _offset_changed(self):
        """update zs data when scale or offset changed """
Esempio n. 2
0
class OdinEmbeddedMeta(Schema):
    """OdinEmbeddedMeta info that can be stored in a schema bundle."""
    #: Subject code
    subject = traits.CBytes(desc='subject code', maxlen=16)

    #: Time of creation
    timestamp = traits.CFloat(desc='unix timestamp')

    #: Number of embedded channels
    num_channels = traits.CInt(desc='number of channels')

    #: Number of classifiers
    num_classifiers = traits.CInt(desc='number of classifiers')
Esempio n. 3
0
class DataAxis(t.HasTraits):
    name = t.Str()
    units = t.Str()
    scale = t.Float()
    offset = t.Float()
    size = t.CInt()
    low_value = t.Float()
    high_value = t.Float()
    value = t.Range('low_value', 'high_value')
    low_index = t.Int(0)
    high_index = t.Int()
    slice = t.Instance(slice)
    navigate = t.Bool(t.Undefined)
    index = t.Range('low_index', 'high_index')
    axis = t.Array()
    continuous_value = t.Bool(False)

    def __init__(self,
                 size,
                 index_in_array=None,
                 name=t.Undefined,
                 scale=1.,
                 offset=0.,
                 units=t.Undefined,
                 navigate=t.Undefined):
        super(DataAxis, self).__init__()
        self.name = name
        self.units = units
        self.scale = scale
        self.offset = offset
        self.size = size
        self.high_index = self.size - 1
        self.low_index = 0
        self.index = 0
        self.update_axis()
        self.navigate = navigate
        self.axes_manager = None
        self.on_trait_change(self.update_axis,
                             ['scale', 'offset', 'size'])
        self.on_trait_change(self.update_value, 'index')
        self.on_trait_change(self.set_index_from_value, 'value')
        self.on_trait_change(self._update_slice, 'navigate')
        self.on_trait_change(self.update_index_bounds, 'size')
        # The slice must be updated even if the default value did not
        # change to correctly set its value.
        self._update_slice(self.navigate)

    @property
    def index_in_array(self):
        if self.axes_manager is not None:
            return self.axes_manager._axes.index(self)
        else:
            raise AttributeError(
                "This DataAxis does not belong to an AxesManager"
                " and therefore its index_in_array attribute "
                " is not defined")

    @property
    def index_in_axes_manager(self):
        if self.axes_manager is not None:
            return self.axes_manager._get_axes_in_natural_order().\
                index(self)
        else:
            raise AttributeError(
                "This DataAxis does not belong to an AxesManager"
                " and therefore its index_in_array attribute "
                " is not defined")

    def _get_positive_index(self, index):
        if index < 0:
            index = self.size + index
            if index < 0:
                raise IndexError("index out of bounds")
        return index

    def _get_index(self, value):
        if isfloat(value):
            return self.value2index(value)
        else:
            return value

    def _get_array_slices(self, slice_):
        """Returns a slice to slice the corresponding data axis without
        changing the offset and scale of the DataAxis.

        Parameters
        ----------
        slice_ : {float, int, slice}

        Returns
        -------
        my_slice : slice

        """
        v2i = self.value2index

        if isinstance(slice_, slice):
            start = slice_.start
            stop = slice_.stop
            step = slice_.step
        else:
            if isfloat(slice_):
                start = v2i(slice_)
            else:
                start = self._get_positive_index(slice_)
            stop = start + 1
            step = None

        if isfloat(step):
            step = int(round(step / self.scale))
        if isfloat(start):
            try:
                start = v2i(start)
            except ValueError:
                # The value is below the axis limits
                # we slice from the start.
                start = None
        if isfloat(stop):
            try:
                stop = v2i(stop)
            except ValueError:
                # The value is above the axes limits
                # we slice up to the end.
                stop = None

        if step == 0:
            raise ValueError("slice step cannot be zero")

        return slice(start, stop, step)

    def _slice_me(self, slice_):
        """Returns a slice to slice the corresponding data axis and
        change the offset and scale of the DataAxis acordingly.

        Parameters
        ----------
        slice_ : {float, int, slice}

        Returns
        -------
        my_slice : slice

        """
        i2v = self.index2value

        my_slice = self._get_array_slices(slice_)

        start, stop, step = my_slice.start, my_slice.stop, my_slice.step

        if start is None:
            if step > 0 or step is None:
                start = 0
            else:
                start = self.size - 1
        self.offset = i2v(start)
        if step is not None:
            self.scale *= step

        return my_slice

    def _get_name(self):
        if self.name is t.Undefined:
            if self.axes_manager is None:
                name = "Unnamed"
            else:
                name = "Unnamed " + ordinal(self.index_in_axes_manager)
        else:
            name = self.name
        return name

    def __repr__(self):
        text = '<%s axis, size: %i' % (self._get_name(),
                                       self.size,)
        if self.navigate is True:
            text += ", index: %i" % self.index
        text += ">"
        return text.encode('utf8')

    def __str__(self):
        return self._get_name() + " axis"

    def connect(self, f, trait='value'):
        self.on_trait_change(f, trait)

    def disconnect(self, f, trait='value'):
        self.on_trait_change(f, trait, remove=True)

    def update_index_bounds(self):
        self.high_index = self.size - 1

    def update_axis(self):
        self.axis = generate_axis(self.offset, self.scale, self.size)
        if len(self.axis) != 0:
            self.low_value, self.high_value = (
                self.axis.min(), self.axis.max())

    def _update_slice(self, value):
        if value is False:
            self.slice = slice(None)
        else:
            self.slice = None

    def get_axis_dictionary(self):
        adict = {
            'name': self.name,
            'scale': self.scale,
            'offset': self.offset,
            'size': self.size,
            'units': self.units,
            'navigate': self.navigate
        }
        return adict

    def copy(self):
        return DataAxis(**self.get_axis_dictionary())

    def __copy__(self):
        return self.copy()

    def __deepcopy__(self, memo):
        cp = self.copy()
        return cp

    def update_value(self):
        self.value = self.axis[self.index]

    def value2index(self, value, rounding=round):
        """Return the closest index to the given value if between the limit.

        Parameters
        ----------
        value : number or numpy array

        Returns
        -------
        index : integer or numpy array

        Raises
        ------
        ValueError if any value is out of the axis limits.

        """
        if value is None:
            return None

        if isinstance(value, np.ndarray):
            if rounding is round:
                rounding = np.round
            elif rounding is math.ceil:
                rounding = np.ceil
            elif rounding is math.floor:
                rounding = np.floor

        index = rounding((value - self.offset) / self.scale)

        if isinstance(value, np.ndarray):
            index = index.astype(int)
            if np.all(self.size > index) and np.all(index >= 0):
                return index
            else:
                raise ValueError("A value is out of the axis limits")
        else:
            index = int(index)
            if self.size > index >= 0:
                return index
            else:
                raise ValueError("The value is out of the axis limits")

    def index2value(self, index):
        if isinstance(index, np.ndarray):
            return self.axis[index.ravel()].reshape(index.shape)
        else:
            return self.axis[index]

    def set_index_from_value(self, value):
        self.index = self.value2index(value)
        # If the value is above the limits we must correct the value
        if self.continuous_value is False:
            self.value = self.index2value(self.index)

    def calibrate(self, value_tuple, index_tuple, modify_calibration=True):
        scale = (value_tuple[1] - value_tuple[0]) /\
            (index_tuple[1] - index_tuple[0])
        offset = value_tuple[0] - scale * index_tuple[0]
        if modify_calibration is True:
            self.offset = offset
            self.scale = scale
        else:
            return offset, scale

    def value_range_to_indices(self, v1, v2):
        """Convert the given range to index range.

        When an out of the axis limits, the endpoint is used instead.

        Parameters
        ----------
        v1, v2 : float
            The end points of the interval in the axis units. v2 must be
            greater than v1.

        """
        if v1 > v2:
            raise ValueError("v2 must be greater than v1.")

        if v1 is not None and v1 > self.low_value and v1 <= self.high_value:
            i1 = self.value2index(v1)
        else:
            i1 = 0
        if v2 is not None and v2 < self.high_value and v2 >= self.low_value:
            i2 = self.value2index(v2)
        else:
            i2 = self.size - 1
        return i1, i2
Esempio n. 4
0
class DataAxis(t.HasTraits):
    name = t.Str()
    units = t.Str()
    scale = t.Float()
    offset = t.Float()
    size = t.CInt()
    low_value = t.Float()
    high_value = t.Float()
    value = t.Range('low_value', 'high_value')
    low_index = t.Int(0)
    high_index = t.Int()
    slice = t.Instance(slice)
    navigate = t.Bool(t.Undefined)
    index = t.Range('low_index', 'high_index')
    axis = t.Array()
    continuous_value = t.Bool(False)

    def __init__(self,
                 size,
                 index_in_array=None,
                 name=t.Undefined,
                 scale=1.,
                 offset=0.,
                 units=t.Undefined,
                 navigate=t.Undefined):
        super(DataAxis, self).__init__()
        self.events = Events()
        self.events.index_changed = Event("""
            Event that triggers when the index of the `DataAxis` changes

            Triggers after the internal state of the `DataAxis` has been
            updated.

            Arguments:
            ---------
            obj : The DataAxis that the event belongs to.
            index : The new index
            """,
                                          arguments=["obj", 'index'])
        self.events.value_changed = Event("""
            Event that triggers when the value of the `DataAxis` changes

            Triggers after the internal state of the `DataAxis` has been
            updated.

            Arguments:
            ---------
            obj : The DataAxis that the event belongs to.
            value : The new value
            """,
                                          arguments=["obj", 'value'])
        self._suppress_value_changed_trigger = False
        self._suppress_update_value = False
        self.name = name
        self.units = units
        self.scale = scale
        self.offset = offset
        self.size = size
        self.high_index = self.size - 1
        self.low_index = 0
        self.index = 0
        self.update_axis()
        self.navigate = navigate
        self.axes_manager = None
        self.on_trait_change(self.update_axis, ['scale', 'offset', 'size'])
        self.on_trait_change(self._update_slice, 'navigate')
        self.on_trait_change(self.update_index_bounds, 'size')
        # The slice must be updated even if the default value did not
        # change to correctly set its value.
        self._update_slice(self.navigate)

    def _index_changed(self, name, old, new):
        self.events.index_changed.trigger(obj=self, index=self.index)
        if not self._suppress_update_value:
            new_value = self.axis[self.index]
            if new_value != self.value:
                self.value = new_value

    def _value_changed(self, name, old, new):
        old_index = self.index
        new_index = self.value2index(new)
        if self.continuous_value is False:  # Only values in the grid alowed
            if old_index != new_index:
                self.index = new_index
                if new == self.axis[self.index]:
                    self.events.value_changed.trigger(obj=self, value=new)
            elif old_index == new_index:
                new_value = self.index2value(new_index)
                if new_value == old:
                    self._suppress_value_changed_trigger = True
                    try:
                        self.value = new_value
                    finally:
                        self._suppress_value_changed_trigger = False

                elif new_value == new and not\
                        self._suppress_value_changed_trigger:
                    self.events.value_changed.trigger(obj=self, value=new)
        else:  # Intergrid values are alowed. This feature is deprecated
            self.events.value_changed.trigger(obj=self, value=new)
            if old_index != new_index:
                self._suppress_update_value = True
                self.index = new_index
                self._suppress_update_value = False

    @property
    def index_in_array(self):
        if self.axes_manager is not None:
            return self.axes_manager._axes.index(self)
        else:
            raise AttributeError(
                "This DataAxis does not belong to an AxesManager"
                " and therefore its index_in_array attribute "
                " is not defined")

    @property
    def index_in_axes_manager(self):
        if self.axes_manager is not None:
            return self.axes_manager._get_axes_in_natural_order().\
                index(self)
        else:
            raise AttributeError(
                "This DataAxis does not belong to an AxesManager"
                " and therefore its index_in_array attribute "
                " is not defined")

    def _get_positive_index(self, index):
        if index < 0:
            index = self.size + index
            if index < 0:
                raise IndexError("index out of bounds")
        return index

    def _get_index(self, value):
        if isfloat(value):
            return self.value2index(value)
        else:
            return value

    def _get_array_slices(self, slice_):
        """Returns a slice to slice the corresponding data axis without
        changing the offset and scale of the DataAxis.

        Parameters
        ----------
        slice_ : {float, int, slice}

        Returns
        -------
        my_slice : slice

        """
        v2i = self.value2index

        if isinstance(slice_, slice):
            start = slice_.start
            stop = slice_.stop
            step = slice_.step
        else:
            if isfloat(slice_):
                start = v2i(slice_)
            else:
                start = self._get_positive_index(slice_)
            stop = start + 1
            step = None

        if isfloat(step):
            step = int(round(step / self.scale))
        if isfloat(start):
            try:
                start = v2i(start)
            except ValueError:
                if start > self.high_value:
                    # The start value is above the axis limit
                    raise IndexError(
                        "Start value above axis high bound for  axis %s."
                        "value: %f high_bound: %f" %
                        (repr(self), start, self.high_value))
                else:
                    # The start value is below the axis limit,
                    # we slice from the start.
                    start = None
        if isfloat(stop):
            try:
                stop = v2i(stop)
            except ValueError:
                if stop < self.low_value:
                    # The stop value is below the axis limits
                    raise IndexError(
                        "Stop value below axis low bound for  axis %s."
                        "value: %f low_bound: %f" %
                        (repr(self), stop, self.low_value))
                else:
                    # The stop value is below the axis limit,
                    # we slice until the end.
                    stop = None

        if step == 0:
            raise ValueError("slice step cannot be zero")

        return slice(start, stop, step)

    def _slice_me(self, slice_):
        """Returns a slice to slice the corresponding data axis and
        change the offset and scale of the DataAxis acordingly.

        Parameters
        ----------
        slice_ : {float, int, slice}

        Returns
        -------
        my_slice : slice

        """
        i2v = self.index2value

        my_slice = self._get_array_slices(slice_)

        start, stop, step = my_slice.start, my_slice.stop, my_slice.step

        if start is None:
            if step is None or step > 0:
                start = 0
            else:
                start = self.size - 1
        self.offset = i2v(start)
        if step is not None:
            self.scale *= step

        return my_slice

    def _get_name(self):
        if self.name is t.Undefined:
            if self.axes_manager is None:
                name = "Unnamed"
            else:
                name = "Unnamed " + ordinal(self.index_in_axes_manager)
        else:
            name = self.name
        return name

    def __repr__(self):
        text = '<%s axis, size: %i' % (
            self._get_name(),
            self.size,
        )
        if self.navigate is True:
            text += ", index: %i" % self.index
        text += ">"
        return text

    def __str__(self):
        return self._get_name() + " axis"

    def update_index_bounds(self):
        self.high_index = self.size - 1

    def update_axis(self):
        self.axis = generate_axis(self.offset, self.scale, self.size)
        if len(self.axis) != 0:
            self.low_value, self.high_value = (self.axis.min(),
                                               self.axis.max())

    def _update_slice(self, value):
        if value is False:
            self.slice = slice(None)
        else:
            self.slice = None

    def get_axis_dictionary(self):
        adict = {
            'name': self.name,
            'scale': self.scale,
            'offset': self.offset,
            'size': self.size,
            'units': self.units,
            'navigate': self.navigate
        }
        return adict

    def copy(self):
        return DataAxis(**self.get_axis_dictionary())

    def __copy__(self):
        return self.copy()

    def __deepcopy__(self, memo):
        cp = self.copy()
        return cp

    def value2index(self, value, rounding=round):
        """Return the closest index to the given value if between the limit.

        Parameters
        ----------
        value : number or numpy array

        Returns
        -------
        index : integer or numpy array

        Raises
        ------
        ValueError if any value is out of the axis limits.

        """
        if value is None:
            return None

        if isinstance(value, np.ndarray):
            if rounding is round:
                rounding = np.round
            elif rounding is math.ceil:
                rounding = np.ceil
            elif rounding is math.floor:
                rounding = np.floor

        index = rounding((value - self.offset) / self.scale)

        if isinstance(value, np.ndarray):
            index = index.astype(int)
            if np.all(self.size > index) and np.all(index >= 0):
                return index
            else:
                raise ValueError("A value is out of the axis limits")
        else:
            index = int(index)
            if self.size > index >= 0:
                return index
            else:
                raise ValueError("The value is out of the axis limits")

    def index2value(self, index):
        if isinstance(index, np.ndarray):
            return self.axis[index.ravel()].reshape(index.shape)
        else:
            return self.axis[index]

    def calibrate(self, value_tuple, index_tuple, modify_calibration=True):
        scale = (value_tuple[1] - value_tuple[0]) /\
            (index_tuple[1] - index_tuple[0])
        offset = value_tuple[0] - scale * index_tuple[0]
        if modify_calibration is True:
            self.offset = offset
            self.scale = scale
        else:
            return offset, scale

    def value_range_to_indices(self, v1, v2):
        """Convert the given range to index range.

        When an out of the axis limits, the endpoint is used instead.

        Parameters
        ----------
        v1, v2 : float
            The end points of the interval in the axis units. v2 must be
            greater than v1.

        """
        if v1 is not None and v2 is not None and v1 > v2:
            raise ValueError("v2 must be greater than v1.")

        if v1 is not None and self.low_value < v1 <= self.high_value:
            i1 = self.value2index(v1)
        else:
            i1 = 0
        if v2 is not None and self.high_value > v2 >= self.low_value:
            i2 = self.value2index(v2)
        else:
            i2 = self.size - 1
        return i1, i2

    def update_from(self, axis, attributes=["scale", "offset", "units"]):
        """Copy values of specified axes fields from the passed AxesManager.

        Parameters
        ----------
        axis : DataAxis
            The DataAxis instance to use as a source for values.
        attributes : iterable container of strings.
            The name of the attribute to update. If the attribute does not
            exist in either of the AxesManagers, an AttributeError will be
            raised.
        Returns
        -------
        A boolean indicating whether any changes were made.

        """
        any_changes = False
        changed = {}
        for f in attributes:
            if getattr(self, f) != getattr(axis, f):
                changed[f] = getattr(axis, f)
        if len(changed) > 0:
            self.trait_set(**changed)
            any_changes = True
        return any_changes
class ImagePlotInspector(traits.HasTraits):

    pixelsX = traits.CInt(768)
    pixelsY = traits.CInt(512)

    xs = traits.Array
    ys = traits.Array
    zs = traits.Array
    minZ = traits.Float
    maxZ = traits.Float
    drawContourBool = traits.Bool(False)
    test = traits.Button("test")
    contourXS = None
    contourYS = None
    contourZS = None
    model_changed = traits.Event

    def _xs_default(self):
        return scipy.linspace(0.0, self.pixelsX - 1, self.pixelsX)

    def _ys_default(self):
        return scipy.linspace(0.0, self.pixelsY - 1, self.pixelsY)

    def _zs_default(self):
        return scipy.zeros((self.pixelsY, self.pixelsX))

    def _minZ_default(self):
        return 0.0

    def _maxZ_default(self):
        return 1.0

    def setData(self, zs):
        self.zs = zs
        self.minZ = zs.min()
        self.maxZ = zs.max()
        self.update()

    settingsGroup = traitsui.VGroup(
        traitsui.VGroup(traitsui.HGroup(
            traitsui.Item('pixelsX', label="Pixels X"),
            traitsui.Item('pixelsY', label="Pixels Y")),
                        traitsui.HGroup(
                            traitsui.Item('contourLevels',
                                          label="Contour Levels"),
                            traitsui.Item('colormap', label="Colour Map"),
                            "test"),
                        traitsui.HGroup(
                            traitsui.Item('fixAspectRatioBool',
                                          label="Fix Plot Aspect Ratio?")),
                        label="settings",
                        show_border=True),
        traitsui.VGroup(traitsui.HGroup('autoRangeColor', 'colorMapRangeLow',
                                        'colorMapRangeHigh'),
                        traitsui.HGroup('horizontalAutoRange',
                                        'horizontalLowerLimit',
                                        'horizontalUpperLimit'),
                        traitsui.HGroup('verticalAutoRange',
                                        'verticalLowerLimit',
                                        'verticalUpperLimit'),
                        label="axis limits",
                        show_border=True))

    plotGroup = traitsui.Group(
        traitsui.Item('container',
                      editor=ComponentEditor(size=(800, 600)),
                      show_label=False))

    traits_view = traitsui.View(settingsGroup,
                                plotGroup,
                                buttons=traitsmenu.NoButtons,
                                handler=ImagePlotInspectorHandler,
                                title="ImagePlotInspectorHandler",
                                resizable=True)

    contourLevels = traits.Int(15)
    colormap = traits.Enum(colormaps.color_map_name_dict.keys())

    autoRangeColor = traits.Bool(True)
    colorMapRangeLow = traits.Float
    colorMapRangeHigh = traits.Float

    horizontalAutoRange = traits.Bool(True)
    horizontalLowerLimit = traits.Float
    horizontalUpperLimit = traits.Float

    verticalAutoRange = traits.Bool(True)
    verticalLowerLimit = traits.Float
    verticalUpperLimit = traits.Float

    fixAspectRatioBool = traits.Bool(False)

    #---------------------------------------------------------------------------
    # Private Traits
    #---------------------------------------------------------------------------
    _image_index = traits.Instance(chaco.GridDataSource)
    _image_value = traits.Instance(chaco.ImageData)
    _cmap = traits.Trait(colormaps.jet, traits.Callable)

    #---------------------------------------------------------------------------
    # Public View interface
    #---------------------------------------------------------------------------

    def __init__(self, *args, **kwargs):
        super(ImagePlotInspector, self).__init__(*args, **kwargs)
        self.create_plot()
        self.update()
        logger.info("initialisation of Image inspector plot")

    def create_plot(self):
        # Create the mapper, etc
        self._image_index = chaco.GridDataSource(scipy.array([]),
                                                 scipy.array([]),
                                                 sort_order=("ascending",
                                                             "ascending"))
        image_index_range = chaco.DataRange2D(self._image_index)
        self._image_index.on_trait_change(self._metadata_changed,
                                          "metadata_changed")

        self._image_value = chaco.ImageData(data=scipy.array([]),
                                            value_depth=1)

        image_value_range = chaco.DataRange1D(self._image_value)

        self.polyplot = chaco.CMapImagePlot(
            index=self._image_index,
            value=self._image_value,
            index_mapper=chaco.GridMapper(range=image_index_range),
            color_mapper=self._cmap(image_value_range))

        # Add a left axis to the plot
        left = chaco.PlotAxis(orientation='left',
                              title="y",
                              mapper=self.polyplot.index_mapper._ymapper,
                              component=self.polyplot)
        self.polyplot.overlays.append(left)

        # Add a bottom axis to the plot
        bottom = chaco.PlotAxis(orientation='bottom',
                                title="x",
                                mapper=self.polyplot.index_mapper._xmapper,
                                component=self.polyplot)
        self.polyplot.overlays.append(bottom)

        # Add some tools to the plot
        self.polyplot.tools.append(
            tools.PanTool(self.polyplot,
                          constrain_key="shift",
                          drag_button="middle"))

        self.polyplot.overlays.append(
            tools.ZoomTool(component=self.polyplot,
                           tool_mode="box",
                           always_on=False))

        self.lineInspectorX = clickableLineInspector.ClickableLineInspector(
            component=self.polyplot,
            axis='index_x',
            inspect_mode="indexed",
            write_metadata=True,
            is_listener=False,
            color="white")

        self.lineInspectorY = clickableLineInspector.ClickableLineInspector(
            component=self.polyplot,
            axis='index_y',
            inspect_mode="indexed",
            write_metadata=True,
            color="white",
            is_listener=False)

        self.polyplot.overlays.append(self.lineInspectorX)
        self.polyplot.overlays.append(self.lineInspectorY)

        self.boxSelection2D = boxSelection2D.BoxSelection2D(
            component=self.polyplot)
        self.polyplot.overlays.append(self.boxSelection2D)

        # Add these two plots to one container
        self.centralContainer = chaco.OverlayPlotContainer(padding=0,
                                                           use_backbuffer=True,
                                                           unified_draw=True)
        self.centralContainer.add(self.polyplot)

        # Create a colorbar
        cbar_index_mapper = chaco.LinearMapper(range=image_value_range)
        self.colorbar = chaco.ColorBar(
            index_mapper=cbar_index_mapper,
            plot=self.polyplot,
            padding_top=self.polyplot.padding_top,
            padding_bottom=self.polyplot.padding_bottom,
            padding_right=40,
            resizable='v',
            width=30)

        self.plotData = chaco.ArrayPlotData(
            line_indexHorizontal=scipy.array([]),
            line_valueHorizontal=scipy.array([]),
            scatter_indexHorizontal=scipy.array([]),
            scatter_valueHorizontal=scipy.array([]),
            scatter_colorHorizontal=scipy.array([]),
            fitLine_indexHorizontal=scipy.array([]),
            fitLine_valueHorizontal=scipy.array([]))

        self.crossPlotHorizontal = chaco.Plot(self.plotData, resizable="h")
        self.crossPlotHorizontal.height = 100
        self.crossPlotHorizontal.padding = 20
        self.crossPlotHorizontal.plot(
            ("line_indexHorizontal", "line_valueHorizontal"), line_style="dot")
        self.crossPlotHorizontal.plot(
            ("scatter_indexHorizontal", "scatter_valueHorizontal",
             "scatter_colorHorizontal"),
            type="cmap_scatter",
            name="dot",
            color_mapper=self._cmap(image_value_range),
            marker="circle",
            marker_size=4)

        self.crossPlotHorizontal.index_range = self.polyplot.index_range.x_range

        self.plotData.set_data("line_indexVertical", scipy.array([]))
        self.plotData.set_data("line_valueVertical", scipy.array([]))
        self.plotData.set_data("scatter_indexVertical", scipy.array([]))
        self.plotData.set_data("scatter_valueVertical", scipy.array([]))
        self.plotData.set_data("scatter_colorVertical", scipy.array([]))
        self.plotData.set_data("fitLine_indexVertical", scipy.array([]))
        self.plotData.set_data("fitLine_valueVertical", scipy.array([]))

        self.crossPlotVertical = chaco.Plot(self.plotData,
                                            width=140,
                                            orientation="v",
                                            resizable="v",
                                            padding=20,
                                            padding_bottom=160)
        self.crossPlotVertical.plot(
            ("line_indexVertical", "line_valueVertical"), line_style="dot")

        self.crossPlotVertical.plot(
            ("scatter_indexVertical", "scatter_valueVertical",
             "scatter_colorVertical"),
            type="cmap_scatter",
            name="dot",
            color_mapper=self._cmap(image_value_range),
            marker="circle",
            marker_size=4)

        self.crossPlotVertical.index_range = self.polyplot.index_range.y_range

        # Create a container and add components
        self.container = chaco.HPlotContainer(padding=40,
                                              fill_padding=True,
                                              bgcolor="white",
                                              use_backbuffer=False)

        inner_cont = chaco.VPlotContainer(padding=40, use_backbuffer=True)
        inner_cont.add(self.crossPlotHorizontal)
        inner_cont.add(self.centralContainer)
        self.container.add(self.colorbar)
        self.container.add(inner_cont)
        self.container.add(self.crossPlotVertical)

    def initialiseContourPlot(self):
        """called if this is the first Fit Plot to be drawn """
        xstep = 0.5
        ystep = 0.5
        self.contourXS = scipy.linspace(xstep / 2, self.pixelsX - xstep / 2,
                                        self.pixelsX - 1)
        self.contourYS = scipy.linspace(ystep / 2, self.pixelsY - ystep / 2,
                                        self.pixelsY - 1)
        self.countourZS = chaco.ImageData(data=scipy.array([]), value_depth=1)

        self.lineplot = chaco.ContourLinePlot(
            index=self._image_index,
            value=self.countourZS,
            index_mapper=chaco.GridMapper(
                range=self.polyplot.index_mapper.range),
            levels=self.contourLevels)
        self.centralContainer.add(self.lineplot)
        self.plotData.set_data("fitLine_indexHorizontal", self.xs)
        self.plotData.set_data("fitLine_indexVertical", self.ys)
        self.crossPlotVertical.plot(
            ("fitLine_indexVertical", "fitLine_valueVertical"),
            type="line",
            name="fitVertical")
        self.crossPlotHorizontal.plot(
            ("fitLine_indexHorizontal", "fitLine_valueHorizontal"),
            type="line",
            name="fitHorizontal")
        logger.debug("initialise fit plot %s " % self.crossPlotVertical.plots)

    def addContourPlot(self, contourZS):
        """add a contour plot on top using fitted data and add additional plots to sidebars (TODO) """
        logger.debug("adding fit plot with fit")
        if not self.drawContourBool:
            logger.info("first fit plot so initialising contour plot")
            self.initialiseFitPlot()
        logger.info("attempting to set fit data")
        self.countourZS.data = contourZS
        self.container.invalidate_draw()
        self.container.request_redraw()
        self.drawContourBool = True

    def update(self):
        logger.info("updating plot")
        logger.info("zs = %s", self.zs)
        if self.autoRangeColor:
            self.colorbar.index_mapper.range.low = self.minZ
            self.colorbar.index_mapper.range.high = self.maxZ
        self._image_index.set_data(self.xs, self.ys)
        self._image_value.data = self.zs
        self.plotData.set_data("line_indexHorizontal", self.xs)
        self.plotData.set_data("line_indexVertical", self.ys)
        if self.drawContourBool and self.contourZS is not None:
            self.plotData.set_data("fitLine_indexHorizontal", self.contourXS)
            self.plotData.set_data("fitLine_indexVertical", self.contourYS)
        self.updatePlotLimits()
        #self._image_index.metadata_changed=True
        self.container.invalidate_draw()
        self.container.request_redraw()

    #---------------------------------------------------------------------------
    # Event handlers
    #---------------------------------------------------------------------------

    def _metadata_changed(self, old, new):
        """ This function takes out a cross section from the image data, based
        on the line inspector selections, and updates the line and scatter 
        plots."""

        if self.horizontalAutoRange:
            self.crossPlotHorizontal.value_range.low = self.minZ
            self.crossPlotHorizontal.value_range.high = self.maxZ
        if self.verticalAutoRange:
            self.crossPlotVertical.value_range.low = self.minZ
            self.crossPlotVertical.value_range.high = self.maxZ
        if self._image_index.metadata.has_key("selections"):
            x_ndx, y_ndx = self._image_index.metadata["selections"]
            if y_ndx and x_ndx:
                self.plotData.set_data("line_valueHorizontal",
                                       self._image_value.data[y_ndx, :])
                self.plotData.set_data("line_valueVertical",
                                       self._image_value.data[:, x_ndx])
                xdata, ydata = self._image_index.get_data()
                xdata, ydata = xdata.get_data(), ydata.get_data()
                self.plotData.set_data("scatter_indexHorizontal",
                                       scipy.array([xdata[x_ndx]]))
                self.plotData.set_data("scatter_indexVertical",
                                       scipy.array([ydata[y_ndx]]))
                self.plotData.set_data(
                    "scatter_valueHorizontal",
                    scipy.array([self._image_value.data[y_ndx, x_ndx]]))
                self.plotData.set_data(
                    "scatter_valueVertical",
                    scipy.array([self._image_value.data[y_ndx, x_ndx]]))
                self.plotData.set_data(
                    "scatter_colorHorizontal",
                    scipy.array([self._image_value.data[y_ndx, x_ndx]]))
                self.plotData.set_data(
                    "scatter_colorVertical",
                    scipy.array([self._image_value.data[y_ndx, x_ndx]]))
                if self.drawContourBool:
                    self.plotData.set_data("fitLine_valueHorizontal",
                                           self.countourZS.data[y_ndx, :])
                    self.plotData.set_data("fitLine_valueVertical",
                                           self.countourZS.data[:, x_ndx])
        else:
            self.plotData.set_data("scatter_valueHorizontal", scipy.array([]))
            self.plotData.set_data("scatter_valueVertical", scipy.array([]))
            self.plotData.set_data("line_valueHorizontal", scipy.array([]))
            self.plotData.set_data("line_valueVertical", scipy.array([]))
            self.plotData.set_data("fitLine_valueHorizontal", scipy.array([]))
            self.plotData.set_data("fitLine_valueVertical", scipy.array([]))

    def _colormap_changed(self):
        self._cmap = colormaps.color_map_name_dict[self.colormap]
        if hasattr(self, "polyplot"):
            value_range = self.polyplot.color_mapper.range
            self.polyplot.color_mapper = self._cmap(value_range)
            value_range = self.crossPlotHorizontal.color_mapper.range
            self.crossPlotHorizontal.color_mapper = self._cmap(value_range)
            # FIXME: change when we decide how best to update plots using
            # the shared colormap in plot object
            self.crossPlotHorizontal.plots["dot"][0].color_mapper = self._cmap(
                value_range)
            self.crossPlotVertical.plots["dot"][0].color_mapper = self._cmap(
                value_range)
            self.container.request_redraw()

    def _colorMapRangeLow_changed(self):
        self.colorbar.index_mapper.range.low = self.colorMapRangeLow

    def _colorMapRangeHigh_changed(self):
        self.colorbar.index_mapper.range.high = self.colorMapRangeHigh

    def _horizontalLowerLimit_changed(self):
        self.crossPlotHorizontal.value_range.low = self.horizontalLowerLimit

    def _horizontalUpperLimit_changed(self):
        self.crossPlotHorizontal.value_range.high = self.horizontalUpperLimit

    def _verticalLowerLimit_changed(self):
        self.crossPlotVertical.value_range.low = self.verticalLowerLimit

    def _verticalUpperLimit_changed(self):
        self.crossPlotVertical.value_range.high = self.verticalUpperLimit

    def _autoRange_changed(self):
        if self.autoRange:
            self.colorbar.index_mapper.range.low = self.minz
            self.colorbar.index_mapper.range.high = self.maxz

    def _num_levels_changed(self):
        if self.num_levels > 3:
            self.polyplot.levels = self.num_levels
            self.lineplot.levels = self.num_levels

    def _colorMapRangeLow_default(self):
        logger.debug("setting color map rangle low default")
        return self.minZ

    def _colorMapRangeHigh_default(self):
        return self.maxZ

    def _horizontalLowerLimit_default(self):
        return self.minZ

    def _horizontalUpperLimit_default(self):
        return self.maxZ

    def _verticalLowerLimit_default(self):
        return self.minZ

    def _verticalUpperLimit_default(self):
        return self.maxZ

    def _selectedFit_changed(self, selected):
        logger.debug("selected fit was changed")

    def _fixAspectRatioBool_changed(self):
        if self.fixAspectRatioBool:
            self.centralContainer.aspect_ratio = float(self.pixelsX) / float(
                self.pixelsY)
        else:
            self.centralContainer.aspect_ratio = None
        self.container.request_redraw()
        self.centralContainer.request_redraw()

    def updatePlotLimits(self):
        """just updates the values in the GUI  """
        if self.autoRangeColor:
            self.colorMapRangeLow = self.minZ
            self.colorMapRangeHigh = self.maxZ
        if self.horizontalAutoRange:
            self.horizontalLowerLimit = self.minZ
            self.horizontalUpperLimit = self.maxZ
        if self.verticalAutoRange:
            self.verticalLowerLimit = self.minZ
            self.verticalUpperLimit = self.maxZ

    def _zs_changed(self):
        logger.debug("zs changed")
        self.update()

    def _random_zs(self, low=0.0, high=1.0):
        """generates random zs. Useful for testing """
        self.setData(
            scipy.random.uniform(low, high, (self.pixelsY, self.pixelsX)))

    def _test_fired(self):
        self._random_zs()
class CameraImage(traits.HasTraits):

    #Traits view definitions:
    traits_view = traitsui.View(traitsui.Group(
        traitsui.HGroup(traitsui.Item('pixelsX', label="Pixels X"),
                        traitsui.Item('pixelsY', label="Pixels Y"))),
                                buttons=["OK", "Cancel"])

    pixelsX = traits.CInt(768)
    pixelsY = traits.CInt(512)

    xs = traits.Array
    ys = traits.Array
    zs = traits.Array

    minZ = traits.Float
    maxZ = traits.Float

    scale = traits.Float(10089.33)
    offset = traits.Float(5000.)

    ODCorrectionBool = traits.Bool(
        False,
        desc=
        "if true will correct the image to account for the maximum OD parameter"
    )
    ODSaturationValue = traits.Float(
        3.0, desc="the value of the saturated optical density")

    model_changed = traits.Event

    fitList = traits.List(fits.Fit)  #list of possible fits

    def __init__(self, *args, **kwargs):
        super(CameraImage, self).__init__(*args, **kwargs)
        self.fitList = [
            fits.GaussianFit(endX=self.pixelsX, endY=self.pixelsY),
            fits.RotatedGaussianFit(endX=self.pixelsX, endY=self.pixelsY),
            fits.ParabolaFit(endX=self.pixelsX, endY=self.pixelsY),
            fits.GaussianAndParabolaFit(endX=self.pixelsX, endY=self.pixelsY),
            fits.FermiGasFit(endX=self.pixelsX, endY=self.pixelsY),
            fits.ClippedExponentialIntegral(endX=self.pixelsX,
                                            endY=self.pixelsY)
        ]

    def _xs_default(self):
        return scipy.linspace(0.0, self.pixelsX - 1, self.pixelsX)

    def _ys_default(self):
        return scipy.linspace(0.0, self.pixelsY - 1, self.pixelsY)

    def _zs_default(self):
        return scipy.zeros((self.pixelsY, self.pixelsX))

    def getImageData(self, imageFile):
        logger.debug("pulling image data")
        # The xs and ys used for the image plot range need to be the
        # edges of the cells.
        self.imageFile = imageFile
        self.xs = scipy.linspace(0.0, self.pixelsX - 1, self.pixelsX)
        self.ys = scipy.linspace(0.0, self.pixelsY - 1, self.pixelsY)
        if not os.path.exists(imageFile):
            #if no file is define the image is flat 0s of camera size
            logger.error("image file not found. filling with zeros")
            self.zs = scipy.zeros((self.pixelsX, self.pixelsY))
            print self.zs
            self.minZ = 0.0
            self.maxZ = 1.0
            self.model_changed = True
        else:
            try:
                self.rawImage = scipy.misc.imread(imageFile)
                self.zs = (self.rawImage - self.offset) / self.scale
                if self.ODCorrectionBool:
                    logger.info("Correcting for OD saturation")
                    self.zs = scipy.log(
                        (1.0 - scipy.exp(-self.ODSaturationValue)) /
                        (scipy.exp(-self.zs) -
                         scipy.exp(-self.ODSaturationValue)))
                    #we should account for the fact if ODSaturation value is wrong or there is noise we can get complex numbers!
                    self.zs[scipy.imag(self.zs) > 0] = scipy.nan
                    self.zs = self.zs.astype(float)
                self.minZ = scipy.nanmin(self.zs)
                self.maxZ = scipy.nanmax(self.zs)
                self.model_changed = True
                for fit in self.fitList:
                    fit.xs = self.xs
                    fit.ys = self.ys
                    fit.zs = self.zs

            except Exception as e:
                logger.error("error in setting data %s" % e.message)
                logger.debug(
                    "Sometimes we get an error  unsupported operand type(s) for -: 'instance' and 'float'. "
                )
                logger.debug(
                    "checking for what could cause this . Tell Tim if you see this error message!!!!"
                )
                logger.debug("type(self.rawImage) ->  %s" %
                             type(self.rawImage))
                logger.debug("type(self.offset) ->  %s" % type(self.offset))

    def _scale_changed(self):
        """update zs data when scale or offset changed """
        logger.info("model scale changed")
        self.getImageData(self.imageFile)

    def _offset_changed(self):
        """update zs data when scale or offset changed """
        self.getImageData(self.imageFile)
Esempio n. 7
0
class DataAxis(t.HasTraits):
    name = t.Str()
    units = t.Str()
    scale = t.Float()
    offset = t.Float()
    size = t.CInt()
    low_value = t.Float()
    high_value = t.Float()
    value = t.Range('low_value', 'high_value')
    low_index = t.Int(0)
    high_index = t.Int()
    slice = t.Instance(slice)
    navigate = t.Bool(t.Undefined)
    index = t.Range('low_index', 'high_index')
    axis = t.Array()
    continuous_value = t.Bool(False)

    def __init__(self,
                 size,
                 index_in_array=None,
                 name=t.Undefined,
                 scale=1.,
                 offset=0.,
                 units=t.Undefined,
                 navigate=t.Undefined):
        super(DataAxis, self).__init__()
        self.name = name
        self.units = units
        self.scale = scale
        self.offset = offset
        self.size = size
        self.high_index = self.size - 1
        self.low_index = 0
        self.index = 0
        self.update_axis()
        self.navigate = navigate
        self.axes_manager = None
        self.on_trait_change(self.update_axis, ['scale', 'offset', 'size'])
        self.on_trait_change(self.update_value, 'index')
        self.on_trait_change(self.set_index_from_value, 'value')
        self.on_trait_change(self._update_slice, 'navigate')
        self.on_trait_change(self.update_index_bounds, 'size')
        # The slice must be updated even if the default value did not
        # change to correctly set its value.
        self._update_slice(self.navigate)

    @property
    def index_in_array(self):
        if self.axes_manager is not None:
            return self.axes_manager._axes.index(self)
        else:
            raise AttributeError(
                "This DataAxis does not belong to an AxesManager"
                " and therefore its index_in_array attribute "
                " is not defined")

    @property
    def index_in_axes_manager(self):
        if self.axes_manager is not None:
            return self.axes_manager._get_axes_in_natural_order().\
                   index(self)
        else:
            raise AttributeError(
                "This DataAxis does not belong to an AxesManager"
                " and therefore its index_in_array attribute "
                " is not defined")

    def _get_positive_index(self, index):
        if index < 0:
            index = self.size + index
            if index < 0:
                raise IndexError("index out of bounds")
        return index

    def _get_index(self, value):
        if isinstance(value, float):
            return self.value2index(value)
        else:
            return value

    def _slice_me(self, slice_):
        """Returns a slice to slice the corresponding data axis and 
        change the offset and scale of the DataAxis acordingly.
        
        Parameters
        ----------
        slice_ : {float, int, slice}
        
        Returns
        -------
        my_slice : slice
        
        """
        i2v = self.index2value
        v2i = self.value2index

        if isinstance(slice_, slice):
            start = slice_.start
            stop = slice_.stop
            step = slice_.step
        else:
            if isinstance(slice_, float):
                start = v2i(slice_)
            else:
                start = self._get_positive_index(slice_)
            stop = start + 1
            step = None

        if isinstance(step, float):
            step = int(round(step / self.scale))
        if isinstance(start, float):
            try:
                start = v2i(start)
            except ValueError:
                # The value is below the axis limits
                # we slice from the start.
                start = None
        if isinstance(stop, float):
            try:
                stop = v2i(stop)
            except ValueError:
                # The value is above the axes limits
                # we slice up to the end.
                stop = None

        if step == 0:
            raise ValueError("slice step cannot be zero")

        my_slice = slice(start, stop, step)

        if start is None:
            if step > 0 or step is None:
                start = 0
            else:
                start = self.size - 1
        self.offset = i2v(start)
        if step is not None:
            self.scale *= step

        return my_slice

    def _get_name(self):
        name = (self.name if self.name is not t.Undefined else
                ("Unnamed " + ordinal(self.index_in_axes_manager)))
        return name

    def __repr__(self):
        text = '<%s axis, size: %i' % (
            self._get_name(),
            self.size,
        )
        if self.navigate is True:
            text += ", index: %i" % self.index
        text += ">"
        return text

    def __str__(self):
        return self._get_name() + " axis"

    def connect(self, f, trait='value'):
        self.on_trait_change(f, trait)

    def disconnect(self, f, trait='value'):
        self.on_trait_change(f, trait, remove=True)

    def update_index_bounds(self):
        self.high_index = self.size - 1

    def update_axis(self):
        self.axis = generate_axis(self.offset, self.scale, self.size)
        if len(self.axis) != 0:
            self.low_value, self.high_value = (self.axis.min(),
                                               self.axis.max())

    def _update_slice(self, value):
        if value is False:
            self.slice = slice(None)
        else:
            self.slice = None

    def get_axis_dictionary(self):
        adict = {
            'name': self.name,
            'scale': self.scale,
            'offset': self.offset,
            'size': self.size,
            'units': self.units,
            'index_in_array': self.index_in_array,
            'navigate': self.navigate
        }
        return adict

    def copy(self):
        return DataAxis(**self.get_axis_dictionary())

    def update_value(self):
        self.value = self.axis[self.index]

    def value2index(self, value, rounding=round):
        """Return the closest index to the given value if between the limit.

        Parameters
        ----------
        value : float

        Returns
        -------
        int

        Raises
        ------
        ValueError if value is out of the axis limits.

        """
        if value is None:
            return None
        else:
            index = int(rounding((value - self.offset) / self.scale))
            if self.size > index >= 0:
                return index
            else:
                raise ValueError("The value is out of the axis limits")

    def index2value(self, index):
        return self.axis[index]

    def set_index_from_value(self, value):
        self.index = self.value2index(value)
        # If the value is above the limits we must correct the value
        if self.continuous_value is False:
            self.value = self.index2value(self.index)

    def calibrate(self, value_tuple, index_tuple, modify_calibration=True):
        scale = (value_tuple[1] - value_tuple[0]) /\
        (index_tuple[1] - index_tuple[0])
        offset = value_tuple[0] - scale * index_tuple[0]
        if modify_calibration is True:
            self.offset = offset
            self.scale = scale
        else:
            return offset, scale

    traits_view = \
    tui.View(
        tui.Group(
            tui.Group(
                tui.Item(name='name'),
                tui.Item(name='size', style='readonly'),
                tui.Item(name='index_in_array', style='readonly'),
                tui.Item(name='index'),
                tui.Item(name='value', style='readonly'),
                tui.Item(name='units'),
                tui.Item(name='navigate', label = 'navigate'),
            show_border = True,),
            tui.Group(
                tui.Item(name='scale'),
                tui.Item(name='offset'),
            label = 'Calibration',
            show_border = True,),
        label = "Data Axis properties",
        show_border = True,),
    title = 'Axis configuration',
    )