adaface人脸特征提取之rknn推理

1. 前言

本文以Android端为例

2. rknn开发环境搭建

https://blog.youkuaiyun.com/wyw0000/article/details/145046442

3. 编译Android库

3.1 配置交叉编译环境

GCC_COMPILER=/rknn_model_zoo/arm-rockchip830-linux-uclibcgnueabihf

3.2 配置NDK

rknn官方特定NDK r19

ANDROID_NDK_PATH=/rknn_model_zoo/android-ndk-r19c

3.3 编译

./build-android.sh

3.4 获得librknn.so

上面是在编译librknn.so,然并卵是在编译examples中的代码,那librknn.so在哪呢?在/root/wyw/rknn_model_zoo-v2.3.0-2024-11-08/3rdparty/rknpu2/Android直接拿来用即可。

4. 搭建Android工程

这里不详述

5. 写adaface推理代码

话不多说上代码
AdaFaceRknn.h

//
// Created by wangy on 2025/1/7.
//

#ifndef ADAFACE_JNI_ADAFACERKNN_H
#define ADAFACE_JNI_ADAFACERKNN_H

#include <string>
#include <vector>
#include "opencv2/opencv.hpp"

#include "rknn_api.h"

class AdaFaceRknn {
public:
    AdaFaceRknn();
    ~AdaFaceRknn();
    int Init(const unsigned char *model, int model_len);
    std::vector<float> Infer(const cv::Mat& img);

private:
    unsigned char *load_model(const char *filename, int *model_size);

private:
    rknn_context _ctx;
    int _model_input_width;
    int _model_input_height;
};


#endif //ADAFACE_JNI_ADAFACERKNN_H

AdaFaceRknn.cpp

//
// Created by wangy on 2025/1/7.
//

#include "AdaFaceRknn.h"
#include <android/log.h>

AdaFaceRknn::AdaFaceRknn() {

}

AdaFaceRknn::~AdaFaceRknn() {

}

unsigned char *AdaFaceRknn::load_model(const char *filename, int *model_size)
{
    FILE *fp = fopen(filename, "rb");
    if (fp == nullptr)
    {
       __android_log_print(ANDROID_LOG_ERROR, "ncnn", "open model file %s failed", 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))
    {
        __android_log_print(ANDROID_LOG_ERROR, "ncnn", "fread model file %s failed", filename);
        free(model);
        return NULL;
    }
    *model_size = model_len;
    if (fp)
    {
        fclose(fp);
    }

    return model;
}

int AdaFaceRknn::Init(const unsigned char *model, int model_len) {
    int ret = rknn_init(&_ctx, (unsigned char*)model, model_len, 0, NULL);
    if (ret < 0)
    {
        __android_log_print(ANDROID_LOG_ERROR, "ncnn", "rknn_init fail! ret=%d\n", ret);
        return -1;
    }

    return 0;
}

