Ricerca nel sito web

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.

Articoli correlati: