读取和处理视频:CameraServer类

概念

FRC | reg |中通常使用的摄像机(商品USB和以太网摄像机,例如Axis摄像机)提供相对有限的操作模式。通常,它们仅以单个分辨率和帧速率提供单个图像输出(通常以RGB压缩格式,例如JPG)输出。 USB相机受到特别限制,因为一次只能有一个应用程序可以访问相机。

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

相机名称

CameraServer中的每个摄像机都必须唯一命名。这也是在仪表板中为相机显示的名称。 CameraServer的“ startAutomaticCapture()”和“ addAxisCamera()”函数的某些变体会自动命名相机(例如“ USB Camera 0”或“ Axis Camera”),或者您可以为相机指定一个更具描述性的名称(例如“进气凸轮”)。唯一的要求是每个摄像机都有一个唯一的名称。

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)并为其启动服务器,以便可以从仪表板上对其进行查看。

  • addAxisCamera():添加一个Axis相机。即使您没有在机器人代码中处理Axis相机的图像,也可能要使用此功能,以便Axis相机出现在“仪表板”的相机下拉列表中。它还会启动服务器,以便当驱动程序站通过USB连接到roboRIO时仍可以查看Axis流(如果将Axis摄像机和roboRIO都连接到两个机器人无线电以太网端口,则在竞争中很有用)。

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

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

cscore库

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

  • 从USB和HTTP(例如Axis)相机获取图像

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

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

  • 充当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函数创建的所有对象的注册表,因此,以这种方式创建的源和接收器永远不会超出范围(除非显式删除)。