Examples

在开发板上运行离线网络模型

建议在开发板上运行离线网络模型之前,使用Simulator对模型进行验证。Simlator能够在PC端模拟板上运行环境。

首先确保开发板已经烧录好最新的固件,使用Demo中的dla_classify可对单张图片推演一次,结果为推演结果的TOP5。下面分解dla_classify.cpp文件,针对使用MI_IPU API的顺序举例说明。

dla_classify.cpp文件路径为:sdk/verify/mi_demo/alderaan/dla_classify/dla_classify.cpp

1. 创建IPU设备


MI_S32 IPUCreateDevice(char* pFirmwarePath, MI_U32 u32VarBufSize) {     MI_S32 s32Ret = MI_SUCCESS;     MI_IPU_DevAttr_t stDevAttr;     stDevAttr.u32MaxVariableBufSize = u32VarBufSize;     stDevAttr.u32YUV420_W_Pitch_Alignment = 16;     stDevAttr.u32YUV420_H_Pitch_Alignment = 2;     stDevAttr.u32XRGB_W_Pitch_Alignment = 16;     s32Ret = MI_IPU_CreateDevice(&stDevAttr, NULL, pFirmwarePath, 0);     return s32Ret; }

输入参数:pFirmwarePath:Firmware文件的路径,传NULL时会调用/config/dla/ipu_firmware.bin
u32VarBufSize:模型内部Tensor使用的memory的最大大小

u32VarBufSize可以通过parse_net工具获取,获取方法如下: parse_net工具路径:SGS_IPU_SDK/bin/parse_net 使用时直接在parse_net命令后加上定点网络模型路径即可,可参考以下命令: ./parse_net xxx_fixed.sim | grep Variable 输出: | |--> SubGraph[0]: Variable buffer 0xd2f00 \@ 0x82f3b40. 其中0xd2f00向2对齐后为u32VarBufSize大小。 在C/C++中,向2对齐的定义为: #define alignment_up(a, size) ((a + size - 1) & (~(size - 1))) 所以u32VarBufSize为864000。

输出参数:MI_IPU API 错误码,具体参考《MI_IPU_API手册》

2. 创建IPU通道


