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

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()

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()

Matplotlib's inset_locator with inverted axes

I want to make a standard inset into my plot. But I don't get how to use the inset_locator.
Here is my code:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1.inset_locator import inset_axes, mark_inset
x = np.linspace(0, 2)
plt.plot(x, np.sin(x))
ax = plt.gca()
ax.invert_yaxis()
axins = inset_axes(ax, width='40%', height='30%', loc='lower left')
x_in = np.linspace(1.25, 1.75)
axins.plot(x_in, np.sin(x_in))
axins.invert_yaxis()
mark_inset(ax, axins, loc1=2, loc2=4)
plt.show()
And the result is:
Apparently it the edges connect the wrong corners. How do I get them right, when my axis goes from maximum to minimum?
Unfortunately the mark_inset cannot cope with inverted axes. So you need to set the locations of the connectors manually.
patch, pp1,pp2 = mark_inset(ax, axins, loc1=1,loc2=1)
pp1.loc1 = 1
pp1.loc2 = 4
pp2.loc1 = 4
pp2.loc2 = 1

Draw Marker in Image

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()

Python 2D plots as 3D (Matplotlib)

Python plot in Matplotlib: I have a number of samples taken daily at the same time which shows a change in measurement (of something). This may be shown as a 2D plot (below left), but as the sample number increases I'd like to display this data as a 3D plot which is stacked (below right image) - this image is for illustration only.
For a starting point my code is below, how may I achieve this?
import numpy as np
import pylab as plt
t = np.arange(1024)*1e-6
y1 = np.sin(t*2e3*np.pi)
y2 = 0.5*y1
y3 = 0.25*y1
plt.plot(t,y1,'k-', label='12/03/14')
plt.plot(t,y2,'r-', label='13/03/14')
plt.plot(t,y3,'b-', label='14/03/14')
plt.xlabel('Time/sample no.')
plt.ylabel('Pk-pk level (arbitrary units)')
plt.legend()
plt.grid()
plt.show()
Would it be something like this?
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.collections import PolyCollection
from matplotlib.colors import colorConverter
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
zs = [0.0, 1.0, 2.0]
t = np.arange(1024)*1e-6
ones = np.ones(1024)
y1 = np.sin(t*2e3*np.pi)
y2 = 0.5*y1
y3 = 0.25*y1
verts=[list(zip(t, y1)), list(zip(t, y2)), list(zip(t, y3))]
poly = PolyCollection(verts, facecolors = ['r','g','b'])
poly.set_alpha(0.7)
ax.add_collection3d(poly, zs=zs, zdir='y')
ax.set_xlabel('X')
ax.set_xlim3d(0, 1024e-6)
ax.set_ylabel('Y')
ax.set_ylim3d(-1, 3)
ax.set_zlabel('Z')
ax.set_zlim3d(-1, 1)
plt.show()

Categories