[PYTHON] Effacez des couleurs spécifiques avec OpenCV + PySimpleGUI

introduction

Afin d'utiliser efficacement OpenCV, il est nécessaire de produire le résultat souhaité non seulement par un processus, mais également par plusieurs processus et combinaison avec d'autres packages. Cette fois, à titre d'exemple, essayons d'effacer la couleur de la partie cliquée.

Que pouvez-vous faire avec cet article

Si vous cliquez avec le bouton gauche sur une partie de couleur spécifique de la vidéo, cette couleur disparaît. Pour ajuster la taille de la partie effacée et le degré de flou, il est nécessaire de modifier des paramètres tels que la valeur HSV. Les paramètres peuvent être modifiés dans l'interface graphique.

test_normal.gif

test.gif

Flux de processus

Le processus est le suivant. Chargement de la vidéo

Fabrication de masques → Convertissez l'image en HSV et réglez-la pour ne masquer qu'une couleur spécifique. Pour extraire la couleur, faites un clic gauche sur la vidéo pour extraire la couleur de cette partie. De plus, il est possible de faire des ajustements fins avec le curseur créé avec PySimple GUI.

Suppression du bruit pour masquer l'image (ouverture, fermeture) → Comme le bruit ne subsiste que par extraction de couleur, le bruit est supprimé par le traitement d'ouverture et de fermeture.

Processus de gonflement (dilatation) pour masquer l'image → Comme le contour de la couleur ne peut pas être bien extrait dans de nombreux cas, la partie du masque est uniformément étendue.

Traitement de réparation (Inpaint) sur la partie masque → La partie masque est réparée avec la couleur environnante.

Flou sur le masque → Puisqu'il ne se démarque qu'avec Inpaint, il est flou.

afficher

programme

Chargement de la vidéo

import PySimpleGUI as sg
import cv2
import numpy as np
from pathlib import Path


def file_read():
    '''
Sélectionnez et chargez un fichier
    '''
    fp = ""
    #Disposition GUI
    layout = [
        [
            sg.FileBrowse(key="file"),
            sg.Text("Fichier"),
            sg.InputText()
        ],
        [sg.Submit(key="submit"), sg.Cancel("Exit")]
    ]
    # self.Génération WINDOW
    window = sg.Window("Sélection de fichiers", layout)

    #Boucle d'événement
    while True:
        event, values = window.read(timeout=100)
        if event == 'Exit' or event == sg.WIN_CLOSED:
            break
        elif event == 'submit':
            if values[0] == "":
                sg.popup("Aucun fichier n'a été saisi.")
                event = ""
            else:
                fp = values[0]
                break
    window.close()
    return Path(fp)

Détection de couleur par HSV

Cette fonction crée un masque pour traiter uniquement la valeur HSV dans la plage spécifiée. Spécifié sur l'interface graphique PySimple Lisez la valeur du curseur et générez une image de masque.


def hsv(frame, H_max, H_min, S_max, S_min, V_max, V_min, reverse=False):
    frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    if reverse:
        lower1 = np.array([0, int(S_min), int(V_min)])
        upper1 = np.array([int(H_min), int(S_max), int(V_max)])
        mask1 = cv2.inRange(frame_hsv, lower1, upper1)
        lower2 = np.array([int(H_max), int(S_min), int(V_min)])
        upper2 = np.array([255, int(S_max), int(V_max)])
        mask2 = cv2.inRange(frame_hsv, lower2, upper2)
        mask = mask1 + mask2
        frame = cv2.bitwise_and(frame, frame, mask=mask)
        # mask = cv2.bitwise_and(frame, mask, mask=mask)

    else:
        lower = np.array([int(H_min), int(S_min), int(V_min)])
        upper = np.array([int(H_max), int(S_max), int(V_max)])
        mask = cv2.inRange(frame_hsv, lower, upper)
        frame = cv2.bitwise_and(frame, frame, mask=mask)

    return frame

Chargement d'image et interface graphique

