토이프로젝트/기타

[python-opencv] 동영상 프레임 추출 프로그램

제주도랏맨 2022. 2. 21. 04:24

 

다운로드

 

학교에서 졸업 프로젝트를 진행하면서 영상의 프레임을 추출해야하는 일이 생겨서 만든 프로그램이다.

이 작업 하나 때문에 프로그램 깔기도 좀 뭐하고

파이썬을 막 시작했을 때라 뭔가 만들어보면 좋을 것 같아서 만들었었다.

 

gui는 tkinter 라이브러리로, 동영상 프레임은 opencv 추출하도록 짰고,

exe파일화는 pyinstaller 라이브러리를 깔아서 만들었다.

 

지금도 그렇긴하지만 파이썬의 기초조차도 아에 모를 때라서 검색에 의존해서 마구자비로 만들었는데

꽤 최근까지도 나름 잘 썼었다.(그런데 폴더나 이름이 한글이면 관련 처리를 안해줘서 추출이 안됬던걸로 기억)

 

import os

desktop = os.path.join(os.path.expanduser('~'),'Desktop')

def fileload():
    filename = fd.askopenfilename(initialdir=desktop, title="Select file",
                                              filetypes=(("video files", "*.mp4"),
                                              ("all files", "*.*")))
    global filedir
    filedir = filename

    global filedirtext
    filedirtext.delete(0, 'end')
    filedirtext.insert(0, filedir)

 

파일 불러오기 화면과 함수이다.
함수를 정의하기 전에 os 모듈을 import하고 os.path~ 명령으로 사용자의 바탕하면 경로를 찾아 저장한다.
그 후 버튼이 눌리면 fileload함수를 호출하여 파일 선택기를 바탕화면을 보여주도록 띄운다.

 

파일이 선택되면 filename에 파일의 경로가 저장되고 이를 전역변수인 filedir에 저장한 후
UI에서 경로를 띄워주는 filedirtext의 기존 내용을 지우고 삽입한다.

 

def run():
    if(filedir == None or dirpath == None):
        msgbox.showinfo("Error!", "입력 파일이 없습니다")
        return
    else:  
        imgwid = int(videowidth.get())
        imghei = int(videoheight.get())

        frame = int(spinbox.get())

        print("Start Extracting...")
        print(filedir)
        print(dirpath)

        vidcap = cv2.VideoCapture(filedir)

        count = 0

        while(vidcap.isOpened()):

            ret, image = vidcap.read()

            if(ret == FALSE): break

            if(int(vidcap.get(1)) % frame == 0):
                print("Saved frame number : " + str(int(vidcap.get(1))))

                image = cv2.resize(image, dsize=(imgwid, imghei), fx=imgfx, fy=imgfy, interpolation=cv2.INTER_LINEAR)

                cv2.imwrite(dirpath + "/frame%d.png" % count, image)

                count += 1

        vidcap.release()
        msgbox.showinfo("Finish!", "추출이 끝났습니다.")
    
        return

 

opencv를 이용해서 동영상을 불러와 프레임 단위로 추출해 저장하는 코드이다.

vidcap에 저장된 동영상을 열고 읽어서 지정한 frame 변수마다 imwrite로 프레임을 저장한다.

 

더보기
from tkinter import *
from tkinter import ttk
import tkinter as tk
import tkinter.filedialog as fd
import tkinter.messagebox as msgbox
import cv2
import os

filedir = None
dirpath = None
desktop = os.path.join(os.path.expanduser('~'),'Desktop')

def fileload():
    filename = fd.askopenfilename(initialdir=desktop, title="Select file",
                                              filetypes=(("video files", "*.mp4"),
                                              ("all files", "*.*")))
    global filedir
    filedir = filename

    global filedirtext
    filedirtext.delete(0, 'end')
    filedirtext.insert(0, filedir)

def pathload():
    pathdiry = fd.askdirectory(initialdir=desktop, title="Select file")
    global dirpath 
    dirpath = pathdiry

    global pathdirtext
    pathdirtext.delete(0, 'end')
    pathdirtext.insert(0, dirpath)

