matplotlibで複雑な画像をPDFで保存する

matplotlibで作成したグラフのデータ数が10万ポイントくらいになると、保存したPDFビューアなどで表示させるのに時間がかかります。そんなとき、プロットエリアだけをラスタ化する方法を紹介します。


カテゴリ: tips  2021-04-18 16:53:12

## 背景 最近は実験結果を図示する際に、Pythonとmatplotlibを使っています。 画像をPDFに出力できるので便利です。ベクタ情報 (\*1)なので拡大してもジャギが出ないし、文字情報もそのまま残せます。 けれども、プロット数が多くなったり(数万ポイント程度)、複雑なグラデーションがかかっていたりする場合は、ベクタ情報には不向きです。特にプロット数が多い場合、PDFビューアで表示させるのに時間がかかります。 ここでは、テキストなど一部の情報をPDFに保持しつつ、プロットエリアをラスタ形式(\*2)で保存する方法を紹介します。 ## ラスタ形式の指定 方法はシンプルです。ラスタ形式に変換したい画像を描画するコマンドに、`rasterized=True`オプションを追加するだけです。 例えば、matplotlibでplotする場合は以下のようになります。 ``` plt.plot(x, y, rasterized=True) ``` ## 例 実際に、10万ポイントをランダムに打った散布図を作成してみます。 以下のような画像を作成することにします。 ![vector.png](/articles/files/f4c62cfd0b9457071454534294f3d3a7/vector.png) ### ベクタ形式の場合 まずはベクタ形式として保存してみます。 ``` import matplotlib.pyplot as plt import numpy as np NUM_POINTS = 100000 x = np.random.rand(NUM_POINTS) y = np.random.rand(NUM_POINTS) plt.figure(figsize=(6, 6)) plt.scatter(x, y, s=10, edgecolors='white') plt.title('Vector Image') plt.savefig('vector.pdf') ``` 生成されたPDFの例です。**表示されるのに時間がかかるので、開く際は注意してください。** [vector.pdf](/articles/files/f4c62cfd0b9457071454534294f3d3a7/vector.pdf) きちんと表示出来た場合は、図のタイトルや軸の値が文字として選択可能であることが確認できると思います。けれども、やはりプロットを描画し終えるまでに時間がかかると思います。 ### プロット部のみラスタ形式の場合 次はプロット部分だけをラスタ形式にしてみます。 `plt.scatter`で散布図を描画する際に、`rasterized=True`オプションを追加しています。 ``` import matplotlib.pyplot as plt import numpy as np NUM_POINTS = 100000 x = np.random.rand(NUM_POINTS) y = np.random.rand(NUM_POINTS) plt.figure(figsize=(6, 6)) plt.scatter(x, y, s=10, edgecolors='white', rasterized=True) plt.title('Partially Rasterized Image') plt.savefig('partially-rasterized.pdf', dpi=200) ``` 生成されたPDFの例です。こちらは、プロット部分もすぐに表示されると思います。また、タイトルや軸はラスタ化していないので、これらは文字として選択できると思います。 [partially-rasterized.pdf](/articles/files/f4c62cfd0b9457071454534294f3d3a7/partially-rasterized.pdf) ### 画像の解像度について なお、プロットをラスタ形式にすると、当然のことながらラスタ画像のデメリットである、*拡大するとぼやけてしまう*ことがあります。 この場合、画像を保存する際に`plt.save`メソッドのオプションで`dpi=XXX`としてXXXに適当な数値を指定すると良いです。dpiは印刷の解像度を示す値で、一般的には200-400程度でそこそこの解像度になるはずです。なお、dpiを上げすぎるとPDFの容量が大きくなります。 ## まとめ matplotlibで、画像の一部をラスタ化しました。 ポイントは`rasterized=True`オプションです。 画像を`plt.save`で保存する際は`dpi=XXX`オプションで適当な解像度を指定すると良いです。



\*1: **ベクタ情報**: 図形を構成する点の位置や線の大きさ、向きを情報として保持しています。図形を表示する際はこれらの情報を計算して表示させるので、拡大してもぼやけることが無いです。また、単純な図形だけで構成された画像の場合は、ベクタ形式の方が高解像度な画像を小容量で保存できます。 参考: https://ja.wikipedia.org/wiki/%E3%83%99%E3%82%AF%E3%82%BF%E3%83%BC%E7%94%BB%E5%83%8F \*2: **ラスタ情報**: ビットマップ情報とも言います。画像を点の集まりで表現します。高解像度にするほど点の数が多くなるので、その分容量も必要になります。幾何学的に表現できない複雑な画像(自然界の写真など)はラスタ形式の方が向いています。 参考: https://ja.wikipedia.org/wiki/%E3%83%93%E3%83%83%E3%83%88%E3%83%9E%E3%83%83%E3%83%97%E7%94%BB%E5%83%8F