web-dev-qa-db-ja.com

ピクセル単位の正確なサイズのFigureの指定と保存

サイズ3841 x 7195ピクセルの画像があるとします。図の内容をディスクに保存したいので、結果として正確なサイズの画像がピクセル単位で指定されます。

軸もタイトルもありません。画像だけ。私はDPIを個人的に気にしません。なぜなら、画像がディスクの画面で取るサイズをピクセルで指定するだけだからです。

私は otherthreads を読んだことがあり、それらはすべてインチへの変換を行ってから、フィギュアの寸法をインチで指定し、何らかの方法でdpiを調整しているようです。ピクセルからインチへの変換から生じる可能性のある精度の潜在的な損失に対処したくない。

私は試しました:

w = 7195
h = 3841
fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(im_np, aspect='normal')
fig.savefig(some_path, dpi=1)

運がない(Pythonは幅と高さがそれぞれ32768(?)未満でなければならないと文句を言う)

私が見たすべてのものから、matplotlibは、inchesdpiで指定されたフィギュアサイズを必要としますが、私はフィギュアがディスクで取るピクセルにのみ興味があります。これどうやってするの?

明確にするために、他の画像保存ライブラリではなく、matplotlibでこれを行う方法を探しています。

113

Matplotlibはピクセルを直接処理するのではなく、物理的なサイズとDPIを処理します。特定のピクセルサイズでFigureを表示する場合は、モニターのDPIを知る必要があります。たとえば、 このリンク はそれを検出します。

3841x7195ピクセルの画像がある場合、モニターするサイズが大きくなる可能性は低いため、そのサイズの図を表示することはできません(サイズを求める場合、matplotlibは図が画面に収まることを要求します大きすぎると、画面サイズに縮小されます)。たとえば、800x800ピクセルの画像が必要な場合を考えてみましょう。モニター(my_dpi=96)に800x800ピクセルの画像を表示する方法は次のとおりです。

plt.figure(figsize=(800/my_dpi, 800/my_dpi), dpi=my_dpi)

したがって、基本的には、DPIで寸法をインチ単位で分割します。

特定のサイズのフィギュアを保存したい場合、それは別の問題です。画面のDPIはもはや重要ではありません(画面に収まらない数字を要求しない限り)。 800x800ピクセルの図の同じ例を使用して、dpisavefigキーワードを使用して、さまざまな解像度で保存できます。画面と同じ解像度で保存するには、同じdpiを使用します。

plt.savefig('my_fig.png', dpi=my_dpi)

8000x8000ピクセルの画像として保存するには、10倍大きいdpiを使用します。

plt.savefig('my_fig.png', dpi=my_dpi * 10)

DPIの設定はすべてのバックエンドでサポートされているわけではないことに注意してください。ここでは、PNGバックエンドが使用されますが、pdfバックエンドとpsバックエンドはサイズを異なる方法で実装します。また、DPIとサイズを変更すると、fontsizeなどにも影響します。 DPIを大きくすると、フォントと要素の相対的なサイズは同じに保たれますが、大きな図に小さいフォントが必要な場合は、DPIではなく物理サイズを大きくする必要があります。

例に戻って、3841 x 7195ピクセルの画像を保存する場合は、次のことができます。

plt.figure(figsize=(3.841, 7.195), dpi=100)
( your code ...)
plt.savefig('myfig.png', dpi=1000)

ほとんどの画面に収まるように100のFigure dpiを使用しましたが、必要な解像度を達成するためにdpi=1000で保存しました。私のシステムでは、これは3840x7190ピクセルのpngを生成します-保存されるDPIは常に選択された値よりも0.02ピクセル/インチ小さく、大きな画像サイズに(小さな)効果があります。これについてもう少し議論します こちら

125
tiago

あなたのコードに基づいて、これは私のために働いて、色ノイズと所望の寸法で93Mb png画像を生成しました:

import matplotlib.pyplot as plt
import numpy

w = 7195
h = 3841

im_np = numpy.random.Rand(h, w)

fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(im_np, aspect='normal')
fig.savefig('figure.png', dpi=1)

Linux Mint 13のPython 2.7ライブラリの最新のPIPバージョンを使用しています。

お役に立てば幸いです!

14
heltonbiker

Tiagoが受け入れた応答に基づいて、numpy配列を配列と同じ解像度の画像にエクスポートする小さな汎用関数を次に示します。

import matplotlib.pyplot as plt
import numpy as np

def export_figure_matplotlib(arr, f_name, dpi=200, resize_fact=1, plt_show=False):
    """
    Export array as figure in original resolution
    :param arr: array of image to save in original resolution
    :param f_name: name of file where to save figure
    :param resize_fact: resize facter wrt shape of arr, in (0, np.infty)
    :param dpi: dpi of your screen
    :param plt_show: show plot or not
    """
    fig = plt.figure(frameon=False)
    fig.set_size_inches(arr.shape[1]/dpi, arr.shape[0]/dpi)
    ax = plt.Axes(fig, [0., 0., 1., 1.])
    ax.set_axis_off()
    fig.add_axes(ax)
    ax.imshow(arr)
    plt.savefig(f_name, dpi=(dpi * resize_fact))
    if plt_show:
        plt.show()
    else:
        plt.close()

Tiagoによる前回の返信で述べたように、画面DPIを最初に見つける必要があります。これは、たとえばここで実行できます。 http://dpi.lv

たとえば、元の解像度の50%(0.5)に画像をエクスポートできる関数に追加の引数resize_factを追加しました。

4
Cyril