简体   繁体   中英

Matplotlib: force aspect ratio in series of plots

I have a series of images that represent physical regions of space, ie they are images of objects. They do not have a uniform amount of pixels, but in EVERY case, the resolution along the y-axis is the same for every image, and the length along the x-axis is the same (specifically: pixels are always 150 um across in the y dimension and images are always 11mm across in the x dimension).

I would like to generate a series of plots whose sizes accurately reflect the spatial sizes of the objects in relation to one another, so images that have a greater number of rows correspond to being larger in the y-dimension spatially, but every image is the same size in the x-dimension regardless of how many pixels there are in that dimension (since resolution in x is not constant).

To do this, my script calculates the x and y dimensions in spatial units (for example, 11mm x 4.2 mm or 11mm x 4.5 mm) and then forces the aspect ratio in plt.imshow using the aspect setting to the ratio of the y axis size to the x axis size.

This does not work for some reason. Also, setting the figure size doesn't work. Here's my code and images: 在此处输入图片说明在此处输入图片说明在此处输入图片说明

for f in mats: # Separate filename from extension name, ext = os.path.splitext(f)

# Get the age of the sample lens
age = int(name.split('_', 1)[0])

bshiftm = io.loadmat(f)['bshiftm']  # Un-interpolated image
bshiftm2 = io.loadmat(f)['bshiftm2']  # Interpolated image

# Load the data about the physical dimensions of images
xscan = int(io.loadmat(f)['xscan'])  # in mm
zscan = int(io.loadmat(f)['zscan'])  # in mm
zframes = int(io.loadmat(f)['zframes'])

# Determine resolution data so we can scale plot axes
xres = float(xscan)/bshiftm.shape[1]  # x dim. is the number of columns
zres = float(zscan)/zframes  # In mm/pixel

# All images have same x dimensions, but z dimensions are all different,
# meaning we have to figure them out based on the resolution of the image
# in the z direction and the number of rows in the raw image
xsize = xscan  # In mm
zsize = zres*bshiftm.shape[0]  # In mm

# Using the physical dimensions of the image, find the resolutions of the
# interpolated images so we can scale the plot axes
xresi = float(xsize)/bshiftm2.shape[1]
zresi = float(zsize)/bshiftm2.shape[0]

# Aspect ratio of image
ar = float(zsize)/xsize

# Start plotting
fig = plt.figure()

# Locations and labels for x tics
xlocs = np.arange(0, bshiftm.shape[1], bshiftm.shape[1]/5)
xlabs = map(str, np.round(xlocs*xres, 1))
plt.xticks(xlocs, xlabs)
plt.xlabel('x (mm)').set_weight('bold')

# Locations and labels for z tics
zlocs = np.arange(0, bshiftm.shape[0], bshiftm.shape[0]/2)
zlabs = map(str, np.round(zlocs*zres, 1))
plt.yticks(zlocs, zlabs)
plt.ylabel('z (mm)').set_weight('bold')

# Set the title
#plt.title(str(age) + ' year old lens, XZ').set_weight('bold')

# Plot the uninterpolated data
plt.imshow(bshiftm,
           cmap='spectral',
           aspect=ar,
           vmin=8,
           vmax=9.6,
           origin='upper',
           interpolation='none')

# Set up colorbar
cbar = plt.colorbar()
#cbar.set_label('Brillouin Shift (GHz)', rotation=270)

plt.savefig(os.path.join(raw, name))
plt.close()

It seems to me that this should work, and I have verified that this script correctly calculates the spatial dimensions of every image, along with the aspect ratio that each should have. I should point out that the third image is 110 pixels across in the x direction, while the other two are only 55.

Also, if anybody has advice on how to place ticks more robustly, that would be wonderful to hear.

At least one rather robust way is to do the image scaling by using the extent keyword.

ax.imshow(image, extent=[0, 10.4, 0, 4.2], aspect=1)

This should handle everything you need. (Of course, change 10.4 and 4.2 to whatever the physical dimensions are). It should be noted that in this case the aspect=1 does not necessarily make square pixels, instead it forces the units on both axis to be equal in scale - which seems to be what you want.

Just as a small example:

import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(np.random.random((20, 20)), extent=(0, 10.5, 2, 4.7), aspect=1, interpolation='nearest')

This gives:

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM