×

签到

分享到微信

打开微信,使用扫一扫进入页面后,点击右上角菜单,

点击“发送给朋友”或“分享到朋友圈”完成分享

【CN-SDK01】寒武纪 CNCV 使用介绍 小飞人2023-07-24 15:56:27 回复 查看 社区交流 干货资源
【CN-SDK01】寒武纪 CNCV 使用介绍
分享到:

知乎链接:https://zhuanlan.zhihu.com/p/612780111


若是初学者,建议先初级课程。

涉及的专有名称:

本文简称标准名词解释
MLUMLU特指寒武纪处理器。
CNCVCambricon Computer Vision Library寒武纪计算机视觉库,基于 MLU 的图像/视觉算法加速库。
CNRTCambricon Runtime Library寒武纪运行时库,提供一套面向MLU设备的高级别接口,用于主机与MLU设备之间的交互。
CNDrv APICambricon Driver API寒武纪驱动API,提供了⼀套⾯向 MLU设备的接口,⽤于主机与 MLU 设备之间的交互。

1、CNCV 及相关概念介绍

1.1 CNCV 简介

CNCV位于Cambricon Sdk层,属于一个视觉SDK库。



它重要的算法功能特点是:

  • 提供多种应用场景下的常用图像算子,包括:训练、推理中图像的预处理,通用图像处理分析等;

  • 在通用概念上与业界标准对齐,包括的图像格式、颜色空间、图像跨距stride(又称step)等;在算法实现上,主要与Opencv对齐;

  • 性能方面,结合MLU硬件架构特点,对算子进行了高度优化,以达到较好的时延和吞吐性能,并占用尽可能少的内存;

  • 编程风格上,它提供的是C风格接口,应用层可以较灵活地集成使用;并保证寒武纪的端云一体,云侧和端侧采用相同的编程模型,且API保持一致。

2 CNCV 相关基础概念

1)API命名约定

API整体命名规则为:cncv<operation>_<key_de ion>_<version_de ion>。

  • <operation> :算法名称。

  • <key_de ion> 依次由 <batch_de ion><roi_de ion><plane_de ion> 组成。

    • <batch_de ion> 有三种类型: Basic、空和 Advanced。

      • Basic:表示单 batch;

      • 空:多 batch 不可变;

      • Advanced:多 batch 可变。

    • <roi_de ion> :是否支持ROI。

    • <plane_de ion> :算子接口支持的图像 plane 数⽬,使用Pn表示。

  • <version_de ion> :算子接口版本,⼀般建议使用最高版本。

一些接口示例:

  • cncvAbsDiff_Basic:单 batch 接口。

  • cncvAbsDiff_V2:支持多 batch 的 V2 接口。

  • cncvResizeConvert_AdvancedROI:支持多 batch 和 ROI,且可变

  • cncvRgbxToYuv_BasicROIP2:支持 ROI 的单 batch 接口,输出的 YUV 图像为2个 plane 的 Planar 格式

2)图像描述信息

在CNCV中,图像信息描述主要使用cncvImageDe or结构体,其中定义了图像的宽、高、图像跨距stride、图像格式pixel_fmt、颜色空间标准color space、图像depth。图像跨距、图像格式、颜色空间标准与业界标准对齐。

