I want to convert a plot generated with matplotlib to an rgb array. In my case, I want to draw two circles using matplotlib. Currently, there are two problems:
You can still see the space taken by the axes
The circles aren't circles anymore in the resulting rgb array
Here is the code so far:
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
Generate the plot:
def drawImage(color, posx, posy, radius):
posx_left, posx_right = posx
posy_left, posy_right = posy
radius_left, radius_right = radius
fig = Figure()
canvas = FigureCanvas(fig)
ax = fig.gca()
ax.axis('off')
ax.set_axis_off()
ax = fig.add_subplot(1, 1, 1)
circle_left = plt.Circle((posx_left, posy_left), radius=radius_left,color=color)
ax.add_patch(circle_left)
circle_right = plt.Circle((posx_right, posy_right), radius=radius_right,color=color)
ax.add_patch(circle_right)
fig.tight_layout(pad=0)
fig.canvas.draw()
width, height = fig.get_size_inches() * fig.get_dpi()
img = np.fromstring(fig.canvas.tostring_rgb(), dtype='uint8').reshape(int(height), int(width), 3)
return img
Generate plot and save as array:
color = "blue"
radius = 0.2, 0.12
posx = 0.0,0.8
posy = 0.3,0.7
img = drawImage(color,posx,posy,radius)
plt.imshow(img)
Related
I would like to add color bar with jet color map into my frame exactly like the picture below. tried to look over the web but did not find anything.
any ideas?
You could do the following:
create a transparent heatmap with correct alpha values
use plt.contourf to add the heatmap on the image
use inset_axes to position the colorbar
Example:
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
def transparent_cmap(cmap, N=255):
fix_cmap = cmap
fix_cmap._init()
fix_cmap._lut[:,-1] = np.linspace(0, 0.8, N+4)
return fix_cmap
fix_cmap = transparent_cmap(plt.cm.Reds)
im = Image.open('test.jpg')
w, h = im.size
y, x = np.mgrid[0:h, 0:w]
heat = np.zeros((h,w))
heat[500:550,500:550] = np.random.random((50,50))
fig, ax = plt.subplots(1, 1)
ax.imshow(im)
plt.contourf(x, y, heat, 15, cmap=fix_cmap)
cbaxes = inset_axes(ax, width="3%", height="30%", loc=1)
plt.colorbar(cax=cbaxes)
plt.show()
This will give:
I am trying to rotate an embedded plot as a whole (i.e. the x axis of the plot should be in 45 degrees with x axis of embed plot). An example code providing a rotated embedded plot according to How to rotate a simple matplotlib Axes can be found below. The rotation does not seem to rotate the data but just the axis. In addition, I can't yet figure out how to move the embed plot within this figure
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors as colors
import matplotlib as mpl
from matplotlib.transforms import Affine2D
import mpl_toolkits.axisartist.floating_axes as floating_axes
def add_subplot_axes(ax,rect,axisbg='w'):
fig = plt.gcf()
box = ax.get_position()
width = box.width
height = box.height
inax_position = ax.transAxes.transform(rect[0:2])
transFigure = fig.transFigure.inverted()
infig_position = transFigure.transform(inax_position)
x = infig_position[0]
y = infig_position[1]
width *= rect[2]
height *= rect[3]
subax = fig.add_axes([x,y,width,height])
x_labelsize = subax.get_xticklabels()[0].get_size()
y_labelsize = subax.get_yticklabels()[0].get_size()
x_labelsize *= rect[2]**0.5
y_labelsize *= rect[3]**0.5
subax.xaxis.set_tick_params(labelsize=x_labelsize)
subax.yaxis.set_tick_params(labelsize=y_labelsize)
return subax
St=np.zeros((150,150))
k=np.random.sample(150)
np.fill_diagonal(np.fliplr(St), k)
fig=plt.figure(figsize=(10,10))
ax=fig.add_subplot(111)
ax.imshow(St,cmap='Greys')
plot_extents = 0, 10, 0, 10
transform = Affine2D().rotate_deg(45)
helper = floating_axes.GridHelperCurveLinear(transform, plot_extents)
ax1 = floating_axes.FloatingSubplot(fig, 111, grid_helper=helper)
ax1.plot(np.arange(0,10),(0,1,2,3,4,5,7,9,10,10))
fig.add_subplot(ax1)
plt.show()
I am playing around with dynamic updates of matplotlib plots.
I would like to be able to update a plot dynamically, based on pulling some data down, say every 0.5 seconds. However instead of using markers, I would like to be able to use a jpg image. i.e. plot several images, and move them along the axis.
Here is a dummy code that performs the idea using markers:
import matplotlib.pyplot as plt
import random
plt.ion()
class DynamicUpdate():
#Suppose we know the x range
min_x = 0
max_x = 10
def on_launch(self):
self.figure, self.ax = plt.subplots()
self.lines, = self.ax.plot([],[], 'o')
self.ax.set_xlim(self.min_x, self.max_x)
self.ax.set_ylim(0,500)
self.ax.grid()
def on_running(self, xdata, ydata):
self.lines.set_xdata(xdata)
self.lines.set_ydata(ydata)
self.figure.canvas.draw()
self.figure.canvas.flush_events()
#Example
def __call__(self):
import numpy as np
import time
self.on_launch()
xdata = np.arange(10)
ydata = np.zeros(10)
for it in range(100):
ydata=[y+random.randint(1,10) for y in ydata]
self.on_running(xdata, ydata)
time.sleep(0.5)
return xdata, ydata
d = DynamicUpdate()
d()
plt.show()
I have attempted using imshow() to add images to the axis, but they refuse to update and move as the data changes.
If anyone has any bright ideas I would be gratful.
This seems to behave as I wanted - images placed on axis and moved along, until it had done a certain number of steps.
It includes some code for automatically setting the image to an appropriate size on the axis:
import matplotlib.pyplot as plt
import random
import matplotlib.image as image
plt.ion()
def get_ax_size(fig, ax):
'''
Returns the size of a given axis in pixels
Args:
fig (matplotlib figure)
ax (matplotlib axes)
'''
bbox = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
width, height = bbox.width, bbox.height
width *= fig.dpi
height *= fig.dpi
return width, height
def get_extent(fig, ax, image_name, xsize, xpos, ypos):
'''
Places an image on a given axes whilst maintaining its aspect ratio
Args:
fig (matplotlib figure)
ax (matplotlib axes)
image_name (string): name of image to place on axes
xsize(float): size of the x-dimension of object given as fraction of the axes length
xpos(float): x-coordinate of image given as fraction of axes
ypos(float): y-coordinate of image given as fraction of axes
'''
import matplotlib.image as image
im = image.imread(image_name)
xrange=ax.get_xlim()[1]-ax.get_xlim()[0]
yrange=ax.get_ylim()[1]-ax.get_ylim()[0]
ysize=(im.shape[0]/im.shape[1])*(xsize*get_ax_size(fig,ax)[0])/get_ax_size(fig,ax)[1]
xsize *= xrange
ysize *= yrange
xpos = (xpos*xrange) + ax.get_xlim()[0]
ypos = (ypos*yrange) + ax.get_ylim()[0]
return (xpos,xpos+xsize,ypos,ypos+ysize)
class DynamicUpdate():
def on_launch(self):
self.figure, self.ax = plt.subplots()
self.lines, = self.ax.plot([],[], 'o')
self.im = []
self.ax.set_xlim(0,10)
self.ax.set_ylim(0,100)
self.ax.grid()
def on_running(self, xdata, ydata):
global flags
im = image.imread('Image.jpg')
for flag in flags:
flag.remove()
flags = []
for i,x in enumerate(xdata):
extent=get_extent(self.figure,self.ax,'Image.jpg',0.1,x,ydata[i])
print(extent)
flags.append(self.ax.imshow(im,aspect='auto',extent=extent,interpolation='none', zorder=10 ))
self.figure.canvas.draw()
self.figure.canvas.flush_events()
#Example
def __call__(self):
import numpy as np
import time
self.on_launch()
xdata = np.arange(10)/10
ydata = np.zeros(10)
for it in range(100):
if it < 10:
ydata=[(y+random.randint(1,5)) for y in ydata]
self.on_running(xdata, ydata/self.ax.get_ylim()[1]-self.ax.get_ylim()[0])
time.sleep(0.2)
return xdata, ydata
flags=[]
d = DynamicUpdate()
d()
plt.show()
Consider a 3D bar plot with custom grid lines:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.ticker import MultipleLocator
# This import registers the 3D projection, but is otherwise unused.
from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import
fig = plt.figure(figsize=(20, 10))
ax = fig.add_subplot(111, projection='3d')
ax.xaxis.set_major_locator(MultipleLocator(1))
ax.yaxis.set_major_locator(MultipleLocator(1))
ax.zaxis.set_major_locator(MultipleLocator(2))
nx = 10
ny = 10
colors = cm.tab20(np.linspace(0, 1, nx))
width = depth = 0.1
for x in np.arange(nx):
for y in np.arange(ny):
ax.bar3d(x, y, 0, width, depth, x+y, shade=False, color = colors[x], edgecolor = 'black')
plt.show()
How can I place the bars so that the bars are centered where the grid lines cross each other in the xy plane?
I'm thinking about something like
ax.bar3d(x+0.5*depth, y+0.5*width, ...)
only it is not clear to me what the offset is that matplotlib uses. It should work for all depth and width values.
For 2D bar plots there is an argument for this, align = 'center', but it doesn't seem to work for 3D.
What looks to you as a shift in coordinates is really just the projection in combination with the margins of the axes. Hence even if the bars are correctly positionned in their center they look offset and that offset is dependent on the axes size, viewing angle etc.
The solution to this is in principle given in this Q&A:
Removing axes margins in 3D plot
You would center the bars by subtracting half of their width and add a patch to remove the margin of the zaxis. Then setting the lower z limit to 0 pins the bars to the grid and makes them look centered for any viewing angle.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.ticker import MultipleLocator
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.axis3d import Axis
def _get_coord_info_new(self, renderer):
mins, maxs, cs, deltas, tc, highs = self._get_coord_info_old(renderer)
correction = deltas * [0,0,1.0/4]
mins += correction
maxs -= correction
return mins, maxs, cs, deltas, tc, highs
if not hasattr(Axis, "_get_coord_info_old"):
Axis._get_coord_info_old = Axis._get_coord_info
Axis._get_coord_info = _get_coord_info_new
fig = plt.figure(figsize=(20, 10))
ax = fig.add_subplot(111, projection='3d')
ax.xaxis.set_major_locator(MultipleLocator(1))
ax.yaxis.set_major_locator(MultipleLocator(1))
ax.zaxis.set_major_locator(MultipleLocator(2))
nx = 10
ny = 10
colors = cm.tab20(np.linspace(0, 1, nx))
width = depth = 0.1
for x in np.arange(nx):
for y in np.arange(ny):
ax.bar3d(x-width/2., y-depth/2., 0, width, depth, x+y, shade=False,
color = colors[x], edgecolor = 'black')
ax.set_zlim(0,None)
plt.show()
I am plotting a collection of rectangles with matplotlib.patches. My code is:
import matplotlib.pyplot as plt
import matplotlib.patches as patches
fig = plt.figure(figsize=(14, 10))
for i in rectangles_list:
ax1 = fig.add_subplot(111, aspect='equal')
ax1.add_patch(patches.Rectangle(
(x[i], y[i]),
width[i],
height[i],
alpha = 1.0,
facecolor = colors_list[i]
)
)
plt.show()
The rectangles may be overlapping, therefore some of them may be completely hidden. Do you know if it is possible to get the colors of the visible rectangles? I mean the colors of the rectangles that are not completely hidden and therefore that can be actually viewed by the user. I was thinking to some function that returns the color of the pixels, but more intelligent ideas are welcome. If possible, I'd prefer to not use PIL. Unfortunately I cannot find any solution on the internet.
Following Vlass Sokolov comment and this Stackoverflow post by Joe Kington, here is how you could get a numpy array containing all the unique colors that are visible on a matplotlib figure:
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np
plt.close('all')
# Generate some data :
N = 1000
x, y = np.random.rand(N), np.random.rand(N)
w, h = np.random.rand(N)/10 + 0.05, np.random.rand(N)/10 + 0.05
colors = np.vstack([np.random.random_integers(0, 255, N),
np.random.random_integers(0, 255, N),
np.random.random_integers(0, 255, N)]).T
# Plot and draw the data :
fig = plt.figure(figsize=(7, 7), facecolor='white')
ax = fig.add_subplot(111, aspect='equal')
for i in range(N):
ax.add_patch(Rectangle((x[i], y[i]), w[i], h[i], fc=colors[i]/255., ec='none'))
ax.axis([0, 1, 0, 1])
ax.axis('off')
fig.canvas.draw()
# Save data in a rgb string and convert to numpy array :
rgb_data = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
rgb_data = rgb_data.reshape((int(len(rgb_data)/3), 3))
# Keep only unique colors :
rgb_data = np.vstack({tuple(row) for row in rgb_data})
# Show and save figure :
fig.savefig('rectangle_colors.png')
plt.show()