使用matplotlib.pyplot、imshow()和savefig()以全分辨率打印?

2024-10-01 00:17:28 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个中等大小的数组(例如1500x3000),我想按比例绘制,因为它是一个图像。然而,垂直和水平尺度是非常不同的。为了简化,假设有一米/行和10/列。然后绘制出一幅大约1500x30000的图像。我使用kwarg范围作为刻度,方面=1以避免变形。 无论是使用绘图窗口(QT4)和imshow()还是使用savefig(),我从未成功地按比例和全分辨率生成图像。

我已经寻找了许多建议的解决方案,如herehere、或heretherethere中所示,以防它是一个bug。我已经修改了matplotlibrc并将其放在~/.config/matplotlib中,尝试强制使用my display/savefig选项,但没有成功。我也试过使用pcolormesh(),但没有成功。我使用来自Ubuntu 14.04和QT4Agg的repo的python 2.7和matplotlib 1.3作为后端。我也试过TkAgg,但速度慢,结果也一样。我的印象是,在x轴的分辨率是正确的,但它绝对是在垂直方向下采样。这里有一段代码应该模拟我的问题。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors

R, C = 1500, 3000
DATA = np.random.random((R, C))
DATA[::2, :] *= -1  # make every other line negative
Yi, Xi = 1, 10 # increment
CMP = 'seismic'
ImageFormat ='pdf'
Name = 'Image'


DataRange = (np.absolute(DATA)).max() # I want my data centred on 0
EXTENT = [0, Xi*C, 0 ,Yi*R]
NORM = matplotlib.colors.Normalize(vmin =-DataRange, vmax= DataRange, clip =True)

for i in range(1,4):
    Fig=plt.figure(figsize=(45, 10), dpi = 100*i, tight_layout=True)
    Fig.suptitle(Name+str(i)+'00DPI')
    ax = Fig.add_subplot(1, 1, 1)
    Plot = ax.imshow(DATA, cmap=plt.get_cmap(CMP), norm = NORM, extent = EXTENT, aspect = 1, interpolation='none') 
    ax.set_xlabel('metres')
    ax.set_ylabel('metres')
    Fig.savefig(Name+str(i)+'00DPI.'+ImageFormat,  format = ImageFormat, dpi = Fig.dpi)
plt.close()

在imshow()中,interpolation='none'或'nearest'或'biliner'不会由于某些原因更改分辨率,尽管我认为如果我确实显示()而不是savefig(),则至少应该在Qt4窗口中更改分辨率。 注意,无论您在plt.figure(dpi=)中设置什么,保存的图形中的分辨率都是相同的。

我对这个系统的运作方式已经失去了概念和理解力。任何帮助都是非常欢迎的。

提前谢谢。


Tags: name图像importdataherematplotlibnpfig
2条回答

运行您的示例,缩放后matplotlib中的一切看起来都很好:无论分辨率如何,结果都是相同的,我看到每个轴单位有一个像素。 另外,尝试使用更小的数组时,pdf(或其他格式)也能很好地工作。

这是我的解释:当您设置图形dpi时,您设置的是整个图形的dpi(不仅仅是数据区域)。在我的系统中,这会导致绘图区域垂直占据整个图形的20%左右。如果设置300 dpi和10的高度,则垂直数据轴的总像素为300x10x0.2=600像素,这不足以表示1500个点,这就解释了为什么必须对输出重新采样。请注意,减小宽度有时会起到意外作用,因为它会更改数据绘图所占的图形部分。

然后,您必须增加dpi,并设置interpolation='none'(分辨率是否设置得完美并不重要,但只要足够接近就很重要)。 此外,还可以调整绘图位置和大小以占据图形的较大部分,但回到最佳分辨率设置,理想情况下,您希望在轴上有多个像素,即数据点的倍数,否则必须进行某种插值(请考虑如何在三个像素上绘制两个点,或viceversa)。

我不知道下面是不是最好的方法,matplotlib中可能有更合适的方法和属性,但是我会尝试这样的方法来计算最佳dpi:

vsize=ax.get_position().size[1]  #fraction of figure occupied by axes
axesdpi= int((Fig.get_size_inches()[1]*vsize)/R)  #(or Yi*R according to what you want to do)

然后,您的代码(简化为第一个循环)变成:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors

