Met Post

Python、プログラミングのメモ、気象、海洋のこととか

cgiからmatplotlibで画像を表示させる

簡単な部内サイトなどを作る時、matplotlibで書いた画像を動的に表示させたい場合cgiスクリプトで動かしたい場合があると思う。 matplotlibの標準出力のサンプルを参考に、レスポンスヘッダを付加してすれば良いと思って、

import sys
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

plt.plot([1, 2, 3])

print('Content-type: image/png')
print()

plt.savefig(sys.stdout.buffer, format='png')

のように書いたところ、エラーで表示されない。Apacheのログをみると

malformed header from script 'image.cgi': Bad header: \x89PNG

となっていた。ヘッダが悪い?と思っていろいろ調べてみると、stackoverflowにそれっぽい回答が。print関数の出力が標準出力にうまく返されていない?のが原因らしい。

回答の通り以下のように書き換えたらうまく表示された。

import sys
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

plt.plot([1, 2, 3])

print('Content-type: image/png')
print()
sys.stdout.flush()

plt.savefig(sys.stdout.buffer, format='png'))

もしくは、

import sys
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

plt.plot([1, 2, 3])

sys.stdout.buffer.write(b'Content-type: image/png\n\n') 
plt.savefig(sys.stdout.buffer, format='png'))

少し前に試した時は、こんなテクニック必要なかったような気がするけどバージョン間の違いだろうか。

開発環境はpyenv & minicondaで仮想環境を構築。バージョンは、python=3.6.2, matplotlib=2.1.0, Apache=2.4