[Git][debian-gis-team/trollimage][upstream] New upstream version 1.9.0
Antonio Valentino
gitlab at salsa.debian.org
Tue Jul 9 07:47:07 BST 2019
Antonio Valentino pushed to branch upstream at Debian GIS Project / trollimage
Commits:
d801eda1 by Antonio Valentino at 2019-07-09T06:32:32Z
New upstream version 1.9.0
- - - - -
4 changed files:
- CHANGELOG.md
- trollimage/tests/test_image.py
- trollimage/version.py
- trollimage/xrimage.py
Changes:
=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,39 @@
+## Version 1.9.0 (2019/06/18)
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 51](https://github.com/pytroll/trollimage/pull/51) - Fix _FillValue not being respected when converting to alpha image
+
+#### Features added
+
+* [PR 49](https://github.com/pytroll/trollimage/pull/49) - Add a new method for image stacking.
+
+In this release 2 pull requests were closed.
+
+
+## Version 1.8.0 (2019/05/10)
+
+### Issues Closed
+
+* [Issue 45](https://github.com/pytroll/trollimage/issues/45) - img.stretch gives TypeError where img.data is xarray.DataArray and img.data.data is a dask.array
+
+In this release 1 issue was closed.
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 47](https://github.com/pytroll/trollimage/pull/47) - Fix xrimage palettize and colorize delaying internal functions
+
+#### Features added
+
+* [PR 46](https://github.com/pytroll/trollimage/pull/46) - Implement blend method for XRImage class
+
+In this release 2 pull requests were closed.
+
+
## Version 1.7.0 (2019/02/28)
### Issues Closed
=====================================
trollimage/tests/test_image.py
=====================================
@@ -1504,12 +1504,15 @@ class TestXRImage(unittest.TestCase):
# L -> LA (int)
with dask.config.set(scheduler=CustomScheduler(max_computes=1)):
img = xrimage.XRImage((dataset1 * 150).astype(np.uint8))
+ img.data.attrs['_FillValue'] = 0 # set fill value
img = img.convert('LA')
self.assertTrue(np.issubdtype(img.data.dtype, np.integer))
self.assertTrue(img.mode == 'LA')
self.assertTrue(len(img.data.coords['bands']) == 2)
- # make sure the alpha band is all opaque
- np.testing.assert_allclose(img.data.sel(bands='A'), 255)
+ # make sure the alpha band is all opaque except the first pixel
+ alpha = img.data.sel(bands='A').values.ravel()
+ np.testing.assert_allclose(alpha[0], 0)
+ np.testing.assert_allclose(alpha[1:], 255)
# L -> LA (float)
with dask.config.set(scheduler=CustomScheduler(max_computes=1)):
@@ -1778,11 +1781,77 @@ class TestXRImage(unittest.TestCase):
self.assertTupleEqual((1, 5, 15), values.shape)
self.assertTupleEqual((2, 4), bw.colors.shape)
+ def test_stack(self):
+
+ import xarray as xr
+ from trollimage import xrimage
+
+ # background image
+ arr1 = np.zeros((2, 2))
+ data1 = xr.DataArray(arr1, dims=['y', 'x'])
+ bkg = xrimage.XRImage(data1)
+
+ # image to be stacked
+ arr2 = np.full((2, 2), np.nan)
+ arr2[0] = 1
+ data2 = xr.DataArray(arr2, dims=['y', 'x'])
+ img = xrimage.XRImage(data2)
+
+ # expected result
+ arr3 = arr1.copy()
+ arr3[0] = 1
+ data3 = xr.DataArray(arr3, dims=['y', 'x'])
+ res = xrimage.XRImage(data3)
+
+ # stack image over the background
+ bkg.stack(img)
+
+ # check result
+ np.testing.assert_allclose(bkg.data, res.data, rtol=1e-05)
+
def test_merge(self):
pass
def test_blend(self):
- pass
+ import xarray as xr
+ from trollimage import xrimage
+
+ core1 = np.arange(75).reshape(5, 5, 3) / 75.0
+ alpha1 = np.linspace(0, 1, 25).reshape(5, 5, 1)
+ arr1 = np.concatenate([core1, alpha1], 2)
+ data1 = xr.DataArray(arr1, dims=['y', 'x', 'bands'],
+ coords={'bands': ['R', 'G', 'B', 'A']})
+ img1 = xrimage.XRImage(data1)
+
+ core2 = np.arange(75, 0, -1).reshape(5, 5, 3) / 75.0
+ alpha2 = np.linspace(1, 0, 25).reshape(5, 5, 1)
+ arr2 = np.concatenate([core2, alpha2], 2)
+ data2 = xr.DataArray(arr2, dims=['y', 'x', 'bands'],
+ coords={'bands': ['R', 'G', 'B', 'A']})
+ img2 = xrimage.XRImage(data2)
+
+ img3 = img1.blend(img2)
+
+ np.testing.assert_allclose(
+ (alpha1 + alpha2 * (1 - alpha1)).squeeze(),
+ img3.data.sel(bands="A"))
+
+ np.testing.assert_allclose(
+ img3.data.sel(bands="R").values,
+ np.array(
+ [[1., 0.95833635, 0.9136842, 0.8666667, 0.8180645],
+ [0.768815, 0.72, 0.6728228, 0.62857145, 0.5885714],
+ [0.55412847, 0.5264665, 0.50666666, 0.495612, 0.49394494],
+ [0.5020408, 0.52, 0.5476586, 0.5846154, 0.63027024],
+ [0.683871, 0.7445614, 0.81142855, 0.8835443, 0.96]]))
+
+ with self.assertRaises(TypeError):
+ img1.blend("Salekhard")
+
+ wrongimg = xrimage.XRImage(
+ xr.DataArray(np.zeros((0, 0)), dims=("y", "x")))
+ with self.assertRaises(ValueError):
+ img1.blend(wrongimg)
def test_replace_luminance(self):
pass
=====================================
trollimage/version.py
=====================================
@@ -23,9 +23,9 @@ def get_keywords():
# setup.py/versioneer.py will grep for the variable names, so they must
# each be defined on a line of their own. _version.py will just call
# get_keywords().
- git_refnames = " (HEAD -> master, tag: v1.7.0)"
- git_full = "d35a7665ad475ff230e457085523e21f2cd3f454"
- git_date = "2019-02-28 12:49:51 -0600"
+ git_refnames = " (tag: v1.9.0)"
+ git_full = "63fa32f2d40bb65ebc39c4be1fb1baf8f163db98"
+ git_date = "2019-06-18 06:13:40 -0500"
keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
return keywords
=====================================
trollimage/xrimage.py
=====================================
@@ -449,11 +449,15 @@ class XRImage(object):
If ``data`` is an integer type then the alpha band will be scaled
to use the smallest (min) value as fully transparent and the largest
- (max) value as fully opaque. For float types the alpha band spans
- 0 to 1.
+ (max) value as fully opaque. If a `_FillValue` attribute is found for
+ integer type data then it is used to identify null values in the data.
+ Otherwise xarray's `isnull` is used.
+
+ For float types the alpha band spans 0 to 1.
"""
- null_mask = alpha if alpha is not None else self._create_alpha(data)
+ fill_value = data.attrs.get('_FillValue', None) # integer fill value
+ null_mask = alpha if alpha is not None else self._create_alpha(data, fill_value)
# if we are using integer data, then alpha needs to be min-int to max-int
# otherwise for floats we want 0 to 1
if np.issubdtype(data.dtype, np.integer):
@@ -914,6 +918,16 @@ class XRImage(object):
self.data = self.data * scale + offset
self.data.attrs = attrs
+ def stack(self, img):
+ """Stack the provided image on top of the current image.
+ """
+ # TODO: Conversions between different modes with notification
+ # to the user, i.e. proper logging
+ if self.mode != img.mode:
+ raise NotImplementedError("Cannot stack images of different modes.")
+
+ self.data = self.data.where(img.data.isnull(), img.data)
+
def merge(self, img):
"""Use the provided image as background for the current *img* image,
that is if the current image has missing data.
@@ -937,6 +951,13 @@ class XRImage(object):
self.channels[i].mask = np.logical_and(selfmask,
img.channels[i].mask)
+ @staticmethod
+ def _colorize(l_data, colormap):
+ # 'l_data' is (1, rows, cols)
+ # 'channels' will be a list of 3 (RGB) or 4 (RGBA) arrays
+ channels = colormap.colorize(l_data)
+ return np.concatenate(channels, axis=0)
+
def colorize(self, colormap):
"""Colorize the current image using `colormap`.
@@ -955,14 +976,7 @@ class XRImage(object):
alpha = None
l_data = self.data.sel(bands=['L'])
-
- def _colorize(l_data, colormap):
- # 'l_data' is (1, rows, cols)
- # 'channels' will be a list of 3 (RGB) or 4 (RGBA) arrays
- channels = colormap.colorize(l_data)
- return np.concatenate(channels, axis=0)
-
- new_data = l_data.data.map_blocks(_colorize, colormap,
+ new_data = l_data.data.map_blocks(self._colorize, colormap,
chunks=(colormap.colors.shape[1],) + l_data.data.chunks[1:],
dtype=np.float64)
@@ -981,6 +995,12 @@ class XRImage(object):
dims = self.data.dims
self.data = xr.DataArray(new_data, coords=coords, attrs=attrs, dims=dims)
+ @staticmethod
+ def _palettize(data, colormap):
+ """Helper for dask-friendly palettize operation."""
+ # returns data and palette, only need data
+ return colormap.palettize(data)[0]
+
def palettize(self, colormap):
"""Palettize the current image using `colormap`.
@@ -994,12 +1014,7 @@ class XRImage(object):
raise ValueError("Image should be grayscale to colorize")
l_data = self.data.sel(bands=['L'])
-
- def _palettize(data):
- # returns data and palette, only need data
- return colormap.palettize(data)[0]
-
- new_data = l_data.data.map_blocks(_palettize, dtype=l_data.dtype)
+ new_data = l_data.data.map_blocks(self._palettize, colormap, dtype=l_data.dtype)
self.palette = tuple(colormap.colors)
if self.mode == "L":
@@ -1011,22 +1026,61 @@ class XRImage(object):
self.data.data = new_data
self.data.coords['bands'] = list(mode)
- def blend(self, other):
- """Alpha blend *other* on top of the current image."""
- raise NotImplementedError("This method has not be implemented for "
- "xarray support.")
+ def blend(self, src):
+ r"""Alpha blend *src* on top of the current image.
+
+ Perform `alpha blending`_ of *src* on top of the current image.
+ Alpha blending is defined as:
+
+ .. math::
+
+ \begin{cases}
+ \mathrm{out}_A =
+ \mathrm{src}_A + \mathrm{dst}_A (1 - \mathrm{src}_A) \\
+ \mathrm{out}_{RGB} =
+ \bigl(\mathrm{src}_{RGB}\mathrm{src}_A
+ + \mathrm{dst}_{RGB} \mathrm{dst}_A
+ \left(1 - \mathrm{src}_A \right) \bigr)
+ \div \mathrm{out}_A \\
+ \mathrm{out}_A = 0 \Rightarrow \mathrm{out}_{RGB} = 0
+ \end{cases}
- if self.mode != "RGBA" or other.mode != "RGBA":
- raise ValueError("Images must be in RGBA")
- src = other
- dst = self
- outa = src.channels[3] + dst.channels[3] * (1 - src.channels[3])
- for i in range(3):
- dst.channels[i] = (src.channels[i] * src.channels[3] +
- dst.channels[i] * dst.channels[3] *
- (1 - src.channels[3])) / outa
- dst.channels[i][outa == 0] = 0
- dst.channels[3] = outa
+ Both images must have mode ``"RGBA"``.
+
+ Args:
+ src (:class:`XRImage` with mode ``"RGBA"``)
+ Image to be blended on top of current image.
+
+ .. _alpha blending: https://en.wikipedia.org/w/index.php?title=Alpha_compositing&oldid=891033105#Alpha_blending
+
+ Returns
+ XRImage with mode "RGBA", blended as described above
+
+ """
+ # NB: docstring maths copy-pasta from enwiki
+
+ if self.mode != "RGBA":
+ raise ValueError(
+ "Expected self.mode='RGBA', got {md!s}".format(
+ md=self.mode))
+ elif not isinstance(src, XRImage):
+ raise TypeError("Expected XRImage, got {tp!s}".format(
+ tp=type(src)))
+ elif src.mode != "RGBA":
+ raise ValueError("Expected src.mode='RGBA', got {sm!s}".format(
+ sm=src.mode))
+
+ srca = src.data.sel(bands="A")
+ dsta = self.data.sel(bands="A")
+ outa = srca + dsta * (1-srca)
+ bi = {"bands": ["R", "G", "B"]}
+ rgb = ((src.data.loc[bi] * srca
+ + self.data.loc[bi] * dsta * (1-srca))
+ / outa).where(outa != 0, 0)
+ return self.__class__(
+ xr.concat(
+ [rgb, outa.expand_dims("bands")],
+ dim="bands"))
def show(self):
"""Display the image on screen."""
View it on GitLab: https://salsa.debian.org/debian-gis-team/trollimage/commit/d801eda139bd9577b5b0cbda31fee0e9f666f548
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/trollimage/commit/d801eda139bd9577b5b0cbda31fee0e9f666f548
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20190709/10e0b2fb/attachment-0001.html>
More information about the Pkg-grass-devel
mailing list