この記事はPythonで動画に関するアプリを作る際に大変役立つと思います。
ネットにこれがっている同じような記事とは全然違います。
今回はアプリっぽくなるように再生ボタンを実装します。
状態遷移は、 空き 再生中 の2つになります。
空き:
タイマーもスレッドも動作していません。
空き→再生中: 録画ボタンをクリックで遷移します。 タイマーとスレッドが動作します。
再生中→空き 録画ボタン(停止と表示されている)をクリックで遷移します。 再生が終わると遷移します。 タイマー、スレッドを終了します。
キモになるところ: 画像の読込はFPSの時間だけ待たないとあっという間に超高速再生で終わってしまいます。表示するタイマー時間も同様に扱います。
def ThreadVideoCapture(self):
while True:
try:
if self.quit == True:
break
ret, self.frame = self.videoPlayer.read()
if ret:
self.dataExist = True
time.sleep(1/self.fps)
else:
self.dataExist = False
break
except:
break
self.termated = True
self.videoPlayer.release()
print("terminate thread")
全ソースです。
import tkinter
from tkinter.constants import SEL_FIRST
import cv2
import PIL.Image, PIL.ImageTk
import time
from datetime import datetime
import threading
class videoApp:
def __init__(self, window, window_title, cameraId=0):
self.window = window
self.window.title(window_title)
self.cameraId = cameraId
# open video source (by default this will try to open the computer webcam)
self.playDevice = MyVideoPlay()
# Create a canvas that can fit the above video source size
self.canvas = tkinter.Canvas(self.window, width = 640, height = 480)
self.canvas.pack()
#録再ボタン
self.playing = False
self.btmPlay = tkinter.Button(
self.window,
text='再生',
command=self.play_click)
self.btmPlay.pack()
# self.showFhoto()
self.window.mainloop()
self.playDevice.terminateReq()
def play_click(self):
if self.playing == False:
self.playing = True
self.btmPlay.configure(text='停止')
vFile = 'my_video.avi'
self.playDevice.setVideoFile(vFile)
self.canvas.configure(width=self.playDevice.width, height=self.playDevice.height)
#スレッドの開始
self.playDevice.termated = False
#スレッドの作成
self.threadVideo = threading.Thread(target=self.playDevice.ThreadVideoCapture)
#スレッドの開始
self.threadVideo.start()
self.showFhoto()
else:
self.playing = False
self.btmPlay.configure(text='再生')
#スレッドの終了
self.playDevice.terminateReq()
self.threadVideo.join()
def showFhoto(self):
ret, frame = self.playDevice.get_frame()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))
self.canvas.create_image(0, 0, image = self.photo, anchor = tkinter.NW)
if self.playDevice.termated == False:
tm = 1000 / self.playDevice.fps
tm = int(tm)
self.window.after(tm, self.showFhoto)
else:
self.btmPlay.configure(text='再生')
self.playing = False
self.playDevice.terminateReq()
self.threadVideo.join()
def __del__(self):
if self.playing == True:
self.playDevice.terminateReq()
self.threadVideo.join()
class MyVideoPlay:
def __init__(self):
self.quit = False
self.termated = False
self.dataExist = False
def setVideoFile(self, vFile):
# Open the video source
self.videoPlayer = cv2.VideoCapture(vFile)
if not self.videoPlayer.isOpened():
raise ValueError("Unable to open video source")
# Get video source width and height
self.width = self.videoPlayer.get(cv2.CAP_PROP_FRAME_WIDTH)
self.height = self.videoPlayer.get(cv2.CAP_PROP_FRAME_HEIGHT)
# Video info
self.fps = self.videoPlayer.get(cv2.CAP_PROP_FPS)
self.quit = False
self.termated = False
self.dataExist = False
def terminateReq(self):
self.quit = True
def get_frame(self):
if self.dataExist:
return True, self.frame
return False, None
def ThreadVideoCapture(self):
while True:
try:
if self.quit == True:
break
ret, self.frame = self.videoPlayer.read()
if ret:
self.dataExist = True
time.sleep(1/self.fps)
else:
self.dataExist = False
break
except:
break
self.termated = True
self.videoPlayer.release()
print("terminate thread")
def __del__(self):
if self.videoPlayer.isOpened():
self.videoPlayer.release()
if __name__ == '__main__':
videoApp(tkinter.Tk(), "Video Playing")
ビデオの録画・再生ができると面白いアプリができそうです。
|