diff --git a/.github/workflows/test_accuracy.yml b/.github/workflows/test_accuracy.yml index 0ce9dc10..37e81794 100644 --- a/.github/workflows/test_accuracy.yml +++ b/.github/workflows/test_accuracy.yml @@ -42,17 +42,8 @@ jobs: mkdir build && cd build pip install nanobind==2.4.0 pip install typing_extensions==4.12.2 - cmake ../tests/cpp/accuracy/ + cmake ../tests/cpp make -j - - name: Build CPP-PY Bindings - run: | - source venv/bin/activate - pip install src/cpp/py_bindings - name: Run CPP Test run: | build/test_accuracy -d data -p tests/python/accuracy/public_scope.json - - name: Run CPP-PY Bindings Test - run: | - source venv/bin/activate - pip list - pytest --data=./data --config=./tests/python/accuracy/public_scope.json tests/cpp/accuracy/test_bindings.py diff --git a/.github/workflows/test_precommit.yml b/.github/workflows/test_precommit.yml index 5ae39dcb..45961b40 100644 --- a/.github/workflows/test_precommit.yml +++ b/.github/workflows/test_precommit.yml @@ -47,81 +47,6 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN}} # missingInclude: cppcheck can't find stl, openvino, opencv other_options: --suppress=missingInclude -Isrc/cpp/models/include -Isrc/cpp/utils/include -Isrc/cpp/pipelines/include --check-config - CPP-Precommit: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: "3.10" - cache: pip - - name: Create and start a virtual environment - run: | - python -m venv venv - source venv/bin/activate - - name: Install dependencies - run: | - source venv/bin/activate - python -m pip install --upgrade pip - pip install src/python/[tests,build] --extra-index-url https://download.pytorch.org/whl/cpu - - sudo bash src/cpp/install_dependencies.sh - - name: Prepare test data - run: | - source venv/bin/activate - python tests/cpp/precommit/prepare_data.py -d data -p tests/cpp/precommit/public_scope.json - - name: Build - run: | - mkdir build && cd build - pip install nanobind==2.4.0 - pip install typing_extensions==4.12.2 - cmake ../tests/cpp/precommit/ - cmake --build . -j $((`nproc`*2+2)) - - name: Run test - run: | - build/test_sanity -d data -p tests/cpp/precommit/public_scope.json && build/test_model_config -d data - CPP-Windows-Precommit: - runs-on: windows-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: 3.9 - cache: pip - - name: Create and start a virtual environment - shell: bash - run: | - python -m venv venv - source venv/Scripts/activate - - name: Install dependencies - shell: bash - run: | - source venv/Scripts/activate - python -m pip install --upgrade pip - pip install src/python/[tests,build] --extra-index-url https://download.pytorch.org/whl/cpu - curl https://storage.openvinotoolkit.org/repositories/openvino/packages/2024.6/windows/w_openvino_toolkit_windows_2024.6.0.17404.4c0f47d2335_x86_64.zip --output w_openvino_toolkit_windows.zip - unzip w_openvino_toolkit_windows.zip - rm w_openvino_toolkit_windows.zip - curl -L https://github.com/opencv/opencv/releases/download/4.10.0/opencv-4.10.0-windows.exe --output opencv-4.10.0-windows.exe - ./opencv-4.10.0-windows.exe -oopencv -y - rm opencv-4.10.0-windows.exe - - name: Prepare test data - shell: bash - run: | - source venv/Scripts/activate - python tests/cpp/precommit/prepare_data.py -d data -p tests/cpp/precommit/public_scope.json - - name: Build - shell: bash - run: | - mkdir build && cd build - MSYS_NO_PATHCONV=1 cmake ../examples/cpp/ -DOpenVINO_DIR=$GITHUB_WORKSPACE/w_openvino_toolkit_windows_2024.6.0.17404.4c0f47d2335_x86_64/runtime/cmake -DOpenCV_DIR=$GITHUB_WORKSPACE/opencv/opencv/build -DCMAKE_CXX_FLAGS=/WX - cmake --build . --config Release -j $((`nproc`*2+2)) - - name: Run sync sample - shell: cmd - # .\w_openvino_toolkit_windows_2023.0.0.10926.b4452d56304_x86_64\setupvars.bat exits with 0 code without moving to a next command. Set PATH manually - run: | - set PATH=opencv\opencv\build\x64\vc16\bin;w_openvino_toolkit_windows_2024.6.0.17404.4c0f47d2335_x86_64\runtime\bin\intel64\Release;w_openvino_toolkit_windows_2024.6.0.17404.4c0f47d2335_x86_64\runtime\3rdparty\tbb\bin;%PATH% - .\build\Release\synchronous_api.exe .\data\otx_models\detection_model_with_xai_head.xml .\data\BloodImage_00007.jpg serving_api: strategy: fail-fast: false diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index fe22571f..f2e10985 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -94,5 +94,4 @@ find_package(OpenCV REQUIRED COMPONENTS imgcodecs) add_subdirectory(../../src/cpp ${Samples_BINARY_DIR}/src/cpp) -add_example(NAME asynchronous_api SOURCES ./asynchronous_api/main.cpp DEPENDENCIES model_api) -add_example(NAME synchronous_api SOURCES ./synchronous_api/main.cpp DEPENDENCIES model_api) +add_example(NAME synchronous_api SOURCES ./main.cpp DEPENDENCIES model_api) diff --git a/examples/cpp/synchronous_api/README.md b/examples/cpp/README.md similarity index 100% rename from examples/cpp/synchronous_api/README.md rename to examples/cpp/README.md diff --git a/examples/cpp/asynchronous_api/README.md b/examples/cpp/asynchronous_api/README.md deleted file mode 100644 index 88e44483..00000000 --- a/examples/cpp/asynchronous_api/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Synchronous API example - -This example demonstrates how to use a C++ API of OpenVINO Model API for asynchronous inference and its basic steps: - -- Instantiate a model -- Set a callback to grab inference results -- Model inference -- Model batch inference -- Process results of the batch inference - -## Prerequisites - -- Install third party dependencies by running the following script: - - ```bash - chmod +x ../../../src/cpp/install_dependencies.sh - sudo ../../../src/cpp/install_dependencies.sh - ``` - -- Build example: - - - Create `build` folder and navigate into it: - - ```bash - mkdir build && cd build - ``` - - - Run cmake: - - ```bash - cmake ../ - ``` - - - Build: - - ```bash - make -j - ``` - -- Download a model by running a Python code with Model API, see Python [example](../../python/asynchronous_api/README.md): - - ```python - from model_api.models import DetectionModel - - model = DetectionModel.create_model("ssd_mobilenet_v1_fpn_coco", - download_dir="tmp") - ``` - -## Run example - -To run the example, please execute the following command: - -```bash -./asynchronous_api ./tmp/public/ssd_mobilenet_v1_fpn_coco/FP16/ssd_mobilenet_v1_fpn_coco.xml -``` diff --git a/examples/cpp/asynchronous_api/main.cpp b/examples/cpp/asynchronous_api/main.cpp deleted file mode 100644 index f8edc2cc..00000000 --- a/examples/cpp/asynchronous_api/main.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2020-2024 Intel Corporation - * SPDX-License-Identifier: Apache-2.0 - */ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char* argv[]) try { - if (argc != 3) { - throw std::runtime_error(std::string{"Usage: "} + argv[0] + " "); - } - - cv::Mat image = cv::imread(argv[2]); - cv::cvtColor(image, image, cv::COLOR_BGR2RGB); - - if (!image.data) { - throw std::runtime_error{"Failed to read the image"}; - } - - // Instantiate Object Detection model - auto model = DetectionModel::create_model(argv[1], - {}, - "", - false); // works with SSD models. Download it using Python Model API - // Define number of parallel infer requests. Is this number is set to 0, OpenVINO will determine it automatically to - // obtain optimal performance. - size_t num_requests = 0; - static ov::Core core; - model->load(core, "CPU", num_requests); - - std::cout << "Async inference will be carried out by " << model->getNumAsyncExecutors() << " parallel executors\n"; - // Prepare batch data - std::vector data; - for (size_t i = 0; i < 3; i++) { - data.push_back(ImageInputData(image)); - } - - // Batch inference is done by processing batch with num_requests parallel infer requests - std::cout << "Starting batch inference\n"; - auto results = model->inferBatch(data); - - std::cout << "Batch mode inference results:\n"; - for (const auto& result : results) { - for (auto& obj : result->objects) { - std::cout << " " << std::left << std::setw(9) << obj.confidence << " " << obj.label << "\n"; - } - std::cout << std::string(10, '-') << "\n"; - } - std::cout << "Batch mode inference done\n"; - std::cout << "Async mode inference results:\n"; - - // Set callback to grab results once the inference is done - model->setCallback([](std::unique_ptr result, const ov::AnyMap& callback_args) { - auto det_result = std::unique_ptr(static_cast(result.release())); - - // callback_args can contain arbitrary data - size_t id = callback_args.find("id")->second.as(); - - std::cout << "Request with id " << id << " is finished\n"; - for (auto& obj : det_result->objects) { - std::cout << " " << std::left << std::setw(9) << obj.confidence << " " << obj.label << "\n"; - } - std::cout << std::string(10, '-') << "\n"; - }); - - for (size_t i = 0; i < 3; i++) { - model->inferAsync(image, {{"id", i}}); - } - model->awaitAll(); -} catch (const std::exception& error) { - std::cerr << error.what() << '\n'; - return 1; -} catch (...) { - std::cerr << "Non-exception object thrown\n"; - return 1; -} diff --git a/examples/cpp/synchronous_api/main.cpp b/examples/cpp/main.cpp similarity index 78% rename from examples/cpp/synchronous_api/main.cpp rename to examples/cpp/main.cpp index 1f79a035..47d2eaf9 100644 --- a/examples/cpp/synchronous_api/main.cpp +++ b/examples/cpp/main.cpp @@ -1,12 +1,11 @@ /* - * Copyright (C) 2020-2024 Intel Corporation + * Copyright (C) 2020-2025 Intel Corporation * SPDX-License-Identifier: Apache-2.0 */ -#include -#include -#include #include +#include +#include #include #include @@ -31,13 +30,13 @@ int main(int argc, char* argv[]) try { } // Instantiate Object Detection model - auto model = DetectionModel::create_model(argv[1]); // works with SSD models. Download it using Python Model API + auto model = DetectionModel::load(argv[1], {}); // works with SSD models. Download it using Python Model API // Run the inference - auto result = model->infer(image); + auto result = model.infer(image); // Process detections - for (auto& obj : result->objects) { + for (auto& obj : result.objects) { std::cout << " " << std::left << std::setw(9) << obj.label << " | " << std::setw(10) << obj.confidence << " | " << std::setw(4) << int(obj.x) << " | " << std::setw(4) << int(obj.y) << " | " << std::setw(4) << int(obj.x + obj.width) << " | " << std::setw(4) << int(obj.y + obj.height) << "\n"; diff --git a/examples/python/asynchronous_api/run.py b/examples/python/asynchronous_api/run.py index 8385b2a9..f0667a3e 100644 --- a/examples/python/asynchronous_api/run.py +++ b/examples/python/asynchronous_api/run.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2024 Intel Corporation +# Copyright (C) 2020-2025 Intel Corporation # SPDX-License-Identifier: Apache-2.0 # diff --git a/examples/python/serving_api/run.py b/examples/python/serving_api/run.py index 99299164..213029dc 100755 --- a/examples/python/serving_api/run.py +++ b/examples/python/serving_api/run.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2024 Intel Corporation +# Copyright (C) 2020-2025 Intel Corporation # SPDX-License-Identifier: Apache-2.0 # diff --git a/examples/python/synchronous_api/run.py b/examples/python/synchronous_api/run.py index 3f3d6806..d45dd03a 100755 --- a/examples/python/synchronous_api/run.py +++ b/examples/python/synchronous_api/run.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2024 Intel Corporation +# Copyright (C) 2020-2025 Intel Corporation # SPDX-License-Identifier: Apache-2.0 # diff --git a/examples/python/visual_prompting/run.py b/examples/python/visual_prompting/run.py index 793e1504..0eab9175 100644 --- a/examples/python/visual_prompting/run.py +++ b/examples/python/visual_prompting/run.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2024 Intel Corporation +# Copyright (C) 2020-2025 Intel Corporation # SPDX-License-Identifier: Apache-2.0 # diff --git a/examples/python/zsl_visual_prompting/run.py b/examples/python/zsl_visual_prompting/run.py index 439c1765..9873e6f3 100644 --- a/examples/python/zsl_visual_prompting/run.py +++ b/examples/python/zsl_visual_prompting/run.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2024 Intel Corporation +# Copyright (C) 2020-2025 Intel Corporation # SPDX-License-Identifier: Apache-2.0 # diff --git a/src/cpp/CMakeLists.txt b/src/cpp/CMakeLists.txt index 880f211a..215bf2aa 100644 --- a/src/cpp/CMakeLists.txt +++ b/src/cpp/CMakeLists.txt @@ -1,106 +1,24 @@ -# Copyright (C) 2018-2025 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 -# - cmake_minimum_required(VERSION 3.26) -# Multi config generators such as Visual Studio ignore CMAKE_BUILD_TYPE. Multi config generators are configured with -# CMAKE_CONFIGURATION_TYPES, but limiting options in it completely removes such build options -get_property(GENERATOR_IS_MULTI_CONFIG_VAR GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(NOT GENERATOR_IS_MULTI_CONFIG_VAR AND NOT DEFINED CMAKE_BUILD_TYPE) - message(STATUS "CMAKE_BUILD_TYPE not defined, 'Release' will be used") - # Setting CMAKE_BUILD_TYPE as CACHE must go before project(). Otherwise project() sets its value and set() doesn't take an effect - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel ...") -endif() - -set(model_api_VERSION 0.0.0) +set(vision_api_VERSION 0.0.0) -project(model_api - VERSION ${model_api_VERSION} +project(vision_api + VERSION ${vision_api_VERSION} DESCRIPTION "OpenVINO Vision API" HOMEPAGE_URL "https://github.com/openvinotoolkit/model_api/" LANGUAGES CXX C) -if(WIN32) - if(NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - message(FATAL_ERROR "Only 64-bit supported on Windows") - endif() - - add_definitions(-DNOMINMAX) -endif() - find_package(OpenCV REQUIRED COMPONENTS core imgproc) find_package(OpenVINO REQUIRED COMPONENTS Runtime Threading) -include(FetchContent) -FetchContent_Declare(json GIT_REPOSITORY https://github.com/nlohmann/json.git - GIT_TAG d41ca94fa85d5119852e2f7a3f94335cc7cb0486 # PR #4709, fixes cmake deprecation warnings - ) -FetchContent_MakeAvailable(json) - -file(GLOB MODELS_SOURCES ./models/src/*.cpp) -file(GLOB MODELS_HEADERS ./models/include/models/*.h) -file(GLOB_RECURSE UTILS_HEADERS ./utils/include/*) -file(GLOB_RECURSE UTILS_SOURCES ./utils/src/*.cpp) -file(GLOB_RECURSE ADAPTERS_HEADERS ./adapters/include/*) -file(GLOB_RECURSE ADAPTERS_SOURCES ./adapters/src/*.cpp) -file(GLOB_RECURSE TILERS_HEADERS ./tilers/include/tilers/*.h) -file(GLOB_RECURSE TILERS_SOURCES ./tilers/src/*.cpp) +file(GLOB TASK_SOURCES src/tasks/**/*.cpp) +file(GLOB TASKS_SOURCES src/tasks/*.cpp) +file(GLOB UTILS_SOURCES src/utils/*.cpp) +file(GLOB ADAPTERS_SOURCES src/adapters/*.cpp) -# Create named folders for the sources within the .vcproj -# Empty name lists them directly under the .vcproj -source_group("models/src" FILES ${MODELS_SOURCES}) -source_group("models/include" FILES ${MODELS_HEADERS}) -source_group("utils/src" FILES ${UTILS_SOURCES}) -source_group("utils/include" FILES ${UTILS_HEADERS}) -source_group("adapters/src" FILES ${ADAPTERS_SOURCES}) -source_group("adapters/include" FILES ${ADAPTERS_HEADERS}) -source_group("tilers/src" FILES ${TILERS_SOURCES}) -source_group("tilers/include" FILES ${TILERS_HEADERS}) +add_library(model_api STATIC ${TASK_SOURCES} ${TASKS_SOURCES} ${UTILS_SOURCES} ${ADAPTERS_SOURCES} ${TILERS_SOURCES}) -add_library(model_api STATIC ${MODELS_SOURCES} ${UTILS_SOURCES} ${ADAPTERS_SOURCES} ${TILERS_SOURCES}) -target_include_directories(model_api PUBLIC "$" "$" "$" "$" "$") target_link_libraries(model_api PUBLIC openvino::runtime opencv_core opencv_imgproc) -target_link_libraries(model_api PRIVATE $) -set_target_properties(model_api PROPERTIES CXX_STANDARD 17) -set_target_properties(model_api PROPERTIES CXX_STANDARD_REQUIRED ON) -if(MSVC) - target_compile_options(model_api PRIVATE /wd4251 /wd4275 /wd4267 # disable some warnings - /W3 # Specify the level of warnings to be generated by the compiler - /EHsc) # Enable standard C++ stack unwinding, assume functions with extern "C" never throw -elseif(CMAKE_CXX_COMPILER_ID MATCHES "^GNU|(Apple)?Clang$") - target_compile_options(model_api PRIVATE -Wall -Wextra -Wpedantic) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") -endif() - -include(GenerateExportHeader) - -generate_export_header(model_api) -set_property(TARGET model_api PROPERTY VERSION ${model_api_VERSION}) -set_property(TARGET model_api PROPERTY SOVERSION 3) -set_property(TARGET model_api PROPERTY INTERFACE_model_api_MAJOR_VERSION 3) -set_property(TARGET model_api APPEND PROPERTY COMPATIBLE_INTERFACE_STRING model_api_MAJOR_VERSION) - -install(TARGETS model_api EXPORT model_apiTargets - LIBRARY DESTINATION lib COMPONENT Devel - ARCHIVE DESTINATION lib COMPONENT Devel - RUNTIME DESTINATION bin COMPONENT Devel - INCLUDES DESTINATION include) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/models/include/" "${CMAKE_CURRENT_SOURCE_DIR}/utils/include/" "${CMAKE_CURRENT_SOURCE_DIR}/adapters/include/" "${CMAKE_CURRENT_SOURCE_DIR}/tilers/include/" - DESTINATION include COMPONENT Devel) - -include(CMakePackageConfigHelpers) -write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/model_apiConfigVersion.cmake" VERSION ${model_api_VERSION} COMPATIBILITY AnyNewerVersion) - -export(EXPORT model_apiTargets FILE "${CMAKE_CURRENT_BINARY_DIR}/model_apiTargets.cmake") -configure_file(cmake/model_apiConfig.cmake "${CMAKE_CURRENT_BINARY_DIR}/model_apiConfig.cmake" COPYONLY) - -set(ConfigPackageLocation lib/cmake/model_api) -install(EXPORT model_apiTargets FILE model_apiTargets.cmake DESTINATION ${ConfigPackageLocation}) -install(FILES cmake/model_apiConfig.cmake "${CMAKE_CURRENT_BINARY_DIR}/model_apiConfigVersion.cmake" - DESTINATION ${ConfigPackageLocation} COMPONENT Devel) - -set(CPACK_PACKAGE_VERSION ${model_api_VERSION}) -include(CPack) +target_include_directories(model_api PUBLIC ${PROJECT_SOURCE_DIR}/include) diff --git a/src/cpp/cmake/model_apiConfig.cmake b/src/cpp/cmake/model_apiConfig.cmake deleted file mode 100644 index 570679ce..00000000 --- a/src/cpp/cmake/model_apiConfig.cmake +++ /dev/null @@ -1,7 +0,0 @@ -include(CMakeFindDependencyMacro) -find_dependency(OpenCV COMPONENTS core imgproc) -find_dependency(OpenVINO COMPONENTS Runtime) - -include("${CMAKE_CURRENT_LIST_DIR}/model_apiTargets.cmake") - -check_required_components() diff --git a/src/cpp/adapters/include/adapters/inference_adapter.h b/src/cpp/include/adapters/inference_adapter.h similarity index 97% rename from src/cpp/adapters/include/adapters/inference_adapter.h rename to src/cpp/include/adapters/inference_adapter.h index 8d28e84e..9911f655 100644 --- a/src/cpp/adapters/include/adapters/inference_adapter.h +++ b/src/cpp/include/adapters/inference_adapter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020-2024 Intel Corporation + * Copyright (C) 2020-2025 Intel Corporation * SPDX-License-Identifier: Apache-2.0 */ diff --git a/src/cpp/adapters/include/adapters/openvino_adapter.h b/src/cpp/include/adapters/openvino_adapter.h similarity index 95% rename from src/cpp/adapters/include/adapters/openvino_adapter.h rename to src/cpp/include/adapters/openvino_adapter.h index 7e1db1ac..7713ae93 100644 --- a/src/cpp/adapters/include/adapters/openvino_adapter.h +++ b/src/cpp/include/adapters/openvino_adapter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020-2024 Intel Corporation + * Copyright (C) 2020-2025 Intel Corporation * SPDX-License-Identifier: Apache-2.0 */ @@ -12,7 +12,7 @@ #include #include "adapters/inference_adapter.h" -#include "utils/async_infer_queue.hpp" +#include "utils/async_infer_queue.h" class OpenVINOInferenceAdapter : public InferenceAdapter { public: @@ -46,7 +46,9 @@ class OpenVINOInferenceAdapter : public InferenceAdapter { // Depends on the implementation details but we should share the model state in this class std::vector inputNames; std::vector outputNames; - ov::CompiledModel compiledModel; std::unique_ptr asyncQueue; ov::AnyMap modelConfig; // the content of model_info section of rt_info + +public: + ov::CompiledModel compiledModel; }; diff --git a/src/cpp/include/tasks/detection.h b/src/cpp/include/tasks/detection.h new file mode 100644 index 00000000..e5405133 --- /dev/null +++ b/src/cpp/include/tasks/detection.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020-2025 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include + +#include "adapters/inference_adapter.h" +#include "tasks/detection/ssd.h" +#include "tasks/results.h" +#include "utils/tiling.h" +#include "utils/vision_pipeline.h" + +class DetectionModel { +public: + std::unique_ptr> pipeline; + + DetectionModel(std::unique_ptr algorithm, const ov::AnyMap& configuration) : algorithm(std::move(algorithm)) { + auto config = this->algorithm->adapter->getModelConfig(); + if (configuration.count("tiling") && configuration.at("tiling").as()) { + if (!utils::config_contains_tiling_info(config)) { + throw std::runtime_error("Model config does not contain tiling properties."); + } + pipeline = std::make_unique>( + this->algorithm->adapter, + utils::get_tiling_info_from_config(config), + [&](cv::Mat image) { + return preprocess(image); + }, + [&](InferenceResult result) { + return postprocess(result); + }, + [&](DetectionResult& result, const cv::Rect& coord) { + return postprocess_tile(result, coord); + }, + [&](const std::vector& tiles_results, + const cv::Size& image_size, + const std::vector& tile_coords, + const utils::TilingInfo& tiling_info) { + return merge_tiling_results(tiles_results, image_size, tile_coords, tiling_info); + }); + } else { + pipeline = std::make_unique>( + this->algorithm->adapter, + [&](cv::Mat image) { + return preprocess(image); + }, + [&](InferenceResult result) { + return postprocess(result); + }); + } + } + + InferenceInput preprocess(cv::Mat); + DetectionResult postprocess(InferenceResult); + DetectionResult postprocess_tile(DetectionResult& result, const cv::Rect& coord); + DetectionResult merge_tiling_results(const std::vector& tiles_results, + const cv::Size& image_size, + const std::vector& tile_coords, + const utils::TilingInfo& tiling_info); + ov::Tensor merge_saliency_maps(const std::vector& tiles_results, + const cv::Size& image_size, + const std::vector& tile_coords, + const utils::TilingInfo& tiling_info); + + static DetectionModel load(const std::string& model_path, const ov::AnyMap& configuration = {}); + + DetectionResult infer(cv::Mat image); + std::vector inferBatch(std::vector image); + +private: + std::unique_ptr algorithm; +}; diff --git a/src/cpp/include/tasks/detection/ssd.h b/src/cpp/include/tasks/detection/ssd.h new file mode 100644 index 00000000..a51311f9 --- /dev/null +++ b/src/cpp/include/tasks/detection/ssd.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020-2025 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include "adapters/inference_adapter.h" +#include "tasks/results.h" +#include "utils/config.h" +#include "utils/preprocessing.h" + +enum SSDOutputMode { single, multi }; + +class NumAndStep { +public: + size_t detectionsNum, objectSize; + + static inline NumAndStep fromSingleOutput(const ov::Shape& shape); + static inline NumAndStep fromMultipleOutputs(const ov::Shape& boxesShape); +}; + +constexpr float box_area_threshold = 1.0f; + +class SSD { +public: + std::shared_ptr adapter; + + SSD(std::shared_ptr adapter, cv::Size input_shape) : adapter(adapter), input_shape(input_shape) { + auto config = adapter->getModelConfig(); + labels = utils::get_from_any_maps("labels", config, {}, labels); + confidence_threshold = utils::get_from_any_maps("confidence_threshold", config, {}, confidence_threshold); + } + std::map preprocess(cv::Mat); + DetectionResult postprocess(InferenceResult& infResult); + + static cv::Size serialize(std::shared_ptr ov_model); + + SSDOutputMode output_mode; + +private: + static void prepareSingleOutput(std::shared_ptr ov_model); + static void prepareMultipleOutputs(std::shared_ptr ov_model); + + DetectionResult postprocessSingleOutput(InferenceResult& infResult); + DetectionResult postprocessMultipleOutputs(InferenceResult& infResult); + + float confidence_threshold = 0.5f; + + std::vector labels; + std::vector filterOutXai(const std::vector&); + + std::vector output_names = {}; + utils::RESIZE_MODE resize_mode = utils::RESIZE_FILL; + ov::Layout layout; + cv::InterpolationFlags interpolation_mode; + cv::Size input_shape; +}; diff --git a/src/cpp/include/tasks/instance_segmentation.h b/src/cpp/include/tasks/instance_segmentation.h new file mode 100644 index 00000000..1e72488e --- /dev/null +++ b/src/cpp/include/tasks/instance_segmentation.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020-2025 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#include "adapters/inference_adapter.h" +#include "tasks/results.h" +#include "utils/config.h" +#include "utils/vision_pipeline.h" + +class InstanceSegmentation { +public: + std::shared_ptr adapter; + VisionPipeline pipeline; + + InstanceSegmentation(std::shared_ptr adapter, cv::Size input_shape) + : adapter(adapter), + input_shape(input_shape) { + pipeline = VisionPipeline( + adapter, + [&](cv::Mat image) { + return preprocess(image); + }, + [&](InferenceResult result) { + return postprocess(result); + }); + + auto config = adapter->getModelConfig(); + labels = utils::get_from_any_maps("labels", config, {}, labels); + confidence_threshold = utils::get_from_any_maps("confidence_threshold", config, {}, confidence_threshold); + } + + static cv::Size serialize(std::shared_ptr& ov_model); + static InstanceSegmentation load(const std::string& model_path); + + InstanceSegmentationResult infer(cv::Mat image); + std::vector inferBatch(std::vector image); + + std::map preprocess(cv::Mat); + InstanceSegmentationResult postprocess(InferenceResult& infResult); + + static std::vector getRotatedRectangles(const InstanceSegmentationResult& result); + static std::vector getContours(const std::vector& objects); + + bool postprocess_semantic_masks = true; + +private: + std::vector labels; + std::string getLabelName(size_t labelID) { + return labelID < labels.size() ? labels[labelID] : std::string("Label #") + std::to_string(labelID); + } + + cv::Size input_shape; + float confidence_threshold = 0.5f; +}; diff --git a/src/cpp/include/tasks/results.h b/src/cpp/include/tasks/results.h new file mode 100644 index 00000000..42c47d3b --- /dev/null +++ b/src/cpp/include/tasks/results.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2020-2025 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +class InferenceResult { +public: + std::map data; + cv::Size inputImageSize; +}; + +struct DetectedObject : public cv::Rect2f { + size_t labelID; + std::string label; + float confidence; + + friend std::ostream& operator<<(std::ostream& os, const DetectedObject& detection) { + return os << int(detection.x) << ", " << int(detection.y) << ", " << int(detection.x + detection.width) << ", " + << int(detection.y + detection.height) << ", " << detection.labelID << " (" << detection.label + << "): " << std::fixed << std::setprecision(3) << detection.confidence; + } +}; + +struct DetectionResult { + DetectionResult() {} + std::vector objects; + ov::Tensor saliency_map, feature_vector; // Contan "saliency_map" and "feature_vector" model outputs if such exist + + friend std::ostream& operator<<(std::ostream& os, const DetectionResult& prediction) { + for (const DetectedObject& obj : prediction.objects) { + os << obj << "; "; + } + try { + os << prediction.saliency_map.get_shape() << "; "; + } catch (ov::Exception&) { + os << "[0]; "; + } + try { + os << prediction.feature_vector.get_shape(); + } catch (ov::Exception&) { + os << "[0]"; + } + return os; + } + + explicit operator std::string() { + std::stringstream ss; + ss << *this; + return ss.str(); + } +}; + +struct Contour { + std::string label; + float probability; + std::vector shape; + + friend std::ostream& operator<<(std::ostream& os, const Contour& contour) { + return os << contour.label << ": " << std::fixed << std::setprecision(3) << contour.probability << ", " + << contour.shape.size(); + } +}; + +struct SemanticSegmentationResult { + SemanticSegmentationResult() {} + cv::Mat resultImage; + cv::Mat soft_prediction; + cv::Mat saliency_map; + ov::Tensor feature_vector; + + friend std::ostream& operator<<(std::ostream& os, const SemanticSegmentationResult& prediction) { + cv::Mat predicted_mask[] = {prediction.resultImage}; + int nimages = 1; + int* channels = nullptr; + cv::Mat mask; + cv::Mat outHist; + int dims = 1; + int histSize[] = {256}; + float range[] = {0, 256}; + const float* ranges[] = {range}; + cv::calcHist(predicted_mask, nimages, channels, mask, outHist, dims, histSize, ranges); + + os << std::fixed << std::setprecision(3); + for (int i = 0; i < range[1]; ++i) { + const float count = outHist.at(i); + if (count > 0) { + os << i << ": " << count / prediction.resultImage.total() << ", "; + } + } + os << '['; + for (int i = 0; i < prediction.soft_prediction.dims; ++i) { + os << prediction.soft_prediction.size[i] << ','; + } + os << prediction.soft_prediction.channels() << "], ["; + if (prediction.saliency_map.data) { + for (int i = 0; i < prediction.saliency_map.dims; ++i) { + os << prediction.saliency_map.size[i] << ','; + } + os << prediction.saliency_map.channels() << "], "; + } else { + os << "0], "; + } + try { + os << prediction.feature_vector.get_shape(); + } catch (ov::Exception&) { + os << "[0]"; + } + return os; + } + explicit operator std::string() { + std::stringstream ss; + ss << *this; + return ss.str(); + } +}; + +struct SegmentedObject : DetectedObject { + cv::Mat mask; + + friend std::ostream& operator<<(std::ostream& os, const SegmentedObject& prediction) { + return os << static_cast(prediction) << ", " << cv::countNonZero(prediction.mask > 0.5); + } +}; + +struct SegmentedObjectWithRects : SegmentedObject { + cv::RotatedRect rotated_rect; + + SegmentedObjectWithRects(const SegmentedObject& segmented_object) : SegmentedObject(segmented_object) {} + + friend std::ostream& operator<<(std::ostream& os, const SegmentedObjectWithRects& prediction) { + os << static_cast(prediction) << std::fixed << std::setprecision(3); + auto rect = prediction.rotated_rect; + os << ", RotatedRect: " << rect.center.x << ' ' << rect.center.y << ' ' << rect.size.width << ' ' + << rect.size.height << ' ' << rect.angle; + return os; + } +}; + +struct InstanceSegmentationResult { + std::vector segmentedObjects; + std::vector> saliency_map; + ov::Tensor feature_vector; +}; diff --git a/src/cpp/include/tasks/semantic_segmentation.h b/src/cpp/include/tasks/semantic_segmentation.h new file mode 100644 index 00000000..f2b97cd9 --- /dev/null +++ b/src/cpp/include/tasks/semantic_segmentation.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2020-2025 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include +#include + +#include "adapters/inference_adapter.h" +#include "tasks/results.h" +#include "utils/config.h" +#include "utils/preprocessing.h" +#include "utils/vision_pipeline.h" + +class SemanticSegmentation { +public: + VisionPipeline pipeline; + std::shared_ptr adapter; + SemanticSegmentation(std::shared_ptr adapter) : adapter(adapter) { + pipeline = VisionPipeline( + adapter, + [&](cv::Mat image) { + return preprocess(image); + }, + [&](InferenceResult result) { + return postprocess(result); + }); + + auto config = adapter->getModelConfig(); + labels = utils::get_from_any_maps("labels", config, {}, labels); + soft_threshold = utils::get_from_any_maps("soft_threshold", config, {}, soft_threshold); + blur_strength = utils::get_from_any_maps("blur_strength", config, {}, blur_strength); + } + + static cv::Size serialize(std::shared_ptr& ov_model); + static SemanticSegmentation load(const std::string& model_path); + + std::map preprocess(cv::Mat); + SemanticSegmentationResult postprocess(InferenceResult& infResult); + std::vector getContours(const SemanticSegmentationResult& result); + + SemanticSegmentationResult infer(cv::Mat image); + std::vector inferBatch(std::vector image); + +private: + cv::Mat create_hard_prediction_from_soft_prediction(cv::Mat, float threshold, int blur_strength); + + // from config + int blur_strength = -1; + float soft_threshold = -std::numeric_limits::infinity(); + bool return_soft_prediction = true; + + std::vector labels; + + std::string getLabelName(size_t labelID) { + return labelID < labels.size() ? labels[labelID] : std::string("Label #") + std::to_string(labelID); + } +}; diff --git a/src/cpp/utils/include/utils/async_infer_queue.hpp b/src/cpp/include/utils/async_infer_queue.h similarity index 68% rename from src/cpp/utils/include/utils/async_infer_queue.hpp rename to src/cpp/include/utils/async_infer_queue.h index 1dd38aa4..033a1f0e 100644 --- a/src/cpp/utils/include/utils/async_infer_queue.hpp +++ b/src/cpp/include/utils/async_infer_queue.h @@ -1,18 +1,7 @@ /* -// Copyright (C) 2024 Intel Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ + * Copyright (C) 2020-2025 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ #include #include diff --git a/src/cpp/include/utils/config.h b/src/cpp/include/utils/config.h new file mode 100644 index 00000000..6319a62c --- /dev/null +++ b/src/cpp/include/utils/config.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020-2025 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +namespace utils { +template +Type get_from_any_maps(const std::string& key, + const ov::AnyMap& top_priority, + const ov::AnyMap& mid_priority, + Type low_priority) { + auto topk_iter = top_priority.find(key); + if (topk_iter != top_priority.end()) { + return topk_iter->second.as(); + } + topk_iter = mid_priority.find(key); + if (topk_iter != mid_priority.end()) { + return topk_iter->second.as(); + } + return low_priority; +} + +inline bool model_has_embedded_processing(std::shared_ptr model) { + if (model->has_rt_info("model_info")) { + auto model_info = model->get_rt_info("model_info"); + auto iter = model_info.find("embedded_processing"); + if (iter != model_info.end()) { + return iter->second.as() == "YES"; + } + } + + return false; +} + +inline cv::Size get_input_shape_from_model_info(std::shared_ptr model) { + cv::Size result; + if (model->has_rt_info("model_info")) { + auto model_info = model->get_rt_info("model_info"); + { + auto iter = model_info.find("orig_height"); + if (iter != model_info.end()) { + result.height = iter->second.as(); + } + } + { + auto iter = model_info.find("orig_width"); + if (iter != model_info.end()) { + result.width = iter->second.as(); + } + } + } + + return result; +} +struct IntervalCondition { + using DimType = size_t; + using IndexType = size_t; + using ConditionChecker = std::function; + + template + constexpr IntervalCondition(IndexType i1, IndexType i2, Cond c) + : impl([=](IndexType i0, const ov::PartialShape& shape) { + return c(shape[i0].get_max_length(), shape[i1].get_max_length()) && + c(shape[i0].get_max_length(), shape[i2].get_max_length()); + }) {} + bool operator()(IndexType i0, const ov::PartialShape& shape) const { + return impl(i0, shape); + } + +private: + ConditionChecker impl; +}; + +template