Python tkinter / リアルタイムエッジ検出アプリを作ってみた Canny法

まくまく
まくまく
先日作成した「2値化GUIアプリ」のCanny法エッジ検出Verです。PythonのGUI作成ライブラリtkinterを使用して、リアルタイムでエッジ検出ができるGUIアプリを作成してみました。


今回もいつものフレームを使用しています。

左側がメニュー、右側が画像表示、下部がスケール(スライダー)です。スライダーはcanny法の2つの閾値を設定します。初期値は50と100に設定しています。


左側のファイル選択ボタンを押すとtkinterのファイルダイアログが立ち上がります。任意のファイルを選択すると、グレースケールで読み込まれます。

画像は600にリサイズしたものを表示しています。大きさが合わない場合は、左側のサイズに適当な値を入力すると画像サイズを変更できるようにしました。


実際にスライダーを動かして、エッジ検出をしてみましょう。スライダーを動かすと、リアルタイムで画像が変化していきます。


左側のラジオボタンで「白背景」を選択すると、白黒が反転します。


「オリジナル」ボタンを押すと、元のグレースケールの画像を確認することができます。


「保存」ボタンを押すと、プログラムと同じフォルダにエッジ検出されたjpg画像が保存されます。

以下がサンプルプログラムとなります。(Pythonは学習中のため無駄な記述などがある場合があることご了承ください)



サンプルプログラム

#ライブラリのインポート
import tkinter as tk
#import tkinter.ttk as ttk
import tkinter.filedialog
import cv2
from PIL import Image, ImageTk

#フォーマット変換の関数
def format(image_bgr):
    image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
    image_pil = Image.fromarray(image_rgb)
    image_tk  = ImageTk.PhotoImage(image_pil)
    return image_tk

#リサイズ関数
def resize(img):
    size = int(en.get())
    h, w = img.shape[:2]
    cvh = size*h/w
    image_bgr = cv2.resize(img, (size,int(cvh)))
    return image_bgr

#画像選択、表示する関数
def getfile():
    global image_tk
    global img
    global image_bgr
    f_path = tk.filedialog.askopenfilename(title="ファイル選択", initialdir="ディレクトリを入力", filetypes=[("Image file", ".png .jpg .jpeg")])
    str_file_path = str(f_path)
    #OpenCVで画像を読み込む
    img = cv2.imread(str_file_path,0)
    image_bgr = resize(img)
    image_tk = format(image_bgr)
    canvas.create_image(0, 0, image=image_tk, anchor=tk.NW)

#エッジ検出関数    
def scale(event=None):
    global image_tk
    global image_canny
    min  = val_min.get()
    max  = val_max.get()
    image_canny = cv2.Canny(img, min, max)
    bw = val_r.get()
    if bw == 2:
        image_canny = cv2.bitwise_not(image_canny)
    image_bgr = resize(image_canny)
    image_tk = format(image_bgr)
    canvas.create_image(0, 0, image=image_tk, anchor=tk.NW)

#オリジナル画像表示
def org():
    global image_tk
    image_bgr = resize(img)
    image_tk = format(image_bgr)
    canvas.create_image(0, 0, image=image_tk, anchor=tk.NW)

#画像保存    
def save():
    cv2.imwrite("image_canny.jpg", image_canny)
    
#ウインドウの作成
root = tk.Tk()
#ウインドウのタイトル
root.title("エッジ検出アプリ")
#ウインドウサイズと位置指定 幅,高さ,x座標,y座標 
root.geometry("800x640+50+50")

#フレームの作成
frame = tk.Frame(root, width=780, height=620, padx=10, pady=10, bg="#D9D9D9")
frame.place(x=10, y=10)

frame_menu = tk.Frame(frame, relief=tk.FLAT, bg="#E6E6E6", bd=2)
frame_menu.place(x=10, y=10, width=150, height=450)

frame_img = tk.Frame(frame, relief=tk.FLAT, bg="#E6E6E6", bd=2)
frame_img.place(x=170, y=10, width=580, height=450)

frame_scale = tk.Frame(frame, relief=tk.FLAT, bg="#E6E6E6", bd=2)
frame_scale.place(x=10, y=480, width=740, height=110)

#テキストボックスの作成
en = tk.Entry(frame_menu, width=15)
en.insert(0, int(600))
en.grid(row=2, column=0, sticky = tk.W)

# チェック有無変数
val_r = tk.IntVar()
# value=0のラジオボタンにチェックを入れる
val_r.set(1)

#ラジオボタン
rdo1 = tk.Radiobutton(frame_menu, value=1, variable=val_r, text='黒背景',command=scale)
rdo1.grid(row=3, column=0, sticky = tk.W)

rdo2 = tk.Radiobutton(frame_menu, value=2, variable=val_r, text='白背景',command=scale)
rdo2.grid(row=4, column=0, sticky = tk.W)


#ボタン作成
button = tk.Button(frame_menu, text="ファイル選択", command=getfile)
button.grid(row=0, column=0, sticky = tk.W)

button_org = tk.Button(frame_menu, text="オリジナル", command=org)
button_org.grid(row=5, column=0, sticky = tk.W)

button_save = tk.Button(frame_menu, text="保存", command=save)
button_save.grid(row=6, column=0, sticky = tk.W)

#Scaleの値格納用変数
val_min = tk.IntVar()
val_min.set(50)
val_max = tk.IntVar()
val_max.set(100)

#Labelの生成
l = tk.Label(frame_menu,text="サイズ", relief="flat")
l.grid(row=1, column=0, sticky = tk.W)
l_min = tk.Label(frame_scale,text="min", relief="flat")
l_min.place(x=10, y=20)
l_max = tk.Label(frame_scale,text="max", relief="flat")
l_max.place(x=10, y=75)

#スケールの作成
sc_min = tk.Scale(frame_scale, variable=val_min, orient='horizontal',length=670, from_=0, to=300, resolution=0.1, command=scale)
sc_min.place(x=50, y=7)

sc_max = tk.Scale(frame_scale, variable=val_max, orient='horizontal',length=670, from_=0, to=300, resolution=0.1, command=scale)
sc_max.place(x=50, y=60)

#キャンバス作成・配置
canvas = tk.Canvas(frame_img, width=550, height=420)
canvas.place(x=10, y=10)

#イベントループ
root.mainloop()
28行目「initialdir」にはディレクトリを入力してください。
タイトルとURLをコピーしました