I am following this answer but I do not get grid lines every 10 steps:
import matplotlib.pyplot as plt
plt.figure()
img=ims[0].copy()
dx, dy = 10,10
# Custom (rgb) grid color
grid_color = -1500
# Modify the image to include the grid
img[:,::dy] = grid_color
img[::dx,:] = grid_color
plt.imshow(img,'gray',interpolation='none',vmin=-1500,vmax=2258)
In order to make sure every pixel in the image is actually shown you need to make sure to draw the image such that one pixel in the image is larger or equal one pixel on screen.
Example: If the figure has a dpi of 100 and is 4.5 inch heigh and you take 10% margin on each side, an image with 350 pixels will be shown correctly,
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize=(6,4.5))
plt.subplots_adjust(top=0.9, bottom=0.1)
img=np.random.rand(350,350)
dx, dy = 10,10
grid_color = -1
img[:,::dy] = grid_color
img[::dx,:] = grid_color
plt.imshow(img,'gray',vmin=-1,vmax=1)
plt.show()
If the figure has a dpi of 100 and is 3.2 inch heigh and you take 10% margin on each side, an image with 350 pixels will not show every pixel and hence you get the following output,
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize=(6,3.2))
plt.subplots_adjust(top=0.9, bottom=0.1)
img=np.random.rand(350,350)
dx, dy = 10,10
grid_color = -1
img[:,::dy] = grid_color
img[::dx,:] = grid_color
plt.imshow(img,'gray',vmin=-1,vmax=1)
plt.show()
So in order to obtain a grid even for the latter case, this answer is a better approach. You can create a grid and set the linewidth of the grid, such that is always 0.72 points (=1pixel # 100dpi).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker
plt.figure(figsize=(6,3.2))
plt.subplots_adjust(top=0.9, bottom=0.1)
img=np.random.rand(350,350)
plt.imshow(img,'gray',vmin=-1,vmax=1)
plt.minorticks_on()
plt.gca().xaxis.set_minor_locator(matplotlib.ticker.MultipleLocator(10))
plt.gca().yaxis.set_minor_locator(matplotlib.ticker.MultipleLocator(10))
plt.grid(which="both", linewidth=0.72,color="k")
plt.tick_params(which="minor", length=0)
plt.show()
Related
How to display an image with imshow on all the width of an axe (viewport) but keeping the ratio 'equal' ?
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(nrows=2, figsize=(8,8))
img = ax[0].imshow(np.random.random((10,10)), aspect='equal')
line = ax[1].plot([12,34],[45,78])
I would like that the image width be aligned on the line plot keeping an equal ratio even if that implies having blank around. My application has a zooming feature coded on the image in fact so I would to offer all the width possible for its display.
So in few words, I would like the same width as with aspect='auto' but with square pixels.
Is it possible ? Thanks.
Found with a reading of Axes class - set explicitly size (width/height) of axes in given units
and the use of
set_adjustable('datalim')
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(nrows=2, figsize=(8,8))
img = ax[0].imshow(np.random.random((10,10)), aspect='equal')
ax[0].set_adjustable('datalim')
line = ax[1].plot([12,34],[45,78])
I am trying to visualise the pixel intensity of a photo by plotting a 3D graph. In the code below, lab is an image I want to analyse. The code will look at the pixel intensity of every pixels in the image and plot a graph, where the height denotes the pixel intensity.
Here is a portion of my code:
import cv2
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from skimage import io, color
import glob
from PIL import Image
plt.figure(dpi=1200)
ax = plt.axes(projection='3d')
y = range(lab.shape[0])
x = range(lab.shape[1])
X, Y = np.meshgrid(x, y)
ax.view_init(elev=60., azim=60)
thickness = ax.plot_surface(
X,
Y,
lab[:, :, 0], # change value here to adjust the height
cmap=cm.coolwarm,
antialiased=False)
# Add a color bar which maps values to colors.
fig.colorbar(thickness, shrink=0.5, aspect=5)
It outputs:
As you can see, the colour gradient is not continuous despite the graph having many fine details and slight fluctuations in height which is not represented by the color map.
Is it possible to achieve a continuous color gradient with surface plot using matplotlib like the image below?
Thank you.
You can use the colormap hsv to get the same result.
import cv2
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from skimage import io, color
import glob
from PIL import Image
lab = cv2.imread('Lenna.png')
lab = cv2.cvtColor(lab, cv2.COLOR_BGR2LAB)
fig = plt.figure()
ax = plt.axes(projection='3d')
y = range(lab.shape[0])
x = range(lab.shape[1])
X, Y = np.meshgrid(x, y)
ax.view_init(elev=60., azim=60)
thickness = ax.plot_surface(
X,
Y,
lab[:, :, 0], # change value here to adjust the height
cmap=plt.get_cmap('hsv'),
antialiased=False)
# Add a color bar which maps values to colors.
fig.colorbar(thickness, shrink=0.5, aspect=5)
plt.show()
output :
Take a look at the documentation for more colormaps.
I am just want to show and then save the same image on plot but got borders.
import numpy as np
import skimage.io
import matplotlib
import matplotlib.pyplot as plt
fileName = "1.jpg"
image=mpimg.imread(fileName)
height, width = image.shape[:2]
my_dpi = 96 / 2
fg, ax = plt.subplots(1, figsize=(1080/my_dpi, 1920/my_dpi), dpi=my_dpi)
ax.set_ylim(height, 0)
ax.set_xlim(0, width)
ax.axis('off')
ax.imshow(image.astype(np.uint8))
plt.savefig("res.png")
Source image:
Result image after resaving:
How to remove the borders and make the result image be the same as original without borders?
When adding a colorbar to images plotting with matplotlib.pyplot.imshow, the colorbar's tick labels are normally placed to the right. If the value range spans positive and negative values, this leads to a misalignment between positive and negative values (positive values do not account for the '-' sign), which, IMHO, is very ugly.
How can I adjust this so that the tick labels are aligned properly?
import numpy as np
import matplotlib.pyplot as plt
data = np.random.rand(100, 100) - 0.5
plt.figure(figsize=(2,1.5))
img = plt.imshow(data)
cbar = plt.colorbar()
In order to have the positive and negative labels on the colorbar aligned, you may align the text to the right and increase the padding between axis and labels. The amount of padding to add will depend on the length of the ticklabels.
import numpy as np
import matplotlib.pyplot as plt
data = np.random.rand(100, 100) - 0.5
plt.figure()
img = plt.imshow(data)
cbar = plt.colorbar()
plt.setp(cbar.ax.get_yticklabels(), ha="right")
cbar.ax.tick_params(pad=30)
plt.show()
I'm not convinced that this looks better though.
I am quite used to working with matlab and now trying to make the shift matplotlib and numpy. Is there a way in matplotlib that an image you are plotting occupies the whole figure window.
import numpy as np
import matplotlib.pyplot as plt
# get image im as nparray
# ........
plt.figure()
plt.imshow(im)
plt.set_cmap('hot')
plt.savefig("frame.png")
I want the image to maintain its aspect ratio and scale to the size of the figure ... so when I do savefig it exactly the same size as the input figure, and it is completely covered by the image.
Thanks.
I did this using the following snippet.
#!/usr/bin/env python
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
from pylab import *
delta = 0.025
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = Z2-Z1 # difference of Gaussians
ax = Axes(plt.gcf(),[0,0,1,1],yticks=[],xticks=[],frame_on=False)
plt.gcf().delaxes(plt.gca())
plt.gcf().add_axes(ax)
im = plt.imshow(Z, cmap=cm.gray)
plt.show()
Note the grey border on the sides is related to the aspect rario of the Axes which is altered by setting aspect='equal', or aspect='auto' or your ratio.
Also as mentioned by Zhenya in the comments Similar StackOverflow Question
mentions the parameters to savefig of bbox_inches='tight' and pad_inches=-1 or pad_inches=0
You can use a function like the one below.
It calculates the needed size for the figure (in inches) according to the resolution in dpi you want.
import numpy as np
import matplotlib.pyplot as plt
def plot_im(image, dpi=80):
px,py = im.shape # depending of your matplotlib.rc you may
have to use py,px instead
#px,py = im[:,:,0].shape # if image has a (x,y,z) shape
size = (py/np.float(dpi), px/np.float(dpi)) # note the np.float()
fig = plt.figure(figsize=size, dpi=dpi)
ax = fig.add_axes([0, 0, 1, 1])
# Customize the axis
# remove top and right spines
ax.spines['right'].set_color('none')
ax.spines['left'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
# turn off ticks
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('none')
ax.xaxis.set_ticklabels([])
ax.yaxis.set_ticklabels([])
ax.imshow(im)
plt.show()
Here's a minimal object-oriented solution:
fig = plt.figure(figsize=(8, 8))
ax = fig.add_axes([0, 0, 1, 1], frameon=False, xticks=[], yticks=[])
Testing it out with
ax.imshow([[0]])
fig.savefig('test.png')
saves out a uniform purple block.
edit: As #duhaime points out below, this requires the figure to have the same aspect as the axes.
If you'd like the axes to resize to the figure, add aspect='auto' to imshow.
If you'd like the figure to resize to be resized to the axes, add
from matplotlib import tight_bbox
bbox = fig.get_tightbbox(fig.canvas.get_renderer())
tight_bbox.adjust_bbox(fig, bbox, fig.canvas.fixed_dpi)
after the imshow call. This is the important bit of matplotlib's tight_layout functionality which is implicitly called by things like Jupyter's renderer.