Hi3516DV300的yolo识别代码详解

源码在https://gitee.com/jiang-guolong/openharmony

主函数sample_ai_main.cpp

switch (*argv[1]) {
        case '0':
            SAMPLE_MEDIA_CNN_TRASH_CLASSIFY();
            break;
        case '1':
            SAMPLE_MEDIA_HAND_CLASSIFY();
            break;
        case '2':
            mediaOpencv.SAMPLE_MEDIA_TENNIS_DETECT();
            break;
        default:
            SAMPLE_AI_Usage(argv[0]);
            break;
    }

中的

case '1':
            SAMPLE_MEDIA_HAND_CLASSIFY();

调用了这个函数之后,转到sample_media_ai.cpp文件

sample_media_ai.cpp

该文件中的SAMPLE_MEDIA_HAND_CLASSIFY函数为程序的第二执行顺序

HI_S32 SAMPLE_MEDIA_HAND_CLASSIFY(HI_VOID)   //主函数2
{
    HI_S32             s32Ret;
    HI_S32             fd = 0;

    /*
     * 配置VI参数
     * Config VI parameter
     */
    ViPramCfg();

    /*
     * 通过Sensor型号获取enPicSize
     * Obtain enPicSize through the Sensor type
     */
    s32Ret = SAMPLE_COMM_VI_GetSizeBySensor(g_aicMediaInfo.viCfg.astViInfo[0].stSnsInfo.enSnsType,
        &g_aicMediaInfo.enPicSize);
    SAMPLE_CHECK_EXPR_RET(s32Ret != HI_SUCCESS, s32Ret, "get pic size by sensor fail, s32Ret=%#x\n", s32Ret);

    /*
     * 根据enPicSize,得到图片的宽高
     * Get picture size(w*h), according enPicSize
     */
    s32Ret = SAMPLE_COMM_SYS_GetPicSize(g_aicMediaInfo.enPicSize, &g_aicMediaInfo.stSize);
    SAMPLE_PRT("AIC: snsMaxSize=%ux%u\n", g_aicMediaInfo.stSize.u32Width, g_aicMediaInfo.stSize.u32Height);
    SAMPLE_CHECK_EXPR_RET(s32Ret != HI_SUCCESS, s32Ret, "get picture size failed, s32Ret=%#x\n", s32Ret);

    /*
     * 配置VB参数
     * Config VB parameter
     */
    StVbParamCfg(&g_aicMediaInfo.vbCfg);

    /*
     * 视频缓存池初始化以及MPI系统初始化
     * VB init & MPI system init
     */
    s32Ret = SAMPLE_COMM_SYS_Init(&g_aicMediaInfo.vbCfg);
    SAMPLE_CHECK_EXPR_RET(s32Ret != HI_SUCCESS, s32Ret, "system init failed, s32Ret=%#x\n", s32Ret);

    /*
     * 设置VO至MIPI通路,获取MIPI设备
     * Set VO config to MIPI, get MIPI device
     */
    s32Ret = SAMPLE_VO_CONFIG_MIPI(&fd);
    SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, EXIT, "CONFIG MIPI FAIL.s32Ret:0x%x\n", s32Ret);

    /*
     * 配置VPSS参数
     * Config VPSS parameter
     */
    VpssParamCfg();
    s32Ret = ViVpssCreate(&g_aicMediaInfo.viSess, &g_aicMediaInfo.viCfg, &g_aicMediaInfo.vpssCfg);
    SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, EXIT1, "ViVpss Sess create FAIL, ret=%#x\n", s32Ret);
    g_aicMediaInfo.vpssGrp = AIC_VPSS_GRP;
    g_aicMediaInfo.vpssChn0 = AIC_VPSS_ZOUT_CHN;

    /*
     * 配置VO参数
     * Config VO parameter
     */
    StVoParamCfg(&g_aicMediaInfo.voCfg);

    /*
     * 启动VO到MIPI lcd通路
     * Start VO to MIPI lcd
     */
    s32Ret = SampleCommVoStartMipi(&g_aicMediaInfo.voCfg);
    SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, EXIT1, "start vo FAIL. s32Ret: 0x%x\n", s32Ret);

    /*
     * VPSS绑定VO
     * VPSS bind VO
     */
    s32Ret = SAMPLE_COMM_VPSS_Bind_VO(g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0, g_aicMediaInfo.voCfg.VoDev, 0);
    SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, EXIT2, "vo bind vpss FAIL. s32Ret: 0x%x\n", s32Ret);
    SAMPLE_PRT("vpssGrp:%d, vpssChn:%d\n", g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0);

    /*
     * 创建工作线程运行ai
     * Create work thread to run ai
     */
    s32Ret = HandClassifyAiThreadProcess(); //主函数3
    SAMPLE_CHECK_EXPR_RET(s32Ret != HI_SUCCESS, s32Ret, "ai proccess thread creat fail:%s\n", strerror(s32Ret));

    Pause();
    g_bAiProcessStopSignal = HI_TRUE;
    /*
     * 等待一个线程结束,线程间同步的操作
     * Waiting for the end of a thread, the operation of synchronization between threads
     */
    pthread_join(g_aiProcessThread, NULL);
    g_aiProcessThread = 0;
    PauseDoUnloadHandClassifyModel();

    SAMPLE_COMM_VPSS_UnBind_VO(g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0, g_aicMediaInfo.voCfg.VoDev, 0);
    SAMPLE_VO_DISABLE_MIPITx(fd);
    SampleCloseMipiTxFd(fd);
    system("echo 0 > /sys/class/gpio/gpio55/value");

