why does my colorbar have lines in it?
Asked Answered
P

7

52

Edit: Since this seems to be a popular post, here's the solution that seems to be working well for me. Thanks @gazzar and @mfra.

cbar.solids.set_rasterized(True)
cbar.solids.set_edgecolor("face")

Does anyone know why my colorbar has what appear to be lines in it? Or rather why is the color transition not smooth? I'm using basemap, obviously, but that shouldn't matter since it's all matplotlib calls under the hood AFAICT. I create the map doing something like

grays = plt.cm.get_cmap("Grays")
sc = mymap.scatter(xpoints, ypoints, s=sizes, c=color_values, cmap=grays, alpha=.75,
                   marker="o", zorder=10, vmin=0, vmax=1)
cbar = mymap.colorbar(sc, drawedges=True, location="bottom")

I tried without and without alpha and the result was the same. Maybe it is because my color_values array is not fine enough? Can I set the underlying values that are mapped to the colorbar somewhere? I don't see how, and I don't see this problem elsewhere. Ie., I can replicate the matplotlib show_colorbars example without this problem.

Bad Colorbar Example

Phosgene answered 21/2, 2013 at 13:22 Comment(6)
Have you tried to save the figure in different formats? If yes, do you always get this issues?Pelagias
I have only seen this as a result of the alpha, are you really sure its the same with alpha=1.0?Keepsake
Yes, it happens with png, but I didn't troubleshoot this. Yes, I saw it for alpha = 1.0 with SVGs.Phosgene
Hmm.. none of the options listed below seems to fix the problem for me. My colorbar still has the white lines when I savefig as PDF.Istanbul
The two lines given as solution in the latest edit should not be used together. Either one or the other is enough.Flocculate
Here is a proposed workaround with calculated mimicked alpha values: https://mcmap.net/q/261564/-add-alpha-to-an-existing-colormapCadge
E
33

