[PYTHON] Erstellen Sie einen Videoplayer mit PySimpleGUI + OpenCV 2 Fügen Sie die ROI-Einstellung hinzu und speichern Sie die Funktion (DIVX, MJPG, GIF).

Einführung

Zusätzlich zum vorherigen Artikel fügen wir die Region of Interest (ROI) hinzu, die als Ziel der Operation ausgewählt werden soll. Zusätzlich fügen wir eine Funktion hinzu, um das Bild mit ausgeschnittenem Rahmen und ROI zu speichern. Es können verschiedene Codecs ausgewählt werden, aber DIVX mit hohem Komprimierungsverhältnis, MJPG, das von ImageJ analysiert werden kann, und GIF, das in Qiita eingefügt werden kann, können gespeichert werden.

Vorheriger Artikel: Erstellen Sie einen Videoplayer mit PySimpleGUI + OpenCV

Was können Sie mit diesem Artikel tun?

Sie können die Größe des Videos mit dem Schieberegler ändern. Sie können dem als Rechteck ausgewählten Teil mit der Maus Graustufen und Unschärfe hinzufügen. 2-1.jpg

Sie können das ausgewählte Teil als DIVX, MJPEG oder GIF speichern. test.gif

Video wird geladen

Verwenden Sie nach wie vor PySimpleGUI, um eine Datei zum Lesen der Benutzeroberfläche zu generieren.

class Main:
    def __init__(self):
        self.fp = file_read()
        self.cap = cv2.VideoCapture(str(self.fp))

        #Holen Sie sich das erste Bild
        #Überprüfen Sie, ob es erhältlich ist
        self.ret, self.f_frame = self.cap.read()

        if self.ret:

            self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
            #Erfassung von Videoinformationen
            self.fps = self.cap.get(cv2.CAP_PROP_FPS)
            self.width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            self.total_count = self.cap.get(cv2.CAP_PROP_FRAME_COUNT)

            #Rahmenbezogen
            self.frame_count = 0
            self.s_frame = 0
            self.e_frame = self.total_count

            #Pause-Flag spielen
            self.stop_flg = False

            cv2.namedWindow("Movie")

        else:
            sg.Popup("Fehler beim Lesen der Datei.")
            return

Video wird geladen

class Main:
    def __init__(self):
        self.fp = file_read()
        self.cap = cv2.VideoCapture(str(self.fp))

        #Holen Sie sich das erste Bild
        #Überprüfen Sie, ob es erhältlich ist
        self.ret, self.f_frame = self.cap.read()
        self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
        #Wenn Sie den Rahmen erhalten können, erhalten Sie verschiedene Parameter
        if self.ret:
            self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
            #Erfassung von Videoinformationen
            self.fps = self.cap.get(cv2.CAP_PROP_FPS)
            self.width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            self.total_count = self.cap.get(cv2.CAP_PROP_FRAME_COUNT)

Die anfängliche Größe des ROI sollte mit der Videogröße übereinstimmen.


            # ROI
            self.frames_roi = np.zeros((5, self.height, self.width))

            #Originalgröße speichern
            self.org_width = self.width
            self.org_height = self.height

            #Rahmenbezogen
            self.frame_count = 0
            self.s_frame = 0
            self.e_frame = self.total_count

            #Bildausschnittposition
            self.x1 = 0
            self.y1 = 0
            self.x2 = self.width
            self.y2 = self.height

            #Pause-Flag spielen
            self.stop_flg = False

            #Video-Speicherflagge
            self.rec_flg = False

            #Kontrolle der Mausbewegung
            #Ob die Maustaste gedrückt wird
            self.mouse_flg = False
            self.event = ""
            #Gibt an, ob die Berechnung auf den ROI angewendet werden soll
            self.roi_flg = True
            cv2.namedWindow("Movie")