Chargez la vidéo à partir du fichier et affichez-la dans OpenCV. Lorsque vous cliquez avec le bouton gauche sur l'image, créez une fonction qui reflète la valeur HSV de cette partie dans la valeur du curseur de PySimpleGUI et spécifiez-la comme fonction de rappel.

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

        #Drapeau d'enregistrement vidéo
        self.rec_flg = False

        #Obtenez la première image
        #Vérifiez s'il peut être obtenu
        self.ret, self.f_frame = self.cap.read()
        self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
        #Si vous pouvez obtenir le cadre, obtenez divers paramètres
        if self.ret:
            self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
            #Acquisition d'informations vidéo
            self.fps = self.cap.get(cv2.CAP_PROP_FPS)
            self.org_width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            self.width = self.org_width
            self.org_height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            self.height = self.org_height
            self.total_count = self.cap.get(cv2.CAP_PROP_FRAME_COUNT)

            #Définition de l'image de masque
            self.mask = np.zeros_like(self.f_frame[:, :, 0])

            #Lié au cadre
            self.frame_count = 0
            self.s_frame = 0
            self.e_frame = self.total_count

            #Lire l'indicateur de pause
            self.stop_flg = False

            #Événement GUI
            self.event = ""

            cv2.namedWindow("Movie")

            #Enregistrement de rappel pour les événements de souris
            cv2.setMouseCallback("Movie", self.onMouse)
        #Quitter si le cadre n'a pas pu être obtenu
        else:
            sg.Popup("Impossible de lire le fichier.")
            return

    #Événement souris
    def onMouse(self, event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONUP:
            hsv = cv2.cvtColor(
                self.frame[y:y + 1, x:x + 1, :], cv2.COLOR_BGR2HSV)
            h = int(hsv[:, :, 0])
            h_min = max(h - 20, 0)
            h_max = min(255, h + 20)
            s = int(hsv[:, :, 1])
            s_min = max(s - 20, 0)
            s_max = min(255, s + 20)
            v = int(hsv[:, :, 2])
            v_min = max(v - 20, 0)
            v_max = min(255, v + 20)
            self.window['-H_MIN SLIDER_MASK-'].update(h_min)
            self.window['-H_MAX SLIDER_MASK-'].update(h_max)
            self.window['-S_MIN SLIDER_MASK-'].update(s_min)
            self.window['-S_MAX SLIDER_MASK-'].update(s_max)
            self.window['-V_MIN SLIDER_MASK-'].update(v_min)
            self.window['-V_MAX SLIDER_MASK-'].update(v_max)
            self.window['-Hue Reverse_MASK-'].update(False)

    def run(self):
        # GUI #######################################################
        #Disposition GUI

        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.Slider(
                (0, self.total_count - 1),
                0,
                1,
                orientation='h',
                size=(50, 15),
                key='-PROGRESS SLIDER-',
                enable_events=True
            )],
            [sg.HorizontalSeparator()],
            [
                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(
                    "Inpaint",
                    size=(10, 1),
                    default=False,
                    key='-INPAINT-',
                    enable_events=True
                )
            ],
            [
                sg.Checkbox(
                    "Opening",
                    size=(20, 1),
                    default=False,
                    key='-OPENING-',
                    enable_events=True
                ),
                sg.Checkbox(
                    "Closing",
                    size=(20, 1),
                    default=False,
                    key='-CLOSING-',
                    enable_events=True
                ),
                sg.Slider(
                    (3, 31),
                    5,
                    2,
                    orientation='h',
                    size=(15, 15),
                    key='-OPENING SLIDER-',
                    enable_events=True
                )
            ],
            [
                sg.Checkbox(
                    "Dilation",
                    size=(10, 1),
                    default=False,
                    key='-DILATION-',
                    enable_events=True
                ),
                sg.Slider(
                    (1, 31),
                    4,
                    2,
                    orientation='h',
                    size=(15, 15),
                    key='-DILATION 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
                )
            ],
            [
                sg.Text(
                    'hsv',
                    size=(10, 1),
                    key='-HSV_MASK-',
                    enable_events=True
                ),
                sg.Button('Blue', size=(10, 1)),
                sg.Button('Green', size=(10, 1)),
                sg.Button('Red', size=(10, 1))
            ],
            [
                sg.Checkbox(
                    'Hue Reverse',
                    size=(10, 1),
                    key='-Hue Reverse_MASK-',
                    enable_events=True
                )
            ],
            [
                sg.Text('Hue', size=(10, 1), key='-Hue_MASK-'),
                sg.Slider(
                    (0, 255),
                    0,
                    1,
                    orientation='h',
                    size=(19.4, 15),
                    key='-H_MIN SLIDER_MASK-',
                    enable_events=True
                ),
                sg.Slider(
                    (1, 255),
                    125,
                    1,
                    orientation='h',
                    size=(19.4, 15),
                    key='-H_MAX SLIDER_MASK-',
                    enable_events=True
                )
            ],
            [
                sg.Text('Saturation', size=(10, 1), key='-Saturation_MASK-'),
                sg.Slider(
                    (0, 255),
                    50,
                    1,
                    orientation='h',
                    size=(19.4, 15),
                    key='-S_MIN SLIDER_MASK-',
                    enable_events=True
                ),
                sg.Slider(
                    (1, 255),
                    255,
                    1,
                    orientation='h',
                    size=(19.4, 15),
                    key='-S_MAX SLIDER_MASK-',
                    enable_events=True
                )
            ],
            [
                sg.Text('Value', size=(10, 1), key='-Value_MASK-'),
                sg.Slider(
                    (0, 255),
                    50,
                    1,
                    orientation='h',
                    size=(19.4, 15),
                    key='-V_MIN SLIDER_MASK-',
                    enable_events=True
                ),
                sg.Slider(
                    (1, 255),
                    255,
                    1,
                    orientation='h',
                    size=(19.4, 15),
                    key='-V_MAX SLIDER_MASK-',
                    enable_events=True
                )
            ],
            [sg.Output(size=(65, 5), key='-OUTPUT-')],
            [sg.Button('Clear')]
        ]

        # self.Générer la fenêtre
        self.window = sg.Window('OpenCV Integration', layout, location=(0, 0))
        #Affichage des informations vidéo
        self.event, values = self.window.read(timeout=0)
        print("Le fichier a été lu.")
        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)))

    #Boucle principale#########################################################
        try:
            while True:
                #Chargement des événements GUI
                self.event, values = self.window.read(
                    timeout=0
                )

                #Afficher l'événement dans la fenêtre
                if self.event != "__TIMEOUT__":
                    print(self.event)
                #Quitter lorsque le bouton Quitter est enfoncé ou lorsque le bouton de fermeture de fenêtre est enfoncé
                if self.event in ('Exit', sg.WIN_CLOSED, None):
                    break

                #Recharger la vidéo
                #Fonctionne lorsque l'image de départ est définie
                if self.event == 'Reset':
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.s_frame)
                    self.frame_count = self.s_frame
                    self.window['-PROGRESS SLIDER-'].update(self.frame_count)

                    self.video_stabilization_flg = False
                    self.stab_prepare_flg = False

                    #Continuer à refléter les modifications apportées au curseur de progression
                    continue

                #Fonctionnement du cadre################################################
                #La priorité est donnée si le curseur est modifié directement
                if self.event == '-PROGRESS SLIDER-':
                    #Définissez le nombre d'images sur la barre de progression
                    self.frame_count = int(values['-PROGRESS SLIDER-'])
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_count)

                #Si vous modifiez l'image de départ
                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
                    self.window['-PROGRESS SLIDER-'].update(self.frame_count)

                #Si le compteur dépasse la trame de fin, redémarrez à partir de la trame de début
                if self.frame_count >= self.e_frame:
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.s_frame)
                    self.frame_count = self.s_frame
                    self.window['-PROGRESS SLIDER-'].update(self.frame_count)
                    continue

                #Suspendre le chargement de la vidéo avec le bouton d'arrêt
                if self.event == 'Play / Stop':
                    self.stop_flg = not self.stop_flg

                #Sauf si l'indicateur d'arrêt est défini et qu'un événement se produit, comptez
                #Arrêtez l'opération

                #Si le bouton d'arrêt est enfoncé, le traitement vidéo sera arrêté, mais quelque chose
                #Si un événement se produit, ne mettez à jour que l'image
                #La même chose s'applique lors de l'utilisation de la souris
                if(
                    (
                        self.stop_flg
                        and self.event == "__TIMEOUT__"
                    )
                ):
                    self.window['-PROGRESS SLIDER-'].update(self.frame_count)
                    continue

                #Cadre de charge##############################################
                self.ret, self.frame = self.cap.read()
                #Quand la dernière image est sur soi.s_Reprendre du cadre
                if not self.ret:
                    self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.s_frame)
                    self.frame_count = self.s_frame
                    continue

                #redimensionner
                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))

                #Effectuer le traitement pour le retour sur investissement##########################################
                if self.event == 'Blue':
                    self.window['-H_MIN SLIDER_MASK-'].update(70)
                    self.window['-H_MAX SLIDER_MASK-'].update(110)
                    self.window['-S_MIN SLIDER_MASK-'].update(70)
                    self.window['-S_MAX SLIDER_MASK-'].update(255)
                    self.window['-V_MIN SLIDER_MASK-'].update(0)
                    self.window['-V_MAX SLIDER_MASK-'].update(255)
                    self.window['-Hue Reverse_MASK-'].update(False)

                if self.event == 'Green':
                    self.window['-H_MIN SLIDER_MASK-'].update(20)
                    self.window['-H_MAX SLIDER_MASK-'].update(70)
                    self.window['-S_MIN SLIDER_MASK-'].update(70)
                    self.window['-S_MAX SLIDER_MASK-'].update(255)
                    self.window['-V_MIN SLIDER_MASK-'].update(0)
                    self.window['-V_MAX SLIDER_MASK-'].update(255)
                    self.window['-Hue Reverse_MASK-'].update(False)

                if self.event == 'Red':
                    self.window['-H_MIN SLIDER_MASK-'].update(20)
                    self.window['-H_MAX SLIDER_MASK-'].update(110)
                    self.window['-S_MIN SLIDER_MASK-'].update(70)
                    self.window['-S_MAX SLIDER_MASK-'].update(255)
                    self.window['-V_MIN SLIDER_MASK-'].update(0)
                    self.window['-V_MAX SLIDER_MASK-'].update(255)
                    self.window['-Hue Reverse_MASK-'].update(True)

                self.mask = self.frame
                self.mask = hsv(
                    self.mask,
                    values['-H_MAX SLIDER_MASK-'],
                    values['-H_MIN SLIDER_MASK-'],
                    values['-S_MAX SLIDER_MASK-'],
                    values['-S_MIN SLIDER_MASK-'],
                    values['-V_MAX SLIDER_MASK-'],
                    values['-V_MIN SLIDER_MASK-'],
                    values['-Hue Reverse_MASK-']
                )