R, C = 1500, 3000
DATA = np.random.random((R, C))
DATA[::2, :] *= -1  # make every other line negative
Yi, Xi = 1, 10 # increment
CMP = 'seismic'
ImageFormat ='pdf'
Name = 'Image'


DataRange = (np.absolute(DATA)).max() # I want my data centred on 0
EXTENT = [0, Xi*C, 0 ,Yi*R]
NORM = matplotlib.colors.Normalize(vmin =-DataRange, vmax= DataRange, clip =True)

for i in (1,):
    print i 
    Fig=plt.figure(figsize=(45, 10), dpi = 100*i, tight_layout=True)
    Fig.suptitle(Name+str(i)+'00DPI')
    ax = Fig.add_subplot(1, 1, 1)
    Plot = ax.imshow(DATA, cmap=plt.get_cmap(CMP), norm = NORM, extent = EXTENT, aspect = 1, interpolation='none') 
    ax.set_xlabel('metres')
    ax.set_ylabel('metres')
    vsize=ax.get_position().size[1]  #fraction of figure occupied by axes
    axesdpi= int((Fig.get_size_inches()[1]*vsize)/R)  #(or Yi*R according to what you want to do)
    Fig.savefig(Name+str(axesdpi)+'DPI.'+ImageFormat,  format = ImageFormat, dpi = axesdpi)
    #plt.close()

这对我来说是合理的。

首先,当您保存为.pdf时,您隐式地使用pdf后端,即使您可能在选项中指定了其他后端。这意味着你的图像是以矢量格式保存的,因此dpi是毫无意义的。在任何分辨率下,如果我在一个像样的查看器中加载您的PDF(我使用了inkscape,其他的都可用),您可以清楚地看到条纹-我实际上发现,如果您将第二行设置为零,观察起来会更容易。生成的所有PDF都包含复制条纹的完整信息,因此几乎完全相同。当您指定figsize=(45, 10)时,所有生成的PDF都建议显示尺寸为45英寸x 10英寸。

如果我指定png作为图像类型,我会看到基于dpi参数的文件大小的差异,我认为这是您所期望的。如果你看100dpi图像,它有4500000个像素,200dpi图像有18000000个像素(4倍多),300dpi图像有40500000个像素(9倍多)。您将注意到4500000==1500 x 3000,即原始数组的每个成员一个像素。因此,更大的dpi设置并不能真正获得任何进一步的定义,相反,您的条纹宽度分别为2或3像素,而不是1像素。

我认为你要做的是有效地绘制每列10次,这样你得到的图像是1500 x 30000像素。为此,使用所有自己的代码,可以使用^{}执行以下操作:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors

R, C = 1500, 3000
DATA = np.random.random((R, C))
DATA[::2, :] = 0  # make every other line plain white
Yi, Xi = 1, 10 # increment
DATA = np.repeat(DATA, Xi, axis=1)
DATA = np.repeat(DATA, Yi)

CMP = 'seismic'
ImageFormat ='pdf'
Name = 'Image'


DataRange = (np.absolute(DATA)).max() # I want my data centred on 0
EXTENT = [0, Xi*C, 0 ,Yi*R]
NORM = matplotlib.colors.Normalize(vmin =-DataRange, vmax= DataRange, clip =True)

for i in range(1,4):
    Fig=plt.figure(figsize=(45, 10), dpi = 100*i, tight_layout=True)
    Fig.suptitle(Name+str(i)+'00DPI')
    ax = Fig.add_subplot(1, 1, 1)
    Plot = ax.imshow(DATA, cmap=plt.get_cmap(CMP), norm = NORM, extent = EXTENT, aspect = 1, interpolation='none') 
    ax.set_xlabel('metres')
    ax.set_ylabel('metres')
    Fig.savefig(Name+str(i)+'00DPI.'+ImageFormat,  format = ImageFormat, dpi = Fig.dpi)
plt.close()

警告:这是一个内存密集型解决方案-可能有更好的方法。如果不需要pdf的矢量图形输出,可以将ImageFormat变量更改为png


让我吃惊的是,你可能关心的另一件事是给图片适当的宽高比(即20倍于它的宽度)。你已经在做了。因此,如果你看pdf中每个像素的表示,它们是矩形的(是高度的10倍),而不是正方形的。

相关问题 更多 >