假设我正在尝试绘制一张具有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
如你所见,打印的图片大小是456x123px,但如果你实际检查上传的图片(从浏览器粘贴的副本),你会发现它只有376x119px。虽然这是可以修复的(如代码中注释的那样),但独立于此的实际“内容”大小仍为282x75 px :-/。
有什么想法吗?
发布于 2019-04-15 20:18:51
不幸的是,对于这一点没有首选的方法。脑海中浮现出一些或多或少复杂的变通方法。
A.使图形与图像一样大,在保存时将其展开
如果主要目的是生成图形的图像文件,最简单的方法可能是使图像绘图的轴与图形完全相同,然后通过bbox_inches="tight"
选项展开最终的图像文件。
它将需要手动放置一个彩条以外的图形。
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")
这样做的主要缺点是,它可能会导致一个像素错误的图像。这是由于位置始终在图形坐标中,导致在将轴大小标记为像素时出现舍入误差。
例如,如果在上面的代码中选择一个dpi=69
,结果将是
交错的线条很容易发现图像的高度太小了一个像素。
B.使数字大于图像,调整页边距
上面的一个缺点是轴装饰和色条在图的外面。要将它们放在其中,可以定义所有边距并计算最终数字需要多大。这是一个但很麻烦的问题。
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")
它也会遭受与A.相同的缺陷,例如,如果使用一些奇数,如
dpi = 72
left = right = 59
top = bottom = 37
cbarwidth = 19
wspace = 12
C.使用figimage
并在顶部放置轴线。
确保没有锯齿效果的唯一方法是使用figimage
。这会将像素坐标中的图像放入图中。但是,在默认情况下,将不会有任何轴。@anntzer提供了一个解决方案,即在图中figimage
所在的位置放置一个轴。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import Bbox
#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
left = right = 60
top = 40
bottom = 65
cbarwidth = 24
wspace = 10
width = left + cols + wspace + cbarwidth + right
height = top + rows + bottom
w = width / dpi
h = height / dpi
fig = plt.figure(figsize=(w,h), dpi=dpi)
im = fig.figimage(image, xo=left, yo=bottom);
# create axes on top
# bbox in pixels
bbox = Bbox([[left, bottom], [left + cols, bottom + rows]])
ax = fig.add_axes(fig.transFigure.inverted().transform_bbox(bbox))
ax.set_facecolor("None")
# recreate axis limits
ax.set(xlim=(-0.5, cols-0.5), ylim=(rows-0.5, -0.5))
# add colorbar
cbbox = Bbox([[left + cols + wspace, bottom],
[left + cols + wspace + cbarwidth, bottom + rows]])
cax = fig.add_axes(fig.transFigure.inverted().transform_bbox(cbbox))
fig.colorbar(im, cax=cax)
fig.savefig("test3.png")
有了这个,就可以确定图像本身是没有失真的。但是,轴的刻度可能会偏离一个像素左右,因为它们会经过图形变换。此外,我还没有完全考虑bbox坐标是否需要移动半个单位。(欢迎对最后一点发表评论!)
https://stackoverflow.com/questions/55691721
复制