def choose():
    global combo
    global imgwid
    global imghei
    global imgfx
    global imgfy
    global videowidth
    global videoheight

    opnum = combo.current()

    if opnum == 0:
        videowidth.delete(0, 'end')
        videoheight.delete(0, 'end')
        videowidth.insert(0, 0)
        videoheight.insert(0, 0)
        videowidth.configure(state=DISABLED)
        videoheight.configure(state=DISABLED)

        imgwid = 0
        imghei = 0
        imgfx = 0.75
        imgfy = 0.75
    elif opnum == 1:
        videowidth.delete(0, 'end')
        videoheight.delete(0, 'end')
        videowidth.insert(0, 0)
        videoheight.insert(0, 0)
        videowidth.configure(state=DISABLED)
        videoheight.configure(state=DISABLED)

        imgwid = 0
        imghei = 0
        imgfx = 0.5
        imgfy = 0.5
    elif opnum == 2:
        videowidth.delete(0, 'end')
        videoheight.delete(0, 'end')
        videowidth.insert(0, 0)
        videoheight.insert(0, 0)
        videowidth.configure(state=DISABLED)
        videoheight.configure(state=DISABLED)

        imgwid = 0
        imghei = 0
        imgfx = 0.25
        imgfy = 0.25
    elif opnum == 3:
        videowidth.delete(0, 'end')
        videoheight.delete(0, 'end')
        videowidth.insert(0, 0)
        videoheight.insert(0, 0)
        videowidth.configure(state=DISABLED)
        videoheight.configure(state=DISABLED)

        imgwid = 0
        imghei = 0
        imgfx = 0.1
        imgfy = 0.1
    elif opnum == 4:
        videowidth.configure(state="normal")
        videoheight.configure(state="normal")

        imgfx = 0
        imgfy = 0

def resizeon():
    global combo
    global imgwid
    global imghei
    global imgfx
    global imgfy

    if(chkValue.get() == FALSE) :
        videowidth.configure(state=DISABLED)
        videoheight.configure(state=DISABLED)
        combo.configure(state="disabled")
        videowidth.delete(0, 'end')
        videoheight.delete(0, 'end')
        videowidth.insert(0, 0)
        videoheight.insert(0, 0)
        imgfx = 1
        imgfy = 1
    else:
        combo.configure(state="readonly")
        choose()

def option(event):
    choose()     

def run():
    if(filedir == None or dirpath == None):
        msgbox.showinfo("Error!", "입력 파일이 없습니다")
        return
    else:  
        imgwid = int(videowidth.get())
        imghei = int(videoheight.get())

        frame = int(spinbox.get())

        print("Start Extracting...")
        print(filedir)
        print(dirpath)

        vidcap = cv2.VideoCapture(filedir)

        count = 0

        while(vidcap.isOpened()):

            ret, image = vidcap.read()

            if(ret == FALSE): break

            if(int(vidcap.get(1)) % frame == 0):
                print("Saved frame number : " + str(int(vidcap.get(1))))

                image = cv2.resize(image, dsize=(imgwid, imghei), fx=imgfx, fy=imgfy, interpolation=cv2.INTER_LINEAR)

                cv2.imwrite(dirpath + "/frame%d.png" % count, image)

                count += 1

        vidcap.release()
        msgbox.showinfo("Finish!", "추출이 끝났습니다.")
    
        return
    
# 창만들기
print("*****************Frame Extractor by LJH**********************")

root = Tk()
root.title("Frame Extrator")
root.geometry("250x350")
root.resizable(False, False)

videotext = Label(root, text="Video")
videotext.place(x=30, y=30)

filedirtext = tk.Entry(root)
# filedirtext.configure(state="readonly")
filedirtext.place(x=30, y=50, width=135, height=25)

filebtn = Button(root, text="Load", command=fileload)
filebtn.place(x=170, y=50, width=50)

foldertext = Label(root, text="저장 경로")
foldertext.place(x=30, y=90)

pathdirtext = tk.Entry(root)
# pathdirtext.configure(state=DISABLED)
pathdirtext.place(x=30, y=110, width=135, height=25)

pathbtn = Button(root, text="Load", command=pathload)
pathbtn.place(x=170, y=110, width=50)

frametext = Label(root, text="frame")
frametext.place(x=30, y=150)

var = StringVar(root)
var.set("15")
spinbox=tk.Spinbox(root, from_ = 0, to = 1000, textvariable=var)
spinbox.place(x=30, y=170, width=190, height=25)

combo = ttk.Combobox(root, width=20, textvariable=str, state='readonly')
combo['values'] = ('75%', '50%', '25%', '10%', '직접 설정')
combo.current(1)
combo.place(x=100, y=213, width=117)
combo.bind("<<ComboboxSelected>>", option)

imgwid = 0
imghei = 0
imgfx = 0.5
imgfy = 0.5

chkValue = tk.BooleanVar() 
resize = tk.Checkbutton(root, text="Resize", command=resizeon, variable=chkValue)
resize.place(x=25, y=210)
resize.select()

videowidth = tk.Entry(root)
videowidth.insert(0, 0)
videowidth.configure(state=DISABLED)
videowidth.place(x=70, y=250, width=65, height=25)

x = Label(root, text="X")
x.place(x=138, y=250)

videoheight = tk.Entry(root, width=20)
videoheight.insert(0, 0)
videoheight.configure(state=DISABLED)
videoheight.place(x=155, y=250, width=65, height=25)

runbtn = Button(root, text="Run!", height=3, command=run)
runbtn.pack(side="bottom", fill="x")

root.mainloop()