画像圧縮が手間?PythonでJPEG画像を200KBにまとめて圧縮する方法

当ページのリンクには広告が含まれています。

ブログに画像をアップロードする際、気になるのがファイルサイズですね。
読み込み速度の向上やSEOへの影響を考えると、画像はできるだけ軽量化しておくことが重要になります。

今回は、Pythonを使って指定フォルダ内のJPEG画像を200KB以下に一括圧縮するプログラムを紹介します。
画像のアップロード前に手軽に処理できるので、ブログ運営の効率化にもおすすめです。

目次

⚠️注意事項⚠️

本記事で紹介しているスクリプトは、あくまで一例です。
実行にあたっては、必ず事前にバックアップを取り、内容をご確認の上でご利用ください。
本コードの利用によって生じた不具合や損害について、本記事では責任を負いかねます。
あくまで自己責任にてご使用ください。

なぜ画像圧縮が必要なのか?

  • 表示速度が遅くなると、ユーザーの離脱率が高まります。
  • ページ表示速度は、GoogleのSEOにも影響する重要な要素です。
  • 1ページあたりの総容量を減らすことで、モバイルユーザーにも優しいブログになります。

他の選択肢との比較(TinyPNGなど)

画像圧縮といえば、TinyPNGのようなWebツールを使っている方も多いかもしれません。
手軽に使えるのがメリットですが、以下のような制約があります。:

  • 無料版では5MB未満の画像しかアップロードできない
  • 圧縮設定を細かく調整できない
  • 再圧縮には限界がある
    例:4MB → 1MB には圧縮できても、さらに圧縮しようとしても 1MB → 1MB のままで変化しません。

このような現象は、おそらく同じ圧縮方式がすでに適用済みで、削れる情報が残っていないためではないかと思われます。
非可逆圧縮(lossy compression)では、一度失われた情報をさらに削るのは難しく、結果として再圧縮してもファイルサイズに変化が出にくいということがあります。

その点、Pythonを使えば:

  • フォルダ内の複数画像を一括処理
  • 圧縮後のファイルサイズを即確認
  • 処理の自動化やカスタマイズも可能

といった柔軟性があるため、日常的に画像を扱うブログ運営者には特におすすめです。

使用しているライブラリ

今回のスクリプトでは、Pythonで画像処理を行うための定番ライブラリ「Pillow」を使用しています。

  • Pillow(PIL)
    リサイズ・圧縮・フォーマット変換・Exif情報の削除など、ブログ用画像の軽量化に便利な機能が揃っています。

作成したプログラム:2段階で200KB以下を目指す

処理フロー

STEP
対象フォルダから対象のファイルパスを取得し、リスト(image_file_list)に保存する。

リスト型変数のimage_file_listを用意します。
次に示すリストのように対象フォルダのすべての画像ファイルパスを保存します。
image_file_list = [対象画像パス①、対象画像パス②、対象画像パス③、・・・]

STEP
image_file_listをループさせて各画像ファイルに対して圧縮処理を行います。
STEP
処理①:リサイズ+Exif削除

以下の作成関数を実行して、リサイズ+Exif削除を行う。
compress_and_resize_jpeg(input_path, output_path, quality, width)

STEP
処理②:サイズ確認+目標ファイルサイズ以上の場合は、再圧縮

以下の作成関数を実行して、サイズ確認+目標ファイルサイズ以上の場合は、再圧縮を行います。
compress_image_to_target_size(input_path, output_path, target_size_kb, initial_quality=85, step=5)

自作関数について解説

紹介するプログラムで処理①と処理②を行う自作関数について詳しく説明します。

自作関数の役割・引数

  1. リサイズ+Exif削除
    • 関数名:compress_and_resize_jpeg(input_path, output_path, quality, width)
      • 引数①  input_path:圧縮対象画像パス
      • 引数② output_path:圧縮完了後の画像保存先パス
      • 引数③ quality:圧縮画像の品質指定
      • 引数④ width:圧縮画像の横幅
  2. サイズ確認+目標ファイルサイズ以上の場合は、再圧縮
    • 関数名:compress_image_to_target_size(input_path, output_path, target_size_kb, initial_quality=85, step=5)
      • 引数① input_path:圧縮対象画像パス
      • 引数② output_path:圧縮完了後の画像保存先パス
      • 引数③  target_size_kb:目標とする圧縮後の画像サイズ
      • 引数④  initial_quality=85:初期設定の品質(85に設定)
      • 引数⑤ step=5:再圧縮時に低下させる品質指定(5ずつ品質を低下させて圧縮処理を行う。)

自作関数の処理の流れ

処理①に使用する関数:compress_and_resize_jpeg(input_path, output_path, quality, width)

STEP
  画像を開いてRGBに変換

JPEG形式に対応させるために、透過などを含む画像もRGBに変換します。

STEP
幅1200ピクセルにリサイズ(アスペクト比維持)

