Use esta técnica para aplicar um pouco de matemática inteligente aos seus vídeos e reduzir a trepidação.

A estabilização de vídeo é uma técnica que reduz movimentos indesejados e tremores em imagens de vídeo. Fotografia manual, vibração e movimento podem causar movimentos instáveis ​​da câmera. A estabilização de vídeo produz um vídeo de aparência mais suave.

O principal objetivo da estabilização de vídeo é estimar o movimento da câmera entre quadros consecutivos. O processo pode então aplicar transformações apropriadas para alinhar os quadros. Isso minimiza o movimento percebido.

Configurando seu ambiente

Começar por criando um ambiente virtual para garantir que os pacotes instalados para executar o programa não entrem em conflito com os existentes. Em seguida, execute este comando de terminal para instalar as bibliotecas necessárias:

pip instalar opencv-python numpy

Este comando instala as bibliotecas NumPy e OpenCV. NumPy fornece ferramentas para tarefas numéricas enquanto o OpenCV lida com tarefas de visão computacional.

O código-fonte completo está disponível em um Repositório GitHub.

Importando as bibliotecas necessárias e definindo três funções cruciais

Crie um novo arquivo Python e dê a ele um nome de sua preferência. Importe as bibliotecas NumPy e OpenCV no início do script.

importar entorpecido como np
importar cv2

A importação dessas bibliotecas permitirá que você use suas funções em seu código.

A seguir, defina três funções que serão cruciais para o processo de estabilização.

A função calculate_moving_average

Crie uma função e nomeie-a calcular_moving_average. Esta função calculará a média móvel de uma determinada curva usando o raio que você especificar. Ele emprega uma operação de convolução com um tamanho de janela especificado e um kernel uniforme. Essa média móvel ajuda a suavizar as flutuações na trajetória.

defcalcular_moving_average(curva, raio):
# Calcula a média móvel de uma curva usando um determinado raio
tamanho_janela = 2 * raio + 1
kernel = np.ones (window_size) / window_size
curve_padded = np.lib.pad (curva, (raio, raio), 'borda')
smoothed_curve = np.convolve (curve_padded, kernel, mode='mesmo')
smoothed_curve = smoothed_curve[raio:-raio]
retornar curva_suavizada

A função retorna uma curva suave. Ajuda a reduzir ruídos e flutuações na curva. Ele faz isso calculando a média dos valores dentro da janela deslizante.

A função smooth_trajectory

Crie outra função e nomeie-a trajetória_suave. Esta função aplicará a média móvel em cada dimensão da trajetória. Ele conseguirá isso criando uma cópia suavizada da trajetória original. Isso melhorará ainda mais a estabilidade do vídeo.

deftrajetória_suave(trajetória):
# Suaviza a trajetória usando a média móvel em cada dimensão
smoothed_trajectory = np.copy (trajetória)

para eu em faixa(3):
smoothed_trajectory[:, i] = calculate_moving_average(
trajetória[:, eu],
raio=SMOOTHING_RADIUS
)

retornar trajetória_suavizada

O trajetória_suave A função retorna uma trajetória suavizada.

A função fix_border

Crie uma função final e nomeie-a fix_border. Esta função corrigirá a borda do quadro aplicando uma transformação de rotação e dimensionamento. Ele pega o quadro de entrada, calcula sua forma, constrói uma matriz de transformação e aplica a transformação ao quadro. Finalmente, ele retorna o quadro fixo.

deffix_border(quadro):
# Fixe a borda do quadro aplicando rotação e transformação de escala
frame_shape = frame.shape

matriz = cv2.getRotationMatrix2D(
(moldura_forma[1] / 2, frame_shape[0] / 2),
0,
1.04
)

frame = cv2.warpAffine (frame, matrix, (frame_shape[1], frame_shape[0]))
retornar quadro

O fix_border A função garante que os quadros estabilizados não tenham nenhum artefato de borda causado pelo processo de estabilização.

Inicializando a estabilização de vídeo e obtendo a entrada

Comece definindo o raio que a função de suavização de trajetória usará.

SMOOTHING_RADIUS = 50

Em seguida, passe o caminho de vídeo do vídeo tremido que deseja estabilizar.

# Abra o arquivo de vídeo de entrada
# Substitua o caminho por 0 para usar sua webcam
cap = cv2.VideoCapture('inputvid.mp4')

Obtenha as propriedades do vídeo tremido:

