diff options
Diffstat (limited to 'silx/gui/plot/items/image.py')
-rw-r--r-- | silx/gui/plot/items/image.py | 72 |
1 files changed, 56 insertions, 16 deletions
diff --git a/silx/gui/plot/items/image.py b/silx/gui/plot/items/image.py index 99a916a..d74f4d3 100644 --- a/silx/gui/plot/items/image.py +++ b/silx/gui/plot/items/image.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017 European Synchrotron Radiation Facility +# Copyright (c) 2017-2019 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -31,11 +31,15 @@ __license__ = "MIT" __date__ = "20/10/2017" -from collections import Sequence +try: + from collections import abc +except ImportError: # Python2 support + import collections as abc import logging import numpy +from ....utils.proxy import docstring from .core import (Item, LabelsMixIn, DraggableMixIn, ColormapMixIn, AlphaMixIn, ItemChangedType) @@ -170,6 +174,12 @@ class ImageBase(Item, LabelsMixIn, DraggableMixIn, AlphaMixIn): else: return xmin, xmax, ymin, ymax + @docstring(DraggableMixIn) + def drag(self, from_, to): + origin = self.getOrigin() + self.setOrigin((origin[0] + to[0] - from_[0], + origin[1] + to[1] - from_[1])) + def getData(self, copy=True): """Returns the image data @@ -199,7 +209,7 @@ class ImageBase(Item, LabelsMixIn, DraggableMixIn, AlphaMixIn): :param origin: (ox, oy) Offset from origin :type origin: float or 2-tuple of float """ - if isinstance(origin, Sequence): + if isinstance(origin, abc.Sequence): origin = float(origin[0]), float(origin[1]) else: # single value origin origin = float(origin), float(origin) @@ -227,7 +237,7 @@ class ImageBase(Item, LabelsMixIn, DraggableMixIn, AlphaMixIn): :param scale: (sx, sy) Scale of the image :type scale: float or 2-tuple of float """ - if isinstance(scale, Sequence): + if isinstance(scale, abc.Sequence): scale = float(scale[0]), float(scale[1]) else: # single value scale scale = float(scale), float(scale) @@ -252,6 +262,7 @@ class ImageData(ImageBase, ColormapMixIn): ColormapMixIn.__init__(self) self._data = numpy.zeros((0, 0), dtype=numpy.float32) self._alternativeImage = None + self.__alpha = None def _addBackendRenderer(self, backend): """Update backend renderer""" @@ -261,8 +272,9 @@ class ImageData(ImageBase, ColormapMixIn): # Do not render with non linear scales return None - if self.getAlternativeImageData(copy=False) is not None: - dataToUse = self.getAlternativeImageData(copy=False) + if (self.getAlternativeImageData(copy=False) is not None or + self.getAlphaData(copy=False) is not None): + dataToUse = self.getRgbaImageData(copy=False) else: dataToUse = self.getData(copy=False) @@ -293,37 +305,56 @@ class ImageData(ImageBase, ColormapMixIn): def getRgbaImageData(self, copy=True): """Get the displayed RGB(A) image - :returns: numpy.ndarray of uint8 of shape (height, width, 4) + :returns: Array of uint8 of shape (height, width, 4) + :rtype: numpy.ndarray """ - if self._alternativeImage is not None: - return _convertImageToRgba32( - self.getAlternativeImageData(copy=False), copy=copy) + alternative = self.getAlternativeImageData(copy=False) + if alternative is not None: + return _convertImageToRgba32(alternative, copy=copy) else: # Apply colormap, in this case an new array is always returned colormap = self.getColormap() image = colormap.applyToData(self.getData(copy=False)) + alphaImage = self.getAlphaData(copy=False) + if alphaImage is not None: + # Apply transparency + image[:, :, 3] = image[:, :, 3] * alphaImage return image def getAlternativeImageData(self, copy=True): """Get the optional RGBA image that is displayed instead of the data - :param copy: True (Default) to get a copy, - False to use internal representation (do not modify!) - :returns: None or numpy.ndarray - :rtype: numpy.ndarray or None + :param bool copy: True (Default) to get a copy, + False to use internal representation (do not modify!) + :rtype: Union[None,numpy.ndarray] """ if self._alternativeImage is None: return None else: return numpy.array(self._alternativeImage, copy=copy) - def setData(self, data, alternative=None, copy=True): + def getAlphaData(self, copy=True): + """Get the optional transparency image applied on the data + + :param bool copy: True (Default) to get a copy, + False to use internal representation (do not modify!) + :rtype: Union[None,numpy.ndarray] + """ + if self.__alpha is None: + return None + else: + return numpy.array(self.__alpha, copy=copy) + + def setData(self, data, alternative=None, alpha=None, copy=True): """"Set the image data and optionally an alternative RGB(A) representation :param numpy.ndarray data: Data array with 2 dimensions (h, w) :param alternative: RGB(A) image to display instead of data, shape: (h, w, 3 or 4) - :type alternative: None or numpy.ndarray + :type alternative: Union[None,numpy.ndarray] + :param alpha: An array of transparency value in [0, 1] to use for + display with shape: (h, w) + :type alpha: Union[None,numpy.ndarray] :param bool copy: True (Default) to get a copy, False to use internal representation (do not modify!) """ @@ -346,6 +377,15 @@ class ImageData(ImageBase, ColormapMixIn): assert alternative.shape[:2] == data.shape[:2] self._alternativeImage = alternative + if alpha is not None: + alpha = numpy.array(alpha, copy=copy) + assert alpha.shape == data.shape + if alpha.dtype.kind != 'f': + alpha = alpha.astype(numpy.float32) + if numpy.any(numpy.logical_or(alpha < 0., alpha > 1.)): + alpha = numpy.clip(alpha, 0., 1.) + self.__alpha = alpha + # TODO hackish data range implementation if self.isVisible(): plot = self.getPlot() |