アスペクト比を維持したまま、幅を指定値に合わせて縮小します。  
高画質なリサンプリング(LANCZOS)を使用しています。

STEP
Exif情報を完全に削除

セキュリティや容量削減のため、Exifやメタデータを含まない新しい画像に描画し直します。

STEP
JPEGで保存(品質指定+最適化)

指定品質でJPEG保存し、`optimize=True` でサイズをさらに調整します。

STEP
必要に応じて品質を落として再圧縮(200KB以下になるまで)

処理②に使用する関数:compress_image_to_target_size(input_path, output_path, target_size_kb, initial_quality=85, step=5)

STEP
保存した画像を再読み込みしてサイズ確認  

ファイルサイズが200KBを超えていれば、次のステップの処理を実行します。

STEP
品質(quality)を段階的に下げて再保存

5刻みで品質を下げつつ、目標サイズ以下になるまでJPEGで再保存します。  
最低品質5まで繰り返し、サイズが収まった時点で処理完了です。

コード

from PIL import Image, ImageDraw, ImageFont
import glob
import os

def compress_and_resize_jpeg(input_path, output_path, quality, width):
    # 元画像を開き、RGBに変換(JPEG形式に対応)
    img = Image.open(input_path).convert("RGB")

    # アスペクト比を保持しながらリサイズ
    aspect_ratio = img.height / img.width
    
    # 指定した横幅に合わせて、高さを計算
    height = int(width * aspect_ratio)

    # 高品質なリサンプリング(LANCZOS)でリサイズ
    img = img.resize((width, height), Image.Resampling.LANCZOS)

    # Exifなどのメタデータを完全に削除するため、新しい画像に貼り付ける
    clean_img = Image.new(img.mode, img.size)
    clean_img.putdata(list(img.getdata()))

    # JPEGで保存(optimizeオプション付き)
    clean_img.save(output_path, "JPEG", quality=quality, optimize=True)

    print(f"Saved {output_path} (Exif removed, quality={quality}, size=({width}x{height}))")

def compress_image_to_target_size(input_path, output_path, target_size_kb, initial_quality=85, step=5):
    # 画像を開く
    img = Image.open(input_path)
    
    quality = initial_quality
    while True:
        # 一時的に画像を保存してサイズを確認
        img.save(output_path, "JPEG", quality=quality)
        
        # ファイルサイズを取得(KBに変換)
        file_size_kb = os.path.getsize(output_path) / 1024
        
        # ファイルサイズが目標以下になったら終了
        if file_size_kb <= target_size_kb or quality <= 5:
            print(f"Image compressed to {file_size_kb:.2f} KB with quality={quality}")
            break
        
        # 品質を下げて再度保存
        quality -= step

# 入力画像パス、出力画像パス、圧縮品質(例: 85)、幅を指定
quality = 85  # 圧縮品質
width = 1200  # 横幅を1200ピクセルにリサイズ
image_file_dir = '*** 圧縮前の画像ファイルの保存先のフルパス ***'
save_dir = '*** 圧縮後の画像ファイルの保存先のフルパス ***'

# 目標ファイルサイズ(KB)
target_size_kb = 200  # 目標サイズ200KB

image_file_list = glob.glob(os.path.join(image_file_dir, "*.jpeg"))

print(image_file_list)

for image_file_path in image_file_list:
    input_image = image_file_path  
    file_name = os.path.basename(image_file_path) 
    print(file_name)
    output_image_path = os.path.join(save_dir, "cmp_" + "_" + file_name)
    print(output_image_path)
    compress_and_resize_jpeg(input_image, output_image_path, quality, width)
    compress_image_to_target_size(output_image_path, output_image_path, target_size_kb, initial_quality=quality, step=5)

print("FINISH")

このプログラムを実行すると次のように圧縮された画像が指定のフォルダに保存されます。
圧縮後の画像品質も良好です。

処理①のみでもかなりの容量削減が可能です。
高画質なリサンプリング(LANCZOS)
は、非常に強力だとわかります。

うまくいかなかった例:圧縮処理のみでは不十分

サイズ確認+品質調整(compress_image_to_target_size)のみで圧縮処理

目標ファイルサイズ以下のみを重視して、「サイズ確認+品質調整(compress_image_to_target_size)」のみで処理を試みてみました。その結果、いくつかの問題があることがわかりました。

問題点

  • 解像度の高い画像は、qualityを5まで下げても200KBを超えてしまうことがある
  • 画質も大きく劣化してしまい、ブログ用には使いにくい
  • リサイズなしでの圧縮は限界があり、安定しない

まとめ

Pythonを使えば、画像のサイズ調整も自動化&高速化できます。
毎回Webツールで手作業するよりもはるかに楽で、ブログ更新の効率もアップします。

画像をまとめて200KB以下に圧縮したい方は、ぜひ一度試してみてください。
特に複数画像を扱う方、毎回手作業が面倒な方、画質も気にしたい方にはとてもおすすめです。

コメント

コメントする

目次