Toybrick

标题: 基于C语言 RKNN多线程框架 [打印本页]

作者: troy    时间: 2019-5-10 17:05
标题: 基于C语言 RKNN多线程框架
本帖最后由 troy 于 2019-9-9 15:35 编辑

  最近整理了一个C语言版本多线程的框架,封装了rknn操作接口,图像获取与处理,以及多线程细节,用户只需要关注模型的后处理,即可快速完成多线程图像demo的开发评估工作。
代码链接:https://pan.baidu.com/s/1wz0y8bSY98rKu7M1yFlbEQ 提取码:xifd
github仓库:https://github.com/troyseed/toybrick_ssd_multithread.git

说明:最近很多人反映在1.0.0上无法运行的情况,在此做下说明,代码里面自带的.rknn是基于0.9.9的ToolKit转换的,因此在1.0.0运行会有兼容性的问题。
因此在ssd_trans,目录下放置了rknn转换代码和pb模型问题,用户需要自己在PC机上进行模型转换,再拷贝转换后的.rknn模型到models/ssd/目录进行运行。

说明文档:[url=]SSD_DEMO USER MUNUAL.pdf[/url]

框架运行时,会创建3个线程,分别用于图像获取,RKNN推理,图像后处理。
用户只需要将自己的后处理实现回调设置到rknn_test框架,即可自动完成图像获取->resize->推理->后处理->结果显示等操作。

1.代码结构如下:
.
├── CMakeLists.txt
├── common                             //多线程框架主体部分,模型无关
│   ├── rknn.cpp                        //封装rknn-api相关操作,包括模型的加载,推理,结果获取
│   ├── rknn.h
│   ├── rknn_test.cpp                 //多线程测试框架
│   ├── rknn_test.h
│   ├── rknn_thread.cpp             //多线程相关处理
│   └── rknn_thread.h
├── models                               //模型文件
│   └── ssd                                //SSD模型相关文件
│       ├── box_priors.txt  
│       ├── coco_labels_list.txt
│       ├── labels.txt
│       └── mobilenet_ssd.rknn
├── ReadMe.txt
└── ssd_demo.cpp                      //SSD模型测试


2.ssd_demo.cpp代码解析
rknn_test.h提供以下几个接口:
  1.   rknn_test(const char *test_name);          /* 构造函数,需要提供一个test_name作为输出的窗口名称 */
  2.         ~rknn_test();                        /* 析构函数 */
  3.         int load_model(const char *path);    /* 加载rknn模型 */
  4.         int set_input_info(int width, int height, int channels);    /* 设置模型的输入大小,rknn_test会自动完成resize功能 */
  5.         int run(int video_node,   /* video节点号,为0时,打开video0节点获取摄像头图像作为输入 */
  6.                    /* func 为后处理回调函数*/
  7.                    int (*func) (void *data /* in 用户数据指针 */, cv::Mat & img /* in 图像数据,可以在上面绘制结果 */, float fps /* in 帧率 */, struct rknn_out_data * out_data<font color="#008000"> /* in rknn推理结果 */</font>),
  8.                    void *data /* 用户数据指针,提供给func使用 */);

  9.         /* 同上,支持视频文件作为输入 */
  10.         int run(const char *video_name,
  11.                 int (*func) (void *data, cv::Mat & img, float fps,
  12.                              struct rknn_out_data * out_data), void *data);
复制代码



ssd_demo.cpp
  1. int main(void)
  2. {
  3.         int ret;
  4.         /* 自定义数据,用于post_process函数使用 */
  5.         struct ssd_data data;
  6.         /* load label and boxPriors */
  7.         loadLabelName(LABEL_PATH, data.labels);
  8.         loadCoderOptions(BOX_PRIO_PATH, data.boxPriors);

  9.         /* 创建一个测试实例 */
  10.         class rknn_test test(WIN_NAME);

  11.         /* 加载模型 */
  12.         ret = test.load_model(MODEL_PATH);
  13.         if (ret < 0) {
  14.                 printf("load_model error!!!\n");
  15.                 return ret;
  16.         }

  17.         /* 设置输入图像的属性 */
  18.         ret = test.set_input_info(INPUT_WIDTH, INPUT_HEIGHT, INPUT_CHANNEL);
  19.         if (ret < 0) {
  20.                 printf("set_input_info error!!!\n");
  21.                 return ret;
  22.         }

  23.         /* 开始运行,支持摄像头和视频文件方式 */
  24.         /* 使用摄像头时,VIDEO_NODE可以设置为0,表示从video 0节点获取图像数据 */
  25.         /* 使用视频时,VIDEO_NODE可以设置为文件路径,如"xxx.mp4",表示从视频文件获取图像数据 */
  26.         /* post_process为自定义的后处理函数,RKNN相关操作已封装,只需完成后处理即可 */
  27.         ret = test.run(VIDEO_NODE, post_process, &data);

  28.         return ret;
  29. }
复制代码


3.编译步骤:
1. sudo dnf install -y cmake gcc gcc-c++ opencv opencv-devel rknn-api
2. mkdir build
3. cd build
4. cmake ..
5. make
6. ./ssd_demo


