web-dev-qa-db-ja.com

matplotlib.pyplot、imshow()、savefig()を使用してフル解像度でプロットしますか?

画像であるため、大規模にプロットしたい中規模の配列(1500x3000など)があります。ただし、垂直方向と水平方向のスケールは大きく異なります。簡単にするために、1メートル/行と10 /列があるとしましょう。プロットは、cである画像を生成する必要があります。 1500x30000。変形を避けるために、スケールとアスペクト= 1にクワーグ範囲を使用します。プロットウィンドウ(QT4)とimshow()を使用するか、savefig()を使用することにより、スケールとフル解像度で画像を作成することに成功しませんでした。

herehere 、に示されているように、私は多くの提案された解決策を見てきました。または here および there または バグだった場合に備えてthere 。 matplotlibrcを変更し、〜/ .config/matplotlibに配置して、display/savefigオプションを強制しようとしましたが、役に立ちませんでした。私もpcolormesh()で試しましたが、成功しませんでした。 Ubuntu14.04とQT4Aggのリポジトリのpython 2.7とmatplotlib1.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()

Imshow()では、補間= 'none'または 'nearest'または 'bilinear'は、savefig()の代わりにshow()を実行すると、少なくともQt4ウィンドウで解像度が変更されるはずですが、何らかの理由で解像度が変更されません。 。 plt.figure(dpi =)で設定したものが何であれ、保存された図の解像度は同じであることに注意してください。

私はアイデアがなく、このシステムで物事がどのように機能するかについての理解の限界にあります。どんな助けでも大歓迎です。

前もって感謝します。

14
Boorhin

例を実行すると、ズーム後のmatplotlibではすべてが良好に見えます。解像度に関係なく、結果は同じで、軸単位ごとに1ピクセルが表示されます。また、小さい配列で試してみると、PDF(または他の形式)がうまく機能します。

これは私の説明です:図のdpiを設定すると、(データ領域だけでなく)図全体のdpiが設定されます。私のシステムでは、これにより、プロット領域が全体の約20%を垂直に占めることになります。図。300dpiと高さ10を設定すると、垂直データ軸に対して合計300x10x0.2 = 600ピクセルが得られますが、これは1500ポイントを表すには不十分です。これは、出力をリサンプリングする必要がある理由を説明しています。幅を小さくすると、データプロットが占める図の割合が変わるため、偶然に機能する場合があることに注意してください。

次に、dpiを上げ、interpolation = 'none'を設定する必要があります(解像度が完全に設定されているかどうかは問題ではありませんが、十分に近いかどうかは重要です)。また、プロットの位置とサイズを調整して図の大部分を占めることもできますが、最適な解像度設定に戻ると、理想的には、軸上にデータポイントの倍数であるピクセル数が必要です。ある種の補間が発生する必要があります(3つのピクセルに2つのポイントをプロットする方法、またはその逆を考えてください)。

以下がそれを行うための最良の方法であるかどうかはわかりません。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)

次に、コード(最初のループに縮小)は次のようになります。

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()

これは私にとって合理的に機能します。

2
Vincenzooo

まず、_.pdf_として保存する場合、オプションで他のバックエンドを指定している場合でも、暗黙的にpdfバックエンドを使用しています。これは、画像がベクター形式で保存されるため、dpiはまったく意味がないことを意味します。どの解像度でも、あなたのPDFをまともなビューアにロードすると(私はinkscapeを使用しましたが、他のものもあります)、ストライプをはっきりと見ることができます-設定すると実際に観察しやすくなりました生成されたすべてのPDFには、ストライプを再現するための完全な情報が含まれているため、実質的に同一です。figsize=(45, 10)を指定すると、生成されたすべてのPDFの表示サイズは45インチx10インチになります。

画像タイプとしてpngを指定すると、dpiパラメータに基づいてファイルサイズに違いが見られます。これは、あなたが期待していることだと思います。 100 dpiの画像を見ると、4500000、200 dpiの画像は18000000ピクセル(4倍)、300 dpiの画像は40500000(9倍)です。 4500000 == 1500 x 3000、つまり元の配列のメンバーごとに1ピクセルであることがわかります。したがって、dpiの設定を大きくしても、実際にはそれ以上の定義は得られません。代わりに、ストライプの幅は1ピクセルではなく2ピクセルまたは3ピクセルになります。

thinkは、すべての列を10回効果的にプロットすることで、1500 x30000ピクセルの画像を取得します。これを行うには、独自のコードをすべて使用して、 _np.repeat_ を使用して次のようなことを行うことができます。

_import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors

R, C = 1500, 3000
DATA = np.random.random((R, C))
DATA[::2, :] = 0  # make every other line plain white
Yi, Xi = 1, 10 # increment
DATA = np.repeat(DATA, Xi, axis=1)
DATA = np.repeat(DATA, Yi)

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()
_

警告:これはメモリを大量に消費するソリューションです。もっと良い方法があるかもしれません。 pdfのベクターグラフィックス出力が必要ない場合は、ImageFormat変数をpngに変更できます。


あなたが懸念しているかもしれない他のことは、写真に適切なアスペクト比(つまり、高さの20倍の幅)を与えることだと私は思います。これはあなたがすでにやっている。したがって、pdf内のピクセルの各表現を見ると、正方形ではなく長方形(高さの10倍の幅)です。

1
J Richard Snape