# Parámetros de codificación de video

Cuando el almacenamiento de video está habilitado, LeRobot guarda cada flujo de cámara como un archivo **MP4** en lugar de guardar un archivo de imagen por paso de tiempo. La codificación de video comprime a lo largo del tiempo, lo que generalmente reduce el tamaño del conjunto de datos y la E/S en comparación con una pila de PNG, manteniendo MP4, un formato que todos los reproductores y cargadores entienden.

Codificar fotogramas en un MP4 es un pipeline completo de FFmpeg: elección del codificador, formato de píxeles, GOP/keyframes, calidad frente a velocidad y banderas adicionales opcionales del codificador. La mayoría de estos parámetros son ajustables por el usuario a través de `rgb_encoder`, una `RGBEncoderConfig` anidada (`lerobot.configs.video.RGBEncoderConfig`) que se pasa a través de PyAV.

Puede establecer estos parámetros desde la CLI con `--dataset.rgb_encoder.<field>` (por ejemplo, con `lerobot-record` o `lerobot-rollout`). El mismo bloque se aplica a cada flujo de video de cámara en esa ejecución.

> [!TIP]
> El almacenamiento de video debe estar activado para que `rgb_encoder` tenga algún efecto: `use_videos=True` en las APIs de Python, o `--dataset.video=true` en la CLI (el valor predeterminado de grabación). Con el video desactivado, las entradas se mantienen como imágenes y `rgb_encoder` se ignora.

