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を使う。

```py
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`に格納されただけである。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/79744/e504721f-532b-723c-a8de-f92f4db39d29.png)

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

# 案1: matplotlib+numpyを使う

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

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/79744/26b10aa3-ec6a-1af2-c4b8-c1907d780c10.png)

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

(2021年4月9日追記)

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79744/e32fc989-702e-081c-7bfe-a1adbc8e8fb5.png)


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

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

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

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/79744/e2d23333-75de-c8a9-462a-b783e8b86105.png)

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

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

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

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/79744/914e777d-49b4-4837-b015-12503dba69b5.png)

# まとめ

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

```py
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における実行結果はこんな感じ。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/79744/add35d97-0483-7633-73b5-83e4248cf774.png)

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

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

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

```py
from IPython.display import display
display(im)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79744/ce1346bb-d1a7-ddc2-f043-075d28725214.png)

これが一番楽ですね。

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

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

```py
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
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79744/217e50c5-6ab7-d7df-15be-3bb641f4dc27.png)

# 参考にしたサイト

* [jupyter notebookで画像を表示させたいだけならば、`IPython.display`モジュールのメソッドを使うべし](https://qiita.com/knknkn1162/items/77999450c59db915ab87)
* [Jupyter上でPIL画像を表示する](https://qiita.com/Tatejimaru137/items/44646c9bb3799768fa81)
* [IPython Notebook + cairo でお絵かき](https://qiita.com/knknkn1162/items/77999450c59db915ab87)