├── CMakeLists.txt ├── README.md ├── best_yolox_nano.onnx ├── demo.jpg └── main.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.13) 3 | project(onnx_c++) 4 | 5 | 6 | find_package(OpenCV REQUIRED) 7 | find_path(ONNX_RUNTIME_SESSION_INCLUDE_DIRS onnxruntime_cxx_api.h HINTS /usr/local/include/onnxruntime/core/session/) 8 | 9 | find_library(ONNX_RUNTIME_LIB onnxruntime HINTS /usr/local/lib) 10 | 11 | add_executable(onnx_c++ main.cpp) 12 | target_include_directories(onnx_c++ PRIVATE ${ONNX_RUNTIME_SESSION_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS}) 13 | target_link_libraries(onnx_c++ PRIVATE ${ONNX_RUNTIME_LIB} ${OpenCV_LIBRARIES}) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # onnx_c++ 2 | 3 | This repo is contains code used alongside with our object detection course. It permits practitioners use an onnx model(yolox) in c++ 4 | -------------------------------------------------------------------------------- /best_yolox_nano.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neuralearn/onnx_cplusplus/e07d0b0fda53fabd5d0126a2e5c1d955cfbb4d11/best_yolox_nano.onnx -------------------------------------------------------------------------------- /demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neuralearn/onnx_cplusplus/e07d0b0fda53fabd5d0126a2e5c1d955cfbb4d11/demo.jpg -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | // https://github.com/microsoft/onnxruntime/blob/v1.8.2/csharp/test/Microsoft.ML.OnnxRuntime.EndToEndTests.Capi/CXX_Api_Sample.cpp 2 | // https://github.com/microsoft/onnxruntime/blob/v1.8.2/include/onnxruntime/core/session/onnxruntime_cxx_api.h 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | /** 35 | * @brief Define names based depends on Unicode path support 36 | */ 37 | #define tcout std::cout 38 | #define file_name_t std::string 39 | #define imread_t cv::imread 40 | #define NMS_THRESH 0.5 41 | #define BBOX_CONF_THRESH 0.3 42 | 43 | static const int INPUT_W = 416; 44 | static const int INPUT_H = 416; 45 | static const int NUM_CLASSES = 80; // COCO has 80 classes. Modify this value on your own dataset. 46 | 47 | template 48 | T vectorProduct(const std::vector& v) 49 | { 50 | return accumulate(v.begin(), v.end(), 1, std::multiplies()); 51 | } 52 | 53 | /** 54 | * @brief Operator overloading for printing vectors 55 | * @tparam T 56 | * @param os 57 | * @param v 58 | * @return std::ostream& 59 | */ 60 | template 61 | std::ostream& operator<<(std::ostream& os, const std::vector& v) 62 | { 63 | os << "["; 64 | for (int i = 0; i < v.size(); ++i) 65 | { 66 | os << v[i]; 67 | if (i != v.size() - 1) 68 | { 69 | os << ", "; 70 | } 71 | } 72 | os << "]"; 73 | return os; 74 | } 75 | 76 | 77 | cv::Mat static_resize(cv::Mat& img) { 78 | float r = std::min(INPUT_W / (img.cols*1.0), INPUT_H / (img.rows*1.0)); 79 | // r = std::min(r, 1.0f); 80 | int unpad_w = r * img.cols; 81 | int unpad_h = r * img.rows; 82 | cv::Mat re(unpad_h, unpad_w, CV_8UC3); 83 | cv::resize(img, re, re.size()); 84 | //cv::Mat out(INPUT_W, INPUT_H, CV_8UC3, cv::Scalar(114, 114, 114)); 85 | cv::Mat out(INPUT_H, INPUT_W, CV_8UC3, cv::Scalar(114, 114, 114)); 86 | re.copyTo(out(cv::Rect(0, 0, re.cols, re.rows))); 87 | return out; 88 | } 89 | 90 | struct Object 91 | { 92 | cv::Rect_ rect; 93 | int label; 94 | float prob; 95 | }; 96 | 97 | struct GridAndStride 98 | { 99 | int grid0; 100 | int grid1; 101 | int stride; 102 | }; 103 | 104 | static void generate_grids_and_stride(const int target_w, const int target_h, std::vector& strides, std::vector& grid_strides) 105 | { 106 | for (auto stride : strides) 107 | { 108 | int num_grid_w = target_w / stride; 109 | int num_grid_h = target_h / stride; 110 | for (int g1 = 0; g1 < num_grid_h; g1++) 111 | { 112 | for (int g0 = 0; g0 < num_grid_w; g0++) 113 | { 114 | grid_strides.push_back((GridAndStride){g0, g1, stride}); 115 | } 116 | } 117 | } 118 | } 119 | 120 | 121 | static void generate_yolox_proposals(std::vector grid_strides, const float* feat_ptr, float prob_threshold, std::vector& objects) 122 | { 123 | 124 | const int num_anchors = grid_strides.size(); std::cout <<"size grid_strides: " << grid_strides.size()<< std::endl; 125 | 126 | for (int anchor_idx = 0; anchor_idx < num_anchors; anchor_idx++) 127 | { 128 | const int grid0 = grid_strides[anchor_idx].grid0; 129 | const int grid1 = grid_strides[anchor_idx].grid1; 130 | const int stride = grid_strides[anchor_idx].stride; 131 | 132 | /*std::cout << "grid 0: "<< grid0<< std::endl; 133 | std::cout << "grid 1: "<< grid1<< std::endl; 134 | std::cout << "stride: "<< stride<< std::endl;*/ 135 | 136 | const int basic_pos = anchor_idx * (NUM_CLASSES + 5); 137 | 138 | 139 | float x_center = (feat_ptr[basic_pos + 0] + grid0) * stride; 140 | float y_center = (feat_ptr[basic_pos + 1] + grid1) * stride; 141 | float w = exp(feat_ptr[basic_pos + 2]) * stride; 142 | float h = exp(feat_ptr[basic_pos + 3]) * stride; 143 | 144 | 145 | 146 | float x0 = x_center - w * 0.5f; 147 | float y0 = y_center - h * 0.5f; 148 | 149 | 150 | float box_objectness = feat_ptr[basic_pos + 4]; 151 | 152 | for (int class_idx = 0; class_idx < NUM_CLASSES; class_idx++) 153 | { 154 | //float box_cls_score = std::exp(feat_ptr[basic_pos + class_idx]) / total; 155 | float box_cls_score = feat_ptr[basic_pos + 5 + class_idx]; 156 | //std::cout << "Probability " << class_idx<< ": "<< box_cls_score<< std::endl; 157 | float box_prob = box_objectness * box_cls_score; ///std::cout <<"box_prob: " << box_prob<< std::endl; 158 | if (box_prob > prob_threshold) 159 | { 160 | Object obj; 161 | obj.rect.x = x0; 162 | obj.rect.y = y0; 163 | obj.rect.width = w; 164 | obj.rect.height = h; 165 | obj.label = class_idx; 166 | obj.prob = box_prob; 167 | 168 | 169 | 170 | objects.push_back(obj); 171 | } 172 | 173 | } // class loop 174 | 175 | } // point anchor loop 176 | } 177 | 178 | static inline float intersection_area(const Object& a, const Object& b) 179 | { 180 | cv::Rect_ inter = a.rect & b.rect; 181 | return inter.area(); 182 | } 183 | 184 | static void qsort_descent_inplace(std::vector& faceobjects, int left, int right) 185 | { 186 | int i = left; 187 | int j = right; 188 | float p = faceobjects[(left + right) / 2].prob; 189 | 190 | while (i <= j) 191 | { 192 | while (faceobjects[i].prob > p) 193 | i++; 194 | 195 | while (faceobjects[j].prob < p) 196 | j--; 197 | 198 | if (i <= j) 199 | { 200 | // swap 201 | std::swap(faceobjects[i], faceobjects[j]); 202 | 203 | i++; 204 | j--; 205 | } 206 | } 207 | 208 | #pragma omp parallel sections 209 | { 210 | #pragma omp section 211 | { 212 | if (left < j) qsort_descent_inplace(faceobjects, left, j); 213 | } 214 | #pragma omp section 215 | { 216 | if (i < right) qsort_descent_inplace(faceobjects, i, right); 217 | } 218 | } 219 | } 220 | 221 | 222 | static void qsort_descent_inplace(std::vector& objects) 223 | { 224 | if (objects.empty()) 225 | return; 226 | 227 | qsort_descent_inplace(objects, 0, objects.size() - 1); 228 | } 229 | 230 | static void nms_sorted_bboxes(const std::vector& faceobjects, std::vector& picked, float nms_threshold) 231 | { 232 | picked.clear(); 233 | 234 | const int n = faceobjects.size(); 235 | 236 | std::vector areas(n); 237 | for (int i = 0; i < n; i++) 238 | { 239 | areas[i] = faceobjects[i].rect.area(); 240 | } 241 | 242 | for (int i = 0; i < n; i++) 243 | { 244 | const Object& a = faceobjects[i]; 245 | 246 | int keep = 1; 247 | for (int j = 0; j < (int)picked.size(); j++) 248 | { 249 | const Object& b = faceobjects[picked[j]]; 250 | 251 | // intersection over union 252 | float inter_area = intersection_area(a, b); 253 | float union_area = areas[i] + areas[picked[j]] - inter_area; 254 | // float IoU = inter_area / union_area 255 | if (inter_area / union_area > nms_threshold) 256 | keep = 0; 257 | } 258 | 259 | if (keep) 260 | picked.push_back(i); 261 | } 262 | } 263 | 264 | 265 | static void decode_outputs(const float* prob, std::vector& objects, float scale, const int img_w, const int img_h) { 266 | std::vector proposals; 267 | std::vector strides = {8, 16, 32}; 268 | std::vector grid_strides; 269 | 270 | generate_grids_and_stride(INPUT_W, INPUT_H, strides, grid_strides); 271 | generate_yolox_proposals(grid_strides, prob, BBOX_CONF_THRESH, proposals); 272 | qsort_descent_inplace(proposals); 273 | 274 | std::vector picked; 275 | nms_sorted_bboxes(proposals, picked, NMS_THRESH); 276 | int count = picked.size(); 277 | objects.resize(count); 278 | 279 | for (int i = 0; i < count; i++) 280 | { 281 | objects[i] = proposals[picked[i]]; 282 | 283 | // adjust offset to original unpadded 284 | float x0 = (objects[i].rect.x) / scale; 285 | float y0 = (objects[i].rect.y) / scale; 286 | float x1 = (objects[i].rect.x + objects[i].rect.width) / scale; 287 | float y1 = (objects[i].rect.y + objects[i].rect.height) / scale; 288 | 289 | // clip 290 | x0 = std::max(std::min(x0, (float)(img_w - 1)), 0.f); 291 | y0 = std::max(std::min(y0, (float)(img_h - 1)), 0.f); 292 | x1 = std::max(std::min(x1, (float)(img_w - 1)), 0.f); 293 | y1 = std::max(std::min(y1, (float)(img_h - 1)), 0.f); 294 | 295 | objects[i].rect.x = x0; 296 | objects[i].rect.y = y0; 297 | objects[i].rect.width = x1 - x0; 298 | objects[i].rect.height = y1 - y0; 299 | } 300 | } 301 | 302 | const float color_list[80][3] = 303 | { 304 | {0.000, 0.447, 0.741}, 305 | {0.850, 0.325, 0.098}, 306 | {0.929, 0.694, 0.125}, 307 | {0.494, 0.184, 0.556}, 308 | {0.466, 0.674, 0.188}, 309 | {0.301, 0.745, 0.933}, 310 | {0.635, 0.078, 0.184}, 311 | {0.300, 0.300, 0.300}, 312 | {0.600, 0.600, 0.600}, 313 | {1.000, 0.000, 0.000}, 314 | {1.000, 0.500, 0.000}, 315 | {0.749, 0.749, 0.000}, 316 | {0.000, 1.000, 0.000}, 317 | {0.000, 0.000, 1.000}, 318 | {0.667, 0.000, 1.000}, 319 | {0.333, 0.333, 0.000}, 320 | {0.333, 0.667, 0.000}, 321 | {0.333, 1.000, 0.000}, 322 | {0.667, 0.333, 0.000}, 323 | {0.667, 0.667, 0.000}, 324 | {0.667, 1.000, 0.000}, 325 | {1.000, 0.333, 0.000}, 326 | {1.000, 0.667, 0.000}, 327 | {1.000, 1.000, 0.000}, 328 | {0.000, 0.333, 0.500}, 329 | {0.000, 0.667, 0.500}, 330 | {0.000, 1.000, 0.500}, 331 | {0.333, 0.000, 0.500}, 332 | {0.333, 0.333, 0.500}, 333 | {0.333, 0.667, 0.500}, 334 | {0.333, 1.000, 0.500}, 335 | {0.667, 0.000, 0.500}, 336 | {0.667, 0.333, 0.500}, 337 | {0.667, 0.667, 0.500}, 338 | {0.667, 1.000, 0.500}, 339 | {1.000, 0.000, 0.500}, 340 | {1.000, 0.333, 0.500}, 341 | {1.000, 0.667, 0.500}, 342 | {1.000, 1.000, 0.500}, 343 | {0.000, 0.333, 1.000}, 344 | {0.000, 0.667, 1.000}, 345 | {0.000, 1.000, 1.000}, 346 | {0.333, 0.000, 1.000}, 347 | {0.333, 0.333, 1.000}, 348 | {0.333, 0.667, 1.000}, 349 | {0.333, 1.000, 1.000}, 350 | {0.667, 0.000, 1.000}, 351 | {0.667, 0.333, 1.000}, 352 | {0.667, 0.667, 1.000}, 353 | {0.667, 1.000, 1.000}, 354 | {1.000, 0.000, 1.000}, 355 | {1.000, 0.333, 1.000}, 356 | {1.000, 0.667, 1.000}, 357 | {0.333, 0.000, 0.000}, 358 | {0.500, 0.000, 0.000}, 359 | {0.667, 0.000, 0.000}, 360 | {0.833, 0.000, 0.000}, 361 | {1.000, 0.000, 0.000}, 362 | {0.000, 0.167, 0.000}, 363 | {0.000, 0.333, 0.000}, 364 | {0.000, 0.500, 0.000}, 365 | {0.000, 0.667, 0.000}, 366 | {0.000, 0.833, 0.000}, 367 | {0.000, 1.000, 0.000}, 368 | {0.000, 0.000, 0.167}, 369 | {0.000, 0.000, 0.333}, 370 | {0.000, 0.000, 0.500}, 371 | {0.000, 0.000, 0.667}, 372 | {0.000, 0.000, 0.833}, 373 | {0.000, 0.000, 1.000}, 374 | {0.000, 0.000, 0.000}, 375 | {0.143, 0.143, 0.143}, 376 | {0.286, 0.286, 0.286}, 377 | {0.429, 0.429, 0.429}, 378 | {0.571, 0.571, 0.571}, 379 | {0.714, 0.714, 0.714}, 380 | {0.857, 0.857, 0.857}, 381 | {0.000, 0.447, 0.741}, 382 | {0.314, 0.717, 0.741}, 383 | {0.50, 0.5, 0} 384 | }; 385 | 386 | static void draw_objects(const cv::Mat& bgr, const std::vector& objects) 387 | { 388 | 389 | 390 | static const char* class_names[] = { 391 | "sunglass", 392 | "hat", 393 | "jacket", 394 | "shirt", 395 | "pants", 396 | "shorts", 397 | "skirt", 398 | "dress", 399 | "bag", 400 | "shoe", 401 | "top" 402 | }; 403 | 404 | cv::Mat image = bgr.clone(); 405 | 406 | for (size_t i = 0; i < objects.size(); i++) 407 | { 408 | const Object& obj = objects[i]; 409 | 410 | cv::Scalar color = cv::Scalar(color_list[obj.label][0], color_list[obj.label][1], color_list[obj.label][2]); 411 | float c_mean = cv::mean(color)[0]; 412 | cv::Scalar txt_color; 413 | if (c_mean > 0.5){ 414 | txt_color = cv::Scalar(0, 0, 0); 415 | }else{ 416 | txt_color = cv::Scalar(255, 255, 255); 417 | } 418 | 419 | cv::rectangle(image, obj.rect, color * 255, 2); 420 | 421 | char text[256]; 422 | sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100); 423 | 424 | int baseLine = 0; 425 | cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.4, 1, &baseLine); 426 | 427 | cv::Scalar txt_bk_color = color * 0.7 * 255; 428 | 429 | int x = obj.rect.x; 430 | int y = obj.rect.y + 1; 431 | //int y = obj.rect.y - label_size.height - baseLine; 432 | if (y > image.rows) 433 | y = image.rows; 434 | //if (x + label_size.width > image.cols) 435 | //x = image.cols - label_size.width; 436 | 437 | cv::rectangle(image, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)), 438 | txt_bk_color, -1); 439 | 440 | cv::putText(image, text, cv::Point(x, y + label_size.height), 441 | cv::FONT_HERSHEY_SIMPLEX, 0.4, txt_color, 1); 442 | } 443 | 444 | cv::imwrite("_demo.jpg" , image); 445 | fprintf(stderr, "save vis file\n"); 446 | /* cv::imshow("image", image); */ 447 | /* cv::waitKey(0); */ 448 | } 449 | 450 | /** 451 | * @brief Print ONNX tensor data type 452 | * https://github.com/microsoft/onnxruntime/blob/rel-1.6.0/include/onnxruntime/core/session/onnxruntime_c_api.h#L93 453 | * @param os 454 | * @param type 455 | * @return std::ostream& 456 | */ 457 | std::ostream& operator<<(std::ostream& os, 458 | const ONNXTensorElementDataType& type) 459 | { 460 | switch (type) 461 | { 462 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED: 463 | os << "undefined"; 464 | break; 465 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT: 466 | os << "float"; 467 | break; 468 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8: 469 | os << "uint8_t"; 470 | break; 471 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8: 472 | os << "int8_t"; 473 | break; 474 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16: 475 | os << "uint16_t"; 476 | break; 477 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16: 478 | os << "int16_t"; 479 | break; 480 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32: 481 | os << "int32_t"; 482 | break; 483 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64: 484 | os << "int64_t"; 485 | break; 486 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING: 487 | os << "std::string"; 488 | break; 489 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL: 490 | os << "bool"; 491 | break; 492 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16: 493 | os << "float16"; 494 | break; 495 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE: 496 | os << "double"; 497 | break; 498 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32: 499 | os << "uint32_t"; 500 | break; 501 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64: 502 | os << "uint64_t"; 503 | break; 504 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX64: 505 | os << "float real + float imaginary"; 506 | break; 507 | case ONNXTensorElementDataType:: 508 | ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX128: 509 | os << "double real + float imaginary"; 510 | break; 511 | case ONNXTensorElementDataType::ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16: 512 | os << "bfloat16"; 513 | break; 514 | default: 515 | break; 516 | } 517 | 518 | return os; 519 | } 520 | 521 | std::vector readLabels(std::string& labelFilepath) 522 | { 523 | std::vector labels; 524 | std::string line; 525 | std::ifstream fp(labelFilepath); 526 | while (std::getline(fp, line)) 527 | { 528 | labels.push_back(line); 529 | } 530 | return labels; 531 | } 532 | 533 | 534 | 535 | cv::Mat GetSquareImage(cv::Mat& img, int target_width, int target_height) 536 | { 537 | int width = img.cols, 538 | height = img.rows; 539 | float scale = 0; 540 | 541 | scale = std::min(target_width / width, target_height / height); 542 | 543 | cv::Mat square = cv::Mat::zeros( target_width, target_height, img.type() ); 544 | 545 | 546 | cv::Rect roi; 547 | 548 | roi.width = (int) scale * width; 549 | roi.x = 0; 550 | roi.height = (int) scale * height; 551 | roi.y = 0; 552 | //roi.y = ( target_width - roi.height ) / 2; 553 | 554 | 555 | 556 | cv::resize( img, square(roi), roi.size() ); 557 | 558 | return square; 559 | } 560 | 561 | 562 | int main(int argc, char* argv[]) 563 | { 564 | bool useCUDA{false}; 565 | const char* useCUDAFlag = "--use_cuda"; 566 | const char* useCPUFlag = "--use_cpu"; 567 | if (argc == 1) 568 | { 569 | useCUDA = false; 570 | } 571 | else if ((argc == 2) && (strcmp(argv[1], useCUDAFlag) == 0)) 572 | { 573 | useCUDA = true; 574 | } 575 | else if ((argc == 2) && (strcmp(argv[1], useCPUFlag) == 0)) 576 | { 577 | useCUDA = false; 578 | } 579 | else if ((argc == 2) && (strcmp(argv[1], useCUDAFlag) != 0)) 580 | { 581 | useCUDA = false; 582 | } 583 | else 584 | { 585 | throw std::runtime_error{"Too many arguments."}; 586 | } 587 | 588 | if (useCUDA) 589 | { 590 | std::cout << "Inference Execution Provider: CUDA" << std::endl; 591 | } 592 | else 593 | { 594 | std::cout << "Inference Execution Provider: CPU" << std::endl; 595 | } 596 | 597 | std::string instanceName{"image-classification-inference"}; 598 | std::string modelFilepath{"best_yolox_nano.onnx"}; 599 | std::string imageFilepath{"demo.jpg"}; 600 | std::string labelFilepath{"synset.txt"}; 601 | 602 | std::vector labels{readLabels(labelFilepath)}; 603 | 604 | 605 | Ort::Env env(OrtLoggingLevel::ORT_LOGGING_LEVEL_WARNING, 606 | instanceName.c_str()); 607 | Ort::SessionOptions sessionOptions; 608 | sessionOptions.SetIntraOpNumThreads(1); 609 | 610 | sessionOptions.SetGraphOptimizationLevel( 611 | GraphOptimizationLevel::ORT_ENABLE_EXTENDED); 612 | 613 | Ort::Session session(env, modelFilepath.c_str(), sessionOptions); 614 | 615 | Ort::AllocatorWithDefaultOptions allocator; 616 | 617 | size_t numInputNodes = session.GetInputCount(); 618 | size_t numOutputNodes = session.GetOutputCount(); 619 | 620 | 621 | const char* inputName = session.GetInputName(0, allocator); 622 | //std::cout << "Input Name: " << inputName << std::endl; 623 | 624 | Ort::TypeInfo inputTypeInfo = session.GetInputTypeInfo(0); 625 | auto inputTensorInfo = inputTypeInfo.GetTensorTypeAndShapeInfo(); 626 | 627 | ONNXTensorElementDataType inputType = inputTensorInfo.GetElementType(); 628 | //std::cout << "Input Type: " << inputType << std::endl; 629 | 630 | std::vector inputDims = inputTensorInfo.GetShape(); 631 | //std::cout << "Input Dimensions: " << inputDims << std::endl; 632 | 633 | const char* outputName = session.GetOutputName(0, allocator); 634 | //std::cout << "Output Name: " << outputName << std::endl; 635 | 636 | Ort::TypeInfo outputTypeInfo = session.GetOutputTypeInfo(0); 637 | auto outputTensorInfo = outputTypeInfo.GetTensorTypeAndShapeInfo(); 638 | 639 | ONNXTensorElementDataType outputType = outputTensorInfo.GetElementType(); 640 | //std::cout << "Output Type: " << outputType << std::endl; 641 | 642 | std::vector outputDims = outputTensorInfo.GetShape(); 643 | //std::cout << "Output Dimensions: " << outputDims << std::endl; 644 | 645 | cv::Mat imageBGR = cv::imread(imageFilepath, cv::ImreadModes::IMREAD_COLOR); 646 | cv::Mat preprocessedImage; 647 | 648 | cv::Mat resizedImage = static_resize(imageBGR); 649 | cv::dnn::blobFromImage(resizedImage, preprocessedImage); 650 | // cv::imwrite("image.jpg", resizedImage); 651 | 652 | 653 | size_t inputTensorSize = vectorProduct(inputDims); 654 | std::vector inputTensorValues(inputTensorSize); 655 | inputTensorValues.assign(preprocessedImage.begin(), 656 | preprocessedImage.end()); 657 | 658 | size_t outputTensorSize = vectorProduct(outputDims); 659 | std::vector outputTensorValues(outputTensorSize); 660 | 661 | std::vector inputNames{inputName}; 662 | std::vector outputNames{outputName}; 663 | std::vector inputTensors; 664 | std::vector outputTensors; 665 | 666 | Ort::MemoryInfo memoryInfo = Ort::MemoryInfo::CreateCpu( 667 | OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); 668 | inputTensors.push_back(Ort::Value::CreateTensor( 669 | memoryInfo, inputTensorValues.data(), inputTensorSize, inputDims.data(), 670 | inputDims.size())); 671 | outputTensors.push_back(Ort::Value::CreateTensor( 672 | memoryInfo, outputTensorValues.data(), outputTensorSize, 673 | outputDims.data(), outputDims.size())); 674 | 675 | session.Run(Ort::RunOptions{nullptr}, inputNames.data(), 676 | inputTensors.data(), 1, outputNames.data(), 677 | outputTensors.data(), 1); 678 | 679 | 680 | 681 | cv::Mat image = imread_t(imageFilepath); 682 | int img_w = image.cols; 683 | int img_h = image.rows; 684 | float scale = std::min(INPUT_W / (image.cols*1.0), INPUT_H / (image.rows*1.0)); 685 | std::vector objects; 686 | 687 | 688 | const float * net_pred = outputTensorValues.data(); 689 | 690 | 691 | decode_outputs(net_pred, objects, scale, img_w, img_h); 692 | draw_objects(image, objects); 693 | 694 | } 695 | 696 | --------------------------------------------------------------------------------