matplotlib出图细节以及提高出图质量(高dpi)

文章目录

    • 省流总结
    • 基础知识
    • 探究过程
      • matplotlib默认出图(作为参照)
      • 改变图片的默认尺寸
        • 改变matplotlib的show的dpi
      • 不同backend的影响
      • 改变matplotlib的showfig的dpi
      • 不同版本matplotlib的showfig
      • 低版本matplotlib使用Agg作为backend提高出图质量
      • 其他的情况

省流总结

  • 在生成figure时,如fig = plt.figure(figsize=(15,10))不能改变其dpi设定。
  • 根据绘图内容,需要确定保存图形最小尺寸,尺寸过小必然影响出图的质量。可以改变show()方法弹出的图像展示窗口尺寸,选出自己满意的尺寸。如发现窗口1000*800像素,显示的图形比较合理,那么就可以确定绘图的尺寸为(1000/100,800/1000),即(10,6).
  • savefig()时,传入要保存的图片的dpi即可,如dpi=600。保存图形时会自动扩大图形像素大小。
  • 选择不同的matplotlib的backendTkAggQtAgg好,无交互窗口的Agg出图比TkAggQtAgg更好。
  • 注意matplotlib版本,有些版本,如3.3.4的matplotlib的savefigshow会相互影响,出现不可预测的问题。升高或者降低版本能解决问题。

原创不易,如果本文对您有帮助的话,还请点赞收藏~

基础知识

  • 屏幕的尺寸:一般以屏幕的对角线尺寸计量,如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出图细节以及提高出图质量(高dpi)_第1张图片

改变图片的默认尺寸

这里使用的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 #3.25英寸
height = fig_height / 2.54#3.45
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.

这里的图形如下:
matplotlib出图细节以及提高出图质量(高dpi)_第2张图片
这里的一些细节就没有显示全,**说明我们改变图片大小时,有些元素是不能等比例缩放的。**如果要显示全最简单的就是增加图片的尺寸。如果真的对图片大小有限制,那么就得对绘图元素的大小进行改变。如果这里的多副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,
         }
# 对于这个AxesHostAxesSubplot,这样设定label是无效的。
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())
# 对于这个AxesHostAxesSubplot设定标签的大小,似乎只能用这种方法?
# host.axis["left"].label.set_fontsize(30)
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.再大就会被截断。显示的图如下:

matplotlib出图细节以及提高出图质量(高dpi)_第3张图片再查看保存的图片:
matplotlib出图细节以及提高出图质量(高dpi)_第4张图片保存的图片也受到了影响

不同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)

此时显示完整的超大图片。但是由于电脑屏幕的限制,窗口垂直方向上也不能显示完全。
matplotlib出图细节以及提高出图质量(高dpi)_第5张图片
但保存的图片不受任何的影响。

matplotlib出图细节以及提高出图质量(高dpi)_第6张图片这里可以看到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) # 一般论文出图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的信息:
matplotlib出图细节以及提高出图质量(高dpi)_第7张图片可以看到尺寸增加到了 3840x2800,恰好是 640*6 与 480*6

再 win 自带的画图工具里打开 demo.jpg,然后再调整回原来的大小,跟 show 图形的对比是一致的:

matplotlib出图细节以及提高出图质量(高dpi)_第8张图片到这里就能完成高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出图细节以及提高出图质量(高dpi)_第9张图片
保存的图片:

matplotlib出图细节以及提高出图质量(高dpi)_第10张图片

这里暴露了 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,
         }
# 对于这个AxesHostAxesSubplot,这样设定label是无效的。
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())
# 对于这个AxesHostAxesSubplot设定标签的大小,似乎只能用这种方法?
# host.axis["left"].label.set_fontsize(30)
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方法的。但是保存的图片却没有任何的问题:
matplotlib出图细节以及提高出图质量(高dpi)_第11张图片
matplotlib出图细节以及提高出图质量(高dpi)_第12张图片

其他的情况

在进行二维和三维混合出图的时候,也发现了一个问题,就是我们show()以及我们保存的图片的像素大小跟指定的大小有偏差,即使我们是采用Agg作为backend,也不能解决这个问题。那么最简单的解决方法,就是视情况多给一些图片尺寸或者少给图片一些尺寸,进行微调。

你可能感兴趣的