目录
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;
}