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); |
|---|
拷贝数据时需注意:
-
模型中input formats设置为BGR 或者RGB 时,stride不要做alignment,做了alignment反而有问题。
-
模型中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。
- 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手册