Pythonで画像を一括リサイズ・圧縮するGUIアプリを作ってみた！

PythonとTKinter、Pillowを使って画像を一括リサイズ・圧縮するGUIアプリを作ってみました。

## アプリの目的

オンラインではなく、ローカル環境で画像を一括リサイズ・圧縮できるようにする。

※現在使っているWebアプリ（オンラインツール）が本当に安全かどうか分からないため。

## アプリの使い方

### 1.JPG画像ファイルを選択

画像のファイル形式はJPG形式で、複数のファイルをまとめて選択することもできます。


### 2.保存先のフォルダを選択

画像の保存先フォルダを指定することができます。

### 3.画像の幅を指定

リサイズする画像の横幅を指定することができます。画像を小さくしてもアスペクト比は変更なく、固定されています。

なお、このアプリは、サムネイル画像作成用のため、元の画像よりも小さいサイズに指定する必要があります。

### 4.圧縮率を指定

画像の圧縮率を指定することができます。

### 5.RUNボタンを押す (圧縮画像の容量を確認)

最後にRUNボタンを押すと、画像がリサイズ・圧縮されて任意のフォルダに保存されます。また、その際に、ターミナル上で、保存した圧縮した画像ファイルの容量(KB)が表示されます。

## 用意するもの

### JPGファイル

jpg形式で保存されているファイルを複数用意してください。

### Pillowをインストール

Pillowをインストールしておきます。

```
pip install pillow
```




## アプリのコード

アプリのコードを実行することで、GUIアプリが自動で起動します。

```python:pillow-app.py
# coding: utf-8
# インポート
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
import os
from PIL import Image
import math

### 関数 ###
# Browse...ファイルを選択
def browse_func():
    # グローバル変数の宣言
    global fname
    typ = [('', '*')] 
    dir = '/home/'
    #複数ファイルを選択する
    fname = filedialog.askopenfilenames(filetypes = typ, initialdir = dir) 
    #print(fname)
    
    #単数のファイルを選択する
    #fname = filedialog.askopenfilename(filetypes = typ, initialdir = dir) 

#保存先のファルダを選択
def Destination_folder():
    # グローバル変数の宣言
    global fname2

    dir = '/home/'
    #フォルダを選択する
    fname2 = filedialog.askdirectory(initialdir = dir) 

    

# ウインドウの作成
root = tk.Tk()
# ウィジェット変数の作成
var = tk.IntVar(root)

var2 = tk.IntVar(root)


#関数 バイトをKB、MB、GBに変換する
def convert_size(size):
    units = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB")
    i = math.floor(math.log(size, 1024)) if size > 0 else 0
    size = round(size / 1024 ** i, 2)

    return f"{size} {units[i]}"

# RUN
def run_func():
    global fname
    global fname2
    
    try:
         for f in fname:
             img = Image.open(f)
             
             value = var.get()
             img.thumbnail((value, 300))

             fname, fext = os.path.splitext(f)
    
             f_output = fname + '_resize' + fext

             value2 = var2.get()
             
             #フォルダ名（ディレクトリ名）を取得: os.path.dirname()
             dirname = os.path.dirname(fname)

             #加工前のフォルダー名
             folder1 = str(dirname)
             #加工後のフォルダー名
             folder2 = str(fname2)

             new_dir_name = f_output.replace(folder1,folder2)

             img.save(new_dir_name, quality= value2, optimize=True)
             
             KB = os.path.getsize(new_dir_name)
             #バイトをKB、MB、GBに変換
             print(convert_size(KB))
             
    except:
        # ステータスの更新
        statusbar["text"] = " Error!!"


### GUI  ###
# ウインドウの作成

#ウィンドウのサイズを設定
#root.geometry("300x300") 

# ステータスバー設置 
statusbar = tk.Label(root, text =  " Here we go!!",
                     bd = 1, relief = tk.SUNKEN, anchor = tk.W)
statusbar.pack(side = tk.BOTTOM, fill = tk.X)
# 実行ボタン
run_button = tk.Button(root, text = "RUN", command = run_func)
run_button.pack(pady = 10, ipadx = 20, side = tk.BOTTOM)
# ラベルフレーム
frame = ttk.Labelframe(root, text = "Select Image", padding = 10)
frame.pack(padx = 20, pady = 5, side = tk.TOP)
# コンボボックス
extensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp"]
cb = ttk.Combobox(frame, values = extensions)
cb.pack(side = tk.LEFT)
# 参照ボタン
browse_button = tk.Button(frame, text = "Browse...",
                          command = browse_func)
browse_button.pack(padx = 10, side = tk.LEFT)

# スケールウィジェットの作成と配置
#app = tkinter.Tk()

scale = tk.Scale(
    root,
    orient=tk.HORIZONTAL,
    label="Width",
    from_=100,
    to=800,
    resolution =100,
    
    variable=var # ウィジェット変数を設定
)

scale2 = tk.Scale(
    root,
    orient=tk.HORIZONTAL,
    label="Quality",
    from_=0,
    to=95,
    resolution =10,
    variable=var2 # ウィジェット変数を設定
)

#scale.pack(padx=10, pady=10, fill=tkinter.X)
scale2.pack(padx=10, pady=10, side=tk.BOTTOM , fill=tk.X)

scale.pack(padx=10, pady=10, side=tk.BOTTOM , fill=tk.X)

#保存フォルダ選択
# ラベルフレーム
frame2 = ttk.Labelframe(root, text = "Destination folder", padding = 10)
frame2.pack(padx = 20, pady = 5, side = tk.BOTTOM)
# コンボボックス
#extensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp"]
#cb = ttk.Combobox(frame2, values = extensions)
cb = ttk.Combobox(frame2)
cb.pack(side = tk.LEFT)
# 参照ボタン
browse_button2 = tk.Button(frame2, text = "Browse...",
                          command = Destination_folder)
browse_button2.pack(padx = 10, side = tk.LEFT)

# ウインドウ状態の維持
root.mainloop()

```

## 作成したアプリをexe化する

pyinstallerを使って、アプリをexe化することができます。exe化すると、ボタンをダブルクリックするだけでアプリを起動できるようになります。

```
pip install pyinstaller
```

```
pyinstaller pillow-app.py
```

Linux Ubuntuだと非常に簡単にexe化できます。

Windowsはけっこう難しかったです。詳しい手順は以下の記事にまとめてあります。

Python デスクトップアプリ開発に挑戦してみた！ https://zenn.dev/akibaburari/articles/32a89b19fcf889

## ＜補足＞ PNG画像を使用する場合

今回はJPG画像を使用しましたが、PiiowでPNG画像を扱うこともできます。

その際はプログラムの一部を書き換える必要があります。

以下のページが参考になります。

「python」PillowでPNGを減色して圧縮する | yokuaru Tech notes https://yamaccu.github.io/tils/20230519-Python-pngcompress

Pythonで画像サイズ（容量）を小さくする | 資格マフィア https://shikaku-mafia.com/python-image-size/