IT

Tkinter와 OpenCV를 이용한 GUI 프로그래밍

유병혁 2020. 4. 18. 20:19

안녕하세요? 이번 글은 Tkinter와 OpenCV를 이용하여 동영상을 재생하는 GUI를 만들어 보겠습니다. Tkinter(트킨터)는 사실상 파이썬의 표준 GUI 패키지, OpenCV(Open Source Computer Vision Library)는 오픈소스 컴퓨터 비전 및 머신러닝 소프트웨어 라이브러리입니다.

 

Tkinter(트킨터) 파이썬 위키 | https://wiki.python.org/moin/TkInter

 

TkInter - Python Wiki

Tkinter is Python's de-facto standard GUI (Graphical User Interface) package. It is a thin object-oriented layer on top of Tcl/Tk. Tkinter is not the only GuiProgramming toolkit for Python. It is however the most commonly used one. CameronLaird calls the y

wiki.python.org

OpenCV 공식 홈페이지 | https://opencv.org/

 

OpenCV

This summer, OpenCV celebrates its 20th anniversary. We want to expand another area of our activity

opencv.org

그리고 OpenCV를 Tkinter 창에 연동하는 과정에서 PIL(Python Imaging Library)을 사용할 것입니다.

정확히는 PIL(http://www.pythonware.com/products/pil/)의 프로젝트 포크(Fork), Pillow를 사용합니다.

 

Pillow(PIL Fork) | https://pillow.readthedocs.io/en/stable/index.html

 

Pillow — Pillow (PIL Fork) 7.1.1 documentation

© Copyright 1995-2011 Fredrik Lundh, 2010-2020 Alex Clark and Contributors Revision ed40d04a.

pillow.readthedocs.io

자, 그럼 시작해볼까요?! 일단 필요한 모듈을 호출합니다.

# 모듈 호출
import tkinter as tk # Tkinter
from PIL import ImageTk, Image # Pillow
import cv2 as cv # OpenCV
import os

첫번째 코딩은 간단한 Tkinter 창을 만들어보겠습니다.

# GUI 설계
win = tk.Tk() # 인스턴스 생성
win.title("AniWatch") # 제목 표시줄 추가
win.geometry("920x640+50+50") # 지오메트리: 너비x높이+x좌표+y좌표
win.resizable(False, False) # x축, y축 크기 조정 비활성화
win.mainloop() #GUI 시작

위 코드에서 저는 win이라는 Tkinter 인스턴스 변수를 하나 생성했습니다. 그리고 title에 'AniWatch'라는 텍스트가 뜨도록 했고, geometry 설정을 통해 920x640의 창 크기를 정의했습니다. 뒤에 덧붙인 +50+50은 윈도우 화면에서 좌측상단을 기준으로 x축으로 50, y축으로 50만큼 떨어진 지점에서 창이 뜨도록 한 것입니다. 그리고 resizable은 x축과 y축 모두 창 크기를 조정할 수 없도록 고정시켰습니다. 끝으로 GUI를 시작하는 mainloop() 함수를 선언했습니다.

 

결과는 다음과 같습니다.

이제 이 창을 채워나가면 됩니다. 일단, 화면에 라벨을 하나 추가해 보겠습니다.

# 라벨 추가
lbl = tk.Label(win, text="Tkinter와 OpenCV를 이용한 GUI 프로그래밍")
lbl.grid(row=0, column=0) # 라벨 행, 열 배치

lbl이라는 이름의 Label 객체를 선언하고 text도 정의했습니다. 이때 grid 설정은 라벨을 어디에 배치할 지 정의합니다.

여기에 코딩된 row와 column은 현재 화면을 행과 열로 나눈 상태에서 인덱스(0, 0)라고 보시면 됩니다.

이번에는 동영상이 들어갈 프레임을 선언해 보겠습니다.

# 프레임 추가
frm = tk.Frame(win, bg="white", width=720, height=480) # 프레임 너비, 높이 설정
frm.grid(row=1, column=0) # 격자 행, 열 배치

프레임 영역을 보기 위해 bg를 whiter로 설정했고, 크기는 720x480입니다. (1, 0)에 프레임을 배치해 봅니다.

자, 이제 위 프레임에 동영상이 재생되도록 할 것입니다. 코딩상 어색하지만 이 작업을 위해 라벨을 하나 더 추가합니다.

# 라벨1 추가
lbl1 = tk.Label(frm)
lbl1.grid()

프레임 안에서 동작하는 동영상의 재생은 OpenCV가, 표출되는 GUI는 Tkinter가 작업할 것입니다. 그리고 OpenCV의 각 프레임을 Tkinter에 맞게 표출하는 과정은 PIL이 담당하며, 이 과정에서 lbl1을 활용할 것입니다.

 

그럼 이제 OpenCV에서 동영상을 재생하는 코드를 작성해볼까요?!

여기서는 이전 글에서 작성한 내용을 재사용할 것입니다.

OpenCV에서 동영상 파일의 재생 및 저장 | http://blog.daum.net/geoscience/1501

 

OpenCV에서 동영상 파일의 재생 및 저장

안녕하세요? 이번 글은 OpenCV에서 동영상 파일을 재생하고 저장하는 방법을 학습해 보도록 하겠습니다. 이 방법은 아래 OpenCV 공식 홈페이지에 자세히 안내되어 있는데요, 약간의 코드 수정과 추가 설명을 통해..

blog.daum.net

위 글에서 작성한 코드를 요약하면 다음과 같습니다. VideoCapture 객체를 통해 동영상을 재생하는 간단한 구조입니다.

os.chdir('D:/GEODATA') # 현재 작업 디렉토리 변경
cap = cv.VideoCapture('otter.avi') # VideoCapture 객체 정의
while cap.isOpened(): # cap 정상동작 확인
    ret, frame = cap.read() # 프레임이 올바르게 읽히면 ret은 True
    if not ret:
        print("프레임을 수신할 수 없습니다(스트림 끝?). 종료 중 ...")
        break
    frame = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
    cv.imshow('Otter', frame)
    if cv.waitKey(42) == ord('q'):
        break
# 작업 완료 후 해제
cap.release()
cv.destroyAllWindows()

위 코드를 약간 변경할 것입니다. 스택 오버플로(Stack Overflow)에 등록된 Devashish Prasad 님의 글을 참고하였습니다.

https://stackoverflow.com/questions/50922175/to-show-video-streaming-inside-frame-in-tkinter

 

to show video streaming inside frame in tkinter

I'm trying to display my live video inside my frame1. I want to know why it's not working as in cv2.imshow I have written frame1. Can anyone help me out Please? my code of my frame/window : from

stackoverflow.com

변경된 코드입니다.

os.chdir('D:/GEODATA') # 현재 작업 디렉토리 변경
cap = cv.VideoCapture('otter.avi') # VideoCapture 객체 정의
def video_play():
    ret, frame = cap.read() # 프레임이 올바르게 읽히면 ret은 True
    if not ret:
        cap.release() # 작업 완료 후 해제
        return
    frame = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
    img = Image.fromarray(frame) # Image 객체로 변환
    imgtk = ImageTk.PhotoImage(image=img) # ImageTk 객체로 변환
    # OpenCV 동영상
    lbl1.imgtk = imgtk
    lbl1.configure(image=imgtk)
    lbl1.after(10, video_play)
video_play()
win.mainloop() #GUI 시작

결과는 다음과 같습니다. 치악산국립공원 수달을 Tk 창에서 보니 더 반가운데요, 빈 공간은 계속 채워보도록 하겠습니다.