当我们转换并量化好模型后,在PC端简单验证后,一定想将自己的模型部署到板端运行测试,但瑞芯微官方给的纯C接口使用起来,感觉较为繁琐。抱着试一试的态度,本人尝试了将常用接口封装成类似于NCNN的接口调用形式,方便自己在工程部署上的应用,目前使用效果还凑合,特将其分享给大家,其中不足之处还望各位大佬指正。QAQ
1.RKNN API函数返回值错误码
错误码 | 错误详情 |
---|---|
RKNN_SUCC(0) | 执行成功 |
RKNN_ERR_FAIL(-1) | 执行出错 |
RKNN_ERR_TIMEOUT(-2) | 执行超时 |
RKNN_ERR_DEVICE_UNAVAILABLE(-3) | NPU 设备不可用 |
RKNN_ERR_MALLOC_FAIL(-4) | 内存分配失败 |
RKNN_ERR_PARAM_INVALID(-5) | 传入参数错误 |
RKNN_ERR_MODEL_INVALID(-6) | 传入的 RKNN 模型无效 |
RKNN_ERR_CTX_INVALID(-7) | 传入的 rknn_context 无效 |
RKNN_ERR_INPUT_INVALID(-8) | 传入的 rknn_input 对象无效 |
RKNN_ERR_OUTPUT_INVALID(-9) | 传入的 rknn_output 对象无效 |
RKNN_ERR_DEVICE_UNMATCH(-10) | 版本不匹配 |
RKNN_ERR_INCOMPATILE_PRE_COMPILE_MODEL(-11) | RKNN 模型使用 pre_compile 模式,但是和当前驱动不兼容 |
RKNN_ERR_INCOMPATILE_OPTIMIZATION_LEVEL_VERSION(-12) | RKNN 模型设置了优化等级的选项,但是和当前驱动不兼容 |
RKNN_ERR_TARGET_PLATFORM_UNMATCH(-13) | RKNN 模型和当前平台不兼容,一般是将 RK1808 的平台的 RKNN 模型放到了 RV1109/RV1126 上。 |
RKNN_ERR_NON_PRE_COMPILED_MODEL_ON_MINI_DRIVER(-14) | RKNN 模型不是 pre_compile 模式,在 mini-driver 上无法执行 |
2.RKNN API函数介绍
-
rknn_init
-
函数说明:初始化RKNN
-
入参:
1.rknn_context *context:rknn_context 指针。函数调用之后,context 将会被赋值。
2.void *model:RKNN 模型的二进制数据。
3.uint32_t size:模型大小。
4.uint32_t flag:特定的初始化标志。 -
返回值:int 错误码(见 1.RKNN API函数返回值错误码)。
-
-
rknn_destroy
-
函数说明:销毁 rknn_context 对象及其相关资源。
-
入参:rknn_context context:要销毁的 rknn_context 对象。
-
返回值:int 错误码(见 1.RKNN API函数返回值错误码)。
-
-
rknn_query
- 函数说明:查询模型与 SDK 的相关信息。
- 入参:
1.rknn_context context:rknn_context 对象。
2.rknn_query_cmd cmd:查询命令。
3.void* info:存放返回结果的结构体变量。
4.uint32_t size:info 对应的结构体变量的大小。 - 返回值:int 错误码(见 1.RKNN API函数返回值错误码)。
-
rknn_inputs_set
-
函数说明:设置模型输入数据。
-
入参:
1.rknn_context context:rknn_contex 对象。
2.uint32_t n_inputs:输入数据个数。
3.rknn_input inputs[]:输入数据数组,数组每个元素是 rknn_input 结构体对象。 -
返回值:int 错误码(见 1.RKNN API函数返回值错误码)。
-
-
rknn_run
-
函数说明:执行一次模型推理。
-
入参:
1.rknn_context context:rknn_context 对象。
2.rknn_run_extend* extend:保留扩展,当前没有使用,传入 NULL 即可。 -
返回值:int 错误码(见 1.RKNN API函数返回值错误码)。
-
-
rknn_outputs_get
-
函数说明:获取模型推理输出
-
入参:
1.rknn_context context:rknn_context 对象。
2.uint32_t n_outputs:输出数据个数。
3.rknn_output outputs[]:输出数据的数组,其中数组每个元素为 rknn_output 结构体对象,代表模型的一个输出。
4.rknn_output_extend* extend:保留扩展,当前没有使用,传入 NULL 即可。 -
返回值:int 错误码(见 1.RKNN API函数返回值错误码)。
-
-
rknn_outputs_release
-
函数说明:释放 rknn_output 对象。
-
入参:
1.rknn_context context:rknn_context 对象。
2.uint32_t n_outputs:输出数据个数。
3.rknn_output outputs[]:要销毁的 rknn_output 数组。 -
返回值:int 错误码(见 1.RKNN API函数返回值错误码)。
-
3.封装代码
net.h
#ifndef _RKNN_NET_H
#define _RKNN_NET_H
#include "opencv2/opencv.hpp"
#include "rknn_api.h"
typedef struct {
int w;
int h;
int c;
float* data;
} Tensor;
class Extractor;
class Net {
public:
rknn_context ctx;
Net() = default;
~Net();
Net(const Net& rhs) = delete;
Net& operator=(const Net& rhs) = delete;
void clear();
int loadModel(const char* filename);
Extractor createExtractor() const;
};
class Extractor {
private:
const Net* net;
rknn_input_output_num io_num;
rknn_output* outputs;
rknn_tensor_attr* output_attr;
public:
~Extractor();
int input(const cv::Mat& in);
int extract(int blob_index, Tensor& feat);
protected:
friend Extractor Net::createExtractor() const;
Extractor(const Net* net);
};
#endif
net.cpp
#include "rknn_net.h"
#include <stdio.h>
#include <stdlib.h>
Net::~Net() { clear(); }
int Net::loadModel(const char *filename) {
FILE *fp = fopen(filename, "rb");
if (fp == nullptr) {
printf("fopen %s fail!\n", filename);
return -1;
}
fseek(fp, 0, SEEK_END);
size_t model_len = ftell(fp);
unsigned char *model = (unsigned char *)malloc(model_len);
fseek(fp, 0, SEEK_SET);
if (model_len != fread(model, 1, model_len, fp)) {
printf("fread %s fail!\n", filename);
free(model);
model = nullptr;
return -2;
}
if (fp) {
fclose(fp);
}
int ret = rknn_init(&ctx, model, model_len, 0);
if (ret < 0) {
free(model);
model = nullptr;
printf("rknn_init fail! ret=%d\n", ret);
return -3;
}
free(model);
model = nullptr;
return 0;
}
Extractor Net::createExtractor() const { return Extractor(this); }
void Net::clear() {
if (ctx >= 0) {
rknn_destroy(ctx);
}
}
Extractor::Extractor(const Net *_net) : net(_net) {
int ret =
rknn_query(net->ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
if (ret != RKNN_SUCC) {
printf("rknn_query fail! ret=%d\n", ret);
return;
}
outputs = (rknn_output *)malloc(sizeof(rknn_output) * io_num.n_output);
memset(outputs, 0, sizeof(rknn_output) * io_num.n_output);
for (size_t i = 0; i < io_num.n_output; i++) {
outputs[i].want_float = 1;
}
output_attr =
(rknn_tensor_attr *)malloc(sizeof(rknn_tensor_attr) * io_num.n_output);
memset(output_attr, 0, sizeof(rknn_tensor_attr) * io_num.n_output);
for (size_t i = 0; i < io_num.n_output; i++) {
output_attr[i].index = i;
ret = rknn_query(net->ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attr[i]),
sizeof(rknn_tensor_attr));
if (ret != RKNN_SUCC) {
printf("rknn_query fail! ret=%d\n", ret);
}
}
}
Extractor::~Extractor() {
rknn_outputs_release(net->ctx, io_num.n_output, outputs);
if (outputs) {
free(outputs);
outputs = nullptr;
}
if (output_attr) {
free(output_attr);
output_attr = nullptr;
}
}
int Extractor::input(const cv::Mat &in) {
if (in.empty()) {
return -1;
}
rknn_input inputs[io_num.n_input];
memset(inputs, 0, sizeof(inputs));
inputs[0].index = 0;
inputs[0].type = RKNN_TENSOR_UINT8;
inputs[0].size = in.cols * in.rows * in.channels();
inputs[0].fmt = RKNN_TENSOR_NHWC;
inputs[0].buf = in.data;
int ret = rknn_inputs_set(net->ctx, io_num.n_input, inputs);
if (ret < 0) {
printf("rknn_input_set fail! ret=%d\n", ret);
return -3;
}
ret = rknn_run(net->ctx, nullptr);
if (ret < 0) {
printf("rknn_run fail! ret=%d\n", ret);
return -4;
}
ret = rknn_outputs_get(net->ctx, io_num.n_output, outputs, NULL);
if (ret < 0) {
printf("rknn_outputs_get fail! ret=%d\n", ret);
return -5;
}
return 0;
}
int Extractor::extract(int blob_index, Tensor &feat) {
feat.c = output_attr[blob_index].dims[2];
feat.h = output_attr[blob_index].dims[1];
feat.w = output_attr[blob_index].dims[0];
feat.data = (float *)outputs[blob_index].buf;
return 0;
}