はじめに
前回のcameraサンプルを改良して動画を再生できるようにしたいと思います。
また、画面に日本語を表示しようとすると文字化けするので表示出来るようにします。
ファイルコピー
camera.pyをコピーしてvideo.pyを作成します。
日本語表示
日本語対応のフォントに変更することで表示可能となります。以下はWindowsにインストールされている「MS ゴシック」の設定です。
from kivy.core.text import LabelBase, DEFAULT_FONT LabelBase.register(DEFAULT_FONT, "c:/windows/fonts/msgothic.ttc")
画面改良
・ファイルダイアログを利用して動画ファイルを選択してファイル名を表示するように修正
・Face、Mosaicを車、ナンバーに修正
その他、Playを再生 Captureを保存にしました。
改良したソースファイル
保存先は、D:\PyDev\GuiKivyです。
仮想環境のアクティブ化を忘れずに実行します。env\Scripts\activate
from kivy.config import Config Config.set('graphics', 'width', '800') Config.set('graphics', 'height', '640') from kivy.app import App from kivy.lang import Builder from kivy.uix.boxlayout import BoxLayout import time import cv2 from kivy.uix.image import Image from kivy.graphics.texture import Texture from kivy.properties import StringProperty, ObjectProperty from kivy.clock import Clock import os from kivy.uix.floatlayout import FloatLayout from kivy.uix.popup import Popup from kivy.uix.textinput import TextInput # 日本語表示対応 from kivy.core.text import LabelBase, DEFAULT_FONT LabelBase.register(DEFAULT_FONT, "c:/windows/fonts/msgothic.ttc") Builder.load_string(''' <CameraClick>: orientation: 'vertical' padding: 0 spacing: 1 size_hint_y: None BoxLayout: orientation: 'horizontal' padding: 0 spacing: 1 Image: id: camera size_hint: None, None size: 640, 480 BoxLayout: orientation: 'vertical' ToggleButton: text: '車' on_press: root.face() size_hint_y: None height: '48dp' ToggleButton: text: 'ナンバー' on_press: root.mosaic() size_hint_y: None height: '48dp' BoxLayout: orientation: 'horizontal' padding: 0 spacing: 1 size_hint_y: None height: '48dp' TextInput: id: txtFName text: '' multiline: False Button: text: 'ファイル選択' on_press: root.fileSelect() ToggleButton: text: '再生' on_press: root.play() size_hint_y: None height: '48dp' Button: text: '保存' size_hint_y: None height: '48dp' on_press: root.capture() <LoadDialog>: BoxLayout: size: root.size pos: root.pos orientation: 'vertical' FileChooserListView: id: filechooser BoxLayout: size_hint_y : None height : 30 Button: text: 'キャンセル' on_release: root.cancel() Button: text: '読み込み' on_release: root.load(filechooser.path, filechooser.selection) ''') class LoadDialog(FloatLayout): load = ObjectProperty(None) cancel = ObjectProperty(None) class CameraClick(BoxLayout): image_texture = ObjectProperty(None) image_capture = ObjectProperty(None) def fileSelect(self): content = LoadDialog(load = self.load, cancel = self.dismiss_popup) self._popup = Popup( title="動画ファイル選択中", content=content, size_hint=(0.9,0.9)) self._popup.open() def load (self, path, filename): txtFName = self.ids['txtFName'] txtFName.text = filename[0] self.dismiss_popup() def dismiss_popup(self): self._popup.dismiss() def face(self): global flgFace flgFace = not flgFace def mosaic(self): global flgMosaic flgMosaic = not flgMosaic def play(self): global flgPlay flgPlay = not flgPlay if flgPlay == True: txtFName = self.ids['txtFName'] self.image_capture = cv2.VideoCapture(txtFName.text) Clock.schedule_interval(self.update, 1.0 / 30) else: Clock.unschedule(self.update) self.image_capture.release() def update(self, dt): ret, frame = self.image_capture.read() if ret: if flgFace == True or flgMosaic == True: # カスケードファイルを指定して検出器を作成 cascade_file = "haarcascade_frontalface_alt.xml" cascade = cv2.CascadeClassifier(cascade_file) # カメラ映像をグレースケールに変換 frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 顔検出 face_list = cascade.detectMultiScale(frame_gray, minSize=(30, 30)) if len(face_list) > 0: red = (0, 0, 255) for (x, y, w, h) in face_list: if flgFace == True: frame = cv2.rectangle(frame, (x, y), (x+w, y+h), red, thickness = 2) if flgMosaic == True: frame = self.procMosaic(frame, (x, y, x+w, y+h), 5) # カメラ映像を上下反転 buf = cv2.flip(frame, 0) # カメラ映像を上下左右反転 #buf = cv2.flip(frame, -1) image_texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr') image_texture.blit_buffer(buf.tostring(), colorfmt='bgr', bufferfmt='ubyte') camera = self.ids['camera'] camera.texture = image_texture def capture(self): ''' Function to capture the images and give them the names according to their captured time and date. ''' camera = self.ids['camera'] timestr = time.strftime("%Y%m%d_%H%M%S") camera.export_to_png("IMG_{}.png".format(timestr)) print("Captured") def procMosaic(self, img, rect, size): # モザイクをかける領域を取得 (x1, y1, x2, y2) = rect w = x2 - x1 h = y2 -y1 i_rect =img[y1:y2, x1:x2] # 一度縮小して拡大する i_small = cv2.resize(i_rect, (size, size)) i_mos = cv2.resize(i_small, (w, h), interpolation=cv2.INTER_AREA) # 画像にモザイク画像を重ねる img2 = img.copy() img2[y1:y2, x1:x2] = i_mos return img2 class TestVideo(App): def build(self): return CameraClick() flgPlay = False flgFace = False flgMosaic = False TestVideo().run()
ちょこっと解説
Windowサイズの指定
・1行目~3行目までで指定しております。
ファイルダイアログ
・82行目~99行目までで画面定義してます。FileChooserListViewを利用すると実現できます。
・ファイルを読み込むとテキストボックスにファイル名が表示されます。
動画再生
・再生ボタンをクリックするとテキストボックスに表示されているファイル名をcv2.VideoCaptureに引数として渡す事で再生します。
・もう一度再生ボタンをクリックすると動画は停止され、再度、再生すると最初から再生します。
最後に
cv2.VideoCapture の引数に動画ファイル名を指定するだけで簡単に改良できたのですが、Windowのサイズ設定やファイルダイアログの実装で試行錯誤してしまいました。
車やナンバーの認識は今回実装できておりません(顔認識のまま)ので、今後の課題です(^^;)
改良の余地ありありなので自分用試行錯誤メモな感じです。参考にされる場合はご注意ください。