Lire et traiter la vidéo: classe CameraServer
Concepts
The cameras typically used in FRC® (commodity USB and Ethernet cameras) offer relatively limited modes of operation. In general, they provide only a single image output (typically in an RGB compressed format such as JPG) at a single resolution and frame rate. USB cameras are particularly limited as only one application may access the camera at a time.
CameraServer peut prendre en charge plusieurs caméras. Il gère des détails tels que la reconnexion automatique lorsqu’une caméra est déconnectée momentanément, et met également les images de la caméra à la disposition de plusieurs « clients » (par exemple, votre code de robot et le tableau de bord peuvent lire les images de la caméra même simultanément).
Nommer les caméras
Each camera in CameraServer must be uniquely named. This is also the name that appears for the camera in the Dashboard. Some variants of the CameraServer startAutomaticCapture()
functions will automatically name the camera (e.g. « USB Camera 0 »), or you can give the camera a more descriptive name (e.g. « Intake Cam »). The only requirement is that each camera have a unique name.
Remarques sur la caméra USB
L’utilisation du processeur
Le CameraServer est conçu pour minimiser l’utilisation du processeur en effectuant uniquement des opérations de compression et de décompression lorsque cela est nécessaire et en désactivant automatiquement le streaming lorsqu’aucun client n’est connecté.
Pour minimiser l’utilisation du processeur, la résolution du tableau de bord doit être réglée sur la même résolution que la caméra; cela permet au CameraServer de ne pas décompresser et recompresser l’image, au lieu de cela, il peut simplement transmettre l’image JPEG reçue de la caméra directement au tableau de bord. Il est important de noter que la modification de la résolution sur le tableau de bord ne change pas la résolution de la caméra; changer la résolution de la caméra peut être fait en appelant setResolution()
sur l’objet caméra.
Bande passante USB
Le roboRIO ne peut transmettre et recevoir autant de données à la fois via ses interfaces USB. Les images de la caméra peuvent nécessiter beaucoup de données, et il est donc relativement facile de dépasser cette limite. La cause plus fréquente d’une erreur de bande passante USB est le choix d’un mode vidéo non JPEG ou une résolution trop élevée, en particulier lorsque plusieurs caméras sont connectées.
Architecture
Le CameraServer se compose de deux couches, la classe haut-niveau WPILib CameraServer class et la librairie de bas-niveau cscore library.
Classe CameraServer
La classe CameraServer (qui fait partie de WPILib) fournit une interface de haut niveau pour ajouter des caméras à votre code de robot. Elle est également responsable de la publication d’informations sur les caméras et les serveurs de caméras sur NetworkTables afin que les tableaux de bord de Driver Station tels que LabVIEW Dashboard et Shuffleboard puissent répertorier les caméras et déterminer l’emplacement de leurs flux. La classe utilise un modèle de type « singleton » (possédant un seul objet à la fois) pour maintenir une base de données de toutes les caméras et tous les serveurs créés.
Certaines fonctions importantes de CameraServer:
startAutomaticCapture()
: ajoute une caméra USB (par exemple Microsoft LifeCam) et lance un serveur pour qu’elle puisse être visualisée à partir du tableau de bord.getVideo()
: Obtenez l’accès OpenCV à une caméra. Cela vous permet d’obtenir des images de la caméra pour le traitement d’images sur le roboRIO (dans votre code de robot).putVideo()
: démarrez un serveur sur lequel vous pouvez fournir des OpenCV à ce serveur. Cela vous permet de passer des images traitées et/ou annotées personnalisées au tableau de bord.
Librairie cscore
La librairie cscore fournit l’implémentation de niveau bas pour:
Get images from USB and HTTP cameras
Modifier les paramètres de l’appareil photo (par exemple le contraste et la luminosité)
Changer les modes vidéo de la caméra (format pixel, résolution et fréquence d’images)
Agir en tant que serveur Web et fournir des images en tant que flux MJPEG standard
Convertir des images vers/depuis des objets
Mat
OpenCV pour le traitement d’images
Sources et Récepteurs
L’architecture de base de la bibliothèque cscore est similaire à celle de MJPGStreamer, avec des fonctionnalités réparties entre les sources et les récepteurs. Il peut y avoir plusieurs Sources et plusieurs Récepteurs créés et fonctionnant simultanément.
Un objet qui génère des images est une Source et un objet qui accepte/consomme des images est un Récepteur. La génération/consommation est du point de vue de la librairie. Les caméras sont donc des Sources (elles génèrent des images). Le serveur Web MJPEG est un Récepteur, car il accepte des images à partir du programme (même s’il peut transférer ces images vers un navigateur Web ou un tableau de bord). Les Sources peuvent être connectées à plusieurs Récepteurs, mais les Récepteurs peuvent être connectés à une seule et unique Source. Lorsqu’un Récepteur est connecté à une Source, la librairie cscore se charge de transmettre chaque image de la Source au Récepteur.
Les Sources obtiennent des images individuelles (comme celles fournies par une caméra USB) et déclenchent un événement lorsqu’une nouvelle image est disponible. Si aucun Récepteur n’écoute une Source particulière, la librairie peut s’arrêter ou se déconnecter d’une Source pour économiser le processeur et les ressources d’Entrées/Sorties. La librairie gère de manière autonome les déconnexions/reconnexions de la caméra en interrompant simplement et en reprenant le déclenchement des événements (par exemple, une déconnexion arrête l’envoie de nouvelles trames, mais ne génère pas d’erreur).
Les Récepteurs écoutent l’événement d’une Source particulière, récupérent la dernière image et la transfère à sa destination dans le format approprié. De même que pour les Sources, si un Récepteur particulier est inactif (par exemple, aucun client n’est connecté à un serveur MJPEG configuré sur HTTP), la librairie peut désactiver des parties de son traitement pour économiser les ressources du processeur.
Le code utilisateur (tel que celui utilisé dans un programme de robot FRC) peut agir soit comme Source (fournissant des images traitées comme s’il s’agissait d’une caméra), soit comme Récepteur (recevant une image pour traitement) via les objets Source et Récepteur OpenCV. Ainsi, un pipeline de traitement d’image qui récupère les images d’une caméra et sert les images traitées ressemble au graphique ci-dessous:
Étant donné que les Sources peuvent avoir plusieurs Récepteurs connectés, le pipeline peut se ramifier. Par exemple, l’image d’origine de la caméra peut également être servie en connectant la Source UsbCamera à un deuxième Récepteur MjpegServer en plus du récepteur CvSink, ce qui donne le graphique ci-dessous:
Lorsqu’une nouvelle image est capturée par la caméra, le CvSink et le MjpegServer [1] la reçoivent.
Le graphique ci-dessus est crée par le bout de code CameraServer ci-dessous:
import edu.wpi.first.cameraserver.CameraServer;
import edu.wpi.cscore.CvSink;
import edu.wpi.cscore.CvSource;
// Creates UsbCamera and MjpegServer [1] and connects them
CameraServer.startAutomaticCapture();
// Creates the CvSink and connects it to the UsbCamera
CvSink cvSink = CameraServer.getVideo();
// Creates the CvSource and MjpegServer [2] and connects them
CvSource outputStream = CameraServer.putVideo("Blur", 640, 480);
#include "cameraserver/CameraServer.h"
// Creates UsbCamera and MjpegServer [1] and connects them
frc::CameraServer::StartAutomaticCapture();
// Creates the CvSink and connects it to the UsbCamera
cs::CvSink cvSink = frc::CameraServer::GetVideo();
// Creates the CvSource and MjpegServer [2] and connects them
cs::CvSource outputStream = frc::CameraServer::PutVideo("Blur", 640, 480);
L’implémentation de CameraServer effectue les opérations suivantes au niveau cscore: CameraServer prend en charge de nombreux détails tels que la création de noms uniques pour tous les objets cscore et la sélection automatique des numéros de port. CameraServer conserve également un registre de type « singleton » des objets créés afin qu’ils ne soient pas détruits s’ils sortent de leur cadre, ou champ.
import edu.wpi.cscore.CvSink;
import edu.wpi.cscore.CvSource;
import edu.wpi.cscore.MjpegServer;
import edu.wpi.cscore.UsbCamera;
// Creates UsbCamera and MjpegServer [1] and connects them
UsbCamera usbCamera = new UsbCamera("USB Camera 0", 0);
MjpegServer mjpegServer1 = new MjpegServer("serve_USB Camera 0", 1181);
mjpegServer1.setSource(usbCamera);
// Creates the CvSink and connects it to the UsbCamera
CvSink cvSink = new CvSink("opencv_USB Camera 0");
cvSink.setSource(usbCamera);
// Creates the CvSource and MjpegServer [2] and connects them
CvSource outputStream = new CvSource("Blur", PixelFormat.kMJPEG, 640, 480, 30);
MjpegServer mjpegServer2 = new MjpegServer("serve_Blur", 1182);
mjpegServer2.setSource(outputStream);
#include "cscore_oo.h"
// Creates UsbCamera and MjpegServer [1] and connects them
cs::UsbCamera usbCamera("USB Camera 0", 0);
cs::MjpegServer mjpegServer1("serve_USB Camera 0", 1181);
mjpegServer1.SetSource(usbCamera);
// Creates the CvSink and connects it to the UsbCamera
cs::CvSink cvSink("opencv_USB Camera 0");
cvSink.SetSource(usbCamera);
// Creates the CvSource and MjpegServer [2] and connects them
cs::CvSource outputStream("Blur", cs::PixelFormat::kMJPEG, 640, 480, 30);
cs::MjpegServer mjpegServer2("serve_Blur", 1182);
mjpegServer2.SetSource(outputStream);
Comptage de références
Tous les objets cscore sont comptés en interne. La connexion d’un Récepteur à une Source augmente le compte de références de la Source, il est donc strictement nécessaire de garder le Récepteur dans sa portée. La classe CameraServer conserve un registre de tous les objets créés avec les fonctions CameraServer, de sorte que les Sources et les Récepteurs créés de cette manière ne sortent jamais de leur portée (sauf s’ils sont explicitement supprimés).