* [C API 2.0]Redifine partial shape and property wrapper
1. Use dimension object to initialize partial_shape rather than string
2. Use void* to unify property value rather than union
3. rename some C api name to strict align with C++ method.
Change-Id: I64b5c521461264dba2d23543808584632fbd6d4b
* [C API 2.0]Memory check and implement all reshape interface
1. Memory safe create and free
2. Implement all reshape interface align with C++ interface
3. rename some api to align with C++ interface
Change-Id: Ib5e4192bdbd8a11cdd7e30b1dc84881ba3f2d505
* Rename prepostprocess to strict align with C++ name
Change-Id: I7a4d0a6e835b2d6ed01cd218ac81b1b621f600bf
* [C API 2.0]redefine ov_node and ov_model interface
1. redefine ov_node and ov_model interface
2. rename some api to aligne with C++ interface
3. remove some redundant code
4. align CMakeLists.txt with OpenVINO 2.0 convention
Change-Id: I4d5e92157e7891319c9754da8e70b9c6150ae2e3
* Redefine ov_layout to support more than one char
Change-Id: I39e5389246cf3edcc2f4734d13157457773d89b8
* Add interface to get partial_shape from node
Change-Id: I8cef77db581b43d2f0a9ac48cfdc09a86e39b694
* Use unique_ptr prevent memory leaks in case of exception
Change-Id: I150b375108a3eded400bdde087ab5c858958c25f
* Put legacy C API and 2.0 C API into a library
Change-Id: I067a55a00e78b80cdede5ae7adad316ee98cabd1
* Only keep OV 2.0 C sample and move legacy C sample to legacy directory
1. Move legacy C samples to tools/legacy/c directory
2. Keep OV 2.0 C samples in samples/c directory
Change-Id: I05880d17ee7cb7eafc6853ebb5394f3969258592
* Fix format and log issues
Change-Id: I05d909b3d7046d41b807e35808a993bb09672e68
* Restore documents update
Change-Id: I82dd92081c0aa1a2d7dca7f114cf6a35131d6f92
* Change config map data be const
Change-Id: I9043859e8308c01d80794dc8280ae236947f3bbb
* Update api document
Change-Id: I35bc149bad0de17424d95f48c3027030b708e147
* Add clang enable
Change-Id: I335639c05fb5fb38e682dbb72bfaf78380c0adaf
* Fix clang issue after enable clang for ie_c_api.c
Change-Id: Idcb4dda9d66e47a169eb79a9c4fe7a7d4df838db
* split header file and c file into multiple files
Change-Id: I7c3398966809ef70d7fcb799f2d612a33b471e31
* Fix clang format issue
Change-Id: Ibd18b45537c8f3bcbb5b995c90ae28999161d54d
* Add single ov_dimension_create method
Change-Id: Icd06b50e4f4df8f7897c7c4327edb67178162544
* Remove all legacy c samples completely
Change-Id: I098360a0d9002340e8769074181f7997b43bce8f
* Update ov_property_value to replace only ptr
Change-Id: I9f5a11b4cf07e759c1998e78e2624f0a1266d9b0
* Split more header files, add static dimension api
Change-Id: I14e4fb8585fc629480c06b86bd8219e75a9682f7
* Change ov_dimensions_create to be ov_dimensions_create_dynamic
Change-Id: I50c02749cea96f12bcea702b53a89c65b289550e
* rename status and get_out_tensor
Change-Id: I762c1d0c5a069454506fe3c04283c63ddbfacf31
* Split ov_c_api_test.cpp
* Split OV2.0 CAPI tests
* move var into Setup
* Merge legacy and 2.0 C API test
* Merge InferenceEngineCAPITests into openvino_capi_test
1. put InferenceEngineCAPITests into openvino_capi_test
2. resolve some format issues
Change-Id: I47bbba6bd70a871ee063becbd80eb57919fa9fb0
* legacy api test skips clang format
Change-Id: Id54ecdba827cf98c99b92295c0a0772123098b63
* Fix clang format issue
Change-Id: I7ed510d8178971fe04a895e812c261db99d8b9f2
* Restore InferenceEngineCAPITests
Change-Id: I4d641ffb1de9ce4d20ebecf35fc036fa7bd73e55
* rename openvino_capi_test to ov_capi_test
Change-Id: I6b6fe0cdb89aab7210abb17f32dbfdcdce72ba25
* unify list size name and refine ov_core_version_t
Change-Id: I137fc6d990c7b07f597ee94fa3b98d07ae843cb6
* align header file path to be openvino/c/openvino.h
Change-Id: I1a4552e1d558098af704942fe45488b0d6d53a90
* Fix path issue
Change-Id: I84d425d25e3b08c1516cbcc842fb9cb75574bf17
* move ov_color_format and remove opencv depenency
Change-Id: I486145f9e92e8bbf2e937d3572334aa9f0e68841
* Resolve some memory allocation error handling issues and read model with empty weight issue
Change-Id: Icd8e3b6de9741147993fa215a0c7cfd7debd5500
* Add GPU test cases
Change-Id: I13324ef019b5b1af79259ca932a36a0cec792c27
* Fix clang issue
Change-Id: I9bb4c47de301d142b5e2a77a39f667689ad9fe38
* Resolve CI test failure
Change-Id: Ia327d5edab19d8dd44ac369670f190d5c57aca79
* Redefine ov_shape and add default ov_core_create
Change-Id: I3e47d607f8aad65cb99cdddacaecf7bf34b1361b
* Remove some unnecessary api of node
Remove the unnecessary node api:
ov_node_get_any_name(ov_output_const_node_t* node, char** tensor_name)
ov_node_get_element_type(ov_output_const_node_t* node, ov_element_type_e* tensor_type)
Change-Id: I80a3243676800263a9e56afa3cfffce7b4bd2ae7
* Rename reshape api
ov_model_reshape should be common case which allow to reshape any models with different number of input.
Change-Id: I26bafeeb8a3dda7cd5164cda15fdb338db8668cb
* Rename ov_node api
Change-Id: I03114ecb6de5c46b6d02c909b6f6fb6c8bfd5cba
* Remove subfolder out of source code
Change-Id: Ib033ae7712cc0460d6fc21a0f89818381ae503c0
* apply absolute path for all header files
Change-Id: I8024c897d424b407025e21460ed4b62829b853d2
* Fix CI issue ov_capi_test failed to find libgna
Change-Id: I166e79a818498c6721fe956f43873f36d9ae1e07
* Resolve build issue to align with PR12214
Change-Id: I9e6094db213b431ee1b46e0d64199131db33bb36
Co-authored-by: ruiqi <ruiqi.yang@intel.com>
364 lines
13 KiB
C
364 lines
13 KiB
C
// Copyright (C) 2018-2022 Intel Corporation
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "openvino/c/openvino.h"
|
|
|
|
/**
|
|
* @brief Struct to store infer results
|
|
*/
|
|
struct infer_result {
|
|
size_t class_id;
|
|
float probability;
|
|
};
|
|
|
|
/**
|
|
* @brief Sort result by probability
|
|
* @param struct with infer results to sort
|
|
* @param result_size of the struct
|
|
* @return none
|
|
*/
|
|
int compare(const void* a, const void* b) {
|
|
const struct infer_result* sa = (const struct infer_result*)a;
|
|
const struct infer_result* sb = (const struct infer_result*)b;
|
|
if (sa->probability < sb->probability) {
|
|
return 1;
|
|
} else if ((sa->probability == sb->probability) && (sa->class_id > sb->class_id)) {
|
|
return 1;
|
|
} else if (sa->probability > sb->probability) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
void infer_result_sort(struct infer_result* results, size_t result_size) {
|
|
qsort(results, result_size, sizeof(struct infer_result), compare);
|
|
}
|
|
|
|
/**
|
|
* @brief Convert output tensor to infer result struct for processing results
|
|
* @param tensor of output tensor
|
|
* @param result_size of the infer result
|
|
* @return struct infer_result
|
|
*/
|
|
struct infer_result* tensor_to_infer_result(ov_tensor_t* tensor, size_t* result_size) {
|
|
ov_status_e status = ov_tensor_get_size(tensor, result_size);
|
|
if (status != OK)
|
|
return NULL;
|
|
|
|
struct infer_result* results = (struct infer_result*)malloc(sizeof(struct infer_result) * (*result_size));
|
|
if (!results)
|
|
return NULL;
|
|
|
|
void* data = NULL;
|
|
status = ov_tensor_data(tensor, &data);
|
|
if (status != OK) {
|
|
free(results);
|
|
return NULL;
|
|
}
|
|
|
|
float* float_data = (float*)(data);
|
|
for (size_t i = 0; i < *result_size; ++i) {
|
|
results[i].class_id = i;
|
|
results[i].probability = float_data[i];
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* @brief Print results of infer
|
|
* @param results of the infer results
|
|
* @param result_size of the struct of classification results
|
|
* @param img_path image path
|
|
* @return none
|
|
*/
|
|
void print_infer_result(struct infer_result* results, size_t result_size, const char* img_path) {
|
|
printf("\nImage %s\n", img_path);
|
|
printf("\nclassid probability\n");
|
|
printf("------- -----------\n");
|
|
for (size_t i = 0; i < result_size; ++i) {
|
|
printf("%zu %f\n", results[i].class_id, results[i].probability);
|
|
}
|
|
}
|
|
|
|
void print_model_input_output_info(ov_model_t* model) {
|
|
char* friendly_name = NULL;
|
|
ov_model_get_friendly_name(model, &friendly_name);
|
|
printf("[INFO] model name: %s \n", friendly_name);
|
|
ov_free(friendly_name);
|
|
}
|
|
|
|
/**
|
|
* @brief Check image has supported width and height
|
|
* @param string image size in WIDTHxHEIGHT format
|
|
* @param pointer to image width
|
|
* @param pointer to image height
|
|
* @return bool status True(success) or False(fail)
|
|
*/
|
|
|
|
bool is_supported_image_size(const char* size_str, size_t* width, size_t* height) {
|
|
const char* _size = size_str;
|
|
size_t _width = 0, _height = 0;
|
|
while (_size && *_size != 'x' && *_size != '\0') {
|
|
if ((*_size <= '9') && (*_size >= '0')) {
|
|
_width = (_width * 10) + (*_size - '0');
|
|
_size++;
|
|
} else {
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
if (_size)
|
|
_size++;
|
|
|
|
while (_size && *_size != '\0') {
|
|
if ((*_size <= '9') && (*_size >= '0')) {
|
|
_height = (_height * 10) + (*_size - '0');
|
|
_size++;
|
|
} else {
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
if (_width > 0 && _height > 0) {
|
|
if (_width % 2 == 0 && _height % 2 == 0) {
|
|
*width = _width;
|
|
*height = _height;
|
|
return true;
|
|
} else {
|
|
printf("Unsupported image size, width and height must be even numbers \n");
|
|
return false;
|
|
}
|
|
} else {
|
|
goto err;
|
|
}
|
|
err:
|
|
printf("Incorrect format of image size parameter, expected WIDTHxHEIGHT, "
|
|
"actual: %s\n",
|
|
size_str);
|
|
return false;
|
|
}
|
|
|
|
size_t read_image_from_file(const char* img_path, unsigned char* img_data, size_t size) {
|
|
FILE* fp = fopen(img_path, "rb");
|
|
size_t read_size = 0;
|
|
|
|
if (fp) {
|
|
fseek(fp, 0, SEEK_END);
|
|
if (ftell(fp) >= size) {
|
|
fseek(fp, 0, SEEK_SET);
|
|
read_size = fread(img_data, 1, size, fp);
|
|
}
|
|
fclose(fp);
|
|
}
|
|
|
|
return read_size;
|
|
}
|
|
|
|
#define CHECK_STATUS(return_status) \
|
|
if (return_status != OK) { \
|
|
fprintf(stderr, "[ERROR] return status %d, line %d\n", return_status, __LINE__); \
|
|
goto err; \
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
// -------- Check input parameters --------
|
|
if (argc != 5) {
|
|
printf("Usage : ./hello_nv12_input_classification_c <path_to_model> <path_to_image> "
|
|
"<WIDTHxHEIGHT> <device_name>\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
size_t input_width = 0, input_height = 0, img_size = 0;
|
|
if (!is_supported_image_size(argv[3], &input_width, &input_height)) {
|
|
fprintf(stderr, "ERROR is_supported_image_size, line %d\n", __LINE__);
|
|
return EXIT_FAILURE;
|
|
}
|
|
unsigned char* img_data = NULL;
|
|
ov_core_t* core = NULL;
|
|
ov_model_t* model = NULL;
|
|
ov_tensor_t* tensor = NULL;
|
|
ov_preprocess_prepostprocessor_t* preprocess = NULL;
|
|
ov_preprocess_inputinfo_t* input_info = NULL;
|
|
ov_model_t* new_model = NULL;
|
|
ov_preprocess_inputtensorinfo_t* input_tensor_info = NULL;
|
|
ov_preprocess_preprocesssteps_t* input_process = NULL;
|
|
ov_preprocess_inputmodelinfo_t* p_input_model = NULL;
|
|
ov_compiled_model_t* compiled_model = NULL;
|
|
ov_infer_request_t* infer_request = NULL;
|
|
ov_tensor_t* output_tensor = NULL;
|
|
struct infer_result* results = NULL;
|
|
char* input_tensor_name = NULL;
|
|
char* output_tensor_name = NULL;
|
|
ov_output_node_list_t input_nodes = {.size = 0, .output_nodes = NULL};
|
|
ov_output_node_list_t output_nodes = {.size = 0, .output_nodes = NULL};
|
|
ov_layout_t* model_layout = NULL;
|
|
ov_shape_t input_shape;
|
|
|
|
// -------- Get OpenVINO runtime version --------
|
|
ov_version_t version = {.description = NULL, .buildNumber = NULL};
|
|
CHECK_STATUS(ov_get_openvino_version(&version));
|
|
printf("---- OpenVINO INFO----\n");
|
|
printf("description : %s \n", version.description);
|
|
printf("build number: %s \n", version.buildNumber);
|
|
ov_version_free(&version);
|
|
|
|
// -------- Parsing and validation of input arguments --------
|
|
const char* input_model = argv[1];
|
|
const char* input_image_path = argv[2];
|
|
const char* device_name = argv[4];
|
|
|
|
// -------- Step 1. Initialize OpenVINO Runtime Core --------
|
|
CHECK_STATUS(ov_core_create(&core));
|
|
|
|
// -------- Step 2. Read a model --------
|
|
printf("[INFO] Loading model files: %s\n", input_model);
|
|
CHECK_STATUS(ov_core_read_model(core, input_model, NULL, &model));
|
|
print_model_input_output_info(model);
|
|
|
|
CHECK_STATUS(ov_model_outputs(model, &output_nodes));
|
|
if (output_nodes.size != 1) {
|
|
fprintf(stderr, "[ERROR] Sample supports models with 1 output only %d\n", __LINE__);
|
|
goto err;
|
|
}
|
|
|
|
CHECK_STATUS(ov_model_inputs(model, &input_nodes));
|
|
if (input_nodes.size != 1) {
|
|
fprintf(stderr, "[ERROR] Sample supports models with 1 input only %d\n", __LINE__);
|
|
goto err;
|
|
}
|
|
|
|
CHECK_STATUS(ov_node_list_get_any_name_by_index(&input_nodes, 0, &input_tensor_name));
|
|
CHECK_STATUS(ov_node_list_get_any_name_by_index(&output_nodes, 0, &output_tensor_name));
|
|
|
|
// -------- Step 3. Configure preprocessing --------
|
|
CHECK_STATUS(ov_preprocess_prepostprocessor_create(model, &preprocess));
|
|
|
|
// 1) Select input with 'input_tensor_name' tensor name
|
|
CHECK_STATUS(ov_preprocess_prepostprocessor_input_by_name(preprocess, input_tensor_name, &input_info));
|
|
|
|
// 2) Set input type
|
|
// - as 'u8' precision
|
|
// - set color format to NV12 (single plane)
|
|
// - static spatial dimensions for resize preprocessing operation
|
|
CHECK_STATUS(ov_preprocess_inputinfo_tensor(input_info, &input_tensor_info));
|
|
CHECK_STATUS(ov_preprocess_inputtensorinfo_set_element_type(input_tensor_info, U8));
|
|
CHECK_STATUS(ov_preprocess_inputtensorinfo_set_color_format(input_tensor_info, NV12_SINGLE_PLANE));
|
|
CHECK_STATUS(ov_preprocess_inputtensorinfo_set_spatial_static_shape(input_tensor_info, input_height, input_width));
|
|
|
|
// 3) Pre-processing steps:
|
|
// a) Convert to 'float'. This is to have color conversion more accurate
|
|
// b) Convert to BGR: Assumes that model accepts images in BGR format. For RGB, change it manually
|
|
// c) Resize image from tensor's dimensions to model ones
|
|
CHECK_STATUS(ov_preprocess_inputinfo_preprocess(input_info, &input_process));
|
|
CHECK_STATUS(ov_preprocess_preprocesssteps_convert_element_type(input_process, F32));
|
|
CHECK_STATUS(ov_preprocess_preprocesssteps_convert_color(input_process, BGR));
|
|
CHECK_STATUS(ov_preprocess_preprocesssteps_resize(input_process, RESIZE_LINEAR));
|
|
|
|
// 4) Set model data layout (Assuming model accepts images in NCHW layout)
|
|
CHECK_STATUS(ov_preprocess_inputinfo_model(input_info, &p_input_model));
|
|
|
|
const char* model_layout_desc = "NCHW";
|
|
CHECK_STATUS(ov_layout_create(&model_layout, model_layout_desc));
|
|
CHECK_STATUS(ov_preprocess_inputmodelinfo_set_layout(p_input_model, model_layout));
|
|
|
|
// 5) Apply preprocessing to an input with 'input_tensor_name' name of loaded model
|
|
CHECK_STATUS(ov_preprocess_prepostprocessor_build(preprocess, &new_model));
|
|
|
|
// -------- Step 4. Loading a model to the device --------
|
|
CHECK_STATUS(ov_core_compile_model(core, new_model, device_name, &compiled_model, NULL));
|
|
|
|
// -------- Step 5. Create an infer request --------
|
|
CHECK_STATUS(ov_compiled_model_create_infer_request(compiled_model, &infer_request));
|
|
|
|
// -------- Step 6. Prepare input data --------
|
|
img_size = input_width * (input_height * 3 / 2);
|
|
if (!img_size) {
|
|
fprintf(stderr, "[ERROR] Invalid Image size, line %d\n", __LINE__);
|
|
goto err;
|
|
}
|
|
img_data = (unsigned char*)calloc(img_size, sizeof(unsigned char));
|
|
if (!img_data) {
|
|
fprintf(stderr, "[ERROR] calloc returned NULL, line %d\n", __LINE__);
|
|
goto err;
|
|
}
|
|
if (img_size != read_image_from_file(input_image_path, img_data, img_size)) {
|
|
fprintf(stderr, "[ERROR] Image dimensions not match with NV12 file size, line %d\n", __LINE__);
|
|
goto err;
|
|
}
|
|
ov_element_type_e input_type = U8;
|
|
size_t batch = 1;
|
|
ov_shape_init(&input_shape, 4);
|
|
input_shape.dims[0] = batch;
|
|
input_shape.dims[1] = input_height * 3 / 2;
|
|
input_shape.dims[2] = input_width;
|
|
input_shape.dims[3] = 1;
|
|
CHECK_STATUS(ov_tensor_create_from_host_ptr(input_type, input_shape, img_data, &tensor));
|
|
|
|
// -------- Step 6. Set input tensor --------
|
|
// Set the input tensor by tensor name to the InferRequest
|
|
CHECK_STATUS(ov_infer_request_set_tensor(infer_request, input_tensor_name, tensor));
|
|
|
|
// -------- Step 7. Do inference --------
|
|
// Running the request synchronously
|
|
CHECK_STATUS(ov_infer_request_infer(infer_request));
|
|
|
|
// -------- Step 8. Process output --------
|
|
CHECK_STATUS(ov_infer_request_get_output_tensor(infer_request, 0, &output_tensor));
|
|
// Print classification results
|
|
size_t results_num = 0;
|
|
results = tensor_to_infer_result(output_tensor, &results_num);
|
|
if (!results) {
|
|
goto err;
|
|
}
|
|
infer_result_sort(results, results_num);
|
|
size_t top = 10;
|
|
if (top > results_num) {
|
|
top = results_num;
|
|
}
|
|
printf("\nTop %zu results:\n", top);
|
|
print_infer_result(results, top, input_image_path);
|
|
|
|
// -------- free allocated resources --------
|
|
err:
|
|
free(results);
|
|
free(img_data);
|
|
ov_shape_deinit(&input_shape);
|
|
ov_free(input_tensor_name);
|
|
ov_free(output_tensor_name);
|
|
ov_output_node_list_free(&output_nodes);
|
|
ov_output_node_list_free(&input_nodes);
|
|
if (output_tensor)
|
|
ov_tensor_free(output_tensor);
|
|
if (infer_request)
|
|
ov_infer_request_free(infer_request);
|
|
if (compiled_model)
|
|
ov_compiled_model_free(compiled_model);
|
|
if (p_input_model)
|
|
ov_preprocess_inputmodelinfo_free(p_input_model);
|
|
if (input_process)
|
|
ov_preprocess_preprocesssteps_free(input_process);
|
|
if (model_layout)
|
|
ov_layout_free(model_layout);
|
|
if (input_tensor_info)
|
|
ov_preprocess_inputtensorinfo_free(input_tensor_info);
|
|
if (input_info)
|
|
ov_preprocess_inputinfo_free(input_info);
|
|
if (preprocess)
|
|
ov_preprocess_prepostprocessor_free(preprocess);
|
|
if (new_model)
|
|
ov_model_free(new_model);
|
|
if (tensor)
|
|
ov_tensor_free(tensor);
|
|
if (model)
|
|
ov_model_free(model);
|
|
if (core)
|
|
ov_core_free(core);
|
|
return EXIT_SUCCESS;
|
|
}
|