Registrieren Sie die Rückruffunktion, damit Sie ein Rechteck auswählen können, indem Sie im Videofenster mit der linken Maustaste auf UNTEN → AUF klicken.

            #Rückrufregistrierung für Mausereignisse
            cv2.setMouseCallback("Movie", self.onMouse)
        #Beenden Sie, wenn der Frame nicht erhalten werden konnte
        else:
            sg.Popup("Fehler beim Lesen der Datei.")
            return

    #Mausereignis
    def onMouse(self, event, x, y, flags, param):
        #Links Klick
        if event == cv2.EVENT_LBUTTONDOWN:
            self.x1 = self.x2 = x
            self.y1 = self.y2 = y
            #Beginnen Sie mit dem Zeichnen eines Rechtecks. Drücken Sie einmal die Maus, um ein Rechteck zu zeichnen.
            self.mouse_flg = True
            #Unterbrechen Sie die Berechnung des ROI-Teils
            self.roi_flg = False
            return

        elif event == cv2.EVENT_LBUTTONUP:
            #Beenden Sie die Aktualisierung von Rechtecken
            self.mouse_flg = False
            #Starten Sie die Berechnung zum ROI
            self.roi_flg = True
            #Wenn die ROI-Auswahl 0 ist, setzen Sie sie zurück und beenden Sie die ROI-Berechnung.
            if (
                x == self.x1
                or y == self.y1
                or x <= 0
                or y <= 0
            ):
                self.x1 = 0
                self.y1 = 0
                self.x2 = self.width
                self.y2 = self.height
                return

            # x1 <Mach es x2
            elif self.x1 < x:
                self.x2 = x
            else:
                self.x2 = self.x1
                self.x1 = x

            if self.y1 < y:
                self.y2 = y
            else:
                self.y2 = self.y1
                self.y1 = y

            #ROI-Bereich anzeigen
            print(
                "ROI x:{0}:{1}  y:{2}:{3}".format(
                    str(self.x1),
                    str(self.x2),
                    str(self.y1),
                    str(self.y2)
                )
            )
            return

        #Zeigt das Rechteck weiterhin an, wenn die Maus gedrückt wird
        if self.mouse_flg:
            self.x2 = x
            self.y2 = y

            return

GUI-Generierung

Graustufen und Unschärfe werden als Verarbeitung für den ROI hinzugefügt. Wir haben auch einen Schieberegler hinzugefügt, um die Bildgröße zu ändern.

 def run(self):
        # GUI #######################################################
        #GUI-Layout
        T1 = sg.Tab("Basic", [
            [
                sg.Text("Resize     ", size=(13, 1)),
                sg.Slider(
                    (0.1, 4),
                    1,
                    0.01,
                    orientation='h',
                    size=(40, 15),
                    key='-RESIZE SLIDER-',
                    enable_events=True
                )
            ],
            [
                sg.Checkbox(
                    'blur',
                    size=(10, 1),
                    key='-BLUR-',
                    enable_events=True
                ),
                sg.Slider(
                    (1, 10),
                    1,
                    1,
                    orientation='h',
                    size=(40, 15),
                    key='-BLUR SLIDER-',
                    enable_events=True
                )
            ],


        ])

        T2 = sg.Tab("processing", [
            [
                sg.Checkbox(
                    'gray',
                    size=(10, 1),
                    key='-GRAY-',
                    enable_events=True
                )
            ],
        ])
        T3 = sg.Tab("mask", [
            [
                sg.Radio(
                    'Rectangle',
                    "RADIO2",
                    key='-RECTANGLE_MASK-',
                    default=True,
                    size=(8, 1)
                ),
                sg.Radio(
                    'Masking',
                    "RADIO2",
                    key='-MASKING-',
                    size=(8, 1)
                )
            ],
        ])
        T4 = sg.Tab("Save", [
            [
                sg.Button('Write', size=(10, 1)),
                sg.Radio(
                    'DIVX',
                    "RADIO1",
                    key='-DIVX-',
                    default=True,
                    size=(8, 1)
                ),
                sg.Radio('MJPG', "RADIO1", key='-MJPG-', size=(8, 1)),
                sg.Radio('GIF', "RADIO1", key='-GIF-', size=(8, 1))
            ],
            [
                sg.Text('Caption', size=(10, 1)),
                sg.InputText(
                    size=(32, 50),
                    key='-CAPTION-',
                    enable_events=True
                )
            ]
        ])

        layout = [
            [
                sg.Text("Start", size=(8, 1)),
                sg.Slider(
                    (0, self.total_count - 1),
                    0,
                    1,
                    orientation='h',
                    size=(45, 15),
                    key='-START FRAME SLIDER-',
                    enable_events=True
                )
            ],
            [
                sg.Text("End ", size=(8, 1)),
                sg.Slider(
                    (0, self.total_count - 1), self.total_count - 1,
                    1,
                    orientation='h',
                    size=(45, 15),
                    key='-END FRAME SLIDER-',
                    enable_events=True
                )
            ],
            [sg.Slider(
                (0, self.total_count - 1),
                0,
                1,
                orientation='h',
                size=(50, 15),
                key='-PROGRESS SLIDER-',
                enable_events=True
            )],
            [
                sg.Button('<<<', size=(5, 1)),
                sg.Button('<<', size=(5, 1)),
                sg.Button('<', size=(5, 1)),
                sg.Button('Play / Stop', size=(9, 1)),
                sg.Button('Reset', size=(7, 1)),
                sg.Button('>', size=(5, 1)),
                sg.Button('>>', size=(5, 1)),
                sg.Button('>>>', size=(5, 1))
            ],
            [
                sg.Text("Speed", size=(6, 1)),
                sg.Slider(
                    (0, 240),
                    10,
                    10,
                    orientation='h',
                    size=(19.4, 15),
                    key='-SPEED SLIDER-',
                    enable_events=True
                ),
                sg.Text("Skip", size=(6, 1)),
                sg.Slider(
                    (0, 300),
                    0,
                    1,
                    orientation='h',
                    size=(19.4, 15),
                    key='-SKIP SLIDER-',
                    enable_events=True
                )
            ],
            [sg.HorizontalSeparator()],
            [
                sg.TabGroup(
                    [[T1, T2, T3, T4]],
                    tab_background_color="#ccc",
                    selected_title_color="#fff",
                    selected_background_color="#444",
                    tab_location="topleft"
                )
            ],
            [sg.Output(size=(65, 5), key='-OUTPUT-')],
            [sg.Button('Clear')]
        ]

        #Fenster generieren
        window = sg.Window('OpenCV Integration', layout, location=(0, 0))
        #Anzeige von Videoinformationen
        self.event, values = window.read(timeout=0)
        print("Die Datei wurde gelesen.")
        print("File Path: " + str(self.fp))
        print("fps: " + str(int(self.fps)))
        print("width: " + str(self.width))
        print("height: " + str(self.height))
        print("frame count: " + str(int(self.total_count)))



    #Hauptschleife#########################################################
        try:
            while True:
                #Laden von GUI-Ereignissen
                self.event, values = window.read(
                    timeout=values["-SPEED SLIDER-"]
                )

                #Ereignis im Fenster anzeigen
                if self.event != "__TIMEOUT__":
                    print(self.event)
                #Beenden, wenn die Schaltfläche Beenden gedrückt wird oder wenn die Schaltfläche zum Schließen des Fensters gedrückt wird
                if self.event in ('Exit', sg.WIN_CLOSED, None):
                    break

                #Video neu laden
                #Funktioniert, wenn der Startrahmen eingestellt ist
                if self.event == 'Reset':
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.s_frame)
                    self.frame_count = self.s_frame
                    window['-PROGRESS SLIDER-'].update(self.frame_count)

                    self.video_stabilization_flg = False
                    self.stab_prepare_flg = False

                    #Reflektieren Sie weiterhin Änderungen am Fortschrittsregler
                    continue

