Pillow の紹介#
Pillow は Python の画像処理の基礎ライブラリで、無料のオープンソースのサードパーティライブラリです。Python コミュニティのボランティアによって Python 言語で開発されました(主な貢献者:Alex Clark)。
Pillow の前身は PIL ライブラリですが、PIL は Python2 バージョンのみをサポートしており、その後 Python コミュニティのボランティアによって Python3 バージョンに移植され、Pillow と改名されました。Pillow と PIL は同じ環境に共存することはできません。
基礎知識#
この部分では、一定の Python の基礎知識と、RGB などの色モードについての理解が必要です。
Pillow のインストールとインポート#
インストールコマンド:pip install pillow
インポートコマンド:from PIL import Image
Image クラス#
Image クラスは Pillow の中で最も重要なクラスで、Image クラスを使用して Image オブジェクトをインスタンス化できます。
Image オブジェクトの作成#
Image オブジェクトを作成する際には、新しい画像を作成することも、既存のファイルを開くこともできます。
new () メソッドを使用して新しいオブジェクトを作成できます。構文は以下の通りです:
img = Image.new(mode, size, color)
パラメータの説明は以下の通りです:
- mode:画像モード、文字列パラメータ(例:RGB(真彩画像)、RGBA(真彩透明画像)、L(グレースケール画像)、CMYK(カラー印刷モード)など);
- size:画像サイズ、タプルパラメータ (width, height)、画像のピクセルサイズを表します;
- color:画像の色、デフォルト値は 0(黒)を示し、パラメータ値は (R,G,B) の三元組の数字形式、色の 16 進数値(例:'#66CCFF')および色の英単語をサポートします。注意:異なる色モードを選択する場合、パラメータの形式も異なります。
open () メソッドを使用して既存のファイルから新しいオブジェクトを作成できます。構文は以下の通りです:
img = Image.open(fp, mode='r', format=None)
パラメータの説明は以下の通りです:
- fp:ファイルパス、文字列パラメータ;
- mode:オプションのパラメータ、デフォルトは 'r' に設定されており、そうでない場合は例外が発生します。
- format:ファイル形式、デフォルトは None で、画像のデフォルト形式で開くことを示します。
Image オブジェクトの属性#
Image オブジェクトの一般的な属性は以下の通りです:
- size:画像サイズを格納する二元組。
- width:幅。
- height:高さ。
- format:ファイル形式。new () メソッドで生成された Image オブジェクトの format は None です。
- mode:画像の色彩モード。
- readonly:画像が読み取り専用かどうかを示し、0 または 1 を返します。
- info:画像に関連する情報を表示する辞書型。
Image オブジェクトの操作関数#
save()#
save () メソッドは画像を保存するために使用されます。ファイル形式を指定しない場合、デフォルトの画像形式で保存されます。画像形式を指定した場合は、指定された形式で画像を保存します。save () の構文は以下の通りです:
img.save(fp, format=None)
パラメータの説明は以下の通りです:
- fp:ファイルパス;
- format:ファイル形式。
convert()#
場合によっては、save () メソッドが直接保存できないことがあります。たとえば、RGBA カラー形式の画像は JPG として保存できません。しかし、convert () メソッドを使用すると、画像の色形式を変換できます。構文は以下の通りです:
img1 = img.convert('RGB')
resize () と thumbnail ()#
実際の使用では、画像サイズを調整する必要がある場合がよくあります。この場合、resize () メソッドを使用します。このメソッドは新しいオブジェクトを返します。構文は以下の通りです:
img1 = img.resize(size, resample=image.BICUBIC, box=None, reducing_gap=None)
パラメータの説明:
- size:タプルパラメータ (width, height)、画像のスケーリング後のサイズ;
- resample:オプションのパラメータ、画像のリサンプリングフィルターを指し、thumbnail () の resample パラメータに似ており、デフォルトは Image.BICUBIC です;
- box:指定された画像領域をスケーリングします。box のパラメータ値は長さ 4 のピクセル座標タプルで、(左,上,右,下) を示します。注意:指定された領域は元の画像の範囲内でなければならず、範囲を超えるとエラーが発生します。このパラメータを渡さない場合、デフォルトで元の画像全体をスケーリングします;
- reducing_gap:オプションのパラメータ、浮動小数点パラメータ値で、画像のスケーリング効果を最適化するために使用され、一般的なパラメータ値は 3.0 と 5.0 です。
サムネイルを作成する必要がある場合、通常は thumbnail () メソッドを使用します。このメソッドは元のオブジェクトを直接変更します。構文は以下の通りです:
img.thumbnail(size, resample)
パラメータは resize () メソッドと同じ意味です。
split () と merge ()#
split () と merge () メソッドは、それぞれ色チャネルを分離および結合する役割を果たします。使用法は以下の通りです:
r, g, b = img.split() # r,g,bは各色チャネルの白黒画像
img1 = Image.merge('RGB', [b,g,r]) # 結合
各色チャネルの画像は同じサイズでなければならず、そうでない場合は結合できません。
crop ()、copy () と paste ()#
crop () の役割は、元の画像を矩形領域で切り取ることです。使用法は以下の通りです:
img_crop = img.crop(box);
box:切り取り領域を示し、デフォルトは None で、元の画像をコピーすることを示します。
注意:box は 4 つの要素からなるタプルで、各パラメータは切り取られる矩形領域の左上隅の x、y 座標と右下隅の x、y 座標を示します。デフォルトは (0, 0) で、座標原点を示し、幅の方向は x 軸、高さの方向は y 軸で、各ピクセルは 1 単位を表します。
copy () と paste () の役割はコピーとペーストです。copy () メソッドは比較的簡単で、ここでは主に paste () メソッドを紹介します:
large_image.paste(image, box=None, mask=None)
この関数の役割は、1 つの画像を別の画像に貼り付けることです。注意:貼り付け後の画像モードは自動的に一致し、追加の変換は必要ありません。パラメータの説明は以下の通りです:
- image:貼り付ける画像;
- box:画像が貼り付けられる位置または領域を指定します。パラメータ値は長さ 2 または 4 のタプルシーケンスで、長さ 2 の場合は特定の点 (x, y) を示します。長さ 4 の場合は画像が貼り付けられる領域を示し、この場合、領域のサイズは貼り付ける画像のサイズと一致する必要があります。
- mask:オプションのパラメータで、画像にマスク効果を追加します。
GIF 画像#
GIF 画像の特異性は、それが動的な画像であり、多くのフレームを含むことが多いことです。したがって、JPG、PNG などの形式と比較して、いくつかの特別な方法があります。
save () のパラメータ#
GIF 画像を save () で保存する際には、より多くの利用可能なパラメータがあります。部分的なパラメータは以下の通りです:
- save_all:すべてのフレームを保存し、通常は True に設定します。
- append_images:最初のフレームの後に続くフレーム。
- duration:2 つのフレームの時間間隔、単位は ms。
- transparency:透明度、0 に設定すると透明になります。
- loop:ループ回数、0 の場合は永久ループ。
- disposal:処理するかどうか、つまり次のフレームを再生する際に前のフレームをクリアするかどうか;2 に設定すると処理されます。
使用例:
# ここでのframe_listはすべてのフレーム情報を格納しています
frame_list[0].save('example.gif', format='gif', save_all=True, append_images=frame_list[1:], duration=50, transparency=0, loop=0, disposal=2)
tell () と seek ()#
GIF 画像にアクセスする際、特定の時点で 1 フレームのみアクセスできます。tell () メソッドを使用すると、現在のフレーム数を確認できます;seek () メソッドを使用すると、対応するフレームにジャンプできます。ただし、存在しないフレームにアクセスすると EOFError が発生します。一般的な使用法は以下の通りです:
try:
while True:
# 一部の処理操作
img.seek(img.tell() + 1) # 次のフレーム
except EOFError:
pass
この使用法は GIF 画像の各フレームを遍歴することができます。
使用例#
私たちは「滑稽」という表情パックを例にして、Pillow ライブラリのいくつかの使用方法を示します。
注:サンプルプログラムは最適解であるとは限りません。
表情パックの結合#
表情パックの結合の大まかな考え方は、適切なサイズの背景を作成し、表情パックを適切な位置に貼り付けることです。コードは以下の通りです:
# 本ファイルはhuaji.gifと同じディレクトリにあります
from PIL import Image
img = Image.open('huaji.gif')
frame_list = [] # 最終的なGIFファイルのフレームを格納
multiple = 9 # 拡大倍率
# gifのすべてのフレームを遍歴し、最後のフレームに達するとEOFErrorが発生し、プログラムが終了します
try:
while True:
frame = img.convert('RGBA') # RGBAカラー形式に変換
frame_large = Image.new('RGBA', (frame.width * multiple, frame.height * multiple),
'#00000000') # 辺の長さが元のmultiple倍の背景画像を作成
for i in range(multiple):
for j in range(multiple):
frame_large.paste(frame, (frame.width * i, frame.height * j)) # [i, j]位置に背景画像を貼り付け
frame_list.append(frame_large) # 現在のフレームをフレームリストに追加
img.seek(img.tell() + 1) # 次のフレームを遍歴
except EOFError: # 最後のフレームに達した後
pass
# save_all: すべてのフレームを保存
# append_images: 次の画像
# duration: 2つのフレームの時間間隔、単位ms
# transparency: 透明
# loop: ループ回数、0の場合は永久ループ
# disposal: 処理、次のフレームを再生する際に前のフレームをクリア
frame_list[0].save('huaji_enlarged.gif', format='gif', save_all=True,
append_images=frame_list[1:], duration=50, transparency=0, loop=0, disposal=2)
効果の表示は以下の画像です:
表情パックの色変更#
このプログラムでは、表情パックの色変更の方法は各ピクセルの色を変更することです。コードは以下の通りです:
# 本ファイルはhuaji.gifと同じディレクトリにあります
from PIL import Image
# 元の色を目標の色で掛け算し、返します
# RGBA色は四元組(r,g,b,a)で表され、r,g,b,aはすべて混合されます
def to_color(color, target) -> tuple:
'ターゲットカラーに変更します。'
temp = []
for i in range(len(color)):
temp.append(color[i] * target[i] // 255) # 混合色の方法は目標色で掛け算し、その最大値で割ります
return tuple(temp)
# 画像を開く
img = Image.open('huaji.gif')
frame_list = []
green = (0, 255, 0, 255)
# すべてのフレームを遍歴し、色を染めます
try:
while True:
frame = img.convert('RGBA')
pixels = frame.load() # 元の画像の各ピクセルを取得し、変更後も元の画像が変更されます
for i in range(frame.width):
for j in range(frame.height): # 単一のピクセルの色を変更
pixels[i, j] = to_color(pixels[i, j], green)
frame_list.append(frame)
img.seek(img.tell() + 1) # 次のフレーム
except EOFError: # すべてを遍歴した後
pass
# 画像を保存
frame_list[0].save('huaji_green.gif', format='gif', save_all=True,
append_images=frame_list[1:], duration=50, transparency=0, loop=0, disposal=2)
効果の表示:
表情パックのピクセルアート#
「表情パックの結合」と「表情パックの色変更」を組み合わせることで、「表情パックのピクセルアート」機能を得ることができます。表情パックのピクセルアートの効果は、画像の各ピクセルをその位置に応じた色の表情パックに置き換えることです。
画像ファイルのサイズと表示効果を考慮して、基底画像のサイズは 40x40、置き換え用の表情パックのサイズは 20x20 に設定します。コードは以下の通りです:
# 本ファイルはhuaji.gifと同じディレクトリにあります
from PIL import Image
# 元の色を目標の色で掛け算し、返します
# RGBA色は四元組(r,g,b,a)で表され、r,g,b,aはすべて混合されます
def to_color(color, target) -> tuple:
'ターゲットカラーに変更します。'
temp = []
for i in range(len(color)):
temp.append(color[i] * target[i] // 255) # 混合色の方法は目標色で掛け算し、その最大値で割ります
return tuple(temp)
# 単一の形状の色を変換します
def shape_to_color(original_image, color):
image = original_image.convert('RGBA') # ここでconvertを使用して新しいオブジェクトをコピーします、
# さもなければ変更されたオブジェクトはoriginal_imageになります
shape_pixels = image.load()
for i in range(image.width):
for j in range(image.height):
shape_pixels[i, j] = to_color(shape_pixels[i, j], color)
return image
base_size = (40, 40)
shape_size = (20, 20)
img_base = Image.open('huaji.gif') # 基底画像
img_shape = Image.open('huaji.gif') # 置き換え用の形状画像
shape_list = [] # 置き換え形状の各フレームを格納
frame_list = [] # 最終的な出力表情の各フレームを格納
# 置き換え形状の各フレームを取得
try:
while True:
shape = img_shape.convert('RGBA').resize(shape_size)
shape_list.append(shape)
img_shape.seek(img_shape.tell() + 1)
except EOFError:
pass
# ピクセルアートの各フレームを生成
try:
while True:
frame = img_base.convert('RGBA').resize(base_size)
shape = shape_list[len(frame_list) % len(shape_list)] # 循環順序に従ってshapeの1フレームを取得
frame_large = Image.new('RGBA', (frame.width * shape.width, frame.height * shape.height),
'#00000000')
pixels = frame.load()
for i in range(frame.width):
for j in range(frame.height):
new_shape = shape_to_color(shape, pixels[i, j]) # 単一の形状の色を変換
frame_large.paste(new_shape, (i * new_shape.width, j * new_shape.width))
frame_list.append(frame_large) # 現在のフレームをフレームリストに追加
img_base.seek(img_base.tell() + 1) # 次のフレームを遍歴
except EOFError:
pass
# 画像を保存
frame_list[0].save('huaji_pixels.gif', format='gif', save_all=True,
append_images=frame_list[1:], duration=50, transparency=0, loop=0, disposal=2)
効果の表示は以下の画像です:
参考資料#
チュートリアル#
Pillow(PIL)入門チュートリアル(非常に詳細)
Python 画像処理 Pillow ライブラリ基礎編
ドキュメント#
stable バージョン:Pillow stable バージョン
latest バージョン:Pillow latest バージョン