Hi3559AV100 NNIE开发(6)RFCN中NNIE实现关键线程函数->SAMPLE_SVP_NNIE_Rfcn_ViToVo()进行数据流分析

  前面随笔给出了NNIE开发的基本知识,下面几篇随笔将着重于Mobilefacenet NNIE开发,实现mobilefacenet.wk的chip版本,并在Hi3559AV100上实现mobilefacenet网络功能,外接USB摄像头通过MPP平台输出至VO HDMI显示结果。下文是Hi3559AV100 NNIE开发(6)RFCN中实现关键线程函数->SAMPLE_SVP_NNIE_Rfcn_ViToVo()进行数据流分析,通过对线程函数分析,详细了解如何对.wk模型数据进行处理并弄清楚检测框绘制这些后处理的实现。

1、SAMPLE_SVP_NNIE_Rfcn_ViToVo()函数调用

  首先给出SAMPLE_SVP_NNIE_Rfcn_ViToVo()函数的调用,为后续分析提供参照:

 1 static pthread_t s_hNnieThread = 0; //全局定义
 2 
 3 HI_CHAR acThreadName[16] = {0};//局部定义
 4 
 5 
 6 /******************************************
 7         Create work thread
 8  ******************************************/
 9 snprintf(acThreadName, 16, "NNIE_ViToVo");
10 prctl(PR_SET_NAME, (unsigned long)acThreadName, 0,0,0);
11 pthread_create(&s_hNnieThread, 0, SAMPLE_SVP_NNIE_Rfcn_ViToVo, NULL);    

  其中prctl函数的定义如下,其中PR_SET_NAME表示使用(char *) arg2所指向的位置中的值设置调用线程的名称。

1 int prctl(int option, unsigned long arg2, unsigned long arg3,
2                  unsigned long arg4, unsigned long arg5);

2、SAMPLE_SVP_NNIE_Rfcn_ViToVo()函数实现具体分析

  下面讲分析SAMPLE_SVP_NNIE_Rfcn_ViToVo(HI_VOID* pArgs)函数的内部实现,具体设计到5个函数,实现的功能已经注释出来,具体如下:

 1 static HI_VOID* SAMPLE_SVP_NNIE_Rfcn_ViToVo(HI_VOID* pArgs)
 2 {
 3 
 4     .... //参数定义
 5 
 6     while (HI_FALSE == s_bNnieStopSignal)
 7     {    
 8         //用户从通道获取一帧处理完成的图像 通道1-输出stExtFrmInfo
 9         s32Ret = HI_MPI_VPSS_GetChnFrame(s32VpssGrp,
10                                          as32VpssChn[1],
11                                          &stExtFrmInfo,
12                                          s32MilliSec);
13         ......
14 
15         //用户从通道获取一帧处理完成的图像 通道0-输出stBaseFrmInfo
16         s32Ret = HI_MPI_VPSS_GetChnFrame(s32VpssGrp, 
17                                          as32VpssChn[0], 
18                                          &stBaseFrmInfo, 
19                                          s32MilliSec);
20         ......
21 
22         //关键处理函数
23         s32Ret = SAMPLE_SVP_NNIE_Rfcn_Proc(
24                      pstParam,
25                      pstSwParam, 
26                      &stExtFrmInfo,
27                      stBaseFrmInfo.stVFrame.u32Width,
28                      stBaseFrmInfo.stVFrame.u32Height);
29         ......
30 
31         //Draw rect
32         s32Ret = SAMPLE_COMM_SVP_NNIE_FillRect(
33                      &stBaseFrmInfo, 
34                      &(pstSwParam->stRect), 
35                      0x0000FF00); //绿色
36     ......    
37 
38         //将视频图像送入指定输出通道显示。
39         s32Ret = HI_MPI_VO_SendFrame(voLayer, 
40                                      voChn, 
41                                      &stBaseFrmInfo, 
42                                      s32MilliSec);
43     ......
44     
45 BASE_RELEASE:
46         s32Ret = HI_MPI_VPSS_ReleaseChnFrame(s32VpssGrp,as32VpssChn[0], &stBaseFrmInfo);
47     ......
48 
49 EXT_RELEASE:
50         s32Ret = HI_MPI_VPSS_ReleaseChnFrame(s32VpssGrp,as32VpssChn[1], &stExtFrmInfo);
51     .......
52 
53     }
54 
55     return HI_NULL;
56 }

2.1、HI_MPI_VPSS_GetChnFrame的实现

  下面给出HI_MPI_VPSS_GetChnFrame函数的参数调用与实现过程(其中VPSS双通道输出),具体功能为从通道获取一帧处理完成的图像:

 1     HI_S32 s32VpssGrp = 0;
 2     HI_S32 as32VpssChn[] = {VPSS_CHN0, VPSS_CHN1};
 3     VIDEO_FRAME_INFO_S stBaseFrmInfo;
 4     VIDEO_FRAME_INFO_S stExtFrmInfo;
 5     HI_S32 s32MilliSec = 20000;
 6 
 7         //用户从通道获取一帧处理完成的图像 通道1-输出stExtFrmInfo
 8      s32Ret = HI_MPI_VPSS_GetChnFrame(s32VpssGrp,
 9                                 as32VpssChn[1],
10                                 &stExtFrmInfo,
11                                 s32MilliSec);
12 
13         //用户从通道获取一帧处理完成的图像 通道0-输出stBaseFrmInfo
14      s32Ret = HI_MPI_VPSS_GetChnFrame(s32VpssGrp, 
15                                 as32VpssChn[0], 
16                                 &stBaseFrmInfo, 
17                                 s32MilliSec);
18 
19     //函数定义
20     HI_S32 HI_MPI_VPSS_GetChnFrame(VPSS_GRP VpssGrp, VPSS_CHN VpssChn,
21 VIDEO_FRAME_INFO_S *pstVideoFrame, HI_S32 s32MilliSec);

