Draw Marker in Image - python

I'm drawing a picture using Matplotlib:
plt.imshow(bild)
plt.show()
How do I add a Marker to this (eg. red dot / arrow) using the coordinates of the image?

You can also use plt.scatter to add a red dot to mark the point. Building on the previous answer's example code:
import matplotlib.pyplot as plt
import numpy as np
img = np.random.randn(100, 100)
plt.figure()
plt.imshow(img)
plt.annotate('25, 50', xy=(25, 50), xycoords='data',
xytext=(0.5, 0.5), textcoords='figure fraction',
arrowprops=dict(arrowstyle="->"))
plt.scatter(25, 50, s=500, c='red', marker='o')
plt.show()

You could use the module matplotlib.patches as shown below. Notice that in order to place a patch at the xth row and yth column of the image you need to reverse the order of the coordinates, i.e. y, x when instantiating the corresponding patch.
from skimage import io
import matplotlib.pyplot as plt
from matplotlib.patches import Arrow, Circle
maze = io.imread('https://i.stack.imgur.com/SQCy9.png')
ax, ay = 300, 25
dx, dy = 0, 75
cx, cy = 300, 750
patches = [Arrow(ay, ax, dy, dx, width=100., color='green'),
Circle((cy, cx), radius=25, color='red')]
fig, ax = plt.subplots(1)
ax.imshow(maze)
for p in patches:
ax.add_patch(p)
plt.show(fig)

You can use the function plt.annotate for this:
import matplotlib.pyplot as plt
import numpy as np
img = np.random.randn(100, 100)
plt.imshow(img)
plt.annotate('25, 50', xy=(25, 40), xycoords='data',
xytext=(0.5, 0.5), textcoords='figure fraction',
arrowprops=dict(arrowstyle="->"))
plt.show()

Related

Fill polygon with vertical gradient in matplotlib?

I would like to fill in polygons with vertical gradient (white-to-red) using the .set_facecolor() method. I defined a colormap using matplotlib.colors.LinearSegmentedColormap but it seems I am not allowed to pass colormap directly to color setting methods like .set_facecolor(). If I merely pass one color, it runs successfully - how can I pass a gradient to have the intended behavior, with colors ranging from white bottom to red top?
Working snippet, with fix color:
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
from matplotlib.patches import Polygon
from matplotlib import colors, patches
import numpy as np
fig,ax = plt.subplots(1)
patches = []
verts = np.random.rand(3,2)
polygon = Polygon(verts,closed=True)
patches.append(polygon)
collection = PatchCollection(patches)
ax.add_collection(collection)
collection.set_color("blue")
ax.autoscale_view()
plt.show()
Not working snippet with custom gradient:
cmap = colors.LinearSegmentedColormap.from_list('white_to_red', ['white', 'red'])
fig,ax = plt.subplots(1)
patches = []
verts = np.random.rand(3,2)
polygon = Polygon(verts,closed=True)
patches.append(polygon)
collection = PatchCollection(patches)
ax.add_collection(collection)
collection.set_facecolor(cmap)
ax.autoscale_view()
plt.show()
You can use:
ax.imshow to create an image with a gradient, localized to a specific region of the plot.
the set_clip_path method to mask the polygon-region over the image.
Like this:
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
from matplotlib.patches import Polygon
from matplotlib import colors, patches
import matplotlib.cm as cm
import numpy as np
fig,ax = plt.subplots(1)
verts = np.random.rand(3, 2)
xmin, xmax = verts[:, 0].min(), verts[:, 0].max()
ymin, ymax = verts[:, 1].min(), verts[:, 1].max()
cmap = colors.LinearSegmentedColormap.from_list('white_to_red', ['white', 'red'])
grad = np.atleast_2d(np.linspace(0, 1, 256)).T
img = ax.imshow(np.flip(grad), extent=[xmin, xmax, ymin, ymax],interpolation='nearest', aspect='auto', cmap=cmap)
polygon = Polygon(verts, closed=True, facecolor='none', edgecolor='none')
ax.add_patch(polygon)
img.set_clip_path(polygon)
ax.autoscale_view()
plt.show()

Why is matplotlib.patches.Circle not a circle?

