【Pillow】Pythonで画像をまとめてリサイズ・圧縮!WebPにも変換可能【コピペでOK】

ローカル環境で画像を圧縮したかったので、Pythonのライブラリ「Pillow」を使って複数枚まとめて圧縮できるコードを作成しました

Web上では画像を圧縮できる便利なツールはいろいろとありますが、会社の画像のように気密性の高い画像では使えないので、今回作ることにしました。

今回の作成したコードのポイントとしては以下です。

  • 任意のフォルダ内の画像をまとめて圧縮できる
  • 画像サイズや圧縮後の画像容量を自分で設定できる
  • PNG / JPEG / WebP に変換できる



「WebP」と「Pillow」について

WebP」はJPEGやPNGと比較して画質を維持しつつ、圧縮率を高くできる次世代画像フォーマットです。

WebP形式の画像をサイトに使うことで、ページの読み込みを高速化することができます。

Webpについてや、WebPへの様々な変換方法については以下の記事を参考にしてください。

WebP(ウェッピー)とは? メリット・デメリットやPNG・JPEGからの変換方法を解説

Pillowで画像をWebPに変換する方法や、エラーが出た時の対処法は以下の記事を参考にしてください。

【Pillow】Pythonで画像をWebpに変換!エラーが出て変換できない場合の対処法も解説

コードの仕様と解説

まず、私の作業環境については以下です。

  • Windows10 Home 64ビット
  • python 3.8.3
  • Pillow 8.3.1

コードの概要

Pythonファイル(Pillow.py)や画像を格納しているディレクトリの構成は以下です。

Pillow/
 ├ Pillow.py
 ├ original/
 │ └ ***.jpg
 │ └ ***.png
 └ convert/ └ ここに変換後の画像を入れます 

「original」フォルダに入っている全ての画像を圧縮して「convert」フォルダに格納します。

ソースコード

コードは以下です。

使い方やコードの中身は後で解説します。

from PIL import Image
import glob, os
import sys
# ------------------------------パラメータ設定設定------------------------------
# 変換したい画像フォーマットを「 png / jpg / webp 」から選択する
img_format = "webp"
# 変換前の画像を格納したフォルダ名
original_folder = 'original/'
# 変換後の画像を格納したフォルダ名
conv_folder = 'convert/'
# 画像サイズを指定(h_size = 横幅, v_size = 縦幅)
# 縦横比は維持。指定のサイズに収まるようにリサイズする
# 画像サイズを変えたくない場合は、元画像よりも大きなサイズを指定しておく
h_size = 3000 # 変換後の横幅のサイズ
v_size = 3000 # 変換後の縦幅のサイズ
# 下記で指定した画像容量になるまで画質調整(画質調整はできるところまで)
Max_size = 200 # KBで指定
# ------------------------------パラメータ設定ここまで------------------------------
# 「 png / jpg / webp 」以外を指定した場合は、終了する。
if not (img_format == 'png' or img_format == 'jpg' or img_format == 'webp'): sys.exit("Error")
# 画像パスを取得
files = glob.glob(original_folder + "*")
# 画像をリサイズ
for file in files: file_name = os.path.splitext(os.path.basename(file))[0] img = Image.open(file).convert('RGB') new_img = img.copy() new_img.thumbnail((h_size, v_size)) new_img_size = os.path.getsize(file) / 1000 # 画質を調整 # png if img_format == 'png': for i in range(7,10): new_img.save(conv_folder + file_name + '.png', compress_level=i) new_img_size = os.path.getsize(conv_folder + file_name + '.png') / 1000 if new_img_size > Max_size and i < 9: print(str(new_img_size) + 'KB '+ 'quality=' + str(i)) os.remove(conv_folder + file_name + '.png') else: print('画質調整終了 ' + str(new_img_size) + 'KB '+ 'compress_level=' + str(i)) break # jpg elif img_format == 'jpg': for i in range(75, -1, -5): new_img.save(conv_folder + file_name + '.jpg', quality=i) new_img_size = os.path.getsize(conv_folder + file_name + '.jpg') / 1000 if new_img_size > Max_size and i > 0: print(str(new_img_size) + 'KB '+ 'quality=' + str(i)) os.remove(conv_folder + file_name + '.jpg') else: print('画質調整終了 ' + str(new_img_size) + 'KB '+ 'quality=' + str(i)) break # webp elif img_format == 'webp': for i in range(80, 0, -5): new_img.save(conv_folder + file_name + '.webp', quality=i) new_img_size = os.path.getsize(conv_folder + file_name + '.webp') / 1000 if new_img_size > Max_size and i > 5: print(str(new_img_size) + 'KB '+ 'quality=' + str(i)) os.remove(conv_folder + file_name + '.webp') else: print('画質調整終了 ' + str(new_img_size) + 'KB '+ 'quality=' + str(i)) break

