#include #include #include #include #include #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(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; }