Computer Vision - Proyecto Final: RepNet¶

Por: Eric Aguayo

Introducción¶

  • Las acciones repetitivas o ciclicas estan siempre en nuestras actividades diarias
    • Evidente en actividades deportivas: crossfit, atletismo, etc..
    • Latidos del corazón, respiración, gente bailando, construcción, ciclos planetarios, etc.
  • Es preciso reconocer estos patrones y tener un sistema que pueda identificar y contar estos ciclos.

Repeticiones¶

image

Objetivos¶

  • Construir un sistema que detecte si un patrón repetivo tiene ocurrencia
  • Si es que hay un patron repetitivo, determinar la longitud del patron y poder contar el número de eventos

Retos/Dificultades¶

  • Los patrones repetitivos pueden ser de distinta índole y muy diferentes entre si
  • Que exista un movimiento no necesariamente implica que haya una actividad repetitiva
  • Los ciclos pueden tener distintas longitudes
    • Pelota rebotando - los periodos se acortan en el tiempo
    • Actividad deportiva - los periodos se alargan en el tiempo por la fatiga

Acercamiento al problema¶

  • Estimar el período con el que una acción se repite en un video -> conteo de repeticiones

Predicciones¶

  • Por cada frame:
    • Determinar si es que hay una periodicidad -> variable binaria (hay repetición o no)
    • Determinar cual es la longitud del período -> permite realizar un conteo

Descripción General de la arquitectura¶

image

El Dataset (Countix)¶

Datos tabulares en CSV conteniendo ids de videos de youtube y etiquetas

video_id class kinetics_start kinetics_end repetition_start repetition_end count
D5Ge91h0XZw battle rope training 38 48 38.000000 44.633333 7

Data Sets:¶

  • Train: 4412 -> 4103
  • Val: 1407 -> 1291
  • Test: 2556 -> 2372

Para descarga se utilizó yt-dlp y se hizo un filtrado de videos no válidos o no accesibles

Arquitectura del modelo¶

image

Video > batches de 64 frames > Encoder > Embeddings > Self-Similarity Matrix > Multihead self-attention Transformer > Period Length Predictor & Periodicity Predictor

Encoder¶

image

  • Extractor de características: Resnet 50 (pretrained weights)
  • Embeddings iniciales pasan por una Convolución 3D para extraer características temporales de los vecinos con kernel de 3x3x3
  • Reducción de disponibilidad (Max Pooling)
# Encoder
encoder = torch.hub.load("huggingface/pytorch-image-models", model_name)
#...

# Temporal Convolution layers
self.temporal_conv = nn.Sequential(
    nn.Conv3d(
        in_channels=1024,  # From encoder
        out_channels=self.temporal_conv_channels,
        kernel_size=self.temporal_conv_kernel_size,
        padding="same",
        dilation=(self.temporal_conv_dilation, 1, 1),
    ),
    nn.BatchNorm3d(self.temporal_conv_channels, eps=0.001),
    nn.ReLU(inplace=True),
    nn.AdaptiveMaxPool3d((None, 1, 1)),
    nn.Flatten(2, 4),
)

Self-similarity Matrix¶

image

image

  • Negativo de la distancia euclideana $f(a,b)= -||a-b||^2$
  • Seguida de una operacion softmax por fila
def get_sims(self, embs, temperature):
    # Compute temporal self-similarity matrix
    sim_mat = torch.cdist(embs, embs)**2 # N x D x D
    sim_mat = -sim_mat / temperature
    sim_mat = sim_mat.softmax(dim=-1)

    return sim_mat

Predictor¶

image

  • Conv2D -> Expandir a 32 canales (upsample) con kernels de 3x3. Cada frame corresponde un "slice" de 32x64
  • Capa de Transforme que usa atención con múltiples cabezas con embeddings posicionales de tamaño 64 y 4 cabezas con 512 dimensiones (cada cabeza 128 dimensiones). Extrae las propiedades temporales
  • Predictores usan la arquitectura de transformer compartida hasta la etapa de clasificación
    • $l = \Psi(S)$
    • $p = \tau(S)$