Video speichern

Wenn Sie es als Videodatei speichern möchten, verwenden Sie cv2.VideoWriter_fourcc. Hier ist es so eingestellt, dass es im MJPEG-Format gespeichert werden kann, das von DIVX mit hoher Komprimierungsrate und ImageJ, einer kostenlosen Videoanalyse-Software, gelesen werden kann. Beim Speichern als GIF-Datei wird Pillow verwendet.


                #Video exportieren
                if self.event == 'Write':
                    self.rec_flg = True
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.s_frame)
                    self.frame_count = self.s_frame
                    window['-PROGRESS SLIDER-'].update(self.frame_count)

                    if values["-GIF-"]:
                        images = []
                    else:
                        #Als Video speichern
                        #Codeauswahl
                        #DIVX hat eine hohe Komprimierungsrate
                        #MJEG kann mit ImageJ analysiert werden
                        if values["-DIVX-"]:
                            codec = "DIVX"
                        elif values["-MJPG-"]:
                            codec = "MJPG"
                        fourcc = cv2.VideoWriter_fourcc(*codec)
                        out = cv2.VideoWriter(
                            str((
                                self.fp.parent / (self.fp.stem + '_' + codec + '.avi')
                            )),
                            fourcc,
                            self.fps,
                            (int(self.x2 - self.x1), int(self.y2 - self.y1))
                        )
                    continue

                #Rahmenbetrieb################################################
                #Priorität wird gegeben, wenn der Schieberegler direkt geändert wird
                if self.event == '-PROGRESS SLIDER-':
                    #Stellen Sie die Frame-Anzahl auf den Fortschrittsbalken ein
                    self.frame_count = int(values['-PROGRESS SLIDER-'])
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_count)
                    if values['-PROGRESS SLIDER-'] > values['-END FRAME SLIDER-']:
                        window['-END FRAME SLIDER-'].update(
                            values['-PROGRESS SLIDER-'])

                #Wenn Sie den Startrahmen ändern
                if self.event == '-START FRAME SLIDER-':
                    self.s_frame = int(values['-START FRAME SLIDER-'])
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.s_frame)
                    self.frame_count = self.s_frame
                    window['-PROGRESS SLIDER-'].update(self.frame_count)
                    if values['-START FRAME SLIDER-'] > values['-END FRAME SLIDER-']:
                        window['-END FRAME SLIDER-'].update(
                            values['-START FRAME SLIDER-'])
                        self.e_frame = self.s_frame

                #Wenn Sie den Endrahmen ändern
                if self.event == '-END FRAME SLIDER-':
                    if values['-END FRAME SLIDER-'] < values['-START FRAME SLIDER-']:
                        window['-START FRAME SLIDER-'].update(
                            values['-END FRAME SLIDER-'])
                        self.s_frame = self.e_frame

                    #Rahmeneinstellungen beenden
                    self.e_frame = int(values['-END FRAME SLIDER-'])

                if self.event == '<<<':
                    self.frame_count = np.maximum(0, self.frame_count - 150)
                    window['-PROGRESS SLIDER-'].update(self.frame_count)
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_count)

                if self.event == '<<':
                    self.frame_count = np.maximum(0, self.frame_count - 30)
                    window['-PROGRESS SLIDER-'].update(self.frame_count)
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_count)

                if self.event == '<':
                    self.frame_count = np.maximum(0, self.frame_count - 1)
                    window['-PROGRESS SLIDER-'].update(self.frame_count)
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_count)

                if self.event == '>':
                    self.frame_count = self.frame_count + 1
                    window['-PROGRESS SLIDER-'].update(self.frame_count)
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_count)

                if self.event == '>>':
                    self.frame_count = self.frame_count + 30
                    window['-PROGRESS SLIDER-'].update(self.frame_count)
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_count)

                if self.event == '>>>':
                    self.frame_count = self.frame_count + 150
                    window['-PROGRESS SLIDER-'].update(self.frame_count)
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_count)

                #Wenn der Zähler den Endrahmen überschreitet, starten Sie ihn vom Startrahmen aus neu
                if self.frame_count >= self.e_frame:
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.s_frame)
                    self.frame_count = self.s_frame
                    window['-PROGRESS SLIDER-'].update(self.frame_count)
                    continue

                #Unterbrechen Sie das Laden des Videos mit der Stopp-Taste
                if self.event == 'Play / Stop':
                    self.stop_flg = not self.stop_flg

                #Wenn das Stopp-Flag nicht gesetzt ist und ein Ereignis eintritt, zählen Sie mit
                #Beenden Sie den Vorgang

                #Wenn die Stopp-Taste gedrückt wird, wird die Videoverarbeitung gestoppt, aber etwas
                #Wenn ein Ereignis auftritt, aktualisieren Sie nur das Image
                #Gleiches gilt für die Bedienung der Maus
                if(
                    (
                        self.stop_flg
                        and self.event == "__TIMEOUT__"
                        and self.mouse_flg is False
                    )
                ):
                    window['-PROGRESS SLIDER-'].update(self.frame_count)
                    continue

                #Frames überspringen
                if not self.stop_flg and values['-SKIP SLIDER-'] != 0:
                    self.frame_count += values["-SKIP SLIDER-"]
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_count)

                #Rahmen laden##############################################
                self.ret, self.frame = self.cap.read()
                self.valid_frame = int(self.frame_count - self.s_frame)
                #Wenn der letzte Frame über sich selbst ist.s_Vom Rahmen fortsetzen
                if not self.ret:
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.s_frame)
                    self.frame_count = self.s_frame
                    continue

