336 lines
10 KiB
C++
336 lines
10 KiB
C++
#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;
|
||
}
|