Traitement pour masquer l'image

Effectue la conversion des niveaux de gris, le traitement d'ouverture, le traitement de fermeture et le traitement de dilatation de l'image de masque.



                #Échelle de gris
                self.mask = cv2.cvtColor(
                    self.mask,
                    cv2.COLOR_BGR2GRAY
                )

                #Suppression du bruit
                if values['-OPENING-']:
                    self.mask = cv2.morphologyEx(self.mask, cv2.MORPH_OPEN,
                        np.ones((int(values['-OPENING SLIDER-']), int(values['-OPENING SLIDER-'])), np.uint8))

                #Suppression du bruit 2
                if values['-CLOSING-']:
                    self.mask = cv2.morphologyEx(self.mask, cv2.MORPH_CLOSE,
                        np.ones((int(values['-OPENING SLIDER-']), int(values['-OPENING SLIDER-'])), np.uint8))

                #Processus d'expansion
                if values['-DILATION-']:
                    self.mask = cv2.dilate(self.mask,
                        np.ones((int(values['-DILATION SLIDER-']), int(values['-DILATION SLIDER-'])), np.uint8), iterations=1)

Traitement de réparation sur le cadre, traitement de flou

Le traitement Inpaint et le traitement Flou sont effectués sur la partie masque. La partie à peindre peut être implémentée en spécifiant une image de masque comme argument. Pour Blur, j'utilise cv2.bitwise_not pour appliquer Blur uniquement à la partie masque.


                if values['-INPAINT-']:
                    self.frame = cv2.inpaint(
                        self.frame,
                        self.mask,
                        2,
                        cv2.INPAINT_TELEA
                    )

                #Brouiller
                if values['-BLUR-']:
                    self.frame_roi = cv2.GaussianBlur(
                        self.frame, (21, 21), values['-BLUR SLIDER-']
                    )

                    #Appliquer le masque dans le cadre
                    #Partie de traitement de masque uniquement.Changer de cadre
                    self.frame = cv2.bitwise_not(
                        cv2.bitwise_not(self.frame_roi),
                        self.frame,
                        mask=self.mask
                    )

                #Affichage du nombre d'images et des secondes écoulées
                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)

                #Afficher l'image
                cv2.imshow("Movie", self.frame)
                cv2.imshow("Mask", cv2.cvtColor(self.mask, cv2.COLOR_GRAY2BGR))

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

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

                #Autre traitement###############################################
                #Effacer la fenêtre du journal
                if self.event == 'Clear':
                    self.window['-OUTPUT-'].update('')

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


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