2.2、SAMPLE_SVP_NNIE_Rfcn_Proc的实现-NNIE数据处理

  SAMPLE_SVP_NNIE_Rfcn_Proc函数实现是整个RFCN NNIE数据处理过程的Key Point,检测加框等信息来源处,现给出函数参数及实现分析:

 1      SAMPLE_SVP_NNIE_PARAM_S *pstParam;
 2     SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S *pstSwParam;
 3 
 4     pstParam = &s_stRfcnNnieParam;
 5     pstSwParam = &s_stRfcnSoftwareParam;
 6 /*  
 7     s_stRfcnNnieParam及s_stRfcnSoftwareParam参数涉及前述操作
 8 
 9     s_stRfcnNnieParam.pstModel = &s_stRfcnModel.stModel;
10     s_stRfcnSoftwareParam.apcRpnDataLayerName[0] = "rpn_cls_score";
11     s_stRfcnSoftwareParam.apcRpnDataLayerName[1] = "rpn_bbox_pred";
12     s32Ret = SAMPLE_SVP_NNIE_Rfcn_ParamInit(&stNnieCfg,
13                                     &s_stRfcnNnieParam,
14                                         &s_stRfcnSoftwareParam);
15 */
16 
17 
18     VIDEO_FRAME_INFO_S stBaseFrmInfo;
19     VIDEO_FRAME_INFO_S stExtFrmInfo;
20 
21 //其中stBaseFrmInfo参数与stExtFrmInfo参数是经过下述函数输出得到
22 /*
23     s32Ret = HI_MPI_VPSS_GetChnFrame(s32VpssGrp,
24                              as32VpssChn[1],
25                             &stExtFrmInfo,
26                             s32MilliSec);
27     s32Ret = HI_MPI_VPSS_GetChnFrame(s32VpssGrp, 
28                              as32VpssChn[0], 
29                             &stBaseFrmInfo, 
30                                 s32MilliSec);
31 */
32 
33 
34     s32Ret = SAMPLE_SVP_NNIE_Rfcn_Proc(
35                      pstParam,
36                      pstSwParam, 
37                      &stExtFrmInfo,
38                      stBaseFrmInfo.stVFrame.u32Width,
39                      stBaseFrmInfo.stVFrame.u32Height);    

  随之给出SAMPLE_SVP_NNIE_Rfcn_Proc函数的定义及内部实现过程:

 1 static HI_S32 SAMPLE_SVP_NNIE_Rfcn_Proc(
 2     SAMPLE_SVP_NNIE_PARAM_S *pstParam,
 3     SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S *pstSwParam, 
 4     VIDEO_FRAME_INFO_S* pstExtFrmInfo,
 5     HI_U32 u32BaseWidth,HI_U32 u32BaseHeight)
 6 {
 7 
 8     ......参数定义
 9 
10     s32Ret = SAMPLE_SVP_NNIE_Forward(pstParam,
11                                      &stInputDataIdx,
12                                      &stProcSegIdx,
13                                      HI_TRUE);
14     
15     ......
16 
17     /*RPN*/
18     s32Ret = SAMPLE_SVP_NNIE_Rfcn_Rpn(pstParam, 
19                                       pstSwParam);
20     
21 
22     if(0 != pstSwParam->stRpnBbox.unShape.stWhc.u32Height)
23     {
24 
25         s32Ret = SAMPLE_SVP_NNIE_ForwardWithBbox(
26                     pstParam,
27                     &stInputDataIdx,
28                     &pstSwParam->stRpnBbox,
29                     &stProcSegIdx,
30                     HI_TRUE);
31        ......
32         
33         s32Ret = SAMPLE_SVP_NNIE_ForwardWithBbox(
34                     pstParam,
35                     &stInputDataIdx,
36                     &pstSwParam->stRpnBbox,
37                     &stProcSegIdx,
38                     HI_TRUE);
39     ......
40 
41         s32Ret = SAMPLE_SVP_NNIE_Rfcn_GetResult(pstParam,
42                                     pstSwParam);
43         
44     }
45     else
46        { ...... }
47     s32Ret = SAMPLE_SVP_NNIE_RoiToRect(
48                 &(pstSwParam->stDstScore),
49                 &(pstSwParam->stDstRoi), 
50                 &(pstSwParam->stClassRoiNum), 
51                 pstSwParam->af32ScoreThr,
52                 HI_TRUE,
53                 &(pstSwParam->stRect),
54                 pstExtFrmInfo->stVFrame.u32Width, 
55                 pstExtFrmInfo->stVFrame.u32Height,
56                 u32BaseWidth,
57                 u32BaseHeight);
58     
59     ......
60 
61     return s32Ret;
62 
63 }

2.2.1、SAMPLE_SVP_NNIE_Forward子函数分析

   首先给出函数的调用及参数细节,便于分析函数功能:

 1     其中在SAMPLE_SVP_NNIE_Rfcn_ViToVo函数中:
 2     SAMPLE_SVP_NNIE_PARAM_S *pstParam;
 3     pstParam = &s_stRfcnNnieParam;  //此值传给SAMPLE_SVP_NNIE_Rfcn_Proc
 4     
 5 
 6 
 7     static HI_S32 SAMPLE_SVP_NNIE_Rfcn_Proc(
 8         SAMPLE_SVP_NNIE_PARAM_S *pstParam,      /*这个参数传进来之后经过了SP420赋值然后送入
 9                                                 SAMPLE_SVP_NNIE_Forward*/
10         SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S *pstSwParam, 
11         VIDEO_FRAME_INFO_S* pstExtFrmInfo,
12         HI_U32 u32BaseWidth,HI_U32 u32BaseHeight)
13 
14     ......
15 
16     SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S stInputDataIdx = {0};
17     SAMPLE_SVP_NNIE_PROCESS_SEG_INDEX_S stProcSegIdx = {0};
18 
19     stInputDataIdx.u32SegIdx = 0;
20     stInputDataIdx.u32NodeIdx = 0;
21 
22      /*SP420*/
23     pstParam->astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].u64VirAddr = pstExtFrmInfo->stVFrame.u64VirAddr[0];
24     pstParam->astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].u64PhyAddr = pstExtFrmInfo->stVFrame.u64PhyAddr[0];
25     pstParam->astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].u32Stride  = pstExtFrmInfo->stVFrame.u32Stride[0];
26 
27     /*NNIE process 0-th seg*/
28     stProcSegIdx.u32SegIdx = 0;
29 
30      /*NNIE process 0-th seg*/
31     stProcSegIdx.u32SegIdx = 0;
32     s32Ret = SAMPLE_SVP_NNIE_Forward(pstParam,
33                                      &stInputDataIdx,
34                                      &stProcSegIdx,
35                                      HI_TRUE);
36 
37     ......
38 
39     //函数定义
40     static HI_S32 SAMPLE_SVP_NNIE_Forward(
41                 SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam,
42                 SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S* pstInputDataIdx,
43                 SAMPLE_SVP_NNIE_PROCESS_SEG_INDEX_S* pstProcSegIdx,
44                 HI_BOOL bInstant)

  此函数如其名,主要实现了NNIE forward功能,下面给出调用具体函数:

 1 /******************************************************************************
 2 * function : NNIE Forward
 3 ******************************************************************************/
 4 static HI_S32 SAMPLE_SVP_NNIE_Forward(SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam,
 5     SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S* pstInputDataIdx,
 6     SAMPLE_SVP_NNIE_PROCESS_SEG_INDEX_S* pstProcSegIdx,HI_BOOL bInstant)
 7 {
 8     ......
 9 
10     SAMPLE_COMM_SVP_FlushCache(
11     pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].stTskBuf.u64PhyAddr,
12     (HI_VOID *) pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].stTskBuf.u64VirAddr,
13      pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].stTskBuf.u32Size);
14 
15     /*set input blob according to node name*/
16     if(pstInputDataIdx->u32SegIdx != pstProcSegIdx->u32SegIdx)
17     {
18         for(i = 0; i < pstNnieParam->pstModel->astSeg[pstProcSegIdx->u32SegIdx].u16SrcNum; i++)
19         {
20             ......
21         }
22     }
23 
24     /*NNIE_Forward  多节点输入输出的 CNN 类型网络预测。 
25     对输入样本(s)进行CNN预测,对对应样本(s)进行输出响应*/
26     s32Ret = HI_MPI_SVP_NNIE_Forward(
27         &hSvpNnieHandle,
28         pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astSrc,
29         pstNnieParam->pstModel, 
30         pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst,
31         &pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx], 
32         bInstant);
33     
34 
35     if(bInstant)
36     {
37         /*Wait NNIE finish,,,,enNnieId 执行网络段的 NNIE 引擎 ID。 */
38         while(HI_ERR_SVP_NNIE_QUERY_TIMEOUT == (s32Ret = HI_MPI_SVP_NNIE_Query(pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].enNnieId,
39             hSvpNnieHandle, &bFinish, HI_TRUE)))
40         {
41             ......
42         }
43     }
44 
45     bFinish = HI_FALSE;
46     for(i = 0; i < pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].u32DstNum; i++)
47     {
48         if(SVP_BLOB_TYPE_SEQ_S32 == pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].enType)
49         {
50             ......
51             
52             SAMPLE_COMM_SVP_FlushCache(
53     pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64PhyAddr,
54     (HI_VOID *) pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64VirAddr,
55     u32TotalStepNum*pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Stride);
56 
57         }
58         else
59         {
60 
61             SAMPLE_COMM_SVP_FlushCache(pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64PhyAddr,
62                 (HI_VOID *) pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64VirAddr,
63                 pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Num*
64                 pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].unShape.stWhc.u32Chn*
65                 pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].unShape.stWhc.u32Height*
66                 pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Stride);
67         }
68     }
69 
70     return s32Ret;
71 }

