Google ColaboratoryでPILで何か描画して表示する

TL;DR

Google Colaboratoryでなにか描画して表示するにはIPython.display.displayを使うと良い。

はじめに

Google Colaboratory上で何かイメージを描画し、それを表示するのは意外に面倒だった。その方法をいくつか紹介する。

PILによるイメージ描画

まず、何か絵を描く。個人的にはCairoに慣れているのでpycairoを使いたいが、Google Colab上ではpycairoが入らない。import cairoすると無いと言われ、!pip install pycairoすると失敗する。もともとCairoのライブラリが入っていないらしい。というわけでPILを使う。

from PIL import Image, ImageDraw
im = Image.new("RGB", (256,256))
draw = ImageDraw.Draw(im)
draw.ellipse((64,64,192,192),fill=(255,0,0))

これで、イメージに黒字に赤い丸が描画されたはずである。以下はセルの実行結果であるが、実行結果であるイメージは表示されない。imに格納されただけである。

image0.png

どうにかしてそれを表示しましょう、というのが本稿の目的である。

案1: matplotlib+numpyを使う

まず、イメージデータをnumpy配列に変換し、matplotlibのpyplot.imshowを使う、という方法がある。

import matplotlib.pyplot as plt
import numpy as np
plt.imshow(np.array(im))

image1.png

一応表示されるのだが、不要な軸があったりする。

(2021年4月9日追記)

いま同じことをすると、軸が表示されないようですね。どこかでデフォルト設定が変わった?

image2.png

案2: バッファに吐いてIPython.display.Imageで表示

一度io.BytesIOで作ったバッファに保存し、そのバッファをIPython.display.Imageで表示する。

import io
buf = io.BytesIO()
im.save(buf,"PNG")
data = buf.getvalue()
IPython.display.Image(data)

PILのImage.saveは、ファイル名を指定する場合はタイプを自動判別してくれるが、バッファに吐く時にはタイプを明示しないといけないことに注意。実行するとこうなる。

image3.png

余計な軸などが表示されず、イメージがそのまま表示されるのでいい感じである。

案3: 一度ファイルに吐いてIPython.display.Imageで表示

先の例では一度バッファに吐いて、getvalue()で生データを取得し、それをIPython.display.Imageに食わせていた。それなら一度ファイルに吐いてしまった方が楽な気もする。

import IPython
im.save("test.png")
IPython.display.Image("test.png")

image4.png

まとめ

Google Colabで「お絵かき」をするのは意外に面倒くさい。3つの方法を紹介したが、個人的にはファイルに吐いてしまって表示する方法が一番楽な気がする。というわけで、Google Colab上で「お絵かき」をするサンプルはこんな感じになる。

from PIL import Image, ImageDraw
import IPython
im = Image.new("RGB", (256,256))
draw = ImageDraw.Draw(im)
draw.ellipse((64,64,192,192),fill=(255,0,0))
im.save("test.png")
IPython.display.Image("test.png")

Google Colabにおける実行結果はこんな感じ。

image5.png

importも最低限だし、これが一番楽なんじゃないかなぁ。

案4: IPython.display.displayを使う (2019年10月1日追記)

IPython.displayにはdisplayという関数があり、それはイメージをそのまま表示できることを後で知った。

from IPython.display import display
display(im)

image6.png

これが一番楽ですね。

案5: (2020年11月26日追記)

コメント欄での指摘の通り、セルでPIL.Image.Imageをそのまま評価してしまうのがもっと楽ですね。

from PIL import Image, ImageDraw
im = Image.new("RGB", (256,256))
draw = ImageDraw.Draw(im)
draw.ellipse((64,64,192,192),fill=(255,0,0))
im

image7.png

参考にしたサイト