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

Skip to content

Allow array alpha for imshow #8183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5099,8 +5099,10 @@ def imshow(self, X, cmap=None, norm=None, aspect=None,
luminance data. Note if you pass a `norm` instance, your
settings for `vmin` and `vmax` will be ignored.

alpha : scalar, optional, default: None
The alpha blending value, between 0 (transparent) and 1 (opaque)
alpha : [scalar | array_like], optional, default: None
The alpha blending value, between 0 (transparent) and 1 (opaque).
If `scalar` is an array, the alpha blending values are applied
pixel by pixel, and `scalar` must have the same shape as `X`.

origin : ['upper' | 'lower'], optional, default: None
Place the [0,0] index of the array in the upper left or lower left
Expand Down
19 changes: 17 additions & 2 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,20 @@ 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
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
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
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 and self._array_alpha is not None:
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
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
385 changes: 385 additions & 0 deletions lib/matplotlib/tests/baseline_images/test_image/image_array_alpha.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified lib/matplotlib/tests/baseline_images/test_image/rotate_image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions lib/matplotlib/tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,3 +741,16 @@ def test_empty_imshow():

with pytest.raises(RuntimeError):
im.make_image(fig._cachedRenderer)


@image_comparison(baseline_images=['image_array_alpha'])
def test_image_array_alpha():
'''per-pixel alpha channel test'''
x = np.linspace(0, 1)
xx, yy = np.meshgrid(x, x)

zz = np.exp(- 3 * ((xx - 0.5) ** 2) + (yy -0.7 ** 2))

fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(zz, alpha=zz)