AI Camera

简介

树莓派人工智能相机使用索尼IMX500成像传感器为所有相机应用程序提供低延迟、高性能的人工智能功能。与 Raspberry Pi’s camera software stack 的紧密集成允许用户以最小的努力部署自己的神经网络模型。

The Raspberry Pi AI Camera

本节演示如何在相机上运行预打包或自定义神经网络模型。此外,本节还包括解释 rpicam-apps and Picamera2 中IMX500上运行的神经网络生成的推理数据所需的步骤。

入门

下面的说明描述了如何在Raspberry Pi AI相机上运行预打包的MobileNet SSD和PoseNet神经网络模型。

硬件安装

将摄像头安装到你的 Pi5 请参考 安装树莓派摄像头.

前提条件

这些说明假定您使用的是连接到Raspberry Pi 4 Model B或Raspberry Pi 5板上的AI相机。稍作改动后,您可以在其他带有相机连接器的Raspberry Pi型号上遵循这些说明,包括Raspberry Pi Zero 2 W和Raspberry Pi 3 Model B+。

首先,确保您的Raspberry Pi运行最新的软件。运行以下命令进行更新:

$ sudo apt update && sudo apt full-upgrade

安装 IMX500 固件

AI相机必须在启动期间将运行时固件下载到IMX500传感器上。要将这些固件文件安装到您的Raspberry Pi上,请运行以下命令:

$ sudo apt install imx500-all

这条命令:

  • installs the /lib/firmware/imx500_loader.fpk and /lib/firmware/imx500_firmware.fpk firmware files required to operate the IMX500 sensor

  • places a number of neural network model firmware files in /usr/share/imx500-models/

  • installs the IMX500 post-processing software stages in rpicam-apps

  • installs the Sony network model packaging tools

Note
IMX500内核设备驱动程序在相机启动时加载所有固件文件。如果之前没有缓存神经网络模型固件,这可能需要几分钟时间。下面的演示在控制台上显示一个进度条来指示固件加载进度。

重启

现在您已经满足q了前提条件,请重新启动Raspberry Pi:

$ sudo reboot

运行示例程序

更新所有系统包并安装固件文件后,我们就可以开始运行一些示例应用程序了。如前所述,树莓派AI相机与 libcamerarpicam-appsPicamera2 完全集成。

rpicam-apps

rpicam-apps 相机应用程序 包括可在后处理管道中运行的 IMX500 对象检测和姿态估计阶段。有关后处理管道的更多信息,请参阅 后处理(post-processing)文档

此页面上的示例使用位于 /usr/share/rpicam-assets/ 中的后处理JSON文件。

对象检测

MobileNet SSD神经网络执行基本目标检测,为找到的每个对象提供边界框和置信度值。imx500_mobilenet_ssd.json 包含使用MobileNet SSD神经网络的IMX500目标检测后处理阶段的配置参数。

imx500_mobilenet_ssd.json 声明了一个包含两个阶段的后处理管道:

  1. imx500_object_detection,它在输出张量中挑选出神经网络生成的边界框和置信度值

  2. object_detect_draw_cv,在图像上绘制边界框和标签

MobileNet SSD张量无需对Raspberry Pi进行大量后处理即可生成边界框的最终输出。所有目标检测都直接在AI相机上运行。

以下ok运行 rpicam-hello 并进行目标检测后处理:

$ rpicam-hello -t 0s --post-process-file /usr/share/rpi-camera-assets/imx500_mobilenet_ssd.json --viewfinder-width 1920 --viewfinder-height 1080 --framerate 30

运行命令后,您应该会看到一个取景器,它将边界框覆盖在神经网络识别的对象上:

IMX500 MobileNet

要录制带有目标检测覆盖的视频,请改用 rpicam-vid

$ rpicam-vid -t 10s -o output.264 --post-process-file /usr/share/rpi-camera-assets/imx500_mobilenet_ssd.json --width 1920 --height 1080 --framerate 30

您可以通过多种方式配置 imx500_object_detection 阶段。