# Counting Module (Self-sim > Conv > Transformer > Classifier)
self.conv_3x3_layer = nn.Sequential(
    nn.Conv2d(
        in_channels=1,  # assuming single channel input from self-similarity
        out_channels=self.conv_channels,
        kernel_size=self.conv_kernel_size,
        padding="same",
    ),
    nn.ReLU(inplace=True),
)
class TransformerHead(nn.Module):
    class TransformerLayer(nn.Module):
        """A single transformer layer with self-attention and positional encoding."""

        def __init__(
            self, in_features: int, n_head: int, out_features: int, num_frames: int
        ):
            super().__init__()
            self.input_projection = nn.Linear(in_features, out_features)
            self.pos_encoding = nn.Parameter(
                torch.normal(mean=0, std=0.02, size=(1, num_frames, out_features))
            )
            self.transformer_layer = nn.TransformerEncoderLayer(
                d_model=out_features,
                nhead=n_head,
                dim_feedforward=out_features,
                activation="relu",
                layer_norm_eps=1e-6,
                batch_first=True,
                norm_first=True,
            )

        def forward(self, x: torch.Tensor) -> torch.Tensor:
            """Forward pass, expected input shape: N x C x D."""
            x = self.input_projection(x)
            x = x + self.pos_encoding
            x = self.transformer_layer(x)
            return x

    def __init__(
        self,
        in_features: int,
        n_head: int,
        hidden_features: int,
        out_features: int,
        num_frames: int,
    ):
        super().__init__()

        self.predictor_head = nn.Sequential(
            self.TransformerLayer(in_features, n_head, hidden_features, num_frames),
            nn.Linear(hidden_features, hidden_features),
            nn.ReLU(inplace=True),
            nn.Linear(hidden_features, hidden_features),
            nn.ReLU(inplace=True),
            nn.Linear(hidden_features, out_features),
        )

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return self.predictor_head(x)

Entrenamiento¶

Datos sintéticos¶

image

  • Repeticiones sinteticas a partir de los videos (repetir loops). Entrenamiento inicial
  • Puede generarse a partir de cualquier video no etiquetado Ventajas:
  • Garantiza etiquetación de periodicidad
  • Método eficiente de data augmentation

Camera Motion Augmentation¶

image

  • No se llegó a implementar por falta de tiempo

Función de Pérdida¶

  • Estimador de periodicidad $\tau$ es una clasificación binaria por lo que se usa binary cross-entropy.
  • Estimador de longitud de período $\Psi$ es una clasificación multiclase $\{2,3, ...,\frac{N}{2}\}$ por lo que se usa softmax cross-entropy.
    # Forward pass
    period_length_pred, periodicity_pred, final_embs = model(frames)
    
    # Calculate losses
    # Convert ground truth period lengths to class indices
    # Period lengths 2-33 map to class indices 0-31
    period_length_classes = torch.clamp(period_length_gt - 2, min=0, max=model.num_frames//2 - 1).long()
    
    # Handle frames with no periodicity (period_length_gt == 0)
    non_periodic_mask = (period_length_gt == 0)
    period_length_classes[non_periodic_mask] = 0  # Assign to class 0 (period length 2)
    
    # period_length_pred shape: (batch_size, num_frames, num_classes)
    # period_length_classes shape: (batch_size, num_frames)
    batch_size, num_frames, num_classes = period_length_pred.shape
    period_loss = period_length_criterion(
        period_length_pred.view(-1, num_classes),  # Flatten to (batch_size * num_frames, num_classes)
        period_length_classes.view(-1)  # Flatten to (batch_size * num_frames,)
    )
    
    # Periodicity is a binary classification task (BCE loss)
    periodicity_pred_sigmoid = torch.sigmoid(periodicity_pred.squeeze(-1))
    periodicity_loss = F.binary_cross_entropy(periodicity_pred_sigmoid, periodicity_gt)

Evaluación¶

image

Inferencia¶

  • Conteo por frame $\frac{p_i}{l_i}$
  • Conteo $\sum_{i=1}^{n} \frac{p_i}{l_i}$

Evaluación multi-velocidad

  • Hace inferencia sobre 4 velocidades, usando strides sobre los frames
  • Se usa el que tenga el mejor score sobre la predicción del período.

Conclusiones¶

  • RepNet es una muestra de que problemas de computer vision que involucran videos con características temporales pueden resolverse con la arquitecura adecuada y las técnicas enseñadas.
  • Al trabajar sobre videos, es decir conjuntos de imágenes que tiene una secuencia, es importante la organización para el manejo adecuado de las dimensiones ya que errores en la dimensionalidad pueden ser dificiles de dar seguimiento.
  • Las convoluciones en 3D pueden ser muy beneficiosas y útiles para capturar las características temporales de las secuencias de frames que de otra forma no sería posible sobre las imagenes individuales.
  • La matriz de auto-similaridad es muy útil para capturar las relaciones entre todos los frames de un batch.
  • El uso de mecanismos de atención a través de transformers puede ser muy útil sobre comportamientos complejos en series.secuenciales en los que es relevante la posición.
  • Es posible que tener un mejor procesamiento de los datos sinteticos de mejores resultados.

Futuras mejoras potenciales¶

  • Volver a entrenar haciendo transfer learning de otro modelo base y descongelar algunas (o todas) capas durante el entrenamiento
  • Combinar con object detection para poder realizar repetition counting en multiples objetos en la imagen
  • Aplicar normalización adicional a los frames de entrada
  • Integrar detección de velocidad de los periodos del video como parte del modelo

Gracias!¶