[PYTHON] Trennung von Hintergrund und sich bewegenden Objekten mithilfe der dynamischen Moduszerlegung

Einführung

Verwenden Sie DMD (Dynamic Mode Decomposition), um den Hintergrund des Videos zu trennen und ein Video nur mit dem dynamischen Modus des sich bewegenden Objekts zu erstellen. Die dynamische Modenzerlegung ist wie eine Kombination aus Hauptkomponentenanalyse und Fourier-Transformation. Eine ausführliche Erläuterung der Zerlegung im dynamischen Modus finden Sie unter dem Link in dem Artikel, den ich zuvor geschrieben habe. -https://qiita.com/matsxxx/items/5e4b272de821fb1c11e0

Umgebung

Was ist eine dynamische Moduszerlegung?

Ich werde kurz das Verfahren der Zerlegung im dynamischen Modus vorstellen. Die dynamische Modenzerlegung erzeugt einen dynamischen Modus, indem die Eigenwerte und Eigenvektoren einer Übergangsmatrix aus Zeitreihendaten ermittelt werden. Es kann in Dimensionen und Zeitrichtung in Merkmale zerlegt werden. Dimensionsmerkmale erscheinen in Eigenvektoren und zeitliche Merkmale erscheinen in einer komplexen Anzahl von Eigenwerten.

Bei der dynamischen Modenzerlegung werden die Eigenwerte und Eigenvektoren der Übergangsmatrix implementiert, so dass sie mit einem realistischen Rechenaufwand erhalten werden können. In diesem Artikel ist es in Exact DMD implementiert. dmd_describe.png

import scipy.linalg as la
#DMD-Definition
def dmd(X, Y, truncate=None):#X=X_{1:n-1} Y=X_{2:n}
    u2,sig2,vh2 = la.svd(X, False) 
    r = len(sig2) if truncate is None else truncate
    u = u2[:,:r]
    sig = np.diag(sig2)[:r,:r]
    v = vh2.conj().T[:,:r]
    Atil = np.dot(np.dot(np.dot(u.conj().T, Y), v), la.inv(sig))
    mu,w = la.eig(Atil)
    phi = np.dot(np.dot(np.dot(Y, v), la.inv(sig)), w)#DMD mode
    return mu, phi

Video

Ich habe Atrium in [Video hier] verwendet (https://www.jpjodoin.com/urbantracker/dataset.html). Dies ist ein Video einer Person, die geht. Ich benutze 120frame bis 170frame. Trennen Sie den Hintergrund von der gehenden Person. original.png

import cv2
#Bildpfad
video_path = "./atrium_video.avi"
#Bild wird geladen
cap = cv2.VideoCapture(video_path)

#Holen Sie sich Bildauflösung, Bildrate, Anzahl der Bilder
wid = cap.get(cv2.CAP_PROP_FRAME_WIDTH)#Seite
hei = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)#Vertikal
fps = cap.get(cv2.CAP_PROP_FPS)#Bildrate
count = cap.get(cv2.CAP_PROP_FRAME_COUNT)#Anzahl der Frames
dt = 1/fps#Anzahl der Sekunden zwischen den Bildern
print(f"wid:{wid}", f" hei:{hei}", f" fps:{fps}", f" count:{count}", f"dt:{dt}")

#Rahmen zu verwenden
start_frame =120
end_frame = 170

#Bildauflösung 1/Auf 4 setzen (um den Rechenaufwand zu reduzieren)
r = 4

#Rahmenextraktion
cat_frames = []
cap.set(cv2.CAP_PROP_POS_FRAMES,start_frame)
for i in range(end_frame - start_frame):
    ret, img = cap.read()
    if not ret:
        print("no image")
        break
    buf = cv2.cvtColor(cv2.resize(img,(int(wid/r), int(hei/r))), cv2.COLOR_BGR2GRAY).flatten()#
    cat_frames.append(buf)
cat_frames = np.array(cat_frames).T#Bild für DMD verwendet
cap.release()

Dynamische Moduszerlegung von Video und Trennung von Hintergrund und sich bewegendem Objekt

