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

Skip to content

Commit 5e83b93

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.
1 parent fbe562c commit 5e83b93

File tree

6 files changed

+329
-7
lines changed

6 files changed

+329
-7
lines changed

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.

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

Lines changed: 269 additions & 0 deletions
Loading

lib/matplotlib/tests/test_image.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def test_image_interps():
6060
ax3.imshow(X, interpolation='bicubic')
6161
ax3.set_ylabel('bicubic')
6262

63+
6364
@image_comparison(baseline_images=['interp_nearest_vs_none'],
6465
extensions=['pdf', 'svg'], remove_text=True)
6566
def test_interp_nearest_vs_none():
@@ -757,6 +758,22 @@ def test_imshow_endianess():
757758
ax2.imshow(Z.astype('>f8'), **kwargs)
758759

759760

761+
@image_comparison(baseline_images=['imshow_masked_interpolation'],
762+
remove_text=True, style='default')
763+
def test_imshow_masked_interpolation():
764+
a = np.random.random((7, 7))*1000
765+
ndv = -9999.0
766+
a[2:4, 1:5] = ndv
767+
am = np.ma.masked_equal(a, ndv)
768+
769+
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(6, 3))
770+
ax1.imshow(am, interpolation='nearest')
771+
ax1.set_title("interpolation='nearest'")
772+
ax2.imshow(am, interpolation='bilinear')
773+
ax2.set_title("interpolation='bilinear'")
774+
plt.show()
775+
776+
760777
@cleanup
761778
def test_imshow_no_warn_invalid():
762779
with warnings.catch_warnings(record=True) as warns:

0 commit comments

Comments
 (0)