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.
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.
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
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)
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
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-']
)
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)
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()
Interpolation d'image automatique avec OpenCV et Python (méthode de marche rapide, Navier-Stokes) Image Inpainting
Recommended Posts