Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1a7c4ef

Browse files
committed
FIX: masked images with interpolation
When determining which pixels to mask in the resampled image, if _any_ contribution to final value comes from a masked pixel, mask the result. Due to Agg special-casing the meaning of the alpha channel, the interpolation for the mask channel needs to be done separately. This is probably a template for doing the over/under separately. print out exact hash on travis
1 parent fbe562c commit 1a7c4ef

7 files changed

Lines changed: 266 additions & 10 deletions

File tree

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ before_install:
7676

7777
install:
7878
- ccache -s
79+
- git describe
7980
# Upgrade pip and setuptools. Mock has issues with the default version of
8081
# setuptools
8182
- |

lib/matplotlib/image.py

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,20 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
371371
# this is to work around spurious warnings coming
372372
# out of masked arrays.
373373
with np.errstate(invalid='ignore'):
374-
rgba[..., 1] = A < 0 # under data
375-
rgba[..., 2] = A > 1 # over data
376-
rgba[..., 3] = ~A.mask # bad data
374+
rgba[..., 1] = np.where(A < 0, np.nan, 1) # under data
375+
rgba[..., 2] = np.where(A > 1, np.nan, 1) # over data
376+
# Have to invert mask, Agg knows what alpha means
377+
# so if you put this in as 0 for 'good' points, they
378+
# all get zeroed out
379+
rgba[..., 3] = 1
380+
if A.mask.shape == A.shape:
381+
# this is the case of a nontrivial mask
382+
mask = np.where(A.mask, np.nan, 1)
383+
else:
384+
# this is the case that the mask is a
385+
# numpy.bool_ of False
386+
mask = A.mask
387+
# ~A.mask # masked data
377388
A = rgba
378389
output = np.zeros((out_height, out_width, 4),
379390
dtype=A.dtype)
@@ -414,12 +425,37 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
414425
# Convert back to a masked greyscale array so
415426
# colormapping works correctly
416427
hid_output = output
428+
# any pixel where the a masked pixel is included
429+
# in the kernel (pulling this down from 1) needs to
430+
# be masked in the output
431+
if len(mask.shape) == 2:
432+
out_mask = np.empty((out_height, out_width),
433+
dtype=mask.dtype)
434+
_image.resample(mask, out_mask, t,
435+
_interpd_[self.get_interpolation()],
436+
True, 1,
437+
self.get_filternorm() or 0.0,
438+
self.get_filterrad() or 0.0)
439+
out_mask = np.isnan(out_mask)
440+
else:
441+
out_mask = mask
442+
# we need to mask both pixels which came in as masked
443+
# and the pixels that Agg is telling us to ignore (relavent
444+
# to non-affine transforms)
445+
# Use half alpha as the threshold for pixels to mask.
446+
out_mask = out_mask | (hid_output[..., 3] < .5)
417447
output = np.ma.masked_array(
418-
hid_output[..., 0], hid_output[..., 3] < 0.5)
419-
# relabel under data
420-
output[hid_output[..., 1] > .5] = -1
448+
hid_output[..., 0],
449+
out_mask)
450+
# 'unshare' the mask array to
451+
# needed to suppress numpy warning
452+
del out_mask
453+
invalid_mask = ~output.mask * ~np.isnan(output.data)
454+
# relabel under data. If any of the input data for
455+
# the pixel has input out of the norm bounds,
456+
output[np.isnan(hid_output[..., 1]) * invalid_mask] = -1
421457
# relabel over data
422-
output[hid_output[..., 2] > .5] = 2
458+
output[np.isnan(hid_output[..., 2]) * invalid_mask] = 2
423459

424460
output = self.to_rgba(output, bytes=True, norm=False)
425461

Binary file not shown.
18.8 KB
Loading

lib/matplotlib/tests/baseline_images/test_image/imshow_masked_interpolation.svg

Lines changed: 185 additions & 0 deletions
Loading
36 Bytes
Loading

lib/matplotlib/tests/test_image.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@
2626
from matplotlib.testing.noseclasses import KnownFailureTest
2727
from copy import copy
2828
from numpy import ma
29+
import matplotlib.image as mimage
2930
import matplotlib.colors as colors
30-
import matplotlib.pyplot as plt
31-
import matplotlib.mlab as mlab
32-
import numpy as np
3331

3432
import nose
3533

@@ -60,6 +58,7 @@ def test_image_interps():
6058
ax3.imshow(X, interpolation='bicubic')
6159
ax3.set_ylabel('bicubic')
6260

61+
6362
@image_comparison(baseline_images=['interp_nearest_vs_none'],
6463
extensions=['pdf', 'svg'], remove_text=True)
6564
def test_interp_nearest_vs_none():
@@ -757,6 +756,41 @@ def test_imshow_endianess():
757756
ax2.imshow(Z.astype('>f8'), **kwargs)
758757

759758

759+
@image_comparison(baseline_images=['imshow_masked_interpolation'],
760+
remove_text=True, style='default')
761+
def test_imshow_masked_interpolation():
762+
763+
cm = copy(plt.get_cmap('viridis'))
764+
cm.set_over('r')
765+
cm.set_under('b')
766+
cm.set_bad('k')
767+
768+
N = 20
769+
n = colors.Normalize(vmin=0, vmax=N*N-1)
770+
771+
# data = np.random.random((N, N))*N*N
772+
data = np.arange(N*N, dtype='float').reshape(N, N)
773+
774+
data[5, 5] = -1
775+
776+
data[15, 5] = 1e5
777+
778+
# data[3, 3] = np.nan
779+
780+
data[15, 15] = np.inf
781+
782+
mask = np.zeros_like(data).astype('bool')
783+
mask[5, 15] = True
784+
785+
data = np.ma.masked_array(data, mask)
786+
787+
fig, ax_grid = plt.subplots(3, 6)
788+
for interp, ax in zip(sorted(mimage._interpd_), ax_grid.ravel()):
789+
ax.set_title(interp)
790+
ax.imshow(data, norm=n, cmap=cm, interpolation=interp)
791+
ax.axis('off')
792+
793+
760794
@cleanup
761795
def test_imshow_no_warn_invalid():
762796
with warnings.catch_warnings(record=True) as warns:

0 commit comments

Comments
 (0)