官方仓库地址GitHub - airockchip/rknn_model_zoo
代码比较长,函数定义和main函数分开解释。main中定义的函数有printRKNNTensor,__get_us,load_data,load_model,saveFloat,load_image,load_hyperm_param,query_model_info。
//打印rknntensor属性,接受指向rknntensor属性结构体的指针attr,打印属性的各个字段
static void printRKNNTensor(rknn_tensor_attr *attr)
{
//打印索引,名字,维度数组,元素数量,大小,格式,数据类型,量化类型
printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d "
"fmt=%d type=%d qnt_type=%d fl=%d zp=%d scale=%f\n",
attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2],
attr->dims[1], attr->dims[0], attr->n_elems, attr->size, 0, attr->type,
attr->qnt_type, attr->fl, attr->zp, attr->scale);
}
//获取时间戳,接受一个timeval结构体,返回double类型的时间戳
double __get_us(struct timeval t) { return (t.tv_sec * 1000000 + t.tv_usec); }
//加载数据,接受指向文件的指针fp,数据偏移量ofst,数据的大小sz,返回指向加载的数据的无符号字符指针
static unsigned char *load_data(FILE *fp, size_t ofst, size_t sz)
{
//将数据指针data初始化为NULL
unsigned char *data;
int ret;
data = NULL;
//fa为空的话,返回null,数据集加载失败
if (NULL == fp)
{
return NULL;
}
//调用fseek函数将文件指针fp定位到偏移量ofst处
ret = fseek(fp, ofst, SEEK_SET);
if (ret != 0)
{
printf("blob seek failure.\n");
return NULL;
}
//用malloc函数动态分配一个大小为sz的内存块,将其赋给数据指针data
data = (unsigned char *)malloc(sz);
if (data == NULL)
{
printf("buffer malloc failure.\n");
return NULL;
}
//调用fread函数从文件中读取sz字节的数据,存储到数据指针data指向的内存
ret = fread(data, 1, sz, fp);
return data;
}
//加载模型文件,接受模型文件和指向整型变量model_size的指针,返回一个指向加载的模型数据的无符号字符指针。
static unsigned char *load_model(const char *filename, int *model_size)
{
//声明一个文件指针 fp 和一个数据指针 data
FILE *fp;
unsigned char *data;
//使用 fopen 函数以二进制只读模式打开指定路径的模型文件。
fp = fopen(filename, "rb");
if (NULL == fp)
{
printf("Open file %s failed.\n", filename);
return NULL;
}
//使用 fseek 函数将文件指针定位到文件末尾
fseek(fp, 0, SEEK_END);
//使用 ftell 函数获取文件大小并赋值给变量 size
int size = ftell(fp);
//调用load_data函数从文件中加载数据,偏移量为 0,大小为 size
data = load_data(fp, 0, size);
//使用 fclose 函数关闭文件指针 fp。
fclose(fp);
//将变量 size 的值赋给 model_size
*model_size = size;
return data;
}
//将浮点数数组保存到文件
//接受一个文件名 file_name、一个浮点数数组 output 和数组元素的数量 element_size
static int saveFloat(const char *file_name, float *output, int element_size)
{
FILE *fp;
//使用 fopen 函数以写入模式打开指定文件名的文件
fp = fopen(file_name, "w");
//遍历浮点数数组 output,从第一个元素到最后一个元素
for (int i = 0; i < element_size; i++)
{
//使用 fprintf 函数将其以浮点数的格式写入文件中,保留小数点后六位
fprintf(fp, "%.6f\n", output[i]);
}
//使用 fclose 函数关闭文件指针 fp
fclose(fp);
return 0;
}
//加载图像,接受图像文件路径,指向整型变量 org_height、org_width、org_ch 和 input_attr 的指针
//返回一个指向加载的图像数据的无符号字符指针
static unsigned char *load_image(const char *image_path, int *org_height, int *org_width, int *org_ch, rknn_tensor_attr *input_attr)
{
int req_height = 0;
int req_width = 0;
int req_channel = 0;
//使用 switch 语句根据输入属性 input_attr 的格式(fmt)设置请求的图像高度、宽度和通道数。
switch (input_attr->fmt)
{
case RKNN_TENSOR_NHWC:
req_height = input_attr->dims[2];
req_width = input_attr->dims[1];
req_channel = input_attr->dims[0];
break;
case RKNN_TENSOR_NCHW:
req_height = input_attr->dims[1];
req_width = input_attr->dims[0];
req_channel = input_attr->dims[2];
break;
default:
printf("meet unsupported layout\n");
return NULL;
}
// printf("w=%d,h=%d,c=%d, fmt=%d\n", req_width, req_height, req_channel, input_attr->fmt);
int height = 0;
int width = 0;
int channel = 0;
unsigned char *image_data = stbi_load(image_path, &width, &height, &channel, req_channel);
if (image_data == NULL)
{
printf("load image-%s failed!\n", image_path);
return NULL;
}
//判断图像的通道数是否为1,如果是灰度图像,则将其转换为RGB图像
if (channel == 1){
printf("image is grey, convert to RGB");
void* rgb_data = malloc(width* height* 3);
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
int offset = (i*width + j)*3;
((unsigned char*)rgb_data)[offset] = ((unsigned char*)image_data)[offset];
((unsigned char*)rgb_data)[offset + 1] = ((unsigned char*)image_data)[offset];
((unsigned char*)rgb_data)[offset + 2] = ((unsigned char*)image_data)[offset];
}
}
free(image_data);
image_data = (unsigned char*)rgb_data;
channel = 3;
}
//判断图像的宽度是否为4的倍数。如果不是,说明图像的宽度不符合像素对齐的要求,需要进行调整
if (width%4 != 0){
int new_width = width+ (4 - width%4);
printf("%d is not pixel align, resize to %d, this will make the result shift slightly\n",width, new_width);
void* resize_data = malloc(new_width* height* channel);
使用stbir_resize_uint8函数将图像数据按照新的宽度进行调整,并将结果保存在resize_data中
stbir_resize_uint8(image_data, width, height, 0, (unsigned char*)resize_data, new_width, height, 0, channel);
free(image_data);
image_data = (unsigned char*)resize_data;
*org_width = new_width;
}
else{
*org_width = width;
}
*org_height = height;
*org_ch = channel;
return image_data;
}
//加载超参数,接受指向MODEL_INFO结构体的指针,以及整型变量argc 和字符指针数组 argv
int load_hyperm_param(MODEL_INFO *m, int argc, char** argv){
if (argc != 7)
{
// printf("Usage: %s <rknn model path> [yolov5/yolov7/yolox] <anchor file path> [fp/u8] [single_img/multi_imgs] <path>\n", argv[0]);
printf("Usage: %s [yolov5/yolov7/yolox] [fp/u8] [single_img/multi_imgs] <rknn model path> <anchor file path> <input_path>\n", argv[0]);
printf(" -- [1] Model type, select from yolov5, yolov7, yolox\n");
printf(" -- [2] Post process type, select from fp, u8. Only quantize-8bit model could use u8\n");
printf(" -- [3] Test type, select from single_img, multi_imgs.\n",
" For single_img, input_path is image. jpg/bmp/bng is allow\n",
" For multi_img, input_path is txt file containing testing images path\n");
printf(" -- [4] RKNN model path\n");
printf(" -- [5] anchor file path. If using yolox model, any character is ok.\n");
printf(" -- [6] input path\n");
return -1;
}
//m->m_path 被赋值为 argv[4],anchor_path 被赋值为 argv[5],m->in_path 被赋值为 argv[6]
int ret=0;
m->m_path = (char *)argv[4];
char* anchor_path = argv[5];
m->in_path = (char *)argv[6];
if (strcmp(argv[1], "yolov5") == 0){
m->m_type = YOLOV5;
m->color_expect = RK_FORMAT_RGB_888;
m->anchor_per_branch = 3;
printf("Runing with yolov5 model\n");
}
else if (strcmp(argv[1], "yolov7") == 0){
m->m_type = YOLOV7;
//将 m->color_expect 设置为 RK_FORMAT_RGB_888,表示期望输入图像的颜色格式为 RGB 888。
m->color_expect = RK_FORMAT_RGB_888;
//将 m->anchor_per_branch 设置为 3,表示每个分支的锚点数量为 3。
m->anchor_per_branch = 3;
printf("Runing with yolov7 model\n");
}
else if (strcmp(argv[1], "yolox") == 0){
/*
RK_FORMAT_RGB_888 if normal api
RK_FORMAT_BGR_888 if pass_through/ zero_copy
*/
m->m_type = YOLOX;
m->color_expect = RK_FORMAT_RGB_888;
//将 m->anchor_per_branch 设置为 1,表示每个分支的锚点数量为 1。
m->anchor_per_branch = 1;
printf("Runing with yolox model\n");
printf("Ignore anchors file %s\n", anchor_path);
}
else{
printf("Only support yolov5/yolov7/yolox model, but got %s\n", argv[1]);
return -1;
}
// load anchors
//根据模型类型和每个分支的锚点数量计算锚点数组的大小
int n = 2* m->out_nodes* m->anchor_per_branch;
//,如果模型类型是 YOLOX,使用循环遍历锚点数组的元素,每个元素的值设置为1
if (m->m_type == YOLOX){
for (int i=0; i<n; i++){
m->anchors[i]=1;
}
}
else {
printf("anchors: ");
float result[n];
int valid_number;
//调用 readFloats 函数从锚点文件中读取 n 个浮点数值,并返回有效的浮点数个数。
ret = readFloats(anchor_path, &result[0], n, &valid_number);
//使用循环将读取的浮点数值转换为整数,并将其赋值给锚点数组的相应位置。
for (int i=0; i<valid_number; i++){
m->anchors[i] = (int)result[i];
printf("%d ", m->anchors[i]);
}
printf("\n");
}
//如果 argv[2] 的值等于 "fp",则表示选择了 FP(浮点数)类型的后处理
if (strcmp(argv[2], "fp") == 0){
m->post_type = FP;
printf("Post process with fp\n");
}
//如果 argv[2] 的值等于 "u8",则表示选择了 U8(无符号8位整数)类型的后处理。
else if (strcmp(argv[2], "u8") == 0){
m->post_type = U8;
printf("Post process with u8\n");
}
else{
printf("Post process type not support: %s\nPlease select from [fp/u8]\n", argv[2]);
return -1;
}
//argv[3] 的值等于 "single_img",则表示选择了单个图像作为输入源;
if (strcmp(argv[3], "single_img") == 0){
m->in_source = SINGLE_IMG;
printf("Test with single img\n");
}
//argv[3] 的值等于 "multi_imgs",则表示选择了多个图像作为输入源
else if (strcmp(argv[3], "multi_imgs") == 0){
m->in_source = MULTI_IMG;
printf("Test with multi imgs\n");
}
else{
printf("Test input type is not support: %s\nPlease select from [single_img/multi_imgs]\n", argv[5]);
return -1;
}
return 0;
}
//查询模型信息并将相关信息存储在MODEL_INFO结构体中
int query_model_info(MODEL_INFO *m, rknn_context ctx){
int ret;
/* Query sdk version */
//定义了一个rknn_sdk_version结构体变量version用于存储查询结果。
rknn_sdk_version version;
//调用rknn_query函数,传入参数ctx表示RKNN上下文,RKNN_QUERY_SDK_VERSION表示查询SDK版本的标识,
//&version表示存储查询结果的变量的地址,sizeof(rknn_sdk_version)表示查询结果的长度。
ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version,
sizeof(rknn_sdk_version));
if (ret < 0)
{
printf("rknn_init error ret=%d\n", ret);
return -1;
}
printf("sdk version: %s driver version: %s\n", version.api_version,
version.drv_version);
/* Get input,output attr */
//定义了一个rknn_input_output_num结构体变量io_num用于存储查询结果
rknn_input_output_num io_num;
//调用rknn_query函数,传入参数ctx表示RKNN上下文,RKNN_QUERY_IN_OUT_NUM表示查询输入输出节点数量的标识,
//&io_num表示存储查询结果的变量的地址,sizeof(io_num)表示查询结果的长度
ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
if (ret < 0)
{
printf("rknn_init error ret=%d\n", ret);
return -1;
}
printf("model input num: %d, output num: %d\n", io_num.n_input,
io_num.n_output);
//查询到的输入节点数量io_num.n_input赋值给MODEL_INFO结构体中的in_nodes成员变量
m->in_nodes = io_num.n_input;
//查询到的输出节点数量io_num.n_output赋值给MODEL_INFO结构体中的out_nodes成员变量
m->out_nodes = io_num.n_output;
//用动态内存分配函数malloc来为MODEL_INFO结构体中的in_attr和out_attr成员变量分配内存空间。
//大小为每个输入节点的属性结构体rknn_tensor_attr的大小乘以输入节点数量和输出节点
//分配成功,返回的指针被转换为rknn_tensor_attr指针,并赋值给MODEL_INFO结构体中的in_attr和out_attr成员变量
m->in_attr = (rknn_tensor_attr*)malloc(sizeof(rknn_tensor_attr)* io_num.n_input);
m->out_attr = (rknn_tensor_attr*)malloc(sizeof(rknn_tensor_attr)* io_num.n_output);
if (m->in_attr == NULL || m->out_attr == NULL){
printf("alloc memery failed\n");
return -1;
}
//使用循环遍历所有的输入节点,并查询每个输入节点的属性信息
for (int i = 0; i < io_num.n_input; i++)
{
//变量i赋值给m->in_attr[i].index,表示输入节点的索引
m->in_attr[i].index = i;
//调用rknn_query函数,传入参数ctx表示RKNN上下文,RKNN_QUERY_INPUT_ATTR表示查询输入节点属性的标识,
//&m->in_attr[i]表示存储查询结果的变量的地址,sizeof(rknn_tensor_attr)表示查询结果的长度。
ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &m->in_attr[i],
sizeof(rknn_tensor_attr));
if (ret < 0)
{
printf("rknn_init error ret=%d\n", ret);
return -1;
}
//调用printRKNNTensor函数打印输入节点的属性信息,
//函数的参数为&m->in_attr[i],表示传递查询到的输入节点的属性信息。
printRKNNTensor(&m->in_attr[i]);
}
//同上,使用循环遍历所有的输出节点,并查询每个输出节点的属性信息
for (int i = 0; i < io_num.n_output; i++)
{
m->out_attr[i].index = i;
ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(m->out_attr[i]),
sizeof(rknn_tensor_attr));
printRKNNTensor(&(m->out_attr[i]));
}
/* get input shape */
//rknn不支持多输入
if (io_num.n_input > 1){
printf("expect model have 1 input, but got %d\n", io_num.n_input);
return -1;
}
//通过判断m->in_attr[0].fmt的值是否等于RKNN_TENSOR_NCHW
if (m->in_attr[0].fmt == RKNN_TENSOR_NCHW)
{
printf("model is NCHW input fmt\n");
m->width = m->in_attr[0].dims[0];
m->height = m->in_attr[0].dims[1];
m->channel = m->in_attr[0].dims[2];
}
else
{
printf("model is NHWC input fmt\n");
m->width = m->in_attr[0].dims[1];
m->height = m->in_attr[0].dims[2];
m->channel = m->in_attr[0].dims[0];
}
printf("model input height=%d, width=%d, channel=%d\n", m->height, m->width,
m->channel);
return 0;
}
main逻辑比较简单,初始化,加载超参数,初始化张量,读图,前处理,推理,后处理,画框。
在demo中提供了单图输入和多图出入两种检测方式,以及带后处理和不带后处理循环读图测试模型推理速度。
int main(int argc, char **argv)
{
int status = 0;
rknn_context ctx;
rga_context rga_ctx;
drm_context drm_ctx;
void *drm_buf = NULL;
int drm_fd = -1;
int buf_fd = -1; // converted from buffer handle
unsigned int handle;
//使用menset函数将rga_ctx,rga_ctx内存初始化为0
memset(&rga_ctx, 0, sizeof(rga_context));
memset(&drm_ctx, 0, sizeof(drm_context));
//调用drm_init函数,传入&drm_ctx作为参数,
//以初始化DRM上下文并返回DRM文件描述符,将返回的文件描述符赋值给drm_fd变量
drm_fd = drm_init(&drm_ctx);
MODEL_INFO m_info;
LETTER_BOX letter_box;
size_t actual_size = 0;
int img_width = 0;
int img_height = 0;
int img_channel = 0;
struct timeval start_time, stop_time;
int ret;
//传入&m_info、argc和argv作为参数,并将返回值赋给了变量ret
//加载超参数,根据命令行参数(argc和argv)以及m_info结构体来解析和设置超参数。
ret = load_hyperm_param(&m_info, argc, argv);
if (ret < 0) return -1;
//
/* Create the neural network */
printf("Loading model...\n");
int model_data_size = 0;
//加载模型并初始化
unsigned char *model_data = load_model(m_info.m_path, &model_data_size);
ret = rknn_init(&ctx, model_data, model_data_size, 0);
if (ret < 0)
{
printf("rknn_init error ret=%d\n", ret);
return -1;
}
查询模型信息并将相关信息存储在MODEL_INFO结构体中
printf("query info\n");
ret = query_model_info(&m_info, ctx);
if (ret < 0){
return -1;
}
//初始化输入的张量
/* Init input tensor */
//输入节点数为1
rknn_input inputs[1];
//使用memset函数将inputs数组的内存空间初始化为0
memset(inputs, 0, sizeof(inputs));
//index:设置为0,表示输入节点的索引
inputs[0].index = 0;
//输入节点的数据类型为无符号整型。
inputs[0].type = RKNN_TENSOR_UINT8; /* SAME AS INPUT IMAGE */
//输入节点的数据总大小
inputs[0].size = m_info.width * m_info.height * m_info.channel;
//表示输入节点的数据格式为NHWC
inputs[0].fmt = RKNN_TENSOR_NHWC; /* SAME AS INPUT IMAGE */
inputs[0].pass_through = 0;
/* Init output tensor */
//初始化输出节点
rknn_output outputs[m_info.out_nodes];
memset(outputs, 0, sizeof(outputs));
//设置输出节点的want_float属性为m_info.post_type
for (int i = 0; i < m_info.out_nodes; i++)
{
outputs[i].want_float = m_info.post_type;
}
//分配大小为inputs[0].size的内存空间,将返回指针赋值给resize_buf
void *resize_buf = malloc(inputs[0].size);
if (resize_buf == NULL){
printf("resize buf alloc failed\n");
return -1;
}
unsigned char *input_data = NULL;
/* Single img input */
/* Due to different input img size, multi img method has to calculate letterbox param each time*/
if (m_info.in_source == SINGLE_IMG)
{
/* Input preprocess */
// Load image
//使用CImg库定义了一个CImg对象img,模板参数为unsigned char,表示图像像素的数据类型。
//构造函数参数为m_info.in_path,表示要加载的图像的路径。
CImg<unsigned char> img(m_info.in_path);
//调用load_image函数加载图像,传入图像路径m_info.in_path
//输出图像的高、宽、通道数
input_data = load_image(m_info.in_path, &img_height, &img_width, &img_channel, &m_info.in_attr[0]);
if (!input_data){
fprintf(stderr, "Error in loading input image\n");
return -1;
}
//打印长宽高和通道数
printf("img_width: %d\nimg_height: %d\nimg_channel: %d\n", img_width, img_height, img_channel);
// DRM alloc buffer
//为DRM分配一个buffer。它接受图像的宽度、高度、通道数和bit深度等参数
//返回buffer的文件描述符(buf_fd)、句柄(handle)和实际大小(actual_size)。
drm_buf = drm_buf_alloc(&drm_ctx, drm_fd, img_width, img_height, img_channel * 8,
&buf_fd, &handle, &actual_size);
printf("create drm buffer finish\n");
//使用memcpy()将输入图像数据input_data拷贝到前面分配的DRM buffer中(drm_buf)
memcpy(drm_buf, input_data, img_width * img_height * img_channel);
//使用memset()将名为resize_buf的buffer清零
memset(resize_buf, 0, inputs[0].size);
printf("set resize buf finish\n");
// Letter box resize
//将letter_box.target_height和letter_box.target_width设置为m_info.height和m_info.width
letter_box.target_height = m_info.height;
letter_box.target_width = m_info.width;
letter_box.in_height = img_height;
letter_box.in_width = img_width;
compute_letter_box(&letter_box);
//判断输入图像的尺寸与模型的期望尺寸是否相等
if ((m_info.height!= img_height) || (m_info.width!= img_width)){
// Init rga context
printf("WARNING: img_size(w:%d h:%d) not match model input_size(w:%d h:%d)\n", img_width, img_height, m_info.width, m_info.height);
printf("Try resize img with rga. If any error occur, please using image with size(w:%d h:%d) or using newer firmware to update rga driver version\n", m_info.width, m_info.height);
//调用RGA_init函数初始化RGA上下文
RGA_init(&rga_ctx);
//调用img_resize_slow函数,使用RGA进行图像调整大小操作,将结果存储在resize_buf中
img_resize_slow(&rga_ctx, drm_buf, img_width, img_height, resize_buf, letter_box.resize_width, letter_box.resize_height,
letter_box.w_pad, letter_box.h_pad, m_info.color_expect,
letter_box.add_extra_sz_w_pad, letter_box.add_extra_sz_h_pad);
//将resize_buf赋值给inputs[0].buf
inputs[0].buf = resize_buf;
}
else{
inputs[0].buf = drm_buf;
}
//将输入数据保存到文件中
#if (DUMP_INPUT == 1)
//声明一个文件指针变量dump_file
FILE* dump_file;
//使用fopen函数打开一个名为./demo_c_input_hwc_rgb.txt的文件,并以二进制写入模式打开
if ((dump_file = fopen("./demo_c_input_hwc_rgb.txt", "wb")) == NULL){
printf("Dump input Failed !\n");
}
//使用fwrite函数将resize_buf中的数据以uint8_t类型写入到文件中,写入的数据大小为inputs[0].size字节
else{
fwrite(resize_buf, sizeof(uint8_t), inputs[0].size, dump_file);
printf("Dump input Successed. Path: ./demo_c_input_hwc_rgb.txt !\n");
}
//使用fclose函数关闭文件指针dump_file,释放文件资源
fclose(dump_file);
#endif
//使用gettimeofday函数获取当前时间,将其保存在start_time变量中
gettimeofday(&start_time, NULL);
//使用rknn_inputs_set函数将输入数据设置到ctx上下文中,参数m_info.in_nodes表示输入节点的数量
rknn_inputs_set(ctx, m_info.in_nodes, inputs);
//使用rknn_run函数执行模型推理,将输出结果保存在outputs数组中
ret = rknn_run(ctx, NULL);
//使用rknn_outputs_get函数从ctx上下文中获取输出数据,参数m_info.out_nodes表示输出节点的数量。
ret = rknn_outputs_get(ctx, m_info.out_nodes, outputs, NULL);
/* Post process */
//声明detect_result_group的detect_result_group_t结构体变量
detect_result_group_t detect_result_group;
//调用post_process函数进行后处理,将输出数据进行解析和处理,将结果保存在detect_result_group中
post_process(outputs, &m_info, &letter_box, &detect_result_group);
//使用gettimeofday函数获取当前时间,将其保存在stop_time变量中
gettimeofday(&stop_time, NULL);
printf("once run use %f ms\n",
(__get_us(stop_time) - __get_us(start_time)) / 1000);
// Draw Objects
//定义了一个名为blue的无符号字符数组,表示绘制框的颜色为蓝色
const unsigned char blue[] = {0, 0, 255};
char score_result[64];
//遍历detect_result_group.results中的每个检测结果
for (int i = 0; i < detect_result_group.count; i++)
{
//检测结果的各个属性(名称、边界框坐标、置信度)
detect_result_t *det_result = &(detect_result_group.results[i]);
printf("%s @ (%d %d %d %d) %f\n",
det_result->name,
det_result->box.left, det_result->box.top, det_result->box.right, det_result->box.bottom,
det_result->prop);
//画框
int x1 = det_result->box.left;
int y1 = det_result->box.top;
int x2 = det_result->box.right;
int y2 = det_result->box.bottom;
int ret = snprintf(score_result, sizeof score_result, "%f", det_result->prop);
//draw box
img.draw_rectangle(x1, y1, x2, y2, blue, 1, ~0U);
img.draw_text(x1, y1 - 24, det_result->name, blue);
img.draw_text(x1, y1 - 12, score_result, blue);
}
img.save("./out.bmp");
//使用rknn_outputs_release函数释放输出缓冲区的内存
ret = rknn_outputs_release(ctx, m_info.out_nodes, outputs);
//带后处理和不带后处理循环推理
// loop test without preprocess, postprocess
int test_count = 10;
gettimeofday(&start_time, NULL);
for (int i = 0; i < test_count; ++i)
{
rknn_inputs_set(ctx, m_info.in_nodes, inputs);
ret = rknn_run(ctx, NULL);
ret = rknn_outputs_get(ctx, m_info.out_nodes, outputs, NULL);
ret = rknn_outputs_release(ctx, m_info.out_nodes, outputs);
}
gettimeofday(&stop_time, NULL);
printf("WITHOUT POST_PROCESS\n run loop count = %d , average time: %f ms\n", test_count,
(__get_us(stop_time) - __get_us(start_time)) / 1000.0 / test_count);
// loop test with postprocess
gettimeofday(&start_time, NULL);
for (int i = 0; i < test_count; ++i)
{
rknn_inputs_set(ctx, m_info.in_nodes, inputs);
ret = rknn_run(ctx, NULL);
ret = rknn_outputs_get(ctx, m_info.out_nodes, outputs, NULL);
post_process(outputs, &m_info, &letter_box, &detect_result_group);
ret = rknn_outputs_release(ctx, m_info.out_nodes, outputs);
}
gettimeofday(&stop_time, NULL);
printf("WITH POST_PROCESS\n run loop count = %d , average time: %f ms\n", test_count,
(__get_us(stop_time) - __get_us(start_time)) / 1000.0 / test_count);
drm_buf_destroy(&drm_ctx, drm_fd, buf_fd, handle, drm_buf, actual_size);
drm_deinit(&drm_ctx, drm_fd);
free(input_data);
}
//多图像的执行语句
if(m_info.in_source == MULTI_IMG){
//声明一个名为output_file的文件指针变量,并将其初始化为NULL
FILE *output_file = NULL;
//使用fopen函数打开一个名为./result_record.txt的文件
output_file = fopen("./result_record.txt", "w+");
//声明一个名为img_paths的字符串指针数组
char *img_paths[COCO_IMG_NUMBER];
//调用readLines函数从m_info.in_path文件中读取图像文件的路径,并将路径保存在img_paths数组中。
ret = readLines(m_info.in_path, img_paths, COCO_IMG_NUMBER);
//调用drm_init函数初始化DRM
drm_fd = drm_init(&drm_ctx);
RGA_init(&rga_ctx);
//使用循环遍历每个图像路径,从img_paths数组中获取图像路径
for (int j=0; j<COCO_IMG_NUMBER; j++)
{
printf("[%d/%d]Detect on %s\n", j+1, COCO_IMG_NUMBER, img_paths[j]);
/* Input preprocess */
// Load image
CImg<unsigned char> img(img_paths[j]);
input_data = load_image(img_paths[j], &img_height, &img_width, &img_channel, &m_info.in_attr[0]);
if (!input_data){
fprintf(stderr, "Error in loading input image");
return -1;
}
// DRM alloc buffer
//使用drm_buf_alloc函数在DRM中分配一个缓冲区,用于存储图像数据。
drm_buf = drm_buf_alloc(&drm_ctx, drm_fd, img_width, img_height, img_channel * 8,
&buf_fd, &handle, &actual_size);
//使用memcpy函数将input_data中的数据拷贝到drm_buf中
memcpy(drm_buf, input_data, img_width * img_height * img_channel);
memset(resize_buf, 0, inputs[0].size);
// Letter box resize
//图像缩放操作,并将缩放后的图像数据保存在resize_buf中。
letter_box.target_height = m_info.height;
letter_box.target_width = m_info.width;
letter_box.in_height = img_height;
letter_box.in_width = img_width;
compute_letter_box(&letter_box);
// Init rga context
img_resize_slow(&rga_ctx, drm_buf, img_width, img_height, resize_buf, letter_box.resize_width, letter_box.resize_height,
letter_box.w_pad, letter_box.h_pad, m_info.color_expect,
letter_box.add_extra_sz_w_pad, letter_box.add_extra_sz_h_pad);
inputs[0].buf = resize_buf;
//推理,同上
gettimeofday(&start_time, NULL);
rknn_inputs_set(ctx, m_info.in_nodes, inputs);
ret = rknn_run(ctx, NULL);
ret = rknn_outputs_get(ctx, m_info.out_nodes, outputs, NULL);
/* Post process */
detect_result_group_t detect_result_group;
post_process(outputs, &m_info, &letter_box, &detect_result_group);
gettimeofday(&stop_time, NULL);
printf("once run use %f ms\n",
(__get_us(stop_time) - __get_us(start_time)) / 1000);
// Draw Objects
//后处理,同上
const unsigned char blue[] = {0, 0, 255};
char score_result[64];
for (int i = 0; i < detect_result_group.count; i++)
{
detect_result_t *det_result = &(detect_result_group.results[i]);
printf("%s @ (%d %d %d %d) %f\n",
det_result->name,
det_result->box.left, det_result->box.top, det_result->box.right, det_result->box.bottom,
det_result->prop);
fprintf(output_file, "%s,%s,%d,%f,(%d %d %d %d)\n",
img_paths[j],
det_result->name,
det_result->class_index,
det_result->prop,
det_result->box.left, det_result->box.top, det_result->box.right, det_result->box.bottom);
}
ret = rknn_outputs_release(ctx, m_info.out_nodes, outputs);
drm_buf_destroy(&drm_ctx, drm_fd, buf_fd, handle, drm_buf, actual_size);
free(input_data);
}
fclose(output_file);
drm_deinit(&drm_ctx, drm_fd);
}
// release
//用于释放资源和清理内存
ret = rknn_destroy(ctx);
//调用RGA_deinit函数释放RGA相关的资源。
RGA_deinit(&rga_ctx);
if (model_data)
{
free(model_data);
}
//使用free函数释放resize_buf所占用的内存。
if (resize_buf)
{
free(resize_buf);
}
//使用free函数释放m_info.in_attr所占用的内存。
if (m_info.in_attr){
free(m_info.in_attr);
}
//使用free函数释放m_info.out_attr所占用的内存。
if (m_info.out_attr){
free(m_info.out_attr);
}
return 0;
}