例如,max_detections 定义了管道在任何给定时间将检测到的最大对象数。threshold 定义了管道将任何输入视为对象所需的最小置信度值。

该网络的原始推理输出数据可能非常嘈杂,因此此阶段还预先进行了一些时间过滤并应用滞后。要禁用此过滤,请删除 temporal_filter 配置块。

姿态预测

PoseNet神经网络执行姿态预测,标记与关节和四肢相关的身体上的关键点。imx500_posenet.json 包含使用PoseNet神经网络的IMX500姿态预测后处理阶段的配置参数。

imx500_posenet.json 声明了一个包含两个阶段的后处理管道:

  • imx500_posenet, which fetches the raw output tensor from the PoseNet neural network

  • plot_pose_cv, which draws line overlays on the image

AI相机执行基本检测,但输出张量需要在主机Raspberry Pi上进行额外的后处理才能产生最终输出。

以下命令运行 rpicam-hello 并进行姿势预测后处理:

$ rpicam-hello -t 0s --post-process-file /usr/share/rpi-camera-assets/imx500_posenet.json --viewfinder-width 1920 --viewfinder-height 1080 --framerate 30
IMX500 PoseNet

您可以通过多种方式配置 imx500_posenet 阶段。

例如,max_detections 定义了管道在任何给定时间将检测到的最大主体数量。threshold 定义了管道将输入视为主体所需的最小置信值。

Picamera2

有关使用Picamera2的图像分类、目标检测、对象分割和姿势预测的示例,请参阅 picamera2 GitHub 仓库

大多数示例使用OpenCV进行一些额外的处理。要安装运行OpenCV所需的依赖项,请运行以下命令:

$ sudo apt install python3-opencv python3-munkres

现在将 the picamera2 repository 下载到您的Raspberry Pi以运行示例。您将在根目录中找到示例文件,并在 README.md 文件中找到其他信息。

从存储库运行以下脚本以运行YOLOv8目标检测:

$ python imx500_object_detection_demo.py --model /usr/share/imx500-models/imx500_network_ssd_mobilenetv2_fpnlite_320x320_pp.rpk

要在Picamera2中尝试姿势预测,请从存储库运行以下脚本:

$ python imx500_pose_estimation_higherhrnet_demo.py

内部机制

概述

Raspberry Pi AI相机的工作方式与传统的基于AI的相机图像处理系统不同,如下图所示:

Traditional versus IMX500 AI camera systems

左侧演示了传统人工智能相机系统的架构。在这样的系统中,相机将图像传送到树莓派。树莓派处理图像,然后执行人工智能推理。传统系统可能使用外部人工智能加速器(如图所示)或完全依赖中央处理器。

右侧演示了使用IMX500的系统架构。相机模块包含一个小型图像信号处理器(ISP),它将原始相机图像数据转换为 输入张量(input tensor)。相机模块将此张量直接发送到相机内的AI加速器,该加速器产生包含推理结果的 输出张量(output tensors)。AI加速器将这些张量发送到树莓派。不需要外部加速器,也不需要树莓派在CPU上运行神经网络软件。

要完全理解这个系统,请熟悉以下概念:

输入张量(Input Tensor)

传递给人工智能引擎进行推理的传感器图像部分。由小型板载ISP生成,该ISP还将相机图像裁剪和缩放到已加载的神经网络预期的尺寸。输入张量通常不适用于应用程序,尽管可以出于调试目的访问它。

感兴趣区域(Region of Interest ROI)

指定传感器图像的哪一部分在重新缩放到神经网络要求的大小之前被裁剪。可以由应用程序查询和设置。使用的单位始终是全分辨率传感器输出中的像素。默认ROI设置使用从传感器接收的完整图像,不裁剪任何数据。

输出张量(Output Tensors)

由神经网络执行的推理结果。输出的精确数量和形状取决于神经网络。应用程序代码必须了解如何处理张量。

系统架构

下图显示了在我们使用Raspberry Pi AI相机模块硬件(红色)进行成像/推理用例期间使用的各种相机软件组件(绿色):

