PySimpleGUI との連携 -- 画像二値化GUIアプリケーション


PySimpleGUI を使用してグラフィカルに画像二値化を行うアプリケーションのサンプルです。

画像はファイルダイアログから選択して読み込むことができます。 二値化しきい値はスライダーにより操作できます。

本サンプルの実行には予め PySimpleGUI をインストールしておく必要があります。 pip コマンドを使用する場合、次のようにインストールしてください。

pip install pysimplegui==4.30.0

コード

import os
import tempfile
import tkinter

import pyfie
import PySimpleGUI as sg

# Windows アプリに近い見た目とする
sg.theme('Default1')
sg.SetOptions(font=('Meiryo UI', 10))

# レイアウト設定
left_frame = sg.Frame(
    "入力画像", [
        [sg.Image(key='-SRC-IMG-')]
    ]
)
right_frame = sg.Frame(
    "二値化画像", [
        [sg.Image(key='-DST-IMG-')]
    ]
)
thresh_frame = sg.Frame(
    "二値化しきい値", [
        [sg.Slider(
            range=(0, 255), resolution=1, default_value=128, size=(70, 20), orientation='horizontal',
            key='-THRESH-', enable_events=True
        )],
    ]
)
layout = [
    [
        sg.Input(key='-SRC-FILE-', visible=False, enable_events=True),
        sg.FileBrowse(
            button_text="入力画像を選択", file_types=[("Image Files", "*.bmp *.png *.jpg *.jpeg *.tif")]
        ),
        sg.Input(key='-SAVEAS-FILE-', visible=False, enable_events=True),
        sg.FileSaveAs(
            button_text="二値化画像を保存", file_types=[("Image Files", "*.bmp *.png *.tif")],
            key='-SAVEAS-BUTTON-', disabled=True)
    ],
    [left_frame, right_frame],
    [thresh_frame]
]

# ウィンドウを作成
window = sg.Window('画像二値化アプリ', layout)

hsrc = None
hdst = None
temp_src_fname = None
temp_dst_fname = None


def process_event(event, values):
    """イベントを処理"""
    def update_dst():
        """出力画像の更新"""
        if hsrc is not None:
            pyfie.fnFIE_binarize(hsrc, hdst, values['-THRESH-'])
            pyfie.imwrite(temp_dst_fname, hdst, comp_level=0)
            window['-DST-IMG-'].update(filename=temp_dst_fname)

    def on_new_file_selected(fname):
        """入力画像ファイルの変更イベント

        入力画像をファイルから読み込み、出力画像も合わせて更新する"""
        global hsrc
        global hdst

        try:
            hsrc_candidate = pyfie.imread(fname)
        except Exception as e:
            sg.Popup('エラー', '画像の読み込みに失敗しました。\n' + str(e))
            return

        if hsrc_candidate.f_type != pyfie.F_IMG_UC8 or hsrc_candidate.ch != 1:
            sg.Popup('エラー', '1チャネルの8ビット濃淡画像のみ受け付けます')
            return

        # sg.Image はbmpやjpegなどに非対応のため、一旦保存
        pyfie.imwrite(temp_src_fname, hsrc_candidate)

        try:
            window['-SRC-IMG-'].update(filename=temp_src_fname)
        except tkinter.TclError as e:
            sg.Popup('エラー', '画像の読み込みに失敗しました。\n' + str(e))
            return

        window['-SAVEAS-BUTTON-'].update(disabled=False)
        hsrc = pyfie.imread(temp_src_fname)
        hdst = hsrc.empty_like(img_type=pyfie.F_IMG_BIN)
        update_dst()

    def on_thresh_changed():
        """二値化しきい値の変更イベント"""
        update_dst()

    def on_save_button_clicked():
        """二値化画像の保存イベント"""
        fname = values['-SAVEAS-FILE-']
        if fname != "":
            try:
                pyfie.imwrite(fname, hdst)
                sg.Popup('保存成功', '二値化画像を保存しました。')
            except Exception as e:
                sg.Popup('エラー', '画像の保存に失敗しました。\n' + str(e))
                return
            # 画像保存がキャンセルされた際に前回の入力が残らないようにするため、
            # 保存先ファイル名を消去。 see: https://github.com/PySimpleGUI/PySimpleGUI/issues/2794
            window['-SAVEAS-FILE-'].update('')

    if event == '-SRC-FILE-' and len(values['-SRC-FILE-']) > 0:
        on_new_file_selected(values['-SRC-FILE-'])
    elif event == '-THRESH-':
        on_thresh_changed()
    elif event == '-SAVEAS-FILE-':
        on_save_button_clicked()


# PySimpleGUIで画像を表示するには一旦画像をファイルやバイト列に変換する必要があるため、
# 一時保存用のフォルダを用意。
with tempfile.TemporaryDirectory() as temp_dir:
    temp_src_fname = os.path.join(temp_dir, "src.png")
    temp_dst_fname = os.path.join(temp_dir, "dst.png")

    # イベントループ
    while True:
        event, values = window.read()
        if event == sg.WIN_CLOSED:
            # アプリを終了
            break
        process_event(event, values)

window.close()

スクリーンキャスト(2倍速)

../../_images/pysimplegui_screencast.gif

備考

画像の描画のため imwrite() により一時的に PNG 形式の画像をファイルに保存しています。 より処理効率を上げるためには、 OpenCV などを利用して画像の保存先をファイルではなくメモリ (BytesIO) にする必要があります。

ダウンロード

関連サンプル