typedef struct cncvImageDe or {
    uint32_t width;                                                    // 图像的宽度。    uint32_t height;                                                  // 图像的高度。    uint32_t stride[CNCV_PLANE_MAX_NUM];   // 图像的行跨距。CNCV_PLANE_MAX_NUM值为6。    cncvPixelFormat pixel_fmt;                             // 图像格式。    cncvColorSpace color_space;                         // 图像色彩空间。    cncvDepth_t depth;                                           // 图像数据格式。  } cncvImageDe or;typedef struct cncvImageDe or *cncvImageDe or_t;typedef enum {
    CNCV_DEPTH_8U  = 0,          //!< 无符号8比特整型数据。    CNCV_DEPTH_8S  = 1,          //!< 有符号8比特整型数据。    CNCV_DEPTH_16U = 2,          //!< 无符号16比特整型数据(保留定义,目前无实际算子支持)。    CNCV_DEPTH_16S = 3,          //!< 有符号16比特整型数据.    CNCV_DEPTH_32U = 4,          //!< 无符号32比特整型数据(保留定义,目前无实际算子支持)。    CNCV_DEPTH_32S = 5,          //!< 有符号32比特整型数据。    CNCV_DEPTH_16F = 6,          //!< 16比特浮点数据。    CNCV_DEPTH_32F = 7,          //!< 32比特浮点数据。    CNCV_DEPTH_INVALID = 8,      //!< 无效数据类型。} cncvDepth_t;

depth是指图像像素的数据类型,如此处( cncvDepth)定义,当前支持了整型U8,半精度浮点FP16,单精度浮点FP32等。

// 当前支持的图像格式类型typedef enum {
  CNCV_PIX_FMT_NV12         = 0,   //!< Semi-planar Y4-U1V1.  CNCV_PIX_FMT_NV21         = 1,   //!< Semi-planar Y4-V1U1.  CNCV_PIX_FMT_I420         = 2,   //!< Planar Y4-U1-V1.  CNCV_PIX_FMT_YV12         = 3,   //!< Planar Y4-V1-U1.  CNCV_PIX_FMT_YUYV         = 4,   //!< Packed Y2U1Y2V1.  CNCV_PIX_FMT_UYVY         = 5,   //!< Packed U1Y2V1Y2.  CNCV_PIX_FMT_YVYU         = 6,   //!< Packed Y2V1Y2U1.  CNCV_PIX_FMT_VYUY         = 7,   //!< Packed V1Y2U1Y2.  CNCV_PIX_FMT_P010         = 8,   //!< 10-bit semi-planar Y4-U1V1.  CNCV_PIX_FMT_YUV420_10BIT = 9,   //!< 10-bit planar Y4-U1-V1.  CNCV_PIX_FMT_YUV444_10BIT = 10,  //!< 10-bit planar Y4-U4-V4.  CNCV_PIX_FMT_RGB          = 11,  //!< Packed RGB.  CNCV_PIX_FMT_BGR          = 12,  //!< Packed BGR.  CNCV_PIX_FMT_ARGB         = 13,  //!< Packed ARGB.  CNCV_PIX_FMT_ABGR         = 14,  //!< Packed ABGR.  CNCV_PIX_FMT_BGRA         = 15,  //!< Packed BGRA.  CNCV_PIX_FMT_RGBA         = 16,  //!< Packed RGBA.  CNCV_PIX_FMT_AYUV         = 17,  //!< Packed AYUV.  CNCV_PIX_FMT_RGB565       = 18,  //!< 8-bit packed R5G6B5.  CNCV_PIX_FMT_GRAY         = 19,  //!< Packed Gray.  CNCV_PIX_FMT_LAB          = 20,  //!< Packed Lab.  CNCV_PIX_FMT_HSV          = 21,  //!< Packed HSV.  CNCV_PIX_FMT_HSV_FULL     = 22,  //!< Packed HSV_FULL.  CNCV_PIX_FMT_COMPLEX      = 23,  //!< Packed DFT.  CNCV_PIX_FMT_INVALID      = 24,  //!< Invalid pixel format.} cncvPixelFormat;

图像格式的支持,如此处(cncvPixelFmt)定义,覆盖常用的图像类型:yuv420采样格式的NV12、NV21、I420,yuv422采样的YUYV、UYVY...,RGB三、四通道的packed格式,HSV、LAB颜色模型及GRAY灰度图等。在CNCV中,GRAY也会用于描述单通道数据。

// 当前支持的颜色空间类型typedef enum {
  CNCV_COLOR_SPACE_BT_709    = 0,
  //!< ITU BT.709 color space.  CNCV_COLOR_SPACE_BT_601    = 1,
  //!< ITU BT.601 color space.  CNCV_COLOR_SPACE_BT_2020   = 2,
  //!< ITU BT.2020 color space.  CNCV_COLOR_SPACE_BT_601_ER = 3,
  //!< ITU BT.601 color space with extended-range values.  CNCV_COLOR_SPACE_BT_709_ER = 4,
  //!< ITU BT.709 color space with extended-range values.  CNCV_COLOR_SPACE_INVALID   = 5,
  //!< Invalid color space.} cncvColorSpace;

在颜色空间标准上,目前实际使用支持的有:BT.709、BT.601、BT.709_ER、BT601_ER;这些主要用在YUV与RGB的颜色模型转换上。

3)内存管理