In case you create vector graphics, have you tried this (taken from http://matplotlib.org/api/pyplot_api.html?highlight=colorbar#matplotlib.pyplot.colorbar):

"It is known that some vector graphics viewer (svg and pdf) renders white gaps between segments of the colorbar. This is due to bugs in the viewers not matplotlib. As a workaround the colorbar can be rendered with overlapping segments:

cbar = colorbar()
cbar.solids.set_edgecolor("face")
draw()

However this has negative consequences in other circumstances. Particularly with semi transparent images (alpha < 1) and colorbar extensions and is not enabled by default see (issue #1188)."

Electrodialysis answered 22/2, 2013 at 10:6 Comment(3)
Thanks. This does indeed seem to be the issue reading through #1188 and the (reverted) PR attached. Interestingly as well, the SVGs do render differently in Firefox and Chrome, which I hadn't thought of.Phosgene
It looks like the best results for alpha < 1 come from using drawedges=False and set_edgecolor("face"). I have black lines now, but they're less pronounced and only in the darker colors.Phosgene
It is indeed a bug in the viewer(s), but it's also true that a suboptimal way is used to render the colorbar. The whole figure should be drawn as a single piece using smooth shading rather than 256 small rectangles. Both PDF and SVG formats support linear smooth shading with an arbitrary number of colours.Sixtasixteen
C
11

It looks like your plot uses some transparent (alpha) values. I was having this same problem (semi-transparent plot, but wanted the color bar to be solid), and this question and answer fixed it for me! For reference:

cbar.set_alpha(1)
cbar.draw_all()
Corridor answered 26/3, 2013 at 3:10 Comment(1)
This changed my colormap in the colorbar! Why?? What the hell :DDicho
S
10

I tried both settings given on the other comments, namely

cbar.solids.set_rasterized(True)
cbar.solids.set_edgecolor("face")

and unfortunately neither worked for me.

So I tried a completely different approach which is a huge hack, but at least gets the job done. When you pass alpha to imshow it sets an alpha value that will be used to blending with other images. Due to reasons of higher power (as @mfra has mentioned) this creates the white lines we so despise. Clearly the key then is to not pass an alpha value to imshow. However, we still need to somehow create the colorbar.

Thus as a workaround we can do the alpha blending ourselves before ever giving colormaps to imshow. As an example, let's use the winter colormap:

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

xx, yy = np.meshgrid(np.linspace(-1, 1, 100), np.linspace(-1, 1, 100))
zz = xx**2 + yy**2

my_cmap_rgb = plt.get_cmap('winter')(np.arange(256))
alpha = 0.5

for i in range(3): # Do not include the last column!
    my_cmap_rgb[:,i] = (1 - alpha) + alpha*my_cmap_rgb[:,i]
my_cmap = mpl.colors.ListedColormap(my_cmap_rgb, name='my_cmap')

Here I create a new colormap, my_cmap_rgb, based on winter and then edit the non-alpha channels (0, 1 and 2) to do the alpha blending myself. I can then simply use this new colormap to plot my figure.

f, ax = plt.subplots()
cim = ax.imshow(zz, cmap=my_cmap)
cbar = plt.colorbar(cim)
ax.set_title("No lines and no transparency")

No lines and no transparency Now compare it with the image you'd obtain without this trick:

f, ax = plt.subplots()
cim = ax.imshow(zz, cmap='winter', alpha=0.5)
cbar = plt.colorbar(cim)
ax.set_title("Lines and transparency")

Lines and transparency

Clearly the problem is solved if you require no transparency. On the other hand, if you do require transparency, there is one more workaround. You have to first plot an image without transparency to obtain the colorbar, and then plot one with transparency, but whose colorbar won't be used.

f, ax = plt.subplots()
cim = ax.imshow(zz, cmap=my_cmap)
cbar = plt.colorbar(cim)
plt.cla()  # Clears axis
ax.plot(50,50, 'ko')
ax.imshow(zz, cmap='winter', alpha=0.5)
ax.set_title("No lines and transparency")

This results in an image whose colorbar has no lines and yet still retains the transparency. While it remains a massive hack, at least we don't have to put up with these lines anymore!

No lines and transparency

Semang answered 27/2, 2016 at 16:25 Comment(1)
I applied the last trick (plot without alpha, plot colorbar, plot with alpha), since I had to images to run through imshow, with the second transparently overlaid on the first, and the second having the colorbar. Thus running into the transparenty - stripes issue.Chery
P
8

I generally prefer to rasterize the colorbar contents to avoid this issue using Eric Firing's advice here by adding the following line.

cbar.solids.set_rasterized(True) 

This workaround supposedly fails for images with transparency, but for most normal cases it produces the desired result.

Pegeen answered 15/8, 2014 at 15:17 Comment(2)
Circling back around to this two years later. This does indeed help quite a bit.Phosgene
Excellent. Looks better, and smaller svg output as well!Tolbutamide
P
1

Try:

cbar = mymap.colorbar(sc, drawedges=False, location="bottom")

It is now well documented on the web (that I could find), but

*drawedges*   [ False | True ] If true, draw lines at color
              boundaries.

(pulled from matplotlib/lib/matplotlib/colorbar.py) I think by setting drawedges to True you are telling it to draw those lines.

Pooley answered 21/2, 2013 at 15:32 Comment(1)
drawedges does indeed draw the edges for me, but draws them in black! Not particularly helpful. mfra's solution works without drawedges for me. (matplotlib v1.3.1)Anchorage
D
1

Since none of the other suggestions worked for me I ended up removing the alpha channel from the colorbar instance:

from matplotlib.colors import to_rgb

lut = colorbar.solids.get_facecolor()
bg_color = to_rgb('white')
lut[:, :3] *= lut[:, 3:]
lut[:, :3] += (1 - lut[:, 3:]) * bg_color
lut[:, 3] = 1.
colorbar.solids.set_facecolor(lut)

The colorbar had to be drawn once before being able to access the face colors.

Downatheel answered 29/1, 2018 at 16:45 Comment(0)
D
0

The following can be another solution even if it is not elegant.

In [1]: children = cbar.ax.get_children()
In [2]: children
Out[2]:
[<matplotlib.collections.QuadMesh at 0x21783c41b70>,
 <matplotlib.collections.LineCollection at 0x21783bfdc18>,
 <matplotlib.patches.Polygon at 0x21783ba0588>,
 <matplotlib.patches.Polygon at 0x21783beef98>,
 <matplotlib.spines.Spine at 0x21783b77c88>,
 <matplotlib.spines.Spine at 0x21783b77470>,
 <matplotlib.spines.Spine at 0x21783b70c88>,
 <matplotlib.spines.Spine at 0x21783b70860>,
 <matplotlib.axis.XAxis at 0x21783b6ac50>,
 <matplotlib.axis.YAxis at 0x21783ba60f0>,
 <matplotlib.text.Text at 0x21783bc2198>,
 <matplotlib.text.Text at 0x21783bc2320>,
 <matplotlib.text.Text at 0x21783bc22b0>,
 <matplotlib.patches.Rectangle at 0x21783bc2358>]
In [3]: obj = children[1]  # Get the LineCollection object
In [4]: obj.set_linewidth(0)
Desta answered 11/11, 2017 at 9:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.