IMX500 block diagram

在启动时,IMX500传感器模块加载固件以运行特定的神经网络模型,在流式传输期间,IMX500 同时 生成图像流和推理流,此推理流保存神经网络模型的输入和输出,也称为输入/输出 张量(tensors)

设备驱动

在最低级别,IMX500传感器内核驱动程序通过I2C总线配置相机模块。CSI2驱动程序(Pi 5上的 CFE,所有其他Pi平台上的 Unicam)设置接收器将图像数据流写入帧缓冲区,以及嵌入数据和推理数据流写入内存中的另一个缓冲区。

固件文件也通过I2C总线传输。在大多数设备上,这使用标准的I2C协议,但树莓派5使用自定义高速协议。内核中的RP2040 SPI驱动程序处理固件文件传输,因为传输使用RP2040微控制器。微控制器通过SPI总线将I2C传输从内核桥接到IMX500。此外,RP2040将固件文件缓存在板载存储中。这避免了通过I2C总线传输整个固件blob的需要,显着加快了已经使用过固件的加载速度。

libcamera

一旦 libcamera 从内核中取出图像和推理数据缓冲区,IMX500特定的 cam-helper 库(libcamera 中Raspberry Pi IPA的一部分)就会解析推理缓冲区以访问输入/输出张量。这些张量被打包为Raspberry Pi供应商特定的 libcamera 控件libcamera 返回以下控件:

Control Description

CnnOutputTensor

Floating point array storing the output tensors.

CnnInputTensor

Floating point array storing the input tensor.

CnnOutputTensorInfo

Network specific parameters describing the output tensors' structure:

struct OutputTensorInfo {
	uint32_t tensorDataNum;
	uint32_t numDimensions;
	uint16_t size[MaxNumDimensions];
};

struct CnnOutputTensorInfo {
	char networkName[NetworkNameLen];
	uint32_t numTensors;
	OutputTensorInfo info[MaxNumTensors];
};

CnnInputTensorInfo

Network specific parameters describing the input tensor’s structure:

struct CnnInputTensorInfo {
	char networkName[NetworkNameLen];
	uint32_t width;
	uint32_t height;
	uint32_t numChannels;
};

rpicam-apps

rpicam-apps 提供了一个IMX500后处理阶段基类,实现了IMX500后处理阶段的功能:https://github.com/raspberrypi/rpicam-apps/blob/main/post_processing_stages/imx500/imx500_post_processing_stage.hpp[IMX500PostProcessingStage]. 使用该基类可为在 IMX500 上运行的任何神经网络模型派生一个新的后处理阶段。示例见 imx500_object_detection.cpp

class ObjectDetection : public IMX500PostProcessingStage
{
public:
	ObjectDetection(RPiCamApp *app) : IMX500PostProcessingStage(app) {}

	char const *Name() const override;

	void Read(boost::property_tree::ptree const &params) override;

	void Configure() override;

	bool Process(CompletedRequestPtr &completed_request) override;
};

对于应用程序接收到的每一帧,都会调用 Process() 函数(在上述情况下为 ObjectDetection::Process())。在此函数中,您可以提取输出张量以进行进一步处理或分析:

auto output = completed_request->metadata.get(controls::rpi::CnnOutputTensor);
if (!output)
{
  LOG_ERROR("No output tensor found in metadata!");
  return false;
}

std::vector<float> output_tensor(output->data(), output->data() + output->size());

完成后,最终结果可以可视化或保存在元数据中,并由另一个下游阶段或顶级应用程序本身使用。在对象推理案例中:

if (objects.size())
  completed_request->post_process_metadata.Set("object_detect.results", objects);

下游运行的 object_detect_draw_cv 后处理阶段从元数据中获取这些结果,并在 ObjectDetectDrawCvStage::Process() 函数中将边界框绘制到图像上:

std::vector<Detection> detections;
completed_request->post_process_metadata.Get("object_detect.results", detections);

下表包含 IMX500PostProcessingStage 提供的帮助函数的完整列表:

Function Description

Read()