Para más detalles sobre **cuándo** se escriben los fotogramas frente a cuando se codifican (transmisión en vivo frente a posterior al episodio), las colas y otros interruptores de nivel superior `--dataset.*`, consulte [Codificación de video en streaming](./streaming_video_encoding). Para una comparación de parámetros de codificación y experimentos, consulte el [Espacio video-benchmark](https://huggingface.co/spaces/lerobot/video-benchmark).

---

## Ejemplo

```bash
lerobot-record \
    --robot.type=so100_follower \
    --robot.port=/dev/tty.usbmodem58760431541 \
    --robot.cameras="{laptop: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
    --robot.id=black \
    --teleop.type=so100_leader \
    --teleop.port=/dev/tty.usbmodem58760431551 \
    --teleop.id=blue \
    --dataset.repo_id=<my_username>/<my_dataset_name> \
    --dataset.num_episodes=2 \
    --dataset.single_task="Grab the cube" \
    --dataset.streaming_encoding=true \
    --dataset.encoder_threads=2 \
    --dataset.rgb_encoder.vcodec=h264 \
    --dataset.rgb_encoder.preset=fast \
    --dataset.rgb_encoder.extra_options='{"tune": "film", "profile:v": "high", "bf": 2}' \
    --display_data=true
```

---

## Parámetros de ajuste

> [!WARNING]
> Los valores predeterminados están ajustados para equilibrar la **relación de compresión**, la **calidad visual** y la **velocidad de decodificación/búsqueda** en conjuntos de datos de robótica típicos. Cambiarlos puede afectar tanto la grabación (carga de CPU, pérdida de fotogramas) como el entrenamiento (rendimiento de decodificación, calidad de imagen).
>
> Sobrescribe estos parámetros solo si tienes una razón específica para hacerlo y mide el impacto en tu pipeline antes de confiar en la nueva configuración.

Todas las banderas a continuación tienen el prefijo `--dataset.rgb_encoder.` en la CLI.

| Parámetro       | Tipo             | Predeterminado       | Descripción                                                                                                                                                                                                             |
| --------------- | ---------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vcodec`        | `str`            | `"libsvtav1"` | Nombre del códec de vídeo. `"auto"` selecciona el primer codificador de hardware disponible de una lista de preferencias fija, con `libsvtav1` como alternativa.                                                                                        |
| `pix_fmt`       | `str`            | `"yuv420p"`   | Formato de píxeles de salida. Debe ser compatible con el códec elegido en tu compilación de FFmpeg.                                                                                                                                        |
| `g`             | `int`            | `2`           | Tamaño de GOP — un fotograma clave cada `g` fotogramas. Se emite como opción de FFmpeg `g`.                                                                                                                                                   |
| `crf`           | `int` o `float` | `30`          | Valor de calidad abstracto, mapeado por códec (ver el [mapeo](https://github.com/huggingface/lerobot/blob/main/src/lerobot/configs/video.py#L197)). Menor → mayor calidad / salida más grande donde el mapeo es monotono. |
| `preset`        | `int` o `str`   | `12` \*       | Preajuste de velocidad del codificador; el significado depende del códec. \* Cuando no se establece y `vcodec=libsvtav1`, LeRobot usa `12` de forma predeterminada.                                                                                                |
| `fast_decode`   | `int`            | `0`           | `libsvtav1`: `0–2`, se pasa mediante `svtav1-params`. `h264` / `hevc` (software): si `>0`, establece `tune=fastdecode`. Otros códecs: generalmente no se usa.                                                                    |
| `video_backend` | `str`            | `"pyav"`      | Actualmente, solo `"pyav"` está implementado para la codificación de video.                                                                                                                                                              |
| `extra_options` | `dict`           | `{}`          | Opciones específicas de FFmpeg o del códec fusionadas después de los campos estructurados anteriores. No pueden sobrescribir las claves ya establecidas por esos campos.                                                                                      |

---

## Streams de profundidad

Los mapas de profundidad (Intel RealSense, Reachy 2) se almacenan como **flujos de video propios** junto a los flujos RGB. La profundidad en bruto (`uint16` milímetros o `float32` metros) no puede sobrevivir a un códec de 8 bits, por lo que LeRobot **cuantiza** cada mapa a un código de 12 bits (`[0, 4095]`) — de forma logarítmica por defecto, para coincidir con el perfil de error `1/depth` de los sensores de profundidad — y luego lo empaqueta en un formato de píxel de alta profundidad de bits (`gray12le`) y lo codifica con un códec de 12 bits.

  
    
      Profundidad en bruto
      
        uint16 mm
        
        float32 m
      
    
    
      →
    
    
      
        Tiempo de registro
      
      
        Clip
        
          a [depth_min,
          
          depth_max]
        
      
      
        →
      
      
        Cuantizar
        
          Códigos de 12 bits 0–4095
          
          log (predeterminado) o lineal
        
      
      
        →
      
      
        Pack
        
          a gray12le
          
          plane
        
      
      
        →
      
      
        Codificar
        
          HEVC
          
          Principal 12
        
      
    
    
      →
    
    
      MP4
      
        almacenado
        
        stream
      
    
    
      →
    
    
      
        Tiempo de carga
      
      
        Descomprimir
        
          a mm / m
        
      
    
  

Configure el pipeline de profundidad mediante un bloque **`depth_encoder`** paralelo (`DepthEncoderConfig`). Comparte todos los campos de `RGBEncoderConfig` (`vcodec`, `pix_fmt`, `crf`, …) y añade cuatro parámetros de cuantizador, configurados mediante `--dataset.depth_encoder.<field>`:

```bash
lerobot-record \
    ... \
    --dataset.depth_encoder.vcodec=hevc \
    --dataset.depth_encoder.depth_min=0.05 \
    --dataset.depth_encoder.depth_max=5.0 \
    --dataset.depth_encoder.use_log=true
```

| Parámetro       | Tipo    | Predeterminado                         | Descripción                                                                                                                            |
| --------------- | ------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `vcodec`        | `str`   | `"hevc"`                        | HEVC Main 12 (un códec con capacidad de 12 bits, compatible con MP4).                                                                                 |
| `extra_options` | `dict`  | `{"x265-params": "lossless=1"}` | **La profundidad predeterminada es sin pérdida** (recorrido de ida y vuelta exacto); `crf` se ignora. Pasa `extra_options={}` y establece `crf` para obtener un flujo con pérdida más pequeño. |
| `pix_fmt`       | `str`   | `"gray12le"`                    | Formato de píxel de 12 bits de un solo canal utilizado para transportar los códigos cuantizados.                                                                  |
| `depth_min`     | `float` | `0.01`                          | Profundidad en metros mapeada al cuántico `0`. Los valores inferiores se recortan en la decodificación.                                                             |
| `depth_max`     | `float` | `10.0`                          | Profundidad en metros mapeada a cuántico `4095`. Los valores superiores se recortan en la decodificación.                                                          |
| `shift`         | `float` | `3.5`                           | Desplazamiento previo al registro (en metros) utilizado en la cuantización logarítmica para la estabilidad numérica cerca de cero. Debe cumplir `depth_min + shift > 0`.      |
| `use_log`       | `bool`  | `True`                          | Si `true`, cuantiza en el espacio logarítmico (recomendado para sensores de profundidad típicos). Establezca `false` para una cuantización uniforme/lineal.              |

> [!TIP]
> `depth_min`, `depth_max` y `shift` se interpretan siempre en **metros**, independientemente de la unidad de la profundidad de entrada. Las entradas se detectan automáticamente: los arrays de enteros (p. ej., milímetros `uint16` directamente de un RealSense) se tratan como milímetros, y los arrays de punto flotante como metros. Elige `depth_min` / `depth_max` para acotar el rango de trabajo real de tu sensor: los valores fuera de ese rango se saturan, lo que puede comprimir el detalle en los límites.

Las características de profundidad se marcan con `"is_depth_map": true` en `meta/info.json`, y sus ajustes de cuantizador (`video.depth_min`, `video.depth_max`, `video.shift`, `video.use_log`) se persisten, lo que permite que la profundidad se **descuantice de vuelta a unidades físicas** al cargar.

### Unidad de salida en el momento de la carga

`depth_encoder` es una cuestión de **tiempo de registro**. La unidad a la que se descuantizan los mapas de profundidad al _cargarlos_ (p. ej., durante el entrenamiento) se establece por separado mediante la bandera de tiempo de lectura `--dataset.depth_output_unit`:

```bash
lerobot-train \
    --dataset.repo_id=<my_username>/<my_dataset_name> \
    --dataset.depth_output_unit=m \
    --policy.type=act
```

| Parámetro           | Tipo  | Predeterminado | Descripción                                                                                  |
| ------------------- | ----- | ------- | -------------------------------------------------------------------------------------------- |
| `depth_output_unit` | `str` | `"mm"`  | Los mapas de profundidad de unidades físicas se descuantizan al cargar: `"mm"` (milímetros) o `"m"` (metros). |

> [!TIP]
> Esta es una elección de presentación puramente en el momento de decodificación: no **altera** el video almacenado ni su metadatos, por lo que el mismo conjunto de datos puede leerse como `mm` o `m` sin volver a codificar. No tiene efecto en conjuntos de datos sin cámaras de profundidad.

---

## Persistencia en los metadatos del conjunto de datos

Tras codificar el primer episodio de un flujo de video, la configuración del codificador se **persiste en los metadatos del conjunto de datos** (`meta/info.json`) bajo cada característica de video, junto con los valores obtenidos del propio archivo. Para una característica de video `observation.images.<camera>`, el esquema en `info.json` es:

```json
{
  "features": {
    "observation.images.laptop": {
      "dtype": "video",
      "shape": [480, 640, 3],
      "info": {
        "video.height": 480,
        "video.width": 640,
        "video.codec": "h264",
        "video.pix_fmt": "yuv420p",
        "video.fps": 30,
        "video.channels": 3,
        "is_depth_map": false,
        "video.g": 2,
        "video.crf": 30,
        "video.preset": "fast",
        "video.fast_decode": 0,
        "video.video_backend": "pyav",
        "video.extra_options": { "tune": "film", "profile:v": "high", "bf": 2 }
      }
    }
  }
}
```

Dos fuentes contribuyen al bloque `info`:

| Fuente              | De dónde proviene                                   | Campos                                                                                                                  |
| ------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| **Derivado de stream**  | Leer de nuevo desde el MP4 codificado con PyAV.             | `video.height`, `video.width`, `video.codec`, `video.pix_fmt`, `video.fps`, `video.channels`, `is_depth_map`, `audio.*` |
| **Derivado del codificador** | Tomado de `RGBEncoderConfig` / `DepthEncoderConfig`. | `video.g`, `video.crf`, `video.preset`, `video.fast_decode`, `video.video_backend`, `video.extra_options`               |

> [!IMPORTANT]
> Este bloque se rellena **una sola vez**, a partir del **primer** episodio. Asume que todos los episodios del conjunto de datos se codificaron con el mismo `rgb_encoder`. No se admite cambiar la configuración del codificador a mitad de la grabación: el `info.json` solo reflejará los parámetros utilizados para el primer episodio.

---

## Combinar conjuntos de datos

Al agregar conjuntos de datos con `merge_datasets`, los archivos de video se concatenan tal cual (sin recodificación) y los campos del codificador en `info.json` se fusionan por clave:

| Regla de fusión         | Campos                                                                             | Comportamiento                                                                                                                                                 |
| ------------------ | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Debe coincidir**     | `video.codec`, `video.pix_fmt`, `video.height`, `video.width`, `video.fps`         | Los campos derivados de los streams deben coincidir entre las fuentes; de lo contrario, el demuxer de concatenación de FFmpeg fallará.                                                                 |
| **Fusionado de forma laxa** | `video.g`, `video.crf`, `video.preset`, `video.fast_decode`, `video.extra_options` | Campos de ajuste del codificador. Si todas las fuentes coinciden, se conserva el valor; si no, se establece en `null` (o `{}` para `video.extra_options`) y se registra una advertencia. |

