How do I put a watermark behind plotted data using matplotlib - python

I found this tutorial on how to do a watermark but I cannot figure out how to put it behind my plotted data.
https://www.tutorialspoint.com/how-to-plot-a-watermark-image-in-matplotlib
Changing zorder has no impact because I think it is being drawn on the entire figure. I would like to have a subdued logo behind my data which is always centered in the figure so I don't really want to plot an image as a data point because then it would move as it is panned/zoomed.

Setting the zorder to a negative value works for me. However, you also need to make the facecolor of the axes transparent:
import numpy as np
import matplotlib.cbook as cbook
import matplotlib.image as image
import matplotlib.pyplot as plt
with cbook.get_sample_data('logo2.png') as file:
im = image.imread(file)
fig, ax = plt.subplots()
fig.figimage(im, 10, 10, zorder=-1, alpha=.5)
ax.plot(np.sin(10 * np.linspace(0, 1)), '-o', ms=20,
alpha=0.7, mfc='orange')
ax.set_facecolor('none')
plt.show()

Related

How can I use SymLogNorm with matplotlib but still have colorbar appear linear?

Is there a way to use SymLogNorm with imshow, but make the colorbar basically stretch the colors so that the colorbar actually appears linear?
Below is a short code
from pylab import *
import numpy as np
from matplotlib.colors import SymLogNorm
data = np.random.uniform(low=-10, high=10, size=(10,10))
norm = SymLogNorm(2,vmin=-10,vmax=10)
fig, axes = plt.subplots()
im = axes.imshow(data,extent=[-10,10,-10,10],cmap=plt.cm.jet,norm=norm)
cb = fig.colorbar(im)
that produces this
I basically want this image, but want to stretch the colorbar so the ticks appear linear, not log.

Matplotlib - Aligning grids

I'm plotting histograms below the images using a Matplotlib.GridSpec as we can see on code below:
import imageio
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
plt.close('all')
plt.style.use('ggplot')
img = imageio.imread('imageio:page.png')
y = np.bincount(img.ravel(), minlength=256)
y = y/np.sum(y)
x = np.arange(len(y))
fig = plt.figure(figsize=(6,8))
gs = gridspec.GridSpec(2, 1, height_ratios=[6,1], width_ratios=[1])
ax0 = plt.subplot(gs[0])
ax0.imshow(img, cmap='gray')
ax0.xaxis.set_visible(False)
ax0.yaxis.set_visible(False)
ax1 = plt.subplot(gs[1])
ax1.fill_between(x, y)
ax1.yaxis.set_visible(False)
ax1.set_xlim([0,255])
fig.tight_layout()
plt.show()
When we pick the correct figure size the image is nicely aligned as in
But if the figure size isn't correctly chosen the histogram is shown too large for image size or too far away as we can see below
or
Is there any way to tell matplotlib to align correctly, that is, put the histogram a fixed amount of pixels below the image and never stretch the histogram larger than image width.

How do I add a colorbar with log data in an image/matrix

I have a 2D matrix I want to plot. The plotting itself works, but I need
a colorbar with it. The figure only makes sense when the data is
log-tranformed. And I want the colorbar show the original values. How
do I do this?
A search provided
A logarithmic colorbar in matplotlib scatter plot
but I cannot make this work.
The code below gives an idea of what I attempt to do. Only the revevant
lines are included (as far as I could see).
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
my_speed=np.ones(shape=(no,no))
fig=plt.figure(2)
ax=fig.add_subplot(1,1,1)
my_speed=np.log10(my_speed)
ax.imshow(my_speed, interpolation='bilinear', cmap=cm.jet)
plt.colorbar() #this does not work
plt.savefig('myspeedplot.png')
plt.close(2)
Thank you for any help
The idea is not to transform your data, but let the visualization do the trick for you.
pylot.imshow[1] has an optional parameter norm that can do the log transformation for you.
my_speed=np.ones(shape=(no,no))
fig = plt.figure(2)
ax = fig.add_subplot(1,1,1)
# my_speed=np.log10(my_speed)
img = ax.imshow(my_speed, interpolation='bilinear', cmap=cm.jet,
norm=mpl.colors.LogNorm())
fig.colorbar(img)
As far as I see, there are two problems with your code.
First, you are trying to have the ticks on colorbar show original values. For this you should not transform the data, but just normalize the plot.
And second, you are using the ax.imshow and this is why the colorbar does not see it. You should use plt.imshow or use im=ax.imshow and then colorbar(im)
Here is a working solution:
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
my_speed = np.random.rand(20, 20)
fig = plt.figure(2)
ax = fig.add_subplot(1, 1, 1)
im = ax.imshow(my_speed, interpolation='bilinear',
norm=mpl.colors.LogNorm(),
cmap=plt.cm.jet)
cb = plt.colorbar(im, orientation='vertical')
plt.show()

Scale image in matplotlib without changing the axis