num_frames = int (cap.get (cv2.CAP_PROP_FRAME_COUNT))
largura = int (cap.get (cv2.CAP_PROP_FRAME_WIDTH))
altura = int (cap.get (cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get (cv2.CAP_PROP_FPS)

Defina o formato de saída. Este é o formato com o qual o programa salvará o vídeo estabilizado. Você pode usar qualquer formato de vídeo comum você gosta.

fourcc = cv2.VideoWriter_fourcc(*'mp4v')

Por fim, inicialize o gravador de vídeo:

out = cv2.VideoWriter('video_out.mp4', fourcc, fps, (2 * largura altura))

A extensão do nome do arquivo que você passa para o gravador de vídeo deve ser a mesma que você definiu no formato de saída.

Lendo e Processando Quadros

A primeira etapa do processamento do vídeo tremido começa aqui. Envolve a leitura de quadros do vídeo de entrada, o cálculo de transformações e o preenchimento da matriz de transformações.

Comece lendo o primeiro quadro.

_, anterior_quadro = cap.read()
prev_gray = cv2.cvtColor (prev_frame, cv2.COLOR_BGR2GRAY)

Em seguida, inicialize a matriz de transformação. Ele irá armazenar informações para cada quadro.

transforma = np.zeros((num_frames - 1, 3), np.float32)

Finalmente, você precisa calcular o fluxo óptico entre quadros consecutivos. Em seguida, estime a transformação afim entre os pontos.

para eu em intervalo (num_frames - 2):
# Calcule o fluxo óptico entre quadros consecutivos
prev_points = cv2.goodFeaturesToTrack(
prev_gray,
maxCorners=200,
nívelqualidade=0.01,
distância min =30,
tamanho do bloco =3
)

sucesso, curr_frame = cap.read()

senão sucesso:
quebrar

curr_gray = cv2.cvtColor (curr_frame, cv2.COLOR_BGR2GRAY)

curr_points, status, err = cv2.calcOpticalFlowPyrLK(
prev_gray,
curr_gray,
prev_points,
Nenhum
)

afirmar prev_points.shape == curr_points.shape
idx = np.where (estado == 1)[0]
pontos_anteriores = pontos_anteriores[idx]
curr_points = curr_points[idx]

# Estima a transformação afim entre os pontos
matriz, _ = cv2.estimateAffine2D(prev_points, curr_points)
tradução_x = matriz[0, 2]
tradução_y = matriz[1, 2]
ângulo_rotação = np.arctan2(matriz[1, 0], matriz[0, 0])
transforma[i] = [translação_x, tradução_y, rotação_ângulo]
prev_gray = curr_gray

O loop itera sobre cada quadro (exceto o último quadro) para calcular as transformações. Ele calcula o fluxo óptico entre quadros consecutivos usando o método Lucas-Kanade. cv2.goodFeaturesToTrack detecta pontos de recurso no quadro anterior prev_gray. Então, cv2.calcOpticalFlowPyrLK rastreia esses pontos no quadro atual curr_gray.

Apenas os pontos com status 1 (indicando rastreamento bem-sucedido) ajudam na estimativa de uma matriz de transformação afim. O código atualiza o prev_gray variável com o quadro de tons de cinza atual para a próxima iteração.

Suavizando a Trajetória

Você precisa suavizar a trajetória obtida das transformações para obter um resultado estável.

# Calcule a trajetória somando cumulativamente as transformações
trajetória = np.cumsum (transforma, eixo=0)

# Suaviza a trajetória usando a média móvel
smoothed_trajectory = smooth_trajectory (trajetória)

# Calcula a diferença entre a trajetória suavizada e original
diferença = smoothed_trajectory - trajetória

# Adicione a diferença de volta às transformações originais para obter suavização
# transformações
transforms_smooth = transforma + diferença

O código acima calcula a trajetória do movimento da câmera e a suaviza.

Estabilizar e escrever quadros

A etapa final é estabilizar os quadros e gravar o vídeo estabilizado em um arquivo de saída.

Comece redefinindo a captura de vídeo. Isso garante que as operações futuras sejam lidas desde o início do vídeo.

cap.set (cv2.CAP_PROP_POS_FRAMES, 0)

Em seguida, estabilize o vídeo processando cada quadro.

# Processe cada quadro e estabilize o vídeo
para eu em intervalo (num_frames - 2):
sucesso, frame = cap.read()

senão sucesso:
quebrar

tradução_x = transforma_suave[i, 0]
tradução_y = transforma_suave[i, 1]
ângulo_rotação = transforma_suave[i, 2]

# Crie a matriz de transformação para estabilização
matriz_de_transformação = np.zeros((2, 3), np.float32)
matriz_de_transformação[0, 0] = np.cos (rotation_angle)
matriz_de_transformação[0, 1] = -np.sin (ângulo_rotação)
matriz_de_transformação[1, 0] = np.sin (ângulo_rotação)
matriz_de_transformação[1, 1] = np.cos (rotation_angle)
matriz_de_transformação[0, 2] = tradução_x
matriz_de_transformação[1, 2] = tradução_y

# Aplique a transformação para estabilizar o quadro
frame_stabilized = cv2.warpAffine(
quadro,
matriz_de_transformação,
(largura altura)
)

# Corrige a borda do quadro estabilizado
frame_stabilized = fix_border (frame_stabilized)

# Concatenar os quadros originais e estabilizados lado a lado
frame_out = cv2.hconcat([frame, frame_stabilized])

# Redimensione o quadro se sua largura exceder 1920 pixels
se frame_out.shape[1] > 1920:
frame_out = cv2.resize(
frame_out,
(frame_out.shape[1] // 2, frame_out.shape[0] // 2)
)

# Exibe os quadros antes e depois
cv2.imshow("Antes e depois", frame_out)
cv2.waitKey(10)

# Escreva o quadro no arquivo de vídeo de saída
out.write (frame_out)

O código acima estabiliza cada quadro usando as transformações calculadas, incluindo ajustes de translação e rotação. Em seguida, combina os quadros estabilizados com os originais para fornecer uma comparação.

Liberando a captura e o gravador de vídeo

Finalize o programa liberando os objetos de captura e gravação de vídeo.

# Libere a captura e o gravador de vídeo e feche todas as janelas abertas
cap.release()
out.release()
cv2.destroyAllWindows()

Este código também fecha todas as janelas abertas.

Saída Final do Programa

A saída do programa será mais ou menos assim:

E aqui está um exemplo do vídeo estabilizado:

A saída mostra a comparação entre o vídeo instável e o estabilizado.

Explore os recursos do OpenCV

Você pode aplicar o OpenCV em muitos campos que envolvem visão computacional. Isso porque ele oferece uma ampla gama de funcionalidades. Você deve explorar seus recursos trabalhando em mais projetos que envolvam visão computacional. Isso irá apresentá-lo a novos conceitos e dar-lhe novas áreas para pesquisa.