使用matplotlib.pyplot,imshow()和savefig()以全分辨率绘图?

Boo*_*hin 13 python plot matplotlib imshow

我有一个中等大小的数组(例如1500x3000)我想要按比例绘制,因为它是一个图像.但是,垂直和水平刻度是非常不同的.为简化起见,假设有一米/行和10 /列.然后该图应该产生一个c的图像.1500x30000.我使用kwarg范围为scale和aspect = 1以避免变形.通过使用绘图窗口(QT4)和imshow()或使用savefig(),我从未成功地以大规模和全分辨率生成图像.

我看过很多提出的解决方案,如表示在这里,在这里,或在这里那里存在的情况下,这是一个错误.我已经改变了我的matplotlibrc并将其放在〜/ .config/matplotlib中以尝试强制我的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()
Run Code Online (Sandbox Code Playgroud)

在imshow()中,interpolation ='none'或'nearest'或'bilinear'由于某些原因不会改变分辨率,尽管我认为至少在Qt4窗口中如果我执行show()而不是savefig() .请注意,无论您在plt.figure(dpi =)中设置了什么,保存的数字中的分辨率都是相同的.

我对这个系统的工作原理非常缺乏理解.非常欢迎任何帮助.

提前致谢.

Vin*_*ooo 3

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

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

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

我不知道以下是否是最好的方法,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)
Run Code Online (Sandbox Code Playgroud)

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

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()
Run Code Online (Sandbox Code Playgroud)

这对我来说很合理。