EXIT2:
    SAMPLE_COMM_VO_StopVO(&g_aicMediaInfo.voCfg);
EXIT1:
    VpssStop(&g_aicMediaInfo.vpssCfg);
    SAMPLE_COMM_VI_UnBind_VPSS(g_aicMediaInfo.viCfg.astViInfo[0].stPipeInfo.aPipe[0],
        g_aicMediaInfo.viCfg.astViInfo[0].stChnInfo.ViChn, g_aicMediaInfo.vpssGrp);
    ViStop(&g_aicMediaInfo.viCfg);
    free(g_aicMediaInfo.viSess);
EXIT:
    SAMPLE_COMM_SYS_Exit();
    return s32Ret;
}

其中的

 /*
     * 创建工作线程运行ai
     * Create work thread to run ai
     */
    s32Ret = HandClassifyAiThreadProcess(); //主函数3
    SAMPLE_CHECK_EXPR_RET(s32Ret != HI_SUCCESS, s32Ret, "ai proccess thread creat fail:%s\n", strerror(s32Ret));

此处函数3调用的本体函数如下

static HI_S32 HandClassifyAiThreadProcess(HI_VOID)
{
    HI_S32 s32Ret;
    if (snprintf_s(acThreadName, BUFFER_SIZE, BUFFER_SIZE - 1, "AIProcess") < 0) {
        HI_ASSERT(0);
    }
    prctl(PR_SET_NAME, (unsigned long)acThreadName, 0, 0, 0);
    s32Ret = pthread_create(&g_aiProcessThread, NULL, GetVpssChnFrameHandClassify, NULL);//主函数4

    return s32Ret;
}

此处将会创造一个线程用来处理yolo识别,紧接着在创建线程之后,程序会调用函数5 GetVpssChnFrameHandClassify

