前面随笔给出了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);

  

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

  1. 基于Hi3559AV100的SVP(NNIE)开发整体流程

    在之后的hi3559AV100板载开发中,除了走通V4L2->VDEC->VPSS->VO(HDMI)输出,还有需要进行神经网络的开发学习,进行如face detection的开发等 ...

  2. Hi3559AV100 NNIE开发(2)-RFCN(.wk)LoadModel及NNIE Init函数运行过程分析

    之后随笔将更多笔墨着重于NNIE开发系列,下文是关于Hi3559AV100 NNIE开发(2)-RFCN(.wk)LoadModel及NNIE Init函数运行过程分析,通过对LoadModel函数及 ...

  3. Hi3559AV100 NNIE开发(4)mobilefacenet.cfg参数配置挖坑解决与SVP_NNIE_Cnn实现分析

    前面随笔给出了NNIE开发的基本知识,下面几篇随笔将着重于Mobilefacenet NNIE开发,实现mobilefacenet.wk的chip版本,并在Hi3559AV100上实现mobilefa ...

  4. Hi3359AV100 NNIE开发(1)-RFCN demo LoadModel函数与参数解析

    之后随笔将更多笔墨着重于NNIE开发系列,下文是关于Hi3359AV100 NNIE开发(1)-RFCN demo LoadModel函数与参数解析,通过对LoadModel函数的解析,能够很好理解. ...

  5. Hi3559AV100 NNIE开发(3)RuyiStudio软件 .wk文件生成过程-mobilefacenet.cfg的参数配置

    之后随笔将更多笔墨着重于NNIE开发系列,下文是关于Hi3559AV100 NNIE开发(3)RuyiStudio软件 .wk文件生成过程-mobilefacenet.cfg的参数配置,目前项目需要对 ...

  6. Hi3559AV100 NNIE开发(5)mobilefacenet.wk仿真成功量化及与CNN_convert_bin_and_print_featuremap.py输出中间层数据对比过程

    前面随笔给出了NNIE开发的基本知识,下面几篇随笔将着重于Mobilefacenet NNIE开发,实现mobilefacenet.wk的chip版本,并在Hi3559AV100上实现mobilefa ...

  7. Hi3559AV100 NNIE开发(7) Ruyistudio 输出mobileface_func.wk与板载运行mobileface_chip.wk输出中间层数据对比

    前面随笔讲了关于NNIE的整个开发流程,并给出了Hi3559AV100 NNIE开发(5)mobilefacenet.wk仿真成功量化及与CNN_convert_bin_and_print_featu ...

  8. 前端开发:Javascript中的数组,常用方法解析

    前端开发:Javascript中的数组,常用方法解析 前言 Array是Javascript构成的一个重要的部分,它可以用来存储字符串.对象.函数.Number,它是非常强大的.因此深入了解Array ...

  9. 分享 Ionic 开发 Hybrid App 中遇到的问题以及后期发布 iOS/Android 的方方面面

    此篇文章主要整理了最近在使用 Ionic 开发 Hybrid App 过程中遇到的一些疑难点以及后期发布生成 iOS 和 Android 版本过程中的种种问题. 文章目录 Ionic 简介和项目需求介 ...

随机推荐

  1. Linux Bash Script loop

    Linux Bash Script loop shell 编程之流程控制 for 循环.while 循环和 until 循环 for var in item1 item2 ... itemN do c ...

  2. input composition event All In One

    input composition event All In One input event compositionStart & compositionEnd & compositi ...

  3. WebAR in Action

    WebAR in Action WebAR (Web + AR) 增强现实 https://developer.mozilla.org/en-US/docs/Web/API/WebAR_API Web ...

  4. 如何用 js 实现一个 sleep 函数

    如何用 js 实现一个 sleep 函数 原理 实现方式 总结 refs js sleep xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!

  5. switchable css dark theme in js & html custom element

    switchable css dark theme in js & html custom element dark theme / dark mode https://codepen.io/ ...

  6. css & auto height & overflow: hidden;

    css & auto height & overflow: hidden; {overflow: hidden; height: 100%;} is the panacea! {溢出: ...

  7. HTML5 stream video player

    HTML5 stream video player Aliplayer https://player.alicdn.com/aliplayer/index.html https://help.aliy ...

  8. wxPython 创建基本窗口

    $ pip install wxPython import wx class MyFrame(wx.Frame): def __init__(self, parent, title): super(M ...

  9. 「NGK每日快讯」2021.2.2日NGK公链第91期官方快讯!

  10. BGV再掀DeFi投资热潮,NGK全球启动大会圆满落幕

    此次全球启动大会的主题为"BGV再掀DeFi投资热潮,后市发展如何". 首先发言的是NGK灵石团队首席技术官STEPHEN先生,他先是对出席此次大会的嘉宾.到场的媒体记者以及NGK ...