【RK3588开发】RKNN库的使用
RKNN库的使用
使用的RKNN库为librknnrt.so和librknn_api.so,两者均为 C 语言接口库
(1)头文件
#include "rknn_api.h"
(2)加载模型
将rknn模型加载至内存
static unsigned char *load_model(const char *filename, int *model_size)
{FILE *fp = fopen(filename, "rb");if (fp == nullptr){printf("fopen %s fail!\n", filename);return NULL;}fseek(fp, 0, SEEK_END);int 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);return NULL;}*model_size = model_len;if (fp){fclose(fp);}return model;
}
(3)模型初始化
函数原型:
int rknn_init(rknn_context* context, void* model, int model_len, int flags, rknn_init_extend* extend);
/*
ctx:上下文句柄指针,函数成功调用后,会在此写入已初始化的RKNN上下文句柄。后续所有RKNN操作(如设置输入、执行推理、获取输出)都依赖于这个句柄
model:模型数据指针,指向已加载到内存中的RKNN模型数据的指针
model_len:模型数据长度,指定model指针所指向数据的长度,单位是字节
flags:初始化标志,用于控制初始化行为的位掩码。通常设置为0以使用默认配置
extend:扩展参数指针,指向可选扩展参数的指针,通常设置为NULL
*/
使用示例:
int ret;
rknn_context ctx = 0;
ret = rknn_init(&ctx, model, model_len, 0, NULL);
if (ret < 0)
{printf("rknn_init fail! ret=%d\n", ret);return -1;
}
(4)获得模型输入和输出信息
函数原型:
int rknn_query(rknn_context context, rknn_query_cmd cmd, void* info, uint32_t size);
/*
ctx:已初始化的 RKNN 上下文句柄
cmd:查询命令,指定要查询的信息类型
info:指向接收查询结果缓冲区的指针
size:缓冲区的大小
*/
查询命令 | 用途 | 返回结构体 |
---|---|---|
RKNN_QUERY_INPUT_ATTR | 查询输入属性 | rknn_input_attr |
RKNN_QUERY_OUTPUT_ATTR | 查询输出属性 | rknn_output_attr |
RKNN_QUERY_SDK_VERSION | 查询SDK版本 | rknn_sdk_version |
RKNN_QUERY_PERF_DETAIL | 查询性能详情 | rknn_perf_detail |
RKNN_QUERY_MEM_SIZE | 查询内存大小 | rknn_mem_size |
使用示例:
信息补充
typedef struct {uint32_t n_input; // 输入张量数量uint32_t n_output; // 输出张量数量
} rknn_input_output_num;typedef struct {uint32_t index; // 输出张量索引uint32_t n_dims; // 维度数量 (通常是4)uint32_t dims[4]; // 维度值 [batch, height, width, channels]uint32_t size; // 数据总大小(字节)rknn_type type; // 数据类型 (FP32, FP16, INT8, UINT8等)rknn_qnt_type qnt_type; // 量化类型 (NONE, AFFINE等)int32_t zp; // 零点(量化参数)float scale; // 缩放系数(量化参数)char name[256]; // 输出张量名称
} rknn_tensor_attr;/*打印输入属性信息*/
static void dump_tensor_attr(rknn_tensor_attr *attr)
{printf(" index=%d, name=%s, n_dims=%d, dims=[%d, %d, %d, %d], n_elems=%d, size=%d, fmt=%s, type=%s, qnt_type=%s, ""zp=%d, scale=%f\n",attr->index, attr->name, attr->n_dims, attr->dims[0], attr->dims[1], attr->dims[2], attr->dims[3],attr->n_elems, attr->size, get_format_string(attr->fmt), get_type_string(attr->type),get_qnt_type_string(attr->qnt_type), attr->zp, attr->scale);
}
①获取模型的输入和输出数量
int ret;
rknn_input_output_num io_num;
ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
if (ret != RKNN_SUCC)
{printf("rknn_query fail! ret=%d\n", ret);return -1;
}
②获取模型的输入属性信息
int ret;
rknn_tensor_attr input_attrs[io_num.n_input];
memset(input_attrs, 0, sizeof(input_attrs));
for (int i = 0; i < io_num.n_input; i++)
{input_attrs[i].index = i;ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr));if (ret != RKNN_SUCC){printf("rknn_query fail! ret=%d\n", ret);return -1;}dump_tensor_attr(&(input_attrs[i]));
}
③获取模型的输出属性信息
int ret;
rknn_tensor_attr output_attrs[io_num.n_output];
memset(output_attrs, 0, sizeof(output_attrs));
for (int i = 0; i < io_num.n_output; i++)
{output_attrs[i].index = i;ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr));if (ret != RKNN_SUCC){printf("rknn_query fail! ret=%d\n", ret);return -1;}dump_tensor_attr(&(output_attrs[i]));
}
(5)设置输入数据
信息补充
/*输入数据结构体*/
typedef struct _rknn_input {uint32_t index; //输入索引,对于哪个输入口,索引就是输入口的标识符void* buf; //输入数据指针uint32_t size; //输入数据大小uint8_t pass_through; //直通模式//若为TRUE(直通模式开启):缓冲区(buf)中的数据会直接传递到 rknn 模型的输入节点,不做任何转换。这种情况下,后续相关的变量无需设置。//若为FALSE(直通模式关闭):缓冲区数据会根据后续设置的类型(type)和格式(fmt)进行转换,以匹配模型的输入要求。这种情况下,需要设置后续相关变量。rknn_tensor_type type; //数据类型rknn_tensor_format fmt; //数据格式
//currently the internal input format of NPU is NCHW by default.
//so entering NCHW data can avoid the format conversion in the driver.
} rknn_input;
函数原型:
int rknn_inputs_set(rknn_context context, uint32_t n_inputs, rknn_input inputs[]);
/*
context:RKNN模型的上下文句柄,用于标识和管理加载的模型实例。
n_inputs:需要设置的输入数量。
inputs[]:输入结构体数组,包含了每个输入张量的具体信息,inputs[]表示指针
*/
使用示例:
int ret;
rknn_input inputs[1];
memset(inputs, 0, sizeof(inputs));inputs[0].index = 0;
inputs[0].type = input_attr.type;
inputs[0].size = input_attr.size;
inputs[0].fmt = input_attr.fmt;
inputs[0].buf = data_buf;
ret = rknn_inputs_set(ctx, io_num.n_input, inputs);
if (ret < 0)
{printf("rknn_input_set fail! ret=%d\n", ret);return -1;
}
(6)运行模型
函数原型:
int rknn_run(rknn_context context, rknn_run_extend* extend);/*
context:RKNN模型的上下文句柄,用于标识和管理加载的模型实例
extend:扩展参数指针,用于设置推理时的额外选项,在大多数情况下可以设置为NULL
*/
使用示例:
int ret;
ret = rknn_run(ctx, NULL);
if (ret < 0)
{printf("rknn_run fail! ret=%d\n", ret);return -1;
}
(7)获取输出信息
信息补充
/*数据数据结构*/
typedef struct _rknn_output {uint8_t want_float; /*作用: 指定是否将输出数据转换为浮点数格式,1: 希望将输出数据转换为浮点数0: 保持原始数据类型(通常是量化后的int8/int16)*/uint8_t is_prealloc; //指定输出缓冲区是否由用户预分配,默认不分配uint32_t index; //输出张量的索引编号void* buf; //输出缓冲区的指针uint32_t size; //输出缓冲区的大小} rknn_output;
函数原型:
int rknn_outputs_get(rknn_context context, uint32_t n_outputs, rknn_output outputs[], rknn_output_extend* extend);
/*
context:RKNN模型的上下文句柄,用于标识和管理加载的模型实例。
n_inputs:输出数量。
outputs[]:输出结构体数组,包含了每个输出张量的具体信息
extend:扩展参数指针,在大多数情况下可以设置为NULL
*/
使用示例:
rknn_output outputs[1];
memset(outputs, 0, sizeof(outputs));
outputs[0].want_float = 1;
ret = rknn_outputs_get(ctx, 1, outputs, NULL);
if (ret < 0)
{printf("rknn_outputs_get fail! ret=%d\n", ret);return -1;
}
(8)释放输出缓冲区
因为每一次获取数据,都会自动分配的输出缓冲区内存,rknn_outputs_release函数释放由 RKNN 运行时自动分配的输出缓冲区内存,避免内存泄漏
函数原型:
int rknn_outputs_release(rknn_context context, uint32_t n_ouputs, rknn_output outputs[]);
/*
context:RKNN模型的上下文句柄,用于标识和管理加载的模型实例。
n_ouputs:要释放的输出数量。
outputs[]:输出结构体数组,
*/
使用示例:
rknn_outputs_release(ctx, 1, outputs);
(9)资源清理函数
函数原型:
int rknn_destroy(rknn_context context);
//context:RKNN模型的上下文句柄,用于标识和管理加载的模型实例。
使用示例:
rknn_destroy(ctx);