Come implementare la stabilizzazione video in tempo reale utilizzando OpenCV
Usa questa tecnica per applicare alcuni calcoli intelligenti ai tuoi video e ridurre le vibrazioni.
La stabilizzazione video è una tecnica che riduce i movimenti indesiderati e le vibrazioni nelle riprese video. Lo scatto manuale, le vibrazioni e il movimento possono causare movimenti instabili della fotocamera. La stabilizzazione video produce un video dall'aspetto più fluido.
L'obiettivo principale della stabilizzazione video è stimare il movimento della fotocamera tra fotogrammi consecutivi. Il processo può quindi applicare le trasformazioni appropriate per allineare i fotogrammi. Ciò riduce al minimo il movimento percepito.
Configurazione dell'ambiente
Inizia creando un ambiente virtuale per garantire che i pacchetti installati per eseguire il programma non siano in conflitto con quelli esistenti. Quindi esegui questo comando da terminale per installare le librerie richieste:
pip install opencv-python numpy
Questo comando installa le librerie NumPy e OpenCV. NumPy fornisce strumenti per attività numeriche mentre OpenCV si occupa di attività di visione artificiale.
Il codice sorgente completo è disponibile in un repository GitHub.
Importazione delle librerie richieste e definizione di tre funzioni cruciali
Crea un nuovo file Python e dagli un nome di tuo gradimento. Importa le librerie NumPy e OpenCV all'inizio dello script.
import numpy as np
import cv2
L'importazione di queste librerie ti consentirà di utilizzare le loro funzioni nel tuo codice.
Successivamente, definire tre funzioni che saranno cruciali per il processo di stabilizzazione.
La funzione calcola_media_mobile
Crea una funzione e chiamala calculate_moving_average. Questa funzione calcolerà la media mobile di una determinata curva utilizzando il raggio specificato. Impiega un'operazione di convoluzione con una dimensione della finestra specificata e un kernel uniforme. Questa media mobile aiuta ad attenuare le fluttuazioni nella traiettoria.
def calculate_moving_average(curve, radius):
# Calculate the moving average of a curve using a given radius
window_size = 2 * radius + 1
kernel = np.ones(window_size) / window_size
curve_padded = np.lib.pad(curve, (radius, radius), 'edge')
smoothed_curve = np.convolve(curve_padded, kernel, mode='same')
smoothed_curve = smoothed_curve[radius:-radius]
return smoothed_curve
La funzione restituisce una curva uniforme. Aiuta a ridurre il rumore e le fluttuazioni della curva. Lo fa calcolando la media dei valori all'interno della finestra scorrevole.
La funzione smooth_trajectory
Crea un'altra funzione e chiamala smooth_trajectory. Questa funzione applicherà la media mobile su ciascuna dimensione della traiettoria. Otterrà questo risultato creando una copia levigata della traiettoria originale. Ciò migliorerà ulteriormente la stabilità del video.
def smooth_trajectory(trajectory):
# Smooth the trajectory using moving average on each dimension
smoothed_trajectory = np.copy(trajectory)
for i in range(3):
smoothed_trajectory[:, i] = calculate_moving_average(
trajectory[:, i],
radius=SMOOTHING_RADIUS
)
return smoothed_trajectory
La funzione smooth_trajectory restituisce una traiettoria attenuata.
La funzione fix_border
Crea una funzione finale e chiamala fix_border. Questa funzione fisserà il bordo della cornice applicando una trasformazione di rotazione e ridimensionamento. Prende il fotogramma di input, ne calcola la forma, costruisce una matrice di trasformazione e applica la trasformazione al fotogramma. Infine, restituisce il frame fisso.
def fix_border(frame):
# Fix the frame border by applying rotation and scaling transformation
frame_shape = frame.shape
matrix = cv2.getRotationMatrix2D(
(frame_shape[1] / 2, frame_shape[0] / 2),
0,
1.04
)
frame = cv2.warpAffine(frame, matrix, (frame_shape[1], frame_shape[0]))
return frame
La funzione fix_border garantisce che i fotogrammi stabilizzati non presentino artefatti sui bordi causati dal processo di stabilizzazione.
Inizializzazione della stabilizzazione video e acquisizione dell'input
Inizia impostando il raggio che utilizzerà la funzione di livellamento della traiettoria.
SMOOTHING_RADIUS = 50
Quindi, passa nel percorso video del video traballante che desideri stabilizzare.
# Open the input video file
# Replace the path with 0 to use your webcam
cap = cv2.VideoCapture('inputvid.mp4')
Ottieni le proprietà del video traballante:
num_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
Imposta il formato di output. Questo è il formato in cui il programma salverà il video stabilizzato. Puoi utilizzare qualsiasi formato video comune che preferisci.
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
Infine, inizializza il video writer:
out = cv2.VideoWriter('video_out.mp4', fourcc, fps, (2 * width, height))
L'estensione del nome file che passi al video writer dovrebbe essere la stessa che hai impostato nel formato di output.
Lettura ed elaborazione dei frame
Il primo passaggio dell'elaborazione del video traballante inizia qui. Implica la lettura dei fotogrammi dal video di input, il calcolo delle trasformazioni e il popolamento dell'array delle trasformazioni.
Inizia leggendo il primo fotogramma.
_, prev_frame = cap.read()
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
Quindi inizializzare l'array di trasformazione. Memorizzerà le informazioni per ciascun frame.
transforms = np.zeros((num_frames - 1, 3), np.float32)
Infine, è necessario calcolare il flusso ottico tra fotogrammi consecutivi. Quindi, stimare la trasformazione affine tra i punti.
for i in range(num_frames - 2):
# Calculate optical flow between consecutive frames
prev_points = cv2.goodFeaturesToTrack(
prev_gray,
maxCorners=200,
qualityLevel=0.01,
minDistance=30,
blockSize=3
)
success, curr_frame = cap.read()
if not success:
break
curr_gray = cv2.cvtColor(curr_frame, cv2.COLOR_BGR2GRAY)
curr_points, status, err = cv2.calcOpticalFlowPyrLK(
prev_gray,
curr_gray,
prev_points,
None
)
assert prev_points.shape == curr_points.shape
idx = np.where(status == 1)[0]
prev_points = prev_points[idx]
curr_points = curr_points[idx]
# Estimate affine transformation between the points
matrix, _ = cv2.estimateAffine2D(prev_points, curr_points)
translation_x = matrix[0, 2]
translation_y = matrix[1, 2]
rotation_angle = np.arctan2(matrix[1, 0], matrix[0, 0])
transforms[i] = [translation_x, translation_y, rotation_angle]
prev_gray = curr_gray
Il ciclo ripete ogni fotogramma (tranne l'ultimo fotogramma) per calcolare le trasformazioni. Calcola il flusso ottico tra fotogrammi consecutivi utilizzando il metodo Lucas-Kanade. cv2.goodFeaturesToTrack rileva i punti caratteristici nel fotogramma precedente prev_gray. Quindi, cv2.calcOpticalFlowPyrLK tiene traccia di questi punti nel fotogramma corrente curr_gray.
Solo i punti con stato 1 (che indica un tracciamento riuscito) aiutano a stimare una matrice di trasformazione affine. Il codice aggiorna la variabile prev_gray con il fotogramma in scala di grigi corrente per l'iterazione successiva.
Levigare la traiettoria
È necessario attenuare la traiettoria ottenuta dalle trasformazioni per ottenere un risultato stabile.
# Calculate the trajectory by cumulatively summing the transformations
trajectory = np.cumsum(transforms, axis=0)
# Smooth the trajectory using moving average
smoothed_trajectory = smooth_trajectory(trajectory)
# Calculate the difference between the smoothed and original trajectory
difference = smoothed_trajectory - trajectory
# Add the difference back to the original transformations to obtain smooth
# transformations
transforms_smooth = transforms + difference
Il codice sopra calcola la traiettoria del movimento della telecamera e la attenua.
Cornici stabilizzatrici e di scrittura
Il passaggio finale consiste nel stabilizzare i fotogrammi e scrivere il video stabilizzato in un file di output.
Inizia reimpostando l'acquisizione video. Ciò garantisce che le operazioni future verranno lette dall'inizio del video.
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
Quindi stabilizza il video elaborando ogni fotogramma.
# Process each frame and stabilize the video
for i in range(num_frames - 2):
success, frame = cap.read()
if not success:
break
translation_x = transforms_smooth[i, 0]
translation_y = transforms_smooth[i, 1]
rotation_angle = transforms_smooth[i, 2]
# Create the transformation matrix for stabilization
transformation_matrix = np.zeros((2, 3), np.float32)
transformation_matrix[0, 0] = np.cos(rotation_angle)
transformation_matrix[0, 1] = -np.sin(rotation_angle)
transformation_matrix[1, 0] = np.sin(rotation_angle)
transformation_matrix[1, 1] = np.cos(rotation_angle)
transformation_matrix[0, 2] = translation_x
transformation_matrix[1, 2] = translation_y
# Apply the transformation to stabilize the frame
frame_stabilized = cv2.warpAffine(
frame,
transformation_matrix,
(width, height)
)
# Fix the border of the stabilized frame
frame_stabilized = fix_border(frame_stabilized)
# Concatenate the original and stabilized frames side by side
frame_out = cv2.hconcat([frame, frame_stabilized])
# Resize the frame if its width exceeds 1920 pixels
if frame_out.shape[1] > 1920:
frame_out = cv2.resize(
frame_out,
(frame_out.shape[1] // 2, frame_out.shape[0] // 2)
)
# Display the before and after frames
cv2.imshow("Before and After", frame_out)
cv2.waitKey(10)
# Write the frame to the output video file
out.write(frame_out)
Il codice precedente stabilizza ciascun fotogramma utilizzando le trasformazioni calcolate, comprese le regolazioni di traslazione e rotazione. Quindi combina i fotogrammi stabilizzati con quelli originali per fornire un confronto.
Rilascio di Video Capture e Writer
Finalizzare il programma rilasciando gli oggetti di acquisizione video e di scrittura.
# Release the video capture and writer, and close any open windows
cap.release()
out.release()
cv2.destroyAllWindows()
Questo codice chiude anche tutte le finestre aperte.
Risultato finale del programma
L'output del programma sarà simile al seguente:
Ed ecco un esempio del video stabilizzato:
L'output mostra il confronto tra il video traballante e quello stabilizzato.
Esplora le funzionalità di OpenCV
Puoi applicare OpenCV in molti campi che coinvolgono la visione artificiale. Questo perché offre una vasta gamma di funzionalità. Dovresti esplorare le sue capacità lavorando su più progetti che coinvolgono la visione artificiale. Questo ti introdurrà a nuovi concetti e ti darà nuove aree su cui ricercare.