2.2.2、SAMPLE_SVP_NNIE_Rfcn_Rpn子函数分析

   下面给出sample_svp_NNIE_Rfcn_Rpn子函数分析,此函数主要是用于rpn相关,下面给出参数调用及函数分析:

 1     其中在SAMPLE_SVP_NNIE_Rfcn_ViToVo函数中:
 2     SAMPLE_SVP_NNIE_PARAM_S *pstParam; //此值传给SAMPLE_SVP_NNIE_Rfcn_Proc
 3     pstParam = &s_stRfcnNnieParam;  //此值传给SAMPLE_SVP_NNIE_Rfcn_Proc
 4 
 5     SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S *pstSwParam;
 6     pstSwParam = &s_stRfcnSoftwareParam;
 7 
 8     static HI_S32 SAMPLE_SVP_NNIE_Rfcn_Proc(
 9         SAMPLE_SVP_NNIE_PARAM_S *pstParam,      /*这个参数传进来之后经过了SP420赋值然后送入
10                                                 SAMPLE_SVP_NNIE_Forward*/
11         SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S *pstSwParam,    //直接传给SAMPLE_SVP_NNIE_Rfcn_Rpn函数
12         VIDEO_FRAME_INFO_S* pstExtFrmInfo,
13         HI_U32 u32BaseWidth,HI_U32 u32BaseHeight)
14 
15      /*SP420*/
16     pstParam->astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].u64VirAddr = pstExtFrmInfo->stVFrame.u64VirAddr[0];
17     pstParam->astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].u64PhyAddr = pstExtFrmInfo->stVFrame.u64PhyAddr[0];
18     pstParam->astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].u32Stride  = pstExtFrmInfo->stVFrame.u32Stride[0];
19 
20     此后,pstParam参数先传入SAMPLE_SVP_NNIE_Forward函数进行处理,随后传入SAMPLE_SVP_NNIE_Rfcn_Rpn函数
21 
22         /*RPN*/
23     s32Ret = SAMPLE_SVP_NNIE_Rfcn_Rpn(pstParam, 
24                                       pstSwParam);
25 
26     // 函数调用  ,用于used to do rpn
27     HI_S32 SAMPLE_SVP_NNIE_Rfcn_Rpn(
28         SAMPLE_SVP_NNIE_PARAM_S*pstNnieParam,
29         SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S* pstSoftwareParam)

  下面给出sample_svp_NNIE_Rfcn_Rpn子函数内部实现,此函数虽然传入两个参数,但是没有对pstNnieParam参数进行任何处理,完成的pstSoftwareParam结构体内大量参数的的处理 ,主要是调用了SVP_NNIE_Rpn函数与SAMPLE_COMM_SVP_FlushCache函数:

 1 HI_S32 SAMPLE_SVP_NNIE_Rfcn_Rpn(SAMPLE_SVP_NNIE_PARAM_S*pstNnieParam,
 2     SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S* pstSoftwareParam)
 3 {
 4     HI_S32 s32Ret = HI_SUCCESS;
 5     s32Ret = SVP_NNIE_Rpn(pstSoftwareParam->aps32Conv,pstSoftwareParam->u32NumRatioAnchors,
 6         pstSoftwareParam->u32NumScaleAnchors,pstSoftwareParam->au32Scales,
 7         pstSoftwareParam->au32Ratios,pstSoftwareParam->u32OriImHeight,
 8         pstSoftwareParam->u32OriImWidth,pstSoftwareParam->au32ConvHeight,
 9         pstSoftwareParam->au32ConvWidth,pstSoftwareParam->au32ConvChannel,
10         pstSoftwareParam->u32ConvStride,pstSoftwareParam->u32MaxRoiNum,
11         pstSoftwareParam->u32MinSize,pstSoftwareParam->u32SpatialScale,
12         pstSoftwareParam->u32NmsThresh,pstSoftwareParam->u32FilterThresh,
13         pstSoftwareParam->u32NumBeforeNms,(HI_U32*)pstSoftwareParam->stRpnTmpBuf.u64VirAddr,
14         (HI_S32*)pstSoftwareParam->stRpnBbox.u64VirAddr,
15         &pstSoftwareParam->stRpnBbox.unShape.stWhc.u32Height);
16     SAMPLE_COMM_SVP_FlushCache(pstSoftwareParam->stRpnBbox.u64PhyAddr,
17         (HI_VOID *) pstSoftwareParam->stRpnBbox.u64VirAddr,
18         pstSoftwareParam->stRpnBbox.u32Num*
19         pstSoftwareParam->stRpnBbox.unShape.stWhc.u32Chn*
20         pstSoftwareParam->stRpnBbox.unShape.stWhc.u32Height*
21         pstSoftwareParam->stRpnBbox.u32Stride);
22     SAMPLE_SVP_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
23         "Error,SVP_NNIE_Rpn failed!\n");
24     return s32Ret;
25 }

2.2.3、SAMPLE_SVP_NNIE_ForwardWithBbox子函数分析

  完成前面几个函数后,通过if判断,当满足条件后执行SAMPLE_SVP_NNIE_ForwardWithBbox函数:

1 if(0 != pstSwParam->stRpnBbox.unShape.stWhc.u32Height)

  否则不满足if条件的时候,将执行下面赋值语句:

1         for (i = 0; i < pstSwParam->stClassRoiNum.unShape.stWhc.u32Width; i++)
2         {
3             *(((HI_U32*)(HI_UL)pstSwParam->stClassRoiNum.u64VirAddr)+i) = 0;
4         }

  而SAMPLE_SVP_NNIE_ForwardWithBbox执行了有两次,分别是对不同NNIE process x-th seg进行处理,具体如下:

 1     其中在SAMPLE_SVP_NNIE_Rfcn_ViToVo函数中:
 2     SAMPLE_SVP_NNIE_PARAM_S *pstParam; //此值传给SAMPLE_SVP_NNIE_Rfcn_Proc
 3     pstParam = &s_stRfcnNnieParam;  //此值传给SAMPLE_SVP_NNIE_Rfcn_Proc
 4 
 5     SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S *pstSwParam;
 6     pstSwParam = &s_stRfcnSoftwareParam;
 7 
 8     static HI_S32 SAMPLE_SVP_NNIE_Rfcn_Proc(
 9         SAMPLE_SVP_NNIE_PARAM_S *pstParam,      /*这个参数传进来之后经过了SP420赋值然后送入
10                                                 SAMPLE_SVP_NNIE_Forward*/
11         SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S *pstSwParam,    /*直接传给SAMPLE_SVP_NNIE_Rfcn_Rpn函数
12                                                             随后传给SAMPLE_SVP_NNIE_ForwardWithBbox函数*/
13         VIDEO_FRAME_INFO_S* pstExtFrmInfo,
14         HI_U32 u32BaseWidth,HI_U32 u32BaseHeight)
15 
16     /*RPN*/
17     s32Ret = SAMPLE_SVP_NNIE_Rfcn_Rpn(pstParam, 
18                                       pstSwParam);  /*此函数完成pstSwParam的相关赋值,并
19                                                      传给SAMPLE_SVP_NNIE_ForwardWithBbox函数*/
20 
21 
22     /*在SAMPLE_SVP_NNIE_ForwardWithBbox函数前面,已经有了下面参数的赋值,
23     并且传入至SAMPLE_SVP_NNIE_Forward函数*/
24         stInputDataIdx.u32SegIdx = 0;
25         stInputDataIdx.u32NodeIdx = 0;
26 
27      s32Ret = SAMPLE_SVP_NNIE_Forward(pstParam,
28                                      &stInputDataIdx, //stInputDataIdx参数已经用于此函数中
29                                      &stProcSegIdx,
30                                      HI_TRUE);
31 
32 
33      //函数调用
34     /*NNIE process 1-th seg, the input data comes from 3-rd report node of 0-th seg,
35      the input roi comes from RPN results*/
36     stInputDataIdx.u32SegIdx = 0;
37     stInputDataIdx.u32NodeIdx = 3;
38 
39     stProcSegIdx.u32SegIdx = 1;
40 
41     s32Ret = SAMPLE_SVP_NNIE_ForwardWithBbox(
42                 pstParam,
43                 &stInputDataIdx,
44                 &pstSwParam->stRpnBbox,
45                 &stProcSegIdx,
46                 HI_TRUE);
47 
48 
49     /*NNIE process 2-nd seg, the input data comes from 4-th report node of 0-th seg
50       the input roi comes from RPN results*/
51     stInputDataIdx.u32SegIdx = 0;
52     stInputDataIdx.u32NodeIdx = 4;
53 
54     stProcSegIdx.u32SegIdx = 2;
55 
56     s32Ret = SAMPLE_SVP_NNIE_ForwardWithBbox(
57                 pstParam,
58                 &stInputDataIdx,
59                 &pstSwParam->stRpnBbox,
60                 &stProcSegIdx,
61                 HI_TRUE);

  下面给出函数具体实现:

 1 /******************************************************************************
 2 * function : NNIE ForwardWithBbox
 3 ******************************************************************************/
 4 static HI_S32 SAMPLE_SVP_NNIE_ForwardWithBbox(
 5             SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam,
 6             SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S* pstInputDataIdx,
 7             SVP_SRC_BLOB_S astBbox[],
 8             SAMPLE_SVP_NNIE_PROCESS_SEG_INDEX_S* pstProcSegIdx,
 9             HI_BOOL bInstant)
10 {
11     ......
12 
13     SAMPLE_COMM_SVP_FlushCache(pstNnieParam->astForwardWithBboxCtrl[pstProcSegIdx->u32SegIdx].stTskBuf.u64PhyAddr,
14         (HI_VOID *) pstNnieParam->astForwardWithBboxCtrl[pstProcSegIdx->u32SegIdx].stTskBuf.u64VirAddr,
15         pstNnieParam->astForwardWithBboxCtrl[pstProcSegIdx->u32SegIdx].stTskBuf.u32Size);
16 
17     /*set input blob according to node name*/
18     if(pstInputDataIdx->u32SegIdx != pstProcSegIdx->u32SegIdx)
19     {
20         for(i = 0; i < pstNnieParam->pstModel->astSeg[pstProcSegIdx->u32SegIdx].u16SrcNum; i++)
21         {
22             for(j = 0; j < pstNnieParam->pstModel->astSeg[pstInputDataIdx->u32SegIdx].u16DstNum; j++)
23             {
24                 ......
25             }
26             ......
27         }
28     }
29     /*NNIE_ForwardWithBbox*/
30     s32Ret = HI_MPI_SVP_NNIE_ForwardWithBbox(
31         &hSvpNnieHandle,
32         pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astSrc,
33         astBbox,
34         pstNnieParam->pstModel,  //网络类型只支持ROI/PSROI
35         pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst,
36         &pstNnieParam->astForwardWithBboxCtrl[pstProcSegIdx->u32SegIdx], 
37         bInstant);
38 
39     ......
40 
41     if(bInstant)
42     {
43         /*Wait NNIE finish*/
44         while(HI_ERR_SVP_NNIE_QUERY_TIMEOUT == (s32Ret = HI_MPI_SVP_NNIE_Query(pstNnieParam->astForwardWithBboxCtrl[pstProcSegIdx->u32SegIdx].enNnieId,
45             hSvpNnieHandle, &bFinish, HI_TRUE)))
46         {
47             ......
48         }
49     }
50 
51     bFinish = HI_FALSE;
52 
53 
54     for(i = 0; i < pstNnieParam->astForwardWithBboxCtrl[pstProcSegIdx->u32SegIdx].u32DstNum; i++)
55     {
56         if(SVP_BLOB_TYPE_SEQ_S32 == pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].enType)
57         {
58             ......
59             
60             SAMPLE_COMM_SVP_FlushCache(pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64PhyAddr,
61                 (HI_VOID *) pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64VirAddr,
62                 u32TotalStepNum*pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Stride);
63         }
64         else
65         {
66             SAMPLE_COMM_SVP_FlushCache(pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64PhyAddr,
67                 (HI_VOID *) pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64VirAddr,
68                 pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Num*
69                 pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].unShape.stWhc.u32Chn*
70                 pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].unShape.stWhc.u32Height*
71                 pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Stride);
72         }
73     }
74 
75     return s32Ret;
76 }

