省流总结
- 在生成figure时,如
fig = plt.figure(figsize=(15,10)) 不能改变其dpi设定。 - 根据绘图内容,需要确定保存图形最小尺寸,尺寸过小必然影响出图的质量。可以改变
show() 方法弹出的图像展示窗口尺寸,选出自己满意的尺寸。如发现窗口1000*800像素,显示的图形比较合理,那么就可以确定绘图的尺寸为(1000/100,800/1000),即(10,6). savefig() 时,传入要保存的图片的dpi即可,如dpi=600 。保存图形时会自动扩大图形像素大小。- 选择不同的matplotlib的
backend 。TkAgg 比QtAgg 好,无交互窗口的Agg 出图比TkAgg 和QtAgg 更好。 - 注意matplotlib版本,有些版本,如
3.3.4 的matplotlib的savefig 和show 会相互影响,出现不可预测的问题。升高或者降低版本能解决问题。
原创不易,如果本文对您有帮助的话,还请点赞收藏~
基础知识
- 屏幕的尺寸:一般以屏幕的对角线尺寸计量,如15.6寸。
- 屏幕分辨率:是值屏幕水平和竖直方向上拥有的物理像素点数。如:分辨率是 1920x1080。
- ppi: 屏幕每英寸的像素密度是ppi。比如我的电脑屏幕尺寸 15.6,分辨率是 1920x1080,用长(1920)跟高的像素数(1080)计算出对角方向的像素数(直角三角形)(2202),然后再用对角的像素数除以屏幕尺寸(15.6),就得到ppi,即141. 如果我们指定显示图像的显示像素,那么由ppi,就可以得到图像在电脑上显示的物理尺寸。然而我们对电脑屏幕截图时,生成的图像大小的dpi一般是100或者96,更有甚者是72. matplotlib的
show() 的dpi,我更倾向于理解为它是一种dpi,指定100即可,太高在屏幕上还要进行扩大,并不能提高显示质量。 - dpi: dpi是打印机的像素密度。而showfig()里的dpi就是这个概念。在科研绘图中,我们一般被要求图片的dpi至少要为300. 本文就是要分析如何生成高质量的高dpi图片。主要是对比不同例子之间的显示差距,方便读者体会。
探究过程
matplotlib默认出图(作为参照)
这里使用的matplotlib的版本是3.4.0,完全使用matplotlib的默认参数。
from mpl_toolkits.axes_grid1 import host_subplot
from mpl_toolkits import axisartist
import matplotlib
import matplotlib.pyplot as plt
host = host_subplot(111, axes_class=axisartist.Axes)
fig=plt.gcf()
print(fig)
plt.subplots_adjust(right=0.75)
par1 = host.twinx()
par2 = host.twinx()
par2.axis["right"] = par2.new_fixed_axis(loc="right", offset=(60, 0))
par1.axis["right"].toggle(all=True)
par2.axis["right"].toggle(all=True)
p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature")
p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")
host.set_xlim(0, 2)
host.set_ylim(0, 2)
par1.set_ylim(0, 4)
par2.set_ylim(1, 65)
host.set_xlabel("Distance")
host.set_ylabel("Density")
par1.set_ylabel("Temperature")
par2.set_ylabel("Velocity")
host.legend()
host.axis["left"].label.set_color(p1.get_color())
par1.axis["right"].label.set_color(p2.get_color())
par2.axis["right"].label.set_color(p3.get_color())
fig=plt.gcf()
print(fig)
plt.savefig("demo.jpg",dpi=100)
fig=plt.gcf()
print(fig)
plt.show()
fig=plt.gcf()
print(fig)
控制台打印结果:
Backend Qt5Agg is interactive backend. Turning interactive mode on.
Figure(640x480)
Figure(640x480)
Figure(640x480)
Figure(640x480)
改变图片的默认尺寸
这里使用的matplotlib的版本是3.4.0。 仅改变figure的尺寸,这里缩小一倍:
from mpl_toolkits.axes_grid1 import host_subplot
from mpl_toolkits import axisartist
import matplotlib
import matplotlib.pyplot as plt
fig_width = 8.26
fig_height = 8.77
width = fig_width / 2.54
height = fig_height / 2.54
fig=plt.figure(figsize=(width,height))
print(fig)
host = host_subplot(111, axes_class=axisartist.Axes)
fig=plt.gcf()
print(fig)
plt.subplots_adjust(right=0.75)
par1 = host.twinx()
par2 = host.twinx()
par2.axis["right"] = par2.new_fixed_axis(loc="right", offset=(60, 0))
par1.axis["right"].toggle(all=True)
par2.axis["right"].toggle(all=True)
p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature")
p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")
host.set_xlim(0, 2)
host.set_ylim(0, 2)
par1.set_ylim(0, 4)
par2.set_ylim(1, 65)
host.set_xlabel("Distance")
host.set_ylabel("Density")
par1.set_ylabel("Temperature")
par2.set_ylabel("Velocity")
host.legend()
host.axis["left"].label.set_color(p1.get_color())
par1.axis["right"].label.set_color(p2.get_color())
par2.axis["right"].label.set_color(p3.get_color())
fig=plt.gcf()
print(fig)
plt.savefig("demo.jpg",dpi=100)
fig=plt.gcf()
print(fig)
plt.show()
fig=plt.gcf()
print(fig)
控制台打印出的结果:
Backend Qt5Agg is interactive backend. Turning interactive mode on.
Figure(325x345)
Figure(325x345)
Figure(325x345)
Figure(325x345)
Figure(325x345)
可以看到由于show指定的 dpi 是 100,而屏幕显示一般ppi就是100.而绘图的大小是 3.25*3.45 英寸。所以占屏幕像素的大小是 325*345.
这里的图形如下: 这里的一些细节就没有显示全,**说明我们改变图片大小时,有些元素是不能等比例缩放的。**如果要显示全最简单的就是增加图片的尺寸。如果真的对图片大小有限制,那么就得对绘图元素的大小进行改变。如果这里的多副y轴的参数。 另外有一点非常重要,就是绘图内容的合理布局需要给它提供合理的尺寸。这个合理的尺寸可以通过手动调节show()弹出窗口,然后测量窗口的尺寸,将其尺寸更新到figure里面,比如如窗口1000*800像素,显示的图形比较合理,那么就可以确定绘图的尺寸为(1000/100,800/1000),即(10,6).
改变matplotlib的show的dpi
这里使用的matplotlib的版本是3.4.0。show()的dpi设定为600.
from mpl_toolkits.axes_grid1 import host_subplot
from mpl_toolkits import axisartist
import matplotlib
matplotlib.use("Qt5Agg")
import matplotlib.pyplot as plt
fig_width = 8.26
fig_height = 8.77
width = fig_width / 2.54
height = fig_height / 2.54
fig=plt.figure(figsize=(6.4,4.8),dpi=600)
print(fig)
host = host_subplot(111, axes_class=axisartist.Axes)
fig=plt.gcf()
print(fig)
plt.subplots_adjust(right=0.75)
par1 = host.twinx()
par2 = host.twinx()
par2.axis["right"] = par2.new_fixed_axis(loc="right", offset=(60, 0))
par1.axis["right"].toggle(all=True)
par2.axis["right"].toggle(all=True)
p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature")
p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")
host.set_xlim(0, 2)
host.set_ylim(0, 2)
par1.set_ylim(0, 4)
par2.set_ylim(1, 65)
font2 = {'family' : 'Times New Roman',
'weight' : 'normal',
'fontsize' : 30,
}
host.set_xlabel("Distance",fontdict=font2)
host.set_ylabel("Density")
par1.set_ylabel("Temperature")
par2.set_ylabel("Velocity")
host.legend()
host.axis["left"].label.set_color(p1.get_color())
par1.axis["right"].label.set_color(p2.get_color())
par2.axis["right"].label.set_color(p3.get_color())
fig=plt.gcf()
print(fig)
plt.savefig("demo.jpg")
fig=plt.gcf()
print(fig)
fig.show()
fig=plt.gcf()
print(fig)
控制台输出:
Backend Qt5Agg is interactive backend. Turning interactive mode on.
Figure(3290x1015)
Figure(3290x1015)
Figure(3290x1015)
Figure(3290x1015)
Figure(3290x1015)
这里可以看到QtAgg的像素的垂直最大值就1050.再大就会被截断。显示的图如下:
再查看保存的图片: 保存的图片也受到了影响
不同backend的影响
在matplotlib 3.4.0版本上,换用TkAgg作为show的backend,并再次运行上节代码:
控制台输出为
Backend TkAgg is interactive backend. Turning interactive mode on.
Figure(3840x2880)
Figure(3840x2880)
Figure(3840x2880)
Figure(3840x2880)
Figure(3840x2880)
此时显示完整的超大图片。但是由于电脑屏幕的限制,窗口垂直方向上也不能显示完全。 但保存的图片不受任何的影响。
这里可以看到TkAgg比Qt5Agg好不少,但是show()这个函数没必要设定dpi,不然会改变图片的比例以及显示质量。
改变matplotlib的showfig的dpi
这里使用的matplotlib的版本是3.4.0。
from mpl_toolkits.axes_grid1 import host_subplot
from mpl_toolkits import axisartist
import matplotlib
import matplotlib.pyplot as plt
fig_width = 8.26
fig_height = 8.77
width = fig_width / 2.54
height = fig_height / 2.54
fig=plt.figure(figsize=(6.4,4.8))
print(fig)
host = host_subplot(111, axes_class=axisartist.Axes)
fig=plt.gcf()
print(fig)
plt.subplots_adjust(right=0.75)
par1 = host.twinx()
par2 = host.twinx()
par2.axis["right"] = par2.new_fixed_axis(loc="right", offset=(60, 0))
par1.axis["right"].toggle(all=True)
par2.axis["right"].toggle(all=True)
p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature")
p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")
host.set_xlim(0, 2)
host.set_ylim(0, 2)
par1.set_ylim(0, 4)
par2.set_ylim(1, 65)
host.set_xlabel("Distance")
host.set_ylabel("Density")
par1.set_ylabel("Temperature")
par2.set_ylabel("Velocity")
host.legend()
host.axis["left"].label.set_color(p1.get_color())
par1.axis["right"].label.set_color(p2.get_color())
par2.axis["right"].label.set_color(p3.get_color())
fig=plt.gcf()
print(fig)
plt.savefig("demo.jpg",dpi=600)
fig=plt.gcf()
print(fig)
plt.show()
fig=plt.gcf()
print(fig)
关于 fig 的控制台输出:
Backend Qt5Agg is interactive backend. Turning interactive mode on.
Figure(640x480)
Figure(640x480)
Figure(640x480)
Figure(640x480)
Figure(640x480)
输出demo.jpg 的信息: 可以看到尺寸增加到了 3840x2800,恰好是 640*6 与 480*6
再 win 自带的画图工具里打开 demo.jpg,然后再调整回原来的大小,跟 show 图形的对比是一致的:
到这里就能完成高dpi的出图的。但是如果你的python版本是3.6,那不能升级matplotlib为3.4.0以上的版本。你使用showfig会出现问题的。
不同版本matplotlib的showfig
如果是matplotlib的版本是3.3.4,运行上一节代码,那么这里大概率就出错了,3.3.4的控制台信息为:
Backend Qt5Agg is interactive backend. Turning interactive mode on.
Figure(640x480)
Figure(640x480)
Figure(640x480)
Figure(106.667x80)
Figure(106.667x80)
show的图片十分诡异:
保存的图片:
这里暴露了 matplotlib版本对出图质量的影响。3.3.4版本的savefig的dpi设定会影响到show() 。即使这里切换到TkAgg也一样。那么就只能切换到Agg的backend.
低版本matplotlib使用Agg作为backend提高出图质量
from mpl_toolkits.axes_grid1 import host_subplot
from mpl_toolkits import axisartist
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig_width = 8.26
fig_height = 8.77
width = fig_width / 2.54
height = fig_height / 2.54
fig=plt.figure(figsize=(6.4,4.8))
print(fig)
host = host_subplot(111, axes_class=axisartist.Axes)
fig=plt.gcf()
print(fig)
plt.subplots_adjust(right=0.75)
par1 = host.twinx()
par2 = host.twinx()
par2.axis["right"] = par2.new_fixed_axis(loc="right", offset=(60, 0))
par1.axis["right"].toggle(all=True)
par2.axis["right"].toggle(all=True)
p1, = host.plot([0, 1, 2], [0, 1, 2], label="Density")
p2, = par1.plot([0, 1, 2], [0, 3, 2], label="Temperature")
p3, = par2.plot([0, 1, 2], [50, 30, 15], label="Velocity")
host.set_xlim(0, 2)
host.set_ylim(0, 2)
par1.set_ylim(0, 4)
par2.set_ylim(1, 65)
font2 = {'family' : 'Times New Roman',
'weight' : 'normal',
'fontsize' : 30,
}
host.set_xlabel("Distance",fontdict=font2)
host.set_ylabel("Density")
par1.set_ylabel("Temperature")
par2.set_ylabel("Velocity")
host.legend()
host.axis["left"].label.set_color(p1.get_color())
par1.axis["right"].label.set_color(p2.get_color())
par2.axis["right"].label.set_color(p3.get_color())
fig=plt.gcf()
print(fig)
plt.savefig("demo.jpg",dpi=600)
fig=plt.gcf()
print(fig)
fig.show()
fig=plt.gcf()
print(fig)
控制台输出:
Figure(640x480)
Figure(640x480)
Figure(640x480)
Figure(640x480)
demo_host_subplot_raw.py:68: UserWarning: Matplotlib is currently using agg, which is a non-GUI backend, so cannot show the figure.
fig.show()
Figure(640x480)
这里可以看到Agg的backend是不支持show方法的。但是保存的图片却没有任何的问题:
其他的情况
在进行二维和三维混合出图的时候,也发现了一个问题,就是我们show()以及我们保存的图片的像素大小跟指定的大小有偏差,即使我们是采用Agg作为backend,也不能解决这个问题。那么最简单的解决方法,就是视情况多给一些图片尺寸或者少给图片一些尺寸,进行微调。
|