Typically called from <Derived Class>::Read(), this function reads the config parameters for input tensor parsing and saving.

This function also reads the neural network model file string ("network_file") and sets up the firmware to be loaded on camera open.

Process()

Typically called from <Derived Class>::Process() this function processes and saves the input tensor to a file if required by the JSON config file.

SetInferenceRoiAbs()

Sets an absolute region of interest (ROI) crop rectangle on the sensor image to use for inferencing on the IMX500.

SetInferenceRoiAuto()

Automatically calculates region of interest (ROI) crop rectangle on the sensor image to preserve the input tensor aspect ratio for a given neural network.

ShowFwProgressBar()

Displays a progress bar on the console showing the progress of the neural network firmware upload to the IMX500.

ConvertInferenceCoordinates()

Converts from the input tensor coordinate space to the final ISP output image space.

There are a number of scaling/cropping/translation operations occurring from the original sensor image to the fully processed ISP output image. This function converts coordinates provided by the output tensor to the equivalent coordinates after performing these operations.

Picamera2

Picamera2中的IMX500集成与 rpicam-apps 中的集成非常相似。Picamera2有一个IMX500帮助类,它提供与 rpicam-apps IMX500PostProcessingStage 基类相同的功能。这可以通过以下方式导入任何Python脚本:

from picamera2.devices.imx500 import IMX500

# This must be called before instantiation of Picamera2
imx500 = IMX500(model_file)

要检索输出张量,请从控件中获取它们。然后,您可以在Python脚本中应用额外的处理。

例如,在 imx500_object_detection_demo.py 等对象推理用例中,在 parse_detections() 中提取对象边界框和置信度值,并在 draw_detections() 中在图像上绘制框:

class Detection:
    def __init__(self, coords, category, conf, metadata):
        """Create a Detection object, recording the bounding box, category and confidence."""
        self.category = category
        self.conf = conf
        obj_scaled = imx500.convert_inference_coords(coords, metadata, picam2)
        self.box = (obj_scaled.x, obj_scaled.y, obj_scaled.width, obj_scaled.height)