使い方

まず、使い方についてです。

  1. 「original」フォルダ(名称変更可能)に圧縮したい画像ファイルを格納する
  2. 圧縮後の「画像フォーマット」、「画像サイズ」、「圧縮後の画像容量」を指定する
  3. 上記Pythonコードを実行する
  4. 「convert」フォルダ(名称変更可能)に圧縮後の画像ファイルが格納される

「original」フォルダ(名称変更可能)に圧縮したい画像ファイルを格納する

複数枚画像を格納すると、全ての画像を圧縮できます。

画像以外のファイルは入れないでください。

画像ファイル以外を対象フォルダに入れるとエラーが出ます。また、「PNG」、「JPEG」、「WebP」では動作することを確認していますが、それ以外の画像フォーマットは未確認です。

圧縮後の「画像フォーマット」、「画像サイズ」、「圧縮後の画像容量」を指定する

以下のコードの部分で任意の設定を行ってください。

# ------------------------------パラメータ設定設定------------------------------
# 変換したい画像フォーマットを「 png / jpg / webp 」から選択する
img_format = "jpg"
# 変換前の画像を格納したフォルダ名
original_folder = 'original/'
# 変換後の画像を格納したフォルダ名
conv_folder = 'convert/'
# 画像サイズを指定(h_size = 横幅, v_size = 縦幅)
# 縦横比は維持。指定のサイズに収まるようにリサイズする
# 画像サイズを変えたくない場合は、元画像よりも大きなサイズを指定しておく
h_size = 500 # 変換後の横幅のサイズ
v_size = 500 # 変換後の縦幅のサイズ
# 下記で指定した画像容量になるまで画質調整(画質調整はできるところまで)
Max_size = 200 # KBで指定
# ------------------------------パラメータ設定ここまで------------------------------

変換したい画像フォーマットを指定する

下記の部分で変換後の画像フォーマットを指定してください。

指定できるのは、「 png / jpg / webp 」の3種類のみです。それ以外の文字を指定すると、エラーが出ます。

# 変換したい画像フォーマットを「 png / jpg / webp 」から選択する
img_format = "jpg"

変換前と変換後の画像を格納するフォルダ名を指定する

変更したい場合は、以下の部分を任意のフォルダ名に変更してください。

# 変換前の画像を格納したフォルダ名
original_folder = 'original/'
# 変換後の画像を格納したフォルダ名
conv_folder = 'convert/'

圧縮後の画像サイズを指定する

画像サイズは、下記の部分で指定します。

画像サイズのリサイズは、Pillowの「thumbnail」メソッドを使っています。

画像の縦横比を維持したままリサイズします。

例えば、画像サイズが「横1000px、縦600px」の画像があったとして、「横500px、縦500px」に指定すると、リサイズ後は「横500px、縦300px」になります(横500px、縦500pxに収まるようにリサイズされます)。

画像サイズを変えたくない場合は指定サイズを元画像よりも大きく指定しておく

画像サイズを変えたくない場合は、フォルダに入っている全ての元画像のサイズよりも大きく指定しておくとリサイズされません。

