プログラミング学習

Pythonで画像を一括圧縮するGUIアプリの作り方【Tkinter + Pillow】

記事内に商品プロモーションを含む場合があります

今回は、Pythonを使って複数の画像を一括で圧縮できるGUIアプリを作ってみたので、その開発の流れをまとめます。

このアプリは以下のような方に特におすすめです。

  • 写真の容量を減らしたい
  • 複数画像を一括で処理したい
  • GUIで直感的に操作したい

私自身、ブログやプレゼン資料用のスクリーンショットをまとめて圧縮する場面が多く、このようなツールを自作すると作業効率が大幅にアップします。

特にWebサイト運営者にとって、画像の軽量化はページ表示速度やSEO対策に直結する重要なポイントです。

使用した技術

  • Python 3.x:プログラミング言語。初心者でも学びやすい。
  • Tkinter:Python標準搭載のGUIライブラリ。インストール不要で使える。
  • Pillow:画像処理ライブラリ。JPEGやPNGの圧縮・変換・リサイズに対応。

TkinterとPillowを組み合わせることで、シンプルながら実用的なアプリを簡単に作れるのが特徴です。
Pillowはオープンソースで広く使われており、公式ドキュメントでも詳細な情報が確認できます。

アプリの機能

  1. フォルダ内の画像をまとめて一括圧縮
  2. JPEG品質をスライダーで調整可能(10〜100の範囲)
  3. 圧縮後のファイル名に自動で _compressed を付与
  4. 圧縮前後の合計ファイルサイズを比較表示

「ただ圧縮するだけ」ではなく、効果が数字で見えるように設計している点がポイントです。これにより、どの設定が最適か試しやすくなっています。

GUIの構成

  1. 入力フォルダ選択
  2. 出力フォルダ選択
  3. JPEG品質スライダー(10〜100)
  4. 一括圧縮ボタン
  5. 圧縮結果の表示(枚数・サイズ)

GUI操作が中心なので、プログラムを触ったことがない方でも直感的に扱える設計になっています。特にJPEG品質スライダーは「容量削減」と「画質維持」のバランスを試す際に便利です。

実装コード(完全版)

以下が完成したPythonコードです。GUIからフォルダを選択し、ボタンひとつで一括圧縮が可能になります。
エラー処理も組み込んであるため、安心して実行できます。

import os
from PIL import Image
import tkinter as tk
from tkinter import filedialog, messagebox

def get_image_files(folder):
    supported_extensions = ("".jpg"", "".jpeg"", "".png"")
    return [f for f in os.listdir(folder) if f.lower().endswith(supported_extensions)]

def get_total_size(file_paths):
    return sum(os.path.getsize(p) for p in file_paths)

def compress_images(input_folder, output_folder, quality):
    image_files = get_image_files(input_folder)
    input_paths = [os.path.join(input_folder, f) for f in image_files]
    total_original_size = get_total_size(input_paths)

    processed_count = 0
    for filename in image_files:
        input_path = os.path.join(input_folder, filename)
        name, ext = os.path.splitext(filename)
        new_filename = f""{name}_compressed.jpg""
        output_path = os.path.join(output_folder, new_filename)
        try:
            img = Image.open(input_path)
            rgb_img = img.convert('RGB')
            rgb_img.save(output_path, ""JPEG"", optimize=True, quality=quality)
            processed_count += 1
        except Exception as e:
            print(f""Error processing {filename}: {e}"")

    output_paths = [os.path.join(output_folder, f""{os.path.splitext(f)[0]}_compressed.jpg"") for f in image_files if os.path.exists(os.path.join(output_folder, f""{os.path.splitext(f)[0]}_compressed.jpg""))]
    total_compressed_size = get_total_size(output_paths)

    return processed_count, total_original_size, total_compressed_size

def format_size(bytes_size):
    for unit in ['B', 'KB', 'MB', 'GB']:
        if bytes_size < 1024:
            return f""{bytes_size:.2f} {unit}""
        bytes_size /= 1024
    return f""{bytes_size:.2f} TB""

def run_compression():
    input_folder = input_folder_var.get()
    output_folder = output_folder_var.get()
    quality = quality_slider.get()

    if not input_folder or not output_folder:
        messagebox.showwarning(""警告"", ""入力フォルダと出力フォルダを選択してください。"")
        return

    count, original_size, compressed_size = compress_images(input_folder, output_folder, quality)
    result_text.set(f""{count} 枚の画像を圧縮しました。\n""
                    f""元の合計サイズ: {format_size(original_size)}\n""
                    f""圧縮後の合計サイズ: {format_size(compressed_size)}"")

