본문 바로가기
OpenCV

파이썬 OpenCV출력 Tkinter UI로 하는 구조적 구현

by 가오가이거 2021. 1. 7.
#main.py
import tkinter as tk
import app_main.main_ui as win
import app_main.make_widgets as mkw
import app_main.camera_service as s

def main():
    img_path = 'img/a.jpg'
    root = tk.Tk()
    app = win.AppWindow(root, '650x700+100+100', img_path) #Frame
    service = s.CameraService(app) #카메라 서비스 생성
    mkw.make(app, service) #app 프레임을 받아서 거기에 위젯을 생성해 배치하고, 이벤트핸들러정의해놓는 작업
    app.mainloop()

main()

 

 

 

#main_ui.py
import tkinter as tk
import cv2
from PIL import Image
from PIL import ImageTk

class AppWindow(tk.Frame):#Frame
    def __init__(self, master=None, size=None, path=None):
        super().__init__(master)
        self.master = master #Tk()객체. 윈도우
        self.master.geometry(size)
        self.master.resizable(True, True)
        self.pack()#이미지 레이블 frame
        self.sub_fr = None#버튼 있는 frame
        self.src = None #이미지 변수
        self.frame = None #이미지 출력할 레이블
        self.create_widgets(path)#기본 위젯 생성 함수

    def make_img(self, path):#path 경로의 이미지를 레이블에 출력
        #opencv 이미지
        src = cv2.imread(path) #path 이미지를 읽어서 src에 저장
        src = cv2.resize(src, (640, 400))
        #이미지 color space를 rgb로 변경
        img = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)
        #넘파이 배열을 필로우 이미지 변환
        img = Image.fromarray(img)#넘파이 배열을 이미지로 변환
        #tkinter에서 사용할 수 있는 이미지로 변환
        #self.src = 이미지
        self.src = ImageTk.PhotoImage(image=img)#tkinter에서 인식할 수 있는 이미지로 생성

    def create_widgets(self, path):#프레임에 위젯 추가
        self.make_img(path)
        #이미지 뿌릴 레이블 생성
        self.frame = tk.Label(self.master, image=self.src)
        #레이블을 프레임에 붙임
        self.frame.pack()
        #추가 위젯을 배치할 프레임 생성
        self.sub_fr = tk.Frame(self.master)#frame
        #프레임 윈도우 붙임
        self.sub_fr.pack()

    def change_img(self, img):#레이블의 이미지 변경
        img = cv2.resize(img, (640, 400))
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = Image.fromarray(img)
        self.src = ImageTk.PhotoImage(image=img)
        self.frame['image']=self.src

 

 

 

#camera_service.py
import threading
import cv2, os

def preview_th(stop, app):
    cam = cv2.VideoCapture(0, cv2.CAP_DSHOW)
    print(1, stop)
    print(2, stop())
    cam.set(3, 640)
    cam.set(4, 400)
    while stop():
        ret, frame = cam.read()
        if ret:
            app.change_img(frame)
        cv2.waitKey(100)
    cam.release()

def img_view_th(stop, app):
    flist = os.listdir('./img')
    i=0
    while stop():
        p = './img/'+flist[i%len(flist)]
        i+=1
        src = cv2.imread(p)
        app.change_img(src)
        cv2.waitKey(1000)

def video_write_th(stop, app, fname):
    cam = cv2.VideoCapture(0, cv2.CAP_DSHOW)
    codec = cv2.VideoWriter_fourcc(*'mp4v')  # 사용할 코덱 생성
    #동영상 작성자 객체 생성
    path= './video/'+fname+'.mp4'
    out = cv2.VideoWriter(path, codec, 25.0, (640, 400))

    while stop(): 
        ret, frame = cam.read()
        if ret:
            frame = cv2.resize(frame, (640,400))
            out.write(frame)
            app.change_img(frame)
        else:
            break
        cv2.waitKey(26)
    cam.release()

def video_read_th(stop, app, fname):
    path = './video/' + fname
    cam = cv2.VideoCapture(path)
    # cam.set(3, 640)
    # cam.set(4, 400)

    while stop(): 
        ret, frame = cam.read()
        if ret:
            app.change_img(frame)
        else:
            break
        cv2.waitKey(26)
    cam.release()

class CameraService:
    def __init__(self, app):
        self.cam = None
        self.app = app #main_ui에서 만든 ui 창. AppWindow 객체
        self.flag = True  #쓰레드 종료 flag

    def stop(self):
        self.flag = False

    def preview(self):
        self.flag = True
        cam_th = threading.Thread(target=preview_th, args=(lambda:self.flag, self.app))
        cam_th.start()

    def capture(self, fname):
        print('capture')
        self.stop()
        self.cam = cv2.VideoCapture(0)
        self.cam.set(3, 640)
        self.cam.set(4, 400)
        ret, frame = self.cam.read()
        self.app.change_img(frame)
        cv2.imwrite('img/'+fname, frame)
        self.cam.release()

    def write_avi(self, fname):
        self.flag = True
        cam_th = threading.Thread(target=video_write_th, args=(lambda: self.flag, self.app, fname))
        cam_th.start()

    def view_img(self):
        self.flag = True
        cam_th = threading.Thread(target=img_view_th, args=(lambda: self.flag, self.app))
        cam_th.start()

    def view_video(self, fname):
        self.flag = True
        cam_th = threading.Thread(target=video_read_th,
                                  args=(lambda: self.flag, self.app, fname))
        cam_th.start()

 

 

 

#make_widgets.py
import tkinter as tk

c_serv = None  #서비스 객체

def btn1_clicked():
    c_serv.preview()

def btn2_clicked(app):
    fname = app.ent.get()
    c_serv.capture(fname)

def btn3_clicked(app):
    fname= app.ent.get()
    c_serv.write_avi(fname)
    print('btn3 clicked')

def btn4_clicked():
    c_serv.stop()

def btn5_clicked(app):
    fname= app.ent.get()
    c_serv.view_video(fname)

def btn6_clicked():
    c_serv.view_img()

def btn7_clicked():
    c_serv.stop()

def make(app, service=None):
    global c_serv
    c_serv = service
    app.ent = tk.Entry(app.sub_fr, width=60)
    app.btn1 = tk.Button(app.sub_fr, width=10, font=60, text='preview')
    app.btn2 = tk.Button(app.sub_fr, width=10, font=60, text='사진저장')
    app.btn3 = tk.Button(app.sub_fr, width=10, font=60, text='영상촬영')
    app.btn4 = tk.Button(app.sub_fr, width=10, font=60, text='촬영종료')
    app.btn5 = tk.Button(app.sub_fr, width=10, font=60, text='동영상보기')
    app.btn6 = tk.Button(app.sub_fr, width=10, font=60, text='슬라이드시작')
    app.btn7 = tk.Button(app.sub_fr, width=10, font=60, text='슬라이드종료')

    app.ent.grid(row=0, column=0, columnspan=4)
    app.btn1.grid(row=1, column=0)
    app.btn2.grid(row=1, column=1)
    app.btn3.grid(row=1, column=2)
    app.btn4.grid(row=1, column=3)
    app.btn5.grid(row=2, column=0)
    app.btn6.grid(row=2, column=1)
    app.btn7.grid(row=2, column=2)

    app.btn1['command'] = btn1_clicked
    app.btn2['command'] = lambda: btn2_clicked(app)
    app.btn3['command'] = lambda: btn3_clicked(app)
    app.btn4['command'] = btn4_clicked
    app.btn5['command'] = lambda: btn5_clicked(app)
    app.btn6['command'] = btn6_clicked
    app.btn7['command'] = btn7_clicked