PythonのPillowのImage.fromarrayの使い方: 配列を画像に変換する

61, 2020-09-28

目次

PillowのImage.fromarrayの使い方

Pythonの画像処理ライブラリであるPillownには画像を処理するImageモジュールがあります。
Imageモジュールは、numpyの配列から画像を生成するfromarray()という関数を持っています。

Image.fromarray()を使えば、加工した配列から画像を生成することが出来ます。
たとえばnumpyを使って画像の配列を加工し、その結果をImage.fromarray()に渡して画像にすると言った具合です。
この記事ではImage.fromarray()について具体的に↓を見ていきます。

  • Image.fromarrayの構造

  • 画像をnumpyの配列に変換する

  • numpyの配列を画像に変換する

  • numpyの配列を編集する

今回使用する画像

今回は↓の画像を使用します。

【共有】水没する2人

RGBAPNG画像で、横幅500px高さ500pxの合計250000ピクセルの画像です。

Image.fromarrayの構造

Image.fromarray()は↓のような構造を持っています。
Image.fromarray()は最大で2つの引数を取り、1つの返り値を返します。

PIL.Image.fromarray(obj, mode=None)

obj(第1引数)

objには配列のインターフェースを持ったオブジェクトを指定します。
配列のインターフェースとは具体的には属性「__array_interface__」のことです。
この属性はnumpyndarrayなどの配列が持っているインターフェースです。
Image.fromarray()は内部でこの属性を参照します。

そのためobjがこの__array_interface__を持っていない場合はエラーになります。
たとえばPythonのlistをこのobjに指定すると↓のようなエラーになります。

AttributeError: 'list' object has no attribute '__array_interface__'

mode(第2引数)

modeは生成する画像のモードを指定します。デフォルトはNoneです。

代表的なモードは↓の通りです。

  • 1 ... 1ビットピクセル画像。黒と白

  • L ... 8ビットピクセル画像。黒と白

  • P ... 8ビットピクセル画像。(8-bit pixels, mapped to any other mode using a color palette)

  • RGB ... 3x8ビット画像。トゥルーカラー

  • RGBA ... 4x8ビット画像。トゥルーカラー&透過チャンネル

  • CMYK ... 4x8ビット画像

  • YCbCr ... 3x8ビット画像。カラー・ビデオ・フォーマット

  • LAB ... 3x8ビット画像。Labカラースペース

  • HSV ... 3x8ビット画像。Hue, Saturation Value カラースペース

  • I ... 32ビット符号付き整数画像

  • F ... 32ビット浮動小数点画像

よく使われるのはRGB, RGBAのモードです。
modeNoneの場合、配列が格納しているタイプ情報からモードが決定されます。

返り値

Image.fromarray()の返り値はPIL.Imageです。
この画像はImage.fromarray()に渡された配列から新しく作成された画像です。
メモリ的にも新しく領域が確保されています。

画像をnumpyの配列に変換する

Image.fromarray()numpyの配列を画像に変換するわけですが、Image.fromarray()の前にnumpyの使い方を簡単に解説します。
まずImagenumpyの配列に変換するには、numpy.array()を使います。

from PIL import Image
import numpy as np

# 画像を開く
im = Image.open('img/a.png')

# 画像をnumpyの配列(ndarray)に変換
arr = np.array(im)

print(type(arr))  # タイプ
print(arr.shape)  # 形状

出力結果。

<class 'numpy.ndarray'>
(500, 500, 4)

このようにnumpy.arrayにPillowのImageを渡すと、画像をnumpyの配列(numpy.ndarray)に変換できます。
arr.shapeを参照すると配列の形状を参照できます。
↑の場合、(500, 500, 4)なので横幅500で高さ500で奥ゆきが4の3次元配列になっています。

numpyの配列を画像に変換する

numpyの配列(numpy.ndarray)をImageオブジェクトに変換するにはタイトルにあるImage.fromarray()を使います。
↓のようにnumpyの配列をImage.fromarray()の第1引数に渡します。

from PIL import Image
import numpy as np

# 画像を開く
im = Image.open('img/a.png')

# 画像をnumpyの配列(ndarray)に変換
arr = np.array(im)

# numpyの配列から画像を生成
dst_im = Image.fromarray(arr)

print(type(dst_im))  # タイプ
dst_im.save('dst/out0.png')  # 画像を保存

出力結果。

<class 'PIL.Image.Image'>

保存した画像は↓のようになります。

【0061】out0.png

特に配列を加工するといったことはしていないので、画像は元の画像と同じものになります。
加工する場合は、Image.fromarray()に配列を渡す前に配列を加工します。

numpyの配列を編集する

numpy.array()で作成した配列を編集し、その配列をImage.fromarray()に渡して画像を生成すれば、結果的にプログラミングで加工された画像を手に入れることが出来ます。

from PIL import Image
import numpy as np

# 画像を開く
im = Image.open('img/a.png')

# 画像をnumpyの配列(ndarray)に変換
arr = np.array(im)

# 以下から編集開始
# 配列をコピー
red = arr.copy()

# すべての画素のG, B成分に0を代入し、Rの成分以外を黒にする
red[:, :, (1, 2)] = 0

# 配列から画像を生成
dst_img = Image.fromarray(red)

# 画像を保存
dst_img.save('dst/out1.png')

保存した画像は↓のようになります。

【0061】out1.png

すべてのG, B成分に0を代入しているので、画像は赤い系統の画像になります。
配列のG, B成分への0の代入は↓のようにnumpyの配列の機能を使います。

red[:, :, (1, 2)] = 0

↑の式の意味はすべての画素の3次元目の1, 2番目の要素、つまりG, Bの要素に0を代入するという意味になります。

出題

Q1: Image.fromarray()の第1引数として適当なものを答えよ

  1. numpy.ndarray

  2. dict

  3. int

Q2: Image.fromarray()の戻り値として適当なものを答えよ

  1. int

  2. dict

  3. Image

Q3: Image.fromarray()の第2引数として適当なものを答えよ

  1. 'L'

  2. 'RGB'

  3. 'color'


正解はこちら↓

Q1: 1
Q2: 3
Q3: 1, 2