Aplicação do Modelo LaMa em Sequências de Vídeo: Pipeline e Otimização de Inpainting Temporal

Introdução ao Inpainting de Sequências de Vídeo

A extrapolação de modelos de restauração de imagens estáticas, como o LaMa (Large Mask Inpaintnig) com transformadas de Fourier (FFT), para o domínio de vídeo apresenta desafios únicos. Embora um vídeo seja fundamentalmente uma sequência de quadros, a aplicação independente de algoritmos de inpainting em cada quadro frequentemente resulta em artefatos visuais, como cintilação (flickering) e inconsistências temporais. Este artigo detalha a engenharia de um pipeline para prcoessar sequências de vídeo utilizando arquiteturas de imagem, abordando desde a extração de quadros até técnicas de suavização temporal.

Arquitetura do Pipeline de Processamento

A abordagem mais direta para adaptar um modelo de imagem para vídeo é a decomposição do fluxo em três estágios: extração de quadros, processamento em lote e recompilação. Para evitar o esgotamento de memória ao lidar com vídeos de alta resolução, a extração e a injeção de quadros devem ser implementadas utilizando geradores e operações de I/O assíncronas.

O trecho abaixo demonstra uma implementação otimizada utilizando a biblioteca OpenCV, estruturada para lidar com fluxos de vídeo de forma iterativa:


import cv2
import numpy as np
from pathlib import Path
from typing import Generator

def yield_video_frames(video_path: str) -> Generator[np.ndarray, None, None]:
    """Extrai quadros de um vídeo utilizando um gerador para economizar memória."""
    capture = cv2.VideoCapture(video_path)
    if not capture.isOpened():
        raise ValueError(f"Não foi possível abrir o vídeo: {video_path}")
    
    while capture.isOpened():
        success, frame = capture.read()
        if not success:
            break
        yield frame
    capture.release()

def compile_sequence_to_video(frame_dir: Path, output_path: str, fps: float):
    """Recompila os quadros processados em um arquivo de vídeo."""
    frame_files = sorted(frame_dir.glob("frame_*.png"))
    if not frame_files:
        raise FileNotFoundError("Nenhum quadro encontrado no diretório especificado.")
    
    sample_frame = cv2.imread(str(frame_files[0]))
    height, width = sample_frame.shape[:2]
    
    # Utilizando o codec H.264 para melhor compatibilidade e compressão
    fourcc = cv2.VideoWriter_fourcc(*'avc1')
    writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
    
    for file_path in frame_files:
        writer.write(cv2.imread(str(file_path)))
    writer.release()

Desafios da Geração de Máscaras e Inferência em Lote

