Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not able to build on Windows 10 #11

Open
rajhlinux opened this issue Jan 1, 2025 · 5 comments
Open

Not able to build on Windows 10 #11

rajhlinux opened this issue Jan 1, 2025 · 5 comments

Comments

@rajhlinux
Copy link

rajhlinux commented Jan 1, 2025

Hello and happy new year.

Not able to build and get the following error:

Using:

Windows 10
ONNX 1.20.0 GPU (Windows Prebuilt Binaries)
OpenCV 5.0.0-Alpha
CMake version 3.30.2

Here is the error:
$ ./build.sh

Downloading onnxruntime ...
** Resuming transfer from byte position 9
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     9    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

gzip: stdin: not in gzip format
tar: Child returned status 1
tar: Error is not recoverable: exiting now
rm: cannot remove 'build': Device or resource busy
mkdir: cannot create directory ‘build’: File exists
Build Code ...
-- Building for: Visual Studio 17 2022
CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
  Compatibility with CMake < 3.5 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.


-- Selecting Windows SDK version 10.0.22621.0 to target Windows 10.0.19045.
-- The C compiler identification is MSVC 19.42.34435.0
-- The CXX compiler identification is MSVC 19.42.34435.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: F:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.42.34433/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: F:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.42.34433/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- ONNXRUNTIME_DIR: F:/AI_Componets/ONNX/windows_bin/onnxruntime-win-x64-gpu-1.20.1
-- OpenCV_DIR: F:/AI_Componets/OpenCV/build/install
-- OpenCV ARCH: x64
-- OpenCV RUNTIME: vc17
-- OpenCV STATIC: OFF
CMake Warning (dev) at F:/AI_Componets/OpenCV/build/install/x64/vc17/lib/OpenCVConfig.cmake:86 (find_package):
  Policy CMP0146 is not set: The FindCUDA module is removed.  Run "cmake
  --help-policy CMP0146" for policy details.  Use the cmake_policy command to
  set the policy and suppress this warning.

Call Stack (most recent call first):
  F:/AI_Componets/OpenCV/build/install/x64/vc17/lib/OpenCVConfig.cmake:108 (find_host_package)
  F:/AI_Componets/OpenCV/build/install/OpenCVConfig.cmake:174 (include)
  CMakeLists.txt:12 (find_package)
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Found CUDA: F:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v12.6 (found suitable exact version "12.6")
-- Found OpenCV: F:/AI_Componets/OpenCV/build/install (found version "5.0.0")
-- Found OpenCV 5.0.0 in F:/AI_Componets/OpenCV/build/install/x64/vc17/lib
-- You might need to add F:\AI_Componets\OpenCV\build\install\x64\vc17\bin to your PATH to be able to run your applications.
-- We are building on Windows!
-- Configuring done (18.3s)
-- Generating done (0.2s)
-- Build files have been written to: F:/AI_Componets/YOLOs-CPP/build
MSBuild version 17.12.12+1cce77968 for .NET Framework

  1>Checking Build System
  Building Custom Rule F:/AI_Componets/YOLOs-CPP/CMakeLists.txt
  camera_inference.cpp
camera_inference.obj : error LNK2019: unresolved external symbol "public: void __cdecl cv::Mat::convertTo(class cv::debug_build_guard::_OutputArray const &,int,double,double)const " (?convertTo@Mat@cv@@QEBAXAEBV_OutputArray@debug_build_guard@2@HNN@Z) referenced in function "private: class cv::Mat __cdecl YOLO7Detector::preprocess(class cv::Mat const &,float * &,class std::vector<__int64,class std::allocator<__int64> > &)" (?preprocess@YOLO7Detector@@AEAA?AVMat@cv@@AEBV23@AEAPEAMAEAV?$vector@_JV?$allocator@_J@std@@@std@@@Z) [F:\AI_Componets\YOLOs-CPP\build\camera_inference.vcxproj]
camera_inference.obj : error LNK2019: unresolved external symbol "void __cdecl cv::copyMakeBorder(class cv::debug_build_guard::_InputArray const &,class cv::debug_build_guard::_OutputArray const &,int,int,int,int,int,class cv::Scalar_<double> const &)" (?copyMakeBorder@cv@@YAXAEBV_InputArray@debug_build_guard@1@AEBV_OutputArray@31@HHHHHAEBV?$Scalar_@N@1@@Z) referenced in function "void __cdecl utils::letterBox(class cv::Mat const &,class cv::Mat &,class cv::Size_<int> const &,class cv::Scalar_<double> const &,bool,bool,bool,int)" (?letterBox@utils@@YAXAEBVMat@cv@@AEAV23@AEBV?$Size_@H@3@AEBV?$Scalar_@N@3@_N44H@Z) [F:\AI_Componets\YOLOs-CPP\build\camera_inference.vcxproj]
camera_inference.obj : error LNK2019: unresolved external symbol "void __cdecl cv::addWeighted(class cv::debug_build_guard::_InputArray const &,double,class cv::debug_build_guard::_InputArray const &,double,double,class cv::debug_build_guard::_OutputArray const &,int)" (?addWeighted@cv@@YAXAEBV_InputArray@debug_build_guard@1@N0NNAEBV_OutputArray@31@H@Z) referenced in function "void __cdecl utils::drawBoundingBoxMask(class cv::Mat &,class std::vector<struct Detection,class std::allocator<struct Detection> > const &,class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > const &,class std::vector<class cv::Scalar_<double>,class std::allocator<class cv::Scalar_<double> > > const &,float)" (?drawBoundingBoxMask@utils@@YAXAEAVMat@cv@@AEBV?$vector@UDetection@@V?$allocator@UDetection@@@std@@@std@@AEBV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@5@AEBV?$vector@V?$Scalar_@N@cv@@V?$allocator@V?$Scalar_@N@cv@@@std@@@5@M@Z) [F:\AI_Componets\YOLOs-CPP\build\camera_inference.vcxproj]
camera_inference.obj : error LNK2019: unresolved external symbol "void __cdecl cv::split(class cv::debug_build_guard::_InputArray const &,class cv::debug_build_guard::_OutputArray const &)" (?split@cv@@YAXAEBV_InputArray@debug_build_guard@1@AEBV_OutputArray@31@@Z) referenced in function "private: class cv::Mat __cdecl YOLO7Detector::preprocess(class cv::Mat const &,float * &,class std::vector<__int64,class std::allocator<__int64> > &)" (?preprocess@YOLO7Detector@@AEAA?AVMat@cv@@AEBV23@AEAPEAMAEAV?$vector@_JV?$allocator@_J@std@@@std@@@Z) [F:\AI_Componets\YOLOs-CPP\build\camera_inference.vcxproj]
camera_inference.obj : error LNK2019: unresolved external symbol "void __cdecl cv::imshow(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class cv::debug_build_guard::_InputArray const &)" (?imshow@cv@@YAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEBV_InputArray@debug_build_guard@1@@Z) referenced in function "public: __cdecl <lambda_7129d35bac13784514d9481ddb7360c7>::operator()(void)const " (??R<lambda_7129d35bac13784514d9481ddb7360c7>@@QEBA@XZ) [F:\AI_Componets\YOLOs-CPP\build\camera_inference.vcxproj]
camera_inference.obj : error LNK2019: unresolved external symbol "void __cdecl cv::resize(class cv::debug_build_guard::_InputArray const &,class cv::debug_build_guard::_OutputArray const &,class cv::Size_<int>,double,double,int)" (?resize@cv@@YAXAEBV_InputArray@debug_build_guard@1@AEBV_OutputArray@31@V?$Size_@H@1@NNH@Z) referenced in function "void __cdecl utils::letterBox(class cv::Mat const &,class cv::Mat &,class cv::Size_<int> const &,class cv::Scalar_<double> const &,bool,bool,bool,int)" (?letterBox@utils@@YAXAEBVMat@cv@@AEAV23@AEBV?$Size_@H@3@AEBV?$Scalar_@N@3@_N44H@Z) [F:\AI_Componets\YOLOs-CPP\build\camera_inference.vcxproj]
camera_inference.obj : error LNK2019: unresolved external symbol "void __cdecl cv::rectangle(class cv::debug_build_guard::_InputOutputArray const &,class cv::Point_<int>,class cv::Point_<int>,class cv::Scalar_<double> const &,int,int,int)" (?rectangle@cv@@YAXAEBV_InputOutputArray@debug_build_guard@1@V?$Point_@H@1@1AEBV?$Scalar_@N@1@HHH@Z) referenced in function "void __cdecl utils::drawBoundingBoxMask(class cv::Mat &,class std::vector<struct Detection,class std::allocator<struct Detection> > const &,class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > const &,class std::vector<class cv::Scalar_<double>,class std::allocator<class cv::Scalar_<double> > > const &,float)" (?drawBoundingBoxMask@utils@@YAXAEAVMat@cv@@AEBV?$vector@UDetection@@V?$allocator@UDetection@@@std@@@std@@AEBV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@5@AEBV?$vector@V?$Scalar_@N@cv@@V?$allocator@V?$Scalar_@N@cv@@@std@@@5@M@Z) [F:\AI_Componets\YOLOs-CPP\build\camera_inference.vcxproj]
camera_inference.obj : error LNK2019: unresolved external symbol "void __cdecl cv::putText(class cv::debug_build_guard::_InputOutputArray const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class cv::Point_<int>,int,double,class cv::Scalar_<double>,int,int,bool)" (?putText@cv@@YAXAEBV_InputOutputArray@debug_build_guard@1@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$Point_@H@1@HNV?$Scalar_@N@1@HH_N@Z) referenced in function "void __cdecl utils::drawBoundingBoxMask(class cv::Mat &,class std::vector<struct Detection,class std::allocator<struct Detection> > const &,class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > const &,class std::vector<class cv::Scalar_<double>,class std::allocator<class cv::Scalar_<double> > > const &,float)" (?drawBoundingBoxMask@utils@@YAXAEAVMat@cv@@AEBV?$vector@UDetection@@V?$allocator@UDetection@@@std@@@std@@AEBV?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@5@AEBV?$vector@V?$Scalar_@N@cv@@V?$allocator@V?$Scalar_@N@cv@@@std@@@5@M@Z) [F:\AI_Componets\YOLOs-CPP\build\camera_inference.vcxproj]
F:\AI_Componets\YOLOs-CPP\build\Debug\camera_inference.exe : fatal error LNK1120: 8 unresolved externals [F:\AI_Componets\YOLOs-CPP\build\camera_inference.vcxproj]

