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
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.
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
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.
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()
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
Utilisez la formule suivante pour reconstruire une vidéo en mode mouvement.
$ \ 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()
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.
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()