TkinterとPillowで画像変換器を作る

141, 2020-12-23

目次

画像変換器を作る

Pythonには標準ライブラリに「Tkinter(キンター)」と呼ばれるGUIライブラリがあります。
Tkinterを使うと標準ライブラリのみでGUIアプリを作ることが可能です。

それから外部ライブラリに「Pillow(ピロウ)」と呼ばれる画像処理ライブラリがあります。
PillowはPythonでは人気のある外部ライブラリで、画像の読み込みや表示、加工などを簡単に行うことが出来ます。

今回はこれら2つのライブラリとPythonを使って簡易的な画像変換器を作ってみました。
ウィンドウの中に画像とボタンを表示して、ボタンをクリックしたら画像にエフェクトが加わるGUIアプリです。
この画像変換器について具体的には↓を見ていきます。

  • アプリの外観

  • コード全文

  • Pillowのインストール

  • ウィンドウの表示

  • レイアウトの設定

  • 画像のリセット

  • ネガポジ反転

  • グレイスケール変換

アプリの外観

今回作ったアプリの外観を最初に紹介します。
↓のような外観になります。

【0141】ss00.png

表示されている画像はオリジナルの画像です。
この画像を右に配置されているボタンで加工する感じです。
動作風景は↓です。

【0141】rec.gif

ボタンにはリセットボタン、ネガポジ反転ボタン、モノクロ変換ボタンがあります。
これらのボタンをクリックすると↑のように左に表示されている画像が変化します。

画像は読み込みなどは行えず、1つの画像を決め打ちで表示しています。
この辺を自由に読み込みが出来るようにするには改造が必要ですが、今回は対応しません。

それではまずはPillowのインストールから始めます。

コード全文

↓が今回制作したGUIアプリのコード全文です。

import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk, ImageOps


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title('Image convertor')

        # ウィンドウの左側のエリア
        self.left_frame = ttk.Frame(self)
        self.left_frame.pack(side=tk.LEFT, expand=True, fill=tk.BOTH)

        # 元画像をPillowで開く
        # 今回はimg/e.pngを開いている
        self.src_pl_img = Image.open('img/e.png')
        self.dst_pl_img = self.src_pl_img

        # ラベルに画像を設定して配置する
        self.dst_photo_img = ImageTk.PhotoImage(self.src_pl_img)
        self.img_label = ttk.Label(self.left_frame, image=self.dst_photo_img)
        self.img_label.pack(expand=True, fill=tk.BOTH)

        # ウィンドウの右側のエリア
        self.right_frame = ttk.Frame(self)
        self.right_frame.pack(side=tk.LEFT, expand=True, fill=tk.BOTH)

        # リセットボタン
        self.reset_btn = ttk.Button(self.right_frame, text='Reset', command=self.reset)
        self.reset_btn.pack(side=tk.TOP)

        # ネガポジ反転ボタン
        self.invert_btn = ttk.Button(self.right_frame, text='Invert', command=self.invert)
        self.invert_btn.pack(side=tk.TOP)

        # グレイスケール変換ボタン
        self.gray_btn = ttk.Button(self.right_frame, text='Gray', command=self.gray)
        self.gray_btn.pack(side=tk.TOP)

    def reset(self):
        """
        画像をリセットする
        """
        self.dst_pl_img = self.src_pl_img
        self.dst_photo_img = ImageTk.PhotoImage(self.src_pl_img)
        self.img_label.config(image=self.dst_photo_img)

    def invert(self):
        """
        画像をネガポジ反転する
        """
        self.dst_pl_img = ImageOps.invert(self.dst_pl_img)
        self.dst_photo_img = ImageTk.PhotoImage(self.dst_pl_img)
        self.img_label.config(image=self.dst_photo_img)

    def gray(self):
        """
        画像をグレイスケール変換する
        """
        self.dst_pl_img = self.dst_pl_img.convert('L')
        self.dst_photo_img = ImageTk.PhotoImage(self.dst_pl_img)
        self.img_label.config(image=self.dst_photo_img)        


def main():
    app = App()
    app.mainloop()


main()

GUIアプリにしては短いコードになっています。
これはTkinterとPillowのおかげです。
Tkinterを使うと↑のように短いコードでGUIアプリを作ることが出来ます。
Pillowによる画像の加工も同様です。

Pillowのインストール

今回、画像処理に使うライブラリであるPillowは外部ライブラリです。
そのためpipなどのパッケージマネージャでこの外部ライブラリをインストールしておく必要があります。
pipでPillowをインストールするには↓のようにコマンドを実行します。

> pip install Pillow

ウィンドウの表示

最初にTkinterでウィンドウを表示します。
タイトルの付いたウィンドウの表示自体は↓のコードで行えます。

import tkinter as tk
from tkinter import ttk


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title('Image convertor')


def main():
    app = App()
    app.mainloop()


main()

tkinterttkなどの必要モジュールをインポートします。
それからAppというクラスを作ってtk.Tkクラスを継承します。
tk.TkはTkinterのルートウィンドウになるクラスです。

App__init__()メソッド内では親の__init__()の呼び出しを行います。
それからtitle()メソッドを使ってウィンドウのタイトルを設定します。
今回のGUIアプリのタイトルは「Image convertor(画像変換器)」にしておきます。

(^ _ ^)

英語にするとかっちょいいね

main()関数内でAppクラスをオブジェクトにしてmainloop()メソッドを呼び出し、ウィンドウを表示します。
main()関数はコードの下の方で適当に呼んでおきます。

レイアウトの設定