Here is the build.sh file:

#!/bin/bash
CURRENT_DIR=$(cd "$(dirname "$0")"; pwd)

# ONNXRUNTIME_VERSION="1.16.3" 
ONNXRUNTIME_VERSION="1.20.0" 

ONNXRUNTIME_GPU=1

# Platform
platform="$(uname -s)"
case "$platform" in
    Darwin*)
        ONNXRUNTIME_PLATFORM="osx"
        ONNXRUNTIME_GPU=0
        ;;
    Linux*)
        ONNXRUNTIME_PLATFORM="linux"
        ;;
    MINGW32_NT*|MINGW64_NT*)
        ONNXRUNTIME_PLATFORM="win"
        ;;
    *)
        echo "Unsupported platform: $platform"
        exit 1
        ;;
esac

# Architecture
architecture="$(uname -m)"
case "$architecture" in
    x86_64)
        ONNXRUNTIME_ARCH="x64"
        ;;
    armv7l)
        ONNXRUNTIME_ARCH="arm"
        ;;
    aarch64|arm64)
        ONNXRUNTIME_ARCH="arm64"
        ;;
    *)
        echo "Unsupported architecture: $architecture"
        exit 1
        ;;
esac

# GPU
if [ ${ONNXRUNTIME_GPU} == 1 ]; then
    ONNXRUNTIME_PATH="onnxruntime-${ONNXRUNTIME_PLATFORM}-${ONNXRUNTIME_ARCH}-gpu-${ONNXRUNTIME_VERSION}"
else
    ONNXRUNTIME_PATH="onnxruntime-${ONNXRUNTIME_PLATFORM}-${ONNXRUNTIME_ARCH}-${ONNXRUNTIME_VERSION}"
fi

# Download onnxruntime
if [ ! -d "${CURRENT_DIR}/${ONNXRUNTIME_PATH}" ]; then
    echo "Downloading onnxruntime ..." 
    curl -L -O -C - https://github.com/microsoft/onnxruntime/releases/download/v${ONNXRUNTIME_VERSION}/${ONNXRUNTIME_PATH}.tgz
    tar -zxvf ${ONNXRUNTIME_PATH}.tgz
fi

ONNXRUNTIME_DIR="${CURRENT_DIR}/${ONNXRUNTIME_PATH}"



# Remove previous build directory
if [ -d "${CURRENT_DIR}/build" ]; then
    rm -rf build
fi

mkdir build 
cd build
echo "Build Code ..."
# cmake .. -D ONNXRUNTIME_DIR="${ONNXRUNTIME_DIR}" -DCMAKE_BUILD_TYPE=Release

# cmake .. -D ONNXRUNTIME_DIR="${ONNXRUNTIME_DIR}" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS_DEBUG="-pg" 

# CMake configuration with optimization flags
cmake .. -D ONNXRUNTIME_DIR="${ONNXRUNTIME_DIR}" -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS_RELEASE="-O3 -march=native"


cmake --build .

Here is the CMakeList.txt file:

cmake_minimum_required(VERSION 3.0.0)
project(yolo_ort)

# Set paths for ONNX Runtime and OpenCV
set(ONNXRUNTIME_DIR "F:/AI_Componets/ONNX/windows_bin/onnxruntime-win-x64-gpu-1.20.1")
set(OpenCV_DIR "F:/AI_Componets/OpenCV/build/install")

message(STATUS "ONNXRUNTIME_DIR: ${ONNXRUNTIME_DIR}")
message(STATUS "OpenCV_DIR: ${OpenCV_DIR}")

# Find OpenCV
find_package(OpenCV REQUIRED PATHS ${OpenCV_DIR} NO_DEFAULT_PATH)

# Add include directories
include_directories("include/")
include_directories("${ONNXRUNTIME_DIR}/include")
include_directories("${OpenCV_DIR}/include")

# Add executables
add_executable(camera_inference src/camera_inference.cpp)

# Set C++ standard
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Link ONNX Runtime and OpenCV libraries for all executables
if(WIN32)
    message(STATUS "We are building on Windows!")
    target_link_libraries(camera_inference "${ONNXRUNTIME_DIR}/lib/onnxruntime.lib" ${OpenCV_LIBS})
endif()

Thanks for any help.

@Geekgineer
Copy link
Owner

Hi happy new year,

OpenCV 5.0.0-Alpha is still under development, and some functions may not work probably. Consider switching to a stable OpenCV version like 4.5.x.

In any case, try below this custom build and make sure to update the paths to the local OpenCV installation

#!/bin/bash
CURRENT_DIR=$(cd "$(dirname "$0")"; pwd)

# ONNXRUNTIME_VERSION="1.16.3" 
ONNXRUNTIME_VERSION="1.19.2" 

ONNXRUNTIME_GPU=0

# Platform check (force Windows-only)
platform="$(uname -s)"
if [[ ! "$platform" =~ ^MINGW32_NT|MINGW64_NT ]]; then
    echo "This script is designed for Windows builds only."
    exit 1
fi

# Architecture
architecture="$(uname -m)"
case "$architecture" in
    x86_64)
        ONNXRUNTIME_ARCH="x64"
        ;;
    *)
        echo "Unsupported architecture: $architecture"
        exit 1
        ;;
esac

# GPU
if [ ${ONNXRUNTIME_GPU} == 1 ]; then
    ONNXRUNTIME_PATH="onnxruntime-win-${ONNXRUNTIME_ARCH}-gpu-${ONNXRUNTIME_VERSION}"
else
    ONNXRUNTIME_PATH="onnxruntime-win-${ONNXRUNTIME_ARCH}-${ONNXRUNTIME_VERSION}"
fi

# Download onnxruntime
if [ ! -d "${CURRENT_DIR}/${ONNXRUNTIME_PATH}" ]; then
    echo "Downloading onnxruntime ..." 
    curl -L -O -C - https://github.com/microsoft/onnxruntime/releases/download/v${ONNXRUNTIME_VERSION}/${ONNXRUNTIME_PATH}.zip
    unzip ${ONNXRUNTIME_PATH}.zip -d "${CURRENT_DIR}"
fi

ONNXRUNTIME_DIR="${CURRENT_DIR}/${ONNXRUNTIME_PATH}"

# Remove previous build directory
if [ -d "${CURRENT_DIR}/build" ]; then
    rm -rf build
fi

mkdir build 
cd build

echo "Build Code ..."

# Path to local OpenCV installation
OPENCV_DIR="C:/path/to/opencv"  # Update with your OpenCV installation path
OPENCV_INCLUDE_DIR="${OPENCV_DIR}/include"
OPENCV_LIB_DIR="${OPENCV_DIR}/x64/vc15/lib"  # Adjust for your compiler

# Generate build files
cmake .. \
    -D ONNXRUNTIME_DIR="${ONNXRUNTIME_DIR}" \
    -D CMAKE_BUILD_TYPE=Release \
    -D OpenCV_DIR="${OPENCV_DIR}" \
    -D OpenCV_INCLUDE_DIR="${OPENCV_INCLUDE_DIR}" \
    -D OpenCV_LIB_DIR="${OPENCV_LIB_DIR}" \
    -D CMAKE_CXX_FLAGS_RELEASE="-O3 -march=native"

# Build the project
cmake --build . --config Release

Let me know if this works!

regards,

@rajhlinux
Copy link
Author

Hello Geekgineer,

Happy new year and thank you for the response.

I will implement and see how it goes.

@rajhlinux
Copy link
Author

I have tried the update you've provided and it seemed to build the project, however running the executable throws an error:

