안녕하세요? 이번 글은 Tkinter와 OpenCV를 이용하여 동영상을 재생하는 GUI를 만들어 보겠습니다. Tkinter(트킨터)는 사실상 파이썬의 표준 GUI 패키지, OpenCV(Open Source Computer Vision Library)는 오픈소스 컴퓨터 비전 및 머신러닝 소프트웨어 라이브러리입니다.
Tkinter(트킨터) 파이썬 위키 | https://wiki.python.org/moin/TkInter
OpenCV 공식 홈페이지 | https://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
자, 그럼 시작해볼까요?! 일단 필요한 모듈을 호출합니다.
# 모듈 호출
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
위 글에서 작성한 코드를 요약하면 다음과 같습니다. 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
변경된 코드입니다.
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 창에서 보니 더 반가운데요, 빈 공간은 계속 채워보도록 하겠습니다.