2.2.4、SAMPLE_SVP_NNIE_Rfcn_GetResult子函数分析

  下一个子函数是获得NNIE Rfcn的结果,前提需要保证网络结构和输入数据保持一致,函数调用如下:

 

 1     其中在SAMPLE_SVP_NNIE_Rfcn_ViToVo函数中:
 2     SAMPLE_SVP_NNIE_PARAM_S *pstParam; //此值传给SAMPLE_SVP_NNIE_Rfcn_Proc
 3     pstParam = &s_stRfcnNnieParam;  //此值传给SAMPLE_SVP_NNIE_Rfcn_Proc
 4 
 5     SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S *pstSwParam;
 6     pstSwParam = &s_stRfcnSoftwareParam;
 7 
 8     static HI_S32 SAMPLE_SVP_NNIE_Rfcn_Proc(
 9         SAMPLE_SVP_NNIE_PARAM_S *pstParam,      /*这个参数传进来之后经过了SP420赋值然后送入
10                                                 SAMPLE_SVP_NNIE_Forward*/
11         SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S *pstSwParam,    /*直接传给SAMPLE_SVP_NNIE_Rfcn_Rpn函数
12                                                             随后传给SAMPLE_SVP_NNIE_ForwardWithBbox函数*/
13         VIDEO_FRAME_INFO_S* pstExtFrmInfo,
14         HI_U32 u32BaseWidth,HI_U32 u32BaseHeight)
15 
16     pstParam->astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].u64VirAddr = pstExtFrmInfo->stVFrame.u64VirAddr[0];
17     pstParam->astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].u64PhyAddr = pstExtFrmInfo->stVFrame.u64PhyAddr[0];
18     pstParam->astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].u32Stride  = pstExtFrmInfo->stVFrame.u32Stride[0];
19 
20 
21     s32Ret = SAMPLE_SVP_NNIE_Forward(pstParam,   //参数调用
22                                      &stInputDataIdx,
23                                      &stProcSegIdx,
24                                      HI_TRUE);
25     
26     /*RPN*/
27     s32Ret = SAMPLE_SVP_NNIE_Rfcn_Rpn(pstParam,   //参数调用
28                                       pstSwParam);  //参数调用
29 
30 
31     s32Ret = SAMPLE_SVP_NNIE_ForwardWithBbox(
32                 pstParam,    //参数调用
33                 &stInputDataIdx,
34                 &pstSwParam->stRpnBbox,
35                 &stProcSegIdx,
36                 HI_TRUE);
37 
38 
39 
40     //函数调用
41 
42     /*GetResult*/
43     /*if user has changed net struct, please make sure SAMPLE_SVP_NNIE_Rfcn_GetResult
44      function's input datas are correct*/
45     s32Ret = SAMPLE_SVP_NNIE_Rfcn_GetResult(pstParam,
46                                             pstSwParam);
47 
48     //函数调用
49     HI_S32 SAMPLE_SVP_NNIE_Rfcn_GetResult(SAMPLE_SVP_NNIE_PARAM_S*pstNnieParam,
50                                       SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S* pstSoftwareParam)

  此函数作用是为了获取NNIE  RFCN的结果,其核心是实现了SVP_NNIE_Rfcn_GetResult函数,具体如下:

 1 HI_S32 SAMPLE_SVP_NNIE_Rfcn_GetResult(SAMPLE_SVP_NNIE_PARAM_S*pstNnieParam,
 2                                       SAMPLE_SVP_NNIE_RFCN_SOFTWARE_PARAM_S* pstSoftwareParam)
 3 {
 4     HI_S32 s32Ret = HI_SUCCESS;
 5     HI_U32 i = 0;
 6     HI_S32* ps32Proposal = (HI_S32*)pstSoftwareParam->stRpnBbox.u64VirAddr;
 7 
 8     ......
 9 
10     for(i = 0; i < pstSoftwareParam->stRpnBbox.unShape.stWhc.u32Height; i++)
11     {
12         *(ps32Proposal+SAMPLE_SVP_NNIE_COORDI_NUM*i) /= SAMPLE_SVP_NNIE_QUANT_BASE;
13         *(ps32Proposal+SAMPLE_SVP_NNIE_COORDI_NUM*i+1) /= SAMPLE_SVP_NNIE_QUANT_BASE;
14         *(ps32Proposal+SAMPLE_SVP_NNIE_COORDI_NUM*i+2) /= SAMPLE_SVP_NNIE_QUANT_BASE;
15         *(ps32Proposal+SAMPLE_SVP_NNIE_COORDI_NUM*i+3) /= SAMPLE_SVP_NNIE_QUANT_BASE;
16     }
17     //this function is used to get RFCN result
18     s32Ret = SVP_NNIE_Rfcn_GetResult(
19                  (HI_S32*)pstNnieParam->astSegData[1].astDst[0].u64VirAddr,
20                  pstNnieParam->astSegData[1].astDst[0].u32Stride,
21                  (HI_S32*)pstNnieParam->astSegData[2].astDst[0].u64VirAddr,
22                  pstNnieParam->astSegData[2].astDst[0].u32Stride,
23                  (HI_S32*)pstSoftwareParam->stRpnBbox.u64VirAddr,
24                  pstSoftwareParam->stRpnBbox.unShape.stWhc.u32Height,
25                  pstSoftwareParam->au32ConfThresh,pstSoftwareParam->u32MaxRoiNum,
26                  pstSoftwareParam->u32ClassNum,pstSoftwareParam->u32OriImWidth,
27                  pstSoftwareParam->u32OriImHeight,pstSoftwareParam->u32ValidNmsThresh,
28                  (HI_U32*)pstSoftwareParam->stGetResultTmpBuf.u64VirAddr,
29                  (HI_S32*)pstSoftwareParam->stDstScore.u64VirAddr,
30                  (HI_S32*)pstSoftwareParam->stDstRoi.u64VirAddr,
31                  (HI_S32*)pstSoftwareParam->stClassRoiNum.u64VirAddr);
32     ......
33     return s32Ret;
34 }

