summaryrefslogtreecommitdiff
path: root/silx/gui/plot/items/image.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot/items/image.py')
-rw-r--r--silx/gui/plot/items/image.py72
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()