# Parâmetros de codificação de vídeo

Quando o armazenamento em vídeo está habilitado, o LeRobot armazena cada fluxo de câmera como um arquivo **MP4** em vez de salvar um arquivo de imagem por passo de tempo. A codificação de vídeo comprime ao longo do tempo, o que geralmente reduz o tamanho do conjunto de dados e a entrada/saída em comparação com uma pilha de PNG, mantendo o MP4 — um formato que todos os players e loaders entendem.

Codificar frames em um MP4 é um pipeline completo do FFmpeg: escolha do codificador, formato de pixel, GOP/keyframes, qualidade versus velocidade e flags extras opcionais do codificador. A maioria desses parâmetros é ajustável pelo usuário por meio de `rgb_encoder`, um `RGBEncoderConfig` aninhado (`lerobot.configs.video.RGBEncoderConfig`) passado por meio do PyAV.

Você pode definir esses parâmetros pela CLI com `--dataset.rgb_encoder.<field>` (por exemplo, com `lerobot-record` ou `lerobot-rollout`). O mesmo bloco se aplica a todos os fluxos de vídeo de câmera naquela execução.

> [!TIP]
> O armazenamento de vídeo deve estar ativado para que `rgb_encoder` tenha algum efeito — `use_videos=True` nas APIs Python, ou `--dataset.video=true` na CLI (o padrão de gravação). Com o vídeo desativado, as entradas permanecem como imagens e `rgb_encoder` é ignorado.