Bringt das Video in den dynamischen Modus. Der Hintergrund ist, dass der Vibrations- / Amplitudenmodus 0 Amplituden und 0 Frequenzen hat. Der sich bewegende Körper ist etwas anderes als der Hintergrund.

#DMD-Berechnung
X1 = cat_frames[:,:-1]
X2 = cat_frames[:,1:]
mu, phi = dmd(X1,X2)
omega = np.log(mu)/dt#Vibrations- / Amplitudenmodus

#Beurteilung des Hintergrunds und des sich bewegenden Objekts
bg = np.abs(omega) < 1e-2 #Hintergrundextraktion
fg = ~bg #Beweglicher Körper

phi_bg = phi[:,bg]#Dynamischer Hintergrundmodus
phi_fg = phi[:,fg]#Dynamischer Modus zum Bewegen von Objekten
omega_bg = omega[bg]#Hintergrundvibrations- / Amplitudenmodus
omega_fg = omega[fg]#Vibrations- / Amplitudenmodus des sich bewegenden Objekts

Rekonstruktion anderer dynamischer Modi als Hintergrundkomponenten

Verwenden Sie die folgende Formel, um ein Video im Bewegungsmodus zu rekonstruieren.

X_{dmd}^{fg} = \sum_{k=1}^{n}b_k^{fg}\phi_{k}^{fg}\exp(\omega_k^{fg}t)=\Phi^{fg} diag(\exp(\omega^{fg} t))\mathbf{b}^{fg}

$ \ Phi ^ {fg} $ ist die dynamische Modusmatrix des sich bewegenden Objekts, $ \ omega ^ {fg} $ ist der Schwingungs- / Amplitudenmodus des sich bewegenden Objekts und die pseudo-inverse Matrix der dynamischen Modusmatrix des sich bewegenden Objekts ist $ \ mathbf {von rechts. Es ist eine Matrix, die erhalten wird, indem das Matrixprodukt des anfänglichen Videowerts von b} ^ {fg} $ genommen wird.

#Wiederaufbau
phi_fg_pinv = la.pinv(phi_fg)#Pseudo-inverse Matrix. Es benötigt viel Zeit. Wenn Sie nicht genügend Speicher haben, erhöhen Sie r.

X_fg = np.zeros((len(omega_fg), end_frame - start_frame), dtype='complex')
b = phi_fg_pinv @ cat_frames[:,0]#Ursprünglicher Wert

for tt in range(end_frame - start_frame):
    X_fg[:,tt] = b * np.exp(omega_fg * dt * tt)
X_fg = phi_fg @ X_fg

#Zur Helligkeitseinstellung
lum_max = np.abs(X_fg.real.max())
lum_min = np.abs(X_fg.real.min())
lum_diff = lum_max + lum_min

#Videoerstellung
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
writer = cv2.VideoWriter("out_dmd_fg.mp4", fourcc, fps, (int(wid/r), int(hei/r)))

for tt in range(end_frame - start_frame):
    a = X_fg[:,tt].real.reshape(int(hei/r), -1)
    a = (a + lum_min)/lum_diff * 255
    a = a.astype("uint8")
    out_img = np.tile(a, (3,1,1)).transpose((1,2,0))#Graustufen[wid, hei, 3]In eine Matrix von konvertieren
    writer.write(out_img)
writer.release()

Ergebnis

Sie können sehen, dass der Hintergrund verschwunden ist und die Bewegung nur für Menschen ist. Ein Nachbild ist jedoch in der Bewegung von Menschen zu sehen. Ich denke, das liegt daran, dass nur 50 Bilder des Videos verwendet werden, aber es scheint, dass ich nicht gut darin bin, lineare Bewegungen zu zerlegen. original DMD

Verweise

Alle Quellcode

import numpy as np
import scipy.linalg as la
import cv2

#Bildpfad
video_path = "./atrium_video.avi"