# GUIセットアップ
root = tk.Tk()
root.title(""画像一括圧縮アプリ"")
root.geometry(""500x300"")

input_folder_var = tk.StringVar()
output_folder_var = tk.StringVar()
result_text = tk.StringVar()

def select_input_folder():
    folder = filedialog.askdirectory()
    if folder:
        input_folder_var.set(folder)

def select_output_folder():
    folder = filedialog.askdirectory()
    if folder:
        output_folder_var.set(folder)

tk.Label(root, text=""入力フォルダ:"").pack(anchor=""w"", padx=10, pady=(10, 0))
tk.Entry(root, textvariable=input_folder_var, width=60).pack(padx=10)
tk.Button(root, text=""フォルダを選択"", command=select_input_folder).pack(pady=5)

tk.Label(root, text=""出力フォルダ:"").pack(anchor=""w"", padx=10, pady=(10, 0))
tk.Entry(root, textvariable=output_folder_var, width=60).pack(padx=10)
tk.Button(root, text=""保存先を選択"", command=select_output_folder).pack(pady=5)

tk.Label(root, text=""JPEG品質:"").pack(anchor=""w"", padx=10, pady=(10, 0))
quality_slider = tk.Scale(root, from_=10, to=100, orient=""horizontal"")
quality_slider.set(70)
quality_slider.pack(padx=10)

tk.Button(root, text=""一括圧縮して保存"", command=run_compression).pack(pady=10)

tk.Label(root, textvariable=result_text, justify=""left"", fg=""blue"").pack(padx=10, pady=10)

root.mainloop()

実行方法

1.上記コードを compressor_gui.py などの名前で保存

2.Pillowをインストール:

pip install pillow

3.ターミナルで実行:

python compressor_gui.py

ちなみに私は.pyファイルをダブルクリックで起動しています。

出力されたGUI

このように、GUI上でフォルダを指定し、スライダーを動かして圧縮率を調整できます。プログラミングを意識せず、誰でも操作できる点がメリットです。

出力結果

実際にJPEG品質を70%に設定して複数の画像を一括圧縮してみました。

圧縮前


圧縮後(JPEG品質70%設定)

結果は以下の通りです。

  • 圧縮前合計サイズ:15.9MB
  • 圧縮後合計サイズ:6.36MB

約半分以上の容量削減に成功しながらも、画質の劣化はほとんど感じられませんでした。
特にスクリーンショットやWeb用画像では、見た目の違和感がないまま容量を減らせるため、実用的な成果が得られました。

このように画像圧縮は「品質を保つか、容量を減らすか」というトレードオフがありますが、今回の70%設定は非常にバランスの良い結果を生み出しました。
「軽くしたいけど画質は落としたくない」というニーズにぴったりの設定といえます。

既存のオンライン圧縮サービスとの比較

画像圧縮といえば TinyPNGImage Compressor といったオンラインサービスが有名です。これらはブラウザから簡単に使えるため非常に便利ですが、一方で以下のような制約があります。

  • 無料版ではアップロード枚数や容量に制限がある
  • インターネット接続が必要(オフラインでは使えない)
  • アップロードした画像が外部サーバに送信されるため、セキュリティやプライバシー面で不安が残る

今回のPythonアプリはローカル環境で完結するため、上記のデメリットを回避できます。特に以下のような場面では大きなメリットがあります。

  • 業務資料や個人情報を含む画像を扱う場合
  • 数十枚〜数百枚の画像をまとめて処理したい場合
  • インターネット環境が不安定な場面でも使いたい場合

もちろん「手軽さ」ではオンラインサービスに軍配が上がりますが、自由度と安心感の面では自作ツールに大きな価値があります。特に、処理の流れをカスタマイズできるのは自作ならではの魅力です。

おわりに

今回は、Python、Tkinter、Pillowを使って画像を一括で圧縮するGUIアプリを作成しました。
シンプルながら実用的なアプリを自作することで、日常の作業効率を大幅に改善できます。

今後の拡張アイデアとしては、以下のような機能も考えられます。

  • WebP形式への変換(より高圧縮・高品質に対応)
  • EXIFデータ(撮影情報など)の削除機能
  • 進行状況を表示するプログレスバー
  • 圧縮後の画像プレビュー表示

画像圧縮はWeb表示の快適さやデータ共有の効率化に直結する重要な作業です。ぜひこのコードをベースに、自分だけの便利ツールへと進化させてみてください。

AIブロガー
筆者:yuki
Python未経験からAIと対話しながらコード作成。副業×自動化×プログラミングの可能性を模索中。 ▷詳細プロフィールはこちら