std::vector<float> AdaFaceRknn::Infer(const cv::Mat& img)
{
    std::vector<float> result;
    rknn_input_output_num io_num;
    int ret = rknn_query(_ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));                    // 使用rknn_query函数获取模型输入输出数量
    if (ret != RKNN_SUCC)
    {
        __android_log_print(ANDROID_LOG_ERROR, "ncnn", "rknn_query fail! ret=%d\n", ret);
        return result;
    }
    __android_log_print(ANDROID_LOG_ERROR, "ncnn","model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output);         // 打印模型输入输出数量

    // ********** 输入输出属性 **********
    rknn_tensor_attr input_attrs[io_num.n_input];        // 使用rknn_tensor_attr结构体存储模型输入属性
    memset(input_attrs, 0, sizeof(input_attrs));         // 将input_attrs用0初始化

    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));    // 使用rknn_query函数获取模型输入信息,存储在input_attrs
        if (ret != RKNN_SUCC)
        {
            __android_log_print(ANDROID_LOG_ERROR, "ncnn", "rknn_query fail! ret=%d\n", ret);
            return result;
        }
        _model_input_width = input_attrs[i].dims[1];    // 获取模型输入的具体宽
        _model_input_height = input_attrs[i].dims[2];   // 获取模型输入的具体高
        // 打印模型输入信息
        __android_log_print(ANDROID_LOG_INFO, "ncnn","input tensors:  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",
               input_attrs[i].index, input_attrs[i].name, input_attrs[i].n_dims, input_attrs[i].dims[0], input_attrs[i].dims[1],
               input_attrs[i].dims[2], input_attrs[i].dims[3],input_attrs[i].n_elems, input_attrs[i].size, get_format_string(input_attrs[i].fmt),
               get_type_string(input_attrs[i].type),get_qnt_type_string(input_attrs[i].qnt_type), input_attrs[i].zp, input_attrs[i].scale);
    }

    rknn_tensor_attr output_attrs[io_num.n_output];       // 使用rknn_tensor_attr结构体存储模型输出信息
    memset(output_attrs, 0, sizeof(output_attrs));        // 将output_attrs用0初始化

    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)
        {
            __android_log_print(ANDROID_LOG_ERROR, "ncnn", "rknn_query fail! ret=%d\n", ret);
            return result;
        }
        // 打印模型输出信息
        __android_log_print(ANDROID_LOG_INFO, "ncnn","output tensors: 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",
               output_attrs[i].index, output_attrs[i].name, output_attrs[i].n_dims, output_attrs[i].dims[0], output_attrs[i].dims[1],
               output_attrs[i].dims[2], output_attrs[i].dims[3],output_attrs[i].n_elems, output_attrs[i].size, get_format_string(output_attrs[i].fmt),
               get_type_string(output_attrs[i].type),get_qnt_type_string(output_attrs[i].qnt_type), output_attrs[i].zp, output_attrs[i].scale);
    }

    // ======================= 前处理 ===================
    cv::Mat orig_img_rgb;
    cv::cvtColor(img, orig_img_rgb, cv::COLOR_BGR2RGB);      // 默认是BGR需要转化成RGB

    cv::Mat resize_img;
    if (img.cols != _model_input_width || img.rows != _model_input_height)
    {
        cv::resize(orig_img_rgb, resize_img, cv::Size(_model_input_width, _model_input_height), 0, 0, cv::INTER_LINEAR);           // 对图像尺寸进行缩放
    }

    resize_img.convertTo(resize_img, CV_32F);
    // 对图像进行标准化处理
    resize_img = resize_img / 255.0;	// 归一化
    resize_img = (resize_img - 0.5) / 0.5;

    // ======================= 设置模型输入 ===================
    rknn_input inputs[io_num.n_input];     // 使用rknn_input结构体存储模型输入信息
    memset(inputs, 0, sizeof(inputs));     // 将inputs用0初始化
    for (int i = 0; i < io_num.n_input; i++)
    {
        inputs[i].index = input_attrs[i].index;     // 设置模型输入索引
        inputs[i].type = RKNN_TENSOR_UINT8;         // 设置模型输入类型
        inputs[i].size = input_attrs[i].dims[1] * input_attrs[i].dims[2] * input_attrs[i].dims[3] * sizeof(uint8_t);  // 设置模型输入大小
        inputs[i].fmt = input_attrs[i].fmt;         // 设置模型输入格式:NHWC
        inputs[i].buf = resize_img.data;                   // 设置模型输入数据
    }

    ret = rknn_inputs_set(_ctx, io_num.n_input, inputs);     // 使用rknn_inputs_set函数设置模型输入
    if (ret < 0)
    {
        __android_log_print(ANDROID_LOG_ERROR, "ncnn", "rknn_input_set fail! ret=%d\n", ret);
        return result;
    }

    // ======================= 推理 ===================
    ret = rknn_run(_ctx, nullptr);         // 使用rknn_run函数运行RKNN模型
    if (ret < 0)
    {
        __android_log_print(ANDROID_LOG_ERROR, "ncnn", "rknn_run fail! ret=%d\n", ret);
        return result;
    }

    // ======================= 获取模型输出 ===================
    rknn_output outputs[io_num.n_output];     // 使用rknn_output结构体存储模型输出信息
    memset(outputs, 0, sizeof(outputs));      // 将outputs用0初始化
    for (int i = 0; i < io_num.n_output; i++)
    {
        outputs[i].want_float = 1;                // 设置模型输出类型为float
    }

    ret = rknn_outputs_get(_ctx, io_num.n_output, outputs, NULL);
    if (ret < 0)
    {
        __android_log_print(ANDROID_LOG_ERROR, "ncnn", "rknn_outputs_get fail! ret=%d\n", ret);
        return result;
    }

    result.assign((float*)outputs[0].buf, (float*)outputs[0].buf + output_attrs[0].size);

    return result;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值