#DMD-Definition
def dmd(X, Y, truncate=None):
    u2,sig2,vh2 = la.svd(X, False) 
    r = len(sig2) if truncate is None else truncate
    u = u2[:,:r]
    sig = np.diag(sig2)[:r,:r]
    v = vh2.conj().T[:,:r]
    Atil = np.dot(np.dot(np.dot(u.conj().T, Y), v), la.inv(sig))
    mu,w = la.eig(Atil)
    phi = np.dot(np.dot(np.dot(Y, v), la.inv(sig)), w)#DMD mode
    return mu, phi

#Bild wird geladen
cap = cv2.VideoCapture(video_path)

#Holen Sie sich Bildauflösung, Bildrate, Anzahl der Bilder
wid = cap.get(cv2.CAP_PROP_FRAME_WIDTH)#Seite
hei = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)#Vertikal
fps = cap.get(cv2.CAP_PROP_FPS)#Bildrate
count = cap.get(cv2.CAP_PROP_FRAME_COUNT)#Anzahl der Frames
dt = 1/fps#Anzahl der Sekunden zwischen den Bildern
print(f"wid:{wid}", f" hei:{hei}", f" fps:{fps}", f" count:{count}", f"dt:{dt}")

#Rahmen zu verwenden
start_frame =120
end_frame = 170

#Bildauflösung 1/Auf 4 setzen (um den Rechenaufwand zu reduzieren)
r = 4

#Rahmenextraktion
cat_frames = []
cap.set(cv2.CAP_PROP_POS_FRAMES,start_frame)
for i in range(end_frame - start_frame):
    ret, img = cap.read()
    if not ret:
        print("no image")
        break
    buf = cv2.cvtColor(cv2.resize(img,(int(wid/r), int(hei/r))), cv2.COLOR_BGR2GRAY).flatten()#
    cat_frames.append(buf)
cat_frames = np.array(cat_frames).T
cap.release()

#DMD-Berechnung
X1 = cat_frames[:,:-1]
X2 = cat_frames[:,1:]
mu, phi = dmd(X1,X2)
omega = np.log(mu)/dt

#Urteil anders als Hintergrund und bewegliches Objekt
bg = np.abs(omega) < 1e-2 #Hintergrundextraktion
fg = ~bg #Extraktion des sich bewegenden Körpers

phi_bg = phi[:,bg]#Dynamischer Hintergrundmodus
phi_fg = phi[:,fg]#Dynamischer Modus zum Bewegen von Objekten
omega_bg = omega[bg]#Hintergrundvibrations- / Amplitudenmodus
omega_fg = omega[fg]#Vibrations- / Amplitudenmodus des sich bewegenden Objekts

#Rekonstruktion im dynamischen Modus
phi_fg_pinv = la.pinv(phi_fg)#Pseudo-inverse Matrix. Wenn ein Speicherfehler auftritt, erhöhen Sie r

X_fg = np.zeros((len(omega_fg), end_frame - start_frame), dtype='complex')
b = phi_fg_pinv @ cat_frames[:,0]#Ursprünglicher Wert

for tt in range(end_frame - start_frame):
    X_fg[:,tt] = b * np.exp(omega_fg * dt * tt)
X_fg = phi_fg @ X_fg

#Zur Helligkeitseinstellung
lum_max = np.abs(X_fg.real.max())
lum_min = np.abs(X_fg.real.min())
lum_diff = lum_max + lum_min

#Videoerstellung
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
writer = cv2.VideoWriter("out_dmd_fg.mp4", fourcc, fps, (int(wid/r), int(hei/r)))

for tt in range(end_frame - start_frame):
    a = X_fg[:,tt].real.reshape(int(hei/r), -1)
    a = (a + lum_min)/lum_diff * 255
    a = a.astype("uint8")
    out_img = np.tile(a, (3,1,1)).transpose((1,2,0))#Graustufen[wid, hei, 3]In eine Matrix von konvertieren
    writer.write(out_img)
writer.release()   

Recommended Posts

Trennung von Hintergrund und sich bewegenden Objekten mithilfe der dynamischen Moduszerlegung
Einführung in die dynamische Moduszerlegung
Verschieben von Objekttrennungen mit Robust PCA