# 画像サイズを指定(h_size = 横幅, v_size = 縦幅)
# 縦横比は維持。指定のサイズに収まるようにリサイズする
# 画像サイズを変えたくない場合は、元画像よりも大きなサイズを指定しておく
h_size = 500 # 変換後の横幅のサイズ
v_size = 500 # 変換後の縦幅のサイズ

圧縮後の画像容量を指定する

圧縮したい画像容量を指定します。

例えば150KBと指定すると、150KBよりぎりぎり小さくなるように画質を調整します。

ただし、画質の調整には限界があるので指定容量以下にならない場合もあります。

その場合は、Pillowで調整できる最低の画質設定として、画像を圧縮します。

画像容量の指定はKB(キロバイト)で指定します。

# 下記で指定した画像容量になるまで画質調整(画質調整はできるところまで)
Max_size = 200 # KBで指定

上記の設定をしてPythonファイルを実行すると、指定フォルダに圧縮後の画像が出力されます。

コードの解説

「PNG」、「JPEG」、「WebP」 以外の画像フォーマットを指定するとエラーを出す

まず、以下の部分で 「PNG」、「JPEG」、「WebP」 以外の画像フォーマットを指定するとエラーを出すようにしています。

# 「 png / jpg / webp 」以外を指定した場合は、終了する。
if not (img_format == 'png' or img_format == 'jpg' or img_format == 'webp'): sys.exit("Error") 

画像ファイルの取得

以下の部分で対象フォルダの画像パスを全て取得します。

# 画像パスを取得
files = glob.glob(original_folder + "*")

以下の部分で画像ファイル名を取得します。これは、圧縮後の画像に圧縮前と同じファイル名を付けるためです。

file_name = os.path.splitext(os.path.basename(file))[0]

以下の部分で画像を取得します。convert('RGB')をしておかないと、PNGをJPEGに変換する際にエラーが出ます。

 img = Image.open(file).convert('RGB')
透過PNGを変換する際はconvert(‘RGBA’)にしておく

convert('RGBA') にしておかないと、透過PNGはうまく変換できないようです。ただし、JPEG画像を変換する際は convert('RGBA') ではエラーが出るので、 convert('RGB')にしてください。

リサイズ

以下でリサイズをします。ただし、 Pillowの「thumbnail」メソッド は元画像を変更してしまいますので、new_img = img.copy()でコピーを作成して元画像をリサイズしないようにしておきます。

new_img = img.copy()
new_img.thumbnail((h_size, v_size))

画像サイズを取得する

圧縮したい指定容量と比較するために、元画像の容量を取得しておきます。

B(バイト)単位で取得されるので、KB(キロバイト)単位に変換しておきます。

new_img_size = os.path.getsize(file) / 1000

画質を調整する

指定容量以下になるまで、画質を調整します。

画質調整は、Pillowのメソッドを利用しています。

PNG、JPEG、WebPでそれぞれ少し内容が違うので、まずはそれぞれの内容を解説します。

PNGの場合

PNGの場合は、compress_levelを調整します。

0 – 9の範囲で調整可能で、1は圧縮速度は最速ですが圧縮率は低く、9は最高の圧縮率、0はまったく圧縮しません。

デフォルトは6です。

optimizeというメソッドがありますが、これは compress_level を9に設定するのと同じ圧縮です。

compress_level

ZLIB compression level, a number between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all. Default is 6. When optimize option is True compress_level has no effect (it is set to 9 regardless of a value passed).

引用:Pillow公式HP

以下の表は、いらすとやのONE PIECEのPNG画像(オリジナルサイズ513KB)をcompress_levelのみを変更して圧縮した結果です。

compress_level を調整してもそれほど圧縮できませんでした。

compress_level画像サイズ(KB)
オリジナル画像513
04293
1632
2614
3591
4549
5531
6513
7503
8494
9487

今回試したPNG画像

JPEGの場合