4.再开发步骤:
1.  编写自己的模型demo文件,如xxx_demo.cpp
2. 修改CMakeLists.txt第26行,add_executable(ssd_demo  ssd_demo.cpp ${common_file}) 修改为add_executable(xxx_demo  xxx_demo.cpp ${common_file})
3. cd build
4. cmake ..
5. make
6. 执行./xxx_demo









作者: xiaqing10    时间: 2019-5-21 18:23
验证码错误
作者: linuxsky    时间: 2019-5-23 19:18
提取码错误
作者: lappaport    时间: 2019-5-23 20:32
百度云盘提取码错误
作者: troy    时间: 2019-5-24 09:56
xiaqing10 发表于 2019-5-21 18:23
验证码错误

已修改
作者: troy    时间: 2019-5-24 09:57
lappaport 发表于 2019-5-23 20:32
百度云盘提取码错误

已修改
作者: troy    时间: 2019-5-24 09:57
linuxsky 发表于 2019-5-23 19:18
提取码错误

已修改
作者: protossw512    时间: 2019-6-15 01:48
可以放到github上面吗?
作者: troy    时间: 2019-6-17 09:06
protossw512 发表于 2019-6-15 01:48
可以放到github上面吗?

后续会考虑一下
作者: little.zhang    时间: 2019-7-30 11:54
本帖最后由 little.zhang 于 2019-7-31 19:03 编辑

谢谢分享

作者: nopattern    时间: 2019-8-8 16:19
请教一下,和python 调用相比,能快多少?
作者: troy    时间: 2019-9-5 11:54
代码上传至github仓库
作者: shopping    时间: 2019-9-16 15:03
这样说,我 mobilenet-ssd 提高至 30fps 有救了?
作者: Devin    时间: 2020-1-2 14:33
shopping 发表于 2019-9-16 15:03
这样说,我 mobilenet-ssd 提高至 30fps 有救了?

请问你实测多少帧啊,谢谢!
作者: shopping    时间: 2020-1-7 20:53
本帖最后由 shopping 于 2020-1-7 20:55 编辑
Devin 发表于 2020-1-2 14:33
请问你实测多少帧啊,谢谢!

如果是单路视频输入,30帧可以达到。因为我们领导要求的是双路视频流输入,目前测试时双路视频流数据处理,每路大概在10帧左右,我们是隔帧送数据入npu推理的。之前尝试过4个线程送数据给npu推理,发现直接卡住不动,3个线程也不行,两个线程才勉强能跑。
作者: Mr.Tang    时间: 2020-5-6 19:03
struct rknn_out_data {
        float *out[8];
};
有问题请教您,我不是很懂c,看到你这个代码,我想把我的rknn模型换上去,以为只改后处理就行了,但是post_process函数中的数组长度对不上,我的模型只有一个输出,float *predictions = (float *)out_data->out;我改成这样打印出的sizeof只有80,和我python输出的结果差很多
作者: troy    时间: 2020-5-7 09:03
Mr.Tang 发表于 2020-5-6 19:03
struct rknn_out_data {
        float *out[8];
};

建议可以先把demo的逻辑先理一下。
框架部分,即common部分代码不需要改动,框架会去自动查询模型有几个输出,float *out[8];代表最多支持8个输出数组。out_data->out[0]存放的是第0个数组的指针(记住是数组指针,空间是由框架分配的),你的后处理的数据应该是
  1. float *predictions = (float *)out_data->out[0]
复制代码
才对!!!然后对predictions的内容打印出来跟Python对比即可。
作者: Mr.Tang    时间: 2020-5-7 09:37
本帖最后由 Mr.Tang 于 2020-5-7 11:48 编辑
troy 发表于 2020-5-7 09:03
建议可以先把demo的逻辑先理一下。
框架部分,即common部分代码不需要改动,框架会去自动查询模型有几个 ...

谢谢,还有问题需要请教在python中你们rknn的api输出的维度一般都是1*n,c输出的维度是怎样的格式,没有找到对应的文档说明。

我试着用下面的代码输出了,求和和python对比,现在的问题是这个n如何设置,我找了几个你们c的demo发现输出的output.size是python输出的4倍,你们的demo里好像是把n设置成python的+1这个地方有点疑问了?
float sum = 0;
for (int i = 0; i < n; ++i) {
        float value;
        value = predictions;
        sum += value;
//            printf("%f\n",value);
//            printf("%d\n",i);
        }
        printf("==================\n");
printf("run over, sum is %f\n",sum);

作者: troy    时间: 2020-5-7 11:59
Mr.Tang 发表于 2020-5-7 09:37
谢谢,还有问题需要请教在python中你们rknn的api输出的维度一般都是1*n,c输出的维度是怎样的格式,没有找 ...

输出的维度是由模型决定的,需要自己去看模型的输出,这个框架,只把输出当成一维数组,具体如何解析是模型决定,而不是RKNN决定。
output.size只是表示buff的大小,并不是数组的个数。输出是4倍,应该是我这个框架指定了输出类型为float,主要是因为有些模型输出是float16的类型,而C语言中没有对应的数据类型,因此设置模型输出类型为float,占用的是8个字节,所以看到会有4倍的差距。





欢迎光临 Toybrick (http://t.rock-chips.com/) Powered by Discuz! X3.3