[PYTHON] GUI-Programmierung mit kivy ~ Teil 3 Video und Suchleiste ~

Einführung

Im vorherigen Artikel (https://qiita.com/kurodae/items/ba0d1c7115d1dc9ad7cf) habe ich viel über den Fortschrittsbalken geschrieben. In diesem Artikel werde ich versuchen, selbst so etwas wie einen Videoplayer zu erstellen. (Kein Ton ...) Ursprünglich wurde Video Player von kivy bereitgestellt, um die Ergebnisse der Videoanalyse (Objekterkennung usw.) zu visualisieren. /api-kivy.uix.videoplayer.html), aber ich habe versucht, einen anpassbaren Player zu erstellen. Wie der Titel schon sagt, fiel es mir schwer, die Suchleiste für den Videoplayer zu implementieren. Daher schreibe ich diesen Artikel für den Informationsaustausch.

Umgebung

Wie eingangs erwähnt, dient der Zweck der Visualisierung der Videoanalyseergebnisse. Installieren Sie daher die Bibliothek opencv für die Videoanalyse (Bildanalyse).

Meine Betriebsumgebung ist wie folgt.

OSX 10.14.6 Python 3.7.2

Wenn Sie pip verwenden, können Sie es mit nur dem folgenden Befehl installieren.

pip install python-opencv

Was ist eine Suchleiste?

Zitiert aus Wiki Ich werde.

Die Suchleiste ist eine der Funktionen der Musik- / Videowiedergabesoftware usw. und zeigt den Wiedergabeort von Daten an. Anhand der Position des "Schiebereglers" können Sie visuell erfassen, wie weit Sie Musik oder Filme vom Anfang bis zum Ende abspielen. Sie können den Schieberegler direkt mit der Maus bewegen und von jedem Ort aus mit der Wiedergabe beginnen Es gibt Vorteile wie.

Seek Bar ist eine Art Schieberegler. Was ist ein Schieberegler? [wiki](https://ja.wikipedia.org/wiki/%E3%82%A6%E3%82%A3%E3%82%B8%E3%82%A7%E3%83%83%E3%83 Es wird aus% 88_ (GUI) #% E9% 81% B8% E6% 8A% 9E) zitiert.

Schieberegler - Ähnlich einer Bildlaufleiste, jedoch ein Widget zum Festlegen eines Werts, nicht zum Scrollen.

kivy verfügt über ein Widget Slider, sodass Sie es zu einer Suchleiste machen können.

Verwendung von kivy.uix.slider

Ich fühle mich so. シークバー.gif

Die Quelle ist wie folgt.


from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock

Builder.load_string('''
<MySlider>
    orientation: 'vertical'
    Label : 
        text: "{}".format(slider.value)
        font_size: slider.value
        
    Slider: 
        id: slider
        step: 1
        min: 200
        max: 500
        
    Button: 
        text: "press"
        on_press: root.move_slider_start()
        
''')

class MySlider(BoxLayout):
    def __init__(self, **kwargs):
        super(MySlider, self).__init__(**kwargs)

    def move_slider_start(self):
        Clock.schedule_interval(self.move_slider, 1 / 60)

    def move_slider(self, dt):
        slider = self.ids['slider']
        if slider.max > slider.value:
            slider.value += 1
        else:
            return False
        
class sliderTest(App):

    def build(self):
        return MySlider()

sliderTest().run()

Die Quelle ist, dass der Wert des Schiebereglers mit dem Text und der Schriftart der Beschriftung am oberen Bildschirmrand verknüpft ist und sich die Beschriftung ändert, wenn der Schieberegler bewegt wird. Ich habe auch einen Prozess hinzugefügt, um den Schiebereglerwert kontinuierlich mit der Uhr zu aktualisieren, wenn Sie die Taste unten drücken (ich werde später etwas Ähnliches mit dem Videoplayer tun).

In der kv-Sprache wird der Beschriftungstext mithilfe der Slider-ID aktualisiert.

    Label : 
        text: "{}".format(slider.value)
        font_size: slider.value

    Slider: 
        id: slider #Ich würde. Es kann in kv-Sprache oder Python aufgerufen werden.
        step: 1 #Der Mindestwert beim Bewegen des Schiebereglers. Wenn Sie es nicht einstellen, erhalten Sie einen fehlerhaften Float-Wert
        min: 200 #Mindestwert des Schiebereglers
        max: 500 #Maximalwert des Schiebereglers

Wählen Sie beim automatischen Verschieben des Schiebereglers außerdem das Widget aus "IDs" aus, in denen die dem Widget zugewiesene ID als assoziatives Array gespeichert ist, und ändern Sie die Parameter wie unten gezeigt. Wie im vorherigen Artikel kann es nicht mit der for-Anweisung betrieben werden, daher habe ich den Schiebereglerwert mit Clock geändert und die Bildschirmzeichnung aktualisiert.


    def move_slider(self, dt):
        slider = self.ids['slider'] #Hier!
        if slider.max > slider.value:
            slider.value += 1
        else:
            return False

Was du versuchst zu machen

Das Bild, das ich dieses Mal machen möchte, ist in der folgenden Abbildung dargestellt.

動画プレイヤー.png

Es ist wie ein Videoplayer mit nur minimalen Funktionen. Sie müssen lediglich das Video laden, abspielen, einen beliebigen Wiedergabestandort angeben und die aktuelle Anzahl der Bilder überprüfen.

Das Video ist wie der Umgang mit opencv.

Quelle

VideoApp.py


from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics.texture import Texture
from kivy.properties import ObjectProperty
from kivy.clock import Clock

from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup

import cv2

Builder.load_file('VideoApp.kv')

#Popup zur Videoauswahl
class LoadDialog(FloatLayout):
    load = ObjectProperty(None)
    cancel = ObjectProperty(None)

class MyVideoPlayer(BoxLayout):
    image_texture = ObjectProperty(None)
    image_capture = ObjectProperty(None)

    def __init__(self, **kwargs):
        super(MyVideoPlayer, self).__init__(**kwargs)
        self.flagPlay = False #Wird das Video abgespielt?
        self.now_frame = 0 #Variable zum Überprüfen des Wiedergaberahmens des Videos auf Suchleiste
        self.image_index = [] #Array zum Speichern von OpenCV-Bildern für die Suchleiste

    #Popup zum Laden des Videos
    def fileSelect(self):
        content = LoadDialog(load = self.load, cancel = self.dismiss_popup)
        self._popup = Popup( title="File Select", content=content, size_hint=(0.9,0.9))
        self._popup.open()

    #Laden von Videodateien
    def load (self, path, filename):
        txtFName = self.ids['txtFName']
        txtFName.text = filename[0]
        self.image_capture = cv2.VideoCapture(txtFName.text)
        self.sliderSetting()
        self.dismiss_popup()

    #Popup schließen
    def dismiss_popup(self):
        self._popup.dismiss()

    #Suchen Sie nach Balkeneinstellungen
    def sliderSetting(self):
        count = self.image_capture.get(cv2.CAP_PROP_FRAME_COUNT)
        self.ids["timeSlider"].max = count

        #Laden Sie das Video einmal und speichern Sie alle Frames in einem Array
        while True:
            ret, frame = self.image_capture.read()
            if ret:
                self.image_index.append(frame)

            else:
                self.image_capture.set(cv2.CAP_PROP_POS_FRAMES, 0)
                break

    #Video-Wiedergabe
    def play(self):
        self.flagPlay = not self.flagPlay
        if self.flagPlay == True:
            self.image_capture.set(cv2.CAP_PROP_POS_FRAMES, self.now_frame)
            Clock.schedule_interval(self.update, 1.0 / self.image_capture.get(cv2.CAP_PROP_FPS))
        else:
            Clock.unschedule(self.update)

    #Verarbeitung der Videowiedergabeuhr
    def update(self, dt):
        ret, frame = self.image_capture.read()
        #Wenn der nächste Frame gelesen werden kann
        if ret:
            self.update_image(frame)
            time = self.image_capture.get(cv2.CAP_PROP_POS_FRAMES)
            self.ids["timeSlider"].value = time
            self.now_frame = int(time)

    #Suchleiste
    def siderTouchMove(self):
        Clock.schedule_interval(self.sliderUpdate, 0)

    #Bildschirmzeichnungsprozess, wenn die Suchleiste verschoben wird
    def sliderUpdate(self, dt):
        #Wenn der Suchbalkenwert und der Wiedergaberahmenwert unterschiedlich sind
        if self.now_frame != int(self.ids["timeSlider"].value):
            frame = self.image_index[self.now_frame-1]
            self.update_image(frame)
            self.now_frame = int(self.ids["timeSlider"].value)

    def update_image(self, frame):
        ##############################
        #Schreiben Sie hier die Bildverarbeitungsquelle! !!
        ##############################
        
        #auf den Kopf stellen
        buf = cv2.flip(frame, 0)
        image_texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
        image_texture.blit_buffer(buf.tostring(), colorfmt='bgr', bufferfmt='ubyte')
        video = self.ids['video']
        video.texture = image_texture

class TestVideo(App):

    def build(self):
        return MyVideoPlayer()

TestVideo().run()

kv-Datei

VideoApp.kv


<MyVideoPlayer>:
    orientation: 'vertical'
    padding: 0
    spacing: 1

    BoxLayout:
        orientation: 'horizontal'
        padding: 0
        spacing: 1
        size_hint: (1.0, 0.1)

        TextInput:
            id: txtFName
            text: ''
            multiline: False

        Button:
            text: 'file load'
            on_press: root.fileSelect()

    BoxLayout:
        orientation: 'horizontal'
        padding: 0
        spacing: 1

        Image:
            id: video

    BoxLayout:
        orientation: 'horizontal'
        padding: 0
        spacing: 1
        size_hint: (1.0, 0.1)
        Slider:
            id: timeSlider
            value: 0.0
            max: 0.0
            min: 0.0
            step: 1
            on_touch_move: root.siderTouchMove()

    BoxLayout:
        orientation: 'horizontal'
        padding: 0
        spacing: 1
        size_hint: (1.0, 0.1)

        ToggleButton:
            size_hint: (0.2, 1)
            text: 'Play'
            on_press: root.play()

        Label:
            size_hint: (0.2, 1)
            text: str(timeSlider.value) + "/" + str(timeSlider.max)

<LoadDialog>:
    BoxLayout:
        size: root.size
        pos: root.pos
        orientation: 'vertical'
        FileChooserListView:
            id: filechooser
            path: "./"

        BoxLayout:
            size_hint_y : None
            height : 30
            Button:
                text: 'Cancel'
                on_release: root.cancel()

            Button:
                text: 'Load'
                on_release: root.load(filechooser.path, filechooser.selection)

Wenn Sie es ausführen, können Sie es so spielen oder die Suchleiste verschieben. Das Video wurde von der Website hier ausgeliehen. Player.gif

Ein kleiner Kommentar

Das Video selbst wird in der gleichen Dosis wie opencvs Video Cupture abgespielt. Ich bin sicher, niemand hat es verwendet, aber es ist, als würde man ein Video Frame für Frame mit einer while-Anweisung lesen und anzeigen (so / python-opencv-videocapture-file-camera /)). Wenn Sie für oder während verwenden, friert es wieder ein. Verwenden Sie also Clock.

    #Verarbeitung der Videowiedergabeuhr
    def update(self, dt):
        ret, frame = self.image_capture.read()
        #Wenn der nächste Frame gelesen werden kann
        if ret:
            self.update_image(frame) #Verarbeitung zum Kopieren auf den Bildschirm
            time = self.image_capture.get(cv2.CAP_PROP_POS_FRAMES) #Ermitteln Sie die Anzahl der Frames für die Suchleiste
            self.ids["timeSlider"].value = time #Ersetzen Sie den Suchleistenwert durch die Anzahl der Wiedergabebilder
            self.now_frame = int(time) #Zum Verschieben der Suchleiste

Verwenden Sie beim Anzeigen eines Bildes auf kivy eine Klasse namens Texture mit `` `blit_buffer```. Behandeln Sie das Bild als Puffer. Zu diesem Zeitpunkt stehen die Bilddaten von opencv auf dem Kopf. Fügen Sie daher einen Prozess hinzu, um sie zu invertieren. Auf diese Weise können Sie die Videowiedergabefunktion auf die gleiche Weise wie bei der normalen openCV-Videowiedergabe implementieren. Die Bildverarbeitung wird diesmal nicht ausgeführt. Wenn Sie hier jedoch eine Verarbeitung wie opencv hinzufügen, können Sie die Verarbeitungsergebnisse problemlos visualisieren.

    def update_image(self, frame):
        ##############################
        #Schreiben Sie hier die Bildverarbeitungsquelle! !!
        ##############################
        
        #auf den Kopf stellen
        buf = cv2.flip(frame, 0)
        image_texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
        image_texture.blit_buffer(buf.tostring(), colorfmt='bgr', bufferfmt='ubyte')
        video = self.ids['video']
        video.texture = image_texture

In der Videosuchleiste zunächst aus der Set-Funktion (Funktion, die den Wiedergaberahmen angibt) der VideoCupter-Klasse von opencv Ich habe versucht, eine Suchleiste zu implementieren, indem ich den Slider-Wert auf die Bildnummer des Videos angewendet habe. Als ich es implementierte, wurde die Operation jedoch extrem schwer. Da die Set-Funktion ein sehr schwerer Prozess zu sein scheint, habe ich nach einer anderen Implementierungsmethode gesucht.

Infolgedessen war es einfach zu implementieren, indem die Bilddaten von opencv so gespeichert wurden, wie sie sich im Array befinden, und der Wert von Slider dem Index des Array von Bildern zugeordnet wurde. Daher wird beim Lesen eines Videos ein Prozess bereitgestellt, mit dem das Video einmal abgespielt und dem Array zugewiesen werden kann.

Da das einmal geladene VideoCupture im Programm wiederverwendet wird, müssen Sie "self.image_capture.set (cv2.CAP_PROP_POS_FRAMES, 0)" angeben, um die Wiedergabeposition des Videos in den Ausgangszustand zurückzusetzen , Sie können das Video nicht abspielen.

    #Suchen Sie nach Balkeneinstellungen
    def sliderSetting(self):
        count = self.image_capture.get(cv2.CAP_PROP_FRAME_COUNT) #Holen Sie sich die Anzahl der Bilder in einem Video
        self.ids["timeSlider"].max = count #Ersetzen Sie den Maximalwert des Schiebereglers durch die Anzahl der Videobilder

        #Laden Sie das Video einmal und speichern Sie alle Frames in einem Array
        while True:
            ret, frame = self.image_capture.read()
            if ret:
                self.image_index.append(frame)

            else:
                #Kehren Sie nach dem Lesen zum letzten Bild zum ersten Bild zurück
                self.image_capture.set(cv2.CAP_PROP_POS_FRAMES, 0)
                break

Verweise

Vielen Dank für all die Hilfe, die Sie mir gegeben haben.

Anzeigen von Bildern mit Opencv oder Pillow mit Python Kivy Umgang mit Texturen usw.

Erste Schritte mit Python 7: Wählen Sie eine Datei aus und spielen Sie das technische Memo eines Video-Akirachin ab Vielen Dank für das Video.

Recommended Posts

GUI-Programmierung mit kivy ~ Teil 3 Video und Suchleiste ~
GUI-Programmierung mit kivy ~ Teil 4 Verschiedene Tasten ~
GUI-Programmierung mit kivy ~ Teil 2 Fortschrittsbalken ~
GUI-Programmierung mit kivy ~ Teil 5 Erstellen von Schaltflächen mit Bildern ~
GUI-Programmierung mit kivy ~ Teil 6 Verschiedene Layouts ~
Programmieren mit Python und Tkinter
[GUI in Python] PyQt5-Drag & Drop-
FM-Modulation und Demodulation mit Python Part 3
Führen Sie die Google-Übersetzung und die DeepL-Übersetzung mit der GUI aus
Lerne Mathematik und Englisch durch Programmieren (Teil 1)
Lerne Mathematik und Englisch durch Programmieren (Teil 2)
GUI-Erstellung mit Pyside Teil 2 <Verwendung der Klasse>