Skip to content
21 changes: 18 additions & 3 deletions lib/matplotlib/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ def __init__(self, ax,
self.axes = ax

self._imcache = None
self._array_alpha = None

self.update(kwargs)

Expand All @@ -260,7 +261,11 @@ def set_alpha(self, alpha):

ACCEPTS: float
"""
martist.Artist.set_alpha(self, alpha)
if np.isscalar(alpha):
martist.Artist.set_alpha(self, alpha)
else:
self._array_alpha = alpha
martist.Artist.set_alpha(self, 1.0)
self._imcache = None

def changed(self):
Expand Down Expand Up @@ -409,6 +414,10 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
if alpha is None:
alpha = 1.0

# Apply the array alpha if provided
if self._array_alpha is not None:
A[..., 3] *= self._array_alpha

_image.resample(
A, output, t, _interpd_[self.get_interpolation()],
self.get_resample(), alpha,
Expand All @@ -419,14 +428,18 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
# colormapping works correctly
hid_output = output
output = np.ma.masked_array(
hid_output[..., 0], hid_output[..., 3] < 0.5)
hid_output[..., 0], hid_output[..., 3] == 0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is going to break a test, but may fix an issue that @QuLogic found with imshow after #8024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this line is indeed causing the problem. In particular, the alpha channel is interpolated near the boundary of an image. The < 0.5 implementation will set pixels that are "more than half transparent" to fully transparent whereas the == 0 implementation only treats them as masked if they are fully transparent.

In short, the boundaries in a few tests differ slightly.

rotate_image-failed-diff

I believe the == 0 implementation is preferable because it effectively provides anti aliasing on the boundary by restoring the alpha channel. Here is a zoomed-in example.

screen shot 2017-03-03 at 15 03 01
screen shot 2017-03-03 at 15 03 06

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, but this causes trouble if there are bad data in image. For example, bad values in one of the tests are shown as blue. If we restore the alpha channel, all bad blue values will become transparent. As a compromise, I have rerendered the problematic images (changes only occur at the boundary) and I only restore the alpha channel if _array_alpha is given.

# relabel under data
output[hid_output[..., 1] > .5] = -1
# relabel over data
output[hid_output[..., 2] > .5] = 2

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

# Restore the alpha channel
if created_rgba_mask:
output[..., 3] = (output[..., 3] * hid_output[..., 3]).astype(output.dtype)

# Apply alpha *after* if the input was greyscale without a mask
if A.ndim == 2 or created_rgba_mask:
alpha = self.get_alpha()
Expand Down Expand Up @@ -495,7 +508,9 @@ def draw(self, renderer, *args, **kwargs):
# actually render the image.
gc = renderer.new_gc()
self._set_gc_clip(gc)
gc.set_alpha(self.get_alpha())
# If the alpha channel is not a sclar, just use full opacity for the artist
alpha = self.get_alpha()
gc.set_alpha(alpha if np.isscalar(alpha) else 1.0)
gc.set_url(self.get_url())
gc.set_gid(self.get_gid())

Expand Down