본문으로 바로가기
728x90

UI는 tkinter 패키지를 사용하면 간단하게 만들어 볼 수 있다.

사용법은 Java swing과 유사하다.

 

App 클래스에서 인터페이스를 관리하고,

MyVideoCapture에서 영상을 관리하도록 개발하였다.

 

tkinter의 after 함수를 통해 주기적으로 화면을 갱신하는데,

녹화영상의 fps를 맞추는 작업이 꽤나 까다로웠다.

fps를 그대로 사용하면 녹화된 영상이 매우 빠르게 재생된다.

3.0을 나눠주면 촬영 속도와 저장된 영상의 속도가 대충 맞는 것 같다.

 

추가로 주의할 사항은 cv2.VideoCapture 에서 받아온 영상은 반전된 BGR이므로

렌더링 할때는 반전과 RGB 변환 작업이 필요하다.

(참고로, 저장할 때는 BGR로 저장한다.)

 

렌더링 할 때는 PIL 패키지를 활용하였다.

좌측 상단에 현재 시간을 띄워주었다.

 

전체 소스코드

import tkinter
import cv2
import PIL.Image, PIL.ImageTk
import datetime

class App:
    def __init__(self, window, window_title, video_source=0):
        self.window = window
        self.window.title(window_title)
        self.video_source = video_source
        self.is_recording = False

        # open video source (by default this will try to open the computer webcam)
        self.vid = MyVideoCapture(self.video_source)

        # Create a canvas that can fit the above video source size
        self.canvas = tkinter.Canvas(window, width = self.vid.width, height = self.vid.height)
        self.canvas.pack()

        # Button that lets the user take a snapshot
        self.btn_snapshot = tkinter.Button(window, text="녹화 시작", width=20, height=5, command=self.record)
        self.btn_snapshot.pack(anchor=tkinter.CENTER, expand=True)

        # After it is called once, the update method will be automatically called every delay milliseconds
        self.fps = self.vid.webcam.get(cv2.CAP_PROP_FPS)
        self.delay = round(1000.0/self.fps)
        self.update()

        self.window.mainloop()

    def record(self):
        self.is_recording = ~self.is_recording
        if self.is_recording:
            self.vid.create_video(self.fps)
            self.btn_snapshot.config(text="녹화 중단")
        else:
            self.vid.video.release()
            self.btn_snapshot.config(text="녹화 시작")

    def update(self):
        # Get a frame from the video source
        ret, frame = self.vid.get_frame()

        if ret:
            if self.is_recording:
                self.vid.video.write(frame)

            self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)))
            self.canvas.create_image(0, 0, image = self.photo, anchor = tkinter.NW)
        self.window.after(self.delay, self.update)

    # Release the video source when the object is destroyed
    def __del__(self):
        if self.is_recording:
           self.vid.video.release()

class MyVideoCapture:
    def __init__(self, video_source=0):
        # Open the video source
        self.webcam = cv2.VideoCapture(video_source)
        if not self.webcam.isOpened():
            raise ValueError("Unable to open video source", video_source)

        # Get video source width and height
        self.width = self.webcam.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.height = self.webcam.get(cv2.CAP_PROP_FRAME_HEIGHT)
        #self.width = 1280
        #self.height = 720
        #self.webcam.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
        #self.webcam.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)

    def create_video(self, fps):
        file_name = str(datetime.datetime.now()) + '.mp4'
        # Define the codec and create VideoWriter object
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')  # opencv 3.0
        # Error: 'module' object has no attribute 'VideoWriter_fourcc'
        # fourcc=cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
        # jpeg,h263,'m', 'p', '4', 'v'
        self.video = cv2.VideoWriter(file_name, fourcc, fps/3.0, (self.width, self.height))

    def get_frame(self):
        if self.webcam.isOpened():
            font_color = (255, 255, 255)
            ret, frame = self.webcam.read()
            if ret:
                frame = cv2.flip(frame, 1)

                # describe the type of
                # font you want to display
                font = cv2.FONT_HERSHEY_SCRIPT_COMPLEX

                # Get date and time and
                # save it inside a variable
                dt = str(datetime.datetime.now())

                # put the dt variable over the
                # video frame
                frame = cv2.putText(frame, dt,
                                    (30, 60),    # position
                                    font, 1, font_color, 4, cv2.LINE_AA)

                # Return a boolean success flag and the current frame converted to BGR
                return (ret, frame)
            else:
                return (ret, None)
        else:
            return (False, None)

     # Release the video source when the object is destroyed
    def __del__(self):
        if self.webcam.isOpened():
            self.webcam.release()

# Create a window and pass it to the Application object
App(tkinter.Tk(), "Tkinter and OpenCV")
728x90