JPEGの場合は、 qualityを調整します。

0 – 95の範囲で調整可能で、デフォルトは75です。

quality

The image quality, on a scale from 0 (worst) to 95 (best). The default is 75. Values above 95 should be avoided; 100 disables portions of the JPEG compression algorithm, and results in large files with hardly any gain in image quality.

引用:Pillow公式HP

以下の表は、iPhoneで撮影したJPEG画像(オリジナルサイズ3400KB)をqualityのみを10間隔で変更して圧縮した結果です。

JPEGの場合は、qualityを下げていけばかなり圧縮率を高くすることができます。

ただし、見た目も荒くなっていくのは目に見えてわかります。

quality画像サイズ(KB)
オリジナル画像3400
902361
801397
701078
60901
50802
40712
30614
20497
10344
0187
今回試したJPEG画像

WebPの場合

WebPの場合もqualityを調整します。

0 – 100の範囲で調整可能で、デフォルトは80です。

quality

Integer, 1-100, Defaults to 80. For lossy, 0 gives the smallest size and 100 the largest. For lossless, this parameter is the amount of effort put into the compression: 0 is the fastest, but gives larger files compared to the slowest, but best, 100.

引用:Pillow公式HP

WebPの場合、JPEGと圧縮後のサイズは近くなりますが、近い画像容量で見た目を比較した場合、WebPの方が断然綺麗です(低画質にした時に差が分かりやすいです)。


以下が画質を調整している部分です。

それぞれの画像フォーマットのデフォルトの値で画質を調整し、目標の画像容量より大きい場合は、compress_levelをPNGの場合は +1、JPEGとWebPの場合はqualityを -5 して再度画像サイズを確認するという作業を繰り返します。

 # 画質を調整 # png if img_format == 'png': for i in range(7,10): new_img.save(conv_folder + file_name + '.png', compress_level=i) new_img_size = os.path.getsize(conv_folder + file_name + '.png') / 1000 if new_img_size > Max_size and i < 9: print(str(new_img_size) + 'KB '+ 'quality=' + str(i)) os.remove(conv_folder + file_name + '.png') else: print('画質調整終了 ' + str(new_img_size) + 'KB '+ 'compress_level=' + str(i)) break # jpg elif img_format == 'jpg': for i in range(75, -1, -5): new_img.save(conv_folder + file_name + '.jpg', quality=i) new_img_size = os.path.getsize(conv_folder + file_name + '.jpg') / 1000 if new_img_size > Max_size and i > 0: print(str(new_img_size) + 'KB '+ 'quality=' + str(i)) os.remove(conv_folder + file_name + '.jpg') else: print('画質調整終了 ' + str(new_img_size) + 'KB '+ 'quality=' + str(i)) break # webp elif img_format == 'webp': for i in range(80, 0, -5): new_img.save(conv_folder + file_name + '.webp', quality=i) new_img_size = os.path.getsize(conv_folder + file_name + '.webp') / 1000 if new_img_size > Max_size and i > 5: print(str(new_img_size) + 'KB '+ 'quality=' + str(i)) os.remove(conv_folder + file_name + '.webp') else: print('画質調整終了 ' + str(new_img_size) + 'KB '+ 'quality=' + str(i)) break

Pillowを使ったWebpへの変換方法は以下の記事に詳しく書いています。

気になる方は参考に見てください。

【Pillow】Pythonで画像をWebpに変換!エラーが出て変換できない場合の対処法も解説

コメントを残す

メールアドレスが公開されることはありません。

CAPTCHA


検索

アーカイブ

プロフィール

プロフィール背景画像
プロフィール画像

チャベス

1989生まれ、機械機器メーカーで研究開発をしている機械系エンジニアです。 業務効率化や日々の生活を効率化できるプログラミングネタを発信しています。PythonとNotionとSANGO(WordPress)が特に好きです。 詳細プロフィールはこちらnoteにNotionの記事も書いています。

WP-Searchにサイト事例として掲載されています。