I want to plot an image with pyplot and on top of that image a point.
That point is from an input field in the pyplot. Here I have a piece of code, where you can put a point in, but after pressing enter, or search button it won't plot the point. Here is my code:
import cv2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox
def imshow_rgb(img_bgr):
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
ims = cv2.imread('plattegrondtekening.png', 1)
fig = plt.imshow(np.flipud(ims), cmap='gray', origin='lower')
plt.subplots_adjust(bottom=0.2)
initial_text = ""
x,y=[500,500]
def submit(text):
x,y = list(map(int,text.split(",")))
print(x,y)
plt.plot(x, y, "ro")
plt.show()
axbox = plt.axes([0.1, 0.05, 0.8, 0.075])
text_box = TextBox(axbox, 'search', initial=initial_text)
text_box.on_submit(submit)
plt.show()
image plot with input field below, this is the output of the code above
But I want that it shows a point on x=900 and y=800, when I enter 900,800 in the input box.
We have to select the active axes first using plt.sca(ax) and for refreshing the canvas we may use fig.canvas.draw() and fig.canvas.flush_events().
Replace fig = plt.imshow(np.flipud(ims), cmap='gray', origin='lower') with:
fig = plt.figure() # Keep the figure for later usage.
ax = plt.gca() # Keep the axes for later usage.
ax.imshow(np.flipud(ims), cmap='gray', origin='lower') # Show the image on axes ax
Replace plt.plot(x, y, "ro") and plt.show() with:
plt.sca(ax) # Set active axes
plt.plot(x, y, "ro")
fig.canvas.draw() # Refresh the canvas.
fig.canvas.flush_events()
Code sample:
import cv2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox
ims = cv2.imread('plattegrondtekening.png', 1)
fig = plt.figure() # Keep fig for later usage
ax = plt.gca() # https://stackoverflow.com/questions/25505341/how-to-get-the-axesimages-from-matplotlib
ax.imshow(np.flipud(ims), cmap='gray', origin='lower')
plt.subplots_adjust(bottom=0.2)
initial_text = ""
x,y=[500,500]
def submit(text):
x, y = list(map(int,text.split(",")))
print(x,y)
plt.sca(ax) # https://stackoverflow.com/questions/19625563/matplotlib-change-the-current-axis-instance-i-e-gca
plt.plot(x, y, "ro")
fig.canvas.draw() # https://stackoverflow.com/questions/4098131/how-to-update-a-plot-in-matplotlib
fig.canvas.flush_events()
axbox = plt.axes([0.1, 0.05, 0.8, 0.075])
text_box = TextBox(axbox, 'search', initial=initial_text)
text_box.on_submit(submit)
plt.show()
Related
I want to save an instance of a plot into an object so that I can display it later by just calling that object.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y1 = x
y2 = np.sin(x)
plt.plot(x, y1, linewidth=1, color = 'deepskyblue')
fig1 = plt.gcf()
plt.plot(x, y2, linewidth=1, color = 'red')
fig2 = plt.gcf()
In this example, I first draw a blue line (y1=x) and use plt.gcf() to save an instance of this plot in fig1. Then I add a red curve (y2=sin(x)) to the plot and use plt.gcf() again to save this plot in fig2. Now, I expect that when I call fig1 I only get the blue line, and when I call fig2 I get both lines. Like this (I'm in Jupyter):
fig1 # or fig1.show() if not in Jupyter
Only blue curve
fig2
Both curves
But, in reality, when I call fig1 and fig2, both of them show both curves (like the second picture). Can someone please help how I can correctly get an instance of each plot so that I can display each of them later whenever I want?
You need to force matplotlib to actually draw the figure by setting a plot.show() in your code:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y1 = x
y2 = np.sin(x)
plt.plot(x, y1, linewidth=1, color = 'deepskyblue')
plt.show()
fig1 = plt.gcf()
plt.plot(x, y2, linewidth=1, color = 'red')
plt.show()
fig2 = plt.gcf()
Using the function plt.plot() always plots to the current axis (if no axis is present, a new one is created).
You can also tell matplotlib explicitly to open a new figure:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y1 = x
y2 = np.sin(x)
# open first figure
fig1 = plt.figure()
# plot
plt.plot(x, y1, linewidth=1, color = 'deepskyblue')
# open second figure
fig2 = plt.figure()
#plot
plt.plot(x, y2, linewidth=1, color = 'red')
Although this already fixes your problem, it is considered good practice to use an object-oriented version of this like this:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y1 = x
y2 = np.sin(x)
# open first figure, axis
fig1, ax1 = plt.subplots()
# plot
ax1.plot(x, y1, linewidth=1, color = 'deepskyblue')
# open second figure, axis
fig2, ax2 = plt.subplots()
#plot
ax2.plot(x, y2, linewidth=1, color = 'red')
In all cases, you will get:
Now, why don't you need plt.show() in the other approaches? Well, matplotlib by explicitly opening a new figure/axis, it is obvious that the previous axis is finished and can be drawn.
The last approach is the clearest as you tell exactly which figure and which axis you are considering.
I'm plotting an image with their two projections (x and y) in a GridSpec. When I use the set_aspect on the central image, the image size box isn't resized for its minimal size (without blank) as you can see below. Does somebody have a solution to resolve this case?
Matplotlib 3.0.2, Python 3.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
plt.rcParams['toolbar'] = 'toolmanager'
matplotlib.use('Qt5Agg')
ldata = np.random.random((256, 256))
xhisto = np.sum(ldata, axis=0)
yhisto = np.sum(ldata, axis=1)
fig = plt.figure()
gs = plt.GridSpec(2, 2, height_ratios=[10,1], width_ratios=[3,30], wspace=0.1, hspace=0.1)
ax_image = plt.subplot(gs[1])
ax_histoy = plt.subplot(gs[0], sharey=ax_image)
ax_histox = plt.subplot(gs[3], sharex=ax_image)
plt.subplots_adjust(right=0.8)
colorAx = plt.axes([0.85, 0.4, 0.02, 0.45])
im = ax_image.imshow(ldata, cmap='jet', interpolation='none', aspect='auto')
ax_histox.plot(xhisto)
ax_histoy.plot(yhisto, range(256))
ax_image.invert_yaxis()
ax_image.tick_params(labelbottom=False, labelleft=False)
ax_histoy.spines['right'].set_visible(False)
ax_histoy.spines['bottom'].set_visible(False)
ax_histox.spines['right'].set_visible(False)
ax_histox.spines['top'].set_visible(False)
ax_histoy.set_ylim(1,256)
ax_histox.set_xlim(1,256)
ax_histox.set_xlabel('X')
ax_histoy.set_ylabel('Y')
ax_image.set_title('Matplotlib - Plot 2D')
ax_histoy.tick_params(axis='x',labelsize=8,labelrotation=90)
ax_histox.tick_params(axis='y',labelsize=8)
ax_histoy.xaxis.tick_top()
ax_histox.yaxis.tick_left()
plt.colorbar(im, cax = colorAx)
ax_image.set_aspect(0.5)
plt.show()
I try to find a solution for resizing the height of the projection on the left
Using the example as you explain give the save result with a ratio different as 1:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
# Fixing random state for reproducibility
np.random.seed(19680801)
# the random data
x = np.random.randn(1000)
y = np.random.randn(1000)
fig, axScatter = plt.subplots(figsize=(5.5, 5.5))
# the scatter plot:
axScatter.scatter(x, y)
axScatter.set_aspect(0.3)
# create new axes on the right and on the top of the current axes
# The first argument of the new_vertical(new_horizontal) method is
# the height (width) of the axes to be created in inches.
divider = make_axes_locatable(axScatter)
axHistx = divider.append_axes("top", 1.2, pad=0.1, sharex=axScatter)
axHisty = divider.append_axes("right", 1.2, pad=0.1, sharey=axScatter)
# make some labels invisible
axHistx.xaxis.set_tick_params(labelbottom=False)
axHisty.yaxis.set_tick_params(labelleft=False)
# now determine nice limits by hand:
binwidth = 0.25
xymax = max(np.max(np.abs(x)), np.max(np.abs(y)))
lim = (int(xymax/binwidth) + 1)*binwidth
bins = np.arange(-lim, lim + binwidth, binwidth)
axHistx.hist(x, bins=bins)
axHisty.hist(y, bins=bins, orientation='horizontal')
# the xaxis of axHistx and yaxis of axHisty are shared with axScatter,
# thus there is no need to manually adjust the xlim and ylim of these
# axis.
axHistx.set_yticks([0, 50, 100])
axHisty.set_xticks([0, 50, 100])
plt.show()
Result with axes_grid
Could it solved with axes_grid ???
Here is a code to plot an animation with blit enabled.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
def update_fig(num, data, plot, ax) :
text = None
if num % 2 == 0 :
text = ax.text(0.5, 0.5, str(num), horizontalalignment='center', verticalalignment='center', transform=ax.transAxes,size=15)
plot.set_array(data[:, :, num %1500].ravel())
if text :
return pp_color, text,
else :
return pp_color,
fig, ax = plt.subplots()
data = np.random.rand(60, 50, 1500)
pp_color = plt.pcolormesh(data[:,:, 0], cmap = 'Reds', vmin=np.amin(data), vmax=np.amax(data))
cbar = plt.colorbar()
t1 = cbar.ax.set_title('t1')
plt.axis('equal')
ax.text(0.5, -0.1, 'footnote text', horizontalalignment='center', transform=ax.transAxes,)
t2 = ax.set_title("t2")
line_ani = animation.FuncAnimation(fig, update_fig,
interval=0.1, blit=True, fargs=(data, pp_color, ax))
plt.show()
When I run the script I get that :
I have to resize my window to have title, colorbar displayed.
When I comment animation code, first figure frame is correctly displayed.
How can I correctly display my figure without resizing it?
I have a little problem with matplotlib.
The size assignment is good when displaying (plt.show()) a 2D or 3D visualization.
But does not happen if it's a 3D visualization during the save (Fig.savefig(...))
The easiest way is to show the result.
Have you an Idea ?
FILES :
2DVisualisationFile
3DVisualisationFile
PYTHON SCRIPT
# coding: utf8
import os
import numpy as np
from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import
import matplotlib.pyplot as plt
DPI = 150
FIG_SIZE = (8.60, 5.40)
mu = 1
sigma = 0.75
S = np.random.normal(mu, sigma, size=(1000,3))
# 2D Visualisation
Fig = plt.figure(figsize = FIG_SIZE, dpi = DPI)
ax = Fig.add_subplot(111)
ax.scatter(S[:,0], S[:,1], alpha=0.5)
print(Fig.get_size_inches())
plt.show()
Fig.savefig(os.path.dirname(__file__) + "/Samples_Test.png", transparent = False, bbox_inches = 'tight', dpi=DPI)
plt.close('all')
# 3D Visualisation
Fig = plt.figure(figsize = FIG_SIZE, dpi = DPI)
ax = Fig.add_subplot(111, projection='3d')
ax.scatter(S[:,0], S[:,1], S[:,2], alpha=0.5)
print(Fig.get_size_inches())
plt.show()
Fig.savefig(os.path.dirname(__file__) + "/Samples_Test2.png", transparent = False, bbox_inches = 'tight', dpi=DPI)
plt.close('all')
The problem is, that with bbox_inches = 'tight' the figure size can be changed during saving, see matplotlib.pyplot.savefig.
Instead use bbox_inches = None and the saved figure will have the correct size.
import matplotlib.pyplot as plt
FIG_SIZE = (6, 3)
fig = plt.figure(figsize = FIG_SIZE)
ax = fig.add_subplot(111, projection='3d')
ax.plot([0,1], [0,1], [0,1])
plt.show()
fig.savefig("bbox_inches_tight.png", bbox_inches = 'tight')
fig.savefig("bbox_inches_None.png", bbox_inches = None)
plt.close('all')
I am playing around with histogram2d and I am trying incorporate a color bar logarithmic values.
Here is my current code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib.colors import LinearSegmentedColormap
cmap = LinearSegmentedColormap.from_list('mycmap', ['black', 'maroon',
'crimson', 'orange', 'white'])
fig = plt.figure()
ax = fig.add_subplot(111)
H = ax.hist2d(gas_pos[:,0]/0.7, gas_pos[:,1]/0.7, cmap=cmap,
norm=matplotlib.colors.LogNorm(), bins=350, weights=np.log(gas_Temp))
ax.tick_params(axis=u'both', which=u'both',length=0)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
cb = fig.colorbar(H[3], ax=ax, shrink=0.8, pad=0.01,
orientation="horizontal", label=r'$\log T\ [\mathrm{K}]$')
cb.ax.set_xticklabels([1,2,3,4])
cb.update_ticks()
empty = Rectangle((0,0 ), 0, 0, alpha=0.0)
redshift = fig.legend([empty], [r'$z = 127$'],
loc='upper right', frameon=False, handlelength=0, handletextpad=0)
redshift.get_texts()[0].set_color('white')
#fig.add_artist(redshift)
plt.show()
The weights are values not passed through np.log() and are currently being normalized through LogNorm().
What I am trying to get is to have the colorbar tic labels to be the logarithmic values of what is currently there eg. 10**4 --> 4, 10**6 --> 6, etc.
I have tried changing the formatting and also passing through the logarithmic values of np.log(gas_Temp), but nothing is really working.
The idiomatic thing to do is use a LogFormatterExponent to do the formatting of your colorbar. That's exactly what you need: to display 10**x values as x, or in other words, to display y values as log10(x).
Proof using dummy data:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
from matplotlib.ticker import LogFormatterExponent # <-- one new import here
# generate dummy data
histdata = 10**(np.random.rand(200,200)*4 + 1) # 10^1 -> 10^5
# plot
fig = plt.figure()
ax = fig.add_subplot(111)
ax.tick_params(axis=u'both', which=u'both',length=0)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
im = plt.imshow(histdata,cmap='viridis',norm=LogNorm())
cb = fig.colorbar(im, ax=ax, shrink=0.8, pad=0.01,
orientation="horizontal", label=r'$\log T\ [\mathrm{K}]$')
# v-- one new line here
cb.formatter = LogFormatterExponent(base=10) # 10 is the default
cb.update_ticks()
Compare the result of your original (left) with the modified version (right):