Séparation d'arrière-plan / objet en mouvement à l'aide de la décomposition en mode dynamique

introduction

Utilisez la décomposition en mode dynamique (DMD) pour séparer l'arrière-plan de la vidéo afin de créer une vidéo avec uniquement le mode dynamique de l'objet en mouvement. La décomposition en mode dynamique est comme une combinaison d'analyse en composantes principales et de transformée de Fourier. Pour une explication détaillée de la décomposition en mode dynamique, veuillez vous référer au lien dans l'article que j'ai écrit précédemment. -https://qiita.com/matsxxx/items/5e4b272de821fb1c11e0

environnement

Qu'est-ce que la décomposition en mode dynamique?

Je présenterai brièvement la procédure de décomposition en mode dynamique. La décomposition en mode dynamique crée un mode dynamique en trouvant les valeurs propres et les vecteurs propres d'une matrice de transition à partir de données de séries temporelles. Il peut être décomposé en entités dans la direction de la dimension et du temps. Les caractéristiques dimensionnelles apparaissent dans les vecteurs propres et les caractéristiques temporelles apparaissent dans des nombres complexes de valeurs propres.

Dans la décomposition en mode dynamique, les valeurs propres et les vecteurs propres de la matrice de transition sont implémentés de manière à pouvoir être obtenus avec une quantité de calcul réaliste. Dans cet article, il est implémenté dans Exact DMD. dmd_describe.png

import scipy.linalg as la
#Définition DMD
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

Vidéo

J'ai utilisé Atrium dans Video here. Ceci est une vidéo d'une personne qui marche. J'utilise 120frame à 170frame. Séparez l'arrière-plan de la personne qui marche. original.png

import cv2
#Chemin de l'image
video_path = "./atrium_video.avi"
#Chargement d'image
cap = cv2.VideoCapture(video_path)

#Obtenez la résolution d'image, la fréquence d'images, le nombre d'images
wid = cap.get(cv2.CAP_PROP_FRAME_WIDTH)#côté
hei = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)#Verticale
fps = cap.get(cv2.CAP_PROP_FPS)#fréquence d'images
count = cap.get(cv2.CAP_PROP_FRAME_COUNT)#Nombre de cadres
dt = 1/fps#Nombre de secondes entre les images
print(f"wid:{wid}", f" hei:{hei}", f" fps:{fps}", f" count:{count}", f"dt:{dt}")

#Cadre à utiliser
start_frame =120
end_frame = 170

#Résolution d'image 1/Régler sur 4 (pour réduire la quantité de calcul)
r = 4

#Extraction du cadre
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#Image utilisée pour DMD
cap.release()

Décomposition en mode dynamique de la vidéo et séparation de l'arrière-plan et de l'objet en mouvement

Met la vidéo en mode dynamique. Le fond est que le mode vibration / amplitude a 0 amplitude et 0 fréquence. Le corps en mouvement est autre chose que l'arrière-plan.

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

#Jugement de l'arrière-plan et de l'objet en mouvement
bg = np.abs(omega) < 1e-2 #Extraction d'arrière-plan
fg = ~bg #Corps en mouvement

phi_bg = phi[:,bg]#Mode d'arrière-plan dynamique
phi_fg = phi[:,fg]#Mode dynamique de l'objet en mouvement
omega_bg = omega[bg]#Mode vibration / amplitude de fond
omega_fg = omega[fg]#Mode vibration / amplitude de l'objet en mouvement

Reconstruction de modes dynamiques autres que les composants d'arrière-plan

Utilisez la formule suivante pour reconstruire une vidéo en mode mouvement.

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} $ est la matrice de mode dynamique de l'objet en mouvement, $ \ omega ^ {fg} $ est le mode de vibration / amplitude de l'objet en mouvement, et la pseudo matrice inverse de la matrice de mode dynamique de l'objet en mouvement est $ \ mathbf {de droite. C'est une matrice obtenue en prenant le produit matriciel de la valeur vidéo initiale de b} ^ {fg} $.

#Reconstitution
phi_fg_pinv = la.pinv(phi_fg)#Matrice pseudo inverse. Ça prend beaucoup de temps. Si vous n'avez pas assez de mémoire, augmentez r.

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

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

#Pour le réglage de la luminosité
lum_max = np.abs(X_fg.real.max())
lum_min = np.abs(X_fg.real.min())
lum_diff = lum_max + lum_min

#Création vidéo
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))#Échelle de gris[wid, hei, 3]Convertir en une matrice de
    writer.write(out_img)
writer.release()

résultat

Vous pouvez voir que l'arrière-plan a disparu et que le mouvement n'est que pour les gens. Cependant, une image rémanente peut être vue dans le mouvement des personnes. Je pense que c'est parce qu'il n'y a que 50 images de la vidéo que j'ai utilisée, mais il semble que je ne suis pas doué pour décomposer les mouvements linéaires. original DMD

Les références

Tout le code source

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

#Chemin de l'image
video_path = "./atrium_video.avi"

#Définition DMD
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

#Chargement d'image
cap = cv2.VideoCapture(video_path)

#Obtenez la résolution d'image, la fréquence d'images, le nombre d'images
wid = cap.get(cv2.CAP_PROP_FRAME_WIDTH)#côté
hei = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)#Verticale
fps = cap.get(cv2.CAP_PROP_FPS)#fréquence d'images
count = cap.get(cv2.CAP_PROP_FRAME_COUNT)#Nombre de cadres
dt = 1/fps#Nombre de secondes entre les images
print(f"wid:{wid}", f" hei:{hei}", f" fps:{fps}", f" count:{count}", f"dt:{dt}")

#Cadre à utiliser
start_frame =120
end_frame = 170

#Résolution d'image 1/Régler sur 4 (pour réduire la quantité de calcul)
r = 4

#Extraction du cadre
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()

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

#Jugement autre que l'arrière-plan et l'objet en mouvement
bg = np.abs(omega) < 1e-2 #Extraction d'arrière-plan
fg = ~bg #Extraction de corps en mouvement

phi_bg = phi[:,bg]#Mode d'arrière-plan dynamique
phi_fg = phi[:,fg]#Mode dynamique de l'objet en mouvement
omega_bg = omega[bg]#Mode vibration / amplitude de fond
omega_fg = omega[fg]#Mode vibration / amplitude de l'objet en mouvement

#Reconstruction du mode dynamique
phi_fg_pinv = la.pinv(phi_fg)#Matrice pseudo inverse. Si une erreur de mémoire se produit, augmentez r

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

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

#Pour le réglage de la luminosité
lum_max = np.abs(X_fg.real.max())
lum_min = np.abs(X_fg.real.min())
lum_diff = lum_max + lum_min

#Création vidéo
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))#Échelle de gris[wid, hei, 3]Convertir en une matrice de
    writer.write(out_img)
writer.release()   

Recommended Posts

Séparation d'arrière-plan / objet en mouvement à l'aide de la décomposition en mode dynamique
Présentation de la décomposition en mode dynamique
Séparation d'objets en mouvement avec Robust PCA