diff --git a/.travis.yml b/.travis.yml index e2bf8cab0590..77350b892738 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,6 +76,7 @@ before_install: install: - ccache -s + - git describe # Upgrade pip and setuptools. Mock has issues with the default version of # setuptools - | diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index b66809c1d491..f0841fa5ad1c 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -371,9 +371,20 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, # this is to work around spurious warnings coming # out of masked arrays. with np.errstate(invalid='ignore'): - rgba[..., 1] = A < 0 # under data - rgba[..., 2] = A > 1 # over data - rgba[..., 3] = ~A.mask # bad data + rgba[..., 1] = np.where(A < 0, np.nan, 1) # under data + rgba[..., 2] = np.where(A > 1, np.nan, 1) # over data + # Have to invert mask, Agg knows what alpha means + # so if you put this in as 0 for 'good' points, they + # all get zeroed out + rgba[..., 3] = 1 + if A.mask.shape == A.shape: + # this is the case of a nontrivial mask + mask = np.where(A.mask, np.nan, 1) + else: + # this is the case that the mask is a + # numpy.bool_ of False + mask = A.mask + # ~A.mask # masked data A = rgba output = np.zeros((out_height, out_width, 4), dtype=A.dtype) @@ -414,12 +425,37 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, # Convert back to a masked greyscale array so # colormapping works correctly hid_output = output + # any pixel where the a masked pixel is included + # in the kernel (pulling this down from 1) needs to + # be masked in the output + if len(mask.shape) == 2: + out_mask = np.empty((out_height, out_width), + dtype=mask.dtype) + _image.resample(mask, out_mask, t, + _interpd_[self.get_interpolation()], + True, 1, + self.get_filternorm() or 0.0, + self.get_filterrad() or 0.0) + out_mask = np.isnan(out_mask) + else: + out_mask = mask + # we need to mask both pixels which came in as masked + # and the pixels that Agg is telling us to ignore (relavent + # to non-affine transforms) + # Use half alpha as the threshold for pixels to mask. + out_mask = out_mask | (hid_output[..., 3] < .5) output = np.ma.masked_array( - hid_output[..., 0], hid_output[..., 3] < 0.5) - # relabel under data - output[hid_output[..., 1] > .5] = -1 + hid_output[..., 0], + out_mask) + # 'unshare' the mask array to + # needed to suppress numpy warning + del out_mask + invalid_mask = ~output.mask * ~np.isnan(output.data) + # relabel under data. If any of the input data for + # the pixel has input out of the norm bounds, + output[np.isnan(hid_output[..., 1]) * invalid_mask] = -1 # relabel over data - output[hid_output[..., 2] > .5] = 2 + output[np.isnan(hid_output[..., 2]) * invalid_mask] = 2 output = self.to_rgba(output, bytes=True, norm=False) diff --git a/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.pdf b/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.pdf new file mode 100644 index 000000000000..ea5a6c877fda Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.png b/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.png new file mode 100644 index 000000000000..07fba85d3c2d Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.svg b/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.svg new file mode 100644 index 000000000000..af53f58c3ab5 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.svg @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_image/mask_image_over_under.png b/lib/matplotlib/tests/baseline_images/test_image/mask_image_over_under.png index 6433cd355e85..447494239a0f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/mask_image_over_under.png and b/lib/matplotlib/tests/baseline_images/test_image/mask_image_over_under.png differ diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 89540aa2bcac..89ce1643918d 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -26,10 +26,8 @@ from matplotlib.testing.noseclasses import KnownFailureTest from copy import copy from numpy import ma +import matplotlib.image as mimage import matplotlib.colors as colors -import matplotlib.pyplot as plt -import matplotlib.mlab as mlab -import numpy as np import nose @@ -60,6 +58,7 @@ def test_image_interps(): ax3.imshow(X, interpolation='bicubic') ax3.set_ylabel('bicubic') + @image_comparison(baseline_images=['interp_nearest_vs_none'], extensions=['pdf', 'svg'], remove_text=True) def test_interp_nearest_vs_none(): @@ -757,6 +756,41 @@ def test_imshow_endianess(): ax2.imshow(Z.astype('>f8'), **kwargs) +@image_comparison(baseline_images=['imshow_masked_interpolation'], + remove_text=True, style='default') +def test_imshow_masked_interpolation(): + + cm = copy(plt.get_cmap('viridis')) + cm.set_over('r') + cm.set_under('b') + cm.set_bad('k') + + N = 20 + n = colors.Normalize(vmin=0, vmax=N*N-1) + + # data = np.random.random((N, N))*N*N + data = np.arange(N*N, dtype='float').reshape(N, N) + + data[5, 5] = -1 + + data[15, 5] = 1e5 + + # data[3, 3] = np.nan + + data[15, 15] = np.inf + + mask = np.zeros_like(data).astype('bool') + mask[5, 15] = True + + data = np.ma.masked_array(data, mask) + + fig, ax_grid = plt.subplots(3, 6) + for interp, ax in zip(sorted(mimage._interpd_), ax_grid.ravel()): + ax.set_title(interp) + ax.imshow(data, norm=n, cmap=cm, interpolation=interp) + ax.axis('off') + + @cleanup def test_imshow_no_warn_invalid(): with warnings.catch_warnings(record=True) as warns: