質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

8094閲覧

[Matplotlib] plt.savefig()で画像サイズが半分になってしまう。。

tetsutail_study

総合スコア25

Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/09/10 06:56

前提・実現したいこと

下記コード(該当部分)のfor文では動画を作ると同時にフレームを保存しています。
fig = plt.figure(frameon=False, figsize=(nx / dpi, ny / dpi))で動画や画像pixelサイズは1280×720になるように指定したつもりでした。
しかし動画は1280×720で保存されるものの、フレーム画像は640×360になってしまいます。
何が原因でしょうか?

該当のソースコード

python

1fig = plt.figure(frameon=False, figsize=(nx / dpi, ny / dpi)) 2 ax = fig.add_subplot(111) 3 scat = ax.scatter([], [], s=dotsize ** 2, alpha=alpha) 4 coords = xyp[0, :, :2] 5 coords[xyp[0, :, 2] < pcutoff] = np.nan 6 scat.set_offsets(coords) 7 colors = cmap(map_) 8 scat.set_color(colors) 9 segs = coords[tuple(zip(*tuple(ind_links))), :].swapaxes(0, 1) if ind_links else [] 10 coll = LineCollection(segs, colors=skeleton_color, alpha=alpha) 11 ax.add_collection(coll) 12 ax.set_xlim(0, nx) 13 ax.set_ylim(0, ny) 14 ax.axis("off") 15 ax.add_patch( 16 plt.Rectangle( 17 (0, 0), 1, 1, facecolor=background_color, transform=ax.transAxes, zorder=-1 18 ) 19 ) 20 ax.invert_yaxis() 21 plt.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0) 22 23 writer = FFMpegWriter(fps=fps, codec=codec) 24 with writer.saving(fig, output_name, dpi=dpi): 25 writer.grab_frame() 26 for index, _ in enumerate(trange(n_frames), start=0): 27 coords = xyp[index, :, :2] 28 coords[xyp[index, :, 2] < pcutoff] = np.nan 29 scat.set_offsets(coords) 30 if ind_links: 31 segs = coords[tuple(zip(*tuple(ind_links))), :].swapaxes(0, 1) 32 coll.set_segments(segs) 33 imagename = dirname + '{}_{}.png'.format(vname, str(index).zfill(12)) 34 plt.savefig(imagename) 35 writer.grab_frame() 36 plt.close(fig) 37 plt.switch_backend(prev_backend)

試したこと

とりあえず以下を挿入し、毎回サイズを倍にしたら1280×720になりました。
根本的な解決ではないので、上記コードの失敗の原因を知りたいです。
よろしくお願いします!

python

12imagename = dirname + '{}_{}.png'.format(vname, str(index).zfill(12)) 3fig.set_figheight(ny / dpi) ←ここ 4fig.set_figwidth(nx / dpi) ←ここ 5plt.savefig(imagename) 6

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

etherbeg

2021/09/11 12:33 編集

dpi, nx, ny の値を設定する部分がソースコードに含まれていませんので追加してください。 1行目の "fig = plt.figure(frameon=False, figsize=(nx / dpi, ny / dpi))" とそれ以外の行のインデントが揃っていませんので訂正してください。
tetsutail_study

2021/09/12 14:47

回答ありがとうございます! 質問のコードに含んでいなかったのですが実はnx=1280、ny=720、dpi=200と上で定義しています。申し訳ございません。。
guest

回答1

0

画像を保存する際の、ピクセルサイズの決定に関係する値は、以下の3つです。

  1. Figure オブジェクトのサイズ(単位: インチ)
  2. Figure オブジェクトの解像度(単位: dpi)
  3. savefig() メソッドで保存する際の解像度(単位: dpi)

ユーザのコードの中でこれらの値が指定された場合は、その値が使われます。
ユーザのコードの中でこれらの値が指定されなかった場合は、matplotlib の初期値が使われます。

私の環境(macOS 11.5、matplotlob 3.4.3)でのこれらの値の初期値は以下の通りです。

python

1>>> import matplotlib.pyplot as plt 2>>> plt.rcParams["figure.figsize"] # Figure オブジェクトのインチサイズ 3[6.4, 4.8] 4>>> plt.rcParams["figure.dpi"] # Figure オブジェクトの解像度 dpi 5100.0 6>>> plt.rcParams["savefig.dpi"] # Figure.savefig() メソッドで使用される解像度 dpi 7'figure'

plt.rcParams["savefig.dpi"] の値が "figure" という文字列ですが、これは Figure オブジェクトの解像度を savefig() メソッドでもそのまま使用することを意味します。

仮に質問文における matplotlib の初期値もこれも同じであると仮定して、質問文中のコードに沿って、画像のピクセルサイズがどのように決定されているかを見ていきます。まず、

python

1nx = 1280 2ny = 720 3dpi = 200 4 5fig = plt.figure(frameon=False, figsize=(nx / dpi, ny / dpi))

ここで明示的に指定されているのは Figure オブジェクトのサイズ figsize のみです。dpi を明示的に指定する場合はキーワード引数 dpi に値を渡すか、Figure オブジェクトの set_dpi() メソッドを使いますが、いずれも使われていないので、 dpi は指定されていません。

fig = plt.figure(frameon=False, figsize=(nx / dpi, ny / dpi))

fig = plt.figure(frameon=False, figsize=(6.4, 3.6))

と同じです。
dpi は指定されていないので、初期値の 100.0 が使われます。
つまりこの Figure オブジェクトのピクセルサイズは

(width, height) = (6.4 * 100.0, 3,6 * 100.0) = 640.0, 360.0

です。

次に、

python

1with writer.saving(fig, output_name, dpi=dpi):

ここでは、書き出される動画ファイルのピクセルサイズが、キーワード引数 dpi に値を渡すことによって、明示的に決定されています。
先に dpi = 200 と変数 dpi に値 200 が入力されているので、ここで dpi 値として渡されている値は 200 です。
Figure オブジェクトのサイズは、saving() に渡された Figure オブジェクト (fig) のサイズがそのまま使われます。
つまりここで保存される動画のピクセルサイズは

(width, height) = (6.4 * 200.0, 3,6 * 200.0) = 1280.0, 720.0

です。

次に、

python

1plt.savefig(imagename)

ここでは書き出される画像のサイズについては、何も指示されていません。
dpi 値は明示されていないので、plt.rcParams["savefig.dpi"] の初期値である "figure"、つまり Figure オブジェクトの解像度が使われます。Figure オブジェクトの解像度は、コード中でここまで明示的に設定されていないので、matplotlib の初期値である 100.0 です。
Figure オブジェクトのサイズは、現在唯一の Figure オブジェクトである fig のサイズがそのまま使われます。
つまりここで保存される画像のピクセルサイズは

(width, height) = (6.4 * 100.0, 3,6 * 100.0) = 640.0, 360.0

です。

動画ファイルのピクセルサイズと同じピクセルサイズで保存したければ、明示的に動画ファイルと同じ dpi を設定します。

python

1plt.savefig(imagename, dpi=dpi) 2 3# もしくは 4# plt.savefig(imagename, dpi=200)

plt.savefig() は動画ファイルのフレーム画像を保存しているのではなく、Figure オブジェクトから直接静止画像を生成していることに注意してください。


とりあえず以下を挿入し、毎回サイズを倍にしたら1280×720になりました

python

1fig.set_figheight(ny / dpi) 2fig.set_figwidth(nx / dpi) 3

fig.set_figheight(3.6) fig.set_figwidth(6.4)

と同義であり、Figure オブジェクトのサイズをまったく変更していません。
つまりこの2行を挿入するだけで、plt.savefig() で保存される画像のピクセルサイズが変更されることはあり得ません。
必ずどこか別のところで行われた変更が反映しています。

投稿2021/09/13 05:13

編集2021/09/15 14:44
etherbeg

総合スコア1195

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問