Lesson5

Pillowの使い方(画像加工)

1. 学習の目標

このレッスンでは画像ファイルの読み込み方を学びます。そのために利用するライブラリが Pillow です。

pl_00_01.png

機械学習では「画像の分類」ができるので、画像を扱うライブラリに慣れましょう。

ここでは簡単に、Pillowのライブラリの利用方法についてまとめます。

2. Pillowについて

PillowはPythonプログラム内で画像を読み込むためのベースとなるライブラリです。

内部でPillowを利用するライブラリ経由で触る機会も多いため、ここでは以下の操作について簡単に見ていきます。

  1. 画像の読み込み
  2. 画像をグレースケールのモノクロ画像へ変換
  3. 画像の保存

2.1 画像の読み込み

Pillowを使うために、まずは以下のインポートが必要です。JupyterLabで Lesson5 という名前で新規ノートブックを作成して、その中で実行してください。後から使いたいのでMatplotlibのpyplotもインポートしています。

from PIL import Image
import matplotlib.pyplot as plt
%matplotlib inline

次に、下記の画像 tokyo_tower.png を右クリック→「名前をつけて保存」(ファイル名は tokyo_tower.png のままにしてください)した上で、Cloud9へアップロードしてください。アップロードする場所は Lesson5.ipynb と同じディレクトリにしましょう。

tokyo_tower.png

この画像は 横のサイズ:500, 縦のサイズ:750(単位:ピクセル(px))です。

次に tokyo_tower.png を読み込みます。

tokyo_tower = Image.open("tokyo_tower.png")
print(type(tokyo_tower))

出力結果:

<class 'PIL.PngImagePlugin.PngImageFile'>

JupyterLab上に読み込んだ画像を表示したい場合はmatplotlib.pyplotの plt.imshow() を実行することで、グラフと同じような形式で画像を表示できます。引数に画像を指定してください。

plt.imshow(tokyo_tower)

出力結果:

pl_01.png

2.2 画像をグレースケールのモノクロ画像へ変換

このPillowのオブジェクトが持つ convert() を実行し、tokyo_tower.jpg の画像をモノクロ画像へ変換します。

gray_tower = tokyo_tower.convert("L")

convert() の引数に "L" と入力することで、モノクロの画像が作成されます。

グレースケール画像の表示

グレースケール化した画像も imshow() で表示できますが、そのままだと、色合いが正しく表示されません。

plt.imshow(gray_tower)

出力結果:

l5_04

これは、Matplotlibのデフォルトのカラーマップ設定によるものです。モノクロ画像の場合、色味が暗いほど青く、明るくなるにつれて 青 → 緑 → 黄色 の色味で表示されるという設定になっています。そのため、JupyterLabで正しく 黒 → グレー → 白 の色味でモノクロ画像を表示するには、imshow() に「黒 → グレー → 白 の色味で表示できるよう、カラーマップの設定を追加してあげる」必要があります。

方法としては imshow()cmap="gray" というキーワード引数を追記するだけです。

plt.imshow(gray_tower, cmap="gray")

出力結果:

pl_02.png

2.3 画像の保存

加工した画像を保存するなら save() を使います。ファイルにつけたい名前を、引数として指定することになります。ここでは tokyo_tower_gray.png というファイル名にしました。なお、imshow() と違って save() で保存する場合は、特別な指定をしなくても正しいモノクロの画像として保存されます。

gray_tower.save("tokyo_tower_gray.png")

これでモノクロの東京タワーの写真(tokyo_tower_gray.png)が tokyo_tower.png と同じディレクトリに保管されました。

tokyo_tower_gray.png

2.4 画像のリサイズと回転・平行移動

画像のリサイズ

画像のサイズを変更するにはPillowのImageモジュールにある resize() を使います。引数には (画像の横のサイズ, 画像の縦のサイズ) というタプルを指定します。

tokyo_tower_resized = tokyo_tower.resize((250, 375))
plt.imshow(tokyo_tower_resized)

出力結果:

pl_01_01.png