Lien de référence

Interpolation d'image automatique avec OpenCV et Python (méthode de marche rapide, Navier-Stokes) Image Inpainting

Recommended Posts

Effacez des couleurs spécifiques avec OpenCV + PySimpleGUI
Créer un lecteur vidéo avec PySimpleGUI + OpenCV
Détecter le retour du chat avec OpenCV
Faire pivoter les sprites avec OpenCV
Augmentation des données avec openCV
TopView facile avec OpenCV
Trébucher avec opencv3 de homebrew
Reconnaissance faciale avec OpenCV de Python
"Traitement Apple" avec OpenCV3 + Python3
Essayez la détection des bords avec OpenCV
Édition d'image avec python OpenCV
Capture de caméra avec Python + OpenCV
[Python] Utilisation d'OpenCV avec Python (basique)
Binariser les données photo avec OpenCV
Chargement de la vidéo en boucle avec opencv
Détection des bords en temps réel avec OpenCV
Détection de visage avec Python + OpenCV
Obtenez des fonctionnalités d'image avec OpenCV
Reconnaissance faciale / coupe avec OpenCV
Essayez OpenCV avec Google Colaboratory
Création d'un classificateur en cascade avec opencv
Utiliser OpenCV avec Python @Mac
Reconnaissance d'image avec Keras + OpenCV
Détection de visage d'anime avec OpenCV
Créer un lecteur vidéo avec PySimpleGUI + OpenCV 3 Ajout de la fonction de masque