Implementierung der Bildverarbeitung

Danach beschreiben wir die Verarbeitung wie Größenänderung, Grauskalierung und Unschärfe. Nach der Größenänderung, die für den gesamten Frame ausgeführt wird, werden Graustufen und Unschärfe nur für den ROI ausgeführt.

                #Beschreiben Sie danach die Verarbeitung für den Frame##################################

                #Führen Sie zunächst die Verarbeitung für den gesamten Frame durch##############################
                #Größe ändern
                self.width = int(self.org_width * values['-RESIZE SLIDER-'])
                self.height = int(self.org_height * values['-RESIZE SLIDER-'])
                self.frame = cv2.resize(self.frame, (self.width, self.height))
                if self.event == '-RESIZE SLIDER-':
                    self.x1 = self.y1 = 0
                    self.x2 = self.width
                    self.y2 = self.height
                #Führen Sie die Verarbeitung für den ROI durch##########################################
                if self.roi_flg:
                    self.frame_roi = self.frame[
                        self.y1:self.y2, self.x1:self.x2, :
                    ]
                    #Verwischen
                    if values['-BLUR-']:
                        self.frame_roi = cv2.GaussianBlur(
                            self.frame_roi, (21, 21), values['-BLUR SLIDER-']
                        )
                    if values['-GRAY-']:
                        self.frame_roi = cv2.cvtColor(
                            self.frame_roi,
                            cv2.COLOR_BGR2GRAY
                        )
                        self.frame_roi = cv2.cvtColor(
                            self.frame_roi,
                            cv2.COLOR_GRAY2BGR
                        )

