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
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.
Sie können das ausgewählte Teil als DIVX, MJPEG oder GIF speichern.
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
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
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
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
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()
Wikipedia HSV Color Space [Python] Erstellen Sie ein GIF aus einem Video
Recommended Posts