[Matplotlib-users] getting extra colors in figure

Smit, Christine E. (GSFC-610.2)[TELOPHASE CORP] christine.e.smit at nasa.gov
Fri Jul 22 14:24:08 EDT 2016


Hi! I’m trying to plot data using imshow and ending up with more colors
than I specify in the colormap. This is causing problems with a downstream
user who is machine reading images to determine approximate data values
based on colors. I think the issue has something to do with rounding
because many colors in the image are only slightly off from the colors I
specified. My plots have exactly one pixel per data value, so each pixel¹s
color should be exactly a color in the colormap.


Here is an example that replicates the issue:

----------------------------------

import numpy as np;
import matplotlib.pylab as plt
import matplotlib.colors as colors
from PIL import Image
import random


#######
# Create some simple data
#######

shape = (8,12)
npixels = shape[0]*shape[1]
data_min = 0.5
data_max = 11.5
data = np.reshape(np.linspace(data_min,data_max,npixels),shape)

# mask out a quarter of the pixels
mask = np.reshape([False]*npixels,shape)
half_width = int(shape[0]/2)
half_height = int(shape[1]/2)
mask[0:half_width,0:half_height] = True;

ma = np.ma.MaskedArray(data = data,mask = mask)


#######
# Create a color palette with three colors, one 'over' color, and one
'under' color
#######

# define some colors in hex
hex_colors = {
    'red':'#ff0000',
    'orange':"#ffa500",
    'yellow': "#ffff00",
    'green': "#00ff00",
    'blue' : "#0000ff"
}

# Create the colormap
cmap = 
colors.ListedColormap([hex_colors['orange'],hex_colors['yellow'],hex_colors
['green']])
cmap.set_under(color=hex_colors['red'])
cmap.set_over(color=hex_colors['blue'])

# Calculate boundaries between colors so we get some data in every color
thresholds = np.linspace(data_min+1,data_max-1,cmap.N+1)
norm = colors.BoundaryNorm(thresholds, cmap.N)


#######
# Make a plot. The aim here is to have one data point per pixel in the
output file with no
# decorators.
#######

# Step 1: turn off the frame
fig = plt.figure(frameon=False)
# Step 2: set the size of the image in inches to the number of data
points. Note: the size is
# specified width, height.
fig.set_size_inches(shape[1], shape[0])
# Step 3: create Axes
ax = plt.Axes(fig, [0., 0., 1., 1.])
# Step 4: turn off drawing axes
ax.set_axis_off()
# Step 5: add the axes to the figure
fig.add_axes(ax)

# Step 6: plot
ax.imshow(ma,
          # Don't interpolate. There's no need to interpolate because we
have
          # exactly as many data points as pixels.
          interpolation='none',
          # Use our 3-color (+1 above, +1 below) colormap
          cmap=cmap,
          # Use thresholds that will spread the data over all colors
          norm=norm,
          # Match image aspect ratio to axes
          aspect='auto',
          # Put ma[0,0] on the lower left
          origin='lower',
          # Lower left and upper right corners are at the edge of the image
          extent=(0,1,0,1)
         )
# Step 7: Save to file.
fig.savefig('out.png',
            # Set dots per square inch (dpi) to 1. Since there is 1 data
point
            # per inch, I should get an image with 1 data point for each
pixel.
            dpi=1,
            # Set transparent to True so the masked data is transparent.
            transparent=True
           )


#######
# Open the image and see what colors are inside. I expect to see 6 colors:
my 5 listed colors
# and 1 transparent color for the masked data.
#######

# Load the image
im = Image.open("out.png")
# Confirm that the image is the right size. Note: once again, the image
size is width, height
# while the data array is arranged height, width.
print "Image size(should be %d x %d): %d x %d\n" %( shape[1], shape[0],
im.size[0], im.size[1])



# Throw all the pixels, which are (red,green,blue,alpha) tuples, into a
list
pix = im.load()
all_pix = []
width = im.size[0]
for i in range(im.size[0]):
    for j in range(im.size[1]):
        all_pix.append(pix[i,j])

# Get a unique list of pixels
unique = list(set(all_pix))
unique.sort()
print "Unique pixels values in file:\n"
for u in unique:
    print "%.2x %.2x %.2x (alpha - %.2x)" % (u[0],u[1],u[2], u[3])
print "\nNumber of unique colors (should be 6): %d" % len(unique)


----------------------------------






The output I get from this script is:

----------------------------------

Image size(should be 12 x 8): 12 x 8

Unique pixels values in file:

00 00 ff (alpha - ff)
00 fe 01 (alpha - ff)
00 ff 00 (alpha - ff)
01 00 00 (alpha - 01)
01 01 00 (alpha - 01)
02 02 00 (alpha - 02)
fd ff 00 (alpha - ff)
fe ff 00 (alpha - ff)
ff 01 00 (alpha - ff)
ff a5 00 (alpha - ff)
ff a6 00 (alpha - ff)
ff ff 00 (alpha - ff)
ff ff ff (alpha - 00)

Number of unique colors (should be 6): 13

----------------------------------


Note the weird colors that weren¹t in my colorbar. E.g. -

00 fe 01 (alpha - ff) --> close to 00 ff 00 (green)
01 00 00 (alpha - 00) --> close to transparent
ff a6 00 (alpha - ff) --> close to ff a5 00 (orange)

I am running Python 2.7.11 :: Anaconda 2.4.0 (64-bit) and matplotlib
version 1.4.3. I tried matplotlib 1.5 and it had the same issue.

Thanks.
Christine



More information about the Matplotlib-users mailing list