Python, Pillowで画像を一括リサイズ(拡大・縮小)
画像処理ライブラリPillow(PIL)のImage
モジュールに、画像をリサイズ(拡大・縮小)するメソッドresize()
が用意されている。
Image.resize(size, resample=0)
Returns a resized copy of this image.
Image.resize() - Pillow (PIL Fork) 10.2.0 documentation
ここでは、以下の内容について説明する。
Image.resize()
の使い方- 一括で処理するコード例
- 特定の拡張子のファイルのみを処理
- Pillowが対応するすべての画像ファイルを処理
Pillow(PIL)のインストール、基本的な使い方などは以下の記事参照。
拡大・縮小ではなく、画像の一部を切り出してサイズを小さくする場合は、crop()
を使う。
Image.resize()の使い方
引数size
には、リサイズ後のサイズを(width, height)
のようにタプルで指定する。
引数resample
には、リサンプリングする際に使われるフィルターを指定する。以下の6種類がある。省略した場合はデフォルトでNEAREST
が使われる。
Filters
- NEAREST
- BOX
- BILINEAR
- HAMMING
- BICUBIC
- LANCZOS
Concepts - Filters - Pillow (PIL Fork) 10.2.0 documentation
下のもの(BICUBIC
とかLANCZOS
)ほど処理に時間はかかるが、品質は高い。
from PIL import Image
img = Image.open('data/src/lena_square.png')
img_resize = img.resize((256, 256))
img_resize.save('data/dst/lena_pillow_resize_nearest.jpg')
img_resize_lanczos = img.resize((256, 256), Image.LANCZOS)
img_resize_lanczos.save('data/dst/lena_pillow_resize_lanczos.jpg')
引数のresample
を省略しNEAREST
フィルターで縮小した画像(左)と、LANCZOS
フィルターを使った画像(右)。
上の例では、リサイズ後の画像サイズを(256, 256)
というように決め打ちしているが、元画像のサイズをもとに指定したい場合は、例えば以下のようにする。
img_resize = img.resize((img.width // 2, img.height // 2))
img_resize_lanczos.save('data/dst/lena_pillow_resize_half.jpg')
サイズは整数で指定する必要があるため、//
を使っている。
一括で処理するコード例
画像の一括処理もファイル名の一括変換と同じ要領でできる。
glob()
でファイルのパス文字列を取得し、それぞれをresize()
でリサイズ、save()
で新たなファイル名を指定して保存する。保存先のディレクトリはmakedirs()
で生成する。
- 関連記事: Pythonで条件を満たすパスの一覧を再帰的に取得するglobの使い方
- 関連記事: Pythonでパス文字列からファイル名・フォルダ名・拡張子を取得、結合
- 関連記事: Pythonでディレクトリ(フォルダ)を作成するmkdir, makedirs
Joblibを用いた並列処理の例は以下の記事を参照。
特定の拡張子のファイルのみを処理
拡張子がjpg
のファイルのみを抽出する例は以下の通り。
import os
import glob
from PIL import Image
dst_dir = 'data/temp/images_half'
os.makedirs(dst_dir, exist_ok=True)
files = glob.glob('./data/temp/images/*.jpg')
for f in files:
img = Image.open(f)
img_resize = img.resize((img.width // 2, img.height // 2))
root, ext = os.path.splitext(f)
basename = os.path.basename(root)
img_resize.save(os.path.join(dst_dir, basename + '_half' + ext))
複数の拡張子のファイルを処理したい場合、例えば以下のようにすべてのファイルのパスをglob()
で抽出したあとで選択する方法がある。
files = glob.glob('./data/temp/images/*')
for f in files:
root, ext = os.path.splitext(f)
if ext in ['.jpg', '.png']:
img = Image.open(f)
img_resize = img.resize((img.width // 2, img.height // 2))
basename = os.path.basename(root)
img_resize.save(os.path.join(dst_dir, basename + '_half' + ext))
Pillowが対応するすべての画像ファイルを処理
拡張子で抽出するのではなく、Pillowが対応する(= Image.open()
で開ける)すべての画像ファイルを処理したい場合は、try ... except ...
による例外処理を行う。
files = glob.glob('./data/temp/images/*')
for f in files:
try:
img = Image.open(f)
img_resize = img.resize((img.width // 2, img.height // 2))
root, ext = os.path.splitext(f)
basename = os.path.basename(root)
img_resize.save(os.path.join(dst_dir, basename + '_half' + ext))
except OSError as e:
pass
画像ファイルをすべて処理したい場合は複数の拡張子を明示的に指定するよりも楽。