画像の左端および下端に表示されている目盛りを見ても、サイズを変更した後の画像は 横のサイズ:250px, 縦のサイズ:375px になっていることがわかります。

画像の回転

回転させるにはPillowのImageモジュールにある rotate() を使います。引数には、反時計回りの回転角度を度数(degree)で指定します。

tokyo_tower_rotated = tokyo_tower.rotate(60)
plt.imshow(tokyo_tower_rotated)

出力結果:

pl_01_02.png

デフォルトでは、上記のように回転前の画像サイズを保ったまま表示するため、回転した画像の一部が枠をはみ出てしまって表示されません。回転後の画像の全体を表示したい場合は rotate()expand=True のキーワード引数を指定してください。

tokyo_tower_rotated = tokyo_tower.rotate(60, expand=True)
plt.imshow(tokyo_tower_rotated)

出力結果:

pl_01_03.png

画像の平行移動

画像を平行移動させるには、回転と同じ rotate() を使います。その際、回転はさせたくないので回転角度は 0度 としてください。そして、translate=(横の移動座標,縦の移動座標) というキーワード引数を追加しましょう。

tokyo_tower_translated = tokyo_tower.rotate(0, translate=(100, 100))
plt.imshow(tokyo_tower_translated)

pl_01_03.png

※上記は回転させていないため、expand=True を指定しても機能せず、平行移動によってはみ出た部分は表示されません。

3. 画像をNumPyの配列に変換する方法

このようにPillowライブラリではopen()を使って画像を読み込むことができますが、このデータを、そのまま機械学習に用いることはできません。

機械学習で扱うには、読み込んだ画像のピクセル単位の情報を、NumPyの配列であるndarrayに変換します。

3.1 画像をndarrayに変換する方法

ndarrayに変換するには、NumPyのarray()を使います。まずは、NumPyをインポートしましょう。

import numpy as np

先の例と同様に、東京タワーの画像を読み込みます。

tokyo_tower = Image.open("tokyo_tower.png")

こうして読み込んだtokyo_towerndarrayに変換するには、次のようにします。

color_image = np.array(tokyo_tower)

type()で確認すると、ndarrayに変換されたことがわかります。

print(type(color_image))

出力結果:

<class 'numpy.ndarray'>

データの形式

変換されたndarrayは、左上から右下に向けて、1ピクセルずつ、RGB(赤、緑、青)の色の強さが0から255までの256段階の値として格納された構造です。ndarrayのプロパティshapeを調べると、配列の要素数がわかるので、確認してみましょう。

print(color_image.shape)

出力結果:

(750, 500, 3)

750, 500 というのは、画像の縦のサイズ(750px)と横のサイズ(500px)です。つまり 「画像の高さ分の行 × 画像の幅分の列」というサイズの2次元配列 として構成されています。

最後の 3750 * 500 の2次元配列を3個持っている、という意味です。この 3 について具体的に言うと、RGB(赤緑青) それぞれについての、色の強さに関する情報です。試しに左上の端にあるピクセル(横と縦の座標が0のピクセル)の情報を見てみましょう。スライスを使います。3番目にコロン(:)のみを指定することで、赤・緑・青すべての色の情報を取得できます。

print(color_image[0, 0, :])

出力結果:

[116 166 218]

出力結果の情報は R(赤), G(緑), B(青) の順でならんでいます。つまり、これは左上のピクセルが、赤 116 、緑 166 、青 218 の色の強さで構成されているということを示しています。

これは光の性質に沿っているので、赤緑青がすべて0なら黒、すべて255なら白い色になります。

また、たとえば、すべてのピクセルにおいてR(赤)の情報のみを取得したい場合は、以下のようにスライスします。3番目はR = 0, G = 1, B = 2で並んでいるので、0と指定すれば赤のみの情報を取得できます。

print(color_image[:, :, 0])

出力結果:

[[116 119 120 ...  86  88  89]
 [119 117 118 ...  89  85  87]
 [121 116 118 ...  87  87  90]
 ...
 [193 190 164 ...  29  24  17]
 [156 171 147 ...  29  25  16]
 [157 146 149 ...  29  28  19]]