Para detalhes sobre **quando** os frames são gravados versus codificados (streaming versus pós-episódio), filas e outros switches de nível superior `--dataset.*`, consulte [Streaming Video Encoding](./streaming_video_encoding). Para uma comparação de parâmetros de codificação e experimentos, consulte o [video-benchmark Space](https://huggingface.co/spaces/lerobot/video-benchmark).

---

## Exemplo

```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]
> Os padrões são ajustados para equilibrar **taxa de compressão**, **qualidade visual** e **velocidade de decodificação/seek** para conjuntos de dados de robótica típicos. Alterá-los pode afetar tanto a gravação (carga de CPU, quedas de quadros) quanto o treinamento (vazão de decodificação, qualidade das imagens).
>
> Sobrescreva esses parâmetros apenas se tiver um motivo específico para isso e meça o impacto no seu pipeline antes de confiar nas novas configurações.

Todas as flags abaixo são prefixadas com `--dataset.rgb_encoder.` na CLI.

| Parâmetro       | Tipo             | Padrão       | Descrição                                                                                                                                                                                                             |
| --------------- | ---------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vcodec`        | `str`            | `"libsvtav1"` | Nome do codec de vídeo. `"auto"` seleciona o primeiro codificador de hardware disponível a partir de uma lista de preferências fixa, com fallback para `libsvtav1`.                                                                                        |
| `pix_fmt`       | `str`            | `"yuv420p"`   | Formato de pixel de saída. Deve ser suportado pelo codec escolhido na sua compilação do FFmpeg.                                                                                                                                        |
| `g`             | `int`            | `2`           | Tamanho do GOP — uma keyframe a cada `g` quadros. Emitido como opção do FFmpeg `g`.                                                                                                                                                   |
| `crf`           | `int` ou `float` | `30`          | Valor de qualidade abstrato, mapeado por codec (veja o [mapeamento](https://github.com/huggingface/lerobot/blob/main/src/lerobot/configs/video.py#L197)). Menor → maior qualidade / saída maior onde o mapeamento é monotônico. |
| `preset`        | `int` ou `str`   | `12` \*       | Preset de velocidade do codificador; o significado depende do codec. \* Quando não definido e `vcodec=libsvtav1`, o LeRobot usa `12` por padrão.                                                                                                |
| `fast_decode`   | `int`            | `0`           | `libsvtav1`: `0–2`, passado via `svtav1-params`. `h264` / `hevc` (software): se `>0`, define `tune=fastdecode`. Outros codecs: geralmente não utilizado.                                                                    |
| `video_backend` | `str`            | `"pyav"`      | Atualmente, apenas `"pyav"` está implementado para codificação de vídeo.                                                                                                                                                              |
| `extra_options` | `dict`           | `{}`          | Opções específicas do FFmpeg ou do codec mescladas após os campos estruturados acima. Não é possível sobrescrever chaves já definidas por esses campos.                                                                                      |

---

## Streams de profundidade

Mapas de profundidade (Intel RealSense, Reachy 2) são armazenados como seus **próprios fluxos de vídeo** ao lado dos fluxos RGB. A profundidade bruta (`uint16` em milímetros ou `float32` em metros) não pode sobreviver a um codec de 8 bits, portanto o LeRobot **quantiza** cada mapa para um código de 12 bits (`[0, 4095]`) — logaritmicamente por padrão, para corresponder ao perfil de erro `1/depth` dos sensores de profundidade — e, em seguida, o empacota em um formato de pixel de alta profundidade de bits (`gray12le`) e o codifica com um codec de 12 bits.

  
    
      Profundidade bruta
      
        uint16 mm
        
        float32 m
      
    
    
      →
    
    
      
        Tempo recorde
      
      
        Clip
        
          para [depth_min,
          
          depth_max]
        
      
      
        →
      
      
        Quantizar
        
          Códigos de 12 bits 0–4095
          
          log (default) ou linear
        
      
      
        →
      
      
        Pacote
        
          para gray12le
          
          plane
        
      
      
        →
      
      
        Codificar
        
          HEVC
          
          Principal 12
        
      
    
    
      →
    
    
      MP4
      
        armazenado
        
        stream
      
    
    
      →
    
    
      
        Tempo de carregamento
      
      
        Desquantizar
        
          para mm / m
        
      
    
  

Configure o pipeline de profundidade por meio de um bloco **`depth_encoder`** paralelo (`DepthEncoderConfig`). Ele compartilha todos os campos de `RGBEncoderConfig` (`vcodec`, `pix_fmt`, `crf`, …) e adiciona quatro parâmetros de quantizador, definidos via `--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    | Padrão                         | Descrição                                                                                                                            |
| --------------- | ------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `vcodec`        | `str`   | `"hevc"`                        | HEVC Main 12 (um codec compatível com 12 bits, compatível com MP4).                                                                                 |
| `extra_options` | `dict`  | `{"x265-params": "lossless=1"}` | **A profundidade padrão é sem perdas** (ida e volta exata); `crf` é ignorado. Passe `extra_options={}` e defina `crf` para um stream com perdas menor. |
| `pix_fmt`       | `str`   | `"gray12le"`                    | Formato de pixel de 12 bits em canal único usado para transportar os códigos quantizados.                                                                  |
| `depth_min`     | `float` | `0.01`                          | Profundidade em metros mapeada para o quântico `0`. Os valores abaixo são limitados na decodificação.                                                             |
| `depth_max`     | `float` | `10.0`                          | Profundidade em metros mapeada para quântico `4095`. Valores acima são limitados na decodificação.                                                          |
| `shift`         | `float` | `3.5`                           | Deslocamento pré-log (em metros) usado na quantização logarítmica para estabilidade numérica perto de zero. Deve satisfazer `depth_min + shift > 0`.      |
| `use_log`       | `bool`  | `True`                          | Se `true`, quantiza no espaço logarítmico (recomendado para sensores de profundidade típicos). Defina como `false` para quantização uniforme/linear.              |

> [!TIP]
> `depth_min`, `depth_max` e `shift` são sempre interpretados em **metros**, independentemente da unidade da profundidade de entrada. As entradas são detectadas automaticamente: arrays inteiros (por exemplo, `uint16` em milímetros direto de um RealSense) são tratados como milímetros, e arrays de ponto flutuante como metros. Escolha `depth_min` / `depth_max` para delimitar a faixa de trabalho real do seu sensor — quanta fora dessa faixa saturam, o que pode comprimir detalhes nas bordas.

Os recursos de profundidade são marcados com `"is_depth_map": true` em `meta/info.json`, e as configurações do quantizador deles (`video.depth_min`, `video.depth_max`, `video.shift`, `video.use_log`) são persistidas — o que permite que a profundidade seja **desquantizada de volta para unidades físicas** no carregamento.

### Unidade de saída no momento do carregamento

`depth_encoder` é uma preocupação de **tempo de gravação**. A unidade para a qual os mapas de profundidade são desquantizados no _carregamento_ (por exemplo, durante o treinamento) é definida separadamente pela flag de tempo de leitura `--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  | Padrão | Descrição                                                                                  |
| ------------------- | ----- | ------- | -------------------------------------------------------------------------------------------- |
| `depth_output_unit` | `str` | `"mm"`  | Os mapas de profundidade de unidades físicas são desquantizados para, ao carregar: `"mm"` (milímetros) ou `"m"` (metros). |

> [!TIP]
> Essa é uma escolha de apresentação apenas no momento da decodificação — ela **não** altera o vídeo armazenado nem seus metadados, de modo que o mesmo conjunto de dados pode ser lido como `mm` ou `m` sem re-codificação. Isso não afeta conjuntos de dados sem câmeras de profundidade.

---

## Persistência nos metadados do conjunto de dados

Após o primeiro episódio de um fluxo de vídeo ser codificado, a configuração do codificador é **persistida nos metadados do conjunto de dados** (`meta/info.json`) sob cada recurso de vídeo, junto com os valores obtidos do próprio arquivo. Para um recurso de vídeo `observation.images.<camera>`, o layout em `info.json` é:

```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 }
      }
    }
  }
}
```

Duas fontes contribuem para o bloco `info`:

| Fonte              | De onde vem                                   | Campos                                                                                                                  |
| ------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| **Derivado de stream**  | Leia de volta do MP4 codificado com PyAV.             | `video.height`, `video.width`, `video.codec`, `video.pix_fmt`, `video.fps`, `video.channels`, `is_depth_map`, `audio.*` |
| **Derivado do codificador** | Retirado de `RGBEncoderConfig` / `DepthEncoderConfig`. | `video.g`, `video.crf`, `video.preset`, `video.fast_decode`, `video.video_backend`, `video.extra_options`               |

> [!IMPORTANT]
> Este bloco é preenchido **uma única vez**, a partir do **primeiro** episódio. Ele pressupõe que todos os episódios no conjunto de dados foram codificados com o mesmo `rgb_encoder`. Alterar as configurações do codificador no meio de uma gravação não é suportado — o `info.json` refletirá apenas os parâmetros usados para o primeiro episódio.

---

## Mesclando conjuntos de dados

Ao agregar conjuntos de dados com `merge_datasets`, os arquivos de vídeo são concatenados como estão (sem re-codificação) e os campos do codificador em `info.json` são mesclados por chave:

| Regra de mesclagem         | Campos                                                                             | Comportamento                                                                                                                                                 |
| ------------------ | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Deve corresponder**     | `video.codec`, `video.pix_fmt`, `video.height`, `video.width`, `video.fps`         | Os campos derivados do stream devem corresponder entre as fontes; caso contrário, o demuxer concat do FFmpeg falha.                                                                 |
| **Mesclados de forma frouxa** | `video.g`, `video.crf`, `video.preset`, `video.fast_decode`, `video.extra_options` | Campos de ajuste do codificador. Se todas as fontes concordarem, o valor é mantido; caso contrário, ele é definido como `null` (ou `{}` para `video.extra_options`) e um aviso é registrado. |