I have a GUI that displays a plot. I want to fit that plot to an existing image. I displayed the image under the plot using:
myaxe.plot(...)
myaxeimage = myaxe.imshow(myimage, axpect='auto', extent=myaxe.axis(), zorder=-1)
I'm already able to play with the opacity of the image, using
myaxeimage.set_alpha()
Now I'd like to be able to zoom in and out and to move around the image, using the GUI, without touching to the existing plot and axes, in order to align it with my plot. In other words, I want to scale to given sx and sy factors, and to put origin of the image at a given (x,y) point, clipping parts of the image going outside the axes. How can I do that?
There is a watermark example distributed with matplotlib that is sort of similar. Starting from that code, we can modify as follows:
Use ax.imshow to plot the image first. I do this because the extent parameter affects the final extent of ax. Since we want the final extent to be governed by the plt.plot(...), let's put it last.
myaximage = ax.imshow(im, aspect='auto', extent=(1,15,0.3,0.7), alpha=0.5, origin='upper', zorder=-1)
Instead of extent=myaxe.axis(), use extent to control the position and size of the image. extent=(1,15,0.3,0.7) places the image in the rectangle with (1, 0.3) as the bottom left corner and (15, 0.7) as the top right corner.
With origin='upper', the [0,0] index of the array im is placed at the upper left corner of the extent. With origin='lower' it would have been placed at the lower left corner.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
import matplotlib.image as image
np.random.seed(1)
datafile = cbook.get_sample_data('logo2.png', asfileobj=False)
im = image.imread(datafile)
fig, ax= plt.subplots()
myaximage = ax.imshow(im, aspect='auto', extent=(1,15,0.3,0.7), alpha=0.5, zorder=-1)
ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=1.0, mfc='orange')
ax.grid()
plt.show()
If you want to expand the image and clip it to the extent of the plot, you might need to use ax.set_xlim and ax.set_ylim as well:
myaximage = ax.imshow(im, aspect='auto', extent=(-1,25,0.3,0.7), alpha=0.5, zorder=-1,
origin='upper')
ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=1.0, mfc='orange')
ax.set_xlim(0,20)
ax.set_ylim(0,1)
Or, for more control, you can clip the image to an arbitrary path by using myaximage.set_clip_path:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
import matplotlib.image as image
import matplotlib.patches as patches
np.random.seed(1)
datafile = cbook.get_sample_data('logo2.png', asfileobj=False)
im = image.imread(datafile)
fig, ax= plt.subplots()
myaximage = ax.imshow(im, aspect='auto', extent=(-5,25,0.3,0.7),
alpha=0.5, origin='upper',
zorder=-2)
# patch = patches.Circle((300,300), radius=100)
patch = patches.Polygon([[5, 0.4], [15, 0.4], [15, 0.6], [5, 0.6]], closed=True,
transform=ax.transData)
myaximage.set_clip_path(patch)
ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=1.0, mfc='orange',
zorder=-1)
ax.set_xlim(0, 20)
ax.set_ylim(0, 1)
plt.show()
Finally, I followed tcaswell suggestion and used 2 different axes. This way, I simply have to play with set_xlim() and set_ylim() of my image axes to change the origin and/or the zooming factor of my image. I order to get the image below my plot, without hiding it with the frame of the plot, I removed the frame of the plot and used the frame of the image axes instead. I also hidden the ticks from the image axes.
from matplotlib import pyplot
f = pyplot.figure()
a = f.add_subplot(111, frameon=False) # Remove frame
a.plot(...)
myimg = pyplot.imread(...)
imgaxes = f.add_axes(a.get_position(), # new axes with same position
label='image', # label to ensure imgaxes is different from a
zorder=-1, # put image below the plot
xticks=[], yticks=[]) # remove the ticks
img = imgaxes.imshow(myimg, aspect='auto') # ensure image takes all the place
# now, to modify things
img.set_alpha(...)
imgaxes.set_xlim((x1, x2)) # x1 and x2 must be calculated from
# image size, origin, and zoom factor

how to get matplotlib physical (not data) scale

I have this simple code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
from matplotlib.patches import Ellipse
PlotFileName="test.pdf"
pdf = PdfPages(PlotFileName)
fig=plt.figure(1)
ax1=fig.add_subplot(111)
plt.xlim([0,10])
plt.ylim([0,10])
ax1.plot([0,10],[0,10])
e=0.0
theta=0
maj_ax=2
min_ax=maj_ax*np.sqrt(1-e**2)
const=1
ax1.add_artist(Ellipse((5, 5), maj_ax, const*min_ax, angle=theta, facecolor="green", edgecolor="black",zorder=2, alpha=0.5))
plt.grid()
pdf.savefig(fig)
pdf.close()
plt.close()
Here is how it looks:
As you see from the code, it should be a circle, but it isn't! I have narrowed the problem down to the const term in line 16. I don't want to use ax1.axis("equal") because my data don't have the same scales on the vertical and horizontal. Could any one tell me how I can ask matplotlib to tell me what aspect ratio it is using so I can set the const term correctly so I have a circle in the end?
In other words I want to know the ratio of the horizontal to the vertical axis "physical" length (for example, what is printed out).
I would really appreciate any suggestions, thanks in advance
One option is to explicitly define the figure size... you may also need to specify the subplot parameters if you are using non-default settings. Adjust figsize and subplot parameters as needed for non-equal horizontal and vertical scales. For example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
fig = plt.figure(figsize=(6,4))
fig.subplots_adjust(left=0.1, right=0.9, bottom=0.1, top=0.9)
ax1 = fig.add_subplot(111, xlim=(-2.5,12.5), ylim=(0,10))
ax1.plot((0,10), (0,10))
maj_ax, e, theta = 2, 0, 0
min_ax = maj_ax * np.sqrt(1 - e**2)
ax1.add_artist(Ellipse((5, 5), maj_ax, min_ax, angle=theta,
fc="green", ec="black", zorder=2, alpha=0.5))
plt.grid()
plt.show()

Categories

Resources