CNCV算子接口使用的内存均由用户侧开辟、管理与释放,这些内存主要包括图像数据内存和workspace内存。

① 图像数据

CNCV 算子接收的输入、输出图像数据均存储于 MLU 上

  • void* src:⼀级 MLU 数据指针

    • 主要用于单 batch 接口中

    • planar 图像:void* src1 、void* src2 、... 依次表⽰

  • void** src:⼆级 MLU 数据指针(MLU 指针数组)

    • 主要用于多 batch接口中

    • 多 batch 的 planar 图像:依次按照每个 batch 的 plane 数据指针摆放。

    • eg(n batch 的NV12):|img_0_y|img_0_uv|img_1_y|img_1_uv|...|img_n_y|img_n_uv|

② workspace

CNCV中也有一些算子接口需要额外的MLU内存来执行计算,称为算子工作空间workspace。

如果算子接口中包含 workspace 和 workspace_size 参数,则说明该算子需要申请 workspace。

用户通过与算⼦接⼝匹配的cncvGetxxxWorkspaceSize 获取算子所需的最小 workspace 空间,其中 xxxx 为算⼦名称。

为了让⽤户最⼤限度地控制内存,优化重复调⽤算⼦接⼝的性能,workspace同样也是由⽤户分配、管理与释放。

4)感兴趣区域ROI

在一些应用场景中,处理图像的感兴趣矩形子区域较为常见。CNCV中部分算子提供这种功能支持,对应的矩形子区域称为感兴趣区域ROI(Region of Interest)。

支持ROI的算子接口在API命名上含有ROI后缀,且需要用户传入ROI参数。

针对二维图像数据,ROI使用cncvRect表示,分别定义矩形子区域的左上角坐标x、y,及矩形区域的宽和高。

typedef struct cncvRect {
  uint32_t x;  //!< The X coordinate of ROI.  uint32_t y;  //!< The Y coordinate of ROI.  uint32_t w;  //!< The width of ROI.  uint32_t h;  //!< The height of ROI.} cncvRect;

功能支持上,有输入ROI和输出ROI两种概念:

1)输入 ROI 表示取源图像的矩形子区域;

2)输出 ROI 表示将处理后的图像数据填充到目标图像的矩形子区域中。

5)句柄与线程安全

CNCV算子接口均为异步操作,使用句柄来管理计算需要的上下文。

  • 句柄:句柄即cncvHandle,与程序运行所在的当前设备绑定,其中维护了包含MLU设备信息、队列信息在内的上下文信息。CNCV中提供了一组运行时接口进行句柄的创建、初始化(设备信息)、更新与销毁。

  • 线程安全:用户层通过 cncvHandle 保证线程安全。但多线程并发中,建议每个线程维护独自的句柄,并在整个线程生命周期中,使用它。

3 支持算法概览

CNCV目前提供的算法大致包括八类,如表中所列:算术和逻辑运算、数据交换和初始化、颜色模型和采样格式转换、二值化与比较操作、形态学操作、线性变换和几何变换、滤波及融合算法类。

前7类主要为常用的图像处理算法,融合算法是针对一些用户场景提供的融合多个通用图像处理算法的算子,如ResizeConvert是融合了缩放算法和YUVToRgb的颜色空间转换操作。

算法分类算法名称
算术和逻辑运算类add、sub、mul、divide、bitwise_and、absdiff、mean_std等
数据交换和初始化类convert_to、copy_to、crop、fill、flip、merge、pad、split、transpose 等
颜色模型和采样格式转换类bgr_to_lab、hsv_to_rgbx、lab_to_bgr、rgbx_to_gray、rgbx_to_hsv、rgbx_to_rgbx、rgbx_to_yuv、yuv_to_rgb、yuv_to_yuv、color_twist等
二值化和比较操作threshold 等
形态学操作dilate、erode 等
线性变换和几何变换类resize_gray、resize_rgbx、resize_yuv、warp_affine、warp_perspective等
滤波类filter、gaussian_blur、blur、laplacian、sobel等
Fusions(融合算法类)针对一些用户场景提供的融合多个通用图像处理算法的算子,包含adjust_contrast、adjust_hue、adjust_saturation、resize_convert、crop_mirror_normalize、compare_and_find_minloc等。

