训练自己的yolov5样本, 并部署到rv1126 <五>

    // 系统初始化
    RK_MPI_SYS_Init();

    // 分别为rknn跟monitor创建vi通道,通道号分别为0跟1
    ret = create_vi(u32Width, u32Height, "rkispp_scale0", DRAW_RESULT_BOX_CHN_INDEX);
    if (ret < 0) {
        printf("create_vi %d failed\n", DRAW_RESULT_BOX_CHN_INDEX);
        return -1;
    }

    ret = create_vi(u32VC1Width, u32VC1Height, "rkispp_scale1", RK_NN_RGA_CHN_INDEX);
    if (ret < 0) {
        printf("create_vi %d cb\n", RK_NN_RGA_CHN_INDEX);
        return -1;
    }

mpi初始化没啥可说的
创建两个vi通道,正如前面说的,有两个输入通道,一个负责展示推理结果,一个负责作为推理的输入.通道号分别为0跟1
DRAW_RESULT_BOX_CHN_INDEX 是0
RK_NN_RGA_CHN_INDEX 是1

    // 创建rga通道,通道号为1
    ret = create_rga_chn(RK_NN_RGA_CHN_INDEX, u32VC1Width,
                         u32VC1Height, MODEL_INPUT_SIZE, MODEL_INPUT_SIZE);
    if (ret < 0) {
        printf("create_rga_chn failed ret:%d\n", ret);
        return -1;
    }
    // 把rga chn1绑定到vi chn1
    bind_vi_rga(s32CamId, RK_NN_RGA_CHN_INDEX, RK_NN_RGA_CHN_INDEX);
    

创建rga通道1,接到vi通道1,rga通道的设置有点混乱:

	RGA_ATTR_S stRgaAttr;
    memset(&stRgaAttr, 0, sizeof(stRgaAttr));
    stRgaAttr.bEnBufPool = RK_TRUE;
    stRgaAttr.u16BufPoolCnt = 3;
    stRgaAttr.stImgIn.imgType = IMAGE_TYPE_NV12;
    stRgaAttr.u16Rotaion = 0;
    stRgaAttr.stImgIn.u32X = (u32WidthIn - u32WidthOut) / 2;   // 输入的起始位置, 例如输入如果是1920x1080, 输出是640x640, 那么输入的起始位置就是(1920-640)/2=640
    stRgaAttr.stImgIn.u32Y = (u32HeightIn - u32HeightOut) / 2;

    stRgaAttr.stImgIn.u32Width = u32WidthOut;   // 注意这里是输出的宽高
    stRgaAttr.stImgIn.u32Height = u32HeightOut;
    stRgaAttr.stImgIn.u32HorStride = u32WidthIn;   // 所谓虚高
    stRgaAttr.stImgIn.u32VirStride = u32HeightIn;

    stRgaAttr.stImgOut.u32X = 0;   // 输出的起始位置
    stRgaAttr.stImgOut.u32Y = 0;
    stRgaAttr.stImgOut.imgType = IMAGE_TYPE_NV12;    // 输出的格式
    stRgaAttr.stImgOut.u32Width = u32WidthOut;       // 输出的宽高
    stRgaAttr.stImgOut.u32Height = u32HeightOut;
    stRgaAttr.stImgOut.u32HorStride = u32WidthOut;   // 输出的虚高;
    stRgaAttr.stImgOut.u32VirStride = u32HeightOut;
    RK_S32 ret = RK_MPI_RGA_CreateChn(rgaChn, &stRgaAttr);   // 创建rga通道

改天我得画个图才能说明白这个事儿.简单来说,咱们输入的可能是1920x1080分辨率,为了给推理当作输入,比如裁剪成640x640,这就涉及比如是否缩放的问题,起始位置的问题,就像你拿一张很大的图片,当作你windows的墙纸一样,有平铺,中心对齐(缩放)跟中心对齐(不缩放),等等几种,现在这个模式就是类似单纯裁剪的模式,仅仅使用画面中640*640的部分,其他部分直接丢掉.
绑定vi到rga就非常简单,指定通道调用RK_MPI_SYS_Bind绑定就行

    // 创建venc通道,通道号为0
    ret = create_venc_chn(30, u32Width, u32Height, DRAW_RESULT_BOX_CHN_INDEX);
    if (ret < 0) {
        printf("create_venc_chn %d\n", DRAW_RESULT_BOX_CHN_INDEX);
        return -1;
    }

接下来创建编码通道,还是使用通道号0, 分辨率跟vi分辨率一样,1920*1080

    // 注册venc的回调函数,即产生了编码好的数据,就会回调这个函数
    ret = reg_venc_cb();
    if (ret < 0) {
        printf("reg venc cb\n");
        return -1;
    }

rkmedia获取数据流的方法有2种:
1.使用RK_MPI_SYS_GetMediaBuffer获取数据在内存中的块
2.注册一个回调获取数据

int reg_venc_cb() {

    MPP_CHN_S stVenChn;
    memset(&stVenChn, 0, sizeof(MPP_CHN_S));
    stVenChn.enModId = RK_ID_VENC;
    stVenChn.s32ChnId = DRAW_RESULT_BOX_CHN_INDEX;  // 使用monitor进程画框子的那个通道

    RK_S32 ret = RK_MPI_SYS_RegisterOutCb(&stVenChn, venc_packet_cb);
    if (ret) {
        printf("ERROR: register output callback for VENC[0] error! ret=%d\n", ret);
        return -1;
    }

    return 0;

}

这里的编码器就是用回调的方法,注册了venc_packet_cb这个函数,它的输入就是已经编码好的数据.


void venc_packet_cb(MEDIA_BUFFER mb) {

    static RK_S32 packet_cnt = 0;
    if (g_flag_run == 0)
        return;

    printf("#Get packet-%d, size %zu\n", packet_cnt, RK_MPI_MB_GetSize(mb));

    if (g_rtsplive && g_rtsp_session) {
        rtsp_tx_video(g_rtsp_session, (const uint8_t *) RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb),
                      RK_MPI_MB_GetTimestamp(mb));
        rtsp_do_event(g_rtsplive);
    }

    RK_MPI_MB_ReleaseBuffer(mb);
    packet_cnt++;
}

这个回调拿到编码完成的数据后,就丢给rtsp去准备推流处理了.

聪明的你可能会问,那我的推理部分跟画框框过程呢?

  // bind_vi_venc();
    // 打开vi通道0的输出,即开始从isp获取数据
    ret = RK_MPI_VI_StartStream(s32CamId, DRAW_RESULT_BOX_CHN_INDEX);
    if (ret < 0) {
        printf("RK_MPI_VI_StartStream failed\n");
        return -1;
    }

首先打开监控的vi通道,因为后面我们要手动画框,所以不能直接绑定,而是使用RK_MPI_VI_StartStream(s32CamId, DRAW_RESULT_BOX_CHN_INDEX); 再RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, DRAW_RESULT_BOX_CHN_INDEX..的方法

    // 创建rknn的队列,用于线程间保存rknn的推理结果
    create_rknn_list(&rknn_list_);

创建推理的结果队列,其实就是个简单的限定长度的fifo链表, 用于保存推理结果.推理结果就是一个结构体,记录找到多少个框,每个框的类别,xy长宽信息.

下面分析最后两个也是最重要的进程, monitor进程跟推理进程.

你可能感兴趣的:(人工智能,算法,嵌入式硬件,ai,深度学习)