逐行解释官方仓库rknn-model-zoo中yolo系列rknn-c-demo主程序

官方仓库地址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;
}

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值