MINGW64 /f/AI_Componets/AI_Projects/YOLOs-CPP (main)
$ ./build_2.sh
Downloading onnxruntime ...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  265M  100  265M    0     0  12.8M      0  0:00:20  0:00:20 --:--:-- 13.6M
Archive:  onnxruntime-win-x64-gpu-1.19.2.zip
   creating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/CodeSignSummary-8f58bf53-cc5a-4fc6-b116-fefdad878754.md
 extracting: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/GIT_COMMIT_ID
   creating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/
   creating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/core/
   creating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/core/providers/
   creating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/core/providers/cuda/
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/core/providers/cuda/cuda_context.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/core/providers/cuda/cuda_resource.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/core/providers/custom_op_context.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/core/providers/resource.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/cpu_provider_factory.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/onnxruntime_cxx_api.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/onnxruntime_cxx_inline.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/onnxruntime_c_api.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/onnxruntime_float16.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/onnxruntime_lite_custom_op.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/onnxruntime_run_options_config_keys.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/onnxruntime_session_options_config_keys.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/onnxruntime_training_cxx_api.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/onnxruntime_training_cxx_inline.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/onnxruntime_training_c_api.h
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/include/provider_options.h
   creating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/onnxruntime.dll
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/onnxruntime.lib
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/onnxruntime.pdb
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/onnxruntime_providers_cuda.dll
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/onnxruntime_providers_cuda.lib
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/onnxruntime_providers_cuda.pdb
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/onnxruntime_providers_shared.dll
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/onnxruntime_providers_shared.lib
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/onnxruntime_providers_shared.pdb
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/onnxruntime_providers_tensorrt.dll
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/onnxruntime_providers_tensorrt.lib
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/lib/onnxruntime_providers_tensorrt.pdb
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/LICENSE
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/Privacy.md
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/README.md
  inflating: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/ThirdPartyNotices.txt
 extracting: /f/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2/VERSION_NUMBER
Build Code ...
-- Building for: Visual Studio 17 2022
CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
  Compatibility with CMake < 3.10 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value.  Or, use the <min>...<max> syntax
  to tell CMake that the project requires at least <min> but has been updated
  to work with policies introduced by <max> or earlier.


-- Selecting Windows SDK version 10.0.22621.0 to target Windows 10.0.19045.
-- The C compiler identification is MSVC 19.42.34435.0
-- The CXX compiler identification is MSVC 19.42.34435.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: F:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.42.34433/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: F:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.42.34433/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- ONNXRUNTIME_DIR: F:/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2
-- OpenCV ARCH: x64
-- OpenCV RUNTIME: vc17
-- OpenCV STATIC: OFF
CMake Warning (dev) at F:/AI_Componets/OpenCV/build/install/x64/vc17/lib/OpenCVConfig.cmake:86 (find_package):
  Policy CMP0146 is not set: The FindCUDA module is removed.  Run "cmake
  --help-policy CMP0146" for policy details.  Use the cmake_policy command to
  set the policy and suppress this warning.

Call Stack (most recent call first):
  F:/AI_Componets/OpenCV/build/install/x64/vc17/lib/OpenCVConfig.cmake:108 (find_host_package)
  F:/AI_Componets/OpenCV/build/install/OpenCVConfig.cmake:174 (include)
  CMakeLists.txt:7 (find_package)
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Found CUDA: F:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v12.6 (found suitable exact version "12.6")
-- Found OpenCV: F:/AI_Componets/OpenCV/build/install (found version "5.0.0")
-- Found OpenCV 5.0.0 in F:/AI_Componets/OpenCV/build/install/x64/vc17/lib
-- You might need to add F:\AI_Componets\OpenCV\build\install\x64\vc17\bin to your PATH to be able to run your applications.
-- We are building on Windows!
-- Configuring done (10.7s)
-- Generating done (1.3s)
CMake Warning:
  Manually-specified variables were not used by the project:

    OpenCV_INCLUDE_DIR
    OpenCV_LIB_DIR


-- Build files have been written to: F:/AI_Componets/AI_Projects/YOLOs-CPP/build
MSBuild version 17.12.12+1cce77968 for .NET Framework

  1>Checking Build System
  Building Custom Rule F:/AI_Componets/AI_Projects/YOLOs-CPP/CMakeLists.txt
cl : command line  warning D9002: ignoring unknown option '-O3' [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\camera_inference.vcxproj]
cl : command line  warning D9002: ignoring unknown option '-march=native' [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\camera_inference.vcxproj]
  camera_inference.cpp
  camera_inference.vcxproj -> F:\AI_Componets\AI_Projects\YOLOs-CPP\build\Release\camera_inference.exe
  Building Custom Rule F:/AI_Componets/AI_Projects/YOLOs-CPP/CMakeLists.txt
