指定并保存具有精确大小(以像素为单位)的图形


151

假设我的图像尺寸为3841 x 7195像素。我想将图形的内容保存到磁盘,以得到我指定的确切大小的图像(以像素为单位)。

没有轴,没有标题。只是图像。我个人并不关心DPI,因为我只想以像素为单位指定图像在屏幕上所占的大小。

我已经阅读了其他 线程,它们似乎都将转换为英寸,然后以英寸为单位指定图形的尺寸,并以某种方式调整dpi。我想避免处理像素到英寸转换可能导致的精度损失。

我尝试过:

w = 7195
h = 3841
fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(im_np, aspect='normal')
fig.savefig(some_path, dpi=1)

没有运气(Python抱怨宽度和高度都必须低于32768(?))

从我所看到的一切来看,都matplotlib需要在inches和中指定图形大小dpi,但是我只对图形在磁盘中占据的像素感兴趣。我怎样才能做到这一点?

需要说明的是:我正在寻找一种使用matplotlib而不是其他图像保存库的方法。


使用matplotlib,无法直接以英寸为单位设置图形大小。
tiago 2012年

Answers:


175

Matplotlib不能直接处理像素,而是可以处理物理尺寸和DPI。如果要显示具有特定像素大小的图形,则需要知道显示器的DPI。例如,此链接将为您检测到该链接

如果您具有3841x7195像素的图像,则监视器不太可能会那么大,因此您将无法显示该尺寸的图形(matplotlib要求该图形适合屏幕尺寸,如果您要求一个尺寸太大会缩小到屏幕尺寸)。让我们想象一下,您仅需要一个800x800像素的图像作为示例。这是在监视器(my_dpi=96)中显示800x800像素图像的方法:

plt.figure(figsize=(800/my_dpi, 800/my_dpi), dpi=my_dpi)

因此,您基本上只需将尺寸(以英寸为单位)除以DPI。

如果要保存特定大小的图形,则是另一回事。屏幕DPI不再那么重要了(除非您要求提供一个不适合屏幕的数字)。使用相同的800x800像素图形示例,我们可以使用dpi关键字来将其保存为不同的分辨率savefig。要将其保存为与屏幕相同的分辨率,只需使用相同的dpi:

plt.savefig('my_fig.png', dpi=my_dpi)

要将其保存为8000x8000像素的图像,请使用10倍大的dpi:

plt.savefig('my_fig.png', dpi=my_dpi * 10)

请注意,并非所有后端都支持DPI的设置。在这里,使用PNG后端,但是pdf和ps后端将以不同的方式实现大小。同样,更改DPI和大小也会影响诸如fontsize之类的内容。较大的DPI将保持相同的字体和元素相对大小,但是如果您希望较小的字体用于较大的图形,则需要增加物理尺寸而不是DPI。

回到您的示例,如果要保存3841 x 7195像素的图像,可以执行以下操作:

plt.figure(figsize=(3.841, 7.195), dpi=100)
( your code ...)
plt.savefig('myfig.png', dpi=1000)

请注意,我使用的数字dpi为100以适合大多数屏幕,但dpi=1000为了获得所需的分辨率而将其保存下来。在我的系统中,这会生成一个3840x7190像素的png -似乎保存的DPI总是比所选值小0.02像素/英寸,这将对大图像尺寸产生(较小)影响。这里对此进行更多讨论。


5
方便记住,显示器尺寸(因此标准浏览器和ui窗口尺寸)通常为96 dpi-96的倍数。当想到这样的时候,像1440像素这样的突然数字是有意义的(15英寸)。
Danny Staple 2014年

无法将此传递figsizeplt.figure。解决的方法是按照其他答案的建议进行操作,并在不致电的情况 figsize致电,然后致电fig.set_size_inches(w,h)
特立尼达

figure和的文档savefig
把手

该链接未显示Apple Thunderbolt Display的正确值。
德米特里(Dmitry)

我喜欢此解决方案,但有一个警告。该文本大小成反比的DPI。(我的系统是MacBook Pro,OS X),因此对于交互式打印,将dpi放大至较大值(如10 * my_dpi)会将文本缩小到几乎看不见的程度。
赫斯特教授,

16

根据您的代码,这对我有用,生成了一个93Mb png图像,带有彩色噪声和所需的尺寸:

import matplotlib.pyplot as plt
import numpy

w = 7195
h = 3841

im_np = numpy.random.rand(h, w)

fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(im_np, aspect='normal')
fig.savefig('figure.png', dpi=1)

我正在使用Linux Mint 13中Python 2.7库的最新PIP版本。

希望有帮助!


4
设置非常低的dpi将意味着字体几乎不可见,除非明确使用非常大的字体。
tiago 2012年

1
最好设置更高的dpi,然后将英寸大小(无论如何都是任意的)除以该dpi。除此之外,您的设置确实会产生用于像素再现的精确像素,谢谢!
FrenchKheldar 2014年

我试图在保存带有绘图元素(圆,线等)的图片之前使用它。这会干扰线宽,从而使元素几乎不可见。
Gauthier 2015年

5

根据tiago接受的响应,这是一个小型通用函数,该函数将numpy数组导出到与该数组具有相同分辨率的图像:

import matplotlib.pyplot as plt
import numpy as np

def export_figure_matplotlib(arr, f_name, dpi=200, resize_fact=1, plt_show=False):
    """
    Export array as figure in original resolution
    :param arr: array of image to save in original resolution
    :param f_name: name of file where to save figure
    :param resize_fact: resize facter wrt shape of arr, in (0, np.infty)
    :param dpi: dpi of your screen
    :param plt_show: show plot or not
    """
    fig = plt.figure(frameon=False)
    fig.set_size_inches(arr.shape[1]/dpi, arr.shape[0]/dpi)
    ax = plt.Axes(fig, [0., 0., 1., 1.])
    ax.set_axis_off()
    fig.add_axes(ax)
    ax.imshow(arr)
    plt.savefig(f_name, dpi=(dpi * resize_fact))
    if plt_show:
        plt.show()
    else:
        plt.close()

如tiago上次答复中所述,需要首先找到屏幕DPI,例如,可以在此处完成:http : //dpi.lv

resize_fact在函数中添加了一个附加参数,例如,您可以将图像导出到原始分辨率的50%(0.5)。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.