__init__()メソッド内でレイアウトを設定します。
今回はウィンドウを左のエリアと右のエリアに分けて、左のエリアに画像を表示して、右のエリアに変換ボタンを配置します。
そのため左のエリアと右のエリアを表すフレームを2つつくります。
そしてそれぞれのフレームに画像のラベル、それから変換ボタンを配置します。

    def __init__(self):
        super().__init__()
        self.title('Image convertor')

        # ウィンドウの左側のエリア
        self.left_frame = ttk.Frame(self)
        self.left_frame.pack(side=tk.LEFT, expand=True, fill=tk.BOTH)

        # 元画像をPillowで開く
        # 今回はimg/e.pngを開いている
        self.src_pl_img = Image.open('img/e.png')
        self.dst_pl_img = self.src_pl_img

        # ラベルに画像を設定して配置する
        self.dst_photo_img = ImageTk.PhotoImage(self.src_pl_img)
        self.img_label = ttk.Label(self.left_frame, image=self.dst_photo_img)
        self.img_label.pack(expand=True, fill=tk.BOTH)

        # ウィンドウの右側のエリア
        self.right_frame = ttk.Frame(self)
        self.right_frame.pack(side=tk.LEFT, expand=True, fill=tk.BOTH)

        # リセットボタン
        self.reset_btn = ttk.Button(self.right_frame, text='Reset', command=self.reset)
        self.reset_btn.pack(side=tk.TOP)

        # ネガポジ反転ボタン
        self.invert_btn = ttk.Button(self.right_frame, text='Invert', command=self.invert)
        self.invert_btn.pack(side=tk.TOP)

        # グレイスケール変換ボタン
        self.gray_btn = ttk.Button(self.right_frame, text='Gray', command=self.gray)
        self.gray_btn.pack(side=tk.TOP)

左のエリアを表すフレームをleft_frame, 右のエリアを表すフレームをright_frameで作成しておきます。

初期状態でsrc_pl_imgに画像を読み込んでおきます。
画像の読み込みはPillowのImage.open()で行うことが出来ます。
Image.open()の引数に画像のパスを指定すると、そのパスの画像を読み込むことが出来ます。

src_pl_imgはリセット処理などに使います。そのため読み込んだ直後の状態で保持しておきます。
src_pl_imgdst_pl_imgに代入しておきます。
実際の変換処理にはこちらのdst_pl_imgを使います。

読み込んだPillowの画像はImageTk.PhotoImageでTkinterの画像(dst_photo_img)に変換しておきます。
その画像をimg_labelに設定します。img_labelの親ウィジェットにはleft_frameを設定しておきます。
このimg_labelをウィンドウの左側に配置しておきます。

right_frameにはreset_btninvert_btn, gray_btnなどのボタンを配置しておきます。
これらのボタンにはそれぞれ「Reset(リセット)」、「Invert(反転)」、「Gray(グレイスケール)」というテキストを設定します。
また、それぞれのボタンにはクリックしたときのイベントとして、reset, invert, grayメソッドを渡しておきます。

ボタンの配置はside=tk.TOPにして上から順に詰めていきます。こうすることで上から下に並ぶボタンを配置できます。

画像のリセット

画像のリセット処理はresetメソッドで行います。
これはsrc_pl_imgdst_pl_imgに代入し、src_pl_imgからImageTk.PhotoImageを使ってdst_photo_imgを作成します。
そしてdst_photo_imgをラベルに再設定するだけです。
こうすることで加工された画像がリセットされます。
ImageTkモジュールはインポートしておく必要があります。

このリセット処理のためにsrc_pl_imgは読み込んだ直後の状態で保持しておく必要があります。
これを加工してしまうとこのメソッドで行うリセット処理が機能しなくなるためです。
そのため主な加工にはdst_pl_imgを使います。

    def reset(self):
        """
        画像をリセットする
        """
        self.dst_pl_img = self.src_pl_img
        self.dst_photo_img = ImageTk.PhotoImage(self.src_pl_img)
        self.img_label.config(image=self.dst_photo_img)

ネガポジ反転

画像のネガポジを反転する処理はinvert()メソッドで行います。
ImageOpsモジュールのinvert()メソッドにPillowのImageを渡すと、ネガポジを反転することが出来ます。
反転した画像(dst_pl_img)はImageTk.PhotoImageでTkinterの画像に変換して、それをimg_labelに設定します。
ImageOpsモジュールはインポートしておく必要があります。

Pillowを使うと↓のようにネガポジの反転処理が1行で書くことが出来ます。
Pillowにはネガポジの反転意外にもさまざまなエフェクトが用意されています。これらのエフェクトを使えばこのGUIアプリの加工ボタンを自由に増やすことが出来るでしょう。

    def invert(self):
        """
        画像をネガポジ反転する
        """
        self.dst_pl_img = ImageOps.invert(self.dst_pl_img)
        self.dst_photo_img = ImageTk.PhotoImage(self.dst_pl_img)
        self.img_label.config(image=self.dst_photo_img)

グレイスケール変換

画像のグレイスケールへの変換はgray()メソッドで行います。
Imageconvert()メソッドにLモードを指定すると画像をグレイスケールに変換することが出来ます。
グレイスケール画像に変換したらImageTk.PhotoImageでTkinterの画像に変換し、ラベルに設定します。

    def gray(self):
        """
        画像をグレイスケール変換する
        """
        self.dst_pl_img = self.dst_pl_img.convert('L')
        self.dst_photo_img = ImageTk.PhotoImage(self.dst_pl_img)
        self.img_label.config(image=self.dst_photo_img)        

Lモードは8ビットのモノクロ画像のモードです。
モードについては↓を参照してください。

おわりに

TkinterとPillowを使うと簡単に画像変換器を作ることが出来ました。
画像を保存できるように改造するなど、いろいろ改善案はあるかと思います。
コードのライセンスはMITです。改造などは自由におこなってください。

(^ _ ^)

画像変換処理はおもしろい



この記事のアンケートを送信する