A aplicação manual de máscaras é inviável para sequências de vídeo. A automação exige a geração programática de máscaras (masks), que podem ser estáticas (para remoção de marcas d'água fixas) ou dinâmicas (utilizando rastreamento de objetos para oclusões em movimento). Para a inferência, processar quadros individualmente introduz uma sobrecarga significativa de I/O e inicialização de tensores. A utilização de DataLoaders do PyTorch permite a inferência em lotes (batching), maximizando a utilização da VRAM da GPU.


import torch
from torch.utils.data import Dataset, DataLoader
from pathlib import Path
import cv2

class VideoInpaintingDataset(Dataset):
    def __init__(self, img_dir: Path, mask_dir: Path, transform=None):
        self.images = sorted(img_dir.glob("*.png"))
        self.masks = sorted(mask_dir.glob("*.png"))
        self.transform = transform

    def __len__(self):
        return len(self.images)

    def __getitem__(self, idx):
        img = cv2.imread(str(self.images[idx]))
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        
        mask = cv2.imread(str(self.masks[idx]), cv2.IMREAD_GRAYSCALE)
        
        if self.transform:
            img = self.transform(img)
            mask = self.transform(mask)
            
        return img, mask, self.images[idx].name

def execute_batch_inference(model, dataloader: DataLoader, device: str, out_dir: Path):
    """Executa a inferência do modelo LaMa em lotes para otimizar o uso da GPU."""
    model.to(device).eval()
    out_dir.mkdir(parents=True, exist_ok=True)
    
    with torch.no_grad():
        for batch_imgs, batch_masks, batch_names in dataloader:
            batch_imgs = batch_imgs.to(device)
            batch_masks = batch_masks.to(device)
            
            # Forward pass do modelo de inpainting
            predictions = model(batch_imgs, batch_masks)
            
            # Salvar resultados no disco
            for i in range(len(predictions)):
                result = predictions[i].cpu().numpy().transpose(1, 2, 0)
                result_bgr = cv2.cvtColor(result, cv2.COLOR_RGB2BGR)
                cv2.imwrite(str(out_dir / batch_names[i]), result_bgr)

Otimização de Consistência Temporal e Desempenho

O principal defeito visual ao aplicar modelos estáticos em vídeos é a cintilação temporal. Como o modelo não possui consciência dos quadros adjacentes, as texturas geradas na região da máscara variam ligeiramente a cada frame. Para mitigar esse problema sem recorrer a modelos 3D complexos, podem ser aplicadas técnicas de pós-processamento, como a suavização por média móvel exponencial ou fusão baseada em fluxo óptico.

Abaixo, uma implementação de suavização temporal que mistura o quadro atual com o quadro restaurado anterior, limitando a variação de pixels na região da máscara:


def apply_temporal_blending(current_frame: np.ndarray, 
                            previous_frame: np.ndarray, 
                            mask: np.ndarray, 
                            alpha: float = 0.4) -> np.ndarray:
    """
    Aplica mistura alfa temporal restrita à região da máscara 
    para reduzir a cintilação (flickering).
    """
    if previous_frame is None:
        return current_frame
        
    # Garantir que a máscara seja de 3 canais para a operação
    mask_3ch = cv2.merge([mask, mask, mask])
    
    # Misturar o quadro atual com o anterior
    blended = cv2.addWeighted(current_frame, alpha, previous_frame, 1 - alpha, 0)
    
    # Aplicar a mistura apenas onde a máscara é válida (região inpainted)
    # e manter o original nas áreas não mascaradas
    result = np.where(mask_3ch > 0, blended, current_frame)
    
    # Suavizar as bordas da máscara para evitar transições bruscas
    blur_kernel = cv2.GaussianBlur(mask.astype(np.float32) / 255.0, (21, 21), 0)
    blur_3ch = cv2.merge([blur_kernel, blur_kernel, blur_kernel])
    
    final_frame = (current_frame * (1 - blur_3ch) + result * blur_3ch).astype(np.uint8)
    return final_frame

Além da consistência visual, o desempenho do pipeline pode ser drasticamente melhorado através do redimensionamento estratégico. Processar vídeos em resoluções 4K é computacionalmente proibitivo para a maioria das arquiteturas de inpainting. Reduzir a resolução para 1080p ou 720p antes da inferência, e utilizando interpolação de vizinhos mais próximos ou bicúbica para restaurar o tamanho original, mantém a integridade estrutural enquanto reduz o tempo de inferência em até 70%.

Análise de Resultados em Diferentes Cenários

A eficácia do pipeline varia substancialmente dependendo da complexidade da cena e do movimento da câmera. A tabela a seguir resume o comportamento do modelo LaMa com FFT em diferentes cotnextos de vídeo:

  • Cenários Estáticos (Câmera fixa, remoção de logotipos/marcas d'água): Excelente desempenho. A máscara permanece constante e a consistência temporal é quase perfeita, resultando em vídeos sem artefatos visíveis.
  • Cenários com Movimento de Câmera (Panning/Tilt): Desempenho moderado. A máscara estática falha em cobrir o objeto em movimento relativo. É obrigatório o uso de algoritmos de rastreamento (como KCF ou SORT) para atualizar as coordenadas da máscara a cada quadro.
  • Oclusões Dinâmicas e Rostos: Desempenho insatisfatório. A alta frequência de detalhes em rostos humanos e a natureza não linear do movimento tornam a cintilação muito evidente, mesmo com técnicas de blending temporal. Modelos específicos para vídeo (como E2FGVI) são recomendados para estes casos.

A integração de modelos de imagem em fluxos de trabalho de vídeo é altamente viável para tarefas de pós-produção específicas, desde que o pipeline incorpore mecanismos robustos de rastreamento de máscaras e estabilização temporal. A escolha do codec de saída e a gestão de espaço de cor (RGB vs BGR) durante a inferência também são fatores críticos que impactam diretamente a fidelidade cromática do material final.

Tags: LaMa Inpainting OpenCV Pytorch computer vision

Publicado em 7-16 01:44