I am trying to plot a circle over a plot. Using the Anatomy of a Figure for inspiration, I've created a short test code :
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.patches import Circle
from matplotlib.patheffects import withStroke
fig = plt.figure()
x = np.arange(1,10,.1)
y3 = np.sin(x)
gs = fig.add_gridspec(1,1) # 2x2 grid
ax=fig.add_subplot(gs[0,0])
ax.plot(x,y3)
Xc = 6
Yc = 0.5
### This produces an ellipse
circle = Circle((Xc, Yc), 0.25, clip_on=False, zorder=10, linewidth=1,
edgecolor='black', facecolor=(0, 0, 0, .0125),
path_effects=[withStroke(linewidth=5, foreground='w')])
ax.add_artist(circle)
plt.show()
which generates the below plot
Question :
Why is the displayed 'circle' really an ellipse?
The help page for Circle, defines a resolution order, but it isn't obvious how this 'resolution' order is decided. Given that my circle is following the format of the above Anatomy of a Figure, I don't understand how this happens.
For your circle to look like a circle, you have to set the aspect ratio of your plot to 1.
In your link, that is done in this line:
ax = fig.add_subplot(1, 1, 1, aspect=1)
In your example:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.patches import Circle
from matplotlib.patheffects import withStroke
fig = plt.figure()
x = np.arange(1,10,.1)
y3 = np.sin(x)
gs = fig.add_gridspec(1,1) # 2x2 grid
ax=fig.add_subplot(gs[0,0], aspect=1)
ax.plot(x,y3)
Xc = 6
Yc = 0.5
### This produces an ellipse
circle = Circle((Xc, Yc), 0.25, clip_on=False, zorder=10, linewidth=1,
edgecolor='black', facecolor=(0, 0, 0, .0125),
path_effects=[withStroke(linewidth=5, foreground='w')])
ax.add_artist(circle)
plt.show()

How to rotate a Subplot by 45 degree in Matplotlib?

I am trying to explore a subplot 2 plots with square in shape rotated by 45 degree.
import matplotlib.pyplot as plt
from matplotlib import colors
import numpy as np
data = np.random.rand(10, 10) * 20
# create discrete colormap
cmap = colors.ListedColormap(['red', 'blue','green'])
bounds = [0,5,10,15]
norm = colors.BoundaryNorm(bounds, cmap.N)
fig, ax= plt.subplots(1,2)
ax[0].imshow(data, cmap=cmap, norm=norm)
# draw gridlines
ax[0].grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
ax[0].set_xticks(np.arange(-.5, 10, 1));
ax[0].set_yticks(np.arange(-.5, 10, 1));
ax[1].imshow(data, cmap=cmap, norm=norm)
# draw gridlines
ax[1].grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
ax[1].set_xticks(np.arange(-.5, 10, 1));
ax[1].set_yticks(np.arange(-.5, 10, 1));
plt.show()
Actual Result is :-
I want to rotate individual plot by 45 degree. Something like:-
I am trying to find in Matplotlib Documentation. Still not getting. Any help?
Please note this is NOT DUPLICATE OF
Is there a way to rotate a matplotlib plot by 45 degrees?
The mentioned URL is for a plot. and the solution is to rotate IMAGE. However this is pertaining to Subplot. I want to rotate PLOT not image as whole.
Based on this link and documentation about floating_axes, you can try something like this:
from mpl_toolkits.axisartist.grid_finder import DictFormatter
import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib import colors
import numpy as np
def setup_axes1(fig, rect, angle):
tr = Affine2D().scale(2, 2).rotate_deg(angle)
#We create dictionarys to keep the xticks and yticks after the rotation
dictio={i:str(val) for i,val in enumerate(np.arange(-.5, 10, 1).tolist())}
reversedictio={i:dictio[val] for i,val in enumerate(list(reversed(sorted(dictio.keys()))))}
grid_helper = floating_axes.GridHelperCurveLinear(
tr, extremes=(-0.5, 9.5,-0.5, 9.5), tick_formatter1= DictFormatter(dictio),
tick_formatter2=DictFormatter(reversedictio))
ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper)
fig.add_subplot(ax1)
aux_ax = ax1.get_aux_axes(tr)
grid_helper.grid_finder.grid_locator1._nbins = 10 #Number of rows
grid_helper.grid_finder.grid_locator2._nbins = 10 #Number of columns
return aux_ax
fig1, axes=plt.subplots(2,figsize=(20,20))
plt.rcParams.update({'font.size': 27})
#We erase the first previous axes
fig1.delaxes(axes[0])
fig1.delaxes(axes[1])
data = np.random.rand(10, 10) * 20
#We create the floating_axes
ax0 = setup_axes1(fig1, 121,-45)
ax1 = setup_axes1(fig1, 122,-45)
# create discrete colormap
cmap = colors.ListedColormap(['red', 'blue','green'])
bounds = [0,5,10,15]
norm = colors.BoundaryNorm(bounds, cmap.N)
ax0.imshow(data, cmap=cmap, norm=norm,interpolation="nearest")
# draw gridlines
ax0.grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
ax1.imshow(data, cmap=cmap, norm=norm,interpolation="nearest")
# draw gridlines
ax1.grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
plt.show()
Output:
Or, as an other alternative, I found a "tricky" way to do it, and it's about catching the figures in the buffer, rotate them -45 degrees, and then merge them into a single image, and since you have the same two images, you can try something like this:
import matplotlib
import io
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib import colors
import numpy as np
##PLOTING THE FIGURE##
data = np.random.rand(10, 10) * 20
# create discrete colormap
cmap = colors.ListedColormap(['red', 'blue','green'])
bounds = [0,5,10,15]
norm = colors.BoundaryNorm(bounds, cmap.N)
#We change style values to get the image with better quality
plt.rcParams.update({'font.size': 46})
plt.figure(figsize=(20,20))
plt.imshow(data, cmap=cmap, norm=norm)
# draw gridlines
plt.grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
plt.gca().set_xticks(np.arange(-.5, 10, 1));
plt.gca().set_yticks(np.arange(-.5, 10, 1));
##SAVING THE FIGURE INTO AN IMAGE##
#We save the current figure as a Image
buf = io.BytesIO()
plt.savefig(buf, format='png',bbox_inches='tight')
buf.seek(0)
im = Image.open(buf) #We open the current image saved in the buffer
#We rotate the image and fill the background with white
img_01=im.rotate(-45, Image.NEAREST, expand = 1, fillcolor = (255,255,255))
buf.close()
##MERGING THE TWO FIGURES##
new_im = Image.new('RGB', (2*img_01.size[0]+20,img_01.size[1]), 'white')
mouse_mask = img_01.convert('RGBA')
new_im.paste(img_01, (0,0))
new_im.paste(img_01, (img_01.size[0]+8,0))
new_im.save("merged_images.png", 'PNG') #Important(just to clarify): save the image, since the buffer is renewed every time you run the script
new_im.show()
Output:
I helped myself with these links:
How-to-merge-images-with-same-size-using-the-python-3-module-pillow
how-to-save-a-pylab-figure-into-in-memory-file-which-can-be-read-into-pil-image
python-pillow-rotate-image-90-180-270-degrees
specify-image-filling-color-when-rotating-in-python-with-pil-and-setting-expand