cl : command line  warning D9002: ignoring unknown option '-O3' [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
cl : command line  warning D9002: ignoring unknown option '-march=native' [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  image_inference.cpp
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(357,47): warning C4244: '=': conversion from 'int' to '_Ty', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(357,47): warning C4244:         with [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(357,47): warning C4244:         [ [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(357,47): warning C4244:             _Ty=float [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(357,47): warning C4244:         ] [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(374,44): warning C4244: 'initializing': conversion from 'const int' to 'float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(374,32): warning C4244: 'initializing': conversion from 'const int' to 'const float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(375,44): warning C4244: 'initializing': conversion from 'const int' to 'float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(375,32): warning C4244: 'initializing': conversion from 'const int' to 'const float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(376,47): warning C4244: 'initializing': conversion from 'int' to 'float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(376,32): warning C4244: 'initializing': conversion from 'int' to 'const float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(377,47): warning C4244: 'initializing': conversion from 'int' to 'float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(377,32): warning C4244: 'initializing': conversion from 'int' to 'const float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(852,47): warning C4244: 'argument': conversion from 'float' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(852,40): warning C4244: 'argument': conversion from 'float' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(852,35): warning C4244: 'argument': conversion from 'float' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(852,29): warning C4244: 'argument': conversion from 'float' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(859,38): warning C4244: '=': conversion from 'double' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(860,38): warning C4244: '=': conversion from 'double' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(861,42): warning C4244: '=': conversion from 'double' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(862,43): warning C4244: '=': conversion from 'double' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

  image_inference.vcxproj -> F:\AI_Componets\AI_Projects\YOLOs-CPP\build\Release\image_inference.exe
  Building Custom Rule F:/AI_Componets/AI_Projects/YOLOs-CPP/CMakeLists.txt
cl : command line  warning D9002: ignoring unknown option '-O3' [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
cl : command line  warning D9002: ignoring unknown option '-march=native' [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  video_inference.cpp
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(357,47): warning C4244: '=': conversion from 'int' to '_Ty', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(357,47): warning C4244:         with [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(357,47): warning C4244:         [ [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(357,47): warning C4244:             _Ty=float [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(357,47): warning C4244:         ] [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(374,44): warning C4244: 'initializing': conversion from 'const int' to 'float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(374,32): warning C4244: 'initializing': conversion from 'const int' to 'const float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(375,44): warning C4244: 'initializing': conversion from 'const int' to 'float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(375,32): warning C4244: 'initializing': conversion from 'const int' to 'const float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(376,47): warning C4244: 'initializing': conversion from 'int' to 'float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(376,32): warning C4244: 'initializing': conversion from 'int' to 'const float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(377,47): warning C4244: 'initializing': conversion from 'int' to 'float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(377,32): warning C4244: 'initializing': conversion from 'int' to 'const float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(852,47): warning C4244: 'argument': conversion from 'float' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(852,40): warning C4244: 'argument': conversion from 'float' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(852,35): warning C4244: 'argument': conversion from 'float' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(852,29): warning C4244: 'argument': conversion from 'float' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(859,38): warning C4244: '=': conversion from 'double' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(860,38): warning C4244: '=': conversion from 'double' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(861,42): warning C4244: '=': conversion from 'double' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO11.hpp(862,43): warning C4244: '=': conversion from 'double' to 'int', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\video_inference.vcxproj]
  (compiling source file '../src/video_inference.cpp')

  video_inference.vcxproj -> F:\AI_Componets\AI_Projects\YOLOs-CPP\build\Release\video_inference.exe
  Building Custom Rule F:/AI_Componets/AI_Projects/YOLOs-CPP/CMakeLists.txt

Here is the error when running the executable:

YOLOs-CPP

I tried building OpenCV 4.5.5, but it gave many errors because it doesn't support Visual Studio 2022 compilers, I'm not willing to use older compilers. So, I tried OpenCV 4.9.0, which also gave same errors, but it was able to build but threw errors at the end. It would be nice if you could implement your project without OpenCV since it's not friendly for modern CUDA version and building opencv can become a nightmare.

I tried Ultralytics OpenCV with the ONNX example, and it works only in CPU mode. OpenCV 5's graph engine is too new and doesn't work with Ultralytics examples in GPU mode.

I will now try Ultralytics ONNX runtime examples, hopefully I can get the CUDA GPU working on this.

Also, I am curious: how exactly is the YOLOv8 model directly interfaced via C++? Are there any documentation or explanations on how to interact with the YOLOv8 model via C++? I spent days trying to find information on this and got nothing but cheap explanations done in python. It's unfortunate that the Computer Vision industry has lost skilled talent in teaching things the proper way using C/C++.

@rajhlinux
Copy link
Author

So that run time error 0xc00007b was related to “onnxruntime.dll” in the system32, I just had to delete/rename it.

The image_inference.exe executable runs now, throws no errors, however nothing is displayed.
All it shows is Inference device: GPU, then the program closes.

YOLOs-CPP_2

@rajhlinux
Copy link
Author

rajhlinux commented Jan 11, 2025

When using Yolov8:

I get the following:

F:\AI_Componets\AI_Projects\YOLOs-CPP\build\Release>F:\AI_Componets\AI_Projects\YOLOs-CPP\build\Release\image_inference.exe
Inference device: GPU
2025-01-11 02:35:06.7329123 [W:onnxruntime:, transformer_memcpy.cc:74 onnxruntime::MemcpyTransformer::ApplyImpl] 132 Memcpy nodes are added to the graph main_graph for CUDAExecutionProvider. It might have negative impact on performance (including unable to run CUDA graph). Set session_options.log_severity_level=1 to see the detail logs before this message.
2025-01-11 02:35:06.7565882 [W:onnxruntime:, session_state.cc:1168 onnxruntime::VerifyEachNodeIsAssignedToAnEp] Some nodes were not assigned to the preferred execution providers which may or may not have an negative impact on performance. e.g. ORT explicitly assigns shape related ops to CPU to improve perf.
2025-01-11 02:35:06.7692485 [W:onnxruntime:, session_state.cc:1170 onnxruntime::VerifyEachNodeIsAssignedToAnEp] Rerunning with verbose output on a non-minimal build will show node assignments.
Model loaded successfully with 1 input nodes and 1 output nodes.
Detection completed in: 507 ms

image

To make Yolov8 model work I had to remove this from line code 636 in YOLO8.hpp:

    /**
     * @brief A robust implementation of a clamp function.
     *        Restricts a value to lie within a specified range [low, high].
     *
     * @tparam T The type of the value to clamp. Should be an arithmetic type (int, float, etc.).
     * @param value The value to clamp.
     * @param low The lower bound of the range.
     * @param high The upper bound of the range.
     * @return const T& The clamped value, constrained to the range [low, high].
     *
     * @note If low > high, the function swaps the bounds automatically to ensure valid behavior.
     */
    template <typename T>
    typename std::enable_if<std::is_arithmetic<T>::value, T>::type
    inline clamp(const T &value, const T &low, const T &high)
    {
        // Ensure the range [low, high] is valid; swap if necessary
        T validLow = low < high ? low : high;
        T validHigh = low < high ? high : low;

        // Clamp the value to the range [validLow, validHigh]
        if (value < validLow)
            return validLow;
        if (value > validHigh)
            return validHigh;
        return value;
    }

If I do not remove this, I get the following error (all the way at the bottom) during build:

$ ./build_2.sh
Build Code ...
-- Building for: Visual Studio 17 2022
CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
  Compatibility with CMake < 3.10 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value.  Or, use the <min>...<max> syntax
  to tell CMake that the project requires at least <min> but has been updated
  to work with policies introduced by <max> or earlier.


-- Selecting Windows SDK version 10.0.22621.0 to target Windows 10.0.19045.
-- The C compiler identification is MSVC 19.42.34435.0
-- The CXX compiler identification is MSVC 19.42.34435.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: F:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.42.34433/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: F:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.42.34433/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- ONNXRUNTIME_DIR: F:/AI_Componets/AI_Projects/YOLOs-CPP/onnxruntime-win-x64-gpu-1.19.2
-- OpenCV ARCH: x64
-- OpenCV RUNTIME: vc17
-- OpenCV STATIC: OFF
CMake Warning (dev) at F:/AI_Componets/OpenCV/build/install/x64/vc17/lib/OpenCVConfig.cmake:86 (find_package):
  Policy CMP0146 is not set: The FindCUDA module is removed.  Run "cmake
  --help-policy CMP0146" for policy details.  Use the cmake_policy command to
  set the policy and suppress this warning.

Call Stack (most recent call first):
  F:/AI_Componets/OpenCV/build/install/x64/vc17/lib/OpenCVConfig.cmake:108 (find_host_package)
  F:/AI_Componets/OpenCV/build/install/OpenCVConfig.cmake:174 (include)
  CMakeLists.txt:7 (find_package)
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Found CUDA: F:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v12.6 (found suitable exact version "12.6")
-- Found OpenCV: F:/AI_Componets/OpenCV/build/install (found version "5.0.0")
-- Found OpenCV 5.0.0 in F:/AI_Componets/OpenCV/build/install/x64/vc17/lib
-- You might need to add F:\AI_Componets\OpenCV\build\install\x64\vc17\bin to your PATH to be able to run your applications.
-- We are building on Windows!
-- Configuring done (7.7s)
-- Generating done (0.4s)
CMake Warning:
  Manually-specified variables were not used by the project:

    OpenCV_INCLUDE_DIR
    OpenCV_LIB_DIR


-- Build files have been written to: F:/AI_Componets/AI_Projects/YOLOs-CPP/build
MSBuild version 17.12.12+1cce77968 for .NET Framework

  1>Checking Build System
  Building Custom Rule F:/AI_Componets/AI_Projects/YOLOs-CPP/CMakeLists.txt
cl : command line  warning D9002: ignoring unknown option '-O3' [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\camera_inference.vcxproj]
cl : command line  warning D9002: ignoring unknown option '-march=native' [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\camera_inference.vcxproj]
  camera_inference.cpp
  camera_inference.vcxproj -> F:\AI_Componets\AI_Projects\YOLOs-CPP\build\Release\camera_inference.exe
  Building Custom Rule F:/AI_Componets/AI_Projects/YOLOs-CPP/CMakeLists.txt
cl : command line  warning D9002: ignoring unknown option '-O3' [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
cl : command line  warning D9002: ignoring unknown option '-march=native' [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  image_inference.cpp
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(383,47): warning C4244: '=': conversion from 'int' to '_Ty', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(383,47): warning C4244:         with [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(383,47): warning C4244:         [ [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(383,47): warning C4244:             _Ty=float [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(383,47): warning C4244:         ] [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(402,44): warning C4244: 'initializing': conversion from 'const int' to 'float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(402,32): warning C4244: 'initializing': conversion from 'const int' to 'const float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(403,44): warning C4244: 'initializing': conversion from 'const int' to 'float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(403,32): warning C4244: 'initializing': conversion from 'const int' to 'const float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(404,47): warning C4244: 'initializing': conversion from 'int' to 'float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(404,32): warning C4244: 'initializing': conversion from 'int' to 'const float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(405,47): warning C4244: 'initializing': conversion from 'int' to 'float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(405,32): warning C4244: 'initializing': conversion from 'int' to 'const float', possible loss of data [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')

F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(636,12): error C2995: 'std::enable_if<std::is_arithmetic<_Ty>::value,T>::type utils::clamp(const T &,const T &,const T &)': function template has already been defined [F:\AI_Componets\AI_Projects\YOLOs-CPP\build\image_inference.vcxproj]
  (compiling source file '../src/image_inference.cpp')
      F:\AI_Componets\AI_Projects\YOLOs-CPP\include\YOLO8.hpp(113,12):
      see declaration of 'utils::clamp'

Also I suggest to implement a try-catch implementation for yolov8.hpp, it helped me to resolve some issues. Here is the code if needed:

#pragma once

// ===================================
// Single YOLOv8 Detector Header File
// ===================================
//
// This header defines the YOLO8Detector class for performing object detection using the YOLOv8 model.
// It includes necessary libraries, utility structures, and helper functions to facilitate model inference
// and result postprocessing.
//
// Author: Abdalrahman M. Amer, www.linkedin.com/in/abdalrahman-m-amer
// Date: 29.09.2024
//
// ================================

/**
 * @file YOLO8Detector.hpp
 * @brief Header file for the YOLO8Detector class, responsible for object detection
 *        using the YOLOv8 model with optimized performance for minimal latency.
 */

// Include necessary ONNX Runtime and OpenCV headers
#include <onnxruntime_cxx_api.h>
#include <opencv2/opencv.hpp>

#include <algorithm>
#include <fstream>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>
#include <memory>
#include <chrono>
#include <random>
#include <unordered_map>
#include <thread>

// Include debug and custom ScopedTimer tools for performance measurement
#include "tools/Debug.hpp"
#include "tools/ScopedTimer.hpp"

/**
 * @brief Confidence threshold for filtering detections.
 */
const float CONFIDENCE_THRESHOLD = 0.4f;

/**
 * @brief  IoU threshold for filtering detections.
 */
const float IOU_THRESHOLD = 0.45f;

/**
 * @brief Struct to represent a bounding box.
 */

// Struct to represent a bounding box
struct BoundingBox
{
    int x;
    int y;
    int width;
    int height;

    BoundingBox() : x(0), y(0), width(0), height(0) {}
    BoundingBox(int x_, int y_, int width_, int height_)
        : x(x_), y(y_), width(width_), height(height_) {}

    float area() const { return static_cast<float>(width * height); }

    BoundingBox intersect(const BoundingBox &other) const
    {
        int xStart = std::max(x, other.x);
        int yStart = std::max(y, other.y);
        int xEnd = std::min(x + width, other.x + other.width);
        int yEnd = std::min(y + height, other.y + other.height);
        int intersectWidth = std::max(0, xEnd - xStart);
        int intersectHeight = std::max(0, yEnd - yStart);
        return BoundingBox{xStart, yStart, intersectWidth, intersectHeight};
    }
};

/**
 * @brief Struct to represent a detection.
 */
struct Detection
{
    BoundingBox box;
    float conf{};
    int classId{};
};

/**
 * @namespace utils
 * @brief Namespace containing utility functions for the YOLO8Detector.
 */
namespace utils
{

    /**
     * @brief A robust implementation of a clamp function.
     *        Restricts a value to lie within a specified range [low, high].
     *
     * @tparam T The type of the value to clamp. Should be an arithmetic type (int, float, etc.).
     * @param value The value to clamp.
     * @param low The lower bound of the range.
     * @param high The upper bound of the range.
     * @return const T& The clamped value, constrained to the range [low, high].
     *
     * @note If low > high, the function swaps the bounds automatically to ensure valid behavior.
     */
    template <typename T>
    typename std::enable_if<std::is_arithmetic<T>::value, T>::type
    inline clamp(const T &value, const T &low, const T &high)
    {
        // Ensure the range [low, high] is valid; swap if necessary
        T validLow = low < high ? low : high;
        T validHigh = low < high ? high : low;

        // Clamp the value to the range [validLow, validHigh]
        if (value < validLow)
            return validLow;
        if (value > validHigh)
            return validHigh;
        return value;
    }

    /**
     * @brief Loads class names from a given file path.
     *
     * @param path Path to the file containing class names.
     * @return std::vector<std::string> Vector of class names.
     */
    std::vector<std::string> getClassNames(const std::string &path)
    {
        std::vector<std::string> classNames;
        std::ifstream infile(path);

        if (infile)
        {
            std::string line;
            while (getline(infile, line))
            {
                // Remove carriage return if present (for Windows compatibility)
                if (!line.empty() && line.back() == '\r')
                    line.pop_back();
                classNames.emplace_back(line);
            }
        }
        else
        {
            std::cerr << "ERROR: Failed to access class name path: " << path << std::endl;
        }

        DEBUG_PRINT("Loaded " << classNames.size() << " class names from " + path);
        return classNames;
    }

    /**
     * @brief Computes the product of elements in a vector.
     *
     * @param vector Vector of integers.
     * @return size_t Product of all elements.
     */
    size_t vectorProduct(const std::vector<int64_t> &vector)
    {
        return std::accumulate(vector.begin(), vector.end(), 1ull, std::multiplies<size_t>());
    }

    /**
     * @brief Resizes an image with letterboxing to maintain aspect ratio.
     *
     * @param image Input image.
     * @param outImage Output resized and padded image.
     * @param newShape Desired output size.
     * @param color Padding color (default is gray).
     * @param auto_ Automatically adjust padding to be multiple of stride.
     * @param scaleFill Whether to scale to fill the new shape without keeping aspect ratio.
     * @param scaleUp Whether to allow scaling up of the image.
     * @param stride Stride size for padding alignment.
     */
    inline void letterBox(const cv::Mat &image, cv::Mat &outImage,
                          const cv::Size &newShape,
                          const cv::Scalar &color = cv::Scalar(114, 114, 114),
                          bool auto_ = true,
                          bool scaleFill = false,
                          bool scaleUp = true,
                          int stride = 32)
    {
        // Calculate the scaling ratio to fit the image within the new shape
        float ratio = std::min(static_cast<float>(newShape.height) / image.rows,
                               static_cast<float>(newShape.width) / image.cols);

        // Prevent scaling up if not allowed
        if (!scaleUp)
        {
            ratio = std::min(ratio, 1.0f);
        }

        // Calculate new dimensions after scaling
        int newUnpadW = static_cast<int>(std::round(image.cols * ratio));
        int newUnpadH = static_cast<int>(std::round(image.rows * ratio));

        // Calculate padding needed to reach the desired shape
        int dw = newShape.width - newUnpadW;
        int dh = newShape.height - newUnpadH;

        if (auto_)
        {
            // Ensure padding is a multiple of stride for model compatibility
            dw = (dw % stride) / 2;
            dh = (dh % stride) / 2;
        }
        else if (scaleFill)
        {
            // Scale to fill without maintaining aspect ratio
            newUnpadW = newShape.width;
            newUnpadH = newShape.height;
            ratio = std::min(static_cast<float>(newShape.width) / image.cols,
                             static_cast<float>(newShape.height) / image.rows);
            dw = 0;
            dh = 0;
        }
        else
        {
            // Evenly distribute padding on both sides
            // Calculate separate padding for left/right and top/bottom to handle odd padding
            int padLeft = dw / 2;
            int padRight = dw - padLeft;
            int padTop = dh / 2;
            int padBottom = dh - padTop;

            // Resize the image if the new dimensions differ
            if (image.cols != newUnpadW || image.rows != newUnpadH)
            {
                cv::resize(image, outImage, cv::Size(newUnpadW, newUnpadH), 0, 0, cv::INTER_LINEAR);
            }
            else
            {
                // Avoid unnecessary copying if dimensions are the same
                outImage = image;
            }

            // Apply padding to reach the desired shape
            cv::copyMakeBorder(outImage, outImage, padTop, padBottom, padLeft, padRight, cv::BORDER_CONSTANT, color);
            return; // Exit early since padding is already applied
        }

        // Resize the image if the new dimensions differ
        if (image.cols != newUnpadW || image.rows != newUnpadH)
        {
            cv::resize(image, outImage, cv::Size(newUnpadW, newUnpadH), 0, 0, cv::INTER_LINEAR);
        }
        else
        {
            // Avoid unnecessary copying if dimensions are the same
            outImage = image;
        }

        // Calculate separate padding for left/right and top/bottom to handle odd padding
        int padLeft = dw / 2;
        int padRight = dw - padLeft;
        int padTop = dh / 2;
        int padBottom = dh - padTop;

        // Apply padding to reach the desired shape
        cv::copyMakeBorder(outImage, outImage, padTop, padBottom, padLeft, padRight, cv::BORDER_CONSTANT, color);
    }

    /**
     * @brief Scales detection coordinates back to the original image size.
     *
     * @param imageShape Shape of the resized image used for inference.
     * @param bbox Detection bounding box to be scaled.
     * @param imageOriginalShape Original image size before resizing.
     * @param p_Clip Whether to clip the coordinates to the image boundaries.
     * @return BoundingBox Scaled bounding box.
     */
    BoundingBox scaleCoords(const cv::Size &imageShape, BoundingBox coords,
                            const cv::Size &imageOriginalShape, bool p_Clip)
    {
        BoundingBox result;
        float gain = std::min(static_cast<float>(imageShape.height) / static_cast<float>(imageOriginalShape.height),
                              static_cast<float>(imageShape.width) / static_cast<float>(imageOriginalShape.width));

        int padX = static_cast<int>(std::round((imageShape.width - imageOriginalShape.width * gain) / 2.0f));
        int padY = static_cast<int>(std::round((imageShape.height - imageOriginalShape.height * gain) / 2.0f));

        result.x = static_cast<int>(std::round((coords.x - padX) / gain));
        result.y = static_cast<int>(std::round((coords.y - padY) / gain));
        result.width = static_cast<int>(std::round(coords.width / gain));
        result.height = static_cast<int>(std::round(coords.height / gain));

        if (p_Clip)
        {
            result.x = utils::clamp(result.x, 0, imageOriginalShape.width);
            result.y = utils::clamp(result.y, 0, imageOriginalShape.height);
            result.width = utils::clamp(result.width, 0, imageOriginalShape.width - result.x);
            result.height = utils::clamp(result.height, 0, imageOriginalShape.height - result.y);
        }
        return result;
    }

    /**
     * @brief Extracts the best class information from a detection row.
     *
     * @param p_Mat Row data containing class scores.
     * @param numClasses Number of classes.
     * @param bestConf Reference to store the best confidence score.
     * @param bestClassId Reference to store the best class ID.
     */
    void getBestClassInfo(const std::vector<float> &p_Mat, const int &numClasses,
                          float &bestConf, int &bestClassId)

    {
        bestClassId = 0;
        bestConf = 0;

        for (int i = 0; i < numClasses; i++)
        {
            if (p_Mat[i + 4] > bestConf)
            {
                bestConf = p_Mat[i + 4];
                bestClassId = i;
            }
        }
    }

    /**
     * @brief Performs Non-Maximum Suppression (NMS) on the bounding boxes.
     *
     * @param boundingBoxes Vector of bounding boxes.
     * @param scores Vector of confidence scores corresponding to each bounding box.
     * @param scoreThreshold Confidence threshold to filter boxes.
     * @param nmsThreshold IoU threshold for NMS.
     * @param indices Output vector of indices that survive NMS.
     */
    // Optimized Non-Maximum Suppression Function
    void NMSBoxes(const std::vector<BoundingBox> &boundingBoxes,
                  const std::vector<float> &scores,
                  float scoreThreshold,
                  float nmsThreshold,
                  std::vector<int> &indices)
    {
        indices.clear();

        const size_t numBoxes = boundingBoxes.size();
        if (numBoxes == 0)
        {
            DEBUG_PRINT("No bounding boxes to process in NMS");
            return;
        }

        // Step 1: Filter out boxes with scores below the threshold
        // and create a list of indices sorted by descending scores
        std::vector<int> sortedIndices;
        sortedIndices.reserve(numBoxes);
        for (size_t i = 0; i < numBoxes; ++i)
        {
            if (scores[i] >= scoreThreshold)
            {
                sortedIndices.push_back(static_cast<int>(i));
            }
        }

        // If no boxes remain after thresholding
        if (sortedIndices.empty())
        {
            DEBUG_PRINT("No bounding boxes above score threshold");
            return;
        }

        // Sort the indices based on scores in descending order
        std::sort(sortedIndices.begin(), sortedIndices.end(),
                  [&scores](int idx1, int idx2)
                  {
                      return scores[idx1] > scores[idx2];
                  });

        // Step 2: Precompute the areas of all boxes
        std::vector<float> areas(numBoxes, 0.0f);
        for (size_t i = 0; i < numBoxes; ++i)
        {
            areas[i] = boundingBoxes[i].width * boundingBoxes[i].height;
        }

        // Step 3: Suppression mask to mark boxes that are suppressed
        std::vector<bool> suppressed(numBoxes, false);

        // Step 4: Iterate through the sorted list and suppress boxes with high IoU
        for (size_t i = 0; i < sortedIndices.size(); ++i)
        {
            int currentIdx = sortedIndices[i];
            if (suppressed[currentIdx])
            {
                continue;
            }

            // Select the current box as a valid detection
            indices.push_back(currentIdx);

            const BoundingBox &currentBox = boundingBoxes[currentIdx];
            const float x1_max = currentBox.x;
            const float y1_max = currentBox.y;
            const float x2_max = currentBox.x + currentBox.width;
            const float y2_max = currentBox.y + currentBox.height;
            const float area_current = areas[currentIdx];

            // Compare IoU of the current box with the rest
            for (size_t j = i + 1; j < sortedIndices.size(); ++j)
            {
                int compareIdx = sortedIndices[j];
                if (suppressed[compareIdx])
                {
                    continue;
                }

                const BoundingBox &compareBox = boundingBoxes[compareIdx];
                const float x1 = std::max(x1_max, static_cast<float>(compareBox.x));
                const float y1 = std::max(y1_max, static_cast<float>(compareBox.y));
                const float x2 = std::min(x2_max, static_cast<float>(compareBox.x + compareBox.width));
                const float y2 = std::min(y2_max, static_cast<float>(compareBox.y + compareBox.height));

                const float interWidth = x2 - x1;
                const float interHeight = y2 - y1;

                if (interWidth <= 0 || interHeight <= 0)
                {
                    continue;
                }

                const float intersection = interWidth * interHeight;
                const float unionArea = area_current + areas[compareIdx] - intersection;
                const float iou = (unionArea > 0.0f) ? (intersection / unionArea) : 0.0f;

                if (iou > nmsThreshold)
                {
                    suppressed[compareIdx] = true;
                }
            }
        }

        DEBUG_PRINT("NMS completed with " + std::to_string(indices.size()) + " indices remaining");
    }

    /**
     * @brief Generates a vector of colors for each class name.
     *
     * @param classNames Vector of class names.
     * @param seed Seed for random color generation to ensure reproducibility.
     * @return std::vector<cv::Scalar> Vector of colors.
     */
    inline std::vector<cv::Scalar> generateColors(const std::vector<std::string> &classNames, int seed = 42)
    {
        // Static cache to store colors based on class names to avoid regenerating
        static std::unordered_map<size_t, std::vector<cv::Scalar>> colorCache;

        // Compute a hash key based on class names to identify unique class configurations
        size_t hashKey = 0;
        for (const auto &name : classNames)
        {
            hashKey ^= std::hash<std::string>{}(name) + 0x9e3779b9 + (hashKey << 6) + (hashKey >> 2);
        }

        // Check if colors for this class configuration are already cached
        auto it = colorCache.find(hashKey);
        if (it != colorCache.end())
        {
            return it->second;
        }

        // Generate unique random colors for each class
        std::vector<cv::Scalar> colors;
        colors.reserve(classNames.size());

        std::mt19937 rng(seed);                         // Initialize random number generator with fixed seed
        std::uniform_int_distribution<int> uni(0, 255); // Define distribution for color values

        for (size_t i = 0; i < classNames.size(); ++i)
        {
            colors.emplace_back(cv::Scalar(uni(rng), uni(rng), uni(rng))); // Generate random BGR color
        }

        // Cache the generated colors for future use
        colorCache.emplace(hashKey, colors);

        return colorCache[hashKey];
    }

    /**
     * @brief Draws bounding boxes and labels on the image based on detections.
     *
     * @param image Image on which to draw.
     * @param detections Vector of detections.
     * @param classNames Vector of class names corresponding to object IDs.
     * @param colors Vector of colors for each class.
     */
    inline void drawBoundingBox(cv::Mat &image, const std::vector<Detection> &detections,
                                const std::vector<std::string> &classNames, const std::vector<cv::Scalar> &colors)
    {
        // Iterate through each detection to draw bounding boxes and labels
        for (const auto &detection : detections)
        {
            // Skip detections below the confidence threshold
            if (detection.conf <= CONFIDENCE_THRESHOLD)
                continue;

            // Ensure the object ID is within valid range
            if (detection.classId < 0 || static_cast<size_t>(detection.classId) >= classNames.size())
                continue;

            // Select color based on object ID for consistent coloring
            const cv::Scalar &color = colors[detection.classId % colors.size()];

            // Draw the bounding box rectangle
            cv::rectangle(image, cv::Point(detection.box.x, detection.box.y),
                          cv::Point(detection.box.x + detection.box.width, detection.box.y + detection.box.height),
                          color, 2, cv::LINE_AA);

            // Prepare label text with class name and confidence percentage
            std::string label = classNames[detection.classId] + ": " + std::to_string(static_cast<int>(detection.conf * 100)) + "%";

            // Define text properties for labels
            int fontFace = cv::FONT_HERSHEY_SIMPLEX;
            double fontScale = std::min(image.rows, image.cols) * 0.0008;
            const int thickness = std::max(1, static_cast<int>(std::min(image.rows, image.cols) * 0.002));
            int baseline = 0;

            // Calculate text size for background rectangles
            cv::Size textSize = cv::getTextSize(label, fontFace, fontScale, thickness, &baseline);

            // Define positions for the label
            int labelY = std::max(detection.box.y, textSize.height + 5);
            cv::Point labelTopLeft(detection.box.x, labelY - textSize.height - 5);
            cv::Point labelBottomRight(detection.box.x + textSize.width + 5, labelY + baseline - 5);

            // Draw background rectangle for label
            cv::rectangle(image, labelTopLeft, labelBottomRight, color, cv::FILLED);

            // Put label text
            cv::putText(image, label, cv::Point(detection.box.x + 2, labelY - 2), fontFace, fontScale, cv::Scalar(255, 255, 255), thickness, cv::LINE_AA);
        }
    }

    /**
     * @brief Draws bounding boxes and semi-transparent masks on the image based on detections.
     *
     * @param image Image on which to draw.
     * @param detections Vector of detections.
     * @param classNames Vector of class names corresponding to object IDs.
     * @param classColors Vector of colors for each class.
     * @param maskAlpha Alpha value for the mask transparency.
     */
    inline void drawBoundingBoxMask(cv::Mat &image, const std::vector<Detection> &detections,
                                    const std::vector<std::string> &classNames, const std::vector<cv::Scalar> &classColors,
                                    float maskAlpha = 0.4f)
    {
        // Validate input image
        if (image.empty())
        {
            std::cerr << "ERROR: Empty image provided to drawBoundingBoxMask." << std::endl;
            return;
        }

        const int imgHeight = image.rows;
        const int imgWidth = image.cols;

        // Precompute dynamic font size and thickness based on image dimensions
        const double fontSize = std::min(imgHeight, imgWidth) * 0.0006;
        const int textThickness = std::max(1, static_cast<int>(std::min(imgHeight, imgWidth) * 0.001));

        // Create a mask image for blending (initialized to zero)
        cv::Mat maskImage(image.size(), image.type(), cv::Scalar::all(0));

        // Pre-filter detections to include only those above the confidence threshold and with valid class IDs
        std::vector<const Detection *> filteredDetections;
        for (const auto &detection : detections)
        {
            if (detection.conf > CONFIDENCE_THRESHOLD &&
                detection.classId >= 0 &&
                static_cast<size_t>(detection.classId) < classNames.size())
            {
                filteredDetections.emplace_back(&detection);
            }
        }

        // Draw filled rectangles on the mask image for the semi-transparent overlay
        for (const auto *detection : filteredDetections)
        {
            cv::Rect box(detection->box.x, detection->box.y, detection->box.width, detection->box.height);
            const cv::Scalar &color = classColors[detection->classId];
            cv::rectangle(maskImage, box, color, cv::FILLED);
        }

        // Blend the maskImage with the original image to apply the semi-transparent masks
        cv::addWeighted(maskImage, maskAlpha, image, 1.0f, 0, image);

        // Draw bounding boxes and labels on the original image
        for (const auto *detection : filteredDetections)
        {
            cv::Rect box(detection->box.x, detection->box.y, detection->box.width, detection->box.height);
            const cv::Scalar &color = classColors[detection->classId];
            cv::rectangle(image, box, color, 2, cv::LINE_AA);

            std::string label = classNames[detection->classId] + ": " + std::to_string(static_cast<int>(detection->conf * 100)) + "%";
            int baseLine = 0;
            cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, fontSize, textThickness, &baseLine);

            int labelY = std::max(detection->box.y, labelSize.height + 5);
            cv::Point labelTopLeft(detection->box.x, labelY - labelSize.height - 5);
            cv::Point labelBottomRight(detection->box.x + labelSize.width + 5, labelY + baseLine - 5);

            // Draw background rectangle for label
            cv::rectangle(image, labelTopLeft, labelBottomRight, color, cv::FILLED);

            // Put label text
            cv::putText(image, label, cv::Point(detection->box.x + 2, labelY - 2), cv::FONT_HERSHEY_SIMPLEX, fontSize, cv::Scalar(255, 255, 255), textThickness, cv::LINE_AA);
        }

        DEBUG_PRINT("Bounding boxes and masks drawn on image.");
    }

    // /**
    //  * @brief A robust implementation of a clamp function.
    //  *        Restricts a value to lie within a specified range [low, high].
    //  *
    //  * @tparam T The type of the value to clamp. Should be an arithmetic type (int, float, etc.).
    //  * @param value The value to clamp.
    //  * @param low The lower bound of the range.
    //  * @param high The upper bound of the range.
    //  * @return const T& The clamped value, constrained to the range [low, high].
    //  *
    //  * @note If low > high, the function swaps the bounds automatically to ensure valid behavior.
    //  */
    // template <typename T>
    // typename std::enable_if<std::is_arithmetic<T>::value, T>::type
    // inline clamp(const T &value, const T &low, const T &high)
    // {
    //     // Ensure the range [low, high] is valid; swap if necessary
    //     T validLow = low < high ? low : high;
    //     T validHigh = low < high ? high : low;

    //     // Clamp the value to the range [validLow, validHigh]
    //     if (value < validLow)
    //         return validLow;
    //     if (value > validHigh)
    //         return validHigh;
    //     return value;
    // }

};

/**
 * @brief YOLO8Detector class handles loading the YOLO model, preprocessing images, running inference, and postprocessing results.
 */
class YOLO8Detector
{
public:
    /**
     * @brief Constructor to initialize the YOLO detector with model and label paths.
     *
     * @param modelPath Path to the ONNX model file.
     * @param labelsPath Path to the file containing class labels.
     * @param useGPU Whether to use GPU for inference (default is false).
     */
    YOLO8Detector(const std::string &modelPath, const std::string &labelsPath, bool useGPU = false);

    /**
     * @brief Runs detection on the provided image.
     *
     * @param image Input image for detection.
     * @param confThreshold Confidence threshold to filter detections (default is 0.4).
     * @param iouThreshold IoU threshold for Non-Maximum Suppression (default is 0.45).
     * @return std::vector<Detection> Vector of detections.
     */
    std::vector<Detection> detect(const cv::Mat &image, float confThreshold = 0.4f, float iouThreshold = 0.45f);

    /**
     * @brief Draws bounding boxes on the image based on detections.
     *
     * @param image Image on which to draw.
     * @param detections Vector of detections.
     */
    void drawBoundingBox(cv::Mat &image, const std::vector<Detection> &detections) const
    {
        utils::drawBoundingBox(image, detections, classNames, classColors);
    }

    /**
     * @brief Draws bounding boxes and semi-transparent masks on the image based on detections.
     *
     * @param image Image on which to draw.
     * @param detections Vector of detections.
     * @param maskAlpha Alpha value for mask transparency (default is 0.4).
     */
    void drawBoundingBoxMask(cv::Mat &image, const std::vector<Detection> &detections, float maskAlpha = 0.4f) const
    {
        utils::drawBoundingBoxMask(image, detections, classNames, classColors, maskAlpha);
    }

private:
    Ort::Env env{nullptr};                       // ONNX Runtime environment
    Ort::SessionOptions sessionOptions{nullptr}; // Session options for ONNX Runtime
    Ort::Session session{nullptr};               // ONNX Runtime session for running inference
    bool isDynamicInputShape{};                  // Flag indicating if input shape is dynamic
    cv::Size inputImageShape;                    // Expected input image shape for the model

    // Vectors to hold allocated input and output node names
    std::vector<Ort::AllocatedStringPtr> inputNodeNameAllocatedStrings;
    std::vector<const char *> inputNames;
    std::vector<Ort::AllocatedStringPtr> outputNodeNameAllocatedStrings;
    std::vector<const char *> outputNames;

    size_t numInputNodes, numOutputNodes; // Number of input and output nodes in the model

    std::vector<std::string> classNames; // Vector of class names loaded from file
    std::vector<cv::Scalar> classColors; // Vector of colors for each class

    /**
     * @brief Preprocesses the input image for model inference.
     *
     * @param image Input image.
     * @param blob Reference to pointer where preprocessed data will be stored.
     * @param inputTensorShape Reference to vector representing input tensor shape.
     * @return cv::Mat Resized image after preprocessing.
     */
    cv::Mat preprocess(const cv::Mat &image, float *&blob, std::vector<int64_t> &inputTensorShape);

    /**
     * @brief Postprocesses the model output to extract detections.
     *
     * @param originalImageSize Size of the original input image.
     * @param resizedImageShape Size of the image after preprocessing.
     * @param outputTensors Vector of output tensors from the model.
     * @param confThreshold Confidence threshold to filter detections.
     * @param iouThreshold IoU threshold for Non-Maximum Suppression.
     * @return std::vector<Detection> Vector of detections.
     */
    std::vector<Detection> postprocess(const cv::Size &originalImageSize, const cv::Size &resizedImageShape,
                                       const std::vector<Ort::Value> &outputTensors,
                                       float confThreshold, float iouThreshold);

    /**
     * @brief Extracts the best class information from a detection row.
     *
     * @param p_Mat Row data containing class scores.
     * @param numClasses Number of classes.
     * @param bestConf Reference to store the best confidence score.
     * @param bestClassId Reference to store the best class ID.
     */
    void getBestClassInfo(const std::vector<float> &p_Mat, const int &numClasses,
                          float &bestConf, int &bestClassId);
};

// Implementation of YOLO8Detector constructor
YOLO8Detector::YOLO8Detector(const std::string &modelPath, const std::string &labelsPath, bool useGPU)
{
    try
    {
        // Initialize ONNX Runtime environment with warning level
        env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, "ONNX_DETECTION");
        sessionOptions = Ort::SessionOptions();

        // Set number of intra-op threads for parallelism
        sessionOptions.SetIntraOpNumThreads(std::min(6, static_cast<int>(std::thread::hardware_concurrency())));
        sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);

        // Retrieve available execution providers (e.g., CPU, CUDA)
        std::vector<std::string> availableProviders = Ort::GetAvailableProviders();
        auto cudaAvailable = std::find(availableProviders.begin(), availableProviders.end(), "CUDAExecutionProvider");
        OrtCUDAProviderOptions cudaOption;

        // Configure session options based on whether GPU is to be used and available
        if (useGPU && cudaAvailable != availableProviders.end())
        {
            std::cout << "Inference device: GPU" << std::endl;
            sessionOptions.AppendExecutionProvider_CUDA(cudaOption); // Append CUDA execution provider
        }
        else
        {
            if (useGPU)
            {
                std::cout << "GPU is not supported by your ONNXRuntime build. Fallback to CPU." << std::endl;
            }
            std::cout << "Inference device: CPU" << std::endl;
        }

        // Load the ONNX model into the session
#ifdef _WIN32
        std::wstring w_modelPath(modelPath.begin(), modelPath.end());
        session = Ort::Session(env, w_modelPath.c_str(), sessionOptions);
#else
        session = Ort::Session(env, modelPath.c_str(), sessionOptions);
#endif

        Ort::AllocatorWithDefaultOptions allocator;

        // Retrieve input tensor shape information
        Ort::TypeInfo inputTypeInfo = session.GetInputTypeInfo(0);
        std::vector<int64_t> inputTensorShapeVec = inputTypeInfo.GetTensorTypeAndShapeInfo().GetShape();
        isDynamicInputShape = (inputTensorShapeVec.size() >= 4) && (inputTensorShapeVec[2] == -1 && inputTensorShapeVec[3] == -1); // Check for dynamic dimensions

        // Allocate and store input node names
        auto input_name = session.GetInputNameAllocated(0, allocator);
        inputNodeNameAllocatedStrings.push_back(std::move(input_name));
        inputNames.push_back(inputNodeNameAllocatedStrings.back().get());

        // Allocate and store output node names
        auto output_name = session.GetOutputNameAllocated(0, allocator);
        outputNodeNameAllocatedStrings.push_back(std::move(output_name));
        outputNames.push_back(outputNodeNameAllocatedStrings.back().get());

        // Set the expected input image shape based on the model's input tensor
        if (inputTensorShapeVec.size() >= 4)
        {
            inputImageShape = cv::Size(static_cast<int>(inputTensorShapeVec[3]), static_cast<int>(inputTensorShapeVec[2]));
        }
        else
        {
            throw std::runtime_error("Invalid input tensor shape.");
        }

        // Get the number of input and output nodes
        numInputNodes = session.GetInputCount();
        numOutputNodes = session.GetOutputCount();

        // Load class names and generate corresponding colors
        classNames = utils::getClassNames(labelsPath);
        classColors = utils::generateColors(classNames);

        std::cout << "Model loaded successfully with " << numInputNodes << " input nodes and " << numOutputNodes << " output nodes." << std::endl;
    }
    catch (const std::exception& e)
    {
        std::cerr << "Error during initialization: " << e.what() << std::endl;
        throw; // Rethrow the exception to be handled by the caller
    }
    catch (...)
    {
        std::cerr << "Unknown error occurred during initialization." << std::endl;
        throw; // Rethrow the unknown error to be handled by the caller
    }
}

// Preprocess function implementation
cv::Mat YOLO8Detector::preprocess(const cv::Mat &image, float *&blob, std::vector<int64_t> &inputTensorShape)
{
    ScopedTimer timer("preprocessing");

    cv::Mat resizedImage;
    try
    {
        // Resize and pad the image using letterBox utility
        utils::letterBox(image, resizedImage, inputImageShape, cv::Scalar(114, 114, 114), isDynamicInputShape, false, true, 32);

        // Update input tensor shape based on resized image dimensions
        inputTensorShape[2] = resizedImage.rows;
        inputTensorShape[3] = resizedImage.cols;

        // Convert image to float and normalize to [0, 1]
        resizedImage.convertTo(resizedImage, CV_32FC3, 1 / 255.0f);

        // Allocate memory for the image blob in CHW format
        blob = new float[resizedImage.cols * resizedImage.rows * resizedImage.channels()];

        // Split the image into separate channels and store in the blob
        std::vector<cv::Mat> chw(resizedImage.channels());
        for (int i = 0; i < resizedImage.channels(); ++i)
        {
            chw[i] = cv::Mat(resizedImage.rows, resizedImage.cols, CV_32FC1, blob + i * resizedImage.cols * resizedImage.rows);
        }
        cv::split(resizedImage, chw); // Split channels into the blob

        DEBUG_PRINT("Preprocessing completed")
    }
    catch (const std::exception& e)
    {
        std::cerr << "Error during preprocessing: " << e.what() << std::endl;
        throw; // Rethrow the exception to be handled by the caller
    }
    catch (...)
    {
        std::cerr << "Unknown error occurred during preprocessing." << std::endl;
        throw; // Rethrow the unknown error to be handled by the caller
    }

    return resizedImage;
}

std::vector<Detection> YOLO8Detector::postprocess(
    const cv::Size &originalImageSize,
    const cv::Size &resizedImageShape,
    const std::vector<Ort::Value> &outputTensors,
    float confThreshold,
    float iouThreshold)
{
    ScopedTimer timer("postprocessing");

    std::vector<Detection> detections;
    try
    {
        // Retrieve raw output data from the first output tensor
        const float *rawOutput = outputTensors[0].GetTensorData<float>();
        const std::vector<int64_t> outputShape = outputTensors[0].GetTensorTypeAndShapeInfo().GetShape();
        const size_t num_features = outputShape[1];
        const size_t num_detections = outputShape[2];

        // Early exit if no detections
        if (num_detections == 0)
        {
            return detections;
        }

        const int numClasses = static_cast<int>(num_features) - 4;
        if (numClasses <= 0)
        {
            // Invalid number of classes
            return detections;
        }

        // Preallocate memory to avoid dynamic resizing
        std::vector<BoundingBox> boxes;
        boxes.reserve(num_detections);
        std::vector<float> confs;
        confs.reserve(num_detections);
        std::vector<int> classIds;
        classIds.reserve(num_detections);
        std::vector<BoundingBox> nms_boxes;
        nms_boxes.reserve(num_detections);

        // Constants for indexing
        const float *ptr = rawOutput;

        for (size_t d = 0; d < num_detections; ++d)
        {
            // Extract bounding box coordinates
            float centerX = ptr[0 * num_detections + d];
            float centerY = ptr[1 * num_detections + d];
            float width = ptr[2 * num_detections + d];
            float height = ptr[3 * num_detections + d];

            // Initialize object confidence and class ID
            float objConf = ptr[4 * num_detections + d];
            int classId = 0;

            // Find the class with the highest confidence
            for (int c = 1; c < numClasses; ++c)
            {
                float classConf = ptr[(4 + c) * num_detections + d];
                if (classConf > objConf)
                {
                    objConf = classConf;
                    classId = c;
                }
            }

            // Filter out low-confidence detections
            if (objConf > confThreshold)
            {
                // Convert center coordinates to top-left
                float left = centerX - width / 2.0f;
                float top = centerY - height / 2.0f;

                // Scale coordinates to original image size
                BoundingBox scaledBox = utils::scaleCoords(
                    resizedImageShape,
                    BoundingBox(left, top, width, height),
                    originalImageSize,
                    true);

                // Round coordinates and adjust for class ID to prevent NMS overlap between classes
                BoundingBox roundedBox;
                roundedBox.x = std::round(scaledBox.x);
                roundedBox.y = std::round(scaledBox.y);
                roundedBox.width = std::round(scaledBox.width);
                roundedBox.height = std::round(scaledBox.height);

                // Adjusted box for NMS to differentiate classes
                BoundingBox nmsBox = roundedBox;
                nmsBox.x += classId * 7680; // Assuming 7680 is larger than image width
                nmsBox.y += classId * 7680;

                // Append to NMS containers
                nms_boxes.emplace_back(nmsBox);
                boxes.emplace_back(roundedBox);
                confs.emplace_back(objConf);
                classIds.emplace_back(classId);
            }
        }

        // Perform Non-Maximum Suppression (NMS)
        std::vector<int> indices;
        utils::NMSBoxes(nms_boxes, confs, confThreshold, iouThreshold, indices);

        // Collect final detections
        detections.reserve(indices.size());
        for (const int idx : indices)
        {
            detections.emplace_back(Detection{
                boxes[idx],
                confs[idx],
                classIds[idx]});
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << "Error during postprocessing: " << e.what() << std::endl;
        throw; // Rethrow the exception to be handled by the caller
    }
    catch (...)
    {
        std::cerr << "Unknown error occurred during postprocessing." << std::endl;
        throw; // Rethrow the unknown error to be handled by the caller
    }

    return detections;
}

// Implementation of getBestClassInfo
void YOLO8Detector::getBestClassInfo(const std::vector<float> &p_Mat, const int &numClasses,
                                     float &bestConf, int &bestClassId)
{
    try
    {
        bestClassId = 0;
        bestConf = 0.0f;

        for (int i = 0; i < numClasses; i++)
        {
            if (p_Mat[i + 4] > bestConf)
            {
                bestConf = p_Mat[i + 4];
                bestClassId = i;
            }
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << "Error in getBestClassInfo: " << e.what() << std::endl;
        throw; // Rethrow the exception to be handled by the caller
    }
    catch (...)
    {
        std::cerr << "Unknown error occurred in getBestClassInfo." << std::endl;
        throw; // Rethrow the unknown error to be handled by the caller
    }
}

// Detect function implementation
std::vector<Detection> YOLO8Detector::detect(const cv::Mat &image, float confThreshold, float iouThreshold)
{
    ScopedTimer timer("Overall detection");

    float *blobPtr = nullptr; // Pointer to hold preprocessed image data
    // Define the shape of the input tensor (batch size, channels, height, width)
    std::vector<int64_t> inputTensorShape = {1, 3, inputImageShape.height, inputImageShape.width};

    try
    {
        // Preprocess the image and obtain a pointer to the blob
        cv::Mat preprocessedImage = preprocess(image, blobPtr, inputTensorShape);

        // Compute the total number of elements in the input tensor
        size_t inputTensorSize = utils::vectorProduct(inputTensorShape);

        // Create a vector from the blob data for ONNX Runtime input
        std::vector<float> inputTensorValues(blobPtr, blobPtr + inputTensorSize);

        delete[] blobPtr; // Free the allocated memory for the blob

        // Create an Ort memory info object (can be cached if used repeatedly)
        static Ort::MemoryInfo memoryInfo = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);

        // Create input tensor object using the preprocessed data
        Ort::Value inputTensor = Ort::Value::CreateTensor<float>(
            memoryInfo,
            inputTensorValues.data(),
            inputTensorSize,
            inputTensorShape.data(),
            inputTensorShape.size());

        // Run the inference session with the input tensor and retrieve output tensors
        std::vector<Ort::Value> outputTensors = session.Run(
            Ort::RunOptions{nullptr},
            inputNames.data(),
            &inputTensor,
            numInputNodes,
            outputNames.data(),
            numOutputNodes);

        // Determine the resized image shape based on input tensor shape
        cv::Size resizedImageShape(static_cast<int>(inputTensorShape[3]), static_cast<int>(inputTensorShape[2]));

        // Postprocess the output tensors to obtain detections
        std::vector<Detection> detections = postprocess(image.size(), resizedImageShape, outputTensors, confThreshold, iouThreshold);

        return detections; // Return the vector of detections
    }
    catch (const std::exception& e)
    {
        std::cerr << "Error during detection: " << e.what() << std::endl;
        throw; // Rethrow the exception to be handled by the caller
    }
    catch (...)
    {
        std::cerr << "Unknown error occurred during detection." << std::endl;
        throw; // Rethrow the unknown error to be handled by the caller
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants