有了自己的模型,接下来就是要部署到rv1126上了,我根据rkmedia的示例代码,重新稍微整理了一下.
整体思路如下:
分两条通路,一条是推理用:
从 scale1 获取数据 -> vi 通道 1 分辨率1920x1080 -> 绑定rga chn 1 -> 使用单独线程 RK_MPI_SYS_GetMediaBuffer(RK_ID_RGA)转成640x640 RGB,推理之后,放入队列.
另一条推流用:
scale0 -> vi chn 0 -> RK_MPI_SYS_GetMediaBuffer(RK_ID_VI)-> 获取rknn的推测结果队列 -> 画框子 -> RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC) to venc chn 0
代码分析如下:
int main(int argc, char **argv) {
RK_U32 u32Width = 1920;
RK_U32 u32Height = 1080;
// GC2053 只能用 1920*1080
// RK_U32 u32VC1Width = 1920;
// RK_U32 u32VC1Height = 1080;
// imx415 可以使用 1280*720
RK_U32 u32VC1Width = 1280;
RK_U32 u32VC1Height = 720;
定义vi的分辨率,实际上imx415能支持到4k, 这里只使用1080p.
signal(SIGINT, sig_proc);
int ret = 0;
if (argc < 2) {
printf("please input model name and rtsp config file\n");
return -1;
}
提示调用的时候需要指定模型的文件名.
// 初始化模型
init_model(argc, argv);
初始化模型这个函数:
int ret = 0;
m_info.m_path = (char *) argv[1];
// 初始化模型参数
printf("model file is : %s \n", m_info.m_path);
m_info.m_type = YOLOV5;
m_info.color_expect = RK_FORMAT_RGB_888;
m_info.anchor_per_branch = 3;
memcpy(m_info.anchors, fixAnchors, sizeof(fixAnchors));
m_info.post_type = U8;
m_info.in_source = VIDEO_STREAM;
m_info是个结构体MODEL_INFO的实例,略有对象编程经验的同学可以当作就是一个对象.
对这个实例的属性进行复制,比如算法是yolov5, 色彩格式,这里我直接把anchors写死,因为yolov5的anchors基本上就是固定的.
int fixAnchors[18] = {10, 13, 16, 30, 33, 23,
30, 61, 62, 45, 59, 119,
116, 90, 156, 198, 373, 326};
另外因为模型是量化过的,所以直接用u8.
printf("Loading model...\n");
int model_data_size = 0;
unsigned char *model_data = load_model(m_info.m_path, &model_data_size);
printf("model_data_size = %d \n", model_data_size);
读取模型文件
// rknn初始化
ret = rknn_init(&m_ctx, model_data, model_data_size, 0);
if (ret < 0) {
printf("rknn_init error ret=%d\n", ret);
return -1;
}
rknn使用前,先用rknn_init
具体的rknn的api文档在sdk的external/rknpu/rknn/doc下面.
// 使用rknn的api查询模型信息
printf("query info\n");
ret = query_model_info(&m_info, m_ctx);
if (ret < 0) {
return -1;
}
查询打印模型信息.
/* 初始化输入tensor */
memset(inputs, 0, sizeof(inputs));
inputs[0].index = 0;
inputs[0].type = RKNN_TENSOR_UINT8; /* SAME AS INPUT IMAGE */
inputs[0].size = MODEL_INPUT_SIZE * MODEL_INPUT_SIZE * 3;
// 这里有点坑,保持默认就好
inputs[0].fmt = RKNN_TENSOR_NHWC;
inputs[0].pass_through = 0;
/* 初始化输出的tensor */
memset(outputs, 0, sizeof(outputs));
// 不使用float, 因为模型是用u8量化过
for (int i = 0; i < m_info.out_nodes; i++) {
outputs[i].want_float = 0;
}
初始化输入输出tensor,主要指定输入的尺寸是640x640,格式这里坑了我2天,我想当然想让这里的设置跟模型的格式一致,因为打印的模型信息是RKNN_TENSOR_NCHW,我就改成了跟模型一致,结果一直没法出推理结果.因为中间有个nv12转RGB, imcvtcolor函数转码的动作,估计这里模型会根据这个设置自动调整.
// 初始化isp
init_isp();
初始化isp就是三个老动作,init, run, setFrameRate
static void init_isp(void) {
SAMPLE_COMM_ISP_Init(s32CamId, hdr_mode, bMultictx, pIqfilesPath);
SAMPLE_COMM_ISP_Run(s32CamId);
SAMPLE_COMM_ISP_SetFrameRate(s32CamId, FPS);
}
这里3点注意,一个是s32CamId.
根据原子支持群里的说法,在原子的rv1126的板子上,如果只有一个imx415摄像头,插CSI1, CSI0都可以,这个s32CamId都是0.
然后根据我的经验,易百纳的板子则是,首先刷2.2.5的固件,然后imx415的板子插CSI0, GC2053插CSI1, s32CamId都是0.
说了半天好像啥也没说是么?哈哈,总之s32CamId都是0.
然后就是pIqfilesPath,这个在不同的开发板上路径不一样,原子直接就是/etc/iqfiles, 易百纳的是/oem/etc/iqfiles跟官方一致.
FPS基本都是30
// 初始化rtsp
init_rtsp();
官方的vi_rknn_venc_rtsp_test.c中用了两个rtsp的session,还要读取配置文件,我直接砍掉一个,只需要保留一个session,用于观测即可.
// 利用rtsp库, 将h264数据封装成rtsp流
void init_rtsp(void) {
printf("init rtsp\n");
g_rtsplive = create_rtsp_demo(554); // 554即rtsp端口号, 返回的是一个rtsp_demo_handle指针
g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/main_stream"); // 创建rtsp会话, 访问地址是rtsp://ip/live/main_stream
rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0); // 设置视频编码格式
rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime()); // 设置时间戳
}
rtsp这个库太好用了,只需要简单的初始化就可以使用.