MI_S32 IPUCreateChannel(MI_U32* u32Channel, char* pModelImage) {     MI_S32 s32Ret ;     MI_IPUChnAttr_t stChnAttr;     //create channel     memset(&stChnAttr, 0, sizeof(stChnAttr));     stChnAttr.u32InputBufDepth = 2;     stChnAttr.u32OutputBufDepth = 2;     return MI_IPU_CreateCHN(u32Channel, &stChnAttr, NULL, pModelImage); } MI_U32 u32Channel; ret = IPUCreateChannel(&u32ChannelID, “./mobilenet_v1_fixed.img”);

输入参数:s32Channel:创建IPU通道的ID
pModelImage:离线网络模型文件路径离线

输出参数:MI_IPU API 错误码,具体参考《MI_IPU_API手册》

3. 获取模型输入输出Tensor属性


MI_IPU_SubNet_InputOutputDesc_t desc; MI_IPU_GetInOutTensorDesc(u32ChannelID, \&desc);

输入参数:u32ChnId:IPU通道的ID
pstDesc:IPU子网络输入/输出描述结构体

输出参数:MI_IPU API 错误码,具体参考《MI_IPU_API手册》

4. 获取输入输出Tensor


MI_IPU_TensorVector_t InputTensorVector; MI_IPU_TensorVector_t OutputTensorVector; MI_IPU_GetInputTensors(u32ChannelID, \&InputTensorVector); MI_IPU_GetOutputTensors(u32ChannelID, \&OutputTensorVector);

输入参数:u32ChannelID:IPU通道的ID
InputTensorVector:输入IPU Tensor数组结构体
OutputTensorVector:输出IPU Tensor数组结构体

输出参数:MI_IPU API 错误码,具体参考《MI_IPU_API手册》

5. 模型数据输入


5.1. 拷贝数据

将数据拷贝至模型输入Tensor的虚拟地址,拷贝完成后调用MI_SYS_FlushInvCache:

MI_U8* pdata = (MI_U8 *)InputTensorVector.astArrayTensors[0].ptTensorData[0]; MI_U8* pSrc  = (MI_U8 *)Input_Data; for(int i = 0; i \< imageSize; i++) {     *(pdata + i) = *(pSrc + i); } MI_SYS_FlushInvCache(pdata, imageSize);

拷贝数据时需注意:

  1. 模型中input formats设置为BGR 或者RGB 时,stride不要做alignment,做了alignment反而有问题。

  2. 模型中input formats设置为 BGRA 或者RGBA(A channel在高地址),alignmet规则是stride = alignment_up(width*4, 16 )。

input_formats设置为BGRA,对应MI_SYS_PixelFormat_e为E_MI_SYSPIXEL_FRAME_ARGB8888。

input_formats设置为RGBA,对应MI_SYS_PixelFormat_e为E_MI_SYSPIXEL_FRAME_ABGR8888。

  1. input formats设置为YUV_NV12或者GRAY时,alignment规则是stride = alignment_up(width,16),height需要2 alignment。

input_formats设置为YUV_NV12或者GRAY时,对应MI_SYS_PixelFormat_e为E_MI_SYSPIXEL_FRAME_YUV_SEMIPLANAR_420。

5.2. 零拷贝数据

如果使用MI的其他模块,为模型输入做好了数据准备,可以不用拷贝数据,直接给传递其他MI模块的物理地址。

以下以MI_DIVP模块示例模型输入零拷贝数据,需注意:

创建IPU通道时,输入InputBufDepth设为零,不再使用MI_IPU_GetInputTensors分配输入空间

stChnAttr.u32InputBufDepth = 0;

由于不使用MI_IPU_GetInputTensors,需手动将网络描述的网络模型输入个数赋给InputTensorVector.u32TensorCount。

InputTensorVector.u32TensorCount = desc.u32InputTensorCount;

使用MI_DIVP缩放图片:

分配MI_DIVP的空间

MI_PHY phySrcBufAddr = 0; MI_PHY phyDstBufAddr = 0; void *pVirSrcBufAddr; void *pVirDstBufAddr; MI_S32 ret; ret = MI_SYS_MMA_Alloc(NULL, SRC_BUFF_SIZE, \&phySrcBufAddr); if(ret != MI_SUCCESS) {     divp_ut_dbg("alloc src buff failed\n");     return -1; } ret = MI_SYS_Mmap(phySrcBufAddr, SRC_BUFF_SIZE, &pVirSrcBufAddr, TRUE); if(ret != MI_SUCCESS) {     MI_SYS_MMA_Free(phySrcBufAddr);     divp_ut_dbg("mmap src buff failed\n");     return -1; } ret = MI_SYS_MMA_Alloc(NULL, DST_BUFF_SIZE, \&phyDstBufAddr); if(ret != MI_SUCCESS) {     MI_SYS_Munmap(pVirSrcBufAddr, SRC_BUFF_SIZE);     MI_SYS_MMA_Free(phySrcBufAddr);     divp_ut_dbg("alloc dst buff failed\n");     return -1; } ret = MI_SYS_Mmap(phyDstBufAddr, DST_BUFF_SIZE, &pVirDstBufAddr, TRUE); if(ret != MI_SUCCESS) {    MI_SYS_Munmap(pVirSrcBufAddr, SRC_BUFF_SIZE);    MI_SYS_MMA_Free(phySrcBufAddr);    MI_SYS_MMA_Free(phyDstBufAddr);    divp_ut_dbg("mmap dst buff failed\n");    return -1; }

配置MI_DIVP参数:

MI_DIVP_DirectBuf_t stSrcBuf; MI_DIVP_DirectBuf_t stDstBuf; MI_SYS_WindowRect_t stSrcCrop; stSrcBuf.ePixelFormat = E_MI_SYS_PIXEL_FRAME_YUV_SEMIPLANAR_420; stSrcBuf.u32Width = SRC_WIDTH; stSrcBuf.u32Height = SRC_HEIGHT; stSrcBuf.u32Stride[0] = SRC_BUFF_STRIDE; stSrcBuf.u32Stride[1] = SRC_BUFF_STRIDE; stSrcBuf.phyAddr[0] = phySrcBufAddr; stSrcBuf.phyAddr[1] = stSrcBuf.phyAddr[0] + SRC_BUFF_STRIDE*SRC_HEIGHT; stDstBuf.ePixelFormat = E_MI_SYS_PIXEL_FRAME_YUV_SEMIPLANAR_420; stDstBuf.u32Width = DST_WIDTH; stDstBuf.u32Height = DST_HEIGHT; stDstBuf.u32Stride[0] = DST_BUFF_STRIDE; stDstBuf.u32Stride[1] = DST_BUFF_STRIDE; stDstBuf.phyAddr[0] = phyDstBufAddr; stDstBuf.phyAddr[1] = stDstBuf.phyAddr[0] + DST_BUFF_STRIDE*DST_HEIGHT; stSrcCrop.u16X = CROP_X; stSrcCrop.u16Y = CROP_Y; stSrcCrop.u16Width = CROP_W; stSrcCrop.u16Height = CROP_H;

输入图片数据:

int ReadSize = 0; FILE *input_fp; input_fp = fopen("./1920x1080_yuv420.yuv","r"); if(!input_fp) {     divp_ut_dbg("open file[%s] failed\n","./1920x1080_yuv420.yuv");     return -1; } for(Idx = 0; Idx \< SRC_HEIGHT * 3 / 2; Idx++) {     ReadSize = fread(pVirSrcBufAddr+Idx*SRC_BUFF_STRIDE, 1, SRC_WIDTH, input_fp);     if(ReadSize != width)     {         return -1;     } }

使用MI_DIVP缩放图片:

MI_DIVP_StretchBuf(&stSrcBuf, &stSrcCrop, &stDstBuf)

将MI_DIVP的输出物理地址赋给MI_IPU的输入Tensor物理地址:

InputTensorVector.astArrayTensors[0].phyTensorAddr[0] = stDstBuf.phyAddr[0]; InputTensorVector.astArrayTensors[0].phyTensorAddr[1] = stDstBuf.phyAddr[1];

之后可以进行模型推演。

使用零拷贝数据,不需要再释放输入Tensor,即不再调用MI_IPU_PutInputTensors函数。

6. 模型推演


ret = MI_IPU_Invoke(u32ChannelID, &InputTensorVector, \&OutputTensorVector); if (ret != MI_SUCCESS) {     MI_IPU_DestroyCHN(u32ChannelID);     MI_IPU_DestroyDevice();     std::cerr \<\< "IPU invoke failed!!" \<\< std::endl; }

输入参数:u32ChannelID:IPU通道的ID
InputTensorVector:输入IPU Tensor数组结构体
OutputTensorVector:输出IPU Tensor数组结构体

输出参数:MI_IPU API 错误码,具体参考《MI_IPU_API手册》

7. 释放输入输出Tensor


MI_IPU_PutInputTensors(u32ChannelID, \&InputTensorVector); MI_IPU_PutOutputTensors(u32ChannelID, \&OutputTensorVector);

输入参数:u32ChannelID:IPU通道的ID
InputTensorVector:输入IPU Tensor数组结构体
OutputTensorVector:输出IPU Tensor数组结构体

输出参数:MI_IPU API 错误码,具体参考《MI_IPU_API手册》

8. 销毁IPU通道


MI_IPU_DestroyCHN(u32ChannelID);

输入参数:s32Channel:创建IPU通道的ID

输出参数:MI_IPU API 错误码,具体参考《MI_IPU_API手册》

9. 销毁IPU设备


MI_IPU_DestroyDevice();

输入参数:空

输出参数:MI_IPU API 错误码,具体参考《MI_IPU_API手册