def draw_detections(request, detections, stream="main"):
    """Draw the detections for this request onto the ISP output."""
    labels = get_labels()
    with MappedArray(request, stream) as m:
        for detection in detections:
            x, y, w, h = detection.box
            label = f"{labels[int(detection.category)]} ({detection.conf:.2f})"
            cv2.putText(m.array, label, (x + 5, y + 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
            cv2.rectangle(m.array, (x, y), (x + w, y + h), (0, 0, 255, 0))
        if args.preserve_aspect_ratio:
            b = imx500.get_roi_scaled(request)
            cv2.putText(m.array, "ROI", (b.x + 5, b.y + 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
            cv2.rectangle(m.array, (b.x, b.y), (b.x + b.width, b.y + b.height), (255, 0, 0, 0))

def parse_detections(request, stream='main'):
    """Parse the output tensor into a number of detected objects, scaled to the ISP output."""
    outputs = imx500.get_outputs(request.get_metadata())
    boxes, scores, classes = outputs[0][0], outputs[1][0], outputs[2][0]
    detections = [ Detection(box, category, score, metadata)
                   for box, score, category in zip(boxes, scores, classes) if score > threshold]
    draw_detections(request, detections, stream)

rpicam-apps 示例不同,此示例不应用额外的滞后或时间过滤。

Picamera2中的IMX500类提供了以下帮助函数:

Function Description

IMX500.get_full_sensor_resolution()

Return the full sensor resolution of the IMX500.

IMX500.config

Returns a dictionary of the neural network configuration.

IMX500.convert_inference_coords(coords, metadata, picamera2)

Converts the coordinates coords from the input tensor coordinate space to the final ISP output image space. Must be passed Picamera2’s image metadata for the image, and the Picamera2 object.

There are a number of scaling/cropping/translation operations occurring from the original sensor image to the fully processed ISP output image. This function converts coordinates provided by the output tensor to the equivalent coordinates after performing these operations.

IMX500.show_network_fw_progress_bar()

Displays a progress bar on the console showing the progress of the neural network firmware upload to the IMX500.

IMX500.get_roi_scaled(request)

Returns the region of interest (ROI) in the ISP output image coordinate space.

IMX500.get_isp_output_size(picamera2)

Returns the ISP output image size.

IMX5000.get_input_size()

Returns the input tensor size based on the neural network model used.

IMX500.get_outputs(metadata)

Returns the output tensors from the Picamera2 image metadata.

IMX500.get_output_shapes(metadata)

Returns the shape of the output tensors from the Picamera2 image metadata for the neural network model used.

IMX500.set_inference_roi_abs(rectangle)

Sets the region of interest (ROI) crop rectangle which determines which part of the sensor image is converted to the input tensor that is used for inferencing on the IMX500. The region of interest should be specified in units of pixels at the full sensor resolution, as a (x_offset, y_offset, width, height) tuple.

IMX500.set_inference_aspect_ratio(aspect_ratio)

Automatically calculates region of interest (ROI) crop rectangle on the sensor image to preserve the given aspect ratio. To make the ROI aspect ratio exactly match the input tensor for this network, use imx500.set_inference_aspect_ratio(imx500.get_input_size()).

IMX500.get_kpi_info(metadata)

Returns the frame-level performance indicators logged by the IMX500 for the given image metadata.

模型部署

要将新的神经网络模型部署到Raspberry Pi AI相机,请完成以下步骤:

  1. 提供神经网络模型。

  2. 量化和压缩模型,以便它可以使用IMX500相机模块上可用的资源运行。

  3. 将压缩模型转换为IMX500格式。

  4. 将模型打包成固件文件,可以在运行时加载到相机上。

前三个步骤通常会在功能更强大的计算机(例如台式机或服务器)上执行。您必须在Raspberry Pi上运行最后的打包步骤。

创建模型

神经网络模型的创建超出了本指南的范围。可以重用现有模型,也可以使用TensorFlow或PyTorch等流行框架创建新模型。

有关更多信息,请参阅官方 AITRIOS开发网站

量化和压缩

使用Sony的模型压缩工具包对模型进行量化和压缩。要安装工具包,请运行以下命令:

$ pip install model_compression_toolkit

有关详细信息,请参阅 索尼模型优化 GitHub 代码库

模型压缩工具包以以下格式生成量化模型:

  • Keras (TensorFlow)

  • ONNX (PyTorch)

转换

要转换模型,首先安装转换器工具:

TensorFlow
$ pip install imx500-converter[tf]
Tip
始终使用用于压缩模型的相同版本的TensorFlow。

PyTorch::

+

$ pip install imx500-converter[pt]

如果您需要安装这两个包,请使用两个单独的Python虚拟环境。这可以防止TensorFlow和PyTorch导致彼此冲突。

接下来,转换模型:

  • TensorFlow

  • PyTorch

$ imxconv-tf -i <compressed Keras model> -o <output folder>
$ imxconv-pt -i <compressed ONNX model> -o <output folder>

这两个命令都会创建一个包含内存使用报告和 packerOut.zip 文件的output文件夹。

为了优化IMX500传感器上加速器的可用存储器,请在上述命令中添加 --no-input-persistency。但是,这将禁用输入张量生成并返回应用程序进行调试。

有关模型转换过程的更多信息,请参阅官方 索尼 IMX500 转换器文档资料

打包

重要提示:您必须在Raspberry Pi上运行此步骤。

最后一步将模型打包到RPK文件中。运行神经网络模型时,我们会将此文件上传到AI相机。在继续之前,运行以下命令以安装必要的工具:

$ sudo apt install imx500-tools

要将模型打包到RPK文件中,请运行以下命令:

$ imx500-package -i <path to packerOut.zip> -o <output folder>

此命令应在输出文件夹中创建一个名为 network.rpk 的文件。您将把此文件的名称传给IMX500相机应用程序。

有关使用的工具的更全面的说明和更多细节,请参阅 索尼 IMX500 打包文档