static HI_VOID* GetVpssChnFrameHandClassify(HI_VOID* arg) //主函数5循环
{
    int ret;
    VIDEO_FRAME_INFO_S frm;
    HI_S32 s32MilliSec = 2000;
    VO_LAYER voLayer = 0;
    VO_CHN voChn = 0;
    SAMPLE_PRT("vpssGrp:%d, vpssChn0:%d\n", g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0);
    while (HI_FALSE == g_bAiProcessStopSignal) {

        ret = HI_MPI_VPSS_GetChnFrame(g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0, &frm, s32MilliSec);//获取图片
        if (ret != 0) {
            SAMPLE_PRT("HI_MPI_VPSS_GetChnFrame FAIL, err=%#x, grp=%d, chn=%d\n",
                ret, g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0);
            ret = HI_MPI_VPSS_ReleaseChnFrame(g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0, &frm);
            if (ret != HI_SUCCESS) {
                SAMPLE_PRT("Error(%#x),HI_MPI_VPSS_ReleaseChnFrame failed,Grp(%d) chn(%d)!\n",
                    ret, g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0);
            }
            continue;
        }
        SAMPLE_PRT("get vpss frame success, weight:%d, height:%d\n", frm.stVFrame.u32Width, frm.stVFrame.u32Height);

        if (g_num == 0) {
            ConfBaseInit(AI_SAMPLE_CFG_FILE);
            g_num++;
        }
        HandClassifyAiProcess(frm, voLayer, voChn);//主函数6
    }
    return HI_NULL;
}

主函数5的作用

GetVpssChnFrameHandClassify 函数的作用是循环从视频输入源(通过 VPSS,视频处理和安全子系统)获取图像帧,并对这些帧进行手部分类处理。这个函数主要负责持续地从视频流中获取帧,并将其传递给手部分类处理函数 (HandClassifyAiProcess),同时它也负责管理帧资源的获取和释放。

    int ret;
    VIDEO_FRAME_INFO_S frm;
    HI_S32 s32MilliSec = 2000;
    VO_LAYER voLayer = 0;
    VO_CHN voChn = 0;