グレースケールの場合のデータ形式

グレースケール(モノクロ)の場合は、RGBのそれぞれの要素ではなく、白と黒のみの濃さとして0(黒)~255(白)の値で構成されます。確認してみましょう。

まずは、グレースケールに変換したndarrayを取得します。

gray_tower = tokyo_tower.convert("L")
gray_image = np.array(gray_tower)

shapeで確認してみましょう。最後の 3 がなくなっています。

print(gray_image.shape)

出力結果:

(750, 500)

左上のピクセルの濃度を次のようにスライスして確認しましょう。カラー画像と違って、RGBの各要素はなく、単一の0~255の値として構成されることがわかります。

print(gray_image[0, 0])

出力結果:

156

3.2 数値で画像データを持つことの利点

ピクセル単位の情報を ndarray 形式として持つことで数値計算による画像加工が可能になります

また、機械学習における「画像の分類」は ピクセルの情報を数値化したものを基にして判断しています。 先ほど説明したカラー画像とモノクロ画像での ndarray の構造は、ぜひ理解しておいてください。

明るさの分布を表示

せっかくの機会なので、ここでは画像の情報を「ヒストグラム(色の明るさ(濃さ)の分布)」で表示してみましょう。ヒストグラムはNumPyの np.histogram() を実行し、戻り値の1つ目のデータを使うと、簡単にグラフが表示できます。

hist_c, bins_c = np.histogram(color_image.flatten(), bins=256)
plt.plot(hist_c)

出力結果:

l5_01

np.histogram()の戻り値の2つ目には、ヒストグラムの横軸の値がリスト形式で格納されています。今回はbin=256のキーワード引数の指定で256段階にデータを分けていますので、0から255までの数値がbins_cに入っています。

RGB各色の分布を表示

もちろんスライスを使うことで、RGB各色のヒストグラムを表示できます。

赤のヒストグラムの出力結果:

l5_01_01

緑のヒストグラムの出力結果:

l5_01_02

青のヒストグラムの出力結果:

l5_01_03

3.3 ndarray形式の画像の表示

このように変換したndarrayに対して、さまざまな数値計算をして、最後に画像として表示したい、つまり、ndarrayの内容をPillowで表示したいということもあります。

ndarray形式のデータを画像として表示する方法を2つ紹介します。

その1:Imageに変換する

Image.fromarray()を使うと、ndarrayをImageオブジェクトに変換できます。このImageオブジェクトをMatplotlib.pyplotのimshow()で指定して表示します。

color_pil = Image.fromarray(np.uint8(color_image))
plt.imshow(color_pil)

出力結果:

l5_02

加工してから表示する例

もちろん加工してから表示することもできます。すでに説明したように、ndarray配列のそれぞれの要素は、RGBの濃度です。そこでたとえば次のようにすれば、濃度を半分(半分の濃さ)に変換し、それを表示できます。実行例を見るとわかるように、画像が暗く表示されます。

おさらい: // は整数の割り算をする演算子で、割り算の結果の小数以下を切り捨てます。
half_image = color_image // 2
plt.imshow(half_image)

出力結果:

l5_03

その2:imshow()に直接ndarrayを指定する

ndarray形式のデータのままでも matplotlib.pyplotimshow() を実行することで、グラフと同じような形式で画像を表示できます。

plt.imshow(color_image)

出力結果:

pl_01.png

4. まとめ

Pillowを使った簡単な画像加工処理について学びました。

主に imshow() でJupyterLab上に画像を表示する方法や convert() でグレースケール(モノクロ画像)化する方法をご紹介しました。

本レッスンで学んだもの以外にも、 imshow()convert() は、さまざまなキーワード引数を持っています。レッスン7までの内容については、以上の内容が身につけばOKですが、深堀りしてみたい方は公式サイト(英語)のドキュメントや、日本語で書かれた技術ブログなどの記事を読んで、さらなる学習をしてみてください。

いよいよ、次のレッスンから機械学習に入っていきます。

Lesson6 scikit-learnの使い方(機械学習)へ進む
Copyright Tellus Inc. All rights reserved