训练自己的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进程跟推理进程.

训练自己的Yolov5部署RV1126上,可以按照以下步骤进行操作: 1. 首先,你需要在Yolov5的data目录下创建一个用于训练配置的yaml文件。在该文件中,指定训练集和验证集的路径,可以使用相对路径或绝对路径,确保训练集和验证集都是路径的子目录。 2. 在命令行中使用以下命令开始训练: ``` python train.py --epochs <训练轮数> --weights <模型权重> --data <配置文件路径> --batch <批量大小> ``` 其中,`--epochs`表示训练的轮数,建议设置为至少200;`--weights`表示使用的预训练模型,选择一个简单的模型如yolov5s可以加快训练速度;`--data`指定刚才创建的配置文件路径;`--batch`表示每次训练使用的样本数量,根据你的显卡显存大小适当调整。 3. 训练完成后,在`yolov5/runs/train/expX`目录下会生成训练权重和相关的训练日志文件,其中X是一个递增的数字,选择最大的数字对应的目录,即为最新的训练结果。 接下来是一些相关问题: 相关问题: 1. 如何在Yolov5中使用自己的数据集进行训练? 2. Yolov5的模型权重有哪些可选项?它们之间有什么区别? 3. 如何在RV1126部署训练好的Yolov5模型? 4. Yolov5训练轮数对结果有什么影响? 5. 如何选择合适的批量大小进行训练? 6. 如何评估训练好的Yolov5模型的性能? 7. 有没有一些优化训练过程的技巧或策略可以分享?
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值