后续也会支持更多的算法。不同平台和版本支持的算子详细情况,可以参考CNCV用户手册和Release Note中的相关说明。

2、CNCV 的使用

1 编程模型



  • BANG 异构编程模型:主机端进行设备资源的申请、管理与释放,设备端负责执行CNCV 算子的计算任务。

  • 多线程并发的编程建议:每个线程维护一个独自的cncvHandle,并在整个线程的生命周期中均使用该handle。

2 编程示例

下面,我们以伪代码的code flow形式,做CNCV单算子和多算子连续调用的编程示例说明

首先是单算子编程示例,基本与刚才讲述的编程模型对应,参考该流程图:



第1步,分别使用cnrtsetDevice()、cnrtQueueCreate()初始化设备信息、创建任务队列;分别使用cncvCreate()、cncvSetHandle()创建并初始化句柄;

第2.1步,准备调用CNCV算子执行的图像计算所需参数:使用cncvGetxxxWorkspaceSize()接口获取算子计算需要的最小MLU内存空间,分别开辟输入/输出图像数据内存、MLU数据指针内存、workspace内存,并依次使用cnrtMemcpy将需要处理的输入图像数据、MLU数据指针从CPU端拷贝至MLU端;

第2.2步,调用cncvOperator() 算子接口下发算子任务;

第2.3步,调用cncvSync()或cnrtQueueSync()同步任务队列获取计算结果;

第3步,释放cpu/mlu内存,并调用cncvDestroy()、cnrtQueueDestroy()销毁cncv句柄和设备队列。

其次,是多算子编程示例,如左侧流程图所示,code flow整体与单算子类似。

区别主要在于,连续调用多个算子时,依次调用多个算子接口下发计算任务后,再调用cncvSync()或cnrtQueueSync()同步任务队列即可。



3 如何限定 CNCV 使用的 MLU 资源

在实际业务开发中,我们可能还会关心的一个问题是如何限制 CNCV 可用的 MLU 资源?

CNCV算子默认会使用MLU上所有可见的Cluster资源,用户侧可以结合 CNDrv API 的 visibleCluser 功能来限制CNCV可用的MLU资源。考虑实际业务处理的设计,有两种配置方式:

  • 第一种,在初始化时配置,即调用 cncvCreate 创建句柄之前。用户侧使用 cnSetCtxConfigParam 配置 visibleCluster 之后,使用 cncvCrete() 创建句柄并执行后续操作;

  • 第二种,在运行时更新,即调用 cncvCreate 创建句柄之后。用户侧配置 visibleCluster 之后,需要使用cncvUpdateContextInformation () 更新句柄中绑定的上下文信息。

下图提供运行时更新的流程示意图。如示例描述,我们在设备和句柄初始化之后,2.3调用cncv算子接口之前,进行 visibleCluster 配置,再使用 cncvUpdateContextInformation 更新句柄的上下文信息即可。



4 部署及开发示例说明

通过前面内容的学习,我们基本对CNCV、相关基础概念、编程模型有了整体的认识。该小节提供CNCV部署和开发样例的参考指南。

部署:CNCV提供了X86_64、Aarch64服务器端的开发包,详细的部署方式参见用户手册的4.2章节。

开发实例:开发包部署后,可以在samples目录下找到CNCV的开发样例,主要可以参考的sample参见表格:

示例示例简要介绍
resize_convert以cncvResizeConvert_AdvancedROI算子接口为例,提供需要 workspace 空间,支持多 batch 且batch 间图像描述信息及 ROI 信息可不同的接口使用示例,且示例中采用等比例缩放方式;
yuv2rgbx以 cncvYuvToRgbx算子接口为例,提供需要 workspace 空间,支持多 batch 且 batch 间图像描述信息必须相同的接口使用示例;
mean_std以 cncvMeanStd算子接口为例,提供需要 workspace 空间,支持多 batch 且 batch 间图像描述信息必须相同,含 cpu数据指针的接口使用示例
filter、warpaffine以 cncvWarpAffine_V3 算子接口为例,提供无须 workspace,支持多 batch 且 batch 间图像描述信息必须相同,除图像数据外,需要其他 mlu数据的接口使用示例
multi_ops SyntaxHighlighter.all();
版权所有 © 2024 寒武纪 Cambricon.com 备案/许可证号:京ICP备17003415号-1