Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: colormapping of integer normed images #6581

Merged
merged 7 commits into from
Jul 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 16 additions & 11 deletions examples/pylab_examples/image_masked.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
The second subplot illustrates the use of BoundaryNorm to
get a filled contour effect.
"""

from copy import copy
from numpy import ma
import matplotlib.colors as colors
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import numpy as np

# compute some interesting data
delta = 0.025
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Expand All @@ -19,7 +20,8 @@
Z = 10*(Z2 - Z1) # difference of Gaussians

# Set up a colormap:
palette = plt.cm.gray
# use copy so that we do not mutate the global colormap instance
palette = copy(plt.cm.gray)
palette.set_over('r', 1.0)
palette.set_under('g', 1.0)
palette.set_bad('b', 1.0)
Expand All @@ -35,22 +37,25 @@
# range to which the regular palette color scale is applied.
# Anything above that range is colored based on palette.set_over, etc.

plt.subplot(1, 2, 1)
im = plt.imshow(Zm, interpolation='bilinear',
# set up the axes
fig, (ax1, ax2) = plt.subplots(1, 2)

# plot using 'continuous' color map
im = ax1.imshow(Zm, interpolation='bilinear',
cmap=palette,
norm=colors.Normalize(vmin=-1.0, vmax=1.0, clip=False),
origin='lower', extent=[-3, 3, -3, 3])
plt.title('Green=low, Red=high, Blue=bad')
plt.colorbar(im, extend='both', orientation='horizontal', shrink=0.8)
ax1.set_title('Green=low, Red=high, Blue=bad')
fig.colorbar(im, extend='both', orientation='horizontal', shrink=0.8, ax=ax1)

plt.subplot(1, 2, 2)
im = plt.imshow(Zm, interpolation='nearest',
# plot using 'discrete' color map
im = ax2.imshow(Zm, interpolation='nearest',
cmap=palette,
norm=colors.BoundaryNorm([-1, -0.5, -0.2, 0, 0.2, 0.5, 1],
ncolors=256, clip=False),
origin='lower', extent=[-3, 3, -3, 3])
plt.title('With BoundaryNorm')
plt.colorbar(im, extend='both', spacing='proportional',
orientation='horizontal', shrink=0.8)
ax2.set_title('With BoundaryNorm')
fig.colorbar(im, extend='both', spacing='proportional',
orientation='horizontal', shrink=0.8, ax=ax2)

plt.show()
52 changes: 38 additions & 14 deletions lib/matplotlib/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,21 +356,42 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
if not unsampled:
created_rgba_mask = False

if A.ndim not in (2, 3):
raise ValueError("Invalid dimensions, got %s" % (A.shape,))

if A.ndim == 2:
A = self.norm(A)
# If the image is greyscale, convert to RGBA with the
# correct alpha channel for resizing
rgba = np.empty((A.shape[0], A.shape[1], 4), dtype=A.dtype)
rgba[..., 0:3] = np.expand_dims(A, 2)
if A.dtype.kind == 'f':
rgba[..., 3] = ~A.mask
# If the image is greyscale, convert to RGBA and
# use the extra channels for resizing the over,
# under, and bad pixels. This is needed because
# Agg's resampler is very aggressive about
# clipping to [0, 1] and we use out-of-bounds
# values to carry the over/under/bad information
rgba = np.empty((A.shape[0], A.shape[1], 4), dtype=A.dtype)
rgba[..., 0] = A # normalized data
rgba[..., 1] = A < 0 # under data
rgba[..., 2] = A > 1 # over data
rgba[..., 3] = ~A.mask # bad data
A = rgba
output = np.zeros((out_height, out_width, 4),
dtype=A.dtype)
alpha = 1.0
created_rgba_mask = True
else:
rgba[..., 3] = np.where(A.mask, 0, np.iinfo(A.dtype).max)
A = rgba
output = np.zeros((out_height, out_width, 4), dtype=A.dtype)
alpha = 1.0
created_rgba_mask = True
elif A.ndim == 3:
# colormap norms that output integers (ex NoNorm
# and BoundaryNorm) to RGBA space before
# interpolating. This is needed due to the
# Agg resampler only working on floats in the
# range [0, 1] and because interpolating indexes
# into an arbitrary LUT may be problematic.
#
# This falls back to interpolating in RGBA space which
# can produce it's own artifacts of colors not in the map
Copy link
Member

Choose a reason for hiding this comment

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

it's -> its

# showing up in the final image.
A = self.cmap(A, alpha=self.get_alpha(), bytes=True)

if not created_rgba_mask:
# Always convert to RGBA, even if only RGB input
if A.shape[2] == 3:
A = _rgb_to_rgba(A)
Expand All @@ -382,8 +403,6 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
alpha = self.get_alpha()
if alpha is None:
alpha = 1.0
else:
raise ValueError("Invalid dimensions, got %s" % (A.shape,))

_image.resample(
A, output, t, _interpd_[self.get_interpolation()],
Expand All @@ -393,8 +412,13 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
if created_rgba_mask:
# Convert back to a masked greyscale array so
# colormapping works correctly
hid_output = output
output = np.ma.masked_array(
output[..., 0], output[..., 3] < 0.5)
hid_output[..., 0], hid_output[..., 3] < 0.5)
# 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)

Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 40 additions & 2 deletions lib/matplotlib/tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@
from numpy.testing import (
assert_array_equal, assert_array_almost_equal, assert_allclose)
from matplotlib.testing.noseclasses import KnownFailureTest


from copy import copy
from numpy import ma
import matplotlib.colors as colors
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import numpy as np

import nose

Expand Down Expand Up @@ -658,6 +662,40 @@ def test_image_preserve_size2():
np.identity(n, bool)[::-1])


@image_comparison(baseline_images=['mask_image_over_under'],
remove_text=True, extensions=['png'])
def test_mask_image_over_under():
delta = 0.025
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = 10*(Z2 - Z1) # difference of Gaussians

palette = copy(plt.cm.gray)
palette.set_over('r', 1.0)
palette.set_under('g', 1.0)
palette.set_bad('b', 1.0)
Zm = ma.masked_where(Z > 1.2, Z)
fig, (ax1, ax2) = plt.subplots(1, 2)
im = ax1.imshow(Zm, interpolation='bilinear',
cmap=palette,
norm=colors.Normalize(vmin=-1.0, vmax=1.0, clip=False),
origin='lower', extent=[-3, 3, -3, 3])
ax1.set_title('Green=low, Red=high, Blue=bad')
fig.colorbar(im, extend='both', orientation='horizontal',
ax=ax1, aspect=10)

im = ax2.imshow(Zm, interpolation='nearest',
cmap=palette,
norm=colors.BoundaryNorm([-1, -0.5, -0.2, 0, 0.2, 0.5, 1],
ncolors=256, clip=False),
origin='lower', extent=[-3, 3, -3, 3])
ax2.set_title('With BoundaryNorm')
fig.colorbar(im, extend='both', spacing='proportional',
orientation='horizontal', ax=ax2, aspect=10)


@image_comparison(baseline_images=['mask_image'],
remove_text=True)
def test_mask_image():
Expand Down