How to draw a circle in a double y axis graph with matplotlib?

I am new to matplotlib. I am trying to draw an empty circle to specify the data points in a double y axis graph. I used the plt.Cirle to do the work. But it did not work. Could you kindly help me? Here is the code, and what I have got is a rectangle instead of a circle.
from matplotlib.patches import *
fig = plt.figure()
ax1 = plt.gca()
markers,stems,base = ax1.stem(x1,oscillator,linefmt='k-',markerfmt='ko')
for stem in stems:
stem.set_linewidth(1)
ax1.set_ylim(0,0.4)
ax1.set_xlim(250,500)
ax1.set_xlabel('Wavelength (nm)')
ax1.set_ylabel('Oscillator strength')
ax1.annotate('Oscillator strength', xy=(307,0.31), xytext=(325,0.35),
arrowprops=dict(arrowstyle= '-|>',connectionstyle='arc3,rad=0.5',lw = 1, color ='k'))
circ = plt.Circle((300,0.3), radius=20, edgecolor='g')
ax1.add_artist(circ)
ax2 = ax1.twinx()
ax2.plot(x2,absorbance,'r-',linewidth=1)
ax2.spines['right'].set_color('red')
ax2.tick_params(axis='y', colors='red')
ax2.yaxis.label.set_color('red')
ax2.set_ylabel('Absorbance',color='r')
ax2.annotate('', xy=(414,0.31), xytext=(450,0.33),
arrowprops=dict(arrowstyle= '-|>',connectionstyle='arc3,rad=0.5',lw = 1, color ='r'))
ax2.text(450,0.33,'Absorbance',color='red')
plt.show()
Here is the graph, the blue rectangle should be a circle:
The problem is that you create a circle in an axes of very unequal data limits. While the x axis ranges in the ranges of hundreds, the y axis ranges in the range below 1. What you observe as rectangle is hence a very distorted circle.
For such cases, where the limits are significantly different, or in any case where you want to have a true Circle on screen, the circle should rather be defined in axes coordinates or even better, in display coordinates.
An easy method to produce a circle in display coordinates is a scatter plot.
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(303,320,3)
y = np.array([.25,.13,.09,.33,.16,.11])
fig, ax = plt.subplots()
ax.stem(x,y,linefmt='k-',markerfmt='ko')
ax.scatter([x[3]],[y[3]], s=40**2, edgecolor="green", facecolor="None", linewidth=3 )
ax.axis([250,500,0,.4])
plt.show()
This should solve your problem:
https://matplotlib.org/gallery/api/patch_collection.html
from matplotlib.patches import Circle, Wedge
from matplotlib.collections import PatchCollection
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
patches = []
circle = Circle((0.5, 0.8), 0.25) #Filled circle
patches.append(circle)
patches += [
Wedge((.3, .7), .1, 0, 360), # Full circle
Wedge((.7, .8), .2, 0, 360, width=0.05), # Full ring
Wedge((.8, .3), .2, 0, 45), # Full sector
Wedge((.8, .3), .2, 45, 90, width=0.10)] # Ring sector
p = PatchCollection(patches, alpha=0.4)
ax.add_collection(p)
plt.show()

