このページでは、まず画像のデータ構成について説明し、その後 Pillow (PIL) の Image
クラスと画像のデータ構成の関係性や、画像の情報を取得する方法について説明していきます。
説明する内容は超基本的なものになりますが、このページで説明する内容は Pillow を利用する上で知っておいた方が良いですし、Pillow だけでなく画像を扱うプログラム・ソフトウェアを開発する上で必須の知識となりますので、是非覚えておいていただければと思います!
Contents
画像のデータ構成
では、まずは画像のデータ構成について説明していきます。
今や我々の生活と画像は切っても切れない存在です。この「画像」は “データとして見た時” にどのようなものになっているのでしょうか?この点について説明していきます。
画像は「ピクセルの集合」である
画像とはピクセルの集合になります。
ピクセルとは、画像を構成する点のことを言います。別名「画素」とも呼ばれます。
画像は遠目だと単なる一枚の絵に見えますが、拡大してみると多数の小さな点の集まりであることが確認できます。この1つ1つの点がピクセルです。
スポンサーリンク
ピクセル値によって画素の見た目が決まる
そして、このピクセルには色がついています。この色がついたピクセルが2次元的に配置され、それを遠目に見ることで画像は1枚の絵として人間に認識されます。
より具体的にいえば、各ピクセルの色は基本的に RGB
と呼ばれる指標により表現されます。R
は赤色の強さ・G
は緑色の強さ、B
は青色の強さをそれぞれ表す指標であり、これら3つの値から各ピクセルの色が決まることが多いです。RGB
はそれぞれ 256
段階の数値、すなわち 0
〜 255
の数値で強さが表され、数値が高いほど色の強さが大きいことを表します。
ピクセル値のデータ構成は画像によって異なる
上記で、わざわざ “基本的に” と言っているように、ピクセルの色は別の指標で表現されることもあります。
赤・緑・青ではなくシアン・マゼンタ・イエロー・ブラックの4つの色の強さにより表現されることもありますし、グレースケール画像の場合は1つの色の強さでピクセルの色が表現されることもあります。
また、ここまで “色” と限定して説明してきましたが、実際にはピクセルには透明度が設定されることもあり、これによっても各ピクセルの見た目が変化することになります。この場合、RGB
ではなく RGBα
の4つの指標によりピクセルの見た目が表現されることになります。α
はアルファチャンネルと呼ばれ、これにより透明度が設定されます。
このような、各ピクセルの色や見た目を決定する指標は「ピクセル値」と呼ばれます。上記の通り、ピクセル値はピクセルの見た目の表現の仕方によって構成が異なります。
例えば、各ピクセルの色が RGB
で表現される画像の場合、ピクセル値は R
・G
・B
の3つの数値によって表現されます。具体的には、(255, 0, 128)
のような 3
次元のデータで表示されます。
それに対し、各ピクセルの見た目が RGBα
で表現される画像の場合、ピクセル値は RGBα
の4つの数値によって表現されます。具体的には、(255, 0, 128, 128)
のような 4
つの数値で表示されます。この場合、各ピクセルでは RGB
にプラスして「透明度」が設定され、透明に設定されたピクセルは背景が透けて見えるようになります(この節での説明図では、各画像の後ろ側に黄色背景が存在する例を示しています)。
また、複数の指標では無く1つの指標で表される画像もあります。具体的には、グレースケール画像であったり白黒画像等がそれにあたります。
例えばグレースケール画像であれば、ピクセル値は1つの数値によって表現されます。そして、その値は 0
〜 255
の 256
段階の値で表現されることが多いです。
また、白黒画像の場合、グレースケール画像同様にピクセル値は1つの数値によって表現されるのですが、その値は 0
or 1
の 2
段階の値で表現されることが多いです。
このように、ピクセル値とは画像を構成する点(ピクセル)の色や見た目を表す値となりますが、そのピクセル値の構成は画像によって異なります。
ピクセルは2次元的に配置されて表示される
前述の通り、画像はピクセルの集まりであり、そのピクセルを2次元的に配置することで画像として構成されることになります。
画像における横方向のピクセルの数は “幅”、画像における縦方向のピクセルの数は “高さ” と呼ばれることが多いです。また、このピクセル数は px という単位で表されます。
そして、この幅や高さも画像によって異なります。
また、画像の全ピクセルの数は “画素数” と呼ばれます。基本的に画像は四角形なので、この画素数は単純に 幅 x 高さ
で計算することができます。したがって、画像とは 幅 x 高さ
個分のピクセルの集まりであると言えます。
もしかしたら、将来四角形以外の画像が現れるかもしれな無いですね。その場合は計算式が変わることになります。とりあえず、2024 年 1 月時点では画像の画素数は 幅 x 高さ
で計算してやれば良いです。
スポンサーリンク
ピクセルの位置は座標で表現される
前述の通り、画像はピクセルの集まりになります。そして、画像内の各ピクセルの位置は “座標” で管理されるようになっています。座標と聞くと、数学等でよく使用した下の図のような座標を思い浮かべる方も多いかと思います。この座標の場合、(0
, 0
) 座標が中心に存在し、横軸の正方向は右、縦軸の正方向は上となります。
ですが、画像の座標に関しては (0
, 0
) 座標が画像の左上に存在し、さらに縦軸の正方向は下となります(横軸の正方向は右)。
また、幅が横方向のピクセル数、高さが縦方向のピクセル数を表すため、画像の一番右下の座標は (幅 - 1
, 高さ - 1
) となります。つまり、画像内の有効な座標は (0
, 0
) 〜 (幅 - 1
, 高さ - 1
) であり、それ以外の座標は画像外の無効な座標となります。
ここで重要な点は、画像内の有効な座標であるかどうかを判断するためには画像の幅や高さが必要になるという点になります。
Image
クラス
さて、ここまで画像データの構成について解説してきました。ここからは、ここまで解説してきた画像データの構成を表す各指標を取得するための方法について説明していきます。これらは Pillow の Image
クラスのデータ属性 or メソッドにより取得可能です。
まず、画像をプログラムで扱う上で重要になるのが画像の幅と高さです。これらは Pillow の Image
クラスの width
・height
データ属性によって取得できます。
そして画像では、この幅と高さを持つ四角形の中にピクセルが2次元的に配置されています。各ピクセルはピクセル値によって見た目が異なります。さらに、このピクセル値のデータ構成は画像によって異なります。このピクセル値のデータ構成の情報に関しては、PIillow の Image
クラスの mode
データ属性から取得可能です。
また、各ピクセル値は PIL の Image
クラスの getpixel
メソッドによって取得可能です。ピクセル値を取得する際には、ピクセル値を取得したい座標を指定する必要があり、この座標は画像の幅や高さを考慮して指定する必要があります。
次は、実際に画像ファイルから Image
クラスのオブジェクトを生成し、これらの情報の取得を行う例を示していきたいと思います。
ファイル読み込みによる Image
オブジェクトの生成
まず、前提知識として Image
クラスのオブジェクトの生成について説明しておきます。
Pillow においては、Image
クラスのオブジェクトは画像ファイルの読み込みによって生成することが可能です。この画像ファイルの読み込みによるオブジェクトの生成は、Image
クラスの open
関数を利用して実現できます。open
関数の第1引数には読み込みたい画像ファイルのパスを指定します。
from PIL import Image
image = Image.open(ファイルのパス)
この open
関数の実行によって返却値として Image
クラスのオブジェクトへの参照が得られます。例えば上記を実行すれば、image
変数が Image
クラスのオブジェクトを参照することになります。また、open
関数を利用した場合、Image
クラスのオブジェクトは読み込んだ画像、すなわち、open
関数に指定した ファイルのパス
に基づいて生成されます。より具体的には、読み込んだ画像の幅・高さ・ピクセル値を持つオブジェクトが生成されます。
そして、前述の通り、このオブジェクトの各種データ属性やメソッドから、この読み込んだ画像の各種情報を取得することができます。。
また、今回は下記の2つの画像から実際に Image
クラスのオブジェクトを生成し、そのオブジェクトから画像の情報を取得する例を示していきたいと思います。このため、事前に2つの画像を同じフォルダ内にダウンロードし、左の画像から順に rgb.png
・gray.png
と名前を変更しておいてください。
さらに、同じフォルダに Python スクリプトファイル image_test.py
を作成し、中身を下記のように変更してください。
from PIL import Image
image1 = Image.open('rgb.png')
image2 = Image.open('gray.png')
このスクリプトを下記のコマンドで実行すれば、同じフォルダ内の rgb.png
と gray.png
がそれぞれ読み込まれ、これらのファイルに基づいた Image
クラスのオブジェクトが生成されることになります。
% python image_test.py
以降もスクリプトの例も示していきますが、スクリプトの実行は全て上記コマンドにより行うことが可能です。
ここからは、これらのオブジェクトから画像の幅や高さ、ピクセルのデータ構成、ピクセル値を取得する例を示していきたいと思います。
スポンサーリンク
width
データ属性と height
データ属性 (size
データ属性)
まずは、各画像の幅と高さを表示してみましょう!
前述の通り、Image
クラスのオブジェクトは width
データ属性と height
データ属性を持っています。そして、これらの width
や height
より、オブジェクトの画像の幅や高さを取得することができます。
したがって、先ほど作成した image_test.py
を下記のように変更すれば、各画像の幅と高さが標準出力に出力されることになります。
from PIL import Image
image1 = Image.open('rgb.png')
image2 = Image.open('gray.png')
print(image1.width, image1.height)
print(image2.width, image2.height)
先ほどと同様のコマンドで image_test.py
を実行すれば下記のような出力結果が得られると思います。これらは上から順に rgb.png
と gray.png
の画像の幅と高さとなります。
100 135 185 250
したがって、rgb.png
の画像内の有効な座標は (0
, 0
) 〜 (99
, 134
) であり、gray.png
の画像内の有効な座標は (0
, 0
) 〜 (184
, 249
) ということになります。
また、これらの幅と高さは size
データ属性から一度に取得することも可能です。size
データ属性はタプルであり、第 0
要素が幅で第 1
要素が高さとなります。
mode
データ属性
続いて mode
データ属性を出力して各画像のピクセル値のデータ構成について確認してきましょう!Image
クラスのオブジェクトは mode
データ属性を持っており、この mode
より、そのオブジェクトのピクセル値のデータ構成の情報を取得することができます。
ということで、先程の image_test.py
を今度は下記のように変更したいと思います。
from PIL import Image
image1 = Image.open('rgb.png')
image2 = Image.open('gray.png')
print(image1.mode)
print(image2.mode)
先ほどと同様のコマンドで image_test.py
を実行すれば下記のような出力結果が得られると思います。これらは上から順に rgb.png
と gray.png
のピクセル値のデータ構成を示す文字列となります。
RGB L
RGB
は、その画像が RGB 画像であることを示します。この場合、ピクセル値は R (赤)・G (緑)・B (青) の3種類の値から構成されることになります。したがって、rgb.png
のピクセル値を取得すると (r
, g
, b
) のような3つの要素のタプルが取得できることになります。
それに対し L
は、その画像がグレースケール画像であることを示します。この場合、ピクセル値は1種類の値から構成されることになります。したがって、gray.png
のピクセル値を取得するとタプルではなく単なる数値が取得できることになります。
getpixel
メソッド
そして、このピクセル値を取得するためのメソッドが getpixel
となります。getpixel
は実行した画像オブジェクトのピクセル値を取得するメソッドです。ピクセル値は画像の中に 幅 x 高さ
個存在しますので、その中からどのピクセル値を取得したいのかを座標で指定する必要があります。
例えば rgb.png
と gray.png
それぞれの中央の座標のピクセル値は下記のようにして取得・出力することが可能です。
from PIL import Image
image1 = Image.open('rgb.png')
image2 = Image.open('gray.png')
center1 = (image1.width // 2, image1.height // 2)
center2 = (image2.width // 2, image2.height // 2)
print(image1.getpixel(center1))
print(image2.getpixel(center2))
出力結果は下記のようなものになると思います。前述の通り、得られるピクセル値のデータ構成は画像によって異なるところがポイントとなります。
(181, 0, 56) 73
これらはそれぞれ下図のような色となります。
こんな感じで、ピクセル値のデータは getpixel
メソッドにより取得可能で、その際には画像の座標を指定する必要があります。前述の通り、画像内の有効な座標は (0
, 0
) 〜 (幅 - 1
, 高さ - 1
) となります。画像内の全ピクセル値を表示したいのであれば、これは下記のようなループによって実現することができます。
from PIL import Image
image1 = Image.open('rgb.png')
image2 = Image.open('gray.png')
for y in range(image1.height):
for x in range(image1.width):
print(image1.getpixel((x, y)))
for y in range(image2.height):
for x in range(image2.width):
print(image2.getpixel((x, y)))
スポンサーリンク
putpixel
メソッド
ついでにピクセルのピクセル値を変更するメソッドについても紹介しておきます。
getpixel
がピクセル値を取得するメソッドであるのに対し、putpixel
はピクセル値を変更するメソッドとなります。例えば下記のような処理を実行すれば、各ピクセル値が反転することになり、その結果が inv_rgb.png
と inv_gray.png
として保存されることになります。
from PIL import Image
image1 = Image.open('rgb.png')
image2 = Image.open('gray.png')
for y in range(image1.height):
for x in range(image1.width):
pixel = image1.getpixel((x, y))
r = 255 - pixel[0]
g = 255 - pixel[1]
b = 255 - pixel[2]
image1.putpixel((x, y), (r, g, b))
for y in range(image2.height):
for x in range(image2.width):
pixel = image2.getpixel((x, y))
v = 255 - pixel
image2.putpixel((x, y), v)
image1.save('inv_rgb.png')
image2.save('inv_gray.png')
ポイントになるのが putpixel
実行時に第2引数に指定するピクセル値の型が image1
と image2
とで異なる点になります。これらが異なるのは image1
と image2
とで mode
が異なるため、すなわち image1
と image2
とで画像を構成するピクセル値のデータの構成が異なるためになります。
つまり、putpixel
の第2引数には、実行するオブジェクトの画像の mode
に応じたデータを指定する必要があります。ここが適切に指定されないと例外が発生したりするので注意が必要で、適切に指定するためには mode
を意識してプログラムを作る必要があります。
また、上記スクリプトを実行することで保存される inv_rgb.png
と inv_gray.png
はそれぞれ下の図のような画像となります。このように、getpixel
で取得したピクセル値を反転し、それを画像のピクセル値に putpixel
で置き換えてやれば、簡単にネガポジ反転画像が作成できることになります。
実は、こういったループを組んでピクセル単位で処理を行わなくても、Pillow ではメソッドを1つ実行するだけで全ピクセルに対して同様の処理を実施することが可能です。そして、その方が処理時間が圧倒的に早いです。なので、実現したい画像処理に適したメソッドが存在するのであれば、特に速度が求められる場合はピクセル単位で処理を行うのではなくメソッドを利用する方が良いです。
ただ、ピクセルに慣れることはプログラムで画像を扱う上で重要ですし、ピクセルの知識はどんな画像処理ライブラリやプログラミング言語を扱う上でもほぼ共通になります。汎用性も高く、簡単な幾何学・数学の知識に基づき putpixel
メソッドを利用すれば、画像内に様々な図形を描画するようなことも可能です。数学と聞くだけで鳥肌が立つような方もおられるかもしれませんが、こういった画像と一緒に数学を学び直すと苦手だったものも楽しく学ぶことができます!
new
関数による Image
オブジェクトの生成
さて、ここまで Image
オブジェクトの生成は全て open
関数を利用して行ってきました。この open
関数を利用すれば、Pillow では画像ファイルを読み込んで、その画像ファイルに基づいたオブジェクトをを自動的に生成することができます。
ただし、Pillow では open
関数ではなく new
関数を利用して Image
オブジェクトを生成することもできます。new
関数は Pillow の Image
クラスに定義されたクラス関数となります。そして、new
関数を利用する場合、画像ファイルを読み込んでオブジェクトを生成するわけではないため、プログラマーが自身で生成するオブジェクトの各種パラメーターを引数を指定して行う必要があります。そして、この引数で指定する必要のあるパラメーターは、ここまでも何度も紹介してきた下記の3つとなります。
-
mode
: ピクセル値の構成の情報size
: 生成する画像の幅と高さのタプルcolor
: ピクセル値
引数 mode
に関しては、データ属性の mode
から取得できるものと同様に文字列の形式で指定する必要があります。引数 mode
に指定可能な値の代表例は下記の通りです。
'1'
: 白黒画像(ピクセル値は0
or1
の値)'L'
: グレースケール画像(ピクセル値は0
〜255
の値)'RGB'
: RGB 画像(ピクセル値は0
〜255
の値を3
つ持つタプル)'RGBA'
: RGBα 画像(ピクセル値は0
〜255
の値を4
つ持つタプル)4
つ目の値が透明度を表す(0
: 透明・255
: 不透明)
mode
に指定可能な値の全リストは下記ページでまとめられていますので、指定可能な値の全てを知りたい方は下記を参照してください。
https://pillow.readthedocs.io/en/stable/handbook/concepts.html
また、引数 size
に関しては生成する画像の幅と高さを要素とするタプルを指定します
さらに、引数 color
に関してはピクセル値を指定します。このピクセル値を指定する際には mode
への指定値を考慮して指定するピクセル値を設定する必要がある点になります。ここまでの解説の中でも説明してきたように mode
、すなわちピクセル値の構成によって指定可能なピクセル値が異なります。
例えば mode
に 'RGB'
を指定した場合は R
・G
・B
の各色の強さを示す3つの値を要素に持つタプルをピクセル値として color
に指定する必要があります。それに対し mode
に 'L'
を指定した場合は1つの色の強さを示す値のみを color
に指定する必要があります。このように mode
に指定する値に応じて color に指定可能なデータの形式が異なることになるので注意してください。
さて、上記のように引数を指定して new
関数を実行した場合は size
で指定した幅と高さの画像のオブジェクトが生成されることになります。そして、その画像の全ピクセルのピクセル値は color
で指定したものとなります。
例えば下記のように new
関数を実行した場合は幅が 200
px、高さが 100
px の全面黄色の画像のオブジェクトが生成されることになり、そのオブジェクトから save
メソッドを実行させているため、このオブジェクトが画像として new_image.png
に保存されることになります。
from PIL import Image
image = Image.new('RGB', (200, 100), color=(255, 255, 0))
image.save('new_image.png')
実行後の new_image.png
は下図のようなものになります。
こんな感じで、出来上がる画像が一色なので open
関数に比べて実用性低いかもしれませんが new
関数によって画像のオブジェクトが生成可能であることは覚えておくと良いと思います。また、new
関数に指定する引数は画像のデータ構成を決定づける重要なパラメーターばかりで、画像のデータ構成がどのようなものであるかを理解しておけば new
関数に指定が必要な引数は自然と導けるようになると思います。new
関数だけでなく、画像を扱う際にはこのページで説明した画像のデータ構成の知識が必ず役に立ちますので、是非このページで解説した内容は理解しておきましょう!
まとめ
このページでは画像のデータ構成と、それらに関わる Pillow (PIL) の Image
クラスのデータ属性、メソッドについて説明しました!
画像とは、幅 x さ 個分のピクセルの集まりになります。そして、各ピクセルはピクセル値によって色(見た目)が設定されています。このピクセル値の構成は画像によって異なる点に注意してください。例えば RGB 画像であればピクセル値は R・G・B それぞれの色の強さを表す3つの値から構成されます。それに対してグレースケール画像の場合は1つの値からピクセル値が構成されます。
そして、Pillow を利用した場合、画像の幅や高さ、ピクセル値の構成の情報やピクセル値そのものは Image
クラスのデータ属性やメソッドから取得することが可能です。具体的には、画像ファイルから image
クラスのオブジェクトを生成した場合、そのオブジェクトの width
・height
データ属性から画像の幅と高さを取得することが可能です。さらに、ピクセル値の構成は mode
データ属性から取得可能で、ピクセル値そのものは getpixel
から取得することができます。
これらは画像を扱うプログラムやソフトウェアも開発する上で必須級の知識となりますので、是非これらについては覚えておきましょう!