# Caméras

LeRobot propose plusieurs options pour la capture vidéo :

| Classe             | Caméras prises en charge                   |
| ----------------- | ----------------------------------- |
| `OpenCVCamera`    | Téléphone, webcam intégrée au portable, webcam USB |
| `ZMQCamera`       | Caméras connectées au réseau           |
| `RealSenseCamera` | Intel RealSense (avec profondeur)        |
| `Reachy2Camera`   | Caméras du robot Reachy 2              |

> [!TIP]
> Pour les détails de compatibilité de `OpenCVCamera`, consultez [Vue d’ensemble de l’entrée/sortie vidéo avec OpenCV](https://docs.opencv.org/4.x/d0/da7/videoio_overview.html).

### Trouvez votre caméra

Chaque caméra nécessite un identifiant unique pour être instanciée, ce qui vous permet de distinguer plusieurs appareils connectés.

`OpenCVCamera` et `RealSenseCamera` prennent en charge la découverte automatique. Exécutez la commande ci-dessous pour lister les appareils disponibles et leurs identifiants. Notez que ces identifiants peuvent changer après un redémarrage de votre ordinateur ou un rebranchement de la caméra, selon votre système d’exploitation.

```bash
lerobot-find-cameras opencv # or realsense for Intel Realsense cameras
```

La sortie ressemblera à ceci si vous avez deux caméras connectées :

```bash
--- Detected Cameras ---
Camera #0:
  Name: OpenCV Camera @ 0
  Type: OpenCV
  Id: 0
  Backend api: AVFOUNDATION
  Default stream profile:
    Format: 16.0
    Width: 1920
    Height: 1080
    Fps: 15.0
--------------------
(more cameras ...)
```

> [!WARNING]
> Lors de l’utilisation de caméras Intel RealSense sous `macOS`, vous pouvez rencontrer cette [erreur](https://github.com/realsenseai/librealsense/issues/12307) : `Error finding RealSense cameras: failed to set power state`. Elle peut être résolue en exécutant la même commande avec les permissions `sudo`. Notez que l’utilisation de caméras RealSense sous `macOS` est instable.

`ZMQCamera` et `Reachy2Camera` ne prennent pas en charge la découverte automatique. Ils doivent être configurés manuellement en fournissant leur adresse réseau et leur port, ou les paramètres du SDK du robot.

## Placement de la caméra et éclairage

Les images de la caméra sont ce dont votre politique apprend, donc un bon réglage de la caméra est plus important qu'une haute résolution :

- **Éclairez la scène de manière uniforme.** Utilisez une lumière diffuse et constante, et évitez la lumière directe du soleil, les reflets intenses et les ombres mouvantes. Un éclairage différent entre l'enregistrement et le déploiement peut entraîner l'échec d'une politique.
- **Fixer les caméras en place.** Les monter de manière rigide et ne pas les déplacer entre les sessions d'enregistrement, ou entre l'enregistrement et le déploiement.
- **Formuler la tâche.** L'objet, la pince et la cible doivent être visibles dans au moins une caméra. Un bon test : pourriez-vous effectuer la tâche vous-même en ne regardant que les images des caméras ?
- **Gardez la résolution modeste.** 640×480 à 30 fps suffit pour la plupart des tâches et permet de maintenir l'enregistrement et l'entraînement rapides.

## Utiliser des caméras

### Modes d'accès aux images

Toutes les classes de caméra implémentent trois modes d'accès pour la capture d'images :

| Méthode                    | Comportement                                                                                                                                                   | Des blocs ?        | Idéal pour                                 |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ---------------------------------------- |
| `read()`                  | Attend que le matériel de la caméra renvoie une image. Peut bloquer pendant une longue durée en fonction de la caméra et du SDK.                                                | Oui            | Scripts simples, capture séquentielle       |
| `async_read(timeout_ms)`  | Retourne la dernière image non consommée depuis le thread en arrière-plan. N’attend que si le tampon est vide, jusqu’à `timeout_ms`. Lève `TimeoutError` si aucune image n’arrive. | Avec un délai d'attente | Boucles de contrôle synchronisées sur la fréquence d'images de la caméra |
| `read_latest(max_age_ms)` | Affiche le dernier frame du buffer (peut être obsolète). Lève `TimeoutError` si le frame est plus ancien que `max_age_ms`.                                        | Non             | Visualisation de l'interface utilisateur, journalisation, surveillance    |

### Exemples d'utilisation

Les exemples suivants montrent comment utiliser l’API caméra pour configurer et capturer des images à partir de différents types de caméras.

- **Capture de trames bloquante et non bloquante** à l'aide d'une caméra basée sur OpenCV
- **Capture de couleur et de profondeur** à l'aide d'une caméra Intel RealSense

> [!WARNING]
> Un débranchement non propre des caméras peut provoquer des fuites de ressources. Utilisez le protocole de gestionnaire de contexte pour garantir un nettoyage automatique :
>
> ```python
> with OpenCVCamera(config) as camera:
>     ...
> ```
>
> Vous pouvez également appeler `connect()` et `disconnect()` manuellement, mais utilisez toujours un bloc `finally` pour ce dernier.

```python
from lerobot.cameras.opencv import OpenCVCamera, OpenCVCameraConfig
from lerobot.cameras import ColorMode, Cv2Rotation

# Construct an `OpenCVCameraConfig` with your desired FPS, resolution, color mode, and rotation.
config = OpenCVCameraConfig(
    index_or_path=0,
    fps=15,
    width=1920,
    height=1080,
    color_mode=ColorMode.RGB,
    rotation=Cv2Rotation.NO_ROTATION
)

# Instantiate and connect an `OpenCVCamera`, performing a warm-up read (default).
with OpenCVCamera(config) as camera:

    # Read a frame synchronously — blocks until hardware delivers a new frame
    frame = camera.read()
    print(f"read() call returned frame with shape:", frame.shape)

    # Read a frame asynchronously with a timeout — returns the latest unconsumed frame or waits up to timeout_ms for a new one
    try:
        for i in range(10):
            frame = camera.async_read(timeout_ms=200)
            print(f"async_read call returned frame {i} with shape:", frame.shape)
    except TimeoutError as e:
        print(f"No frame received within timeout: {e}")

    # Instantly return a frame - returns the most recent frame captured by the camera
    try:
        initial_frame = camera.read_latest(max_age_ms=1000)
        for i in range(10):
            frame = camera.read_latest(max_age_ms=1000)
            print(f"read_latest call returned frame {i} with shape:", frame.shape)
            print(f"Was a new frame received by the camera? {not (initial_frame == frame).any()}")
    except TimeoutError as e:
        print(f"Frame too old: {e}")

```

```python
from lerobot.cameras.realsense import RealSenseCamera, RealSenseCameraConfig
from lerobot.cameras import ColorMode, Cv2Rotation

# Create a `RealSenseCameraConfig` specifying your camera’s serial number and enabling depth.
config = RealSenseCameraConfig(
    serial_number_or_name="233522074606",
    fps=15,
    width=640,
    height=480,
    color_mode=ColorMode.RGB,
    use_depth=True,
    # Optional fixed color controls. Omit them to leave the current sensor settings unchanged.
    exposure=120,
    gain=64,
    white_balance=4600,
    rotation=Cv2Rotation.NO_ROTATION
)

# Instantiate and connect a `RealSenseCamera` with warm-up read (default).
camera = RealSenseCamera(config)
camera.connect()

# Capture a color frame via `read()` and a depth map via `read_depth()`.
try:
    color_frame = camera.read()
    depth_map = camera.read_depth()
    print("Color frame shape:", color_frame.shape)
    print("Depth map shape:", depth_map.shape)
finally:
    camera.disconnect()
```

Les réglages manuels de couleur désactivent le mode d’exposition automatique ou d’équilibrage des blancs correspondant. Les plages de valeurs prises en charge varient selon le modèle d’appareil photo ; une valeur invalide provoque une erreur au moment de la connexion, incluant la plage signalée par le capteur. La demande d’un réglage non pris en charge provoque également une erreur. Les réglements omis laissent inchangé le réglage automatique ou manuel existant du capteur. Ces options nécessitent `use_rgb=True`.

Les réglages manuels de la couleur nécessitent un module RGB dédié. Les caméras qui n’en sont pas équipées, comme la RealSense D405, ne les prennent pas en charge et déclenchent une erreur lors de la connexion.

### Travailler avec la profondeur

Les caméras Intel RealSense et Reachy 2 peuvent capturer la couleur et la profondeur de manière synchronisée. L’appel de `read()` renvoie le **frame couleur** sous forme de `(H, W, 3)` `uint8`. L’appel de `read_depth()` renvoie la **carte de profondeur** sous forme de `(H, W, 1)` `uint16`, où chaque valeur de pixel représente la distance par rapport au capteur, exprimée en **millimètres**. Une valeur de pixel de `0` signifie généralement « aucune mesure disponible » (hors plage, occlusion ou faible confiance).

Pendant l’enregistrement, la boucle de contrôle consulte de manière non bloquante les images tamponnées les plus récentes via `read_latest()` (couleur) et `read_latest_depth()` (profondeur), en ajoutant la carte de profondeur comme fonctionnalité sœur (par exemple `front_depth` à côté de `front`).

Pour savoir comment les flux de profondeur sont stockés et encodés lors de l’enregistrement d’un jeu de données, consultez la section [Depth streams](./video_encoding_parameters#depth-streams) du guide d’encodage vidéo.

## Utilisez l'appareil photo de votre téléphone

Pour utiliser votre iPhone comme caméra sur macOS, activez la fonctionnalité Continuity Camera :

- Assurez-vous que votre Mac exécute macOS 13 ou une version ultérieure, et que votre iPhone est sur iOS 16 ou une version ultérieure.
- Connectez-vous aux deux appareils avec le même identifiant Apple.
- Connectez vos appareils avec un câble USB ou activez le Wi-Fi et le Bluetooth pour une connexion sans fil.

Pour plus de détails, consultez [Apple support](https://support.apple.com/en-gb/guide/mac-help/mchl77879b8a/mac).

Si vous souhaitez utiliser votre téléphone comme caméra avec OBS, suivez ces étapes pour configurer une caméra virtuelle.

1. _(Linux uniquement) Installez `v4l2loopback-dkms` et `v4l-utils`_. Ces paquets créent des périphériques de caméra virtuels et vérifient leurs paramètres. Installez-les avec :

```bash
sudo apt install v4l2loopback-dkms v4l-utils
```

2. _Installez l'[application DroidCam](https://droidcam.app) sur votre téléphone_. Cette application est disponible sur iOS et Android.
3. _Téléchargez et installez [OBS Studio](https://obsproject.com)_.
4. _Téléchargez et installez le [plugin DroidCam OBS](https://droidcam.app/obs)_.
5. _Démarrer OBS Studio_.

6. _Ajoutez votre téléphone en tant que source_. Suivez les instructions [ici](https://droidcam.app/obs/usage). Assurez-vous de définir la résolution sur `640x480` pour éviter les filigranes.
7. _Ajuster les paramètres de résolution_. Dans OBS Studio, allez dans `File > Settings > Video` ou `OBS > Preferences... > Video`. Modifiez la `Base(Canvas) Resolution` et la `Output(Scaled) Resolution` en `640x480` en les saisissant manuellement.
8. _Démarrer la caméra virtuelle_. Dans OBS Studio, suivez les instructions [ici](https://obsproject.com/kb/virtual-camera-guide).
9. _Vérifier la configuration de la caméra virtuelle et la résolution_.
   - **Linux** : utilisez `v4l2-ctl` pour lister les périphériques et vérifier la résolution :
     ```bash
     v4l2-ctl --list-devices  # find VirtualCam and note its /dev/videoX path
     v4l2-ctl -d /dev/videoX --get-fmt-video  # replace with your VirtualCam path
     ```
     Vous devriez voir `VirtualCam` répertorié et la résolution `640x480`.
   - **macOS** : ouvrez Photo Booth ou FaceTime et sélectionnez « OBS Virtual Camera » en tant qu’entrée.
   - **Windows** : l’application native Camera ne prend pas en charge les caméras virtuelles. Utilisez une application de visioconférence (Zoom, Teams) ou exécutez directement `lerobot-find-cameras opencv` pour vérifier.

Dépannage

> La résolution de la caméra virtuelle est incorrecte.

Supprimez la source de caméra virtuelle et recréez-la. La résolution ne peut pas être modifiée après la création.

> Erreur lors de la lecture de la trame dans le thread en arrière-plan pour OpenCVCamera(X) : la largeur=640 ou la hauteur=480 de la trame OpenCVCamera(X) ne correspond pas à la largeur=1920 ou à la hauteur=1080 configurées.

Cette erreur est causée par le fait que la caméra virtuelle OBS annonce une résolution `1920x1080` malgré le redimensionnement. La seule solution pour le moment est de commenter la vérification de la largeur et de la hauteur dans `_postprocess_image()`.

Si tout est correctement configuré, votre téléphone apparaîtra comme une caméra OpenCV standard et pourra être utilisé avec `OpenCVCamera`.

