指定matplotlib轴内容大小

时间:2019-04-15 14:45:19

标签: matplotlib jupyter-notebook imshow

比方说,我正在尝试绘制x*y像素(或英寸)的黑洞图片。与许多其他视图不同,我不想指定图形的总大小,而是要指定图内容的大小(因此,框架中的所有内容,不包括框架本身,刻度线,标签,颜色条,边距,...)。

首选的方法是什么?


自己尝试

我已经遇到过几次这个问题,但是我现在更喜欢其他一些方法,而不是以前的反复尝试大小的迭代……这是我的“游乐场代码”:

import numpy as np

%matplotlib inline
import matplotlib
from matplotlib import pyplot as plt
import notebook

print(matplotlib.__version__, notebook.__version__)
# 3.0.0, 5.7.0 in my case

# some data
x, y = 456, 123
a = np.random.randn(y, x)

# the following at least gets the actual figure size in browser right,
# but if possible i'd like to avoid large white space margins as well...
# %config InlineBackend.print_figure_kwargs = {'bbox_inches': None}

dpi = plt.rcParams['figure.dpi']
fig, ax = plt.subplots(figsize=(x/dpi, y/dpi), dpi=dpi)
im = ax.imshow(a, interpolation='none')
cb = fig.colorbar(im)

# print sizes
bbox = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
print(f"content size: ({bbox.width}, {bbox.height}) inch, ({bbox.width*fig.dpi}, {bbox.height*fig.dpi}) px")
print(f"fig size:     {fig.get_size_inches()} in, {fig.dpi*fig.get_size_inches()} px")
print(f"dpi: {fig.dpi}")

输出:

content size: (4.023888888888889, 1.3870138888888888) inch, (289.72, 99.865) px
fig size:     [6.33333333 1.70833333] in, [456. 123.] px
dpi: 72.0

sample output

您可以看到打印的图形尺寸为456x123像素,但是如果您实际检查上传的图片(从浏览器粘贴的副本),则会看到它仅为376x119像素。尽管可以固定(如代码中所述),但独立于此的实际“内容”尺寸保持为282x75 px:-/。

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

不幸的是,没有首选的方法。 我想到了一些或多或少复杂的解决方法。

A。使图形与图像一样大,在保存的同时将其展开

如果主要目的是生成图形的图像文件,那么最简单的方法可能是使图像绘图的轴与图形完全一样大,然后通过{{1} }选项。

尽管如此,仍需要在图形外部手动放置一个颜色栏。

bbox_inches="tight"

enter image description here

此方法的主要缺点是可能导致图像错误一个像素。这是因为位置始终位于图形坐标中,因此在将轴大小标记为像素时会导致舍入错误。

例如如果以上选择一个import numpy as np import matplotlib.pyplot as plt #create some image, with lines every second pixel rows = 123 cols = 456 image = np.zeros((rows,cols)) image[:, np.arange(0,image.shape[1], 2)] = 1 image[np.arange(0,image.shape[0], 2), :] = 0.5 dpi = 100 fig, ax = plt.subplots(figsize=(image.shape[1]/dpi, image.shape[0]/dpi), dpi=dpi) fig.subplots_adjust(0,0,1,1) im = ax.imshow(image) cax = fig.add_axes([1.05, 0, 0.03, 1]) fig.colorbar(im, cax=cax) fig.savefig("test.png", bbox_inches="tight") ,结果将是

enter image description here

交错的线条使您很容易发现图像高度太小一个像素。

B。使图形比图像大,调整边距

上述方法的一个缺点是轴装饰和颜色栏位于图形外部。要将其放入内部,可以定义所有边距并计算最终数字将需要多大。这是一个繁琐的工作。

dpi=69

enter image description here

它也将遭受与 A。相同的缺陷,例如如果使用一些奇数,例如

import numpy as np
import matplotlib.pyplot as plt

#create some image
rows = 123
cols = 456
image = np.zeros((rows,cols))
image[:, np.arange(0,image.shape[1], 2)] = 1
image[np.arange(0,image.shape[0], 2), :] = 0.5

dpi = 100

left = right = 60
top = bottom = 40
cbarwidth = 24
wspace = 10

width = left + cols + wspace + cbarwidth + right
height = top + rows + bottom

w = width / dpi
h = height / dpi

fig, (ax, cax) = plt.subplots(ncols = 2, figsize=(w,h), dpi=dpi, 
                              gridspec_kw=dict(width_ratios=[cols, cbarwidth]))
fig.subplots_adjust(left = left/width, right = 1-right/width, 
                    bottom = bottom/height, top = 1-top/height, 
                    wspace = wspace / (cols + cbarwidth))

im = ax.imshow(image)
fig.colorbar(im, cax=cax)

fig.savefig("test2.png")

enter image description here

C。使用dpi = 72 left = right = 59 top = bottom = 37 cbarwidth = 19 wspace = 12 并将轴放置在顶部。

确保没有混叠效果的唯一方法是使用figimage。这会将图像以像素坐标放置到图形中。但是,默认情况下将没有任何轴。 @anntzer已proposed recently解决了这个问题,就是将一个轴放在图中figimage的位置。

figimage

enter image description here

使用此功能可以确保图像本身没有失真。但是轴刻度线可能会偏离一个像素左右,因为它们经历了图形转换。另外,我还没有完全考虑过是否需要将bbox坐标移动半个单位。 (欢迎在最后一点发表评论!)