Plot semi transparent contour plot over image file using matplotlib
Asked Answered
D

2

8

I'd like to plot a transparent contour plot over an image file in matplotlib/pyplot.

Here's what I got so far...

I have a 600x600 pixel square image file test.png that looks like so:

enter image description here

I would like to plot a contour plot over this image (having the image file be 'below' and a semi-transparent version of the contour plot overlaid) using matplotlib and pyplot. As a bonus, the image would be automatically scaled to fit within the current plotting boundaries. My example plotting script is as follows:

from matplotlib import pyplot
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
from matplotlib.colors import BoundaryNorm
from matplotlib.ticker import MaxNLocator
from pylab import *
import numpy as np
import random

# ----------------------------- #

dx, dy = 500.0, 500.0
y, x = np.mgrid[slice(-2500.0, 2500.0 + dy, dy),slice(-2500.0, 2500.0 + dx, dx)]

z = []
for i in x:
    z.append([])
    for j in y:
        z[-1].append(random.uniform(80.0,100.0))

# ----------------------------- #

plot_aspect = 1.2
plot_height = 10.0
plot_width = int(plot_height*plot_aspect)

# ----------------------------- #

pyplot.figure(figsize=(plot_width, plot_height), dpi=100)
pyplot.subplots_adjust(left=0.10, right=1.00, top=0.90, bottom=0.06, hspace=0.30)
subplot1 = pyplot.subplot(111)

# ----------------------------- #

cbar_max = 100.0
cbar_min = 80.0
cbar_step = 1.0
cbar_num_colors = 200
cbar_num_format = "%d"

# ----------

levels = MaxNLocator(nbins=cbar_num_colors).tick_values(cbar_min, cbar_max)
cmap = pyplot.get_cmap('jet')
norm = BoundaryNorm(levels, ncolors=cmap.N, clip=True)
pp = pyplot.contourf(x,y,z,levels=levels,cmap=cmap)
cbar = pyplot.colorbar(pp, orientation='vertical', ticks=np.arange(cbar_min, cbar_max+cbar_step, cbar_step), format=cbar_num_format)
cbar.ax.set_ylabel('Color Scale [unit]', fontsize = 16, weight="bold")

# ----------

CS = pyplot.contour(x,y,z, alpha=0.5)

# ----------

majorLocator1   = MultipleLocator(500)
majorFormatter1 = FormatStrFormatter('%d')
minorLocator1   = MultipleLocator(250)

subplot1.xaxis.set_major_locator(majorLocator1)
subplot1.xaxis.set_major_formatter(majorFormatter1)
subplot1.xaxis.set_minor_locator(minorLocator1)

pyplot.xticks(fontsize = 16)
pyplot.xlim(-2500.0,2500.0)

# ----------

majorLocator2   = MultipleLocator(500)
majorFormatter2 = FormatStrFormatter('%d')
minorLocator2   = MultipleLocator(250)

subplot1.yaxis.set_major_locator(majorLocator2)
subplot1.yaxis.set_major_formatter(majorFormatter2)
subplot1.yaxis.set_minor_locator(minorLocator2)

pyplot.yticks(fontsize = 16)
pyplot.ylim(-2500.0,2500.0)

# ----------

subplot1.xaxis.grid()
subplot1.yaxis.grid()

# ----------

subplot1.axes.set_aspect('equal')

# ----------

pyplot.suptitle('Main Title', fontsize = 24, weight="bold")

# ----------

pyplot.xlabel('X [m]', fontsize=16, weight="bold")
pyplot.ylabel('Y [m]', fontsize=16, weight="bold")

# ----------

implot = subplot1.imshow( pyplot.imread('test.png') , interpolation='nearest', alpha=0.5)

# ----------
pyplot.show()
#pyplot.savefig("tmp.png", dpi=100)
pyplot.close()

...but I'm not getting the result I want... instead I just see the contour plot part. Something like:

enter image description here

What should I do in my code to get what I want?

Daft answered 18/9, 2015 at 12:47 Comment(0)
T
14

You basically need to do two things, set the extent of the image you want in the background. If you dont, the coordinates are assumed to be pixel coordinates, in this case 0 till 600 for both x and y. So adjust you imshow command to:

implot = subplot1.imshow(pyplot.imread(r'test.png'), interpolation='nearest', 
                         alpha=0.5, extent=[-2500.0,2500.0,-2500.0,2500.0])

If you want to stretch the image to the limits of the plot automatically, you can grab the extent with:

extent = subplot1.get_xlim()+ subplot1.get_ylim()

And pass it to imshow as extent=extent.

Since its the background image, setting the alpha to 0.5 makes it very faint, i would set it to 1.0.

Secondly, you set the alpha of the contour lines, but you probably also (or especially) want to set the alpha of the filled contours. And when you use alpha with filled contours, enabling anti-aliasing reduces artifacts. So change your contourf command to:

pp = pyplot.contourf(x,y,z,levels=levels,cmap=cmap, alpha=.5, antialiased=True)

And since you already create the subplot object yourself, i would advice also using it to do the plotting instead of the pyplot interface, which operates on the currently active axes.

So:

subplot1.contourf()
etc

Instead of:

pyplot.contourf()

With the two changes mentioned above, my result looks like:

enter image description here

Tribe answered 18/9, 2015 at 13:11 Comment(0)
G
0

I personally used the multiple contour plot answer for a while with great results. However, I had to output my figures to PostScript, which does not support opacity (alpha option). I found this answer useful since it does not require the use of opacity.

The reason these lines show up is due to the edge color of the faces that make up the contour plot. The linked solution avoids this by changing the edge color to the face color.

cf = plt.contourf(x, y, z, levels=100)

# This is the fix for the white lines between contour levels
for c in cf.collections:
    c.set_edgecolor("face")
Guidon answered 8/2, 2023 at 19:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.