ローカル環境で画像を圧縮したかったので、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
使い方
まず、使い方についてです。
- 「original」フォルダ(名称変更可能)に圧縮したい画像ファイルを格納する
- 圧縮後の「画像フォーマット」、「画像サイズ」、「圧縮後の画像容量」を指定する
- 上記Pythonコードを実行する
- 「convert」フォルダ(名称変更可能)に圧縮後の画像ファイルが格納される
複数枚画像を格納すると、全ての画像を圧縮できます。
以下のコードの部分で任意の設定を行ってください。
# ------------------------------パラメータ設定設定------------------------------
# 変換したい画像フォーマットを「 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 / 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')
以下でリサイズをします。ただし、 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の場合は、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
引用:Pillow公式HPoptimize
option is Truecompress_level
has no effect (it is set to 9 regardless of a value passed).
以下の表は、いらすとやのONE PIECEのPNG画像(オリジナルサイズ513KB)をcompress_level
のみを変更して圧縮した結果です。
compress_level
を調整してもそれほど圧縮できませんでした。
compress_level | 画像サイズ(KB) |
---|---|
オリジナル画像 | 513 |
0 | 4293 |
1 | 632 |
2 | 614 |
3 | 591 |
4 | 549 |
5 | 531 |
6 | 513 |
7 | 503 |
8 | 494 |
9 | 487 |
今回試したPNG画像
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 |
90 | 2361 |
80 | 1397 |
70 | 1078 |
60 | 901 |
50 | 802 |
40 | 712 |
30 | 614 |
20 | 497 |
10 | 344 |
0 | 187 |
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に変換!エラーが出て変換できない場合の対処法も解説