读取和处理视频:CameraServer类

概念

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支持多个摄像机。它处理诸如断开摄像机连接时自动重新连接之类的细节,并使来自摄像机的图像可供多个“客户端”使用(例如,您的机器人代码和仪表板都可以同时连接到摄像机)。

相机名称

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.

USB相机注意事项

CPU使用率

CameraServer旨在通过仅在需要时执行压缩和解压缩操作,并在未连接任何客户端时自动禁用流传输来最大程度地减少CPU使用率。

为了最大程度地减少CPU使用率,仪表板分辨率应设置为与摄像头相同的分辨率。这使CameraServer无需解压缩和重新压缩图像,而是可以简单地将从照相机接收的JPEG图像直接转发到仪表板。请务必注意,更改仪表板上的分辨率不会更改相机分辨率;可以通过在相机对象上调用setResolution()来更改相机分辨率。

USB带宽

roboRIO一次只能通过其USB接口发送和接收大量数据。相机图像可能需要大量数据,因此相对容易遇到此限制。 USB带宽错误的最常见原因是选择非JPEG视频模式或分辨率过高,尤其是在连接多台摄像机时。

体系结构

CameraServer由两层组成,高层WPILib ** CameraServer类**和底层** cscore库**。

CameraServer类

CameraServer类(WPILib的一部分)提供了一个高级接口,用于将摄像机添加到您的机器人代码中。它还负责将有关摄像机和摄像机服务器的信息发布到NetworkTables,以便LabVIEW Dashboard和Shuffleboard等Driver Station仪表板可以列出摄像机并确定其流的位置。它使用单例模式来维护所有创建的摄像机和服务器的数据库。

CameraServer中的一些关键功能包括:

  • startAutomaticCapture():添加一个USB摄像头(例如Microsoft LifeCam)并为其启动服务器,以便可以从仪表板上对其进行查看。

  • getVideo():获取对摄像机的OpenCV访问。这使您可以从相机获取图像以在roboRIO上进行图像处理(在您的机器人代码中)。

  • putVideo():启动一个服务器,您可以向其中提供OpenCV图像。这使您可以将自定义处理和/或带注释的图像传递到仪表板。

cscore库

cscore库提供了较低级别的实现:

  • Get images from USB and HTTP cameras

  • 更改相机设置(例如对比度和亮度)

  • 更改相机视频模式(像素格式,分辨率和帧频)

  • 充当Web服务器并将图像作为标准MJPEG流提供

  • 将图像与OpenCV``Mat’’对象之间来回转换以进行图像处理

来源和接收

cscore库的基本体系结构与MJPGStreamer相似,功能在源和接收器之间分配。可以创建多个源并同时创建多个接收器。

生成图像的对象是源,接受/使用图像的对象是接收器。生成/使用是从库的角度进行的。因此,相机是来源(它们生成图像)。 MJPEG Web服务器是一个接收器,因为它接受程序中的图像(即使它可能会将这些图像转发到Web浏览器或仪表板上)。源可以连接到多个接收器,但是接收器可以连接到一个且仅一个源。当接收器连接到源时,cscore库会负责将每个映像从源传递到接收器。

  • **来源**获取单独的帧(例如USB摄像头提供的帧),并在有新帧可用时触发事件。如果没有接收器正在监听特定的源,则该库可能会暂停或与源断开连接,以节省处理器和I / O资源。该库可通过简单地暂停和恢复事件的触发来自动处理摄像机的断开/重新连接(例如,断开连接不会导致出现新的帧,不会出现错误)。

  • 接收者监听特定来源的事件,获取最新图像,并以适当的格式将其转发到目的地。与源相似,如果特定接收器处于非活动状态(例如,没有客户端通过HTTP服务器连接到已配置的MJPEG),则该库可能会禁用其部分处理以节省处理器资源。

用户代码(例如FRC机器人程序中使用的代码)可以通过OpenCV源对象和接收器对象充当源(提供处理过的帧,就好像它是照相机一样),也可以充当接收器(接收要处理的帧)。因此,从相机获取图像并将处理后的图像输出的图像处理线程如下图所示:

Block diagram showing that a program can either sink or source from OpenCV.

因为源可以连接多个接收器,所以线程可以分支。例如,还可以通过将UsbCamera源连接到CvSink之外的第二个MjpegServer接收器来提供原始摄像机图像,如下图所示:

Block diagram of multiple sinks.

当摄像机捕获到新图像时,CvSink和MjpegServer [1]都会接收到它。

上图是以下CameraServer片段创建的:

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);

CameraServer实现在cscore级别上有效地执行了以下操作(出于解释目的)。 CameraServer处理许多细节,例如为所有cscore对象创建唯一名称以及自动选择端口号。 CameraServer还会保留已创建对象的单例注册表,因此如果它们超出范围,它们不会被破坏。

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);

参考计数

所有cscore对象都在内部进行引用计数。将接收器连接到源会增加源的引用计数,因此仅严格要将接收器保持在范围内。 CameraServer类保留使用CameraServer函数创建的所有对象的注册表,因此,以这种方式创建的源和接收器永远不会超出范围(除非显式删除)。