🏞️

Pillow Image を bytes に変換する方法

2020/10/05に公開

機械学習をしていると、画像をバイト列( Python でいう bytes)として扱うことがあります。あれは、糞便💩形状判別プログラムを作っているときでした。アノテーションデータから tfrecord を生成しようとしたとき、データ型の問題に直面しました。

私が画像を扱うときはいつも Pillow を使っていました。 Pillow はとても安定しているライブラリで、画像のリサイズやクロップであれば高速に処理できます。機械学習の前処理で、10万枚以上の画像を使ったときは重宝しました。もし、これがなかったら、カップラーメンを何個作っても終わらない時間をかけて、 OpenCV や Scikit-learn などの重厚なライブラリをインストールして大量の画像処理を待つ間にうたたねをすることになるでしょう。

Pillow で画像を読み込むのは、とても簡単です。リサイズだって一瞬です。

from PIL import Image
im = Image.open("lena.jpg")  # 読み込む
resized = im.resize((32, 32))  # 32x32にリサイズ

とても分かりやすいインターフェースで覚えやすいですね。私は、 OpenCV のような第一引数に画像を渡す形式は苦手です。

想像してみてください。

あなたは、画像を切り出して、リサイズしなければなりません。メソッドチェーンのように書いて楽をするのか、それともカッコに悩まされるのか。 cv2.resize(cv2.imread("lena.png"), (32, 32))

切り出しとクロップは、画像処理ではよく行われます。Pillow で書くとこうなります。

from PIL import Image
im = Image.open("lena.jpg").crop((30, 40, 94, 104)).resize((32, 32))

メソッドチェーンで読みやすいでしょう! かっこの位置に悩まなくていいって最高!

先日、冒頭で述べた糞便💩形状判別プログラムを作っていたときに、Pillowの弱点に気づきました。Tensorflowで画像データとアノテーションデータが一緒に扱える tfrecord というデータ構造があります。アノテーションと画像が一つになっているので片方を紛失していまうこともありませんし、画像ファイル形式がJPEGだとかのメタ情報も入る、素敵なファイル形式です。

tfrecordは、画像データを bytes として同梱する必要がありました。画像加工には、Pillow を使って、 tfrecord にするときには bytes にしなければなりません。調べてみると Pillow Image にそんな機能はありませんでした。

Pillow Image は画像加工以外に保存するときも同じようなインタフェースを備えています。保存も簡単です。

from PIL import Image
im = Image.open("lena.jpg").crop((30, 40, 94, 104)).resize((32, 32)).save("resized.jpg")

保存できるなら、 io.BytesIO に保存しよう! これがきっかけでした。

Pillow.Image.save は第一引数に seek や tell や write を持っているオブジェクトを求めていますから、 io.BytesIO は問題なさそうです。

io.BytesIO には、 getvalue というバッファすべてを bytes として出力するメソッドがあります。

Pillow.Image.save と io.BytesIO.getvalue を組み合わせて、 bytes を取得するには次のようにします。

import io

file_path = "lena.png"
img = Image.open(file_path, mode='r')
resized_img = img.resize((32, 32))  # 32x32に変形

img_bytes = io.BytesIO()
resized_img.save(img_bytes, format='PNG')
img_bytes = img_bytes.getvalue()  # これが bytes

以上、結論です。

参考資料

https://stackoverflow.com/questions/33101935/convert-pil-image-to-byte-array

Discussion