336 lines
10 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include "mobilenet.h"
#include "common.h"
#include "file_utils.h"
#include "image_utils.h"
static thread_local double runtime_preprocess_ms = 0.0;
static thread_local double runtime_inference_ms = 0.0;
static thread_local double runtime_postprocess_ms = 0.0;
extern "C" void yolo_adapter_set_thresholds(float, float) {}
extern "C" void yolo_adapter_get_performance(double *preprocess_ms, double *inference_ms, double *postprocess_ms)
{
if (preprocess_ms) *preprocess_ms = runtime_preprocess_ms;
if (inference_ms) *inference_ms = runtime_inference_ms;
if (postprocess_ms) *postprocess_ms = runtime_postprocess_ms;
}
static int64_t runtime_time_us()
{
struct timeval value;
gettimeofday(&value, nullptr);
return static_cast<int64_t>(value.tv_sec) * 1000000 + value.tv_usec;
}
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);
}
typedef struct {
float value;
int index;
} element_t;
static void swap(element_t* a, element_t* b) {
element_t temp = *a;
*a = *b;
*b = temp;
}
static int partition(element_t arr[], int low, int high) {
float pivot = arr[high].value;
int i = low - 1;
for (int j = low; j <= high - 1; j++) {
if (arr[j].value >= pivot) {
i++;
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[high]);
return (i + 1);
}
static void quick_sort(element_t arr[], int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quick_sort(arr, low, pi - 1);
quick_sort(arr, pi + 1, high);
}
}
static void softmax(float* array, int size) {
// Find the maximum value in the array
float max_val = array[0];
for (int i = 1; i < size; i++) {
if (array[i] > max_val) {
max_val = array[i];
}
}
// Subtract the maximum value from each element to avoid overflow
for (int i = 0; i < size; i++) {
array[i] -= max_val;
}
// Compute the exponentials and sum
float sum = 0.0;
for (int i = 0; i < size; i++) {
array[i] = expf(array[i]);
sum += array[i];
}
// Normalize the array by dividing each element by the sum
for (int i = 0; i < size; i++) {
array[i] /= sum;
}
}
static bool looks_like_probability_distribution(const float* array, int size)
{
if (array == NULL || size <= 0) return false;
float sum = 0.0f;
for (int i = 0; i < size; ++i) {
const float value = array[i];
if (!isfinite(value) || value < 0.0f || value > 1.0f) return false;
sum += value;
}
return fabsf(sum - 1.0f) < 0.01f;
}
static void normalize_classification_scores(float* array, int size)
{
// Keep the same scoring contract as Windows ONNX inference: preserve an
// existing probability distribution and apply softmax only to raw logits.
if (!looks_like_probability_distribution(array, size)) {
softmax(array, size);
}
}
static void get_topk_with_indices(float arr[], int size, int k, mobilenet_result* result) {
// Create an array of elements, saving values ​​and index numbers
element_t* elements = (element_t*)malloc(size * sizeof(element_t));
for (int i = 0; i < size; i++) {
elements[i].value = arr[i];
elements[i].index = i;
}
// Quick sort an array of elements
quick_sort(elements, 0, size - 1);
// Get the top K maximum values ​​and their index numbers
for (int i = 0; i < k; i++) {
result[i].score = elements[i].value;
result[i].cls = elements[i].index;
}
free(elements);
}
int init_mobilenet_model(const char* model_path, rknn_app_context_t* app_ctx)
{
int ret;
int model_len = 0;
char* model;
rknn_context ctx = 0;
// Load RKNN Model
model_len = read_data_from_file(model_path, &model);
if (model == NULL) {
printf("load_model fail!\n");
return -1;
}
ret = rknn_init(&ctx, model, model_len, 0, NULL);
free(model);
if (ret < 0) {
printf("rknn_init fail! ret=%d\n", ret);
return -1;
}
// Get Model Input Output Number
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;
}
printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output);
// Get Model Input Info
printf("input tensors:\n");
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]));
}
// Get Model Output Info
printf("output tensors:\n");
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]));
}
// Set to context
app_ctx->rknn_ctx = ctx;
app_ctx->io_num = io_num;
app_ctx->input_attrs = (rknn_tensor_attr*)malloc(io_num.n_input * sizeof(rknn_tensor_attr));
memcpy(app_ctx->input_attrs, input_attrs, io_num.n_input * sizeof(rknn_tensor_attr));
app_ctx->output_attrs = (rknn_tensor_attr*)malloc(io_num.n_output * sizeof(rknn_tensor_attr));
memcpy(app_ctx->output_attrs, output_attrs, io_num.n_output * sizeof(rknn_tensor_attr));
if (input_attrs[0].fmt == RKNN_TENSOR_NCHW) {
printf("model is NCHW input fmt\n");
app_ctx->model_channel = input_attrs[0].dims[1];
app_ctx->model_height = input_attrs[0].dims[2];
app_ctx->model_width = input_attrs[0].dims[3];
} else {
printf("model is NHWC input fmt\n");
app_ctx->model_height = input_attrs[0].dims[1];
app_ctx->model_width = input_attrs[0].dims[2];
app_ctx->model_channel = input_attrs[0].dims[3];
}
printf("model input height=%d, width=%d, channel=%d\n",
app_ctx->model_height, app_ctx->model_width, app_ctx->model_channel);
return 0;
}
int release_mobilenet_model(rknn_app_context_t* app_ctx)
{
if (app_ctx->input_attrs != NULL) {
free(app_ctx->input_attrs);
app_ctx->input_attrs = NULL;
}
if (app_ctx->output_attrs != NULL) {
free(app_ctx->output_attrs);
app_ctx->output_attrs = NULL;
}
if (app_ctx->rknn_ctx != 0) {
rknn_destroy(app_ctx->rknn_ctx);
app_ctx->rknn_ctx = 0;
}
return 0;
}
int inference_mobilenet_model(rknn_app_context_t* app_ctx, image_buffer_t* src_img, mobilenet_result* out_result, int topk)
{
int ret;
int class_count = 0;
int result_count = 0;
image_buffer_t img;
rknn_input inputs[1];
rknn_output outputs[1];
memset(&img, 0, sizeof(image_buffer_t));
memset(inputs, 0, sizeof(inputs));
memset(outputs, 0, sizeof(outputs));
for (int index = 0; index < topk; ++index) {
out_result[index].cls = -1;
out_result[index].score = 0.0f;
}
runtime_preprocess_ms = 0.0;
runtime_inference_ms = 0.0;
runtime_postprocess_ms = 0.0;
// Pre Process
int64_t started_us = runtime_time_us();
img.width = app_ctx->model_width;
img.height = app_ctx->model_height;
img.format = IMAGE_FORMAT_RGB888;
img.size = get_image_size(&img);
img.virt_addr = (unsigned char*)malloc(img.size);
if (img.virt_addr == NULL) {
printf("malloc buffer size:%d fail!\n", img.size);
return -1;
}
ret = convert_image(src_img, &img, NULL, NULL, 0);
if (ret < 0) {
printf("convert_image fail! ret=%d\n", ret);
return -1;
}
// Set Input Data
inputs[0].index = 0;
inputs[0].type = RKNN_TENSOR_UINT8;
inputs[0].fmt = RKNN_TENSOR_NHWC;
inputs[0].size = app_ctx->model_width * app_ctx->model_height * app_ctx->model_channel;
inputs[0].buf = img.virt_addr;
ret = rknn_inputs_set(app_ctx->rknn_ctx, 1, inputs);
if (ret < 0) {
printf("rknn_input_set fail! ret=%d\n", ret);
return -1;
}
runtime_preprocess_ms = (runtime_time_us() - started_us) / 1000.0;
// Run
printf("rknn_run\n");
started_us = runtime_time_us();
ret = rknn_run(app_ctx->rknn_ctx, nullptr);
runtime_inference_ms = (runtime_time_us() - started_us) / 1000.0;
printf("rknn_run time=%.2fms, FPS = %.2f\n", runtime_inference_ms,
runtime_inference_ms > 0 ? 1000.0 / runtime_inference_ms : 0.0);
if (ret < 0) {
printf("rknn_run fail! ret=%d\n", ret);
return -1;
}
// Get Output
outputs[0].want_float = 1;
ret = rknn_outputs_get(app_ctx->rknn_ctx, 1, outputs, NULL);
if (ret < 0) {
printf("rknn_outputs_get fail! ret=%d\n", ret);
goto out;
}
// Post Process
started_us = runtime_time_us();
class_count = app_ctx->output_attrs[0].n_elems;
normalize_classification_scores((float*)outputs[0].buf, class_count);
result_count = topk < class_count ? topk : class_count;
get_topk_with_indices((float*)outputs[0].buf, class_count, result_count, out_result);
runtime_postprocess_ms = (runtime_time_us() - started_us) / 1000.0;
// Remeber to release rknn output
rknn_outputs_release(app_ctx->rknn_ctx, 1, outputs);
out:
if (img.virt_addr != NULL) {
free(img.virt_addr);
}
return ret;
}