局部变量定义:

  • ret:用于存储函数的返回值。
  • frm:用来存储从视频源获取的图像帧信息。
  • s32MilliSec:获取帧时的超时时间,设置为 2000 毫秒(即 2 秒)。
  • voLayer 和 voChn:视频输出层和通道,用于后续帧的显示输出。
    SAMPLE_PRT("vpssGrp:%d, vpssChn0:%d\n", g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0);
  • 打印出当前的 VPSS 组和通道信息,用于调试。
	while (HI_FALSE == g_bAiProcessStopSignal) {
  • 循环:这个 while 循环会一直执行,直到 g_bAiProcessStopSignal 为 HI_TRUE,即停止信号被触发。
	 ret = HI_MPI_VPSS_GetChnFrame(g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0, &frm, s32MilliSec);
  • 获取视频帧:
    • 使用 HI_MPI_VPSS_GetChnFrame 函数从 VPSS 通道中获取一帧图像,并将结果存储在 frm 中。
    • 如果获取失败,返回值 ret 不为 0,表示获取帧失败。
	if (ret != 0) {
            SAMPLE_PRT("HI_MPI_VPSS_GetChnFrame FAIL, err=%#x, grp=%d, chn=%d\n",
                ret, g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0);
  • 获取失败的处理:

    • 如果 HI_MPI_VPSS_GetChnFrame 返回失败,打印错误信息,并释放已获取的帧资源:
    	ret = HI_MPI_VPSS_ReleaseChnFrame(g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0, &frm);
          if (ret != HI_SUCCESS) {
              SAMPLE_PRT("Error(%#x),HI_MPI_VPSS_ReleaseChnFrame failed,Grp(%d) chn(%d)!\n",
                  ret, g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0);
          }
          continue;
    
    • 如果释放帧失败,打印错误信息并继续循环,尝试获取下一帧。
	SAMPLE_PRT("get vpss frame success, weight:%d, height:%d\n", frm.stVFrame.u32Width, frm.stVFrame.u32Height);
  • 帧获取成功:
    • 打印获取到的视频帧的宽度和高度,表明帧已成功获取。
	if (g_num == 0) {
            ConfBaseInit(AI_SAMPLE_CFG_FILE);
            g_num++;
        }
  • 配置初始化:
    • 在第一次执行时(g_num == 0),调用 ConfBaseInit 函数进行配置初始化,加载配置文件 AI_SAMPLE_CFG_FILE。
	 HandClassifyAiProcess(frm, voLayer, voChn); // 主函数 6
  • 手部分类处理:
    • 调用 HandClassifyAiProcess 函数进行手部分类推理,并将获取到的帧 frm 传递给该函数。该函数会对图像进行推理,处理分类任务,并将结果输出到显示层(由 voLayer 和 voChn 指定)。
	return HI_NULL;
  • 循环结束:
    • 如果外部停止信号触发,循环结束,函数返回 HI_NULL。

其中的函数6,会调用HandClassifyAiProcess

HandClassifyAiProcess(frm, voLayer, voChn);//主函数6

HandClassifyAiProcess的本体如下

static HI_VOID HandClassifyAiProcess(VIDEO_FRAME_INFO_S frm, VO_LAYER voLayer, VO_CHN voChn)//主函数7 试验过了((1980,1080)),0,0)
{
    int ret;
    if (GetCfgBool("hand_classify_switch:support_hand_classify", true)) {
        if (g_workPlug.model == 0) {   
            ret = Yolo2HandDetectResnetClassifyLoad(&g_workPlug.model);  //主函数8
            if (ret == 0){
                printf("---------------------model success-------------------\n");
            }
        }
     
        VIDEO_FRAME_INFO_S resizeFrm;
        ret = MppFrmResize(&frm, &resizeFrm, HAND_FRM_WIDTH, HAND_FRM_HEIGHT);  //(1980,1080)->(640,384)

        ret = Yolo2HandDetectResnetClassifyCal(g_workPlug.model, &resizeFrm, &frm); //主函数10
        SAMPLE_CHECK_EXPR_GOTO(ret < 0, HAND_RELEASE,
            "hand classify plug cal FAIL, ret=%#x\n", ret);

        ret = HI_MPI_VO_SendFrame(voLayer, voChn, &frm, 0);
        SAMPLE_CHECK_EXPR_GOTO(ret != HI_SUCCESS, HAND_RELEASE,
            "HI_MPI_VO_SendFrame fail, Error(%#x)\n", ret);

        MppFrmDestroy(&resizeFrm);
    }

    HAND_RELEASE:
        ret = HI_MPI_VPSS_ReleaseChnFrame(g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0, &frm);
        if (ret != HI_SUCCESS) {
            SAMPLE_PRT("Error(%#x),HI_MPI_VPSS_ReleaseChnFrame failed,Grp(%d) chn(%d)!\n",
                ret, g_aicMediaInfo.vpssGrp, g_aicMediaInfo.vpssChn0);
        }
}

此函数的作用

HandClassifyAiProcess 函数的主要作用是进行图像帧的手部分类处理。它通过加载和应用手部检测模型(例如 YOLO2 手部分类模型),调整图像的尺寸,并通过模型推理来分类图像,最终将处理后的图像输出到显示层。

    if (GetCfgBool("hand_classify_switch:support_hand_classify", true)) {

配置检查:

  • 通过 GetCfgBool 函数检查是否支持手部分类。hand_classify_switch:support_hand_classify 是一个配置项,用于确定是否开启手部分类功能。如果该配置项为 true,则继续执行手部分类任务。
        if (g_workPlug.model == 0) {   
            ret = Yolo2HandDetectResnetClassifyLoad(&g_workPlug.model);  // 主函数 8
            if (ret == 0) {
                printf("---------------------model success-------------------\n");
            }
        }

加载模型:

  • 如果 g_workPlug.model 为 0(即模型尚未加载),则调用 Yolo2HandDetectResnetClassifyLoad 函数加载 YOLO2 手部检测模型。加载成功后会打印 model success。
        VIDEO_FRAME_INFO_S resizeFrm;
        ret = MppFrmResize(&frm, &resizeFrm, HAND_FRM_WIDTH, HAND_FRM_HEIGHT);  //(1980,1080)->(640,384)

调整图像大小:

  • 调用 MppFrmResize 函数将输入帧 frm 调整为目标尺寸(如 640x384),并将调整后的图像存储在 resizeFrm 中。
        ret = Yolo2HandDetectResnetClassifyCal(g_workPlug.model, &resizeFrm, &frm); // 主函数 10
        SAMPLE_CHECK_EXPR_GOTO(ret < 0, HAND_RELEASE,
            "hand classify plug cal FAIL, ret=%#x\n", ret);

进行推理:

  • 使用已经加载的 YOLO2 手部检测模型对调整后的图像 resizeFrm 进行推理,推理结果会存储在 frm 中。
  • 如果推理失败(ret < 0),则跳转到 HAND_RELEASE 进行资源释放。
        ret = HI_MPI_VO_SendFrame(voLayer, voChn, &frm, 0);
        SAMPLE_CHECK_EXPR_GOTO(ret != HI_SUCCESS, HAND_RELEASE,
            "HI_MPI_VO_SendFrame fail, Error(%#x)\n", ret);

输出处理后的图像:

  • 使用 HI_MPI_VO_SendFrame 函数将处理后的图像帧 frm 发送到指定的视频输出层(voLayer)和通道(voChn),显示处理后的图像。
  • 如果发送失败(ret != HI_SUCCESS),则跳转到 HAND_RELEASE 进行资源释放。

主函数8调用的函数本体如下

HI_S32 Yolo2HandDetectResnetClassifyLoad(uintptr_t* model)//主函数9
{
    SAMPLE_SVP_NNIE_CFG_S *self = NULL;
    HI_S32 ret;
    *model = 1;
    // ret = CnnCreate(&self, MODEL_FILE_GESTURE);  //不需要加载
    // *model = ret < 0 ? 0 : (uintptr_t)self;
    HandDetectInit(); // Initialize the hand detection model  //支线1.3---重点---这才是加载yolov5模型的入口
    SAMPLE_PRT("Load hand detect claasify model success\n");
    /*
     * Uart串口初始化
     * Uart open init
     */
    uartFd = UartOpenInit();
    if (uartFd < 0) {
        printf("uart1 open failed\r\n");
    } else {
        printf("uart1 open successed\r\n");
    }
    return ret;
}

Yolo2HandDetectResnetClassifyLoad 函数的作用是加载手部检测和分类模型,并进行一些必要的初始化工作。这包括初始化手部检测模型、打开串口(UART)等。

    HandDetectInit(); // Initialize the hand detection model  // 支线1.3---重点---这才是加载yolov5模型的入口

初始化手部检测模型:

  • 调用 HandDetectInit 函数来初始化手部检测模型。这是一个关键步骤,它实际完成了 YOLOv5 模型的加载和配置。HandDetectInit 函数可能会加载 YOLOv5 模型的权重、配置文件以及初始化所需的其他资源。
/*
 * Uart串口初始化
 * Uart open init
 */
uartFd = UartOpenInit();
if (uartFd < 0) {
    printf("uart1 open failed\r\n");
} else {
    printf("uart1 open successed\r\n");
}

串口初始化:

调用 UartOpenInit 函数初始化串口(UART)。串口通常用于与外部设备(如调试控制台或传感器)进行通信。

如果串口初始化失败(uartFd < 0),则打印 “uart1 open failed”;否则,打印 “uart1 open successed”,表示串口已成功打开。

其中函数10的本体如下

HI_S32 Yolo2HandDetectResnetClassifyCal(uintptr_t model, VIDEO_FRAME_INFO_S *srcFrm, VIDEO_FRAME_INFO_S *dstFrm)//2 //cnnmodel,rezeimg,img
{
    SAMPLE_SVP_NNIE_CFG_S *self = (SAMPLE_SVP_NNIE_CFG_S*)model;
    HI_S32 resLen = 0;
    int objNum;
    int ret;
    int num = 0;

    ret = FrmToOrigImg((VIDEO_FRAME_INFO_S*)srcFrm, &img);
    SAMPLE_CHECK_EXPR_RET(ret != HI_SUCCESS, ret, "hand detect for YUV Frm to Img FAIL, ret=%#x\n", ret);
    objNum = HandDetectCal(&img, objs); // Send IMG to the detection net for reasoning
    for (int i = 0; i < objNum; i++) {
        cnnBoxs[i] = objs[i].box;
        RectBox *box = &objs[i].box;
        RectBoxTran(box, HAND_FRM_WIDTH, HAND_FRM_HEIGHT,
            dstFrm->stVFrame.u32Width, dstFrm->stVFrame.u32Height);
            SAMPLE_PRT("yolo5_out: {%d, %d, %d, %d} cls:%s, score:%f \n", box->xmin, box->ymin, box->xmax, box->ymax, classnamee[objs[i].cls], objs[i].score);
        boxs[i] = *box;
    }
    biggestBoxIndex = GetBiggestHandIndex(boxs, objNum);
    SAMPLE_PRT("biggestBoxIndex:%d, objNum:%d\n", biggestBoxIndex, objNum);

    /*
     * 当检测到对象时,在DSTFRM中绘制一个矩形
     * When an object is detected, a rectangle is drawn in the DSTFRM
     */
    if (biggestBoxIndex >= 0) {
        objBoxs[0] = boxs[biggestBoxIndex];
        MppFrmDrawRects(dstFrm, objBoxs, 1, RGB888_GREEN,8); // Target hand objnum is equal to 1

        for (int j = 0; (j < objNum) && (objNum > 1); j++) {
            if (j != biggestBoxIndex) {
                remainingBoxs[num++] = boxs[j];
                /*
                 * 其他手objnum等于objnum -1
                 * Others hand objnum is equal to objnum -1
                 */
                MppFrmDrawRects(dstFrm, remainingBoxs, objNum - 1, RGB888_RED, 8);
            }
        }
    }
    free(objs);
    return 1;
}

Yolo2HandDetectResnetClassifyCal 函数的主要作用是使用 YOLO 模型对图像进行手部检测和分类推理,并根据检测结果在目标帧中绘制矩形框,标记出检测到的手部对象。

ret = FrmToOrigImg((VIDEO_FRAME_INFO_S*)srcFrm, &img);
SAMPLE_CHECK_EXPR_RET(ret != HI_SUCCESS, ret, "hand detect for YUV Frm to Img FAIL, ret=%#x\n", ret);

帧转换:

调用 FrmToOrigImg 将 YUV 格式的帧 srcFrm 转换为原始图像格式(例如 RGB),并将结果存储在 img 中。如果转换失败,打印错误信息并返回。

objNum = HandDetectCal(&img, objs); // Send IMG to the detection net for reasoning

手部检测推理:

调用 HandDetectCal 函数,将转换后的图像 img 传递给手部检测网络进行推理。返回值 objNum 表示检测到的手部对象数量,objs 是检测到的手部对象数组。

for (int i = 0; i < objNum; i++) {
    cnnBoxs[i] = objs[i].box;
    RectBox *box = &objs[i].box;
    RectBoxTran(box, HAND_FRM_WIDTH, HAND_FRM_HEIGHT,
        dstFrm->stVFrame.u32Width, dstFrm->stVFrame.u32Height);
    SAMPLE_PRT("yolo5_out: {%d, %d, %d, %d} cls:%s, score:%f \n", box->xmin, box->ymin, box->xmax, box->ymax, classnamee[objs[i].cls], objs[i].score);
    boxs[i] = *box;
}

处理检测结果:

对检测到的每个手部对象,存储其边界框(box)信息,并将框的坐标通过 RectBoxTran 函数转换为目标帧的尺寸。

打印每个检测框的信息,包括框的坐标(xmin, ymin, xmax, ymax),分类标签(cls)以及置信度(score)。

将每个检测框存储在 boxs[i] 中。

biggestBoxIndex = GetBiggestHandIndex(boxs, objNum);
SAMPLE_PRT("biggestBoxIndex:%d, objNum:%d\n", biggestBoxIndex, objNum);

找到最大框:

调用 GetBiggestHandIndex 函数,获取最大小的手部框的索引,并打印出最大的框的索引和检测到的对象数量。

    if (biggestBoxIndex >= 0) {
        objBoxs[0] = boxs[biggestBoxIndex];
        MppFrmDrawRects(dstFrm, objBoxs, 1, RGB888_GREEN, 8); // Target hand objnum is equal to 1
  • 绘制最大检测框:
    • 如果检测到的手部对象数量大于 0,选择最大的框(通过 biggestBoxIndex 获取),并在目标帧 dstFrm 中绘制一个绿色的矩形框,标记出最大的手部对象。
	 for (int j = 0; (j < objNum) && (objNum > 1); j++) {
            if (j != biggestBoxIndex) {
                remainingBoxs[num++] = boxs[j];
                MppFrmDrawRects(dstFrm, remainingBoxs, objNum - 1, RGB888_RED, 8);
            }
        }
    }

绘制其他检测框:

  • 对于其他的检测到的手部框(排除最大的框),将它们存储在 remainingBoxs 中,并在目标帧 dstFrm 上绘制红色的矩形框。

其中

objNum = HandDetectCal(&img, objs); // Send IMG to the detection net for reasoning

的HandDetectCal函数本体如下

HI_S32 HandDetectCal(IVE_IMAGE_S *srcYuv, DetectObjInfo resArr[]) //主函数11 srcYuv是图像
{
    int ret = HandDetect(g_handModel, srcYuv, resArr);
    return ret;
}

此处开始套娃

static HI_S32 HandDetect(uintptr_t model, IVE_IMAGE_S *srcYuv, DetectObjInfo boxs[])//主函数12
{
    SAMPLE_SVP_NNIE_CFG_S *self = (SAMPLE_SVP_NNIE_CFG_S*)model;
    int objNum;
    int ret = Yolo2CalImg(self, srcYuv, boxs, DETECT_OBJ_MAX, &objNum);  //yolov2模型, 图像, bbox,32 // detect max obj,int?
    if (ret < 0) {
        SAMPLE_PRT("Hand detect Yolo2CalImg FAIL, for cal FAIL, ret:%d\n", ret);
        return ret;
    }

    return objNum;
}

调用Yolo2CalImg,此函数在ai_infer_process.c中

int Yolo2CalImg(SAMPLE_SVP_NNIE_CFG_S* self,
    const IVE_IMAGE_S *img, DetectObjInfo resBuf[], int resSize, int* resLen)  // 主函数10  //yolov2模型, 图像, bbox,32最大框数detect max obj,*int
{   
    SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S stInputDataIdx = {0};
    SAMPLE_SVP_NNIE_PROCESS_SEG_INDEX_S stProcSegIdx = {0};
    HI_S32 s32Ret;
    yolo_result *output_result = NULL;
    /*
     * 填充源数据
     * Fill src data
     */
    self->pszPic = NULL;
    stInputDataIdx.u32SegIdx = 0;
    stInputDataIdx.u32NodeIdx = 0;
    s32Ret = FillNnieByImg(self, &g_stYolov2NnieParam, 0, 0, img); // 主函数11
    SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret, YOLOV2_FAIL_0, SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error, SAMPLE_SVP_NNIE_FillSrcData failed!\n");
    
    /*
     * NNIE推理(process the 0-th segment)
     * NNIE process(process the 0-th segment)
     */
    stProcSegIdx.u32SegIdx = 0;
    s32Ret = SAMPLE_SVP_NNIE_Forward(&g_stYolov2NnieParam, &stInputDataIdx, &stProcSegIdx, HI_TRUE);
    SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret, YOLOV2_FAIL_0, SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error, SAMPLE_SVP_NNIE_Forward failed!\n");
    yolov5__process(&g_stYolov2NnieParam, resBuf, resLen);

    return 0;

    YOLOV2_FAIL_0:
    return -1;
}

更多推荐