How to fix a rotated output in zoom in region in image?

I want to zoom a portion of an image and insert in the same plot. Right now, I am using mpl_toolkits package to do it. However, it results in a rotating zoom in the region as the below figure. How could I fix it using Python?
This is my full code
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes
from mpl_toolkits.axes_grid1.inset_locator import mark_inset
import matplotlib.image as mpimg
import matplotlib.patches as patches
def zoom_in_rec(input, ax,rect, cmap):
axins = zoomed_inset_axes(ax, 2, loc=3)
x1, x2, y1, y2 = rect.get_x(), rect.get_x()+rect.get_width(),rect.get_y(), rect.get_y()+rect.get_height() # specify the limits
axins.set_xlim(x1, x2) # apply the x-limits
axins.set_ylim(y1, y2) # apply the y-limits
mark_inset(ax, axins, loc1=3, loc2=4, fc="none", ec="1.0")
plt.yticks(visible=False)
plt.xticks(visible=False)
axins.imshow(input, cmap=cmap)
if __name__ == "__main__":
img = mpimg.imread('lena.bmp')
#Plot
fig = plt.figure(figsize=(128,128))
fig.patch.set_facecolor('white')
gs1 = gridspec.GridSpec(1,1)
gs1.update(wspace=0.02, hspace=0.02) # set the spacing between axes.
ax1 = plt.subplot(gs1[0])
ax1.imshow(img,cmap='gray')
rect1 = patches.Rectangle((200, 200), 120, 80, linewidth=3, edgecolor='r', facecolor='none')
zoom_in_rec(img, ax1,rect1, cmap='gray')
plt.show()
This is the lena image
This is my current output
The coordinate system of an image and the plot are different, the image has a coordinate system whose origin is in the upper left, and the y axis goes upwards downwards, while in the plot the coordinate system is in the lower left, and the y axis goes from bottom to top. to solve we can use the flipud function of numpy:
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes
from mpl_toolkits.axes_grid1.inset_locator import mark_inset
import matplotlib.image as mpimg
import matplotlib.patches as patches
import numpy as np
def zoom_in_rec(input, ax,rect, cmap):
axins = zoomed_inset_axes(ax, 2, loc=3) # zoom-factor: 2.5, location: upper-left
x1, x2, y1, y2 = rect.get_x(), rect.get_x()+rect.get_width(),rect.get_y(), rect.get_y()+rect.get_height() # specify the limits
axins.set_xlim(x1, x2) # apply the x-limits
axins.set_ylim(y1, y2) # apply the y-limits
mark_inset(ax, axins, loc1=3, loc2=4, fc="none", ec="1.0")
plt.yticks(visible=False)
plt.xticks(visible=False)
# flip image
rot = np.flipud(input)
axins.imshow(rot, cmap=cmap)
if __name__ == "__main__":
img = mpimg.imread('/home/eyllanesc/Downloads/lena.png')
#Plot
fig = plt.figure(figsize=(128,128))
fig.patch.set_facecolor('white')
gs1 = gridspec.GridSpec(1,1)
gs1.update(wspace=0.02, hspace=0.02) # set the spacing between axes.
ax1 = plt.subplot(gs1[0])
ax1.imshow(img,cmap='gray')
rect1 = patches.Rectangle((200, 200), 120, 80, linewidth=3, edgecolor='r', facecolor='none')
zoom_in_rec(img, ax1,rect1, cmap='gray')
plt.show()

Categories