【Python】Tkinterで簡易的な画像結合アプリを作成してみた

こんにちは。本日はPythonのライブラリであるTkinterを用いて、簡易的な画像結合アプリを作成しました。
もともとは研究で使用するために画像を横方向に結合する機能を作成したのですが、アプリ化してGUIで操作できるようにしたほうが便利であると感じたため、今回作成するに至りました。

Tkinterとは

Tkinterとは、Pythonのデスクトップアプリ作成時によく使われるモジュールで、ボタンやエントリー、リストなどのウィジェットを使ってGUIアプリを実装することが出来るものです。
ちなみに、Pythonでデスクトップアプリを作成する場合、Tkinter以外にも以下のライブラリがあります。
1. Kivy
2. wxPython
3. PyQt

環境構築

1.Pythonのインストール

www.python.org こちらの サイトからPythonのインストールを行います。
とりあえず、最新バージョンをインストールしておけば問題ないと思います。
インストーラーが開いたら、下図のように操作を行います。

Python installer

インストールが終わったら、Pythonのバージョンを確認しましょう。
コマンドプロンプトを開いたら、python --versionと入力します。

バージョンが返ってきたら成功です。

2.ライブラリのインストール

画像結合アプリを作成するのに必要なライブラリをインストールしてきます。
今回必要なライブラリは以下の三つです。すべてpipを使ってインストールすることが出来ます。

1.OpenCV

opencv-pythonは以下のコマンドでインストールします。

pip install opencv-python
pip install opencv-contrib-python

2.Numpy

Numpyは以下のコマンドでインストールします。

pip install numpy

3.Pandas

Pandasは以下のコマンドでインストールします。

pip install pandas

ライブラリの確認

pip list と入力してライブラリがインストールされているか確認しましょう。

上記のライブラリがすべて表示されていれば成功です。

3.エディターのダウンロード

エディターは基本なんでもいいです。
筆者はVisual Studio Codeを使用しました。
ダウンロードはこちらから
azure.microsoft.com

【実装例】画像結合アプリの作成

ライブラリインポート

まず、必要なライブラリをインポートします。

import tkinter.filedialog as fl
import tkinter as tk
import tkinter.messagebox as mb
import pandas as pd
import cv2
import glob
import numpy as np
import os
from tkinter import ttk
from tkinter import *

カレントディレクトリの取得

後々使うのでカレントディレクトリを変数に格納しておきます。

PROJECT_DIR = os.getcwd()

関数の作成

ファイル選択関数

ファイルをGUIで選択できるようにしておきたいため、そのための関数を作成します。

def selectfile():
    global path                  #globalとして宣言
    #フォルダのパスを選択
    path = fl.askdirectory(initialdir = PROJECT_DIR, title = "select file")
    
    entry1.set(path)            

水平方向結合のための関数

def hconcat():
    try:
        #filesに拡張子が.pngのファイルを格納
        files = glob.glob(path + "/*.png")
        # 空のリストを準備
        img_list = []
        #リストのインデックスと要素を取得し、画像を読み込む
        for i, figures in enumerate(files):
            img = cv2.imread(figures)
            img = np.asarray(img)
            img_list.append(img)

        #hconcat によって img_list内の画像を結合
        combined = cv2.hconcat(img_list)

        #結果をウィンドウに出力
        cv2.imshow(entry_title.get(), combined)
        

        #ファイルの書き出し
        cv2.imwrite(entry_title.get() + ".png", combined)
        cv2.waitKey(0)
        mb.showinfo("完了",  PROJECT_DIR + "に画像を保存しました。")

    except:
        mb.showinfo("エラー", "エラー。設定を確認してください")

垂直方向結合のための関数

def vconcat():
    try:
        #フォルダ内写真を結合
        files = glob.glob(path + "/*.png")
        # 空のリストを準備
        img_list = []
        #リストのインデックスと要素を取得し、画像を読み込む
        for i, figures in enumerate(files):
            img = cv2.imread(figures)
            img = np.asarray(img)
            img_list.append(img)

        #vconcat によって img_list内の画像を結合
        combined = cv2.vconcat(img_list)

        #結果をウィンドウに出力
        cv2.imshow(entry_title.get(), combined)
        

        #ファイルの書き出し
        cv2.imwrite(entry_title.get() + ".png", combined)
        cv2.waitKey(0)
        mb.showinfo("完了",  PROJECT_DIR + "ディレクトリに画像を保存しました。")

    except:
        mb.showinfo("エラー", "エラー。設定を確認してください")

上記二つの関数の中身はそこまで変わりませんね

Tkinterによるデザインの作成

Tkinterを使って見た目を作っていきます。 必要な機能は
・ タイトル入力欄
・ フォルダの参照
・ 結合ボタン
の三つに絞りました。

if __name__ == "__main__":
    root = tk.Tk()
    root.title('PhotoCombine')
    root.resizable(False,False)
    frame1 = tk.LabelFrame(root, text = "png画像の結合", foreground = "green")
    frame1.grid(row = 0, sticky = tk.W)

    #タイトル入力  ラベルの作成
    titleLabel = ttk.Label(frame1, text = "タイトルを入力>>", padding = (5, 2))
    titleLabel.grid(row = 0, column = 0)


    #タイトル入力 エントリーの作成
    entry_title = tk.Entry(frame1, width = 30)
    entry_title.insert(tk.END, "file_name")
    entry_title.grid(row = 0, column = 1, padx = 5)


    #フォルダ参照 エントリーの作成
    entry1 = StringVar()
    IDirEntry = ttk.Entry(frame1, textvariable=entry1, width=30)
    IDirEntry.grid(row = 1, column = 0)
    #フォルダ参照 ボタンの作成
    button_select = tk.Button(frame1, text = "フォルダを参照", width = 10, command = selectfile)
    button_select.grid(row = 1, column = 1) 

    #横方向結合ボタンの作成
    button_exe = tk.Button(frame1, text = "横方向に結合", width = 10, command = hconcat)
    button_exe.grid(row = 1, column = 2)

    #縦方向結合ボタンの作成
    button_exe = tk.Button(frame1, text = "縦方向に結合", width = 10, command = vconcat)
    button_exe.grid(row = 1, column = 3)

    root.mainloop()

完成品

使い方

タイトルを入力し、ファイルを参照ボタンを押下して、画像ファイルの格納されたフォルダを選択します。


imagesフォルダには、以下のような画像が格納されています。

その後、横方向結合ボタン、もしくは縦方向結合ボタンを押すと、結合された画像が下図のようにウィンドウに表示されます。
※画像フォルダのディレクトリ名に日本語が含まれているとエラーが発生するので注意
ウィンドウを閉じると、以下のようなメッセージが表示される

最後に  

今回、Tkinterを用いて簡易的な画像結合アプリを作成しましたが、改善点が多くあげられます。 いくつか例を挙げると
・画像をpng形式でしか読み取ることができない(変数filesの定義方法が原因)
・事前に画像を一つのフォルダにまとめておく必要がある(任意の画像をリストに追加していけるような関数が必要)
・デザインがダサい
・画像ファイルがあるディレクトリ名に日本語が入っているとエラーが発生する
他にも挙げるとキリがないですが、限定された環境で動くようなアプリの作成には成功しました。
GUIの作成には、Pythonはあまり向いている言語ではありませんが、Pythonは「視覚的に記述が可能」「ライブラリが豊富」といったメリットがあるので、機能の拡張も非常にしやすいです。

かなり簡単に作ることができるので、PythonTkinterOpenCVに興味がある方はぜひ一度作成してみることをおススメします。