Der verarbeitete ROI wird an den Frame zurückgegeben und angezeigt.

                    #Geben Sie den verarbeiteten ROI an den Frame zurück
                    self.frame[self.y1:self.y2, self.x1:self.x2, :] = self.frame_roi
                #Video speichern
                if self.rec_flg:
                    #Schneiden Sie das Roi nach der Verwacklungskorrektur wieder aus
                    self.frame_roi = self.frame[
                        self.y1:self.y2, self.x1:self.x2, :
                    ]
                    if values["-GIF-"]:
                        images.append(
                            Image.fromarray(
                                cv2.cvtColor(
                                    self.frame_roi, cv2.COLOR_BGR2RGB
                                )
                            )
                        )
                    else:
                        out.write(self.frame_roi)

                    #Anzeige beim Speichern
                    cv2.putText(
                        self.frame,
                        str("Now Recording"),
                        (20, 60),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        0.5,
                        (10, 10, 255),
                        1,
                        cv2.LINE_AA
                    )

                    # e_Beenden Sie, wenn es ein Rahmen wird
                    if self.frame_count >= self.e_frame - values["-SKIP SLIDER-"] - 1:
                        if values["-GIF-"]:
                            images[0].save(
                                str((self.fp.parent / (self.fp.stem + '.gif'))),
                                save_all=True,
                                append_images=images[1:],
                                optimize=False,
                                duration=1000 // self.fps,
                                loop=0
                            )
                        else:
                            out.release()
                        self.rec_flg = False

                #Anzeige der Anzahl der Frames und der verstrichenen Sekunden
                cv2.putText(
                    self.frame, str("framecount: {0:.0f}".format(self.frame_count)), (
                        15, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (240, 230, 0), 1, cv2.LINE_AA
                )
                cv2.putText(
                    self.frame, str("time: {0:.1f} sec".format(
                        self.frame_count / self.fps)), (15, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (240, 230, 0), 1, cv2.LINE_AA
                )

                #Bei der Berechnung des ROI oder beim Drücken der linken Maustaste
                #Zeichne ein Rechteck
                if self.roi_flg or self.mouse_flg:
                    cv2.rectangle(
                        self.frame,
                        (self.x1, self.y1),
                        (self.x2 - 1, self.y2 - 1),
                        (128, 128, 128)
                    )

                #Bild anzeigen
                cv2.imshow("Movie", self.frame)

                if self.stop_flg:
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_count)

                else:
                    self.frame_count += 1
                    window['-PROGRESS SLIDER-'].update(self.frame_count + 1)

                #Andere Verarbeitung###############################################
                #Protokollfenster löschen
                if self.event == 'Clear':
                    window['-OUTPUT-'].update('')

        finally:
            cv2.destroyWindow("Movie")
            self.cap.release()
            window.close()


if __name__ == '__main__':
    Main().run()

Referenzlink

Wikipedia HSV Color Space [Python] Erstellen Sie ein GIF aus einem Video

Recommended Posts

Erstellen Sie einen Videoplayer mit PySimpleGUI + OpenCV 2 Fügen Sie die ROI-Einstellung hinzu und speichern Sie die Funktion (DIVX, MJPG, GIF).
Erstellen Sie einen Videoplayer mit PySimpleGUI + OpenCV 3 Hinzufügen einer Maskenfunktion
Erstellen Sie einen Videoplayer mit PySimpleGUI + OpenCV
So schleifen und spielen Sie ein GIF-Video mit openCV ab
Erstellen Sie eine WEB-Überwachungskamera mit Raspberry Pi und OpenCV
Erstellen Sie mit python wxpython + openCV ein einfaches Videoanalysetool
Lassen Sie uns mit Pylearn2 eine Drei-Wege-KI erstellen - Modell speichern und laden -
Erstellen Sie eine gestreifte Illusion mit Gammakorrektur für Python3 und openCV3
Erstellen Sie mit Class einen Python-Funktionsdekorator