插件框架开发

Vision SDK提供的插件框架,定义了如何生成标准插件,从而实现插件间的数据对接、插件属性设置。Vision SDK目前支持单输入、单输出、多输入、多输出插件。

  1. 开发插件类。

    1. 继承“MxPluginBase.h”中的MxPluginBase基类,并自定义插件类名。
    2. 重写基类中的Init()、DeInit()、Process()三个成员函数,参见表1
    3. MxpluginBase中包含了“status_”“deviceId_”“dataSource_”三个公共属性,参考表2,公共属性可通过pipeline文件进行配置。
      表1 成员函数说明

      成员函数

      说明

      Init()

      该接口仅调用一次,用于实现配置参数获取、业务逻辑初始化(例如插件内可复用内存的申请)。

      DeInit()

      该接口仅调用一次,用于实现去初始化任务(例如内存释放)。

      Process()

      该接口由数据驱动,用于接收上游插件传入的数据,并在完成用户的业务处理逻辑后,向输出端口发送结果。

      表2 插件公共属性说明

      公共属性

      说明

      status_

      该属性指定插件运行方式为同步执行或异步,int类型,取值范围[0, 1],0为异步,1为同步,默认为0(异步执行),请用户根据实际情况设置。

      deviceId_

      该属性指定插件运行的芯片,int类型,默认与pipeline配置文件中开头的deviceId相同,请用户根据实际情况设置。

      dataSource_

      该属性指定插件处理数据的来源,string类型,默认为“auto”,用户可指定上游插件名称作为数据来源,请用户根据实际情况设置。

  2. 注册属性与获取属性值。

    属性用于提升插件的通用性,可通过修改pipeline配置文件中插件的属性来实现参数传递,进而达到适配业务的目的。
    1. 注册元件属性。

      用户重写DefineProperties函数,并按照如下方式注册自定义属性:

      1
      2
      3
      4
      std::vector<std::shared_ptr<void>> properties;
      std::shared_ptr<ElementProperty<string>> outputResizeHeight(new ElementProperty<string> {STRING, "resizeHeight", "resizeHeight", "the height of the resized output image",  640, 6, 4096});
      properties.push_back(parentNameSptr);
      return properties;
      
      其中ElementProperty为描述属性的结构体,内容和说明如下:
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      template<class T>
      struct ElementProperty {
          PropertyType type;                     // 属性数据类型,可选STRING, INT, UINT, FLOAT, DOUBLE, LONG, ULONG。
          std::string name;                      // 属性名称,用于获取属性值(在获取属性值章节中介绍)。
          std::string nickName;                  // 属性别名,保留字段。
          std::string desc;                      // 属性描述,介绍该属性的功能。
          T defaultValue;                        // 属性默认值。
          T min;                                 // 属性最小值,按需填写,例如STRING类型数据无最小值,可填“NULL”。
          T max;                                 // 属性最大值,按需填写,同上。
      };
      
    2. 获取元件属性值。

      “Init()”函数中,插件对应的属性值将通过“configParamMap”入参传入,可通过属性名称获取。

      1
      2
      std::shared_ptr<string> outputResizeHeight = std::static_pointer_cast<string>configParamMap["resizeHeight"]);
      std::string parentName = *outputResizeHeight.get();
      

  3. (可选)定义端口属性。

    插件的输入和输出端口分别用于接收输入数据和发送输出结果,插件支持注册多个输入/输出端口(参考多输入/输出插件端口创建)。

    端口类型包括静态和动态两种(参考动态端口创建):静态端口为创建元件后必然生成的端口,端口必须有连接,否则Stream创建失败,动态端口数量可变,根据实际需要创建。

    通过重写DefineInputPorts与DefineOutputPorts函数,用户可以定义端口,以单输入静态端口为例,按照如下方式注册端口自定义属性:
    • “DefineInputPorts”中,通过GenerateStaticInputPortsInfo()接口构建静态输入端口信息MxpiPortInfo,Vision SDK获取“DefineInputPorts”的返回值用于创建端口。
    • 接口入参为一个二层vector,内层vector用于定义端口支持的数据格式(端口格式用于区分元件端口是否允许连接,目前已有端口格式请参考表3),例如:"image/jpeg"为JPEG图像解码前数据,用户可以给单个端口定义多个数据格式(以推理插件为例:{"metadata/object", "metadata/class"})。
    • 接口外层vector用于区分端口数量,例如:{{"image/jpeg"}, {"metadata/object"}}表示创建两个端口,第一个为“jpeg”格式,第二个为“object”格式。

      自定义字符串只支持以英文字母开头,可使用字母,阿拉伯数字与下划线。建议使用"一级类别/二级类别"命名,例如"image/yuv"。

    • 同样的方式,在“DefineOutputPorts”定义输出端口。
      • 基类中:
        1
        2
        3
        4
        5
        6
        7
        MxpiPortInfo MxPluginBase::DefineInputPorts()
        {
            MxpiPortInfo defaultInputPortInfo;
            std::vector<std::vector<std::string>> value = {{"ANY"}};
            GenerateStaticInputPortsInfo(value, defaultInputPortInfo);
            return defaultInputPortInfo;
        }
        
      • 推理插件中:
        1
        2
        3
        4
        5
        6
        7
        MxpiPortInfo MpModelInfer::DefineInputPorts()
        {
            MxpiPortInfo inputPortInfo;
            std::vector<std::vector<std::string>> value = {{"image/yuv"}};
            GenerateStaticInputPortsInfo(value, inputPortInfo);
            return inputPortInfo;
        }
        

    目前已开发的插件所使用到的端口格式如表3

    表3 端口格式

    端口

    类型

    原图

    image/jpeg

    视频

    video/x-h264

    解码图

    image/yuv

    image/rgb

    目标框

    metadata/object

    图像类别

    metadata/class

    图像属性

    metadata/attribute

    图像特征

    metadata/feature-vector

    张量

    metadata/tensor

    绘图单元

    metadata/osd

    语义分割像素

    metadata/semanticseg

    纯文本

    metadata/texts

    目标关键点

    metadata/keypoint

    序列化结果

    result/json-result

    兼容任意

    ANY,表示可以匹配任意格式数据,即这个端口可以与任意端口相连。

    表4 插件描述

    插件

    输入

    输出

    简述

    mxpi_imagedecoder

    image/jpeg

    image/yuv或

    image/rgb或

    metadata/object

    图片解码,分块时支持metadata/object。

    mxpi_videodecoder

    video/x-h264

    image/yuv或

    image/rgb

    视频解码。

    mxpi_imageresize

    image/yuv或

    image/rgb或

    metadata/object

    image/yuv或

    image/rgb

    图片缩放。

    mxpi_tensorinfer

    metadata/tensor或

    image/yuv

    metadata/tensor

    张量推理。输入张量或者图片,输出张量。

    mxpi_modelinfer

    image/yuv

    metadata/object或

    metadata/class或

    metadata/attribute或

    metadata/feature-vector或

    metadata/tensor

    推理图片,不同模型后处理会得到不同结果:目标框、属性或特征。

    不使用后处理时,直接输出推理结果。

    mxpi_imagecrop

    metadata/objectbox

    image/yuv或

    image/rgb

    图片裁剪。

    mxpi_dataserialize

    ANY

    result/json-result

    序列化为JSON字符串。

  4. 插件框架构建与命名。

    插件源文件中,通过如下宏构建插件框架(其中“CustomPlugin”为用户自定义插件类名)。
    1
    MX_PLUGIN_GENERATE(CustomPlugin)              // Register a custom plugin using macro.
    
    插件名称对应于pipeline配置文件中的“factory”参数,在插件“CMakeLists.txt”文件中通过以下方式注册(其中“mxpi_customplugin”为插件名)。
    1
    2
    set(PLUGIN_NAME "mxpi_customplugin")
    add_compile_options("-DPLUGIN_NAME=${PLUGIN_NAME}")
    