2.2.5、SAMPLE_SVP_NNIE_RoiToRect子函数分析

  SAMPLE_SVP_NNIE_Rfcn_Proc函数最后一个子函数为对ROI(感兴趣区域画框),RFNC对21类物体进行目标识别,具体调用及实现如下:

 1     /*draw result, this sample has 21 classes:
 2      class 0:background     class 1:plane           class 2:bicycle
 3      class 3:bird           class 4:boat            class 5:bottle
 4      class 6:bus            class 7:car             class 8:cat
 5      class 9:chair          class10:cow             class11:diningtable
 6      class 12:dog           class13:horse           class14:motorbike
 7      class 15:person        class16:pottedplant     class17:sheep
 8      class 18:sofa          class19:train           class20:tvmonitor*/
 9     s32Ret = SAMPLE_SVP_NNIE_RoiToRect(
10                  &(pstSwParam->stDstScore),
11                  &(pstSwParam->stDstRoi), 
12                  &(pstSwParam->stClassRoiNum), 
13                  pstSwParam->af32ScoreThr,
14                  HI_TRUE,
15                  &(pstSwParam->stRect),
16                  pstExtFrmInfo->stVFrame.u32Width, 
17                  pstExtFrmInfo->stVFrame.u32Height,
18                  u32BaseWidth,
19                  u32BaseHeight);
20 
21 /******************************************************************************
22 * function : roi to rect
23 ******************************************************************************/
24 HI_S32 SAMPLE_SVP_NNIE_RoiToRect(SVP_BLOB_S *pstDstScore,
25                                  SVP_BLOB_S *pstDstRoi, SVP_BLOB_S *pstClassRoiNum, HI_FLOAT *paf32ScoreThr,
26                                  HI_BOOL bRmBg,SAMPLE_SVP_NNIE_RECT_ARRAY_S *pstRect,
27                                  HI_U32 u32SrcWidth, HI_U32 u32SrcHeight,HI_U32 u32DstWidth,HI_U32 u32DstHeight)
28 {
29     HI_U32 i = 0, j = 0;
30     HI_U32 u32RoiNumBias = 0;
31     HI_U32 u32ScoreBias = 0;
32     HI_U32 u32BboxBias = 0;
33     HI_FLOAT f32Score = 0.0f;
34     HI_S32* ps32Score = (HI_S32*)pstDstScore->u64VirAddr;
35     HI_S32* ps32Roi = (HI_S32*)pstDstRoi->u64VirAddr;
36     HI_S32* ps32ClassRoiNum = (HI_S32*)pstClassRoiNum->u64VirAddr;
37     HI_U32 u32ClassNum = pstClassRoiNum->unShape.stWhc.u32Width;
38     HI_U32 u32RoiNumTmp = 0;
39 
40     .......
41     
42     pstRect->u32TotalNum = 0;
43     pstRect->u32ClsNum = u32ClassNum;
44     if (bRmBg)
45     {
46         pstRect->au32RoiNum[0] = 0;
47         u32RoiNumBias += ps32ClassRoiNum[0];
48         for (i = 1; i < u32ClassNum; i++)
49         {
50             u32ScoreBias = u32RoiNumBias;
51             u32BboxBias = u32RoiNumBias * SAMPLE_SVP_NNIE_COORDI_NUM;
52             u32RoiNumTmp = 0;
53             /*if the confidence score greater than result thresh, the result will be drawed*/
54             if(((HI_FLOAT)ps32Score[u32ScoreBias] / SAMPLE_SVP_NNIE_QUANT_BASE >=
55                     paf32ScoreThr[i])  &&  (ps32ClassRoiNum[i] != 0))
56             {
57                 for (j = 0; j < (HI_U32)ps32ClassRoiNum[i]; j++)
58                 {
59                     /*Score is descend order*/
60                     f32Score = (HI_FLOAT)ps32Score[u32ScoreBias + j] / SAMPLE_SVP_NNIE_QUANT_BASE;
61                     if ((f32Score < paf32ScoreThr[i]) || (u32RoiNumTmp >= SAMPLE_SVP_NNIE_MAX_ROI_NUM_OF_CLASS))
62                     {
63                         break;
64                     }
65 
66                     pstRect->astRect[i][u32RoiNumTmp].astPoint[0].s32X = (HI_U32)((HI_FLOAT)ps32Roi[u32BboxBias + j*SAMPLE_SVP_NNIE_COORDI_NUM] / (HI_FLOAT)u32SrcWidth * (HI_FLOAT)u32DstWidth) & (~1) ;
67                     pstRect->astRect[i][u32RoiNumTmp].astPoint[0].s32Y = (HI_U32)((HI_FLOAT)ps32Roi[u32BboxBias + j*SAMPLE_SVP_NNIE_COORDI_NUM + 1] / (HI_FLOAT)u32SrcHeight * (HI_FLOAT)u32DstHeight) & (~1);
68 
69                     pstRect->astRect[i][u32RoiNumTmp].astPoint[1].s32X = (HI_U32)((HI_FLOAT)ps32Roi[u32BboxBias + j*SAMPLE_SVP_NNIE_COORDI_NUM + 2]/ (HI_FLOAT)u32SrcWidth * (HI_FLOAT)u32DstWidth) & (~1);
70                     pstRect->astRect[i][u32RoiNumTmp].astPoint[1].s32Y = pstRect->astRect[i][u32RoiNumTmp].astPoint[0].s32Y;
71 
72                     pstRect->astRect[i][u32RoiNumTmp].astPoint[2].s32X = pstRect->astRect[i][u32RoiNumTmp].astPoint[1].s32X;
73                     pstRect->astRect[i][u32RoiNumTmp].astPoint[2].s32Y = (HI_U32)((HI_FLOAT)ps32Roi[u32BboxBias + j*SAMPLE_SVP_NNIE_COORDI_NUM + 3] / (HI_FLOAT)u32SrcHeight * (HI_FLOAT)u32DstHeight) & (~1);
74 
75                     pstRect->astRect[i][u32RoiNumTmp].astPoint[3].s32X =  pstRect->astRect[i][u32RoiNumTmp].astPoint[0].s32X;
76                     pstRect->astRect[i][u32RoiNumTmp].astPoint[3].s32Y =  pstRect->astRect[i][u32RoiNumTmp].astPoint[2].s32Y;
77 
78                     u32RoiNumTmp++;
79                 }
80 
81             }
82 
83             pstRect->au32RoiNum[i] = u32RoiNumTmp;
84             pstRect->u32TotalNum += u32RoiNumTmp;
85             u32RoiNumBias += ps32ClassRoiNum[i];
86         }
87 
88     }
89     return HI_SUCCESS;
90 }

3、SAMPLE_COMM_SVP_NNIE_FillRect函数分析

  SAMPLE_COMM_SVP_NNIE_FillRect主要是配合VGS实现画框功能,具体调用和实现如下,函数功能已由注释给出:

 1     //Draw rect
 2     s32Ret = SAMPLE_COMM_SVP_NNIE_FillRect(
 3                  &stBaseFrmInfo, 
 4                  &(pstSwParam->stRect), 
 5                  0x0000FF00); //绿色
 6 
 7 HI_S32 SAMPLE_COMM_SVP_NNIE_FillRect(
 8     VIDEO_FRAME_INFO_S *pstFrmInfo,
 9     SAMPLE_SVP_NNIE_RECT_ARRAY_S* pstRect, 
10     HI_U32 u32Color)
11 {
12     VGS_HANDLE VgsHandle = -1;
13     HI_S32 s32Ret = HI_SUCCESS;
14     HI_U32 i,j;
15     VGS_TASK_ATTR_S stVgsTask;//定义 VGS task 的属性
16     VGS_ADD_COVER_S stVgsAddCover;//定义 VGS 上 COVER 的配置
17     static HI_U32 u32Frm = 0;
18     u32Frm++;
19     if (0 == pstRect->u32TotalNum)
20     {
21         return s32Ret;
22     }
23     s32Ret = HI_MPI_VGS_BeginJob(&VgsHandle); //启动一个 job。
24     if (s32Ret != HI_SUCCESS)
25     {
26         ......
27         return s32Ret;
28     }
29 
30     memcpy(&stVgsTask.stImgIn, pstFrmInfo, sizeof(VIDEO_FRAME_INFO_S));
31     memcpy(&stVgsTask.stImgOut, pstFrmInfo, sizeof(VIDEO_FRAME_INFO_S));
32 
33     stVgsAddCover.enCoverType = COVER_QUAD_RANGLE;//任意四边形COVER
34     stVgsAddCover.u32Color = u32Color;  //RGB888
35     stVgsAddCover.stQuadRangle.bSolid = HI_FALSE; //空心 COVER
36     stVgsAddCover.stQuadRangle.u32Thick = 2; //2 像素对齐
37 
38     for (i = 0; i < pstRect->u32ClsNum; i++)
39     {
40         for (j = 0; j < pstRect->au32RoiNum[i]; j++)
41         {
42             memcpy(stVgsAddCover.stQuadRangle.stPoint, pstRect->astRect[i][j].astPoint, sizeof(pstRect->astRect[i][j].astPoint));
43             
44             //做 COVER 任务时,输入输出图像为同一块 buffer
45             //往一个已经启动的 job 里添加打 COVER task。 task属性必须满足VGS的能力。
46             s32Ret = HI_MPI_VGS_AddCoverTask(VgsHandle, &stVgsTask, &stVgsAddCover);
47             if (s32Ret != HI_SUCCESS)
48             {
49                 SAMPLE_PRT("HI_MPI_VGS_AddCoverTask fail,Error(%#x)\n", s32Ret);
50                 HI_MPI_VGS_CancelJob(VgsHandle);
51                 return s32Ret;
52             }
53 
54         }
55 
56     }
57     //提交一个 job。
58     s32Ret = HI_MPI_VGS_EndJob(VgsHandle);
59     if (s32Ret != HI_SUCCESS)
60     {
61         SAMPLE_PRT("HI_MPI_VGS_EndJob fail,Error(%#x)\n", s32Ret);
62         HI_MPI_VGS_CancelJob(VgsHandle);
63         return s32Ret;
64     }
65 
66     return s32Ret;

4、HI_MPI_VO_SendFrame函数分析

  SAMPLE_SVP_NNIE_Rfcn_ViToVo线程函数执行的最后一个函数HI_MPI_VO_SendFrame,函数作用是将视频图像送入指定输出通道显示,具体调用如下:

1         s32Ret = HI_MPI_VO_SendFrame(voLayer, 
2                                      voChn, 
3                                      &stBaseFrmInfo, 
4                                      s32MilliSec);