多输入/输出插件端口创建

插件开发支持用户添加多个输入端口或多个输出端口,多输入多输出插件开发需要增加如下步骤。

  1. 选择数据的处理模式。
    • 同步处理:等待所有输入端口都收到上游发送的数据后调用插件的Process()函数,即保证Process()函数入参的vector中所有输入端口对应序号的数据都存在。
    • 异步处理:任意一个输入端口收到上游发送的数据就调用插件的Process()函数,需判断Process()函数入参的vector中的第几个编号存在数据。

    处理模式在pipeline中设置为配置参数“status”“0”为异步,“1”为同步,默认为异步。

    图1 通过status配置处理模式
  2. 修改插件输入接口DefineInputPorts函数的portNum,以推理插件为例,将输入设置成两端口。
    1
    2
    3
    4
    5
    6
    7
    MxpiPortInfo MpModelInfer::DefineInputPorts()
    {
        MxpiPortInfo inputPortInfo;
        std::vector<std::vector<std::string>> value = {{"image/yuv"},{"image/yuv"}};
        GenerateStaticInputPortsInfo(value, inputPortInfo);
        return inputPortInfo;
    }
    

    修改插件输出接口DefineOutputPorts函数的portNum,以推理插件为例,将输出设置成两端口。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    MxpiPortInfo MpModelInfer::DefineOutputPorts()
    {
        MxpiPortInfo outputPortInfo;
        std::vector<std::vector<std::string>> value = {
            {"metadata/object", "metadata/class", "metadata/attribute", "metadata/feature-vector", "metadata/tensor"}
            {"metadata/object", "metadata/class", "metadata/attribute", "metadata/feature-vector", "metadata/tensor"}
        };
        GenerateStaticOutputPortsInfo(value, outputPortInfo);
        return outputPortInfo;
    }
    
  3. 修改pipeline。
    • 含多输入端口的元件。
      图2 多输入端口元件示例
    • 含多输出端口的元件。
      图3 多输出端口元件示例

动态端口创建

动态端口应用于可变输入或输出数据数量的场景,例如同步等待插件(mxpi_synchronize),等待的输入数据个数可以由用户配置。动态端口的数量可变,用户可根据需要通过编辑pipeline配置文件创建。

图4所示,同步等待插件既可以创建两个也可以创建三个输入端口,图中右侧内容为pipeline配置文件对应内容。

图4 动态端口元件使用示例