diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..606d9d074 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,74 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +name: "CodeQL" + +on: + push: + branches: [master] + pull_request: + # The branches below must be a subset of the branches above + branches: [master] + schedule: + - cron: '0 10 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-24.04 + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + language: ['cpp'] + # Supported version for OpenCV + opencv: [ + 4.10.0 + ] + compiler: [g++] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + - name: Install dev dependencies + run: | + # Install OpenCV dev libraries + sudo add-apt-repository "deb http://security.ubuntu.com/ubuntu focal-security main" + sudo apt-get update + sudo apt-get install -y \ + build-essential checkinstall cmake pkg-config \ + git \ + libopencv-dev + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 000000000..175aa01c6 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,30 @@ +name: Macos C/C++ CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + brew uninstall cmake + brew update + brew install opencv cmake + + - name: Configure CMake + run: | + mkdir -p build + cd build + cmake .. + + - name: Build + working-directory: build + run: make + diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 000000000..6e60e5663 --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,29 @@ +name: Ubuntu C/C++ CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y libopencv-dev cmake g++ + + - name: Configure CMake + run: | + mkdir -p build + cd build + cmake .. + + - name: Build + working-directory: build + run: make + diff --git a/.gitignore b/.gitignore index b96c2d79a..c25a85a74 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ build -CMakeLists.txt.user - +CMakeLists.txt.user* +CMakeFiles +CMakeCache.txt +*.engine +*.onnx +*.weights +*.pt +*.pth diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 11cbde9f5..000000000 --- a/.travis.yml +++ /dev/null @@ -1,76 +0,0 @@ -language: - - cpp -dist: trusty -compiler: - - g++ - - -matrix: - include: - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.9 - env: - - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" - - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-5 - env: - - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" - - -before_install: - - eval "${MATRIX_EVAL}" - - sudo apt-get update - - sudo apt-get update -qq - -install: - -# OpenCV v3.0.0 (beta) install code (modified from orignal source: https://github.com/jayrambhia/Install-OpenCV) - - # OpenCV dependencies - Details available at: http://docs.opencv.org/trunk/doc/tutorials/introduction/linux_install/linux_install.html - - sudo apt-get install -y build-essential - - sudo apt-get install -y cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev - - sudo apt-get install -y python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev - - # Download v3.0.0 .zip file and extract. - - curl -sL https://github.com/Itseez/opencv/archive/3.3.1.zip > opencv.zip - - unzip opencv.zip - - cd opencv-3.3.1 - - # Create a new 'build' folder. - - mkdir build - - cd build - - # Set build instructions for Ubuntu distro. - - cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D WITH_TBB=ON -D BUILD_NEW_PYTHON_SUPPORT=OFF -D WITH_V4L=ON -D INSTALL_C_EXAMPLES=OFF -D INSTALL_PYTHON_EXAMPLES=OFF -D BUILD_TESTS=OFF -D BUILD_PERF_TESTS=OFF -D BUILD_EXAMPLES=OFF -D WITH_QT=OFF -D WITH_OPENGL=OFF .. - - # Run 'make' with four threads. - - make -j4 - - # Install to OS. - - sudo make install - - # Add configuration to OpenCV to tell it where the library files are located on the file system (/usr/local/lib) - - sudo sh -c 'echo "/usr/local/lib" > /etc/ld.so.conf.d/opencv.conf' - - - sudo ldconfig - - echo "OpenCV installed." - - # We need to return to the repo "root" folder, so we can then 'cd' into the C++ project folder. - - cd ../../ - -script: - - cmake . - - make \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ec1a62230..fed4b1aec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,237 +1,127 @@ -# ---------------------------------------------------------------------------- -# минимальная версия CMake, способная переварить этот файл -# ---------------------------------------------------------------------------- -cmake_minimum_required(VERSION 2.8) -project(MultitargetTracker) - -unset(CMAKE_CXX_FLAGS CACHE) - -if (CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic-errors --std=gnu++14" CACHE STRING COMPILE_FLAGS FORCE) - set(CMAKE_CXX_FLAGS_RELEASE "-O3 -g -march=native -mtune=native --fast-math -ffast-math -funroll-loops -Wall -DNDEBUG -DBOOST_DISABLE_ASSERTS" CACHE STRING COMPILE_FLAGS FORCE) - set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -march=native -mtune=native -Wall -DDEBUG" CACHE STRING COMPILE_FLAGS FORCE) -elseif (MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /W4 -DGTL_STATIC" CACHE STRING COMPILE_FLAGS FORCE) -endif() - - -set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/build) -set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) -set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}) - -# ---------------------------------------------------------------------------- -# Предполагаем, что FindOpenCV.cmake расположен по адресу CMAKE_MODULE_PATH. -# ---------------------------------------------------------------------------- - FIND_PACKAGE(OpenCV) -# ---------------------------------------------------------------------------- -# ищем все .cpp и .h файлы и добавляем их в наш проект -# ---------------------------------------------------------------------------- - set(folder_source - main.cpp - - Detector/BaseDetector.cpp - Detector/MotionDetector.cpp - Detector/BackgroundSubtract.cpp - Detector/vibe_src/vibe.cpp - Detector/Subsense/BackgroundSubtractorLBSP.cpp - Detector/Subsense/BackgroundSubtractorLOBSTER.cpp - Detector/Subsense/BackgroundSubtractorSuBSENSE.cpp - Detector/Subsense/LBSP.cpp - Detector/FaceDetector.cpp - Detector/PedestrianDetector.cpp - Detector/pedestrians/c4-pedestrian-detector.cpp - Detector/DNNDetector.cpp - - Tracker/Ctracker.cpp - Tracker/track.cpp - Tracker/HungarianAlg/HungarianAlg.cpp - Tracker/LocalTracker.cpp - Tracker/Kalman.cpp) - - set(folder_header - MouseExample.h - VideoExample.h - nms.h - defines.h - - Detector/BaseDetector.h - Detector/MotionDetector.h - Detector/BackgroundSubtract.h - Detector/vibe_src/vibe.hpp - Detector/Subsense/BackgroundSubtractorLBSP.h - Detector/Subsense/BackgroundSubtractorLOBSTER.h - Detector/Subsense/BackgroundSubtractorSuBSENSE.h - Detector/Subsense/DistanceUtils.h - Detector/Subsense/LBSP.h - Detector/Subsense/RandUtils.h - Detector/FaceDetector.h - Detector/PedestrianDetector.h - Detector/pedestrians/c4-pedestrian-detector.h - Detector/DNNDetector.h - - Tracker/Ctracker.h - Tracker/track.h - Tracker/HungarianAlg/HungarianAlg.h - Tracker/LocalTracker.h - Tracker/Kalman.h) - - set(graph_source - Tracker/graph/tokenise.cpp -# Tracker/graph/script.cpp -# Tracker/graph/rings.cpp - Tracker/graph/mytree.cpp - Tracker/graph/mygraph.cpp - Tracker/graph/mwbmatching.cpp - Tracker/graph/mincut.cpp - Tracker/graph/gport.cpp -# Tracker/graph/gml2nestedsql.cpp -# Tracker/graph/gml2dot.cpp -# Tracker/graph/components.cpp - Tracker/graph/fheap.c - ) - - set(graph_header - Tracker/graph/tokenise.h - Tracker/graph/mytree.h - Tracker/graph/mygraph.h - Tracker/graph/mwbmatching.h - Tracker/graph/mincut.h - Tracker/graph/gport.h - Tracker/graph/gdefs.h - Tracker/graph/fheap.h - ) - - set(gtl_source - Tracker/graph/GTL/src/bellman_ford.cpp - Tracker/graph/GTL/src/bfs.cpp - Tracker/graph/GTL/src/biconnectivity.cpp - Tracker/graph/GTL/src/bid_dijkstra.cpp - Tracker/graph/GTL/src/components.cpp - Tracker/graph/GTL/src/debug.cpp - Tracker/graph/GTL/src/dfs.cpp - Tracker/graph/GTL/src/dijkstra.cpp - Tracker/graph/GTL/src/edge.cpp - Tracker/graph/GTL/src/embedding.cpp - Tracker/graph/GTL/src/fm_partition.cpp - Tracker/graph/GTL/src/gml_parser.cpp - Tracker/graph/GTL/src/gml_scanner.cpp - Tracker/graph/GTL/src/graph.cpp - Tracker/graph/GTL/src/maxflow_ff.cpp - Tracker/graph/GTL/src/maxflow_pp.cpp - Tracker/graph/GTL/src/maxflow_sap.cpp - Tracker/graph/GTL/src/min_tree.cpp - Tracker/graph/GTL/src/node.cpp - Tracker/graph/GTL/src/planarity.cpp - Tracker/graph/GTL/src/pq_node.cpp - Tracker/graph/GTL/src/pq_tree.cpp - Tracker/graph/GTL/src/ratio_cut_partition.cpp - Tracker/graph/GTL/src/st_number.cpp - Tracker/graph/GTL/src/topsort.cpp - ) - - set(gtl_header - Tracker/graph/GTL/include/GTL/algorithm.h - Tracker/graph/GTL/include/GTL/bellman_ford.h - Tracker/graph/GTL/include/GTL/bfs.h - Tracker/graph/GTL/include/GTL/biconnectivity.h - Tracker/graph/GTL/include/GTL/bid_dijkstra.h - Tracker/graph/GTL/include/GTL/bin_heap.h - Tracker/graph/GTL/include/GTL/components.h - Tracker/graph/GTL/include/GTL/debug.h - Tracker/graph/GTL/include/GTL/dfs.h - Tracker/graph/GTL/include/GTL/dijkstra.h - Tracker/graph/GTL/include/GTL/edge.h - Tracker/graph/GTL/include/GTL/edge_data.h - Tracker/graph/GTL/include/GTL/edge_map.h - Tracker/graph/GTL/include/GTL/embedding.h - Tracker/graph/GTL/include/GTL/fm_partition.h - Tracker/graph/GTL/include/GTL/gml_parser.h - Tracker/graph/GTL/include/GTL/gml_scanner.h - Tracker/graph/GTL/include/GTL/graph.h - Tracker/graph/GTL/include/GTL/maxflow_ff.h - Tracker/graph/GTL/include/GTL/maxflow_pp.h - Tracker/graph/GTL/include/GTL/maxflow_sap.h - Tracker/graph/GTL/include/GTL/min_tree.h - Tracker/graph/GTL/include/GTL/ne_map.h - Tracker/graph/GTL/include/GTL/node.h - Tracker/graph/GTL/include/GTL/node_data.h - Tracker/graph/GTL/include/GTL/node_map.h - Tracker/graph/GTL/include/GTL/planarity.h - Tracker/graph/GTL/include/GTL/pq_node.h - Tracker/graph/GTL/include/GTL/pq_tree.h - Tracker/graph/GTL/include/GTL/ratio_cut_partition.h - Tracker/graph/GTL/include/GTL/st_number.h - Tracker/graph/GTL/include/GTL/symlist.h - Tracker/graph/GTL/include/GTL/topsort.h - Tracker/graph/GTL/include/GTL/version.h - Tracker/graph/GTL/include/GTL/GTL.h - ) - - SOURCE_GROUP("Source Files" FILES ${folder_source}) - SOURCE_GROUP("Header Files" FILES ${folder_header}) - - SOURCE_GROUP("graph" FILES ${graph_source} ${graph_header}) - SOURCE_GROUP("GTL" FILES ${gtl_source} ${gtl_header}) - -include(CheckIncludeFileCXX) -check_include_file_cxx(opencv2/bgsegm.hpp HAVE_OPENCV_CONTRIB) -if(HAVE_OPENCV_CONTRIB) - add_definitions(-DHAVE_OPENCV_CONTRIB) - - option(USE_OCV_BGFG "Should use the bgfg algorithms from opencv_contrib?" ON) - option(USE_OCV_KCF "Should use the KCF tracker from opencv_contrib for collisions resolving?" ON) - option(USE_OCV_UKF "Should use the Unscented Kalman Filterr from opencv_contrib?" ON) - -else(HAVE_OPENCV_CONTRIB) - - option(USE_OCV_BGFG "Should use the bgfg algorithms from opencv_contrib?" OFF) - option(USE_OCV_KCF "Should use the KCF tracker from opencv_contrib for collisions resolving?" OFF) - option(USE_OCV_UKF "Should use the Unscented Kalman Filterr from opencv_contrib?" OFF) -endif(HAVE_OPENCV_CONTRIB) - -if(USE_OCV_UKF) - add_definitions(-DUSE_OCV_UKF) -endif(USE_OCV_UKF) - -if(USE_OCV_KCF) - add_definitions(-DUSE_OCV_KCF) -endif(USE_OCV_KCF) - -if(USE_OCV_BGFG) - add_definitions(-DUSE_OCV_BGFG) -endif(USE_OCV_BGFG) - -# ---------------------------------------------------------------------------- -# создаем проект -# ---------------------------------------------------------------------------- - ADD_EXECUTABLE(MultitargetTracker ${folder_source} ${folder_header} ${graph_source} ${graph_header} ${gtl_source} ${gtl_header}) -# ---------------------------------------------------------------------------- -# добавляем include директории -# ---------------------------------------------------------------------------- - INCLUDE_DIRECTORIES( - ${PROJECT_SOURCE_DIR}/Detector - ${PROJECT_SOURCE_DIR}/Detector/vibe_src - ${PROJECT_SOURCE_DIR}/Detector/Subsense - ${PROJECT_SOURCE_DIR}/Tracker - ${PROJECT_SOURCE_DIR}/Tracker/HungarianAlg - ${PROJECT_SOURCE_DIR}/Tracker/graph - ${PROJECT_SOURCE_DIR}/Tracker/graph/GTL/include) - - INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}) - INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS}) -# ---------------------------------------------------------------------------- -# и Lib-ы opencv -# ---------------------------------------------------------------------------- - - -if (CMAKE_COMPILER_IS_GNUCXX) -set(LIBS - ${OpenCV_LIBS} -# iconv -) -else(CMAKE_COMPILER_IS_GNUCXX) -set(LIBS - ${OpenCV_LIBS} -) -endif() -TARGET_LINK_LIBRARIES(MultitargetTracker ${LIBS}) +cmake_minimum_required(VERSION 3.9) + +project(MTTracking VERSION 1.1.0) + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +unset(CMAKE_C_FLAGS CACHE) +unset(CMAKE_CXX_FLAGS CACHE) +unset(CMAKE_CXX_FLAGS_RELEASE CACHE) +# unset(CMAKE_CXX_FLAGS_DEBUG CACHE) + +find_package(OpenMP) +if (OPENMP_FOUND) + list(APPEND CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + list(APPEND CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") +endif() + +set(CMAKE_CXX_STANDARD 17) + +if (CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC" CACHE STRING COMPILE_FLAGS FORCE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic-errors -fPIC" CACHE STRING COMPILE_FLAGS FORCE) + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -g -march=native -mtune=native -funroll-loops -DNDEBUG -DBOOST_DISABLE_ASSERTS" CACHE STRING COMPILE_FLAGS FORCE) + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -march=native -mtune=native -DDEBUG" CACHE STRING COMPILE_FLAGS FORCE) +elseif (MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /W4 /utf-8" CACHE STRING COMPILE_FLAGS FORCE) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD /Ox /Ob2 /Oi /Ot /arch:AVX2 /fp:fast /DNDEBUG" CACHE STRING COMPILE_FLAGS FORCE) + # set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd /Od /Ob0 /DDEBUG" CACHE STRING COMPILE_FLAGS FORCE) + + add_definitions(-D_USE_MATH_DEFINES -DNOMINMAX) +endif() + +set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/build) +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) +set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}) + +FIND_PACKAGE(OpenCV REQUIRED) + +option(SILENT_WORK "Remove all imshow and waitKey functions?" OFF) +if (SILENT_WORK) + add_definitions(-DSILENT_WORK) +endif(SILENT_WORK) + +include(CheckIncludeFileCXX) +check_include_file_cxx(filesystem HAVE_FILESYSTEM) +if(HAVE_FILESYSTEM) + message("Founded filesystem header") +else(HAVE_FILESYSTEM) + add_definitions(-DHAVE_EXPERIMENTAL_FILESYSTEM) + message("Do not found filesystem header") +endif(HAVE_FILESYSTEM) + +option(BUILD_EXAMPLES "Should compiled examples (motion detection, pedestrians, faces, DNNs etc)?" ON) +if (BUILD_EXAMPLES) + add_subdirectory(example) +endif(BUILD_EXAMPLES) + + +option(USE_CLIP "Should be used RuCLIP|CLIP for objects classification?" OFF) +if (USE_CLIP) + add_definitions(-DUSE_CLIP) +endif(USE_CLIP) + +option(BUILD_CARS_COUNTING "Should compiled Cars counting example?" OFF) +if (BUILD_CARS_COUNTING) + add_definitions(-DBUILD_CARS_COUNTING) +endif(BUILD_CARS_COUNTING) + +option(BUILD_ASYNC_DETECTOR "Should compiled async example with low fps Detector?" OFF) +if (BUILD_ASYNC_DETECTOR) + add_subdirectory(async_detector) +endif(BUILD_ASYNC_DETECTOR) + +option(BUILD_ONNX_TENSORRT "Should compiled TensorRT binding for ONNX models?" OFF) +if (BUILD_ONNX_TENSORRT) + add_subdirectory(src/Detector/tensorrt_onnx) + add_definitions(-DBUILD_ONNX_TENSORRT) +endif(BUILD_ONNX_TENSORRT) + +option(MTRACKER_PYTHON "Build mtracking Python bindings?" OFF) +if(MTRACKER_PYTHON) + set(NUMPY_INCLUDE_DIR "" CACHE FILEPATH "Path to numpy header if cmake can't find them.") + if (NOT ${NUMPY_INCLUDE_DIR} STREQUAL "") + message( " *** NUMPY_INCLUDE_DIR : ${NUMPY_INCLUDE_DIR}" ) + if(NOT EXISTS ${NUMPY_INCLUDE_DIR}/numpy/ndarrayobject.h) + message(SEND_ERROR "Can't find numpy/ndarrayobject.h in ${NUMPY_INCLUDE_DIR}") + endif() + include_directories(${NUMPY_INCLUDE_DIR}) +endif() + + set(PYBIND11_LTO_CXX_FLAGS "") + set(PYBIND11_PYTHON_VERSION 3) + add_subdirectory(thirdparty/pybind11) +endif(MTRACKER_PYTHON) + + +add_subdirectory(thirdparty) +add_subdirectory(src) + + +# Create CMake config files for distribution +set(INCLUDE_INSTALL_DIR include/ ) +set(LIB_INSTALL_DIR lib/ ) + +install(EXPORT MTTrackingExports + FILE ${PROJECT_NAME}Targets.cmake + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${LIB_INSTALL_DIR}/${PROJECT_NAME}/cmake +) + +include(CMakePackageConfigHelpers) + +set(CONFIG_FILENAME ${PROJECT_NAME}Config.cmake) + +configure_package_config_file(${CONFIG_FILENAME}.in + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_FILENAME} + INSTALL_DESTINATION ${LIB_INSTALL_DIR}/${PROJECT_NAME}/cmake + PATH_VARS INCLUDE_INSTALL_DIR) + +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion ) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_FILENAME} + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + DESTINATION ${LIB_INSTALL_DIR}/${PROJECT_NAME}/cmake ) diff --git a/Detector/BackgroundSubtract.cpp b/Detector/BackgroundSubtract.cpp deleted file mode 100644 index 48c36f274..000000000 --- a/Detector/BackgroundSubtract.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "BackgroundSubtract.h" - -//---------------------------------------------------------------------- -// -//---------------------------------------------------------------------- -BackgroundSubtract::BackgroundSubtract( - BGFG_ALGS algType, - int channels, - int samples, - int pixel_neighbor, - int distance_threshold, - int matching_threshold, - int update_factor - ) - : - m_channels(channels), - m_algType(algType) -{ - for (bool failed = true; failed;) - { - failed = false; - - switch (m_algType) - { - case ALG_VIBE: - m_modelVibe = std::make_unique(m_channels, samples, pixel_neighbor, distance_threshold, matching_threshold, update_factor); - break; - -#ifdef USE_OCV_BGFG - case ALG_MOG: - m_modelOCV = cv::bgsegm::createBackgroundSubtractorMOG(100, 3, 0.7, 0); - break; - - case ALG_GMG: - m_modelOCV = cv::bgsegm::createBackgroundSubtractorGMG(50, 0.7); - break; - - case ALG_CNT: -#if (((CV_VERSION_MAJOR == 3) && (CV_VERSION_MINOR >= 2)) || (CV_VERSION_MAJOR > 3)) - m_modelOCV = cv::bgsegm::createBackgroundSubtractorCNT(15, true, 15 * 60, true); - break; -#else - std::cerr << "OpenCV CNT algorithm is not implemented! Used Vibe by default." << std::endl; - failed = true; - break; -#endif - -#else - case ALG_MOG: - case ALG_GMG: - case ALG_CNT: - std::cerr << "OpenCV bgfg algorithms are not implemented! Used Vibe by default." << std::endl; - failed = true; - break; -#endif - - case ALG_SuBSENSE: - m_modelSuBSENSE = std::make_unique(); // default params - break; - - case ALG_LOBSTER: - m_modelSuBSENSE = std::make_unique(); // default params - break; - - case ALG_MOG2: - m_modelOCV = cv::createBackgroundSubtractorMOG2(500, 16, true).dynamicCast(); - break; - - default: - m_modelVibe = std::make_unique(m_channels, samples, pixel_neighbor, distance_threshold, matching_threshold, update_factor); - break; - } - if (failed) - { - m_algType = ALG_VIBE; - failed = false; - } - } -} - -//---------------------------------------------------------------------- -// -//---------------------------------------------------------------------- -BackgroundSubtract::~BackgroundSubtract() -{ -} - -//---------------------------------------------------------------------- -// -//---------------------------------------------------------------------- -void BackgroundSubtract::subtract(const cv::UMat& image, cv::UMat& foreground) -{ - auto GetImg = [&]() -> cv::UMat - { - if (image.channels() != m_channels) - { - if (image.channels() == 1) - { - cv::UMat newImg; - cv::cvtColor(image, newImg, CV_GRAY2BGR); - return newImg; - } - else if (image.channels() == 3) - { - cv::UMat newImg; - cv::cvtColor(image, newImg, CV_BGR2GRAY); - return newImg; - } - } - return image; - }; - - switch (m_algType) - { - case ALG_VIBE: - m_modelVibe->update(GetImg().getMat(cv::ACCESS_READ)); - foreground = m_modelVibe->getMask().getUMat(cv::ACCESS_READ); - break; - - case ALG_MOG: - case ALG_GMG: - case ALG_CNT: -#ifdef USE_OCV_BGFG - m_modelOCV->apply(GetImg(), foreground); - break; -#else - std::cerr << "OpenCV bgfg algorithms are not implemented!" << std::endl; - break; -#endif - - case ALG_SuBSENSE: - case ALG_LOBSTER: - if (foreground.size() != image.size()) - { - m_modelSuBSENSE->initialize(GetImg().getMat(cv::ACCESS_READ), cv::Mat()); - foreground.create(image.size(), CV_8UC1); - } - else - { - m_modelSuBSENSE->apply(GetImg(), foreground); - } - break; - - case ALG_MOG2: - m_modelOCV->apply(GetImg(), foreground); - cv::threshold(foreground, foreground, 200, 255, cv::THRESH_BINARY); - break; - - default: - m_modelVibe->update(GetImg().getMat(cv::ACCESS_READ)); - foreground = m_modelVibe->getMask().getUMat(cv::ACCESS_READ); - break; - } - - //cv::imshow("before", foreground); - - cv::medianBlur(foreground, foreground, 3); - - cv::Mat dilateElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1)); - cv::dilate(foreground, foreground, dilateElement, cv::Point(-1, -1), 2); - - //cv::imshow("after", foreground); -} diff --git a/Detector/BackgroundSubtract.h b/Detector/BackgroundSubtract.h deleted file mode 100644 index 53a778d05..000000000 --- a/Detector/BackgroundSubtract.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _BACKGROUND_SUBTRACT_H_ -#define _BACKGROUND_SUBTRACT_H_ - -#include "defines.h" -#include "vibe_src/vibe.hpp" -#include "Subsense/BackgroundSubtractorSuBSENSE.h" -#include "Subsense/BackgroundSubtractorLOBSTER.h" - -#ifdef USE_OCV_BGFG -#include -#endif - -class BackgroundSubtract -{ -public: - enum BGFG_ALGS - { - ALG_VIBE, - ALG_MOG, - ALG_GMG, - ALG_CNT, - ALG_SuBSENSE, - ALG_LOBSTER, - ALG_MOG2 - }; - - BackgroundSubtract(BGFG_ALGS algType, int channels = 1, int samples = 20, int pixel_neighbor = 1, int distance_threshold = 20, int matching_threshold = 3, int update_factor = 16); - ~BackgroundSubtract(); - - void subtract(const cv::UMat& image, cv::UMat& foreground); - - int m_channels; - BGFG_ALGS m_algType; - -private: - std::unique_ptr m_modelVibe; - cv::Ptr m_modelOCV; - std::unique_ptr m_modelSuBSENSE; -}; - -#endif diff --git a/Detector/BaseDetector.cpp b/Detector/BaseDetector.cpp deleted file mode 100644 index 8b04b050b..000000000 --- a/Detector/BaseDetector.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "BaseDetector.h" -#include "MotionDetector.h" -#include "FaceDetector.h" -#include "PedestrianDetector.h" -#include "DNNDetector.h" - -/// -/// \brief CreateDetector -/// \param detectorType -/// \param collectPoints -/// \param gray -/// \return -/// -BaseDetector* CreateDetector( - tracking::Detectors detectorType, - const BaseDetector::config_t& config, - bool collectPoints, - cv::UMat& gray - ) -{ - BaseDetector* detector = nullptr; - - switch (detectorType) - { - case tracking::Motion_VIBE: - detector = new MotionDetector(BackgroundSubtract::BGFG_ALGS::ALG_VIBE, collectPoints, gray); - break; - - case tracking::Motion_MOG: - detector = new MotionDetector(BackgroundSubtract::BGFG_ALGS::ALG_MOG, collectPoints, gray); - break; - - case tracking::Motion_GMG: - detector = new MotionDetector(BackgroundSubtract::BGFG_ALGS::ALG_GMG, collectPoints, gray); - break; - - case tracking::Motion_CNT: - detector = new MotionDetector(BackgroundSubtract::BGFG_ALGS::ALG_CNT, collectPoints, gray); - break; - - case tracking::Motion_SuBSENSE: - detector = new MotionDetector(BackgroundSubtract::BGFG_ALGS::ALG_SuBSENSE, collectPoints, gray); - break; - - case tracking::Motion_LOBSTER: - detector = new MotionDetector(BackgroundSubtract::BGFG_ALGS::ALG_LOBSTER, collectPoints, gray); - break; - - case tracking::Motion_MOG2: - detector = new MotionDetector(BackgroundSubtract::BGFG_ALGS::ALG_MOG2, collectPoints, gray); - break; - - case tracking::Face_HAAR: - detector = new FaceDetector(collectPoints, gray); - break; - - case tracking::Pedestrian_HOG: - case tracking::Pedestrian_C4: - detector = new PedestrianDetector(collectPoints, gray); - break; - - case tracking::DNN: - detector = new DNNDetector(collectPoints, gray); - break; - - default: - break; - } - - if (!detector->Init(config)) - { - delete detector; - detector = nullptr; - } - return detector; -} diff --git a/Detector/BaseDetector.h b/Detector/BaseDetector.h deleted file mode 100644 index bbda3d468..000000000 --- a/Detector/BaseDetector.h +++ /dev/null @@ -1,150 +0,0 @@ -#pragma once - -#include -#include -#include -#include "defines.h" - -/// -/// \brief The BaseDetector class -/// -class BaseDetector -{ -public: - /// - /// \brief BaseDetector - /// \param collectPoints - /// \param frame - /// - BaseDetector(bool collectPoints, cv::UMat& frame) - : m_collectPoints(collectPoints) - { - m_minObjectSize.width = std::max(5, frame.cols / 100); - m_minObjectSize.height = m_minObjectSize.width; - } - /// - /// \brief ~BaseDetector - /// - virtual ~BaseDetector(void) - { - } - - typedef std::map config_t; - /// - /// \brief Init - /// \param config - /// - virtual bool Init(const config_t& config) = 0; - - /// - /// \brief Detect - /// \param frame - /// - virtual void Detect(cv::UMat& frame) = 0; - - /// - /// \brief SetMinObjectSize - /// \param minObjectSize - /// - void SetMinObjectSize(cv::Size minObjectSize) - { - m_minObjectSize = minObjectSize; - } - - /// - /// \brief GetDetects - /// \return - /// - const regions_t& GetDetects() const - { - return m_regions; - } - - /// - /// \brief CollectPoints - /// \param region - /// - virtual void CollectPoints(CRegion& region) - { - const int yStep = 5; - const int xStep = 5; - - for (int y = region.m_rect.y, yStop = region.m_rect.y + region.m_rect.height; y < yStop; y += yStep) - { - for (int x = region.m_rect.x, xStop = region.m_rect.x + region.m_rect.width; x < xStop; x += xStep) - { - if (region.m_rect.contains(cv::Point(x, y))) - { - region.m_points.push_back(cv::Point2f(static_cast(x), static_cast(y))); - } - } - } - - if (region.m_points.empty()) - { - region.m_points.push_back(cv::Point2f(region.m_rect.x + 0.5f * region.m_rect.width, region.m_rect.y + 0.5f * region.m_rect.height)); - } - } - - /// - /// \brief CalcMotionMap - /// \param frame - /// - virtual void CalcMotionMap(cv::Mat frame) - { - if (m_motionMap.size() != frame.size()) - { - m_motionMap = cv::Mat(frame.size(), CV_32FC1, cv::Scalar(0, 0, 0)); - } - - cv::Mat foreground(m_motionMap.size(), CV_8UC1, cv::Scalar(0, 0, 0)); - for (const auto& region : m_regions) - { - cv::ellipse(foreground, - cv::RotatedRect((region.m_rect.tl() + region.m_rect.br()) / 2, region.m_rect.size(), 0), - cv::Scalar(255, 255, 255), CV_FILLED); - } - - cv::Mat normFor; - cv::normalize(foreground, normFor, 255, 0, cv::NORM_MINMAX, m_motionMap.type()); - - double alpha = 0.95; - cv::addWeighted(m_motionMap, alpha, normFor, 1 - alpha, 0, m_motionMap); - - const int chans = frame.channels(); - - for (int y = 0; y < frame.rows; ++y) - { - uchar* imgPtr = frame.ptr(y); - float* moPtr = reinterpret_cast(m_motionMap.ptr(y)); - for (int x = 0; x < frame.cols; ++x) - { - for (int ci = chans - 1; ci < chans; ++ci) - { - imgPtr[ci] = cv::saturate_cast(imgPtr[ci] + moPtr[0]); - } - imgPtr += chans; - ++moPtr; - } - } - } - -protected: - regions_t m_regions; - - cv::Size m_minObjectSize; - - bool m_collectPoints; - - cv::Mat m_motionMap; -}; - - -/// -/// \brief CreateDetector -/// \param detectorType -/// \param collectPoints -/// \param gray -/// \return -/// -BaseDetector* CreateDetector(tracking::Detectors detectorType, const BaseDetector::config_t& config, bool collectPoints, cv::UMat& gray); diff --git a/Detector/DNNDetector.cpp b/Detector/DNNDetector.cpp deleted file mode 100644 index 58f678679..000000000 --- a/Detector/DNNDetector.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include "DNNDetector.h" -#include "nms.h" - -/// -/// \brief DNNDetector::DNNDetector -/// \param collectPoints -/// \param gray -/// -DNNDetector::DNNDetector( - bool collectPoints, - cv::UMat& colorFrame - ) - : - BaseDetector(collectPoints, colorFrame), - m_WHRatio(InWidth / (float)InHeight), - m_inScaleFactor(0.007843f), - m_meanVal(127.5), - m_confidenceThreshold(0.2f) -{ - m_classNames = { "background", - "aeroplane", "bicycle", "bird", "boat", - "bottle", "bus", "car", "cat", "chair", - "cow", "diningtable", "dog", "horse", - "motorbike", "person", "pottedplant", - "sheep", "sofa", "train", "tvmonitor" }; -} - -/// -/// \brief DNNDetector::~DNNDetector -/// -DNNDetector::~DNNDetector(void) -{ -} - -/// -/// \brief DNNDetector::Init -/// \return -/// -bool DNNDetector::Init(const config_t& config) -{ - auto modelConfiguration = config.find("modelConfiguration"); - auto modelBinary = config.find("modelBinary"); - if (modelConfiguration != config.end() && modelBinary != config.end()) - { - m_net = cv::dnn::readNetFromCaffe(modelConfiguration->second, modelBinary->second); - } - - auto confidenceThreshold = config.find("confidenceThreshold"); - if (confidenceThreshold != config.end()) - { - m_confidenceThreshold = std::stof(confidenceThreshold->second); - } - - return !m_net.empty(); -} - -/// -/// \brief DNNDetector::Detect -/// \param gray -/// -void DNNDetector::Detect(cv::UMat& colorFrame) -{ - m_regions.clear(); - - regions_t tmpRegions; - - cv::Mat colorMat = colorFrame.getMat(cv::ACCESS_READ); - - if (colorFrame.cols / (float)colorFrame.rows > m_WHRatio) - { - cv::Rect crop(0, 0, cvRound(colorFrame.rows * m_WHRatio), colorFrame.rows); - - for (; crop.x < colorMat.cols; crop.x += crop.width) - { - if (crop.x + crop.width >= colorMat.cols) - { - crop.x = colorMat.cols - crop.width; - } - - DetectInCrop(colorMat, crop, tmpRegions); - } - } - else - { - cv::Rect crop(0, 0, colorFrame.cols, cvRound(colorFrame.cols / m_WHRatio)); - - for (; crop.y < colorMat.rows; crop.y += crop.height) - { - if (crop.y + crop.height >= colorMat.rows) - { - crop.y = colorMat.rows - crop.height; - } - - DetectInCrop(colorMat, crop, tmpRegions); - } - } - - nms3(tmpRegions, m_regions, 0.5f, - [](const CRegion& reg) -> cv::Rect { return reg.m_rect; }, - [](const CRegion& reg) -> float { return reg.m_confidence; }, - 0, 0.f); - - if (m_collectPoints) - { - for (auto& region : m_regions) - { - CollectPoints(region); - } - } -} - -/// -/// \brief DNNDetector::DetectInCrop -/// \param colorFrame -/// \param crop -/// \param tmpRegions -/// -void DNNDetector::DetectInCrop(cv::Mat colorFrame, const cv::Rect& crop, regions_t& tmpRegions) -{ - //Convert Mat to batch of images - cv::Mat inputBlob = cv::dnn::blobFromImage(cv::Mat(colorFrame, crop), m_inScaleFactor, cv::Size(InWidth, InHeight), m_meanVal, false, true); - - m_net.setInput(inputBlob, "data"); //set the network input - - cv::Mat detection = m_net.forward("detection_out"); //compute output - - //std::vector layersTimings; - //double freq = cv::getTickFrequency() / 1000; - //double time = m_net.getPerfProfile(layersTimings) / freq; - - cv::Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr()); - - //cv::Mat frame = colorFrame(crop); - - //ss << "FPS: " << 1000/time << " ; time: " << time << " ms"; - //putText(frame, ss.str(), Point(20,20), 0, 0.5, Scalar(0,0,255)); - //std::cout << "Inference time, ms: " << time << endl; - - //cv::Point correctPoint((colorFrame.cols - crop.width) / 2, (colorFrame.rows - crop.height) / 2); - - for (int i = 0; i < detectionMat.rows; ++i) - { - float confidence = detectionMat.at(i, 2); - - if (confidence > m_confidenceThreshold) - { - size_t objectClass = (size_t)(detectionMat.at(i, 1)); - - int xLeftBottom = cvRound(detectionMat.at(i, 3) * crop.width) + crop.x; - int yLeftBottom = cvRound(detectionMat.at(i, 4) * crop.height) + crop.y; - int xRightTop = cvRound(detectionMat.at(i, 5) * crop.width) + crop.x; - int yRightTop = cvRound(detectionMat.at(i, 6) * crop.height) + crop.y; - - cv::Rect object(xLeftBottom, yLeftBottom, xRightTop - xLeftBottom, yRightTop - yLeftBottom); - - tmpRegions.push_back(CRegion(object, m_classNames[objectClass], confidence)); - - //cv::rectangle(frame, object, Scalar(0, 255, 0)); - //std::string label = classNames[objectClass] + ": " + std::to_string(confidence); - //int baseLine = 0; - //cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); - //cv::rectangle(frame, cv::Rect(cv::Point(xLeftBottom, yLeftBottom - labelSize.height), cv::Size(labelSize.width, labelSize.height + baseLine)), cv::Scalar(255, 255, 255), CV_FILLED); - //cv::putText(frame, label, Point(xLeftBottom, yLeftBottom), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0,0,0)); - } - } -} diff --git a/Detector/DNNDetector.h b/Detector/DNNDetector.h deleted file mode 100644 index b8f94bbd8..000000000 --- a/Detector/DNNDetector.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "BaseDetector.h" - -#include -#include - -/// -/// \brief The DNNDetector class -/// -class DNNDetector : public BaseDetector -{ -public: - DNNDetector(bool collectPoints, cv::UMat& colorFrame); - ~DNNDetector(void); - - bool Init(const config_t& config); - - void Detect(cv::UMat& colorFrame); - -private: - cv::dnn::Net m_net; - - void DetectInCrop(cv::Mat colorFrame, const cv::Rect& crop, regions_t& tmpRegions); - - static const int InWidth = 300; - static const int InHeight = 300; - float m_WHRatio; - float m_inScaleFactor; - float m_meanVal; - float m_confidenceThreshold; - std::vector m_classNames; -}; diff --git a/Detector/FaceDetector.cpp b/Detector/FaceDetector.cpp deleted file mode 100644 index f6877dae5..000000000 --- a/Detector/FaceDetector.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "FaceDetector.h" - -/// -/// \brief FaceDetector::FaceDetector -/// \param collectPoints -/// \param gray -/// -FaceDetector::FaceDetector( - bool collectPoints, - cv::UMat& gray - ) - : BaseDetector(collectPoints, gray) -{ -} - -/// -/// \brief FaceDetector::~FaceDetector -/// -FaceDetector::~FaceDetector(void) -{ -} - -/// -/// \brief FaceDetector::Init -/// \param cascadeFileName -/// \return -/// -bool FaceDetector::Init(const config_t& config) -{ - auto cascadeFileName = config.find("cascadeFileName"); - if (cascadeFileName != config.end() && - (!m_cascade.load(cascadeFileName->second) || m_cascade.empty())) - { - std::cerr << "Cascade " << cascadeFileName->second << " not opened!" << std::endl; - return false; - } - return true; -} - -/// -/// \brief FaceDetector::Detect -/// \param gray -/// -void FaceDetector::Detect(cv::UMat& gray) -{ - bool findLargestObject = false; - bool filterRects = true; - std::vector faceRects; - m_cascade.detectMultiScale(gray, - faceRects, - 1.1, - (filterRects || findLargestObject) ? 3 : 0, - findLargestObject ? cv::CASCADE_FIND_BIGGEST_OBJECT : 0, - m_minObjectSize, - cv::Size(gray.cols / 2, gray.rows / 2)); - m_regions.clear(); - for (auto rect : faceRects) - { - m_regions.push_back(rect); - } - - if (m_collectPoints) - { - for (auto& region : m_regions) - { - CollectPoints(region); - } - } -} diff --git a/Detector/FaceDetector.h b/Detector/FaceDetector.h deleted file mode 100644 index 71a8dfa11..000000000 --- a/Detector/FaceDetector.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "BaseDetector.h" - -/// -/// \brief The FaceDetector class -/// -class FaceDetector : public BaseDetector -{ -public: - FaceDetector(bool collectPoints, cv::UMat& gray); - ~FaceDetector(void); - - bool Init(const config_t& config); - - void Detect(cv::UMat& gray); - -private: - cv::CascadeClassifier m_cascade; -}; diff --git a/Detector/MotionDetector.cpp b/Detector/MotionDetector.cpp deleted file mode 100644 index aadb32ac9..000000000 --- a/Detector/MotionDetector.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "MotionDetector.h" - -/// -/// \brief MotionDetector::MotionDetector -/// \param algType -/// \param collectPoints -/// \param gray -/// -MotionDetector::MotionDetector( - BackgroundSubtract::BGFG_ALGS algType, - bool collectPoints, - cv::UMat& gray - ) - : BaseDetector(collectPoints, gray) -{ - m_fg = gray.clone(); - m_backgroundSubst = std::make_unique(algType, gray.channels()); -} - -/// -/// \brief MotionDetector::~MotionDetector -/// -MotionDetector::~MotionDetector(void) -{ -} - -/// -/// \brief MotionDetector::Init -/// \param config -/// \return -/// -bool MotionDetector::Init(const config_t& config) -{ - return true; -} - -/// -/// \brief MotionDetector::DetectContour -/// -void MotionDetector::DetectContour() -{ - m_regions.clear(); - std::vector> contours; - std::vector hierarchy; - cv::findContours(m_fg, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cv::Point()); - if (contours.size() > 0) - { - for (size_t i = 0; i < contours.size(); i++) - { - cv::Rect r = cv::boundingRect(contours[i]); - - if (r.width >= m_minObjectSize.width && - r.height >= m_minObjectSize.height) - { - CRegion region(r); - cv::Point2f center(r.x + 0.5f * r.width, r.y + 0.5f * r.height); - - if (m_collectPoints) - { - const int yStep = 5; - const int xStep = 5; - - for (int y = r.y; y < r.y + r.height; y += yStep) - { - cv::Point2f pt(0, static_cast(y)); - for (int x = r.x; x < r.x + r.width; x += xStep) - { - pt.x = static_cast(x); - if (cv::pointPolygonTest(contours[i], pt, false) > 0) - { - region.m_points.push_back(pt); - } - } - } - - if (region.m_points.empty()) - { - region.m_points.push_back(center); - } - } - - m_regions.push_back(region); - } - } - } -} - -/// -/// \brief MotionDetector::Detect -/// \param gray -/// -void MotionDetector::Detect(cv::UMat& gray) -{ - m_backgroundSubst->subtract(gray, m_fg); - - DetectContour(); -} - -/// -/// \brief MotionDetector::CalcMotionMap -/// \param frame -/// -void MotionDetector::CalcMotionMap(cv::Mat frame) -{ - if (m_motionMap.size() != frame.size()) - { - m_motionMap = cv::Mat(frame.size(), CV_32FC1, cv::Scalar(0, 0, 0)); - } - - cv::Mat normFor; - cv::normalize(m_fg, normFor, 255, 0, cv::NORM_MINMAX, m_motionMap.type()); - - double alpha = 0.95; - cv::addWeighted(m_motionMap, alpha, normFor, 1 - alpha, 0, m_motionMap); - - const int chans = frame.channels(); - - for (int y = 0; y < frame.rows; ++y) - { - uchar* imgPtr = frame.ptr(y); - float* moPtr = reinterpret_cast(m_motionMap.ptr(y)); - for (int x = 0; x < frame.cols; ++x) - { - for (int ci = chans - 1; ci < chans; ++ci) - { - imgPtr[ci] = cv::saturate_cast(imgPtr[ci] + moPtr[0]); - } - imgPtr += chans; - ++moPtr; - } - } -} diff --git a/Detector/MotionDetector.h b/Detector/MotionDetector.h deleted file mode 100644 index 0f45d6136..000000000 --- a/Detector/MotionDetector.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "BaseDetector.h" -#include "BackgroundSubtract.h" - -/// -/// \brief The MotionDetector class -/// -class MotionDetector : public BaseDetector -{ -public: - MotionDetector(BackgroundSubtract::BGFG_ALGS algType, bool collectPoints, cv::UMat& gray); - ~MotionDetector(void); - - bool Init(const config_t& config); - - void Detect(cv::UMat& gray); - - void CalcMotionMap(cv::Mat frame); - -private: - void DetectContour(); - - std::unique_ptr m_backgroundSubst; - - cv::UMat m_fg; -}; diff --git a/Detector/PedestrianDetector.cpp b/Detector/PedestrianDetector.cpp deleted file mode 100644 index b6c1ac20c..000000000 --- a/Detector/PedestrianDetector.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "PedestrianDetector.h" -#include "nms.h" - -/// -/// \brief PedestrianDetector::PedestrianDetector -/// \param collectPoints -/// \param gray -/// -PedestrianDetector::PedestrianDetector( - bool collectPoints, - cv::UMat& gray - ) - : - BaseDetector(collectPoints, gray), - m_scannerC4(HUMAN_height, HUMAN_width, HUMAN_xdiv, HUMAN_ydiv, 256, 0.8) -{ -} - -/// -/// \brief PedestrianDetector::~PedestrianDetector -/// -PedestrianDetector::~PedestrianDetector(void) -{ -} - -/// -/// \brief PedestrianDetector::Init -/// \param cascadeFileName -/// \return -/// -bool PedestrianDetector::Init(const config_t& config) -{ - auto detectorType = config.find("detectorType"); - - if (detectorType == config.end()) - { - m_detectorType = HOG; - } - else - { - m_detectorType = (detectorType->second == "HOG") ? HOG : C4; - } - - switch (m_detectorType) - { - case HOG: - m_hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector()); - return true; - - case C4: - { - auto cascadeFileName1 = config.find("cascadeFileName1"); - auto cascadeFileName2 = config.find("cascadeFileName2"); - if (cascadeFileName1 == config.end() || cascadeFileName2 == config.end()) - { - return false; - } - else - { - LoadCascade(cascadeFileName1->second, cascadeFileName2->second, m_scannerC4); - return true; - } - } - - default: - return false; - } - - return false; -} - -/// -/// \brief PedestrianDetector::Detect -/// \param gray -/// -void PedestrianDetector::Detect(cv::UMat& gray) -{ - std::vector foundRects; - std::vector filteredRects; - - int neighbors = 0; - if (m_detectorType == HOG) - { - m_hog.detectMultiScale(gray, foundRects, 0, cv::Size(8, 8), cv::Size(32, 32), 1.05, 4, false); - } - else - { - IntImage original; - original.Load(gray.getMat(cv::ACCESS_READ)); - - m_scannerC4.FastScan(original, foundRects, 2); - neighbors = 1; - } - - nms(foundRects, filteredRects, 0.3f, neighbors); - - m_regions.clear(); - for (auto rect : filteredRects) - { - rect.x += cvRound(rect.width * 0.1f); - rect.width = cvRound(rect.width * 0.8f); - rect.y += cvRound(rect.height * 0.07f); - rect.height = cvRound(rect.height * 0.8f); - - m_regions.push_back(rect); - } - - if (m_collectPoints) - { - for (auto& region : m_regions) - { - CollectPoints(region); - } - } -} diff --git a/Detector/PedestrianDetector.h b/Detector/PedestrianDetector.h deleted file mode 100644 index 76a437777..000000000 --- a/Detector/PedestrianDetector.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "BaseDetector.h" -#include "Detector/pedestrians/c4-pedestrian-detector.h" - -/// -/// \brief The PedestrianDetector class -/// -class PedestrianDetector : public BaseDetector -{ -public: - enum DetectorTypes - { - HOG, - C4 - }; - - PedestrianDetector(bool collectPoints, cv::UMat& gray); - ~PedestrianDetector(void); - - bool Init(const config_t& config); - - void Detect(cv::UMat& gray); - -private: - DetectorTypes m_detectorType; - - /// - /// \brief m_hog - /// HOG detector - /// - cv::HOGDescriptor m_hog; - - /// - /// \brief m_scannerC4 - /// C4 detector - /// - DetectionScanner m_scannerC4; - static const int HUMAN_height = 108; - static const int HUMAN_width = 36; - static const int HUMAN_xdiv = 9; - static const int HUMAN_ydiv = 4; -}; diff --git a/Detector/Subsense/BackgroundSubtractorLBSP.cpp b/Detector/Subsense/BackgroundSubtractorLBSP.cpp deleted file mode 100644 index cf903286c..000000000 --- a/Detector/Subsense/BackgroundSubtractorLBSP.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "BackgroundSubtractorLBSP.h" -#include "DistanceUtils.h" -#include "RandUtils.h" -#include -#include -#include -#include -#include - -// local define used to determine the default median blur kernel size -#define DEFAULT_MEDIAN_BLUR_KERNEL_SIZE (9) - -BackgroundSubtractorLBSP::BackgroundSubtractorLBSP(float fRelLBSPThreshold, size_t nLBSPThresholdOffset) - : m_nImgChannels(0) - ,m_nImgType(0) - ,m_nLBSPThresholdOffset(nLBSPThresholdOffset) - ,m_fRelLBSPThreshold(fRelLBSPThreshold) - ,m_nTotPxCount(0) - ,m_nTotRelevantPxCount(0) - ,m_nFrameIndex(SIZE_MAX) - ,m_nFramesSinceLastReset(0) - ,m_nModelResetCooldown(0) - ,m_aPxIdxLUT(nullptr) - ,m_aPxInfoLUT(nullptr) - ,m_nDefaultMedianBlurKernelSize(DEFAULT_MEDIAN_BLUR_KERNEL_SIZE) - ,m_bInitialized(false) - ,m_bAutoModelResetEnabled(true) - ,m_bUsingMovingCamera(false) - ,nDebugCoordX(0),nDebugCoordY(0) { - CV_Assert(m_fRelLBSPThreshold>=0); -} - -BackgroundSubtractorLBSP::~BackgroundSubtractorLBSP() {} - -void BackgroundSubtractorLBSP::initialize(const cv::Mat& oInitImg) { - this->initialize(oInitImg,cv::Mat()); -} - -//cv::Algorithm* BackgroundSubtractorLBSP::info() const { -// return nullptr; -//} - -cv::Mat BackgroundSubtractorLBSP::getROICopy() const { - return m_oROI.clone(); -} - -void BackgroundSubtractorLBSP::setROI(cv::Mat& oROI) { - LBSP::validateROI(oROI); - CV_Assert(cv::countNonZero(oROI)>0); - if(m_bInitialized) { - cv::Mat oLatestBackgroundImage; - getBackgroundImage(oLatestBackgroundImage); - initialize(oLatestBackgroundImage,oROI); - } - else - m_oROI = oROI.clone(); -} - -void BackgroundSubtractorLBSP::setAutomaticModelReset(bool bVal) { - m_bAutoModelResetEnabled = bVal; -} diff --git a/Detector/Subsense/BackgroundSubtractorLBSP.h b/Detector/Subsense/BackgroundSubtractorLBSP.h deleted file mode 100644 index 72b26ff6f..000000000 --- a/Detector/Subsense/BackgroundSubtractorLBSP.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include -#include -#include "LBSP.h" - -/*! - Local Binary Similarity Pattern (LBSP)-based change detection algorithm (abstract version/base class). - - For more details on the different parameters, see P.-L. St-Charles and G.-A. Bilodeau, "Improving Background - Subtraction using Local Binary Similarity Patterns", in WACV 2014, or G.-A. Bilodeau et al, "Change Detection - in Feature Space Using Local Binary Similarity Patterns", in CRV 2013. - - This algorithm is currently NOT thread-safe. - */ -class BackgroundSubtractorLBSP : public cv::BackgroundSubtractor { -public: - //! full constructor - BackgroundSubtractorLBSP(float fRelLBSPThreshold, size_t nLBSPThresholdOffset=0); - //! default destructor - virtual ~BackgroundSubtractorLBSP(); - //! (re)initiaization method; needs to be called before starting background subtraction - virtual void initialize(const cv::Mat& oInitImg); - //! (re)initiaization method; needs to be called before starting background subtraction - virtual void initialize(const cv::Mat& oInitImg, const cv::Mat& oROI)=0; - //! primary model update function; the learning param is used to override the internal learning speed (ignored when <= 0) - virtual void operator()(cv::InputArray image, cv::OutputArray fgmask, double learningRate=0)=0; - //! unused, always returns nullptr - //virtual cv::Algorithm* info() const; - //! returns a copy of the ROI used for descriptor extraction - virtual cv::Mat getROICopy() const; - //! sets the ROI to be used for descriptor extraction (note: this function will reinit the model and return the usable ROI) - virtual void setROI(cv::Mat& oROI); - //! turns automatic model reset on or off - void setAutomaticModelReset(bool); - -protected: - struct PxInfoBase { - int nImgCoord_Y; - int nImgCoord_X; - size_t nModelIdx; - }; - //! background model ROI used for LBSP descriptor extraction (specific to the input image size) - cv::Mat m_oROI; - //! input image size - cv::Size m_oImgSize; - //! input image channel size - size_t m_nImgChannels; - //! input image type - int m_nImgType; - //! LBSP internal threshold offset value, used to reduce texture noise in dark regions - const size_t m_nLBSPThresholdOffset; - //! LBSP relative internal threshold (kept here since we don't keep an LBSP object) - const float m_fRelLBSPThreshold; - //! total number of pixels (depends on the input frame size) & total number of relevant pixels - size_t m_nTotPxCount, m_nTotRelevantPxCount; - //! current frame index, frame count since last model reset & model reset cooldown counters - size_t m_nFrameIndex, m_nFramesSinceLastReset, m_nModelResetCooldown; - //! pre-allocated internal LBSP threshold values LUT for all possible 8-bit intensities - size_t m_anLBSPThreshold_8bitLUT[UCHAR_MAX+1]; - //! internal pixel index LUT for all relevant analysis regions (based on the provided ROI) - size_t* m_aPxIdxLUT; - //! internal pixel info LUT for all possible pixel indexes - PxInfoBase* m_aPxInfoLUT; - //! default kernel size for median blur post-proc filtering - const int m_nDefaultMedianBlurKernelSize; - //! specifies whether the algorithm is fully initialized or not - bool m_bInitialized; - //! specifies whether automatic model resets are enabled or not - bool m_bAutoModelResetEnabled; - //! specifies whether the camera is considered moving or not - bool m_bUsingMovingCamera; - //! copy of latest pixel intensities (used when refreshing model) - cv::Mat m_oLastColorFrame; - //! copy of latest descriptors (used when refreshing model) - cv::Mat m_oLastDescFrame; - //! the foreground mask generated by the method at [t-1] - cv::Mat m_oLastFGMask; - -public: - // ######## DEBUG PURPOSES ONLY ########## - int nDebugCoordX, nDebugCoordY; - std::string sDebugName; -}; - diff --git a/Detector/Subsense/BackgroundSubtractorLOBSTER.cpp b/Detector/Subsense/BackgroundSubtractorLOBSTER.cpp deleted file mode 100755 index 09187349c..000000000 --- a/Detector/Subsense/BackgroundSubtractorLOBSTER.cpp +++ /dev/null @@ -1,335 +0,0 @@ -#include "BackgroundSubtractorLOBSTER.h" -#include "DistanceUtils.h" -#include "RandUtils.h" -#include -#include -#include -#include - -BackgroundSubtractorLOBSTER::BackgroundSubtractorLOBSTER( float fRelLBSPThreshold - ,size_t nLBSPThresholdOffset - ,size_t nDescDistThreshold - ,size_t nColorDistThreshold - ,size_t nBGSamples - ,size_t nRequiredBGSamples) - : BackgroundSubtractorLBSP(fRelLBSPThreshold,nLBSPThresholdOffset) - ,m_nColorDistThreshold(nColorDistThreshold) - ,m_nDescDistThreshold(nDescDistThreshold) - ,m_nBGSamples(nBGSamples) - ,m_nRequiredBGSamples(nRequiredBGSamples) { - CV_Assert(m_nRequiredBGSamples<=m_nBGSamples); - m_bAutoModelResetEnabled = false; // @@@@@@ not supported here for now -} - -BackgroundSubtractorLOBSTER::~BackgroundSubtractorLOBSTER() { - if(m_aPxIdxLUT) - delete[] m_aPxIdxLUT; - if(m_aPxInfoLUT) - delete[] m_aPxInfoLUT; -} - -void BackgroundSubtractorLOBSTER::initialize(const cv::Mat& oInitImg, const cv::Mat& oROI) { - CV_Assert(!oInitImg.empty() && oInitImg.cols>0 && oInitImg.rows>0); - CV_Assert(oInitImg.isContinuous()); - CV_Assert(oInitImg.type()==CV_8UC1 || oInitImg.type()==CV_8UC3); - if(oInitImg.type()==CV_8UC3) { - std::vector voInitImgChannels; - cv::split(oInitImg,voInitImgChannels); - if(!cv::countNonZero((voInitImgChannels[0]!=voInitImgChannels[1])|(voInitImgChannels[2]!=voInitImgChannels[1]))) - std::cout << std::endl << "\tBackgroundSubtractorLOBSTER : Warning, grayscale images should always be passed in CV_8UC1 format for optimal performance." << std::endl; - } - cv::Mat oNewBGROI; - if(oROI.empty() && (m_oROI.empty() || oROI.size()!=oInitImg.size())) { - oNewBGROI.create(oInitImg.size(),CV_8UC1); - oNewBGROI = cv::Scalar_(UCHAR_MAX); - } - else if(oROI.empty()) - oNewBGROI = m_oROI; - else { - CV_Assert(oROI.size()==oInitImg.size() && oROI.type()==CV_8UC1); - CV_Assert(cv::countNonZero((oROI0))==0); - oNewBGROI = oROI.clone(); - } - LBSP::validateROI(oNewBGROI); - const size_t nROIPxCount = (size_t)cv::countNonZero(oNewBGROI); - CV_Assert(nROIPxCount>0); - m_oROI = oNewBGROI; - m_oImgSize = oInitImg.size(); - m_nImgType = oInitImg.type(); - m_nImgChannels = oInitImg.channels(); - m_nTotPxCount = m_oImgSize.area(); - m_nTotRelevantPxCount = nROIPxCount; - m_nFrameIndex = 0; - m_nFramesSinceLastReset = 0; - m_nModelResetCooldown = 0; - m_oLastFGMask.create(m_oImgSize,CV_8UC1); - m_oLastFGMask = cv::Scalar_(0); - m_oLastColorFrame.create(m_oImgSize,CV_8UC((int)m_nImgChannels)); - m_oLastColorFrame = cv::Scalar_::all(0); - m_oLastDescFrame.create(m_oImgSize,CV_16UC((int)m_nImgChannels)); - m_oLastDescFrame = cv::Scalar_::all(0); - m_voBGColorSamples.resize(m_nBGSamples); - m_voBGDescSamples.resize(m_nBGSamples); - for(size_t s=0; s::all(0); - m_voBGDescSamples[s].create(m_oImgSize,CV_16UC((int)m_nImgChannels)); - m_voBGDescSamples[s] = cv::Scalar_::all(0); - } - if(m_aPxIdxLUT) - delete[] m_aPxIdxLUT; - if(m_aPxInfoLUT) - delete[] m_aPxInfoLUT; - m_aPxIdxLUT = new size_t[m_nTotRelevantPxCount]; - m_aPxInfoLUT = new PxInfoBase[m_nTotPxCount]; - if(m_nImgChannels==1) { - CV_Assert(m_oLastColorFrame.step.p[0]==(size_t)m_oImgSize.width && m_oLastColorFrame.step.p[1]==1); - CV_Assert(m_oLastDescFrame.step.p[0]==m_oLastColorFrame.step.p[0]*2 && m_oLastDescFrame.step.p[1]==m_oLastColorFrame.step.p[1]*2); - for(size_t t=0; t<=UCHAR_MAX; ++t) - m_anLBSPThreshold_8bitLUT[t] = cv::saturate_cast((t*m_fRelLBSPThreshold+m_nLBSPThresholdOffset)/2); - for(size_t nPxIter=0, nModelIter=0; nPxIter(t*m_fRelLBSPThreshold+m_nLBSPThresholdOffset); - for(size_t nPxIter=0, nModelIter=0; nPxIter0.0f && fSamplesRefreshFrac<=1.0f); - const size_t nModelsToRefresh = fSamplesRefreshFrac<1.0f?(size_t)(fSamplesRefreshFrac*m_nBGSamples):m_nBGSamples; - const size_t nRefreshStartPos = fSamplesRefreshFrac<1.0f?rand()%m_nBGSamples:0; - if(m_nImgChannels==1) { - for(size_t nModelIter=0; nModelIter0); - cv::Mat oInputImg = _image.getMat(); - CV_Assert(oInputImg.type()==m_nImgType && oInputImg.size()==m_oImgSize); - CV_Assert(oInputImg.isContinuous()); - _fgmask.create(m_oImgSize,CV_8UC1); - cv::Mat oCurrFGMask = _fgmask.getMat(); - oCurrFGMask = cv::Scalar_(0); - const size_t nLearningRate = (size_t)ceil(learningRate); - if(m_nImgChannels==1) { - for(size_t nModelIter=0; nModelIterm_nColorDistThreshold/2) - goto failedcheck1ch; - LBSP::computeGrayscaleDescriptor(oInputImg,nBGColor,nCurrImgCoord_X,nCurrImgCoord_Y,m_anLBSPThreshold_8bitLUT[nBGColor],nCurrInputDesc); - const size_t nDescDist = hdist(nCurrInputDesc,*((ushort*)(m_voBGDescSamples[nModelIdx].data+nDescIter))); - if(nDescDist>m_nDescDistThreshold) - goto failedcheck1ch; - nGoodSamplesCount++; - } - failedcheck1ch: - nModelIdx++; - } - if(nGoodSamplesCount(nSampleImgCoord_Y,nSampleImgCoord_X); - LBSP::computeGrayscaleDescriptor(oInputImg,nCurrColor,nCurrImgCoord_X,nCurrImgCoord_Y,m_anLBSPThreshold_8bitLUT[nCurrColor],nRandInputDesc); - m_voBGColorSamples[nSampleModelIdx].at(nSampleImgCoord_Y,nSampleImgCoord_X) = nCurrColor; - } - } - } - } - else { //m_nImgChannels==3 - const size_t nCurrDescDistThreshold = m_nDescDistThreshold*3; - const size_t nCurrColorDistThreshold = m_nColorDistThreshold*3; - const size_t nCurrSCDescDistThreshold = nCurrDescDistThreshold/2; - const size_t nCurrSCColorDistThreshold = nCurrColorDistThreshold/2; - const size_t desc_row_step = m_voBGDescSamples[0].step.p[0]; - const size_t img_row_step = m_voBGColorSamples[0].step.p[0]; - for(size_t nModelIter=0; nModelIternCurrSCColorDistThreshold) - goto failedcheck3ch; - LBSP::computeSingleRGBDescriptor(oInputImg,anBGColor[c],nCurrImgCoord_X,nCurrImgCoord_Y,c,m_anLBSPThreshold_8bitLUT[anBGColor[c]],anCurrInputDesc[c]); - const size_t nDescDist = hdist(anCurrInputDesc[c],anBGDesc[c]); - if(nDescDist>nCurrSCDescDistThreshold) - goto failedcheck3ch; - nTotColorDist += nColorDist; - nTotDescDist += nDescDist; - } - if(nTotDescDist<=nCurrDescDistThreshold && nTotColorDist<=nCurrColorDistThreshold) - nGoodSamplesCount++; - failedcheck3ch: - nModelIdx++; - } - if(nGoodSamplesCount 0 (smaller values == faster adaptation) - virtual void operator()(cv::InputArray image, cv::OutputArray fgmask, double learningRate=BGSLOBSTER_DEFAULT_LEARNING_RATE); - //! returns a copy of the latest reconstructed background image - void getBackgroundImage(cv::OutputArray backgroundImage) const; - //! returns a copy of the latest reconstructed background descriptors image - virtual void getBackgroundDescriptorsImage(cv::OutputArray backgroundDescImage) const; - //! compute foreground mask - virtual void apply(cv::InputArray image, cv::OutputArray fgmask, double learningRateOverride=BGSLOBSTER_DEFAULT_LEARNING_RATE); - -protected: - //! absolute color distance threshold - const size_t m_nColorDistThreshold; - //! absolute descriptor distance threshold - const size_t m_nDescDistThreshold; - //! number of different samples per pixel/block to be taken from input frames to build the background model - const size_t m_nBGSamples; - //! number of similar samples needed to consider the current pixel/block as 'background' - const size_t m_nRequiredBGSamples; - //! background model pixel intensity samples - std::vector m_voBGColorSamples; - //! background model descriptors samples - std::vector m_voBGDescSamples; -}; - diff --git a/Detector/Subsense/BackgroundSubtractorSuBSENSE.cpp b/Detector/Subsense/BackgroundSubtractorSuBSENSE.cpp deleted file mode 100644 index aca011245..000000000 --- a/Detector/Subsense/BackgroundSubtractorSuBSENSE.cpp +++ /dev/null @@ -1,743 +0,0 @@ -#include "BackgroundSubtractorSuBSENSE.h" -#include "DistanceUtils.h" -#include "RandUtils.h" -#include -#include -#include -#include - -/* - * - * Intrinsic parameters for our method are defined here; tuning these for better - * performance should not be required in most cases -- although improvements in - * very specific scenarios are always possible. - * - */ -//! defines the threshold value(s) used to detect long-term ghosting and trigger the fast edge-based absorption heuristic -#define GHOSTDET_D_MAX (0.010f) // defines 'negligible' change here -#define GHOSTDET_S_MIN (0.995f) // defines the required minimum local foreground saturation value -//! parameter used to scale dynamic distance threshold adjustments ('R(x)') -#define FEEDBACK_R_VAR (0.01f) -//! parameters used to adjust the variation step size of 'v(x)' -#define FEEDBACK_V_INCR (1.000f) -#define FEEDBACK_V_DECR (0.100f) -//! parameters used to scale dynamic learning rate adjustments ('T(x)') -#define FEEDBACK_T_DECR (0.2500f) -#define FEEDBACK_T_INCR (0.5000f) -#define FEEDBACK_T_LOWER (2.0000f) -#define FEEDBACK_T_UPPER (256.00f) -//! parameters used to define 'unstable' regions, based on segm noise/bg dynamics and local dist threshold values -#define UNSTABLE_REG_RATIO_MIN (0.100f) -#define UNSTABLE_REG_RDIST_MIN (3.000f) -//! parameters used to scale the relative LBSP intensity threshold used for internal comparisons -#define LBSPDESC_NONZERO_RATIO_MIN (0.100f) -#define LBSPDESC_NONZERO_RATIO_MAX (0.500f) -//! parameters used to define model reset/learning rate boosts in our frame-level component -#define FRAMELEVEL_MIN_COLOR_DIFF_THRESHOLD (m_nMinColorDistThreshold/2) -#define FRAMELEVEL_ANALYSIS_DOWNSAMPLE_RATIO (8) - -// local define used to display debug information -#define DISPLAY_SUBSENSE_DEBUG_INFO 0 -// local define used to specify the default frame size (320x240 = QVGA) -#define DEFAULT_FRAME_SIZE cv::Size(320,240) -// local define used to specify the color dist threshold offset used for unstable regions -#define STAB_COLOR_DIST_OFFSET (m_nMinColorDistThreshold/5) -// local define used to specify the desc dist threshold offset used for unstable regions -#define UNSTAB_DESC_DIST_OFFSET (m_nDescDistThresholdOffset) - -static const size_t s_nColorMaxDataRange_1ch = UCHAR_MAX; -static const size_t s_nDescMaxDataRange_1ch = LBSP::DESC_SIZE*8; -static const size_t s_nColorMaxDataRange_3ch = s_nColorMaxDataRange_1ch*3; -static const size_t s_nDescMaxDataRange_3ch = s_nDescMaxDataRange_1ch*3; - -BackgroundSubtractorSuBSENSE::BackgroundSubtractorSuBSENSE( float fRelLBSPThreshold - ,size_t nDescDistThresholdOffset - ,size_t nMinColorDistThreshold - ,size_t nBGSamples - ,size_t nRequiredBGSamples - ,size_t nSamplesForMovingAvgs) - : BackgroundSubtractorLBSP(fRelLBSPThreshold) - ,m_nMinColorDistThreshold(nMinColorDistThreshold) - ,m_nDescDistThresholdOffset(nDescDistThresholdOffset) - ,m_nBGSamples(nBGSamples) - ,m_nRequiredBGSamples(nRequiredBGSamples) - ,m_nSamplesForMovingAvgs(nSamplesForMovingAvgs) - ,m_fLastNonZeroDescRatio(0.0f) - ,m_bLearningRateScalingEnabled(true) - ,m_fCurrLearningRateLowerCap(FEEDBACK_T_LOWER) - ,m_fCurrLearningRateUpperCap(FEEDBACK_T_UPPER) - ,m_nMedianBlurKernelSize(m_nDefaultMedianBlurKernelSize) - ,m_bUse3x3Spread(true) - ,m_defaultMorphologyKernel(cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3))) { - CV_Assert(m_nBGSamples>0 && m_nRequiredBGSamples<=m_nBGSamples); - CV_Assert(m_nMinColorDistThreshold>=STAB_COLOR_DIST_OFFSET); -} - -BackgroundSubtractorSuBSENSE::~BackgroundSubtractorSuBSENSE() { - if(m_aPxIdxLUT) - delete[] m_aPxIdxLUT; - if(m_aPxInfoLUT) - delete[] m_aPxInfoLUT; -} - -void BackgroundSubtractorSuBSENSE::initialize(const cv::Mat& oInitImg, const cv::Mat& oROI) { - // == init - CV_Assert(!oInitImg.empty() && oInitImg.cols>0 && oInitImg.rows>0); - CV_Assert(oInitImg.isContinuous()); - CV_Assert(oInitImg.type()==CV_8UC3 || oInitImg.type()==CV_8UC1); - if(oInitImg.type()==CV_8UC3) { - std::vector voInitImgChannels; - cv::split(oInitImg,voInitImgChannels); - if(!cv::countNonZero((voInitImgChannels[0]!=voInitImgChannels[1])|(voInitImgChannels[2]!=voInitImgChannels[1]))) - std::cout << std::endl << "\tBackgroundSubtractorSuBSENSE : Warning, grayscale images should always be passed in CV_8UC1 format for optimal performance." << std::endl; - } - cv::Mat oNewBGROI; - if(oROI.empty() && (m_oROI.empty() || oROI.size()!=oInitImg.size())) { - oNewBGROI.create(oInitImg.size(),CV_8UC1); - oNewBGROI = cv::Scalar_(UCHAR_MAX); - } - else if(oROI.empty()) - oNewBGROI = m_oROI; - else { - CV_Assert(oROI.size()==oInitImg.size() && oROI.type()==CV_8UC1); - CV_Assert(cv::countNonZero((oROI0))==0); - oNewBGROI = oROI.clone(); - cv::Mat oTempROI; - cv::dilate(oNewBGROI,oTempROI,m_defaultMorphologyKernel,cv::Point(-1,-1),LBSP::PATCH_SIZE/2); - cv::bitwise_or(oNewBGROI,oTempROI/2,oNewBGROI); - } - const size_t nOrigROIPxCount = (size_t)cv::countNonZero(oNewBGROI); - CV_Assert(nOrigROIPxCount>0); - LBSP::validateROI(oNewBGROI); - const size_t nFinalROIPxCount = (size_t)cv::countNonZero(oNewBGROI); - CV_Assert(nFinalROIPxCount>0); - m_oROI = oNewBGROI; - m_oImgSize = oInitImg.size(); - m_nImgType = oInitImg.type(); - m_nImgChannels = oInitImg.channels(); - m_nTotPxCount = m_oImgSize.area(); - m_nTotRelevantPxCount = nFinalROIPxCount; - m_nFrameIndex = 0; - m_nFramesSinceLastReset = 0; - m_nModelResetCooldown = 0; - m_fLastNonZeroDescRatio = 0.0f; - const int nTotImgPixels = m_oImgSize.height*m_oImgSize.width; - if(nOrigROIPxCount>=m_nTotPxCount/2 && (int)m_nTotPxCount>=DEFAULT_FRAME_SIZE.area()) { - m_bLearningRateScalingEnabled = true; - m_bAutoModelResetEnabled = true; - m_bUse3x3Spread = !(nTotImgPixels>DEFAULT_FRAME_SIZE.area()*2); - const int nRawMedianBlurKernelSize = std::min((int)floor((float)nTotImgPixels/DEFAULT_FRAME_SIZE.area()+0.5f)+m_nDefaultMedianBlurKernelSize,14); - m_nMedianBlurKernelSize = (nRawMedianBlurKernelSize%2)?nRawMedianBlurKernelSize:nRawMedianBlurKernelSize-1; - m_fCurrLearningRateLowerCap = FEEDBACK_T_LOWER; - m_fCurrLearningRateUpperCap = FEEDBACK_T_UPPER; - } - else { - m_bLearningRateScalingEnabled = false; - m_bAutoModelResetEnabled = false; - m_bUse3x3Spread = true; - m_nMedianBlurKernelSize = m_nDefaultMedianBlurKernelSize; - m_fCurrLearningRateLowerCap = FEEDBACK_T_LOWER*2; - m_fCurrLearningRateUpperCap = FEEDBACK_T_UPPER*2; - } - m_oUpdateRateFrame.create(m_oImgSize,CV_32FC1); - m_oUpdateRateFrame = cv::Scalar(m_fCurrLearningRateLowerCap); - m_oDistThresholdFrame.create(m_oImgSize,CV_32FC1); - m_oDistThresholdFrame = cv::Scalar(1.0f); - m_oVariationModulatorFrame.create(m_oImgSize,CV_32FC1); - m_oVariationModulatorFrame = cv::Scalar(10.0f); // should always be >= FEEDBACK_V_DECR - m_oMeanLastDistFrame.create(m_oImgSize,CV_32FC1); - m_oMeanLastDistFrame = cv::Scalar(0.0f); - m_oMeanMinDistFrame_LT.create(m_oImgSize,CV_32FC1); - m_oMeanMinDistFrame_LT = cv::Scalar(0.0f); - m_oMeanMinDistFrame_ST.create(m_oImgSize,CV_32FC1); - m_oMeanMinDistFrame_ST = cv::Scalar(0.0f); - m_oDownSampledFrameSize = cv::Size(m_oImgSize.width/FRAMELEVEL_ANALYSIS_DOWNSAMPLE_RATIO,m_oImgSize.height/FRAMELEVEL_ANALYSIS_DOWNSAMPLE_RATIO); - m_oMeanDownSampledLastDistFrame_LT.create(m_oDownSampledFrameSize,CV_32FC((int)m_nImgChannels)); - m_oMeanDownSampledLastDistFrame_LT = cv::Scalar(0.0f); - m_oMeanDownSampledLastDistFrame_ST.create(m_oDownSampledFrameSize,CV_32FC((int)m_nImgChannels)); - m_oMeanDownSampledLastDistFrame_ST = cv::Scalar(0.0f); - m_oMeanRawSegmResFrame_LT.create(m_oImgSize,CV_32FC1); - m_oMeanRawSegmResFrame_LT = cv::Scalar(0.0f); - m_oMeanRawSegmResFrame_ST.create(m_oImgSize,CV_32FC1); - m_oMeanRawSegmResFrame_ST = cv::Scalar(0.0f); - m_oMeanFinalSegmResFrame_LT.create(m_oImgSize,CV_32FC1); - m_oMeanFinalSegmResFrame_LT = cv::Scalar(0.0f); - m_oMeanFinalSegmResFrame_ST.create(m_oImgSize,CV_32FC1); - m_oMeanFinalSegmResFrame_ST = cv::Scalar(0.0f); - m_oUnstableRegionMask.create(m_oImgSize,CV_8UC1); - m_oUnstableRegionMask = cv::Scalar_(0); - m_oBlinksFrame.create(m_oImgSize,CV_8UC1); - m_oBlinksFrame = cv::Scalar_(0); - m_oDownSampledFrame_MotionAnalysis.create(m_oDownSampledFrameSize,CV_8UC((int)m_nImgChannels)); - m_oDownSampledFrame_MotionAnalysis = cv::Scalar_::all(0); - m_oLastColorFrame.create(m_oImgSize,CV_8UC((int)m_nImgChannels)); - m_oLastColorFrame = cv::Scalar_::all(0); - m_oLastDescFrame.create(m_oImgSize,CV_16UC((int)m_nImgChannels)); - m_oLastDescFrame = cv::Scalar_::all(0); - m_oLastRawFGMask.create(m_oImgSize,CV_8UC1); - m_oLastRawFGMask = cv::Scalar_(0); - m_oLastFGMask.create(m_oImgSize,CV_8UC1); - m_oLastFGMask = cv::Scalar_(0); - m_oLastFGMask_dilated.create(m_oImgSize,CV_8UC1); - m_oLastFGMask_dilated = cv::Scalar_(0); - m_oLastFGMask_dilated_inverted.create(m_oImgSize,CV_8UC1); - m_oLastFGMask_dilated_inverted = cv::Scalar_(0); - m_oFGMask_FloodedHoles.create(m_oImgSize,CV_8UC1); - m_oFGMask_FloodedHoles = cv::Scalar_(0); - m_oFGMask_PreFlood.create(m_oImgSize,CV_8UC1); - m_oFGMask_PreFlood = cv::Scalar_(0); - m_oCurrRawFGBlinkMask.create(m_oImgSize,CV_8UC1); - m_oCurrRawFGBlinkMask = cv::Scalar_(0); - m_oLastRawFGBlinkMask.create(m_oImgSize,CV_8UC1); - m_oLastRawFGBlinkMask = cv::Scalar_(0); - m_voBGColorSamples.resize(m_nBGSamples); - m_voBGDescSamples.resize(m_nBGSamples); - for(size_t s=0; s::all(0); - m_voBGDescSamples[s].create(m_oImgSize,CV_16UC((int)m_nImgChannels)); - m_voBGDescSamples[s] = cv::Scalar_::all(0); - } - if(m_aPxIdxLUT) - delete[] m_aPxIdxLUT; - if(m_aPxInfoLUT) - delete[] m_aPxInfoLUT; - m_aPxIdxLUT = new size_t[m_nTotRelevantPxCount]; - m_aPxInfoLUT = new PxInfoBase[m_nTotPxCount]; - if(m_nImgChannels==1) { - CV_Assert(m_oLastColorFrame.step.p[0]==(size_t)m_oImgSize.width && m_oLastColorFrame.step.p[1]==1); - CV_Assert(m_oLastDescFrame.step.p[0]==m_oLastColorFrame.step.p[0]*2 && m_oLastDescFrame.step.p[1]==m_oLastColorFrame.step.p[1]*2); - for(size_t t=0; t<=UCHAR_MAX; ++t) - m_anLBSPThreshold_8bitLUT[t] = cv::saturate_cast((m_nLBSPThresholdOffset+t*m_fRelLBSPThreshold)/3); - for(size_t nPxIter=0, nModelIter=0; nPxIter(m_nLBSPThresholdOffset+t*m_fRelLBSPThreshold); - for(size_t nPxIter=0, nModelIter=0; nPxIter0.0f && fSamplesRefreshFrac<=1.0f); - const size_t nModelsToRefresh = fSamplesRefreshFrac<1.0f?(size_t)(fSamplesRefreshFrac*m_nBGSamples):m_nBGSamples; - const size_t nRefreshStartPos = fSamplesRefreshFrac<1.0f?rand()%m_nBGSamples:0; - if(m_nImgChannels==1) { - for(size_t nModelIter=0; nModelIterUNSTABLE_REG_RDIST_MIN || (*pfCurrMeanRawSegmRes_LT-*pfCurrMeanFinalSegmRes_LT)>UNSTABLE_REG_RATIO_MIN || (*pfCurrMeanRawSegmRes_ST-*pfCurrMeanFinalSegmRes_ST)>UNSTABLE_REG_RATIO_MIN)?1:0; - size_t nGoodSamplesCount=0, nSampleIdx=0; - while(nGoodSamplesCountnCurrColorDistThreshold) - goto failedcheck1ch; - const ushort& nBGIntraDesc = *((ushort*)(m_voBGDescSamples[nSampleIdx].data+nDescIter)); - const size_t nIntraDescDist = hdist(nCurrIntraDesc,nBGIntraDesc); - LBSP::computeGrayscaleDescriptor(oInputImg,nBGColor,nCurrImgCoord_X,nCurrImgCoord_Y,m_anLBSPThreshold_8bitLUT[nBGColor],nCurrInterDesc); - const size_t nInterDescDist = hdist(nCurrInterDesc,nBGIntraDesc); - const size_t nDescDist = (nIntraDescDist+nInterDescDist)/2; - if(nDescDist>nCurrDescDistThreshold) - goto failedcheck1ch; - const size_t nSumDist = std::min((nDescDist/4)*(s_nColorMaxDataRange_1ch/s_nDescMaxDataRange_1ch)+nColorDist,s_nColorMaxDataRange_1ch); - if(nSumDist>nCurrColorDistThreshold) - goto failedcheck1ch; - if(nMinDescDist>nDescDist) - nMinDescDist = nDescDist; - if(nMinSumDist>nSumDist) - nMinSumDist = nSumDist; - nGoodSamplesCount++; - } - failedcheck1ch: - nSampleIdx++; - } - const float fNormalizedLastDist = ((float)L1dist(nLastColor,nCurrColor)/s_nColorMaxDataRange_1ch+(float)hdist(nLastIntraDesc,nCurrIntraDesc)/s_nDescMaxDataRange_1ch)/2; - *pfCurrMeanLastDist = (*pfCurrMeanLastDist)*(1.0f-fRollAvgFactor_ST) + fNormalizedLastDist*fRollAvgFactor_ST; - if(nGoodSamplesCount0?(size_t)ceil(learningRateOverride):(size_t)ceil(*pfCurrLearningRate); - if((rand()%nLearningRate)==0) { - const size_t s_rand = rand()%m_nBGSamples; - *((ushort*)(m_voBGDescSamples[s_rand].data+nDescIter)) = nCurrIntraDesc; - m_voBGColorSamples[s_rand].data[nPxIter] = nCurrColor; - } - int nSampleImgCoord_Y, nSampleImgCoord_X; - const bool bCurrUsing3x3Spread = m_bUse3x3Spread && !m_oUnstableRegionMask.data[nPxIter]; - if(bCurrUsing3x3Spread) - getRandNeighborPosition_3x3(nSampleImgCoord_X,nSampleImgCoord_Y,nCurrImgCoord_X,nCurrImgCoord_Y,LBSP::PATCH_SIZE/2,m_oImgSize); - else - getRandNeighborPosition_5x5(nSampleImgCoord_X,nSampleImgCoord_Y,nCurrImgCoord_X,nCurrImgCoord_Y,LBSP::PATCH_SIZE/2,m_oImgSize); - const size_t n_rand = rand(); - const size_t idx_rand_uchar = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; - const size_t idx_rand_flt32 = idx_rand_uchar*4; - const float fRandMeanLastDist = *((float*)(m_oMeanLastDistFrame.data+idx_rand_flt32)); - const float fRandMeanRawSegmRes = *((float*)(m_oMeanRawSegmResFrame_ST.data+idx_rand_flt32)); - if((n_rand%(bCurrUsing3x3Spread?nLearningRate:(nLearningRate/2+1)))==0 - || (fRandMeanRawSegmRes>GHOSTDET_S_MIN && fRandMeanLastDistm_fCurrLearningRateLowerCap) - *pfCurrLearningRate -= FEEDBACK_T_DECR*(*pfCurrVariationFactor)/std::max(*pfCurrMeanMinDist_LT,*pfCurrMeanMinDist_ST); - if((*pfCurrLearningRate)m_fCurrLearningRateUpperCap) - *pfCurrLearningRate = m_fCurrLearningRateUpperCap; - if(std::max(*pfCurrMeanMinDist_LT,*pfCurrMeanMinDist_ST)>UNSTABLE_REG_RATIO_MIN && m_oBlinksFrame.data[nPxIter]) - (*pfCurrVariationFactor) += FEEDBACK_V_INCR; - else if((*pfCurrVariationFactor)>FEEDBACK_V_DECR) { - (*pfCurrVariationFactor) -= m_oLastFGMask.data[nPxIter]?FEEDBACK_V_DECR/4:m_oUnstableRegionMask.data[nPxIter]?FEEDBACK_V_DECR/2:FEEDBACK_V_DECR; - if((*pfCurrVariationFactor)=2) - ++nNonZeroDescCount; - nLastIntraDesc = nCurrIntraDesc; - nLastColor = nCurrColor; - } - } - else { //m_nImgChannels==3 - for(size_t nModelIter=0; nModelIterUNSTABLE_REG_RDIST_MIN || (*pfCurrMeanRawSegmRes_LT-*pfCurrMeanFinalSegmRes_LT)>UNSTABLE_REG_RATIO_MIN || (*pfCurrMeanRawSegmRes_ST-*pfCurrMeanFinalSegmRes_ST)>UNSTABLE_REG_RATIO_MIN)?1:0; - size_t nGoodSamplesCount=0, nSampleIdx=0; - while(nGoodSamplesCountnCurrSCColorDistThreshold) - goto failedcheck3ch; - const size_t nIntraDescDist = hdist(anCurrIntraDesc[c],anBGIntraDesc[c]); - LBSP::computeSingleRGBDescriptor(oInputImg,anBGColor[c],nCurrImgCoord_X,nCurrImgCoord_Y,c,m_anLBSPThreshold_8bitLUT[anBGColor[c]],anCurrInterDesc[c]); - const size_t nInterDescDist = hdist(anCurrInterDesc[c],anBGIntraDesc[c]); - const size_t nDescDist = (nIntraDescDist+nInterDescDist)/2; - const size_t nSumDist = std::min((nDescDist/2)*(s_nColorMaxDataRange_1ch/s_nDescMaxDataRange_1ch)+nColorDist,s_nColorMaxDataRange_1ch); - if(nSumDist>nCurrSCColorDistThreshold) - goto failedcheck3ch; - nTotDescDist += nDescDist; - nTotSumDist += nSumDist; - } - if(nTotDescDist>nCurrTotDescDistThreshold || nTotSumDist>nCurrTotColorDistThreshold) - goto failedcheck3ch; - if(nMinTotDescDist>nTotDescDist) - nMinTotDescDist = nTotDescDist; - if(nMinTotSumDist>nTotSumDist) - nMinTotSumDist = nTotSumDist; - nGoodSamplesCount++; - failedcheck3ch: - nSampleIdx++; - } - const float fNormalizedLastDist = ((float)L1dist<3>(anLastColor,anCurrColor)/s_nColorMaxDataRange_3ch+(float)hdist<3>(anLastIntraDesc,anCurrIntraDesc)/s_nDescMaxDataRange_3ch)/2; - *pfCurrMeanLastDist = (*pfCurrMeanLastDist)*(1.0f-fRollAvgFactor_ST) + fNormalizedLastDist*fRollAvgFactor_ST; - if(nGoodSamplesCount0?(size_t)ceil(learningRateOverride):(size_t)ceil(*pfCurrLearningRate); - if((rand()%nLearningRate)==0) { - const size_t s_rand = rand()%m_nBGSamples; - for(size_t c=0; c<3; ++c) { - *((ushort*)(m_voBGDescSamples[s_rand].data+nDescIterRGB+2*c)) = anCurrIntraDesc[c]; - *(m_voBGColorSamples[s_rand].data+nPxIterRGB+c) = anCurrColor[c]; - } - } - int nSampleImgCoord_Y, nSampleImgCoord_X; - const bool bCurrUsing3x3Spread = m_bUse3x3Spread && !m_oUnstableRegionMask.data[nPxIter]; - if(bCurrUsing3x3Spread) - getRandNeighborPosition_3x3(nSampleImgCoord_X,nSampleImgCoord_Y,nCurrImgCoord_X,nCurrImgCoord_Y,LBSP::PATCH_SIZE/2,m_oImgSize); - else - getRandNeighborPosition_5x5(nSampleImgCoord_X,nSampleImgCoord_Y,nCurrImgCoord_X,nCurrImgCoord_Y,LBSP::PATCH_SIZE/2,m_oImgSize); - const size_t n_rand = rand(); - const size_t idx_rand_uchar = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; - const size_t idx_rand_flt32 = idx_rand_uchar*4; - const float fRandMeanLastDist = *((float*)(m_oMeanLastDistFrame.data+idx_rand_flt32)); - const float fRandMeanRawSegmRes = *((float*)(m_oMeanRawSegmResFrame_ST.data+idx_rand_flt32)); - if((n_rand%(bCurrUsing3x3Spread?nLearningRate:(nLearningRate/2+1)))==0 - || (fRandMeanRawSegmRes>GHOSTDET_S_MIN && fRandMeanLastDistm_fCurrLearningRateLowerCap) - *pfCurrLearningRate -= FEEDBACK_T_DECR*(*pfCurrVariationFactor)/std::max(*pfCurrMeanMinDist_LT,*pfCurrMeanMinDist_ST); - if((*pfCurrLearningRate)m_fCurrLearningRateUpperCap) - *pfCurrLearningRate = m_fCurrLearningRateUpperCap; - if(std::max(*pfCurrMeanMinDist_LT,*pfCurrMeanMinDist_ST)>UNSTABLE_REG_RATIO_MIN && m_oBlinksFrame.data[nPxIter]) - (*pfCurrVariationFactor) += FEEDBACK_V_INCR; - else if((*pfCurrVariationFactor)>FEEDBACK_V_DECR) { - (*pfCurrVariationFactor) -= m_oLastFGMask.data[nPxIter]?FEEDBACK_V_DECR/4:m_oUnstableRegionMask.data[nPxIter]?FEEDBACK_V_DECR/2:FEEDBACK_V_DECR; - if((*pfCurrVariationFactor)(anCurrIntraDesc)>=4) - ++nNonZeroDescCount; - for(size_t c=0; c<3; ++c) { - anLastIntraDesc[c] = anCurrIntraDesc[c]; - anLastColor[c] = anCurrColor[c]; - } - } - } -#if DISPLAY_SUBSENSE_DEBUG_INFO - std::cout << std::endl; - cv::Point dbgpt(nDebugCoordX,nDebugCoordY); - cv::Mat oMeanMinDistFrameNormalized; m_oMeanMinDistFrame_ST.copyTo(oMeanMinDistFrameNormalized); - cv::circle(oMeanMinDistFrameNormalized,dbgpt,5,cv::Scalar(1.0f)); - cv::resize(oMeanMinDistFrameNormalized,oMeanMinDistFrameNormalized,DEFAULT_FRAME_SIZE); - cv::imshow("d_min(x)",oMeanMinDistFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " d_min(" << dbgpt << ") = " << m_oMeanMinDistFrame_ST.at(dbgpt) << std::endl; - cv::Mat oMeanLastDistFrameNormalized; m_oMeanLastDistFrame.copyTo(oMeanLastDistFrameNormalized); - cv::circle(oMeanLastDistFrameNormalized,dbgpt,5,cv::Scalar(1.0f)); - cv::resize(oMeanLastDistFrameNormalized,oMeanLastDistFrameNormalized,DEFAULT_FRAME_SIZE); - cv::imshow("d_last(x)",oMeanLastDistFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " d_last(" << dbgpt << ") = " << m_oMeanLastDistFrame.at(dbgpt) << std::endl; - cv::Mat oMeanRawSegmResFrameNormalized; m_oMeanRawSegmResFrame_ST.copyTo(oMeanRawSegmResFrameNormalized); - cv::circle(oMeanRawSegmResFrameNormalized,dbgpt,5,cv::Scalar(1.0f)); - cv::resize(oMeanRawSegmResFrameNormalized,oMeanRawSegmResFrameNormalized,DEFAULT_FRAME_SIZE); - cv::imshow("s_avg(x)",oMeanRawSegmResFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " s_avg(" << dbgpt << ") = " << m_oMeanRawSegmResFrame_ST.at(dbgpt) << std::endl; - cv::Mat oMeanFinalSegmResFrameNormalized; m_oMeanFinalSegmResFrame_ST.copyTo(oMeanFinalSegmResFrameNormalized); - cv::circle(oMeanFinalSegmResFrameNormalized,dbgpt,5,cv::Scalar(1.0f)); - cv::resize(oMeanFinalSegmResFrameNormalized,oMeanFinalSegmResFrameNormalized,DEFAULT_FRAME_SIZE); - cv::imshow("z_avg(x)",oMeanFinalSegmResFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " z_avg(" << dbgpt << ") = " << m_oMeanFinalSegmResFrame_ST.at(dbgpt) << std::endl; - cv::Mat oDistThresholdFrameNormalized; m_oDistThresholdFrame.convertTo(oDistThresholdFrameNormalized,CV_32FC1,0.25f,-0.25f); - cv::circle(oDistThresholdFrameNormalized,dbgpt,5,cv::Scalar(1.0f)); - cv::resize(oDistThresholdFrameNormalized,oDistThresholdFrameNormalized,DEFAULT_FRAME_SIZE); - cv::imshow("r(x)",oDistThresholdFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " r(" << dbgpt << ") = " << m_oDistThresholdFrame.at(dbgpt) << std::endl; - cv::Mat oVariationModulatorFrameNormalized; cv::normalize(m_oVariationModulatorFrame,oVariationModulatorFrameNormalized,0,255,cv::NORM_MINMAX,CV_8UC1); - cv::circle(oVariationModulatorFrameNormalized,dbgpt,5,cv::Scalar(255)); - cv::resize(oVariationModulatorFrameNormalized,oVariationModulatorFrameNormalized,DEFAULT_FRAME_SIZE); - cv::imshow("v(x)",oVariationModulatorFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " v(" << dbgpt << ") = " << m_oVariationModulatorFrame.at(dbgpt) << std::endl; - cv::Mat oUpdateRateFrameNormalized; m_oUpdateRateFrame.convertTo(oUpdateRateFrameNormalized,CV_32FC1,1.0f/FEEDBACK_T_UPPER,-FEEDBACK_T_LOWER/FEEDBACK_T_UPPER); - cv::circle(oUpdateRateFrameNormalized,dbgpt,5,cv::Scalar(1.0f)); - cv::resize(oUpdateRateFrameNormalized,oUpdateRateFrameNormalized,DEFAULT_FRAME_SIZE); - cv::imshow("t(x)",oUpdateRateFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " t(" << dbgpt << ") = " << m_oUpdateRateFrame.at(dbgpt) << std::endl; -#endif //DISPLAY_SUBSENSE_DEBUG_INFO - cv::bitwise_xor(oCurrFGMask,m_oLastRawFGMask,m_oCurrRawFGBlinkMask); - cv::bitwise_or(m_oCurrRawFGBlinkMask,m_oLastRawFGBlinkMask,m_oBlinksFrame); - m_oCurrRawFGBlinkMask.copyTo(m_oLastRawFGBlinkMask); - oCurrFGMask.copyTo(m_oLastRawFGMask); - cv::morphologyEx(oCurrFGMask,m_oFGMask_PreFlood,cv::MORPH_CLOSE, m_defaultMorphologyKernel); - m_oFGMask_PreFlood.copyTo(m_oFGMask_FloodedHoles); - cv::floodFill(m_oFGMask_FloodedHoles,cv::Point(0,0),UCHAR_MAX); - cv::bitwise_not(m_oFGMask_FloodedHoles,m_oFGMask_FloodedHoles); - cv::erode(m_oFGMask_PreFlood,m_oFGMask_PreFlood,m_defaultMorphologyKernel,cv::Point(-1,-1),3); - cv::bitwise_or(oCurrFGMask,m_oFGMask_FloodedHoles,oCurrFGMask); - cv::bitwise_or(oCurrFGMask,m_oFGMask_PreFlood,oCurrFGMask); - cv::medianBlur(oCurrFGMask,m_oLastFGMask,m_nMedianBlurKernelSize); - cv::dilate(m_oLastFGMask,m_oLastFGMask_dilated,m_defaultMorphologyKernel,cv::Point(-1,-1),3); - cv::bitwise_and(m_oBlinksFrame,m_oLastFGMask_dilated_inverted,m_oBlinksFrame); - cv::bitwise_not(m_oLastFGMask_dilated,m_oLastFGMask_dilated_inverted); - cv::bitwise_and(m_oBlinksFrame,m_oLastFGMask_dilated_inverted,m_oBlinksFrame); - m_oLastFGMask.copyTo(oCurrFGMask); - cv::addWeighted(m_oMeanFinalSegmResFrame_LT,(1.0f-fRollAvgFactor_LT),m_oLastFGMask,(1.0/UCHAR_MAX)*fRollAvgFactor_LT,0,m_oMeanFinalSegmResFrame_LT,CV_32F); - cv::addWeighted(m_oMeanFinalSegmResFrame_ST,(1.0f-fRollAvgFactor_ST),m_oLastFGMask,(1.0/UCHAR_MAX)*fRollAvgFactor_ST,0,m_oMeanFinalSegmResFrame_ST,CV_32F); - const float fCurrNonZeroDescRatio = (float)nNonZeroDescCount/m_nTotRelevantPxCount; - if(fCurrNonZeroDescRatiocv::saturate_cast(m_nLBSPThresholdOffset+ceil(t*m_fRelLBSPThreshold/4))) - --m_anLBSPThreshold_8bitLUT[t]; - } - else if(fCurrNonZeroDescRatio>LBSPDESC_NONZERO_RATIO_MAX && m_fLastNonZeroDescRatio>LBSPDESC_NONZERO_RATIO_MAX) { - for(size_t t=0; t<=UCHAR_MAX; ++t) - if(m_anLBSPThreshold_8bitLUT[t](m_nLBSPThresholdOffset+UCHAR_MAX*m_fRelLBSPThreshold)) - ++m_anLBSPThreshold_8bitLUT[t]; - } - m_fLastNonZeroDescRatio = fCurrNonZeroDescRatio; - if(m_bLearningRateScalingEnabled) { - cv::resize(oInputImg,m_oDownSampledFrame_MotionAnalysis,m_oDownSampledFrameSize,0,0,cv::INTER_AREA); - cv::accumulateWeighted(m_oDownSampledFrame_MotionAnalysis,m_oMeanDownSampledLastDistFrame_LT,fRollAvgFactor_LT); - cv::accumulateWeighted(m_oDownSampledFrame_MotionAnalysis,m_oMeanDownSampledLastDistFrame_ST,fRollAvgFactor_ST); - size_t nTotColorDiff = 0; - for(int i=0; i1000) - m_bAutoModelResetEnabled = false; - else if(fCurrColorDiffRatio>=FRAMELEVEL_MIN_COLOR_DIFF_THRESHOLD && m_nModelResetCooldown==0) { - m_nFramesSinceLastReset = 0; - refreshModel(0.1f); // reset 10% of the bg model - m_nModelResetCooldown = m_nSamplesForMovingAvgs/4; - m_oUpdateRateFrame = cv::Scalar(1.0f); - } - else - ++m_nFramesSinceLastReset; - } - else if(fCurrColorDiffRatio>=FRAMELEVEL_MIN_COLOR_DIFF_THRESHOLD*2) { - m_nFramesSinceLastReset = 0; - m_bAutoModelResetEnabled = true; - } - if(fCurrColorDiffRatio>=FRAMELEVEL_MIN_COLOR_DIFF_THRESHOLD/2) { - m_fCurrLearningRateLowerCap = (float)std::max((int)FEEDBACK_T_LOWER>>(int)(fCurrColorDiffRatio/2),1); - m_fCurrLearningRateUpperCap = (float)std::max((int)FEEDBACK_T_UPPER>>(int)(fCurrColorDiffRatio/2),1); - } - else { - m_fCurrLearningRateLowerCap = FEEDBACK_T_LOWER; - m_fCurrLearningRateUpperCap = FEEDBACK_T_UPPER; - } - if(m_nModelResetCooldown>0) - --m_nModelResetCooldown; - } -} - -void BackgroundSubtractorSuBSENSE::getBackgroundImage(cv::OutputArray backgroundImage) const { - CV_Assert(m_bInitialized); - cv::Mat oAvgBGImg = cv::Mat::zeros(m_oImgSize,CV_32FC((int)m_nImgChannels)); - for(size_t s=0; s m_voBGColorSamples; - //! background model descriptors samples - std::vector m_voBGDescSamples; - - //! per-pixel update rates ('T(x)' in PBAS, which contains pixel-level 'sigmas', as referred to in ViBe) - cv::Mat m_oUpdateRateFrame; - //! per-pixel distance thresholds (equivalent to 'R(x)' in PBAS, but used as a relative value to determine both intensity and descriptor variation thresholds) - cv::Mat m_oDistThresholdFrame; - //! per-pixel distance variation modulators ('v(x)', relative value used to modulate 'R(x)' and 'T(x)' variations) - cv::Mat m_oVariationModulatorFrame; - //! per-pixel mean distances between consecutive frames ('D_last(x)', used to detect ghosts and high variation regions in the sequence) - cv::Mat m_oMeanLastDistFrame; - //! per-pixel mean minimal distances from the model ('D_min(x)' in PBAS, used to control variation magnitude and direction of 'T(x)' and 'R(x)') - cv::Mat m_oMeanMinDistFrame_LT, m_oMeanMinDistFrame_ST; - //! per-pixel mean downsampled distances between consecutive frames (used to analyze camera movement and control max learning rates globally) - cv::Mat m_oMeanDownSampledLastDistFrame_LT, m_oMeanDownSampledLastDistFrame_ST; - //! per-pixel mean raw segmentation results (used to detect unstable segmentation regions) - cv::Mat m_oMeanRawSegmResFrame_LT, m_oMeanRawSegmResFrame_ST; - //! per-pixel mean raw segmentation results (used to detect unstable segmentation regions) - cv::Mat m_oMeanFinalSegmResFrame_LT, m_oMeanFinalSegmResFrame_ST; - //! a lookup map used to keep track of unstable regions (based on segm. noise & local dist. thresholds) - cv::Mat m_oUnstableRegionMask; - //! per-pixel blink detection map ('Z(x)') - cv::Mat m_oBlinksFrame; - //! pre-allocated matrix used to downsample the input frame when needed - cv::Mat m_oDownSampledFrame_MotionAnalysis; - //! the foreground mask generated by the method at [t-1] (without post-proc, used for blinking px detection) - cv::Mat m_oLastRawFGMask; - - //! pre-allocated CV_8UC1 matrices used to speed up morph ops - cv::Mat m_oFGMask_PreFlood; - cv::Mat m_oFGMask_FloodedHoles; - cv::Mat m_oLastFGMask_dilated; - cv::Mat m_oLastFGMask_dilated_inverted; - cv::Mat m_oCurrRawFGBlinkMask; - cv::Mat m_oLastRawFGBlinkMask; - - //! default kernel for morphology operations - cv::Mat m_defaultMorphologyKernel; -}; - diff --git a/Detector/Subsense/DistanceUtils.h b/Detector/Subsense/DistanceUtils.h deleted file mode 100644 index 54d1cecd9..000000000 --- a/Detector/Subsense/DistanceUtils.h +++ /dev/null @@ -1,316 +0,0 @@ -#pragma once - -#include - -//! computes the L1 distance between two integer values -template static inline typename std::enable_if::value,size_t>::type L1dist(T a, T b) { - return (size_t)abs((int)a-b); -} - -//! computes the L1 distance between two float values -template static inline typename std::enable_if::value,float>::type L1dist(T a, T b) { - return fabs((float)a-(float)b); -} - -//! computes the L1 distance between two generic arrays -template static inline auto L1dist(const T* a, const T* b) -> decltype(L1dist(*a,*b)) { - decltype(L1dist(*a,*b)) oResult = 0; - for(size_t c=0; c static inline auto L1dist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) -> decltype(L1dist(a,b)) { - decltype(L1dist(a,b)) oResult = 0; - size_t nTotElements = nElements*nChannels; - if(m) { - for(size_t n=0,i=0; n(a+n,b+n); - } - else { - for(size_t n=0; n(a+n,b+n); - } - return oResult; -} - -//! computes the L1 distance between two generic arrays -template static inline auto L1dist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) -> decltype(L1dist<3>(a,b,nElements,m)) { - CV_Assert(nChannels>0 && nChannels<=4); - switch(nChannels) { - case 1: return L1dist<1>(a,b,nElements,m); - case 2: return L1dist<2>(a,b,nElements,m); - case 3: return L1dist<3>(a,b,nElements,m); - case 4: return L1dist<4>(a,b,nElements,m); - default: return 0; - } -} - -//! computes the L1 distance between two opencv vectors -template static inline auto L1dist_(const cv::Vec& a, const cv::Vec& b) -> decltype(L1dist((T*)(0),(T*)(0))) { - T a_array[nChannels], b_array[nChannels]; - for(size_t c=0; c(a_array,b_array); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -//! computes the squared L2 distance between two generic variables -template static inline auto L2sqrdist(T a, T b) -> decltype(L1dist(a,b)) { - auto oResult = L1dist(a,b); - return oResult*oResult; -} - -//! computes the squared L2 distance between two generic arrays -template static inline auto L2sqrdist(const T* a, const T* b) -> decltype(L2sqrdist(*a,*b)) { - decltype(L2sqrdist(*a,*b)) oResult = 0; - for(size_t c=0; c static inline auto L2sqrdist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) -> decltype(L2sqrdist(a,b)) { - decltype(L2sqrdist(a,b)) oResult = 0; - size_t nTotElements = nElements*nChannels; - if(m) { - for(size_t n=0,i=0; n(a+n,b+n); - } - else { - for(size_t n=0; n(a+n,b+n); - } - return oResult; -} - -//! computes the squared L2 distance between two generic arrays -template static inline auto L2sqrdist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) -> decltype(L2sqrdist<3>(a,b,nElements,m)) { - CV_Assert(nChannels>0 && nChannels<=4); - switch(nChannels) { - case 1: return L2sqrdist<1>(a,b,nElements,m); - case 2: return L2sqrdist<2>(a,b,nElements,m); - case 3: return L2sqrdist<3>(a,b,nElements,m); - case 4: return L2sqrdist<4>(a,b,nElements,m); - default: return 0; - } -} - -//! computes the squared L2 distance between two opencv vectors -template static inline auto L2sqrdist_(const cv::Vec& a, const cv::Vec& b) -> decltype(L2sqrdist((T*)(0),(T*)(0))) { - T a_array[nChannels], b_array[nChannels]; - for(size_t c=0; c(a_array,b_array); -} - -//! computes the L2 distance between two generic arrays -template static inline float L2dist(const T* a, const T* b) { - decltype(L2sqrdist(*a,*b)) oResult = 0; - for(size_t c=0; c static inline float L2dist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) { - decltype(L2sqrdist(a,b)) oResult = 0; - size_t nTotElements = nElements*nChannels; - if(m) { - for(size_t n=0,i=0; n(a+n,b+n); - } - else { - for(size_t n=0; n(a+n,b+n); - } - return sqrt((float)oResult); -} - -//! computes the squared L2 distance between two generic arrays -template static inline float L2dist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) { - CV_Assert(nChannels>0 && nChannels<=4); - switch(nChannels) { - case 1: return L2dist<1>(a,b,nElements,m); - case 2: return L2dist<2>(a,b,nElements,m); - case 3: return L2dist<3>(a,b,nElements,m); - case 4: return L2dist<4>(a,b,nElements,m); - default: return 0; - } -} - -//! computes the L2 distance between two opencv vectors -template static inline float L2dist_(const cv::Vec& a, const cv::Vec& b) { - T a_array[nChannels], b_array[nChannels]; - for(size_t c=0; c(a_array,b_array); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -//! computes the color distortion between two integer arrays -template static inline typename std::enable_if::value,size_t>::type cdist(const T* curr, const T* bg) { - static_assert(nChannels>1,"cdist: requires more than one channel"); - size_t curr_sqr = 0; - bool bSkip = true; - for(size_t c=0; c static inline typename std::enable_if::value,float>::type cdist(const T* curr, const T* bg) { - static_assert(nChannels>1,"cdist: requires more than one channel"); - float curr_sqr = 0; - bool bSkip = true; - for(size_t c=0; c static inline auto cdist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) -> decltype(cdist(a,b)) { - decltype(cdist(a,b)) oResult = 0; - size_t nTotElements = nElements*nChannels; - if(m) { - for(size_t n=0,i=0; n(a+n,b+n); - } - else { - for(size_t n=0; n(a+n,b+n); - } - return oResult; -} - -//! computes the color distortion between two generic arrays -template static inline auto cdist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) -> decltype(cdist<3>(a,b,nElements,m)) { - CV_Assert(nChannels>1 && nChannels<=4); - switch(nChannels) { - case 2: return cdist<2>(a,b,nElements,m); - case 3: return cdist<3>(a,b,nElements,m); - case 4: return cdist<4>(a,b,nElements,m); - default: return 0; - } -} - -//! computes the color distortion between two opencv vectors -template static inline auto cdist_(const cv::Vec& a, const cv::Vec& b) -> decltype(cdist((T*)(0),(T*)(0))) { - T a_array[nChannels], b_array[nChannels]; - for(size_t c=0; c(a_array,b_array); -} - -//! computes a color distortion-distance mix using two generic distances -template static inline T cmixdist(T oL1Distance, T oCDistortion) { - return (oL1Distance/2+oCDistortion*4); -} - -//! computes a color distoirtion-distance mix using two generic arrays -template static inline typename std::enable_if::value,size_t>::type cmixdist(const T* curr, const T* bg) { - return cmixdist(L1dist(curr,bg),cdist(curr,bg)); -} - -template static inline typename std::enable_if::value,float>::type cmixdist(const T* curr, const T* bg) { - return cmixdist(L1dist(curr,bg),cdist(curr,bg)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -//! popcount LUT for 8-bit vectors -static const uchar popcount_LUT8[256] = { - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, -}; - -//! computes the population count of an N-byte vector using an 8-bit popcount LUT -template static inline size_t popcount(T x) { - size_t nBytes = sizeof(T); - size_t nResult = 0; - for(size_t l=0; l>l*8)]; - return nResult; -} - -//! computes the hamming distance between two N-byte vectors using an 8-bit popcount LUT -template static inline size_t hdist(T a, T b) { - return popcount(a^b); -} - -//! computes the gradient magnitude distance between two N-byte vectors using an 8-bit popcount LUT -template static inline size_t gdist(T a, T b) { - return L1dist(popcount(a),popcount(b)); -} - -//! computes the population count of a (nChannels*N)-byte vector using an 8-bit popcount LUT -template static inline size_t popcount(const T* x) { - size_t nBytes = sizeof(T); - size_t nResult = 0; - for(size_t c=0; c>l*8)]; - return nResult; -} - -//! computes the hamming distance between two (nChannels*N)-byte vectors using an 8-bit popcount LUT -template static inline size_t hdist(const T* a, const T* b) { - T xor_array[nChannels]; - for(size_t c=0; c(xor_array); -} - -//! computes the gradient magnitude distance between two (nChannels*N)-byte vectors using an 8-bit popcount LUT -template static inline size_t gdist(const T* a, const T* b) { - return L1dist(popcount(a),popcount(b)); -} diff --git a/Detector/Subsense/LBSP.cpp b/Detector/Subsense/LBSP.cpp deleted file mode 100644 index de35f1a09..000000000 --- a/Detector/Subsense/LBSP.cpp +++ /dev/null @@ -1,318 +0,0 @@ -#include "LBSP.h" - -LBSP::LBSP(size_t nThreshold) - : m_bOnlyUsingAbsThreshold(true) - ,m_fRelThreshold(0) // unused - ,m_nThreshold(nThreshold) - ,m_oRefImage() {} - -LBSP::LBSP(float fRelThreshold, size_t nThresholdOffset) - : m_bOnlyUsingAbsThreshold(false) - ,m_fRelThreshold(fRelThreshold) - ,m_nThreshold(nThresholdOffset) - ,m_oRefImage() { - CV_Assert(m_fRelThreshold>=0); -} - -LBSP::~LBSP() {} - -void LBSP::read(const cv::FileNode& /*fn*/) { - // ... = fn["..."]; -} - -void LBSP::write(cv::FileStorage& /*fs*/) const { - //fs << "..." << ...; -} - -void LBSP::setReference(const cv::Mat& img) { - CV_DbgAssert(img.empty() || img.type()==CV_8UC1 || img.type()==CV_8UC3); - m_oRefImage = img; -} - -int LBSP::descriptorSize() const { - return DESC_SIZE; -} - -int LBSP::descriptorType() const { - return CV_16U; -} - -bool LBSP::isUsingRelThreshold() const { - return !m_bOnlyUsingAbsThreshold; -} - -float LBSP::getRelThreshold() const { - return m_fRelThreshold; -} - -size_t LBSP::getAbsThreshold() const { - return m_nThreshold; -} - -static inline void lbsp_computeImpl( const cv::Mat& oInputImg, - const cv::Mat& oRefImg, - const std::vector& voKeyPoints, - cv::Mat& oDesc, - size_t _t) { - CV_DbgAssert(oRefImg.empty() || (oRefImg.size==oInputImg.size && oRefImg.type()==oInputImg.type())); - CV_DbgAssert(oInputImg.type()==CV_8UC1 || oInputImg.type()==CV_8UC3); - CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size - const size_t nChannels = (size_t)oInputImg.channels(); - const size_t _step_row = oInputImg.step.p[0]; - const uchar* _data = oInputImg.data; - const uchar* _refdata = oRefImg.empty()?oInputImg.data:oRefImg.data; - const size_t nKeyPoints = voKeyPoints.size(); - if(nChannels==1) { - oDesc.create((int)nKeyPoints,1,CV_16UC1); - for(size_t k=0; k((int)k); - #include "LBSP_16bits_dbcross_1ch.i" - } - } - else { //nChannels==3 - oDesc.create((int)nKeyPoints,1,CV_16UC3); - for(size_t k=0; k& voKeyPoints, - cv::Mat& oDesc, - float fThreshold, - size_t nThresholdOffset) { - CV_DbgAssert(oRefImg.empty() || (oRefImg.size==oInputImg.size && oRefImg.type()==oInputImg.type())); - CV_DbgAssert(oInputImg.type()==CV_8UC1 || oInputImg.type()==CV_8UC3); - CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size - CV_DbgAssert(fThreshold>=0); - const size_t nChannels = (size_t)oInputImg.channels(); - const size_t _step_row = oInputImg.step.p[0]; - const uchar* _data = oInputImg.data; - const uchar* _refdata = oRefImg.empty()?oInputImg.data:oRefImg.data; - const size_t nKeyPoints = voKeyPoints.size(); - if(nChannels==1) { - oDesc.create((int)nKeyPoints,1,CV_16UC1); - for(size_t k=0; k((int)k); - const size_t _t = (size_t)(_ref*fThreshold)+nThresholdOffset; - #include "LBSP_16bits_dbcross_1ch.i" - } - } - else { //nChannels==3 - oDesc.create((int)nKeyPoints,1,CV_16UC3); - for(size_t k=0; k& voKeyPoints, - cv::Mat& oDesc, - size_t _t) { - CV_DbgAssert(oRefImg.empty() || (oRefImg.size==oInputImg.size && oRefImg.type()==oInputImg.type())); - CV_DbgAssert(oInputImg.type()==CV_8UC1 || oInputImg.type()==CV_8UC3); - CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size - const size_t nChannels = (size_t)oInputImg.channels(); - const size_t _step_row = oInputImg.step.p[0]; - const uchar* _data = oInputImg.data; - const uchar* _refdata = oRefImg.empty()?oInputImg.data:oRefImg.data; - const size_t nKeyPoints = voKeyPoints.size(); - if(nChannels==1) { - oDesc.create(oInputImg.size(),CV_16UC1); - for(size_t k=0; k(_y,_x); - #include "LBSP_16bits_dbcross_1ch.i" - } - } - else { //nChannels==3 - oDesc.create(oInputImg.size(),CV_16UC3); - for(size_t k=0; k& voKeyPoints, - cv::Mat& oDesc, - float fThreshold, - size_t nThresholdOffset) { - CV_DbgAssert(oRefImg.empty() || (oRefImg.size==oInputImg.size && oRefImg.type()==oInputImg.type())); - CV_DbgAssert(oInputImg.type()==CV_8UC1 || oInputImg.type()==CV_8UC3); - CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size - CV_DbgAssert(fThreshold>=0); - const size_t nChannels = (size_t)oInputImg.channels(); - const size_t _step_row = oInputImg.step.p[0]; - const uchar* _data = oInputImg.data; - const uchar* _refdata = oRefImg.empty()?oInputImg.data:oRefImg.data; - const size_t nKeyPoints = voKeyPoints.size(); - if(nChannels==1) { - oDesc.create(oInputImg.size(),CV_16UC1); - for(size_t k=0; k(_y,_x); - const size_t _t = (size_t)(_ref*fThreshold)+nThresholdOffset; - #include "LBSP_16bits_dbcross_1ch.i" - } - } - else { //nChannels==3 - oDesc.create(oInputImg.size(),CV_16UC3); - for(size_t k=0; k& voKeypoints, cv::Mat& oDescriptors) const { - CV_Assert(!oImage.empty()); - cv::KeyPointsFilter::runByImageBorder(voKeypoints,oImage.size(),PATCH_SIZE/2); - cv::KeyPointsFilter::runByKeypointSize(voKeypoints,std::numeric_limits::epsilon()); - if(voKeypoints.empty()) { - oDescriptors.release(); - return; - } - if(m_bOnlyUsingAbsThreshold) - lbsp_computeImpl2(oImage,m_oRefImage,voKeypoints,oDescriptors,m_nThreshold); - else - lbsp_computeImpl2(oImage,m_oRefImage,voKeypoints,oDescriptors,m_fRelThreshold,m_nThreshold); -} - -void LBSP::compute2(const std::vector& voImageCollection, std::vector >& vvoPointCollection, std::vector& voDescCollection) const { - CV_Assert(voImageCollection.size() == vvoPointCollection.size()); - voDescCollection.resize(voImageCollection.size()); - for(size_t i=0; i& voKeypoints, cv::Mat& oDescriptors) const { - CV_Assert(!oImage.empty()); - cv::KeyPointsFilter::runByImageBorder(voKeypoints,oImage.size(),PATCH_SIZE/2); - cv::KeyPointsFilter::runByKeypointSize(voKeypoints,std::numeric_limits::epsilon()); - if(voKeypoints.empty()) { - oDescriptors.release(); - return; - } - if(m_bOnlyUsingAbsThreshold) - lbsp_computeImpl(oImage,m_oRefImage,voKeypoints,oDescriptors,m_nThreshold); - else - lbsp_computeImpl(oImage,m_oRefImage,voKeypoints,oDescriptors,m_fRelThreshold,m_nThreshold); -} - -void LBSP::reshapeDesc(cv::Size oSize, const std::vector& voKeypoints, const cv::Mat& oDescriptors, cv::Mat& oOutput) { - CV_DbgAssert(!voKeypoints.empty()); - CV_DbgAssert(!oDescriptors.empty() && oDescriptors.cols==1); - CV_DbgAssert(oSize.width>0 && oSize.height>0); - CV_DbgAssert(DESC_SIZE==2); // @@@ also relies on a constant desc size - CV_DbgAssert(oDescriptors.type()==CV_16UC1 || oDescriptors.type()==CV_16UC3); - const size_t nChannels = (size_t)oDescriptors.channels(); - const size_t nKeyPoints = voKeypoints.size(); - if(nChannels==1) { - oOutput.create(oSize,CV_16UC1); - oOutput = cv::Scalar_(0); - for(size_t k=0; k(voKeypoints[k].pt) = oDescriptors.at((int)k); - } - else { //nChannels==3 - oOutput.create(oSize,CV_16UC3); - oOutput = cv::Scalar_(0,0,0); - for(size_t k=0; k(i,j) = (uchar)(fScaleFactor*hdist(desc1_ptr[j],desc2_ptr[j])); - } - } - else { //nChannels==3 - if(bForceMergeChannels) - oOutput.create(oDesc1.size(),CV_8UC1); - else - oOutput.create(oDesc1.size(),CV_8UC3); - oOutput = cv::Scalar::all(0); - for(int i=0; i& voKeypoints, cv::Size oImgSize) { - cv::KeyPointsFilter::runByImageBorder(voKeypoints,oImgSize,PATCH_SIZE/2); -} - -void LBSP::validateROI(cv::Mat& oROI) { - CV_Assert(!oROI.empty() && oROI.type()==CV_8UC1); - cv::Mat oROI_new(oROI.size(),CV_8UC1,cv::Scalar_(0)); - const size_t nBorderSize = PATCH_SIZE/2; - const cv::Rect nROI_inner(nBorderSize,nBorderSize,oROI.cols-nBorderSize*2,oROI.rows-nBorderSize*2); - cv::Mat(oROI,nROI_inner).copyTo(cv::Mat(oROI_new,nROI_inner)); - oROI = oROI_new; -} diff --git a/Detector/Subsense/LBSP.h b/Detector/Subsense/LBSP.h deleted file mode 100644 index 52efd88d7..000000000 --- a/Detector/Subsense/LBSP.h +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -#include -#include -#include -#include "DistanceUtils.h" - -/*! - Local Binary Similarity Pattern (LBSP) feature extractor - - Note 1: both grayscale and RGB/BGR images may be used with this extractor. - Note 2: using LBSP::compute2(...) is logically equivalent to using LBSP::compute(...) followed by LBSP::reshapeDesc(...). - - For more details on the different parameters, see G.-A. Bilodeau et al, "Change Detection in Feature Space Using Local - Binary Similarity Patterns", in CRV 2013. - - This algorithm is currently NOT thread-safe. - */ -class LBSP : public cv::DescriptorExtractor { -public: - //! constructor 1, threshold = absolute intensity 'similarity' threshold used when computing comparisons - LBSP(size_t nThreshold); - //! constructor 2, threshold = relative intensity 'similarity' threshold used when computing comparisons - LBSP(float fRelThreshold, size_t nThresholdOffset=0); - //! default destructor - virtual ~LBSP(); - //! loads extractor params from the specified file node @@@@ not impl - virtual void read(const cv::FileNode&); - //! writes extractor params to the specified file storage @@@@ not impl - virtual void write(cv::FileStorage&) const; - //! sets the 'reference' image to be used for inter-frame comparisons (note: if no image is set or if the image is empty, the algorithm will default back to intra-frame comparisons) - virtual void setReference(const cv::Mat&); - //! returns the current descriptor size, in bytes - virtual int descriptorSize() const; - //! returns the current descriptor data type - virtual int descriptorType() const; - //! returns whether this extractor is using a relative threshold or not - virtual bool isUsingRelThreshold() const; - //! returns the current relative threshold used for comparisons (-1 = invalid/not used) - virtual float getRelThreshold() const; - //! returns the current absolute threshold used for comparisons (-1 = invalid/not used) - virtual size_t getAbsThreshold() const; - - //! similar to DescriptorExtractor::compute(const cv::Mat& image, ...), but in this case, the descriptors matrix has the same shape as the input matrix (possibly slower, but the result can be displayed) - void compute2(const cv::Mat& oImage, std::vector& voKeypoints, cv::Mat& oDescriptors) const; - //! batch version of LBSP::compute2(const cv::Mat& image, ...), also similar to DescriptorExtractor::compute(const std::vector& imageCollection, ...) - void compute2(const std::vector& voImageCollection, std::vector >& vvoPointCollection, std::vector& voDescCollection) const; - - //! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (1-channel version) - inline static void computeGrayscaleDescriptor(const cv::Mat& oInputImg, const uchar _ref, const int _x, const int _y, const size_t _t, ushort& _res) { - CV_DbgAssert(!oInputImg.empty()); - CV_DbgAssert(oInputImg.type()==CV_8UC1); - CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size - CV_DbgAssert(_x>=(int)LBSP::PATCH_SIZE/2 && _y>=(int)LBSP::PATCH_SIZE/2); - CV_DbgAssert(_x=(int)LBSP::PATCH_SIZE/2 && _y>=(int)LBSP::PATCH_SIZE/2); - CV_DbgAssert(_x=(int)LBSP::PATCH_SIZE/2 && _y>=(int)LBSP::PATCH_SIZE/2); - CV_DbgAssert(_x=(int)LBSP::PATCH_SIZE/2 && _y>=(int)LBSP::PATCH_SIZE/2); - CV_DbgAssert(_x& voKeypoints, const cv::Mat& oDescriptors, cv::Mat& oOutput); - //! utility function, used to illustrate the difference between two descriptor images - static void calcDescImgDiff(const cv::Mat& oDesc1, const cv::Mat& oDesc2, cv::Mat& oOutput, bool bForceMergeChannels=false); - //! utility function, used to filter out bad keypoints that would trigger out of bounds error because they're too close to the image border - static void validateKeyPoints(std::vector& voKeypoints, cv::Size oImgSize); - //! utility function, used to filter out bad pixels in a ROI that would trigger out of bounds error because they're too close to the image border - static void validateROI(cv::Mat& oROI); - //! utility, specifies the pixel size of the pattern used (width and height) - static const size_t PATCH_SIZE = 5; - //! utility, specifies the number of bytes per descriptor (should be the same as calling 'descriptorSize()') - static const size_t DESC_SIZE = 2; - -protected: - //! classic 'compute' implementation, based on the regular DescriptorExtractor::computeImpl arguments & expected output - virtual void computeImpl(const cv::Mat& oImage, std::vector& voKeypoints, cv::Mat& oDescriptors) const; - - const bool m_bOnlyUsingAbsThreshold; - const float m_fRelThreshold; - const size_t m_nThreshold; - cv::Mat m_oRefImage; -}; diff --git a/Detector/Subsense/LBSP_16bits_dbcross_1ch.i b/Detector/Subsense/LBSP_16bits_dbcross_1ch.i deleted file mode 100644 index ba5ffd0b1..000000000 --- a/Detector/Subsense/LBSP_16bits_dbcross_1ch.i +++ /dev/null @@ -1,44 +0,0 @@ -// note: this is the LBSP 16 bit double-cross single channel pattern as used in -// the original article by G.-A. Bilodeau et al. -// -// O O O 4 .. 3 .. 6 -// O O O .. 15 8 13 .. -// O O X O O => 0 9 X 11 1 -// O O O .. 12 10 14 .. -// O O O 7 .. 2 .. 5 -// -// -// must be defined externally: -// _t (size_t, absolute threshold used for comparisons) -// _ref (uchar, 'central' value used for comparisons) -// _data (uchar*, single-channel data to be covered by the pattern) -// _y (int, pattern rows location in the image data) -// _x (int, pattern cols location in the image data) -// _step_row (size_t, step size between rows, including padding) -// _res (ushort, 16 bit result vector) -// L1dist (function, returns the absolute difference between two uchars) - -#ifdef _val -#error "definitions clash detected" -#else -#define _val(x,y) _data[_step_row*(_y+y)+_x+x] -#endif - -_res = ((L1dist(_val(-1, 1),_ref) > _t) << 15) - + ((L1dist(_val( 1,-1),_ref) > _t) << 14) - + ((L1dist(_val( 1, 1),_ref) > _t) << 13) - + ((L1dist(_val(-1,-1),_ref) > _t) << 12) - + ((L1dist(_val( 1, 0),_ref) > _t) << 11) - + ((L1dist(_val( 0,-1),_ref) > _t) << 10) - + ((L1dist(_val(-1, 0),_ref) > _t) << 9) - + ((L1dist(_val( 0, 1),_ref) > _t) << 8) - + ((L1dist(_val(-2,-2),_ref) > _t) << 7) - + ((L1dist(_val( 2, 2),_ref) > _t) << 6) - + ((L1dist(_val( 2,-2),_ref) > _t) << 5) - + ((L1dist(_val(-2, 2),_ref) > _t) << 4) - + ((L1dist(_val( 0, 2),_ref) > _t) << 3) - + ((L1dist(_val( 0,-2),_ref) > _t) << 2) - + ((L1dist(_val( 2, 0),_ref) > _t) << 1) - + ((L1dist(_val(-2, 0),_ref) > _t)); - -#undef _val diff --git a/Detector/Subsense/LBSP_16bits_dbcross_3ch1t.i b/Detector/Subsense/LBSP_16bits_dbcross_3ch1t.i deleted file mode 100644 index da0ebf9a2..000000000 --- a/Detector/Subsense/LBSP_16bits_dbcross_3ch1t.i +++ /dev/null @@ -1,46 +0,0 @@ -// note: this is the LBSP 16 bit double-cross indiv RGB pattern as used in -// the original article by G.-A. Bilodeau et al. -// -// O O O 4 .. 3 .. 6 -// O O O .. 15 8 13 .. -// O O X O O => 0 9 X 11 1 -// O O O .. 12 10 14 .. -// O O O 7 .. 2 .. 5 -// 3x 3x -// -// must be defined externally: -// _t (size_t, absolute threshold used for comparisons) -// _ref (uchar[3], 'central' values used for comparisons) -// _data (uchar*, triple-channel data to be covered by the pattern) -// _y (int, pattern rows location in the image data) -// _x (int, pattern cols location in the image data) -// _step_row (size_t, step size between rows, including padding) -// _res (ushort[3], 16 bit result vectors vector) -// L1dist (function, returns the absolute difference between two uchars) - -#ifdef _val -#error "definitions clash detected" -#else -#define _val(x,y,n) _data[_step_row*(_y+y)+3*(_x+x)+n] -#endif - -for(int n=0; n<3; ++n) { - _res[n] = ((L1dist(_val(-1, 1, n),_ref[n]) > _t) << 15) - + ((L1dist(_val( 1,-1, n),_ref[n]) > _t) << 14) - + ((L1dist(_val( 1, 1, n),_ref[n]) > _t) << 13) - + ((L1dist(_val(-1,-1, n),_ref[n]) > _t) << 12) - + ((L1dist(_val( 1, 0, n),_ref[n]) > _t) << 11) - + ((L1dist(_val( 0,-1, n),_ref[n]) > _t) << 10) - + ((L1dist(_val(-1, 0, n),_ref[n]) > _t) << 9) - + ((L1dist(_val( 0, 1, n),_ref[n]) > _t) << 8) - + ((L1dist(_val(-2,-2, n),_ref[n]) > _t) << 7) - + ((L1dist(_val( 2, 2, n),_ref[n]) > _t) << 6) - + ((L1dist(_val( 2,-2, n),_ref[n]) > _t) << 5) - + ((L1dist(_val(-2, 2, n),_ref[n]) > _t) << 4) - + ((L1dist(_val( 0, 2, n),_ref[n]) > _t) << 3) - + ((L1dist(_val( 0,-2, n),_ref[n]) > _t) << 2) - + ((L1dist(_val( 2, 0, n),_ref[n]) > _t) << 1) - + ((L1dist(_val(-2, 0, n),_ref[n]) > _t)); -} - -#undef _val diff --git a/Detector/Subsense/LBSP_16bits_dbcross_3ch3t.i b/Detector/Subsense/LBSP_16bits_dbcross_3ch3t.i deleted file mode 100644 index 4662367b8..000000000 --- a/Detector/Subsense/LBSP_16bits_dbcross_3ch3t.i +++ /dev/null @@ -1,46 +0,0 @@ -// note: this is the LBSP 16 bit double-cross indiv RGB pattern as used in -// the original article by G.-A. Bilodeau et al. -// -// O O O 4 .. 3 .. 6 -// O O O .. 15 8 13 .. -// O O X O O => 0 9 X 11 1 -// O O O .. 12 10 14 .. -// O O O 7 .. 2 .. 5 -// 3x 3x -// -// must be defined externally: -// _t (size_t[3], absolute thresholds used for comparisons) -// _ref (uchar[3], 'central' values used for comparisons) -// _data (uchar*, triple-channel data to be covered by the pattern) -// _y (int, pattern rows location in the image data) -// _x (int, pattern cols location in the image data) -// _step_row (size_t, step size between rows, including padding) -// _res (ushort[3], 16 bit result vectors vector) -// L1dist (function, returns the absolute difference between two uchars) - -#ifdef _val -#error "definitions clash detected" -#else -#define _val(x,y,n) _data[_step_row*(_y+y)+3*(_x+x)+n] -#endif - -for(int n=0; n<3; ++n) { - _res[n] = ((L1dist(_val(-1, 1, n),_ref[n]) > _t[n]) << 15) - + ((L1dist(_val( 1,-1, n),_ref[n]) > _t[n]) << 14) - + ((L1dist(_val( 1, 1, n),_ref[n]) > _t[n]) << 13) - + ((L1dist(_val(-1,-1, n),_ref[n]) > _t[n]) << 12) - + ((L1dist(_val( 1, 0, n),_ref[n]) > _t[n]) << 11) - + ((L1dist(_val( 0,-1, n),_ref[n]) > _t[n]) << 10) - + ((L1dist(_val(-1, 0, n),_ref[n]) > _t[n]) << 9) - + ((L1dist(_val( 0, 1, n),_ref[n]) > _t[n]) << 8) - + ((L1dist(_val(-2,-2, n),_ref[n]) > _t[n]) << 7) - + ((L1dist(_val( 2, 2, n),_ref[n]) > _t[n]) << 6) - + ((L1dist(_val( 2,-2, n),_ref[n]) > _t[n]) << 5) - + ((L1dist(_val(-2, 2, n),_ref[n]) > _t[n]) << 4) - + ((L1dist(_val( 0, 2, n),_ref[n]) > _t[n]) << 3) - + ((L1dist(_val( 0,-2, n),_ref[n]) > _t[n]) << 2) - + ((L1dist(_val( 2, 0, n),_ref[n]) > _t[n]) << 1) - + ((L1dist(_val(-2, 0, n),_ref[n]) > _t[n])); -} - -#undef _val diff --git a/Detector/Subsense/LBSP_16bits_dbcross_s3ch.i b/Detector/Subsense/LBSP_16bits_dbcross_s3ch.i deleted file mode 100644 index 6fdc67606..000000000 --- a/Detector/Subsense/LBSP_16bits_dbcross_s3ch.i +++ /dev/null @@ -1,45 +0,0 @@ -// note: this is the LBSP 16 bit double-cross indiv RGB pattern as used in -// the original article by G.-A. Bilodeau et al. -// -// O O O 4 .. 3 .. 6 -// O O O .. 15 8 13 .. -// O O X O O => 0 9 X 11 1 -// O O O .. 12 10 14 .. -// O O O 7 .. 2 .. 5 -// (single/3x) (single/3x) -// -// must be defined externally: -// _t (size_t, absolute threshold used for comparisons) -// _ref (uchar, 'central' value used for comparisons) -// _data (uchar*, triple-channel data to be covered by the pattern) -// _y (int, pattern rows location in the image data) -// _x (int, pattern cols location in the image data) -// _c (size_t, pattern channel location in the image data) -// _step_row (size_t, step size between rows, including padding) -// _res (ushort, 16 bit result vector) -// L1dist (function, returns the absolute difference between two uchars) - -#ifdef _val -#error "definitions clash detected" -#else -#define _val(x,y,n) _data[_step_row*(_y+y)+3*(_x+x)+n] -#endif - -_res = ((L1dist(_val(-1, 1, _c),_ref) > _t) << 15) - + ((L1dist(_val( 1,-1, _c),_ref) > _t) << 14) - + ((L1dist(_val( 1, 1, _c),_ref) > _t) << 13) - + ((L1dist(_val(-1,-1, _c),_ref) > _t) << 12) - + ((L1dist(_val( 1, 0, _c),_ref) > _t) << 11) - + ((L1dist(_val( 0,-1, _c),_ref) > _t) << 10) - + ((L1dist(_val(-1, 0, _c),_ref) > _t) << 9) - + ((L1dist(_val( 0, 1, _c),_ref) > _t) << 8) - + ((L1dist(_val(-2,-2, _c),_ref) > _t) << 7) - + ((L1dist(_val( 2, 2, _c),_ref) > _t) << 6) - + ((L1dist(_val( 2,-2, _c),_ref) > _t) << 5) - + ((L1dist(_val(-2, 2, _c),_ref) > _t) << 4) - + ((L1dist(_val( 0, 2, _c),_ref) > _t) << 3) - + ((L1dist(_val( 0,-2, _c),_ref) > _t) << 2) - + ((L1dist(_val( 2, 0, _c),_ref) > _t) << 1) - + ((L1dist(_val(-2, 0, _c),_ref) > _t)); - -#undef _val diff --git a/Detector/Subsense/LICENSE.txt b/Detector/Subsense/LICENSE.txt deleted file mode 100644 index bfe0e9d86..000000000 --- a/Detector/Subsense/LICENSE.txt +++ /dev/null @@ -1,26 +0,0 @@ -Copyright (c) 2014, P.-L. St-Charles (pierre-luc.st-charles@polymtl.ca) -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Ecole Polytechnique de Montreal nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. diff --git a/Detector/Subsense/README.txt b/Detector/Subsense/README.txt deleted file mode 100644 index a41aabb15..000000000 --- a/Detector/Subsense/README.txt +++ /dev/null @@ -1,21 +0,0 @@ -This directory contains a 'cleaned' version of the SuBSENSE method configuration as presented in -the 2014 CVPRW paper 'Flexible Background Subtraction With Self-Balanced Local Sensitivity'. - -The main class used for background subtraction is BackgroundSubtractionSuBSENSE; all other files -contain either dependencies, utilities or interfaces for this method. It is based on OpenCV's -BackgroundSubtractor interface, and has been tested with versions 2.4.5 and 2.4.7. By default, -its constructor uses the parameters suggested in the paper. - - -TL;DR : - -BackgroundSubtractorSuBSENSE bgs(...); -bgs.initialize(...); -for(all frames in the video) { - ... - bgs(input,output); - ... -} - - -See LICENSE.txt for terms of use and contact information. diff --git a/Detector/Subsense/RandUtils.h b/Detector/Subsense/RandUtils.h deleted file mode 100644 index 24ca5f683..000000000 --- a/Detector/Subsense/RandUtils.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -/*// gaussian 3x3 pattern, based on 'floor(fspecial('gaussian', 3, 1)*256)' -static const int s_nSamplesInitPatternWidth = 3; -static const int s_nSamplesInitPatternHeight = 3; -static const int s_nSamplesInitPatternTot = 256; -static const int s_anSamplesInitPattern[s_nSamplesInitPatternHeight][s_nSamplesInitPatternWidth] = { - {19, 32, 19,}, - {32, 52, 32,}, - {19, 32, 19,}, -};*/ - -// gaussian 7x7 pattern, based on 'floor(fspecial('gaussian',7,2)*512)' -static const int s_nSamplesInitPatternWidth = 7; -static const int s_nSamplesInitPatternHeight = 7; -static const int s_nSamplesInitPatternTot = 512; -static const int s_anSamplesInitPattern[s_nSamplesInitPatternHeight][s_nSamplesInitPatternWidth] = { - {2, 4, 6, 7, 6, 4, 2,}, - {4, 8, 12, 14, 12, 8, 4,}, - {6, 12, 21, 25, 21, 12, 6,}, - {7, 14, 25, 28, 25, 14, 7,}, - {6, 12, 21, 25, 21, 12, 6,}, - {4, 8, 12, 14, 12, 8, 4,}, - {2, 4, 6, 7, 6, 4, 2,}, -}; - -//! returns a random init/sampling position for the specified pixel position; also guards against out-of-bounds values via image/border size check. -static inline void getRandSamplePosition(int& x_sample, int& y_sample, const int x_orig, const int y_orig, const int border, const cv::Size& imgsize) { - int r = 1+rand()%s_nSamplesInitPatternTot; - for(x_sample=0; x_sample=imgsize.width-border) - x_sample = imgsize.width-border-1; - if(y_sample=imgsize.height-border) - y_sample = imgsize.height-border-1; -} - -// simple 8-connected (3x3) neighbors pattern -static const int s_anNeighborPatternSize_3x3 = 8; -static const int s_anNeighborPattern_3x3[8][2] = { - {-1, 1}, { 0, 1}, { 1, 1}, - {-1, 0}, { 1, 0}, - {-1,-1}, { 0,-1}, { 1,-1}, -}; - -//! returns a random neighbor position for the specified pixel position; also guards against out-of-bounds values via image/border size check. -static inline void getRandNeighborPosition_3x3(int& x_neighbor, int& y_neighbor, const int x_orig, const int y_orig, const int border, const cv::Size& imgsize) { - int r = rand()%s_anNeighborPatternSize_3x3; - x_neighbor = x_orig+s_anNeighborPattern_3x3[r][0]; - y_neighbor = y_orig+s_anNeighborPattern_3x3[r][1]; - if(x_neighbor=imgsize.width-border) - x_neighbor = imgsize.width-border-1; - if(y_neighbor=imgsize.height-border) - y_neighbor = imgsize.height-border-1; -} - -// 5x5 neighbors pattern -static const int s_anNeighborPatternSize_5x5 = 24; -static const int s_anNeighborPattern_5x5[24][2] = { - {-2, 2}, {-1, 2}, { 0, 2}, { 1, 2}, { 2, 2}, - {-2, 1}, {-1, 1}, { 0, 1}, { 1, 1}, { 2, 1}, - {-2, 0}, {-1, 0}, { 1, 0}, { 2, 0}, - {-2,-1}, {-1,-1}, { 0,-1}, { 1,-1}, { 2,-1}, - {-2,-2}, {-1,-2}, { 0,-2}, { 1,-2}, { 2,-2}, -}; - -//! returns a random neighbor position for the specified pixel position; also guards against out-of-bounds values via image/border size check. -static inline void getRandNeighborPosition_5x5(int& x_neighbor, int& y_neighbor, const int x_orig, const int y_orig, const int border, const cv::Size& imgsize) { - int r = rand()%s_anNeighborPatternSize_5x5; - x_neighbor = x_orig+s_anNeighborPattern_5x5[r][0]; - y_neighbor = y_orig+s_anNeighborPattern_5x5[r][1]; - if(x_neighbor=imgsize.width-border) - x_neighbor = imgsize.width-border-1; - if(y_neighbor=imgsize.height-border) - y_neighbor = imgsize.height-border-1; -} diff --git a/Detector/pedestrians/c4-pedestrian-detector.cpp b/Detector/pedestrians/c4-pedestrian-detector.cpp deleted file mode 100644 index d3961fd4a..000000000 --- a/Detector/pedestrians/c4-pedestrian-detector.cpp +++ /dev/null @@ -1,291 +0,0 @@ -#include "c4-pedestrian-detector.h" - -/*****************************************/ -// Pedestrian_ICRA.cpp -/*****************************************/ - -// --------------------------------------------------------------------- -// Helper functions - -// compute the Sobel image "ct" from "original" -void ComputeCT(IntImage& original,IntImage& ct) -{ - ct.Create(original.nrow,original.ncol); - for(int i=2; i& result) -{ - std::ifstream in(modelfile); - if(in.good()==false) - { - std::cout<<"SVM model "<>buffer; - assert(buffer=="nr_feature"); - int num_dim = m; - in>>num_dim; - assert(num_dim>0 && num_dim==m); - std::getline(in,buffer); // end of line 4 - in>>buffer; - assert(buffer=="bias"); - int bias; - in>>bias; - std::getline(in,buffer); //end of line 5; - in>>buffer; - assert(buffer=="w"); - std::getline(in,buffer); //end of line 6 - result.Create(1,num_dim); - for(int i=0; i>result.buf[i]; - double rho = 0; - if(bias>=0) in>>rho; - in.close(); - return rho; -} - -// Load SVM models -- Histogram Intersectin Kernel SVM trained by libHIK -double UseSVM_CD_FastEvaluationStructure(const char* modelfile, const int m, const int upper_bound, Array2dC& result) -{ - - std::ifstream fs(modelfile, std::fstream::binary); - if( !fs.is_open() ) - { - std::cout << "SVM model " << modelfile << " can not be loaded." << std::endl; - exit(-1); - } - // Header - int rows, cols, type, channels; - fs.read((char*)&rows, sizeof(int)); // rows - fs.read((char*)&cols, sizeof(int)); // cols - fs.read((char*)&type, sizeof(int)); // type - fs.read((char*)&channels, sizeof(int)); // channels - - // Data - cv::Mat mat(rows, cols, type); - fs.read((char*)mat.data, CV_ELEM_SIZE(type) * rows * cols); - - int num_dim = m; - - result.Create(num_dim, upper_bound); - for(int i=0; i(i, j); - } - - return -0.00455891; -} - -// End of Helper functions -// --------------------------------------------------------------------- - -// --------------------------------------------------------------------- -// Functions that load the two classifiers -void LoadCascade(std::string cascade1, std::string cascade2, DetectionScanner& ds) -{ - std::vector types; - std::vector upper_bounds; - std::vector filenames; - - types.push_back(NodeDetector::CD_LIN); // first node - upper_bounds.push_back(100); - filenames.push_back(cascade1); - types.push_back(NodeDetector::CD_HIK); // second node - upper_bounds.push_back(353); - filenames.push_back(cascade2); - - ds.LoadDetector(types,upper_bounds,filenames); - // You can adjust these parameters for different speed, accuracy etc - ds.cascade->nodes[0]->thresh += 0.8; - ds.cascade->nodes[1]->thresh -= 0.095; -} - -void DetectionScanner::LoadDetector(std::vector& types,std::vector& upper_bounds,std::vector& filenames) -{ - unsigned int depth = types.size(); - assert(depth>0 && depth==upper_bounds.size() && depth==filenames.size()); - if(cascade) - delete cascade; - cascade = new CascadeDetector; - assert(xdiv>0 && ydiv>0); - for(unsigned int i=0; iAddNode(types[i],(xdiv-EXT)*(ydiv-EXT)*baseflength,upper_bounds[i],filenames[i].c_str()); - - hist.Create(1,baseflength*(xdiv-EXT)*(ydiv-EXT)); -} - -void NodeDetector::Load(const NodeType _type,const int _featurelength,const int _upper_bound,const int _index,const char* _filename) -{ - type = _type; - index = _index; - filename = _filename; - featurelength = _featurelength; - upper_bound = _upper_bound; - if(type==CD_LIN) - thresh = UseSVM_CD_FastEvaluationStructure(_filename,_featurelength,classifier); - else if(type==CD_HIK) - thresh = UseSVM_CD_FastEvaluationStructure(_filename,_featurelength,upper_bound,classifier); - - if(type==CD_LIN) type = LINEAR; - if(type==CD_HIK) type = HISTOGRAM; -} - -void CascadeDetector::AddNode(const NodeDetector::NodeType _type,const int _featurelength,const int _upper_bound,const char* _filename) -{ - if(length==size) - { - int newsize = size * 2; - NodeDetector** p = new NodeDetector*[newsize]; - assert(p!=NULL); - std::copy(nodes,nodes+size,p); - size = newsize; - delete[] nodes; - nodes = p; - } - nodes[length] = new NodeDetector(_type,_featurelength,_upper_bound,length,_filename); - length++; -} - -// End of functions that load the two classifiers -// --------------------------------------------------------------------- - -// --------------------------------------------------------------------- -// Detection functions - -// initialization -- compute the Census Tranform image for CENTRIST -void DetectionScanner::InitImage(IntImage& original) -{ - image = original; - image.Sobel(sobel,false,false); - ComputeCT(sobel,ct); -} - -// combine the (xdiv-1)*(ydiv-1) integral images into a single one -void DetectionScanner::InitIntegralImages(const int stepsize) -{ - if(cascade->nodes[0]->type!=NodeDetector::LINEAR) - return; // No need to prepare integral images - - const int hd = height/xdiv*2-2; - const int wd = width/ydiv*2-2; - scores.Create(ct.nrow,ct.ncol); - scores.Zero(cascade->nodes[0]->thresh/hd/wd); - double* linearweights = cascade->nodes[0]->classifier.buf; - for(int i=0; i& original,std::vector& results,const int stepsize) -{ - if(original.nrownodes[1]; - double** pc = node->classifier.p; - int oheight = original.nrow, owidth = original.ncol; - cv::Rect rect; - while(image.nrow>=height && image.ncol>=width) - { - InitIntegralImages(stepsize); - for(int i=2; i+heightthresh; - for(int k=0; kclassifier.nrow; k++) score += pc[k][hist.buf[k]]; - if(score>0) - { - rect.y = i * oheight / image.nrow; - rect.height = (oheight * height) / image.nrow + 1; - rect.x = j * owidth / image.ncol; - rect.width = (width * owidth) /image.ncol + 1; - results.push_back(rect); - } - } - } - ResizeImage(); - } - return 0; -} - -// End of Detection functions -// --------------------------------------------------------------------- diff --git a/Detector/pedestrians/c4-pedestrian-detector.h b/Detector/pedestrians/c4-pedestrian-detector.h deleted file mode 100644 index 32f472f3c..000000000 --- a/Detector/pedestrians/c4-pedestrian-detector.h +++ /dev/null @@ -1,560 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define USE_DOUBLE - -#ifdef USE_DOUBLE -typedef double REAL; -#else -typedef float REAL; -#endif - -template class Array2dC; - -template -class Array2d -{ -public: - int nrow; - int ncol; - T** p; -public: - Array2d():nrow(0),ncol(0),p(NULL) { } - Array2d(const int nrow,const int ncol):nrow(0),ncol(0),p(NULL) - { - Create(nrow,ncol); - } - Array2d(const Array2d& source); - virtual ~Array2d() - { - Clear(); - } - - Array2d& operator=(const Array2d& source); - void Create(const int _nrow,const int _ncol); - void Swap(Array2d& array2); - void Clear(); - void Zero(const T t = 0); -}; - -template -class Array2dC -{ -public: - int nrow; - int ncol; - T** p; - T* buf; -public: - Array2dC():nrow(0),ncol(0),p(NULL),buf(NULL) {} - Array2dC(const int nrow,const int ncol):nrow(0),ncol(0),p(NULL),buf(NULL) - { - Create(nrow,ncol); - } - Array2dC(const Array2dC& source); - virtual ~Array2dC() - { - Clear(); - } - - Array2dC& operator=(const Array2dC& source); - void Create(const int _nrow,const int _ncol); - void Swap(Array2dC& array2); - void Zero(const T t = 0); - void Clear(); -}; - -template -Array2d::Array2d(const Array2d& source):nrow(0),ncol(0),p(NULL) -{ - if(source.p!=NULL) - { - Create(source.nrow,source.ncol); - for(int i=0; i -Array2d& Array2d::operator=(const Array2d& source) -{ - if(source.p!=NULL) - { - Create(source.nrow,source.ncol); - for(int i=0; i -void Array2d::Create(const int _nrow,const int _ncol) -{ - assert(_nrow>0 && _ncol>0); - Clear(); - nrow = _nrow; - ncol = _ncol; - p = new T*[nrow]; - assert(p!=NULL); - for(int i=0; i -void Array2d::Swap(Array2d& array2) -{ - std::swap(nrow,array2.nrow); - std::swap(ncol,array2.ncol); - std::swap(p,array2.p); -} - -template -void Array2d::Zero(const T t) -{ - if(nrow>0) - { - for(int i=0; i -void Array2d::Clear() -{ - for(int i=0; i -Array2dC::Array2dC(const Array2dC& source):nrow(0),ncol(0),p(NULL),buf(NULL) -{ - if(source.buf!=NULL) - { - Create(source.nrow,source.ncol); - std::copy(source.buf,source.buf+nrow*ncol,buf); - } -} - -template -Array2dC& Array2dC::operator=(const Array2dC& source) -{ - if(source.buf!=NULL) - { - Create(source.nrow,source.ncol); - std::copy(source.buf,source.buf+nrow*ncol,buf); - } - else - Clear(); - return *this; -} - -template -void Array2dC::Create(const int _nrow,const int _ncol) -{ - assert(_nrow>0 && _ncol>0); - if(nrow==_nrow && ncol==_ncol) return; - Clear(); - nrow = _nrow; - ncol = _ncol; - buf = new T[nrow*ncol]; - assert(buf!=NULL); - p = new T*[nrow]; - assert(p!=NULL); - for(int i=0; i -void Array2dC::Swap(Array2dC& array2) -{ - std::swap(nrow,array2.nrow); - std::swap(ncol,array2.ncol); - std::swap(p,array2.p); - std::swap(buf,array2.buf); -} - -template -void Array2dC::Zero(const T t) -{ - if(nrow>0) std::fill(buf,buf+nrow*ncol,t); -} - -template -void Array2dC::Clear() -{ - delete[] buf; - buf = NULL; - delete[] p; - p = NULL; - nrow = ncol = 0; -} - - -/*****************************************/ -// IntImage.h -/*****************************************/ - -template -class IntImage:public Array2dC -{ -private: - IntImage(const IntImage &source) { } // prohibit copy constructor - -public: - IntImage():variance(0.0),label(-1) { } - virtual ~IntImage() - { - Clear(); - } - - virtual void Clear(void); - inline void SetSize(const int h, const int w); - bool Load(cv::Mat img); - void Save(const std::string& filename) const; - void Swap(IntImage& image2); - - void CalcIntegralImageInPlace(void); - void Resize(IntImage &result,const REAL ratio) const; - void Resize(IntImage& result,const int height,const int width) const; - - IntImage& operator=(const IntImage& source); - - void Sobel(IntImage& result,const bool useSqrt,const bool normalize); -public: - using Array2dC::nrow; - using Array2dC::ncol; - using Array2dC::buf; - using Array2dC::p; - REAL variance; - int label; -}; - -template -void IntImage::Clear(void) -{ - Array2dC::Clear(); - variance = 0.0; - label = -1; -} - -template -bool IntImage::Load(cv::Mat img) -{ - if (img.empty()) return false; - - SetSize(img.rows, img.cols); - for(int i=0,ih=img.rows,iw=img.cols; i(img.data+img.step*i); - for(int j=0; j -void IntImage::Save(const std::string& filename) const -{ - IplImage* img; - - img = cvCreateImage(cvSize(ncol,nrow),IPL_DEPTH_8U,1); - for(int i=0,ih=img->height,iw=img->width; i(img->imageData+img->widthStep*i); - for(int j=0; j -void IntImage::SetSize(const int h,const int w) -{ - if((h == nrow) && (w == ncol)) return; - Clear(); - Array2dC::Create(h,w); -} - -template -IntImage& IntImage::operator=(const IntImage& source) -{ - if(&source==this) return *this; - SetSize(source.nrow,source.ncol); - std::copy(source.buf,source.buf+nrow*ncol,buf); - label = source.label; - variance = source.variance; - return *this; -} - -template -void IntImage::Resize(IntImage &result,const REAL ratio) const -{ - Resize(result,int(nrow*ratio),int(ncol*ratio)); -} - -template -void IntImage::Resize(IntImage& result,const int height,const int width) const -{ - assert(height>0 && width>0); - result.SetSize(height,width); - REAL ixratio = nrow*1.0/height, iyratio = ncol*1.0/width; - - REAL* p_y = new REAL[result.ncol]; - assert(p_y!=NULL); - int* p_y0 = new int[result.ncol]; - assert(p_y0!=NULL); - for(int i=0; i -void IntImage::CalcIntegralImageInPlace(void) -// We pad a zero column and a zero row, so 24*24 image will be 25*25 in size -// if the input image is not padded, the results on 1st row will be problematic -{ - for(int i=1; i -void IntImage::Swap(IntImage& image2) -{ - Array2dC::Swap(image2); - std::swap(variance,image2.variance); - std::swap(label,image2.label); -} - -template -void IntImage::Sobel(IntImage& result,const bool useSqrt,const bool normalize) -{ - // compute the Sobel gradient. For now, we just use the very inefficient way. Optimization can be done later -// if useSqrt = true, we compute the real Sobel gradient; otherwise, the square of it -// if normalize = true, the numbers are normalized to be in 0..255 - result.Create(nrow,ncol); - for(int i=0; imaxv) - maxv = result.p[i][j]; - } - } - for(int i=0; i classifier; - double thresh; - int featurelength; - int upper_bound; - int index; - std::string filename; -public: - NodeDetector(const NodeType _type,const int _featurelength,const int _upper_bound,const int _index,const char* _filename) - { - Load(_type,_featurelength,_upper_bound,_index,_filename); - minvalue = DBL_MAX; - maxvalue = -minvalue; - } - ~NodeDetector() - { - } - - void Load(const NodeType _type,const int _featurelength,const int _upper_bound,const int _index,const char* _filename); - bool Classify(int* f); -private: - double minvalue; - double maxvalue; -public: - void SetValues(const double v) - { - if(v>maxvalue) maxvalue = v; - if(v& types,std::vector& upper_bounds,std::vector& filenames); - - int Scan(IntImage& original,std::vector& results,const int stepsize,const int round,std::ofstream* out,const int upper_bound); - int FastScan(IntImage& original,std::vector& results,const int stepsize); - int FeatureLength() const - { - return (xdiv-1)*(ydiv-1)*baseflength; - } - - - int height; - int width; - int xdiv; - int ydiv; - int baseflength; - double ratio; - static const int EXT = 1; - - CascadeDetector* cascade; - -private: - IntImage* integrals; - IntImage image,sobel; - IntImage ct; - Array2dC hist; - IntImage scores; - - void InitImage(IntImage& original); - void InitIntegralImages(const int stepsize); - void ResizeImage(); -}; - -void LoadCascade(std::string cascade1, std::string cascade2, DetectionScanner& ds); diff --git a/Detector/vibe_src/vibe.cpp b/Detector/vibe_src/vibe.cpp deleted file mode 100644 index e6dd7fdd2..000000000 --- a/Detector/vibe_src/vibe.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include "vibe.hpp" -#include -#include - -namespace vibe -{ - -VIBE::VIBE(int channels, int samples, int pixel_neighbor, int distance_threshold, int matching_threshold, int update_factor): - samples_(samples), - channels_(channels), - pixel_neighbor_(pixel_neighbor), - distance_threshold_(distance_threshold), - matching_threshold_(matching_threshold), - update_factor_(update_factor) -{ - model_ = nullptr; - rng_idx_ = 0; - srand(0); - for (int i = 0; i < RANDOM_BUFFER_SIZE; i ++) - { - rng_[i] = rand(); - } -} - -VIBE::~VIBE() -{ - if (model_ != nullptr) - { - delete[] model_; - } -} - -cv::Vec2i VIBE::getRndNeighbor(int i, int j) -{ - int neighbor_count = (pixel_neighbor_ * 2 + 1) * (pixel_neighbor_ * 2 + 1); - int rnd = rng_[rng_idx_ = ( rng_idx_ + 1 ) % RANDOM_BUFFER_SIZE] % neighbor_count; - int start_i = i - pixel_neighbor_; - int start_j = j - pixel_neighbor_; - int area = pixel_neighbor_ * 2 + 1; - int position_i = rnd / area; - int position_j = rnd % area; - int cur_i = std::max(std::min(start_i + position_i, size_.height - 1), 0); - int cur_j = std::max(std::min(start_j + position_j, size_.width - 1), 0); - return cv::Vec2i(cur_i, cur_j); -} - -void VIBE::init(const cv::Mat &img) -{ - CV_Assert(img.channels() == channels_); - - size_ = img.size(); - - if (model_ != nullptr) - { - delete[] model_; - } - model_ = new unsigned char[channels_ * samples_ * size_.width * size_.height]; - - mask_ = cv::Mat(size_, CV_8UC1, cv::Scalar::all(0)); - - unsigned char* image = img.data; - for (int i = 0; i < img.rows; i++) - { - for (int j = 0; j < img.cols; j++) - { - for (int c = 0; c < channels_; c++) - { - model_[channels_ * samples_ * size_.width * i + channels_ * samples_ * j + c] = image[channels_ * size_.width * i + channels_ * j + c]; - } - for (int s = 1; s < samples_; s++) - { - cv::Vec2i rnd_pos = getRndNeighbor(i, j); - int img_idx = channels_ * size_.width * rnd_pos[0] + channels_ * rnd_pos[1]; - int model_idx = channels_ * samples_ * size_.width * i + channels_ * samples_ * j + channels_ * s; - for (int c = 0; c < channels_; c ++) - { - model_[model_idx + c] = image[img_idx + c]; - } - } - } - } -} - -void VIBE::update(const cv::Mat& img) -{ - CV_Assert(channels_ == img.channels()); - - if (size_ != img.size()) - { - init(img); - return; - } - - unsigned char *img_ptr = img.data; - for (int i = 0; i < img.rows; i++) - { - for (int j = 0; j < img.cols; j++) - { - bool flag = false; - int matching_counter = 0; - int img_idx = channels_ * size_.width * i + channels_ * j; - for (int s = 0; s < samples_; s ++) - { - int model_idx = channels_ * samples_ * size_.width * i + channels_ * samples_ * j + channels_ * s; - int channels_counter = 0; - for (int c = 0; c < channels_; c ++) - { - if (std::abs(img_ptr[img_idx + c] - model_[model_idx + c]) < distance_threshold_) - { - channels_counter++; - } - } - if (channels_counter == channels_) - { - matching_counter++; - } - if (matching_counter > matching_threshold_) - { - flag = true; - break; - } - } - - if (flag) - { - mask_.data[size_.width * i + j] = 0; - if (0 == rng_[ rng_idx_ = ( rng_idx_ + 1 ) % RANDOM_BUFFER_SIZE] % update_factor_) - { - int sample = rng_[ rng_idx_ = ( rng_idx_ + 1 ) % RANDOM_BUFFER_SIZE] % samples_; - int model_idx = channels_ * samples_ * size_.width * i + channels_ * samples_ * j + channels_ * sample; - for (int c = 0; c < channels_; c ++) - { - model_[model_idx + c] = img_ptr[img_idx + c]; - } - - cv::Vec2i rnd_pos = getRndNeighbor(i, j); - sample = rng_[rng_idx_ = ( rng_idx_ + 1) % RANDOM_BUFFER_SIZE] % samples_; - model_idx = channels_ * samples_ * size_.width * rnd_pos[0] + channels_ * samples_ * rnd_pos[1] + channels_ * sample; - for (int c = 0; c < channels_; c ++) - { - model_[model_idx + c] = img_ptr[img_idx + c]; - } - - } - } - else - { - mask_.data[size_.width * i + j] = 255; - } - } - } -} - -cv::Mat& VIBE::getMask() -{ - return mask_; -} - - -} diff --git a/Detector/vibe_src/vibe.hpp b/Detector/vibe_src/vibe.hpp deleted file mode 100644 index 354fb8f41..000000000 --- a/Detector/vibe_src/vibe.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef __VIBE_HPP__ -#define __VIBE_HPP__ - -#include -#include - -#define RANDOM_BUFFER_SIZE (65535) - -namespace vibe -{ - -class VIBE -{ -public: - VIBE(int channels = 1, int samples = 20, int pixel_neighbor = 1, int distance_threshold = 20, int matching_threshold = 3, int update_factor = 16); - ~VIBE(); - - void update(const cv::Mat& img); - cv::Mat& getMask(); - - int GetChannels() const - { - return channels_; - } - -private: - int samples_; - int channels_; - int pixel_neighbor_; - int distance_threshold_; - int matching_threshold_; - int update_factor_; - - cv::Size size_; - unsigned char *model_; - - cv::Mat mask_; - - unsigned int rng_[RANDOM_BUFFER_SIZE]; - int rng_idx_; - - cv::Vec2i getRndNeighbor(int i, int j); - void init(const cv::Mat& img); -}; - -} - -#endif /*__VIBE_HPP__*/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/MTTrackingConfig.cmake.in b/MTTrackingConfig.cmake.in new file mode 100644 index 000000000..796457847 --- /dev/null +++ b/MTTrackingConfig.cmake.in @@ -0,0 +1,10 @@ +set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@) + +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") + +set_and_check(@PROJECT_NAME@_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@") +set_and_check(@PROJECT_NAME@_LIB_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@../lib") + +check_required_components(@PROJECT_NAME@) diff --git a/MouseExample.h b/MouseExample.h deleted file mode 100644 index a428980df..000000000 --- a/MouseExample.h +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once - -//#include - -#include "Ctracker.h" - -#include -#include - -//------------------------------------------------------------------------ -// Mouse callbacks -//------------------------------------------------------------------------ -void mv_MouseCallback(int event, int x, int y, int /*flags*/, void* param) -{ - if (event == cv::EVENT_MOUSEMOVE) - { - cv::Point2f* p = (cv::Point2f*)param; - if (p) - { - p->x = static_cast(x); - p->y = static_cast(y); - } - } -} - -// ---------------------------------------------------------------------- -void MouseTracking(cv::CommandLineParser parser) -{ - std::string outFile = parser.get("out"); - - cv::VideoWriter writer; - - int k = 0; - std::vector colors = { cv::Scalar(255, 0, 0), cv::Scalar(0, 255, 0), cv::Scalar(0, 0, 255), cv::Scalar(255, 255, 0), cv::Scalar(0, 255, 255), cv::Scalar(255, 0, 255), cv::Scalar(255, 127, 255), cv::Scalar(127, 0, 255), cv::Scalar(127, 0, 127) }; - cv::namedWindow("Video"); - cv::Mat frame = cv::Mat(800, 800, CV_8UC3); - - if (!writer.isOpened()) - { - writer.open(outFile, cv::VideoWriter::fourcc('P', 'I', 'M', '1'), 20, frame.size(), true); - } - - // Set mouse callback - cv::Point2f pointXY; - cv::setMouseCallback("Video", mv_MouseCallback, (void*)&pointXY); - - bool useLocalTracking = false; - - CTracker tracker(useLocalTracking, - tracking::DistCenters, - tracking::KalmanLinear, - tracking::FilterCenter, - tracking::TrackNone, - tracking::MatchHungrian, - 0.2f, - 0.5f, - 100.0f, - 25, - 25); - track_t alpha = 0; - cv::RNG rng; - while (k != 27) - { - frame = cv::Scalar::all(0); - - // Noise addition (measurements/detections simulation ) - float Xmeasured = pointXY.x + static_cast(rng.gaussian(2.0)); - float Ymeasured = pointXY.y + static_cast(rng.gaussian(2.0)); - - // Append circulating around mouse cv::Points (frequently intersecting) - std::vector pts; - pts.push_back(Point_t(Xmeasured + 100.0f*sin(-alpha), Ymeasured + 100.0f*cos(-alpha))); - pts.push_back(Point_t(Xmeasured + 100.0f*sin(alpha), Ymeasured + 100.0f*cos(alpha))); - pts.push_back(Point_t(Xmeasured + 100.0f*sin(alpha / 2.0f), Ymeasured + 100.0f*cos(alpha / 2.0f))); - pts.push_back(Point_t(Xmeasured + 100.0f*sin(alpha / 3.0f), Ymeasured + 100.0f*cos(alpha / 1.0f))); - alpha += 0.05f; - - regions_t regions; - for (auto p : pts) - { - regions.push_back(CRegion(cv::Rect(cvRound(p.x), cvRound(p.y), 1, 1))); - } - - - for (size_t i = 0; i < pts.size(); i++) - { - cv::circle(frame, pts[i], 3, cv::Scalar(0, 255, 0), 1, CV_AA); - } - - tracker.Update(regions, cv::UMat()); - - std::cout << tracker.tracks.size() << std::endl; - - for (size_t i = 0; i < tracker.tracks.size(); i++) - { - const auto& track = tracker.tracks[i]; - - if (track->m_trace.size() > 1) - { - for (size_t j = 0; j < track->m_trace.size() - 1; j++) - { - cv::line(frame, track->m_trace[j], track->m_trace[j + 1], colors[i % colors.size()], 2, CV_AA); - } - } - } - - cv::imshow("Video", frame); - - if (writer.isOpened()) - { - writer << frame; - } - - k = cv::waitKey(10); - } -} diff --git a/README.md b/README.md index ccccb1f94..a7850596d 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,143 @@ -![travis ci:](https://travis-ci.org/Smorodov/Multitarget-tracker.svg?branch=master) - -# Multitarget-tracker - -Hungarian algorithm + Kalman filter multitarget tracker implementation. - -#### Demo Videos - -* Mouse tracking: - -[![Tracking:](https://img.youtube.com/vi/2fW5TmAtAXM/0.jpg)](https://www.youtube.com/watch?v=2fW5TmAtAXM) - -* Motion Detection and tracking: - -[![Motion Detection and tracking:](https://img.youtube.com/vi/GjN8jOy4kVw/0.jpg)](https://www.youtube.com/watch?v=GjN8jOy4kVw) - -* Multiple Faces tracking: - -[![Multiple Faces tracking:](https://img.youtube.com/vi/j67CFwFtciU/0.jpg)](https://www.youtube.com/watch?v=j67CFwFtciU) - -#### Parameters -1. Background substraction: built-in Vibe, SuBSENSE and LOBSTER; MOG2 from opencv; MOG, GMG and CNT from opencv_contrib -2. Foreground segmentation: contours -3. Matching: Hungrian algorithm or algorithm based on weighted bipartite graphs -4. Tracking: Linear or Unscented Kalman filter for objects center or for object coordinates and size -5. Use or not local tracker (LK optical flow) for smooth trajectories -6. KCF, MIL, MedianFlow, GOTURN or MOSSE tracking for lost objects and collision resolving -7. Haar face detector from OpenCV -8. HOG and C4 pedestrian detectors -9. SSD detector from OpenCV and models from chuanqi305/MobileNet-SSD - -#### Build -1. Download project sources -2. Install CMake -3. Install OpenCV (https://github.com/opencv/opencv) and OpenCV contrib (https://github.com/opencv/opencv_contrib) repositories -4. Configure project CmakeLists.txt, set OpenCV_DIR. If opencv_contrib don't installed then set disable options USE_OCV_BGFG, USE_OCV_KCF and USE_OCV_UKF -5. Go to the build directory and run make - -**Usage:** - - Usage: - ./MultitargetTracker [--example]= [--start_frame]= [--end_frame]= [--end_delay]= [--out]= [--show_logs]= [--gpu]= - ./MultitargetTracker ../data/atrium.avi -e=1 -o=../data/atrium_motion.avi - Press: - * 'm' key for change mode: play|pause. When video is paused you can press any key for get next frame. - * Press Esc to exit from video - - Params: - 1. Movie file, for example ../data/atrium.avi - 2. [Optional] Number of example: 0 - MouseTracking, 1 - MotionDetector, 2 - FaceDetector, 3 - PedestrianDetector, 4 - Hybrid face and motion detectors, 5 - MobileNet SSD detector - -e=0 or --example=1 - 3. [Optional] Frame number to start a video from this position - -sf=0 or --start_frame==1500 - 4. [Optional] Play a video to this position (if 0 then played to the end of file) - -ef=0 or --end_frame==200 - 5. [Optional] Delay in milliseconds after video ending - -ed=0 or --end_delay=1000 - 6. [Optional] Name of result video file - -o=out.avi or --out=result.mp4 - 7. [Optional] Show Trackers logs in terminal - -sl=1 or --show_logs=0 - 8. [Optional] Use built-in OpenCL - -g=1 or --gpu=0 - -#### Thirdparty libraries -* OpenCV (and contrib): https://github.com/opencv/opencv and https://github.com/opencv/opencv_contrib -* Vibe: https://github.com/BelBES/VIBE -* SuBSENSE and LOBSTER: https://github.com/ethereon/subsense -* GTL: https://github.com/rdmpage/graph-template-library -* MWBM: https://github.com/rdmpage/maximum-weighted-bipartite-matching -* Pedestrians detector: https://github.com/sturkmen72/C4-Real-time-pedestrian-detection -* Non Maximum Suppression: https://github.com/Nuzhny007/Non-Maximum-Suppression -* MobileNet SSD: https://github.com/chuanqi305/MobileNet-SSD -* GOTURN models: https://github.com/opencv/opencv_extra/tree/c4219d5eb3105ed8e634278fad312a1a8d2c182d/testdata/tracking - -#### License -GNU GPLv3: http://www.gnu.org/licenses/gpl-3.0.txt +# Multitarget Tracker + +[![Build Ubuntu](https://github.com/Smorodov/Multitarget-tracker/actions/workflows/ubuntu.yml/badge.svg)](https://github.com/Smorodov/Multitarget-tracker/actions/workflows/ubuntu.yml) +[![Build MacOS](https://github.com/Smorodov/Multitarget-tracker/actions/workflows/macos.yml/badge.svg)](https://github.com/Smorodov/Multitarget-tracker/actions/workflows/macos.yml) +[![CodeQL](https://github.com/Smorodov/Multitarget-tracker/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/Smorodov/Multitarget-tracker/actions/workflows/codeql-analysis.yml) + +## Latest Features + +- Add new SOTA: YOLOv26, YOLOv26-obb and YOLOv26-seg models from [ultralytics/ultralytics](https://github.com/ultralytics/ultralytics) +- Add RT-DETRv4 (API similar D-FINE) detection model [RT-DETRs/RT-DETRv4](https://github.com/RT-DETRs/RT-DETRv4) +- Add D-FINE seg detection model [ArgoHA/D-FINE-seg](https://github.com/ArgoHA/D-FINE-seg) +- Add ByteTrack MOT algorithm based on [Vertical-Beach/ByteTrack-cpp](https://github.com/Vertical-Beach/ByteTrack-cpp) +- Big code cleanup from old style algorithms and detectors: some bgfg detectors, some VOT trackes, Face and Pedestrin detectors, Darknet based backend for old YOLO etc +- YOLOv13 detector works with TensorRT! Export pre-trained PyTorch models [here (iMoonLab/yolov13)](https://github.com/iMoonLab/yolov13) to ONNX format and run Multitarget-tracker with `-e=3` example +- Instance segmentation model from RF-DETR detector works with TensorRT! Export pre-trained PyTorch models [here (roboflow/rf-detr)](https://github.com/roboflow/rf-detr) to ONNX format and run Multitarget-tracker with `-e=3` example +- New linear assignment algorithm - [Jonker-Volgenant / LAPJV algorithm](https://github.com/yongyanghz/LAPJV-algorithm-c) used in [scipy](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.linear_sum_assignment.html) as alternative for Hungarian allgorithm +- D-FINE detector works with TensorRT! Export pre-trained PyTorch models [here (Peterande/D-FINE)](https://github.com/Peterande/D-FINE) to ONNX format and run Multitarget-tracker with `-e=3` example +- RF-DETR detector works with TensorRT! Export pre-trained PyTorch models [here (roboflow/rf-detr)](https://github.com/roboflow/rf-detr) to ONNX format and run Multitarget-tracker with `-e=3` example +- YOLOv12 detector works with TensorRT! Export pre-trained PyTorch models [here (sunsmarterjie/yolov12)](https://github.com/sunsmarterjie/yolov12) to ONNX format and run Multitarget-tracker with `-e=3` example + +## Demo Videos + +### Detection & Tracking + +[![RF-DETR: detection vs instance segmentation](https://img.youtube.com/vi/oKy7jEKT83c/0.jpg)](https://youtu.be/oKy7jEKT83c) +[![Satellite planes detection and tracking with YOLOv11-obb](https://img.youtube.com/vi/gTpWnkMF7Lg/0.jpg)](https://youtu.be/gTpWnkMF7Lg) +[![4-in-1 latest SOTA detectors](https://img.youtube.com/vi/Pb_HnejRpY4/0.jpg)](https://youtu.be/Pb_HnejRpY4) +[![YOLOv8-obb detection with rotated boxes](https://img.youtube.com/vi/1e6ur57Fhzs/0.jpg)](https://youtu.be/1e6ur57Fhzs) +[![Very fast and small objects tracking](https://img.youtube.com/vi/PalIIAfgX88/0.jpg)](https://youtu.be/PalIIAfgX88) + +## Documentation + +### Core Components + +#### 1. Object Detectors +Available through `CreateDetector` function with different `detectorType`: +1. **Background Subtraction**: + - Built-in: VIBE (`tracking::Motion_VIBE`), SuBSENSE (`tracking::Motion_SuBSENSE`), LOBSTER (`tracking::Motion_LOBSTER`) + - OpenCV: MOG2 (`tracking::Motion_MOG2`) + - OpenCV Contrib: MOG (`tracking::Motion_MOG`), GMG (`tracking::Motion_GMG`), CNT (`tracking::Motion_CNT`) + - Foreground segmentation uses OpenCV contours producing `cv::RotatedRect` +2. **Deep Learning Models**: + - OpenCV DNN module (`tracking::DNN_OCV`) + - TensorRT-accelerated YOLO (`tracking::Yolo_TensorRT`) + +#### 2. Matching Algorithms +For solving assignment problems: +- **Hungarian Algorithm** (`tracking::MatchHungrian`) - O(N³) complexity +- **LAPJV** (`tracking::MatchBipart`) - O(M*N²) complexity +- **Distance Metrics**: + - Center distance (`tracking::DistCenters`) + - Bounding box distance (`tracking::DistRects`) + - Jaccard/IoU similarity (`tracking::DistJaccard`) + +#### 3. Trajectory Smoothing +- Kalman filters: Linear (`tracking::KalmanLinear`) and Unscented (`tracking::KalmanUnscented`) +- State models: Constant velocity and constant acceleration +- Tracking modes: Position-only (`tracking::FilterCenter`) and position+size (`tracking::FilterRect`) +- Specialized features: Abandoned object detection, line intersection counting + +#### 4. Visual Search +When targets disappear: +- KCF (`tracking::TrackKCF`) +- CSRT (`tracking::TrackCSRT`) +- DaSiamRPN (`tracking::TrackDaSiamRPN`) +- Vit (`tracking::TrackVit`) +- Nano (`tracking::TrackNano`) + +### Processing Pipelines +1. **Synchronous** (`SyncProcess`): Single-threaded processing +2. **Asynchronous (2 threads)** (`AsyncProcess`): Decouples detection and tracking +3. **Fully Asynchronous (4 threads)**: For low-FPS deep learning detectors + +### Installation & Building +```bash +git clone https://github.com/Smorodov/Multitarget-tracker.git +cd Multitarget-tracker +mkdir build && cd build +cmake . .. \ + -DUSE_OCV_BGFG=ON \ + -DUSE_OCV_KCF=ON \ + -DUSE_OCV_UKF=ON \ + -DBUILD_ONNX_TENSORRT=ON \ + -DBUILD_ASYNC_DETECTOR=ON \ + -DBUILD_CARS_COUNTING=ON +make -j +``` + +### Usage Guide +Basic command syntax: +```bash +./MultitargetTracker [--example=] [--start_frame=] + [--end_frame=] [--end_delay=] [--out=] + [--show_logs] [--gpu] [--async] [--res=] + [--settings=] [--batch_size=] +``` + +Example: +```bash +./MultitargetTracker ../data/atrium.avi -e=1 -o=../data/atrium_motion.avi +``` + +Keyboard Controls: +- `m`: Toggle play/pause +- Any key: Step forward when paused +- `Esc`: Exit + +### Integration as Library +```cpp +#include + +std::unique_ptr m_tracker; +TrackerSettings settings; +settings.SetDistance(tracking::DistJaccard); +m_tracker = BaseTracker::CreateTracker(settings); +``` + +### Third-party Dependencies + +- [OpenCV (and contrib)](https://github.com/opencv/opencv) +- [Vibe](https://github.com/BelBES/VIBE) +- [Non Maximum Suppression](https://github.com/Nuzhny007/Non-Maximum-Suppression) +- [Ini file parser](https://github.com/benhoyt/inih) +- [Circular Code](https://github.com/LiorKogan/Circular) + +### License +[Apache 2.0 License](https://github.com/Smorodov/Multitarget-tracker/blob/master/LICENSE) + +#### Project citations +1. Jeroen PROVOOST "Camera gebaseerde analysevan de verkeersstromen aaneen kruispunt", 2014 ( https://iiw.kuleuven.be/onderzoek/eavise/mastertheses/provoost.pdf ) +2. Roberto Ciano, Dimitrij Klesev "Autonome Roboterschwarme in geschlossenen Raumen", 2015 ( https://www.hs-furtwangen.de/fileadmin/user_upload/fak_IN/Dokumente/Forschung_InformatikJournal/informatikJournal_2016.pdf#page=18 ) +3. Wenda Qin, Tian Zhang, Junhe Chen "Traffic Monitoring By Video: Vehicles Tracking and Vehicle Data Analysing", 2016 ( http://cs-people.bu.edu/wdqin/FinalProject/CS585%20FinalProjectReport.html ) +4. Ipek BARIS "CLASSIFICATION AND TRACKING OF VEHICLES WITH HYBRID CAMERA SYSTEMS", 2016 ( http://cvrg.iyte.edu.tr/publications/IpekBaris_MScThesis.pdf ) +5. Cheng-Ta Lee, Albert Y. Chen, Cheng-Yi Chang "In-building Coverage of Automated External Defibrillators Considering Pedestrian Flow", 2016 ( http://www.see.eng.osaka-u.ac.jp/seeit/icccbe2016/Proceedings/Full_Papers/092-132.pdf ) +6. Roberto Ciano, Dimitrij Klesev "Autonome Roboterschwarme in geschlossenen Raumen" in "informatikJournal 2016/17", 2017 ( https://docplayer.org/124538994-2016-17-informatikjournal-2016-17-aktuelle-berichte-aus-forschung-und-lehre-der-fakultaet-informatik.html ) +7. Omid Noorshams "Automated systems to assess weights and activity in grouphoused mice", 2017 ( https://pdfs.semanticscholar.org/e5ff/f04b4200c149fb39d56f171ba7056ab798d3.pdf ) +8. RADEK VOPÁLENSKÝ "DETECTION,TRACKING AND CLASSIFICATION OF VEHICLES", 2018 ( https://www.vutbr.cz/www_base/zav_prace_soubor_verejne.php?file_id=181063 ) +9. Márk Rátosi, Gyula Simon "Real-Time Localization and Tracking using Visible Light Communication", 2018 ( https://ieeexplore.ieee.org/abstract/document/8533800 ) +10. Thi Nha Ngo, Kung-Chin Wu, En-Cheng Yang, Ta-Te Lin "A real-time imaging system for multiple honey bee tracking and activity monitoring", 2019 ( https://www.sciencedirect.com/science/article/pii/S0168169919301498 ) +11. Tiago Miguel, Rodrigues de Almeida "Multi-Camera and Multi-Algorithm Architecture for VisualPerception onboard the ATLASCAR2", 2019 ( http://lars.mec.ua.pt/public/LAR%20Projects/Vision/2019_TiagoAlmeida/Thesis_Tiago_AlmeidaVF_26Jul2019.pdf ) +12. ROS, http://docs.ros.org/lunar/api/costmap_converter/html/Ctracker_8cpp_source.html +13. Sangeeth Kochanthara, Yanja Dajsuren, Loek Cleophas, Mark van den Brand "Painting the Landscape of Automotive Software in GitHub", 2022 ( https://arxiv.org/abs/2203.08936 ) +14. Fesus, A., Kovari, B., Becsi, T., Leginusz, L. "Dynamic Prompt-Based Approach for Open Vocabulary Multi-Object Tracking", 2025 ( https://link.springer.com/chapter/10.1007/978-3-031-81799-1_25 ) diff --git a/TODO b/TODO deleted file mode 100644 index dcf0331c8..000000000 --- a/TODO +++ /dev/null @@ -1,2 +0,0 @@ -+ New Multitarget tracking algorithm based on Discrete-Continuous Energy Minimization: https://bitbucket.org/amilan/dctracking -+ Deep SORT: https://github.com/nwojke/deep_sort diff --git a/Tracker/Ctracker.cpp b/Tracker/Ctracker.cpp deleted file mode 100644 index 94b1b9526..000000000 --- a/Tracker/Ctracker.cpp +++ /dev/null @@ -1,271 +0,0 @@ -#include "Ctracker.h" -#include "HungarianAlg.h" - -#include -#include "mygraph.h" -#include "mwbmatching.h" -#include "tokenise.h" - -// --------------------------------------------------------------------------- -// Tracker. Manage tracks. Create, remove, update. -// --------------------------------------------------------------------------- -CTracker::CTracker( - bool useLocalTracking, - tracking::DistType distType, - tracking::KalmanType kalmanType, - tracking::FilterGoal filterGoal, - tracking::LostTrackType lostTrackType, - tracking::MatchType matchType, - track_t dt_, - track_t accelNoiseMag_, - track_t dist_thres_, - size_t maximum_allowed_skipped_frames_, - size_t max_trace_length_ - ) - : - m_useLocalTracking(useLocalTracking), - m_distType(distType), - m_kalmanType(kalmanType), - m_filterGoal(filterGoal), - m_lostTrackType(lostTrackType), - m_matchType(matchType), - dt(dt_), - accelNoiseMag(accelNoiseMag_), - dist_thres(dist_thres_), - maximum_allowed_skipped_frames(maximum_allowed_skipped_frames_), - max_trace_length(max_trace_length_), - NextTrackID(0) -{ -} - -// --------------------------------------------------------------------------- -// -// --------------------------------------------------------------------------- -CTracker::~CTracker(void) -{ -} - -// --------------------------------------------------------------------------- -// -// --------------------------------------------------------------------------- -void CTracker::Update( - const regions_t& regions, - cv::UMat grayFrame - ) -{ - if (m_prevFrame.size() == grayFrame.size()) - { - if (m_useLocalTracking) - { - m_localTracker.Update(tracks, m_prevFrame, grayFrame); - } - } - - // ----------------------------------- - // If there is no tracks yet, then every cv::Point begins its own track. - // ----------------------------------- - if (tracks.size() == 0) - { - // If no tracks yet - for (size_t i = 0; i < regions.size(); ++i) - { - tracks.push_back(std::make_unique(regions[i], - m_kalmanType, - dt, - accelNoiseMag, - NextTrackID++, - m_filterGoal == tracking::FilterRect, - m_lostTrackType)); - } - } - - size_t N = tracks.size(); // треки - size_t M = regions.size(); // детекты - - assignments_t assignment(N, -1); // назначения - - if (!tracks.empty()) - { - // Матрица расстояний от N-ного трека до M-ного детекта. - distMatrix_t Cost(N * M); - - // ----------------------------------- - // Треки уже есть, составим матрицу расстояний - // ----------------------------------- - const track_t maxPossibleCost = grayFrame.cols * grayFrame.rows; - track_t maxCost = 0; - switch (m_distType) - { - case tracking::DistCenters: - for (size_t i = 0; i < tracks.size(); i++) - { - for (size_t j = 0; j < regions.size(); j++) - { - auto dist = tracks[i]->CheckType(regions[j].m_type) ? tracks[i]->CalcDist((regions[j].m_rect.tl() + regions[j].m_rect.br()) / 2) : maxPossibleCost; - Cost[i + j * N] = dist; - if (dist > maxCost) - { - maxCost = dist; - } - } - } - break; - - case tracking::DistRects: - for (size_t i = 0; i < tracks.size(); i++) - { - for (size_t j = 0; j < regions.size(); j++) - { - auto dist = tracks[i]->CheckType(regions[j].m_type) ? tracks[i]->CalcDist(regions[j].m_rect) : maxPossibleCost; - Cost[i + j * N] = dist; - if (dist > maxCost) - { - maxCost = dist; - } - } - } - break; - - case tracking::DistJaccard: - for (size_t i = 0; i < tracks.size(); i++) - { - for (size_t j = 0; j < regions.size(); j++) - { - auto dist = tracks[i]->CheckType(regions[j].m_type) ? tracks[i]->CalcDistJaccard(regions[j].m_rect) : 1; - Cost[i + j * N] = dist; - if (dist > maxCost) - { - maxCost = dist; - } - } - } - break; - } - // ----------------------------------- - // Solving assignment problem (tracks and predictions of Kalman filter) - // ----------------------------------- - if (m_matchType == tracking::MatchHungrian) - { - AssignmentProblemSolver APS; - APS.Solve(Cost, N, M, assignment, AssignmentProblemSolver::optimal); - } - else - { - MyGraph G; - G.make_directed(); - - std::vector nodes(N + M); - - for (size_t i = 0; i < nodes.size(); ++i) - { - nodes[i] = G.new_node(); - } - - edge_map weights(G, 100); - for (size_t i = 0; i < tracks.size(); i++) - { - bool hasZeroEdge = false; - - for (size_t j = 0; j < regions.size(); j++) - { - track_t currCost = Cost[i + j * N]; - - edge e = G.new_edge(nodes[i], nodes[N + j]); - - if (currCost < dist_thres) - { - int weight = maxCost - currCost + 1; - G.set_edge_weight(e, weight); - weights[e] = weight; - } - else - { - if (!hasZeroEdge) - { - G.set_edge_weight(e, 0); - weights[e] = 0; - } - hasZeroEdge = true; - } - } - } - - edges_t L = MAX_WEIGHT_BIPARTITE_MATCHING(G, weights); - for (edges_t::iterator it = L.begin(); it != L.end(); ++it) - { - node a = it->source(); - node b = it->target(); - assignment[b.id()] = a.id() - N; - } - } - - // ----------------------------------- - // clean assignment from pairs with large distance - // ----------------------------------- - for (size_t i = 0; i < assignment.size(); i++) - { - if (assignment[i] != -1) - { - if (Cost[i + assignment[i] * N] > dist_thres) - { - assignment[i] = -1; - tracks[i]->m_skippedFrames++; - } - } - else - { - // If track have no assigned detect, then increment skipped frames counter. - tracks[i]->m_skippedFrames++; - } - } - - // ----------------------------------- - // If track didn't get detects long time, remove it. - // ----------------------------------- - for (int i = 0; i < static_cast(tracks.size()); i++) - { - if (tracks[i]->m_skippedFrames > maximum_allowed_skipped_frames) - { - tracks.erase(tracks.begin() + i); - assignment.erase(assignment.begin() + i); - i--; - } - } - } - - // ----------------------------------- - // Search for unassigned detects and start new tracks for them. - // ----------------------------------- - for (size_t i = 0; i < regions.size(); ++i) - { - if (find(assignment.begin(), assignment.end(), i) == assignment.end()) - { - tracks.push_back(std::make_unique(regions[i], - m_kalmanType, - dt, - accelNoiseMag, - NextTrackID++, - m_filterGoal == tracking::FilterRect, - m_lostTrackType)); - } - } - - // Update Kalman Filters state - - for (size_t i = 0; i < assignment.size(); i++) - { - // If track updated less than one time, than filter state is not correct. - - if (assignment[i] != -1) // If we have assigned detect, then update using its coordinates, - { - tracks[i]->m_skippedFrames = 0; - tracks[i]->Update(regions[assignment[i]], true, max_trace_length, m_prevFrame, grayFrame); - } - else // if not continue using predictions - { - tracks[i]->Update(CRegion(), false, max_trace_length, m_prevFrame, grayFrame); - } - } - - grayFrame.copyTo(m_prevFrame); -} diff --git a/Tracker/Ctracker.h b/Tracker/Ctracker.h deleted file mode 100644 index c60fa1471..000000000 --- a/Tracker/Ctracker.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once -#include -#include -#include -#include - -#include "defines.h" -#include "track.h" -#include "LocalTracker.h" - -// ---------------------------------------------------------------------- -class CTracker -{ -public: - CTracker(bool useLocalTracking, - tracking::DistType distType, - tracking::KalmanType kalmanType, - tracking::FilterGoal filterGoal, - tracking::LostTrackType lostTrackType, - tracking::MatchType matchType, - track_t dt_, - track_t accelNoiseMag_, - track_t dist_thres_ = 60, - size_t maximum_allowed_skipped_frames_ = 10, - size_t max_trace_length_ = 10); - ~CTracker(void); - - tracks_t tracks; - void Update(const regions_t& regions, cv::UMat grayFrame); - - bool GrayFrameToTrack() const - { - return m_lostTrackType != tracking::LostTrackType::TrackGOTURN; - } - -private: - // Use local tracking for regions between two frames - bool m_useLocalTracking; - - tracking::DistType m_distType; - tracking::KalmanType m_kalmanType; - tracking::FilterGoal m_filterGoal; - tracking::LostTrackType m_lostTrackType; - tracking::MatchType m_matchType; - - // Шаг времени опроса фильтра - track_t dt; - - track_t accelNoiseMag; - - // Порог расстояния. Если точки находятся дуг от друга на расстоянии, - // превышающем этот порог, то эта пара не рассматривается в задаче о назначениях. - track_t dist_thres; - // Максимальное количество кадров которое трек сохраняется не получая данных о измерений. - size_t maximum_allowed_skipped_frames; - // Максимальная длина следа - size_t max_trace_length; - - size_t NextTrackID; - - LocalTracker m_localTracker; - - cv::UMat m_prevFrame; -}; diff --git a/Tracker/HungarianAlg/HungarianAlg.cpp b/Tracker/HungarianAlg/HungarianAlg.cpp deleted file mode 100644 index ab9a17c7f..000000000 --- a/Tracker/HungarianAlg/HungarianAlg.cpp +++ /dev/null @@ -1,724 +0,0 @@ -#include "HungarianAlg.h" -#include - -// -------------------------------------------------------------------------- -// -// -------------------------------------------------------------------------- -AssignmentProblemSolver::AssignmentProblemSolver() -{ -} - -// -------------------------------------------------------------------------- -// -// -------------------------------------------------------------------------- -AssignmentProblemSolver::~AssignmentProblemSolver() -{ -} - -// -------------------------------------------------------------------------- -// -// -------------------------------------------------------------------------- -track_t AssignmentProblemSolver::Solve( - const distMatrix_t& distMatrixIn, - size_t nOfRows, - size_t nOfColumns, - std::vector& assignment, - TMethod Method - ) -{ - assignment.resize(nOfRows, -1); - - track_t cost = 0; - - switch (Method) - { - case optimal: - assignmentoptimal(assignment, cost, distMatrixIn, nOfRows, nOfColumns); - break; - - case many_forbidden_assignments: - assignmentsuboptimal1(assignment, cost, distMatrixIn, nOfRows, nOfColumns); - break; - - case without_forbidden_assignments: - assignmentsuboptimal2(assignment, cost, distMatrixIn, nOfRows, nOfColumns); - break; - } - - return cost; -} -// -------------------------------------------------------------------------- -// Computes the optimal assignment (minimum overall costs) using Munkres algorithm. -// -------------------------------------------------------------------------- -void AssignmentProblemSolver::assignmentoptimal(assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns) -{ - // Generate distance cv::Matrix - // and check cv::Matrix elements positiveness :) - - // Total elements number - size_t nOfElements = nOfRows * nOfColumns; - // Memory allocation - track_t* distMatrix = (track_t *)malloc(nOfElements * sizeof(track_t)); - - if (distMatrix == nullptr) - { - return; - } - - // Pointer to last element - track_t* distMatrixEnd = distMatrix + nOfElements; - - for (size_t row = 0; row < nOfElements; row++) - { - track_t value = distMatrixIn[row]; - assert(value >= 0); - distMatrix[row] = value; - } - - // Memory allocation - bool* coveredColumns = (bool*)calloc(nOfColumns, sizeof(bool)); - bool* coveredRows = (bool*)calloc(nOfRows, sizeof(bool)); - bool* starMatrix = (bool*)calloc(nOfElements, sizeof(bool)); - bool* primeMatrix = (bool*)calloc(nOfElements, sizeof(bool)); - bool* newStarMatrix = (bool*)calloc(nOfElements, sizeof(bool)); /* used in step4 */ - - /* preliminary steps */ - if (nOfRows <= nOfColumns) - { - for (size_t row = 0; row < nOfRows; row++) - { - /* find the smallest element in the row */ - track_t* distMatrixTemp = distMatrix + row; - track_t minValue = *distMatrixTemp; - distMatrixTemp += nOfRows; - while (distMatrixTemp < distMatrixEnd) - { - track_t value = *distMatrixTemp; - if (value < minValue) - { - minValue = value; - } - distMatrixTemp += nOfRows; - } - /* subtract the smallest element from each element of the row */ - distMatrixTemp = distMatrix + row; - while (distMatrixTemp < distMatrixEnd) - { - *distMatrixTemp -= minValue; - distMatrixTemp += nOfRows; - } - } - /* Steps 1 and 2a */ - for (size_t row = 0; row < nOfRows; row++) - { - for (size_t col = 0; col < nOfColumns; col++) - { - if (distMatrix[row + nOfRows*col] == 0) - { - if (!coveredColumns[col]) - { - starMatrix[row + nOfRows * col] = true; - coveredColumns[col] = true; - break; - } - } - } - } - } - else /* if(nOfRows > nOfColumns) */ - { - for (size_t col = 0; col < nOfColumns; col++) - { - /* find the smallest element in the column */ - track_t* distMatrixTemp = distMatrix + nOfRows*col; - track_t* columnEnd = distMatrixTemp + nOfRows; - track_t minValue = *distMatrixTemp++; - while (distMatrixTemp < columnEnd) - { - track_t value = *distMatrixTemp++; - if (value < minValue) - { - minValue = value; - } - } - /* subtract the smallest element from each element of the column */ - distMatrixTemp = distMatrix + nOfRows*col; - while (distMatrixTemp < columnEnd) - { - *distMatrixTemp++ -= minValue; - } - } - /* Steps 1 and 2a */ - for (size_t col = 0; col < nOfColumns; col++) - { - for (size_t row = 0; row < nOfRows; row++) - { - if (distMatrix[row + nOfRows*col] == 0) - { - if (!coveredRows[row]) - { - starMatrix[row + nOfRows*col] = true; - coveredColumns[col] = true; - coveredRows[row] = true; - break; - } - } - } - } - - for (size_t row = 0; row < nOfRows; row++) - { - coveredRows[row] = false; - } - } - /* move to step 2b */ - step2b(assignment, distMatrix, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, (nOfRows <= nOfColumns) ? nOfRows : nOfColumns); - /* compute cost and remove invalid assignments */ - computeassignmentcost(assignment, cost, distMatrixIn, nOfRows); - /* free allocated memory */ - free(distMatrix); - free(coveredColumns); - free(coveredRows); - free(starMatrix); - free(primeMatrix); - free(newStarMatrix); - return; -} -// -------------------------------------------------------------------------- -// -// -------------------------------------------------------------------------- -void AssignmentProblemSolver::buildassignmentvector(assignments_t& assignment, bool *starMatrix, size_t nOfRows, size_t nOfColumns) -{ - for (size_t row = 0; row < nOfRows; row++) - { - for (size_t col = 0; col < nOfColumns; col++) - { - if (starMatrix[row + nOfRows * col]) - { - assignment[row] = static_cast(col); - break; - } - } - } -} -// -------------------------------------------------------------------------- -// -// -------------------------------------------------------------------------- -void AssignmentProblemSolver::computeassignmentcost(const assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows) -{ - for (size_t row = 0; row < nOfRows; row++) - { - const int col = assignment[row]; - if (col >= 0) - { - cost += distMatrixIn[row + nOfRows * col]; - } - } -} - -// -------------------------------------------------------------------------- -// -// -------------------------------------------------------------------------- -void AssignmentProblemSolver::step2a(assignments_t& assignment, track_t *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim) -{ - bool *starMatrixTemp, *columnEnd; - /* cover every column containing a starred zero */ - for (size_t col = 0; col < nOfColumns; col++) - { - starMatrixTemp = starMatrix + nOfRows * col; - columnEnd = starMatrixTemp + nOfRows; - while (starMatrixTemp < columnEnd) - { - if (*starMatrixTemp++) - { - coveredColumns[col] = true; - break; - } - } - } - /* move to step 3 */ - step2b(assignment, distMatrix, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim); -} - -// -------------------------------------------------------------------------- -// -// -------------------------------------------------------------------------- -void AssignmentProblemSolver::step2b(assignments_t& assignment, track_t *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim) -{ - /* count covered columns */ - size_t nOfCoveredColumns = 0; - for (size_t col = 0; col < nOfColumns; col++) - { - if (coveredColumns[col]) - { - nOfCoveredColumns++; - } - } - if (nOfCoveredColumns == minDim) - { - /* algorithm finished */ - buildassignmentvector(assignment, starMatrix, nOfRows, nOfColumns); - } - else - { - /* move to step 3 */ - step3_5(assignment, distMatrix, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim); - } -} - -// -------------------------------------------------------------------------- -// -// -------------------------------------------------------------------------- -void AssignmentProblemSolver::step3_5(assignments_t& assignment, track_t *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim) -{ - for (;;) - { - /* step 3 */ - bool zerosFound = true; - while (zerosFound) - { - zerosFound = false; - for (size_t col = 0; col < nOfColumns; col++) - { - if (!coveredColumns[col]) - { - for (size_t row = 0; row < nOfRows; row++) - { - if ((!coveredRows[row]) && (distMatrix[row + nOfRows*col] == 0)) - { - /* prime zero */ - primeMatrix[row + nOfRows*col] = true; - /* find starred zero in current row */ - size_t starCol = 0; - for (; starCol < nOfColumns; starCol++) - { - if (starMatrix[row + nOfRows * starCol]) - { - break; - } - } - if (starCol == nOfColumns) /* no starred zero found */ - { - /* move to step 4 */ - step4(assignment, distMatrix, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim, row, col); - return; - } - else - { - coveredRows[row] = true; - coveredColumns[starCol] = false; - zerosFound = true; - break; - } - } - } - } - } - } - /* step 5 */ - track_t h = std::numeric_limits::max(); - for (size_t row = 0; row < nOfRows; row++) - { - if (!coveredRows[row]) - { - for (size_t col = 0; col < nOfColumns; col++) - { - if (!coveredColumns[col]) - { - const track_t value = distMatrix[row + nOfRows*col]; - if (value < h) - { - h = value; - } - } - } - } - } - /* add h to each covered row */ - for (size_t row = 0; row < nOfRows; row++) - { - if (coveredRows[row]) - { - for (size_t col = 0; col < nOfColumns; col++) - { - distMatrix[row + nOfRows*col] += h; - } - } - } - /* subtract h from each uncovered column */ - for (size_t col = 0; col < nOfColumns; col++) - { - if (!coveredColumns[col]) - { - for (size_t row = 0; row < nOfRows; row++) - { - distMatrix[row + nOfRows*col] -= h; - } - } - } - } -} - -// -------------------------------------------------------------------------- -// -// -------------------------------------------------------------------------- -void AssignmentProblemSolver::step4(assignments_t& assignment, track_t *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim, size_t row, size_t col) -{ - const size_t nOfElements = nOfRows * nOfColumns; - /* generate temporary copy of starMatrix */ - for (size_t n = 0; n < nOfElements; n++) - { - newStarMatrix[n] = starMatrix[n]; - } - /* star current zero */ - newStarMatrix[row + nOfRows*col] = true; - /* find starred zero in current column */ - size_t starCol = col; - size_t starRow = 0; - for (; starRow < nOfRows; starRow++) - { - if (starMatrix[starRow + nOfRows * starCol]) - { - break; - } - } - while (starRow < nOfRows) - { - /* unstar the starred zero */ - newStarMatrix[starRow + nOfRows*starCol] = false; - /* find primed zero in current row */ - size_t primeRow = starRow; - size_t primeCol = 0; - for (; primeCol < nOfColumns; primeCol++) - { - if (primeMatrix[primeRow + nOfRows * primeCol]) - { - break; - } - } - /* star the primed zero */ - newStarMatrix[primeRow + nOfRows*primeCol] = true; - /* find starred zero in current column */ - starCol = primeCol; - for (starRow = 0; starRow < nOfRows; starRow++) - { - if (starMatrix[starRow + nOfRows * starCol]) - { - break; - } - } - } - /* use temporary copy as new starMatrix */ - /* delete all primes, uncover all rows */ - for (size_t n = 0; n < nOfElements; n++) - { - primeMatrix[n] = false; - starMatrix[n] = newStarMatrix[n]; - } - for (size_t n = 0; n < nOfRows; n++) - { - coveredRows[n] = false; - } - /* move to step 2a */ - step2a(assignment, distMatrix, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim); -} - -// -------------------------------------------------------------------------- -// Computes a suboptimal solution. Good for cases without forbidden assignments. -// -------------------------------------------------------------------------- -void AssignmentProblemSolver::assignmentsuboptimal2(assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns) -{ - /* make working copy of distance Matrix */ - const size_t nOfElements = nOfRows * nOfColumns; - track_t* distMatrix = (track_t*)malloc(nOfElements * sizeof(track_t)); - for (size_t n = 0; n < nOfElements; n++) - { - distMatrix[n] = distMatrixIn[n]; - } - - /* recursively search for the minimum element and do the assignment */ - for (;;) - { - /* find minimum distance observation-to-track pair */ - track_t minValue = std::numeric_limits::max(); - size_t tmpRow = 0; - size_t tmpCol = 0; - for (size_t row = 0; row < nOfRows; row++) - { - for (size_t col = 0; col < nOfColumns; col++) - { - const track_t value = distMatrix[row + nOfRows*col]; - if (value != std::numeric_limits::max() && (value < minValue)) - { - minValue = value; - tmpRow = row; - tmpCol = col; - } - } - } - - if (minValue != std::numeric_limits::max()) - { - assignment[tmpRow] = static_cast(tmpCol); - cost += minValue; - for (size_t n = 0; n < nOfRows; n++) - { - distMatrix[n + nOfRows*tmpCol] = std::numeric_limits::max(); - } - for (size_t n = 0; n < nOfColumns; n++) - { - distMatrix[tmpRow + nOfRows*n] = std::numeric_limits::max(); - } - } - else - { - break; - } - } - - free(distMatrix); -} -// -------------------------------------------------------------------------- -// Computes a suboptimal solution. Good for cases with many forbidden assignments. -// -------------------------------------------------------------------------- -void AssignmentProblemSolver::assignmentsuboptimal1(assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns) -{ - /* make working copy of distance Matrix */ - const size_t nOfElements = nOfRows * nOfColumns; - track_t* distMatrix = (track_t *)malloc(nOfElements * sizeof(track_t)); - for (size_t n = 0; n < nOfElements; n++) - { - distMatrix[n] = distMatrixIn[n]; - } - - /* allocate memory */ - int* nOfValidObservations = (int *)calloc(nOfRows, sizeof(int)); - int* nOfValidTracks = (int *)calloc(nOfColumns, sizeof(int)); - - /* compute number of validations */ - bool infiniteValueFound = false; - bool finiteValueFound = false; - for (size_t row = 0; row < nOfRows; row++) - { - for (size_t col = 0; col < nOfColumns; col++) - { - if (distMatrix[row + nOfRows*col] != std::numeric_limits::max()) - { - nOfValidTracks[col] += 1; - nOfValidObservations[row] += 1; - finiteValueFound = true; - } - else - { - infiniteValueFound = true; - } - } - } - - if (infiniteValueFound) - { - if (!finiteValueFound) - { - /* free allocated memory */ - free(nOfValidObservations); - free(nOfValidTracks); - free(distMatrix); - - return; - } - bool repeatSteps = true; - - while (repeatSteps) - { - repeatSteps = false; - - /* step 1: reject assignments of multiply validated tracks to singly validated observations */ - for (size_t col = 0; col < nOfColumns; col++) - { - bool singleValidationFound = false; - for (size_t row = 0; row < nOfRows; row++) - { - if (distMatrix[row + nOfRows * col] != std::numeric_limits::max() && (nOfValidObservations[row] == 1)) - { - singleValidationFound = true; - break; - } - - if (singleValidationFound) - { - for (size_t row = 0; row < nOfRows; row++) - if ((nOfValidObservations[row] > 1) && distMatrix[row + nOfRows*col] != std::numeric_limits::max()) - { - distMatrix[row + nOfRows*col] = std::numeric_limits::max(); - nOfValidObservations[row] -= 1; - nOfValidTracks[col] -= 1; - repeatSteps = true; - } - } - } - } - - /* step 2: reject assignments of multiply validated observations to singly validated tracks */ - if (nOfColumns > 1) - { - for (size_t row = 0; row < nOfRows; row++) - { - bool singleValidationFound = false; - for (size_t col = 0; col < nOfColumns; col++) - { - if (distMatrix[row + nOfRows*col] != std::numeric_limits::max() && (nOfValidTracks[col] == 1)) - { - singleValidationFound = true; - break; - } - } - - if (singleValidationFound) - { - for (size_t col = 0; col < nOfColumns; col++) - { - if ((nOfValidTracks[col] > 1) && distMatrix[row + nOfRows*col] != std::numeric_limits::max()) - { - distMatrix[row + nOfRows*col] = std::numeric_limits::max(); - nOfValidObservations[row] -= 1; - nOfValidTracks[col] -= 1; - repeatSteps = true; - } - } - } - } - } - } /* while(repeatSteps) */ - - /* for each multiply validated track that validates only with singly validated */ - /* observations, choose the observation with minimum distance */ - for (size_t row = 0; row < nOfRows; row++) - { - if (nOfValidObservations[row] > 1) - { - bool allSinglyValidated = true; - track_t minValue = std::numeric_limits::max(); - size_t tmpCol = 0; - for (size_t col = 0; col < nOfColumns; col++) - { - const track_t value = distMatrix[row + nOfRows*col]; - if (value != std::numeric_limits::max()) - { - if (nOfValidTracks[col] > 1) - { - allSinglyValidated = false; - break; - } - else if ((nOfValidTracks[col] == 1) && (value < minValue)) - { - tmpCol = col; - minValue = value; - } - } - } - - if (allSinglyValidated) - { - assignment[row] = static_cast(tmpCol); - cost += minValue; - for (size_t n = 0; n < nOfRows; n++) - { - distMatrix[n + nOfRows*tmpCol] = std::numeric_limits::max(); - } - for (size_t n = 0; n < nOfColumns; n++) - { - distMatrix[row + nOfRows*n] = std::numeric_limits::max(); - } - } - } - } - - // for each multiply validated observation that validates only with singly validated track, choose the track with minimum distance - for (size_t col = 0; col < nOfColumns; col++) - { - if (nOfValidTracks[col] > 1) - { - bool allSinglyValidated = true; - track_t minValue = std::numeric_limits::max(); - size_t tmpRow = 0; - for (size_t row = 0; row < nOfRows; row++) - { - const track_t value = distMatrix[row + nOfRows*col]; - if (value != std::numeric_limits::max()) - { - if (nOfValidObservations[row] > 1) - { - allSinglyValidated = false; - break; - } - else if ((nOfValidObservations[row] == 1) && (value < minValue)) - { - tmpRow = row; - minValue = value; - } - } - } - - if (allSinglyValidated) - { - assignment[tmpRow] = static_cast(col); - cost += minValue; - for (size_t n = 0; n < nOfRows; n++) - { - distMatrix[n + nOfRows*col] = std::numeric_limits::max(); - } - for (size_t n = 0; n < nOfColumns; n++) - { - distMatrix[tmpRow + nOfRows*n] = std::numeric_limits::max(); - } - } - } - } - } /* if(infiniteValueFound) */ - - - /* now, recursively search for the minimum element and do the assignment */ - for (;;) - { - /* find minimum distance observation-to-track pair */ - track_t minValue = std::numeric_limits::max(); - size_t tmpRow = 0; - size_t tmpCol = 0; - for (size_t row = 0; row < nOfRows; row++) - { - for (size_t col = 0; col < nOfColumns; col++) - { - const track_t value = distMatrix[row + nOfRows*col]; - if (value != std::numeric_limits::max() && (value < minValue)) - { - minValue = value; - tmpRow = row; - tmpCol = col; - } - } - } - - if (minValue != std::numeric_limits::max()) - { - assignment[tmpRow] = static_cast(tmpCol); - cost += minValue; - for (size_t n = 0; n < nOfRows; n++) - { - distMatrix[n + nOfRows*tmpCol] = std::numeric_limits::max(); - } - for (size_t n = 0; n < nOfColumns; n++) - { - distMatrix[tmpRow + nOfRows*n] = std::numeric_limits::max(); - } - } - else - { - break; - } - } - - /* free allocated memory */ - free(nOfValidObservations); - free(nOfValidTracks); - free(distMatrix); -} diff --git a/Tracker/HungarianAlg/HungarianAlg.h b/Tracker/HungarianAlg/HungarianAlg.h deleted file mode 100644 index 7b1cb36ff..000000000 --- a/Tracker/HungarianAlg/HungarianAlg.h +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include -#include -#include -#include "defines.h" -// http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=hungarianAlgorithm - -typedef std::vector assignments_t; -typedef std::vector distMatrix_t; - -class AssignmentProblemSolver -{ -private: - // -------------------------------------------------------------------------- - // Computes the optimal assignment (minimum overall costs) using Munkres algorithm. - // -------------------------------------------------------------------------- - void assignmentoptimal(assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns); - void buildassignmentvector(assignments_t& assignment, bool *starMatrix, size_t nOfRows, size_t nOfColumns); - void computeassignmentcost(const assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows); - void step2a(assignments_t& assignment, track_t *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim); - void step2b(assignments_t& assignment, track_t *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim); - void step3_5(assignments_t& assignment, track_t *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim); - void step4(assignments_t& assignment, track_t *distMatrix, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim, size_t row, size_t col); - // -------------------------------------------------------------------------- - // Computes a suboptimal solution. Good for cases with many forbidden assignments. - // -------------------------------------------------------------------------- - void assignmentsuboptimal1(assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns); - // -------------------------------------------------------------------------- - // Computes a suboptimal solution. Good for cases with many forbidden assignments. - // -------------------------------------------------------------------------- - void assignmentsuboptimal2(assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns); - -public: - enum TMethod - { - optimal, - many_forbidden_assignments, - without_forbidden_assignments - }; - - AssignmentProblemSolver(); - ~AssignmentProblemSolver(); - track_t Solve(const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns, assignments_t& assignment, TMethod Method = optimal); -}; diff --git a/Tracker/Kalman.cpp b/Tracker/Kalman.cpp deleted file mode 100644 index e35636ad6..000000000 --- a/Tracker/Kalman.cpp +++ /dev/null @@ -1,736 +0,0 @@ -#include "Kalman.h" -#include -#include - -//--------------------------------------------------------------------------- -//--------------------------------------------------------------------------- -TKalmanFilter::TKalmanFilter( - tracking::KalmanType type, - Point_t pt, - track_t deltaTime, // time increment (lower values makes target more "massive") - track_t accelNoiseMag - ) - : - m_type(type), - m_initialized(false), - m_deltaTime(deltaTime), - m_accelNoiseMag(accelNoiseMag) -{ - m_initialPoints.push_back(pt); - m_lastPointResult = pt; -} - -//--------------------------------------------------------------------------- -TKalmanFilter::TKalmanFilter( - tracking::KalmanType type, - cv::Rect rect, - track_t deltaTime, // time increment (lower values makes target more "massive") - track_t accelNoiseMag - ) - : - m_type(type), - m_initialized(false), - m_deltaTime(deltaTime), - m_accelNoiseMag(accelNoiseMag) -{ - m_initialRects.push_back(rect); - m_lastRectResult = rect; -} - -//--------------------------------------------------------------------------- -TKalmanFilter::~TKalmanFilter() -{ -} - -//--------------------------------------------------------------------------- -void TKalmanFilter::CreateLinear(Point_t xy0, Point_t xyv0) -{ - // We don't know acceleration, so, assume it to process noise. - // But we can guess, the range of acceleration values thich can be achieved by tracked object. - // Process noise. (standard deviation of acceleration: m/s^2) - // shows, woh much target can accelerate. - - //4 state variables, 2 measurements - m_linearKalman = std::make_unique(4, 2, 0); - // Transition cv::Matrix - m_linearKalman->transitionMatrix = (cv::Mat_(4, 4) << - 1, 0, m_deltaTime, 0, - 0, 1, 0, m_deltaTime, - 0, 0, 1, 0, - 0, 0, 0, 1); - - // init... - m_lastPointResult = xy0; - m_linearKalman->statePre.at(0) = xy0.x; // x - m_linearKalman->statePre.at(1) = xy0.y; // y - - m_linearKalman->statePre.at(2) = xyv0.x; - m_linearKalman->statePre.at(3) = xyv0.y; - - m_linearKalman->statePost.at(0) = xy0.x; - m_linearKalman->statePost.at(1) = xy0.y; - - cv::setIdentity(m_linearKalman->measurementMatrix); - - m_linearKalman->processNoiseCov = (cv::Mat_(4, 4) << - pow(m_deltaTime,4.0)/4.0 ,0 ,pow(m_deltaTime,3.0)/2.0 ,0, - 0 ,pow(m_deltaTime,4.0)/4.0 ,0 ,pow(m_deltaTime,3.0)/2.0, - pow(m_deltaTime,3.0)/2.0 ,0 ,pow(m_deltaTime,2.0) ,0, - 0 ,pow(m_deltaTime,3.0)/2.0 ,0 ,pow(m_deltaTime,2.0)); - - - m_linearKalman->processNoiseCov *= m_accelNoiseMag; - - setIdentity(m_linearKalman->measurementNoiseCov, cv::Scalar::all(0.1)); - - setIdentity(m_linearKalman->errorCovPost, cv::Scalar::all(.1)); - - m_initialized = true; -} - -//--------------------------------------------------------------------------- -void TKalmanFilter::CreateLinear(cv::Rect_ rect0, Point_t rectv0) -{ - // We don't know acceleration, so, assume it to process noise. - // But we can guess, the range of acceleration values thich can be achieved by tracked object. - // Process noise. (standard deviation of acceleration: m/s^2) - // shows, woh much target can accelerate. - - //4 state variables (x, y, dx, dy, width, height), 4 measurements (x, y, width, height) - m_linearKalman = std::make_unique(6, 4, 0); - // Transition cv::Matrix - m_linearKalman->transitionMatrix = (cv::Mat_(6, 6) << - 1, 0, 0, 0, m_deltaTime, 0, - 0, 1, 0, 0, 0, m_deltaTime, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 1); - - // init... - m_linearKalman->statePre.at(0) = rect0.x; // x - m_linearKalman->statePre.at(1) = rect0.y; // y - m_linearKalman->statePre.at(2) = rect0.width; // width - m_linearKalman->statePre.at(3) = rect0.height; // height - m_linearKalman->statePre.at(4) = rectv0.x; // dx - m_linearKalman->statePre.at(5) = rectv0.y; // dy - - m_linearKalman->statePost.at(0) = rect0.x; - m_linearKalman->statePost.at(1) = rect0.y; - m_linearKalman->statePost.at(2) = rect0.width; - m_linearKalman->statePost.at(3) = rect0.height; - - cv::setIdentity(m_linearKalman->measurementMatrix); - - track_t n1 = pow(m_deltaTime, 4.) / 4.; - track_t n2 = pow(m_deltaTime, 3.) / 2.; - track_t n3 = pow(m_deltaTime, 2.); - m_linearKalman->processNoiseCov = (cv::Mat_(6, 6) << - n1, 0, 0, 0, n2, 0, - 0, n1, 0, 0, 0, n2, - 0, 0, n1, 0, 0, 0, - 0, 0, 0, n1, 0, 0, - n2, 0, 0, 0, n3, 0, - 0, n2, 0, 0, 0, n3); - - m_linearKalman->processNoiseCov *= m_accelNoiseMag; - - setIdentity(m_linearKalman->measurementNoiseCov, cv::Scalar::all(0.1)); - - setIdentity(m_linearKalman->errorCovPost, cv::Scalar::all(.1)); - - m_initialized = true; -} - -//--------------------------------------------------------------------------- -template inline -T sqr(T val) -{ - return val * val; -} - -//--------------------------------------------------------------------------- -template -void get_lin_regress_params( - const CONT& in_data, - size_t start_pos, - size_t in_data_size, - T& kx, T& bx, T& ky, T& by) -{ - T m1(0.), m2(0.); - T m3_x(0.), m4_x(0.); - T m3_y(0.), m4_y(0.); - - const T el_count = static_cast(in_data_size - start_pos); - for (size_t i = start_pos; i < in_data_size; ++i) - { - m1 += i; - m2 += sqr(i); - - m3_x += in_data[i].x; - m4_x += i * in_data[i].x; - - m3_y += in_data[i].y; - m4_y += i * in_data[i].y; - } - T det_1 = 1. / (el_count * m2 - sqr(m1)); - - m1 *= -1.; - - kx = det_1 * (m1 * m3_x + el_count * m4_x); - bx = det_1 * (m2 * m3_x + m1 * m4_x); - - ky = det_1 * (m1 * m3_y + el_count * m4_y); - by = det_1 * (m2 * m3_y + m1 * m4_y); -} - -#ifdef USE_OCV_UKF -//--------------------------------------------------------------------------- -class AcceleratedModel: public cv::tracking::UkfSystemModel -{ -public: - AcceleratedModel(track_t deltaTime, bool rectModel) - : - cv::tracking::UkfSystemModel(), - m_deltaTime(deltaTime), - m_rectModel(rectModel) - { - - } - - void stateConversionFunction(const cv::Mat& x_k, const cv::Mat& u_k, const cv::Mat& v_k, cv::Mat& x_kplus1) - { - track_t x0 = x_k.at(0, 0); - track_t y0 = x_k.at(1, 0); - track_t vx0 = x_k.at(2, 0); - track_t vy0 = x_k.at(3, 0); - track_t ax0 = x_k.at(4, 0); - track_t ay0 = x_k.at(5, 0); - - x_kplus1.at(0, 0) = x0 + vx0 * m_deltaTime + ax0 * sqr(m_deltaTime) / 2; - x_kplus1.at(1, 0) = y0 + vy0 * m_deltaTime + ay0 * sqr(m_deltaTime) / 2; - x_kplus1.at(2, 0) = vx0 + ax0 * m_deltaTime; - x_kplus1.at(3, 0) = vy0 + ay0 * m_deltaTime; - x_kplus1.at(4, 0) = ax0; - x_kplus1.at(5, 0) = ay0; - - if (m_rectModel) - { - x_kplus1.at(6, 0) = x_k.at(6, 0); - x_kplus1.at(7, 0) = x_k.at(7, 0); - } - - if (v_k.size() == u_k.size()) - { - x_kplus1 += v_k + u_k; - } - else - { - x_kplus1 += v_k; - } - } - - void measurementFunction(const cv::Mat& x_k, const cv::Mat& n_k, cv::Mat& z_k) - { - track_t x0 = x_k.at(0, 0); - track_t y0 = x_k.at(1, 0); - track_t vx0 = x_k.at(2, 0); - track_t vy0 = x_k.at(3, 0); - track_t ax0 = x_k.at(4, 0); - track_t ay0 = x_k.at(5, 0); - - z_k.at(0, 0) = x0 + vx0 * m_deltaTime + ax0 * sqr(m_deltaTime) / 2 + n_k.at(0, 0); - z_k.at(1, 0) = y0 + vy0 * m_deltaTime + ay0 * sqr(m_deltaTime) / 2 + n_k.at(1, 0); - - if (m_rectModel) - { - z_k.at(2, 0) = x_k.at(6, 0); - z_k.at(3, 0) = x_k.at(7, 0); - } - } - -private: - track_t m_deltaTime; - bool m_rectModel; -}; - -//--------------------------------------------------------------------------- -void TKalmanFilter::CreateUnscented(Point_t xy0, Point_t xyv0) -{ - int MP = 2; - int DP = 6; - int CP = 0; - - cv::Mat processNoiseCov = cv::Mat::zeros(DP, DP, Mat_t(1)); - processNoiseCov.at(0, 0) = 1e-14; - processNoiseCov.at(1, 1) = 1e-14; - processNoiseCov.at(2, 2) = 1e-6; - processNoiseCov.at(3, 3) = 1e-6; - processNoiseCov.at(4, 4) = 1e-6; - processNoiseCov.at(5, 5) = 1e-6; - - cv::Mat measurementNoiseCov = cv::Mat::zeros(MP, MP, Mat_t(1)); - measurementNoiseCov.at(0, 0) = 1e-6; - measurementNoiseCov.at(1, 1) = 1e-6; - - cv::Mat initState(DP, 1, Mat_t(1)); - initState.at(0, 0) = xy0.x; - initState.at(1, 0) = xy0.y; - initState.at(2, 0) = xyv0.x; - initState.at(3, 0) = xyv0.y; - initState.at(4, 0) = 0; - initState.at(5, 0) = 0; - - cv::Mat P = 1e-6 * cv::Mat::eye(DP, DP, Mat_t(1)); - - cv::Ptr model(new AcceleratedModel(m_deltaTime, false)); - cv::tracking::UnscentedKalmanFilterParams params(DP, MP, CP, 0, 0, model); - params.dataType = Mat_t(1); - params.stateInit = initState.clone(); - params.errorCovInit = P.clone(); - params.measurementNoiseCov = measurementNoiseCov.clone(); - params.processNoiseCov = processNoiseCov.clone(); - - params.alpha = 1.0; - params.beta = 2.0; - params.k = -2.0; - - m_uncsentedKalman = cv::tracking::createUnscentedKalmanFilter(params); - - m_initialized = true; -} - -//--------------------------------------------------------------------------- -void TKalmanFilter::CreateUnscented(cv::Rect_ rect0, Point_t rectv0) -{ - int MP = 4; - int DP = 8; - int CP = 0; - - cv::Mat processNoiseCov = cv::Mat::zeros(DP, DP, Mat_t(1)); - processNoiseCov.at(0, 0) = 1e-3; - processNoiseCov.at(1, 1) = 1e-3; - processNoiseCov.at(2, 2) = 1e-3; - processNoiseCov.at(3, 3) = 1e-3; - processNoiseCov.at(4, 4) = 1e-3; - processNoiseCov.at(5, 5) = 1e-3; - processNoiseCov.at(6, 6) = 1e-3; - processNoiseCov.at(7, 7) = 1e-3; - - cv::Mat measurementNoiseCov = cv::Mat::zeros(MP, MP, Mat_t(1)); - measurementNoiseCov.at(0, 0) = 1e-3; - measurementNoiseCov.at(1, 1) = 1e-3; - measurementNoiseCov.at(2, 2) = 1e-3; - measurementNoiseCov.at(3, 3) = 1e-3; - - cv::Mat initState(DP, 1, Mat_t(1)); - initState.at(0, 0) = rect0.x; - initState.at(1, 0) = rect0.y; - initState.at(2, 0) = rectv0.x; - initState.at(3, 0) = rectv0.y; - initState.at(4, 0) = 0; - initState.at(5, 0) = 0; - initState.at(6, 0) = rect0.width; - initState.at(7, 0) = rect0.height; - - cv::Mat P = 1e-3 * cv::Mat::eye(DP, DP, Mat_t(1)); - - cv::Ptr model(new AcceleratedModel(m_deltaTime, true)); - cv::tracking::UnscentedKalmanFilterParams params(DP, MP, CP, 0, 0, model); - params.dataType = Mat_t(1); - params.stateInit = initState.clone(); - params.errorCovInit = P.clone(); - params.measurementNoiseCov = measurementNoiseCov.clone(); - params.processNoiseCov = processNoiseCov.clone(); - - params.alpha = 1; - params.beta = 2.0; - params.k = -2.0; - - m_uncsentedKalman = cv::tracking::createUnscentedKalmanFilter(params); - - m_initialized = true; -} - -//--------------------------------------------------------------------------- -void TKalmanFilter::CreateAugmentedUnscented(Point_t xy0, Point_t xyv0) -{ - int MP = 2; - int DP = 6; - int CP = 0; - - cv::Mat processNoiseCov = cv::Mat::zeros(DP, DP, Mat_t(1)); - processNoiseCov.at(0, 0) = 1e-14; - processNoiseCov.at(1, 1) = 1e-14; - processNoiseCov.at(2, 2) = 1e-6; - processNoiseCov.at(3, 3) = 1e-6; - processNoiseCov.at(4, 4) = 1e-6; - processNoiseCov.at(5, 5) = 1e-6; - - cv::Mat measurementNoiseCov = cv::Mat::zeros(MP, MP, Mat_t(1)); - measurementNoiseCov.at(0, 0) = 1e-6; - measurementNoiseCov.at(1, 1) = 1e-6; - - cv::Mat initState(DP, 1, Mat_t(1)); - initState.at(0, 0) = xy0.x; - initState.at(1, 0) = xy0.y; - initState.at(2, 0) = xyv0.x; - initState.at(3, 0) = xyv0.y; - initState.at(4, 0) = 0; - initState.at(5, 0) = 0; - - cv::Mat P = 1e-6 * cv::Mat::eye(DP, DP, Mat_t(1)); - - cv::Ptr model(new AcceleratedModel(m_deltaTime, false)); - cv::tracking::AugmentedUnscentedKalmanFilterParams params(DP, MP, CP, 0, 0, model); - params.dataType = Mat_t(1); - params.stateInit = initState.clone(); - params.errorCovInit = P.clone(); - params.measurementNoiseCov = measurementNoiseCov.clone(); - params.processNoiseCov = processNoiseCov.clone(); - - params.alpha = 1; - params.beta = 2.0; - params.k = -2.0; - - m_uncsentedKalman = cv::tracking::createAugmentedUnscentedKalmanFilter(params); - - m_initialized = true; -} - -//--------------------------------------------------------------------------- -void TKalmanFilter::CreateAugmentedUnscented(cv::Rect_ rect0, Point_t rectv0) -{ - int MP = 4; - int DP = 8; - int CP = 0; - - cv::Mat processNoiseCov = cv::Mat::zeros(DP, DP, Mat_t(1)); - processNoiseCov.at(0, 0) = 1e-3; - processNoiseCov.at(1, 1) = 1e-3; - processNoiseCov.at(2, 2) = 1e-3; - processNoiseCov.at(3, 3) = 1e-3; - processNoiseCov.at(4, 4) = 1e-3; - processNoiseCov.at(5, 5) = 1e-3; - processNoiseCov.at(6, 6) = 1e-3; - processNoiseCov.at(7, 7) = 1e-3; - - cv::Mat measurementNoiseCov = cv::Mat::zeros(MP, MP, Mat_t(1)); - measurementNoiseCov.at(0, 0) = 1e-3; - measurementNoiseCov.at(1, 1) = 1e-3; - measurementNoiseCov.at(2, 2) = 1e-3; - measurementNoiseCov.at(3, 3) = 1e-3; - - cv::Mat initState(DP, 1, Mat_t(1)); - initState.at(0, 0) = rect0.x; - initState.at(1, 0) = rect0.y; - initState.at(2, 0) = rectv0.x; - initState.at(3, 0) = rectv0.y; - initState.at(4, 0) = 0; - initState.at(5, 0) = 0; - initState.at(6, 0) = rect0.width; - initState.at(7, 0) = rect0.height; - - cv::Mat P = 1e-3 * cv::Mat::eye(DP, DP, Mat_t(1)); - - cv::Ptr model(new AcceleratedModel(m_deltaTime, true)); - cv::tracking::AugmentedUnscentedKalmanFilterParams params(DP, MP, CP, 0, 0, model); - params.dataType = Mat_t(1); - params.stateInit = initState.clone(); - params.errorCovInit = P.clone(); - params.measurementNoiseCov = measurementNoiseCov.clone(); - params.processNoiseCov = processNoiseCov.clone(); - - params.alpha = 1; - params.beta = 2.0; - params.k = -2.0; - - m_uncsentedKalman = cv::tracking::createAugmentedUnscentedKalmanFilter(params); - - m_initialized = true; -} -#endif - -//--------------------------------------------------------------------------- -Point_t TKalmanFilter::GetPointPrediction() -{ - if (m_initialized) - { - cv::Mat prediction; - - switch (m_type) - { - case tracking::KalmanLinear: - prediction = m_linearKalman->predict(); - break; - - case tracking::KalmanUnscented: - case tracking::KalmanAugmentedUnscented: -#ifdef USE_OCV_UKF - prediction = m_uncsentedKalman->predict(); -#else - prediction = m_linearKalman->predict(); - std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; -#endif - break; - } - - m_lastPointResult = Point_t(prediction.at(0), prediction.at(1)); - } - else - { - - } - return m_lastPointResult; -} - -//--------------------------------------------------------------------------- -Point_t TKalmanFilter::Update(Point_t pt, bool dataCorrect) -{ - if (!m_initialized) - { - if (m_initialPoints.size() < MIN_INIT_VALS) - { - if (dataCorrect) - { - m_initialPoints.push_back(pt); - } - } - if (m_initialPoints.size() == MIN_INIT_VALS) - { - track_t kx = 0; - track_t bx = 0; - track_t ky = 0; - track_t by = 0; - get_lin_regress_params(m_initialPoints, 0, MIN_INIT_VALS, kx, bx, ky, by); - Point_t xy0(kx * (MIN_INIT_VALS - 1) + bx, ky * (MIN_INIT_VALS - 1) + by); - Point_t xyv0(kx, ky); - - switch (m_type) - { - case tracking::KalmanLinear: - CreateLinear(xy0, xyv0); - break; - - case tracking::KalmanUnscented: -#ifdef USE_OCV_UKF - CreateUnscented(xy0, xyv0); -#else - CreateLinear(xy0, xyv0); - std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; -#endif - break; - - case tracking::KalmanAugmentedUnscented: -#ifdef USE_OCV_UKF - CreateAugmentedUnscented(xy0, xyv0); -#else - CreateLinear(xy0, xyv0); - std::cerr << "AugmentedUnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; -#endif - break; - } - } - } - - if (m_initialized) - { - cv::Mat measurement(2, 1, Mat_t(1)); - if (!dataCorrect) - { - measurement.at(0) = m_lastPointResult.x; //update using prediction - measurement.at(1) = m_lastPointResult.y; - } - else - { - measurement.at(0) = pt.x; //update using measurements - measurement.at(1) = pt.y; - } - // Correction - cv::Mat estimated; - switch (m_type) - { - case tracking::KalmanLinear: - estimated = m_linearKalman->correct(measurement); - break; - - case tracking::KalmanUnscented: - case tracking::KalmanAugmentedUnscented: -#ifdef USE_OCV_UKF - estimated = m_uncsentedKalman->correct(measurement); -#else - estimated = m_linearKalman->correct(measurement); - std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; -#endif - break; - } - - m_lastPointResult.x = estimated.at(0); //update using measurements - m_lastPointResult.y = estimated.at(1); - } - else - { - if (dataCorrect) - { - m_lastPointResult = pt; - } - } - return m_lastPointResult; -} - -//--------------------------------------------------------------------------- -cv::Rect TKalmanFilter::GetRectPrediction() -{ - if (m_initialized) - { - cv::Mat prediction; - - switch (m_type) - { - case tracking::KalmanLinear: - prediction = m_linearKalman->predict(); - break; - - case tracking::KalmanUnscented: - case tracking::KalmanAugmentedUnscented: -#ifdef USE_OCV_UKF - prediction = m_uncsentedKalman->predict(); -#else - prediction = m_linearKalman->predict(); - std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; -#endif - break; - } - - m_lastRectResult = cv::Rect_(prediction.at(0), prediction.at(1), prediction.at(2), prediction.at(3)); - } - else - { - - } - return cv::Rect(static_cast(m_lastRectResult.x), static_cast(m_lastRectResult.y), static_cast(m_lastRectResult.width), static_cast(m_lastRectResult.height)); -} - -//--------------------------------------------------------------------------- -cv::Rect TKalmanFilter::Update(cv::Rect rect, bool dataCorrect) -{ - if (!m_initialized) - { - if (m_initialRects.size() < MIN_INIT_VALS) - { - if (dataCorrect) - { - m_initialRects.push_back(rect); - } - } - if (m_initialRects.size() == MIN_INIT_VALS) - { - std::vector initialPoints; - Point_t averageSize(0, 0); - for (const auto& r : m_initialRects) - { - initialPoints.push_back(Point_t(r.x, r.y)); - averageSize.x += r.width; - averageSize.y += r.height; - } - averageSize.x /= MIN_INIT_VALS; - averageSize.y /= MIN_INIT_VALS; - - track_t kx = 0; - track_t bx = 0; - track_t ky = 0; - track_t by = 0; - get_lin_regress_params(initialPoints, 0, MIN_INIT_VALS, kx, bx, ky, by); - cv::Rect_ rect0(kx * (MIN_INIT_VALS - 1) + bx, ky * (MIN_INIT_VALS - 1) + by, averageSize.x, averageSize.y); - Point_t rectv0(kx, ky); - - switch (m_type) - { - case tracking::KalmanLinear: - CreateLinear(rect0, rectv0); - break; - - case tracking::KalmanUnscented: -#ifdef USE_OCV_UKF - CreateUnscented(rect0, rectv0); -#else - CreateLinear(rect0, rectv0); - std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; -#endif - break; - - case tracking::KalmanAugmentedUnscented: -#ifdef USE_OCV_UKF - CreateAugmentedUnscented(rect0, rectv0); -#else - CreateLinear(rect0, rectv0); - std::cerr << "AugmentedUnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; -#endif - break; - } - } - } - - if (m_initialized) - { - cv::Mat measurement(4, 1, Mat_t(1)); - if (!dataCorrect) - { - measurement.at(0) = m_lastRectResult.x; // update using prediction - measurement.at(1) = m_lastRectResult.y; - measurement.at(2) = m_lastRectResult.width; - measurement.at(3) = m_lastRectResult.height; - } - else - { - measurement.at(0) = static_cast(rect.x); // update using measurements - measurement.at(1) = static_cast(rect.y); - measurement.at(2) = static_cast(rect.width); - measurement.at(3) = static_cast(rect.height); - } - // Correction - cv::Mat estimated; - switch (m_type) - { - case tracking::KalmanLinear: - estimated = m_linearKalman->correct(measurement); - - m_lastRectResult.x = estimated.at(0); //update using measurements - m_lastRectResult.y = estimated.at(1); - m_lastRectResult.width = estimated.at(2); - m_lastRectResult.height = estimated.at(3); - break; - - case tracking::KalmanUnscented: - case tracking::KalmanAugmentedUnscented: -#ifdef USE_OCV_UKF - estimated = m_uncsentedKalman->correct(measurement); - - m_lastRectResult.x = estimated.at(0); //update using measurements - m_lastRectResult.y = estimated.at(1); - m_lastRectResult.width = estimated.at(6); - m_lastRectResult.height = estimated.at(7); -#else - estimated = m_linearKalman->correct(measurement); - - m_lastRectResult.x = estimated.at(0); //update using measurements - m_lastRectResult.y = estimated.at(1); - m_lastRectResult.width = estimated.at(2); - m_lastRectResult.height = estimated.at(3); - std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; -#endif - break; - } - } - else - { - if (dataCorrect) - { - m_lastRectResult.x = static_cast(rect.x); - m_lastRectResult.y = static_cast(rect.y); - m_lastRectResult.width = static_cast(rect.width); - m_lastRectResult.height = static_cast(rect.height); - } - } - return cv::Rect(static_cast(m_lastRectResult.x), static_cast(m_lastRectResult.y), static_cast(m_lastRectResult.width), static_cast(m_lastRectResult.height)); -} diff --git a/Tracker/Kalman.h b/Tracker/Kalman.h deleted file mode 100644 index 5837324df..000000000 --- a/Tracker/Kalman.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once -#include "defines.h" -#include - -#include - -#ifdef USE_OCV_UKF -#include -#include -#endif - -// http://www.morethantechnical.com/2011/06/17/simple-kalman-filter-for-tracking-using-opencv-2-2-w-code/ -class TKalmanFilter -{ -public: - TKalmanFilter(tracking::KalmanType type, Point_t pt, track_t deltaTime = 0.2, track_t accelNoiseMag = 0.5); - TKalmanFilter(tracking::KalmanType type, cv::Rect rect, track_t deltaTime = 0.2, track_t accelNoiseMag = 0.5); - ~TKalmanFilter(); - - Point_t GetPointPrediction(); - Point_t Update(Point_t pt, bool dataCorrect); - - cv::Rect GetRectPrediction(); - cv::Rect Update(cv::Rect rect, bool dataCorrect); - -private: - tracking::KalmanType m_type; - std::unique_ptr m_linearKalman; -#ifdef USE_OCV_UKF - cv::Ptr m_uncsentedKalman; -#endif - - std::deque m_initialPoints; - std::deque m_initialRects; - static const size_t MIN_INIT_VALS = 4; - - Point_t m_lastPointResult; - cv::Rect_ m_lastRectResult; - cv::Rect_ m_lastRect; - - bool m_initialized; - track_t m_deltaTime; - track_t m_accelNoiseMag; - - void CreateLinear(Point_t xy0, Point_t xyv0); - void CreateLinear(cv::Rect_ rect0, Point_t rectv0); -#ifdef USE_OCV_UKF - void CreateUnscented(Point_t xy0, Point_t xyv0); - void CreateUnscented(cv::Rect_ rect0, Point_t rectv0); - void CreateAugmentedUnscented(Point_t xy0, Point_t xyv0); - void CreateAugmentedUnscented(cv::Rect_ rect0, Point_t rectv0); -#endif -}; diff --git a/Tracker/LocalTracker.cpp b/Tracker/LocalTracker.cpp deleted file mode 100644 index d56a00824..000000000 --- a/Tracker/LocalTracker.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "LocalTracker.h" - -// --------------------------------------------------------------------------- -// -// --------------------------------------------------------------------------- -LocalTracker::LocalTracker() -{ -} - -// --------------------------------------------------------------------------- -// -// --------------------------------------------------------------------------- -LocalTracker::~LocalTracker(void) -{ -} - -// --------------------------------------------------------------------------- -// -// --------------------------------------------------------------------------- -void LocalTracker::Update( - tracks_t& tracks, - cv::UMat prevFrame, - cv::UMat currFrame - ) -{ - std::vector points[2]; - - points[0].reserve(8 * tracks.size()); - for (auto& track : tracks) - { - for (const auto& pt : track->m_lastRegion.m_points) - { - points[0].push_back(pt); - } - } - if (points[0].empty()) - { - return; - } - - cv::TermCriteria termcrit(cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 30, 0.01); - cv::Size subPixWinSize(3, 3); - cv::Size winSize(21, 21); - - cv::cornerSubPix(prevFrame, points[0], subPixWinSize, cv::Size(-1,-1), termcrit); - - std::vector status; - std::vector err; - - cv::calcOpticalFlowPyrLK(prevFrame, currFrame, points[0], points[1], status, err, winSize, 3, termcrit, 0, 0.001); - - size_t i = 0; - for (auto& track : tracks) - { - track->m_averagePoint = Point_t(0, 0); - track->m_boundidgRect = cv::Rect(0, 0, 0, 0); - - for (auto it = track->m_lastRegion.m_points.begin(); it != track->m_lastRegion.m_points.end();) - { - if (status[i]) - { - *it = points[1][i]; - track->m_averagePoint += *it; - - ++it; - } - else - { - it = track->m_lastRegion.m_points.erase(it); - } - - ++i; - } - - if (!track->m_lastRegion.m_points.empty()) - { - track->m_averagePoint /= static_cast(track->m_lastRegion.m_points.size()); - - cv::Rect br = cv::boundingRect(track->m_lastRegion.m_points); -#if 0 - br.x -= subPixWinSize.width; - br.width += 2 * subPixWinSize.width; - if (br.x < 0) - { - br.width += br.x; - br.x = 0; - } - if (br.x + br.width >= currFrame.cols) - { - br.x = currFrame.cols - br.width - 1; - } - - br.y -= subPixWinSize.height; - br.height += 2 * subPixWinSize.height; - if (br.y < 0) - { - br.height += br.y; - br.y = 0; - } - if (br.y + br.height >= currFrame.rows) - { - br.y = currFrame.rows - br.height - 1; - } -#endif - track->m_boundidgRect = br; - } - } -} diff --git a/Tracker/LocalTracker.h b/Tracker/LocalTracker.h deleted file mode 100644 index 4a31e6b46..000000000 --- a/Tracker/LocalTracker.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "defines.h" -#include "track.h" - -// -------------------------------------------------------------------------- -// Tracking only founded regions between two frames (now used LK optical flow) -// -------------------------------------------------------------------------- -class LocalTracker -{ -public: - LocalTracker(); - ~LocalTracker(void); - - void Update(tracks_t& tracks, cv::UMat prevFrame, cv::UMat currFrame); -}; diff --git a/Tracker/graph/GTL/include/GTL/1.txt b/Tracker/graph/GTL/include/GTL/1.txt deleted file mode 100644 index ccca79639..000000000 --- a/Tracker/graph/GTL/include/GTL/1.txt +++ /dev/null @@ -1,39 +0,0 @@ -Makefile.am -Makefile.in -algorithm.h -bellman_ford.h -bfs.h -biconnectivity.h -bid_dijkstra.h -bin_heap.h -components.h -debug.h -dfs.h -dijkstra.h -edge.h -edge_data.h -edge_map.h -embedding.h -fm_partition.h -gml_parser.h -gml_scanner.h -graph.h -maxflow_ff.h -maxflow_pp.h -maxflow_sap.h -min_tree.h -ne_map.h -node.h -node_data.h -node_map.h -planarity.h -pq_node.h -pq_tree.h -ratio_cut_partition.h -st_number.h -symlist.h -topsort.h -version.h -version.h.in -GTL.h -1.txt diff --git a/Tracker/graph/GTL/include/GTL/GTL.h b/Tracker/graph/GTL/include/GTL/GTL.h deleted file mode 100644 index 1ca2f1f11..000000000 --- a/Tracker/graph/GTL/include/GTL/GTL.h +++ /dev/null @@ -1,144 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// GTL.h - Internal header: DO NO USE IT DIRECTLY !!! -// -//========================================================================== -// $Id: GTL.h,v 1.29 2008/02/03 18:17:08 chris Exp $ - -#ifndef GTL_GTL_H -#define GTL_GTL_H - -#include - -//-------------------------------------------------------------------------- -// Generic iteration over container elements -//-------------------------------------------------------------------------- -// -// elem: loop variable -// cont: container to iterate over -// iter_t: iterator type -// iter: prefix for begin() and end() -// -// contains a hack for Microsoft Visual C++ 5.0, because code like -// -// for(int i=0; i<10; ++i) { ... do something ... } -// for(int i=0; i<10; ++i) { ... do something again ... } -// -// is illegal with Microsoft Extensions enabled, but without Microsoft -// Extensions, the Microsoft STL does not work :-(. -// So we code the line number (__LINE__) into our loop variables. - -#define GTL_CONCAT(x, y) x##y -#define GTL_FORALL_VAR(y) GTL_CONCAT(GTL_FORALL_VAR, y) - -#define GTL_FORALL(elem, cont, iter_t, iter) \ -if ((cont).iter##begin() != (cont).iter##end()) \ - (elem) = *((cont).iter##begin()); \ -for (iter_t GTL_FORALL_VAR(__LINE__) = (cont).iter##begin(); \ - GTL_FORALL_VAR(__LINE__) != (cont).iter##end(); \ - (elem) = (++GTL_FORALL_VAR(__LINE__)) == \ - (cont).iter##end() ? (elem) : *GTL_FORALL_VAR(__LINE__)) - -//-------------------------------------------------------------------------- -// Configuration for GCC >= 2.8.0 -//-------------------------------------------------------------------------- - -// -// Using namespaces is the default; may be unset by one of the -// following configurations. -// - -#define __GTL_USE_NAMESPACES - -#ifdef __GNUC__ - -# define __GTL_GCC - -# if __GNUC__ == 2 && __GNUC_MINOR__ >= 8 - -# undef __GTL_USE_NAMESPACES - -# elif __GNUC__ < 3 - -# error "Need at least version 2.8.0 of GCC to compile GTL." - -# endif - -// -// 2/3/2008 chris: -// -// Enable comparison of iterators in debug mode -// - -# if __GNUC__ >= 4 -# undef _GLIBCXX_DEBUG -# endif -#endif - -//-------------------------------------------------------------------------- -// Configuration for Microsoft Visual C++ 5.0 -//-------------------------------------------------------------------------- - -#ifdef _MSC_VER -/* -# if _MSC_VER >= 1400 // Visual Studio 2005 - -# define _HAS_ITERATOR_DEBUGGING 0 -# define _CRT_SECURE_NO_DEPRECATE 1 -# define _SECURE_SCL 0 - -# endif -*/ -# if _MSC_VER >= 1100 - -# define __GTL_USE_NAMESPACES -# define __GTL_MSVCC - -# pragma warning( disable : 4786 ) -# pragma warning( disable : 4251 ) - -# define GTL_EXTERN -# else - -# error "Need at least version 5.0 of MS Visual C++ to compile GTL." - -# endif -#else - -# define GTL_EXTERN - -#endif - -//-------------------------------------------------------------------------- -// Namespaces -//-------------------------------------------------------------------------- - -#ifdef __GTL_USE_NAMESPACES - -# define __GTL_BEGIN_NAMESPACE namespace GTL { -# define __GTL_END_NAMESPACE } - -#else - -# define __GTL_BEGIN_NAMESPACE -# define __GTL_END_NAMESPACE - -#endif - -//-------------------------------------------------------------------------- -// Temporary hack until Graphlet (i.e. gcc) supports Namespaces -//-------------------------------------------------------------------------- - -#ifdef __GTL_USE_NAMESPACES - -namespace GTL {} -using namespace GTL; - -#endif // __GTL_USE_NAMESPACES - -#endif // GTL_GTL_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/Makefile.am b/Tracker/graph/GTL/include/GTL/Makefile.am deleted file mode 100644 index 73c6e932a..000000000 --- a/Tracker/graph/GTL/include/GTL/Makefile.am +++ /dev/null @@ -1,56 +0,0 @@ -#=========================================================================== -# -# include/GTL/Makefile.am -# -#=========================================================================== -# $Id: Makefile.am,v 1.28 2003/01/31 08:58:08 chris Exp $ - -BUILT_SOURCES = version.h - -pkginclude_HEADERS = \ - GTL.h \ - algorithm.h \ - bfs.h \ - biconnectivity.h \ - debug.h \ - dfs.h \ - edge.h \ - edge_data.h \ - edge_map.h \ - embedding.h \ - gml_parser.h \ - gml_scanner.h \ - graph.h \ - ne_map.h \ - node.h \ - node_data.h \ - node_map.h \ - planarity.h \ - pq_node.h \ - pq_tree.h \ - st_number.h \ - symlist.h \ - topsort.h \ - maxflow_ff.h \ - maxflow_pp.h \ - maxflow_sap.h \ - components.h \ - fm_partition.h \ - ratio_cut_partition.h \ - min_tree.h \ - dijkstra.h \ - bellman_ford.h \ - bin_heap.h \ - bid_dijkstra.h - -nodist_pkginclude_HEADERS = \ - version.h - - -dist-hook: version.h - cp version.h $(distdir) - -#--------------------------------------------------------------------------- -# end of file -#--------------------------------------------------------------------------- - diff --git a/Tracker/graph/GTL/include/GTL/Makefile.in b/Tracker/graph/GTL/include/GTL/Makefile.in deleted file mode 100644 index 1a262a2d9..000000000 --- a/Tracker/graph/GTL/include/GTL/Makefile.in +++ /dev/null @@ -1,487 +0,0 @@ -# Makefile.in generated by automake 1.10 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006 Free Software Foundation, Inc. -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -#=========================================================================== -# -# include/GTL/Makefile.am -# -#=========================================================================== -# $Id: Makefile.am,v 1.28 2003/01/31 08:58:08 chris Exp $ - -VPATH = @srcdir@ -pkgdatadir = $(datadir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -subdir = include/GTL -DIST_COMMON = $(pkginclude_HEADERS) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in $(srcdir)/version.h.in -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/configure.in -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_CLEAN_FILES = version.h -SOURCES = -DIST_SOURCES = -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; -am__installdirs = "$(DESTDIR)$(pkgincludedir)" \ - "$(DESTDIR)$(pkgincludedir)" -nodist_pkgincludeHEADERS_INSTALL = $(INSTALL_HEADER) -pkgincludeHEADERS_INSTALL = $(INSTALL_HEADER) -HEADERS = $(nodist_pkginclude_HEADERS) $(pkginclude_HEADERS) -ETAGS = etags -CTAGS = ctags -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -ECHO = @ECHO@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -F77 = @F77@ -FFLAGS = @FFLAGS@ -GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ -GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ -GENERIC_RELEASE = @GENERIC_RELEASE@ -GENERIC_VERSION = @GENERIC_VERSION@ -GREP = @GREP@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -LDFLAGS = @LDFLAGS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBTOOL = @LIBTOOL@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -MAKEINFO = @MAKEINFO@ -MKDIR_P = @MKDIR_P@ -OBJEXT = @OBJEXT@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -VERSION = @VERSION@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_F77 = @ac_ct_F77@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -doxygen = @doxygen@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target_alias = @target_alias@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -BUILT_SOURCES = version.h -pkginclude_HEADERS = \ - GTL.h \ - algorithm.h \ - bfs.h \ - biconnectivity.h \ - debug.h \ - dfs.h \ - edge.h \ - edge_data.h \ - edge_map.h \ - embedding.h \ - gml_parser.h \ - gml_scanner.h \ - graph.h \ - ne_map.h \ - node.h \ - node_data.h \ - node_map.h \ - planarity.h \ - pq_node.h \ - pq_tree.h \ - st_number.h \ - symlist.h \ - topsort.h \ - maxflow_ff.h \ - maxflow_pp.h \ - maxflow_sap.h \ - components.h \ - fm_partition.h \ - ratio_cut_partition.h \ - min_tree.h \ - dijkstra.h \ - bellman_ford.h \ - bin_heap.h \ - bid_dijkstra.h - -nodist_pkginclude_HEADERS = \ - version.h - -all: $(BUILT_SOURCES) - $(MAKE) $(AM_MAKEFLAGS) all-am - -.SUFFIXES: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ - && exit 0; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/GTL/Makefile'; \ - cd $(top_srcdir) && \ - $(AUTOMAKE) --gnu include/GTL/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -version.h: $(top_builddir)/config.status $(srcdir)/version.h.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs -install-nodist_pkgincludeHEADERS: $(nodist_pkginclude_HEADERS) - @$(NORMAL_INSTALL) - test -z "$(pkgincludedir)" || $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" - @list='$(nodist_pkginclude_HEADERS)'; for p in $$list; do \ - if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ - f=$(am__strip_dir) \ - echo " $(nodist_pkgincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(pkgincludedir)/$$f'"; \ - $(nodist_pkgincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(pkgincludedir)/$$f"; \ - done - -uninstall-nodist_pkgincludeHEADERS: - @$(NORMAL_UNINSTALL) - @list='$(nodist_pkginclude_HEADERS)'; for p in $$list; do \ - f=$(am__strip_dir) \ - echo " rm -f '$(DESTDIR)$(pkgincludedir)/$$f'"; \ - rm -f "$(DESTDIR)$(pkgincludedir)/$$f"; \ - done -install-pkgincludeHEADERS: $(pkginclude_HEADERS) - @$(NORMAL_INSTALL) - test -z "$(pkgincludedir)" || $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" - @list='$(pkginclude_HEADERS)'; for p in $$list; do \ - if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ - f=$(am__strip_dir) \ - echo " $(pkgincludeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(pkgincludedir)/$$f'"; \ - $(pkgincludeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(pkgincludedir)/$$f"; \ - done - -uninstall-pkgincludeHEADERS: - @$(NORMAL_UNINSTALL) - @list='$(pkginclude_HEADERS)'; for p in $$list; do \ - f=$(am__strip_dir) \ - echo " rm -f '$(DESTDIR)$(pkgincludedir)/$$f'"; \ - rm -f "$(DESTDIR)$(pkgincludedir)/$$f"; \ - done - -ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - mkid -fID $$unique -tags: TAGS - -TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - tags=; \ - here=`pwd`; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$tags $$unique; \ - fi -ctags: CTAGS -CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - tags=; \ - here=`pwd`; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - test -z "$(CTAGS_ARGS)$$tags$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$tags $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && cd $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) $$here - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ - fi; \ - cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ - else \ - test -f $(distdir)/$$file \ - || cp -p $$d/$$file $(distdir)/$$file \ - || exit 1; \ - fi; \ - done - $(MAKE) $(AM_MAKEFLAGS) \ - top_distdir="$(top_distdir)" distdir="$(distdir)" \ - dist-hook -check-am: all-am -check: $(BUILT_SOURCES) - $(MAKE) $(AM_MAKEFLAGS) check-am -all-am: Makefile $(HEADERS) -installdirs: - for dir in "$(DESTDIR)$(pkgincludedir)" "$(DESTDIR)$(pkgincludedir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: $(BUILT_SOURCES) - $(MAKE) $(AM_MAKEFLAGS) install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." - -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) -clean: clean-am - -clean-am: clean-generic clean-libtool mostlyclean-am - -distclean: distclean-am - -rm -f Makefile -distclean-am: clean-am distclean-generic distclean-tags - -dvi: dvi-am - -dvi-am: - -html: html-am - -info: info-am - -info-am: - -install-data-am: install-nodist_pkgincludeHEADERS \ - install-pkgincludeHEADERS - -install-dvi: install-dvi-am - -install-exec-am: - -install-html: install-html-am - -install-info: install-info-am - -install-man: - -install-pdf: install-pdf-am - -install-ps: install-ps-am - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: uninstall-nodist_pkgincludeHEADERS \ - uninstall-pkgincludeHEADERS - -.MAKE: install-am install-strip - -.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ - clean-libtool ctags dist-hook distclean distclean-generic \ - distclean-libtool distclean-tags distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dvi install-dvi-am install-exec \ - install-exec-am install-html install-html-am install-info \ - install-info-am install-man install-nodist_pkgincludeHEADERS \ - install-pdf install-pdf-am install-pkgincludeHEADERS \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \ - uninstall-am uninstall-nodist_pkgincludeHEADERS \ - uninstall-pkgincludeHEADERS - - -dist-hook: version.h - cp version.h $(distdir) - -#--------------------------------------------------------------------------- -# end of file -#--------------------------------------------------------------------------- -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/Tracker/graph/GTL/include/GTL/algorithm.h b/Tracker/graph/GTL/include/GTL/algorithm.h deleted file mode 100644 index fc85a453f..000000000 --- a/Tracker/graph/GTL/include/GTL/algorithm.h +++ /dev/null @@ -1,94 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// algorithm.h -// -//========================================================================== -// $Id: algorithm.h,v 1.14 2003/03/24 15:58:54 raitner Exp $ - -#ifndef GTL_ALGORITHM_H -#define GTL_ALGORITHM_H - -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * $Date: 2003/03/24 15:58:54 $ - * $Revision: 1.14 $ - * - * @brief Abstract baseclass for all algoritm-classes. - */ -class GTL_EXTERN algorithm { -public: - /** - * @var algorithm::GTL_OK - * Used as (positive) return value of algorithm::check and - * algorithm::run. - */ - - /** - * @var algorithm::GTL_ERROR - * Used as (negative) return value of algorithm::check and - * algorithm::run. - */ - - /** - * @brief Return values for algorithm::check and algorithm::run - */ - enum { - GTL_OK = 1, - GTL_ERROR = 0 - }; - - /** - * @brief Creates an algorithm object. - */ - algorithm () { }; - - /** - * @brief Destroys the algorithm object. - */ - virtual ~algorithm () { }; - - /** - * @brief Applies %algorithm to %graph g. - * - * @param g %graph - * @retval algorithm::GTL_OK on success - * @retval algorithm::GTL_ERROR otherwise - */ - virtual int run (graph& g) = 0; - - /** - * @brief Checks whether all preconditions are satisfied. - * - * @em Please @em note: It is - * definitly required (and #run relies on it), - * that this method was called in advance. - * - * @param g %graph - * @retval algorithm::GTL_OK if %algorithm can be applied - * @retval algorithm::GTL_ERROR otherwise. - */ - virtual int check (graph& g) = 0; - - /** - * @brief Resets %algorithm - * - * Prepares the %algorithm to be applied to - * another %graph. @em Please @em note: The options an - * %algorithm may support do @em not get reset by - * this. It is just to reset internally used datastructures. - */ - virtual void reset () = 0; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_ALGORITHM_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/bellman_ford.h b/Tracker/graph/GTL/include/GTL/bellman_ford.h deleted file mode 100644 index 91f43d489..000000000 --- a/Tracker/graph/GTL/include/GTL/bellman_ford.h +++ /dev/null @@ -1,237 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// bellman_ford.h -// -//========================================================================== -// $Id: bellman_ford.h,v 1.5 2003/03/24 15:58:54 raitner Exp $ - -#ifndef GTL_BELLMAN_FORD_H -#define GTL_BELLMAN_FORD_H - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - - -/** - * $Date: 2003/03/24 15:58:54 $ - * $Revision: 1.5 $ - * - * @brief Bellman Ford %algorithm. - * - * Implementation of the single source shortest path due to - * Bellman and Ford. Unlike Dijkstra's SSSP %algorithm this one - * allows negative edge weights, as long as there are no cycles - * with negative weight. If there are negative cycles this - * implementation finds them. - */ - -class GTL_EXTERN bellman_ford : public algorithm -{ -public: - - /** - * @brief Constructor. - */ - bellman_ford(); - - /** - * @brief Destructor. - */ - virtual ~bellman_ford(); - - /** - * @brief Checks whether the preconditions for Bellman Ford - * are satisfied. - * - * The Precondition are that the weights of the edges - * have been set and that the graph has at least one node. - * - * @param G graph. - * @retval algorithm::GTL_OK if %algorithm can be applied - * @retval algorithm::GTL_ERROR otherwise. - */ - int check (graph& G); - - int run (graph& G); - - /** - * @brief Resets the algorithm. - * - * The weights are not reset. You can apply this algorithms - * twice without setting the weights for the second call. - */ - void reset (); - - /** - * @brief Sets source. - * - * The default source is the invalid %node (node::node()), - * in this case an arbitrary %node is chosen and stored when - * this algorithm is run. - * - * @param n source. - */ - void source (const node& n) {s = n;} - - /** - * @brief Returns source. - * - * @return source. - */ - node source () const {return s;} - - /** - * @brief Sets weights of the edges. - * - * This method @b must be called before run. - * - * @param w weights of the edges. - */ - void weights (const edge_map& weight) {w = weight; vars_set = true; } - - /** - * @brief Enables or disables the storing of predecessors. - * - * If enabled for every %node the predecessor on the shortest - * path from will be stored. - * - * @param set if true predecessors will be stored. - * @sa bellman_ford::predecessor_node, - * bellman_ford::predecessor_edge - */ - void store_preds (bool set); - - /** - * @brief Returns whether the storing of predecessors is enabled. - * - * @retval true iff the storing of predecessors is enabled. - * - * @sa bellman_ford::predecessor_node, - * bellman_ford::predecessor_edge - */ - bool store_preds () const {return preds != 0;} - - /** - * @brief Returns whether is reachable from source. - * - * @param n node - */ - bool reached (const node& n) const {return !inf[n];} - - /** - * @brief Returns the distance from source to @a n - * - * @param n node - */ - double distance (const node& n) const {return d[n];} - - /** - * @brief edge to predecessor of %node @a n on the shortest - * path from source - * - * If @a n is a root or wasn't reached the return value is - * the invalid %edge edge::edge(). - * - * @em Please @em note that this requires that this option - * was enabled during last run. - * - * @param n node. - * @return predecessor of @a n. - * @sa bellman_ford::store_preds - */ - edge predecessor_edge (const node& n) const - {assert (preds); return (*preds)[n];} - - /** - * @brief predecessor of %node @a n on the shortest - * path from source - * - * If @a n is a root or wasn't reached the return value is - * the invalid %node node::node(). - * - * @em Please @em note that this requires that this option - * was enabled during last run. - * - * @param n node. - * @return predecessor of @a n. - * @sa bellman_ford::store_preds - */ - node predecessor_node (const node& n) const - {edge e = predecessor_edge(n); return e == edge() ? node() : e.opposite(n); } - - /** - * @brief Returns whether there is a cycle with negative - * weight. - */ - bool negative_cycle() const - {return cycle;} - -private: - - - /** - * @brief Main method for Bellman Ford - * - * @param e edge to be relaxed - */ - void relax (const edge& e, bool dir); - - /** - * @brief Stores source. - * - * @sa bellman_ford::source. - */ - node s; - - /** - * @brief Stores the weights of the edges. - * - * @sa bellman_ford::weights. - */ - edge_map w; - - /** - * @brief Indicates whether weights were set. - * - * @sa bellman_ford::weights. - */ - bool vars_set; - - /** - * @brief distance from source s. - * - * @sa bellman_ford::distance. - */ - node_map d; - - /** - * @brief Indicates whether the node has distance infinity - * - * @sa bellman_ford::distance. - */ - node_map inf; - - /** - * @brief Stores father of each %node (if enabled) - * - * @sa bellman_ford::store_preds - */ - node_map* preds; - - /** - * @brief Indicates whether there is a cycle with negative - * weight - * - * @sa bellman_ford::negative_cycle. - */ - bool cycle; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_BELLMAN_FORD_H diff --git a/Tracker/graph/GTL/include/GTL/bfs.h b/Tracker/graph/GTL/include/GTL/bfs.h deleted file mode 100644 index d00895ecd..000000000 --- a/Tracker/graph/GTL/include/GTL/bfs.h +++ /dev/null @@ -1,585 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// bfs.h -// -//========================================================================== -// $Id: bfs.h,v 1.14 2003/03/24 15:58:54 raitner Exp $ - -#ifndef GTL_BFS_H -#define GTL_BFS_H - -#include -#include -#include - -#include - -__GTL_BEGIN_NAMESPACE - -/** - * $Date: 2003/03/24 15:58:54 $ - * $Revision: 1.14 $ - * - * @brief Breadth-First-Search (BFS) %algorithm. - * - * Encapsulates the BFS %algorithm together with all data - * produced by it. There are a few parameters, which on the one - * hand influence the behaviour of BFS (e.g. bfs::start_node) and - * on the other hand toggle the storing of extra information, - * such as the level-number of each %node. In detail these are: - * - bfs::start_node - * (default: an arbitrary %node will be chosen) - * - bfs::scan_whole_graph states whether BFS will be - * continued in the unused part of the %graph, if not all - * nodes were touched at the end of BFS started at the start-%node. - * (default: disabled) - * - bfs::calc_level toggle storing of level-numbers for each - * %node, i.e. its distance from the start-%node. - * (default: disabled) - * - bfs::store_preds toggle storing the predecessor of each - * %node, i.e. the father in the BFS-tree. (default: disabled) - * - bfs::store_non_tree_edges toggle storing of all non_tree_edges - * (tree_edges are always stored) in a list and thus enable or disable - * iteration through all non_tree_edges. - * (default: disabled) - * - * @em Please @em note that the %algorithm always starts with the - * given start-%node (if none was given, the first %node is chosen - * and stored, thus after BFS the root of the tree is always - * accesible via bfs::start_node) and continues until no more - * unused nodes are reachable from already used ones. Thus if the - * %graph isn't connected not @em all nodes will be reached. If - * bfs::scan_whole_graph isn't set the BFS stops here. If it is - * set, the BFS will be continued with the next unused %node and - * so on until all nodes were used. - * - * For further customization a few virtual functions, so called - * handler, are called at crucial stages of the %algorithm. In - * this basic implementation all of these handler are empty. So - * if one wants to add only a few lines of code (e.g. some new - * numbering) he is likely to take this class as base-class and - * override the handler where neccessary. In detail these are - * (please look at the source code to see where they are called): - * - bfs::init_handler - * - bfs::end_handler - * - bfs::popped_node_handler - * - bfs::finished_node_handler - * - bfs::unused_node_handler - * - bfs::used_node_handler - * - bfs::new_start_handler - * - * @em Please @em note: We do @em not claim that the set of - * handlers provided is sufficient in any way. So if you believe - * that some new handler is needed urgently please let us know. - * - * There is a lot of information stored during BFS (e.g. nodes in - * bfs-order, list of non-tree edges). Some of it can be obtained directly - * by using the corresponding member-function (e.g. bfs::bfs_num), - * but all information that can be thought of as a list (e.g. nodes in - * bfs-order) can be accessed through iterators. In detail these are (of - * course depending on what options are chosen!): - * - bfs::bfs_iterator - * - bfs::tree_edges_iterator - * - bfs::non_tree_edges_iterator - * - bfs::roots_iterator - */ -class GTL_EXTERN bfs : public algorithm -{ -public: - - /** - * @brief Constructor. - */ - bfs (); - - /** - * @brief Destructor. - */ - virtual ~bfs (); - - int run (graph& G); - - /** - * @brief Checks whether the preconditions for BFS are satisfied. - * - * Currently there aren't any restricitions for the BFS %algorithm. - * - * @param G graph. - * @retval algorithm::GTL_OK if %algorithm can be applied - * @retval algorithm::GTL_ERROR otherwise. - */ - virtual int check (graph& /*G*/) { return GTL_OK; } - - virtual void reset (); - - //----------------------------------------------------------------------- - // Parameters - //----------------------------------------------------------------------- - - /** - * @brief Sets start-%node for BFS. - * - * The default start-%node is the invalid %node (node::node()), - * in this case an arbitrary %node is chosen and stored when - * BFS is run. - * - * @param n start-%node. - */ - void start_node (const node& n) {start = n;} - - /** - * @brief Returns start-%node for BFS. - * - * @return start-%node. - */ - node start_node () const {return start;} - - /** - * @brief Enables or disables scanning of the whole %graph. - * - * If enabled and the BFS started at the given start-%node - * stops without having touched all nodes, it will be - * continued with the next unused %node, and so on until all - * nodes were used. This makes sure that for every %node - * bfs::bfs_num is defined. - * - * If this feature is disabled, you are able to check what - * nodes can be reached, when starting a BFS at the - * start-%node, because for those not reached bfs::bfs_num - * will be 0. - * - * @param set if true enable scanning the whole %graph. - * @sa bfs::roots_begin, bfs::roots_end - */ - void scan_whole_graph (bool set) {whole_graph = set;} - - /** - * @brief Returns whether the whole graph will be scanned. - * - * @retval true iff the whole graph will be scanned. - * @sa bfs::roots_begin, bfs::roots_end - */ - bool scan_whole_graph () const {return whole_graph;} - - /** - * @brief Enables or disables the calculation of level-numbers for each - * %node. - * - * If enabled each %node gets a level-number, i.e. its - * distance from the start-%node. - * - * @param set if true level-number will be calculated. - * @sa bfs::level - */ - void calc_level (bool set); - - /** - * @brief Returns whether level-numbers will be calculated. - * - * @retval true iff level-numbers will be calculated. - * @sa bfs::level - */ - bool calc_level () const {return level_number != 0;} - - /** - * @brief Enables or disables the storing of non-tree-edges. - * - * If enabled all non-tree-edges will be stored in - * the order they occured. - * - * @param set if true non-tree-edges will be stored. - * @sa bfs::non_tree_edges_begin, bfs::non_tree_edges_end - */ - void store_non_tree_edges (bool set); - - /** - * @brief Returns whether the storing of non-tree-edges is - * enabled. - * - * @retval true iff the storing of non-tree-edges is enabled. - * @sa bfs::non_tree_edges_begin, bfs::non_tree_edges_end - */ - bool store_non_tree_edges () const {return non_tree != 0;} - - - /** - * @brief Enables or disables the storing of predecessors. - * - * If enabled for every %node the predecessor in the BFS-forest - * will be stored. - * - * @param set if true predecessors will be stored. - * @sa bfs::father - */ - void store_preds (bool set); - - /** - * @brief Returns whether the storing of predecessors is enabled. - * - * @retval true iff the storing of predecessors is enabled. - * @sa bfs::father - */ - bool store_preds () const {return preds != 0;} - - /** - * @brief Checks whether %node @a n was reached in BFS. - * - * @param n %node. - * @retval true iff @a n was reached. - */ - bool reached (const node& n) const - {return bfs_number[n] != 0;} - - /** - * @brief BFS-number of @a n. - * - * @em Please @em note that BFS-number 0 means that this %node wasn't - * reached. - * - * @param n %node. - * @return BFS-number of @a n. - */ - int bfs_num (const node& n) const - {return bfs_number[n];} - - /** - * @brief BFS-number of @a n. - * - * @em Please @em note that BFS-number 0 means that this %node wasn't - * reached. - * - * @param n %node. - * @return BFS-number of @a n. - */ - int operator[] (const node& n) const - {return bfs_number[n];} - - /** - * @brief Level-number of %node @a n. - * - * @em Please @em note that this requires that this option - * was enabled during last run. - * - * @param n node. - * @return level-number of @a n. - * @sa bfs::calc_level - */ - int level (const node& n) const - {assert (level_number); return (*level_number)[n];} - - /** - * @brief Father of %node @a n in BFS-forest. - * - * If @a n is a root in the forest or wasn't reached the - * return value is the invalid %node node::node(). - * - * @em Please @em note that this requires that this option - * was enabled during last run. - * - * @param n node. - * @return Father of @a n. - * @sa bfs::store_preds - */ - node father (const node& n) const - {assert (preds); return (*preds)[n];} - - /** - * @brief Iterator for tree-edges. - */ - typedef edges_t::const_iterator tree_edges_iterator; - - /** - * @brief Iterate through all tree-edges of last BFS. - * - * @em Please @em note that this edges not always form a - * tree. In case the %graph is not (strongly) connected and - * the whole graph was scanned, they form a forest. - * - * @return Start for iteration through all tree-edges. - */ - tree_edges_iterator tree_edges_begin () const - {return tree.begin();} - - /** - * @brief End-iterator for iteration through all tree-edges - * picked of last BFS. - * - * @return End for iteration through all tree-edges. - */ - tree_edges_iterator tree_edges_end () const - {return tree.end();} - - /** - * @brief Iterator for nodes in BFS-order. - */ - typedef nodes_t::const_iterator bfs_iterator; - - /** - * @brief Iterate through all (reached) nodes in BFS-Order. - * - * @return Start for iteration through all nodes in BFS-order. - */ - bfs_iterator begin () const - {return bfs_order.begin();} - - /** - * @brief End-iterator for iteration through all (reached) - * nodes in BFS-Order. - * - * @return End for iteration through all (reached) nodes - */ - bfs_iterator end () const - {return bfs_order.end();} - - /** - * @brief Iterator for non-tree-edges. - */ - typedef edges_t::const_iterator non_tree_edges_iterator; - - /** - * @brief Iterate through all non-tree-edges (if enabled). - * - * @return Start for iteration through all non-tree-edges. - * @sa bfs::store_non_tree_edges - */ - non_tree_edges_iterator non_tree_edges_begin () const - {assert (non_tree); return non_tree->begin(); } - - /** - * @brief End-iterator for iteration through all - * non-tree-edges (if enabled). - * - * @return End for iteration through all non-tree-edges. - * @sa bfs::store_non_tree_edges - */ - non_tree_edges_iterator non_tree_edges_end () const - {assert (non_tree); return non_tree->end(); } - - /** - * @brief Iterator for roots of trees in BFS-forest. - */ - typedef std::list::const_iterator roots_iterator; - - /** - * @brief Iterator pointing towards the first root in the - * BFS-forest. - * - * @em Please @em note that instead of pointing directly - * towards the %node (i.e. @c *it is of type @c node) - * the iterator points towards a bfs-iterator, which - * represents the root (i.e. @c *it is of type - * @c bfs_iterator). - * - * Using this technique makes it possible not only to obtain - * all the roots in the forest, but also the whole trees - * associated with each one. This can be achieved because a - * @c root_iterator specifies the exact position of the root - * in the BFS-ordering and by definition of BFS all the - * descendents of the root, i.e. the whole tree below, will - * come later in BFS, such that by incrementing the @c - * bfs_iterator a @c roots_iterator refers to, one can - * traverse the whole tree with this given root. - * - * Of course if the root isn't the last %node in the - * BFS-forest all following trees also will be traversed. But - * since the first %node of such a tree, that will be - * discovered, is its root, the successor of the @c - * roots_iterator can be used as end-iterator. - * - * @return Start for iteration through all roots in BFS-forest. - * @sa bfs::scan_whole_graph - */ - roots_iterator roots_begin () const - {return roots.begin();} - - /** - * @brief Iterator pointing to the end of all roots. - * - * @return End for iteration through all roots in BFS-forest. - * @sa bfs::scan_whole_graph - */ - roots_iterator roots_end () const - {return roots.end();} - - /** - * @brief Number of nodes reached in last BFS. - * - * @return Number of reached nodes. - * @sa bfs::scan_whole_graph - */ - int number_of_reached_nodes () const - {return reached_nodes;} - - //----------------------------------------------------------------------- - // Handler - //----------------------------------------------------------------------- - - /** - * @brief Called at the start of BFS. - * - * @param G %graph for which BFS was invoked. - */ - virtual void init_handler (graph& /*G*/) { }; - - /** - * @brief Called right before the end of BFS. - * - * @param G %graph for which BFS was invoked. - */ - virtual void end_handler (graph& /*G*/) { }; - - /** - * @brief Called after the %node @a n was taken out of the queue. - * - * @param G %graph for which BFS was invoked. - * @param n %node taken out of the queue. - */ - virtual void popped_node_handler (graph& /*G*/, node& /*n*/) { }; - - /** - * @brief Called when finished with the %node @a n. - - * A %node is finished after all its neighbors have been - * visited. - * - * @param G %graph for which BFS was invoked. - * @param n finished %node. - */ - virtual void finished_node_handler (graph& /*G*/, node& /*n*/) { }; - - /** - * @brief Called when an unused %node @a n was discovered. - * - * This means that the actual %node's @a f neighbor @a n was - * not previously discovered. - * - * @param G %graph for which BFS was invoked. - * @param n unused %node. - * @param f actual %node. - */ - virtual void unused_node_handler (graph& /*G*/, node& /*n*/, node& /*f*/) { }; - - /** - * @brief Called when an used %node @a n was found. - * - * This means that the actual %node's (@a f) neighbor @a n - * has already been discovered. - * - * @param G %graph for which BFS was invoked. - * @param n used %node. - * @param f actual %node. - */ - virtual void used_node_handler (graph& /*G*/, node& /*n*/, node& /*f*/) { }; - - /** - * @brief Called when BFS is started with start-%node - * @a n. - - * This is particularly useful when BFS was invoked with the - * @c scan_whole_graph option. - * - * @param G %graph for which BFS was invoked. - * @param n start-%node. - * @sa bfs::scan_whole_graph - */ - virtual void new_start_handler (graph& /*G*/, node& /*n*/) { }; - -private: - - void bfs_sub (graph&, const node&, edge_map*); - -protected: - - //----------------------------------------------------------------------- - // Data - //----------------------------------------------------------------------- - - /** - * @brief BFS number that will be assigned next. - */ - int act_bfs_num; - - /** - * @brief queue used in BFS. - */ - std::deque qu; - - /** - * @brief List of nodes in BFS-order - * - * @sa bfs::begin, bfs::end - */ - nodes_t bfs_order; - - /** - * @brief List of all edges of the BFS-tree - * - * @sa bfs::tree_edges_begin, bfs::tree_edges_end - */ - edges_t tree; - - /** - * @brief Stores BFS-number of nodes. - */ - node_map bfs_number; - - /** - * @brief Number of nodes reached so far. - */ - int reached_nodes; - - /** - * @brief List of all roots of the BFS-tree - * - * @sa bfs::roots_begin, bfs::roots_end - */ - std::list roots; - - //----------------------------------------------------------------------- - // Optional - //----------------------------------------------------------------------- - - /** - * @brief Stores whether whole %graph will be scanned. - * - * @sa bfs::scan_whole_graph - */ - bool whole_graph; - - /** - * @brief Stores start %node. - * - * @sa bfs:start_node - */ - node start; - - /** - * @brief Stores level number of each %node (if enabled) - * - * @sa bfs::calc_level - */ - node_map* level_number; - - /** - * @brief List of non-tree edges (if enabled) - * - * @sa bfs::store_non_tree_edges - */ - edges_t* non_tree; - - /** - * @brief Stores father of each %node (if enabled) - * - * @sa bfs::store_preds - */ - node_map* preds; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_BFS_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/biconnectivity.h b/Tracker/graph/GTL/include/GTL/biconnectivity.h deleted file mode 100644 index b666271fb..000000000 --- a/Tracker/graph/GTL/include/GTL/biconnectivity.h +++ /dev/null @@ -1,328 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// biconnectivity.h -// -//========================================================================== -// $Id: biconnectivity.h,v 1.18 2003/03/26 13:37:14 raitner Exp $ - -#ifndef GTL_BICONNECTIVITY_H -#define GTL_BICONNECTIVITY_H - -#include -#include - -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * $Date: 2003/03/26 13:37:14 $ - * $Revision: 1.18 $ - * - * @brief Biconnectivity-test and low-numbers. - * - * Obviously there is a close relationship between DFS and the testing of - * biconnectivity. Thus this test takes advantage of the possibility to - * add pieces of code to the DFS-class in order to calculate the - * low-numbers. - * - * As default no biconnected components will be stored and no edges - * will be added to make the graph biconnected. The test will run on the - * whole graph, even if it is not connected. - */ - -class GTL_EXTERN biconnectivity : public dfs -{ -public: - /** - * @brief Creates biconnectivity algorithm object. - * - * @see dfs::dfs - */ - biconnectivity (); - - /** - * @brief Destroys biconnectivity algorithm object. - * - * @see dfs::~dfs - */ - virtual ~biconnectivity () {} - - /** - * @brief Checks whether the algorithm can be applied. - * - * Necessary preconditions: - * - G is undirected. - * - storing of predecessors is enabled. - * - DFS may be applied - * - * @param G graph. - * @return algorithm::GTL_OK if binconnectivity-test can - * be applied to @a G. - * @sa dfs::scan_whole_graph, dfs::store_preds - */ - virtual int check (graph& G); - - virtual void reset (); - - /** - * @brief low-number. - * - * @param n node. - * @return low-number of n. - */ - int low_number (const node& n) const - {return low_num[n];} - - /** - * @brief Biconnectivity-test. - * - * @return true iff graph is biconnected. - */ - bool is_biconnected () const - {return num_of_components == 1;} - - /** - * @brief Returns whether the storing of components is enabled. - * - * @return true iff storing of components is enabled. - * @sa biconnectivity::components_begin, biconnectivity::components_end - */ - bool store_components () const - { return store_comp; } - - /** - * @brief Enables or disables the storing of biconnected components. - * - * If this feature is enabled, the whole graph will be scanned - * in order to get all the biconnected components even if the graph - * isn't connected. By default this feature is disabled. - * - * @param set if true each biconnected component will be stored. - * @sa biconnectivity::components_begin, biconnectivity::components_end - */ - void store_components (bool set) - { store_comp = set; if (set) scan_whole_graph (set); } - - /** - * @brief If enabled edges will be added to the graph in order to make it - * biconnected, if cutpoints are discovered. - * - * The list of added edges can be accessed via additional_begin and - * additional_end. - * - * @param set if true additional edges will we inserted - * to make the graph biconnected. - * @sa biconnectivity::additional_begin, biconnectivity::additional_end - */ - void make_biconnected (bool set) - { add_edges = set; if (set) scan_whole_graph (set); } - - /** - * @brief Returns whether addition of edges neccessary to make graph - * biconnected is enabled. - * - * @return true iff addition edges is enabled. - * @sa biconnectivity::additional_begin, biconnectivity::additional_end - */ - bool make_biconnected () const - { return add_edges; } - - /** - * @brief Begin of edges added to make graph biconnected. - * - * @return begin of additional edges - * @sa biconnectivity::make_biconnected - */ - edges_t::iterator additional_begin() - { return additional.begin (); } - - /** - * @brief End of edges added to make graph biconnected - * - * @return end of additional edges - * @sa biconnectivity::make_biconnected - */ - edges_t::iterator additional_end() - { return additional.end (); } - - /** - * @internal - */ - typedef nodes_t::iterator cutpoint_iterator; - - /** - * @brief Start iteration over all cutpoints found. - * - * A cutpoints is a node whose removal will disconnect the graph, - * thus a graph with no cutpoints is biconnected and vice versa. - * - * @return iterator to first cutpoint. - * @sa biconnectivity::cut_points_end - */ - cutpoint_iterator cut_points_begin () - { return cut_points.begin(); } - - /** - * @brief End of iteration over all cutpoints. - * - * @return one-past-the-end iterator. - * @sa biconnectivity::cut_points_begin - */ - cutpoint_iterator cut_points_end () - { return cut_points.end(); } - - - /** - * @internal - */ - typedef std::list >::iterator component_iterator; - - /** - * @brief Start iteration over all biconnected components (if enabled during - * last call to run). - * - * Components are represented as a pair consisting of - * a list of nodes and a list of edges, - * i.e. if it is of type component_iterator - * then *it is of type - * pair<list<node>,list<edge> >. - * - * @return iterator to first component - * @sa biconnectivity::store_components - */ - component_iterator components_begin () - { return components.begin(); } - - - /** - * @brief End of iteration over all biconnected components. - * - * @return end of iteration over biconnected components - * @sa biconnectivity::store_components - */ - component_iterator components_end () - { return components.end(); } - - /** - * @brief Number von biconnected components detected during the last run. - * - * @return number of biconnected components. - */ - int number_of_components () const - {return num_of_components; } - - //----------------------------------------------------------------------- - // Handler used to extend dfs to biconnectivity - //----------------------------------------------------------------------- - /** - * @internal - */ - virtual void init_handler (graph&); - - /** - * @internal - */ - virtual void entry_handler (graph&, node&, node&); - - /** - * @internal - */ - virtual void before_recursive_call_handler (graph&, edge&, node&); - - /** - * @internal - */ - virtual void after_recursive_call_handler (graph&, edge&, node&); - - /** - * @internal - */ - virtual void old_adj_node_handler (graph&, edge&, node&); - - /** - * @internal - */ - virtual void new_start_handler (graph&, node&); - - /** - * @internal - */ - virtual void leave_handler (graph&, node&, node&); - - /** - * @internal - */ - virtual void end_handler (graph&); - - -protected: - /** - * @internal - */ - edges_t self_loops; - - /** - * @internal - */ - node_map in_component; - - /** - * @internal - */ - node_map low_num; - /** - * @internal - */ - int num_of_components; - /** - * @internal - */ - bool store_comp; - /** - * @internal - */ - bool add_edges; - /** - * @internal - */ - node last; - /** - * @internal - */ - std::stack node_stack; - /** - * @internal - */ - std::stack edge_stack; - /** - * @internal - */ - std::list > components; - /** - * @internal - */ - nodes_t cut_points; - /** - * @internal - */ - node_map cut_count; - /** - * @internal - */ - edges_t additional; - /** - * @internal - */ - node_map first_child; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_BICONNECTIVITY_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/bid_dijkstra.h b/Tracker/graph/GTL/include/GTL/bid_dijkstra.h deleted file mode 100644 index 603449eff..000000000 --- a/Tracker/graph/GTL/include/GTL/bid_dijkstra.h +++ /dev/null @@ -1,387 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// bid_dijkstra.h -// -//========================================================================== -// $Id: bid_dijkstra.h,v 1.3 2003/03/24 15:58:54 raitner Exp $ - -#ifndef GTL_BID_DIJKSTRA_H -#define GTL_BID_DIJKSTRA_H - -#include -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * $Date: 2003/03/24 15:58:54 $ - * $Revision: 1.3 $ - * - * @brief Dijkstra's Algorithm for computing a shortest path from a single - * source to a single target. - * - * This class implements Dijkstra's algorithm in a bidirectional manner for - * computing a shortest path from a single source to a single target in - * \f$\mathcal{O}((|V| + |E|) log |V|)\f$ worst case. - * - * @sa dijkstra - * @sa bellman_ford - * - * @author Christian Bachmaier chris@infosun.fmi.uni-passau.de - */ -class GTL_EXTERN bid_dijkstra : public algorithm -{ -public: - /** - * @brief Iterator type for traversing %nodes on one shortest path. - */ - typedef nodes_t::const_iterator shortest_path_node_iterator; - - /** - * @brief Iterator type for traversing %edges on one shortest path. - */ - typedef edges_t::const_iterator shortest_path_edge_iterator; - - /** - * @internal - */ - enum node_color {white, grey, black}; - - /** - * @brief Default constructor. - * - * Enables only the calculation of shortest paths. - * - * @sa algorithm::algorithm - */ - bid_dijkstra(); - - /** - * @brief Destructor. - * - * @sa algorithm::~algorithm - */ - virtual ~bid_dijkstra(); - - /** - * @brief Sets source and target %node. - * - * Must be executed every time before check and run of this %algorithm. - * - * @param s source %node - * @param t target %node - */ - void source_target(const node& s, const node& t); - - /** - * @brief Sets weights of the edges. - * - * This method @b must be called before check and run. - * - * @param weight weights of the %edges - */ - void weights(const edge_map& weight); - - /** - * @brief Enables or disables the storing of the shortest path. - * - * If enabled for every %node and edge on the shortest path from source - * to target will be stored. - * - * @param set true if path should be stored - * - * @sa dijkstra::predecessor_node - * @sa dijkstra::predecessor_edge - */ - void store_path(bool set); - - /** - * @brief Checks whether the preconditions for bidirectional Dijkstra are - * satisfied. - * - * The Precondition are that the weights of the edges have been set and - * that the graph has at least one %node. Additionally all %edge weights - * must be \f$\ge 0\f$ and and source and target %nodes must be found in - * @p G. - * - * @param G graph - * - * @retval algorithm::GTL_OK if %algorithm can be applied - * @retval algorithm::GTL_ERROR otherwise - * - * @sa dijkstra::source - * @sa dijkstra::weigths - * @sa algorithm::check - */ - virtual int check(graph& G); - - /** - * @brief Runs shortest path algorithm on @p G. - * - * This should return always algorithm::GTL_OK. The return value only - * tracks errors that might occur. - * Afterwards the result of the test can be accessed via access methods. - * - * @param G graph - * - * @retval algorithm::GTL_OK on success - * @retval algorithm::GTL_ERROR otherwise - * - * @sa algorithm::run - */ - int run(graph& G); - - /** - * @brief Returns source %node. - * - * @return source %node - */ - node source() const; - - /** - * @brief Returns target %node if set, node::node() else. - * - * @return target %node - */ - node target() const; - - /** - * @brief Returns whether the storing of the shortest path is enabled. - * - * @return @c true iff the storing of path is enabled. - * - * @sa dijkstra::predecessor - */ - bool store_path() const; - - /** - * @brief Returns whether target is reachable from source. - * - * @return @c true iff target was reached from source - */ - bool reached() const; - - /** - * @brief Returns the distance from source %node to target %node. - * - * @return distance if target is bid_dijkstra::reached, -1.0 - * else - */ - double distance() const; - - /** - * @brief Returns an iterator to the beginning (to the source %node) of - * the shortest %node path to target %node. - * - * @return beginning %node iterator of the shortest path - * - * @sa bid_dijkstra::store_path - * - * @note The method requires that path calculation option was - * enabled during last run. - */ - shortest_path_node_iterator shortest_path_nodes_begin(); - - /** - * @brief Returns an iterator one after the end (one after target - * %node) of the shortest %node path to target %node. - * - * @return shortest path end %node iterator - * - * @sa bid_dijkstra::store_path - * - * @note The method requires that path calculation option was - * enabled during last run. - */ - shortest_path_node_iterator shortest_path_nodes_end(); - - /** - * @brief Returns an iterator to the beginning %edge of the shortest - * %edge path to target %node. - * - * @sa bid_dijkstra::store_path - * - * @return beginning %edge iterator of the shortest path - * - * @note The method requires that path calculation option was - * enabled during last run. - */ - shortest_path_edge_iterator shortest_path_edges_begin(); - - /** - * @brief Returns an iterator one after the end of a shortest %edge path - * to target %node. - * - * @sa bid_dijkstra::store_path - * - * @return shortest path end %edge iterator - * - * @note The method requires that predecessor calculation option was - * enabled during last run. - */ - shortest_path_edge_iterator shortest_path_edges_end(); - - /** - * @brief Resets Dijkstra's bidirectional algorithm. - * - * It prepares the algorithm to be applied again, possibly to another - * graph. - * - * @note The weights are not reset. You can apply this algorithms - * - * @sa algorithm::reset - */ - virtual void reset(); -private: - /** - * @internal - * Stores source. - * - * @sa bid_dijkstra::source. - */ - node s; - - /** - * @internal - * Stores target. - * - * @sa bid_dijkstra::source. - */ - node t; - - /** - * @internal - * Indicates whether weights were set. - * - * @sa bid_dijkstra::weights. - */ - bool weights_set; - - /** - * @internal - * Indicates whether predecessors should be computed. - * - * @sa bid_dijkstra::store_preds. - */ - bool path_set; - - /** - * @internal - * Stores the weights of the %edges. - * - * @sa bid_dijkstra::weights. - */ - edge_map weight; - - /** - * @internal - * Stores distance between @s and @t. - * (default: -1.0) - */ - double dist; - - /** - * @internal - * Stores if @a t can be reached from @s. - * (default: false) - */ - bool reached_t; - - /** - * @internal - * Stores predecessor of each %node in shortest path. - * (default: edge() (if enabled)) - */ - node_map pred; - - /** - * @internal - * Stores successor of each %node in shortest path tree. - * (default: edge() (if enabled)) - */ - node_map succ; - - /** - * @internal - * Indicates the current %node status. - * (default: black) - */ - node_map source_mark; - - /** - * @internal - * Indicates the current %node status. - * (default: black) - */ - node_map target_mark; - - /** - * @internal - * Distance from source @a s. - * (default: -1.0) - */ - node_map source_dist; - - /** - * @internal - * Distance to target @a t. - * (default: -1.0) - */ - node_map target_dist; - - /** - * @internal - * Stores for target %node @a t a list of nodes on the shortest path - * from source @a s to it. - * (default: empty) - * - * @sa dijkstra::shortest_path_nodes_begin - * @sa dijkstra::shortest_path_nodes_end - */ - nodes_t shortest_path_node_list; - - /** - * @internal - * Stores for target %node @a t a list of edges on the shortest path - * from source @a s to it. - * (default: empty) - * - * @sa dijkstra::shortest_path_edges_begin - * @sa dijkstra::shortest_path_edges_end - */ - edges_t shortest_path_edge_list; - - /** - * @internal - * Prepares the %algorithm to be applied once again. - */ - void reset_algorithm(); - - /** - * @internal - * Inits data structure. - */ - void init(graph& G); - - /** - * @internal - * Fills ordered lists @a shortest_path_node_list and @a - * shortest_path_edge_list with nodes respective edges of shortest path - * from @a s to @a t. Calculates distance. - * - * @param n first white node of the two directions - */ - void fill_node_edge_lists(const node& n); -}; - -__GTL_END_NAMESPACE - -#endif // GTL_BID_DIJKSTRA_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/bin_heap.h b/Tracker/graph/GTL/include/GTL/bin_heap.h deleted file mode 100644 index 682e65780..000000000 --- a/Tracker/graph/GTL/include/GTL/bin_heap.h +++ /dev/null @@ -1,425 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// bin_heap.h -// -//========================================================================== -// $Id: bin_heap.h,v 1.10 2003/01/07 07:01:05 chris Exp $ - -#ifndef GTL_BIN_HEAP_H -#define GTL_BIN_HEAP_H - -#include - -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @internal - * Node type of container. - */ -template -class heap_node -{ -public: - /** - * @internal - * Default constructor. - */ - heap_node() - { - } - - /** - * @internal - */ - heap_node(const T& n) : data(n) - { - } - - /** - * @internal - * Data member. - */ - T data; - - /** - * @internal - * Position in container. - */ - int pos; -}; - - -/** - * @brief Binary heap. - * - * @author Christian Bachmaier chris@infosun.fmi.uni-passau.de - */ -template -class bin_heap -{ -public: - /** - * @brief Creates empty binary heap. - * - * @param prd binary predicate to compare two Ts - */ - bin_heap(const Pred& prd); - - /** - * @brief Creates empty binary heap. - * - * @param prd binary predicate to compare two @c Ts - * @param est_size estimated maximal size of heap - */ - bin_heap(const Pred& prd, const int est_size); - - /** - * @brief Copy constructor. - * - * @param bh binary heap to copy - */ - bin_heap(const bin_heap& bh); - - /** - * @brief Assigns @c bh to this binary heap. - * - * All elements in this heap will be deleted. The predicate of this heap - * must be physically the same as the one of @p bh. - * - * @param bh binary heap - * - * @return this heap - */ - bin_heap& operator=(const bin_heap& bh); - - /** - * @brief Destructor. - */ - ~bin_heap(); - - /** - * @brief Inserts @p ins in heap. - * - * @param ins data element to be inserted - */ - void push(const T& ins); - - /** - * @brief Removes the element on top of the heap. - */ - void pop(); - - /** - * @brief Returns a reference to the element at the top of the heap. - * - * @return top element of the heap - */ - const T& top() const; - - /** - * @brief Reconstructs heap condition after changing key value of @p - * cha externally. - * - * @param cha element with changed key value - * - * @note @c changeKey doesn't operate if @p cha is a primitive data - * structure, because it represents its key value itself, or if one - * object is stored more than once in the data structure. - * - * @sa dijkstra - */ - void changeKey(const T& cha); - - /** - * @brief Checks if heap is empty. - * - * @return @c true iff empty - */ - bool is_empty() const; - - /** - * @internal - * Makes heap empty. - */ - void clear(); -private: - /** - * @internal - * Binary predicate to compare two T's. - */ - const Pred& prd; - - /** - * @internal - * Next free position in @a container. - */ - int size; - - /** - * @internal - * Estimated maximum size of @a container. Initially set to estimated - * size of user in constructor #bin_heap. - */ - int capacity; - - /** - * @internal - * Data container. - */ - std::vector* > container; - - /** - * @internal - * Mapping between data member T and its heap_node. - */ - std::map* > heap_node_map; - - /** - * @internal - * Reconstructs heap condition with bubbling up heap_node @p n. - */ - void bubble_up(heap_node* const n); - - /** - * @internal - * Reconstructs heap condition with bubbling down heap_node @p n. - */ - void bubble_down(heap_node* const n); -#ifdef _DEBUG -public: - /** - * @internal - * Prints @a container for debug purposes. - */ - void print_data_container(); -#endif // _DEBUG -}; - -// Implementation Begin - -template -bin_heap::bin_heap(const Pred& prd) : - prd(prd), size(0), capacity(50) -{ - container.resize(capacity); -} - - -template -bin_heap::bin_heap(const Pred& prd, const int est_size) : - prd(prd), size(0), capacity(50) -{ - if (est_size > 50) - { - capacity = est_size; - } - container.resize(capacity); -} - - -template -bin_heap::bin_heap(const bin_heap& bh) : - prd(bh.prd), size(bh.size), capacity(bh.capacity) -{ - container.resize(capacity); - for (int i = 0; i < size; ++i) - { - container[i] = new heap_node(bh.container[i]->data); - } -} - - -template -bin_heap& bin_heap::operator=(const bin_heap& bh) -{ - if (this != &bh) // no self assignment - { - assert(&prd == &(bh.prd)); - clear(); - size = bh.size; - capacity = bh.capacity; - container.resize(capacity); - for (int i = 0; i < size; ++i) - { - container[i] = new heap_node(bh.container[i]->data); - } - } - return *this; -} - - -template -bin_heap::~bin_heap() -{ - clear(); -} - - -template -void bin_heap::push(const T& ins) -{ - if (size == capacity) - { - // dynamic memory allocation - capacity *= 2; - container.resize(capacity); - } - heap_node* n = new heap_node(ins); - n->pos = size; - container[size] = n; - heap_node_map[ins] = n; - ++size; - bubble_up(n); -} - - -template -void bin_heap::pop() -{ - assert(size > 0); - // save smallest element for return (ensured by heap condition) - heap_node_map.erase(container[0]->data); - delete container[0]; - // replace by last element in array and decrease heap "size" - if (size > 1) - { - container[0] = container[--size]; - container[0]->pos = 0; - // reorder heap to ensure heap conditions - bubble_down(container[0]); - } - else - { - size = 0; - } -} - - -template -const T& bin_heap::top() const -{ - return container[0]->data; -} - - -template -void bin_heap::changeKey(const T& cha) -{ - int pos = heap_node_map[cha]->pos; - heap_node* n = container[pos]; - if (pos != 0) - { - heap_node* father = container[(pos - 1) / 2]; - if (prd(n->data, father->data)) - { - bubble_up(n); - return; - } - } - bubble_down(n); -} - - -template -bool bin_heap::is_empty() const -{ - // empty if if first free index is 0 - return size == 0; -} - - -template -void bin_heap::clear() -{ - for (int i = 0; i < size; ++i) - { - delete container[i]; - } - size = 0; - heap_node_map.clear(); -} - - -template -void bin_heap::bubble_up(heap_node* const n) -{ - int pos = n->pos; - // if we are not already at top AND the parent in heap is more - while ((pos != 0) && - (prd(n->data, container[(pos - 1) / 2]->data))) - { - // move father down - container[pos] = container[(pos - 1) / 2]; - container[pos]->pos = pos; - // increment k to parent index - pos = (pos - 1) / 2; - } - // place value in its highest position in heap - container[pos] = n; - container[pos]->pos = pos; -} - - -template -void bin_heap::bubble_down(heap_node* const n) -{ - int pos = n->pos; - int j = 0; - while (pos < size / 2) - { - j = 2 * pos + 1; - // if right child is smaller than left child get right child - if ((j < size - 1) && - (prd(container[j + 1]->data, container[j]->data))) - { - ++j; - } - // if element is less or equal than its child leave it here - if (!prd(container[j]->data, n->data)) - { - break; - } - // else move its child up - container[pos] = container[j]; - container[pos]->pos = pos; - // repeat for new position - pos = j; - } - // place element into position, where heap condition is fulfilled - container[pos] = n; - container[pos]->pos = pos; -} - -#ifdef _DEBUG -template -void bin_heap::print_data_container() -{ - if (size == 0) - { - cout << "empty"; - } - else - { - for (int pos = 0; pos < size; ++pos) - { - cout << container[pos]->data << " "; - } - } - cout << endl; -} -#endif // _DEBUG - -// Implementation End - -__GTL_END_NAMESPACE - -#endif // GTL_BIN_HEAP_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/components.h b/Tracker/graph/GTL/include/GTL/components.h deleted file mode 100644 index df8e3f775..000000000 --- a/Tracker/graph/GTL/include/GTL/components.h +++ /dev/null @@ -1,133 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// components.h -// -//========================================================================== -// $Id: components.h,v 1.5 2003/04/03 11:44:42 raitner Exp $ - -#ifndef GTL_COMPONENTS_H -#define GTL_COMPONENTS_H - -#include -#include - -#include - -__GTL_BEGIN_NAMESPACE -/** - * @brief Connected components algorithm - */ -class GTL_EXTERN components : public dfs -{ -public: - /** - * @brief Creates connected components algorithm object. - * - * @sa dfs::dfs - */ - components (); - - /** - * @brief Destroys connected components algorithm object. - * - * @sa dfs::~dfs - */ - virtual ~components () {} - - /** - * @brief Checks whether the connected components algorithm can be applied - * - * Necessary preconditions: - * - G is undirected. - * - scanning of whole graph is enabled. - * - DFS may be applied - * - * @param G graph. - * @return algorithm::GTL_OK if connected components can be computed for G. - * @sa dfs::scan_whole_graph - */ - virtual int check (graph& G); - - virtual void reset (); - - /** - * @internal - */ - typedef std::list >::iterator component_iterator; - - /** - * @brief Start iteration over all components (if enabled during - * last call to run). - - * Components are represented as a pair consisting of - * a list of nodes and a list of edges, - * i.e. if @c it is of type @c component_iterator - * then @c *it is of type - * @c pair<list<node>,list<edge> >. - * - * @return iterator to first component - */ - component_iterator components_begin () - { return comp.begin(); } - - - /** - * @brief End of iteration over all components. - * - * @return end of iteration over biconnected components - * @sa biconnectivity::store_components - */ - component_iterator components_end () - { return comp.end(); } - - /** - * @brief Number of components detected during the last run. - * - * @return number of components. - */ - int number_of_components () const - {return num_of_components; } - - //----------------------------------------------------------------------- - // Handler used to extend dfs to biconnectivity - //----------------------------------------------------------------------- - /** - * @internal - */ - virtual void before_recursive_call_handler (graph&, edge&, node&); - - /** - * @internal - */ - virtual void old_adj_node_handler (graph&, edge&, node&); - - /** - * @internal - */ - virtual void new_start_handler (graph&, node&); - - -protected: - - /** - * @internal - */ - int num_of_components; - /** - * @internal - */ - std::list > comp; - /** - * @internal - */ - component_iterator li; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_BICONNECTIVITY_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/debug.h b/Tracker/graph/GTL/include/GTL/debug.h deleted file mode 100644 index ea0ae6696..000000000 --- a/Tracker/graph/GTL/include/GTL/debug.h +++ /dev/null @@ -1,48 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// debug.h - Functions, which are useful for debugging -// -//========================================================================== -// $Id: debug.h,v 1.8 2001/10/10 08:30:00 chris Exp $ - -#ifndef GTL_DEBUG_H -#define GTL_DEBUG_H - -#include - -#include - -__GTL_BEGIN_NAMESPACE - -// -// If _DEBUG is defined the funtions defined here will produce output. -// You can either define _DEBUG here (or undef it) or you can set it as -// option of your compiler. -// -//#define _DEBUG 1 -//#undef _DEBUG -// - -/** - * @internal - */ -class GTL_EXTERN GTL_debug { -public: - static void debug_message (const char*, ...); - static void init_debug(); - static void close_debug(); - static std::ostream& os() - { return *GTLerr; } - -private: - static std::ostream* GTLerr; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_DEBUG_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/dfs.h b/Tracker/graph/GTL/include/GTL/dfs.h deleted file mode 100644 index a95d50cca..000000000 --- a/Tracker/graph/GTL/include/GTL/dfs.h +++ /dev/null @@ -1,571 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// dfs.h -// -//========================================================================== -// $Id: dfs.h,v 1.25 2003/03/24 15:58:54 raitner Exp $ - -#ifndef GTL_DFS_H -#define GTL_DFS_H - -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * $Date: 2003/03/24 15:58:54 $ - * $Revision: 1.25 $ - * - * @brief Depth-First-Search (DFS) %algorithm - * - * Encapsulates the DFS %algoritm together with all the data - * produced by a run of DFS. Since there exits so much different - * things which one might want to calculate during a DFS this - * class provides basically two different customization - * features. First it is possible to take influence on the - * behaviour of this %algortihm by changing some of the following - * options: - * - dfs::start_node - * (default: an arbitrary %node will be chosen) - * - dfs::scan_whole_graph states whether BFS will be - * continued in the unused part of the %graph, if not all - * nodes were touched at the end of DFS started at the start-%node. - * (default: disabled) - * - dfs::calc_comp_num toggle storing of completion-numbers - * for each %node, i.e. a numbering which reflects the order in which - * nodes were @em finished. (default: disabled) - * - dfs::store_preds toggle storing the predecessor of each - * %node, i.e. the father in DFS-tree. (default: disabled) - * - dfs::store_non_tree_edges toggle storing of all non-tree-edges - * (tree-edges are always stored) in a list and thus enable or disable - * iteration through all non-tree-edges. - * (default: disabled) - * - * But the trouble with most DFS-%algorithm is that one always - * wants to add a little bit of code somewhere in the - * %algorithm. And then there are only two ways to get this - * done. The more efficient one (in terms of runtime) is to - * implement the DFS anew and add the new code where - * necessary. The other way (which is more efficient in terms of - * code-writing) is to take the %algorithm as provided and run - * through the list of nodes it returns (resulting in an extra - * factor of 2). - * - * Our DFS-%algoritm class provides a new method to add small - * pieces of code to the %algorithm: Handler. These are virtual - * functions called at well-defined, important states of the - * %algorithm (e.g. before a new recursive call). So the only - * thing to do is to derive your extended DFS from this class and - * to override the handlers where needed. In detail there are the - * following handler supported (have a look at the source code - * for details): - * - dfs::init_handler - * - dfs::end_handler - * - dfs::entry_handler - * - dfs::leave_handler - * - dfs::before_recursive_call_handler - * - dfs::after_recursive_call_handler - * - dfs::old_adj_node_handler - * - dfs::new_start_handler - * - * @em Please @em note: We do @em not claim that this set of handlers - * is sufficient in any way. So if you believe that some new handler is - * needed urgently please let us know. - * - * There is a lot of information stored during DFS (e.g. nodes in - * dfs-order, list of non-tree-edges). Some of it can be obtained directly - * by using the corresponding member-function (e.g. dfs::dfs_num), - * but all information that can be thought of as a list (e.g. nodes in - * dfs-order) can be accessed through iterators. In detail these are (of - * course depending on what options are chosen!): - * - dfs::dfs_iterator - * - dfs::tree_edges_iterator - * - dfs::non_tree_edges_iterator - * - dfs::roots_iterator - */ -class GTL_EXTERN dfs : public algorithm -{ -public: - /** - * @brief Constructor. - */ - dfs (); - - - /** - * @brief Destructor. - */ - virtual ~dfs (); - - int run (graph& G); - - /** - * @brief Checks whether the preconditions for DFS are - * satisfied. - * - * Currently there aren't any restricitions for the DFS - * %algorithm. - * - * @param G graph. - * @retval algorithm::GTL_OK if %algorithm can be applied - * @retval algorithm::GTL_ERROR otherwise. - */ - virtual int check (graph& G); - - virtual void reset (); - - - //--------------------------------------------------------------------- - // Parameters - //--------------------------------------------------------------------- - - /** - * @brief Sets start-%node for DFS. - * - * @param n start-node. - */ - void start_node (const node& n) - { start = n; } - - /** - * @brief Returns start-%node for DFS. - * - * @return start-%node. - */ - node start_node () const {return start;} - - /** - * @brief Enables or disables scanning of the whole %graph. - * - * If enabled and the DFS started at the given start-%node - * stops without having touched all nodes, it will be - * continued with the next unused %node, and so on until all - * nodes were used. This makes sure that for every %node - * #dfs_number is defined. - * - * On the other hand, if this feature is disabled, one - * will be able to check what nodes can be reached, when - * starting a DFS at the start-%node, because for those not - * reached #dfs_number will be 0. - * - * @param set if true enable scanning the whole graph. - * @sa dfs::roots_begin - * @sa dfs::roots_end - */ - void scan_whole_graph (bool set) {whole_graph = set;} - - /** - * @brief Returns true iff the whole graph will be scanned. - * - * @retval true iff the whole graph will be scanned. - * @sa dfs::roots_begin - * @sa dfs::roots_end - */ - bool scan_whole_graph () const {return whole_graph;} - - /** - * @brief Enables or Disables the calculation of the completion number. - * - * @param set if true completion-numbers will be calculated. - * @sa dfs::comp_num - */ - void calc_comp_num (bool set); - - /** - * @brief Returns true iff completion-numbers will be calculated. - * - * @retval true iff completion-numbers will be calculated. - * @sa dfs::comp_num - */ - bool calc_comp_num () const {return comp_number != 0;} - - - /** - * @brief Enables or disables the storing of predecessors. - * - * If enabled for every %node the predecessor in DFS will be - * stored. - * - * @param set if true predecessors will be stored. - * @sa dfs::father - */ - void store_preds (bool set); - - /** - * @brief Returns true iff the storing of predecessors is enabled. - * - * @retval true iff the storing of predecessors is enabled. - * @sa dfs::father - */ - bool store_preds () const {return preds != 0;} - - /** - * @brief Enables the storing of back-edges. - * - * If enabled the list of non-tree-edges can be traversed in - * the order they occured using #non_tree_edges_iterator. - * - * @param set if true non_tree_edges will be stored. - * @sa dfs::non_tree_edges_begin - * @sa dfs::non_tree_edges_end - */ - void store_non_tree_edges (bool set); - - /** - * @brief Returns true iff the storing of non-tree-edges is enabled. - * - * @return true iff the storing of non-tree-edges is enabled. - * @sa dfs::non_tree_edges_begin - * @sa dfs::non_tree_edges_end - */ - bool store_non_tree_edges () const {return back_edges != 0;} - - //--------------------------------------------------------------------- - // Access - //---------------------------------------------------------------------- - - /** - * @brief Checks whether %node @a n was reached in last DFS. - * - * @param n %node to be checked. - * @return true iff @a n was reached. - */ - bool reached (const node& n) const - {return dfs_number[n] != 0;} - - /** - * @brief DFS-Number of @a n. - * - * Please note that DFS-Number 0 means that this %node wasn't - * reached. - * - * @param n %node. - * @return DFS-Number of @a n. - */ - int dfs_num (const node& n) const - {return dfs_number[n];} - - /** - * @brief DFS-Number of @a n. - * - * Please note that DFS-Number 0 means that this %node wasn't - * reached. - * - * @param n %node. - * @return DFS-Number of @a n. - */ - int operator[] (const node& n) const - {return dfs_number[n];} - - /** - * @brief Completion-number of %node @a n, if enabled in last - * run. - * - * @param n %node. - * @return Completion-number of @a n. - * @sa dfs::calc_comp_num - */ - int comp_num (const node& n) const - {assert (comp_number); return (*comp_number)[n];} - - /** - * @brief Returns father of node @a n in DFS-forest. - * - * If @a n is a root in the forest or wasn't reached the - * return value is @c node(). - * - * @param n %node. - * @return Father of @a n. - * @sa dfs::store_preds - */ - node father (const node& n) const - {assert (preds); return (*preds)[n];} - - /** - * @brief Iterator for the tree edges of the DFS-tree. - */ - typedef edges_t::const_iterator tree_edges_iterator; - - /** - * @brief Iterate through all edges picked in last DFS. - * - * Please note that this edges not always form a tree. In - * case the %graph is not (strongly) connected they form a - * forest. - * - * @return start for iteration through all edges followed in DFS. - */ - tree_edges_iterator tree_edges_begin () const - {return tree.begin();} - - /** - * @brief End-iterator for iteration through all edges picked in last DFS. - * - * @return end for iteration through all edges followed in DFS. - */ - tree_edges_iterator tree_edges_end () const - {return tree.end();} - - /** - * @brief Iterator for the (reached) nodes in DFS-order. - */ - typedef nodes_t::const_iterator dfs_iterator; - - /** - * @brief Iterate through all (reached) nodes in DFS-order. - * - * @return start for iteration through all nodes in DFS-order. - */ - dfs_iterator begin () const - {return dfs_order.begin();} - - /** - * @brief End-Iterator for iteration through all (reached) - * nodes in DFS-order. - * - * @return end for iteration through all (reached) nodes - */ - dfs_iterator end () const - {return dfs_order.end();} - - /** - * @brief Iterator for the non-tree-edges - */ - typedef edges_t::const_iterator non_tree_edges_iterator; - - /** - * @brief Iterate through all non-tree-edges (if enabled). - * - * @return start for iteration through all non-tree-edges. - * @sa dfs::store_non_tree_edges - */ - non_tree_edges_iterator non_tree_edges_begin () const - {assert (back_edges); return back_edges->begin(); } - - /** - * @brief End-iterator for iteration through all - * non-tree-edges (if enabled). - * - * @return end for iteration through all non-tree-edges. - * @sa dfs::store_non_tree_edges - */ - non_tree_edges_iterator non_tree_edges_end () const - {assert (back_edges); return back_edges->end(); } - - /** - * @brief Iterator for the roots of the DFS-forest. - */ - typedef std::list::const_iterator roots_iterator; - - /** - * @brief Iterator pointing towards the first root in the DFS-forest. - * - * Please note that intstead of pointing directly - * towards the node (i.e. @c *it is of type node) the - * iterator points towards a #dfs_iterator, which represents - * the root (i.e. @c *it is of type #dfs_iterator). - * - * Using this technique makes it possible not only to obtain - * all the roots in the forest, but also the whole trees - * associated with each one. This can be achieved because a - * #root_iterator specifies the exact position of the root in - * the DFS-ordering and by definition of DFS all the - * descendents of the root, i.e. the whole tree, will come - * later in DFS, such that by incrementing the #dfs_iterator, - * a #roots_iterator points at, one can traverse the whole - * tree with this given root. - * - * Of course if the root isn't the last node in the - * DFS-forest on will also traverse all following trees, but - * since the first node of such a tree one will discover is - * its root, the successor of the #roots_iterator can be used - * as end-iterator. - * - * @return start for iteration through all roots in DFS-forest. - * @sa dfs::scan_whole_graph - */ - roots_iterator roots_begin () const - {return roots.begin();} - - /** - * @brief Iterator pointing to the end of all roots. - * - * @return end for iteration through all roots in DFS-forest. - * @sa dfs::scan_whole_graph - */ - roots_iterator roots_end () const - {return roots.end();} - - /** - * @brief Number of nodes reached in last DFS. - * - * @return number of reached nodes. - * @sa dfs::scan_whole_graph - */ - int number_of_reached_nodes () const - {return reached_nodes;} - - - //----------------------------------------------------------------------- - // Handler - for customization purposes - //----------------------------------------------------------------------- - - /** - * @brief Handler called before the start of DFS. - * - * @param G %graph for which DFS was invoked. - */ - virtual void init_handler (graph& /*G*/) {} - - /** - * @brief Handler called at the end of DFS. - * - * @param G %graph for which DFS was invoked. - */ - virtual void end_handler (graph& /*G*/) {} - - /** - * @brief Handler called when touching %node @a n. - * - * @param G %graph for which DFS was invoked. - * @param n actual %node. - * @param f predecessor. - */ - virtual void entry_handler (graph& /*G*/, node& /*n*/, node& /*f*/) {} - - /** - * @brief Handler called after all the adjacent edges of @a n - * have been examined. - * - * @param G %graph for which DFS was invoked. - * @param n actual %node. - * @param f predecessor. - */ - virtual void leave_handler (graph& /*G*/, node& /*n*/, node& /*f*/) {} - - /** - * @brief Handler called when a unused %node @a n connected to the - * actual %node by @a e is found. - * - * @param G %graph for which DFS was invoked. - * @param e %edge connecting the actual %node to the unused one. - * @param n unused %node. - */ - virtual void before_recursive_call_handler (graph& /*G*/, edge& /*e*/, node& /*n*/) {} - - /** - * @brief Handler called after the %algorithm returns from the - * subtree starting at @a n connected to the actual %node by - * @a e. - * - * @param G %graph for which DFS was invoked. - * @param e %edge connecting the actual %node to the unused one. - * @param n unused %node. - */ - virtual void after_recursive_call_handler (graph& /*G*/, edge& /*e*/, node& /*n*/) {} - - /** - * @brief Handler called when a already marked %node @a n connected - * to the actual %node by @a e is found during the search of all - * adjacent edges of the actual %node. - * - * @param G %graph for which DFS was invoked. - * @param e %edge connecting the actual %node to the old one. - * @param n used %node. - */ - virtual void old_adj_node_handler (graph& /*G*/, edge& /*e*/, node& /*n*/) {} - - /** - * @brief Called when DFS is started with start-%node @a - * n. - * - * This is particularly useful when DFS was invoked with the - * #scan_whole_graph option. - * - * @param G %graph for which DFS was invoked. - * @param n start-%node. - */ - virtual void new_start_handler (graph& /*G*/, node& /*n*/) { }; - -private: - - /** - * @internal - */ - void dfs_sub (graph&, node&, node&); - -protected: - - //---------------------------------------------------------------------- - // Data - //---------------------------------------------------------------------- - - /** - * @internal - */ - int act_dfs_num; - /** - * @internal - */ - int act_comp_num; - /** - * @internal - */ - edges_t tree; - /** - * @internal - */ - nodes_t dfs_order; - /** - * @internal - */ - node_map dfs_number; - /** - * @internal - */ - int reached_nodes; - /** - * @internal - */ - edge_map* used; - /** - * @internal - */ - std::list roots; - - - //----------------------------------------------------------------------- - // Optional - //----------------------------------------------------------------------- - - /** - * @internal - */ - node_map* comp_number; - /** - * @internal - */ - node_map* preds; - /** - * @internal - */ - edges_t* back_edges; - /** - * @internal - */ - node start; - /** - * @internal - */ - bool whole_graph; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_DFS_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/dijkstra.h b/Tracker/graph/GTL/include/GTL/dijkstra.h deleted file mode 100644 index 184f99217..000000000 --- a/Tracker/graph/GTL/include/GTL/dijkstra.h +++ /dev/null @@ -1,421 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// dijkstra.h -// -//========================================================================== -// $Id: dijkstra.h,v 1.8 2003/02/25 09:18:19 chris Exp $ - -#ifndef GTL_DIJKSTRA_H -#define GTL_DIJKSTRA_H - -#include -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @brief Dijkstra's Algorithm for computing single source shortest path. - * - * This class implements Dijkstra's algorithm for computing single source - * shortest path in @f$\mathcal{O}((|V| + |E|) log |V|)@f$ worst case. - * - * @sa bellman_ford - * - * @author Christian Bachmaier chris@infosun.fmi.uni-passau.de - */ -class GTL_EXTERN dijkstra : public algorithm -{ -public: - /** - * @brief Iterator type for traversing %nodes on one shortest path. - */ - typedef nodes_t::const_iterator shortest_path_node_iterator; - - /** - * @brief Iterator type for traversing %edges on one shortest path. - */ - typedef edges_t::const_iterator shortest_path_edge_iterator; - - /** - * @internal - */ - enum node_color {white, grey, black}; - - /** - * @brief Default constructor. - * - * Enables only the calculation of shortest paths. - * - * @sa algorithm::algorithm - */ - dijkstra(); - - /** - * @brief Destructor. - * - * @sa algorithm::~algorithm - */ - virtual ~dijkstra(); - - /** - * @brief Sets source %node. - * - * The default source is the invalid %node (node::node()), - * in this case an arbitrary %node is chosen and stored when - * this algorithm is run. - * - * @param n source node - */ - void source(const node& n); - - /** - * @brief Sets target %node. - * - * If a target is set with this method the %algorithm stops if a - * shortest distance to @p n is found. Ohterwise shortest paths are - * computed from source to any %node in the %graph. - * - * @param n target node - */ - void target(const node& n); - - /** - * @brief Sets weights of the edges. - * - * This method @b must be called before check run. - * - * @param weight weights of the %edges - */ - void weights(const edge_map& weight); - - /** - * @brief Enables or disables the storing of predecessors. - * - * If enabled for every %node the predecessor on the shortest - * path from will be stored. - * - * @param set @c true if predecessors should be stored - * - * @sa dijkstra::predecessor_node - * @sa dijkstra::predecessor_edge - */ - void store_preds(bool set); - - /** - * @brief Checks whether the preconditions for Dijkstra are satisfied. - * - * Necessary preconditions are: - * - the weights of the edges are set - * - the %graph @p G has at least one %node - * - all %edge weights must be \f$\ge 0\f$ - * - the source %node and (if set) target %node must be found in @p G - * - * @param G graph - * - * @retval algorithm::GTL_OK if %algorithm can be applied - * @retval algorithm::GTL_ERROR otherwise - * - * @sa dijkstra::source - * @sa dijkstra::weights - * @sa algorithm::check - */ - virtual int check(graph& G); - - /** - * @brief Runs shortest path %algorithm on @p G. - * - * This should return always algorithm::GTL_OK. The return value only - * tracks errors that might occur. - * Afterwards the result of the test can be accessed via access methods. - * - * @param G graph - * - * @retval algorithm::GTL_OK on success - * @retval algorithm::GTL_ERROR otherwise - * - * @sa algorithm::run - */ - int run(graph& G); - - /** - * @brief Returns source %node. - * - * @return source %node - */ - node source() const; - - /** - * @brief Returns target %node if set, node::node() else. - * - * @return target %node - */ - node target() const; - - /** - * @brief Returns whether the storing of predecessors is enabled. - * - * @return @c true iff the storing of predecessors is enabled - * - * @sa dijkstra::predecessor - */ - bool store_preds() const; - - /** - * @brief Returns whether @p n is reachable from source %node. - * - * @param n node - * - * @return @c true iff @p n was reached from source - */ - bool reached(const node& n) const; - - /** - * @brief Returns the distance from source %node to %node @p n. - * - * @param n node - * - * @return distance if @p n is dijkstra::reached, -1.0 else - */ - double distance(const node& n) const; - - /** - * @brief Predecessor %node of %node @p n on the shortest path from the - * source %node. - * - * If @p n is a root or wasn't reached the return value is - * the invalid %node node::node(). - * - * @param n node - * - * @return predecessor %node of @p n - * - * @sa dijkstra::store_preds - * @sa dijkstra::predecessor_edge - * - * @note The method requires that predecessor calculation option was - * enabled during last run. - */ - node predecessor_node(const node& n) const; - - /** - * @brief Predecessor %edge of %node @p n on the shortest path from the - * source %node. - * - * If @p n is a root or wasn't reached the return value is - * the invalid %edge edge::edge(). - * - * @param n node - * - * @return predecessor %edge of @p n - * - * @sa dijkstra::store_preds - * @sa dijkstra::predecessor_node - * - * @note The method requires that predecessor calculation option was - * enabled during last run. - */ - edge predecessor_edge(const node& n) const; - - /** - * @brief Returns an iterator to the beginning (to the source %node) of - * a shortest %node path to %node @p dest. - * - * @param dest target %node - * - * @return beginning %node iterator of a shortest path - * - * @note The method requires that predecessor calculation option was - * enabled during last run. If this method is called on the shortest - * path to @p dest for the first time (before - * dijkstra::shortest_path_nodes_end) it needs - * @f$\mathcal{O}(\mbox{length of this path})@f$ time. - */ - shortest_path_node_iterator shortest_path_nodes_begin(const node& dest); - - /** - * @brief Returns an iterator one after the end (one after - * %node @p dest) of a shortest %node path to %node @p dest. - * - * @param dest target %node - * - * @return shortest path end %node iterator - * - * @note The method requires that predecessor calculation option was - * enabled during last run. If this method is called on the shortest - * path to @p dest for the first time (before - * dijkstra::shortest_path_nodes_begin) it needs - * @f$\mathcal{O}(\mbox{length of this path})@f$ time. - */ - shortest_path_node_iterator shortest_path_nodes_end(const node& dest); - - /** - * @brief Returns an iterator to the beginning %edge of a shortest %edge - * path to %node @p dest. - * - * @param dest target %node - * - * @return beginning %edge iterator of a shortest path - * - * @note The method requires that predecessor calculation option was - * enabled during last run. If this method is called on the shortest - * path to @p dest for the first time (before - * dijkstra::shortest_path_edges_end) it needs - * @f$\mathcal{O}(\mbox{length of this path})@f$ time. - */ - shortest_path_edge_iterator shortest_path_edges_begin(const node& dest); - - /** - * @brief Returns an iterator one after the end of a shortest %edge path - * to %node @p dest. - * - * @param dest target %node - * - * @return shortest path end %edge iterator - * - * @note The method requires that predecessor calculation option was - * enabled during last run. If this method is called on the shortest - * path to @p dest for the first time (before - * dijkstra::shortest_path_edges_begin) it needs - * @f$\mathcal{O}(\mbox{length of this path})@f$ time. - */ - shortest_path_edge_iterator shortest_path_edges_end(const node& dest); - - /** - * @brief Resets Dijkstra's algorithm. - * - * It prepares the algorithm to be applied again, possibly to another - * graph. - * - * @note The weights are not reset. You can apply this algorithms - * - * @sa algorithm::reset - */ - virtual void reset(); -private: - /** - * @internal - * Stores source. - * - * @sa dijkstra::source. - */ - node s; - - /** - * @internal - * Stores target. - * - * @sa dijkstra::source. - */ - node t; - - /** - * @internal - * Indicates whether weights were set. - * - * @sa dijkstra::weights. - */ - bool weights_set; - - /** - * @internal - * Indicates whether predecessors should be computed. - * - * @sa dijkstra::store_preds. - */ - bool preds_set; - - /** - * @internal - * Stores the weights of the %edges. - * - * @sa dijkstra::weights. - */ - edge_map weight; - - /** - * @internal - * Stores father of each %node in shortest path tree (if enabled). - * (default: edge() (if enabled)) - * - * @sa dijkstra::store_preds - */ - node_map pred; - - /** - * @internal - * Indicates the current %node status. - * (default: black) - */ - node_map mark; - - /** - * @internal - * Distance from source @a s. - * (default: -1) - * - * @sa dijkstra::distance. - */ - node_map dist; - - /** - * @internal - * Stores for every target %node a list of nodes on the shortest path - * from source @a s to it. Filled on demand by methods creating - * iterators. - * (default: empty) - * - * @sa dijkstra::shortest_path_nodes_begin - * @sa dijkstra::shortest_path_nodes_end - */ - node_map shortest_path_node_list; - - /** - * @internal - * Stores for every target node a list of edges on the shortest path - * from source @a s to it. Filled on demand by methods creating - * iterators. - * (default: empty) - * - * @sa dijkstra::shortest_path_edges_begin - * @sa dijkstra::shortest_path_edges_end - */ - node_map shortest_path_edge_list; - - /** - * @internal - * Prepares the %algorithm to be applied once again. - */ - void reset_algorithm(); - - /** - * @internal - * Inits data structure. - */ - void init(graph& G); - - /** - * @internal - * Fills ordered list shortest_path_node_list[t] - * with nodes of shortest path from @a s to @p t. - */ - void fill_node_list(const node& t); - - /** - * @internal - * Fills ordered list shortest_path_edge_list[t] - * with edges of shortest path from @a s to @p t. - */ - void fill_edge_list(const node& t); -}; - -__GTL_END_NAMESPACE - -#endif // GTL_DIJKSTRA_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/edge.h b/Tracker/graph/GTL/include/GTL/edge.h deleted file mode 100644 index ee18aead9..000000000 --- a/Tracker/graph/GTL/include/GTL/edge.h +++ /dev/null @@ -1,149 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// edge.h -// -//========================================================================== -// $Id: edge.h,v 1.15 2001/04/17 14:35:25 raitner Exp $ - -#ifndef GTL_EDGE_H -#define GTL_EDGE_H - -#include - -#include -#include - -__GTL_BEGIN_NAMESPACE - -//-------------------------------------------------------------------------- -// For MSVC 5.0 edge.h has to be included before node.h and -// {node,edge}_data.h. So we only declare needed classnames here -//-------------------------------------------------------------------------- - -class node; -typedef std::list nodes_t; - -class edge_data; - -//-------------------------------------------------------------------------- -// edge -//-------------------------------------------------------------------------- - -/** - * @short An edge in a graph - */ -class GTL_EXTERN edge -{ -public: - /** - * Default constructor. Creates an invalid edge. - * The only way to obtain a valid edge is through @ref - * graph#new_edge. Example: - *
-     *   graph g;
-     *   node n1, n2;
-     *   edge e;
-     *
-     *   n1 = g.new_node();
-     *   n2 = g.new_node();
-     *   e = g.new_edge(n1, n2);
-     * 
- * - * @see graph#new_edge - */ - edge(); - - /** - * Returns the source node of the edge. - * - * @return source - */ - node source() const; - - /** - * Returns the target node of the edge. - * - * @return target - */ - node target() const; - const node& target_() const; - - /** - * Changes the direction of this edge. - */ - void reverse (); - - /** - * Makes n the source of this edge. Takes O(1) time. - * - * @param n new source - */ - void change_source (node n); - - /** - * Makes n the target of this edge. Takes O(1) time. - * - * @param n new target - */ - void change_target (node n); - - /** - * Returns the node opposite to n referring to - * this edge. - * - * @param n a node incident to this edge - */ - const node& opposite(node n) const; - - /** - * @internal - */ - nodes_t sources() const; - - /** - * @internal - */ - nodes_t targets() const; - - /** - * @internal - */ - int id() const; - - - /** - * Returns true iff node is hidden. - * - * @return true iff node is hidden. - * @see graph#hide_edge - * @see graph#restore_edge - */ - bool is_hidden () const; - - - //================================================== Implementation - -private: - edge_data *data; - - void remove_from(int where) const; // 0 = sources, 1 == targets - - friend class graph; - friend class node; - - GTL_EXTERN friend bool operator==(edge, edge); - GTL_EXTERN friend bool operator!=(edge, edge); - GTL_EXTERN friend bool operator<(edge, edge); - GTL_EXTERN friend std::ostream& operator<< (std::ostream& os, const edge& e); -}; - -typedef std::list edges_t; - -__GTL_END_NAMESPACE - -#endif // GTL_EDGE_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/edge_data.h b/Tracker/graph/GTL/include/GTL/edge_data.h deleted file mode 100644 index e347b06f3..000000000 --- a/Tracker/graph/GTL/include/GTL/edge_data.h +++ /dev/null @@ -1,42 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// edge_data.h - Internal header: DO NO USE IT DIRECTLY !!! -// -//========================================================================== -// $Id: edge_data.h,v 1.9 2000/02/03 12:49:50 raitner Exp $ - -#ifndef GTL_EDGE_DATA_H -#define GTL_EDGE_DATA_H - -#include -#include -#include - -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @internal - */ -class GTL_EXTERN edge_data -{ -public: - int id; // internal numbering - nodes_t nodes[2]; // nodes[0] = sources, - // nodes[1] = targets - std::list adj_pos[2];// positions in the adjacency lists - // of sources and targets - edges_t::iterator pos; // position in the list of all edges - bool hidden; - graph* owner; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_EDGE_DATA_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/edge_map.h b/Tracker/graph/GTL/include/GTL/edge_map.h deleted file mode 100644 index 42724b8b7..000000000 --- a/Tracker/graph/GTL/include/GTL/edge_map.h +++ /dev/null @@ -1,82 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// edge_map.h -// -//========================================================================== -// $Id: edge_map.h,v 1.8 2005/06/14 12:22:12 raitner Exp $ - -#ifndef GTL_EDGE_MAP_H -#define GTL_EDGE_MAP_H - -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -class graph; - -/** - * @short A specialized map with edges as keys - * - * A edge_map is a specialized and optimized map - * implementation with edges as keys. Using a edge_map is - * the standard way to attach user defined information to - * the edges of a graph. - * - * An example of usage: - *
- *   graph g;
- *
- *   node v1 = g.new_node();
- *   node v2 = g.new_node();
- *   edge e = g.new_edge(v1, v2);
- *
- *   edge_map<string> label(g, "Default Label");
- *
- *   label[e] = "An edge";
- *
- *   assert(label[e] == "An edge");
- * 
- * - * The edges used as keys for a edge_map MUST be edges - * of the same graph. If you want to use edges from different graphs, use - * a map<edge,T> instead. A graph and a copy of it are - * considered to be different. - * - * Most of the functionality of edge_map is inherited from - * @ref ne_map. - * - * @see node_map - */ -template > -class edge_map : public ne_map -{ -public: - - /** - * Constructs an empty edge_map not associated with any - * graph. You may (but need not) call - * ne_map::init(const graph &, T) to associate it to - * a graph. - */ - edge_map() : ne_map() {}; - - /** - * Constructs a edge_map associated to the graph - * g. - * The value associated to each edge in g is set to - * t. - */ - explicit edge_map(const graph &g, T t=T()) : - ne_map(g,t) {}; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_EDGE_MAP_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/embedding.h b/Tracker/graph/GTL/include/GTL/embedding.h deleted file mode 100644 index ae485dd6b..000000000 --- a/Tracker/graph/GTL/include/GTL/embedding.h +++ /dev/null @@ -1,370 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// embedding.h -// -//========================================================================== -// $Id: embedding.h,v 1.20 2003/06/11 11:28:21 raitner Exp $ - -#ifndef __EMBEDDING__H -#define __EMBEDDING__H - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @brief Ordered adjacency lists as a result of planarity testing. - * - * It is known that if a graph is planar the adjacency list of every node - * can be ordered in such a way that it reflects the order the adjacent - * edges will have in a planar drawing around the node. Although the tested - * graph might have been directed the planar embedding one gets will always - * correspond to the underlying undirected graph, i.e. an edge from @c n1 to - * @c n2 will occurr in both adjacency lists. - */ -class GTL_EXTERN planar_embedding -{ -public: - /** - * @internal - */ - typedef symlist adj_list; - - /** - * @internal - */ - typedef symlist::iterator iterator; -private: - /** - * @internal - * Creates an empty planar embedding not related to any graph. - * @note At the moment planar embedding are thought as an output of - * planarity testing, this why they can't be constructed from scratch. - */ - planar_embedding() : G(0) - { - } -public: - /** - * - * Make this object a copy of @p em. - * - * @param em planar embedding - */ - planar_embedding(const planar_embedding& em); - - /** - * - * Destructor. - */ - virtual ~planar_embedding() - { - } - - /** - * - * Assigns @p em to this object. All former information in this object - * will be deleted. - * - * @param em - * - * @return reference to this object - */ - planar_embedding& operator=(const planar_embedding& em); -private: - /** - * @internal - * Initializes adjacency lists. - * - * @param G graph - */ - void init(graph& G); - - /** - * @internal - * Turns adjacency list of node @p n. - * - * @param n node. - */ - void turn(node n); - - /** - * @internal - * Insert edge @p e at the end of adjacency list of @p n. - * - * @param n node - * @param e edge to be inserted - * - * @return iterator to position of insertion - */ - iterator push_back(node n, edge e); - - /** - * @internal - * Insert edge @p e at the beginning of adjacency list of @p n. - * - * @param n node - * @param e edge to be inserted - * - * @return iterator to position of insertion - */ - iterator push_front(node n, edge e); - - /** - * @internal - * Insert selfloop @p e. - * - * @param @p e selfloop - */ - void insert_selfloop (edge e); - - /** - * @internal - * Returns position of edge @p e in adjacency list of node @p n. - * - * @param n node - * @param e adjacent edge - * - * @return position of @p e - */ - iterator& pos (node, edge); -public: - /** - * - * Returns reference to ordered adjacency list of node @p n. - * - * @param n node - * - * @return ordered adjacency list - */ - adj_list& adjacency(node n) - { - return adj[n]; - } - - /** - * - * Returns reference to ordered adjacency list of node @p n. - * - * @param n node - * - * @return ordered adjacency list - */ - const adj_list& adjacency(node n) const - { - return adj[n]; - } - - /** - * - * Start iteration through adjacency list of node @p n. - * - * @param n node - * - * @return start iterator - */ - iterator adj_edges_begin(node n) - { - return adj[n].begin(); - } - - /** - * - * End of iteration through adjacency list of node @p n. - * - * @param @p n node - * - * @return one-past the end iterator - */ - iterator adj_edges_end(node n) - { - return adj[n].end(); - } - - /** - * - * Returns the cyclic successor of edge @p e in the adjacency list of - * node @p n. - * - * @param n node - * @param e edge adjacent to @p n - * - * @return edge following @p e in adjacency of @p n - */ - edge cyclic_next(node n, edge e); - - /** - * - * Returns the cyclic predecessor of edge @p e in the adjacency list of - * node @p n. - * - * @param n node - * @param e edge adjacent to @p n - * - * @return edge preceding @p e in adjacency of @p n - */ - edge cyclic_prev(node n, edge e); - - - /** - * - * Writes embedding with st-numbers as given by @p st to @p os. - * - * @param os output stream - * - * @param st st-numbers - */ - void write_st(std::ostream& os, st_number& st); - - /** - * - * Returns list of selfloops contained in the graph. These will not - * occur in the adjacency lists. - * - * @retval list of selfloops - */ - edges_t& selfloops() - { - return self; - } - - /** - * - * Returns list of selfloops contained in the graph. These will not - * occur in the adjacency lists. - * - * @retval list of selfloops - */ - const edges_t& selfloops() const - { - return self; - } - - /** - * - * Returns list of multiple edges contained in the graph. These are - * edges for which there is already another edge connecting the same - * endpoints is contained in the adjacency lists. Please note that the - * notion "connecting" is meant in an undirected sense. These edges will - * not occur it the adjacency lists. - * - * @retval list of multiple edges - */ - edges_t& multiple_edges() - { - return multi; - } - - /** - * - * Returns list of multiple edges contained in the graph. These are - * edges for which there is already another edge connecting the same - * endpoints is contained in the adjacency lists. Please note that the - * notion "connecting" is meant in an undirected sense. These edges will - * not occur it the adjacency lists. - * - * @retval list of multiple edges - */ - const edges_t& multiple_edges() const - { - return multi; - } - - /** - * - * Used for debugging only. Checks whether this is a correct planar - * embedding by checking the faces of the graph, i.e. at any node - * starting with an arbitrary adjacent edge and advancing along @c - * cyclic_next the start node must be met through the edge given by @c - * cyclic_prev of the edge we started with. - * - * @retval true iff embedding is correct - */ - bool check(); - - /** - * @internal - */ - friend class planarity; - - /** - * @internal - */ - friend class pq_tree; - - /** - * @internal - */ - GTL_EXTERN friend std::ostream& operator<< (std::ostream&, planar_embedding&); -private: - /** - * @internal - * Graph. - */ - graph* G; - - /** - * @internal - * Adjacency lists. - */ - node_map adj; - - /** - * @internal - * Positions of edges in its source's adjacency list. - */ - edge_map s_pos; - - /** - * @internal - * Positions of edges in its target's adjacency list. - */ - edge_map t_pos; - - /** - * @internal - * Selfloops. - */ - edges_t self; - - /** - * @internal - * Multiple edges. - */ - edges_t multi; -}; - - -// class face -// { -// public: -// face (planar_embedding& em, node n, edge e) : embed (em), -// start (n), first (e) { } -// virtual ~face () { } - -// private: -// planar_embedding& embed; -// node start; -// edge first; - -// friend class planar_embedding; -// }; - -// struct _face_iterator -// { - - -// face& _face; -// }; - -__GTL_END_NAMESPACE - -#endif - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/fm_partition.h b/Tracker/graph/GTL/include/GTL/fm_partition.h deleted file mode 100644 index 6ffc83867..000000000 --- a/Tracker/graph/GTL/include/GTL/fm_partition.h +++ /dev/null @@ -1,751 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// fm_partition.h -// -//========================================================================== -// $Id: fm_partition.h,v 1.8 2003/01/31 08:15:05 chris Exp $ - -#ifndef GTL_FM_PARTITION_H -#define GTL_FM_PARTITION_H - -#include -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - - -/** - * @short Heuristic graph bi-partitioning algorithm (Fiduccia-Mattheyses). - * - * This class implements a heuristic graph bi-partitioning algorithm, based - * on iterative movement, proposed by C. M. Fiduccia and R. M. Mattheyses - * in 1982. - * - *

In the case E is the set of edges of the graph, the algorithm needs - * O(|E|) time to proceed. - * - * @see ratio_cut_partition - */ -class GTL_EXTERN fm_partition : public algorithm -{ -public: - /** - * Return type of @ref fm_partition#get_side_of_node. - * - * @see fm_partition#A - * @see fm_partition#B - */ - typedef int side_type; - - /** - * A means the node is on side A. - * - * @see fm_partition#side_type - */ - const static side_type A; - - /** - * B means the node is on side B. - * - * @see fm_partition#side_type - */ - const static side_type B; - - /** - * Fix type of each node (needed with @ref fm_partition#set_vars). - * - * @see fm_partition#FIXA - * @see fm_partition#FIXB - * @see fm_partition#UNFIXED - */ - typedef short int fix_type; - - /** - * FIXA means fix node on side A. - * - * @see fm_partition#set_vars - */ - const static fix_type FIXA; - - /** - * FIXB means fix node on side B. - * - * @see fm_partition#fixe_type - */ - const static fix_type FIXB; - - /** - * UNFIXED means node is free. - * - * @see fm_partition#fixe_type - */ - const static fix_type UNFIXED; - - /** - * Default constructor. - * - * @see fm_partition#fixe_type - */ - fm_partition(); - - /** - * Destructor. - * - * @see algorithm#~algorithm - */ - virtual ~fm_partition(); - - /** - * Sets variables. - * Must be executed before @ref fm_partition#check! - * - * @param G undirected graph - * @param node_weight weight of each node - * @param edge_weight weight of each edge - * @see fm_partition#check - */ - void set_vars(const graph& G, const node_map& node_weight, - const edge_map& edge_weight); - - /** - * Sets variables. - * Must be executed before @ref fm_partition#check! - * In order to get good results, init_side should - * almost be in balance. - * - * @param G undirected graph - * @param node_weight weight of each node - * @param edge_weight weight of each edge - * @param init_side initial bi-partitioning - * @see fm_partition#check - */ - void set_vars(const graph& G, const node_map& node_weight, - const edge_map& edge_weight, - const node_map& init_side); - - /** - * Sets variables. - * Must be executed before @ref fm_partition#check! - * - * @param G undirected graph - * @param node_weight weight of each node - * @param edge_weight weight of each edge - * @param fixed fixed nodes - * @see fm_partition#check - */ - void set_vars(const graph& G, const node_map& node_weight, - const edge_map& edge_weight, - const node_map& fixed); - - /** - * Sets variables. - * Must be executed before @ref fm_partition#check! - * In order to get good results, init_side should - * almost be in balance. Fixed nodes are on their fix side, their - * initial side is overwritten then. - * - * @param G undirected graph - * @param node_weight weight of each node - * @param edge_weight weight of each edge - * @param init_side initial bi-partitioning - * @param fixed fixed nodes - * @see fm_partition#check - */ - void set_vars(const graph& G, const node_map& node_weight, - const edge_map& edge_weight, - const node_map& init_side, - const node_map& fixed); - - /** - * Enables the storing of cut-edges. If enabled the list of - * cut-edges can be traversed using @ref - * fm_partition#cut_edges_iterator. - * - * @param set if true cut_edges will be stored - * @see fm_partition#cut_edges_begin - * @see fm_partition#cut_edges_end - */ - void store_cut_edges(const bool set); - - /** - * Enables the storing of nodes on their side. If enabled the nodes - * of each side can be traversed using @ref - * fm_partition#nodes_on_one_side_iterator. - * - * @param set if true nodes will be stored on their sides - * @see fm_partition#nodes_of_sideA_begin - * @see fm_partition#nodes_of_sideA_end - * @see fm_partition#nodes_of_sideB_begin - * @see fm_partition#nodes_of_sideB_end - */ - void store_nodesAB(const bool set); - - /** - * Checks whether following preconditions are satisfied: - *

    - *
  • @ref fm_partition#set_vars has been executed before. - *
  • graph G is undirected. - *
  • only node_weights >= 0 are applied. - *
  • only edge_weights >= 0 are applied. - *
- * - * @param G graph - * @return algorithm::GTL_OK on success, - * algorithm::GTL_ERROR otherwise - * @see fm_partition#set_vars - * @see algorithm#check - */ - virtual int check(graph& G); - - /** - * Computes a partitioning with G, that means a - * division of its vertices in two sides fm_partition::A - * and fm_partition::B. - * - * @param G graph - * @return algorithm::GTL_OK on success, - * algorithm::GTL_ERROR otherwise - * @see algorithm#run - */ - int run(graph& G); - - /** - * Gets the size of the cut after bi-partitioning. - * - * @return cutsize - */ - int get_cutsize(); - - /** - * Gets the number of passes needed to create a bi-partition with - * this heuristic. - * - * @return number of passes - */ - int get_needed_passes(); - - /** - * Gets side of the node after bi-partitioning. - * - * @param n node of graph @c G - * @return fm_partition::A if n lies on - * side A, fm_partition::B otherwise - */ - side_type get_side_of_node(const node& n) const; - - /** - * Gets side of the node after bi-partitioning. - * - * @param n node of graph @c G - * @return fm_partition::A if n lies on - * side A, fm_partition::B otherwise - * @see fm_partition#get_side_of_node - */ - side_type operator [](const node& n) const; - - /** - * Gets the sum of all node weights from nodes on side A. - * - * @param G graph - * @return node_weight_on_sideA - */ - int get_weight_on_sideA(const graph& G) const; - - /** - * Gets the sum of all node weights from nodes on side B. - * - * @param G graph - * @return node_weight_on_sideB - */ - int get_weight_on_sideB(const graph& G) const; - - /** - * Iterator type for edges which belong to the cut. - */ - typedef edges_t::const_iterator cut_edges_iterator; - - /** - * Iterate through all edges which belong to the cut, that means - * all edges with end-nodes on different sides. - * It is only valid if enabled with @ref - * fm_partition#store_cut_edges before. - * - * @return start for iteration through all cut edges - */ - cut_edges_iterator cut_edges_begin() const; - - /** - * End-Iterator for iteration through all edges which belong to the - * cut. - * It is only valid if enabled with @ref - * fm_partition#store_cut_edges before. - * - * @return end for iteration through all cut-edges - */ - cut_edges_iterator cut_edges_end() const; - - /** - * Iterator type of nodes of a side. - */ - typedef nodes_t::const_iterator nodes_of_one_side_iterator; - - /** - * Iterate through all nodes which belong to side A. - * It is only valid if enabled with @ref - * fm_partition#store_nodesAB before. - * - * @return start for iteration through all nodes on A - */ - nodes_of_one_side_iterator nodes_of_sideA_begin() const; - - /** - * End-Iterator for iteration through all nodes which belong to side - * A. - * It is only valid if enabled with @ref - * fm_partition#store_nodesAB before. - * - * @return end for iteration through all nodes on A - */ - nodes_of_one_side_iterator nodes_of_sideA_end() const; - - /** - * Iterate through all nodes which belong to side B, - * It is only valid if enabled with @ref - * fm_partition#store_nodesAB before. - * - * @return start for iteration through all nodes on B - */ - nodes_of_one_side_iterator nodes_of_sideB_begin() const; - - /** - * End-Iterator for iteration through all nodes which belong to side - * B, - * It is only valid if enabled with @ref - * fm_partition#store_nodesAB before. - * - * @return end for iteration through all nodes on B - */ - nodes_of_one_side_iterator nodes_of_sideB_end() const; - - /** - * Resets fm_partition, i.e. prepares the algorithm to be applied - * to another graph. - * - * @see algorithm#reset - */ - virtual void reset(); -protected: - /** - * @internal - * true, iff user enabled storing of cut-edges with - * @ref fm_partition#store_cut_edges. - */ - bool enable_cut_edges_storing; - - /** - * @internal - * List of edges which belong to the cut. - */ - edges_t cut_edges; - - /** - * @internal - * true, iff user enabled storing of nodes with - * @ref fm_partition#store_nodesAB. - */ - bool enable_nodesAB_storing; - - /** - * @internal - * List of nodes which belong to side A. - */ - nodes_t nodesA; - - /** - * @internal - * List of nodes which belong to side A. - */ - nodes_t nodesB; - - /** - * @internal - * true, iff user has executed @ref fm_partition# - * set_vars before @ref fm_partition#check and @ref fm_partition# - * run. - */ - bool set_vars_executed; - - /** - * @internal - * true, iff user has provided init_side - * with @ref fm_partition#set_vars, false otherwise. - */ - bool provided_initial_part; - - /** - * @internal - * true, iff user has provided fixed with - * @ref fm_partition#set_vars, false otherwise. - */ - bool provided_fix; - - /** - * @internal - * Contains information where a node is fixed. - */ - node_map fixed; - - /** - * @internal - * Contains the weight of each node. - * Corresponds to w(v) in [Leng90]. - */ - node_map node_weight; - - /** - * @internal - * Contains the maximum weight of a node in G. - * (maximum of node_weight[...]) - */ - int max_node_weight; - - /** - * @internal - * Contains the weight of each edge. - * Corresponds to c(e) in [Leng90]. - */ - edge_map edge_weight; - - /** - * @internal - * Contains the maximum weight of an edge in G. - * (maximum of edge_weight[...]) - */ - int max_edge_weight; - - /** - * @internal - * Contains the sum over all vertex weights in G. - * Corresponds to w(V) in [Leng90]. - */ - int total_node_weight; - - /** - * @internal - * Contains the sum over all vertex weights on side A. - * Corresponds to w(A) in [Leng90]. - */ - int node_weight_on_sideA; - - /** - * @internal - * Contains the sum over all vertex weights on side B. - * Corresponds to w(B) in [Leng90]. - */ - int node_weight_on_sideB; - - /** - * @internal - * Contains information about the current side of a node. - */ - node_map side; - - /** - * @internal - * Corresponds to CELL array in [FidMat82] - */ - node_map position_in_bucket; - - /** - * @internal - * Contains the maximal number of adjacent to a node. - */ - int max_vertex_degree; - - /** - * @internal - * Contains how many nodes an edge has on side A. - */ - edge_map aside; - - /** - * @internal - * Contains how many nodes an edge has on side B. - */ - edge_map bside; - - /** - * @internal - * Contains the unlocked nodes of an edge on side A. - * (max. 2) - */ - edge_map unlockedA; - - /** - * @internal - * Contains the unlocked nodes of an edge on side B. - * (max. 2) - */ - edge_map unlockedB; - - /** - * @internal - * Corresponds to D value in Leng[90]. - */ - node_map gain_value; - - /** - * @internal - * true, iff bucketA is empty. - */ - bool bucketA_empty; - - /** - * @internal - * true, iff bucketB is empty. - */ - bool bucketB_empty; - - /** - * @internal - * Contains the maximum gain value of a node in - * bucketA. - */ - int max_gainA; - - /** - * @internal - * Contains the maximum gain value of a node in - * bucketB. - */ - int max_gainB; - - /** - * @internal - * Like a hash table over the gain_value of each node - * on side A. (open hashing, collisions in gain buckets - * are organized through LIFO lists) - */ - std::vector bucketA; - - /** - * @internal - * Like a hash table over the gain_value of each node - * on side B. (open hashing, collisions in gain buckets - * are organized through LIFO lists) - */ - std::vector bucketB; - - /** - * @internal - * Sum over all edge_costs[e] where edge e is an - * element of the cut. - */ - int cur_cutsize; - - /** - * @internal - * Number of needed passes. - */ - int no_passes; - - /** - * @internal - * Fix FIXA nodes on side A and FIXB - * nodes on side B. - */ - void divide_up(const graph& G); - - /** - * @internal - * Hides self loops of G. - */ - void hide_self_loops(graph& G); - - /** - * @internal - * Computes max_edge_weight, max_node_weight - * and total_node_weight. - */ - void init_variables(const graph& G); - - /** - * @internal - * Divides nodes of G arbitrary into two sides A - * and B. Here, side will be - * filled with an arbitrary feasible solution. - */ - void create_initial_bipart(const graph& G); - - /** - * @internal - * Shuffles order of node_vector with size - * vector_size. - */ - void shuffle_vector(const int vector_size, - std::vector& node_vector); - - /** - * @internal - * Computes max_vertex_degree. - */ - void compute_max_vertex_degree(const graph& G); - - /** - * @internal - * Runs as much passes as needed. - */ - void pass_manager(const graph& G); - - /** - * @internal - * Copies side node maps. - */ - void copy_side_node_map(const graph& G, node_map& dest, - const node_map source) const; - - /** - * @internal - * Initialization of the data structure for each pass. - */ - void init_data_structure(const graph& G); - - /** - * @internal - * Computes initial gain_value for each node and inserts it in the - * corresponding bucket data structure. - */ - void init_filling_buckets(const graph& G); - - /** - * @internal - * Compute initial gain of a node on side A. - * @return initial gain_value of a node on side A - */ - int inital_gain_of_node_on_sideA(const node cur_node); - - /** - * @internal - * Compute initial gain of a node on side B. - * @return initial gain_value of a node on side B - */ - int inital_gain_of_node_on_sideB(const node cur_node); - - /** - * @internal - * Moves nodes within a pass. - */ - void move_manager(const graph& G); - - /** - * @internal - * Move a single node - * @return true if vertex stored in parameter - * moved_node has been found - */ - bool move_vertex(const graph& G, node& moved_node); - - /** - * @internal - * Only valid on unlocked nodes! - * @return true if a certain balance criterion can be - * hold, false otherwise - */ - bool balance_holds(const graph& G, const node cur_node); - - /** - * @internal - * Executed, if cur_node is chosen to move from side - * A to B. - */ - void update_data_structure_A2B(const node cur_node); - - /** - * @internal - * Executed, if cur_node is chosen to move from side - * B to A. - */ - void update_data_structure_B2A(const node cur_node); - - /** - * @internal - * Reorganizes bucketA if a nodes gain of it has been - * changed. - */ - void update_bucketA(const node cur_node, const int old_gain, - const int new_gain); - - /** - * @internal - * Reorganizes bucketB if a nodes gain of it has been - * changed. - */ - void update_bucketB(const node cur_node, const int old_gain, - const int new_gain); - - /** - * @internal - * Recomputes max_gainA or max_gainB - * respectively. - */ - void update_max_gain(const side_type side); - - /** - * @internal - * Transform a range from [-a..+a] to [0..2a]. - * (reverse to @ref fm_partition#range_up) - */ - inline int range_up(const int gain_value) const; - - /** - * @internal - * Transform a range from [0..2a] to [-a..+a]. - * (reverse to @ref fm_partition#range_down) - */ - inline int range_down(const int index) const; - - /** - * @internal - * Do some garbage collection. - */ - void clean_pass(const graph& G); - - /** - * @internal - * Computes list cut_edges. - */ - void compute_cut_edges(const graph& G); - - /** - * @internal - * Computes lists nodesA and nodesB. - */ - void compute_nodesAB(const graph& G); -private: -#ifdef _DEBUG - /** - * @internal - * Prints content of bucketA with associated gain values. - */ - void print_bucketA(); - - /** - * @internal - * Prints content of bucketB with associated gain values. - */ - void print_bucketB(); -#endif // _DEBUG -}; - - -__GTL_END_NAMESPACE - -#endif // GTL_FM_PARTITION_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/gml_parser.h b/Tracker/graph/GTL/include/GTL/gml_parser.h deleted file mode 100644 index 029e0e102..000000000 --- a/Tracker/graph/GTL/include/GTL/gml_parser.h +++ /dev/null @@ -1,80 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// gml_parser.h -// -//========================================================================== -// $Id: gml_parser.h,v 1.7 2000/01/05 16:32:36 raitner Exp $ - -#ifndef GTL_GML_PARSER_H -#define GTL_GML_PARSER_H - -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @internal - */ -union GTL_EXTERN GML_pair_val { - long integer; - double floating; - char* str; - struct GML_pair* list; -}; - -/** - * @internal - */ -struct GTL_EXTERN GML_pair { - char* key; - GML_value kind; - union GML_pair_val value; - struct GML_pair* next; -}; - -/** - * @internal - */ -struct GTL_EXTERN GML_list_elem { - char* key; - struct GML_list_elem* next; -}; - -/** - * @internal - */ -struct GTL_EXTERN GML_stat { - struct GML_error err; - struct GML_list_elem* key_list; -}; - -/* - * returns list of KEY - VALUE pairs. Errors and a pointer to a list - * of key-names are returned in GML_stat. Previous information contained - * in GML_stat, i.e. the key_list, will be *lost*. - */ - -GTL_EXTERN GML_pair* GML_parser (FILE*, GML_stat*, int); - -/* - * free memory used in a list of GML_pair - */ - -GTL_EXTERN void GML_free_list (GML_pair*, GML_list_elem*); - - -/* - * debugging - */ - -GTL_EXTERN void GML_print_list (GML_pair*, int); - -__GTL_END_NAMESPACE - -#endif // GTL_GML_PARSER_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/gml_scanner.h b/Tracker/graph/GTL/include/GTL/gml_scanner.h deleted file mode 100644 index 92c0c93d4..000000000 --- a/Tracker/graph/GTL/include/GTL/gml_scanner.h +++ /dev/null @@ -1,138 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// gml_scanner.h -// -//========================================================================== -// $Id: gml_scanner.h,v 1.11 2000/03/06 15:16:52 raitner Exp $ - -#ifndef GTL_GML_SCANNER_H -#define GTL_GML_SCANNER_H - -#include - -#include - -__GTL_BEGIN_NAMESPACE - -/* - * start-size of buffers for reading strings. If too small it will be enlarged - * dynamically - */ - -#define INITIAL_SIZE 1024 - -GTL_EXTERN typedef enum { - GML_KEY, GML_INT, GML_DOUBLE, GML_STRING, GML_L_BRACKET, - GML_R_BRACKET, GML_END, GML_LIST, GML_ERROR -} GML_value; - - -/** - * Possible errors while parsing a GML file. - */ -GTL_EXTERN typedef enum { - GML_UNEXPECTED, GML_SYNTAX, GML_PREMATURE_EOF, GML_TOO_MANY_DIGITS, - GML_OPEN_BRACKET, GML_TOO_MANY_BRACKETS, GML_OK, GML_FILE_NOT_FOUND -} GML_error_value; - - -/** - * @short Reason and position of an error in a GML file. - * - * When an error occurs while parsing the structure of a GML file - * GML_error is used to return the type and position - * of the error detected. Position is specified by - * line and column, but might be - * somewhat imprecise. However at least the line number should - * not differ too much from the real position. - * - * @see graph#load - */ -struct GTL_EXTERN GML_error { - /** - * Contains the error description as symbolic constant: - *
    - *
  • GML_FILE_NOT_FOUND: A file with that name - * doesn't exist.
  • - *
  • GML_OK: No error :-)
  • - *
  • GML_TOO_MANY_BRACKETS: A mismatch of - * brackets was detected, i.e. there were too many closing - * brackets (]).
  • - *
  • GML_OPEN_BRACKET: Now, there were too many - * opening brackets ([)
  • - *
  • GML_TOO_MANY_DIGITS: The number of digits a - * integer or floating point value can have is limited to - * 1024, this should be enough :-)
  • - *
  • GML_PREMATURE_EOF: An EOF occured, where it - * wasn't expected, e.g. while scanning a string.
  • - *
  • GML_SYNTAX: The file isn't a valid GML file, - * e.g. a mismatch in the key-value pairs.
  • - *
  • GML_UNEXPECTED: A character occured, where - * it makes no sense, e.g. non-numerical characters in - * numbers or keys beginning with numbers
  • - *
- * - */ - GML_error_value err_num; - - /** - * Contains the line, where the error was detected. This will - * usually be near the line where the error really is - * located. - */ - int line; - - /** - * Contains the column, where the error was detected. - */ - int column; -}; - - -union GTL_EXTERN GML_tok_val { - long integer; - double floating; - char* str; - struct GML_error err; -}; - -/** - * @internal - */ -struct GTL_EXTERN GML_token { - GML_value kind; - union GML_tok_val value; -}; - -/* - * global variables - */ - -GTL_EXTERN extern unsigned int GML_line; -GTL_EXTERN extern unsigned int GML_column; - -/* - * if you are interested in the position where an error occured it is a good - * idea to set GML_line and GML_column back. - * This is what GML_init does. - */ - -GTL_EXTERN void GML_init (); - -/* - * returns the next token in file. If an error occured it will be stored in - * GML_token. - */ - -GTL_EXTERN struct GML_token GML_scanner (FILE*); - -GTL_EXTERN extern const char* GML_table[]; - -__GTL_END_NAMESPACE - -#endif // GTL_GML_SCANNER_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/graph.h b/Tracker/graph/GTL/include/GTL/graph.h deleted file mode 100644 index c1bb5d814..000000000 --- a/Tracker/graph/GTL/include/GTL/graph.h +++ /dev/null @@ -1,818 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// graph.h -// -//========================================================================== -// $Id: graph.h,v 1.43 2002/11/06 08:49:35 raitner Exp $ - -#ifndef GTL_GRAPH_H -#define GTL_GRAPH_H - -#include -#include -#include -#include -#include -#include - -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * $Date: 2002/11/06 08:49:35 $ - * $Revision: 1.43 $ - * - * @brief A directed or undirected graph. - * - * A graph G=(V,E) consists of a set of nodes - * V and a set of edges E , where every - * edge can be viewed as a (ordered) pair of nodes (u,v) - * connecting source u with target v . - * Obviously this implies a direction on the edges, which is why we - * call these graphs directed (this is the default). A graph can be made - * undirected by just ignoring the (implicit) direction. - * - * @see node - * @see edge - */ - -class GTL_EXTERN graph -{ -public: - //================================================== Con-/Destructors - - /** - * Generates an empty graph, i.e. without any nodes and any edges. - */ - graph(); - - /** - * Copy constructor. Please note: This will generate an - * isomorpic copy of G. Although this graph will look - * like G it is not physically the same. - * Especially it consists of nodes and edges, which of course have - * counterparts in G, but are different. This means - * that the nodes (edges) in the copy have undefined behaviour if - * used within a @ref node_map (@ref edge_map ) of the original graph. - * - * @param G graph - */ - graph (const graph& G); - - /** - * Makes new graph isomorphic to the subgraph induced by nodes. - * The same restriction as for the ordinary copy constructor applies to - * this one. - * - * @param G graph - * @param nodes nodes of G, which form - * the induced subgraph this graph will be isomorphic to. - */ - graph(const graph& G, const nodes_t& nodes); - - /** - * Makes new graph isomorphic to the subgraph induced by the nodes - * in the range from it to end - * The same restriction as for the ordinary copy constructor applies to - * this one. - * - * @param G graph - * @param it beginning of nodes - * @param end end of nodes - */ - graph (const graph& G, - nodes_t::const_iterator it, - nodes_t::const_iterator end); - - /** - * Destructor. Deletes all nodes and edges. - */ - virtual ~graph(); - - //================================================== Directed/Undirected - - /** - * Makes graph directed. - */ - void make_directed(); - - /** - * Makes graph undirected. - */ - void make_undirected(); - - //================================================== Tests / Information - - /** - * Test whether the graph is directed. - * - * @return true iff the graph is directed. - */ - bool is_directed() const; - - /** - * Test whether the graph is undirected. - * - * @return true iff the graph is undirected - */ - bool is_undirected() const; - - /** - * Checks if for all edges (v, w) the reverse edge - * (w,v) is present, too. Additionally the reverse of some - * edge e will be stored as rev[e]. If there - * is no reverse edge of e rev[e] will be the - * invalid edge edge(). - * - * @param rev map associating every edge with its - * reverse edge. - * @return true iff every edge has a reverse edge. - */ - bool is_bidirected(edge_map& rev) const; - - /** - * Test whether the graph is connected - * - * @return true iff the graph is connected - * @see dfs - * @see bfs - */ - bool is_connected() const; - - /** - * Test whether the graph is acyclic - * - * @return true iff the graph contains no cycles - * @see topsort - */ - bool is_acyclic() const; - - /** - * Returns the number of nodes in the graph. - * - * @return number of nodes - */ - int number_of_nodes() const; - - /** - * Returns the number of (visible) edges in the graph - * - * @return number of edges - */ - int number_of_edges() const; - - /** - * Returns a center of the graph which is defined as a node with - * maximum excentricity. - * - * @return one node of the graph center - */ - node center() const; - - //================================================== Creation - - /** - * Adds a new node. - * - * @return new node. - */ - virtual node new_node(); - - /** - * Adds new edge from s to - * t. - * - *

- * Precondition: s,t are valid nodes in this graph. - * - * @param s source of new edge - * @param t target of new edge - * @return new edge. - */ - virtual edge new_edge(node s, node t); - - /** - * @internal - */ - virtual edge new_edge(const nodes_t &sources, const nodes_t &targets); - - //================================================== Deletion - - /** - * Deletes node n, and thus all edges incident with - * n. - * - *

- * Precondition: n is a valid visible node - * in this graph - * - * @param n visible node to be deleted - */ - void del_node(node n); - - /** - * @deprecated - * Deletes all visible nodes, i.e. the hidden ones stay. - */ - void del_all_nodes(); - - /** - * Deletes edge e. - * - *

- * Precondition: e is a valid visible edge - * in this graph. - * - * @param e edge to be deleted - */ - void del_edge(edge e); - - /** - * @deprecated - * Deletes all visible edges, i.e. the hidden ones stay. - */ - void del_all_edges(); - - /** - * Deletes all nodes and edges, even the hidden ones - */ - void clear(); - - //================================================== Iterators - - /** - * @internal - */ - typedef nodes_t::const_iterator node_iterator; - /** - * @internal - */ - typedef edges_t::const_iterator edge_iterator; - - /** - * Iterate through all nodes in the graph. - * - * @return start for iteration through all nodes in the graph. - */ - node_iterator nodes_begin() const; - - /** - * Iterate through all nodes in the graph. - * - * @return end for iteration through all nodes in the graph. - */ - node_iterator nodes_end() const; - - /** - * Iterate through all edges in the graph. - * - * @return start for iteration through all edges in the graph. - */ - edge_iterator edges_begin() const; - - /** - * Iterate through all edges in the graph. - * - * @return end for iteration through all edges in the graph. - */ - edge_iterator edges_end() const; - - //================================================== get nodes/edges - - /** - * @deprecated - * @return a list of all nodes of the graph - */ - nodes_t all_nodes() const; - - /** - * @deprecated - * @return a list of all edges of the graph - */ - edges_t all_edges() const; - - /** - * @deprecated - */ - node choose_node () const; - - //================================================== Hide / Restore - - /** - * Hides an edge. - * - *

- * Precondition: e is a valid edge in this graph - * - * @param e edge to be hidden - */ - void hide_edge (edge e); - - /** - * Restores a hidden edge - * - *

- * Precondition: e is a valid edge in this graph - * - * @param e hidden edge - */ - void restore_edge (edge e); - - /** - * Hides a node. Please note: all the edges incident with - * n will be hidden, too. All these edges are returned - * in a list. - * - *

- * Precondition: n is a valid node in this graph - * - * @param e node to be hidden - * @return list of implicitly hidden, incident edges - */ - edges_t hide_node(node n); - - /** - * Restores a hidden node. This only restores the node itself. It - * doesn't restore the incident edges, i.e. you will have to restore - * all the edges you get returned when calling @ref graph#hide_node - * yourself. - * - *

- * Precondition: n is a valid node in this graph - * @param n hidden node - */ - void restore_node (node n); - - /** - * Hides all nodes not contained in subgraph_nodes, i.e. - * (the visible part of) the graph is the induced subgraph with - * respect to the nodes in subgraph_nodes. It is allowed - * to apply this function recursively, i.e. one may call - * induced_subgraph on a graph that is already a induced - * subgraph. - * - * @param subgraph_nodes nodes of subgraph. - * @see graph#restore_graph - */ - void induced_subgraph(nodes_t& subgraph_nodes); - - /** - * Restores all hidden nodes and edges - * This means that, although the nodes - * and edges got hidden at different times, they will be restored all - * together. - * - * @see graph#induced_subgraph - * @see graph#hide_edge - * @see graph#hide_node - */ - void restore_graph (); - - //================================================== Others - - /** - * @deprecated - * inserts for all edges of the graph a reverse edge - * NOTE: this functions does NOT care about existing reverse edges - */ - edges_t insert_reverse_edges(); - - //================================================== I/O - - /** - * Load graph from a file in GML-format. The optional - * parameter preserve_ids controls whether to - * give the nodes the same ids as in the GML file. You can enable this - * for debugging but you should disable it for final releases since - * it may make node_map unecessarily large. - * - * @param filename file in GML-format. - * @param preserve_ids if true all the nodes - * will get the same id as in the GML file. If false (default) - * the nodes will be numbered consecutively beginning with 0. However - * the order of the nodes in the GML file will be preserved. - * @return detailed error description (hopefully GML_OK). For details - * see @ref GML_error#err_num. - */ - - GML_error load(const std::string& filename, bool preserve_ids = false) - { return load (filename.c_str(), preserve_ids); } - - - /** - * Load graph from a file in GML-format. The optional - * parameter preserve_ids controls whether to - * give the nodes the same ids as in the GML file. You can enable this - * for debugging but you should disable it for final releases since - * it may make node_map unecessarily large. - * - * @param filename file in GML-format. - * @param preserve_ids if true all the nodes - * will get the same id as in the GML file. If false (default) - * the nodes will be numbered consecutively beginning with 0. However - * the order of the nodes in the GML file will be preserved. - * @return detailed error description (hopefully GML_OK). For details - * see @ref GML_error#err_num. - */ - - GML_error load (const char* filename, bool preserve_ids = false); - - /** - * Save graph to file filename in GML-format, i.e. - * graph [ node [ id # ] ... edge [ source # target #] ... ] - * - * @param filename - * @return 0 on error 1 otherwise - */ - - int save (const char* filename) const; - - /** - * Saves graph to stream file in GML-format. - * - * @param file output stream defaults to cout. - */ - - void save(std::ostream* file = &std::cout) const; - - //================================================== Node handlers - - /** - * Virtual function called before a new node is created; - * can be redefined in a derived class for customization - * - * @see graph#new_node - */ - virtual void pre_new_node_handler() {} - - /** - * Virtual function called after a new node was created; - * can be redefined in a derived class for customization - * - * @param n created node - * @see graph#new_node - */ - virtual void post_new_node_handler(node /*n*/) {} - - /** - * Virtual function called before a node is deleted; - * can be redefined in a derived class for customization - * - * @param n node deleted afterwards - * @see graph#del_node - */ - virtual void pre_del_node_handler(node /*n*/) {} - - /** - * Virtual function called after a node was deleted; - * can be redefined in a derived class for customization - * - * @see graph#del_node - */ - virtual void post_del_node_handler() {} - - /** - * Virtual function called before a node gets hidden; - * can be redefined in a derived class for customization - * - * @param n node to be hidden - * @see graph#hide_node - */ - virtual void pre_hide_node_handler(node /*n*/) {} - - /** - * Virtual function called after a node got hidden; - * can be redefined in a derived class for customization - * - * @param n hidden node - * @see graph#hide_node - */ - virtual void post_hide_node_handler(node /*n*/) {} - - /** - * Virtual function called before a node is restored; - * can be redefined in a derived class for customization - * - * @param n node to be restored - * @see graph#restore_node - */ - virtual void pre_restore_node_handler(node /*n*/) {} - - /** - * Virtual function called after a node was restored; - * can be redefined in a derived class for customization - * - * @param n restored node - * @see graph#restore_node - */ - virtual void post_restore_node_handler(node /*n*/) {} - - //================================================== Edge handlers - - /** - * Virtual function called before a new edge is inserted; - * can be redefined in a derived class for customization - * - * @param s source of edge created afterwards - * @param t target of edge created afterwards - * @see graph#new_edge - */ - virtual void pre_new_edge_handler(node /*s*/, node /*t*/) {} - - /** - * Virtual function called after a new edge was inserted; - * can be redefined in a derived class for customization - * - * @param e created edge - * @see graph#new_edge - */ - virtual void post_new_edge_handler(edge /*e*/) {} - - /** - * Virtual function called before a edge is deleted; - * can be redefined in a derived class for customization - * - * @param e edge to be deleted - * @see graph#del_edge - */ - virtual void pre_del_edge_handler(edge /*e*/) {} - - /** - * Virtual function called after a edge was deleted; - * can be redefined in a derived class for customization - * - * @param s source of edge deleted - * @param t target of edge deleted - * @see graph#del_edge - */ - virtual void post_del_edge_handler(node, node) {} - - /** - * Virtual function called before a edge gets hidden; - * can be redefined in a derived class for customization - * - * @param e edge to be hidden - * @see graph#hide_edge - */ - virtual void pre_hide_edge_handler(edge /*e*/) {} - - /** - * Virtual function called after a edge got hidden; - * can be redefined in a derived class for customization - * - * @param e hidden edge - * @see graph#hide_edge - */ - virtual void post_hide_edge_handler(edge /*e*/) {} - - /** - * Virtual function called before a edge is restored; - * can be redefined in a derived class for customization - * - * @param e edge to be restored - * @see graph#restore_edge - */ - virtual void pre_restore_edge_handler(edge /*e*/) {} - - /** - * Virtual function called after a edge was restored; - * can be redefined in a derived class for customization - * - * @param e restored edge - * @see graph#restore_edge - */ - virtual void post_restore_edge_handler(edge /*e*/) {} - - //================================================== Global handlers - - /** - * Virtual function called before performing clear; - * can be redefined in a derived class for customization. - * Please note: Although nodes and edges are deleted - * during @ref graph#clear this is not achieved by calling - * @ref graph#del_node and @ref graph#del_edge, which is why - * the correspondig handler will not be called. - * - * @see graph#clear - */ - virtual void pre_clear_handler() {} - - /** - * Virtual function called after the graph was cleared; - * can be redefined in a derived class for customization - * Please note: Although nodes and edges are deleted - * during @ref graph#clear this is not achieved by calling - * @ref graph#del_node and @ref graph#del_edge, which is why - * the correspondig handler will not be called. - * - * @see graph#clear - */ - virtual void post_clear_handler() {} - - /** - * Virtual function called before performing make_directed - * (only if graph was undirected) - * can be redefined in a derived class for customization - * - * @see graph#make_directed - */ - virtual void pre_make_directed_handler() {} - - /** - * Virtual function called after performing make_directed; - * (only if graph was undirected) - * can be redefined in a derived class for customization - * - * @see graph#make_directed - */ - virtual void post_make_directed_handler() {} - - /** - * Virtual function called before performing make_undirected; - * (only if graph was directed) - * can be redefined in a derived class for customization - * - * @see graph#make_undirected - */ - virtual void pre_make_undirected_handler() {} - - /** - * Virtual function called after performing make_undirected; - * (only if graph was directed) - * can be redefined in a derived class for customization - * - * @see graph#make_undirected - */ - virtual void post_make_undirected_handler() {} - - - //================================================== I/O - Handler - - /** - * Called before writing the graph key to os. This can be - * used to write top-level keys that should appear before the graph in - * the file. - * - * @param os output stream. - * @see graph#save - */ - virtual void pre_graph_save_handler(std::ostream* /*os*/) const { }; - - /** - * Called before the closing bracket of the list belonging to the - * graph key is written. This can be used to write information that - * belong to the graph, and thus should appear within the list - * associated with the graph key. - * - * @param os output stream. - * @see graph#save - */ - virtual void save_graph_info_handler(std::ostream*) const { }; - - /** - * Called before the closing bracket of the list belonging to the key - * of node n is written. This can be used to write - * information belonging to the node n and thus should - * appear within the list associated with this node. - * - * @param os output stream. - * @see graph#save - */ - virtual void save_node_info_handler(std::ostream*, node) const { }; - - /** - * Called before the closing bracket of the list belonging to the key - * of edge e is written. This can be used to write - * information belonging to the edge e and thus should - * appear within the list associated with this edge. - * - * @param os output stream. - * @see graph#save - */ - virtual void save_edge_info_handler(std::ostream*, edge) const { }; - - /** - * Called after writing the graph key to os. This can be - * used to write top-level keys that should appear after the graph in - * the file. - * - * @param os output stream. - * @see graph#save - */ - virtual void after_graph_save_handler(std::ostream*) const { }; - - /** - * Called after the graph is completely built. The topmost list - * of key-value-pairs is passed to this handler. NB: This list - * also contains the graph key, which was used to build the graph. - * - * @param list pointer to the list of key-value pairs at - * top level - * @see graph#load - */ - virtual void top_level_key_handler (GML_pair* list); - - /** - * Called after a node is created. The whole list of key-value-pairs - * belonging to this node is passed to this handler together with the - * node itself. - * - * @param n node parsed - * @param list pointer to the list of key-value-pairs of - * this node. - * @see graph#load - */ - virtual void load_node_info_handler (node n, GML_pair* list ); - - /** - * Called after an edge is created. The whole list of key-value-pairs - * belonging to this edge is passed to this handler together with the - * edge itself. - * - * @param e edge parsed - * @param list pointer to the list of key-value-pairs of - * this edge. - * @see graph#load - */ - virtual void load_edge_info_handler (edge e, GML_pair* list); - - /** - * Called after the graph is completely built. The whole list for - * the graph key used to build this graph is passed to this handler. - * - * @param list pointer to the list of key-value-pairs of - * the graph. - * @see graph#load - */ - virtual void load_graph_info_handler (GML_pair* list); - -private: - - //================================================== Flags - - mutable bool directed; - - //================================================== Visible Nodes/Edges - - nodes_t nodes; - edges_t edges; - int nodes_count, edges_count; - - //================================================== Hidden Nodes/Edges - - nodes_t hidden_nodes; - edges_t hidden_edges; - int hidden_nodes_count, hidden_edges_count; - - //================================================== Node/edge numbering - - int new_node_id(); - int new_edge_id(); - - //================================================== Copy - - void copy (const graph& G, - nodes_t::const_iterator it, - nodes_t::const_iterator end); - -public: // needs to be public, because template friends are not possible - /** - * @internal - */ - int number_of_ids(node) const; - - /** - * @internal - */ - int number_of_ids(edge) const; - -private: - std::list free_node_ids; - std::list free_edge_ids; - int free_node_ids_count, free_edge_ids_count; - - //================================================== utilities - - void del_list(nodes_t &); - void del_list(edges_t &); - - GTL_EXTERN friend std::ostream& operator<< (std::ostream& os, const graph& G); -}; - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// Iteration -//-------------------------------------------------------------------------- - -#define forall_nodes(v,g) GTL_FORALL(v,g,graph::node_iterator,nodes_) -#define forall_edges(v,g) GTL_FORALL(v,g,graph::edge_iterator,edges_) - -#endif // GTL_GRAPH_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/maxflow_ff.h b/Tracker/graph/GTL/include/GTL/maxflow_ff.h deleted file mode 100644 index a9f4694bc..000000000 --- a/Tracker/graph/GTL/include/GTL/maxflow_ff.h +++ /dev/null @@ -1,240 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// maxflow_ff.h -// -//========================================================================== -// $Id: maxflow_ff.h,v 1.5 2003/01/31 08:15:05 chris Exp $ - -#ifndef GTL_MAXFLOW_FF_H -#define GTL_MAXFLOW_FF_H - -#include -#include -#include -#include -#include - -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @short Maximum flow algorithm (Edmonds-Karp). - */ -class GTL_EXTERN maxflow_ff : public algorithm -{ -public: - /** - * Default constructor. Enables only the calculation of - * maximum flow. - * - * @see algorithm#algorithm - */ - maxflow_ff(); - - /** - * Destructor. - * - * @see algorithm#~algorithm - */ - virtual ~maxflow_ff(); - - /** - * Sets capacity of every edge for maximum flow calculation - * where artificial start-node and end_node will be computed - * automatically. - * - * @param edge_capacity capacity of every edge - */ - void set_vars(const edge_map& edge_capacity); - - /** - * Sets capacity of every edge for maximum flow calculation - * - * @param edge_capacity capacity of every edge - * @param net_source start-node - * @param net_target end-node - */ - void set_vars( - const edge_map& edge_capacity, - const node& net_source, - const node& net_target); - - /** - * Checks whether following preconditions are satisfied: - *

    - *
  • @ref maxflow_ff#set_vars has been executed before. - *
  • only edge_capacities >= 0 are applied. - *
  • G is directed. - *
  • G is connected. - *
  • G has at least one edge and two nodes. - *
  • if not applied, start-nodes and end-nodes exists. - *
  • if applied, start-node is not the same node as end-node. - *
- * - * @param G graph - * @return algorithm::GTL_OK on success - * algorithm::GTL_ERROR otherwise - * @see algorithm#check - */ - virtual int check(graph& G); - - /** - * Computes maximum flow of graph G. - * - * @param G graph - * @return algorithm::GTL_OK on success - * algorithm::GTL_ERROR otherwise - * @see algorithm#run - */ - int run(graph& G); - - /** - * Returns the maximum flow of an edge. - * - * @param e edge of a graph G - * @return maximum flow value - */ - double get_max_flow(const edge& e) const; - - /** - * Returns the maximum flow of the whole graph G. - * - * @return maximum flow value - */ - double get_max_flow() const; - - /** - * Returns the remaining free capacity of an edge. - * - * @param e edge of a graph G - * @return remaining capacity - */ - double get_rem_cap(const edge& e) const; - - /** - * Resets maximum flow algorithm, i.e. prepares the - * algorithm to be applied to another graph. - * - * @see algorithm#reset - */ - virtual void reset(); -protected: - /** - * @internal - */ - enum {SP_FOUND = 2, NO_SP_FOUND = 3}; - - /** - * @internal - */ - bool artif_source_target; - - /** - * @internal - */ - bool set_vars_executed; - - /** - * @internal - */ - double max_graph_flow; - - /** - * @internal - */ - node net_source; - - /** - * @internal - */ - node net_target; - - /** - * @internal edges to remove from G after run - */ - edges_t edges_not_org; - - /** - * @internal original edge or inserted back edge - */ - edge_map edge_org; - - /** - * @internal - */ - edge_map back_edge_exists; - - /** - * @internal every edge knows its back edge - */ - edge_map back_edge; - - /** - * @internal - */ - edge_map edge_capacity; - - /** - * @internal - */ - edge_map edge_max_flow; - - /** - * @internal - */ - void create_artif_source_target(graph& G); - - /** - * @internal - */ - void prepare_run(const graph& G); - - /** - * @internal - */ - void comp_single_flow(graph& G, node_map& last_edge); - - /** - * @internal every node knows its predecessor then - */ - int get_sp(const graph& G, node_map& last_edge); - - /** - * @internal - */ - int comp_sp( - const graph& G, - std::queue& next_nodes, - node_map& visited, - node_map& last_edge); - - /** - * @internal - */ - double extra_charge(const node_map& last_edge) const; - - /** - * @internal - */ - void create_back_edge(graph& G, const edge& org_edge); - - /** - * @internal - */ - void comp_max_flow(const graph& G); - - /** - * @internal - */ - void restore_graph(graph& G); -}; - -__GTL_END_NAMESPACE - -#endif // GTL_MAXFLOW_FF_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/maxflow_pp.h b/Tracker/graph/GTL/include/GTL/maxflow_pp.h deleted file mode 100644 index 7cbc5a1a5..000000000 --- a/Tracker/graph/GTL/include/GTL/maxflow_pp.h +++ /dev/null @@ -1,305 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// maxflow_pp.h -// -//========================================================================== -// $Id: maxflow_pp.h,v 1.5 2003/01/31 08:15:05 chris Exp $ - -#ifndef GTL_MAXFLOW_PP_H -#define GTL_MAXFLOW_PP_H - -#include -#include -#include -#include -#include - -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @short Maximum flow algorithm (Malhotra, Kumar, Maheshwari). - */ -class GTL_EXTERN maxflow_pp : public algorithm -{ -public: - /** - * Default constructor. Enables only the calculation of - * maximum flow. - * - * @see algorithm#algorithm - */ - maxflow_pp(); - - /** - * Destructor. - * - * @see algorithm#~algorithm - */ - virtual ~maxflow_pp(); - - /** - * Sets capacity of every edge for maximum flow calculation - * where artificial start-node and end_node will be computed - * automatically. - * - * @param edge_capacity capacity of every edge - */ - void set_vars(const edge_map& edge_capacity); - - /** - * Sets capacity of every edge for maximum flow calculation - * - * @param edge_capacity capacity of every edge - * @param net_source start-node - * @param net_target end-node - */ - void set_vars( - const edge_map& edge_capacity, - const node& net_source, const node& net_target); - - /** - * Checks whether following preconditions are satisfied: - *
    - *
  • @ref maxflow_pp#set_vars has been executed before. - *
  • only edge_capacities >= 0 are applied. - *
  • G is directed. - *
  • G is connected. - *
  • G has at least one edge and two nodes. - *
  • if not applied, start-nodes and end-nodes exists. - *
  • if applied, start-node is not the same node as end-node. - *
- * - * @param G graph - * @return algorithm::GTL_OK on success - * algorithm::GTL_ERROR - * otherwise - * @see algorithm#check - */ - virtual int check(graph& G); - - /** - * Computes maximum flow of graph G. - * - * @param G graph - * @return algorithm::GTL_OK on success - * algorithm::GTL_ERROR otherwise - * @see algorithm#run - */ - int run(graph& G); - - /** - * Returns the maximum flow of an edge. - * - * @param e edge of a graph @c G - * @return maximum flow value - */ - double get_max_flow(const edge& e) const; - - /** - * Returns the maximum flow of the whole graph @c G. - * - * @return maximum flow value - */ - double get_max_flow() const; - - /** - * Returns the remaining free capacity of an edge. - * - * @param e edge of a graph G - * @return remaining capacity - */ - double get_rem_cap(const edge& e) const; - - /** - * Resets maximum flow algorithm, i.e. prepares the - * algorithm to be applied to another graph. - * @see algorithm#reset - */ - virtual void reset(); -protected: - /** - * @internal - */ - enum {TARGET_FROM_SOURCE_REACHABLE = 2, TARGET_FROM_SOURCE_NOT_REACHABLE = 3}; - - /** - * @internal - */ - bool artif_source_target; - - /** - * @internal - */ - bool set_vars_executed; - - /** - * @internal - */ - double max_graph_flow; - - /** - * @internal - */ - node net_source; - - /** - * @internal - */ - node net_target; - - /** - * @internal edges to remove from G after run - */ - edges_t edges_not_org; - - /** - * @internal original edge or inserted back edge - */ - edge_map edge_org; - - /** - * @internal - */ - edge_map back_edge_exists; - - /** - * @internal every edge knows its back edge - */ - edge_map back_edge; - - /** - * @internal - */ - edge_map edge_capacity; - - /** - * @internal - */ - edge_map edge_max_flow; - - /** - * @internal - */ - edge_map flow_update; - - /** - * @internal - */ - edges_t full_edges; - - /** - * @internal - */ - nodes_t temp_unvisible_nodes; - - /** - * @internal - */ - edges_t temp_unvisible_edges; - - /** - * @internal - */ - void create_artif_source_target(graph& G); - - /** - * @internal - */ - void prepare_run(const graph& G); - - /** - * @internal - */ - int leveling(graph& G); - - /** - * @internal - */ - void hide_unreachable_nodes(graph& G); - - /** - * @internal - */ - void store_temp_unvisible_edges(const node& cur_node); - - /** - * @internal - */ - void min_throughput_node(const graph& G, node& min_tp_node, double& min_value); - - /** - * @internal - */ - double comp_min_throughput(const node cur_node) const; - - /** - * @internal every node knows its predecessor then - */ - void get_sp_ahead(const graph& G, const node& start_node, - node_map& last_edge); - - /** - * @internal every node knows its successor then - */ - void get_sp_backwards(const graph& G, const node& start_node, - node_map& prev_edge); - - /** - * @internal - */ - void push(graph& G, const node& start_node, const double flow_value); - - /** - * @internal - */ - void pull(graph& G, const node& start_node, const double flow_value); - - /** - * @internal - */ - void comp_rem_net(graph& G); - - /** - * @internal - */ - void single_edge_update(graph& G, edge cur_edge); - - /** - * @internal - */ - double extra_charge_ahead(const node& start_node, const - node_map& last_edge) const; - - /** - * @internal - */ - double extra_charge_backwards(const node& start_node, - const node_map& prev_edge) const; - - /** - * @internal - */ - void create_back_edge(graph& G, const edge& org_edge); - - /** - * @internal - */ - void comp_max_flow(const graph& G); - - /** - * @internal - */ - void restore_graph(graph& G); -private: -}; - -__GTL_END_NAMESPACE - -#endif // GTL_MAXFLOW_PP_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/maxflow_sap.h b/Tracker/graph/GTL/include/GTL/maxflow_sap.h deleted file mode 100644 index 279e08bd2..000000000 --- a/Tracker/graph/GTL/include/GTL/maxflow_sap.h +++ /dev/null @@ -1,268 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// maxflow_sap.h -// -//========================================================================== -// $Id: maxflow_sap.h,v 1.4 2003/01/31 08:15:05 chris Exp $ - -#ifndef GTL_MAXFLOW_SAP_H -#define GTL_MAXFLOW_SAP_H - -#include -#include -#include -#include -#include - -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @short Maximum flow algorithm with shortest augmenting paths - * - * This class implements a maximum flow algorithm with shortest augmenting - * paths due to Ahuja and Orlin. - * - *

In the case V is the set of vertices and E is the set of edges of - * the graph, the algorithm needs O(|V| * |V| * |E|) time to proceed. - * - * @see maxflow_ff - * @see maxflow_pp - */ -class GTL_EXTERN maxflow_sap : public algorithm -{ -public: - /** - * Default constructor. Enables only the calculation of - * maximum flow. - * - * @see algorithm#algorithm - */ - maxflow_sap(); - - /** - * Destructor. - * - * @see algorithm#~algorithm - */ - virtual ~maxflow_sap(); - - /** - * Sets capacity of every edge for maximum flow calculation - * where artificial start-node and end_node will be computed - * automatically. - * - * @param edge_capacity capacity of every edge - */ - void set_vars(const edge_map& edge_capacity); - - /** - * Sets capacity of every edge for maximum flow calculation - * - * @param edge_capacity capacity of every edge - * @param net_source start-node - * @param net_target end-node - */ - void set_vars(const edge_map& edge_capacity, - const node& net_source, - const node& net_target); - - /** - * Checks whether following preconditions are satisfied: - *

    - *
  • @ref maxflow_sap#set_vars has been executed before. - *
  • only edge_capacities >= 0 are applied. - *
  • G is directed. - *
  • G is connected. - *
  • G has at least one edge and two nodes. - *
  • if not applied, start-nodes and end-nodes exists. - *
  • if applied, start-node is not the same node as end-node. - *
- * - * @param G graph - * @return algorithm::GTL_OK on success - * algorithm::GTL_ERROR otherwise - * @see algorithm#check - */ - virtual int check(graph& G); - - /** - * Computes maximum flow of graph G. - * - * @param G graph - * @return algorithm::GTL_OK on success - * algorithm::GTL_ERROR otherwise - * @see algorithm#run - */ - int run(graph& G); - - /** - * Returns the maximum flow of an edge. - * - * @param e edge of a graph @c G - * @return maximum flow value - */ - double get_max_flow(const edge& e) const; - - /** - * Returns the maximum flow of the whole graph G. - * - * @return maximum flow value - */ - double get_max_flow() const; - - /** - * Returns the remaining free capacity of an edge. - * - * @param e edge of a graph @c G - * @return remaining capacity - */ - double get_rem_cap(const edge& e) const; - - /** - * Resets maximum flow algorithm, i.e. prepares the - * algorithm to be applied to another graph. - * - * @see algorithm#reset - */ - virtual void reset(); -protected: - /** - * @internal - */ - enum {AP_FOUND = 2, NO_AP_FOUND = 3}; - - /** - * @internal - */ - bool artif_source_target; - - /** - * @internal - */ - bool set_vars_executed; - - /** - * @internal - */ - double max_graph_flow; - - /** - * @internal - */ - node net_source; - - /** - * @internal - */ - node net_target; - - /** - * @internal edges to remove from G after run - */ - edges_t edges_not_org; - - /** - * @internal - */ - node_map dist_label; - - /** - * @internal original edge or inserted back edge - */ - edge_map edge_org; - - /** - * @internal - */ - edge_map back_edge_exists; - - /** - * @internal every edge knows its back edge - */ - edge_map back_edge; - - /** - * @internal - */ - edge_map edge_capacity; - - /** - * @internal - */ - edge_map edge_max_flow; - - /** - * @internal - */ - void create_artif_source_target(graph& G); - - /** - * @internal - */ - void prepare_run(const graph& G); - - /** - * @internal - */ - void comp_dist_labels(const graph& G, std::vector& numb); - - /** - * @internal - */ - bool has_an_admissible_arc(const node cur_node); - - /** - * @internal - */ - void advance(node& cur_node, node_map& last_edge); - - /** - * @internal - */ - void augment(graph& G, const node_map& last_edge); - - /** - * @internal - */ - bool retreat(const int number_of_nodes, - node& cur_node, - const node_map& last_edge, - std::vector& numb); - - /** - * @internal - */ - int min_neighbour_label(const int number_of_nodes, - const node cur_node) const; - - /** - * @internal - */ - double free_capacity(const node_map& last_edge) const; - - /** - * @internal - */ - void create_back_edge(graph& G, const edge& org_edge); - - /** - * @internal - */ - void comp_max_flow(const graph& G); - - /** - * @internal - */ - void restore_graph(graph& G); -}; - -__GTL_END_NAMESPACE - -#endif // GTL_MAXFLOW_SAP_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/min_tree.h b/Tracker/graph/GTL/include/GTL/min_tree.h deleted file mode 100644 index 1dee4480b..000000000 --- a/Tracker/graph/GTL/include/GTL/min_tree.h +++ /dev/null @@ -1,108 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// min_tree.cpp -// -//========================================================================== -// $Id: min_tree.h,v 1.3 2001/06/21 10:55:08 chris Exp $ - -#ifndef GTL_MIN_TREE_H -#define GTL_MIN_TREE_H - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @brief Kruskal's %algorithm for finding minimal spanning tree - * of a %graph. - * - * @author Marcus Raitner - */ -class min_tree: public algorithm { - -public: - - /** - * @brief Constructor - */ - min_tree (); - - /** - * @brief Destructor - */ - virtual ~min_tree () {}; - - /** - * @brief Checks whether %algorithm can be applied. - * - * The %graph must - * - be undirected - * - be connected - * - have more than 2 nodes - * - * Additionally the weights of the edges must have been set - * in advance using min_tree::set_distances. - * - * @param g graph - * @return algorithm::GTL_OK if %algorithm can be applied - * algorithm::GTL_ERROR otherwise. - */ - int check (graph& g); - - int run (graph& g); - - virtual void reset (); - - /** - * @brief Sets %edge weights. - * - * Setting of %edge weights must be done before calling - * min_tree::check and min_tree::run. - * - * @param dist %edge weigths. - */ - void set_distances (const edge_map& dist); - - /** - * @brief Edges of minimal spanning tree calculated in the - * last call of min_tree::run. - * - * @return Set of edges of representing the minimal spanning - * tree - */ - std::set get_min_tree(); - - /** - * @brief Weight of minimal spanning tree. - * - * @return weight of minimal spanning tree. - */ - int get_min_tree_length(); - -private: - typedef std::pair TSP_A_VALUE; - - class input_comp { - public: - bool operator()(TSP_A_VALUE x, TSP_A_VALUE y) - { return x.first > y.first;} - }; - - edge_map dist; - int weight; - std::set tree; - bool is_set_distances; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_MIN_TREE_H - - - - - diff --git a/Tracker/graph/GTL/include/GTL/ne_map.h b/Tracker/graph/GTL/include/GTL/ne_map.h deleted file mode 100644 index c32e201bc..000000000 --- a/Tracker/graph/GTL/include/GTL/ne_map.h +++ /dev/null @@ -1,190 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// ne_map.h - common implementation of node_map and edge_map -// -//========================================================================== -// $Id: ne_map.h,v 1.20 2005/06/14 12:22:12 raitner Exp $ - -#ifndef GTL_NE_MAP_H -#define GTL_NE_MAP_H - -#include - -#include -#include - -//-------------------------------------------------------------------------- -// Class declaration -//-------------------------------------------------------------------------- - -__GTL_BEGIN_NAMESPACE - -/** - * @short Baseclass for node_map and edge_map - * - * ne_map is the common implementation of @ref node_map - * and @ref edge_map and cannot be used directly. - */ - -template > -class ne_map -{ -protected: - - //================================================== Constructors - - /** - * Constructs an empty ne_map not associated to any - * graph. - */ - ne_map(); - - /** - * Constructs a ne_map associated to the - * graph g. The value associated to each key is set - * to def. - * You may (but need not) call - * ne_map::init(const graph &, T) to associate it to - * a graph. - * - * @param g associated graph - * @param def default value - */ - explicit ne_map(const Graph &g, Value def=Value()); - - //================================================== Operations - -public: - - /** - * Initializes the ne_map to hold information for the elements - * of graph g. def is the value associated with all elements. - * - * @param g associated graph - * @param def default value - */ - void init(const Graph &, Value def=Value()); - - /** - * @internal - */ -#if defined(__GTL_MSVCC) && _MSC_VER < 1310 - typedef Value& value_reference; -#else - typedef typename std::vector::reference value_reference; -#endif - - /** - * @internal - */ -#if defined(__GTL_MSVCC) && _MSC_VER < 1310 - typedef const Value& const_value_reference; -#else - typedef typename std::vector::const_reference const_value_reference; -#endif - - /** - * Read/write accessor function to the value associated with - * key. - * Use this function to change the value of an element in the - * ne_map. Assume that ne is a - * ne_map<int>. Then you can assign the value - * 5 to key with: - *
-     *   ne[key] = 5;
-     * 
- * - * If there is no entry in the ne_map associated - * with key, one is created. - * - * @param key Key of the Entry to change - * @return a reference to the value associated to key. - */ - value_reference operator[](Key key); - - /** - * Read-only accessor function to the value associated with - * key. - * Use this function to read the value of an element in the - * ne_map. Assume that ne is a - * ne_map<int>. Then you can print the value - * associated with key with: - *
-     *   cout << ne[key];
-     * 
- * - * @param key Key of the Entry to look up - * @return a const reference to the value associated to - * key. - */ - const_value_reference operator[](Key key) const; - - /** - * Erases a elements of this nodemap - */ - void clear (); - - //================================================== Implementation - -private: - std::vector data; -}; - -// Implementation Begin - -template - ne_map::ne_map() -{ -} - -template -ne_map::ne_map(const Graph &g, Value t2) : - data(g.number_of_ids(Key()), t2) -{ -} - -template -void ne_map::init(const Graph &g, Value t2) -{ - int n = g.number_of_ids(Key()); - data.resize(n); - fill_n(data.begin(), n, t2); -} - -template -typename ne_map::value_reference ne_map::operator[](Key t1) -{ - if(t1.id() >= (signed)data.size()) - { - if (t1.id() >= (signed)data.capacity()) { - data.reserve((6 * t1.id()) / 5 + 1); - } - - data.insert(data.end(), t1.id()+1-data.size(), Value()); - } - return data.operator[](t1.id()); -} - -template -typename ne_map::const_value_reference ne_map::operator[](Key t1) const -{ - assert(t1.id() < (signed)data.size()); - return data.operator[](t1.id()); -} - -template -void ne_map::clear () -{ - data.clear(); -} - -// Implementation End - -__GTL_END_NAMESPACE - -#endif // GTL_NE_MAP_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/node.h b/Tracker/graph/GTL/include/GTL/node.h deleted file mode 100644 index 648bf016e..000000000 --- a/Tracker/graph/GTL/include/GTL/node.h +++ /dev/null @@ -1,345 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// node.h -// -//========================================================================== -// $Id: node.h,v 1.20 2003/11/27 13:36:56 raitner Exp $ - -#ifndef GTL_NODE_H -#define GTL_NODE_H - -#include -#include - -#include - -__GTL_BEGIN_NAMESPACE - -//-------------------------------------------------------------------------- -// For MSVC 5.0 node.h has to be included before {node,edge}_data.h. -// So we only declare node_data here -//-------------------------------------------------------------------------- - -class node_data; - -//-------------------------------------------------------------------------- -// The first alternative is correct. The second one is a workaround -// for compilers that don't support namespaces and use the SGI STL -// (i.e. gcc/egcs) -//-------------------------------------------------------------------------- - -#ifdef __GTL_USE_NAMESPACES - -class node; -typedef std::iterator bi_iter_edge; -typedef std::iterator bi_iter_node; - -#else - -class node; -typedef bidirectional_iterator bi_iter_edge; -typedef bidirectional_iterator bi_iter_node; - -#endif // __GTL_USE_NAMESPACES - -//-------------------------------------------------------------------------- -// nodes -//-------------------------------------------------------------------------- - -/** - * @short A node in a graph - */ -class GTL_EXTERN node -{ -public: - /** - * Default constructor. Creates an invalid node. - * The only way to obtain a valid node is through @ref - * graph#new_node Example: - *
-     *   graph g;
-     *   node n;
-     *
-     *   n = g.new_node();
-     * 
- * - * @see graph#new_node - */ - node(); - - /** - * Returns the degree of the node, i. e. - * @ref node#outdeg + @ref node#indeg . - */ - int degree() const; - - /** - * Returns the out degree of the node, i. e. the number of outgoing edges. - */ - int outdeg() const; - - /** - * Returns the in degree of the node, i. e. the number of incoming edges. - */ - int indeg() const; - - /** - * @internal - */ - int id() const; - - /** - * Returns the node on the opposite side of e. - * - * @param e an edge incident to the node - */ - const node& opposite(edge e) const; - - /** - * @internal - */ - nodes_t opposites(edge) const; - - /** - * Returns true iff node is hidden. - * - * @return true iff node is hidden. - * @see graph#hide_edge - * @see graph#restore_edge - */ - bool is_hidden () const; - - /** - * Returns the excentricity of the node, i.e. the maximum graph-theoretic - * distance to another node - * - * @return excentricity of node. - */ - int excentricity() const; - - //================================================== Iterator types - - /** - * @internal - */ - typedef edges_t::const_iterator in_edges_iterator; - /** - * @internal - */ - typedef edges_t::const_iterator out_edges_iterator; - - /** - * @internal - */ - class inout_edges_iterator; - - /** - * @internal - */ - class adj_nodes_iterator; - - /** - * @internal - */ - class adj_edges_iterator; - - //================================================== Iterators - - /** - * Iterate through all adjacent nodes. - * - * @return start for iteration through all adjacent nodes - */ - adj_nodes_iterator adj_nodes_begin() const; - - /** - * Iterate through all adjacent nodes. - * - * @return end for iteration through all adjacent nodes - */ - adj_nodes_iterator adj_nodes_end() const; - - /** - * Iterate through all adjacent edges. - * - * @return start for iteration through all adjacent edges - */ - adj_edges_iterator adj_edges_begin() const; - - /** - * Iterate through all adjacent edges. - * - * @return end for iteration through all adjacent edges - */ - adj_edges_iterator adj_edges_end() const; - - /** - * Iterate through all incoming edges. - * - * @return start for iteration through all incoming edges - */ - in_edges_iterator in_edges_begin() const; - - /** - * Iterate through all incoming edges. - * - * @return end for iteration through all incoming edges - */ - in_edges_iterator in_edges_end() const; - - /** - * Iterate through all outgoing edges. - * - * @return start for iteration through all outgoing edges - */ - out_edges_iterator out_edges_begin() const; - - /** - * Iterate through all outgoing edges. - * - * @return end for iteration through all outgoing edges - */ - out_edges_iterator out_edges_end() const; - - /** - * Iterate through all incoming and outgoing edges. - * - * @return start for iteration through all incoming and outgoing edges - */ - inout_edges_iterator inout_edges_begin() const; - - /** - * Iterate through all incoming and outgoing edges. - * - * @return end for iteration through all incoming and outgoing edges - */ - inout_edges_iterator inout_edges_end() const; - - //================================================== Implementation - -private: - node_data *data; - - bool is_directed() const; - bool is_undirected() const; - - friend class graph; - friend class edge; - friend class adj_edges_iterator; - - GTL_EXTERN friend bool operator==(node, node); - GTL_EXTERN friend bool operator!=(node, node); - GTL_EXTERN friend bool operator<(node, node); - GTL_EXTERN friend std::ostream& operator<< (std::ostream& os, const node& n); -}; - -/** - * @short Iterator for adjacent edges of a node - */ -class GTL_EXTERN node::adj_edges_iterator : public bi_iter_edge -{ -public: - - // constructor - adj_edges_iterator(); - adj_edges_iterator(node, bool); - - // comparibility - bool operator==(const adj_edges_iterator&) const; - bool operator!=(const adj_edges_iterator&) const; - - // operators - adj_edges_iterator &operator++(); - adj_edges_iterator operator++(int); - adj_edges_iterator &operator--(); - adj_edges_iterator operator--(int); - - // dereferencing - const edge& operator*() const; - const edge* operator->() const; - -private: - in_edges_iterator akt_edge[2], last_edge[2], begin_edge[2]; - int inout; // in=0, out=1 - bool directed; // graph directed ?? -}; - -/** - * @short Iterator for all incident edges of a node - */ -class GTL_EXTERN node::inout_edges_iterator : public bi_iter_edge -{ -public: - - // constructor - inout_edges_iterator(); - inout_edges_iterator(node n, bool start); - - // comparibility - bool operator==(const inout_edges_iterator&) const; - bool operator!=(const inout_edges_iterator&) const; - - // operators - inout_edges_iterator &operator++(); - inout_edges_iterator operator++(int); - inout_edges_iterator &operator--(); - inout_edges_iterator operator--(int); - - // dereferencing - const edge& operator*() const; - const edge* operator->() const; - -private: - in_edges_iterator akt_edge[2], last_edge, begin_edge; - int inout; // in=0, out=1 -}; - -/** - * @short Iterator for adjacent nodes of a node - */ -class GTL_EXTERN node::adj_nodes_iterator : public bi_iter_node -{ -public: - - // constructor - adj_nodes_iterator(); - adj_nodes_iterator(const node&, bool); - - // comparibility - bool operator==(const adj_nodes_iterator&) const; - bool operator!=(const adj_nodes_iterator&) const; - - // operators - adj_nodes_iterator &operator++(); - adj_nodes_iterator operator++(int); - adj_nodes_iterator &operator--(); - adj_nodes_iterator operator--(int); - - // dereferencing - const node& operator*() const; - const node* operator->() const; - -private: - adj_edges_iterator akt_edge; - node int_node; -}; - - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// Iteration -//-------------------------------------------------------------------------- - -// #define forall_adj_nodes(v,w) GTL_FORALL(v,w,node::adj_nodes_iterator,adj_nodes_) -#define forall_out_edges(e,v) GTL_FORALL(e,v,node::out_edges_iterator,out_edges_) -#define forall_in_edges(e,v) GTL_FORALL(e,v,node::in_edges_iterator,in_edges_) -#define forall_inout_edges(e,v) GTL_FORALL(e,v,node::inout_edges_iterator,inout_edges_) -#define forall_adj_edges(e,v) GTL_FORALL(e,v,node::adj_edges_iterator,adj_edges_) - -#endif // GTL_NODE_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/node_data.h b/Tracker/graph/GTL/include/GTL/node_data.h deleted file mode 100644 index 251316a09..000000000 --- a/Tracker/graph/GTL/include/GTL/node_data.h +++ /dev/null @@ -1,42 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// node_data.h - Internal header: DO NO USE IT DIRECTLY !!! -// -//========================================================================== -// $Id: node_data.h,v 1.7 2000/01/05 16:32:38 raitner Exp $ - -#ifndef GTL_NODE_DATA_H -#define GTL_NODE_DATA_H - -#include -#include -#include - -#include - -__GTL_BEGIN_NAMESPACE - -class graph; - -/** - * @internal - */ -class GTL_EXTERN node_data -{ -public: - int id; // internal numbering - graph *owner; // graph containing this node - nodes_t::iterator pos; // position in the list of all nodes - edges_t edges[2]; // edges incident to this node - // edges[0] = in_edges, edges[1] = out_edges - bool hidden; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_NODE_DATA_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/node_map.h b/Tracker/graph/GTL/include/GTL/node_map.h deleted file mode 100644 index 07fe8f489..000000000 --- a/Tracker/graph/GTL/include/GTL/node_map.h +++ /dev/null @@ -1,81 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// node_map.h -// -//========================================================================== -// $Id: node_map.h,v 1.8 2005/06/14 12:22:12 raitner Exp $ - -#ifndef GTL_NODE_MAP_H -#define GTL_NODE_MAP_H - -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -class graph; - -/** - * @short A specialized map with nodes as keys - * - * A node_map is a specialized and optimized map - * implementation with nodes as keys. Using a node_map is - * the standard way to attach user defined information to - * the nodes of a graph. - * - * An example of usage: - *
- *   graph g;
- *
- *   node v1 = g.new_node();
- *   node v2 = g.new_node();
- *
- *   node_map<string> label(g, "Default Label");
- *
- *   label[v1] = "v1";
- *   label[v2] = "v2";
- *
- *   assert(label[v1] != label[v2]);
- * 
- * - * The nodes used as keys for a node_map MUST be nodes - * of the same graph. If you want to use nodes from different graphs, use - * a map<node,T> instead. A graph and a copy of it are - * considered to be different. - * - * Most of the functionality of node_map is inherited from - * @ref ne_map. - * - * @see edge_map - */ -template > -class node_map : public ne_map -{ -public: - - /** - * Constructs an empty node_map not associated with any - * graph. You may (but need not) call - * ne_map::init(const graph &, T) to associate it to - * a graph. - */ - node_map() : ne_map() {}; - - /** - * Constructs a node_map associated to the graph - * g. - * The value associated to each node in g is set to - * t. - */ - explicit node_map(const graph &g, T t=T()) : ne_map(g,t) {}; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_NODE_MAP_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/planarity.h b/Tracker/graph/GTL/include/GTL/planarity.h deleted file mode 100644 index b8caac878..000000000 --- a/Tracker/graph/GTL/include/GTL/planarity.h +++ /dev/null @@ -1,620 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// planarity.h -// -//========================================================================== -// $Id: planarity.h,v 1.22 2008/02/03 18:17:08 chris Exp $ - -#ifndef PLANARITY_H -#define PLANARITY_H - -#include -#include -#include -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * $Date: 2008/02/03 18:17:08 $ - * $Revision: 1.22 $ - * - * @brief Tests if a %graph can be drawn on a plane without any %edge - * crossings - * - * This class implements the Lempel-Even-Cederbaum %planarity test using - * PQ-trees. In case the %graph is planar a planar embedding is obtained, - * i.e. for each %node in the %graph an ordered adjacency list is calculated, - * such that there exists a planar drawing in which all adjacent edges - * around a %node apply to this order. - * - * If the %graph is not planar Kuratowski's famous theorem states that it - * must contain a subgraph hoemeomorphic to either K5 (the complete %graph - * with five nodes) or K3,3 (the complete bipartite %graph with three nodes - * each side). In this case the nodes and edges of the tested %graph that - * form either of these two are calculated. - * - * In case the %graph is planar and has @f$N@f$ nodes the algorithm needs - * @f$\mathcal{O}(N)@f$ time for the test (including the planar embedding). - * In case the %graph isn't planar it needs at most @f$\mathcal{O}(E)@f$ - * time if @f$E@f$ is the number of edges for both the test and the - * detection of K5 or K3,3. - */ -class GTL_EXTERN planarity : public algorithm -{ -public: - /** - * @brief Creates an object of the planarity test %algorithm. - * - * @sa algorithm - */ - planarity(); - - /** - * @brief Destructor - */ - ~planarity(); - - /** - * @brief Checks whether planarity test can be applied to @p G. - * - * This should return always @c GTL_OK. There aren't any - * restrictions on @p G, even multiple edges and selfloops - * are tolerated. - * - * @note Selfloops and multiple edges will not be added to - * the planar embedding. planar_embedding::selfloops and - * planar_embedding::multiple_edges can be used to get - * these. - * - * @param G arbitrary %graph - * - * @retval GTL_OK if %planarity test can be applied - * @retval GTL_ERROR if not - * - * @sa algorithm#check - */ - int check(graph& G); - - /** - * @brief Runs planarity test on @p G. - * - * This should return always @c GTL_OK. The return value only - * tracks errors that might occur, it is definitly @em not - * the result of the test itself. The result of the test is - * stored in a member variable and can be accessed via - * #is_planar. - * - * @param G arbitrary %graph - * - * @retval GTL_OK if %planarity test was sucessfully applied - * @retval GTL_ERROR if not - * - * @sa algorithm::run - */ - int run(graph& G); - - /** - * @brief Resets algorithm object, such that it can be applied to - * another graph. - * - * @sa algorithm::reset - */ - void reset(); - - /** - * @brief If @p p is true a planar embedding will be calculated in - * the next run. - * - * @param p @c true iff embedding should be calculated - * - * @sa #get_embedding - * @sa planar_embedding - */ - void calc_embedding(bool p) - { - emp = p; - if (!emp) kup = false; - } - - /** - * @brief Returns true if a planar embedding will be calculated in - * the next run. - * - * @retval true iff embedding will be calculated - * - * @sa #get_embedding - * @sa planar_embedding - */ - bool calc_embedding () const - { return emp; } - - /** - * @brief If @p p is true the obstructions to %planarity will be - * calculated in the next %run. - * - * This implies the calculation of an embedding. - * - * @param p @c true iff obstructions to %planarity should be calculated - * - * @sa #get_obstruction_edges - * @sa #get_obstruction_nodes - */ - void calc_obstruction(bool p) - { - kup = p; - if (kup) emp = true; - } - - /** - * @brief Returns true if the obstructions to %planarity will be - * calculated in the next %run. - * - * @retval true iff obstructions to %planarity will be calculated - * - * @sa #get_obstruction_edges - * @sa #get_obstruction_nodes - */ - bool calc_obstruction() const - { - return kup; - } - - /** - * @brief Determines the strategy used to test a graph which is not - * biconnected. - * - * If this is enabled the graph will be made biconnected by - * adding some new edges. This is usually faster than testing - * the biconnected components one by one, which is done if - * this option is disabled. By default this is enabled. - * - * @note This is not fully tested, i.e. at the moment this - * feature should be used only for the test without embedding - * or kuratowski graphs. - * - * @param p true iff %graph should be made biconnected - * - * @sa biconnectivity::make_biconnected - */ - void make_biconnected(bool p) - { - bip = p; - } - - /** - * @brief Returns strategy for testing graphs, which are not - * biconnected. - * - * @retval true iff graph will be made biconnected before test - * - * @sa biconnectivity#make_biconnected - */ - bool make_biconnected() const - { - return bip; - } - - /** - * @brief Result of last test. - * - * @retval true iff %graph in last %run was planar. - */ - bool is_planar() const - { - return planar; - } - - /** - * @brief If %graph in last #run was planar a planar embedding is - * calculated during the reductions. This function gives access to it. - * - * @return planar embedding of %graph in last %run - * - * @sa #calc_embedding - */ - planar_embedding& get_embedding() - { - return embedding; - } - - /** - * @brief Returns the edges of a subgraph homeomorphic to - * either K3,3 or K5 if %graph in last %run was not planar. - * - * @return edges of subgraph homeomorphic to either K3,3 or K5 - * - * @sa #get_obstruction_nodes - * @sa #calc_obstruction - */ - edges_t& get_obstruction_edges() - { - return ob_edges; - } - - /** - * @brief Returns the nodes of a subgraph homeomorphic to - * either K3,3 or K5 if %graph in last %run was not planar. - * - * @return nodes of subgraph homeomorphic to either K3,3 or K5 - * - * @sa #get_obstruction_edges - * @sa #calc_obstruction - */ - nodes_t& get_obstruction_nodes() - { - return ob_nodes; - } -private: - /** - * @internal - * Main procedure for planarity test. Assumes @p G to be undirected and - * biconnected. Used to test whether the biconnected components of a - * %graph are planar. - * - * @param G biconnected, undirected graph - * @param em planar embedding (should be empty) - * - * @retval true if @c G is planar - */ - bool run_on_biconnected(graph& G, planar_embedding& em); - - /** - * @internal - * Adds the embedding for component @c G to the embedding of the whole - * %graph. - * - * @param G biconnected graph - * @param em embedding obtained through testing @p G - */ - void add_to_embedding(graph& G, planar_embedding& em); - - /** - * @internal - * The so called upward embedding can be obtained from the list of edges - * one gets in the reduction steps of the %algorithm. The only problem - * is that some of these lists may be turned later in the algorithm. - * This procedure corrects the reversions according to the information - * stored in @p dirs. - * - * @param em embedding - * @param st st-numbers of biconnected %graph - * @param dirs direction indicators obtained after each reduction - */ - void correct_embedding(planar_embedding& em, - st_number& st, - node_map >& dirs); - - /** - * @internal - * After the embedding has been corrected by the above procedure, we - * have a so called upward embedding, this means only the edges leading - * to nodes with smaller st-number than itself are in the adjacency list - * for some node. This procedure extends the upward embedding @p em to a - * full embedding. This is a recursive procedure (well basically it's a - * DFS starting at the %node with the highest st-number). - * - * @param n current node (used for recursion) - * @param em embedding (at the beginning an upward embedding) - * @param mark marks used nodes in DFS. - * @param upward_begin marks the beginning of the upward embedding - */ - void extend_embedding( - node n, - planar_embedding& em, - node_map& mark, - node_map::iterator >& upward_begin); - - /** - * @internal - * Make @p G the component specified in @p it by hiding everything not - * in this subgraph. For the sake of efficiency the whole graph is - * hidden at the beginning and then only what is in this component is - * restored. - * - * @param G whole graph; partially hidden - * @param it component to highlight - * - * @sa graph::hide - */ - void switch_to_component(graph& G, - biconnectivity::component_iterator it); - - /** - * @internal - * Main procedure for detecting K5 or K3,3. Many cases have to be taken - * into account so it is split in a lot of subroutines decribed below. - * - * @param G biconnected graph. - * @param st st-numbers of @p G - * @param act node for which the reduction failed - * @param fail (PQ-) node at which no matching could be applied - * @param failed_at_root @c true iff @p fail is the root of the - * pertinent subtree. - * @param em planar embedding obtained up to the moment the matchings - * stopped - * @param dirs direction indicators obtained up to the moment the - * matchings stopped - * @param PQ tree - */ - void examine_obstruction(graph& G, - st_number& st, - node act, - pq_node* fail, - bool failed_at_root, - planar_embedding& em, - node_map >& dirs, - pq_tree* PQ); - - /** - * @internal - * Calculates a DFS-tree for the so called bush-form for the node with - * st-number @p stop, i.e. the induced subgraph consisting of all nodes - * with st-number smaller than @p stop and all edges from one of these - * to a higher numbered node lead to a virtual node with that number - * (there may be duplicates). - * - * @param act used in recursion; starts with node numbered 1 - * @param mark marks for DFS; initially for all nodes 0 - * @param st st-numbers for graph - * @param stop lowest st-number of virtual nodes - * @param to_father stores the edge to predecessor of each node - */ - void dfs_bushform(node act, - node_map& mark, - st_number& st, - int stop, - node_map& to_father); - - - /** - * @internal - * In case the reduction failed at a Q-node the boundary of the - * biconnected component the Q-node represents can be obtained from @p - * em. - * No return value is needed, since all the edges on the boundary are - * added to the obstruction edges (although some of them have to be - * deleted in some cases). - * - * @param n node with lowest st-number in biconnected component - * @param em planar embedding (at least for this component) - */ - void attachment_cycle (node n, planar_embedding& em); - - /** - * @internal - * Marks all neighbors of leaves in the subtree rooted at @p n. - * In some cases where the reduction fails at a Q-node, which is not the - * root of the pertinent subtree, an adjacent edge of the node for which - * the reduction failed, which does not lead to that component has to be - * found. - * - * @param n root of subtree - * @param mark edges in subtree recieve 1, all other are unchanged. - */ - void mark_all_neighbors_of_leaves (pq_node* act, node_map& mark); - - /** - * @internal - * Searches one full and one empty leaf beneath @p partial. The join of - * these leaves and the node on the boundary @p v to which @p partial is - * attached is added to the obstruction nodes. All edges that form this - * join are added to the obstruction edges. - * - * @param partial partial %node - * @param mark nodes already used - * @param to_father predecessor relation in DFS tree - * @param v node on the boundary - * @return empty leaf - */ - pq_leaf* run_through_partial(q_node* partial, - node_map& mark, - node_map& to_father, - node v); - - /** - * @internal - * Uses @p to_father to determine an already marked predecessor. - * - * @param act node - * @param mark nodes already used - * @param to_father predecessor relation in DFS tree - * - * @return marked node - */ - node up_until_marked(node act, - node_map& mark, - node_map& to_father); - - /** - * @internal - * Always uses a adjacent node with higher st-number as predecessor. - * Searches marked predecessor. - * - * @param act node - * @param mark nodes already used - * @param st used to determine predecessor - * - * @return marked node - */ - node up_until_marked(node act, - node_map& mark, - st_number& st); - - /** - * @internal - * Assumes that @p n is non empty. Searches full leaf beneath @p n. - * - * @param n (PQ-) node - * - * @return full leaf in subtree of @p n - */ - pq_leaf* search_full_leaf (pq_node* n); - - /** - * @internal - * Assumes that @p n is non full. Searches empty leaf beneath @p n. - * - * @param n (PQ-) node - * - * @return empty leaf in subtree of @p n - */ - pq_leaf* search_empty_leaf(pq_node* n); - - /** - * @internal - * Reduction failed at a P-%node, which had at least three pertial - * sons. - * - * @param p_fail P-%node at which reduction failed - * @param act node for which reduction failed - * @param _st st-numbers of graph - * @param to_father predecessors in DFS-tree of bushform - * @param G graph tested - */ - void case_A(p_node* p_fail, - node act, - st_number& _st, - node_map to_father, - graph& G); - - /** - * @internal - * Reduction failed at a P-%node, which isn't the root of the pertinent - * subtree and had at least two partial children. - * - * @param p_fail P-%node at which reduction failed - * @param act node for which reduction failed - * @param _st st-numbers of graph - * @param to_father predecessors in DFS-tree of bushform - * @param G graph tested - */ - void case_B(p_node* p_fail, - node act, - st_number& _st, - node_map to_father, - graph& G); - - /** - * @internal - * Reduction failed at a Q-node, such that there exist children a < b < - * c and a and c are both non-empty and b is non-full. - * - * @param nodes nodes on the boundary of @p q_fail to which the sons a, - * b, c are attached. - * @param leaves leaves in the subtrees of a, b, c. For a and c full - * leaves and an empty one for b. - * @param _st st-numbers of graph - * @param to_father predecessors in DFS-tree of bushform - * @param G graph tested - * @param q_fail Q-node at which reduction failed - */ - void case_C(node* nodes, - pq_leaf** leaves, - st_number& _st, - node_map to_father, - graph& G, - q_node* q_fail); - - /** - * @internal - * Reduction failed at a non-root Q-node, such that there exist children - * a < b < c and a and c are both non-full and b is non-empty. - * - * @param nodes nodes on the boundary of @p q_fail to which the sons a, - * b, c are attached. - * @param leaves leaves in the subtrees of a, b, c. For a and c full - * leaves and an empty one for b. - * @param _st st-numbers of graph - * @param to_father predecessors in DFS-tree of bushform - * @param G graph tested - * @param q_fail Q-node at which reduction failed - */ - void case_D(node* nodes, - pq_leaf** leaves, - st_number& _st, - node_map to_father, - graph& G, - q_node* q_fail); - - /** - * @internal - * Reduction failed at a non-root Q-node which has only two children, - * both partial. - * - * @param nodes nodes on the boundary of @p q_fail to which the two - * partial sons are attached. - * @param leaves two leaves in each subtree of a partial son. One full - * other empty. - * @param _st st-numbers of graph - * @param to_father predecessors in DFS-tree of bushform - * @param G graph tested - * @param q_fail Q-node at which reduction failed - */ - void case_E(node* nodes, - pq_leaf** leaves, - st_number& _st, - node_map to_father, - graph& G, - q_node* q_fail); - -#ifdef _DEBUG - /** - * @internal - */ - void write_bushform(graph& G, st_number& _st, int k, const char* name, - const node_map& mark, const node_map& to_father); - - /** - * @internal - */ - void write_node(std::ostream& os, int id, int label, int mark); -#endif - - /** - * @internal - */ - edges_t ob_edges; - - /** - * @internal - */ - nodes_t ob_nodes; - - /** - * @internal - */ - planar_embedding embedding; - - /** - * @internal - */ - bool planar; - - /** - * @internal - */ - bool emp; - - /** - * @internal - */ - bool kup; - - /** - * @internal - */ - bool bip; -}; - -__GTL_END_NAMESPACE - -#endif // PLANARITY_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/pq_node.h b/Tracker/graph/GTL/include/GTL/pq_node.h deleted file mode 100644 index f7bbf9f65..000000000 --- a/Tracker/graph/GTL/include/GTL/pq_node.h +++ /dev/null @@ -1,789 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// pq_node.h -// -//========================================================================== -// $Id: pq_node.h,v 1.15 2003/04/03 11:48:26 raitner Exp $ - -#ifndef PQ_NODE_H -#define PQ_NODE_H - -#include -#include -#include - -#include -#include - -__GTL_BEGIN_NAMESPACE - -class pq_tree; -class p_node; -class q_node; -class pq_leaf; -class direction_indicator; - -/** - * @internal - */ -class GTL_EXTERN pq_node -{ -protected: - /** - * @internal - */ - typedef symlist::iterator iterator; - - /** - * @internal - */ - enum PQ_KIND {P_NODE, Q_NODE, LEAF, DIR}; - - /** - * @internal - */ - enum PQ_MARK {UNMARKED, QUEUED, BLOCKED, UNBLOCKED}; - - /** - * @internal - */ - pq_node (node n_, int id_) : pert_children(0), - pert_leaves(0), - mark (UNMARKED), - n (n_), - id (id_) - { - } - - /** - * @internal - */ - virtual ~pq_node (); - - /** - * @internal - * Used to identify nodes. - */ - virtual PQ_KIND kind() const = 0; - - /** - * @internal - * Called whenever a son is known to be partial during reduction phase. - */ - virtual void partial(iterator) - { - } - - /** - * @internal - * Called whenever a son is known to be full during reduction phase. - */ - virtual void full(iterator) - { - } - - /** - * @internal - * Used to write a description of this node into a stream. - */ - virtual void write(std::ostream&, int) = 0; - - /** - * @internal - * Reset node for next reduction. - */ - virtual void clear() - { - mark = UNMARKED; - pert_leaves = 0; - pert_children = 0; - } - - // type-casts - - /** - * @internal - * Interface type-cast to P-node. - */ - virtual p_node* P() = 0; - - /** - * @internal - * Interface type-cast to Q-node. - */ - virtual q_node* Q() = 0; - - /** - * @internal - * Interface type-cast to direction indicator. - */ - virtual direction_indicator* D() = 0; - - /** - * @internal - * Interface type-cast to PQ-leaf. - */ - virtual pq_leaf* L() = 0; - - // - // Data used in reductions - // - - /** - * @internal - * Number of pertinent children; is calculated during bubble-up phase - * and gets decreased whenever a pertinent child is matched in reduction - * phase, such that it can be assured that this node is matched @em - * after all its pertinent children were correctly matched. - */ - int pert_children; - - /** - * @internal - * Number of pertinent leaves in the subtree rooted at this node; is - * calculated in the reduction phase and is used to determine the root - * of the pertinent subtree, i.e. the last node for template matchings. - */ - int pert_leaves; - - /** - * @internal - * For Q-nodes it is not acceptable to maintain father pointers for @em - * all sons (cf. Booth, Luecker); fortunatly this isn't neccessary and - * the father pointer is only valid if is_endmost is true. For the sons - * of a Q-node is_endmost is only true for the first and the last son. - * For the sons of P-nodes ths flag is always true. - */ - bool is_endmost; - - /** - * @internal - * The main operations on PQ-trees are performed bottom up so each node - * should know its father; Because of complexity issuses this isn't - * always possible and thus father is valid iff is_endmost is true. - */ - pq_node* father; - - /** - * @internal - * Describes the role this node plays in the reduction at the moment; - * four states are possible: - * -# @c UNMARKED: node wasn't touched so far - * -# @c BLOCKED: during bubble-up phase this node got queued, but as - * yet it was not possible to get a valid father pointer - * -# @c UNBLOCKED: node was touched during bubble-up and it either had - * a valid father pointer or one could be borrowed from one of its - * siblings - * -# @c QUEUED: node has been put into the queue - */ - PQ_MARK mark; - - /** - * @internal - * List of sons. - */ - symlist sons; - - /** - * @internal - * Position in the list of sons of node's father. - */ - iterator pos; - - /** - * @internal - * Position in the list of nodes to be cleared in reset. Each node - * touched in #bubble-up phase is stored in the list of nodes to be - * cleared. As they get matched in the reduction phase they are cleared - * and deleted from this list. But even if the reduction is successful - * not all nodes touched in the first phase really get matched. - */ - std::list::iterator lpos; - - // - // Application specific data (should become template parameter) - // - - /** - * @internal - * Node of the graph which this PQ-node represents. - */ - node n; - - /** - * @internal - */ - int id; - - /** - * @internal - */ - node up; - - /** - * @internal - */ - int up_id; - - // - // Friends - // - - /** - * @internal - * Allow q_node private access. - */ - friend class q_node; - - /** - * @internal - * Allow p_node private access. - */ - friend class p_node; - - /** - * @internal - * Allow my_pq_tree private access. - */ - friend class pq_tree; - - /** - * @internal - * Allow planarity private access. - */ - friend class planarity; - - /** - * @internal - * Allow operator<< private access. - */ - GTL_EXTERN friend std::ostream& operator<<(std::ostream&, const pq_tree&); -}; - - -/** - * @internal - */ -class GTL_EXTERN p_node : public pq_node -{ -private: - /** - * @internal - */ - p_node(node, int); - - /** - * @internal - */ - p_node(node, int, symlist&); - - // - // pq_node interface - // - - /** - * @internal - */ - void partial(iterator); - - /** - * @internal - */ - void full(iterator); - - /** - * @internal - * Determines kind of this %node. - */ - PQ_KIND kind () const - { - return P_NODE; - } - - /** - * @internal - * Print this %node in gml format. - */ - void write(std::ostream&, int); - - /** - * @internal - */ - void clear (); - - // type-casts - - /** - * @internal - * Type-cast to P-node. - */ - p_node* P() - { - return this; - } - - /** - * @internal - * Type-cast to Q-node. - */ - q_node* Q() - { - assert(false); - return 0; - } - - /** - * @internal - * Type-cast to direction indicator. - */ - direction_indicator* D() - { - assert(false); - return 0; - } - - /** - * @internal - * Type-cast to PQ-leaf. - */ - pq_leaf* L() - { - assert(false); - return 0; - } - - // - // Additional - // - - /** - * @internal - * Whenever a child is known to be full, it is moved from the list of - * sons to this list. - */ - symlist full_sons; - - /** - * @internal - * Whenever a child is known to be partial, it is moved from the list of - * sons to this list. - */ - symlist partial_sons; - - /** - * @internal - * Number of children. - */ - int child_count; - - /** - * @internal - * Number of partial children. - */ - int partial_count; - - /** - * @internal - * Number of full children. - */ - int full_count; - - // - // Friends - // - - /** - * @internal - * Allow planarity private access. - */ - friend class planarity; - - /** - * @internal - * Allow pq_tree private access. - */ - friend class pq_tree; - - /** - * @internal - * Allow operator<< private access. - */ - GTL_EXTERN friend std::ostream& operator<<(std::ostream&, const pq_tree&); -}; - - -/** - * @internal - */ -class GTL_EXTERN q_node : public pq_node -{ -private: - /** - * @internal - */ - q_node (node, int); - - // - // pq_node interface - // - - /** - * @internal - */ - void partial(iterator); - - /** - * @internal - */ - void full(iterator); - - /** - * @internal - * Determines kind of this %node. - */ - PQ_KIND kind() const - { - return Q_NODE; - } - - /** - * @internal - * Print this %node in gml format. - */ - void write(std::ostream&, int); - - /** - * @internal - */ - void clear(); - - // type-casts - - /** - * @internal - * Type-cast to P-node. - */ - p_node* P() - { - assert (false); - return 0; - } - - /** - * @internal - * Type-cast to Q-node. - */ - q_node* Q() - { - return this; - } - - /** - * @internal - * Type-cast to direction indicator. - */ - direction_indicator* D() - { - assert (false); - return 0; - } - - /** - * @internal - * Type-cast to PQ-leaf. - */ - pq_leaf* L() - { - assert (false); - return 0; - } - - // - // Additional - // - - /** - * @internal - * Determines pert_begin and pert_end the first time a full or partial - * child is found. - */ - void pertinent(iterator); - - /** - * @internal - * In #Q2 and #Q3 matchings the sons of partial children have to be - * merged into the list of sons of this node at the partial node's - * position - */ - q_node* merge (iterator); - - /** - * @internal - * @em Depreacted. - */ - void turn (); - - /** - * @internal - * First son full or partial viewed from the beginning of the list of - * pq_node::sons. - */ - iterator pert_begin; - - /** - * @internal - * Last son full or partial; usually this is the last son. - */ - iterator pert_end; - - /** - * @internal - * Positions of the partial nodes among the sons. Normally only two - * partial sons are allowed, but the third one is needed in planarity - * testing. - */ - iterator partial_pos[3]; - - /** - * @internal - * True when all the pertinent children are consecutive; f false - * @a pert_begin lies in one block of pertinent children and @a pert_end - * in another, such that --pert_end is empty and between the - * two blocks. - */ - bool pert_cons; - - /** - * @internal - * Number of partial children. - */ - int partial_count; - - /** - * @internal - * Number of full children. - */ - int full_count; - - // - // Friends - // - - /** - * @internal - * Allow planarity private access. - */ - friend class planarity; - - /** - * @internal - * Allow pq_tree private access. - */ - friend class pq_tree; -}; - - -/** - * @internal - */ -class GTL_EXTERN pq_leaf : public pq_node -{ -public: - /** - * @internal - */ - pq_leaf (int, int, edge, node); -private: - /** - * @internal - * Determines kind of this %node. - */ - PQ_KIND kind() const - { - return LEAF; - } - - /** - * @internal - * Print this %node in gml format. - */ - void write(std::ostream&, int); - - // type-casts - - /** - * @internal - * Type-cast to P-node. - */ - p_node* P() - { - assert(false); - return 0; - } - - /** - * @internal - * Type-cast to Q-node. - */ - q_node* Q() - { - assert(false); - return 0; - } - - /** - * @internal - * Type-cast to direction indicator. - */ - direction_indicator* D() - { - assert(false); - return 0; - } - - /** - * @internal - * Type-cast to PQ-leaf. - */ - pq_leaf* L() - { - return this; - } - - // - // Additional - // - - /** - * @internal - */ - int other_id; - - /** - * @internal - */ - edge e; - - // - // Friends - // - - /** - * @internal - * Allow planarity private access. - */ - friend class planarity; - - /** - * @internal - * Allow pq_tree private access. - */ - friend class pq_tree; -}; - - -/** - * @internal - */ -class GTL_EXTERN direction_indicator : public pq_node -{ -private: - /** - * @internal - */ - direction_indicator (node n_, int id_) : pq_node (n_, id_) { }; - - // - // pq_node interface - // - - /** - * @internal - * Determines kind of this %node. - */ - PQ_KIND kind() const - { - return DIR; - } - - /** - * @internal - * Print this %node in gml format. - */ - void write(std::ostream& os, int); - - // type-casts - - /** - * @internal - * Type-cast to P-node. - */ - p_node* P() - { - assert(false); - return 0; - } - - /** - * @internal - * Type-cast to Q-node. - */ - q_node* Q() - { - assert(false); - return 0; - } - - /** - * @internal - * Type-cast to direction indicator. - */ - direction_indicator* D() - { - return this; - } - - /** - * @internal - * Type-cast to PQ-leaf. - */ - pq_leaf* L() - { - assert(false); - return 0; - } - - // - // Additional - // - - /** - * @internal - */ - bool direction; - - // - // Friends - // - - /** - * @internal - * Allow planarity private access. - */ - friend class planarity; - - /** - * @internal - * Allow pq_tree private access. - */ - friend class pq_tree; -}; - -__GTL_END_NAMESPACE - -#endif - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/pq_tree.h b/Tracker/graph/GTL/include/GTL/pq_tree.h deleted file mode 100644 index 6fe7b4f94..000000000 --- a/Tracker/graph/GTL/include/GTL/pq_tree.h +++ /dev/null @@ -1,392 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// pq_tree.h -// -//========================================================================== -// $Id: pq_tree.h,v 1.20 2008/02/03 18:17:08 chris Exp $ - -#ifndef PQ_TREE_H -#define PQ_TREE_H - -#include -#include -#include -#include - -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * $Date: 2008/02/03 18:17:08 $ - * $Revision: 1.20 $ - * - * @brief PQ-Trees. - * - */ -class GTL_EXTERN pq_tree -{ -public: - /** - * @internal - */ - typedef symlist sons_list; - - /** - * @internal - */ - typedef symlist::iterator sons_iterator; - - /** - * @brief Creates empty pq_tree. - */ - pq_tree() : root(0), pert_root(0), pseudo(0), fail(0) - { - } - - /** - * @brief Creates a PQ-tree consisting of a single P-node whose - * whose children are the leaves given in list @p le. - * - * @param id st-number of @p n - * @param n node in the %graph to which the P-node refers - * @param le list of children - */ - pq_tree(int id, node n, const std::list& le); - - /** - * @brief Deletes PQ-tree. - */ - ~pq_tree(); - - /** - * @brief Applies so called template matchings to the tree until either - * all leaves labeled with @c id are consecutive in all equivalent - * trees or until it is recognized that this can't be achieved. - * - * This operation is guaranteed to perform in O(PPT), where - * PPT is the size of the so called @em pruned @em pertinent - * @em subtree, which can be constructed, by cutting away all - * the parts of the PQ-tree, that do not contain a leaf - * labeled with @c id. - * - * @param leaves list of full leaves - * - * @retval true if tree was successfully reduced - * @retval false if reduction failed - */ - bool reduce(std::list& leaves); - - /** - * @brief Replaces all the pertinent parts of the PQ-tree after a - * (successful) reduction by a new P-node, whose children are given in - * @p le. - * - * The edges (in the %graph), represented by the leaves are stored in - * left to right order in @c em[n] They form (up to reversion) - * the so called upward-embedding. A direction indicator representing - * the direction in which the leaves were scanned is added to the sons - * of the root of the pertinent subtree (if neccessary). All direction - * indicators in the pertinent subtree are stored in @p dirs. - * - * @param id st-number of @p n - * @param n node in the %graph to which the new P-node refers - * @param le list of children - * @param em planar embedding - * @param dirs direction indicators in pertinent subtree - */ - void replace_pert(int id, - node n, - const std::list& le, - planar_embedding* em = 0, - std::list* dirs = 0); - - /** - * @brief Scans whole tree from left to right and stores edges (in the - * %graph) represented by the leaves in @p em. - * - * All direction indicators in the tree are stored in @p - * dirs. This is used in %planarity test to get the upward - * %embedding of the last node, because no reduction is - * needed in this case since all leaves are labeled with the - * same number. - * - * @param em planar embedding - * @param dirs direction indicators in tree - */ - void get_frontier(planar_embedding& em, std::list& dirs); - - /** - * @brief After a (successful) reduction @c reset has to be called in - * order to prepare the tree for the next reduction. - */ - void reset (); - - /** - * @brief Returns the (PQ-) node to which none of the - * template matchings were applicable. - * - * @return PQ-node at which the reduction failed - */ - pq_node* get_fail() - { - return fail; - } - - /** - * @brief Returns true iff fail is the root of the - * pertinent subtree. - * - * @retval true iff reduction failed at the root of the - * pertinent subtree. - */ - bool is_fail_root() - { - return failed_at_root; - } - - /** - * @brief Remove a direction indicator among sons of a Q-node. - * Needed for computation of the obstruction set. - * - * @param q_fail the Q-node on which the reduction failed - * @param the position of the direction indicator among the sons - * - * @retval next valid sons iterator - */ - sons_iterator remove_dir_ind(q_node* q_fail, sons_iterator s_it); - - /** - * @brief Checks the structure of the tree. - * - * @note Use this only for debugging since it scans the whole tree, - * which isn't acceptable in terms of performance in most cases. - * - * @retval true iff tree passes checks - */ - bool integrity_check () const; - -// p_node* insert_P (pq_node*, sons_list&); - -// q_node* insert_Q (pq_node*, sons_list&); - -// pq_leaf* insert_leaf (pq_node*); - -// void insert (pq_node*, pq_node*); -private: - /** - * @internal - * Tries to give all the nodes in the pertinent subtree the right father - * pointer. If either all nodes in the pertinent subtree recieved a - * valid father pointer or there was excactly one block of inner nodes - * just below the root of the pertinent subtree, the result is true. If - * @c bubble_up returns false a reduction isn't possible. - * - * @param leaves list of full leaves - * - * @retval true iff bubble-up succeeded - */ - bool bubble_up(std::list& leaves); - - /** - * @internal - * Scans the subtree rooted at @p p and stores edges (in the %graph) - * represented by the leaves in @p em. All direction indicators in the - * subtree are stored in @p dirs. - * - * @param p root of subtree - * @param em planar embedding - * @param dirs direction indicators in subtree - */ - void dfs(pq_node* p, - planar_embedding& em, - std::list& dirs); - - /** - * @internal - * Test whether one of the predecessors of @p le has mark @c BLOCKED. - * Used when bubble-up failed to determine a minimum subtree, whose root - * has inner pertinent children. Minimum in this regard means that no - * descendant of the subtree's root has @c BLOCKED children. - * - * @param le (PQ-)node - * - * @return @c BLOCKED node or @c 0 - */ - pq_node* leads_to_blocked(pq_node* le); - - - /** - * @internal - * Tests wheter @p le leads to @p other, i.e. if @p other is a - * predecessor of @p le. Used to limit the leaves for reduction in case - * that bubble-up failed to the leaves in the minimum subtree, whose - * root has inner pertinent children. - * - * @param le node to be tested - * @param other root of subtree - * - * @retval true iff @p le is in subtree rooted at @p other - */ - bool leads_to(pq_node* le, pq_node* other); - - - /** - * @internal - * In case bubble-up failed a (PQ-)node has to be found which has inner - * children pertinent such that no node in its subtree has inner - * children pertinet. Template matchings then are only performed in this - * subtree. - * - * @param leaves list of full leaves - * - * @return root of the minimum subtree - */ - pq_node* where_bubble_up_failed(std::list& leaves); - - - /** - * @internal - * Tests whether some descendants of @p n are @c BLOCKED. - * - * @param n root for subtree to be checked - * - * @return (PQ-) @c BLOCKED node or @c 0 - */ - pq_node* blocked_in_subtree(pq_node* n); - - - // - // Template Matchings - // - - //---------------------------------------------------------- P-Templates - - /** - * @internal - * Template P1. - */ - bool P1 (p_node* x, bool); - - /** - * @internal - * Template P2. - */ - bool P2 (p_node* x); - - /** - * @internal - * Template P3. - */ - bool P3 (p_node* x); - - /** - * @internal - * Template P4. - */ - bool P4 (p_node* x); - - /** - * @internal - * Template P5. - */ - bool P5 (p_node* x); - - /** - * @internal - * Template P6. - */ - bool P6 (p_node* x); - - //---------------------------------------------------------- Q-Templates - - /** - * @internal - * Template Q1. - */ - bool Q1 (q_node* x, bool); - - /** - * @internal - * Template Q2. - */ - bool Q2 (q_node* x, bool); - - /** - * @internal - * Template Q3. - */ - bool Q3 (q_node* x); - - - // - // Data - // - - /** - * @internal - * List of (PQ-) nodes to be cleared if the reduction stopped now. - */ - std::list clear_me; - - /** - * @internal - * Root of tree. - */ - pq_node* root; - - /** - * @internal - * Root of pertinent subtree; defined after succesful reduction. - */ - pq_node* pert_root; - - /** - * @internal - * In some cases the root of the pertinent subtree might not be known, - * because it is a Q-node and all its pertinent children are inner. In - * this case for the time of reduction an pseudo node is created as root - * of the pertinent subtree, which gets only the pertinent children as - * sons. - */ - q_node* pseudo; - - /** - * @internal - * (PQ-) node for which the reduction failed. - */ - pq_node* fail; - - /** - * @internal - * @c true iff reduction failed at the root of the pertinent subtree. - */ - bool failed_at_root; - - /** - * @internal - * Number of pertinent leaves for the current reduction; defined after - * bubble-up. - */ - int pert_leaves_count; - - // - // Friends - // - - /** - * @internal - * Allow operator<< private access. - */ - GTL_EXTERN friend std::ostream& operator<< (std::ostream&, const pq_tree&); -}; - -__GTL_END_NAMESPACE - -#endif - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/ratio_cut_partition.h b/Tracker/graph/GTL/include/GTL/ratio_cut_partition.h deleted file mode 100644 index 7cde94636..000000000 --- a/Tracker/graph/GTL/include/GTL/ratio_cut_partition.h +++ /dev/null @@ -1,909 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// ratio_cut_partition.h -// -//========================================================================== -// $Id: ratio_cut_partition.h,v 1.8 2003/01/31 08:15:04 chris Exp $ - -#ifndef GTL_RATIO_CUT_PARTITION_H -#define GTL_RATIO_CUT_PARTITION_H - -#include -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - - -/** - * @short Heuristic graph bi-partitioning algorithm (Wei-Cheng). - * - * This class implements a heuristic graph bi-partitioning algorithm using - * the ratio cut method proposed by Y. C. Wei and C. K. Cheng in 1991. - * - *

In the case E is the set of edges of the graph, the algorithm needs - * O(|E|) time to proceed. - * - * @see fm_partition - */ -class GTL_EXTERN ratio_cut_partition : public algorithm -{ -public: - /** - * Return type of @ref ratio_cut_partition#get_side_of_node. - * - * @see ratio_cut_partition#A - * @see ratio_cut_partition#B - */ - typedef int side_type; - - /** - * A means the node is on side A. - * - * @see ratio_cut_partition#side_type - */ - const static side_type A; - - /** - * B means the node is on side B. - * - * @see ratio_cut_partition#side_type - */ - const static side_type B; - - /** - * Fix type of each node (needed with - * @ref ratio_cut_partition#set_vars). - * - * @see ratio_cut_partition#FIXA - * @see ratio_cut_partition#FIXB - * @see ratio_cut_partition#UNFIXED - */ - typedef short int fix_type; - - /** - * FIXA means fix node on side A. - * - * @see ratio_cut_partition#set_vars - */ - const static fix_type FIXA; - - /** - * FIXB means fix node on side B. - * - * @see ratio_cut_partition#fixe_type - */ - const static fix_type FIXB; - - /** - * UNFIXED means node is free. - * - * @see ratio_cut_partition#fixe_type - */ - const static fix_type UNFIXED; - - /** - * Default constructor. - * - * @see algorithm#algorithm - */ - ratio_cut_partition(); - - /** - * Destructor. - * - * @see algorithm#~algorithm - */ - virtual ~ratio_cut_partition(); - - /** - * Sets variables. - * Must be executed before @ref ratio_cut_partition#check! - * source_node and target_node will be - * determined automatically. - * - * @param G undirected graph - * @param node_weight weight of each node - * @param edge_weight weight of each edge. - * @see ratio_cut_partition#check - */ - void set_vars(const graph& G, const node_map& node_weight, - const edge_map& edge_weight); - - /** - * Sets variables. - * Must be executed before @ref ratio_cut_partition#check! - * In order to get good results, you should take two graph - * theoretically far away nodes as source and target. - * - * @param G undirected graph - * @param node_weight weight of each node - * @param edge_weight weight of each edge - * @param source_node start-node, remains on side A - * @param target_node end-node, remains on side B - * @see ratio_cut_partition#check - */ - void set_vars(const graph& G, const node_map& node_weight, - const edge_map& edge_weight, const node source_node, - const node target_node); - - /** - * Sets variables. - * Must be executed before @ref ratio_cut_partition#check! - * In order to get good results, you should take two graph - * theoretically far away nodes as source and target. Additionally - * init_side should nearly be in balance. - * source_node must be on side A in - * init_side and target_node on side B - * respectively. - * - * @param G undirected graph - * @param node_weight weight of each node - * @param edge_weight weight of each edge - * @param source_node start-node, remains on side A - * @param target_node end-node, remains on side B - * @param init_side initial bi-partitioning - * @see ratio_cut_partition#check - */ - void set_vars(const graph& G, const node_map& node_weight, - const edge_map& edge_weight, const node source_node, - const node target_node, const node_map& init_side); - - /** - * Sets variables. - * Must be executed before @ref ratio_cut_partition#check! - * In order to get good results, you should take two graph - * theoretically far away nodes as source and target. - * source_node must not be fixed on side B - * . - * target_node must not be fixed on side A - * . - * - * @param G undirected graph - * @param node_weight weight of each node - * @param edge_weight weight of each edge - * @param source_node start-node, remains on side A - * @param target_node end-node, remains on side B - * @param fixed fixed nodes - * @see ratio_cut_partition#check - */ - void set_vars(const graph& G, const node_map& node_weight, - const edge_map& edge_weight, const node source_node, - const node target_node, const node_map& fixed); - - /** - * Sets variables. - * Must be executed before @ref ratio_cut_partition#check! - * In order to get good results, you should take two graph - * theoretically far away nodes as source and target. Additionally - * init_side should nearly be in balance. Fixed nodes - * are on their fix side, their initial side is overwritten then. - * source_node must be on side A in init_side - * and target_node on side B respectively. - * source_node must not be fixed on side B - * . - * target_node must not be fixed on side A - * . - * - * @param G undirected graph - * @param node_weight weight of each node - * @param edge_weight weight of each edge - * @param source_node start-node, remains on side A - * @param target_node end-node, remains on side B - * @param init_side initial bi-partitioning - * @param fixed fixed nodes - * @see ratio_cut_partition#check - */ - void set_vars(const graph& G, const node_map& node_weight, - const edge_map& edge_weight, const node source_node, - const node target_node, const node_map& init_side, - const node_map& fixed); - - /** - * Enables the storing of cut-edges. If enabled the list of - * cut-edges can be traversed using @ref - * ratio_cut_partition#cut_edges_iterator. - * - * @param set if true cut_edges will be stored - * @see ratio_cut_partition#cut_edges_begin - * @see ratio_cut_partition#cut_edges_end - */ - void store_cut_edges(const bool set); - - /** - * Enables the storing of nodes on their side. If enabled the nodes - * of each side can be traversed using - * ratio_cut_partition#nodes_of_one_side_iterator. - * - * @param set if true nodes on their side will be stored - * @see ratio_cut_partition#nodes_of_sideA_begin - * @see ratio_cut_partition#nodes_of_sideA_end - * @see ratio_cut_partition#nodes_of_sideB_begin - * @see ratio_cut_partition#nodes_of_sideB_end - */ - void store_nodesAB(const bool set); - - /** - * Checks whether following preconditions are satisfied: - *

    - *
  • One of the @ref ratio_cut_partition#set_vars procedures has - * been executed before. - *
  • graph G is undirected. - *
  • if applied, source_node and target_node - * are 2 distinct nodes with node weights > 0. - *
  • only node_weights >= 0 are applied. - *
  • only edge_weights >= 0 are applied. - *
  • if G has more than 2 nodes, then at least - * two of them have a weight > 0. - *
  • if applied fixed source node, fixed[source_node] - * is FIXA. - *
  • if applied fixed target node, fixed[target_node] - * is FIXB. - *
- * - * @param G graph - * @return algorithm::GTL_OK on success, - * algorithm::GTL_ERROR otherwise - * @see ratio_cut_partition#set_vars - * @see algorithm#check - */ - virtual int check(graph& G); - - /** - * Computes a partitioning of G, that means a division - * of its vertices in two sides ratio_cut_partition::A - * and ratio_cut_partition::B. - * - * @param G graph - * @return algorithm::GTL_OK on success, - * algorithm::GTL_ERROR otherwise - * @see algorithm#run - */ - int run(graph& G); - - /** - * Gets the size of the cut after bi-partitioning. - * - * @return cutsize - */ - int get_cutsize(); - - /** - * Gets the ratio of the cut after bi-partitioning as defined in - * [WeiChe91]. - * - * @return cutratio - */ - double get_cutratio(); - - /** - * Gets side of the node after bi-partitioning. - * - * @param n node of graph G - * @return ratio_cut_partition::A if n - * lies on side A, ratio_cut_partition::B - * otherwise - */ - side_type get_side_of_node(const node& n) const; - - /** - * Gets side of the node after bi-partitioning. - * - * @param n node of graph G - * @return ratio_cut_partition::A if n - * lies on side A, ratio_cut_partition::B - * otherwise - * @see ratio_cut_partition#get_side_of_node - */ - side_type operator [](const node& n) const; - - /** - * Gets the sum of all node weights from nodes on side A - * . - * - * @param G graph - * @return node_weight_on_sideA - */ - int get_weight_on_sideA(const graph& G) const; - - /** - * Gets the sum of all node weights from nodes on side B - * . - * - * @param G graph - * @return node_weight_on_sideB - */ - int get_weight_on_sideB(const graph& G) const; - - /** - * Iterator type for edges which belong to the cut. - */ - typedef edges_t::const_iterator cut_edges_iterator; - - /** - * Iterate through all edges which belong to the cut, that means - * all edges with end-nodes on different sides. - * It is only valid if enabled with @ref - * ratio_cut_partition#store_cut_edges before. - * - * @return start for iteration through all cut edges - */ - cut_edges_iterator cut_edges_begin() const; - - /** - * End-Iterator for iteration through all edges which belong to the - * cut. - * It is only valid if enabled with @ref - * ratio_cut_partition#store_cut_edges before. - * - * @return end for iteration through all cut-edges - */ - cut_edges_iterator cut_edges_end() const; - - /** - * Iterator type for nodes of a side. - */ - typedef nodes_t::const_iterator nodes_of_one_side_iterator; - - /** - * Iterate through all nodes which belong to side A, - * It is only valid if enabled with @ref - * ratio_cut_partition#store_nodesAB before. - * - * @return start for iteration through all nodes on A - */ - nodes_of_one_side_iterator nodes_of_sideA_begin() const; - - /** - * End-Iterator for iteration through all nodes which belong to side - * A, - * It is only valid if enabled with @ref - * ratio_cut_partition#store_nodesAB before. - * - * @return end for iteration through all nodes on A - */ - nodes_of_one_side_iterator nodes_of_sideA_end() const; - - /** - * Iterate through all nodes which belong to side B, - * It is only valid if enabled with @ref - * ratio_cut_partition#store_nodesAB before. - * - * @return start for iteration through all nodes on B - */ - nodes_of_one_side_iterator nodes_of_sideB_begin() const; - - /** - * End-Iterator for iteration through all nodes which belong to side - * B, - * It is only valid if enabled with @ref - * ratio_cut_partition#store_nodesAB before. - * - * @return end for iteration through all nodes on B - */ - nodes_of_one_side_iterator nodes_of_sideB_end() const; - - /** - * Resets ratio_cut_partition, i.e. prepares the algorithm to be - * applied to another graph. - * - * @see algorithm#reset - */ - virtual void reset(); -protected: - /** - * @internal - */ - enum direction_type {LEFT_SHIFT = 2, RIGHT_SHIFT = 3}; - - /** - * @internal - * true, iff user enabled storing of cut-edges with - * @ref ratio_cut_partition#store_cut_edges. - */ - bool enable_cut_edges_storing; - - /** - * @internal - * List of edges which belong to the cut. - */ - edges_t cut_edges; - - /** - * @internal - * true, iff user enabled storing of nodes with @ref - * ratio_cut_partition#store_nodesAB. - */ - bool enable_nodesAB_storing; - - /** - * @internal - * List of nodes which belong to side A. - */ - nodes_t nodesA; - - /** - * @internal - * List of nodes which belong to side A. - */ - nodes_t nodesB; - - /** - * @internal - * Corresponds to s in [WeiChe91]. - */ - node source_node; - - /** - * @internal - * Corresponds to t in [WeiChe91]. - */ - node target_node; - - /** - * @internal - * true, iff user has executed @ref - * ratio_cut_partition#set_vars before @ref ratio_cut_partition# - * check and @ref ratio_cut_partition#run. - */ - bool set_vars_executed; - - /** - * @internal - * true, iff user has provided source_node - * and target_node, false else. - */ - bool provided_st; - - /** - * @internal - * true, iff user has provided init_side - * with @ref ratio_cut_partition#set_vars, false - * otherwise. - */ - bool provided_initial_part; - - /** - * @internal - * true, iff user has provided fixed with - * @ref ratio_cut_partition#set_vars, false otherwise. - */ - bool provided_fix; - - /** - * @internal - * Contains information where a node is fixed. - */ - node_map fixed; - - /** - * @internal - * LEFT if @ref ratio_cut_partition#left_shift_op has - * computed last cut, RIGHT else. - */ - direction_type direction; - - /** - * @internal - * Contains the weight of each node. - * Corresponds to w(v) in [Leng90]. - */ - node_map node_weight; - - /** - * @internal - * Contains the weight of each edge. - * Corresponds to c(e) in [Leng90]. - */ - edge_map edge_weight; - - /** - * @internal - * Contains the maximum weight of an edge in G. - * (maximum of edge_weight[...]) - */ - int max_edge_weight; - - /** - * @internal - * Contains the sum over all vertex weights on side A. - * Corresponds to w(A) in [Leng90]. - */ - int node_weight_on_sideA; - - /** - * @internal - * Contains the sum over all vertex weights on side B. - * Corresponds to w(B) in [Leng90]. - */ - int node_weight_on_sideB; - - /** - * @internal - * Counts nodes on side A. - */ - int nodes_on_sideA; - - /** - * @internal - * Counts nodes on side B. - */ - int nodes_on_sideB; - - /** - * @internal - * Contains information about the current side of a node. - */ - node_map side; - - /** - * @internal - * Corresponds to CELL array in [FidMat82] - */ - node_map position_in_bucket; - - /** - * @internal - * Contains the maximal number of adjacent to a node. - */ - int max_vertex_degree; - - /** - * @internal - * Contains how many nodes an edge has on side A. - */ - edge_map aside; - - /** - * @internal - * Contains how many nodes an edge has on side B. - */ - edge_map bside; - - /** - * @internal - * Contains the unlocked nodes of an edge on side A. - * (max. 2) - */ - edge_map unlockedA; - - /** - * @internal - * Contains the unlocked nodes of an edge on side B. - * (max. 2) - */ - edge_map unlockedB; - - /** - * @internal - * Corresponds to D value in Leng[90]. - */ - node_map gain_value; - - /** - * @internal - * true, iff bucketA is empty. - */ - bool bucketA_empty; - - /** - * @internal - * true, iff bucketB is empty. - */ - bool bucketB_empty; - - /** - * @internal - * Contains the maximum gain value of a node in - * bucketA. - */ - int max_gainA; - - /** - * @internal - * Contains the maximum gain value of a node in - * bucketB. - */ - int max_gainB; - - /** - * @internal - * Like a hash table over the gain_value of each node - * on side A. (open hashing, collisions in gain buckets - * are organized through LIFO lists) - */ - std::vector bucketA; - - /** - * @internal - * Like a hash table over the gain_value of each node - * on side B. (open hashing, collisions in gain buckets - * are organized through LIFO lists) - */ - std::vector bucketB; - - /** - * @internal - * Sum over all edge_costs[e] where edge e is an - * element of the cut. - */ - int cur_cutsize; - - /** - * @internal - * Cut ratio as defined in [WeiChe91]. - */ - double cur_cutratio; - - /** - * @internal - * Fix FIXA nodes on side A and FIXB - * nodes on side B. - */ - void divide_up(const graph& G); - - /** - * @internal - * Makes G connected for the run of this algorithm. - * This is done by introducing edges with weight 0 since Ratio Cut - * works well on connected graphs only. - */ - void make_connected(graph& G, edges_t& artificial_edges); - - /** - * @internal - * Deletes the edges introduced in @ref ratio_cut_partition# - * make_connected. - */ - void restore(graph& G, edges_t& artificial_edges); - - /** - * @internal - * Corresponds to phase 1 in [WeiChe91]. - */ - void initialization(const graph& G); - - /** - * @internal - * Initialization of the data structure for each step. - */ - void init_data_structure(const graph& G); - - /** - * @internal - * Computes initial gain_value for each node and inserts it in the - * corresponding bucket data structure. - */ - void init_filling_buckets(const graph& G); - - /** - * @internal - * Compute initial gain of a node on side A. - * @return initial gain_value of a node on side A - */ - int inital_gain_of_node_on_sideA(const node cur_node); - - /** - * @internal - * Compute initial gain of a node on side B. - * @return initial gain_value of a node on side B - */ - int inital_gain_of_node_on_sideB(const node cur_node); - - /** - * @internal - * Computes some maximum variables. - */ - void init_variables(const graph& G); - - /** - * @internal - * Computes max_vertex_degree. - */ - void compute_max_vertex_degree(const graph& G); - - /** - * @internal - * Compute source seed [WeiChe91]. - */ - void determine_source_node(const graph& G); - - /** - * @internal - * Compute target seed [WeiChe91]. - */ - void compute_target_node(const graph& G); - - /** - * @internal - * Corresponds to right shifting operation as defined in [WeiChe91]. - * Moves nodes from side A to B. - */ - void right_shift_op(const graph& G); - - /** - * @internal - * Corresponds to left shifting operation as defined in [WeiChe91]. - * Moves nodes from side B to A. - */ - void left_shift_op(const graph& G); - - /** - * @internal - * Moves max_gain node from side A to - * B. - * @return true if vertex stored in parameter - * moved_node has been found - */ - bool move_vertex_A2B(const graph& G, node& moved_node); - - /** - * @internal - * Moves max_gain node from side B to A. - * @return true if vertex stored in parameter - * moved_node has been found - */ - bool move_vertex_B2A(const graph& G, node& moved_node); - - /** - * @internal - * Selects node with highest ratio_gain - */ - node compute_highest_ratio_node(nodes_t node_list); - - /** - * @internal - * Computes cut_ratio. - * @return cut_ratio with cutsize cur_cutsize - * and current side weights node_weight_on_sideA - * and node_weight_on_sideB - */ - double cutratio(); - - /** - * @internal - * Corresponds to r(i) in [WeiChe91]. - * @return ratio gain of a node cur_node on side - * A - */ - double ratio_of_node_A2B(const node cur_node); - - /** - * @internal - * Corresponds to r(i) in [WeiChe91]. - * @return ratio gain of a node cur_node on side - * B - */ - double ratio_of_node_B2A(const node cur_node); - - /** - * @internal - * Transform a range from [-a..+a] to [0..2a]. - * (reverse to @ref ratio_cut_partition#range_up) - */ - inline int range_up(const int gain_value) const; - - /** - * @internal - * Transform a range from [0..2a] to [-a..+a]. - * (reverse to @ref ratio_cut_partition#range_down) - */ - inline int range_down(const int index) const; - - /** - * @internal - * Executed, if cur_node is chosen to move from side - * A to B. - */ - void update_data_structure_A2B(const node cur_node, - const bool init_mode); - - /** - * @internal - * Executed, if cur_node is chosen to move from side - * B to A. - */ - void update_data_structure_B2A(const node cur_node, - const bool init_mode); - - /** - * @internal - * Reorganizes bucketA if a nodes gain of it has been - * changed. - */ - void update_bucketA(const node cur_node, const int old_gain, - const int new_gain, const bool init_mode); - - /** - * @internal - * Reorganizes bucketB if a nodes gain of it has been - * changed. - */ - void update_bucketB(const node cur_node, const int old_gain, - const int new_gain, const bool init_mode); - - /** - * @internal - * Recomputes max_gainA or max_gainB - * respectively. - */ - void update_max_gain(const side_type side); - - /** - * @internal - * Do some garbage collection. - */ - void clean_step(const graph& G); - - /** - * @internal - * Copies side node maps. - */ - void copy_side_node_map(const graph& G, node_map& dest, - const node_map source) const; - - /** - * @internal - * Corresponds to phase 2 in [WeiChe91]. - */ - void iterative_shifting(const graph& G); - - /** - * @internal - * Corresponds to phase 3 in [WeiChe91]. - */ - void group_swapping(const graph& G); - - /** - * @internal - * Moves nodes in group swapping phase. - * @return true on improvement, false - * else - */ - bool move_manager(const graph& G); - - /** - * @internal - * Moves a single node. - * @return true if vertex stored in parameter - * moved_node has been found - */ - bool move_vertex(const graph& G, node& moved_node); - - /** - * @internal - * Computes list cut_edges. - */ - void compute_cut_edges(const graph& G); - - /** - * @internal - * Computes lists nodesA and nodesB. - */ - void compute_nodesAB(const graph& G); -private: -#ifdef _DEBUG - /** - * @internal - * Prints content of bucketA with associated gain values. - */ - void print_bucketA(); - - /** - * @internal - * Prints content of bucketB with associated gain values. - */ - void print_bucketB(); -#endif // _DEBUG -}; - -__GTL_END_NAMESPACE - -#endif // GTL_RATIO_CUT_PARTITION_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/st_number.h b/Tracker/graph/GTL/include/GTL/st_number.h deleted file mode 100644 index e817208ad..000000000 --- a/Tracker/graph/GTL/include/GTL/st_number.h +++ /dev/null @@ -1,442 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// st_number.h -// -//========================================================================== -// $Id: st_number.h,v 1.17 2002/12/20 08:26:08 chris Exp $ - -#ifndef GTL_ST_NUMBER_H -#define GTL_ST_NUMBER_H - -#include -#include -#include -#include -#include - -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** -* @internal -*/ -class GTL_EXTERN pathfinder -{ -public: - //---------------------------------------------------------- CONSTRUCTOR - - /** - * @internal - */ - pathfinder(const graph& G, edge st, node s); - - /** - * @internal - */ - bool is_valid() - { - return is_biconn; - } - - //------------------------------------------------------------- ITERATOR - - /** - * @internal - */ - class const_iterator - { - public: - /** - * @internal - */ - const_iterator(pathfinder& _pf) : pf (_pf) - { - } - - /** - * @internal - */ - const_iterator(pathfinder& _pf, node n); - - /** - * @internal - */ - const_iterator& operator++(); - /** - * @internal - */ - const_iterator operator++(int); - /** - * @internal - */ - const node& operator*() const - { - return curr; - } - - /** - * @internal - */ - bool operator==(const const_iterator& it) - { - return curr == it.curr; - } - - /** - * @internal - */ - bool operator!=(const const_iterator& it) - { - return curr != it.curr; - } - private: - /** - * @internal - */ - enum iteration_state {END, UP, DOWN}; - - /** - * @internal - */ - iteration_state state; - - /** - * @internal - */ - node curr; - - /** - * @internal - */ - pathfinder& pf; - }; - - /** - * @internal - */ - const_iterator path(node n) - { - return const_iterator(*this, n); - } - - /** - * @internal - */ - const_iterator end() - { - return const_iterator (*this); - } - -private: - //------------------------------------------------------------ FUNCTIONS - - /** - * @internal - */ - void dfs_sub (node&, node&); - - //-------------------------------------------------------------- MEMBERS - - /** - * @internal - */ - node_map dfs_num; - - /** - * @internal - */ - node_map low_num; - - /** - * @internal - */ - node_map tree; - - /** - * @internal - */ - node_map back; - - /** - * @internal - */ - node_map forward; - - /** - * @internal - */ - node_map to_low; - - /** - * @internal - */ - node_map to_father; - - /** - * @internal - */ - typedef std::pair pos_pair; - - /** - * @internal - */ - edge_map pos; - - /** - * @internal - */ - node_map used; - - /** - * @internal - */ - int act_dfs_num; - - /** - * @internal - */ - int new_nodes; - - /** - * @internal - */ - bool is_biconn; - - /** - * @internal - * Allows const_iterator private access. - */ - friend class const_iterator; -}; - -/** - * @brief ST-number algorithm. - * - * Encapsulates the st-number algorithm together with all the data produced - * by it. - *

- * Assigns an integer st[n] to each node @c n of a undirected, - * biconnected graph, such that each node is connected with at least one - * node having a smaller and with at least one having a larger number than - * itself. The only exception to this rule are the endpoints of edge @a st - * connecting nodes @a s (st-number 1) and @c t (highest st-number). - *

- * The following options are supported: - * - #st_edge sets/retrieves the edge that connects the node with the lowest - * number to that with the highest. - * - #s_node sets/retrieves that endpoints of the @a st_edge, which gets - * number 1. - */ -class GTL_EXTERN st_number : public algorithm -{ -public: - /** - * @brief Default constructor. - * Creates st-number object. Please note that there are no reasonable - * default settings for the parameters, i.e. the edge @s st connecting - * the lowest with highest numbers node and which of its endpoints - * should get number 1 (= node @a s) has to be specified always. - */ - st_number() : algorithm() - { - } - - /** - * @brief Destructor - */ - virtual ~st_number() - { - } - - /** - * @brief Sets edge @a st for the next run. - * - * @param e edge @a st - */ - void st_edge(edge e) - { - st = e; - } - - /** - * @brief Get edge @a st. - * - * @retval edge @a st - */ - edge st_edge() const - { - return st; - } - - /** - * @brief Sets node @a s for next run. - * - * This must be one of the endpoints of edge @a st. This node will get - * st-number 1 and thus the other endpoint will get the highest - * st-number. - * - * @param n node @a s - */ - void s_node(node n) - { - s = n; - } - - /** - * @brief Get node @a s. - * - * @retval node @a s - */ - node s_node() const - { - return s; - } - - /** - * @brief Returns st-number of node @p n as determined in the last run. - * - * @param n node - * - * @return st-number of @p n - */ - int& operator[](const node& n) - { - return st_num[n]; - } - - /** - * @internal - */ - typedef nodes_t::iterator iterator; - - /** - * @internal - */ - typedef nodes_t::reverse_iterator reverse_iterator; - - /** - * @brief Iteration through the nodes of graph st-numbered in last - * run in st-number order, i.e. from 1 to highest st-number. - * - * @return start of iteration through nodes in st-number order - */ - iterator begin() - { - return st_ord.begin(); - } - - /** - * @brief Iteration through nodes of graph in st-number order. - * - * @return end of iteration through nodes of graph in st-number order - */ - iterator end() - { - return st_ord.end(); - } - - /** - * @brief Iteration through the nodes of graph st-numbered in last run - * in reverse st-number order, i.e. from highest st-number down - * to 1. - * - * @return start of iteration through nodes in reverse st-number order - */ - reverse_iterator rbegin() - { - return st_ord.rbegin(); - } - - /** - * @brief End of iteration through nodes of graph in reverse st-number - * order. - * - * @return end of iteration through nodes in reverse st-number order - */ - reverse_iterator rend() - { - return st_ord.rend(); - } - - - /** - * @brief Checks whether st-number algorithm can be applied to @p G. - * - * Besides from the trivial preconditions that edge @a st and node @a s - * lie in @p G and @a s is really an endpoint of @a st (which isn't - * checked), @p G must be undirected and biconnected. - * @note As for all algorithms in GTL, #check must be called, because it - * might do some initialization. - * - * @param G graph - * - * @retval algorithm::GTL_OK iff st-number algorithm may be applied - * - * @sa algorithm::check - */ - int check(graph& G); - - - /** - * @brief Runs st-number algorithm on graph @p G. - * - * It is assumed that #check was called previously and returned - * algorithm::GTL_OK. - * - * @param G graph - * - * @return algorithm::GTL_OK iff @p G could be correctly st-numbered - * - * @sa algorithm::run - */ - int run(graph& G); - - - /** - * @brief Resets algorithm in order to be applied to the next graph. - * - * This will delete most of the information obtained in the last run. - * - * @sa algorithm::reset - */ - void reset() - { - st_ord.erase (st_ord.begin(), st_ord.end()); - } -protected: - /** - * @internal - */ - edge st; - - /** - * @internal - */ - node s; - - /** - * @internal - */ - pathfinder* pf; - - /** - * @internal - */ - nodes_t st_ord; - - /** - * @internal - */ - node_map st_num; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_ST_NUMBER_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/symlist.h b/Tracker/graph/GTL/include/GTL/symlist.h deleted file mode 100644 index 2b4de84f1..000000000 --- a/Tracker/graph/GTL/include/GTL/symlist.h +++ /dev/null @@ -1,737 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// symlist.h -// -//========================================================================== -// $Id: symlist.h,v 1.17 2002/12/20 08:26:08 chris Exp $ - -#ifndef SYMLIST_H -#define SYMLIST_H - -#include - -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @internal - */ -template -struct symnode -{ - /** - * @internal - */ - symnode() - { - } - - /** - * @internal - */ - symnode(const T& n) : data(n) - { - } - - /** - * @internal - */ - symnode* adj[2]; - - /** - * @internal - */ - T data; -}; - -/** - * @internal - */ -template -struct symlist_iterator -{ - /** - * @internal - */ - typedef symlist_iterator self; - - /** - * @internal - */ - typedef symnode* linktype; - - /** - * @internal - */ - symlist_iterator() : act (0) - { - } - - /** - * @internal - */ - symlist_iterator(const self& it) : act(it.act), dir(it.dir) - { - } - - /** - * @internal - */ - symlist_iterator(linktype _act, int _dir) : act(_act), dir(_dir) - { - } - - /** - * @internal - */ - symlist_iterator(linktype _act, linktype _prev) : - act(_act), - dir (where_not (_act, _prev)) - { - } - - /** - * @internal - */ - self& operator=(const self& it) - { - act = it.act; - dir = it.dir; - return *this; - } - - /** - * @internal - */ - bool operator==(const self& it) const - { - return act == it.act; - } - - /** - * @internal - */ - bool operator!=(const self& it) const - { - return act != it.act; - } - - /** - * @internal - */ - Ref operator*() - { - return act->data; - } - - /** - * @internal - */ - self& operator++(); - - /** - * @internal - */ - self& operator--(); - - /** - * @internal - */ - static int where(linktype _act, linktype _prev) - { - return _prev == _act->adj[0] ? 0 : 1; - } - - /** - * @internal - */ - static int where_not(linktype _act, linktype _prev) - { - return _prev == _act->adj[1] ? 0 : 1; - } - - /** - * @internal - */ - void reverse() - { - dir = 1 - dir; - } - - /** - * @internal - */ - linktype& next() - { - return act->adj[dir]; - } - - /** - * @internal - */ - linktype& prev() - { - return act->adj[1 - dir]; - } - - /** - * @internal - */ - linktype act; - - /** - * @internal - */ - int dir; -}; - -/** - * @brief List which can be reversed in @f$\mathcal{O}(1)@f$. - * - * The problem with the STL class list - as with most doubly linked lists -- - * is that isn't possible to turn it in constant time, because each entry in - * the list contains next and prev pointer and turning the list means to - * switch these two in @em each element in the list. Another point is the - * splice operation in STL lists, which is constant time, but for the same - * reason as mentioned above it is not possible to splice a list in reverse - * order into another in constant time. - *

- * The problems arise from the fact that each element "knows" what its next - * and previous elements are. An element in a symlist only knows what its - * neighbors are, what is next and what previous depends on the direction of - * iteration. This of course imposes some overhead in iteration (one - * if-statement) but allows reversion and a splice in reversed order in - * constant time. - */ -template -class symlist -{ -public: - /** - * @internal - */ - typedef symlist_iterator iterator; - - /** - * @internal - */ - typedef symlist_iterator const_iterator; - - /** - * @brief Creates empty symlist. - */ - symlist() - { - link = new symnode; - link->adj[0] = link->adj[1] = link; - } - - /** - * @brief Makes the created list a copy of @c li. - * - * @param li symlist. - */ - symlist(const symlist& li); - - /** - * @brief Assignes @c li to this list. - * - * @note All elements in this list will be deleted. - * - * @param li - * - * @return this list - */ - symlist& operator=(const symlist& li); - - /** - * @brief Destructor - */ - ~symlist(); - - /** - * @brief Checks whether list is empty. - * - * Takes constant time. - * - * @retval true iff list is empty - */ - bool empty() const - { - return link->adj[0] == link && link->adj[1] == link; - } - - /** - * @brief First element in list. - * - * Assumes that list ins't empty. - * - * @return first element - */ - T& front() - { - return link->adj[0]->data; - } - - /** - * @brief Last element in list. - * - * Assumes that list ins't empty. - * - * @return last element - */ - T& back() - { - return link->adj[1]->data; - } - - /** - * @brief Start iteration through elements of list. - * - * @return start iterator - */ - iterator begin() - { - return ++end(); - } - - /** - * @brief End of iteration through elements of list. - * - * @return end iterator - */ - iterator end() - { - return iterator(link, 0); - } - - /** - * @brief Start iteration through elements of list. - * - * @return start iterator - */ - const_iterator begin() const - { - return ++end(); - } - - /** - * @brief End of iteration through elements of list. - * - * @return end iterator - */ - const_iterator end () const - { - return const_iterator (link, 0); - } - - /** - * @brief Start iteration through element of list in reverse order. - * - * @return start iterator - */ - iterator rbegin() - { - return ++rend(); - } - - /** - * @brief End of iteration through elements of list in reverse order. - * - * @return end iterator - */ - iterator rend() - { - return iterator (link, 1); - } - - /** - * @brief Start iteration through element of list in reverse order. - * - * @return start iterator - */ - const_iterator rbegin() const - { - return ++rend(); - } - - /** - * @brief End of iteration through elements of list in reverse order. - * - * @return end iterator - */ - const_iterator rend() const - { - return const_iterator(link, 1); - } - - /** - * @brief Inserts @p data before @p pos in list. - * - * @param pos position - * @param data element to be inserted - * - * @return position of insertion - */ - iterator insert (iterator pos, const T& data); - - /** - * @brief Inserts the element @p it points to before @p pos into this - * list. - * - * It is assumed that the element @p it refers lies in a different list. - * All iterators to elements in either of the two lists stay valid. - * Takes constant time. - * - * @param pos position - * @param it position of element to be inserted - */ - void splice (iterator pos, iterator it); - - /** - * @brief Inserts the elements [it,end) refers to before @p pos - * into this list. - * - * It is assumed that [it,end) lies in a different - * list. All iterators to elements in either of the two lists stay - * valid. Takes constant time. - * - * @param pos position - * @param it position of first element to be inserted - * @param end position of one-past the last element to be inserted - */ - void splice (iterator pos, iterator it, iterator end); - - /** - * @brief Deletes element at position @p pos from list. - * - * @param pos position to be deleted - * - * @return position of next element - */ - iterator erase (iterator pos); - - /** - * @brief Deletes the elements [it, end) from list. - * - * @param it first position to be deleted - * @param end one-past the last position to be deleted - * - * @return position of next element. - */ - iterator erase (iterator it, iterator end); - - /** - * @internal - */ - void attach_sublist (iterator, iterator); - - /** - * @internal - */ - void detach_sublist (); - - /** - * @brief Change the direction of list. - * - * Takes constant time. - */ - void reverse (); -private: - /** - * @internal - */ - symnode* link; - - /** - * @internal - * - * @note Needed only when used as sublist. - */ - iterator _prev; - - /** - * @internal - * - * @note Needed only when used as sublist. - */ - iterator _next; -}; - - -// Implementation Begin - -template -symlist_iterator& symlist_iterator::operator++() -{ - symnode* prev = act; - act = act->adj[dir]; - dir = where_not(act, prev); - return *this; -} - - -template -symlist_iterator& symlist_iterator::operator--() -{ - symnode* prev = act; - act = act->adj[1 - dir]; - dir = where(act, prev); - return *this; -} - - -template -symlist::symlist (const symlist& l) -{ - link = new symnode; - link->adj[0] = link->adj[1] = link; - - const_iterator it = l.begin(); - const_iterator e = l.end(); - - while (it != e) - { - insert(end(), *it); - ++it; - } -} - - -template -symlist::~symlist() -{ - if (_next == iterator()) - { - erase (begin(), end()); - } - else - { - detach_sublist(); - } - - delete link; -} - - -template -symlist& symlist::operator=(const symlist& l) -{ - erase(begin(), end()); - - const_iterator it = l.begin(); - const_iterator e = l.end(); - - while (it != e) - { - insert(end(), *it); - ++it; - } - - return *this; -} - - -template -symlist_iterator symlist::insert( - symlist_iterator pos, - const T& ins) -{ - iterator prev = pos; - --prev; - symnode* n = new symnode(ins); - n->adj[0] = pos.act; - n->adj[1] = prev.act; - - if (pos == prev) - { - pos = prev; - } - - pos.prev() = n; - prev.next() = n; - - return iterator(n, 0); -} - - -template -void symlist::splice(symlist_iterator pos, - symlist_iterator beg, - symlist_iterator end) -{ - if (beg != end) - { - iterator prev = beg; - --prev; - iterator last = end; - --last; - - // - // The following seems to be rather senseless, but it is required - // since two iterator are equal, iff the point to the same element. - // This implies that they might have different directions. Suppose - // that prev == end is true and they have different directions, - // than prev.next() and end.prev() return the same element !! Thus - // the assignment prev = end corrects this, since the direction - // gets copied, too. - // - if (prev == end) - { - prev = end; - } - - prev.next() = end.act; - end.prev() = prev.act; - - prev = pos; - --prev; - - if (pos == prev) - { - pos = prev; - } - - if (last == beg) - { - last = beg; - } - - prev.next() = beg.act; - beg.prev() = prev.act; - pos.prev() = last.act; - last.next() = pos.act; - } -} - - -template -void symlist::splice(symlist_iterator pos, - symlist_iterator beg) -{ - iterator tmp = beg; - ++tmp; - splice(pos, beg, tmp); -} - - -template -symlist_iterator symlist::erase(symlist_iterator pos) -{ - assert (pos.act != link); - iterator prev = pos; - --prev; - iterator next = pos; - ++next; - - if (next == prev) - { - next = prev; - } - - next.prev() = prev.act; - prev.next() = next.act; - - delete (pos.act); - - return next; -} - -template -symlist_iterator symlist::erase(symlist_iterator beg, - symlist_iterator end) -{ - iterator prev = beg; - --prev; - iterator it = beg; - symnode* act; - - while (it != end) - { - assert (it.act != link); - act = it.act; - ++it; - delete (act); - } - - if (prev == end) - { - prev = end; - } - - end.prev() = prev.act; - prev.next() = end.act; - - return end; -} - - -template -void symlist::attach_sublist(symlist_iterator it, - symlist_iterator end) -{ - assert (empty()); - iterator last = end; - --last; - _prev = it; - --_prev; - _next = end; - - if (it == last) - { - it = last; - } - - link->adj[0] = it.act; - it.prev() = link; - link->adj[1] = last.act; - last.next() = link; -} - - -template -void symlist::detach_sublist() -{ - if (_next != iterator()) - { - iterator it(begin()); - iterator e(end()); - - --e; - - if (e == it) - { - e = it; - } - - _prev.next() = it.act; - it.prev() = _prev.act; - _next.prev() = e.act; - e.next() = _next.act; - link->adj[0] = link->adj[1] = link; - - _next = iterator(); - _prev = iterator(); - } -} - - -template -inline void symlist::reverse() -{ - symnode* tmp = link->adj[0]; - link->adj[0] = link->adj[1]; - link->adj[1] = tmp; -} - -// Implementation End - -__GTL_END_NAMESPACE - -#endif // SYMLIST_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/topsort.h b/Tracker/graph/GTL/include/GTL/topsort.h deleted file mode 100644 index b22e53d84..000000000 --- a/Tracker/graph/GTL/include/GTL/topsort.h +++ /dev/null @@ -1,143 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// topsort.h -// -//========================================================================== -// $Id: topsort.h,v 1.8 2000/09/11 07:36:43 raitner Exp $ - -#ifndef GTL_TOPSORT -#define GTL_TOPSORT - -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @short Topological sorting. - * - * Assigns to each node n a number top_num such - * that for every edge (u,v) top_num[u] < - * top_num[v], if possible, i.e. iff the directed graph is - * acyclic. - * - *

- * Similar to the testing of biconnectivity, which extends DFS to calculate - * low-numbers, the topsort-algorithm extends DFS to calculate the new - * numbering (and thus to test whether such a numbering is possible). - * - *

- * In order to traverse all the nodes in the order of its top-numbers, a - * new iterator, topsort_iterator is provided. - */ - -class GTL_EXTERN topsort : public dfs -{ -public: - /** - * default constructor; enables scanning of the whole_graph. - * - * @see dfs#dfs - */ - topsort () : dfs () {whole_graph = true; acyclic = true;} - - /** - * Number in topological order. - * - * @param n node. - * @return number in topological order. - */ - int top_num (const node& n) const - { return top_numbers[n]; } - - /** - * Tests if graph was acyclic. - * - * @return true iff graph was acyclic. - */ - bool is_acyclic () const - { return acyclic; } - - /** - * @internal - */ - typedef nodes_t::const_iterator topsort_iterator; - - /** - * Iterate through nodes in topsort-order. - * - * @return start-iterator. - */ - topsort_iterator top_order_begin() const - { return top_order.begin(); } - - /** - * Iterate through nodes in topsort-order. - * - * @return end-iterator. - */ - topsort_iterator top_order_end() const - { return top_order.end(); } - - /** - * Preconditions: - *

    - *
  • G is directed. - *
  • DFS may be applied - *
- * - * @param G graph. - * @return algorithm::GTL_OK if topsort may be applied to - * G. - * @see dfs#check - */ - virtual int check (graph& G); - - /** - * Reset - * @see dfs#reset - */ - virtual void reset (); - - /** - * @internal - */ - virtual void init_handler (graph& G); - - /** - * @internal - */ - virtual void leave_handler (graph&, node&, node&); - - /** - * @internal - */ - virtual void old_adj_node_handler (graph&, edge&, node&); - -protected: - /** - * @internal - */ - int act_top_num; - /** - * @internal - */ - node_map top_numbers; - /** - * @internal - */ - nodes_t top_order; - /** - * @internal - */ - bool acyclic; -}; - -__GTL_END_NAMESPACE - -#endif // GTL_TOPSORT - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/version.h b/Tracker/graph/GTL/include/GTL/version.h deleted file mode 100644 index 1f3148771..000000000 --- a/Tracker/graph/GTL/include/GTL/version.h +++ /dev/null @@ -1,19 +0,0 @@ -//========================================================================== -// -// version.h.in - GTL version -// -//========================================================================== -// $Id: version.h.in,v 1.1 1999/02/18 18:46:59 forster Exp $ - -#ifndef GTL_VERSION_H -#define GTL_VERSION_H - -#define GTL_MAJOR_VERSION "@MAJOR_VERSION@" -#define GTL_MINOR_VERSION "@MINOR_VERSION@" -#define GTL_MINI_VERSION "@MINI_VERSION@" - -#endif // GT_VERSION_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/GTL/version.h.in b/Tracker/graph/GTL/include/GTL/version.h.in deleted file mode 100644 index 1f3148771..000000000 --- a/Tracker/graph/GTL/include/GTL/version.h.in +++ /dev/null @@ -1,19 +0,0 @@ -//========================================================================== -// -// version.h.in - GTL version -// -//========================================================================== -// $Id: version.h.in,v 1.1 1999/02/18 18:46:59 forster Exp $ - -#ifndef GTL_VERSION_H -#define GTL_VERSION_H - -#define GTL_MAJOR_VERSION "@MAJOR_VERSION@" -#define GTL_MINOR_VERSION "@MINOR_VERSION@" -#define GTL_MINI_VERSION "@MINI_VERSION@" - -#endif // GT_VERSION_H - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/include/Makefile.am b/Tracker/graph/GTL/include/Makefile.am deleted file mode 100644 index 9d7578570..000000000 --- a/Tracker/graph/GTL/include/Makefile.am +++ /dev/null @@ -1,13 +0,0 @@ -#=========================================================================== -# -# include/Makefile.am -# -#=========================================================================== -# $Id: Makefile.am,v 1.1 1998/05/26 07:02:54 forster Exp $ - -SUBDIRS = GTL - -#--------------------------------------------------------------------------- -# end of file -#--------------------------------------------------------------------------- - diff --git a/Tracker/graph/GTL/include/Makefile.in b/Tracker/graph/GTL/include/Makefile.in deleted file mode 100644 index 906890a6b..000000000 --- a/Tracker/graph/GTL/include/Makefile.in +++ /dev/null @@ -1,495 +0,0 @@ -# Makefile.in generated by automake 1.10 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006 Free Software Foundation, Inc. -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -#=========================================================================== -# -# include/Makefile.am -# -#=========================================================================== -# $Id: Makefile.am,v 1.1 1998/05/26 07:02:54 forster Exp $ -VPATH = @srcdir@ -pkgdatadir = $(datadir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -subdir = include -DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/configure.in -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_CLEAN_FILES = -SOURCES = -DIST_SOURCES = -RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ - html-recursive info-recursive install-data-recursive \ - install-dvi-recursive install-exec-recursive \ - install-html-recursive install-info-recursive \ - install-pdf-recursive install-ps-recursive install-recursive \ - installcheck-recursive installdirs-recursive pdf-recursive \ - ps-recursive uninstall-recursive -RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ - distclean-recursive maintainer-clean-recursive -ETAGS = etags -CTAGS = ctags -DIST_SUBDIRS = $(SUBDIRS) -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -ECHO = @ECHO@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -F77 = @F77@ -FFLAGS = @FFLAGS@ -GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ -GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ -GENERIC_RELEASE = @GENERIC_RELEASE@ -GENERIC_VERSION = @GENERIC_VERSION@ -GREP = @GREP@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -LDFLAGS = @LDFLAGS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBTOOL = @LIBTOOL@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -MAKEINFO = @MAKEINFO@ -MKDIR_P = @MKDIR_P@ -OBJEXT = @OBJEXT@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -VERSION = @VERSION@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_F77 = @ac_ct_F77@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -doxygen = @doxygen@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target_alias = @target_alias@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -SUBDIRS = GTL -all: all-recursive - -.SUFFIXES: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ - && exit 0; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/Makefile'; \ - cd $(top_srcdir) && \ - $(AUTOMAKE) --gnu include/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - -# This directory's subdirectories are mostly independent; you can cd -# into them and run `make' without going through this Makefile. -# To change the values of `make' variables: instead of editing Makefiles, -# (1) if the variable is set in `config.status', edit `config.status' -# (which will cause the Makefiles to be regenerated when you run `make'); -# (2) otherwise, pass the desired values on the `make' command line. -$(RECURSIVE_TARGETS): - @failcom='exit 1'; \ - for f in x $$MAKEFLAGS; do \ - case $$f in \ - *=* | --[!k]*);; \ - *k*) failcom='fail=yes';; \ - esac; \ - done; \ - dot_seen=no; \ - target=`echo $@ | sed s/-recursive//`; \ - list='$(SUBDIRS)'; for subdir in $$list; do \ - echo "Making $$target in $$subdir"; \ - if test "$$subdir" = "."; then \ - dot_seen=yes; \ - local_target="$$target-am"; \ - else \ - local_target="$$target"; \ - fi; \ - (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ - || eval $$failcom; \ - done; \ - if test "$$dot_seen" = "no"; then \ - $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ - fi; test -z "$$fail" - -$(RECURSIVE_CLEAN_TARGETS): - @failcom='exit 1'; \ - for f in x $$MAKEFLAGS; do \ - case $$f in \ - *=* | --[!k]*);; \ - *k*) failcom='fail=yes';; \ - esac; \ - done; \ - dot_seen=no; \ - case "$@" in \ - distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ - *) list='$(SUBDIRS)' ;; \ - esac; \ - rev=''; for subdir in $$list; do \ - if test "$$subdir" = "."; then :; else \ - rev="$$subdir $$rev"; \ - fi; \ - done; \ - rev="$$rev ."; \ - target=`echo $@ | sed s/-recursive//`; \ - for subdir in $$rev; do \ - echo "Making $$target in $$subdir"; \ - if test "$$subdir" = "."; then \ - local_target="$$target-am"; \ - else \ - local_target="$$target"; \ - fi; \ - (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ - || eval $$failcom; \ - done && test -z "$$fail" -tags-recursive: - list='$(SUBDIRS)'; for subdir in $$list; do \ - test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ - done -ctags-recursive: - list='$(SUBDIRS)'; for subdir in $$list; do \ - test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ - done - -ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - mkid -fID $$unique -tags: TAGS - -TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - tags=; \ - here=`pwd`; \ - if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ - include_option=--etags-include; \ - empty_fix=.; \ - else \ - include_option=--include; \ - empty_fix=; \ - fi; \ - list='$(SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ - test ! -f $$subdir/TAGS || \ - tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ - fi; \ - done; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$tags $$unique; \ - fi -ctags: CTAGS -CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - tags=; \ - here=`pwd`; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - test -z "$(CTAGS_ARGS)$$tags$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$tags $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && cd $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) $$here - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ - fi; \ - cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ - else \ - test -f $(distdir)/$$file \ - || cp -p $$d/$$file $(distdir)/$$file \ - || exit 1; \ - fi; \ - done - list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ - test -d "$(distdir)/$$subdir" \ - || $(MKDIR_P) "$(distdir)/$$subdir" \ - || exit 1; \ - distdir=`$(am__cd) $(distdir) && pwd`; \ - top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ - (cd $$subdir && \ - $(MAKE) $(AM_MAKEFLAGS) \ - top_distdir="$$top_distdir" \ - distdir="$$distdir/$$subdir" \ - am__remove_distdir=: \ - am__skip_length_check=: \ - distdir) \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-recursive -all-am: Makefile -installdirs: installdirs-recursive -installdirs-am: -install: install-recursive -install-exec: install-exec-recursive -install-data: install-data-recursive -uninstall: uninstall-recursive - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-recursive -install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-recursive - -clean-am: clean-generic clean-libtool mostlyclean-am - -distclean: distclean-recursive - -rm -f Makefile -distclean-am: clean-am distclean-generic distclean-tags - -dvi: dvi-recursive - -dvi-am: - -html: html-recursive - -info: info-recursive - -info-am: - -install-data-am: - -install-dvi: install-dvi-recursive - -install-exec-am: - -install-html: install-html-recursive - -install-info: install-info-recursive - -install-man: - -install-pdf: install-pdf-recursive - -install-ps: install-ps-recursive - -installcheck-am: - -maintainer-clean: maintainer-clean-recursive - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-recursive - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -pdf: pdf-recursive - -pdf-am: - -ps: ps-recursive - -ps-am: - -uninstall-am: - -.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \ - install-strip - -.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ - all all-am check check-am clean clean-generic clean-libtool \ - ctags ctags-recursive distclean distclean-generic \ - distclean-libtool distclean-tags distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dvi install-dvi-am install-exec \ - install-exec-am install-html install-html-am install-info \ - install-info-am install-man install-pdf install-pdf-am \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs installdirs-am maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ - uninstall uninstall-am - - -#--------------------------------------------------------------------------- -# end of file -#--------------------------------------------------------------------------- -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/Tracker/graph/GTL/src/GTL.dsw b/Tracker/graph/GTL/src/GTL.dsw deleted file mode 100644 index 7cd08291f..000000000 --- a/Tracker/graph/GTL/src/GTL.dsw +++ /dev/null @@ -1,41 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELSCHT WERDEN! - -############################################################################### - -Project: "GTL_dynamic"=.\GTL_dynamic.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "GTL_static"=.\GTL_static.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/Tracker/graph/GTL/src/GTL.sln b/Tracker/graph/GTL/src/GTL.sln deleted file mode 100644 index 4e18be7e7..000000000 --- a/Tracker/graph/GTL/src/GTL.sln +++ /dev/null @@ -1,29 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 8.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GTL_dynamic", "GTL_dynamic.vcproj", "{07089390-1804-4A56-B337-15BED26C6421}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GTL_static", "GTL_static.vcproj", "{8A26B071-C818-4E24-B786-042233620B1F}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfiguration) = preSolution - Debug = Debug - Release = Release - EndGlobalSection - GlobalSection(ProjectConfiguration) = postSolution - {07089390-1804-4A56-B337-15BED26C6421}.Debug.ActiveCfg = Debug|Win32 - {07089390-1804-4A56-B337-15BED26C6421}.Debug.Build.0 = Debug|Win32 - {07089390-1804-4A56-B337-15BED26C6421}.Release.ActiveCfg = Release|Win32 - {07089390-1804-4A56-B337-15BED26C6421}.Release.Build.0 = Release|Win32 - {8A26B071-C818-4E24-B786-042233620B1F}.Debug.ActiveCfg = Debug|Win32 - {8A26B071-C818-4E24-B786-042233620B1F}.Debug.Build.0 = Debug|Win32 - {8A26B071-C818-4E24-B786-042233620B1F}.Release.ActiveCfg = Release|Win32 - {8A26B071-C818-4E24-B786-042233620B1F}.Release.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - EndGlobalSection - GlobalSection(ExtensibilityAddIns) = postSolution - EndGlobalSection -EndGlobal diff --git a/Tracker/graph/GTL/src/GTL_dynamic.dsp b/Tracker/graph/GTL/src/GTL_dynamic.dsp deleted file mode 100644 index 02210e4c5..000000000 --- a/Tracker/graph/GTL/src/GTL_dynamic.dsp +++ /dev/null @@ -1,338 +0,0 @@ -# Microsoft Developer Studio Project File - Name="GTL_dynamic" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** NICHT BEARBEITEN ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=GTL_dynamic - Win32 Debug -!MESSAGE Dies ist kein gltiges Makefile. Zum Erstellen dieses Projekts mit NMAKE -!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und fhren Sie den Befehl -!MESSAGE -!MESSAGE NMAKE /f "GTL_dynamic.mak". -!MESSAGE -!MESSAGE Sie knnen beim Ausfhren von NMAKE eine Konfiguration angeben -!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel: -!MESSAGE -!MESSAGE NMAKE /f "GTL_dynamic.mak" CFG="GTL_dynamic - Win32 Debug" -!MESSAGE -!MESSAGE Fr die Konfiguration stehen zur Auswahl: -!MESSAGE -!MESSAGE "GTL_dynamic - Win32 Release" (basierend auf "Win32 (x86) Dynamic-Link Library") -!MESSAGE "GTL_dynamic - Win32 Debug" (basierend auf "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "GTL_dynamic - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../bin/" -# PROP Intermediate_Dir "../build/release/GTL_dynamic" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GTL_DYNAMIC_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MD /W3 /GX /O2 /I "../include/" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GTL_EXPORTS" /YX /FD /c -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x407 /d "NDEBUG" -# ADD RSC /l 0x407 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../bin/GTL.dll" /implib:"../lib/GTL.lib" -# SUBTRACT LINK32 /pdb:none - -!ELSEIF "$(CFG)" == "GTL_dynamic - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 2 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../bin-debug/" -# PROP Intermediate_Dir "../build/debug/GTL_dynamic" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GTL_DYNAMIC_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "../include/" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GTL_EXPORTS" /D "_WINDLL" /D "_AFXDLL" /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x407 /d "_DEBUG" -# ADD RSC /l 0x407 /d "_DEBUG" /d "_AFXDLL" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"../bin-debug/GTL.dll" /implib:"../lib-debug/GTL.lib" /pdbtype:sept -# SUBTRACT LINK32 /pdb:none - -!ENDIF - -# Begin Target - -# Name "GTL_dynamic - Win32 Release" -# Name "GTL_dynamic - Win32 Debug" -# Begin Group "Quellcodedateien" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\bellman_ford.cpp -# End Source File -# Begin Source File - -SOURCE=.\bfs.cpp -# End Source File -# Begin Source File - -SOURCE=.\biconnectivity.cpp -# End Source File -# Begin Source File - -SOURCE=.\components.cpp -# End Source File -# Begin Source File - -SOURCE=.\debug.cpp -# End Source File -# Begin Source File - -SOURCE=.\dfs.cpp -# End Source File -# Begin Source File - -SOURCE=.\dijkstra.cpp -# End Source File -# Begin Source File - -SOURCE=.\edge.cpp -# End Source File -# Begin Source File - -SOURCE=.\embedding.cpp -# End Source File -# Begin Source File - -SOURCE=.\fm_partition.cpp -# End Source File -# Begin Source File - -SOURCE=.\gml_parser.cpp -# End Source File -# Begin Source File - -SOURCE=.\gml_scanner.cpp -# End Source File -# Begin Source File - -SOURCE=.\graph.cpp -# End Source File -# Begin Source File - -SOURCE=.\maxflow_ff.cpp -# End Source File -# Begin Source File - -SOURCE=.\maxflow_pp.cpp -# End Source File -# Begin Source File - -SOURCE=.\maxflow_sap.cpp -# End Source File -# Begin Source File - -SOURCE=.\min_tree.cpp -# End Source File -# Begin Source File - -SOURCE=.\node.cpp -# End Source File -# Begin Source File - -SOURCE=.\planarity.cpp -# End Source File -# Begin Source File - -SOURCE=.\pq_node.cpp -# End Source File -# Begin Source File - -SOURCE=.\pq_tree.cpp -# End Source File -# Begin Source File - -SOURCE=.\ratio_cut_partition.cpp -# End Source File -# Begin Source File - -SOURCE=.\st_number.cpp -# End Source File -# Begin Source File - -SOURCE=.\topsort.cpp -# End Source File -# End Group -# Begin Group "Header-Dateien" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\include\GTL\algorithm.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\bellman_ford.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\bfs.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\biconnectivity.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\bin_heap.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\components.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\debug.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\dfs.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\dijkstra.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\edge.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\edge_data.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\edge_map.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\embedding.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\fm_partition.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\gml_parser.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\gml_scanner.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\graph.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\GTL.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\maxflow_ff.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\maxflow_pp.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\maxflow_sap.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\min_tree.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\ne_map.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\node.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\node_data.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\node_map.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\planarity.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\pq_node.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\pq_tree.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\ratio_cut_partition.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\st_number.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\symlist.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\topsort.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\version.h -# End Source File -# End Group -# Begin Group "Ressourcendateien" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project diff --git a/Tracker/graph/GTL/src/GTL_dynamic.vcproj b/Tracker/graph/GTL/src/GTL_dynamic.vcproj deleted file mode 100644 index 76caaf5d7..000000000 --- a/Tracker/graph/GTL/src/GTL_dynamic.vcproj +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tracker/graph/GTL/src/GTL_static.dsp b/Tracker/graph/GTL/src/GTL_static.dsp deleted file mode 100644 index e5b5b75a5..000000000 --- a/Tracker/graph/GTL/src/GTL_static.dsp +++ /dev/null @@ -1,325 +0,0 @@ -# Microsoft Developer Studio Project File - Name="GTL_static" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** NICHT BEARBEITEN ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=GTL_static - Win32 Debug -!MESSAGE Dies ist kein gltiges Makefile. Zum Erstellen dieses Projekts mit NMAKE -!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und fhren Sie den Befehl -!MESSAGE -!MESSAGE NMAKE /f "GTL_static.mak". -!MESSAGE -!MESSAGE Sie knnen beim Ausfhren von NMAKE eine Konfiguration angeben -!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel: -!MESSAGE -!MESSAGE NMAKE /f "GTL_static.mak" CFG="GTL_static - Win32 Debug" -!MESSAGE -!MESSAGE Fr die Konfiguration stehen zur Auswahl: -!MESSAGE -!MESSAGE "GTL_static - Win32 Release" (basierend auf "Win32 (x86) Static Library") -!MESSAGE "GTL_static - Win32 Debug" (basierend auf "Win32 (x86) Static Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "GTL_static - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "../lib/" -# PROP Intermediate_Dir "../build/release/GTL_static" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /W3 /GX /O2 /I "../include/" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "GTL_STATIC" /YX /FD /c -# ADD BASE RSC /l 0x407 /d "NDEBUG" -# ADD RSC /l 0x407 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo /out:"../lib/GTLstatic.lib" - -!ELSEIF "$(CFG)" == "GTL_static - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "GTL_static___Win32_Debug" -# PROP BASE Intermediate_Dir "GTL_static___Win32_Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 2 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "../lib-debug/" -# PROP Intermediate_Dir "../build/debug/GTL_static" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "../include/" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "GTL_STATIC" /D "_AFXDLL" /FD /GZ /c -# SUBTRACT CPP /YX /Yc /Yu -# ADD BASE RSC /l 0x407 /d "_DEBUG" -# ADD RSC /l 0x407 /d "_DEBUG" /d "_AFXDLL" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo /out:"../lib-debug/GTLstatic.lib" - -!ENDIF - -# Begin Target - -# Name "GTL_static - Win32 Release" -# Name "GTL_static - Win32 Debug" -# Begin Group "Quellcodedateien" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\bellman_ford.cpp -# End Source File -# Begin Source File - -SOURCE=.\bfs.cpp -# End Source File -# Begin Source File - -SOURCE=.\biconnectivity.cpp -# End Source File -# Begin Source File - -SOURCE=.\components.cpp -# End Source File -# Begin Source File - -SOURCE=.\debug.cpp -# End Source File -# Begin Source File - -SOURCE=.\dfs.cpp -# End Source File -# Begin Source File - -SOURCE=.\dijkstra.cpp -# End Source File -# Begin Source File - -SOURCE=.\edge.cpp -# End Source File -# Begin Source File - -SOURCE=.\embedding.cpp -# End Source File -# Begin Source File - -SOURCE=.\fm_partition.cpp -# End Source File -# Begin Source File - -SOURCE=.\gml_parser.cpp -# End Source File -# Begin Source File - -SOURCE=.\gml_scanner.cpp -# End Source File -# Begin Source File - -SOURCE=.\graph.cpp -# End Source File -# Begin Source File - -SOURCE=.\maxflow_ff.cpp -# End Source File -# Begin Source File - -SOURCE=.\maxflow_pp.cpp -# End Source File -# Begin Source File - -SOURCE=.\maxflow_sap.cpp -# End Source File -# Begin Source File - -SOURCE=.\min_tree.cpp -# End Source File -# Begin Source File - -SOURCE=.\node.cpp -# End Source File -# Begin Source File - -SOURCE=.\planarity.cpp -# End Source File -# Begin Source File - -SOURCE=.\pq_node.cpp -# End Source File -# Begin Source File - -SOURCE=.\pq_tree.cpp -# End Source File -# Begin Source File - -SOURCE=.\ratio_cut_partition.cpp -# End Source File -# Begin Source File - -SOURCE=.\st_number.cpp -# End Source File -# Begin Source File - -SOURCE=.\topsort.cpp -# End Source File -# End Group -# Begin Group "Header-Dateien" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=..\include\GTL\algorithm.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\bellman_ford.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\bfs.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\biconnectivity.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\bin_heap.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\components.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\debug.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\dfs.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\dijkstra.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\edge.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\edge_data.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\edge_map.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\embedding.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\fm_partition.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\gml_parser.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\gml_scanner.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\graph.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\GTL.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\maxflow_ff.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\maxflow_pp.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\maxflow_sap.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\min_tree.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\ne_map.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\node.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\node_data.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\node_map.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\planarity.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\pq_node.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\pq_tree.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\ratio_cut_partition.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\st_number.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\symlist.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\topsort.h -# End Source File -# Begin Source File - -SOURCE=..\include\GTL\version.h -# End Source File -# End Group -# End Target -# End Project diff --git a/Tracker/graph/GTL/src/GTL_static.vcproj b/Tracker/graph/GTL/src/GTL_static.vcproj deleted file mode 100644 index 02c71ccf6..000000000 --- a/Tracker/graph/GTL/src/GTL_static.vcproj +++ /dev/null @@ -1,312 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tracker/graph/GTL/src/Makefile.am b/Tracker/graph/GTL/src/Makefile.am deleted file mode 100644 index 3332995ac..000000000 --- a/Tracker/graph/GTL/src/Makefile.am +++ /dev/null @@ -1,47 +0,0 @@ -#=========================================================================== -# -# src/Makefile.am -# -#=========================================================================== -# $Id: Makefile.am,v 1.37 2003/01/31 08:58:09 chris Exp $ - -lib_LTLIBRARIES = libGTL.la - -INCLUDES = \ - -I$(top_srcdir)/include \ - -I$(top_builddir)/include - -libGTL_la_LDFLAGS = \ - -version-info $(GENERIC_LIBRARY_VERSION) - -libGTL_la_SOURCES = \ - gml_scanner.cpp \ - gml_parser.cpp \ - edge.cpp \ - graph.cpp \ - node.cpp \ - dfs.cpp \ - biconnectivity.cpp \ - bfs.cpp \ - topsort.cpp \ - st_number.cpp \ - embedding.cpp \ - pq_node.cpp \ - pq_tree.cpp \ - planarity.cpp \ - maxflow_ff.cpp \ - maxflow_pp.cpp \ - maxflow_sap.cpp \ - debug.cpp \ - components.cpp \ - fm_partition.cpp \ - ratio_cut_partition.cpp \ - min_tree.cpp \ - dijkstra.cpp \ - bellman_ford.cpp \ - bid_dijkstra.cpp - -#--------------------------------------------------------------------------- -# end of file -#--------------------------------------------------------------------------- - diff --git a/Tracker/graph/GTL/src/Makefile.in b/Tracker/graph/GTL/src/Makefile.in deleted file mode 100644 index 4d62fa70d..000000000 --- a/Tracker/graph/GTL/src/Makefile.in +++ /dev/null @@ -1,541 +0,0 @@ -# Makefile.in generated by automake 1.10 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006 Free Software Foundation, Inc. -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -#=========================================================================== -# -# src/Makefile.am -# -#=========================================================================== -# $Id: Makefile.am,v 1.37 2003/01/31 08:58:09 chris Exp $ - -VPATH = @srcdir@ -pkgdatadir = $(datadir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -subdir = src -DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/configure.in -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_CLEAN_FILES = -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; -am__installdirs = "$(DESTDIR)$(libdir)" -libLTLIBRARIES_INSTALL = $(INSTALL) -LTLIBRARIES = $(lib_LTLIBRARIES) -libGTL_la_LIBADD = -am_libGTL_la_OBJECTS = gml_scanner.lo gml_parser.lo edge.lo graph.lo \ - node.lo dfs.lo biconnectivity.lo bfs.lo topsort.lo \ - st_number.lo embedding.lo pq_node.lo pq_tree.lo planarity.lo \ - maxflow_ff.lo maxflow_pp.lo maxflow_sap.lo debug.lo \ - components.lo fm_partition.lo ratio_cut_partition.lo \ - min_tree.lo dijkstra.lo bellman_ford.lo bid_dijkstra.lo -libGTL_la_OBJECTS = $(am_libGTL_la_OBJECTS) -libGTL_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ - $(CXXFLAGS) $(libGTL_la_LDFLAGS) $(LDFLAGS) -o $@ -DEFAULT_INCLUDES = -I.@am__isrc@ -depcomp = $(SHELL) $(top_srcdir)/config/depcomp -am__depfiles_maybe = depfiles -CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -CXXLD = $(CXX) -CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ - $(LDFLAGS) -o $@ -SOURCES = $(libGTL_la_SOURCES) -DIST_SOURCES = $(libGTL_la_SOURCES) -ETAGS = etags -CTAGS = ctags -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFLAGS = @CFLAGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CXXDEPMODE = @CXXDEPMODE@ -CXXFLAGS = @CXXFLAGS@ -CYGPATH_W = @CYGPATH_W@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -ECHO = @ECHO@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -F77 = @F77@ -FFLAGS = @FFLAGS@ -GENERIC_LIBRARY_NAME = @GENERIC_LIBRARY_NAME@ -GENERIC_LIBRARY_VERSION = @GENERIC_LIBRARY_VERSION@ -GENERIC_RELEASE = @GENERIC_RELEASE@ -GENERIC_VERSION = @GENERIC_VERSION@ -GREP = @GREP@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -LDFLAGS = @LDFLAGS@ -LIBOBJS = @LIBOBJS@ -LIBS = @LIBS@ -LIBTOOL = @LIBTOOL@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -MAKEINFO = @MAKEINFO@ -MKDIR_P = @MKDIR_P@ -OBJEXT = @OBJEXT@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -RANLIB = @RANLIB@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -STRIP = @STRIP@ -VERSION = @VERSION@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_CXX = @ac_ct_CXX@ -ac_ct_F77 = @ac_ct_F77@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -doxygen = @doxygen@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target_alias = @target_alias@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -lib_LTLIBRARIES = libGTL.la -INCLUDES = \ - -I$(top_srcdir)/include \ - -I$(top_builddir)/include - -libGTL_la_LDFLAGS = \ - -version-info $(GENERIC_LIBRARY_VERSION) - -libGTL_la_SOURCES = \ - gml_scanner.cpp \ - gml_parser.cpp \ - edge.cpp \ - graph.cpp \ - node.cpp \ - dfs.cpp \ - biconnectivity.cpp \ - bfs.cpp \ - topsort.cpp \ - st_number.cpp \ - embedding.cpp \ - pq_node.cpp \ - pq_tree.cpp \ - planarity.cpp \ - maxflow_ff.cpp \ - maxflow_pp.cpp \ - maxflow_sap.cpp \ - debug.cpp \ - components.cpp \ - fm_partition.cpp \ - ratio_cut_partition.cpp \ - min_tree.cpp \ - dijkstra.cpp \ - bellman_ford.cpp \ - bid_dijkstra.cpp - -all: all-am - -.SUFFIXES: -.SUFFIXES: .cpp .lo .o .obj -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ - && exit 0; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ - cd $(top_srcdir) && \ - $(AUTOMAKE) --gnu src/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -install-libLTLIBRARIES: $(lib_LTLIBRARIES) - @$(NORMAL_INSTALL) - test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" - @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ - if test -f $$p; then \ - f=$(am__strip_dir) \ - echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ - $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ - else :; fi; \ - done - -uninstall-libLTLIBRARIES: - @$(NORMAL_UNINSTALL) - @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ - p=$(am__strip_dir) \ - echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ - $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ - done - -clean-libLTLIBRARIES: - -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) - @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ - dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ - test "$$dir" != "$$p" || dir=.; \ - echo "rm -f \"$${dir}/so_locations\""; \ - rm -f "$${dir}/so_locations"; \ - done -libGTL.la: $(libGTL_la_OBJECTS) $(libGTL_la_DEPENDENCIES) - $(libGTL_la_LINK) -rpath $(libdir) $(libGTL_la_OBJECTS) $(libGTL_la_LIBADD) $(LIBS) - -mostlyclean-compile: - -rm -f *.$(OBJEXT) - -distclean-compile: - -rm -f *.tab.c - -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bellman_ford.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bfs.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/biconnectivity.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bid_dijkstra.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/components.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dfs.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dijkstra.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edge.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/embedding.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fm_partition.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gml_parser.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gml_scanner.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/graph.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maxflow_ff.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maxflow_pp.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maxflow_sap.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/min_tree.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/planarity.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pq_node.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pq_tree.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ratio_cut_partition.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/st_number.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/topsort.Plo@am__quote@ - -.cpp.o: -@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< - -.cpp.obj: -@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` -@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - -.cpp.lo: -@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - -ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - mkid -fID $$unique -tags: TAGS - -TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - tags=; \ - here=`pwd`; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$tags $$unique; \ - fi -ctags: CTAGS -CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - tags=; \ - here=`pwd`; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - test -z "$(CTAGS_ARGS)$$tags$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$tags $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && cd $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) $$here - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ - fi; \ - cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ - else \ - test -f $(distdir)/$$file \ - || cp -p $$d/$$file $(distdir)/$$file \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile $(LTLIBRARIES) -installdirs: - for dir in "$(DESTDIR)$(libdir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ - mostlyclean-am - -distclean: distclean-am - -rm -rf ./$(DEPDIR) - -rm -f Makefile -distclean-am: clean-am distclean-compile distclean-generic \ - distclean-tags - -dvi: dvi-am - -dvi-am: - -html: html-am - -info: info-am - -info-am: - -install-data-am: - -install-dvi: install-dvi-am - -install-exec-am: install-libLTLIBRARIES - -install-html: install-html-am - -install-info: install-info-am - -install-man: - -install-pdf: install-pdf-am - -install-ps: install-ps-am - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: uninstall-libLTLIBRARIES - -.MAKE: install-am install-strip - -.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ - clean-libLTLIBRARIES clean-libtool ctags distclean \ - distclean-compile distclean-generic distclean-libtool \ - distclean-tags distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am \ - install-libLTLIBRARIES install-man install-pdf install-pdf-am \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-compile \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags uninstall uninstall-am uninstall-libLTLIBRARIES - - -#--------------------------------------------------------------------------- -# end of file -#--------------------------------------------------------------------------- -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/Tracker/graph/GTL/src/bellman_ford.cpp b/Tracker/graph/GTL/src/bellman_ford.cpp deleted file mode 100644 index bc7be35e9..000000000 --- a/Tracker/graph/GTL/src/bellman_ford.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// bellman_ford.cpp -// -//========================================================================== -// $Id: bellman_ford.cpp,v 1.4 2003/01/30 17:50:56 raitner Exp $ - -#include - -__GTL_BEGIN_NAMESPACE - -bellman_ford::bellman_ford() -{ - vars_set = false; - preds = 0; -} - -bellman_ford::~bellman_ford() -{ - if (preds) delete preds; -} - -void bellman_ford::store_preds (bool set) -{ - if (set && !preds) { - preds = new node_map; - } else if (!set && preds) { - delete preds; - preds = 0; - } -} - - -int bellman_ford::check(graph& G) -{ - if (!vars_set) - { - return algorithm::GTL_ERROR; - } - - if (G.nodes_begin() == G.nodes_end()) - { - return algorithm::GTL_ERROR; - } - - return algorithm::GTL_OK; -} - -int bellman_ford::run(graph& G) -{ - if (s == node()) - { - s = *(G.nodes_begin()); - } - - //---------------------------------------------------------------------- - // initialize - //---------------------------------------------------------------------- - - inf.init (G, true); - - if (preds) { - preds->init (G, edge()); - } - - inf[s] = false; - d[s] = 0; - cycle = false; - - //---------------------------------------------------------------------- - // relax - //---------------------------------------------------------------------- - - graph::edge_iterator it, end; - - for (int i = 1; i < G.number_of_nodes(); ++i) - { - for (it = G.edges_begin(), end = G.edges_end(); it != end; ++it) - { - relax (*it, true); - - if (G.is_undirected()) - { - relax(*it, false); - } - } - } - - //---------------------------------------------------------------------- - // cycle detection - //---------------------------------------------------------------------- - - for (it = G.edges_begin(), end = G.edges_end(); it != end; ++it) - { - node u = it->source(); - node v = it->target(); - - if(!inf[u] && !inf[v]) - { - if (d[v] > d[u] + w[*it]) - { - cycle = true; - } - } - } - - return algorithm::GTL_OK; -} - -void bellman_ford::reset() -{ -} - -void bellman_ford::relax(const edge& e, bool dir ) -{ - node u = e.source(); - node v = e.target(); - - if (!dir) { - node tmp = u; - u = v; - v = tmp; - } - - if (!inf[u] && (inf[v] || (d[v] > d[u] + w[e]))) - { - d[v] = d[u] + w[e]; - inf[v] = false; - - if (preds) - { - (*preds)[v] = e; - } - } -} - - - -__GTL_END_NAMESPACE diff --git a/Tracker/graph/GTL/src/bfs.cpp b/Tracker/graph/GTL/src/bfs.cpp deleted file mode 100644 index 95f0dd689..000000000 --- a/Tracker/graph/GTL/src/bfs.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// bfs.cpp -// -//========================================================================== -// $Id: bfs.cpp,v 1.11 2001/11/07 13:58:09 pick Exp $ - -#include -#include - -__GTL_BEGIN_NAMESPACE - -//-------------------------------------------------------------------------- -// Con-/Destructors -//-------------------------------------------------------------------------- - -bfs::bfs () : algorithm () -{ - level_number = 0; - preds = 0; - non_tree = 0; - act_bfs_num = 1; - reached_nodes = 0; - whole_graph = false; -} - -bfs::~bfs () -{ - if (level_number) delete level_number; - if (preds) delete preds; - if (non_tree) delete non_tree; -} - - -//-------------------------------------------------------------------------- -// Parameters -//-------------------------------------------------------------------------- - -void bfs::calc_level (bool set) -{ - if (set && !level_number) { - level_number = new node_map; - } else if (!set && level_number) { - delete level_number; - level_number = 0; - } -} - -void bfs::store_preds (bool set) -{ - if (set && !preds) { - preds = new node_map; - } else if (!set && preds) { - delete preds; - preds = 0; - } -} - -void bfs::store_non_tree_edges (bool set) -{ - if (set && !non_tree) - { - non_tree = new edges_t; - } - else if (!set && non_tree) - { - delete non_tree; - non_tree = 0; - } -} - -//-------------------------------------------------------------------------- -// GTL_Algorithm - Interface -//-------------------------------------------------------------------------- - -void bfs::reset () -{ - act_bfs_num = 1; - tree.erase (tree.begin(), tree.end()); - bfs_order.erase (bfs_order.begin(), bfs_order.end()); - roots.erase (roots.begin(), roots.end()); - reached_nodes = 0; - if (non_tree) { - non_tree->erase (non_tree->begin(), non_tree->end()); - } -} - - -int bfs::run (graph& G) { - - bfs_number.init (G, 0); - - if (level_number) { - level_number->init (G); - } - - if (preds) { - preds->init (G, node()); - } - - edge_map *used = 0; - - if (non_tree) { - used = new edge_map (G, 0); - } - - init_handler (G); - - // - // Set start-node - // - - if (start == node()) { - start = G.choose_node(); - } - - new_start_handler (G, start); - - bfs_sub (G, start, used); - - node curr; - - if (whole_graph && reached_nodes < G.number_of_nodes()) { - forall_nodes (curr, G) { - if (bfs_number[curr] == 0) { - - new_start_handler (G, curr); - - bfs_sub (G, curr, used); - } - } - } - - if (non_tree) { - delete used; - } - - end_handler (G); - - return 1; -} - - - -//-------------------------------------------------------------------------- -// PRIVATE -//-------------------------------------------------------------------------- - - -void bfs::bfs_sub (graph& G, const node& st, edge_map* used) -{ - qu.push_back (st); - bfs_number[st] = act_bfs_num; - ++act_bfs_num; - - if (level_number) { - (*level_number)[st] = 0; - } - - while (!qu.empty()) { - node tmp = qu.front(); - qu.pop_front(); - ++reached_nodes; - - if (tmp == st) { - roots.push_back (bfs_order.insert (bfs_order.end(), tmp)); - } else { - bfs_order.push_back (tmp); - } - - popped_node_handler (G, tmp); - - node::adj_edges_iterator it = tmp.adj_edges_begin(); - node::adj_edges_iterator end = tmp.adj_edges_end(); - - for (; it != end; ++it) { - edge curr = *it; - node opp = tmp.opposite (curr); - - if (bfs_number[opp] == 0) { - bfs_number[opp] = act_bfs_num; - ++act_bfs_num; - tree.push_back (curr); - - if (non_tree) { - (*used)[curr] = 1; - } - - if (level_number) { - (*level_number)[opp] = (*level_number)[tmp] + 1; - } - - if (preds) { - (*preds)[opp] = tmp; - } - - qu.push_back (opp); - - unused_node_handler (G, opp, tmp); - - } else { - if (non_tree && !(*used)[curr]) { - (*used)[curr] = 1; - non_tree->push_back(curr); - } - - used_node_handler (G, opp, tmp); - } - } - - finished_node_handler (G, tmp); - } -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/biconnectivity.cpp b/Tracker/graph/GTL/src/biconnectivity.cpp deleted file mode 100644 index a4a9236a3..000000000 --- a/Tracker/graph/GTL/src/biconnectivity.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// biconnectivity.cpp -// -//========================================================================== -// $Id: biconnectivity.cpp,v 1.20 2002/02/28 15:40:52 raitner Exp $ - -#include - -__GTL_BEGIN_NAMESPACE - -biconnectivity::biconnectivity() : dfs() -{ - add_edges = false; - store_preds(true); - store_comp = false; - scan_whole_graph(true); - num_of_components = 0; -} - -void biconnectivity::reset() -{ - dfs::reset(); - - if (store_comp) { - while (!node_stack.empty()) { - node_stack.pop(); - } - - while (!edge_stack.empty()) { - edge_stack.pop(); - } - - components.erase(components.begin(), components.end()); - } - - if (add_edges) { - additional.erase(additional.begin(), additional.end()); - } - - cut_points.erase(cut_points.begin(), cut_points.end()); - num_of_components = 0; -} - -int biconnectivity::check(graph& G) -{ - return G.is_undirected() && preds && - dfs::check(G) == GTL_OK ? GTL_OK : GTL_ERROR; -} - - -//-------------------------------------------------------------------------- -// Handler -//-------------------------------------------------------------------------- - - -void biconnectivity::init_handler(graph& G) -{ - if (add_edges) { - dfs D; - D.scan_whole_graph(true); - D.check(G); - D.run(G); - - roots_iterator it, end; - it = D.roots_begin(); - end = D.roots_end(); - start = *(*it); - ++it; - - for (; it != end; ++it) { - additional.push_back(G.new_edge(start, *(*it))); - } - - first_child.init(G, node()); - } - - low_num.init(G); - in_component.init(G); - cut_count.init(G, 0); - - // - // Detect self loops and hide them. - // - - assert(self_loops.empty()); - graph::edge_iterator eit = G.edges_begin(), - eend = G.edges_end(); - - while (eit != eend) { - edge e = *eit; - eit++; - if (e.target() == e.source()) { - self_loops.push_back(e); - G.hide_edge(e); - } - } -} - -void biconnectivity::entry_handler(graph& /*G*/, node& curr, node& father) -{ - if (add_edges) { - if (father != node()) { - if (first_child[father] == node()) { - first_child[father] = curr; - } - } - } - - low_num[curr] = dfs_number[curr]; -} - -void biconnectivity::new_start_handler(graph& /*G*/, node& st) -{ - cut_count[st] = -1; - - // - // If this node has no adjacent edges, we - // must write down the component right here. This is because - // then the method after_recursive_call_handle is never - // executed. - // - // 28/2/2002 MR - // - - if (st.degree() == 0) { - ++num_of_components; - - if (store_comp) { - component_iterator li = components.insert( - components.end(), - std::pair(nodes_t(), edges_t())); - - li->first.push_back(st); - in_component[st] = li; - } - } -} - -void biconnectivity::before_recursive_call_handler(graph& /*G*/, edge& /*e*/, node& n) -{ - if (store_comp) { - node_stack.push(n); - } -} - - -void biconnectivity::after_recursive_call_handler(graph& G, edge& e, node& n) -{ - node curr = n.opposite(e); - - if (low_num[n] < low_num[curr]) { - low_num[curr] = low_num[n]; - } - - if (low_num[n] >= dfs_num(curr)) { - // - // Component found - // - - if (store_comp) { - component_iterator li = components.insert( - components.end(), - std::pair(nodes_t(), edges_t())); - - nodes_t& component = li->first; - edges_t& co_edges = li->second; - - // - // Nodes of biconnected component - // - - node tmp = node_stack.top(); - - while (dfs_num(tmp) >= dfs_num(n)) { - node_stack.pop(); - component.push_back(tmp); - in_component[tmp] = li; - if (node_stack.empty()) break; - else tmp = node_stack.top(); - } - - component.push_back(curr); - - // - // edges of biconnected component - // - - edge ed = edge_stack.top(); - - while ((dfs_num(ed.source()) >= dfs_num(n) && - dfs_num(ed.target()) >= dfs_num(n)) || - (dfs_num(ed.source()) == dfs_num(curr) && - dfs_num(ed.target()) >= dfs_num(n)) || - (dfs_num(ed.source()) >= dfs_num(n) && - dfs_num(ed.target()) == dfs_num(curr))) { - edge_stack.pop(); - co_edges.push_back(ed); - if (edge_stack.empty()) break; - else ed = edge_stack.top(); - } - } - - - ++num_of_components; - - // - // curr is cut point; increase counter - // - - ++cut_count[curr]; - - if (add_edges) { - node father = (*preds)[curr]; - node first = first_child[curr]; - - if (father != node() && n == first) { - additional.push_back(G.new_edge(father, first)); - } - - if (n != first) { - additional.push_back(G.new_edge(n, first)); - } - } - - } -} - -void biconnectivity::old_adj_node_handler(graph& /*G*/, edge& e, node& n) -{ - node curr = n.opposite(e); - - // - // Store backedges at lower endpoint - // - - if (store_comp) { - if (dfs_num(curr) > dfs_num(n)) { - edge_stack.push(e); - } - } - - if (dfs_num(n) < low_num[curr]) { - low_num[curr] = dfs_number[n]; - } -} - -void biconnectivity::leave_handler(graph& /*G*/, node& n, node& /*f*/) -{ - if (cut_count[n] > 0) - { - cut_points.push_back(n); - } -} - -void biconnectivity::end_handler(graph& G) -{ - edges_t::iterator it = self_loops.begin(); - edges_t::iterator end = self_loops.end(); - - while (it != end) - { - G.restore_edge(*it); - if (store_comp) - { - component_iterator cit = in_component[it->target()]; - cit->second.push_back(*it); - } - - it = self_loops.erase(it); - } -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/bid_dijkstra.cpp b/Tracker/graph/GTL/src/bid_dijkstra.cpp deleted file mode 100644 index c88c47cc9..000000000 --- a/Tracker/graph/GTL/src/bid_dijkstra.cpp +++ /dev/null @@ -1,580 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// bid_dijkstra.cpp -// -//========================================================================== -//$Id: bid_dijkstra.cpp,v 1.2 2004/05/06 11:58:19 chris Exp $ - -#include -#include - -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @internal - * Binary predicate that compares two nodes according to their distance. - */ -class less_dist : public std::binary_function -{ -public: - /** - * @internal - * Constructor sets pointer to node distances and infimum info. - */ - less_dist(const node_map* dist, const node_map* mark) - { - this->dist = dist; - this->mark = mark; - } - - /** - * @internal - * Compares distances of @p n1 and @p n2. - */ - bool operator()(const node n1, const node n2) const - { - if (((*mark)[n1] == bid_dijkstra::black) && - ((*mark)[n2] == bid_dijkstra::black)) - { - return false; - } - else if ((*mark)[n1] == bid_dijkstra::black) - { - return false; - } - else if ((*mark)[n2] == bid_dijkstra::black) - { - return true; - } - return (*dist)[n1] < (*dist)[n2]; - } -private: - /** - * @internal - * Node distances from source. - */ - const node_map* dist; - - /** - * @internal - * Infimum distance info (color of nodes). - */ - const node_map* mark; -}; - - -bid_dijkstra::bid_dijkstra() -{ - reset_algorithm(); -} - - -bid_dijkstra::~bid_dijkstra() -{ -} - - -void bid_dijkstra::source_target(const node& s, const node& t) -{ - this->s = s; - this->t = t; -} - - -void bid_dijkstra::weights(const edge_map& weight) -{ - this->weight = weight; - weights_set = true; -} - - -void bid_dijkstra::store_path(bool set) -{ - path_set = set; -} - - -int bid_dijkstra::check(graph& G) -{ - if ((s == node()) || (t == node()) || (!weights_set)) - { - return GTL_ERROR; - } - - bool source_found = false; - bool target_found = false; - graph::node_iterator node_it; - graph::node_iterator nodes_end = G.nodes_end(); - for (node_it = G.nodes_begin(); node_it != nodes_end; ++node_it) - { - if (*node_it == s) - { - source_found = true; - if (target_found) - { - break; - } - } - if (*node_it == t) - { - target_found = true; - if (source_found) - { - break; - } - } - } - if ((!source_found) || (!target_found)) - { - return(GTL_ERROR); - } - - graph::edge_iterator edge_it; - graph::edge_iterator edges_end = G.edges_end(); - for(edge_it = G.edges_begin(); edge_it != edges_end; ++edge_it) - { - if (weight[*edge_it] < 0.0) - { - return false; - } - } - - return GTL_OK; -} - - -int bid_dijkstra::run(graph& G) -{ - init(G); - - double max_dist = 1; - graph::edge_iterator edge_it; - graph::edge_iterator edges_end = G.edges_end(); - for(edge_it = G.edges_begin(); edge_it != edges_end; ++edge_it) - { - max_dist += weight[*edge_it]; - } - - less_dist source_prd(&source_dist, &source_mark); - less_dist target_prd(&target_dist, &target_mark); - bin_heap source_heap(source_prd, - G.number_of_nodes()); - bin_heap target_heap(target_prd, - G.number_of_nodes()); - - source_mark[s] = grey; - source_dist[s] = 0.0; - source_heap.push(s); - target_mark[t] = grey; - target_dist[t] = 0.0; - target_heap.push(t); - while ((!source_heap.is_empty()) || (!target_heap.is_empty())) - { - if (source_dist[source_heap.top()] <= - target_dist[target_heap.top()]) - { - - // debug: - // source_heap.print_data_container(); - - node cur_node = source_heap.top(); - source_heap.pop(); - - // debug: - // source_heap.print_data_container(); - - source_mark[cur_node] = white; - - if ((target_mark[cur_node] == white) && - (max_dist == source_dist[cur_node] + - target_dist[cur_node])) - { - fill_node_edge_lists(cur_node); - break; - } - - node::adj_edges_iterator adj_edge_it; - node::adj_edges_iterator adj_edges_end = - cur_node.adj_edges_end(); - for (adj_edge_it = cur_node.adj_edges_begin(); - adj_edge_it != adj_edges_end; - ++adj_edge_it) - { - node op_node = adj_edge_it->opposite(cur_node); - if (source_mark[op_node] == black) - { - source_mark[op_node] = grey; - source_dist[op_node] = source_dist[cur_node] + - weight[*adj_edge_it]; - source_heap.push(op_node); - - // debug: - // source_heap.print_data_container(); - - if (path_set) - { - pred[op_node] = *adj_edge_it; - } - - if ((target_mark[op_node] == grey) || - (target_mark[op_node] == white)) - { - if (max_dist > source_dist[op_node] + - target_dist[op_node]) - { - max_dist = source_dist[op_node] + - target_dist[op_node]; - } - } - } - else if (source_mark[op_node] == grey) - { - if (source_dist[op_node] > source_dist[cur_node] + - weight[*adj_edge_it]) - { - source_dist[op_node] = source_dist[cur_node] + - weight[*adj_edge_it]; - source_heap.changeKey(op_node); - - // debug: - // source_heap.print_data_container(); - - if (path_set) - { - pred[op_node] = *adj_edge_it; - } - - if ((target_mark[op_node] == grey) || - (target_mark[op_node] == white)) - { - if (max_dist > source_dist[op_node] + - target_dist[op_node]) - { - max_dist = source_dist[op_node] + - target_dist[op_node]; - } - } - } - } - else // (source_mark[op_node] == white) - { - // nothing to do: shortest distance to op_node is - // already computed - } - } - } - else // (source_dist[source_heap.top()] > - // target_dist[target_heap.top()]) - { - - // debug: - // target_heap.print_data_container(); - - node cur_node = target_heap.top(); - target_heap.pop(); - - // debug: - // target_heap.print_data_container(); - - target_mark[cur_node] = white; - - if ((source_mark[cur_node] == white) && - (max_dist == source_dist[cur_node] + - target_dist[cur_node])) - { - fill_node_edge_lists(cur_node); - break; - } - - if (G.is_directed()) - { - node::in_edges_iterator in_edge_it; - node::in_edges_iterator in_edges_end = - cur_node.in_edges_end(); - for (in_edge_it = cur_node.in_edges_begin(); - in_edge_it != in_edges_end; - ++in_edge_it) - { - node op_node = in_edge_it->opposite(cur_node); - if (target_mark[op_node] == black) - { - target_mark[op_node] = grey; - target_dist[op_node] = target_dist[cur_node] + - weight[*in_edge_it]; - target_heap.push(op_node); - - // debug: - // target_heap.print_data_container(); - - if (path_set) - { - succ[op_node] = *in_edge_it; - } - - if ((source_mark[op_node] == grey) || - (source_mark[op_node] == white)) - { - if (max_dist > source_dist[op_node] + - target_dist[op_node]) - { - max_dist = source_dist[op_node] + - target_dist[op_node]; - } - } - } - else if (target_mark[op_node] == grey) - { - if (target_dist[op_node] > target_dist[cur_node] + - weight[*in_edge_it]) - { - target_dist[op_node] = target_dist[cur_node] + - weight[*in_edge_it]; - target_heap.changeKey(op_node); - - // debug: - // target_heap.print_data_container(); - - if (path_set) - { - succ[op_node] = *in_edge_it; - } - - if ((source_mark[op_node] == grey) || - (source_mark[op_node] == white)) - { - if (max_dist > source_dist[op_node] + - target_dist[op_node]) - { - max_dist = source_dist[op_node] + - target_dist[op_node]; - } - } - } - } - else // (target_mark[op_node] == white) - { - // nothing to do: shortest distance to op_node is - // already computed - } - } - } - else // (G.is_undirected()) - { - node::adj_edges_iterator adj_edge_it; - node::adj_edges_iterator adj_edges_end = - cur_node.adj_edges_end(); - for (adj_edge_it = cur_node.adj_edges_begin(); - adj_edge_it != adj_edges_end; - ++adj_edge_it) - { - node op_node = adj_edge_it->opposite(cur_node); - if (target_mark[op_node] == black) - { - target_mark[op_node] = grey; - target_dist[op_node] = target_dist[cur_node] + - weight[*adj_edge_it]; - target_heap.push(op_node); - - // debug: - // target_heap.print_data_container(); - - if (path_set) - { - succ[op_node] = *adj_edge_it; - } - - if ((source_mark[op_node] == grey) || - (source_mark[op_node] == white)) - { - if (max_dist > source_dist[op_node] + - target_dist[op_node]) - { - max_dist = source_dist[op_node] + - target_dist[op_node]; - } - } - } - else if (target_mark[op_node] == grey) - { - if (target_dist[op_node] > target_dist[cur_node] + - weight[*adj_edge_it]) - { - target_dist[op_node] = target_dist[cur_node] + - weight[*adj_edge_it]; - target_heap.changeKey(op_node); - - // debug: - // target_heap.print_data_container(); - - if (path_set) - { - succ[op_node] = *adj_edge_it; - } - - if ((source_mark[op_node] == grey) || - (source_mark[op_node] == white)) - { - if (max_dist > source_dist[op_node] + - target_dist[op_node]) - { - max_dist = source_dist[op_node] + - target_dist[op_node]; - } - } - } - } - else // (target_mark[op_node] == white) - { - // nothing to do: shortest distance to op_node is - // already computed - } - } - } - } - } - - return GTL_OK; -} - - -node bid_dijkstra::source() const -{ - return s; -} - - -node bid_dijkstra::target() const -{ - return t; -} - - -bool bid_dijkstra::store_path() const -{ - return path_set; -} - - -bool bid_dijkstra::reached() const -{ - return reached_t; -} - - -double bid_dijkstra::distance() const -{ - return dist; -} - - -bid_dijkstra::shortest_path_node_iterator -bid_dijkstra::shortest_path_nodes_begin() -{ - assert(path_set); - return shortest_path_node_list.begin(); -} - - -bid_dijkstra::shortest_path_node_iterator -bid_dijkstra::shortest_path_nodes_end() -{ - assert(path_set); - return shortest_path_node_list.end(); -} - - -bid_dijkstra::shortest_path_edge_iterator -bid_dijkstra::shortest_path_edges_begin() -{ - assert(path_set); - return shortest_path_edge_list.begin(); -} - - -bid_dijkstra::shortest_path_edge_iterator -bid_dijkstra::shortest_path_edges_end() -{ - assert(path_set); - return shortest_path_edge_list.end(); -} - - -void bid_dijkstra::reset() -{ - reset_algorithm(); -} - - -void bid_dijkstra::reset_algorithm() -{ - s = node(); - t = node(); - weights_set = false; - path_set = false; - dist = -1.0; - reached_t = false; -} - - -void bid_dijkstra::init(graph& G) -{ - source_dist.init(G, -1.0); - source_mark.init(G, black); - target_dist.init(G, -1.0); - target_mark.init(G, black); - - if (path_set) - { - pred.init(G, edge()); - succ.init(G, edge()); - shortest_path_node_list.clear(); - shortest_path_edge_list.clear(); - } -} - - -void bid_dijkstra::fill_node_edge_lists(const node& n) -{ - reached_t = true; - if (t == s) - { - return; - } - dist = source_dist[n] + target_dist[n]; - if (path_set) - { - node cur_node; - edge cur_edge; - - cur_node = n; - cur_edge = pred[cur_node]; - while (cur_edge != edge()) - { - shortest_path_edge_list.push_front(cur_edge); - cur_node = cur_edge.opposite(cur_node); - cur_edge = pred[cur_node]; - shortest_path_node_list.push_front(cur_node); - } - shortest_path_node_list.push_back(n); - cur_node = n; - cur_edge = succ[cur_node]; - while (cur_edge != edge()) - { - shortest_path_edge_list.push_back(cur_edge); - cur_node = cur_edge.opposite(cur_node); - cur_edge = succ[cur_node]; - shortest_path_node_list.push_back(cur_node); - } - } -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/components.cpp b/Tracker/graph/GTL/src/components.cpp deleted file mode 100644 index 53d68e12c..000000000 --- a/Tracker/graph/GTL/src/components.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// components.cpp -// -//========================================================================== -// $Id: components.cpp,v 1.5 2001/11/07 13:58:09 pick Exp $ - -#include - -__GTL_BEGIN_NAMESPACE - -components::components () : dfs () -{ - scan_whole_graph (true); - num_of_components = 0; -} - -void components::reset () -{ - dfs::reset (); - comp.erase (comp.begin(), comp.end()); - num_of_components = 0; -} - -int components::check (graph& G) -{ - return G.is_undirected() && whole_graph && - dfs::check (G) == GTL_OK ? GTL_OK : GTL_ERROR; -} - - -//-------------------------------------------------------------------------- -// Handler -//-------------------------------------------------------------------------- - - -void components::new_start_handler (graph& /*G*/, node& st) -{ - li = comp.insert(comp.end(), std::pair(nodes_t(), edges_t())); - li->first.push_back(st); - ++num_of_components; -} - -void components::before_recursive_call_handler (graph& /*G*/, edge& /*e*/, node& n) -{ - li->first.push_back(n); - // li->second.push_back(e); -} - - -void components::old_adj_node_handler (graph& /*G*/, edge& e, node& n) -{ - node curr = n.opposite (e); - - // - // Store backedges at lower endpoint - // - - if (dfs_num (curr) > dfs_num (n)) { - li->second.push_back (e); - } -} - - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/debug.cpp b/Tracker/graph/GTL/src/debug.cpp deleted file mode 100644 index 615da85cd..000000000 --- a/Tracker/graph/GTL/src/debug.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// debug.cpp -// -//========================================================================== -// $Id: debug.cpp,v 1.10 2001/11/07 13:58:09 pick Exp $ - -#include - -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -std::ostream* GTL_debug::GTLerr = 0; - -void GTL_debug::debug_message (const char* message, ...) -{ -#ifdef _DEBUG - va_list arg_list; - va_start(arg_list, message); - - char buf[1024]; - vsprintf(buf, message, arg_list); - if (GTLerr) { - os() << buf; - } -#endif -} - -void GTL_debug::init_debug () -{ - if (!GTLerr) { -#ifdef __GTL_MSVCC - GTLerr = new std::ofstream("ERRLOG.txt", std::ios::out | std::ios::app); -#else - GTLerr = &std::cerr; -#endif - } -} - -void GTL_debug::close_debug () -{ - if (GTLerr) { -#ifdef __GTL_MSVCC - ((std::ofstream*) GTLerr)->close(); - delete GTLerr; - GTLerr = 0; -#endif - } -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/dfs.cpp b/Tracker/graph/GTL/src/dfs.cpp deleted file mode 100644 index dd898c45c..000000000 --- a/Tracker/graph/GTL/src/dfs.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// dfs.cpp -// -//========================================================================== -// $Id: dfs.cpp,v 1.18 2001/11/07 13:58:09 pick Exp $ - -#include -#include - -__GTL_BEGIN_NAMESPACE - -//-------------------------------------------------------------------------- -// Con-/Destructors -//-------------------------------------------------------------------------- - -dfs::dfs () : algorithm () -{ - act_dfs_num = 1; - act_comp_num = 1; - reached_nodes = 0; - whole_graph = false; - comp_number = 0; - preds = 0; - used = 0; - back_edges = 0; -} - -dfs::~dfs () -{ - if (comp_number) delete comp_number; - if (preds) delete preds; - if (back_edges) { - delete back_edges; - delete used; - } -} - -//-------------------------------------------------------------------------- -// GTL_Algorithm - Interface -//-------------------------------------------------------------------------- - - -void dfs::reset () -{ - act_dfs_num = 1; - act_comp_num = 1; - reached_nodes = 0; - tree.erase (tree.begin(), tree.end()); - dfs_order.erase (dfs_order.begin(), dfs_order.end()); - roots.erase (roots.begin(), roots.end()); - start = node(); - - if (back_edges) { - back_edges->erase (back_edges->begin(), back_edges->end()); - } -} - - -int dfs::check (graph& /*G*/) -{ - return GTL_OK; -} - -int dfs::run (graph& G) -{ - // - // initialization - // - - node curr; - node dummy; - - dfs_number.init (G, 0); - - if (comp_number) { - comp_number->init (G); - } - - if (preds) { - preds->init (G, node()); - } - - if (back_edges) { - used = new edge_map (G, 0); - } - - init_handler (G); - - // - // Set start-node - // - - if (G.number_of_nodes() == 0) { - return GTL_OK; - } - - if (start == node()) { - start = G.choose_node(); - } - - new_start_handler (G, start); - - dfs_sub (G, start, dummy); - - if (whole_graph && reached_nodes < G.number_of_nodes()) { - - // - // Continue DFS with next unused node. - // - - forall_nodes (curr, G) { - if (dfs_number[curr] == 0) { - new_start_handler (G, curr); - dfs_sub (G, curr, dummy); - } - } - } - - if (back_edges) { - delete used; - used = 0; - } - - end_handler(G); - - return GTL_OK; -} - - -//-------------------------------------------------------------------------- -// PRIVATE -//-------------------------------------------------------------------------- - - -void dfs::dfs_sub (graph& G, node& curr, node& father) -{ - node opp; - edge adj; - - if (father == node()) { - roots.push_back (dfs_order.insert (dfs_order.end(), curr)); - } else { - dfs_order.push_back (curr); - } - - dfs_number[curr] = act_dfs_num; - reached_nodes++; - - if (preds) { - (*preds)[curr] = father; - } - - entry_handler (G, curr, father); - - ++act_dfs_num; - node::adj_edges_iterator it = curr.adj_edges_begin(); - node::adj_edges_iterator end = curr.adj_edges_end(); - - while (it != end) { - adj = *it; - opp = curr.opposite(adj); - - if (dfs_number[opp] == 0) { - tree.push_back (adj); - - if (back_edges) { - (*used)[adj] = 1; - } - - before_recursive_call_handler (G, adj, opp); - dfs_sub (G, opp, curr); - after_recursive_call_handler (G, adj, opp); - - } else { - if (back_edges && !(*used)[adj]) { - (*used)[adj] = 1; - back_edges->push_back (adj); - } - - old_adj_node_handler (G, adj, opp); - } - - ++it; - } - - leave_handler (G, curr, father); - - if (comp_number) { - (*comp_number)[curr] = act_comp_num; - ++act_comp_num; - } -} - -//-------------------------------------------------------------------------- -// Parameters -//-------------------------------------------------------------------------- - -void dfs::calc_comp_num (bool set) -{ - if (set && !comp_number) { - comp_number = new node_map; - } else if (!set && comp_number) { - delete comp_number; - comp_number = 0; - } -} - -void dfs::store_preds (bool set) -{ - if (set && !preds) { - preds = new node_map; - } else if (!set && preds) { - delete preds; - preds = 0; - } -} - -void dfs::store_non_tree_edges (bool set) -{ - if (set && !back_edges) - { - back_edges = new edges_t; - } - else if (!set && back_edges) - { - delete back_edges; - back_edges = 0; - } -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/dijkstra.cpp b/Tracker/graph/GTL/src/dijkstra.cpp deleted file mode 100644 index 1a2bbf07e..000000000 --- a/Tracker/graph/GTL/src/dijkstra.cpp +++ /dev/null @@ -1,396 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// dijkstra.cpp -// -//========================================================================== -//$Id: dijkstra.cpp,v 1.6 2002/12/23 13:46:41 chris Exp $ - -#include -#include - -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -/** - * @internal - * Binary predicate that compares two nodes according to their distance. - */ -class less_dist : public std::binary_function -{ -public: - /** - * @internal - * Constructor sets pointer to node distances and infimum info. - */ - less_dist(const node_map* dist, const node_map* mark) - { - this->dist = dist; - this->mark = mark; - } - - /** - * @internal - * Compares distances of @p n1 and @p n2. - */ - bool operator()(const node n1, const node n2) const - { - if (((*mark)[n1] == dijkstra::black) && - ((*mark)[n2] == dijkstra::black)) - { - return false; - } - else if ((*mark)[n1] == dijkstra::black) - { - return false; - } - else if ((*mark)[n2] == dijkstra::black) - { - return true; - } - return (*dist)[n1] < (*dist)[n2]; - } -private: - /** - * @internal - * Node distances from source. - */ - const node_map* dist; - - /** - * @internal - * Infimum distance info (color of nodes). - */ - const node_map* mark; -}; - - -dijkstra::dijkstra() -{ - reset_algorithm(); -} - - -dijkstra::~dijkstra() -{ -} - - -void dijkstra::source(const node& n) -{ - s = n; -} - - -void dijkstra::target(const node& n) -{ - t = n; -} - - -void dijkstra::weights(const edge_map& weight) -{ - this->weight = weight; - weights_set = true; -} - - -void dijkstra::store_preds(bool set) -{ - preds_set = set; -} - - -int dijkstra::check(graph& G) -{ - if ((s == node()) || (!weights_set)) - { - return GTL_ERROR; - } - - bool source_found = false; - graph::node_iterator node_it; - graph::node_iterator nodes_end = G.nodes_end(); - for (node_it = G.nodes_begin(); node_it != nodes_end; ++node_it) - { - if (*node_it == s) - { - source_found = true; - break; - } - } - if (!source_found) - { - return(GTL_ERROR); - } - - graph::edge_iterator edge_it; - graph::edge_iterator edges_end = G.edges_end(); - for(edge_it = G.edges_begin(); edge_it != edges_end; ++edge_it) - { - if (weight[*edge_it] < 0.0) - { - return false; - } - } - - return GTL_OK; -} - - -int dijkstra::run(graph& G) -{ - init(G); - - less_dist prd(&dist, &mark); - bin_heap node_heap(prd, G.number_of_nodes()); - mark[s] = grey; - dist[s] = 0.0; - node_heap.push(s); - while (!node_heap.is_empty()) - { - - // debug: - // node_heap.print_data_container(); - - node cur_node = node_heap.top(); - node_heap.pop(); - - // debug: - // node_heap.print_data_container(); - - mark[cur_node] = white; - if (cur_node == t) - { - // if @a t is set through #target we are ready - return GTL_OK; - } - - node::adj_edges_iterator adj_edge_it; - node::adj_edges_iterator adj_edges_end = cur_node.adj_edges_end(); - for (adj_edge_it = cur_node.adj_edges_begin(); - adj_edge_it != adj_edges_end; - ++adj_edge_it) - { - node op_node = adj_edge_it->opposite(cur_node); - if (mark[op_node] == black) - { - mark[op_node] = grey; - dist[op_node] = dist[cur_node] + weight[*adj_edge_it]; - node_heap.push(op_node); - - // debug: - // node_heap.print_data_container(); - - if (preds_set) - { - pred[op_node] = *adj_edge_it; - } - } - else if (mark[op_node] == grey) - { - if (dist[op_node] > dist[cur_node] + weight[*adj_edge_it]) - { - dist[op_node] = dist[cur_node] + weight[*adj_edge_it]; - node_heap.changeKey(op_node); - - // debug: - // node_heap.print_data_container(); - - if (preds_set) - { - pred[op_node] = *adj_edge_it; - } - } - } - else // (mark[op_node] == white) - { - // nothing to do: shortest distance to op_node is already - // computed - } - } - } - - return GTL_OK; -} - - -node dijkstra::source() const -{ - return s; -} - - -node dijkstra::target() const -{ - return t; -} - - -bool dijkstra::store_preds() const -{ - return preds_set; -} - - -bool dijkstra::reached(const node& n) const -{ - return mark[n] != black; -} - - -double dijkstra::distance(const node& n) const -{ - return dist[n]; -} - - -node dijkstra::predecessor_node(const node& n) const -{ - assert(preds_set); - if ((n == s) || (!reached(n))) - { - return node(); - } - return pred[n].opposite(n); -} - - -edge dijkstra::predecessor_edge(const node& n) const -{ - assert(preds_set); - return pred[n]; -} - - -dijkstra::shortest_path_node_iterator dijkstra::shortest_path_nodes_begin( - const node& dest) -{ - assert(preds_set); - if ((shortest_path_node_list[dest].empty()) && - (dest != s) && - (reached(dest))) - { - fill_node_list(dest); - } - return shortest_path_node_list[dest].begin(); -} - - -dijkstra::shortest_path_node_iterator dijkstra::shortest_path_nodes_end( - const node& dest) -{ - assert(preds_set); - if ((shortest_path_node_list[dest].empty()) && - (dest != s) && - (reached(dest))) - { - fill_node_list(dest); - } - return shortest_path_node_list[dest].end(); -} - - -dijkstra::shortest_path_edge_iterator dijkstra::shortest_path_edges_begin( - const node& dest) -{ - assert(preds_set); - if ((shortest_path_edge_list[dest].empty()) && - (dest != s) && - (reached(dest))) - { - fill_edge_list(dest); - } - return shortest_path_edge_list[dest].begin(); -} - - -dijkstra::shortest_path_edge_iterator dijkstra::shortest_path_edges_end( - const node& dest) -{ - assert(preds_set); - if ((shortest_path_edge_list[dest].empty()) && - (dest != s) && - (reached(dest))) - { - fill_edge_list(dest); - } - return shortest_path_edge_list[dest].end(); -} - - -void dijkstra::reset() -{ - reset_algorithm(); -} - - -void dijkstra::reset_algorithm() -{ - s = node(); - t = node(); - weights_set = false; - preds_set = false; -} - - -void dijkstra::init(graph& G) -{ - dist.init(G, -1.0); - mark.init(G, black); - - if (preds_set) - { - pred.init(G, edge()); - graph::node_iterator node_it; - graph::node_iterator nodes_end = G.nodes_end(); - for (node_it = G.nodes_begin(); node_it != nodes_end; ++node_it) - { - shortest_path_node_list[(*node_it)].clear(); - shortest_path_edge_list[(*node_it)].clear(); - } - } -} - - -void dijkstra::fill_node_list(const node& dest) -{ - if ((dest == s) || (!reached(dest))) - { - return; - } - - node cur_node = dest; - while (cur_node != node()) - { - shortest_path_node_list[dest].push_front(cur_node); - cur_node = predecessor_node(cur_node); - } -} - - -void dijkstra::fill_edge_list(const node& dest) -{ - if ((dest == s) || (!reached(dest))) - { - return; - } - - node cur_node = dest; - edge cur_edge = predecessor_edge(dest); - while (cur_edge != edge()) - { - shortest_path_edge_list[dest].push_front(cur_edge); - cur_node = predecessor_node(cur_node); - cur_edge = predecessor_edge(cur_node); - } -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/edge.cpp b/Tracker/graph/GTL/src/edge.cpp deleted file mode 100644 index cbdf66087..000000000 --- a/Tracker/graph/GTL/src/edge.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// edge.cpp -// -//========================================================================== -// $Id: edge.cpp,v 1.17 2001/11/07 13:58:09 pick Exp $ - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -//-------------------------------------------------------------------------- -// edge -//-------------------------------------------------------------------------- - -edge::edge() : - data(0) -{ -} - -GTL_EXTERN std::ostream& operator<< (std::ostream& os, const edge& e) { - if (e != edge ()) { - return os << e.source() << "-->" << e.target(); - } else { - return os << "UNDEF"; - } -} - -node edge::source() const -{ - return data->nodes[0].front(); -} - -node edge::target() const -{ - return data->nodes[1].front(); -} - -const node& edge::target_() const -{ - return data->nodes[1].front(); -} - -void edge::change_source (node new_source) -{ - // - // First delete this edge from source's adjacency list - // and clear the list of sources - // - - nodes_t::iterator the_nodes = data->nodes[0].begin(); - nodes_t::iterator the_nodes_end = data->nodes[0].end(); - - while(the_nodes != the_nodes_end) - { - the_nodes->data->edges[1].erase (data->adj_pos[0].front()); - data->adj_pos[0].pop_front(); - - the_nodes = data->nodes[0].erase (the_nodes); - } - - // - // Just to be sure :) - // - - assert (data->nodes[0].empty()); - assert (data->adj_pos[0].empty()); - - // - // insert this edge in the list of outgoing edges of new_source - // - - data->adj_pos[0].push_back(new_source.data->edges[1].insert ( - new_source.data->edges[1].end(), *this)); - - // - // make new_source a source of this node. - // - - data->nodes[0].push_back (new_source); -} - - -void edge::change_target (node new_target) { - // - // First delete this edge from target's adjacency list - // and clear the list of targets - // - - nodes_t::iterator the_nodes = data->nodes[1].begin(); - nodes_t::iterator the_nodes_end = data->nodes[1].end(); - - while(the_nodes != the_nodes_end) - { - the_nodes->data->edges[0].erase (data->adj_pos[1].front()); - data->adj_pos[1].pop_front(); - - the_nodes = data->nodes[1].erase (the_nodes); - } - - // - // Just to be sure :) - // - - assert (data->nodes[1].empty()); - assert (data->adj_pos[1].empty()); - - // - // insert this edge in the list of incoming edges of new_target - // - - data->adj_pos[1].push_back(new_target.data->edges[0].insert ( - new_target.data->edges[0].end(), *this)); - - // - // make new_target a target of this node. - // - - data->nodes[1].push_back (new_target); -} - - -void edge::reverse () -{ - // - // First delete this edge from all adjacency lists - // - - nodes_t::iterator the_nodes = data->nodes[0].begin(); - nodes_t::iterator the_nodes_end = data->nodes[0].end(); - - while(the_nodes != the_nodes_end) - { - the_nodes->data->edges[1].erase (data->adj_pos[0].front()); - data->adj_pos[0].pop_front(); - - ++the_nodes; - } - - the_nodes = data->nodes[1].begin(); - the_nodes_end = data->nodes[1].end(); - - while(the_nodes != the_nodes_end) - { - the_nodes->data->edges[0].erase (data->adj_pos[1].front()); - data->adj_pos[1].pop_front(); - - ++the_nodes; - } - - // - // Now the lists of positions in the adjacency - lists should be empty - // - - assert (data->adj_pos[0].empty()); - assert (data->adj_pos[1].empty()); - - // - // Now insert this edge reversed - // - - the_nodes = data->nodes[1].begin(); - the_nodes_end = data->nodes[1].end(); - - while(the_nodes != the_nodes_end) - { - data->adj_pos[0].push_back(the_nodes->data->edges[1].insert ( - the_nodes->data->edges[1].end(), *this)); - - ++the_nodes; - } - - the_nodes = data->nodes[0].begin(); - the_nodes_end = data->nodes[0].end(); - - while(the_nodes != the_nodes_end) - { - data->adj_pos[1].push_back(the_nodes->data->edges[0].insert ( - the_nodes->data->edges[0].end(), *this)); - - ++the_nodes; - } - - // - // swap nodes[0] and nodes[1] - // - - nodes_t tmp = data->nodes[0]; - data->nodes[0] = data->nodes[1]; - data->nodes[1] = tmp; -} - - - -nodes_t edge::sources() const -{ - return data->nodes[0]; -} - -nodes_t edge::targets() const -{ - return data->nodes[1]; -} - -int edge::id() const -{ - return data->id; -} - -bool edge::is_hidden () const -{ - return data->hidden; -} - -void edge::remove_from(int where) const -{ - nodes_t::iterator the_nodes = data->nodes[where].begin(); - nodes_t::iterator the_nodes_end = data->nodes[where].end(); - - std::list::iterator the_adj_pos = data->adj_pos[where].begin(); - - while (the_nodes != the_nodes_end) - { - the_nodes->data->edges[1 - where].erase(*the_adj_pos); - - ++the_nodes; - ++the_adj_pos; - } -} - -const node& edge::opposite(node n) const -{ - // not implemented for hypergraphs - assert(data); - - node& s = *(data->nodes[0].begin()); - if (n == s) - return *(data->nodes[1].begin()); - else - return s; -} - -GTL_EXTERN bool operator==(edge e1, edge e2) -{ - return e1.data == e2.data; -} - -GTL_EXTERN bool operator!=(edge e1, edge e2) -{ - return e1.data != e2.data; -} - -GTL_EXTERN bool operator<(edge e1, edge e2) -{ - return e1.data < e2.data; -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/embedding.cpp b/Tracker/graph/GTL/src/embedding.cpp deleted file mode 100644 index f79223069..000000000 --- a/Tracker/graph/GTL/src/embedding.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// embedding.cpp -// -//========================================================================== -// $Id: embedding.cpp,v 1.18 2002/10/04 08:07:36 chris Exp $ - -#include - -__GTL_BEGIN_NAMESPACE - -planar_embedding::planar_embedding (const planar_embedding& em) -{ - init (*(em.G)); - - node n; - forall_nodes (n, *G) { - adj_list::const_iterator it = em.adj[n].begin(); - adj_list::const_iterator end = em.adj[n].end(); - - for (; it != end; ++it) { - pos (n, *it) = push_back (n, *it); - } - } - - self.insert (self.begin(), em.self.begin(), em.self.end()); - multi.insert (multi.begin(), em.multi.begin(), em.multi.begin()); -} - - -planar_embedding& -planar_embedding::operator= (const planar_embedding& em) -{ - node n; - if (G != 0) { - forall_nodes (n, *G) { - adj[n].erase (adj[n].begin(), adj[n].end()); - } - } - - self.erase (self.begin(), self.end()); - multi.erase (multi.begin(), multi.end()); - - init (*(em.G)); - - forall_nodes (n, *G) { - adj_list::const_iterator it = em.adjacency(n).begin(); - adj_list::const_iterator end = em.adjacency(n).end(); - - for (; it != end; ++it) { - pos (n, *it) = push_back (n, *it); - } - } - - self.insert (self.begin(), em.self.begin(), em.self.end()); - multi.insert (multi.begin(), em.multi.begin(), em.multi.begin()); - - return *this; -} - - -void -planar_embedding::init (graph& my_G) -{ - adj.init (my_G); - - // - // There is a problem with node/edge maps of iterators with Visual C++ - // which I dont fully understand at the moment. Anyway the init for the - // maps below is only needed to allocate memory, which is done anyway, when - // values are assigned to it. - // - -#ifndef __GTL_MSVCC - s_pos.init (my_G); - t_pos.init (my_G); -#endif - G = &my_G; -} - - -symlist::iterator -planar_embedding::push_back (node n, edge e) -{ - return adj[n].insert (adj[n].end(), e); -} - - -symlist::iterator -planar_embedding::push_front (node n, edge e) -{ - return adj[n].insert (adj[n].begin(), e); -} - - -symlist::iterator& -planar_embedding::pos (node n, edge e) -{ - if (e.source() == n) { - return s_pos[e]; - } else if (e.target() == n) { - return t_pos[e]; - } else { - assert (false); - // this should not happen. - return s_pos[e]; - } -} - - -void -planar_embedding::insert_selfloop (edge e) -{ - node n = e.source(); - s_pos[e] = t_pos[e] = adj[n].insert (adj[n].begin(), e); -} - - -void -planar_embedding::turn (node n) -{ - adj[n].reverse(); -} - - -edge -planar_embedding::cyclic_next (node n, edge e) -{ - iterator it = pos (n, e); - ++it; - - if (it == adj[n].end()) { - ++it; - } - - return *it; -} - - -edge -planar_embedding::cyclic_prev (node n, edge e) -{ - iterator it = pos (n, e); - --it; - - if (it == adj[n].end()) { - --it; - } - - return *it; -} - -bool -planar_embedding::check () -{ - node n; - forall_nodes (n ,*G) { - iterator it, end; - - for (it = adj[n].begin(), end = adj[n].end(); it != end; ++it) { - edge curr = *it; - node other = n.opposite (curr); - - edge prev = cyclic_prev (n, curr); - edge next = cyclic_next (n, prev); - assert (next == curr); - - while (other != n) { - curr = cyclic_next (other, curr); - other = other.opposite (curr); - } - if (curr != prev) { - return false; - } - - } - } - - return true; -} - - -void -planar_embedding::write_st(std::ostream& os, st_number& st) -{ - st_number::iterator n_it = st.begin(); - st_number::iterator n_end = st.end(); - iterator it, end; - - for (; n_it != n_end; ++n_it) { - node n = *n_it; - os << "[" << st[n] << "]::"; - - it = adj[n].begin(); - end = adj[n].end(); - - for (; it != end; ++it) { - os << "[" << st[n.opposite (*it)] << "]"; - } - - os << std::endl; - } - - os << "SELFLOOPS:" << std::endl; - edges_t::iterator e_it, e_end; - for (e_it = self.begin(), e_end = self.end(); e_it != e_end; ++e_it) - { - os << st[e_it->source()] << "---" << st[e_it->target()] << std::endl; - } - - os << "MULTIPLE EDGES:" << std::endl; - for (e_it = multi.begin(), e_end = multi.end(); e_it != e_end; ++e_it) - { - os << st[e_it->source()] << "---" << st[e_it->target()] << std::endl; - } -} - -GTL_EXTERN std::ostream& operator<< (std::ostream& os, planar_embedding& em) -{ - graph::node_iterator n_it = em.G->nodes_begin(); - graph::node_iterator n_end = em.G->nodes_end(); - symlist::iterator it, end; - - for (; n_it != n_end; ++n_it) { - node n = *n_it; - os << n << ":: "; - - it = em.adj[n].begin(); - end = em.adj[n].end(); - - for (; it != end; ++it) { - os << n.opposite (*it) << "*"; - } - - os << std::endl; - } - - os << "SELFLOOPS:" << std::endl; - edges_t::iterator e_it, e_end; - for (e_it = em.self.begin(), e_end = em.self.end(); e_it != e_end; ++e_it) - { - os << *e_it << std::endl; - } - - os << "MULTIPLE EDGES:" << std::endl; - for (e_it = em.multi.begin(), e_end = em.multi.end(); e_it != e_end; ++e_it) - { - os << *e_it << std::endl; - } - - return os; -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/fm_partition.cpp b/Tracker/graph/GTL/src/fm_partition.cpp deleted file mode 100644 index afb56331c..000000000 --- a/Tracker/graph/GTL/src/fm_partition.cpp +++ /dev/null @@ -1,1147 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// fm_partition.cpp -// -//========================================================================== -// $Id: fm_partition.cpp,v 1.8 2001/11/07 13:58:10 pick Exp $ - -#include -#include - -#include -#include - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - - -const fm_partition::side_type fm_partition::A = 0; -const fm_partition::side_type fm_partition::B = 1; - - -const fm_partition::fix_type fm_partition::FIXA = 0; -const fm_partition::fix_type fm_partition::FIXB = 1; -const fm_partition::fix_type fm_partition::UNFIXED = 2; - - -fm_partition::fm_partition() -{ - set_vars_executed = false; - enable_cut_edges_storing = false; - enable_nodesAB_storing = false; -} - - -fm_partition::~fm_partition() -{ -} - - -void fm_partition::set_vars(const graph& G, - const node_map& node_weight, const edge_map& edge_weight) -{ - this->node_weight = node_weight; - this->edge_weight = edge_weight; - set_vars_executed = true; - provided_initial_part = false; - this->fixed.init(G, UNFIXED); - provided_fix = false; - side.init(G); -} - - -void fm_partition::set_vars(const graph& G, - const node_map& node_weight, const edge_map& edge_weight, - const node_map& init_side) -{ - this->node_weight = node_weight; - this->edge_weight = edge_weight; - this->side = init_side; - set_vars_executed = true; - provided_initial_part = true; - this->fixed.init(G, UNFIXED); - provided_fix = false; -} - - -void fm_partition::set_vars(const graph& G, - const node_map& node_weight, const edge_map& edge_weight, - const node_map& fixed) -{ - this->node_weight = node_weight; - this->edge_weight = edge_weight; - set_vars_executed = true; - provided_initial_part = false; - this->fixed = fixed; - provided_fix = true; - side.init(G); -} - - -void fm_partition::set_vars(const graph& /*G*/, - const node_map& node_weight, const edge_map& edge_weight, - const node_map& init_side, - const node_map& fixed) -{ - this->node_weight = node_weight; - this->edge_weight = edge_weight; - this->side = init_side; - set_vars_executed = true; - provided_initial_part = true; - this->fixed = fixed; - provided_fix = true; -} - - -void fm_partition::store_cut_edges(const bool set) -{ - enable_cut_edges_storing = set; -} - - -void fm_partition::store_nodesAB(const bool set) -{ - enable_nodesAB_storing = set; -} - - -int fm_partition::check(graph& G) -{ - if ((!set_vars_executed) || (!G.is_undirected())) - { - return GTL_ERROR; - } - - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - if (edge_weight[*edge_it] < 0) - { - return GTL_ERROR; - } - ++edge_it; - } - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (node_weight[*node_it] < 0) - { - return GTL_ERROR; - } - ++node_it; - } - - return GTL_OK; -} - - -int fm_partition::run(graph& G) -{ - init_variables(G); - if ((provided_initial_part) && (provided_fix)) - { - divide_up(G); - } - if (!provided_initial_part) - { - create_initial_bipart(G); - } - - hide_self_loops(G); - compute_max_vertex_degree(G); - - pass_manager(G); - - if (enable_cut_edges_storing) - { - compute_cut_edges(G); - } - if (enable_nodesAB_storing) - { - compute_nodesAB(G); - } - G.restore_graph(); - - return GTL_OK; -} - - -int fm_partition::get_cutsize() -{ - return cur_cutsize; -} - - -int fm_partition::get_needed_passes() -{ - return no_passes; -} - - -fm_partition::side_type fm_partition::get_side_of_node(const node& n) const -{ - return side[n]; -} - - -fm_partition::side_type fm_partition::operator [](const node& n) const -{ - return side[n]; -} - - -int fm_partition::get_weight_on_sideA(const graph& G) const -{ - int nwA = 0; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (side[*node_it] == A) - { - nwA += node_weight[*node_it]; - } - ++node_it; - } - return nwA; -} - - -int fm_partition::get_weight_on_sideB(const graph& G) const -{ - int nwB = 0; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (side[*node_it] == B) - { - nwB += node_weight[*node_it]; - } - ++node_it; - } - return nwB; -} - - -fm_partition::cut_edges_iterator fm_partition::cut_edges_begin() const -{ - return cut_edges.begin(); -} - - -fm_partition::cut_edges_iterator fm_partition::cut_edges_end() const -{ - return cut_edges.end(); -} - - -fm_partition::nodes_of_one_side_iterator -fm_partition::nodes_of_sideA_begin() const -{ - return nodesA.begin(); -} - - -fm_partition::nodes_of_one_side_iterator -fm_partition::nodes_of_sideA_end() const -{ - return nodesA.end(); -} - - -fm_partition::nodes_of_one_side_iterator -fm_partition::nodes_of_sideB_begin() const -{ - return nodesB.begin(); -} - - -fm_partition::nodes_of_one_side_iterator -fm_partition::nodes_of_sideB_end() const -{ - return nodesB.end(); -} - - -void fm_partition::reset() -{ - set_vars_executed = false; - cut_edges.clear(); - nodesA.clear(); - nodesB.clear(); -} - - -void fm_partition::divide_up(const graph& G) -{ - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (fixed[*node_it] == FIXA) - { - side[*node_it] = A; - } - else if (fixed[*node_it] == FIXB) - { - side[*node_it] = B; - } - ++node_it; - } -} - - -void fm_partition::hide_self_loops(graph& G) -{ - graph::edge_iterator temp_it; - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - if (edge_it->source() == edge_it->target()) - { - temp_it = edge_it; - ++edge_it; - G.hide_edge(*temp_it); - } - else - { - ++edge_it; - } - } -} - - -void fm_partition::init_variables(const graph& G) -{ - bool first_edge_found = true; - bool first_node_found = true; - max_edge_weight = 0; - max_node_weight = 0; - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - if (first_edge_found) - { - max_edge_weight = edge_weight[*edge_it]; - first_edge_found = false; - } - else if (edge_weight[*edge_it] > max_edge_weight) - { - max_edge_weight = edge_weight[*edge_it]; - } - ++edge_it; - } - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - total_node_weight = 0; - while (node_it != nodes_end) - { - total_node_weight += node_weight[*node_it]; - if (first_node_found) - { - max_node_weight = node_weight[*node_it]; - first_node_found = false; - } - else if (node_weight[*node_it] > max_node_weight) - { - max_node_weight = node_weight[*node_it]; - } - ++node_it; - } -} - - -void fm_partition::create_initial_bipart(const graph& G) -{ - int i = 0; // counter - int no_nodes = G.number_of_nodes(); - node_weight_on_sideA = 0; - node_weight_on_sideB = 0; - - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - std::vector node_vector(G.number_of_nodes()); - while (node_it != nodes_end) - { - node_vector[i] = node_it; - if (fixed[*node_it] == FIXA) - { - side[*node_it] = A; - node_weight_on_sideA += node_weight[*node_it]; - } - else if (fixed[*node_it] == FIXB) - { - side[*node_it] = B; - node_weight_on_sideB += node_weight[*node_it]; - } - else // fixed[*node_it] == UNFIXED - { - node_weight_on_sideB += node_weight[*node_it]; - side[*node_it] = B; - } - ++i; - ++node_it; - } - shuffle_vector(no_nodes, node_vector); - - // compute best balance - int best_bal = node_weight_on_sideA * node_weight_on_sideB; - int best_pos = -1; - for (i = 0; i < no_nodes; i++) - { - if (fixed[*node_vector[i]] == UNFIXED) - { - node_weight_on_sideA += node_weight[*node_vector[i]]; - node_weight_on_sideB -= node_weight[*node_vector[i]]; - if (node_weight_on_sideA * node_weight_on_sideB > best_bal) - { - best_bal = node_weight_on_sideA * node_weight_on_sideB; - best_pos = i; - } - } - } - - // create partition with best balance - for (i = 0; i <= best_pos; i++) - { - if (fixed[*node_vector[i]] == UNFIXED) - { - side[*node_vector[i]] = A; - } - } -} - - -void fm_partition::shuffle_vector(const int vector_size, - std::vector& node_vector) -{ - srand((unsigned)time(NULL)); - rand(); // necessary, otherwise the next rand() returns always 0 ?-) - for (int i = 1; i <= vector_size; i++) - { - int pos_1 = (int)floor((((double)rand() / (double)RAND_MAX) * - (double)(vector_size - 1)) + 0.5); - int pos_2 = (int)floor((((double)rand() / (double)RAND_MAX) * - (double)(vector_size - 1)) + 0.5); - graph::node_iterator temp_it; - temp_it = node_vector[pos_1]; - node_vector[pos_1] = node_vector[pos_2]; - node_vector[pos_2] = temp_it; - } -} - - -void fm_partition::compute_max_vertex_degree(const graph& G) -{ - max_vertex_degree = 0; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (max_vertex_degree < node_it->degree()) - { - max_vertex_degree = node_it->degree(); - } - ++node_it; - } -} - - -void fm_partition::pass_manager(const graph& G) -{ - // final pass which doesn't improve cur_cutsize is not counted - no_passes = -1; - int best_cutsize = -1; // = -1 to avoid warning - node_map best_side(G); - bool improved_cutsize; - - do - { - init_data_structure(G); - if (no_passes == -1) - { - best_cutsize = cur_cutsize; - copy_side_node_map(G, best_side, side); - } - move_manager(G); - clean_pass(G); - improved_cutsize = false; - if (best_cutsize > cur_cutsize) - { - best_cutsize = cur_cutsize; - copy_side_node_map(G, best_side, side); - improved_cutsize = true; - } - ++no_passes; - } - while (improved_cutsize); - cur_cutsize = best_cutsize; - copy_side_node_map(G, side, best_side); -} - - -void fm_partition::copy_side_node_map(const graph& G, - node_map& dest, const node_map source) const -{ - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - dest[*node_it] = source[*node_it]; - ++node_it; - } -} - - -void fm_partition::init_data_structure(const graph& G) -{ - aside.init(G); - bside.init(G); - unlockedA.init(G); - unlockedB.init(G); - cur_cutsize = 0; - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - if ((side[edge_it->source()] == A) && - (side[edge_it->target()] == A)) - { - aside[*edge_it] = 2; - bside[*edge_it] = 0; - unlockedA[*edge_it].push_back(edge_it->source()); - unlockedA[*edge_it].push_back(edge_it->target()); - } - else if ((side[edge_it->source()] == B) && - (side[edge_it->target()] == B)) - { - aside[*edge_it] = 0; - bside[*edge_it] = 2; - unlockedB[*edge_it].push_back(edge_it->source()); - unlockedB[*edge_it].push_back(edge_it->target()); - } - else if ((side[edge_it->source()] == A) && - (side[edge_it->target()] == B)) - { - aside[*edge_it] = 1; - bside[*edge_it] = 1; - cur_cutsize += edge_weight[*edge_it]; - unlockedA[*edge_it].push_back(edge_it->source()); - unlockedB[*edge_it].push_back(edge_it->target()); - } - else if ((side[edge_it->source()] == B) && - (side[edge_it->target()] == A)) - { - aside[*edge_it] = 1; - bside[*edge_it] = 1; - cur_cutsize += edge_weight[*edge_it]; - unlockedA[*edge_it].push_back(edge_it->target()); - unlockedB[*edge_it].push_back(edge_it->source()); - } - ++edge_it; - } - - bucketA.resize(2 * max_vertex_degree * max_edge_weight + 1); - bucketB.resize(2 * max_vertex_degree * max_edge_weight + 1); - - init_filling_buckets(G); -} - - -void fm_partition::init_filling_buckets(const graph &G) -{ - node_weight_on_sideA = 0; - node_weight_on_sideB = 0; - bucketA_empty = true; - bucketB_empty = true; - bool first_A_node = true; - bool first_B_node = true; - int index; - // position_in_bucket.init(G); - gain_value.init(G); - - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (side[*node_it] == A) - { - node_weight_on_sideA += node_weight[*node_it]; - gain_value[*node_it] = inital_gain_of_node_on_sideA(*node_it); - if (fixed[*node_it] == UNFIXED) - { - if (first_A_node) - { - bucketA_empty = false; - max_gainA = gain_value[*node_it]; - first_A_node = false; - } - else - { - if (max_gainA < gain_value[*node_it]) - { - max_gainA = gain_value[*node_it]; - } - } - index = range_up(gain_value[*node_it]); - position_in_bucket[*node_it] = bucketA[index].insert( - bucketA[index].end(), *node_it); - } - } - else // side[*node_it] == B - { - node_weight_on_sideB += node_weight[*node_it]; - gain_value[*node_it] = inital_gain_of_node_on_sideB(*node_it); - if (fixed[*node_it] == UNFIXED) - { - if (first_B_node) - { - bucketB_empty = false; - max_gainB = gain_value[*node_it]; - first_B_node = false; - } - else - { - if (max_gainB < gain_value[*node_it]) - { - max_gainB = gain_value[*node_it]; - } - } - index = range_up(gain_value[*node_it]); - position_in_bucket[*node_it] = bucketB[index].insert( - bucketB[index].end(), *node_it); - } - } - ++node_it; - } -} - - -int fm_partition::inital_gain_of_node_on_sideA(const node cur_node) -{ - int node_gain = 0; - node::adj_edges_iterator adj_edge_it = cur_node.adj_edges_begin(); - node::adj_edges_iterator adj_edges_end = cur_node.adj_edges_end(); - while (adj_edge_it != adj_edges_end) - { - if (aside[*adj_edge_it] == 1) - { - node_gain += edge_weight[*adj_edge_it]; - } - if (bside[*adj_edge_it] == 0) - { - node_gain -= edge_weight[*adj_edge_it]; - } - ++adj_edge_it; - } - return node_gain; -} - - -int fm_partition::inital_gain_of_node_on_sideB(const node cur_node) -{ - int node_gain = 0; - node::adj_edges_iterator adj_edge_it = cur_node.adj_edges_begin(); - node::adj_edges_iterator adj_edges_end = cur_node.adj_edges_end(); - while (adj_edge_it != adj_edges_end) - { - if (bside[*adj_edge_it] == 1) - { - node_gain += edge_weight[*adj_edge_it]; - } - if (aside[*adj_edge_it] == 0) - { - node_gain -= edge_weight[*adj_edge_it]; - } - ++adj_edge_it; - } - return node_gain; -} - - -void fm_partition::move_manager(const graph& G) -{ - int step_number = 0; - int best_tentative_move = 0; - int best_bal = node_weight_on_sideA * node_weight_on_sideB; - std::vector tentative_moves(G.number_of_nodes() + 1); - std::vector tentative_cutsize(G.number_of_nodes() + 1); - node moved_node; - tentative_cutsize[0] = cur_cutsize; - - while (move_vertex(G, moved_node)) - { - ++step_number; - tentative_cutsize[step_number] = cur_cutsize; - tentative_moves[step_number] = moved_node; - if (tentative_cutsize[best_tentative_move] > cur_cutsize) - { - best_tentative_move = step_number; - best_bal = node_weight_on_sideA * node_weight_on_sideB; - } - else if (tentative_cutsize[best_tentative_move] == cur_cutsize) - { - if (node_weight_on_sideA * node_weight_on_sideB > best_bal) - { - best_tentative_move = step_number; - best_bal = node_weight_on_sideA * node_weight_on_sideB; - } - } - } - - for (int i = 1; i <= best_tentative_move; i++) - { - if (side[tentative_moves[i]] == A) - { - side[tentative_moves[i]] = B; - } - else // side[tentative_moves[i]] == B - { - side[tentative_moves[i]] = A; - } - } - cur_cutsize = tentative_cutsize[best_tentative_move]; -} - - -bool fm_partition::move_vertex(const graph& G, node& moved_node) -{ - node cons_nodeA; - if (!bucketA_empty) - { - cons_nodeA = bucketA[range_up(max_gainA)].back(); - } - node cons_nodeB; - if (!bucketB_empty) - { - cons_nodeB = bucketB[range_up(max_gainB)].back(); - } - if ((!bucketA_empty) && (!bucketB_empty) && - (balance_holds(G, cons_nodeA)) && (balance_holds(G, cons_nodeB))) - { - if (gain_value[cons_nodeA] > gain_value[cons_nodeB]) - { - update_data_structure_A2B(cons_nodeA); - moved_node = cons_nodeA; - } - else if (gain_value[cons_nodeB] > gain_value[cons_nodeA]) - { - update_data_structure_B2A(cons_nodeB); - moved_node = cons_nodeB; - } - else // gain_value[cons_nodeB] == gain_value[cons_nodeA] - { - int bal_diff_A2B = abs(node_weight_on_sideA - 2 * - node_weight[cons_nodeA] - node_weight_on_sideB); - int bal_diff_B2A = abs(node_weight_on_sideB - 2 * - node_weight[cons_nodeB] - node_weight_on_sideA); - if (bal_diff_A2B < bal_diff_B2A) - { - update_data_structure_A2B(cons_nodeA); - moved_node = cons_nodeA; - } - else if (bal_diff_B2A < bal_diff_A2B) - { - update_data_structure_B2A(cons_nodeB); - moved_node = cons_nodeB; - } - else // break remaining ties as desired [FidMat82] - { - update_data_structure_A2B(cons_nodeA); - moved_node = cons_nodeA; - } - } - } - else if ((!bucketA_empty) && (balance_holds(G, cons_nodeA))) - { - update_data_structure_A2B(cons_nodeA); - moved_node = cons_nodeA; - } - else if ((!bucketB_empty) && (balance_holds(G, cons_nodeB))) - { - update_data_structure_B2A(cons_nodeB); - moved_node = cons_nodeB; - } - else - { - return false; // no more vertices can be moved - } - update_max_gain(A); - update_max_gain(B); - return true; -} - - -bool fm_partition::balance_holds(const graph& /*G*/, const node cur_node) -{ - if (side[cur_node] == A) - { - if ((double)node_weight_on_sideB + (double)node_weight[cur_node] - <= ((double)total_node_weight / 2.0) + (double)max_node_weight) - { - return true; - } - } - else // side[cur_node] == B - { - if ((double)node_weight_on_sideA + (double)node_weight[cur_node] - <= ((double)total_node_weight / 2.0) + (double)max_node_weight) - { - return true; - } - } - return false; -} - - -void fm_partition::update_data_structure_A2B(const node cur_node) -{ - bucketA[range_up(max_gainA)].pop_back(); - node_weight_on_sideA -= node_weight[cur_node]; - node_weight_on_sideB += node_weight[cur_node]; - cur_cutsize -= gain_value[cur_node]; - - // updating gain values - node::adj_edges_iterator adj_edge_it = cur_node.adj_edges_begin(); - node::adj_edges_iterator adj_edges_end = cur_node.adj_edges_end(); - while (adj_edge_it != adj_edges_end) - { - // delete cur_node from side A -#if 1 - unlockedA[*adj_edge_it].remove(cur_node); -#else - auto& ua = unlockedA[*adj_edge_it]; - ua.erase(std::remove(ua.begin(), ua.end(), cur_node), ua.end()); -#endif - --aside[*adj_edge_it]; - if (aside[*adj_edge_it] == 0) - { - nodes_t::iterator node_it = unlockedB[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedB[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketB(*node_it, gain_value[*node_it], - gain_value[*node_it] - edge_weight[*adj_edge_it]); - gain_value[*node_it] -= edge_weight[*adj_edge_it]; - ++node_it; - } - } - else if (aside[*adj_edge_it] == 1) - { - nodes_t::iterator node_it = unlockedA[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedA[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketA(*node_it, gain_value[*node_it], - gain_value[*node_it] + edge_weight[*adj_edge_it]); - gain_value[*node_it] += edge_weight[*adj_edge_it]; - ++node_it; - } - } - // add cur_node to side B - ++bside[*adj_edge_it]; - if (bside[*adj_edge_it] == 1) - { - nodes_t::iterator node_it = unlockedA[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedA[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketA(*node_it, gain_value[*node_it], - gain_value[*node_it] + edge_weight[*adj_edge_it]); - gain_value[*node_it] += edge_weight[*adj_edge_it]; - ++node_it; - } - } - else if (bside[*adj_edge_it] == 2) - { - nodes_t::iterator node_it = unlockedB[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedB[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketB(*node_it, gain_value[*node_it], - gain_value[*node_it] - edge_weight[*adj_edge_it]); - gain_value[*node_it] -= edge_weight[*adj_edge_it]; - ++node_it; - } - } - ++adj_edge_it; - } -} - - -void fm_partition::update_data_structure_B2A(const node cur_node) -{ - bucketB[range_up(max_gainB)].pop_back(); - node_weight_on_sideA += node_weight[cur_node]; - node_weight_on_sideB -= node_weight[cur_node]; - cur_cutsize -= gain_value[cur_node]; - - // updating gain values - node::adj_edges_iterator adj_edge_it = cur_node.adj_edges_begin(); - node::adj_edges_iterator adj_edges_end = cur_node.adj_edges_end(); - while (adj_edge_it != adj_edges_end) - { - // delete cur_node from side B -#if 1 - unlockedB[*adj_edge_it].remove(cur_node); -#else - auto& ub = unlockedB[*adj_edge_it]; - ub.erase(std::remove(ub.begin(), ub.end(), cur_node), ub.end()); -#endif - - bside[*adj_edge_it] -= 1; - if (bside[*adj_edge_it] == 0) - { - nodes_t::iterator node_it = unlockedA[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedA[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketA(*node_it, gain_value[*node_it], - gain_value[*node_it] - edge_weight[*adj_edge_it]); - gain_value[*node_it] -= edge_weight[*adj_edge_it]; - ++node_it; - } - } - else if (bside[*adj_edge_it] == 1) - { - nodes_t::iterator node_it = unlockedB[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedB[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketB(*node_it, gain_value[*node_it], - gain_value[*node_it] + edge_weight[*adj_edge_it]); - gain_value[*node_it] += edge_weight[*adj_edge_it]; - ++node_it; - } - } - // add cur_node to side A - aside[*adj_edge_it] += 1; - if (aside[*adj_edge_it] == 1) - { - nodes_t::iterator node_it = unlockedB[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedB[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketB(*node_it, gain_value[*node_it], - gain_value[*node_it] + edge_weight[*adj_edge_it]); - gain_value[*node_it] += edge_weight[*adj_edge_it]; - ++node_it; - } - } - else if (aside[*adj_edge_it] == 2) - { - nodes_t::iterator node_it = unlockedA[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedA[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketA(*node_it, gain_value[*node_it], - gain_value[*node_it] - edge_weight[*adj_edge_it]); - gain_value[*node_it] -= edge_weight[*adj_edge_it]; - ++node_it; - } - } - ++adj_edge_it; - } -} - - -void fm_partition::update_bucketA(const node cur_node, const int old_gain, - const int new_gain) -{ - if (fixed[cur_node] != UNFIXED) - { - return; // fixed nodes need no update - } - - bucketA[range_up(old_gain)].erase(position_in_bucket[cur_node]); - position_in_bucket[cur_node] = bucketA[range_up(new_gain)].insert( - bucketA[range_up(new_gain)].end(), cur_node); - - if (max_gainA < new_gain) - { - max_gainA = new_gain; - } -} - - -void fm_partition::update_bucketB(const node cur_node, const int old_gain, - const int new_gain) -{ - if (fixed[cur_node] != UNFIXED) - { - return; // fixed nodes need no update - } - - bucketB[range_up(old_gain)].erase(position_in_bucket[cur_node]); - position_in_bucket[cur_node] = bucketB[range_up(new_gain)].insert( - bucketB[range_up(new_gain)].end(), cur_node); - - if (max_gainB < new_gain) - { - max_gainB = new_gain; - } -} - - -void fm_partition::update_max_gain(const side_type side) -{ - if ((side == A) && (!bucketA_empty)) - { - while (bucketA[range_up(max_gainA)].empty()) - { - --max_gainA; - if (range_up(max_gainA) < 0) - { - bucketA_empty = true; - return; - } - } - bucketA_empty = false; - } - if ((side == B) && (!bucketB_empty)) - { - while (bucketB[range_up(max_gainB)].empty()) - { - --max_gainB; - if (range_up(max_gainB) < 0) - { - bucketB_empty = true; - return; - } - } - bucketB_empty = false; - } -} - - -inline int fm_partition::range_up(const int gain_value) const -{ - return gain_value + (max_vertex_degree * max_edge_weight); -} - - -inline int fm_partition::range_down(const int index) const -{ - return index - (max_vertex_degree * max_edge_weight); -} - - -void fm_partition::clean_pass(const graph& G) -{ - // clean unlocked* lists - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - unlockedA[*edge_it].clear(); - unlockedB[*edge_it].clear(); - ++edge_it; - } - - // clean buckets - for (int i = 0; i <= 2 * max_vertex_degree * max_edge_weight; i++) - { - bucketA[i].clear(); - bucketB[i].clear(); - } - bucketA.clear(); - bucketB.clear(); -} - - -void fm_partition::compute_cut_edges(const graph& G) -{ - cut_edges.clear(); - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - if (side[edge_it->source()] != side[edge_it->target()]) - { - cut_edges.push_back(*edge_it); - } - ++edge_it; - } -} - - -void fm_partition::compute_nodesAB(const graph& G) -{ - nodesA.clear(); - nodesB.clear(); - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (side[*node_it] == A) - { - nodesA.push_back(*node_it); - } - else // side[*node_it] == B - { - nodesB.push_back(*node_it); - } - ++node_it; - } -} - - -#ifdef _DEBUG -void fm_partition::print_bucketA() -{ - GTL_debug::init_debug(); - GTL_debug::os() << std::endl << "bucketA:" << std::endl; - for (int i = 0; i <= 2 * max_vertex_degree * max_edge_weight; i++) - { - GTL_debug::os() << range_down(i) << ": "; - nodes_t::iterator node_it = bucketA[i].begin(); - nodes_t::iterator nodes_end = bucketA[i].end(); - while (node_it != nodes_end) - { - GTL_debug::os() << *node_it << " "; - ++node_it; - } - GTL_debug::os() << std::endl; - } - GTL_debug::os() << std::endl; - GTL_debug::close_debug(); -} - - -void fm_partition::print_bucketB() -{ - GTL_debug::init_debug(); - GTL_debug::os() << std::endl << "bucketB:" << std::endl; - for (int i = 0; i <= 2 * max_vertex_degree * max_edge_weight; i++) - { - GTL_debug::os() << range_down(i) << ": "; - nodes_t::iterator node_it = bucketB[i].begin(); - nodes_t::iterator nodes_end = bucketB[i].end(); - while (node_it != nodes_end) - { - GTL_debug::os() << *node_it << " "; - ++node_it; - } - GTL_debug::os() << std::endl; - } - GTL_debug::os() << std::endl; - GTL_debug::close_debug(); -} -#endif // _DEBUG - - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/gml_parser.cpp b/Tracker/graph/GTL/src/gml_parser.cpp deleted file mode 100644 index f6f6bf23e..000000000 --- a/Tracker/graph/GTL/src/gml_parser.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// gml_parser.cpp - parser for the GML-file-format specified in: -// Michael Himsolt, GML: Graph Modelling Language, -// 21.01.1997 -// -//========================================================================== -// $Id: gml_parser.cpp,v 1.9 2001/11/07 13:58:10 pick Exp $ - -#include - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -struct GML_pair* GML_parser (FILE* source, struct GML_stat* stat, int open) { - - struct GML_token token; - struct GML_pair* pair; - struct GML_pair* list; - struct GML_pair* tmp = NULL; - struct GML_list_elem* tmp_elem; - - assert (stat); - - pair = (struct GML_pair*) malloc (sizeof (struct GML_pair)); - pair->next = NULL; - list = pair; - - for (;;) { - token = GML_scanner (source); - - if (token.kind == GML_END) { - if (open) { - stat->err.err_num = GML_OPEN_BRACKET; - stat->err.line = GML_line; - stat->err.column = GML_column; - free (pair); - - if (tmp == NULL) { - return NULL; - } else { - tmp->next = NULL; - return list; - } - } - - break; - - } else if (token.kind == GML_R_BRACKET) { - if (!open) { - stat->err.err_num = GML_TOO_MANY_BRACKETS; - stat->err.line = GML_line; - stat->err.column = GML_column; - free (pair); - - if (tmp == NULL) { - return NULL; - } else { - tmp->next = NULL; - return list; - } - } - - break; - - } else if (token.kind == GML_ERROR) { - stat->err.err_num = token.value.err.err_num; - stat->err.line = token.value.err.line; - stat->err.column = token.value.err.column; - free (pair); - - if (tmp == NULL) { - return NULL; - } else { - tmp->next = NULL; - return list; - } - - } else if (token.kind != GML_KEY) { - stat->err.err_num = GML_SYNTAX; - stat->err.line = GML_line; - stat->err.column = GML_column; - free (pair); - - if (token.kind == GML_STRING) { - free (token.value.str); - } - - if (tmp == NULL) { - return NULL; - } else { - tmp->next = NULL; - return list; - } - } - - if (!stat->key_list) { - stat->key_list = (struct GML_list_elem*) - malloc (sizeof (struct GML_list_elem)); - stat->key_list->next = NULL; - stat->key_list->key = token.value.str; - pair->key = token.value.str; - - } else { - tmp_elem = stat->key_list; - - while (tmp_elem) { - if (!strcmp (tmp_elem->key, token.value.str)) { - free (token.value.str); - pair->key = tmp_elem->key; - break; - } - - tmp_elem = tmp_elem->next; - } - - if (!tmp_elem) { - tmp_elem = (struct GML_list_elem*) - malloc (sizeof (struct GML_list_elem)); - tmp_elem->next = stat->key_list; - stat->key_list = tmp_elem; - tmp_elem->key = token.value.str; - pair->key = token.value.str; - } - } - - token = GML_scanner (source); - - switch (token.kind) { - case GML_INT: - pair->kind = GML_INT; - pair->value.integer = token.value.integer; - break; - - case GML_DOUBLE: - pair->kind = GML_DOUBLE; - pair->value.floating = token.value.floating; - break; - - case GML_STRING: - pair->kind = GML_STRING; - pair->value.str = token.value.str; - break; - - case GML_L_BRACKET: - pair->kind = GML_LIST; - pair->value.list = GML_parser (source, stat, 1); - - if (stat->err.err_num != GML_OK) { - return list; - } - - break; - - case GML_ERROR: - stat->err.err_num = token.value.err.err_num; - stat->err.line = token.value.err.line; - stat->err.column = token.value.err.column; - free (pair); - - if (tmp == NULL) { - return NULL; - } else { - tmp->next = NULL; - return list; - } - - default: - stat->err.line = GML_line; - stat->err.column = GML_column; - stat->err.err_num = GML_SYNTAX; - free (pair); - - if (tmp == NULL) { - return NULL; - } else { - tmp->next = NULL; - return list; - } - } - - tmp = pair; - pair = (struct GML_pair*) malloc (sizeof (struct GML_pair)); - tmp->next = pair; - pair->next = NULL; - } - - stat->err.err_num = GML_OK; - free (pair); - - if (tmp == NULL) { - return NULL; - } else { - tmp->next = NULL; - return list; - } -} - - -void GML_free_list (struct GML_pair* list, struct GML_list_elem* keys) { - - struct GML_pair* tmp = list; - struct GML_list_elem* tmp_key; - - while (keys) { - free (keys->key); - tmp_key = keys->next; - free (keys); - keys = tmp_key; - } - - while (list) { - - switch (list->kind) { - case GML_LIST: - GML_free_list (list->value.list, NULL); - break; - - case GML_STRING: - free (list->value.str); - break; - - default: - break; - } - - tmp = list->next; - free (list); - list = tmp; - } -} - - - -void GML_print_list (struct GML_pair* list, int level) { - - struct GML_pair* tmp = list; - int i; - - while (tmp) { - - for (i = 0; i < level; i++) { - printf (" "); - } - - printf ("*KEY* : %s", tmp->key); - - switch (tmp->kind) { - case GML_INT: - printf (" *VALUE* (long) : %ld \n", tmp->value.integer); - break; - - case GML_DOUBLE: - printf (" *VALUE* (double) : %f \n", tmp->value.floating); - break; - - case GML_STRING: - printf (" *VALUE* (string) : %s \n", tmp->value.str); - break; - - case GML_LIST: - printf (" *VALUE* (list) : \n"); - GML_print_list (tmp->value.list, level+1); - break; - - default: - break; - } - - tmp = tmp->next; - } -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/gml_scanner.cpp b/Tracker/graph/GTL/src/gml_scanner.cpp deleted file mode 100644 index 33a858a1e..000000000 --- a/Tracker/graph/GTL/src/gml_scanner.cpp +++ /dev/null @@ -1,426 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// gml_scanner.cpp - Scanner for the GML - file format -// -//========================================================================== -// $Id: gml_scanner.cpp,v 1.10 2001/11/07 13:58:10 pick Exp $ - -#include - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -/* - * ISO8859-1 coding of chars >= 160 - */ - -const char* GML_table[] = { - " ", /* 160 */ - "¡", - "¢", - "£", - "¤", - "¥", - "¦", - "§", - "¨", - "©", - "ª", /* 170 */ - "«", - "¬", - "­", - "®", - "¯", - "°", - "±", - "²", - "³", /* 180 */ - "´", - "µ", - "¶", - "·", - "¸", - "¹", - "º", - "»", - "¼", - "½", - "¾", /* 190 */ - "¿", - "À", - "Á", - "Â", - "Ã", - "Ä", - "Å", - "Æ", - "Ç", - "È", /* 200 */ - "É", - "Ê", - "Ë", - "Ì", - "Í", - "Î", - "Ï", - "Ð", - "Ñ", - "Ò", /* 210 */ - "Ó", - "Ô", - "Õ", - "Ö", - "×", - "Ø", - "Ù", - "Ú", - "Û", - "Ü", /* 220 */ - "Ý", - "Þ", - "ß", - "à", - "á", - "â", - "ã", - "ä", - "å", - "æ", /* 230 */ - "ç", - "è", - "é", - "ê", - "ë", - "ì", - "í", - "î", - "ï", - "ð", /* 240 */ - "ñ", - "ò", - "ó", - "ô", - "õ", - "ö", - "÷", - "ø", - "ù", - "ú", /* 250 */ - "û", - "ü", - "ý", - "þ", - "ÿ" -}; - - -unsigned int GML_line; -unsigned int GML_column; - - -int GML_search_ISO (char* str, int len) { - - int i; - int ret = '&'; - - // - // First check the extraordinary ones - // - - if (!strncmp (str, """, len)) { - return 34; - } else if (!strncmp (str, "&", len)) { - return 38; - } else if (!strncmp (str, "<", len)) { - return 60; - } else if (!strncmp (str, ">", len)) { - return 62; - } - - for (i = 0; i < 96; i++) { - if (!strncmp (str, GML_table[i], len)) { - ret = i + 160; - break; - } - } - - return ret; -} - - -void GML_init () { - - GML_line = 1; - GML_column = 1; -} - - - -struct GML_token GML_scanner (FILE* source) { - - int cur_max_size = INITIAL_SIZE; - static char buffer[INITIAL_SIZE]; - char* tmp = buffer; - char* ret = tmp; - struct GML_token token; - int is_float = 0; - int count = 0; - int next; - char ISO_buffer[8]; - int ISO_count; - - assert (source != NULL); - - /* - * eliminate preceeding white spaces - */ - - do { - next = fgetc (source); - GML_column++; - - if (next == '\n') { - GML_line++; - GML_column = 1; - } else if (next == EOF) { - token.kind = GML_END; - return token; - } - } while (isspace (next)); - - if (isdigit (next) || next == '.' || next == '+' || next == '-') { - - /* - * floating point or integer - */ - - do { - if (count == INITIAL_SIZE - 1) { - token.value.err.err_num = GML_TOO_MANY_DIGITS; - token.value.err.line = GML_line; - token.value.err.column = GML_column + count; - token.kind = GML_ERROR; - return token; - } - - if (next == '.' || next == 'E') { - is_float = 1; - } - - buffer[count] = next; - count++; - next = fgetc (source); - - } while (!isspace(next) && next != ']'); - - if (next == ']') { - ungetc (next, source); - } - - buffer[count] = 0; - - if (next == '\n') { - GML_line++; - GML_column = 1; - } else { - GML_column += count; - } - - if (is_float) { - token.value.floating = atof (tmp); - token.kind = GML_DOUBLE; - } else { - token.value.integer = atol (tmp); - token.kind = GML_INT; - } - - return token; - - } else if (isalpha (next) || next == '_') { - - /* - * key - */ - - do { - if (count == cur_max_size - 1) { - *tmp = 0; - tmp = (char*) malloc(2 * cur_max_size * sizeof (char)); - strcpy (tmp, ret); - - if (cur_max_size > INITIAL_SIZE) { - free (ret); - } - - ret = tmp; - tmp += count; - cur_max_size *= 2; - } - - if (!isalnum (next) && next != '_') { - token.value.err.err_num = GML_UNEXPECTED; - token.value.err.line = GML_line; - token.value.err.column = GML_column + count; - token.kind = GML_ERROR; - - if (cur_max_size > INITIAL_SIZE) { - free (ret); - } - - return token; - } - - *tmp++ = next; - count++; - next = fgetc (source); - } while (!isspace (next) && next != EOF); - - if (next == '\n') { - GML_line++; - GML_column = 1; - } else { - GML_column += count; - } - - *tmp = 0; - token.kind = GML_KEY; - token.value.str = (char*) malloc((count+1) * sizeof (char)); - strcpy (token.value.str, ret); - - if (cur_max_size > INITIAL_SIZE) { - free (ret); - } - - return token; - - } else { - /* - * comments, brackets and strings - */ - - switch (next) { - case '#': - do { - next = fgetc (source); - } while (next != '\n' && next != EOF); - - GML_line++; - GML_column = 1; - return GML_scanner (source); - - case '[': - token.kind = GML_L_BRACKET; - return token; - - case ']': - token.kind = GML_R_BRACKET; - return token; - - case '"': - next = fgetc (source); - GML_column++; - - while (next != '"') { - - if (count >= cur_max_size - 8) { - *tmp = 0; - tmp = (char*) malloc (2 * cur_max_size * sizeof(char)); - strcpy (tmp, ret); - - if (cur_max_size > INITIAL_SIZE) { - free (ret); - } - - ret = tmp; - tmp += count; - cur_max_size *= 2; - } - - if (next == '&') { - ISO_count = 0; - - while (next != ';') { - if (next == '"' || next == EOF) { - ungetc (next, source); - ISO_count = 0; - break; - } - - if (ISO_count < 8) { - ISO_buffer[ISO_count] = next; - ISO_count++; - } - - next = fgetc (source); - } - - if (ISO_count == 8) { - ISO_count = 0; - } - - if (ISO_count) { - ISO_buffer[ISO_count] = ';'; - ISO_count++; - next = GML_search_ISO (ISO_buffer, ISO_count); - ISO_count = 0; - } else { - next = '&'; - } - } - - *tmp++ = next; - count++; - GML_column++; - - next = fgetc (source); - - if (next == EOF) { - token.value.err.err_num = GML_PREMATURE_EOF; - token.value.err.line = GML_line; - token.value.err.column = GML_column + count; - token.kind = GML_ERROR; - - if (cur_max_size > INITIAL_SIZE) { - free (ret); - } - - return token; - } - - if (next == '\n') { - GML_line++; - GML_column = 1; - } - } - - *tmp = 0; - token.kind = GML_STRING; - token.value.str = (char*) malloc((count+1) * sizeof (char)); - strcpy (token.value.str, ret); - - if (cur_max_size > INITIAL_SIZE) { - free (ret); - } - - return token; - - default: - token.value.err.err_num = GML_UNEXPECTED; - token.value.err.line = GML_line; - token.value.err.column = GML_column; - token.kind = GML_ERROR; - return token; - } - } -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/graph.cpp b/Tracker/graph/GTL/src/graph.cpp deleted file mode 100644 index fe5223fcd..000000000 --- a/Tracker/graph/GTL/src/graph.cpp +++ /dev/null @@ -1,1123 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// graph.cpp -// -//========================================================================== -// $Id: graph.cpp,v 1.58 2003/01/14 16:47:14 raitner Exp $ - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -//-------------------------------------------------------------------------- -// Con-/Destructors -//-------------------------------------------------------------------------- - -graph::graph() : - directed(true), - nodes_count(0), edges_count(0), - hidden_nodes_count(0), hidden_edges_count(0), - free_node_ids_count(0), free_edge_ids_count(0) -{ -} - -graph::graph(const graph &G) : - directed(G.directed), - nodes_count(0), edges_count(0), - hidden_nodes_count(0), hidden_edges_count(0), - free_node_ids_count(0), free_edge_ids_count(0) -{ - copy (G, G.nodes.begin(), G.nodes.end()); -} - - -graph::graph(const graph& G, const nodes_t& nod) : - directed(G.directed), - nodes_count(0), edges_count(0), - hidden_nodes_count(0), hidden_edges_count(0), - free_node_ids_count(0), free_edge_ids_count(0) -{ - copy (G, nod.begin(), nod.end()); -} - - -graph::graph (const graph& G, - nodes_t::const_iterator it, - nodes_t::const_iterator end) : - directed(G.directed), - nodes_count(0), edges_count(0), - hidden_nodes_count(0), hidden_edges_count(0), - free_node_ids_count(0), free_edge_ids_count(0) -{ - copy (G, it, end); -} - -void graph::copy (const graph& G, - nodes_t::const_iterator it, - nodes_t::const_iterator end) -{ - node_map copy (G, node()); - nodes_t::const_iterator n_it; - nodes_t::const_iterator n_end; - - for(n_it = it, n_end = end; n_it != n_end; ++n_it) - { - copy[*n_it] = new_node(); - } - - for(n_it = it, n_end = end; n_it != n_end; ++n_it) - { - node::out_edges_iterator e_it, e_end; - - for (e_it = n_it->out_edges_begin(), e_end = n_it->out_edges_end(); - e_it != e_end; ++e_it) { - - if (copy[e_it->target()] != node()) { - new_edge(copy[e_it->source()], copy[e_it->target()]); - } - } - } -} - - - -graph::~graph() -{ - clear(); -} - -//------------------------------------------------------------------------- -// Output -//------------------------------------------------------------------------- - -GTL_EXTERN std::ostream& operator<< (std::ostream& os, const graph& G) { - node n; - edge out; - std::string conn; - - if (G.is_directed()) - conn = "-->"; - else - conn = "<-->"; - - forall_nodes (n, G) { - os << n << ":: "; - - forall_adj_edges (out, n) { - os << conn << n.opposite (out); - } - - os << std::endl; - } - - return os; -} - -//-------------------------------------------------------------------------- -// Directed/Undirected -//-------------------------------------------------------------------------- - -void graph::make_directed() -{ - if (!directed) - { - pre_make_directed_handler(); - directed = true; - post_make_directed_handler(); - } -} - -void graph::make_undirected() -{ - if (directed) - { - pre_make_undirected_handler(); - directed = false; - post_make_undirected_handler(); - } -} - -bool graph::is_directed() const -{ - return directed; -} - -bool graph::is_undirected() const -{ - return !directed; -} - -//-------------------------------------------------------------------------- -// Creation -//-------------------------------------------------------------------------- - -node graph::new_node() -{ - pre_new_node_handler(); - - // create node - - node n; - n.data = new node_data; - - // set data variables - - n.data->id = new_node_id(); - n.data->owner = this; - n.data->pos = nodes.insert(nodes.end(), n); - n.data->hidden = false; - ++nodes_count; - - // done - - post_new_node_handler(n); - - return n; -} - -edge graph::new_edge(node source, node target) -{ - assert(source.data); - assert(target.data); - assert(source.data->owner == this); - assert(target.data->owner == this); - - pre_new_edge_handler(source, target); - - // create edge - - edge e; - e.data = new edge_data; - - // set id - - e.data->owner = this; - e.data->id = new_edge_id(); - - // set sources and targets - - e.data->nodes[0].push_back(source); - e.data->nodes[1].push_back(target); - - // set pos - - e.data->pos = edges.insert(edges.end(), e); - e.data->hidden = false; - ++edges_count; - - // set adj_pos - - edges_t& source_adj = source.data->edges[1]; - edges_t& target_adj = target.data->edges[0]; - - e.data->adj_pos[0].push_back(source_adj.insert(source_adj.begin(), e)); - e.data->adj_pos[1].push_back(target_adj.insert(target_adj.begin(), e)); - - // done - - post_new_edge_handler(e); - - return e; -} - -edge graph::new_edge(const nodes_t &/*sources*/, const nodes_t &/*targets*/) -{ - // not implemented - - return edge(); -} - -//-------------------------------------------------------------------------- -// Deletion -//-------------------------------------------------------------------------- - -void graph::del_node(node n) -{ - assert (n.data); - assert (n.data->owner == this); - - // delete edges - - while(n.in_edges_begin() != n.in_edges_end()) - { - del_edge (*n.in_edges_begin()); - } - - while(n.out_edges_begin() != n.out_edges_end()) - { - del_edge (*n.out_edges_begin()); - } - - // - // delete hidden edges adjacent to n. - // - // [ TODO ] This is only a quick fix and should be thought - // over some time or the other. - // - - edges_t::iterator it = hidden_edges.begin(); - edges_t::iterator end = hidden_edges.end(); - - while (it != end) - { - if (it->source() == n || it->target() == n) - { - delete it->data; - it = hidden_edges.erase (it); - } - else - { - ++it; - } - } - - // delete node - - pre_del_node_handler(n); - - nodes.erase(n.data->pos); - --nodes_count; - free_node_ids.push_back(n.data->id); - ++free_node_ids_count; - delete n.data; - - post_del_node_handler(); -} - -void graph::del_edge(edge e) -{ - assert (e.data->owner == this); - assert (e.data->owner == this); - - pre_del_edge_handler(e); - node s = e.source(); - node t = e.target(); - - e.remove_from(0); - e.remove_from(1); - edges.erase(e.data->pos); - --edges_count; - free_edge_ids.push_back(e.data->id); - ++free_edge_ids_count; - delete e.data; - - post_del_edge_handler(s, t); -} - -void graph::clear() -{ - pre_clear_handler(); - - del_list(edges); - del_list(hidden_edges); - del_list(nodes); - del_list(hidden_nodes); - - free_node_ids.clear(); - free_edge_ids.clear(); - - nodes_count = edges_count = 0; - hidden_nodes_count = hidden_edges_count = 0; - free_node_ids_count = free_edge_ids_count = 0; - - post_clear_handler(); -} - -void graph::del_all_nodes() -{ - assert(false); - // not fully implemented: - // * update id lists !!! - // * call handlers - - del_list(edges); - del_list(nodes); - - nodes_count = edges_count = 0; -} - -void graph::del_all_edges() -{ - assert(false); - // not fully implemented: - // * update id lists !!! - // * call handlers - del_list(edges); - - edges_count = 0; - - nodes_t::iterator it = nodes.begin(); - nodes_t::iterator end = nodes.end(); - - while(it != end) - { - it->data->edges[0].clear(); - it->data->edges[1].clear(); - } -} - -//-------------------------------------------------------------------------- -// Informations -//-------------------------------------------------------------------------- - - - -bool graph::is_bidirected(edge_map& rev) const { - edge e1; - node target, source; - bool bidirected = true; - node::out_edges_iterator it; - node::out_edges_iterator end; - - forall_edges (e1, *this) { - target = e1.target (); - source = e1.source (); - end = target.out_edges_end (); - it = target.out_edges_begin (); - - // - // Search all out-edges of target if they are connected to the actual - // edges source. - // - - while (it != end) { - if (it->target () == source) { - break; - } - ++it; - } - - if (it == end) { - bidirected = false; - rev[e1] = edge (); - } else { - rev[e1] = *it; - } - } - - return bidirected; -} - -bool graph::is_connected() const -{ - bool save_directed = directed; - directed = false; - - dfs d; - d.run(*const_cast(this)); - - directed = save_directed; - - return d.number_of_reached_nodes() == number_of_nodes(); -} - -bool graph::is_acyclic() const -{ - topsort t; - t.run(*const_cast(this)); - - return t.is_acyclic(); -} - -int graph::number_of_nodes() const -{ - return nodes_count - hidden_nodes_count; -} - -int graph::number_of_edges() const -{ - return edges_count - hidden_edges_count; -} - -node graph::center() const -{ - int min_excentricity = number_of_nodes()+1; - node n, center; - forall_nodes(n, *this) - { - int excentricity = n.excentricity(); - if(excentricity < min_excentricity) - { - center = n; - min_excentricity = excentricity; - } - } - return center; -} - -//-------------------------------------------------------------------------- -// Iterators -//-------------------------------------------------------------------------- - -graph::node_iterator graph::nodes_begin() const -{ - return nodes.begin(); -} - -graph::node_iterator graph::nodes_end() const -{ - return nodes.end(); -} - -graph::edge_iterator graph::edges_begin() const -{ - return edges.begin(); -} - -graph::edge_iterator graph::edges_end() const -{ - return edges.end(); -} - -//-------------------------------------------------------------------------- -// Node/Edge lists -//-------------------------------------------------------------------------- - -nodes_t graph::all_nodes() const -{ - return nodes; -} - -edges_t graph::all_edges() const -{ - return edges; -} - -//-------------------------------------------------------------------------- -// Hide -// If an edge is already hidden (this really happens :-), it will not be -// hidden for the second time -//-------------------------------------------------------------------------- - -void graph::hide_edge (edge e) -{ - assert (e.data->owner == this); - assert (e.data->owner == this); - - pre_hide_edge_handler (e); - - if (!e.is_hidden()) { - - // - // remove e from all sources and targets adjacency lists - // - e.remove_from(0); - e.remove_from(1); - - // - // clear the list of positions - // - e.data->adj_pos[0].erase - (e.data->adj_pos[0].begin(), e.data->adj_pos[0].end()); - e.data->adj_pos[1].erase - (e.data->adj_pos[1].begin(), e.data->adj_pos[1].end()); - - // - // remove e from the list of all edges - // - edges.erase (e.data->pos); - - // - // insert e in hidden edges list - // - e.data->pos = hidden_edges.insert(hidden_edges.end(), e); - e.data->hidden = true; - ++hidden_edges_count; - } - - post_hide_edge_handler (e); -} - -//-------------------------------------------------------------------------- -// restore_edge -// An edge will be restored only if it is hidden (sounds wise, hmm ...) -//-------------------------------------------------------------------------- - -void graph::restore_edge (edge e) -{ - assert (e.data->owner == this); - assert (e.data->owner == this); - - pre_restore_edge_handler (e); - - if (e.is_hidden()) { - // - // remove e from hidden edges list - // - hidden_edges.erase (e.data->pos); - --hidden_edges_count; - - // - // for each source of e insert e in its list of out-edges and store - // the position in e's list of positions - // - nodes_t::iterator it; - nodes_t::iterator end = e.data->nodes[0].end(); - - for (it = e.data->nodes[0].begin (); it != end; ++it) - { - edges_t& adj = it->data->edges[1]; - e.data->adj_pos[0].push_back(adj.insert(adj.begin(), e)); - } - - // - // for each target of e insert e in its list of in-edges and store - // the pos - // - end = e.data->nodes[1].end(); - - for (it = e.data->nodes[1].begin (); it != end; ++it) - { - edges_t& adj = it->data->edges[0]; - e.data->adj_pos[1].push_back(adj.insert(adj.begin(), e)); - } - - e.data->pos = edges.insert(edges.end(), e); - e.data->hidden = false; - } - - post_restore_edge_handler (e); -} - -//-------------------------------------------------------------------------- -// Hide -// If an node is already hidden (this really happens :-), it will not be -// hidden for the second time -// Note: also all adjacent edges will be hidden -//-------------------------------------------------------------------------- - -edges_t graph::hide_node(node n) -{ - assert (n.data->owner == this); - - pre_hide_node_handler (n); - edges_t implicitly_hidden_edges; - - if (!n.is_hidden()){ - // hide all connected egdes - for (int i = 0; i <= 1; ++i) - { - edges_t::iterator end = n.data->edges[i].end(); - edges_t::iterator edge = n.data->edges[i].begin(); - while (edge != end) - { - implicitly_hidden_edges.push_back(*edge); - hide_edge(*edge); - edge = n.data->edges[i].begin(); - } - } - - // hide node - hidden_nodes.push_back(n); - nodes.erase(n.data->pos); - n.data->hidden = true; - ++hidden_nodes_count; - } - - post_hide_node_handler (n); - - return implicitly_hidden_edges; -} - -//-------------------------------------------------------------------------- -// restore_node -// A node will be restored only if it is hidden (sounds wise, hmm ...) -// connected nodes won't be restored automatically ! -//-------------------------------------------------------------------------- - -void graph::restore_node (node n) -{ - assert (n.data->owner == this); - - pre_restore_node_handler(n); - - if (n.is_hidden()) - { - // node is hidden - - nodes.push_back(n); - n.data->pos = --nodes.end(); - -#if 1 - hidden_nodes.remove(n); -#else - hidden_nodes.erase(std::remove(hidden_nodes.begin(), hidden_nodes.end(), n), hidden_nodes.end()); -#endif - n.data->hidden = false; - --hidden_nodes_count; - } - - post_restore_node_handler (n); -} - - -void graph::induced_subgraph(nodes_t& sub_nodes) -{ - node_map in_sub (*this, 0); - nodes_t::iterator it, end, tmp; - - for (it = sub_nodes.begin(), end = sub_nodes.end(); it != end; ++it) { - in_sub[*it] = 1; - } - - it = nodes.begin(); - end = nodes.end(); - - while (it != end) { - tmp = it; - ++tmp; - - if (!in_sub[*it]) { - hide_node (*it); - } - - it = tmp; - } -} - -void graph::restore_graph () -{ - nodes_t::iterator it, end, tmp; - - it = hidden_nodes.begin(); - end = hidden_nodes.end(); - - while (it != end) - { - tmp = it; - ++tmp; - restore_node(*it); - it = tmp; - } - - edges_t::iterator e_it = hidden_edges.begin(); - edges_t::iterator e_end = hidden_edges.end(); - - while (e_it != e_end) - { - edges_t::iterator e_tmp = e_it; - ++e_tmp; - restore_edge (*e_it); - e_it = e_tmp; - } -} - -//-------------------------------------------------------------------------- -// Node/edge numbering -//-------------------------------------------------------------------------- - -int graph::number_of_ids(node) const -{ - return - free_node_ids_count + - nodes_count; -} - -int graph::number_of_ids(edge) const -{ - return - free_edge_ids_count + - edges_count; -} - -int graph::new_node_id() -{ - if(free_node_ids.empty()) - return nodes_count; - - int id = free_node_ids.back(); - free_node_ids.pop_back(); - --free_node_ids_count; - return id; -} - -int graph::new_edge_id() -{ - if(free_edge_ids.empty()) - return edges_count; - - int id = free_edge_ids.back(); - free_edge_ids.pop_back(); - --free_edge_ids_count; - return id; -} - -//-------------------------------------------------------------------------- -// Utilities -//-------------------------------------------------------------------------- - -void graph::del_list(nodes_t& l) -{ - nodes_t::const_iterator it = l.begin(); - nodes_t::const_iterator end = l.end(); - - while(it != end) - { - delete it->data; - ++it; - } - - l.clear(); -} - -void graph::del_list(edges_t& l) -{ - edges_t::const_iterator it = l.begin(); - edges_t::const_iterator end = l.end(); - - while(it != end) - { - delete it->data; - ++it; - } - - l.clear(); -} - -//-------------------------------------------------------------------------- -// Others -//-------------------------------------------------------------------------- - -edges_t graph::insert_reverse_edges() { - edges_t rev; - edge e; - - node::out_edges_iterator it, end; - - forall_edges (e, *this) { - it = e.target().out_edges_begin(); - end = e.target().out_edges_end(); - - while (it != end) { - if (it->target() == e.source()) - break; - ++it; - } - - if (it == end) { - rev.push_back(new_edge (e.target(), e.source())); - } - } - - return rev; -} - -node graph::choose_node () const -{ - // Well, probably doesn't guarantee uniform distribution :-) - return nodes.empty() ? node() : nodes.front(); -} - -//-------------------------------------------------------------------------- -// I/O -//-------------------------------------------------------------------------- - -GML_error graph::load (const char* filename, bool preserve_ids) { - - GML_stat stat; - stat.key_list = NULL; - GML_pair* key_list; - GML_pair* orig_list; - - FILE* file = fopen (filename, "r"); - - if (!file) { - stat.err.err_num = GML_FILE_NOT_FOUND; - return stat.err; - } - - GML_init (); - key_list = GML_parser (file, &stat, 0); - fclose (file); - - if (stat.err.err_num != GML_OK) { - GML_free_list (key_list, stat.key_list); - return stat.err; - } - - // - // This file is a valid GML-file, let's build the graph. - // - - clear(); - orig_list = key_list; - - - - // - // get the first entry with key "graph" in the list - // - - while (key_list) { - if (!strcmp ( "graph", key_list->key)) { - break; - } - - key_list = key_list->next; - } - - assert (key_list); - - key_list = key_list->value.list; - GML_pair* graph_list = key_list; - - GML_pair* tmp_list; - // GML_pair* node_entries = 0; - // GML_pair* edge_entries = 0; - - std::list > node_entries; - std::list, GML_pair*> > edge_entries; - - int num_nodes = 0; - - bool target_found; - bool source_found; - - // - // Node and edge keys may come in arbitrary order, so sort them such - // that all nodes come before all edges. - // - - while (key_list) { - if (!strcmp (key_list->key, "node")) { - - // - // Search the list associated with this node for the id - // - - assert (key_list->kind == GML_LIST); - tmp_list = key_list->value.list; - std::pair n; - n.second = tmp_list; - - while (tmp_list) { - if (!strcmp (tmp_list->key, "id")) { - assert (tmp_list->kind == GML_INT); - n.first = tmp_list->value.integer; - break; - } - - tmp_list = tmp_list->next; - } - - assert (tmp_list); - node_entries.push_back(n); - ++num_nodes; - - } else if (!strcmp (key_list->key, "edge")) { - - // - // Search for source and target entries - // - - assert (key_list->kind == GML_LIST); - tmp_list = key_list->value.list; - source_found = false; - target_found = false; - std::pair, GML_pair*> e; - e.second = tmp_list; - - while (tmp_list) { - if (!strcmp (tmp_list->key, "source")) { - assert (tmp_list->kind == GML_INT); - source_found = true; - e.first.first = tmp_list->value.integer; - if (target_found) break; - - } else if (!strcmp (tmp_list->key, "target")) { - assert (tmp_list->kind == GML_INT); - target_found = true; - e.first.second = tmp_list->value.integer; - if (source_found) break; - } - - tmp_list = tmp_list->next; - } - - assert (source_found && target_found); - edge_entries.push_back (e); - - } else if (!strcmp (key_list->key, "directed")) { - directed = (key_list->value.integer != 0); - } - - key_list = key_list->next; - } - - // - // make this graph the graph decribed in list - // - - std::map id_2_node; - node source, target; - node tmp_node; - edge tmp_edge; - std::list >::iterator it, end; - std::vector node_ids; - node_ids.reserve(num_nodes); - - for (it = node_entries.begin(), end = node_entries.end(); - it != end; ++it) { - tmp_node = new_node (); - if (preserve_ids) { - tmp_node.data->id = it->first; - node_ids.push_back(it->first); - } - id_2_node[it->first] = tmp_node; - load_node_info_handler (tmp_node, it->second); - } - - std::list, GML_pair*> >::iterator eit, eend; - for (eit = edge_entries.begin(), eend = edge_entries.end(); - eit != eend; ++eit) { - source = id_2_node[eit->first.first]; - target = id_2_node[eit->first.second]; - tmp_edge = new_edge (source, target); - load_edge_info_handler (tmp_edge, eit->second); - } - - load_graph_info_handler (graph_list); - top_level_key_handler (orig_list); - - sort(node_ids.begin(),node_ids.end()); - - std::vector::iterator iit, iend; - int prev = 0; - - for (iit = node_ids.begin(), iend = node_ids.end(); - iit != iend; ++iit) - { - if (iit != node_ids.begin()) { - free_node_ids_count += *iit - prev - 1; - } else { - free_node_ids_count += *iit; - } - prev = *iit; - } - - GML_free_list (orig_list, stat.key_list); - stat.err.err_num = GML_OK; - return stat.err; -} - -void graph::load_node_info_handler (node /*n*/, GML_pair* /*li*/) { -} - - -void graph::load_edge_info_handler (edge /*e*/, GML_pair* /*li*/) { -} - -void graph::load_graph_info_handler (GML_pair* /*li*/) { -} - -void graph::top_level_key_handler (GML_pair* /*li*/) { -} - - -void graph::save(std::ostream* file) const { - pre_graph_save_handler (file); - (*file) << "graph [" << std::endl; - (*file) << "directed " << (directed ? "1" : "0") << std::endl; - - node_iterator it = nodes_begin(); - node_iterator end = nodes_end(); - - for (;it != end; ++it) { - (*file) << "node [\n" << "id " << it->id() << "\n"; - save_node_info_handler (file, *it); - (*file) << " ]" << std::endl; - } - - edge_iterator e_it = edges_begin(); - edge_iterator e_end = edges_end(); - - for (; e_it != e_end; ++e_it) { - (*file) << "edge [\n" << "source " << e_it->source().id() << "\n"; - (*file) << "target " << e_it->target().id() << "\n"; - save_edge_info_handler (file, *e_it); - (*file) << " ]" << std::endl; - } - - save_graph_info_handler (file); - - (*file) << "]" << std::endl; - after_graph_save_handler (file); -} - -int graph::save (const char* filename) const { - - std::ofstream file(filename); - if (!file) return 0; - - save (&file); - - return 1; -} - - -// void graph::top_level_key_handler (GML_pair_list::const_iterator it, -// GML_pair_list::const_iterator end) -// { -// cout << "TOP_LEVEL_HANDLER" << endl; - -// for (; it != end; ++it) { -// cout << *it << endl; -// } -// } - -// void graph::load_graph_info_handler (GML_pair_list::const_iterator it, -// GML_pair_list::const_iterator end) -// { -// cout << "GRAPH_INFO_HANDLER" << endl; - -// for (; it != end; ++it) { -// cout << *it << endl; -// } -// } - -// void graph::load_node_info_handler (node n, GML_pair_list::const_iterator it, -// GML_pair_list::const_iterator end) -// { -// cout << "NODE_INFO_HANDLER for " << n << endl; - -// for (; it != end; ++it) { -// cout << *it << endl; -// } -// } - -// void graph::load_edge_info_handler (edge e, GML_pair_list::const_iterator it, -// GML_pair_list::const_iterator end) -// { -// cout << "EDGE_INFO_HANDLER for " << e.source() << "-->" -// << e.target() << endl; - -// for (; it != end; ++it) { -// cout << *it << endl; -// } -// } - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/maxflow_ff.cpp b/Tracker/graph/GTL/src/maxflow_ff.cpp deleted file mode 100644 index 7240f7daa..000000000 --- a/Tracker/graph/GTL/src/maxflow_ff.cpp +++ /dev/null @@ -1,351 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// maxflow_ff.cpp -// -//========================================================================== -// $Id: maxflow_ff.cpp,v 1.7 2001/11/07 13:58:10 pick Exp $ - -#include - -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -maxflow_ff::maxflow_ff() -{ - max_graph_flow = 0.0; - set_vars_executed = false; -} - - -maxflow_ff::~maxflow_ff() -{ -} - - -void maxflow_ff::set_vars(const edge_map& edge_capacity) -{ - this->edge_capacity = edge_capacity; - artif_source_target = true; - max_graph_flow = 0.0; - set_vars_executed = true; -} - - -void maxflow_ff::set_vars(const edge_map& edge_capacity, - const node& net_source, const node& net_target) -{ - this->edge_capacity = edge_capacity; - this->net_source = net_source; - this->net_target = net_target; - artif_source_target = false; - max_graph_flow = 0.0; - set_vars_executed = true; -} - - -int maxflow_ff::check(graph& G) -{ - if (!set_vars_executed) - { - return(GTL_ERROR); - } - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - if (edge_capacity[*edge_it] < 0) - { - return(GTL_ERROR); - } - ++edge_it; - } - // G.is_acyclic may be false - if ((G.number_of_nodes() <= 1) || (!G.is_connected()) || (G.is_undirected())) - { - return(GTL_ERROR); - } - if (artif_source_target) - { - bool source_found = false; - bool target_found = false; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (node_it->indeg() == 0) - { - source_found = true; - } - if (node_it->outdeg() == 0) - { - target_found = true; - } - ++node_it; - } - if (!(source_found && target_found)) - { - return(GTL_ERROR); - } - } - else - { - if (net_source == net_target) - { - return(GTL_ERROR); - } - } - return(GTL_OK); // ok -} - - -int maxflow_ff::run(graph& G) -{ - // init - if (artif_source_target) - { - create_artif_source_target(G); - } - prepare_run(G); - - node_map last_edge(G); - - while (get_sp(G, last_edge) == SP_FOUND) - { - comp_single_flow(G, last_edge); - } - - restore_graph(G); - return(GTL_OK); -} - - -double maxflow_ff::get_max_flow(const edge& e) const -{ - return(edge_max_flow[e]); -} - - -double maxflow_ff::get_max_flow() const -{ - return(max_graph_flow); -} - - -double maxflow_ff::get_rem_cap(const edge& e) const -{ - return(edge_capacity[e] - edge_max_flow[e]); -} - - -void maxflow_ff::reset() -{ -} - - -void maxflow_ff::create_artif_source_target(graph& G) -{ - net_source = G.new_node(); - net_target = G.new_node(); - edge e; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (*node_it != net_source && node_it->indeg() == 0) - { - e = G.new_edge(net_source, *node_it); - edge_capacity[e] = 1.0; // 1.0 prevents e from hiding - node::out_edges_iterator out_edge_it = node_it->out_edges_begin(); - node::out_edges_iterator out_edges_end = node_it->out_edges_end(); - while (out_edge_it != out_edges_end) - { - edge_capacity[e] += edge_capacity[*out_edge_it]; - ++out_edge_it; - } - } - if (*node_it != net_target && node_it->outdeg() == 0) - { - e = G.new_edge(*node_it, net_target); - edge_capacity[e] = 1.0; // 1.0 prevents e from hiding - node::in_edges_iterator in_edge_it = node_it->in_edges_begin(); - node::in_edges_iterator in_edges_end = node_it->in_edges_end(); - while (in_edge_it != in_edges_end) - { - edge_capacity[e] += edge_capacity[*in_edge_it]; - ++in_edge_it; - } - } - ++node_it; - } -} - - -void maxflow_ff::prepare_run(const graph& G) -{ - edge_max_flow.init(G, 0.0); - edge_org.init(G, true); - back_edge_exists.init(G, false); - max_graph_flow = 0.0; -} - - -void maxflow_ff::comp_single_flow(graph& G, node_map& last_edge) -{ - double min_value = extra_charge(last_edge); - - node cur_node = net_target; - do - { - if (edge_org[last_edge[cur_node]]) // shortest path runs over a org. edge - { - if (!back_edge_exists[last_edge[cur_node]]) // create back edge - { - create_back_edge(G, last_edge[cur_node]); - } - edge_max_flow[last_edge[cur_node]] += min_value; - G.restore_edge(back_edge[last_edge[cur_node]]); - edge_capacity[back_edge[last_edge[cur_node]]] += min_value; - } - else // shortest path runs over a inserted back edge - { - edge oe = back_edge[last_edge[cur_node]]; - G.restore_edge(oe); - edge_max_flow[oe] -= min_value; - edge_capacity[last_edge[cur_node]] -= min_value; - } - if (edge_capacity[last_edge[cur_node]] <= edge_max_flow[last_edge[cur_node]]) - { - G.hide_edge(last_edge[cur_node]); - } - cur_node = last_edge[cur_node].source(); - } - while (cur_node != net_source); -} - - -int maxflow_ff::get_sp(const graph& G, node_map& last_edge) -{ - std::queue next_nodes; - node_map visited(G, false); - next_nodes.push(net_source); - visited[net_source] = true; - - if (comp_sp(G, next_nodes, visited, last_edge) == SP_FOUND) - { - return(SP_FOUND); - } - else - { - return(NO_SP_FOUND); - } -} - - -int maxflow_ff::comp_sp(const graph& /*G*/, std::queue& next_nodes, - node_map& visited, node_map& last_edge) -{ - node cur_node; - - while (!next_nodes.empty()) - { - cur_node = next_nodes.front(); - next_nodes.pop(); - - node::out_edges_iterator out_edge_it = cur_node.out_edges_begin(); - node::out_edges_iterator out_edges_end = cur_node.out_edges_end(); - while (out_edge_it != out_edges_end) - { - node next = out_edge_it->target(); - if (!visited[next]) - { - last_edge[next] = *out_edge_it; - if (next == net_target) - { - return(SP_FOUND); - } - else - { - next_nodes.push(next); - visited[next] = true; - } - } - ++out_edge_it; - } - } - return(NO_SP_FOUND); -} - - -double maxflow_ff::extra_charge(const node_map& last_edge) const -{ - node cur_node = net_target; - double min_value = - edge_capacity[last_edge[cur_node]] - edge_max_flow[last_edge[cur_node]]; - double cur_capacity; - - do - { - cur_capacity = - edge_capacity[last_edge[cur_node]] - edge_max_flow[last_edge[cur_node]]; - - if (cur_capacity < min_value) min_value = cur_capacity; - cur_node = last_edge[cur_node].source(); - } - while (cur_node != net_source); - return(min_value); -} - - -void maxflow_ff::create_back_edge(graph& G, const edge& org_edge) -{ - edge be = G.new_edge(org_edge.target(), org_edge.source()); - edge_org[be] = false; - edges_not_org.push_back(be); - back_edge[org_edge] = be; - back_edge[be] = org_edge; - edge_max_flow[be] = 0.0; - edge_capacity[be] = 0.0; - back_edge_exists[org_edge] = true; - back_edge_exists[be] = true; // a back edge always has a org. edge ;-) -} - - -void maxflow_ff::comp_max_flow(const graph& /*G*/) -{ - max_graph_flow = 0.0; - - node::out_edges_iterator out_edge_it = net_source.out_edges_begin(); - node::out_edges_iterator out_edges_end = net_source.out_edges_end(); - while (out_edge_it != out_edges_end) - { - max_graph_flow += edge_max_flow[*out_edge_it]; - ++out_edge_it; - } -} - - -void maxflow_ff::restore_graph(graph& G) -{ - G.restore_graph(); // hidden edges can not be deleted! - while (!edges_not_org.empty()) - { - G.del_edge(edges_not_org.front()); - edges_not_org.pop_front(); - } - comp_max_flow(G); - if (artif_source_target) - { - G.del_node(net_source); - G.del_node(net_target); - } -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/maxflow_pp.cpp b/Tracker/graph/GTL/src/maxflow_pp.cpp deleted file mode 100644 index 21c65086f..000000000 --- a/Tracker/graph/GTL/src/maxflow_pp.cpp +++ /dev/null @@ -1,737 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// maxflow_pp.cpp -// -//========================================================================== -// $Id: maxflow_pp.cpp,v 1.7 2001/11/07 13:58:10 pick Exp $ - -#include - -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -maxflow_pp::maxflow_pp() -{ - max_graph_flow = 0.0; - set_vars_executed = false; -} - - -maxflow_pp::~maxflow_pp() -{ -} - - -void maxflow_pp::set_vars(const edge_map& edge_capacity) -{ - this->edge_capacity = edge_capacity; - artif_source_target = true; - max_graph_flow = 0.0; - set_vars_executed = true; -} - - -void maxflow_pp::set_vars(const edge_map& edge_capacity, - const node& net_source, const node& net_target) -{ - this->edge_capacity = edge_capacity; - this->net_source = net_source; - this->net_target = net_target; - artif_source_target = false; - max_graph_flow = 0.0; - set_vars_executed = true; -} - - -int maxflow_pp::check(graph& G) -{ - if (!set_vars_executed) - { - return(GTL_ERROR); - } - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - if (edge_capacity[*edge_it] < 0) - { - return(GTL_ERROR); - } - ++edge_it; - } - // G.is_acyclic may be false - if ((G.number_of_nodes() <= 1) || (!G.is_connected()) || (G.is_undirected())) - { - return(GTL_ERROR); - } - if (artif_source_target) - { - bool source_found = false; - bool target_found = false; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (node_it->indeg() == 0) - { - source_found = true; - } - if (node_it->outdeg() == 0) - { - target_found = true; - } - ++node_it; - } - if (!(source_found && target_found)) - { - return(GTL_ERROR); - } - } - else - { - if (net_source == net_target) - { - return(GTL_ERROR); - } - } - return(GTL_OK); // ok -} - - -int maxflow_pp::run(graph& G) -{ - // init - if (artif_source_target) - { - create_artif_source_target(G); - } - prepare_run(G); - - double flow_value = 0; - node min_tp_node; - while(leveling(G) == TARGET_FROM_SOURCE_REACHABLE) - { - hide_unreachable_nodes(G); - min_throughput_node(G, min_tp_node, flow_value); - push(G, min_tp_node, flow_value); - pull(G, min_tp_node, flow_value); - comp_rem_net(G); - } - - restore_graph(G); - return(GTL_OK); -} - - -double maxflow_pp::get_max_flow(const edge& e) const -{ - return(edge_max_flow[e]); -} - - -double maxflow_pp::get_max_flow() const -{ - return(max_graph_flow); -} - - -double maxflow_pp::get_rem_cap(const edge& e) const -{ - return(edge_capacity[e] - edge_max_flow[e]); -} - - -void maxflow_pp::reset() -{ -} - - -void maxflow_pp::create_artif_source_target(graph& G) -{ - net_source = G.new_node(); - net_target = G.new_node(); - edge e; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (*node_it != net_source && node_it->indeg() == 0) - { - e = G.new_edge(net_source, *node_it); - edge_capacity[e] = 1.0; // 1.0 prevents e from hiding - node::out_edges_iterator out_edge_it = node_it->out_edges_begin(); - node::out_edges_iterator out_edges_end = node_it->out_edges_end(); - while (out_edge_it != out_edges_end) - { - edge_capacity[e] += edge_capacity[*out_edge_it]; - ++out_edge_it; - } - } - if (*node_it != net_target && node_it->outdeg() == 0) - { - e = G.new_edge(*node_it, net_target); - edge_capacity[e] = 1.0; // 1.0 prevents e from hiding - node::in_edges_iterator in_edge_it = node_it->in_edges_begin(); - node::in_edges_iterator in_edges_end = node_it->in_edges_end(); - while (in_edge_it != in_edges_end) - { - edge_capacity[e] += edge_capacity[*in_edge_it]; - ++in_edge_it; - } - } - ++node_it; - } -} - - -void maxflow_pp::prepare_run(const graph& G) -{ - flow_update.init(G, 0.0); - edge_max_flow.init(G, 0.0); - edge_org.init(G, true); - back_edge_exists.init(G, false); - max_graph_flow = 0.0; - full_edges.clear(); - temp_unvisible_nodes.clear(); - temp_unvisible_edges.clear(); -} - - -int maxflow_pp::leveling(graph& G) -{ - bool source_target_con = false; - node_map level(G, -1); // -1 means no level yet! - std::queue next_nodes; - next_nodes.push(net_source); - level[net_source] = 0; - node cur_node; - while (!next_nodes.empty()) - { - cur_node = next_nodes.front(); - next_nodes.pop(); - node::out_edges_iterator out_edge_it = cur_node.out_edges_begin(); - node::out_edges_iterator out_edges_end = cur_node.out_edges_end(); - while (out_edge_it != out_edges_end) - { - if (level[out_edge_it->target()] == -1) - { - if (out_edge_it->target() == net_target) - { - source_target_con = true; - } - level[out_edge_it->target()] = level[cur_node] + 1; - next_nodes.push(out_edge_it->target()); - ++out_edge_it; - } - else if (level[out_edge_it->target()] <= level[cur_node]) - { - node::out_edges_iterator temp_it = out_edge_it; - ++out_edge_it; - temp_unvisible_edges.push_back(*temp_it); - G.hide_edge(*temp_it); - } - else - { - ++out_edge_it; - } - } - } - if (source_target_con) - { - return(TARGET_FROM_SOURCE_REACHABLE); - } - else - { - return(TARGET_FROM_SOURCE_NOT_REACHABLE); - } -} - - -void maxflow_pp::hide_unreachable_nodes(graph& G) -{ - node_map reachable_from_net_source(G, false); - node_map reachable_from_net_target(G, false); - std::queue next_nodes; - node cur_node; - - next_nodes.push(net_source); - reachable_from_net_source[net_source] = true; - while (!next_nodes.empty()) - { - cur_node = next_nodes.front(); - next_nodes.pop(); - node::out_edges_iterator out_edge_it = cur_node.out_edges_begin(); - node::out_edges_iterator out_edges_end = cur_node.out_edges_end(); - while (out_edge_it != out_edges_end) - { - node next = out_edge_it->target(); - if (!reachable_from_net_source[next]) - { - next_nodes.push(next); - reachable_from_net_source[next] = true; - } - ++out_edge_it; - } - } - - next_nodes.push(net_target); - reachable_from_net_target[net_target] = true; - while (!next_nodes.empty()) - { - cur_node = next_nodes.front(); - next_nodes.pop(); - node::in_edges_iterator in_edge_it = cur_node.in_edges_begin(); - node::in_edges_iterator in_edges_end = cur_node.in_edges_end(); - while (in_edge_it != in_edges_end) - { - node next = in_edge_it->source(); - if (!reachable_from_net_target[next]) - { - next_nodes.push(next); - reachable_from_net_target[next] = true; - } - ++in_edge_it; - } - } - - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if ((!reachable_from_net_source[*node_it]) || - (!reachable_from_net_target[*node_it])) - { - graph::node_iterator temp_it = node_it; - ++node_it; - temp_unvisible_nodes.push_back(*temp_it); - store_temp_unvisible_edges(*temp_it); - G.hide_node(*temp_it); - } - else - { - ++node_it; - } - } -} - - -void maxflow_pp::store_temp_unvisible_edges(const node& cur_node) -{ - node::in_edges_iterator in_it = cur_node.in_edges_begin(); - node::in_edges_iterator in_edges_end = cur_node.in_edges_end(); - while (in_it != in_edges_end) - { - temp_unvisible_edges.push_back(*in_it); - ++in_it; - } - node::out_edges_iterator out_it = cur_node.out_edges_begin(); - node::out_edges_iterator out_edges_end = cur_node.out_edges_end(); - while (out_it != out_edges_end) - { - temp_unvisible_edges.push_back(*out_it); - ++out_it; - } -} - - -void maxflow_pp::min_throughput_node(const graph& G, node& min_tp_node, - double& flow_value) -{ - min_tp_node = net_source; - flow_value = comp_min_throughput(min_tp_node); - - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - double cur_tp; - while (node_it != nodes_end) - { - cur_tp = comp_min_throughput(*node_it); - if (cur_tp < flow_value) - { - min_tp_node = *node_it; - flow_value = cur_tp; - } - ++node_it; - } -} - - -double maxflow_pp::comp_min_throughput(const node cur_node) const -{ - double in_flow = 0.0; - double out_flow = 0.0; - node::in_edges_iterator in_it = cur_node.in_edges_begin(); - node::in_edges_iterator in_edges_end = cur_node.in_edges_end(); - while (in_it != in_edges_end) - { - in_flow += edge_capacity[*in_it] - edge_max_flow[*in_it]; - ++in_it; - } - node::out_edges_iterator out_it = cur_node.out_edges_begin(); - node::out_edges_iterator out_edges_end = cur_node.out_edges_end(); - while (out_it != out_edges_end) - { - out_flow += edge_capacity[*out_it] - edge_max_flow[*out_it]; - ++out_it; - } - if (cur_node == net_source) - { - return(out_flow); - } - if (cur_node == net_target) - { - return(in_flow); - } - return(in_flow < out_flow ? in_flow : out_flow); -} - - -void maxflow_pp::get_sp_ahead(const graph& G, const node& start_node, - node_map& last_edge) -{ - std::queue next_nodes; - node_map visited(G, false); - next_nodes.push(start_node); - visited[start_node] = true; - - node cur_node; - while (!next_nodes.empty()) - { - cur_node = next_nodes.front(); - next_nodes.pop(); - - node::out_edges_iterator out_edge_it = cur_node.out_edges_begin(); - node::out_edges_iterator out_edges_end = cur_node.out_edges_end(); - while (out_edge_it != out_edges_end) - { - node next = out_edge_it->target(); - if (!visited[next]) - { - last_edge[next] = *out_edge_it; - if (next == net_target) - { - return; // sp found - } - next_nodes.push(next); - visited[next] = true; - } - ++out_edge_it; - } - } -} - - -void maxflow_pp::get_sp_backwards(const graph& G, const node& start_node, - node_map& prev_edge) -{ - std::queue next_nodes; - node_map visited(G, false); - next_nodes.push(start_node); - visited[start_node] = true; - - node cur_node; - while (!next_nodes.empty()) - { - cur_node = next_nodes.front(); - next_nodes.pop(); - - node::in_edges_iterator in_edge_it = cur_node.in_edges_begin(); - node::in_edges_iterator in_edges_end = cur_node.in_edges_end(); - while (in_edge_it != in_edges_end) - { - node next = in_edge_it->source(); - if (!visited[next]) - { - prev_edge[next] = *in_edge_it; - if (next == net_source) - { - return; // sp found - } - next_nodes.push(next); - visited[next] = true; - } - ++in_edge_it; - } - } -} - - -void maxflow_pp::push(graph& G, const node& start_node, const double flow_value) -{ - node_map last_edge; - double cur_flow = flow_value; - double min_value = 0.0; - - if (start_node == net_target) - { - return; // no push necessary - } - do - { - get_sp_ahead(G, start_node, last_edge); - min_value = extra_charge_ahead(start_node, last_edge); - if (min_value > cur_flow) - { - min_value = cur_flow; - } - node cur_node = net_target; - do - { - if (edge_org[last_edge[cur_node]]) - { - edge_max_flow[last_edge[cur_node]] += min_value; - if (back_edge_exists[last_edge[cur_node]]) - { - flow_update[back_edge[last_edge[cur_node]]] += min_value; - } - } - else - { - edge_capacity[last_edge[cur_node]] -= min_value; - flow_update[back_edge[last_edge[cur_node]]] += min_value; - } - if (edge_capacity[last_edge[cur_node]] <= - edge_max_flow[last_edge[cur_node]]) - { - full_edges.push_back(last_edge[cur_node]); - G.hide_edge(last_edge[cur_node]); - } - cur_node = last_edge[cur_node].source(); - } - while (cur_node != start_node); - cur_flow -= min_value; - if (cur_flow < 1e-015) // quite hacky ;-) - { - cur_flow = 0.0; // to avoid rounding errors - } - } while (cur_flow > 0.0); -} - - -void maxflow_pp::pull(graph& G, const node& start_node, const double flow_value) -{ - node_map prev_edge; - double cur_flow = flow_value; - double min_value = 0.0; - - if (start_node == net_source) - { - return; // pull not necessary - } - do - { - get_sp_backwards(G, start_node, prev_edge); - min_value = extra_charge_backwards(start_node, prev_edge); - if (min_value > cur_flow) - { - min_value = cur_flow; - } - node cur_node = net_source; - do - { - if (edge_org[prev_edge[cur_node]]) - { - edge_max_flow[prev_edge[cur_node]] += min_value; - if (back_edge_exists[prev_edge[cur_node]]) - { - flow_update[back_edge[prev_edge[cur_node]]] += min_value; - } - } - else - { - edge_capacity[prev_edge[cur_node]] -= min_value; - flow_update[back_edge[prev_edge[cur_node]]] += min_value; - } - if (edge_capacity[prev_edge[cur_node]] <= - edge_max_flow[prev_edge[cur_node]]) - { - full_edges.push_back(prev_edge[cur_node]); - G.hide_edge(prev_edge[cur_node]); - } - cur_node = prev_edge[cur_node].target(); - } - while (cur_node != start_node); - cur_flow -= min_value; - if (cur_flow < 1e-015) // quite hacky ;-) - { - cur_flow = 0.0; // to avoid rounding errors - } - } while (cur_flow > 0.0); -} - - -void maxflow_pp::comp_rem_net(graph& G) -{ - // update back_edges - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - single_edge_update(G, *edge_it); - ++edge_it; - } - edges_t::iterator list_it = full_edges.begin(); - edges_t::iterator list_end = full_edges.end(); - while (list_it != list_end) - { - G.restore_edge(*list_it); - if (flow_update[*list_it] > 0.0) - { - single_edge_update(G, *list_it); - edges_t::iterator temp_it = list_it; - ++list_it; - full_edges.erase(temp_it); // now it's visible again - } - else - { - if (!back_edge_exists[*list_it]) - { - create_back_edge(G, *list_it); - edge_capacity[back_edge[*list_it]] = edge_max_flow[*list_it]; - } - G.hide_edge(*list_it); - ++list_it; - } - } - - - // make hidden levels visible again - nodes_t::iterator temp_un_node_it = temp_unvisible_nodes.begin(); - nodes_t::iterator temp_un_nodes_end = temp_unvisible_nodes.end(); - while (temp_un_node_it != temp_un_nodes_end) - { - G.restore_node(*temp_un_node_it); - ++temp_un_node_it; - } - edges_t::iterator temp_un_edge_it = temp_unvisible_edges.begin(); - edges_t::iterator temp_un_edges_end = temp_unvisible_edges.end(); - while (temp_un_edge_it != temp_un_edges_end) - { - G.restore_edge(*temp_un_edge_it); - if (flow_update[*temp_un_edge_it] > 0.0) - { - single_edge_update(G, *temp_un_edge_it); - } - ++temp_un_edge_it; - } - temp_unvisible_nodes.clear(); - temp_unvisible_edges.clear(); -} - - -void maxflow_pp::single_edge_update(graph& G, edge cur_edge) -{ - if(edge_org[cur_edge]) - { - edge_max_flow[cur_edge] -= flow_update[cur_edge]; - flow_update[cur_edge] = 0.0; - if (!back_edge_exists[cur_edge]) - { - if (edge_max_flow[cur_edge] > 0.0) - { - create_back_edge(G, cur_edge); - edge_capacity[back_edge[cur_edge]] = edge_max_flow[cur_edge]; - } - } - } - else - { - edge_capacity[cur_edge] += flow_update[cur_edge]; - flow_update[cur_edge] = 0.0; - } -} - - -double maxflow_pp::extra_charge_ahead(const node& start_node, - const node_map& last_edge) const -{ - node cur_node = net_target; - double min_value = edge_capacity[last_edge[cur_node]] - - edge_max_flow[last_edge[cur_node]]; - double cur_capacity; - - do - { - cur_capacity = edge_capacity[last_edge[cur_node]] - - edge_max_flow[last_edge[cur_node]]; - if (cur_capacity < min_value) min_value = cur_capacity; - cur_node = last_edge[cur_node].source(); - } - while (cur_node != start_node); - return(min_value); -} - - -double maxflow_pp::extra_charge_backwards(const node& start_node, const node_map& prev_edge) const -{ - node cur_node = net_source; - double min_value = edge_capacity[prev_edge[cur_node]] - - edge_max_flow[prev_edge[cur_node]]; - double cur_capacity; - - do - { - cur_capacity = edge_capacity[prev_edge[cur_node]] - - edge_max_flow[prev_edge[cur_node]]; - if (cur_capacity < min_value) min_value = cur_capacity; - cur_node = prev_edge[cur_node].target(); - } - while (cur_node != start_node); - return(min_value); -} - - -void maxflow_pp::create_back_edge(graph& G, const edge& org_edge) -{ - edge be = G.new_edge(org_edge.target(), org_edge.source()); - edge_org[be] = false; - edges_not_org.push_back(be); - back_edge[org_edge] = be; - back_edge[be] = org_edge; - edge_max_flow[be] = 0.0; - edge_capacity[be] = 0.0; - back_edge_exists[org_edge] = true; - back_edge_exists[be] = true; // a back edge always has a org. edge ;-) - flow_update[be] = 0.0; -} - - -void maxflow_pp::comp_max_flow(const graph& /*G*/) -{ - max_graph_flow = 0.0; - - node::out_edges_iterator out_edge_it = net_source.out_edges_begin(); - node::out_edges_iterator out_edges_end = net_source.out_edges_end(); - while (out_edge_it != out_edges_end) - { - max_graph_flow += edge_max_flow[*out_edge_it]; - ++out_edge_it; - } -} - - -void maxflow_pp::restore_graph(graph& G) -{ - G.restore_graph(); - while (!edges_not_org.empty()) - { - G.del_edge(edges_not_org.front()); - edges_not_org.pop_front(); - } - comp_max_flow(G); - if (artif_source_target) - { - G.del_node(net_source); - G.del_node(net_target); - } -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/maxflow_sap.cpp b/Tracker/graph/GTL/src/maxflow_sap.cpp deleted file mode 100644 index 49eb92e96..000000000 --- a/Tracker/graph/GTL/src/maxflow_sap.cpp +++ /dev/null @@ -1,437 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// maxflow_sap.cpp -// -//========================================================================== -// $Id: maxflow_sap.cpp,v 1.6 2001/11/07 13:58:10 pick Exp $ - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -maxflow_sap::maxflow_sap() -{ - max_graph_flow = 0.0; - set_vars_executed = false; -} - - -maxflow_sap::~maxflow_sap() -{ -} - - -void maxflow_sap::set_vars(const edge_map& edge_capacity) -{ - this->edge_capacity = edge_capacity; - artif_source_target = true; - max_graph_flow = 0.0; - set_vars_executed = true; -} - - -void maxflow_sap::set_vars(const edge_map& edge_capacity, - const node& net_source, const node& net_target) -{ - this->edge_capacity = edge_capacity; - this->net_source = net_source; - this->net_target = net_target; - artif_source_target = false; - max_graph_flow = 0.0; - set_vars_executed = true; -} - - -int maxflow_sap::check(graph& G) -{ - if (!set_vars_executed) - { - return(GTL_ERROR); - } - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - if (edge_capacity[*edge_it] < 0) - { - return(GTL_ERROR); - } - ++edge_it; - } - // G.is_acyclic may be false - if ((G.number_of_nodes() <= 1) || (!G.is_connected()) - || (G.is_undirected())) - { - return(GTL_ERROR); - } - if (artif_source_target) - { - bool source_found = false; - bool target_found = false; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (node_it->indeg() == 0) - { - source_found = true; - } - if (node_it->outdeg() == 0) - { - target_found = true; - } - ++node_it; - } - if (!(source_found && target_found)) - { - return(GTL_ERROR); - } - } - else - { - if (net_source == net_target) - { - return(GTL_ERROR); - } - } - return(GTL_OK); // everything ok -} - - -int maxflow_sap::run(graph& G) -{ - // init - if (artif_source_target) - { - create_artif_source_target(G); - } - bool go_on = true; - node_map last_edge(G); - node cur_node; - int number_of_nodes = G.number_of_nodes(); - std::vector numb(number_of_nodes, 0); - prepare_run(G); - - comp_dist_labels(G, numb); - cur_node = net_source; - - while (go_on) - { - if (has_an_admissible_arc(cur_node)) - { - advance(cur_node, last_edge); - if (cur_node == net_target) - { - augment(G, last_edge); - cur_node = net_source; - } - } - else // only inadmissible edges - { - go_on = retreat(number_of_nodes, cur_node, last_edge, numb); - } - } - - restore_graph(G); - return(GTL_OK); -} - - -double maxflow_sap::get_max_flow(const edge& e) const -{ - return(edge_max_flow[e]); -} - - -double maxflow_sap::get_max_flow() const -{ - return(max_graph_flow); -} - - -double maxflow_sap::get_rem_cap(const edge& e) const -{ - return(edge_capacity[e] - edge_max_flow[e]); -} - - -void maxflow_sap::reset() -{ - max_graph_flow = 0.0; - set_vars_executed = false; -} - - -void maxflow_sap::create_artif_source_target(graph& G) -{ - net_source = G.new_node(); - net_target = G.new_node(); - edge e; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (*node_it != net_source && node_it->indeg() == 0) - { - e = G.new_edge(net_source, *node_it); - edge_capacity[e] = 1.0; // 1.0 prevents e from hiding - node::out_edges_iterator out_edge_it = - node_it->out_edges_begin(); - node::out_edges_iterator out_edges_end = - node_it->out_edges_end(); - while (out_edge_it != out_edges_end) - { - edge_capacity[e] += edge_capacity[*out_edge_it]; - ++out_edge_it; - } - } - if (*node_it != net_target && node_it->outdeg() == 0) - { - e = G.new_edge(*node_it, net_target); - edge_capacity[e] = 1.0; // 1.0 prevents e from hiding - node::in_edges_iterator in_edge_it = - node_it->in_edges_begin(); - node::in_edges_iterator in_edges_end = - node_it->in_edges_end(); - while (in_edge_it != in_edges_end) - { - edge_capacity[e] += edge_capacity[*in_edge_it]; - ++in_edge_it; - } - } - ++node_it; - } -} - - -void maxflow_sap::prepare_run(const graph& G) -{ - edge_max_flow.init(G, 0.0); - edge_org.init(G, true); - back_edge_exists.init(G, false); - max_graph_flow = 0.0; -} - - -void maxflow_sap::comp_dist_labels(const graph& G, std::vector& numb) -{ - std::queue next_nodes; - node_map visited(G, false); - next_nodes.push(net_target); - visited[net_target] = true; - dist_label[net_target] = 0; - numb[0] = 1; // only one sink - node cur_node; - - while (!next_nodes.empty()) - { - cur_node = next_nodes.front(); - next_nodes.pop(); - node::in_edges_iterator in_edge_it = cur_node.in_edges_begin(); - node::in_edges_iterator in_edges_end = cur_node.in_edges_end(); - while (in_edge_it != in_edges_end) - { - node next = in_edge_it->source(); - if (!visited[next]) - { - next_nodes.push(next); - visited[next] = true; - dist_label[next] = dist_label[cur_node] + 1; - ++numb[dist_label[next]]; - } - ++in_edge_it; - } - } -} - - -bool maxflow_sap::has_an_admissible_arc(const node cur_node) -{ - node::out_edges_iterator out_edge_it = cur_node.out_edges_begin(); - node::out_edges_iterator out_edges_end = cur_node.out_edges_end(); - while (out_edge_it != out_edges_end) - { - if (dist_label[cur_node] == dist_label[out_edge_it->target()] + 1) - { - return true; - } - ++out_edge_it; - } - return false; -} - - -void maxflow_sap::advance(node& cur_node, node_map& last_edge) -{ - node::out_edges_iterator out_edge_it = cur_node.out_edges_begin(); - node::out_edges_iterator out_edges_end = cur_node.out_edges_end(); - while (out_edge_it != out_edges_end) - { - if (dist_label[cur_node] == dist_label[out_edge_it->target()] + 1) - { - last_edge[out_edge_it->target()] = *out_edge_it; - cur_node = out_edge_it->target(); - } - ++out_edge_it; - } -} - - -void maxflow_sap::augment(graph& G, const node_map& last_edge) -{ - double additional_flow = free_capacity(last_edge); - node cur_node = net_target; - - do - { - if (edge_org[last_edge[cur_node]]) - // shortest path runs over a org. edge - { - if (!back_edge_exists[last_edge[cur_node]]) // create back edge - { - create_back_edge(G, last_edge[cur_node]); - } - edge_max_flow[last_edge[cur_node]] += additional_flow; - G.restore_edge(back_edge[last_edge[cur_node]]); - edge_capacity[back_edge[last_edge[cur_node]]] += - additional_flow; - } - else // shortest path runs over a inserted back edge - { - edge oe = back_edge[last_edge[cur_node]]; - G.restore_edge(oe); - edge_max_flow[oe] -= additional_flow; - edge_capacity[last_edge[cur_node]] -= additional_flow; - } - if (edge_capacity[last_edge[cur_node]] <= - edge_max_flow[last_edge[cur_node]]) - { - G.hide_edge(last_edge[cur_node]); - } - cur_node = last_edge[cur_node].source(); - } - while (cur_node != net_source); -} - - -bool maxflow_sap::retreat(const int number_of_nodes, - node& cur_node, - const node_map& last_edge, - std::vector& numb) -{ - --numb[dist_label[cur_node]]; - if (numb[dist_label[cur_node]] == 0) - { - return false; - } - else - { - dist_label[cur_node] = - min_neighbour_label(number_of_nodes, cur_node) + 1; - ++numb[dist_label[cur_node]]; - if (cur_node != net_source) - { - cur_node = last_edge[cur_node].source(); - } - return true; - } -} - - -int maxflow_sap::min_neighbour_label(const int number_of_nodes, - const node cur_node) const -{ - int min_value = number_of_nodes; // if no out edge exists - - node::out_edges_iterator out_edge_it = cur_node.out_edges_begin(); - node::out_edges_iterator out_edges_end = cur_node.out_edges_end(); - while (out_edge_it != out_edges_end) - { - if (min_value > dist_label[out_edge_it->target()]) - { - min_value = dist_label[out_edge_it->target()]; - } - ++out_edge_it; - } - return min_value; -} - - -double maxflow_sap::free_capacity(const node_map& last_edge) const -{ - node cur_node = net_target; - double min_value = - edge_capacity[last_edge[cur_node]] - - edge_max_flow[last_edge[cur_node]]; - double cur_capacity; - - do - { - cur_capacity = - edge_capacity[last_edge[cur_node]] - - edge_max_flow[last_edge[cur_node]]; - - if (cur_capacity < min_value) - { - min_value = cur_capacity; - } - cur_node = last_edge[cur_node].source(); - } - while (cur_node != net_source); - return(min_value); -} - - -void maxflow_sap::create_back_edge(graph& G, const edge& org_edge) -{ - edge be = G.new_edge(org_edge.target(), org_edge.source()); - edge_org[be] = false; - edges_not_org.push_back(be); - back_edge[org_edge] = be; - back_edge[be] = org_edge; - edge_max_flow[be] = 0.0; - edge_capacity[be] = 0.0; - back_edge_exists[org_edge] = true; - back_edge_exists[be] = true; // a back edge always has a org. edge -} - - -void maxflow_sap::comp_max_flow(const graph& /*G*/) -{ - max_graph_flow = 0.0; - - node::out_edges_iterator out_edge_it = net_source.out_edges_begin(); - node::out_edges_iterator out_edges_end = net_source.out_edges_end(); - while (out_edge_it != out_edges_end) - { - max_graph_flow += edge_max_flow[*out_edge_it]; - ++out_edge_it; - } -} - - -void maxflow_sap::restore_graph(graph& G) -{ - G.restore_graph(); // hidden edges can not be deleted! - while (!edges_not_org.empty()) - { - G.del_edge(edges_not_org.front()); - edges_not_org.pop_front(); - } - comp_max_flow(G); - if (artif_source_target) - { - G.del_node(net_source); - G.del_node(net_target); - } -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/min_tree.cpp b/Tracker/graph/GTL/src/min_tree.cpp deleted file mode 100644 index 9eebcdda7..000000000 --- a/Tracker/graph/GTL/src/min_tree.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// min_tree.cpp -// -//========================================================================== -// $Id: min_tree.cpp,v 1.4 2001/11/07 13:58:10 pick Exp $ - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -min_tree::min_tree () { - is_set_distances = false; - weight = 0; -} - -int min_tree::check (graph& g) { - if (g.is_directed()) return GTL_ERROR; - else if (g.number_of_nodes() < 2) return GTL_ERROR; - else if (!g.is_connected()) return GTL_ERROR; - else if (!is_set_distances) return GTL_ERROR; - else return GTL_OK; -} - -void min_tree::set_distances (const edge_map& dist) { - this->dist = dist; - is_set_distances = true; -} - -std::set min_tree::get_min_tree() { - return this->tree; -} - -int min_tree::get_min_tree_length() { - int sum; - std::set::iterator tree_it; - - sum = 0; - - for (tree_it = tree.begin(); tree_it != tree.end(); tree_it++) - sum += dist[*tree_it]; - - return sum; -} - -int min_tree::run (graph& g) { - std::priority_queue , input_comp> node_distances; - node::adj_edges_iterator adj_it, adj_end; - std::set tree_nodes; - std::set::iterator tree_it; - edge curr; - node new_node; - graph::edge_iterator edge_it, edges_end; - unsigned int number_of_nodes; - int min_dist; - - - // making out the start edge - - edge_it = g.edges_begin(); - edges_end = g.edges_end(); - - curr = *edge_it; - min_dist = dist[*edge_it]; - - for (; edge_it != edges_end; edge_it++) { - if (dist[*edge_it] < min_dist) { - curr = *edge_it; - min_dist = dist[*edge_it]; - } - } - - tree.insert(curr); - - tree_nodes.insert(curr.source()); - tree_nodes.insert(curr.target()); - - - for (tree_it = tree_nodes.begin(); tree_it != tree_nodes.end(); tree_it++) { - adj_it = tree_it->adj_edges_begin(); - adj_end = tree_it->adj_edges_end(); - - for (; adj_it != adj_end; adj_it++) { - node_distances.push(TSP_A_VALUE(dist[*adj_it], adj_it)); - } - } - - // create the min_tree - - number_of_nodes = g.number_of_nodes(); - - while(tree.size() < number_of_nodes - 1) { - curr = *((node_distances.top()).second); - - node_distances.pop(); - - if (tree_nodes.find(curr.source()) != tree_nodes.end() && - tree_nodes.find(curr.target()) != tree_nodes.end()) { - } else { - tree.insert(curr); - weight += dist[curr]; - - if (tree_nodes.find(curr.source()) != tree_nodes.end()) { - new_node = curr.target(); - } else { - new_node = curr.source(); - } - - tree_nodes.insert(new_node); - - adj_it = new_node.adj_edges_begin(); - adj_end = new_node.adj_edges_end(); - - for (; adj_it != adj_end; adj_it++) { - node_distances.push(TSP_A_VALUE(dist[*adj_it], adj_it)); - } - } - } - - return GTL_OK; -} - -void min_tree::reset() { - tree.erase (tree.begin(), tree.end()); - weight = 0; -} - -__GTL_END_NAMESPACE - - - - - - - - - diff --git a/Tracker/graph/GTL/src/node.cpp b/Tracker/graph/GTL/src/node.cpp deleted file mode 100644 index 432bc5758..000000000 --- a/Tracker/graph/GTL/src/node.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// node.cpp -// -//========================================================================== -// $Id: node.cpp,v 1.18 2001/11/07 13:58:10 pick Exp $ - -#include -#include -#include -#include - -#include -#include - -__GTL_BEGIN_NAMESPACE - -node::node() : - data(0) -{ -} - -GTL_EXTERN std::ostream& operator<< (std::ostream& os, const node& n) { - if (n != node()) { - return os << "[" << n.id() << "]"; - } else { - return os << "[ UNDEF ]"; - } -} - -node::adj_nodes_iterator node::adj_nodes_begin() const -{ - return node::adj_nodes_iterator(*this, true); -} - -node::adj_nodes_iterator node::adj_nodes_end() const -{ - return node::adj_nodes_iterator(*this, false); -} - -node::adj_edges_iterator node::adj_edges_begin() const -{ - return node::adj_edges_iterator(*this, true); -} - -node::adj_edges_iterator node::adj_edges_end() const -{ - return node::adj_edges_iterator(*this, false); -} - -node::inout_edges_iterator node::inout_edges_begin() const -{ - return node::inout_edges_iterator(*this, true); -} - -node::inout_edges_iterator node::inout_edges_end() const -{ - return node::inout_edges_iterator(*this, false); -} - -node::in_edges_iterator node::in_edges_begin() const -{ - return data->edges[0].begin(); -} - -node::in_edges_iterator node::in_edges_end() const -{ - return data->edges[0].end(); -} - -node::out_edges_iterator node::out_edges_begin() const -{ - return data->edges[1].begin(); -} - -node::out_edges_iterator node::out_edges_end() const -{ - return data->edges[1].end(); -} - -int node::degree() const -{ - return outdeg() + indeg(); -} - -int node::outdeg() const -{ - return data->edges[1].size(); -} - -int node::indeg() const -{ - return data->edges[0].size(); -} - -int node::id() const -{ - return data->id; -} - -bool node::is_directed() const -{ - return data->owner->is_directed(); -} - -bool node::is_undirected() const -{ - return data->owner->is_undirected(); -} - -const node& node::opposite(edge e) const -{ - // not implemented for hypergraphs - assert(e.data); - - node& s = *(e.data->nodes[0].begin()); - if (*this == s) - return *(e.data->nodes[1].begin()); - else - return s; -} - -nodes_t node::opposites(edge) const -{ - // not implemented yet - return nodes_t(); // to avoid compiler warnings -} - -bool node::is_hidden () const -{ - return data->hidden; -} - -int node::excentricity() const -{ - bfs b; - b.start_node(*this); - b.calc_level(true); - b.run(*data->owner); - - node last_node = *(--b.end()); - - return b.level(last_node); -} - -GTL_EXTERN bool operator==(node v1, node v2) -{ - return v1.data == v2.data; -} - -GTL_EXTERN bool operator!=(node v1, node v2) -{ - return v1.data != v2.data; -} - -GTL_EXTERN bool operator<(node v1, node v2) -{ - return v1.data < v2.data; -} - -//-------------------------------------------------------------------------- -// adj_edges_iterator -//-------------------------------------------------------------------------- - -node::adj_edges_iterator::adj_edges_iterator() -{ -} - -node::adj_edges_iterator::adj_edges_iterator(node n, bool start) -{ - // iterators that are used everytime - last_edge[0] = n.out_edges_end(); - last_edge[1] = n.in_edges_end(); - directed = n.is_directed(); - if (!directed) - { - begin_edge[0] = n.out_edges_begin(); - begin_edge[1] = n.in_edges_begin(); - } - - // set at start or end - if (start) - { - inout = 0; - akt_edge[0] = n.out_edges_begin(); - if (!directed) - { - akt_edge[1] = n.in_edges_begin(); - if (akt_edge[0] == last_edge[0]) - inout = 1; - } - } - else - { - inout = directed ? 0 : 1; - akt_edge[0] = n.out_edges_end(); - if (!directed) - akt_edge[1] = n.in_edges_end(); - } -} - -bool node::adj_edges_iterator::operator==(const - node::adj_edges_iterator& i) const -{ - return i.akt_edge[i.inout] == akt_edge[inout]; -} - -bool node::adj_edges_iterator::operator!=(const - node::adj_edges_iterator& i) const -{ - return i.akt_edge[i.inout] != akt_edge[inout]; -} - -node::adj_edges_iterator& node::adj_edges_iterator::operator++() -{ - if (directed) - ++akt_edge[inout]; - else - { - if (inout == 0) - { - ++akt_edge[0]; - if (akt_edge[0] == last_edge[0]) - ++inout; - } - else // inout == 1 - { - if (akt_edge[1] == last_edge[1]) - { - inout = 0; - akt_edge[0] = begin_edge[0]; - akt_edge[1] = begin_edge[1]; - if (begin_edge[0] == last_edge[0]) - inout = 1; - } - else - ++akt_edge[inout]; - } - } - return *this; -} - -node::adj_edges_iterator node::adj_edges_iterator::operator++(int) -{ - node::adj_edges_iterator tmp = *this; - operator++(); - return tmp; -} - -node::adj_edges_iterator& node::adj_edges_iterator::operator--() -{ - if (!directed && inout == 1 && akt_edge[1] == begin_edge[1]) - inout = 0; - --akt_edge[inout]; - return *this; -} - -node::adj_edges_iterator node::adj_edges_iterator::operator--(int) -{ - node::adj_edges_iterator tmp = *this; - operator--(); - return tmp; -} - -const edge& node::adj_edges_iterator::operator*() const -{ - return *akt_edge[inout]; -} - -const edge* node::adj_edges_iterator::operator->() const -{ - return akt_edge[inout].operator->(); -} - -//-------------------------------------------------------------------------- -// inout_edges_iterator -//-------------------------------------------------------------------------- - -node::inout_edges_iterator::inout_edges_iterator() -{ -} - -node::inout_edges_iterator::inout_edges_iterator(node n, bool start) -{ - // iterators that are used everytime - last_edge = n.in_edges_end(); - begin_edge = n.out_edges_begin(); - - // set at start or end - if (start) - { - inout = 0; - akt_edge[0] = n.in_edges_begin(); - akt_edge[1] = n.out_edges_begin(); - if (akt_edge[0] == last_edge) - inout = 1; - } - else - { - inout = 1; - akt_edge[0] = n.in_edges_end(); - akt_edge[1] = n.out_edges_end(); - } -} - -bool node::inout_edges_iterator::operator==(const - node::inout_edges_iterator& i) const -{ - return i.akt_edge[i.inout] == akt_edge[inout]; -} - -bool node::inout_edges_iterator::operator!=(const - node::inout_edges_iterator& i) const -{ - return i.akt_edge[i.inout] != akt_edge[inout]; -} - -node::inout_edges_iterator& node::inout_edges_iterator::operator++() -{ - ++akt_edge[inout]; - if ((akt_edge[inout] == last_edge) && (inout==0)) - ++inout; - return *this; -} - -node::inout_edges_iterator node::inout_edges_iterator::operator++(int) -{ - node::inout_edges_iterator tmp = *this; - operator++(); - return tmp; -} - -node::inout_edges_iterator& node::inout_edges_iterator::operator--() -{ - if (inout == 1 && (akt_edge[1] == begin_edge)) - inout = 0; - --akt_edge[inout]; - return *this; -} - -node::inout_edges_iterator node::inout_edges_iterator::operator--(int) -{ - node::inout_edges_iterator tmp = *this; - operator--(); - return tmp; -} - -const edge& node::inout_edges_iterator::operator*() const -{ - return *akt_edge[inout]; -} - -const edge* node::inout_edges_iterator::operator->() const -{ - return akt_edge[inout].operator->(); -} - -//-------------------------------------------------------------------------- -// adj_nodes_iterator -//-------------------------------------------------------------------------- - -node::adj_nodes_iterator::adj_nodes_iterator() -{ -} - -node::adj_nodes_iterator::adj_nodes_iterator(const node& n, bool start) -{ - int_node = n; - if (start) - akt_edge = n.adj_edges_begin(); - else - akt_edge = n.adj_edges_end(); -} - -bool node::adj_nodes_iterator::operator==(const - node::adj_nodes_iterator& i) const -{ - return i.akt_edge == akt_edge; -} - -bool node::adj_nodes_iterator::operator!=(const - node::adj_nodes_iterator& i) const -{ - return i.akt_edge != akt_edge; -} - -node::adj_nodes_iterator& node::adj_nodes_iterator::operator++() -{ - ++akt_edge; - return *this; -} - -node::adj_nodes_iterator node::adj_nodes_iterator::operator++(int) -{ - node::adj_nodes_iterator tmp = *this; - operator++(); - return tmp; -} - -node::adj_nodes_iterator& node::adj_nodes_iterator::operator--() -{ - --akt_edge; - return *this; -} - -node::adj_nodes_iterator node::adj_nodes_iterator::operator--(int) -{ - node::adj_nodes_iterator tmp = *this; - operator--(); - return tmp; -} - -const node& node::adj_nodes_iterator::operator*() const -{ - return int_node.opposite(*akt_edge); -} - -const node* node::adj_nodes_iterator::operator->() const -{ - return &(int_node.opposite(*akt_edge)); -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/planarity.cpp b/Tracker/graph/GTL/src/planarity.cpp deleted file mode 100644 index 80d4ea9a3..000000000 --- a/Tracker/graph/GTL/src/planarity.cpp +++ /dev/null @@ -1,1670 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// planarity.cpp -// -//========================================================================== -// $Id: planarity.cpp,v 1.28 2008/02/03 18:12:07 chris Exp $ - -#include -#include -#include -#include - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -//-------------------------------------------------------------------------- -// Planarity Test -//-------------------------------------------------------------------------- - - -planarity::planarity() : - algorithm (), emp (false), kup (false), bip (true) -{ -#ifdef _DEBUG - GTL_debug::init_debug(); -#endif -} - -planarity::~planarity() -{ -#ifdef _DEBUG - GTL_debug::close_debug(); -#endif -} - - -int planarity::check (graph& /*G*/) -{ - return algorithm::GTL_OK; -} - -bool planarity::run_on_biconnected (graph& G, planar_embedding& em) -{ - - if (G.number_of_edges() == 0) return algorithm::GTL_OK; - - st_number st_; - - // - // The graph may have self loops. Make sure that we - // choose a normal edge for st. - // - - graph::edge_iterator - edge_it = G.edges_begin(), - edge_end = G.edges_end(); - - edge st; - - while (edge_it != edge_end) { - if (edge_it->source() != edge_it->target()) { - st = *edge_it; - break; - } - ++edge_it; - } - - // - // G has only selfloops - // - - if (st == edge()) { - if (emp) { - em.init (G); - edge_it = G.edges_begin(); - edge_end = G.edges_end(); - - for (;edge_it != edge_end; ++edge_it) { - em.self.push_back (*edge_it); - } - } - - return algorithm::GTL_OK; - } - - st_.st_edge (st); - st_.s_node (st.source()); - int res = st_.check(G); - assert (res == algorithm::GTL_OK); - res = st_.run(G); - assert (res == algorithm::GTL_OK); - int size = G.number_of_nodes(); - - if (emp) { - em.init (G); - } - - std::list neighbors; - st_number::iterator st_it = st_.begin(); - node curr = *st_it; - node::out_edges_iterator o_it = curr.out_edges_begin(); - node::out_edges_iterator o_end = curr.out_edges_end(); - node::in_edges_iterator i_it = curr.in_edges_begin(); - node::in_edges_iterator i_end = curr.in_edges_end(); - edges_t self_loops; - node opp; - node_map visited_from (G, 0); - pq_leaf* tmp_leaf; - std::vector< std::list > leaves(size); - - for (; o_it != o_end; ++o_it) { - opp = curr.opposite (*o_it); - - if (opp != curr) { - if (visited_from[opp] == st_[curr] && emp) { - em.multi.push_back (*o_it); - } else { - visited_from[opp] = st_[curr]; - tmp_leaf = new pq_leaf (st_[opp], st_[curr], *o_it, opp); - leaves[st_[opp]-1].push_back (tmp_leaf); - neighbors.push_back (tmp_leaf); - } - - } else if (emp) { - em.self.push_back (*o_it); - } - } - - for (; i_it != i_end; ++i_it) { - opp = curr.opposite (*i_it); - - if (opp != curr) { - if (visited_from[opp] == st_[curr] && emp) { - em.multi.push_back (*i_it); - } else { - visited_from[opp] = st_[curr]; - tmp_leaf = new pq_leaf (st_[opp], st_[curr], *i_it, opp); - leaves[st_[opp]-1].push_back (tmp_leaf); - neighbors.push_back (tmp_leaf); - } - } - } - - node_map > dirs; - - // - // There is a problem with node/edge maps of iterators with Visual C++ - // which I dont fully understand at the moment. Fortunatly the init for the - // maps below is only needed to allocate memory, which is done anyway, when - // values are assigned to it. - // - -#ifndef __GTL_MSVCC - dirs.init (G); -#endif - pq_tree PQ (st_[curr], curr, neighbors); - neighbors.erase (neighbors.begin(), neighbors.end()); - ++st_it; - curr = *st_it; - - while (st_[curr] < size) { - -#ifdef _DEBUG - char filename[10] = "out"; - char buffer[12]; -#ifdef __GTL_MSVCC - _snprintf (buffer, 12, "%s%d.gml", filename, st_[curr]); -#else - snprintf (buffer, 12, "%s%d.gml", filename, st_[curr]); -#endif - std::ofstream os(buffer, std::ios::out | std::ios::trunc); - os << PQ << std::endl; - os.close(); - bool ret_flag = PQ.integrity_check(); - assert(ret_flag); -#endif - - if (!PQ.reduce (leaves[st_[curr]-1])) { -#ifdef _DEBUG - os.open("fail.gml", std::ios::out | std::ios::trunc); - os << PQ << std::endl; - os.close (); -#endif - if (kup) { - examine_obstruction (G, st_, curr, - PQ.get_fail(), PQ.is_fail_root(), em, dirs, &PQ); - } - - PQ.reset(); - return false; - } - - - // - // It seems to be not very comfortable to use in and out iterators to - // go through the adjacency of a node. For graphs without selfloops this - // could be replaced by using adj_iterator, but if there are selfloops - // they will occur in both the list of outgoing and the one of incoming - // edges, and thus two times in the adjacency. - // - - o_it = curr.out_edges_begin(); - o_end = curr.out_edges_end(); - i_it = curr.in_edges_begin(); - i_end = curr.in_edges_end(); - - for (; o_it != o_end; ++o_it) { - opp = curr.opposite (*o_it); - - if (st_[opp] > st_[curr]) { - if (visited_from[opp] == st_[curr] && emp) { - em.multi.push_back (*o_it); - } else { - visited_from[opp] = st_[curr]; - tmp_leaf = new pq_leaf (st_[opp], st_[curr], *o_it, opp); - leaves[st_[opp]-1].push_back (tmp_leaf); - neighbors.push_back (tmp_leaf); - } - - } else if (st_[opp] == st_[curr] && emp) { - em.self.push_back (*o_it); - } - } - - for (; i_it != i_end; ++i_it) { - opp = curr.opposite (*i_it); - - if (st_[opp] > st_[curr]) { - if (visited_from[opp] == st_[curr] && emp) { - em.multi.push_back (*i_it); - } else { - visited_from[opp] = st_[curr]; - tmp_leaf = new pq_leaf (st_[opp], st_[curr], *i_it, opp); - leaves[st_[opp]-1].push_back (tmp_leaf); - neighbors.push_back (tmp_leaf); - } - } - } - - if (emp) { - PQ.replace_pert (st_[curr], curr, neighbors, &em, &(dirs[curr])); -#ifdef _DEBUG - GTL_debug::os() << "Embedding of " << st_[curr] << ":: "; - planar_embedding::iterator adit, adend; - for (adit = em.adj_edges_begin (curr), adend = em.adj_edges_end (curr); adit != adend; ++adit) { - GTL_debug::os() << "[" << st_[curr.opposite (*adit)] << "]"; - } - GTL_debug::os() << std::endl; - GTL_debug::os() << "Direction Indicators for: " << st_[curr] << ":: "; - std::list::iterator dit, dend; - - for (dit = dirs[curr].begin(), dend = dirs[curr].end(); dit != dend; ++dit) { - GTL_debug::os() << "["; - if (dit->direction) - GTL_debug::os() << ">> " << dit->id << " >>"; - else - GTL_debug::os() << "<< " << dit->id << " <<"; - GTL_debug::os() << "]"; - } - GTL_debug::os() << std::endl; -#endif - - } else { - PQ.replace_pert (st_[curr], curr, neighbors); - } - - PQ.reset(); - - - neighbors.erase (neighbors.begin(), neighbors.end()); - ++st_it; - curr = *st_it; - } - - if (emp) { - PQ.get_frontier (em, dirs[curr]); - - - // - // get self_loops for last node - // - - o_it = curr.out_edges_begin(); - o_end = curr.out_edges_end(); - - for (; o_it != o_end; ++o_it) { - if (o_it->target() == o_it->source()) { - em.self.push_back (*o_it); - } - } - - // - // some adjcacency list of the embedding obtained so far have to be - // turned. - // - - correct_embedding(em, st_, dirs); - - node_map mark; - mark.init (G, 0); - node_map::iterator > upward_begin; - upward_begin.init (G); - node tmp; - - forall_nodes (tmp, G) { - upward_begin[tmp] = em.adjacency(tmp).begin(); - } - - extend_embedding(curr, em, mark, upward_begin); - } - - return true; -} - - -int planarity::run (graph& G) -{ - bool directed = false; - - if (G.is_directed()) { - G.make_undirected(); - directed = true; - } - - biconnectivity biconn; - - if (bip) { - biconn.make_biconnected (true); - } else { - biconn.store_components (true); - } - - biconn.check (G); - biconn.run (G); - - if (emp) { - embedding.init (G); - } - - planar_embedding em; - - if (!biconn.is_biconnected() && !bip) { - biconnectivity::component_iterator c_it, c_end; - - for (c_it = biconn.components_begin(), c_end = biconn.components_end(); - c_it != c_end; ++c_it) { - - switch_to_component (G, c_it); - -#ifdef _DEBUG - GTL_debug::os() << "Component is: " << std::endl; - GTL_debug::os() << G << std::endl; -#endif - if (!run_on_biconnected (G, em)) { - if (directed) { - G.make_directed(); - } - - G.restore_graph(); - planar = false; - return algorithm::GTL_OK; - } - - if (emp) { - add_to_embedding (G, em); - } - } - - G.restore_graph(); - - } else { - - // - // G is already biconnected - // - - GTL_debug::debug_message ("graph is biconnected\n"); - - if (!run_on_biconnected (G, embedding)) { - if (directed) { - G.make_directed(); - } - - planar = false; - return algorithm::GTL_OK; - } - } - - if (bip) { - edges_t::iterator it, end; - it = biconn.additional_begin(); - end = biconn.additional_end(); - - for (; it != end; ++it) { - - if (emp) { - node s = it->source(); - node t = it->target(); - embedding.adj[s].erase (embedding.s_pos[*it]); - embedding.adj[t].erase (embedding.t_pos[*it]); - } - - G.del_edge (*it); - } - } - - if (directed) { - G.make_directed(); - } - - planar = true; - return algorithm::GTL_OK; -} - -void planarity::add_to_embedding (graph& G, planar_embedding& em) -{ - node n; - forall_nodes (n, G) { - planar_embedding::iterator it = em.adj_edges_begin (n); - planar_embedding::iterator end = em.adj_edges_end (n); - - for (; it != end; ++it) { - embedding.pos (n, *it) = em.pos (n, *it); - } - - embedding.adjacency(n).splice ( - embedding.adj_edges_end (n), - em.adj_edges_begin (n), - em.adj_edges_end (n)); - } - - embedding.self.splice ( - embedding.self.end(), - em.self, em.self.begin(), em.self.end()); - embedding.multi.splice ( - embedding.multi.end(), - em.multi, em.multi.begin(), em.multi.end()); -} - - -void planarity::reset () -{ - ob_edges.erase (ob_edges.begin(), ob_edges.end()); - ob_nodes.erase (ob_nodes.begin(), ob_nodes.end()); -} - - -void planarity::correct_embedding ( - planar_embedding& em, - st_number& st_, - node_map >& dirs) -{ - st_number::reverse_iterator it = st_.rbegin(); - st_number::reverse_iterator end = st_.rend(); - bool* turn = new bool[st_[*it]]; - - for (int i = 0; i < st_[*it]; ++i) { - turn[i] = false; - } - - while (it != end) { - node curr = *it; - - if (turn[st_[curr] - 1]) { - em.adjacency(curr).reverse(); - } - - std::list::iterator d_it = dirs[curr].begin(); - - while (!dirs[curr].empty()) { - - if (d_it->direction && turn[st_[curr] - 1] || - !d_it->direction && !turn[st_[curr] - 1]) { - turn[d_it->id - 1] = true; - } - - d_it = dirs[curr].erase (d_it); - } - - ++it; - } - - delete[] turn; -} - - -void planarity::extend_embedding ( - node n, - planar_embedding& em, - node_map& mark, - node_map::iterator >& upward_begin) -{ - mark[n] = 1; - - symlist::iterator it = upward_begin[n]; - symlist::iterator end = em.adjacency(n).end(); - node other; - - for (; it != end; ++it) { - em.pos (n, *it) = it; - other = n.opposite (*it); - em.pos (other, *it) = em.push_front (other, *it); - - if (mark[other] == 0) { - extend_embedding (other, em, mark, upward_begin); - } - } -} - -void planarity::switch_to_component (graph& G, - biconnectivity::component_iterator c_it) -{ - // - // hide all nodes - // - - nodes_t dummy; - G.induced_subgraph (dummy); - - // - // Restore nodes in this component. - // - - nodes_t::iterator it = c_it->first.begin(); - nodes_t::iterator end = c_it->first.end(); - - for (; it != end; ++it) { - G.restore_node (*it); - } - - // - // Restore edges in this component. - // - - edges_t::iterator e_it = c_it->second.begin(); - edges_t::iterator e_end = c_it->second.end(); - - for (; e_it != e_end; ++e_it) { - G.restore_edge (*e_it); - } -} - -void planarity::examine_obstruction (graph& G, - st_number& st_, - node act, - pq_node* fail, - bool is_root, - planar_embedding& em, - node_map >& dirs, - pq_tree* PQ) -{ - node_map used (G, 0); - node_map to_father (G); - - // - // Create a dfs-tree of the so called bush form. This is basically a normal dfs - // applied to the induced subgraph of G consisting only of the nodes with st_number - // 1, ..., st_[act] - 1. The only difference is that edges are always directed from - // the lower numbered vertex to higher numbered one. - // - - dfs_bushform (st_.s_node(), used, st_, st_[act], to_father); - - if (fail->kind() == pq_node::Q_NODE) { - - // - // In case the reduction failed at a Q-Node we need to know the edges that - // form the boundary of the biconnected component, which this Q-Node represents. - // These can easily be obtained from the embedding we got so far. - // - - q_node* q_fail = fail->Q(); - - pq_tree::sons_iterator s_it = q_fail->sons.begin(); - pq_tree::sons_iterator s_end = q_fail->sons.end(); - node greatest = fail->n; - - while (s_it != s_end) { - if ((*s_it)->kind() == pq_node::DIR) { - direction_indicator* dir = (*s_it)->D(); - pq_tree::sons_iterator tmp = s_it; - - if (++tmp == ++(dir->pos)) { - dir->direction = true; - } else { - dir->direction = false; - } - - dirs[act].push_back (*dir); - - // - // chris 2/3/2008: - // - // To avoid a memory leak, it is not sufficient to erase it from the - // PQ-tree (-node). The direction indicator object also has to be - // deleted. Since it is then not a member of the pertinent subtree any - // more, it must not be cleared by PQ->reset(). The instance in the - // dirs node map is a clone! - // - - // s_it = q_fail->sons.erase (s_it); - s_it = PQ->remove_dir_ind(q_fail, s_it); - } else { - if (st_[(*s_it)->up] > st_[greatest]) { - greatest = (*s_it)->up; - } - - ++s_it; - } - } - - correct_embedding (em, st_, dirs); - node_map mark; - mark.init (G, 0); - node_map::iterator > upward_begin; - upward_begin.init (G); - node tmp; - - em.adjacency(fail->n).erase ( - em.adjacency(fail->n).begin(), - em.adjacency(fail->n).end()); - - forall_nodes (tmp, G) { - upward_begin[tmp] = em.adjacency(tmp).begin(); - } - - // - // chris 2/3/2008: - // - // With the code of MR 11/27/2001 the component of the failing Q-node is not found - // correctly. - // - - extend_embedding(greatest, em, mark, upward_begin); - - /* - // - // MR 11/27/2001: - // - // This is important! We restricted building the embedding to the nodes in - // the biconnected component which the Q-node fail refers to. But the st-number - // obtained for the whole graph restricted to these nodes will not be a st-numbering - // for this biconnected component. - // - - st_number::reverse_iterator st_it, st_end; - - for (st_it = st_.rbegin(), st_end = st_.rend(); - st_it != st_end; - ++st_it) - { - if (mark[*st_it] == 0) { - extend_embedding (*st_it, em, mark, upward_begin); - } - } - */ -#ifdef _DEBUG - GTL_debug::os() << "Embedding so far (st_numbered): " << std::endl; - em.write_st (GTL_debug::os(), st_); -#endif - - attachment_cycle (fail->n, em); - - if (!q_fail->pert_cons) { - - // - // the reduction failed because there was more than one block - // of pertinent children. The reduction in this case assures that - // pert_begin and pert_end lie in different blocks and that - // --pert_end is empty and lies between these two blocks. - // - // This is one of the two cases that may apply when the reduction - // fails already in bubble up. The reduction takes care of this. - // - - GTL_debug::debug_message ("CASE C (non consecutive pertinent children)\n"); - pq_tree::sons_iterator tmp = q_fail->pert_begin; - pq_leaf* leaves[3]; - node nodes[3]; - leaves[0] = search_full_leaf (*tmp); - nodes[0] = (*tmp)->up; - - tmp = q_fail->pert_end; - leaves[2] = search_full_leaf (*tmp); - nodes[2] = (*tmp)->up; - - --tmp; - while ((*tmp)->kind() == pq_node::DIR) { - --tmp; - } - - leaves[1] = search_empty_leaf (*tmp); - nodes[1] = (*tmp)->up; - - case_C (nodes, leaves, st_, to_father, G, q_fail); - - } else if (!(*(q_fail->pert_end))->is_endmost && !is_root) { - - GTL_debug::debug_message ("CASE D (non-root q-node with both endmost sons empty)\n"); - pq_tree::sons_iterator tmp = q_fail->sons.begin(); - pq_leaf* leaves[3]; - node nodes[3]; - leaves[0] = search_empty_leaf (*tmp); - nodes[0] = (*tmp)->up; - - tmp = --(q_fail->sons.end()); - leaves[2] = search_empty_leaf (*tmp); - nodes[2] = (*tmp)->up; - - tmp = q_fail->pert_begin; - leaves[1] = search_full_leaf (*tmp); - nodes[1] = (*tmp)->up; - - case_D (nodes, leaves, st_, to_father, G, q_fail); - - } else if (q_fail->partial_count == 1) { - if (q_fail->partial_pos[0] == q_fail->pert_end) { - GTL_debug::debug_message ("CASE D (non-root q-node with partial child at end of pertinent children\n"); - pq_tree::sons_iterator tmp = q_fail->sons.begin(); - pq_leaf* leaves[3]; - node nodes[3]; - leaves[0] = search_empty_leaf (*tmp); - nodes[0] = (*tmp)->up; - - tmp = q_fail->pert_end; - leaves[2] = search_empty_leaf (*tmp); - nodes[2] = (*tmp)->up; - - tmp = q_fail->pert_begin; - leaves[1] = search_full_leaf (*tmp); - nodes[1] = (*tmp)->up; - - case_D (nodes, leaves, st_, to_father, G, q_fail); - } else { - GTL_debug::debug_message ("CASE C (q-node with partial children surrounded by pertinent children)\n"); - pq_tree::sons_iterator tmp = q_fail->pert_begin; - pq_leaf* leaves[3]; - node nodes[3]; - leaves[0] = search_full_leaf (*tmp); - nodes[0] = (*tmp)->up; - - tmp = q_fail->pert_end; - leaves[2] = search_full_leaf (*tmp); - nodes[2] = (*tmp)->up; - - tmp = q_fail->partial_pos[0]; - leaves[1] = search_empty_leaf (*tmp); - nodes[1] = (*tmp)->up; - - - case_C (nodes, leaves, st_, to_father, G, q_fail); - } - - } else if ((q_fail->partial_pos[0] == q_fail->pert_begin || - q_fail->partial_pos[0] == q_fail->pert_end) && - (q_fail->partial_pos[1] == q_fail->pert_begin || - q_fail->partial_pos[1] == q_fail->pert_end)) { - - if (++(q_fail->sons.begin()) == --(q_fail->sons.end())) { - - // - // q_node with two children, which are both partial. - // - - pq_tree::sons_iterator tmp = q_fail->sons.begin(); - pq_leaf* leaves[4]; - node nodes[2]; - leaves[0] = search_empty_leaf (*tmp); - nodes[0] = (*tmp)->up; - leaves[1] = search_full_leaf (*tmp); - - ++tmp; - leaves[2] = search_empty_leaf (*tmp); - nodes[1] = (*tmp)->up; - leaves[3] = search_full_leaf (*tmp); - - case_E (nodes, leaves, st_, to_father, G, q_fail); - - } else if (q_fail->partial_count == 2) { - GTL_debug::debug_message ("CASE D (non-root q_node with first and last pertinent children partial)\n"); - - // - // sons.begin() is empty, pert_begin is partial, pert_end is partial - // - - pq_tree::sons_iterator tmp = q_fail->sons.begin(); - pq_leaf* leaves[3]; - node nodes[3]; - leaves[0] = search_empty_leaf (*tmp); - nodes[0] = (*tmp)->up; - - tmp = q_fail->pert_end; - leaves[2] = search_empty_leaf (*tmp); - nodes[2] = (*tmp)->up; - - tmp = q_fail->pert_begin; - - if (tmp == q_fail->sons.begin()) { - ++tmp; - } - - leaves[1] = search_full_leaf (*tmp); - nodes[1] = (*tmp)->up; - - case_D (nodes, leaves, st_, to_father, G, q_fail); - - } else { - GTL_debug::debug_message ("CASE C (q_node with at least three partial children)\n"); - - // - // There must be at least one other partial child among the pertinent. - // - - pq_tree::sons_iterator tmp = q_fail->pert_begin; - pq_leaf* leaves[3]; - node nodes[3]; - leaves[0] = search_full_leaf (*tmp); - nodes[0] = (*tmp)->up; - - tmp = q_fail->pert_end; - leaves[2] = search_full_leaf (*tmp); - nodes[2] = (*tmp)->up; - - tmp = q_fail->partial_pos[2]; - leaves[1] = search_empty_leaf (*tmp); - nodes[1] = (*tmp)->up; - - case_C (nodes, leaves, st_, to_father, G, q_fail); - } - - } else { - - // - // At least one partial son is in between the pertinent sons. - // - - GTL_debug::debug_message ("CASE C (q_node with at least two partial children, at least one surrounded by pertinent)\n"); - pq_tree::sons_iterator tmp = q_fail->pert_begin; - pq_leaf* leaves[3]; - node nodes[3]; - leaves[0] = search_full_leaf (*tmp); - nodes[0] = (*tmp)->up; - - tmp = q_fail->pert_end; - leaves[2] = search_full_leaf (*tmp); - nodes[2] = (*tmp)->up; - - tmp = q_fail->partial_pos[0]; - - if (tmp == q_fail->pert_begin || tmp == q_fail->pert_end) { - tmp = q_fail->partial_pos[1]; - } - - leaves[1] = search_empty_leaf (*tmp); - nodes[1] = (*tmp)->up; - - case_C (nodes, leaves, st_, to_father, G, q_fail); - } - - } else { - - // - // pert_root is a P-Node ==> at least two partial children. - // - - p_node* p_fail = fail->P(); - - if (p_fail->partial_count == 2) { - GTL_debug::debug_message ("CASE B (non-root p-node with two partial children)\n"); - case_B (p_fail, act, st_, to_father, G); - - } else { - - // - // We have at least three partial children - // - - GTL_debug::debug_message ("CASE A (p-node with at least three partial children)\n"); - case_A (p_fail, act, st_, to_father, G); - } - } -} - - - -void planarity::case_A (p_node* p_fail, - node act, - st_number& st_, - node_map to_father, - graph& G) -{ - node art = p_fail->n; - ob_nodes.push_back (art); - ob_nodes.push_back (act); - node_map mark (G, 0); - mark[art] = 1; - pq_leaf* empty[3]; - pq_tree::sons_iterator part_pos = p_fail->partial_sons.begin(); - int i; - - for (i = 0; i < 3; ++i) { - q_node* q_part = (*part_pos)->Q(); - empty[i] = run_through_partial (q_part, mark, to_father, art); - ++part_pos; - } - - node t_node = st_.s_node().opposite (st_.st_edge()); - mark[t_node] = 1; - node tmp[3]; - - for (i = 0; i < 3; ++i) { - tmp[i] = up_until_marked (empty[i]->n, mark, st_); - } - - assert (tmp[0] == t_node); - node tmp_node; - - // - // The three paths found meet at the nodes tmp[1] and tmp[2]; the one - // one with the higher st_number is the one we are looking for. Since the - // first path always ends at t and it may happen that the paths meet below - // t, we might have to delete some of the edges found in the first path. - // - - if (st_[tmp[1]] < st_[tmp[2]]) { - tmp_node = tmp[2]; - ob_nodes.push_back (tmp[1]); - } else { - tmp_node = tmp[1]; - ob_nodes.push_back (tmp[2]); - } - - - if (tmp_node != t_node) { - edges_t::iterator it, end; - int max_st = st_[tmp_node]; - - it = ob_edges.begin(); - end = ob_edges.end(); - - while (it != end) { - edge cur = *it; - - if (st_[cur.source()] > max_st || st_[cur.target()] > max_st) { - it = ob_edges.erase (it); - } else { - ++it; - } - } - } -} - - - -void planarity::case_B (p_node* p_fail, - node act, - st_number& st_, - node_map to_father, - graph& G) -{ - // - // P-Node, which is not the root of the pertinent subtree, but has - // two partial children. - // - - node art = p_fail->n; - ob_nodes.push_back (art); - ob_nodes.push_back (act); - node_map mark (G, 0); - node_map below (G, 0); - mark[art] = 1; - - // - // mark edges leading to full leaves from full sons. - // - - pq_tree::sons_iterator it, end; - for (it = p_fail->full_sons.begin(), end = p_fail->full_sons.end(); it != end; ++it) { - mark_all_neighbors_of_leaves (*it, below); - } - - // - // search paths from one full and one empty leaf to the articulation point - // in TBk. mark edges leading to full leaves from pertinent sons of part. - // - - pq_tree::sons_iterator part_pos = p_fail->partial_sons.begin(); - q_node* q_part = (*part_pos)->Q(); - pq_leaf* empty1 = run_through_partial (q_part, mark, to_father, art); - - for (it = q_part->pert_begin, end = ++(q_part->pert_end); it != end; ++it) { - mark_all_neighbors_of_leaves (*it, below); - } - - // - // search paths from one full and one empty leaf to the articulation point - // in TBk. mark edges leading to full leaves from pertinent sons of part. - // - - ++part_pos; - q_part = (*part_pos)->Q(); - pq_leaf* empty2 = run_through_partial (q_part, mark, to_father, art); - - - for (it = q_part->pert_begin, end = ++(q_part->pert_end); it != end; ++it) { - mark_all_neighbors_of_leaves (*it, below); - } - - // - // now that all the adjacent edges of act, that lead to art in TBk have been - // marked search an unmarked adj. edge of act, - // - - node::adj_edges_iterator a_it, a_end; - - for (a_it = act.adj_edges_begin(), a_end = act.adj_edges_end(); a_it != a_end; ++a_it) { - if (below[act.opposite (*a_it)] == 0 && st_[act.opposite (*a_it)] < st_[act]) { - break; - } - } - - assert (a_it != a_end); - mark[st_.s_node()] = 1; - mark[art] = 0; - node tmp = up_until_marked (art, mark, to_father); - assert (tmp == st_.s_node()); - tmp = up_until_marked (act.opposite (*a_it), mark, to_father); - assert(tmp != art); - ob_nodes.push_back (tmp); - ob_edges.push_back (*a_it); - ob_edges.push_back (st_.st_edge()); - - // - // search from empty1 and empty2 to t. - // - - node t_node = st_.s_node().opposite (st_.st_edge()); - mark[t_node] = 1; - tmp = up_until_marked (empty1->n, mark, st_); - assert (tmp == t_node); - tmp = up_until_marked (empty2->n, mark, st_); - ob_nodes.push_back (tmp); -} - - -void planarity::case_C (node* nodes, - pq_leaf** leaves, - st_number& st_, - node_map to_father, - graph& G, - q_node* q_fail) -{ - int i; - node_map mark (G, 0); - node y_0 = q_fail->n; - - for (i = 0; i < 3; ++i) { - mark[nodes[i]] = 1; - edge tmp_edge = leaves[i]->e; - node tmp_node = leaves[i]->n; - ob_edges.push_back (tmp_edge); - tmp_node = up_until_marked (tmp_node.opposite (tmp_edge), mark, to_father); - assert (tmp_node == nodes[i]); - ob_nodes.push_back (nodes[i]); - } - - ob_nodes.push_back (y_0); - mark[st_.s_node()] = 1; - node tmp = up_until_marked (y_0, mark, to_father); - assert (tmp == st_.s_node ()); - - ob_nodes.push_back (leaves[2]->n); - ob_edges.push_back (st_.st_edge()); - - node t_node = st_.s_node().opposite (st_.st_edge()); - mark[t_node] = 1; - tmp = up_until_marked (leaves[1]->n, mark, st_); - assert (tmp == t_node); - tmp = up_until_marked (leaves[2]->n, mark, st_); - ob_nodes.push_back (tmp); -} - - -void planarity::case_D (node* nodes, - pq_leaf** leaves, - st_number& st_, - node_map to_father, - graph& G, - q_node* q_fail) -{ - // - // Mark all edges from leaves leading to this component. - // - - node y_0 = q_fail->n; - pq_tree::sons_iterator it, end; - node_map below (G, 0); - node act = leaves[1]->n; - - for (it = q_fail->sons.begin(), end = q_fail->sons.end(); it != end; ++it) { - mark_all_neighbors_of_leaves (*it, below); - } - - node::adj_edges_iterator a_it, a_end; - - for (a_it = act.adj_edges_begin(), a_end = act.adj_edges_end(); a_it != a_end; ++a_it) { - if (below[act.opposite (*a_it)] == 0 && st_[act.opposite (*a_it)] < st_[act]) { - break; - } - } - - - // - // Since q_fail can't be the root of the pertinent subtree, there must - // be at least one edge from act not leading to the component described by - // q_fail. - // - - assert (a_it != a_end); - - int i; - node_map mark (G, 0); - - for (i = 0; i < 3; ++i) { - mark[nodes[i]] = 1; - edge tmp_edge = leaves[i]->e; - node tmp_node = leaves[i]->n; - ob_edges.push_back (tmp_edge); - tmp_node = up_until_marked (tmp_node.opposite (tmp_edge), mark, to_father); - assert (tmp_node == nodes[i]); - ob_nodes.push_back (nodes[i]); - } - - ob_nodes.push_back (y_0); - mark[st_.s_node()] = 1; - node tmp = up_until_marked (y_0, mark, to_father); - assert (tmp == st_.s_node ()); - ob_edges.push_back (*a_it); - tmp = up_until_marked (act.opposite (*a_it), mark, to_father); - - - // - // The paths from y_0 and from act meet in tmp. If tmp != s_node we have - // to delete some edges. - // - - if (tmp != st_.s_node()) { - edges_t::iterator it, end; - int min_st = st_[tmp]; - it = ob_edges.begin(); - end = ob_edges.end(); - - while (it != end) { - edge cur = *it; - - if (st_[cur.source()] < min_st || st_[cur.target()] < min_st) { - it = ob_edges.erase (it); - } else { - ++it; - } - } - } - - ob_nodes.push_back (act); - - node t_node = st_.s_node().opposite (st_.st_edge()); - mark[t_node] = 1; - node tmp_nodes[3]; - - for (i = 0; i < 3; ++i) { - tmp_nodes[i] = up_until_marked (leaves[i]->n, mark, st_); - } - - assert (tmp_nodes[0] == t_node); - - // - // The three paths found meet at the nodes tmp[1] and tmp[2]; the one - // one with the higher st_number is the one we are looking for. Since the - // first path always ends at t and it may happen that the paths meet below - // t, we might have to delete some of the edges found in the first path. - // - - if (st_[tmp_nodes[1]] < st_[tmp_nodes[2]]) { - tmp = tmp_nodes[2]; - ob_nodes.push_back (tmp_nodes[1]); - } else { - tmp = tmp_nodes[1]; - ob_nodes.push_back (tmp_nodes[2]); - } - - - if (tmp != t_node) { - edges_t::iterator it, end; - int max_st = st_[tmp]; - it = ob_edges.begin(); - end = ob_edges.end(); - - while (it != end) { - edge cur = *it; - - if (st_[cur.source()] > max_st || st_[cur.target()] > max_st) { - it = ob_edges.erase (it); - } else { - ++it; - } - } - } -} - - -void planarity::case_E (node* nodes, - pq_leaf** leaves, - st_number& st_, - node_map to_father, - graph& G, - q_node* q_fail) -{ - - // - // Mark all edges from the act node leading to this component. - // - - node y_0 = q_fail->n; - pq_tree::sons_iterator it, end; - node_map below (G, 0); - node act = leaves[1]->n; - - for (it = q_fail->pert_begin, end = ++(q_fail->pert_end); it != end; ++it) { - mark_all_neighbors_of_leaves (*it, below); - } - - node::adj_edges_iterator a_it, a_end; - - for (a_it = act.adj_edges_begin(), a_end = act.adj_edges_end(); a_it != a_end; ++a_it) { - if (below[act.opposite (*a_it)] == 0 && st_[act.opposite (*a_it)] < st_[act]) { - break; - } - } - - - // - // Since q_fail can't be the root of the pertinent subtree, there must - // be at least one edge from act not leading to the component described by - // q_fail. - // - - assert (a_it != a_end); - - // - // The list ob_edges at the moment contains the boundary. we need to know the paths - // from y_0 to nodes[0] ( = y_1), from nodes[0] to nodes[1] ( = y_2 ) and from nodes[1] - // back to y_0, because some of them will be eventually deleted later. - // - - edges_t::iterator paths_begin[3]; - edges_t::iterator l_it, l_end; - node next = y_0; - - for (l_it = ob_edges.begin(), l_end = ob_edges.end(); l_it != l_end; ++l_it) { - next = next.opposite (*l_it); - - if (next == nodes[1]) { - node tmp = nodes[1]; - nodes[1] = nodes[0]; - nodes[0] = tmp; - pq_leaf* tmp_leaf = leaves[2]; - leaves[2] = leaves[0]; - leaves[0] = tmp_leaf; - tmp_leaf = leaves[3]; - leaves[3] = leaves[1]; - leaves[1] = tmp_leaf; - - paths_begin[0] = l_it; - ++paths_begin[0]; - break; - } else if (next == nodes[0]) { - paths_begin[0] = l_it; - ++paths_begin[0]; - break; - } - } - - assert (l_it != l_end); - ++l_it; - assert (l_it != l_end); - - for (; l_it != l_end; ++l_it) { - next = next.opposite (*l_it); - - if (next == nodes[1]) { - paths_begin[1] = l_it; - ++paths_begin[1]; - break; - } - } - - assert (l_it != l_end); - - paths_begin[2] = --l_end; - - node y[3]; - int i; - node_map mark (G, 0); - edges_t from_act[3]; - edges_t::iterator pos; - - for (i = 0; i < 2; ++i) { - mark[nodes[i]] = 1; - edge tmp_edge = leaves[2 * i]->e; - node tmp_node = leaves[2 * i]->n; - ob_edges.push_back (tmp_edge); - tmp_node = up_until_marked (tmp_node.opposite (tmp_edge), mark, to_father); - assert (tmp_node == nodes[i]); - tmp_edge = leaves[2 * i + 1]->e; - tmp_node = leaves[2 * i + 1]->n; - pos = ob_edges.insert (ob_edges.end(), tmp_edge); - y[i + 1] = up_until_marked (tmp_node.opposite (tmp_edge), mark, to_father); - from_act[i + 1].splice (from_act[i + 1].begin(), ob_edges, pos, ob_edges.end()); - } - - mark[st_.s_node()] = 1; - node tmp = up_until_marked (y_0, mark, to_father); - assert (tmp == st_.s_node ()); - pos = ob_edges.insert (ob_edges.end(), *a_it); - y[0] = up_until_marked (act.opposite (*a_it), mark, to_father); - from_act[0].splice (from_act[0].begin(), ob_edges, pos, ob_edges.end()); - - node t_node = st_.s_node().opposite (st_.st_edge()); - mark[t_node] = 1; - node tmp_nodes[3]; - node_map from_where (G, 0); - - for (i = 0; i < 2; ++i) { - pos = --(ob_edges.end()); - tmp_nodes[i] = up_until_marked (leaves[2 * i]->n, mark, st_); - for (l_it = ++pos, l_end = ob_edges.end(); l_it != l_end; ++l_it) { - from_where[l_it->source()] = i + 1; - from_where[l_it->target()] = i + 1; - } - } - - assert (tmp_nodes[0] == t_node); - - if (y_0 != y[0]) { - ob_nodes.push_back (y_0); - ob_nodes.push_back (y[0]); - ob_nodes.push_back (y[1]); - ob_nodes.push_back (y[2]); - ob_nodes.push_back (act); - ob_nodes.push_back (tmp_nodes[1]); - - l_it = paths_begin[0]; - l_end = paths_begin[1]; - ob_edges.erase (l_it, l_end); - - for (i = 0; i < 3; ++i) { - ob_edges.splice (ob_edges.end(), from_act[i], from_act[i].begin(), from_act[i].end()); - } - - GTL_debug::debug_message ("CASE E(i)\n"); - - } else if (nodes[0] != y[1]) { - ob_nodes.push_back (y_0); - ob_nodes.push_back (y[1]); - ob_nodes.push_back (nodes[0]); - ob_nodes.push_back (y[2]); - ob_nodes.push_back (act); - ob_nodes.push_back (tmp_nodes[1]); - l_it = paths_begin[1]; - l_end = paths_begin[2]; - ++l_end; - ob_edges.erase (l_it, l_end); - - for (i = 0; i < 3; ++i) { - ob_edges.splice (ob_edges.end(), from_act[i], from_act[i].begin(), from_act[i].end()); - } - - GTL_debug::debug_message ("CASE E(ii)\n"); - - } else if (nodes[1] != y[2]) { - ob_nodes.push_back (y_0); - ob_nodes.push_back (y[1]); - ob_nodes.push_back (nodes[1]); - ob_nodes.push_back (y[2]); - ob_nodes.push_back (act); - ob_nodes.push_back (tmp_nodes[1]); - l_it = ob_edges.begin(); - l_end = paths_begin[0]; - ob_edges.erase (l_it, l_end); - - for (i = 0; i < 3; ++i) { - ob_edges.splice (ob_edges.end(), from_act[i], from_act[i].begin(), from_act[i].end()); - } - - GTL_debug::debug_message ("CASE E(ii)\n"); - - } else { - tmp_nodes[2] = up_until_marked (leaves[1]->n, mark, st_); - ob_nodes.push_back (y_0); - ob_nodes.push_back (y[1]); - ob_nodes.push_back (tmp_nodes[1]); - ob_nodes.push_back (y[2]); - ob_nodes.push_back (act); - - if (st_[tmp_nodes[1]] < st_[tmp_nodes[2]]) { - ob_nodes.push_back (tmp_nodes[2]); - l_it = paths_begin[0]; - l_end = paths_begin[1]; - ob_edges.erase (l_it, l_end); - - for (i = 1; i < 3; ++i) { - ob_edges.splice (ob_edges.end(), from_act[i], from_act[i].begin(), from_act[i].end()); - } - - GTL_debug::debug_message ("CASE E(iii) (1)\n"); - - } else if (st_[tmp_nodes[1]] > st_[tmp_nodes[2]]) { - edge last_edge = *(--(ob_edges.end())); - ob_nodes.push_back (tmp_nodes[2]); - ob_edges.splice (ob_edges.end(), from_act[0], from_act[0].begin(), from_act[0].end()); - int from; - - if (from_where[last_edge.source()] > 0) { - from = from_where[last_edge.source()]; - } else { - from = from_where[last_edge.target()]; - } - - assert (from > 0); - - if (from == 1) { - ob_edges.splice (ob_edges.end(), from_act[2], from_act[2].begin(), from_act[2].end()); - - l_it = paths_begin[1]; - l_end = paths_begin[2]; - ++l_end; - ob_edges.erase (l_it, l_end); - - } else { - ob_edges.splice (ob_edges.end(), from_act[1], from_act[1].begin(), from_act[1].end()); - - l_it = ob_edges.begin(); - l_end = paths_begin[0]; - ob_edges.erase (l_it, l_end); - } - - GTL_debug::debug_message ("CASE E(iii) (2)\n"); - - } else { - for (i = 0; i < 3; ++i) { - ob_edges.splice (ob_edges.end(), from_act[i], from_act[i].begin(), from_act[i].end()); - } - - GTL_debug::debug_message ("CASE E(iii) (3)\n"); - } - } - - ob_edges.push_back (st_.st_edge()); -} - - -pq_leaf* planarity::search_full_leaf (pq_node* n) -{ - switch (n->kind()) { - case pq_node::LEAF: - return n->L(); - - case pq_node::P_NODE: - case pq_node::Q_NODE: - return search_full_leaf (*(--(n->sons.end()))); - - default: - assert (false); - return 0; - } -} - - -pq_leaf* planarity::search_empty_leaf (pq_node* n) -{ - switch (n->kind()) { - case pq_node::LEAF: - return n->L(); - - case pq_node::Q_NODE: - case pq_node::P_NODE: - return search_empty_leaf (*(n->sons.begin())); - - default: - assert (false); - return 0; - } -} - - - -void planarity::mark_all_neighbors_of_leaves (pq_node* act, node_map& mark) -{ - if (act->kind() == pq_node::LEAF) { - mark[((pq_leaf*)act)->e.opposite(((pq_leaf*)act)->n)] = 1; - } else { - pq_tree::sons_iterator it, end; - - for (it = act->sons.begin(), end = act->sons.end(); it != end; ++it) { - mark_all_neighbors_of_leaves (*it, mark); - } - } -} - - -pq_leaf* planarity::run_through_partial (q_node* part, node_map& mark, node_map& to_father, node v) -{ - pq_leaf* tmp = search_full_leaf (part); - edge tmp_edge = tmp->e; - node tmp_node = tmp->n; - ob_edges.push_back (tmp_edge); - tmp_node = up_until_marked (tmp_node.opposite (tmp_edge), mark, to_father); - - tmp = search_empty_leaf (part); - tmp_node = tmp->n; - tmp_edge = tmp->e; - ob_edges.push_back (tmp_edge); - tmp_node = up_until_marked (tmp_node.opposite (tmp_edge), mark, to_father); - assert (tmp_node != v); - ob_nodes.push_back (tmp_node); - - return tmp->L(); -} - - -node planarity::up_until_marked (node act, node_map& mark, node_map& to_father) -{ - while (mark[act] == 0) { - mark[act] = 1; - edge next = to_father[act]; - ob_edges.push_back (next); - act = act.opposite (next); - } - - return act; -} - -node planarity::up_until_marked (node act, node_map& mark, st_number& st_) -{ - while (mark[act] == 0) { - mark[act] = 1; - node opp; - node::adj_edges_iterator it, end; - - for (it = act.adj_edges_begin(), end = act.adj_edges_end(); it != end; ++it) { - opp = act.opposite (*it); - if (st_[opp] > st_[act]) { - break; - } - } - - assert (it != end); - ob_edges.push_back (*it); - act = opp; - } - - return act; -} - -void planarity::attachment_cycle (node start, planar_embedding& em) -{ - edge act = em.adjacency(start).front(); - node next = start.opposite (act); - ob_edges.push_back (act); - - while (next != start) { - act = em.cyclic_next (next, act); - next = next.opposite (act); - ob_edges.push_back (act); - } -} - - -void planarity::dfs_bushform (node n, - node_map& used, - st_number& st_, - int stop, - node_map& to_father) -{ - used[n] = 1; - node::adj_edges_iterator it, end; - - for (it = n.adj_edges_begin(), end = n.adj_edges_end(); it != end; ++it) { - edge act = *it; - node other = n.opposite(act); - - if (used[other] == 0 && st_[other] < stop) { - to_father[other] = *it; - dfs_bushform (other, used, st_, stop, to_father); - } - } -} - -#ifdef _DEBUG - -void planarity::write_node(std::ostream& os, int id, int label, int mark) { - os << "node [\n" << "id " << id << std::endl; - os << "label \"" << label << "\"\n"; - os << "graphics [\n" << "x 100\n" << "y 100 \n"; - if (mark == 1) { - os << "outline \"#ff0000\"\n"; - } - os << "]\n"; - os << "]" << std::endl; -} -#endif - -#ifdef _DEBUG -void planarity::write_bushform(graph& G, st_number& st_, int k, const char* name, const node_map& mark, - const node_map& to_father) -{ - // create the bushform Bk for the k where the test failed - st_number::iterator st_it, st_end; - int id = 0; - node_map ids (G); - std::ofstream os(name, std::ios::out | std::ios::trunc); - - os << "graph [\n" << "directed 1" << std::endl; - - for (st_it = st_.begin(), st_end = st_.end(); st_it != st_end && st_[*st_it] <= k; ++st_it) { - write_node(os, id, st_[*st_it], mark[*st_it]); - ids[*st_it] = id; - id++; - } - - for (st_it = st_.begin(), st_end = st_.end(); st_it != st_end && st_[*st_it] <= k; ++st_it) { - node::adj_edges_iterator ait, aend; - - for (ait = st_it->adj_edges_begin(), aend = st_it->adj_edges_end(); ait != aend; ait++) { - node other = ait->opposite(*st_it); - int other_id; - if (st_[*st_it] < st_[other]) { - if(st_[other] > k) { - write_node(os, id, st_[other], mark[other]); - other_id = id; - id++; - } else { - other_id = ids[other]; - } - - os << "edge [\n" << "source " << ids[*st_it] << "\ntarget " << other_id << std::endl; - if (*ait == to_father[*st_it] || *ait == to_father[other]) { - os << "graphics [\n" << "fill \"#0000ff\"" << std::endl; - os << "width 4.0\n]" << std::endl; - } - os << "\n]" << std::endl; - } - } - } - - os << "]" << std::endl; -} - -#endif - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/pq_node.cpp b/Tracker/graph/GTL/src/pq_node.cpp deleted file mode 100644 index 43fe8762c..000000000 --- a/Tracker/graph/GTL/src/pq_node.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// pq_node.cpp -// -//========================================================================== -// $Id: pq_node.cpp,v 1.12 2002/10/04 08:07:36 chris Exp $ - -#include - -__GTL_BEGIN_NAMESPACE - -pq_node::~pq_node () -{ - while (!sons.empty()) { - pq_node* tmp = sons.front(); - sons.erase (sons.begin()); - delete tmp; - } -} - - -//-------------------------------------------------------------------------- -// P-NODE -//-------------------------------------------------------------------------- - -p_node::p_node (node n_, int id_) : pq_node (n_, id_), partial_count (0), full_count (0) -{ -} - -p_node::p_node (node n_, int id_, symlist& s) : - pq_node (n_, id_), child_count (0), partial_count (0), full_count (0) -{ - sons.splice (sons.end(), s.begin(), s.end()); - - iterator it = sons.begin(); - iterator end = sons.end(); - - for (; it != end; ++it) { - ++child_count; - (*it)->is_endmost = true; - (*it)->father = this; - } -} - -void p_node::clear () -{ - pq_node::clear(); - partial_count = full_count = 0; - if (!full_sons.empty()) - sons.splice (sons.end(), full_sons.begin(), full_sons.end()); - - if (!partial_sons.empty()) - sons.splice (sons.end(), partial_sons.begin(), partial_sons.end()); -} - -inline void p_node::partial (iterator it) -{ - ++partial_count; - pert_leaves += (*it)->pert_leaves; - partial_sons.splice (partial_sons.end(), it); -} - -inline void p_node::full (iterator it) -{ - ++full_count; - pert_leaves += (*it)->pert_leaves; - full_sons.splice (full_sons.end(), it); -} - - -inline void p_node::write(std::ostream& os, int _id) -{ - os << "node [\n" << "id " << _id << std::endl; - os << "label \"" << id << "\nP" << "\"\n"; - os << "graphics [\n" << "x 100\n" << "y 100\n"; - if (mark == UNBLOCKED) { - os << "outline \"#0000ff\"\n"; - } else if (mark == BLOCKED) { - os << "outline \"#ff0000\"\n"; - } - os << "type \"oval\"\n" << "]" << std::endl; - os << "LabelGraphics [\n"; - os << "type \"text\"\n]\n]" << std::endl; -} - -//-------------------------------------------------------------------------- -// Q-NODE -//-------------------------------------------------------------------------- - -q_node::q_node (node n_, int id_) : pq_node (n_, id_), partial_count (0), full_count (0) -{ -} - -inline void q_node::partial (iterator it) -{ - if (partial_count < 3) { - partial_pos[partial_count] = it; - } - - pert_leaves += (*it)->pert_leaves; - ++partial_count; - - if (pert_begin == iterator()) { - pertinent (it); - } -} - - -inline void q_node::full (iterator it) -{ - ++full_count; - pert_leaves += (*it)->pert_leaves; - - - if (pert_begin == iterator()) { - pertinent (it); - } -} - - -void q_node::pertinent (iterator it) -{ - iterator end = sons.end(); - iterator tmp = it; - pq_node* first; - pq_node* last; - pert_end = it; - ++tmp; - int pert_block_count = 1; - - while (tmp != end) { - if ((*tmp)->mark != UNBLOCKED) { - break; - } - - if ((*tmp)->kind () != DIR) { - ++pert_block_count; - pert_end = tmp; - } - - ++tmp; - } - - last = *pert_end; - - pert_begin = tmp = it; - --tmp; - - while (tmp != end) { - if ((*tmp)->mark != UNBLOCKED) { - break; - } - - if ((*tmp)->kind () != DIR) { - ++pert_block_count; - pert_begin = tmp; - } - - --tmp; - } - - first = *pert_begin; - pert_cons = (pert_block_count == pert_children); - - // - // it must be true, that either first or last is in - // {sons.front(), sons.back()} (or both). Thus we are able to achive - // the following normalization: pert_end is *always* sons.last() and - // pert_begin is some other child, such that ++pert_begin leads towards - // pert_end. - // - - if (pert_cons) { - if (last == sons.front()) { - turn(); - } else if (last != sons.back()) { - tmp = pert_begin; - pert_begin = pert_end; - pert_end = tmp; - pert_end.reverse(); - pert_begin.reverse(); - - if (first == sons.front()) { - turn(); - } else if (first != sons.back()) { - - // - // This should not happen. In this case the pertinent children are - // BLOCKED and thus this method wouldt be called. - // - // 17.3. Now this can happen. - // - - // pert_cons = false; - - // assert (false); - } - } - - } else { - - // - // In case that there are more than one block of pertinent children although - // bubble up didnt fail (e.g. pp...pe...ep...pp or ee...ep...pe...ep...pp) - // we need some element of the second block in order to find K5 or K33. So we - // leave pert_begin as it is, but assign pert_end to something in the second - // block - // - - tmp = pert_begin; - --tmp; - - while (tmp != sons.end()) { - if ((*tmp)->mark == UNBLOCKED && (*tmp)->kind () != DIR) { - break; - } - - --tmp; - } - - - // - // We need an empty child. So we always keep the invariant that --pert_end - // leads to an empty child. Please note that --pert_end might be a DI. - // - - tmp.reverse(); - - if (tmp == sons.end()) { - tmp = pert_end; - ++tmp; - - while (tmp != sons.end()) { - if ((*tmp)->mark == UNBLOCKED && (*tmp)->kind () != DIR) { - break; - } - - ++tmp; - } - - assert (tmp != sons.end()); - } - - pert_end = tmp; - } - - // - // In the case that there is in fact only one pertinent child we so far - // only assured that it is the last child, but it is still possible - // that pert_begin (and pert_end, too) is headed the wrong way. - // - - if (pert_begin == pert_end && pert_cons && pert_end == --(sons.end())) { - pert_begin = pert_end = --(sons.end()); - } -} - -inline void q_node::clear () -{ - pq_node::clear(); - partial_count = full_count = 0; - pert_begin = symlist::iterator(); - pert_end = symlist::iterator(); -} - -inline void q_node::write(std::ostream& os, int _id) -{ - os << "node [\n" << "id " << _id << std::endl; - os << "label \"" << id << "\n" << "Q" << "\"\n"; - os << "graphics [\n" << "x 100\n" << "y 100 \n"; - if (mark == UNBLOCKED) { - os << "outline \"#0000ff\"\n"; - } else if (mark == BLOCKED) { - os << "outline \"#ff0000\"\n"; - } - os << "]\n"; - os << "LabelGraphics [\n"; - os << "type \"text\"\n]\n]" << std::endl; -} - -q_node* q_node::merge (iterator it) -{ - assert ((*it)->kind() == pq_node::Q_NODE); - q_node* part = (q_node*) *it; - - if (part == sons.front()) { - part->sons.front()->father = this; - part->sons.back()->is_endmost = false; - } else if (part == sons.back()){ - part->sons.back()->father = this; - part->sons.front()->is_endmost = false; - } else { - part->sons.front()->is_endmost = false; - part->sons.back()->is_endmost = false; - } - - sons.splice (it, part->sons.begin(), part->sons.end()); - sons.erase (it); - - return part; -} - - -void q_node::turn () -{ - sons.reverse(); -} - - -//-------------------------------------------------------------------------- -// LEAF -//-------------------------------------------------------------------------- - - -pq_leaf::pq_leaf (int id_, int other_, edge e_, node n_) : pq_node (n_, id_) -{ - up_id = other_; - up = n_.opposite (e_); - other_id = other_; - e = e_; -} - -inline void pq_leaf::write(std::ostream& os, int _id) -{ - os << "node [\n" << "id " << _id << std::endl; - os << "label \"" << other_id << "\n" << id << "\"\n"; - os << "graphics [\n" << "x 100\n" << "y 100 \n"; - if (mark == UNBLOCKED) { - os << "outline \"#0000ff\"\n"; - } else if (mark == BLOCKED) { - os << "outline \"#ff0000\"\n"; - } - os << "]\n"; - os << "LabelGraphics [\n"; - os << "type \"text\"\n]\n]" << std::endl; -} - - -void direction_indicator::write(std::ostream& os, int _id) -{ - os << "node [\n" << "id " << _id << std::endl; - os << "label \"DIR\n" << id << "\"\n"; - os << "graphics [\n" << "x 100\n" << "y 100 \n"; - if (mark == UNBLOCKED) { - os << "outline \"#0000ff\"\n"; - } else if (mark == BLOCKED) { - os << "outline \"#ff0000\"\n"; - } - os << "]\n"; - os << "LabelGraphics [\n"; - os << "type \"text\"\n]\n]" << std::endl; -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/pq_tree.cpp b/Tracker/graph/GTL/src/pq_tree.cpp deleted file mode 100644 index ad58013f4..000000000 --- a/Tracker/graph/GTL/src/pq_tree.cpp +++ /dev/null @@ -1,1574 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// pq_tree.cpp -// -//========================================================================== -// $Id: pq_tree.cpp,v 1.22 2008/02/03 18:12:07 chris Exp $ - -#include -#include - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - -pq_tree::pq_tree (int id, node n, const std::list& li) -{ -#ifdef _DEBUG - GTL_debug::init_debug(); -#endif - std::list::const_iterator it; - std::list::const_iterator end = li.end(); - sons_list sons; - pq_leaf* tmp; - - for (it = li.begin(); it != end; ++it) { - tmp = *it; - tmp->pos = sons.insert (sons.end(), tmp); - } - - root = new p_node(n, id, sons); - pert_root = 0; - fail = 0; - pseudo = 0; -} - -pq_tree::~pq_tree () -{ -#ifdef _DEBUG - GTL_debug::close_debug(); -#endif - reset (); - - if (root) { - delete root; - } -} - - -bool pq_tree::bubble_up (std::list& leaves) -{ - std::queue qu; - int block_count = 0; - int blocked_siblings = 0; - pert_leaves_count = 0; - int off_the_top = 0; - pq_node* tmp; - - assert (clear_me.empty()); - - std::list::const_iterator it = leaves.begin(); - std::list::const_iterator lend = leaves.end(); - - while (it != lend) { - qu.push (*it); - (*it)->lpos = clear_me.insert (clear_me.end(), *it); - pert_leaves_count++; - ++it; - } - - sons_iterator next, prev, end; - pq_node* father; - int size = pert_leaves_count; - - while (size + block_count + off_the_top > 1) { - if (size == 0) { - return false; - } - - tmp = qu.front(); - qu.pop(); - size--; - tmp->pert_leaves = 0; - - if (tmp == root) { - off_the_top = 1; - tmp->mark = pq_node::UNBLOCKED; - continue; - } - - tmp->mark = pq_node::BLOCKED; - - if (tmp->is_endmost) { - father = tmp->father; - tmp->mark = pq_node::UNBLOCKED; - end = father->sons.end(); - - if (father->kind() == pq_node::Q_NODE) { - blocked_siblings = 0; - next = tmp->pos; - prev = tmp->pos; - ++next; - --prev; - - if (next != end) { - if ((*next)->mark == pq_node::BLOCKED) { - ++blocked_siblings; - } - } else if (prev != end) { - if ((*prev)->mark == pq_node::BLOCKED) { - ++blocked_siblings; - } - } - } - - } else { - next = tmp->pos; - prev = tmp->pos; - ++next; - --prev; - blocked_siblings = 0; - - if ((*prev)->mark == pq_node::UNBLOCKED) { - tmp->mark = pq_node::UNBLOCKED; - tmp->father = (*prev)->father; - father = tmp->father; - end = father->sons.end(); - } else if ((*prev)->mark == pq_node::BLOCKED) { - blocked_siblings++; - } - - if ((*next)->mark == pq_node::UNBLOCKED) { - tmp->mark = pq_node::UNBLOCKED; - tmp->father = (*next)->father; - father = tmp->father; - end = father->sons.end(); - } else if ((*next)->mark == pq_node::BLOCKED) { - blocked_siblings++; - } - } - - if (tmp->mark == pq_node::UNBLOCKED) { - ++(father->pert_children); - - if (father->mark == pq_node::UNMARKED) { - qu.push (father); - father->lpos = clear_me.insert (clear_me.end(), father); - size++; - father->mark = pq_node::QUEUED; - } - - if (father->kind() == pq_node::Q_NODE) { - pq_node* tmp; - - while (next != end) { - tmp = *next; - if (tmp->mark == pq_node::BLOCKED) { - tmp->father = father; - tmp->mark = pq_node::UNBLOCKED; - - if (tmp->kind () != pq_node::DIR) { - ++(father->pert_children); - } - } else if (tmp->kind () == pq_node::DIR && - tmp->mark == pq_node::UNMARKED) { - tmp->lpos = clear_me.insert (clear_me.end(), tmp); - tmp->father = father; - tmp->mark = pq_node::UNBLOCKED; - } else { - break; - } - - ++next; - } - - while (prev != end) { - tmp = *prev; - if (tmp->mark == pq_node::BLOCKED) { - tmp->father = father; - tmp->mark = pq_node::UNBLOCKED; - - if (tmp->kind () != pq_node::DIR) { - ++(father->pert_children); - } - } else if (tmp->kind () == pq_node::DIR && - tmp->mark == pq_node::UNMARKED) { - tmp->lpos = clear_me.insert (clear_me.end(), tmp); - tmp->father = father; - tmp->mark = pq_node::UNBLOCKED; - } else { - break; - } - - --prev; - } - - block_count -= blocked_siblings; - } - - } else { - - // - // tmp is BLOCKED - // - - while ((*next)->kind() == pq_node::DIR && - (*next)->mark == pq_node::UNMARKED) { - (*next)->mark = pq_node::BLOCKED; - (*next)->lpos = clear_me.insert (clear_me.end(), *next); - ++next; - } - - while ((*prev)->kind() == pq_node::DIR && - (*prev)->mark == pq_node::UNMARKED) { - (*prev)->mark = pq_node::BLOCKED; - (*prev)->lpos = clear_me.insert (clear_me.end(), *prev); - --prev; - } - - block_count += 1 - blocked_siblings; - } - } - - return true; -} - - -pq_node* pq_tree::where_bubble_up_failed (std::list& leaves) -{ - - // - // Search the first leaf that leads to an interior block. - // - - pq_leaf* le; - pq_node* blocked = 0; - - std::list::iterator l_it = leaves.begin(); - std::list::iterator l_end = leaves.end(); - q_node* father = 0; - - while (l_it != l_end ) { - le = *l_it; - blocked = leads_to_blocked (le); - - if (blocked != 0) { - // - // Search the father of this block. - // - - sons_iterator it = blocked->pos; - while (!(*it)->is_endmost) { - ++it; - } - - father = (*it)->father->Q(); - - // - // give all the children the right father. - // - - it = father->sons.begin(); - sons_iterator end = father->sons.end(); - - while (it != end) { - if ((*it)->mark == pq_node::BLOCKED) { - (*it)->father = father; - (*it)->mark = pq_node::UNBLOCKED; - if ((*it)->kind() != pq_node::DIR) { - ++(father->pert_children); - } - } - - ++it; - } - - // - // We have to assure that there isn't any other interior block in the - // subtree of father. - // - - pq_node* another = blocked_in_subtree (father); - - if (another == 0) { - break; - } - } - - ++l_it; - } - - assert (father != 0); - - // - // delete all pertinent leaves that do not lead to father - // - - l_it = leaves.begin(); - - while (l_it != l_end ) { - le = *l_it; - - if (!leads_to (le, father)) { - l_it = leaves.erase (l_it); - } else { - ++l_it; - } - } - - return father; -} - - -pq_node* pq_tree::blocked_in_subtree (pq_node* n) -{ - if (n->kind() == pq_node::LEAF) { - return 0; - } - - if (n->mark == pq_node::BLOCKED) { - return n; - } - - sons_iterator it = n->sons.begin(); - sons_iterator end = n->sons.end(); - - while (it != end) { - pq_node* bl = blocked_in_subtree (*it); - - if (bl) { - return bl; - } - - ++it; - } - - return 0; -} - - -bool pq_tree::leads_to (pq_node* le, pq_node* other) -{ - if (le == root) { - return false; - } else if (le->mark == pq_node::BLOCKED) { - return false; - } else if (le->mark == pq_node::UNMARKED) { - return false; - } else if (le->father == other) { - return true; - } else { - return leads_to (le->father, other); - } -} - -pq_node* pq_tree::leads_to_blocked (pq_node* le) -{ - if (le == root) { - return 0; - } else if (le->mark == pq_node::BLOCKED) { - return le; - } else if (le->mark == pq_node::UNMARKED) { - return 0; - } else { - return leads_to_blocked (le->father); - } -} - - -bool pq_tree::reduce (std::list& leaves) -{ - - GTL_debug::debug_message ("REDUCING %d\n", leaves.front()->id); - fail = 0; - - if (!bubble_up (leaves)) { - - // - // Find the node that has an interior block. - // - - GTL_debug::debug_message ("Bubble-Up failed !!\n"); - fail = where_bubble_up_failed (leaves); - } - - std::queue qu; - pq_leaf* le; - std::list::iterator l_it = leaves.begin(); - std::list::iterator l_end = leaves.end(); - - while (l_it != l_end ) { - le = *l_it; - qu.push (le); - le->pert_leaves = 1; - ++l_it; - } - - pq_node* tmp; - - while (!qu.empty()) { - tmp = qu.front(); - qu.pop(); - clear_me.erase (tmp->lpos); - - if (tmp->mark == pq_node::BLOCKED) { - pseudo = new q_node (node(), 0); - sons_iterator past = tmp->pos; - - // - // Get maximal connected block of BLOCKED siblings right of tmp - // - - while ((*past)->mark == pq_node::BLOCKED) { - (*past)->mark = pq_node::UNBLOCKED; - (*past)->father = pseudo; - - if ((*past)->kind() != pq_node::DIR) { - pseudo->pert_children++; - } - - ++past; - } - - - // - // Delete surrounding direction indicators - // - - --past; - - while ((*past)->kind() == pq_node::DIR) { - (*past)->clear(); - clear_me.erase ((*past)->lpos); - --past; - } - - pseudo->pert_end = past; - - // - // Get maximal connected block of BLOCKED siblings left of tmp - // - - sons_iterator first = tmp->pos; - --first; - - while ((*first)->mark == pq_node::BLOCKED) { - (*first)->mark = pq_node::UNBLOCKED; - (*first)->father = pseudo; - - if ((*first)->kind() != pq_node::DIR) { - pseudo->pert_children++; - } - - --first; - } - - - // - // Delete surrounding direction indicators - // - - ++first; - - while ((*first)->kind() == pq_node::DIR) { - (*first)->clear(); - clear_me.erase ((*first)->lpos); - ++first; - } - - pseudo->pert_begin = first; - - GTL_debug::debug_message ("creating pseudo-node as root\n"); - pseudo->mark = pq_node::UNBLOCKED; - ++past; - pseudo->sons.attach_sublist (first, past); - pseudo->pert_cons = true; - pseudo->lpos = clear_me.insert (clear_me.end(), pseudo); - } - - if (tmp->pert_leaves == pert_leaves_count) { - - // - // tmp is the root of the pertinent subtree - // - - if (tmp->kind() == pq_node::LEAF) { - pert_root = tmp; - GTL_debug::debug_message ("full leaf is root\n"); - } else if (tmp->kind() == pq_node::P_NODE) { - if (P1 (tmp->P(), true)) { - GTL_debug::debug_message ("P1 matched for root\n"); - } else if (P2 (tmp->P())) { - GTL_debug::debug_message ("P2 matched for root\n"); - } else if (P4 (tmp->P())) { - GTL_debug::debug_message ("P4 matched for root\n"); - } else if (P6 (tmp->P())) { - GTL_debug::debug_message ("P6 matched for root\n"); - } else { - GTL_debug::debug_message ("NO MATCHING FOR P-ROOT\n"); - fail = tmp; - failed_at_root = true; - return false; - } - } else { - if (!tmp->Q()->pert_cons) { - GTL_debug::debug_message ("pertinent children not consecutive\n"); - fail = tmp; - failed_at_root = true; - return false; - } else if (Q1 (tmp->Q(), true)) { - GTL_debug::debug_message ("Q1 matched for root\n"); - } else if (Q2 (tmp->Q(), true)) { - GTL_debug::debug_message ("Q2 matched for root\n"); - } else if (Q3 (tmp->Q())) { - GTL_debug::debug_message ("Q3 matched for root\n"); - } else { - GTL_debug::debug_message ("NO MATCHING FOR Q-ROOT\n"); - - if (tmp == pseudo) { - - // - // search the real father - // - - sons_iterator it = pseudo->sons.begin(); - pseudo->sons.front()->is_endmost = false; - pseudo->sons.back()->is_endmost = false; - pseudo->sons.detach_sublist(); - assert (pseudo->sons.empty()); - - while (!(*it)->is_endmost) { - --it; - } - - tmp = (*it)->father; - q_node* q_tmp = tmp->Q(); - q_tmp->pert_begin = pseudo->pert_begin; - q_tmp->pert_end = pseudo->pert_end; - q_tmp->partial_count = pseudo->partial_count; - q_tmp->full_count = pseudo->full_count; - q_tmp->pert_cons = pseudo->pert_cons; - - for (int i = 0; i < q_tmp->partial_count; ++i) { - q_tmp->partial_pos[i] = pseudo->partial_pos[i]; - } - - delete pseudo; - pseudo = 0; - } - - fail = tmp; - failed_at_root = true; - return false; - } - } - - } else { - - // - // tmp is not the root of the pertinent subtree. - // - - if (tmp == pseudo || tmp == root) { - - // - // This should not happen when bubble_up was true. - // - - assert (false); - - } else { - pq_node* father = tmp->father; - - if (tmp->kind() == pq_node::LEAF) { - father->full (tmp->pos); - tmp->clear(); - GTL_debug::debug_message ("full leaf processed\n"); - - } else if (tmp->kind() == pq_node::P_NODE) { - if (P1 (tmp->P(), false)) { - GTL_debug::debug_message ("P1 matched for non-root\n"); - } else if (P3 (tmp->P())) { - GTL_debug::debug_message ("P3 matched for non-root\n"); - } else if (P5 (tmp->P())) { - GTL_debug::debug_message ("P5 matched for non-root\n"); - } else { - GTL_debug::debug_message ("NO MATCHING FOR P-NON-ROOT\n"); - fail = tmp; - failed_at_root = false; - return false; - } - - } else { - if (!tmp->Q()->pert_cons) { - GTL_debug::debug_message ("pertinent children not consecutive\n"); - fail = tmp; - return false; - } else if (Q1 (tmp->Q(), false)) { - GTL_debug::debug_message ("Q1 matched for non-root\n"); - } else if (Q2 (tmp->Q(), false)) { - GTL_debug::debug_message ("Q2 matched for non-root\n"); - } else { - GTL_debug::debug_message ("NO MATCHING FOR Q-NON-ROOT\n"); - fail = tmp; - failed_at_root = false; - return false; - } - } - - - // - // If all the other pertinent siblings of tmp have already been - // matched father of tmp is queued. - // - - --(father->pert_children); - - if (father->pert_children == 0) { - if (father == fail) { - failed_at_root = false; - return false; - } else { - qu.push (father); - } - } - } - } - } - - return true; -} - - -void pq_tree::reset () -{ - pq_node* tmp; - - while (!clear_me.empty()) { - tmp = clear_me.front(); - GTL_debug::debug_message ("Clearing %d\n", tmp->id); - clear_me.pop_front(); - tmp->clear(); - tmp->pert_children = 0; - } - - if (pert_root) { - pert_root->clear(); - pert_root = 0; - } - - if (pseudo) { - pseudo->sons.front()->is_endmost = false; - pseudo->sons.back()->is_endmost = false; - pseudo->sons.detach_sublist(); - assert (pseudo->sons.empty()); - delete pseudo; - pseudo = 0; - } - - if (fail) { - fail->clear(); - fail = 0; - } -} - - -void pq_tree::dfs (pq_node* act, planar_embedding& em, - std::list& dirs) -{ - if (act->kind() == pq_node::LEAF) { - em.push_back (act->n, ((pq_leaf*) act)->e); - return; - } - - sons_iterator it = act->sons.begin(); - sons_iterator end = act->sons.end(); - - while (it != end) { - if ((*it)->kind() == pq_node::DIR) { - direction_indicator* dir = (*it)->D(); - if (dir->mark != pq_node::UNMARKED) { - clear_me.erase (dir->lpos); - } - sons_iterator tmp = it; - - if (++tmp == ++(dir->pos)) { - dir->direction = true; - } else { - dir->direction = false; - } - - dirs.push_back (*dir); - - } else { - dfs (*it, em, dirs); - } - - ++it; - } -} - - -void pq_tree::replace_pert (int id, node _n, const std::list& li, - planar_embedding* em, std::list* dirs) -{ - assert (pert_root); - assert (!li.empty()); - pq_leaf* tmp = 0; - std::list::const_iterator it; - std::list::const_iterator end = li.end(); - sons_list sons; - int size = 0; - - for (it = li.begin(); it != end; ++it) { - tmp = *it; - tmp->pos = sons.insert (sons.end(), tmp); - ++size; - } - - pq_node* ins; - - if (size == 1) { - sons.erase (tmp->pos); - ins = tmp; - } else { - ins = new p_node(_n, id, sons); - } - - if (pert_root->kind() == pq_node::Q_NODE) { - q_node* q_root = pert_root->Q(); - sons_iterator it = q_root->pert_begin; - sons_iterator end = q_root->pert_end; - sons_iterator tmp = it; - sons_iterator sons_end = q_root->sons.end(); - --tmp; - - while (tmp != sons_end) { - if ((*tmp)->kind() != pq_node::DIR) { - break; - } - - --tmp; - } - - it = ++tmp; - - tmp = end; - ++tmp; - - while (tmp != sons_end) { - if ((*tmp)->kind() != pq_node::DIR) { - break; - } - - ++tmp; - } - - end = --tmp; - - ins->is_endmost = (*end)->is_endmost; - ++end; - - while (it != end) { - if (em && dirs) { - if ((*it)->kind() == pq_node::DIR) { - direction_indicator* dir = (*it)->D(); - clear_me.erase (dir->lpos); - sons_iterator tmp = it; - - if (++tmp == ++(dir->pos)) { - dir->direction = true; - } else { - dir->direction = false; - } - - dirs->push_back (*dir); - } else { - dfs (*it, *em, *dirs); - } - } - - delete *it; - it = pert_root->sons.erase (it); - } - - if (pert_root->sons.empty() && pert_root != pseudo) { - ins->pos = pert_root->pos; - ins->father = pert_root->father; - ins->is_endmost = pert_root->is_endmost; - ins->up = pert_root->up; - ins->up_id = pert_root->up_id; - - if (root == pert_root) { - root = ins; - } else { - *(pert_root->pos) = ins; - } - - delete pert_root; - pert_root = 0; - - } else { - if (em && dirs) { - direction_indicator* ind = new direction_indicator (_n, id); - ind->is_endmost = false; - ind->pos = pert_root->sons.insert (end, ind); - } - - ins->pos = pert_root->sons.insert (end, ins); - ins->father = pert_root; - ins->up = _n; - ins->up_id = id; - } - - } else { - if (em && dirs) { - dfs (pert_root, *em, *dirs); - } - - ins->is_endmost = pert_root->is_endmost; - ins->father = pert_root->father; - ins->pos = pert_root->pos; - ins->up = pert_root->up; - ins->up_id = pert_root->up_id; - - if (root == pert_root) { - root = ins; - } else { - *(pert_root->pos) = ins; - } - - delete pert_root; - pert_root = 0; - } -} - -void pq_tree::get_frontier (planar_embedding& em, - std::list& dirs) -{ - dfs (root, em, dirs); -} - -//------------------------------------------------------------------------ P1 -// Requirements: -// -// * x is a P-node having only full children -// * wheter x is the root or not is specified by the second parameter -// - -bool pq_tree::P1 (p_node* x, bool is_root) -{ - if (x->child_count == x->full_count) { - if (!is_root) { - x->father->full (x->pos); - } else { - pert_root = x; - } - - x->sons.splice (x->sons.end(), x->full_sons.begin(), - x->full_sons.end()); - x->clear(); - return true; - } - - return false; -} - - -//----------------------------------------------------------------------- P2 -// Requirements: -// -// * x is a P-node having both full and empty children -// * x has no partial children -// * x is the root of the pertinent subtree -// ==> more than one pertinent child -// * P1 didn't match -// ==> at least one non-full child -// -bool pq_tree::P2 (p_node* x) -{ - if (x->partial_count != 0) { - return false; - } - - p_node* ins = new p_node(x->n, x->id, x->full_sons); - ins->father = x; - ins->is_endmost = true; - ins->pos = x->sons.insert (x->sons.end(), ins); - x->child_count -= (x->full_count - 1); - x->clear(); - pert_root = ins; - return true; -} - - -//------------------------------------------------------------------------ P3 -// Requirements: -// -// * x is a P-node having both full and empty children. -// * x isn't the root of the pertinent subtree. -// * P1 didn't match. -// ==> at least one non-full child. -// * x has no partial children -// - -bool pq_tree::P3 (p_node* x) -{ - if (x->partial_count != 0) { - return false; - } - - q_node* new_q = new q_node (x->n, x->id); - pq_node* father = x->father; - pq_node* ins; - - *(x->pos) = new_q; - new_q->pos = x->pos; - new_q->up = x->up; - new_q->up_id = x->up_id; - new_q->is_endmost = x->is_endmost; - new_q->father = father; - new_q->pert_leaves = x->pert_leaves; - - if (x->full_count > 1) { - ins = new p_node (x->n, x->id, x->full_sons); - } else { - ins = x->full_sons.front(); - x->full_sons.erase (x->full_sons.begin()); - assert (x->full_sons.empty()); - } - - ins->up = x->n; - ins->up_id = x->id; - ins->is_endmost = true; - ins->father = new_q; - ins->pos = new_q->sons.insert (new_q->sons.end(), ins); - new_q->pert_cons = true; - new_q->pert_begin = ins->pos; - new_q->pert_end = ins->pos; - - if (x->child_count - x->full_count > 1) { - ins = x; - ins->up = x->n; - ins->up_id = x->id; - x->child_count -= x->full_count; - x->clear(); - } else { - ins = x->sons.front(); - ins->up = x->n; - ins->up_id = x->id; - x->sons.erase (x->sons.begin()); - assert (x->sons.empty()); - delete x; - } - - ins->is_endmost = true; - ins->father = new_q; - ins->pos = new_q->sons.insert (new_q->pert_begin, ins); - father->partial (new_q->pos); - - return true; -} - -//------------------------------------------------------------------------ P4 -// Requirements: -// -// * x is a P-node and the root of the pertinent subtree. -// ==> more than one non-empty child, i.e. at least one full child. -// * x has excactly one partial child -// * P1 and P2 didn't match -// ==> at least one partial child -// -bool pq_tree::P4 (p_node* x) -{ - if (x->partial_count > 1) { - return false; - } - - q_node* part = x->partial_sons.front()->Q(); - part->n = x->n; - part->id = x->id; - pq_node* ins; - - if (x->full_count > 1) { - ins = new p_node (x->n, x->id, x->full_sons); - } else { - ins = x->full_sons.front(); - x->full_sons.erase (x->full_sons.begin()); - assert (x->full_sons.empty()); - } - - part->sons.back()->is_endmost = false; - ins->is_endmost = true; - - ins->up = x->n; - ins->up_id = x->id; - ins->father = part; - ins->pos = part->sons.insert (part->sons.end(), ins); - part->pert_end = ins->pos; - x->child_count -= x->full_count; - - if (x->child_count == 1) { - if (root == x) { - root = part; - } else { - *(x->pos) = part; - } - - part->pos = x->pos; - part->is_endmost = x->is_endmost; - part->father = x->father; - part->up = x->up; - part->up_id = x->up_id; - x->partial_sons.erase (x->partial_sons.begin()); - - delete x; - } else { - x->sons.splice (x->sons.end(), part->pos); - x->clear(); - } - - pert_root = part; - return true; -} - - -//------------------------------------------------------------------------ P5 -// Requirements: -// -// * x is a P-node and not the root of the pertinent subtree -// * x has exactly one partial child. -// * P1 and P3 didn't match -// ==> at least one partial child -// -bool pq_tree::P5 (p_node* x) -{ - if (x->partial_count > 1) { - return false; - } - - pq_node* father = x->father; - q_node* part = x->partial_sons.front()->Q(); - part->n = x->n; - part->id = x->id; - part->up = x->up; - part->up_id = x->up_id; - - x->partial_sons.erase (x->partial_sons.begin()); - part->is_endmost = x->is_endmost; - part->father = father; - *(x->pos) = part; - part->pos = x->pos; - part->pert_leaves = x->pert_leaves; - pq_node* ins; - - if (x->full_count > 1) { - ins = new p_node (x->n, x->id, x->full_sons); - } else if (x->full_count == 1) { - ins = x->full_sons.front(); - x->full_sons.erase (x->full_sons.begin()); - assert (x->full_sons.empty()); - } else { - ins = 0; - } - - if (ins) { - ins->up = x->n; - ins->up_id = x->id; - part->sons.back()->is_endmost = false; - ins->is_endmost = true; - ins->father = part; - ins->pos = part->sons.insert (part->sons.end(), ins); - part->pert_end = ins->pos; - } - - x->child_count -= (x->full_count + 1); - - if (x->child_count > 1) { - ins = x; - ins->up = x->n; - ins->up_id = x->id; - x->clear(); - } else if (x->child_count == 1) { - ins = x->sons.front(); - ins->up = x->n; - ins->up_id = x->id; - x->sons.erase (x->sons.begin()); - delete x; - } else { - ins = 0; - delete x; - } - - if (ins) { - part->sons.front()->is_endmost = false; - ins->is_endmost = true; - ins->father = part; - ins->pos = part->sons.insert (part->sons.begin(), ins); - } - - father->partial (part->pos); - return true; -} - - -//------------------------------------------------------------------------ P6 -// Requirements: -// -// * x is the root of the pertinent subtree and has two partial children. -// * P1, P2 and P4 didn't match -// ==> at least two partial children. -// -bool pq_tree::P6 (p_node* x) -{ - if (x->partial_count > 2) { - return false; - } - - - q_node* part2 = x->partial_sons.front()->Q(); - x->partial_sons.erase (x->partial_sons.begin()); - q_node* part1 = x->partial_sons.front()->Q(); - part1->n = x->n; - part1->id = x->id; - pq_node* ins; - - if (x->full_count > 1) { - ins = new p_node (x->n, x->id, x->full_sons); - } else if (x->full_count == 1) { - ins = x->full_sons.front(); - x->full_sons.erase (x->full_sons.begin()); - assert (x->full_sons.empty()); - } else { - ins = 0; - } - - part1->sons.back()->is_endmost = false; - - if (ins) { - ins->up = x->n; - ins->up_id = x->id; - ins->is_endmost = false; - ins->pos = part1->sons.insert (part1->sons.end(), ins); - } - - part2->turn (); - part2->sons.front()->is_endmost = false; - part2->sons.back()->father = part1; - part1->sons.splice (part1->sons.end(), part2->sons.begin(), - part2->sons.end()); - part1->pert_end = part2->pert_begin; - part1->pert_end.reverse(); - x->child_count -= (x->full_count + 1); - delete part2; - - if (x->child_count == 1) { - if (root == x) { - root = part1; - } else { - *(x->pos) = part1; - } - part1->pos = x->pos; - part1->is_endmost = x->is_endmost; - part1->father = x->father; - part1->up = x->up; - part1->up_id = x->up_id; - x->partial_sons.erase (x->partial_sons.begin()); - - delete x; - } else { - x->sons.splice (x->sons.end(), x->partial_sons.begin()); - x->clear(); - } - - pert_root = part1; - return true; -} - -//------------------------------------------------------------------------ Q1 -// Requirements: -// -// * x is a Q-node having only full children -// * wheter x is the root or not is specified by the second parameter -// -bool pq_tree::Q1 (q_node* x, bool is_root) -{ - if (x->partial_count > 0) return false; - - if (*(x->pert_begin) == x->sons.front() - && *(x->pert_end) == x->sons.back()) { - - if (!is_root) { - x->father->full (x->pos); - } else { - pert_root = x; - } - - return true; - } - - return false; -} - - -//------------------------------------------------------------------------ Q2 -// Requirements: -// -// * Q1 didn't match ==> x has at least one non-full child. -// * wheter x is the root or not is specified by the second parameter -// * x has at most one partial child -// * If x has empty children, the partial child must be at pert_begin; -// if x hasn't any empty children the partial child is allowed to be at -// pert_end, since this can be transformed into the former case. -// -bool pq_tree::Q2 (q_node* x, bool is_root) -{ - if (x->partial_count > 1) { - return false; - } - - if (x->partial_count == 1) { - if (x->partial_pos[0] == x->pert_end && - x->pert_begin == x->sons.begin() && - x->pert_begin != x->pert_end) - { - if (!is_root) { - q_node* part = (*(x->pert_end))->Q(); - x->turn(); - sons_iterator tmp = x->pert_begin; - x->pert_begin = x->pert_end; - x->pert_end = tmp; - x->pert_begin.reverse(); - x->pert_end.reverse(); - x->merge (x->pert_begin); - x->pert_begin = part->pert_begin; - delete part; - } else { - q_node* part = (*(x->pert_end))->Q(); - part->turn(); - x->merge (x->pert_end); - x->pert_end = x->pert_begin; - x->pert_begin = part->pert_begin; - x->pert_end.reverse(); - // x->pert_begin.reverse(); - delete part; - } - - } else if (x->partial_pos[0] != x->pert_begin) { - return false; - } else { - // - // Partial child is at pert_begin and x has at least one - // empty child (i.e. pert_begin != sons.begin()) - // - - q_node* part = x->merge (x->pert_begin); - - if (x->pert_begin == x->pert_end) { - x->pert_end = part->pert_end; - } - - x->pert_begin = part->pert_begin; - delete part; - } - } - - if (!is_root) { - x->father->partial (x->pos); - } else { - pert_root = x; - } - - return true; -} - - -//------------------------------------------------------------------------ Q3 -// Requirements: -// -// * x is the root of the pertinent subtree. -// * Q1 and Q2 didn't match -// ==> at least one partial child -// * if there is only one partial child it must be at pert_end, and x must -// have at least one empty and one full child. -// if there are two partial children they must be at pert_begin and -// pert_end. -// -bool pq_tree::Q3 (q_node* x) -{ - if (x->partial_count > 2 || x->partial_count < 1) return false; - - if (x->partial_count == 1) { - if (x->partial_pos[0] != x->pert_end) return false; - - // - // One partial child at pert_end. - // - - } else { - if (x->partial_pos[0] != x->pert_end) { - if (x->partial_pos[1] != x->pert_end || - x->partial_pos[0] != x->pert_begin) return false; - } else { - if (x->partial_pos[1] != x->pert_begin) return false; - } - - // - // One partial child at pert_begin and one at pert_end - // - } - - q_node* part = (*(x->pert_end))->Q(); - part->turn(); - x->merge (x->pert_end); - x->pert_end = part->pert_begin; - x->pert_end.reverse(); - delete part; - - if (x->partial_count == 2) { - part = x->merge (x->pert_begin); - x->pert_begin = part->pert_begin; - delete part; - } - - pert_root = x; - return true; -} - - - - -GTL_EXTERN std::ostream& operator<< (std::ostream& os, const pq_tree& tree) -{ - if (!tree.root) return os; - - int id = 0; - std::pair tmp; - std::queue > qu; - pq_node* act; - - os << "graph [\n" << "directed 1" << std::endl; - tree.root->write (os, id); - tmp.first = tree.root; - tmp.second = id; - ++id; - qu.push (tmp); - - while (!qu.empty()) { - tmp = qu.front(); - qu.pop(); - - if (tmp.first->kind() == pq_node::Q_NODE || tmp.first->kind() == pq_node::P_NODE) { - pq_tree::sons_iterator it = tmp.first->sons.begin(); - pq_tree::sons_iterator end = tmp.first->sons.end(); - - for (; it != end; ++it) { - act = *it; - act->write (os, id); - - os << "edge [\n" << "source " << tmp.second << std::endl; - os << "target " << id << "\n]" << std::endl; - - qu.push(std::pair(act, id)); - ++id; - } - } - - if (tmp.first->kind() == pq_node::P_NODE) { - p_node* P = tmp.first->P(); - pq_tree::sons_iterator it = P->full_sons.begin(); - pq_tree::sons_iterator end = P->full_sons.end(); - - for (; it != end; ++it) { - act = *it; - act->write (os, id); - - os << "edge [\n" << "source " << tmp.second << std::endl; - os << "target " << id << "\n]" << std::endl; - - qu.push(std::pair(act, id)); - ++id; - } - - it = P->partial_sons.begin(); - end = P->partial_sons.end(); - - for (; it != end; ++it) { - act = *it; - act->write (os, id); - - os << "edge [\n" << "source " << tmp.second << std::endl; - os << "target " << id << "\n]" << std::endl; - - qu.push(std::pair(act, id)); - ++id; - } - } - } - - os << "]" << std::endl; - - return os; -} - -pq_tree::sons_iterator -pq_tree::remove_dir_ind(q_node* q_fail, sons_iterator s_it) -{ - direction_indicator* dir = (*s_it)->D(); - sons_iterator res = q_fail->sons.erase(s_it); - clear_me.erase(dir->lpos); - delete dir; - return res; -} - - -//-------------------------------------------------------------------------- -// DEBUGGING -//-------------------------------------------------------------------------- - -bool pq_tree::integrity_check () const -{ - if (!root) return true; - - std::queue qu; - qu.push (root); - pq_node* tmp; - - while (!qu.empty()) { - tmp = qu.front(); - qu.pop(); - - if (tmp->kind() == pq_node::LEAF) continue; - if (tmp->kind() == pq_node::DIR) continue; - - sons_iterator it = tmp->sons.begin(); - sons_iterator end = tmp->sons.end(); - int count = 0; - int endmost_count = 0; - - for (; it != end; ++it) { - ++count; - if ((*it)->is_endmost) { - ++endmost_count; - - if ((*it)->father != tmp) { - GTL_debug::debug_message ("Wrong father !!!\n"); - GTL_debug::close_debug(); - return false; - } - } - - if ((*it)->pos != it) { - GTL_debug::debug_message ("Wrong position !!\n"); - GTL_debug::close_debug(); - return false; - } - - qu.push (*it); - } - - if (tmp->kind() == pq_node::P_NODE - && count != (tmp->P()->child_count)) { - GTL_debug::debug_message ("Wrong number of children !!!\n"); - GTL_debug::close_debug(); - return false; - } - - if (tmp->kind() == pq_node::Q_NODE && count < 2) { - GTL_debug::debug_message ("Q-Node with too few children !!\n"); - GTL_debug::close_debug(); - return false; - } - - if (tmp->kind() == pq_node::P_NODE && count < 2) { - GTL_debug::debug_message ("P-Node with too few children !!\n"); - GTL_debug::close_debug(); - return false; - } - - if (tmp->kind() == pq_node::Q_NODE) { - if (endmost_count == 2) { - if (!(tmp->sons.front()->is_endmost && - tmp->sons.back()->is_endmost)) { - GTL_debug::debug_message ("Q-node with inner children labeled endmost\n"); - GTL_debug::close_debug(); - return false; - } - } else { - GTL_debug::debug_message ("Q-node with too many or too few endmost children\n"); - GTL_debug::close_debug(); - return false; - } - } - } - - return true; -} - -/* -void pq_tree::insert (pq_node* father, pq_node* ins) { - ins->father = father; - ins->is_endmost = true; - - if (father->kind() == pq_node::Q_NODE) { - father->sons.back()->is_endmost = false; - } else { - ((p_node*)father)->child_count++; - } - - ins->pos = father->sons.insert (father->sons.end(), ins); -} - - -p_node* pq_tree::insert_P (pq_node* father, sons_list& sons) -{ - p_node* p = new p_node(); - insert (father, p); - pq_node* tmp; - - sons_iterator it = sons.begin(); - sons_iterator end = sons.end(); - - for (; it != end; ++it) { - p->child_count++; - tmp = *it; - tmp->father = p; - tmp->is_endmost = true; - tmp->pos = p->sons.insert (p->sons.end(), tmp); - - if (tmp->kind() == pq_node::LEAF) { - leaves.push_back ((pq_leaf*)tmp); - } - } - - return p; -} - - -q_node* pq_tree::insert_Q (pq_node* father, sons_list& sons) -{ - q_node* q = new q_node(); - insert (father, q); - pq_node* tmp; - sons_iterator it = sons.begin(); - sons_iterator end = sons.end(); - - for (; it != end; ++it) { - tmp = *it; - tmp->is_endmost = false; - tmp->pos = q->sons.insert (q->sons.end(), tmp); - - if (tmp->kind() == pq_node::LEAF) { - leaves.push_back (tmp->L()); - } - } - - q->sons.front()->father = q; - q->sons.front()->is_endmost = true; - q->sons.back()->father = q; - q->sons.back()->is_endmost = true; - - return q; -} - -*/ - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/ratio_cut_partition.cpp b/Tracker/graph/GTL/src/ratio_cut_partition.cpp deleted file mode 100644 index a95346f48..000000000 --- a/Tracker/graph/GTL/src/ratio_cut_partition.cpp +++ /dev/null @@ -1,1580 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// ratio_cut_partition.cpp -// -//========================================================================== -// $Id: ratio_cut_partition.cpp,v 1.9 2001/11/07 13:58:11 pick Exp $ - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -__GTL_BEGIN_NAMESPACE - - -const ratio_cut_partition::side_type ratio_cut_partition::A = 0; -const ratio_cut_partition::side_type ratio_cut_partition::B = 1; - - -const ratio_cut_partition::fix_type ratio_cut_partition::FIXA = 0; -const ratio_cut_partition::fix_type ratio_cut_partition::FIXB = 1; -const ratio_cut_partition::fix_type ratio_cut_partition::UNFIXED = 2; - - -ratio_cut_partition::ratio_cut_partition() -{ - set_vars_executed = false; - enable_cut_edges_storing = false; - enable_nodesAB_storing = false; -} - - -ratio_cut_partition::~ratio_cut_partition() -{ -} - - -void ratio_cut_partition::set_vars(const graph& G, - const node_map& node_weight, const edge_map& edge_weight) -{ - this->node_weight = node_weight; - this->edge_weight = edge_weight; - set_vars_executed = true; - provided_st = false; - provided_fix = false; - this->fixed.init(G, UNFIXED); - provided_initial_part = false; - side.init(G); -} - - -void ratio_cut_partition::set_vars(const graph& G, - const node_map& node_weight, const edge_map& edge_weight, - const node source_node, const node target_node) -{ - this->node_weight = node_weight; - this->edge_weight = edge_weight; - this->source_node = source_node; - this->target_node = target_node; - set_vars_executed = true; - provided_st = true; - provided_fix = false; - this->fixed.init(G, UNFIXED); - provided_initial_part = false; - side.init(G); -} - - -void ratio_cut_partition::set_vars(const graph& G, - const node_map& node_weight, const edge_map& edge_weight, - const node source_node, const node target_node, - const node_map& init_side) -{ - this->node_weight = node_weight; - this->edge_weight = edge_weight; - this->source_node = source_node; - this->target_node = target_node; - this->side = init_side; - set_vars_executed = true; - provided_st = true; - provided_fix = false; - this->fixed.init(G, UNFIXED); - provided_initial_part = true; -} - - -void ratio_cut_partition::set_vars(const graph& G, - const node_map& node_weight, const edge_map& edge_weight, - const node source_node, const node target_node, - const node_map& fixed) -{ - this->node_weight = node_weight; - this->edge_weight = edge_weight; - this->source_node = source_node; - this->target_node = target_node; - this->fixed = fixed; - set_vars_executed = true; - provided_st = true; - provided_fix = true; - provided_initial_part = false; - side.init(G); -} - - -void ratio_cut_partition::set_vars(const graph& /*G*/, - const node_map& node_weight, const edge_map& edge_weight, - const node source_node, const node target_node, - const node_map& init_side, const node_map& fixed) -{ - this->node_weight = node_weight; - this->edge_weight = edge_weight; - this->source_node = source_node; - this->target_node = target_node; - this->side = init_side; - this->fixed = fixed; - set_vars_executed = true; - provided_st = true; - provided_fix = true; - provided_initial_part = true; -} - - -void ratio_cut_partition::store_cut_edges(const bool set) -{ - enable_cut_edges_storing = set; -} - - -void ratio_cut_partition::store_nodesAB(const bool set) -{ - enable_nodesAB_storing = set; -} - - -int ratio_cut_partition::check(graph& G) -{ - if ((!set_vars_executed) || (!G.is_undirected())) - { - return GTL_ERROR; - } - - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - if (edge_weight[*edge_it] < 0) - { - return GTL_ERROR; - } - ++edge_it; - } - int real_node_weights = 0; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (node_weight[*node_it] > 0) - { - ++real_node_weights; - } - if (node_weight[*node_it] < 0) - { - return GTL_ERROR; - } - ++node_it; - } - if ((G.number_of_nodes() >= 2) && (real_node_weights < 2)) - { - return GTL_ERROR; - } - - if ((provided_st) && (source_node == target_node) && - (G.number_of_nodes() > 1)) - { - return GTL_ERROR; - } - - if ((provided_initial_part) && ((side[source_node] != A) || - (side[target_node] != B))) - { - return GTL_ERROR; - } - - if ((provided_fix) && ((fixed[source_node] == FIXB) || - (fixed[target_node] == FIXA))) - { - return GTL_ERROR; - } - - if ((provided_st) && (node_weight[source_node] == 0 || - node_weight[target_node] == 0)) - { - return GTL_ERROR; - } - - return GTL_OK; -} - - -int ratio_cut_partition::run(graph& G) -{ - cur_cutsize = 0; - cur_cutratio = 0.0; - if (G.number_of_nodes() == 0) - { - return GTL_OK; // nothing to do - } - if (G.number_of_nodes() == 1) - { - side[*G.nodes_begin()] = A; - return GTL_OK; - } - - edges_t artificial_edges; - if (!G.is_connected()) - { - make_connected(G, artificial_edges); - } - - if (provided_fix) - { - divide_up(G); - } - - if (!provided_st) - { - determine_source_node(G); - compute_target_node(G); - } - - if (provided_initial_part) - { - init_variables(G); - init_data_structure(G); - direction = LEFT_SHIFT; - clean_step(G); - } - else - { - initialization(G); - } - iterative_shifting(G); - group_swapping(G); - - if (enable_cut_edges_storing) - { - compute_cut_edges(G); - } - if (enable_nodesAB_storing) - { - compute_nodesAB(G); - } - restore(G, artificial_edges); - - return GTL_OK; -} - - -int ratio_cut_partition::get_cutsize() -{ - return cur_cutsize; -} - - -double ratio_cut_partition::get_cutratio() -{ - return cur_cutratio; -} - - -ratio_cut_partition::side_type -ratio_cut_partition::get_side_of_node(const node& n) const -{ - return side[n]; -} - - -ratio_cut_partition::side_type ratio_cut_partition::operator [] -(const node& n) const -{ - return side[n]; -} - - -int ratio_cut_partition::get_weight_on_sideA(const graph& G) const -{ - int nwA = 0; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (side[*node_it] == A) - { - nwA += node_weight[*node_it]; - } - ++node_it; - } - return nwA; -} - - -int ratio_cut_partition::get_weight_on_sideB(const graph& G) const -{ - int nwB = 0; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (side[*node_it] == B) - { - nwB += node_weight[*node_it]; - } - ++node_it; - } - return nwB; -} - - -ratio_cut_partition::cut_edges_iterator -ratio_cut_partition::cut_edges_begin() const -{ - return cut_edges.begin(); -} - - -ratio_cut_partition::cut_edges_iterator -ratio_cut_partition::cut_edges_end() const -{ - return cut_edges.end(); -} - - -ratio_cut_partition::nodes_of_one_side_iterator -ratio_cut_partition::nodes_of_sideA_begin() const -{ - return nodesA.begin(); -} - - -ratio_cut_partition::nodes_of_one_side_iterator -ratio_cut_partition::nodes_of_sideA_end() const -{ - return nodesA.end(); -} - - -ratio_cut_partition::nodes_of_one_side_iterator -ratio_cut_partition::nodes_of_sideB_begin() const -{ - return nodesB.begin(); -} - - -ratio_cut_partition::nodes_of_one_side_iterator -ratio_cut_partition::nodes_of_sideB_end() const -{ - return nodesB.end(); -} - - -void ratio_cut_partition::reset() -{ - set_vars_executed = false; - cut_edges.clear(); - nodesA.clear(); - nodesB.clear(); -} - - -void ratio_cut_partition::divide_up(const graph& G) -{ - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (fixed[*node_it] == FIXA) - { - side[*node_it] = A; - } - else if (fixed[*node_it] == FIXB) - { - side[*node_it] = B; - } - ++node_it; - } -} - - -void ratio_cut_partition::make_connected(graph& G, - edges_t& artificial_edges) -{ - dfs conn; - conn.scan_whole_graph(true); - conn.check(G); - conn.run(G); - - // connect dfs roots with zero edges - dfs::roots_iterator root_it = conn.roots_begin(); - dfs::roots_iterator rootes_end = conn.roots_end(); - while (root_it != rootes_end) - { - node edge_start = **root_it; - ++root_it; - if (root_it != rootes_end) - { - edge ne = G.new_edge(edge_start, **root_it); - edge_weight[ne] = 0; // this edge has no cut costs - artificial_edges.push_back(ne); - } - } -} - - -void ratio_cut_partition::restore(graph& G, edges_t& artificial_edges) -{ - edges_t::iterator edge_it = artificial_edges.begin(); - edges_t::iterator edges_end = artificial_edges.end(); - while (edge_it != edges_end) - { - G.del_edge(*edge_it); - ++edge_it; - } -} - - -void ratio_cut_partition::initialization(const graph& G) -{ - int cutsize_A2B, cutsize_B2A; - double cutratio_A2B, cutratio_B2A; - node_map side_B2A(G); - - init_variables(G); - - // start with moves from B to A - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (fixed[*node_it] == UNFIXED) - { - side[*node_it] = B; - } - ++node_it; - } - side[source_node] = A; - side[target_node] = B; - init_data_structure(G); - if (fixed[target_node] == UNFIXED) - { - bucketB[range_up(gain_value[target_node])]. - erase(position_in_bucket[target_node]); - update_max_gain(B); - } - left_shift_op(G); - clean_step(G); - cutsize_B2A = cur_cutsize; - cutratio_B2A = cur_cutratio; - copy_side_node_map(G, side_B2A, side); - - // continue with moves from A to B - node_it = G.nodes_begin(); - while (node_it != nodes_end) - { - if (fixed[*node_it] == UNFIXED) - { - side[*node_it] = A; - } - ++node_it; - } - side[source_node] = A; - side[target_node] = B; - init_data_structure(G); - if (fixed[source_node] == UNFIXED) - { - bucketA[range_up(gain_value[source_node])]. - erase(position_in_bucket[source_node]); - update_max_gain(A); - } - right_shift_op(G); - clean_step(G); - cutsize_A2B = cur_cutsize; - cutratio_A2B = cur_cutratio; - - if (cutratio_B2A < cutratio_A2B) - { - copy_side_node_map(G, side, side_B2A); - cur_cutsize = cutsize_B2A; - cur_cutratio = cutratio_B2A; - direction = LEFT_SHIFT; - } - else - { - // copy_side_node_map(...) not necessary - cur_cutsize = cutsize_A2B; - cur_cutratio = cutratio_A2B; - direction = RIGHT_SHIFT; - } -} - - -void ratio_cut_partition::init_data_structure(const graph& G) -{ - aside.init(G); - bside.init(G); - unlockedA.init(G); - unlockedB.init(G); - cur_cutsize = 0; - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - if ((side[edge_it->source()] == A) && - (side[edge_it->target()] == A)) - { - aside[*edge_it] = 2; - bside[*edge_it] = 0; - unlockedA[*edge_it].push_back(edge_it->source()); - unlockedA[*edge_it].push_back(edge_it->target()); - } - else if ((side[edge_it->source()] == B) && - (side[edge_it->target()] == B)) - { - aside[*edge_it] = 0; - bside[*edge_it] = 2; - unlockedB[*edge_it].push_back(edge_it->source()); - unlockedB[*edge_it].push_back(edge_it->target()); - } - else if ((side[edge_it->source()] == A) && - (side[edge_it->target()] == B)) - { - aside[*edge_it] = 1; - bside[*edge_it] = 1; - cur_cutsize += edge_weight[*edge_it]; - unlockedA[*edge_it].push_back(edge_it->source()); - unlockedB[*edge_it].push_back(edge_it->target()); - } - else if ((side[edge_it->source()] == B) && - (side[edge_it->target()] == A)) - { - aside[*edge_it] = 1; - bside[*edge_it] = 1; - cur_cutsize += edge_weight[*edge_it]; - unlockedA[*edge_it].push_back(edge_it->target()); - unlockedB[*edge_it].push_back(edge_it->source()); - } - ++edge_it; - } - - bucketA.resize(2 * max_vertex_degree * max_edge_weight + 1); - bucketB.resize(2 * max_vertex_degree * max_edge_weight + 1); - - init_filling_buckets(G); - cur_cutratio = cutratio(); -} - - -void ratio_cut_partition::init_filling_buckets(const graph &G) -{ - node_weight_on_sideA = 0; - node_weight_on_sideB = 0; - nodes_on_sideA = 0; - nodes_on_sideB = 0; - bucketA_empty = true; - bucketB_empty = true; - bool first_A_node = true; - bool first_B_node = true; - int index; - // position_in_bucket.init(G); - gain_value.init(G); - - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (side[*node_it] == A) - { - node_weight_on_sideA += node_weight[*node_it]; - ++nodes_on_sideA; - gain_value[*node_it] = inital_gain_of_node_on_sideA(*node_it); - if (fixed[*node_it] == UNFIXED) - { - if (first_A_node) - { - bucketA_empty = false; - max_gainA = gain_value[*node_it]; - first_A_node = false; - } - else - { - if (max_gainA < gain_value[*node_it]) - { - max_gainA = gain_value[*node_it]; - } - } - index = range_up(gain_value[*node_it]); - position_in_bucket[*node_it] = bucketA[index].insert( - bucketA[index].begin(), *node_it); - } - } - else // side[*node_it] == B - { - node_weight_on_sideB += node_weight[*node_it]; - ++nodes_on_sideB; - gain_value[*node_it] = inital_gain_of_node_on_sideB(*node_it); - if (fixed[*node_it] == UNFIXED) - { - if (first_B_node) - { - bucketB_empty = false; - max_gainB = gain_value[*node_it]; - first_B_node = false; - } - else - { - if (max_gainB < gain_value[*node_it]) - { - max_gainB = gain_value[*node_it]; - } - } - index = range_up(gain_value[*node_it]); - position_in_bucket[*node_it] = bucketB[index].insert( - bucketB[index].begin(), *node_it); - } - } - ++node_it; - } -} - - -int ratio_cut_partition::inital_gain_of_node_on_sideA(const node cur_node) -{ - int node_gain = 0; - node::adj_edges_iterator adj_edge_it = cur_node.adj_edges_begin(); - node::adj_edges_iterator adj_edges_end = cur_node.adj_edges_end(); - while (adj_edge_it != adj_edges_end) - { - if (aside[*adj_edge_it] == 1) - { - node_gain += edge_weight[*adj_edge_it]; - } - if (bside[*adj_edge_it] == 0) - { - node_gain -= edge_weight[*adj_edge_it]; - } - ++adj_edge_it; - } - return node_gain; -} - - -int ratio_cut_partition::inital_gain_of_node_on_sideB(const node cur_node) -{ - int node_gain = 0; - node::adj_edges_iterator adj_edge_it = cur_node.adj_edges_begin(); - node::adj_edges_iterator adj_edges_end = cur_node.adj_edges_end(); - while (adj_edge_it != adj_edges_end) - { - if (bside[*adj_edge_it] == 1) - { - node_gain += edge_weight[*adj_edge_it]; - } - if (aside[*adj_edge_it] == 0) - { - node_gain -= edge_weight[*adj_edge_it]; - } - ++adj_edge_it; - } - return node_gain; -} - - -void ratio_cut_partition::init_variables(const graph& G) -{ - compute_max_vertex_degree(G); - bool first_edge_found = true; - max_edge_weight = 0; - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - if (first_edge_found) - { - max_edge_weight = edge_weight[*edge_it]; - first_edge_found = false; - } - else if (edge_weight[*edge_it] > max_edge_weight) - { - max_edge_weight = edge_weight[*edge_it]; - } - ++edge_it; - } -} - - -void ratio_cut_partition::compute_max_vertex_degree(const graph& G) -{ - max_vertex_degree = 0; - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (max_vertex_degree < node_it->degree()) - { - max_vertex_degree = node_it->degree(); - } - ++node_it; - } -} - - -void ratio_cut_partition::determine_source_node(const graph& G) -{ - srand((unsigned)time(NULL)); - rand(); // necessary, otherwise the next rand() returns always 0 ?-) - int node_id = (int)floor((((double)rand() / (double)RAND_MAX) * - (double)(G.number_of_nodes() - 1)) + 0.5); - graph::node_iterator node_it = G.nodes_begin(); - for (int i = 1; i <= node_id; i++) - { - ++node_it; - } - source_node = *node_it; - if (node_weight[source_node] == 0) - { - node_it = G.nodes_begin(); - while (node_weight[*node_it] == 0) - { - ++node_it; - } - source_node = *node_it; - } -} - - -void ratio_cut_partition::compute_target_node(const graph& G) -{ - node cur_node, next; - node_map visited(G, false); - std::queue next_nodes; - next_nodes.push(source_node); - visited[source_node] = true; - - while (!next_nodes.empty()) - { - cur_node = next_nodes.front(); - next_nodes.pop(); - - node::adj_edges_iterator adj_edge_it = cur_node.adj_edges_begin(); - node::adj_edges_iterator adj_edges_end = cur_node.adj_edges_end(); - while (adj_edge_it != adj_edges_end) - { - if (adj_edge_it->target() != cur_node) - { - next = adj_edge_it->target(); - } - else - { - next = adj_edge_it->source(); - } - if (!visited[next]) - { - next_nodes.push(next); - visited[next] = true; - } - ++adj_edge_it; - } - } - target_node = cur_node; - if (node_weight[target_node] == 0) - { - graph::node_iterator node_it = G.nodes_begin(); - while ((node_weight[*node_it] == 0) || (*node_it == source_node)) - { - ++node_it; - } - target_node = *node_it; - } -} - - -void ratio_cut_partition::right_shift_op(const graph& G) -{ - int step_number = 0; - int best_tentative_move = 0; - int best_bal = node_weight_on_sideA * node_weight_on_sideB; - std::vector tentative_moves(G.number_of_nodes() + 1); - std::vector tentative_cutratio(G.number_of_nodes() + 1); - node moved_node; - tentative_cutratio[0] = cur_cutratio; - int best_cutsize = cur_cutsize; - - while (move_vertex_A2B(G, moved_node)) - { - ++step_number; - tentative_cutratio[step_number] = cur_cutratio; - tentative_moves[step_number] = moved_node; - if (tentative_cutratio[best_tentative_move] > cur_cutratio) - { - best_tentative_move = step_number; - best_cutsize = cur_cutsize; - best_bal = node_weight_on_sideA * node_weight_on_sideB; - } - else if (tentative_cutratio[best_tentative_move] == cur_cutratio) - { - if (node_weight_on_sideA * node_weight_on_sideB > best_bal) - { - best_tentative_move = step_number; - best_cutsize = cur_cutsize; - best_bal = node_weight_on_sideA * node_weight_on_sideB; - } - } - } - - for (int i = 1; i <= best_tentative_move; i++) - { - if (side[tentative_moves[i]] == A) - { - side[tentative_moves[i]] = B; - } - else // side[tentative_moves[i]] == B - { - side[tentative_moves[i]] = A; - } - } - cur_cutratio = tentative_cutratio[best_tentative_move]; - cur_cutsize = best_cutsize; -} - - -void ratio_cut_partition::left_shift_op(const graph& G) -{ - int step_number = 0; - int best_tentative_move = 0; - int best_bal = node_weight_on_sideA * node_weight_on_sideB; - std::vector tentative_moves(G.number_of_nodes() + 1); - std::vector tentative_cutratio(G.number_of_nodes() + 1); - node moved_node; - tentative_cutratio[0] = cur_cutratio; - int best_cutsize = cur_cutsize; - - while (move_vertex_B2A(G, moved_node)) - { - ++step_number; - tentative_cutratio[step_number] = cur_cutratio; - tentative_moves[step_number] = moved_node; - if (tentative_cutratio[best_tentative_move] > cur_cutratio) - { - best_tentative_move = step_number; - best_cutsize = cur_cutsize; - } - else if (tentative_cutratio[best_tentative_move] == cur_cutratio) - { - if (node_weight_on_sideA * node_weight_on_sideB > best_bal) - { - best_tentative_move = step_number; - best_cutsize = cur_cutsize; - best_bal = node_weight_on_sideA * node_weight_on_sideB; - } - } - } - - for (int i = 1; i <= best_tentative_move; i++) - { - if (side[tentative_moves[i]] == A) - { - side[tentative_moves[i]] = B; - } - else // side[tentative_moves[i]] == B - { - side[tentative_moves[i]] = A; - } - } - cur_cutratio = tentative_cutratio[best_tentative_move]; - cur_cutsize = best_cutsize; -} - - -bool ratio_cut_partition::move_vertex_A2B(const graph &/*G*/, node& moved_node) -{ - if (!bucketA_empty) - { - node cons_nodeA = - compute_highest_ratio_node(bucketA[range_up(max_gainA)]); - bucketA[range_up(max_gainA)].erase(position_in_bucket[cons_nodeA]); - update_data_structure_A2B(cons_nodeA, true); - moved_node = cons_nodeA; - } - else - { - return false; // no more vertices can be moved - } - update_max_gain(A); - return true; -} - - -bool ratio_cut_partition::move_vertex_B2A(const graph &/*G*/, node& moved_node) -{ - if (!bucketB_empty) - { - node cons_nodeB = - compute_highest_ratio_node(bucketB[range_up(max_gainB)]); - bucketB[range_up(max_gainB)].erase(position_in_bucket[cons_nodeB]); - update_data_structure_B2A(cons_nodeB, true); - moved_node = cons_nodeB; - } - else - { - return false; // no more vertices can be moved - } - update_max_gain(B); - return true; -} - - -node ratio_cut_partition::compute_highest_ratio_node(nodes_t node_list) -{ - node cons_node = node_list.front(); - double ratio, best_ratio; - if (side[cons_node] == A) - { - best_ratio = ratio_of_node_A2B(cons_node); - } - else // side[cons_node] == B - { - best_ratio = ratio_of_node_B2A(cons_node); - } - - nodes_t::iterator node_it = node_list.begin(); - nodes_t::iterator nodes_end = node_list.end(); - while (node_it != nodes_end) - { - if (side[cons_node] == A) - { - ratio = ratio_of_node_A2B(*node_it); - } - else // side[cons_node] == B - { - ratio = ratio_of_node_B2A(*node_it); - } - if (ratio > best_ratio) // choose node with highest ratio - { - best_ratio = ratio; - cons_node = *node_it; - } - ++node_it; - } - return cons_node; -} - - -double ratio_cut_partition::cutratio() -{ - double number_of_nodes = (double)(nodes_on_sideA + nodes_on_sideB); - return ((double)cur_cutsize + number_of_nodes) / (double) - (node_weight_on_sideA * node_weight_on_sideB); -} - - -double ratio_cut_partition::ratio_of_node_A2B(const node cur_node) -{ - return (double)gain_value[cur_node] / - ((double)((node_weight_on_sideB + node_weight[cur_node]) * - (node_weight_on_sideA - node_weight[cur_node]))); -} - - -double ratio_cut_partition::ratio_of_node_B2A(const node cur_node) -{ - return (double)gain_value[cur_node] / - ((double)((node_weight_on_sideA + node_weight[cur_node]) * - (node_weight_on_sideB - node_weight[cur_node]))); -} - - -inline int ratio_cut_partition::range_up(const int gain_value) const -{ - return gain_value + (max_vertex_degree * max_edge_weight); -} - - -inline int ratio_cut_partition::range_down(const int index) const -{ - return index - (max_vertex_degree * max_edge_weight); -} - - -void ratio_cut_partition::update_data_structure_A2B(const node cur_node, - const bool init_mode) -{ - node_weight_on_sideA -= node_weight[cur_node]; - node_weight_on_sideB += node_weight[cur_node]; - --nodes_on_sideA; - ++nodes_on_sideB; - cur_cutsize -= gain_value[cur_node]; - cur_cutratio = cutratio(); - - // updating gain values - node::adj_edges_iterator adj_edge_it = cur_node.adj_edges_begin(); - node::adj_edges_iterator adj_edges_end = cur_node.adj_edges_end(); - while (adj_edge_it != adj_edges_end) - { - // delete cur_node from side A -#if 1 - unlockedA[*adj_edge_it].remove(cur_node); -#else - auto& ua = unlockedA[*adj_edge_it]; - ua.erase(std::remove(ua.begin(), ua.end(), cur_node), ua.end()); -#endif - --aside[*adj_edge_it]; - if (aside[*adj_edge_it] == 0) - { - nodes_t::iterator node_it = unlockedB[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedB[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketB(*node_it, gain_value[*node_it], - gain_value[*node_it] - edge_weight[*adj_edge_it], - init_mode); - gain_value[*node_it] -= edge_weight[*adj_edge_it]; - ++node_it; - } - } - else if (aside[*adj_edge_it] == 1) - { - nodes_t::iterator node_it = unlockedA[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedA[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketA(*node_it, gain_value[*node_it], - gain_value[*node_it] + edge_weight[*adj_edge_it], - init_mode); - gain_value[*node_it] += edge_weight[*adj_edge_it]; - ++node_it; - } - } - // add cur_node to side B - ++bside[*adj_edge_it]; - if (bside[*adj_edge_it] == 1) - { - nodes_t::iterator node_it = unlockedA[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedA[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketA(*node_it, gain_value[*node_it], - gain_value[*node_it] + edge_weight[*adj_edge_it], - init_mode); - gain_value[*node_it] += edge_weight[*adj_edge_it]; - ++node_it; - } - } - else if (bside[*adj_edge_it] == 2) - { - nodes_t::iterator node_it = unlockedB[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedB[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketB(*node_it, gain_value[*node_it], - gain_value[*node_it] - edge_weight[*adj_edge_it], - init_mode); - gain_value[*node_it] -= edge_weight[*adj_edge_it]; - ++node_it; - } - } - ++adj_edge_it; - } -} - - -void ratio_cut_partition::update_data_structure_B2A(const node cur_node, - const bool init_mode) -{ - node_weight_on_sideA += node_weight[cur_node]; - node_weight_on_sideB -= node_weight[cur_node]; - ++nodes_on_sideA; - --nodes_on_sideB; - cur_cutsize -= gain_value[cur_node]; - cur_cutratio = cutratio(); - - // updating gain values - node::adj_edges_iterator adj_edge_it = cur_node.adj_edges_begin(); - node::adj_edges_iterator adj_edges_end = cur_node.adj_edges_end(); - while (adj_edge_it != adj_edges_end) - { - // delete cur_node from side B -#if 1 - unlockedB[*adj_edge_it].remove(cur_node); -#else - auto& ub = unlockedB[*adj_edge_it]; - ub.erase(std::remove(ub.begin(), ub.end(), cur_node), ub.end()); -#endif - bside[*adj_edge_it] -= 1; - if (bside[*adj_edge_it] == 0) - { - nodes_t::iterator node_it = unlockedA[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedA[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketA(*node_it, gain_value[*node_it], - gain_value[*node_it] - edge_weight[*adj_edge_it], - init_mode); - gain_value[*node_it] -= edge_weight[*adj_edge_it]; - ++node_it; - } - } - else if (bside[*adj_edge_it] == 1) - { - nodes_t::iterator node_it = unlockedB[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedB[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketB(*node_it, gain_value[*node_it], - gain_value[*node_it] + edge_weight[*adj_edge_it], - init_mode); - gain_value[*node_it] += edge_weight[*adj_edge_it]; - ++node_it; - } - } - // add cur_node to side A - aside[*adj_edge_it] += 1; - if (aside[*adj_edge_it] == 1) - { - nodes_t::iterator node_it = unlockedB[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedB[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketB(*node_it, gain_value[*node_it], - gain_value[*node_it] + edge_weight[*adj_edge_it], - init_mode); - gain_value[*node_it] += edge_weight[*adj_edge_it]; - ++node_it; - } - } - else if (aside[*adj_edge_it] == 2) - { - nodes_t::iterator node_it = unlockedA[*adj_edge_it].begin(); - nodes_t::iterator nodes_end = unlockedA[*adj_edge_it].end(); - while (node_it != nodes_end) - { - update_bucketA(*node_it, gain_value[*node_it], - gain_value[*node_it] - edge_weight[*adj_edge_it], - init_mode); - gain_value[*node_it] -= edge_weight[*adj_edge_it]; - ++node_it; - } - } - ++adj_edge_it; - } -} - - -void ratio_cut_partition::update_bucketA(const node cur_node, - const int old_gain, const int new_gain, const bool init_mode) -{ - if ((init_mode) && (cur_node == source_node)) - { - return; // this one needs no update with init_mode - } - if (fixed[cur_node] != UNFIXED) - { - return; // fixed nodes need no update - } - bucketA[range_up(old_gain)].erase(position_in_bucket[cur_node]); - bucketA[range_up(new_gain)].push_front(cur_node); - position_in_bucket[cur_node] = bucketA[range_up(new_gain)].begin(); - if (max_gainA < new_gain) - { - max_gainA = new_gain; - } -} - - -void ratio_cut_partition::update_bucketB(const node cur_node, - const int old_gain, const int new_gain, const bool init_mode) -{ - if ((init_mode) && (cur_node == target_node)) - { - return; // this one needs no update with init_mode - } - if (fixed[cur_node] != UNFIXED) - { - return; // fixed nodes need no update - } - bucketB[range_up(old_gain)].erase(position_in_bucket[cur_node]); - bucketB[range_up(new_gain)].push_front(cur_node); - position_in_bucket[cur_node] = bucketB[range_up(new_gain)].begin(); - if (max_gainB < new_gain) - { - max_gainB = new_gain; - } -} - - -void ratio_cut_partition::update_max_gain(const side_type side) -{ - if ((side == A) && (!bucketA_empty)) - { - while (bucketA[range_up(max_gainA)].begin() == - bucketA[range_up(max_gainA)].end()) - { - --max_gainA; - if (range_up(max_gainA) < 0) - { - bucketA_empty = true; - return; - } - } - bucketA_empty = false; - } - if ((side == B) && (!bucketB_empty)) - { - while (bucketB[range_up(max_gainB)].begin() == - bucketB[range_up(max_gainB)].end()) - { - --max_gainB; - if (range_up(max_gainB) < 0) - { - bucketB_empty = true; - return; - } - } - bucketB_empty = false; - } -} - - -void ratio_cut_partition::clean_step(const graph& G) -{ - // clean unlocked* lists - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - unlockedA[*edge_it].clear(); - unlockedB[*edge_it].clear(); - ++edge_it; - } - - // clean buckets - for (int i = 0; i <= 2 * max_vertex_degree * max_edge_weight; i++) - { - bucketA[i].clear(); - bucketB[i].clear(); - } - bucketA.clear(); - bucketB.clear(); -} - - -void ratio_cut_partition::copy_side_node_map(const graph& G, - node_map& dest, const node_map source) const -{ - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - dest[*node_it] = source[*node_it]; - ++node_it; - } -} - - -void ratio_cut_partition::iterative_shifting(const graph& G) -{ - bool continue_loop = true; - double old_cutratio = cur_cutratio; - - while (continue_loop) - { - if (direction == LEFT_SHIFT) - { - init_data_structure(G); - if (fixed[source_node] == UNFIXED) - { - bucketA[range_up(gain_value[source_node])]. - erase(position_in_bucket[source_node]); - update_max_gain(A); - } - right_shift_op(G); - clean_step(G); - if (cur_cutratio < old_cutratio) - { - continue_loop = true; - direction = RIGHT_SHIFT; - old_cutratio = cur_cutratio; - } - else - { - continue_loop = false; - } - } - else // direction == RIGHT_SHIFT - { - init_data_structure(G); - if (fixed[target_node] == UNFIXED) - { - bucketB[range_up(gain_value[target_node])]. - erase(position_in_bucket[target_node]); - update_max_gain(B); - } - left_shift_op(G); - clean_step(G); - if (cur_cutratio < old_cutratio) - { - continue_loop = true; - direction = LEFT_SHIFT; - old_cutratio = cur_cutratio; - } - else - { - continue_loop = false; - } - } - } -} - - -void ratio_cut_partition::group_swapping(const graph& G) -{ - bool improved_cutratio; - - do - { - init_data_structure(G); - improved_cutratio = move_manager(G); - clean_step(G); - } - while (improved_cutratio); -} - - -bool ratio_cut_partition::move_manager(const graph& G) -{ - int step_number = 0; - int best_tentative_move = 0; - int best_bal = node_weight_on_sideA * node_weight_on_sideB; - std::vector tentative_moves(G.number_of_nodes() + 1); - std::vector tentative_cutratio(G.number_of_nodes() + 1); - node moved_node; - tentative_cutratio[0] = cur_cutratio; - int best_cutsize = cur_cutsize; - - while (move_vertex(G, moved_node)) - { - ++step_number; - tentative_moves[step_number] = moved_node; - tentative_cutratio[step_number] = cur_cutratio; - if (tentative_cutratio[best_tentative_move] > cur_cutratio) - { - best_tentative_move = step_number; - best_cutsize = cur_cutsize; - best_bal = node_weight_on_sideA * node_weight_on_sideB; - } - else if (tentative_cutratio[best_tentative_move] == cur_cutratio) - { - if (node_weight_on_sideA * node_weight_on_sideB > best_bal) - { - best_tentative_move = step_number; - best_cutsize = cur_cutsize; - best_bal = node_weight_on_sideA * node_weight_on_sideB; - } - } - } - - for (int i = 1; i <= best_tentative_move; i++) - { - if (side[tentative_moves[i]] == A) - { - side[tentative_moves[i]] = B; - } - else // side[tentative_moves[i]] == B - { - side[tentative_moves[i]] = A; - } - } - cur_cutratio = tentative_cutratio[best_tentative_move]; - cur_cutsize = best_cutsize; - if (best_tentative_move > 0) // cutratio improved - { - return true; - } - return false; // best_move == 0 --> cutratio not improved -} - - -bool ratio_cut_partition::move_vertex(const graph &/*G*/, node& moved_node) -{ - bool consA_ok = false, consB_ok = false; - node cons_nodeA, cons_nodeB; - - if (!bucketA_empty) - { - cons_nodeA = - compute_highest_ratio_node(bucketA[range_up(max_gainA)]); - consA_ok = true; - if (node_weight_on_sideA - node_weight[cons_nodeA] == 0) - { - node temp_node = cons_nodeA; - bucketA[range_up(gain_value[cons_nodeA])]. - erase(position_in_bucket[cons_nodeA]); - update_max_gain(A); - if (!bucketA_empty) // nodes with smaller weight available? - { - cons_nodeA = compute_highest_ratio_node - (bucketA[range_up(max_gainA)]); - } - else - { - consA_ok = false; - } - bucketA_empty = false; - bucketA[range_up(gain_value[temp_node])].push_front(temp_node); - position_in_bucket[temp_node] = - bucketA[range_up(gain_value[temp_node])].begin(); - max_gainA = gain_value[temp_node]; - } - } - if (!bucketB_empty) - { - cons_nodeB = - compute_highest_ratio_node(bucketB[range_up(max_gainB)]); - consB_ok = true; - if (node_weight_on_sideB - node_weight[cons_nodeB] == 0) - { - node temp_node = cons_nodeB; - bucketB[range_up(gain_value[cons_nodeB])]. - erase(position_in_bucket[cons_nodeB]); - update_max_gain(B); - if (!bucketB_empty) // nodes with smaller weight available? - { - cons_nodeB = compute_highest_ratio_node - (bucketB[range_up(max_gainB)]); - } - else - { - consB_ok = false; - } - bucketB_empty = false; - bucketB[range_up(gain_value[temp_node])].push_front(temp_node); - position_in_bucket[temp_node] = - bucketB[range_up(gain_value[temp_node])].begin(); - max_gainB = gain_value[temp_node]; - } - } - - if (consA_ok && consB_ok) - { - double ratio_A2B = ratio_of_node_A2B(cons_nodeA); - double ratio_B2A = ratio_of_node_B2A(cons_nodeB); - if (ratio_A2B > ratio_B2A) - { - moved_node = cons_nodeA; - bucketA[range_up(max_gainA)]. - erase(position_in_bucket[cons_nodeA]); - update_data_structure_A2B(cons_nodeA, false); - } - else // ratio_A2B <= ratio_B2A - { - moved_node = cons_nodeB; - bucketB[range_up(max_gainB)]. - erase(position_in_bucket[cons_nodeB]); - update_data_structure_B2A(cons_nodeB, false); - } - } - else if (consA_ok) - { - moved_node = cons_nodeA; - bucketA[range_up(max_gainA)].erase(position_in_bucket[cons_nodeA]); - update_data_structure_A2B(cons_nodeA, false); - } - else if (consB_ok) - { - moved_node = cons_nodeB; - bucketB[range_up(max_gainB)].erase(position_in_bucket[cons_nodeB]); - update_data_structure_B2A(cons_nodeB, false); - } - else - { - return false; // no more vertices can be moved - } - update_max_gain(A); - update_max_gain(B); - return true; -} - - -void ratio_cut_partition::compute_cut_edges(const graph& G) -{ - cut_edges.clear(); - graph::edge_iterator edge_it = G.edges_begin(); - graph::edge_iterator edges_end = G.edges_end(); - while (edge_it != edges_end) - { - if (side[edge_it->source()] != side[edge_it->target()]) - { - cut_edges.push_back(*edge_it); - } - ++edge_it; - } -} - - -void ratio_cut_partition::compute_nodesAB(const graph& G) -{ - nodesA.clear(); - nodesB.clear(); - graph::node_iterator node_it = G.nodes_begin(); - graph::node_iterator nodes_end = G.nodes_end(); - while (node_it != nodes_end) - { - if (side[*node_it] == A) - { - nodesA.push_back(*node_it); - } - else // side[*node_it] == B - { - nodesB.push_back(*node_it); - } - ++node_it; - } -} - - -#ifdef _DEBUG -void ratio_cut_partition::print_bucketA() -{ - GTL_debug::init_debug(); - GTL_debug::os() << std::endl << "bucketA:" << std::endl; - for (int i = 0; i <= 2 * max_vertex_degree * max_edge_weight; i++) - { - GTL_debug::os() << range_down(i) << ": "; - nodes_t::iterator node_it = bucketA[i].begin(); - nodes_t::iterator nodes_end = bucketA[i].end(); - while (node_it != nodes_end) - { - GTL_debug::os() << *node_it << " "; - ++node_it; - } - GTL_debug::os() << std::endl; - } - GTL_debug::os() << std::endl; - GTL_debug::close_debug(); -} - - -void ratio_cut_partition::print_bucketB() -{ - GTL_debug::init_debug(); - GTL_debug::os() << std::endl << "bucketB:" << std::endl; - for (int i = 0; i <= 2 * max_vertex_degree * max_edge_weight; i++) - { - GTL_debug::os() << range_down(i) << ": "; - nodes_t::iterator node_it = bucketB[i].begin(); - nodes_t::iterator nodes_end = bucketB[i].end(); - while (node_it != nodes_end) - { - GTL_debug::os() << *node_it << " "; - ++node_it; - } - GTL_debug::os() << std::endl; - } - GTL_debug::os() << std::endl; - GTL_debug::close_debug(); -} -#endif // _DEBUG - - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/st_number.cpp b/Tracker/graph/GTL/src/st_number.cpp deleted file mode 100644 index 20c528a6a..000000000 --- a/Tracker/graph/GTL/src/st_number.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// st_number.cpp -// -//========================================================================== -// $Id: st_number.cpp,v 1.10 2001/11/07 13:58:11 pick Exp $ - -#include - -#include - -__GTL_BEGIN_NAMESPACE - -pathfinder::pathfinder (const graph& G, edge st, node s) -{ - node t = s.opposite (st); - dfs_num.init (G, 0); - low_num.init (G); - tree.init(G, edges_t()); - back.init(G, edges_t()); - forward.init(G, edges_t()); - - // - // There is a problem with node/edge maps of iterators with Visual C++ - // which I dont fully understand at the moment. Anyway the init for the - // maps below is only needed to allocate memory, which is done anyway, when - // values are assigned to it. - // - -#ifndef __GTL_MSVCC - to_low.init (G); - to_father.init (G); - pos.init (G); -#endif - - used.init (G,0); - act_dfs_num = 1; - new_nodes = G.number_of_nodes(); - is_biconn = true; - - // - // Do DFS with biconnectivity extensions. - // - - dfs_num[t] = act_dfs_num++; - low_num[t] = dfs_num[t]; - new_nodes--; - - dfs_sub (s, t); - - if (new_nodes != 0) { - is_biconn = false; - } - - used[t] = used[s] = 1; -} - - -void pathfinder::dfs_sub (node& curr, node& father) -{ - low_num[curr] = dfs_num[curr] = act_dfs_num++; - new_nodes--; - - node::adj_edges_iterator it = curr.adj_edges_begin(); - node::adj_edges_iterator end = curr.adj_edges_end(); - - while (it != end) { - edge adj = *it; - node opp = curr.opposite(adj); - - if (dfs_num[opp] == 0) { - - edges_t::iterator tmp = tree[curr].insert (tree[curr].end(), adj); - to_father[opp] = tmp; - - dfs_sub (opp, curr); - - if (low_num[opp] < low_num[curr]) { - low_num[curr] = low_num[opp]; - to_low[curr] = tmp; - } - - if (low_num[opp] >= dfs_num[curr]) { - is_biconn = false; - } - - } else if (opp != father && dfs_num[opp] < dfs_num[curr]) { - edges_t::iterator back_pos = back[curr].insert (back[curr].end(), adj); - edges_t::iterator forward_pos = forward[opp].insert (forward[opp].end(), adj); - pos[adj] = pos_pair (forward_pos, back_pos); - - if (dfs_num[opp] < low_num[curr]) { - low_num[curr] = dfs_num[opp]; - to_low[curr] = back_pos; - } - } - - ++it; - } -} - - -//-------------------------------------------------------------------------- -// ITERATOR -//-------------------------------------------------------------------------- - -pathfinder::const_iterator::const_iterator (pathfinder& _pf, node n) : - pf (_pf) -{ - if (!pf.back[n].empty()) { - edge back = pf.back[n].front(); - curr = n.opposite (back); - pf.used[curr] = 1; - pf.back[n].pop_front(); - pf.forward[curr].erase (pf.pos[back].first); - state = END; - - } else if (!pf.tree[n].empty()) { - curr = n.opposite (pf.tree[n].front()); - pf.used[curr] = 1; - pf.tree[n].pop_front(); - state = DOWN; - - } else if (!pf.forward[n].empty()) { - edge forward = pf.forward[n].front(); - curr = n.opposite (forward); - pf.forward[n].pop_front(); - pf.back[curr].erase (pf.pos[forward].second); - - if (pf.used[curr]) { - state = END; - } else { - pf.used[curr] = 1; - state = UP; - } - } -} - -pathfinder::const_iterator& pathfinder::const_iterator::operator++ () -{ - edges_t::iterator tmp; - edge adj; - node opp; - - switch (state) { - case END : - curr = node(); - break; - - case UP : - tmp = pf.to_father[curr]; - curr = curr.opposite (*tmp); - pf.tree[curr].erase (tmp); - - if (pf.used[curr]) { - state = END; - } else { - pf.used[curr] = 1; - } - - break; - - case DOWN : - tmp = pf.to_low[curr]; - adj = *tmp; - opp = curr.opposite (adj); - - if (pf.used[opp]) { - pf.forward[opp].erase (pf.pos[adj].first); - pf.back[curr].erase (tmp); - state = END; - } else { - pf.tree[curr].erase (tmp); - pf.used[opp] = 1; - } - - curr = opp; - break; - - default: - assert (0); - } - - return *this; -} - - -pathfinder::const_iterator pathfinder::const_iterator::operator++ (int) -{ - const_iterator tmp = *this; - operator++(); - return tmp; -} - - -//-------------------------------------------------------------------------- -// ST-NUMBER -//-------------------------------------------------------------------------- - -int st_number::check (graph& G) -{ - if (G.is_directed()) return GTL_ERROR; - - pf = new pathfinder (G, st, s); - - return pf->is_valid() ? GTL_OK : GTL_ERROR; -} - - -int st_number::run (graph& /*G*/) -{ - nodes_t order; - node t = s.opposite (st); - order.push_back (t); - node tmp = s; - pathfinder::const_iterator end = pf->end(); - int act_st = 1; - - while (tmp != t) - { - pathfinder::const_iterator it = pf->path(tmp); - nodes_t::iterator pos; - - if (it == end) - { - st_num[tmp] = act_st++; - st_ord.push_back(tmp); - tmp = order.back(); - order.pop_back(); - - } - else - { - pos = order.end(); - - while (it != end) - { - pos = order.insert(pos, *it); - ++it; - } - - order.erase(pos); - } - } - - st_num[t] = act_st; - st_ord.push_back (t); - - delete pf; - - return GTL_OK; -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/GTL/src/topsort.cpp b/Tracker/graph/GTL/src/topsort.cpp deleted file mode 100644 index 1de71c571..000000000 --- a/Tracker/graph/GTL/src/topsort.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* This software is distributed under the GNU Lesser General Public License */ -//========================================================================== -// -// topsort.cpp -// -//========================================================================== -// $Id: topsort.cpp,v 1.7 2001/11/07 13:58:12 pick Exp $ - -#include - -__GTL_BEGIN_NAMESPACE - -//-------------------------------------------------------------------------- -// algorithm - interface -//-------------------------------------------------------------------------- - - -void topsort::reset () -{ - dfs::reset(); - acyclic = true; - top_order.erase (top_order.begin(), top_order.end());; -} - -int topsort::check (graph& G) -{ - return G.is_directed() ? GTL_OK : GTL_ERROR; -} - - - -//-------------------------------------------------------------------------- -// Handler -//-------------------------------------------------------------------------- - - -void topsort::init_handler (graph& G) -{ - top_numbers.init (G, 0); - act_top_num = G.number_of_nodes(); -} - - -void topsort::leave_handler (graph& /*G*/, node& n, node& /*f*/) -{ - top_numbers[n] = act_top_num; - act_top_num--; - top_order.push_front (n); -} - - -void topsort::old_adj_node_handler (graph& /*G*/, edge& /*adj*/, node& opp) -{ - if (top_numbers[opp] == 0) { - acyclic = false; - } -} - -__GTL_END_NAMESPACE - -//-------------------------------------------------------------------------- -// end of file -//-------------------------------------------------------------------------- diff --git a/Tracker/graph/components.cpp b/Tracker/graph/components.cpp deleted file mode 100644 index b78ce8ed0..000000000 --- a/Tracker/graph/components.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// $Id: components.cpp,v 1.1 2006/01/18 17:40:47 rdmp1c Exp $ - -// -// Simple program to extract all the components of a GML format graph -// - - -#include -#include - -#include -#include - -#include "mygraph.h" - - - -int main (int argc, const char * argv[]) -{ - if (argc < 2) - { - cout << "Usage: components " << endl; - exit(1); - } - char filename[256]; - strcpy (filename, argv[1]); - - // --------------------------------------------------------- - // Read graph - - MyGraph G; - - GML_error err = G.load (filename); - if (err.err_num != GML_OK) - { - cerr << "Error (" << err.err_num << ") loading graph from file \"" << filename << "\""; - switch (err.err_num) - { - case GML_FILE_NOT_FOUND: cerr << "A file with that name doesn't exist."; break; - case GML_TOO_MANY_BRACKETS: cerr << "A mismatch of brackets was detected, i.e. there were too many closing brackets (])."; break; - case GML_OPEN_BRACKET: cerr << "Now, there were too many opening brackets ([)"; break; - case GML_TOO_MANY_DIGITS: cerr << "The number of digits a integer or floating point value can have is limited to 1024, this should be enough :-)"; break; - case GML_PREMATURE_EOF: cerr << "An EOF occured, where it wasn't expected, e.g. while scanning a string."; break; - case GML_SYNTAX: cerr << "The file isn't a valid GML file, e.g. a mismatch in the key-value pairs."; break; - case GML_UNEXPECTED: cerr << "A character occured, where it makes no sense, e.g. non-numerical characters"; break; - case GML_OK: break; - } - cerr << endl; - exit(1); - } - else - { - cout << "Graph read from file \"" << filename << "\" has " << G.number_of_nodes() << " nodes and " << G.number_of_edges() << " edges" << endl; - } - - // Components - G.make_undirected(); - - if (!G.is_connected()) - { - // 2. Get components - components cp; - if (cp.check(G) != algorithm::GTL_OK) - { - cerr << "component check failed at line " << __LINE__ << endl; - exit(1); - } - else - { - if (cp.run(G) != algorithm::GTL_OK) - { - cerr << "component algorithm failed at line " << __LINE__ << endl; - exit(1); - } - else - { - cout << "Graph has " << cp.number_of_components() << " components" << endl; - - G.make_directed(); - - // Dump components - int count = 0; - components::component_iterator it = cp.components_begin (); - components::component_iterator end = cp.components_end (); - while (it != end) - { - - list comp = (*it).first; - - G.induced_subgraph (comp); - - char buf[64]; - sprintf (buf, "%d.%d.gml", comp.size(), count); - - G.save(buf); - - count++; - - G.restore_graph(); - - it++; - } - - - } - } - } - - - return 0; -} diff --git a/Tracker/graph/fheap.c b/Tracker/graph/fheap.c deleted file mode 100644 index 1a032e862..000000000 --- a/Tracker/graph/fheap.c +++ /dev/null @@ -1,488 +0,0 @@ -// $Id: fheap.c,v 1.1.1.1 2003/11/05 15:19:14 rdmp1c Exp $ - -/** - * @file fheap.c - * - * Fibonacci heap - * - */ - -/* -This code comes from -A Comparison of Data Structures for Dijkstra's -Single Source Shortest Path Algorithm by Shane Saunders (Department -of Computer Science, University of Canterbury, NZ). -The code itself is available from Tadao Takaoka's - Algorithm Repository Home Page -*/ - - -/*** Fibonacci Heap Implementation ***/ -/* - * Shane Saunders - */ -#include -#include -#if FHEAP_DUMP -#include -#endif -#include "fheap.h" - - - -/*** Prototypes of functions only visible within this file. ***/ -void fh_dump_nodes(fheap_node_t *ptr, int level); -void fh_meld(fheap_t *h, fheap_node_t *tree_list); - - - -/*** Definitions for functions that are visible outside this file. ***/ - -/* fh_alloc() - creates and and returns a pointer to a F-heap which can contian - * up to max_nodes nodes. - */ -fheap_t *fh_alloc(int max_nodes) -{ - fheap_t *h; -#if FHEAP_DUMP -printf("alloc, "); -#endif - - /* Create the heap. */ - h = (fheap_t *)malloc(sizeof(fheap_t)); - - h->max_trees = (int)(1.0 + 1.44 * log(max_nodes)/log(2.0)); - h->max_nodes = max_nodes; - h->trees = (fheap_node_t **)calloc(h->max_trees, sizeof(fheap_node_t *)); - h->nodes = (fheap_node_t **)calloc(max_nodes, sizeof(fheap_node_t *)); - h->n = 0; - - /* The value of the heap helps to keep track of the maximum rank while - * nodes are inserted or deleted. - */ - h->value = 0; - - /* For experimental purposes, we keep a count of the number of key - * comparisons. - */ - h->key_comps = 0; - -#if FHEAP_DUMP -printf("alloc-exited, "); -#endif - return h; -} - - -/* fh_free() - destroys the heap pointed to by h, freeing up any space that was - * used by it. - */ -void fh_free(fheap_t *h) -{ - int i; - -#if FHEAP_DUMP -printf("free, "); -#endif - - for(i = 0; i < h->max_nodes; i++) { - free(h->nodes[i]); - } - - free(h->nodes); - free(h->trees); - free(h); - -#if FHEAP_DUMP -printf("free-exited, "); -#endif -} - - -/* fh_insert() - creates and inserts new a node representing vertex_no with key - * k into the heap h. - */ -void fh_insert(fheap_t *h, int vertex_no, long k) -{ - fheap_node_t *newn; - -#if FHEAP_DUMP -printf("insert, "); -#endif - - /* Create an initialise the new node. */ - newn = (fheap_node_t *)malloc(sizeof(fheap_node_t)); - newn->child = NULL; - newn->left = newn->right = newn; - newn->rank = 0; - newn->vertex_no = vertex_no; - newn->key = k; - - /* Maintain a pointer vertex_no's new node in the heap. */ - h->nodes[vertex_no] = newn; - - /* Meld the new node into the heap. */ - fh_meld(h, newn); - - /* Update the heaps node count. */ - h->n++; - -#if FHEAP_DUMP -printf("insert-exited, "); -#endif -} - - -/* fh_delete_min() - deletes the minimum node from the heap pointed to by h and - * returns its vertex number. - */ -int fh_delete_min(fheap_t *h) -{ - fheap_node_t *min_node, *child, *next; - long k, k2; - int r, v, vertex_no; - -#if FHEAP_DUMP -printf("delete_min, "); -#endif - - /* First we determine the maximum rank in the heap. */ - v = h->value; - r = -1; - while(v) { - v = v >> 1; - r++; - }; - - /* Now determine which root node is the minimum. */ - min_node = h->trees[r]; - k = min_node->key; - while(r > 0) { - r--; - next = h->trees[r]; - if(next) { - if((k2 = next->key) < k) { - k = k2; - min_node = next; - } - h->key_comps++; - } - } - - /* We remove the minimum node from the heap but keep a pointer to it. */ - r = min_node->rank; - h->trees[r] = NULL; - h->value -= (1 << r); - - child = min_node->child; - if(child) fh_meld(h, child); - - /* Record the vertex no of the old minimum node before deleting it. */ - vertex_no = min_node->vertex_no; - h->nodes[vertex_no] = NULL; - free(min_node); - h->n--; - -#if FHEAP_DUMP -printf("delete_min-exited, "); -#endif - - return vertex_no; -} - - -/* fh_decrease_key() - decreases the key used for vertex, vertex_no, to - * new_value. No check is made to ensure that new_value is in-fact less than - * the current value so it is up to the user of this function to ensure that - * it is. - */ -void fh_decrease_key(fheap_t *h, int vertex_no, long new_value) -{ - fheap_node_t *cut_node, *parent, *new_roots, *r, *l; - int prev_rank; - -#if FHEAP_DUMP -printf("decrease_key on vn = %d, ", vertex_no); -#endif - - /* Obtain a pointer to the decreased node and its parent then decrease the - * nodes key. - */ - cut_node = h->nodes[vertex_no]; - parent = cut_node->parent; - cut_node->key = new_value; - - /* No reinsertion occurs if the node changed was a root. */ - if(!parent) { -#if FHEAP_DUMP -printf("decrease_key-exited, "); -#endif - return; - } - - /* Update the left and right pointers of cut_node and its two neighbouring - * nodes. - */ - l = cut_node->left; - r = cut_node->right; - l->right = r; - r->left = l; - cut_node->left = cut_node->right = cut_node; - - /* Initially the list of new roots contains only one node. */ - new_roots = cut_node; - - /* While there is a parent node that is marked a cascading cut occurs. */ - while(parent && parent->marked) { - - /* Decrease the rank of cut_node's parent an update its child pointer. - */ - parent->rank--; - if(parent->rank) { - if(parent->child == cut_node) parent->child = r; - } - else { - parent->child = NULL; - } - - /* Update the cut_node and parent pointers to the parent. */ - cut_node = parent; - parent = cut_node->parent; - - /* Update the left and right pointers of cut_nodes two neighbouring - * nodes. - */ - l = cut_node->left; - r = cut_node->right; - l->right = r; - r->left = l; - - /* Add cut_node to the list of nodes to be reinserted as new roots. */ - l = new_roots->left; - new_roots->left = l->right = cut_node; - cut_node->left = l; - cut_node->right = new_roots; - new_roots = cut_node; - } - - /* If the root node is being relocated then update the trees[] array. - * Otherwise mark the parent of the last node cut. - */ - if(!parent) { - prev_rank = cut_node->rank + 1; - h->trees[prev_rank] = NULL; - h->value -= (1 << prev_rank); - } - else { - /* Decrease the rank of cut_node's parent an update its child pointer. - */ - parent->rank--; - if(parent->rank) { - if(parent->child == cut_node) parent->child = r; - } - else { - parent->child = NULL; - } - - parent->marked = 1; - } - - /* Meld the new roots into the heap. */ - fh_meld(h, new_roots); - -#if FHEAP_DUMP -printf("decrease_key-exited, "); -#endif -} - - - -/*** Definitions of functions that are only visible within this file. ***/ - -/* fh_meld() - melds the linked list of trees pointed to by *tree_list into - * the heap pointed to by h. - */ -void fh_meld(fheap_t *h, fheap_node_t *tree_list) -{ - fheap_node_t *first, *next, *node_ptr, *new_root, *temp, *temp2, *lc, *rc; - int r; - -#if FHEAP_DUMP -printf("meld: "); -#endif - - /* We meld each tree in the circularly linked list back into the root level - * of the heap. Each node in the linked list is the root node of a tree. - * The circularly linked list uses the sibling pointers of nodes. This - * makes melding of the child nodes from a delete_min operation simple. - */ - node_ptr = first = tree_list; - - do { - -#if FHEAP_DUMP -printf("%d, ", node_ptr->vertex_no); -#endif - - /* Keep a pointer to the next node and remove sibling and parent links - * from the current node. node_ptr points to the current node. - */ - next = node_ptr->right; - node_ptr->right = node_ptr->left = node_ptr; - node_ptr->parent = NULL; - - /* We merge the current node, node_ptr, by inserting it into the - * root level of the heap. - */ - new_root = node_ptr; - r = node_ptr->rank; - - /* This loop inserts the new root into the heap, possibly restructuring - * the heap to ensure that only one tree for each degree exists. - */ - do { - - /* Check if there is already a tree of degree r in the heap. - * If there is then we need to link it with new_root so it will be - * reinserted into a new place in the heap. - */ - if((temp = h->trees[r])) { - - /* temp will be linked to new_root and relocated so we no - * longer will have a tree of degree r. - */ - h->trees[r] = NULL; - h->value -= (1 << r); - - /* Swap temp and new_root if necessary so that new_root always - * points to the root node which has the smaller key of the - * two. - */ - if(temp->key < new_root->key) { - temp2 = new_root; - new_root = temp; - temp = temp2; - } - h->key_comps++; - - /* Link temp with new_root, making sure that sibling pointers - * get updated if rank is greater than 0. Also, increase r for - * the next pass through the loop since the rank of new has - * increased. - */ - if(r++ > 0) { - rc = new_root->child; - lc = rc->left; - temp->left = lc; - temp->right = rc; - lc->right = rc->left = temp; - } - new_root->child = temp; - new_root->rank = r; - temp->parent = new_root; - temp->marked = 0; - } - /* Otherwise if there is not a tree of degree r in the heap we - * allow new_root, which possibly carries moved trees in the heap, - * to be a tree of degree r in the heap. - */ - else { - - h->trees[r] = new_root; - h->value += (1 << r);; - - /* NOTE: Because new_root is now a root we ensure it is - * marked. - */ - new_root->marked = 1; - } - - /* Note that temp will be NULL if and only if there was not a tree - * of degree r. - */ - } while(temp); - - node_ptr = next; - - } while(node_ptr != first); - -#if FHEAP_DUMP -printf("meld-exited, "); -#endif -} - - - -/*** Debugging functions ***/ - -/* Recursively print the nodes of a Fibonacci heap. */ -#define FHEAP_DUMP 0 -#if FHEAP_DUMP -void fh_dump_nodes(fheap_node_t *ptr, int level) -{ - fheap_node_t *child_ptr, *partner; - int i, ch_count; - - /* Print leading whitespace for this level. */ - for(i = 0; i < level; i++) printf(" "); - - printf("%d(%ld)[%d]\n", ptr->vertex_no, ptr->key, ptr->rank); - - if((child_ptr = ptr->child)) { - child_ptr = ptr->child->right; - - ch_count = 0; - - do { - fh_dump_nodes(child_ptr, level+1); - if(child_ptr->dim > ptr->dim) { - for(i = 0; i < level+1; i++) printf(" "); - printf("error(dim)\n"); exit(1); - } - if(child_ptr->parent != ptr) { - for(i = 0; i < level+1; i++) printf(" "); - printf("error(parent)\n"); - } - child_ptr = child_ptr->right; - ch_count++; - } while(child_ptr != ptr->child->right); - - if(ch_count != ptr->dim) { - for(i = 0; i < level; i++) printf(" "); - printf("error(ch_count)\n"); exit(1); - } - } - else { - if(ptr->dim != 0) { - for(i = 0; i < level; i++) printf(" "); - printf("error(dim)\n"); exit(1); - } - } - -} -#endif - -/* Print out a Fibonacci heap. */ -#if FHEAP_DUMP -void fh_dump(fheap_t *h) -{ - int i; - fheap_node_t *ptr; - - printf("\n"); - printf("value = %d\n", h->value); - printf("array entries 0..max_trees ="); - for(i=0; imax_trees; i++) { - printf(" %d", h->trees[i] ? 1 : 0 ); - } - printf("\n\n"); - for(i=0; imax_trees; i++) { - if((ptr = h->trees[i])) { - printf("tree %d\n\n", i); - fh_dump_nodes(ptr, 0); - printf("\n"); - } - } - fflush(stdout); -} -#endif diff --git a/Tracker/graph/fheap.h b/Tracker/graph/fheap.h deleted file mode 100644 index 15a014fe5..000000000 --- a/Tracker/graph/fheap.h +++ /dev/null @@ -1,149 +0,0 @@ -// $Id : $ - -/** - * @file fheap.h - * - * Fibonacci heap - * - */ - -#ifndef FHEAP_H -#define FHEAP_H - -// rdmp -#ifdef __cplusplus -extern "C" { -#endif - - -/* -This code comes from -A Comparison of Data Structures for Dijkstra's -Single Source Shortest Path Algorithm by Shane Saunders (Department -of Computer Science, University of Canterbury, NZ). -The code itself is available from Tadao Takaoka's - Algorithm Repository Home Page -*/ - -/*** Header File for the Fibonacci Heap Implementation ***/ -/* - * Shane Saunders - */ - - - -/* Option to allow printing of debugging information. Use 1 for yes, or 0 for - * no. - */ -#define FHEAP_DUMP 0 - -#if FHEAP_DUMP - #include -#endif - - - -/*** Definitions of structure types. ***/ - -/* The structure type for Fibonacci heap nodes. - * - * Nodes have the following pointers: - * parent - a pointer to the nodes parent node (if any). - * child - a pointer to a child node (typically the highest rank child). - * left, right - sibling pointers which provide a circular doubly linked list - * containing all the parents nodes children. - * - * The remaining structure fields are: - * rank - the nodes rank, that is, the number of children it has. - * `key' - the nodes key. - * vertex_no - the number of the graph vertex that the node corresponds to. - * Vertex numbering in the graph should be: - * 1, 2, 3, ... max_vertex. - */ -typedef struct fheap_node { - struct fheap_node *parent; - struct fheap_node *left, *right; - struct fheap_node *child; - int rank; - int marked; - long key; - int vertex_no; -} fheap_node_t; - -/* The structure type for a Fibonacci heap. - * - * trees - An array of pointers to trees at root level in the heap. Entry i - * in the array points to the root node of a tree that has nodes of - * dimension i on the main trunk. - * nodes - An array of pointers to nodes in the heap. Nodes are indexed - * according to their vertex number. This array can then be used to - * look up the node for corresponding to a vertex number, and is - * useful when freeing space taken up by the heap. - * max_nodes - The maximum number of nodes allowed in the heap. - * max_trees - The maximum number of trees allowed in the heap (calculated from - * max_nodes). - * n - The current number of nodes in the heap. - * value - The binary value represented by trees in the heap. - * By maintaining this it is easy to keep track of the maximum rank - * tree in the heap. - * key_comps - can be used for experimental purposes when counting the number - * of key comparisons. - */ -typedef struct fheap { - fheap_node_t **trees; - fheap_node_t **nodes; - int max_nodes, max_trees, n, value; - long key_comps; -} fheap_t; - - - -/*** Function prototypes. ***/ - -/* Fibonacci heap functions. */ -/* Note that fheap_t pointers are used so that function definitions are compatible - * with those of other heaps. This allows any type heap to be given as an - * argument to a particular algorithm. It is up to the user to ensure the - * correct heap type is passed to the given functions. - */ - -/* fh_alloc() - creates and and returns a pointer to a F-heap which can contain - * up to max_nodes nodes. - */ -fheap_t *fh_alloc(int max_nodes); - -/* fh_free() - destroys the heap pointed to by h, freeing up any space that was - * used by it. - */ -void fh_free(fheap_t *h); - -/* fh_insert() - creates and inserts new a node representing vertex_no with key - * k into the heap h. - */ -void fh_insert(fheap_t *h, int vertex_no, long k); - -/* fh_delete_min() - deletes the minimum node from the heap pointed to by h and - * returns its vertex number. - */ -int fh_delete_min(fheap_t *h); - -/* fh_decrease_key() - decreases the key used for vertex, vertex_no, to - * new_value. No check is made to ensure that new_value is in-fact less than - * the current value so it is up to the user of this function to ensure that - * it is. - */ -void fh_decrease_key(fheap_t *h, int vertex_no, long new_value); - -/* Debugging functions. */ -#if FHEAP_DUMP -void fh_dump(fheap_t *h); -#endif - - -// rdmp -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/Tracker/graph/gdefs.h b/Tracker/graph/gdefs.h deleted file mode 100644 index 8eada9fed..000000000 --- a/Tracker/graph/gdefs.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef GDEFH -#define GDEFH - -// Determine which platform we are building for - - - -#if __BORLANDC__ // Borland specific options - #define GPORT_WINDOWS 1 // Windows - #define GPORT_MAC 0 -#endif - -#ifdef __MWERKS__ // Metrowerks specific options - #ifdef __INTEL__ - #define GPORT_WINDOWS 1 // Windows - #define GPORT_MAC 0 - #define __WIN32__ // MetroWerks only supports Win32 - #endif /* __INTEL__ */ - - #ifdef macintosh // MacOS - #ifdef __WXMAC__ - #define USE_WXWINDOWS 1 // wxWindows - #define GPORT_MAC 0 - #define GPORT_WINDOWS 0 - #else - #define GPORT_MAC 1 // Macintosh - #define GPORT_WINDOWS 0 - #endif - #endif /* macintosh */ -#endif - -#ifdef __GNUC__ - #define GPORT_MAC 0 // Assume gcc implies X windows - #define GPORT_WINDOWS 0 -#endif - -#endif - diff --git a/Tracker/graph/gml2dot.cpp b/Tracker/graph/gml2dot.cpp deleted file mode 100644 index 5720300e1..000000000 --- a/Tracker/graph/gml2dot.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// $Id: gml2dot.cpp,v 1.1 2004/03/30 21:45:45 rdmp1c Exp $ - -// -// Simple program to convert a GML format graph into a DOT format graph -// for display by GraphViz -// - - -#include -#include - -#include -#include - -#include "mygraph.h" - - - -int main (int argc, const char * argv[]) -{ - if (argc < 2) - { - cout << "Usage: gml2dot " << endl; - exit(1); - } - char filename[256]; - strcpy (filename, argv[1]); - - // --------------------------------------------------------- - // Read graph - - MyGraph G; - - GML_error err = G.load (filename); - if (err.err_num != GML_OK) - { - cerr << "Error (" << err.err_num << ") loading graph from file \"" << filename << "\""; - switch (err.err_num) - { - case GML_FILE_NOT_FOUND: cerr << "A file with that name doesn't exist."; break; - case GML_TOO_MANY_BRACKETS: cerr << "A mismatch of brackets was detected, i.e. there were too many closing brackets (])."; break; - case GML_OPEN_BRACKET: cerr << "Now, there were too many opening brackets ([)"; break; - case GML_TOO_MANY_DIGITS: cerr << "The number of digits a integer or floating point value can have is limited to 1024, this should be enough :-)"; break; - case GML_PREMATURE_EOF: cerr << "An EOF occured, where it wasn't expected, e.g. while scanning a string."; break; - case GML_SYNTAX: cerr << "The file isn't a valid GML file, e.g. a mismatch in the key-value pairs."; break; - case GML_UNEXPECTED: cerr << "A character occured, where it makes no sense, e.g. non-numerical characters"; break; - case GML_OK: break; - } - cerr << endl; - exit(1); - } - else - { - cout << "Graph read from file \"" << filename << "\" has " << G.number_of_nodes() << " nodes and " << G.number_of_edges() << " edges" << endl; - } - - char dotfilename[256]; - strcpy (dotfilename, filename); - strcat (dotfilename, ".dot"); - G.save_dot (dotfilename); - - return 0; -} diff --git a/Tracker/graph/gml2nestedsql.cpp b/Tracker/graph/gml2nestedsql.cpp deleted file mode 100644 index a0f768c19..000000000 --- a/Tracker/graph/gml2nestedsql.cpp +++ /dev/null @@ -1,288 +0,0 @@ -// $Id: gml2nestedsql.cpp,v 1.2 2006/03/24 15:06:57 rdmp1c Exp $ - - -#include -#include - -#include -#include - -#include "mygraph.h" -#include "mytree.h" - -#include - - -void Tokenise (std::string s, std::string delimiters, std::vector &tokens); - -void Tokenise (std::string s, std::string delimiters, std::vector &tokens) -{ - tokens.erase (tokens.begin(), tokens.end()); - int start, stop; - int n = s.length(); - start = s.find_first_not_of (delimiters); - while ((start >= 0) && (start < n)) - { - stop = s.find_first_of (delimiters, start); - if ((stop < 0) || (stop > n)) stop = n; - tokens.push_back (s.substr(start, stop - start)); - start = stop + delimiters.length(); - } -} - -string escape_string (string s); - -//------------------------------------------------- -// Escape string so it can be INSERTed into a SQL database -string escape_string (string s) -{ - if (s.size() == 0) return s; - - string result = ""; - for (unsigned int i = 0;i left_visitation; -node_map right_visitation; -node_map num_children; -node_map child_number; -node_map path; -node_map depth; - -// All purpose traversal of tree (ugly) -class mydfs : public dfs -{ -public: - mydfs () : dfs () { visit = 1; current_path = ""; height = 0; }; - virtual void entry_handler (graph &G, node &n, node &f) - { - // Depth of node - depth[n] = height; - height++; - - // SQL visitation number - left_visitation[n] = visit++; - - // Path as a string of child numbers - string p = current_path; - if (n.indeg() > 0) - { - char buf[32]; - sprintf (buf, "/%d", child_number[n]); - p += buf; - path[n] = p; - } - current_path = p; - - } - virtual void leave_handler (graph &G, node &n, node &f) - { - height--; - right_visitation[n] = visit++; - - // Parent - if (n.indeg() == 0) - current_path = ""; - else - { - edge e = (*n.in_edges_begin()); - current_path = path[e.source()]; - } - } -protected: - int visit; - int height; - string current_path; -}; - - -int main (int argc, const char * argv[]) -{ - if (argc < 2) - { - cout << "Usage: graph " << endl; - exit(1); - } - char filename[256]; - strcpy (filename, argv[1]); - - // --------------------------------------------------------- - // Read graph - - MyTree G; - - G.read_labels_as_weights(); - t0 = clock(); - GML_error err = G.load (filename); - t1 = clock(); - if (err.err_num != GML_OK) - { - cerr << "Error (" << err.err_num << ") loading graph from file \"" << filename << "\""; - switch (err.err_num) - { - case GML_FILE_NOT_FOUND: cerr << "A file with that name doesn't exist."; break; - case GML_TOO_MANY_BRACKETS: cerr << "A mismatch of brackets was detected, i.e. there were too many closing brackets (])."; break; - case GML_OPEN_BRACKET: cerr << "Now, there were too many opening brackets ([)"; break; - case GML_TOO_MANY_DIGITS: cerr << "The number of digits a integer or floating point value can have is limited to 1024, this should be enough :-)"; break; - case GML_PREMATURE_EOF: cerr << "An EOF occured, where it wasn't expected, e.g. while scanning a string."; break; - case GML_SYNTAX: cerr << "The file isn't a valid GML file, e.g. a mismatch in the key-value pairs."; break; - case GML_UNEXPECTED: cerr << "A character occured, where it makes no sense, e.g. non-numerical characters"; break; - case GML_OK: break; - } - cerr << endl; - exit(1); - } - else - { - cout << "Graph read from file \"" << filename << "\" has " << G.number_of_nodes() << " nodes and " << G.number_of_edges() << " edges" << endl; - } - ShowTimeUsed (t0, t1); - - // --------------------------------------------------------- - // Test that it is a tree - if (is_tree (G)) - { - cout << "Is a tree" << endl; - } - else - { - cout << "Graph is not a tree" << endl; - node v; - forall_nodes(v,G) - if ( v.indeg () < 1 ) cout << G.get_node_label(v) << " has no parent" << endl; - if (!G.is_connected() ) - cout << "Not connected"; - - exit(1); - } - - node root = G.root(); - - // --------------------------------------------------------- - // Assign ids to nodes - node_map id (G,0); - - // Extract id's from name - { - node v; - forall_nodes(v,G) - { - string s = G.get_node_label(v); - int n = atol (s.c_str()); - id[v] = n; - } - } - - // --------------------------------------------------------- - // Compute number of children for each node, and - // assign each node its child number - { - node v; - forall_nodes(v,G) - { - num_children[v] = 0; - node::adj_nodes_iterator it = v.adj_nodes_begin(); - node::adj_nodes_iterator end = v.adj_nodes_end(); - int j = 0; - while (it != end) - { - j++; - child_number[(*it)] = j; - num_children[v]++; - it++; - } - } - } - - // --------------------------------------------------------- - // Get visitation numbers for SQL queries - - mydfs d; - d.start_node (G.root()); - if (d.check(G) != algorithm::GTL_OK) - { - cerr << "dfs check failed at " << __LINE__ << " in " << __FILE__ << endl; - } - else - { - if (d.run(G) != algorithm::GTL_OK) - { - cerr << "dfs algorithm failed at " << __LINE__ << " in " << __FILE__ << endl; - } - } - - // --------------------------------------------------------- - // Ensure root path = / - path[G.root()] = "/"; - - - // --------------------------------------------------------- - // SQL Dump - - ofstream sql("tree.sql"); - node v; - forall_nodes(v,G) - { - sql << "INSERT INTO ncbi_tree (tax_id, parent_id, left_id, right_id, path) "; - sql << "VALUES (" << id[v] << ", "; - - // Ensure the node label is acceptable to SQL (NCBI names may have quotes, etc.) -// string s = G.get_node_label(v); -// sql << "'" << escape_string (s) << "', "; - // For Oracle to work we need to ensure the root of the tree has a NULL parent - if (G.parent(v) == v) - sql << "NULL"; - else - { - sql << id[G.parent(v)]; - } - - sql << ", " << left_visitation[v] << ", " << right_visitation[v] << ", '" << path[v] << "');" << endl; - } - sql.close(); - - return 0; -} diff --git a/Tracker/graph/gport.cpp b/Tracker/graph/gport.cpp deleted file mode 100644 index 97f1882e0..000000000 --- a/Tracker/graph/gport.cpp +++ /dev/null @@ -1,333 +0,0 @@ -#include "gport.h" - - -// System specific defines - -#if GPORT_MAC - // From MacTech 4(6) "Comments about PICTs" - #define picGrpBeg 140 - #define picGrpEnd 141 -#endif - - -// The global port -GBasePort *Port = NULL; - - -// A sensible default font -GBaseFont::GBaseFont () -{ - description = "Times-Roman"; - name = "Times-Roman"; - size = 10; - bold = false; - italic = true; -} - -GPostscriptPort::GPostscriptPort () -{ - PenWidth = 1; - DocumentFonts = ""; - Device = devPostscript; - DisplayRect.SetRect (0, 0, 595-144, 842-144); - fill_r = 1; - fill_g = fill_b = 0; -} - -void GPostscriptPort::DrawArc (const GPoint &pt, const int radius, - const double startAngleDegrees, const double endAngleDegrees) -{ - PostscriptStream << "newpath" << std::endl; - PostscriptStream << pt.GetX() << " " << -pt.GetY() - << " " << radius - << " " << (360.0 -startAngleDegrees) - << " " << (360.0 - endAngleDegrees) - << " arcn" << std::endl; - PostscriptStream << "stroke" << std::endl; - PostscriptStream << std::endl; -} - - -void GPostscriptPort::DrawLine (const int x1, const int y1, const int x2, const int y2) -{ - //PostscriptStream << x2 << " " << -y2 << " " << x1 << " " << -y1 << " " << PenWidth << " DrawLine" << endl; - - PostscriptStream << " gsave" << std::endl; - PostscriptStream << PenWidth << " setlinewidth" << std::endl; - // We may not always want to set this as it works best with rectangular trees... -// PostscriptStream << " 2 setlinecap" << endl; -// PostscriptStream << " 0 setgray" << endl; - //PostscriptStream << " 0.7 setgray" << endl; - PostscriptStream << fill_r << " " << fill_g << " " << fill_b << " setrgbcolor" << std::endl; - PostscriptStream << x2 << " " << -y2 << " moveto" << std::endl; - PostscriptStream << x1 << " " << -y1 << " lineto" << std::endl; - PostscriptStream << " stroke" << std::endl; - PostscriptStream << " grestore" << std::endl; - -} - -void GPostscriptPort::DrawCircle (const GPoint &pt, const int radius) -{ - PostscriptStream << "newpath" << std::endl; - PostscriptStream << pt.GetX() << " " << -pt.GetY() << " " << radius << " 0 360 arc" << std::endl; - PostscriptStream << "stroke" << std::endl; - PostscriptStream << std::endl; -} - -void GPostscriptPort::FillCircle (const GPoint &pt, const int radius) -{ - PostscriptStream << "newpath" << std::endl; - PostscriptStream << pt.GetX() << " " << -pt.GetY() << " " << radius << " 0 360 arc" << std::endl; - //PostscriptStream << "gsave" << endl; - //PostscriptStream << "0.90 setgray" << endl; - PostscriptStream << fill_r << " " << fill_g << " " << fill_b << " setrgbcolor" << std::endl; - - PostscriptStream << "fill" << std::endl; - //PostscriptStream << "grestore" << endl; - PostscriptStream << std::endl; -} - - -void GPostscriptPort::DrawRect (const GRect &r) -{ - PostscriptStream << r.GetLeft() << " " << -r.GetTop() << " moveto" << std::endl; - PostscriptStream << r.GetWidth() << " 0 rlineto" << std::endl; - PostscriptStream << "0 " << -r.GetHeight() << " rlineto" << std::endl; - PostscriptStream << -r.GetWidth() << " 0 rlineto" << std::endl; - PostscriptStream << "0 " << r.GetHeight() << " rlineto" << std::endl; - PostscriptStream << "closepath" << std::endl; - PostscriptStream << "stroke" << std::endl; - PostscriptStream << std::endl; -} - -void GPostscriptPort::DrawText (const int x, const int y, const char *text) -{ - PostscriptStream << "(" << text << ") " << x << " " << -y << " DrawText" << std::endl; -} - - -void GPostscriptPort::GetPrintingRect (GRect &r) -{ - // A4, with 1" margin - r.SetRect (0, 0, 595-144, 842-144); -} - - -void GPostscriptPort::SetCurrentFont (GBaseFont &font) -{ - std::string face = font.GetName(); - if (font.IsBold() || font.IsItalic()) - { - face += "-"; - if (font.IsBold()) - face += "Bold"; - if (font.IsItalic()) - face += "Italic"; - } -/* - // Duh -- need to do this earlier, perhaps scan the list of - // fonts already created and output those... - // Store this font in the list of fonts we need for our document - int found = DocumentFonts.find_first_of (face, 0); - if ((found < 0) || (found > DocumentFonts.length())) - { - if (DocumentFonts.length() > 0) - DocumentFonts += ", "; - DocumentFonts += face; - } -*/ - PostscriptStream << std::endl; - PostscriptStream << "/" << face << " findfont" << std::endl; - PostscriptStream << font.GetSize () << " scalefont" << std::endl; - PostscriptStream << "setfont" << std::endl; - PostscriptStream << std::endl; -} - - -// Mac -// Win -// Postscript - -void GPostscriptPort::SetPenWidth (int w) -{ - PenWidth = w; - PostscriptStream << w << " setlinewidth" << std::endl; - PostscriptStream << std::endl; -} - -void GPostscriptPort::StartPicture (char *pictFileName) -{ - PostscriptStream.open (pictFileName); - - - // Postscript header - PostscriptStream << "%!PS-Adobe-2.0" << std::endl; - PostscriptStream << "%%Creator: Roderic D. M. Page" << std::endl; - PostscriptStream << "%%DocumentFonts: Times-Roman" << std::endl; - PostscriptStream << "%%Title:" << pictFileName << std::endl; - PostscriptStream << "%%BoundingBox: 0 0 595 842" << std::endl; // A4 - PostscriptStream << "%%Pages: 1" << std::endl; - PostscriptStream << "%%EndComments" << std::endl; - PostscriptStream << std::endl; - - // Move origin to top left corner - PostscriptStream << "0 842 translate" << std::endl; - PostscriptStream << "72 -72 translate" << std::endl; // one inch margin - - // Some definitions for drawing lines, etc. - - // Drawline draws text with encaps that project... - PostscriptStream << "% Encapsulate drawing a line" << std::endl; - PostscriptStream << "% arguments x1 y1 x2 xy2 width" << std::endl; - PostscriptStream << "/DrawLine {" << std::endl; - PostscriptStream << " gsave" << std::endl; - PostscriptStream << " setlinewidth" << std::endl; - // We may not always want to set this as it works best with rectangular trees... -// PostscriptStream << " 2 setlinecap" << endl; - PostscriptStream << " 0 setgray" << std::endl; - //PostscriptStream << " 0.7 setgray" << endl; - PostscriptStream << " moveto" << std::endl; - PostscriptStream << " lineto" << std::endl; - PostscriptStream << " stroke" << std::endl; - PostscriptStream << " grestore" << std::endl; - PostscriptStream << " } bind def" << std::endl; - PostscriptStream << std::endl; - - PostscriptStream << "% Encapsulate drawing text" << std::endl; - PostscriptStream << "% arguments x y text" << std::endl; - PostscriptStream << "/DrawText {" << std::endl; - PostscriptStream << " gsave 1 setlinewidth 0 setgray" << std::endl; - PostscriptStream << " moveto" << std::endl; - PostscriptStream << " show grestore" << std::endl; - PostscriptStream << "} bind def" << std::endl; - PostscriptStream << std::endl; - -} - -void GPostscriptPort::EndPicture () -{ - PostscriptStream << "showpage" << std::endl; - PostscriptStream << "%%Trailer" << std::endl; - PostscriptStream << "%%end" << std::endl; - PostscriptStream << "%%EOF" << std::endl; - PostscriptStream.close (); -} - - - -#if GPORT_MAC -// Macintosh -void GMacPort::BeginGroup () -{ -// ::PicComment (picGrpBeg, 0, NULL); -} - -void GMacPort::EndGroup () -{ -// ::PicComment (picGrpEnd, 0, NULL); -} -#endif - -SVGPort::SVGPort () -{ - fontString = "font-family:Times;font-size:12"; - DisplayRect.SetRect (0, 0, 400, 400); -} - -void SVGPort::DrawLine (const int x1, const int y1, const int x2, const int y2) -{ - svgStream << ""; - - -// svgStream << ""; -// svgStream << "" << endl; -} - -void SVGPort::DrawCircle (const GPoint &/*pt*/, const int /*radius*/) -{ -/* PostscriptStream << "newpath" << endl; - PostscriptStream << pt.GetX() << " " << -pt.GetY() << " " << radius << " 0 360 arc" << endl; - PostscriptStream << "stroke" << endl; - PostscriptStream << endl; -*/ -} - - -void SVGPort::DrawRect (const GRect &/*r*/) -{ -/* PostscriptStream << r.GetLeft() << " " << -r.GetTop() << " moveto" << endl; - PostscriptStream << r.GetWidth() << " 0 rlineto" << endl; - PostscriptStream << "0 " << -r.GetHeight() << " rlineto" << endl; - PostscriptStream << -r.GetWidth() << " 0 rlineto" << endl; - PostscriptStream << "0 " << r.GetHeight() << " rlineto" << endl; - PostscriptStream << "closepath" << endl; - PostscriptStream << "stroke" << endl; - PostscriptStream << endl; -*/ -} - -void SVGPort::DrawText (const int x, const int y, const char *text) -{ - svgStream << "" - << text << "" << std::endl; -} - - -void SVGPort::StartPicture (char *pictFileName) -{ - svgStream.open (pictFileName); - - svgStream << "" << std::endl; - svgStream << " " << std::endl; - - svgStream << "" << std::endl; - -// test -// test -} - -void SVGPort::EndPicture () -{ - svgStream << "" << std::endl; - svgStream.close (); -} - - -void SVGPort::GetPrintingRect (GRect &r) -{ - r = DisplayRect; -} - -void SVGPort::SetCurrentFont (GBaseFont &font) -{ - fontString = ""; - fontString += "font-family:"; - fontString += font.GetName(); - - char buf[32]; - sprintf (buf, ";font-size:%d", font.GetSize()); - fontString += buf; - - if (font.IsItalic()) - { - fontString += ";font-style:italic"; - } - if (font.IsBold()) - { - fontString += ";font-weight:bold"; - } - -} - - - diff --git a/Tracker/graph/gport.h b/Tracker/graph/gport.h deleted file mode 100644 index 7f4ae1b46..000000000 --- a/Tracker/graph/gport.h +++ /dev/null @@ -1,323 +0,0 @@ -#ifndef GPORTH -#define GPORTH - -#ifdef __BORLANDC__ - // Undefine __MINMAX_DEFINED so that min and max are correctly defined - #ifdef __MINMAX_DEFINED - #undef __MINMAX_DEFINED - #endif - // Ignore "Cannot create precompiled header: code in header" message - // generated when compiling string.cc - #pragma warn -pch -#endif - -#include -#include -#include - -#ifdef __BORLANDC__ - #pragma warn .pch -#endif - - -#include "gdefs.h" - -// System specific includes here -#if GPORT_WINDOWS -#endif - -#if GPORT_MAC -// #include -#endif - -enum GPortDevice {devScreen, devPrinter, devPicture, devPostscript}; - - - - -/* Note that we always draw using the following coordinate system: - -(0,0)--------->(+x,0) - | - | - | - \/ -(+y,0) - - Hence the origin is the top left hand corner, and y goes down rather than up. - This is typical for drawing to a window. Some systems have other coordinate - systems (such as Postscript). We make the translation internally. - -*/ - -// A point -class GPoint -{ -public: - GPoint () { SetPoint (0, 0); }; - GPoint (const GPoint &p) { X = p.X; Y = p.Y; }; - GPoint (const int x, const int y) { SetPoint (x, y); }; - virtual int GetX () const { return X; }; - virtual int GetY () const { return Y; }; - virtual void Offset (const int xoff, const int yoff) { X += xoff; Y += yoff; }; - virtual void SetPoint (const int x, const int y) { X = x; Y = y; }; - virtual void SetX (int x) { X = x; }; - virtual void SetY (int y) { Y = y; }; - - int operator== (const GPoint &p) { return (int) ( (X == p.X) && ( Y == p.Y)); }; - int operator!= (const GPoint &p) { return (int) ( (X != p.X) || ( Y != p.Y)); }; -protected: - int X; - int Y; -}; - -// A rectangle -class GRect -{ -public: - GRect () { left = top = right = bottom = 0; }; - GRect (const int l, const int t, const int r, const int b) { SetRect (l, t, r, b); }; - virtual int GetLeft () const { return left; }; - virtual int GetTop () const { return top; }; - virtual int GetRight () const { return right; }; - virtual int GetBottom () const { return bottom; }; - virtual int GetWidth () const { return right - left; }; - virtual int GetHeight () const { return bottom - top; }; - - virtual void Inset (const int dx, const int dy) { left += dx; right -= dx; top += dy; bottom -= dy; }; - virtual void Offset (const int dx, const int dy) { left += dx; right += dx; top += dy; bottom += dy; }; - virtual bool PointInRect (GPoint &pt) - { - return (((pt.GetX() >= left) && (pt.GetX() <= right)) && - ((pt.GetY() >= top) && (pt.GetY() <= bottom))); - } - - virtual void SetLeft (const int l) {left = l; }; - virtual void SetTop (const int t) {top = t; }; - virtual void SetRight (const int r) {right = r; }; - virtual void SetBottom (const int b) {bottom = b; }; - virtual void SetRect (const int l, const int t, const int r, const int b) - { left = l; top = t; right = r; bottom = b; }; - virtual void SetRectWH (const int l, const int t, const int w, const int h) - { left = l; top = t; right = l + w; bottom = t + h; }; - -protected: - int left, top, right, bottom; -}; - -// Base class for system specific fonts -class GBaseFont -{ -public: - GBaseFont (); - virtual ~GBaseFont () {}; - virtual std::string GetName () { return description; }; - virtual std::string GetDescription () { return description; }; - virtual int GetSize () { return size; }; - virtual bool IsBold () { return bold; }; - virtual bool IsItalic () { return italic; }; -private: - std::string description; - std::string name; - int size; - bool bold; - bool italic; -}; -typedef GBaseFont *GBaseFontPtr; - -typedef GBaseFont GFont ; // for now -typedef GFont *GFontPtr; - - - -// Windows needs the two handles for screen and printer fonts -// Mac just sets things -// Postscript writes to the postscript stream - - -// Virtual class to encapsulate printing -class GBasePrinter -{ -public: - GBasePrinter () {}; - virtual ~GBasePrinter () {}; - virtual void PrinterSetup () = 0; - virtual void AbortPrinting () = 0; - virtual void EndDoc (); - virtual bool EndPage (); - virtual void GetPrintingRect (GRect &r) = 0; - virtual void GetPhysicalPageRect (GRect &r) = 0; - virtual bool StartPage () = 0; - virtual bool StartDoc (char *jobname) = 0; // "GBasePrinter" -}; - -// Windows port VPort -// Mac port VPort -// Postscript - just write to file - - - -// Encapsulates the complete graphics system (screen drawing, picture files, -// printing, clipboard). -class GBasePort -{ -public: - GBasePort () { Device = devScreen; PenWidth = 1;}; - virtual ~GBasePort() {}; - virtual void DrawArc (const GPoint &pt, const int radius, - const double startAngleDegrees, const double endAngleDegrees) = 0; - virtual void DrawCircle (const GPoint &pt, const int radius) = 0; - virtual void DrawLine (const int x1, const int y1, const int x2, const int y2) = 0; - virtual void DrawLinePts (const GPoint &pt1, const GPoint &pt2) - { DrawLine (pt1.GetX(), pt1.GetY(), pt2.GetX(), pt2.GetY()); }; - virtual void DrawRect (const GRect &r) = 0; - virtual void DrawText (const int x, const int y, const char *s) = 0; - - // Display - virtual GPortDevice GetCurrentDevice () { return Device; }; - virtual void GetDisplayRect (GRect &r) { r = DisplayRect; }; - virtual void SetDisplayRect (GRect &r) { DisplayRect = r; }; - - // Pen - virtual int GetPenWidth () { return PenWidth; }; - virtual void SetPenWidth (int w) { PenWidth = w; }; - - // Fonts - virtual void SetCurrentFont (GBaseFont &font) = 0; - - // Pictures - virtual void StartPicture (char *pictFileName) = 0; - virtual void EndPicture () = 0; - - // Groups - virtual void BeginGroup () = 0; - virtual void EndGroup () = 0; - - // Printing - virtual void GetPrintingRect (GRect &r) = 0; - - // Colour - virtual void SetFillColorRGB (int /*r*/, int /*g*/, int /*b*/) {}; - - -protected: - // list of fonts - // printer class - //pens - - int PenWidth; - - // Device info - GPortDevice Device; - GRect DisplayRect; -}; - -// Mac -// Win -// Postscript - -class GPostscriptPort : public GBasePort -{ -public: - GPostscriptPort (); - virtual ~GPostscriptPort () {}; - virtual void DrawArc (const GPoint &pt, const int radius, - const double startAngleDegrees, const double endAngleDegrees); - virtual void DrawCircle (const GPoint &pt, const int radius); - virtual void DrawLine (const int x1, const int y1, const int x2, const int y2); - virtual void DrawRect (const GRect &r); - virtual void DrawText (const int x, const int y, const char *text); - - - virtual void FillCircle (const GPoint &pt, const int radius); - - - - // Pen - virtual void SetPenWidth (int w); - - - // Fonts - virtual void SetCurrentFont (GBaseFont &font); - - // Pictures - virtual void StartPicture (char *pictFileName); - virtual void EndPicture (); - - // Groups - virtual void BeginGroup () {}; - virtual void EndGroup () {}; - - // Printing - virtual void GetPrintingRect (GRect &r); - - virtual void SetFillColorRGB (int r, int g, int b) - { - fill_r = (double)r/255.0; - fill_g = (double)g/255.0; - fill_b = (double)b/255.0; - }; - -protected: - std::ofstream PostscriptStream; - std::string DocumentFonts; - - double fill_r, fill_g, fill_b; -}; - -class GMacPort : public GBasePort -{ -public: - // Groups - virtual void BeginGroup (); - virtual void EndGroup (); -protected: -}; - -class SVGPort : public GBasePort -{ -public: - SVGPort (); - virtual void DrawArc (const GPoint &/*pt*/, const int /*radius*/, - const double /*startAngleDegrees*/, const double /*endAngleDegrees*/) {}; - virtual void DrawCircle (const GPoint &pt, const int radius); - virtual void DrawLine (const int x1, const int y1, const int x2, const int y2); - virtual void DrawRect (const GRect &r); - virtual void DrawText (const int x, const int y, const char *text); - - // Pen - virtual void SetPenWidth (int /*w*/) {}; - - - // Fonts - virtual void SetCurrentFont (GBaseFont &font); - - // Pictures - virtual void StartPicture (char *pictFileName); - virtual void EndPicture (); - - // Groups - virtual void BeginGroup () {}; - virtual void EndGroup () {}; - - // Printing - virtual void GetPrintingRect (GRect &r); -protected: - std::ofstream svgStream; - std::string fontString; -}; - - -#ifndef USE_VC2 -extern GBasePort *Port; // for now -#endif - -#ifdef __BORLANDC__ - // Redefine __MINMAX_DEFINED so Windows header files compile - #ifndef __MINMAX_DEFINED - #define __MINMAX_DEFINED - #endif -#endif - - -#endif diff --git a/Tracker/graph/mincut.cpp b/Tracker/graph/mincut.cpp deleted file mode 100644 index 477791557..000000000 --- a/Tracker/graph/mincut.cpp +++ /dev/null @@ -1,224 +0,0 @@ -// $Id: mincut.cpp,v 1.1.1.1 2003/11/05 15:19:13 rdmp1c Exp $ - -#include "mincut.h" - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fheap.h" - -mincut::mincut () : algorithm () -{ - set_vars_executed = false; -} - - -mincut::~mincut () -{ -} - -void mincut::set_vars(const edge_map& edge_weight) -{ - this->edge_weight = edge_weight; - min_cut = 0; - set_vars_executed = true; -} - -int mincut::check (graph& G) -{ - if (!set_vars_executed) - { - return(GTL_ERROR); - } - if ((G.number_of_nodes() <= 1) || (!G.is_connected()) || (G.is_directed())) - { - return(GTL_ERROR); - } - return GTL_OK; -} - -void mincut::reset () -{ - st_list.erase (st_list.begin(), st_list.end()); -} - -int mincut::run(graph& G) -{ - graph g; - g.make_undirected(); - - // Make a local copy of the graph as mincut modifies the original graph - - // List of nodes in the original graph - node_map partner (G); - node_map orig (g); - - node x; - forall_nodes (x, G) - { - partner[x] = g.new_node(); - orig[partner[x]] = x; // so we can look up original node - } - - // Create edges and associated weights - edge_map w(g, 0); - edge e; - forall_edges (e, G) - { - if (e.source() != e.target()) - { - edge ec = g.new_edge (partner[e.source()], partner[e.target()]); - w[ec] = edge_weight[e]; - } - } - - // Start of algorithm. $a$ is an arbitrary single node in $g$. The set $A$ - // of nodes initially comprises $a$ - graph::node_iterator na = g.nodes_begin(); - node a = *na; - int n = g.number_of_nodes(); - int cut_weight = std::numeric_limits::max(); - int best_value = std::numeric_limits::max(); - while (n >= 2 ) - { - node t = a; - node s, v; - edge e; - node::adj_edges_iterator it; - node::adj_edges_iterator end; - - fheap_t *pq = fh_alloc (n); - node_map vertex_number (g, 0); - std::map > nv; - int vertex_count = 0; - - // Nodes in $A$ are not in the queue - node_map in_PQ(g, false); - forall_nodes (v, g) - { - vertex_number[v] = vertex_count; - nv[vertex_count] = v; - vertex_count++; - if (v != a) - { - in_PQ[v] = true; - fh_insert (pq, vertex_number[v], 0); - } - } - node_map inf (g, 0); - // Get weight of edges adjacent to $a$ - it = a.adj_edges_begin(); - end = a.adj_edges_end(); - while (it != end) - { - v = a.opposite (*it); - inf[v] += w[*it]; - it++; - } - // Store weights in a queue - it = a.adj_edges_begin(); - end = a.adj_edges_end(); - while (it != end) - { - v = a.opposite (*it); - fh_decrease_key (pq, vertex_number[v], -inf[v]); - it++; - } - - while (pq->n > 0) - { - s = t; - - // Get the node that is most tightly connected to $A$ - t = nv[fh_delete_min (pq)]; - cut_weight = inf[t]; - in_PQ[t] = false; - - // Increase the key of nodes adjacent to t and not in $A$ by adding the - // weights of edges connecting t with nodes not in $A$ - it = t.adj_edges_begin(); - end = t.adj_edges_end(); - while (it != end) - { - v = t.opposite (*it); - if (in_PQ[v]) - { - inf[v] += w[*it]; - fh_decrease_key (pq, vertex_number[v], -inf[v]); - } - it++; - } - } - fh_free (pq); - - //cout << " cut-of-the-phase = " << cut_weight << endl; - - if (cut_weight <= best_value) - { - if (cut_weight < best_value) - { - // Clear list of (s,t) pairs - st_list.erase (st_list.begin(), st_list.end()); - best_value = cut_weight; - } - st_list.push_back (node_pair (orig[s], orig[t])); - } - - // Nodes s and t are the last two nodes to be added to A - //cout << "s=" << s << " t=" << t << endl; - - // Get list of edges adjacent to s - edge dummy; - node_map s_edge(g, dummy); - it = s.adj_edges_begin(); - end = s.adj_edges_end(); - while (it != end) - { - s_edge[s.opposite(*it)] = *it; - it++; - } - - // Merge s and t - it = t.adj_edges_begin(); - end = t.adj_edges_end(); - - - // Iterate over edges adjacent to t. If a node v adjacent to - // t is also adjacent to s, then add w(it) to e(s,v) - // otherwise make a new edge e(s,v) - while (it != end) - { - v = t.opposite (*it); - - if (s_edge[v] != dummy) - { - w[s_edge[v]] += w[*it]; - } - else if (s != v) - { - edge ne = g.new_edge (s, v); - w[ne] = w[*it]; - } - it++; - - - } - - // Delete node t from graph - g.del_node(t); - n--; - } - - min_cut = best_value; - - return(GTL_OK); -} diff --git a/Tracker/graph/mincut.h b/Tracker/graph/mincut.h deleted file mode 100644 index 3241e32f0..000000000 --- a/Tracker/graph/mincut.h +++ /dev/null @@ -1,92 +0,0 @@ -// $Id: mincut.h,v 1.1.1.1 2003/11/05 15:19:13 rdmp1c Exp $ - -#ifndef MINCUT_H -#define MINCUT_H - - -#include - -/** - * @typedef node_pair - * A pair of nodes - */ - -typedef std::pair node_pair; - -class GTL_EXTERN mincut : public algorithm -{ -public: - mincut (); - virtual ~mincut(); - - /** - * Sets weight of every edge for mincut calculation. - * - * @param edge_weight weight of every edge. - */ - void set_vars(const edge_map& edge_weight); - - /** - * Finds a mincut of G. - * - * @param G graph. - * @return algorithm::GTL_OK on success, - * algorithm::GTL_ERROR otherwise. - * @see algorithm#run - */ - int run (graph& G); - - - /** - * Checks whether the preconditions for mincut are satisfied. - * - * @param G graph. - * @return algorithm::GTL_OK on success, - * algorithm::GTL_ERROR otherwise. - * @see algorithm#check - */ - virtual int check (graph& G); - - /** - * Reset. - * - * @see algorithm#reset - */ - virtual void reset (); - - /** - * Returns the mincut for the graph G. - * - * @return mincut value - * - */ - int get_mincut() const { return min_cut; }; - -protected: - /** - * @internal - */ - int min_cut; - - /** - * @internal - */ - bool set_vars_executed; - - /** - * @internal - */ - edge_map edge_weight; - - /** - * @internal - */ - std::list st_list; - - - - -}; - - -#endif diff --git a/Tracker/graph/mwbmatching.cpp b/Tracker/graph/mwbmatching.cpp deleted file mode 100644 index dd5104a0b..000000000 --- a/Tracker/graph/mwbmatching.cpp +++ /dev/null @@ -1,330 +0,0 @@ -// $Id: mwbmatching.cpp,v 1.3 2007/10/28 08:47:20 rdmp1c Exp $ - -#include "mwbmatching.h" - - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __GNUC__ -#include -#endif - -#ifdef __BORLANDC__ - #include -#endif -#if (defined __MWERKS__) || (defined __GNUC__) - #include - #define MAXINT INT_MAX -#endif - - -mwbmatching::mwbmatching () - : - algorithm(), - mwbm(0), - set_vars_executed(false), - pq(NULL) -{ -} - - -mwbmatching::~mwbmatching () -{ -} - -void mwbmatching::set_vars(const edge_map& edge_weight) -{ - this->edge_weight = edge_weight; - mwbm = 0; - set_vars_executed = true; -} - -int mwbmatching::check (graph& G) -{ - if (!set_vars_executed) - { - return(GTL_ERROR); - } - if ((G.number_of_nodes() <= 1) || (!G.is_connected()) || (!G.is_directed())) - { - return(GTL_ERROR); - } - return GTL_OK; -} - - -int mwbmatching::run(graph& G) -{ - // Initialise - pot.init (G, 0); - free.init (G, true); - dist.init (G, 0); - - nodes_t A; - nodes_t B; - node n; - // Partition graph based on direction of edges - forall_nodes (n, G) - { - if (n.outdeg() == 0) - { - B.push_back (n); - } - else - { - A.push_back(n); - } - - node_from_id[n.id()] = n; - } - - // Simple heuristic - int C = 0; - edge e; - forall_edges (e, G) - { - edge_from_id[e.id()] = e; - if (edge_weight[e] > C) - C = edge_weight[e]; - } - - nodes_t::iterator it = A.begin(); - nodes_t::iterator end = A.end(); - while (it != end) - { - pot[*it] = C; - it++; - } - - it = A.begin(); - - while (it != end) - { - if (free[*it]) - { - augment (G, *it); - } - it++; - } - - - // Get edges in matching - it = B.begin(); - end = B.end(); - while (it != end) - { - edge e; - forall_out_edges (e, *it) - { - result.push_back (e); - mwbm += edge_weight[e]; - } - it++; - } - - - return(GTL_OK); -} - - - -int mwbmatching::augment(graph& G, node a) -{ - // Initialise - pred.init(G, -1); - pq = fh_alloc(G.number_of_nodes()); - - dist[a] = 0; - node best_node_in_A = a; - long minA = pot[a]; - long delta; - - std::stack > RA; - RA.push(a); - std::stack > RB; - - node a1 = a; - edge e; - - // Relax - forall_adj_edges (e, a1) - { - const node& b = e.target_(); - long db = dist[a1] + (pot[a1] + pot[b] - edge_weight[e]); - if (pred[b] == -1) - { - dist[b] = db; - pred[b] = e.id(); - RB.push(b); - - fh_insert (pq, b.id(), db); - } - else - { - if (db < dist[b]) - { - dist[b] = db; - pred[b] = e.id(); - - fh_decrease_key (pq, b.id(), db); - } - } - } - - for (;;) - { - // Find node with minimum distance db - int node_id; - long db = 0; - if (pq->n == 0) - { - node_id = -1; - } - else - { - node_id = fh_delete_min (pq); - db = dist[node_from_id[node_id]]; - } - - if (node_id == -1 || db >= minA) - { - delta = minA; - // augmentation by best node in A - augment_path_to (G, best_node_in_A); - free[a] = false; - free[best_node_in_A] = true; - break; - } - else - { - node b = node_from_id[node_id]; - if (free[b]) - { - delta = db; - // augmentation by path to b, so a and b are now matched - augment_path_to (G, b); - free[a] = false; - free[b] = false; - break; - } - else - { - // continue shortest path computation - edge e = (*b.adj_edges_begin()); - const node& a1 = e.target_(); - pred[a1] = e.id(); - RA.push(a1); - dist[a1] = db; - - if (db + pot[a1] < minA) - { - best_node_in_A = a1; - minA = db + pot[a1]; - } - - // Relax - forall_adj_edges (e, a1) - { - const node& b = e.target_(); - long db = dist[a1] + (pot[a1] + pot[b] - edge_weight[e]); - if (pred[b] == -1) - { - dist[b] = db; - pred[b] = e.id(); - RB.push(b); - - fh_insert (pq, b.id(), db); - } - else - { - if (db < dist[b]) - { - dist[b] = db; - pred[b] = e.id(); - - fh_decrease_key (pq, b.id(), db); - } - } - } - - } - } - } - - - while (!RA.empty()) - { - node a = RA.top(); - RA.pop(); - pred[a] = -1; - long pot_change = delta - dist[a]; - if (pot_change <= 0) continue; - pot[a] = pot[a] - pot_change; - } - while (!RB.empty()) - { - node b = RB.top(); - RB.pop(); - pred[b] = -1; - - long pot_change = delta - dist[b]; - if (pot_change <= 0) continue; - pot[b] = pot[b] + pot_change; - } - - // Clean up - fh_free(pq); - - return 0; -} - -void mwbmatching::augment_path_to (graph &/*G*/, node v) -{ - int i = pred[v]; - while (i != -1) - { - edge e = edge_from_id[i]; - e.reverse(); - i = pred[e.target()]; - } - -} - -edges_t MAX_WEIGHT_BIPARTITE_MATCHING(graph &G, edge_map weights) -{ - edges_t L; - - mwbmatching mwbm; - mwbm.set_vars(weights); - - //if (mwbm.check(G) != algorithm::GTL_OK) - //{ - // cout << "Maximum weight bipartite matching algorithm check failed" << endl; - //exit(1); - //} - //else - { - if (mwbm.run(G) != algorithm::GTL_OK) - { - std::cout << "Error running maximum weight bipartite matching algorithm" << std::endl; - //exit(1); - } - else - { - L = mwbm.get_match(); - } - } - return L; - -} - - - - diff --git a/Tracker/graph/mwbmatching.h b/Tracker/graph/mwbmatching.h deleted file mode 100644 index 1f0c4f3a9..000000000 --- a/Tracker/graph/mwbmatching.h +++ /dev/null @@ -1,116 +0,0 @@ -// $Id: mwbmatching.h,v 1.3 2007/10/28 08:47:21 rdmp1c Exp $ - -#ifndef MWBMATCHING_H -#define MWBMATCHING_H - -#include -#include -#include "fheap.h" - -class GTL_EXTERN mwbmatching : public algorithm -{ -public: - mwbmatching (); - virtual ~mwbmatching(); - - /** - * Sets weight of every edge for maximum weight bipartite matching calculation. - * - * @param edge_weight weight of every edge. - */ - void set_vars(const edge_map& edge_weight); - - /** - * Finds a maximum weight bipartite matching of G. - * - * @param G graph. - * @return algorithm::GTL_OK on success, - * algorithm::GTL_ERROR otherwise. - * @see algorithm#run - */ - int run (graph& G); - - - /** - * Checks whether the preconditions for maximum weight bipartite matching are satisfied. - * - * @param G graph. - * @return algorithm::GTL_OK on success, - * algorithm::GTL_ERROR otherwise. - * @see algorithm#check - */ - virtual int check (graph& G); - - /** - * Reset. - * - * @see algorithm#reset - */ - virtual void reset () {}; - - /** - * Returns the value of the maximum weight bipartite matching for the graph G. - * - * @return maximum weight bipartite matching value - * - */ - int get_mwbm() const { return mwbm; }; - - /** - * Returns the maximum weight bipartite matching for the graph G as a list of - * edges. - * - * @return list of edges in maximum weight bipartite matching - * - */ - edges_t get_match() { return result; }; - -protected: - /** - * @internal - */ - long mwbm; - - /** - * @internal - */ - bool set_vars_executed; - - /** - * @internal - */ - edge_map edge_weight; - - edges_t result; - - node_map pot; - node_map free; - node_map dist; - node_map pred; - std::map > node_from_id; - std::map > edge_from_id; - - fheap_t *pq; - - int augment(graph& G, node a); - inline void augment_path_to (graph &G, node v); - - - -}; - - -/** - * Wrapper around the maximum weight bipartite matching algorithm to simplify - * it's use. Note that the algorithm expects a directed graph where nodes in one - * partition are sources and nodes in the other are targets. It uses this to determine - * how to partition the nodes. - * - */ -edges_t MAX_WEIGHT_BIPARTITE_MATCHING(graph &G, edge_map weights); - - - - -#endif - diff --git a/Tracker/graph/mygraph.cpp b/Tracker/graph/mygraph.cpp deleted file mode 100644 index 4273e647b..000000000 --- a/Tracker/graph/mygraph.cpp +++ /dev/null @@ -1,393 +0,0 @@ -// $Id: mygraph.cpp,v 1.6 2005/03/16 09:51:26 rdmp1c Exp $ - -#include "mygraph.h" - -#ifdef __GNUC__ - #if __GNUC__ == 3 - #include - #endif -#endif -#include -#include - -void MyGraph::load_edge_info_handler (edge e, GML_pair* list) -{ - if (list) - { - // Iterate over the list of GML_pair values - struct GML_pair *p = list; - while (p) - { - switch (p->kind) - { - case GML_STRING: - store_edge_string (e, p->key, p->value.str); - break; - case GML_INT: - store_edge_integer (e, p->key, p->value.integer); - break; - case GML_DOUBLE: - store_edge_double (e, p->key, p->value.floating); - break; - default: - break; - } - p = p->next; - } - } -} - -void MyGraph::store_edge_double (edge /*e*/, char * /*key*/, double /*value*/) -{ -} - - -void MyGraph::store_edge_integer (edge e, char *key, int value) -{ - if (strcmp (key, "weight") == 0) - { - weight[e] = value; - } - -} - -void MyGraph::store_edge_string (edge e, char *key, char *value) -{ - if (strcmp (key, "label") == 0) - { - if (labels_as_weights) - { - // Treat edge label as a weight - std::istringstream iss(value); - iss >> weight[e]; - } - else - { - // Store edge label as a label - edge_label[e] = value; - } - } -} - - -void MyGraph::load_node_info_handler(node n, GML_pair* list ) -{ - if (list) - { - // Iterate over the list of GML_pair values - struct GML_pair *p = list; - while (p) - { - switch (p->kind) - { - case GML_STRING: - store_node_string (n, p->key, p->value.str); - break; - case GML_INT: - store_node_integer (n, p->key, p->value.integer); - break; - case GML_DOUBLE: - store_node_double (n, p->key, p->value.floating); - break; - default: - break; - } - p = p->next; - } - } -} - -void MyGraph::store_node_double (node /*n*/, char * /*key*/, double /*value*/) -{ -} - - -void MyGraph::store_node_integer (node /*n*/, char * /*key*/, int /*value*/) -{ -} - -void MyGraph::store_node_string (node n, char *key, char *value) -{ - if (strcmp (key, "label") == 0) - { - label[n] = value; - } -} - - -//------------------------------------------------------------------------------ -void MyGraph::save_edge_info_handler(std::ostream *os, edge e) const -{ - graph::save_edge_info_handler (os, e); - *os << "weight " << weight[e] << std::endl; - *os << "label \"" << edge_label[e] << "\"" << std::endl; - - // Line width 1 pt - *os << "graphics [" << std::endl; - *os << "width 1.0" << std::endl; - *os << "]" << std::endl; - - // Use standard Postscript font - *os << "LabelGraphics [" << std::endl; - *os << "type \"text\"" << std::endl; - *os << "font \"Helvetica\"" << std::endl; - *os << "]" << std::endl; -} - -//------------------------------------------------------------------------------ -void MyGraph::save_node_info_handler(std::ostream *os, node n) const -{ - graph::save_node_info_handler (os, n); - if (label[n] != "") - *os << "label \"" << label[n] << "\"" << std::endl; - - // Use standard Postscript font - *os << "LabelGraphics [" << std::endl; - *os << "type \"text\"" << std::endl; - *os << "font \"Helvetica\"" << std::endl; - *os << "]" << std::endl; - -} - - -//------------------------------------------------------------------------------ -void MyGraph::save_dot (char *fname, bool weights) -{ - std::ofstream f (fname); - save_dot (f, weights); - f.close (); -} - -//------------------------------------------------------------------------------ -void MyGraph::save_dot(std::ostream &f, bool weights) -{ - node_map index; - graph::node_iterator nit = nodes_begin(); - graph::node_iterator nend = nodes_end(); - int count = 0; - while (nit != nend) - { - index[*nit] = count++; - nit++; - } - - if (is_directed()) - f << "digraph"; - else - f << "graph"; - - f << " G {" << std::endl; - - // Try and make the graph look nice - f << " node [width=.2,height=.2,fontsize=10];" << std::endl; - f << " edge [fontsize=10,len=2];" << std::endl; - - // Write node labels - nit = nodes_begin(); - while (nit != nend) - { - f << " " << index[*nit] << " [label=\"" << label[*nit] << "\""; - - if (node_colour[*nit] != "white") - { - f << ", color=" << node_colour[*nit] << ", style=filled"; - } - f << "];" << std::endl; - - nit++; - } - - - // Write edges - graph::edge_iterator it = edges_begin(); - graph::edge_iterator end = edges_end(); - while (it != end) - { - f << " " << index[it->source()]; - if (is_directed()) - f << " -> "; - else - f << " -- "; - f << index[it->target()]; - - f << " ["; - - if (weights) - { - f << "label=\"" << weight[*it] << "\", "; - } - else - { - std::string s = edge_label[*it]; - if (s != "") - f << "label=\"" << s << "\", "; - } - - f << " color=" << edge_colour[*it] << "];" << std::endl; - - - - it++; - } - - f << "}" << std::endl; -} - -//------------------------------------------------------------------------------ -bool MyGraph::edge_exists (node n1, node n2) -{ - bool result = false; - - if (is_undirected ()) - { - // Visit all edges adjacent to n1 and ask whether any - // is connect to n2 - node::adj_edges_iterator eit = n1.adj_edges_begin(); - node::adj_edges_iterator eend = n1.adj_edges_end(); - node found = n1; - while ((found == n1) && (eit != eend)) - { - if (n1.opposite (*eit) == n2) - found = n2; - else - eit++; - } - if (found == n2) - { - result = true; - } - } - else - { - // Visit all edges that have n1 as their source and ask - // whether any is connected to n2 - node::out_edges_iterator eit = n1.out_edges_begin(); - node::out_edges_iterator eend = n1.out_edges_end(); - node found = n1; - while ((found == n1) && (eit != eend)) - { - if (n1.opposite (*eit) == n2) - found = n2; - else - eit++; - } - if (found == n2) - { - result = true; - } - } - - return result; -} - -//------------------------------------------------------------------------------ -void MyGraph::delete_edge (node n1, node n2) -{ - edge e; - bool exists = false; - - if (is_undirected ()) - { - // Visit all edges adjacent to n1 and ask whether any - // is connect to n2 - node::adj_edges_iterator eit = n1.adj_edges_begin(); - node::adj_edges_iterator eend = n1.adj_edges_end(); - node found = n1; - while ((found == n1) && (eit != eend)) - { - if (n1.opposite (*eit) == n2) - { - found = n2; - e = *eit; - } - else - eit++; - } - exists = (found == n2); - } - else - { - // Visit all edges that have n1 as their source and ask - // whether any is connected to n2 - node::out_edges_iterator eit = n1.out_edges_begin(); - node::out_edges_iterator eend = n1.out_edges_end(); - node found = n1; - while ((found == n1) && (eit != eend)) - { - if (n1.opposite (*eit) == n2) - { - found = n2; - e = *eit; - } - else - eit++; - } - exists = (found == n2); - } - - if (exists) - del_edge(e); -} - - -double MyGraph::node_cliqueishness (node &n) -{ - double c = 1.0; - - int numneighbours = n.degree(); - int possconnections = numneighbours * (numneighbours - 1) / 2; - int actualconnections = 0; - if (possconnections > 0) - { - // Build list of all nodes adjacent to n (n's neighbours) - nodes_t neighbours; - - node::adj_nodes_iterator nit = n.adj_nodes_begin (); - node::adj_nodes_iterator nend = n.adj_nodes_end (); - while (nit != nend) - { - node ne = (*nit); - neighbours.push_back (ne); - nit++; - } - - // Count number of edges between neighbours - nodes_t::iterator i = neighbours.begin(); - nodes_t::iterator iend = neighbours.end(); - while (i != iend) - { - nodes_t::iterator j = i; - j++; - - while (j != iend) - { - if (edge_exists (*i, *j)) - actualconnections++; - j++; - } - i++; - } - c = (double) actualconnections / (double) possconnections; - } - return c; -} - -double MyGraph::cliqueishness () -{ - double sum = 0.0; - graph::node_iterator nit = nodes_begin(); - graph::node_iterator nend = nodes_end(); - - while (nit != nend) - { - node n = (*nit); - sum += node_cliqueishness (n); - nit++; - } - - return ( sum / (double)number_of_nodes() ); -} - - - - diff --git a/Tracker/graph/mygraph.h b/Tracker/graph/mygraph.h deleted file mode 100644 index f917d2e00..000000000 --- a/Tracker/graph/mygraph.h +++ /dev/null @@ -1,218 +0,0 @@ - // $Id: mygraph.h,v 1.6 2006/01/02 16:03:14 rdmp1c Exp $ - -#ifndef MYGRAPH_H -#define MYGRAPH_H - -// STL -#include -#include -#include -#include - -// GTL -#include - - - -/** - * @class MyGraph - * MyGrap extends the GTL class graph to provide support for graphs - * with weighted edges. - * - */ -class MyGraph : public graph -{ -public: - MyGraph () { labels_as_weights = false; }; - - int get_edge_weight (edge e) const { return weight[e]; }; - void set_edge_weight (edge e, int w) { weight[e] = w; }; - - - std::string get_edge_label(edge e) const { return edge_label[e]; }; - void set_edge_label(edge e, std::string s) { edge_label[e] = s; }; - - /** - * Returns true if an edge exists between a pair of nodes. - * - * @param n1 a node. - * @param n2 another node. - */ - virtual bool edge_exists (node n1, node n2); - - - /** - * Delete the edge (if it exists) between a pair of nodes. - * - * @param n1 a node. - * @param n2 another node. - */ - virtual void delete_edge (node n1, node n2); - - - /** - * Sets the labels_as_weights flag to true (the default is false). - * If this flag is set the load_edge_info_handler will - * read any labels associated with an edge in a GML file an integer - * weight. This is useful if you want to import a GML graph generated - * by LEDA. - * - */ - void read_labels_as_weights () { labels_as_weights = true; }; - - /** - * Extends graph::load_edge_info_handler to read in edge weights. These are - * stored in the list of key-value-pairs, where the key is the sttring "weight" - * and the value is the integer weight of that edge. - * - * @param e edge parsed - * @param list pointer to the list of key-value-pairs of - * this edge. - * @see graph#load_edge_info_handler - */ - - virtual void load_edge_info_handler (edge e, GML_pair* list); - - virtual void store_edge_double (edge e, char *key, double value); - - /** - * Handles an integer value associated with an edge. By default, it - * process the "weight" key by storing value in the - * weight edge map. This method is called by - * load_edge_info_handler. - * @param n the node - * @param key the name of the item (e.g., "weight") - * @param value the contents of the key (e.g., "5") - * - */ - virtual void store_edge_integer (edge e, char *key, int value); - /** - * Handles a string value associated with an edge. By default, it - * process the "label" key by storing value in the - * label edge map. If labels_as_weights - * is true, then converts label to integer and - * sets the edge weight to that value. This method is called by - * load_edge_info_handler. - * @param e the edge - * @param key the name of the item (e.g., "label") - * @param value the contents of the key (e.g., "branch") - * - */ - virtual void store_edge_string (edge e, char *key, char *value); - - - /** - * Extends graph::post_new_edge_handler to ensure by default edge has weight 1, - * and edge_label is "". - * - * @param e created edge - * @see graph#new_edge - */ - virtual void post_new_edge_handler(edge /*e*/) { - //weight[e] = 1; - //edge_label[e] = ""; - //edge_colour[e] = "black"; - } - - /** - * Extends graph::save_edge_info_handler to write the weight of the edge - * as a label when saving the graph to a GML file. - * @param ostream the stream being written to - * @param e the edge - * - */ - virtual void save_edge_info_handler(std::ostream *os, edge e) const; - - - /** - * Extends graph::load_node_info_handler iterator over the list - * of values associated with node n (if any) in the GML file. After - * determining the type of the associated value (integer, floating - * point, or string), the method calls the appropriate handler from - * store_node_double, store_node_double, or store_node_string. - * @param n the node being read - * @param list pointer to the list of paired values - * - */ - virtual void load_node_info_handler(node n, GML_pair* list ); - - - virtual void store_node_double (node n, char *key, double value); - - virtual void store_node_integer (node n, char *key, int value); - /** - * Handles a string value associated with a node. By default, it - * process the "label" key by storing value in the - * label node map. This method is called by - * load_node_info_handler. - * @param n the node - * @param key the name of the item (e.g., "label") - * @param value the contents of the key (e.g., "root") - * - */ - virtual void store_node_string (node n, char *key, char *value); - - /** - * Extends graph::post_new_node_handler to ensure by default node label is "". - * - * @param n created node - * @see graph#new_node - */ - virtual void post_new_node_handler(node /*n*/) - { - //label[n] = ""; - //node_colour[n] = "white"; - } - - virtual void save_node_info_handler(std::ostream *os, node n) const; - - /** - * @param f output stream - * - * Write the graph in dot format (as used by programs in the - * GraphViz - * package. - */ - virtual void save_dot(std::ostream &f, bool weights = false); - - /** - * @param fname output file name - * - * Write the graph in dot format (as used by programs in the - * GraphViz - * package. - */ - virtual void save_dot (char *fname, bool weights = false); - - std::string get_node_label(node n) { return label[n]; }; - void set_node_label(node n, std::string s) { label[n] = s; }; - - void set_edge_colour(edge e, std::string colour) { edge_colour[e] = colour; }; - void set_node_colour(node n, std::string colour) { node_colour[n] = colour; }; - - - double node_cliqueishness (node &n); - double cliqueishness (); - - -protected: - /** - * A map between edges and an integer weight, being the weight of that edge - * in the graph. - * - */ - edge_map weight; - edge_map edge_label; - node_map label; - - bool labels_as_weights; - - // Styles - node_map node_colour; - edge_map edge_colour; - - - -}; - -#endif diff --git a/Tracker/graph/mytree.cpp b/Tracker/graph/mytree.cpp deleted file mode 100644 index 8b3bb134b..000000000 --- a/Tracker/graph/mytree.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// $Id: mytree.cpp,v 1.2 2005/08/16 12:22:52 rdmp1c Exp $ - - -#include "mytree.h" - -#include -#include - -bool is_tree (const graph& G) -{ - node v; - forall_nodes(v,G) - if ( v.indeg () > 1 ) return false; // nonunique parent - return ( G.number_of_nodes() == G.number_of_edges() + 1 - && G.is_connected() ); -} - -bool MyTree::is_root( const node v ) const -{ - return (v.indeg() == 0); -} - -bool MyTree::is_leaf( const node v ) const -{ - return (v.outdeg() == 0); -} - -node MyTree::parent( const node v ) const -{ - if (v.indeg() == 0) return v; - edge e = (*v.in_edges_begin()); - return e.source(); -} - -node MyTree::root() const -{ - node v = (*nodes_begin()); - while (!is_root(v)) - v = parent(v); - return v; -} - -void MyTree::postorder_traversal() -{ - std::stack < node, std::vector > S; - S.push (root()); - int num = 1; - do { - node v = S.top(); - S.pop(); - int n = number_of_nodes() - num++ + 1; // order in which node is visited in postorder - order[v] = n; - number[n] = v; - -// cout << label[v] << " " << order[v] << endl; - - // store info about order here... - - - - node::adj_nodes_iterator it = v.adj_nodes_begin(); - node::adj_nodes_iterator end = v.adj_nodes_end(); - while (it != end) - { - S.push (*it); - it++; - } - } while ( !S.empty() ); -} - - - -node MyTree::get_left_child(const node v) const -{ - return (*(v.adj_nodes_begin())); -} - -node MyTree::get_right_child(const node v) const -{ - node right; - node::adj_nodes_iterator it = v.adj_nodes_begin(); - node::adj_nodes_iterator end = v.adj_nodes_end(); - while (it != end) - { - right = *it; - it++; - } - return right; -} - diff --git a/Tracker/graph/mytree.h b/Tracker/graph/mytree.h deleted file mode 100644 index 208dcfd1d..000000000 --- a/Tracker/graph/mytree.h +++ /dev/null @@ -1,60 +0,0 @@ -// $Id: mytree.h,v 1.2 2005/08/16 12:22:53 rdmp1c Exp $ - -#ifndef MYTREE_H -#define MYTREE_H - - -/* This code is heavily based on the code provided by Gabriel Valiente for - his book "Algorithms on Trees and Graphs" (Berlin: Springer-Verlag, 2002). - I've modified it to call GTL rather than LEDA functions. -*/ - - - -/* - To do: - - LCA functions - Preorder - Inorder - Visitation numbers - Bottom up - Height - Depth - etc. - -*/ - -#include "mygraph.h" - - -// Test whether graph is a tree -bool is_tree (const graph& G); - -class MyTree : public MyGraph -{ -public: - MyTree () { }; - - node parent( const node v ) const; - node root() const; - - bool is_root( const node v ) const; - bool is_leaf( const node v ) const; - - node get_left_child(const node v) const; - node get_right_child(const node v) const; - - void postorder_traversal(); - - int postorder (const node v) const { return order[v]; }; - -protected: - node_map order; - std::map > number; -}; - - - -#endif - diff --git a/Tracker/graph/rings.cpp b/Tracker/graph/rings.cpp deleted file mode 100644 index c9093c8aa..000000000 --- a/Tracker/graph/rings.cpp +++ /dev/null @@ -1,422 +0,0 @@ -// $Id: rings.cpp,v 1.1 2005/01/07 23:01:20 rdmp1c Exp $ - -// Output RINGS representation of a GML tree -// See "RINGS: A Technique for Visualizing Large Hierarchies" -// http://www.cs.ucdavis.edu/~ma/papers/graph2.pdf - - -#include -#include -#include - -#include -#include -#include - -#include "gport.h" - -#include "mygraph.h" -#include "mytree.h" - -#include - -GPostscriptPort g; - - -clock_t t0; -clock_t t1; - -void ShowTimeUsed (clock_t &t0, clock_t &t1); - -void ShowTimeUsed (clock_t &t0, clock_t &t1) -{ - cout << "CPU time used = " << (float)(t1 - t0)/(float)CLOCKS_PER_SEC << " seconds" << endl; -} - -node_map num_children; -node_map num_grandchildren; -node_map R1; -node_map pt; -node_map outer_ring; - - -class CompareNodes { -public: - bool operator() (node x, node y) - { - return num_children[x] < num_children[y]; - } -}; - - -// Get numbers of children and grandchildren -class mydfs : public dfs -{ -public: - mydfs () : dfs () { }; - virtual void entry_handler (graph &G, node &n, node &f) - { - num_children[n] = n.outdeg(); - num_grandchildren[n] = 0; - } - virtual void leave_handler (graph &G, node &n, node &f) - { - if (n.outdeg() > 0) - { - node::adj_nodes_iterator it = n.adj_nodes_begin(); - while (it != n.adj_nodes_end()) - { - num_grandchildren[n] += num_children[(*it)]; - it++; - } - } - } -}; - -#ifndef M_PI - #define M_PI 3.14159265358979323846 // define pi -#endif - -#define SQR(X) ((X) * (X)) - - -double f (double &R1, double &R2, int n); - -double f (double &R1, double &R2, int n) -{ - double theta = M_PI/double(n); - double fn = SQR(1 - sin(theta)) / SQR (1 + sin(theta)); - R2 = sqrt (fn * SQR(R1)); - return fn; -} - - -// Compute and draw layout ussing a breadth first search -class mybfs : public bfs -{ -public: - mybfs () : bfs () { }; - virtual void popped_node_handler (graph &G, node &n) - { - if ((n.outdeg() > 0) && (num_grandchildren[n] > 0)) - { - // Colour map by level in graph - switch (level(n)) - { - case 0: g.SetFillColorRGB (125,126,190); break; - case 1: g.SetFillColorRGB (202,154,152); break; - case 2: g.SetFillColorRGB (178,219,178); break; - case 3: g.SetFillColorRGB (255,179,179); break; - case 4: g.SetFillColorRGB (225,224,179); break; - case 5: g.SetFillColorRGB (255,178,255); break; - default: g.SetFillColorRGB (192,192,192); break; - } - - - // Create a list of the children sorted by their number of children - double total_grandchildren = 0.0; - priority_queue , CompareNodes> p; - node::adj_nodes_iterator it = n.adj_nodes_begin(); - while (it != n.adj_nodes_end()) - { - p.push(*it); - outer_ring[*it] = false; - total_grandchildren += (double)num_children[*it]; - it++; - } - - - // Find how many children to put in the two rings - double R2; - double sum_grandchildren = 0.0; - int count = 0; - int outer = num_children[n]; // number in outer ring - int inner = 0; // number in inner ring - - // We look at the sorted list of children and - // put the split between inner and outer rings at the point where - // the fraction of children yet to be included is less than the - // amount of space available in an inner ring if count rings are - // in the outer ring. It is possible that there won't be an inner ring, - // in which case inner is 0. - // We use the node map outer_ring to classify children by which ring they - // are assigned to. - while (!p.empty()) - { - - node x = p.top(); - outer_ring[x] = (inner == 0); - count++; - - sum_grandchildren += (double)num_children[x]; - - double fraction = 1.0 - (sum_grandchildren / total_grandchildren); - - double fk = f(R1[n], R2, count); - - if ((count > 2) && (inner == 0)) - { - if (fraction < fk) - { - inner = outer - count; - outer = count; - } - } - p.pop(); - } - - // Compute radius of children in outer ring - double fn = f(R1[n], R2, outer); - double r_outer = (R1[n] - R2)/2.0; - double theta_outer = M_PI/double(outer); - - - // Compute radius of children in inner ring (if any) - double r_inner = 0.0; - double R3 = 0.0; - double theta_inner = 0.0; - if (inner > 0) - { - fn = f(R2, R3, inner); - r_inner = (R2 - R3)/2.0; - theta_inner = M_PI/double(inner); - } - int inner_count = 0; - int outer_count = 0; - it = n.adj_nodes_begin(); - while (it != n.adj_nodes_end()) - { - if (outer_ring[*it]) - { - R1[*it] = r_outer; - int offset_x = (int)((R1[*it] + R2) * cos(2 * theta_outer * outer_count)); - int offset_y = (int)((R1[*it] + R2) * sin(2 * theta_outer * outer_count)); - outer_count++; - - // Draw!!! - GPoint p = pt[n]; - p.Offset (offset_x, offset_y); - pt[*it] = p; - g.DrawLinePts (pt[n], pt[*it]); - //g.DrawCircle (pt[*it], R1[*it]); - - // Centre - pt[*it] = p; - - - - } - else - { - - R1[*it] = r_inner; - int offset_x = (int)((R1[*it] + R3) * cos(2 * theta_inner * inner_count)); - int offset_y = (int)((R1[*it] + R3) * sin(2 * theta_inner * inner_count)); - inner_count++; - - // Draw!!! - GPoint p = pt[n]; - p.Offset (offset_x, offset_y); - pt[*it] = p; - g.DrawLinePts (pt[n], pt[*it]); - //g.DrawCircle (pt[*it], R1[*it]); - - // Centre - pt[*it] = p; - - } - - - it++; - } - - - - } - else - { - // leaf, or node with no grandchildren (i.e., a star) - if (n.outdeg() > 0) - { - //g.DrawCircle (pt[n], R1[n]); - - - double theta = (2 * M_PI)/double(n.outdeg()); - - double radius = R1[n] * 0.9; - - double gap = fabs (sin(theta) * radius); - - // If the gap between two edges is too small to be easily visible - // we draw a filled circle - if ((gap < 2.0) && (n.outdeg() > 1)) - { - g.FillCircle (pt[n], (int)radius); - } - else - { - int count = 0; - node::adj_nodes_iterator it = n.adj_nodes_begin(); - while (it != n.adj_nodes_end()) - { - int offset_x = (int)(radius * cos(theta*count)); - int offset_y = (int)(radius * sin(theta*count)); - GPoint p = pt[n]; - p.Offset (offset_x, offset_y); - pt[*it] = p; - g.DrawLinePts (pt[n], pt[*it]); - count++; - it++; - } - - } - } - - } - } - virtual void finished_handler (graph &G, node &n) - { - } -}; - - - - -int main (int argc, const char * argv[]) -{ - if (argc < 2) - { - cout << "Usage: graph " << endl; - exit(1); - } - char filename[256]; - strcpy (filename, argv[1]); - - // --------------------------------------------------------- - // Read graph - - MyTree G; - - G.read_labels_as_weights(); - t0 = clock(); - GML_error err = G.load (filename); - t1 = clock(); - if (err.err_num != GML_OK) - { - cerr << "Error (" << err.err_num << ") loading graph from file \"" << filename << "\""; - switch (err.err_num) - { - case GML_FILE_NOT_FOUND: cerr << "A file with that name doesn't exist."; break; - case GML_TOO_MANY_BRACKETS: cerr << "A mismatch of brackets was detected, i.e. there were too many closing brackets (])."; break; - case GML_OPEN_BRACKET: cerr << "Now, there were too many opening brackets ([)"; break; - case GML_TOO_MANY_DIGITS: cerr << "The number of digits a integer or floating point value can have is limited to 1024, this should be enough :-)"; break; - case GML_PREMATURE_EOF: cerr << "An EOF occured, where it wasn't expected, e.g. while scanning a string."; break; - case GML_SYNTAX: cerr << "The file isn't a valid GML file, e.g. a mismatch in the key-value pairs."; break; - case GML_UNEXPECTED: cerr << "A character occured, where it makes no sense, e.g. non-numerical characters"; break; - case GML_OK: break; - } - cerr << endl; - exit(1); - } - else - { - cout << "Graph read from file \"" << filename << "\" has " << G.number_of_nodes() << " nodes and " << G.number_of_edges() << " edges" << endl; - } - ShowTimeUsed (t0, t1); - - // --------------------------------------------------------- - // Test that it is a tree - if (is_tree (G)) - { - cout << "Is a tree" << endl; - } - else - { - cout << "Graph is not a tree" << endl; - node v; - forall_nodes(v,G) - if ( v.indeg () < 1 ) cout << G.get_node_label(v) << " has no parent" << endl; - if (!G.is_connected() ) cout << "Not connected"; - - exit(1); - } - - node root = G.root(); - cout << "Root = " << root << " " << "\"" << G.get_node_label (root) << "\"" << endl; - - cout << "Computing layout..." << endl; - t0 = clock(); - - bfs b; - b.start_node (G.root()); - b.calc_level(true); - if (b.check(G) != algorithm::GTL_OK) - { - cerr << "bfs check failed at " << __LINE__ << " in " << __FILE__ << endl; - exit(1); - } - else - { - if (b.run(G) != algorithm::GTL_OK) - { - cerr << "bfs algorithm failed at " << __LINE__ << " in " << __FILE__ << endl; - exit(1); - } - } - - // dfs - mydfs d; - d.start_node (G.root()); - if (d.check(G) != algorithm::GTL_OK) - { - cerr << "dfs check failed at " << __LINE__ << " in " << __FILE__ << endl; - exit(1); - } - else - { - if (d.run(G) != algorithm::GTL_OK) - { - cerr << "dfs algorithm failed at " << __LINE__ << " in " << __FILE__ << endl; - exit(1); - } - } - - char picture_filename[256]; - strcpy (picture_filename, filename); - strcat (picture_filename, ".ps"); - g.StartPicture (picture_filename); - g.SetPenWidth(1); - - - - R1[G.root()] = 200.0; - GPoint centre(200,200); - pt[G.root()] = centre; - - mybfs layout; - layout.start_node (G.root()); - layout.calc_level(true); - if (layout.check(G) != algorithm::GTL_OK) - { - cerr << "bfs check failed at " << __LINE__ << " in " << __FILE__ << endl; - exit(1); - } - else - { - if (layout.run(G) != algorithm::GTL_OK) - { - cerr << "bfs algorithm failed at " << __LINE__ << " in " << __FILE__ << endl; - exit(1); - } - } - - g.EndPicture (); - - t1 = clock(); - ShowTimeUsed (t0, t1); - - return 0; -} - - - diff --git a/Tracker/graph/script.cpp b/Tracker/graph/script.cpp deleted file mode 100644 index 02a41804c..000000000 --- a/Tracker/graph/script.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// $Id: script.cpp,v 1.1 2005/03/16 09:52:54 rdmp1c Exp $ - -// -// Simple program to apply an edit script to aa graph -// - - -#include -#include -#include - -#include -#include - -#include "mygraph.h" -#include "tokenise.h" - - - -int main (int argc, const char * argv[]) -{ - if (argc < 2) - { - cout << "Usage: script " << endl; - exit(1); - } - char filename[256]; - strcpy (filename, argv[1]); - - // --------------------------------------------------------- - // Read graph - - MyGraph G; - - GML_error err = G.load (filename); - if (err.err_num != GML_OK) - { - cerr << "Error (" << err.err_num << ") loading graph from file \"" << filename << "\""; - switch (err.err_num) - { - case GML_FILE_NOT_FOUND: cerr << "A file with that name doesn't exist."; break; - case GML_TOO_MANY_BRACKETS: cerr << "A mismatch of brackets was detected, i.e. there were too many closing brackets (])."; break; - case GML_OPEN_BRACKET: cerr << "Now, there were too many opening brackets ([)"; break; - case GML_TOO_MANY_DIGITS: cerr << "The number of digits a integer or floating point value can have is limited to 1024, this should be enough :-)"; break; - case GML_PREMATURE_EOF: cerr << "An EOF occured, where it wasn't expected, e.g. while scanning a string."; break; - case GML_SYNTAX: cerr << "The file isn't a valid GML file, e.g. a mismatch in the key-value pairs."; break; - case GML_UNEXPECTED: cerr << "A character occured, where it makes no sense, e.g. non-numerical characters"; break; - case GML_OK: break; - } - cerr << endl; - exit(1); - } - else - { - cout << "Graph read from file \"" << filename << "\" has " << G.number_of_nodes() << " nodes and " << G.number_of_edges() << " edges" << endl; - } - - - // Output starting graph - G.save_dot ("start.dot"); - - - // 1. Get map between labels and nodes - std::map < std::string, GTL::node, std::less > l; - node n; - forall_nodes (n, G) - { - l[G.get_node_label(n)] = n; - } - - // Read edit script - { - ifstream f ("script.txt"); - char buf[512]; - - while (f.good()) - { - f.getline (buf, sizeof(buf)); - - if (f.good()) - { - - // Break line into tokens - std::vector tokens; - std::string s = buf; - std::string delimiters = "|"; - Tokenise (s, delimiters, tokens); - - if (tokens[0] == "delete") - { - if (tokens[1] == "node") - { - string name = tokens[2]; - if (l.find(name) == l.end()) - { - cout << "Node labelled \"" << name << "\" not found" << endl; - exit(1); - } - else - { - cout << "Node \"" << name << "\" deleted" << endl; - G.del_node(l[name]); - l.erase(name); - } - } - else if (tokens[1] == "branch") - { - string source = tokens[2]; - string target = tokens[3]; - if (l.find(source) == l.end()) - { - cout << "Node labelled \"" << source << "\" not found" << endl; - exit(1); - } - if (l.find(target) == l.end()) - { - cout << "Node labelled \"" << target << "\" not found" << endl; - exit(1); - } - cout << "Edge \"" << source << "\"-->\"" << target << "\" deleted" << endl; - G.delete_edge(l[source], l[target]); - } - } - if (tokens[0] == "insert") - { - if (tokens[1] == "node") - { - string name = tokens[2]; - node n = G.new_node(); - G.set_node_label (n, name); - G.set_node_colour (n, "green"); - l[name] = n; - cout << "Node \"" << name << "\" inserted" << endl; - } - else if (tokens[1] == "branch") - { - string source = tokens[2]; - string target = tokens[3]; - if (l.find(source) == l.end()) - { - cout << "Node labelled \"" << source << "\" not found" << endl; - exit(1); - } - if (l.find(target) == l.end()) - { - cout << "Node labelled \"" << target << "\" not found" << endl; - exit(1); - } - - edge e = G.new_edge (l[source], l[target]); - G.set_edge_colour (e, "green"); - - cout << "Edge \"" << source << "\"-->\"" << target << "\" added" << endl; - } - } - - } - - } - f.close(); - } - - G.save_dot ("end.dot"); - - - - return 0; -} diff --git a/Tracker/graph/tokenise.cpp b/Tracker/graph/tokenise.cpp deleted file mode 100644 index f252ba28c..000000000 --- a/Tracker/graph/tokenise.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * tokenise.cpp - * taxonnames - * - * Created by Roderic Page on Fri Apr 04 2003. - * Copyright (c) 2001 __MyCompanyName__. All rights reserved. - * - */ - -#include "tokenise.h" - -void Tokenise (std::string s, std::string delimiters, std::vector &tokens) -{ - tokens.erase (tokens.begin(), tokens.end()); - int start, stop; - int n = s.length(); - start = s.find_first_not_of (delimiters); - while ((start >= 0) && (start < n)) - { - stop = s.find_first_of (delimiters, start); - if ((stop < 0) || (stop > n)) stop = n; - tokens.push_back (s.substr(start, stop - start)); - start = stop + delimiters.length(); - } -} - - diff --git a/Tracker/graph/tokenise.h b/Tracker/graph/tokenise.h deleted file mode 100644 index eedfd9f0f..000000000 --- a/Tracker/graph/tokenise.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * tokenise.h - * taxonnames - * - * Created by Roderic Page on Fri Apr 04 2003. - * Copyright (c) 2001 __MyCompanyName__. All rights reserved. - * - */ - -#ifndef TOKENISE_H -#define TOKENISE_H - -#include -#include -#include - -void Tokenise (std::string s, std::string delimiters, std::vector &tokens); - -#endif diff --git a/Tracker/track.cpp b/Tracker/track.cpp deleted file mode 100644 index 985e3da13..000000000 --- a/Tracker/track.cpp +++ /dev/null @@ -1,461 +0,0 @@ -#include "track.h" - -/// -/// \brief CTrack -/// \param pt -/// \param region -/// \param deltaTime -/// \param accelNoiseMag -/// \param trackID -/// \param filterObjectSize -/// \param externalTrackerForLost -/// -CTrack::CTrack( - const CRegion& region, - tracking::KalmanType kalmanType, - track_t deltaTime, - track_t accelNoiseMag, - size_t trackID, - bool filterObjectSize, - tracking::LostTrackType externalTrackerForLost - ) - : - m_trackID(trackID), - m_skippedFrames(0), - m_lastRegion(region), - m_predictionPoint((region.m_rect.tl() + region.m_rect.br()) / 2), - m_filterObjectSize(filterObjectSize), - m_externalTrackerForLost(externalTrackerForLost) -{ - if (filterObjectSize) - { - m_kalman = new TKalmanFilter(kalmanType, region.m_rect, deltaTime, accelNoiseMag); - } - else - { - m_kalman = new TKalmanFilter(kalmanType, m_predictionPoint, deltaTime, accelNoiseMag); - } - m_trace.push_back(m_predictionPoint, m_predictionPoint); -} - -/// -/// \brief CalcDist -/// \param pt -/// \return -/// -track_t CTrack::CalcDist(const Point_t& pt) const -{ - Point_t diff = m_predictionPoint - pt; - return sqrtf(diff.x * diff.x + diff.y * diff.y); -} - -/// -/// \brief CalcDist -/// \param r -/// \return -/// -track_t CTrack::CalcDist(const cv::Rect& r) const -{ - std::array diff; - diff[0] = m_predictionPoint.x - m_lastRegion.m_rect.width / 2.f - r.x; - diff[1] = m_predictionPoint.y - m_lastRegion.m_rect.height / 2.f - r.y; - diff[2] = static_cast(m_lastRegion.m_rect.width - r.width); - diff[3] = static_cast(m_lastRegion.m_rect.height - r.height); - - track_t dist = 0; - for (size_t i = 0; i < diff.size(); ++i) - { - dist += diff[i] * diff[i]; - } - return sqrtf(dist); -} - -/// -/// \brief CalcOverlap -/// \param r -/// \return -/// -track_t CTrack::CalcDistJaccard(const cv::Rect& r) const -{ - cv::Rect rr(GetLastRect()); - - track_t intArea = (r & rr).area(); - track_t unionArea = r.area() + rr.area() - intArea; - - return 1 - intArea / unionArea; -} - -/// -/// \brief CTrack::CheckType -/// \param type -/// \return -/// -bool CTrack::CheckType(const std::string& type) const -{ - return m_lastRegion.m_type.empty() || type.empty() || (m_lastRegion.m_type == type); -} - -/// -/// \brief Update -/// \param pt -/// \param region -/// \param dataCorrect -/// \param max_trace_length -/// \param prevFrame -/// \param currFrame -/// -void CTrack::Update( - const CRegion& region, - bool dataCorrect, - size_t max_trace_length, - cv::UMat prevFrame, - cv::UMat currFrame - ) -{ - cv::Point pt((region.m_rect.tl() + region.m_rect.br()) / 2); - - if (m_filterObjectSize) // Kalman filter for object coordinates and size - { - RectUpdate(region, dataCorrect, prevFrame, currFrame); - } - else // Kalman filter only for object center - { - PointUpdate(pt, dataCorrect, currFrame.size()); - } - - if (dataCorrect) - { - m_lastRegion = region; - m_trace.push_back(m_predictionPoint, pt); - } - else - { - m_trace.push_back(m_predictionPoint); - } - - if (m_trace.size() > max_trace_length) - { - m_trace.pop_front(m_trace.size() - max_trace_length); - } -} - -/// -/// \brief IsRobust -/// \param minTraceSize -/// \param minRawRatio -/// \param sizeRatio -/// \return -/// -bool CTrack::IsRobust(int minTraceSize, float minRawRatio, cv::Size2f sizeRatio) const -{ - bool res = m_trace.size() > static_cast(minTraceSize); - res &= m_trace.GetRawCount(m_trace.size() - 1) / static_cast(m_trace.size()) > minRawRatio; - if (sizeRatio.width + sizeRatio.height > 0) - { - float sr = m_lastRegion.m_rect.width / static_cast(m_lastRegion.m_rect.height); - if (sizeRatio.width > 0) - { - res &= (sr > sizeRatio.width); - } - if (sizeRatio.height > 0) - { - res &= (sr < sizeRatio.height); - } - } - return res; -} - -/// -/// \brief GetLastRect -/// \return -/// -cv::Rect CTrack::GetLastRect() const -{ - if (m_filterObjectSize) - { - return m_predictionRect; - } - else - { - return cv::Rect( - static_cast(m_predictionPoint.x - m_lastRegion.m_rect.width / 2), - static_cast(m_predictionPoint.y - m_lastRegion.m_rect.height / 2), - m_lastRegion.m_rect.width, - m_lastRegion.m_rect.height); - } -} - -/// -/// \brief RectUpdate -/// \param region -/// \param dataCorrect -/// \param prevFrame -/// \param currFrame -/// -void CTrack::RectUpdate( - const CRegion& region, - bool dataCorrect, - cv::UMat prevFrame, - cv::UMat currFrame - ) -{ - m_kalman->GetRectPrediction(); - - bool recalcPrediction = true; - - auto Clamp = [](int& v, int& size, int hi) - { - if (size < 2) - { - size = 2; - } - if (v < 0) - { - v = 0; - } - else if (v + size > hi - 1) - { - v = hi - 1 - size; - } - }; - - switch (m_externalTrackerForLost) - { - case tracking::TrackNone: - break; - - case tracking::TrackKCF: - case tracking::TrackMIL: - case tracking::TrackMedianFlow: - case tracking::TrackGOTURN: - case tracking::TrackMOSSE: -#ifdef USE_OCV_KCF - if (!dataCorrect) - { - cv::Size roiSize(std::max(2 * m_predictionRect.width, currFrame.cols / 4), std::min(2 * m_predictionRect.height, currFrame.rows / 4)); - if (roiSize.width > currFrame.cols) - { - roiSize.width = currFrame.cols; - } - if (roiSize.height > currFrame.rows) - { - roiSize.height = currFrame.rows; - } - cv::Point roiTL(m_predictionRect.x + m_predictionRect.width / 2 - roiSize.width / 2, m_predictionRect.y + m_predictionRect.height / 2 - roiSize.height / 2); - cv::Rect roiRect(roiTL, roiSize); - Clamp(roiRect.x, roiRect.width, currFrame.cols); - Clamp(roiRect.y, roiRect.height, currFrame.rows); - - if (!m_tracker || m_tracker.empty()) - { - CreateExternalTracker(); - - cv::Rect2d lastRect(m_predictionRect.x - roiRect.x, m_predictionRect.y - roiRect.y, m_predictionRect.width, m_predictionRect.height); - if (lastRect.x >= 0 && - lastRect.y >= 0 && - lastRect.x + lastRect.width < roiRect.width && - lastRect.y + lastRect.height < roiRect.height && - lastRect.area() > 0) - { - m_tracker->init(cv::UMat(prevFrame, roiRect), lastRect); - } - else - { - m_tracker.release(); - } - } - cv::Rect2d newRect; - if (!m_tracker.empty() && m_tracker->update(cv::UMat(currFrame, roiRect), newRect)) - { - cv::Rect prect(cvRound(newRect.x) + roiRect.x, cvRound(newRect.y) + roiRect.y, cvRound(newRect.width), cvRound(newRect.height)); - - m_predictionRect = m_kalman->Update(prect, true); - - recalcPrediction = false; - - m_boundidgRect = cv::Rect(); - m_lastRegion.m_points.clear(); - } - } - else - { - if (m_tracker && !m_tracker.empty()) - { - m_tracker.release(); - } - } -#else - std::cerr << "KCF tracker was disabled in CMAKE! Set lostTrackType = TrackNone in constructor." << std::endl; -#endif - break; - } - - if (recalcPrediction) - { - if (m_boundidgRect.area() > 0) - { - if (dataCorrect) - { - cv::Rect prect( - (m_boundidgRect.x + region.m_rect.x) / 2, - (m_boundidgRect.y + region.m_rect.y) / 2, - (m_boundidgRect.width + region.m_rect.width) / 2, - (m_boundidgRect.height + region.m_rect.height) / 2); - - m_predictionRect = m_kalman->Update(prect, dataCorrect); - } - else - { - cv::Rect prect( - (m_boundidgRect.x + m_predictionRect.x) / 2, - (m_boundidgRect.y + m_predictionRect.y) / 2, - (m_boundidgRect.width + m_predictionRect.width) / 2, - (m_boundidgRect.height + m_predictionRect.height) / 2); - - m_predictionRect = m_kalman->Update(prect, true); - } - } - else - { - m_predictionRect = m_kalman->Update(region.m_rect, dataCorrect); - } - } - - Clamp(m_predictionRect.x, m_predictionRect.width, currFrame.cols); - Clamp(m_predictionRect.y, m_predictionRect.height, currFrame.rows); - - m_predictionPoint = (m_predictionRect.tl() + m_predictionRect.br()) / 2; -} - -/// -/// \brief CreateExternalTracker -/// -void CTrack::CreateExternalTracker() -{ - switch (m_externalTrackerForLost) - { - case tracking::TrackNone: - break; - - case tracking::TrackKCF: -#ifdef USE_OCV_KCF - if (!m_tracker || m_tracker.empty()) - { - cv::TrackerKCF::Params params; - params.compressed_size = 1; - params.desc_pca = cv::TrackerKCF::GRAY; - params.desc_npca = cv::TrackerKCF::GRAY; - params.resize = true; - params.detect_thresh = 0.3; -#if (((CV_VERSION_MAJOR == 3) && (CV_VERSION_MINOR >= 3)) || (CV_VERSION_MAJOR > 3)) - m_tracker = cv::TrackerKCF::create(params); -#else - m_tracker = cv::TrackerKCF::createTracker(params); -#endif - } -#endif - break; - - case tracking::TrackMIL: -#ifdef USE_OCV_KCF - if (!m_tracker || m_tracker.empty()) - { - cv::TrackerMIL::Params params; - -#if (((CV_VERSION_MAJOR == 3) && (CV_VERSION_MINOR >= 3)) || (CV_VERSION_MAJOR > 3)) - m_tracker = cv::TrackerMIL::create(params); -#else - m_tracker = cv::TrackerMIL::createTracker(params); -#endif - } -#endif - break; - - case tracking::TrackMedianFlow: -#ifdef USE_OCV_KCF - if (!m_tracker || m_tracker.empty()) - { - cv::TrackerMedianFlow::Params params; - -#if (((CV_VERSION_MAJOR == 3) && (CV_VERSION_MINOR >= 3)) || (CV_VERSION_MAJOR > 3)) - m_tracker = cv::TrackerMedianFlow::create(params); -#else - m_tracker = cv::TrackerMedianFlow::createTracker(params); -#endif - } -#endif - break; - - case tracking::TrackGOTURN: -#ifdef USE_OCV_KCF - if (!m_tracker || m_tracker.empty()) - { - cv::TrackerGOTURN::Params params; - -#if (((CV_VERSION_MAJOR == 3) && (CV_VERSION_MINOR >= 3)) || (CV_VERSION_MAJOR > 3)) - m_tracker = cv::TrackerGOTURN::create(params); -#else - m_tracker = cv::TrackerGOTURN::createTracker(params); -#endif - } -#endif - break; - - case tracking::TrackMOSSE: -#ifdef USE_OCV_KCF - if (!m_tracker || m_tracker.empty()) - { -#if (((CV_VERSION_MAJOR == 3) && (CV_VERSION_MINOR >= 3)) || (CV_VERSION_MAJOR > 3)) - m_tracker = cv::TrackerMOSSE::create(); -#else - m_tracker = cv::TrackerMOSSE::createTracker(); -#endif - } -#endif - break; - } -} - -/// -/// \brief PointUpdate -/// \param pt -/// \param dataCorrect -/// -void CTrack::PointUpdate( - const Point_t& pt, - bool dataCorrect, - const cv::Size& frameSize - ) -{ - m_kalman->GetPointPrediction(); - - if (m_averagePoint.x + m_averagePoint.y > 0) - { - if (dataCorrect) - { - m_predictionPoint = m_kalman->Update((pt + m_averagePoint) / 2, dataCorrect); - } - else - { - m_predictionPoint = m_kalman->Update((m_predictionPoint + m_averagePoint) / 2, true); - } - } - else - { - m_predictionPoint = m_kalman->Update(pt, dataCorrect); - } - - auto Clamp = [](float& v, int hi) - { - if (v < 0) - { - v = 0; - } - else if (hi && v > hi - 1) - { - v = hi - 1; - } - }; - Clamp(m_predictionPoint.x, frameSize.width); - Clamp(m_predictionPoint.y, frameSize.height); -} diff --git a/Tracker/track.h b/Tracker/track.h deleted file mode 100644 index 795d2380d..000000000 --- a/Tracker/track.h +++ /dev/null @@ -1,213 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -#ifdef USE_OCV_KCF -#include -#endif - -#include "defines.h" -#include "Kalman.h" - -// -------------------------------------------------------------------------- -/// -/// \brief The TrajectoryPoint struct -/// -struct TrajectoryPoint -{ - /// - /// \brief TrajectoryPoint - /// - TrajectoryPoint() - : m_hasRaw(false) - { - } - - /// - /// \brief TrajectoryPoint - /// \param prediction - /// - TrajectoryPoint(const Point_t& prediction) - : - m_hasRaw(false), - m_prediction(prediction) - { - } - - /// - /// \brief TrajectoryPoint - /// \param prediction - /// \param raw - /// - TrajectoryPoint(const Point_t& prediction, const Point_t& raw) - : - m_hasRaw(true), - m_prediction(prediction), - m_raw(raw) - { - } - - bool m_hasRaw; - Point_t m_prediction; - Point_t m_raw; -}; - -// -------------------------------------------------------------------------- -/// -/// \brief The Trace class -/// -class Trace -{ -public: - /// - /// \brief operator [] - /// \param i - /// \return - /// - const Point_t& operator[](size_t i) const - { - return m_trace[i].m_prediction; - } - - /// - /// \brief operator [] - /// \param i - /// \return - /// - Point_t& operator[](size_t i) - { - return m_trace[i].m_prediction; - } - - /// - /// \brief at - /// \param i - /// \return - /// - const TrajectoryPoint& at(size_t i) const - { - return m_trace[i]; - } - - /// - /// \brief size - /// \return - /// - size_t size() const - { - return m_trace.size(); - } - - /// - /// \brief push_back - /// \param prediction - /// - void push_back(const Point_t& prediction) - { - m_trace.push_back(TrajectoryPoint(prediction)); - } - void push_back(const Point_t& prediction, const Point_t& raw) - { - m_trace.push_back(TrajectoryPoint(prediction, raw)); - } - - /// - /// \brief pop_front - /// \param count - /// - void pop_front(size_t count) - { - if (count < size()) - { - m_trace.erase(m_trace.begin(), m_trace.begin() + count); - } - else - { - m_trace.clear(); - } - } - - /// - /// \brief GetRawCount - /// \param lastPeriod - /// \return - /// - size_t GetRawCount(size_t lastPeriod) const - { - size_t res = 0; - - size_t i = 0; - if (lastPeriod < m_trace.size()) - { - i = m_trace.size() - lastPeriod; - } - for (; i < m_trace.size(); ++i) - { - if (m_trace[i].m_hasRaw) - { - ++res; - } - } - - return res; - } - -private: - std::deque m_trace; -}; - -// -------------------------------------------------------------------------- -/// -/// \brief The CTrack class -/// -class CTrack -{ -public: - CTrack(const CRegion& region, - tracking::KalmanType kalmanType, - track_t deltaTime, - track_t accelNoiseMag, - size_t trackID, - bool filterObjectSize, - tracking::LostTrackType externalTrackerForLost); - - track_t CalcDist(const Point_t& pt) const; - track_t CalcDist(const cv::Rect& r) const; - track_t CalcDistJaccard(const cv::Rect& r) const; - bool CheckType(const std::string& type) const; - - void Update(const CRegion& region, bool dataCorrect, size_t max_trace_length, cv::UMat prevFrame, cv::UMat currFrame); - - bool IsRobust(int minTraceSize, float minRawRatio, cv::Size2f sizeRatio) const; - - Trace m_trace; - size_t m_trackID; - size_t m_skippedFrames; - CRegion m_lastRegion; - Point_t m_averagePoint; ///< Average point after LocalTracking - cv::Rect m_boundidgRect; ///< Bounding rect after LocalTracking - - cv::Rect GetLastRect() const; - -private: - Point_t m_predictionPoint; - cv::Rect m_predictionRect; - TKalmanFilter* m_kalman; - bool m_filterObjectSize; - - tracking::LostTrackType m_externalTrackerForLost; -#ifdef USE_OCV_KCF - cv::Ptr m_tracker; -#endif - - void RectUpdate(const CRegion& region, bool dataCorrect, cv::UMat prevFrame, cv::UMat currFrame); - - void CreateExternalTracker(); - - void PointUpdate(const Point_t& pt, bool dataCorrect, const cv::Size& frameSize); -}; - -typedef std::vector> tracks_t; diff --git a/VideoExample.h b/VideoExample.h deleted file mode 100644 index 294355774..000000000 --- a/VideoExample.h +++ /dev/null @@ -1,648 +0,0 @@ -#pragma once - -#include "BaseDetector.h" - -#include "Ctracker.h" -#include -#include -#include - -#include "nms.h" - -// ---------------------------------------------------------------------- - -/// -/// \brief The VideoExample class -/// -class VideoExample -{ -public: - VideoExample(const cv::CommandLineParser& parser) - : - m_fps(25), - m_useLocalTracking(false) - { - m_inFile = parser.get(0); - m_outFile = parser.get("out"); - m_showLogs = parser.get("show_logs") != 0; - m_startFrame = parser.get("start_frame"); - m_endFrame = parser.get("end_frame"); - m_finishDelay = parser.get("end_delay"); - - m_colors.push_back(cv::Scalar(255, 0, 0)); - m_colors.push_back(cv::Scalar(0, 255, 0)); - m_colors.push_back(cv::Scalar(0, 0, 255)); - m_colors.push_back(cv::Scalar(255, 255, 0)); - m_colors.push_back(cv::Scalar(0, 255, 255)); - m_colors.push_back(cv::Scalar(255, 0, 255)); - m_colors.push_back(cv::Scalar(255, 127, 255)); - m_colors.push_back(cv::Scalar(127, 0, 255)); - m_colors.push_back(cv::Scalar(127, 0, 127)); - } - virtual ~VideoExample() - { - - } - - /// - /// \brief Process - /// - void Process() - { - cv::VideoWriter writer; - - cv::VideoCapture capture(m_inFile); - if (!capture.isOpened()) - { - std::cerr << "Can't open " << m_inFile << std::endl; - return; - } - cv::namedWindow("Video"); - cv::Mat frame; - cv::UMat gray; - - capture.set(cv::CAP_PROP_POS_FRAMES, m_startFrame); - - m_fps = std::max(1, cvRound(capture.get(cv::CAP_PROP_FPS))); - - capture >> frame; - cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY); - - if (!InitTracker(gray)) - { - return; - } - - int k = 0; - - double freq = cv::getTickFrequency(); - - int64 allTime = 0; - - bool manualMode = false; - int framesCounter = m_startFrame + 1; - while (k != 27) - { - capture >> frame; - if (frame.empty()) - { - break; - } - cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY); - - if (!writer.isOpened()) - { - writer.open(m_outFile, cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), capture.get(cv::CAP_PROP_FPS), frame.size(), true); - } - - int64 t1 = cv::getTickCount(); - - ProcessFrame(frame, gray); - - int64 t2 = cv::getTickCount(); - - allTime += t2 - t1; - int currTime = cvRound(1000 * (t2 - t1) / freq); - - DrawData(frame, framesCounter, currTime); - - cv::imshow("Video", frame); - - int waitTime = manualMode ? 0 : std::max(1, 1000 / m_fps - currTime); - k = cv::waitKey(waitTime); - - if (k == 'm' || k == 'M') - { - manualMode = !manualMode; - } - - if (writer.isOpened()) - { - writer << frame; - } - - ++framesCounter; - if (m_endFrame && framesCounter > m_endFrame) - { - break; - } - } - - std::cout << "work time = " << (allTime / freq) << std::endl; - cv::waitKey(m_finishDelay); - } - -protected: - std::unique_ptr m_detector; - std::unique_ptr m_tracker; - - virtual bool GrayProcessing() const - { - return true; - } - - virtual bool InitTracker(cv::UMat frame) = 0; - - /// - /// \brief ProcessFrame - /// \param grayFrame - /// - virtual void ProcessFrame(cv::Mat frame, cv::UMat grayFrame) - { - cv::UMat clFrame; - if (!GrayProcessing() || !m_tracker->GrayFrameToTrack()) - { - clFrame = frame.getUMat(cv::ACCESS_READ); - } - - m_detector->Detect(GrayProcessing() ? grayFrame : clFrame); - - const regions_t& regions = m_detector->GetDetects(); - - m_tracker->Update(regions, m_tracker->GrayFrameToTrack() ? grayFrame : clFrame); - } - - virtual void DrawData(cv::Mat frame, int framesCounter, int currTime) = 0; - - bool m_showLogs; - int m_fps; - bool m_useLocalTracking; - - /// - /// \brief DrawTrack - /// \param frame - /// \param resizeCoeff - /// \param track - /// - void DrawTrack(cv::Mat frame, - int resizeCoeff, - const CTrack& track - ) - { - auto ResizeRect = [&](const cv::Rect& r) -> cv::Rect - { - return cv::Rect(resizeCoeff * r.x, resizeCoeff * r.y, resizeCoeff * r.width, resizeCoeff * r.height); - }; - auto ResizePoint = [&](const cv::Point& pt) -> cv::Point - { - return cv::Point(resizeCoeff * pt.x, resizeCoeff * pt.y); - }; - - cv::rectangle(frame, ResizeRect(track.GetLastRect()), cv::Scalar(0, 255, 0), 1, CV_AA); - - cv::Scalar cl = m_colors[track.m_trackID % m_colors.size()]; - - for (size_t j = 0; j < track.m_trace.size() - 1; ++j) - { - const TrajectoryPoint& pt1 = track.m_trace.at(j); - const TrajectoryPoint& pt2 = track.m_trace.at(j + 1); - - cv::line(frame, ResizePoint(pt1.m_prediction), ResizePoint(pt2.m_prediction), cl, 1, CV_AA); - if (!pt2.m_hasRaw) - { - cv::circle(frame, ResizePoint(pt2.m_prediction), 4, cl, 1, CV_AA); - } - } - } - -private: - std::string m_inFile; - std::string m_outFile; - int m_startFrame; - int m_endFrame; - int m_finishDelay; - std::vector m_colors; -}; - -// ---------------------------------------------------------------------- - -/// -/// \brief The MotionDetectorExample class -/// -class MotionDetectorExample : public VideoExample -{ -public: - MotionDetectorExample(const cv::CommandLineParser& parser) - : - VideoExample(parser), - m_minObjWidth(10) - { - } - -protected: - /// - /// \brief InitTracker - /// \param grayFrame - /// - bool InitTracker(cv::UMat frame) - { - m_minObjWidth = frame.cols / 50; - - BaseDetector::config_t config; - m_detector = std::unique_ptr(CreateDetector(tracking::Detectors::Motion_MOG2, config, m_useLocalTracking, frame)); - m_detector->SetMinObjectSize(cv::Size(m_minObjWidth, m_minObjWidth)); - - m_tracker = std::make_unique(m_useLocalTracking, - tracking::DistCenters, - tracking::KalmanLinear, - tracking::FilterRect, - tracking::TrackKCF, // Use KCF tracker for collisions resolving - tracking::MatchHungrian, - 0.2f, // Delta time for Kalman filter - 0.1f, // Accel noise magnitude for Kalman filter - frame.rows / 10, // Distance threshold between region and object on two frames - m_fps, // Maximum allowed skipped frames - 3 * m_fps // Maximum trace length - ); - - return true; - } - - /// - /// \brief DrawData - /// \param frame - /// - void DrawData(cv::Mat frame, int framesCounter, int currTime) - { - if (m_showLogs) - { - std::cout << "Frame " << framesCounter << ": tracks = " << m_tracker->tracks.size() << ", time = " << currTime << std::endl; - } - - for (const auto& track : m_tracker->tracks) - { - if (track->IsRobust(m_fps / 2, // Minimal trajectory size - 0.6f, // Minimal ratio raw_trajectory_points / trajectory_lenght - cv::Size2f(0.1f, 8.0f)) // Min and max ratio: width / height - ) - { - DrawTrack(frame, 1, *track); - } - } - - m_detector->CalcMotionMap(frame); - } - -private: - int m_minObjWidth; -}; - -// ---------------------------------------------------------------------- - -/// -/// \brief The FaceDetectorExample class -/// -class FaceDetectorExample : public VideoExample -{ -public: - FaceDetectorExample(const cv::CommandLineParser& parser) - : - VideoExample(parser) - { - } - -protected: - /// - /// \brief InitTracker - /// \param grayFrame - /// - bool InitTracker(cv::UMat frame) - { - BaseDetector::config_t config; - config["cascadeFileName"] = "../data/haarcascade_frontalface_alt2.xml"; - m_detector = std::unique_ptr(CreateDetector(tracking::Detectors::Face_HAAR, config, m_useLocalTracking, frame)); - if (!m_detector.get()) - { - return false; - } - m_detector->SetMinObjectSize(cv::Size(frame.cols / 20, frame.rows / 20)); - - m_tracker = std::make_unique(m_useLocalTracking, - tracking::DistJaccard, - tracking::KalmanUnscented, - tracking::FilterRect, - tracking::TrackKCF, // Use KCF tracker for collisions resolving - tracking::MatchHungrian, - 0.3f, // Delta time for Kalman filter - 0.1f, // Accel noise magnitude for Kalman filter - 0.8f, // Distance threshold between region and object on two frames - m_fps / 2, // Maximum allowed skipped frames - 5 * m_fps // Maximum trace length - ); - - return true; - } - - /// - /// \brief DrawData - /// \param frame - /// - void DrawData(cv::Mat frame, int framesCounter, int currTime) - { - if (m_showLogs) - { - std::cout << "Frame " << framesCounter << ": tracks = " << m_tracker->tracks.size() << ", time = " << currTime << std::endl; - } - - for (const auto& track : m_tracker->tracks) - { - if (track->IsRobust(8, // Minimal trajectory size - 0.4f, // Minimal ratio raw_trajectory_points / trajectory_lenght - cv::Size2f(0.1f, 8.0f)) // Min and max ratio: width / height - ) - { - DrawTrack(frame, 1, *track); - } - } - - m_detector->CalcMotionMap(frame); - } -}; - -// ---------------------------------------------------------------------- - -/// -/// \brief The PedestrianDetectorExample class -/// -class PedestrianDetectorExample : public VideoExample -{ -public: - PedestrianDetectorExample(const cv::CommandLineParser& parser) - : - VideoExample(parser) - { - } - -protected: - /// - /// \brief InitTracker - /// \param grayFrame - /// - bool InitTracker(cv::UMat frame) - { - tracking::Detectors detectorType = tracking::Detectors::Pedestrian_C4; // tracking::Detectors::Pedestrian_HOG; - - BaseDetector::config_t config; - config["detectorType"] = (detectorType == tracking::Pedestrian_HOG) ? "HOG" : "C4"; - config["cascadeFileName1"] = "../data/combined.txt.model"; - config["cascadeFileName2"] = "../data/combined.txt.model_"; - m_detector = std::unique_ptr(CreateDetector(detectorType, config, m_useLocalTracking, frame)); - if (!m_detector.get()) - { - return false; - } - m_detector->SetMinObjectSize(cv::Size(frame.cols / 20, frame.rows / 20)); - - m_tracker = std::make_unique(m_useLocalTracking, - tracking::DistRects, - tracking::KalmanLinear, - tracking::FilterRect, - tracking::TrackKCF, // Use KCF tracker for collisions resolving - tracking::MatchHungrian, - 0.3f, // Delta time for Kalman filter - 0.1f, // Accel noise magnitude for Kalman filter - frame.rows / 10, // Distance threshold between region and object on two frames - 1 * m_fps, // Maximum allowed skipped frames - 5 * m_fps // Maximum trace length - ); - - return true; - } - - /// - /// \brief DrawData - /// \param frame - /// - void DrawData(cv::Mat frame, int framesCounter, int currTime) - { - if (m_showLogs) - { - std::cout << "Frame " << framesCounter << ": tracks = " << m_tracker->tracks.size() << ", time = " << currTime << std::endl; - } - - for (const auto& track : m_tracker->tracks) - { - if (track->IsRobust(m_fps / 2, // Minimal trajectory size - 0.4f, // Minimal ratio raw_trajectory_points / trajectory_lenght - cv::Size2f(0.1f, 8.0f)) // Min and max ratio: width / height - ) - { - DrawTrack(frame, 1, *track); - } - } - - m_detector->CalcMotionMap(frame); - } -}; - -// ---------------------------------------------------------------------- - -/// -/// \brief The HybridFaceDetectorExample class -/// -class HybridFaceDetectorExample : public VideoExample -{ -public: - HybridFaceDetectorExample(const cv::CommandLineParser& parser) - : - VideoExample(parser) - { - } - -protected: - /// - /// \brief InitTracker - /// \param grayFrame - /// - bool InitTracker(cv::UMat frame) - { - std::string fileName = "../data/haarcascade_frontalface_alt2.xml"; - m_cascade.load(fileName); - if (m_cascade.empty()) - { - std::cerr << "Cascade not opened!" << std::endl; - return false; - } - - m_tracker = std::make_unique(m_useLocalTracking, - tracking::DistCenters, - tracking::KalmanUnscented, - tracking::FilterRect, - tracking::TrackKCF, // Use KCF tracker for collisions resolving - tracking::MatchHungrian, - 0.3f, // Delta time for Kalman filter - 0.1f, // Accel noise magnitude for Kalman filter - frame.cols / 10, // Distance threshold between region and object on two frames - 2 * m_fps, // Maximum allowed skipped frames - 5 * m_fps // Maximum trace length - ); - - BaseDetector::config_t config; - m_detector = std::unique_ptr(CreateDetector(tracking::Detectors::Motion_MOG2, config, m_useLocalTracking, frame)); - m_detector->SetMinObjectSize(cv::Size(frame.cols / 50, frame.rows / 50)); - - return true; - } - - /// - /// \brief ProcessFrame - /// \param grayFrame - /// - void ProcessFrame(cv::Mat /*frame*/, cv::UMat grayFrame) - { - bool findLargestObject = false; - bool filterRects = true; - std::vector faceRects; - m_cascade.detectMultiScale(grayFrame, - faceRects, - 1.1, - (filterRects || findLargestObject) ? 3 : 0, - findLargestObject ? cv::CASCADE_FIND_BIGGEST_OBJECT : 0, - cv::Size(grayFrame.cols / 20, grayFrame.rows / 20), - cv::Size(grayFrame.cols / 2, grayFrame.rows / 2)); - - m_detector->Detect(grayFrame); - const regions_t& regions1 = m_detector->GetDetects(); - - std::vector scores(faceRects.size(), 0.5f); - for (const auto& reg : regions1) - { - faceRects.push_back(reg.m_rect); - scores.push_back(0.4f); - } - faceRects.insert(faceRects.end(), m_prevRects.begin(), m_prevRects.end()); - scores.insert(scores.end(), m_prevRects.size(), 0.4f); - std::vector allRects; - nms2(faceRects, scores, allRects, 0.3f, 1, 0.7); - - regions_t regions; - for (auto rect : allRects) - { - regions.push_back(rect); - } - - m_tracker->Update(regions, grayFrame); - } - - /// - /// \brief DrawData - /// \param frame - /// - void DrawData(cv::Mat frame, int framesCounter, int currTime) - { - if (m_showLogs) - { - std::cout << "Frame " << framesCounter << ": tracks = " << m_tracker->tracks.size() << ", time = " << currTime << std::endl; - } - - m_prevRects.clear(); - - for (const auto& track : m_tracker->tracks) - { - if (track->IsRobust(4, // Minimal trajectory size - 0.4f, // Minimal ratio raw_trajectory_points / trajectory_lenght - cv::Size2f(0.4f, 3.0f)) // Min and max ratio: width / height - ) - { - DrawTrack(frame, 1, *track); - m_prevRects.push_back(track->GetLastRect()); - } - } - - m_detector->CalcMotionMap(frame); - } - -private: - std::vector m_prevRects; - cv::CascadeClassifier m_cascade; -}; - -// ---------------------------------------------------------------------- - -/// -/// \brief The DNNDetectorExample class -/// -class DNNDetectorExample : public VideoExample -{ -public: - DNNDetectorExample(const cv::CommandLineParser& parser) - : - VideoExample(parser) - { - } - -protected: - /// - /// \brief InitTracker - /// \param grayFrame - /// - bool InitTracker(cv::UMat frame) - { - BaseDetector::config_t config; - config["modelConfiguration"] = "../data/MobileNetSSD_deploy.prototxt"; - config["modelBinary"] = "../data/MobileNetSSD_deploy.caffemodel"; - config["confidenceThreshold"] = "0.2"; - m_detector = std::unique_ptr(CreateDetector(tracking::Detectors::DNN, config, m_useLocalTracking, frame)); - if (!m_detector.get()) - { - return false; - } - m_detector->SetMinObjectSize(cv::Size(frame.cols / 20, frame.rows / 20)); - - m_tracker = std::make_unique(m_useLocalTracking, - tracking::DistRects, - tracking::KalmanLinear, - tracking::FilterRect, - tracking::TrackKCF, // Use KCF tracker for collisions resolving - tracking::MatchHungrian, - 0.3f, // Delta time for Kalman filter - 0.1f, // Accel noise magnitude for Kalman filter - frame.rows / 10, // Distance threshold between region and object on two frames - 2 * m_fps, // Maximum allowed skipped frames - 5 * m_fps // Maximum trace length - ); - - return true; - } - - /// - /// \brief DrawData - /// \param frame - /// - void DrawData(cv::Mat frame, int framesCounter, int currTime) - { - if (m_showLogs) - { - std::cout << "Frame " << framesCounter << ": tracks = " << m_tracker->tracks.size() << ", time = " << currTime << std::endl; - } - - for (const auto& track : m_tracker->tracks) - { - if (track->IsRobust(5, // Minimal trajectory size - 0.5f, // Minimal ratio raw_trajectory_points / trajectory_lenght - cv::Size2f(0.1f, 8.0f)) // Min and max ratio: width / height - ) - { - DrawTrack(frame, 1, *track); - - std::string label = track->m_lastRegion.m_type + ": " + std::to_string(track->m_lastRegion.m_confidence); - int baseLine = 0; - cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); - auto rect(track->GetLastRect()); - cv::rectangle(frame, cv::Rect(cv::Point(rect.x, rect.y - labelSize.height), cv::Size(labelSize.width, labelSize.height + baseLine)), cv::Scalar(255, 255, 255), CV_FILLED); - cv::putText(frame, label, cv::Point(rect.x, rect.y), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0)); - } - } - - m_detector->CalcMotionMap(frame); - } - - /// - /// \brief GrayProcessing - /// \return - /// - bool GrayProcessing() const - { - return false; - } -}; diff --git a/async_detector/AsyncDetector.cpp b/async_detector/AsyncDetector.cpp new file mode 100644 index 000000000..58854ed2e --- /dev/null +++ b/async_detector/AsyncDetector.cpp @@ -0,0 +1,425 @@ +#include "AsyncDetector.h" + +/// +/// \brief AsyncDetector::AsyncDetector +/// \param parser +/// +AsyncDetector::AsyncDetector(const cv::CommandLineParser& parser) +{ + m_inFile = parser.get(0); + m_outFile = parser.get("out"); + m_showLogsLevel = parser.get("show_logs"); + m_startFrame = parser.get("start_frame"); + m_endFrame = parser.get("end_frame"); + m_finishDelay = parser.get("end_delay"); + + m_colors.emplace_back(255, 0, 0); + m_colors.emplace_back(0, 255, 0); + m_colors.emplace_back(0, 0, 255); + m_colors.emplace_back(255, 255, 0); + m_colors.emplace_back(0, 255, 255); + m_colors.emplace_back(255, 0, 255); + m_colors.emplace_back(255, 127, 255); + m_colors.emplace_back(127, 0, 255); + m_colors.emplace_back(127, 0, 127); + + // Create loggers + m_consoleSink = std::make_shared(); + m_consoleSink->set_level(spdlog::level::from_str(m_showLogsLevel)); + m_consoleSink->set_pattern("[%^%l%$] %v"); + + auto currentTime = std::chrono::system_clock::now(); + auto transformed = currentTime.time_since_epoch().count() / 1000000; + std::time_t tt = std::chrono::system_clock::to_time_t(currentTime); + char buffer[80]; +#ifdef WIN32 + tm timeInfo; + localtime_s(&timeInfo, &tt); + strftime(buffer, 80, "%G%m%d_%H%M%S", &timeInfo); +#else + auto timeInfo = localtime(&tt); + strftime(buffer, 80, "%G%m%d_%H%M%S", timeInfo); +#endif + + size_t max_size = 1024 * 1024 * 5; + size_t max_files = 3; + m_fileSink = std::make_shared("logs/" + std::string(buffer) + std::to_string(transformed % 1000) + ".txt", max_size, max_files); + m_fileSink->set_level(spdlog::level::from_str(m_showLogsLevel)); + + m_logger = std::shared_ptr(new spdlog::logger("traffic", { m_consoleSink, m_fileSink })); + m_logger->set_level(spdlog::level::from_str(m_showLogsLevel)); + m_logger->info("Start service"); +} + +/// +/// \brief AsyncDetector::Process +/// +void AsyncDetector::Process() +{ + double freq = cv::getTickFrequency(); + int64 allTime = 0; + + bool stopFlag = false; + + std::thread thCapture(CaptureThread, m_inFile, m_startFrame, &m_framesCount, &m_fps, &m_framesQue, &stopFlag); + +#ifndef SILENT_WORK + cv::namedWindow("Video", cv::WINDOW_NORMAL | cv::WINDOW_KEEPRATIO); + cv::waitKey(1); +#endif + + cv::VideoWriter writer; + + int framesCounter = m_startFrame + 1; + + for (; !stopFlag;) + { + int64 t1 = cv::getTickCount(); + + // Show frame after detecting and tracking + frame_ptr processedFrame = m_framesQue.GetFirstProcessedFrame(); + if (!processedFrame) + { + stopFlag = true; + break; + } + + int64 t2 = cv::getTickCount(); + + allTime += t2 - processedFrame->m_dt; + int currTime = cvRound(1000 * (t2 - t1) / freq); + + DrawData(processedFrame, framesCounter, currTime); + + if (!m_outFile.empty()) + { + if (!writer.isOpened()) + writer.open(m_outFile, cv::VideoWriter::fourcc('H', 'F', 'Y', 'U'), m_fps, processedFrame->m_frame.size(), true); + if (writer.isOpened()) + writer << processedFrame->m_frame; + } + +#ifndef SILENT_WORK + cv::imshow("Video", processedFrame->m_frame); + + int waitTime = 1;// std::max(1, cvRound(1000 / m_fps - currTime)); + int k = cv::waitKey(waitTime); + if (k == 27) + break; +#else + //std::this_thread::sleep_for(std::chrono::milliseconds(1)); +#endif + + ++framesCounter; + if (m_endFrame && framesCounter > m_endFrame) + { + m_logger->info("Process: riched last {} frame", m_endFrame); + break; + } + } + + m_logger->info("Stopping threads..."); + stopFlag = true; + m_framesQue.SetBreak(true); + + if (thCapture.joinable()) + thCapture.join(); + + m_logger->info("work time = {}", allTime / freq); +#ifndef SILENT_WORK + cv::waitKey(m_finishDelay); +#endif +} + +/// +/// \brief AsyncDetector::DrawTrack +/// \param frame +/// \param track +/// \param drawTrajectory +/// \param isStatic +/// +void AsyncDetector::DrawTrack(cv::Mat frame, + const TrackingObject& track, + bool drawTrajectory) +{ + if (track.m_isStatic) + { +#if (CV_VERSION_MAJOR >= 4) + cv::rectangle(frame, track.m_rrect.boundingRect(), cv::Scalar(255, 0, 255), 2, cv::LINE_AA); +#else + cv::rectangle(frame, track.m_rrect.boundingRect(), cv::Scalar(255, 0, 255), 2, CV_AA); +#endif + } + else + { +#if (CV_VERSION_MAJOR >= 4) + cv::rectangle(frame, track.m_rrect.boundingRect(), cv::Scalar(0, 255, 0), 1, cv::LINE_AA); +#else + cv::rectangle(frame, track.m_rrect.boundingRect(), cv::Scalar(0, 255, 0), 1, CV_AA); +#endif + } + + if (drawTrajectory) + { + cv::Scalar cl = m_colors[track.m_ID.ID2Module(m_colors.size())]; + + for (size_t j = 0; j < track.m_trace.size() - 1; ++j) + { + const TrajectoryPoint& pt1 = track.m_trace.at(j); + const TrajectoryPoint& pt2 = track.m_trace.at(j + 1); +#if (CV_VERSION_MAJOR >= 4) + cv::line(frame, pt1.m_prediction, pt2.m_prediction, cl, 1, cv::LINE_AA); +#else + cv::line(frame, pt1.m_prediction, pt2.m_prediction, cl, 1, CV_AA); +#endif + if (pt2.m_hasRaw) + { +#if (CV_VERSION_MAJOR >= 4) + cv::circle(frame, pt2.m_prediction, 4, cl, 4, cv::LINE_AA); +#else + cv::circle(frame, pt2.m_prediction, 4, cl, 4, CV_AA); +#endif + } + } + } +} + +/// +/// \brief AsyncDetector::DrawData +/// \param frameinfo +/// +void AsyncDetector::DrawData(frame_ptr frameInfo, int framesCounter, int currTime) +{ + int id = frameInfo->m_inDetector.load(); + if (id != FrameInfo::StateNotProcessed && id != FrameInfo::StateSkipped) + m_logger->info("Frame {0} ({1}): ({2}) detects= {3}, tracks = {4}, time = {5}", framesCounter, m_framesCount, id, frameInfo->m_regions.size(), frameInfo->m_tracks.size(), currTime); + else + m_logger->info("Frame {0} ({1}): tracks = {2}, time = {3}", framesCounter, m_framesCount, frameInfo->m_tracks.size(), currTime); + + for (const auto& track : frameInfo->m_tracks) + { + if (track.m_isStatic) + { + DrawTrack(frameInfo->m_frame, track, true); + } + else + { + if (track.IsRobust(1, // Minimal trajectory size + 0.3f, // Minimal ratio raw_trajectory_points / trajectory_lenght + cv::Size2f(0.1f, 8.0f))) // Min and max ratio: width / height + { + //std::cout << TypeConverter::Type2Str(track.m_type) << " - " << track.m_rect << std::endl; + + DrawTrack(frameInfo->m_frame, track, true); + + std::string label = TypeConverter::Type2Str(track.m_type);// +": " + std::to_string(track.m_confidence); + int baseLine = 0; + cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); + + cv::Rect brect = track.m_rrect.boundingRect(); + if (brect.x < 0) + { + brect.width = std::min(brect.width, frameInfo->m_frame.cols - 1); + brect.x = 0; + } + else if (brect.x + brect.width >= frameInfo->m_frame.cols) + { + brect.x = std::max(0, frameInfo->m_frame.cols - brect.width - 1); + brect.width = std::min(brect.width, frameInfo->m_frame.cols - 1); + } + if (brect.y - labelSize.height < 0) + { + brect.height = std::min(brect.height, frameInfo->m_frame.rows - 1); + brect.y = labelSize.height; + } + else if (brect.y + brect.height >= frameInfo->m_frame.rows) + { + brect.y = std::max(0, frameInfo->m_frame.rows - brect.height - 1); + brect.height = std::min(brect.height, frameInfo->m_frame.rows - 1); + } + DrawFilledRect(frameInfo->m_frame, cv::Rect(cv::Point(brect.x, brect.y - labelSize.height), cv::Size(labelSize.width, labelSize.height + baseLine)), cv::Scalar(200, 200, 200), 150); + cv::putText(frameInfo->m_frame, label, brect.tl(), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0)); + } + } + } +} + +/// +/// \brief AsyncDetector::CaptureThread +/// \param fileName +/// \param framesQue +/// \param stopFlag +/// +void AsyncDetector::CaptureThread(std::string fileName, int startFrame, int* framesCount, float* fps, FramesQueue* framesQue, bool* stopFlag) +{ + cv::VideoCapture capture; + if (fileName.size() == 1) + capture.open(atoi(fileName.c_str())); + else + capture.open(fileName); + + if (!capture.isOpened()) + { + *stopFlag = true; + std::cerr << "Can't open " << fileName << std::endl; + return; + } + *framesCount = cvRound(capture.get(cv::CAP_PROP_FRAME_COUNT)); + capture.set(cv::CAP_PROP_POS_FRAMES, startFrame); + + time_point_t startTimeStamp = std::chrono::system_clock::now(); + + *fps = std::max(1.f, (float)capture.get(cv::CAP_PROP_FPS)); + int frameHeight = cvRound(capture.get(cv::CAP_PROP_FRAME_HEIGHT)); + + // Detector + config_t detectorConfig; + +#ifdef _WIN32 + std::string pathToModel = "../../data/"; +#else + std::string pathToModel = "../data/"; +#endif + +#if 0 + detectorConfig.emplace("modelConfiguration", pathToModel + "yolov3-tiny.cfg"); + detectorConfig.emplace("modelBinary", pathToModel + "yolov3-tiny.weights"); +#else + detectorConfig.emplace("modelConfiguration", pathToModel + "yolov4-csp.cfg"); + detectorConfig.emplace("modelBinary", pathToModel + "yolov4-csp.weights"); +#endif + detectorConfig.emplace("classNames", pathToModel + "coco.names"); + detectorConfig.emplace("confidenceThreshold", "0.3"); + detectorConfig.emplace("maxCropRatio", "3.0"); + +#if 1 + detectorConfig.emplace("white_list", "person"); + detectorConfig.emplace("white_list", "car"); + detectorConfig.emplace("white_list", "bicycle"); + detectorConfig.emplace("white_list", "motorbike"); + detectorConfig.emplace("white_list", "bus"); + detectorConfig.emplace("white_list", "truck"); +#endif + + // Tracker + const int minStaticTime = 5; + + TrackerSettings trackerSettings; + trackerSettings.SetDistance(tracking::DistCenters); + trackerSettings.m_kalmanType = tracking::KalmanLinear; + trackerSettings.m_filterGoal = tracking::FilterRect; + trackerSettings.m_lostTrackType = tracking::TrackCSRT; // Use KCF tracker for collisions resolving. Used if m_filterGoal == tracking::FilterRect + trackerSettings.m_matchType = tracking::MatchHungrian; + trackerSettings.m_dt = 0.2f; // Delta time for Kalman filter + trackerSettings.m_accelNoiseMag = 0.3f; // Accel noise magnitude for Kalman filter + trackerSettings.m_distThres = 0.8f; // Distance threshold between region and object on two frames + trackerSettings.m_minAreaRadiusPix = frameHeight / 20.f; + + trackerSettings.m_useAbandonedDetection = false; + if (trackerSettings.m_useAbandonedDetection) + { + trackerSettings.m_minStaticTime = minStaticTime; + trackerSettings.m_maxStaticTime = 60; + trackerSettings.m_maximumAllowedLostTime = trackerSettings.m_minStaticTime; // Maximum allowed lost time + trackerSettings.m_maxTraceLength = 2 * trackerSettings.m_maximumAllowedLostTime; // Maximum trace length + } + else + { + trackerSettings.m_maximumAllowedLostTime = 2.; // Maximum allowed lost time + trackerSettings.m_maxTraceLength = 4.; // Maximum trace length + } + + // Capture the first frame + size_t frameInd = startFrame; + cv::Mat firstFrame; + capture >> firstFrame; + ++frameInd; + + std::thread thDetection(DetectThread, detectorConfig, firstFrame, framesQue, stopFlag); + std::thread thTracking(TrackingThread, trackerSettings, framesQue, *fps, stopFlag); + + // Capture frame + for (; !(*stopFlag);) + { + frame_ptr frameInfo(new FrameInfo(frameInd)); + frameInfo->m_dt = cv::getTickCount(); + frameInfo->m_frameTimeStamp = startTimeStamp + std::chrono::milliseconds(cvRound(frameInd * (1000.f / (*fps)))); + capture >> frameInfo->m_frame; + if (frameInfo->m_frame.empty()) + { + std::cerr << "Frame is empty!" << std::endl; + *stopFlag = true; + framesQue->SetBreak(true); + break; + } + if (frameInfo->m_clFrame.empty()) + frameInfo->m_clFrame = frameInfo->m_frame.getUMat(cv::ACCESS_READ); + + framesQue->AddNewFrame(frameInfo, 15); + +#if 1 + //std::this_thread::sleep_for(std::chrono::milliseconds(1000 / cvRound(*fps) - 1)); +#else + //std::this_thread::sleep_for(std::chrono::milliseconds(1)); +#endif + + ++frameInd; + } + + framesQue->SetBreak(true); + if (thTracking.joinable()) + thTracking.join(); + + framesQue->SetBreak(true); + if (thDetection.joinable()) + thDetection.join(); + + framesQue->SetBreak(true); +} + +/// +/// \brief AsyncDetector::DetectThread +/// \param +/// +void AsyncDetector::DetectThread(const config_t& config, cv::Mat firstFrame, FramesQueue* framesQue, bool* stopFlag) +{ + cv::UMat ufirst = firstFrame.getUMat(cv::ACCESS_READ); + std::unique_ptr detector = BaseDetector::CreateDetector(tracking::Detectors::ONNX_TensorRT, config, ufirst); + + for (; !(*stopFlag);) + { + frame_ptr frameInfo = framesQue->GetLastUndetectedFrame(); + if (frameInfo) + { + detector->Detect(frameInfo->m_clFrame); + + const regions_t& regions = detector->GetDetects(); + frameInfo->m_regions.assign(regions.begin(), regions.end()); + + frameInfo->m_inDetector.store(FrameInfo::StateCompleted); + framesQue->Signal(frameInfo->m_dt); + } + } +} + +/// +/// \brief AsyncDetector::TrackingThread +/// \param +/// +void AsyncDetector::TrackingThread(const TrackerSettings& settings, FramesQueue* framesQue, float fps, bool* stopFlag) +{ + std::unique_ptr tracker = BaseTracker::CreateTracker(settings, fps); + + for (; !(*stopFlag);) + { + frame_ptr frameInfo = framesQue->GetFirstDetectedFrame(); + if (frameInfo) + { + tracker->Update(frameInfo->m_regions, frameInfo->m_clFrame, frameInfo->m_frameTimeStamp); + + tracker->GetTracks(frameInfo->m_tracks); + frameInfo->m_inTracker.store(FrameInfo::StateCompleted); + framesQue->Signal(frameInfo->m_dt); + } + } +} diff --git a/async_detector/AsyncDetector.h b/async_detector/AsyncDetector.h new file mode 100644 index 000000000..9a0a2a763 --- /dev/null +++ b/async_detector/AsyncDetector.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BaseDetector.h" +#include "BaseTracker.h" + +#include "spdlog/spdlog.h" +#include "spdlog/async.h" +#include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/sinks/basic_file_sink.h" +#include "spdlog/sinks/rotating_file_sink.h" + +// ---------------------------------------------------------------------- + +/// +/// \brief The FrameInfo class +/// +struct FrameInfo +{ + cv::Mat m_frame; + cv::UMat m_clFrame; + regions_t m_regions; + std::vector m_tracks; + time_point_t m_frameTimeStamp; + int64 m_dt = 0; + size_t m_frameInd = 0; + + static constexpr int StateNotProcessed = 0; + static constexpr int StateInProcess = 1; + static constexpr int StateCompleted = 2; + static constexpr int StateSkipped = 3; + + std::atomic m_inDetector; // 0 - not in Detector, 1 - detector started processing, 2 - objects was detected, 3 - detector skip this frame + std::atomic m_inTracker; // 0 - not in Tracker, 1 - tracker started processing, 2 - objects was tracked + + FrameInfo(size_t frameInd) + : m_frameInd(frameInd), m_inDetector(StateNotProcessed), m_inTracker(StateNotProcessed) + { + } +}; + +#include "Queue.h" + +// ---------------------------------------------------------------------- + +/// +/// \brief The AsyncDetector class +/// +class AsyncDetector +{ +public: + AsyncDetector(const cv::CommandLineParser& parser); + ~AsyncDetector() = default; + + void Process(); + +private: + std::string m_showLogsLevel = "info"; + float m_fps = 25; + + std::string m_inFile; + std::string m_outFile; + int m_startFrame = 0; + int m_endFrame = 0; + int m_finishDelay = 0; + int m_framesCount = 0; + std::vector m_colors; + + FramesQueue m_framesQue; + + void DrawData(frame_ptr frameInfo, int framesCounter, int currTime); + void DrawTrack(cv::Mat frame, const TrackingObject& track, bool drawTrajectory = true); + + static void CaptureThread(std::string fileName, int startFrame, int* framesCount, float* fps, FramesQueue* framesQue, bool* stopFlag); + static void DetectThread(const config_t& config, cv::Mat firstFrame, FramesQueue* framesQue, bool* stopFlag); + static void TrackingThread(const TrackerSettings& settings, FramesQueue* framesQue, float fps, bool* stopFlag); + + std::shared_ptr m_consoleSink; + std::shared_ptr m_fileSink; + std::shared_ptr m_logger; +}; diff --git a/async_detector/CMakeLists.txt b/async_detector/CMakeLists.txt new file mode 100644 index 000000000..2a552a621 --- /dev/null +++ b/async_detector/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required (VERSION 3.5) + +project(AsyncDetector) + +set(SOURCES main.cpp + AsyncDetector.cpp) + +set(HEADERS AsyncDetector.h + Queue.h) + +# ---------------------------------------------------------------------------- +# добавляем include директории +# ---------------------------------------------------------------------------- +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/../src + ${PROJECT_SOURCE_DIR}/../src/mtracking + ${PROJECT_SOURCE_DIR}/../src/Detector + ${PROJECT_SOURCE_DIR}/../src/Detector/vibe_src + ${PROJECT_SOURCE_DIR}/../src/Detector/Subsense + ${PROJECT_SOURCE_DIR}/../src/Tracker + ${PROJECT_SOURCE_DIR}/../src/Tracker/HungarianAlg + ${PROJECT_SOURCE_DIR}/../thirdparty/spdlog/include) + +set(LIBS ${OpenCV_LIBS} + mtracking + mdetection) + +ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCES} ${HEADERS}) + +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE ${LIBS}) + +set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "apps") \ No newline at end of file diff --git a/async_detector/Queue.h b/async_detector/Queue.h new file mode 100644 index 000000000..46aa468f0 --- /dev/null +++ b/async_detector/Queue.h @@ -0,0 +1,271 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SHOW_QUE_LOG 0 +#if SHOW_QUE_LOG +/// +/// \brief currTime +/// \return +/// +inline std::string CurrTime_() +{ + auto t = std::time(nullptr); + auto tm = *std::localtime(&t); + + std::ostringstream oss; + oss << std::put_time(&tm, "%d.%m.%Y %H:%M:%S:"); + return oss.str(); +} + +#define QUE_LOG std::cout << CurrTime_() +#define QUE_ERR_LOG (std::cerr << CurrTime_()) +#endif + +struct FrameInfo; +typedef std::shared_ptr frame_ptr; +/// +/// A threadsafe-queue with Frames +/// +class FramesQueue +{ +private: + typedef std::list queue_t; + +public: + /// + /// \brief FramesQueue + /// + FramesQueue() + : m_que(), + m_mutex(), + m_cond(), + m_break(false) + {} + + FramesQueue(const FramesQueue&) = delete; + FramesQueue(FramesQueue&&) = delete; + FramesQueue& operator=(const FramesQueue&) = delete; + FramesQueue& operator=(FramesQueue&&) = delete; + + /// + ~FramesQueue(void) = default; + + /// + /// \brief AddNewFrame + /// \param frameInfo + /// + void AddNewFrame(frame_ptr frameInfo, size_t maxQueueSize) + { +#if SHOW_QUE_LOG + QUE_LOG << "AddNewFrame start: " << frameInfo->m_dt << std::endl; +#endif + std::lock_guard lock(m_mutex); + + if (!maxQueueSize || (maxQueueSize > 0 && m_que.size() < maxQueueSize)) + m_que.push_back(frameInfo); + +#if SHOW_QUE_LOG + QUE_LOG << "AddNewFrame end: " << frameInfo->m_dt << ", frameInd " << frameInfo->m_frameInd << ", queue size " << m_que.size() << std::endl; +#endif + + m_cond.notify_all(); + } + +#if SHOW_QUE_LOG + /// + /// \brief PrintQue + /// + void PrintQue() + { + QUE_LOG << "m_que (" << m_que.size() << "): "; + size_t i = 0; + for (auto it : m_que) + { + if (it->m_inDetector.load() != FrameInfo::StateNotProcessed && it->m_inTracker.load() != FrameInfo::StateNotProcessed) + std::cout << i << " d" << it->m_inDetector.load() << " t" << it->m_inTracker.load() << "; "; + else if (it->m_inDetector.load() != FrameInfo::StateNotProcessed) + std::cout << i << " d" << it->m_inDetector.load() << "; "; + else if (it->m_inTracker.load() != FrameInfo::StateNotProcessed) + std::cout << i << " t" << it->m_inTracker.load() << "; "; + else + std::cout << i << "; "; + + ++i; + } + std::cout << std::endl; + } +#endif + /// + /// \brief GetLastUndetectedFrame + /// \return + /// + frame_ptr GetLastUndetectedFrame() + { +#if SHOW_QUE_LOG + QUE_LOG << "GetLastUndetectedFrame start" << std::endl; +#endif + std::unique_lock lock(m_mutex); + while (m_que.empty() || m_que.back()->m_inDetector.load() != FrameInfo::StateNotProcessed) + { + if (m_break.load()) + break; + + m_cond.wait(lock); + //PrintQue(); + } + if (!m_break.load()) + { + frame_ptr frameInfo = m_que.back(); + assert(frameInfo->m_inDetector.load() == FrameInfo::StateNotProcessed); + assert(frameInfo->m_inTracker.load() == FrameInfo::StateNotProcessed); + frameInfo->m_inDetector.store(FrameInfo::StateInProcess); + + queue_t::reverse_iterator it = m_que.rbegin(); + for (++it; it != m_que.rend(); ++it) + { + if ((*it)->m_inDetector.load() == FrameInfo::StateNotProcessed) + (*it)->m_inDetector.store(FrameInfo::StateSkipped); + else + break; + } +#if SHOW_QUE_LOG + PrintQue(); + QUE_LOG << "GetLastUndetectedFrame end: " << frameInfo->m_dt << ", frameInd " << frameInfo->m_frameInd << std::endl; +#endif + return frameInfo; + } + return nullptr; + } + + /// + /// \brief SearchUntracked + /// \return + /// + queue_t::iterator SearchUntracked() + { + queue_t::iterator res_it = m_que.end(); + for (queue_t::iterator it = m_que.begin(); it != m_que.end(); ++it) + { + if ((*it)->m_inDetector.load() == FrameInfo::StateInProcess || (*it)->m_inDetector.load() == FrameInfo::StateNotProcessed) + { + break; + } + else if ((*it)->m_inTracker.load() == FrameInfo::StateNotProcessed) + { + res_it = it; + break; + } + } + return res_it; + } + + /// + /// \brief GetFirstDetectedFrame + /// \return + /// + frame_ptr GetFirstDetectedFrame() + { +#if SHOW_QUE_LOG + QUE_LOG << "GetFirstDetectedFrame start" << std::endl; +#endif + std::unique_lock lock(m_mutex); + queue_t::iterator it = SearchUntracked(); + while (it == m_que.end()) + { + if (m_break.load()) + break; + + m_cond.wait(lock); + it = SearchUntracked(); + //PrintQue(); + } + if (!m_break.load()) + { + frame_ptr frameInfo = *it; + assert(frameInfo->m_inTracker.load() == FrameInfo::StateNotProcessed); + assert(frameInfo->m_inDetector.load() != FrameInfo::StateInProcess && frameInfo->m_inDetector.load() != FrameInfo::StateNotProcessed); + frameInfo->m_inTracker.store(FrameInfo::StateInProcess); +#if SHOW_QUE_LOG + QUE_LOG << "GetFirstDetectedFrame end: " << frameInfo->m_dt << ", frameInd " << frameInfo->m_frameInd << std::endl; +#endif + return frameInfo; + } + return nullptr; + } + + /// + /// \brief GetFirstProcessedFrame + /// \return + /// + frame_ptr GetFirstProcessedFrame() + { +#if SHOW_QUE_LOG + QUE_LOG << "GetFirstProcessedFrame start" << std::endl; +#endif + std::unique_lock lock(m_mutex); + while (m_que.empty() || m_que.front()->m_inTracker.load() != FrameInfo::StateCompleted) + { + if (m_break.load()) + break; + + m_cond.wait(lock); + //PrintQue(); + } + if (!m_break.load()) + { + frame_ptr frameInfo = std::move(m_que.front()); + m_que.pop_front(); +#if SHOW_QUE_LOG + QUE_LOG << "GetFirstProcessedFrame end: " << frameInfo->m_dt << ", frameInd " << frameInfo->m_frameInd << std::endl; +#endif + return frameInfo; + } + return nullptr; + } + + /// + /// \brief Signal + /// + void Signal( +#if SHOW_QUE_LOG + int64 ts +#else + int64 /*ts*/ +#endif + ) + { +#if SHOW_QUE_LOG + QUE_LOG << "Signal start:" << ts << std::endl; +#endif + m_cond.notify_all(); +#if SHOW_QUE_LOG + QUE_LOG << "Signal end: " << ts << std::endl; +#endif + } + + void SetBreak(bool val) + { +#if SHOW_QUE_LOG + QUE_LOG << "SetBreak start:" << val << std::endl; +#endif + m_break = val; + Signal(0); +#if SHOW_QUE_LOG + QUE_LOG << "SetBreak end:" << val << std::endl; +#endif + } + +private: + queue_t m_que; + mutable std::mutex m_mutex; + std::condition_variable m_cond; + std::atomic m_break; +}; diff --git a/async_detector/main.cpp b/async_detector/main.cpp new file mode 100644 index 000000000..27a422b8e --- /dev/null +++ b/async_detector/main.cpp @@ -0,0 +1,52 @@ +#include "AsyncDetector.h" + +#include +#include + +// ---------------------------------------------------------------------- + +static void Help() +{ + printf("\nExample of the AsyncDetector\n" + "Usage: \n" + " ./AsyncDetector [--start_frame]= [--end_frame]= [--end_delay]= [--out]= [--show_logs]= \n\n" + "Press:\n" + "\'m\' key for change mode: play|pause. When video is paused you can press any key for get next frame. \n\n" + "Press Esc to exit from video \n\n" + ); +} + +const char* keys = +{ + "{ @1 |../data/atrium.avi | movie file | }" + "{ sf start_frame |0 | Start a video from this position | }" + "{ ef end_frame |0 | Play a video to this position (if 0 then played to the end of file) | }" + "{ ed end_delay |0 | Delay in milliseconds after video ending | }" + "{ o out | | Name of result video file | }" + "{ show_logs |info | Show Trackers logs: trace, debug, info, warning, error, critical, off | }" + "{ g gpu |0 | Use OpenCL acceleration | }" + "{ contrast_adjustment |0 | Use contrast adjustment for frames before detection | }" +}; + +// ---------------------------------------------------------------------- + +int main(int argc, char** argv) +{ + Help(); + + cv::CommandLineParser parser(argc, argv, keys); + + bool useOCL = parser.get("gpu") ? 1 : 0; + cv::ocl::setUseOpenCL(useOCL); + std::cout << (cv::ocl::useOpenCL() ? "OpenCL is enabled" : "OpenCL not used") << std::endl; + + AsyncDetector asyncDetector(parser); + asyncDetector.Process(); + +#ifndef SILENT_WORK + cv::destroyAllWindows(); +#endif + + std::cout << "Correct exit" << std::endl; + return 0; +} diff --git a/data/9k.names b/data/9k.names new file mode 100644 index 000000000..e81c80e79 --- /dev/null +++ b/data/9k.names @@ -0,0 +1,9418 @@ +thing +matter +object +atmospheric phenomenon +body part +body of water +head +hair +structure +vein +mouth +heel +watercourse +ocean +gas +solid +substance +food +tear gas +sky +ice +food +cheese +yogurt +produce +baked goods +cake mix +Emmenthal +Camembert +Brie +mozzarella +Stilton +double cream +edible fruit +vegetable +currant +custard apple +citrus +jackfruit +pomegranate +avocado +prickly pear +apple +carambola +fig +mangosteen +tangelo +plum +papaya +apricot +berry +elderberry +loquat +pear +litchi +peach +muscat +grape +banana +pitahaya +rambutan +kiwi +melon +breadfruit +pineapple +mango +date +papaw +durian +passion fruit +jujube +guava +dried fruit +cherry +quince +nectarine +cherimoya +soursop +lime +mandarin +kumquat +orange +lemon +citron +grapefruit +pomelo +clementine +tangerine +satsuma +sweet orange +bitter orange +navel orange +Valencia orange +crab apple +eating apple +Granny Smith +Delicious +McIntosh +Red Delicious +Golden Delicious +strawberry +mulberry +currant +lingonberry +blackberry +red currant +raspberry +cranberry +acerola +persimmon +blueberry +bilberry +muskmelon +watermelon +cantaloup +sour cherry +sweet cherry +bing cherry +mushroom +asparagus +plantain +pumpkin +cucumber +root vegetable +cruciferous vegetable +raw vegetable +solanaceous vegetable +artichoke +legume +leek +squash +greens +celery +cardoon +gumbo +pieplant +onion +fennel +taro +beet +yam +carrot +potato +baked potato +mashed potato +french fries +mustard +cabbage +kohlrabi +cauliflower +brussels sprouts +broccoli rabe +broccoli +radish +turnip +collards +bok choy +savoy cabbage +head cabbage +kale +pepper +tomato +eggplant +tomatillo +sweet pepper +hot pepper +bell pepper +pimento +green pepper +chili +tabasco +cayenne +jalapeno +cherry tomato +beefsteak tomato +bean +pea +chickpea +lentil +soy +common bean +black bean +kidney bean +fresh bean +green bean +shell bean +snap bean +haricot vert +string bean +fava bean +green soybean +green pea +snow pea +sugar snap pea +summer squash +winter squash +spaghetti squash +zucchini +yellow squash +butternut squash +acorn squash +chard +turnip greens +salad green +bean sprout +spinach +lamb's-quarter +cress +chicory +chicory escarole +radicchio +lettuce +leaf lettuce +crisphead lettuce +cos +green onion +shallot +purple onion +doughnut +bread +crouton +breadstick +soft pretzel +rye bread +dark bread +raisin bread +brown bread +cinnamon bread +quick bread +matzo +sour bread +bun +white bread +challah +loaf of bread +pretzel +English muffin +toast +nan +chapatti +garlic bread +Yorkshire pudding +banana bread +scone +Irish soda bread +biscuit +muffin +drop scone +cornbread +nut bread +buttermilk biscuit +hardtack +shortcake +corn muffin +popover +bran muffin +cornpone +hush puppy +johnnycake +hamburger bun +bagel +frankfurter bun +sweet roll +hard roll +brioche +crescent roll +honey bun +cinnamon roll +cross bun +Italian bread +baguet +French bread +meat loaf +French loaf +material +stucco +gravel +rock +leopard +soil +sand +loofa +paper +litter +toilet tissue +queen +comestible +foodstuff +fare +beverage +soul food +feed +nutriment +yolk +comfort food +egg +grain +carrot juice +soya milk +whole wheat flour +oatmeal +ingredient +dairy product +cocoa +concoction +Spam +juice +canned food +corn +rice +wild rice +barley +wheat +sweet corn +popcorn +white rice +paddy +flavorer +egg yolk +saffron +juniper berries +cayenne +sesame seed +sassafras +spice +condiment +sweetening +herb +paprika +garlic +nasturtium +mocha +cardamom +nutmeg +stick cinnamon +Chinese anise +clove +cinnamon +guacamole +chili sauce +olive +chutney +vinegar +dip +soy sauce +salsa +cranberry sauce +catsup +spread +green olive +sauce +wine vinegar +cider vinegar +hummus +miso +spaghetti sauce +chocolate sauce +Tabasco +hot sauce +veloute +pesto +dressing +bourguignon +hollandaise +carbonara +tomato sauce +green mayonnaise +mayonnaise +powdered sugar +honey +syrup +sorghum +grenadine +maple syrup +basil +lemon balm +sweet woodruff +clary sage +hyssop +comfrey +coriander +mint +chives +marjoram +borage +sage +tea +rosemary +parsley +bay leaf +thyme +tea bag +oolong +souchong +cream +milk +whipping cream +clotted cream +light cream +heavy cream +stuffing +batter +dough +filling +pastry +bread dough +puff paste +phyllo +chow +menu +dietary +diet +diet +dietary supplement +vegetarianism +vitamin pill +multivitamin +alcohol +fruit juice +fizz +near beer +cocoa +coffee +cider +tea +soft drink +fruit drink +ginger beer +drinking water +potion +smoothie +mixed drink +liquor +sake +wine +hooch +home brew +liqueur +hard cider +brew +neutral spirits +aperitif +highball +cocktail +spritzer +punch +pina colada +mimosa +julep +gin and tonic +Bloody Mary +martini +gimlet +gin and it +daiquiri +sidecar +Sazerac +margarita +cup +May wine +eggnog +fruit punch +vodka +firewater +aquavit +grog +schnapps +arrack +gin +rum +aqua vitae +tequila +bitters +geneva +brandy +ouzo +whiskey +eau de vie +Cognac +grappa +Armagnac +Calvados +Irish +bourbon +sour mash +Scotch +rye +corn whiskey +blended whiskey +blush wine +vintage +champagne +vin ordinaire +dessert wine +macon +sparkling wine +Cotes de Provence +varietal +Burgundy +fortified wine +Bordeaux +table wine +California wine +vermouth +red wine +Rhone wine +white wine +Montrachet +Beaujolais +Chablis +Madeira +sherry +malmsey +port +muscat +Saint Emilion +claret +dry vermouth +sweet vermouth +Medoc +Chianti +Pinot noir +Rioja +Merlot +Cabernet +zinfandel +Riesling +Sauvignon blanc +Muscadet +Yquem +Pinot blanc +Sauterne +Chenin blanc +Chardonnay +sack +Verdicchio +Canary wine +Pernod +Drambuie +sambuca +triple sec +absinth +maraschino +anisette +beer +lager +draft beer +ale +suds +Munich beer +Pilsner +light beer +malt +bock +Weissbier +porter +stout +bitter +pale ale +Guinness +Weizenbock +orange juice +cranberry juice +nectar +iced coffee +caffe latte +espresso +cappuccino +Irish coffee +chicory +cafe au lait +mocha +Turkish coffee +ice tea +cuppa +tonic +cola +orange soda +ginger ale +pop +root beer +Coca Cola +Pepsi +mineral water +bottled water +soda water +oil cake +bird feed +fodder +eatage +hay +alfalfa +broad bean +dainty +fast food +puree +finger food +dish +course +mother's milk +vitamin +kosher +meal +jello +gelatin +sweet +candied apple +confiture +candy +chewing gum +confectionery +maraschino +conserve +strawberry jam +apple butter +lemon curd +jam +jelly +peppermint +peanut brittle +chocolate kiss +nougat bar +candy bar +jelly bean +lollipop +candy cane +truffle +chocolate fudge +cough drop +sugar candy +Easter egg +kiss +gumdrop +candy corn +fondant +cotton candy +caramel +fudge +candy egg +chocolate egg +bubble gum +gum ball +poached egg +piece de resistance +side dish +stew +omelet +soup +sashimi +taco +French toast +cheese souffle +potpie +lamb curry +stuffed tomato +chow mein +croquette +gefilte fish +coq au vin +special +spaghetti and meatballs +eggs Benedict +schnitzel +buffalo wing +chicken casserole +rissole +paella +frittata +meatball +chili +porridge +tamale +stuffed tomato +couscous +deviled egg +beef Wellington +pasta +egg roll +enchilada +falafel +mushy peas +turnover +scrambled eggs +Spanish rice +teriyaki +barbecued spareribs +pilaf +kabob +tempura +samosa +fried egg +sandwich plate +chicken cacciatore +saute +fried rice +custard +sukiyaki +fish and chips +souffle +steak au poivre +pizza +fondue +biryani +stuffed peppers +mousse +shirred egg +Swedish meatball +jambalaya +Scotch egg +burrito +risotto +salad +boiled egg +curry +snack food +bouillabaisse +goulash +pottage +beef stew +hot pot +fish stew +hotchpotch +Irish stew +ratatouille +gazpacho +won ton +petite marmite +split-pea soup +consomme +chowder +potage +marmite +lentil soup +bisque +pea soup +pepper pot +chicken broth +chicken soup +broth +broth +gumbo +borsch +corn chowder +clam chowder +fish chowder +gruel +congee +macaroni and cheese +lasagna +cannelloni +spaghetti +creme caramel +creme brulee +pepperoni pizza +anchovy pizza +cheese pizza +Sicilian pizza +sausage pizza +chocolate fondue +cheese fondue +coleslaw +macaroni salad +tossed salad +salad nicoise +pasta salad +fruit salad +tabbouleh +green salad +chef's salad +hard-boiled egg +Easter egg +sandwich +corn chip +chip +bomber +cheeseburger +chicken sandwich +ham sandwich +Reuben +bacon-lettuce-tomato sandwich +chili dog +open-face sandwich +gyro +wrap +hamburger +club sandwich +hotdog +tortilla chip +nacho +entree +plate +dessert +appetizer +mousse +tiramisu +frozen dessert +pudding +pudding +trifle +flan +whip +dumpling +compote +chocolate mousse +pavlova +parfait +ice-cream cake +ice lolly +ice-cream sundae +ice-cream cone +ice cream +ice +banana split +frozen yogurt +frozen custard +vanilla ice cream +peach ice cream +chocolate ice cream +strawberry ice cream +plum pudding +chocolate pudding +shrimp cocktail +stuffed mushroom +cocktail +hors d'oeuvre +carrot stick +antipasto +water-soluble vitamin +fat-soluble vitamin +vitamin P +vitamin C +B-complex vitamin +vitamin B2 +inositol +vitamin B6 +choline +pantothenic acid +vitamin B12 +vitamin Bc +vitamin D +vitamin A1 +picnic +bite +supper +breakfast +refection +smorgasbord +buffet +brunch +continental breakfast +dinner +lunch +banquet +cookout +fish fry +barbecue +refreshment +nosh +land +location +land +fomite +part +geological formation +cobweb +whole +hail +swamp +cultivated land +region +region +pass +line +point +opening +bedside +soil horizon +extremity +boundary +nib +selvage +shoreline +benthos +resort area +geographical area +district +scrubland +bush +oilfield +field +tract +heronry +grassland +site +court +basketball court +fairground +plot +field +amusement park +veld +pasture +campsite +garbage heap +cemetery +flowerbed +garden +topiary +peach orchard +yard +grainfield +playground +garden +city +city district +eparchy +kasbah +waterfront +business district +col +defile +hemline +spoor +crest +topographic point +workplace +half-mast +intersection +bus stop +mecca +hole-in-the-wall +patisserie +bakery +farm +piggery +ranch +dairy +knothole +chasm +oxbow +floor +pinetum +plain +steppe +cigarette butt +pipefitting +handle +panhandle +stock +haft +ax handle +broomstick +pistol grip +arete +volcanic crater +spring +ice mass +natural depression +natural elevation +oceanfront +massif +cliff +shore +talus +ridge +range +lakefront +slope +cave +foreshore +beach +hot spring +geyser +icecap +iceberg +Alpine glacier +glacier +valley +lunar crater +landfill +sinkhole +basin +crater +bed +hole +arroyo +ravine +canyon +gorge +tidal basin +cirque +ocean floor +riverbed +streambed +burrow +pothole +tableland +hill +mountain +highland +ridge +promontory +anthill +butte +foothill +knoll +alp +ben +volcano +sandbar +dune +reef +bank +coral reef +atoll +sandbank +bluff +point +mull +crag +precipice +seashore +strand +lakeside +littoral +seaside +mountainside +descent +hillside +ski slope +escarpment +bank +downhill +ascent +brae +uphill +riverbank +waterside +cove +cavern +grotto +artifact +living thing +natural object +assembly +block +millstone +paving +creation +opening +plaything +surface +tramline +structure +instrumentality +padding +covering +fabric +bookmark +float +building material +decoration +way +strip +article +facility +excavation +commodity +sheet +fixture +blacktop +line +bullion +tessera +tile +anvil +representation +art +needlework +product +pieta +map +sketch +sonogram +photograph +waxwork +arthrogram +radiogram +photomicrograph +photostat +painting +triptych +nude +finger-painting +smocking +stitch +sewing stitch +knitting stitch +lockstitch +hemstitch +garter stitch +purl +book +work +jotter +newspaper +wicker +masterpiece +openwork +woodwork +lacquerware +cabinetwork +joinery +decolletage +gargoyle +aperture +hole +mouthpiece +outfall +plughole +manhole +keyhole +perforation +thumbhole +pogo stick +ball +pinata +teddy +jungle gym +bubble +hula-hoop +pinwheel +slide +sport kite +cockhorse +doll +foam +air bubble +spume +shaving foam +golliwog +kachina +horizontal surface +board +tabletop +side +platform +tarmacadam +floor +turntable +stage +dais +sumo ring +hurricane deck +flatbed +parquet +backgammon board +pegboard +facade +ceiling +body +floor +bridge +corner +conformation +superstructure +airdock +cross +house of cards +hull +gun enclosure +lookout +building complex +shelter +honeycomb +column +altar +arch +tower +transept +mound +fountain +obelisk +fan vaulting +arcade +loggia +coil +billboard +partition +masonry +skein +colonnade +obstruction +building +sail +projection +peristyle +door +stadium +drinking fountain +area +balcony +porch +dock +high altar +housing +supporting structure +balance +defensive structure +entablature +memorial +establishment +signboard +bodywork +fuselage +ground floor +mezzanine +loft +basement +footbridge +drawbridge +cantilever bridge +rope bridge +overpass +truss bridge +viaduct +steel arch bridge +covered bridge +gangplank +suspension bridge +trestle bridge +plant +college +winery +factory +distillery +oil refinery +refinery +rolling mill +foundry +steel mill +lumbermill +stamp mill +quartz battery +battery +harbor +Nissen hut +hovel +tent +hut +igloo +dugout +mountain tent +pup tent +pavilion +backpacking tent +fly tent +canvas tent +field tent +wall tent +circus tent +Gothic arch +round arch +pointed arch +triumphal arch +broken arch +Moorish arch +Roman arch +campanile +turret +clock tower +shot tower +church tower +minaret +pylon +silo +watchtower +trestle +pylon +steeple +fire tower +high-rise +supporting tower +control tower +bell tower +beacon +burial mound +snowbank +rampart +fraise +battlement +altarpiece +wall +gable +wainscoting +attic +pediment +bell gable +brickwork +stonework +barrier +obstacle +plug +lever +grate +safety rail +movable barrier +bannister +breakwater +grille +weir +railing +hurdle +starting gate +fence +dam +gate +door +lychgate +portcullis +turnstile +French window +car door +screen door +French door +double door +interior door +sliding door +revolving door +barn door +hatchback +storm door +wall +retaining wall +rail fence +chainlink fence +dry wall +hedge +worm fence +picket fence +stone wall +water jump +bunker +earplug +cork +tap +presbytery +hotel +tenement +abattoir +apartment building +aviary +hall +house +Roman building +rest house +outbuilding +funeral home +medical building +hotel-casino +library +casino +farm building +place of worship +restaurant +ministry +rotunda +observatory +office building +temple +signal box +government building +greenhouse +rink +planetarium +public house +bowling alley +house +ruin +architecture +skyscraper +gazebo +school +chapterhouse +theater +hall of residence +conservatory +center +resort hotel +resort +motel +dude ranch +Ritz +ski lodge +hostel +motor hotel +city hall +guildhall +lyceum +field house +oast house +courthouse +shed +garage +carport +outhouse +coach house +boathouse +woodshed +apiary +maternity hospital +dispensary +stable +chicken coop +cowbarn +barn +pantheon +church +temple +shrine +stupa +masjid +synagogue +mosque +chapel +kirk +abbey +cathedral +cathedral +minster +cafe +rotisserie +automat +brasserie +cafeteria +diner +capitol +embassy +town hall +chancellery +customhouse +Statehouse +courthouse +ice rink +ice hockey rink +alehouse +free house +solar house +bungalow +row house +cabin +duplex house +mansion +lodging house +gatehouse +log cabin +saltbox +country house +dollhouse +ranch house +boarding house +detached house +villa +chalet +residence +farmhouse +terraced house +brownstone +palace +stately home +manor +summer house +dacha +villa +chateau +manse +religious residence +glebe house +parsonage +palace +monastery +abbey +abbey +day school +conservatory +music school +opera +music hall +cinema +little theater +home theater +control center +settlement house +call center +cornice +cog +knob +bill +flange +brim +tine +eaves +tooth +pinhead +football stadium +hippodrome +dome +ballpark +bullring +amphitheater +patio +corner +baggage claim +hideaway +choir +breakfast area +quad +chancel +auditorium +court +dining area +room +assembly hall +enclosure +nave +aisle +storage space +goalmouth +food court +atrium +cloister +forecourt +toilet +sun parlor +engineering +surgery +rotunda +classroom +gallery +manor hall +cell +lounge +sauna +dressing room +billiard room +belfry +kitchen +library +storeroom +workroom +sewing room +anechoic chamber +dining room +recreation room +hospital room +reading room +booth +conference room +bedroom +clean room +living room +door +hall +reception room +boardroom +study +locker room +cocktail lounge +television room +compartment +court +poolroom +bathroom +control room +anteroom +water closet +men's room +washroom +public toilet +home room +lecture room +study hall +pantry +stockroom +vault +refectory +dining-hall +canteen +family room +rumpus room +emergency room +recovery room +operating room +telephone booth +voting booth +confessional +shower stall +master bedroom +motel room +guestroom +hotel room +dormitory +nursery +day nursery +great hall +concert hall +palace +exhibition hall +parlor +drawing room +press box +command module +cabin +pilothouse +cab +luggage compartment +cabinet +cockpit +stateroom +car +cable car +stall +drawing room +terrarium +cage +playpen +pen +vivarium +pound +lock +chicken yard +chamber +recess +birdcage +rabbit hutch +hutch +cow pen +rodeo +fold +sounding board +burial chamber +firing chamber +resonator +furnace +bomb shelter +hyperbaric chamber +repository +mausoleum +kiln +blast furnace +oast +gas oven +incinerator +mihrab +columbarium +fire +fireplace +apse +cellar +cupboard +stacks +gallery +amphitheater +organ loft +stoop +sun deck +front porch +veranda +deck +back porch +portico +marina +dry dock +block +dwelling +tennis camp +living quarters +mobile home +condominium +apartment +ward +cellblock +condominium +yurt +lodge +vacation home +hearth +fixer-upper +cliff dwelling +homestead +semi-detached house +wigwam +tepee +accommodation +first class +cabin class +bedsitting room +flatlet +chassis +support +framework +pedestal +buttress +flying buttress +abutment +ribbing +bustle +window frame +frame +gantry +honeycomb +truss +lattice +cornice +picture frame +window +climbing frame +trellis +airframe +grate +grape arbor +walker +casing +tambour +arbor +rack +mounting +sash +casement +oriel +bay window +stained-glass window +skylight +display window +rose window +porthole +transom +clerestory +dormer +dormer window +lancet window +fanlight +plate rack +barbecue +bicycle rack +luggage rack +dish rack +towel rack +passe-partout +pave +mount +stronghold +fortress +fortification +bastion +keep +kremlin +acropolis +alcazar +martello tower +fieldwork +bastion +escarpment +palisade +castle +cenotaph +megalith +Seven Wonders of the Ancient World +brass +national monument +pantheon +dolmen +menhir +place of business +institution +university +mercantile establishment +office +cabaret +health spa +plaza +country store +department store +shop +marketplace +boutique +salon +shoe shop +bookshop +package store +thriftshop +junk shop +toyshop +cleaners +bazaar +gift shop +florist +drugstore +garage +delicatessen +small stores +barbershop +stall +tobacco shop +newsstand +butcher shop +pizzeria +confectionery +convenience store +bazaar +agora +grocery store +open-air market +supermarket +hypermarket +greengrocery +souk +farmer's market +flea market +newsroom +box office +headquarters +correctional institution +orphanage +jail +penitentiary +prison +toiletry +weaponry +equipment +connection +implement +furnishing +device +ceramic +system +container +conveyance +medium +deodorant +bath oil +cream +lotion +shaving cream +hair spray +mousse +perfume +hairdressing +antiperspirant +powder +cosmetic +bath salts +hand cream +cold cream +sunscreen +lanolin +body lotion +toner +hand lotion +after-shave +potpourri +patchouli +perfumery +cologne +toilet water +pomade +brilliantine +toilet powder +talcum +depilatory +highlighter +makeup +face powder +lip-gloss +eyeshadow +mascara +lipstick +rouge +eyeliner +eyebrow pencil +armament +defense system +bomb +ammunition +naval weaponry +bazooka +artillery +launcher +cannon +field artillery +mortar +basilisk +hydrogen bomb +atom bomb +round +shotgun shell +recorder +sports equipment +photographic equipment +naval equipment +parasail +gear +satellite +game equipment +parachute +electronic equipment +apparatus +automation +material +baggage +tape recorder +cassette recorder +Dictaphone +videocassette recorder +baseball equipment +croquet mallet +clay pigeon +skate +wrestling mat +cricket equipment +basketball equipment +javelin +shuttlecock +golf equipment +spike +stick +boxing equipment +boxing glove +gymnastic apparatus +weight +baseball glove +batting cage +batting glove +base +batting helmet +baseball bat +home plate +first base +third base +second base +in-line skate +Rollerblade +roller skate +ice skate +hockey skate +speed skate +figure skate +cricket bat +wicket +golfcart +golf glove +tee +golf club +wood +iron +driver +spoon +wedge +midiron +putter +niblick +pitching wedge +sand wedge +hockey stick +polo mallet +horizontal bar +horse +uneven parallel bars +parallel bars +trampoline +balance beam +vaulting horse +pommel horse +dumbbell +barbell +enlarger +camera +clapperboard +film +light meter +box camera +flash camera +Polaroid camera +point-and-shoot camera +webcam +motion-picture camera +digital camera +portrait camera +reflex camera +X-ray film +reel +negative +regalia +kit +rig +fishing gear +stable gear +rigging +crown +crown jewels +sewing kit +first-aid kit +carpenter's kit +layette +drill rig +drilling platform +harness +snaffle +headgear +saddle blanket +halter +bridle +sputnik +space station +backboard +ball +puzzle +pool table +bowling pin +man +chip +roulette wheel +goal +volleyball net +pinball machine +soccer ball +pool ball +bowling ball +softball +field hockey ball +punching bag +billiard ball +croquet ball +cricket ball +tennis ball +golf ball +rugby ball +cue ball +medicine ball +basketball +eight ball +ping-pong ball +handball +baseball +racquetball +bocce ball +volleyball +jigsaw puzzle +crossword puzzle +chessman +white +pawn +basket +net +electronic fetal monitor +monitor +monitor +television monitor +telephone +oscilloscope +peripheral +booster +cassette player +CD player +receiver +audio system +lens +playback +television equipment +circuitry +cassette deck +central processing unit +mixer +scanner +tape player +detector +modem +equalizer +tape deck +amplifier +cellular telephone +speakerphone +desk phone +pay-phone +handset +dial telephone +radiotelephone +television receiver +radio receiver +satellite receiver +heterodyne receiver +clock radio +reproducer +hi-fi +stereo +iPod +Walkman +video iPod +ghetto blaster +camcorder +television camera +pendulum +purifier +sequencer +reformer +duplicator +heat pump +semaphore +tomograph +ultracentrifuge +generator +incubator +burner +Foucault pendulum +clock pendulum +metronome +Photostat +photocopier +Xerox +facsimile +mimeograph +positron emission tomography scanner +computerized axial tomography scanner +gas burner +blowtorch +bunsen burner +gas ring +packaging +blister pack +roofing +temporary hookup +slip ring +telephone line +ligament +junction +hot line +digital subscriber line +land line +binder +wire +chain +concertina +barbed wire +paper chain +anchor chain +fob +bicycle chain +tire chain +chatelaine +joint +contact +dovetail +welt +hinge +scarf joint +weld +seam +mortise joint +butt hinge +strap hinge +distributor point +tread +wiper +bar +tool +utensil +rubber eraser +needle +eraser +stick +brush +hook +sharpener +sports implement +leather strip +swatter +fire iron +oar +stick +cleaning implement +rod +writing implement +shovel +split rail +fret +bolt +rotor +towel rail +lever +track +handlebar +crowbar +stick +key +tappet +pedal +rocker arm +gun trigger +space bar +backspace key +shift key +telegraph key +accelerator +sustaining pedal +hand tool +jack +pestle +garden tool +plow +comb +drill +cutting implement +tamp +garden rake +rake +stamp +locking pliers +pestle +plunger +pincer +pliers +soldering iron +spade +hammer +pipe cutter +wrench +screwdriver +trowel +saw +opener +scraper +shovel +brick trowel +spatula +carpenter's hammer +gavel +mallet +maul +torque wrench +pipe wrench +adjustable wrench +open-end wrench +Allen wrench +box wrench +hacksaw +folding saw +handsaw +pruner +pruning saw +corkscrew +bottle opener +can opener +hedge trimmer +lawn mower +power mower +riding mower +power drill +electric drill +cutter +twist bit +bit +blade +knife blade +bolt cutter +cigar cutter +edge tool +scissors +knife +ax +razor +wire cutter +chisel +plane +shears +snips +pruning shears +secateurs +carving knife +Bowie knife +pocketknife +cleaver +hunting knife +case knife +parer +letter opener +switchblade +penknife +battle-ax +hatchet +shaver +straight razor +safety razor +cold chisel +wood chisel +jointer +smooth plane +spokeshave +kitchen utensil +ceramic ware +funnel +rolling pin +reamer +masher +kitchenware +squeezer +mixer +cookie cutter +cooking utensil +grater +mincer +eggbeater +whisk +blender +pan +Crock Pot +chafing dish +spatula +griddle +enamelware +steamer +cookie sheet +cooker +turner +omelet pan +stewing pan +frying pan +roaster +wok +saucepan +graniteware +cloisonne +porcelain +earthenware +stoneware +pottery +Spode +china +bone china +majolica +faience +knitting needle +crochet needle +walking stick +matchstick +club +fiddlestick +spindle +stob +staff +drumstick +mallet +cane +sword cane +bat +table-tennis racquet +truncheon +alpenstock +flagpole +crutch +electric toothbrush +toothbrush +sable +scrub brush +hairbrush +bristle brush +shaving brush +pencil sharpener +steel +cue +racket +squash racket +tennis racket +badminton racket +thong +strap +cheekpiece +rein +noseband +leading rein +scull +paddle +besom +scouring pad +dustmop +squeegee +broom +swab +rotating shaft +wand +shaft +piston rod +kickstand +axle +pole +fishing rod +connecting rod +tie rod +driveshaft +crankshaft +transmission shaft +spindle +camshaft +boom +stilt +ski pole +clothes tree +caber +spar +mast +mast +bowsprit +yard +mizzenmast +royal mast +mainmast +foremast +fly rod +spinning rod +pencil +pen +highlighter +chalk +crayon +lead pencil +ballpoint +Sharpie +quill +fountain pen +felt-tip pen +furniture +office furniture +dining-room furniture +wardrobe +bedroom furniture +table +table +wall unit +lamp +dining-room table +washstand +buffet +cabinet +baby bed +bedstead +lawn furniture +credenza +bookcase +entertainment center +etagere +seat +sectional +chest of drawers +file +Rolodex +card index +vertical file +clothes closet +armoire +bed +berth +platform bed +hospital bed +bunk +trundle bed +four-poster +couch +bunk bed +twin bed +sleigh bed +single bed +hammock +Murphy bed +double bed +gaming table +gueridon +table-tennis table +counter +altar +breakfast table +stand +conference table +pedestal table +kitchen table +operating table +tea table +lectern +worktable +gateleg table +dressing table +desk +drop-leaf table +coffee table +trestle table +console table +checkout +bar +meat counter +reception desk +salad bar +snack bar +drafting table +lab bench +writing desk +secretary +davenport +dining table +dinner table +refectory table +floor lamp +table lamp +reading lamp +dresser +china cabinet +medicine chest +bassinet +crib +carrycot +cradle +chair +toilet seat +stool +sofa +ottoman +bench +lawn chair +chaise longue +rocking chair +swivel chair +throne +straight chair +ladder-back +highchair +armchair +Windsor chair +folding chair +wheelchair +motorized wheelchair +barber chair +easy chair +recliner +Morris chair +wing chair +deck chair +camp chair +music stool +taboret +footstool +settee +daybed +convertible +love seat +chesterfield +studio couch +park bench +flat bench +pew +settle +window seat +chiffonier +highboy +bird feeder +heater +lighter +signal +converter +crusher +drive +knocker +peeler +musical instrument +shoehorn +shock absorber +machine +conductor +bait +stabilizer +filter +mechanism +acoustic device +trap +charger +airfoil +router +pick +energizer +fan +hydrofoil +dental appliance +adapter +toy +support +optical device +straightener +tongs +phonograph needle +instrument +comb +remote control +exercise device +comforter +washboard +shredder +water ski +blower +ventilator +breathing device +applicator +skeleton key +guitar pick +restraint +keyboard +electrical device +appliance +fire extinguisher +corrective +reflector +alarm +electronic device +snowshoe +holding device +memory device +key +noisemaker +source of illumination +indicator +detector +breathalyzer +imprint +afterburner +horn +elastic device +ski +lifting device +solar heater +electric heater +radiator +gas heater +convector +space heater +stove +cigar lighter +match +cairn +sign +street sign +traffic light +electrical converter +catalytic converter +inverter +synchronous converter +external drive +CD-ROM drive +internal drive +stringed instrument +electronic instrument +keyboard instrument +wind instrument +bass +percussion instrument +dulcimer +chordophone +banjo +zither +samisen +guitar +bowed stringed instrument +sitar +lute +mandola +mandolin +harp +acoustic guitar +Hawaiian guitar +uke +electric guitar +viol +violin +cello +viola +viola da gamba +Stradavarius +theremin +electric organ +synthesizer +piano +clavier +organ +accordion +grand piano +upright +spinet +mechanical piano +baby grand +concert grand +harpsichord +spinet +ocarina +woodwind +brass +organ pipe +free-reed instrument +whistle +pipe +kazoo +flute +beating-reed instrument +double-reed instrument +single-reed instrument +bassoon +oboe +clarinet +sax +baritone +bugle +flugelhorn +French horn +trombone +cornet +harmonium +harmonica +concertina +chanter +panpipe +bagpipe +fipple flute +pennywhistle +drone +bass fiddle +bass horn +bass guitar +euphonium +handbell +bones +gong +vibraphone +steel drum +marimba +glockenspiel +chime +kettle +maraca +drum +cymbal +bongo +bass drum +tambourine +snare drum +tenor drum +slot machine +power shovel +press +backhoe +printer +machine tool +motor +snow thrower +cash machine +farm machine +computer +Zamboni +mill +staple gun +power tool +concrete mixer +stapler +slicer +textile machine +record player +calculator +vending machine +slot +automat +garlic press +bench press +hydraulic press +punch press +character printer +impact printer +printer +drum printer +line printer +laser printer +Linotype +thermal printer +portable +typewriter +bar printer +wire matrix printer +dot matrix printer +bubble jet printer +ink-jet printer +shaper +drill press +grinder +lathe +miller +engine +electric motor +heat engine +jet engine +automobile engine +aircraft engine +generator +steam engine +internal-combustion engine +wind turbine +gasoline engine +diesel +outboard motor +radial engine +rocket +fanjet +booster +space rocket +alternator +windmill +starter +kick starter +cultivator +haymaker +combine +thresher +harvester +disk harrow +harrow +slide rule +web site +home computer +server +digital computer +supercomputer +workstation +personal computer +portable computer +desktop computer +notebook +planner +laptop +hand-held computer +pepper mill +water mill +meat grinder +coffee mill +treadmill +windmill +electric hammer +power saw +buffer +circular saw +chain saw +table saw +saber saw +bandsaw +spinning wheel +loom +jukebox +gramophone +abacus +adding machine +hand calculator +semiconductor device +wire +cord +heat sink +cable +microprocessor +transistor +light-emitting diode +chip +filament +jumper cable +telephone wire +patchcord +telephone cord +power cord +extension cord +ethernet cable +electrical cable +printer cable +power line +fisherman's lure +fly +dry fly +wet fly +streamer fly +outrigger +vane +strainer +air filter +oil filter +sieve +tea-strainer +colander +fusee drive +android +radiator +mechanical device +rotating mechanism +rotor head +carriage +control +power steering +automaton +action +cooling system +gear +tape drive +film advance +sprinkler +propeller +anchor +golf-club head +weathervane +machine +seeder +pump +gearshift +ride +bumper +hook +ski binding +coupling +record changer +swing +windshield wiper +winder +winder +diaphragm +shutter +escapement +broadcaster +curler +splint +compressor +air compressor +carburetor +dildo +cartridge holder +trapeze +gearing +stator +airplane propeller +screw +pulley +wheel +idle pulley +lever +inclined plane +millwheel +waterwheel +roller +bicycle wheel +caster +grinding wheel +rowel +fifth wheel +wagon wheel +waterwheel +car wheel +sprocket +pinwheel +potter's wheel +gear +driving wheel +paddlewheel +roulette +spur gear +bevel gear +pinion +ramp +ax head +screw +grease-gun +gas pump +bicycle pump +sump pump +hand pump +centrifugal pump +Ferris wheel +roller coaster +carousel +universal joint +clutch +freewheel +disk clutch +bobbin +reel +shuttle +blade +gyroscope +circle +rotor +paddle +impeller +fan blade +disk +puck +brake disk +token +Frisbee +planchet +tail rotor +main rotor +valve +steering wheel +governor +joystick +regulator +switch +ball valve +butterfly valve +timer +flywheel +faucet +thermostat +aperture +mixing faucet +stopcock +toggle switch +push button +dial +horn button +mouse button +doorbell +bell push +flintlock +movement +gunlock +cooling tower +evaporative cooler +air conditioner +gearset +four-wheel drive +whistle +silencer +megaphone +hearing aid +bell +cowbell +church bell +dinner bell +spider web +mousetrap +lobster pot +web +net +landing net +fishnet +vertical stabilizer +spoiler +spoiler +rotor blade +flap +rudder +horizontal stabilizer +wing +exhaust fan +electric fan +brace +denture +backboard +stirrup +pier +pier +back +shelf +landing gear +baluster +spoke +base +step +brace +pillow block +bearing +rocker +coat hanger +harp +rest +bracket +tailstock +bookend +structural member +headstock +seat +thrust bearing +hanger +rack +harness +cantle +ladder-back +bookshelf +mantel +neck brace +knee brace +ankle brace +back brace +arm +headrest +chin rest +armrest +sconce +corbel +shelf bracket +sill +riser +upright +brace +tread +beam +windowsill +doorsill +stile +jamb +column +post +support column +caryatid +goalpost +newel post +bollard +lamppost +telephone pole +maypole +timber +rundle +tie +rafter +girder +timber +floor joist +joist +car seat +pillion +plane seat +saddle +chair +bicycle seat +bucket seat +backseat +stock saddle +English saddle +tripod +spice rack +magazine rack +music stand +camera tripod +easel +autofocus +projector +finder +laser +lens +objective +condenser +camera lens +anastigmat +contact +sunglass +eyepiece +field lens +Fresnel lens +portrait lens +closeup lens +telephoto lens +wide-angle lens +plotter +scientific instrument +measuring instrument +weapon +guillotine +drafting instrument +analyzer +navigational instrument +optical instrument +medical instrument +instrument of punishment +catapult +extractor +theodolite +riding crop +tachymeter +collider +microtome +accelerator +stroboscope +magnifier +console +telescope +microscope +astronomical telescope +equatorial +optical telescope +radio telescope +refracting telescope +field glass +reflecting telescope +Cassegrainian telescope +Newtonian telescope +Schmidt telescope +Maksutov telescope +electron microscope +field-emission microscope +light microscope +binocular microscope +hand glass +operating microscope +compound microscope +loupe +oximeter +dropper +refractometer +rangefinder +barometer +pedometer +thermometer +astrolabe +measuring stick +gauge +timepiece +aneroid barometer +caliper +potentiometer +tachometer +scale +tape +meter +hygrometer +sextant +rule +altazimuth +pyrometer +meat thermometer +water gauge +vacuum gauge +anemometer +gasoline gauge +pressure gauge +manometer +sphygmomanometer +atomic clock +clock +watch +sundial +timer +hourglass +grandfather clock +digital clock +alarm clock +wall clock +analog clock +pendulum clock +cuckoo clock +digital watch +analog watch +pocket watch +wristwatch +stopwatch +parking meter +chronograph +vernier caliper +micrometer +balance +analytical balance +electronic balance +electric meter +odometer +ammeter +speedometer +ohmmeter +water meter +voltmeter +magnetometer +tomahawk +gun +bow +bow and arrow +brass knucks +knife +sword +stun gun +projectile +antiaircraft +firearm +set gun +air gun +gas gun +paintball gun +cannon +autoloader +pistol +twenty-two +Mauser +muzzle loader +rifle +repeating firearm +semiautomatic firearm +automatic firearm +Garand rifle +Luger +semiautomatic pistol +automatic rifle +assault rifle +automatic pistol +machine gun +submachine gun +burp gun +Uzi +Kalashnikov +Tommy gun +Colt +derringer +revolver +gat +flintlock +musket +sniper rifle +Winchester +carbine +crossbow +longbow +khukuri +bayonet +machete +dagger +rapier +fencing sword +broadsword +cavalry sword +saber +epee +foil +bullet +cannonball +compass +protractor +artificial horizon +depth finder +magnetic compass +compass +binoculars +spectacles +projector +telescopic sight +goggles +sunglasses +slide projector +front projector +movie projector +overhead projector +hypodermic syringe +cardiograph +syringe +stethoscope +laryngoscope +otoscope +surgical instrument +retractor +hemostat +pillory +rattan +exercise bike +treadmill +respirator +snorkel +oxygen mask +aqualung +paintbrush +spray gun +brake +handcuff +fastener +seat belt +leash +safety belt +brake system +muzzle +chain +bolt +buckle +knot +cleat +clothespin +catch +pin +dowel +screw +slide fastener +button +seal +paper fastener +lock +thumbtack +locker +clasp +clip +carabiner +nail +toggle +nut and bolt +bowline +bow +latch +hasp +rivet +hairpin +skewer +hatpin +brochette +bobby pin +barrette +safety pin +shirt button +coat button +washer +gasket +head gasket +O ring +padlock +sash fastener +latch +combination lock +doorlock +paper clip +bulldog clip +hair slide +hydraulic brake +disk brake +drum brake +typewriter keyboard +QWERTY keyboard +computer keyboard +piano keyboard +circuit +Segway +jack +control panel +telephone jack +circuit breaker +plug +electrolytic +dashboard +transducer +solar cell +antenna +capacitor +spark plug +relay +surge suppressor +solar array +battery +Tesla coil +closed circuit +wiring +computer circuit +integrated circuit +module +printed circuit +interface +CPU board +circuit board +mosaic +electro-acoustic transducer +earphone +microphone +loudspeaker +telephone receiver +headset +condenser microphone +cardioid microphone +tweeter +bullhorn +tannoy +woofer +subwoofer +omnidirectional antenna +directional antenna +radio antenna +television antenna +dish +scanner +yagi +voltaic battery +flashlight battery +lead-acid battery +pack +prosthesis +solar dish +mirror +hand glass +car mirror +rearview mirror +outside mirror +burglar alarm +automobile horn +shofar +fire alarm +readout +scanner +tube +display +personal digital assistant +dongle +trackball +mouse +answering machine +hearing aid +beeper +triode +pentode +computer monitor +monitor +screen +digital display +liquid crystal display +flat panel display +window +dialog box +caller ID +computer screen +background +C-clamp +chuck +collet +holder +vise +candlestick +cigarette holder +candelabrum +menorah +Menorah +cache +optical disk +magnetic disk +memory +magnetic tape +recording +auxiliary storage +compact disk +videodisk +CD-ROM +CD-R +audio CD +hard disc +diskette +flash memory +random-access memory +videotape +cassette tape +tape +phonograph record +LP +seventy-eight +lamp +light +flash +lantern +candle +neon lamp +vigil light +taillight +gas lamp +oil lamp +hurricane lamp +fluorescent lamp +streetlight +spotlight +electric lamp +jack-o'-lantern +Chinese lantern +flashlight +light bulb +penlight +headlight +room light +strip lighting +fairy light +sconce +searchlight +night-light +blinker +torch +flood +fuel gauge +gnomon +dial +vernier scale +pointer +light pen +hand +sweep hand +minute hand +second hand +hour hand +spring +rubber band +coil spring +box spring +hoist +winch +elevator +crane +wheel and axle +derrick +maze +communication system +network +Global Positioning System +resonator +exhaust +mechanical system +computer system +scaffolding +reticle +walkie-talkie +radio +telecommunication system +telephone system +intercommunication system +interphone +television +satellite television +surveillance system +color television +local area network +superhighway +ethernet +wireless local area network +production line +linkage +suspension +fuel injection +planter +trophy case +wastepaper basket +dish +bread-bin +dispenser +pot +bunker +reliquary +cup +bag +cassette +Dumpster +bag +measuring cup +glass +paintball +measure +envelope +shaker +piggy bank +basket +sewing basket +savings bank +powder horn +can +wheeled vehicle +workbasket +bin +canister +mold +cargo container +videocassette +case +case +vessel +drawer +receptacle +package +watering can +box +cocotte +Petri dish +gravy boat +serving dish +tureen +sugar bowl +bowl +casserole +ramekin +butter dish +salad bowl +mixing bowl +porringer +cereal bowl +soup bowl +punch bowl +roll-on +aerosol +soap dispenser +atomizer +inhaler +demitasse +beaker +kylix +coffee cup +chalice +teacup +Dixie cup +evening bag +shoulder bag +clutch bag +reticule +backpack +sachet +beanbag +sandbag +carryall +pannier +duffel bag +book bag +tool bag +mailbag +purse +drawstring bag +envelope +saddlebag +sack +pouch +shopping bag +toilet bag +gamebag +kitbag +plastic bag +golf bag +sleeping bag +gunnysack +grocery bag +sporran +pocket +waist pack +fanny pack +hip pocket +patch pocket +flute +tumbler +water glass +bumper +liqueur glass +snifter +shot glass +beer glass +rummer +goblet +wineglass +cocktail shaker +saltshaker +pepper shaker +pannier +clothes hamper +hamper +breadbasket +shopping basket +wicker basket +milk can +beer can +soda can +pedicab +camper trailer +rolling stock +motor scooter +self-propelled vehicle +unicycle +wagon +bassinet +handcart +baby buggy +bicycle +horse-drawn vehicle +trailer +car +tricycle +armored vehicle +recreational vehicle +tracked vehicle +snowmobile +bulldozer +locomotive +streetcar +motor vehicle +tractor +forklift +armored personnel carrier +armored car +dune buggy +camper +van +shunter +diesel locomotive +electric locomotive +tank engine +traction engine +steam locomotive +diesel-electric locomotive +diesel-hydraulic locomotive +hearse +truck +amphibian +four-wheel drive +motorcycle +go-kart +car +snowplow +fire engine +van +trailer truck +transporter +garbage truck +ladder truck +tow truck +dump truck +tractor +pickup +delivery truck +moving van +passenger van +police van +bookmobile +trail bike +moped +beach wagon +loaner +Model T +electric +minivan +convertible +compact +cab +shooting brake +racer +hatchback +roadster +berlin +sport utility +sedan +jeep +limousine +cruiser +ambulance +used-car +stock car +subcompact +pace car +hot rod +sports car +coupe +covered wagon +cart +horse cart +dumpcart +jinrikisha +pony cart +oxcart +tea cart +laundry cart +serving cart +barrow +shopping cart +hand truck +bicycle-built-for-two +safety bicycle +push-bike +mountain bike +carriage +gharry +buggy +stagecoach +four-wheeler +baggage car +freight car +passenger car +cabin car +boxcar +tank car +nonsmoker +Pullman +dining car +smoker +recycling bin +ashcan +litterbin +sandbox +pig bed +briefcase +compact +dispatch case +kit +wallet +cardcase +portfolio +ditty bag +cigarette case +shoe +gun case +attache case +locket +writing desk +watch case +baggage +glasses case +hand luggage +satchel +bag +trunk +hatbox +garment bag +weekender +carpetbag +portmanteau +overnighter +valise +boiler +flagon +bowl +ladle +bottle +bottle +pot +pitcher +bathtub +mortar +bucket +drinking vessel +cream pitcher +wine bucket +pressure cooker +tub +inkwell +tin +basin +monstrance +autoclave +churn +barrel +tank +jar +censer +toilet bowl +fishbowl +scoop +soup ladle +smelling bottle +pop bottle +water bottle +jug +catsup bottle +gourd +pill bottle +carboy +flask +beer bottle +ink bottle +demijohn +whiskey bottle +cruet +wine bottle +carafe +phial +whiskey jug +water jug +hipflask +Erlenmeyer flask +thermos +canteen +vacuum flask +magnum +jeroboam +saucepot +teapot +Dutch oven +urn +stockpot +kettle +caldron +percolator +teakettle +coffeepot +coffee urn +samovar +tea urn +sitz bath +hot tub +footbath +mug +loving cup +tankard +coffee mug +toby +beer mug +bidet +birdbath +washbasin +baptismal font +beer barrel +wine cask +keg +gas tank +water heater +septic tank +aquarium +reservoir +water tower +rain barrel +canopic jar +amphora +cookie jar +beaker +urn +Mason jar +vase +crock +jampot +plate +tray +cat box +dustpan +chamberpot +salver +garbage +in-basket +hot-water bottle +ossuary +socket +ashtray +packet +bundle +deck +bale +hay bale +pack +ballot box +carton +coffin +shoebox +snuffbox +pencil box +crate +bandbox +window box +chest +strongbox +cereal box +mailbox +casket +bier +packing box +toolbox +toy box +coffer +hope chest +treasure chest +cedar chest +cash register +safe-deposit +cashbox +safe +tramway +chairlift +sidecar +public transport +semitrailer +horsebox +vehicle +ski tow +roll-on roll-off +trailer +shipping +litter +express +shuttle bus +train +bus +local +freight liner +passenger train +subway train +mail train +freight train +commuter +bullet train +trolleybus +minibus +school bus +steamroller +bumper car +rocket +military vehicle +missile +craft +sled +half track +tank +panzer +personnel carrier +Humvee +aircraft +vessel +spacecraft +hovercraft +heavier-than-air craft +stealth aircraft +lighter-than-air craft +hang glider +glider +helicopter +warplane +airplane +autogiro +bomber +amphibian +propeller plane +airliner +biplane +floatplane +jet +fighter +stealth bomber +seaplane +airbus +widebody aircraft +jumbojet +jetliner +stealth fighter +interceptor +airship +blimp +balloon +hot-air balloon +boat +trawler +yacht +ship +sailing vessel +bareboat +lifeboat +police boat +gondola +sea boat +barge +river boat +tugboat +punt +pilot boat +small boat +ferry +tender +canal boat +fireboat +motorboat +dredger +pontoon +houseboat +skiff +canoe +dinghy +racing boat +coracle +yawl +gig +jolly boat +rowing boat +kayak +outrigger canoe +dugout canoe +racing gig +racing skiff +speedboat +outboard motorboat +cabin cruiser +hydrofoil +shipwreck +wreck +passenger ship +pirate +lightship +hospital ship +steamer +cargo ship +sister ship +warship +liner +luxury liner +cargo liner +cruise ship +paddle steamer +sternwheeler +bottom +container ship +banana boat +oil tanker +submarine +guided missile cruiser +frigate +battleship +guided missile frigate +aircraft carrier +man-of-war +destroyer +attack submarine +nautilus +yawl +clipper +felucca +sloop +ketch +dhow +sailboat +bark +schooner +windjammer +trimaran +catamaran +catboat +space shuttle +space capsule +dogsled +bobsled +bobsled +stretcher +covered couch +telecommunication +vehicle +print media +broadcasting +telephone +radiotelephone +television +reception +radio +cable television +high-definition television +three-way calling +call +voice mail +press +journalism +magazine +newspaper +pulp +slick +comic book +news magazine +tabloid +daily +gazette +Fleet Street +yellow journalism +pillow +pad +sanitary napkin +beer mat +futon +carpet pad +range hood +screen +top +footwear +protective covering +cloak +wrapping +upholstery +cloth covering +mask +finger +floor cover +coating +canopy +flap +domino +folder +planking +earmuff +camouflage +shoji +cap +manhole cover +lid +radiator cap +bottlecap +nipple +clog +shoe +arctic +boot +flats +slipper +overshoe +sabot +slingback +chukka +saddle oxford +spectator pump +brogan +wing tip +walker +blucher +anklet +cleats +gaiter +Loafer +running shoe +oxford +bowling shoe +plimsoll +pump +sandal +chopine +pusher +talaria +flip-flop +espadrille +jodhpur +buskin +ski boot +hip boot +riding boot +rubber boot +Hessian boot +waders +cowboy boot +mule +bootee +cold frame +cloche +washboard +toecap +mulch +shield +bracer +screen +sheathing +bell jar +shade +shelter +splashboard +testudo +roof +faceplate +hood +sheath +cap +mask +facing +crystal +calash +armor +binder +binding +housing +blind +lining +plate +horseshoe +armor plate +breastplate +helmet +cannon +knee piece +pickelhaube +sallet +window screen +fire screen +windshield +mosquito net +lampshade +parasol +lean-to +bell cote +sentry box +birdhouse +canopy +kennel +awning +umbrella +gamp +gable roof +sunroof +mansard +dome +hip roof +tile roof +housetop +vault +slate roof +gambrel +thatch +cupola +geodesic dome +onion dome +barrel vault +ribbed vault +holster +scabbard +shoulder holster +hubcap +thimble +distributor cap +lens cap +gasmask +face mask +ski mask +catcher's mask +body armor +shield +chain mail +bulletproof vest +corselet +cuirass +cabinet +radome +boot +window blind +jalousie +curtain +shutter +Venetian blind +window shade +roller blind +theater curtain +shower curtain +bushing +brake lining +gift wrapping +envelope +cellophane +book jacket +jacket +plastic wrap +shoulder +pant leg +leg +back +cosy +bandage +bosom +slipcover +bedclothes +sleeve +blindfold +eyepatch +skirt +seat +Band Aid +swathe +cast +elastic bandage +quilt +afghan +blanket +bedspread +mattress cover +patchwork +eiderdown +crazy quilt +coverlet +quilted bedspread +raglan sleeve +long sleeve +rug +doormat +mat +scatter rug +shag rug +prayer rug +broadloom +stair-carpet +red carpet +Brussels carpet +fixative +gold plate +verdigris +paint +nail polish +gilt +couch +enamel +veneer +finger paint +enamel +encaustic +oil paint +water-base paint +latex paint +whitewash +earflap +pocket flap +lapel +tongue +revers +tent-fly +file folder +matchbook +plush +muslin +tarpaulin +velvet +batik +khaki +belting +sacking +diaper +voile +duffel +chenille +cotton flannel +toweling +crinoline +panting +chintz +felt +cotton +velveteen +satin +knit +sateen +print +flannel +webbing +gabardine +camouflage +worsted +cashmere +tartan +mohair +brocade +velour +shirttail +boucle +madras +net +paisley +yoke +percale +piece of cloth +moquette +terry +rayon +acetate rayon +cord +permanent press +chiffon +burlap +ticking +basket weave +lace +sheeting +georgette +poplin +denim +flannelette +shantung +camel's hair +nylon +drapery +gauze +organza +foulard +gingham +wool +suede cloth +taffeta +leatherette +tweed +organdy +canopy +etamine +damask +oilcloth +tapestry +broadcloth +pique +homespun +tricot +double knit +jersey +gauze +tulle +chicken wire +handkerchief +groundsheet +dustcloth +dishrag +towel +bandanna +gusset +bib +sail +patch +hand towel +paper towel +dishtowel +fore-and-aft sail +foresail +spinnaker +headsail +topsail +mainsail +balloon sail +jib +mizzen +gaff topsail +lugsail +staysail +lateen +flash +shoulder patch +narrow wale +Bedford cord +macrame +pillow lace +raft +life preserver +life buoy +Mae West +life jacket +stone +brick +lumber +bricks and mortar +tile +concrete +quoin +millstone +stele +hone +grindstone +curbstone +gravestone +firebrick +mud brick +clinker +adobe +strip +chipboard +slat +fingerboard +toothpick +hip tile +pantile +cornice +embellishment +graffito +epergne +necklet +marquetry +brass +garnish +arabesque +design +adornment +frieze +lambrequin +tattoo +mihrab +emblem +swastika +herringbone +spot +flag +banner +totem pole +crucifix +fleur-de-lis +macule +parhelion +jewelry +frill +lavaliere +peplum +bangle +cigar band +aigrette +bracelet +bling +pendant earring +necklace +ghat +path +road +passage +sidewalk +towpath +pedestrian crossing +highway +carriageway +thoroughfare +trail +divided highway +expressway +arterial road +autostrada +autobahn +street +street +piste +horse-trail +adit +conduit +passageway +tube +sluice +snorkel +waterspout +catheter +barrel +pipe +hookah +tailpipe +drain +culvert +soil pipe +tunnel +stairwell +gangway +catacomb +railroad tunnel +tape +band +inkle +adhesive tape +plaster +cellulose tape +headstall +girdle +tire +armlet +radial +car tire +tableware +riband +cutlery +glass +hollowware +platter +spoon +table knife +fork +Spork +soupspoon +teaspoon +sugar spoon +wooden spoon +iced-tea spoon +tablespoon +dessert spoon +case knife +butter knife +steak knife +tablefork +carving fork +airfield +telpherage +air terminal +airport +menagerie +storehouse +station +warehouse +granary +crib +mineshaft +ditch +irrigation ditch +furrow +consumer goods +linen +clothing +appliance +leisure wear +grey +blue +nightwear +protective garment +outerwear +neckpiece +knitwear +loungewear +apparel +collar +military uniform +headdress +pajama +garment +array +woman's clothing +overall +glove +accessory +black +footwear +attire +ready-to-wear +beachwear +man's clothing +street clothes +slip-on +shin guard +overall +pressure suit +arm guard +foul-weather gear +diving suit +apron +shoulder pad +coverall +chest protector +elbow pad +spacesuit +knee pad +gown +vestment +chasuble +academic gown +battle dress +fatigues +dress uniform +khakis +helmet +hood +turban +hat +cap +cowl +tiara +football helmet +hard hat +crash helmet +sunhat +fur hat +cowboy hat +bearskin +boater +snap-brim hat +fedora +cavalier hat +sombrero +tricorn +beaver +porkpie +bonnet +pith hat +bowler hat +millinery +cloche +pillbox +baseball cap +coonskin cap +shower cap +kepi +balaclava +fez +tam +beret +skullcap +cloth cap +ski cap +watch cap +bathing cap +mortarboard +yarmulke +beanie +head covering +scarf +romper +diaper +wraparound +robe +wet suit +legging +skirt +undergarment +separate +vest +shirt +overgarment +hose +burqa +trouser +trouser +straitjacket +fur +neckwear +sweat suit +leotard +swimsuit +hand-me-down +raglan +suit +sweater +gown +face veil +niqab +chador +mantilla +muffler +headscarf +tudung +feather boa +stole +hijab +khimar +dressing gown +kimono +abaya +bathrobe +gaiter +spat +overskirt +grass skirt +miniskirt +kilt +maxi +ballet skirt +dirndl +sarong +hoopskirt +petticoat +brassiere +foundation garment +singlet +garter belt +crinoline +underwear +body stocking +camisole +uplift +chemise +underpants +corset +panty girdle +roll-on +lingerie +long johns +BVD +undies +nightgown +bloomers +thong +bikini pants +briefs +pantie +drawers +work-shirt +kurta +jersey +dashiki +polo shirt +coat +cloak +snowsuit +surcoat +duffel coat +sheepskin coat +frock coat +lab coat +greatcoat +jacket +raincoat +capote +sack coat +fur coat +mess jacket +single-breasted jacket +bomber jacket +pea jacket +swallow-tailed coat +doublet +bolero +parka +oilskin +trench coat +mink +sable coat +poncho +toga virilis +toga +kameez +serape +tunic +shawl +caftan +short pants +pajama +sweat pants +salwar +breeches +chino +slacks +jodhpurs +pedal pusher +long trousers +jean +cords +Levi's +stretch pants +bellbottom trousers +buckskins +hot pants +Bermuda shorts +lederhosen +necktie +cravat +bolo tie +Windsor tie +bow tie +black tie +maillot +swimming trunks +bikini +double-breasted suit +pinstripe +single-breasted suit +pants suit +business suit +three-piece suit +two-piece +turtleneck +cardigan +sweatshirt +pullover +top +G-string +camisole +dress +bodice +blouse +halter +cocktail dress +sari +caftan +sundress +chemise +strapless +gown +jumper +dirndl +bridal gown +tea gown +ball gown +gauntlet +mitten +kid glove +belt +furnishing +money belt +holster +cartridge belt +hosiery +tights +sock +stocking +pantyhose +maillot +athletic sock +tabi +knee-high +argyle +nylons +Christmas stocking +formalwear +ensemble +outfit +ao dai +costume +fancy dress +costume +frock +sportswear +academic costume +disguise +hairpiece +dinner jacket +balldress +dinner dress +dress suit +Afro-wig +toupee +wig +dress hat +brace +athletic supporter +home appliance +dryer +vacuum +iron +trouser press +curling iron +white goods +sewing machine +serger +kitchen appliance +Hoover +travel iron +steam iron +dishwasher +refrigerator +washer +cooler +electric refrigerator +ice machine +deep-freeze +toaster +microwave +toaster oven +coffee maker +hot plate +waffle iron +disposal +espresso maker +stove +oven +food processor +ice maker +cookstove +electric range +gas range +Primus stove +broiler +rotisserie +Dutch oven +gas oven +hand blower +clothes dryer +spin dryer +tumble-dryer +wringer +bath towel +doily +Turkish towel +bed linen +pillow sham +sheet +tinfoil +plywood +doorplate +board +drumhead +panel +laminate +blackboard +snowboard +Sheetrock +surfboard +skateboard +sideboard +scoreboard +wainscot +headboard +chandelier +plumbing fixture +soap dish +toilet +shower +water faucet +flush toilet +potty seat +rope +cord +lasso +bungee +spun yarn +cordage +thread +bootlace +wick +lanyard +floss +woof +worsted +organism +cell +mistletoe +plant +animal +microorganism +bryophyte +person +fungus +benthos +flowering maple +vascular plant +strangler +aquatic +annual +houseplant +poisonous plant +agave +pteridophyte +spermatophyte +aquatic plant +herb +vine +woody plant +weed +cultivar +bulbous plant +succulent +American agave +maguey +maguey +sansevieria +dracaena +mother-in-law's tongue +fern ally +fern +club moss +scouring rush +ground pine +ground cedar +ground fir +flowering fern +lady fern +Boston fern +flowering fern +royal fern +tree fern +oak fern +common polypody +mountain fern +shield fern +deer fern +wood fern +American maidenhair fern +hart's-tongue +soft shield fern +holly fern +maidenhair +holly fern +water clover +sensitive fern +Christmas fern +angiopteris +soft tree fern +male fern +marginal wood fern +angiosperm +gymnosperm +barbados cherry +dicot +flower +wildflower +commelina +woodland star +nigella +black-eyed Susan +mistflower +calceolaria +toadflax +zinnia +centaury +Easter daisy +African violet +brompton stock +verbena +blue daisy +pink calla +Mexican sunflower +bloomer +achimenes +lychnis +painted daisy +treasure flower +globe amaranth +common valerian +rose moss +tidytips +common daisy +composite +ice plant +gentian +soapwort +anemone +veronica +larkspur +spring beauty +gazania +damask violet +Barberton daisy +bush violet +baby's breath +corydalis +calendula +sunflower +scabious +valerian +rue anemone +sandwort +candytuft +horn poppy +sandwort +poppy +stokes' aster +dahlia +Virginia spring beauty +petunia +orchid +hybrid petunia +African daisy +pink +African daisy +African daisy +daisy +common ageratum +oxeye daisy +columbine +calla lily +sweet alyssum +spathiphyllum +four o'clock +common marigold +cornflower +strawflower +silene +tuberose +common four-o'clock +rocket larkspur +bellwort +begonia +streptocarpus +Swan River daisy +wallflower +peony +love-in-a-mist +wallflower +cineraria +chrysanthemum +stock +sandwort +Malcolm stock +mountain sandwort +coneflower +ageratum +coneflower +sowbread +scorpionweed +cyclamen +delphinium +marigold +aster +cosmos +Mediterranean snapdragon +mullein pink +ragged robin +mayweed +tansy +dusty miller +corn chamomile +shasta daisy +everlasting +wingstem +rosinweed +oxeye daisy +strawflower +strawflower +cudweed +pearly everlasting +gentianella +agueweed +closed gentian +closed gentian +great yellow gentian +fringed gentian +marsh gentian +snowdrop anemone +wood anemone +wood anemone +germander speedwell +common speedwell +common sunflower +prairie sunflower +giant sunflower +Jerusalem artichoke +sweet scabious +field scabious +Iceland poppy +wind poppy +Iceland poppy +celandine +oriental poppy +opium poppy +celandine poppy +prickly poppy +California poppy +corn poppy +blue poppy +aerides +coelogyne +lady's slipper +Venus' slipper +cymbid +sobralia +spider orchid +spider orchid +Psychopsis papilio +liparis +butterfly orchid +butterfly orchid +butterfly orchid +oncidium +twayblade +twayblade +grass pink +brassavola +fragrant orchid +fly orchid +frog orchid +coral root +cattleya +lesser butterfly orchid +vanilla +short-spurred fragrant orchid +common spotted orchid +bog rose +ladies' tresses +odontoglossum +orchis +vanda +pansy orchid +Bletilla striata +rattlesnake plantain +marsh orchid +stanhopea +laelia +phaius +lizard orchid +caladenia +calypso +moth orchid +blue orchid +bee orchid +early spider orchid +masdevallia +bog rein orchid +European ladies' tresses +fen orchid +pogonia +fringed orchis +dendrobium +fly orchid +helleborine +helleborine +stelis +greater butterfly orchid +yellow lady's slipper +large yellow lady's slipper +common lady's-slipper +moccasin flower +butterfly orchid +male orchis +ragged orchid +purple-fringed orchid +stream orchid +Epipactis helleborine +sweet William +china pink +Japanese pink +carnation +cottage pink +maiden pink +meeting house +granny's bonnets +blue columbine +fire pink +white campion +bladder campion +red campion +wild pink +moss campion +wax begonia +hybrid tuberous begonia +rex begonia +crown daisy +corn marigold +florist's chrysanthemum +African marigold +French marigold +New England aster +bushy aster +Michaelmas daisy +Indian paintbrush +goldenrod +sand verbena +bitterroot +Indian pipe +heliopsis +meadow goldenrod +pasqueflower +fleabane +blazing star +edelweiss +coneflower +balloon flower +wild carrot +prairie gentian +desert sunflower +Arnica montana +butterweed +gaillardia +brittlebush +orange daisy +daisy fleabane +Mexican hat +long-head coneflower +cycad +welwitschia +encephalartos +dioon +macrozamia +false sago +water shamrock +water hyacinth +pistia +water lily +marsh plant +water nymph +European white lily +bog star +marsh marigold +wild calla +sedge +parnassia +skunk cabbage +skunk cabbage +cotton grass +nutgrass +common cotton grass +winter aconite +buttercup +phlox +willowherb +stapelia +skullcap +bedstraw +gumweed +kangaroo paw +common chickweed +hyssop +arum +common comfrey +borage +nasturtium +canna +loosestrife +toad lily +globe thistle +wild thyme +common fennel +bear's breech +ironweed +feverfew +monarda +physostegia +creeping bugle +vegetable +hedge nettle +plum tomato +ground cherry +flax +primrose +oxalis +kniphofia +boneset +chickweed +periwinkle +garden angelica +bugloss +Dutchman's breeches +pie plant +cow parsnip +butterbur +milk thistle +mouse-ear chickweed +yellow bells +lobelia +anise hyssop +banana +Joe-Pye weed +Joe-Pye weed +garden forget-me-not +evening primrose +spiderflower +sweet false chamomile +agrimonia +hepatica +medic +peperomia +geranium +viola +okra +bergenia +astrantia +aspidistra +thyme +common teasel +carnivorous plant +harvest-lice +nemophila +hawkweed +hawkweed +fleabane +plumbago +spiderwort +prickly poppy +common foxglove +stonecrop +garden lettuce +teasel +herb Paris +coltsfoot +basil +sainfoin +sneezeweed +cockscomb +baby blue-eyes +coleus +spurge nettle +arnica +sour dock +clover +mint +coreopsis +pimpernel +kidney vetch +foxglove +legume +reseda +forget-me-not +Virginia bluebell +pineapple +blueweed +anchusa +moss pink +common dandelion +false lupine +sage +chamomile +crucifer +chicory +broad-leaved plantain +bugle +milkweed +fireweed +spirea +inula +hemp nettle +garden nasturtium +pokeweed +moneywort +asparagus +Italian parsley +rhubarb +jewelweed +asparagus fern +sedum +yarrow +bird's foot trefoil +scarlet pimpernel +campanula +mayapple +painted nettle +pigweed +bleeding heart +achillea +snow-in-summer +gramineous plant +balsamroot +Abyssinian banana +herbage +astilbe +ginger +saxifrage +cow parsley +dill +common mullein +dead nettle +creeping buttercup +meadow buttercup +yellow bedstraw +sweet woodruff +caladium +cuckoopint +jack-in-the-pulpit +alocasia +taro +amorphophallus +bee balm +bee balm +artichoke +cardoon +tomatillo +tomatillo +English primrose +oxlip +cowslip +polyanthus +creeping oxalis +common wood sorrel +Bermuda buttercup +red-hot poker +poker plant +dwarf banana +Japanese banana +plantain +sundrops +common evening primrose +ivy geranium +cranesbill +fish geranium +rose geranium +meadow cranesbill +wild geranium +dove's foot geranium +herb robert +horned violet +field pansy +violet +dog violet +pale violet +bird's-foot violet +hedge violet +Venus's flytrap +pitcher plant +tropical pitcher plant +sundew +white clover +red clover +crimson clover +pennyroyal +water-mint +beach pea +chickpea +vetch +tufted vetch +bean +wild pea +scarlet runner +sieva bean +clary +common sage +clary sage +wild sage +purple sage +meadow clary +bok choy +mustard +cabbage +cauliflower +collard +broccoli +brussels sprout +garlic mustard +head cabbage +radish plant +bittercress +alyssum +field mustard +rape +radish +radish +lady's smock +crinkleroot +butterfly weed +swamp milkweed +tussock bellflower +Canterbury bell +clustered bellflower +peach bells +giant bamboo +grass +fescue +cordgrass +feather reed grass +reed grass +orchard grass +cereal +broom beard grass +tall oat grass +tallgrass +St. Augustine grass +pampas grass +grama +dallisgrass +zoysia +rye grass +brome +fountain grass +rye +popcorn +wheat +millet +sorghum +panic grass +goose grass +switch grass +common ginger +shellflower +meadow saxifrage +purple saxifrage +white dead nettle +henbit +ground ivy +blue pea +purple clematis +black-eyed Susan +bougainvillea +butterfly pea +butterfly pea +bindweed +kudzu +Boston ivy +squash +yellow jasmine +wax plant +morning glory +liana +Japanese wistaria +allamanda +field bindweed +common allamanda +passionflower +convolvulus +gourd +grape +Chinese gooseberry +summer squash +winter squash +pumpkin +spaghetti squash +yellow squash +acorn squash +winter crookneck +cypress vine +Japanese morning glory +moonflower +golden pothos +ceriman +jade vine +pothos +love-in-a-mist +maypop +granadilla +sweet melon +bottle gourd +net melon +winter melon +cantaloupe +Sauvignon grape +fox grape +wild indigo +shrub +tree +raspberry +lupine +abelia +banksia +bird pepper +sea holly +guelder rose +crape myrtle +castor-oil plant +spirea +hydrangea +fuchsia +redberry +saltbush +false indigo +bridal wreath +protea +Oregon grape +grevillea +gorse +rockrose +cowberry +subshrub +honeypot +California fuchsia +sumac +jasmine +impala lily +currant +axseed +mimosa +southern buckthorn +flowering quince +yucca +purple heather +waratah +mallow +strawberry tree +mock orange +honeysuckle +spurge +kalmia +bush hibiscus +weigela +Christmasberry +angel's trumpet +angel's trumpet +gooseberry +dusty miller +croton +Pyracantha +forsythia +artemisia +silversword +waratah +philadelphus +common lilac +saltwort +calliandra +wahoo +bird of paradise +cape jasmine +camellia +night jasmine +rose +mountain laurel +cotoneaster +rhododendron +frangipani +broom +desert pea +lavender +butterfly bush +deutzia +hortensia +burdock +prairie smoke +centaury +sea lavender +common mugwort +bird's foot trefoil +large periwinkle +great burdock +St John's wort +eriogonum +purple loosestrife +loosestrife +mountain avens +matilija poppy +bur marigold +wild lupine +marguerite +dusty miller +great knapweed +knapweed +creeping St John's wort +klammath weed +common St John's wort +common jasmine +winter jasmine +Adam's needle +bear grass +Joshua tree +Spanish dagger +hollyhock +rose mallow +common mallow +marsh mallow +musk mallow +hibiscus +althea +rose mallow +cotton rose +woodbine +trumpet honeysuckle +Japanese honeysuckle +poinsettia +crown of thorns +damask rose +musk rose +azalea +rosebay +swamp azalea +common broom +woodwaxen +English lavender +spike lavender +French lavender +locust tree +kowhai +bottle-tree +timber tree +linden +bonsai +snag +hackberry +pepper tree +Japanese oak +European hackberry +cork tree +birch +star anise +red silk-cotton tree +roble +common alder +fig tree +Japanese pagoda tree +albizzia +European hornbeam +cassia +coral tree +neem +white mangrove +Chinese parasol tree +bayberry +yellowwood +elm +alder +prickly ash +angiospermous tree +chestnut +cabbage bark +ash +beech +fringe tree +golden shower tree +lead tree +palm +balata +sapling +black beech +acacia +coffee +gymnospermous tree +ceibo +incense tree +lacebark +shade tree +pollard +gum tree +wild medlar +hornbeam +willow +textile screw pine +mescal bean +Brazilian rosewood +pandanus +white mangrove +oak +bean tree +plane tree +blackwood +coralwood +Kentucky coffee tree +black locust +honey locust +flame tree +flame tree +kurrajong +American basswood +silver lime +black birch +silver birch +swamp birch +downy birch +grey birch +golden fig +India-rubber tree +fig +banyan +pipal +rain tree +silk tree +smooth-leaved elm +American elm +English elm +cedar elm +myrtle +mangrove +magnolia +Queen's crape myrtle +looking-glass plant +tulip tree +maple +nut tree +redbud +baobab +poplar +tree of heaven +ailanthus +dogwood +holly +cacao +laurel +mountain ebony +kapok +sorrel tree +cacao bean +Spanish elm +rowan +mountain ash +royal poinciana +iron tree +fruit tree +sweet bay +southern magnolia +star magnolia +umbrella tree +box elder +red maple +hedge maple +Norway maple +Japanese maple +sycamore +California box elder +silver maple +sugar maple +Oregon maple +cashew +walnut +hazelnut +black walnut +English walnut +black poplar +aspen +cottonwood +white poplar +quaking aspen +Eastern cottonwood +black cottonwood +cornelian cherry +bunchberry +common European dogwood +common white dogwood +bearberry +inkberry +true laurel +cassia +citrus +mulberry +jackfruit +pomegranate +pawpaw +persimmon +carambola +plum +almond tree +durian +papaya +olive tree +longan +pear +loquat +medlar +peach +white mulberry +olive +litchi +Japanese apricot +rambutan +apple tree +Japanese persimmon +mango +breadfruit +guava +guava +jaboticaba +cherry +Surinam cherry +lime +mandarin +orange +kumquat +lemon +pomelo +grapefruit +clementine +tangerine +sweet orange +sour orange +bergamot +cherry plum +Allegheny plum +flowering almond +almond +flowering almond +crab apple +apple +wild apple +Southern crab apple +Bechtel crab +Iowa crab +sour cherry +flowering cherry +wild cherry +sweet cherry +chokecherry +Japanese flowering cherry +oriental cherry +fuji +hagberry tree +black cherry +Ozark chinkapin +American chestnut +pumpkin ash +mountain ash +manna ash +European ash +red ash +weeping beech +American beech +copper beech +bamboo palm +wine palm +fan palm +royal palm +cabbage palm +cabbage palm +sago palm +miniature fan palm +lady palm +feather palm +coconut +cabbage palm +carnauba +caranday +palmyra +cabbage palmetto +key palm +saw palmetto +palmetto +date palm +oil palm +silver wattle +wattle +huisache +ginkgo +conifer +kauri +green douglas fir +miro +cedar +cedar +douglas fir +matai +arborvitae +spruce +yew +araucaria +cypress +metasequoia +pine +fir +larch +hemlock +cedar of Lebanon +Atlas cedar +deodar +southern white cedar +incense cedar +Oregon cedar +Japanese cedar +Oriental arborvitae +American arborvitae +western red cedar +Colorado spruce +white spruce +Norway spruce +red spruce +black spruce +Sitka spruce +oriental spruce +bunya bunya +monkey puzzle +Monterey cypress +Arizona cypress +Italian cypress +Scotch pine +pond pine +pitch pine +table-mountain pine +ancient pine +stone pine +Jeffrey pine +loblolly pine +Swiss pine +spruce pine +white pine +red pine +Japanese black pine +Swiss mountain pine +black pine +Monterey pine +yellow pine +Torrey pine +shore pine +bristlecone pine +whitebark pine +western white pine +longleaf pine +ponderosa +silver fir +Fraser fir +Alpine fir +amabilis fir +lowland fir +balsam fir +European silver fir +white fir +western larch +American larch +eastern hemlock +western hemlock +mountain hemlock +gumbo-limbo +elephant tree +sweet gum +eucalyptus +sour gum +liquidambar +snow gum +mountain ash +black mallee +alpine ash +red gum +red gum +blue gum +osier +pussy willow +bay willow +weeping willow +swamp willow +purple willow +common osier +European turkey oak +red oak +cork oak +black oak +live oak +chestnut oak +bluejack oak +pin oak +post oak +shingle oak +white oak +laurel oak +northern red oak +southern red oak +southern live oak +canyon oak +coast live oak +chinquapin oak +basket oak +swamp chestnut oak +bur oak +Oregon white oak +common oak +tamarind +catalpa +carob +California sycamore +American sycamore +London plane +lightwood +black mangrove +wild raspberry +black raspberry +bluebonnet +Texas bluebonnet +thistle +cat's-ear +corn cockle +yellow rocket +fireweed +stinging nettle +horseweed +stemless carline thistle +musk thistle +cotton thistle +plume thistle +field thistle +bull thistle +Canada thistle +hippeastrum +narcissus +iridaceous plant +fritillary +liliaceous plant +star-of-Bethlehem +daffodil +jonquil +jonquil +iris +blue-eyed grass +blackberry-lily +dwarf iris +dwarf iris +beardless iris +bearded iris +Japanese iris +German iris +snake's head fritillary +crown imperial +dogtooth violet +lily +African lily +grape hyacinth +common camas +false lily of the valley +common hyacinth +camas +clintonia +lemon lily +squaw grass +scilla +tulip +alliaceous plant +fawn lily +glacier lily +yellow adder's tongue +Turk's-cap +Turk's-cap +tiger lily +tiger lily +mountain lily +Easter lily +tassel hyacinth +common grape hyacinth +Tulipa gesneriana +Darwin tulip +garlic chive +wild garlic +Hottentot fig +livingstone daisy +cactus +nopal +nopal +barrel cactus +night-blooming cereus +night-blooming cereus +night-blooming cereus +cholla +echinocactus +mammillaria +feather ball +prickly pear +crab cactus +saguaro +Christmas cactus +hedgehog cactus +golden barrel cactus +flamingo flower +anthurium +gloxinia +baneberry +red baneberry +poison ivy +gloriosa +monkshood +American holly +oleander +poison ash +herbivore +big game +thoroughbred +creepy-crawly +young +domestic animal +pet +critter +larva +feeder +male +pest +omnivore +predator +chordate +work animal +invertebrate +female +marine animal +scavenger +hexapod +mate +prey +carnivore +young mammal +orphan +spat +young bird +hatchling +foal +kitten +calf +pup +calf +lamb +baby +puppy +suckling +cub +piglet +nestling +fledgling +head +stray +feeder +tadpole +caterpillar +nymph +doodlebug +tobacco hornworm +cabbageworm +tomato hornworm +silkworm +cutworm +woolly bear +measuring worm +armyworm +silkworm +tussock caterpillar +tent caterpillar +colt +ridgeling +sire +sea squirt +ascidian +vertebrate +aquatic vertebrate +amphibian +mammal +baby +fetus +quadruped +reptile +bird +fish +lamprey +teleost fish +food fish +elasmobranch +ganoid +trumpetfish +pipefish +seahorse +spiny-finned fish +soft-finned fish +needlefish +beluga +gar +bowfin +paddlefish +sturgeon +percoid fish +dragonet +frogfish +barracuda +soldierfish +goosefish +scorpaenoid +flatfish +great barracuda +plectognath +snook +perch +perch +dolphinfish +freshwater bass +scombroid +bass +parrotfish +sea bream +grunt +flathead +bluefish +carangid fish +damselfish +butterfly fish +mudskipper +pike +goby +tautog +sunfish +snapper +snapper +sciaenid fish +wolffish +cichlid +wrasse +yellow perch +European perch +walleye +mackerel +skipjack +black marlin +sailfish +marlin +bonito +tuna +wahoo +Spanish mackerel +Spanish mackerel +cero +king mackerel +bluefin +yellowfin +jack +permit +scad +crevalle jack +kingfish +amberjack +yellowtail +horse mackerel +horse mackerel +clown anemone fish +sergeant major +anemone fish +chaetodon +rock beauty +angelfish +northern pike +pickerel +muskellunge +black bass +pumpkinseed +freshwater bream +bluegill +crappie +smallmouth +largemouth +sea trout +croaker +kingfish +mulloway +red drum +white croaker +white croaker +scorpaenid +flathead +scorpionfish +lionfish +stonefish +rockfish +plaice +flounder +halibut +cowfish +boxfish +ocean sunfish +puffer +spiny puffer +triggerfish +balloonfish +porcupinefish +tarpon +bonefish +pollack +anchovy +lizardfish +catfish +cypriniform fish +eel +clupeid fish +European catfish +flathead catfish +channel catfish +blue catfish +characin +electric eel +cyprinodont +loach +cyprinid +topminnow +piranha +cardinal tetra +tetra +killifish +striped killifish +guppy +swordtail +carp +minnow +tench +crucian carp +goldfish +gudgeon +platy +mosquitofish +conger +tuna +moray +sardine +pilchard +sea bass +trout +salmon +barracouta +grouper +striped bass +jewfish +hind +sea trout +brook trout +rainbow trout +brown trout +lake trout +chinook +Atlantic salmon +redfish +coho +landlocked salmon +shark +ray +sand tiger +angel shark +nurse shark +requiem shark +smooth dogfish +hammerhead +mackerel shark +whale shark +bull shark +blue shark +sandbar shark +blacktip shark +whitetip shark +tiger shark +lemon shark +whitetip shark +smoothhound +great white shark +mako +porbeagle +stingray +electric ray +spotted eagle ray +Atlantic manta +manta +skate +eagle ray +salamander +frog +spotted salamander +newt +European fire salamander +slender salamander +ambystomid +eft +common newt +red eft +spotted salamander +axolotl +tiger salamander +true toad +true frog +tailed frog +crapaud +tree toad +tree frog +natterjack +Eurasian green toad +bufo +American toad +agua +western toad +European toad +grass frog +wood-frog +bullfrog +leopard frog +pickerel frog +green frog +spring peeper +chorus frog +placental +tusker +monotreme +marsupial +female mammal +aardvark +livestock +insectivore +hyrax +doe +edentate +stag +bull +primate +carnivore +bat +aquatic mammal +lagomorph +rock hyrax +yearling +rodent +cow +pachyderm +buck +pangolin +ungulate +shrew +hedgehog +peba +sloth +armadillo +anteater +two-toed sloth +two-toed sloth +three-toed sloth +ant bear +tamandua +simian +tarsier +homo +ape +lemur +monkey +Homo sapiens sapiens +Homo sapiens +Neandertal man +anthropoid ape +lesser ape +great ape +siamang +gibbon +chimpanzee +orangutan +gorilla +pygmy chimpanzee +central chimpanzee +western lowland gorilla +mountain gorilla +silverback +indri +Madagascar cat +potto +galago +slow loris +Old World monkey +New World monkey +baboon +vervet +proboscis monkey +colobus +patas +macaque +guenon +langur +chacma +mandrill +Barbary ape +rhesus +spider monkey +marmoset +squirrel monkey +titi +capuchin +howler monkey +tamarin +pygmy marmoset +procyonid +feline +viverrine +canine +musteline mammal +bear +coati +common raccoon +lesser panda +raccoon +kinkajou +giant panda +big cat +cat +jaguar +tiger +leopard +cheetah +lion +snow leopard +tigress +Bengal tiger +tiger cub +lioness +lion cub +domestic cat +wildcat +tabby +tiger cat +tabby +tortoiseshell +Manx +Egyptian cat +Abyssinian +kitty +Angora +Persian cat +Burmese cat +Siamese cat +alley cat +tom +mouser +margay +ocelot +lynx +cougar +European wildcat +serval +manul +sand cat +common lynx +bobcat +caracal +Canada lynx +meerkat +genet +mongoose +slender-tailed meerkat +suricate +dog +wild dog +wolf +bitch +jackal +fox +hyena +pug +corgi +Great Pyrenees +Brabancon griffon +poodle +cur +Leonberg +griffon +dalmatian +pooch +spitz +toy dog +hunting dog +working dog +basenji +Mexican hairless +Newfoundland +lapdog +Cardigan +Pembroke +standard poodle +toy poodle +miniature poodle +Pomeranian +keeshond +chow +Samoyed +toy spaniel +Shih-Tzu +toy terrier +Maltese dog +Japanese spaniel +Chihuahua +Pekinese +King Charles spaniel +Blenheim spaniel +papillon +terrier +Rhodesian ridgeback +sausage dog +sporting dog +hound +dachshund +Dandie Dinmont +schnauzer +wirehair +Airedale +West Highland white terrier +Kerry blue terrier +Norfolk terrier +Border terrier +Yorkshire terrier +wire-haired fox terrier +Bedlington terrier +Tibetan terrier +silky terrier +Lhasa +Scotch terrier +cairn +Boston bull +fox terrier +Australian terrier +bullterrier +Norwich terrier +Irish terrier +rat terrier +soft-coated wheaten terrier +standard schnauzer +giant schnauzer +miniature schnauzer +Lakeland terrier +Welsh terrier +Sealyham terrier +Staffordshire bullterrier +American Staffordshire terrier +Manchester terrier +toy Manchester +water dog +pointer +bird dog +setter +spaniel +retriever +vizsla +German short-haired pointer +Gordon setter +English setter +Irish setter +cocker spaniel +water spaniel +springer spaniel +Brittany spaniel +clumber +Sussex spaniel +Irish water spaniel +English springer +Welsh springer spaniel +flat-coated retriever +golden retriever +curly-coated retriever +Chesapeake Bay retriever +Labrador retriever +otterhound +bloodhound +wolfhound +basset +Ibizan hound +Norwegian elkhound +coonhound +Saluki +Afghan hound +black-and-tan coonhound +bluetick +Scottish deerhound +redbone +foxhound +beagle +Weimaraner +greyhound +borzoi +Irish wolfhound +English foxhound +Walker hound +whippet +Italian greyhound +Great Dane +watchdog +Eskimo dog +Tibetan mastiff +sled dog +Saint Bernard +French bulldog +police dog +bulldog +Sennenhunde +bull mastiff +shepherd dog +boxer +mastiff +kuvasz +housedog +pinscher +schipperke +Doberman +miniature pinscher +affenpinscher +Siberian husky +malamute +Greater Swiss Mountain dog +EntleBucher +Bernese mountain dog +Appenzeller +Belgian sheepdog +kelpie +Shetland sheepdog +komondor +Border collie +collie +Rottweiler +Old English sheepdog +German shepherd +briard +Bouvier des Flandres +groenendael +malinois +African hunting dog +dingo +dhole +coyote +wolf pup +red wolf +white wolf +timber wolf +red fox +red fox +kit fox +Arctic fox +grey fox +kit fox +spotted hyena +striped hyena +mink +black-footed ferret +striped skunk +pine marten +sea otter +otter +weasel +polecat +glutton +skunk +badger +ferret +river otter +Eurasian otter +ice bear +American black bear +bear cub +Asiatic black bear +brown bear +sloth bear +grizzly +Alaskan brown bear +carnivorous bat +flying fox +fruit bat +brown bat +vespertilian bat +pallid bat +pipistrelle +cetacean +sea cow +pinniped mammal +whale +toothed whale +baleen whale +dolphin +bottle-nosed whale +porpoise +common dolphin +bottlenose dolphin +pilot whale +killer whale +white whale +Pacific bottlenose dolphin +Atlantic bottlenose dolphin +grey whale +rorqual +blue whale +lesser rorqual +finback +manatee +dugong +walrus +seal +earless seal +eared seal +elephant seal +harbor seal +harp seal +fur seal +fur seal +sea lion +California sea lion +Australian sea lion +Steller sea lion +pika +leporid +rabbit +hare +eastern cottontail +wood rabbit +bunny +European rabbit +lapin +Angora +rabbit ears +snowshoe hare +European hare +jackrabbit +chinchilla +rat +capybara +golden hamster +water vole +porcupine +coypu +vole +beaver +hamster +prairie dog +squirrel +marmot +blacktail prairie dog +cavy +gerbil +mouse +muskrat +gopher +brown rat +black rat +chipmunk +ground squirrel +eastern chipmunk +tree squirrel +rock squirrel +mantled ground squirrel +eastern grey squirrel +red squirrel +black squirrel +American red squirrel +fox squirrel +hoary marmot +groundhog +aperea +guinea pig +field mouse +house mouse +elephant +African elephant +Indian elephant +even-toed ungulate +odd-toed ungulate +ruminant +camel +swine +llama +vicuna +collared peccary +hippopotamus +peccary +pronghorn +deer +bovid +giraffe +okapi +woodland caribou +caribou +fallow deer +elk +hart +mule deer +fawn +red deer +muntjac +Virginia deer +wapiti +Japanese deer +roe deer +black-tailed deer +wild sheep +bison +musk ox +Old World buffalo +bovine +antelope +sheep +goat antelope +goat +aoudad +mountain sheep +Dall sheep +bighorn +mouflon +American bison +wisent +carabao +water buffalo +Cape buffalo +Brahman +ox +zebu +cattle +yak +gaur +beef +ox +bull +bullock +heifer +cow +dairy cattle +longhorn +Charolais +Hereford +Durham +Aberdeen Angus +Galloway +Friesian +Brown Swiss +kudu +addax +blackbuck +waterbuck +eland +steenbok +dik-dik +gnu +harnessed antelope +gerenuk +sassaby +impala +greater kudu +sable antelope +hartebeest +bongo +gemsbok +oryx +gazelle +nyala +bushbuck +Thomson's gazelle +springbok antelope +domestic sheep +black sheep +ewe +wether +ram +mountain goat +chamois +takin +nanny +kid +ibex +Angora +domestic goat +billy +wild goat +Bactrian camel +Arabian camel +wild boar +warthog +boar +hog +guanaco +alpaca +rhinoceros +tapir +equine +Malayan tapir +Indian rhinoceros +black rhinoceros +white rhinoceros +horse +zebra +ass +bay +broodmare +racehorse +palomino +wild horse +pinto +hack +roan +male horse +post horse +liver chestnut +tarpan +saddle horse +chestnut +harness horse +polo pony +workhorse +mare +pony +pony +sorrel +yearling +thoroughbred +trotting horse +stud +stallion +gelding +Tennessee walker +hack +cavalry horse +grey +Morgan +buckskin +dun +Arabian +quarter horse +cob +hackney +plow horse +farm horse +draft horse +carthorse +Percheron +Clydesdale +shire +cayuse +bronco +mustang +Welsh pony +Shetland pony +Exmoor +common zebra +mountain zebra +grevy's zebra +jennet +burro +domestic ass +echidna +platypus +echidna +kangaroo +koala +wombat +common opossum +opossum +dasyurid marsupial +phalanger +giant kangaroo +wallaby +rock wallaby +tree wallaby +Tasmanian devil +numbat +chelonian +diapsid +turtle +Western box turtle +box turtle +common snapping turtle +terrapin +soft-shelled turtle +painted turtle +sea turtle +snapping turtle +slider +tortoise +mud turtle +cooter +hawksbill turtle +loggerhead +green turtle +leatherback turtle +ridley +Pacific ridley +Atlantic ridley +giant tortoise +gopher tortoise +European tortoise +desert tortoise +crocodilian reptile +snake +tuatara +lizard +dinosaur +alligator +crocodile +American alligator +caiman +Asian crocodile +African crocodile +blind snake +viper +sea snake +elapid +constrictor +colubrid snake +horned viper +asp +adder +puff adder +pit viper +water moccasin +copperhead +rattlesnake +ground rattler +massasauga +diamondback +Mojave rattlesnake +timber rattlesnake +prairie rattlesnake +Western diamondback +sidewinder +rock rattlesnake +speckled rattlesnake +cobra +green mamba +taipan +copperhead +mamba +coral snake +coral snake +Indian cobra +hamadryad +boa +python +rosy boa +boa constrictor +anaconda +reticulated python +carpet snake +rock python +blacksnake +garter snake +bull snake +hognose snake +rat snake +whip-snake +water snake +green snake +racer +green snake +thunder snake +ringneck snake +vine snake +king snake +night snake +ribbon snake +common garter snake +pine snake +gopher snake +corn snake +black rat snake +grass snake +common water snake +water moccasin +smooth green snake +rough green snake +milk snake +common kingsnake +banded gecko +chameleon +monitor +skink +Gila monster +Komodo dragon +whiptail +iguanid +agamid +gecko +African chameleon +lacertid lizard +anguid lizard +horned lizard +tree lizard +chuckwalla +American chameleon +basilisk +side-blotched lizard +spiny lizard +collared lizard +common iguana +marine iguana +leopard lizard +western fence lizard +fence lizard +agama +mountain devil +frilled lizard +green lizard +sand lizard +blindworm +alligator lizard +ornithischian +tyrannosaur +stegosaur +triceratops +bird of passage +aquatic bird +passerine +cock +hummingbird +piciform bird +coraciiform bird +quetzal +bird of prey +caprimulgiform bird +cuculiform bird +gamecock +ratite +gallinaceous bird +trogon +parrot +carinate +dickeybird +hen +wading bird +swan +gallinule +seabird +waterfowl +heron +crested cariama +trumpeter +bustard +ibis +stork +whooping crane +crane +limpkin +chunga +flamingo +rail +spoonbill +shoebill +shorebird +great blue heron +night heron +little blue heron +boatbill +great white heron +egret +bittern +black-crowned night heron +yellow-crowned night heron +great white heron +little egret +snowy egret +American egret +cattle egret +least bittern +American bittern +wood ibis +sacred ibis +marabou +black stork +white stork +saddlebill +jabiru +policeman bird +wood ibis +notornis +weka +spotted crake +crake +coot +Old World coot +American coot +common spoonbill +roseate spoonbill +plover +godwit +Hudsonian godwit +stilt +stone curlew +oystercatcher +stilt +American woodcock +snipe +woodcock +avocet +sandpiper +European curlew +pratincole +curlew +phalarope +golden plover +ruddy turnstone +killdeer +lapwing +turnstone +piping plover +black-necked stilt +black-winged stilt +whole snipe +Wilson's snipe +great snipe +dowitcher +tattler +greenshank +willet +curlew sandpiper +sanderling +redshank +spotted sandpiper +knot +red-backed sandpiper +upland sandpiper +least sandpiper +pectoral sandpiper +ruff +European sandpiper +yellowlegs +greater yellowlegs +lesser yellowlegs +red phalarope +Wilson's phalarope +pen +cygnet +trumpeter +coscoroba +mute swan +cob +whooper +black swan +tundra swan +whistling swan +Bewick's swan +purple gallinule +European gallinule +moorhen +coastal diving bird +pelagic bird +grebe +auk +loon +pelecaniform seabird +sphenisciform seabird +puffin +larid +jaeger +skimmer +sea swallow +gull +tern +ivory gull +mew +laughing gull +black-backed gull +kittiwake +herring gull +skua +parasitic jaeger +petrel +albatross +wandering albatross +shearwater +storm petrel +fulmar +red-necked grebe +great crested grebe +pied-billed grebe +black-necked grebe +dabchick +razorbill +guillemot +auklet +murre +black guillemot +common murre +pigeon guillemot +frigate bird +cormorant +snakebird +pelican +gannet +water turkey +tropic bird +white pelican +Old world white pelican +solan +booby +penguin +emperor penguin +jackass penguin +king penguin +rock hopper +Adelie +horned puffin +tufted puffin +Atlantic puffin +anseriform bird +goose +duck +blue goose +barnacle goose +snow goose +Chinese goose +common brant goose +brant +gosling +greylag +gander +honker +diving duck +scaup +shelduck +wood drake +bufflehead +black duck +mandarin duck +American widgeon +pintail +mallard +sheldrake +teal +Barrow's goldeneye +quack-quack +wild duck +ruddy duck +wood duck +drake +muscovy duck +shoveler +dabbling duck +widgeon +sea duck +redhead +pochard +goldeneye +canvasback +duckling +greater scaup +lesser scaup +garganey +greenwing +bluewing +eider +old squaw +merganser +scoter +common scoter +American merganser +red-breasted merganser +hooded merganser +smew +goosander +wren +broadbill +tyrannid +oscine +scrubbird +sparrow +marsh wren +rock wren +winter wren +cactus wren +house wren +Carolina wren +ovenbird +manakin +pitta +woodhewer +New World flycatcher +kingbird +phoebe +pewee +vermillion flycatcher +western wood pewee +scissortail +grey kingbird +eastern kingbird +Arkansas kingbird +warbler +brown creeper +corvine bird +starling +pipit +titmouse +fairy bluebird +thrush +hedge sparrow +wood swallow +shrike +lark +golden oriole +Old World flycatcher +thrasher +vireo +tanager +honeycreeper +finch +bowerbird +water ouzel +accentor +mockingbird +brown thrasher +skylark +catbird +satin bowerbird +waxwing +red-eyed vireo +New World oriole +Old World oriole +babbler +swallow +creeper +songbird +Australian magpie +wagtail +meadow pipit +spotted flycatcher +weaver +nuthatch +greater whitethroat +New World warbler +kinglet +Old World warbler +gnatcatcher +lesser whitethroat +yellowthroat +common yellowthroat +ovenbird +parula warbler +Blackburn +yellow warbler +American redstart +yellow-breasted chat +Audubon's warbler +Wilson's warbler +Cape May warbler +myrtle warbler +goldcrest +ruby-crowned kinglet +tailorbird +sedge warbler +wren warbler +blackcap +rook +Clark's nutcracker +jackdaw +European magpie +jay +raven +crow +magpie +American crow +blue jay +Canada jay +common starling +hill myna +myna +bushtit +chickadee +blue tit +tufted titmouse +Carolina chickadee +black-capped chickadee +robin +robin +hermit thrush +redwing +fieldfare +song thrush +nightingale +blackbird +missel thrush +ring ouzel +wheatear +bluebird +thrush nightingale +bluethroat +redstart +bulbul +Old World chat +wood thrush +stonechat +whinchat +butcherbird +loggerhead shrike +bush shrike +northern shrike +European shrike +western tanager +summer tanager +scarlet tanager +serin +bullfinch +grosbeak +goldfinch +New World sparrow +crossbill +bunting +linnet +cardinal +siskin +common canary +towhee +purple finch +honeycreeper +brambling +New World goldfinch +pine siskin +redpoll +dark-eyed junco +house finch +chaffinch +canary +redpoll +junco +pine grosbeak +evening grosbeak +hawfinch +song sparrow +white-throated sparrow +tree sparrow +field sparrow +white-crowned sparrow +swamp sparrow +chipping sparrow +indigo bunting +reed bunting +snow bunting +ortolan +yellowhammer +cedar waxwing +Bohemian waxwing +bobolink +meadowlark +northern oriole +orchard oriole +New World blackbird +eastern meadowlark +western meadowlark +Bullock's oriole +Baltimore oriole +purple grackle +cowbird +grackle +red-winged blackbird +white-bellied swallow +tree swallow +martin +barn swallow +cliff swallow +house martin +bank martin +butcherbird +currawong +Java sparrow +zebra finch +red-breasted nuthatch +European nuthatch +white-breasted nuthatch +English sparrow +tree sparrow +thornbill +Archilochus colubris +jacamar +woodpecker +barbet +toucanet +toucan +flicker +downy woodpecker +green woodpecker +sapsucker +wryneck +redheaded woodpecker +yellow-shafted flicker +red-breasted sapsucker +yellow-bellied sapsucker +kingfisher +roller +motmot +Euopean hoopoe +hornbill +European roller +hoopoe +bee eater +kookaburra +Eurasian kingfisher +belted kingfisher +vulture +hawk +secretary bird +eagle +owl +Old World vulture +New World vulture +Egyptian vulture +bearded vulture +black vulture +griffon vulture +black vulture +buzzard +king vulture +condor +Andean condor +California condor +harrier +goshawk +red-shouldered hawk +honey buzzard +falcon +harrier eagle +Cooper's hawk +osprey +kite +rough-legged hawk +buzzard +sparrow hawk +marsh harrier +marsh hawk +carancha +gyrfalcon +peregrine +caracara +hobby +pigeon hawk +kestrel +sparrow hawk +white-tailed kite +swallow-tailed kite +black kite +eaglet +golden eagle +sea eagle +bald eagle +harpy +tawny eagle +fishing eagle +ern +tawny owl +owlet +spotted owl +screech owl +horned owl +screech owl +little owl +barn owl +scops owl +Old World scops owl +hawk owl +great horned owl +barred owl +long-eared owl +great grey owl +frogmouth +goatsucker +touraco +cuckoo +coucal +roadrunner +rhea +rhea +ostrich +emu +cassowary +domestic fowl +columbiform bird +brush turkey +red jungle fowl +jungle fowl +game bird +turkey cock +bantam +turkey +guinea fowl +chicken +cockerel +cock +Rhode Island red +chick +Orpington +hen +pullet +brood hen +sandgrouse +pigeon +domestic pigeon +dove +wood pigeon +rock dove +homing pigeon +roller +Streptopelia turtur +turtledove +Australian turtledove +mourning dove +phasianid +tinamou +grouse +pheasant +quail +partridge +tragopan +ring-necked pheasant +golden pheasant +peafowl +peahen +blue peafowl +peacock +green peafowl +bobwhite +California quail +northern bobwhite +red-legged partridge +Hungarian partridge +spruce grouse +prairie chicken +capercaillie +ruffed grouse +sage grouse +moorhen +black grouse +ptarmigan +cockateel +parakeet +cockatoo +poll +kea +African grey +macaw +amazon +lovebird +lory +popinjay +budgerigar +ring-necked parakeet +sulphur-crested cockatoo +pink cockatoo +rainbow lorikeet +lorikeet +beast of burden +draft animal +ctenophore +worm +mollusk +echinoderm +coelenterate +arthropod +sponge +nematode +annelid +flatworm +medicinal leech +earthworm +chiton +bivalve +cephalopod +gastropod +oyster +ark shell +clam +mussel +cockle +scallop +pearl oyster +soft-shell clam +quahog +giant clam +freshwater mussel +edible mussel +zebra mussel +octopod +chambered nautilus +cuttlefish +octopus +paper nautilus +sea hare +cowrie +conch +seasnail +ormer +tiger cowrie +sea slug +slug +snail +common limpet +whelk +nerita +edible snail +brown snail +garden snail +starfish +feather star +sand dollar +sea urchin +sea cucumber +brittle star +polyp +anthozoan +Portuguese man-of-war +jellyfish +sea pen +sea anemone +coral +stony coral +gorgonian +sea fan +mushroom coral +brain coral +centipede +crustacean +trilobite +millipede +arachnid +horseshoe crab +instar +insect +house centipede +daphnia +brachyuran +mantis shrimp +malacostracan crustacean +decapod crustacean +isopod +amphipod +pill bug +woodlouse +lobster +shrimp +hermit crab +prawn +crab +crayfish +Norway lobster +spiny lobster +American lobster +king crab +blue crab +rock crab +Dungeness crab +fiddler crab +European spider crab +scorpion +harvestman +acarine +spider +tick +mite +wood tick +orb-weaving spider +European wolf spider +tarantula +wolf spider +garden spider +black widow +black and gold garden spider +barn spider +orthopterous insect +hemipterous insect +neuropteron +dictyopterous insect +collembolan +mayfly +homopterous insect +dipterous insect +earwig +common European earwig +phasmid +pollinator +bug +pupa +walking stick +scorpion fly +beetle +heteropterous insect +stonefly +hymenopterous insect +lepidopterous insect +chrysalis +odonate +silverfish +worker bee +grasshopper +cricket +katydid +locust +true bug +bedbug +dobson +green lacewing +lacewing +mantis +praying mantis +cockroach +American cockroach +German cockroach +oriental cockroach +plant louse +cicada +meadow spittlebug +seventeen-year locust +mealybug +leafhopper +aphid +mosquito +crane fly +midge +fruit fly +fly +horse tick +robber fly +Asian tiger mosquito +common mosquito +bee fly +horsefly +flesh fly +blowfly +housefly +greenbottle +bluebottle +Colorado potato beetle +firefly +ground beetle +sawyer +ladybug +lamellicorn beetle +rove beetle +Asian longhorned beetle +leaf beetle +elaterid beetle +click beetle +tiger beetle +weevil +long-horned beetle +Hippodamia convergens +vedalia +scarabaeid beetle +stag beetle +rose chafer +June beetle +Japanese beetle +rhinoceros beetle +dung beetle +scarab +cockchafer +water strider +wheel bug +wasp +ichneumon fly +ant +bee +cicada killer +digger wasp +vespid +hornet +paper wasp +common wasp +giant hornet +yellow jacket +carpenter ant +fire ant +wood ant +carpenter bee +honeybee +mason bee +andrena +leaf-cutting bee +bumblebee +Africanized bee +black bee +butterfly +moth +lycaenid +nymphalid +sulphur butterfly +ringlet +monarch +cabbage butterfly +blue +hairstreak +copper +tortoiseshell +fritillary +admiral +banded purple +peacock +red-spotted purple +painted beauty +mourning cloak +viceroy +red admiral +white admiral +comma +small white +large white +cinnabar +saturniid +noctuid moth +hawkmoth +tea tortrix +geometrid +tineid +atlas moth +emperor +polyphemus moth +cecropia +luna moth +carpet moth +clothes moth +dragonfly +damselfly +hen +filly +dam +herpes +protoctist +herpes simplex +herpes zoster +cytomegalovirus +herpes varicella zoster +alga +protozoan +seagrass +pond scum +green algae +plasmodium +ameba +ciliate +paramecium +sphagnum +hepatica +liverwort +peer +birth +adult +juvenile +countrywoman +businessperson +native +celebrant +native +Filipino +male +Gemini +onlooker +queen +referee +commoner +expert +newcomer +face +demonstrator +orphan +Black woman +contestant +bullfighter +lowerclassman +candidate +friend +life +anomaly +actor +thrower +creature +child +sheep +scuba diver +dancer +garbage man +entertainer +lover +unfortunate +anti +defender +sphinx +Indian +patient +Slav +White +brick +recipient +religious person +rescuer +Latin +money handler +rich person +domestic partner +creator +consumer +worker +groom +boy scout +inhabitant +African +fan +eager beaver +leader +schoolmate +man +philatelist +advocate +eccentric +bad person +transvestite +citizen +communicator +nonworker +parrot +intellectual +nonsmoker +student +chameleon +combatant +platinum blond +appointee +unpleasant person +politician +ruler +ancient +spectator +right-hander +traveler +scientist +picker +female +acquaintance +Black +relative +beard +redhead +sleeper +computer user +associate +participant +member +raiser +groom +bride +commissioner +director +tribesman +board member +important person +professional +oldster +celebrity +very important person +serjeant-at-law +educator +health professional +teacher +reading teacher +schoolmaster +nurse +medical practitioner +pharmacist +head nurse +probationer +doctor +surgeon +specialist +house physician +cardiologist +radiologist +schoolchild +child +bairn +orphan +entrepreneur +baron +agent +merchant +certified public accountant +syndic +insurance broker +fishmonger +vintner +peddler +seller +male child +mother's boy +son +man +cub +farm boy +bat boy +Herr +hunk +Peter Pan +patriarch +adonis +young buck +stud +guy +patriarch +sleuth +archer +authority +military attache +therapist +technician +black belt +high priest +critic +taster +panelist +physical therapist +osteopath +player +athlete +rival +billiard player +medalist +seeded player +chess master +pool player +football player +tennis player +ball hawk +vaulter +runner +skater +acrobat +climber +diver +alpinist +soccer player +winger +tennis pro +forward +sport +basketball player +miler +ballplayer +gymnast +back +lineman +halfback +quarterback +tailback +skateboarder +speedskater +circus acrobat +aerialist +fielder +designated hitter +base runner +minor leaguer +first baseman +outfielder +right fielder +infielder +semifinalist +foe +matador +picador +banderillero +buddy +mate +flatmate +pitcher +closer +right-handed pitcher +folk dancer +square dancer +morris dancer +compere +master of ceremonies +caricaturist +performer +fire-eater +executant +dancer +juggler +puppeteer +actor +clown +musician +dancing-master +ballet dancer +understudy +starlet +tenor saxophonist +percussionist +guitarist +keyboardist +trumpeter +sitar player +singer +oboist +cellist +violist +flutist +organist +rock star +drummer +songster +bass +fiance +darling +fancier +soul mate +sweetheart +kisser +amputee +homeless +casualty +guard +fireman +zoo keeper +lawman +military policeman +attorney general +policeman +bobby +Mountie +detective +motorcycle cop +trooper +traffic cop +Kiliwa +Biloxi +Chickasaw +Kickapoo +Arab +white man +Omani +Bedouin +Yemeni +protegee +heiress +swami +Buddhist +Muslim +novitiate +religious +Muslimah +Sufi +mother +monk +Sister +treasurer +ratepayer +state treasurer +bursar +cobbler +artist +choreographer +farmer +musician +stylist +sculptor +press photographer +songwriter +arranger +beekeeper +breeder +agriculturist +drinker +policyholder +drinker +concert-goer +drunkard +beer drinker +maid +employee +assistant +gondolier +skilled worker +skidder +boatman +waiter +bartender +staff member +salesperson +workman +settler +breadwinner +waitress +salesman +gardener +laborer +mill-hand +hired hand +coal miner +horse wrangler +goat herder +farmhand +attendant +cog +model +escort +caddie +companion +lifeguard +steward +color guard +honor guard +cover girl +artist's model +electrician +official +falconer +balloonist +craftsman +pilot +blacksmith +trawler +mender +baker +serviceman +painter +diplomat +judge +incumbent +appointee +presbyter +ambassador +high commissioner +plenipotentiary +glassblower +carpenter +coiffeur +machinist +wright +hairdresser +fighter pilot +copilot +artilleryman +Navy SEAL +military officer +enlisted person +noncommissioned officer +commanding officer +naval commander +adjutant general +commander in chief +commissioned officer +army officer +adjutant +inspector general +sergeant +first sergeant +staff sergeant +commissioned military officer +commissioned naval officer +line officer +major +lieutenant +first lieutenant +marshal +captain +general +lieutenant colonel +lieutenant commander +rear admiral +soldier +enlisted man +tanker +reservist +Unknown Soldier +private +recruit +yard bird +villager +Tahitian +American +Asian +American +Polynesian +European +New Zealander +North Carolinian +Minnesotan +Nebraskan +Floridian +Afghan +Tibetan +Mongol +Papuan +Indian +Jordanian +Japanese +Malay +Korean +Timorese +Bornean +Lao +Iraqi +Gujarati +Punjabi +West Indian +Latin American +North American +South American +Bahamian +Barbadian +Haitian +Central American +Canadian +Mexican +Nicaraguan +Mexican-American +Bolivian +Guyanese +Albanian +Byelorussian +Monegasque +Frank +Scandinavian +Laconian +Netherlander +Slovene +Sabine +Bulgarian +Romanian +Lithuanian +Englishwoman +Britisher +Yugoslav +Dubliner +Parisian +Eritrean +Tanzanian +Zulu +Black African +Cameroonian +Sudanese +Senegalese +Kenyan +Togolese +Ugandan +Liberian +Herero +Zimbabwean +Nigerian +Gambian +Tuareg +Guinean +Ethiopian +South African +mayor +politician +trainer +employer +Speaker +lawgiver +cheerleader +head +aristocrat +spiritual leader +instigator +mistress +boss +demagogue +Labourite +animal trainer +pitching coach +legislator +deputy +senator +administrator +department head +secretary +manageress +executive +hotelier +chief executive officer +Treasury +minister +Secretary of State +Secretary of the Interior +duchess +viscount +clergyman +lama +rabbi +Dalai Lama +officiant +priest +cleric +vicar +Father +bishop +diocesan +cardinal +metropolitan +federalist +supporter +ambassador +protectionist +loyalist +cheerleader +adulteress +wrongdoer +hypocrite +abettor +skinhead +biographer +disk jockey +speaker +representative +reporter +orator +interlocutor +organ-grinder +head of state +alderman +resident commissioner +President of the United States +president +television reporter +anchor +retiree +sunbather +camper +scholar +exponent +casuist +futurist +licentiate +reader +brawler +boxer +wrestler +flyweight +middleweight +sparring partner +prizefighter +light heavyweight +featherweight +lightweight +heavyweight +flyweight +sumo wrestler +bantamweight +egotist +fire-eater +upstart +bragger +exhibitionist +sovereign +Pharaoh +Cheops +sheik +rider +motorcyclist +musher +astronaut +pedestrian +mover +commuter +pilgrim +skin-diver +settler +tourist +runner +gringo +unicyclist +hang glider +jockey +horseman +saunterer +marcher +hitter +scrambler +psycholinguist +social scientist +lumper +sociologist +political scientist +economist +econometrician +microeconomist +female child +woman +mother's daughter +girl wonder +Boy Scout +Cub Scout +enchantress +lady +old woman +nymph +donna +bridesmaid +smasher +primigravida +signorina +girl +beldam +heroine +widow +call girl +baggage +wife +gal +baby +lass +maid +first lady +old lady +crown princess +father-in-law +cousin +kinswoman +ancestor +kinsman +second cousin +in-law +kin +twin +offspring +sibling +niece +aunt +great-niece +sister +great-aunt +little sister +big sister +parent +forefather +forebear +patriarch +mater +father +mother +dad +old man +great grandparent +grandparent +great grandmother +nan +grandma +grandfather +great-nephew +little brother +grandchild +firstborn +child +successor +granddaughter +great grandchild +great grandson +great granddaughter +baby +godson +premature baby +neonate +shiitake +common stinkhorn +earthball +truffle +hen-of-the-woods +gyromitra +mildew +lichen +white fungus +true slime mold +slime mold +club fungus +earthstar +coral fungus +false morel +puffball +pythium +helvella +giant puffball +Scleroderma citrinum +jelly fungus +agaric +stinkhorn +discomycete +basidiomycete +Phytophthora infestans +Jew's-ear +bolete +powdery mildew +downy mildew +reindeer moss +beard lichen +Iceland moss +lecanora +Sarcoscypha coccinea +Aleuria aurantia +gill fungus +polypore +agaric +mushroom +Polyporus squamosus +bracket fungus +Entoloma lividum +mushroom +inky cap +mushroom +oyster mushroom +deer mushroom +parasol mushroom +fairy-ring mushroom +royal agaric +blewits +honey mushroom +Pholiota squarrosa +lepiota +blushing mushroom +horse mushroom +nameko +winter mushroom +false deathcap +shaggymane +destroying angel +toadstool +chanterelle +meadow mushroom +death cap +fly agaric +morel +common morel +black morel +Boletus edulis +Boletus luridus +Boletus chrysenteron +somatic cell +histiocyte +leukocyte +lymphocyte +neutrophil +nest +tangle +radiator +plant part +rock +comet +cadaver +star +snowdrift +covering +aerie +wasp's nest +lip +tendril +plant organ +mycelium +reproductive structure +leaf +root +stalk +hypanthium +flower +fruit +pistil +rosebud +inflorescence +floret +umbel +flower cluster +panicle +olive +ear +buckthorn berry +berry +wild cherry +acorn +rowanberry +mealie +gourd +seed +hip +juniper berry +pod +corn +coffee bean +nut +buckeye +oilseed +bean +edible seed +edible nut +pine nut +macadamia nut +pistachio +hazelnut +walnut +cashew +chestnut +pecan +peanut +coconut +linseed +rapeseed +broad bean +soy +cumin +sunflower seed +pumpkin seed +legume +okra +chickpea +pea +cowpea +garden pea +lentil +dandelion green +frond +petal +cassava +chicory +tuber +spadix +branchlet +bulb +petiole +scape +cornstalk +rattan +Jerusalem artichoke +yam +squill +onion +belay +outcrop +tor +supernova +sun +shell +bracteole +shell +cassia bark +snowcap +perianth +body covering +roof +seashell +scallop shell +oyster shell +exoskeleton +cuticle +plastron +skin +hair +scapular +hairdo +forelock +encolure +facial hair +pigtail +thatch +pompadour +mustache +beard +mustachio +soup-strainer +stubble +soul patch +weather +dust storm +cloud +snow +wave diff --git a/data/MobileNetSSD_deploy.caffemodel b/data/MobileNetSSD_deploy.caffemodel deleted file mode 100644 index 7104f06cd..000000000 Binary files a/data/MobileNetSSD_deploy.caffemodel and /dev/null differ diff --git a/data/MobileNetSSD_deploy.prototxt b/data/MobileNetSSD_deploy.prototxt deleted file mode 100644 index fdc812628..000000000 --- a/data/MobileNetSSD_deploy.prototxt +++ /dev/null @@ -1,1912 +0,0 @@ -name: "MobileNet-SSD" -input: "data" -input_shape { - dim: 1 - dim: 3 - dim: 300 - dim: 300 -} -layer { - name: "conv0" - type: "Convolution" - bottom: "data" - top: "conv0" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 32 - pad: 1 - kernel_size: 3 - stride: 2 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv0/relu" - type: "ReLU" - bottom: "conv0" - top: "conv0" -} -layer { - name: "conv1/dw" - type: "Convolution" - bottom: "conv0" - top: "conv1/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 32 - pad: 1 - kernel_size: 3 - group: 32 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv1/dw/relu" - type: "ReLU" - bottom: "conv1/dw" - top: "conv1/dw" -} -layer { - name: "conv1" - type: "Convolution" - bottom: "conv1/dw" - top: "conv1" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 64 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv1/relu" - type: "ReLU" - bottom: "conv1" - top: "conv1" -} -layer { - name: "conv2/dw" - type: "Convolution" - bottom: "conv1" - top: "conv2/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 64 - pad: 1 - kernel_size: 3 - stride: 2 - group: 64 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv2/dw/relu" - type: "ReLU" - bottom: "conv2/dw" - top: "conv2/dw" -} -layer { - name: "conv2" - type: "Convolution" - bottom: "conv2/dw" - top: "conv2" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 128 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv2/relu" - type: "ReLU" - bottom: "conv2" - top: "conv2" -} -layer { - name: "conv3/dw" - type: "Convolution" - bottom: "conv2" - top: "conv3/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 128 - pad: 1 - kernel_size: 3 - group: 128 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv3/dw/relu" - type: "ReLU" - bottom: "conv3/dw" - top: "conv3/dw" -} -layer { - name: "conv3" - type: "Convolution" - bottom: "conv3/dw" - top: "conv3" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 128 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv3/relu" - type: "ReLU" - bottom: "conv3" - top: "conv3" -} -layer { - name: "conv4/dw" - type: "Convolution" - bottom: "conv3" - top: "conv4/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 128 - pad: 1 - kernel_size: 3 - stride: 2 - group: 128 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv4/dw/relu" - type: "ReLU" - bottom: "conv4/dw" - top: "conv4/dw" -} -layer { - name: "conv4" - type: "Convolution" - bottom: "conv4/dw" - top: "conv4" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 256 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv4/relu" - type: "ReLU" - bottom: "conv4" - top: "conv4" -} -layer { - name: "conv5/dw" - type: "Convolution" - bottom: "conv4" - top: "conv5/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 256 - pad: 1 - kernel_size: 3 - group: 256 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv5/dw/relu" - type: "ReLU" - bottom: "conv5/dw" - top: "conv5/dw" -} -layer { - name: "conv5" - type: "Convolution" - bottom: "conv5/dw" - top: "conv5" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 256 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv5/relu" - type: "ReLU" - bottom: "conv5" - top: "conv5" -} -layer { - name: "conv6/dw" - type: "Convolution" - bottom: "conv5" - top: "conv6/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 256 - pad: 1 - kernel_size: 3 - stride: 2 - group: 256 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv6/dw/relu" - type: "ReLU" - bottom: "conv6/dw" - top: "conv6/dw" -} -layer { - name: "conv6" - type: "Convolution" - bottom: "conv6/dw" - top: "conv6" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv6/relu" - type: "ReLU" - bottom: "conv6" - top: "conv6" -} -layer { - name: "conv7/dw" - type: "Convolution" - bottom: "conv6" - top: "conv7/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - pad: 1 - kernel_size: 3 - group: 512 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv7/dw/relu" - type: "ReLU" - bottom: "conv7/dw" - top: "conv7/dw" -} -layer { - name: "conv7" - type: "Convolution" - bottom: "conv7/dw" - top: "conv7" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv7/relu" - type: "ReLU" - bottom: "conv7" - top: "conv7" -} -layer { - name: "conv8/dw" - type: "Convolution" - bottom: "conv7" - top: "conv8/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - pad: 1 - kernel_size: 3 - group: 512 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv8/dw/relu" - type: "ReLU" - bottom: "conv8/dw" - top: "conv8/dw" -} -layer { - name: "conv8" - type: "Convolution" - bottom: "conv8/dw" - top: "conv8" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv8/relu" - type: "ReLU" - bottom: "conv8" - top: "conv8" -} -layer { - name: "conv9/dw" - type: "Convolution" - bottom: "conv8" - top: "conv9/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - pad: 1 - kernel_size: 3 - group: 512 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv9/dw/relu" - type: "ReLU" - bottom: "conv9/dw" - top: "conv9/dw" -} -layer { - name: "conv9" - type: "Convolution" - bottom: "conv9/dw" - top: "conv9" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv9/relu" - type: "ReLU" - bottom: "conv9" - top: "conv9" -} -layer { - name: "conv10/dw" - type: "Convolution" - bottom: "conv9" - top: "conv10/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - pad: 1 - kernel_size: 3 - group: 512 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv10/dw/relu" - type: "ReLU" - bottom: "conv10/dw" - top: "conv10/dw" -} -layer { - name: "conv10" - type: "Convolution" - bottom: "conv10/dw" - top: "conv10" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv10/relu" - type: "ReLU" - bottom: "conv10" - top: "conv10" -} -layer { - name: "conv11/dw" - type: "Convolution" - bottom: "conv10" - top: "conv11/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - pad: 1 - kernel_size: 3 - group: 512 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv11/dw/relu" - type: "ReLU" - bottom: "conv11/dw" - top: "conv11/dw" -} -layer { - name: "conv11" - type: "Convolution" - bottom: "conv11/dw" - top: "conv11" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv11/relu" - type: "ReLU" - bottom: "conv11" - top: "conv11" -} -layer { - name: "conv12/dw" - type: "Convolution" - bottom: "conv11" - top: "conv12/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - pad: 1 - kernel_size: 3 - stride: 2 - group: 512 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv12/dw/relu" - type: "ReLU" - bottom: "conv12/dw" - top: "conv12/dw" -} -layer { - name: "conv12" - type: "Convolution" - bottom: "conv12/dw" - top: "conv12" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 1024 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv12/relu" - type: "ReLU" - bottom: "conv12" - top: "conv12" -} -layer { - name: "conv13/dw" - type: "Convolution" - bottom: "conv12" - top: "conv13/dw" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 1024 - pad: 1 - kernel_size: 3 - group: 1024 - engine: CAFFE - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv13/dw/relu" - type: "ReLU" - bottom: "conv13/dw" - top: "conv13/dw" -} -layer { - name: "conv13" - type: "Convolution" - bottom: "conv13/dw" - top: "conv13" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 1024 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv13/relu" - type: "ReLU" - bottom: "conv13" - top: "conv13" -} -layer { - name: "conv14_1" - type: "Convolution" - bottom: "conv13" - top: "conv14_1" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 256 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv14_1/relu" - type: "ReLU" - bottom: "conv14_1" - top: "conv14_1" -} -layer { - name: "conv14_2" - type: "Convolution" - bottom: "conv14_1" - top: "conv14_2" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 512 - pad: 1 - kernel_size: 3 - stride: 2 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv14_2/relu" - type: "ReLU" - bottom: "conv14_2" - top: "conv14_2" -} -layer { - name: "conv15_1" - type: "Convolution" - bottom: "conv14_2" - top: "conv15_1" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 128 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv15_1/relu" - type: "ReLU" - bottom: "conv15_1" - top: "conv15_1" -} -layer { - name: "conv15_2" - type: "Convolution" - bottom: "conv15_1" - top: "conv15_2" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 256 - pad: 1 - kernel_size: 3 - stride: 2 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv15_2/relu" - type: "ReLU" - bottom: "conv15_2" - top: "conv15_2" -} -layer { - name: "conv16_1" - type: "Convolution" - bottom: "conv15_2" - top: "conv16_1" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 128 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv16_1/relu" - type: "ReLU" - bottom: "conv16_1" - top: "conv16_1" -} -layer { - name: "conv16_2" - type: "Convolution" - bottom: "conv16_1" - top: "conv16_2" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 256 - pad: 1 - kernel_size: 3 - stride: 2 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv16_2/relu" - type: "ReLU" - bottom: "conv16_2" - top: "conv16_2" -} -layer { - name: "conv17_1" - type: "Convolution" - bottom: "conv16_2" - top: "conv17_1" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 64 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv17_1/relu" - type: "ReLU" - bottom: "conv17_1" - top: "conv17_1" -} -layer { - name: "conv17_2" - type: "Convolution" - bottom: "conv17_1" - top: "conv17_2" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 128 - pad: 1 - kernel_size: 3 - stride: 2 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv17_2/relu" - type: "ReLU" - bottom: "conv17_2" - top: "conv17_2" -} -layer { - name: "conv11_mbox_loc" - type: "Convolution" - bottom: "conv11" - top: "conv11_mbox_loc" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 12 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv11_mbox_loc_perm" - type: "Permute" - bottom: "conv11_mbox_loc" - top: "conv11_mbox_loc_perm" - permute_param { - order: 0 - order: 2 - order: 3 - order: 1 - } -} -layer { - name: "conv11_mbox_loc_flat" - type: "Flatten" - bottom: "conv11_mbox_loc_perm" - top: "conv11_mbox_loc_flat" - flatten_param { - axis: 1 - } -} -layer { - name: "conv11_mbox_conf" - type: "Convolution" - bottom: "conv11" - top: "conv11_mbox_conf" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 63 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv11_mbox_conf_perm" - type: "Permute" - bottom: "conv11_mbox_conf" - top: "conv11_mbox_conf_perm" - permute_param { - order: 0 - order: 2 - order: 3 - order: 1 - } -} -layer { - name: "conv11_mbox_conf_flat" - type: "Flatten" - bottom: "conv11_mbox_conf_perm" - top: "conv11_mbox_conf_flat" - flatten_param { - axis: 1 - } -} -layer { - name: "conv11_mbox_priorbox" - type: "PriorBox" - bottom: "conv11" - bottom: "data" - top: "conv11_mbox_priorbox" - prior_box_param { - min_size: 60.0 - aspect_ratio: 2.0 - flip: true - clip: false - variance: 0.1 - variance: 0.1 - variance: 0.2 - variance: 0.2 - offset: 0.5 - } -} -layer { - name: "conv13_mbox_loc" - type: "Convolution" - bottom: "conv13" - top: "conv13_mbox_loc" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 24 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv13_mbox_loc_perm" - type: "Permute" - bottom: "conv13_mbox_loc" - top: "conv13_mbox_loc_perm" - permute_param { - order: 0 - order: 2 - order: 3 - order: 1 - } -} -layer { - name: "conv13_mbox_loc_flat" - type: "Flatten" - bottom: "conv13_mbox_loc_perm" - top: "conv13_mbox_loc_flat" - flatten_param { - axis: 1 - } -} -layer { - name: "conv13_mbox_conf" - type: "Convolution" - bottom: "conv13" - top: "conv13_mbox_conf" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 126 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv13_mbox_conf_perm" - type: "Permute" - bottom: "conv13_mbox_conf" - top: "conv13_mbox_conf_perm" - permute_param { - order: 0 - order: 2 - order: 3 - order: 1 - } -} -layer { - name: "conv13_mbox_conf_flat" - type: "Flatten" - bottom: "conv13_mbox_conf_perm" - top: "conv13_mbox_conf_flat" - flatten_param { - axis: 1 - } -} -layer { - name: "conv13_mbox_priorbox" - type: "PriorBox" - bottom: "conv13" - bottom: "data" - top: "conv13_mbox_priorbox" - prior_box_param { - min_size: 105.0 - max_size: 150.0 - aspect_ratio: 2.0 - aspect_ratio: 3.0 - flip: true - clip: false - variance: 0.1 - variance: 0.1 - variance: 0.2 - variance: 0.2 - offset: 0.5 - } -} -layer { - name: "conv14_2_mbox_loc" - type: "Convolution" - bottom: "conv14_2" - top: "conv14_2_mbox_loc" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 24 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv14_2_mbox_loc_perm" - type: "Permute" - bottom: "conv14_2_mbox_loc" - top: "conv14_2_mbox_loc_perm" - permute_param { - order: 0 - order: 2 - order: 3 - order: 1 - } -} -layer { - name: "conv14_2_mbox_loc_flat" - type: "Flatten" - bottom: "conv14_2_mbox_loc_perm" - top: "conv14_2_mbox_loc_flat" - flatten_param { - axis: 1 - } -} -layer { - name: "conv14_2_mbox_conf" - type: "Convolution" - bottom: "conv14_2" - top: "conv14_2_mbox_conf" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 126 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv14_2_mbox_conf_perm" - type: "Permute" - bottom: "conv14_2_mbox_conf" - top: "conv14_2_mbox_conf_perm" - permute_param { - order: 0 - order: 2 - order: 3 - order: 1 - } -} -layer { - name: "conv14_2_mbox_conf_flat" - type: "Flatten" - bottom: "conv14_2_mbox_conf_perm" - top: "conv14_2_mbox_conf_flat" - flatten_param { - axis: 1 - } -} -layer { - name: "conv14_2_mbox_priorbox" - type: "PriorBox" - bottom: "conv14_2" - bottom: "data" - top: "conv14_2_mbox_priorbox" - prior_box_param { - min_size: 150.0 - max_size: 195.0 - aspect_ratio: 2.0 - aspect_ratio: 3.0 - flip: true - clip: false - variance: 0.1 - variance: 0.1 - variance: 0.2 - variance: 0.2 - offset: 0.5 - } -} -layer { - name: "conv15_2_mbox_loc" - type: "Convolution" - bottom: "conv15_2" - top: "conv15_2_mbox_loc" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 24 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv15_2_mbox_loc_perm" - type: "Permute" - bottom: "conv15_2_mbox_loc" - top: "conv15_2_mbox_loc_perm" - permute_param { - order: 0 - order: 2 - order: 3 - order: 1 - } -} -layer { - name: "conv15_2_mbox_loc_flat" - type: "Flatten" - bottom: "conv15_2_mbox_loc_perm" - top: "conv15_2_mbox_loc_flat" - flatten_param { - axis: 1 - } -} -layer { - name: "conv15_2_mbox_conf" - type: "Convolution" - bottom: "conv15_2" - top: "conv15_2_mbox_conf" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 126 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv15_2_mbox_conf_perm" - type: "Permute" - bottom: "conv15_2_mbox_conf" - top: "conv15_2_mbox_conf_perm" - permute_param { - order: 0 - order: 2 - order: 3 - order: 1 - } -} -layer { - name: "conv15_2_mbox_conf_flat" - type: "Flatten" - bottom: "conv15_2_mbox_conf_perm" - top: "conv15_2_mbox_conf_flat" - flatten_param { - axis: 1 - } -} -layer { - name: "conv15_2_mbox_priorbox" - type: "PriorBox" - bottom: "conv15_2" - bottom: "data" - top: "conv15_2_mbox_priorbox" - prior_box_param { - min_size: 195.0 - max_size: 240.0 - aspect_ratio: 2.0 - aspect_ratio: 3.0 - flip: true - clip: false - variance: 0.1 - variance: 0.1 - variance: 0.2 - variance: 0.2 - offset: 0.5 - } -} -layer { - name: "conv16_2_mbox_loc" - type: "Convolution" - bottom: "conv16_2" - top: "conv16_2_mbox_loc" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 24 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv16_2_mbox_loc_perm" - type: "Permute" - bottom: "conv16_2_mbox_loc" - top: "conv16_2_mbox_loc_perm" - permute_param { - order: 0 - order: 2 - order: 3 - order: 1 - } -} -layer { - name: "conv16_2_mbox_loc_flat" - type: "Flatten" - bottom: "conv16_2_mbox_loc_perm" - top: "conv16_2_mbox_loc_flat" - flatten_param { - axis: 1 - } -} -layer { - name: "conv16_2_mbox_conf" - type: "Convolution" - bottom: "conv16_2" - top: "conv16_2_mbox_conf" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 126 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv16_2_mbox_conf_perm" - type: "Permute" - bottom: "conv16_2_mbox_conf" - top: "conv16_2_mbox_conf_perm" - permute_param { - order: 0 - order: 2 - order: 3 - order: 1 - } -} -layer { - name: "conv16_2_mbox_conf_flat" - type: "Flatten" - bottom: "conv16_2_mbox_conf_perm" - top: "conv16_2_mbox_conf_flat" - flatten_param { - axis: 1 - } -} -layer { - name: "conv16_2_mbox_priorbox" - type: "PriorBox" - bottom: "conv16_2" - bottom: "data" - top: "conv16_2_mbox_priorbox" - prior_box_param { - min_size: 240.0 - max_size: 285.0 - aspect_ratio: 2.0 - aspect_ratio: 3.0 - flip: true - clip: false - variance: 0.1 - variance: 0.1 - variance: 0.2 - variance: 0.2 - offset: 0.5 - } -} -layer { - name: "conv17_2_mbox_loc" - type: "Convolution" - bottom: "conv17_2" - top: "conv17_2_mbox_loc" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 24 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv17_2_mbox_loc_perm" - type: "Permute" - bottom: "conv17_2_mbox_loc" - top: "conv17_2_mbox_loc_perm" - permute_param { - order: 0 - order: 2 - order: 3 - order: 1 - } -} -layer { - name: "conv17_2_mbox_loc_flat" - type: "Flatten" - bottom: "conv17_2_mbox_loc_perm" - top: "conv17_2_mbox_loc_flat" - flatten_param { - axis: 1 - } -} -layer { - name: "conv17_2_mbox_conf" - type: "Convolution" - bottom: "conv17_2" - top: "conv17_2_mbox_conf" - param { - lr_mult: 1.0 - decay_mult: 1.0 - } - param { - lr_mult: 2.0 - decay_mult: 0.0 - } - convolution_param { - num_output: 126 - kernel_size: 1 - weight_filler { - type: "msra" - } - bias_filler { - type: "constant" - value: 0.0 - } - } -} -layer { - name: "conv17_2_mbox_conf_perm" - type: "Permute" - bottom: "conv17_2_mbox_conf" - top: "conv17_2_mbox_conf_perm" - permute_param { - order: 0 - order: 2 - order: 3 - order: 1 - } -} -layer { - name: "conv17_2_mbox_conf_flat" - type: "Flatten" - bottom: "conv17_2_mbox_conf_perm" - top: "conv17_2_mbox_conf_flat" - flatten_param { - axis: 1 - } -} -layer { - name: "conv17_2_mbox_priorbox" - type: "PriorBox" - bottom: "conv17_2" - bottom: "data" - top: "conv17_2_mbox_priorbox" - prior_box_param { - min_size: 285.0 - max_size: 300.0 - aspect_ratio: 2.0 - aspect_ratio: 3.0 - flip: true - clip: false - variance: 0.1 - variance: 0.1 - variance: 0.2 - variance: 0.2 - offset: 0.5 - } -} -layer { - name: "mbox_loc" - type: "Concat" - bottom: "conv11_mbox_loc_flat" - bottom: "conv13_mbox_loc_flat" - bottom: "conv14_2_mbox_loc_flat" - bottom: "conv15_2_mbox_loc_flat" - bottom: "conv16_2_mbox_loc_flat" - bottom: "conv17_2_mbox_loc_flat" - top: "mbox_loc" - concat_param { - axis: 1 - } -} -layer { - name: "mbox_conf" - type: "Concat" - bottom: "conv11_mbox_conf_flat" - bottom: "conv13_mbox_conf_flat" - bottom: "conv14_2_mbox_conf_flat" - bottom: "conv15_2_mbox_conf_flat" - bottom: "conv16_2_mbox_conf_flat" - bottom: "conv17_2_mbox_conf_flat" - top: "mbox_conf" - concat_param { - axis: 1 - } -} -layer { - name: "mbox_priorbox" - type: "Concat" - bottom: "conv11_mbox_priorbox" - bottom: "conv13_mbox_priorbox" - bottom: "conv14_2_mbox_priorbox" - bottom: "conv15_2_mbox_priorbox" - bottom: "conv16_2_mbox_priorbox" - bottom: "conv17_2_mbox_priorbox" - top: "mbox_priorbox" - concat_param { - axis: 2 - } -} -layer { - name: "mbox_conf_reshape" - type: "Reshape" - bottom: "mbox_conf" - top: "mbox_conf_reshape" - reshape_param { - shape { - dim: 0 - dim: -1 - dim: 21 - } - } -} -layer { - name: "mbox_conf_softmax" - type: "Softmax" - bottom: "mbox_conf_reshape" - top: "mbox_conf_softmax" - softmax_param { - axis: 2 - } -} -layer { - name: "mbox_conf_flatten" - type: "Flatten" - bottom: "mbox_conf_softmax" - top: "mbox_conf_flatten" - flatten_param { - axis: 1 - } -} -layer { - name: "detection_out" - type: "DetectionOutput" - bottom: "mbox_loc" - bottom: "mbox_conf_flatten" - bottom: "mbox_priorbox" - top: "detection_out" - include { - phase: TEST - } - detection_output_param { - num_classes: 21 - share_location: true - background_label_id: 0 - nms_param { - nms_threshold: 0.45 - top_k: 100 - } - code_type: CENTER_SIZE - keep_top_k: 100 - confidence_threshold: 0.25 - } -} diff --git a/data/build.sh b/data/build.sh new file mode 100755 index 000000000..acdc51b7a --- /dev/null +++ b/data/build.sh @@ -0,0 +1,6 @@ +cd .. +mkdir build +cd build +cmake . .. -DOpenCV_DIR=/home/nuzhny/work/libraries/opencv/rel -DUSE_OCV_BGFG=ON -DUSE_OCV_KCF=ON -DUSE_OCV_UKF=ON -DSILENT_WORK=OFF -DBUILD_EXAMPLES=ON -DBUILD_ASYNC_DETECTOR=ON -DBUILD_CARS_COUNTING=ON -DBUILD_YOLO_LIB=OFF -DBUILD_YOLO_TENSORRT=OFF +make -j4 + diff --git a/data/cmake_vs2017.bat b/data/cmake_vs2017.bat new file mode 100644 index 000000000..810a1c36d --- /dev/null +++ b/data/cmake_vs2017.bat @@ -0,0 +1,19 @@ +cd .. +md build +cd build +cmake.exe . .. -G "Visual Studio 15 2017 Win64" ^ + -DOpenCV_DIR=C:/work/libraries/opencv/opencv_64_cuda ^ + -DUSE_OCV_BGFG=ON ^ + -DUSE_OCV_KCF=ON ^ + -DUSE_OCV_UKF=ON ^ + -DSILENT_WORK=OFF ^ + -DBUILD_EXAMPLES=ON ^ + -DBUILD_ASYNC_DETECTOR=ON ^ + -DBUILD_CARS_COUNTING=ON ^ + -DBUILD_YOLO_LIB=ON ^ + -DCUDNN_INCLUDE_DIR=C:/cudnn-11.1-windows-x64-v8.0.5.39/cuda/include ^ + -DCUDNN_LIBRARY=C:/cudnn-11.1-windows-x64-v8.0.5.39/cuda/lib/x64/cudnn.lib ^ + -DBUILD_YOLO_TENSORRT=ON ^ + -DTensorRT_LIBRARY=C:/TensorRT-7.2.3.4/lib/*.lib ^ + -DTensorRT_INCLUDE_DIR=C:/TensorRT-7.2.3.4/include +cmake.exe --build . -j 6 --config Release diff --git a/data/cmake_vs2019.bat b/data/cmake_vs2019.bat new file mode 100644 index 000000000..54bf7829e --- /dev/null +++ b/data/cmake_vs2019.bat @@ -0,0 +1,19 @@ +cd .. +md build +cd build +cmake.exe . .. -G "Visual Studio 16 2019" -A "x64" ^ + -DOpenCV_DIR=C:/work/libraries/opencv/opencv_64_cuda ^ + -DUSE_OCV_BGFG=ON ^ + -DUSE_OCV_KCF=ON ^ + -DUSE_OCV_UKF=ON ^ + -DSILENT_WORK=OFF ^ + -DBUILD_EXAMPLES=ON ^ + -DBUILD_ASYNC_DETECTOR=ON ^ + -DBUILD_CARS_COUNTING=ON ^ + -DBUILD_YOLO_LIB=ON ^ + -DCUDNN_INCLUDE_DIR=C:/cudnn-10.0-windows10-x64-v7.6.5.32/cuda/include ^ + -DCUDNN_LIBRARY=C:/cudnn-10.0-windows10-x64-v7.6.5.32/cuda/lib/x64/cudnn.lib ^ + -DBUILD_YOLO_TENSORRT=ON ^ + -DTensorRT_LIBRARY=C:/TensorRT-5.1.5.0/lib/*.lib ^ + -DTensorRT_INCLUDE_DIR=C:/TensorRT-5.1.5.0/include +cmake.exe --build . -j 6 --config Release diff --git a/data/cmake_vs2022.bat b/data/cmake_vs2022.bat new file mode 100644 index 000000000..f554e4ba9 --- /dev/null +++ b/data/cmake_vs2022.bat @@ -0,0 +1,19 @@ +cd .. +md build +cd build +cmake.exe . .. -G "Visual Studio 17 2022" -A "x64" ^ + -DOpenCV_DIR=C:/work/libraries/opencv/opencv_64_2022 ^ + -DUSE_OCV_BGFG=ON ^ + -DUSE_OCV_KCF=ON ^ + -DUSE_OCV_UKF=ON ^ + -DSILENT_WORK=OFF ^ + -DBUILD_EXAMPLES=ON ^ + -DBUILD_ASYNC_DETECTOR=ON ^ + -DBUILD_CARS_COUNTING=ON ^ + -DBUILD_YOLO_LIB=ON ^ + -DCUDNN_INCLUDE_DIR=C:/cuda/cudnn-windows-x86_64-8.6.0.163_cuda11/include ^ + -DCUDNN_LIBRARY=C:/cuda/cudnn-windows-x86_64-8.6.0.163_cuda11/lib/x64/cudnn.lib ^ + -DBUILD_YOLO_TENSORRT=ON ^ + -DTensorRT_LIBRARY=C:/cuda/TensorRT-8.4.3.1/lib/*.lib ^ + -DTensorRT_INCLUDE_DIR=C:/cuda/TensorRT-8.4.3.1/include +cmake.exe --build . -j 6 --config Release diff --git a/data/coco/coco.names b/data/coco/coco.names new file mode 100644 index 000000000..ca76c80b5 --- /dev/null +++ b/data/coco/coco.names @@ -0,0 +1,80 @@ +person +bicycle +car +motorbike +aeroplane +bus +train +truck +boat +traffic light +fire hydrant +stop sign +parking meter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +backpack +umbrella +handbag +tie +suitcase +frisbee +skis +snowboard +sports ball +kite +baseball bat +baseball glove +skateboard +surfboard +tennis racket +bottle +wine glass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hot dog +pizza +donut +cake +chair +sofa +pottedplant +bed +diningtable +toilet +tvmonitor +laptop +mouse +remote +keyboard +cell phone +microwave +oven +toaster +sink +refrigerator +book +clock +vase +scissors +teddy bear +hair drier +toothbrush diff --git a/data/coco/coco_91.names b/data/coco/coco_91.names new file mode 100644 index 000000000..c5b8f040d --- /dev/null +++ b/data/coco/coco_91.names @@ -0,0 +1,91 @@ +person +bicycle +car +motorbike +aeroplane +bus +train +truck +boat +trafficlight +firehydrant +streetsign +stopsign +parkingmeter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +hat +backpack +umbrella +shoe +eyeglasses +handbag +tie +suitcase +frisbee +skis +snowboard +sportsball +kite +baseballbat +baseballglove +skateboard +surfboard +tennisracket +bottle +plate +wineglass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hotdog +pizza +donut +cake +chair +sofa +pottedplant +bed +mirror +diningtable +window +desk +toilet +door +tvmonitor +laptop +mouse +remote +keyboard +cellphone +microwave +oven +toaster +sink +refrigerator +blender +book +clock +vase +scissors +teddybear +hairdrier +toothbrush +hairbrush diff --git a/data/coco/full.names b/data/coco/full.names new file mode 100644 index 000000000..ca76c80b5 --- /dev/null +++ b/data/coco/full.names @@ -0,0 +1,80 @@ +person +bicycle +car +motorbike +aeroplane +bus +train +truck +boat +traffic light +fire hydrant +stop sign +parking meter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +backpack +umbrella +handbag +tie +suitcase +frisbee +skis +snowboard +sports ball +kite +baseball bat +baseball glove +skateboard +surfboard +tennis racket +bottle +wine glass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hot dog +pizza +donut +cake +chair +sofa +pottedplant +bed +diningtable +toilet +tvmonitor +laptop +mouse +remote +keyboard +cell phone +microwave +oven +toaster +sink +refrigerator +book +clock +vase +scissors +teddy bear +hair drier +toothbrush diff --git a/data/coco/white_full.names b/data/coco/white_full.names new file mode 100644 index 000000000..4e53804f6 --- /dev/null +++ b/data/coco/white_full.names @@ -0,0 +1,9 @@ +person +bicycle +car +motorbike +aeroplane +bus +train +truck +boat diff --git a/data/combined.txt.model b/data/combined.txt.model deleted file mode 100644 index d76ef96cd..000000000 --- a/data/combined.txt.model +++ /dev/null @@ -1,6151 +0,0 @@ -solver_type L2R_L2LOSS_SVC -nr_class 2 -label 1 -1 -nr_feature 6144 -bias 1 -w --0.005003520347911358 -0.002069803013184546 -0.005174648715386205 -0.005770423962997794 --0.01448714985262205 --0.0367621344258923 --0.02794459664664868 -0.01101089457859012 -0.008666665134766542 --0.02012648943416123 -0.06575279828180118 -0.02424015059444452 --0.02078629004738948 --0.0174857471188182 -0.01500355425557434 -0.00630631014205099 -0.01284952839634534 --0.01440907292094286 -0.03312943265799884 --0.01037956465816266 -0.01091253397466537 --0.008546722578458163 --0.000137242882255898 --0.006039288632080092 -0.02785929565990963 -0.01875779505813649 -0.001523177575111411 -0.001975857592573251 -0.0355570028413526 -0.00222207838522583 -0.01676492228306924 --0.014860750409855 --0.0007474025127886761 -0.001263892979786343 -0.01315113524375824 --0.004714853506517448 --0.02282390016881265 --0.005469724342330157 --0.02510834369198174 -0.01764526328517421 --0.007660696427159833 -0.004981475262397511 --0.01690123403309758 -0.006578004626564695 --0.01275568672542528 --0.002289109263499082 --0.0003020207915855601 --0.0247449283333634 --0.004337326818695084 --0.02127970823354044 --0.00205813933788758 --0.00697602903805972 -0.0378438461173811 -0.0271264404242818 --0.01749792865161751 --0.02820862901581239 --0.02182719361448771 -0.03875054149879911 -0.02987474445180364 -0.008319553173496204 -0.01863953592520786 --0.01377758079542864 -0.02447160859508605 --0.006161226087019822 -0.002664889266725591 --0.004195764438625247 -0.0258392781694716 -0.03597235631195608 --0.02312395751737616 --0.0264391761526756 -0.008879937882809405 -0.01317848370247718 -0.01442150630026301 -0.01147596267241498 -0.008140992211158772 -0.005028185738844742 -0.006646845288083352 --0.03718286608957103 --0.003187494927532327 -0.02737291596187517 --0.001307662724359647 -0.01299300062055115 --0.0077356816468095 --0.02314412706978508 -0.007754396219211935 -0.00237656846180913 --0.001021865536112009 --0.01633714512603848 -0.03010638215034521 -0.001959273739762304 --0.01388451168609694 --0.02902924355619753 --0.006209452098316521 --0.04394066992596347 -0.005438391961759498 -0.0004430470439009493 -0.01839911411320389 -0.01312083096711165 --0.003596098504759031 -0.008205299317585775 -0.01158208655470198 -0.01143715477397159 --0.01337154284471177 -0.01332920143227819 --0.01079045366246222 --0.002444117903920587 -0.0004225568703275601 -0.008454014893288557 -0.01382371036393016 --0.0008277937384174614 --0.003095762379452143 -0.005920876257174643 --0.018435862770501 --0.01325746795130324 -0.01572515069785909 --0.03636954142028007 -0.03327623206298854 --0.008945717976554125 --0.01512813884792744 -0.002350702483139244 -0.01876030251118957 -0.01426424964277364 --0.0005273014953322304 --0.02554912327528925 -0.02471023092682494 -0.0005748998346132857 --0.001425466379099617 -0.001345201563785435 -0.009799302154985012 --0.001669371383296941 --0.03301632929190409 -0.001847080476171332 -0.0001326086229401944 --0.0209294907115855 --0.006623246612253285 -0.004417154127878315 -0.01134309840262906 --0.02975562097965451 -0.0137113098352971 --0.01336515888658423 --0.03056310050626673 --0.01643979100209925 --0.02140371227986727 --0.003964933677113916 --0.01220623609634657 -0.001639161831019996 --0.02070818633068418 -0.01311884189460462 -0.005854753472316884 -0.01308041798150547 -0.002198357793834611 --0.0007248654118830881 -0.04799990767843172 -0.003443565906509785 --0.0172558476936482 -0.0325125542177515 -0.0360486890477587 -0.01615102901872734 -0.02015870522725214 -0.0005302504072158193 --0.01491375362800119 --0.03574601936142056 --0.03892317418311501 --0.009719590114107404 --0.01384706348878529 --0.02032916461597326 --0.007949499630951016 -0.03510776808035251 --0.0007874502885196378 -0.006597192831704409 --0.00103788408425645 --0.03949375140812197 -0.01678745005331527 -0.009661148644304823 -0.0008120819316022267 --0.01360737543568321 -0.002153041211443719 --0.03278667140273392 --0.03419092177714698 -0.009065814936435299 -0.01915344068421205 --0.0166592764738116 --0.04608819472223481 -0.006810744023721218 -0.006806002439833975 --0.02293703473477056 --0.05606649769217235 -0.005418833639128474 -0.009339001173000717 -0.003557382462747092 --0.0003072634595633458 -0.01103428562528969 --0.001007442607148272 --0.01986254205070798 --0.003417074690970731 --0.02089191748796974 -0.01755733343424117 --0.01421880445163473 --0.007706256573348884 --0.0316305685923691 --0.02001652813974759 -0.02608050258069597 --0.03436624696618167 -0.02159538092131812 -0.00708380668103448 --0.003111134841378749 --0.004926318633533562 --0.001885235455617492 --0.01859751732172839 --0.01414900801814637 --0.02612153641307975 --0.009910123218352357 --0.006365778234346128 --0.01733177591856793 --0.006387543032471968 -0.008249748548657456 -0.01882365990003728 -0.004518595720905678 -0.004560404907278093 -0.02327821450917032 --0.002912346258285851 -0.02390941125930645 --0.03844018508339368 -0.0007930765426637359 -0.008025583670277952 -0.03943723601097654 -0.01167127417601206 -0.01772513051309598 -0.03317502203206862 -0.000893028963210232 --0.002394382684041357 -0.005016197319593135 --0.005279746775419308 --0.01388213471146204 -0.02621994621239625 -0.01302632594667858 -0.01185686121428527 -0.009362310203846862 -9.299257314664079e-05 -0.0004835098562204914 --0.002713304851430683 --0.01226718338014476 --0.009877020734323479 --0.0002919310650716624 -0.005194238050333014 --0.001662500000528359 --0.01011829167243831 -0.00773409574225973 --0.01235530121162729 --0.001547045998920955 --0.03075499845710892 -0.005740627724918379 -0.006992768992037683 -0.01175696745869032 --0.0007052100534328659 -0.0004624023950462225 -0.009228664121560546 -0.01924883493209572 --0.008364770638044009 -0.01567359364829157 -0.02287365117026793 -0.01211287886899546 -0.02567785299236859 -0.01503671634458731 -0.00189163873778995 -0.01758876251815896 -0.03250564528844271 -0.006482028534461236 -0.02290977956631286 -0.01272808235187591 -0.01443941206680375 -0.0002526422229615169 -0.003539317372202528 -0.01253998372839131 -0.02069401285759962 --0.002050895412099214 -0.01399534715079634 -0.01687501104736621 -0.002147809845668891 --3.142267451271448e-06 --0.03082919024973758 -0.00409833618300046 -0.01764242996451706 --0.01824280153221269 --0.006756723434835348 --0.0001450205760352501 --0.00533948906003771 --0.0266457012220864 -0.001344069557904624 --0.02128193239408986 --0.003659783389711483 --0.001852477576654513 --0.005583923348945666 --0.01929853851723307 -0.005032591258722955 -0.01916912290010374 -0.0308524055529778 -0.01373768182407962 -0.04879855877438867 -0.002400002027748995 -0.01194276900965131 -0.02472784420662469 -0.0152947015615006 -0.008229897751963602 --0.008467217513181489 --0.02719336608141725 -0.002982316793370578 --0.005297610306972945 --0.01178221269438317 -0.00291670535831723 -0.01514055354402407 -0.000843004470314921 -0.03418930834618266 --0.007025330524047327 --0.006446516301463833 -0.02243084941706928 -0.01535965071131457 --0.01781964505478506 --0.005839037552966624 --0.004932980815182085 -0.003013755445519076 --0.006938248188829542 --0.0162717211528717 --0.01798497036975532 --0.004645095001116832 -0.01089958322771388 --0.02477933155243444 -0.009662665748671747 --0.006078020463963898 --0.01345160543476964 --0.01660954500628031 -0.002251851517249183 --0.01416684620464115 --0.02017698268253854 --0.008873431760135699 --0.02034543497200495 -0.0006466372085425197 --0.04878086408083279 --0.0027483181373843 -0.01151986776061373 --0.0162265344855075 --0.003256801104442416 --0.0005921090590034859 --0.03103687301830104 -0.02003929866102026 -0.002993458840512375 --0.01774967334631447 --0.01129472265352868 -0.003989231828537564 --0.01614046959996082 --0.007969116104857841 --0.02862444942950542 -0.009149897923654771 --0.003472215813725276 -0.00824400324360478 --0.00514985193198036 -0.0300742921766338 -0.002550702409033196 -0.02522315171355281 -0.002239783524537956 --0.004458744012660044 --0.002685748035980243 --0.02003084152623159 --0.01394261241327988 --0.007585878529702793 --0.009753075559757908 -0.01003755003767885 --0.03275255100672173 -0.04517121808063266 -0.002434769598134361 -0.008076037975557804 --0.01300263749545634 -0.06302110617897295 -0.01687153647848769 --0.00930962876055192 --0.02893428004246289 -0.02770691707775359 --0.005809321332112434 --0.01989749364275967 --0.02537833095641314 -0.014181271454644 -0.004880702816569228 --0.002332279482607529 -2.360044861530883e-05 --0.0009735975942982823 --0.0006798436192469192 -0.01153117588693445 -0.01366220762121318 --0.01213616902933886 --0.03932064342835775 -0.01955031541064437 -0.02112332835847193 --0.01045117123023636 -0.0092375852883199 -0.005412830881953227 --0.009652129185600017 --0.03190723978774343 --0.004397134614096571 -0.0004653366920910375 -0.001801215448612087 -0.03659409852610278 -0.02164238473311287 -0.02952981655803447 -0.02760507272583731 -0.004868429239963265 -0.008575218199526917 -0.007607863558952699 -0.001140099589671244 -0.0359315056212372 -0.005974921143204162 --0.02207612606506396 -0.02224194803133473 --9.614643673055539e-05 --0.01235900634964228 -0.004810958582936387 -0.0005553201699475764 --0.04959617981761505 --0.02175683843999135 --0.002460682721387544 --0.04291025402634877 --0.007117533720152886 -0.009538461446059016 --0.02533512467883599 -0.0108719727858132 -0.01030006497856292 -0.00534026568387216 --0.02907301857171546 -0.009029428125775235 -0.003677969233221066 --0.00835826756718748 --0.006584188295177362 --0.01725879084872028 -0.01481922594341135 --0.007829060961651034 --0.02239608867235906 --0.007239649108550513 --0.02079908011132775 --0.0122402558111806 -0.02223178833145775 --0.02642044855104548 -0.02460513396431681 -0.01025987325311695 --0.007924876597065754 --0.008469098669326826 -0.01162427789506362 -0.00757125579340331 --0.006651687432548528 --2.011760012418087e-05 -0.007571021043648778 -0.004030482263946447 -0.03764152288361701 -0.01113734846155956 -0.00762682292082857 --0.03114581685183132 --0.01292423174770759 --0.006479712360404123 -0.02762814909895847 -0.06441575785681913 --0.01090172949626452 --0.007697231186210299 --0.004743469016488679 --0.005243778891252429 --0.008732594053048595 --0.01768043245652324 -0.005280437836049898 -0.0006756851338277547 --0.002040749662085092 -0.01905870498060573 --0.003946572948967477 --0.0008642364785054555 -0.001916326215645003 --0.01981899711425225 -0.02575605458110392 -0.003919275294279793 --0.03415197974394633 --0.02038051887277834 --0.01947517087181787 -0.00232395253007428 --0.005005491814130655 --0.01574473896058118 --0.02108647113005644 --0.0390933560020202 --0.03381029152027456 --0.01374129627045977 --0.02156975197338225 --0.01742708450368665 --0.02264858345077536 --0.004319312911059957 -0.003887998324021627 -0.0232337907043668 --0.01624569974857062 --0.01125570695150681 --0.003702716727484901 --0.01400482921065592 -0.007011594349336642 --0.007573526736130579 --0.001032097377261878 --0.001047323103030321 --0.01016048814324655 --0.003540457030422892 -0.02083373788634737 --0.004967312684087704 --0.01222224714773651 --0.01253554718983541 -0.03627487978079577 --0.009200758609774632 --0.007995646261622871 --0.00661199873420455 -0.006537820686641866 --0.016958763151676 --0.007092649599914428 --0.007604167826467551 --0.001179703955100132 --0.004741446528877903 -0.01117942094133146 --0.01394367513870646 -0.006387018723445646 --0.01684775476809702 --0.008020723392915732 -0.02508636717351514 -0.01386299878517946 -0.0006163434569274912 -0.0226836256010943 --0.01192388276706972 --0.0145837783431355 --0.005101115192672873 -0.001440175842190362 --0.005931771432837823 -0.00751238104121529 --0.01367162748473635 -0.05651837815750417 -0.01617179191117598 --0.007314792298486002 --0.01149634251780092 -0.02047849803547178 -0.001023744937027721 -0.01471597875686846 -0.022454024687387 -0.0005124787304554669 -0.01797356687499928 -0.02827670547412039 -0.003611171801694697 -0.0153327716641086 --0.01084053129238595 -0.01120063482298114 -0.01146654149954871 --0.02186676349862713 --0.00878975941847282 -0.008550492104857975 --0.02310384955313644 --0.02413534865119355 --0.008913855602135069 --0.01039153630834005 -0.01066052334187154 --0.03595931444715042 --0.0008432535929275898 --0.008362226615794064 -0.012899442247164 -0.02930312588308863 --0.0009787165783535431 -0.00892214047220974 --0.02385351670709739 -0.002751156663230699 --0.05194250425000137 -0.005817084444285272 --0.006364928017669904 -0.006554827842347569 --0.008228184021934926 -0.02026067920090452 -0.0215193956363785 --0.0008296911817124732 -0.01498914366173797 --0.01072137031975259 --0.01324846683708499 -0.02690443982661931 -0.01217516804592704 -0.009245736318703481 --0.008722076304629822 -0.02223697697300151 -0.0006550776533903614 -0.008829673087587061 --0.009493019881536384 -0.02427893935681946 -0.01138262875558655 -0.008640065790915905 -0.008527334397447163 --0.0098517077633191 --0.001196774832557466 -0.01245135534212849 --0.01246543959827981 --0.01467837007481979 -0.005603671578263095 -0.01812599547963147 --0.003369978597163579 -0.02449992214460639 --0.01115553459047753 -0.005743490644701667 --0.01878079907846746 -0.014432070644365 -0.01323665835919854 -0.0231617086915668 -0.007303629654081573 --0.01427799433969487 --0.00831587623286807 -0.01432656833254217 --0.05565068721077037 --0.01649008086536423 -0.003193506865209827 -0.007043880530607463 -0.001381125822896695 --0.01284515838845966 -0.006167126542678558 --0.04102781952506392 --0.01144135058687889 --0.0171272919308507 --0.02751586690268498 --0.008662432431961922 --0.006198087795207047 --0.02528455231929987 --0.007763599861541351 --0.02216969816341033 --0.008917916822090642 -0.01167172747087434 --0.00395815915473401 --0.01948004640889512 -0.0256515703053247 --0.04185080715448322 --0.007234759438274927 -0.03046349003474088 -0.004402390410109493 -0.0188800543541134 -0.006061779987044675 -0.005257153766757948 -0.01501098576484898 --0.003452513228169648 --0.009840069829909743 -0.01654368552238866 -0.01980687921490583 -0.01317655728945977 -0.005416029144507892 -0.005951078966694971 --0.0132302571559345 -0.008730753821912659 --0.00626315087461618 --0.01274903461132639 --0.01032682640154472 -0.004530181275655699 -0.002186481569415141 -0.001848766116629715 -0.01948645099304751 --0.00984287213593949 --0.0180245006576852 --0.03178474172238394 -0.01668474916730084 --0.03386605864417302 --0.0209899028109063 --0.01516451159349272 --0.01415197055902736 -0.004454583359133935 --0.002347227621329079 -0.007593957870811625 -0.01094008076835564 -0.002658616813757957 --0.01021824349388779 --0.01473303058960936 -0.01503165577939028 -0.001971848067470669 -0.005478605934674059 -0.03879781096731207 --0.01418999381390136 --0.02919782949085926 -0.002659307296285537 --0.004162860232794933 --0.02452273251202593 --0.02897421148975042 --0.03982240308212088 --0.01756816048193873 --0.02231481586771881 --0.01283711115525081 -0.009687819609816677 -0.0003102851276981148 -0.004850545105721929 --0.02300374335417232 --0.03357373683186745 --0.007839363000441121 --0.01276449801594297 -0.001456108171957251 -0.01902255297265176 -0.004446782695133483 -0.02133695901712592 --0.01976270669646911 --0.009685266451037178 -0.01117016334713575 -0.001370637539317458 --0.04889195949797102 --0.01341348733981518 -0.008660924479100355 --0.006907570434681563 --0.05135507268549426 --0.006779827440184504 -0.0008447743350087972 -0.0003712496640469208 --0.0003096522608354965 -0.01243270782590681 -0.01482536527371885 -0.02348964808340296 -0.005496272695601416 --0.0114049635202043 -0.01840097419337173 -0.006809039144939852 --0.0007937697922170802 -0.01955452591090784 --0.01876173414526512 -0.02710441349839449 --0.01917579124605914 -0.008709505240794215 --0.03285763002067066 -0.008752924583107179 --0.009943121157616173 -0.0154154708695081 --0.004067415511192045 --0.01662204632073492 -0.007713758102871562 --0.02813539294527369 --0.007732075358615671 --0.009669999234922904 -0.003334904816078541 -0.006649279863757005 -0.01590848455206553 -0.01644953937864884 --0.00417288889175121 --0.002969069162019801 -0.002023646471877018 -0.007582525973530554 --0.007257317751561066 -0.008973606018068452 -0.01348266443738668 -0.02305226829085775 -0.01666651816093993 -0.005733506299952687 -0.02159362222256716 -0.02116430086849335 -0.02082676800221974 -0.01677199759208592 --0.003676742192525374 -0.0133594044288274 --0.006966099325457501 --0.01086624358270451 --0.01750917256243403 -0.003772616822794145 -0.01445849007631814 -0.01008133637260704 --0.005569721859292821 -0.008549287445647404 -0.02281150273017499 --0.001416827691289391 --0.0150205897591585 -0.009187222437584734 -0.01236589259930272 -0.001630923087993068 --0.008152626816441411 -0.00252836853997357 --0.03222698840858164 --0.004781627978895055 --0.01246961236144454 -0.007181335714098646 -0.002028017078066252 --0.000931230815235418 --0.004526607560690233 --0.004996772754726691 --0.01279344662708906 -0.009563394319059554 -0.02632692231095652 --0.01028169608130237 -0.01228557262409598 --0.006016241022531138 --0.005900086155128476 -0.004077276432807645 --0.003347002165918217 --0.007247045554594302 --0.01728603566366559 --0.007859542706558123 -0.008038538291788092 --0.002645537288856739 -5.631471902796162e-05 -0.02961785542620612 --0.007282038702990256 -0.009838163956636713 --0.0004541778055609444 --0.018851081439879 --0.007379994526259774 -0.00434105343449142 --0.0261505533204982 --0.01014342479063603 --0.013312296220665 -0.02535063133725272 --0.008604934926263814 -0.004249021610531419 -0.03842678837784637 -0.01633589451662827 -0.02331866322491402 --0.01513965575352876 --0.0006038281169199562 -0.01997283243793812 --0.03909843620979078 --0.03575608791174428 --0.007581068743560676 -0.0134128227318906 --0.01239968046154283 --0.01261845051053615 -0.03049350370135833 --0.007226058769735589 --0.03017111294837905 -0.003929833351086065 -0.02064870228684692 -0.01667975044362359 --0.005677985457621018 -0.01203943098849442 --0.006498714389489587 --0.008899659825761869 --0.03245627621117831 -0.002287396896372641 --0.01747357375295201 --0.005263202761156655 --0.002032089753542359 --0.02790047025537045 -0.01059833973557582 -0.01282794753260645 --0.004232534790009283 -0.0009357382639767375 --0.03576428937547799 --0.00083780002237321 -0.004978412793922927 -0.01258086485260979 --0.02632671321711023 -0.002238249644627302 -0.009422383756532184 --0.03012115733417143 -0.02005045674263074 --0.02794243219462856 -0.007944221635426341 -0.04687920120304011 -0.01769916917983936 -0.01151348530964719 -0.004522737294149561 --0.02845976288069472 -0.0294433539432547 --0.0005098037823555218 -0.01334523409664714 --0.02082987004203069 --0.01235735391652316 --0.03065008463184635 -0.01668406439250987 -0.001161124906661016 --0.006925260487972621 -0.004003793255223161 -0.006773856522758004 -0.006686522031750745 --0.002928694763745174 --0.03515073814306881 -0.02232431930467069 --0.01810688808631062 -0.01101243690552882 -0.006149247655117805 -0.006563621638512924 -0.002742267343763326 --0.002934133638926621 -0.006218681022348667 --0.01299984475141811 --0.009197691693698922 --0.01707199630179087 -0.0003562807362521091 -0.00942523639502338 -0.00534091608854026 --0.003072840871387063 -0.005975332047230792 -0.003410723032808418 --0.004858688991411602 -0.008732599815901198 --0.006263204051530742 -0.01976643804093755 --0.004093722954093836 --0.02379095106889214 --0.009397992598695693 -0.01512927032265278 --0.01912868562825778 -0.0022241066256846 --0.02146993368663007 --0.0160001202402291 -0.008397155968020901 --0.002147075557762623 -0.0286505559232231 --0.006493694282837291 --0.00555377503013729 --0.03541788382866402 --0.00971100561622322 -0.0106989109655744 -0.003088634382949682 -0.01342848260381314 -0.01738877692590295 --0.0004090858687376276 -0.01080847082271384 -0.02624079697899995 --0.01006288148859815 --0.001314596113568419 -0.002427969831360457 -0.01759485420134566 -0.00335404119796532 --0.0006235249319439905 -0.01576131784070835 -0.01024487503860761 --0.01323614327231774 -0.01347837951322855 --0.008469883535618087 -0.0234011066639342 -0.0245682063330038 --0.004141336085151608 -0.007272684160143496 --0.003025842895219159 -0.01215033041189319 -0.009843505777525033 -0.003392946616741205 --0.0008123250732940032 -0.005735360954720714 --0.02214883681806597 -0.01152493861337968 -0.005657061300064797 -0.0214609622218948 -0.02191934252453447 --0.01181257471694092 --0.02185621762028104 --0.005980574085987012 --0.001250567183323252 --0.01352735566891516 --0.01324867112556336 --0.007003203601505609 -0.04801858977417573 --0.007267288733386458 --0.01192539853984659 -0.01126640007483809 -0.003475505967396253 --0.02071941632647525 --0.0005726632876119915 --0.02124756696850617 -0.009165408753565928 --0.01596268692181218 --0.005410651845821386 -0.03260214469903287 -0.01180725287964493 --0.0172347020873347 --0.02303862219300323 -0.007084016282822183 -0.009034675608347871 -0.02071271363538896 --0.009337486351723779 --0.002787646393651261 --0.007540650869381602 -0.001861903136428323 -0.008547987580841467 --0.006074118708461347 -0.004796385069469019 -0.036937313265285 --0.007751895864964408 --0.01396263115572428 --0.0116446885137104 -0.004208290438050806 --0.003508682408418307 --0.009086530015347198 -0.007974846659870112 -0.01356496285280434 -0.003528156114944038 --0.01425837980743495 --0.02639577591607816 --0.01147037072516825 -0.008995922258968891 --0.005476601754817025 -0.00701839078881001 --0.01197198408600142 -0.03583687709242954 --0.004955060770351197 --0.003485903071434123 --0.005966957995723868 --0.00239923975529647 --0.02938000831829269 -0.01724271618774327 -0.007881455062929622 --0.002376120846033699 --0.02201814907702552 --0.01516904893084525 -0.001345944917531528 --0.01581797713778328 -0.02007238195262654 --0.02152801725351697 --0.003499986557644242 -0.002047534776443752 --0.01703357104186727 --0.001862878534423749 --0.0006485279936755399 -0.0157709023893298 --0.02129401144776363 --7.010851507036585e-05 -0.009858520788405455 -0.002171557588504047 -0.01372494912786795 --0.01131839846222653 --0.002573610063288637 -0.004621947287358354 -0.004480559250421569 -0.008404828524134737 --0.005233266333193224 -0.01015328308671275 --0.02031568488211503 -0.01613797689501945 -0.01087596118627642 -0.001261085050951664 --0.004333542983386869 --0.001655390807485342 --0.02829844289512774 --0.02780752210416536 --0.01149875156822533 -0.009339410166709276 --0.004295733687255748 --0.001730982684304364 --0.0005518559355724889 --0.0006390047475683922 --0.006176003354337999 -0.001652431106243968 -0.009975680609600053 --0.02797716032439142 --0.02796078978464962 -0.01293655799452167 -0.009462661686558181 -0.001720462174180446 -0.00538565413143505 --0.03982530334741089 --0.0230692181112452 --0.02775607670932757 --0.01867121876882981 -0.006124590369981068 --0.0115123357938056 --0.003292444294299765 --0.04226045495291274 --0.04357481183566375 -0.02271855345833626 --0.00368781224889539 --0.02605808767965867 --0.0207169378045665 --0.01040852873734821 -0.002794919733733894 --0.01272359864278693 --0.01715868726144977 --0.0226767421324506 --0.01394224301366201 --0.03070126894664099 --0.02572050885216095 --0.002008218893069433 --0.004650434812568835 --0.019088732402207 --0.01858335187924904 -0.01147255610535523 -0.02778753112518982 -0.01532477851527851 -0.02947139164742346 -0.0006836624135609246 -0.02169082365026173 -0.007002409027876449 --0.02969237553403356 --0.004677918740103713 --0.03079164869444623 -0.005921347620402013 -0.01535746133649107 -0.02607031986513803 -0.0009086930421562581 --0.01339850803104417 -0.001333154985832634 -4.523194894381093e-05 --0.003980997228864974 -0.007425556043199984 --0.01715065179755247 --0.009572315371755902 -0.01939270169416925 --0.006169037978444524 -0.002081107773190845 -0.02022986357913467 --0.0296829271989799 --0.01265391942337506 -0.002324521523848259 --0.007833253639127683 --0.01065668480385865 --0.007275690202289063 --0.03829107984712437 --0.001788424778669945 -0.01740012438947449 --0.02592312340793472 --0.005363133735930519 -0.008910702131600497 -0.0268562611766555 -0.004729785523850201 --0.02132410325556956 -0.003506265723436317 --0.004407086210031078 --0.002573578308815765 -0.01639184693073134 --0.02121863359582059 -0.03216023953632659 --0.01966522325521147 --0.008384905317312516 -0.007667680613204453 -0.006738477741570359 -0.02110768068472282 -0.01416261736187769 --0.002783537962769521 -0.01350074401990306 --0.01238523711923566 --0.00836844333622231 --0.0001439869586081977 --0.01188376881311585 -0.01436928598772737 -0.003701623706305913 --0.0001570848046949758 --0.01819086682984613 -0.003876201604736075 --0.02628373458248906 -0.01532336312094816 --0.002162087531885517 --0.003681890038165341 --0.02158096626403967 --0.008089374395960726 -0.01871300583129416 -0.00171283457770458 -0.01472417724262834 --0.006057375363335498 -0.01507825626174323 --0.0285148420666857 --0.002616051751570292 -0.03035710440489543 -0.01991031087699378 -0.006601068137507364 -0.006701670317906971 -0.006709548563959679 --0.05549094661077508 -0.00846851981125136 --0.001310453723899519 -0.0093500523387716 -0.04403808006011854 -0.03026800906903658 -0.00354281819681558 -0.02073404252501665 -0.01229763550669773 -0.003145973733199772 --0.006398797982830115 -0.002429985726766671 -0.006928799740781565 -0.02301236623245129 --0.01999113303747349 -0.02447745467679782 -0.02432190174233914 -0.0006711037648610372 -0.0002625404952274126 -0.01644475727207527 -0.01381828021931978 --0.006573518789668793 --0.001156057956530029 --0.02613865519501167 -0.02361864292094831 -0.001538780202158474 --0.02099547098618208 -0.01180313448441145 -0.02124335357395203 -0.01069889427636538 --0.03134015728741897 -0.01124294269619394 -0.00526642481142349 -0.0009161121776108466 --0.01239099961886825 -0.0324917634048297 -0.004414170836474048 --0.02711929184169255 --0.0025413945878939 --0.009420688242076257 --0.01465679300880252 -0.002994704691665026 -0.01303448781022158 --0.008555980553149619 -0.03758171739578691 -0.01369238095434317 -0.03102939622740859 --0.02139151757909076 -0.01996929817050929 -0.006100769717538641 --0.01907228158195173 --0.004594001391666422 --0.01526195415261814 -6.404736927731995e-05 -0.007539458885203562 --0.003758019633489102 --0.0129643274399308 --0.0317117822959932 -0.01430740619264509 -0.01694960457866395 --0.006149883023033967 --0.006285779399859284 -0.01300576890431393 --0.001299501562367687 --0.00831666041744163 --0.03836364702808483 --0.0002810624539674921 -0.03089618347328809 -0.00912228057212104 -0.01045996785905204 -0.002976913329276479 --0.003029722063112611 -0.01883520752600152 -0.0001731651016554949 -0.00180922617372372 --0.007927668771411461 --0.01684815360985548 --0.009873785166053379 --0.03507024190894514 --0.01450769214639179 --0.001469580626923542 --0.004086785500224501 -0.009139807157084722 --0.003662090009687014 -0.01857811816775874 --0.05140866349543106 --0.00891822066339383 -0.009491031535568597 --0.005602467195330627 --0.0006649467196109227 --8.829420871987943e-05 -0.001953480971514434 -0.01842889028580623 -0.01055343116676067 -0.03310209584083586 -0.01316171414399559 -0.0006077435305859221 -0.0074151944193758 --0.01070742607748996 -0.04039232784923243 -0.04351448887082744 -0.01554534296542149 --0.002623518226829833 --0.01291027010589196 -0.02102299872240167 -0.003941404585195916 -0.01065377304111713 --0.002679951433724593 --0.002197120987992306 -0.0004573578802347325 --0.04940684894304934 --0.01257021805803177 --0.004517346375347697 -0.02969722084853378 -0.01062016921093883 --0.01679256910348336 -0.002506829076528198 --0.02793814340952067 -0.008155525009253166 --0.002999720645911231 --0.004507767137964646 --0.008437836693765133 -0.01350066833075329 -0.0009649577049395295 -0.004327225998526005 --0.03894084336514281 -0.03011161810407604 --0.005866895842685601 --0.02962246632526599 --0.01681067036895511 --0.005837722828876494 -0.001806177792797483 -0.02239029701557491 -0.01567381931622651 -0.0161521231265102 -0.005420737670518611 -0.01430669777088642 -0.0001729845813669936 --0.006186082840723674 --0.001544224393595961 --0.01365928360859867 -0.01762679295692892 --0.01484575650890804 -0.02162990788304155 --0.001737349184228652 --0.008102272154194312 -0.01417889801209776 --0.005233640200117716 --0.009979657204175272 -0.001468139079119028 -0.006999075277513353 --0.01070522731509243 -0.01776039103677174 --0.002902028748232633 -0.002690456055858746 --0.005389317738992409 --0.001106849239182241 --0.002280029606724315 --0.0041617960039521 --0.00271124534171149 --0.002274521205382005 --0.001199122806967336 --0.003381413462688697 --0.002069650024562991 --0.02045259891373353 --0.004691797159059354 --0.01208489563332363 -0.02952037745740787 -0.01863784978971038 --0.0008226739496181532 -0.0281790813136313 -0.01162368607078986 -0.01057071758529727 --0.01141344276211927 -0.02485476244422052 -0.04142366328399727 --0.01840940780746401 -0.007121708177212917 -0.01465907309643502 -0.001962801902275464 -0.01929200351396545 --0.01321126031683692 -0.03201451917980419 --0.004374109292591829 -0.01376705380192939 --0.01392858697730624 -0.02131172669155383 -0.01361921131946055 --0.0007643569560068617 --0.008327947797862457 -0.02493391528440564 --0.009019696476093448 --0.01193179010597067 --0.009205421559163807 --0.008873285819021167 -0.004123744671031855 -0.0151181856841698 --0.0257053897731305 -0.006408013558245591 -0.006055883490354082 -0.003394584847893433 -0.01048456725519591 --0.002901671959328282 -0.0177621789019933 --0.005446654006457716 -0.008041762327690672 --0.02755412078947034 -0.009040012365746202 -0.02055796391038681 --0.01432827622475474 --0.006200933929039688 --0.02595742122388938 -0.01195999481460946 -0.02078173343079194 --0.02717770899474817 -0.0121787165001857 --0.009760118634534383 --0.01693100754318937 --0.0191939037441852 -0.002165468622998385 -0.007951599055087752 -0.01197262017914202 -0.02903205101763904 -0.00510062691965784 -0.03955743110441621 --0.03802025548196284 -0.02341398939831249 --0.005006010168649757 --0.002596679966943346 -0.02580468541147135 --0.005439449416049159 -0.00242446870376706 --0.003027646069901763 --0.003832517352031034 --0.02071579284597203 -0.02475879872796235 --0.0001523846707009587 --0.01717768808928039 -0.0006527949369039565 -0.002764497900412905 -0.04319860048639278 --0.0003247530481430921 --0.004274968872881717 --0.0161417394934138 -0.01056885762068592 --0.002245443626152647 -0.02357311089687288 -0.01071281725118027 --0.01525025907003227 -0.02029888360897047 -0.001712451028408189 -0.001235863575940385 -0.009623287200969353 --0.01190860524543881 --0.0002081385004862613 --0.004649755010509842 -0.02583741107595951 --0.006368630882671673 --0.01131622678774626 --0.01795852580958378 --0.008555671738609756 --0.02929152273554363 --0.000709073223111646 -0.01646982830366987 -0.009003043399942207 --0.01250697082509796 --0.0143529999347808 -0.005058496428771095 -0.009903129116738418 --0.01387846206508754 -0.01033258842816794 --0.02241265100679323 --0.01049867657282457 --0.03472017120212344 -0.01140018640965411 -0.006353605583671204 -0.007044850867990847 --0.03334084130889706 --0.01815536714042336 --0.006597813653152828 --0.002944114564730585 --0.003151301080311825 --0.0175489836338453 --0.00292277431506027 --0.01269710773226217 --0.01817665904715475 -0.02891782468591024 -0.02363387193656099 --0.002334970686911417 -0.005075179967321053 --0.004293165263022737 -0.01338752888530809 --0.008189891283890605 --0.02888004328256247 -0.005976171943321576 --0.03054265593550055 -0.00409360046619447 -0.00700114704571883 -0.02040441627155631 --0.007255636770380255 -0.02471835355939004 --0.005770396316131142 --0.009481817421261014 --0.02537280598799997 --0.01616607340333887 --0.01166795471462989 --0.01030717362160463 --0.01020563128249139 -0.01204355715910457 -0.00107665412671168 --0.01105781610676676 --0.009829295582062216 --0.02579946510737528 --0.008813465255136351 -0.02744419233645272 -0.003154862143748513 -0.0306946417970386 --0.01923205519884618 --0.003731101453174634 -0.007690085875117345 --0.01173853945392214 --0.01176456165208266 -0.007089938013491295 --0.006279176213257642 -0.01609908110490792 --0.007092850346320017 -0.01161369460883531 --0.009005255962950274 --0.009507957296795625 -0.001804834127237599 --0.01478675733682424 --0.006397884495899582 --0.005405912722089408 -0.002908319350599251 --0.01114769937290839 --0.01938655970709622 -0.009852432980088754 --0.005054752670656166 -0.007016266710337288 --0.006703192086792324 -0.01871598645536511 -0.01492743141270434 --0.03745023957899398 --0.008485888380715899 --0.004038292982514992 --0.01198558971160972 --0.02375426326151019 --0.002903958307600663 -0.01133342954193736 --0.01683629094173543 --0.01223375044813632 --0.01739028583023601 -0.01037516025504453 --0.003107623465307698 -0.0002396046402810485 --0.02212865045010241 --0.004616187445806634 --0.02056273883767412 --0.01937964917474239 --0.01201589248589414 -0.008737530601627855 --0.01635095025773224 --0.003659369417039584 --0.01000491752906364 --0.01853179667369896 -0.01416680610967523 --0.00482065920008995 -0.004412067732489665 -0.005343602111942241 --0.004125881152733555 --0.01227852810373454 --0.01698938299633002 -0.0101925541242239 -0.007192541702351497 -0.003587128149857245 -0.004254068054333355 -0.02453396486988631 -0.003291990695359868 --0.01150989698542116 --0.007292218301004141 -0.006416084845809224 -0.004905692259397617 -0.005119232514710382 -0.003315245338359686 -0.007894083650786334 --0.003514580909909075 -0.008650577108185733 -0.005749784513878927 --0.004622953060973246 -0.01551305274611372 --0.00561225874185945 -0.01675692317561542 --0.00150730086269186 -0.0005249396477207156 --0.009371017454593199 -0.006201585857213717 --0.008948643361457943 --0.009399518899456512 -0.01320490256867866 -0.01140117538422683 --0.0217766939943769 -0.005361646334480745 -0.02220180489809371 -0.008501566039285405 -0.006953726085302888 -0.008704864669685175 --0.006441751769055778 --0.01381542177323393 --0.005944034331242305 --0.01218475348220682 -0.003922472310812214 --0.00602366271385793 --0.00213509669400386 --0.001033230842284614 -0.04120285166456205 --0.002188274964100489 --0.03098711780986696 --0.01049611767253735 -0.01124084396989755 --0.01111288004194584 --0.01014125674386878 -0.01778457458737278 -0.003281076382235856 -0.005805308666886533 --0.01742328898907234 -0.0009151763155163628 -0.01070431167493432 --0.01131637486733626 --0.006217205649200906 -0.000762881670301675 -0.01416269392415296 --0.009981211427369267 --0.01444880317085256 --0.01272026328173392 --0.02540926451129358 --0.0110940278000756 -0.01028752658432173 -0.01645240640409192 -0.02051555290889537 -0.00587454949398061 --0.003231574489585367 --0.03068125216141897 -0.01120657639672886 -0.008330686250254356 --0.009493475668337539 --0.01368142099305075 --0.01400800180051238 -0.01697814095090428 --0.01229647990724075 -0.01934460456855885 -0.001551965902217507 --0.009596304714087308 --0.01050409980890137 --0.01452079698399888 -0.01115842872397633 -0.01563242221630854 -0.007327640732617182 -0.00579267812636958 -0.001650448967289342 -0.009306936907413816 -0.007439166366189124 --0.01018925317033319 -0.00969163873057791 --0.01175712955749901 --0.007964891666828764 --0.006324480316764834 --0.002676458230663862 --0.01830728065071105 -0.0004147968191985238 --0.004296348619290511 --0.008092367990933611 --0.022057375682272 --0.0124513396451638 -0.0176467554648664 -0.01998212856241889 --0.01206368036008044 -0.001550206286200484 -0.007952215907272707 -0.003401219608152595 --0.01441786192752461 -0.02077645225973389 --0.03084454894573975 -0.0363784128960089 -0.02018201823968818 -0.01364249708937373 -0.00617436671021727 --0.002228029271523364 --0.03883719842159266 -0.01478723499798924 --0.01409578532458002 -0.009263019766992578 --0.02527446887922895 -0.001067351001919242 --0.003147110161307728 -0.0218906940497717 --0.03186871323025285 -0.00206437100102532 --0.0101308930174031 -0.0253970890795401 --0.02081592442687558 --0.0004463766730193994 --0.01028576381632534 -0.002770651697642995 --0.01424911978077413 --0.008075192534093672 --0.00623227572630522 --0.01474215367962464 --0.005506982026165508 --0.01674136864098932 -0.01020920282104112 --0.008875559230809248 --0.007370471144570866 -0.01253139647828153 -0.002695980001513695 --0.0006757054769491802 -0.004964574977104456 --0.01408464144738169 -4.952085915429198e-05 --0.009006168754575231 --0.002701926070958608 --0.03652006717782153 -0.00352810499820388 --0.03176354969543605 --0.006602237593244335 --0.008497205511120157 --0.02910878561275678 --0.005132216800594898 --0.01483773259105742 -0.001634658535703456 -0.00443287022948464 -0.01489563581253084 --0.003697830224897859 --0.01795687552894865 -0.01100850139522968 -0.01926404402098334 -0.01338736178114266 --0.001893031564882353 --0.003607267989229011 --0.009045911040115959 -0.0001338731318121252 --0.0005282693316574292 -0.001023639970315132 -0.01392749750217165 -0.01033420203439172 -0.005417066174624127 -0.0007537700818801907 -0.004432917114122274 --0.005334675763975693 --0.01565126773071325 --0.02731810662705819 -0.00291330405708042 -0.02421584151004475 --0.005249598455701276 --0.01226079962354958 -0.008546356057942133 --0.009246286439145173 --0.004109753261901383 --0.0024803664445579 --0.01844028177342592 --0.02206502169415311 --0.01501350500373293 --0.008289712471360374 -0.02888004605814559 --0.02365459934338274 -0.02534272022897188 -0.02061740623520734 -0.007184995361966554 --0.01013127271287851 --0.02192806487418398 -0.01498574666009387 --0.01514422347708445 -0.002152694875570596 --0.0005536661968098869 --0.02192714860170471 -0.01126830570741071 --0.01171952232304339 -0.009814866225505788 --0.001308025889672113 -0.02976895882458623 --0.02468174944609434 --0.0163768052140718 -0.00219581912761048 -0.02975168356536123 -0.02554194968222021 --0.004868350948090618 -0.004045195907803895 -0.01043056049576934 --0.000926534232991738 -0.004354649707789 -0.001226545839203135 -0.01560319963806674 -0.001905144611664917 --0.006326733869281486 -0.006569985477555002 -0.01157829013057778 -0.01957635534935027 --0.003321375280374849 -0.008416917690371759 --0.01003682501591048 --0.02776346586252576 --0.01152164576963457 --0.009744685466522959 -0.002071920485550573 --0.002979984701550322 --0.005479651753659253 --0.01364616209480277 -0.005988516978321402 --0.01538178215270365 -0.01758859567402726 --0.007290021252996312 --0.0106408859464586 -0.006720670206756741 -0.01431381187586868 -0.008090427295510915 -0.002720632529275261 --0.008710135586119407 -0.01636155692790863 -0.02049819960779948 -0.006206926831703229 --0.01508181677025995 -0.02031963757079138 --0.00648705750455322 --0.001608184223053293 -0.003556902509380737 --0.0082359740514686 -0.00212032554698236 -0.01652830061843327 --0.0002580693519802035 --0.0004127808258744676 -0.01613807090585076 --0.0246365408723604 -0.002117096890481068 --0.01063398364787518 -0.01759033003990421 --0.01240796838544492 -0.008783998410565892 --0.005568712386780652 -0.02764591914784978 --0.01108040913587555 -0.006797108011556103 --0.001327266061640904 -0.01205256256503283 -0.0139698404902154 -0.01516507736363942 --0.006898339858677156 -0.0212124928519463 --0.000148445693949533 --0.001895614077862456 -0.007632918467954866 --0.0004807396485253313 -0.0103066822307925 -0.003916236093863992 --0.009276712758796796 -0.00720428133283992 -0.0003994497827654003 --0.01667932575420029 -0.005148294190774466 --0.01406916688560416 --0.007982165164102308 --0.004712129143940626 --0.001515507636347674 -0.004540869651158856 -0.02274961027329835 -0.002268591438389791 --0.01079677399552864 --0.005959473329797133 -0.01319665351608812 --0.003658540995209761 --0.005158410699229659 --0.0008904921892527685 -0.01120458585360172 -0.009013221956260373 -0.002634157991232431 -0.0127132795061336 -0.004015897215154004 --0.001097823140122553 -0.009089997065789109 --0.005479713173394797 --0.01370333603244495 --0.006419213778338505 --0.006295421397127702 -0.00385608027579798 --0.01424023232732749 -0.0003384557981018936 --0.01006837056229665 --0.008717334337668801 -0.002936647617698697 -0.001239956879051464 --0.01746058354760543 --0.01871604598745298 --0.007320731364577541 -0.007779248230564683 --0.002467991322152353 --0.008672840961702172 -0.009631914420758723 -0.004896726634768131 --0.01576727848157885 --0.003573193571067725 --0.04192338953196287 --0.01527484967141659 -0.002072438515832884 -0.003424345102880843 -0.001994470215648774 -0.01466509385798341 --0.004415147584698019 --0.003145019022950545 -0.002095842840257558 -0.0133516797018277 -0.001493321829783797 -0.003069021280439011 -0.01714674764595616 -0.01353612282972051 --0.005082987813406201 --0.02672833333266446 --0.008488830239328666 -0.005640163781592418 -0.004109339175898203 -0.002481593591264732 -0.007660783872947382 --0.02313767904605801 --0.008787522946113163 -0.001583216750771078 --0.02147026299250385 --0.007796454695051316 --0.02037903170225247 --0.005047036581321928 --0.006620068624280741 --0.01242103455226167 -0.008387904339089664 -0.03371612071087385 -0.01176073241466659 -0.003636957329555025 --0.01972449564460617 --0.00322155193966083 --0.009339150369214354 -0.0005341079914695783 -0.005236183094495943 -0.005967084342315498 --0.002666981256748724 -0.01532543628646216 -0.010622684972155 --0.02175488913460132 -0.0009980325881792627 -0.005448783860387378 --0.01742789152095397 -0.04272284150790095 -0.005218702343931118 -0.01031743025332115 -0.0004207338245167571 -0.005953948859266752 -0.01116728542750235 --0.007658999606593614 --0.03799733527470842 --0.004217275268056678 --0.02327466359328111 --0.003540099153485855 --0.01355939863373944 --0.006081097456144878 --0.008182434877587901 -0.00288968943246931 --0.02879825862531147 -0.007406723912783224 --0.01397462016687308 --0.0198093785865078 -0.01383852211211272 -0.005208812763819525 -0.008619897198737169 -0.03086765013553723 --0.0006232975408835002 --0.009190095984739119 --0.001703885701654368 -0.007822859004768382 --0.01414732369915412 --0.007004530865133945 -0.01297750573911034 -0.002383433752758294 -0.01882511659263764 -0.02113437827974255 -0.001837737216603922 -0.03802063019626707 --0.004471401036174494 --0.02922211819090566 -0.001729673334381762 --0.006929139894733195 --0.004668969225044269 -0.0001286217071147185 --0.0002413885637529605 --0.003331467266312645 -0.004202900577206381 --0.02981849785327401 -0.008493475006931095 --0.01231716500203459 -0.0196896631964582 -0.002970411328770317 -0.01795934445082835 -0.01214209041458672 --0.01276243229978402 --0.0159461186524732 -0.007208098642611125 --0.03674408387931711 --0.003145622692913188 --0.01275860825691216 -0.008936599217483573 --0.0180704963035322 --0.02086921240022233 -0.002973712992810211 -0.01467132235696355 --0.02380029879126567 -0.00513482249627457 --0.02399715324972109 -0.01534672681064307 --0.003128532518688702 --0.007377468881844495 --0.0127545989370851 -0.005256913081541892 --0.004244761040882263 --0.004747073092273296 -0.01203554954684451 -0.006623621211960685 --0.04140957275759271 --0.0145725606288553 --0.01635225794479575 -0.01460143669157566 -0.009470800817233819 --0.02248572013527337 --0.02397058337937009 --0.0008486383658285704 -0.001024265886541806 -0.002143125438135531 --0.01062443874511401 --0.0009706296318111102 -0.004753473842249189 --0.0008420742830849998 --0.02022177760170625 --0.01931781893121642 --0.006529523714771198 --0.001884601106828844 --0.01943815748134014 -0.02886135044550207 --0.003305818953922596 --0.00272159641495474 --0.008870605242995076 -0.01362004369209151 -0.02043286039385004 --0.002175003186002545 --0.006954305622970268 --0.0005679007296741869 -0.00139035663462976 -0.005714924887406452 -0.001938363463719039 --0.0002415994783482895 -0.007000962088272354 -0.01123427563609114 -0.00287038214416711 --0.01798911025299659 -0.00370779670711086 --0.01084514546214282 --0.004012904287551436 -0.02873892138717055 -0.007137954627948636 --0.003694132171820698 --0.03835879195254621 --0.005902904884355925 -0.01205224112261373 --0.00974347336546042 -0.01001140888025003 -0.01432189225272032 --0.0139857508016259 -0.002438549314738905 -0.02809062366245842 -0.01235049867233432 -0.009663211759146596 -0.001410053869368667 -0.009973731580857582 -0.008742875282310604 -0.02041620113860615 --0.01064458245250706 -0.001514612074283982 -0.001365081156937389 -0.005933235598170941 --0.002691460823918697 -0.0370619414005915 --0.003854611648674897 --0.01553803661692626 -0.0007097145667704935 -0.004063903429233576 -0.02584296083319736 --0.004871916226947137 -0.01469468647295661 --0.0009054671156323789 -0.005135410684124127 --0.0113408257339239 --0.003796779329546391 -0.02440607481538242 -0.03401237765906474 -0.0103505746824679 -0.004260912786952374 -0.00577738114309503 --0.008000564086254861 --0.008689354599246638 --0.001704719191427385 -0.002522142952408068 -0.01252347922889033 -0.01037006316462945 --0.008133283249006716 -0.01849096318793482 -0.01279757683894496 --0.0002704084718143122 --0.01120057811504341 --0.01787424597016895 -0.01869016827400498 -0.01234369184454925 --0.002879643894441999 --0.01258183881033513 --0.002365820803913704 -0.002509934654712851 -0.001436991636734602 --0.02568024372509321 -0.00589528438123128 -0.01545851285764664 --0.00523896531027182 --0.03234347961174518 -0.007674725815789449 --0.009964328240874839 -0.007004386712421436 --0.01286763108334246 -0.0077721110681705 --0.004028054843890901 --0.002454777369002381 -0.009104579021518823 --0.01082336420502796 --0.0002165925288060717 --0.01844996898769538 --0.02792388284432292 --0.002975364873161067 --0.01037771469820443 -0.03359172335176542 --0.001311061554399674 -0.001743989928088901 --0.006306447835338422 --0.007961936756730558 -0.002065816620806474 -0.002647816307532489 --0.00276987187710808 --0.03278090589524882 --0.01490168637781322 --0.001515279675662869 --0.003049714925619508 --0.009284162714922474 -0.002878156169090059 -0.02084557097978784 -0.0003488664199960495 -0.0001635344928407963 --0.002844925069185727 -0.002069375412611047 --0.002465447579877238 --0.0002159478585181012 --0.007040160067226106 -0.01506935382181274 --0.02219040644910598 --0.004310026809657723 --0.001781670803243691 -0.003122239159630988 --0.005315849392103794 --0.006532599706156394 --0.001715932076123513 --0.002989108350223651 -0.02248270114473491 --0.006556181485085518 -0.004763862999797742 -0.02302189563103465 --0.02257955159853654 -0.004689299277101635 -0.0131821245905344 -0.003571745364035062 -0.01670166832866063 --0.0130637249930093 --0.008195505799059753 --0.007353624524849719 --0.01435806091140487 --0.005137334649216573 -0.02349783639382982 --0.006275925886844006 -0.01680073955923369 -0.008266812278978172 -0.004092963157955242 --0.01694838256425278 -0.03141164399251254 -0.01142088548669545 --0.01487351305868063 -0.001363795308014595 -0.005752476200893491 --0.01690086877198604 -0.003812042980651998 --0.01804164739435991 --0.02732359173193372 --0.0009347281646605042 -0.003713435883542896 -0.005064994812885675 --0.002106702516469806 --0.003349116128602967 -0.009566176231663646 --0.03354338041739061 --0.02090600631328638 -0.02128738093029466 --0.02786290924500091 -0.02347670925869287 -0.0176447655789603 -0.0005314377759297441 -0.003637824055762669 -0.00144811417567656 -0.02332289574525618 --0.001327568899650631 -0.0185338738268935 -0.01684697013622281 -0.00179317680439744 --0.01136914971660081 --0.008941787952923025 --0.01458483182229048 --0.0004492068077432991 -0.001495671322347571 --0.02059100197001574 -0.001083798881128133 --0.006659824070091525 -0.02488264676319637 --0.02021072513467227 --0.01989333499382044 --0.004279845029171672 --0.006086477171847801 -0.03113427268186063 -0.001172737935519379 -0.01874969540991632 -0.009014176341452485 -0.02444500859854239 --0.008700978998431089 --0.0115791402119921 --0.0108153142388073 --0.01912867813771941 -0.01018198323463959 --0.03393925330587132 --0.003924252520350437 --0.01034969958728167 -0.0009883829025064195 --0.01326250910790931 --0.005621689071267985 --0.009609510787098128 -0.007688016945296672 -0.004227157306997512 --0.02859549725762468 -0.01489192943066743 -0.02355293658754843 --0.02291419801680763 -0.01822321108532695 --0.01312570986555983 -0.01604906574506344 --0.0004595975531300679 -0.01899561490727598 --0.01462427248272488 -0.001494705441768974 -0.001905964683011385 --0.009799758105250618 -0.005547047278518652 -0.01382905931200318 -0.02092439496440041 -0.01936652207303139 -0.01550535030417885 -0.02674497747620155 -0.00576166763560173 --0.0115034015636466 --0.02112856402008893 --0.01482567728319138 -0.017082803519069 --0.01142404230521733 -0.004433560589238124 --0.004890778947616842 --0.03614331575040152 --0.001341824267321704 -0.001255218186316121 -0.01671012672273671 -0.01692900460602901 --0.02414695624971315 --0.005743197880363125 --0.0002696316502599029 --0.00450614971519081 -0.01831264206976355 --0.02556687993608042 --0.01770022394029044 -0.001507431986734305 --0.0004980745879944637 -0.01960154327174893 --0.004138551468099692 -0.001042239161567194 -4.909094144944038e-05 -0.006556915547313093 -0.01921241259466239 -0.01932421529795036 -0.001468164299560825 -0.01202916679468099 -0.01704819829638292 --0.0005507839172033823 -0.00381351811638083 -0.01118379326287371 -0.0315133145196759 -0.006386302086683933 -0.01407903384944601 --0.01081601079837569 --0.02129290823586634 --0.03664599377734008 --0.02667161218242034 --0.0007517421980233682 --0.02463666520626446 --0.006943864820063279 -0.007209044052348279 -0.0137080993207799 -0.01607735220295615 -0.01113602924837804 --0.006511092572930015 -0.003684141681970768 -0.001743380799568595 --0.007586067955615654 --0.009986408993960114 --0.01669451928853517 --0.02106918531968227 --0.008536992795825487 --0.004945685919596834 --0.01298872537500636 --0.02125024060177954 --0.006973566227285793 --0.01316817465346896 -0.003647933498417234 --0.01837289002544488 -0.001039968298474646 -0.02537865064065581 --0.01151045353743365 --0.008962012292139632 -0.02208625915567814 -0.02077239022858686 --0.01247060708682805 --0.007375083252304015 -0.01019843303946165 --0.009851862438267881 -0.000642808463346124 -0.0144460312764683 -0.005277248176850801 -0.001361616457871382 -0.01316088269705898 --0.00381459564002949 -0.01049048807091046 --0.003074273827120378 --0.01430489427954377 -0.001467040414201362 --0.005113496867326737 -0.0162397340962202 -0.02002709024923622 -0.01218471109199535 -0.01178019565942995 --0.01113540820875802 --0.003424481377394616 --0.0003671240081975226 -0.0004908632740117324 -0.002877624349510244 --0.00110181654274505 -0.003298956723275216 --0.0005889029162330185 --0.005032345698888414 -0.00191515320573489 --0.003198517958139457 --0.005703876267251325 -0.004371478694364967 --0.001911275604830361 --0.004036154421795037 -0.00535327169693817 --0.003423116494465768 --0.003803607899312536 -0.001550362546360488 -0.001621908303313092 --0.01070987768074877 -0.01142652274928564 -0.00988786219077363 --0.004655446689595956 --0.004913188418852635 --0.0120095913903339 --0.01335663139272611 --0.006026432003878769 -0.01131679237011094 --0.000444383493994854 --0.006418259543750929 --0.000392158564922129 --0.0172140138063681 -0.02020775528350636 -0.01470010924276834 -0.02792409533037354 -0.01528434207411887 -0.001330581468934865 --0.003442839349525545 -0.002911491826165634 -0.01689270901883589 --0.02864694838057828 -0.005697941117327089 -0.01725514154576722 --0.01994990488241509 --0.00692013997374557 -0.00944546517676562 --0.01652806061813834 -0.001746650279986182 -0.01190569412286772 -0.0006285132602118713 --0.004245878672251296 -0.003667277194884902 -0.01761684798198465 --0.004465864511546299 --0.00669851043596429 --0.02088558533532674 --0.005031008291812541 --0.020970298515525 -0.004675627562426433 --0.004281105090615566 --0.02024099186944913 --0.0209487674779752 --0.0005513734193942432 -0.02015265789923825 -0.01676482523689774 --0.01275346754330884 -0.02048546144781847 -0.009429682315738862 --0.008250894424783745 -0.02804283978831124 --0.02148751097779556 -0.006426382591364275 --0.005522955270290042 --0.0183250066012254 -0.01607746427600082 -0.01151173227955941 --0.02331610971392275 -0.02250211941681595 --0.01336649583204941 -0.0183500590129814 -0.02698680465312318 -0.00382174566798805 --0.003662191859213944 -0.0002390458208330781 --0.01652459740186936 --0.009364726479549161 --0.004274920298827001 -0.02534583897073507 -0.003265016781284869 -0.02497132380111935 -0.01612273873124122 -0.01095409046093517 --0.007928166644929851 --0.007249353817561233 -0.01260615720364627 --0.004932984372119232 -0.006796271592265942 --0.01100581095961108 --0.03936977704377553 --0.03020498529667122 --0.001125999161456072 --0.01506575858722887 --0.006704391195025735 --0.005842307861477143 -0.02658941391225035 -0.01624030078807339 -0.0002577229843212264 --0.01141418194098192 --0.009459311024563842 -0.0228155209868675 --0.01682692921879555 --0.004669168446513064 -0.002147956248980857 --0.009373288602506503 --0.0050570706205701 --0.01512786462362374 -0.002538098226317524 -0.02562269625152127 --0.006477718818795826 -0.03428520743774296 -0.003682234075161793 -0.004235193288878322 -0.007700114136755443 --0.01644923922407888 --0.01423582528584173 --0.0005338099463999129 -0.00301266958065962 -0.02277146963689098 --0.01397974234436716 --0.01431385347125994 -0.006079978950344602 -0.006532303026167034 -0.01361059425985696 --0.01296307116666921 -0.004676795868620871 -0.007564666580555421 --0.0146067307154655 -0.01352904425861247 --0.01632135002848036 --0.01149095548675444 --0.02965070117058712 --0.006913962784835835 -0.007919949437343057 -0.0002065301072555029 --0.01079563904850628 -0.01050195489500416 --0.01610434409957887 --0.01310679571637003 --0.02108069010253484 -0.007440627853076287 --0.001895800912861425 -0.003244746412517607 --0.00527074935348076 -0.02070222376186465 -0.03157770167961717 -0.01257504255850474 --0.03833464348626855 --0.01179891071944893 -0.00834370985776867 --0.0102066009217699 --0.01836784677466994 --0.007333155314888711 --0.01033989419356877 --0.01415856161195885 -0.04020795633287545 -0.01173107530763114 -0.01257040458696674 --0.01474852413533504 --0.01857237816250542 --0.01238911148251291 --0.01976794537252115 --0.02818178317146671 --0.004388787103571509 --0.01452629266449142 --0.03236570441782424 --0.0003941767401386946 -0.001719612835356063 --0.005894317606336506 --0.003810321954825299 -0.0005270276250813289 -0.007071992595765255 -0.01268028427941534 -0.01647945268154318 --0.01234377636327738 --0.007301472587575512 -0.0134270428797852 -0.01099398280772294 -0.01337798898201532 -0.02261132292152309 --0.01981982720803956 -0.00911311948697417 --0.00488981582279104 --0.02459900028421479 --0.01162682738004301 -0.01905702570511612 -0.00132393551515848 --0.01852679635813066 --0.01313961727286282 -0.001058958596005187 -0.01654553861510839 --0.0105108483148453 --0.003746959666241295 --0.0009723873728074475 -0.01762208224252926 -0.02584669267620573 -0.01335083561993331 --0.01082044217718478 -0.006492036234966164 -0.0006907878440447861 --0.009804570661036108 -0.01120854162861286 --0.001606561870553593 -0.02216596997103981 --0.009884699179391988 --0.01528922406773776 -0.008842022145278718 -0.01692303892803204 -0.02759571569424609 -0.0101765816686874 --0.01265316152996029 --0.0171890437074447 -0.009033905980518489 --0.004304614271996819 --0.00826650627476927 -0.006071479791476846 --0.01711988614818794 --0.03087169602841707 -0.01960013349083772 -0.02114130624773448 --0.003698143477789018 -0.01665571582999152 -0.006419193632957428 --0.01276808744431999 --0.01603943710194419 -0.01643459129875542 -0.003922889186219592 --0.01584155772301158 -0.02311905166087386 -0.0109385645740239 --0.005376403351910834 -0.009109733875422256 --0.0157785477878026 --0.007099614080983856 -0.01439959140438157 --0.002298661294363577 -0.02349746992508149 -0.003629503400692517 --0.01003649050670035 --0.01468978140989673 --0.009341402997386252 -0.01539382858813153 -0.007056184629012798 --0.02424052449330511 --0.01013438349156317 --0.0161106189463998 --0.008968145175644135 --0.008580799396574867 -0.007217677857083818 --0.001505020796593376 --0.009252048759125216 -0.0006322572879615543 --0.01362873965177152 --0.0006068447899512543 --0.02218425759213502 --0.03344566508308651 --0.01639386391818827 -0.001962087768299741 --0.03526650794905884 --0.02716860274747279 --7.252873791058854e-06 --0.03158285291418419 --0.001623340225867352 --0.01104157106194296 --0.005447367061230906 --0.02012298785820303 --0.01959336549695371 -0.01271556607725358 -0.007037157897222413 --0.002965922500334253 -0.00590758125794512 --0.01364028263341433 --0.02010108074600562 -0.009709222349498857 --0.01423346720587442 -0.009726284718385703 --0.01668415456711171 -0.006154823850582735 --0.02534602587405186 -0.01703970992267478 --0.0002098898215236721 -0.01301996193443654 -0.006127027443035371 -0.01244127461428899 --0.01432956237763387 -0.01395119683912871 --0.01568464085482351 -0.0001176104337549285 --0.02421691145033501 -0.000133025446500447 --0.01005712408048885 --0.01507405279576225 --0.001758876654180785 -0.007298171501762781 --0.003581791363545802 --0.02358688361607457 --0.002590266760278887 --0.01509102745872397 -0.03346706732342922 --0.01504549584126259 --0.001771791367793212 -0.002671613222433593 --0.0003709529221979855 -0.008728933944250549 -0.01391004449228192 -0.0075713781798582 -0.001815263478631578 --0.019966802603665 --0.01691576503225619 --0.01226879362884551 -0.02739827640152937 -0.008247124735032448 -0.02076070437183108 -0.0005848872978915303 --0.009779930206619349 -0.005675720692849909 --0.01951816439149086 --0.01056015423804004 --0.02534574544743919 --0.01925142490658781 -0.009922212784173618 --0.007176253680480551 -0.005451578879695868 -0.009918531347128976 --0.0006720105208877023 -0.0105588960696659 -0.02066111321455487 -0.008288703841042079 -0.005288264043898389 --0.01387705399840257 --0.0009930899310949576 --0.01056924902037246 --0.01238941323100972 -0.001870119050933335 -0.02686588041397876 --0.002065211177161746 --0.01278292221842857 --0.005142943319099935 -0.02377251988108977 --0.01159082718201512 --0.03379098646124078 -0.008318353679237245 -0.0114529071095676 --0.01645100703285619 --0.003181099628004045 --0.006916245828306857 --0.01278138467840095 -0.02146752707008293 -0.007600035285103075 -0.0526377486572026 --0.008154331199861043 --0.003390502996875069 -0.002062008044862195 --0.01517865902805408 --0.01389728504977737 -0.01137834139620722 -0.02036474884454893 -0.01386567122466357 -0.01310006832275501 --0.0004114574493389831 -0.01493565630573209 -0.00266181772025316 --0.003002376039593435 --0.02235784650481129 -0.01080210963443454 -0.02893646779230268 -0.02079502145671575 -0.01696665427415874 --0.00821198743310983 -0.03252951745743831 --0.0002119602316261224 --0.009151221812743944 -0.04070257868877229 -0.02967613461586078 --0.0009392779784078375 -0.008278877278034502 --0.003997352635048516 -0.003299601677460417 -0.002812284173243165 --0.01667055712361534 -0.01004118163730036 -0.003220893810245169 --0.02054611297250312 -0.005300592867177189 --0.0276434851611295 -0.01770331833063773 --0.01266179054227942 --0.02204107898268882 --0.002784037837270497 -0.004173796282149081 --0.008632584948110981 --0.001095515482764154 -0.02128074900090392 --0.001254958849304849 --0.004419810348378892 --0.005555478186945212 -0.01846776530131816 -0.001540673626872334 -0.002921914811082011 -0.005206986742777751 --0.002498625282395165 -0.01568001231641996 --0.02064791536508081 --0.009384359278438937 --0.01846263293103973 --0.004279961640969861 -0.0310155959502296 -0.02278584020863179 -0.004456847712152289 --0.0248668651197845 -0.006045076994333196 --0.01990997783962895 --0.01395455659290044 --0.006382026172753417 --0.003684809762670747 --0.01466019534038032 -0.01176884283707358 -0.01028352761420503 --0.01709943596191365 --0.004433992865450464 -0.007714383803794786 --0.03251271274410604 --0.02218674442464503 --0.0199451163926457 -0.01156248548342475 -0.003535659786422155 -0.00833831371179951 -0.0007474594260389695 -0.02810276146438476 -0.005727505750275822 --0.02458364128489382 --0.01062632241752211 -0.01141852620411286 -0.02165817996009011 --0.002370292424771842 --0.00243632094707594 -0.01249269639012427 -0.02516895808140605 --0.003309003623402556 -0.001559284947320723 --0.00194578392965551 --0.009251044517080588 --0.009362909904812894 --0.02417237177206746 --0.001254690935436669 --0.02288845194202567 --0.005734752371341872 --0.01427563410971964 --0.006363852293372386 --0.001562287672018273 --0.003412395608508961 -0.01296654411079786 --0.02383050116092834 -0.01944708601240925 -0.004642619424617453 --0.002601398101052739 --0.008971464113034624 -0.03676503580484968 -0.01423068213421022 --0.01773655798797057 -0.03871908578672768 -0.03964360236200849 -0.004463109524221587 --0.0002361359174218638 -0.005107668418078351 -0.003323590770881154 --0.005589388807401902 --0.02238946932039637 --0.003899800171947515 -0.01524521454188645 -0.01218266976100981 --0.01792749546122066 --0.01503548223806147 -0.002024560014768462 -0.01831192662299935 --0.005966062397026387 --0.009829197509904162 --0.004129005126445169 --0.0005858952377448667 -0.02452327675567211 -0.01329272263209953 --0.005371391037680929 -0.01818144228686881 -0.005330406867795841 -0.001644018928360227 --0.01326050890070832 -0.004065850555653653 --0.02741948362604604 -0.008848844008885852 -0.009685013981666823 -0.02299485400376819 --0.02062581336312209 -0.003570478582776307 -0.005109054188545755 -0.01525225932418012 -0.003300509886728114 --0.01039101723639359 -0.001478894165331377 --0.01944028958477118 --0.01697254376310092 --0.002141629122872058 -0.003301696199506949 --0.01434591013438162 --0.005107999497058222 -0.02256102406857514 -0.005657765061814224 -0.008780011300034423 --0.02241746509261875 --0.002272780186270637 --0.008657542563293784 -0.0021575973694357 -0.01336349975417421 --0.005981873037479388 -0.003790736331877263 -0.004140872679474091 --6.670914454933108e-05 --0.007315457382532402 -0.01215815364411283 -0.0008535743517559006 -0.0103060191291738 --0.003171904507291982 --0.001799830570586872 --0.01009750820301736 --0.003234315392605725 --0.01667326546333653 -0.001985844093464942 -0.0008755570258095779 --0.03324142561190395 --0.00513346728883581 -0.01349198768168489 --0.001884629120528639 --0.01067455799128833 --0.009052577826730036 --0.00963686447285496 --0.008426317920287769 -0.00222868039115213 -0.01425161561675471 -0.001152318369255607 -0.001019207509525064 --0.01823198609418282 --0.006527777887195718 --0.00230102788369858 -0.007513348468336135 --0.003576762697547948 --0.0006925910562061161 --0.006151036374173463 --0.02967808628872888 --0.01495870810410679 --0.02829215203691276 --0.01307077424018101 -0.004145194466820171 -0.01301554679763758 -0.01204314662106529 --0.007896881182351928 --0.002936518892835794 --0.01584137234858014 -0.03248689768717838 --0.01298404267246572 -0.0136470362798298 --0.003320805140709715 -0.01048840042848568 -0.007979589599270533 --0.00992977835896376 --0.005357187132347961 --0.01968606290952043 --0.01365581478873764 --0.01653047617420842 -0.008186062101038196 -0.001422792317902872 -0.003430948564170666 --0.006445923005789379 -0.02519639187680115 --0.00269855712628907 -0.01446977037462762 -0.002906802859013142 -0.01169415983924786 --0.01991106777674354 -0.02295310523935646 --0.005966728869365576 --0.009527416101862083 -0.02032987404184939 --0.02727924303001762 -0.03562935648936395 -0.01580005948870444 -0.02457018169518309 -0.01736100079211771 --0.0001498850191639035 -0.002718131521107321 -0.0002293909632381129 -0.01533726591844334 --0.0002921174741558058 --0.008789265837034863 --0.01214631799276147 --0.005771828433829178 -0.04187522792145641 --0.01012376786305625 -0.01861931501416082 --0.01438323107707888 --0.01342155661166992 --0.03357526386382455 --0.006826237914237056 --0.0006356986310774942 -0.007961888727519739 --0.01119856504631344 --0.01322733321158712 --0.004877102552970699 -0.00346087396311695 -0.03108907365246578 -0.01786536291866745 -0.009064996929934961 --0.01782528745427776 -0.02346156672659736 -0.01800870908899703 --0.0103503940354268 --0.001120251800766291 --0.0114589689458703 -0.04093179967132143 --0.002716911827795133 -0.0006807472810140623 --0.02841189917233079 --0.006436509718522184 -0.009020525383371787 -0.0112531976885015 --0.002530892831712752 --0.01497003470457219 --0.008304741511837474 -0.008732684668980653 -0.002339282367122902 -0.01081516002532559 -0.006101295963644212 --0.0006083981545125659 -0.0111925919478086 --0.01817106018404243 -0.006247508107651983 --0.0005560937016598665 -0.01593001697870242 --0.01399127181184955 -0.007103197798536229 -0.01590357633458265 --0.02817126311954438 --0.001907328250532535 --0.007421590158099848 --0.03286112748140223 -0.002060921833398145 --0.00550142042672082 --0.00807426328750442 --0.02792579795212676 -0.01743142669567604 --0.003818186494500163 --0.003613771667183527 --0.01449346711149582 --0.01230035645351027 --0.007890448296507955 --0.006916513211149906 -0.0005896081008539403 -0.006745808773798426 -0.0162682831645454 --0.003785729549068591 -0.008153100619575486 -0.006161850662829519 --0.004112142720619922 -0.0102169740638249 -0.03163672955152776 -0.01095923541816692 --0.01306363175868779 -0.01841526260512721 -0.02318835426168499 --0.008247187269686236 -0.01611268793272442 --0.009982654485706281 --0.01965346314290377 --0.04392394587071819 --0.01543615116627523 -0.00619010939060881 --0.01661978469937312 -0.002749501975780859 -0.01034152522149821 --0.01621423901008867 -0.002852204862551527 -0.001788157176179739 -0.01035698672559023 --0.02437197578331224 -0.007842483333716017 -0.009730880603382702 -0.00310695488466267 --0.007923273442411087 --0.01852459513463494 -0.001205385575680988 --0.006926671146404534 -0.01285900954481521 -0.006028116230868197 -0.01022213514487542 --0.01968123654387025 -0.007467184794166561 --0.001513862107688405 -9.275203202469809e-05 --0.02160593420112384 --0.003037808331528349 --0.00463388021826113 -0.0009072008471823982 -0.01209908437075692 --0.006763663457900014 --0.001432962532834532 --0.02637618313141538 -0.02145385516254548 -0.007823962878214012 -0.01297784112901031 --0.01681282127108752 --0.01081137084825162 --0.03057891160642245 -0.02703611698715908 --0.0004326449471321721 --0.0009016799908266695 -0.02096619150384676 -0.002862586767818165 --0.008249325788232904 -0.04259944984787371 -0.0128484749708137 -0.006286325770660806 --0.02675904010794916 -0.006881454635124842 -0.01763054342473057 -0.007041496630679498 --0.003859050967826861 -0.0003920201767116419 --0.01725842822724766 --0.004114684348636946 -0.00827885858990788 --0.01260824093519398 --0.007796541933741968 -0.002328399831708919 --0.0008823971650326642 -0.008185900051901292 --0.0004760619302936898 -0.006146148117364696 -0.020578570662521 --6.06603232904655e-06 --0.002161307033339793 --0.0007347499353844648 --0.01535776236513778 -0.02115795781593732 --0.004727285679441861 --3.076978446484957e-05 -0.01466515273507737 --0.007337214381672696 --0.005955293505640252 --0.009211815153339794 --0.001013830480485472 -0.003165817197710146 --0.02194133528525962 --0.0123339807218651 -0.0008037768341064717 --0.0181025967329025 -0.009644795261822022 -0.005900597676539191 --0.002868240527846985 -0.005740122848112462 --0.003141714655261093 --0.01328320993236815 --0.007019741866511205 -0.00131086464379034 -0.003449584783044305 --0.01108276947437757 -0.0004426494666975864 --0.01419997745867885 --0.001921288544665169 --0.00769478223964514 --0.004233784825981601 -0.01264627952885882 --0.0193665875821791 --0.004125892601370766 -0.01541290020980129 -0.008922721224138303 -0.0103141522715134 -0.002178373620125354 -0.01437701846676927 -0.03763652844020955 --0.005601920961801258 -0.002958666561788732 -0.00740438301661268 -0.003610226248119495 --0.007494497981039089 --0.0001244141391152115 -0.02262157542949992 -0.04267373566853217 -0.01047898258888232 --0.03100259525061034 --0.01330382121075038 --0.009646182802211604 --0.008507711599268539 -0.003047233632048879 -0.009104007732643219 -0.02704983410222673 -0.0139480306243001 -0.0008023522213313902 -0.01342774015015171 -0.04680392766486464 -0.001977258586009847 --0.01731072095950852 --0.0008673468987175747 -0.01024074315277151 -0.0220290881744565 --0.05461936562487078 --0.01671330146150685 --0.008816271755744957 -0.01875693996529748 --0.001970044567113991 -0.009553301991468191 -0.01405532688010382 -0.004772411399556049 --0.0300463536860414 -0.01515476181038647 -0.0161372997685075 -0.02163034003237584 -0.02347144630322073 --0.01676585880107048 -0.009572572885280394 --0.02296600424521639 --0.03113955917342857 --0.009365906472692302 --0.0002414886769392478 -0.01408412000351894 -0.007212212726331374 -0.02293836666086397 -0.004255111232987584 -0.02233744968861413 --0.01139204486677259 --0.003624043687740727 --0.0259464460035085 -0.0101768713611255 -0.008540364807789969 -0.006309910032365329 --0.004563537595672934 --0.04162241540206459 --0.001550925795703522 --0.01212730648130652 -0.001191433310164514 --0.006663741736751594 -0.01515059071752698 --0.007381250912364885 --0.002563720727718177 -0.02743224106869607 -0.01753686274227351 -0.001125710999688163 --0.02145905499244218 --0.01175833854148734 --0.003451049855824337 -0.001609729619164475 --7.249892792516692e-06 -0.01800621050569225 --0.004535380153641234 --0.02370092072119814 -0.004645685978706206 -0.01213633954531436 --0.02335332813744484 --0.001299381309864154 -0.00822616576228976 --0.009813973890719092 -0.01815210347300254 -0.01693559653205544 --0.001925541532559558 --0.02973922734869056 -0.02047630700354801 --0.006405079313002251 -0.007180420361159274 --0.01481911587333832 --0.02518710764752834 --0.007727895804268055 --0.002302905369232538 -0.01787154743983526 --0.005083889956903552 -0.0004807035131491862 --0.02367260047144851 --0.01148439108201316 --0.0004393562687057712 --0.02914766188081922 --0.01461389976664723 -0.001686048624952834 --0.003363138925213664 --0.02468060677073512 --0.002926810475046166 --0.003614934843680914 --0.02106436685202944 -0.01293251507906198 --0.005788114416657888 -0.007664502480287444 -0.006752197222882973 -0.01188513116288301 --0.00390192259270895 -0.02870095252493179 -0.002242517502101011 --0.0008154859464253555 -0.006271615538385315 --0.01364760442718507 --0.01422010075670395 -0.0001550506524105195 --0.01475633963484422 -0.0008581519030434652 --0.01655298564424954 --0.01291285653545685 --0.006600723488608474 -0.0006973498150875454 -0.001734889691953508 --0.02211454170288848 --0.007935948464175037 --0.009471362064735549 --0.00687801906152408 --0.002653541828557804 --0.02376741796206714 --0.001105150099200283 -0.005154064259733975 --0.0211389747315035 -0.01286504133121527 -0.006289622872198105 --0.002084011605783891 -0.02556985365021749 -0.01709252360864669 -0.00899275825243172 --0.009646607879679289 --0.021292184559867 --0.01125586041827413 --0.003274819727009699 --0.007649013867020772 --0.01888188905379376 -0.0002759683653543373 --0.008804193348626088 --0.01622478875444812 --0.01369160134034805 --0.01467412908456944 -0.009722947259323964 --0.02001782820039825 --0.003887019984632322 --0.04530632592307829 --0.007589493706426229 --0.004452622527134434 --0.01366356670992284 --0.0002516596596313096 --0.007881804552822187 --0.02320950635931417 --0.03198442479497952 -0.005096871985900889 -0.02774636879513193 --0.01742563267868617 -0.008826341668986208 -0.002801899089232056 --0.0121109875560514 -0.01281107657366471 -0.02722660206699005 --0.0002022642756702222 --0.003287871939784436 -0.003197845880977589 -0.007504731957759122 --0.01436268216965977 --0.01570014644383539 --0.00531481962300456 -0.007394798354655848 -0.007660398940911749 --0.002518748256518284 -0.002158094551403613 -0.001927191062784752 -0.01587267449450228 --0.02147868260069313 --0.01182043690984726 --0.000755724298548532 -0.01129132224356749 --0.007111148048983773 -0.0001758388475596971 -0.01152460459957085 --0.0006074430644429766 --0.006179619010560642 --0.01284124050767895 -0.01989345910197729 -0.01216053407628957 --0.008723770548105952 -0.003302631478968715 --0.005851925788608017 -0.004012092778994189 --0.03144821740774612 -0.01260273039001092 --0.0018103057931378 --0.003654124763568644 -0.02204958846307291 --0.005232378583426781 -0.01578578188444911 --0.001609639660602506 -0.005581637100705215 -0.01075980082732734 -0.004830613046387652 -0.002698585502203707 -0.0007461177046031344 --0.001466751889450543 -0.006153517336300792 -0.01329167176568332 -0.01450319940031771 -0.009234553114465877 -0.002810021747574173 --0.01697727141816338 -0.01123665760246998 -0.002124541022159736 -0.01390262972590621 --0.008649847934861591 -0.006162335713658087 --0.01941795756677263 --0.00580267354868882 -0.008216971894694714 -0.00442225946396887 -0.00133411786699842 -0.009998450828092406 -0.003232814125072982 --0.01004382385357007 --0.004005857570312589 --0.02379997202040748 -0.02119102728938049 -0.01622496974620829 -0.001558310490635856 -0.02363735930049632 --0.01517977909569882 -0.01442111699139319 -0.01054632306282705 --0.000900653503022452 --0.004289166731162807 --0.004706816699441552 --0.02273224442566374 --0.01347603156775597 --0.009755575988894637 --0.001127324985399928 --0.01684842293377311 --0.01588462186460805 --0.004123798643170593 --0.0186785359848796 --0.01132920220753525 --0.007452140775776656 -0.01731083234666636 -0.008636518802689895 --0.02666957377180495 --0.002043238853550058 -0.001232595802033744 -0.003183197424533352 --0.01780330883560586 --0.006888540169121596 -0.003640440225692836 -0.01249085235475565 -0.003573853789403236 -0.007649679457840198 -0.01694682211513468 --0.001076736228272598 -0.006806709232433727 --0.02516933522587362 -0.02571300823666357 -0.006469843965520342 --0.005217354467404563 --0.0007524223755534171 -0.01914259072937686 -0.0001635210972976692 --0.01886973417647719 --0.03283691601227646 --0.01021106291512091 --0.01039358306730022 --0.003844912336835711 -0.01505341621310096 --0.003733100581145783 --0.02908710666976869 --0.02079821849208126 --0.01128264392467088 -0.001484439146734521 -0.005376665337948546 --0.004552458380303557 -0.02505142806555408 --0.01791762140020132 --0.02816102674309326 -0.004765186734834283 --0.02377897226450733 -0.01124661755027366 --0.02291496848647871 --0.006378570573125721 --0.004212955056218151 --0.005172464100505938 -0.01951956329371419 -0.001067032040447101 --0.0212337355673045 -0.0104500378355902 --0.002750313408159631 -0.01838138546231393 -0.01103588447240928 -0.00294278862716998 --0.001496456938573537 --0.01235825304694326 --0.004784973133995887 --0.0145304553450953 --0.020140106162011 --0.00649299751854693 --0.0152953012393497 -0.004514143495064866 -0.0109016072304059 --0.03960501905355521 -0.01371169420850173 --0.001320239186767634 --0.01484213184204995 -0.0327029092061576 --0.0141335010090685 --0.0210443609576398 -0.007665197539278155 --0.01264553457679247 -0.02006843789864211 -0.005441667170899345 --0.01744461041429433 -1.212419434708521e-05 --0.01368567040520507 --0.009016701264038045 --0.003186756251476529 --0.02006309634706837 -0.007404114542599849 -0.002352436630585836 --0.0005338109721588705 -0.01167361284963523 -0.0421586983484245 -0.01003739211331729 --0.007773518035069656 --0.01152677320402499 --0.006671082036985199 --0.01759431528179283 -0.01360307666384341 --0.0140734535058952 -0.014236486843532 -0.009841414804657682 --0.003375232676774457 --0.01019207015367098 --0.005440720867744793 -0.02433834590656121 -0.00990296526148814 --0.01271482717545408 -0.005362931905873316 -0.01261851604816073 --0.009574888468903956 --0.01769048824441234 -0.01336024193461339 -0.004748150017263107 -0.01330626206249999 -0.002034255390311251 -0.004672331522973912 --0.02156332159651577 --0.0153669229298173 -0.01527014545682585 -0.02363608432652282 -0.001384455399370721 -0.0240363662792935 -0.02552393067600009 -0.02709365475569762 -0.02132684391082558 -0.004483419169985984 --0.01649861493134095 --0.0092342473152426 --0.01319064630755653 --0.003643793071693718 --0.0197475437691385 --0.003994801452147038 --0.002990360912840994 --0.0192888727616264 --0.001339941451199551 --0.01642196751266858 -0.01420953146836242 -0.008974932029288436 --0.02989795692915964 -0.008872894513016804 --0.006297590180550504 -0.02321590822087837 --0.01665498703114366 --0.005698120037312774 -0.007280931316962126 -0.003080904727680463 --0.00557740634035294 --0.00104246952627834 -0.005454012809754877 --0.01002492099601569 -0.01161941809066553 -0.02508609347111236 -0.005223515892492482 --0.00565139552149169 --0.01872817280569484 -0.009067802423220996 --0.006998996227259443 -0.008089984496130858 -0.02991334290998401 --0.03548166835152131 -0.01230134601422771 --0.01737064963703465 --0.02903886569650859 --0.0111393877203405 --0.02943279990176062 -0.03418693352944115 -0.003478428452752899 -0.004683991658266416 --0.008906813376685654 -0.006422704118738241 --0.009676341937862504 --0.02280210770554284 -0.005542359814264647 --0.01127976221935154 -0.02272517058639634 --0.01269946835307127 -0.01411583442236518 --0.0173203586910601 -0.003339116194443834 --0.01861329775144972 -0.02212966193136476 --0.02781329139154201 --0.0218128275638751 --0.003932053941037814 -0.008630890620953497 -0.01254089155308593 -0.006213317909976061 -0.01787667051215017 -0.02332843671447739 -0.01243436196543425 --0.01087863795629823 --0.01393953653606182 --0.009671820651194133 -5.478519393532387e-05 --0.02210024177452707 --0.001677496270470337 -0.0001319310649757755 --0.001108146910662826 --0.004201542780059761 --0.01429468455378747 -0.001875512661577212 --0.01976629591679434 -0.007199624538408438 --0.005513546013732728 -0.009493122547765961 --0.02931475960931559 --0.006557188016138213 --0.01624226463318285 -0.02012401505150916 -0.002224455263804069 --0.004055509930986369 -0.01669411745556907 --0.0008404923721063164 --0.00876071204872711 -0.02300731209100981 -0.02201575999693554 --0.008442528219364627 -0.01964005993379015 -0.0245259293798195 -0.02799786667924047 -0.04034048816718881 -0.002514677989239008 -0.003631306723336363 -0.01333130541788209 -0.005180309066577605 -0.006955112522422015 --0.002695423313747646 --0.0006143999353419546 --0.01183784297265191 --0.0221996880760404 --0.001661762667092851 -0.01620421957371512 -0.02058047856314743 -0.030121781673893 -0.01047755222995705 -0.004972500150327923 -0.006228687546233684 --1.831830180524466e-06 --0.004762067055073417 --0.001387774325787077 -0.02173331418537729 -0.01450610332824094 -0.01825236961214413 -0.01902660218284419 -0.01466500002943218 --0.01454984103556262 --0.02807121070706109 --0.008205880419726067 --0.02584023988698189 --0.005786006907455564 --0.006783572237874262 -0.018241250159699 -0.008978942917308512 -0.007576223407926604 --0.01600248188990005 --0.01105825845290509 -0.01612700074643601 -0.01125907293498918 --0.009110189134051008 -0.008700281467609835 --0.009502920889178647 -0.006117562896580875 --0.0006047785607954177 --0.02669981570177031 -0.02434079283924822 --0.005506521563884174 -0.02686315485546403 --0.0152260676896741 -0.006281130847753235 --0.01525627770873395 -0.002957363135672856 -0.008850973430756822 -0.029219269716664 --0.009433200414891815 -0.008761774574679807 --0.006912381117449397 --0.007585344647579727 -0.002493671630715789 --0.0008302299480667347 --0.01227293371713308 -0.02197281511938133 -0.008025919878023631 -0.02317062505097985 -0.01060004539960217 -0.02509688296574898 -0.01199354119820134 --0.007935417009972547 --0.01199306432812687 --0.00845302805208145 --0.0193208375784711 --0.01248185619191264 --0.0007155242533027175 -0.0007975345939231534 --0.02153598688329926 -0.002616140522354241 --0.002535882972465923 --0.00493178622533052 -0.01009581914072191 --0.01524790727401618 -0.02019304186943155 -0.00185997119107519 -0.03316813552620992 -0.01457439505917512 -0.004702630442664113 --0.01091187629139023 --0.03084893234114522 --0.009449966937694232 -0.008612008022292332 --0.01818182241816406 --0.000235980792110004 --0.005215899153847526 --0.003967029025564525 --0.009299318893942161 --0.002992213855126377 --0.008762814327358856 --0.01266358097640896 -0.01790981181088667 --0.02301842801614264 -0.00282181826052129 -0.007767915818340168 --0.002783168328430195 -0.007593271724295097 --0.007063224755874797 --0.007070914542405755 --0.01889520980484604 --0.0135606436224545 --0.006020044478371998 -0.002016663023160911 --0.0005593598848843508 -0.01715254605549726 -0.007520546776152653 --0.01048236268201505 -0.01662582819107709 --0.001240268176235298 -0.01369983726345297 --0.007764922224304761 -0.004756785630950981 --0.009559163166284973 --0.008521872296596622 --0.002001679010345239 -0.03532387456455704 -0.002980804622552097 --0.02234976350513048 -0.0007075110002783091 -0.0004313369817162948 -0.007654225103055262 -0.002241243007702808 --0.01120319555780888 -0.01644861239972022 --0.02280169648725505 --0.0004781473630653622 --0.01939394294887693 -0.02122874721007592 -0.009466348344938489 -0.01784227081438027 -0.006808143404756192 -0.003071810821036979 -0.0126904977064881 -0.0004121479581607766 -0.01098106728195575 -0.003284240020346807 --0.003065731711405691 -0.001013642973547149 --0.002350337867709835 --0.01158058981202177 -0.006006171849806169 -0.008467614363005631 --0.005834008160083576 -0.003572628868997989 -0.01067747594807222 -0.007703974237387842 -0.008326276449521991 --0.02139878594709162 -0.005913546278723272 --0.01015850345672509 --0.01228838108842892 -0.003964836139591737 --0.01131810730877458 -0.003350255335217148 -0.01378071730595013 -0.01582855502367351 -0.002270173149936549 --0.02753099762346006 --0.05458642059081979 -0.02096032105811956 --0.001335484491201703 -0.01423010597891592 --0.02489862457580549 -0.01558284862868952 -0.02820669000635973 -0.01158249832417735 --0.01860121434540492 --0.0006092550771201546 --0.01747960795757261 --0.0005409875725104656 -0.0136153622100675 --0.02373777414829764 --0.0008395239705694436 -0.00385574667561625 --0.008115589329708314 -0.001348610617313697 -0.001566003639002294 -0.01011761843041649 --0.0006608921203804334 --0.002575911857168541 -0.01815302480945557 --0.02072552125018492 --0.0262641385888572 --0.00442329267669539 -0.003623058058477043 -0.01736958407660402 -0.01185339056892922 -0.01491089713277747 -0.003033433952060501 --0.03566189430730117 --0.03240925406491323 --0.0239731766402716 --0.01742046142700509 --0.01467664260449347 -0.0008938419992220022 -0.01209376378045557 -0.02441103157422871 --0.01066050878697695 -0.006635337459409193 -0.001579031591803346 -0.0007379584919571555 -0.01442697709865477 --0.0009201054111061487 -0.006343522036326627 --0.008046016315949046 --0.006297029607812271 --0.0172546954889419 -0.006460912369517464 --0.002329618393333419 --0.007863998213912805 --0.02000387018598969 --0.01260559724438364 -0.01921327072230643 --0.01463553140805201 -0.002326277422815354 --0.02020158891088118 -0.01269230619097776 -0.02476304290947576 --0.00517040377964231 --0.002465953442932902 -0.001409118310045361 -0.01324025677522835 --0.03281199315770576 --0.0003423238073269405 --0.001246177163486365 --0.02748829694390831 --0.03015794723933947 --0.01924801816383726 -0.01222660916097782 --0.02055139124712056 --0.0008745854164056718 --0.005685244557756954 -0.003017674225079332 --0.01568443385868898 -0.01706211403336673 -0.01712415562152072 -0.01905680635399063 --0.002342804049826326 -0.002391042279268207 --0.01842818413682125 --0.02642391961824708 --0.001360081998066609 --0.01204509975338573 --0.01797442524509399 -0.00650415321361745 --0.008096629155731604 -0.001851714696659468 -0.007486337247737841 -0.01441049324431237 -0.00387699768868964 -0.006844537297344711 -0.02736233468481648 -0.005215242408168589 -0.001455948881156338 -0.01745843090745991 -0.000599199008415539 --0.02575695642395088 -0.009299947670920622 -0.003162467775286378 -0.02242416348286005 --0.001568095452381308 -0.0002971744930163637 --0.001554683234122159 -0.01059006605789259 --0.006310478860653798 --0.00161541665782159 --0.03068336323756268 --0.02630841528345237 -0.0126561169042398 -0.006227509667878234 --0.02257396240385347 --0.002011813484823319 -0.01180103911245726 -0.01581621539440026 --0.01823070604310181 --0.00362716938467956 -0.007187084995237632 --0.005141851659345853 -0.03045397549861853 --0.006370914531422539 -0.00143230184826514 -0.01771523209263294 -0.001877022396101989 -0.01812318513711229 --0.001567799541643825 --0.009594502271964017 --0.01065181315969197 --0.009723825357177508 -0.0068462349771462 -0.002674864853343341 -0.01675687399552197 --0.002999728454018915 -0.0003320632504074073 -0.0089567877274079 -0.006995749589962106 --0.01751345653549044 --0.0122589339318042 --0.02114035527388586 --0.0234242870460309 -0.005067914168254901 --0.002015454890077364 -0.001570858608644338 --0.01440096517550899 --0.0002569490148673067 -0.002590762395233006 -0.003113197084896254 -0.008888832357408378 --0.007309055974670796 -0.008546472979519104 --0.007487408644024259 -0.01468467143550598 -0.02721778477071241 --0.003169289676179141 --0.009660119064975006 -0.005542655758731487 -0.003041099659027408 --0.01387152297798735 -0.01154306930722966 --0.01624432639396148 --0.005179414522033523 --0.01296095317884778 -0.001035686504454403 --0.01458542986173656 --0.005672881234826287 -0.005871984424042715 -0.01222314070247131 -0.01065564032360763 -0.002873094970990704 --0.003544858241490187 --0.01986275069875775 -0.01770282214844496 -0.01518346418469071 -0.003598029123158551 --0.009484972074418648 --0.01980104566195168 --0.01864140429493547 --0.006366600431551619 --0.003590152107738395 --0.01108666093078196 --0.02363230447683965 --0.02810397154050185 -0.04358161260104806 -0.02965577143514629 -0.01631307634090015 -0.004265813328068092 --0.008949948667113976 --0.01540487984287483 --0.02324373613255554 --0.01294605666121041 --0.004757469994201534 --0.008748337048483219 --0.009029178788397516 --0.01940666705744033 -0.007194241069759199 -0.01336974079073218 --0.0173986693048557 --0.001462381680950147 -0.02418683813024882 -0.01007430439882672 -0.004293174649509116 -3.279015482495487e-05 -0.006536096835981952 -0.01661222391274818 --0.01218046237060773 --0.0008894670166556978 -0.02865334347744229 --0.002510828997796783 --0.02014045359714705 -0.0105902378128561 -0.006907431795473882 --0.001386094893788052 -0.0107862469842047 -0.02556772409882847 --0.01940556284588086 -0.01055544497566266 -0.01529272022451759 -0.01632223204583959 --0.003723701382305042 -0.005292373282768331 -0.01074647097007686 --0.001390334696726014 --0.005000550204930666 --0.0005861474952635248 -0.02356111316306296 -0.004516693101181987 -0.01189796409315707 --0.02393858303140722 --0.01005755066991063 --0.01592947074190488 --0.009134093320879519 -0.00399485919732284 --0.02000856405988809 --0.01631490561543362 --0.01711089392143549 -0.007982513069088572 --0.005608303054401505 --0.001226671687094277 -0.02425915836168496 -0.02189034170776282 -0.01369454436504142 -0.0002951253392119872 --0.006798025116779333 -0.007069106856879686 -0.003445530439520182 -0.02315414291550566 --0.03782637388154714 -0.02758922392078583 --0.004608301044793327 --0.01393077428919685 --0.02632151122798797 --0.01337028202518651 --0.01237240072137181 -0.007479449823903507 --0.01702435150545808 --0.03433728306255194 -0.002677277160745685 --0.00775994661867491 -0.007685157309950867 --0.01448886736735199 -0.03646831627829175 -0.02412834577327096 --0.00186146673470772 --0.01432617387416049 -0.004667873461486125 --0.01588261038629788 -0.01245513660661845 -0.01007868826382511 --0.005797379878496465 -0.007947415650676722 --0.01753389710271067 --0.0220967068975579 -0.01538763476728527 -0.009218232705024164 -0.0008123266863471926 --0.02827824033239022 --0.008422050289485233 -0.008962310576136234 -0.01472770980815467 --0.03380801125873714 --0.009181217601688781 --0.007142760118595578 --0.01070595596621364 --0.03629006350402003 -0.005854884624560524 --0.003434943149016071 --0.02495769520534488 -0.01806811216454718 --0.04743243101334736 -0.02633535657875922 --0.01054902999928291 -0.00187995928445413 --0.01367431870181536 -0.005509459995039222 --0.002760966171878608 -0.03524267084662847 -0.0001756655692120215 --0.01530821546171491 --0.002619454016890088 --0.01545706825884323 -0.00244194615771597 --0.02878119278148453 -0.0005359223490676475 -0.002632718091934924 --0.004484495000386859 --0.007731445899600694 -0.01714452039528974 -0.01311772616138143 --0.01239191064266938 --0.02193009895020458 -0.006765129257295823 --0.008511224096674316 -0.01258806145873188 -2.953615389715363e-05 -0.005391084544388754 --0.01324152141232201 --0.00851487749723602 -0.0002372532523585972 -0.01030696936019442 -0.001968084975612464 --0.009312935994278413 -0.011102823379852 -0.02196030089626533 --0.008711335438126414 -0.01209803597862764 -0.01430330899640266 -0.02233083074595976 -0.001386279969065225 --0.001458734387335184 -0.0133535942670621 --0.007584654867509442 -0.01773134799349694 -0.02446612947189461 -0.01506638421894652 --0.007371600543009406 --0.01342256893681092 -0.01985098440127005 --0.006546874618847636 --0.005446174244860969 --0.006875102331625598 -0.003389365849001732 -0.002199948611427924 -0.01459801647749705 -0.01810308200644735 -0.01117981661778893 --0.01268673001858681 --0.008473161743006311 --0.01374317576208295 -0.00163481134757355 -0.005278705326246254 -0.008968380644583222 --0.0009769150019168244 --0.007505584793028684 --0.01117219315462986 -0.01541792336983751 -0.02710125843852925 -0.02269429583280697 --0.008660284125027055 -0.00483634511207379 -0.008010010174108883 -0.004919172184792992 --0.005033643658121242 --0.009361103140145611 -0.01039952920978106 -0.0168728134498484 --0.002210214303858658 -0.001644286017232677 -0.0016473883694918 --0.02544694756751488 -0.0002627462135695244 --0.01250130016965456 --0.01509447931709746 --0.002253091948850594 -0.002679284372520129 --0.02236065939064861 --0.006587697464090558 -0.01956082453616368 -0.003462133301381847 --0.02279282515307312 -0.0008983647168733348 --0.00214573760881331 -0.01174230450208774 --0.005147334127329778 -0.01145394383815482 --0.0008223154777248628 --0.002003896027831352 --0.00429962105385158 --0.003645408838351193 -0.01003791709976675 -0.003755589830439826 --0.009057414797964661 -0.00108378385016625 --0.001485256100679439 -0.004725659052103081 --0.01725916348334064 --0.01465034209488935 -0.002365079669621194 --0.008163719172141724 --0.001075605502041322 --0.01354100851725906 --0.02337594152975484 --0.01339871784440387 --0.003773593071622333 --0.01505060873464348 --0.003026768159962125 --0.01258011873354536 --0.02214482680948268 --0.01574664087772463 -0.03217252443288242 --0.01298603111777673 --0.005925822839875057 --0.001644087676671262 --0.01903739968696957 -0.02133855878579407 --0.004167567535977313 --0.01629138495582334 --0.001457283029492609 --0.03232177514073072 --0.007571611519078271 --0.009901430500913444 --0.01316175359418615 --0.0008684598040789766 --0.005883452455566981 --0.006442656982238817 --0.01622374076624674 --0.004937040691055238 --0.006790018834121446 -0.005477333952238251 --0.01422204337088316 --0.0105162417604342 --0.01730872575917793 --0.001177343139364629 --0.01643161994344187 -0.02232543151929379 -0.004856239290661274 --0.004775805247646656 --0.01241966970821021 -0.001683854318048276 -0.01341142940483655 -0.01608338676543095 --0.01598837972712713 -0.006783714825382198 --0.01528601319027678 -0.0243970769979488 -0.03443117578194481 --0.009821883161118695 --0.005093588488883336 --0.008870239263593394 --0.006677370027695895 -0.009707389695374172 --0.01299853433879688 --0.01176897173115015 -0.01711791015402429 -0.005432066804389119 -0.0005383346873025084 -0.01867950508493068 -0.001138793808832343 --0.005788785401621202 -0.01779241122026541 --0.00426308758147475 -0.01206538862516047 -0.006870106993930029 --0.01377999600842015 --0.003745437525637571 -0.007999014320935204 -0.01050614247376416 -0.01843881599892026 --0.01717895313996071 -0.006596635273447197 -0.003579198577964199 --0.02543225406576396 --0.001557530080308451 -0.01674135968000739 -0.002447897979049682 -0.007542423975165838 --0.01611164034333274 -0.005487527810549575 --0.001741674390096337 -0.01203154859980562 -0.01594675409258664 -0.04287854779555862 --0.01031677214531021 -0.01525435020605757 --0.004472114677029778 --0.04314596815680957 --0.01878009034727854 -0.00842015633794331 --0.008560327238365216 -0.002417888044225988 -0.003141360129199047 --0.01327622919922524 -0.00240739148345492 -0.02110023806144208 -0.003981906198952117 --0.003498445488061459 -0.02833365247793092 --0.001051424494949802 --0.02430548692214859 -0.02727205402480347 --0.01205563256421332 --0.01131241017155675 -0.001442875242569484 -0.008945422549528429 -0.009785759157804294 --0.001988460840637024 -0.01321148050316419 -0.01086849952997028 -0.01556297420780715 -0.02806224765511247 -0.003984350793121086 --0.01035712191752304 --0.006517141308520551 --0.005397465057372651 --0.00713524924877088 -0.003705964327725333 -0.005001421080033161 --0.01339723674419992 --0.01515586829955307 --0.005718337347237418 -0.01100869280278039 --0.01053084979813007 -0.0004294393407962753 --0.01299248100449674 --0.02709292963770401 -0.02548358539644635 --0.02968152304922021 --0.0074779339971157 --0.004378392225775813 --0.00230931464809077 --0.02377674929568305 --0.007371895750751153 -0.01116199168577324 --0.006658593477453835 --0.02530835134582443 -0.02460182261648258 --0.02714180321875245 -0.01142929171019894 -0.02958991753091236 --0.003056752253358676 -0.03056121573801894 -0.009450681122954743 --0.003845920806718005 --0.01976281598517505 --0.01877940179195196 --0.009351956540127023 -0.00161613606744749 --0.01075110375017815 --0.002365227170694966 -0.006997163081335574 -0.02220070813143225 -0.003159546728129675 --0.02534661535995247 --0.005655320880152411 -0.001804959452269903 --0.03184139848659729 -0.01764925509598555 -0.003985458763753926 -0.009448410760337179 --0.01364018830925454 --0.001730736116933022 --0.01392567461441758 --0.009002500126537333 --0.007791264554680338 --0.002270507235609968 -0.004737090658872144 -0.01138363638236161 -0.00063287890843606 -0.008233619457178074 --0.02122013366262418 -0.01035269895196205 --0.003612956386870847 -0.005872440514353084 -0.02214910001406518 --0.004505210134343432 -0.007176145536187845 -0.03207861596905075 -0.03260694570918068 --0.02295940899589659 --0.0008044939896385729 --0.004902899406882458 --0.001803609086011628 --0.02547534369046859 -0.008339031455697046 -0.02896388500687931 --0.003480909616696143 --0.005124301767374085 -0.02158182571432071 --0.006246488857733734 --0.01051900619283845 -0.005503990374951596 --0.004782502640102333 --0.01363926271891486 -0.00216777907938039 -0.04326847417740708 --0.004524733702412898 --0.01456007053785066 --0.002449332959988761 --0.004036706496715323 --0.002183515300142529 -0.04280082678802109 -0.004082295245692164 -0.001265718213655355 -0.002079810562432237 -0.02691187644898443 -0.01944588618029608 -0.002007001024370076 --0.01697505232735191 --0.01989281786023239 --0.01109682663608693 --0.008696623144786482 --0.002413476953728136 --0.01258725869977341 --0.0007869444007620255 --0.02136730067890838 --0.0005813431717016943 --0.001041637439004884 -0.003695688302787591 -0.003799527982220035 -0.005864772640687918 --0.01250458247284068 -0.000882340298624268 -0.001590549631966471 --0.001807562204734891 --0.00242225688851789 -0.0161680297029911 --0.0003227593881921463 -0.009027993200080435 -0.002663507334251682 -0.01013530935833316 --0.001757586973210711 --0.005071872074905203 -0.009646760942940284 --0.005844214226176702 -0.02072028883714406 -0.004387147163223604 --0.001035153166440642 -0.009895163793190817 --0.01542652712175189 --0.00573718883872852 -0.003910540166469667 --0.01076148726055582 --0.005529488557247855 --0.005426325425868439 -0.00937907442924997 -0.002067897034571891 --0.004744997394110944 -0.002482888192322983 -0.01192812133997755 -0.007083367042630865 --0.02237825349496664 --0.01508654185335042 --0.004005457554853573 --0.02358368343891624 -0.01108524725337704 -0.006116881774818506 --0.009398793642311838 --0.01018379395836166 --0.000678896746564428 --0.001592775653576762 --0.01417088081526732 --0.01426928793113908 --0.003338173046620963 --0.01061186951088806 -0.01329776926039383 -0.01745906839500359 --0.01404291805352927 -0.01169437928079268 -0.001476824819444713 -0.03492567432903454 --0.008694675538107566 --0.005812747141314726 -0.006613300432141644 -0.01955078728853462 -0.03943271650417808 --0.02194225877669898 --0.01379058451679611 --0.002262888437192467 -0.01233424019896446 --0.01110852723794907 -0.008627369668247765 -0.0007782601368858096 --0.008054627048642932 --0.01774333590391123 --0.02643061485626071 -0.02592073180573467 --0.008426225971986683 --0.01297293591143285 -0.0004450720069415193 --0.008466169550768312 --0.0008731601680611137 --0.01938335746283689 -0.02876193742223214 --0.004524913700150957 --0.007260165219141731 --0.003262254581556876 --0.006295607198167523 --0.02871747077627873 --0.01710499229869638 --0.02109506399447368 --0.03614772513035996 --0.02329646209318838 -0.01153965983578244 --0.00158295408790223 -0.01470146677673169 -0.00794657514368333 --0.003101550423983673 -0.01651225477300172 -0.002014460695806933 --0.02098140841733181 --0.01973511613420898 -0.01476854480204782 --0.004416668526445178 --0.01543455572510525 -0.004693658975196252 -0.02717703528680578 -0.005441847157597294 -0.008941613860259212 --0.0001016496505559675 -0.02404397915330471 --0.01462995627876619 -0.01562843130123212 -0.006711945995703162 -0.01083955876779178 --0.002344214847598842 --0.01053824849008605 -0.01672011915740813 --0.007123418455373212 --0.01050276814996309 -0.01181375034352936 --0.003692388209212168 --0.006197181110515579 --0.004990649119706141 -0.02763406435365599 -0.008232879264365207 --7.635851920595113e-05 --0.01647957879344179 -0.004970544262147969 -0.02077511132500649 -0.02080228248128793 --0.02063231699555177 -0.03271939607800944 -0.01852565967928637 --0.01205664064847238 --0.002116742984091494 -0.01648815822592242 -0.006502062292723928 -0.004528820298251461 --0.02329025006564391 --0.002621260927044112 -0.008036775972731546 -0.003957689350239761 --0.01223085875126413 --0.004807480994937861 --0.00937288435718546 -0.009426976729854139 --0.004073623135211561 -0.01394869170258077 --0.003032907588016949 -0.01419920849776457 -0.003981445505857536 -0.03149207149963506 -0.02697533682541672 -0.0196505493889447 -0.003886927523359138 --0.0226550509588688 --0.001765852859099732 -0.02130096097811054 --0.01301965201122001 -0.006647781978185874 --0.004788550144350071 --0.02032918057752187 --0.02555113320794481 --0.006146132438767107 --0.03168984208800225 -0.01050926998081637 --0.01585244611803002 --0.01209930568144516 --0.01840396673303841 --0.01999030789127674 --0.007683178144557741 -0.005653841389420187 -0.00236062896936699 -0.005920526889319284 --0.004126061987838854 -0.008224602789655505 -0.003248361158424754 -0.009314744093099998 -0.02531155387347262 -0.001531360989604527 -0.004463419424068961 -0.01416045722933502 --0.006762527169707457 -0.008424776039206612 --0.01683109910378435 -0.03131674407955989 --0.0130605616982668 -0.009390660446481751 --0.003684966548829124 --0.03608880972776977 --0.03041779807728703 --0.0260813638574111 --0.01675681278014084 -0.02551667361835429 --3.307672422964662e-05 -0.04072899607500657 --0.004531213531825409 --0.003722029193317969 -0.0154853732717035 --0.009592785684314202 --0.006183420058898514 --0.01329990856859712 -0.01466750377679854 -0.02399693781801437 --0.0239621108032971 --0.004792716694845249 -0.01718289590602544 --0.01796587828695473 -0.00708122170691673 -0.006075358410119343 --0.01460363626244955 --0.02748912592714141 --0.03287920546697236 --0.002065223209649254 --0.004585338533037592 -0.01561895620428921 --0.01666166601362881 --0.008200336685154779 --0.01462885583440839 --0.01096387838293024 --0.004602835535997034 --0.001689067646043055 -0.009666802210291965 -0.03131502598052375 --0.02044321346140698 --0.001179923980752694 --0.01003304782932002 -0.03387629132001316 --0.009114524827415087 -0.02215591348240966 --0.003821432102033054 --0.00943066274026 -0.001087823722885975 -0.02175491923844605 -0.02227818411209811 -0.006847173497328751 -0.01838326704215108 --0.009626729277977619 --0.008736830558426214 --0.01289355891377251 -0.02107202375374004 -0.003024271435519285 --0.004782145792186998 --0.01090849840996144 -0.003576632097989232 --0.03271904084894355 --0.0313139365512832 -0.002646443621973686 --0.01755448948010632 -0.03310408083293312 --0.01471532685340964 --0.01941310925610028 --0.003047302499689319 -0.01367455474940252 --0.002630247304288531 --0.01889738361337345 -0.01092461422459755 -0.01378318728680117 --0.00653632733768279 -0.006088416912183969 -0.01632965665578229 -0.0009866326553690226 --0.02332315402704107 -0.01864112773492806 -0.007866838427917895 --0.003652488995947925 --0.00880963610108703 --0.00848970771541353 --0.003937148817801966 --0.0004248936383170823 -0.009166496794338596 -0.005525471080202236 -0.01163784259895482 -0.0003348339349303169 -0.007355973953225249 -0.0003971880607078939 --0.001721615327122501 --0.007567700675988665 --0.004511033218695818 --0.002928729319244283 -0.00236767622222564 --0.0009532957669714733 --0.002138588304012223 --0.003093597316895234 -0.003311886601406318 --0.001982453113180975 --0.000329968500908672 -0.0091042366072486 --0.003635810031537688 -0.003004991136891852 --0.001994148201600247 -0.01859402779087027 -0.01594096662220327 -0.006878030954645049 -0.004852622790295839 -0.01994901004475198 --0.01818286534983383 --0.006689212813708048 --0.002044798789832125 -0.03396037549765166 --0.000602469054994144 -0.003228718150871455 -0.005952119832391481 -0.006888000529981863 -0.00526410760350293 --0.006629374501131399 -0.01236076768832963 -0.00702823308362652 --0.00874362741435118 --0.005358545752747608 -0.03076653403219983 -0.0002369909265770311 -0.02838557326916515 --0.006665819007913285 -0.006317726449988297 -0.01960441897107203 -0.004993839926528477 --0.002889967825507399 -0.02230006788489563 --0.0009233096934082925 --0.01119466785607565 -0.00280794739247922 --0.01902413311459814 --0.02527669742720819 -0.002432645124033397 --0.01636941269283271 -0.0257974572286534 -0.01518472597594466 -0.01070374598739892 --0.009891866339335222 -0.04255417860420893 -0.02599083373080074 --0.0004157584544795538 --0.0128130626293219 -0.007653001052072896 --0.0150933395864396 -0.007619158139307343 --0.01913140679717263 -0.0009898521820700466 --0.008415772150386566 -0.004416067517567056 --0.008669761904759293 -0.023336874350295 -0.00575133334216248 -0.0310844029079565 --0.006442081159825758 --0.03252429226292493 -0.01055111653330694 --0.01180807811106285 -0.006522368417279286 -0.01511683051685856 --0.02091707201178297 --0.005873488721834425 -0.008505664137965467 --0.01946698883408938 --0.01176387850802354 --0.004836880973877548 --0.005845131634876481 -0.00165331302136902 --0.01412895836607164 --0.002181823201348984 -0.01145087120642928 --0.002202471684578441 -0.009869959778725517 --0.01769314022173792 -0.01251498185552966 -0.004251982453711679 --0.02344066626918898 -0.01990935720259963 -0.01467414885566977 -0.007499117997281543 -0.01115831595653478 -0.01125447223768158 --0.002132607272848243 --0.002151235416793215 --0.001386119560109006 --0.00435548160591074 -0.004902627347076239 -0.01662244889469332 -0.0182680604713647 --0.02257500506198227 -0.005955829773188407 --0.000448391655007923 --0.0246226618708224 -0.02595620539088966 -0.002626608588674199 --0.02991068494682555 --0.02110726292343404 --0.00447906804660547 --0.01446562822060928 --0.004355788986039104 --0.0161426678213275 --0.02482698838603063 --0.01531017461922219 -0.01234987262140467 --0.001575369163503677 --0.01393896567663532 -0.01579347583076785 --0.01195873615966028 -0.005356924621168618 -0.001391912030398659 --0.0004860039571481506 --0.002503961399176677 --0.002582375058873727 -0.0004347066589999512 --0.01575358655589022 -0.00837336221465078 -0.008463652655045579 --0.001022982430272171 -0.00520501696632679 --0.002986895043934904 -0.002267202740410702 --0.000432410593425114 -0.006481056569952309 --0.04800938712283203 -0.01219744499076798 --0.02286896996470082 --0.01295446358153783 --0.00544114002278997 --0.0001237358077866703 -0.002240164344840785 --0.0101150514317291 --0.0137522725123128 -0.004994765815622544 --0.01677559357707318 --0.01760740783336645 --0.003318770647864493 -0.006712631032641739 --0.004255388525951637 --0.004520004778856928 --0.02519599531229698 -0.00780982026567117 --0.01910927649965499 -0.004117824410622747 -0.008841719538471255 -0.00726362410036519 --0.004976155618507604 --0.03155080029538329 -0.009227914946593412 --0.01856140545211758 --0.02448406066441343 --0.004746170577440117 --0.001214472009549104 -0.0535408578276222 --0.008528038111435741 --0.03816144774023091 --0.01649557904580179 --0.02873313963071926 -0.01500359724942046 --0.01411336024963264 --0.005592538642348038 --0.009835830861223027 -0.03528496331221245 -0.02976552324709967 -0.01156566379187163 --0.0111850286336193 -0.001463332609201945 --0.009075223628426341 -0.0009346363071058567 --0.01234165908453529 -0.002968012104723452 --0.005533038806710751 --0.02688319393112949 --0.001325419476030691 -0.0330680224329899 -0.02629958558733903 --0.008662718845278909 --0.003625173293204199 -0.01425165352679195 -0.001228533672495057 --0.02565148184069205 --0.007080142837340462 -0.0004223911227202333 --0.003211110733183318 -0.008968612154802437 --0.01479846927038774 --0.008247930482367419 -0.002771963800329455 -0.01534408484507895 --0.01710026840391086 -0.01171027959991574 -0.02402212406967619 -0.00309137136966967 --0.002117538536186037 --0.00911457082057051 --0.009459703268057902 --0.00152315496863822 -0.01888945130102366 --0.01160647459368317 --0.01100378495199468 --0.02095568323355821 -0.006988652683639423 --0.01162141982520174 --0.01173569026504158 --0.007727105389662799 --0.008184212682712695 --0.0004380835180553959 -0.01326999051884771 -0.01728955878481326 --0.01452513473934265 --0.004537318772464566 -0.03443853622286151 --0.008004819950932249 -0.002580874765181795 --0.01721603326898202 --0.005988701343349047 -0.01356391900301972 --0.01899990136776278 --0.004660266012002088 --0.0001799670267742414 --0.008337724791892032 --0.02093198881690757 -0.00497643027829724 --0.004655166729874216 -0.008374116030766564 -0.01708520938317977 -0.00127074445223101 --0.007500741631608927 -0.01104679027628023 --0.0103847644255375 --0.004404265213231994 --0.005093248577204034 -0.01522986715527878 --0.001245939435174563 -0.000137615131160662 --0.000901876929547232 -0.01132340024071861 --0.004626502286594386 -0.01099961713290055 -0.01042898780140086 -0.01447321282904225 -0.01318539289919886 --0.0007025928114086782 -0.00592733002265691 --0.005005985573309787 --0.01841661831486491 --0.01090873985961376 -0.002457154252750759 --0.006182142241504851 --0.00593633374450929 --0.008204129405485656 --0.0005513062353826733 --0.0003262053347149774 --0.001484593273448234 -0.0002399278071498377 --0.004181326911945497 -0.02086970560028008 --0.001363182231838604 -0.01285457275618442 --0.01314161227296116 -0.005123954462881402 -0.01174799808671217 -0.008254679318684049 --0.0003914701295366052 --0.01448323127632071 -0.004590350196914486 -0.007429014750571237 --0.01791216865507756 -0.0155450637293779 --0.008386293179615549 -0.01389566374229496 --0.0005751971093523717 -0.001119223160355214 -0.006774709885436895 -0.004472819097868453 --0.02334784004813516 --0.02504836807289502 --0.009107378114486461 --0.002536504695090088 --0.02249429671421361 -0.02445384844701983 --0.009281353515978785 --0.01388931651242649 -0.005935502043677677 -0.01376845454522151 -0.005748472504045479 -0.01241346713199773 -0.007028821055044452 --0.0113789323718302 --0.01712669390800247 --0.02199995653215517 --0.002026079142969788 --0.006815008606571565 -0.03019077003408841 -0.005111824232890477 -0.0001240762395036108 -0.01747625822036971 -0.01709098435014246 -0.0006495272773432004 -0.02355623116398139 --0.01006215624589642 --0.0006836519330520807 --0.00664700668111633 -0.01335862796887182 --0.0071346556844671 --0.02733985624559693 --0.003758098950371275 -0.005455161445328613 -0.03297825037012105 --0.009420977780484646 --0.007229741488679471 --0.02141001566652911 -0.01145420506435428 --0.0224997140962453 --0.01104964908943273 -0.0027379471540166 -0.00170041668178301 --0.01090790133826591 -0.01409202623647622 --0.009751673412586452 --0.01223534940035067 -0.02305520640284937 --0.01524771581617245 --0.005324409813668551 -0.005939939252526662 --0.02414897280790787 --0.004958173675856237 -0.001229356921646565 --0.01438180073891094 -0.01628735103828915 -0.005323494255030514 --0.00390812841417758 -0.001609492620279846 --0.01996789587248918 -0.01219210112435778 --0.003871994096287004 --0.02889881872572414 -0.002482564292332902 --0.006975013135280087 --0.04634255691284854 --0.008395110552666082 -0.002831146147286676 --0.006162989723940918 --0.002421235527046069 -0.004394359287582313 -0.00433073776109897 -0.008412281327709688 -0.004734982682449173 --0.01660159032085854 -0.005488952525395874 --0.001791074044562602 --0.01946984491131826 --0.01652989235304309 --0.04273075468829583 -0.01055478124066546 --0.01166553623699939 -0.008995214246888105 --0.01706998461498297 -0.004376089678065407 --0.01543549035866707 -0.0008877688943729031 --0.01145316127346453 -0.002464920648666707 -0.001817785128128876 -0.03177326911342972 -0.00880651483187408 --0.006200743706968137 -0.01837332126698483 -0.00882725848335923 -0.004308607847147712 --0.00842877843306402 -0.02836981078349971 -0.00967868298921829 --0.006026203216130596 --0.002261321533324721 -0.004599563773545781 -0.01068700777622773 -0.008045841156592263 -0.01573259378650451 --0.005199937854318985 -0.008360536610402224 -0.002906926106021127 --0.007616712177906346 -0.001145348909207857 --0.005693724542258696 -0.003292062325396654 --0.01315248213028009 -0.02408815247506037 --0.009160904044120528 -0.01122126414768435 --0.02941265319724936 --0.01148169994412605 --0.02853228928863357 -0.000673452068606376 --0.01169284783012576 -0.005560227317419792 -0.005210142034995962 -0.01079073695426922 -0.001901969110087884 --0.005147450736950603 -0.02222156551038382 -0.01025434227612661 -0.002524536626112206 --0.005679254439708144 --0.02572794438290521 -0.01031382728177885 --0.01470150749362793 -0.03543497475775316 -0.005689245719326667 --0.01067330551251063 -0.0009548414424256995 --0.03095938558167714 --0.01635261658949197 --0.02308912696253445 -0.004674143378091358 --0.007621953397951528 --0.01230600455385311 -0.01936752839456654 -0.04728568573305469 --0.001895846479870579 -0.006036893799525046 --0.004638500528102567 -0.002640158175632735 --0.01767818562214534 -0.02752327226573516 -0.007193348964005182 -0.00489373436986091 --0.02426392040323166 --0.006217780241019454 -0.0008083012162578878 -0.01572494836014236 -0.0006097627854191757 -0.01936893065581738 --0.01218275403279498 -0.0165999979175938 -0.005329791590545539 -0.03533524460165303 --0.02272071365749606 -0.003316187937271407 -0.03236487003305031 -0.02143594909215428 --0.01218811504635551 -0.01877557477323152 -0.0042731381318537 --0.01955734781922343 -0.002925113006907796 --0.02323024950985484 --0.01345202502011725 --0.003304058746681566 --0.008684056156435452 --0.002163862574983402 --0.01643966156834209 -0.02586055388112574 -0.01407859308697153 --0.01337850116415801 -0.02324349341892464 --0.003671188223633858 --0.0103631765367267 --0.01555510748278809 -0.002404649550303531 --0.0163750566359102 -0.001903041260215059 -0.00722207833076819 -0.007501112406203057 -0.003803629459414235 -0.005714272791965208 --0.009968392639959861 -0.03006014022281655 --0.009070210931690463 --0.00814483621414277 -0.01770472018781694 -0.01367959534308426 -0.003604521091145355 -0.001271993741419705 -0.007133286519030656 --0.01315810057552531 --0.0155367824108035 --0.02106282389481737 --0.0244814282829759 -0.002209596062493842 --0.004584221608802652 --0.009438099820647092 -0.02583005464061603 --0.009909457090528598 -0.007113654579060055 -0.007227665055871958 --0.006484712434597325 --0.01189933289208502 -0.005590310231099386 -0.007397985380134291 -0.005407842712607397 --0.01920591786135048 --0.01171918300484283 -0.0007105742507445786 -0.006260336976728494 -0.006950419449576898 -0.002058232310930714 --0.0363614566676785 -0.00455735504751413 --0.006400843946043929 -0.01036767868636081 --0.0194957407002138 --0.007482184923385937 -0.008179379923398717 -0.01091666165169481 --0.01124579885012563 -0.006574801684584704 --0.001619829484791423 -0.003114017131408277 -0.01431117041012382 -0.03253441069271527 -0.006997404858053531 --0.01988037024675691 --0.001231390228950152 -0.001929071545877891 -0.005649390895935678 --0.02401916653708915 -0.009234650316530748 -0.01202399533808105 -0.0007777404826719666 --0.004244523168833211 -0.01542517568911507 --0.007607221462152986 -0.006415157018443484 -0.006113978962449352 -0.01767650506593157 -0.0280726424721713 -0.006039569056600746 -0.003089935519962547 --0.003097886759974776 -0.006985924919270068 --0.01964202528004993 --0.00404512804437 -0.004926470494239515 -0.002955021776779445 --0.01208045608229583 --0.0008870961228251745 -0.002872188482398446 -0.004583935690550723 --0.003858163788996736 --0.008541089535762022 -0.01232799719527246 -0.01198496114626416 --0.0376487804658333 --0.01239740283997382 --0.001438273787931985 -0.0130484721531434 -0.00392882918265642 -0.0004624613723549903 -0.00988422733329984 -0.01432758687115645 --0.01295527112561802 -0.01071001769917615 --0.01760074102594351 -0.01025089082363851 --0.02234255213784205 --0.02173085608298476 --0.00360001702717 -0.007860628749591106 --0.03032262509308284 --0.01032386846149099 -0.00864622049949451 --0.01486429206589223 --0.02464569526697027 --1.356897650041551e-05 --0.0249802268672495 -0.02073118207115261 -0.007877261053166643 -0.004734312137613272 -0.00281568530988095 --0.003626684377528272 -0.006663645507143537 --0.002417990464944067 --0.04259101424467835 --0.007744765423826181 -0.007762924695421096 --0.02871628090518566 -0.0004086141444095896 --0.005958034132690692 --0.005117810226449309 -0.00676070352035596 --0.007756069061809423 -0.004817211425157856 --0.008848955771684316 -0.002933174450045206 -0.007958756317302619 --0.00951207819635976 --0.01412364017349722 -0.004052790938513488 --0.006021536665858019 -0.01417334775672426 --0.005440701623951991 --0.01212959036890946 --0.01123115931482615 --0.02153961841801609 --0.0007934487767917474 --0.01056882475536789 -0.003049040040623498 -0.01078974910962729 --0.02259810865999345 -0.01180398841472394 -0.01907616534136599 --0.01337347782632039 -0.01352076439601524 -0.03841108368346392 --0.04055373316825232 -0.02833531234145752 -0.01652709019531644 --0.02033993480304984 -0.003655060252318861 -0.000786771673395233 --0.008266414290187582 -0.01362906475767354 --0.007772942977004248 --0.009794519653819026 --0.01081830980746562 -0.01778198066175413 --6.337367060934039e-05 --0.0003400641738031175 --0.006781242610329956 --0.004072983842581091 --0.001164080705672999 -0.006908706997562371 --0.004993150015659275 --0.002177302292586101 --0.0069359300816773 -0.008593972743989814 -0.02775783108677 -0.008631506504358042 -0.00413099363578831 --0.004145777660538971 --0.02612339911197775 -0.02261321343630619 --0.01097753142264771 --0.006255561837063394 --0.001702905788777154 --0.01414948821366367 -0.01492285574237289 --0.003126796773281403 -0.01731028146504614 --0.01007660250033313 -0.005940126251586111 -0.02830321696088971 --0.01657982983551986 --0.03820714029272418 --0.008129723342072553 --0.0171744786367911 -0.003839906906576135 --0.01716711541643853 --0.009369421885099248 -0.01876499888644826 --0.006730907102688891 --0.02222333069040138 -0.01602411831999552 -0.007773288259687514 -0.03012405961732158 -0.0199150608714509 -0.001048116846537224 -0.004190428790468613 -0.006068646508991573 --0.006948025164089164 -0.008448157587918515 -0.0005037567595575689 -0.01941094340009932 --0.01558262823935828 -0.02441847459204936 --0.01034157579081853 --0.01439937828896192 --0.02047233347858545 --0.01527119985291525 --0.02420322278461007 --0.003134143733260424 --0.01572790692753894 -0.0244147616010543 --0.005072830696996081 --0.01764259376087315 --0.002444796668407607 -0.02337880541947749 -0.008268183448937707 --0.0472520816506342 --0.006198419892204437 -0.01533382492127159 -0.03157494244398008 -0.02066852643023912 -0.0005212499234999194 --0.02832043108514444 --0.003024176325080968 -0.01826422943706914 -0.02325718918458554 --0.003337195695177221 -0.008751006585410372 --0.01109095382046884 --0.004918274026477905 -0.02318414903935491 --0.01577811276111911 --0.009481758579210874 --0.003271246810777286 -0.005759151212969622 --0.004913793396815016 -0.007348137944217296 --0.002091022978430097 -0.01574210259428629 --0.009943784653053926 -0.008028408046474443 -0.009954774769305598 -0.0282150308612504 -0.02308067747978576 -0.005954108039567689 -0.005619699463107813 --0.01249247942439769 -0.01290003048818869 --0.0003265508120333372 --0.01469157886755291 --0.006378406991335836 -0.008942414711110406 --0.01630974723025268 --0.01142928556562286 --0.006460002000714391 --0.01338443104625616 --0.005828552013528361 -0.01026697958827046 --0.02132481693647283 --0.007603400019158524 -0.005358107271568497 --0.01139980533626015 -0.008149538920396923 -0.0008710539203635368 -0.006156161791800552 --0.01961807017007714 --0.009556160215132187 -0.0002905488407600563 -0.002685614344771768 --0.00453110306326527 -0.001141345204497309 -0.02929899422818204 --0.000146685257744896 --0.01917343489512589 --0.01344873978246047 --0.002472150161016636 --0.004213550411267318 -0.02462283972800554 --0.01367361473040458 --0.008504043237079829 -0.01705807719727934 -0.00983585751175377 -0.01124340753305263 --0.001757970096786753 --0.0001334327687674705 -0.009646428776346975 -0.005015378952103714 -0.005265539624014826 -0.009735755869555203 -0.0026793006113741 -0.007716179608036353 -0.002667543106616402 -0.00196322262018457 --0.01755616568027096 --0.03105504367818732 --0.01852146545840607 --0.007892178156758462 --0.006344020793244809 -0.001736492655039864 -0.0005654292706995542 --0.01171120395627112 --0.01190598522173246 --0.009074451498291242 --0.007594742078175147 --0.008955932701465347 -0.002909371466195072 --0.02191025028141997 --0.02194379277787434 -0.000192683116072489 --0.009148711567351076 --0.001862945744971001 --0.002179597969434772 --0.01273896768328057 -0.008431554601103078 -0.00106024741333531 --0.005934655764505666 --0.002196371321038518 --0.0100894212552339 -0.02700045869582356 -0.007334900402754162 --0.009897847296833684 -0.00715976102733476 --0.01991733939380202 --0.007357571038926293 --0.001319399661219862 -0.04652879838361375 -0.009973162015194711 -0.009618742335764058 -0.03057005098961838 --0.02818024699769371 -0.003189392647853853 -0.009030429141936552 -0.00251109153778454 -0.001308665377700117 -0.01606308181099008 -0.01438997033976475 --0.01463184399033823 --0.0009535991470952108 --0.02583281586964435 --0.01412254762049034 -0.01390209474623605 -0.004950827196089225 -0.03497512079680302 --0.01123471153187777 --0.006679336715318984 -0.004201188760536267 -0.02052605940156248 --0.003058915191952805 --0.0001681188276387851 --0.005376602520693313 --0.005257042703876781 -0.01975915040983996 -0.003320443697274082 -0.004696593320418577 --0.0403446072861945 -0.0008715451809642304 -0.02230239133717548 -0.01365825094038805 -0.01799221302687173 -0.01512857695194226 --0.004925035054470859 --0.01665001713451024 -0.01754550906569217 --0.008585444873006637 --0.007689722259475283 -0.03039968227985917 -0.03850410626190468 --0.00519157452897538 --0.01204823750018192 -0.02725250362549569 --0.006654209129966883 --0.01412857624485538 -0.02578844478895474 -0.00996881675076023 --0.007549656552828025 -0.03038992015884526 -0.02038503412032708 -0.003779836143993172 --0.01123360077986335 --0.002467890119529495 -0.01531501741348034 --0.007316801130663386 --0.009613193308891771 -0.003157725172311934 -0.01483373140697417 -0.007592517011473918 --0.01704261468175717 -0.02214190083770232 --0.004406747703956672 --0.0207663346317325 --0.003782296682800211 --0.007051686339933606 --0.005914969893877447 -0.03373725520739661 -0.002425119865916307 --0.00309265302130277 --0.009216635366219115 -0.002746039378600673 --0.00888515005715354 --0.01054683990350574 -0.003287695026993249 --0.01217806469614054 -0.006502264552732562 -0.02714031616947617 -0.01828334470794161 --0.004619290241341466 --0.0006677405313025577 -0.01077791261163933 --0.03120473967703216 --0.03683242118198414 --0.01832041251490778 --0.0003497210665655151 -0.01020025716042055 --0.01524897691181655 --0.01005985079402065 --0.01900942416893886 -0.02834803241375867 -0.01551767911573025 --0.02833643852533742 -0.009895908852247073 -0.004952556944618854 --0.02180069944933652 -0.008523656075957786 --0.01223104310666341 -0.02357472449337427 --0.007848588429341781 -0.01007407812277661 --0.01049423319536491 --0.003692773467291717 --0.04100564678520459 -0.000269864666563532 -0.02038056679634657 -0.01114612403695811 --0.008490506292959653 -0.008225535377320379 -0.02192906253175392 -0.00240048131858734 --0.00402537600825677 -0.01547775852489192 -0.001991643746644618 --0.009955977451476189 --0.01752304483853321 --0.0004880264940159554 -0.00184148271379681 -0.0107713907643284 --0.02565781489879055 -0.003523787900757492 -0.03150256804863619 -0.01440471598178603 -0.01188344506108061 --0.007597927891781663 --0.005688475332986122 -0.008368961302056166 --0.03406343617413088 -0.007651773068034115 --0.002810614992210151 --0.01442355023193317 --0.00930942517908496 --0.01738687851762602 --0.01496750270809569 --0.001880131533996531 --0.02150940258425937 -0.01891878116566312 -0.02535369682026802 --0.008001460217392713 -0.01457954926397509 --0.005996851935756954 --0.02645879905527309 --0.01925532918353922 --0.03035450009468155 --0.004498623530743501 --0.03764008086239421 --0.004510430910161323 -0.002778516395608248 --0.004863356269083184 --0.002861916086107435 --0.03010558923798578 --0.009612790808077698 -0.006870374459038819 -0.007183679358267206 --0.01056143179122944 -0.009246213763820533 --0.03407414026039592 -0.006536109577288808 --0.03570880650006806 --0.00256972215619462 -0.02308572980782077 --0.02264905849994069 --0.05410349133434062 --0.03065995798434372 -0.031863893106199 --0.01789927994077019 --0.01578340952287412 -0.01612195199705951 --0.0161754669172831 -0.008967087146923434 -0.01951791168127714 --0.005550980727729709 -0.01593155394105586 --0.006737185408648629 -0.02658820277524416 -0.01964556236776288 -0.02800367959005816 -0.02928421563736574 --0.003406911554392224 --0.004683231814117317 -0.01469177006074971 -0.01043533264507262 --0.001326671671339284 --0.01501005052017033 --0.03712251598512212 --0.00579401557140454 --0.02453559300724241 --0.02285450929285504 --0.0006336748959054387 -0.01518813684715994 --0.01484761422123549 -0.01312734447579811 -0.002708230628160069 --0.02006481914527003 -0.0229095791354312 --0.01674121776424106 --0.007448030816575247 --0.0129143579538611 -0.005898341603338111 -0.03940992889730704 -0.01563042402664403 --0.0112737737131252 -0.01313133291807826 -0.03056694985152921 -0.01277810382730765 -0.006074553003625459 -0.002643228556582045 -0.001852565306432142 -0.009950528115110007 --0.00978732767477377 --0.01330989069333403 --0.001847937864769756 -0.004673887153457299 -0.006673689168090389 --0.007797684895687047 -0.003895373914808362 -0.000877769969073182 -0.003234935159984071 --0.002382328483122484 --0.006838504395495584 --0.007109499654775752 -0.004690532455906133 --0.01578540762506189 --0.0105186108454337 -0.006449627651086042 -0.0008659955369537049 --0.01593994288097261 --0.004364885651002412 --0.00297714642303416 --0.003276880160015473 --0.002913579875677131 --0.005293863394846808 -0.01500060536879348 -0.02718197426782389 -0.001973063604433861 --0.01110063579036806 -0.01856553495664489 -0.001107601409016411 -0.00561501627705944 --0.005546774648695785 --0.002920830040983198 --0.001072365539073448 --0.02868215724871776 -0.01108259439364273 --0.001038948672440086 --0.002551597250200969 -0.006789073246382256 --0.0106527188330943 --0.01393504545568204 --0.008254621084635002 --0.0007596866344118802 --0.01074174093348666 -0.002094179527578489 --0.006852628810115411 --0.005958139833308673 --0.01720135294300625 --0.001156409360436312 -0.005544355991337468 --0.002706345139482663 --0.004001522067199958 -0.03657233067842677 --0.003180665729650278 --0.00671291974076794 --0.01252689920645897 --0.04176006895435073 -0.00300204829262944 --0.0019631064042179 --0.002887174967372167 --0.009633091089705224 -0.03197655319102453 -0.004677648190332648 --0.00422058801337834 --0.02329711356613009 --0.0001658912860526463 -0.01047949595913793 --0.001330315136965086 -0.03675530086562152 -0.01582561317980396 --0.002708693519195159 --0.04822072866946137 -0.01280724733588027 -0.004492782277935439 --0.02846603272897769 --0.01550594501095104 -4.245254987837467e-05 --0.006435485714250382 -0.01236203449806062 --0.02945947057757245 --0.01467583504710125 --0.009194771191684775 --0.030940241609674 -0.005702229936330789 --0.02845484858111282 -0.01556843813261503 -0.005253243002647325 --0.01533881042846362 -0.004638977649766434 --0.02164714998971094 -0.00361133622408166 -0.0371863405304098 --0.007207325197702643 --0.008072944543663023 -0.03145071816656336 -0.004279937779200758 --0.02439676903785956 -0.01570624486136686 -0.02049964857755283 -0.008571127046193158 --0.01569139109953498 -0.007010297565927171 -0.0137918003747383 -0.004474221166233592 --0.01946445121864369 --0.008979531198974339 -0.006114188637525397 --0.01433565214411659 -0.01401985074914269 -0.02418435815121221 --0.04947458859574772 --0.008662373286325537 --0.007518488780997615 -0.01254010445474588 --0.009068525930721901 --0.015428080901077 -0.001245014469365529 -0.01340376860400185 -0.007408507975101764 -0.01224980159270087 -0.01221519533041613 --0.002450309316354523 --0.01403446778394327 --0.01501718422290021 -0.01382119355322259 -0.006925837551820885 -0.01422543346146635 -0.003126316130391671 --0.009759507585943447 --0.004515081040667552 -0.0184211583778267 --0.00301334658383286 -0.03526382100861304 --0.005763125629352025 -0.003859628102802385 -0.01640992715249558 --0.0132724185004416 --0.005429495624950713 --0.01610039127796882 -0.0190666544080012 --0.006008169545401953 --0.001640952182593106 -0.008678963120391098 --0.006560033202091975 --0.01178731303978556 -0.01678707458057755 --0.003742623816834697 -0.01025773572604616 --0.007378540838533612 --0.003727362841688868 --0.000837522225570645 -0.005631750173079518 --0.02295009761451031 --0.02060608942868164 --0.01459792328485058 --0.01602339266022188 -0.004820403169663087 -0.01327368680494862 --0.02007515477855184 --0.01433959294531711 -0.02069041124632693 -0.005073165997318196 --0.01869705326717852 --0.00697138840158981 --0.0332817989791832 -0.006186898695763961 -0.007756986993487383 --0.01343075855582001 --0.01331099248621712 -0.03630076040567996 -0.001750181901429801 -0.008236691580402281 --0.007424815882232308 -0.01302245024972943 -0.03309769642738106 --0.01093175213848803 --0.002247245997031039 --0.02008036255533713 --0.00885610589240567 -0.005350029110215819 --0.007763626370236366 -0.001855312284918393 --0.04054569097076473 --0.01931669526263645 -0.01037077748590742 --0.01378737972509295 --0.003298226780031409 --0.01077276358634363 -0.003565177543258448 --0.007346028800042844 --0.004967302033390996 -0.01162919611371515 --0.003618604735717639 --0.01771246381439291 --0.02162694884119417 --0.03058398165228755 -0.01129803453831104 -0.01988557807548826 -0.003090755698382868 --0.01503023188282204 -0.0139015637894172 --0.003170569976254531 -0.01004330018724429 --0.005514814437924848 --0.006909174112045331 -0.009132437683185673 --0.01626133377305174 --0.003395230429404601 -0.01706234056137755 --0.0009556272437728052 -0.01032593776344388 --0.0161055690697245 --0.003802496858683152 --0.0001045289663565352 -0.009260188578330154 --0.007087580885692222 -0.006637454797923579 --0.002473127019125103 -0.02403494801378056 --0.01140655705142183 -0.03183263598949247 -0.007399362812465869 -0.0175512050714865 --0.01006068316617398 --0.007405734287376196 --0.01282018810092489 --0.007026390622589841 -0.009910072367183956 -0.003474072157141561 -0.01442648729935228 -0.005078431989435122 -0.01707525876406475 --0.004732839122433889 -0.02889917475456872 -0.003105141177924763 --0.005230209002812439 --0.007474395437483116 -0.005351739605678192 --0.00791345102367071 --0.00787197521927511 --0.01117473676391647 --0.03604038065176966 --0.007511897661885605 -0.001634010057073694 -0.01377114410533289 -0.00980477295699818 -0.02397216916237251 -0.006303681208571039 -0.001305973472988622 -0.003068833736754394 -0.009712442755140544 -0.02313812611247533 -0.01847771290869842 --0.01240904162976993 -0.00866447519406865 --0.02296742859721455 -0.01726599170420226 -0.007680741808977268 --0.02359946657768762 --0.002506896760679496 --0.0014444060339337 --0.01188211223242937 -0.008462957566127537 --0.02036825267874936 -0.007048906669455735 -0.007320347017932488 --0.003888724139715328 --0.0002038557167446097 -0.01076458425422057 --0.008849398556298934 -0.01750518473606481 -0.00649076115189502 -0.01632116394140625 -0.001588517511907531 -0.005394235147066633 --0.008584940276567648 -0.01751061734991783 -0.002200734330235819 --0.006876940316865636 --0.007860281268000924 --0.01195695109695417 --0.02303525308255487 --0.02193756919565023 --0.004417098812208356 --0.01299878072111846 --0.01500916082433785 --0.002569339668940366 --0.0119437556285678 -0.02292838197667446 --0.01398311344776766 --0.002480835206852796 -0.0144114349141262 -0.007404394114182884 --0.007804194318305033 --0.003257282807043618 --0.01506450993692678 -0.01866899190302616 --0.0008034727598605041 -0.003208336163053487 -0.008976540539303772 --0.01259311946237895 --0.001366530593811902 --0.001725063596972607 -0.02144484948185182 -0.01384025574023827 --0.009909140743035153 -0.03315125027395317 -0.006904412816946368 -0.01382324888550336 -0.001598431960756535 --0.003804017504204842 -0.01900910121709114 -0.01261570802457964 --0.001364268621036571 --0.02693978151524247 --0.004580673028005613 -0.01316716943583306 -0.02459615893625969 -0.01361500452629209 -0.008176704201722099 -1.197955201233251e-05 --0.01117539339090682 --0.03311034229307786 -0.005231735134011704 -0.02224010929835316 --0.004402460390882429 -0.01270930673348074 --0.01055248764250602 -0.007862828278494667 --0.004655738824427762 --0.03145101100301825 --0.01631270519561777 --0.01235817303394284 -0.02039030310521683 -0.003747146981692299 --0.01773495066741079 -0.0158880806014404 -0.02355239702142845 --0.02519791103684815 --0.03996701995438993 --0.009147440906650548 --0.01201076937582297 --0.001745933908305584 --0.01015953270929641 -0.05411292102502311 -0.005879375847221719 -0.007251730069490883 -0.01237500663803921 --0.0004249924873270862 --0.01475861942556251 -0.01072882758323287 -0.007858024885138997 --0.01625218026698153 --0.01604772525044638 -0.00581368634557428 -0.00638483819377105 --0.0006513546862175954 -0.009131134521716044 -0.03521455157619021 -0.01066692724542432 --0.007809868984940659 --0.01380670346623858 -0.01696569213572393 -0.019291133231876 -0.02999733046749321 -0.007424388248721526 --0.01468518254145522 --0.01057420252769083 --0.001453259656950157 -0.01933253108711584 --0.03081772101281013 -0.01548993995195879 --0.005072038094306734 --0.01232566549368926 --0.02525342900595093 --0.01495885097694772 -0.002118847028197435 --0.005895042441752004 --0.002981069043224838 --0.02312955791798903 -0.01384561741944177 -0.007893081889197811 -0.003310096499544184 --0.01948649592910526 --0.02592984286373955 -0.007827077285211401 --0.008427002587605257 --0.03852505771688302 -0.03489698776233709 -0.02654639265967004 -0.03590686222294073 --0.001167594158782169 -0.004948762328436335 -0.01542619570998532 -0.005237409335841976 --0.004680062638277922 -0.03064082517211047 -0.02407422111065966 --0.01835936629463496 --0.01940408814121492 --0.0003015836736504799 --0.003279187286941378 -0.003460404511508226 --0.03006130330703631 --0.002221712135099534 -0.003245402796957847 --0.01010417721497479 --0.02210106881780647 -0.02139571462286946 --0.01172203886870713 -0.01603489720504438 --0.01158060220004198 -0.01795321187941191 -0.005762843557234979 -0.01244513045636123 -0.02100765127278477 -0.002256507965716916 --0.04498735438632368 -0.00406366452792569 --0.004715770210993587 --0.0163223762337607 --0.005126678270572817 -0.01882227980030095 -0.009301602762092702 -0.0365317617045287 -0.02083200977701033 --0.002695492681371857 -0.003197993533616333 --0.001472975051209063 --0.0003186004996362475 --0.00246748496713778 --0.007494180230948377 -0.01472725543282279 -0.03665734325216793 --0.01458367360933156 --0.02006216193689288 -0.01709203912042638 -0.007299759554254884 --0.02550838494588182 --0.009175745773407242 --0.002439928449770857 --0.009460724405402654 --0.02733190418472063 -0.001382641863765436 -0.04172133512368545 --0.01891337623770762 --0.01567365458450751 --0.03432054990161081 -0.01188376276679091 --0.03056358240640782 --0.002841202340091693 --0.0004848467491689695 -0.002707310166625807 --0.001994689711391858 -0.001979987282358538 --0.02218750955138959 -0.003053599109053886 --0.009304206656967136 --0.02381657953166434 -0.03351834306207179 --0.03080237455344828 -0.002712576360957537 --0.01866048178070419 --0.04275923139936443 --0.007897959145482122 -0.01116667018229131 --0.02290031686912631 --0.0365918939021931 --0.007672177373576339 --0.008362136036287748 --0.003078938324631922 -0.007039215751315215 --0.009018467205375623 --0.00470860635890057 -0.003602678919302242 --0.003086681927234068 -0.002502598254855494 -0.01520604009601336 -0.002918008994150385 -0.003850689193926608 --0.006880675210700436 --0.02072870478100336 --0.01345486105727304 -0.01669076439246846 -0.006398660888555329 -0.005579883521651698 -0.01870131934365079 --0.0208097178659284 --0.0009436042167345623 --0.02532025226035112 --0.001544584588500054 --0.04155546179234096 -0.004861309078836543 -0.01204093956078467 -0.04162541559719533 -0.02050737042312399 --0.001214160382287964 --0.003688134921549865 --0.004908708211978426 -0.01719240141975271 -0.01234398515654684 --0.002730021609324972 -0.01128233844293456 -0.02054598684784238 -0.004588128805327625 --0.009030848883505915 --0.005912720340995263 -0.02797786574909942 -0.0009488273136703182 -0.0007460614501613194 --0.008079922249812769 -0.007042786142229772 --0.006725859535732895 -0.002884405307200951 --0.01861324621635662 -0.005654274981213094 --0.006496547564795704 -0.009422114284617372 -0.003891733699865319 --0.01937898149158729 -0.009824713927864904 --0.003928011245137781 --0.004047471340990934 --0.003145561644031582 --0.005145018171695522 -0.009935977007277299 --0.006413503944716811 --0.003993981527416234 -0.0284827621070262 -0.0006976840598726484 --0.01890689118680361 --0.006379917738488249 -0.001106944144783874 --0.001821220227014897 --0.0004432979499690127 diff --git a/data/combined.txt.model_ b/data/combined.txt.model_ deleted file mode 100644 index 90edbb7a9..000000000 Binary files a/data/combined.txt.model_ and /dev/null differ diff --git a/data/demo.py b/data/demo.py new file mode 100644 index 000000000..7f467e45e --- /dev/null +++ b/data/demo.py @@ -0,0 +1,103 @@ +import sys +import glob +import getopt +import numpy as np +import cv2 as cv +import pymtracking as mt + +print("OpenCV Version: {}".format(cv.__version__)) + + +def draw_regions(img, regions, color): + for reg in regions: + brect = reg.brect + cv.rectangle(img, (brect.x, brect.y, brect.width, brect.height), color, 2) + + +def draw_tracks(img, tracks, fps): + for track in tracks: + brect = track.GetBoundingRect() + if track.isStatic: + cv.rectangle(img, (brect.x, brect.y, brect.width, brect.height), (255, 0, 255), 2) + elif track.IsRobust(int(fps / 4), 0.7, (0.1, 10.), 3): + cv.rectangle(img, (brect.x, brect.y, brect.width, brect.height), (0, 255, 0), 2) + trajectory = track.GetTrajectory() + for i in range(0, len(trajectory) - 1): + cv.line(img, trajectory[i], trajectory[i+1], (0, 255, 0), 1) + + +def main(): + args, video_src = getopt.getopt(sys.argv[1:], '', ['cascade=', 'nested-cascade=']) + try: + video_src = video_src[0] + except: + video_src = 0 + args = dict(args) + + cam = cv.VideoCapture(video_src) + + _ret, img = cam.read() + print("cam.read res = ", _ret, ", im size = ", img.shape) + + fps = cam.get(cv.CAP_PROP_FPS) + print(video_src, " fps = ", fps) + + configBGFG = mt.KeyVal() + configBGFG.Add('useRotatedRect', '20') + configBGFG.Add('history', '1000') + configBGFG.Add("nmixtures", "3") + configBGFG.Add("backgroundRatio", "0.7") + configBGFG.Add("noiseSigma", "0") + print("configBGFG = ", configBGFG) + mdetector = mt.BaseDetector(mt.BaseDetector.Detectors.MOG, configBGFG, img) + print("CanGrayProcessing: ", mdetector.CanGrayProcessing()) + mdetector.SetMinObjectSize((1, 1)) + + tracker_settings = mt.TrackerSettings() + + tracker_settings.SetDistance(mt.MTracker.DistRects) + tracker_settings.kalmanType = mt.MTracker.KalmanLinear + tracker_settings.filterGoal = mt.MTracker.FilterCenter + tracker_settings.lostTrackType = mt.MTracker.TrackNone + tracker_settings.matchType = mt.MTracker.MatchHungrian + tracker_settings.useAcceleration = False + tracker_settings.dt = 0.5 + tracker_settings.accelNoiseMag = 0.1 + tracker_settings.distThres = 0.95 + tracker_settings.minAreaRadiusPix = img.shape[0] / 5. + tracker_settings.minAreaRadiusK = 0.8 + tracker_settings.useAbandonedDetection = False + tracker_settings.maximumAllowedSkippedFrames = int(2 * fps) + tracker_settings.maxTraceLength = int(2 * fps) + + mtracker = mt.MTracker(tracker_settings) + + while True: + _ret, img = cam.read() + if _ret: + print("cam.read res = ", _ret, ", im size = ", img.shape, ", fps = ", fps) + else: + break + + mdetector.Detect(img) + regions = mdetector.GetDetects() + print("mdetector.Detect:", len(regions)) + + mtracker.Update(regions, img, fps) + tracks = mtracker.GetTracks() + print("mtracker.Update:", len(tracks)) + + vis = img.copy() + # draw_regions(vis, regions, (255, 0, 255)) + draw_tracks(vis, tracks, fps) + cv.imshow('detect', vis) + + if cv.waitKey(int(1000 / fps)) == 27: + break + + print('Done') + + +if __name__ == '__main__': + main() + cv.destroyAllWindows() diff --git a/data/dota/DOTA_v1.0.names b/data/dota/DOTA_v1.0.names new file mode 100644 index 000000000..adea76198 --- /dev/null +++ b/data/dota/DOTA_v1.0.names @@ -0,0 +1,15 @@ +plane +ship +storage_tank +baseball_diamond +tennis_court +basketball_court +ground_track_field +harbor +bridge +large_vehicle +small_vehicle +helicopter +roundabout +soccer_ball_field +swimming_pool \ No newline at end of file diff --git a/data/dota/DOTA_v1.5.names b/data/dota/DOTA_v1.5.names new file mode 100644 index 000000000..4d18c4f11 --- /dev/null +++ b/data/dota/DOTA_v1.5.names @@ -0,0 +1,16 @@ +baseball_diamond +basketball_court +bridge +container_crane +ground_track_field +harbor +helicopter +large_vehicle +plane +roundabout +ship +small_vehicle +soccer_ball_field +storage_tank +swimming_pool +tennis_court diff --git a/data/haarcascade_frontalface_alt2.xml b/data/haarcascade_frontalface_alt2.xml deleted file mode 100644 index b49cf5df3..000000000 --- a/data/haarcascade_frontalface_alt2.xml +++ /dev/null @@ -1,20719 +0,0 @@ - - - -BOOST - HAAR - 20 - 20 - - 109 - - 0 - 20 - - <_> - 3 - 3.5069230198860168e-01 - - <_> - - 0 1 0 4.3272329494357109e-03 -1 -2 1 1.3076160103082657e-02 - - 3.8381900638341904e-02 8.9652568101882935e-01 - 2.6293140649795532e-01 - <_> - - 0 1 2 5.2434601821005344e-04 -1 -2 3 4.4573000632226467e-03 - - 1.0216630250215530e-01 1.2384019792079926e-01 - 6.9103831052780151e-01 - <_> - - 1 0 4 -9.2708261217921972e-04 -1 -2 5 3.3989109215326607e-04 - - 1.9536970555782318e-01 2.1014410257339478e-01 - 8.2586747407913208e-01 - <_> - 9 - 3.4721779823303223e+00 - - <_> - - 0 1 6 2.3025739938020706e-03 -1 -2 7 4.4174338690936565e-03 - - 1.0183759778738022e-01 8.2190579175949097e-01 - 1.9565549492835999e-01 - <_> - - 0 1 8 2.2203210741281509e-02 -1 -2 9 -1.7283110355492681e-04 - - 2.2054070234298706e-01 7.3263257741928101e-02 - 5.9314841032028198e-01 - <_> - - 0 1 10 4.3567270040512085e-03 -1 -2 11 - -2.6032889727503061e-03 - - 1.8441149592399597e-01 4.0322139859199524e-01 - 8.0665212869644165e-01 - <_> - - 0 1 12 1.7309630056843162e-03 -1 -2 13 - -7.8146401792764664e-03 - - 2.5483280420303345e-01 6.0570698976516724e-01 - 2.7790638804435730e-01 - <_> - - 0 1 14 -8.7343417108058929e-03 -1 -2 15 - 9.4522320432588458e-04 - - 2.8899800777435303e-01 7.6165872812271118e-01 - 3.4956431388854980e-01 - <_> - - 1 0 16 4.9414858222007751e-02 -1 -2 17 - 4.4891750440001488e-03 - - 8.1516528129577637e-01 2.8087830543518066e-01 - 6.0277748107910156e-01 - <_> - - 1 0 18 6.0313619673252106e-02 -1 -2 19 - -1.0762850288301706e-03 - - 7.6075017452239990e-01 4.4440358877182007e-01 - 1.4373120665550232e-01 - <_> - - 1 0 20 -9.5083238556981087e-03 -1 -2 21 - 7.6601309701800346e-03 - - 5.3181701898574829e-01 5.4110521078109741e-01 - 2.1806870400905609e-01 - <_> - - 1 0 22 7.6467678882181644e-03 -1 -2 23 - -8.4662932204082608e-04 - - 1.1589600145816803e-01 2.3406790196895599e-01 - 5.9903818368911743e-01 - <_> - 14 - 5.9844889640808105e+00 - - <_> - - 1 0 24 -4.8506218008697033e-03 -1 -2 25 - -4.6141650527715683e-03 - - 1.8054960668087006e-01 2.1778939664363861e-01 - 8.0182367563247681e-01 - <_> - - 0 1 26 -2.4301309604197741e-03 -1 -2 27 - 4.1787960799410939e-04 - - 1.1413549631834030e-01 1.2030939757823944e-01 - 6.1085307598114014e-01 - <_> - - 0 1 28 1.0010929545387626e-03 -1 -2 29 - 1.0577100329101086e-03 - - 2.0799599587917328e-01 3.3020541071891785e-01 - 7.5110942125320435e-01 - <_> - - 1 0 30 1.2376549420878291e-03 -1 -2 31 - 3.5315038985572755e-04 - - 2.7682220935821533e-01 1.6682930290699005e-01 - 5.8294767141342163e-01 - <_> - - 0 1 32 -1.1953660286962986e-02 -1 -2 33 - 1.4182999730110168e-03 - - 1.5087880194187164e-01 4.3912279605865479e-01 - 7.6465952396392822e-01 - <_> - - 1 0 34 3.4642980899661779e-03 -1 -2 35 - -1.4948950149118900e-02 - - 2.6515561342239380e-01 2.2980530560016632e-01 - 5.4421657323837280e-01 - <_> - - 1 0 36 -1.0506849503144622e-03 -1 -2 37 - -4.0782918222248554e-03 - - 3.6228439211845398e-01 2.6012599468231201e-01 - 7.2336578369140625e-01 - <_> - - 0 1 38 5.4242828628048301e-04 -1 -2 39 - -7.3204059153795242e-03 - - 3.8496789336204529e-01 2.9655128717422485e-01 - 5.4803091287612915e-01 - <_> - - 0 1 40 1.1421289527788758e-03 -1 -2 41 - 1.1783400550484657e-03 - - 4.1047701239585876e-01 7.2390240430831909e-01 - 2.7872839570045471e-01 - <_> - - 0 1 42 4.4077109545469284e-02 -1 -2 43 - 3.7900090683251619e-03 - - 5.6405162811279297e-01 5.9475481510162354e-01 - 3.3120200037956238e-01 - <_> - - 0 1 44 -2.4291418958455324e-03 -1 -2 45 - 9.4262324273586273e-03 - - 6.6032320261001587e-01 4.6806651353836060e-01 - 2.0643380284309387e-01 - <_> - - 0 1 46 8.0630257725715637e-03 -1 -2 47 - 5.2240812219679356e-03 - - 5.2988511323928833e-01 5.2816027402877808e-01 - 1.9095499813556671e-01 - <_> - - 0 1 48 -7.0630568079650402e-03 -1 -2 49 - 5.6897541508078575e-03 - - 1.3806459307670593e-01 5.4906368255615234e-01 - 1.2602810561656952e-01 - <_> - - 0 1 50 1.2472929665818810e-03 -1 -2 51 - 4.9543488770723343e-02 - - 2.3726630210876465e-01 5.2401661872863770e-01 - 1.7692160606384277e-01 - <_> - 19 - 8.5117864608764648e+00 - - <_> - - 1 0 52 -4.9326149746775627e-03 -1 -2 53 - 2.7918140403926373e-05 - - 1.9980649650096893e-01 2.2993800044059753e-01 - 7.3932111263275146e-01 - <_> - - 1 0 54 3.0876200180500746e-03 -1 -2 55 - 7.4669660534709692e-06 - - 1.5338400006294250e-01 2.0368589460849762e-01 - 5.8549159765243530e-01 - <_> - - 0 1 56 1.8739729421213269e-03 -1 -2 57 - 9.3380251200869679e-04 - - 2.0498959720134735e-01 3.2341998815536499e-01 - 7.3230141401290894e-01 - <_> - - 0 1 58 1.9151850137859583e-03 -1 -2 59 - -5.9683797881007195e-03 - - 3.0451491475105286e-01 2.9321339726448059e-01 - 5.6212961673736572e-01 - <_> - - 0 1 60 -7.2115601506084204e-04 -1 -2 61 - -5.9663117863237858e-03 - - 3.6580368876457214e-01 2.7121558785438538e-01 - 7.2263348102569580e-01 - <_> - - 0 1 62 3.0874179676175117e-02 -1 -2 63 - -1.1099710129201412e-02 - - 4.4198378920555115e-01 3.6129769682884216e-01 - 5.2514511346817017e-01 - <_> - - 0 1 64 2.1164179779589176e-03 -1 -2 65 - -9.4317439943552017e-03 - - 3.6286169290542603e-01 1.6010950505733490e-01 - 7.0522767305374146e-01 - <_> - - 0 1 66 -3.5266019403934479e-03 -1 -2 67 - -1.6907559474930167e-03 - - 1.3012880086898804e-01 1.7863239347934723e-01 - 5.5215299129486084e-01 - <_> - - 0 1 68 4.6470930101349950e-04 -1 -2 69 - -1.0215570218861103e-02 - - 3.4873831272125244e-01 2.6739910244941711e-01 - 6.6679191589355469e-01 - <_> - - 1 0 70 1.2634709710255265e-03 -1 -2 71 - -1.1875299736857414e-02 - - 3.4378638863563538e-01 5.9953361749649048e-01 - 3.4977179765701294e-01 - <_> - - 0 1 72 -1.0732339695096016e-02 -1 -2 73 - 7.1836481802165508e-03 - - 2.1504899859428406e-01 6.2714362144470215e-01 - 2.5195419788360596e-01 - <_> - - 0 1 74 -2.8340889140963554e-02 -1 -2 75 - -4.5813230099156499e-04 - - 8.2411892712116241e-02 5.9100568294525146e-01 - 3.7052011489868164e-01 - <_> - - 1 0 76 4.2940340936183929e-03 -1 -2 77 - 1.0751079767942429e-02 - - 1.5947279334068298e-01 5.9804809093475342e-01 - 2.8325080871582031e-01 - <_> - - 1 0 78 2.2465119138360023e-02 -1 -2 79 - -5.7988539338111877e-02 - - 7.8770911693572998e-01 1.5557409822940826e-01 - 5.2396571636199951e-01 - <_> - - 1 0 80 7.2110891342163086e-03 -1 -2 81 - -4.8367571085691452e-02 - - 6.6203659772872925e-01 1.4247199892997742e-01 - 4.4298338890075684e-01 - <_> - - 0 1 82 -1.4418059960007668e-02 -1 -2 83 - -2.3156389594078064e-02 - - 1.5885409712791443e-01 2.3757989704608917e-01 - 5.2171349525451660e-01 - <_> - - 1 0 84 7.6985340565443039e-03 -1 -2 85 - -5.6248619221150875e-03 - - 1.9417250156402588e-01 6.2784057855606079e-01 - 3.7460449337959290e-01 - <_> - - 1 0 86 -7.2936748620122671e-04 -1 -2 87 - 6.1783898854628205e-04 - - 3.8409221172332764e-01 3.1064930558204651e-01 - 5.5378472805023193e-01 - <_> - - 1 0 88 -4.5803939428878948e-05 -1 -2 89 - -1.4719359569426160e-05 - - 3.4444490075111389e-01 2.7295520901679993e-01 - 6.4289510250091553e-01 - <_> - 19 - 8.4680156707763672e+00 - - <_> - - 0 1 90 -1.3469370314851403e-03 -1 -2 91 - -2.4774789344519377e-03 - - 1.6570860147476196e-01 2.2738510370254517e-01 - 6.9893497228622437e-01 - <_> - - 0 1 92 5.2632777951657772e-03 -1 -2 93 - 4.9075339920818806e-03 - - 1.5120740234851837e-01 5.5644702911376953e-01 - 1.6054420173168182e-01 - <_> - - 0 1 94 -2.3254349362105131e-03 -1 -2 95 - -1.4665479538962245e-03 - - 1.8802590668201447e-01 3.1224989891052246e-01 - 7.1653962135314941e-01 - <_> - - 1 0 96 -1.2311690300703049e-01 -1 -2 97 - 2.2108340635895729e-03 - - 3.8595831394195557e-01 2.4552939832210541e-01 - 5.6957101821899414e-01 - <_> - - 0 1 98 2.0661531016230583e-03 -1 -2 99 - 3.6130280932411551e-04 - - 2.7165201306343079e-01 2.2933620214462280e-01 - 7.2086298465728760e-01 - <_> - - 1 0 100 7.9957872629165649e-02 -1 -2 101 - 2.6064720004796982e-03 - - 7.8336209058761597e-01 5.5452322959899902e-01 - 2.5506898760795593e-01 - <_> - - 1 0 102 6.5699010156095028e-03 -1 -2 103 - 1.6259610420092940e-03 - - 1.8193900585174561e-01 3.5298758745193481e-01 - 6.5528190135955811e-01 - <_> - - 0 1 104 3.6204981151968241e-03 -1 -2 105 - -4.4391951523721218e-03 - - 5.4623097181320190e-01 1.3598430156707764e-01 - 5.4158151149749756e-01 - <_> - - 0 1 106 -9.0540945529937744e-03 -1 -2 107 - -4.6067481162026525e-04 - - 1.1151199787855148e-01 5.8467197418212891e-01 - 2.5983488559722900e-01 - <_> - - 0 1 108 -5.6621041148900986e-03 -1 -2 109 - 5.1165837794542313e-03 - - 1.6105690598487854e-01 5.3766787052154541e-01 - 1.7394550144672394e-01 - <_> - - 0 1 110 -2.1362339612096548e-03 -1 -2 111 - -5.4809921421110630e-03 - - 1.9020730257034302e-01 3.2720080018043518e-01 - 6.3648408651351929e-01 - <_> - - 0 1 112 -8.1061907112598419e-03 -1 -2 113 - 6.0048708692193031e-03 - - 6.9148528575897217e-01 4.3273261189460754e-01 - 6.9638431072235107e-01 - <_> - - 0 1 114 -8.7028548121452332e-02 -1 -2 115 - -4.7809639945626259e-03 - - 8.5941338539123535e-01 9.7394466400146484e-02 - 4.5870301127433777e-01 - <_> - - 0 1 116 -2.2166660055518150e-03 -1 -2 117 - 1.3642730191349983e-03 - - 2.5546258687973022e-01 3.3190909028053284e-01 - 5.9641027450561523e-01 - <_> - - 0 1 118 -9.0077864006161690e-03 -1 -2 119 - -1.5494120307266712e-02 - - 2.6665949821472168e-01 1.8481859564781189e-01 - 6.2459707260131836e-01 - <_> - - 1 0 120 -4.2165028862655163e-03 -1 -2 121 - 4.3249759823083878e-02 - - 5.3799271583557129e-01 5.1830291748046875e-01 - 2.1704199910163879e-01 - <_> - - 1 0 122 2.8786511393263936e-04 -1 -2 123 - 1.2373150093480945e-03 - - 2.6133841276168823e-01 2.7865320444107056e-01 - 5.9089881181716919e-01 - <_> - - 1 0 124 1.9528300035744905e-03 -1 -2 125 - -1.4947060262784362e-03 - - 2.6128691434860229e-01 5.9154129028320312e-01 - 3.4557819366455078e-01 - <_> - - 1 0 126 3.5878680646419525e-03 -1 -2 127 - -2.5938691105693579e-03 - - 1.5870520472526550e-01 1.2704110145568848e-01 - 5.9794288873672485e-01 - <_> - 27 - 1.2578499794006348e+01 - - <_> - - 0 1 128 3.5810680128633976e-03 -1 -2 129 - -2.8552350122481585e-03 - - 1.9951049983501434e-01 7.3730701208114624e-01 - 2.9217371344566345e-01 - <_> - - 0 1 130 1.9758539274334908e-03 -1 -2 131 - 3.2583118882030249e-03 - - 1.9564199447631836e-01 5.6920468807220459e-01 - 1.8390649557113647e-01 - <_> - - 0 1 132 2.3711679386906326e-04 -1 -2 133 - 2.5942500215023756e-03 - - 2.1716670691967010e-01 2.7199891209602356e-01 - 7.1502441167831421e-01 - <_> - - 0 1 134 -2.5032449513673782e-02 -1 -2 135 - 6.3087949529290199e-03 - - 1.8251839280128479e-01 5.6998378038406372e-01 - 3.5098528861999512e-01 - <_> - - 1 0 136 -3.2494920305907726e-03 -1 -2 137 - -1.4885730110108852e-02 - - 4.0239268541336060e-01 3.6040958762168884e-01 - 7.2919952869415283e-01 - <_> - - 1 0 138 8.0623216927051544e-03 -1 -2 139 - 2.7405679225921631e-02 - - 6.4914900064468384e-01 5.5189931392669678e-01 - 2.6596811413764954e-01 - <_> - - 1 0 140 3.4368600696325302e-02 -1 -2 141 - -2.7292970567941666e-02 - - 6.7125129699707031e-01 1.6913780570030212e-01 - 4.3262779712677002e-01 - <_> - - 0 1 142 7.4452121043577790e-04 -1 -2 143 - 7.0336280623450875e-04 - - 3.4051001071929932e-01 5.5167931318283081e-01 - 3.3113878965377808e-01 - <_> - - 0 1 144 -1.2275460362434387e-01 -1 -2 145 - 3.2559928949922323e-03 - - 1.6753150522708893e-01 3.6157518625259399e-01 - 6.4207828044891357e-01 - <_> - - 0 1 146 -3.2090399414300919e-02 -1 -2 147 - 3.2957999501377344e-03 - - 2.9210790991783142e-01 5.6130319833755493e-01 - 3.3578601479530334e-01 - <_> - - 0 1 148 -3.2273170072585344e-03 -1 -2 149 - 1.1171669466421008e-03 - - 6.9706428050994873e-01 3.5411500930786133e-01 - 6.1440062522888184e-01 - <_> - - 1 0 150 -1.7279950901865959e-02 -1 -2 151 - 1.1741200461983681e-02 - - 5.5371809005737305e-01 5.3419572114944458e-01 - 2.7571049332618713e-01 - <_> - - 1 0 152 4.6405228786170483e-03 -1 -2 153 - -1.6913030296564102e-02 - - 2.4895210564136505e-01 1.7119289934635162e-01 - 5.5239528417587280e-01 - <_> - - 1 0 154 1.0060169734060764e-02 -1 -2 155 - -6.0715491417795420e-04 - - 8.2734507322311401e-01 3.7793910503387451e-01 - 5.4762518405914307e-01 - <_> - - 1 0 156 -1.0865400545299053e-03 -1 -2 157 - 8.9362077414989471e-03 - - 3.2965409755706787e-01 6.0628837347030640e-01 - 2.4342200160026550e-01 - <_> - - 1 0 158 -2.6372660067863762e-04 -1 -2 159 - 1.3110050000250340e-02 - - 3.8140949606895447e-01 5.5176162719726562e-01 - 3.7268930673599243e-01 - <_> - - 0 1 160 -2.9806280508637428e-03 -1 -2 161 - -4.1619571857154369e-03 - - 1.2296640127897263e-01 7.2522747516632080e-01 - 4.9734550714492798e-01 - <_> - - 0 1 162 3.3842328935861588e-02 -1 -2 163 - -1.2564560165628791e-03 - - 5.3483128547668457e-01 5.8519148826599121e-01 - 4.3841668963432312e-01 - <_> - - 0 1 164 -1.9635230302810669e-02 -1 -2 165 - -9.9625496659427881e-04 - - 2.2978340089321136e-01 6.2959378957748413e-01 - 4.1315990686416626e-01 - <_> - - 0 1 166 -2.3127110674977303e-02 -1 -2 167 - 2.3525709286332130e-02 - - 1.6954590380191803e-01 5.1741302013397217e-01 - 5.9519391506910324e-02 - <_> - - 0 1 168 -1.9356520846486092e-02 -1 -2 169 - -4.1787112131714821e-03 - - 1.3572479784488678e-01 2.9966288805007935e-01 - 5.7916951179504395e-01 - <_> - - 1 0 170 3.1488779932260513e-03 -1 -2 171 - 7.3972279205918312e-03 - - 6.5925890207290649e-01 5.3071719408035278e-01 - 3.7951210141181946e-01 - <_> - - 0 1 172 7.1955118983169086e-06 -1 -2 173 - 4.7114409506320953e-02 - - 3.1283149123191833e-01 5.5378931760787964e-01 - 1.0273090004920959e-01 - <_> - - 0 1 174 7.2878710925579071e-03 -1 -2 175 - -6.1887511983513832e-03 - - 4.6608591079711914e-01 7.1588581800460815e-01 - 4.7244489192962646e-01 - <_> - - 1 0 176 2.9757320880889893e-03 -1 -2 177 - -1.8449809867888689e-03 - - 5.9345688670873642e-02 7.0273017883300781e-01 - 4.7187310457229614e-01 - <_> - - 0 1 178 1.0239540279144421e-04 -1 -2 179 - 2.4277009069919586e-03 - - 5.8947342634201050e-01 4.8623558878898621e-01 - 5.2475881576538086e-01 - <_> - - 0 1 180 -6.4751312136650085e-02 -1 -2 181 - 3.9380151429213583e-04 - - 6.9174712896347046e-01 4.6696171164512634e-01 - 2.3824059963226318e-01 - <_> - 31 - 1.4546750068664551e+01 - - <_> - - 0 1 182 1.4397440245375037e-03 -1 -2 183 - -5.4068560712039471e-04 - - 2.7734708786010742e-01 7.4271547794342041e-01 - 2.4797350168228149e-01 - <_> - - 1 0 184 -7.1237959673453588e-06 -1 -2 185 - -2.3661039303988218e-03 - - 2.1995030343532562e-01 5.8899897336959839e-01 - 2.5957161188125610e-01 - <_> - - 0 1 186 1.7343269428238273e-03 -1 -2 187 - 1.5874590026214719e-03 - - 1.8601259589195251e-01 4.1518709063529968e-01 - 7.1034741401672363e-01 - <_> - - 1 0 188 3.7285638973116875e-03 -1 -2 189 - -1.2883819639682770e-01 - - 2.5279670953750610e-01 1.3930009305477142e-01 - 5.2545148134231567e-01 - <_> - - 1 0 190 7.9412180930376053e-03 -1 -2 191 - -1.2661729939281940e-02 - - 2.4877290427684784e-01 2.7107000350952148e-01 - 6.6188377141952515e-01 - <_> - - 0 1 192 3.0146789868013002e-05 -1 -2 193 - -1.6330160200595856e-02 - - 3.8128259778022766e-01 2.3264320194721222e-01 - 5.2630108594894409e-01 - <_> - - 0 1 194 1.4622770322603174e-05 -1 -2 195 - -2.0858660340309143e-02 - - 4.2933320999145508e-01 1.6004039347171783e-01 - 6.7823147773742676e-01 - <_> - - 1 0 196 2.8194559272378683e-03 -1 -2 197 - 3.7899368908256292e-03 - - 6.6792941093444824e-01 4.5877051353454590e-01 - 7.1762388944625854e-01 - <_> - - 1 0 198 3.5344641655683517e-02 -1 -2 199 - -1.1571600334718823e-03 - - 1.8640750646591187e-01 5.5382597446441650e-01 - 3.1504508852958679e-01 - <_> - - 0 1 200 -5.8742752298712730e-03 -1 -2 201 - -1.5201780115603469e-05 - - 2.8287911415100098e-01 5.8702242374420166e-01 - 3.7048238515853882e-01 - <_> - - 1 0 202 -2.2681879636365920e-04 -1 -2 203 - 3.7845689803361893e-03 - - 4.2189309000968933e-01 6.6670012474060059e-01 - 2.4611820280551910e-01 - <_> - - 1 0 204 -8.5295992903411388e-05 -1 -2 205 - -4.4394891709089279e-02 - - 3.5575878620147705e-01 1.6655470430850983e-01 - 5.2348488569259644e-01 - <_> - - 0 1 206 1.0126030538231134e-03 -1 -2 207 - -7.6327780261635780e-03 - - 2.8846129775047302e-01 2.9693400859832764e-01 - 6.0801112651824951e-01 - <_> - - 0 1 208 4.0330411866307259e-03 -1 -2 209 - 1.3676689565181732e-01 - - 4.5363900065422058e-01 5.1772642135620117e-01 - 1.4491820335388184e-01 - <_> - - 0 1 210 -5.0060478970408440e-03 -1 -2 211 - -1.2475839816033840e-02 - - 7.6169097423553467e-01 2.1597060561180115e-01 - 5.4601877927780151e-01 - <_> - - 1 0 212 -9.4012258341535926e-04 -1 -2 213 - -1.2191980145871639e-02 - - 3.9262959361076355e-01 3.4788811206817627e-01 - 5.5426627397537231e-01 - <_> - - 0 1 214 -5.4959481349214911e-04 -1 -2 215 - -2.1802430273965001e-04 - - 6.0642760992050171e-01 5.6974071264266968e-01 - 1.7797139286994934e-01 - <_> - - 0 1 216 6.9115799851715565e-03 -1 -2 217 - -9.7631698008626699e-04 - - 5.3793722391128540e-01 3.3278390765190125e-01 - 5.4615312814712524e-01 - <_> - - 0 1 218 -8.7870173156261444e-03 -1 -2 219 - -1.6761029837653041e-03 - - 2.1161609888076782e-01 6.6358232498168945e-01 - 4.3658590316772461e-01 - <_> - - 1 0 220 -5.5694948881864548e-02 -1 -2 221 - -1.9844379276037216e-02 - - 5.3874248266220093e-01 1.6028049588203430e-01 - 5.3304588794708252e-01 - <_> - - 0 1 222 -7.4751611100509763e-04 -1 -2 223 - 2.3032890632748604e-02 - - 2.9174768924713135e-01 5.6081241369247437e-01 - 1.9979810714721680e-01 - <_> - - 1 0 224 -3.0700280331075191e-03 -1 -2 225 - -1.1636839481070638e-03 - - 3.9383140206336975e-01 5.7574361562728882e-01 - 4.2394569516181946e-01 - <_> - - 1 0 226 2.2464339435100555e-01 -1 -2 227 - 1.4412109740078449e-03 - - 7.6765531301498413e-01 5.3538662195205688e-01 - 2.5147768855094910e-01 - <_> - - 0 1 228 -3.0011249706149101e-02 -1 -2 229 - -5.3078960627317429e-02 - - 2.3649039864540100e-01 2.3858639597892761e-01 - 5.4146647453308105e-01 - <_> - - 1 0 230 2.0800929050892591e-03 -1 -2 231 - -4.0738182142376900e-03 - - 6.5116149187088013e-01 6.0304141044616699e-01 - 3.5877010226249695e-01 - <_> - - 1 0 232 -1.9529370591044426e-02 -1 -2 233 - -5.3309470415115356e-02 - - 5.4235929250717163e-01 2.3609539866447449e-01 - 5.4017579555511475e-01 - <_> - - 0 1 234 -3.4849561750888824e-02 -1 -2 235 - -1.2658450007438660e-01 - - 2.8369858860969543e-01 1.8135160207748413e-01 - 5.4210460186004639e-01 - <_> - - 0 1 236 7.3325118137290701e-06 -1 -2 237 - -1.1843870393931866e-02 - - 3.9803659915924072e-01 2.6163849234580994e-01 - 5.2377301454544067e-01 - <_> - - 0 1 238 -4.8470678739249706e-03 -1 -2 239 - 8.1693977117538452e-03 - - 2.4381080269813538e-01 5.3271460533142090e-01 - 8.1903767585754395e-01 - <_> - - 1 0 240 -6.4716790802776814e-03 -1 -2 241 - -1.5188479665084742e-05 - - 4.6796938776969910e-01 5.5639117956161499e-01 - 4.3675860762596130e-01 - <_> - - 1 0 242 3.0696711037307978e-03 -1 -2 243 - -1.6296720423270017e-04 - - 6.6643488407135010e-01 5.5946111679077148e-01 - 3.0427119135856628e-01 - <_> - 39 - 1.8572250366210938e+01 - - <_> - - 1 0 244 -9.8275858908891678e-03 -1 -2 245 - -4.1693858802318573e-03 - - 2.1160189807415009e-01 6.9246852397918701e-01 - 3.0437770485877991e-01 - <_> - - 0 1 246 3.5341319744475186e-04 -1 -2 247 - 4.8054549843072891e-03 - - 3.1832858920097351e-01 5.4565590620040894e-01 - 2.5222688913345337e-01 - <_> - - 0 1 248 2.1071180526632816e-04 -1 -2 249 - -2.8318869881331921e-03 - - 2.9026180505752563e-01 3.1304559111595154e-01 - 6.8849372863769531e-01 - <_> - - 1 0 250 -7.5633679443853907e-06 -1 -2 251 - -8.2888139877468348e-04 - - 2.9624658823013306e-01 3.0996260046958923e-01 - 5.7525151968002319e-01 - <_> - - 0 1 252 1.6209259629249573e-03 -1 -2 253 - 9.1338958591222763e-03 - - 3.9931958913803101e-01 4.8273721337318420e-01 - 7.5378328561782837e-01 - <_> - - 0 1 254 -4.1212290525436401e-03 -1 -2 255 - -2.5447290390729904e-03 - - 2.6169270277023315e-01 3.1087028980255127e-01 - 5.4912358522415161e-01 - <_> - - 0 1 256 -6.2652782071381807e-04 -1 -2 257 - -3.6596331483451650e-05 - - 3.2396918535232544e-01 6.5174108743667603e-01 - 4.1789120435714722e-01 - <_> - - 1 0 258 1.3882719911634922e-02 -1 -2 259 - 1.0493700392544270e-03 - - 6.7712038755416870e-01 4.1595110297203064e-01 - 5.6528919935226440e-01 - <_> - - 1 0 260 1.8215360119938850e-02 -1 -2 261 - -1.1334580369293690e-02 - - 7.6896011829376221e-01 2.8733238577842712e-01 - 4.9889329075813293e-01 - <_> - - 1 0 262 -4.1097560897469521e-03 -1 -2 263 - 4.2612891411408782e-04 - - 5.4630082845687866e-01 3.6312350630760193e-01 - 5.5125522613525391e-01 - <_> - - 1 0 264 6.0301548801362514e-03 -1 -2 265 - 3.3587709185667336e-04 - - 1.1437670141458511e-01 2.8910788893699646e-01 - 5.4473417997360229e-01 - <_> - - 1 0 266 6.2279507983475924e-04 -1 -2 267 - -2.5837119668722153e-02 - - 3.0234318971633911e-01 2.1670059859752655e-01 - 5.2781528234481812e-01 - <_> - - 1 0 268 2.1774910390377045e-02 -1 -2 269 - 1.7682299949228764e-03 - - 3.2548341155052185e-01 5.2630507946014404e-01 - 7.5263291597366333e-01 - <_> - - 0 1 270 -1.3793810270726681e-02 -1 -2 271 - -5.0852829590439796e-03 - - 7.4103301763534546e-01 6.8366098403930664e-01 - 4.5790711045265198e-01 - <_> - - 1 0 272 6.1795017682015896e-03 -1 -2 273 - 1.0030319914221764e-02 - - 7.4499362707138062e-01 4.8607799410820007e-01 - 2.3614570498466492e-01 - <_> - - 0 1 274 -6.4201927743852139e-03 -1 -2 275 - -5.6961281225085258e-03 - - 1.4673270285129547e-01 2.3478199541568756e-01 - 5.3233772516250610e-01 - <_> - - 0 1 276 -7.1498160250484943e-03 -1 -2 277 - 2.4450740311294794e-03 - - 1.4770570397377014e-01 3.4985339641571045e-01 - 5.8035618066787720e-01 - <_> - - 1 0 278 -3.7503410130739212e-02 -1 -2 279 - 4.7799441381357610e-04 - - 5.2595508098602295e-01 4.3628829717636108e-01 - 6.2089228630065918e-01 - <_> - - 0 1 280 -7.0806080475449562e-03 -1 -2 281 - 3.2818000763654709e-02 - - 2.0394609868526459e-01 5.1983588933944702e-01 - 1.3711960613727570e-01 - <_> - - 1 0 282 6.5188988810405135e-04 -1 -2 283 - 4.6485587954521179e-03 - - 6.3234299421310425e-01 4.7201630473136902e-01 - 6.5670871734619141e-01 - <_> - - 0 1 284 -1.9827929791063070e-03 -1 -2 285 - -1.6011310508474708e-03 - - 6.0530602931976318e-01 5.0905191898345947e-01 - 3.1169331073760986e-01 - <_> - - 0 1 286 -3.0539939180016518e-03 -1 -2 287 - 4.3212040327489376e-04 - - 3.4298041462898254e-01 3.8384029269218445e-01 - 5.7755982875823975e-01 - <_> - - 0 1 288 -2.7452120557427406e-02 -1 -2 289 - 9.3099439982324839e-04 - - 2.1434690058231354e-01 5.9529662132263184e-01 - 3.7601581215858459e-01 - <_> - - 0 1 290 6.7144189961254597e-03 -1 -2 291 - -3.3701690845191479e-03 - - 5.6926268339157104e-01 5.7843041419982910e-01 - 3.9742821455001831e-01 - <_> - - 0 1 292 -1.8903959542512894e-02 -1 -2 293 - -6.5850871615111828e-03 - - 1.8188929557800293e-01 6.8491101264953613e-01 - 4.3515840172767639e-01 - <_> - - 1 0 294 5.8810501359403133e-03 -1 -2 295 - 8.0092082498595119e-04 - - 2.7266609668731689e-01 4.2364311218261719e-01 - 5.8446758985519409e-01 - <_> - - 1 0 296 1.8510579830035567e-03 -1 -2 297 - 6.3273650594055653e-03 - - 3.3713209629058838e-01 5.2702218294143677e-01 - 8.0536508560180664e-01 - <_> - - 0 1 298 -3.3820930402725935e-03 -1 -2 299 - -1.9292969955131412e-03 - - 2.8660181164741516e-01 5.8889460563659668e-01 - 3.8957870006561279e-01 - <_> - - 1 0 300 1.4995220117270947e-02 -1 -2 301 - -2.6330750435590744e-02 - - 2.1778169274330139e-01 1.7753170430660248e-01 - 5.6714701652526855e-01 - <_> - - 1 0 302 -4.1734222322702408e-03 -1 -2 303 - 2.7268350124359131e-02 - - 4.6529620885848999e-01 4.7683110833168030e-01 - 5.6952387094497681e-01 - <_> - - 1 0 304 9.8880263976752758e-04 -1 -2 305 - -1.0528849670663476e-03 - - 3.3974018692970276e-01 6.2500411272048950e-01 - 4.2884120345115662e-01 - <_> - - 0 1 306 5.2288072183728218e-03 -1 -2 307 - 3.0395459383726120e-02 - - 5.3477621078491211e-01 4.1155189275741577e-01 - 5.6607538461685181e-01 - <_> - - 0 1 308 -7.9113930463790894e-02 -1 -2 309 - 1.8231669440865517e-02 - - 7.8813230991363525e-01 3.6043399572372437e-01 - 5.5695050954818726e-01 - <_> - - 0 1 310 5.2288072183728218e-03 -1 -2 311 - 4.3922828626818955e-04 - - 5.4166442155838013e-01 5.5071568489074707e-01 - 3.8822770118713379e-01 - <_> - - 0 1 312 -8.6501962505280972e-04 -1 -2 313 - 1.0326979681849480e-03 - - 3.1858509778976440e-01 5.5783641338348389e-01 - 3.2192459702491760e-01 - <_> - - 0 1 314 -7.2997747920453548e-03 -1 -2 315 - -9.3629042385146022e-04 - - 7.0732331275939941e-01 5.5580157041549683e-01 - 4.6138420701026917e-01 - <_> - - 0 1 316 -6.0483231209218502e-03 -1 -2 317 - 6.7529221996665001e-03 - - 6.8692898750305176e-01 4.8703178763389587e-01 - 2.6503708958625793e-01 - <_> - - 0 1 318 5.3078029304742813e-02 -1 -2 319 - -1.0225810110569000e-03 - - 5.2815151214599609e-01 6.0858821868896484e-01 - 4.3048679828643799e-01 - <_> - - 1 0 320 3.1270649284124374e-02 -1 -2 321 - -6.3522169366478920e-03 - - 5.4458320140838623e-01 5.3283357620239258e-01 - 2.3643240332603455e-01 - <_> - 45 - 2.1578119277954102e+01 - - <_> - - 1 0 322 -6.2215630896389484e-03 -1 -2 323 - 2.1097389981150627e-03 - - 2.6255810260772705e-01 1.5649929642677307e-01 - 6.7928832769393921e-01 - <_> - - 0 1 324 1.0845859535038471e-02 -1 -2 325 - 6.4230401767417789e-04 - - 3.4858089685440063e-01 3.6982551217079163e-01 - 5.9216582775115967e-01 - <_> - - 1 0 326 7.3311722371727228e-04 -1 -2 327 - 1.0134200565516949e-03 - - 3.0070841312408447e-01 3.6249229311943054e-01 - 7.0724260807037354e-01 - <_> - - 0 1 328 1.1093559674918652e-02 -1 -2 329 - -7.9127531498670578e-03 - - 4.4167020916938782e-01 3.0287081003189087e-01 - 5.4173761606216431e-01 - <_> - - 0 1 330 1.2905309908092022e-02 -1 -2 331 - -4.2430912144482136e-03 - - 4.3745040893554688e-01 4.4015899300575256e-01 - 7.5651907920837402e-01 - <_> - - 0 1 332 -2.1304309484548867e-04 -1 -2 333 - -2.2308640182018280e-03 - - 2.3107869923114777e-01 3.5681959986686707e-01 - 5.7499992847442627e-01 - <_> - - 0 1 334 2.6400520000606775e-03 -1 -2 335 - 7.5101032853126526e-02 - - 3.5936889052391052e-01 6.3635677099227905e-01 - 2.3270289599895477e-01 - <_> - - 0 1 336 -7.7012968249619007e-03 -1 -2 337 - 1.5588370151817799e-03 - - 7.0746237039566040e-01 5.7002371549606323e-01 - 3.5904508829116821e-01 - <_> - - 0 1 338 -4.7687938786111772e-04 -1 -2 339 - 8.4234727546572685e-04 - - 2.8054410219192505e-01 4.1254189610481262e-01 - 6.1779958009719849e-01 - <_> - - 1 0 340 -1.2825109995901585e-02 -1 -2 341 - -6.5156567143276334e-04 - - 5.4030781984329224e-01 5.6336438655853271e-01 - 3.3565390110015869e-01 - <_> - - 0 1 342 -1.2006159871816635e-02 -1 -2 343 - 1.3213419588282704e-03 - - 7.1095108985900879e-01 4.9038508534431458e-01 - 2.8245830535888672e-01 - <_> - - 0 1 344 -2.0307440310716629e-02 -1 -2 345 - 4.0180929936468601e-03 - - 1.8913699686527252e-01 5.3779661655426025e-01 - 3.1194949150085449e-01 - <_> - - 1 0 346 4.5315311290323734e-03 -1 -2 347 - -4.4381739571690559e-03 - - 7.2067582607269287e-01 1.8546679615974426e-01 - 4.9817329645156860e-01 - <_> - - 1 0 348 1.5692010056227446e-03 -1 -2 349 - -4.9516442231833935e-03 - - 2.6382741332054138e-01 6.8710672855377197e-01 - 4.7146868705749512e-01 - <_> - - 0 1 350 -2.7429679408669472e-02 -1 -2 351 - 1.4181969454512000e-03 - - 1.5482850372791290e-01 4.3768429756164551e-01 - 6.3273680210113525e-01 - <_> - - 0 1 352 -1.3078940100967884e-02 -1 -2 353 - -3.5092779435217381e-03 - - 3.1668141484260559e-01 6.1997437477111816e-01 - 4.3796870112419128e-01 - <_> - - 1 0 354 1.8920730799436569e-02 -1 -2 355 - 2.1683350205421448e-03 - - 1.4707140624523163e-01 5.8094590902328491e-01 - 3.4319490194320679e-01 - <_> - - 0 1 356 1.6401590546593070e-03 -1 -2 357 - 1.4005920093040913e-04 - - 3.9594578742980957e-01 3.2400250434875488e-01 - 5.6466472148895264e-01 - <_> - - 1 0 358 -3.3137591090053320e-03 -1 -2 359 - -2.9459029901772738e-03 - - 4.2745280265808105e-01 3.3416679501533508e-01 - 6.6279602050781250e-01 - <_> - - 0 1 360 1.3612229668069631e-04 -1 -2 361 - 6.0512032359838486e-04 - - 4.0469279885292053e-01 5.4840582609176636e-01 - 3.5699409246444702e-01 - <_> - - 0 1 362 -1.7513990402221680e-02 -1 -2 363 - -1.8735030665993690e-02 - - 1.8241509795188904e-01 7.9718202352523804e-01 - 5.0685691833496094e-01 - <_> - - 1 0 364 1.2065649963915348e-02 -1 -2 365 - -2.6544178836047649e-03 - - 2.1670070290565491e-01 6.5841788053512573e-01 - 4.6282431483268738e-01 - <_> - - 1 0 366 1.4501289697363973e-03 -1 -2 367 - 1.0954019613564014e-02 - - 2.0902520418167114e-01 5.1123052835464478e-01 - 7.7845758199691772e-01 - <_> - - 0 1 368 1.5771709382534027e-02 -1 -2 369 - -1.4252689667046070e-02 - - 5.1323592662811279e-01 1.7424149811267853e-01 - 5.2671480178833008e-01 - <_> - - 0 1 370 3.0411860279855318e-05 -1 -2 371 - 2.3486299440264702e-02 - - 3.4184479713439941e-01 5.6312650442123413e-01 - 2.0063939690589905e-01 - <_> - - 1 0 372 5.2205449901521206e-03 -1 -2 373 - -2.5812430307269096e-02 - - 6.2496489286422729e-01 3.2032281160354614e-01 - 5.1993298530578613e-01 - <_> - - 0 1 374 -1.9526650430634618e-03 -1 -2 375 - -8.1470049917697906e-03 - - 6.1407059431076050e-01 6.5928959846496582e-01 - 3.7111249566078186e-01 - <_> - - 1 0 376 3.2962448894977570e-03 -1 -2 377 - -1.3961310032755136e-03 - - 2.9521119594573975e-01 3.3208039402961731e-01 - 5.5284148454666138e-01 - <_> - - 0 1 378 -4.1055441834032536e-03 -1 -2 379 - -1.0888779535889626e-02 - - 1.7105500400066376e-01 3.3594349026679993e-01 - 5.6749051809310913e-01 - <_> - - 1 0 380 -7.6768421567976475e-03 -1 -2 381 - -9.7729787230491638e-03 - - 4.7732418775558472e-01 8.0810451507568359e-01 - 4.8458281159400940e-01 - <_> - - 1 0 382 6.0439710505306721e-03 -1 -2 383 - -4.6134641161188483e-04 - - 6.7840021848678589e-01 5.5146390199661255e-01 - 3.6423599720001221e-01 - <_> - - 1 0 384 5.7992361485958099e-02 -1 -2 385 - 5.9384980704635382e-04 - - 1.2544350326061249e-01 4.4248789548873901e-01 - 5.7284617424011230e-01 - <_> - - 0 1 386 -6.2353480607271194e-03 -1 -2 387 - -1.2784929946064949e-02 - - 2.8050419688224792e-01 1.9509120285511017e-01 - 5.6529247760772705e-01 - <_> - - 1 0 388 4.1973669431172311e-04 -1 -2 389 - 8.0646801507100463e-04 - - 6.1664837598800659e-01 4.5265799760818481e-01 - 5.9444868564605713e-01 - <_> - - 1 0 390 -1.6339010326191783e-03 -1 -2 391 - -4.8299999907612801e-03 - - 4.0869420766830444e-01 2.7935269474983215e-01 - 6.4449352025985718e-01 - <_> - - 1 0 392 -6.3992068171501160e-03 -1 -2 393 - 1.0819199681282043e-01 - - 5.6716561317443848e-01 5.3118121623992920e-01 - 2.6143568754196167e-01 - <_> - - 1 0 394 6.5056560561060905e-04 -1 -2 395 - 2.0611250773072243e-02 - - 2.9967740178108215e-01 4.4899430871009827e-01 - 6.8882799148559570e-01 - <_> - - 1 0 396 -2.5129050016403198e-02 -1 -2 397 - 1.7922939732670784e-03 - - 5.1968640089035034e-01 3.4669959545135498e-01 - 5.5335879325866699e-01 - <_> - - 1 0 398 1.5626220265403390e-03 -1 -2 399 - -6.1898730928078294e-04 - - 3.0814400315284729e-01 2.6938709616661072e-01 - 5.5444890260696411e-01 - <_> - - 0 1 400 4.8111421056091785e-03 -1 -2 401 - 2.2484229411929846e-03 - - 5.5878478288650513e-01 4.6721130609512329e-01 - 6.0908252000808716e-01 - <_> - - 0 1 402 -3.0147239565849304e-02 -1 -2 403 - 2.7548679709434509e-01 - - 9.0275919437408447e-01 4.7198349237442017e-01 - 2.1969200670719147e-01 - <_> - - 1 0 404 3.6894630175083876e-03 -1 -2 405 - 7.2957701049745083e-03 - - 6.2730091810226440e-01 4.8392179608345032e-01 - 6.9090622663497925e-01 - <_> - - 0 1 406 -5.6211069226264954e-02 -1 -2 407 - -2.6478560175746679e-03 - - 1.7384879291057587e-01 6.3041448593139648e-01 - 4.4743019342422485e-01 - <_> - - 1 0 408 -1.4534000074490905e-03 -1 -2 409 - 2.8540920466184616e-03 - - 5.3025382757186890e-01 5.3383970260620117e-01 - 3.7968829274177551e-01 - <_> - - 1 0 410 5.8243022067472339e-04 -1 -2 411 - 9.2509482055902481e-04 - - 3.2698369026184082e-01 4.5548120141029358e-01 - 6.3583481311798096e-01 - <_> - 47 - 2.2585290908813477e+01 - - <_> - - 0 1 412 1.9806440919637680e-02 -1 -2 413 - 7.0395611692219973e-04 - - 2.8097251057624817e-01 3.1198260188102722e-01 - 7.0903062820434570e-01 - <_> - - 0 1 414 2.5563780218362808e-03 -1 -2 415 - 1.0824160417541862e-03 - - 2.9819479584693909e-01 3.0205601453781128e-01 - 5.8088111877441406e-01 - <_> - - 1 0 416 -9.2893769033253193e-04 -1 -2 417 - -1.8009729683399200e-02 - - 3.7381029129028320e-01 2.1631260216236115e-01 - 6.6192537546157837e-01 - <_> - - 1 0 418 2.3500190582126379e-03 -1 -2 419 - 8.1822491483762860e-04 - - 2.9104039072990417e-01 5.5786228179931641e-01 - 3.3666279911994934e-01 - <_> - - 0 1 420 6.2095321482047439e-04 -1 -2 421 - 9.6780969761312008e-04 - - 4.0724259614944458e-01 6.8595957756042480e-01 - 3.1054618954658508e-01 - <_> - - 1 0 422 4.8000211245380342e-04 -1 -2 423 - 9.0538640506565571e-05 - - 3.3373329043388367e-01 3.3709588646888733e-01 - 5.4512107372283936e-01 - <_> - - 0 1 424 -4.3914798647165298e-02 -1 -2 425 - -5.6501338258385658e-03 - - 2.6256701350212097e-01 6.0504627227783203e-01 - 3.2324150204658508e-01 - <_> - - 1 0 426 3.8661491125822067e-03 -1 -2 427 - -6.3069426687434316e-05 - - 3.2626131176948547e-01 5.8173078298568726e-01 - 4.1643899679183960e-01 - <_> - - 1 0 428 5.2533738315105438e-02 -1 -2 429 - 1.3818660518154502e-03 - - 7.0953989028930664e-01 5.2928757667541504e-01 - 2.5413888692855835e-01 - <_> - - 1 0 430 -8.9264067355543375e-04 -1 -2 431 - 8.5579507052898407e-02 - - 4.0853410959243774e-01 5.2632361650466919e-01 - 3.0032029747962952e-01 - <_> - - 1 0 432 -1.8343339615967125e-04 -1 -2 433 - -9.7924815490841866e-03 - - 4.0292051434516907e-01 3.5213199257850647e-01 - 6.6640049219131470e-01 - <_> - - 0 1 434 1.4428620226681232e-02 -1 -2 435 - -4.5687001198530197e-02 - - 4.5935660600662231e-01 1.4747560024261475e-01 - 5.1786321401596069e-01 - <_> - - 0 1 436 -2.5763090234249830e-03 -1 -2 437 - -3.8301859050989151e-02 - - 1.8372780084609985e-01 8.0826580524444580e-01 - 5.1666879653930664e-01 - <_> - - 0 1 438 2.8978290501981974e-03 -1 -2 439 - -2.5165060069411993e-03 - - 4.7980138659477234e-01 3.3462959527969360e-01 - 5.4444491863250732e-01 - <_> - - 0 1 440 5.6281982688233256e-04 -1 -2 441 - 3.6684391088783741e-03 - - 3.5890269279479980e-01 5.9831297397613525e-01 - 2.9839640855789185e-01 - <_> - - 1 0 442 2.1319789811968803e-03 -1 -2 443 - 7.6037310063838959e-03 - - 6.1632239818572998e-01 5.2171301841735840e-01 - 2.0541590452194214e-01 - <_> - - 1 0 444 -1.1668079969240353e-04 -1 -2 445 - 3.1659509986639023e-03 - - 3.4466689825057983e-01 5.5974847078323364e-01 - 2.6737868785858154e-01 - <_> - - 0 1 446 -2.2569499909877777e-02 -1 -2 447 - 2.7129601221531630e-04 - - 6.9002681970596313e-01 4.4866389036178589e-01 - 5.5087852478027344e-01 - <_> - - 0 1 448 -1.5434459783136845e-02 -1 -2 449 - -8.4861656650900841e-03 - - 2.0483230054378510e-01 1.2549529969692230e-01 - 5.0603562593460083e-01 - <_> - - 0 1 450 -1.1807470023632050e-01 -1 -2 451 - -1.2300079688429832e-03 - - 6.7633062601089478e-02 5.6607007980346680e-01 - 4.2922011017799377e-01 - <_> - - 0 1 452 -7.0290351286530495e-03 -1 -2 453 - 8.9325206354260445e-03 - - 7.1364039182662964e-01 4.3388760089874268e-01 - 7.0608752965927124e-01 - <_> - - 1 0 454 -4.7735981643199921e-02 -1 -2 455 - -4.4155579060316086e-02 - - 5.2686852216720581e-01 2.5805801153182983e-01 - 5.4069608449935913e-01 - <_> - - 0 1 456 -2.5983480736613274e-02 -1 -2 457 - -4.7885831445455551e-03 - - 1.9050540030002594e-01 2.5518929958343506e-01 - 5.3390771150588989e-01 - <_> - - 0 1 458 6.7423451691865921e-03 -1 -2 459 - 1.1654750443994999e-02 - - 4.6933099627494812e-01 5.2619642019271851e-01 - 3.1454348564147949e-01 - <_> - - 0 1 460 -5.6982729583978653e-03 -1 -2 461 - -7.2983349673449993e-03 - - 1.7568530142307281e-01 7.7747297286987305e-01 - 5.1242929697036743e-01 - <_> - - 0 1 462 7.9091778025031090e-03 -1 -2 463 - -1.5874979726504534e-04 - - 5.2845597267150879e-01 3.8878020644187927e-01 - 5.5011737346649170e-01 - <_> - - 0 1 464 -6.2235877849161625e-03 -1 -2 465 - 1.3308860361576080e-03 - - 2.4898290634155273e-01 4.2621460556983948e-01 - 5.9350621700286865e-01 - <_> - - 1 0 466 5.2055278792977333e-03 -1 -2 467 - 1.4065169729292393e-02 - - 2.5452229380607605e-01 4.8519900441169739e-01 - 7.0214188098907471e-01 - <_> - - 0 1 468 -6.7384149879217148e-03 -1 -2 469 - 3.3406780567020178e-03 - - 7.1432709693908691e-01 5.1757252216339111e-01 - 2.8086438775062561e-01 - <_> - - 1 0 470 -1.1880699545145035e-02 -1 -2 471 - 1.4226379571482539e-03 - - 5.1732218265533447e-01 4.5028659701347351e-01 - 5.7956951856613159e-01 - <_> - - 1 0 472 2.9858129564672709e-03 -1 -2 473 - -2.0481580868363380e-03 - - 1.9151160120964050e-01 6.5024322271347046e-01 - 4.5593151450157166e-01 - <_> - - 0 1 474 1.7122729914262891e-03 -1 -2 475 - -1.6980869695544243e-02 - - 5.3762471675872803e-01 7.0562332868576050e-01 - 4.9146059155464172e-01 - <_> - - 0 1 476 -1.1290470138192177e-03 -1 -2 477 - 2.8620059601962566e-03 - - 2.6787060499191284e-01 4.4108539819717407e-01 - 6.3683199882507324e-01 - <_> - - 0 1 478 -3.8065758999437094e-03 -1 -2 479 - 5.9090270660817623e-03 - - 2.7635639905929565e-01 4.8673018813133240e-01 - 6.7287760972976685e-01 - <_> - - 0 1 480 1.1004370171576738e-03 -1 -2 481 - -2.3396299220621586e-03 - - 4.0705141425132751e-01 2.6049488782882690e-01 - 6.1548602581024170e-01 - <_> - - 0 1 482 -3.6068160552531481e-03 -1 -2 483 - 4.0831189602613449e-02 - - 5.7319998741149902e-01 4.9733769893646240e-01 - 7.3870068788528442e-01 - <_> - - 0 1 484 -7.1082250215113163e-03 -1 -2 485 - -9.3759730225428939e-04 - - 6.9847512245178223e-01 2.6911678910255432e-01 - 4.7417798638343811e-01 - <_> - - 0 1 486 -1.6740820137783885e-03 -1 -2 487 - 8.8287703692913055e-02 - - 3.5510140657424927e-01 5.2446138858795166e-01 - 2.0966500043869019e-01 - <_> - - 0 1 488 8.2009629113599658e-04 -1 -2 489 - -7.6624617213383317e-04 - - 4.1310968995094299e-01 4.6202930808067322e-01 - 6.7754101753234863e-01 - <_> - - 1 0 490 6.5769668435677886e-04 -1 -2 491 - -2.1304790861904621e-03 - - 5.6282752752304077e-01 5.5768597126007080e-01 - 4.5776501297950745e-01 - <_> - - 1 0 492 -3.7317050737328827e-04 -1 -2 493 - -1.1172230355441570e-02 - - 4.9592560529708862e-01 5.6256359815597534e-01 - 2.0471079647541046e-01 - <_> - - 1 0 494 4.3435219675302505e-02 -1 -2 495 - 9.6736161503940821e-04 - - 2.2421480715274811e-01 4.5333439111709595e-01 - 6.1999320983886719e-01 - <_> - - 0 1 496 -3.1452889088541269e-03 -1 -2 497 - 1.5233129961416125e-03 - - 6.6627562046051025e-01 5.0079882144927979e-01 - 2.3849929869174957e-01 - <_> - - 1 0 498 2.0854279864579439e-03 -1 -2 499 - 3.6098200827836990e-02 - - 3.7535008788108826e-01 5.1771712303161621e-01 - 1.6344930231571198e-01 - <_> - - 1 0 500 1.6179570229724050e-03 -1 -2 501 - -6.2132300809025764e-04 - - 2.5873818993568420e-01 6.2995338439941406e-01 - 4.6587899327278137e-01 - <_> - - 1 0 502 7.1878539165481925e-04 -1 -2 503 - -3.9339520037174225e-02 - - 3.3540761470794678e-01 2.1541289985179901e-01 - 5.2357137203216553e-01 - <_> - - 0 1 504 -1.0988829890266061e-03 -1 -2 505 - 2.1191420964896679e-03 - - 6.4688968658447266e-01 2.8930890560150146e-01 - 5.2548158168792725e-01 - <_> - 53 - 2.5609300613403320e+01 - - <_> - - 0 1 506 5.2359891124069691e-03 -1 -2 507 - -2.2169889416545630e-03 - - 3.2997110486030579e-01 7.0415931940078735e-01 - 3.2354658842086792e-01 - <_> - - 1 0 508 -8.2303592935204506e-03 -1 -2 509 - -8.2303592935204506e-03 - - 4.9611708521842957e-01 7.1280431747436523e-01 - 4.9611708521842957e-01 - <_> - - 0 1 510 4.5343261444941163e-04 -1 -2 511 - -4.1777061414904892e-04 - - 3.2084721326828003e-01 6.6139167547225952e-01 - 3.5513329505920410e-01 - <_> - - 0 1 512 2.7823769487440586e-03 -1 -2 513 - -6.0361868236213923e-05 - - 3.7101349234580994e-01 5.7463937997817993e-01 - 3.8948801159858704e-01 - <_> - - 1 0 514 3.5061789676547050e-03 -1 -2 515 - 1.7013119941111654e-04 - - 3.0541029572486877e-01 2.8855779767036438e-01 - 6.4877450466156006e-01 - <_> - - 1 0 516 -2.3378930054605007e-03 -1 -2 517 - -2.1369170863181353e-03 - - 3.1744310259819031e-01 3.8209199905395508e-01 - 5.2328932285308838e-01 - <_> - - 0 1 518 1.0250400518998504e-03 -1 -2 519 - -4.4726220949087292e-05 - - 3.6227950453758240e-01 6.5389591455459595e-01 - 4.0036809444427490e-01 - <_> - - 1 0 520 5.7102291611954570e-04 -1 -2 521 - 5.7743012439459562e-04 - - 3.8931730389595032e-01 5.6145328283309937e-01 - 3.6876440048217773e-01 - <_> - - 1 0 522 7.9692091094329953e-04 -1 -2 523 - 3.5945948911830783e-04 - - 6.4430278539657593e-01 3.3808529376983643e-01 - 5.8246481418609619e-01 - <_> - - 1 0 524 4.3973900028504431e-04 -1 -2 525 - -8.9061429025605321e-04 - - 3.9387670159339905e-01 3.4279710054397583e-01 - 5.5156987905502319e-01 - <_> - - 1 0 526 5.4110242053866386e-03 -1 -2 527 - -8.5764907998964190e-04 - - 3.8035380840301514e-01 6.4395052194595337e-01 - 4.1683459281921387e-01 - <_> - - 0 1 528 -2.2000649943947792e-02 -1 -2 529 - -7.8731682151556015e-03 - - 6.6546010971069336e-01 4.1827228665351868e-01 - 5.6047242879867554e-01 - <_> - - 0 1 530 -2.7444459497928619e-02 -1 -2 531 - 1.9792269449681044e-03 - - 6.5868628025054932e-01 3.2449120283126831e-01 - 4.8828700184822083e-01 - <_> - - 0 1 532 -5.6783691979944706e-03 -1 -2 533 - 1.5057219570735469e-05 - - 2.2290790081024170e-01 4.1072851419448853e-01 - 5.7475912570953369e-01 - <_> - - 0 1 534 -5.4136710241436958e-03 -1 -2 535 - 5.3679239936172962e-03 - - 2.0657970011234283e-01 4.9264231324195862e-01 - 7.1394848823547363e-01 - <_> - - 0 1 536 -3.1426660716533661e-03 -1 -2 537 - 1.0907390154898167e-02 - - 6.7800867557525635e-01 5.2149301767349243e-01 - 1.1439959704875946e-01 - <_> - - 1 0 538 5.8436761610209942e-03 -1 -2 539 - 9.0507230197545141e-05 - - 1.9375260174274445e-01 3.8125771284103394e-01 - 5.5141878128051758e-01 - <_> - - 0 1 540 -1.6345789656043053e-02 -1 -2 541 - 1.5987500082701445e-03 - - 2.4740239977836609e-01 4.8177829384803772e-01 - 5.9230798482894897e-01 - <_> - - 0 1 542 -4.0257978253066540e-03 -1 -2 543 - -6.7750471644103527e-03 - - 7.5082087516784668e-01 2.8798109292984009e-01 - 5.1996952295303345e-01 - <_> - - 0 1 544 -3.2470689620822668e-03 -1 -2 545 - 1.5409620245918632e-03 - - 3.0449101328849792e-01 4.0634828805923462e-01 - 5.6765627861022949e-01 - <_> - - 0 1 546 -1.2858119793236256e-02 -1 -2 547 - -1.4824670506641269e-04 - - 9.6717558801174164e-02 4.5378330349922180e-01 - 6.1153751611709595e-01 - <_> - - 1 0 548 -9.0210810303688049e-03 -1 -2 549 - -2.8795029968023300e-02 - - 4.8077508807182312e-01 3.4037950634956360e-01 - 5.2555292844772339e-01 - <_> - - 1 0 550 9.0210810303688049e-03 -1 -2 551 - 7.4121179059147835e-03 - - 7.5058358907699585e-01 5.4554468393325806e-01 - 3.2260689139366150e-01 - <_> - - 0 1 552 -3.7217529024928808e-03 -1 -2 553 - 1.9865889847278595e-01 - - 2.3118489980697632e-01 5.2710479497909546e-01 - 1.4699299633502960e-01 - <_> - - 0 1 554 1.5208719560177997e-05 -1 -2 555 - -3.9089918136596680e-03 - - 3.6781388521194458e-01 7.1319299936294556e-01 - 4.9938669800758362e-01 - <_> - - 0 1 556 2.5106288958340883e-03 -1 -2 557 - 2.3921660613268614e-04 - - 5.3120541572570801e-01 4.6893781423568726e-01 - 5.7140219211578369e-01 - <_> - - 1 0 558 6.9443131797015667e-03 -1 -2 559 - 1.2065629707649350e-03 - - 6.9487977027893066e-01 4.0045049786567688e-01 - 5.8748817443847656e-01 - <_> - - 0 1 560 2.5106288958340883e-03 -1 -2 561 - 1.7514040227979422e-03 - - 5.3295719623565674e-01 5.5458492040634155e-01 - 3.4495818614959717e-01 - <_> - - 0 1 562 -4.1978210210800171e-03 -1 -2 563 - 1.3092850567772985e-03 - - 1.2171830236911774e-01 5.3750497102737427e-01 - 3.4156250953674316e-01 - <_> - - 0 1 564 6.7396182566881180e-04 -1 -2 565 - -1.0530710220336914e-02 - - 4.1951790452003479e-01 3.4607538580894470e-01 - 5.1558601856231689e-01 - <_> - - 0 1 566 -4.0672299265861511e-01 -1 -2 567 - -2.6314549148082733e-02 - - 5.8065678924322128e-02 1.4734490215778351e-01 - 5.5593782663345337e-01 - <_> - - 1 0 568 2.2557149641215801e-03 -1 -2 569 - 1.2154860422015190e-02 - - 5.4777151346206665e-01 4.2077910900115967e-01 - 5.6218808889389038e-01 - <_> - - 0 1 570 -1.8436539918184280e-02 -1 -2 571 - 5.3676147945225239e-04 - - 6.4471471309661865e-01 2.7651271224021912e-01 - 4.8885959386825562e-01 - <_> - - 1 0 572 -2.6265541091561317e-03 -1 -2 573 - -5.1119807176291943e-04 - - 5.2646911144256592e-01 5.7853102684020996e-01 - 4.2911028861999512e-01 - <_> - - 1 0 574 4.1454841266386211e-04 -1 -2 575 - -5.5028748465701938e-04 - - 3.4554108977317810e-01 6.0269188880920410e-01 - 4.1438931226730347e-01 - <_> - - 0 1 576 -1.0347720235586166e-03 -1 -2 577 - -3.3966631162911654e-03 - - 6.0952937602996826e-01 6.1082822084426880e-01 - 4.7077208757400513e-01 - <_> - - 1 0 578 3.1795909162610769e-03 -1 -2 579 - -1.6528950072824955e-04 - - 3.2443669438362122e-01 3.8307571411132812e-01 - 5.7343262434005737e-01 - <_> - - 1 0 580 8.3725210279226303e-03 -1 -2 581 - -2.5799809955060482e-03 - - 6.6109192371368408e-01 6.1393070220947266e-01 - 4.6861499547958374e-01 - <_> - - 1 0 582 9.0194388758391142e-04 -1 -2 583 - 3.6952210939489305e-04 - - 3.5200220346450806e-01 2.5787541270256042e-01 - 5.4672420024871826e-01 - <_> - - 0 1 584 9.9746137857437134e-04 -1 -2 585 - -3.6688039544969797e-03 - - 4.8201468586921692e-01 5.7101500034332275e-01 - 4.8319110274314880e-01 - <_> - - 0 1 586 -8.9501030743122101e-04 -1 -2 587 - 5.1904921419918537e-03 - - 6.1336791515350342e-01 4.9285829067230225e-01 - 2.5813090801239014e-01 - <_> - - 0 1 588 4.2274440056644380e-04 -1 -2 589 - 8.5176713764667511e-03 - - 4.4711241126060486e-01 5.1610249280929565e-01 - 3.3165338635444641e-01 - <_> - - 0 1 590 -3.6623608320951462e-02 -1 -2 591 - -4.1103712283074856e-03 - - 9.2606216669082642e-02 8.5221147537231445e-01 - 5.1379078626632690e-01 - <_> - - 1 0 592 -6.6017331555485725e-03 -1 -2 593 - 2.5578640401363373e-02 - - 5.4590600728988647e-01 5.2193528413772583e-01 - 1.9271859526634216e-01 - <_> - - 1 0 594 1.1447439901530743e-02 -1 -2 595 - 7.2427501436322927e-04 - - 1.9160020351409912e-01 5.2315711975097656e-01 - 3.5353401303291321e-01 - <_> - - 1 0 596 9.7127500921487808e-03 -1 -2 597 - -1.1337569914758205e-02 - - 6.4641010761260986e-01 7.3830378055572510e-01 - 4.9647438526153564e-01 - <_> - - 0 1 598 -8.1453882157802582e-03 -1 -2 599 - -8.5570756345987320e-03 - - 3.6117058992385864e-01 3.4219071269035339e-01 - 5.9435117244720459e-01 - <_> - - 0 1 600 2.2993308957666159e-03 -1 -2 601 - 3.8430930580943823e-03 - - 4.5501041412353516e-01 4.7168621420860291e-01 - 6.6561907529830933e-01 - <_> - - 1 0 602 -9.9116540513932705e-04 -1 -2 603 - 2.5496469810605049e-02 - - 4.5927169919013977e-01 6.5634012222290039e-01 - 1.2588350474834442e-01 - <_> - - 1 0 604 -1.5748359262943268e-02 -1 -2 605 - -1.8046120181679726e-02 - - 5.2395021915435791e-01 8.0158519744873047e-01 - 5.0079578161239624e-01 - <_> - - 1 0 606 1.0323390364646912e-02 -1 -2 607 - 1.6452240524813533e-03 - - 2.2748200595378876e-01 4.3519461154937744e-01 - 5.8676278591156006e-01 - <_> - - 0 1 608 1.5881149098277092e-02 -1 -2 609 - 1.0586519725620747e-02 - - 4.4650518894195557e-01 4.5444580912590027e-01 - 5.7071107625961304e-01 - <_> - - 0 1 610 -2.1531689912080765e-02 -1 -2 611 - 5.2480469457805157e-03 - - 6.5276437997817993e-01 3.4447279572486877e-01 - 5.3246361017227173e-01 - <_> - 67 - 3.2647129058837891e+01 - - <_> - - 0 1 612 1.8219340126961470e-03 -1 -2 613 - 8.1313941627740860e-03 - - 3.1087881326675415e-01 3.1332370638847351e-01 - 6.6458672285079956e-01 - <_> - - 0 1 614 1.7055979697033763e-03 -1 -2 615 - -7.4483548814896494e-05 - - 2.6401311159133911e-01 5.6472051143646240e-01 - 3.4853729605674744e-01 - <_> - - 1 0 616 3.8342390325851738e-04 -1 -2 617 - 3.1868910882622004e-03 - - 3.1406548619270325e-01 6.4891988039016724e-01 - 3.8877290487289429e-01 - <_> - - 1 0 618 1.6044320166110992e-01 -1 -2 619 - -6.7285560071468353e-03 - - 7.2165298461914062e-01 1.6531379520893097e-01 - 5.1398259401321411e-01 - <_> - - 0 1 620 7.2638481469766703e-06 -1 -2 621 - 5.5551197146996856e-04 - - 3.1406199932098389e-01 5.9936988353729248e-01 - 3.3173981308937073e-01 - <_> - - 0 1 622 -1.0822320356965065e-02 -1 -2 623 - -4.5834020711481571e-03 - - 2.6529380679130554e-01 1.8495689332485199e-01 - 5.3139579296112061e-01 - <_> - - 1 0 624 -3.0205070506781340e-03 -1 -2 625 - 7.7864617109298706e-02 - - 4.0400999784469604e-01 6.1581897735595703e-01 - 1.7864869534969330e-01 - <_> - - 0 1 626 2.6494380086660385e-02 -1 -2 627 - 3.6912109702825546e-02 - - 4.5110899209976196e-01 4.5282199978828430e-01 - 5.9722828865051270e-01 - <_> - - 1 0 628 5.7857790961861610e-03 -1 -2 629 - 9.3849771656095982e-04 - - 2.5338920950889587e-01 3.4104120731353760e-01 - 5.9236437082290649e-01 - <_> - - 0 1 630 -1.1003199964761734e-02 -1 -2 631 - -1.1737640015780926e-03 - - 6.9580441713333130e-01 3.8510841131210327e-01 - 5.4081892967224121e-01 - <_> - - 0 1 632 -3.6596669815480709e-03 -1 -2 633 - -2.4822750128805637e-03 - - 2.0093089342117310e-01 6.2953931093215942e-01 - 4.3950408697128296e-01 - <_> - - 0 1 634 -4.4606071896851063e-03 -1 -2 635 - -3.5969649907201529e-03 - - 2.4052999913692474e-01 5.4501742124557495e-01 - 3.7823578715324402e-01 - <_> - - 0 1 636 -3.6222559865564108e-03 -1 -2 637 - 1.2059339787811041e-03 - - 3.0338969826698303e-01 4.6337789297103882e-01 - 6.3359522819519043e-01 - <_> - - 1 0 638 4.3124938383698463e-03 -1 -2 639 - -4.4961250387132168e-03 - - 6.5988260507583618e-01 6.6216969490051270e-01 - 4.7552469372749329e-01 - <_> - - 0 1 640 -1.3860689941793680e-03 -1 -2 641 - -5.1588460337370634e-04 - - 2.8012010455131531e-01 3.8294890522956848e-01 - 5.6236267089843750e-01 - <_> - - 0 1 642 7.0330002927221358e-05 -1 -2 643 - -2.0976549421902746e-04 - - 4.5363429188728333e-01 5.6081390380859375e-01 - 4.2657798528671265e-01 - <_> - - 1 0 644 1.3642259873449802e-03 -1 -2 645 - 1.5483660390600562e-03 - - 2.6370918750762939e-01 4.1707509756088257e-01 - 5.9329879283905029e-01 - <_> - - 0 1 646 1.9179609417915344e-01 -1 -2 647 - -4.4776909053325653e-03 - - 5.2567642927169800e-01 6.6326218843460083e-01 - 4.8925888538360596e-01 - <_> - - 0 1 648 -1.2649179995059967e-01 -1 -2 649 - 6.5253327193204314e-05 - - 1.4997789263725281e-01 4.2333200573921204e-01 - 5.7560402154922485e-01 - <_> - - 0 1 650 4.1856421157717705e-03 -1 -2 651 - 2.7478230185806751e-04 - - 5.2888268232345581e-01 4.5240178704261780e-01 - 5.6041252613067627e-01 - <_> - - 0 1 652 -2.2906810045242310e-03 -1 -2 653 - 1.6744500026106834e-03 - - 5.5782741308212280e-01 3.3230578899383545e-01 - 5.5587881803512573e-01 - <_> - - 1 0 654 1.2349759927019477e-03 -1 -2 655 - -8.7158754467964172e-03 - - 3.6539471149444580e-01 1.9245339930057526e-01 - 5.3136497735977173e-01 - <_> - - 1 0 656 4.6613621525466442e-03 -1 -2 657 - -8.5815992206335068e-03 - - 2.0277309417724609e-01 7.6360601186752319e-01 - 5.1408261060714722e-01 - <_> - - 0 1 658 1.4352120459079742e-02 -1 -2 659 - -7.7948719263076782e-03 - - 5.2529758214950562e-01 2.6329371333122253e-01 - 5.3286892175674438e-01 - <_> - - 0 1 660 -3.4155680332332850e-03 -1 -2 661 - -4.2639090679585934e-03 - - 2.4160879850387573e-01 3.9365449547767639e-01 - 5.4787421226501465e-01 - <_> - - 0 1 662 8.7177697569131851e-03 -1 -2 663 - -3.2232629600912333e-03 - - 4.7881990671157837e-01 3.6316120624542236e-01 - 5.2883160114288330e-01 - <_> - - 0 1 664 -4.2188368737697601e-02 -1 -2 665 - 1.9875749945640564e-02 - - 6.9311392307281494e-01 4.5201000571250916e-01 - 6.8550550937652588e-01 - <_> - - 1 0 666 -3.1134510412812233e-02 -1 -2 667 - 5.7032387703657150e-03 - - 5.3004240989685059e-01 5.6068921089172363e-01 - 4.2306229472160339e-01 - <_> - - 1 0 668 5.2733682096004486e-03 -1 -2 669 - -3.1231069006025791e-03 - - 3.2472288608551025e-01 1.9856959581375122e-01 - 5.3498727083206177e-01 - <_> - - 0 1 670 4.6453849063254893e-04 -1 -2 671 - 3.0355889350175858e-02 - - 4.2075088620185852e-01 5.1534587144851685e-01 - 3.1181010603904724e-01 - <_> - - 0 1 672 -4.2992769740521908e-03 -1 -2 673 - 1.9509199773892760e-04 - - 3.2745069265365601e-01 5.9530782699584961e-01 - 4.2255210876464844e-01 - <_> - - 0 1 674 -7.7784480527043343e-03 -1 -2 675 - 1.6917599365115166e-02 - - 7.2111797332763672e-01 4.9365919828414917e-01 - 7.0302772521972656e-01 - <_> - - 0 1 676 -5.1948569715023041e-02 -1 -2 677 - -5.4751220159232616e-03 - - 1.4255349338054657e-01 6.0593318939208984e-01 - 4.3939951062202454e-01 - <_> - - 0 1 678 1.5210839592327829e-05 -1 -2 679 - 1.0235579684376717e-03 - - 4.4888499379158020e-01 4.2565500736236572e-01 - 5.7954382896423340e-01 - <_> - - 0 1 680 -1.0427719826111570e-04 -1 -2 681 - 8.7853781878948212e-03 - - 4.2460399866104126e-01 4.9580091238021851e-01 - 6.7594307661056519e-01 - <_> - - 0 1 682 3.4012699034065008e-03 -1 -2 683 - 5.8582378551363945e-04 - - 5.4234808683395386e-01 3.6365428566932678e-01 - 5.4643487930297852e-01 - <_> - - 0 1 684 -2.2973360028117895e-03 -1 -2 685 - -1.4330189675092697e-02 - - 2.5488188862800598e-01 6.5876567363739014e-01 - 4.5328021049499512e-01 - <_> - - 0 1 686 9.8565965890884399e-04 -1 -2 687 - -4.6640761196613312e-02 - - 3.8227710127830505e-01 3.0773219466209412e-01 - 5.2441328763961792e-01 - <_> - - 0 1 688 -1.1907300353050232e-01 -1 -2 689 - 1.9333280622959137e-02 - - 1.0338629782199860e-01 5.5547451972961426e-01 - 3.2213169336318970e-01 - <_> - - 0 1 690 3.1427849084138870e-02 -1 -2 691 - 2.0082130504306406e-04 - - 4.6823790669441223e-01 5.3730702400207520e-01 - 3.8006669282913208e-01 - <_> - - 0 1 692 -6.2584900297224522e-03 -1 -2 693 - 8.2861045375466347e-03 - - 1.7992070317268372e-01 5.0950688123703003e-01 - 7.5446051359176636e-01 - <_> - - 0 1 694 2.0529709290713072e-03 -1 -2 695 - 3.2524869311600924e-03 - - 5.6286448240280151e-01 4.8016890883445740e-01 - 5.8021020889282227e-01 - <_> - - 0 1 696 -3.1884901225566864e-02 -1 -2 697 - 1.8379340181127191e-03 - - 1.7427450418472290e-01 3.4665969014167786e-01 - 5.1071548461914062e-01 - <_> - - 1 0 698 -4.8512680223211646e-04 -1 -2 699 - -2.5407879147678614e-03 - - 5.3260862827301025e-01 6.3427752256393433e-01 - 4.9926930665969849e-01 - <_> - - 0 1 700 -5.1559060811996460e-03 -1 -2 701 - -4.4968750327825546e-02 - - 3.4334290027618408e-01 1.8681369721889496e-01 - 5.2154648303985596e-01 - <_> - - 1 0 702 5.8984281495213509e-03 -1 -2 703 - 3.2763120252639055e-03 - - 6.2293052673339844e-01 4.9357721209526062e-01 - 7.2179448604583740e-01 - <_> - - 1 0 704 -1.0161520185647532e-04 -1 -2 705 - -1.6290300118271261e-04 - - 5.0079762935638428e-01 6.0241490602493286e-01 - 2.3295080661773682e-01 - <_> - - 0 1 706 9.0541364625096321e-03 -1 -2 707 - 3.5398490726947784e-02 - - 4.5104169845581055e-01 5.1419967412948608e-01 - 2.8602918982505798e-01 - <_> - - 0 1 708 5.6469351984560490e-03 -1 -2 709 - -2.4807190056890249e-03 - - 4.7049251198768616e-01 4.1798511147499084e-01 - 6.7266470193862915e-01 - <_> - - 0 1 710 -4.1088787838816643e-03 -1 -2 711 - -2.0714469719678164e-03 - - 5.8098018169403076e-01 6.0747838020324707e-01 - 4.5240598917007446e-01 - <_> - - 0 1 712 -2.8939060866832733e-03 -1 -2 713 - 1.3467279495671391e-03 - - 3.3835199475288391e-01 5.6969100236892700e-01 - 3.9708450436592102e-01 - <_> - - 0 1 714 -9.0779133141040802e-02 -1 -2 715 - -8.3171762526035309e-02 - - 1.5027019381523132e-01 7.5736707448959351e-01 - 4.9364370107650757e-01 - <_> - - 0 1 716 -1.4107000315561891e-03 -1 -2 717 - 5.5668760091066360e-02 - - 3.3909329771995544e-01 5.0250971317291260e-01 - 7.4220830202102661e-01 - <_> - - 0 1 718 5.7701539248228073e-02 -1 -2 719 - -4.2503291368484497e-01 - - 5.1973718404769897e-01 9.7346916794776917e-02 - 5.1857399940490723e-01 - <_> - - 0 1 720 -4.4380719191394746e-04 -1 -2 721 - 1.7924769781529903e-04 - - 3.6493501067161560e-01 5.6192791461944580e-01 - 3.7602970004081726e-01 - <_> - - 1 0 722 5.0382469780743122e-03 -1 -2 723 - 1.5191170386970043e-02 - - 6.3284450769424438e-01 4.9360820651054382e-01 - 7.4265247583389282e-01 - <_> - - 0 1 724 -1.2300389818847179e-02 -1 -2 725 - 1.5168030513450503e-03 - - 1.3893499970436096e-01 5.0919622182846069e-01 - 3.4826481342315674e-01 - <_> - - 1 0 726 9.5754547510296106e-04 -1 -2 727 - -1.8962200731039047e-02 - - 6.0363167524337769e-01 2.3191730678081512e-01 - 5.1166528463363647e-01 - <_> - - 0 1 728 -2.2272260859608650e-02 -1 -2 729 - -2.5145230814814568e-02 - - 6.5550220012664795e-01 1.3260710239410400e-01 - 4.6740341186523438e-01 - <_> - - 0 1 730 1.9533900544047356e-02 -1 -2 731 - -1.1231349781155586e-03 - - 5.1820272207260132e-01 6.3182431459426880e-01 - 4.8255190253257751e-01 - <_> - - 0 1 732 -1.4861139934509993e-03 -1 -2 733 - 3.5002888762392104e-04 - - 2.9186710715293884e-01 5.6213712692260742e-01 - 4.2492130398750305e-01 - <_> - - 1 0 734 -1.1231349781155586e-03 -1 -2 735 - 1.0409739799797535e-02 - - 4.8137450218200684e-01 5.1840060949325562e-01 - 2.0512230694293976e-01 - <_> - - 0 1 736 -8.7832562625408173e-02 -1 -2 737 - 1.6584879485890269e-03 - - 1.1799219995737076e-01 4.9878111481666565e-01 - 6.9737559556961060e-01 - <_> - - 1 0 738 -2.3008750285953283e-03 -1 -2 739 - 3.3026169985532761e-02 - - 5.3398311138153076e-01 5.0332891941070557e-01 - 6.8519067764282227e-01 - <_> - - 0 1 740 -1.3585069682449102e-03 -1 -2 741 - 7.8067491995170712e-04 - - 3.0028221011161804e-01 4.5930838584899902e-01 - 6.4400452375411987e-01 - <_> - - 1 0 742 -1.8025759607553482e-02 -1 -2 743 - 1.2354910140857100e-03 - - 5.3112912178039551e-01 4.7291061282157898e-01 - 5.7214611768722534e-01 - <_> - - 0 1 744 -9.2583027435466647e-04 -1 -2 745 - 8.0123997759073973e-04 - - 3.6623328924179077e-01 5.3619897365570068e-01 - 3.0086329579353333e-01 - <_> - 63 - 3.0672130584716797e+01 - - <_> - - 0 1 746 2.4914839304983616e-03 -1 -2 747 - -5.0488598644733429e-02 - - 3.4223890304565430e-01 7.7034580707550049e-01 - 4.5163908600807190e-01 - <_> - - 1 0 748 -7.7838351717218757e-04 -1 -2 749 - 2.3572890495415777e-04 - - 3.2563421130180359e-01 3.4065559506416321e-01 - 5.8970272541046143e-01 - <_> - - 0 1 750 4.5575071126222610e-03 -1 -2 751 - 8.1241987645626068e-03 - - 4.3065789341926575e-01 7.1495872735977173e-01 - 4.3456849455833435e-01 - <_> - - 0 1 752 -4.4612158671952784e-04 -1 -2 753 - -2.8972938889637589e-04 - - 3.2959741353988647e-01 5.8456200361251831e-01 - 3.5266879200935364e-01 - <_> - - 0 1 754 7.1604831646254752e-06 -1 -2 755 - -3.8497708737850189e-04 - - 4.0819549560546875e-01 4.2031130194664001e-01 - 6.6341269016265869e-01 - <_> - - 0 1 756 1.9489860278554261e-04 -1 -2 757 - -1.7083849757909775e-02 - - 3.9424669742584229e-01 2.2940720617771149e-01 - 5.2389609813690186e-01 - <_> - - 0 1 758 8.3513697609305382e-04 -1 -2 759 - 7.5499608647078276e-04 - - 3.0260318517684937e-01 6.0321962833404541e-01 - 3.4124588966369629e-01 - <_> - - 1 0 760 8.0216713249683380e-03 -1 -2 761 - -3.8930509239435196e-02 - - 7.3062407970428467e-01 3.5993251204490662e-01 - 5.2343809604644775e-01 - <_> - - 1 0 762 -7.0348767621908337e-05 -1 -2 763 - -8.5350573062896729e-03 - - 3.4937581419944763e-01 2.7461090683937073e-01 - 5.6265860795974731e-01 - <_> - - 0 1 764 1.0854450054466724e-02 -1 -2 765 - 4.5329501153901219e-04 - - 5.2822262048721313e-01 4.5220491290092468e-01 - 6.0543018579483032e-01 - <_> - - 0 1 766 1.8117150466423482e-04 -1 -2 767 - 4.6641560038551688e-04 - - 3.3068621158599854e-01 1.4550000429153442e-01 - 5.3849279880523682e-01 - <_> - - 1 0 768 -8.4854792803525925e-03 -1 -2 769 - -1.8934309482574463e-02 - - 4.8141559958457947e-01 3.5637411475181580e-01 - 5.4051452875137329e-01 - <_> - - 1 0 770 4.9814549274742603e-03 -1 -2 771 - 3.4286780282855034e-03 - - 6.9577431678771973e-01 5.0508928298950195e-01 - 2.3169949650764465e-01 - <_> - - 1 0 772 4.4203791185282171e-04 -1 -2 773 - 2.3822550429031253e-04 - - 6.0185819864273071e-01 4.7550821304321289e-01 - 5.5852377414703369e-01 - <_> - - 0 1 774 -6.4261639490723610e-03 -1 -2 775 - 9.9637769162654877e-03 - - 2.2824659943580627e-01 4.0405881404876709e-01 - 5.6501698493957520e-01 - <_> - - 0 1 776 1.3654050417244434e-02 -1 -2 777 - -9.9892877042293549e-03 - - 5.2677392959594727e-01 6.7940497398376465e-01 - 4.7970339655876160e-01 - <_> - - 1 0 778 3.6558631807565689e-02 -1 -2 779 - 4.8999379941960797e-05 - - 8.8425733149051666e-02 4.0207880735397339e-01 - 5.4573321342468262e-01 - <_> - - 0 1 780 1.3654050417244434e-02 -1 -2 781 - 1.8802779959514737e-03 - - 5.2676129341125488e-01 4.8060521483421326e-01 - 6.3943648338317871e-01 - <_> - - 0 1 782 -1.3654050417244434e-02 -1 -2 783 - 1.2778700329363346e-03 - - 1.7248100042343140e-01 4.4798240065574646e-01 - 6.3100087642669678e-01 - <_> - - 1 0 784 9.8843395244330168e-04 -1 -2 785 - 1.4511500012304168e-05 - - 5.9481692314147949e-01 4.8541748523712158e-01 - 5.3093612194061279e-01 - <_> - - 0 1 786 -2.2775429533794522e-04 -1 -2 787 - -1.4753740280866623e-02 - - 3.1836318969726562e-01 3.0849760770797729e-01 - 5.3520262241363525e-01 - <_> - - 0 1 788 -3.4148250706493855e-03 -1 -2 789 - 7.5806681998074055e-03 - - 6.1153268814086914e-01 4.9516460299491882e-01 - 7.0613312721252441e-01 - <_> - - 1 0 790 -5.7734688743948936e-03 -1 -2 791 - 7.4033669079653919e-05 - - 3.7542209029197693e-01 4.1155171394348145e-01 - 5.8894449472427368e-01 - <_> - - 0 1 792 -8.2278084009885788e-03 -1 -2 793 - 5.3380909375846386e-03 - - 9.5610566437244415e-02 5.3005087375640869e-01 - 3.9618980884552002e-01 - <_> - - 0 1 794 -2.7049109339714050e-03 -1 -2 795 - 7.7341338619589806e-03 - - 6.4818692207336426e-01 5.1104402542114258e-01 - 3.1215190887451172e-01 - <_> - - 0 1 796 1.0886609554290771e-02 -1 -2 797 - 1.1038660071790218e-02 - - 4.8014289140701294e-01 5.4297101497650146e-01 - 4.1623631119728088e-01 - <_> - - 0 1 798 -1.0054199956357479e-02 -1 -2 799 - 7.7072880230844021e-03 - - 7.3293352127075195e-01 5.3568720817565918e-01 - 3.4555470943450928e-01 - <_> - - 0 1 800 -5.8278098003938794e-04 -1 -2 801 - -2.5739220436662436e-03 - - 3.6550220847129822e-01 3.7767601013183594e-01 - 5.3917747735977173e-01 - <_> - - 0 1 802 -7.0167761296033859e-03 -1 -2 803 - -1.7727289814502001e-03 - - 4.0393048524856567e-01 6.9504439830780029e-01 - 4.9811169505119324e-01 - <_> - - 1 0 804 -1.6318289563059807e-02 -1 -2 805 - -1.1663000099360943e-02 - - 5.2967327833175659e-01 5.8426398038864136e-01 - 4.7895029187202454e-01 - <_> - - 1 0 806 2.5881489273160696e-03 -1 -2 807 - -3.7328999023884535e-03 - - 6.0921788215637207e-01 6.7217427492141724e-01 - 4.0668940544128418e-01 - <_> - - 0 1 808 -1.4355930034071207e-03 -1 -2 809 - 1.8340899841859937e-03 - - 3.5850879549980164e-01 5.3711581230163574e-01 - 4.0335071086883545e-01 - <_> - - 1 0 810 1.2280289828777313e-01 -1 -2 811 - 5.0228700041770935e-02 - - 1.5475720167160034e-01 5.4338437318801880e-01 - 8.4292672574520111e-02 - <_> - - 1 0 812 -2.1437000483274460e-02 -1 -2 813 - -3.1009620055556297e-02 - - 4.8600539565086365e-01 1.8330100178718567e-01 - 5.2075541019439697e-01 - <_> - - 0 1 814 -1.2973720207810402e-02 -1 -2 815 - 1.5818020328879356e-03 - - 7.0482409000396729e-01 4.1705870628356934e-01 - 5.8651638031005859e-01 - <_> - - 1 0 816 -9.7806248813867569e-03 -1 -2 817 - 1.1735740117728710e-03 - - 5.3079181909561157e-01 5.5224531888961792e-01 - 3.5071650147438049e-01 - <_> - - 1 0 818 1.4651629608124495e-03 -1 -2 819 - 2.3532148916274309e-03 - - 3.0426511168479919e-01 5.3393232822418213e-01 - 2.8062361478805542e-01 - <_> - - 0 1 820 -6.1809681355953217e-03 -1 -2 821 - 6.5688649192452431e-04 - - 6.4101332426071167e-01 5.6208711862564087e-01 - 4.3903189897537231e-01 - <_> - - 1 0 822 2.6228010654449463e-02 -1 -2 823 - -1.7958110198378563e-02 - - 6.4455568790435791e-01 2.0027139782905579e-01 - 4.6246650815010071e-01 - <_> - - 1 0 824 -7.6468721963465214e-03 -1 -2 825 - -2.7482809964567423e-03 - - 5.2632009983062744e-01 5.8739811182022095e-01 - 4.8366001248359680e-01 - <_> - - 1 0 826 1.3851850293576717e-02 -1 -2 827 - 2.6369190309196711e-03 - - 1.5661309659481049e-01 4.2701789736747742e-01 - 5.8066600561141968e-01 - <_> - - 0 1 828 -3.1513599678874016e-03 -1 -2 829 - -1.4788460248382762e-05 - - 6.2158662080764771e-01 5.5766427516937256e-01 - 4.1220021247863770e-01 - <_> - - 0 1 830 -7.3676988482475281e-02 -1 -2 831 - -3.0912780202925205e-03 - - 1.5367099642753601e-01 6.3442689180374146e-01 - 4.5074120163917542e-01 - <_> - - 0 1 832 7.9240966588258743e-03 -1 -2 833 - 8.5778040811419487e-03 - - 5.4579752683639526e-01 5.4016572237014771e-01 - 3.8907998800277710e-01 - <_> - - 1 0 834 5.5403169244527817e-03 -1 -2 835 - -1.1886510037584230e-04 - - 3.5556110739707947e-01 5.8367502689361572e-01 - 4.2743161320686340e-01 - <_> - - 0 1 836 -1.8408369272947311e-02 -1 -2 837 - -2.3490579333156347e-03 - - 5.8604401350021362e-01 4.4989579916000366e-01 - 5.4981988668441772e-01 - <_> - - 1 0 838 -7.6157399453222752e-03 -1 -2 839 - -3.3190969843417406e-03 - - 4.1009929776191711e-01 6.7013788223266602e-01 - 4.3530011177062988e-01 - <_> - - 1 0 840 -9.4642979092895985e-04 -1 -2 841 - 8.7858550250530243e-03 - - 5.3911769390106201e-01 5.5040502548217773e-01 - 3.9909350872039795e-01 - <_> - - 1 0 842 1.6395459533669055e-04 -1 -2 843 - -2.3508940357714891e-03 - - 3.5929331183433533e-01 4.0341728925704956e-01 - 5.8060771226882935e-01 - <_> - - 1 0 844 7.5449963333085179e-05 -1 -2 845 - 2.7018489316105843e-02 - - 5.4123848676681519e-01 4.9449229240417480e-01 - 5.5894362926483154e-01 - <_> - - 1 0 846 8.4561208495870233e-04 -1 -2 847 - -1.1687109945341945e-03 - - 5.8092182874679565e-01 4.7469571232795715e-01 - 2.8458958864212036e-01 - <_> - - 1 0 848 2.2897500544786453e-02 -1 -2 849 - 7.0879262685775757e-01 - - 2.4144110083580017e-01 5.1957648992538452e-01 - 1.0300920158624649e-01 - <_> - - 1 0 850 3.7483830004930496e-02 -1 -2 851 - 1.2827500468119979e-03 - - 1.8146389722824097e-01 4.2460718750953674e-01 - 5.7079732418060303e-01 - <_> - - 0 1 852 -5.1718312315642834e-03 -1 -2 853 - 2.7545939665287733e-03 - - 6.1433231830596924e-01 5.2056711912155151e-01 - 4.2204418778419495e-01 - <_> - - 0 1 854 -3.6072919610887766e-03 -1 -2 855 - -2.5258748792111874e-04 - - 3.1825920939445496e-01 5.7104682922363281e-01 - 4.2260938882827759e-01 - <_> - - 1 0 856 -7.0514748804271221e-03 -1 -2 857 - -5.4323761723935604e-03 - - 5.1628297567367554e-01 2.6662889122962952e-01 - 5.2146798372268677e-01 - <_> - - 1 0 858 -1.4652940080850385e-05 -1 -2 859 - -1.8556920113041997e-03 - - 3.9817610383033752e-01 3.3227631449699402e-01 - 5.7058340311050415e-01 - <_> - - 1 0 860 4.7609540633857250e-03 -1 -2 861 - 1.5676260227337480e-03 - - 6.6365581750869751e-01 5.5055677890777588e-01 - 4.4206619262695312e-01 - <_> - - 1 0 862 5.4239919409155846e-03 -1 -2 863 - -6.4692399464547634e-03 - - 5.9599381685256958e-01 5.3695940971374512e-01 - 3.7443399429321289e-01 - <_> - - 0 1 864 -7.8038539504632354e-04 -1 -2 865 - 4.5086450874805450e-02 - - 4.1035950183868408e-01 5.1775068044662476e-01 - 1.8781000375747681e-01 - <_> - - 0 1 866 -5.1405387930572033e-03 -1 -2 867 - -2.1236129105091095e-02 - - 2.3528920114040375e-01 1.7087510228157043e-01 - 5.4249739646911621e-01 - <_> - - 0 1 868 -2.3763340432196856e-03 -1 -2 869 - 5.4122589528560638e-02 - - 5.8365309238433838e-01 5.1174330711364746e-01 - 1.8659310042858124e-01 - <_> - - 0 1 870 -5.3492980077862740e-04 -1 -2 871 - -5.8454048121348023e-04 - - 5.1086932420730591e-01 4.7754910588264465e-01 - 2.4398539960384369e-01 - <_> - 71 - 3.4677078247070312e+01 - - <_> - - 0 1 872 3.0031939968466759e-03 -1 -2 873 - 6.9161207647994161e-04 - - 3.3496499061584473e-01 4.5183679461479187e-01 - 7.2893542051315308e-01 - <_> - - 0 1 874 1.1212790384888649e-02 -1 -2 875 - -7.6108198845759034e-04 - - 2.9508009552955627e-01 5.6690549850463867e-01 - 2.8308510780334473e-01 - <_> - - 0 1 876 1.1984579759882763e-04 -1 -2 877 - -1.9725349557120353e-04 - - 4.0905779600143433e-01 6.9514942169189453e-01 - 4.6378681063652039e-01 - <_> - - 1 0 878 -5.5180420167744160e-03 -1 -2 879 - 1.2148249661549926e-03 - - 3.1676751375198364e-01 3.3167061209678650e-01 - 5.3963977098464966e-01 - <_> - - 0 1 880 -4.2497441172599792e-03 -1 -2 881 - -9.4915721565485001e-03 - - 2.6005738973617554e-01 7.4842947721481323e-01 - 5.0731921195983887e-01 - <_> - - 1 0 882 6.5378600265830755e-04 -1 -2 883 - -4.9741100519895554e-04 - - 3.9520108699798584e-01 5.8802747726440430e-01 - 3.5521200299263000e-01 - <_> - - 0 1 884 -4.3079249560832977e-02 -1 -2 885 - -5.1999092102050781e-04 - - 2.4348780512809753e-01 3.1955629587173462e-01 - 5.5854547023773193e-01 - <_> - - 1 0 886 -4.5451628975570202e-03 -1 -2 887 - -7.9610403627157211e-03 - - 4.8452898859977722e-01 3.8011810183525085e-01 - 5.3585118055343628e-01 - <_> - - 1 0 888 -3.1919340835884213e-04 -1 -2 889 - -1.9223889335989952e-02 - - 4.3563291430473328e-01 2.6130661368370056e-01 - 6.1554962396621704e-01 - <_> - - 0 1 890 -1.3076990144327283e-03 -1 -2 891 - 1.9825039431452751e-02 - - 5.9420621395111084e-01 4.9454280734062195e-01 - 7.3848551511764526e-01 - <_> - - 0 1 892 -2.2013280540704727e-03 -1 -2 893 - -7.8596705570816994e-03 - - 2.2144819796085358e-01 3.6009770631790161e-01 - 5.2985501289367676e-01 - <_> - - 1 0 894 1.4142199652269483e-03 -1 -2 895 - -1.1232759803533554e-02 - - 5.7765662670135498e-01 6.9344568252563477e-01 - 4.8272070288658142e-01 - <_> - - 1 0 896 2.9746301006525755e-03 -1 -2 897 - 5.3283828310668468e-04 - - 3.2166770100593567e-01 3.9625000953674316e-01 - 5.6803637742996216e-01 - <_> - - 1 0 898 1.0105259716510773e-02 -1 -2 899 - -1.1653699912130833e-02 - - 7.5674182176589966e-01 6.5235567092895508e-01 - 5.0270539522171021e-01 - <_> - - 0 1 900 -7.0609981194138527e-03 -1 -2 901 - 2.2343141026794910e-03 - - 2.5387701392173767e-01 4.3872770667076111e-01 - 6.1776322126388550e-01 - <_> - - 1 0 902 -2.9802279546856880e-02 -1 -2 903 - 1.1611840454861522e-03 - - 5.2011400461196899e-01 4.6479099988937378e-01 - 6.1842548847198486e-01 - <_> - - 1 0 904 9.4824447296559811e-04 -1 -2 905 - 4.1284630424343050e-04 - - 3.0409941077232361e-01 4.5188081264495850e-01 - 6.2457829713821411e-01 - <_> - - 0 1 906 -3.1203540042042732e-02 -1 -2 907 - 2.7652881108224392e-03 - - 2.7889358997344971e-01 4.6985000371932983e-01 - 6.5024542808532715e-01 - <_> - - 1 0 908 2.5644779205322266e-02 -1 -2 909 - -7.5331530533730984e-03 - - 1.8051710724830627e-01 3.2080689072608948e-01 - 5.5220228433609009e-01 - <_> - - 1 0 910 3.2047149725258350e-03 -1 -2 911 - -2.4282479716930538e-04 - - 6.4369338750839233e-01 5.6767052412033081e-01 - 4.5091038942337036e-01 - <_> - - 0 1 912 -6.1979342717677355e-04 -1 -2 913 - -8.0101029016077518e-04 - - 3.1221461296081543e-01 2.9651939868927002e-01 - 5.2304947376251221e-01 - <_> - - 1 0 914 -9.1816839994862676e-04 -1 -2 915 - 1.2239529751241207e-03 - - 5.4647117853164673e-01 4.6185028553009033e-01 - 5.6795489788055420e-01 - <_> - - 0 1 916 -6.8743730662390590e-04 -1 -2 917 - -1.8252469599246979e-03 - - 5.4308801889419556e-01 5.4336231946945190e-01 - 3.3852210640907288e-01 - <_> - - 1 0 918 -7.4570789001882076e-03 -1 -2 919 - 5.3775748237967491e-03 - - 5.2655947208404541e-01 4.8572158813476562e-01 - 6.8151241540908813e-01 - <_> - - 1 0 920 3.7602309603244066e-03 -1 -2 921 - 8.7752222316339612e-04 - - 2.8321608901023865e-01 3.9668309688568115e-01 - 5.5124807357788086e-01 - <_> - - 1 0 922 5.5084479972720146e-03 -1 -2 923 - -7.5949047459289432e-04 - - 6.7846202850341797e-01 3.9065030217170715e-01 - 5.4572027921676636e-01 - <_> - - 1 0 924 1.6352660022675991e-03 -1 -2 925 - -1.2750849418807775e-04 - - 3.6402040719985962e-01 5.8297240734100342e-01 - 4.1949799656867981e-01 - <_> - - 0 1 926 2.2067610174417496e-02 -1 -2 927 - -1.9203789532184601e-02 - - 4.6067029237747192e-01 3.2614830136299133e-01 - 5.2360808849334717e-01 - <_> - - 0 1 928 -1.2998109683394432e-02 -1 -2 929 - -3.1332690268754959e-03 - - 7.0221120119094849e-01 2.8704708814620972e-01 - 5.0764769315719604e-01 - <_> - - 1 0 930 -5.2937557920813560e-03 -1 -2 931 - 2.1857069805264473e-03 - - 4.7095209360122681e-01 4.7082918882369995e-01 - 6.1698418855667114e-01 - <_> - - 0 1 932 -4.5750709250569344e-03 -1 -2 933 - -4.5152138918638229e-02 - - 3.1142529845237732e-01 1.8514350056648254e-01 - 5.5048149824142456e-01 - <_> - - 1 0 934 -2.7783559635281563e-03 -1 -2 935 - -2.5752480141818523e-03 - - 4.9373480677604675e-01 6.1529481410980225e-01 - 4.7354999184608459e-01 - <_> - - 1 0 936 1.1614130344241858e-03 -1 -2 937 - 2.3350189439952374e-03 - - 6.5105718374252319e-01 4.0883418917655945e-01 - 5.6841522455215454e-01 - <_> - - 1 0 938 3.8499289657920599e-03 -1 -2 939 - 2.4529630318284035e-03 - - 3.0258288979530334e-01 5.2325028181076050e-01 - 2.0176209509372711e-01 - <_> - - 1 0 940 3.6731390282511711e-03 -1 -2 941 - 2.1937100682407618e-03 - - 6.4284259080886841e-01 4.3288651108741760e-01 - 6.4205098152160645e-01 - <_> - - 1 0 942 -6.4666871912777424e-03 -1 -2 943 - -5.7186251506209373e-03 - - 5.2540659904479980e-01 2.4909840524196625e-01 - 5.2876192331314087e-01 - <_> - - 1 0 944 9.9941878579556942e-04 -1 -2 945 - -7.8276498243212700e-04 - - 3.3297958970069885e-01 3.5983449220657349e-01 - 5.4983407258987427e-01 - <_> - - 0 1 946 4.3231188319623470e-03 -1 -2 947 - 4.0838290005922318e-03 - - 4.8187050223350525e-01 5.2663302421569824e-01 - 3.1057891249656677e-01 - <_> - - 1 0 948 3.0515898833982646e-04 -1 -2 949 - 1.2640280183404684e-03 - - 3.9952918887138367e-01 3.2284379005432129e-01 - 5.8192151784896851e-01 - <_> - - 0 1 950 -1.0152660310268402e-02 -1 -2 951 - -2.6863690000027418e-03 - - 8.0260711908340454e-01 3.8756170868873596e-01 - 5.4665708541870117e-01 - <_> - - 1 0 952 -9.0515613555908203e-03 -1 -2 953 - -6.3204211182892323e-03 - - 4.3720579147338867e-01 1.1265510320663452e-01 - 6.3954162597656250e-01 - <_> - - 0 1 954 2.6117300149053335e-03 -1 -2 955 - 1.4339019544422626e-02 - - 5.4239892959594727e-01 4.9792730808258057e-01 - 6.0422360897064209e-01 - <_> - - 1 0 956 2.8452780097723007e-03 -1 -2 957 - 1.4783289771003183e-05 - - 3.4910920262336731e-01 4.1950678825378418e-01 - 5.7759660482406616e-01 - <_> - - 0 1 958 8.1814555451273918e-03 -1 -2 959 - 6.6321990452706814e-03 - - 4.8859870433807373e-01 5.4444682598114014e-01 - 4.4209951162338257e-01 - <_> - - 0 1 960 -2.2483461070805788e-03 -1 -2 961 - 1.2374560348689556e-02 - - 6.6997921466827393e-01 4.4786059856414795e-01 - 6.5648937225341797e-01 - <_> - - 1 0 962 -6.6516688093543053e-03 -1 -2 963 - -8.5750613361597061e-03 - - 5.5118787288665771e-01 4.0174451470375061e-01 - 5.4055362939834595e-01 - <_> - - 1 0 964 6.5078441984951496e-03 -1 -2 965 - 2.8675209730863571e-02 - - 2.2943930327892303e-01 5.1779001951217651e-01 - 3.5677561163902283e-01 - <_> - - 0 1 966 7.0673860609531403e-03 -1 -2 967 - 1.2367829913273454e-03 - - 5.5646997690200806e-01 3.6276981234550476e-01 - 5.5724138021469116e-01 - <_> - - 1 0 968 7.4818679131567478e-03 -1 -2 969 - 4.7109839506447315e-03 - - 6.7849111557006836e-01 4.1212528944015503e-01 - 6.0722357034683228e-01 - <_> - - 1 0 970 -6.9405790418386459e-03 -1 -2 971 - 3.3302098512649536e-02 - - 5.4597669839859009e-01 5.2767068147659302e-01 - 2.3749159276485443e-01 - <_> - - 1 0 972 3.6104630678892136e-02 -1 -2 973 - 1.9674649462103844e-02 - - 7.2492793202400208e-02 4.6263459324836731e-01 - 8.2089632749557495e-01 - <_> - - 0 1 974 3.4766150638461113e-03 -1 -2 975 - 1.3987369602546096e-03 - - 5.2087318897247314e-01 5.4844141006469727e-01 - 4.2300349473953247e-01 - <_> - - 1 0 976 4.0974249131977558e-03 -1 -2 977 - 2.6973790954798460e-03 - - 2.7805531024932861e-01 5.4038310050964355e-01 - 3.7909889221191406e-01 - <_> - - 1 0 978 -5.6591699831187725e-03 -1 -2 979 - 3.9460969856008887e-04 - - 4.7983360290527344e-01 3.7669500708580017e-01 - 5.4292291402816772e-01 - <_> - - 1 0 980 2.1750570740550756e-03 -1 -2 981 - 1.4614439569413662e-03 - - 6.2071627378463745e-01 3.3579450845718384e-01 - 5.1426321268081665e-01 - <_> - - 1 0 982 -5.3006567759439349e-04 -1 -2 983 - 1.4869309961795807e-01 - - 5.3446400165557861e-01 5.1596081256866455e-01 - 2.5618231296539307e-01 - <_> - - 1 0 984 -5.8816498494707048e-05 -1 -2 985 - -1.6275369562208652e-03 - - 5.1230919361114502e-01 6.0176461935043335e-01 - 3.1093719601631165e-01 - <_> - - 0 1 986 -1.2881809845566750e-02 -1 -2 987 - 9.4982917653396726e-04 - - 2.7122870087623596e-01 5.4424422979354858e-01 - 4.0288880467414856e-01 - <_> - - 1 0 988 -1.2315999716520309e-02 -1 -2 989 - 9.0286601334810257e-03 - - 4.7360658645629883e-01 7.4514347314834595e-01 - 3.4879919886589050e-01 - <_> - - 0 1 990 -8.6876116693019867e-02 -1 -2 991 - -1.5107560102478601e-05 - - 2.2903330624103546e-01 5.5178898572921753e-01 - 4.3931490182876587e-01 - <_> - - 0 1 992 -1.7457660287618637e-02 -1 -2 993 - -2.5219470262527466e-03 - - 9.0167902410030365e-02 6.2335401773452759e-01 - 4.7894591093063354e-01 - <_> - - 0 1 994 1.0656520025804639e-03 -1 -2 995 - -4.2540300637483597e-03 - - 5.4896962642669678e-01 5.5798089504241943e-01 - 4.3758779764175415e-01 - <_> - - 0 1 996 -9.0349102392792702e-03 -1 -2 997 - -1.5230999561026692e-03 - - 3.5791561007499695e-01 5.6136602163314819e-01 - 3.9390438795089722e-01 - <_> - - 1 0 998 2.8441150207072496e-03 -1 -2 999 - -3.2824429217725992e-03 - - 3.9015549421310425e-01 4.5286190509796143e-01 - 5.4413431882858276e-01 - <_> - - 1 0 1000 3.2161718991119415e-05 -1 -2 1001 - 3.0118400900391862e-05 - - 5.8031117916107178e-01 3.3368501067161560e-01 - 5.5048561096191406e-01 - <_> - - 0 1 1002 -5.6150099262595177e-03 -1 -2 1003 - -1.7389209941029549e-02 - - 6.1247891187667847e-01 8.7271630764007568e-02 - 5.2045881748199463e-01 - <_> - - 0 1 1004 -4.4361080654198304e-05 -1 -2 1005 - 1.0354899859521538e-04 - - 3.9353290200233459e-01 5.9188538789749146e-01 - 4.1196140646934509e-01 - <_> - - 0 1 1006 1.5939630102366209e-03 -1 -2 1007 - 2.5440789759159088e-03 - - 4.8396238684654236e-01 4.7873649001121521e-01 - 6.3606631755828857e-01 - <_> - - 0 1 1008 1.5083180187502876e-05 -1 -2 1009 - -9.9282202427275479e-05 - - 4.2311170697212219e-01 4.2745891213417053e-01 - 6.0940480232238770e-01 - <_> - - 1 0 1010 5.5371708003804088e-04 -1 -2 1011 - 1.9186759600415826e-03 - - 4.2719879746437073e-01 4.4971078634262085e-01 - 5.5491220951080322e-01 - <_> - - 1 0 1012 -5.0764222396537662e-04 -1 -2 1013 - 1.7236480489373207e-03 - - 5.4771959781646729e-01 2.8829228878021240e-01 - 5.6151270866394043e-01 - <_> - 75 - 3.6726501464843750e+01 - - <_> - - 0 1 1014 1.3092169538140297e-02 -1 -2 1015 - 4.1446479735895991e-04 - - 3.3388701081275940e-01 3.0993521213531494e-01 - 6.6774922609329224e-01 - <_> - - 0 1 1016 2.1835729479789734e-02 -1 -2 1017 - 4.8323940485715866e-02 - - 4.3690490722656250e-01 4.3017241358757019e-01 - 6.1538851261138916e-01 - <_> - - 0 1 1018 1.6091950237751007e-03 -1 -2 1019 - 1.3469760306179523e-03 - - 3.3873260021209717e-01 6.2487137317657471e-01 - 3.5941308736801147e-01 - <_> - - 0 1 1020 1.7729059618432075e-04 -1 -2 1021 - 3.6743620876222849e-04 - - 3.8684248924255371e-01 4.4093450903892517e-01 - 5.4764741659164429e-01 - <_> - - 0 1 1022 -1.2352119665592909e-03 -1 -2 1023 - 1.1705530341714621e-03 - - 3.2601711153984070e-01 4.1113489866256714e-01 - 6.0881638526916504e-01 - <_> - - 1 0 1024 -2.9695429475395940e-05 -1 -2 1025 - 2.7050738572143018e-04 - - 4.2694228887557983e-01 4.3064668774604797e-01 - 5.8105140924453735e-01 - <_> - - 1 0 1026 -7.9626210208516568e-05 -1 -2 1027 - 3.3152441028505564e-04 - - 3.6691430211067200e-01 4.6106639504432678e-01 - 6.2905901670455933e-01 - <_> - - 1 0 1028 -5.2305828779935837e-02 -1 -2 1029 - 2.6880469173192978e-02 - - 5.3286898136138916e-01 5.2132612466812134e-01 - 3.2312199473381042e-01 - <_> - - 1 0 1030 -2.4203000066336244e-04 -1 -2 1031 - -1.6424639616161585e-03 - - 3.5685700178146362e-01 3.4406611323356628e-01 - 5.6256049871444702e-01 - <_> - - 1 0 1032 -2.6830288697965443e-04 -1 -2 1033 - -2.2649629972875118e-03 - - 4.5611730217933655e-01 5.3213518857955933e-01 - 3.6741548776626587e-01 - <_> - - 1 0 1034 1.5627209097146988e-02 -1 -2 1035 - 1.6211320459842682e-01 - - 2.0293539762496948e-01 5.5630332231521606e-01 - 2.6188498735427856e-01 - <_> - - 0 1 1036 -3.7391691002994776e-03 -1 -2 1037 - -2.0878419745713472e-03 - - 6.0621947050094604e-01 5.9507638216018677e-01 - 4.5451170206069946e-01 - <_> - - 1 0 1038 2.3334210272878408e-03 -1 -2 1039 - 6.5116386394947767e-05 - - 6.4355242252349854e-01 3.5207340121269226e-01 - 5.1797789335250854e-01 - <_> - - 0 1 1040 7.4625718407332897e-03 -1 -2 1041 - -2.2032689303159714e-02 - - 5.3266882896423340e-01 3.4919810295104980e-01 - 5.4292368888854980e-01 - <_> - - 0 1 1042 -8.3081610500812531e-03 -1 -2 1043 - -4.3259368976578116e-04 - - 2.0840230584144592e-01 3.9652720093727112e-01 - 5.4254537820816040e-01 - <_> - - 1 0 1044 -3.2209228724241257e-02 -1 -2 1045 - -9.0424838708713651e-04 - - 5.3064119815826416e-01 5.4503858089447021e-01 - 4.2566969990730286e-01 - <_> - - 1 0 1046 2.2727500181645155e-03 -1 -2 1047 - 5.9820008464157581e-03 - - 5.9686112403869629e-01 4.7581401467323303e-01 - 3.1509441137313843e-01 - <_> - - 1 0 1048 -5.8856618124991655e-04 -1 -2 1049 - -8.8227191008627415e-04 - - 4.8477488756179810e-01 5.4263162612915039e-01 - 4.3383410573005676e-01 - <_> - - 1 0 1050 -7.4473457061685622e-05 -1 -2 1051 - 3.9148979703895748e-04 - - 4.2875099182128906e-01 6.3451850414276123e-01 - 4.1018518805503845e-01 - <_> - - 1 0 1052 -3.6939629353582859e-03 -1 -2 1053 - -1.1207849718630314e-02 - - 4.8491048812866211e-01 4.1463369131088257e-01 - 5.4712641239166260e-01 - <_> - - 0 1 1054 -1.0337409563362598e-02 -1 -2 1055 - 3.6883640568703413e-03 - - 2.8771838545799255e-01 5.1019018888473511e-01 - 7.2169512510299683e-01 - <_> - - 1 0 1056 -3.8984280545264482e-03 -1 -2 1057 - -5.9986729174852371e-03 - - 5.2761822938919067e-01 6.6184598207473755e-01 - 4.8416310548782349e-01 - <_> - - 1 0 1058 4.5043681748211384e-03 -1 -2 1059 - 1.7799530178308487e-02 - - 1.8741579353809357e-01 4.6169349551200867e-01 - 7.0889657735824585e-01 - <_> - - 0 1 1060 -1.8462570384144783e-02 -1 -2 1061 - 1.4931300029275008e-05 - - 3.0019798874855042e-01 4.5618081092834473e-01 - 5.6107878684997559e-01 - <_> - - 0 1 1062 -8.6021229624748230e-02 -1 -2 1063 - -6.0818758356617764e-05 - - 2.3417009413242340e-01 5.6722861528396606e-01 - 4.1999641060829163e-01 - <_> - - 1 0 1064 1.2670679716393352e-03 -1 -2 1065 - 1.3699879636988044e-03 - - 6.2074822187423706e-01 5.3949588537216187e-01 - 3.8238629698753357e-01 - <_> - - 1 0 1066 3.3162781037390232e-03 -1 -2 1067 - -1.4532039640471339e-03 - - 7.0616811513900757e-01 3.0655130743980408e-01 - 4.8273730278015137e-01 - <_> - - 1 0 1068 -7.1492061018943787e-02 -1 -2 1069 - 1.9857978913933039e-03 - - 5.1931220293045044e-01 4.6424350142478943e-01 - 5.8076947927474976e-01 - <_> - - 1 0 1070 6.2516499310731888e-03 -1 -2 1071 - 2.7005500160157681e-03 - - 2.9498139023780823e-01 4.5858868956565857e-01 - 6.0223537683486938e-01 - <_> - - 0 1 1072 1.1130389757454395e-02 -1 -2 1073 - 1.5092849731445312e-02 - - 4.3578410148620605e-01 4.5615398883819580e-01 - 6.1190617084503174e-01 - <_> - - 0 1 1074 -2.7943300083279610e-02 -1 -2 1075 - 4.4036991312168539e-05 - - 6.5371441841125488e-01 3.4747231006622314e-01 - 5.3369677066802979e-01 - <_> - - 0 1 1076 -1.2232770211994648e-02 -1 -2 1077 - -6.8591412855312228e-04 - - 3.7316760420799255e-01 5.7172292470932007e-01 - 4.7933790087699890e-01 - <_> - - 0 1 1078 -3.8992990739643574e-03 -1 -2 1079 - 4.9113907152786851e-04 - - 4.0564361214637756e-01 6.1740481853485107e-01 - 4.4717541337013245e-01 - <_> - - 1 0 1080 8.2117747515439987e-03 -1 -2 1081 - -4.5564480125904083e-02 - - 6.1796981096267700e-01 2.2854949533939362e-01 - 5.2495658397674561e-01 - <_> - - 0 1 1082 -5.3631910122931004e-03 -1 -2 1083 - -1.2274970300495625e-02 - - 1.7849500477313995e-01 7.2619527578353882e-01 - 4.5503988862037659e-01 - <_> - - 0 1 1084 5.4185991175472736e-03 -1 -2 1085 - 8.1846961984410882e-04 - - 5.2529907226562500e-01 5.4452222585678101e-01 - 3.2722181081771851e-01 - <_> - - 1 0 1086 4.1358140297234058e-03 -1 -2 1087 - 3.9578010910190642e-04 - - 7.0138317346572876e-01 4.9659439921379089e-01 - 3.2955980300903320e-01 - <_> - - 0 1 1088 4.6887691132724285e-03 -1 -2 1089 - -1.8255440518260002e-02 - - 5.3626418113708496e-01 6.4961087703704834e-01 - 4.7571370005607605e-01 - <_> - - 0 1 1090 -6.2736468389630318e-03 -1 -2 1091 - 2.4320168886333704e-03 - - 2.3437410593032837e-01 4.6201181411743164e-01 - 6.8984192609786987e-01 - <_> - - 0 1 1092 -4.9617629498243332e-02 -1 -2 1093 - 1.1701210169121623e-03 - - 2.1007199585437775e-01 4.6215289831161499e-01 - 5.7971358299255371e-01 - <_> - - 0 1 1094 -4.5237291604280472e-02 -1 -2 1095 - 4.7563421539962292e-03 - - 2.1182620525360107e-01 4.8846149444580078e-01 - 6.8724989891052246e-01 - <_> - - 1 0 1096 -1.4835969544947147e-02 -1 -2 1097 - 7.7436608262360096e-04 - - 5.2751058340072632e-01 4.1723209619522095e-01 - 5.4911398887634277e-01 - <_> - - 1 0 1098 1.4835969544947147e-02 -1 -2 1099 - -8.0892542609944940e-04 - - 2.1248769760131836e-01 5.4952150583267212e-01 - 4.2077958583831787e-01 - <_> - - 0 1 1100 7.7517668250948191e-04 -1 -2 1101 - -6.7618978209793568e-03 - - 3.3219420909881592e-01 2.2129580378532410e-01 - 5.2326530218124390e-01 - <_> - - 0 1 1102 -4.0135860443115234e-02 -1 -2 1103 - -3.3651469275355339e-03 - - 1.1017960309982300e-01 3.8101008534431458e-01 - 5.6172919273376465e-01 - <_> - - 1 0 1104 7.4713007779791951e-04 -1 -2 1105 - -4.2727389372885227e-03 - - 5.7950568199157715e-01 6.3922691345214844e-01 - 4.7114381194114685e-01 - <_> - - 1 0 1106 3.6202510818839073e-03 -1 -2 1107 - 4.7307618660852313e-04 - - 3.4098839759826660e-01 3.6593028903007507e-01 - 5.3881710767745972e-01 - <_> - - 1 0 1108 3.3094909042119980e-02 -1 -2 1109 - -1.1544119566679001e-02 - - 7.1703857183456421e-01 6.3868182897567749e-01 - 4.6813040971755981e-01 - <_> - - 0 1 1110 -7.4234469793736935e-03 -1 -2 1111 - -4.2252950370311737e-03 - - 3.2637009024620056e-01 5.7678192853927612e-01 - 4.3464180827140808e-01 - <_> - - 0 1 1112 1.8133109435439110e-02 -1 -2 1113 - 7.0903049781918526e-03 - - 4.6978279948234558e-01 4.4373890757560730e-01 - 6.0616689920425415e-01 - <_> - - 0 1 1114 -1.3272940181195736e-02 -1 -2 1115 - 1.4632199599873275e-04 - - 6.5585112571716309e-01 3.3763539791107178e-01 - 5.0916552543640137e-01 - <_> - - 0 1 1116 -3.5790191031992435e-03 -1 -2 1117 - -4.6997101162560284e-04 - - 2.9478839039802551e-01 5.5569821596145630e-01 - 4.6654561161994934e-01 - <_> - - 0 1 1118 -4.8179440200328827e-02 -1 -2 1119 - -9.2581362696364522e-04 - - 7.3383557796478271e-01 3.5438719391822815e-01 - 5.2851498126983643e-01 - <_> - - 0 1 1120 -1.4780730009078979e-02 -1 -2 1121 - -1.0027450323104858e-01 - - 1.9444419443607330e-01 9.9049292504787445e-02 - 5.1398539543151855e-01 - <_> - - 0 1 1122 -9.3848101096227765e-04 -1 -2 1123 - -2.8861360624432564e-03 - - 5.8271098136901855e-01 3.4414279460906982e-01 - 5.1488387584686279e-01 - <_> - - 1 0 1124 -4.3682761490345001e-02 -1 -2 1125 - 2.6115700602531433e-03 - - 5.2079981565475464e-01 4.8355031013488770e-01 - 6.3222199678421021e-01 - <_> - - 1 0 1126 4.3682761490345001e-02 -1 -2 1127 - 1.7179530113935471e-03 - - 1.3645380735397339e-01 4.5373201370239258e-01 - 6.0667508840560913e-01 - <_> - - 1 0 1128 -3.3964909613132477e-02 -1 -2 1129 - -1.0993590112775564e-03 - - 4.9683749675750732e-01 5.8316808938980103e-01 - 4.6882399916648865e-01 - <_> - - 1 0 1130 5.4301079362630844e-02 -1 -2 1131 - 1.0993590112775564e-03 - - 7.5682890415191650e-01 4.3301481008529663e-01 - 5.7684689760208130e-01 - <_> - - 1 0 1132 -1.4954120160837192e-05 -1 -2 1133 - 3.1415868550539017e-02 - - 4.4432818889617920e-01 5.2744728326797485e-01 - 3.0378559231758118e-01 - <_> - - 1 0 1134 1.0831849649548531e-02 -1 -2 1135 - 8.6545711383223534e-04 - - 3.5817208886146545e-01 5.9375840425491333e-01 - 4.2946299910545349e-01 - <_> - - 1 0 1136 2.2743160370737314e-03 -1 -2 1137 - 3.9340821094810963e-03 - - 5.9545767307281494e-01 4.7922229766845703e-01 - 5.8561331033706665e-01 - <_> - - 1 0 1138 8.1451907753944397e-03 -1 -2 1139 - -5.2763288840651512e-03 - - 3.5734778642654419e-01 4.0260228514671326e-01 - 5.7647430896759033e-01 - <_> - - 1 0 1140 -8.3787851035594940e-03 -1 -2 1141 - 1.5621910570189357e-03 - - 4.9813330173492432e-01 4.7365880012512207e-01 - 5.5836081504821777e-01 - <_> - - 1 0 1142 3.2318739686161280e-03 -1 -2 1143 - 6.6804019734263420e-03 - - 6.1674368381500244e-01 4.1314241290092468e-01 - 6.2806951999664307e-01 - <_> - - 0 1 1144 -3.3396480139344931e-03 -1 -2 1145 - -2.0933480560779572e-01 - - 3.4463581442832947e-01 1.0386580228805542e-01 - 5.2044892311096191e-01 - <_> - - 1 0 1146 6.3805822283029556e-03 -1 -2 1147 - -6.0137799009680748e-03 - - 2.1674020588397980e-01 6.7383992671966553e-01 - 4.8966509103775024e-01 - <_> - - 1 0 1148 -8.1756077706813812e-03 -1 -2 1149 - 6.3951779156923294e-04 - - 5.1779150962829590e-01 4.8196458816528320e-01 - 5.4644381999969482e-01 - <_> - - 1 0 1150 1.0127760469913483e-03 -1 -2 1151 - 4.9784599104896188e-04 - - 3.4235960245132446e-01 4.4884610176086426e-01 - 5.9126710891723633e-01 - <_> - - 1 0 1152 1.3596490316558629e-04 -1 -2 1153 - 1.3571660034358501e-02 - - 5.5688631534576416e-01 5.1610678434371948e-01 - 1.7130009829998016e-01 - <_> - - 1 0 1154 3.0259079721872695e-05 -1 -2 1155 - -3.2625840976834297e-03 - - 4.9162039160728455e-01 6.4046627283096313e-01 - 2.8590849041938782e-01 - <_> - - 1 0 1156 -1.9217010412830859e-04 -1 -2 1157 - 2.1993879228830338e-02 - - 5.4592829942703247e-01 4.7157138586044312e-01 - 5.6900751590728760e-01 - <_> - - 1 0 1158 7.8907777788117528e-04 -1 -2 1159 - 5.0893891602754593e-04 - - 3.2798269391059875e-01 4.3020078539848328e-01 - 5.6960451602935791e-01 - <_> - - 1 0 1160 1.1662710312521085e-04 -1 -2 1161 - 8.0604078248143196e-03 - - 5.3872352838516235e-01 5.0214231014251709e-01 - 5.9653222560882568e-01 - <_> - - 1 0 1162 9.5925969071686268e-04 -1 -2 1163 - -1.9526129588484764e-02 - - 3.4734940528869629e-01 6.4755451679229736e-01 - 4.6437820792198181e-01 - <_> - 78 - 3.8236038208007812e+01 - - <_> - - 0 1 1164 4.1242439299821854e-02 -1 -2 1165 - 1.5626709908246994e-02 - - 3.3933150768280029e-01 5.1041001081466675e-01 - 7.7728152275085449e-01 - <_> - - 0 1 1166 2.9947189614176750e-04 -1 -2 1167 - -1.0037609608843923e-03 - - 3.6646738648414612e-01 5.4056507349014282e-01 - 3.9262050390243530e-01 - <_> - - 0 1 1168 6.8128242855891585e-04 -1 -2 1169 - 1.3098999625071883e-04 - - 4.2515191435813904e-01 4.1351449489593506e-01 - 6.9257462024688721e-01 - <_> - - 1 0 1170 3.1696720980107784e-03 -1 -2 1171 - -2.0587369799613953e-03 - - 3.4558731317520142e-01 2.2341939806938171e-01 - 5.2861189842224121e-01 - <_> - - 1 0 1172 -4.6395038953050971e-04 -1 -2 1173 - 3.5089480224996805e-03 - - 4.2065200209617615e-01 6.5029817819595337e-01 - 4.1175979375839233e-01 - <_> - - 1 0 1174 -2.3975980002433062e-03 -1 -2 1175 - 1.0901279747486115e-03 - - 3.6733010411262512e-01 2.9062381386756897e-01 - 5.4451119899749756e-01 - <_> - - 0 1 1176 -1.6524370585102588e-04 -1 -2 1177 - -4.1602319106459618e-04 - - 4.2335158586502075e-01 3.8863611221313477e-01 - 6.2691658735275269e-01 - <_> - - 0 1 1178 -2.3739910102449358e-04 -1 -2 1179 - 2.4739760905504227e-02 - - 5.5244511365890503e-01 4.9600958824157715e-01 - 5.3734910488128662e-01 - <_> - - 0 1 1180 -1.5342839993536472e-02 -1 -2 1181 - 1.1540469713509083e-02 - - 6.8494051694869995e-01 4.0372350811958313e-01 - 6.7869400978088379e-01 - <_> - - 1 0 1182 6.4230621792376041e-03 -1 -2 1183 - 1.2977809645235538e-02 - - 3.8146761059761047e-01 5.5270588397979736e-01 - 3.7449559569358826e-01 - <_> - - 0 1 1184 1.1063399724662304e-03 -1 -2 1185 - 1.3743690215051174e-03 - - 3.5209289193153381e-01 5.6419032812118530e-01 - 3.0750259757041931e-01 - <_> - - 0 1 1186 1.6233779489994049e-02 -1 -2 1187 - -8.1519351806491613e-04 - - 4.8888280987739563e-01 5.4563212394714355e-01 - 4.7435501217842102e-01 - <_> - - 0 1 1188 -9.0782493352890015e-02 -1 -2 1189 - 1.1665210127830505e-02 - - 2.9252481460571289e-01 4.6884548664093018e-01 - 6.2303477525711060e-01 - <_> - - 0 1 1190 -2.3286409676074982e-02 -1 -2 1191 - 2.1559339947998524e-03 - - 6.8958431482315063e-01 5.3558021783828735e-01 - 3.4234660863876343e-01 - <_> - - 0 1 1192 -4.3167220428586006e-03 -1 -2 1193 - 1.5610599657520652e-03 - - 5.9370762109756470e-01 4.7086599469184875e-01 - 2.7369970083236694e-01 - <_> - - 0 1 1194 1.4076639898121357e-02 -1 -2 1195 - 7.1018589660525322e-03 - - 5.2871561050415039e-01 5.3361928462982178e-01 - 3.2248139381408691e-01 - <_> - - 0 1 1196 -4.8221647739410400e-03 -1 -2 1197 - -5.3852899000048637e-03 - - 2.9839101433753967e-01 5.6239992380142212e-01 - 4.2959120869636536e-01 - <_> - - 1 0 1198 7.3483278974890709e-03 -1 -2 1199 - -3.5707519855350256e-03 - - 6.8139612674713135e-01 5.8579689264297485e-01 - 4.6034291386604309e-01 - <_> - - 1 0 1200 2.3340100888162851e-03 -1 -2 1201 - 4.7432780265808105e-03 - - 2.7448511123657227e-01 5.0475269556045532e-01 - 2.3627419769763947e-01 - <_> - - 0 1 1202 6.5055489540100098e-03 -1 -2 1203 - 1.2589249759912491e-02 - - 5.2422481775283813e-01 4.8236909508705139e-01 - 6.7525368928909302e-01 - <_> - - 0 1 1204 -6.3358368352055550e-03 -1 -2 1205 - -5.7639651931822300e-03 - - 1.7346349358558655e-01 6.3543808460235596e-01 - 4.5874750614166260e-01 - <_> - - 0 1 1206 1.3599749654531479e-03 -1 -2 1207 - 2.8404260054230690e-02 - - 4.5803809165954590e-01 5.1763808727264404e-01 - 1.2043850123882294e-01 - <_> - - 0 1 1208 -9.2958156019449234e-03 -1 -2 1209 - -1.1800320353358984e-03 - - 2.3379570245742798e-01 3.9028140902519226e-01 - 5.6529301404953003e-01 - <_> - - 0 1 1210 -2.0948140881955624e-03 -1 -2 1211 - 4.1679958812892437e-03 - - 5.5120289325714111e-01 5.4559761285781860e-01 - 4.7989490628242493e-01 - <_> - - 1 0 1212 5.4458891972899437e-03 -1 -2 1213 - -1.2766510481014848e-03 - - 6.1270868778228760e-01 5.3171318769454956e-01 - 3.8509321212768555e-01 - <_> - - 0 1 1214 5.9404270723462105e-04 -1 -2 1215 - 4.2309608310461044e-02 - - 5.4464370012283325e-01 5.2346438169479370e-01 - 2.2130440175533295e-01 - <_> - - 0 1 1216 5.6189671158790588e-03 -1 -2 1217 - 7.2401198558509350e-03 - - 4.9161979556083679e-01 1.4714759588241577e-01 - 4.8528939485549927e-01 - <_> - - 0 1 1218 -4.5610670931637287e-03 -1 -2 1219 - 4.5506159949582070e-05 - - 2.7737739682197571e-01 4.6264618635177612e-01 - 5.7680791616439819e-01 - <_> - - 0 1 1220 -6.1903791502118111e-03 -1 -2 1221 - 8.1186462193727493e-04 - - 1.6442899405956268e-01 4.7785910964012146e-01 - 6.2618649005889893e-01 - <_> - - 0 1 1222 1.3779809698462486e-02 -1 -2 1223 - 1.1290319962427020e-03 - - 5.2573078870773315e-01 5.4980480670928955e-01 - 3.9831069111824036e-01 - <_> - - 0 1 1224 -1.0610350000206381e-04 -1 -2 1225 - 1.6695790691301227e-04 - - 4.0335190296173096e-01 4.1493400931358337e-01 - 5.7953411340713501e-01 - <_> - - 1 0 1226 1.1290319962427020e-03 -1 -2 1227 - -1.2019349634647369e-01 - - 3.9341148734092712e-01 7.3400482535362244e-02 - 5.2025860548019409e-01 - <_> - - 0 1 1228 -1.5230740420520306e-02 -1 -2 1229 - 3.5759829916059971e-03 - - 3.7495058774948120e-01 5.0781500339508057e-01 - 6.6060662269592285e-01 - <_> - - 0 1 1230 1.3479460030794144e-02 -1 -2 1231 - -2.1162950433790684e-03 - - 4.5477110147476196e-01 3.3110061287879944e-01 - 5.3842592239379883e-01 - <_> - - 0 1 1232 -1.7877709120512009e-02 -1 -2 1233 - 1.0931970318779349e-03 - - 6.5132528543472290e-01 5.2647650241851807e-01 - 3.4569910168647766e-01 - <_> - - 0 1 1234 -3.0553159303963184e-03 -1 -2 1235 - 3.6365049891173840e-03 - - 6.2686139345169067e-01 5.3992128372192383e-01 - 4.3453970551490784e-01 - <_> - - 0 1 1236 9.7896481747739017e-05 -1 -2 1237 - -3.2714448752813041e-04 - - 3.8356059789657593e-01 3.3376678824424744e-01 - 5.5391657352447510e-01 - <_> - - 1 0 1238 4.3425030889920890e-04 -1 -2 1239 - 1.4005579985678196e-02 - - 5.7882702350616455e-01 5.2750778198242188e-01 - 2.7011251449584961e-01 - <_> - - 0 1 1240 -9.2654931358993053e-04 -1 -2 1241 - 3.9504268206655979e-03 - - 5.8522802591323853e-01 4.7283369302749634e-01 - 3.3139181137084961e-01 - <_> - - 1 0 1242 -5.8086868375539780e-04 -1 -2 1243 - -1.2018020264804363e-02 - - 4.2588108777999878e-01 5.6097871065139771e-01 - 4.8951920866966248e-01 - <_> - - 0 1 1244 -1.4521540701389313e-01 -1 -2 1245 - -6.6049019806087017e-03 - - 4.3894480913877487e-02 4.2291709780693054e-01 - 5.6162929534912109e-01 - <_> - - 1 0 1246 -3.4909751266241074e-02 -1 -2 1247 - 3.7478420417755842e-03 - - 4.7881281375885010e-01 4.8002821207046509e-01 - 5.8013892173767090e-01 - <_> - - 1 0 1248 3.3038031309843063e-02 -1 -2 1249 - 3.6872599739581347e-03 - - 7.0781761407852173e-01 4.4496241211891174e-01 - 5.9577310085296631e-01 - <_> - - 0 1 1250 -4.5311939902603626e-03 -1 -2 1251 - 4.1058510541915894e-03 - - 4.1770470142364502e-01 5.3729480504989624e-01 - 3.7369269132614136e-01 - <_> - - 0 1 1252 -8.7599847465753555e-03 -1 -2 1253 - -2.3003309965133667e-02 - - 6.6588079929351807e-01 2.6479220390319824e-01 - 5.1018178462982178e-01 - <_> - - 0 1 1254 5.3664818406105042e-03 -1 -2 1255 - 3.8971770554780960e-02 - - 4.5486348867416382e-01 5.1570618152618408e-01 - 3.4364390373229980e-01 - <_> - - 0 1 1256 -2.7767190709710121e-02 -1 -2 1257 - -9.8894089460372925e-03 - - 2.3543910682201385e-01 6.8877410888671875e-01 - 5.1110517978668213e-01 - <_> - - 0 1 1258 -3.2073140610009432e-03 -1 -2 1259 - -6.7484978353604674e-04 - - 5.4388678073883057e-01 5.4511487483978271e-01 - 4.8313531279563904e-01 - <_> - - 0 1 1260 -5.1947520114481449e-03 -1 -2 1261 - -2.6169899501837790e-04 - - 2.1134190261363983e-01 5.2736818790435791e-01 - 3.9925870299339294e-01 - <_> - - 0 1 1262 2.2421479225158691e-03 -1 -2 1263 - -1.2139769969508052e-03 - - 4.6882608532905579e-01 5.5042350292205811e-01 - 4.3848711252212524e-01 - <_> - - 0 1 1264 -2.9469770379364491e-03 -1 -2 1265 - -3.9291830034926534e-04 - - 3.8928470015525818e-01 6.0017228126525879e-01 - 4.5616629719734192e-01 - <_> - - 1 0 1266 6.2550729513168335e-01 -1 -2 1267 - 9.7744520753622055e-03 - - 6.8125613033771515e-02 4.8130258917808533e-01 - 5.6206572055816650e-01 - <_> - - 1 0 1268 9.4378247857093811e-02 -1 -2 1269 - -1.9560910295695066e-03 - - 6.6632293164730072e-02 3.5882329940795898e-01 - 5.2954071760177612e-01 - <_> - - 0 1 1270 9.0652769431471825e-03 -1 -2 1271 - 4.2138071148656309e-04 - - 4.8226881027221680e-01 4.6703329682350159e-01 - 5.6831127405166626e-01 - <_> - - 1 0 1272 -4.4220191193744540e-04 -1 -2 1273 - -4.7313501127064228e-03 - - 5.3607952594757080e-01 6.1372458934783936e-01 - 3.1880891323089600e-01 - <_> - - 0 1 1274 1.5395509544759989e-03 -1 -2 1275 - 2.4315000046044588e-03 - - 4.4877201318740845e-01 4.8941668868064880e-01 - 6.7166537046432495e-01 - <_> - - 0 1 1276 -1.5581619925796986e-02 -1 -2 1277 - 1.0816920548677444e-03 - - 3.3367419242858887e-01 4.7182199358940125e-01 - 5.9606271982192993e-01 - <_> - - 0 1 1278 -2.2197659127414227e-03 -1 -2 1279 - -9.3048671260476112e-04 - - 3.5885548591613770e-01 6.2187129259109497e-01 - 4.8173001408576965e-01 - <_> - - 0 1 1280 -4.7418707981705666e-03 -1 -2 1281 - -6.2950369901955128e-03 - - 2.5500270724296570e-01 6.7280787229537964e-01 - 5.0510638952255249e-01 - <_> - - 0 1 1282 3.5216049291193485e-03 -1 -2 1283 - -2.4289379362016916e-03 - - 5.4019099473953247e-01 5.4194617271423340e-01 - 4.3471428751945496e-01 - <_> - - 0 1 1284 -2.5261470582336187e-03 -1 -2 1285 - -1.4817339833825827e-03 - - 6.9706249237060547e-01 3.2634168863296509e-01 - 4.9178731441497803e-01 - <_> - - 0 1 1286 -2.2474530339241028e-01 -1 -2 1287 - 2.8342509176582098e-03 - - 7.2937291115522385e-03 4.5792299509048462e-01 - 5.3798812627792358e-01 - <_> - - 0 1 1288 -2.0821610465645790e-02 -1 -2 1289 - 1.4896340144332498e-04 - - 6.0240888595581055e-01 3.3361440896987915e-01 - 4.9628159403800964e-01 - <_> - - 0 1 1290 -3.3524499740451574e-03 -1 -2 1291 - -3.7279881536960602e-02 - - 3.5587510466575623e-01 1.6985629498958588e-01 - 5.2089858055114746e-01 - <_> - - 1 0 1292 1.3896770542487502e-04 -1 -2 1293 - -3.1912620761431754e-04 - - 5.5906862020492554e-01 5.8487337827682495e-01 - 3.7958368659019470e-01 - <_> - - 1 0 1294 5.4003461264073849e-04 -1 -2 1295 - 3.8956850767135620e-03 - - 5.6702882051467896e-01 5.1826947927474976e-01 - 3.3277091383934021e-01 - <_> - - 1 0 1296 1.6084529925137758e-03 -1 -2 1297 - -5.7474587811157107e-04 - - 5.4104858636856079e-01 6.0226422548294067e-01 - 3.6446440219879150e-01 - <_> - - 1 0 1298 1.3435039669275284e-02 -1 -2 1299 - 2.1368139423429966e-03 - - 3.4412819147109985e-01 5.2924340963363647e-01 - 2.7470758557319641e-01 - <_> - - 1 0 1300 1.4157629571855068e-02 -1 -2 1301 - 5.3884391672909260e-03 - - 8.0278682708740234e-01 5.2223151922225952e-01 - 3.5867279767990112e-01 - <_> - - 0 1 1302 8.8013410568237305e-03 -1 -2 1303 - 3.8858849438838661e-04 - - 4.9003869295120239e-01 4.6810561418533325e-01 - 5.7219529151916504e-01 - <_> - - 0 1 1304 -2.2143588867038488e-03 -1 -2 1305 - -8.4642972797155380e-03 - - 5.3888058662414551e-01 6.6755378246307373e-01 - 3.4484419226646423e-01 - <_> - - 1 0 1306 1.5044390223920345e-02 -1 -2 1307 - 7.6346402056515217e-03 - - 9.2396140098571777e-01 4.8848968744277954e-01 - 6.3060528039932251e-01 - <_> - - 1 0 1308 3.3895121305249631e-04 -1 -2 1309 - 2.1157610171940178e-04 - - 3.9974310994148254e-01 5.6639820337295532e-01 - 3.9729809761047363e-01 - <_> - - 1 0 1310 -2.7514949440956116e-02 -1 -2 1311 - 5.1603060215711594e-02 - - 5.2010637521743774e-01 5.1407301425933838e-01 - 1.2451309710741043e-01 - <_> - - 1 0 1312 3.7510651163756847e-03 -1 -2 1313 - -2.1457639522850513e-03 - - 3.8020950555801392e-01 3.3094480633735657e-01 - 5.4745388031005859e-01 - <_> - - 1 0 1314 -5.8178009930998087e-04 -1 -2 1315 - -9.3638541875407100e-04 - - 4.8926019668579102e-01 5.9373992681503296e-01 - 4.6646690368652344e-01 - <_> - - 1 0 1316 4.1667491197586060e-02 -1 -2 1317 - -6.7763780243694782e-03 - - 7.0213532447814941e-01 3.2227510213851929e-01 - 5.0683951377868652e-01 - <_> - - 1 0 1318 -2.9170580673962831e-03 -1 -2 1319 - 3.2789530814625323e-04 - - 4.7177010774612427e-01 4.5093831419944763e-01 - 5.6511628627777100e-01 - <_> - 91 - 4.4682968139648438e+01 - - <_> - - 0 1 1320 1.1729800142347813e-02 -1 -2 1321 - 1.1712179984897375e-03 - - 3.8052248954772949e-01 3.1400179862976074e-01 - 6.8581461906433105e-01 - <_> - - 1 0 1322 9.3555096536874771e-03 -1 -2 1323 - 1.6570610459893942e-03 - - 6.8346732854843140e-01 2.9924729466438293e-01 - 5.4756778478622437e-01 - <_> - - 1 0 1324 -1.3387809740379453e-03 -1 -2 1325 - 1.7580550047568977e-04 - - 2.9414069652557373e-01 3.8969779014587402e-01 - 5.8729708194732666e-01 - <_> - - 0 1 1326 -2.9473248869180679e-03 -1 -2 1327 - 8.3220899105072021e-03 - - 3.5765719413757324e-01 5.2324008941650391e-01 - 3.2310879230499268e-01 - <_> - - 1 0 1328 7.4366689659655094e-03 -1 -2 1329 - -2.1322889369912446e-04 - - 6.7156732082366943e-01 5.4705417156219482e-01 - 3.8633960485458374e-01 - <_> - - 0 1 1330 -7.8024631366133690e-03 -1 -2 1331 - 5.6611228501424193e-04 - - 2.7714601159095764e-01 4.6891361474990845e-01 - 5.8519637584686279e-01 - <_> - - 0 1 1332 -9.2346500605344772e-03 -1 -2 1333 - -1.4676499631605111e-05 - - 2.7043971419334412e-01 5.6225502490997314e-01 - 3.5793170332908630e-01 - <_> - - 0 1 1334 9.7007937729358673e-03 -1 -2 1335 - -3.5320650786161423e-03 - - 4.1738718748092651e-01 4.1950130462646484e-01 - 5.5494689941406250e-01 - <_> - - 1 0 1336 2.1616410464048386e-02 -1 -2 1337 - 3.4567608963698149e-03 - - 2.8573909401893616e-01 6.0245329141616821e-01 - 4.3775078654289246e-01 - <_> - - 0 1 1338 2.2914320230484009e-02 -1 -2 1339 - 3.4328910987824202e-03 - - 4.6893501281738281e-01 4.6646049618721008e-01 - 5.7625621557235718e-01 - <_> - - 0 1 1340 -8.6510833352804184e-03 -1 -2 1341 - 1.4510039472952485e-03 - - 6.3817399740219116e-01 3.7114879488945007e-01 - 5.5307507514953613e-01 - <_> - - 0 1 1342 7.8191719949245453e-03 -1 -2 1343 - 2.0798550394829363e-04 - - 5.2643620967864990e-01 3.7305128574371338e-01 - 5.4457312822341919e-01 - <_> - - 0 1 1344 -3.9962218143045902e-03 -1 -2 1345 - -1.5010139577498194e-05 - - 2.4381700158119202e-01 5.3246712684631348e-01 - 3.6829888820648193e-01 - <_> - - 0 1 1346 -4.2428788729012012e-03 -1 -2 1347 - 9.1374982148408890e-03 - - 6.4814740419387817e-01 4.8961588740348816e-01 - 6.5588432550430298e-01 - <_> - - 1 0 1348 8.8254585862159729e-03 -1 -2 1349 - 9.4092212384566665e-04 - - 3.6138701438903809e-01 5.5028957128524780e-01 - 3.6325180530548096e-01 - <_> - - 0 1 1350 -1.2503350153565407e-02 -1 -2 1351 - 8.6759645491838455e-03 - - 2.2611320018768311e-01 4.9878901243209839e-01 - 6.8471962213516235e-01 - <_> - - 0 1 1352 -1.0416760109364986e-02 -1 -2 1353 - 2.7432460337877274e-03 - - 2.4462990462779999e-01 3.5115250945091248e-01 - 5.3998267650604248e-01 - <_> - - 0 1 1354 -4.2385691776871681e-03 -1 -2 1355 - 1.8325870856642723e-02 - - 6.8236732482910156e-01 4.8915800452232361e-01 - 7.1356189250946045e-01 - <_> - - 0 1 1356 -2.4334540590643883e-02 -1 -2 1357 - 4.6469361404888332e-04 - - 3.5225218534469604e-01 4.0498688817024231e-01 - 5.5158257484436035e-01 - <_> - - 1 0 1358 3.4260009415447712e-03 -1 -2 1359 - -2.5827318895608187e-03 - - 4.1267699003219604e-01 2.8994289040565491e-01 - 5.3864318132400513e-01 - <_> - - 1 0 1360 1.0545699624344707e-03 -1 -2 1361 - -9.1257691383361816e-04 - - 3.7713441252708435e-01 5.8273869752883911e-01 - 4.2675569653511047e-01 - <_> - - 0 1 1362 2.6589010376483202e-03 -1 -2 1363 - 4.8598358407616615e-03 - - 4.6881249547004700e-01 4.8539221286773682e-01 - 6.1636447906494141e-01 - <_> - - 1 0 1364 8.0638676881790161e-03 -1 -2 1365 - -7.5898370705544949e-03 - - 1.7491950094699860e-01 6.8261897563934326e-01 - 4.8940700292587280e-01 - <_> - - 0 1 1366 3.6368070868775249e-04 -1 -2 1367 - 6.2594950199127197e-02 - - 4.6145960688591003e-01 5.1830172538757324e-01 - 2.6866960525512695e-01 - <_> - - 0 1 1368 -4.9753207713365555e-03 -1 -2 1369 - -2.0880119409412146e-03 - - 1.7584669589996338e-01 6.3693821430206299e-01 - 4.9300441145896912e-01 - <_> - - 1 0 1370 9.5644511748105288e-04 -1 -2 1371 - -3.1721461564302444e-02 - - 4.1393989324569702e-01 6.0455572605133057e-01 - 4.8163640499114990e-01 - <_> - - 0 1 1372 1.2898689601570368e-03 -1 -2 1373 - 9.8405163735151291e-03 - - 5.4508107900619507e-01 2.9240009188652039e-01 - 6.6996061801910400e-01 - <_> - - 1 0 1374 1.2237089686095715e-03 -1 -2 1375 - -8.4232585504651070e-03 - - 6.2828367948532104e-01 5.9865701198577881e-01 - 4.8525801301002502e-01 - <_> - - 0 1 1376 -7.2726322105154395e-04 -1 -2 1377 - 4.6842931769788265e-03 - - 3.3400490880012512e-01 5.1689237356185913e-01 - 2.6794800162315369e-01 - <_> - - 0 1 1378 -1.0379579616710544e-03 -1 -2 1379 - 9.1342730447649956e-03 - - 5.9257918596267700e-01 5.4377281665802002e-01 - 4.3468001484870911e-01 - <_> - - 0 1 1380 1.4971119817346334e-03 -1 -2 1381 - 1.5762320253998041e-03 - - 4.1295009851455688e-01 4.5228740572929382e-01 - 6.5562921762466431e-01 - <_> - - 0 1 1382 8.7496247142553329e-03 -1 -2 1383 - -8.5103599121794105e-04 - - 4.5320340991020203e-01 3.7859839200973511e-01 - 5.4169750213623047e-01 - <_> - - 0 1 1384 -1.7325570806860924e-02 -1 -2 1385 - -8.3266440778970718e-03 - - 6.8842482566833496e-01 3.0913260579109192e-01 - 5.2436548471450806e-01 - <_> - - 0 1 1386 1.5157909729168750e-05 -1 -2 1387 - 1.8041470320895314e-03 - - 4.7657939791679382e-01 4.7253859043121338e-01 - 5.7165551185607910e-01 - <_> - - 1 0 1388 3.0691560823470354e-03 -1 -2 1389 - -5.2225510444259271e-05 - - 2.1433599293231964e-01 5.6532102823257446e-01 - 4.3851110339164734e-01 - <_> - - 1 0 1390 1.0072169970953837e-04 -1 -2 1391 - 1.3573700562119484e-04 - - 5.9247761964797974e-01 4.5734488964080811e-01 - 5.7693827152252197e-01 - <_> - - 1 0 1392 9.2137878527864814e-04 -1 -2 1393 - 3.0316581251099706e-04 - - 5.9926092624664307e-01 3.6100810766220093e-01 - 5.0493258237838745e-01 - <_> - - 1 0 1394 3.9582479745149612e-02 -1 -2 1395 - 4.7519680112600327e-02 - - 1.5384890139102936e-01 5.2161407470703125e-01 - 1.4283910393714905e-01 - <_> - - 1 0 1396 1.8871759995818138e-02 -1 -2 1397 - -3.9876459049992263e-04 - - 2.8255069255828857e-01 4.0350168943405151e-01 - 5.4377931356430054e-01 - <_> - - 0 1 1398 4.6556600136682391e-04 -1 -2 1399 - 6.7090610973536968e-03 - - 4.6689969301223755e-01 5.3313547372817993e-01 - 4.1365718841552734e-01 - <_> - - 0 1 1400 -1.8931160448119044e-03 -1 -2 1401 - -1.3056949712336063e-02 - - 7.1551632881164551e-01 3.1178998947143555e-01 - 5.2084398269653320e-01 - <_> - - 1 0 1402 -1.9484119547996670e-04 -1 -2 1403 - 1.5093220099515747e-05 - - 4.6376588940620422e-01 4.5616531372070312e-01 - 5.4452341794967651e-01 - <_> - - 1 0 1404 -7.1617960202274844e-06 -1 -2 1405 - 3.0164679628796875e-04 - - 4.1931080818176270e-01 5.9662377834320068e-01 - 4.1005000472068787e-01 - <_> - - 0 1 1406 4.4195181690156460e-03 -1 -2 1407 - -7.3984181508421898e-03 - - 4.8450559377670288e-01 6.2068462371826172e-01 - 4.9312090873718262e-01 - <_> - - 1 0 1408 -7.8031201846897602e-03 -1 -2 1409 - -1.0731429792940617e-02 - - 5.2824628353118896e-01 9.1048341989517212e-01 - 3.4559220075607300e-01 - <_> - - 0 1 1410 1.4246780192479491e-03 -1 -2 1411 - -8.2717568147927523e-05 - - 4.7085541486740112e-01 5.6516230106353760e-01 - 4.7310239076614380e-01 - <_> - - 1 0 1412 4.4803409837186337e-03 -1 -2 1413 - 3.0789140146225691e-03 - - 6.1758869886398315e-01 5.1395332813262939e-01 - 3.4230878949165344e-01 - <_> - - 1 0 1414 -1.1310289846733212e-03 -1 -2 1415 - -1.0410690447315574e-03 - - 4.9182820320129395e-01 5.9420871734619141e-01 - 4.9230429530143738e-01 - <_> - - 1 0 1416 1.1648540385067463e-03 -1 -2 1417 - 9.0057362103834748e-04 - - 6.4052718877792358e-01 4.5043969154357910e-01 - 6.1920768022537231e-01 - <_> - - 0 1 1418 6.8781538866460323e-03 -1 -2 1419 - -3.5283900797367096e-02 - - 5.3748130798339844e-01 2.2471010684967041e-01 - 5.2171707153320312e-01 - <_> - - 0 1 1420 -1.3320200378075242e-03 -1 -2 1421 - -2.3177571129053831e-03 - - 2.5547030568122864e-01 3.7925159931182861e-01 - 5.2432268857955933e-01 - <_> - - 0 1 1422 2.1332940377760679e-04 -1 -2 1423 - 1.3467900454998016e-02 - - 3.8603371381759644e-01 5.3806877136230469e-01 - 4.1783639788627625e-01 - <_> - - 0 1 1424 -1.2829169863834977e-03 -1 -2 1425 - 5.1571638323366642e-04 - - 6.1336231231689453e-01 4.0285378694534302e-01 - 5.5368518829345703e-01 - <_> - - 0 1 1426 3.9254198782145977e-03 -1 -2 1427 - -3.3780589699745178e-02 - - 5.2799212932586670e-01 2.3346750438213348e-01 - 5.1759117841720581e-01 - <_> - - 0 1 1428 -3.7853721529245377e-02 -1 -2 1429 - -4.0752900531515479e-04 - - 1.0748530179262161e-01 5.3459298610687256e-01 - 4.1989380121231079e-01 - <_> - - 0 1 1430 -3.1193809118121862e-03 -1 -2 1431 - -1.5714969485998154e-02 - - 3.8558250665664673e-01 3.3351901173591614e-01 - 5.2632021903991699e-01 - <_> - - 0 1 1432 -7.8525702701881528e-04 -1 -2 1433 - -2.8750501223839819e-04 - - 5.8603972196578979e-01 5.4377847909927368e-01 - 3.7161049246788025e-01 - <_> - - 1 0 1434 2.8016859665513039e-02 -1 -2 1435 - -1.9018839811906219e-03 - - 3.3307549357414246e-01 5.3665977716445923e-01 - 4.6937939524650574e-01 - <_> - - 1 0 1436 2.0647559314966202e-02 -1 -2 1437 - 4.3002571910619736e-03 - - 1.0069560259580612e-01 4.8160359263420105e-01 - 6.2156772613525391e-01 - <_> - - 0 1 1438 1.3459140434861183e-02 -1 -2 1439 - -1.0320040397346020e-02 - - 5.4619538784027100e-01 4.5784530043601990e-01 - 5.4193097352981567e-01 - <_> - - 1 0 1440 3.1990748643875122e-01 -1 -2 1441 - 9.2198798665776849e-04 - - 2.0080469548702240e-01 5.1932811737060547e-01 - 3.9121940732002258e-01 - <_> - - 0 1 1442 4.1852539288811386e-04 -1 -2 1443 - 3.5891108564101160e-04 - - 4.2997440695762634e-01 4.3445029854774475e-01 - 5.5319738388061523e-01 - <_> - - 0 1 1444 -2.0992439985275269e-01 -1 -2 1445 - -4.9328152090311050e-03 - - 1.0757210105657578e-01 5.7627969980239868e-01 - 4.5746439695358276e-01 - <_> - - 1 0 1446 2.3409130517393351e-03 -1 -2 1447 - 4.7120270319283009e-03 - - 7.4768078327178955e-01 5.2617651224136353e-01 - 4.5055508613586426e-01 - <_> - - 0 1 1448 2.8713190928101540e-02 -1 -2 1449 - -2.6156550738960505e-03 - - 4.4071030616760254e-01 4.2442709207534790e-01 - 6.8929767608642578e-01 - <_> - - 0 1 1450 -1.3558969832956791e-02 -1 -2 1451 - -3.0331799644045532e-04 - - 1.2522679567337036e-01 4.0777918696403503e-01 - 5.4428178071975708e-01 - <_> - - 0 1 1452 -5.5601762142032385e-04 -1 -2 1453 - 2.4025330785661936e-03 - - 5.3780037164688110e-01 3.1665799021720886e-01 - 5.2857381105422974e-01 - <_> - - 1 0 1454 -3.4089901018887758e-03 -1 -2 1455 - 8.0019602319225669e-04 - - 4.9052149057388306e-01 4.5227360725402832e-01 - 5.5806142091751099e-01 - <_> - - 1 0 1456 2.1901070140302181e-03 -1 -2 1457 - 3.3745369873940945e-03 - - 6.6126817464828491e-01 5.1077651977539062e-01 - 3.3869299292564392e-01 - <_> - - 1 0 1458 8.0019602319225669e-04 -1 -2 1459 - 1.7346069216728210e-02 - - 5.7075601816177368e-01 5.0160211324691772e-01 - 6.3064599037170410e-01 - <_> - - 0 1 1460 -1.9568449351936579e-03 -1 -2 1461 - -1.1229019612073898e-02 - - 3.0178061127662659e-01 6.2938511371612549e-01 - 4.5204889774322510e-01 - <_> - - 0 1 1462 -2.6608388870954514e-03 -1 -2 1463 - -1.1615100316703320e-02 - - 3.3440071344375610e-01 2.8253790736198425e-01 - 5.1509708166122437e-01 - <_> - - 0 1 1464 -9.5248602330684662e-02 -1 -2 1465 - 7.3701781220734119e-03 - - 1.3982650637626648e-01 5.2939987182617188e-01 - 2.3317280411720276e-01 - <_> - - 1 0 1466 -1.4953900128602982e-02 -1 -2 1467 - 5.7038792874664068e-04 - - 4.9404659867286682e-01 5.4665708541870117e-01 - 4.6267679333686829e-01 - <_> - - 1 0 1468 5.8516198769211769e-03 -1 -2 1469 - 2.1150549582671374e-04 - - 6.2700408697128296e-01 5.5081409215927124e-01 - 4.0618729591369629e-01 - <_> - - 1 0 1470 -6.9679190346505493e-06 -1 -2 1471 - -7.9677387839183211e-04 - - 4.0965679287910461e-01 5.6155568361282349e-01 - 4.6668860316276550e-01 - <_> - - 1 0 1472 1.9459480419754982e-02 -1 -2 1473 - -1.1160830035805702e-02 - - 2.3114809393882751e-01 3.0870118737220764e-01 - 5.5146622657775879e-01 - <_> - - 1 0 1474 1.4056149870157242e-02 -1 -2 1475 - -3.2958350493572652e-04 - - 7.0050561428070068e-01 5.7974857091903687e-01 - 4.6916508674621582e-01 - <_> - - 0 1 1476 -5.4636420682072639e-03 -1 -2 1477 - 5.8881669247057289e-05 - - 5.9285950660705566e-01 3.7413978576660156e-01 - 5.1701688766479492e-01 - <_> - - 0 1 1478 6.6343429498374462e-03 -1 -2 1479 - 4.5263409614562988e-02 - - 5.4149878025054932e-01 5.1803272962570190e-01 - 1.5296840667724609e-01 - <_> - - 0 1 1480 -8.0646127462387085e-03 -1 -2 1481 - 4.7389548853971064e-04 - - 2.5154680013656616e-01 5.1219987869262695e-01 - 3.7259489297866821e-01 - <_> - - 1 0 1482 1.4877359717502259e-05 -1 -2 1483 - 2.4321159347891808e-02 - - 5.5324357748031616e-01 4.9607661366462708e-01 - 5.9833151102066040e-01 - <_> - - 0 1 1484 6.9931396865285933e-05 -1 -2 1485 - 2.6287760119885206e-03 - - 4.1639530658721924e-01 5.8801448345184326e-01 - 3.3996629714965820e-01 - <_> - - 1 0 1486 3.8190539926290512e-03 -1 -2 1487 - -2.5989150628447533e-02 - - 7.8466212749481201e-01 3.2881140708923340e-01 - 5.1550877094268799e-01 - <_> - - 0 1 1488 1.2062400346621871e-03 -1 -2 1489 - -1.5557400183752179e-03 - - 4.5960599184036255e-01 3.1269869208335876e-01 - 7.1833992004394531e-01 - <_> - - 1 0 1490 -2.2691930644214153e-03 -1 -2 1491 - 2.3287249496206641e-04 - - 5.2740061283111572e-01 4.8786661028862000e-01 - 5.6151527166366577e-01 - <_> - - 1 0 1492 -5.5999699980020523e-03 -1 -2 1493 - -1.0496189817786217e-02 - - 5.1608121395111084e-01 5.7016140222549438e-01 - 3.2048508524894714e-01 - <_> - - 0 1 1494 -1.4814930182183161e-05 -1 -2 1495 - -6.4287078566849232e-04 - - 5.5388379096984863e-01 5.3494292497634888e-01 - 4.4721511006355286e-01 - <_> - - 0 1 1496 -1.8891949730459601e-04 -1 -2 1497 - -9.0413521975278854e-03 - - 5.0128370523452759e-01 2.5629359483718872e-01 - 4.5033830404281616e-01 - <_> - - 1 0 1498 7.9534705728292465e-03 -1 -2 1499 - -2.7908999472856522e-03 - - 2.6304998993873596e-01 5.7565087080001831e-01 - 4.8548638820648193e-01 - <_> - - 1 0 1500 3.2857100013643503e-03 -1 -2 1501 - 7.7063008211553097e-04 - - 4.0847519040107727e-01 4.0733560919761658e-01 - 5.9202408790588379e-01 - <_> - 97 - 4.7763450622558594e+01 - - <_> - - 0 1 1502 6.3021942973136902e-02 -1 -2 1503 - -2.8374609537422657e-03 - - 3.4193828701972961e-01 6.8295639753341675e-01 - 4.4045230746269226e-01 - <_> - - 0 1 1504 4.6461950987577438e-02 -1 -2 1505 - 2.9152540490031242e-02 - - 4.3917450308799744e-01 4.6010631322860718e-01 - 6.3579368591308594e-01 - <_> - - 1 0 1506 -1.4000290320836939e-05 -1 -2 1507 - -1.2757079675793648e-03 - - 3.7300100922584534e-01 3.0938240885734558e-01 - 5.9013700485229492e-01 - <_> - - 0 1 1508 1.3596529606729746e-03 -1 -2 1509 - 1.7991929780691862e-04 - - 4.3375650048255920e-01 4.2175039649009705e-01 - 5.8468478918075562e-01 - <_> - - 1 0 1510 -1.4166639630275313e-05 -1 -2 1511 - 6.0252390539972112e-05 - - 4.0846911072731018e-01 5.0872868299484253e-01 - 7.2771841287612915e-01 - <_> - - 1 0 1512 6.4320368692278862e-03 -1 -2 1513 - 4.6682319953106344e-04 - - 2.9679030179977417e-01 4.1104629635810852e-01 - 5.5812197923660278e-01 - <_> - - 0 1 1514 5.7436279021203518e-03 -1 -2 1515 - 3.2019240316003561e-03 - - 4.2873099446296692e-01 4.2661958932876587e-01 - 6.4440459012985229e-01 - <_> - - 1 0 1516 -5.7637941790744662e-04 -1 -2 1517 - -3.7901920732110739e-03 - - 4.0848249197006226e-01 3.1819209456443787e-01 - 5.2306932210922241e-01 - <_> - - 1 0 1518 4.8914109356701374e-03 -1 -2 1519 - 4.6459292061626911e-03 - - 3.5483568906784058e-01 5.6105977296829224e-01 - 2.6938489079475403e-01 - <_> - - 0 1 1520 -6.8799369037151337e-03 -1 -2 1521 - -1.8147470429539680e-02 - - 6.2354081869125366e-01 2.8619819879531860e-01 - 5.2268481254577637e-01 - <_> - - 1 0 1522 1.1409220314817503e-04 -1 -2 1523 - -5.4334272863343358e-04 - - 3.2578331232070923e-01 3.8829690217971802e-01 - 5.3411662578582764e-01 - <_> - - 0 1 1524 -2.7602489572018385e-03 -1 -2 1525 - -1.9730569329112768e-03 - - 6.3539659976959229e-01 5.8807611465454102e-01 - 4.5930901169776917e-01 - <_> - - 1 0 1526 2.4565239436924458e-03 -1 -2 1527 - 1.9392010290175676e-04 - - 3.1340101361274719e-01 5.2771317958831787e-01 - 3.6041069030761719e-01 - <_> - - 0 1 1528 7.8643016517162323e-02 -1 -2 1529 - 6.5276869572699070e-03 - - 5.2903419733047485e-01 4.6544799208641052e-01 - 6.0449051856994629e-01 - <_> - - 0 1 1530 -7.8716799616813660e-02 -1 -2 1531 - 5.7298499159514904e-03 - - 2.5411269068717957e-01 4.3669191002845764e-01 - 5.8228862285614014e-01 - <_> - - 1 0 1532 6.2386557692661881e-04 -1 -2 1533 - -8.5267230868339539e-02 - - 5.4726922512054443e-01 1.4616079628467560e-01 - 5.1818108558654785e-01 - <_> - - 1 0 1534 4.0981110185384750e-02 -1 -2 1535 - 7.7135749161243439e-03 - - 1.2701350450515747e-01 4.8326849937438965e-01 - 2.2235789895057678e-01 - <_> - - 0 1 1536 -6.8663940764963627e-03 -1 -2 1537 - 1.4559639617800713e-02 - - 5.9189289808273315e-01 4.7615069150924683e-01 - 5.7272237539291382e-01 - <_> - - 0 1 1538 -1.0064310394227505e-02 -1 -2 1539 - 3.6274080630391836e-03 - - 3.6367309093475342e-01 5.2717310190200806e-01 - 2.7405250072479248e-01 - <_> - - 0 1 1540 -2.3421540390700102e-03 -1 -2 1541 - -2.4686409160494804e-02 - - 5.4977840185165405e-01 6.0598951578140259e-01 - 4.9603140354156494e-01 - <_> - - 1 0 1542 1.9456120207905769e-04 -1 -2 1543 - 3.1714211218059063e-04 - - 3.7694650888442993e-01 4.0623620152473450e-01 - 5.6682151556015015e-01 - <_> - - 0 1 1544 2.0793990697711706e-03 -1 -2 1545 - 1.7982709687203169e-03 - - 4.6186569333076477e-01 4.8675051331520081e-01 - 6.5184497833251953e-01 - <_> - - 0 1 1546 -2.2287059982772917e-04 -1 -2 1547 - 3.2623921288177371e-04 - - 5.6775957345962524e-01 3.7107339501380920e-01 - 5.6766051054000854e-01 - <_> - - 0 1 1548 -6.6792681813240051e-02 -1 -2 1549 - -1.4869889710098505e-03 - - 2.5115218758583069e-01 3.8867509365081787e-01 - 5.2622538805007935e-01 - <_> - - 0 1 1550 -5.0454870797693729e-03 -1 -2 1551 - -4.8297587782144547e-03 - - 6.5574729442596436e-01 5.9341061115264893e-01 - 4.2859220504760742e-01 - <_> - - 1 0 1552 -1.0722599690780044e-03 -1 -2 1553 - 8.7901195511221886e-03 - - 5.4260587692260742e-01 5.3513032197952271e-01 - 4.8342779278755188e-01 - <_> - - 0 1 1554 -7.1750381030142307e-03 -1 -2 1555 - 1.1251230025663972e-03 - - 2.0671689510345459e-01 5.1122522354125977e-01 - 3.4687140583992004e-01 - <_> - - 0 1 1556 1.0634710080921650e-02 -1 -2 1557 - -1.1763219721615314e-02 - - 4.4790080189704895e-01 6.2539017200469971e-01 - 4.9689871072769165e-01 - <_> - - 1 0 1558 9.2324063181877136e-02 -1 -2 1559 - 1.8991080578416586e-03 - - 2.0313039422035217e-01 5.6187218427658081e-01 - 4.0465721487998962e-01 - <_> - - 1 0 1560 -1.0510340332984924e-02 -1 -2 1561 - -7.4531312566250563e-04 - - 4.9432641267776489e-01 5.6134277582168579e-01 - 3.8453319668769836e-01 - <_> - - 1 0 1562 8.0041000619530678e-03 -1 -2 1563 - 5.8110528625547886e-03 - - 7.7598422765731812e-01 4.6247330307960510e-01 - 6.2862771749496460e-01 - <_> - - 0 1 1564 -2.7918580919504166e-02 -1 -2 1565 - 2.1739399526268244e-03 - - 2.4093140661716461e-01 5.3455048799514771e-01 - 3.5079580545425415e-01 - <_> - - 0 1 1566 -4.0639587678015232e-03 -1 -2 1567 - 6.0017139185220003e-04 - - 6.6471010446548462e-01 4.9985098838806152e-01 - 3.0221650004386902e-01 - <_> - - 1 0 1568 1.9214770291000605e-03 -1 -2 1569 - -1.3860830105841160e-02 - - 5.9191507101058960e-01 6.3517677783966064e-01 - 4.9933108687400818e-01 - <_> - - 1 0 1570 2.3006850853562355e-02 -1 -2 1571 - -1.3857929734513164e-03 - - 1.9023360311985016e-01 5.2533692121505737e-01 - 3.9858600497245789e-01 - <_> - - 0 1 1572 1.2637410545721650e-03 -1 -2 1573 - -1.4675210230052471e-02 - - 4.6661040186882019e-01 3.8231649994850159e-01 - 5.3266328573226929e-01 - <_> - - 0 1 1574 -2.9535070061683655e-03 -1 -2 1575 - -1.7189770005643368e-03 - - 7.0636558532714844e-01 3.8134628534317017e-01 - 5.2467352151870728e-01 - <_> - - 1 0 1576 -4.2484089499339461e-04 -1 -2 1577 - -8.5248658433556557e-04 - - 4.7916388511657715e-01 4.4912180304527283e-01 - 5.3709012269973755e-01 - <_> - - 1 0 1578 8.9034568518400192e-03 -1 -2 1579 - 1.4895649655954912e-05 - - 2.0764739811420441e-01 4.4476351141929626e-01 - 5.6671631336212158e-01 - <_> - - 0 1 1580 -4.7091601300053298e-04 -1 -2 1581 - 4.3084810022264719e-04 - - 5.4650712013244629e-01 5.4932618141174316e-01 - 4.5807081460952759e-01 - <_> - - 0 1 1582 -6.3893961487337947e-04 -1 -2 1583 - -7.3733746830839664e-05 - - 5.5015718936920166e-01 5.0857907533645630e-01 - 3.3056980371475220e-01 - <_> - - 0 1 1584 -8.8991485536098480e-03 -1 -2 1585 - -1.0253350250422955e-02 - - 4.2764690518379211e-01 1.1232180148363113e-01 - 5.1527231931686401e-01 - <_> - - 0 1 1586 -5.9637490659952164e-02 -1 -2 1587 - 2.1707199513912201e-02 - - 7.3867720365524292e-01 4.9962919950485229e-01 - 1.3394139707088470e-01 - <_> - - 0 1 1588 9.9107045680284500e-03 -1 -2 1589 - -1.0998300276696682e-02 - - 4.6790120005607605e-01 6.9286561012268066e-01 - 5.0120681524276733e-01 - <_> - - 1 0 1590 7.4608891736716032e-04 -1 -2 1591 - 2.9539171373471618e-04 - - 5.8335822820663452e-01 3.8263911008834839e-01 - 5.5663508176803589e-01 - <_> - - 1 0 1592 5.0054129213094711e-02 -1 -2 1593 - -7.2330660186707973e-03 - - 3.0027210712432861e-01 5.9080427885055542e-01 - 5.0008708238601685e-01 - <_> - - 0 1 1594 -2.6863380335271358e-03 -1 -2 1595 - -1.0195849463343620e-03 - - 3.9750349521636963e-01 3.6976858973503113e-01 - 5.7561928033828735e-01 - <_> - - 0 1 1596 -2.0204920321702957e-02 -1 -2 1597 - 2.1340379025787115e-03 - - 6.3752681016921997e-01 5.3632658720016479e-01 - 4.4331708550453186e-01 - <_> - - 0 1 1598 -1.8348889425396919e-03 -1 -2 1599 - -5.9489468112587929e-03 - - 5.8289992809295654e-01 2.6806709170341492e-01 - 4.6428859233856201e-01 - <_> - - 0 1 1600 -2.3030120064504445e-04 -1 -2 1601 - 5.0581009127199650e-03 - - 5.4753202199935913e-01 5.3208339214324951e-01 - 4.6464928984642029e-01 - <_> - - 0 1 1602 -5.1950011402368546e-04 -1 -2 1603 - -6.8620947422459722e-04 - - 5.2327448129653931e-01 4.9350860714912415e-01 - 3.1031179428100586e-01 - <_> - - 0 1 1604 -7.4936267919838428e-03 -1 -2 1605 - -1.5682930126786232e-02 - - 2.8830468654632568e-01 3.6403131484985352e-01 - 5.3687548637390137e-01 - <_> - - 0 1 1606 -3.2649750355631113e-03 -1 -2 1607 - 3.8463930832222104e-04 - - 6.4686310291290283e-01 5.2596598863601685e-01 - 3.8314279913902283e-01 - <_> - - 1 0 1608 4.4492390006780624e-03 -1 -2 1609 - 2.3118320852518082e-02 - - 2.0868189632892609e-01 4.9785330891609192e-01 - 5.9612572193145752e-01 - <_> - - 1 0 1610 2.0835159812122583e-03 -1 -2 1611 - 1.1513150529935956e-03 - - 5.7464218139648438e-01 3.5868450999259949e-01 - 5.3634738922119141e-01 - <_> - - 1 0 1612 3.6104708909988403e-02 -1 -2 1613 - 3.6256198654882610e-04 - - 2.8331369161605835e-01 5.4777222871780396e-01 - 4.1105321049690247e-01 - <_> - - 0 1 1614 -3.4635469783097506e-03 -1 -2 1615 - -2.8796829283237457e-03 - - 5.9903860092163086e-01 5.7252532243728638e-01 - 4.1495120525360107e-01 - <_> - - 1 0 1616 -8.1119500100612640e-03 -1 -2 1617 - 4.5932079665362835e-03 - - 5.3963518142700195e-01 5.3797042369842529e-01 - 3.8913029432296753e-01 - <_> - - 1 0 1618 7.0014740340411663e-03 -1 -2 1619 - 8.0169539432972670e-04 - - 3.7146711349487305e-01 5.5295670032501221e-01 - 3.7558048963546753e-01 - <_> - - 1 0 1620 -8.6652329191565514e-03 -1 -2 1621 - -2.7315050829201937e-03 - - 5.0257730484008789e-01 5.8503222465515137e-01 - 4.6175739169120789e-01 - <_> - - 1 0 1622 1.3301590224727988e-03 -1 -2 1623 - -4.2648240923881531e-03 - - 5.9377008676528931e-01 5.6453680992126465e-01 - 3.9376249909400940e-01 - <_> - - 0 1 1624 6.3251499086618423e-03 -1 -2 1625 - -3.0753740575164557e-03 - - 5.1821058988571167e-01 3.0074161291122437e-01 - 5.1964038610458374e-01 - <_> - - 0 1 1626 -7.3622138006612659e-04 -1 -2 1627 - 3.0082479497650638e-05 - - 3.6975800991058350e-01 4.3275931477546692e-01 - 5.7158088684082031e-01 - <_> - - 0 1 1628 -3.8722730241715908e-03 -1 -2 1629 - 6.2879058532416821e-04 - - 3.4737130999565125e-01 5.4382592439651489e-01 - 4.4539061188697815e-01 - <_> - - 1 0 1630 1.3411579420790076e-03 -1 -2 1631 - -8.3681922405958176e-03 - - 6.5117138624191284e-01 1.4432950317859650e-01 - 4.8881998658180237e-01 - <_> - - 1 0 1632 9.3305751215666533e-04 -1 -2 1633 - -1.0746510233730078e-03 - - 3.9511090517044067e-01 3.9102658629417419e-01 - 5.3495037555694580e-01 - <_> - - 0 1 1634 -1.8610050901770592e-02 -1 -2 1635 - 1.3651419430971146e-03 - - 1.2757439911365509e-01 5.0382888317108154e-01 - 6.9513040781021118e-01 - <_> - - 0 1 1636 7.3744421824812889e-03 -1 -2 1637 - 8.4163323044776917e-03 - - 5.2534431219100952e-01 5.0112438201904297e-01 - 7.3113328218460083e-01 - <_> - - 0 1 1638 5.1413988694548607e-03 -1 -2 1639 - 4.5847031287848949e-03 - - 4.9535360932350159e-01 2.5355559587478638e-01 - 6.4624428749084473e-01 - <_> - - 1 0 1640 2.8565239161252975e-02 -1 -2 1641 - 4.3958800961263478e-04 - - 2.3307220637798309e-01 4.7022441029548645e-01 - 5.5445492267608643e-01 - <_> - - 1 0 1642 3.1459458172321320e-02 -1 -2 1643 - 5.6011630222201347e-03 - - 3.3689688891172409e-02 4.7871211171150208e-01 - 6.3383519649505615e-01 - <_> - - 0 1 1644 7.1835669223219156e-04 -1 -2 1645 - -5.5303089320659637e-03 - - 5.4314869642257690e-01 4.1058328747749329e-01 - 5.4039907455444336e-01 - <_> - - 1 0 1646 1.4129279879853129e-03 -1 -2 1647 - 2.5530709535814822e-04 - - 3.1055399775505066e-01 4.2544719576835632e-01 - 5.4471540451049805e-01 - <_> - - 1 0 1648 3.1966410460881889e-04 -1 -2 1649 - 5.0411392003297806e-03 - - 6.1183619499206543e-01 5.2900421619415283e-01 - 4.2247870564460754e-01 - <_> - - 0 1 1650 7.7617880888283253e-03 -1 -2 1651 - 2.9374631121754646e-03 - - 4.3153458833694458e-01 6.6292631626129150e-01 - 3.0289649963378906e-01 - <_> - - 1 0 1652 -1.6497720498591661e-03 -1 -2 1653 - -5.8834417723119259e-03 - - 5.4918527603149414e-01 3.1885540485382080e-01 - 5.1842892169952393e-01 - <_> - - 1 0 1654 8.7459187489002943e-04 -1 -2 1655 - -1.5308779664337635e-02 - - 3.3288308978080750e-01 3.9236080646514893e-01 - 5.2351391315460205e-01 - <_> - - 1 0 1656 3.2292451709508896e-02 -1 -2 1657 - -4.3842519517056644e-04 - - 5.9776467084884644e-01 4.5416879653930664e-01 - 5.3694289922714233e-01 - <_> - - 1 0 1658 1.5429529594257474e-03 -1 -2 1659 - -2.4733028840273619e-03 - - 6.3181412220001221e-01 3.4906330704689026e-01 - 4.7590249776840210e-01 - <_> - - 1 0 1660 2.0994939841330051e-03 -1 -2 1661 - -5.7541108690202236e-03 - - 5.8871978521347046e-01 5.9613317251205444e-01 - 4.8419830203056335e-01 - <_> - - 0 1 1662 -1.0233130306005478e-02 -1 -2 1663 - 2.2554509341716766e-01 - - 1.7054040729999542e-01 4.7793799638748169e-01 - 9.7879663109779358e-02 - <_> - - 1 0 1664 2.9666559770703316e-02 -1 -2 1665 - -2.8518449980765581e-03 - - 5.8222240209579468e-01 5.4596269130706787e-01 - 4.6100661158561707e-01 - <_> - - 1 0 1666 9.7465328872203827e-04 -1 -2 1667 - 1.4044740055396687e-05 - - 3.6703228950500488e-01 4.3023860454559326e-01 - 5.6917107105255127e-01 - <_> - - 0 1 1668 -1.7579430714249611e-02 -1 -2 1669 - -5.2381679415702820e-02 - - 6.9173210859298706e-01 7.1100401878356934e-01 - 5.0601547956466675e-01 - <_> - - 0 1 1670 -1.1242110282182693e-02 -1 -2 1671 - -3.6728400737047195e-03 - - 8.7691891193389893e-01 6.5191918611526489e-01 - 4.5460689067840576e-01 - <_> - - 0 1 1672 3.5082760732620955e-03 -1 -2 1673 - 6.1679710634052753e-03 - - 5.3298658132553101e-01 5.2204591035842896e-01 - 2.9535189270973206e-01 - <_> - - 1 0 1674 -9.7009900491684675e-04 -1 -2 1675 - -1.0957010090351105e-02 - - 5.0486332178115845e-01 5.8373582363128662e-01 - 3.0200859904289246e-01 - <_> - - 0 1 1676 -8.3272513002157211e-03 -1 -2 1677 - 2.9798380637657829e-05 - - 3.1580638885498047e-01 4.3863898515701294e-01 - 5.4432111978530884e-01 - <_> - - 1 0 1678 2.8244039276614785e-04 -1 -2 1679 - -8.1364117795601487e-04 - - 5.6253957748413086e-01 5.2811980247497559e-01 - 3.4014078974723816e-01 - <_> - - 1 0 1680 1.8008040497079492e-03 -1 -2 1681 - -6.9944779388606548e-03 - - 3.4716591238975525e-01 4.4816970825195312e-01 - 5.3857702016830444e-01 - <_> - - 0 1 1682 4.5625398342963308e-05 -1 -2 1683 - -7.3189922841265798e-04 - - 4.4925129413604736e-01 4.1673120856285095e-01 - 6.0211020708084106e-01 - <_> - - 0 1 1684 -2.9980219551362097e-04 -1 -2 1685 - -2.9060940505587496e-05 - - 4.1484281420707703e-01 5.5920898914337158e-01 - 4.0732109546661377e-01 - <_> - - 0 1 1686 -5.9742690064013004e-04 -1 -2 1687 - 1.4831830048933625e-04 - - 6.0889142751693726e-01 5.2983051538467407e-01 - 3.7619501352310181e-01 - <_> - - 1 0 1688 -2.9441029764711857e-03 -1 -2 1689 - 1.3741210103034973e-01 - - 4.7160848975181580e-01 5.1013368368148804e-01 - 4.6746801584959030e-02 - <_> - - 0 1 1690 -8.8414177298545837e-02 -1 -2 1691 - 7.0610277354717255e-02 - - 1.1818689852952957e-01 5.1190632581710815e-01 - 7.7784419059753418e-01 - <_> - - 0 1 1692 -7.7188978902995586e-03 -1 -2 1693 - 1.5115399844944477e-02 - - 1.8741349875926971e-01 4.9800279736518860e-01 - 7.0058178901672363e-01 - <_> - - 0 1 1694 1.0671879863366485e-03 -1 -2 1695 - 7.0487911580130458e-04 - - 4.4822388887405396e-01 6.2657529115676880e-01 - 4.4026550650596619e-01 - <_> - 90 - 4.4251281738281250e+01 - - <_> - - 1 0 1696 -9.8690733313560486e-02 -1 -2 1697 - 6.2373418360948563e-02 - - 3.9994749426841736e-01 5.2477848529815674e-01 - 8.1935757398605347e-01 - <_> - - 0 1 1698 1.9496519817039371e-03 -1 -2 1699 - -8.9139147894456983e-04 - - 3.5298168659210205e-01 5.8527278900146484e-01 - 3.2459780573844910e-01 - <_> - - 0 1 1700 -5.5150408297777176e-04 -1 -2 1701 - -1.1721949558705091e-03 - - 3.8928169012069702e-01 4.3350520730018616e-01 - 6.5206241607666016e-01 - <_> - - 1 0 1702 -7.4480642797425389e-04 -1 -2 1703 - -2.6264840271323919e-03 - - 4.0411350131034851e-01 5.6249821186065674e-01 - 3.9675250649452209e-01 - <_> - - 0 1 1704 -3.9712688885629177e-04 -1 -2 1705 - 3.5984949208796024e-03 - - 3.8561120629310608e-01 5.9978890419006348e-01 - 4.2416140437126160e-01 - <_> - - 1 0 1706 5.3080618381500244e-03 -1 -2 1707 - 9.6319877775385976e-04 - - 6.6601687669754028e-01 4.4813790917396545e-01 - 5.5834877490997314e-01 - <_> - - 0 1 1708 5.0776469288393855e-04 -1 -2 1709 - 3.6223160568624735e-03 - - 3.5354590415954590e-01 3.4098070859909058e-01 - 5.4206877946853638e-01 - <_> - - 0 1 1710 -6.2061410397291183e-02 -1 -2 1711 - 6.4387189922854304e-04 - - 1.9340839982032776e-01 4.0836268663406372e-01 - 5.4902219772338867e-01 - <_> - - 1 0 1712 2.6239909231662750e-02 -1 -2 1713 - 8.1940297968685627e-04 - - 2.2857080399990082e-01 4.6486678719520569e-01 - 6.0173559188842773e-01 - <_> - - 1 0 1714 2.3833119485061616e-04 -1 -2 1715 - -1.5869759954512119e-03 - - 3.5980388522148132e-01 4.2596510052680969e-01 - 5.4764348268508911e-01 - <_> - - 0 1 1716 -6.7263417877256870e-03 -1 -2 1717 - 1.1006110347807407e-02 - - 6.5072381496429443e-01 5.1494097709655762e-01 - 3.3629849553108215e-01 - <_> - - 1 0 1718 7.1445819921791553e-03 -1 -2 1719 - -4.7233798541128635e-03 - - 2.6729300618171692e-01 5.6521821022033691e-01 - 4.2981448769569397e-01 - <_> - - 1 0 1720 9.8437406122684479e-03 -1 -2 1721 - 1.5124640412977897e-05 - - 1.1518859863281250e-01 4.3735980987548828e-01 - 5.6121289730072021e-01 - <_> - - 0 1 1722 3.9908871054649353e-02 -1 -2 1723 - 5.3903679363429546e-03 - - 5.2046489715576172e-01 4.8134678602218628e-01 - 6.3612091541290283e-01 - <_> - - 0 1 1724 -3.9908871054649353e-02 -1 -2 1725 - 5.3903679363429546e-03 - - 1.5068709850311279e-01 4.5816949009895325e-01 - 6.2002408504486084e-01 - <_> - - 1 0 1726 6.7005190066993237e-03 -1 -2 1727 - -1.2623789720237255e-02 - - 3.4322351217269897e-01 3.0882269144058228e-01 - 5.2267378568649292e-01 - <_> - - 1 0 1728 1.1806610040366650e-02 -1 -2 1729 - -3.4257229417562485e-03 - - 7.1879392862319946e-01 3.1208148598670959e-01 - 5.0658440589904785e-01 - <_> - - 0 1 1730 3.9385299896821380e-04 -1 -2 1731 - 3.4388188272714615e-02 - - 4.7545841336250305e-01 5.2616578340530396e-01 - 3.3501741290092468e-01 - <_> - - 0 1 1732 -7.5009986758232117e-02 -1 -2 1733 - 4.9022492021322250e-04 - - 1.7134809494018555e-01 4.7258019447326660e-01 - 5.9564691781997681e-01 - <_> - - 0 1 1734 -8.5525289177894592e-03 -1 -2 1735 - 1.3135520566720515e-04 - - 6.5582227706909180e-01 4.8354008793830872e-01 - 5.5869138240814209e-01 - <_> - - 1 0 1736 4.7948658466339111e-03 -1 -2 1737 - 2.0124691072851419e-03 - - 2.6457059383392334e-01 3.6579450964927673e-01 - 5.1247721910476685e-01 - <_> - - 0 1 1738 -1.1785479635000229e-01 -1 -2 1739 - 1.5575019642710686e-03 - - 2.3856540024280548e-01 5.4904741048812866e-01 - 4.2747479677200317e-01 - <_> - - 0 1 1740 -1.5573759563267231e-02 -1 -2 1741 - -2.1854790393263102e-03 - - 6.9389009475708008e-01 3.6459881067276001e-01 - 5.0925260782241821e-01 - <_> - - 0 1 1742 2.9272339306771755e-03 -1 -2 1743 - 6.4663668163120747e-03 - - 4.6858081221580505e-01 4.9734100699424744e-01 - 7.7260971069335938e-01 - <_> - - 0 1 1744 -7.6140360906720161e-03 -1 -2 1745 - 4.1512572206556797e-03 - - 6.8774658441543579e-01 4.7885251045227051e-01 - 6.9216579198837280e-01 - <_> - - 0 1 1746 2.7711640577763319e-03 -1 -2 1747 - -1.2836109846830368e-02 - - 5.4818397760391235e-01 3.8001629710197449e-01 - 5.2044928073883057e-01 - <_> - - 0 1 1748 -2.4380050599575043e-03 -1 -2 1749 - 2.1713329479098320e-03 - - 2.5824350118637085e-01 4.9611631035804749e-01 - 3.2152029871940613e-01 - <_> - - 1 0 1750 6.2800728483125567e-04 -1 -2 1751 - -9.7982389852404594e-03 - - 5.4604238271713257e-01 6.0465437173843384e-01 - 4.9399220943450928e-01 - <_> - - 1 0 1752 7.3543828912079334e-03 -1 -2 1753 - -1.4665040187537670e-02 - - 5.2910941839218140e-01 5.4461228847503662e-01 - 3.5673621296882629e-01 - <_> - - 0 1 1754 3.0244510620832443e-02 -1 -2 1755 - -5.6660208851099014e-02 - - 5.5183291435241699e-01 6.9309788942337036e-01 - 5.0933879613876343e-01 - <_> - - 0 1 1756 -5.6967479176819324e-03 -1 -2 1757 - 3.0806770548224449e-02 - - 3.2015261054039001e-01 4.9892461299896240e-01 - 2.2770540416240692e-01 - <_> - - 0 1 1758 2.2748769260942936e-03 -1 -2 1759 - 2.0436900667846203e-03 - - 4.8109310865402222e-01 5.2838671207427979e-01 - 3.2559248805046082e-01 - <_> - - 0 1 1760 -8.6277956143021584e-03 -1 -2 1761 - 6.5113382879644632e-04 - - 6.2665361166000366e-01 5.0971370935440063e-01 - 3.1919100880622864e-01 - <_> - - 0 1 1762 8.8188261725008488e-04 -1 -2 1763 - -1.4594909735023975e-02 - - 4.5495858788490295e-01 2.6450389623641968e-01 - 5.1538681983947754e-01 - <_> - - 0 1 1764 -1.2304580304771662e-03 -1 -2 1765 - -2.1867299801670015e-04 - - 6.1975848674774170e-01 5.4691988229751587e-01 - 4.2068558931350708e-01 - <_> - - 0 1 1766 -1.0909959673881531e-03 -1 -2 1767 - 3.5210378700867295e-04 - - 4.1407600045204163e-01 5.4766088724136353e-01 - 4.1550210118293762e-01 - <_> - - 0 1 1768 -7.2563779540359974e-03 -1 -2 1769 - 1.4701850013807416e-03 - - 7.1604692935943604e-01 5.2408081293106079e-01 - 3.7296628952026367e-01 - <_> - - 0 1 1770 1.1472719779703766e-04 -1 -2 1771 - 3.0506469774991274e-03 - - 4.0337988734245300e-01 5.2639859914779663e-01 - 3.5600930452346802e-01 - <_> - - 0 1 1772 2.6269949739798903e-04 -1 -2 1773 - -3.6365550477057695e-03 - - 4.5697999000549316e-01 3.0425709486007690e-01 - 5.8682537078857422e-01 - <_> - - 1 0 1774 -8.4893293678760529e-03 -1 -2 1775 - 5.8107408694922924e-03 - - 4.9141570925712585e-01 4.9185299873352051e-01 - 6.2669628858566284e-01 - <_> - - 1 0 1776 7.5583951547741890e-04 -1 -2 1777 - -2.2017690353095531e-03 - - 5.6332361698150635e-01 5.5539160966873169e-01 - 3.8276460766792297e-01 - <_> - - 0 1 1778 2.7908938936889172e-03 -1 -2 1779 - -1.8228569533675909e-03 - - 5.4986977577209473e-01 4.3822830915451050e-01 - 5.4240328073501587e-01 - <_> - - 0 1 1780 -7.2495508939027786e-03 -1 -2 1781 - -6.8744522286579013e-04 - - 2.8881219029426575e-01 3.4726551175117493e-01 - 5.0763708353042603e-01 - <_> - - 0 1 1782 2.5174440816044807e-03 -1 -2 1783 - -1.0151379741728306e-02 - - 4.6612051129341125e-01 3.7447750568389893e-01 - 5.2940011024475098e-01 - <_> - - 1 0 1784 -4.1399952024221420e-03 -1 -2 1785 - -4.7078551724553108e-03 - - 4.6604850888252258e-01 4.1750618815422058e-01 - 6.9163060188293457e-01 - <_> - - 1 0 1786 4.1981041431427002e-02 -1 -2 1787 - -1.4272999949753284e-02 - - 2.0182150602340698e-01 7.5111979246139526e-01 - 5.0320839881896973e-01 - <_> - - 1 0 1788 4.0869521908462048e-03 -1 -2 1789 - 1.7606799956411123e-03 - - 2.5045138597488403e-01 3.3014011383056641e-01 - 5.2183371782302856e-01 - <_> - - 0 1 1790 1.2550549581646919e-04 -1 -2 1791 - -2.9503209516406059e-03 - - 4.6144428849220276e-01 4.6199500560760498e-01 - 5.2470302581787109e-01 - <_> - - 0 1 1792 -1.1312420247122645e-03 -1 -2 1793 - -1.6983180539682508e-03 - - 6.3143682479858398e-01 3.4013068675994873e-01 - 5.0555270910263062e-01 - <_> - - 1 0 1794 -1.1457820422947407e-02 -1 -2 1795 - -8.4962565451860428e-03 - - 4.9399960041046143e-01 2.9654508829116821e-01 - 5.1943677663803101e-01 - <_> - - 1 0 1796 1.1919089592993259e-02 -1 -2 1797 - 6.4416420646011829e-03 - - 7.8869980573654175e-01 5.1069867610931396e-01 - 2.9671460390090942e-01 - <_> - - 0 1 1798 -8.7857811013236642e-04 -1 -2 1799 - -2.0312711130827665e-03 - - 5.7143712043762207e-01 4.4812008738517761e-01 - 5.3849118947982788e-01 - <_> - - 0 1 1800 -1.5262430533766747e-03 -1 -2 1801 - 4.2860880494117737e-03 - - 6.1935687065124512e-01 4.3398851156234741e-01 - 7.6972991228103638e-01 - <_> - - 1 0 1802 3.5010920837521553e-03 -1 -2 1803 - 1.2587670236825943e-02 - - 3.1713891029357910e-01 5.2466988563537598e-01 - 4.2412081360816956e-01 - <_> - - 0 1 1804 2.6207490009255707e-04 -1 -2 1805 - 4.4701730075757951e-05 - - 4.2318999767303467e-01 4.1741389036178589e-01 - 5.9196037054061890e-01 - <_> - - 0 1 1806 7.8084698179736733e-04 -1 -2 1807 - 8.8851212058216333e-04 - - 4.2773890495300293e-01 3.7201610207557678e-01 - 5.2268189191818237e-01 - <_> - - 0 1 1808 2.3369069676846266e-03 -1 -2 1809 - 1.6688359901309013e-03 - - 5.4780668020248413e-01 3.6286789178848267e-01 - 6.1500048637390137e-01 - <_> - - 0 1 1810 3.0844469438306987e-04 -1 -2 1811 - 3.4617560449987650e-03 - - 4.7470751404762268e-01 4.5801380276679993e-01 - 5.5856817960739136e-01 - <_> - - 0 1 1812 1.8961310386657715e-02 -1 -2 1813 - 1.7347310483455658e-01 - - 5.2988010644912720e-01 3.6983850598335266e-01 - 8.4986197948455811e-01 - <_> - - 1 0 1814 2.0020549709443003e-04 -1 -2 1815 - 1.0967060225084424e-03 - - 5.5656617879867554e-01 4.7957131266593933e-01 - 6.2862598896026611e-01 - <_> - - 0 1 1816 1.5107099898159504e-04 -1 -2 1817 - -3.4463501069694757e-03 - - 4.0524059534072876e-01 6.1730152368545532e-01 - 4.4142639636993408e-01 - <_> - - 1 0 1818 8.5176620632410049e-03 -1 -2 1819 - -3.5812109708786011e-02 - - 3.5705709457397461e-01 3.1513288617134094e-01 - 5.2527028322219849e-01 - <_> - - 0 1 1820 -2.1155400201678276e-02 -1 -2 1821 - 8.9890940580517054e-04 - - 6.1247211694717407e-01 5.1699757575988770e-01 - 3.5962718725204468e-01 - <_> - - 1 0 1822 -1.5613760333508253e-03 -1 -2 1823 - 6.7120860330760479e-04 - - 4.9149879813194275e-01 4.5462110638618469e-01 - 5.3958117961883545e-01 - <_> - - 0 1 1824 -2.1597029641270638e-02 -1 -2 1825 - -2.4947229772806168e-02 - - 1.9031339883804321e-01 6.9740772247314453e-01 - 4.9677160382270813e-01 - <_> - - 0 1 1826 1.8725979607552290e-03 -1 -2 1827 - 6.3912719488143921e-03 - - 4.7489479184150696e-01 5.1801782846450806e-01 - 2.9243218898773193e-01 - <_> - - 0 1 1828 -9.1552399098873138e-03 -1 -2 1829 - 2.1715660113841295e-03 - - 7.6658701896667480e-01 5.2155512571334839e-01 - 3.3657190203666687e-01 - <_> - - 1 0 1830 1.2330369791015983e-03 -1 -2 1831 - -4.0785901364870369e-04 - - 6.2609577178955078e-01 4.5335099101066589e-01 - 5.3864890336990356e-01 - <_> - - 0 1 1832 4.6437609125860035e-04 -1 -2 1833 - -1.1600199650274590e-04 - - 4.1034960746765137e-01 5.8303910493850708e-01 - 4.3041059374809265e-01 - <_> - - 0 1 1834 -1.2718720361590385e-02 -1 -2 1835 - 8.9431880041956902e-05 - - 2.1325829625129700e-01 4.8728910088539124e-01 - 5.4589152336120605e-01 - <_> - - 0 1 1836 -3.3913689549081028e-04 -1 -2 1837 - -1.8026340752840042e-02 - - 3.9743649959564209e-01 7.5685507059097290e-01 - 5.0456118583679199e-01 - <_> - - 1 0 1838 6.9179181009531021e-03 -1 -2 1839 - -1.1839679791592062e-04 - - 3.9662998914718628e-01 4.1980829834938049e-01 - 5.4358041286468506e-01 - <_> - - 0 1 1840 -3.9474181830883026e-03 -1 -2 1841 - 6.0050919273635373e-05 - - 6.3694578409194946e-01 5.2695667743682861e-01 - 3.8122430443763733e-01 - <_> - - 1 0 1842 9.1423643752932549e-03 -1 -2 1843 - 2.1305440168362111e-04 - - 4.1567629575729370e-01 3.5235330462455750e-01 - 5.3494542837142944e-01 - <_> - - 1 0 1844 -2.0855850016232580e-04 -1 -2 1845 - 1.3130389852449298e-03 - - 4.4033220410346985e-01 6.0581612586975098e-01 - 4.4682189822196960e-01 - <_> - - 1 0 1846 -2.9134768992662430e-03 -1 -2 1847 - 2.9645769391208887e-03 - - 4.8257058858871460e-01 4.8359981179237366e-01 - 6.0392779111862183e-01 - <_> - - 1 0 1848 1.7772549763321877e-03 -1 -2 1849 - -7.7136349864304066e-03 - - 6.8718272447586060e-01 2.8422209620475769e-01 - 5.1454281806945801e-01 - <_> - - 1 0 1850 5.1027478184551001e-04 -1 -2 1851 - 1.7460630042478442e-03 - - 6.0244262218475342e-01 4.7566100955009460e-01 - 5.7211542129516602e-01 - <_> - - 1 0 1852 3.8068278809078038e-04 -1 -2 1853 - 2.8228890150785446e-03 - - 4.9310690164566040e-01 3.3116981387138367e-01 - 6.2275981903076172e-01 - <_> - - 1 0 1854 -5.3000478073954582e-03 -1 -2 1855 - 4.4951299059903249e-05 - - 5.2320927381515503e-01 3.9952319860458374e-01 - 5.3147977590560913e-01 - <_> - - 0 1 1856 3.2752458937466145e-03 -1 -2 1857 - -2.8162579983472824e-03 - - 4.4816198945045471e-01 3.9079719781875610e-01 - 6.6716408729553223e-01 - <_> - - 0 1 1858 1.4112279750406742e-03 -1 -2 1859 - 8.3062034100294113e-03 - - 5.3570109605789185e-01 4.7709658741950989e-01 - 5.5700999498367310e-01 - <_> - - 0 1 1860 2.2164839319884777e-03 -1 -2 1861 - -4.9868631176650524e-03 - - 4.9471241235733032e-01 5.2413070201873779e-01 - 2.5126549601554871e-01 - <_> - - 1 0 1862 -3.6664260551333427e-03 -1 -2 1863 - -1.0581229813396931e-02 - - 4.6195539832115173e-01 6.3017189502716064e-01 - 4.9730318784713745e-01 - <_> - - 1 0 1864 7.3366491124033928e-03 -1 -2 1865 - -3.9318940252996981e-04 - - 2.8709700703620911e-01 4.2528051137924194e-01 - 5.5792468786239624e-01 - <_> - - 0 1 1866 -8.1375334411859512e-03 -1 -2 1867 - 2.4809150490909815e-03 - - 5.7473158836364746e-01 5.2033740282058716e-01 - 3.9035668969154358e-01 - <_> - - 1 0 1868 8.8749779388308525e-04 -1 -2 1869 - -4.2194919660687447e-04 - - 5.5343210697174072e-01 5.3380441665649414e-01 - 3.9258408546447754e-01 - <_> - - 0 1 1870 -7.9790111631155014e-03 -1 -2 1871 - 1.1439629597589374e-03 - - 4.1443160176277161e-01 4.7013729810714722e-01 - 5.2817362546920776e-01 - <_> - - 1 0 1872 7.5542130507528782e-03 -1 -2 1873 - 1.0288399644196033e-03 - - 2.5272560119628906e-01 5.6051462888717651e-01 - 4.2978560924530029e-01 - <_> - - 1 0 1874 -1.7234670231118798e-03 -1 -2 1875 - 5.7586699724197388e-01 - - 4.8396828770637512e-01 5.1105028390884399e-01 - 8.0489329993724823e-02 - <_> - 109 - 5.3755569458007812e+01 - - <_> - - 0 1 1876 6.6640521399676800e-03 -1 -2 1877 - 8.9905522763729095e-03 - - 3.8289201259613037e-01 4.8584291338920593e-01 - 7.3549592494964600e-01 - <_> - - 1 0 1878 5.7154200039803982e-03 -1 -2 1879 - 1.1257929727435112e-03 - - 6.7232239246368408e-01 4.4295778870582581e-01 - 6.0707777738571167e-01 - <_> - - 1 0 1880 -9.1789010912179947e-04 -1 -2 1881 - -1.0492859873920679e-03 - - 3.0763450264930725e-01 5.5936437845230103e-01 - 3.6510229110717773e-01 - <_> - - 0 1 1882 3.5453929740469903e-05 -1 -2 1883 - 2.9015709878876805e-04 - - 4.2779681086540222e-01 4.5835450291633606e-01 - 5.2846831083297729e-01 - <_> - - 1 0 1884 1.6071660502348095e-04 -1 -2 1885 - -5.2961107576265931e-04 - - 3.7981921434402466e-01 3.8504371047019958e-01 - 5.9396880865097046e-01 - <_> - - 0 1 1886 2.6682569296099246e-04 -1 -2 1887 - -1.3492540165316314e-04 - - 4.1230249404907227e-01 5.7605999708175659e-01 - 4.2376458644866943e-01 - <_> - - 0 1 1888 -1.0841679759323597e-02 -1 -2 1889 - 1.2077829800546169e-02 - - 3.9299210906028748e-01 5.7619231939315796e-01 - 2.7804449200630188e-01 - <_> - - 0 1 1890 2.2128869313746691e-03 -1 -2 1891 - -1.5266190283000469e-02 - - 4.7945070266723633e-01 7.4055880308151245e-02 - 5.1535779237747192e-01 - <_> - - 1 0 1892 6.7929533543065190e-05 -1 -2 1893 - 1.7633590323384851e-04 - - 5.8587378263473511e-01 3.5676109790802002e-01 - 5.5989629030227661e-01 - <_> - - 1 0 1894 8.1311381654813886e-04 -1 -2 1895 - 3.2630451023578644e-03 - - 5.3468507528305054e-01 4.7825369238853455e-01 - 5.4567539691925049e-01 - <_> - - 0 1 1896 -3.9503918960690498e-03 -1 -2 1897 - -3.9864578866399825e-04 - - 2.8318119049072266e-01 5.4852157831192017e-01 - 4.1596978902816772e-01 - <_> - - 0 1 1898 -1.1432520113885403e-02 -1 -2 1899 - 5.3339172154664993e-03 - - 5.6391012668609619e-01 4.5969840884208679e-01 - 5.9312427043914795e-01 - <_> - - 1 0 1900 8.3193257451057434e-03 -1 -2 1901 - -4.2479918920435011e-04 - - 3.2306200265884399e-01 3.7952938675880432e-01 - 5.4086112976074219e-01 - <_> - - 0 1 1902 -1.1189430207014084e-01 -1 -2 1903 - -7.5553781352937222e-03 - - 1.1322979629039764e-01 6.3393700122833252e-01 - 4.8387709259986877e-01 - <_> - - 0 1 1904 -7.0337029173970222e-03 -1 -2 1905 - -1.4833680354058743e-02 - - 5.6652551889419556e-01 6.7514181137084961e-01 - 4.1409450769424438e-01 - <_> - - 1 0 1906 8.7506724521517754e-03 -1 -2 1907 - 1.6645010327920318e-03 - - 3.5612589120864868e-01 5.3472799062728882e-01 - 3.6497798562049866e-01 - <_> - - 1 0 1908 9.4900820404291153e-03 -1 -2 1909 - 1.1133110383525491e-03 - - 2.7546560764312744e-01 4.2259928584098816e-01 - 5.6291788816452026e-01 - <_> - - 0 1 1910 9.4940755516290665e-03 -1 -2 1911 - -1.5396620146930218e-03 - - 4.9060368537902832e-01 4.0070518851280212e-01 - 5.3807091712951660e-01 - <_> - - 1 0 1912 1.3434959948062897e-01 -1 -2 1913 - -9.4940755516290665e-03 - - 2.2146719694137573e-01 7.3531562089920044e-01 - 5.0050330162048340e-01 - <_> - - 1 0 1914 2.0011790096759796e-02 -1 -2 1915 - -1.8875009845942259e-03 - - 3.3279061317443848e-01 3.9152890443801880e-01 - 5.4018497467041016e-01 - <_> - - 1 0 1916 7.1842782199382782e-03 -1 -2 1917 - 1.6976969782263041e-03 - - 7.1766048669815063e-01 4.5269781351089478e-01 - 6.0769128799438477e-01 - <_> - - 1 0 1918 4.9219978973269463e-03 -1 -2 1919 - 1.1803199537098408e-02 - - 2.5698339939117432e-01 4.9996379017829895e-01 - 5.9582281112670898e-01 - <_> - - 0 1 1920 -9.7703449428081512e-03 -1 -2 1921 - 2.1174899302423000e-03 - - 3.4590938687324524e-01 4.5151269435882568e-01 - 5.8297157287597656e-01 - <_> - - 0 1 1922 9.4801411032676697e-03 -1 -2 1923 - -2.6078789960592985e-03 - - 4.8073920607566833e-01 3.4622168540954590e-01 - 5.2015948295593262e-01 - <_> - - 0 1 1924 -5.7252747938036919e-03 -1 -2 1925 - -8.2325618714094162e-03 - - 6.5998530387878418e-01 2.8218281269073486e-01 - 5.1252847909927368e-01 - <_> - - 0 1 1926 8.9571950957179070e-04 -1 -2 1927 - -1.5021569561213255e-04 - - 4.8838189244270325e-01 4.8299181461334229e-01 - 5.4287171363830566e-01 - <_> - - 0 1 1928 4.8489659093320370e-04 -1 -2 1929 - -9.6192650496959686e-02 - - 4.4345989823341370e-01 2.2566360235214233e-01 - 5.9562277793884277e-01 - <_> - - 0 1 1930 -1.1053519556298852e-03 -1 -2 1931 - -1.0215040296316147e-01 - - 4.5272240042686462e-01 2.8443491458892822e-01 - 5.1864528656005859e-01 - <_> - - 1 0 1932 3.0147889629006386e-03 -1 -2 1933 - 7.6131648384034634e-03 - - 3.8089990615844727e-01 5.7186990976333618e-01 - 4.2625638842582703e-01 - <_> - - 1 0 1934 1.5197630273178220e-03 -1 -2 1935 - -1.4197279699146748e-02 - - 5.9427189826965332e-01 7.7311038970947266e-01 - 4.9976539611816406e-01 - <_> - - 0 1 1936 -1.3818879611790180e-02 -1 -2 1937 - -5.0701329018920660e-04 - - 6.6811382770538330e-01 3.3056080341339111e-01 - 4.7499749064445496e-01 - <_> - - 0 1 1938 -9.3537531793117523e-03 -1 -2 1939 - -9.4771059229969978e-03 - - 2.8609329462051392e-01 6.1888831853866577e-01 - 4.8421001434326172e-01 - <_> - - 1 0 1940 1.6923650400713086e-03 -1 -2 1941 - 5.8652542065829039e-04 - - 6.0702490806579590e-01 3.7826898694038391e-01 - 5.3681969642639160e-01 - <_> - - 0 1 1942 -2.5826620403677225e-03 -1 -2 1943 - -2.7307639829814434e-03 - - 3.6902099847793579e-01 3.8571149110794067e-01 - 5.3181087970733643e-01 - <_> - - 1 0 1944 2.1871570497751236e-02 -1 -2 1945 - -1.5010299648565706e-05 - - 2.3270089924335480e-01 5.5607229471206665e-01 - 4.3014100193977356e-01 - <_> - - 1 0 1946 5.3583700209856033e-03 -1 -2 1947 - 5.0057549960911274e-03 - - 6.7676377296447754e-01 5.1949042081832886e-01 - 3.6128538846969604e-01 - <_> - - 0 1 1948 -1.9030070398002863e-03 -1 -2 1949 - -7.8506693243980408e-03 - - 3.2378450036048889e-01 1.1948519945144653e-01 - 4.9917238950729370e-01 - <_> - - 1 0 1950 -2.7093670796602964e-03 -1 -2 1951 - 1.4138079714030027e-03 - - 4.8549601435661316e-01 4.8723229765892029e-01 - 5.9035778045654297e-01 - <_> - - 1 0 1952 9.0300198644399643e-03 -1 -2 1953 - -9.7925681620836258e-04 - - 6.5473157167434692e-01 5.8492732048034668e-01 - 4.5542308688163757e-01 - <_> - - 1 0 1954 1.3984439428895712e-03 -1 -2 1955 - 8.3372107474133372e-04 - - 4.0646260976791382e-01 5.3995430469512939e-01 - 4.1528099775314331e-01 - <_> - - 1 0 1956 1.0551059618592262e-02 -1 -2 1957 - 8.8344102550763637e-05 - - 1.7966809868812561e-01 4.2518630623817444e-01 - 5.4135227203369141e-01 - <_> - - 1 0 1958 -4.1022308170795441e-02 -1 -2 1959 - 7.5065628625452518e-03 - - 5.2281248569488525e-01 4.8537430167198181e-01 - 6.0934442281723022e-01 - <_> - - 1 0 1960 4.1022308170795441e-02 -1 -2 1961 - -5.3961377125233412e-04 - - 2.2050240635871887e-01 5.6927317380905151e-01 - 4.4687569141387939e-01 - <_> - - 0 1 1962 -6.8696036934852600e-02 -1 -2 1963 - -1.8447940237820148e-03 - - 1.4833140373229980e-01 6.2112838029861450e-01 - 4.9666011333465576e-01 - <_> - - 0 1 1964 -6.0959919355809689e-03 -1 -2 1965 - -4.2068301700055599e-03 - - 2.2946719825267792e-01 6.4070910215377808e-01 - 4.7485628724098206e-01 - <_> - - 1 0 1966 -7.1332789957523346e-04 -1 -2 1967 - 1.1756779998540878e-01 - - 5.3549361228942871e-01 5.1369780302047729e-01 - 1.0595739819109440e-02 - <_> - - 0 1 1968 5.9354289987822995e-05 -1 -2 1969 - -6.3173691742122173e-03 - - 3.7118038535118103e-01 1.7120739817619324e-01 - 5.0617581605911255e-01 - <_> - - 1 0 1970 1.4941499568521976e-02 -1 -2 1971 - -2.0789399277418852e-03 - - 6.7291188240051270e-01 4.4106459617614746e-01 - 5.4440277814865112e-01 - <_> - - 0 1 1972 -7.0736219640821218e-04 -1 -2 1973 - -3.1247111037373543e-03 - - 5.5689108371734619e-01 5.0238692760467529e-01 - 3.5624051094055176e-01 - <_> - - 1 0 1974 -7.8919378574937582e-04 -1 -2 1975 - 1.0179580189287663e-02 - - 5.4567861557006836e-01 5.5451387166976929e-01 - 4.6223109960556030e-01 - <_> - - 1 0 1976 -2.7506109327077866e-03 -1 -2 1977 - 1.0601329617202282e-02 - - 4.9425360560417175e-01 2.9612338542938232e-01 - 5.9643387794494629e-01 - <_> - - 0 1 1978 5.1466780714690685e-03 -1 -2 1979 - 7.6321147382259369e-02 - - 5.4952287673950195e-01 5.1739591360092163e-01 - 2.9402169585227966e-01 - <_> - - 0 1 1980 -1.5027689514681697e-03 -1 -2 1981 - 1.2266670353710651e-02 - - 3.1062999367713928e-01 4.6511501073837280e-01 - 6.8466138839721680e-01 - <_> - - 1 0 1982 -3.1118579208850861e-02 -1 -2 1983 - 2.8905589133501053e-02 - - 5.2260571718215942e-01 5.1822441816329956e-01 - 2.7054280042648315e-01 - <_> - - 1 0 1984 4.7598380595445633e-02 -1 -2 1985 - 3.0808549374341965e-02 - - 1.1095120012760162e-01 4.9386250972747803e-01 - 1.4041109383106232e-01 - <_> - - 1 0 1986 -2.1277810446918011e-04 -1 -2 1987 - 7.8969962894916534e-02 - - 4.3923568725585938e-01 5.2165520191192627e-01 - 2.2941139340400696e-01 - <_> - - 0 1 1988 -1.0257950052618980e-02 -1 -2 1989 - 1.2604889925569296e-03 - - 6.1766529083251953e-01 5.2362227439880371e-01 - 3.3289659023284912e-01 - <_> - - 1 0 1990 -3.3490460366010666e-02 -1 -2 1991 - -5.9202767442911863e-04 - - 4.8661869764328003e-01 4.1164070367813110e-01 - 5.3956401348114014e-01 - <_> - - 1 0 1992 3.0320750738610514e-05 -1 -2 1993 - -5.4369680583477020e-04 - - 5.6107360124588013e-01 5.6213891506195068e-01 - 3.4612038731575012e-01 - <_> - - 1 0 1994 -3.3490460366010666e-02 -1 -2 1995 - -5.9202767442911863e-04 - - 4.8967620730400085e-01 4.3054041266441345e-01 - 5.3407138586044312e-01 - <_> - - 0 1 1996 2.0550889894366264e-03 -1 -2 1997 - -4.4353571720421314e-03 - - 5.5449998378753662e-01 6.0385400056838989e-01 - 3.7465929985046387e-01 - <_> - - 1 0 1998 -8.4170423448085785e-02 -1 -2 1999 - 6.7419027909636497e-03 - - 5.0073480606079102e-01 5.2980971336364746e-01 - 4.7161450982093811e-01 - <_> - - 1 0 2000 1.0278150439262390e-02 -1 -2 2001 - 5.8800862170755863e-03 - - 6.2693750858306885e-01 5.1548278331756592e-01 - 3.8130408525466919e-01 - <_> - - 1 0 2002 -6.9679190346505493e-06 -1 -2 2003 - 8.2419527461752295e-04 - - 4.4402399659156799e-01 4.6975341439247131e-01 - 5.4855042695999146e-01 - <_> - - 0 1 2004 -5.5268318392336369e-03 -1 -2 2005 - 9.6128671430051327e-04 - - 5.5136048793792725e-01 3.6186391115188599e-01 - 5.8384567499160767e-01 - <_> - - 1 0 2006 2.4810510221868753e-03 -1 -2 2007 - -1.0480589699000120e-03 - - 2.5232228636741638e-01 4.1172578930854797e-01 - 5.3929960727691650e-01 - <_> - - 0 1 2008 -6.1287907883524895e-03 -1 -2 2009 - 1.1682329932227731e-04 - - 6.7263299226760864e-01 5.0411927700042725e-01 - 3.6077290773391724e-01 - <_> - - 0 1 2010 -3.9909478276968002e-02 -1 -2 2011 - 1.5859459526836872e-03 - - 1.5637390315532684e-01 4.8919808864593506e-01 - 5.7798451185226440e-01 - <_> - - 0 1 2012 -2.2690229117870331e-02 -1 -2 2013 - 2.0916070789098740e-03 - - 2.1868790686130524e-01 4.7715771198272705e-01 - 6.0992312431335449e-01 - <_> - - 0 1 2014 -2.4715419858694077e-02 -1 -2 2015 - -1.3419450260698795e-02 - - 3.4639969468116760e-01 3.6306929588317871e-01 - 5.2521961927413940e-01 - <_> - - 0 1 2016 -6.0629472136497498e-03 -1 -2 2017 - -2.0921030081808567e-03 - - 6.6663217544555664e-01 3.3995470404624939e-01 - 5.0356978178024292e-01 - <_> - - 0 1 2018 2.5961859151721001e-02 -1 -2 2019 - 1.7908669542521238e-04 - - 5.0368028879165649e-01 5.4185307025909424e-01 - 4.3189769983291626e-01 - <_> - - 0 1 2020 -3.1546850223094225e-03 -1 -2 2021 - -1.1397759662941098e-03 - - 7.2210252285003662e-01 3.3209729194641113e-01 - 5.0244337320327759e-01 - <_> - - 0 1 2022 -4.7840211540460587e-02 -1 -2 2023 - 4.1577088995836675e-04 - - 1.9387650489807129e-01 4.8021888732910156e-01 - 5.7307147979736328e-01 - <_> - - 0 1 2024 -4.4247039477340877e-04 -1 -2 2025 - 1.4479350065812469e-03 - - 4.2625150084495544e-01 5.7191711664199829e-01 - 4.0641531348228455e-01 - <_> - - 0 1 2026 1.5701510012149811e-02 -1 -2 2027 - 2.7805729769170284e-04 - - 4.9957260489463806e-01 5.2892869710922241e-01 - 4.5817288756370544e-01 - <_> - - 0 1 2028 -2.9010509606450796e-03 -1 -2 2029 - 2.0830519497394562e-04 - - 6.0121482610702515e-01 5.0579768419265747e-01 - 3.5994321107864380e-01 - <_> - - 1 0 2030 -5.1530029624700546e-02 -1 -2 2031 - 1.7163449956569821e-04 - - 4.9917969107627869e-01 4.6754699945449829e-01 - 5.3747731447219849e-01 - <_> - - 1 0 2032 2.3614279925823212e-02 -1 -2 2033 - -5.6427798699587584e-04 - - 6.5864789485931396e-01 3.8532960414886475e-01 - 5.1960402727127075e-01 - <_> - - 1 0 2034 6.6903959959745407e-03 -1 -2 2035 - -4.8789530992507935e-03 - - 6.0042357444763184e-01 3.2932278513908386e-01 - 5.2452367544174194e-01 - <_> - - 0 1 2036 -6.8537332117557526e-03 -1 -2 2037 - 9.9893810693174601e-04 - - 2.5659140944480896e-01 4.6154940128326416e-01 - 5.9424322843551636e-01 - <_> - - 0 1 2038 -1.3354700058698654e-04 -1 -2 2039 - 1.0165109997615218e-03 - - 5.4873758554458618e-01 4.5783591270446777e-01 - 5.4269278049468994e-01 - <_> - - 1 0 2040 9.1216771397739649e-04 -1 -2 2041 - 1.0080259526148438e-03 - - 3.9394611120223999e-01 4.0497899055480957e-01 - 5.5207037925720215e-01 - <_> - - 1 0 2042 -1.3102490629535168e-04 -1 -2 2043 - 5.5228749988600612e-04 - - 4.8790889978408813e-01 4.8449438810348511e-01 - 5.5128258466720581e-01 - <_> - - 1 0 2044 -1.2130969844292849e-04 -1 -2 2045 - -1.5112989785848185e-05 - - 4.3679711222648621e-01 6.4259552955627441e-01 - 4.8818269371986389e-01 - <_> - - 1 0 2046 -4.0125829400494695e-04 -1 -2 2047 - -6.5766851184889674e-04 - - 5.3720992803573608e-01 5.8345532417297363e-01 - 4.8690780997276306e-01 - <_> - - 1 0 2048 6.2220421386882663e-04 -1 -2 2049 - 1.4663359615951777e-03 - - 3.8246369361877441e-01 4.8134881258010864e-01 - 6.9667392969131470e-01 - <_> - - 0 1 2050 -4.9547709524631500e-02 -1 -2 2051 - 1.3017569435760379e-03 - - 5.3927659988403320e-02 5.3374558687210083e-01 - 4.1607481241226196e-01 - <_> - - 0 1 2052 -4.4914530590176582e-03 -1 -2 2053 - 1.6592369647696614e-03 - - 5.9974372386932373e-01 3.7271851301193237e-01 - 5.1156342029571533e-01 - <_> - - 0 1 2054 6.4695458859205246e-03 -1 -2 2055 - 4.9810269847512245e-03 - - 5.2520352602005005e-01 5.2567178010940552e-01 - 3.9344060420989990e-01 - <_> - - 0 1 2056 -3.8536980748176575e-02 -1 -2 2057 - -2.8275650739669800e-01 - - 2.0619249343872070e-01 6.1883211135864258e-02 - 4.9250578880310059e-01 - <_> - - 0 1 2058 -9.0301828458905220e-03 -1 -2 2059 - -4.3866269290447235e-02 - - 3.1575900316238403e-01 2.0336820185184479e-01 - 5.1647698879241943e-01 - <_> - - 0 1 2060 -4.5701069757342339e-03 -1 -2 2061 - -2.3362410720437765e-03 - - 6.6111832857131958e-01 2.8077891469001770e-01 - 4.9628761410713196e-01 - <_> - - 0 1 2062 5.3960331715643406e-03 -1 -2 2063 - -2.6297608856111765e-03 - - 5.1463878154754639e-01 6.2844878435134888e-01 - 4.9555888772010803e-01 - <_> - - 0 1 2064 -3.8577478844672441e-03 -1 -2 2065 - 1.3963800156489015e-03 - - 1.4867480099201202e-01 4.7013381123542786e-01 - 6.3209718465805054e-01 - <_> - - 1 0 2066 -8.8699469342827797e-03 -1 -2 2067 - -7.0626288652420044e-04 - - 5.2868181467056274e-01 4.6483701467514038e-01 - 5.3332102298736572e-01 - <_> - - 0 1 2068 4.2645810171961784e-03 -1 -2 2069 - 6.1572100967168808e-02 - - 5.0848782062530518e-01 3.6296251416206360e-01 - 8.7571567296981812e-01 - <_> - - 1 0 2070 -4.5381980016827583e-03 -1 -2 2071 - -4.0877899155020714e-03 - - 4.8566961288452148e-01 4.5841160416603088e-01 - 5.4202407598495483e-01 - <_> - - 1 0 2072 6.4308601431548595e-03 -1 -2 2073 - 7.0455260574817657e-03 - - 2.7073028683662415e-01 5.0574868917465210e-01 - 7.0265239477157593e-01 - <_> - - 1 0 2074 -2.3246440105140209e-03 -1 -2 2075 - 6.0276601288933307e-05 - - 4.8272788524627686e-01 4.2472490668296814e-01 - 5.5087631940841675e-01 - <_> - - 1 0 2076 1.8084559589624405e-02 -1 -2 2077 - 8.4693520329892635e-04 - - 8.1048011779785156e-01 5.1546192169189453e-01 - 3.5143798589706421e-01 - <_> - - 1 0 2078 -2.6931039988994598e-02 -1 -2 2079 - -4.2346641421318054e-03 - - 4.8868888616561890e-01 4.6223780512809753e-01 - 5.3824782371520996e-01 - <_> - - 1 0 2080 2.6947110891342163e-02 -1 -2 2081 - 4.6446882188320160e-03 - - 6.3665962219238281e-01 5.3685069084167480e-01 - 3.7654298543930054e-01 - <_> - - 0 1 2082 -6.9577661342918873e-03 -1 -2 2083 - 8.7609712500125170e-04 - - 4.2346870899200439e-01 4.6724060177803040e-01 - 5.3506839275360107e-01 - <_> - - 1 0 2084 1.6103329835459590e-03 -1 -2 2085 - -1.2848590267822146e-03 - - 5.7327628135681152e-01 5.4817992448806763e-01 - 3.7845930457115173e-01 - <_> - - 0 1 2086 1.0243539698421955e-02 -1 -2 2087 - 2.6889349101111293e-04 - - 5.1559072732925415e-01 5.3531897068023682e-01 - 4.3871539831161499e-01 - <_> - - 0 1 2088 3.7903659977018833e-03 -1 -2 2089 - -2.9369680210947990e-02 - - 5.0320029258728027e-01 5.8735388517379761e-01 - 2.2154450416564941e-01 - <_> - - 1 0 2090 6.0743088833987713e-03 -1 -2 2091 - -1.2710720300674438e-02 - - 5.4170298576354980e-01 6.0565119981765747e-01 - 4.9851819872856140e-01 - <_> - - 0 1 2092 -5.9445449151098728e-03 -1 -2 2093 - -2.8927479870617390e-03 - - 3.3520698547363281e-01 6.9292408227920532e-01 - 4.7782200574874878e-01 - - <_> - - <_> - 2 7 16 4 -1. - <_> - 2 9 16 2 2. - <_> - - <_> - 8 4 3 14 -1. - <_> - 8 11 3 7 2. - <_> - - <_> - 13 6 1 6 -1. - <_> - 13 9 1 3 2. - <_> - - <_> - 4 2 12 8 -1. - <_> - 8 2 4 8 3. - <_> - - <_> - 6 3 1 9 -1. - <_> - 6 6 1 3 3. - <_> - - <_> - 3 7 14 9 -1. - <_> - 3 10 14 3 3. - <_> - - <_> - 4 7 4 4 -1. - <_> - 4 9 4 2 2. - <_> - - <_> - 9 4 2 16 -1. - <_> - 9 12 2 8 2. - <_> - - <_> - 1 1 18 5 -1. - <_> - 7 1 6 5 3. - <_> - - <_> - 4 5 13 8 -1. - <_> - 4 9 13 4 2. - <_> - - <_> - 1 7 16 9 -1. - <_> - 1 10 16 3 3. - <_> - - <_> - 2 0 15 4 -1. - <_> - 2 2 15 2 2. - <_> - - <_> - 7 5 6 4 -1. - <_> - 9 5 2 4 3. - <_> - - <_> - 6 3 8 9 -1. - <_> - 6 6 8 3 3. - <_> - - <_> - 8 12 3 8 -1. - <_> - 8 16 3 4 2. - <_> - - <_> - 3 16 2 2 -1. - <_> - 3 17 2 1 2. - <_> - - <_> - 14 1 6 12 -1. - <_> - 14 1 3 12 2. - <_> - - <_> - 4 4 12 6 -1. - <_> - 8 4 4 6 3. - <_> - - <_> - 0 2 6 15 -1. - <_> - 3 2 3 15 2. - <_> - - <_> - 5 4 9 6 -1. - <_> - 5 6 9 2 3. - <_> - - <_> - 13 11 6 3 -1. - <_> - 13 12 6 1 3. - <_> - - <_> - 12 12 6 4 -1. - <_> - 12 14 6 2 2. - <_> - - <_> - 1 11 6 3 -1. - <_> - 1 12 6 1 3. - <_> - - <_> - 2 5 5 8 -1. - <_> - 2 9 5 4 2. - <_> - - <_> - 5 4 10 4 -1. - <_> - 5 6 10 2 2. - <_> - - <_> - 2 4 16 12 -1. - <_> - 2 8 16 4 3. - <_> - - <_> - 4 5 12 6 -1. - <_> - 8 5 4 6 3. - <_> - - <_> - 13 7 2 9 -1. - <_> - 13 10 2 3 3. - <_> - - <_> - 5 7 2 9 -1. - <_> - 5 10 2 3 3. - <_> - - <_> - 7 1 6 8 -1. - <_> - 9 1 2 8 3. - <_> - - <_> - 12 0 4 12 -1. - <_> - 14 0 2 6 2. - <_> - 12 6 2 6 2. - <_> - - <_> - 5 8 10 2 -1. - <_> - 5 9 10 1 2. - <_> - - <_> - 5 1 6 4 -1. - <_> - 7 1 2 4 3. - <_> - - <_> - 0 3 9 12 -1. - <_> - 3 3 3 12 3. - <_> - - <_> - 9 8 3 12 -1. - <_> - 9 12 3 4 3. - <_> - - <_> - 0 5 20 15 -1. - <_> - 0 10 20 5 3. - <_> - - <_> - 2 2 6 8 -1. - <_> - 2 2 3 4 2. - <_> - 5 6 3 4 2. - <_> - - <_> - 2 1 6 2 -1. - <_> - 2 2 6 1 2. - <_> - - <_> - 10 15 6 4 -1. - <_> - 13 15 3 2 2. - <_> - 10 17 3 2 2. - <_> - - <_> - 12 14 2 6 -1. - <_> - 12 16 2 2 3. - <_> - - <_> - 5 15 4 4 -1. - <_> - 5 15 2 2 2. - <_> - 7 17 2 2 2. - <_> - - <_> - 7 18 1 2 -1. - <_> - 7 19 1 1 2. - <_> - - <_> - 4 5 12 10 -1. - <_> - 10 5 6 5 2. - <_> - 4 10 6 5 2. - <_> - - <_> - 7 4 8 12 -1. - <_> - 11 4 4 6 2. - <_> - 7 10 4 6 2. - <_> - - <_> - 9 11 2 3 -1. - <_> - 9 12 2 1 3. - <_> - - <_> - 3 3 12 12 -1. - <_> - 3 3 6 6 2. - <_> - 9 9 6 6 2. - <_> - - <_> - 15 11 5 3 -1. - <_> - 15 12 5 1 3. - <_> - - <_> - 10 18 3 2 -1. - <_> - 11 18 1 2 3. - <_> - - <_> - 0 11 5 3 -1. - <_> - 0 12 5 1 3. - <_> - - <_> - 7 18 3 2 -1. - <_> - 8 18 1 2 3. - <_> - - <_> - 2 8 16 2 -1. - <_> - 2 9 16 1 2. - <_> - - <_> - 9 6 5 12 -1. - <_> - 9 12 5 6 2. - <_> - - <_> - 6 3 8 6 -1. - <_> - 6 6 8 3 2. - <_> - - <_> - 4 7 12 2 -1. - <_> - 8 7 4 2 3. - <_> - - <_> - 10 9 6 8 -1. - <_> - 10 13 6 4 2. - <_> - - <_> - 12 5 3 10 -1. - <_> - 12 10 3 5 2. - <_> - - <_> - 4 6 3 9 -1. - <_> - 4 9 3 3 3. - <_> - - <_> - 7 4 6 4 -1. - <_> - 9 4 2 4 3. - <_> - - <_> - 12 3 8 3 -1. - <_> - 12 3 4 3 2. - <_> - - <_> - 15 0 3 6 -1. - <_> - 15 3 3 3 2. - <_> - - <_> - 2 12 10 8 -1. - <_> - 2 12 5 4 2. - <_> - 7 16 5 4 2. - <_> - - <_> - 5 5 6 8 -1. - <_> - 5 9 6 4 2. - <_> - - <_> - 12 3 8 3 -1. - <_> - 12 3 4 3 2. - <_> - - <_> - 15 0 3 6 -1. - <_> - 15 3 3 3 2. - <_> - - <_> - 0 3 8 3 -1. - <_> - 4 3 4 3 2. - <_> - - <_> - 2 1 4 4 -1. - <_> - 2 3 4 2 2. - <_> - - <_> - 10 2 3 2 -1. - <_> - 11 2 1 2 3. - <_> - - <_> - 10 3 3 1 -1. - <_> - 11 3 1 1 3. - <_> - - <_> - 7 15 3 4 -1. - <_> - 7 17 3 2 2. - <_> - - <_> - 4 13 3 6 -1. - <_> - 4 15 3 2 3. - <_> - - <_> - 10 5 1 14 -1. - <_> - 10 12 1 7 2. - <_> - - <_> - 5 4 10 6 -1. - <_> - 5 6 10 2 3. - <_> - - <_> - 5 0 6 3 -1. - <_> - 7 0 2 3 3. - <_> - - <_> - 6 0 3 5 -1. - <_> - 7 0 1 5 3. - <_> - - <_> - 7 15 6 5 -1. - <_> - 9 15 2 5 3. - <_> - - <_> - 9 10 2 6 -1. - <_> - 9 12 2 2 3. - <_> - - <_> - 8 17 3 2 -1. - <_> - 9 17 1 2 3. - <_> - - <_> - 1 12 7 6 -1. - <_> - 1 14 7 2 3. - <_> - - <_> - 9 6 3 7 -1. - <_> - 10 6 1 7 3. - <_> - - <_> - 16 3 4 9 -1. - <_> - 16 6 4 3 3. - <_> - - <_> - 8 6 3 7 -1. - <_> - 9 6 1 7 3. - <_> - - <_> - 0 5 18 8 -1. - <_> - 0 5 9 4 2. - <_> - 9 9 9 4 2. - <_> - - <_> - 13 5 2 10 -1. - <_> - 13 10 2 5 2. - <_> - - <_> - 12 10 2 6 -1. - <_> - 12 13 2 3 2. - <_> - - <_> - 7 0 3 5 -1. - <_> - 8 0 1 5 3. - <_> - - <_> - 6 5 8 6 -1. - <_> - 6 7 8 2 3. - <_> - - <_> - 10 3 6 14 -1. - <_> - 13 3 3 7 2. - <_> - 10 10 3 7 2. - <_> - - <_> - 13 5 1 8 -1. - <_> - 13 9 1 4 2. - <_> - - <_> - 4 3 6 14 -1. - <_> - 4 3 3 7 2. - <_> - 7 10 3 7 2. - <_> - - <_> - 6 5 1 8 -1. - <_> - 6 9 1 4 2. - <_> - - <_> - 8 1 1 6 -1. - <_> - 8 3 1 2 3. - <_> - - <_> - 2 0 15 2 -1. - <_> - 2 1 15 1 2. - <_> - - <_> - 0 7 20 6 -1. - <_> - 0 9 20 2 3. - <_> - - <_> - 10 10 6 8 -1. - <_> - 10 14 6 4 2. - <_> - - <_> - 7 1 3 2 -1. - <_> - 8 1 1 2 3. - <_> - - <_> - 8 1 2 2 -1. - <_> - 9 1 1 2 2. - <_> - - <_> - 4 3 12 9 -1. - <_> - 4 6 12 3 3. - <_> - - <_> - 6 5 9 5 -1. - <_> - 9 5 3 5 3. - <_> - - <_> - 5 5 9 5 -1. - <_> - 8 5 3 5 3. - <_> - - <_> - 4 6 6 12 -1. - <_> - 4 10 6 4 3. - <_> - - <_> - 13 0 6 18 -1. - <_> - 13 0 3 18 2. - <_> - - <_> - 10 8 1 12 -1. - <_> - 10 12 1 4 3. - <_> - - <_> - 3 2 6 10 -1. - <_> - 3 2 3 5 2. - <_> - 6 7 3 5 2. - <_> - - <_> - 1 2 4 6 -1. - <_> - 3 2 2 6 2. - <_> - - <_> - 9 18 3 2 -1. - <_> - 10 18 1 2 3. - <_> - - <_> - 10 18 3 2 -1. - <_> - 11 18 1 2 3. - <_> - - <_> - 2 8 2 6 -1. - <_> - 2 10 2 2 3. - <_> - - <_> - 7 5 6 6 -1. - <_> - 7 7 6 2 3. - <_> - - <_> - 7 19 6 1 -1. - <_> - 9 19 2 1 3. - <_> - - <_> - 10 18 3 2 -1. - <_> - 11 18 1 2 3. - <_> - - <_> - 8 3 3 1 -1. - <_> - 9 3 1 1 3. - <_> - - <_> - 2 2 16 2 -1. - <_> - 2 2 8 1 2. - <_> - 10 3 8 1 2. - <_> - - <_> - 8 11 5 3 -1. - <_> - 8 12 5 1 3. - <_> - - <_> - 7 13 6 3 -1. - <_> - 7 14 6 1 3. - <_> - - <_> - 0 1 6 15 -1. - <_> - 2 1 2 15 3. - <_> - - <_> - 2 12 2 3 -1. - <_> - 2 13 2 1 3. - <_> - - <_> - 16 13 1 3 -1. - <_> - 16 14 1 1 3. - <_> - - <_> - 13 7 6 4 -1. - <_> - 16 7 3 2 2. - <_> - 13 9 3 2 2. - <_> - - <_> - 7 13 3 6 -1. - <_> - 7 16 3 3 2. - <_> - - <_> - 7 5 1 14 -1. - <_> - 7 12 1 7 2. - <_> - - <_> - 15 12 2 3 -1. - <_> - 15 13 2 1 3. - <_> - - <_> - 10 5 3 14 -1. - <_> - 10 12 3 7 2. - <_> - - <_> - 6 10 2 6 -1. - <_> - 6 13 2 3 2. - <_> - - <_> - 6 5 1 8 -1. - <_> - 6 9 1 4 2. - <_> - - <_> - 13 11 2 1 -1. - <_> - 13 11 1 1 2. - <_> - - <_> - 12 1 6 10 -1. - <_> - 15 1 3 5 2. - <_> - 12 6 3 5 2. - <_> - - <_> - 3 12 2 3 -1. - <_> - 3 13 2 1 3. - <_> - - <_> - 9 18 2 1 -1. - <_> - 10 18 1 1 2. - <_> - - <_> - 1 0 17 9 -1. - <_> - 1 3 17 3 3. - <_> - - <_> - 1 2 8 8 -1. - <_> - 1 2 4 4 2. - <_> - 5 6 4 4 2. - <_> - - <_> - 9 5 6 4 -1. - <_> - 9 5 3 4 2. - <_> - - <_> - 10 9 7 10 -1. - <_> - 10 14 7 5 2. - <_> - - <_> - 5 5 6 4 -1. - <_> - 8 5 3 4 2. - <_> - - <_> - 0 7 20 6 -1. - <_> - 0 9 20 2 3. - <_> - - <_> - 6 5 9 10 -1. - <_> - 6 10 9 5 2. - <_> - - <_> - 8 4 4 12 -1. - <_> - 8 10 4 6 2. - <_> - - <_> - 6 6 8 3 -1. - <_> - 6 7 8 1 3. - <_> - - <_> - 3 13 10 6 -1. - <_> - 3 13 5 3 2. - <_> - 8 16 5 3 2. - <_> - - <_> - 15 1 4 11 -1. - <_> - 15 1 2 11 2. - <_> - - <_> - 5 7 10 10 -1. - <_> - 10 7 5 5 2. - <_> - 5 12 5 5 2. - <_> - - <_> - 1 1 4 11 -1. - <_> - 3 1 2 11 2. - <_> - - <_> - 1 5 8 12 -1. - <_> - 1 11 8 6 2. - <_> - - <_> - 13 7 6 4 -1. - <_> - 16 7 3 2 2. - <_> - 13 9 3 2 2. - <_> - - <_> - 11 10 7 4 -1. - <_> - 11 12 7 2 2. - <_> - - <_> - 0 4 20 12 -1. - <_> - 0 4 10 6 2. - <_> - 10 10 10 6 2. - <_> - - <_> - 1 5 6 15 -1. - <_> - 1 10 6 5 3. - <_> - - <_> - 11 10 3 8 -1. - <_> - 11 14 3 4 2. - <_> - - <_> - 11 12 7 6 -1. - <_> - 11 14 7 2 3. - <_> - - <_> - 9 11 2 3 -1. - <_> - 9 12 2 1 3. - <_> - - <_> - 8 13 4 3 -1. - <_> - 8 14 4 1 3. - <_> - - <_> - 3 14 14 4 -1. - <_> - 10 14 7 2 2. - <_> - 3 16 7 2 2. - <_> - - <_> - 18 7 2 4 -1. - <_> - 18 9 2 2 2. - <_> - - <_> - 3 12 6 6 -1. - <_> - 3 14 6 2 3. - <_> - - <_> - 0 4 3 6 -1. - <_> - 0 6 3 2 3. - <_> - - <_> - 9 14 3 3 -1. - <_> - 9 15 3 1 3. - <_> - - <_> - 10 7 10 4 -1. - <_> - 15 7 5 2 2. - <_> - 10 9 5 2 2. - <_> - - <_> - 7 2 6 8 -1. - <_> - 7 6 6 4 2. - <_> - - <_> - 6 3 6 2 -1. - <_> - 8 3 2 2 3. - <_> - - <_> - 10 6 3 5 -1. - <_> - 11 6 1 5 3. - <_> - - <_> - 9 0 6 19 -1. - <_> - 11 0 2 19 3. - <_> - - <_> - 3 12 1 2 -1. - <_> - 3 13 1 1 2. - <_> - - <_> - 7 14 5 3 -1. - <_> - 7 15 5 1 3. - <_> - - <_> - 2 1 18 4 -1. - <_> - 11 1 9 2 2. - <_> - 2 3 9 2 2. - <_> - - <_> - 10 5 3 8 -1. - <_> - 11 5 1 8 3. - <_> - - <_> - 0 1 18 4 -1. - <_> - 0 1 9 2 2. - <_> - 9 3 9 2 2. - <_> - - <_> - 7 5 3 8 -1. - <_> - 8 5 1 8 3. - <_> - - <_> - 9 5 2 6 -1. - <_> - 9 7 2 2 3. - <_> - - <_> - 10 8 5 2 -1. - <_> - 10 9 5 1 2. - <_> - - <_> - 2 10 15 1 -1. - <_> - 7 10 5 1 3. - <_> - - <_> - 2 7 2 6 -1. - <_> - 2 9 2 2 3. - <_> - - <_> - 9 14 3 3 -1. - <_> - 9 15 3 1 3. - <_> - - <_> - 9 7 4 10 -1. - <_> - 9 12 4 5 2. - <_> - - <_> - 0 8 8 2 -1. - <_> - 0 8 4 1 2. - <_> - 4 9 4 1 2. - <_> - - <_> - 5 9 10 8 -1. - <_> - 5 9 5 4 2. - <_> - 10 13 5 4 2. - <_> - - <_> - 9 7 2 4 -1. - <_> - 9 7 1 4 2. - <_> - - <_> - 9 6 3 4 -1. - <_> - 10 6 1 4 3. - <_> - - <_> - 8 3 2 1 -1. - <_> - 9 3 1 1 2. - <_> - - <_> - 8 6 3 4 -1. - <_> - 9 6 1 4 3. - <_> - - <_> - 12 0 4 14 -1. - <_> - 14 0 2 7 2. - <_> - 12 7 2 7 2. - <_> - - <_> - 12 5 6 9 -1. - <_> - 12 5 3 9 2. - <_> - - <_> - 0 2 6 16 -1. - <_> - 3 2 3 16 2. - <_> - - <_> - 1 12 4 2 -1. - <_> - 1 13 4 1 2. - <_> - - <_> - 7 7 6 1 -1. - <_> - 9 7 2 1 3. - <_> - - <_> - 8 3 4 9 -1. - <_> - 8 6 4 3 3. - <_> - - <_> - 12 10 4 6 -1. - <_> - 12 13 4 3 2. - <_> - - <_> - 8 1 8 16 -1. - <_> - 12 1 4 8 2. - <_> - 8 9 4 8 2. - <_> - - <_> - 4 6 3 6 -1. - <_> - 4 9 3 3 2. - <_> - - <_> - 1 3 6 2 -1. - <_> - 4 3 3 2 2. - <_> - - <_> - 9 8 3 12 -1. - <_> - 9 12 3 4 3. - <_> - - <_> - 10 9 7 10 -1. - <_> - 10 14 7 5 2. - <_> - - <_> - 3 9 7 10 -1. - <_> - 3 14 7 5 2. - <_> - - <_> - 7 5 1 14 -1. - <_> - 7 12 1 7 2. - <_> - - <_> - 13 14 1 6 -1. - <_> - 13 16 1 2 3. - <_> - - <_> - 14 12 3 6 -1. - <_> - 14 14 3 2 3. - <_> - - <_> - 6 14 1 6 -1. - <_> - 6 16 1 2 3. - <_> - - <_> - 3 12 3 6 -1. - <_> - 3 14 3 2 3. - <_> - - <_> - 8 13 5 3 -1. - <_> - 8 14 5 1 3. - <_> - - <_> - 9 14 2 3 -1. - <_> - 9 15 2 1 3. - <_> - - <_> - 5 1 10 8 -1. - <_> - 5 1 5 4 2. - <_> - 10 5 5 4 2. - <_> - - <_> - 6 4 5 4 -1. - <_> - 6 6 5 2 2. - <_> - - <_> - 1 10 18 1 -1. - <_> - 7 10 6 1 3. - <_> - - <_> - 11 10 4 3 -1. - <_> - 11 10 2 3 2. - <_> - - <_> - 5 11 6 1 -1. - <_> - 7 11 2 1 3. - <_> - - <_> - 3 13 2 3 -1. - <_> - 3 14 2 1 3. - <_> - - <_> - 12 12 3 4 -1. - <_> - 12 14 3 2 2. - <_> - - <_> - 11 10 5 6 -1. - <_> - 11 12 5 2 3. - <_> - - <_> - 0 8 16 2 -1. - <_> - 0 9 16 1 2. - <_> - - <_> - 2 1 3 4 -1. - <_> - 2 3 3 2 2. - <_> - - <_> - 9 7 3 3 -1. - <_> - 10 7 1 3 3. - <_> - - <_> - 5 6 12 6 -1. - <_> - 9 6 4 6 3. - <_> - - <_> - 8 7 3 3 -1. - <_> - 9 7 1 3 3. - <_> - - <_> - 3 6 12 6 -1. - <_> - 7 6 4 6 3. - <_> - - <_> - 10 5 6 5 -1. - <_> - 12 5 2 5 3. - <_> - - <_> - 5 7 10 2 -1. - <_> - 5 7 5 2 2. - <_> - - <_> - 4 5 6 5 -1. - <_> - 6 5 2 5 3. - <_> - - <_> - 9 3 2 10 -1. - <_> - 9 8 2 5 2. - <_> - - <_> - 3 1 16 2 -1. - <_> - 11 1 8 1 2. - <_> - 3 2 8 1 2. - <_> - - <_> - 9 9 3 2 -1. - <_> - 9 10 3 1 2. - <_> - - <_> - 1 1 16 2 -1. - <_> - 1 1 8 1 2. - <_> - 9 2 8 1 2. - <_> - - <_> - 8 14 1 3 -1. - <_> - 8 15 1 1 3. - <_> - - <_> - 4 5 12 10 -1. - <_> - 10 5 6 5 2. - <_> - 4 10 6 5 2. - <_> - - <_> - 7 13 6 6 -1. - <_> - 10 13 3 3 2. - <_> - 7 16 3 3 2. - <_> - - <_> - 8 9 3 2 -1. - <_> - 8 10 3 1 2. - <_> - - <_> - 7 2 6 4 -1. - <_> - 9 2 2 4 3. - <_> - - <_> - 6 6 9 3 -1. - <_> - 6 7 9 1 3. - <_> - - <_> - 10 7 6 1 -1. - <_> - 12 7 2 1 3. - <_> - - <_> - 0 0 18 6 -1. - <_> - 6 0 6 6 3. - <_> - - <_> - 6 10 2 6 -1. - <_> - 6 13 2 3 2. - <_> - - <_> - 11 12 3 6 -1. - <_> - 11 15 3 3 2. - <_> - - <_> - 4 4 12 12 -1. - <_> - 10 4 6 6 2. - <_> - 4 10 6 6 2. - <_> - - <_> - 1 2 3 6 -1. - <_> - 2 2 1 6 3. - <_> - - <_> - 1 5 3 7 -1. - <_> - 2 5 1 7 3. - <_> - - <_> - 4 13 12 4 -1. - <_> - 10 13 6 2 2. - <_> - 4 15 6 2 2. - <_> - - <_> - 3 3 17 12 -1. - <_> - 3 9 17 6 2. - <_> - - <_> - 3 3 14 12 -1. - <_> - 3 3 7 6 2. - <_> - 10 9 7 6 2. - <_> - - <_> - 2 11 16 9 -1. - <_> - 2 14 16 3 3. - <_> - - <_> - 9 14 3 6 -1. - <_> - 9 17 3 3 2. - <_> - - <_> - 8 14 4 6 -1. - <_> - 10 14 2 3 2. - <_> - 8 17 2 3 2. - <_> - - <_> - 6 2 6 1 -1. - <_> - 8 2 2 1 3. - <_> - - <_> - 9 5 2 5 -1. - <_> - 10 5 1 5 2. - <_> - - <_> - 9 8 3 5 -1. - <_> - 10 8 1 5 3. - <_> - - <_> - 9 12 6 1 -1. - <_> - 9 12 3 1 2. - <_> - - <_> - 8 8 3 5 -1. - <_> - 9 8 1 5 3. - <_> - - <_> - 6 10 4 3 -1. - <_> - 8 10 2 3 2. - <_> - - <_> - 0 4 20 6 -1. - <_> - 0 6 20 2 3. - <_> - - <_> - 1 3 8 6 -1. - <_> - 1 3 4 3 2. - <_> - 5 6 4 3 2. - <_> - - <_> - 7 15 6 4 -1. - <_> - 7 17 6 2 2. - <_> - - <_> - 3 10 14 10 -1. - <_> - 3 15 14 5 2. - <_> - - <_> - 6 4 4 4 -1. - <_> - 8 4 2 4 2. - <_> - - <_> - 0 4 20 10 -1. - <_> - 0 9 20 5 2. - <_> - - <_> - 9 4 2 14 -1. - <_> - 9 11 2 7 2. - <_> - - <_> - 2 0 16 4 -1. - <_> - 2 2 16 2 2. - <_> - - <_> - 4 12 6 8 -1. - <_> - 4 12 3 4 2. - <_> - 7 16 3 4 2. - <_> - - <_> - 0 5 6 7 -1. - <_> - 3 5 3 7 2. - <_> - - <_> - 10 7 10 4 -1. - <_> - 15 7 5 2 2. - <_> - 10 9 5 2 2. - <_> - - <_> - 5 8 12 1 -1. - <_> - 9 8 4 1 3. - <_> - - <_> - 9 9 2 2 -1. - <_> - 9 10 2 1 2. - <_> - - <_> - 9 4 2 4 -1. - <_> - 9 6 2 2 2. - <_> - - <_> - 9 6 3 6 -1. - <_> - 10 6 1 6 3. - <_> - - <_> - 12 7 6 4 -1. - <_> - 15 7 3 2 2. - <_> - 12 9 3 2 2. - <_> - - <_> - 8 6 3 6 -1. - <_> - 9 6 1 6 3. - <_> - - <_> - 1 6 18 6 -1. - <_> - 1 6 9 3 2. - <_> - 10 9 9 3 2. - <_> - - <_> - 9 1 3 3 -1. - <_> - 10 1 1 3 3. - <_> - - <_> - 10 8 5 2 -1. - <_> - 10 9 5 1 2. - <_> - - <_> - 8 1 3 3 -1. - <_> - 9 1 1 3 3. - <_> - - <_> - 5 8 5 2 -1. - <_> - 5 9 5 1 2. - <_> - - <_> - 8 6 8 8 -1. - <_> - 12 6 4 4 2. - <_> - 8 10 4 4 2. - <_> - - <_> - 5 7 10 2 -1. - <_> - 5 7 5 2 2. - <_> - - <_> - 4 5 12 10 -1. - <_> - 4 5 6 5 2. - <_> - 10 10 6 5 2. - <_> - - <_> - 5 5 2 3 -1. - <_> - 5 6 2 1 3. - <_> - - <_> - 7 14 6 3 -1. - <_> - 7 15 6 1 3. - <_> - - <_> - 9 14 3 3 -1. - <_> - 9 15 3 1 3. - <_> - - <_> - 8 14 3 3 -1. - <_> - 8 15 3 1 3. - <_> - - <_> - 1 10 8 9 -1. - <_> - 1 13 8 3 3. - <_> - - <_> - 9 7 2 3 -1. - <_> - 9 8 2 1 3. - <_> - - <_> - 12 3 3 3 -1. - <_> - 13 3 1 3 3. - <_> - - <_> - 5 3 3 3 -1. - <_> - 6 3 1 3 3. - <_> - - <_> - 5 6 2 12 -1. - <_> - 5 10 2 4 3. - <_> - - <_> - 1 11 18 4 -1. - <_> - 10 11 9 2 2. - <_> - 1 13 9 2 2. - <_> - - <_> - 7 12 6 2 -1. - <_> - 7 13 6 1 2. - <_> - - <_> - 6 0 3 6 -1. - <_> - 7 0 1 6 3. - <_> - - <_> - 0 11 18 4 -1. - <_> - 0 11 9 2 2. - <_> - 9 13 9 2 2. - <_> - - <_> - 7 12 6 2 -1. - <_> - 7 13 6 1 2. - <_> - - <_> - 9 12 3 3 -1. - <_> - 9 13 3 1 3. - <_> - - <_> - 9 12 2 3 -1. - <_> - 9 13 2 1 3. - <_> - - <_> - 8 11 4 3 -1. - <_> - 8 12 4 1 3. - <_> - - <_> - 13 3 4 2 -1. - <_> - 13 4 4 1 2. - <_> - - <_> - 4 0 12 2 -1. - <_> - 4 1 12 1 2. - <_> - - <_> - 6 9 8 8 -1. - <_> - 6 9 4 4 2. - <_> - 10 13 4 4 2. - <_> - - <_> - 1 11 6 2 -1. - <_> - 1 12 6 1 2. - <_> - - <_> - 2 5 18 8 -1. - <_> - 11 5 9 4 2. - <_> - 2 9 9 4 2. - <_> - - <_> - 7 1 6 10 -1. - <_> - 7 6 6 5 2. - <_> - - <_> - 0 3 3 6 -1. - <_> - 0 5 3 2 3. - <_> - - <_> - 4 5 4 3 -1. - <_> - 4 6 4 1 3. - <_> - - <_> - 19 3 1 6 -1. - <_> - 19 5 1 2 3. - <_> - - <_> - 6 15 8 2 -1. - <_> - 6 16 8 1 2. - <_> - - <_> - 0 3 1 6 -1. - <_> - 0 5 1 2 3. - <_> - - <_> - 5 5 3 3 -1. - <_> - 5 6 3 1 3. - <_> - - <_> - 8 8 4 3 -1. - <_> - 8 9 4 1 3. - <_> - - <_> - 10 6 6 3 -1. - <_> - 12 6 2 3 3. - <_> - - <_> - 8 13 2 6 -1. - <_> - 8 16 2 3 2. - <_> - - <_> - 9 11 2 8 -1. - <_> - 9 15 2 4 2. - <_> - - <_> - 10 6 6 3 -1. - <_> - 12 6 2 3 3. - <_> - - <_> - 5 15 15 5 -1. - <_> - 10 15 5 5 3. - <_> - - <_> - 2 14 2 2 -1. - <_> - 2 15 2 1 2. - <_> - - <_> - 4 7 6 2 -1. - <_> - 6 7 2 2 3. - <_> - - <_> - 8 3 6 1 -1. - <_> - 10 3 2 1 3. - <_> - - <_> - 1 0 18 12 -1. - <_> - 7 0 6 12 3. - <_> - - <_> - 0 14 8 6 -1. - <_> - 4 14 4 6 2. - <_> - - <_> - 0 15 15 5 -1. - <_> - 5 15 5 5 3. - <_> - - <_> - 8 3 6 1 -1. - <_> - 10 3 2 1 3. - <_> - - <_> - 11 11 3 6 -1. - <_> - 11 14 3 3 2. - <_> - - <_> - 6 3 6 1 -1. - <_> - 8 3 2 1 3. - <_> - - <_> - 6 11 3 6 -1. - <_> - 6 14 3 3 2. - <_> - - <_> - 9 6 3 4 -1. - <_> - 10 6 1 4 3. - <_> - - <_> - 12 10 4 7 -1. - <_> - 12 10 2 7 2. - <_> - - <_> - 8 6 3 4 -1. - <_> - 9 6 1 4 3. - <_> - - <_> - 4 6 4 7 -1. - <_> - 6 6 2 7 2. - <_> - - <_> - 10 3 4 12 -1. - <_> - 10 3 2 12 2. - <_> - - <_> - 10 8 3 4 -1. - <_> - 11 8 1 4 3. - <_> - - <_> - 1 0 18 14 -1. - <_> - 7 0 6 14 3. - <_> - - <_> - 2 8 6 11 -1. - <_> - 5 8 3 11 2. - <_> - - <_> - 1 4 15 4 -1. - <_> - 1 6 15 2 2. - <_> - - <_> - 5 5 10 8 -1. - <_> - 5 9 10 4 2. - <_> - - <_> - 14 2 6 8 -1. - <_> - 14 2 3 8 2. - <_> - - <_> - 11 6 6 14 -1. - <_> - 14 6 3 7 2. - <_> - 11 13 3 7 2. - <_> - - <_> - 9 5 2 12 -1. - <_> - 9 11 2 6 2. - <_> - - <_> - 3 7 4 6 -1. - <_> - 3 9 4 2 3. - <_> - - <_> - 14 3 6 6 -1. - <_> - 14 3 3 6 2. - <_> - - <_> - 15 2 4 4 -1. - <_> - 15 4 4 2 2. - <_> - - <_> - 0 2 6 7 -1. - <_> - 3 2 3 7 2. - <_> - - <_> - 3 6 6 14 -1. - <_> - 3 6 3 7 2. - <_> - 6 13 3 7 2. - <_> - - <_> - 4 6 16 8 -1. - <_> - 4 10 16 4 2. - <_> - - <_> - 10 12 2 8 -1. - <_> - 10 16 2 4 2. - <_> - - <_> - 7 0 6 20 -1. - <_> - 9 0 2 20 3. - <_> - - <_> - 1 7 16 12 -1. - <_> - 1 7 8 6 2. - <_> - 9 13 8 6 2. - <_> - - <_> - 9 11 3 3 -1. - <_> - 9 12 3 1 3. - <_> - - <_> - 11 9 4 5 -1. - <_> - 11 9 2 5 2. - <_> - - <_> - 3 3 1 2 -1. - <_> - 3 4 1 1 2. - <_> - - <_> - 7 17 5 3 -1. - <_> - 7 18 5 1 3. - <_> - - <_> - 8 12 4 8 -1. - <_> - 10 12 2 4 2. - <_> - 8 16 2 4 2. - <_> - - <_> - 7 4 10 12 -1. - <_> - 12 4 5 6 2. - <_> - 7 10 5 6 2. - <_> - - <_> - 8 14 4 3 -1. - <_> - 8 15 4 1 3. - <_> - - <_> - 5 9 4 5 -1. - <_> - 7 9 2 5 2. - <_> - - <_> - 9 9 8 2 -1. - <_> - 9 9 4 2 2. - <_> - - <_> - 14 15 5 2 -1. - <_> - 14 16 5 1 2. - <_> - - <_> - 9 14 2 3 -1. - <_> - 9 15 2 1 3. - <_> - - <_> - 1 7 8 4 -1. - <_> - 1 7 4 2 2. - <_> - 5 9 4 2 2. - <_> - - <_> - 19 3 1 2 -1. - <_> - 19 4 1 1 2. - <_> - - <_> - 9 12 2 3 -1. - <_> - 9 13 2 1 3. - <_> - - <_> - 3 14 14 4 -1. - <_> - 3 14 7 2 2. - <_> - 10 16 7 2 2. - <_> - - <_> - 5 0 10 2 -1. - <_> - 5 1 10 1 2. - <_> - - <_> - 11 14 4 6 -1. - <_> - 11 16 4 2 3. - <_> - - <_> - 7 14 6 3 -1. - <_> - 7 15 6 1 3. - <_> - - <_> - 7 13 6 6 -1. - <_> - 7 13 3 3 2. - <_> - 10 16 3 3 2. - <_> - - <_> - 0 2 1 6 -1. - <_> - 0 4 1 2 3. - <_> - - <_> - 6 7 8 2 -1. - <_> - 6 8 8 1 2. - <_> - - <_> - 9 7 6 1 -1. - <_> - 9 7 3 1 2. - <_> - - <_> - 7 1 6 10 -1. - <_> - 7 6 6 5 2. - <_> - - <_> - 0 2 6 2 -1. - <_> - 0 3 6 1 2. - <_> - - <_> - 11 4 2 4 -1. - <_> - 11 4 1 4 2. - <_> - - <_> - 11 10 3 6 -1. - <_> - 11 13 3 3 2. - <_> - - <_> - 3 9 8 2 -1. - <_> - 7 9 4 2 2. - <_> - - <_> - 0 0 4 6 -1. - <_> - 2 0 2 6 2. - <_> - - <_> - 7 0 6 2 -1. - <_> - 9 0 2 2 3. - <_> - - <_> - 9 15 2 3 -1. - <_> - 9 16 2 1 3. - <_> - - <_> - 3 12 1 2 -1. - <_> - 3 13 1 1 2. - <_> - - <_> - 4 5 11 3 -1. - <_> - 4 6 11 1 3. - <_> - - <_> - 11 4 2 4 -1. - <_> - 11 4 1 4 2. - <_> - - <_> - 8 3 6 3 -1. - <_> - 10 3 2 3 3. - <_> - - <_> - 7 4 2 4 -1. - <_> - 8 4 1 4 2. - <_> - - <_> - 6 3 6 3 -1. - <_> - 8 3 2 3 3. - <_> - - <_> - 11 4 4 3 -1. - <_> - 11 5 4 1 3. - <_> - - <_> - 11 8 2 8 -1. - <_> - 11 12 2 4 2. - <_> - - <_> - 8 7 3 5 -1. - <_> - 9 7 1 5 3. - <_> - - <_> - 9 7 2 5 -1. - <_> - 10 7 1 5 2. - <_> - - <_> - 14 11 1 6 -1. - <_> - 14 13 1 2 3. - <_> - - <_> - 8 8 4 3 -1. - <_> - 8 9 4 1 3. - <_> - - <_> - 0 3 2 2 -1. - <_> - 0 4 2 1 2. - <_> - - <_> - 4 14 5 6 -1. - <_> - 4 16 5 2 3. - <_> - - <_> - 11 4 4 3 -1. - <_> - 11 5 4 1 3. - <_> - - <_> - 12 4 3 3 -1. - <_> - 12 5 3 1 3. - <_> - - <_> - 5 4 4 3 -1. - <_> - 5 5 4 1 3. - <_> - - <_> - 5 15 4 2 -1. - <_> - 7 15 2 2 2. - <_> - - <_> - 15 1 5 9 -1. - <_> - 15 4 5 3 3. - <_> - - <_> - 9 10 3 3 -1. - <_> - 9 11 3 1 3. - <_> - - <_> - 1 6 2 6 -1. - <_> - 1 8 2 2 3. - <_> - - <_> - 2 4 8 15 -1. - <_> - 2 9 8 5 3. - <_> - - <_> - 9 12 3 2 -1. - <_> - 9 13 3 1 2. - <_> - - <_> - 9 12 3 3 -1. - <_> - 9 13 3 1 3. - <_> - - <_> - 7 6 3 5 -1. - <_> - 8 6 1 5 3. - <_> - - <_> - 5 3 6 2 -1. - <_> - 7 3 2 2 3. - <_> - - <_> - 6 1 8 10 -1. - <_> - 10 1 4 5 2. - <_> - 6 6 4 5 2. - <_> - - <_> - 0 0 20 10 -1. - <_> - 10 0 10 5 2. - <_> - 0 5 10 5 2. - <_> - - <_> - 6 3 3 1 -1. - <_> - 7 3 1 1 3. - <_> - - <_> - 0 2 6 8 -1. - <_> - 2 2 2 8 3. - <_> - - <_> - 11 10 3 4 -1. - <_> - 11 12 3 2 2. - <_> - - <_> - 12 6 3 8 -1. - <_> - 12 10 3 4 2. - <_> - - <_> - 6 10 3 4 -1. - <_> - 6 12 3 2 2. - <_> - - <_> - 5 6 3 8 -1. - <_> - 5 10 3 4 2. - <_> - - <_> - 2 6 18 6 -1. - <_> - 11 6 9 3 2. - <_> - 2 9 9 3 2. - <_> - - <_> - 7 14 7 3 -1. - <_> - 7 15 7 1 3. - <_> - - <_> - 0 0 2 12 -1. - <_> - 1 0 1 12 2. - <_> - - <_> - 1 2 18 16 -1. - <_> - 1 10 18 8 2. - <_> - - <_> - 9 13 5 3 -1. - <_> - 9 14 5 1 3. - <_> - - <_> - 8 13 4 3 -1. - <_> - 8 14 4 1 3. - <_> - - <_> - 0 6 18 6 -1. - <_> - 0 6 9 3 2. - <_> - 9 9 9 3 2. - <_> - - <_> - 7 13 6 3 -1. - <_> - 7 14 6 1 3. - <_> - - <_> - 17 4 1 3 -1. - <_> - 17 5 1 1 3. - <_> - - <_> - 12 11 1 9 -1. - <_> - 12 14 1 3 3. - <_> - - <_> - 2 4 1 3 -1. - <_> - 2 5 1 1 3. - <_> - - <_> - 5 4 2 3 -1. - <_> - 5 5 2 1 3. - <_> - - <_> - 1 2 18 3 -1. - <_> - 7 2 6 3 3. - <_> - - <_> - 0 1 20 6 -1. - <_> - 0 3 20 2 3. - <_> - - <_> - 7 5 6 3 -1. - <_> - 9 5 2 3 3. - <_> - - <_> - 13 7 6 4 -1. - <_> - 16 7 3 2 2. - <_> - 13 9 3 2 2. - <_> - - <_> - 3 1 4 10 -1. - <_> - 3 1 2 5 2. - <_> - 5 6 2 5 2. - <_> - - <_> - 0 4 19 10 -1. - <_> - 0 9 19 5 2. - <_> - - <_> - 9 8 3 12 -1. - <_> - 9 12 3 4 3. - <_> - - <_> - 11 18 5 2 -1. - <_> - 11 19 5 1 2. - <_> - - <_> - 5 16 6 4 -1. - <_> - 5 16 3 2 2. - <_> - 8 18 3 2 2. - <_> - - <_> - 5 18 3 2 -1. - <_> - 5 19 3 1 2. - <_> - - <_> - 13 11 3 2 -1. - <_> - 13 12 3 1 2. - <_> - - <_> - 8 5 8 4 -1. - <_> - 8 5 4 4 2. - <_> - - <_> - 1 2 18 6 -1. - <_> - 1 2 9 3 2. - <_> - 10 5 9 3 2. - <_> - - <_> - 3 5 14 6 -1. - <_> - 3 7 14 2 3. - <_> - - <_> - 18 1 2 6 -1. - <_> - 18 3 2 2 3. - <_> - - <_> - 9 11 6 1 -1. - <_> - 11 11 2 1 3. - <_> - - <_> - 0 2 6 11 -1. - <_> - 3 2 3 11 2. - <_> - - <_> - 4 12 2 3 -1. - <_> - 4 13 2 1 3. - <_> - - <_> - 6 12 9 2 -1. - <_> - 9 12 3 2 3. - <_> - - <_> - 9 4 6 15 -1. - <_> - 9 4 3 15 2. - <_> - - <_> - 5 11 6 1 -1. - <_> - 7 11 2 1 3. - <_> - - <_> - 5 4 6 15 -1. - <_> - 8 4 3 15 2. - <_> - - <_> - 14 12 6 7 -1. - <_> - 14 12 3 7 2. - <_> - - <_> - 18 3 2 9 -1. - <_> - 18 6 2 3 3. - <_> - - <_> - 8 1 3 1 -1. - <_> - 9 1 1 1 3. - <_> - - <_> - 0 12 6 7 -1. - <_> - 3 12 3 7 2. - <_> - - <_> - 13 7 6 4 -1. - <_> - 16 7 3 2 2. - <_> - 13 9 3 2 2. - <_> - - <_> - 8 0 10 2 -1. - <_> - 8 1 10 1 2. - <_> - - <_> - 1 7 6 4 -1. - <_> - 1 7 3 2 2. - <_> - 4 9 3 2 2. - <_> - - <_> - 1 2 3 3 -1. - <_> - 1 3 3 1 3. - <_> - - <_> - 9 13 4 3 -1. - <_> - 9 14 4 1 3. - <_> - - <_> - 12 13 7 2 -1. - <_> - 12 14 7 1 2. - <_> - - <_> - 5 12 9 2 -1. - <_> - 8 12 3 2 3. - <_> - - <_> - 6 10 4 8 -1. - <_> - 6 14 4 4 2. - <_> - - <_> - 1 0 18 4 -1. - <_> - 7 0 6 4 3. - <_> - - <_> - 12 0 5 2 -1. - <_> - 12 1 5 1 2. - <_> - - <_> - 7 7 1 12 -1. - <_> - 7 13 1 6 2. - <_> - - <_> - 6 2 3 4 -1. - <_> - 7 2 1 4 3. - <_> - - <_> - 0 13 20 6 -1. - <_> - 0 15 20 2 3. - <_> - - <_> - 8 5 12 2 -1. - <_> - 14 5 6 1 2. - <_> - 8 6 6 1 2. - <_> - - <_> - 8 14 2 3 -1. - <_> - 8 15 2 1 3. - <_> - - <_> - 8 14 4 3 -1. - <_> - 8 15 4 1 3. - <_> - - <_> - 12 13 7 6 -1. - <_> - 12 15 7 2 3. - <_> - - <_> - 6 0 8 12 -1. - <_> - 10 0 4 6 2. - <_> - 6 6 4 6 2. - <_> - - <_> - 0 15 9 4 -1. - <_> - 0 17 9 2 2. - <_> - - <_> - 9 0 2 5 -1. - <_> - 10 0 1 5 2. - <_> - - <_> - 9 5 2 6 -1. - <_> - 9 5 1 6 2. - <_> - - <_> - 17 2 3 6 -1. - <_> - 17 4 3 2 3. - <_> - - <_> - 3 11 2 3 -1. - <_> - 3 12 2 1 3. - <_> - - <_> - 7 13 3 3 -1. - <_> - 7 14 3 1 3. - <_> - - <_> - 14 12 5 3 -1. - <_> - 14 13 5 1 3. - <_> - - <_> - 4 8 14 3 -1. - <_> - 4 9 14 1 3. - <_> - - <_> - 1 12 5 3 -1. - <_> - 1 13 5 1 3. - <_> - - <_> - 1 15 12 2 -1. - <_> - 1 15 6 1 2. - <_> - 7 16 6 1 2. - <_> - - <_> - 12 11 4 2 -1. - <_> - 12 12 4 1 2. - <_> - - <_> - 9 8 3 5 -1. - <_> - 10 8 1 5 3. - <_> - - <_> - 9 5 2 6 -1. - <_> - 10 5 1 6 2. - <_> - - <_> - 0 2 3 6 -1. - <_> - 0 4 3 2 3. - <_> - - <_> - 12 11 4 2 -1. - <_> - 12 12 4 1 2. - <_> - - <_> - 9 7 3 5 -1. - <_> - 10 7 1 5 3. - <_> - - <_> - 4 11 4 2 -1. - <_> - 4 12 4 1 2. - <_> - - <_> - 8 8 3 5 -1. - <_> - 9 8 1 5 3. - <_> - - <_> - 9 3 3 1 -1. - <_> - 10 3 1 1 3. - <_> - - <_> - 16 5 3 8 -1. - <_> - 17 5 1 8 3. - <_> - - <_> - 8 3 3 1 -1. - <_> - 9 3 1 1 3. - <_> - - <_> - 1 5 3 8 -1. - <_> - 2 5 1 8 3. - <_> - - <_> - 10 1 3 3 -1. - <_> - 11 1 1 3 3. - <_> - - <_> - 17 5 2 4 -1. - <_> - 17 5 1 4 2. - <_> - - <_> - 2 8 14 3 -1. - <_> - 2 9 14 1 3. - <_> - - <_> - 9 7 1 3 -1. - <_> - 9 8 1 1 3. - <_> - - <_> - 6 1 8 10 -1. - <_> - 6 6 8 5 2. - <_> - - <_> - 13 0 6 8 -1. - <_> - 16 0 3 4 2. - <_> - 13 4 3 4 2. - <_> - - <_> - 1 5 2 4 -1. - <_> - 2 5 1 4 2. - <_> - - <_> - 4 2 12 2 -1. - <_> - 4 3 12 1 2. - <_> - - <_> - 8 8 4 4 -1. - <_> - 8 10 4 2 2. - <_> - - <_> - 5 6 12 4 -1. - <_> - 9 6 4 4 3. - <_> - - <_> - 1 2 8 1 -1. - <_> - 5 2 4 1 2. - <_> - - <_> - 1 1 6 10 -1. - <_> - 3 1 2 10 3. - <_> - - <_> - 8 6 8 2 -1. - <_> - 8 6 4 2 2. - <_> - - <_> - 10 7 6 6 -1. - <_> - 12 7 2 6 3. - <_> - - <_> - 4 6 8 2 -1. - <_> - 8 6 4 2 2. - <_> - - <_> - 4 7 6 6 -1. - <_> - 6 7 2 6 3. - <_> - - <_> - 3 14 16 4 -1. - <_> - 3 16 16 2 2. - <_> - - <_> - 8 12 4 2 -1. - <_> - 8 13 4 1 2. - <_> - - <_> - 8 12 3 3 -1. - <_> - 8 13 3 1 3. - <_> - - <_> - 5 12 6 1 -1. - <_> - 8 12 3 1 2. - <_> - - <_> - 18 10 2 3 -1. - <_> - 18 11 2 1 3. - <_> - - <_> - 16 8 4 6 -1. - <_> - 16 10 4 2 3. - <_> - - <_> - 8 3 2 1 -1. - <_> - 9 3 1 1 2. - <_> - - <_> - 7 1 3 9 -1. - <_> - 8 1 1 9 3. - <_> - - <_> - 5 11 11 6 -1. - <_> - 5 14 11 3 2. - <_> - - <_> - 12 2 3 14 -1. - <_> - 12 9 3 7 2. - <_> - - <_> - 8 7 3 3 -1. - <_> - 9 7 1 3 3. - <_> - - <_> - 3 5 12 5 -1. - <_> - 7 5 4 5 3. - <_> - - <_> - 1 2 6 3 -1. - <_> - 4 2 3 3 2. - <_> - - <_> - 5 5 6 10 -1. - <_> - 5 5 3 5 2. - <_> - 8 10 3 5 2. - <_> - - <_> - 16 18 2 2 -1. - <_> - 16 18 1 2 2. - <_> - - <_> - 16 18 2 2 -1. - <_> - 16 18 1 2 2. - <_> - - <_> - 8 4 2 5 -1. - <_> - 9 4 1 5 2. - <_> - - <_> - 8 4 1 4 -1. - <_> - 8 6 1 2 2. - <_> - - <_> - 7 15 12 4 -1. - <_> - 13 15 6 2 2. - <_> - 7 17 6 2 2. - <_> - - <_> - 11 18 6 2 -1. - <_> - 11 19 6 1 2. - <_> - - <_> - 7 7 4 10 -1. - <_> - 7 12 4 5 2. - <_> - - <_> - 5 6 10 8 -1. - <_> - 5 10 10 4 2. - <_> - - <_> - 11 1 6 12 -1. - <_> - 14 1 3 6 2. - <_> - 11 7 3 6 2. - <_> - - <_> - 5 8 12 1 -1. - <_> - 9 8 4 1 3. - <_> - - <_> - 4 7 3 6 -1. - <_> - 4 9 3 2 3. - <_> - - <_> - 4 11 3 4 -1. - <_> - 4 13 3 2 2. - <_> - - <_> - 14 16 2 2 -1. - <_> - 14 17 2 1 2. - <_> - - <_> - 15 15 2 2 -1. - <_> - 15 16 2 1 2. - <_> - - <_> - 7 12 6 2 -1. - <_> - 7 13 6 1 2. - <_> - - <_> - 8 13 4 2 -1. - <_> - 8 14 4 1 2. - <_> - - <_> - 11 1 6 12 -1. - <_> - 14 1 3 6 2. - <_> - 11 7 3 6 2. - <_> - - <_> - 12 2 4 2 -1. - <_> - 12 3 4 1 2. - <_> - - <_> - 3 10 12 6 -1. - <_> - 3 10 6 3 2. - <_> - 9 13 6 3 2. - <_> - - <_> - 3 1 6 12 -1. - <_> - 3 1 3 6 2. - <_> - 6 7 3 6 2. - <_> - - <_> - 16 6 4 14 -1. - <_> - 18 6 2 7 2. - <_> - 16 13 2 7 2. - <_> - - <_> - 5 1 10 8 -1. - <_> - 10 1 5 4 2. - <_> - 5 5 5 4 2. - <_> - - <_> - 0 6 4 14 -1. - <_> - 0 6 2 7 2. - <_> - 2 13 2 7 2. - <_> - - <_> - 1 15 12 4 -1. - <_> - 1 15 6 2 2. - <_> - 7 17 6 2 2. - <_> - - <_> - 10 17 3 3 -1. - <_> - 11 17 1 3 3. - <_> - - <_> - 11 2 2 6 -1. - <_> - 12 2 1 3 2. - <_> - 11 5 1 3 2. - <_> - - <_> - 7 17 3 3 -1. - <_> - 8 17 1 3 3. - <_> - - <_> - 8 15 4 3 -1. - <_> - 8 16 4 1 3. - <_> - - <_> - 10 15 4 2 -1. - <_> - 12 15 2 1 2. - <_> - 10 16 2 1 2. - <_> - - <_> - 13 13 4 3 -1. - <_> - 13 14 4 1 3. - <_> - - <_> - 3 13 4 3 -1. - <_> - 3 14 4 1 3. - <_> - - <_> - 7 2 2 6 -1. - <_> - 7 2 1 3 2. - <_> - 8 5 1 3 2. - <_> - - <_> - 2 1 16 3 -1. - <_> - 2 2 16 1 3. - <_> - - <_> - 10 15 4 2 -1. - <_> - 12 15 2 1 2. - <_> - 10 16 2 1 2. - <_> - - <_> - 6 15 4 2 -1. - <_> - 6 15 2 1 2. - <_> - 8 16 2 1 2. - <_> - - <_> - 3 0 13 3 -1. - <_> - 3 1 13 1 3. - <_> - - <_> - 0 9 20 3 -1. - <_> - 0 10 20 1 3. - <_> - - <_> - 6 7 9 2 -1. - <_> - 6 8 9 1 2. - <_> - - <_> - 8 14 3 6 -1. - <_> - 9 14 1 6 3. - <_> - - <_> - 9 10 2 2 -1. - <_> - 9 11 2 1 2. - <_> - - <_> - 9 7 2 5 -1. - <_> - 9 7 1 5 2. - <_> - - <_> - 5 6 10 3 -1. - <_> - 5 6 5 3 2. - <_> - - <_> - 9 7 2 5 -1. - <_> - 10 7 1 5 2. - <_> - - <_> - 5 6 10 3 -1. - <_> - 10 6 5 3 2. - <_> - - <_> - 13 9 2 2 -1. - <_> - 13 9 1 2 2. - <_> - - <_> - 4 3 12 11 -1. - <_> - 8 3 4 11 3. - <_> - - <_> - 7 1 2 7 -1. - <_> - 8 1 1 7 2. - <_> - - <_> - 7 4 3 8 -1. - <_> - 8 4 1 8 3. - <_> - - <_> - 13 9 2 2 -1. - <_> - 13 9 1 2 2. - <_> - - <_> - 11 6 2 2 -1. - <_> - 12 6 1 1 2. - <_> - 11 7 1 1 2. - <_> - - <_> - 5 4 2 3 -1. - <_> - 5 5 2 1 3. - <_> - - <_> - 6 5 1 3 -1. - <_> - 6 6 1 1 3. - <_> - - <_> - 13 9 2 2 -1. - <_> - 13 9 1 2 2. - <_> - - <_> - 16 14 3 3 -1. - <_> - 16 15 3 1 3. - <_> - - <_> - 5 9 2 2 -1. - <_> - 6 9 1 2 2. - <_> - - <_> - 1 14 3 3 -1. - <_> - 1 15 3 1 3. - <_> - - <_> - 13 1 1 6 -1. - <_> - 13 3 1 2 3. - <_> - - <_> - 13 3 7 2 -1. - <_> - 13 4 7 1 2. - <_> - - <_> - 0 6 20 14 -1. - <_> - 0 13 20 7 2. - <_> - - <_> - 0 4 3 6 -1. - <_> - 0 6 3 2 3. - <_> - - <_> - 10 1 9 6 -1. - <_> - 10 3 9 2 3. - <_> - - <_> - 8 0 12 5 -1. - <_> - 8 0 6 5 2. - <_> - - <_> - 0 0 18 5 -1. - <_> - 6 0 6 5 3. - <_> - - <_> - 1 1 9 6 -1. - <_> - 1 3 9 2 3. - <_> - - <_> - 15 15 2 2 -1. - <_> - 15 16 2 1 2. - <_> - - <_> - 13 16 3 4 -1. - <_> - 13 18 3 2 2. - <_> - - <_> - 3 15 2 2 -1. - <_> - 3 16 2 1 2. - <_> - - <_> - 4 16 3 4 -1. - <_> - 4 18 3 2 2. - <_> - - <_> - 11 14 1 3 -1. - <_> - 11 15 1 1 3. - <_> - - <_> - 9 13 5 3 -1. - <_> - 9 14 5 1 3. - <_> - - <_> - 0 0 3 6 -1. - <_> - 0 2 3 2 3. - <_> - - <_> - 4 1 6 3 -1. - <_> - 6 1 2 3 3. - <_> - - <_> - 9 13 4 3 -1. - <_> - 9 14 4 1 3. - <_> - - <_> - 8 15 5 3 -1. - <_> - 8 16 5 1 3. - <_> - - <_> - 8 3 3 2 -1. - <_> - 9 3 1 2 3. - <_> - - <_> - 1 8 18 2 -1. - <_> - 1 9 18 1 2. - <_> - - <_> - 11 14 1 3 -1. - <_> - 11 15 1 1 3. - <_> - - <_> - 8 13 6 3 -1. - <_> - 8 14 6 1 3. - <_> - - <_> - 8 14 1 3 -1. - <_> - 8 15 1 1 3. - <_> - - <_> - 4 13 12 4 -1. - <_> - 4 13 6 2 2. - <_> - 10 15 6 2 2. - <_> - - <_> - 10 7 2 2 -1. - <_> - 10 7 1 2 2. - <_> - - <_> - 13 4 2 8 -1. - <_> - 14 4 1 4 2. - <_> - 13 8 1 4 2. - <_> - - <_> - 0 5 4 6 -1. - <_> - 0 7 4 2 3. - <_> - - <_> - 8 7 2 2 -1. - <_> - 9 7 1 2 2. - <_> - - <_> - 13 0 3 7 -1. - <_> - 14 0 1 7 3. - <_> - - <_> - 11 2 2 14 -1. - <_> - 11 2 1 14 2. - <_> - - <_> - 4 0 3 7 -1. - <_> - 5 0 1 7 3. - <_> - - <_> - 5 5 8 12 -1. - <_> - 5 5 4 6 2. - <_> - 9 11 4 6 2. - <_> - - <_> - 11 4 6 3 -1. - <_> - 11 5 6 1 3. - <_> - - <_> - 12 3 4 3 -1. - <_> - 12 4 4 1 3. - <_> - - <_> - 5 5 10 12 -1. - <_> - 5 5 5 6 2. - <_> - 10 11 5 6 2. - <_> - - <_> - 3 6 12 3 -1. - <_> - 9 6 6 3 2. - <_> - - <_> - 9 6 2 7 -1. - <_> - 9 6 1 7 2. - <_> - - <_> - 9 5 2 4 -1. - <_> - 9 5 1 4 2. - <_> - - <_> - 8 7 3 3 -1. - <_> - 9 7 1 3 3. - <_> - - <_> - 5 1 6 4 -1. - <_> - 7 1 2 4 3. - <_> - - <_> - 13 16 7 3 -1. - <_> - 13 17 7 1 3. - <_> - - <_> - 12 4 3 3 -1. - <_> - 12 5 3 1 3. - <_> - - <_> - 0 16 7 3 -1. - <_> - 0 17 7 1 3. - <_> - - <_> - 5 4 3 3 -1. - <_> - 5 5 3 1 3. - <_> - - <_> - 12 9 8 10 -1. - <_> - 12 9 4 10 2. - <_> - - <_> - 8 10 12 5 -1. - <_> - 12 10 4 5 3. - <_> - - <_> - 0 9 8 10 -1. - <_> - 4 9 4 10 2. - <_> - - <_> - 0 10 12 5 -1. - <_> - 4 10 4 5 3. - <_> - - <_> - 2 3 6 2 -1. - <_> - 5 3 3 2 2. - <_> - - <_> - 0 0 17 9 -1. - <_> - 0 3 17 3 3. - <_> - - <_> - 4 7 12 2 -1. - <_> - 8 7 4 2 3. - <_> - - <_> - 10 4 6 4 -1. - <_> - 12 4 2 4 3. - <_> - - <_> - 0 10 20 4 -1. - <_> - 0 12 20 2 2. - <_> - - <_> - 4 3 6 5 -1. - <_> - 6 3 2 5 3. - <_> - - <_> - 1 1 18 4 -1. - <_> - 7 1 6 4 3. - <_> - - <_> - 13 9 2 3 -1. - <_> - 13 9 1 3 2. - <_> - - <_> - 6 15 7 4 -1. - <_> - 6 17 7 2 2. - <_> - - <_> - 3 17 4 2 -1. - <_> - 3 18 4 1 2. - <_> - - <_> - 9 4 8 10 -1. - <_> - 9 9 8 5 2. - <_> - - <_> - 9 17 3 2 -1. - <_> - 10 17 1 2 3. - <_> - - <_> - 8 2 4 8 -1. - <_> - 8 6 4 4 2. - <_> - - <_> - 3 4 14 12 -1. - <_> - 3 4 7 6 2. - <_> - 10 10 7 6 2. - <_> - - <_> - 7 7 6 4 -1. - <_> - 9 7 2 4 3. - <_> - - <_> - 6 7 9 4 -1. - <_> - 6 9 9 2 2. - <_> - - <_> - 2 10 3 3 -1. - <_> - 2 11 3 1 3. - <_> - - <_> - 4 6 2 9 -1. - <_> - 4 9 2 3 3. - <_> - - <_> - 9 11 3 3 -1. - <_> - 9 12 3 1 3. - <_> - - <_> - 3 1 15 2 -1. - <_> - 3 2 15 1 2. - <_> - - <_> - 9 8 2 3 -1. - <_> - 9 9 2 1 3. - <_> - - <_> - 9 6 2 5 -1. - <_> - 10 6 1 5 2. - <_> - - <_> - 9 7 2 3 -1. - <_> - 9 8 2 1 3. - <_> - - <_> - 4 10 12 10 -1. - <_> - 4 15 12 5 2. - <_> - - <_> - 0 10 4 2 -1. - <_> - 0 11 4 1 2. - <_> - - <_> - 5 15 9 2 -1. - <_> - 5 16 9 1 2. - <_> - - <_> - 8 14 6 3 -1. - <_> - 8 15 6 1 3. - <_> - - <_> - 8 16 4 3 -1. - <_> - 8 17 4 1 3. - <_> - - <_> - 8 9 4 2 -1. - <_> - 8 10 4 1 2. - <_> - - <_> - 3 3 14 2 -1. - <_> - 3 4 14 1 2. - <_> - - <_> - 11 12 1 2 -1. - <_> - 11 13 1 1 2. - <_> - - <_> - 4 12 12 1 -1. - <_> - 8 12 4 1 3. - <_> - - <_> - 0 2 1 2 -1. - <_> - 0 3 1 1 2. - <_> - - <_> - 7 4 4 6 -1. - <_> - 9 4 2 6 2. - <_> - - <_> - 0 2 20 14 -1. - <_> - 10 2 10 7 2. - <_> - 0 9 10 7 2. - <_> - - <_> - 14 6 1 3 -1. - <_> - 14 7 1 1 3. - <_> - - <_> - 0 4 20 12 -1. - <_> - 0 4 10 6 2. - <_> - 10 10 10 6 2. - <_> - - <_> - 8 12 1 2 -1. - <_> - 8 13 1 1 2. - <_> - - <_> - 9 18 3 2 -1. - <_> - 10 18 1 2 3. - <_> - - <_> - 9 17 6 2 -1. - <_> - 11 17 2 2 3. - <_> - - <_> - 5 6 2 3 -1. - <_> - 5 7 2 1 3. - <_> - - <_> - 5 4 3 3 -1. - <_> - 5 5 3 1 3. - <_> - - <_> - 14 15 3 2 -1. - <_> - 14 16 3 1 2. - <_> - - <_> - 11 3 3 4 -1. - <_> - 12 3 1 4 3. - <_> - - <_> - 3 15 3 2 -1. - <_> - 3 16 3 1 2. - <_> - - <_> - 9 12 2 3 -1. - <_> - 9 13 2 1 3. - <_> - - <_> - 9 13 3 7 -1. - <_> - 10 13 1 7 3. - <_> - - <_> - 12 12 5 3 -1. - <_> - 12 13 5 1 3. - <_> - - <_> - 8 18 3 2 -1. - <_> - 9 18 1 2 3. - <_> - - <_> - 4 7 12 4 -1. - <_> - 4 7 6 2 2. - <_> - 10 9 6 2 2. - <_> - - <_> - 6 19 14 1 -1. - <_> - 6 19 7 1 2. - <_> - - <_> - 16 14 3 2 -1. - <_> - 16 15 3 1 2. - <_> - - <_> - 1 0 6 10 -1. - <_> - 1 0 3 5 2. - <_> - 4 5 3 5 2. - <_> - - <_> - 1 0 4 10 -1. - <_> - 1 0 2 5 2. - <_> - 3 5 2 5 2. - <_> - - <_> - 15 3 5 6 -1. - <_> - 15 5 5 2 3. - <_> - - <_> - 9 5 2 15 -1. - <_> - 9 10 2 5 3. - <_> - - <_> - 0 3 5 6 -1. - <_> - 0 5 5 2 3. - <_> - - <_> - 6 0 3 2 -1. - <_> - 7 0 1 2 3. - <_> - - <_> - 12 8 8 2 -1. - <_> - 16 8 4 1 2. - <_> - 12 9 4 1 2. - <_> - - <_> - 5 8 12 1 -1. - <_> - 9 8 4 1 3. - <_> - - <_> - 3 13 3 3 -1. - <_> - 3 14 3 1 3. - <_> - - <_> - 5 13 3 2 -1. - <_> - 5 14 3 1 2. - <_> - - <_> - 9 15 3 3 -1. - <_> - 9 16 3 1 3. - <_> - - <_> - 7 15 7 3 -1. - <_> - 7 16 7 1 3. - <_> - - <_> - 3 14 11 6 -1. - <_> - 3 16 11 2 3. - <_> - - <_> - 0 19 14 1 -1. - <_> - 7 19 7 1 2. - <_> - - <_> - 9 17 6 2 -1. - <_> - 11 17 2 2 3. - <_> - - <_> - 12 11 6 2 -1. - <_> - 14 11 2 2 3. - <_> - - <_> - 5 17 6 2 -1. - <_> - 7 17 2 2 3. - <_> - - <_> - 0 1 9 10 -1. - <_> - 3 1 3 10 3. - <_> - - <_> - 10 1 3 3 -1. - <_> - 11 1 1 3 3. - <_> - - <_> - 9 5 6 4 -1. - <_> - 9 5 3 4 2. - <_> - - <_> - 7 1 3 3 -1. - <_> - 8 1 1 3 3. - <_> - - <_> - 0 4 4 11 -1. - <_> - 2 4 2 11 2. - <_> - - <_> - 9 5 6 4 -1. - <_> - 9 5 3 4 2. - <_> - - <_> - 6 0 8 10 -1. - <_> - 10 0 4 5 2. - <_> - 6 5 4 5 2. - <_> - - <_> - 6 6 5 14 -1. - <_> - 6 13 5 7 2. - <_> - - <_> - 8 5 4 14 -1. - <_> - 8 12 4 7 2. - <_> - - <_> - 7 7 6 5 -1. - <_> - 9 7 2 5 3. - <_> - - <_> - 9 3 3 9 -1. - <_> - 9 6 3 3 3. - <_> - - <_> - 8 1 3 3 -1. - <_> - 9 1 1 3 3. - <_> - - <_> - 9 6 2 4 -1. - <_> - 10 6 1 4 2. - <_> - - <_> - 10 8 6 9 -1. - <_> - 10 8 3 9 2. - <_> - - <_> - 16 4 3 8 -1. - <_> - 17 4 1 8 3. - <_> - - <_> - 5 9 10 6 -1. - <_> - 5 9 5 3 2. - <_> - 10 12 5 3 2. - <_> - - <_> - 5 5 6 4 -1. - <_> - 8 5 3 4 2. - <_> - - <_> - 9 8 4 2 -1. - <_> - 9 9 4 1 2. - <_> - - <_> - 11 7 2 2 -1. - <_> - 11 7 1 2 2. - <_> - - <_> - 8 12 4 8 -1. - <_> - 8 12 2 4 2. - <_> - 10 16 2 4 2. - <_> - - <_> - 0 1 4 9 -1. - <_> - 0 4 4 3 3. - <_> - - <_> - 9 10 3 3 -1. - <_> - 9 11 3 1 3. - <_> - - <_> - 8 11 4 2 -1. - <_> - 8 12 4 1 2. - <_> - - <_> - 7 8 4 2 -1. - <_> - 7 9 4 1 2. - <_> - - <_> - 7 8 6 1 -1. - <_> - 9 8 2 1 3. - <_> - - <_> - 16 0 4 9 -1. - <_> - 16 0 2 9 2. - <_> - - <_> - 16 0 3 6 -1. - <_> - 16 3 3 3 2. - <_> - - <_> - 0 0 4 9 -1. - <_> - 2 0 2 9 2. - <_> - - <_> - 1 0 3 6 -1. - <_> - 1 3 3 3 2. - <_> - - <_> - 9 7 6 9 -1. - <_> - 11 7 2 9 3. - <_> - - <_> - 10 6 3 6 -1. - <_> - 11 6 1 6 3. - <_> - - <_> - 1 2 18 2 -1. - <_> - 1 2 9 1 2. - <_> - 10 3 9 1 2. - <_> - - <_> - 5 8 6 8 -1. - <_> - 7 8 2 8 3. - <_> - - <_> - 9 0 6 16 -1. - <_> - 11 0 2 16 3. - <_> - - <_> - 14 1 6 18 -1. - <_> - 17 1 3 9 2. - <_> - 14 10 3 9 2. - <_> - - <_> - 2 9 2 3 -1. - <_> - 2 10 2 1 3. - <_> - - <_> - 0 1 6 18 -1. - <_> - 0 1 3 9 2. - <_> - 3 10 3 9 2. - <_> - - <_> - 11 8 4 12 -1. - <_> - 11 8 2 12 2. - <_> - - <_> - 2 1 18 18 -1. - <_> - 2 10 18 9 2. - <_> - - <_> - 6 3 3 1 -1. - <_> - 7 3 1 1 3. - <_> - - <_> - 4 12 2 2 -1. - <_> - 4 13 2 1 2. - <_> - - <_> - 8 13 5 3 -1. - <_> - 8 14 5 1 3. - <_> - - <_> - 8 14 4 3 -1. - <_> - 8 15 4 1 3. - <_> - - <_> - 3 12 5 3 -1. - <_> - 3 13 5 1 3. - <_> - - <_> - 6 3 3 4 -1. - <_> - 7 3 1 4 3. - <_> - - <_> - 11 10 2 2 -1. - <_> - 12 10 1 1 2. - <_> - 11 11 1 1 2. - <_> - - <_> - 5 8 12 1 -1. - <_> - 9 8 4 1 3. - <_> - - <_> - 8 4 4 8 -1. - <_> - 10 4 2 8 2. - <_> - - <_> - 6 6 8 5 -1. - <_> - 10 6 4 5 2. - <_> - - <_> - 10 4 6 4 -1. - <_> - 12 4 2 4 3. - <_> - - <_> - 12 7 2 2 -1. - <_> - 13 7 1 1 2. - <_> - 12 8 1 1 2. - <_> - - <_> - 3 5 10 8 -1. - <_> - 3 9 10 4 2. - <_> - - <_> - 7 1 2 12 -1. - <_> - 7 7 2 6 2. - <_> - - <_> - 12 7 2 2 -1. - <_> - 13 7 1 1 2. - <_> - 12 8 1 1 2. - <_> - - <_> - 11 13 1 6 -1. - <_> - 11 16 1 3 2. - <_> - - <_> - 5 1 6 15 -1. - <_> - 7 1 2 15 3. - <_> - - <_> - 6 7 2 2 -1. - <_> - 6 7 1 1 2. - <_> - 7 8 1 1 2. - <_> - - <_> - 17 5 2 2 -1. - <_> - 17 6 2 1 2. - <_> - - <_> - 10 3 4 10 -1. - <_> - 12 3 2 5 2. - <_> - 10 8 2 5 2. - <_> - - <_> - 1 5 2 2 -1. - <_> - 1 6 2 1 2. - <_> - - <_> - 7 10 2 2 -1. - <_> - 7 10 1 1 2. - <_> - 8 11 1 1 2. - <_> - - <_> - 3 12 14 4 -1. - <_> - 10 12 7 2 2. - <_> - 3 14 7 2 2. - <_> - - <_> - 9 15 3 2 -1. - <_> - 9 16 3 1 2. - <_> - - <_> - 1 13 3 3 -1. - <_> - 1 14 3 1 3. - <_> - - <_> - 0 3 1 2 -1. - <_> - 0 4 1 1 2. - <_> - - <_> - 7 7 6 1 -1. - <_> - 9 7 2 1 3. - <_> - - <_> - 0 4 16 6 -1. - <_> - 0 6 16 2 3. - <_> - - <_> - 9 3 2 14 -1. - <_> - 9 10 2 7 2. - <_> - - <_> - 12 0 4 3 -1. - <_> - 12 0 2 3 2. - <_> - - <_> - 4 18 12 2 -1. - <_> - 8 18 4 2 3. - <_> - - <_> - 4 10 12 4 -1. - <_> - 8 10 4 4 3. - <_> - - <_> - 9 9 2 2 -1. - <_> - 9 10 2 1 2. - <_> - - <_> - 14 1 2 8 -1. - <_> - 15 1 1 4 2. - <_> - 14 5 1 4 2. - <_> - - <_> - 3 4 9 1 -1. - <_> - 6 4 3 1 3. - <_> - - <_> - 3 3 4 2 -1. - <_> - 3 4 4 1 2. - <_> - - <_> - 11 15 2 4 -1. - <_> - 11 17 2 2 2. - <_> - - <_> - 14 13 2 6 -1. - <_> - 14 15 2 2 3. - <_> - - <_> - 6 6 1 6 -1. - <_> - 6 9 1 3 2. - <_> - - <_> - 6 10 8 8 -1. - <_> - 6 14 8 4 2. - <_> - - <_> - 8 13 4 3 -1. - <_> - 8 14 4 1 3. - <_> - - <_> - 10 11 4 8 -1. - <_> - 10 15 4 4 2. - <_> - - <_> - 5 11 6 1 -1. - <_> - 7 11 2 1 3. - <_> - - <_> - 5 4 6 10 -1. - <_> - 8 4 3 10 2. - <_> - - <_> - 14 2 6 3 -1. - <_> - 14 3 6 1 3. - <_> - - <_> - 9 12 3 2 -1. - <_> - 9 13 3 1 2. - <_> - - <_> - 8 1 4 6 -1. - <_> - 8 3 4 2 3. - <_> - - <_> - 3 5 13 8 -1. - <_> - 3 9 13 4 2. - <_> - - <_> - 12 5 5 3 -1. - <_> - 12 6 5 1 3. - <_> - - <_> - 5 14 15 6 -1. - <_> - 5 16 15 2 3. - <_> - - <_> - 3 5 5 3 -1. - <_> - 3 6 5 1 3. - <_> - - <_> - 9 14 2 6 -1. - <_> - 9 14 1 3 2. - <_> - 10 17 1 3 2. - <_> - - <_> - 9 12 3 2 -1. - <_> - 9 13 3 1 2. - <_> - - <_> - 9 13 3 2 -1. - <_> - 9 14 3 1 2. - <_> - - <_> - 0 2 6 3 -1. - <_> - 0 3 6 1 3. - <_> - - <_> - 0 1 9 11 -1. - <_> - 3 1 3 11 3. - <_> - - <_> - 8 13 4 6 -1. - <_> - 10 13 2 3 2. - <_> - 8 16 2 3 2. - <_> - - <_> - 7 13 6 3 -1. - <_> - 7 14 6 1 3. - <_> - - <_> - 3 12 14 4 -1. - <_> - 3 12 7 2 2. - <_> - 10 14 7 2 2. - <_> - - <_> - 7 14 1 4 -1. - <_> - 7 16 1 2 2. - <_> - - <_> - 8 13 4 6 -1. - <_> - 10 13 2 3 2. - <_> - 8 16 2 3 2. - <_> - - <_> - 10 14 1 3 -1. - <_> - 10 15 1 1 3. - <_> - - <_> - 8 13 4 6 -1. - <_> - 8 13 2 3 2. - <_> - 10 16 2 3 2. - <_> - - <_> - 9 14 1 3 -1. - <_> - 9 15 1 1 3. - <_> - - <_> - 10 15 2 3 -1. - <_> - 10 16 2 1 3. - <_> - - <_> - 11 16 1 2 -1. - <_> - 11 17 1 1 2. - <_> - - <_> - 9 0 2 2 -1. - <_> - 9 1 2 1 2. - <_> - - <_> - 0 1 5 8 -1. - <_> - 0 5 5 4 2. - <_> - - <_> - 10 14 2 3 -1. - <_> - 10 15 2 1 3. - <_> - - <_> - 10 13 2 3 -1. - <_> - 10 14 2 1 3. - <_> - - <_> - 0 3 16 6 -1. - <_> - 0 6 16 3 2. - <_> - - <_> - 4 1 2 2 -1. - <_> - 5 1 1 2 2. - <_> - - <_> - 9 7 2 3 -1. - <_> - 9 8 2 1 3. - <_> - - <_> - 10 8 2 12 -1. - <_> - 10 12 2 4 3. - <_> - - <_> - 9 7 2 2 -1. - <_> - 10 7 1 2 2. - <_> - - <_> - 5 0 6 8 -1. - <_> - 7 0 2 8 3. - <_> - - <_> - 9 7 3 6 -1. - <_> - 10 7 1 6 3. - <_> - - <_> - 8 12 10 8 -1. - <_> - 8 16 10 4 2. - <_> - - <_> - 8 7 3 6 -1. - <_> - 9 7 1 6 3. - <_> - - <_> - 4 7 12 2 -1. - <_> - 10 7 6 2 2. - <_> - - <_> - 8 6 8 3 -1. - <_> - 8 6 4 3 2. - <_> - - <_> - 16 15 3 3 -1. - <_> - 16 16 3 1 3. - <_> - - <_> - 4 6 12 3 -1. - <_> - 10 6 6 3 2. - <_> - - <_> - 7 8 3 5 -1. - <_> - 8 8 1 5 3. - <_> - - <_> - 0 10 20 2 -1. - <_> - 10 10 10 1 2. - <_> - 0 11 10 1 2. - <_> - - <_> - 11 16 9 4 -1. - <_> - 14 16 3 4 3. - <_> - - <_> - 0 5 3 4 -1. - <_> - 1 5 1 4 3. - <_> - - <_> - 8 15 4 2 -1. - <_> - 8 15 2 1 2. - <_> - 10 16 2 1 2. - <_> - - <_> - 1 8 19 3 -1. - <_> - 1 9 19 1 3. - <_> - - <_> - 15 16 3 3 -1. - <_> - 15 17 3 1 3. - <_> - - <_> - 0 4 20 10 -1. - <_> - 0 4 10 5 2. - <_> - 10 9 10 5 2. - <_> - - <_> - 2 14 7 6 -1. - <_> - 2 16 7 2 3. - <_> - - <_> - 8 6 6 6 -1. - <_> - 10 6 2 6 3. - <_> - - <_> - 16 4 4 6 -1. - <_> - 16 6 4 2 3. - <_> - - <_> - 7 13 6 3 -1. - <_> - 7 14 6 1 3. - <_> - - <_> - 7 13 4 3 -1. - <_> - 7 14 4 1 3. - <_> - - <_> - 13 13 6 2 -1. - <_> - 13 14 6 1 2. - <_> - - <_> - 14 12 2 3 -1. - <_> - 14 13 2 1 3. - <_> - - <_> - 1 13 6 2 -1. - <_> - 1 14 6 1 2. - <_> - - <_> - 4 12 2 3 -1. - <_> - 4 13 2 1 3. - <_> - - <_> - 17 4 3 5 -1. - <_> - 18 4 1 5 3. - <_> - - <_> - 5 5 14 8 -1. - <_> - 12 5 7 4 2. - <_> - 5 9 7 4 2. - <_> - - <_> - 6 8 6 5 -1. - <_> - 8 8 2 5 3. - <_> - - <_> - 0 4 4 6 -1. - <_> - 0 6 4 2 3. - <_> - - <_> - 9 1 3 6 -1. - <_> - 10 1 1 6 3. - <_> - - <_> - 10 4 6 3 -1. - <_> - 10 5 6 1 3. - <_> - - <_> - 8 1 3 6 -1. - <_> - 9 1 1 6 3. - <_> - - <_> - 4 4 6 3 -1. - <_> - 4 5 6 1 3. - <_> - - <_> - 12 4 3 3 -1. - <_> - 12 5 3 1 3. - <_> - - <_> - 12 11 4 2 -1. - <_> - 12 12 4 1 2. - <_> - - <_> - 0 2 20 6 -1. - <_> - 0 2 10 3 2. - <_> - 10 5 10 3 2. - <_> - - <_> - 5 4 3 3 -1. - <_> - 5 5 3 1 3. - <_> - - <_> - 2 10 16 4 -1. - <_> - 10 10 8 2 2. - <_> - 2 12 8 2 2. - <_> - - <_> - 3 10 16 6 -1. - <_> - 11 10 8 3 2. - <_> - 3 13 8 3 2. - <_> - - <_> - 1 10 16 6 -1. - <_> - 1 10 8 3 2. - <_> - 9 13 8 3 2. - <_> - - <_> - 4 7 2 4 -1. - <_> - 5 7 1 4 2. - <_> - - <_> - 11 16 9 4 -1. - <_> - 14 16 3 4 3. - <_> - - <_> - 3 16 14 4 -1. - <_> - 10 16 7 2 2. - <_> - 3 18 7 2 2. - <_> - - <_> - 0 16 9 4 -1. - <_> - 3 16 3 4 3. - <_> - - <_> - 1 14 6 6 -1. - <_> - 1 14 3 3 2. - <_> - 4 17 3 3 2. - <_> - - <_> - 9 0 2 1 -1. - <_> - 9 0 1 1 2. - <_> - - <_> - 6 7 8 10 -1. - <_> - 10 7 4 5 2. - <_> - 6 12 4 5 2. - <_> - - <_> - 2 15 1 2 -1. - <_> - 2 16 1 1 2. - <_> - - <_> - 0 14 7 6 -1. - <_> - 0 16 7 2 3. - <_> - - <_> - 7 8 6 2 -1. - <_> - 7 9 6 1 2. - <_> - - <_> - 9 2 2 15 -1. - <_> - 9 7 2 5 3. - <_> - - <_> - 5 6 2 2 -1. - <_> - 5 7 2 1 2. - <_> - - <_> - 6 6 8 3 -1. - <_> - 6 7 8 1 3. - <_> - - <_> - 12 13 5 6 -1. - <_> - 12 15 5 2 3. - <_> - - <_> - 0 0 20 18 -1. - <_> - 0 9 20 9 2. - <_> - - <_> - 5 1 6 6 -1. - <_> - 7 1 2 6 3. - <_> - - <_> - 5 1 4 9 -1. - <_> - 7 1 2 9 2. - <_> - - <_> - 1 19 18 1 -1. - <_> - 7 19 6 1 3. - <_> - - <_> - 14 16 5 2 -1. - <_> - 14 17 5 1 2. - <_> - - <_> - 0 5 15 10 -1. - <_> - 0 10 15 5 2. - <_> - - <_> - 7 15 4 2 -1. - <_> - 7 15 2 1 2. - <_> - 9 16 2 1 2. - <_> - - <_> - 14 11 2 2 -1. - <_> - 14 12 2 1 2. - <_> - - <_> - 9 8 3 3 -1. - <_> - 9 9 3 1 3. - <_> - - <_> - 4 11 2 2 -1. - <_> - 4 12 2 1 2. - <_> - - <_> - 8 8 3 3 -1. - <_> - 8 9 3 1 3. - <_> - - <_> - 9 10 2 3 -1. - <_> - 9 11 2 1 3. - <_> - - <_> - 8 8 4 3 -1. - <_> - 8 9 4 1 3. - <_> - - <_> - 1 9 4 10 -1. - <_> - 1 9 2 5 2. - <_> - 3 14 2 5 2. - <_> - - <_> - 0 12 6 8 -1. - <_> - 2 12 2 8 3. - <_> - - <_> - 9 1 4 2 -1. - <_> - 11 1 2 1 2. - <_> - 9 2 2 1 2. - <_> - - <_> - 12 13 7 6 -1. - <_> - 12 15 7 2 3. - <_> - - <_> - 7 0 2 3 -1. - <_> - 7 1 2 1 3. - <_> - - <_> - 7 14 6 3 -1. - <_> - 9 14 2 3 3. - <_> - - <_> - 9 6 6 4 -1. - <_> - 11 6 2 4 3. - <_> - - <_> - 8 10 8 3 -1. - <_> - 8 10 4 3 2. - <_> - - <_> - 6 10 4 3 -1. - <_> - 8 10 2 3 2. - <_> - - <_> - 6 8 3 5 -1. - <_> - 7 8 1 5 3. - <_> - - <_> - 0 4 8 1 -1. - <_> - 4 4 4 1 2. - <_> - - <_> - 8 2 2 6 -1. - <_> - 8 2 1 3 2. - <_> - 9 5 1 3 2. - <_> - - <_> - 0 7 20 6 -1. - <_> - 0 9 20 2 3. - <_> - - <_> - 12 10 3 6 -1. - <_> - 12 13 3 3 2. - <_> - - <_> - 8 15 1 4 -1. - <_> - 8 17 1 2 2. - <_> - - <_> - 5 16 2 4 -1. - <_> - 5 18 2 2 2. - <_> - - <_> - 6 2 8 12 -1. - <_> - 6 6 8 4 3. - <_> - - <_> - 4 7 12 2 -1. - <_> - 8 7 4 2 3. - <_> - - <_> - 7 0 6 1 -1. - <_> - 9 0 2 1 3. - <_> - - <_> - 8 11 3 3 -1. - <_> - 8 12 3 1 3. - <_> - - <_> - 12 11 3 6 -1. - <_> - 12 14 3 3 2. - <_> - - <_> - 11 2 6 10 -1. - <_> - 14 2 3 5 2. - <_> - 11 7 3 5 2. - <_> - - <_> - 5 7 10 12 -1. - <_> - 5 7 5 6 2. - <_> - 10 13 5 6 2. - <_> - - <_> - 4 4 2 10 -1. - <_> - 4 9 2 5 2. - <_> - - <_> - 9 7 2 3 -1. - <_> - 9 7 1 3 2. - <_> - - <_> - 11 9 6 2 -1. - <_> - 11 9 3 2 2. - <_> - - <_> - 4 7 2 2 -1. - <_> - 5 7 1 2 2. - <_> - - <_> - 0 2 4 6 -1. - <_> - 0 4 4 2 3. - <_> - - <_> - 10 7 3 4 -1. - <_> - 11 7 1 4 3. - <_> - - <_> - 9 7 3 5 -1. - <_> - 10 7 1 5 3. - <_> - - <_> - 9 1 1 3 -1. - <_> - 9 2 1 1 3. - <_> - - <_> - 0 6 16 6 -1. - <_> - 0 6 8 3 2. - <_> - 8 9 8 3 2. - <_> - - <_> - 10 15 3 3 -1. - <_> - 10 16 3 1 3. - <_> - - <_> - 9 14 4 3 -1. - <_> - 9 15 4 1 3. - <_> - - <_> - 3 2 6 10 -1. - <_> - 3 2 3 5 2. - <_> - 6 7 3 5 2. - <_> - - <_> - 3 0 14 2 -1. - <_> - 3 1 14 1 2. - <_> - - <_> - 9 14 3 3 -1. - <_> - 9 15 3 1 3. - <_> - - <_> - 10 15 3 3 -1. - <_> - 10 16 3 1 3. - <_> - - <_> - 9 13 2 6 -1. - <_> - 9 16 2 3 2. - <_> - - <_> - 7 13 6 3 -1. - <_> - 7 14 6 1 3. - <_> - - <_> - 12 11 3 6 -1. - <_> - 12 14 3 3 2. - <_> - - <_> - 8 12 5 2 -1. - <_> - 8 13 5 1 2. - <_> - - <_> - 5 11 3 6 -1. - <_> - 5 14 3 3 2. - <_> - - <_> - 8 12 3 2 -1. - <_> - 8 13 3 1 2. - <_> - - <_> - 11 13 7 6 -1. - <_> - 11 15 7 2 3. - <_> - - <_> - 7 14 6 3 -1. - <_> - 7 15 6 1 3. - <_> - - <_> - 3 13 14 4 -1. - <_> - 3 13 7 2 2. - <_> - 10 15 7 2 2. - <_> - - <_> - 8 14 4 6 -1. - <_> - 8 14 2 3 2. - <_> - 10 17 2 3 2. - <_> - - <_> - 8 15 4 3 -1. - <_> - 8 16 4 1 3. - <_> - - <_> - 7 16 6 2 -1. - <_> - 9 16 2 2 3. - <_> - - <_> - 7 7 6 2 -1. - <_> - 7 8 6 1 2. - <_> - - <_> - 3 9 13 3 -1. - <_> - 3 10 13 1 3. - <_> - - <_> - 9 8 3 4 -1. - <_> - 9 10 3 2 2. - <_> - - <_> - 8 10 4 3 -1. - <_> - 8 11 4 1 3. - <_> - - <_> - 7 7 3 4 -1. - <_> - 8 7 1 4 3. - <_> - - <_> - 8 7 3 5 -1. - <_> - 9 7 1 5 3. - <_> - - <_> - 12 3 3 4 -1. - <_> - 13 3 1 4 3. - <_> - - <_> - 9 7 2 3 -1. - <_> - 9 7 1 3 2. - <_> - - <_> - 5 3 3 4 -1. - <_> - 6 3 1 4 3. - <_> - - <_> - 3 7 12 1 -1. - <_> - 7 7 4 1 3. - <_> - - <_> - 12 5 3 3 -1. - <_> - 12 6 3 1 3. - <_> - - <_> - 11 2 6 2 -1. - <_> - 11 3 6 1 2. - <_> - - <_> - 3 2 14 2 -1. - <_> - 3 2 7 1 2. - <_> - 10 3 7 1 2. - <_> - - <_> - 6 1 7 14 -1. - <_> - 6 8 7 7 2. - <_> - - <_> - 8 0 12 5 -1. - <_> - 8 0 6 5 2. - <_> - - <_> - 1 9 18 1 -1. - <_> - 7 9 6 1 3. - <_> - - <_> - 0 0 10 5 -1. - <_> - 5 0 5 5 2. - <_> - - <_> - 2 5 8 15 -1. - <_> - 2 10 8 5 3. - <_> - - <_> - 12 5 3 3 -1. - <_> - 12 6 3 1 3. - <_> - - <_> - 13 4 2 3 -1. - <_> - 13 5 2 1 3. - <_> - - <_> - 2 15 4 3 -1. - <_> - 2 16 4 1 3. - <_> - - <_> - 5 6 10 3 -1. - <_> - 10 6 5 3 2. - <_> - - <_> - 11 6 2 2 -1. - <_> - 12 6 1 1 2. - <_> - 11 7 1 1 2. - <_> - - <_> - 12 4 4 3 -1. - <_> - 12 5 4 1 3. - <_> - - <_> - 7 6 2 2 -1. - <_> - 7 6 1 1 2. - <_> - 8 7 1 1 2. - <_> - - <_> - 4 4 4 3 -1. - <_> - 4 5 4 1 3. - <_> - - <_> - 11 4 3 3 -1. - <_> - 12 4 1 3 3. - <_> - - <_> - 9 3 2 1 -1. - <_> - 9 3 1 1 2. - <_> - - <_> - 4 5 5 3 -1. - <_> - 4 6 5 1 3. - <_> - - <_> - 4 6 4 3 -1. - <_> - 4 7 4 1 3. - <_> - - <_> - 11 4 3 3 -1. - <_> - 12 4 1 3 3. - <_> - - <_> - 8 8 4 3 -1. - <_> - 8 9 4 1 3. - <_> - - <_> - 6 4 3 3 -1. - <_> - 7 4 1 3 3. - <_> - - <_> - 4 14 1 3 -1. - <_> - 4 15 1 1 3. - <_> - - <_> - 9 7 2 3 -1. - <_> - 9 7 1 3 2. - <_> - - <_> - 17 0 3 2 -1. - <_> - 17 1 3 1 2. - <_> - - <_> - 8 10 2 9 -1. - <_> - 8 13 2 3 3. - <_> - - <_> - 0 8 18 2 -1. - <_> - 0 9 18 1 2. - <_> - - <_> - 9 15 2 3 -1. - <_> - 9 16 2 1 3. - <_> - - <_> - 8 7 4 3 -1. - <_> - 8 8 4 1 3. - <_> - - <_> - 1 14 6 6 -1. - <_> - 1 14 3 3 2. - <_> - 4 17 3 3 2. - <_> - - <_> - 0 18 6 2 -1. - <_> - 0 19 6 1 2. - <_> - - <_> - 12 9 4 3 -1. - <_> - 12 9 2 3 2. - <_> - - <_> - 9 8 3 8 -1. - <_> - 10 8 1 8 3. - <_> - - <_> - 4 9 4 3 -1. - <_> - 6 9 2 3 2. - <_> - - <_> - 4 18 6 1 -1. - <_> - 6 18 2 1 3. - <_> - - <_> - 9 7 3 2 -1. - <_> - 10 7 1 2 3. - <_> - - <_> - 6 7 8 12 -1. - <_> - 10 7 4 6 2. - <_> - 6 13 4 6 2. - <_> - - <_> - 8 7 3 2 -1. - <_> - 9 7 1 2 3. - <_> - - <_> - 8 7 3 6 -1. - <_> - 9 7 1 6 3. - <_> - - <_> - 3 16 14 4 -1. - <_> - 10 16 7 2 2. - <_> - 3 18 7 2 2. - <_> - - <_> - 1 14 18 4 -1. - <_> - 10 14 9 2 2. - <_> - 1 16 9 2 2. - <_> - - <_> - 8 7 3 3 -1. - <_> - 8 8 3 1 3. - <_> - - <_> - 0 4 20 12 -1. - <_> - 0 4 10 6 2. - <_> - 10 10 10 6 2. - <_> - - <_> - 5 5 10 12 -1. - <_> - 10 5 5 6 2. - <_> - 5 11 5 6 2. - <_> - - <_> - 10 2 4 7 -1. - <_> - 10 2 2 7 2. - <_> - - <_> - 8 11 4 3 -1. - <_> - 8 12 4 1 3. - <_> - - <_> - 8 12 3 3 -1. - <_> - 8 13 3 1 3. - <_> - - <_> - 13 13 5 6 -1. - <_> - 13 15 5 2 3. - <_> - - <_> - 7 0 6 6 -1. - <_> - 9 0 2 6 3. - <_> - - <_> - 2 13 5 6 -1. - <_> - 2 15 5 2 3. - <_> - - <_> - 0 4 2 12 -1. - <_> - 0 4 1 6 2. - <_> - 1 10 1 6 2. - <_> - - <_> - 9 19 3 1 -1. - <_> - 10 19 1 1 3. - <_> - - <_> - 18 0 2 6 -1. - <_> - 18 2 2 2 3. - <_> - - <_> - 0 3 1 6 -1. - <_> - 0 5 1 2 3. - <_> - - <_> - 0 0 3 6 -1. - <_> - 0 2 3 2 3. - <_> - - <_> - 17 2 3 7 -1. - <_> - 18 2 1 7 3. - <_> - - <_> - 10 3 4 7 -1. - <_> - 10 3 2 7 2. - <_> - - <_> - 0 2 3 7 -1. - <_> - 1 2 1 7 3. - <_> - - <_> - 6 2 4 8 -1. - <_> - 8 2 2 8 2. - <_> - - <_> - 13 0 1 4 -1. - <_> - 13 2 1 2 2. - <_> - - <_> - 5 1 12 5 -1. - <_> - 9 1 4 5 3. - <_> - - <_> - 6 0 1 4 -1. - <_> - 6 2 1 2 2. - <_> - - <_> - 3 1 12 5 -1. - <_> - 7 1 4 5 3. - <_> - - <_> - 9 12 3 8 -1. - <_> - 10 12 1 8 3. - <_> - - <_> - 7 13 6 1 -1. - <_> - 9 13 2 1 3. - <_> - - <_> - 7 14 6 3 -1. - <_> - 7 15 6 1 3. - <_> - - <_> - 5 16 7 3 -1. - <_> - 5 17 7 1 3. - <_> - - <_> - 0 12 20 6 -1. - <_> - 0 14 20 2 3. - <_> - - <_> - 4 18 14 2 -1. - <_> - 4 19 14 1 2. - <_> - - <_> - 8 12 3 8 -1. - <_> - 9 12 1 8 3. - <_> - - <_> - 7 13 3 3 -1. - <_> - 7 14 3 1 3. - <_> - - <_> - 5 5 12 10 -1. - <_> - 11 5 6 5 2. - <_> - 5 10 6 5 2. - <_> - - <_> - 8 1 5 10 -1. - <_> - 8 6 5 5 2. - <_> - - <_> - 5 4 9 12 -1. - <_> - 5 10 9 6 2. - <_> - - <_> - 7 13 6 6 -1. - <_> - 7 15 6 2 3. - <_> - - <_> - 8 4 5 16 -1. - <_> - 8 12 5 8 2. - <_> - - <_> - 8 12 4 6 -1. - <_> - 8 15 4 3 2. - <_> - - <_> - 7 13 2 2 -1. - <_> - 7 13 1 1 2. - <_> - 8 14 1 1 2. - <_> - - <_> - 7 12 2 2 -1. - <_> - 7 12 1 1 2. - <_> - 8 13 1 1 2. - <_> - - <_> - 18 0 2 14 -1. - <_> - 18 0 1 14 2. - <_> - - <_> - 12 11 7 2 -1. - <_> - 12 12 7 1 2. - <_> - - <_> - 1 18 1 2 -1. - <_> - 1 19 1 1 2. - <_> - - <_> - 2 18 1 2 -1. - <_> - 2 19 1 1 2. - <_> - - <_> - 9 7 2 1 -1. - <_> - 9 7 1 1 2. - <_> - - <_> - 9 6 2 3 -1. - <_> - 9 6 1 3 2. - <_> - - <_> - 3 1 2 2 -1. - <_> - 4 1 1 2 2. - <_> - - <_> - 3 0 3 2 -1. - <_> - 3 1 3 1 2. - <_> - - <_> - 12 10 3 4 -1. - <_> - 12 12 3 2 2. - <_> - - <_> - 7 7 8 2 -1. - <_> - 7 8 8 1 2. - <_> - - <_> - 8 8 3 4 -1. - <_> - 8 10 3 2 2. - <_> - - <_> - 7 12 6 3 -1. - <_> - 7 13 6 1 3. - <_> - - <_> - 0 2 10 3 -1. - <_> - 5 2 5 3 2. - <_> - - <_> - 0 1 20 6 -1. - <_> - 0 3 20 2 3. - <_> - - <_> - 7 6 6 3 -1. - <_> - 9 6 2 3 3. - <_> - - <_> - 3 7 14 4 -1. - <_> - 3 9 14 2 2. - <_> - - <_> - 5 7 3 6 -1. - <_> - 5 9 3 2 3. - <_> - - <_> - 8 8 3 12 -1. - <_> - 8 12 3 4 3. - <_> - - <_> - 9 17 6 2 -1. - <_> - 12 17 3 1 2. - <_> - 9 18 3 1 2. - <_> - - <_> - 10 17 4 3 -1. - <_> - 10 18 4 1 3. - <_> - - <_> - 4 2 4 2 -1. - <_> - 4 3 4 1 2. - <_> - - <_> - 7 3 6 14 -1. - <_> - 9 3 2 14 3. - <_> - - <_> - 15 13 1 6 -1. - <_> - 15 16 1 3 2. - <_> - - <_> - 13 14 2 6 -1. - <_> - 13 16 2 2 3. - <_> - - <_> - 4 11 5 6 -1. - <_> - 4 14 5 3 2. - <_> - - <_> - 4 17 4 2 -1. - <_> - 6 17 2 2 2. - <_> - - <_> - 0 6 20 2 -1. - <_> - 0 6 10 2 2. - <_> - - <_> - 6 5 10 12 -1. - <_> - 11 5 5 6 2. - <_> - 6 11 5 6 2. - <_> - - <_> - 4 0 2 12 -1. - <_> - 4 0 1 6 2. - <_> - 5 6 1 6 2. - <_> - - <_> - 4 1 6 2 -1. - <_> - 6 1 2 2 3. - <_> - - <_> - 13 7 2 1 -1. - <_> - 13 7 1 1 2. - <_> - - <_> - 5 5 15 6 -1. - <_> - 5 7 15 2 3. - <_> - - <_> - 1 10 18 2 -1. - <_> - 1 10 9 1 2. - <_> - 10 11 9 1 2. - <_> - - <_> - 1 6 15 7 -1. - <_> - 6 6 5 7 3. - <_> - - <_> - 8 14 4 3 -1. - <_> - 8 15 4 1 3. - <_> - - <_> - 9 14 3 3 -1. - <_> - 9 15 3 1 3. - <_> - - <_> - 8 14 4 3 -1. - <_> - 8 15 4 1 3. - <_> - - <_> - 8 13 3 2 -1. - <_> - 8 14 3 1 2. - <_> - - <_> - 15 14 5 3 -1. - <_> - 15 15 5 1 3. - <_> - - <_> - 0 14 20 1 -1. - <_> - 0 14 10 1 2. - <_> - - <_> - 0 14 6 3 -1. - <_> - 0 15 6 1 3. - <_> - - <_> - 5 3 4 2 -1. - <_> - 5 4 4 1 2. - <_> - - <_> - 0 6 20 1 -1. - <_> - 0 6 10 1 2. - <_> - - <_> - 6 3 10 14 -1. - <_> - 11 3 5 7 2. - <_> - 6 10 5 7 2. - <_> - - <_> - 8 12 4 2 -1. - <_> - 8 13 4 1 2. - <_> - - <_> - 6 3 8 6 -1. - <_> - 6 3 4 3 2. - <_> - 10 6 4 3 2. - <_> - - <_> - 13 7 2 1 -1. - <_> - 13 7 1 1 2. - <_> - - <_> - 6 3 10 14 -1. - <_> - 11 3 5 7 2. - <_> - 6 10 5 7 2. - <_> - - <_> - 5 7 2 1 -1. - <_> - 6 7 1 1 2. - <_> - - <_> - 4 3 10 14 -1. - <_> - 4 3 5 7 2. - <_> - 9 10 5 7 2. - <_> - - <_> - 9 7 2 2 -1. - <_> - 9 7 1 2 2. - <_> - - <_> - 0 3 20 1 -1. - <_> - 0 3 10 1 2. - <_> - - <_> - 2 1 10 3 -1. - <_> - 2 2 10 1 3. - <_> - - <_> - 9 7 2 2 -1. - <_> - 10 7 1 2 2. - <_> - - <_> - 9 17 3 2 -1. - <_> - 10 17 1 2 3. - <_> - - <_> - 9 7 3 6 -1. - <_> - 10 7 1 6 3. - <_> - - <_> - 8 17 3 2 -1. - <_> - 9 17 1 2 3. - <_> - - <_> - 8 7 3 6 -1. - <_> - 9 7 1 6 3. - <_> - - <_> - 16 3 4 6 -1. - <_> - 16 5 4 2 3. - <_> - - <_> - 15 6 2 12 -1. - <_> - 16 6 1 6 2. - <_> - 15 12 1 6 2. - <_> - - <_> - 1 4 18 10 -1. - <_> - 1 4 9 5 2. - <_> - 10 9 9 5 2. - <_> - - <_> - 9 4 2 4 -1. - <_> - 9 6 2 2 2. - <_> - - <_> - 12 5 3 2 -1. - <_> - 12 6 3 1 2. - <_> - - <_> - 5 12 10 4 -1. - <_> - 5 14 10 2 2. - <_> - - <_> - 5 5 3 2 -1. - <_> - 5 6 3 1 2. - <_> - - <_> - 4 6 12 6 -1. - <_> - 8 6 4 6 3. - <_> - - <_> - 14 4 6 6 -1. - <_> - 14 6 6 2 3. - <_> - - <_> - 16 0 4 6 -1. - <_> - 18 0 2 3 2. - <_> - 16 3 2 3 2. - <_> - - <_> - 0 4 6 6 -1. - <_> - 0 6 6 2 3. - <_> - - <_> - 0 0 4 6 -1. - <_> - 0 0 2 3 2. - <_> - 2 3 2 3 2. - <_> - - <_> - 12 0 8 5 -1. - <_> - 12 0 4 5 2. - <_> - - <_> - 16 0 4 17 -1. - <_> - 16 0 2 17 2. - <_> - - <_> - 1 0 18 20 -1. - <_> - 7 0 6 20 3. - <_> - - <_> - 6 0 2 5 -1. - <_> - 7 0 1 5 2. - <_> - - <_> - 0 6 20 1 -1. - <_> - 0 6 10 1 2. - <_> - - <_> - 8 7 6 4 -1. - <_> - 10 7 2 4 3. - <_> - - <_> - 1 1 16 4 -1. - <_> - 1 1 8 2 2. - <_> - 9 3 8 2 2. - <_> - - <_> - 7 2 4 2 -1. - <_> - 7 2 2 1 2. - <_> - 9 3 2 1 2. - <_> - - <_> - 7 4 9 3 -1. - <_> - 7 5 9 1 3. - <_> - - <_> - 10 4 5 12 -1. - <_> - 10 10 5 6 2. - <_> - - <_> - 3 12 2 3 -1. - <_> - 3 13 2 1 3. - <_> - - <_> - 8 8 3 5 -1. - <_> - 9 8 1 5 3. - <_> - - <_> - 13 9 2 3 -1. - <_> - 13 9 1 3 2. - <_> - - <_> - 15 11 2 2 -1. - <_> - 15 12 2 1 2. - <_> - - <_> - 5 6 2 3 -1. - <_> - 5 7 2 1 3. - <_> - - <_> - 2 11 6 2 -1. - <_> - 2 12 6 1 2. - <_> - - <_> - 15 11 4 3 -1. - <_> - 15 12 4 1 3. - <_> - - <_> - 16 0 4 17 -1. - <_> - 16 0 2 17 2. - <_> - - <_> - 1 11 4 3 -1. - <_> - 1 12 4 1 3. - <_> - - <_> - 9 11 1 3 -1. - <_> - 9 12 1 1 3. - <_> - - <_> - 10 9 6 7 -1. - <_> - 10 9 3 7 2. - <_> - - <_> - 8 15 4 2 -1. - <_> - 8 16 4 1 2. - <_> - - <_> - 4 9 6 7 -1. - <_> - 7 9 3 7 2. - <_> - - <_> - 9 14 2 3 -1. - <_> - 9 15 2 1 3. - <_> - - <_> - 0 2 20 2 -1. - <_> - 10 2 10 1 2. - <_> - 0 3 10 1 2. - <_> - - <_> - 6 7 8 2 -1. - <_> - 6 8 8 1 2. - <_> - - <_> - 0 2 20 2 -1. - <_> - 0 2 10 1 2. - <_> - 10 3 10 1 2. - <_> - - <_> - 3 1 2 10 -1. - <_> - 3 1 1 5 2. - <_> - 4 6 1 5 2. - <_> - - <_> - 13 4 1 10 -1. - <_> - 13 9 1 5 2. - <_> - - <_> - 9 8 4 3 -1. - <_> - 9 9 4 1 3. - <_> - - <_> - 2 11 16 4 -1. - <_> - 2 11 8 2 2. - <_> - 10 13 8 2 2. - <_> - - <_> - 5 1 3 5 -1. - <_> - 6 1 1 5 3. - <_> - - <_> - 9 10 2 3 -1. - <_> - 9 11 2 1 3. - <_> - - <_> - 9 11 2 2 -1. - <_> - 9 12 2 1 2. - <_> - - <_> - 0 10 20 2 -1. - <_> - 0 11 20 1 2. - <_> - - <_> - 1 7 6 4 -1. - <_> - 1 7 3 2 2. - <_> - 4 9 3 2 2. - <_> - - <_> - 12 0 8 8 -1. - <_> - 16 0 4 4 2. - <_> - 12 4 4 4 2. - <_> - - <_> - 14 1 6 4 -1. - <_> - 16 1 2 4 3. - <_> - - <_> - 6 3 2 14 -1. - <_> - 6 10 2 7 2. - <_> - - <_> - 6 1 7 12 -1. - <_> - 6 7 7 6 2. - <_> - - <_> - 5 0 15 5 -1. - <_> - 10 0 5 5 3. - <_> - - <_> - 15 0 4 10 -1. - <_> - 15 0 2 10 2. - <_> - - <_> - 1 0 18 3 -1. - <_> - 7 0 6 3 3. - <_> - - <_> - 0 0 17 2 -1. - <_> - 0 1 17 1 2. - <_> - - <_> - 10 0 3 3 -1. - <_> - 11 0 1 3 3. - <_> - - <_> - 10 0 3 12 -1. - <_> - 11 0 1 12 3. - <_> - - <_> - 1 3 4 16 -1. - <_> - 1 3 2 8 2. - <_> - 3 11 2 8 2. - <_> - - <_> - 7 0 3 3 -1. - <_> - 8 0 1 3 3. - <_> - - <_> - 9 13 2 6 -1. - <_> - 9 16 2 3 2. - <_> - - <_> - 9 0 6 13 -1. - <_> - 11 0 2 13 3. - <_> - - <_> - 7 7 3 2 -1. - <_> - 8 7 1 2 3. - <_> - - <_> - 8 2 1 12 -1. - <_> - 8 6 1 4 3. - <_> - - <_> - 4 10 12 6 -1. - <_> - 10 10 6 3 2. - <_> - 4 13 6 3 2. - <_> - - <_> - 13 5 2 3 -1. - <_> - 13 6 2 1 3. - <_> - - <_> - 4 10 12 6 -1. - <_> - 4 10 6 3 2. - <_> - 10 13 6 3 2. - <_> - - <_> - 5 5 2 3 -1. - <_> - 5 6 2 1 3. - <_> - - <_> - 8 6 6 7 -1. - <_> - 10 6 2 7 3. - <_> - - <_> - 9 6 2 4 -1. - <_> - 9 6 1 4 2. - <_> - - <_> - 6 6 6 7 -1. - <_> - 8 6 2 7 3. - <_> - - <_> - 9 6 2 4 -1. - <_> - 10 6 1 4 2. - <_> - - <_> - 12 9 2 3 -1. - <_> - 12 9 1 3 2. - <_> - - <_> - 0 6 20 1 -1. - <_> - 0 6 10 1 2. - <_> - - <_> - 5 7 10 2 -1. - <_> - 10 7 5 2 2. - <_> - - <_> - 1 16 4 3 -1. - <_> - 1 17 4 1 3. - <_> - - <_> - 12 4 3 3 -1. - <_> - 12 5 3 1 3. - <_> - - <_> - 10 3 5 3 -1. - <_> - 10 4 5 1 3. - <_> - - <_> - 3 9 14 8 -1. - <_> - 3 9 7 4 2. - <_> - 10 13 7 4 2. - <_> - - <_> - 6 8 8 10 -1. - <_> - 6 8 4 5 2. - <_> - 10 13 4 5 2. - <_> - - <_> - 12 4 3 3 -1. - <_> - 12 5 3 1 3. - <_> - - <_> - 10 3 5 3 -1. - <_> - 10 4 5 1 3. - <_> - - <_> - 5 4 3 3 -1. - <_> - 5 5 3 1 3. - <_> - - <_> - 5 3 5 3 -1. - <_> - 5 4 5 1 3. - <_> - - <_> - 13 16 2 3 -1. - <_> - 13 17 2 1 3. - <_> - - <_> - 0 5 20 6 -1. - <_> - 0 7 20 2 3. - <_> - - <_> - 3 14 3 3 -1. - <_> - 3 15 3 1 3. - <_> - - <_> - 7 15 5 3 -1. - <_> - 7 16 5 1 3. - <_> - - <_> - 12 9 2 3 -1. - <_> - 12 9 1 3 2. - <_> - - <_> - 15 13 2 6 -1. - <_> - 15 13 1 6 2. - <_> - - <_> - 6 9 2 3 -1. - <_> - 7 9 1 3 2. - <_> - - <_> - 3 13 2 6 -1. - <_> - 4 13 1 6 2. - <_> - - <_> - 11 4 2 4 -1. - <_> - 11 4 1 4 2. - <_> - - <_> - 13 4 2 5 -1. - <_> - 13 4 1 5 2. - <_> - - <_> - 7 4 2 4 -1. - <_> - 8 4 1 4 2. - <_> - - <_> - 5 4 2 5 -1. - <_> - 6 4 1 5 2. - <_> - - <_> - 19 6 1 2 -1. - <_> - 19 7 1 1 2. - <_> - - <_> - 12 7 8 13 -1. - <_> - 12 7 4 13 2. - <_> - - <_> - 0 6 1 2 -1. - <_> - 0 7 1 1 2. - <_> - - <_> - 6 15 4 3 -1. - <_> - 6 16 4 1 3. - <_> - - <_> - 11 8 2 2 -1. - <_> - 11 9 2 1 2. - <_> - - <_> - 11 7 2 4 -1. - <_> - 11 7 1 4 2. - <_> - - <_> - 4 13 2 3 -1. - <_> - 4 14 2 1 3. - <_> - - <_> - 0 17 18 3 -1. - <_> - 6 17 6 3 3. - <_> - - <_> - 1 0 18 5 -1. - <_> - 7 0 6 5 3. - <_> - - <_> - 5 7 3 4 -1. - <_> - 5 9 3 2 2. - <_> - - <_> - 10 6 2 2 -1. - <_> - 10 6 1 2 2. - <_> - - <_> - 6 4 14 4 -1. - <_> - 13 4 7 2 2. - <_> - 6 6 7 2 2. - <_> - - <_> - 5 16 6 4 -1. - <_> - 5 16 3 2 2. - <_> - 8 18 3 2 2. - <_> - - <_> - 7 15 2 4 -1. - <_> - 7 17 2 2 2. - <_> - - <_> - 8 5 5 14 -1. - <_> - 8 12 5 7 2. - <_> - - <_> - 9 9 2 2 -1. - <_> - 9 10 2 1 2. - <_> - - <_> - 7 5 3 7 -1. - <_> - 8 5 1 7 3. - <_> - - <_> - 0 0 3 9 -1. - <_> - 0 3 3 3 3. - <_> - - <_> - 8 6 8 8 -1. - <_> - 12 6 4 4 2. - <_> - 8 10 4 4 2. - <_> - - <_> - 4 8 13 2 -1. - <_> - 4 9 13 1 2. - <_> - - <_> - 4 3 6 1 -1. - <_> - 6 3 2 1 3. - <_> - - <_> - 9 1 2 6 -1. - <_> - 9 3 2 2 3. - <_> - - <_> - 10 5 6 4 -1. - <_> - 12 5 2 4 3. - <_> - - <_> - 9 5 2 12 -1. - <_> - 9 9 2 4 3. - <_> - - <_> - 8 14 4 3 -1. - <_> - 8 15 4 1 3. - <_> - - <_> - 8 12 4 3 -1. - <_> - 8 13 4 1 3. - <_> - - <_> - 10 3 6 7 -1. - <_> - 12 3 2 7 3. - <_> - - <_> - 3 10 16 6 -1. - <_> - 3 12 16 2 3. - <_> - - <_> - 5 5 3 10 -1. - <_> - 5 10 3 5 2. - <_> - - <_> - 6 10 3 6 -1. - <_> - 6 13 3 3 2. - <_> - - <_> - 17 2 2 12 -1. - <_> - 17 2 1 12 2. - <_> - - <_> - 16 6 2 14 -1. - <_> - 16 13 2 7 2. - <_> - - <_> - 3 11 12 9 -1. - <_> - 3 14 12 3 3. - <_> - - <_> - 0 2 4 12 -1. - <_> - 2 2 2 12 2. - <_> - - <_> - 18 0 2 18 -1. - <_> - 18 0 1 18 2. - <_> - - <_> - 16 12 3 2 -1. - <_> - 16 13 3 1 2. - <_> - - <_> - 0 2 2 15 -1. - <_> - 1 2 1 15 2. - <_> - - <_> - 1 10 2 4 -1. - <_> - 1 12 2 2 2. - <_> - - <_> - 11 1 2 18 -1. - <_> - 11 1 1 18 2. - <_> - - <_> - 3 2 14 2 -1. - <_> - 10 2 7 1 2. - <_> - 3 3 7 1 2. - <_> - - <_> - 7 1 2 18 -1. - <_> - 8 1 1 18 2. - <_> - - <_> - 6 1 8 12 -1. - <_> - 6 7 8 6 2. - <_> - - <_> - 8 14 4 3 -1. - <_> - 8 15 4 1 3. - <_> - - <_> - 7 14 6 3 -1. - <_> - 7 15 6 1 3. - <_> - - <_> - 0 13 5 2 -1. - <_> - 0 14 5 1 2. - <_> - - <_> - 9 0 2 6 -1. - <_> - 9 0 1 3 2. - <_> - 10 3 1 3 2. - <_> - - <_> - 9 0 2 6 -1. - <_> - 10 0 1 3 2. - <_> - 9 3 1 3 2. - <_> - - <_> - 9 7 3 6 -1. - <_> - 10 7 1 6 3. - <_> - - <_> - 9 0 2 6 -1. - <_> - 9 0 1 3 2. - <_> - 10 3 1 3 2. - <_> - - <_> - 8 7 3 6 -1. - <_> - 9 7 1 6 3. - <_> - - <_> - 9 6 2 6 -1. - <_> - 9 6 1 6 2. - <_> - - <_> - 9 4 4 3 -1. - <_> - 9 4 2 3 2. - <_> - - <_> - 0 4 4 3 -1. - <_> - 0 5 4 1 3. - <_> - - <_> - 8 7 4 2 -1. - <_> - 8 8 4 1 2. - <_> - - <_> - 10 6 6 3 -1. - <_> - 12 6 2 3 3. - <_> - - <_> - 9 6 3 12 -1. - <_> - 9 10 3 4 3. - <_> - - <_> - 5 4 2 3 -1. - <_> - 5 5 2 1 3. - <_> - - <_> - 5 6 1 3 -1. - <_> - 5 7 1 1 3. - <_> - - <_> - 9 17 3 2 -1. - <_> - 10 17 1 2 3. - <_> - - <_> - 0 7 20 2 -1. - <_> - 0 8 20 1 2. - <_> - - <_> - 4 3 6 7 -1. - <_> - 6 3 2 7 3. - <_> - - <_> - 5 10 6 10 -1. - <_> - 5 10 3 5 2. - <_> - 8 15 3 5 2. - <_> - - <_> - 9 17 3 2 -1. - <_> - 10 17 1 2 3. - <_> - - <_> - 9 10 2 2 -1. - <_> - 9 11 2 1 2. - <_> - - <_> - 8 17 3 2 -1. - <_> - 9 17 1 2 3. - <_> - - <_> - 5 6 1 3 -1. - <_> - 5 7 1 1 3. - <_> - - <_> - 0 1 20 2 -1. - <_> - 10 1 10 1 2. - <_> - 0 2 10 1 2. - <_> - - <_> - 14 2 6 9 -1. - <_> - 14 5 6 3 3. - <_> - - <_> - 5 3 3 2 -1. - <_> - 5 4 3 1 2. - <_> - - <_> - 5 4 4 2 -1. - <_> - 7 4 2 2 2. - <_> - - <_> - 14 2 6 9 -1. - <_> - 14 5 6 3 3. - <_> - - <_> - 0 12 20 6 -1. - <_> - 0 14 20 2 3. - <_> - - <_> - 2 2 16 4 -1. - <_> - 2 2 8 2 2. - <_> - 10 4 8 2 2. - <_> - - <_> - 7 12 5 3 -1. - <_> - 7 13 5 1 3. - <_> - - <_> - 14 9 6 10 -1. - <_> - 14 9 3 10 2. - <_> - - <_> - 16 6 3 2 -1. - <_> - 16 7 3 1 2. - <_> - - <_> - 0 9 6 10 -1. - <_> - 3 9 3 10 2. - <_> - - <_> - 0 16 5 2 -1. - <_> - 0 17 5 1 2. - <_> - - <_> - 9 12 2 3 -1. - <_> - 9 13 2 1 3. - <_> - - <_> - 9 7 2 12 -1. - <_> - 9 11 2 4 3. - <_> - - <_> - 3 2 6 2 -1. - <_> - 5 2 2 2 3. - <_> - - <_> - 4 1 1 2 -1. - <_> - 4 2 1 1 2. - <_> - - <_> - 11 15 1 2 -1. - <_> - 11 16 1 1 2. - <_> - - <_> - 3 1 16 2 -1. - <_> - 11 1 8 1 2. - <_> - 3 2 8 1 2. - <_> - - <_> - 3 6 2 2 -1. - <_> - 3 6 1 1 2. - <_> - 4 7 1 1 2. - <_> - - <_> - 5 11 10 6 -1. - <_> - 5 11 5 3 2. - <_> - 10 14 5 3 2. - <_> - - <_> - 10 11 4 6 -1. - <_> - 10 14 4 3 2. - <_> - - <_> - 14 9 6 11 -1. - <_> - 16 9 2 11 3. - <_> - - <_> - 0 9 6 11 -1. - <_> - 2 9 2 11 3. - <_> - - <_> - 2 11 16 6 -1. - <_> - 2 11 8 3 2. - <_> - 10 14 8 3 2. - <_> - - <_> - 12 0 8 10 -1. - <_> - 16 0 4 5 2. - <_> - 12 5 4 5 2. - <_> - - <_> - 14 2 6 4 -1. - <_> - 16 2 2 4 3. - <_> - - <_> - 0 0 8 10 -1. - <_> - 0 0 4 5 2. - <_> - 4 5 4 5 2. - <_> - - <_> - 0 2 6 4 -1. - <_> - 2 2 2 4 3. - <_> - - <_> - 4 9 15 2 -1. - <_> - 9 9 5 2 3. - <_> - - <_> - 12 3 4 8 -1. - <_> - 14 3 2 4 2. - <_> - 12 7 2 4 2. - <_> - - <_> - 9 2 2 9 -1. - <_> - 10 2 1 9 2. - <_> - - <_> - 0 2 20 1 -1. - <_> - 10 2 10 1 2. - <_> - - <_> - 16 1 4 5 -1. - <_> - 16 1 2 5 2. - <_> - - <_> - 16 0 4 6 -1. - <_> - 16 3 4 3 2. - <_> - - <_> - 4 3 6 4 -1. - <_> - 6 3 2 4 3. - <_> - - <_> - 0 0 18 5 -1. - <_> - 6 0 6 5 3. - <_> - - <_> - 6 2 12 14 -1. - <_> - 12 2 6 7 2. - <_> - 6 9 6 7 2. - <_> - - <_> - 11 8 3 5 -1. - <_> - 12 8 1 5 3. - <_> - - <_> - 5 12 2 2 -1. - <_> - 5 13 2 1 2. - <_> - - <_> - 5 10 4 3 -1. - <_> - 7 10 2 3 2. - <_> - - <_> - 4 9 15 2 -1. - <_> - 9 9 5 2 3. - <_> - - <_> - 10 7 6 2 -1. - <_> - 12 7 2 2 3. - <_> - - <_> - 1 9 15 2 -1. - <_> - 6 9 5 2 3. - <_> - - <_> - 5 0 2 10 -1. - <_> - 5 0 1 5 2. - <_> - 6 5 1 5 2. - <_> - - <_> - 0 0 20 14 -1. - <_> - 0 7 20 7 2. - <_> - - <_> - 12 7 8 4 -1. - <_> - 12 7 4 4 2. - <_> - - <_> - 0 7 8 4 -1. - <_> - 4 7 4 4 2. - <_> - - <_> - 8 1 3 3 -1. - <_> - 9 1 1 3 3. - <_> - - <_> - 9 7 3 4 -1. - <_> - 10 7 1 4 3. - <_> - - <_> - 9 9 3 1 -1. - <_> - 10 9 1 1 3. - <_> - - <_> - 8 9 3 2 -1. - <_> - 8 10 3 1 2. - <_> - - <_> - 8 4 2 8 -1. - <_> - 8 4 1 4 2. - <_> - 9 8 1 4 2. - <_> - - <_> - 5 8 12 3 -1. - <_> - 5 9 12 1 3. - <_> - - <_> - 11 14 1 3 -1. - <_> - 11 15 1 1 3. - <_> - - <_> - 6 10 3 6 -1. - <_> - 6 12 3 2 3. - <_> - - <_> - 4 17 8 3 -1. - <_> - 4 18 8 1 3. - <_> - - <_> - 17 6 2 3 -1. - <_> - 17 7 2 1 3. - <_> - - <_> - 9 12 2 2 -1. - <_> - 10 12 1 1 2. - <_> - 9 13 1 1 2. - <_> - - <_> - 9 13 2 4 -1. - <_> - 9 13 1 2 2. - <_> - 10 15 1 2 2. - <_> - - <_> - 9 11 2 3 -1. - <_> - 9 12 2 1 3. - <_> - - <_> - 5 5 12 10 -1. - <_> - 11 5 6 5 2. - <_> - 5 10 6 5 2. - <_> - - <_> - 6 3 12 12 -1. - <_> - 12 3 6 6 2. - <_> - 6 9 6 6 2. - <_> - - <_> - 5 7 2 2 -1. - <_> - 5 7 1 1 2. - <_> - 6 8 1 1 2. - <_> - - <_> - 4 3 3 2 -1. - <_> - 5 3 1 2 3. - <_> - - <_> - 6 2 12 14 -1. - <_> - 12 2 6 7 2. - <_> - 6 9 6 7 2. - <_> - - <_> - 5 2 12 3 -1. - <_> - 9 2 4 3 3. - <_> - - <_> - 1 1 18 17 -1. - <_> - 7 1 6 17 3. - <_> - - <_> - 0 9 10 1 -1. - <_> - 5 9 5 1 2. - <_> - - <_> - 16 8 4 3 -1. - <_> - 16 9 4 1 3. - <_> - - <_> - 7 13 6 6 -1. - <_> - 7 16 6 3 2. - <_> - - <_> - 6 14 1 6 -1. - <_> - 6 16 1 2 3. - <_> - - <_> - 6 17 4 2 -1. - <_> - 6 18 4 1 2. - <_> - - <_> - 10 18 6 2 -1. - <_> - 13 18 3 1 2. - <_> - 10 19 3 1 2. - <_> - - <_> - 16 8 1 3 -1. - <_> - 16 9 1 1 3. - <_> - - <_> - 8 13 4 3 -1. - <_> - 8 14 4 1 3. - <_> - - <_> - 9 15 1 2 -1. - <_> - 9 16 1 1 2. - <_> - - <_> - 13 0 3 12 -1. - <_> - 14 0 1 12 3. - <_> - - <_> - 15 11 1 3 -1. - <_> - 15 12 1 1 3. - <_> - - <_> - 8 15 3 3 -1. - <_> - 8 16 3 1 3. - <_> - - <_> - 4 0 3 12 -1. - <_> - 5 0 1 12 3. - <_> - - <_> - 9 7 3 3 -1. - <_> - 10 7 1 3 3. - <_> - - <_> - 9 9 3 1 -1. - <_> - 10 9 1 1 3. - <_> - - <_> - 2 2 12 14 -1. - <_> - 2 2 6 7 2. - <_> - 8 9 6 7 2. - <_> - - <_> - 4 2 12 3 -1. - <_> - 8 2 4 3 3. - <_> - - <_> - 18 18 2 2 -1. - <_> - 18 18 1 2 2. - <_> - - <_> - 17 2 3 8 -1. - <_> - 18 2 1 8 3. - <_> - - <_> - 0 18 2 2 -1. - <_> - 1 18 1 2 2. - <_> - - <_> - 6 11 2 6 -1. - <_> - 6 14 2 3 2. - <_> - - <_> - 13 10 5 6 -1. - <_> - 13 12 5 2 3. - <_> - - <_> - 5 8 15 3 -1. - <_> - 5 9 15 1 3. - <_> - - <_> - 2 10 5 6 -1. - <_> - 2 12 5 2 3. - <_> - - <_> - 0 8 15 3 -1. - <_> - 0 9 15 1 3. - <_> - - <_> - 16 2 3 1 -1. - <_> - 17 2 1 1 3. - <_> - - <_> - 17 4 3 2 -1. - <_> - 18 4 1 2 3. - <_> - - <_> - 0 8 8 12 -1. - <_> - 0 8 4 6 2. - <_> - 4 14 4 6 2. - <_> - - <_> - 1 7 8 6 -1. - <_> - 1 7 4 3 2. - <_> - 5 10 4 3 2. - <_> - - <_> - 14 1 6 2 -1. - <_> - 16 1 2 2 3. - <_> - - <_> - 15 0 4 4 -1. - <_> - 17 0 2 2 2. - <_> - 15 2 2 2 2. - <_> - - <_> - 1 1 4 11 -1. - <_> - 3 1 2 11 2. - <_> - - <_> - 5 5 1 8 -1. - <_> - 5 9 1 4 2. - <_> - - <_> - 7 7 6 1 -1. - <_> - 9 7 2 1 3. - <_> - - <_> - 4 7 12 2 -1. - <_> - 8 7 4 2 3. - <_> - - <_> - 8 4 4 4 -1. - <_> - 8 6 4 2 2. - <_> - - <_> - 2 4 9 1 -1. - <_> - 5 4 3 1 3. - <_> - - <_> - 9 12 2 8 -1. - <_> - 9 16 2 4 2. - <_> - - <_> - 3 8 14 12 -1. - <_> - 3 14 14 6 2. - <_> - - <_> - 6 13 7 3 -1. - <_> - 6 14 7 1 3. - <_> - - <_> - 5 9 6 3 -1. - <_> - 7 9 2 3 3. - <_> - - <_> - 12 1 6 3 -1. - <_> - 12 2 6 1 3. - <_> - - <_> - 8 12 6 2 -1. - <_> - 8 13 6 1 2. - <_> - - <_> - 0 2 18 2 -1. - <_> - 0 2 9 1 2. - <_> - 9 3 9 1 2. - <_> - - <_> - 6 10 3 6 -1. - <_> - 6 13 3 3 2. - <_> - - <_> - 14 0 6 6 -1. - <_> - 14 0 3 6 2. - <_> - - <_> - 15 0 5 8 -1. - <_> - 15 4 5 4 2. - <_> - - <_> - 7 16 6 4 -1. - <_> - 9 16 2 4 3. - <_> - - <_> - 2 11 14 4 -1. - <_> - 2 11 7 2 2. - <_> - 9 13 7 2 2. - <_> - - <_> - 14 10 6 10 -1. - <_> - 14 10 3 10 2. - <_> - - <_> - 9 8 10 12 -1. - <_> - 14 8 5 6 2. - <_> - 9 14 5 6 2. - <_> - - <_> - 0 10 6 10 -1. - <_> - 3 10 3 10 2. - <_> - - <_> - 1 8 10 12 -1. - <_> - 1 8 5 6 2. - <_> - 6 14 5 6 2. - <_> - - <_> - 9 3 6 1 -1. - <_> - 11 3 2 1 3. - <_> - - <_> - 7 4 6 3 -1. - <_> - 9 4 2 3 3. - <_> - - <_> - 5 3 6 1 -1. - <_> - 7 3 2 1 3. - <_> - - <_> - 4 5 6 3 -1. - <_> - 6 5 2 3 3. - <_> - - <_> - 9 16 3 3 -1. - <_> - 9 17 3 1 3. - <_> - - <_> - 8 14 6 3 -1. - <_> - 8 15 6 1 3. - <_> - - <_> - 6 0 8 12 -1. - <_> - 6 0 4 6 2. - <_> - 10 6 4 6 2. - <_> - - <_> - 4 12 2 3 -1. - <_> - 4 13 2 1 3. - <_> - - <_> - 12 16 6 3 -1. - <_> - 12 17 6 1 3. - <_> - - <_> - 7 12 7 2 -1. - <_> - 7 13 7 1 2. - <_> - - <_> - 2 16 6 3 -1. - <_> - 2 17 6 1 3. - <_> - - <_> - 0 7 16 6 -1. - <_> - 0 10 16 3 2. - <_> - - <_> - 9 7 3 3 -1. - <_> - 10 7 1 3 3. - <_> - - <_> - 9 7 3 5 -1. - <_> - 10 7 1 5 3. - <_> - - <_> - 0 5 20 10 -1. - <_> - 0 5 10 5 2. - <_> - 10 10 10 5 2. - <_> - - <_> - 3 1 4 2 -1. - <_> - 5 1 2 2 2. - <_> - - <_> - 7 6 8 10 -1. - <_> - 11 6 4 5 2. - <_> - 7 11 4 5 2. - <_> - - <_> - 17 6 3 2 -1. - <_> - 17 7 3 1 2. - <_> - - <_> - 5 6 8 10 -1. - <_> - 5 6 4 5 2. - <_> - 9 11 4 5 2. - <_> - - <_> - 5 12 10 6 -1. - <_> - 5 14 10 2 3. - <_> - - <_> - 9 7 3 3 -1. - <_> - 10 7 1 3 3. - <_> - - <_> - 10 3 2 6 -1. - <_> - 11 3 1 3 2. - <_> - 10 6 1 3 2. - <_> - - <_> - 0 4 3 3 -1. - <_> - 0 5 3 1 3. - <_> - - <_> - 3 16 8 4 -1. - <_> - 3 16 4 2 2. - <_> - 7 18 4 2 2. - <_> - - <_> - 8 13 5 2 -1. - <_> - 8 14 5 1 2. - <_> - - <_> - 8 7 4 12 -1. - <_> - 8 11 4 4 3. - <_> - - <_> - 5 9 2 2 -1. - <_> - 6 9 1 2 2. - <_> - - <_> - 9 15 2 3 -1. - <_> - 9 16 2 1 3. - <_> - - <_> - 13 9 2 3 -1. - <_> - 13 9 1 3 2. - <_> - - <_> - 14 0 6 17 -1. - <_> - 16 0 2 17 3. - <_> - - <_> - 5 10 2 2 -1. - <_> - 6 10 1 2 2. - <_> - - <_> - 2 9 9 1 -1. - <_> - 5 9 3 1 3. - <_> - - <_> - 9 11 2 3 -1. - <_> - 9 12 2 1 3. - <_> - - <_> - 7 11 6 3 -1. - <_> - 7 12 6 1 3. - <_> - - <_> - 0 6 3 2 -1. - <_> - 0 7 3 1 2. - <_> - - <_> - 7 0 6 1 -1. - <_> - 9 0 2 1 3. - <_> - - <_> - 9 16 3 3 -1. - <_> - 9 17 3 1 3. - <_> - - <_> - 2 13 17 6 -1. - <_> - 2 16 17 3 2. - <_> - - <_> - 1 3 3 7 -1. - <_> - 2 3 1 7 3. - <_> - - <_> - 1 1 6 4 -1. - <_> - 3 1 2 4 3. - <_> - - <_> - 14 1 6 5 -1. - <_> - 14 1 3 5 2. - <_> - - <_> - 13 2 3 2 -1. - <_> - 13 3 3 1 2. - <_> - - <_> - 0 1 6 5 -1. - <_> - 3 1 3 5 2. - <_> - - <_> - 2 3 2 6 -1. - <_> - 2 5 2 2 3. - <_> - - <_> - 9 10 3 2 -1. - <_> - 9 11 3 1 2. - <_> - - <_> - 8 13 4 3 -1. - <_> - 8 14 4 1 3. - <_> - - <_> - 6 3 3 1 -1. - <_> - 7 3 1 1 3. - <_> - - <_> - 8 2 3 12 -1. - <_> - 8 6 3 4 3. - <_> - - <_> - 11 12 1 2 -1. - <_> - 11 13 1 1 2. - <_> - - <_> - 11 12 2 2 -1. - <_> - 12 12 1 1 2. - <_> - 11 13 1 1 2. - <_> - - <_> - 5 5 2 2 -1. - <_> - 5 6 2 1 2. - <_> - - <_> - 5 4 1 3 -1. - <_> - 5 5 1 1 3. - <_> - - <_> - 3 11 16 4 -1. - <_> - 11 11 8 2 2. - <_> - 3 13 8 2 2. - <_> - - <_> - 0 10 20 3 -1. - <_> - 0 11 20 1 3. - <_> - - <_> - 1 11 16 4 -1. - <_> - 1 11 8 2 2. - <_> - 9 13 8 2 2. - <_> - - <_> - 4 2 4 2 -1. - <_> - 4 3 4 1 2. - <_> - - <_> - 12 6 2 2 -1. - <_> - 13 6 1 1 2. - <_> - 12 7 1 1 2. - <_> - - <_> - 12 11 6 6 -1. - <_> - 12 13 6 2 3. - <_> - - <_> - 6 6 2 2 -1. - <_> - 6 6 1 1 2. - <_> - 7 7 1 1 2. - <_> - - <_> - 6 4 4 16 -1. - <_> - 8 4 2 16 2. - <_> - - <_> - 11 18 3 2 -1. - <_> - 11 19 3 1 2. - <_> - - <_> - 9 17 6 2 -1. - <_> - 12 17 3 1 2. - <_> - 9 18 3 1 2. - <_> - - <_> - 2 13 5 2 -1. - <_> - 2 14 5 1 2. - <_> - - <_> - 3 15 2 2 -1. - <_> - 3 16 2 1 2. - <_> - - <_> - 9 7 3 3 -1. - <_> - 10 7 1 3 3. - <_> - - <_> - 9 6 2 6 -1. - <_> - 9 6 1 6 2. - <_> - - <_> - 1 14 7 6 -1. - <_> - 1 16 7 2 3. - <_> - - <_> - 8 1 2 11 -1. - <_> - 9 1 1 11 2. - <_> - - <_> - 9 7 2 4 -1. - <_> - 9 7 1 4 2. - <_> - - <_> - 11 10 2 1 -1. - <_> - 11 10 1 1 2. - <_> - - <_> - 0 3 3 9 -1. - <_> - 1 3 1 9 3. - <_> - - <_> - 0 3 3 6 -1. - <_> - 0 5 3 2 3. - <_> - - <_> - 11 15 2 2 -1. - <_> - 12 15 1 1 2. - <_> - 11 16 1 1 2. - <_> - - <_> - 11 14 2 2 -1. - <_> - 12 14 1 1 2. - <_> - 11 15 1 1 2. - <_> - - <_> - 7 15 2 2 -1. - <_> - 7 15 1 1 2. - <_> - 8 16 1 1 2. - <_> - - <_> - 7 14 2 2 -1. - <_> - 7 14 1 1 2. - <_> - 8 15 1 1 2. - <_> - - <_> - 8 13 4 6 -1. - <_> - 10 13 2 3 2. - <_> - 8 16 2 3 2. - <_> - - <_> - 2 14 16 4 -1. - <_> - 10 14 8 2 2. - <_> - 2 16 8 2 2. - <_> - - <_> - 9 8 2 2 -1. - <_> - 9 9 2 1 2. - <_> - - <_> - 7 7 5 3 -1. - <_> - 7 8 5 1 3. - <_> - - <_> - 7 5 6 2 -1. - <_> - 9 5 2 2 3. - <_> - - <_> - 9 1 6 18 -1. - <_> - 11 1 2 18 3. - <_> - - <_> - 8 6 3 4 -1. - <_> - 9 6 1 4 3. - <_> - - <_> - 8 5 2 4 -1. - <_> - 8 5 1 2 2. - <_> - 9 7 1 2 2. - <_> - - <_> - 9 13 2 6 -1. - <_> - 10 13 1 3 2. - <_> - 9 16 1 3 2. - <_> - - <_> - 11 0 3 18 -1. - <_> - 12 0 1 18 3. - <_> - - <_> - 6 0 3 18 -1. - <_> - 7 0 1 18 3. - <_> - - <_> - 5 15 4 2 -1. - <_> - 7 15 2 2 2. - <_> - - <_> - 1 9 18 1 -1. - <_> - 7 9 6 1 3. - <_> - - <_> - 0 0 20 3 -1. - <_> - 0 1 20 1 3. - <_> - - <_> - 9 6 2 4 -1. - <_> - 10 6 1 4 2. - <_> - - <_> - 6 10 6 2 -1. - <_> - 8 10 2 2 3. - <_> - - <_> - 0 7 20 1 -1. - <_> - 0 7 10 1 2. - <_> - - <_> - 11 3 5 4 -1. - <_> - 11 5 5 2 2. - <_> - - <_> - 5 7 10 1 -1. - <_> - 10 7 5 1 2. - <_> - - <_> - 8 10 3 3 -1. - <_> - 8 11 3 1 3. - <_> - - <_> - 2 0 16 8 -1. - <_> - 10 0 8 4 2. - <_> - 2 4 8 4 2. - <_> - - <_> - 11 0 9 10 -1. - <_> - 11 5 9 5 2. - <_> - - <_> - 0 2 8 18 -1. - <_> - 4 2 4 18 2. - <_> - - <_> - 0 0 2 6 -1. - <_> - 0 2 2 2 3. - <_> - - <_> - 6 0 9 2 -1. - <_> - 6 1 9 1 2. - <_> - - <_> - 4 1 12 2 -1. - <_> - 4 2 12 1 2. - <_> - - <_> - 2 1 16 14 -1. - <_> - 2 8 16 7 2. - <_> - - <_> - 5 1 8 12 -1. - <_> - 5 7 8 6 2. - <_> - - <_> - 9 11 2 2 -1. - <_> - 9 12 2 1 2. - <_> - - <_> - 9 10 5 6 -1. - <_> - 9 12 5 2 3. - <_> - - <_> - 3 0 13 8 -1. - <_> - 3 4 13 4 2. - <_> - - <_> - 6 7 5 8 -1. - <_> - 6 11 5 4 2. - <_> - - <_> - 9 5 2 3 -1. - <_> - 9 6 2 1 3. - <_> - - <_> - 6 8 8 3 -1. - <_> - 6 9 8 1 3. - <_> - - <_> - 2 2 7 6 -1. - <_> - 2 5 7 3 2. - <_> - - <_> - 2 1 14 4 -1. - <_> - 2 1 7 2 2. - <_> - 9 3 7 2 2. - <_> - - <_> - 11 14 1 3 -1. - <_> - 11 15 1 1 3. - <_> - - <_> - 6 15 8 2 -1. - <_> - 6 16 8 1 2. - <_> - - <_> - 8 14 1 3 -1. - <_> - 8 15 1 1 3. - <_> - - <_> - 8 11 2 8 -1. - <_> - 8 15 2 4 2. - <_> - - <_> - 6 15 8 2 -1. - <_> - 6 16 8 1 2. - <_> - - <_> - 7 16 8 3 -1. - <_> - 7 17 8 1 3. - <_> - - <_> - 0 16 2 2 -1. - <_> - 0 17 2 1 2. - <_> - - <_> - 1 16 8 4 -1. - <_> - 1 16 4 2 2. - <_> - 5 18 4 2 2. - <_> - - <_> - 2 9 16 3 -1. - <_> - 2 10 16 1 3. - <_> - - <_> - 13 11 2 4 -1. - <_> - 13 11 1 4 2. - <_> - - <_> - 0 13 16 6 -1. - <_> - 0 15 16 2 3. - <_> - - <_> - 5 11 2 4 -1. - <_> - 6 11 1 4 2. - <_> - - <_> - 18 2 2 18 -1. - <_> - 19 2 1 9 2. - <_> - 18 11 1 9 2. - <_> - - <_> - 19 7 1 9 -1. - <_> - 19 10 1 3 3. - <_> - - <_> - 0 2 2 18 -1. - <_> - 0 2 1 9 2. - <_> - 1 11 1 9 2. - <_> - - <_> - 0 7 1 9 -1. - <_> - 0 10 1 3 3. - <_> - - <_> - 14 12 2 2 -1. - <_> - 14 13 2 1 2. - <_> - - <_> - 11 14 2 3 -1. - <_> - 11 15 2 1 3. - <_> - - <_> - 7 8 6 2 -1. - <_> - 7 9 6 1 2. - <_> - - <_> - 7 12 4 6 -1. - <_> - 7 12 2 3 2. - <_> - 9 15 2 3 2. - <_> - - <_> - 8 13 5 3 -1. - <_> - 8 14 5 1 3. - <_> - - <_> - 12 14 2 2 -1. - <_> - 13 14 1 1 2. - <_> - 12 15 1 1 2. - <_> - - <_> - 7 13 6 3 -1. - <_> - 7 14 6 1 3. - <_> - - <_> - 7 13 5 2 -1. - <_> - 7 14 5 1 2. - <_> - - <_> - 2 10 16 4 -1. - <_> - 10 10 8 2 2. - <_> - 2 12 8 2 2. - <_> - - <_> - 7 0 6 6 -1. - <_> - 9 0 2 6 3. - <_> - - <_> - 7 1 6 3 -1. - <_> - 7 2 6 1 3. - <_> - - <_> - 0 12 6 2 -1. - <_> - 0 13 6 1 2. - <_> - - <_> - 6 3 11 2 -1. - <_> - 6 4 11 1 2. - <_> - - <_> - 12 0 8 6 -1. - <_> - 16 0 4 3 2. - <_> - 12 3 4 3 2. - <_> - - <_> - 8 12 1 2 -1. - <_> - 8 13 1 1 2. - <_> - - <_> - 8 8 1 12 -1. - <_> - 8 12 1 4 3. - <_> - - <_> - 11 11 2 2 -1. - <_> - 12 11 1 1 2. - <_> - 11 12 1 1 2. - <_> - - <_> - 12 7 3 13 -1. - <_> - 13 7 1 13 3. - <_> - - <_> - 7 11 2 2 -1. - <_> - 7 11 1 1 2. - <_> - 8 12 1 1 2. - <_> - - <_> - 3 13 1 3 -1. - <_> - 3 14 1 1 3. - <_> - - <_> - 10 18 3 2 -1. - <_> - 11 18 1 2 3. - <_> - - <_> - 11 11 2 1 -1. - <_> - 11 11 1 1 2. - <_> - - <_> - 1 10 5 9 -1. - <_> - 1 13 5 3 3. - <_> - - <_> - 4 8 6 4 -1. - <_> - 6 8 2 4 3. - <_> - - <_> - 13 12 1 4 -1. - <_> - 13 14 1 2 2. - <_> - - <_> - 11 3 4 14 -1. - <_> - 13 3 2 7 2. - <_> - 11 10 2 7 2. - <_> - - <_> - 6 12 1 4 -1. - <_> - 6 14 1 2 2. - <_> - - <_> - 5 3 4 14 -1. - <_> - 5 3 2 7 2. - <_> - 7 10 2 7 2. - <_> - - <_> - 10 18 3 2 -1. - <_> - 11 18 1 2 3. - <_> - - <_> - 9 12 3 3 -1. - <_> - 9 13 3 1 3. - <_> - - <_> - 2 2 12 6 -1. - <_> - 2 2 6 3 2. - <_> - 8 5 6 3 2. - <_> - - <_> - 6 6 6 2 -1. - <_> - 9 6 3 2 2. - <_> - - <_> - 1 0 18 12 -1. - <_> - 7 0 6 12 3. - <_> - - <_> - 5 7 6 4 -1. - <_> - 5 7 3 2 2. - <_> - 8 9 3 2 2. - <_> - - <_> - 5 7 10 4 -1. - <_> - 5 9 10 2 2. - <_> - - <_> - 7 7 6 4 -1. - <_> - 9 7 2 4 3. - <_> - - <_> - 9 5 2 2 -1. - <_> - 9 6 2 1 2. - <_> - - <_> - 9 9 2 2 -1. - <_> - 9 10 2 1 2. - <_> - - <_> - 6 17 8 3 -1. - <_> - 6 18 8 1 3. - <_> - - <_> - 9 17 6 2 -1. - <_> - 12 17 3 1 2. - <_> - 9 18 3 1 2. - <_> - - <_> - 4 12 2 2 -1. - <_> - 4 13 2 1 2. - <_> - - <_> - 3 12 9 2 -1. - <_> - 3 13 9 1 2. - <_> - - <_> - 8 3 6 1 -1. - <_> - 10 3 2 1 3. - <_> - - <_> - 9 3 4 6 -1. - <_> - 11 3 2 3 2. - <_> - 9 6 2 3 2. - <_> - - <_> - 0 3 6 5 -1. - <_> - 3 3 3 5 2. - <_> - - <_> - 2 0 2 18 -1. - <_> - 2 6 2 6 3. - <_> - - <_> - 14 2 4 9 -1. - <_> - 14 5 4 3 3. - <_> - - <_> - 10 18 3 2 -1. - <_> - 11 18 1 2 3. - <_> - - <_> - 2 2 4 9 -1. - <_> - 2 5 4 3 3. - <_> - - <_> - 7 18 3 2 -1. - <_> - 8 18 1 2 3. - <_> - - <_> - 10 14 3 3 -1. - <_> - 10 15 3 1 3. - <_> - - <_> - 10 12 2 6 -1. - <_> - 10 15 2 3 2. - <_> - - <_> - 7 5 3 6 -1. - <_> - 7 7 3 2 3. - <_> - - <_> - 3 3 6 2 -1. - <_> - 3 4 6 1 2. - <_> - - <_> - 8 4 7 3 -1. - <_> - 8 5 7 1 3. - <_> - - <_> - 13 6 2 3 -1. - <_> - 13 7 2 1 3. - <_> - - <_> - 8 8 2 12 -1. - <_> - 8 12 2 4 3. - <_> - - <_> - 5 4 8 14 -1. - <_> - 5 4 4 7 2. - <_> - 9 11 4 7 2. - <_> - - <_> - 0 1 20 8 -1. - <_> - 10 1 10 4 2. - <_> - 0 5 10 4 2. - <_> - - <_> - 4 0 12 2 -1. - <_> - 4 1 12 1 2. - <_> - - <_> - 0 1 20 8 -1. - <_> - 0 1 10 4 2. - <_> - 10 5 10 4 2. - <_> - - <_> - 4 0 12 2 -1. - <_> - 4 1 12 1 2. - <_> - - <_> - 9 5 6 3 -1. - <_> - 9 5 3 3 2. - <_> - - <_> - 8 13 10 6 -1. - <_> - 8 15 10 2 3. - <_> - - <_> - 5 5 6 3 -1. - <_> - 8 5 3 3 2. - <_> - - <_> - 6 3 6 1 -1. - <_> - 8 3 2 1 3. - <_> - - <_> - 11 18 9 2 -1. - <_> - 14 18 3 2 3. - <_> - - <_> - 13 11 6 7 -1. - <_> - 13 11 3 7 2. - <_> - - <_> - 4 6 12 10 -1. - <_> - 4 6 6 5 2. - <_> - 10 11 6 5 2. - <_> - - <_> - 8 17 3 3 -1. - <_> - 9 17 1 3 3. - <_> - - <_> - 11 18 9 2 -1. - <_> - 14 18 3 2 3. - <_> - - <_> - 13 11 6 8 -1. - <_> - 13 11 3 8 2. - <_> - - <_> - 4 16 2 2 -1. - <_> - 4 17 2 1 2. - <_> - - <_> - 7 15 4 4 -1. - <_> - 7 17 4 2 2. - <_> - - <_> - 12 4 3 3 -1. - <_> - 12 5 3 1 3. - <_> - - <_> - 13 6 2 3 -1. - <_> - 13 7 2 1 3. - <_> - - <_> - 5 11 6 1 -1. - <_> - 7 11 2 1 3. - <_> - - <_> - 7 10 3 1 -1. - <_> - 8 10 1 1 3. - <_> - - <_> - 0 12 20 4 -1. - <_> - 0 14 20 2 2. - <_> - - <_> - 10 2 3 2 -1. - <_> - 10 3 3 1 2. - <_> - - <_> - 5 4 3 3 -1. - <_> - 5 5 3 1 3. - <_> - - <_> - 5 5 4 3 -1. - <_> - 5 6 4 1 3. - <_> - - <_> - 8 8 4 3 -1. - <_> - 8 9 4 1 3. - <_> - - <_> - 10 4 2 12 -1. - <_> - 10 8 2 4 3. - <_> - - <_> - 0 3 4 3 -1. - <_> - 0 4 4 1 3. - <_> - - <_> - 1 3 2 3 -1. - <_> - 1 4 2 1 3. - <_> - - <_> - 16 1 4 11 -1. - <_> - 16 1 2 11 2. - <_> - - <_> - 18 2 2 16 -1. - <_> - 19 2 1 8 2. - <_> - 18 10 1 8 2. - <_> - - <_> - 1 8 6 12 -1. - <_> - 3 8 2 12 3. - <_> - - <_> - 7 2 6 2 -1. - <_> - 7 2 3 1 2. - <_> - 10 3 3 1 2. - <_> - - <_> - 12 4 8 2 -1. - <_> - 16 4 4 1 2. - <_> - 12 5 4 1 2. - <_> - - <_> - 10 6 6 2 -1. - <_> - 12 6 2 2 3. - <_> - - <_> - 0 4 8 2 -1. - <_> - 0 4 4 1 2. - <_> - 4 5 4 1 2. - <_> - - <_> - 1 3 3 5 -1. - <_> - 2 3 1 5 3. - <_> - - <_> - 16 3 4 6 -1. - <_> - 16 5 4 2 3. - <_> - - <_> - 8 6 4 3 -1. - <_> - 8 7 4 1 3. - <_> - - <_> - 8 14 1 3 -1. - <_> - 8 15 1 1 3. - <_> - - <_> - 4 11 1 2 -1. - <_> - 4 12 1 1 2. - <_> - - <_> - 8 14 6 3 -1. - <_> - 8 15 6 1 3. - <_> - - <_> - 7 15 7 3 -1. - <_> - 7 16 7 1 3. - <_> - - <_> - 9 12 2 8 -1. - <_> - 9 16 2 4 2. - <_> - - <_> - 4 6 6 2 -1. - <_> - 6 6 2 2 3. - <_> - - <_> - 12 7 4 2 -1. - <_> - 12 8 4 1 2. - <_> - - <_> - 5 3 13 10 -1. - <_> - 5 8 13 5 2. - <_> - - <_> - 4 7 4 2 -1. - <_> - 4 8 4 1 2. - <_> - - <_> - 0 8 16 2 -1. - <_> - 0 8 8 1 2. - <_> - 8 9 8 1 2. - <_> - - <_> - 11 8 2 5 -1. - <_> - 11 8 1 5 2. - <_> - - <_> - 10 0 6 13 -1. - <_> - 10 0 3 13 2. - <_> - - <_> - 1 6 4 2 -1. - <_> - 1 7 4 1 2. - <_> - - <_> - 4 3 2 1 -1. - <_> - 5 3 1 1 2. - <_> - - <_> - 11 8 2 5 -1. - <_> - 11 8 1 5 2. - <_> - - <_> - 12 10 4 8 -1. - <_> - 12 10 2 8 2. - <_> - - <_> - 7 8 2 5 -1. - <_> - 8 8 1 5 2. - <_> - - <_> - 4 10 4 8 -1. - <_> - 6 10 2 8 2. - <_> - - <_> - 6 7 9 12 -1. - <_> - 9 7 3 12 3. - <_> - - <_> - 11 13 2 3 -1. - <_> - 11 13 1 3 2. - <_> - - <_> - 7 10 6 10 -1. - <_> - 10 10 3 10 2. - <_> - - <_> - 8 11 4 8 -1. - <_> - 8 11 2 4 2. - <_> - 10 15 2 4 2. - <_> - - <_> - 16 1 4 11 -1. - <_> - 16 1 2 11 2. - <_> - - <_> - 18 2 2 4 -1. - <_> - 18 2 1 4 2. - <_> - - <_> - 5 6 6 2 -1. - <_> - 5 6 3 1 2. - <_> - 8 7 3 1 2. - <_> - - <_> - 5 4 1 3 -1. - <_> - 5 5 1 1 3. - <_> - - <_> - 11 1 4 14 -1. - <_> - 11 1 2 14 2. - <_> - - <_> - 4 2 12 3 -1. - <_> - 8 2 4 3 3. - <_> - - <_> - 5 1 4 14 -1. - <_> - 7 1 2 14 2. - <_> - - <_> - 7 3 6 2 -1. - <_> - 9 3 2 2 3. - <_> - - <_> - 2 0 18 4 -1. - <_> - 8 0 6 4 3. - <_> - - <_> - 9 5 2 10 -1. - <_> - 9 10 2 5 2. - <_> - - <_> - 8 6 3 4 -1. - <_> - 9 6 1 4 3. - <_> - - <_> - 5 5 9 11 -1. - <_> - 8 5 3 11 3. - <_> - - <_> - 10 6 3 5 -1. - <_> - 11 6 1 5 3. - <_> - - <_> - 8 9 6 5 -1. - <_> - 8 9 3 5 2. - <_> - - <_> - 7 6 3 5 -1. - <_> - 8 6 1 5 3. - <_> - - <_> - 6 10 6 3 -1. - <_> - 9 10 3 3 2. - <_> - - <_> - 10 0 3 7 -1. - <_> - 11 0 1 7 3. - <_> - - <_> - 0 3 20 12 -1. - <_> - 0 9 20 6 2. - <_> - - <_> - 9 7 2 2 -1. - <_> - 10 7 1 2 2. - <_> - - <_> - 5 9 4 1 -1. - <_> - 7 9 2 1 2. - <_> - - <_> - 13 13 3 2 -1. - <_> - 13 14 3 1 2. - <_> - - <_> - 16 9 4 6 -1. - <_> - 16 9 2 6 2. - <_> - - <_> - 7 15 6 3 -1. - <_> - 7 16 6 1 3. - <_> - - <_> - 6 16 7 3 -1. - <_> - 6 17 7 1 3. - <_> - - <_> - 11 14 9 6 -1. - <_> - 11 16 9 2 3. - <_> - - <_> - 19 14 1 3 -1. - <_> - 19 15 1 1 3. - <_> - - <_> - 0 9 6 6 -1. - <_> - 3 9 3 6 2. - <_> - - <_> - 0 19 9 1 -1. - <_> - 3 19 3 1 3. - <_> - - <_> - 11 14 9 6 -1. - <_> - 11 16 9 2 3. - <_> - - <_> - 12 12 6 6 -1. - <_> - 12 14 6 2 3. - <_> - - <_> - 1 14 8 6 -1. - <_> - 1 16 8 2 3. - <_> - - <_> - 8 1 3 2 -1. - <_> - 9 1 1 2 3. - <_> - - <_> - 18 2 2 4 -1. - <_> - 18 2 1 4 2. - <_> - - <_> - 14 0 6 3 -1. - <_> - 16 0 2 3 3. - <_> - - <_> - 0 2 2 4 -1. - <_> - 1 2 1 4 2. - <_> - - <_> - 0 0 6 3 -1. - <_> - 2 0 2 3 3. - <_> - - <_> - 9 0 3 2 -1. - <_> - 10 0 1 2 3. - <_> - - <_> - 12 1 2 2 -1. - <_> - 12 1 1 2 2. - <_> - - <_> - 8 0 3 2 -1. - <_> - 9 0 1 2 3. - <_> - - <_> - 6 1 2 2 -1. - <_> - 7 1 1 2 2. - <_> - - <_> - 10 8 2 3 -1. - <_> - 10 9 2 1 3. - <_> - - <_> - 13 15 6 2 -1. - <_> - 13 16 6 1 2. - <_> - - <_> - 8 12 2 2 -1. - <_> - 8 12 1 1 2. - <_> - 9 13 1 1 2. - <_> - - <_> - 8 15 3 5 -1. - <_> - 9 15 1 5 3. - <_> - - <_> - 8 6 4 12 -1. - <_> - 8 12 4 6 2. - <_> - - <_> - 7 6 7 8 -1. - <_> - 7 10 7 4 2. - <_> - - <_> - 0 11 8 2 -1. - <_> - 0 12 8 1 2. - <_> - - <_> - 8 11 2 2 -1. - <_> - 8 11 1 1 2. - <_> - 9 12 1 1 2. - <_> - - <_> - 7 7 12 1 -1. - <_> - 11 7 4 1 3. - <_> - - <_> - 10 8 3 2 -1. - <_> - 11 8 1 2 3. - <_> - - <_> - 1 7 12 1 -1. - <_> - 5 7 4 1 3. - <_> - - <_> - 6 5 8 2 -1. - <_> - 6 5 4 1 2. - <_> - 10 6 4 1 2. - <_> - - <_> - 9 10 3 10 -1. - <_> - 10 10 1 10 3. - <_> - - <_> - 16 0 2 4 -1. - <_> - 16 0 1 4 2. - <_> - - <_> - 8 10 3 10 -1. - <_> - 9 10 1 10 3. - <_> - - <_> - 9 10 2 3 -1. - <_> - 9 11 2 1 3. - <_> - - <_> - 8 9 4 2 -1. - <_> - 10 9 2 1 2. - <_> - 8 10 2 1 2. - <_> - - <_> - 12 14 7 6 -1. - <_> - 12 16 7 2 3. - <_> - - <_> - 6 1 3 1 -1. - <_> - 7 1 1 1 3. - <_> - - <_> - 2 0 2 4 -1. - <_> - 3 0 1 4 2. - <_> - - <_> - 11 11 2 2 -1. - <_> - 12 11 1 1 2. - <_> - 11 12 1 1 2. - <_> - - <_> - 12 12 6 6 -1. - <_> - 12 14 6 2 3. - <_> - - <_> - 1 0 6 10 -1. - <_> - 1 0 3 5 2. - <_> - 4 5 3 5 2. - <_> - - <_> - 3 0 2 9 -1. - <_> - 3 3 2 3 3. - <_> - - <_> - 14 13 3 2 -1. - <_> - 14 14 3 1 2. - <_> - - <_> - 15 2 3 2 -1. - <_> - 15 3 3 1 2. - <_> - - <_> - 2 13 5 2 -1. - <_> - 2 14 5 1 2. - <_> - - <_> - 3 4 12 10 -1. - <_> - 3 4 6 5 2. - <_> - 9 9 6 5 2. - <_> - - <_> - 5 1 14 6 -1. - <_> - 5 3 14 2 3. - <_> - - <_> - 15 3 3 2 -1. - <_> - 15 4 3 1 2. - <_> - - <_> - 7 11 2 2 -1. - <_> - 7 11 1 1 2. - <_> - 8 12 1 1 2. - <_> - - <_> - 2 14 6 6 -1. - <_> - 2 16 6 2 3. - <_> - - <_> - 6 13 8 3 -1. - <_> - 6 14 8 1 3. - <_> - - <_> - 1 19 18 1 -1. - <_> - 7 19 6 1 3. - <_> - - <_> - 8 12 1 6 -1. - <_> - 8 15 1 3 2. - <_> - - <_> - 0 0 14 15 -1. - <_> - 0 5 14 5 3. - <_> - - <_> - 3 0 16 8 -1. - <_> - 3 4 16 4 2. - <_> - - <_> - 6 1 8 12 -1. - <_> - 6 7 8 6 2. - <_> - - <_> - 5 3 3 3 -1. - <_> - 6 3 1 3 3. - <_> - - <_> - 5 1 3 4 -1. - <_> - 6 1 1 4 3. - <_> - - <_> - 15 14 4 6 -1. - <_> - 17 14 2 3 2. - <_> - 15 17 2 3 2. - <_> - - <_> - 12 11 6 8 -1. - <_> - 15 11 3 4 2. - <_> - 12 15 3 4 2. - <_> - - <_> - 8 7 2 4 -1. - <_> - 9 7 1 4 2. - <_> - - <_> - 6 11 3 1 -1. - <_> - 7 11 1 1 3. - <_> - - <_> - 12 3 2 14 -1. - <_> - 12 3 1 14 2. - <_> - - <_> - 12 11 6 2 -1. - <_> - 15 11 3 1 2. - <_> - 12 12 3 1 2. - <_> - - <_> - 0 2 5 2 -1. - <_> - 0 3 5 1 2. - <_> - - <_> - 0 0 15 1 -1. - <_> - 5 0 5 1 3. - <_> - - <_> - 12 11 6 2 -1. - <_> - 15 11 3 1 2. - <_> - 12 12 3 1 2. - <_> - - <_> - 10 5 2 2 -1. - <_> - 10 5 1 2 2. - <_> - - <_> - 9 7 2 2 -1. - <_> - 10 7 1 2 2. - <_> - - <_> - 9 0 2 10 -1. - <_> - 9 0 1 5 2. - <_> - 10 5 1 5 2. - <_> - - <_> - 18 14 2 2 -1. - <_> - 18 15 2 1 2. - <_> - - <_> - 13 11 4 9 -1. - <_> - 13 14 4 3 3. - <_> - - <_> - 8 13 2 2 -1. - <_> - 8 13 1 1 2. - <_> - 9 14 1 1 2. - <_> - - <_> - 7 8 4 3 -1. - <_> - 7 9 4 1 3. - <_> - - <_> - 8 9 4 2 -1. - <_> - 8 10 4 1 2. - <_> - - <_> - 13 12 4 2 -1. - <_> - 13 13 4 1 2. - <_> - - <_> - 6 14 2 2 -1. - <_> - 6 14 1 1 2. - <_> - 7 15 1 1 2. - <_> - - <_> - 0 14 2 2 -1. - <_> - 0 15 2 1 2. - <_> - - <_> - 7 13 6 3 -1. - <_> - 7 14 6 1 3. - <_> - - <_> - 7 9 10 6 -1. - <_> - 7 11 10 2 3. - <_> - - <_> - 2 9 12 4 -1. - <_> - 6 9 4 4 3. - <_> - - <_> - 7 9 6 11 -1. - <_> - 10 9 3 11 2. - <_> - - <_> - 9 7 2 3 -1. - <_> - 9 8 2 1 3. - <_> - - <_> - 9 14 4 3 -1. - <_> - 9 15 4 1 3. - <_> - - <_> - 2 3 3 17 -1. - <_> - 3 3 1 17 3. - <_> - - <_> - 0 11 6 3 -1. - <_> - 0 12 6 1 3. - <_> - - <_> - 4 3 11 9 -1. - <_> - 4 6 11 3 3. - <_> - - <_> - 0 2 6 11 -1. - <_> - 3 2 3 11 2. - <_> - - <_> - 13 0 4 5 -1. - <_> - 13 0 2 5 2. - <_> - - <_> - 9 7 6 4 -1. - <_> - 12 7 3 2 2. - <_> - 9 9 3 2 2. - <_> - - <_> - 5 7 8 2 -1. - <_> - 9 7 4 2 2. - <_> - - <_> - 1 8 15 1 -1. - <_> - 6 8 5 1 3. - <_> - - <_> - 4 12 12 2 -1. - <_> - 8 12 4 2 3. - <_> - - <_> - 13 0 4 10 -1. - <_> - 15 0 2 5 2. - <_> - 13 5 2 5 2. - <_> - - <_> - 9 9 2 2 -1. - <_> - 9 10 2 1 2. - <_> - - <_> - 3 9 6 2 -1. - <_> - 6 9 3 2 2. - <_> - - <_> - 8 17 4 3 -1. - <_> - 8 18 4 1 3. - <_> - - <_> - 8 3 9 2 -1. - <_> - 11 3 3 2 3. - <_> - - <_> - 3 3 9 2 -1. - <_> - 6 3 3 2 3. - <_> - - <_> - 5 0 9 14 -1. - <_> - 8 0 3 14 3. - <_> - - <_> - 7 3 7 10 -1. - <_> - 7 8 7 5 2. - <_> - - <_> - 4 8 13 3 -1. - <_> - 4 9 13 1 3. - <_> - - <_> - 3 12 14 4 -1. - <_> - 3 12 7 2 2. - <_> - 10 14 7 2 2. - <_> - - <_> - 8 12 4 2 -1. - <_> - 8 13 4 1 2. - <_> - - <_> - 6 10 9 8 -1. - <_> - 6 14 9 4 2. - <_> - - <_> - 9 12 2 8 -1. - <_> - 9 16 2 4 2. - <_> - - <_> - 8 12 3 3 -1. - <_> - 8 13 3 1 3. - <_> - - <_> - 5 5 4 10 -1. - <_> - 7 5 2 10 2. - <_> - - <_> - 14 15 3 3 -1. - <_> - 14 16 3 1 3. - <_> - - <_> - 4 6 13 3 -1. - <_> - 4 7 13 1 3. - <_> - - <_> - 3 15 3 3 -1. - <_> - 3 16 3 1 3. - <_> - - <_> - 3 9 4 2 -1. - <_> - 3 9 2 1 2. - <_> - 5 10 2 1 2. - <_> - - <_> - 0 11 20 4 -1. - <_> - 10 11 10 2 2. - <_> - 0 13 10 2 2. - <_> - - <_> - 8 15 4 3 -1. - <_> - 8 16 4 1 3. - <_> - - <_> - 0 11 20 4 -1. - <_> - 0 11 10 2 2. - <_> - 10 13 10 2 2. - <_> - - <_> - 8 15 4 3 -1. - <_> - 8 16 4 1 3. - <_> - - <_> - 10 13 1 6 -1. - <_> - 10 16 1 3 2. - <_> - - <_> - 2 1 18 2 -1. - <_> - 11 1 9 1 2. - <_> - 2 2 9 1 2. - <_> - - <_> - 8 14 3 3 -1. - <_> - 8 15 3 1 3. - <_> - - <_> - 4 1 6 1 -1. - <_> - 6 1 2 1 3. - <_> - - <_> - 11 13 1 3 -1. - <_> - 11 14 1 1 3. - <_> - - <_> - 13 5 2 12 -1. - <_> - 13 11 2 6 2. - <_> - - <_> - 1 14 18 6 -1. - <_> - 1 16 18 2 3. - <_> - - <_> - 8 13 1 3 -1. - <_> - 8 14 1 1 3. - <_> - - <_> - 7 13 6 3 -1. - <_> - 7 14 6 1 3. - <_> - - <_> - 9 10 3 2 -1. - <_> - 9 11 3 1 2. - <_> - - <_> - 5 1 3 3 -1. - <_> - 6 1 1 3 3. - <_> - - <_> - 5 5 6 5 -1. - <_> - 8 5 3 5 2. - <_> - - <_> - 7 5 6 14 -1. - <_> - 7 12 6 7 2. - <_> - - <_> - 7 16 6 2 -1. - <_> - 9 16 2 2 3. - <_> - - <_> - 0 2 2 12 -1. - <_> - 1 2 1 12 2. - <_> - - <_> - 1 0 5 3 -1. - <_> - 1 1 5 1 3. - <_> - - <_> - 12 4 3 3 -1. - <_> - 12 5 3 1 3. - <_> - - <_> - 12 6 3 3 -1. - <_> - 12 7 3 1 3. - <_> - - <_> - 5 4 3 3 -1. - <_> - 5 5 3 1 3. - <_> - - <_> - 5 6 3 3 -1. - <_> - 5 7 3 1 3. - <_> - - <_> - 8 12 4 8 -1. - <_> - 10 12 2 4 2. - <_> - 8 16 2 4 2. - <_> - - <_> - 2 17 18 2 -1. - <_> - 11 17 9 1 2. - <_> - 2 18 9 1 2. - <_> - - <_> - 9 3 2 2 -1. - <_> - 9 4 2 1 2. - <_> - - <_> - 8 5 4 6 -1. - <_> - 8 7 4 2 3. - <_> - - <_> - 9 0 8 6 -1. - <_> - 9 2 8 2 3. - <_> - - <_> - 1 0 18 4 -1. - <_> - 7 0 6 4 3. - <_> - - <_> - 0 0 4 8 -1. - <_> - 2 0 2 8 2. - <_> - - <_> - 0 4 6 9 -1. - <_> - 2 4 2 9 3. - <_> - - <_> - 1 4 18 2 -1. - <_> - 7 4 6 2 3. - <_> - - <_> - 8 16 12 4 -1. - <_> - 14 16 6 2 2. - <_> - 8 18 6 2 2. - <_> - - <_> - 0 0 18 2 -1. - <_> - 0 0 9 1 2. - <_> - 9 1 9 1 2. - <_> - - <_> - 3 0 3 18 -1. - <_> - 4 0 1 18 3. - <_> - - <_> - 14 9 4 7 -1. - <_> - 14 9 2 7 2. - <_> - - <_> - 15 14 2 2 -1. - <_> - 15 15 2 1 2. - <_> - - <_> - 2 9 4 7 -1. - <_> - 4 9 2 7 2. - <_> - - <_> - 3 14 2 2 -1. - <_> - 3 15 2 1 2. - <_> - - <_> - 11 0 6 6 -1. - <_> - 11 2 6 2 3. - <_> - - <_> - 14 0 2 6 -1. - <_> - 15 0 1 3 2. - <_> - 14 3 1 3 2. - <_> - - <_> - 7 11 2 2 -1. - <_> - 7 11 1 1 2. - <_> - 8 12 1 1 2. - <_> - - <_> - 7 10 2 2 -1. - <_> - 8 10 1 2 2. - <_> - - <_> - 9 14 2 6 -1. - <_> - 9 17 2 3 2. - <_> - - <_> - 12 18 4 2 -1. - <_> - 12 19 4 1 2. - <_> - - <_> - 8 17 4 3 -1. - <_> - 8 18 4 1 3. - <_> - - <_> - 2 18 8 2 -1. - <_> - 2 19 8 1 2. - <_> - - <_> - 2 9 16 3 -1. - <_> - 2 10 16 1 3. - <_> - - <_> - 9 9 2 2 -1. - <_> - 9 10 2 1 2. - <_> - - <_> - 5 14 2 4 -1. - <_> - 5 14 1 2 2. - <_> - 6 16 1 2 2. - <_> - - <_> - 8 9 4 2 -1. - <_> - 8 9 2 1 2. - <_> - 10 10 2 1 2. - <_> - - <_> - 9 5 2 5 -1. - <_> - 9 5 1 5 2. - <_> - - <_> - 9 9 3 2 -1. - <_> - 10 9 1 2 3. - <_> - - <_> - 8 9 3 2 -1. - <_> - 9 9 1 2 3. - <_> - - <_> - 8 8 3 6 -1. - <_> - 9 8 1 6 3. - <_> - - <_> - 8 12 4 8 -1. - <_> - 10 12 2 4 2. - <_> - 8 16 2 4 2. - <_> - - <_> - 2 17 16 2 -1. - <_> - 10 17 8 1 2. - <_> - 2 18 8 1 2. - <_> - - <_> - 8 12 3 8 -1. - <_> - 9 12 1 8 3. - <_> - - <_> - 3 10 1 3 -1. - <_> - 3 11 1 1 3. - <_> - - <_> - 9 14 10 6 -1. - <_> - 14 14 5 3 2. - <_> - 9 17 5 3 2. - <_> - - <_> - 14 13 3 6 -1. - <_> - 14 15 3 2 3. - <_> - - <_> - 1 19 18 1 -1. - <_> - 7 19 6 1 3. - <_> - - <_> - 2 10 15 2 -1. - <_> - 7 10 5 2 3. - <_> - - <_> - 4 17 16 3 -1. - <_> - 4 18 16 1 3. - <_> - - <_> - 8 6 4 9 -1. - <_> - 8 9 4 3 3. - <_> - - <_> - 9 16 2 4 -1. - <_> - 9 16 1 2 2. - <_> - 10 18 1 2 2. - <_> - - <_> - 5 5 10 8 -1. - <_> - 5 9 10 4 2. - <_> - - <_> - 13 1 4 2 -1. - <_> - 13 1 2 2 2. - <_> - - <_> - 14 0 3 6 -1. - <_> - 14 2 3 2 3. - <_> - - <_> - 6 7 2 2 -1. - <_> - 6 7 1 1 2. - <_> - 7 8 1 1 2. - <_> - - <_> - 7 1 6 1 -1. - <_> - 9 1 2 1 3. - <_> - - <_> - 9 11 3 3 -1. - <_> - 9 12 3 1 3. - <_> - - <_> - 12 9 3 3 -1. - <_> - 13 9 1 3 3. - <_> - - <_> - 8 11 3 3 -1. - <_> - 8 12 3 1 3. - <_> - - <_> - 5 9 3 3 -1. - <_> - 6 9 1 3 3. - <_> - - <_> - 10 11 1 3 -1. - <_> - 10 12 1 1 3. - <_> - - <_> - 7 9 6 4 -1. - <_> - 10 9 3 2 2. - <_> - 7 11 3 2 2. - <_> - - <_> - 4 7 2 2 -1. - <_> - 4 7 1 1 2. - <_> - 5 8 1 1 2. - <_> - - <_> - 5 7 3 1 -1. - <_> - 6 7 1 1 3. - <_> - - <_> - 18 3 2 3 -1. - <_> - 18 4 2 1 3. - <_> - - <_> - 13 1 4 2 -1. - <_> - 13 1 2 2 2. - <_> - - <_> - 3 1 4 2 -1. - <_> - 5 1 2 2 2. - <_> - - <_> - 3 0 5 2 -1. - <_> - 3 1 5 1 2. - <_> - - <_> - 14 7 6 4 -1. - <_> - 17 7 3 2 2. - <_> - 14 9 3 2 2. - <_> - - <_> - 4 8 16 2 -1. - <_> - 4 9 16 1 2. - <_> - - <_> - 2 11 5 6 -1. - <_> - 2 13 5 2 3. - <_> - - <_> - 5 16 2 4 -1. - <_> - 5 16 1 2 2. - <_> - 6 18 1 2 2. - <_> - - <_> - 15 6 2 12 -1. - <_> - 16 6 1 6 2. - <_> - 15 12 1 6 2. - <_> - - <_> - 13 3 6 16 -1. - <_> - 15 3 2 16 3. - <_> - - <_> - 4 5 12 12 -1. - <_> - 4 5 6 6 2. - <_> - 10 11 6 6 2. - <_> - - <_> - 5 1 10 13 -1. - <_> - 10 1 5 13 2. - <_> - - <_> - 11 5 2 2 -1. - <_> - 12 5 1 1 2. - <_> - 11 6 1 1 2. - <_> - - <_> - 13 5 1 3 -1. - <_> - 13 6 1 1 3. - <_> - - <_> - 7 4 2 4 -1. - <_> - 7 4 1 2 2. - <_> - 8 6 1 2 2. - <_> - - <_> - 7 5 6 4 -1. - <_> - 10 5 3 4 2. - <_> - - <_> - 12 4 4 6 -1. - <_> - 14 4 2 3 2. - <_> - 12 7 2 3 2. - <_> - - <_> - 12 11 7 6 -1. - <_> - 12 13 7 2 3. - <_> - - <_> - 5 6 6 6 -1. - <_> - 7 6 2 6 3. - <_> - - <_> - 9 8 2 2 -1. - <_> - 9 9 2 1 2. - <_> - - <_> - 15 6 2 2 -1. - <_> - 16 6 1 1 2. - <_> - 15 7 1 1 2. - <_> - - <_> - 14 7 4 4 -1. - <_> - 16 7 2 2 2. - <_> - 14 9 2 2 2. - <_> - - <_> - 5 5 6 2 -1. - <_> - 7 5 2 2 3. - <_> - - <_> - 1 19 18 1 -1. - <_> - 7 19 6 1 3. - <_> - - <_> - 12 3 3 3 -1. - <_> - 12 4 3 1 3. - <_> - - <_> - 16 0 2 3 -1. - <_> - 16 1 2 1 3. - <_> - - <_> - 5 3 3 3 -1. - <_> - 5 4 3 1 3. - <_> - - <_> - 2 0 2 3 -1. - <_> - 2 1 2 1 3. - <_> - - <_> - 15 6 2 2 -1. - <_> - 16 6 1 1 2. - <_> - 15 7 1 1 2. - <_> - - <_> - 10 13 1 6 -1. - <_> - 10 16 1 3 2. - <_> - - <_> - 0 7 10 2 -1. - <_> - 0 7 5 1 2. - <_> - 5 8 5 1 2. - <_> - - <_> - 3 10 6 2 -1. - <_> - 3 11 6 1 2. - <_> - - <_> - 12 18 4 2 -1. - <_> - 12 19 4 1 2. - <_> - - <_> - 12 18 2 2 -1. - <_> - 13 18 1 1 2. - <_> - 12 19 1 1 2. - <_> - - <_> - 6 19 2 1 -1. - <_> - 7 19 1 1 2. - <_> - - <_> - 0 4 2 16 -1. - <_> - 0 4 1 8 2. - <_> - 1 12 1 8 2. - <_> - - <_> - 16 1 4 9 -1. - <_> - 16 4 4 3 3. - <_> - - <_> - 10 2 1 2 -1. - <_> - 10 3 1 1 2. - <_> - - <_> - 4 14 4 6 -1. - <_> - 4 14 2 3 2. - <_> - 6 17 2 3 2. - <_> - - <_> - 4 15 1 4 -1. - <_> - 4 17 1 2 2. - <_> - - <_> - 0 2 20 4 -1. - <_> - 10 2 10 2 2. - <_> - 0 4 10 2 2. - <_> - - <_> - 14 5 2 8 -1. - <_> - 14 9 2 4 2. - <_> - - <_> - 5 12 4 5 -1. - <_> - 7 12 2 5 2. - <_> - - <_> - 0 13 9 6 -1. - <_> - 0 15 9 2 3. - <_> - - <_> - 9 14 11 3 -1. - <_> - 9 15 11 1 3. - <_> - - <_> - 7 14 7 3 -1. - <_> - 7 15 7 1 3. - <_> - - <_> - 3 6 2 2 -1. - <_> - 3 6 1 1 2. - <_> - 4 7 1 1 2. - <_> - - <_> - 6 7 2 7 -1. - <_> - 7 7 1 7 2. - <_> - - <_> - 14 5 1 3 -1. - <_> - 14 6 1 1 3. - <_> - - <_> - 13 4 4 3 -1. - <_> - 13 5 4 1 3. - <_> - - <_> - 2 7 4 4 -1. - <_> - 2 7 2 2 2. - <_> - 4 9 2 2 2. - <_> - - <_> - 2 9 13 6 -1. - <_> - 2 12 13 3 2. - <_> - - <_> - 10 1 3 4 -1. - <_> - 11 1 1 4 3. - <_> - - <_> - 9 8 5 2 -1. - <_> - 9 9 5 1 2. - <_> - - <_> - 0 14 11 3 -1. - <_> - 0 15 11 1 3. - <_> - - <_> - 8 11 2 8 -1. - <_> - 8 15 2 4 2. - <_> - - <_> - 5 11 10 6 -1. - <_> - 5 14 10 3 2. - <_> - - <_> - 5 13 15 5 -1. - <_> - 10 13 5 5 3. - <_> - - <_> - 8 10 1 10 -1. - <_> - 8 15 1 5 2. - <_> - - <_> - 4 14 6 2 -1. - <_> - 6 14 2 2 3. - <_> - - <_> - 7 14 7 3 -1. - <_> - 7 15 7 1 3. - <_> - - <_> - 7 16 9 3 -1. - <_> - 7 17 9 1 3. - <_> - - <_> - 8 7 3 3 -1. - <_> - 8 8 3 1 3. - <_> - - <_> - 3 5 1 6 -1. - <_> - 3 8 1 3 2. - <_> - - <_> - 6 5 11 2 -1. - <_> - 6 6 11 1 2. - <_> - - <_> - 9 0 3 2 -1. - <_> - 10 0 1 2 3. - <_> - - <_> - 5 5 1 3 -1. - <_> - 5 6 1 1 3. - <_> - - <_> - 8 7 3 2 -1. - <_> - 9 7 1 2 3. - <_> - - <_> - 5 2 10 6 -1. - <_> - 10 2 5 3 2. - <_> - 5 5 5 3 2. - <_> - - <_> - 8 4 6 4 -1. - <_> - 8 4 3 4 2. - <_> - - <_> - 8 16 3 4 -1. - <_> - 9 16 1 4 3. - <_> - - <_> - 9 13 2 6 -1. - <_> - 9 13 1 3 2. - <_> - 10 16 1 3 2. - <_> - - <_> - 9 8 3 1 -1. - <_> - 10 8 1 1 3. - <_> - - <_> - 2 5 18 15 -1. - <_> - 2 10 18 5 3. - <_> - - <_> - 1 3 6 2 -1. - <_> - 4 3 3 2 2. - <_> - - <_> - 7 6 6 2 -1. - <_> - 9 6 2 2 3. - <_> - - <_> - 8 17 4 3 -1. - <_> - 8 18 4 1 3. - <_> - - <_> - 10 13 2 3 -1. - <_> - 10 14 2 1 3. - <_> - - <_> - 0 10 20 4 -1. - <_> - 0 12 20 2 2. - <_> - - <_> - 5 7 6 4 -1. - <_> - 5 7 3 2 2. - <_> - 8 9 3 2 2. - <_> - - <_> - 11 12 1 2 -1. - <_> - 11 13 1 1 2. - <_> - - <_> - 10 10 2 3 -1. - <_> - 10 11 2 1 3. - <_> - - <_> - 9 5 2 2 -1. - <_> - 9 6 2 1 2. - <_> - - <_> - 4 4 1 10 -1. - <_> - 4 9 1 5 2. - <_> - - <_> - 11 18 4 2 -1. - <_> - 11 18 2 2 2. - <_> - - <_> - 12 18 3 2 -1. - <_> - 12 19 3 1 2. - <_> - - <_> - 0 6 16 6 -1. - <_> - 0 6 8 3 2. - <_> - 8 9 8 3 2. - <_> - - <_> - 7 6 4 12 -1. - <_> - 7 12 4 6 2. - <_> - - <_> - 11 18 4 2 -1. - <_> - 11 18 2 2 2. - <_> - - <_> - 12 18 3 2 -1. - <_> - 12 19 3 1 2. - <_> - - <_> - 8 12 1 2 -1. - <_> - 8 13 1 1 2. - <_> - - <_> - 8 13 1 3 -1. - <_> - 8 14 1 1 3. - <_> - - <_> - 11 18 4 2 -1. - <_> - 11 18 2 2 2. - <_> - - <_> - 14 12 4 6 -1. - <_> - 14 12 2 6 2. - <_> - - <_> - 6 0 3 4 -1. - <_> - 7 0 1 4 3. - <_> - - <_> - 4 0 2 8 -1. - <_> - 4 0 1 4 2. - <_> - 5 4 1 4 2. - <_> - - <_> - 11 17 9 3 -1. - <_> - 14 17 3 3 3. - <_> - - <_> - 16 2 4 5 -1. - <_> - 16 2 2 5 2. - <_> - - <_> - 0 2 5 9 -1. - <_> - 0 5 5 3 3. - <_> - - <_> - 7 2 3 2 -1. - <_> - 8 2 1 2 3. - <_> - - <_> - 11 17 9 3 -1. - <_> - 14 17 3 3 3. - <_> - - <_> - 16 2 4 5 -1. - <_> - 16 2 2 5 2. - <_> - - <_> - 0 17 9 3 -1. - <_> - 3 17 3 3 3. - <_> - - <_> - 0 2 4 5 -1. - <_> - 2 2 2 5 2. - <_> - - <_> - 5 11 10 9 -1. - <_> - 5 14 10 3 3. - <_> - - <_> - 9 6 3 3 -1. - <_> - 9 7 3 1 3. - <_> - - <_> - 3 17 5 3 -1. - <_> - 3 18 5 1 3. - <_> - - <_> - 7 5 4 7 -1. - <_> - 9 5 2 7 2. - <_> - - <_> - 9 8 2 5 -1. - <_> - 9 8 1 5 2. - <_> - - <_> - 2 2 18 2 -1. - <_> - 2 3 18 1 2. - <_> - - <_> - 2 8 15 6 -1. - <_> - 7 8 5 6 3. - <_> - - <_> - 9 8 2 5 -1. - <_> - 10 8 1 5 2. - <_> - - <_> - 12 10 4 6 -1. - <_> - 12 12 4 2 3. - <_> - - <_> - 14 3 6 2 -1. - <_> - 14 4 6 1 2. - <_> - - <_> - 5 5 2 3 -1. - <_> - 5 6 2 1 3. - <_> - - <_> - 4 6 3 3 -1. - <_> - 4 7 3 1 3. - <_> - - <_> - 14 12 3 3 -1. - <_> - 14 13 3 1 3. - <_> - - <_> - 6 12 11 3 -1. - <_> - 6 13 11 1 3. - <_> - - <_> - 1 2 3 6 -1. - <_> - 1 4 3 2 3. - <_> - - <_> - 1 0 4 7 -1. - <_> - 3 0 2 7 2. - <_> - - <_> - 9 8 3 4 -1. - <_> - 10 8 1 4 3. - <_> - - <_> - 10 9 2 2 -1. - <_> - 10 10 2 1 2. - <_> - - <_> - 8 8 3 4 -1. - <_> - 9 8 1 4 3. - <_> - - <_> - 4 4 10 10 -1. - <_> - 4 9 10 5 2. - <_> - - <_> - 9 10 3 2 -1. - <_> - 10 10 1 2 3. - <_> - - <_> - 9 10 3 2 -1. - <_> - 9 11 3 1 2. - <_> - - <_> - 8 10 3 2 -1. - <_> - 9 10 1 2 3. - <_> - - <_> - 2 4 14 12 -1. - <_> - 2 4 7 6 2. - <_> - 9 10 7 6 2. - <_> - - <_> - 10 12 1 6 -1. - <_> - 10 15 1 3 2. - <_> - - <_> - 7 3 8 16 -1. - <_> - 11 3 4 8 2. - <_> - 7 11 4 8 2. - <_> - - <_> - 5 6 8 10 -1. - <_> - 5 6 4 5 2. - <_> - 9 11 4 5 2. - <_> - - <_> - 6 2 8 8 -1. - <_> - 6 2 4 4 2. - <_> - 10 6 4 4 2. - <_> - - <_> - 10 5 4 2 -1. - <_> - 12 5 2 1 2. - <_> - 10 6 2 1 2. - <_> - - <_> - 12 4 3 3 -1. - <_> - 12 5 3 1 3. - <_> - - <_> - 4 19 12 1 -1. - <_> - 8 19 4 1 3. - <_> - - <_> - 8 2 3 1 -1. - <_> - 9 2 1 1 3. - <_> - - <_> - 13 17 4 3 -1. - <_> - 13 18 4 1 3. - <_> - - <_> - 7 14 6 3 -1. - <_> - 7 15 6 1 3. - <_> - - <_> - 9 14 2 3 -1. - <_> - 9 15 2 1 3. - <_> - - <_> - 7 15 6 3 -1. - <_> - 7 16 6 1 3. - <_> - - <_> - 10 18 3 2 -1. - <_> - 11 18 1 2 3. - <_> - - <_> - 14 12 2 3 -1. - <_> - 14 13 2 1 3. - <_> - - <_> - 4 10 4 6 -1. - <_> - 4 12 4 2 3. - <_> - - <_> - 4 13 3 2 -1. - <_> - 4 14 3 1 2. - <_> - - <_> - 9 16 2 3 -1. - <_> - 9 17 2 1 3. - <_> - - <_> - 10 18 3 2 -1. - <_> - 11 18 1 2 3. - <_> - - <_> - 7 18 3 2 -1. - <_> - 8 18 1 2 3. - <_> - - <_> - 1 10 4 2 -1. - <_> - 1 11 4 1 2. - <_> - - <_> - 12 4 6 3 -1. - <_> - 12 5 6 1 3. - <_> - - <_> - 14 4 1 3 -1. - <_> - 14 5 1 1 3. - <_> - - <_> - 2 4 6 3 -1. - <_> - 2 5 6 1 3. - <_> - - <_> - 5 4 1 3 -1. - <_> - 5 5 1 1 3. - <_> - - <_> - 14 12 3 3 -1. - <_> - 14 13 3 1 3. - <_> - - <_> - 15 12 2 3 -1. - <_> - 15 13 2 1 3. - <_> - - <_> - 3 16 4 3 -1. - <_> - 3 17 4 1 3. - <_> - - <_> - 8 0 4 2 -1. - <_> - 8 1 4 1 2. - <_> - - <_> - 0 0 20 1 -1. - <_> - 0 0 10 1 2. - <_> - - <_> - 9 7 3 4 -1. - <_> - 10 7 1 4 3. - <_> - - <_> - 0 0 20 1 -1. - <_> - 10 0 10 1 2. - <_> - - <_> - 8 7 3 4 -1. - <_> - 9 7 1 4 3. - <_> - - <_> - 1 6 19 3 -1. - <_> - 1 7 19 1 3. - <_> - - <_> - 12 7 4 2 -1. - <_> - 12 8 4 1 2. - <_> - - <_> - 7 8 3 3 -1. - <_> - 7 9 3 1 3. - <_> - - <_> - 7 7 3 3 -1. - <_> - 8 7 1 3 3. - <_> - - <_> - 2 9 16 3 -1. - <_> - 2 10 16 1 3. - <_> - - <_> - 9 4 2 12 -1. - <_> - 9 8 2 4 3. - <_> - - <_> - 7 3 2 5 -1. - <_> - 8 3 1 5 2. - <_> - - <_> - 9 7 2 3 -1. - <_> - 9 8 2 1 3. - <_> - - <_> - 9 14 4 3 -1. - <_> - 9 15 4 1 3. - <_> - - <_> - 7 8 6 4 -1. - <_> - 10 8 3 2 2. - <_> - 7 10 3 2 2. - <_> - - <_> - 9 7 2 2 -1. - <_> - 10 7 1 2 2. - <_> - - <_> - 5 5 6 6 -1. - <_> - 7 5 2 6 3. - <_> - - <_> - 9 1 3 6 -1. - <_> - 10 1 1 6 3. - <_> - - <_> - 4 5 12 2 -1. - <_> - 8 5 4 2 3. - <_> - - <_> - 4 2 6 4 -1. - <_> - 6 2 2 4 3. - <_> - - <_> - 4 7 8 2 -1. - <_> - 4 8 8 1 2. - <_> - - <_> - 3 6 14 6 -1. - <_> - 10 6 7 3 2. - <_> - 3 9 7 3 2. - <_> - - <_> - 3 6 14 3 -1. - <_> - 3 6 7 3 2. - <_> - - <_> - 0 5 2 2 -1. - <_> - 0 6 2 1 2. - <_> - - <_> - 8 13 4 3 -1. - <_> - 8 14 4 1 3. - <_> - - <_> - 13 0 3 20 -1. - <_> - 14 0 1 20 3. - <_> - - <_> - 10 8 10 3 -1. - <_> - 10 9 10 1 3. - <_> - - <_> - 4 0 3 20 -1. - <_> - 5 0 1 20 3. - <_> - - <_> - 0 8 10 3 -1. - <_> - 0 9 10 1 3. - <_> - - <_> - 12 5 3 4 -1. - <_> - 13 5 1 4 3. - <_> - - <_> - 6 7 12 4 -1. - <_> - 10 7 4 4 3. - <_> - - <_> - 1 14 6 6 -1. - <_> - 1 14 3 3 2. - <_> - 4 17 3 3 2. - <_> - - <_> - 1 17 6 2 -1. - <_> - 1 18 6 1 2. - <_> - - <_> - 14 8 6 12 -1. - <_> - 17 8 3 6 2. - <_> - 14 14 3 6 2. - <_> - - <_> - 18 5 2 2 -1. - <_> - 18 6 2 1 2. - <_> - - <_> - 3 16 4 2 -1. - <_> - 3 16 2 1 2. - <_> - 5 17 2 1 2. - <_> - - <_> - 2 16 6 2 -1. - <_> - 4 16 2 2 3. - <_> - - <_> - 14 8 6 12 -1. - <_> - 17 8 3 6 2. - <_> - 14 14 3 6 2. - <_> - - <_> - 18 5 2 2 -1. - <_> - 18 6 2 1 2. - <_> - - <_> - 5 16 9 2 -1. - <_> - 8 16 3 2 3. - <_> - - <_> - 3 14 6 6 -1. - <_> - 3 14 3 3 2. - <_> - 6 17 3 3 2. - <_> - - <_> - 14 8 6 12 -1. - <_> - 17 8 3 6 2. - <_> - 14 14 3 6 2. - <_> - - <_> - 11 7 2 12 -1. - <_> - 11 11 2 4 3. - <_> - - <_> - 0 8 6 12 -1. - <_> - 0 8 3 6 2. - <_> - 3 14 3 6 2. - <_> - - <_> - 7 7 2 12 -1. - <_> - 7 11 2 4 3. - <_> - - <_> - 14 12 1 2 -1. - <_> - 14 13 1 1 2. - <_> - - <_> - 12 13 8 1 -1. - <_> - 12 13 4 1 2. - <_> - - <_> - 0 3 16 6 -1. - <_> - 0 6 16 3 2. - <_> - - <_> - 1 4 8 2 -1. - <_> - 1 4 4 1 2. - <_> - 5 5 4 1 2. - <_> - - <_> - 14 12 1 2 -1. - <_> - 14 13 1 1 2. - <_> - - <_> - 15 12 2 3 -1. - <_> - 15 13 2 1 3. - <_> - - <_> - 8 16 3 3 -1. - <_> - 8 17 3 1 3. - <_> - - <_> - 5 12 1 2 -1. - <_> - 5 13 1 1 2. - <_> - - <_> - 13 4 3 15 -1. - <_> - 14 4 1 15 3. - <_> - - <_> - 17 3 2 6 -1. - <_> - 18 3 1 3 2. - <_> - 17 6 1 3 2. - <_> - - <_> - 4 4 3 15 -1. - <_> - 5 4 1 15 3. - <_> - - <_> - 1 3 2 6 -1. - <_> - 1 3 1 3 2. - <_> - 2 6 1 3 2. - <_> - - <_> - 7 15 12 4 -1. - <_> - 7 17 12 2 2. - <_> - - <_> - 1 0 19 3 -1. - <_> - 1 1 19 1 3. - <_> - - <_> - 3 17 10 2 -1. - <_> - 3 17 5 1 2. - <_> - 8 18 5 1 2. - <_> - - <_> - 2 5 10 15 -1. - <_> - 2 10 10 5 3. - <_> - - <_> - 13 8 3 4 -1. - <_> - 13 10 3 2 2. - <_> - - <_> - 19 13 1 2 -1. - <_> - 19 14 1 1 2. - <_> - - <_> - 4 8 3 4 -1. - <_> - 4 10 3 2 2. - <_> - - <_> - 0 13 1 2 -1. - <_> - 0 14 1 1 2. - <_> - - <_> - 12 7 2 12 -1. - <_> - 12 13 2 6 2. - <_> - - <_> - 14 7 2 2 -1. - <_> - 15 7 1 1 2. - <_> - 14 8 1 1 2. - <_> - - <_> - 5 3 8 2 -1. - <_> - 5 4 8 1 2. - <_> - - <_> - 0 2 2 6 -1. - <_> - 0 4 2 2 3. - <_> - - <_> - 18 2 2 12 -1. - <_> - 19 2 1 6 2. - <_> - 18 8 1 6 2. - <_> - - <_> - 18 1 1 2 -1. - <_> - 18 2 1 1 2. - <_> - - <_> - 0 2 2 12 -1. - <_> - 0 2 1 6 2. - <_> - 1 8 1 6 2. - <_> - - <_> - 1 1 1 2 -1. - <_> - 1 2 1 1 2. - <_> - - <_> - 16 4 4 14 -1. - <_> - 18 4 2 7 2. - <_> - 16 11 2 7 2. - <_> - - <_> - 10 14 1 6 -1. - <_> - 10 17 1 3 2. - <_> - - <_> - 0 4 4 14 -1. - <_> - 0 4 2 7 2. - <_> - 2 11 2 7 2. - <_> - - <_> - 9 14 1 6 -1. - <_> - 9 17 1 3 2. - <_> - - <_> - 9 14 4 3 -1. - <_> - 9 15 4 1 3. - <_> - - <_> - 4 7 12 2 -1. - <_> - 8 7 4 2 3. - <_> - - <_> - 0 8 4 3 -1. - <_> - 0 9 4 1 3. - <_> - - <_> - 4 7 2 2 -1. - <_> - 4 7 1 1 2. - <_> - 5 8 1 1 2. - <_> - - <_> - 13 7 2 1 -1. - <_> - 13 7 1 1 2. - <_> - - <_> - 11 4 4 5 -1. - <_> - 11 4 2 5 2. - <_> - - <_> - 4 8 3 3 -1. - <_> - 5 8 1 3 3. - <_> - - <_> - 0 3 8 1 -1. - <_> - 4 3 4 1 2. - <_> - - <_> - 13 7 2 1 -1. - <_> - 13 7 1 1 2. - <_> - - <_> - 14 7 3 2 -1. - <_> - 15 7 1 2 3. - <_> - - <_> - 5 7 2 1 -1. - <_> - 6 7 1 1 2. - <_> - - <_> - 3 7 3 2 -1. - <_> - 4 7 1 2 3. - <_> - - <_> - 18 5 2 2 -1. - <_> - 18 6 2 1 2. - <_> - - <_> - 12 14 2 2 -1. - <_> - 13 14 1 1 2. - <_> - 12 15 1 1 2. - <_> - - <_> - 0 5 2 2 -1. - <_> - 0 6 2 1 2. - <_> - - <_> - 6 14 2 2 -1. - <_> - 6 14 1 1 2. - <_> - 7 15 1 1 2. - <_> - - <_> - 7 12 6 5 -1. - <_> - 9 12 2 5 3. - <_> - - <_> - 12 17 5 2 -1. - <_> - 12 18 5 1 2. - <_> - - <_> - 1 11 6 3 -1. - <_> - 4 11 3 3 2. - <_> - - <_> - 1 9 6 3 -1. - <_> - 4 9 3 3 2. - <_> - - <_> - 12 7 2 12 -1. - <_> - 12 13 2 6 2. - <_> - - <_> - 8 7 5 3 -1. - <_> - 8 8 5 1 3. - <_> - - <_> - 6 7 2 12 -1. - <_> - 6 13 2 6 2. - <_> - - <_> - 1 2 9 18 -1. - <_> - 4 2 3 18 3. - <_> - - <_> - 12 17 5 2 -1. - <_> - 12 18 5 1 2. - <_> - - <_> - 4 7 12 2 -1. - <_> - 4 7 6 2 2. - <_> - - <_> - 6 7 6 1 -1. - <_> - 8 7 2 1 3. - <_> - - <_> - 7 3 3 2 -1. - <_> - 8 3 1 2 3. - <_> - - <_> - 9 4 3 1 -1. - <_> - 10 4 1 1 3. - <_> - - <_> - 11 11 3 1 -1. - <_> - 12 11 1 1 3. - <_> - - <_> - 8 4 3 1 -1. - <_> - 9 4 1 1 3. - <_> - - <_> - 6 11 3 1 -1. - <_> - 7 11 1 1 3. - <_> - - <_> - 12 13 6 6 -1. - <_> - 12 15 6 2 3. - <_> - - <_> - 14 13 1 6 -1. - <_> - 14 15 1 2 3. - <_> - - <_> - 2 13 6 6 -1. - <_> - 2 15 6 2 3. - <_> - - <_> - 1 5 18 1 -1. - <_> - 7 5 6 1 3. - <_> - - <_> - 4 7 12 2 -1. - <_> - 10 7 6 1 2. - <_> - 4 8 6 1 2. - <_> - - <_> - 6 1 8 10 -1. - <_> - 10 1 4 5 2. - <_> - 6 6 4 5 2. - <_> - - <_> - 3 13 4 3 -1. - <_> - 3 14 4 1 3. - <_> - - <_> - 6 13 4 3 -1. - <_> - 6 14 4 1 3. - <_> - - <_> - 9 14 4 3 -1. - <_> - 9 15 4 1 3. - <_> - - <_> - 12 9 2 3 -1. - <_> - 12 10 2 1 3. - <_> - - <_> - 7 14 4 3 -1. - <_> - 7 15 4 1 3. - <_> - - <_> - 9 0 2 1 -1. - <_> - 10 0 1 1 2. - <_> - - <_> - 5 0 10 5 -1. - <_> - 5 0 5 5 2. - <_> - - <_> - 6 6 8 7 -1. - <_> - 6 6 4 7 2. - <_> - - <_> - 5 0 10 5 -1. - <_> - 10 0 5 5 2. - <_> - - <_> - 6 6 8 7 -1. - <_> - 10 6 4 7 2. - <_> - - <_> - 5 9 10 8 -1. - <_> - 10 9 5 4 2. - <_> - 5 13 5 4 2. - <_> - - <_> - 10 0 4 10 -1. - <_> - 12 0 2 5 2. - <_> - 10 5 2 5 2. - <_> - - <_> - 1 4 8 3 -1. - <_> - 1 5 8 1 3. - <_> - - <_> - 4 4 8 3 -1. - <_> - 4 5 8 1 3. - <_> - - <_> - 9 7 4 3 -1. - <_> - 9 8 4 1 3. - <_> - - <_> - 12 8 3 12 -1. - <_> - 12 14 3 6 2. - <_> - - <_> - 7 7 4 3 -1. - <_> - 7 8 4 1 3. - <_> - - <_> - 5 8 3 12 -1. - <_> - 5 14 3 6 2. - <_> - - <_> - 10 0 7 6 -1. - <_> - 10 2 7 2 3. - <_> - - <_> - 2 1 18 1 -1. - <_> - 8 1 6 1 3. - <_> - - <_> - 5 0 3 8 -1. - <_> - 6 0 1 8 3. - <_> - - <_> - 4 7 4 2 -1. - <_> - 4 8 4 1 2. - diff --git a/data/map/aire_0.dat b/data/map/aire_0.dat new file mode 100644 index 000000000..08d5457c3 --- /dev/null +++ b/data/map/aire_0.dat @@ -0,0 +1,6 @@ +2 +5.97270011901855,45.5285783467813 +5.976382791996,45.5285783467813 +5.976382791996,45.5258330159471 +5.97270011901855,45.5258330159471 +(SASPlanet) diff --git a/data/map/aire_0.jpg b/data/map/aire_0.jpg new file mode 100644 index 000000000..6224da23a Binary files /dev/null and b/data/map/aire_0.jpg differ diff --git a/data/map/geo_bind.ini b/data/map/geo_bind.ini new file mode 100644 index 000000000..d3b448e1b --- /dev/null +++ b/data/map/geo_bind.ini @@ -0,0 +1,57 @@ +[points] + +# Points pair select manual: from maps.google [lat, lon] <-> [x, y] in pixels from imge editor (gimp, paint etc) +# Pixel coordinates are in [0, 1] - independed from frame resolution +lat0 = 45.526646 +lon0 = 5.974535 +px_x0 = 0.328125 +px_y0 = 0.483333333333333 + +lat1 = 45.527566 +lon1 = 5.973849 +px_x1 = 0.39765625 +px_y1 = 0.393055555555556 + +lat2 = 45.527904 +lon2 = 5.974135 +px_x2 = 0.57109375 +px_y2 = 0.390277777777778 + +lat3 = 45.526867 +lon3 = 5.974826 +px_x3 = 0.65625 +px_y3 = 0.476388888888889 + + +[lines] + +# Line coordinates are in [0, 1] - independed from frame resolution +line0_x0 = 0.1 +line0_y0 = 0.7 +line0_x1 = 0.47 +line0_y1 = 0.7 + +line1_x0 = 0.52 +line1_y0 = 0.6 +line1_x1 = 0.8 +line1_y1 = 0.6 + + +[map] + +# optional: map exported from SASPlanet with *.dat file in Merkator projection + +file = map\aire_0.jpg + +left_top_lat = 45.5285783467813 +left_top_lon = 5.97270011901855 + +right_top_lat = 45.5285783467813 +right_top_lon = 5.976382791996 + +right_bottom_lat = 45.5258330159471 +right_bottom_lon = 5.976382791996 + +left_bottom_lat = 45.5258330159471 +left_bottom_lon = 5.97270011901855 + diff --git a/data/map/manual/france_bind.jpg b/data/map/manual/france_bind.jpg new file mode 100644 index 000000000..bbeca503d Binary files /dev/null and b/data/map/manual/france_bind.jpg differ diff --git a/data/map/manual/paint1.png b/data/map/manual/paint1.png new file mode 100644 index 000000000..d7d95172c Binary files /dev/null and b/data/map/manual/paint1.png differ diff --git a/data/map/manual/photo_2023-08-10_09-44-05.jpg b/data/map/manual/photo_2023-08-10_09-44-05.jpg new file mode 100644 index 000000000..1801574bb Binary files /dev/null and b/data/map/manual/photo_2023-08-10_09-44-05.jpg differ diff --git a/data/map/manual/text_editor1.png b/data/map/manual/text_editor1.png new file mode 100644 index 000000000..86186616e Binary files /dev/null and b/data/map/manual/text_editor1.png differ diff --git a/data/map/run_cars.bat b/data/map/run_cars.bat new file mode 100644 index 000000000..cb696bd85 --- /dev/null +++ b/data/map/run_cars.bat @@ -0,0 +1,3 @@ + +MultitargetTracker.exe map/Relaxing_highway_traffic.mp4 -e=7 --geo_bind=map/geo_bind.ini --settings=settings.ini + diff --git a/data/map/run_cars.sh b/data/map/run_cars.sh new file mode 100644 index 000000000..f48fbe52a --- /dev/null +++ b/data/map/run_cars.sh @@ -0,0 +1,2 @@ +./MultitargetTracker map/Relaxing_highway_traffic.mp4 -e=7 --geo_bind=map/geo_bind.ini --settings=settings.ini + diff --git a/data/reid/osnet_x0_25_msmt17.onnx b/data/reid/osnet_x0_25_msmt17.onnx new file mode 100644 index 000000000..43a689e71 Binary files /dev/null and b/data/reid/osnet_x0_25_msmt17.onnx differ diff --git a/data/settings.ini b/data/settings.ini new file mode 100644 index 000000000..d91395d5d --- /dev/null +++ b/data/settings.ini @@ -0,0 +1,139 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 6 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = ../../data/yolov4.weights +nn_config = ../../data/yolov4.cfg +class_names = ../../data/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = -1 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV4 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precison = FP32 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_coco.ini b/data/settings_coco.ini new file mode 100644 index 000000000..b016a5554 --- /dev/null +++ b/data/settings_coco.ini @@ -0,0 +1,154 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +# nn_weights = data/coco/yolov7.onnx +# nn_config = data/coco/yolov7.onnx + +# nn_weights = data/coco/yolov6s.onnx +# nn_config = data/coco/yolov6s.onnx + +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/yolov4.weights +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/yolov4.cfg + +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco.names + +#----------------------------- +confidence_threshold = 0.2 + +max_crop_ratio = -1 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV4 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP32 + +#----------------------------- +# Detect only set of types, ";" +white_list = + +#----------------------------- +# For TensorRT optimization, bytes +video_memory = 0; + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_deimv2.ini b/data/settings_deimv2.ini new file mode 100644 index 000000000..e691d9ac1 --- /dev/null +++ b/data/settings_deimv2.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/deimv2_dinov3_m_coco.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/deimv2_dinov3_m_coco.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.1 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = DFINE + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_dfine.ini b/data/settings_dfine.ini new file mode 100644 index 000000000..0ad660cf1 --- /dev/null +++ b/data/settings_dfine.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/dfine_m_obj2coco.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/dfine_m_obj2coco.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = DFINE + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_dfine_seg.ini b/data/settings_dfine_seg.ini new file mode 100644 index 000000000..e82c504ae --- /dev/null +++ b/data/settings_dfine_seg.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/dfine_seg_s_coco.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/dfine_seg_s_coco.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = DFINE_IS + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_rfdetr.ini b/data/settings_rfdetr.ini new file mode 100644 index 000000000..10037acdd --- /dev/null +++ b/data/settings_rfdetr.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/rfdetr_sim_coco.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/rfdetr_sim_coco.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco_91.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = RFDETR + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_rfdetr_seg.ini b/data/settings_rfdetr_seg.ini new file mode 100644 index 000000000..f1cc5da66 --- /dev/null +++ b/data/settings_rfdetr_seg.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/rfdetr_seg_coco.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/rfdetr_seg_coco.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco_91.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = RFDETR_IS + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_rtdetrv4.ini b/data/settings_rtdetrv4.ini new file mode 100644 index 000000000..4734cf9fb --- /dev/null +++ b/data/settings_rtdetrv4.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/RTv4-M-hgnet.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/RTv4-M-hgnet.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = DFINE + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov10.ini b/data/settings_yolov10.ini new file mode 100644 index 000000000..a7db951b1 --- /dev/null +++ b/data/settings_yolov10.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov10s.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov10s.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.3 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV10 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov11_obb.ini b/data/settings_yolov11_obb.ini new file mode 100644 index 000000000..36641f010 --- /dev/null +++ b/data/settings_yolov11_obb.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/dota/yolo11s-obb.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/dota/yolo11s-obb.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/dota/DOTA_v1.0.names + +#----------------------------- +confidence_threshold = 0.3 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV11_OBB + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov11_seg.ini b/data/settings_yolov11_seg.ini new file mode 100644 index 000000000..a296bb651 --- /dev/null +++ b/data/settings_yolov11_seg.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolo11s-seg.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolo11s-seg.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.3 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV11Mask + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov11m.ini b/data/settings_yolov11m.ini new file mode 100644 index 000000000..e2d29ee39 --- /dev/null +++ b/data/settings_yolov11m.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolo11m.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolo11m.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV11 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov11x.ini b/data/settings_yolov11x.ini new file mode 100644 index 000000000..d8043afa7 --- /dev/null +++ b/data/settings_yolov11x.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolo11x.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolo11x.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV11 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov11x_obb.ini b/data/settings_yolov11x_obb.ini new file mode 100644 index 000000000..bb65f3b5b --- /dev/null +++ b/data/settings_yolov11x_obb.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/dota/yolo11x-obb.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/dota/yolo11x-obb.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/dota/DOTA_v1.0.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 1 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV11_OBB + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov12m.ini b/data/settings_yolov12m.ini new file mode 100644 index 000000000..ca6d381b7 --- /dev/null +++ b/data/settings_yolov12m.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_INFERENCE_ENGINE + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov12m.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov12m.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV12 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov12x.ini b/data/settings_yolov12x.ini new file mode 100644 index 000000000..6f1e7be6a --- /dev/null +++ b/data/settings_yolov12x.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov12x.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov12x.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV12 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov13s.ini b/data/settings_yolov13s.ini new file mode 100644 index 000000000..cfd75f5ba --- /dev/null +++ b/data/settings_yolov13s.ini @@ -0,0 +1,148 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov13s.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov13s.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV13 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# UniversalTracker = 0 +# ByteTrack = 1 +tracker_type = 1 + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackCSRT = 2 +# TrackDaSiamRPN = 3 +# TrackNano = 4 +# TrackVit = 5 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchLAPJV = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 + +#----------------------------- +# Settings only for m_tracker = tracking::ByteTrack +bytetrack_track_buffer = 30 +bytetrack_track_thresh = 0.5 +bytetrack_high_thresh = 0.5 +bytetrack_match_thresh = 0.8 diff --git a/data/settings_yolov26m.ini b/data/settings_yolov26m.ini new file mode 100644 index 000000000..625ce893a --- /dev/null +++ b/data/settings_yolov26m.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_INFERENCE_ENGINE + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolo26m.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolo26m.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV26 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov26m_obb.ini b/data/settings_yolov26m_obb.ini new file mode 100644 index 000000000..d31e8425b --- /dev/null +++ b/data/settings_yolov26m_obb.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/dota/yolo26m-obb.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/dota/yolo26m-obb.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/dota/DOTA_v1.0.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 1 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV26_OBB + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov26m_seg.ini b/data/settings_yolov26m_seg.ini new file mode 100644 index 000000000..3a4ed1d05 --- /dev/null +++ b/data/settings_yolov26m_seg.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolo26m-seg.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolo26m-seg.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.3 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV26Mask + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov5.ini b/data/settings_yolov5.ini new file mode 100644 index 000000000..85cc89cc7 --- /dev/null +++ b/data/settings_yolov5.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov5m.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov5m.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 1.5 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV5 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov6.ini b/data/settings_yolov6.ini new file mode 100644 index 000000000..140e07d8e --- /dev/null +++ b/data/settings_yolov6.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov6m.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov6m.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 1.5 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV6 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov7.ini b/data/settings_yolov7.ini new file mode 100644 index 000000000..201ab70b7 --- /dev/null +++ b/data/settings_yolov7.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov7_b1.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov7_b1.onnx +#nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov7_b2.onnx +#nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov7_b2.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 1 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV7 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov7mask.ini b/data/settings_yolov7mask.ini new file mode 100644 index 000000000..bc6ca37c1 --- /dev/null +++ b/data/settings_yolov7mask.ini @@ -0,0 +1,146 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov7-seg_orig.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov7-seg_orig.onnx +#nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov7_b1.onnx +#nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov7_b1.onnx +#nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov7_b2.onnx +#nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov7_b2.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV7Mask +#net_type = YOLOV7 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov8.ini b/data/settings_yolov8.ini new file mode 100644 index 000000000..6be1c1db1 --- /dev/null +++ b/data/settings_yolov8.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov8m.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov8m.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 1.5 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV8 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov8mask.ini b/data/settings_yolov8mask.ini new file mode 100644 index 000000000..edec20eba --- /dev/null +++ b/data/settings_yolov8mask.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov8s-seg.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov8s-seg.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV8Mask + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov8x_obb.ini b/data/settings_yolov8x_obb.ini new file mode 100644 index 000000000..05ebf94f6 --- /dev/null +++ b/data/settings_yolov8x_obb.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CPU + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_OPENCV + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/dota/yolov8x-obb.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/dota/yolov8x-obb.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/dota/DOTA_v1.0.names + +#----------------------------- +confidence_threshold = 0.5 + +max_crop_ratio = 1 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV8_OBB + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov9.ini b/data/settings_yolov9.ini new file mode 100644 index 000000000..ec1382e5f --- /dev/null +++ b/data/settings_yolov9.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CUDA_FP16 + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_CUDA + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov9-e.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/coco/yolov9-e.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/coco/coco.names + +#----------------------------- +confidence_threshold = 0.3 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV9 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/data/settings_yolov9_dota.ini b/data/settings_yolov9_dota.ini new file mode 100644 index 000000000..6597ccf31 --- /dev/null +++ b/data/settings_yolov9_dota.ini @@ -0,0 +1,141 @@ +[detection] + +#----------------------------- +# opencv_dnn = 6 +# tensorrt = 5 +detector_backend = 5 + +#----------------------------- +# Target and backend for opencv_dnn detector +# DNN_TARGET_CPU +# DNN_TARGET_OPENCL +# DNN_TARGET_OPENCL_FP16 +# DNN_TARGET_MYRIAD +# DNN_TARGET_CUDA +# DNN_TARGET_CUDA_FP16 +ocv_dnn_target = DNN_TARGET_CUDA_FP16 + +# DNN_BACKEND_DEFAULT +# DNN_BACKEND_HALIDE +# DNN_BACKEND_INFERENCE_ENGINE +# DNN_BACKEND_OPENCV +# DNN_BACKEND_VKCOM +# DNN_BACKEND_CUDA +# DNN_BACKEND_INFERENCE_ENGINE_NGRAPH +# DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 +ocv_dnn_backend = DNN_BACKEND_CUDA + +#----------------------------- +nn_weights = C:/work/home/mtracker/Multitarget-tracker/data/dota/yolov9_DOTA.onnx +nn_config = C:/work/home/mtracker/Multitarget-tracker/data/dota/yolov9_DOTA.onnx +class_names = C:/work/home/mtracker/Multitarget-tracker/data/dota/DOTA_v1.5.names + +#----------------------------- +confidence_threshold = 0.3 + +max_crop_ratio = 0 +max_batch = 1 +gpu_id = 0 + +#----------------------------- +# YOLOV3 +# YOLOV4 +# YOLOV5 +net_type = YOLOV9 + +#----------------------------- +# INT8 +# FP16 +# FP32 +inference_precision = FP16 + + +[tracking] + +#----------------------------- +# DistCenters = 0 // Euclidean distance between centers, pixels +# DistRects = 1 // Euclidean distance between bounding rectangles, pixels +# DistJaccard = 2 // Intersection over Union, IoU, [0, 1] +# DistHist = 3 // Bhatacharia distance between histograms, [0, 1] + +distance_type = 0 + +#----------------------------- +# KalmanLinear = 0 +# KalmanUnscented = 1 + +kalman_type = 0 + +#----------------------------- +# FilterCenter = 0 +# FilterRect = 1 +# FilterRRect = 2 + +filter_goal = 0 + +#----------------------------- +# TrackNone = 0 +# TrackKCF = 1 +# TrackMIL = 2 +# TrackMedianFlow = 3 +# TrackGOTURN = 4 +# TrackMOSSE = 5 +# TrackCSRT = 6 +# TrackDAT = 7 +# TrackSTAPLE = 8 +# TrackLDES = 9 +# TrackDaSiamRPN = 10 +# Used if filter_goal == FilterRect + +lost_track_type = 0 + +#----------------------------- +# MatchHungrian = 0 +# MatchBipart = 1 + +match_type = 0 + +#----------------------------- +# Use constant acceleration motion model: +# 0 - unused (stable) +# 1 - use acceleration in Kalman filter (experimental) +use_aceleration = 0 + +#----------------------------- +# Delta time for Kalman filter +delta_time = 0.4 + +#----------------------------- +# Accel noise magnitude for Kalman filter +accel_noise = 0.2 + +#----------------------------- +# Distance threshold between region and object on two frames +dist_thresh = 0.8 + +#----------------------------- +# If this value > 0 than will be used circle with this radius +# If this value <= 0 than will be used ellipse with size (3*vx, 3*vy), vx and vy - horizontal and vertical speed in pixelsa +min_area_radius_pix = -1 + +#----------------------------- +# Minimal area radius in ration for object size. Used if min_area_radius_pix < 0 +min_area_radius_k = 0.8 + +#----------------------------- +# If the object do not assignment more than this seconds then it will be removed +max_lost_time = 2 + +#----------------------------- +# The maximum trajectory length +max_trace_len = 2 + +#----------------------------- +# Detection abandoned objects +detect_abandoned = 0 +# After this time (in seconds) the object is considered abandoned +min_static_time = 5 +# After this time (in seconds) the abandoned object will be removed +max_static_time = 25 +# Speed in pixels. If speed of object is more that this value than object is non static +max_speed_for_static = 10 diff --git a/defines.h b/defines.h deleted file mode 100644 index 2ae5a1943..000000000 --- a/defines.h +++ /dev/null @@ -1,117 +0,0 @@ -#pragma once -#include -#include -#include - - -// --------------------------------------------------------------------------- -// -// --------------------------------------------------------------------------- -typedef float track_t; -typedef cv::Point_ Point_t; -#define Mat_t CV_32FC - -/// -/// \brief The CRegion class -/// -class CRegion -{ -public: - CRegion() - : m_type(""), m_confidence(-1) - { - } - - CRegion(const cv::Rect& rect) - : m_rect(rect) - { - - } - - CRegion(const cv::Rect& rect, const std::string& type, float confidence) - : m_rect(rect), m_type(type), m_confidence(confidence) - { - - } - - cv::Rect m_rect; - std::vector m_points; - - std::string m_type; - float m_confidence; -}; - -typedef std::vector regions_t; - -/// -/// -/// -namespace tracking -{ -/// -enum Detectors -{ - Motion_VIBE, - Motion_MOG, - Motion_GMG, - Motion_CNT, - Motion_SuBSENSE, - Motion_LOBSTER, - Motion_MOG2, - Face_HAAR, - Pedestrian_HOG, - Pedestrian_C4, - DNN -}; - -/// -/// \brief The DistType enum -/// -enum DistType -{ - DistCenters = 0, - DistRects = 1, - DistJaccard = 2 -}; - -/// -/// \brief The FilterGoal enum -/// -enum FilterGoal -{ - FilterCenter = 0, - FilterRect = 1 -}; - -/// -/// \brief The KalmanType enum -/// -enum KalmanType -{ - KalmanLinear = 0, - KalmanUnscented = 1, - KalmanAugmentedUnscented -}; - -/// -/// \brief The MatchType enum -/// -enum MatchType -{ - MatchHungrian = 0, - MatchBipart = 1 -}; - -/// -/// \brief The LostTrackType enum -/// -enum LostTrackType -{ - TrackNone = 0, - TrackKCF = 1, - TrackMIL, - TrackMedianFlow, - TrackGOTURN, - TrackMOSSE -}; -} diff --git a/demo.py b/demo.py new file mode 100644 index 000000000..7f467e45e --- /dev/null +++ b/demo.py @@ -0,0 +1,103 @@ +import sys +import glob +import getopt +import numpy as np +import cv2 as cv +import pymtracking as mt + +print("OpenCV Version: {}".format(cv.__version__)) + + +def draw_regions(img, regions, color): + for reg in regions: + brect = reg.brect + cv.rectangle(img, (brect.x, brect.y, brect.width, brect.height), color, 2) + + +def draw_tracks(img, tracks, fps): + for track in tracks: + brect = track.GetBoundingRect() + if track.isStatic: + cv.rectangle(img, (brect.x, brect.y, brect.width, brect.height), (255, 0, 255), 2) + elif track.IsRobust(int(fps / 4), 0.7, (0.1, 10.), 3): + cv.rectangle(img, (brect.x, brect.y, brect.width, brect.height), (0, 255, 0), 2) + trajectory = track.GetTrajectory() + for i in range(0, len(trajectory) - 1): + cv.line(img, trajectory[i], trajectory[i+1], (0, 255, 0), 1) + + +def main(): + args, video_src = getopt.getopt(sys.argv[1:], '', ['cascade=', 'nested-cascade=']) + try: + video_src = video_src[0] + except: + video_src = 0 + args = dict(args) + + cam = cv.VideoCapture(video_src) + + _ret, img = cam.read() + print("cam.read res = ", _ret, ", im size = ", img.shape) + + fps = cam.get(cv.CAP_PROP_FPS) + print(video_src, " fps = ", fps) + + configBGFG = mt.KeyVal() + configBGFG.Add('useRotatedRect', '20') + configBGFG.Add('history', '1000') + configBGFG.Add("nmixtures", "3") + configBGFG.Add("backgroundRatio", "0.7") + configBGFG.Add("noiseSigma", "0") + print("configBGFG = ", configBGFG) + mdetector = mt.BaseDetector(mt.BaseDetector.Detectors.MOG, configBGFG, img) + print("CanGrayProcessing: ", mdetector.CanGrayProcessing()) + mdetector.SetMinObjectSize((1, 1)) + + tracker_settings = mt.TrackerSettings() + + tracker_settings.SetDistance(mt.MTracker.DistRects) + tracker_settings.kalmanType = mt.MTracker.KalmanLinear + tracker_settings.filterGoal = mt.MTracker.FilterCenter + tracker_settings.lostTrackType = mt.MTracker.TrackNone + tracker_settings.matchType = mt.MTracker.MatchHungrian + tracker_settings.useAcceleration = False + tracker_settings.dt = 0.5 + tracker_settings.accelNoiseMag = 0.1 + tracker_settings.distThres = 0.95 + tracker_settings.minAreaRadiusPix = img.shape[0] / 5. + tracker_settings.minAreaRadiusK = 0.8 + tracker_settings.useAbandonedDetection = False + tracker_settings.maximumAllowedSkippedFrames = int(2 * fps) + tracker_settings.maxTraceLength = int(2 * fps) + + mtracker = mt.MTracker(tracker_settings) + + while True: + _ret, img = cam.read() + if _ret: + print("cam.read res = ", _ret, ", im size = ", img.shape, ", fps = ", fps) + else: + break + + mdetector.Detect(img) + regions = mdetector.GetDetects() + print("mdetector.Detect:", len(regions)) + + mtracker.Update(regions, img, fps) + tracks = mtracker.GetTracks() + print("mtracker.Update:", len(tracks)) + + vis = img.copy() + # draw_regions(vis, regions, (255, 0, 255)) + draw_tracks(vis, tracks, fps) + cv.imshow('detect', vis) + + if cv.waitKey(int(1000 / fps)) == 27: + break + + print('Done') + + +if __name__ == '__main__': + main() + cv.destroyAllWindows() diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 000000000..163680cb1 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required (VERSION 3.5) + +project(MultitargetTracker) + +set(SOURCES main.cpp + VideoExample.cpp) + +set(HEADERS MouseExample.h + VideoExample.h + examples.h + MotionDetectorExample.h + FileLogger.h) + +if (BUILD_CARS_COUNTING) + set(SOURCES ${SOURCES} CarsCounting.cpp) + set(HEADERS ${HEADERS} CarsCounting.h) +endif(BUILD_CARS_COUNTING) + +# ---------------------------------------------------------------------------- +# добавляем include директории +# ---------------------------------------------------------------------------- +INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/../src + ${PROJECT_SOURCE_DIR}/../src/mtracking + ${PROJECT_SOURCE_DIR}/../src/Detector + ${PROJECT_SOURCE_DIR}/../src/Detector/vibe_src + ${PROJECT_SOURCE_DIR}/../src/Detector/Subsense + ${PROJECT_SOURCE_DIR}/../src/Tracker + ${PROJECT_SOURCE_DIR}/../src/Tracker/HungarianAlg + ${PROJECT_SOURCE_DIR}/../thirdparty + ${PROJECT_SOURCE_DIR}/../thirdparty/spdlog/include) + +set(LIBS ${OpenCV_LIBS} + mtracking + mdetection + inih) + +if (BUILD_ONNX_TENSORRT) + add_definitions(-DBUILD_ONNX_TENSORRT) +endif(BUILD_ONNX_TENSORRT) + +if (BUILD_CARS_COUNTING) + add_definitions(-DBUILD_CARS_COUNTING) +endif(BUILD_CARS_COUNTING) + +if (USE_CLIP) + add_definitions(-DUSE_CLIP) + set(LIBS ${LIBS} ruclip) +endif(USE_CLIP) + +ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCES} ${HEADERS}) + +TARGET_LINK_LIBRARIES(${PROJECT_NAME} PRIVATE ${LIBS}) + +set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "apps") \ No newline at end of file diff --git a/example/CarsCounting.cpp b/example/CarsCounting.cpp new file mode 100644 index 000000000..cf801901a --- /dev/null +++ b/example/CarsCounting.cpp @@ -0,0 +1,472 @@ +#include "CarsCounting.h" +#include + +/// +/// \brief CarsCounting::CarsCounting +/// \param parser +/// +CarsCounting::CarsCounting(const cv::CommandLineParser& parser) + : VideoExample(parser) +{ +#ifdef _WIN32 + std::string pathToModel = "../../data/"; +#else + std::string pathToModel = "../data/"; +#endif + + m_drawHeatMap = parser.get("heat_map") != 0; + + std::string settingsFile = parser.get("settings"); + m_trackerSettingsLoaded = ParseTrackerSettings(settingsFile, m_trackerSettings); + + m_logger->info("Inference loaded ({0}) from {1}: used {2} backend, weights: {3}, config: {4}, names: {5}", + m_trackerSettingsLoaded, settingsFile, m_trackerSettings.m_detectorBackend, m_trackerSettings.m_nnWeights, m_trackerSettings.m_nnConfig, m_trackerSettings.m_classNames); + + m_geoBindFile = parser.get("geo_bind"); +} + +/// +/// \brief CarsCounting::DrawTrack +/// \param frame +/// \param track +/// \param drawTrajectory +/// \param framesCounters +/// +void CarsCounting::DrawTrack(cv::Mat frame, const TrackingObject& track, bool drawTrajectory, int framesCounter, const std::string& /*userLabel*/) +{ + cv::Rect brect = track.m_rrect.boundingRect(); + + m_resultsLog.AddTrack(framesCounter, track.m_ID, brect, track.m_type, track.m_confidence); + m_resultsLog.AddRobustTrack(track.m_ID); + + if (track.m_isStatic) + { +#if (CV_VERSION_MAJOR >= 4) + cv::rectangle(frame, brect, cv::Scalar(255, 0, 255), 2, cv::LINE_AA); +#else + cv::rectangle(frame, brect, cv::Scalar(255, 0, 255), 2, CV_AA); +#endif + } + else + { +#if (CV_VERSION_MAJOR >= 4) + cv::rectangle(frame, brect, cv::Scalar(0, 255, 0), 1, cv::LINE_AA); +#else + cv::rectangle(frame, brect, cv::Scalar(0, 255, 0), 1, CV_AA); +#endif + + if (!m_geoParams.Empty()) + { + int traceSize = static_cast(track.m_trace.size()); + int period = std::min(2 * cvRound(m_fps), traceSize); + const auto& from = m_geoParams.Pix2Geo(track.m_trace[traceSize - period]); + const auto& to = m_geoParams.Pix2Geo(track.m_trace[traceSize - 1]); + auto dist = DistanceInMeters(from, to); + + std::stringstream label; + if (period >= cvRound(m_fps) / 4) + { + auto velocity = (3.6f * dist * m_fps) / period; + //std::cout << TypeConverter::Type2Str(track.m_type) << ": distance " << std::fixed << std::setw(2) << std::setprecision(2) << dist << " on time " << (period / m_fps) << " with velocity " << velocity << " km/h: " << track.m_confidence << std::endl; + if (velocity < 1.f || std::isnan(velocity)) + velocity = 0; + //label << TypeConverter::Type2Str(track.m_type) << " " << std::fixed << std::setw(2) << std::setprecision(2) << velocity << " km/h"; + label << TypeConverter::Type2Str(track.m_type) << " " << cvRound(velocity) << " km/h"; + + int baseLine = 0; + double fontScale = (frame.cols < 2000) ? 0.5 : 1.; + cv::Size labelSize = cv::getTextSize(label.str(), cv::FONT_HERSHEY_TRIPLEX, fontScale, 1, &baseLine); + + if (brect.x < 0) + { + brect.width = std::min(brect.width, frame.cols - 1); + brect.x = 0; + } + else if (brect.x + brect.width >= frame.cols) + { + brect.x = std::max(0, frame.cols - brect.width - 1); + brect.width = std::min(brect.width, frame.cols - 1); + } + if (brect.y - labelSize.height < 0) + { + brect.height = std::min(brect.height, frame.rows - 1); + brect.y = labelSize.height; + } + else if (brect.y + brect.height >= frame.rows) + { + brect.y = std::max(0, frame.rows - brect.height - 1); + brect.height = std::min(brect.height, frame.rows - 1); + } + cv::rectangle(frame, cv::Rect(cv::Point(brect.x, brect.y - labelSize.height), cv::Size(labelSize.width, labelSize.height + baseLine)), cv::Scalar(200, 200, 200), cv::FILLED); + cv::putText(frame, label.str(), brect.tl(), cv::FONT_HERSHEY_TRIPLEX, fontScale, cv::Scalar(0, 0, 0)); + + if (velocity > 3) + AddToHeatMap(brect); + } + } + } + + if (drawTrajectory) + { + cv::Scalar cl = m_colors[track.m_ID.ID2Module(m_colors.size())]; + + for (size_t j = 0; j < track.m_trace.size() - 1; ++j) + { + const TrajectoryPoint& pt1 = track.m_trace.at(j); + const TrajectoryPoint& pt2 = track.m_trace.at(j + 1); +#if (CV_VERSION_MAJOR >= 4) + cv::line(frame, pt1.m_prediction, pt2.m_prediction, cl, 1, cv::LINE_AA); +#else + cv::line(frame, pt1.m_prediction, pt2.m_prediction, cl, 1, CV_AA); +#endif + if (!pt2.m_hasRaw) + { +#if (CV_VERSION_MAJOR >= 4) + cv::circle(frame, pt2.m_prediction, 4, cl, 1, cv::LINE_AA); +#else + cv::circle(frame, pt2.m_prediction, 4, cl, 1, CV_AA); +#endif + } + } + } +} + +/// +/// \brief CarsCounting::InitDetector +/// \param frame +/// +bool CarsCounting::InitDetector(cv::UMat frame) +{ + if (!m_trackerSettingsLoaded) + return false; + + config_t config; + + config.emplace("modelConfiguration", m_trackerSettings.m_nnConfig); + config.emplace("modelBinary", m_trackerSettings.m_nnWeights); + config.emplace("confidenceThreshold", std::to_string(m_trackerSettings.m_confidenceThreshold)); + config.emplace("classNames", m_trackerSettings.m_classNames); + config.emplace("maxCropRatio", std::to_string(m_trackerSettings.m_maxCropRatio)); + config.emplace("maxBatch", std::to_string(m_trackerSettings.m_maxBatch)); + config.emplace("gpuId", std::to_string(m_trackerSettings.m_gpuId)); + config.emplace("net_type", m_trackerSettings.m_netType); + config.emplace("inference_precision", m_trackerSettings.m_inferencePrecision); + config.emplace("video_memory", std::to_string(m_trackerSettings.m_maxVideoMemory)); + config.emplace("dnnTarget", m_trackerSettings.m_dnnTarget); + config.emplace("dnnBackend", m_trackerSettings.m_dnnBackend); + config.emplace("inWidth", std::to_string(m_trackerSettings.m_inputSize.width)); + config.emplace("inHeight", std::to_string(m_trackerSettings.m_inputSize.height)); + + for (auto wname : m_trackerSettings.m_whiteList) + { + config.emplace("white_list", wname); + } + + m_detector = BaseDetector::CreateDetector((tracking::Detectors)m_trackerSettings.m_detectorBackend, config, frame); + + return m_detector.operator bool(); +} + +/// +/// \brief CarsCounting::InitTracker +/// \param grayFrame +/// +bool CarsCounting::InitTracker(cv::UMat frame) +{ + if (!m_trackerSettingsLoaded) + return false; + + if (m_drawHeatMap) + { + if (frame.channels() == 3) + m_keyFrame = frame.getMat(cv::ACCESS_READ).clone(); + else + cv::cvtColor(frame, m_keyFrame, cv::COLOR_GRAY2BGR); + m_heatMap = cv::Mat(m_keyFrame.size(), CV_32FC1, cv::Scalar::all(0)); + } + + const int minStaticTime = 5; + + TrackerSettings settings; + settings.SetDistance(tracking::DistJaccard); + settings.m_kalmanType = tracking::KalmanLinear; + settings.m_filterGoal = tracking::FilterCenter; + settings.m_lostTrackType = tracking::TrackCSRT; // Use KCF tracker for collisions resolving. Used if m_filterGoal == tracking::FilterRect + settings.m_matchType = tracking::MatchHungrian; + settings.m_dt = 0.3f; // Delta time for Kalman filter + settings.m_accelNoiseMag = 0.2f; // Accel noise magnitude for Kalman filter + settings.m_distThres = 0.7f; // Distance threshold between region and object on two frames + settings.m_minAreaRadiusPix = frame.rows / 20.f; + settings.m_maximumAllowedLostTime = 2.; // Maximum allowed lost time + + settings.AddNearTypes(TypeConverter::Str2Type("car"), TypeConverter::Str2Type("bus"), false); + settings.AddNearTypes(TypeConverter::Str2Type("car"), TypeConverter::Str2Type("truck"), false); + settings.AddNearTypes(TypeConverter::Str2Type("person"), TypeConverter::Str2Type("bicycle"), true); + settings.AddNearTypes(TypeConverter::Str2Type("person"), TypeConverter::Str2Type("motorbike"), true); + + settings.m_useAbandonedDetection = false; + if (settings.m_useAbandonedDetection) + { + settings.m_minStaticTime = minStaticTime; + settings.m_maxStaticTime = 60; + settings.m_maximumAllowedLostTime = settings.m_minStaticTime; // Maximum allowed lost time + settings.m_maxTraceLength = 2 * settings.m_maximumAllowedLostTime; // Maximum trace length + } + else + { + settings.m_maximumAllowedLostTime = 10.; // Maximum allowed lost time + settings.m_maxTraceLength = 4.; // Maximum trace length + } + + m_tracker = BaseTracker::CreateTracker(settings, m_fps); + + ReadGeobindings(frame.size()); + return true; +} + +/// +/// \brief CarsCounting::DrawData +/// \param frame +/// +void CarsCounting::DrawData(cv::Mat frame, const std::vector& tracks, int framesCounter, int currTime) +{ + m_logger->info("Frame ({1}): tracks = {2}, time = {3}", framesCounter, tracks.size(), currTime); + +#if 1 // Debug output + if (!m_geoParams.Empty()) + { + std::vector points = m_geoParams.GetFramePoints(); + for (size_t i = 0; i < points.size(); ++i) + { + cv::line(frame, points[i % points.size()], points[(i + 1) % points.size()], cv::Scalar(255, 255, 255), 1, cv::LINE_AA); + } + } +#endif + + for (const auto& track : tracks) + { + if (track.m_isStatic) + { + DrawTrack(frame, track, true, framesCounter); + } + else + { + if (track.IsRobust(cvRound(m_fps / 4), // Minimal trajectory size + 0.8f, // Minimal ratio raw_trajectory_points / trajectory_lenght + cv::Size2f(0.1f, 8.0f)) // Min and max ratio: width / height + ) + { + DrawTrack(frame, track, true, framesCounter); + + CheckLinesIntersection(track, static_cast(frame.cols), static_cast(frame.rows)); + } + } + } + //m_detector->CalcMotionMap(frame); + + if (!m_geoParams.Empty()) + { + cv::Mat geoMap = m_geoParams.DrawTracksOnMap(tracks); + if (!geoMap.empty()) + { +#ifndef SILENT_WORK + cv::namedWindow("Geo map", cv::WINDOW_NORMAL); + cv::imshow("Geo map", geoMap); +#endif + if (true) + { + double k = 0.25; + cv::Size mapPreview(cvRound(frame.cols * k), cvRound(((frame.cols * k) / geoMap.cols) * geoMap.rows)); + cv::resize(geoMap, frame(cv::Rect(frame.cols - mapPreview.width - 1, frame.rows - mapPreview.height - 1, mapPreview.width, mapPreview.height)), mapPreview, 0, 0, cv::INTER_CUBIC); + } + } + } + + for (const auto& rl : m_lines) + { + rl.Draw(frame); + } + + cv::Mat heatMap = DrawHeatMap(); +#ifndef SILENT_WORK + if (!heatMap.empty()) + cv::imshow("Heat map", heatMap); +#endif +} + +/// +/// \brief CarsCounting::AddLine +/// \param newLine +/// +void CarsCounting::AddLine(const RoadLine& newLine) +{ + m_lines.push_back(newLine); +} + +/// +/// \brief CarsCounting::GetLine +/// \param lineUid +/// \return +/// +bool CarsCounting::GetLine(unsigned int lineUid, RoadLine& line) +{ + for (const auto& rl : m_lines) + { + if (rl.m_uid == lineUid) + { + line = rl; + return true; + } + } + return false; +} + +/// +/// \brief CarsCounting::RemoveLine +/// \param lineUid +/// \return +/// +bool CarsCounting::RemoveLine(unsigned int lineUid) +{ + for (auto it = std::begin(m_lines); it != std::end(m_lines);) + { + if (it->m_uid == lineUid) + it = m_lines.erase(it); + else + ++it; + } + return false; +} + +/// +/// \brief CarsCounting::CheckLinesIntersection +/// \param track +/// +void CarsCounting::CheckLinesIntersection(const TrackingObject& track, float xMax, float yMax) +{ + auto Pti2f = [&](cv::Point pt) + { + return cv::Point2f(pt.x / xMax, pt.y / yMax); + }; + + constexpr size_t minTrack = 5; + if (track.m_trace.size() >= minTrack) + { + for (auto& rl : m_lines) + { + rl.IsIntersect(track.m_ID, Pti2f(track.m_trace[track.m_trace.size() - minTrack]), Pti2f(track.m_trace[track.m_trace.size() - 1])); + } + } +} + +/// +/// \brief CarsCounting::DrawHeatMap +/// +cv::Mat CarsCounting::DrawHeatMap() +{ + cv::Mat res; + if (!m_heatMap.empty()) + { + cv::normalize(m_heatMap, m_normHeatMap, 255, 0, cv::NORM_MINMAX, CV_8UC1); + cv::applyColorMap(m_normHeatMap, m_colorMap, cv::COLORMAP_HOT); + cv::bitwise_or(m_keyFrame, m_colorMap, res); + } + return res; +} + +/// +/// \brief CarsCounting::AddToHeatMap +/// +void CarsCounting::AddToHeatMap(const cv::Rect& rect) +{ + if (m_heatMap.empty()) + return; + + constexpr float w = 0.001f; + for (int y = 0; y < rect.height; ++y) + { + float* heatPtr = m_heatMap.ptr(rect.y + y) + rect.x; + for (int x = 0; x < rect.width; ++x) + { + heatPtr[x] += w; + } + } +} + +/// +/// \brief CarsCounting::ReadGeobindings +/// +bool CarsCounting::ReadGeobindings(cv::Size frameSize) +{ + bool res = true; + INIReader reader(m_geoBindFile); + + int parseError = reader.ParseError(); + if (parseError < 0) + { + m_logger->critical("GeoBindFile file {} does not exist!", m_geoBindFile); + res = false; + } + else if (parseError > 0) + { + m_logger->critical("GeoBindFile file {0} parse error in line: {1}", m_geoBindFile, parseError); + res = false; + } + if (!res) + return res; + + // Read frame-map bindings + std::vector geoPoints; + std::vector framePoints; + for (size_t i = 0;; ++i) + { + cv::Point2d geoPoint; + std::string lat = "lat" + std::to_string(i); + std::string lon = "lon" + std::to_string(i); + std::string px_x = "px_x" + std::to_string(i); + std::string px_y = "px_y" + std::to_string(i); + if (reader.HasValue("points", lat) && reader.HasValue("points", lon) && reader.HasValue("points", px_x) && reader.HasValue("points", px_y)) + { + geoPoints.emplace_back(reader.GetReal("points", lat, 0), reader.GetReal("points", lon, 0)); + framePoints.emplace_back(cvRound(reader.GetReal("points", px_x, 0) * frameSize.width), cvRound(reader.GetReal("points", px_y, 0) * frameSize.height)); + } + else + { + break; + } + } + res = m_geoParams.SetKeyPoints(framePoints, geoPoints); + + // Read map image + std::string mapFile = reader.GetString("map", "file", ""); + std::vector mapGeoCorners; + mapGeoCorners.emplace_back(reader.GetReal("map", "left_top_lat", 0), reader.GetReal("map", "left_top_lon", 0)); + mapGeoCorners.emplace_back(reader.GetReal("map", "right_top_lat", 0), reader.GetReal("map", "right_top_lon", 0)); + mapGeoCorners.emplace_back(reader.GetReal("map", "right_bottom_lat", 0), reader.GetReal("map", "right_bottom_lon", 0)); + mapGeoCorners.emplace_back(reader.GetReal("map", "left_bottom_lat", 0), reader.GetReal("map", "left_bottom_lon", 0)); + m_geoParams.SetMapParams(mapFile, mapGeoCorners); + + // Read lines + m_logger->info("Read lines:"); + for (size_t i = 0;; ++i) + { + std::string line = "line" + std::to_string(i); + std::string x0 = line + "_x0"; + std::string y0 = line + "_y0"; + std::string x1 = line + "_x1"; + std::string y1 = line + "_y1"; + if (reader.HasValue("lines", x0) && reader.HasValue("lines", y0) && reader.HasValue("lines", x1) && reader.HasValue("lines", y1)) + { + cv::Point2f p0(static_cast(reader.GetReal("lines", x0, 0)), static_cast(reader.GetReal("lines", y0, 0))); + cv::Point2f p1(static_cast(reader.GetReal("lines", x1, 0)), static_cast(reader.GetReal("lines", y1, 0))); + m_logger->info("Line {0}: ({1}, {2}) - ({3}, 4)", i, p0.x, p0.y, p1.x, p1.y); + AddLine(RoadLine(p0, p1, static_cast(i))); + } + else + { + break; + } + } + + return res; +} diff --git a/example/CarsCounting.h b/example/CarsCounting.h new file mode 100644 index 000000000..929672869 --- /dev/null +++ b/example/CarsCounting.h @@ -0,0 +1,485 @@ +#pragma once + +#include +#include "VideoExample.h" + +/// +constexpr double DEG_TO_RAD = 0.017453292519943295769236907684886; +constexpr double EARTH_RADIUS_IN_METERS = 6372797.560856; + +template +T Haversine(const cv::Point_& from, const cv::Point_& to) +{ + constexpr T Deg2Rad = static_cast(DEG_TO_RAD); + + T lat_arc = (from.x - to.x) * Deg2Rad; + T lon_arc = (from.y - to.y) * Deg2Rad; + T lat_h = sin(lat_arc * static_cast(0.5)); + lat_h *= lat_h; + T lon_h = sin(lon_arc * static_cast(0.5)); + lon_h *= lon_h; + T tmp = cos(from.x * Deg2Rad) * cos(to.y * Deg2Rad); + return static_cast(2.0) * asin(sqrt(lat_h + tmp * lon_h)); +} + +/// +template +T DistanceInMeters(const cv::Point_& from, const cv::Point_& to) +{ + constexpr T EarthRadius = static_cast(EARTH_RADIUS_IN_METERS); + return EarthRadius * Haversine(from, to); +} + +// Fix cv::getPerspectiveTransform for double +template +cv::Mat GetPerspectiveTransform(const std::vector>& src, const std::vector>& dst, int solveMethod) +{ + cv::Mat M(3, 3, CV_64F), X(8, 1, CV_64F, M.ptr()); + double a[8][8], b[8]; + cv::Mat A(8, 8, CV_64F, a), B(8, 1, CV_64F, b); + + for (int i = 0; i < 4; ++i) + { + a[i][0] = a[i + 4][3] = src[i].x; + a[i][1] = a[i + 4][4] = src[i].y; + a[i][2] = a[i + 4][5] = 1; + a[i][3] = a[i][4] = a[i][5] = a[i + 4][0] = a[i + 4][1] = a[i + 4][2] = 0; + a[i][6] = -src[i].x * dst[i].x; + a[i][7] = -src[i].y * dst[i].x; + a[i + 4][6] = -src[i].x * dst[i].y; + a[i + 4][7] = -src[i].y * dst[i].y; + b[i] = dst[i].x; + b[i + 4] = dst[i].y; + } + + cv::solve(A, B, X, solveMethod); + M.ptr()[8] = 1.; + + return M; +} + +/// +/// \brief The GeoParams class +/// +template +class GeoParams +{ +public: + /// + GeoParams() = default; + + /// + GeoParams(const std::vector& framePoints, const std::vector>& geoPoints) + { + SetKeyPoints(framePoints, geoPoints); + } + + /// + bool SetKeyPoints(const std::vector& framePoints, const std::vector>& geoPoints) + { + m_framePoints = framePoints; + m_geoPoints = geoPoints; + + assert(m_framePoints.size() == m_geoPoints.size()); + assert(m_framePoints.size() >= 4); + + bool res = true; + + std::vector> tmpPix; + tmpPix.reserve(m_framePoints.size()); + for (const auto& pix : m_framePoints) + { + tmpPix.emplace_back(static_cast(pix.x), static_cast(pix.y)); + } +#if 0 + std::cout << "Coords pairs: "; + for (size_t i = 0; i < tmpPix.size(); ++i) + { + std::cout << tmpPix[i] << " - " << m_geoPoints[i] << "; "; + } + std::cout << std::endl; +#endif + cv::Mat toGeo = GetPerspectiveTransform(tmpPix, m_geoPoints, (int)cv::DECOMP_LU); + cv::Mat toPix = GetPerspectiveTransform(m_geoPoints, tmpPix, (int)cv::DECOMP_LU); + m_toGeo = toGeo; + m_toPix = toPix; + //std::cout << "To Geo: " << m_toGeo << std::endl; + //std::cout << "To Pix: " << m_toPix << std::endl; + + return res; + } + + /// + bool SetMapParams(const std::string& mapfile, const std::vector>& mapGeoCorners) + { + m_map = cv::imread(mapfile); + m_mapGeoCorners.assign(std::begin(mapGeoCorners), std::end(mapGeoCorners)); + + if (!m_map.empty()) + m_p2mParams.Init(m_mapGeoCorners, m_map.cols, m_map.rows); + + return !m_map.empty(); + } + + /// + cv::Point Geo2Pix(const cv::Point_& geo) const + { + cv::Vec g(geo.x, geo.y, 1); + auto p = m_toPix * g; + return cv::Point(cvRound(p[0] / p[2]), cvRound(p[1] / p[2])); + } + + /// + cv::Point_ Pix2Geo(const cv::Point& pix) const + { + cv::Vec p(static_cast(pix.x), static_cast(pix.y), 1); + auto g = m_toGeo * p; + return cv::Point_(g[0] / g[2], g[1] / g[2]); + } + + /// + std::vector GetFramePoints() const + { + return m_framePoints; + } + + /// + bool Empty() const + { + return m_framePoints.size() != m_geoPoints.size() || m_framePoints.size() < 4; + } + + /// + cv::Mat DrawTracksOnMap(const std::vector& tracks) + { + if (m_map.empty()) + return cv::Mat(); + + cv::Mat map = m_map.clone(); + +#if 1 // Debug output + // Draw bindings points + for (size_t i = 0; i < m_geoPoints.size(); ++i) + { + cv::Point_ gp1(m_geoPoints[i % m_geoPoints.size()]); + cv::Point p1(m_p2mParams.Geo2Pix(gp1)); + + cv::Point_ gp2(m_geoPoints[(i + 1) % m_geoPoints.size()]); + cv::Point p2(m_p2mParams.Geo2Pix(gp2)); + + cv::line(map, p1, p2, cv::Scalar(255, 255, 255), 2, cv::LINE_AA); + + //std::cout << p1 << " - " << p2 << std::endl; + + std::stringstream label; + label << std::fixed << std::setw(5) << std::setprecision(5) << "[" << m_geoPoints[i].x << ", " << m_geoPoints[i].y << "]"; + int baseLine = 0; + double fontScale = (map.cols < 1000 && map.rows < 1000) ? 0.5 : 1.; + cv::Size labelSize = cv::getTextSize(label.str(), cv::FONT_HERSHEY_TRIPLEX, fontScale, 1, &baseLine); + cv::putText(map, label.str(), p1, cv::FONT_HERSHEY_TRIPLEX, fontScale, cv::Scalar(255, 255, 255)); + } +#endif + + // Draw tracks + for (const auto& track : tracks) + { + if (track.m_lastRobust) + { + auto geoCenter = Pix2Geo(track.m_rrect.center); + auto center = m_p2mParams.Geo2Pix(geoCenter); + //std::cout << "Convert: " << track.m_rrect.center << " -> " << geoCenter << " -> " << center << std::endl; + if (center.x > 0 && center.x < map.cols && center.y > 0 && center.y < map.rows) + cv::ellipse(map, center, cv::Size(5, 5), 0, 0, 360, cv::Scalar(255, 0, 255), cv::FILLED, 8); + } + } + + return map; + } + +private: + std::vector m_framePoints; + std::vector> m_geoPoints; + + cv::Matx m_toGeo; + cv::Matx m_toPix; + + cv::Mat m_map; + std::vector> m_mapGeoCorners; + + /// + struct Pix2MapParams + { + cv::Matx m_toPix; + + /// + cv::Point Geo2Pix(const cv::Point_& geo) const + { + cv::Vec g(geo.x, geo.y, 1); + auto p = m_toPix * g; + return cv::Point(cvRound(p[0] / p[2]), cvRound(p[1] / p[2])); + } + + /// + void Init(const std::vector>& geoCorners, int mapWidth, int mapHeight) + { + std::vector> frameCorners { cv::Point_(0, 0), cv::Point_(mapWidth, 0), cv::Point_(mapWidth, mapHeight), cv::Point_(0, mapHeight) }; + cv::Mat toPix = GetPerspectiveTransform(geoCorners, frameCorners, (int)cv::DECOMP_LU); + m_toPix = toPix; + } + }; + Pix2MapParams m_p2mParams; +}; + +/// +/// \brief The RoadLine struct +/// +class RoadLine +{ +public: + /// + /// \brief RoadLine + /// + RoadLine() = default; + RoadLine(const cv::Point2f& pt1, const cv::Point2f& pt2, unsigned int uid) + : + m_pt1(pt1), m_pt2(pt2), m_uid(uid) + { + } + + cv::Point2f m_pt1; + cv::Point2f m_pt2; + + unsigned int m_uid = 0; + + int m_intersect1 = 0; + int m_intersect2 = 0; + + /// + /// \brief operator == + /// \param line + /// \return + /// + bool operator==(const RoadLine &line) const + { + return line.m_uid == m_uid; + } + + /// + /// \brief Draw + /// \param frame + /// + void Draw(cv::Mat frame) const + { + auto Ptf2i = [&](cv::Point2f pt) + { + return cv::Point(cvRound(frame.cols * pt.x), cvRound(frame.rows * pt.y)); + }; + + cv::line(frame, Ptf2i(m_pt1), Ptf2i(m_pt2), cv::Scalar(0, 255, 255), 1, cv::LINE_8, 0); + + std::string label = "Line " + std::to_string(m_uid) + ": " + std::to_string(m_intersect1) + "/" + std::to_string(m_intersect2); + int baseLine = 0; + double fontScale = 0.7; + int thickness = 1; + cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, fontScale, 1, &baseLine); + cv::Point pt(Ptf2i(0.5f * (m_pt1 + m_pt2))); + pt.y += labelSize.height; + //pt.x += labelSize.width; + cv::putText(frame, label, pt, cv::FONT_HERSHEY_TRIPLEX, fontScale, cv::Scalar(0, 0, 0), thickness); + } + + /// + /// \brief IsIntersect + /// \param pt1 + /// \param pt2 + /// \return + /// + int IsIntersect(track_id_t objID, cv::Point2f pt1, cv::Point2f pt2) + { + int direction = 0; + + if (m_lastIntersections.find(objID) != m_lastIntersections.end()) + return direction; + + bool isIntersect = CheckIntersection(pt1, pt2); + + if (isIntersect) + { + m_lastIntersections.emplace(objID); + + cv::Point2f pt; + if ((m_pt1.x <= m_pt2.x) && (m_pt1.y > m_pt2.y)) + { + pt.x = (m_pt1.x + m_pt2.x) / 2.f - 0.01f; + pt.y = (m_pt1.y + m_pt1.y) / 2.f - 0.01f; + } + else + { + if ((m_pt1.x <= m_pt2.x) && (m_pt1.y <= m_pt2.y)) + { + pt.x = (m_pt1.x + m_pt2.x) / 2.f + 0.01f; + pt.y = (m_pt1.y + m_pt1.y) / 2.f - 0.01f; + } + else + { + if ((m_pt1.x > m_pt2.x) && (m_pt1.y > m_pt2.y)) + { + pt.x = (m_pt1.x + m_pt2.x) / 2.f - 0.01f; + pt.y = (m_pt1.y + m_pt1.y) / 2.f + 0.01f; + } + else + { + if ((m_pt1.x > m_pt2.x) && (m_pt1.y <= m_pt2.y)) + { + pt.x = (m_pt1.x + m_pt2.x) / 2.f + 0.01f; + pt.y = (m_pt1.y + m_pt1.y) / 2.f + 0.01f; + } + } + } + } + if (CheckIntersection(pt1, pt)) + { + direction = 1; + ++m_intersect1; + } + else + { + direction = 2; + ++m_intersect2; + } + } + + return direction; + } + +private: + + std::unordered_set m_lastIntersections; + + /// + /// \brief CheckIntersection + /// \param pt1 + /// \param pt2 + /// \return + /// + bool CheckIntersection(cv::Point2f pt1, cv::Point2f pt2) const + { + const float eps = 0.00001f; // Epsilon for equal comparing + + // First line equation + float a1 = 0; + float b1 = 0; + bool trivial1 = false; // Is first line is perpendicular with OX + + if (fabs(m_pt1.x - m_pt2.x) < eps) + { + trivial1 = true; + } + else + { + a1 = (m_pt2.y - m_pt1.y) / (m_pt2.x - m_pt1.x); + b1 = (m_pt2.x * m_pt1.y - m_pt1.x * m_pt2.y) / (m_pt2.x - m_pt1.x); + } + + // Second line equation + float a2 = 0; + float b2 = 0; + bool trivial2 = false; // Is second line is perpendicular with OX + + if (fabs(pt1.x - pt2.x) < eps) + { + trivial2 = true; + } + else + { + a2 = (pt2.y - pt1.y) / (pt2.x - pt1.x); + b2 = (pt2.x * pt1.y - pt1.x * pt2.y) / (pt2.x - pt1.x); + } + + // Intersection coords + cv::Point2f intersectPt; + + bool isIntersect = true; + if (trivial1) + { + if (trivial2) + isIntersect = (fabs(m_pt1.x - pt1.x) < eps); + else + intersectPt.x = m_pt1.x; + + intersectPt.y = a2 * intersectPt.x + b2; + } + else + { + if (trivial2) + { + intersectPt.x = pt1.x; + } + else + { + if (fabs(a2 - a1) > eps) + intersectPt.x = (b1 - b2) / (a2 - a1); + else + isIntersect = false; + } + intersectPt.y = a1 * intersectPt.x + b1; + } + + if (isIntersect) + { + auto InRange = [](float val, float minVal, float maxVal) -> bool + { + return (val >= minVal) && (val <= maxVal); + }; + + isIntersect = InRange(intersectPt.x, std::min(m_pt1.x, m_pt2.x) - eps, std::max(m_pt1.x, m_pt2.x) + eps) && + InRange(intersectPt.x, std::min(pt1.x, pt2.x) - eps, std::max(pt1.x, pt2.x) + eps) && + InRange(intersectPt.y, std::min(m_pt1.y, m_pt2.y) - eps, std::max(m_pt1.y, m_pt2.y) + eps) && + InRange(intersectPt.y, std::min(pt1.y, pt2.y) - eps, std::max(pt1.y, pt2.y) + eps); + } + + return isIntersect; + } +}; + +/// +/// \brief The CarsCounting class +/// +class CarsCounting final : public VideoExample +{ +public: + CarsCounting(const cv::CommandLineParser& parser); + + // Lines API + void AddLine(const RoadLine& newLine); + bool GetLine(unsigned int lineUid, RoadLine& line); + bool RemoveLine(unsigned int lineUid); + +private: + + bool m_drawHeatMap = false; + + bool InitDetector(cv::UMat frame) override; + bool InitTracker(cv::UMat frame) override; + + void DrawData(cv::Mat frame, const std::vector& tracks, int framesCounter, int currTime) override; + void DrawTrack(cv::Mat frame, const TrackingObject& track, bool drawTrajectory, int framesCounter, const std::string& userLabel = "") override; + + // Road lines + std::deque m_lines; + void CheckLinesIntersection(const TrackingObject& track, float xMax, float yMax); + + // Binding frame coordinates to geographical coordinates + GeoParams m_geoParams; + std::string m_geoBindFile; + bool ReadGeobindings(cv::Size frameSize); + + // Heat map for visualization long term detections + cv::Mat m_keyFrame; + cv::Mat m_heatMap; + cv::Mat m_normHeatMap; + cv::Mat m_colorMap; + + void AddToHeatMap(const cv::Rect& rect); + cv::Mat DrawHeatMap(); +}; diff --git a/example/FileLogger.h b/example/FileLogger.h new file mode 100644 index 000000000..3b8109c2c --- /dev/null +++ b/example/FileLogger.h @@ -0,0 +1,166 @@ +#pragma once +#include +#include +#include + +#include + +#include "object_types.h" + +/// +/// \brief The ResultsLog class +/// +class ResultsLog +{ +public: + /// + ResultsLog(const std::string& fileName, int writeEachNFrame) + : m_fileName(fileName), m_writeEachNFrame(writeEachNFrame) + { + } + + /// + ~ResultsLog() + { + WriteAll(true); + } + + /// + bool Open() + { + m_resCSV.close(); + if (m_fileName.size() > 5) + { + m_resCSV.open(m_fileName); + return m_resCSV.is_open(); + } + return false; + } + + /// + bool AddTrack(int framesCounter, track_id_t trackID, const cv::Rect& brect, objtype_t type, float confidence) + { + if (m_resCSV.is_open()) + { + auto frame = m_frames.find(framesCounter); + if (frame == std::end(m_frames)) + { + DetectsOnFrame tmpFrame; + tmpFrame.m_detects.emplace_back(trackID, brect, type, confidence); + m_frames.emplace(framesCounter, tmpFrame); + } + else + { + frame->second.m_detects.emplace_back(trackID, brect, type, confidence); + } + return true; + } + return false; + } + + /// + void AddRobustTrack(track_id_t trackID) + { + m_robustIDs.insert(trackID); + } + + /// + void Flush() + { + WriteAll(true); + m_frames.clear(); + } + +private: + std::string m_fileName; + std::ofstream m_resCSV; + + /// + struct Detection + { + cv::Rect m_rect; + objtype_t m_type; + float m_conf = 0.f; + track_id_t m_trackID = 0; + + Detection(track_id_t trackID, const cv::Rect& brect, objtype_t type, float confidence) + { + m_type = type; + m_rect = brect; + m_conf = confidence; + m_trackID = trackID; + } + }; + + /// + struct DetectsOnFrame + { + std::vector m_detects; + }; + std::map m_frames; + std::unordered_set m_robustIDs; + int m_writeEachNFrame = 1; + + /// + void WriteAll(bool byFrames) + { + if (byFrames) + { +#if 1 + char delim = ','; + for (const auto& frame : m_frames) + { + if (frame.first % m_writeEachNFrame == 0) + { + for (const auto& detect : frame.second.m_detects) + { + if (m_robustIDs.find(detect.m_trackID) != std::end(m_robustIDs)) + { + m_resCSV << frame.first << delim << TypeConverter::Type2Str(detect.m_type) << delim << detect.m_rect.x << delim << detect.m_rect.y << delim << + detect.m_rect.width << delim << detect.m_rect.height << delim << + detect.m_conf << delim << std::endl; + } + } + } + } +#else + char delim = ' '; + for (const auto& frame : m_frames) + { + for (const auto& detect : frame.second.m_detects) + { + if (m_robustIDs.find(detect.m_trackID) != std::end(m_robustIDs)) + { + m_resCSV << frame.first << delim << TypeConverter::Type2Str(detect.m_type) << delim << detect.m_rect.x << delim << detect.m_rect.y << delim << + (detect.m_rect.x + detect.m_rect.width) << delim << (detect.m_rect.y + detect.m_rect.height) << delim << + detect.m_conf << delim << detect.m_trackID << std::endl; + } + } + } +#endif + } + else + { + char delim = ','; + for (auto id : m_robustIDs) + { + for (const auto& frame : m_frames) + { + if (frame.first % m_writeEachNFrame == 0) + { + for (const auto& detect : frame.second.m_detects) + { + if (detect.m_trackID == id) + { + m_resCSV << frame.first << delim << id.ID2Str() << delim << detect.m_rect.x << delim << detect.m_rect.y << delim << + detect.m_rect.width << delim << detect.m_rect.height << delim << + detect.m_conf << ",-1,-1,-1," << std::endl; + break; + } + } + } + } + } + } + } +}; diff --git a/example/MotionDetectorExample.h b/example/MotionDetectorExample.h new file mode 100644 index 000000000..7136b7586 --- /dev/null +++ b/example/MotionDetectorExample.h @@ -0,0 +1,228 @@ +#pragma once + +#include +#include +#include + +#include "VideoExample.h" + +#ifdef USE_CLIP +#include "ruclip/ClipAPI.h" +#endif // USE_CLIP + +/// +/// \brief The MotionDetectorExample class +/// +class MotionDetectorExample final : public VideoExample +{ +public: + MotionDetectorExample(const cv::CommandLineParser& parser) + : VideoExample(parser), m_minObjWidth(10) + { +#ifdef USE_CLIP + std::string clipModel = "C:/work/clip/ruclip_/CLIP/data/ruclip-vit-large-patch14-336"; + std::string bpeModel = "C:/work/clip/ruclip_/CLIP/data/ruclip-vit-large-patch14-336/bpe.model"; + m_clip.Init(clipModel, bpeModel, 336, 0, { "pedestrian", "person", "suv", "pickup", "car", "truck", "bus" }); +#endif // USE_CLIP + + m_logger->info("MotionDetectorExample"); + } + +protected: + /// + /// \brief InitDetector + /// \param frame + /// \return + /// + bool InitDetector(cv::UMat frame) override + { + m_logger->info("MotionDetectorExample::InitDetector"); + + //m_minObjWidth = frame.cols / 20; + m_minObjWidth = 4; + + config_t config; + config.emplace("useRotatedRect", "0"); + + tracking::Detectors detectorType = tracking::Detectors::Motion_MOG; + + switch (detectorType) + { + case tracking::Detectors::Motion_VIBE: + config.emplace("samples", "20"); + config.emplace("pixelNeighbor", "1"); + config.emplace("distanceThreshold", "20"); + config.emplace("matchingThreshold", "3"); + config.emplace("updateFactor", "16"); + break; + case tracking::Detectors::Motion_MOG: + config.emplace("history", std::to_string(cvRound(5000 * m_fps))); + config.emplace("nmixtures", "3"); + config.emplace("backgroundRatio", "0.7"); + config.emplace("noiseSigma", "0"); + break; + case tracking::Detectors::Motion_GMG: + config.emplace("initializationFrames", "50"); + config.emplace("decisionThreshold", "0.7"); + break; + case tracking::Detectors::Motion_CNT: + config.emplace("minPixelStability", "15"); + config.emplace("maxPixelStability", std::to_string(cvRound(20 * m_minStaticTime * m_fps))); + config.emplace("useHistory", "1"); + config.emplace("isParallel", "1"); + break; + case tracking::Detectors::Motion_MOG2: + config.emplace("history", std::to_string(cvRound(20 * m_minStaticTime * m_fps))); + config.emplace("varThreshold", "10"); + config.emplace("detectShadows", "1"); + break; + } + m_detector = BaseDetector::CreateDetector(detectorType, config, frame); + + if (m_detector.get()) + { + m_detector->SetMinObjectSize(cv::Size(m_minObjWidth, m_minObjWidth)); + return true; + } + return false; + } + /// + /// \brief InitTracker + /// \param frame + /// \return + /// + bool InitTracker(cv::UMat frame) override + { + m_logger->info("MotionDetectorExample::InitTracker"); + + if (!m_trackerSettingsLoaded) + { + m_trackerSettings.SetDistance(tracking::DistJaccard); + m_trackerSettings.m_kalmanType = tracking::KalmanLinear; + m_trackerSettings.m_filterGoal = tracking::FilterCenter; + m_trackerSettings.m_lostTrackType = tracking::TrackNone; // Use visual objects tracker for collisions resolving. Used if m_filterGoal == tracking::FilterRect + m_trackerSettings.m_matchType = tracking::MatchHungrian; + m_trackerSettings.m_useAcceleration = false; // Use constant acceleration motion model + m_trackerSettings.m_dt = m_trackerSettings.m_useAcceleration ? 0.05f : 0.3f; // Delta time for Kalman filter + m_trackerSettings.m_accelNoiseMag = 0.1f; // Accel noise magnitude for Kalman filter + m_trackerSettings.m_distThres = 0.95f; // Distance threshold between region and object on two frames +#if 1 + m_trackerSettings.m_minAreaRadiusPix = frame.rows / 5.f; +#else + m_trackerSettings.m_minAreaRadiusPix = -1.f; +#endif + m_trackerSettings.m_minAreaRadiusK = 0.8f; + + m_trackerSettings.m_useAbandonedDetection = false; + if (m_trackerSettings.m_useAbandonedDetection) + { + m_trackerSettings.m_minStaticTime = m_minStaticTime; + m_trackerSettings.m_maxStaticTime = 10; + m_trackerSettings.m_maximumAllowedLostTime = m_trackerSettings.m_minStaticTime; // Maximum allowed lost time + m_trackerSettings.m_maxTraceLength = 2 * m_trackerSettings.m_maximumAllowedLostTime; // Maximum trace length + } + else + { + m_trackerSettings.m_maximumAllowedLostTime = 2.; // Maximum allowed lost time + m_trackerSettings.m_maxTraceLength = 2.; // Maximum trace length + } + } + + m_tracker = BaseTracker::CreateTracker(m_trackerSettings, m_fps); + return true; + } + + /// + /// \brief DrawData + /// \param frame + /// \param tracks + /// \param framesCounter + /// \param currTime + /// + void DrawData(cv::Mat frame, const std::vector& tracks, int framesCounter, int currTime) override + { + m_logger->info("Frame ({0}): tracks = {1}, time = {2}", framesCounter, tracks.size(), currTime); + +#ifdef USE_CLIP + std::vector clipResult; + std::vector clipRects; + clipRects.reserve(tracks.size()); + for (const auto& track : tracks) + { + clipRects.emplace_back(track.GetBoundingRect()); + } + m_clip.ProcessFrame(frame, clipRects, clipResult); +#endif // USE_CLIP + + for (size_t i = 0; i < tracks.size(); ++i) + { + const auto& track = tracks[i]; + if (track.m_isStatic) + { +#ifdef USE_CLIP + DrawTrack(frame, track, false, framesCounter, clipResult[i].m_label + ", " + std::to_string(clipResult[i].m_conf)); +#else + DrawTrack(frame, track, false, framesCounter); +#endif //USE_CLIP + std::string label = "abandoned " + track.m_ID.ID2Str(); + int baseLine = 0; + cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_TRIPLEX, 0.5, 1, &baseLine); + + cv::Rect brect = track.m_rrect.boundingRect(); + if (brect.x < 0) + { + brect.width = std::min(brect.width, frame.cols - 1); + brect.x = 0; + } + else if (brect.x + brect.width >= frame.cols) + { + brect.x = std::max(0, frame.cols - brect.width - 1); + brect.width = std::min(brect.width, frame.cols - 1); + } + if (brect.y - labelSize.height < 0) + { + brect.height = std::min(brect.height, frame.rows - 1); + brect.y = labelSize.height; + } + else if (brect.y + brect.height >= frame.rows) + { + brect.y = std::max(0, frame.rows - brect.height - 1); + brect.height = std::min(brect.height, frame.rows - 1); + } + DrawFilledRect(frame, cv::Rect(cv::Point(brect.x, brect.y - labelSize.height), cv::Size(labelSize.width, labelSize.height + baseLine)), cv::Scalar(255, 0, 255), 150); + cv::putText(frame, label, brect.tl(), cv::FONT_HERSHEY_TRIPLEX, 0.5, cv::Scalar(0, 0, 0)); + } + else + { + auto velocity = sqrt(sqr(track.m_velocity[0]) + sqr(track.m_velocity[1])); + if (track.IsRobust(4, // Minimal trajectory size + 0.3f, // Minimal ratio raw_trajectory_points / trajectory_lenght + cv::Size2f(0.2f, 5.0f), // Min and max ratio: width / height + 2)) + //velocity > 30 // Velocity more than 30 pixels per second + { + //track_t mean = 0; + //track_t stddev = 0; + //TrackingObject::LSParams lsParams; + //if (track.LeastSquares2(20, mean, stddev, lsParams) && mean > stddev) + { +#ifdef USE_CLIP + DrawTrack(frame, track, true, framesCounter, clipResult[i].m_label + ", " + std::to_string(clipResult[i].m_conf)); +#else + DrawTrack(frame, track, true, framesCounter); +#endif //USE_CLIP + } + } + } + } + m_detector->CalcMotionMap(frame); + } + +private: + int m_minObjWidth = 8; + int m_minStaticTime = 5; + +#ifdef USE_CLIP + ClassificationCLIP m_clip; +#endif // USE_CLIP +}; diff --git a/example/MouseExample.h b/example/MouseExample.h new file mode 100644 index 000000000..e58fd5a1e --- /dev/null +++ b/example/MouseExample.h @@ -0,0 +1,127 @@ +#pragma once + +#include "BaseTracker.h" + +#include +#include + +//------------------------------------------------------------------------ +// Mouse callbacks +//------------------------------------------------------------------------ +void mv_MouseCallback(int event, int x, int y, int /*flags*/, void* param) +{ + if (event == cv::EVENT_MOUSEMOVE) + { + cv::Point2f* p = (cv::Point2f*)param; + if (p) + { + p->x = static_cast(x); + p->y = static_cast(y); + } + } +} + +// ---------------------------------------------------------------------- +void MouseTracking(cv::CommandLineParser parser) +{ + std::string outFile = parser.get("out"); + + cv::VideoWriter writer; + + int k = 0; + std::vector colors = { cv::Scalar(255, 0, 0), cv::Scalar(0, 255, 0), cv::Scalar(0, 0, 255), cv::Scalar(255, 255, 0), cv::Scalar(0, 255, 255), cv::Scalar(255, 0, 255), cv::Scalar(255, 127, 255), cv::Scalar(127, 0, 255), cv::Scalar(127, 0, 127) }; + +#ifndef SILENT_WORK + cv::namedWindow("Video"); +#endif + + cv::Mat frame = cv::Mat(800, 800, CV_8UC3); + + if (!outFile.empty() && !writer.isOpened()) + writer.open(outFile, cv::VideoWriter::fourcc('h', '2', '6', '4'), 20, frame.size(), true); + + // Set mouse callback + cv::Point2f pointXY; + cv::setMouseCallback("Video", mv_MouseCallback, (void*)&pointXY); + + TrackerSettings settings; + settings.SetDistance(tracking::DistCenters); + settings.m_kalmanType = tracking::KalmanLinear; + settings.m_filterGoal = tracking::FilterCenter; + settings.m_lostTrackType = tracking::TrackNone; + settings.m_matchType = tracking::MatchHungrian; + settings.m_dt = 0.2f; + settings.m_accelNoiseMag = 0.5f; + settings.m_distThres = 0.8f; + settings.m_minAreaRadiusPix = frame.rows / 20.f; + settings.m_maximumAllowedLostTime = 1.; + settings.m_maxTraceLength = 1.; + + std::unique_ptr tracker = BaseTracker::CreateTracker(settings, 30.f); + + track_t alpha = 0; + cv::RNG rng; + while (k != 27) + { + frame = cv::Scalar::all(0); + + // Noise addition (measurements/detections simulation ) + float Xmeasured = pointXY.x + static_cast(rng.gaussian(2.0)); + float Ymeasured = pointXY.y + static_cast(rng.gaussian(2.0)); + + // Append circulating around mouse cv::Points (frequently intersecting) + std::vector pts; + pts.emplace_back(Xmeasured + 100.0f*sin(-alpha), Ymeasured + 100.0f*cos(-alpha)); + pts.emplace_back(Xmeasured + 100.0f*sin(alpha), Ymeasured + 100.0f*cos(alpha)); + pts.emplace_back(Xmeasured + 100.0f*sin(alpha / 2.0f), Ymeasured + 100.0f*cos(alpha / 2.0f)); + pts.emplace_back(Xmeasured + 100.0f*sin(alpha / 3.0f), Ymeasured + 100.0f*cos(alpha / 1.0f)); + alpha += 0.05f; + + regions_t regions; + for (auto p : pts) + { + regions.push_back(CRegion(cv::Rect(cvRound(p.x), cvRound(p.y), 1, 1))); + } + + for (size_t i = 0; i < pts.size(); i++) + { +#if (CV_VERSION_MAJOR >= 4) + cv::circle(frame, pts[i], 3, cv::Scalar(0, 255, 0), 1, cv::LINE_AA); +#else + cv::circle(frame, pts[i], 3, cv::Scalar(0, 255, 0), 1, CV_AA); +#endif + } + + tracker->Update(regions, cv::UMat(), std::chrono::system_clock::now()); + + std::vector tracks; + tracker->GetTracks(tracks); + std::cout << tracks.size() << std::endl; + + for (size_t i = 0; i < tracks.size(); i++) + { + const auto& track = tracks[i]; + + if (track.m_trace.size() > 1) + { + for (size_t j = 0; j < track.m_trace.size() - 1; j++) + { +#if (CV_VERSION_MAJOR >= 4) + cv::line(frame, track.m_trace[j], track.m_trace[j + 1], colors[i % colors.size()], 2, cv::LINE_AA); +#else + cv::line(frame, track.m_trace[j], track.m_trace[j + 1], colors[i % colors.size()], 2, CV_AA); +#endif + } + } + } + + if (writer.isOpened()) + writer << frame; + +#ifndef SILENT_WORK + cv::imshow("Video", frame); + + k = cv::waitKey(10); +#endif + } +} diff --git a/example/VideoExample.cpp b/example/VideoExample.cpp new file mode 100644 index 000000000..4c41e0032 --- /dev/null +++ b/example/VideoExample.cpp @@ -0,0 +1,731 @@ +#include +#include + +#include "VideoExample.h" + +/// +/// \brief VideoExample::VideoExample +/// \param parser +/// +VideoExample::VideoExample(const cv::CommandLineParser& parser) + : m_resultsLog(parser.get("log_res"), parser.get("write_n_frame")), + m_cvatAnnotationsGenerator(parser.get("cvat_res")) +{ + m_inFile = parser.get(0); + m_outFile = parser.get("out"); + m_showLogsLevel = parser.get("show_logs"); + m_startFrame = parser.get("start_frame"); + m_endFrame = parser.get("end_frame"); + m_finishDelay = parser.get("end_delay"); + m_batchSize = std::max(1, parser.get("batch_size")); + m_useContrastAdjustment = parser.get("contrast_adjustment") != 0; + + m_colors.emplace_back(255, 0, 0); + m_colors.emplace_back(0, 255, 0); + m_colors.emplace_back(0, 0, 255); + m_colors.emplace_back(255, 255, 0); + m_colors.emplace_back(0, 255, 255); + m_colors.emplace_back(255, 0, 255); + m_colors.emplace_back(255, 127, 255); + m_colors.emplace_back(127, 0, 255); + m_colors.emplace_back(127, 0, 127); + + m_resultsLog.Open(); + + // Create loggers + m_consoleSink = std::make_shared(); + m_consoleSink->set_level(spdlog::level::from_str(m_showLogsLevel)); + m_consoleSink->set_pattern("[%^%l%$] %v"); + + auto currentTime = std::chrono::system_clock::now(); + auto transformed = currentTime.time_since_epoch().count() / 1000000; + std::time_t tt = std::chrono::system_clock::to_time_t(currentTime); + char buffer[80]; +#ifdef WIN32 + tm timeInfo; + localtime_s(&timeInfo, &tt); + strftime(buffer, 80, "%G%m%d_%H%M%S", &timeInfo); +#else + auto timeInfo = localtime(&tt); + strftime(buffer, 80, "%G%m%d_%H%M%S", timeInfo); +#endif + + size_t max_size = 1024 * 1024 * 5; + size_t max_files = 3; + m_fileSink = std::make_shared("logs/" + std::string(buffer) + std::to_string(transformed % 1000) + ".txt", max_size, max_files); + m_fileSink->set_level(spdlog::level::from_str(m_showLogsLevel)); + + m_logger = std::shared_ptr(new spdlog::logger("traffic", { m_consoleSink, m_fileSink })); + m_logger->set_level(spdlog::level::from_str(m_showLogsLevel)); + m_logger->info("Start service"); + + std::string settingsFile = parser.get("settings"); + m_trackerSettingsLoaded = ParseTrackerSettings(settingsFile, m_trackerSettings); + + if (m_batchSize > 1) + { + m_frameInfo[0].SetBatchSize(m_batchSize); + m_frameInfo[1].SetBatchSize(m_batchSize); + } + for (auto& fr : m_frameInfo[0].m_frames) + { + fr.SetUseAdjust(m_useContrastAdjustment); + } + for (auto& fr : m_frameInfo[1].m_frames) + { + fr.SetUseAdjust(m_useContrastAdjustment); + } + + m_startTimeStamp = currentTime; +} + +/// +/// \brief VideoExample::SyncProcess +/// +void VideoExample::SyncProcess() +{ + cv::VideoWriter writer; + +#ifndef SILENT_WORK + cv::namedWindow("Video", cv::WINDOW_NORMAL | cv::WINDOW_KEEPRATIO); + bool manualMode = false; +#endif + + double freq = cv::getTickFrequency(); + int64 allTime = 0; + + int framesCounter = m_startFrame + 1; + + cv::VideoCapture capture; + if (!OpenCapture(capture)) + { + m_logger->critical("Can't open {}", m_inFile); + return; + } + +#if 0 + // Write preview + cv::Mat prFrame; + capture >> prFrame; + cv::Mat textFrame(prFrame.size(), CV_8UC3); + textFrame = cv::Scalar(0, 0, 0); + std::string label{ "Original video" }; + int baseLine = 0; + double fontScale = (textFrame.cols < 1920) ? 2.0 : 3.0; + int thickness = 2; + int lineType = cv::LINE_AA; + int fontFace = cv::FONT_HERSHEY_TRIPLEX; + cv::Size labelSize = cv::getTextSize(label, fontFace, fontScale, thickness, &baseLine); + cv::putText(textFrame, label, cv::Point(textFrame.cols / 2 - labelSize.width / 2, textFrame.rows / 2 - labelSize.height / 2), fontFace, fontScale, cv::Scalar(255, 255, 255), thickness, lineType); + for (size_t fi = 0; fi < cvRound(2 * m_fps); ++fi) + { + WriteFrame(writer, textFrame); + } + WriteFrame(writer, prFrame); + for (;;) + { + capture >> prFrame; + if (prFrame.empty()) + break; + WriteFrame(writer, prFrame); + } + textFrame = cv::Scalar(0, 0, 0); + label = "Detection result"; + labelSize = cv::getTextSize(label, fontFace, fontScale, thickness, &baseLine); + cv::putText(textFrame, label, cv::Point(textFrame.cols / 2 - labelSize.width / 2, textFrame.rows / 2 - labelSize.height / 2), fontFace, fontScale, cv::Scalar(255, 255, 255), thickness, lineType); + for (size_t fi = 0; fi < cvRound(2 * m_fps); ++fi) + { + WriteFrame(writer, textFrame); + } + capture.release(); + OpenCapture(capture); +#endif + + FrameInfo frameInfo(m_batchSize); + frameInfo.m_frames.resize(frameInfo.m_batchSize); + frameInfo.m_frameInds.resize(frameInfo.m_batchSize); + frameInfo.m_frameTimeStamps.resize(frameInfo.m_batchSize); + + for (auto& fr : frameInfo.m_frames) + { + fr.SetUseAdjust(m_useContrastAdjustment); + } + + int64 startLoopTime = cv::getTickCount(); + + //double fps = capture.get(cv::CAP_PROP_FPS); + //double readPeriodSeconds = 2.; + //int readPeriodFrames = cvRound(readPeriodSeconds * fps); + + for (;;) + { + //int currFramesPos = cvRound(capture.get(cv::CAP_PROP_POS_FRAMES)); + + size_t i = 0; + for (; i < m_batchSize; ++i) + { + capture >> frameInfo.m_frames[i].GetMatBGRWrite(); + if (frameInfo.m_frames[i].empty()) + break; + frameInfo.m_frameInds[i] = framesCounter; + frameInfo.m_frameTimeStamps[i] = GetNextTimeStamp(framesCounter); + frameInfo.m_frames[i].AdjustMatBGR(); + + ++framesCounter; + if (m_endFrame && framesCounter > m_endFrame) + { + m_logger->info("Process: riched last {} frame", m_endFrame); + break; + } + + m_logger->debug("VideoExample::SyncProcess: Capture {0} frame", framesCounter); + } + if (i < m_batchSize) + break; + + //capture.set(cv::CAP_PROP_POS_FRAMES, currFramesPos + readPeriodFrames); + + if (!m_isDetectorInitialized || !m_isTrackerInitialized) + { + cv::UMat ufirst = frameInfo.m_frames[0].GetUMatBGR(); + if (!m_isDetectorInitialized) + { + m_isDetectorInitialized = InitDetector(ufirst); + if (!m_isDetectorInitialized) + { + m_logger->critical("CaptureAndDetect: Detector initialize error!!!"); + break; + } + } + if (!m_isTrackerInitialized) + { + m_isTrackerInitialized = InitTracker(ufirst); + if (!m_isTrackerInitialized) + { + m_logger->critical("CaptureAndDetect: Tracker initialize error!!!"); + break; + } + } + } + + int64 t1 = cv::getTickCount(); + + Detection(frameInfo); + Tracking(frameInfo); + int64 t2 = cv::getTickCount(); + + allTime += t2 - t1; + int currTime = cvRound(1000 * (t2 - t1) / freq); + + for (i = 0; i < m_batchSize; ++i) + { + DrawData(frameInfo.m_frames[i].GetMatBGR(), frameInfo.m_tracks[i], frameInfo.m_frameInds[i], currTime); + +#ifndef SILENT_WORK + cv::imshow("Video", frameInfo.m_frames[i].GetMatBGR()); + + int waitTime = manualMode ? 0 : 1;// std::max(1, cvRound(1000 / m_fps - currTime)); + int k = cv::waitKey(waitTime); + if (k == 27) + break; + else if (k == 'm' || k == 'M') + manualMode = !manualMode; +#else + //std::this_thread::sleep_for(std::chrono::milliseconds(1)); +#endif + + WriteFrame(writer, frameInfo.m_frames[i].GetMatBGR()); + } + if (framesCounter % 100 == 0) + m_resultsLog.Flush(); + } + + m_cvatAnnotationsGenerator.Save(m_inFile, m_framesCount, m_frameSize); + + int64 stopLoopTime = cv::getTickCount(); + + m_logger->info("algorithms time = {0}, work time = {1}", allTime / freq, (stopLoopTime - startLoopTime) / freq); +#ifndef SILENT_WORK + cv::waitKey(m_finishDelay); +#endif +} + +/// +/// \brief VideoExample::AsyncProcess +/// +void VideoExample::AsyncProcess() +{ + std::atomic stopCapture(false); + + std::thread thCapDet(CaptureAndDetect, this, std::ref(stopCapture)); + + cv::VideoWriter writer; + +#ifndef SILENT_WORK + cv::namedWindow("Video", cv::WINDOW_NORMAL | cv::WINDOW_KEEPRATIO); + bool manualMode = false; +#endif + + double freq = cv::getTickFrequency(); + + int64 allTime = 0; + int64 startLoopTime = cv::getTickCount(); + size_t processCounter = 0; + for (; !stopCapture.load(); ) + { + FrameInfo& frameInfo = m_frameInfo[processCounter % 2]; + m_logger->debug("--- waiting tracking from {0} ind = {1}", processCounter % 2, processCounter); + { + std::unique_lock lock(frameInfo.m_mutex); + if (!frameInfo.m_cond.wait_for(lock, std::chrono::milliseconds(m_captureTimeOut), [&frameInfo] { return frameInfo.m_captured.load(); })) + { + m_logger->info("--- Wait frame timeout!"); + break; + } + } + m_logger->debug("--- tracking from {} in progress...", processCounter % 2); + if (!m_isTrackerInitialized) + { + m_isTrackerInitialized = InitTracker(frameInfo.m_frames[0].GetUMatBGR()); + if (!m_isTrackerInitialized) + { + m_logger->critical("--- AsyncProcess: Tracker initialize error!!!"); + frameInfo.m_cond.notify_one(); + break; + } + } + + int64 t1 = cv::getTickCount(); + + Tracking(frameInfo); + + int64 t2 = cv::getTickCount(); + + allTime += t2 - t1 + frameInfo.m_dt; + int currTime = cvRound(1000 * (t2 - t1 + frameInfo.m_dt) / freq); + + m_logger->debug("--- Frame {0}: td = {1}, tt = {2}", frameInfo.m_frameInds[0], 1000 * frameInfo.m_dt / freq, 1000 * (t2 - t1) / freq); + + int key = 0; + for (size_t i = 0; i < m_batchSize; ++i) + { + DrawData(frameInfo.m_frames[i].GetMatBGR(), frameInfo.m_tracks[i], frameInfo.m_frameInds[i], currTime); + + WriteFrame(writer, frameInfo.m_frames[i].GetMatBGR()); + +#ifndef SILENT_WORK + cv::imshow("Video", frameInfo.m_frames[i].GetMatBGR()); + + int waitTime = manualMode ? 0 : 1;// std::max(1, cvRound(1000 / m_fps - currTime)); + key = cv::waitKey(waitTime); + if (key == 'm' || key == 'M') + manualMode = !manualMode; + else + break; +#else + //std::this_thread::sleep_for(std::chrono::milliseconds(1)); +#endif + } + + { + std::unique_lock lock(frameInfo.m_mutex); + m_logger->debug("--- tracking m_captured {0} - captured still {1}", processCounter % 2, frameInfo.m_captured.load()); + assert(frameInfo.m_captured.load()); + frameInfo.m_captured = false; + } + frameInfo.m_cond.notify_one(); + + if (key == 27) + break; + + ++processCounter; + + if (processCounter % 100 == 0) + m_resultsLog.Flush(); + } + stopCapture = true; + + if (thCapDet.joinable()) + thCapDet.join(); + + m_cvatAnnotationsGenerator.Save(m_inFile, m_framesCount, m_frameSize); + + int64 stopLoopTime = cv::getTickCount(); + + m_logger->info("--- algorithms time = {0}, work time = {1}", allTime / freq, (stopLoopTime - startLoopTime) / freq); + +#ifndef SILENT_WORK + cv::waitKey(m_finishDelay); +#endif +} + +/// +/// \brief VideoExample::CaptureAndDetect +/// \param thisPtr +/// \param stopCapture +/// +void VideoExample::CaptureAndDetect(VideoExample* thisPtr, std::atomic& stopCapture) +{ + cv::VideoCapture capture; + if (!thisPtr->OpenCapture(capture)) + { + thisPtr->m_logger->critical("+++ Can't open {}", thisPtr->m_inFile); + stopCapture = true; + return; + } + + int framesCounter = 0; + + const auto localEndFrame = thisPtr->m_endFrame; + auto localIsDetectorInitialized = thisPtr->m_isDetectorInitialized; + auto localTrackingTimeOut = thisPtr->m_trackingTimeOut; + size_t processCounter = 0; + for (; !stopCapture.load();) + { + FrameInfo& frameInfo = thisPtr->m_frameInfo[processCounter % 2]; + thisPtr->m_logger->debug("+++ waiting capture to {0}, ind = {1}", processCounter % 2, processCounter); + { + std::unique_lock lock(frameInfo.m_mutex); + if (!frameInfo.m_cond.wait_for(lock, std::chrono::milliseconds(localTrackingTimeOut), [&frameInfo] { return !frameInfo.m_captured.load(); })) + { + thisPtr->m_logger->info("+++ Wait tracking timeout!"); + frameInfo.m_cond.notify_one(); + break; + } + } + thisPtr->m_logger->debug("+++ capture to {0} in progress...", processCounter % 2); + if (frameInfo.m_frames.size() < frameInfo.m_batchSize) + { + frameInfo.m_frames.resize(frameInfo.m_batchSize); + frameInfo.m_frameInds.resize(frameInfo.m_batchSize); + frameInfo.m_frameTimeStamps.resize(frameInfo.m_batchSize); + } + + cv::Mat frame; + size_t i = 0; + for (; i < frameInfo.m_batchSize; ++i) + { + capture >> frame; + if (frame.empty()) + { + thisPtr->m_logger->error("+++ CaptureAndDetect: frame is empty!"); + frameInfo.m_cond.notify_one(); + break; + } + frameInfo.m_frames[i].GetMatBGRWrite() = frame; + frameInfo.m_frames[i].AdjustMatBGR(); + frameInfo.m_frameInds[i] = framesCounter; + frameInfo.m_frameTimeStamps[i] = thisPtr->GetNextTimeStamp(framesCounter); + ++framesCounter; + + if (localEndFrame && framesCounter > localEndFrame) + { + thisPtr->m_logger->info("+++ Process: riched last {} frame", localEndFrame); + break; + } + } + if (i < frameInfo.m_batchSize) + break; + + if (!localIsDetectorInitialized) + { + thisPtr->m_isDetectorInitialized = thisPtr->InitDetector(frameInfo.m_frames[0].GetUMatBGR()); + localIsDetectorInitialized = thisPtr->m_isDetectorInitialized; + if (!thisPtr->m_isDetectorInitialized) + { + thisPtr->m_logger->critical("+++ CaptureAndDetect: Detector initialize error!!!"); + frameInfo.m_cond.notify_one(); + break; + } + } + + int64 t1 = cv::getTickCount(); + thisPtr->Detection(frameInfo); + int64 t2 = cv::getTickCount(); + frameInfo.m_dt = t2 - t1; + + { + std::unique_lock lock(frameInfo.m_mutex); + thisPtr->m_logger->debug("+++ capture m_captured {0} - captured still {1}", processCounter % 2, frameInfo.m_captured.load()); + assert(!frameInfo.m_captured.load()); + frameInfo.m_captured = true; + } + frameInfo.m_cond.notify_one(); + + ++processCounter; + } + stopCapture = true; +} + +/// +/// \brief VideoExample::GetNextTimeStamp +/// \param framesCounter +/// \return +/// +time_point_t VideoExample::GetNextTimeStamp(int framesCounter) const +{ + if (m_useArchieveTime) + return m_startTimeStamp + std::chrono::milliseconds(cvRound(framesCounter * (1000.f / m_fps))); + else + return std::chrono::system_clock::now(); +} + +/// +/// \brief VideoExample::Detection +/// \param frame +/// \param regions +/// +void VideoExample::Detection(FrameInfo& frame) +{ + if (m_trackerSettings.m_useAbandonedDetection) + { + for (const auto& track : m_tracks) + { + if (track.m_isStatic) + m_detector->ResetModel(frame.m_frames[0].GetUMatBGR(), track.m_rrect.boundingRect()); + } + } + + std::vector frames; + for (size_t i = 0; i < frame.m_frames.size(); ++i) + { + if (m_detector->CanGrayProcessing()) + frames.emplace_back(frame.m_frames[i].GetUMatGray()); + else + frames.emplace_back(frame.m_frames[i].GetUMatBGR()); + } + frame.CleanRegions(); + m_detector->Detect(frames, frame.m_regions); +} + +/// +/// \brief VideoExample::Tracking +/// \param frame +/// \param regions +/// +void VideoExample::Tracking(FrameInfo& frame) +{ + assert(frame.m_regions.size() == frame.m_frames.size()); + + frame.CleanTracks(); + for (size_t i = 0; i < frame.m_frames.size(); ++i) + { + m_tracker->Update(frame.m_regions[i], frame.m_frames[i].GetUMatBGR(), frame.m_frameTimeStamps[i]); + m_tracker->GetTracks(frame.m_tracks[i]); + + m_cvatAnnotationsGenerator.NewDetects(frame.m_frameInds[i], frame.m_tracks[i], 0); + } + if (m_trackerSettings.m_useAbandonedDetection) + m_tracker->GetTracks(m_tracks); +} + +/// +/// \brief VideoExample::DrawTrack +/// \param frame +/// \param track +/// \param drawTrajectory +/// +void VideoExample::DrawTrack(cv::Mat frame, + const TrackingObject& track, + bool drawTrajectory, + int framesCounter, + const std::string& userLabel) +{ + cv::Scalar color = track.m_isStatic ? cv::Scalar(255, 0, 255) : cv::Scalar(0, 255, 0); + cv::Point2f rectPoints[4]; + track.m_rrect.points(rectPoints); + //std::cout << "track: rrect [" << track.m_rrect.size << " from " << track.m_rrect.center << ", " << track.m_rrect.angle << "]" << std::endl; + for (int i = 0; i < 4; ++i) + { + cv::line(frame, rectPoints[i], rectPoints[(i+1) % 4], color); + } + +#if 0 +#if 0 + track_t minAreaRadiusPix = frame.rows / 20.f; +#else + track_t minAreaRadiusPix = -1.f; +#endif + track_t minAreaRadiusK = 0.5f; + cv::Size_ minRadius(minAreaRadiusPix, minAreaRadiusPix); + if (minAreaRadiusPix < 0) + { + minRadius.width = minAreaRadiusK * track.m_rrect.size.width; + minRadius.height = minAreaRadiusK * track.m_rrect.size.height; + } + + Point_t d(3.f * track.m_velocity[0], 3.f * track.m_velocity[1]); + cv::Size2f els(std::max(minRadius.width, fabs(d.x)), std::max(minRadius.height, fabs(d.y))); + Point_t p1 = track.m_rrect.center; + Point_t p2(p1.x + d.x, p1.y + d.y); + float angle = 0; + Point_t nc = p1; + Point_t p2_(p2.x - p1.x, p2.y - p1.y); + if (fabs(p2_.x - p2_.y) > 5) // pix + { + if (fabs(p2_.x) > 0.0001f) + { + track_t l = std::min(els.width, els.height) / 3; + + track_t p2_l = sqrt(sqr(p2_.x) + sqr(p2_.y)); + nc.x = l * p2_.x / p2_l + p1.x; + nc.y = l * p2_.y / p2_l + p1.y; + + angle = atan(p2_.y / p2_.x); + } + else + { + nc.y += d.y / 3; + angle = CV_PI / 2.f; + } + } + + cv::RotatedRect rr(nc, els, 180.f * angle / CV_PI); + cv::ellipse(frame, rr, cv::Scalar(100, 0, 100), 1); +#endif + if (drawTrajectory) + { + cv::Scalar cl = m_colors[track.m_ID.ID2Module(m_colors.size())]; + + for (size_t j = 0; j < track.m_trace.size() - 1; ++j) + { + const TrajectoryPoint& pt1 = track.m_trace.at(j); + const TrajectoryPoint& pt2 = track.m_trace.at(j + 1); +#if (CV_VERSION_MAJOR >= 4) + cv::line(frame, pt1.m_prediction, pt2.m_prediction, cl, 1, cv::LINE_AA); +#else + cv::line(frame, pt1.m_prediction, pt2.m_prediction, cl, 1, CV_AA); +#endif + if (!pt2.m_hasRaw) + { +#if (CV_VERSION_MAJOR >= 4) + cv::circle(frame, pt2.m_prediction, 4, cl, 1, cv::LINE_AA); +#else + cv::circle(frame, pt2.m_prediction, 4, cl, 1, CV_AA); +#endif + } + } + } + + cv::Rect brect = track.m_rrect.boundingRect(); + std::stringstream label; + label << track.m_ID.ID2Str(); + if (track.m_type != bad_type) + label << ": " << TypeConverter::Type2Str(track.m_type); + else if (!userLabel.empty()) + label << ": " << userLabel; + if (track.m_confidence > 0) + label << ", " << std::fixed << std::setw(2) << std::setprecision(2) << track.m_confidence; +#if 0 + track_t mean = 0; + track_t stddev = 0; + TrackingObject::LSParams lsParams; + if (track.LeastSquares2(10, mean, stddev, lsParams)) + { + std::cout << "LSParams: " << lsParams << std::endl; + cv::Scalar cl(255, 0, 255); + label += ", [" + std::to_string(cvRound(mean)) + ", " + std::to_string(cvRound(stddev)) + "]"; + for (size_t j = 0; j < track.m_trace.size() - 1; ++j) + { + track_t t1 = j; + track_t t2 = j + 1; + cv::Point pt1(lsParams.m_ax * sqr(t1) + lsParams.m_v0x * t1 + lsParams.m_x0, lsParams.m_ay * sqr(t1) + lsParams.m_v0y * t1 + lsParams.m_y0); + cv::Point pt2(lsParams.m_ax * sqr(t2) + lsParams.m_v0x * t2 + lsParams.m_x0, lsParams.m_ay * sqr(t2) + lsParams.m_v0y * t2 + lsParams.m_y0); + //std::cout << pt1 << " - " << pt2 << std::endl; +#if (CV_VERSION_MAJOR >= 4) + cv::line(frame, pt1, pt2, cl, 1, cv::LINE_AA); +#else + cv::line(frame, pt1, pt2, cl, 1, CV_AA); +#endif + } + } + label += ", " + std::to_string(cvRound(sqrt(sqr(track.m_velocity[0]) + sqr(track.m_velocity[1])))); +#endif + int baseLine = 0; + double fontScale = (frame.cols < 1920) ? 0.5 : 0.7; + cv::Size labelSize = cv::getTextSize(label.str(), cv::FONT_HERSHEY_TRIPLEX, fontScale, 1, &baseLine); + if (brect.x < 0) + { + brect.width = std::min(brect.width, frame.cols - 1); + brect.x = 0; + } + else if (brect.x + brect.width >= frame.cols) + { + brect.x = std::max(0, frame.cols - brect.width - 1); + brect.width = std::min(brect.width, frame.cols - 1); + } + if (brect.y - labelSize.height < 0) + { + brect.height = std::min(brect.height, frame.rows - 1); + brect.y = labelSize.height; + } + else if (brect.y + brect.height >= frame.rows) + { + brect.y = std::max(0, frame.rows - brect.height - 1); + brect.height = std::min(brect.height, frame.rows - 1); + } + DrawFilledRect(frame, cv::Rect(cv::Point(brect.x, brect.y - labelSize.height), cv::Size(labelSize.width, labelSize.height + baseLine)), cv::Scalar(200, 200, 200), 150); + cv::putText(frame, label.str(), brect.tl(), cv::FONT_HERSHEY_TRIPLEX, fontScale, cv::Scalar(0, 0, 0)); + + m_resultsLog.AddTrack(framesCounter, track.m_ID, brect, track.m_type, track.m_confidence); + m_resultsLog.AddRobustTrack(track.m_ID); +} + +/// +/// \brief VideoExample::OpenCapture +/// \param capture +/// \return +/// +bool VideoExample::OpenCapture(cv::VideoCapture& capture) +{ + if (m_inFile.size() == 1) + { +#ifdef _WIN32 + capture.open(atoi(m_inFile.c_str()), cv::CAP_DSHOW); +#else + capture.open(atoi(m_inFile.c_str())); +#endif + //if (capture.isOpened()) + // capture.set(cv::CAP_PROP_SETTINGS, 1); + } + else + capture.open(m_inFile); + + if (capture.isOpened()) + { + capture.set(cv::CAP_PROP_POS_FRAMES, m_startFrame); + + m_fps = std::max(1.f, (float)capture.get(cv::CAP_PROP_FPS)); + + m_frameSize.width = cvRound(capture.get(cv::CAP_PROP_FRAME_WIDTH)); + m_frameSize.height = cvRound(capture.get(cv::CAP_PROP_FRAME_HEIGHT)); + m_framesCount = cvRound(capture.get(cv::CAP_PROP_FRAME_COUNT)); + + std::cout << "Video " << m_inFile << " was started from " << m_startFrame << " frame with " << m_fps << " fps, frame size " << m_frameSize << " and length " << m_framesCount << std::endl; + + return true; + } + return false; +} + +/// +/// \brief VideoExample::WriteFrame +/// \param writer +/// \param frame +/// \return +/// +bool VideoExample::WriteFrame(cv::VideoWriter& writer, const cv::Mat& frame) +{ + if (!m_outFile.empty()) + { + if (!writer.isOpened()) + writer.open(m_outFile, m_fourcc, m_fps, frame.size(), true); + + if (writer.isOpened()) + { + writer << frame; + return true; + } + } + return false; +} diff --git a/example/VideoExample.h b/example/VideoExample.h new file mode 100644 index 000000000..b5e0fc693 --- /dev/null +++ b/example/VideoExample.h @@ -0,0 +1,302 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "BaseDetector.h" +#include "BaseTracker.h" +#include "FileLogger.h" +#include "cvatAnnotationsGenerator.h" + +#include "spdlog/spdlog.h" +#include "spdlog/async.h" +#include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/sinks/basic_file_sink.h" +#include "spdlog/sinks/rotating_file_sink.h" + +/// +/// \brief The Frame struct +/// +class Frame +{ +public: + Frame() = default; + Frame(cv::Mat imgBGR, bool useCLAHE) + { + m_mBGR = imgBGR; + if (useCLAHE) + { + m_clahe = cv::createCLAHE(1.2, cv::Size(4, 4)); + AdjustMatBGR(); + } + } + + /// + void SetUseAdjust(bool useCLAHE) + { + if (useCLAHE) + { + m_clahe = cv::createCLAHE(1.2, cv::Size(4, 4)); + AdjustMatBGR(); + } + else + { + m_clahe.reset(); + } + } + + /// + bool empty() const noexcept + { + return m_mBGR.empty(); + } + + /// + const cv::Mat& GetMatBGR() const noexcept + { + return m_mBGR; + } + /// + cv::Mat& GetMatBGRWrite() + { + m_umBGRGenerated = false; + m_mGrayGenerated = false; + m_umGrayGenerated = false; + return m_mBGR; + } + /// + bool AdjustMatBGR() + { + if (m_mBGR.empty() || m_clahe.empty()) + return false; + + cv::cvtColor(m_mBGR, m_mHSV, cv::COLOR_BGR2HSV); + cv::split(m_mHSV, m_chansHSV); + m_clahe->apply(m_chansHSV[2], m_chansHSV[2]); + cv::merge(m_chansHSV, m_mHSV); + cv::cvtColor(m_mHSV, m_mBGR, cv::COLOR_HSV2BGR); + + //std::cout << "AdjustMatBGR()" << std::endl; + + return true; + } + /// + const cv::Mat& GetMatGray() + { + if (m_mGray.empty() || !m_mGrayGenerated) + { + if (m_umGray.empty() || !m_umGrayGenerated) + cv::cvtColor(m_mBGR, m_mGray, cv::COLOR_BGR2GRAY); + else + m_mGray = m_umGray.getMat(cv::ACCESS_READ); + m_mGrayGenerated = true; + } + return m_mGray; + } + /// + const cv::UMat& GetUMatBGR() + { + std::thread::id lastThreadID = std::this_thread::get_id(); + + if (m_umBGR.empty() || !m_umBGRGenerated || lastThreadID != m_umBGRThreadID) + { + m_umBGR = m_mBGR.getUMat(cv::ACCESS_READ); + m_umBGRGenerated = true; + m_umBGRThreadID = lastThreadID; + } + return m_umBGR; + } + /// + const cv::UMat& GetUMatGray() + { + std::thread::id lastThreadID = std::this_thread::get_id(); + + if (m_umGray.empty() || !m_umGrayGenerated || lastThreadID != m_umGrayThreadID) + { + if (m_mGray.empty() || !m_mGrayGenerated) + { + if (m_umBGR.empty() || !m_umBGRGenerated || lastThreadID != m_umGrayThreadID) + cv::cvtColor(m_mBGR, m_umGray, cv::COLOR_BGR2GRAY); + else + cv::cvtColor(m_umBGR, m_umGray, cv::COLOR_BGR2GRAY); + } + else + { + m_umGray = m_mGray.getUMat(cv::ACCESS_READ); + } + m_umGrayGenerated = true; + m_umGrayThreadID = lastThreadID; + } + return m_umGray; + } + +private: + cv::Mat m_mBGR; + cv::Mat m_mGray; + cv::UMat m_umBGR; + cv::UMat m_umGray; + bool m_umBGRGenerated = false; + bool m_mGrayGenerated = false; + bool m_umGrayGenerated = false; + std::thread::id m_umBGRThreadID; + std::thread::id m_umGrayThreadID; + + cv::Ptr m_clahe; + cv::Mat m_mHSV; + std::vector m_chansHSV; +}; + +/// +/// \brief The FrameInfo struct +/// +struct FrameInfo +{ + /// + FrameInfo() + { + m_frames.reserve(m_batchSize); + m_regions.reserve(m_batchSize); + m_frameInds.reserve(m_batchSize); + } + /// + FrameInfo(size_t batchSize) + : m_batchSize(batchSize) + { + m_frames.reserve(m_batchSize); + m_regions.reserve(m_batchSize); + m_frameInds.reserve(m_batchSize); + } + + /// + void SetBatchSize(size_t batchSize) + { + m_batchSize = batchSize; + m_frames.reserve(m_batchSize); + m_regions.reserve(m_batchSize); + m_frameInds.reserve(m_batchSize); + } + + /// + void CleanRegions() + { + if (m_regions.size() != m_batchSize) + m_regions.resize(m_batchSize); + for (auto& regions : m_regions) + { + regions.clear(); + } + } + + /// + void CleanTracks() + { + if (m_tracks.size() != m_batchSize) + m_tracks.resize(m_batchSize); + for (auto& tracks : m_tracks) + { + tracks.clear(); + } + } + + std::vector m_frames; + std::vector m_regions; + std::vector> m_tracks; + std::vector m_frameInds; + std::vector m_frameTimeStamps; + + size_t m_batchSize = 1; + + int64 m_dt = 0; + + std::condition_variable m_cond; + std::mutex m_mutex; + std::atomic m_captured { false }; +}; + +/// +/// \brief The VideoExample class +/// +class VideoExample +{ +public: + VideoExample(const cv::CommandLineParser& parser); + VideoExample(const VideoExample&) = delete; + VideoExample(VideoExample&&) = delete; + VideoExample& operator=(const VideoExample&) = delete; + VideoExample& operator=(VideoExample&&) = delete; + + virtual ~VideoExample() = default; + + void AsyncProcess(); + void SyncProcess(); + +protected: + std::unique_ptr m_detector; + std::unique_ptr m_tracker; + + std::string m_showLogsLevel = "debug"; + float m_fps = 25; + cv::Size m_frameSize; + int m_framesCount = 0; + + bool m_useContrastAdjustment = false; + + size_t m_batchSize = 1; + + int m_captureTimeOut = 60000; + int m_trackingTimeOut = 60000; + + ResultsLog m_resultsLog; + CVATAnnotationsGenerator m_cvatAnnotationsGenerator; + + static void CaptureAndDetect(VideoExample* thisPtr, std::atomic& stopCapture); + + virtual bool InitDetector(cv::UMat frame) = 0; + virtual bool InitTracker(cv::UMat frame) = 0; + + void Detection(FrameInfo& frame); + void Tracking(FrameInfo& frame); + + virtual void DrawData(cv::Mat frame, const std::vector& tracks, int framesCounter, int currTime) = 0; + virtual void DrawTrack(cv::Mat frame, const TrackingObject& track, bool drawTrajectory, int framesCounter, const std::string& userLabel = ""); + + TrackerSettings m_trackerSettings; + bool m_trackerSettingsLoaded = false; + + std::vector m_colors; + + std::shared_ptr m_consoleSink; + std::shared_ptr m_fileSink; + std::shared_ptr m_logger; + +private: + std::vector m_tracks; + + bool m_isTrackerInitialized = false; + bool m_isDetectorInitialized = false; + std::string m_inFile; + std::string m_outFile; +#if 0 + int m_fourcc = cv::VideoWriter::fourcc('h', '2', '6', '4'); +#else + int m_fourcc = cv::VideoWriter::fourcc('M', 'J', 'P', 'G'); +#endif + int m_startFrame = 0; + int m_endFrame = 0; + int m_finishDelay = 0; + + time_point_t m_startTimeStamp; + bool m_useArchieveTime = true; + + FrameInfo m_frameInfo[2]; + + time_point_t GetNextTimeStamp(int framesCounter) const; + + bool OpenCapture(cv::VideoCapture& capture); + bool WriteFrame(cv::VideoWriter& writer, const cv::Mat& frame); +}; diff --git a/example/cvatAnnotationsGenerator.h b/example/cvatAnnotationsGenerator.h new file mode 100644 index 000000000..49f140a62 --- /dev/null +++ b/example/cvatAnnotationsGenerator.h @@ -0,0 +1,252 @@ +#pragma once + +#include +#include +#include "defines.h" + +/// +class CVATAnnotationsGenerator +{ +public: + /// + CVATAnnotationsGenerator(const std::string& annFileName) + : m_annFileName(annFileName) + { + } + + /// + bool NewDetects(int frameInd, const std::vector& tracks, size_t detectorInd) + { + if (m_annFileName.empty()) + return false; + + auto it = m_detects.find(frameInd); + if (it == m_detects.end()) + { + if (detectorInd == 0) + { + m_detects.emplace(frameInd, tracks); + } + else + { + std::vector tmpTracks = tracks; + for (auto& track : tmpTracks) + { + track.m_ID.m_val += detectorInd * DetectorIDRange; + } + m_detects.emplace(frameInd, tmpTracks); + } + + //it = m_detects.find(frameInd); + //std::cout << "New detects 1: Frame " << frameInd << ", detector ind " << detectorInd << std::endl; + //for (const auto& track : it->second) + //{ + // std::cout << "track " << track.m_ID.ID2Str() << ", type = " << track.m_type << ", rect = " << track.m_rrect.boundingRect() << std::endl; + //} + } + else + { + if (detectorInd == 0) + { + it->second.insert(it->second.end(), tracks.begin(), tracks.end()); + } + else + { + std::vector tmpTracks = tracks; + for (auto& track : tmpTracks) + { + track.m_ID.m_val += detectorInd * DetectorIDRange; + } + it->second.insert(it->second.end(), tmpTracks.begin(), tmpTracks.end()); + } + + //std::cout << "New detects 2: Frame " << frameInd << ", detector ind " << detectorInd << std::endl; + //for (const auto& track : it->second) + //{ + // std::cout << "track " << track.m_ID.ID2Str() << ", type = " << track.m_type << ", rect = " << track.m_rrect.boundingRect() << std::endl; + //} + } + + return true; + } + + /// + bool Save(const std::string& videoFileName, int framesCount, cv::Size frameSize) + { + //PrintDetects(); + + bool res = !m_annFileName.empty(); + if (!res) + return res; + + std::ofstream annFile(m_annFileName); + res = annFile.is_open(); + if (!res) + return res; + + WriteMeta(annFile, videoFileName, framesCount, frameSize); + + auto WritePoly = [&](int frameInd, const cv::RotatedRect& rrect) + { + cv::Point2f pts[4]; + rrect.points(pts); + annFile << " \n"; + annFile << " \n"; + }; + + std::unordered_set writedTracks; + + for (auto itStartFrame = std::begin(m_detects); itStartFrame != std::end(m_detects); ++itStartFrame) + { + for (const auto& track : itStartFrame->second) + { + if (writedTracks.find(track.m_ID.m_val) != std::end(writedTracks)) + continue; + writedTracks.emplace(track.m_ID.m_val); + + annFile << " \n"; + WritePoly(itStartFrame->first, track.m_rrect); + + //std::cout << "track " << track.m_ID.ID2Str() << ", type = " << track.m_type << ", rect = " << track.m_rrect.boundingRect() << std::endl; + + auto itNextFrame = itStartFrame; + for (++itNextFrame; itNextFrame != std::end(m_detects); ++itNextFrame) + { + for (const auto& subTrack : itNextFrame->second) + { + if (track.m_ID.m_val != subTrack.m_ID.m_val) + continue; + + WritePoly(itNextFrame->first, subTrack.m_rrect); + + //std::cout << "subTrack " << subTrack.m_ID.ID2Str() << ", type = " << subTrack.m_type << ", rect = " << subTrack.m_rrect.boundingRect() << "\n"; + break; + } + } + + annFile << " \n"; + } + } + + FinalMeta(annFile); + + return res; + } + +private: + std::string m_annFileName; + + std::map> m_detects; + + static constexpr track_id_t::value_type DetectorIDRange = 1000000000; + + /// + void PrintDetects() + { + std::cout << "Print detects:\n"; + for (auto it = m_detects.begin(); it != m_detects.end(); ++it) + { + std::cout << "Frame " << it->first << ": \n"; + for (const auto track : it->second) + { + std::cout << "track " << track.m_ID.ID2Str() << ", type = " << track.m_type << ", rect = " << track.m_rrect.boundingRect() << "\n"; + } + } + std::cout.flush(); + } + + /// + template + std::string Time2Str(TimePoint now) + { + // get number of milliseconds for the current second + // (remainder after division into seconds) + auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; + + // convert to std::time_t in order to convert to std::tm (broken time) + auto timer = std::chrono::system_clock::to_time_t(now); + + // convert to broken time +#ifdef _WIN32 + std::tm bt; + localtime_s(&bt, &timer); +#else + std::tm bt = *std::localtime(&timer); +#endif + + std::ostringstream oss; + oss << std::put_time(&bt, "%Y-%m-%d %H:%M:%S"); + oss << '.' << std::setfill('0') << std::setw(3) << ms.count() << "+00:00"; + + return oss.str(); + } + + /// + void WriteMeta(std::ofstream& annFile, const std::string& videoFileName, int framesCount, cv::Size frameSize) + { + std::string currTime = Time2Str(std::chrono::system_clock::now()); + + annFile << "\n"; + annFile << "\n"; + annFile << " 1.1\n"; + annFile << " \n"; + annFile << " \n"; + annFile << " 777\n"; + annFile << " " << videoFileName << "\n"; + annFile << " " << framesCount << "\n"; + annFile << " interpolation\n"; + annFile << " 5\n"; + annFile << " \n"; + annFile << " " << currTime << "\n"; + annFile << " " << currTime << "\n"; + annFile << " default\n"; + annFile << " " << 0 << "\n"; + annFile << " " << (framesCount - 1) << "\n"; + annFile << " \n"; + annFile << " \n"; + annFile << " \n"; + annFile << " 777\n"; + annFile << " " << 0 << "\n"; + annFile << " " << (framesCount - 1) << "\n"; + annFile << " http://127.0.0.1:8080/?id=777\n"; + annFile << " \n"; + annFile << " \n"; + annFile << " \n"; + annFile << " user\n"; + annFile << " user@de-id.ca\n"; + annFile << " \n"; + annFile << " \n"; + annFile << " \n"; + annFile << " \n"; + annFile << " \n"; + annFile << " \n"; + annFile << " \n"; + annFile << " " << frameSize.width << "\n"; + annFile << " " << frameSize.height << "\n"; + annFile << " \n"; + annFile << " \n"; + annFile << " " << currTime << "\n"; + annFile << " " << videoFileName << "\n"; + annFile << " \n"; + } + + /// + void FinalMeta(std::ofstream& annFile) + { + annFile << "\n"; + } +}; diff --git a/example/examples.h b/example/examples.h new file mode 100644 index 000000000..bc6db8a1f --- /dev/null +++ b/example/examples.h @@ -0,0 +1,550 @@ +#pragma once + +#include +#include +#include + +#include "VideoExample.h" + + +// ---------------------------------------------------------------------- + +/// +/// \brief The OpenCVDNNExample class +/// +class OpenCVDNNExample final : public VideoExample +{ +public: + OpenCVDNNExample(const cv::CommandLineParser& parser) + : VideoExample(parser) + { + } + +protected: + /// + /// \brief InitDetector + /// \param frame + /// \return + /// + bool InitDetector(cv::UMat frame) override + { + config_t config; + if (!m_trackerSettingsLoaded) + { +#ifdef _WIN32 + std::string pathToModel = "../../data/"; +#else + std::string pathToModel = "../data/"; +#endif + enum class NNModels + { + TinyYOLOv3 = 0, + YOLOv3, + YOLOv4, + TinyYOLOv4, + MobileNetSSD + }; + NNModels usedModel = NNModels::MobileNetSSD; + switch (usedModel) + { + case NNModels::TinyYOLOv3: + config.emplace("modelConfiguration", pathToModel + "yolov3-tiny.cfg"); + config.emplace("modelBinary", pathToModel + "yolov3-tiny.weights"); + config.emplace("classNames", pathToModel + "coco.names"); + config.emplace("confidenceThreshold", "0.5"); + break; + + case NNModels::YOLOv3: + config.emplace("modelConfiguration", pathToModel + "yolov3.cfg"); + config.emplace("modelBinary", pathToModel + "yolov3.weights"); + config.emplace("classNames", pathToModel + "coco.names"); + config.emplace("confidenceThreshold", "0.7"); + break; + + case NNModels::YOLOv4: + config.emplace("modelConfiguration", pathToModel + "yolov4.cfg"); + config.emplace("modelBinary", pathToModel + "yolov4.weights"); + config.emplace("classNames", pathToModel + "coco.names"); + config.emplace("confidenceThreshold", "0.5"); + break; + + case NNModels::TinyYOLOv4: + config.emplace("modelConfiguration", pathToModel + "yolov4-tiny.cfg"); + config.emplace("modelBinary", pathToModel + "yolov4-tiny.weights"); + config.emplace("classNames", pathToModel + "coco.names"); + config.emplace("confidenceThreshold", "0.5"); + break; + + case NNModels::MobileNetSSD: + config.emplace("modelConfiguration", pathToModel + "MobileNetSSD_deploy.prototxt"); + config.emplace("modelBinary", pathToModel + "MobileNetSSD_deploy.caffemodel"); + config.emplace("classNames", pathToModel + "voc.names"); + config.emplace("confidenceThreshold", "0.5"); + break; + } + config.emplace("maxCropRatio", "-1"); + + config.emplace("dnnTarget", "DNN_TARGET_CPU"); + config.emplace("dnnBackend", "DNN_BACKEND_DEFAULT"); + } + else + { + config.emplace("modelConfiguration", m_trackerSettings.m_nnConfig); + config.emplace("modelBinary", m_trackerSettings.m_nnWeights); + config.emplace("confidenceThreshold", std::to_string(m_trackerSettings.m_confidenceThreshold)); + config.emplace("classNames", m_trackerSettings.m_classNames); + config.emplace("maxCropRatio", std::to_string(m_trackerSettings.m_maxCropRatio)); + config.emplace("maxBatch", std::to_string(m_trackerSettings.m_maxBatch)); + config.emplace("gpuId", std::to_string(m_trackerSettings.m_gpuId)); + config.emplace("net_type", m_trackerSettings.m_netType); + config.emplace("inference_precision", m_trackerSettings.m_inferencePrecision); + config.emplace("video_memory", std::to_string(m_trackerSettings.m_maxVideoMemory)); + config.emplace("dnnTarget", m_trackerSettings.m_dnnTarget); + config.emplace("dnnBackend", m_trackerSettings.m_dnnBackend); + config.emplace("inWidth", std::to_string(m_trackerSettings.m_inputSize.width)); + config.emplace("inHeight", std::to_string(m_trackerSettings.m_inputSize.height)); + + for (auto wname : m_trackerSettings.m_whiteList) + { + config.emplace("white_list", wname); + } + } + m_detector = BaseDetector::CreateDetector(tracking::Detectors::DNN_OCV, config, frame); + return (m_detector.get() != nullptr); + } + + /// + /// \brief InitTracker + /// \param frame + /// \return + /// + bool InitTracker(cv::UMat frame) override + { + if (!m_trackerSettingsLoaded) + { + m_trackerSettings.SetDistance(tracking::DistCenters); + m_trackerSettings.m_kalmanType = tracking::KalmanLinear; + m_trackerSettings.m_filterGoal = tracking::FilterRect; + m_trackerSettings.m_lostTrackType = tracking::TrackCSRT; // Use visual objects tracker for collisions resolving. Used if m_filterGoal == tracking::FilterRect + m_trackerSettings.m_matchType = tracking::MatchHungrian; + m_trackerSettings.m_useAcceleration = false; // Use constant acceleration motion model + m_trackerSettings.m_dt = m_trackerSettings.m_useAcceleration ? 0.05f : 0.4f; // Delta time for Kalman filter + m_trackerSettings.m_accelNoiseMag = 0.2f; // Accel noise magnitude for Kalman filter + m_trackerSettings.m_distThres = 0.8f; // Distance threshold between region and object on two frames +#if 0 + m_trackerSettings.m_minAreaRadiusPix = frame.rows / 20.f; +#else + m_trackerSettings.m_minAreaRadiusPix = -1.f; +#endif + m_trackerSettings.m_minAreaRadiusK = 0.8f; + m_trackerSettings.m_maximumAllowedLostTime = 2.; // Maximum allowed skipped frames + m_trackerSettings.m_maxTraceLength = 2.; // Maximum trace length + } + m_tracker = BaseTracker::CreateTracker(m_trackerSettings, m_fps); + return true; + } + + /// + /// \brief DrawData + /// \param frame + /// \param tracks + /// \param framesCounter + /// \param currTime + /// + void DrawData(cv::Mat frame, const std::vector& tracks, int framesCounter, int currTime) override + { + m_logger->info("Frame {0} ({1}): tracks = {2}, time = {3}", framesCounter, m_framesCount, tracks.size(), currTime); + + for (const auto& track : tracks) + { + if (track.IsRobust(3, // Minimal trajectory size + 0.5f, // Minimal ratio raw_trajectory_points / trajectory_lenght + cv::Size2f(0.1f, 8.0f))) // Min and max ratio: width / height + { + DrawTrack(frame, track, false, framesCounter); + + + std::stringstream label; + label << TypeConverter::Type2Str(track.m_type) << std::setprecision(2) << ": " << track.m_confidence; + + int baseLine = 0; + cv::Size labelSize = cv::getTextSize(label.str(), cv::FONT_HERSHEY_TRIPLEX, 0.5, 1, &baseLine); + + cv::Rect brect = track.m_rrect.boundingRect(); + if (brect.x < 0) + { + brect.width = std::min(brect.width, frame.cols - 1); + brect.x = 0; + } + else if (brect.x + brect.width >= frame.cols) + { + brect.x = std::max(0, frame.cols - brect.width - 1); + brect.width = std::min(brect.width, frame.cols - 1); + } + if (brect.y - labelSize.height < 0) + { + brect.height = std::min(brect.height, frame.rows - 1); + brect.y = labelSize.height; + } + else if (brect.y + brect.height >= frame.rows) + { + brect.y = std::max(0, frame.rows - brect.height - 1); + brect.height = std::min(brect.height, frame.rows - 1); + } + //DrawFilledRect(frame, cv::Rect(cv::Point(brect.x, brect.y - labelSize.height), cv::Size(labelSize.width, labelSize.height + baseLine)), cv::Scalar(200, 200, 200), 150); + //cv::putText(frame, label.str(), brect.tl(), cv::FONT_HERSHEY_TRIPLEX, 0.5, cv::Scalar(0, 0, 0)); + } + } + + //m_detector->CalcMotionMap(frame); + } +}; + +// ---------------------------------------------------------------------- + +#ifdef BUILD_ONNX_TENSORRT + +/// +/// \brief The ONNXTensorRTExample class +/// +class ONNXTensorRTExample final : public VideoExample +{ +public: + ONNXTensorRTExample(const cv::CommandLineParser& parser) + : VideoExample(parser) + { + } + +protected: + /// + /// \brief InitDetector + /// \param frame + /// \return + /// + bool InitDetector(cv::UMat frame) override + { + config_t config; + if (!m_trackerSettingsLoaded) + { +#ifdef _WIN32 + std::string pathToModel = "../../data/"; +#else + std::string pathToModel = "../data/"; +#endif + size_t maxBatch = 1; + enum class YOLOModels + { + TinyYOLOv3 = 0, + YOLOv3, + YOLOv4, + TinyYOLOv4, + YOLOv5, + YOLOv6, + YOLOv7, + YOLOv7Mask, + YOLOv8, + YOLOV8_OBB, + YOLOv8Mask, + YOLOv9, + YOLOv10, + YOLOv11, + YOLOv11_OBB, + YOLOv11Mask, + YOLOv12 + }; + YOLOModels usedModel = YOLOModels::YOLOv9; + switch (usedModel) + { + case YOLOModels::TinyYOLOv3: + config.emplace("modelConfiguration", pathToModel + "yolov3-tiny.cfg"); + config.emplace("modelBinary", pathToModel + "yolov3-tiny.weights"); + config.emplace("confidenceThreshold", "0.5"); + config.emplace("inference_precision", "FP32"); + config.emplace("net_type", "YOLOV3"); + maxBatch = 4; + config.emplace("maxCropRatio", "2"); + break; + + case YOLOModels::YOLOv3: + config.emplace("modelConfiguration", pathToModel + "yolov3.cfg"); + config.emplace("modelBinary", pathToModel + "yolov3.weights"); + config.emplace("confidenceThreshold", "0.7"); + config.emplace("inference_precision", "FP32"); + config.emplace("net_type", "YOLOV3"); + maxBatch = 2; + config.emplace("maxCropRatio", "-1"); + break; + + case YOLOModels::YOLOv4: + config.emplace("modelConfiguration", pathToModel + "yolov4.cfg"); + config.emplace("modelBinary", pathToModel + "yolov4.weights"); + config.emplace("confidenceThreshold", "0.4"); + config.emplace("inference_precision", "FP32"); + config.emplace("net_type", "YOLOV4"); + maxBatch = 1; + config.emplace("maxCropRatio", "-1"); + break; + + case YOLOModels::TinyYOLOv4: + config.emplace("modelConfiguration", pathToModel + "yolov4-tiny.cfg"); + config.emplace("modelBinary", pathToModel + "yolov4-tiny.weights"); + config.emplace("confidenceThreshold", "0.5"); + config.emplace("inference_precision", "FP32"); + config.emplace("net_type", "YOLOV4_TINY"); + maxBatch = 1; + config.emplace("maxCropRatio", "-1"); + break; + + case YOLOModels::YOLOv5: + config.emplace("modelConfiguration", pathToModel + "yolov5s.cfg"); + config.emplace("modelBinary", pathToModel + "yolov5s.weights"); + config.emplace("confidenceThreshold", "0.5"); + config.emplace("inference_precision", "FP32"); + config.emplace("net_type", "YOLOV5"); + maxBatch = 1; + config.emplace("maxCropRatio", "-1"); + break; + + case YOLOModels::YOLOv6: + config.emplace("modelConfiguration", pathToModel + "yolov6s.onnx"); + config.emplace("modelBinary", pathToModel + "yolov6s.onnx"); + config.emplace("confidenceThreshold", "0.5"); + config.emplace("inference_precision", "FP32"); + config.emplace("net_type", "YOLOV6"); + maxBatch = 1; + config.emplace("maxCropRatio", "-1"); + break; + + case YOLOModels::YOLOv7: + config.emplace("modelConfiguration", pathToModel + "yolov7.onnx"); + config.emplace("modelBinary", pathToModel + "yolov7.onnx"); + config.emplace("confidenceThreshold", "0.2"); + config.emplace("inference_precision", "FP32"); + config.emplace("net_type", "YOLOV7"); + maxBatch = 1; + config.emplace("maxCropRatio", "-1"); + break; + + case YOLOModels::YOLOv7Mask: + config.emplace("modelConfiguration", pathToModel + "yolov7-mask.onnx"); + config.emplace("modelBinary", pathToModel + "yolov7-mask.onnx"); + config.emplace("confidenceThreshold", "0.2"); + config.emplace("inference_precision", "FP32"); + config.emplace("net_type", "YOLOV7Mask"); + maxBatch = 1; + config.emplace("maxCropRatio", "-1"); + break; + + case YOLOModels::YOLOv8: + config.emplace("modelConfiguration", pathToModel + "yolov8s.onnx"); + config.emplace("modelBinary", pathToModel + "yolov8s.onnx"); + config.emplace("confidenceThreshold", "0.2"); + config.emplace("inference_precision", "FP32"); + config.emplace("net_type", "YOLOV8"); + maxBatch = 1; + config.emplace("maxCropRatio", "-1"); + break; + + case YOLOModels::YOLOV8_OBB: + config.emplace("modelConfiguration", pathToModel + "yolov8s-obb.onnx"); + config.emplace("modelBinary", pathToModel + "yolov8s-obb.onnx"); + config.emplace("confidenceThreshold", "0.2"); + config.emplace("inference_precision", "FP16"); + config.emplace("net_type", "YOLOV8_OBB"); + maxBatch = 1; + config.emplace("maxCropRatio", "-1"); + break; + + case YOLOModels::YOLOv8Mask: + config.emplace("modelConfiguration", pathToModel + "yolov8s-seg.onnx"); + config.emplace("modelBinary", pathToModel + "yolov8s-seg.onnx"); + config.emplace("confidenceThreshold", "0.2"); + config.emplace("inference_precision", "FP32"); + config.emplace("net_type", "YOLOV8Mask"); + maxBatch = 1; + config.emplace("maxCropRatio", "-1"); + break; + + case YOLOModels::YOLOv9: + config.emplace("modelConfiguration", pathToModel + "yolov9-c.onnx"); + config.emplace("modelBinary", pathToModel + "yolov9-c.onnx"); + config.emplace("confidenceThreshold", "0.2"); + config.emplace("inference_precision", "FP32"); + config.emplace("net_type", "YOLOV9"); + maxBatch = 1; + config.emplace("maxCropRatio", "-1"); + break; + } + if (maxBatch < m_batchSize) + maxBatch = m_batchSize; + config.emplace("maxBatch", std::to_string(maxBatch)); + config.emplace("classNames", pathToModel + "coco.names"); + + //config.emplace("white_list", "person"); + //config.emplace("white_list", "car"); + //config.emplace("white_list", "bicycle"); + //config.emplace("white_list", "motorbike"); + //config.emplace("white_list", "bus"); + //config.emplace("white_list", "truck"); + } + else + { + config.emplace("modelConfiguration", m_trackerSettings.m_nnConfig); + config.emplace("modelBinary", m_trackerSettings.m_nnWeights); + config.emplace("confidenceThreshold", std::to_string(m_trackerSettings.m_confidenceThreshold)); + config.emplace("classNames", m_trackerSettings.m_classNames); + config.emplace("maxCropRatio", std::to_string(m_trackerSettings.m_maxCropRatio)); + config.emplace("maxBatch", std::to_string(m_trackerSettings.m_maxBatch)); + config.emplace("gpuId", std::to_string(m_trackerSettings.m_gpuId)); + config.emplace("net_type", m_trackerSettings.m_netType); + config.emplace("inference_precision", m_trackerSettings.m_inferencePrecision); + config.emplace("video_memory", std::to_string(m_trackerSettings.m_maxVideoMemory)); + + for (auto wname : m_trackerSettings.m_whiteList) + { + config.emplace("white_list", wname); + } + + m_logger->info("YoloTensorRTExample:"); + m_logger->info("modelConfiguration: {}", m_trackerSettings.m_nnConfig); + m_logger->info("modelBinary: {}", m_trackerSettings.m_nnWeights); + m_logger->info("confidenceThreshold: {}", m_trackerSettings.m_confidenceThreshold); + m_logger->info("classNames: {}", m_trackerSettings.m_classNames); + m_logger->info("maxCropRatio: {}", m_trackerSettings.m_maxCropRatio); + m_logger->info("maxBatch: {}", m_trackerSettings.m_maxBatch); + m_logger->info("gpuId: {}", m_trackerSettings.m_gpuId); + m_logger->info("net_type: {}", m_trackerSettings.m_netType); + m_logger->info("inference_precision: {}", m_trackerSettings.m_inferencePrecision); + m_logger->info("video_memory: {}", m_trackerSettings.m_maxVideoMemory); + for (auto wname : m_trackerSettings.m_whiteList) + { + m_logger->info("white name: {}", wname); + } + } + + m_detector = BaseDetector::CreateDetector(tracking::Detectors::ONNX_TensorRT, config, frame); + return (m_detector.get() != nullptr); + } + + /// + /// \brief InitTracker + /// \param frame + /// \return + /// + bool InitTracker(cv::UMat frame) override + { + if (!m_trackerSettingsLoaded) + { + bool useDeepSORT = true; + if (useDeepSORT) + { +#ifdef _WIN32 + std::string pathToModel = "../../data/"; +#else + std::string pathToModel = "../data/"; +#endif + + m_trackerSettings.m_embeddings.emplace_back(pathToModel + "reid/osnet_x0_25_msmt17.onnx", pathToModel + "reid/osnet_x0_25_msmt17.onnx", + cv::Size(128, 256), + std::vector{ TypeConverter::Str2Type("person"), TypeConverter::Str2Type("car"), TypeConverter::Str2Type("bus"), TypeConverter::Str2Type("truck"), TypeConverter::Str2Type("vehicle") }); + + std::array distType{ + 0.f, // DistCenters + 0.f, // DistRects + 0.5f, // DistJaccard + 0.f, // DistHist + 0.5f, // DistFeatureCos + 0.f // DistMahalanobis + }; + if (!m_trackerSettings.SetDistances(distType)) + m_logger->error("SetDistances failed! Absolutly summ must be equal 1"); + } + else + { + m_trackerSettings.SetDistance(tracking::DistCenters); + } + + //m_trackerSettings.SetDistance(tracking::DistCenters); + m_trackerSettings.m_kalmanType = tracking::KalmanLinear; + m_trackerSettings.m_filterGoal = tracking::FilterCenter; + m_trackerSettings.m_lostTrackType = tracking::TrackKCF; // Use visual objects tracker for collisions resolving. Used if m_filterGoal == tracking::FilterRect + m_trackerSettings.m_matchType = tracking::MatchHungrian; + m_trackerSettings.m_dt = 0.3f; // Delta time for Kalman filter + m_trackerSettings.m_accelNoiseMag = 0.2f; // Accel noise magnitude for Kalman filter + m_trackerSettings.m_distThres = 0.8f; // Distance threshold between region and object on two frames + m_trackerSettings.m_minAreaRadiusPix = frame.rows / 20.f; + m_trackerSettings.m_maximumAllowedLostTime = 2.; // Maximum allowed skipped frames + m_trackerSettings.m_maxTraceLength = 5.; // Maximum trace length + } + //m_trackerSettings.AddNearTypes(TypeConverter::Str2Type("car"), TypeConverter::Str2Type("bus"), false); + //m_trackerSettings.AddNearTypes(TypeConverter::Str2Type("car"), TypeConverter::Str2Type("truck"), false); + //m_trackerSettings.AddNearTypes(TypeConverter::Str2Type("person"), TypeConverter::Str2Type("bicycle"), true); + //m_trackerSettings.AddNearTypes(TypeConverter::Str2Type("person"), TypeConverter::Str2Type("motorbike"), true); + + m_tracker = BaseTracker::CreateTracker(m_trackerSettings, m_fps); + + return true; + } + + /// + /// \brief DrawData + /// \param frame + /// \param tracks + /// \param framesCounter + /// \param currTime + /// + void DrawData(cv::Mat frame, const std::vector& tracks, int framesCounter, int currTime) override + { + m_logger->info("Frame {0} ({1}): tracks = {2}, time = {3}", framesCounter, m_framesCount, tracks.size(), currTime); + + static float averFps = 0; + if (averFps == 0) + averFps = 1000.f / currTime; + else + averFps = 0.9f * averFps + 0.1f * (1000.f / currTime); + cv::putText(frame, std::to_string(cvRound(averFps)) + " fps", cv::Point(10, 40), cv::FONT_HERSHEY_TRIPLEX, (frame.cols > 1000) ? 1.5 : 1.0, cv::Scalar(255, 0, 255)); + + for (const auto& track : tracks) + { + if (track.IsRobust(2, // Minimal trajectory size + 0.5f, // Minimal ratio raw_trajectory_points / trajectory_lenght + cv::Size2f(0.1f, 8.0f), 2)) // Min and max ratio: width / height + { + DrawTrack(frame, track, true, framesCounter); + + std::stringstream label; + label << TypeConverter::Type2Str(track.m_type) << " " << std::setprecision(2) << track.m_velocity << ": " << track.m_confidence; + int baseLine = 0; + cv::Size labelSize = cv::getTextSize(label.str(), cv::FONT_HERSHEY_TRIPLEX, 0.5, 1, &baseLine); + + cv::Rect brect = track.m_rrect.boundingRect(); + if (brect.x < 0) + { + brect.width = std::min(brect.width, frame.cols - 1); + brect.x = 0; + } + else if (brect.x + brect.width >= frame.cols) + { + brect.x = std::max(0, frame.cols - brect.width - 1); + brect.width = std::min(brect.width, frame.cols - 1); + } + if (brect.y - labelSize.height < 0) + { + brect.height = std::min(brect.height, frame.rows - 1); + brect.y = labelSize.height; + } + else if (brect.y + brect.height >= frame.rows) + { + brect.y = std::max(0, frame.rows - brect.height - 1); + brect.height = std::min(brect.height, frame.rows - 1); + } + //DrawFilledRect(frame, cv::Rect(cv::Point(brect.x, brect.y - labelSize.height), cv::Size(labelSize.width, labelSize.height + baseLine)), cv::Scalar(200, 200, 200), 150); + //cv::putText(frame, label.str(), brect.tl(), cv::FONT_HERSHEY_TRIPLEX, 0.5, cv::Scalar(0, 0, 0)); + } + } + + //m_detector->CalcMotionMap(frame); + } +}; + +#endif // BUILD_ONNX_TENSORRT diff --git a/example/main.cpp b/example/main.cpp new file mode 100644 index 000000000..f266bbd6e --- /dev/null +++ b/example/main.cpp @@ -0,0 +1,97 @@ +#include "MouseExample.h" +#include "examples.h" +#include "MotionDetectorExample.h" + +#ifdef BUILD_CARS_COUNTING +#include "CarsCounting.h" +#endif + +#include +#include + +///---------------------------------------------------------------------- +int main(int argc, char** argv) +{ + const char* keys = + { + "{ @1 |../data/atrium.avi | movie file | }" + "{ e example |1 | number of example 0 - MouseTracking, 1 - MotionDetector, 2 - opencv_dnn detector, 3 - YOLO TensorRT Detector, 4 - Cars counting | }" + "{ sf start_frame |0 | Start a video from this position | }" + "{ ef end_frame |0 | Play a video to this position (if 0 then played to the end of file) | }" + "{ ed end_delay |0 | Delay in milliseconds after video ending | }" + "{ o out | | Name of result video file | }" + "{ show_logs |info | Show Trackers logs: trace, debug, info, warning, error, critical, off | }" + "{ g gpu |0 | Use OpenCL acceleration | }" + "{ a async |1 | Use 2 theads for processing pipeline | }" + "{ r log_res | | Path to the csv file with tracking result | }" + "{ cvat_res | | Path to the xml file in cvat format with tracking result | }" + "{ s settings | | Path to the ini file with tracking settings | }" + "{ bs batch_size |1 | Batch size - frames count for processing | }" + "{ wf write_n_frame |1 | Write logs on each N frame: 1 for writing each frame | }" + "{ hm heat_map |0 | For CarsCounting: Draw heat map | }" + "{ geo_bind |geo_bind.ini | For CarsCounting: ini file with geographical binding | }" + "{ contrast_adjustment |0 | Use contrast adjustment for frames before detection | }" + }; + + cv::CommandLineParser parser(argc, argv, keys); + + std::cout << "\nExamples of the Multitarget tracking algorithm\n" + "Usage: \n" + " ./MultitargetTracker [--example]= [--start_frame]= [--end_frame]= [--end_delay]= [--out]= [--show_logs]= [--async]= [--res]= [--settings]= [--batch_size=] \n\n" + "Press:\n" + "\'m\' key for change mode: play|pause. When video is paused you can press any key for get next frame. \n\n" + "Press Esc to exit from video \n" << std::endl; + + parser.printMessage(); + + bool useOCL = parser.get("gpu") != 0; + cv::ocl::setUseOpenCL(useOCL); + std::cout << (cv::ocl::useOpenCL() ? "OpenCL is enabled" : "OpenCL not used") << std::endl; + + int exampleNum = parser.get("example"); + int asyncPipeline = parser.get("async"); + + std::unique_ptr detector; + + switch (exampleNum) + { + case 0: + MouseTracking(parser); + break; + + case 1: + detector = std::make_unique(parser); + break; + + case 2: + detector = std::make_unique(parser); + break; + +#ifdef BUILD_ONNX_TENSORRT + case 3: + detector = std::make_unique(parser); + break; +#endif + +#ifdef BUILD_CARS_COUNTING + case 4: + { + auto carsCounting = new CarsCounting(parser); + detector = std::unique_ptr(carsCounting); + break; + } +#endif + + default: + std::cerr << "Wrong example number: " << exampleNum << std::endl; + break; + } + + if (detector.get()) + asyncPipeline ? detector->AsyncProcess() : detector->SyncProcess(); + +#ifndef SILENT_WORK + cv::destroyAllWindows(); +#endif + return 0; +} diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 5f331f8ed..000000000 --- a/main.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "MouseExample.h" -#include "VideoExample.h" - -#include -#include - -// ---------------------------------------------------------------------- - -static void Help() -{ - printf("\nExamples of the Multitarget tracking algorithm\n" - "Usage: \n" - " ./MultitargetTracker [--example]= [--start_frame]= [--end_frame]= [--end_delay]= [--out]= [--show_logs]= \n\n" - "Press:\n" - "\'m\' key for change mode: play|pause. When video is paused you can press any key for get next frame. \n\n" - "Press Esc to exit from video \n\n" - ); -} - -const char* keys = -{ - "{ @1 |../data/atrium.avi | movie file | }" - "{ e example |1 | number of example 0 - MouseTracking, 1 - MotionDetector, 2 - FaceDetector, 3 - PedestrianDetector, 4 - Hybrid face and motion detectors, 5 - MobileNet SSD detector | }" - "{ sf start_frame |0 | Start a video from this position | }" - "{ ef end_frame |0 | Play a video to this position (if 0 then played to the end of file) | }" - "{ ed end_delay |0 | Delay in milliseconds after video ending | }" - "{ o out | | Name of result video file | }" - "{ sl show_logs |1 | Show Trackers logs | }" - "{ g gpu |0 | Use OpenCL acceleration | }" -}; - -// ---------------------------------------------------------------------- - -int main(int argc, char** argv) -{ - Help(); - - cv::CommandLineParser parser(argc, argv, keys); - - bool useOCL = parser.get("gpu") ? 1 : 0; - cv::ocl::setUseOpenCL(useOCL); - std::cout << (cv::ocl::useOpenCL() ? "OpenCL is enabled" : "OpenCL not used") << std::endl; - - int exampleNum = parser.get("example"); - - switch (exampleNum) - { - case 0: - MouseTracking(parser); - break; - - case 1: - { - MotionDetectorExample mdetector(parser); - mdetector.Process(); - break; - } - - case 2: - { - FaceDetectorExample face_detector(parser); - face_detector.Process(); - break; - } - - case 3: - { - PedestrianDetectorExample ped_detector(parser); - ped_detector.Process(); - break; - } - - case 4: - { - HybridFaceDetectorExample face_detector(parser); - face_detector.Process(); - break; - } - - case 5: - { - DNNDetectorExample dnn_detector(parser); - dnn_detector.Process(); - break; - } - } - - - cv::destroyAllWindows(); - return 0; -} diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..a5308c628 --- /dev/null +++ b/setup.py @@ -0,0 +1,213 @@ +import os, re, sys, shutil, platform, subprocess + +from setuptools import setup, find_packages, Extension +from setuptools.command.build_ext import build_ext +from setuptools.command.install_lib import install_lib +from setuptools.command.install_scripts import install_scripts +from distutils.command.install_data import install_data +from distutils.version import LooseVersion + +PACKAGE_NAME = "pymtracking" + +class CMakeExtension(Extension): + def __init__(self, name, sourcedir=''): + Extension.__init__(self, name, sources=[]) + self.sourcedir = os.path.abspath(sourcedir) + + +class InstallCMakeLibsData(install_data): + """ + Just a wrapper to get the install data into the egg-info + Listing the installed files in the egg-info guarantees that + all of the package files will be uninstalled when the user + uninstalls your package through pip + """ + def run(self): + """ + Outfiles are the libraries that were built using cmake + """ + # There seems to be no other way to do this; I tried listing the + # libraries during the execution of the InstallCMakeLibs.run() but + # setuptools never tracked them, seems like setuptools wants to + # track the libraries through package data more than anything... + # help would be appriciated + self.outfiles = self.distribution.data_files + +__metaclass__ = type +class InstallCMakeLibs(install_lib, object): + """ + Get the libraries from the parent distribution, use those as the outfiles + Skip building anything; everything is already built, forward libraries to + the installation step + """ + def run(self): + """ + Copy libraries from the bin directory and place them as appropriate + """ + self.announce("Moving library files", level=3) + # We have already built the libraries in the previous build_ext step + self.skip_build = True + if hasattr(self.distribution, 'bin_dir'): + bin_dir = self.distribution.bin_dir + else: + bin_dir = os.path.join(self.build_dir, "Release") + if not os.path.exists(bin_dir): + bin_dir = "build/Release" + self.build_dir = "build/Release" + print("bin_dir:", bin_dir, "build_dir:", self.build_dir) + # Depending on the files that are generated from your cmake + # build chain, you may need to change the below code, such that + # your files are moved to the appropriate location when the installation + # is run + libs = [os.path.join(bin_dir, _lib) for _lib in + os.listdir(bin_dir) if + os.path.isfile(os.path.join(bin_dir, _lib)) and + os.path.splitext(_lib)[1] in [".dll", ".so"] + and not (_lib.startswith("python") or _lib.startswith(PACKAGE_NAME))] + for lib in libs: + shutil.move(lib, os.path.join(self.build_dir, + os.path.basename(lib))) + # Mark the libs for installation, adding them to + # distribution.data_files seems to ensure that setuptools' record + # writer appends them to installed-files.txt in the package's egg-info + # + # Also tried adding the libraries to the distribution.libraries list, + # but that never seemed to add them to the installed-files.txt in the + # egg-info, and the online recommendation seems to be adding libraries + # into eager_resources in the call to setup(), which I think puts them + # in data_files anyways. + # + # What is the best way? + # These are the additional installation files that should be + # included in the package, but are resultant of the cmake build + # step; depending on the files that are generated from your cmake + # build chain, you may need to modify the below code + self.distribution.data_files = [os.path.join(self.install_dir, + os.path.basename(lib)) + for lib in libs] + # Must be forced to run after adding the libs to data_files + self.distribution.run_command("install_data") + super(InstallCMakeLibs, self).run() + +__metaclass__ = type +class InstallCMakeScripts(install_scripts, object): + """ + Install the scripts in the build dir + """ + def run(self): + """ + Copy the required directory to the build directory and super().run() + """ + self.announce("Moving scripts files", level=3) + # Scripts were already built in a previous step + self.skip_build = True + bin_dir = self.distribution.bin_dir + scripts_dirs = [os.path.join(bin_dir, _dir) for _dir in + os.listdir(bin_dir) if + os.path.isdir(os.path.join(bin_dir, _dir))] + for scripts_dir in scripts_dirs: + shutil.move(scripts_dir, + os.path.join(self.build_dir, + os.path.basename(scripts_dir))) + # Mark the scripts for installation, adding them to + # distribution.scripts seems to ensure that the setuptools' record + # writer appends them to installed-files.txt in the package's egg-info + self.distribution.scripts = scripts_dirs + super(InstallCMakeScripts, self).run() + +__metaclass__ = type +class BuildCMakeExt(build_ext, object): + """ + Builds using cmake instead of the python setuptools implicit build + """ + def run(self): + """ + Perform build_cmake before doing the 'normal' stuff + """ + for extension in self.extensions: + self.build_cmake(extension) + super(BuildCMakeExt, self).run() + + def build_cmake(self, extension): + """ + The steps required to build the extension + """ + self.announce("Preparing the build environment", level=3) + build_dir = os.path.join(self.build_temp) + extension_path = os.path.abspath(os.path.dirname(self.get_ext_fullpath(extension.name))) + os.makedirs(build_dir) + os.makedirs(extension_path) + python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1]) + + # Now that the necessary directories are created, build + self.announce("Configuring cmake project", level=3) + cmake_args = ['-DPYTHON_EXECUTABLE=' + sys.executable, + '-DUSE_OCV_BGFG=ON', + '-DUSE_OCV_KCF=ON', + '-DSILENT_WORK=ON', + '-DBUILD_EXAMPLES=OFF', + '-DBUILD_ASYNC_DETECTOR=OFF', + '-DBUILD_CARS_COUNTING=OFF', + '-DBUILD_YOLO_LIB=OFF', + '-DBUILD_YOLO_TENSORRT=OFF', + '-DMTRACKER_PYTHON=ON'] + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + self.spawn(['cmake', '-H'+extension.sourcedir, '-B'+self.build_temp]+ cmake_args) + + self.announce("Building binaries", level=3) + self.spawn(["cmake", "--build", self.build_temp, + "--config", "Release", '--', '-j8']) + + # Build finished, now copy the files into the copy directory + # The copy directory is the parent directory of the extension (.pyd) + self.announce("Moving built python module", level=3) + + bin_dir = "build" # self.build_temp + self.distribution.bin_dir = bin_dir + list_bin = os.listdir(bin_dir) + print("bin_dir:", bin_dir, ", extension_path:", extension_path, ", list_bin:", list_bin) + pyd_path = [] + for _pyd in list_bin: + print("_pyd:", _pyd) + if os.path.isfile(os.path.join(bin_dir, _pyd)) and os.path.splitext(_pyd)[0].startswith(PACKAGE_NAME) and os.path.splitext(_pyd)[1] in [".pyd", ".so"]: + pyd_path.append(os.path.join(bin_dir, _pyd)) + print("pyd_path:", pyd_path) + pyd_path = pyd_path[0] + shutil.move(pyd_path, extension_path) + + # After build_ext is run, the following commands will run: + # + # install_lib + # install_scripts + # + # These commands are subclassed above to avoid pitfalls that + # setuptools tries to impose when installing these, as it usually + # wants to build those libs and scripts as well or move them to a + # different place. See comments above for additional information + +with open("README.md", "r") as fh: + long_description = fh.read() + +setup( + name=PACKAGE_NAME, + version='1.0.1', + author='Nuzhny007', + author_email='nuzhny@mail.ru', + url='https://github.com/Smorodov/Multitarget-tracker', + license='Apache 2.0', + description='Official Python wrapper for Multitarget-tracker', + long_description=long_description, + long_description_content_type="text/markdown", + ext_modules=[CMakeExtension(name=PACKAGE_NAME, sourcedir='.')], + cmdclass={ + 'build_ext': BuildCMakeExt, + 'install_data': InstallCMakeLibsData, + 'install_lib': InstallCMakeLibs, + #'install_scripts': InstallCMakeScripts + }, + zip_safe=False, + packages=find_packages(), + keywords=['Multitarget-tracker', 'Multiple Object Tracking', 'Computer Vision', 'Machine Learning'], +) + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 000000000..5aa83f307 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,36 @@ +add_subdirectory(Detector) +add_subdirectory(Tracker) + +if(MTRACKER_PYTHON) + file(GLOB_RECURSE mtracker_python_src python_bind/*.cpp) + file(GLOB_RECURSE mtracker_python_inc python_bind/*.h) + + include_directories(${CMAKE_SOURCE_DIR}/thirdparty/pybind11/include) + include_directories(${PYTHON_INCLUDE_DIRS}) + include_directories(${NUMPY_INCLUDE_DIR}) + +if (MSVC) + if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") + set(BIT_SYSTEM x32) + else() + set(BIT_SYSTEM x64) + endif() + + include_directories(Detector/darknet/3rdparty/include) + link_directories(Detector/darknet/3rdparty/lib/${BIT_SYSTEM}) + set(LIB_PTHREAD pthreadVC2) +else() + set(LIB_PTHREAD pthread) +endif() + + include_directories(mtracking) + + pybind11_add_module(pymtracking ${mtracker_python_src} ${mtracker_python_inc}) + target_link_libraries(pymtracking PRIVATE mtracking mdetection ${OpenCV_LIBS} ${PYTHON_LIBRARY} pybind11::module) + + # set_target_properties(pymtracking PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") + + # target_compile_definitions(pymtracking PRIVATE BGS_PYTHON_SUPPORT=1) + + set_property(TARGET pymtracking PROPERTY OUTPUT_NAME "pymtracking") +endif() diff --git a/src/Detector/BackgroundSubtract.cpp b/src/Detector/BackgroundSubtract.cpp new file mode 100644 index 000000000..a4fc689cf --- /dev/null +++ b/src/Detector/BackgroundSubtract.cpp @@ -0,0 +1,279 @@ +#include "BackgroundSubtract.h" +#include + +//---------------------------------------------------------------------- +// +//---------------------------------------------------------------------- +BackgroundSubtract::BackgroundSubtract(BGFG_ALGS algType, int channels) + : + m_channels(channels), + m_algType(algType) +{ + config_t config; + Init(config); +} + +//---------------------------------------------------------------------- +// +//---------------------------------------------------------------------- +bool BackgroundSubtract::Init(const config_t& config) +{ + bool failed = true; + for (; failed;) + { + failed = false; + + switch (m_algType) + { + case ALG_VIBE: + { + std::array params = { 20, 1, 20, 3, 16 }; + std::array paramsConf = { "samples", "pixelNeighbor", "distanceThreshold", "matchingThreshold", "updateFactor" }; + + for (size_t i = 0; i < paramsConf.size(); ++i) + { + auto conf = config.find(paramsConf[i]); + if (conf != config.end()) + { + params[i] = std::stoi(conf->second); + } + } + m_modelVibe = std::make_unique(m_channels, params[0], params[1], params[2], params[3], params[4]); + break; + } + +#ifdef USE_OCV_BGFG + case ALG_MOG: + { + auto params = std::make_tuple(100, 3, 0.7, 0); + std::array::value> paramsConf = { "history", "nmixtures", "backgroundRatio", "noiseSigma" }; + + for (size_t i = 0; i < paramsConf.size(); ++i) + { + auto conf = config.find(paramsConf[i]); + if (conf != config.end()) + { + std::stringstream ss(conf->second); + + switch (i) + { + case 0: + ss >> std::get<0>(params); + break; + case 1: + ss >> std::get<1>(params); + break; + case 2: + ss >> std::get<2>(params); + break; + case 3: + ss >> std::get<3>(params); + break; + } + } + } + m_modelOCV = cv::bgsegm::createBackgroundSubtractorMOG(std::get<0>(params), std::get<1>(params), std::get<2>(params), std::get<3>(params)); + break; + } + + case ALG_GMG: + { + auto params = std::make_tuple(50, 0.7); + std::array::value> paramsConf = { "initializationFrames", "decisionThreshold" }; + + for (size_t i = 0; i < paramsConf.size(); ++i) + { + auto conf = config.find(paramsConf[i]); + if (conf != config.end()) + { + std::stringstream ss(conf->second); + + switch (i) + { + case 0: + ss >> std::get<0>(params); + break; + case 1: + ss >> std::get<1>(params); + break; + } + } + } + m_modelOCV = cv::bgsegm::createBackgroundSubtractorGMG(std::get<0>(params), std::get<1>(params)); + break; + } + + case ALG_CNT: + { +#if (((CV_VERSION_MAJOR == 3) && (CV_VERSION_MINOR >= 2)) || (CV_VERSION_MAJOR > 3)) + auto params = std::make_tuple(15, 1, 15 * 60, 1); + std::array::value> paramsConf = { "minPixelStability", "useHistory", "maxPixelStability", "isParallel" }; + + for (size_t i = 0; i < paramsConf.size(); ++i) + { + auto conf = config.find(paramsConf[i]); + if (conf != config.end()) + { + std::stringstream ss(conf->second); + + switch (i) + { + case 0: + ss >> std::get<0>(params); + break; + case 1: + ss >> std::get<1>(params); + break; + case 2: + ss >> std::get<2>(params); + break; + case 3: + ss >> std::get<3>(params); + break; + } + } + } + m_modelOCV = cv::bgsegm::createBackgroundSubtractorCNT(std::get<0>(params), std::get<1>(params) != 0, std::get<2>(params), std::get<3>(params) != 0); +#else + std::cerr << "OpenCV CNT algorithm is not implemented! Used Vibe by default." << std::endl; + failed = true; +#endif + break; + } + +#else + case ALG_MOG: + case ALG_GMG: + case ALG_CNT: + std::cerr << "OpenCV bgfg algorithms are not implemented! Used Vibe by default." << std::endl; + failed = true; + break; +#endif + + case ALG_MOG2: + { + auto params = std::make_tuple(500, 16, 1); + std::array::value> paramsConf = { "history", "varThreshold", "detectShadows" }; + + for (size_t i = 0; i < paramsConf.size(); ++i) + { + auto conf = config.find(paramsConf[i]); + if (conf != config.end()) + { + std::stringstream ss(conf->second); + + switch (i) + { + case 0: + ss >> std::get<0>(params); + break; + case 1: + ss >> std::get<1>(params); + break; + case 2: + ss >> std::get<2>(params); + break; + } + } + } + m_modelOCV = cv::createBackgroundSubtractorMOG2(std::get<0>(params), std::get<1>(params), std::get<2>(params) != 0).dynamicCast(); + break; + } + + default: + { + m_algType = ALG_VIBE; + failed = false; + break; + } + } + if (failed) + { + m_algType = ALG_VIBE; + failed = false; + } + } + return !failed; +} + +//---------------------------------------------------------------------- +// +//---------------------------------------------------------------------- +cv::UMat BackgroundSubtract::GetImg(const cv::UMat& image) +{ + if (image.channels() != m_channels) + { + if (image.channels() == 1) + { + cv::UMat newImg; + cv::cvtColor(image, newImg, cv::COLOR_GRAY2BGR); + return newImg; + } + else if (image.channels() == 3) + { + cv::UMat newImg; + cv::cvtColor(image, newImg, cv::COLOR_BGR2GRAY); + return newImg; + } + } + return image; +} + +//---------------------------------------------------------------------- +// +//---------------------------------------------------------------------- +void BackgroundSubtract::Subtract(const cv::UMat& image, cv::UMat& foreground) +{ + switch (m_algType) + { + case ALG_VIBE: + m_modelVibe->update(GetImg(image).getMat(cv::ACCESS_READ)); + m_rawForeground = m_modelVibe->getMask().getUMat(cv::ACCESS_READ); + break; + + case ALG_MOG: + case ALG_GMG: + case ALG_CNT: +#ifdef USE_OCV_BGFG + m_modelOCV->apply(GetImg(image), m_rawForeground); + break; +#else + std::cerr << "OpenCV bgfg algorithms are not implemented!" << std::endl; + break; +#endif + + case ALG_MOG2: + m_modelOCV->apply(GetImg(image), m_rawForeground); + cv::threshold(m_rawForeground, m_rawForeground, 200, 255, cv::THRESH_BINARY); + break; + + default: + m_modelVibe->update(GetImg(image).getMat(cv::ACCESS_READ)); + m_rawForeground = m_modelVibe->getMask().getUMat(cv::ACCESS_READ); + break; + } + +#ifndef SILENT_WORK + //cv::imshow("before", foreground); +#endif + + cv::medianBlur(m_rawForeground, foreground, 3); + + //cv::Mat dilateElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1)); + //cv::dilate(foreground, foreground, dilateElement, cv::Point(-1, -1), 2); + +#ifndef SILENT_WORK + //cv::imshow("after", foreground); +#endif +} + +//---------------------------------------------------------------------- +// +//---------------------------------------------------------------------- +void BackgroundSubtract::ResetModel(const cv::UMat& img, const cv::Rect& roiRect) +{ + if (m_algType == ALG_VIBE) + { + m_modelVibe->ResetModel(GetImg(img).getMat(cv::ACCESS_READ), roiRect); + } +} diff --git a/src/Detector/BackgroundSubtract.h b/src/Detector/BackgroundSubtract.h new file mode 100644 index 000000000..31dfe5d97 --- /dev/null +++ b/src/Detector/BackgroundSubtract.h @@ -0,0 +1,44 @@ +#pragma once + +#include "mtracking/defines.h" +#include "vibe_src/vibe.hpp" + +#ifdef USE_OCV_BGFG +#include +#endif + +/// +/// \brief The BackgroundSubtract class +/// +class BackgroundSubtract +{ +public: + enum BGFG_ALGS + { + ALG_VIBE, + ALG_MOG, + ALG_GMG, + ALG_CNT, + ALG_MOG2 + }; + + BackgroundSubtract(BGFG_ALGS algType, int channels); + ~BackgroundSubtract() = default; + + bool Init(const config_t& config); + + void Subtract(const cv::UMat& image, cv::UMat& foreground); + + void ResetModel(const cv::UMat& img, const cv::Rect& roiRect); + + int m_channels = 1; + BGFG_ALGS m_algType = BGFG_ALGS::ALG_MOG2; + +private: + std::unique_ptr m_modelVibe; + cv::Ptr m_modelOCV; + + cv::UMat m_rawForeground; + + cv::UMat GetImg(const cv::UMat& image); +}; diff --git a/src/Detector/BaseDetector.cpp b/src/Detector/BaseDetector.cpp new file mode 100644 index 000000000..6c197efff --- /dev/null +++ b/src/Detector/BaseDetector.cpp @@ -0,0 +1,76 @@ +#include "BaseDetector.h" +#include "MotionDetector.h" +#include "OCVDNNDetector.h" + +#ifdef BUILD_ONNX_TENSORRT +#include "ONNXTensorRTDetector.h" +#endif + +/// +/// \brief CreateDetector +/// \param detectorType +/// \param gray +/// \return +/// +std::unique_ptr BaseDetector::CreateDetector(tracking::Detectors detectorType, + const config_t& config, + const cv::UMat& frame) +{ + std::unique_ptr detector; + + switch (detectorType) + { + case tracking::Motion_VIBE: + detector = std::make_unique(BackgroundSubtract::BGFG_ALGS::ALG_VIBE, frame); + break; + + case tracking::Motion_MOG: + detector = std::make_unique(BackgroundSubtract::BGFG_ALGS::ALG_MOG, frame); + break; + + case tracking::Motion_GMG: + detector = std::make_unique(BackgroundSubtract::BGFG_ALGS::ALG_GMG, frame); + break; + + case tracking::Motion_CNT: + detector = std::make_unique(BackgroundSubtract::BGFG_ALGS::ALG_CNT, frame); + break; + + case tracking::Motion_MOG2: + detector = std::make_unique(BackgroundSubtract::BGFG_ALGS::ALG_MOG2, frame); + break; + +#ifdef USE_OCV_DNN + case tracking::DNN_OCV: + detector = std::make_unique(frame); + break; +#endif + + case tracking::ONNX_TensorRT: +#ifdef BUILD_ONNX_TENSORRT + detector = std::make_unique(frame); +#else + std::cerr << "TensorRT inference engine was not configured in CMake" << std::endl; +#endif + break; + + default: + break; + } + + if (!detector->Init(config)) + detector.reset(); + return detector; +} + +/// +std::unique_ptr BaseDetector::CreateDetectorKV(tracking::Detectors detectorType, const KeyVal& config, const cv::Mat& gray) +{ + config_t mconfig; + for (auto kv : config.m_config) + { + mconfig.emplace(kv.first, kv.second); + } + cv::UMat uframe = gray.getUMat(cv::ACCESS_READ); + return CreateDetector(detectorType, mconfig, uframe); +} diff --git a/src/Detector/BaseDetector.h b/src/Detector/BaseDetector.h new file mode 100644 index 000000000..bf8ae8828 --- /dev/null +++ b/src/Detector/BaseDetector.h @@ -0,0 +1,303 @@ +#pragma once + +#include +#include "mtracking/defines.h" + +/// +/// \brief The KeyVal struct +/// +struct KeyVal +{ + KeyVal() = default; + void Add(const std::string& key, const std::string& val) + { + m_config.emplace_back(key, val); + } + + std::vector> m_config; +}; + +/// +/// \brief The BaseDetector class +/// +class BaseDetector +{ +public: + /// + /// \brief BaseDetector + /// \param frame + /// + BaseDetector() + { + m_minObjectSize.width = 5; + m_minObjectSize.height = m_minObjectSize.width; + } + /// + /// \brief BaseDetector + /// \param frame + /// + BaseDetector(const cv::UMat& frame) + { + m_minObjectSize.width = std::max(5, frame.cols / 100); + m_minObjectSize.height = m_minObjectSize.width; + } + /// + /// \brief BaseDetector + /// \param frame + /// + BaseDetector(const cv::Mat& frame) + { + m_minObjectSize.width = std::max(5, frame.cols / 100); + m_minObjectSize.height = m_minObjectSize.width; + } + /// + /// \brief ~BaseDetector + /// + virtual ~BaseDetector(void) = default; + + /// + /// \brief Init + /// \param config + /// + virtual bool Init(const config_t& config) = 0; + + /// + /// \brief Detect + /// \param frame + /// + virtual void Detect(const cv::UMat& frame) = 0; + virtual void DetectMat(cv::Mat frame) + { + cv::UMat um = frame.getUMat(cv::ACCESS_READ); + return Detect(um); + } + + /// + /// \brief Detect + /// \param frames + /// \param regions + /// + virtual void Detect(const std::vector& frames, std::vector& regions) + { + for (size_t i = 0; i < frames.size(); ++i) + { + Detect(frames[i]); + const auto& res = GetDetects(); + regions[i].assign(std::begin(res), std::end(res)); + } + } + + /// + /// \brief ResetModel + /// \param img + /// \param roiRect + /// + virtual void ResetModel(const cv::UMat& /*img*/, const cv::Rect& /*roiRect*/) + { + } + + /// + /// \brief ResetIgnoreMask + /// + virtual void ResetIgnoreMask() + { + if (!m_ignoreMask.empty()) + m_ignoreMask = 255; + } + + /// + /// \brief UpdateIgnoreMask + /// \param img + /// \param roiRect + /// + virtual void UpdateIgnoreMask(const cv::UMat& img, cv::Rect roiRect) + { + if (m_ignoreMask.empty()) + m_ignoreMask = cv::Mat(img.size(), CV_8UC1, cv::Scalar(255)); + + auto Clamp = [](int& v, int& size, int hi) + { + if (v < 0) + { + size += v; + v = 0; + } + else if (v + size > hi - 1) + { + size = hi - 1 - v; + } + }; + Clamp(roiRect.x, roiRect.width, m_ignoreMask.cols); + Clamp(roiRect.y, roiRect.height, m_ignoreMask.rows); + m_ignoreMask(roiRect) = 0; + } + + /// + /// \brief CanGrayProcessing + /// + virtual bool CanGrayProcessing() const = 0; + + /// + /// \brief SetMinObjectSize + /// \param minObjectSize + /// + void SetMinObjectSize(cv::Size minObjectSize) + { + m_minObjectSize = minObjectSize; + } + + /// + /// \brief GetDetects + /// \return + /// + const regions_t& GetDetects() const + { + return m_regions; + } + + /// + /// \brief CalcMotionMap + /// \param frame + /// + virtual void CalcMotionMap(cv::Mat& frame) + { + if (m_motionMap.size() != frame.size()) + m_motionMap = cv::Mat(frame.size(), CV_32FC1, cv::Scalar(0, 0, 0)); + + cv::Mat foreground(m_motionMap.size(), CV_8UC1, cv::Scalar(0, 0, 0)); + for (const auto& region : m_regions) + { + if (region.m_boxMask.empty()) + { + cv::ellipse(foreground, region.m_rrect, cv::Scalar(255, 255, 255), cv::FILLED); + } + else + { + cv::Rect brect = Clamp(cv::Rect(region.m_brect.x, region.m_brect.y, region.m_boxMask.cols, region.m_boxMask.rows), foreground.size()); + region.m_boxMask.copyTo(foreground(brect)); + } + } + if (!m_ignoreMask.empty()) + cv::bitwise_and(foreground, m_ignoreMask, foreground); + cv::normalize(foreground, m_normFor, 255, 0, cv::NORM_MINMAX, m_motionMap.type()); + + double alpha = 0.9; + cv::addWeighted(m_motionMap, alpha, m_normFor, 1 - alpha, 0, m_motionMap); + + const int chans = frame.channels(); + const int height = frame.rows; +#pragma omp parallel for + for (int y = 0; y < height; ++y) + { + uchar* imgPtr = frame.ptr(y); + const float* moPtr = reinterpret_cast(m_motionMap.ptr(y)); + for (int x = 0; x < frame.cols; ++x) + { + for (int ci = chans - 1; ci < chans; ++ci) + { + imgPtr[ci] = cv::saturate_cast(imgPtr[ci] + moPtr[0]); + } + imgPtr += chans; + ++moPtr; + } + } +#if 0 + if (!m_ignoreMask.empty()) + cv::imshow("ignoreMask", m_ignoreMask); +#endif + } + + /// + static std::unique_ptr CreateDetector(tracking::Detectors detectorType, const config_t& config, const cv::UMat& gray); + static std::unique_ptr CreateDetectorKV(tracking::Detectors detectorType, const KeyVal& config, const cv::Mat& gray); + + +protected: + regions_t m_regions; + + cv::Size m_minObjectSize{2, 2}; + + cv::Mat m_ignoreMask; + + // Motion map for visualization current detections + cv::Mat m_motionMap; + cv::Mat m_normFor; + + std::set m_classesWhiteList; + + std::vector GetCrops(float maxCropRatio, cv::Size netSize, cv::Size imgSize) const + { + std::vector crops; + + const float whRatio = static_cast(netSize.width) / static_cast(netSize.height); + int cropHeight = cvRound(maxCropRatio * netSize.height); + int cropWidth = cvRound(maxCropRatio * netSize.width); + + if (imgSize.width / (float)imgSize.height > whRatio) + { + if (cropHeight >= imgSize.height) + cropHeight = imgSize.height; + cropWidth = cvRound(cropHeight * whRatio); + } + else + { + if (cropWidth >= imgSize.width) + cropWidth = imgSize.width; + cropHeight = cvRound(cropWidth / whRatio); + } + + //std::cout << "Frame size " << imgSize << ", crop size = " << cv::Size(cropWidth, cropHeight) << ", ratio = " << maxCropRatio << std::endl; + + const int stepX = 3 * cropWidth / 4; + const int stepY = 3 * cropHeight / 4; + for (int y = 0; y < imgSize.height; y += stepY) + { + bool needBreakY = false; + if (y + cropHeight >= imgSize.height) + { + y = imgSize.height - cropHeight; + needBreakY = true; + } + for (int x = 0; x < imgSize.width; x += stepX) + { + bool needBreakX = false; + if (x + cropWidth >= imgSize.width) + { + x = imgSize.width - cropWidth; + needBreakX = true; + } + crops.emplace_back(x, y, cropWidth, cropHeight); + if (needBreakX) + break; + } + if (needBreakY) + break; + } + return crops; + } + + /// + bool FillTypesMap(const std::vector& classNames) + { + bool res = true; + + m_typesMap.resize(classNames.size(), bad_type); + for (size_t i = 0; i < classNames.size(); ++i) + { + objtype_t type = TypeConverter::Str2Type(classNames[i]); + m_typesMap[i] = type; + res &= (type != bad_type); + } + return res; + } + + /// + objtype_t T2T(size_t typeInd) const + { + objtype_t res = (typeInd < m_typesMap.size()) ? m_typesMap[typeInd] : bad_type; + return res; + } + +private: + std::vector m_typesMap; +}; diff --git a/src/Detector/CMakeLists.txt b/src/Detector/CMakeLists.txt new file mode 100644 index 000000000..ffc367077 --- /dev/null +++ b/src/Detector/CMakeLists.txt @@ -0,0 +1,83 @@ +cmake_minimum_required(VERSION 3.9) + +project(mdetection) + +set(detector_sources + BaseDetector.cpp + MotionDetector.cpp + BackgroundSubtract.cpp + vibe_src/vibe.cpp) + +set(detector_headers + BaseDetector.h + MotionDetector.h + BackgroundSubtract.h + vibe_src/vibe.hpp) + +if (BUILD_ONNX_TENSORRT) + set(detector_sources ${detector_sources} ONNXTensorRTDetector.cpp) + set(detector_headers ${detector_headers} ONNXTensorRTDetector.h) +endif() + +option(USE_OCV_DNN "Use OpenCV DNN module?" ON) +if (USE_OCV_DNN) + set(detector_sources ${detector_sources} OCVDNNDetector.cpp) + set(detector_headers ${detector_headers} OCVDNNDetector.h) + add_definitions(-DUSE_OCV_DNN) +endif() + +SOURCE_GROUP("Detector" FILES ${detector_sources} ${detector_headers}) + +include(CheckIncludeFileCXX) +check_include_file_cxx(opencv2/bgsegm.hpp HAVE_OPENCV_CONTRIB) +if(HAVE_OPENCV_CONTRIB) + add_definitions(-DHAVE_OPENCV_CONTRIB) + + option(USE_OCV_BGFG "Should use the bgfg algorithms from opencv_contrib?" ON) + +else(HAVE_OPENCV_CONTRIB) + + option(USE_OCV_BGFG "Should use the bgfg algorithms from opencv_contrib?" OFF) + +endif(HAVE_OPENCV_CONTRIB) + +if(USE_OCV_BGFG) + add_definitions(-DUSE_OCV_BGFG) +else() + remove_definitions(-DUSE_OCV_BGFG) +endif(USE_OCV_BGFG) + + +include_directories(${PROJECT_SOURCE_DIR}) +include_directories(${PROJECT_SOURCE_DIR}/../src) +include_directories(${PROJECT_SOURCE_DIR}/..) + +if (CMAKE_COMPILER_IS_GNUCXX) + add_library(${PROJECT_NAME} SHARED + ${detector_sources}) +else(CMAKE_COMPILER_IS_GNUCXX) + add_library(${PROJECT_NAME} + ${detector_sources}) +endif() + +if (CMAKE_COMPILER_IS_GNUCXX) + set(LIBS ${OpenCV_LIBS}) +else(CMAKE_COMPILER_IS_GNUCXX) + set(LIBS ${OpenCV_LIBS}) +endif() + +if (BUILD_ONNX_TENSORRT) + set(LIBS ${LIBS} yolo_rt_lib) +endif(BUILD_ONNX_TENSORRT) + + +target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBS}) + +set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "${detector_headers}") +install(TARGETS ${PROJECT_NAME} + EXPORT MTTrackingExports + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + PUBLIC_HEADER DESTINATION include/${PROJECT_NAME}) + +set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "libs") diff --git a/src/Detector/MotionDetector.cpp b/src/Detector/MotionDetector.cpp new file mode 100644 index 000000000..cf6f3c26c --- /dev/null +++ b/src/Detector/MotionDetector.cpp @@ -0,0 +1,134 @@ +#include "MotionDetector.h" + +/// +/// \brief MotionDetector::MotionDetector +/// \param algType +/// \param gray +/// +MotionDetector::MotionDetector(BackgroundSubtract::BGFG_ALGS algType, const cv::UMat& gray) + : BaseDetector(gray), m_algType(algType) +{ + m_fg.create(gray.size(), CV_8UC1); + m_backgroundSubst = std::make_unique(algType, gray.channels()); +} + +/// +/// \brief MotionDetector::MotionDetector +/// \param algType +/// \param gray +/// +MotionDetector::MotionDetector(BackgroundSubtract::BGFG_ALGS algType, const cv::Mat& gray) + : BaseDetector(gray), m_algType(algType) +{ + m_fg.create(gray.size(), CV_8UC1); + m_backgroundSubst = std::make_unique(algType, gray.channels()); +} + +/// +/// \brief MotionDetector::Init +/// \param config +/// \return +/// +bool MotionDetector::Init(const config_t& config) +{ + auto conf = config.find("useRotatedRect"); + if (conf != config.end()) + m_useRotatedRect = std::stoi(conf->second) != 0; + + return m_backgroundSubst->Init(config); +} + +/// +/// \brief MotionDetector::DetectContour +/// +void MotionDetector::DetectContour() +{ + m_regions.clear(); + std::vector> contours; +#if ((CV_VERSION_MAJOR > 4) || ((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR > 9))) + cv::findContoursLinkRuns(m_fg, contours); +#else + cv::findContours(m_fg, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); +#endif + for (size_t i = 0; i < contours.size(); ++i) + { + cv::Rect br = cv::boundingRect(contours[i]); + + if (br.width >= m_minObjectSize.width && + br.height >= m_minObjectSize.height) + { + if (m_useRotatedRect) + { + cv::RotatedRect rr = cv::minAreaRect(contours[i]); + m_regions.push_back(CRegion(rr)); + } + else + { + m_regions.push_back(CRegion(br)); + } + } + } +} + +/// +/// \brief MotionDetector::Detect +/// \param gray +/// +void MotionDetector::Detect(const cv::UMat& gray) +{ + m_backgroundSubst->Subtract(gray, m_fg); + if (!m_ignoreMask.empty()) + cv::bitwise_and(m_fg, m_ignoreMask, m_fg); + + DetectContour(); +} + +/// +/// \brief MotionDetector::ResetModel +/// \param img +/// \param roiRect +/// +void MotionDetector::ResetModel(const cv::UMat& img, const cv::Rect& roiRect) +{ + m_backgroundSubst->ResetModel(img, roiRect); +} + +/// +/// \brief MotionDetector::CalcMotionMap +/// \param frame +/// +void MotionDetector::CalcMotionMap(cv::Mat& frame) +{ + if (m_motionMap.size() != frame.size()) + m_motionMap = cv::Mat(frame.size(), CV_32FC1, cv::Scalar(0, 0, 0)); + + cv::normalize(m_fg, m_normFor, 255, 0, cv::NORM_MINMAX, m_motionMap.type()); + + double alpha = 0.95; + cv::addWeighted(m_motionMap, alpha, m_normFor, 1 - alpha, 0, m_motionMap); + + const int chans = frame.channels(); + + const int height = frame.rows; +#pragma omp parallel for + for (int y = 0; y < height; ++y) + { + uchar* imgPtr = frame.ptr(y); + float* moPtr = reinterpret_cast(m_motionMap.ptr(y)); + for (int x = 0; x < frame.cols; ++x) + { + for (int ci = chans - 1; ci < chans; ++ci) + { + imgPtr[ci] = cv::saturate_cast(imgPtr[ci] + moPtr[0]); + } + imgPtr += chans; + ++moPtr; + } + } + +#if 0 + std::cout << "m_ignoreMask = " << m_ignoreMask.size() << std::endl; + if (!m_ignoreMask.empty()) + cv::imshow("ignoreMask", m_ignoreMask); +#endif +} diff --git a/src/Detector/MotionDetector.h b/src/Detector/MotionDetector.h new file mode 100644 index 000000000..f61d4f0f3 --- /dev/null +++ b/src/Detector/MotionDetector.h @@ -0,0 +1,38 @@ +#pragma once + +#include "BaseDetector.h" +#include "BackgroundSubtract.h" + +/// +/// \brief The MotionDetector class +/// +class MotionDetector : public BaseDetector +{ +public: + MotionDetector(BackgroundSubtract::BGFG_ALGS algType, const cv::UMat& gray); + MotionDetector(BackgroundSubtract::BGFG_ALGS algType, const cv::Mat& gray); + ~MotionDetector(void) = default; + + bool Init(const config_t& config) override; + + void Detect(const cv::UMat& gray) override; + + bool CanGrayProcessing() const override + { + return true; + } + + void CalcMotionMap(cv::Mat& frame) override; + + void ResetModel(const cv::UMat& img, const cv::Rect& roiRect) override; + +private: + void DetectContour(); + + std::unique_ptr m_backgroundSubst; + + cv::UMat m_fg; + + BackgroundSubtract::BGFG_ALGS m_algType = BackgroundSubtract::BGFG_ALGS::ALG_MOG2; + bool m_useRotatedRect = false; +}; diff --git a/src/Detector/OCVDNNDetector.cpp b/src/Detector/OCVDNNDetector.cpp new file mode 100644 index 000000000..8151c6be6 --- /dev/null +++ b/src/Detector/OCVDNNDetector.cpp @@ -0,0 +1,1227 @@ +#include +#include "OCVDNNDetector.h" +#include "mtracking/nms.h" + +/// +/// \brief OCVDNNDetector::OCVDNNDetector +/// \param colorFrame +/// +OCVDNNDetector::OCVDNNDetector(const cv::UMat& colorFrame) + : BaseDetector(colorFrame) + +{ + m_classNames = { "background", + "aeroplane", "bicycle", "bird", "boat", + "bottle", "bus", "car", "cat", "chair", + "cow", "diningtable", "dog", "horse", + "motorbike", "person", "pottedplant", + "sheep", "sofa", "train", "tvmonitor" }; +} + +/// +/// \brief OCVDNNDetector::OCVDNNDetector +/// \param colorFrame +/// +OCVDNNDetector::OCVDNNDetector(const cv::Mat& colorFrame) + : BaseDetector(colorFrame) + +{ + m_classNames = { "background", + "aeroplane", "bicycle", "bird", "boat", + "bottle", "bus", "car", "cat", "chair", + "cow", "diningtable", "dog", "horse", + "motorbike", "person", "pottedplant", + "sheep", "sofa", "train", "tvmonitor" }; +} + +/// +/// \brief OCVDNNDetector::Init +/// \return +/// +bool OCVDNNDetector::Init(const config_t& config) +{ +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR >= 2)) || (CV_VERSION_MAJOR > 4)) + std::map dictTargets; + dictTargets[cv::dnn::DNN_TARGET_CPU] = "DNN_TARGET_CPU"; + dictTargets[cv::dnn::DNN_TARGET_OPENCL] = "DNN_TARGET_OPENCL"; + dictTargets[cv::dnn::DNN_TARGET_OPENCL_FP16] = "DNN_TARGET_OPENCL_FP16"; + dictTargets[cv::dnn::DNN_TARGET_MYRIAD] = "DNN_TARGET_MYRIAD"; + dictTargets[cv::dnn::DNN_TARGET_CUDA] = "DNN_TARGET_CUDA"; + dictTargets[cv::dnn::DNN_TARGET_CUDA_FP16] = "DNN_TARGET_CUDA_FP16"; +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR >= 10)) || (CV_VERSION_MAJOR > 4)) + dictTargets[cv::dnn::DNN_TARGET_HDDL] = "DNN_TARGET_HDDL"; + dictTargets[cv::dnn::DNN_TARGET_NPU] = "DNN_TARGET_NPU"; + dictTargets[cv::dnn::DNN_TARGET_CPU_FP16] = "DNN_TARGET_CPU_FP16"; +#endif + + std::map dictBackends; + dictBackends[cv::dnn::DNN_BACKEND_DEFAULT] = "DNN_BACKEND_DEFAULT"; + dictBackends[cv::dnn::DNN_BACKEND_INFERENCE_ENGINE] = "DNN_BACKEND_INFERENCE_ENGINE"; + dictBackends[cv::dnn::DNN_BACKEND_OPENCV] = "DNN_BACKEND_OPENCV"; + dictBackends[cv::dnn::DNN_BACKEND_VKCOM] = "DNN_BACKEND_VKCOM"; + dictBackends[cv::dnn::DNN_BACKEND_CUDA] = "DNN_BACKEND_CUDA"; +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR >= 10)) || (CV_VERSION_MAJOR > 4)) + dictBackends[cv::dnn::DNN_BACKEND_WEBNN] = "DNN_BACKEND_WEBNN"; + dictBackends[cv::dnn::DNN_BACKEND_TIMVX] = "DNN_BACKEND_TIMVX"; + dictBackends[cv::dnn::DNN_BACKEND_CANN] = "DNN_BACKEND_CANN"; +#endif + dictBackends[1000000] = "DNN_BACKEND_INFERENCE_ENGINE_NGRAPH"; + dictBackends[1000000 + 1] = "DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019"; + + std::cout << "Avaible pairs for Target - backend:" << std::endl; + std::vector> pairs = cv::dnn::getAvailableBackends(); + for (auto p : pairs) + { + std::cout << dictBackends[p.first] << " (" << p.first << ") - " << dictTargets[p.second] << " (" << p.second << ")" << std::endl; + } +#endif + + auto modelConfiguration = config.find("modelConfiguration"); + auto modelBinary = config.find("modelBinary"); + if (modelConfiguration != config.end() && modelBinary != config.end()) + m_net = cv::dnn::readNet(modelConfiguration->second, modelBinary->second, ""); + + auto dnnTarget = config.find("dnnTarget"); + if (dnnTarget != config.end()) + { + std::map targets; + targets["DNN_TARGET_CPU"] = cv::dnn::DNN_TARGET_CPU; + targets["DNN_TARGET_OPENCL"] = cv::dnn::DNN_TARGET_OPENCL; +#if (CV_VERSION_MAJOR >= 4) + targets["DNN_TARGET_OPENCL_FP16"] = cv::dnn::DNN_TARGET_OPENCL_FP16; + targets["DNN_TARGET_MYRIAD"] = cv::dnn::DNN_TARGET_MYRIAD; +#endif +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR >= 2)) || (CV_VERSION_MAJOR > 4)) + targets["DNN_TARGET_CUDA"] = cv::dnn::DNN_TARGET_CUDA; + targets["DNN_TARGET_CUDA_FP16"] = cv::dnn::DNN_TARGET_CUDA_FP16; +#endif +#if (CV_VERSION_MAJOR > 4) + targets["DNN_TARGET_HDDL"] = cv::dnn::DNN_TARGET_HDDL; + targets["DNN_TARGET_NPU"] = cv::dnn::DNN_TARGET_NPU; + targets["DNN_TARGET_CPU_FP16"] = cv::dnn::DNN_TARGET_CPU_FP16; +#endif + + std::cout << "Trying to set target " << dnnTarget->second << "... "; + auto target = targets.find(dnnTarget->second); + if (target != std::end(targets)) + { + std::cout << "Succeded!" << std::endl; + m_net.setPreferableTarget(target->second); + } + else + { + std::cout << "Failed" << std::endl; + } + } + +#if (CV_VERSION_MAJOR >= 4) + auto dnnBackend = config.find("dnnBackend"); + if (dnnBackend != config.end()) + { + std::map backends; + backends["DNN_BACKEND_DEFAULT"] = cv::dnn::DNN_BACKEND_DEFAULT; + backends["DNN_BACKEND_INFERENCE_ENGINE"] = cv::dnn::DNN_BACKEND_INFERENCE_ENGINE; + backends["DNN_BACKEND_OPENCV"] = cv::dnn::DNN_BACKEND_OPENCV; + backends["DNN_BACKEND_VKCOM"] = cv::dnn::DNN_BACKEND_VKCOM; +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR >= 2)) || (CV_VERSION_MAJOR > 4)) + backends["DNN_BACKEND_CUDA"] = cv::dnn::DNN_BACKEND_CUDA; +#endif +#if (CV_VERSION_MAJOR > 4) + backends["DNN_BACKEND_WEBNN"] = cv::dnn::DNN_BACKEND_WEBNN; + backends["DNN_BACKEND_TIMVX"] = cv::dnn::DNN_BACKEND_TIMVX; + backends["DNN_BACKEND_CANN"] = cv::dnn::DNN_BACKEND_CANN; +#endif + + std::cout << "Trying to set backend " << dnnBackend->second << "... "; + auto backend = backends.find(dnnBackend->second); + if (backend != std::end(backends)) + { + std::cout << "Succeded!" << std::endl; + m_net.setPreferableBackend(backend->second); + } + else + { + std::cout << "Failed" << std::endl; + } + } +#endif + + auto net_type = config.find("net_type"); + if (net_type != config.end()) + { + std::map dictNetType; + dictNetType["YOLOV3"] = ModelType::YOLOV3; + dictNetType["YOLOV4"] = ModelType::YOLOV4; + dictNetType["YOLOV4_TINY"] = ModelType::YOLOV4_TINY; + dictNetType["YOLOV5"] = ModelType::YOLOV5; + dictNetType["YOLOV5_OBB"] = ModelType::YOLOV5_OBB; + dictNetType["YOLOV5Mask"] = ModelType::YOLOV5Mask; + dictNetType["YOLOV6"] = ModelType::YOLOV6; + dictNetType["YOLOV7"] = ModelType::YOLOV7; + dictNetType["YOLOV7Mask"] = ModelType::YOLOV7Mask; + dictNetType["YOLOV8"] = ModelType::YOLOV8; + dictNetType["YOLOV8_OBB"] = ModelType::YOLOV8_OBB; + dictNetType["YOLOV8Mask"] = ModelType::YOLOV8Mask; + dictNetType["YOLOV9"] = ModelType::YOLOV9; + dictNetType["YOLOV10"] = ModelType::YOLOV10; + dictNetType["YOLOV11"] = ModelType::YOLOV11; + dictNetType["YOLOV11_OBB"] = ModelType::YOLOV11_OBB; + dictNetType["YOLOV11Mask"] = ModelType::YOLOV11Mask; + dictNetType["YOLOV12"] = ModelType::YOLOV12; + dictNetType["RFDETR"] = ModelType::RFDETR; + dictNetType["RFDETR_IS"] = ModelType::RFDETR_IS; + dictNetType["DFINE"] = ModelType::DFINE; + dictNetType["YOLOV13"] = ModelType::YOLOV13; + dictNetType["DFINE_IS"] = ModelType::DFINE_IS; + dictNetType["YOLOV26"] = ModelType::YOLOV26; + dictNetType["YOLOV26_OBB"] = ModelType::YOLOV26_OBB; + dictNetType["YOLOV26Mask"] = ModelType::YOLOV26Mask; + + auto netType = dictNetType.find(net_type->second); + if (netType != dictNetType.end()) + m_netType = netType->second; + else + { + assert(netType == dictNetType.end()); + std::cerr << "net_type = " << net_type->second << ", " << (int)m_netType << std::endl; + } + + std::cout << "net_type = " << net_type->second << ", " << (int)m_netType << std::endl; + } + + auto classNames = config.find("classNames"); + if (classNames != config.end()) + { + std::ifstream classNamesFile(classNames->second); + if (classNamesFile.is_open()) + { + m_classNames.clear(); + std::string className; + for (; std::getline(classNamesFile, className); ) + { + className.erase(className.find_last_not_of(" \t\n\r\f\v") + 1); + m_classNames.push_back(className); + } + if (!FillTypesMap(m_classNames)) + { + std::cout << "Unknown types in class names!" << std::endl; + assert(0); + } + } + } + + m_classesWhiteList.clear(); + auto whiteRange = config.equal_range("white_list"); + for (auto it = whiteRange.first; it != whiteRange.second; ++it) + { + m_classesWhiteList.insert(TypeConverter::Str2Type(it->second)); + } + + auto confidenceThreshold = config.find("confidenceThreshold"); + if (confidenceThreshold != config.end()) + m_confidenceThreshold = std::stof(confidenceThreshold->second); + + auto nmsThreshold = config.find("nmsThreshold"); + if (nmsThreshold != config.end()) + m_nmsThreshold = std::stof(nmsThreshold->second); + + auto swapRB = config.find("swapRB"); + if (swapRB != config.end()) + m_swapRB = std::stoi(swapRB->second) != 0; + + auto maxCropRatio = config.find("maxCropRatio"); + if (maxCropRatio != config.end()) + m_maxCropRatio = std::stof(maxCropRatio->second); + + auto inWidth = config.find("inWidth"); + if (inWidth != config.end()) + m_inWidth = std::stoi(inWidth->second); + + auto inHeight = config.find("inHeight"); + if (inHeight != config.end()) + m_inHeight = std::stoi(inHeight->second); + + if (!m_net.empty()) + { + m_outNames = m_net.getUnconnectedOutLayersNames(); + m_outLayers = m_net.getUnconnectedOutLayers(); + assert(!m_outLayers.empty()); + + m_outLayerTypes.clear(); + for (auto it : m_outLayers) + { + m_outLayerTypes.push_back(m_net.getLayer(it)->type); + } + + std::cout << "outNames: "; + for (auto it : m_outNames) + { + std::cout << it << " | "; + } + std::cout << std::endl; + + std::cout << "outLayerType: "; + for (auto it : m_outLayerTypes) + { + std::cout << it << " | "; + } + std::cout << std::endl; + +#if (CV_VERSION_MAJOR < 5) + std::vector outputs; + std::vector internals; + m_net.getLayerShapes(cv::dnn::MatShape(), 0, outputs, internals); +#else + std::vector outputs; + std::vector internals; + m_net.getLayerShapes(cv::MatShape(), CV_32F, 0, outputs, internals); +#endif + std::cout << "getLayerShapes: outputs (" << outputs.size() << ") = " << (outputs.size() > 0 ? outputs[0].size() : 0) << ", internals (" << internals.size() << ") = " << (internals.size() > 0 ? internals[0].size() : 0) << std::endl; + if (outputs.size() && outputs[0].size() > 3) + { + std::cout << "outputs: "; + for (size_t i = 0; i < outputs.size(); ++i) + { +#if (CV_VERSION_MAJOR < 5) + std::cout << i << ": ["; + for (size_t j = 0; j < outputs[i].size(); ++j) + { + std::cout << outputs[i][j] << " "; + } + std::cout << "]"; +#else + std::cout << i << ": " << outputs[i].str(); +#endif + } + std::cout << std::endl; + + std::cout << "internals: "; + for (size_t i = 0; i < internals.size(); ++i) + { +#if (CV_VERSION_MAJOR < 5) + std::cout << i << ": ["; + for (size_t j = 0; j < internals[i].size(); ++j) + { + std::cout << internals[i][j] << " "; + } + std::cout << "]"; +#else + std::cout << i << ": " << internals[i].str(); +#endif + } + std::cout << std::endl; + + if (!m_inWidth || !m_inHeight) + { + m_inWidth = outputs[0][2]; + m_inHeight = outputs[0][3]; + } + } + } + if (!m_inWidth || !m_inHeight) + { + m_inWidth = 608; + m_inHeight = 608; + } + m_WHRatio = static_cast(m_inWidth) / static_cast(m_inHeight); + + std::cout << "input size: " << cv::Size(m_inWidth, m_inHeight) << ", m_WHRatio = " << m_WHRatio << std::endl; + + return !m_net.empty(); +} + +/// +/// \brief OCVDNNDetector::Detect +/// \param gray +/// +void OCVDNNDetector::Detect(const cv::UMat& colorFrame) +{ + m_regions.clear(); + + regions_t tmpRegions; + if (m_maxCropRatio <= 0) + { + DetectInCrop(colorFrame, cv::Rect(0, 0, colorFrame.cols, colorFrame.rows), tmpRegions); + } + else + { + std::vector crops = GetCrops(m_maxCropRatio, cv::Size(m_inWidth, m_inHeight), colorFrame.size()); + for (size_t i = 0; i < crops.size(); ++i) + { + const auto& crop = crops[i]; + //std::cout << "Crop " << i << ": " << crop << std::endl; + DetectInCrop(colorFrame, crop, tmpRegions); + } + } + nms3(tmpRegions, m_regions, m_nmsThreshold, + [](const CRegion& reg) { return reg.m_brect; }, + [](const CRegion& reg) { return reg.m_confidence; }, + [](const CRegion& reg) { return reg.m_type; }, + 0, static_cast(0)); +} + +/// +/// \brief OCVDNNDetector::DetectInCrop +/// \param colorFrame +/// \param crop +/// \param tmpRegions +/// +void OCVDNNDetector::DetectInCrop(const cv::UMat& colorFrame, const cv::Rect& crop, regions_t& tmpRegions) +{ + //Convert Mat to batch of images + cv::dnn::blobFromImage(colorFrame(crop), m_inputBlob, 1.0, cv::Size(m_inWidth, m_inHeight), m_meanVal, m_swapRB, false, CV_8U); + + m_net.setInput(m_inputBlob, "", m_inScaleFactor, m_meanVal); //set the network input + + if (m_net.getLayer(0)->outputNameToIndex("im_info") != -1) // Faster-RCNN or R-FCN + { + //cv::resize(frame, frame, cv::Size(m_inWidth, m_inHeight)); + cv::Mat imInfo = (cv::Mat_(1, 3) << m_inHeight, m_inWidth, 1.6f); + m_net.setInput(imInfo, "im_info"); + } + + std::vector detections; + m_net.forward(detections, m_outNames); //compute output + + switch (m_netType) + { + case ModelType::YOLOV5: + ParseYOLOv5(crop, detections, tmpRegions); + break; + case ModelType::YOLOV8: + ParseYOLOv8(crop, detections, tmpRegions); + break; + case ModelType::YOLOV9: + ParseYOLOv9(crop, detections, tmpRegions); + break; + case ModelType::YOLOV10: + ParseYOLOv10(crop, detections, tmpRegions); + break; + case ModelType::YOLOV11: + ParseYOLOv11(crop, detections, tmpRegions); + break; + case ModelType::YOLOV12: + ParseYOLOv11(crop, detections, tmpRegions); + break; + + case ModelType::YOLOV5_OBB: + case ModelType::YOLOV8_OBB: + case ModelType::YOLOV11_OBB: + ParseYOLOv5_8_11_obb(crop, detections, tmpRegions); + break; + + case ModelType::YOLOV5Mask: + case ModelType::YOLOV8Mask: + case ModelType::YOLOV11Mask: + ParseYOLOv5_8_11_seg(crop, detections, tmpRegions); + break; + + case ModelType::RFDETR: + ParseRFDETR(crop, detections, tmpRegions); + break; + + case ModelType::RFDETR_IS: + ParseRFDETR_IS(crop, detections, tmpRegions); + break; + + case ModelType::DFINE: + ParseDFINE(crop, detections, tmpRegions); + break; + + case ModelType::YOLOV13: + ParseYOLOv11(crop, detections, tmpRegions); + break; + + case ModelType::DFINE_IS: + ParseDFINE_IS(crop, detections, tmpRegions); + break; + + case ModelType::YOLOV26: + ParseYOLOv26(crop, detections, tmpRegions); + break; + + case ModelType::YOLOV26_OBB: + ParseYOLOv26_obb(crop, detections, tmpRegions); + break; + + case ModelType::YOLOV26Mask: + ParseYOLOv26_seg(crop, detections, tmpRegions); + break; + + default: + ParseOldYOLO(crop, detections, tmpRegions); + break; + } +} + +/// +/// \brief OCVDNNDetector::ParseOldYOLO +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseOldYOLO(const cv::Rect& crop, const std::vector& detections, regions_t& tmpRegions) +{ + if (m_outLayerTypes[0] == "DetectionOutput") + { + // Network produces output blob with a shape 1x1xNx7 where N is a number of detections and an every detection is a vector of values + // [batchId, classId, confidence, left, top, right, bottom] + CV_Assert(detections.size() > 0); + for (size_t k = 0; k < detections.size(); ++k) + { + const float* data = reinterpret_cast(detections[k].data); + for (size_t i = 0; i < detections[k].total(); i += 7) + { + float confidence = data[i + 2]; + if (confidence > m_confidenceThreshold) + { + int left = (int)data[i + 3]; + int top = (int)data[i + 4]; + int right = (int)data[i + 5]; + int bottom = (int)data[i + 6]; + int width = right - left + 1; + int height = bottom - top + 1; + if (width <= 2 || height <= 2) + { + left = cvRound(data[i + 3] * crop.width); + top = cvRound(data[i + 4] * crop.height); + right = cvRound(data[i + 5] * crop.width); + bottom = cvRound(data[i + 6] * crop.height); + width = right - left + 1; + height = bottom - top + 1; + } + size_t objectClass = (int)(data[i + 1]) - 1; + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(objectClass)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::Rect(left + crop.x, top + crop.y, width, height), T2T(objectClass), confidence); + } + } + } + } + else if (m_outLayerTypes[0] == "Region") + { + for (size_t i = 0; i < detections.size(); ++i) + { + // Network produces output blob with a shape NxC where N is a number of detected objects and C is a number of classes + 4 where the first 4 + // numbers are [center_x, center_y, width, height] + const float* data = reinterpret_cast(detections[i].data); + for (int j = 0; j < detections[i].rows; ++j, data += detections[i].cols) + { + cv::Mat scores = detections[i].row(j).colRange(5, detections[i].cols); + cv::Point classIdPoint; + double confidence = 0; + cv::minMaxLoc(scores, 0, &confidence, 0, &classIdPoint); + if (confidence > m_confidenceThreshold) + { + int centerX = cvRound(data[0] * crop.width); + int centerY = cvRound(data[1] * crop.height); + int width = cvRound(data[2] * crop.width); + int height = cvRound(data[3] * crop.height); + int left = centerX - width / 2; + int top = centerY - height / 2; + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(classIdPoint.x)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::Rect(left + crop.x, top + crop.y, width, height), T2T(classIdPoint.x), static_cast(confidence)); + } + } + } + } + else + { + CV_Error(cv::Error::StsNotImplemented, "OCVDNNDetector::ParseOldYOLO: Unknown output layer type: " + m_outLayerTypes[0] + ", net type " + std::to_string((int)m_netType)); + } +} + +/// +/// \brief OCVDNNDetector::ParseYOLOv5 +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseYOLOv5(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + int dimensions = detections[0].size[2]; + + // yolov5 has an output of shape (batchSize, 25200, 85) (Num classes + box[x,y,w,h] + confidence[c]) + // yolov8 has an output of shape (batchSize, 84, 8400) (Num classes + box[x,y,w,h]) + if (dimensions > rows) // Check if the shape[2] is more than shape[1] (yolov8) + { + rows = detections[0].size[2]; + dimensions = detections[0].size[1]; + + detections[0] = detections[0].reshape(1, dimensions); + cv::transpose(detections[0], detections[0]); + } + float* data = (float*)detections[0].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + for (int i = 0; i < rows; ++i) + { + float confidence = data[4]; + + if (confidence >= m_confidenceThreshold) + { + float* classes_scores = data + 5; + + cv::Mat scores(1, static_cast(m_classNames.size()), CV_32FC1, classes_scores); + cv::Point class_id; + double maxClassScore = 0; + cv::minMaxLoc(scores, 0, &maxClassScore, 0, &class_id); + + if (maxClassScore > m_confidenceThreshold) + { + float x = data[0]; + float y = data[1]; + float w = data[2]; + float h = data[3]; + + int left = cvRound((x - 0.5f * w) * x_factor); + int top = cvRound((y - 0.5f * h) * y_factor); + + int width = cvRound(w * x_factor); + int height = cvRound(h * y_factor); + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(class_id.x)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::Rect(left + crop.x, top + crop.y, width, height), T2T(class_id.x), static_cast(maxClassScore)); + } + } + data += dimensions; + } +} + +/// +/// \brief OCVDNNDetector::ParseYOLOv8 +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseYOLOv8(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + int dimensions = detections[0].size[2]; + + // yolov5 has an output of shape (batchSize, 25200, 85) (Num classes + box[x,y,w,h] + confidence[c]) + // yolov8 has an output of shape (batchSize, 84, 8400) (Num classes + box[x,y,w,h]) + if (dimensions > rows) // Check if the shape[2] is more than shape[1] (yolov8) + { + rows = detections[0].size[2]; + dimensions = detections[0].size[1]; + + detections[0] = detections[0].reshape(1, dimensions); + cv::transpose(detections[0], detections[0]); + } + float* data = (float*)detections[0].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + for (int i = 0; i < rows; ++i) + { + float* classes_scores = data + 4; + + cv::Mat scores(1, static_cast(m_classNames.size()), CV_32FC1, classes_scores); + cv::Point class_id; + double maxClassScore = 0; + cv::minMaxLoc(scores, 0, &maxClassScore, 0, &class_id); + + if (maxClassScore > m_confidenceThreshold) + { + float x = data[0]; + float y = data[1]; + float w = data[2]; + float h = data[3]; + + int left = cvRound((x - 0.5f * w) * x_factor); + int top = cvRound((y - 0.5f * h) * y_factor); + + int width = cvRound(w * x_factor); + int height = cvRound(h * y_factor); + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(class_id.x)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::Rect(left + crop.x, top + crop.y, width, height), T2T(class_id.x), static_cast(maxClassScore)); + } + data += dimensions; + } +} + +/// +/// \brief OCVDNNDetector::ParseYOLOv9 +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseYOLOv9(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + int dimensions = detections[0].size[2]; + + // yolov5 has an output of shape (batchSize, 25200, 85) (Num classes + box[x,y,w,h] + confidence[c]) + // yolov8 has an output of shape (batchSize, 84, 8400) (Num classes + box[x,y,w,h]) + if (dimensions > rows) // Check if the shape[2] is more than shape[1] (yolov8) + { + rows = detections[0].size[2]; + dimensions = detections[0].size[1]; + + detections[0] = detections[0].reshape(1, dimensions); + cv::transpose(detections[0], detections[0]); + } + float* data = (float*)detections[0].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + for (int i = 0; i < rows; ++i) + { + float* classes_scores = data + 4; + + cv::Mat scores(1, static_cast(m_classNames.size()), CV_32FC1, classes_scores); + cv::Point class_id; + double maxClassScore = 0; + cv::minMaxLoc(scores, 0, &maxClassScore, 0, &class_id); + + if (maxClassScore > m_confidenceThreshold) + { + float x = data[0]; + float y = data[1]; + float w = data[2]; + float h = data[3]; + + int left = cvRound((x - 0.5f * w) * x_factor); + int top = cvRound((y - 0.5f * h) * y_factor); + + int width = cvRound(w * x_factor); + int height = cvRound(h * y_factor); + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(class_id.x)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::Rect(left + crop.x, top + crop.y, width, height), T2T(class_id.x), static_cast(maxClassScore)); + } + data += dimensions; + } +} + +/// +/// \brief OCVDNNDetector::ParseYOLOv10 +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseYOLOv10(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + int dimensions = detections[0].size[2]; + + // yolov5 has an output of shape (batchSize, 25200, 85) (Num classes + box[x,y,w,h] + confidence[c]) + // yolov8 has an output of shape (batchSize, 84, 8400) (Num classes + box[x,y,w,h]) + if (dimensions > rows) // Check if the shape[2] is more than shape[1] (yolov8) + { + rows = detections[0].size[2]; + dimensions = detections[0].size[1]; + + detections[0] = detections[0].reshape(1, dimensions); + cv::transpose(detections[0], detections[0]); + } + float* data = (float*)detections[0].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + for (int i = 0; i < rows; ++i) + { + int left = cvRound(x_factor * data[0]); + int top = cvRound(y_factor * data[1]); + int width = cvRound(x_factor * (data[2] - data[0])); + int height = cvRound(y_factor * (data[3] - data[1])); + float confidence = data[4]; + int classId = cvRound(data[5]); + + if (confidence >= m_confidenceThreshold) + { + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(classId)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::Rect(left + crop.x, top + crop.y, width, height), T2T(classId), confidence); + } + data += dimensions; + } +} + +/// +/// \brief OCVDNNDetector::ParseYOLOv11 +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseYOLOv11(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + int dimensions = detections[0].size[2]; + + // yolov5 has an output of shape (batchSize, 25200, 85) (Num classes + box[x,y,w,h] + confidence[c]) + // yolov8 has an output of shape (batchSize, 84, 8400) (Num classes + box[x,y,w,h]) + if (dimensions > rows) // Check if the shape[2] is more than shape[1] (yolov8) + { + rows = detections[0].size[2]; + dimensions = detections[0].size[1]; + + detections[0] = detections[0].reshape(1, dimensions); + cv::transpose(detections[0], detections[0]); + } + float* data = (float*)detections[0].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + for (int i = 0; i < rows; ++i) + { + float* classes_scores = data + 4; + + cv::Mat scores(1, static_cast(m_classNames.size()), CV_32FC1, classes_scores); + cv::Point class_id; + double maxClassScore = 0; + cv::minMaxLoc(scores, 0, &maxClassScore, 0, &class_id); + + if (maxClassScore > m_confidenceThreshold) + { + float x = data[0]; + float y = data[1]; + float w = data[2]; + float h = data[3]; + + int left = cvRound((x - 0.5f * w) * x_factor); + int top = cvRound((y - 0.5f * h) * y_factor); + + int width = cvRound(w * x_factor); + int height = cvRound(h * y_factor); + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(class_id.x)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::Rect(left + crop.x, top + crop.y, width, height), T2T(class_id.x), static_cast(maxClassScore)); + } + data += dimensions; + } +} + +/// +/// \brief OCVDNNDetector::ParseYOLOv5_8_11_obb +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseYOLOv5_8_11_obb(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + int dimensions = detections[0].size[2]; + + // yolov5 has an output of shape (batchSize, 25200, 85) (Num classes + box[x,y,w,h] + confidence[c]) + // yolov8 has an output of shape (batchSize, 84, 8400) (Num classes + box[x,y,w,h]) + if (dimensions > rows) // Check if the shape[2] is more than shape[1] (yolov8) + { + rows = detections[0].size[2]; + dimensions = detections[0].size[1]; + + detections[0] = detections[0].reshape(1, dimensions); + cv::transpose(detections[0], detections[0]); + } + float* data = (float*)detections[0].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + for (int i = 0; i < rows; ++i) + { + float* classes_scores = data + 4; + + cv::Mat scores(1, static_cast(m_classNames.size()), CV_32FC1, classes_scores); + cv::Point class_id; + double maxClassScore = 0; + cv::minMaxLoc(scores, 0, &maxClassScore, 0, &class_id); + + if (maxClassScore > m_confidenceThreshold) + { + float x = data[0] * x_factor + crop.x; + float y = data[1] * y_factor + crop.y; + float w = data[2] * x_factor; + float h = data[3] * y_factor; + float angle = 180.f * data[4 + scores.cols] / static_cast(M_PI); + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(class_id.x)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::RotatedRect(cv::Point2f(x, y), cv::Size2f(w, h), angle), T2T(class_id.x), static_cast(maxClassScore)); + } + data += dimensions; + } +} + +/// +/// \brief OCVDNNDetector::ParseYOLOv5_8_11_seg +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseYOLOv5_8_11_seg(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + int dimensions = detections[0].size[2]; + + // yolov5 has an output of shape (batchSize, 25200, 85) (Num classes + box[x,y,w,h] + confidence[c]) + // yolov8 has an output of shape (batchSize, 84, 8400) (Num classes + box[x,y,w,h]) + if (dimensions > rows) // Check if the shape[2] is more than shape[1] (yolov8) + { + rows = detections[0].size[2]; + dimensions = detections[0].size[1]; + + detections[0] = detections[0].reshape(1, dimensions); + cv::transpose(detections[0], detections[0]); + } + float* data = (float*)detections[0].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + for (int i = 0; i < rows; ++i) + { + float* classes_scores = data + 4; + + cv::Mat scores(1, static_cast(m_classNames.size()), CV_32FC1, classes_scores); + cv::Point class_id; + double maxClassScore = 0; + cv::minMaxLoc(scores, 0, &maxClassScore, 0, &class_id); + + if (maxClassScore > m_confidenceThreshold) + { + float x = data[0] * x_factor + crop.x; + float y = data[1] * y_factor + crop.y; + float w = data[2] * x_factor; + float h = data[3] * y_factor; + //float angle = 180.f * data[4 + scores.cols] / M_PI; + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(class_id.x)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::RotatedRect(cv::Point2f(x, y), cv::Size2f(w, h), 0), T2T(class_id.x), static_cast(maxClassScore)); + } + data += dimensions; + } +} + +/// +/// \brief OCVDNNDetector::ParseRFDETR +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseRFDETR(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + int dimensionsDets = detections[0].size[2]; + int dimensionsLabels = detections[1].size[2]; + + //0: name: input, size : 1x3x560x560 + //1: name: dets, size : 1x300x4 + //2: name: labels, size : 1x300x91 + + float* dets = (float*)detections[0].data; + float* labels = (float*)detections[1].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + auto L2Conf = [](float v) + { + return 1.f / (1.f + std::exp(-v)); + }; + + for (int i = 0; i < rows; ++i) + { + float maxClassScore = L2Conf(labels[0]); + size_t classId = 0; + for (size_t cli = 1; cli < static_cast(dimensionsLabels); ++cli) + { + auto conf = L2Conf(labels[cli]); + if (maxClassScore < conf) + { + maxClassScore = conf; + classId = cli; + } + } + if (classId > 0) + --classId; + + if (maxClassScore > m_confidenceThreshold) + { + float x = dets[0]; + float y = dets[1]; + float w = dets[2]; + float h = dets[3]; + + int left = cvRound((x - 0.5f * w) * x_factor); + int top = cvRound((y - 0.5f * h) * y_factor); + + int width = cvRound(w * x_factor); + int height = cvRound(h * y_factor); + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(classId)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::Rect(left + crop.x, top + crop.y, width, height), T2T(classId), static_cast(maxClassScore)); + } + dets += dimensionsDets; + labels += dimensionsLabels; + } +} + +/// +/// \brief OCVDNNDetector::ParseRFDETR_IS +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseRFDETR_IS(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + int dimensionsDets = detections[0].size[2]; + int dimensionsLabels = detections[1].size[2]; + + //0: name: input, size : 1x3x560x560 + //1: name: dets, size : 1x300x4 + //2: name: labels, size : 1x300x91 + + float* dets = (float*)detections[0].data; + float* labels = (float*)detections[1].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + auto L2Conf = [](float v) + { + return 1.f / (1.f + std::exp(-v)); + }; + + for (int i = 0; i < rows; ++i) + { + float maxClassScore = L2Conf(labels[0]); + size_t classId = 0; + for (size_t cli = 1; cli < static_cast(dimensionsLabels); ++cli) + { + auto conf = L2Conf(labels[cli]); + if (maxClassScore < conf) + { + maxClassScore = conf; + classId = cli; + } + } + if (classId > 0) + --classId; + + if (maxClassScore > m_confidenceThreshold) + { + float x = dets[0]; + float y = dets[1]; + float w = dets[2]; + float h = dets[3]; + + int left = cvRound((x - 0.5f * w) * x_factor); + int top = cvRound((y - 0.5f * h) * y_factor); + + int width = cvRound(w * x_factor); + int height = cvRound(h * y_factor); + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(classId)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::Rect(left + crop.x, top + crop.y, width, height), T2T(classId), static_cast(maxClassScore)); + } + dets += dimensionsDets; + labels += dimensionsLabels; + } +} + +/// +/// \brief OCVDNNDetector::ParseDFINE +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseDFINE(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + + //0: name: images, size : 1x3x640x640 + //1: name: orig_target_sizes, size : 1x2 + //2: name: labels, size : 1x300 + //3: name: boxes, size : 1x300x4 + //4: name: scores, size : 1x300 + + int64_t* labels = (int64_t*)detections[0].data; + float* dets = (float*)detections[1].data; + float* scores = (float*)detections[2].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + for (int i = 0; i < rows; ++i) + { + float maxClassScore = scores[i]; + size_t classId = labels[i]; + + if (maxClassScore > m_confidenceThreshold) + { + float x = dets[4 * i + 0]; + float y = dets[4 * i + 1]; + float w = dets[4 * i + 2] - x; + float h = dets[4 * i + 3] - y; + + int left = cvRound(x * x_factor); + int top = cvRound(y * y_factor); + + int width = cvRound(w * x_factor); + int height = cvRound(h * y_factor); + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(classId)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::Rect(left + crop.x, top + crop.y, width, height), T2T(classId), static_cast(maxClassScore)); + } + } +} + +/// +/// \brief OCVDNNDetector::ParseDFINE_IS +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseDFINE_IS(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + assert(0); +} + +/// +/// \brief OCVDNNDetector::ParseYOLOv26 +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseYOLOv26(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + + //0: name: images, size: 1x3x640x640 + //1: name: output0, size: 1x300x6 + + float* dets = (float*)detections[0].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + //std::cout << "detections: " << rows << std::endl; + + for (int i = 0; i < rows; ++i) + { + auto ind = 6 * i; + + float maxClassScore = dets[ind + 4]; + size_t classId = static_cast(dets[ind + 5]); + + if (maxClassScore > m_confidenceThreshold) + { + float x = dets[ind + 0]; + float y = dets[ind + 1]; + float w = dets[ind + 2] - x; + float h = dets[ind + 3] - y; + + int left = cvRound(x * x_factor); + int top = cvRound(y * y_factor); + + int width = cvRound(w * x_factor); + int height = cvRound(h * y_factor); + + //std::cout << "ind: " << ind << ", score = " << maxClassScore << ", class = " << classId << ", rect = " << cv::Rect(left, top, width, height) << std::endl; + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(classId)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::Rect(left + crop.x, top + crop.y, width, height), T2T(classId), static_cast(maxClassScore)); + } + } +} + +/// +/// \brief OCVDNNDetector::ParseYOLOv26_obb +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseYOLOv26_obb(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + + //0: name: images, size: 1x3x1024x1024 + //1: name: output0, size: 1x300x7 + + float* dets = (float*)detections[0].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + //std::cout << "detections: " << rows << std::endl; + + for (int i = 0; i < rows; ++i) + { + auto ind = 7 * i; + + float maxClassScore = dets[ind + 4]; + size_t classId = static_cast(dets[ind + 5]); + + if (maxClassScore > m_confidenceThreshold) + { + float x = dets[ind + 0] * x_factor; + float y = dets[ind + 1] * y_factor; + float w = dets[ind + 2] * x_factor; + float h = dets[ind + 3] * y_factor; + float angle = 180.f * dets[ind + 6] / static_cast(M_PI); + + //std::cout << "ind: " << ind << ", score = " << maxClassScore << ", class = " << classId << ", rect = " << cv::Rect(left, top, width, height) << std::endl; + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(classId)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::RotatedRect(cv::Point2f(x + crop.x, y + crop.y), cv::Size2f(w, h), angle), T2T(classId), static_cast(maxClassScore)); + } + } +} + +/// +/// \brief OCVDNNDetector::ParseYOLOv26_seg +/// \param crop +/// \param detections +/// \param tmpRegions +/// +void OCVDNNDetector::ParseYOLOv26_seg(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions) +{ + int rows = detections[0].size[1]; + + //0: name: images, size: 1x3x640x640 + //1: name: output0, size: 1x300x38 + //2: name: output1, size: 1x32x160x160 + + float* dets = (float*)detections[0].data; + + float x_factor = crop.width / static_cast(m_inWidth); + float y_factor = crop.height / static_cast(m_inHeight); + + //std::cout << "detections: " << rows << std::endl; + + for (int i = 0; i < rows; ++i) + { + auto ind = 38 * i; + + float maxClassScore = dets[ind + 4]; + size_t classId = static_cast(dets[ind + 5]); + + if (maxClassScore > m_confidenceThreshold) + { + float x = dets[ind + 0]; + float y = dets[ind + 1]; + float w = dets[ind + 2] - x; + float h = dets[ind + 3] - y; + + int left = cvRound(x * x_factor); + int top = cvRound(y * y_factor); + + int width = cvRound(w * x_factor); + int height = cvRound(h * y_factor); + + //std::cout << "ind: " << ind << ", score = " << maxClassScore << ", class = " << classId << ", rect = " << cv::Rect(left, top, width, height) << std::endl; + + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(classId)) != std::end(m_classesWhiteList)) + tmpRegions.emplace_back(cv::Rect(left + crop.x, top + crop.y, width, height), T2T(classId), static_cast(maxClassScore)); + } + } +} diff --git a/src/Detector/OCVDNNDetector.h b/src/Detector/OCVDNNDetector.h new file mode 100644 index 000000000..3a55dd675 --- /dev/null +++ b/src/Detector/OCVDNNDetector.h @@ -0,0 +1,102 @@ +#pragma once + +#ifdef USE_OCV_DNN + +#include "BaseDetector.h" + +#include +#include + +/// +/// \brief The OCVDNNDetector class +/// +class OCVDNNDetector final : public BaseDetector +{ +public: + OCVDNNDetector(const cv::UMat& colorFrame); + OCVDNNDetector(const cv::Mat& colorFrame); + ~OCVDNNDetector(void) = default; + + bool Init(const config_t& config) override; + + void Detect(const cv::UMat& colorFrame) override; + + bool CanGrayProcessing() const override + { + return false; + } + +private: + enum class ModelType + { + Unknown, + YOLOV3, + YOLOV3_TINY, + YOLOV4, + YOLOV4_TINY, + YOLOV5, + YOLOV5_OBB, + YOLOV5Mask, + YOLOV6, + YOLOV7, + YOLOV7Mask, + YOLOV8, + YOLOV8_OBB, + YOLOV8Mask, + YOLOV9, + YOLOV10, + YOLOV11, + YOLOV11_OBB, + YOLOV11Mask, + YOLOV12, + RFDETR, + RFDETR_IS, + DFINE, + YOLOV13, + DFINE_IS, + YOLOV26, + YOLOV26_OBB, + YOLOV26Mask + }; + + cv::dnn::Net m_net; + + void DetectInCrop(const cv::UMat& colorFrame, const cv::Rect& crop, regions_t& tmpRegions); + + int m_inWidth = 608; + int m_inHeight = 608; + + float m_WHRatio = 1.f; + double m_inScaleFactor = 0.003921; // 1 / 255 + //double m_inScaleFactor = 1.0; + cv::Scalar m_meanVal = {0, 0, 0}; + float m_confidenceThreshold = 0.24f; + track_t m_nmsThreshold = static_cast(0.4); + bool m_swapRB = true; + float m_maxCropRatio = 2.0f; + ModelType m_netType = ModelType::Unknown; + std::vector m_classNames; + std::vector m_outNames; + std::vector m_outLayers; + std::vector m_outLayerTypes; + cv::UMat m_inputBlob; + + void ParseOldYOLO(const cv::Rect& crop, const std::vector& detections, regions_t& tmpRegions); + + void ParseYOLOv5(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseYOLOv8(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseYOLOv9(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseYOLOv10(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseYOLOv11(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseYOLOv5_8_11_obb(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseYOLOv5_8_11_seg(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseRFDETR(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseRFDETR_IS(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseDFINE(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseDFINE_IS(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseYOLOv26(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseYOLOv26_obb(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); + void ParseYOLOv26_seg(const cv::Rect& crop, std::vector& detections, regions_t& tmpRegions); +}; + +#endif diff --git a/src/Detector/ONNXTensorRTDetector.cpp b/src/Detector/ONNXTensorRTDetector.cpp new file mode 100644 index 000000000..9d28da54e --- /dev/null +++ b/src/Detector/ONNXTensorRTDetector.cpp @@ -0,0 +1,339 @@ +#include +#include "ONNXTensorRTDetector.h" +#include "mtracking/nms.h" + +/// +/// \brief ONNXTensorRTDetector::ONNXTensorRTDetector +/// \param colorFrame +/// +ONNXTensorRTDetector::ONNXTensorRTDetector(const cv::UMat& colorFrame) + : BaseDetector(colorFrame) +{ + m_localConfig.m_calibrationImageListFileTxt = ""; + m_localConfig.m_inferencePrecision = tensor_rt::FP32; + m_localConfig.m_netType = tensor_rt::YOLOV4; + m_localConfig.m_detectThresh = 0.5f; + m_localConfig.m_gpuInd = 0; +} + +/// +/// \brief ONNXTensorRTDetector::ONNXTensorRTDetector +/// \param colorFrame +/// +ONNXTensorRTDetector::ONNXTensorRTDetector(const cv::Mat& colorFrame) + : BaseDetector(colorFrame) +{ + m_localConfig.m_calibrationImageListFileTxt = ""; + m_localConfig.m_inferencePrecision = tensor_rt::FP32; + m_localConfig.m_netType = tensor_rt::YOLOV4; + m_localConfig.m_detectThresh = 0.5f; + m_localConfig.m_gpuInd = 0; +} + +/// +/// \brief ONNXTensorRTDetector::Init +/// \return +/// +bool ONNXTensorRTDetector::Init(const config_t& config) +{ + //std::cout << "YoloTensorRTDetector::Init" << std::endl; + + m_detector.reset(); + + auto modelConfiguration = config.find("modelConfiguration"); + auto modelBinary = config.find("modelBinary"); + if (modelConfiguration == config.end() || modelBinary == config.end()) + return false; + + auto confidenceThreshold = config.find("confidenceThreshold"); + if (confidenceThreshold != config.end()) + m_localConfig.m_detectThresh = std::stof(confidenceThreshold->second); + + auto gpuId = config.find("gpuId"); + if (gpuId != config.end()) + m_localConfig.m_gpuInd = std::max(0, std::stoi(gpuId->second)); + + auto maxBatch = config.find("maxBatch"); + if (maxBatch != config.end()) + m_batchSize = std::max(1, std::stoi(maxBatch->second)); + m_localConfig.m_batchSize = static_cast(m_batchSize); + + auto videoMemory = config.find("video_memory"); + if (videoMemory != config.end()) + m_localConfig.m_videoMemory = std::max(0, std::stoul(videoMemory->second)); + + m_localConfig.m_fileModelCfg = modelConfiguration->second; + m_localConfig.m_fileModelWeights = modelBinary->second; + + auto inference_precision = config.find("inference_precision"); + if (inference_precision != config.end()) + { + std::map dictPrecision; + dictPrecision["INT8"] = tensor_rt::INT8; + dictPrecision["FP16"] = tensor_rt::FP16; + dictPrecision["FP32"] = tensor_rt::FP32; + auto precision = dictPrecision.find(inference_precision->second); + if (precision != dictPrecision.end()) + m_localConfig.m_inferencePrecision = precision->second; + } + + auto net_type = config.find("net_type"); + if (net_type != config.end()) + { + std::map dictNetType; + dictNetType["YOLOV3"] = tensor_rt::YOLOV3; + dictNetType["YOLOV4"] = tensor_rt::YOLOV4; + dictNetType["YOLOV4_TINY"] = tensor_rt::YOLOV4_TINY; + dictNetType["YOLOV5"] = tensor_rt::YOLOV5; + dictNetType["YOLOV6"] = tensor_rt::YOLOV6; + dictNetType["YOLOV7"] = tensor_rt::YOLOV7; + dictNetType["YOLOV7Mask"] = tensor_rt::YOLOV7Mask; + dictNetType["YOLOV8"] = tensor_rt::YOLOV8; + dictNetType["YOLOV8_OBB"] = tensor_rt::YOLOV8_OBB; + dictNetType["YOLOV8Mask"] = tensor_rt::YOLOV8Mask; + dictNetType["YOLOV9"] = tensor_rt::YOLOV9; + dictNetType["YOLOV10"] = tensor_rt::YOLOV10; + dictNetType["YOLOV11"] = tensor_rt::YOLOV11; + dictNetType["YOLOV11_OBB"] = tensor_rt::YOLOV11_OBB; + dictNetType["YOLOV11Mask"] = tensor_rt::YOLOV11Mask; + dictNetType["YOLOV12"] = tensor_rt::YOLOV12; + dictNetType["RFDETR"] = tensor_rt::RFDETR; + dictNetType["RFDETR_IS"] = tensor_rt::RFDETR_IS; + dictNetType["DFINE"] = tensor_rt::DFINE; + dictNetType["YOLOV13"] = tensor_rt::YOLOV13; + dictNetType["DFINE_IS"] = tensor_rt::DFINE_IS; + dictNetType["YOLOV26"] = tensor_rt::YOLOV26; + dictNetType["YOLOV26_OBB"] = tensor_rt::YOLOV26_OBB; + dictNetType["YOLOV26Mask"] = tensor_rt::YOLOV26Mask; + + auto netType = dictNetType.find(net_type->second); + if (netType != dictNetType.end()) + m_localConfig.m_netType = netType->second; + else + { + assert(netType == dictNetType.end()); + std::cerr << "net_type = " << net_type->second << ", " << (int)m_localConfig.m_netType << std::endl; + } + } + + auto classNames = config.find("classNames"); + if (classNames != config.end()) + { + std::ifstream classNamesFile(classNames->second); + if (classNamesFile.is_open()) + { + m_classNames.clear(); + std::string className; + for (; std::getline(classNamesFile, className); ) + { + className.erase(className.find_last_not_of(" \t\n\r\f\v") + 1); + m_classNames.push_back(className); + } + if (!FillTypesMap(m_classNames)) + { + std::cout << "Unknown types in class names!" << std::endl; + assert(0); + } + } + else + { + std::cout << "File with class names can not be opened!" << std::endl; + assert(0); + } + } + + m_classesWhiteList.clear(); + auto whiteRange = config.equal_range("white_list"); + for (auto it = whiteRange.first; it != whiteRange.second; ++it) + { + m_classesWhiteList.insert(TypeConverter::Str2Type(it->second)); + } + + auto maxCropRatio = config.find("maxCropRatio"); + if (maxCropRatio != config.end()) + m_maxCropRatio = std::stof(maxCropRatio->second); + + + std::cout << "YoloTensorRTDetector::Init: tensor_rt::Detector" << std::endl; + m_detector = std::make_unique(); + if (m_detector) + m_detector->Init(m_localConfig); + + std::cout << "YoloTensorRTDetector::Init: Detector created = " << (m_detector.get() != nullptr) << std::endl; + + return m_detector.get() != nullptr; +} + +/// +/// \brief ONNXTensorRTDetector::Detect +/// \param gray +/// +void ONNXTensorRTDetector::Detect(const cv::UMat& colorFrame) +{ + m_regions.clear(); + cv::Mat colorMat = colorFrame.getMat(cv::ACCESS_READ); + +#define DRAW_MASK 0 +#if DRAW_MASK + cv::Mat img = colorMat.clone(); + std::vector color; + srand(time(0)); + for (int i = 0; i < m_classNames.size(); i++) + { + int b = rand() % 256; + int g = rand() % 256; + int r = rand() % 256; + color.emplace_back(b, g, r); + } + cv::Mat mask = img.clone(); +#endif + + if (m_maxCropRatio <= 0) + { + std::vector batch = { colorMat }; + std::vector detects; + m_detector->Detect(batch, detects); + for (const tensor_rt::BatchResult& dets : detects) + { + for (const tensor_rt::Result& bbox : dets) + { + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(bbox.m_id)) != std::end(m_classesWhiteList)) + { + m_regions.emplace_back(bbox.m_rrect, bbox.m_brect, T2T(bbox.m_id), bbox.m_prob, bbox.m_boxMask); + + //std::cout << "YoloTensorRTDetector::Detect: bbox.m_rrect " << bbox.m_rrect.center << ", " << bbox.m_rrect.angle << ", " << bbox.m_rrect.size << std::endl; + //std::cout << "YoloTensorRTDetector::Detect: m_regions.back().m_rrect " << m_regions.back().m_rrect.center << ", " << m_regions.back().m_rrect.angle << ", " << m_regions.back().m_rrect.size << std::endl; +#if DRAW_MASK + rectangle(img, bbox.m_brect, color[bbox.m_id], 2, 8); + mask(bbox.m_brect).setTo(color[bbox.m_id], bbox.m_boxMask); +#endif + } + } + } +#if DRAW_MASK + cv::addWeighted(img, 0.5, mask, 0.5, 0, img); + cv::imshow("mask", mask); + cv::waitKey(1); +#endif + } + else + { + std::vector crops = GetCrops(m_maxCropRatio, m_detector->GetInputSize(), colorMat.size()); + //std::cout << "Image on " << crops.size() << " crops with size " << crops.front().size() << ", input size " << m_detector->GetInputSize() << ", batch " << m_batchSize << ", frame " << colorMat.size() << std::endl; + regions_t tmpRegions; + std::vector batch; + batch.reserve(m_batchSize); + for (size_t i = 0; i < crops.size(); i += m_batchSize) + { + size_t batchSize = std::min(static_cast(m_batchSize), crops.size() - i); + batch.clear(); + for (size_t j = 0; j < batchSize; ++j) + { + batch.emplace_back(colorMat, crops[i + j]); + } + std::vector detects; + m_detector->Detect(batch, detects); + + for (size_t j = 0; j < batchSize; ++j) + { + const auto& crop = crops[i + j]; + //std::cout << "batch " << (i / batchSize) << ", crop " << (i + j) << ": " << crop << std::endl; + + for (const tensor_rt::Result& bbox : detects[j]) + { + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(bbox.m_id)) != std::end(m_classesWhiteList)) + { + cv::RotatedRect newRRect(bbox.m_rrect); + newRRect.center.x += crop.x; + newRRect.center.y += crop.y; + tmpRegions.emplace_back(newRRect, T2T(bbox.m_id), bbox.m_prob); + } + } + } + } + + if (crops.size() > 1) + { + nms3(tmpRegions, m_regions, static_cast(0.4), + [](const CRegion& reg) { return reg.m_brect; }, + [](const CRegion& reg) { return reg.m_confidence; }, + [](const CRegion& reg) { return reg.m_type; }, + 0, static_cast(0)); + //std::cout << "nms for " << tmpRegions.size() << " objects - result " << m_regions.size() << std::endl; + } + } +} + +/// +/// \brief ONNXTensorRTDetector::Detect +/// \param frames +/// \param regions +/// +void ONNXTensorRTDetector::Detect(const std::vector& frames, std::vector& regions) +{ + if (frames.size() == 1) + { + Detect(frames.front()); + regions[0].assign(std::begin(m_regions), std::end(m_regions)); + } + else + { + std::vector batch; + for (const auto& frame : frames) + { + batch.emplace_back(frame.getMat(cv::ACCESS_READ)); + } + + std::vector detects; + m_detector->Detect(batch, detects); + for (size_t i = 0; i < detects.size(); ++i) + { + const tensor_rt::BatchResult& dets = detects[i]; + for (const tensor_rt::Result& bbox : dets) + { + if (m_classesWhiteList.empty() || m_classesWhiteList.find(T2T(bbox.m_id)) != std::end(m_classesWhiteList)) + regions[i].emplace_back(bbox.m_rrect, T2T(bbox.m_id), bbox.m_prob); + } + } + m_regions.assign(std::begin(regions.back()), std::end(regions.back())); + } +} + +/// +/// \brief CalcMotionMap +/// \param frame +/// +void ONNXTensorRTDetector::CalcMotionMap(cv::Mat& frame) +{ + if (m_localConfig.m_netType == tensor_rt::YOLOV7Mask + || m_localConfig.m_netType == tensor_rt::YOLOV8Mask + || m_localConfig.m_netType == tensor_rt::YOLOV11Mask + || m_localConfig.m_netType == tensor_rt::YOLOV26Mask) + { + static std::vector color; + if (color.empty()) + { + srand((unsigned int)time(0)); + for (int i = 0; i < m_classNames.size(); i++) + { + int b = rand() % 256; + int g = rand() % 256; + int r = rand() % 256; + color.emplace_back(b, g, r); + } + } + cv::Mat mask = frame.clone(); + + for (const auto& region : m_regions) + { + //cv::rectangle(frame, region.m_brect, color[region.m_type], 2, 8); + if (!region.m_boxMask.empty()) + mask(region.m_brect).setTo(color[region.m_type], region.m_boxMask); + } + cv::addWeighted(frame, 0.5, mask, 0.5, 0, frame); + } + else + { + BaseDetector::CalcMotionMap(frame); + } +} diff --git a/src/Detector/ONNXTensorRTDetector.h b/src/Detector/ONNXTensorRTDetector.h new file mode 100644 index 000000000..39c5fd711 --- /dev/null +++ b/src/Detector/ONNXTensorRTDetector.h @@ -0,0 +1,36 @@ +#pragma once + +#include "BaseDetector.h" +#include "tensorrt_onnx/class_detector.h" + +/// +/// \brief The ONNXTensorRTDetector class +/// +class ONNXTensorRTDetector final : public BaseDetector +{ +public: + ONNXTensorRTDetector(const cv::UMat& colorFrame); + ONNXTensorRTDetector(const cv::Mat& colorFrame); + ~ONNXTensorRTDetector(void) = default; + + bool Init(const config_t& config) override; + + void Detect(const cv::UMat& colorFrame) override; + void Detect(const std::vector& frames, std::vector& regions) override; + + bool CanGrayProcessing() const override + { + return false; + } + + void CalcMotionMap(cv::Mat& frame); + +private: + std::unique_ptr m_detector; + + float m_maxCropRatio = 3.0f; + std::vector m_classNames; + + tensor_rt::Config m_localConfig; + size_t m_batchSize = 1; +}; diff --git a/src/Detector/tensorrt_onnx/API.h b/src/Detector/tensorrt_onnx/API.h new file mode 100644 index 000000000..b8c18f8d7 --- /dev/null +++ b/src/Detector/tensorrt_onnx/API.h @@ -0,0 +1,17 @@ +#ifdef API_EXPORTS + +#if defined(_MSC_VER) +#define API __declspec(dllexport) +#else +#define API __attribute__((visibility("default"))) +#endif + +#else + +#if defined(_MSC_VER) +#define API __declspec(dllimport) +#else +#define API +#endif + +#endif \ No newline at end of file diff --git a/src/Detector/tensorrt_onnx/CMakeLists.txt b/src/Detector/tensorrt_onnx/CMakeLists.txt new file mode 100644 index 000000000..257dd3e28 --- /dev/null +++ b/src/Detector/tensorrt_onnx/CMakeLists.txt @@ -0,0 +1,99 @@ +cmake_minimum_required (VERSION 3.10) + +set(libname_rt "yolo_rt_lib") +project(${libname_rt}) + +#cuda +find_package(CUDA REQUIRED) + +if (CMAKE_COMPILER_IS_GNUCXX) + set(CUDA_PROPAGATE_HOST_FLAGS OFF) + set(CUDA_HOST_COMPILATION_CPP ON) + set(CUDA_NVCC_FLAGS -std=c++11 -g -Xcompiler -fexceptions -Xcompiler -fPIC) + set(CUDA_SEPARABLE_COMPILATION ON) +elseif(MSVC) +# set(CUDA_PROPAGATE_HOST_FLAGS OFF) + set(CUDA_HOST_COMPILATION_CPP ON) +# set(CUDA_NVCC_FLAGS -std=c++11 -g -Xcompiler -fexceptions -Xcompiler -fPIC) + set(CUDA_SEPARABLE_COMPILATION ON) +else() +# set(CUDA_PROPAGATE_HOST_FLAGS OFF) + set(CUDA_HOST_COMPILATION_CPP ON) + set(CUDA_NVCC_FLAGS -std=c++11 -g -Xcompiler -fexceptions -Xcompiler -fPIC) + set(CUDA_SEPARABLE_COMPILATION ON) +endif() + +set(CUDA_WARNING "cross-execution-space-call") +# new flags introduced in CUDA 9 set(CUDA_WARNING "reorder,cross-execution- +# space-call,deprecated-declarations") +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Werror ${CUDA_WARNING} -restrict") +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_61,code=sm_61") +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_52,code=sm_52") +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_50,code=sm_50") +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_75,code=sm_75") +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_70,code=sm_70") +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_72,code=sm_72") +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_80,code=sm_80") +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_86,code=sm_86") +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_86,code=sm_89") +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_89,code=sm_90") +set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_90,code=compute_75") + +SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +find_package(CUDNN REQUIRED) +find_package(TensorRT REQUIRED) + +message("TensorRT version: " ${TensorRT_VERSION}) + +include_directories(${OpenCV_INCLUDE_DIRS}) +include_directories(${CUDA_INCLUDE_DIRS}) +include_directories(${CUDNN_INCLUDE_DIR}) +include_directories(${TensorRT_INCLUDE_DIRS}) +include_directories(${PROJECT_SOURCE_DIR}/../../mtracking) + +file(GLOB TENSORRT_SOURCE_FILES *.cpp common/*.cpp) +file(GLOB TENSORRT_HEADER_FILES *.h* common/*.h*) + +file(GLOB TENSORRT_CUDA_FILES *.cu) + +cuda_add_library(${libname_rt} SHARED + ${TENSORRT_CUDA_FILES} + ${TENSORRT_SOURCE_FILES} + ${TENSORRT_HEADER_FILES}) + +#message("TensorRT OpenCV libraries:") +#message("${OpenCV_LIBS}") +#message(${OpenCV_DIR}) + +set(TensorRT_LIBRARIES ${TensorRT_LIBRARIES} ${TensorRT_nvinfer_LIBRARY} ${TensorRT_nvinfer_plugin_LIBRARY} ${TensorRT_nvonnxparser_LIBRARY}) + +message("TensorRT_LIBRARIES: ${TensorRT_LIBRARIES}") + + +set(TENSORRT_LIBS + ${OpenCV_LIBS} + #${CUDA_LIBRARIES} + ${CUDA_CUDART_LIBRARY} + #${CUDA_CUDA_LIBRARY} + ${CUDA_cublas_LIBRARY} + ${CUDA_curand_LIBRARY} + ${CUDNN_LIBRARY} + # ${LIB_PTHREAD} + ${TensorRT_LIBRARIES}) + +if (CMAKE_COMPILER_IS_GNUCXX) + set(TENSORRT_LIBS ${TENSORRT_LIBS} stdc++fs nvinfer_plugin nvonnxparser) +endif(CMAKE_COMPILER_IS_GNUCXX) + +message("TENSORRT_LIBS: ${TENSORRT_LIBS}") + +target_link_libraries(${libname_rt} ${TENSORRT_LIBS}) + +install(TARGETS ${libname_rt} + EXPORT MTTrackingExports + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + PUBLIC_HEADER DESTINATION include/${PROJECT_NAME}) + +set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "libs") diff --git a/src/Detector/tensorrt_onnx/DFINE_bb.hpp b/src/Detector/tensorrt_onnx/DFINE_bb.hpp new file mode 100644 index 000000000..a75bae2b9 --- /dev/null +++ b/src/Detector/tensorrt_onnx/DFINE_bb.hpp @@ -0,0 +1,121 @@ +#pragma once + +#include "YoloONNX.hpp" + +#include "nms.h" + +/// +/// \brief The DFINE_bb_onnx class +/// +class DFINE_bb_onnx : public YoloONNX +{ +public: + DFINE_bb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + inputTensorNames.push_back("orig_target_sizes"); + outputTensorNames.push_back("scores"); + outputTensorNames.push_back("labels"); + outputTensorNames.push_back("boxes"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector tmpBoxes; + + //0: name: images, size : 1x3x640x640 + //1: name: orig_target_sizes, size : 1x2 + //2: name: labels, size : 1x300 + //3: name: boxes, size : 1x300x4 + //4: name: scores, size : 1x300 + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + //std::cout << "m_resizedROI: " << m_resizedROI << ", frameSize: " << frameSize << ", fw_h: " << cv::Size2f(fw, fh) << ", m_inputDims: " << cv::Point3i(m_inputDims.d[1], m_inputDims.d[2], m_inputDims.d[3]) << std::endl; + + auto labels = (const int64_t*)outputs[1]; + auto boxes = outputs[2]; + auto scores = outputs[0]; + +#if 0 + std::cout << "scores mem:\n"; + for (size_t ii = 0; ii < 15; ++ii) + { + std::cout << ii << ": "; + for (size_t jj = 0; jj < 20; ++jj) + { + std::cout << scores[ii * 20 + jj] << " "; + } + std::cout << ";" << std::endl; + } + std::cout << std::endl; + + std::cout << "labels mem:\n"; + for (size_t ii = 0; ii < 15; ++ii) + { + std::cout << ii << ": "; + for (size_t jj = 0; jj < 20; ++jj) + { + std::cout << labels[ii * 20 + jj] << " "; + } + std::cout << ";" << std::endl; + } + std::cout << std::endl; + + std::cout << "boxes mem:\n"; + for (size_t ii = 0; ii < 15; ++ii) + { + std::cout << ii << ": "; + for (size_t jj = 0; jj < 20; ++jj) + { + std::cout << boxes[ii * 20 + jj] << " "; + } + std::cout << ";" << std::endl; + } + std::cout << std::endl; + + std::cout << "m_outpuDims[0].d[1] = " << m_outpuDims[0].d[1] << std::endl; +#endif + + for (size_t i = 0; i < static_cast(m_outpuDims[0].d[1]); ++i) + { + float classConf = scores[i]; + int64_t classId = labels[i]; + + //if (classId > 0) + // --classId; + + if (classConf >= m_params.m_confThreshold) + { + auto ind = i * m_outpuDims[1].d[2]; + float x = fw * (boxes[ind + 0] - m_resizedROI.x); + float y = fh * (boxes[ind + 1] - m_resizedROI.y); + float width = fw * (boxes[ind + 2] - boxes[ind + 0]); + float height = fh * (boxes[ind + 3] - boxes[ind + 1]); + + //std::cout << "ind = " << ind << ", boxes[0] = " << boxes[ind + 0] << ", boxes[1] = " << boxes[ind + 1] << ", boxes[2] = " << boxes[ind + 2] << ", boxes[3] = " << boxes[ind + 3] << std::endl; + //std::cout << "ind = " << ind << ", x = " << x << ", y = " << y << ", width = " << width << ", height = " << height << std::endl; + + tmpBoxes.emplace_back(classId, classConf, cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height))); + } + } + + std::vector resBoxes; + resBoxes.reserve(tmpBoxes.size()); + + nms3(tmpBoxes, resBoxes, static_cast(0.3), + [](const tensor_rt::Result& reg) { return reg.m_brect; }, + [](const tensor_rt::Result& reg) { return reg.m_prob; }, + [](const tensor_rt::Result& reg) { return reg.m_id; }, + 0, static_cast(0)); + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/DFINE_is.hpp b/src/Detector/tensorrt_onnx/DFINE_is.hpp new file mode 100644 index 000000000..84c46ed9f --- /dev/null +++ b/src/Detector/tensorrt_onnx/DFINE_is.hpp @@ -0,0 +1,194 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The DFINE_is_onnx class +/// +class DFINE_is_onnx : public YoloONNX +{ +public: + DFINE_is_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("input"); + outputTensorNames.push_back("logits"); + outputTensorNames.push_back("boxes"); + outputTensorNames.push_back("mask_probs"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: input, size: 1x3x640x640 + //1: name: logits, size: 1x300x80 + //2: name: boxes, size: 1x300x4 + //3: name: mask_probs, size: 1x300x160x160 + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + cv::Size inputSize(static_cast(m_inputDims[0].d[3]), static_cast(m_inputDims[0].d[2])); + cv::Size2f inputSizef(static_cast(inputSize.width), static_cast(inputSize.height)); + + //std::cout << "m_resizedROI: " << m_resizedROI << ", frameSize: " << frameSize << ", fw_h: " << cv::Size2f(fw, fh) << ", m_inputDims: " << cv::Point3i(m_inputDims.d[1], m_inputDims.d[2], m_inputDims.d[3]) << std::endl; + + int labelsInd = 0; + int detsInd = 1; + int segInd = 2; + + auto dets = outputs[detsInd]; + auto labels = outputs[labelsInd]; + + auto masks = outputs[segInd]; + + size_t ncInd = 2; + size_t lenInd = 1; + + + size_t nc = m_outpuDims[labelsInd].d[ncInd]; + size_t len = static_cast(m_outpuDims[detsInd].d[lenInd]) / m_params.m_explicitBatchSize; + auto volume0 = len * m_outpuDims[detsInd].d[ncInd]; // Volume(m_outpuDims[0]); + dets += volume0 * imgIdx; + auto volume1 = len * m_outpuDims[labelsInd].d[ncInd]; // Volume(m_outpuDims[0]); + labels += volume1 * imgIdx; + + int segChannels = static_cast(m_outpuDims[segInd].d[1]); + int segWidth = static_cast(m_outpuDims[segInd].d[2]); + int segHeight = static_cast(m_outpuDims[segInd].d[3]); + masks += imgIdx * segChannels * segWidth * segHeight; + + cv::Mat binaryMask8U(segHeight, segWidth, CV_8UC1); + + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.m_confThreshold << ", volume0 = " << volume0 << ", volume1 = " << volume1 << std::endl; + + auto L2Conf = [](float v) + { + return 1.f / (1.f + std::exp(-v)); + }; + + for (size_t i = 0; i < len; ++i) + { + float classConf = L2Conf(labels[0]); + size_t classId = 0; + for (size_t cli = 1; cli < nc; ++cli) + { + auto conf = L2Conf(labels[cli]); + if (classConf < conf) + { + classConf = conf; + classId = cli; + } + } + + if (classConf >= m_params.m_confThreshold) + { + float d0 = dets[0]; + float d1 = dets[1]; + float d2 = dets[2]; + float d3 = dets[3]; + + float x = fw * (inputSizef.width * (d0 - d2 / 2.f) - m_resizedROI.x); + float y = fh * (inputSizef.height * (d1 - d3 / 2.f) - m_resizedROI.y); + float width = fw * inputSizef.width * d2; + float height = fh * inputSizef.height * d3; + + //if (i == 0) + //{ + // std::cout << i << ": classConf = " << classConf << ", classId = " << classId << " (" << labels[classId] << "), rect = " << cv::Rect2f(x, y, width, height) << std::endl; + // std::cout << "dets = " << d0 << ", " << d1 << ", " << d2 << ", " << d3 << std::endl; + //} + resBoxes.emplace_back(classId, classConf, cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height))); + + double maskThreshold = 0.1; + for (int row = 0; row < segHeight; ++row) + { + const float* maskPtr = masks + row * segWidth; + uchar* binMaskPtr = binaryMask8U.ptr(row); + + for (int col = 0; col < segWidth; ++col) + { + binMaskPtr[col] = (maskPtr[col] > maskThreshold) ? 255 : 0; + } + } + + tensor_rt::Result& resObj = resBoxes.back(); + + cv::Rect smallRect; + smallRect.x = cvRound(segHeight * (d0 - d2 / 2.f)); + smallRect.y = cvRound(segHeight * (d1 - d3 / 2.f)); + smallRect.width = cvRound(segHeight * d2); + smallRect.height = cvRound(segHeight * d3); + smallRect = Clamp(smallRect, cv::Size(segWidth, segHeight)); + + if (smallRect.area() > 0) + { + cv::resize(binaryMask8U(smallRect), resObj.m_boxMask, resObj.m_brect.size(), 0, 0, cv::INTER_NEAREST); + +#if 0 + static int globalObjInd = 0; + SaveMat(mask, std::to_string(globalObjInd) + "_mask", ".png", "tmp", true); + SaveMat(binaryMask, std::to_string(globalObjInd) + "_bin_mask", ".png", "tmp", true); + SaveMat(binaryMask8U, std::to_string(globalObjInd) + "_bin_mask_8u", ".png", "tmp", true); + SaveMat(resObj.m_boxMask, std::to_string(globalObjInd++) + "_obj_mask", ".png", "tmp", true); + std::cout << "inputSize: " << inputSize << ", localRect: " << localRect << std::endl; +#endif + +#if 0 + std::vector> contours; +#if ((CV_VERSION_MAJOR > 4) || ((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR > 9))) + cv::findContoursLinkRuns(resObj.m_boxMask, contours); +#else + cv::findContours(resObj.m_boxMask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); +#endif + for (const auto& contour : contours) + { + cv::Rect br = cv::boundingRect(contour); + + if (br.width >= 4 && + br.height >= 4) + { + int dx = resObj.m_brect.x; + int dy = resObj.m_brect.y; + + cv::RotatedRect rr = (contour.size() < 5) ? cv::minAreaRect(contour) : cv::fitEllipse(contour); + rr.center.x = rr.center.x * fw + dx; + rr.center.y = rr.center.y * fw + dy; + rr.size.width *= fw; + rr.size.height *= fh; + + br.x = cvRound(dx + br.x * fw); + br.y = cvRound(dy + br.y * fh); + br.width = cvRound(br.width * fw); + br.height = cvRound(br.height * fh); + + resObj.m_brect = br; + //resObj.m_rrect = rr; + + //std::cout << "resBoxes[" << i << "] br: " << br << ", rr: (" << rr.size << " from " << rr.center << ", " << rr.angle << ")" << std::endl; + + break; + } + } +#endif + } + else + { + resObj.m_boxMask = cv::Mat(resObj.m_brect.size(), CV_8UC1, cv::Scalar(255)); + } + } + + dets += m_outpuDims[detsInd].d[ncInd]; + labels += m_outpuDims[labelsInd].d[ncInd]; + masks += segWidth * segHeight; + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/RFDETR_bb.hpp b/src/Detector/tensorrt_onnx/RFDETR_bb.hpp new file mode 100644 index 000000000..ea2c86ffc --- /dev/null +++ b/src/Detector/tensorrt_onnx/RFDETR_bb.hpp @@ -0,0 +1,113 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The RFDETR_bb_onnx class +/// +class RFDETR_bb_onnx : public YoloONNX +{ +public: + RFDETR_bb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("input"); + outputTensorNames.push_back("dets"); + outputTensorNames.push_back("labels"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: input, size : 1x3x560x560 + //1: name: dets, size : 1x300x4 + //2: name: labels, size : 1x300x91 + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + //std::cout << "m_resizedROI: " << m_resizedROI << ", frameSize: " << frameSize << ", fw_h: " << cv::Size2f(fw, fh) << ", m_inputDims: " << cv::Point3i(m_inputDims.d[1], m_inputDims.d[2], m_inputDims.d[3]) << std::endl; + + auto dets = outputs[0]; + auto labels = outputs[1]; + + size_t ncInd = 2; + size_t lenInd = 1; + + size_t nc = m_outpuDims[1].d[ncInd]; + size_t len = static_cast(m_outpuDims[0].d[lenInd]) / m_params.m_explicitBatchSize; + auto volume0 = len * m_outpuDims[0].d[ncInd]; // Volume(m_outpuDims[0]); + dets += volume0 * imgIdx; + auto volume1 = len * m_outpuDims[1].d[ncInd]; // Volume(m_outpuDims[0]); + labels += volume1 * imgIdx; + + + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume0 = " << volume0 << ", volume1 = " << volume1 << std::endl; + + //for (size_t i = 0; i < len; ++i) + //{ + // std::cout << "labels: "; + // for (size_t j = 0; j < m_outpuDims[1].d[ncInd]; ++j) + // { + // std::cout << labels[j] << " | "; + // } + // std::cout << std::endl; + // + // std::cout << "dets: "; + // for (size_t j = 0; j < m_outpuDims[0].d[ncInd]; ++j) + // { + // std::cout << dets[j] << " | "; + // } + // std::cout << std::endl; + //} + + + auto L2Conf = [](float v) + { + return 1.f / (1.f + std::exp(-v)); + }; + + for (size_t i = 0; i < len; ++i) + { + float classConf = L2Conf(labels[0]); + size_t classId = 0; + for (size_t cli = 1; cli < nc; ++cli) + { + auto conf = L2Conf(labels[cli]); + if (classConf < conf) + { + classConf = conf; + classId = cli; + } + } + if (classId > 0) + --classId; + + if (classConf >= m_params.m_confThreshold) + { + float x = fw * (m_inputDims[0].d[2] * (dets[0] - dets[2] / 2.f) - m_resizedROI.x); + float y = fh * (m_inputDims[0].d[3] * (dets[1] - dets[3] / 2.f) - m_resizedROI.y); + float width = fw * m_inputDims[0].d[2] * dets[2]; + float height = fh * m_inputDims[0].d[3] * dets[3]; + + //if (i == 0) + //{ + // std::cout << i << ": classConf = " << classConf << ", classId = " << classId << " (" << labels[classId] << "), rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + // std::cout << "dets = " << dets[0] << ", " << dets[1] << ", " << dets[2] << ", " << dets[3] << std::endl; + //} + resBoxes.emplace_back(classId, classConf, cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height))); + } + + dets += m_outpuDims[0].d[ncInd]; + labels += m_outpuDims[1].d[ncInd]; + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/RFDETR_is.hpp b/src/Detector/tensorrt_onnx/RFDETR_is.hpp new file mode 100644 index 000000000..d2f1988ed --- /dev/null +++ b/src/Detector/tensorrt_onnx/RFDETR_is.hpp @@ -0,0 +1,188 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The RFDETR_is_onnx class +/// +class RFDETR_is_onnx : public YoloONNX +{ +public: + RFDETR_is_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("input"); + outputTensorNames.push_back("dets"); + outputTensorNames.push_back("labels"); + outputTensorNames.push_back("4245"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: input, size: 1x3x432x432 + //1: name: dets, size: 1x200x4 + //2: name: labels, size: 1x200x91 + //3: name: 4245, size: 1x200x108x108 + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + cv::Size inputSize(static_cast(m_inputDims[0].d[3]), static_cast(m_inputDims[0].d[2])); + cv::Size2f inputSizef(static_cast(inputSize.width), static_cast(inputSize.height)); + + //std::cout << "m_resizedROI: " << m_resizedROI << ", frameSize: " << frameSize << ", fw_h: " << cv::Size2f(fw, fh) << ", m_inputDims: " << cv::Point3i(m_inputDims.d[1], m_inputDims.d[2], m_inputDims.d[3]) << std::endl; + + auto dets = outputs[0]; + auto labels = outputs[1]; + + int segInd = 2; + auto masks = outputs[segInd]; + + size_t ncInd = 2; + size_t lenInd = 1; + + + size_t nc = m_outpuDims[1].d[ncInd]; + size_t len = static_cast(m_outpuDims[0].d[lenInd]) / m_params.m_explicitBatchSize; + auto volume0 = len * m_outpuDims[0].d[ncInd]; // Volume(m_outpuDims[0]); + dets += volume0 * imgIdx; + auto volume1 = len * m_outpuDims[1].d[ncInd]; // Volume(m_outpuDims[0]); + labels += volume1 * imgIdx; + + int segChannels = static_cast(m_outpuDims[segInd].d[1]); + int segWidth = static_cast(m_outpuDims[segInd].d[2]); + int segHeight = static_cast(m_outpuDims[segInd].d[3]); + masks += imgIdx * segChannels * segWidth * segHeight; + + cv::Mat binaryMask8U(segHeight, segWidth, CV_8UC1); + + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume0 = " << volume0 << ", volume1 = " << volume1 << std::endl; + + auto L2Conf = [](float v) + { + return 1.f / (1.f + std::exp(-v)); + }; + + for (size_t i = 0; i < len; ++i) + { + float classConf = L2Conf(labels[0]); + size_t classId = 0; + for (size_t cli = 1; cli < nc; ++cli) + { + auto conf = L2Conf(labels[cli]); + if (classConf < conf) + { + classConf = conf; + classId = cli; + } + } + if (classId > 0) + --classId; + + if (classConf >= m_params.m_confThreshold) + { + float x = fw * (inputSizef.width * (dets[0] - dets[2] / 2.f) - m_resizedROI.x); + float y = fh * (inputSizef.height * (dets[1] - dets[3] / 2.f) - m_resizedROI.y); + float width = fw * inputSizef.width * dets[2]; + float height = fh * inputSizef.height * dets[3]; + + //if (i == 0) + //{ + // std::cout << i << ": classConf = " << classConf << ", classId = " << classId << " (" << labels[classId] << "), rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + // std::cout << "dets = " << dets[0] << ", " << dets[1] << ", " << dets[2] << ", " << dets[3] << std::endl; + //} + resBoxes.emplace_back(classId, classConf, cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height))); + + double maskThreshold = 0.1; + for (int row = 0; row < segHeight; ++row) + { + const float* maskPtr = masks + row * segWidth; + uchar* binMaskPtr = binaryMask8U.ptr(row); + + for (int col = 0; col < segWidth; ++col) + { + binMaskPtr[col] = (maskPtr[col] > maskThreshold) ? 255 : 0; + } + } + + tensor_rt::Result& resObj = resBoxes.back(); + + cv::Rect smallRect; + smallRect.x = cvRound(segHeight * (dets[0] - dets[2] / 2.f)); + smallRect.y = cvRound(segHeight * (dets[1] - dets[3] / 2.f)); + smallRect.width = cvRound(segHeight * dets[2]); + smallRect.height = cvRound(segHeight * dets[3]); + smallRect = Clamp(smallRect, cv::Size(segWidth, segHeight)); + + if (smallRect.area() > 0) + { + cv::resize(binaryMask8U(smallRect), resObj.m_boxMask, resObj.m_brect.size(), 0, 0, cv::INTER_NEAREST); + +#if 0 + static int globalObjInd = 0; + SaveMat(mask, std::to_string(globalObjInd) + "_mask", ".png", "tmp", true); + SaveMat(binaryMask, std::to_string(globalObjInd) + "_bin_mask", ".png", "tmp", true); + SaveMat(binaryMask8U, std::to_string(globalObjInd) + "_bin_mask_8u", ".png", "tmp", true); + SaveMat(resObj.m_boxMask, std::to_string(globalObjInd++) + "_obj_mask", ".png", "tmp", true); + std::cout << "inputSize: " << inputSize << ", localRect: " << localRect << std::endl; +#endif + +#if 0 + std::vector> contours; +#if ((CV_VERSION_MAJOR > 4) || ((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR > 9))) + cv::findContoursLinkRuns(resObj.m_boxMask, contours); +#else + cv::findContours(resObj.m_boxMask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); +#endif + for (const auto& contour : contours) + { + cv::Rect br = cv::boundingRect(contour); + + if (br.width >= 4 && + br.height >= 4) + { + int dx = resObj.m_brect.x; + int dy = resObj.m_brect.y; + + cv::RotatedRect rr = (contour.size() < 5) ? cv::minAreaRect(contour) : cv::fitEllipse(contour); + rr.center.x = rr.center.x * fw + dx; + rr.center.y = rr.center.y * fw + dy; + rr.size.width *= fw; + rr.size.height *= fh; + + br.x = cvRound(dx + br.x * fw); + br.y = cvRound(dy + br.y * fh); + br.width = cvRound(br.width * fw); + br.height = cvRound(br.height * fh); + + resObj.m_brect = br; + //resObj.m_rrect = rr; + + //std::cout << "resBoxes[" << i << "] br: " << br << ", rr: (" << rr.size << " from " << rr.center << ", " << rr.angle << ")" << std::endl; + + break; + } + } +#endif + } + else + { + resObj.m_boxMask = cv::Mat(resObj.m_brect.size(), CV_8UC1, cv::Scalar(255)); + } + } + + dets += m_outpuDims[0].d[ncInd]; + labels += m_outpuDims[1].d[ncInd]; + masks += segWidth * segHeight; + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNX.cpp b/src/Detector/tensorrt_onnx/YoloONNX.cpp new file mode 100644 index 000000000..aa4d23a65 --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNX.cpp @@ -0,0 +1,496 @@ +#include + +#define DEFINE_TRT_ENTRYPOINTS 1 + +#include "YoloONNX.hpp" +#include "../../mtracking/defines.h" + +//! +//! \brief Creates the network, configures the builder and creates the network engine +//! +//! \details This function creates the YOLO network by parsing the ONNX model and builds +//! the engine that will be used to run YOLO (m_engine) +//! +//! \return Returns true if the engine was created successfully and false otherwise +//! +bool YoloONNX::Init(const SampleYoloParams& params) +{ + bool res = false; + + m_params = params; + + initLibNvInferPlugins(&sample::gLogger.getTRTLogger(), ""); + + auto GetBindings = [&]() + { + auto numBindings = m_engine->getNbIOTensors(); + + m_inputDims.clear(); + m_outpuDims.clear(); + + std::cout << "** Bindings: " << numBindings << " **" << std::endl; + for (int32_t i = 0; i < numBindings; ++i) + { + std::string bindName = m_engine->getIOTensorName(i); + nvinfer1::Dims dim = m_engine->getTensorShape(bindName.c_str()); + + for (const auto& inName : m_params.m_inputTensorNames) + { + if (bindName == inName) + { + m_inputDims.emplace_back(dim); + break; + } + } + + for (const auto& outName : m_params.m_outputTensorNames) + { + if (bindName == outName) + { + m_outpuDims.emplace_back(dim); + break; + } + } + + std::cout << i << ": name: " << bindName; + std::cout << ", size: "; + for (int j = 0; j < dim.nbDims; ++j) + { + std::cout << dim.d[j]; + if (j < dim.nbDims - 1) + std::cout << "x"; + } + std::cout << std::endl; + } + }; + + if (fs::exists(m_params.m_engineFileName)) + { + std::vector trtModelStream; + size_t size{0}; + std::ifstream file(m_params.m_engineFileName, std::ios::binary); + if (file.good()) + { + file.seekg(0, file.end); + size = file.tellg(); + file.seekg(0, file.beg); + trtModelStream.resize(size); + file.read(trtModelStream.data(), size); + file.close(); + } + + nvinfer1::IRuntime* infer = nvinfer1::createInferRuntime(sample::gLogger); + if (m_params.m_dlaCore >= 0) + infer->setDLACore(m_params.m_dlaCore); + + m_engine = std::shared_ptr(infer->deserializeCudaEngine(trtModelStream.data(), size), samplesCommon::InferDeleter()); +#if (NV_TENSORRT_MAJOR < 8) + infer->destroy(); +#else + //delete infer; +#endif + + if (m_engine) + { + GetBindings(); + res = true; + } + else + { + res = true; + } + sample::gLogInfo << "TRT Engine loaded from: " << m_params.m_engineFileName << " with res = " << res << std::endl; + } + else + { + auto builder = YoloONNXUniquePtr(nvinfer1::createInferBuilder(sample::gLogger.getTRTLogger())); + if (!builder) + return false; + + const auto explicitBatch = 1U << static_cast(nvinfer1::NetworkDefinitionCreationFlag::kEXPLICIT_BATCH); + auto network = YoloONNXUniquePtr(builder->createNetworkV2(explicitBatch)); + if (!network) + return false; + + auto parser = YoloONNXUniquePtr(nvonnxparser::createParser(*network, sample::gLogger.getTRTLogger())); + if (!parser) + return false; + + auto config = YoloONNXUniquePtr(builder->createBuilderConfig()); + if (!config) + return false; + + auto constructed = ConstructNetwork(builder, network, config, parser); + if (!constructed) + return false; + + GetBindings(); + assert(m_inputDims[0].nbDims == 4); + + res = true; + } + + std::cout << "YoloONNX::Init: loaded = " << res << std::endl; + + if (res) + { + m_buffers = std::make_unique(m_engine, 0/*m_params.batchSize*/); + std::cout << "YoloONNX::Init: m_buffers = " << (m_buffers != nullptr) << std::endl; + m_context = YoloONNXUniquePtr(m_engine->createExecutionContext()); + std::cout << "YoloONNX::Init: m_context = " << (m_context != nullptr) << std::endl; + if (!m_context) + res = false; + } + + std::cout << "YoloONNX::Init: res = " << res << std::endl; + + return res; +} + +//! +//! \brief Uses an onnx parser to create the YOLO Network and marks the +//! output layers +//! +//! \param network Pointer to the network that will be populated with the YOLO network +//! +//! \param builder Pointer to the engine builder +//! +bool YoloONNX::ConstructNetwork(YoloONNXUniquePtr& builder, + YoloONNXUniquePtr& network, + YoloONNXUniquePtr& config, + YoloONNXUniquePtr& parser) +{ + bool res = false; + + // Parse ONNX model file to populate TensorRT INetwork + //int verbosity = (int) nvinfer1::ILogger::Severity::kERROR; + int verbosity = (int)nvinfer1::ILogger::Severity::kVERBOSE; + + sample::gLogInfo << "Parsing ONNX file: " << m_params.m_onnxFileName << std::endl; + + if (!parser->parseFromFile(m_params.m_onnxFileName.c_str(), verbosity)) + { + sample::gLogError << "Unable to parse ONNX model file: " << m_params.m_onnxFileName << std::endl; + return res; + } + + { + nvinfer1::IOptimizationProfile* profile = builder->createOptimizationProfile(); + + //std::cout << "m_params.inputTensorNames.size = " << m_params.inputTensorNames.size() << ", m_inputDims.size = " << m_inputDims.size() << std::endl; + if (m_params.m_inputTensorNames.size() > 0) + { + nvinfer1::Dims dim = network->getInput(0)->getDimensions(); + //std::cout << "dim[0] = " << dim.nbDims << ": [" << dim.d[0] << ", " << dim.d[1] << ", " << dim.d[2] << ", " << dim.d[3] << "]" << std::endl; + if (dim.d[0] < 1) + dim.d[0] = 1; // batch size + if (dim.d[1] < 1) + dim.d[1] = 3; // Channels + if (dim.d[2] < 1) + dim.d[2] = 640; // Width + if (dim.d[3] < 1) + dim.d[3] = 640; // Height + + profile->setDimensions(m_params.m_inputTensorNames[0].c_str(), nvinfer1::OptProfileSelector::kMIN, dim); + profile->setDimensions(m_params.m_inputTensorNames[0].c_str(), nvinfer1::OptProfileSelector::kOPT, dim); + profile->setDimensions(m_params.m_inputTensorNames[0].c_str(), nvinfer1::OptProfileSelector::kMAX, dim); + } + + // For D-FINE + if (m_params.m_inputTensorNames.size() > 1) + { + nvinfer1::Dims dim = network->getInput(1)->getDimensions(); + //std::cout << "dim[1] = " << dim.nbDims << ": [" << dim.d[0] << ", " << dim.d[1] << "]" << std::endl; + if (dim.d[0] < 1) + dim.d[0] = 1; // batch size + if (dim.d[1] < 1) + dim.d[1] = 2; // Input size + + profile->setDimensions(m_params.m_inputTensorNames[1].c_str(), nvinfer1::OptProfileSelector::kMIN, dim); + profile->setDimensions(m_params.m_inputTensorNames[1].c_str(), nvinfer1::OptProfileSelector::kOPT, dim); + profile->setDimensions(m_params.m_inputTensorNames[1].c_str(), nvinfer1::OptProfileSelector::kMAX, dim); + } + + config->addOptimizationProfile(profile); + } + +#if (NV_TENSORRT_MAJOR < 8) + builder->setMaxBatchSize(m_params.batchSize); + config->setMaxWorkspaceSize(m_params.videoMemory ? m_params.videoMemory : 4096_MiB); +#else + size_t workspaceSize = config->getMemoryPoolLimit(nvinfer1::MemoryPoolType::kWORKSPACE); + size_t dlaManagedSRAMSize = config->getMemoryPoolLimit(nvinfer1::MemoryPoolType::kDLA_MANAGED_SRAM); + size_t dlaLocalDRAMSize = config->getMemoryPoolLimit(nvinfer1::MemoryPoolType::kDLA_LOCAL_DRAM); + size_t dlaGlobalDRAMSize = config->getMemoryPoolLimit(nvinfer1::MemoryPoolType::kDLA_GLOBAL_DRAM); + std::cout << "m_params.videoMemory = " << m_params.m_videoMemory << ", workspaceSize = " << workspaceSize << ", dlaManagedSRAMSize = " << dlaManagedSRAMSize << ", dlaLocalDRAMSize = " << dlaLocalDRAMSize << ", dlaGlobalDRAMSize = " << dlaGlobalDRAMSize << std::endl; + + config->setMemoryPoolLimit(nvinfer1::MemoryPoolType::kWORKSPACE, m_params.m_videoMemory ? m_params.m_videoMemory : workspaceSize); +#endif + + config->setFlag(nvinfer1::BuilderFlag::kGPU_FALLBACK); + + switch (m_params.m_precision) + { + case tensor_rt::Precision::FP16: + config->setFlag(nvinfer1::BuilderFlag::kFP16); + break; + + case tensor_rt::Precision::INT8: + { + // Calibrator life time needs to last until after the engine is built. + std::unique_ptr calibrator; + + BatchStream calibrationStream(m_params.m_explicitBatchSize, m_params.m_nbCalBatches, m_params.m_calibrationBatches, m_params.m_dataDirs); + calibrator.reset(new Int8EntropyCalibrator2(calibrationStream, 0, "Yolo", m_params.m_inputTensorNames[0].c_str())); + config->setFlag(nvinfer1::BuilderFlag::kINT8); + config->setInt8Calibrator(calibrator.get()); + } + break; + + default: + break; + } + + // Enable DLA if mParams.dlaCore is true + samplesCommon::enableDLA(builder.get(), config.get(), m_params.m_dlaCore); + + sample::gLogInfo << "Building TensorRT engine: " << m_params.m_engineFileName << std::endl; + +#if (NV_TENSORRT_MAJOR < 8) + m_engine = std::shared_ptr(builder->buildEngineWithConfig(*network, *config), samplesCommon::InferDeleter()); +#else + nvinfer1::IRuntime* infer = nvinfer1::createInferRuntime(sample::gLogger); + if (m_params.m_dlaCore >= 0) + infer->setDLACore(m_params.m_dlaCore); + nvinfer1::IHostMemory* mem = builder->buildSerializedNetwork(*network, *config); + if (mem) + m_engine = std::shared_ptr(infer->deserializeCudaEngine(mem->data(), mem->size()), samplesCommon::InferDeleter()); + else + sample::gLogError << "Unable to buildSerializedNetwork" << std::endl; + delete infer; +#endif + + if (!m_engine) + return res; + + if (m_params.m_engineFileName.size() > 0) + { + std::ofstream p(m_params.m_engineFileName, std::ios::binary); + if (!p) + return false; + + nvinfer1::IHostMemory* ptr = m_engine->serialize(); + assert(ptr); + p.write(reinterpret_cast(ptr->data()), ptr->size()); +#if (NV_TENSORRT_MAJOR < 8) + ptr->destroy(); +#else + delete ptr; +#endif + p.close(); + sample::gLogInfo << "TRT Engine file saved to: " << m_params.m_engineFileName << std::endl; + } + res = true; + + return res; +} + +/// +/// \brief YoloONNX::Detect +/// \param frames +/// \param bboxes +/// \return +/// +bool YoloONNX::Detect(const std::vector& frames, std::vector& bboxes) +{ + // Read the input data into the managed buffers + if (!ProcessInputAspectRatio(frames)) + return false; + + // Memcpy from host input buffers to device input buffers + m_buffers->copyInputToDevice(); + + bool status = m_context->executeV2(m_buffers->getDeviceBindings().data()); + if (!status) + return false; + + // Memcpy from device output buffers to host output buffers + m_buffers->copyOutputToHost(); + + // Post-process detections and verify results + bboxes.resize(frames.size()); + for (size_t i = 0; i < bboxes.size(); ++i) + { + VerifyOutputAspectRatio(i, bboxes[i], frames[i].size()); + } + + return true; +} + +/// +/// \brief YoloONNX::GetInputSize +/// \return Return input size +/// +cv::Size YoloONNX::GetInputSize() const +{ + return cv::Size(static_cast(m_inputDims[0].d[3]), static_cast(m_inputDims[0].d[2])); +} + +/// +/// \brief YoloONNX::GetNumClasses +/// \return +/// +size_t YoloONNX::GetNumClasses() const +{ + if (m_outpuDims[0].nbDims == 2) // with nms + { + return 0; + } + else + { + size_t ncInd = 2; + int nc = static_cast(m_outpuDims[0].d[ncInd] - 5); + return (size_t)nc; + } +} + +//! +//! \brief Reads the input and mean data, preprocesses, and stores the result in a managed buffer +//! +bool YoloONNX::ProcessInputAspectRatio(const std::vector& sampleImages) +{ + const int inputB = static_cast(m_inputDims[0].d[0]); + const int inputC = static_cast(m_inputDims[0].d[1]); + const int inputH = static_cast(m_inputDims[0].d[2]); + const int inputW = static_cast(m_inputDims[0].d[3]); + + float* hostInputBuffer = nullptr; + if (m_params.m_inputTensorNames[0].empty()) + hostInputBuffer = static_cast(m_buffers->getHostBuffer(0)); + else + hostInputBuffer = static_cast(m_buffers->getHostBuffer(m_params.m_inputTensorNames[0])); + + if (static_cast(m_inputChannels.size()) < inputB) + { + for (int b = 0; b < inputB; ++b) + { + m_inputChannels.push_back(std::vector {static_cast(inputC)}); + } + } + + m_resizedROI = cv::Rect(0, 0, inputW, inputH); + +#if 1 + // resize the image with scale + const float imgHeight = static_cast(sampleImages[0].rows); + const float imgWidth = static_cast(sampleImages[0].cols); + float dim = std::max(imgHeight, imgWidth); + int resizeH = cvRound((imgHeight * inputH) / dim); + int resizeW = cvRound((imgWidth * inputW) / dim); + //float scalingFactor = static_cast(resizeH) / imgHeight; + + // Additional checks for images with non even dims + if ((inputW - resizeW) % 2) + resizeW--; + if ((inputH - resizeH) % 2) + resizeH--; + assert((inputW - resizeW) % 2 == 0); + assert((inputH - resizeH) % 2 == 0); + + float xOffset = (inputW - resizeW) / 2.f; + float yOffset = (inputH - resizeH) / 2.f; + + assert(2 * xOffset + resizeW == inputW); + assert(2 * yOffset + resizeH == inputH); + + cv::Size scaleSize(inputW, inputH); + m_resizedROI = cv::Rect(cvRound(xOffset), cvRound(yOffset), resizeW, resizeH); + + //std::cout << "m_resizedROI: " << m_resizedROI << ", frameSize: " << sampleImages[0].size() << ", resizeW_H: " << cv::Size2f(resizeW, resizeH) << std::endl; + + if (m_resizedBatch.size() < sampleImages.size()) + m_resizedBatch.resize(sampleImages.size()); + + // Each element in batch share the same image matrix + for (int b = 0; b < inputB; ++b) + { + if (m_resizedBatch[b].size() != scaleSize) + m_resizedBatch[b] = cv::Mat(scaleSize, sampleImages[b].type(), cv::Scalar::all(128)); + cv::resize(sampleImages[b], cv::Mat(m_resizedBatch[b], m_resizedROI), m_resizedROI.size(), 0, 0, cv::INTER_LINEAR); + cv::split(m_resizedBatch[b], m_inputChannels[b]); + std::swap(m_inputChannels[b][0], m_inputChannels[b][2]); + } +#else + auto scaleSize = cv::Size(inputW, inputH); + + if (m_resizedBatch.size() < sampleImages.size()) + m_resizedBatch.resize(sampleImages.size()); + + // Each element in batch share the same image matrix + for (int b = 0; b < inputB; ++b) + { + cv::resize(sampleImages[b], m_resizedBatch[b], scaleSize, 0, 0, cv::INTER_LINEAR); + cv::split(m_resizedBatch[b], m_inputChannels[b]); + std::swap(m_inputChannels[b][0], m_inputChannels[b][2]); + } +#endif + + int volBatch = inputC * inputH * inputW; + int volChannel = inputH * inputW; + + constexpr float to1 = 1.f / 255.0f; + + int d_batch_pos = 0; + for (int b = 0; b < inputB; ++b) + { + int d_c_pos = d_batch_pos; + for (int c = 0; c < inputC; ++c) + { + m_inputChannels[b][c].convertTo(cv::Mat(inputH, inputW, CV_32FC1, &hostInputBuffer[d_c_pos]), CV_32FC1, to1, 0); + d_c_pos += volChannel; + } + d_batch_pos += volBatch; + } + + // For D-FINE + if (m_params.m_inputTensorNames.size() > 1) + { + int64_t* hostInput2 = static_cast(m_buffers->getHostBuffer(m_params.m_inputTensorNames[1])); + hostInput2[0] = inputW; + hostInput2[1] = inputH; + } + return true; +} + +//! +//! \brief Filters output detections and verify result +//! +//! \return whether the detection output matches expectations +//! +bool YoloONNX::VerifyOutputAspectRatio(size_t imgIdx, std::vector& nms_bboxes, cv::Size frameSize) +{ + std::vector outputs; + for (size_t i = 0; i < m_params.m_outputTensorNames.size();) + { + float* output = static_cast(m_buffers->getHostBuffer(m_params.m_outputTensorNames[i])); +#if 0 + if (output) + outputs.push_back(output); +#else + if (!output) + { + std::cout << i << " output tensor \"" << m_params.m_outputTensorNames[i] << "\" is null, will be removed" << std::endl; + m_params.m_outputTensorNames.erase(std::begin(m_params.m_outputTensorNames) + i); + } + else + { + outputs.push_back(output); + ++i; + } +#endif + } + if (!outputs.empty()) + nms_bboxes = GetResult(imgIdx, m_params.m_keepTopK, outputs, frameSize); + + return !outputs.empty(); +} diff --git a/src/Detector/tensorrt_onnx/YoloONNX.hpp b/src/Detector/tensorrt_onnx/YoloONNX.hpp new file mode 100644 index 000000000..2452f61d2 --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNX.hpp @@ -0,0 +1,113 @@ +#pragma once + +#include "common/BatchStream.h" +#include "common/EntropyCalibrator.h" +#include "common/buffers.h" +#include "common/common.h" +#include "common/logger.h" + +#include "NvOnnxParser.h" +#include "NvInfer.h" +#include +#include +#include +#include +#include +#include + +#include +#include "class_detector.h" + +//! +//! \brief The SampleYoloParams structure groups the additional parameters required by +//! the SSD sample. +//! +struct SampleYoloParams +{ + int m_keepTopK = 1000; //!< The maximum number of detection post-NMS + int m_nbCalBatches = 100; //!< The number of batches for calibration + float m_confThreshold = 0.3; + float m_nmsThreshold = 0.5; + + size_t m_videoMemory = 0 ; //!< If zero then will use default value + + int m_explicitBatchSize = 1; + std::string m_calibrationBatches; //!< The path to calibration batches + std::string m_engineFileName; + + std::string m_onnxFileName; //!< Filename of ONNX file of a network + int32_t m_dlaCore{-1}; //!< Specify the DLA core to run network on. + tensor_rt::ModelType m_netType { tensor_rt::ModelType::YOLOV7 }; + tensor_rt::Precision m_precision { tensor_rt::Precision::FP32 }; //!< Allow runnning the network in Int8 mode. + std::vector m_dataDirs; //!< Directory paths where sample data files are stored + std::vector m_inputTensorNames; + std::vector m_outputTensorNames; +}; + +/// +/// \brief The YoloONNX class +/// +class YoloONNX +{ + template + using YoloONNXUniquePtr = std::unique_ptr; + +public: + YoloONNX() = default; + virtual ~YoloONNX() = default; + + //! + //! \brief Function builds the network engine + //! + bool Init(const SampleYoloParams& params); + + //! + //! \brief Runs the TensorRT inference engine for this sample + //! + bool Detect(const std::vector& frames, std::vector& bboxes); + + //! + //! \brief Return input size + //! + cv::Size GetInputSize() const; + + //! + //! \brief Return classes count + //! + size_t GetNumClasses() const; + +protected: + SampleYoloParams m_params; //!< The parameters for the sample + std::vector m_inputDims; //!< The dimensions of the input to the network + std::vector m_outpuDims; //!< The dimensions of the input to the network + cv::Rect m_resizedROI; //!< Input frame resized into input dimensions with the frame aspect ratio + + virtual std::vector GetResult(size_t imgIdx, int keep_topk, const std::vector& outputs, cv::Size frameSize) = 0; + +private: + std::shared_ptr m_engine; //!< The TensorRT engine used to run the network + + cv::Mat m_resized; + std::vector m_resizedBatch; + std::vector> m_inputChannels; + + std::unique_ptr m_buffers; + YoloONNXUniquePtr m_context; + + //! + //! \brief Parses an ONNX model for YOLO and creates a TensorRT network + //! + bool ConstructNetwork(YoloONNXUniquePtr& builder, + YoloONNXUniquePtr& network, YoloONNXUniquePtr& config, + YoloONNXUniquePtr& parser); + + //! + //! \brief Reads the input and mean data, preprocesses, and stores the result in a managed buffer + //! + bool ProcessInputAspectRatio(const std::vector& sampleImages); + + //! + //! \brief Filters output detections and verify results + //! + bool VerifyOutputAspectRatio(size_t imgIdx, std::vector& nms_bboxes, cv::Size frameSize); +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv10_bb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv10_bb.hpp new file mode 100644 index 000000000..6fdabb43c --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv10_bb.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv10_bb_onnx class +/// +class YOLOv10_bb_onnx : public YoloONNX +{ +public: + YOLOv10_bb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: images, size: 1x3x640x640 + //1: name: output0, size: 1x300x6 + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + auto output = outputs[0]; + + size_t ncInd = 2; + size_t lenInd = 1; + size_t len = static_cast(m_outpuDims[0].d[lenInd]);// / m_params.explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[0].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * 6; + + //if (i == 0) + // std::cout << i << ": " << output[k + 0] << " " << output[k + 1] << " " << output[k + 2] << " " << output[k + 3] << " " << output[k + 4] << " " << output[k + 5] << std::endl; + + float x = fw * (output[k + 0] - m_resizedROI.x); + float y = fh * (output[k + 1] - m_resizedROI.y); + float width = fw * (output[k + 2] - output[k + 0]); + float height = fh * (output[k + 3] - output[k + 1]); + float objectConf = output[k + 4]; + int classId = cvRound(output[k + 5]); + //if (i == 0) + // std::cout << i << ": object_conf = " << objectConf << ", classId = " << classId << ", rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + + if (objectConf >= m_params.m_confThreshold) + { + classIds.push_back(classId); + confidences.push_back(objectConf); + + // (center x, center y, width, height) to (x, y, w, h) + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], rectBoxes[indices[bi]]); + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv11_bb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv11_bb.hpp new file mode 100644 index 000000000..972ab2f55 --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv11_bb.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv11_bb_onnx class +/// +class YOLOv11_bb_onnx : public YoloONNX +{ +public: + YOLOv11_bb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: images, size: 1x3x640x640 + //1: name: output0, size: 1x84x8400 + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + auto output = outputs[0]; + + size_t ncInd = 1; + size_t lenInd = 2; + int nc = static_cast(m_outpuDims[0].d[ncInd] - 4); + int dimensions = nc + 4; + size_t len = static_cast(m_outpuDims[0].d[lenInd]);// / m_params.explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[0].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + cv::Mat rawMemory(1, dimensions * static_cast(len), CV_32FC1, output); + rawMemory = rawMemory.reshape(1, dimensions); + cv::transpose(rawMemory, rawMemory); + output = (float*)rawMemory.data; + + //std::cout << "output[0] mem:\n"; + //for (size_t ii = 0; ii < 100; ++ii) + //{ + // std::cout << ii << ": "; + // for (size_t jj = 0; jj < 20; ++jj) + // { + // std::cout << output[ii * 20 + jj] << " "; + // } + // std::cout << ";" << std::endl; + //} + //std::cout << ";" << std::endl; + + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * (nc + 4); + + int classId = -1; + float objectConf = 0.f; + for (int j = 0; j < nc; ++j) + { + const float classConf = output[k + 4 + j]; + if (classConf > objectConf) + { + classId = j; + objectConf = classConf; + } + } + + //if (i == 0) + // std::cout << i << ": object_conf = " << object_conf << ", class_conf = " << class_conf << ", classId = " << classId << ", rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + + if (objectConf >= m_params.m_confThreshold) + { + classIds.push_back(classId); + confidences.push_back(objectConf); + + // (center x, center y, width, height) to (x, y, w, h) + float x = fw * (output[k] - output[k + 2] / 2 - m_resizedROI.x); + float y = fh * (output[k + 1] - output[k + 3] / 2 - m_resizedROI.y); + float width = fw * output[k + 2]; + float height = fh * output[k + 3]; + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], rectBoxes[indices[bi]]); + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv11_instance.hpp b/src/Detector/tensorrt_onnx/YoloONNXv11_instance.hpp new file mode 100644 index 000000000..641c5c7dc --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv11_instance.hpp @@ -0,0 +1,308 @@ +#pragma once + +#include "YoloONNX.hpp" +#include "../../mtracking/defines.h" + +/// +/// \brief The YOLOv11_instance_onnx class +/// +class YOLOv11_instance_onnx : public YoloONNX +{ +public: + YOLOv11_instance_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + outputTensorNames.push_back("output1"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + size_t outInd = 0; + size_t segInd = 1; + + auto output = outputs[outInd]; + + //std::cout << "output[1] mem:\n"; + //auto output1 = outputs[1]; + //for (size_t ii = 0; ii < 100; ++ii) + //{ + // std::cout << ii << ": "; + // for (size_t jj = 0; jj < 20; ++jj) + // { + // std::cout << output1[ii * 20 + jj] << " "; + // } + // std::cout << ";" << std::endl; + //} + //std::cout << ";" << std::endl; + + //0: name: images, size: 1x3x640x640 + //1: name: output0, size: 1x116x8400 + //2: name: output1, size: 1x32x160x160 + // 25200 = 3x80x80 + 3x40x40 + 3x20x20 + // 116 = x, y, w, h, 80 classes, 32 seg ancors + // 80 * 8 = 640, 40 * 16 = 640, 20 * 32 = 640 + + size_t ncInd = 1; + size_t lenInd = 2; + int nc = static_cast(m_outpuDims[outInd].d[ncInd] - 4 - 32); + int dimensions = nc + 32 + 4; + size_t len = static_cast(m_outpuDims[outInd].d[lenInd]);// / m_params.explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[outInd].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + cv::Mat rawMemory(1, dimensions * static_cast(len), CV_32FC1, output); + rawMemory = rawMemory.reshape(1, dimensions); + cv::transpose(rawMemory, rawMemory); + output = (float*)rawMemory.data; + + //std::cout << "output[0] mem:\n"; + //for (size_t ii = 0; ii < 100; ++ii) + //{ + // std::cout << ii << ": "; + // for (size_t jj = 0; jj < 20; ++jj) + // { + // std::cout << output[ii * 20 + jj] << " "; + // } + // std::cout << ";" << std::endl; + //} + //std::cout << ";" << std::endl; + +#if 1 + int segWidth = 160; + int segHeight = 160; + int segChannels = 32; + + if (outputs.size() > 1) + { + //std::cout << "output1 nbDims: " << m_outpuDims[segInd].nbDims << ", "; + //for (size_t i = 0; i < m_outpuDims[segInd].nbDims; ++i) + //{ + // std::cout << m_outpuDims[segInd].d[i]; + // if (i + 1 != m_outpuDims[segInd].nbDims) + // std::cout << "x"; + //} + //std::cout << std::endl; + //std::cout << "output nbDims: " << m_outpuDims[outInd].nbDims << ", "; + //for (size_t i = 0; i < m_outpuDims[outInd].nbDims; ++i) + //{ + // std::cout << m_outpuDims[outInd].d[i]; + // if (i + 1 != m_outpuDims[outInd].nbDims) + // std::cout << "x"; + //} + //std::cout << std::endl; + + segChannels = static_cast(m_outpuDims[segInd].d[1]); + segWidth = static_cast(m_outpuDims[segInd].d[2]); + segHeight = static_cast(m_outpuDims[segInd].d[3]); + } + cv::Mat maskProposals; + std::vector> picked_proposals; + int net_width = nc + 4 + segChannels; +#endif + + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * (nc + 4 + 32); + + int classId = -1; + float objectConf = 0.f; + for (int j = 0; j < nc; ++j) + { + const float classConf = output[k + 4 + j]; + if (classConf > objectConf) + { + classId = j; + objectConf = classConf; + } + } + + //if (i == 0) + //{ + // std::cout << "without nms: mem" << i << ": "; + // for (size_t ii = 0; ii < 4; ++ii) + // { + // std::cout << output[k + ii] << " "; + // } + // std::cout << ";" << std::endl; + // for (size_t ii = 4; ii < nc + 4; ++ii) + // { + // std::cout << output[k + ii] << " "; + // } + // std::cout << ";" << std::endl; + // for (size_t ii = nc + 4; ii < nc + 4 + 32; ++ii) + // { + // std::cout << output[k + ii] << " "; + // } + // std::cout << ";" << std::endl; + //} + + if (objectConf >= m_params.m_confThreshold) + { + // (center x, center y, width, height) to (x, y, w, h) + float x = output[k] - output[k + 2] / 2; + float y = output[k + 1] - output[k + 3] / 2; + float width = output[k + 2]; + float height = output[k + 3]; + + //auto ClampToFrame = [](float& v, float& size, int hi) -> int + //{ + // int res = 0; +// + // if (size < 1) + // size = 0; +// + // if (v < 0) + // { + // res = v; + // v = 0; + // return res; + // } + // else if (v + size > hi - 1) + // { + // res = v; + // v = hi - 1 - size; + // if (v < 0) + // { + // size += v; + // v = 0; + // } + // res -= v; + // return res; + // } + // return res; + //}; + //ClampToFrame(x, width, frameSize.width); + //ClampToFrame(y, height, frameSize.height); + + //if (i == 0) + // std::cout << i << ": object_conf = " << object_conf << ", class_conf = " << class_conf << ", classId = " << classId << ", rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + + if (width > 4 && height > 4) + { + classIds.push_back(classId); + confidences.push_back(objectConf); + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + + std::vector temp_proto(output + k + 4 + nc, output + k + net_width); + picked_proposals.push_back(temp_proto); + } + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], Clamp(rectBoxes[indices[bi]], frameSize)); + maskProposals.push_back(cv::Mat(picked_proposals[indices[bi]]).t()); + } + + if (!maskProposals.empty()) + { + // Mask processing + const float* pdata = outputs[segInd]; + std::vector maskFloat(pdata, pdata + segChannels * segWidth * segHeight); + + int INPUT_W = static_cast(m_inputDims[0].d[3]); + int INPUT_H = static_cast(m_inputDims[0].d[2]); + static constexpr float MASK_THRESHOLD = 0.5; + + cv::Mat mask_protos = cv::Mat(maskFloat); + cv::Mat protos = mask_protos.reshape(0, { segChannels, segWidth * segHeight }); + + cv::Mat matmulRes = (maskProposals * protos).t();//n*32 32*25600 + cv::Mat masks = matmulRes.reshape(static_cast(resBoxes.size()), { segWidth, segHeight }); + std::vector maskChannels; + split(masks, maskChannels); + for (size_t i = 0; i < resBoxes.size(); ++i) + { + cv::Mat dest; + cv::Mat mask; + //sigmoid + cv::exp(-maskChannels[i], dest); + dest = 1.0 / (1.0 + dest);//160*160 + + int padw = 0; + int padh = 0; + cv::Rect roi(int((float)padw / INPUT_W * segWidth), int((float)padh / INPUT_H * segHeight), int(segWidth - padw / 2), int(segHeight - padh / 2)); + dest = dest(roi); + + cv::resize(dest, mask, cv::Size(INPUT_W, INPUT_H), cv::INTER_NEAREST); + + resBoxes[i].m_boxMask = mask(resBoxes[i].m_brect) > MASK_THRESHOLD; + +#if 0 + static int globalObjInd = 0; + SaveMat(resBoxes[i].m_boxMask, std::to_string(globalObjInd++), ".png", "tmp", true); +#endif + +#if 1 + std::vector> contours; +#if ((CV_VERSION_MAJOR > 4) || ((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR > 9))) + cv::findContoursLinkRuns(resBoxes[i].m_boxMask, contours); +#else + cv::findContours(resBoxes[i].m_boxMask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); +#endif + for (const auto& contour : contours) + { + cv::Rect br = cv::boundingRect(contour); + + if (br.width >= 4 && + br.height >= 4) + { + int dx = resBoxes[i].m_brect.x; + int dy = resBoxes[i].m_brect.y; + + cv::RotatedRect rr = (contour.size() < 5) ? cv::minAreaRect(contour) : cv::fitEllipse(contour); + rr.center.x = (rr.center.x + dx - m_resizedROI.x) * fw; + rr.center.y = (rr.center.y + dy - m_resizedROI.y) * fw; + rr.size.width *= fw; + rr.size.height *= fh; + + br.x = cvRound((dx + br.x - m_resizedROI.x) * fw); + br.y = cvRound((dy + br.y - m_resizedROI.y) * fh); + br.width = cvRound(br.width * fw); + br.height = cvRound(br.height * fh); + + resBoxes[i].m_brect = br; + resBoxes[i].m_rrect = rr; + + //std::cout << "resBoxes[" << i << "] br: " << br << ", rr: (" << rr.size << " from " << rr.center << ", " << rr.angle << ")" << std::endl; + + break; + } + } +#endif + } + } + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv11_obb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv11_obb.hpp new file mode 100644 index 000000000..e32903f14 --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv11_obb.hpp @@ -0,0 +1,131 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv11_obb_onnx class +/// +class YOLOv11_obb_onnx : public YoloONNX +{ +public: + YOLOv11_obb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: images, size: 1x3x1024x1024 + //1: name: output0, size: 1x20x21504 + //20: 15 DOTA classes + x + y + w + h + a + constexpr int shapeDataSize = 5; + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + auto output = outputs[0]; + + size_t ncInd = 1; + size_t lenInd = 2; + int nc = static_cast(m_outpuDims[0].d[ncInd] - shapeDataSize); + int dimensions = nc + shapeDataSize; + size_t len = static_cast(m_outpuDims[0].d[lenInd]);// / m_params.explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[0].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + cv::Mat rawMemory(1, dimensions * static_cast(len), CV_32FC1, output); + rawMemory = rawMemory.reshape(1, dimensions); + cv::transpose(rawMemory, rawMemory); + output = (float*)rawMemory.data; + + //std::cout << "output[0] mem:\n"; + //for (size_t ii = 0; ii < 100; ++ii) + //{ + // std::cout << ii << ": "; + // for (size_t jj = 0; jj < 20; ++jj) + // { + // std::cout << output[ii * 20 + jj] << " "; + // } + // std::cout << ";" << std::endl; + //} + //std::cout << ";" << std::endl; + + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * (nc + shapeDataSize); + + int classId = -1; + float objectConf = 0.f; + for (int j = 0; j < nc; ++j) + { + const float classConf = output[k + 4 + j]; + if (classConf > objectConf) + { + classId = j; + objectConf = classConf; + } + } + + //if (i == 0) + //{ + // for (int jj = 0; jj < 20; ++jj) + // { + // std::cout << output[jj] << " "; + // } + // std::cout << std::endl; + //} + + if (objectConf >= m_params.m_confThreshold) + { + classIds.push_back(classId); + confidences.push_back(objectConf); + + // (center x, center y, width, height) + float cx = fw * (output[k] - m_resizedROI.x); + float cy = fh * (output[k + 1] - m_resizedROI.y); + float width = fw * output[k + 2]; + float height = fh * output[k + 3]; + float angle = 180.f * output[k + nc + shapeDataSize - 1] / static_cast(M_PI); + rectBoxes.emplace_back(cv::Point2f(cx, cy), cv::Size2f(width, height), angle); + + //if (rectBoxes.size() == 1) + // std::cout << i << ": object_conf = " << objectConf << ", classId = " << classId << ", rect = " << rectBoxes.back().boundingRect() << ", angle = " << angle << std::endl; + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + //std::vector indices; + //cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.confThreshold, m_params.nmsThreshold, indices); + //resBoxes.reserve(indices.size()); + + resBoxes.reserve(rectBoxes.size()); + for (size_t bi = 0; bi < rectBoxes.size(); ++bi) + { + resBoxes.emplace_back(classIds[bi], confidences[bi], rectBoxes[bi]); + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv12_bb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv12_bb.hpp new file mode 100644 index 000000000..afa08e1af --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv12_bb.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv12_bb_onnx class +/// +class YOLOv12_bb_onnx : public YOLOv11_bb_onnx +{ +public: + YOLOv12_bb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + : YOLOv11_bb_onnx(inputTensorNames, outputTensorNames) + { + } + +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv13_bb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv13_bb.hpp new file mode 100644 index 000000000..ca4512581 --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv13_bb.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv13_bb_onnx class +/// +class YOLOv13_bb_onnx : public YoloONNX +{ +public: + YOLOv13_bb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: images, size: 1x3x640x640 + //1: name: output0, size: 1x84x8400 + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + auto output = outputs[0]; + + size_t ncInd = 1; + size_t lenInd = 2; + int nc = static_cast(m_outpuDims[0].d[ncInd] - 4); + int dimensions = nc + 4; + size_t len = static_cast(m_outpuDims[0].d[lenInd]);// / m_params.explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[0].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + cv::Mat rawMemory(1, dimensions * static_cast(len), CV_32FC1, output); + rawMemory = rawMemory.reshape(1, dimensions); + cv::transpose(rawMemory, rawMemory); + output = (float*)rawMemory.data; + + //std::cout << "output[0] mem:\n"; + //for (size_t ii = 0; ii < 100; ++ii) + //{ + // std::cout << ii << ": "; + // for (size_t jj = 0; jj < 20; ++jj) + // { + // std::cout << output[ii * 20 + jj] << " "; + // } + // std::cout << ";" << std::endl; + //} + //std::cout << ";" << std::endl; + + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * (nc + 4); + + int classId = -1; + float objectConf = 0.f; + for (int j = 0; j < nc; ++j) + { + const float classConf = output[k + 4 + j]; + if (classConf > objectConf) + { + classId = j; + objectConf = classConf; + } + } + + //if (i == 0) + // std::cout << i << ": object_conf = " << object_conf << ", class_conf = " << class_conf << ", classId = " << classId << ", rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + + if (objectConf >= m_params.m_confThreshold) + { + classIds.push_back(classId); + confidences.push_back(objectConf); + + // (center x, center y, width, height) to (x, y, w, h) + float x = fw * (output[k] - output[k + 2] / 2 - m_resizedROI.x); + float y = fh * (output[k + 1] - output[k + 3] / 2 - m_resizedROI.y); + float width = fw * output[k + 2]; + float height = fh * output[k + 3]; + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], rectBoxes[indices[bi]]); + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv26_bb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv26_bb.hpp new file mode 100644 index 000000000..7677244b2 --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv26_bb.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv26_bb_onnx class +/// +class YOLOv26_bb_onnx : public YoloONNX +{ +public: + YOLOv26_bb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: images, size: 1x3x640x640 + //1: name: output0, size: 1x300x6 + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + auto output = outputs[0]; + + size_t lenInd = 1; + size_t len = static_cast(m_outpuDims[0].d[lenInd]); + auto volume = len * m_outpuDims[0].d[2]; + output += volume * imgIdx; + //std::cout << "len = " << len << ", confThreshold = " << m_params.m_confThreshold << ", volume = " << volume << std::endl; + + for (size_t i = 0; i < len; ++i) + { + auto ind = i * m_outpuDims[0].d[2]; + + float classConf = output[ind + 4]; + int64_t classId = output[ind + 5]; + + if (classConf >= m_params.m_confThreshold) + { + float x = fw * (output[ind + 0] - m_resizedROI.x); + float y = fh * (output[ind + 1] - m_resizedROI.y); + float width = fw * (output[ind + 2] - output[ind + 0]); + float height = fh * (output[ind + 3] - output[ind + 1]); + + //std::cout << "ind = " << ind << ", output[0] = " << output[ind + 0] << ", output[1] = " << output[ind + 1] << ", output[2] = " << output[ind + 2] << ", output[3] = " << output[ind + 3] << std::endl; + //std::cout << "ind = " << ind << ", classConf = " << classConf << ", classId = " << classId << ", x = " << x << ", y = " << y << ", width = " << width << ", height = " << height << std::endl; + + resBoxes.emplace_back(classId, classConf, cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height))); + } + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv26_instance.hpp b/src/Detector/tensorrt_onnx/YoloONNXv26_instance.hpp new file mode 100644 index 000000000..abcb3b4fa --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv26_instance.hpp @@ -0,0 +1,175 @@ +#pragma once + +#include "YoloONNX.hpp" +#include "../../mtracking/defines.h" + +/// +/// \brief The YOLOv26_instance_onnx class +/// +class YOLOv26_instance_onnx : public YoloONNX +{ +public: + YOLOv26_instance_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + outputTensorNames.push_back("output1"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + size_t outInd = 0; + size_t segInd = 1; + + auto output = outputs[outInd]; + + //0: name: images, size: 1x3x640x640 + //1: name: output0, size: 1x300x38 + //2: name: output1, size: 1x32x160x160 + + size_t dimInd = 2; + size_t lenInd = 1; + int dimensions = static_cast(m_outpuDims[outInd].d[dimInd]); + size_t len = static_cast(m_outpuDims[outInd].d[lenInd]); + auto volume = len * dimensions; + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + int segWidth = 160; + int segHeight = 160; + int segChannels = 32; + + if (outputs.size() > 1) + { + segChannels = static_cast(m_outpuDims[segInd].d[1]); + segWidth = static_cast(m_outpuDims[segInd].d[2]); + segHeight = static_cast(m_outpuDims[segInd].d[3]); + } + cv::Mat maskProposals; + int netWidth = 6 + segChannels; + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * dimensions; + + float objectConf = output[k + 4]; + int classId = output[k + 5]; + + if (objectConf >= m_params.m_confThreshold) + { + // (center x, center y, width, height) to (x, y, w, h) + float x = output[k]; + float y = output[k + 1]; + float width = output[k + 2] - output[k]; + float height = output[k + 3] - output[k + 1]; + + if (width > 4 && height > 4) + { + resBoxes.emplace_back(classId, objectConf, cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height))); + + std::vector tempProto(output + k + 6, output + k + netWidth); + maskProposals.push_back(cv::Mat(tempProto).t()); + } + } + } + + //std::cout << "maskProposals.size = " << maskProposals.size() << std::endl; + if (!maskProposals.empty()) + { + // Mask processing + const float* pdata = outputs[segInd]; + std::vector maskFloat(pdata, pdata + segChannels * segWidth * segHeight); + + int INPUT_W = static_cast(m_inputDims[0].d[3]); + int INPUT_H = static_cast(m_inputDims[0].d[2]); + static constexpr float MASK_THRESHOLD = 0.5; + + cv::Mat mask_protos = cv::Mat(maskFloat); + cv::Mat protos = mask_protos.reshape(0, { segChannels, segWidth * segHeight }); + + cv::Mat matmulRes = (maskProposals * protos).t();//n*32 32*25600 + cv::Mat masks = matmulRes.reshape(static_cast(resBoxes.size()), { segWidth, segHeight }); + std::vector maskChannels; + split(masks, maskChannels); + for (size_t i = 0; i < resBoxes.size(); ++i) + { + cv::Mat dest; + cv::Mat mask; + //sigmoid + cv::exp(-maskChannels[i], dest); + dest = 1.0 / (1.0 + dest);//160*160 + + int padw = 0; + int padh = 0; + cv::Rect roi(int((float)padw / INPUT_W * segWidth), int((float)padh / INPUT_H * segHeight), int(segWidth - padw / 2), int(segHeight - padh / 2)); + dest = dest(roi); + + cv::resize(dest, mask, cv::Size(INPUT_W, INPUT_H), cv::INTER_NEAREST); + + //std::cout << "m_brect = " << resBoxes[i].m_brect << ", dest = " << dest.size() << ", mask = " << mask.size() << std::endl; + + resBoxes[i].m_boxMask = mask(resBoxes[i].m_brect) > MASK_THRESHOLD; + + //std::cout << "m_boxMask = " << resBoxes[i].m_boxMask.size() << ", m_brect = " << resBoxes[i].m_brect << ", dest = " << dest.size() << ", mask = " << mask.size() << std::endl; + +#if 0 + static int globalObjInd = 0; + SaveMat(resBoxes[i].m_boxMask, std::to_string(globalObjInd++), ".png", "tmp", true); +#endif + +#if 1 + std::vector> contours; +#if ((CV_VERSION_MAJOR > 4) || ((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR > 9))) + cv::findContoursLinkRuns(resBoxes[i].m_boxMask, contours); +#else + cv::findContours(resBoxes[i].m_boxMask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); +#endif + for (const auto& contour : contours) + { + cv::Rect br = cv::boundingRect(contour); + + if (br.width >= 4 && + br.height >= 4) + { + int dx = resBoxes[i].m_brect.x; + int dy = resBoxes[i].m_brect.y; + + cv::RotatedRect rr = (contour.size() < 5) ? cv::minAreaRect(contour) : cv::fitEllipse(contour); + rr.center.x = (rr.center.x + dx - m_resizedROI.x) * fw; + rr.center.y = (rr.center.y + dy - m_resizedROI.y) * fw; + rr.size.width *= fw; + rr.size.height *= fh; + + br.x = cvRound((dx + br.x - m_resizedROI.x) * fw); + br.y = cvRound((dy + br.y - m_resizedROI.y) * fh); + br.width = cvRound(br.width * fw); + br.height = cvRound(br.height * fh); + + resBoxes[i].m_brect = br; + resBoxes[i].m_rrect = rr; + + cv::resize(resBoxes[i].m_boxMask, resBoxes[i].m_boxMask, resBoxes[i].m_brect.size(), 0, 0, cv::INTER_NEAREST); + + //std::cout << "resBoxes[" << i << "] br: " << br << ", rr: (" << rr.size << " from " << rr.center << ", " << rr.angle << ")" << std::endl; + + break; + } + } +#endif + } + } + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv26_obb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv26_obb.hpp new file mode 100644 index 000000000..0b70b509e --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv26_obb.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv26_obb_onnx class +/// +class YOLOv26_obb_onnx : public YoloONNX +{ +public: + YOLOv26_obb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: images, size: 1x3x1024x1024 + //1: name: output0, size: 1x300x7 + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + auto output = outputs[0]; + + size_t lenInd = 1; + size_t len = static_cast(m_outpuDims[0].d[lenInd]); + auto volume = len * m_outpuDims[0].d[2]; + output += volume * imgIdx; + //std::cout << "len = " << len << ", confThreshold = " << m_params.m_confThreshold << ", volume = " << volume << std::endl; + + for (size_t i = 0; i < len; ++i) + { + auto ind = i * m_outpuDims[0].d[2]; + + float classConf = output[ind + 4]; + int64_t classId = output[ind + 5]; + + if (classConf >= m_params.m_confThreshold) + { + float x = fw * (output[ind + 0] - m_resizedROI.x); + float y = fh * (output[ind + 1] - m_resizedROI.y); + float width = fw * output[ind + 2]; + float height = fh * output[ind + 3]; + float angle = 180.f * output[ind + 6] / static_cast(M_PI); + //std::cout << "ind = " << ind << ", output[0] = " << output[ind + 0] << ", output[1] = " << output[ind + 1] << ", output[2] = " << output[ind + 2] << ", output[3] = " << output[ind + 3] << std::endl; + //std::cout << "ind = " << ind << ", classConf = " << classConf << ", classId = " << classId << ", x = " << x << ", y = " << y << ", width = " << width << ", height = " << height << ", angle = " << angle << std::endl; + + resBoxes.emplace_back(classId, classConf, cv::RotatedRect(cv::Point2f(x, y), cv::Size2f(width, height), angle)); + } + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv5_bb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv5_bb.hpp new file mode 100644 index 000000000..afd0e3151 --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv5_bb.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv5_bb_onnx class +/// +class YOLOv5_bb_onnx : public YoloONNX +{ +public: + YOLOv5_bb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: images, size: 1x3x640x640 + //1: name: output0, size: 1x84x8400 + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + auto output = outputs[0]; + + size_t ncInd = 1; + size_t lenInd = 2; + int nc = static_cast(m_outpuDims[0].d[ncInd] - 4); + int dimensions = nc + 4; + size_t len = static_cast(m_outpuDims[0].d[lenInd]);// / m_params.explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[0].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + cv::Mat rawMemory(1, dimensions * static_cast(len), CV_32FC1, output); + rawMemory = rawMemory.reshape(1, dimensions); + cv::transpose(rawMemory, rawMemory); + output = (float*)rawMemory.data; + + //std::cout << "output[0] mem:\n"; + //for (size_t ii = 0; ii < 100; ++ii) + //{ + // std::cout << ii << ": "; + // for (size_t jj = 0; jj < 20; ++jj) + // { + // std::cout << output[ii * 20 + jj] << " "; + // } + // std::cout << ";" << std::endl; + //} + //std::cout << ";" << std::endl; + + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * (nc + 4); + + int classId = -1; + float objectConf = 0.f; + for (int j = 0; j < nc; ++j) + { + const float classConf = output[k + 4 + j]; + if (classConf > objectConf) + { + classId = j; + objectConf = classConf; + } + } + + //if (i == 0) + // std::cout << i << ": object_conf = " << object_conf << ", class_conf = " << class_conf << ", classId = " << classId << ", rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + + if (objectConf >= m_params.m_confThreshold) + { + classIds.push_back(classId); + confidences.push_back(objectConf); + + // (center x, center y, width, height) to (x, y, w, h) + float x = fw * (output[k] - output[k + 2] / 2 - m_resizedROI.x); + float y = fh * (output[k + 1] - output[k + 3] / 2 - m_resizedROI.y); + float width = fw * output[k + 2]; + float height = fh * output[k + 3]; + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], rectBoxes[indices[bi]]); + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv6_bb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv6_bb.hpp new file mode 100644 index 000000000..5493f55d0 --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv6_bb.hpp @@ -0,0 +1,200 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv6_bb_onnx class +/// +class YOLOv6_bb_onnx : public YoloONNX +{ +public: + YOLOv6_bb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("image_arrays"); + outputTensorNames.push_back("outputs"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + if (outputs.size() == 4) + { + auto dets = reinterpret_cast(outputs[0]); + auto boxes = outputs[1]; + auto scores = outputs[2]; + auto classes = reinterpret_cast(outputs[3]); + + int objectsCount = static_cast(m_outpuDims[1].d[1]); + + //std::cout << "Dets[" << imgIdx << "] = " << dets[imgIdx] << ", objectsCount = " << objectsCount << std::endl; + + const size_t step1 = imgIdx * objectsCount; + const size_t step2 = 4 * imgIdx * objectsCount; + for (size_t i = 0; i < static_cast(dets[imgIdx]); ++i) + { + // Box + const size_t k = i * 4; + float class_conf = scores[i + step1]; + int classId = classes[i + step1]; + if (class_conf >= m_params.m_confThreshold) + { + float x = fw * (boxes[k + 0 + step2] - m_resizedROI.x); + float y = fh * (boxes[k + 1 + step2] - m_resizedROI.y); + float width = fw * boxes[k + 2 + step2] - x; + float height = fh * boxes[k + 3 + step2] - y; + + //if (i == 0) + //{ + // std::cout << i << ": class_conf = " << class_conf << ", classId = " << classId << " (" << classes[i + step1] << "), rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + // std::cout << "boxes = " << boxes[k + 0 + step2] << ", " << boxes[k + 1 + step2] << ", " << boxes[k + 2 + step2] << ", " << boxes[k + 3 + step2] << std::endl; + //} + resBoxes.emplace_back(classId, class_conf, cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height))); + } + } + } + else if (outputs.size() == 1) + { + auto output = outputs[0]; + + size_t ncInd = 2; + size_t lenInd = 1; + if (m_outpuDims[0].nbDims == 2) + { + ncInd = 1; + lenInd = 0; + } + int nc = static_cast(m_outpuDims[0].d[ncInd] - 5); + size_t len = static_cast(m_outpuDims[0].d[lenInd]);// / m_params.explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[0].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + if (m_outpuDims[0].nbDims == 2) // With nms + { + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * 7; + float class_conf = output[k + 6]; + int classId = cvRound(output[k + 5]); + if (class_conf >= m_params.m_confThreshold) + { + float x = fw * (output[k + 1] - m_resizedROI.x); + float y = fh * (output[k + 2] - m_resizedROI.y); + float width = fw * (output[k + 3] - output[k + 1]); + float height = fh * (output[k + 4] - output[k + 2]); + + //if (i == 0) + // std::cout << i << ": class_conf = " << class_conf << ", classId = " << classId << ", rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + + classIds.push_back(classId); + confidences.push_back(class_conf); + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + + //bboxes.emplace_back(classId, class_conf, cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height))); + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], rectBoxes[indices[bi]]); + } + } + else // Without nms + { + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * (nc + 5); + float object_conf = output[k + 4]; + + //if (i == 0) + //{ + // std::cout << "mem" << i << ": "; + // for (size_t ii = 0; ii < nc + 5; ++ii) + // { + // std::cout << output[k + ii] << " "; + // } + // std::cout << std::endl; + //} + + if (object_conf >= m_params.m_confThreshold) + { + // (center x, center y, width, height) to (x, y, w, h) + float x = fw * (output[k] - output[k + 2] / 2 - m_resizedROI.x); + float y = fh * (output[k + 1] - output[k + 3] / 2 - m_resizedROI.y); + float width = fw * output[k + 2]; + float height = fh * output[k + 3]; + + // Classes + float class_conf = output[k + 5]; + int classId = 0; + + for (int j = 1; j < nc; ++j) + { + if (class_conf < output[k + 5 + j]) + { + classId = j; + class_conf = output[k + 5 + j]; + } + } + + class_conf *= object_conf; + + //if (i == 0) + // std::cout << i << ": object_conf = " << object_conf << ", class_conf = " << class_conf << ", classId = " << classId << ", rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + + classIds.push_back(classId); + confidences.push_back(class_conf); + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], rectBoxes[indices[bi]]); + } + } + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv7_bb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv7_bb.hpp new file mode 100644 index 000000000..1d6b72601 --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv7_bb.hpp @@ -0,0 +1,204 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv7_bb_onnx class +/// +class YOLOv7_bb_onnx : public YoloONNX +{ +public: + YOLOv7_bb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output"); + outputTensorNames.push_back("num_dets"); // batch x 1 + outputTensorNames.push_back("det_boxes"); // batch x 100 x 4 + outputTensorNames.push_back("det_scores"); // batch x 100 + outputTensorNames.push_back("det_classes"); // batch x 100 + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + if (outputs.size() == 4) + { + auto dets = reinterpret_cast(outputs[0]); + auto boxes = outputs[1]; + auto scores = outputs[2]; + auto classes = reinterpret_cast(outputs[3]); + + int objectsCount = static_cast(m_outpuDims[1].d[1]); + + //std::cout << "Dets[" << imgIdx << "] = " << dets[imgIdx] << ", objectsCount = " << objectsCount << std::endl; + + const size_t step1 = imgIdx * objectsCount; + const size_t step2 = 4 * imgIdx * objectsCount; + for (size_t i = 0; i < static_cast(dets[imgIdx]); ++i) + { + // Box + const size_t k = i * 4; + float class_conf = scores[i + step1]; + int classId = classes[i + step1]; + if (class_conf >= m_params.m_confThreshold) + { + float x = fw * (boxes[k + 0 + step2] - m_resizedROI.x); + float y = fh * (boxes[k + 1 + step2] - m_resizedROI.y); + float width = fw * boxes[k + 2 + step2] - x; + float height = fh * boxes[k + 3 + step2] - y; + + //if (i == 0) + //{ + // std::cout << i << ": class_conf = " << class_conf << ", classId = " << classId << " (" << classes[i + step1] << "), rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + // std::cout << "boxes = " << boxes[k + 0 + step2] << ", " << boxes[k + 1 + step2] << ", " << boxes[k + 2 + step2] << ", " << boxes[k + 3 + step2] << std::endl; + //} + resBoxes.emplace_back(classId, class_conf, cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height))); + } + } + } + else if (outputs.size() == 1) + { + auto output = outputs[0]; + + size_t ncInd = 2; + size_t lenInd = 1; + if (m_outpuDims[0].nbDims == 2) + { + ncInd = 1; + lenInd = 0; + } + int nc = static_cast(m_outpuDims[0].d[ncInd] - 5); + size_t len = static_cast(m_outpuDims[0].d[lenInd]) / m_params.m_explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[0].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + if (m_outpuDims[0].nbDims == 2) // With nms + { + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * 7; + float class_conf = output[k + 6]; + int classId = cvRound(output[k + 5]); + if (class_conf >= m_params.m_confThreshold) + { + float x = fw * (output[k + 1] - m_resizedROI.x); + float y = fh * (output[k + 2] - m_resizedROI.y); + float width = fw * (output[k + 3] - output[k + 1]); + float height = fh * (output[k + 4] - output[k + 2]); + + //if (i == 0) + // std::cout << i << ": class_conf = " << class_conf << ", classId = " << classId << ", rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + + classIds.push_back(classId); + confidences.push_back(class_conf); + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + + //bboxes.emplace_back(classId, class_conf, cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height))); + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], rectBoxes[indices[bi]]); + } + } + else // Without nms + { + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * (nc + 5); + float object_conf = output[k + 4]; + + //if (i == 0) + //{ + // std::cout << "mem" << i << ": "; + // for (size_t ii = 0; ii < nc + 5; ++ii) + // { + // std::cout << output[k + ii] << " "; + // } + // std::cout << std::endl; + //} + + if (object_conf >= m_params.m_confThreshold) + { + // (center x, center y, width, height) to (x, y, w, h) + float x = fw * (output[k] - output[k + 2] / 2 - m_resizedROI.x); + float y = fh * (output[k + 1] - output[k + 3] / 2 - m_resizedROI.y); + float width = fw * output[k + 2]; + float height = fh * output[k + 3]; + + // Classes + float class_conf = output[k + 5]; + int classId = 0; + + for (int j = 1; j < nc; ++j) + { + if (class_conf < output[k + 5 + j]) + { + classId = j; + class_conf = output[k + 5 + j]; + } + } + + class_conf *= object_conf; + + //if (i == 0) + // std::cout << i << ": object_conf = " << object_conf << ", class_conf = " << class_conf << ", classId = " << classId << ", rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + + classIds.push_back(classId); + confidences.push_back(class_conf); + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], rectBoxes[indices[bi]]); + } + } + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv7_instance.hpp b/src/Detector/tensorrt_onnx/YoloONNXv7_instance.hpp new file mode 100644 index 000000000..247e352c9 --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv7_instance.hpp @@ -0,0 +1,260 @@ +#pragma once + +#include "YoloONNX.hpp" +#include "../../mtracking/defines.h" + +/// +/// \brief The YOLOv7_instance_onnx class +/// +class YOLOv7_instance_onnx : public YoloONNX +{ +public: + YOLOv7_instance_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output"); + outputTensorNames.push_back("516"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + size_t outInd = (outputs.size() == 0) ? 0 : 1; + size_t segInd = (outputs.size() == 0) ? 1 : 0; + + auto output = outputs[0]; + + //0: name: images, size : 1x3x640x640 + //1 : name : 516, size : 1x32x160x160 + //2 : name : onnx::Slice_542, size : 1x3x80x80x117 + //3 : name : onnx::Slice_710, size : 1x3x40x40x117 + //4 : name : onnx::Slice_878, size : 1x3x20x20x117 + //5 : name : output, size : 1x25200x117 + // 25200 = 3x80x80 + 3x40x40 + 3x20x20 + // 117 = x, y, w, h, c, 80 classes, 32 seg ancors + // 80 * 8 = 640, 40 * 16 = 640, 20 * 32 = 640 + + size_t ncInd = 2; + size_t lenInd = 1; + if (m_outpuDims[outInd].nbDims == 2) + { + ncInd = 1; + lenInd = 0; + } + int nc = static_cast(m_outpuDims[outInd].d[ncInd] - 5 - 32); + size_t len = static_cast(m_outpuDims[outInd].d[lenInd]);// / m_params.explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[outInd].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + +#if 1 + int segWidth = 160; + int segHeight = 160; + int segChannels = 32; + + if (outputs.size() > 1) + { + //std::cout << "516 nbDims: " << m_outpuDims[segInd].nbDims << ", "; + //for (size_t i = 0; i < m_outpuDims[segInd].nbDims; ++i) + //{ + // std::cout << m_outpuDims[segInd].d[i]; + // if (i + 1 != m_outpuDims[segInd].nbDims) + // std::cout << "x"; + //} + //std::cout << std::endl; +// + //std::cout << "output nbDims: " << m_outpuDims[outInd].nbDims << ", "; + //for (size_t i = 0; i < m_outpuDims[outInd].nbDims; ++i) + //{ + // std::cout << m_outpuDims[outInd].d[i]; + // if (i + 1 != m_outpuDims[outInd].nbDims) + // std::cout << "x"; + //} + //std::cout << std::endl; + + segChannels = static_cast(m_outpuDims[segInd].d[1]); + segWidth = static_cast(m_outpuDims[segInd].d[2]); + segHeight = static_cast(m_outpuDims[segInd].d[3]); + } + cv::Mat maskProposals; + std::vector> picked_proposals; + int net_width = nc + 5 + segChannels; +#endif + + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * (nc + 5 + 32); + float object_conf = output[k + 4]; + + if (i == 0) + { + //std::cout << "without nms: mem" << i << ": "; + //for (size_t ii = 0; ii < nc; ++ii) + //{ + // std::cout << output[k + ii] << " "; + //} + //std::cout << ";" << std::endl; + //for (size_t ii = nc; ii < nc + 4; ++ii) + //{ + // std::cout << output[k + ii] << " "; + //} + //std::cout << ";" << std::endl; + //for (size_t ii = nc + 4; ii < nc + 4 + 32; ++ii) + //{ + // std::cout << output[k + ii] << " "; + //} + //std::cout << ";" << std::endl; + } + + if (object_conf >= m_params.m_confThreshold) + { + // (center x, center y, width, height) to (x, y, w, h) + float x = output[k] - output[k + 2] / 2; + float y = output[k + 1] - output[k + 3] / 2; + float width = output[k + 2]; + float height = output[k + 3]; + + // Classes + float class_conf = output[k + 5]; + int classId = 0; + + for (int j = 1; j < nc; ++j) + { + if (class_conf < output[k + 5 + j]) + { + classId = j; + class_conf = output[k + 5 + j]; + } + } + + class_conf *= object_conf; + + //if (i == 0) + // std::cout << i << ": object_conf = " << object_conf << ", class_conf = " << class_conf << ", classId = " << classId << ", rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + + classIds.push_back(classId); + confidences.push_back(class_conf); + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + + std::vector temp_proto(output + k + 5 + nc, output + k + net_width); + picked_proposals.push_back(temp_proto); + + if (rectBoxes.size() >= CV_CN_MAX - 1) + break; + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], Clamp(rectBoxes[indices[bi]], frameSize)); + maskProposals.push_back(cv::Mat(picked_proposals[indices[bi]]).t()); + } + + if (!maskProposals.empty()) + { + // Mask processing + const float* pdata = outputs[1]; + std::vector maskFloat(pdata, pdata + segChannels * segWidth * segHeight); + + int INPUT_W = static_cast(m_inputDims[0].d[3]); + int INPUT_H = static_cast(m_inputDims[0].d[2]); + static constexpr float MASK_THRESHOLD = 0.5; + + cv::Mat mask_protos = cv::Mat(maskFloat); + cv::Mat protos = mask_protos.reshape(0, { segChannels, segWidth * segHeight }); + + cv::Mat matmulRes = (maskProposals * protos).t();//n*32 32*25600 + cv::Mat masks = matmulRes.reshape(static_cast(resBoxes.size()), { segWidth, segHeight }); + std::vector maskChannels; + split(masks, maskChannels); + for (size_t i = 0; i < resBoxes.size(); ++i) + { + cv::Mat dest; + cv::Mat mask; + //sigmoid + cv::exp(-maskChannels[i], dest); + dest = 1.0 / (1.0 + dest);//160*160 + + int padw = 0; + int padh = 0; + cv::Rect roi(int((float)padw / INPUT_W * segWidth), int((float)padh / INPUT_H * segHeight), int(segWidth - padw / 2), int(segHeight - padh / 2)); + dest = dest(roi); + + cv::resize(dest, mask, cv::Size(INPUT_W, INPUT_H), cv::INTER_NEAREST); + + resBoxes[i].m_boxMask = mask(resBoxes[i].m_brect) > MASK_THRESHOLD; + +#if 0 + static int globalObjInd = 0; + SaveMat(resBoxes[i].m_boxMask, std::to_string(globalObjInd++), ".png", "tmp", true); +#endif + +#if 1 + std::vector> contours; +#if ((CV_VERSION_MAJOR > 4) || ((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR > 9))) + cv::findContoursLinkRuns(resBoxes[i].m_boxMask, contours); +#else + cv::findContours(resBoxes[i].m_boxMask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); +#endif + for (const auto& contour : contours) + { + cv::Rect br = cv::boundingRect(contour); + + if (br.width >= 4 && + br.height >= 4) + { + int dx = resBoxes[i].m_brect.x; + int dy = resBoxes[i].m_brect.y; + + cv::RotatedRect rr = (contour.size() < 5) ? cv::minAreaRect(contour) : cv::fitEllipse(contour); + rr.center.x = (rr.center.x + dx - m_resizedROI.x) * fw; + rr.center.y = (rr.center.y + dy - m_resizedROI.y) * fw; + rr.size.width *= fw; + rr.size.height *= fh; + + br.x = cvRound((dx + br.x - m_resizedROI.x) * fw); + br.y = cvRound((dy + br.y - m_resizedROI.y) * fh); + br.width = cvRound(br.width * fw); + br.height = cvRound(br.height * fh); + + resBoxes[i].m_brect = br; + resBoxes[i].m_rrect = rr; + + //std::cout << "resBoxes[" << i << "] br: " << br << ", rr: (" << rr.size << " from " << rr.center << ", " << rr.angle << ")" << std::endl; + + break; + } + } +#endif + } + } + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv8_bb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv8_bb.hpp new file mode 100644 index 000000000..0efdbdd04 --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv8_bb.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv8_bb_onnx class +/// +class YOLOv8_bb_onnx : public YoloONNX +{ +public: + YOLOv8_bb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: images, size: 1x3x640x640 + //1: name: output0, size: 1x84x8400 + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + auto output = outputs[0]; + + size_t ncInd = 1; + size_t lenInd = 2; + int nc = static_cast(m_outpuDims[0].d[ncInd] - 4); + int dimensions = nc + 4; + size_t len = static_cast(m_outpuDims[0].d[lenInd]);// / m_params.explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[0].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + cv::Mat rawMemory(1, dimensions * static_cast(len), CV_32FC1, output); + rawMemory = rawMemory.reshape(1, dimensions); + cv::transpose(rawMemory, rawMemory); + output = (float*)rawMemory.data; + + //std::cout << "output[0] mem:\n"; + //for (size_t ii = 0; ii < 100; ++ii) + //{ + // std::cout << ii << ": "; + // for (size_t jj = 0; jj < 20; ++jj) + // { + // std::cout << output[ii * 20 + jj] << " "; + // } + // std::cout << ";" << std::endl; + //} + //std::cout << ";" << std::endl; + + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * (nc + 4); + + int classId = -1; + float objectConf = 0.f; + for (int j = 0; j < nc; ++j) + { + const float classConf = output[k + 4 + j]; + if (classConf > objectConf) + { + classId = j; + objectConf = classConf; + } + } + + //if (i == 0) + // std::cout << i << ": object_conf = " << object_conf << ", class_conf = " << class_conf << ", classId = " << classId << ", rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + + if (objectConf >= m_params.m_confThreshold) + { + classIds.push_back(classId); + confidences.push_back(objectConf); + + // (center x, center y, width, height) to (x, y, w, h) + float x = fw * (output[k] - output[k + 2] / 2 - m_resizedROI.x); + float y = fh * (output[k + 1] - output[k + 3] / 2 - m_resizedROI.y); + float width = fw * output[k + 2]; + float height = fh * output[k + 3]; + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], rectBoxes[indices[bi]]); + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv8_instance.hpp b/src/Detector/tensorrt_onnx/YoloONNXv8_instance.hpp new file mode 100644 index 000000000..6422cc47c --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv8_instance.hpp @@ -0,0 +1,312 @@ +#pragma once + +#include "YoloONNX.hpp" +#include "../../mtracking/defines.h" + +/// +/// \brief The YOLOv8_instance_onnx class +/// +class YOLOv8_instance_onnx : public YoloONNX +{ +public: + YOLOv8_instance_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + outputTensorNames.push_back("output1"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + size_t outInd = 0; + size_t segInd = 1; + + auto output = outputs[0]; + + //std::cout << "output[1] mem:\n"; + //auto output1 = outputs[1]; + //for (size_t ii = 0; ii < 100; ++ii) + //{ + // std::cout << ii << ": "; + // for (size_t jj = 0; jj < 20; ++jj) + // { + // std::cout << output1[ii * 20 + jj] << " "; + // } + // std::cout << ";" << std::endl; + //} + //std::cout << ";" << std::endl; + + //0: name: images, size: 1x3x640x640 + //1: name: output0, size: 1x116x8400 + //2: name: output1, size: 1x32x160x160 + // 25200 = 3x80x80 + 3x40x40 + 3x20x20 + // 116 = x, y, w, h, 80 classes, 32 seg ancors + // 80 * 8 = 640, 40 * 16 = 640, 20 * 32 = 640 + + size_t ncInd = 1; + size_t lenInd = 2; + int nc = static_cast(m_outpuDims[outInd].d[ncInd] - 4 - 32); + int dimensions = nc + 32 + 4; + size_t len = static_cast(m_outpuDims[outInd].d[lenInd]);// / m_params.explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[outInd].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + cv::Mat rawMemory(1, dimensions * static_cast(len), CV_32FC1, output); + rawMemory = rawMemory.reshape(1, dimensions); + cv::transpose(rawMemory, rawMemory); + output = (float*)rawMemory.data; + + //std::cout << "output[0] mem:\n"; + //for (size_t ii = 0; ii < 100; ++ii) + //{ + // std::cout << ii << ": "; + // for (size_t jj = 0; jj < 20; ++jj) + // { + // std::cout << output[ii * 20 + jj] << " "; + // } + // std::cout << ";" << std::endl; + //} + //std::cout << ";" << std::endl; + +#if 1 + int segWidth = 160; + int segHeight = 160; + int segChannels = 32; + + if (outputs.size() > 1) + { + //std::cout << "output1 nbDims: " << m_outpuDims[segInd].nbDims << ", "; + //for (size_t i = 0; i < m_outpuDims[segInd].nbDims; ++i) + //{ + // std::cout << m_outpuDims[segInd].d[i]; + // if (i + 1 != m_outpuDims[segInd].nbDims) + // std::cout << "x"; + //} + //std::cout << std::endl; + //std::cout << "output nbDims: " << m_outpuDims[outInd].nbDims << ", "; + //for (size_t i = 0; i < m_outpuDims[outInd].nbDims; ++i) + //{ + // std::cout << m_outpuDims[outInd].d[i]; + // if (i + 1 != m_outpuDims[outInd].nbDims) + // std::cout << "x"; + //} + //std::cout << std::endl; + + segChannels = static_cast(m_outpuDims[segInd].d[1]); + segWidth = static_cast(m_outpuDims[segInd].d[2]); + segHeight = static_cast(m_outpuDims[segInd].d[3]); + } + cv::Mat maskProposals; + std::vector> picked_proposals; + int net_width = nc + 4 + segChannels; +#endif + + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * (nc + 4 + 32); + + int classId = -1; + float objectConf = 0.f; + for (int j = 0; j < nc; ++j) + { + const float classConf = output[k + 4 + j]; + if (classConf > objectConf) + { + classId = j; + objectConf = classConf; + } + } + + //if (objectConf > 0.1) + // std::cout << i << ": objectConf = " << objectConf << ", classId = " << classId << std::endl; + + //if (i == 0) + //{ + // std::cout << "without nms: mem" << i << ": "; + // for (size_t ii = 0; ii < 4; ++ii) + // { + // std::cout << output[k + ii] << " "; + // } + // std::cout << ";" << std::endl; + // for (size_t ii = 4; ii < nc + 4; ++ii) + // { + // std::cout << output[k + ii] << " "; + // } + // std::cout << ";" << std::endl; + // for (size_t ii = nc + 4; ii < nc + 4 + 32; ++ii) + // { + // std::cout << output[k + ii] << " "; + // } + // std::cout << ";" << std::endl; + //} + + if (objectConf >= m_params.m_confThreshold) + { + // (center x, center y, width, height) to (x, y, w, h) + float x = output[k] - output[k + 2] / 2; + float y = output[k + 1] - output[k + 3] / 2; + float width = output[k + 2]; + float height = output[k + 3]; + + //auto ClampToFrame = [](float& v, float& size, int hi) -> int + //{ + // int res = 0; +// + // if (size < 1) + // size = 0; +// + // if (v < 0) + // { + // res = v; + // v = 0; + // return res; + // } + // else if (v + size > hi - 1) + // { + // res = v; + // v = hi - 1 - size; + // if (v < 0) + // { + // size += v; + // v = 0; + // } + // res -= v; + // return res; + // } + // return res; + //}; + //ClampToFrame(x, width, frameSize.width); + //ClampToFrame(y, height, frameSize.height); + + if (width > 4 && height > 4) + { + classIds.push_back(classId); + confidences.push_back(objectConf); + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + + std::vector temp_proto(output + k + 4 + nc, output + k + net_width); + picked_proposals.push_back(temp_proto); + } + } + } + + //std::cout << "rectBoxes.size = " << rectBoxes.size() << std::endl; + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], Clamp(rectBoxes[indices[bi]], frameSize)); + maskProposals.push_back(cv::Mat(picked_proposals[indices[bi]]).t()); + } + + if (!maskProposals.empty()) + { + // Mask processing + const float* pdata = outputs[1]; + std::vector maskFloat(pdata, pdata + segChannels * segWidth * segHeight); + + int INPUT_W = static_cast(m_inputDims[0].d[3]); + int INPUT_H = static_cast(m_inputDims[0].d[2]); + static constexpr float MASK_THRESHOLD = 0.5; + + cv::Mat mask_protos = cv::Mat(maskFloat); + cv::Mat protos = mask_protos.reshape(0, { segChannels, segWidth * segHeight }); + + cv::Mat matmulRes = (maskProposals * protos).t();//n*32 32*25600 + cv::Mat masks = matmulRes.reshape(static_cast(resBoxes.size()), { segWidth, segHeight }); + std::vector maskChannels; + split(masks, maskChannels); + for (size_t i = 0; i < resBoxes.size(); ++i) + { + cv::Mat dest; + cv::Mat mask; + //sigmoid + cv::exp(-maskChannels[i], dest); + dest = 1.0 / (1.0 + dest);//160*160 + + int padw = 0; + int padh = 0; + cv::Rect roi(int((float)padw / INPUT_W * segWidth), int((float)padh / INPUT_H * segHeight), int(segWidth - padw / 2), int(segHeight - padh / 2)); + dest = dest(roi); + + cv::resize(dest, mask, cv::Size(INPUT_W, INPUT_H), cv::INTER_NEAREST); + + resBoxes[i].m_boxMask = mask(resBoxes[i].m_brect) > MASK_THRESHOLD; + +#if 0 + static int globalObjInd = 0; + SaveMat(resBoxes[i].m_boxMask, std::to_string(globalObjInd++), ".png", "tmp", true); +#endif + +#if 1 + std::vector> contours; +#if ((CV_VERSION_MAJOR > 4) || ((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR > 9))) + cv::findContoursLinkRuns(resBoxes[i].m_boxMask, contours); +#else + cv::findContours(resBoxes[i].m_boxMask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); +#endif + for (const auto& contour : contours) + { + cv::Rect br = cv::boundingRect(contour); + + //std::cout << "contour br: " << br << std::endl; + + if (br.width >= 4 && + br.height >= 4) + { + int dx = resBoxes[i].m_brect.x; + int dy = resBoxes[i].m_brect.y; + + cv::RotatedRect rr = (contour.size() < 5) ? cv::minAreaRect(contour) : cv::fitEllipse(contour); + rr.center.x = (rr.center.x + dx - m_resizedROI.x) * fw; + rr.center.y = (rr.center.y + dy - m_resizedROI.y) * fw; + rr.size.width *= fw; + rr.size.height *= fh; + + br.x = cvRound((dx + br.x - m_resizedROI.x) * fw); + br.y = cvRound((dy + br.y - m_resizedROI.y) * fh); + br.width = cvRound(br.width * fw); + br.height = cvRound(br.height * fh); + + resBoxes[i].m_brect = br; + resBoxes[i].m_rrect = rr; + + //std::cout << "resBoxes[" << i << "] br: " << br << ", rr: (" << rr.size << " from " << rr.center << ", " << rr.angle << ")" << std::endl; + + break; + } + } +#endif + } + } + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv8_obb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv8_obb.hpp new file mode 100644 index 000000000..ed44eb4a2 --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv8_obb.hpp @@ -0,0 +1,131 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv8_obb_onnx class +/// +class YOLOv8_obb_onnx : public YoloONNX +{ +public: + YOLOv8_obb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: images, size: 1x3x1024x1024 + //1: name: output0, size: 1x20x21504 + //20: 15 DOTA classes + x + y + w + h + a + constexpr int shapeDataSize = 5; + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + auto output = outputs[0]; + + size_t ncInd = 1; + size_t lenInd = 2; + int nc = static_cast(m_outpuDims[0].d[ncInd] - shapeDataSize); + int dimensions = nc + shapeDataSize; + size_t len = static_cast(m_outpuDims[0].d[lenInd]);// / m_params.explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[0].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + cv::Mat rawMemory(1, dimensions * static_cast(len), CV_32FC1, output); + rawMemory = rawMemory.reshape(1, dimensions); + cv::transpose(rawMemory, rawMemory); + output = (float*)rawMemory.data; + + //std::cout << "output[0] mem:\n"; + //for (size_t ii = 0; ii < 100; ++ii) + //{ + // std::cout << ii << ": "; + // for (size_t jj = 0; jj < 20; ++jj) + // { + // std::cout << output[ii * 20 + jj] << " "; + // } + // std::cout << ";" << std::endl; + //} + //std::cout << ";" << std::endl; + + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * (nc + shapeDataSize); + + int classId = -1; + float objectConf = 0.f; + for (int j = 0; j < nc; ++j) + { + const float classConf = output[k + 4 + j]; + if (classConf > objectConf) + { + classId = j; + objectConf = classConf; + } + } + + //if (i == 0) + //{ + // for (int jj = 0; jj < 20; ++jj) + // { + // std::cout << output[jj] << " "; + // } + // std::cout << std::endl; + //} + + if (objectConf >= m_params.m_confThreshold) + { + classIds.push_back(classId); + confidences.push_back(objectConf); + + // (center x, center y, width, height) + float cx = fw * (output[k] - m_resizedROI.x); + float cy = fh * (output[k + 1] - m_resizedROI.y); + float width = fw * output[k + 2]; + float height = fh * output[k + 3]; + float angle = 180.f * output[k + nc + shapeDataSize - 1] / static_cast(M_PI); + rectBoxes.emplace_back(cv::Point2f(cx, cy), cv::Size2f(width, height), angle); + + //if (rectBoxes.size() == 1) + // std::cout << i << ": object_conf = " << objectConf << ", classId = " << classId << ", rect = " << rectBoxes.back().boundingRect() << ", angle = " << angle << std::endl; + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + //std::vector indices; + //cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.confThreshold, m_params.nmsThreshold, indices); + //resBoxes.reserve(indices.size()); + + resBoxes.reserve(rectBoxes.size()); + for (size_t bi = 0; bi < rectBoxes.size(); ++bi) + { + resBoxes.emplace_back(classIds[bi], confidences[bi], rectBoxes[bi]); + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/YoloONNXv9_bb.hpp b/src/Detector/tensorrt_onnx/YoloONNXv9_bb.hpp new file mode 100644 index 000000000..8d456abdd --- /dev/null +++ b/src/Detector/tensorrt_onnx/YoloONNXv9_bb.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include "YoloONNX.hpp" + +/// +/// \brief The YOLOv9_bb_onnx class +/// +class YOLOv9_bb_onnx : public YoloONNX +{ +public: + YOLOv9_bb_onnx(std::vector& inputTensorNames, std::vector& outputTensorNames) + { + inputTensorNames.push_back("images"); + outputTensorNames.push_back("output0"); + } + +protected: + /// + /// \brief GetResult + /// \param output + /// \return + /// + std::vector GetResult(size_t imgIdx, int /*keep_topk*/, const std::vector& outputs, cv::Size frameSize) + { + std::vector resBoxes; + + //0: name: images, size: 1x3x640x640 + //1: name: output0, size: 1x84x8400 + //84: 80 COCO classes + x + y + w + h + constexpr int shapeDataSize = 4; + + const float fw = static_cast(frameSize.width) / static_cast(m_resizedROI.width); + const float fh = static_cast(frameSize.height) / static_cast(m_resizedROI.height); + + auto output = outputs[0]; + + size_t ncInd = 1; + size_t lenInd = 2; + int nc = static_cast(m_outpuDims[0].d[ncInd] - shapeDataSize); + int dimensions = nc + shapeDataSize; + size_t len = static_cast(m_outpuDims[0].d[lenInd]);// / m_params.explicitBatchSize; + //auto Volume = [](const nvinfer1::Dims& d) + //{ + // return std::accumulate(d.d, d.d + d.nbDims, 1, std::multiplies()); + //}; + auto volume = len * m_outpuDims[0].d[ncInd]; // Volume(m_outpuDims[0]); + output += volume * imgIdx; + //std::cout << "len = " << len << ", nc = " << nc << ", m_params.confThreshold = " << m_params.confThreshold << ", volume = " << volume << std::endl; + + cv::Mat rawMemory(1, dimensions * static_cast(len), CV_32FC1, output); + rawMemory = rawMemory.reshape(1, dimensions); + cv::transpose(rawMemory, rawMemory); + output = (float*)rawMemory.data; + + //std::cout << "output[0] mem:\n"; + //for (size_t ii = 0; ii < 100; ++ii) + //{ + // std::cout << ii << ": "; + // for (size_t jj = 0; jj < 20; ++jj) + // { + // std::cout << output[ii * 20 + jj] << " "; + // } + // std::cout << ";" << std::endl; + //} + //std::cout << ";" << std::endl; + + std::vector classIds; + std::vector confidences; + std::vector rectBoxes; + classIds.reserve(len); + confidences.reserve(len); + rectBoxes.reserve(len); + + for (size_t i = 0; i < len; ++i) + { + // Box + size_t k = i * (nc + shapeDataSize); + + int classId = -1; + float objectConf = 0.f; + for (int j = 0; j < nc; ++j) + { + const float classConf = output[k + shapeDataSize + j]; + if (classConf > objectConf) + { + classId = j; + objectConf = classConf; + } + } + + //if (i == 0) + // std::cout << i << ": object_conf = " << object_conf << ", class_conf = " << class_conf << ", classId = " << classId << ", rect = " << cv::Rect(cvRound(x), cvRound(y), cvRound(width), cvRound(height)) << std::endl; + + if (objectConf >= m_params.m_confThreshold) + { + classIds.push_back(classId); + confidences.push_back(objectConf); + + // (center x, center y, width, height) to (x, y, w, h) + float x = fw * (output[k] - output[k + 2] / 2 - m_resizedROI.x); + float y = fh * (output[k + 1] - output[k + 3] / 2 - m_resizedROI.y); + float width = fw * output[k + 2]; + float height = fh * output[k + 3]; + rectBoxes.emplace_back(cvRound(x), cvRound(y), cvRound(width), cvRound(height)); + } + } + + // Non-maximum suppression to eliminate redudant overlapping boxes + std::vector indices; + cv::dnn::NMSBoxes(rectBoxes, confidences, m_params.m_confThreshold, m_params.m_nmsThreshold, indices); + resBoxes.reserve(indices.size()); + + for (size_t bi = 0; bi < indices.size(); ++bi) + { + resBoxes.emplace_back(classIds[indices[bi]], confidences[indices[bi]], rectBoxes[indices[bi]]); + } + + return resBoxes; + } +}; diff --git a/src/Detector/tensorrt_onnx/class_detector.cpp b/src/Detector/tensorrt_onnx/class_detector.cpp new file mode 100644 index 000000000..70f2aa48b --- /dev/null +++ b/src/Detector/tensorrt_onnx/class_detector.cpp @@ -0,0 +1,214 @@ +#include "class_detector.h" +#include "YoloONNX.hpp" + +#include "YoloONNXv5_bb.hpp" +#include "YoloONNXv6_bb.hpp" +#include "YoloONNXv7_bb.hpp" +#include "YoloONNXv7_instance.hpp" +#include "YoloONNXv8_bb.hpp" +#include "YoloONNXv8_obb.hpp" +#include "YoloONNXv8_instance.hpp" +#include "YoloONNXv9_bb.hpp" +#include "YoloONNXv10_bb.hpp" +#include "YoloONNXv11_bb.hpp" +#include "YoloONNXv11_obb.hpp" +#include "YoloONNXv11_instance.hpp" +#include "YoloONNXv12_bb.hpp" +#include "RFDETR_bb.hpp" +#include "RFDETR_is.hpp" +#include "DFINE_bb.hpp" +#include "YoloONNXv13_bb.hpp" +#include "DFINE_is.hpp" +#include "YoloONNXv26_bb.hpp" +#include "YoloONNXv26_obb.hpp" +#include "YoloONNXv26_instance.hpp" + +namespace tensor_rt +{ + /// + /// \brief The Detector::Impl class + /// + class Detector::Impl + { + public: + Impl() = default; + virtual ~Impl() = default; + + virtual bool Init(const Config& config) = 0; + virtual void Detect(const std::vector& mat_image, std::vector& vec_batch_result) = 0; + virtual cv::Size GetInputSize() const = 0; + }; + + /// + /// \brief The YoloDectectorImpl class + /// + class YoloONNXImpl final : public Detector::Impl + { + public: + bool Init(const Config& config) override + { + // The onnx file to load + m_params.m_onnxFileName = config.m_fileModelCfg.empty() ? config.m_fileModelWeights : config.m_fileModelCfg; //"yolov6s.onnx" + + switch (config.m_netType) + { + case ModelType::YOLOV5: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV6: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV7: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV7Mask: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV8: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV8_OBB: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV8Mask: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV9: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV10: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV11: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV11_OBB: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV11Mask: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV26: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV26_OBB: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV26Mask: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV12: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::RFDETR: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::RFDETR_IS: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::DFINE: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::YOLOV13: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + case ModelType::DFINE_IS: + m_detector = std::make_unique(m_params.m_inputTensorNames, m_params.m_outputTensorNames); + break; + } + + // Threshold values + m_params.m_confThreshold = config.m_detectThresh; + m_params.m_nmsThreshold = 0.5; + + m_params.m_videoMemory = config.m_videoMemory; + + // Batch size, you can modify to other batch size values if needed + m_params.m_explicitBatchSize = config.m_batchSize; + + m_params.m_precision = config.m_inferencePrecision; + m_params.m_netType = config.m_netType; + + std::string precisionStr; + std::map dictprecision; + dictprecision[tensor_rt::INT8] = "kINT8"; + dictprecision[tensor_rt::FP16] = "kHALF"; + dictprecision[tensor_rt::FP32] = "kFLOAT"; + auto precision = dictprecision.find(m_params.m_precision); + if (precision != dictprecision.end()) + precisionStr = precision->second; + m_params.m_engineFileName = config.m_fileModelCfg + "-" + precisionStr + "-batch" + std::to_string(config.m_batchSize) + ".engine"; + + return m_detector->Init(m_params); + } + + void Detect(const std::vector& mat_image, std::vector& vec_batch_result) override + { + vec_batch_result.clear(); + if (vec_batch_result.capacity() < mat_image.size()) + vec_batch_result.reserve(mat_image.size()); + + m_detector->Detect(mat_image, vec_batch_result); + } + + cv::Size GetInputSize() const override + { + return m_detector->GetInputSize(); + } + + private: + std::unique_ptr m_detector; + SampleYoloParams m_params; + }; + + /// + /// \brief Detector::Detector + /// + Detector::Detector() noexcept + { + } + + /// + /// \brief Detector::~Detector + /// + Detector::~Detector() + { + if (m_impl) + delete m_impl; + } + + /// + /// \brief Detector::Init + /// \param config + /// + bool Detector::Init(const Config& config) + { + if (m_impl) + delete m_impl; + + m_impl = new YoloONNXImpl(); + + bool res = m_impl->Init(config); + assert(res); + return res; + } + + /// + /// \brief Detector::Detect + /// \param mat_image + /// \param vec_batch_result + /// + void Detector::Detect(const std::vector& mat_image, std::vector& vec_batch_result) + { + m_impl->Detect(mat_image, vec_batch_result); + } + + /// + /// \brief Detector::GetInputSize + /// \return + /// + cv::Size Detector::GetInputSize() const + { + return m_impl->GetInputSize(); + } +} diff --git a/src/Detector/tensorrt_onnx/class_detector.h b/src/Detector/tensorrt_onnx/class_detector.h new file mode 100644 index 000000000..297806852 --- /dev/null +++ b/src/Detector/tensorrt_onnx/class_detector.h @@ -0,0 +1,124 @@ +#ifndef CLASS_DETECTOR_H_ +#define CLASS_DETECTOR_H_ + +#include "API.h" +#include +#include + +namespace tensor_rt +{ + /// + /// \brief The Result struct + /// + struct Result + { + cv::RotatedRect m_rrect; + cv::Rect m_brect; + int m_id = -1; + float m_prob = 0.f; + cv::Mat m_boxMask; + + Result(int id, float prob, const cv::Rect& brect) + : m_brect(brect), m_id(id), m_prob(prob) + { + m_rrect = cv::RotatedRect(m_brect.tl(), cv::Point2f(static_cast(m_brect.x + m_brect.width), static_cast(m_brect.y)), m_brect.br()); + if (m_rrect.size.width < 1) + m_rrect.size.width = 1; + if (m_rrect.size.height < 1) + m_rrect.size.height = 1; + } + + Result(int id, float prob, const cv::RotatedRect& rrect) + : m_rrect(rrect), m_id(id), m_prob(prob) + { + m_brect = m_rrect.boundingRect(); + } + }; + + using BatchResult = std::vector; + + /// + /// \brief The ModelType enum + /// + enum ModelType + { + YOLOV3, + YOLOV3_TINY, + YOLOV4, + YOLOV4_TINY, + YOLOV5, + YOLOV6, + YOLOV7, + YOLOV7Mask, + YOLOV8, + YOLOV8_OBB, + YOLOV8Mask, + YOLOV9, + YOLOV10, + YOLOV11, + YOLOV11_OBB, + YOLOV11Mask, + YOLOV12, + RFDETR, + RFDETR_IS, + DFINE, + YOLOV13, + DFINE_IS, + YOLOV26, + YOLOV26_OBB, + YOLOV26Mask + }; + + /// + /// \brief The Precision enum + /// + enum Precision + { + INT8 = 0, + FP16, + FP32 + }; + + /// + /// \brief The Config struct + /// + struct Config + { + std::string m_fileModelCfg = "yolov4.cfg"; + std::string m_fileModelWeights = "yolov4.weights"; + float m_detectThresh = 0.5f; + ModelType m_netType = YOLOV4; + Precision m_inferencePrecision = FP32; + int m_gpuInd = 0; + size_t m_videoMemory = 0; + uint32_t m_batchSize = 1; + std::string m_calibrationImageListFileTxt = "configs/calibration_images.txt"; + }; + + /// + /// \brief The Detector class + /// + class API Detector + { + public: + explicit Detector() noexcept; + ~Detector(); + + bool Init(const Config& config); + + void Detect(const std::vector& mat_image, std::vector& vec_batch_result); + + cv::Size GetInputSize() const; + + class Impl; + + private: + Detector(const Detector &); + const Detector& operator =(const Detector&) + { + } + + Impl* m_impl = nullptr; + }; +} +#endif // !CLASS_QH_DETECTOR_H_ diff --git a/src/Detector/tensorrt_onnx/cmake/FindCUDNN.cmake b/src/Detector/tensorrt_onnx/cmake/FindCUDNN.cmake new file mode 100644 index 000000000..7a692b055 --- /dev/null +++ b/src/Detector/tensorrt_onnx/cmake/FindCUDNN.cmake @@ -0,0 +1,104 @@ +# Distributed under the OSI-approved BSD 3-Clause License. +# Copyright Stefano Sinigardi + +#.rst: +# FindCUDNN +# -------- +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# This module will set the following variables in your project:: +# +# ``CUDNN_FOUND`` +# True if CUDNN found on the local system +# +# ``CUDNN_INCLUDE_DIRS`` +# Location of CUDNN header files. +# +# ``CUDNN_LIBRARIES`` +# The CUDNN libraries. +# +# ``CuDNN::CuDNN`` +# The CUDNN target +# + +include(FindPackageHandleStandardArgs) + +find_path(CUDNN_INCLUDE_DIR NAMES cudnn.h cudnn_v8.h cudnn_v7.h + HINTS $ENV{CUDA_PATH} $ENV{CUDA_TOOLKIT_ROOT_DIR} $ENV{CUDA_HOME} $ENV{CUDNN_ROOT_DIR} /usr/include + PATH_SUFFIXES cuda/include include) +find_library(CUDNN_LIBRARY NAMES cudnn cudnn8 cudnn7 + HINTS $ENV{CUDA_PATH} $ENV{CUDA_TOOLKIT_ROOT_DIR} $ENV{CUDA_HOME} $ENV{CUDNN_ROOT_DIR} /usr/lib/x86_64-linux-gnu/ + PATH_SUFFIXES lib lib64 cuda/lib cuda/lib64 lib/x64 cuda/lib/x64) +if(EXISTS "${CUDNN_INCLUDE_DIR}/cudnn.h") + file(READ ${CUDNN_INCLUDE_DIR}/cudnn.h CUDNN_HEADER_CONTENTS) +elseif(EXISTS "${CUDNN_INCLUDE_DIR}/cudnn_v8.h") + file(READ ${CUDNN_INCLUDE_DIR}/cudnn_v8.h CUDNN_HEADER_CONTENTS) +elseif(EXISTS "${CUDNN_INCLUDE_DIR}/cudnn_v7.h") + file(READ ${CUDNN_INCLUDE_DIR}/cudnn_v7.h CUDNN_HEADER_CONTENTS) +endif() +if(EXISTS "${CUDNN_INCLUDE_DIR}/cudnn_version.h") + file(READ "${CUDNN_INCLUDE_DIR}/cudnn_version.h" CUDNN_VERSION_H_CONTENTS) + string(APPEND CUDNN_HEADER_CONTENTS "${CUDNN_VERSION_H_CONTENTS}") + unset(CUDNN_VERSION_H_CONTENTS) +elseif(EXISTS "${CUDNN_INCLUDE_DIR}/cudnn_version_v8.h") + file(READ "${CUDNN_INCLUDE_DIR}/cudnn_version_v8.h" CUDNN_VERSION_H_CONTENTS) + string(APPEND CUDNN_HEADER_CONTENTS "${CUDNN_VERSION_H_CONTENTS}") + unset(CUDNN_VERSION_H_CONTENTS) +elseif(EXISTS "${CUDNN_INCLUDE_DIR}/cudnn_version_v7.h") + file(READ "${CUDNN_INCLUDE_DIR}/cudnn_version_v7.h" CUDNN_VERSION_H_CONTENTS) + string(APPEND CUDNN_HEADER_CONTENTS "${CUDNN_VERSION_H_CONTENTS}") + unset(CUDNN_VERSION_H_CONTENTS) +endif() +if(CUDNN_HEADER_CONTENTS) + string(REGEX MATCH "define CUDNN_MAJOR * +([0-9]+)" + _CUDNN_VERSION_MAJOR "${CUDNN_HEADER_CONTENTS}") + string(REGEX REPLACE "define CUDNN_MAJOR * +([0-9]+)" "\\1" + _CUDNN_VERSION_MAJOR "${_CUDNN_VERSION_MAJOR}") + string(REGEX MATCH "define CUDNN_MINOR * +([0-9]+)" + _CUDNN_VERSION_MINOR "${CUDNN_HEADER_CONTENTS}") + string(REGEX REPLACE "define CUDNN_MINOR * +([0-9]+)" "\\1" + _CUDNN_VERSION_MINOR "${_CUDNN_VERSION_MINOR}") + string(REGEX MATCH "define CUDNN_PATCHLEVEL * +([0-9]+)" + _CUDNN_VERSION_PATCH "${CUDNN_HEADER_CONTENTS}") + string(REGEX REPLACE "define CUDNN_PATCHLEVEL * +([0-9]+)" "\\1" + _CUDNN_VERSION_PATCH "${_CUDNN_VERSION_PATCH}") + if(NOT _CUDNN_VERSION_MAJOR) + set(CUDNN_VERSION "?") + else() + set(CUDNN_VERSION "${_CUDNN_VERSION_MAJOR}.${_CUDNN_VERSION_MINOR}.${_CUDNN_VERSION_PATCH}") + endif() +endif() + +set(CUDNN_INCLUDE_DIRS ${CUDNN_INCLUDE_DIR}) +set(CUDNN_LIBRARIES ${CUDNN_LIBRARY}) +mark_as_advanced(CUDNN_LIBRARY CUDNN_INCLUDE_DIR) + +find_package_handle_standard_args(CUDNN + REQUIRED_VARS CUDNN_INCLUDE_DIR CUDNN_LIBRARY + VERSION_VAR CUDNN_VERSION +) + +if(WIN32) + set(CUDNN_DLL_DIR ${CUDNN_INCLUDE_DIR}) + list(TRANSFORM CUDNN_DLL_DIR APPEND "/../bin") + find_file(CUDNN_LIBRARY_DLL NAMES cudnn64_${CUDNN_VERSION_MAJOR}.dll PATHS ${CUDNN_DLL_DIR}) +endif() + +if( CUDNN_FOUND AND NOT TARGET CuDNN::CuDNN ) + if( EXISTS "${CUDNN_LIBRARY_DLL}" ) + add_library( CuDNN::CuDNN SHARED IMPORTED ) + set_target_properties( CuDNN::CuDNN PROPERTIES + IMPORTED_LOCATION "${CUDNN_LIBRARY_DLL}" + IMPORTED_IMPLIB "${CUDNN_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${CUDNN_INCLUDE_DIR}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" ) + else() + add_library( CuDNN::CuDNN UNKNOWN IMPORTED ) + set_target_properties( CuDNN::CuDNN PROPERTIES + IMPORTED_LOCATION "${CUDNN_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${CUDNN_INCLUDE_DIR}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" ) + endif() +endif() diff --git a/src/Detector/tensorrt_onnx/cmake/FindCUDNN.cmake_ b/src/Detector/tensorrt_onnx/cmake/FindCUDNN.cmake_ new file mode 100644 index 000000000..37388d30b --- /dev/null +++ b/src/Detector/tensorrt_onnx/cmake/FindCUDNN.cmake_ @@ -0,0 +1,180 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. +#.rst: +# FindCUDNN +# ------- +# +# Find CUDNN library +# +# Valiables that affect result: +# , , : as usual +# +# : as usual, plus we do find '5.1' version if you wanted '5' +# (not if you wanted '5.0', as usual) +# +# Result variables +# ^^^^^^^^^^^^^^^^ +# +# This module will set the following variables in your project: +# +# ``CUDNN_INCLUDE`` +# where to find cudnn.h. +# ``CUDNN_LIBRARY`` +# the libraries to link against to use CUDNN. +# ``CUDNN_FOUND`` +# If false, do not try to use CUDNN. +# ``CUDNN_VERSION`` +# Version of the CUDNN library we looked for +# +# Exported functions +# ^^^^^^^^^^^^^^^^ +# function(CUDNN_INSTALL version __dest_libdir [__dest_incdir]) +# This function will try to download and install CUDNN. +# CUDNN5 and CUDNN6 are supported. +# +# + +function(CUDNN_INSTALL version dest_libdir dest_incdir dest_bindir) + message(STATUS "CUDNN_INSTALL: Installing CUDNN ${version}, lib:${dest_libdir}, inc:${dest_incdir}, bin:${dest_bindir}") + string(REGEX REPLACE "-rc$" "" version_base "${version}") + set(tar_libdir cuda/lib64) + set(tar_incdir cuda/include) + + if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(url_extension tgz) + if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") + set(url_arch_name linux-x64 ) + elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "ppc") + set(url_arch_name linux-ppc64le ) + # TX1 has to be installed via JetPack + endif() + elseif (APPLE) + set(url_extension tgz) + set(tar_libdir cuda/lib) + set(url_arch_name osx-x64) + elseif(WIN32) + set(url_extension zip) + set(tar_bindir cuda/bin) + set(tar_libdir cuda/lib/x64) + if(CMAKE_SYSTEM_VERSION MATCHES "10") + set(url_arch_name windows10-x64) + else() + set(url_arch_name windows7-x64) + endif() + endif() + + # Download and install CUDNN locally if not found on the system + if(url_arch_name) + set(download_dir ${CMAKE_CURRENT_BINARY_DIR}/downloads/cudnn${version}) + file(MAKE_DIRECTORY ${download_dir}) + set(cudnn_filename cudnn-${CUDA_VERSION}-${url_arch_name}-v${version}.${url_extension}) + set(base_url http://developer.download.nvidia.com/compute/redist/cudnn) + set(cudnn_url ${base_url}/v${version_base}/${cudnn_filename}) + set(cudnn_file ${download_dir}/${cudnn_filename}) + + if(NOT EXISTS ${cudnn_file}) + message(STATUS "Downloading CUDNN library from NVIDIA...") + file(DOWNLOAD ${cudnn_url} ${cudnn_file} + SHOW_PROGRESS STATUS cudnn_status + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzvf ${cudnn_file} WORKING_DIRECTORY ${download_dir} RESULT_VARIABLE cudnn_status) + + if(NOT "${cudnn_status}" MATCHES "0") + message(STATUS "Was not able to download CUDNN from ${cudnn_url}. Please install CuDNN manually from https://developer.nvidia.com/cuDNN") + endif() + endif() + + if(dest_bindir AND tar_bindir) + file(COPY ${download_dir}/${tar_bindir}/ DESTINATION ${dest_bindir}) + endif() + + if(dest_incdir) + file(COPY ${download_dir}/${tar_incdir}/ DESTINATION ${dest_incdir}) + endif() + + file(COPY ${download_dir}/${tar_libdir}/ DESTINATION ${dest_libdir} ) + + get_filename_component(dest_dir ${dest_libdir} DIRECTORY) + + set(CUDNN_ROOT_DIR ${dest_dir} PARENT_SCOPE) + unset(CUDNN_LIBRARY CACHE) + unset(CUDNN_INCLUDE_DIR CACHE) + + endif(url_arch_name) +endfunction() + +##################################################### + +find_package(PkgConfig) +pkg_check_modules(PC_CUDNN QUIET CUDNN) + +get_filename_component(__libpath_cudart "${CUDA_CUDART_LIBRARY}" PATH) + +# We use major only in library search as major/minor is not entirely consistent among platforms. +# Also, looking for exact minor version of .so is in general not a good idea. +# More strict enforcement of minor/patch version is done if/when the header file is examined. +if(CUDNN_FIND_VERSION_EXACT) + SET(__cudnn_ver_suffix ".${CUDNN_FIND_VERSION_MAJOR}") + SET(__cudnn_lib_win_name cudnn64_${CUDNN_FIND_VERSION_MAJOR}) +else() + SET(__cudnn_lib_win_name cudnn64) +endif() + +find_library(CUDNN_LIBRARY + NAMES libcudnn.so${__cudnn_ver_suffix} libcudnn${__cudnn_ver_suffix}.dylib ${__cudnn_lib_win_name} + PATHS $ENV{LD_LIBRARY_PATH} ${__libpath_cudart} ${CUDNN_ROOT_DIR} ${PC_CUDNN_LIBRARY_DIRS} ${CMAKE_INSTALL_PREFIX} + PATH_SUFFIXES lib lib64 bin + DOC "CUDNN library." ) + +if(CUDNN_LIBRARY) + SET(CUDNN_MAJOR_VERSION ${CUDNN_FIND_VERSION_MAJOR}) + set(CUDNN_VERSION ${CUDNN_MAJOR_VERSION}) + get_filename_component(__found_cudnn_root ${CUDNN_LIBRARY} PATH) + find_path(CUDNN_INCLUDE_DIR + NAMES cudnn.h + HINTS ${PC_CUDNN_INCLUDE_DIRS} ${CUDNN_ROOT_DIR} ${CUDA_TOOLKIT_INCLUDE} ${__found_cudnn_root} + PATH_SUFFIXES include + DOC "Path to CUDNN include directory." ) +endif() + +if(CUDNN_LIBRARY AND CUDNN_INCLUDE_DIR) + file(READ ${CUDNN_INCLUDE_DIR}/cudnn.h CUDNN_VERSION_FILE_CONTENTS) + string(REGEX MATCH "define CUDNN_MAJOR * +([0-9]+)" + CUDNN_MAJOR_VERSION "${CUDNN_VERSION_FILE_CONTENTS}") + string(REGEX REPLACE "define CUDNN_MAJOR * +([0-9]+)" "\\1" + CUDNN_MAJOR_VERSION "${CUDNN_MAJOR_VERSION}") + string(REGEX MATCH "define CUDNN_MINOR * +([0-9]+)" + CUDNN_MINOR_VERSION "${CUDNN_VERSION_FILE_CONTENTS}") + string(REGEX REPLACE "define CUDNN_MINOR * +([0-9]+)" "\\1" + CUDNN_MINOR_VERSION "${CUDNN_MINOR_VERSION}") + string(REGEX MATCH "define CUDNN_PATCHLEVEL * +([0-9]+)" + CUDNN_PATCH_VERSION "${CUDNN_VERSION_FILE_CONTENTS}") + string(REGEX REPLACE "define CUDNN_PATCHLEVEL * +([0-9]+)" "\\1" + CUDNN_PATCH_VERSION "${CUDNN_PATCH_VERSION}") + set(CUDNN_VERSION ${CUDNN_MAJOR_VERSION}.${CUDNN_MINOR_VERSION}) +endif() + +if(CUDNN_MAJOR_VERSION) + ## Fixing the case where 5.1 does not fit 'exact' 5. + if(CUDNN_FIND_VERSION_EXACT AND NOT CUDNN_FIND_VERSION_MINOR) + if("${CUDNN_MAJOR_VERSION}" STREQUAL "${CUDNN_FIND_VERSION_MAJOR}") + set(CUDNN_VERSION ${CUDNN_FIND_VERSION}) + endif() + endif() +else() + # Try to set CUDNN version from config file + set(CUDNN_VERSION ${PC_CUDNN_CFLAGS_OTHER}) +endif() + +find_package_handle_standard_args( + CUDNN + FOUND_VAR CUDNN_FOUND + REQUIRED_VARS CUDNN_LIBRARY + VERSION_VAR CUDNN_VERSION + ) + +if(CUDNN_FOUND) + set(CUDNN_LIBRARIES ${CUDNN_LIBRARY}) + set(CUDNN_INCLUDE_DIRS ${CUDNN_INCLUDE_DIR}) + set(CUDNN_DEFINITIONS ${PC_CUDNN_CFLAGS_OTHER}) +endif() diff --git a/src/Detector/tensorrt_onnx/cmake/FindTensorRT.cmake b/src/Detector/tensorrt_onnx/cmake/FindTensorRT.cmake new file mode 100644 index 000000000..fc9e92d72 --- /dev/null +++ b/src/Detector/tensorrt_onnx/cmake/FindTensorRT.cmake @@ -0,0 +1,75 @@ +# This module defines the following variables: +# +# :: +# +# TensorRT_INCLUDE_DIRS +# TensorRT_LIBRARIES +# TensorRT_FOUND +# +# :: +# +# TensorRT_VERSION_STRING - version (x.y.z) +# TensorRT_VERSION_MAJOR - major version (x) +# TensorRT_VERSION_MINOR - minor version (y) +# TensorRT_VERSION_PATCH - patch version (z) +# +# Hints +# ^^^^^ +# A user may set ``TensorRT_ROOT`` to an installation root to tell this module where to look. +# +set(_TensorRT_SEARCHES) + +if(TensorRT_ROOT) + set(_TensorRT_SEARCH_ROOT PATHS ${TensorRT_ROOT} NO_DEFAULT_PATH) + list(APPEND _TensorRT_SEARCHES _TensorRT_SEARCH_ROOT) +endif() + +# appends some common paths +set(_TensorRT_SEARCH_NORMAL + PATHS "/usr" +) +list(APPEND _TensorRT_SEARCHES _TensorRT_SEARCH_NORMAL) + +# Include dir +foreach(search ${_TensorRT_SEARCHES}) + find_path(TensorRT_INCLUDE_DIR NAMES NvInfer.h ${${search}} PATH_SUFFIXES include) +endforeach() + +if(NOT TensorRT_LIBRARY) + foreach(search ${_TensorRT_SEARCHES}) + find_library(TensorRT_LIBRARY NAMES nvinfer ${${search}} PATH_SUFFIXES lib) + endforeach() +endif() + +mark_as_advanced(TensorRT_INCLUDE_DIR) + +if(TensorRT_INCLUDE_DIR AND EXISTS "${TensorRT_INCLUDE_DIR}/NvInferVersion.h") + file(STRINGS "${TensorRT_INCLUDE_DIR}/NvInferVersion.h" TensorRT_MAJOR REGEX "^#define NV_TENSORRT_MAJOR [0-9]+.*$") + file(STRINGS "${TensorRT_INCLUDE_DIR}/NvInferVersion.h" TensorRT_MINOR REGEX "^#define NV_TENSORRT_MINOR [0-9]+.*$") + file(STRINGS "${TensorRT_INCLUDE_DIR}/NvInferVersion.h" TensorRT_PATCH REGEX "^#define NV_TENSORRT_PATCH [0-9]+.*$") + + string(REGEX REPLACE "^#define NV_TENSORRT_MAJOR ([0-9]+).*$" "\\1" TensorRT_VERSION_MAJOR "${TensorRT_MAJOR}") + string(REGEX REPLACE "^#define NV_TENSORRT_MINOR ([0-9]+).*$" "\\1" TensorRT_VERSION_MINOR "${TensorRT_MINOR}") + string(REGEX REPLACE "^#define NV_TENSORRT_PATCH ([0-9]+).*$" "\\1" TensorRT_VERSION_PATCH "${TensorRT_PATCH}") + set(TensorRT_VERSION_STRING "${TensorRT_VERSION_MAJOR}.${TensorRT_VERSION_MINOR}.${TensorRT_VERSION_PATCH}") + message("TensorRT_VERSION_STRING from ${TensorRT_INCLUDE_DIR}/NvInferVersion.h: ${TensorRT_VERSION_STRING}") +else() + message("${TensorRT_INCLUDE_DIR}/NvInferVersion.h not found") +endif() + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(TensorRT REQUIRED_VARS TensorRT_LIBRARY TensorRT_INCLUDE_DIR VERSION_VAR TensorRT_VERSION_STRING) + +if(TensorRT_FOUND) + set(TensorRT_INCLUDE_DIRS ${TensorRT_INCLUDE_DIR}) + + if(NOT TensorRT_LIBRARIES) + set(TensorRT_LIBRARIES ${TensorRT_LIBRARY}) + endif() + + if(NOT TARGET TensorRT::TensorRT) + add_library(TensorRT::TensorRT UNKNOWN IMPORTED) + set_target_properties(TensorRT::TensorRT PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${TensorRT_INCLUDE_DIRS}") + set_property(TARGET TensorRT::TensorRT APPEND PROPERTY IMPORTED_LOCATION "${TensorRT_LIBRARY}") + endif() +endif() diff --git a/src/Detector/tensorrt_onnx/cmake/FindTensorRT.cmake_ b/src/Detector/tensorrt_onnx/cmake/FindTensorRT.cmake_ new file mode 100644 index 000000000..f4f9f42c0 --- /dev/null +++ b/src/Detector/tensorrt_onnx/cmake/FindTensorRT.cmake_ @@ -0,0 +1,124 @@ +# ~~~ +# Copyright 2021 Olivier Le Doeuff +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# This module defines the following variables: +# +# - TensorRT_FOUND: A boolean specifying whether or not TensorRT was found. +# - TensorRT_VERSION: The exact version of TensorRT found +# - TensorRT_VERSION_MAJOR: The major version of TensorRT. +# - TensorRT_VERSION_MINOR: The minor version of TensorRT. +# - TensorRT_VERSION_PATCH: The patch version of TensorRT. +# - TensorRT_VERSION_TWEAK: The tweak version of TensorRT. +# - TensorRT_INCLUDE_DIRS: The path to TensorRT ``include`` folder containing the header files required to compile a project linking against TensorRT. +# - TensorRT_LIBRARY_DIRS: The path to TensorRT library directory that contains libraries. +# +# This module create following targets: +# - trt::nvinfer +# - trt::nvinfer_plugin +# - trt::nvonnxparser +# - trt::nvparsers +# This script was inspired from https://github.com/NicolasIRAGNE/CMakeScripts +# This script was inspired from https://github.com/NVIDIA/tensorrt-laboratory/blob/master/cmake/FindTensorRT.cmake +# +# Hints +# ^^^^^ +# A user may set ``TensorRT_ROOT`` to an installation root to tell this module where to look. +# ~~~ + +if(NOT TensorRT_FIND_COMPONENTS) + set(TensorRT_FIND_COMPONENTS nvinfer nvinfer_plugin nvonnxparser) +endif() +set(TensorRT_LIBRARIES) + +# find the include directory of TensorRT +find_path( + TensorRT_INCLUDE_DIR + NAMES NvInfer.h + PATHS ${TensorRT_ROOT} ENV TensorRT_ROOT + PATH_SUFFIXES include +) + +string(FIND ${TensorRT_INCLUDE_DIR} "NOTFOUND" _include_dir_notfound) +if(NOT _include_dir_notfound EQUAL -1) + if(TensorRT_FIND_REQUIRED) + message(FATAL_ERROR "Fail to find TensorRT, please set TensorRT_ROOT. Include path not found.") + endif() + return() + +if(NOT TensorRT_LIBRARY) + foreach(search ${_TensorRT_SEARCHES}) + find_library(TRT_NVONNX_PARSER NAMES nvonnxparser ${${search}} PATH_SUFFIXES lib lib64 lib/x64) + find_library(TRT_NVINFER NAMES nvinfer ${${search}} PATH_SUFFIXES lib lib64 lib/x64) + find_library(TRT_NVINFER_PLUGIN NAMES nvinfer_plugin ${${search}} PATH_SUFFIXES lib lib64 lib/x64) + endforeach() + list(APPEND TensorRT_LIBRARY ${TRT_NVINFER} ${TRT_NVINFER_PLUGIN} ${TRT_NVONNX_PARSER}) + +endif() +set(TensorRT_INCLUDE_DIRS ${TensorRT_INCLUDE_DIR}) + +# Extract version of tensorrt +if(EXISTS "${TensorRT_INCLUDE_DIR}/NvInferVersion.h") + file(STRINGS "${TensorRT_INCLUDE_DIR}/NvInferVersion.h" TensorRT_MAJOR REGEX "^#define NV_TENSORRT_MAJOR [0-9]+.*$") + file(STRINGS "${TensorRT_INCLUDE_DIR}/NvInferVersion.h" TensorRT_MINOR REGEX "^#define NV_TENSORRT_MINOR [0-9]+.*$") + file(STRINGS "${TensorRT_INCLUDE_DIR}/NvInferVersion.h" TensorRT_PATCH REGEX "^#define NV_TENSORRT_PATCH [0-9]+.*$") + file(STRINGS "${TensorRT_INCLUDE_DIR}/NvInferVersion.h" TensorRT_TWEAK REGEX "^#define NV_TENSORRT_BUILD [0-9]+.*$") + + string(REGEX REPLACE "^#define NV_TENSORRT_MAJOR ([0-9]+).*$" "\\1" TensorRT_VERSION_MAJOR "${TensorRT_MAJOR}") + string(REGEX REPLACE "^#define NV_TENSORRT_MINOR ([0-9]+).*$" "\\1" TensorRT_VERSION_MINOR "${TensorRT_MINOR}") + string(REGEX REPLACE "^#define NV_TENSORRT_PATCH ([0-9]+).*$" "\\1" TensorRT_VERSION_PATCH "${TensorRT_PATCH}") + string(REGEX REPLACE "^#define NV_TENSORRT_BUILD ([0-9]+).*$" "\\1" TensorRT_VERSION_TWEAK "${TensorRT_TWEAK}") + set(TensorRT_VERSION "${TensorRT_VERSION_MAJOR}.${TensorRT_VERSION_MINOR}.${TensorRT_VERSION_PATCH}.${TensorRT_VERSION_TWEAK}") +endif() + +function(_find_trt_component component) + + # Find library for component (ie nvinfer, nvparsers, etc...) + find_library( + TensorRT_${component}_LIBRARY + NAMES ${component} + PATHS ${TensorRT_ROOT} ${TENSORRT_LIBRARY_DIR} ENV TensorRT_ROOT + ) + + string(FIND ${TensorRT_${component}_LIBRARY} "NOTFOUND" _library_not_found) + + if(NOT TensorRT_LIBRARY_DIR) + get_filename_component(_path ${TensorRT_${component}_LIBRARY} DIRECTORY) + set(TensorRT_LIBRARY_DIR + "${_path}" + CACHE INTERNAL "TensorRT_LIBRARY_DIR" + ) + endif() + + if(NOT TensorRT_LIBRARY_DIRS) + get_filename_component(_path ${TensorRT_${component}_LIBRARY} DIRECTORY) + set(TensorRT_LIBRARY_DIRS + "${_path}" + CACHE INTERNAL "TensorRT_LIBRARY_DIRS" + ) + endif() + + # Library found, and doesn't already exists + if(_library_not_found EQUAL -1 AND NOT TARGET trt::${component}) + set(TensorRT_${component}_FOUND + TRUE + CACHE INTERNAL "Found ${component}" + ) + + # Create a target + add_library(trt::${component} IMPORTED INTERFACE) + target_include_directories(trt::${component} SYSTEM INTERFACE "${TensorRT_INCLUDE_DIRS}") + target_link_libraries(trt::${component} INTERFACE "${TensorRT_${component}_LIBRARY}") + set(TensorRT_LIBRARIES ${TensorRT_LIBRARIES} ${TensorRT_${component}_LIBRARY}) + endif() + +endfunction() + +# Find each components +foreach(component IN LISTS TensorRT_FIND_COMPONENTS) + _find_trt_component(${component}) +endforeach() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(TensorRT HANDLE_COMPONENTS VERSION_VAR TensorRT_VERSION REQUIRED_VARS TensorRT_INCLUDE_DIR) diff --git a/src/Detector/tensorrt_onnx/common/BatchStream.h b/src/Detector/tensorrt_onnx/common/BatchStream.h new file mode 100644 index 000000000..7bbb89ea7 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/BatchStream.h @@ -0,0 +1,380 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ +#ifndef BATCH_STREAM_H +#define BATCH_STREAM_H + +#include "NvInfer.h" +#include "common.h" +#include +#include +#include + +class IBatchStream +{ +public: + virtual void reset(int firstBatch) = 0; + virtual bool next() = 0; + virtual void skip(int skipCount) = 0; + virtual float* getBatch() = 0; + virtual float* getLabels() = 0; + virtual int getBatchesRead() const = 0; + virtual int getBatchSize() const = 0; + virtual nvinfer1::Dims getDims() const = 0; +}; + +class MNISTBatchStream : public IBatchStream +{ +public: + MNISTBatchStream(int batchSize, int maxBatches, const std::string& dataFile, const std::string& labelsFile, + const std::vector& directories) + : mBatchSize{batchSize} + , mMaxBatches{maxBatches} + , mDims{3, {1, 28, 28}} //!< We already know the dimensions of MNIST images. + { + readDataFile(locateFile(dataFile, directories)); + readLabelsFile(locateFile(labelsFile, directories)); + } + + void reset(int firstBatch) override + { + mBatchCount = firstBatch; + } + + bool next() override + { + if (mBatchCount >= mMaxBatches) + { + return false; + } + ++mBatchCount; + return true; + } + + void skip(int skipCount) override + { + mBatchCount += skipCount; + } + + float* getBatch() override + { + return mData.data() + (mBatchCount * mBatchSize * samplesCommon::volume(mDims)); + } + + float* getLabels() override + { + return mLabels.data() + (mBatchCount * mBatchSize); + } + + int getBatchesRead() const override + { + return mBatchCount; + } + + int getBatchSize() const override + { + return mBatchSize; + } + + nvinfer1::Dims getDims() const override + { + return nvinfer1::Dims{4, {mBatchSize, mDims.d[0], mDims.d[1], mDims.d[2]}}; + } + +private: + void readDataFile(const std::string& dataFilePath) + { + std::ifstream file{dataFilePath.c_str(), std::ios::binary}; + + int magicNumber, numImages, imageH, imageW; + file.read(reinterpret_cast(&magicNumber), sizeof(magicNumber)); + // All values in the MNIST files are big endian. + magicNumber = samplesCommon::swapEndianness(magicNumber); + ASSERT(magicNumber == 2051 && "Magic Number does not match the expected value for an MNIST image set"); + + // Read number of images and dimensions + file.read(reinterpret_cast(&numImages), sizeof(numImages)); + file.read(reinterpret_cast(&imageH), sizeof(imageH)); + file.read(reinterpret_cast(&imageW), sizeof(imageW)); + + numImages = samplesCommon::swapEndianness(numImages); + imageH = samplesCommon::swapEndianness(imageH); + imageW = samplesCommon::swapEndianness(imageW); + + // The MNIST data is made up of unsigned bytes, so we need to cast to float and normalize. + int numElements = numImages * imageH * imageW; + std::vector rawData(numElements); + file.read(reinterpret_cast(rawData.data()), numElements * sizeof(uint8_t)); + mData.resize(numElements); + std::transform( + rawData.begin(), rawData.end(), mData.begin(), [](uint8_t val) { return static_cast(val) / 255.F; }); + } + + void readLabelsFile(const std::string& labelsFilePath) + { + std::ifstream file{labelsFilePath.c_str(), std::ios::binary}; + int magicNumber, numImages; + file.read(reinterpret_cast(&magicNumber), sizeof(magicNumber)); + // All values in the MNIST files are big endian. + magicNumber = samplesCommon::swapEndianness(magicNumber); + ASSERT(magicNumber == 2049 && "Magic Number does not match the expected value for an MNIST labels file"); + + file.read(reinterpret_cast(&numImages), sizeof(numImages)); + numImages = samplesCommon::swapEndianness(numImages); + + std::vector rawLabels(numImages); + file.read(reinterpret_cast(rawLabels.data()), numImages * sizeof(uint8_t)); + mLabels.resize(numImages); + std::transform( + rawLabels.begin(), rawLabels.end(), mLabels.begin(), [](uint8_t val) { return static_cast(val); }); + } + + int mBatchSize{0}; + int mBatchCount{0}; //!< The batch that will be read on the next invocation of next() + int mMaxBatches{0}; + nvinfer1::Dims mDims{}; + std::vector mData{}; + std::vector mLabels{}; +}; + +class BatchStream : public IBatchStream +{ +public: + BatchStream(int batchSize, int maxBatches, std::string const& prefix, std::string const& suffix, + std::vector const& directories) + : mBatchSize(batchSize) + , mMaxBatches(maxBatches) + , mPrefix(prefix) + , mSuffix(suffix) + , mDataDir(directories) + { + std::ifstream file(locateFile(mPrefix + std::string("0") + mSuffix, mDataDir).c_str(), std::ios::binary); + ASSERT(file.good()); + int d[4]; + file.read(reinterpret_cast(d), 4 * sizeof(int32_t)); + mDims.nbDims = 4; // The number of dimensions. + mDims.d[0] = d[0]; // Batch Size + mDims.d[1] = d[1]; // Channels + mDims.d[2] = d[2]; // Height + mDims.d[3] = d[3]; // Width + ASSERT(mDims.d[0] > 0 && mDims.d[1] > 0 && mDims.d[2] > 0 && mDims.d[3] > 0); + + mImageSize = static_cast(mDims.d[1] * mDims.d[2] * mDims.d[3]); + mBatch.resize(mBatchSize * mImageSize, 0); + mLabels.resize(mBatchSize, 0); + mFileBatch.resize(mDims.d[0] * mImageSize, 0); + mFileLabels.resize(mDims.d[0], 0); + } + + BatchStream(int batchSize, int maxBatches, std::string const& prefix, std::vector const& directories) + : BatchStream(batchSize, maxBatches, prefix, ".batch", directories) + { + } + + BatchStream(int batchSize, int maxBatches, nvinfer1::Dims const& dims, std::string const& listFile, + std::vector const& directories) + : mBatchSize(batchSize) + , mMaxBatches(maxBatches) + , mDims(dims) + , mListFile(listFile) + , mDataDir(directories) + { + mImageSize = static_cast(mDims.d[1] * mDims.d[2] * mDims.d[3]); + mBatch.resize(mBatchSize * mImageSize, 0); + mLabels.resize(mBatchSize, 0); + mFileBatch.resize(mDims.d[0] * mImageSize, 0); + mFileLabels.resize(mDims.d[0], 0); + } + + // Resets data members + void reset(int firstBatch) override + { + mBatchCount = 0; + mFileCount = 0; + mFileBatchPos = static_cast(mDims.d[0]); + skip(firstBatch); + } + + // Advance to next batch and return true, or return false if there is no batch left. + bool next() override + { + if (mBatchCount == mMaxBatches) + { + return false; + } + + for (int64_t csize = 1, batchPos = 0; batchPos < mBatchSize; batchPos += csize, mFileBatchPos += csize) + { + ASSERT(mFileBatchPos > 0 && mFileBatchPos <= mDims.d[0]); + if (mFileBatchPos == mDims.d[0] && !update()) + { + return false; + } + + // copy the smaller of: elements left to fulfill the request, or elements left in the file buffer. + csize = std::min(mBatchSize - batchPos, mDims.d[0] - mFileBatchPos); + std::copy_n( + getFileBatch() + mFileBatchPos * mImageSize, csize * mImageSize, getBatch() + batchPos * mImageSize); + std::copy_n(getFileLabels() + mFileBatchPos, csize, getLabels() + batchPos); + } + mBatchCount++; + return true; + } + + // Skips the batches + void skip(int skipCount) override + { + if (mBatchSize >= mDims.d[0] && mBatchSize % mDims.d[0] == 0 && mFileBatchPos == mDims.d[0]) + { + mFileCount += skipCount * mBatchSize / mDims.d[0]; + return; + } + + int x = mBatchCount; + for (int i = 0; i < skipCount; i++) + { + next(); + } + mBatchCount = x; + } + + float* getBatch() override + { + return mBatch.data(); + } + + float* getLabels() override + { + return mLabels.data(); + } + + int getBatchesRead() const override + { + return mBatchCount; + } + + int getBatchSize() const override + { + return static_cast(mBatchSize); + } + + nvinfer1::Dims getDims() const override + { + return mDims; + } + +private: + float* getFileBatch() + { + return mFileBatch.data(); + } + + float* getFileLabels() + { + return mFileLabels.data(); + } + + bool update() + { + if (mListFile.empty()) + { + std::string inputFileName = locateFile(mPrefix + std::to_string(mFileCount++) + mSuffix, mDataDir); + std::ifstream file(inputFileName.c_str(), std::ios::binary); + if (!file) + { + return false; + } + int d[4]; + file.read(reinterpret_cast(d), 4 * sizeof(int32_t)); + ASSERT(mDims.d[0] == d[0] && mDims.d[1] == d[1] && mDims.d[2] == d[2] && mDims.d[3] == d[3]); + file.read(reinterpret_cast(getFileBatch()), sizeof(float) * mDims.d[0] * mImageSize); + file.read(reinterpret_cast(getFileLabels()), sizeof(float) * mDims.d[0]); + } + else + { + std::vector fNames; + std::ifstream file(locateFile(mListFile, mDataDir), std::ios::binary); + if (!file) + { + return false; + } + + sample::gLogInfo << "Batch #" << mFileCount << std::endl; + file.seekg(((mBatchCount * mBatchSize)) * 7); + + for (int i = 1; i <= mBatchSize; i++) + { + std::string sName; + std::getline(file, sName); + sName = sName + ".ppm"; + sample::gLogInfo << "Calibrating with file " << sName << std::endl; + fNames.emplace_back(sName); + } + + mFileCount++; + + const int imageC = 3; + const int imageH = 300; + const int imageW = 300; + std::vector> ppms(fNames.size()); + for (uint32_t i = 0; i < fNames.size(); ++i) + { + readPPMFile(locateFile(fNames[i], mDataDir), ppms[i]); + } + + std::vector data(samplesCommon::volume(mDims)); + const float scale = 2.0 / 255.0; + const float bias = 1.0; + long int volChl = static_cast(mDims.d[2] * mDims.d[3]); + + // Normalize input data + int64_t volImg = mDims.d[1] * mDims.d[2] * mDims.d[3]; + for (int i = 0; i < mBatchSize; ++i) + { + for (int c = 0; c < mDims.d[1]; ++c) + { + for (int j = 0; j < volChl; ++j) + { + data[i * volImg + c * volChl + j] = scale * float(ppms[i].buffer[j * mDims.d[1] + c]) - bias; + } + } + } + + std::copy_n(data.data(), mDims.d[0] * mImageSize, getFileBatch()); + } + + mFileBatchPos = 0; + return true; + } + + int64_t mBatchSize{0}; + int mMaxBatches{0}; + int mBatchCount{0}; + int mFileCount{0}; + int mFileBatchPos{0}; + int mImageSize{0}; + std::vector mBatch; //!< Data for the batch + std::vector mLabels; //!< Labels for the batch + std::vector mFileBatch; //!< List of image files + std::vector mFileLabels; //!< List of label files + std::string mPrefix; //!< Batch file name prefix + std::string mSuffix; //!< Batch file name suffix + nvinfer1::Dims mDims; //!< Input dimensions + std::string mListFile; //!< File name of the list of image names + std::vector mDataDir; //!< Directories where the files can be found +}; + +#endif diff --git a/src/Detector/tensorrt_onnx/common/EntropyCalibrator.h b/src/Detector/tensorrt_onnx/common/EntropyCalibrator.h new file mode 100644 index 000000000..67a0130ee --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/EntropyCalibrator.h @@ -0,0 +1,136 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef ENTROPY_CALIBRATOR_H +#define ENTROPY_CALIBRATOR_H + +#include "BatchStream.h" +#include "NvInfer.h" + +//! \class EntropyCalibratorImpl +//! +//! \brief Implements common functionality for Entropy calibrators. +//! +template +class EntropyCalibratorImpl +{ +public: + EntropyCalibratorImpl(TBatchStream const& stream, int firstBatch, std::string const& networkName, + const char* inputBlobName, bool readCache = true) + : mStream{stream} + , mCalibrationTableName("CalibrationTable" + networkName) + , mInputBlobName(inputBlobName) + , mReadCache(readCache) + { + nvinfer1::Dims dims = mStream.getDims(); + mInputCount = samplesCommon::volume(dims); + CHECK(cudaMalloc(&mDeviceInput, mInputCount * sizeof(float))); + mStream.reset(firstBatch); + } + + virtual ~EntropyCalibratorImpl() + { + CHECK(cudaFree(mDeviceInput)); + } + + int getBatchSize() const noexcept + { + return mStream.getBatchSize(); + } + + bool getBatch(void* bindings[], const char* names[], int nbBindings) noexcept + { + if (!mStream.next()) + { + return false; + } + CHECK(cudaMemcpy(mDeviceInput, mStream.getBatch(), mInputCount * sizeof(float), cudaMemcpyHostToDevice)); + ASSERT(!strcmp(names[0], mInputBlobName)); + bindings[0] = mDeviceInput; + return true; + } + + const void* readCalibrationCache(size_t& length) noexcept + { + mCalibrationCache.clear(); + std::ifstream input(mCalibrationTableName, std::ios::binary); + input >> std::noskipws; + if (mReadCache && input.good()) + { + std::copy(std::istream_iterator(input), std::istream_iterator(), + std::back_inserter(mCalibrationCache)); + } + length = mCalibrationCache.size(); + return length ? mCalibrationCache.data() : nullptr; + } + + void writeCalibrationCache(const void* cache, size_t length) noexcept + { + std::ofstream output(mCalibrationTableName, std::ios::binary); + output.write(reinterpret_cast(cache), length); + } + +private: + TBatchStream mStream; + size_t mInputCount; + std::string mCalibrationTableName; + const char* mInputBlobName; + bool mReadCache{true}; + void* mDeviceInput{nullptr}; + std::vector mCalibrationCache; +}; + +//! \class Int8EntropyCalibrator2 +//! +//! \brief Implements Entropy calibrator 2. +//! CalibrationAlgoType is kENTROPY_CALIBRATION_2. +//! +template +class Int8EntropyCalibrator2 : public nvinfer1::IInt8EntropyCalibrator2 +{ +public: + Int8EntropyCalibrator2(TBatchStream const& stream, int32_t firstBatch, const char* networkName, + const char* inputBlobName, bool readCache = true) + : mImpl(stream, firstBatch, networkName, inputBlobName, readCache) + { + } + + int getBatchSize() const noexcept override + { + return mImpl.getBatchSize(); + } + + bool getBatch(void* bindings[], const char* names[], int nbBindings) noexcept override + { + return mImpl.getBatch(bindings, names, nbBindings); + } + + const void* readCalibrationCache(size_t& length) noexcept override + { + return mImpl.readCalibrationCache(length); + } + + void writeCalibrationCache(const void* cache, size_t length) noexcept override + { + mImpl.writeCalibrationCache(cache, length); + } + +private: + EntropyCalibratorImpl mImpl; +}; + +#endif // ENTROPY_CALIBRATOR_H diff --git a/src/Detector/tensorrt_onnx/common/ErrorRecorder.h b/src/Detector/tensorrt_onnx/common/ErrorRecorder.h new file mode 100644 index 000000000..bfb857c52 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/ErrorRecorder.h @@ -0,0 +1,138 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef ERROR_RECORDER_H +#define ERROR_RECORDER_H +#include "NvInferRuntime.h" +#include "logger.h" +#include +#include +#include +#include +#include + +using nvinfer1::IErrorRecorder; +using nvinfer1::ErrorCode; + +//! +//! A simple implementation of the IErrorRecorder interface for +//! use by samples. This interface also can be used as a reference +//! implementation. +//! The sample Error recorder is based on a vector that pairs the error +//! code and the error string into a single element. It also uses +//! standard mutex's and atomics in order to make sure that the code +//! works in a multi-threaded environment. +//! +class SampleErrorRecorder : public IErrorRecorder +{ + using errorPair = std::pair; + using errorStack = std::vector; + +public: + SampleErrorRecorder() = default; + + ~SampleErrorRecorder() noexcept override {} + int32_t getNbErrors() const noexcept final + { + return mErrorStack.size(); + } + ErrorCode getErrorCode(int32_t errorIdx) const noexcept final + { + return invalidIndexCheck(errorIdx) ? ErrorCode::kINVALID_ARGUMENT : (*this)[errorIdx].first; + }; + IErrorRecorder::ErrorDesc getErrorDesc(int32_t errorIdx) const noexcept final + { + return invalidIndexCheck(errorIdx) ? "errorIdx out of range." : (*this)[errorIdx].second.c_str(); + } + // This class can never overflow since we have dynamic resize via std::vector usage. + bool hasOverflowed() const noexcept final + { + return false; + } + + // Empty the errorStack. + void clear() noexcept final + { + try + { + // grab a lock so that there is no addition while clearing. + std::lock_guard guard(mStackLock); + mErrorStack.clear(); + } + catch (const std::exception& e) + { + sample::gLogFatal << "Internal Error: " << e.what() << std::endl; + } + }; + + //! Simple helper function that + bool empty() const noexcept + { + return mErrorStack.empty(); + } + + bool reportError(ErrorCode val, IErrorRecorder::ErrorDesc desc) noexcept final + { + try + { + std::lock_guard guard(mStackLock); + sample::gLogError << "Error[" << static_cast(val) << "]: " << desc << std::endl; + mErrorStack.push_back(errorPair(val, desc)); + } + catch (const std::exception& e) + { + sample::gLogFatal << "Internal Error: " << e.what() << std::endl; + } + // All errors are considered fatal. + return true; + } + + // Atomically increment or decrement the ref counter. + IErrorRecorder::RefCount incRefCount() noexcept final + { + return ++mRefCount; + } + IErrorRecorder::RefCount decRefCount() noexcept final + { + return --mRefCount; + } + +private: + // Simple helper functions. + const errorPair& operator[](size_t index) const noexcept + { + return mErrorStack[index]; + } + + bool invalidIndexCheck(int32_t index) const noexcept + { + // By converting signed to unsigned, we only need a single check since + // negative numbers turn into large positive greater than the size. + size_t sIndex = index; + return sIndex >= mErrorStack.size(); + } + // Mutex to hold when locking mErrorStack. + std::mutex mStackLock; + + // Reference count of the class. Destruction of the class when mRefCount + // is not zero causes undefined behavior. + std::atomic mRefCount{0}; + + // The error stack that holds the errors recorded by TensorRT. + errorStack mErrorStack; +}; // class SampleErrorRecorder +#endif // ERROR_RECORDER_H diff --git a/src/Detector/tensorrt_onnx/common/argsParser.h b/src/Detector/tensorrt_onnx/common/argsParser.h new file mode 100644 index 000000000..1f0b9025c --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/argsParser.h @@ -0,0 +1,162 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ +#ifndef TENSORRT_ARGS_PARSER_H +#define TENSORRT_ARGS_PARSER_H + +#ifdef _MSC_VER +#include "getOptWin.h" +#else +#include +#endif +#include +#include +#include + +namespace samplesCommon +{ + +//! +//! \brief The SampleParams structure groups the basic parameters required by +//! all sample networks. +//! +struct SampleParams +{ + int32_t batchSize{1}; //!< Number of inputs in a batch + int32_t dlaCore{-1}; //!< Specify the DLA core to run network on. + bool int8{false}; //!< Allow runnning the network in Int8 mode. + bool fp16{false}; //!< Allow running the network in FP16 mode. + bool bf16{false}; //!< Allow running the network in BF16 mode. + std::vector dataDirs; //!< Directory paths where sample data files are stored + std::vector inputTensorNames; + std::vector outputTensorNames; + std::string timingCacheFile; //!< Path to timing cache file +}; + +//! +//! \brief The OnnxSampleParams structure groups the additional parameters required by +//! networks that use ONNX +//! +struct OnnxSampleParams : public SampleParams +{ + std::string onnxFileName; //!< Filename of ONNX file of a network +}; + +//! +//! /brief Struct to maintain command-line arguments. +//! +struct Args +{ + bool runInInt8{false}; + bool runInFp16{false}; + bool runInBf16{false}; + bool help{false}; + int32_t useDLACore{-1}; + int32_t batch{1}; + std::vector dataDirs; + std::string saveEngine; + std::string loadEngine; + bool rowOrder{true}; + std::string timingCacheFile; +}; + +//! +//! \brief Populates the Args struct with the provided command-line parameters. +//! +//! \throw invalid_argument if any of the arguments are not valid +//! +//! \return boolean If return value is true, execution can continue, otherwise program should exit +//! +inline bool parseArgs(Args& args, int32_t argc, char* argv[]) +{ + while (1) + { + int32_t arg; + static struct option long_options[] + = {{"help", no_argument, 0, 'h'}, {"datadir", required_argument, 0, 'd'}, {"int8", no_argument, 0, 'i'}, + {"fp16", no_argument, 0, 'f'}, {"bf16", no_argument, 0, 'z'}, {"columnOrder", no_argument, 0, 'c'}, + {"saveEngine", required_argument, 0, 's'}, {"loadEngine", required_argument, 0, 'o'}, + {"useDLACore", required_argument, 0, 'u'}, {"batch", required_argument, 0, 'b'}, + {"timingCacheFile", required_argument, 0, 't'}, {nullptr, 0, nullptr, 0}}; + int32_t option_index = 0; + arg = getopt_long(argc, argv, "hd:iu", long_options, &option_index); + if (arg == -1) + { + break; + } + + switch (arg) + { + case 'h': args.help = true; return true; + case 'd': + if (optarg) + { + args.dataDirs.push_back(optarg); + } + else + { + std::cerr << "ERROR: --datadir requires option argument" << std::endl; + return false; + } + break; + case 's': + if (optarg) + { + args.saveEngine = optarg; + } + break; + case 'o': + if (optarg) + { + args.loadEngine = optarg; + } + break; + case 'i': args.runInInt8 = true; break; + case 'f': args.runInFp16 = true; break; + case 'z': args.runInBf16 = true; break; + case 'c': args.rowOrder = false; break; + case 'u': + if (optarg) + { + args.useDLACore = std::stoi(optarg); + } + break; + case 'b': + if (optarg) + { + args.batch = std::stoi(optarg); + } + break; + case 't': + if (optarg) + { + args.timingCacheFile = optarg; + } + else + { + std::cerr << "ERROR: --timingCacheFile requires option argument" << std::endl; + return false; + } + break; + default: return false; + } + } + return true; +} + +} // namespace samplesCommon + +#endif // TENSORRT_ARGS_PARSER_H diff --git a/src/Detector/tensorrt_onnx/common/bfloat16.cpp b/src/Detector/tensorrt_onnx/common/bfloat16.cpp new file mode 100644 index 000000000..8222826ae --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/bfloat16.cpp @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#include "bfloat16.h" +#include + +namespace sample +{ + +BFloat16::operator float() const +{ + static_assert(sizeof(uint32_t) == sizeof(float), ""); + float val{0.F}; + auto bits = static_cast(mRep) << 16; + std::memcpy(&val, &bits, sizeof(uint32_t)); + return val; +} + +BFloat16::BFloat16(float x) +{ + static_assert(sizeof(uint32_t) == sizeof(float), ""); + uint32_t bits{0}; + std::memcpy(&bits, &x, sizeof(float)); + + // FP32 format: 1 sign bit, 8 bit exponent, 23 bit mantissa + // BF16 format: 1 sign bit, 8 bit exponent, 7 bit mantissa + + // Mask for exponent + constexpr uint32_t exponent = 0xFFU << 23; + + // Check if exponent is all 1s (NaN or infinite) + if ((bits & exponent) != exponent) + { + // x is finite - round to even + bits += 0x7FFFU + (bits >> 16 & 1); + } + + mRep = static_cast(bits >> 16); +} + +BFloat16 operator+(BFloat16 x, BFloat16 y) +{ + return BFloat16(static_cast(x) + static_cast(y)); +} + +} // namespace sample diff --git a/src/Detector/tensorrt_onnx/common/bfloat16.h b/src/Detector/tensorrt_onnx/common/bfloat16.h new file mode 100644 index 000000000..0d0ab9222 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/bfloat16.h @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#pragma once + +#include + +namespace sample +{ + +//! Implements "Brain Floating Point": like an IEEE FP32, +//! but the significand is only 7 bits instead of 23 bits. +class BFloat16 +{ +public: + BFloat16() + : mRep(0) + { + } + + // Rounds to even if there is a tie. + BFloat16(float x); + + operator float() const; + +private: + //! Value stored in BFloat16 representation. + uint16_t mRep; +}; +BFloat16 operator+(BFloat16 x, BFloat16 y); + +} // namespace sample diff --git a/src/Detector/tensorrt_onnx/common/buffers.h b/src/Detector/tensorrt_onnx/common/buffers.h new file mode 100644 index 000000000..e58f2f5c1 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/buffers.h @@ -0,0 +1,456 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ +#ifndef TENSORRT_BUFFERS_H +#define TENSORRT_BUFFERS_H + +#include "NvInfer.h" +#include "common.h" +#include "half.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace samplesCommon +{ + +//! +//! \brief The GenericBuffer class is a templated class for buffers. +//! +//! \details This templated RAII (Resource Acquisition Is Initialization) class handles the allocation, +//! deallocation, querying of buffers on both the device and the host. +//! It can handle data of arbitrary types because it stores byte buffers. +//! The template parameters AllocFunc and FreeFunc are used for the +//! allocation and deallocation of the buffer. +//! AllocFunc must be a functor that takes in (void** ptr, size_t size) +//! and returns bool. ptr is a pointer to where the allocated buffer address should be stored. +//! size is the amount of memory in bytes to allocate. +//! The boolean indicates whether or not the memory allocation was successful. +//! FreeFunc must be a functor that takes in (void* ptr) and returns void. +//! ptr is the allocated buffer address. It must work with nullptr input. +//! +template +class GenericBuffer +{ +public: + //! + //! \brief Construct an empty buffer. + //! + GenericBuffer(nvinfer1::DataType type = nvinfer1::DataType::kFLOAT) + : mSize(0) + , mCapacity(0) + , mType(type) + , mBuffer(nullptr) + { + } + + //! + //! \brief Construct a buffer with the specified allocation size in bytes. + //! + GenericBuffer(size_t size, nvinfer1::DataType type) + : mSize(size) + , mCapacity(size) + , mType(type) + { + if (!allocFn(&mBuffer, this->nbBytes())) + { + throw std::bad_alloc(); + } + } + + GenericBuffer(GenericBuffer&& buf) + : mSize(buf.mSize) + , mCapacity(buf.mCapacity) + , mType(buf.mType) + , mBuffer(buf.mBuffer) + { + buf.mSize = 0; + buf.mCapacity = 0; + buf.mType = nvinfer1::DataType::kFLOAT; + buf.mBuffer = nullptr; + } + + GenericBuffer& operator=(GenericBuffer&& buf) + { + if (this != &buf) + { + freeFn(mBuffer); + mSize = buf.mSize; + mCapacity = buf.mCapacity; + mType = buf.mType; + mBuffer = buf.mBuffer; + // Reset buf. + buf.mSize = 0; + buf.mCapacity = 0; + buf.mBuffer = nullptr; + } + return *this; + } + + //! + //! \brief Returns pointer to underlying array. + //! + void* data() + { + return mBuffer; + } + + //! + //! \brief Returns pointer to underlying array. + //! + const void* data() const + { + return mBuffer; + } + + //! + //! \brief Returns the size (in number of elements) of the buffer. + //! + size_t size() const + { + return mSize; + } + + //! + //! \brief Returns the size (in bytes) of the buffer. + //! + size_t nbBytes() const + { + return this->size() * samplesCommon::getElementSize(mType); + } + + //! + //! \brief Resizes the buffer. This is a no-op if the new size is smaller than or equal to the current capacity. + //! + void resize(size_t newSize) + { + mSize = newSize; + if (mCapacity < newSize) + { + freeFn(mBuffer); + if (!allocFn(&mBuffer, this->nbBytes())) + { + throw std::bad_alloc{}; + } + mCapacity = newSize; + } + } + + //! + //! \brief Overload of resize that accepts Dims + //! + void resize(const nvinfer1::Dims& dims) + { + return this->resize(samplesCommon::volume(dims)); + } + + ~GenericBuffer() + { + freeFn(mBuffer); + } + +private: + size_t mSize{0}, mCapacity{0}; + nvinfer1::DataType mType; + void* mBuffer; + AllocFunc allocFn; + FreeFunc freeFn; +}; + +class DeviceAllocator +{ +public: + bool operator()(void** ptr, size_t size) const + { + return cudaMalloc(ptr, size) == cudaSuccess; + } +}; + +class DeviceFree +{ +public: + void operator()(void* ptr) const + { + cudaFree(ptr); + } +}; + +class HostAllocator +{ +public: + bool operator()(void** ptr, size_t size) const + { + *ptr = malloc(size); + return *ptr != nullptr; + } +}; + +class HostFree +{ +public: + void operator()(void* ptr) const + { + free(ptr); + } +}; + +using DeviceBuffer = GenericBuffer; +using HostBuffer = GenericBuffer; + +//! +//! \brief The ManagedBuffer class groups together a pair of corresponding device and host buffers. +//! +class ManagedBuffer +{ +public: + DeviceBuffer deviceBuffer; + HostBuffer hostBuffer; +}; + +//! +//! \brief The BufferManager class handles host and device buffer allocation and deallocation. +//! +//! \details This RAII class handles host and device buffer allocation and deallocation, +//! memcpy between host and device buffers to aid with inference, +//! and debugging dumps to validate inference. The BufferManager class is meant to be +//! used to simplify buffer management and any interactions between buffers and the engine. +//! +class BufferManager +{ +public: + static const size_t kINVALID_SIZE_VALUE = ~size_t(0); + + //! + //! \brief Create a BufferManager for handling buffer interactions with engine, when the I/O tensor volumes + //! are provided + //! + BufferManager( + std::shared_ptr engine, std::vector const& volumes, int32_t batchSize = 0) + : mEngine(engine) + , mBatchSize(batchSize) + { + // Create host and device buffers + for (int32_t i = 0; i < mEngine->getNbIOTensors(); i++) + { + auto const name = engine->getIOTensorName(i); + mNames[name] = i; + + nvinfer1::DataType type = mEngine->getTensorDataType(name); + + std::unique_ptr manBuf{new ManagedBuffer()}; + manBuf->deviceBuffer = DeviceBuffer(volumes[i], type); + manBuf->hostBuffer = HostBuffer(volumes[i], type); + void* deviceBuffer = manBuf->deviceBuffer.data(); + mDeviceBindings.emplace_back(deviceBuffer); + mManagedBuffers.emplace_back(std::move(manBuf)); + } + } + + //! + //! \brief Create a BufferManager for handling buffer interactions with engine. + //! + BufferManager(std::shared_ptr engine, int32_t const batchSize = 0, + nvinfer1::IExecutionContext const* context = nullptr) + : mEngine(engine) + , mBatchSize(batchSize) + { + // Create host and device buffers + for (int32_t i = 0, e = mEngine->getNbIOTensors(); i < e; i++) + { + auto const name = engine->getIOTensorName(i); + mNames[name] = i; + + auto dims = context ? context->getTensorShape(name) : mEngine->getTensorShape(name); + size_t vol = context || !mBatchSize ? 1 : static_cast(mBatchSize); + nvinfer1::DataType type = mEngine->getTensorDataType(name); + int32_t vecDim = mEngine->getTensorVectorizedDim(name); + if (-1 != vecDim) // i.e., 0 != lgScalarsPerVector + { + int32_t scalarsPerVec = mEngine->getTensorComponentsPerElement(name); + dims.d[vecDim] = divUp(dims.d[vecDim], scalarsPerVec); + vol *= scalarsPerVec; + } + vol *= samplesCommon::volume(dims); + std::unique_ptr manBuf{new ManagedBuffer()}; + manBuf->deviceBuffer = DeviceBuffer(vol, type); + manBuf->hostBuffer = HostBuffer(vol, type); + void* deviceBuffer = manBuf->deviceBuffer.data(); + mDeviceBindings.emplace_back(deviceBuffer); + mManagedBuffers.emplace_back(std::move(manBuf)); + } + } + + //! + //! \brief Returns a vector of device buffers that you can use directly as + //! bindings for the execute and enqueue methods of IExecutionContext. + //! + std::vector& getDeviceBindings() + { + return mDeviceBindings; + } + + //! + //! \brief Returns a vector of device buffers. + //! + std::vector const& getDeviceBindings() const + { + return mDeviceBindings; + } + + //! + //! \brief Returns the device buffer corresponding to tensorName. + //! Returns nullptr if no such tensor can be found. + //! + void* getDeviceBuffer(std::string const& tensorName) const + { + return getBuffer(false, tensorName); + } + + //! + //! \brief Returns the host buffer corresponding to tensorName. + //! Returns nullptr if no such tensor can be found. + //! + void* getHostBuffer(std::string const& tensorName) const + { + return getBuffer(true, tensorName); + } + + //! + //! \brief Returns the size of the host and device buffers that correspond to tensorName. + //! Returns kINVALID_SIZE_VALUE if no such tensor can be found. + //! + size_t size(std::string const& tensorName) const + { + auto record = mNames.find(tensorName); + if (record == mNames.end()) + return kINVALID_SIZE_VALUE; + return mManagedBuffers[record->second]->hostBuffer.nbBytes(); + } + + //! + //! \brief Templated print function that dumps buffers of arbitrary type to std::ostream. + //! rowCount parameter controls how many elements are on each line. + //! A rowCount of 1 means that there is only 1 element on each line. + //! + template + void print(std::ostream& os, void* buf, size_t bufSize, size_t rowCount) + { + assert(rowCount != 0); + assert(bufSize % sizeof(T) == 0); + T* typedBuf = static_cast(buf); + size_t numItems = bufSize / sizeof(T); + for (int32_t i = 0; i < static_cast(numItems); i++) + { + // Handle rowCount == 1 case + if (rowCount == 1 && i != static_cast(numItems) - 1) + os << typedBuf[i] << std::endl; + else if (rowCount == 1) + os << typedBuf[i]; + // Handle rowCount > 1 case + else if (i % rowCount == 0) + os << typedBuf[i]; + else if (i % rowCount == rowCount - 1) + os << " " << typedBuf[i] << std::endl; + else + os << " " << typedBuf[i]; + } + } + + //! + //! \brief Copy the contents of input host buffers to input device buffers synchronously. + //! + void copyInputToDevice() + { + memcpyBuffers(true, false, false); + } + + //! + //! \brief Copy the contents of output device buffers to output host buffers synchronously. + //! + void copyOutputToHost() + { + memcpyBuffers(false, true, false); + } + + //! + //! \brief Copy the contents of input host buffers to input device buffers asynchronously. + //! + void copyInputToDeviceAsync(cudaStream_t const& stream = 0) + { + memcpyBuffers(true, false, true, stream); + } + + //! + //! \brief Copy the contents of output device buffers to output host buffers asynchronously. + //! + void copyOutputToHostAsync(cudaStream_t const& stream = 0) + { + memcpyBuffers(false, true, true, stream); + } + + ~BufferManager() = default; + +private: + void* getBuffer(bool const isHost, std::string const& tensorName) const + { + auto record = mNames.find(tensorName); + if (record == mNames.end()) + return nullptr; + return (isHost ? mManagedBuffers[record->second]->hostBuffer.data() + : mManagedBuffers[record->second]->deviceBuffer.data()); + } + + bool tenosrIsInput(const std::string& tensorName) const + { + return mEngine->getTensorIOMode(tensorName.c_str()) == nvinfer1::TensorIOMode::kINPUT; + } + + void memcpyBuffers(bool const copyInput, bool const deviceToHost, bool const async, cudaStream_t const& stream = 0) + { + for (auto const& n : mNames) + { + void* dstPtr = deviceToHost ? mManagedBuffers[n.second]->hostBuffer.data() + : mManagedBuffers[n.second]->deviceBuffer.data(); + void const* srcPtr = deviceToHost ? mManagedBuffers[n.second]->deviceBuffer.data() + : mManagedBuffers[n.second]->hostBuffer.data(); + size_t const byteSize = mManagedBuffers[n.second]->hostBuffer.nbBytes(); + const cudaMemcpyKind memcpyType = deviceToHost ? cudaMemcpyDeviceToHost : cudaMemcpyHostToDevice; + if ((copyInput && tenosrIsInput(n.first)) || (!copyInput && !tenosrIsInput(n.first))) + { + if (async) + CHECK(cudaMemcpyAsync(dstPtr, srcPtr, byteSize, memcpyType, stream)); + else + CHECK(cudaMemcpy(dstPtr, srcPtr, byteSize, memcpyType)); + } + } + } + + std::shared_ptr mEngine; //!< The pointer to the engine + int mBatchSize; //!< The batch size for legacy networks, 0 otherwise. + std::vector> mManagedBuffers; //!< The vector of pointers to managed buffers + std::vector mDeviceBindings; //!< The vector of device buffers needed for engine execution + std::unordered_map mNames; //!< The map of tensor name and index pairs +}; + +} // namespace samplesCommon + +#endif // TENSORRT_BUFFERS_H diff --git a/src/Detector/tensorrt_onnx/common/common.h b/src/Detector/tensorrt_onnx/common/common.h new file mode 100644 index 000000000..e29c6a302 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/common.h @@ -0,0 +1,943 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef TENSORRT_COMMON_H +#define TENSORRT_COMMON_H +#include "NvInfer.h" +#if !TRT_WINML +#include "NvInferPlugin.h" +#endif +#include "logger.h" +#include "safeCommon.h" +#include "timingCache.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +// For loadLibrary +// Needed so that the max/min definitions in windows.h do not conflict with std::max/min. +#define NOMINMAX +#include +#undef NOMINMAX +#else +#include +#endif + +#ifdef _MSC_VER +#define FN_NAME __FUNCTION__ +#else +#define FN_NAME __func__ +#endif + +#if defined(__aarch64__) || defined(__QNX__) +#define ENABLE_DLA_API 1 +#endif + +#define CHECK_RETURN_W_MSG(status, val, errMsg) \ + do \ + { \ + if (!(status)) \ + { \ + sample::gLogError << errMsg << " Error in " << __FILE__ << ", function " << FN_NAME << "(), line " << __LINE__ \ + << std::endl; \ + return val; \ + } \ + } while (0) + +#undef ASSERT +#define ASSERT(condition) \ + do \ + { \ + if (!(condition)) \ + { \ + sample::gLogError << "Assertion failure: " << #condition << std::endl; \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + + +#define CHECK_RETURN(status, val) CHECK_RETURN_W_MSG(status, val, "") + +#define OBJ_GUARD(A) std::unique_ptr + +template +OBJ_GUARD(T) +makeObjGuard(T_* t) +{ + CHECK(!(std::is_base_of::value || std::is_same::value)); + auto deleter = [](T* t) { delete t; }; + return std::unique_ptr{static_cast(t), deleter}; +} + +constexpr long double operator"" _GiB(long double val) +{ + return val * (1 << 30); +} +constexpr long double operator"" _MiB(long double val) +{ + return val * (1 << 20); +} +constexpr long double operator"" _KiB(long double val) +{ + return val * (1 << 10); +} + +struct SimpleProfiler : public nvinfer1::IProfiler +{ + struct Record + { + float time{0}; + int count{0}; + }; + + void reportLayerTime(const char* layerName, float ms) noexcept override + { + mProfile[layerName].count++; + mProfile[layerName].time += ms; + if (std::find(mLayerNames.begin(), mLayerNames.end(), layerName) == mLayerNames.end()) + { + mLayerNames.push_back(layerName); + } + } + + SimpleProfiler(const char* name, const std::vector& srcProfilers = std::vector()) + : mName(name) + { + for (const auto& srcProfiler : srcProfilers) + { + for (const auto& rec : srcProfiler.mProfile) + { + auto it = mProfile.find(rec.first); + if (it == mProfile.end()) + { + mProfile.insert(rec); + } + else + { + it->second.time += rec.second.time; + it->second.count += rec.second.count; + } + } + } + } + + friend std::ostream& operator<<(std::ostream& out, const SimpleProfiler& value) + { + out << "========== " << value.mName << " profile ==========" << std::endl; + float totalTime = 0; + std::string layerNameStr = "TensorRT layer name"; + int maxLayerNameLength = std::max(static_cast(layerNameStr.size()), 70); + for (const auto& elem : value.mProfile) + { + totalTime += elem.second.time; + maxLayerNameLength = std::max(maxLayerNameLength, static_cast(elem.first.size())); + } + + auto old_settings = out.flags(); + auto old_precision = out.precision(); + // Output header + { + out << std::setfill(' ') << std::setw(maxLayerNameLength) << layerNameStr << " "; + out << std::setw(12) << "Runtime, " + << "%" + << " "; + out << std::setw(12) << "Invocations" + << " "; + out << std::setw(12) << "Runtime, ms" << std::endl; + } + for (size_t i = 0; i < value.mLayerNames.size(); i++) + { + const std::string layerName = value.mLayerNames[i]; + auto elem = value.mProfile.at(layerName); + out << std::setw(maxLayerNameLength) << layerName << " "; + out << std::setw(12) << std::fixed << std::setprecision(1) << (elem.time * 100.0F / totalTime) << "%" + << " "; + out << std::setw(12) << elem.count << " "; + out << std::setw(12) << std::fixed << std::setprecision(2) << elem.time << std::endl; + } + out.flags(old_settings); + out.precision(old_precision); + out << "========== " << value.mName << " total runtime = " << totalTime << " ms ==========" << std::endl; + + return out; + } + +private: + std::string mName; + std::vector mLayerNames; + std::map mProfile; +}; + +namespace samplesCommon +{ +using nvinfer1::utils::loadTimingCacheFile; +using nvinfer1::utils::buildTimingCacheFromFile; +using nvinfer1::utils::saveTimingCacheFile; +using nvinfer1::utils::updateTimingCacheFile; +// Swaps endianness of an integral type. +template ::value, int>::type = 0> +inline T swapEndianness(const T& value) +{ + uint8_t bytes[sizeof(T)]; + for (int i = 0; i < static_cast(sizeof(T)); ++i) + { + bytes[sizeof(T) - 1 - i] = *(reinterpret_cast(&value) + i); + } + return *reinterpret_cast(bytes); +} + +class HostMemory +{ +public: + HostMemory() = delete; + virtual void* data() const noexcept + { + return mData; + } + virtual std::size_t size() const noexcept + { + return mSize; + } + virtual nvinfer1::DataType type() const noexcept + { + return mType; + } + virtual ~HostMemory() {} + +protected: + HostMemory(std::size_t size, nvinfer1::DataType type) + : mData{nullptr} + , mSize(size) + , mType(type) + { + } + void* mData; + std::size_t mSize; + nvinfer1::DataType mType; +}; + +template +class TypedHostMemory : public HostMemory +{ +public: + explicit TypedHostMemory(std::size_t size) + : HostMemory(size, dataType) + { + mData = new ElemType[size]; + }; + ~TypedHostMemory() noexcept override + { + delete[](ElemType*) mData; + } + ElemType* raw() noexcept + { + return static_cast(data()); + } +}; + +using FloatMemory = TypedHostMemory; +using HalfMemory = TypedHostMemory; +using ByteMemory = TypedHostMemory; + +inline void* safeCudaMalloc(size_t memSize) +{ + void* deviceMem; + CHECK(cudaMalloc(&deviceMem, memSize)); + if (deviceMem == nullptr) + { + std::cerr << "Out of memory" << std::endl; + exit(EXIT_FAILURE); + } + return deviceMem; +} + +inline bool isDebug() +{ + return (std::getenv("TENSORRT_DEBUG") ? true : false); +} + +struct InferDeleter +{ + template + void operator()(T* obj) const + { + delete obj; + } +}; + +template +using SampleUniquePtr = std::unique_ptr; + +static auto StreamDeleter = [](cudaStream_t* pStream) { + if (pStream) + { + static_cast(cudaStreamDestroy(*pStream)); + delete pStream; + } +}; + +inline std::unique_ptr makeCudaStream() +{ + std::unique_ptr pStream(new cudaStream_t, StreamDeleter); + if (cudaStreamCreateWithFlags(pStream.get(), cudaStreamNonBlocking) != cudaSuccess) + { + pStream.reset(nullptr); + } + + return pStream; +} + +//! Return vector of indices that puts magnitudes of sequence in descending order. +template +std::vector argMagnitudeSort(Iter begin, Iter end) +{ + std::vector indices(end - begin); + std::iota(indices.begin(), indices.end(), 0); + std::sort(indices.begin(), indices.end(), [&begin](size_t i, size_t j) { return std::abs(begin[j]) < std::abs(begin[i]); }); + return indices; +} + +inline bool readReferenceFile(const std::string& fileName, std::vector& refVector) +{ + std::ifstream infile(fileName); + if (!infile.is_open()) + { + std::cout << "ERROR: readReferenceFile: Attempting to read from a file that is not open." << std::endl; + return false; + } + std::string line; + while (std::getline(infile, line)) + { + if (line.empty()) + continue; + refVector.push_back(line); + } + infile.close(); + return true; +} + +template +std::vector classify( + const std::vector& refVector, const std::vector& output, const size_t topK) +{ + const auto inds = samplesCommon::argMagnitudeSort(output.cbegin(), output.cend()); + std::vector result; + result.reserve(topK); + for (size_t k = 0; k < topK; ++k) + { + result.push_back(refVector[inds[k]]); + } + return result; +} + +// Returns indices of highest K magnitudes in v. +template +std::vector topKMagnitudes(const std::vector& v, const size_t k) +{ + std::vector indices = samplesCommon::argMagnitudeSort(v.cbegin(), v.cend()); + indices.resize(k); + return indices; +} + +template +bool readASCIIFile(const std::string& fileName, const size_t size, std::vector& out) +{ + std::ifstream infile(fileName); + if (!infile.is_open()) + { + std::cout << "ERROR readASCIIFile: Attempting to read from a file that is not open." << std::endl; + return false; + } + out.clear(); + out.reserve(size); + out.assign(std::istream_iterator(infile), std::istream_iterator()); + infile.close(); + return true; +} + +template +bool writeASCIIFile(const std::string& fileName, const std::vector& in) +{ + std::ofstream outfile(fileName); + if (!outfile.is_open()) + { + std::cout << "ERROR: writeASCIIFile: Attempting to write to a file that is not open." << std::endl; + return false; + } + for (auto fn : in) + { + outfile << fn << "\n"; + } + outfile.close(); + return true; +} + +inline void print_version() +{ + std::cout << " TensorRT version: " << NV_TENSORRT_MAJOR << "." << NV_TENSORRT_MINOR << "." << NV_TENSORRT_PATCH + << "." << NV_TENSORRT_BUILD << std::endl; +} + +inline std::string getFileType(const std::string& filepath) +{ + return filepath.substr(filepath.find_last_of(".") + 1); +} + +inline std::string toLower(const std::string& inp) +{ + std::string out = inp; + std::transform(out.begin(), out.end(), out.begin(), ::tolower); + return out; +} + +inline float getMaxValue(const float* buffer, int64_t size) +{ + assert(buffer != nullptr); + assert(size > 0); + return *std::max_element(buffer, buffer + size); +} + +// Ensures that every tensor used by a network has a dynamic range set. +// +// All tensors in a network must have a dynamic range specified if a calibrator is not used. +// This function is just a utility to globally fill in missing scales and zero-points for the entire network. +// +// If a tensor does not have a dyanamic range set, it is assigned inRange or outRange as follows: +// +// * If the tensor is the input to a layer or output of a pooling node, its dynamic range is derived from inRange. +// * Otherwise its dynamic range is derived from outRange. +// +// The default parameter values are intended to demonstrate, for final layers in the network, +// cases where dynamic ranges are asymmetric. +// +// The default parameter values choosen arbitrarily. Range values should be choosen such that +// we avoid underflow or overflow. Also range value should be non zero to avoid uniform zero scale tensor. +inline void setAllDynamicRanges(nvinfer1::INetworkDefinition* network, float inRange = 2.0F, float outRange = 4.0F) +{ + // Ensure that all layer inputs have a scale. + for (int i = 0; i < network->getNbLayers(); i++) + { + auto layer = network->getLayer(i); + for (int j = 0; j < layer->getNbInputs(); j++) + { + nvinfer1::ITensor* input{layer->getInput(j)}; + // Optional inputs are nullptr here and are from RNN layers. + if (input != nullptr && !input->dynamicRangeIsSet()) + { + ASSERT(input->setDynamicRange(-inRange, inRange)); + } + } + } + + // Ensure that all layer outputs have a scale. + // Tensors that are also inputs to layers are ingored here + // since the previous loop nest assigned scales to them. + for (int i = 0; i < network->getNbLayers(); i++) + { + auto layer = network->getLayer(i); + for (int j = 0; j < layer->getNbOutputs(); j++) + { + nvinfer1::ITensor* output{layer->getOutput(j)}; + // Optional outputs are nullptr here and are from RNN layers. + if (output != nullptr && !output->dynamicRangeIsSet()) + { + // Pooling must have the same input and output scales. + if (layer->getType() == nvinfer1::LayerType::kPOOLING) + { + ASSERT(output->setDynamicRange(-inRange, inRange)); + } + else + { + ASSERT(output->setDynamicRange(-outRange, outRange)); + } + } + } + } +} + +inline void setDummyInt8DynamicRanges(const nvinfer1::IBuilderConfig* c, nvinfer1::INetworkDefinition* n) +{ + // Set dummy per-tensor dynamic range if Int8 mode is requested. + if (c->getFlag(nvinfer1::BuilderFlag::kINT8)) + { + sample::gLogWarning << "Int8 calibrator not provided. Generating dummy per-tensor dynamic range. Int8 accuracy " + "is not guaranteed." + << std::endl; + setAllDynamicRanges(n); + } +} + +inline void enableDLA( + nvinfer1::IBuilder* builder, nvinfer1::IBuilderConfig* config, int useDLACore, bool allowGPUFallback = true) +{ + if (useDLACore >= 0) + { + if (builder->getNbDLACores() == 0) + { + std::cerr << "Trying to use DLA core " << useDLACore << " on a platform that doesn't have any DLA cores" + << std::endl; + assert("Error: use DLA core on a platfrom that doesn't have any DLA cores" && false); + } + if (allowGPUFallback) + { + config->setFlag(nvinfer1::BuilderFlag::kGPU_FALLBACK); + } + if (!config->getFlag(nvinfer1::BuilderFlag::kINT8)) + { + // User has not requested INT8 Mode. + // By default run in FP16 mode. FP32 mode is not permitted. + config->setFlag(nvinfer1::BuilderFlag::kFP16); + } + config->setDefaultDeviceType(nvinfer1::DeviceType::kDLA); + config->setDLACore(useDLACore); + } +} + +inline int32_t parseDLA(int32_t argc, char** argv) +{ + for (int32_t i = 1; i < argc; i++) + { + if (strncmp(argv[i], "--useDLACore=", 13) == 0) + { + return std::stoi(argv[i] + 13); + } + } + return -1; +} + +inline uint32_t getElementSize(nvinfer1::DataType t) noexcept +{ + switch (t) + { +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kINT64: return 8; +#endif + case nvinfer1::DataType::kINT32: + case nvinfer1::DataType::kFLOAT: return 4; +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kBF16: +#endif + case nvinfer1::DataType::kHALF: return 2; + case nvinfer1::DataType::kBOOL: + case nvinfer1::DataType::kUINT8: + case nvinfer1::DataType::kINT8: + case nvinfer1::DataType::kFP8: return 1; +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kINT4: + ASSERT(false && "Element size is not implemented for sub-byte data-types"); +#endif + } + return 0; +} + +inline int64_t volume(nvinfer1::Dims const& dims, int32_t start, int32_t stop) +{ + ASSERT(start >= 0); + ASSERT(start <= stop); + ASSERT(stop <= dims.nbDims); + ASSERT(std::all_of(dims.d + start, dims.d + stop, [](int32_t x) { return x >= 0; })); + return std::accumulate(dims.d + start, dims.d + stop, int64_t{1}, std::multiplies{}); +} + +template +struct PPM +{ + std::string magic, fileName; + int h, w, max; + uint8_t buffer[C * H * W]; +}; + +// New vPPM(variable sized PPM) class with variable dimensions. +struct vPPM +{ + std::string magic, fileName; + int h, w, max; + std::vector buffer; +}; + +struct BBox +{ + float x1, y1, x2, y2; +}; + +template +void readPPMFile(const std::string& filename, samplesCommon::PPM& ppm) +{ + ppm.fileName = filename; + std::ifstream infile(filename, std::ifstream::binary); + assert(infile.is_open() && "Attempting to read from a file that is not open."); + infile >> ppm.magic >> ppm.w >> ppm.h >> ppm.max; + infile.seekg(1, infile.cur); + infile.read(reinterpret_cast(ppm.buffer), ppm.w * ppm.h * 3); +} + +inline void readPPMFile(const std::string& filename, vPPM& ppm, std::vector& input_dir) +{ + ppm.fileName = filename; + std::ifstream infile(locateFile(filename, input_dir), std::ifstream::binary); + infile >> ppm.magic >> ppm.w >> ppm.h >> ppm.max; + infile.seekg(1, infile.cur); + + for (int i = 0; i < ppm.w * ppm.h * 3; ++i) + { + ppm.buffer.push_back(0); + } + + infile.read(reinterpret_cast(&ppm.buffer[0]), ppm.w * ppm.h * 3); +} + +template +void writePPMFileWithBBox(const std::string& filename, PPM& ppm, const BBox& bbox) +{ + std::ofstream outfile("./" + filename, std::ofstream::binary); + assert(!outfile.fail()); + outfile << "P6" + << "\n" + << ppm.w << " " << ppm.h << "\n" + << ppm.max << "\n"; + + auto round = [](float x) -> int { return int(std::floor(x + 0.5F)); }; + const int x1 = std::min(std::max(0, round(int(bbox.x1))), W - 1); + const int x2 = std::min(std::max(0, round(int(bbox.x2))), W - 1); + const int y1 = std::min(std::max(0, round(int(bbox.y1))), H - 1); + const int y2 = std::min(std::max(0, round(int(bbox.y2))), H - 1); + + for (int x = x1; x <= x2; ++x) + { + // bbox top border + ppm.buffer[(y1 * ppm.w + x) * 3] = 255; + ppm.buffer[(y1 * ppm.w + x) * 3 + 1] = 0; + ppm.buffer[(y1 * ppm.w + x) * 3 + 2] = 0; + // bbox bottom border + ppm.buffer[(y2 * ppm.w + x) * 3] = 255; + ppm.buffer[(y2 * ppm.w + x) * 3 + 1] = 0; + ppm.buffer[(y2 * ppm.w + x) * 3 + 2] = 0; + } + + for (int y = y1; y <= y2; ++y) + { + // bbox left border + ppm.buffer[(y * ppm.w + x1) * 3] = 255; + ppm.buffer[(y * ppm.w + x1) * 3 + 1] = 0; + ppm.buffer[(y * ppm.w + x1) * 3 + 2] = 0; + // bbox right border + ppm.buffer[(y * ppm.w + x2) * 3] = 255; + ppm.buffer[(y * ppm.w + x2) * 3 + 1] = 0; + ppm.buffer[(y * ppm.w + x2) * 3 + 2] = 0; + } + + outfile.write(reinterpret_cast(ppm.buffer), ppm.w * ppm.h * 3); +} + +inline void writePPMFileWithBBox(const std::string& filename, vPPM ppm, std::vector& dets) +{ + std::ofstream outfile("./" + filename, std::ofstream::binary); + assert(!outfile.fail()); + outfile << "P6" + << "\n" + << ppm.w << " " << ppm.h << "\n" + << ppm.max << "\n"; + auto round = [](float x) -> int { return int(std::floor(x + 0.5F)); }; + + for (auto bbox : dets) + { + for (int x = int(bbox.x1); x < int(bbox.x2); ++x) + { + // bbox top border + ppm.buffer[(round(bbox.y1) * ppm.w + x) * 3] = 255; + ppm.buffer[(round(bbox.y1) * ppm.w + x) * 3 + 1] = 0; + ppm.buffer[(round(bbox.y1) * ppm.w + x) * 3 + 2] = 0; + // bbox bottom border + ppm.buffer[(round(bbox.y2) * ppm.w + x) * 3] = 255; + ppm.buffer[(round(bbox.y2) * ppm.w + x) * 3 + 1] = 0; + ppm.buffer[(round(bbox.y2) * ppm.w + x) * 3 + 2] = 0; + } + + for (int y = int(bbox.y1); y < int(bbox.y2); ++y) + { + // bbox left border + ppm.buffer[(y * ppm.w + round(bbox.x1)) * 3] = 255; + ppm.buffer[(y * ppm.w + round(bbox.x1)) * 3 + 1] = 0; + ppm.buffer[(y * ppm.w + round(bbox.x1)) * 3 + 2] = 0; + // bbox right border + ppm.buffer[(y * ppm.w + round(bbox.x2)) * 3] = 255; + ppm.buffer[(y * ppm.w + round(bbox.x2)) * 3 + 1] = 0; + ppm.buffer[(y * ppm.w + round(bbox.x2)) * 3 + 2] = 0; + } + } + + outfile.write(reinterpret_cast(&ppm.buffer[0]), ppm.w * ppm.h * 3); +} + +class TimerBase +{ +public: + virtual void start() {} + virtual void stop() {} + float microseconds() const noexcept + { + return mMs * 1000.F; + } + float milliseconds() const noexcept + { + return mMs; + } + float seconds() const noexcept + { + return mMs / 1000.F; + } + void reset() noexcept + { + mMs = 0.F; + } + +protected: + float mMs{0.0F}; +}; + +class GpuTimer : public TimerBase +{ +public: + explicit GpuTimer(cudaStream_t stream) + : mStream(stream) + { + CHECK(cudaEventCreate(&mStart)); + CHECK(cudaEventCreate(&mStop)); + } + ~GpuTimer() + { + CHECK(cudaEventDestroy(mStart)); + CHECK(cudaEventDestroy(mStop)); + } + void start() override + { + CHECK(cudaEventRecord(mStart, mStream)); + } + void stop() override + { + CHECK(cudaEventRecord(mStop, mStream)); + float ms{0.0F}; + CHECK(cudaEventSynchronize(mStop)); + CHECK(cudaEventElapsedTime(&ms, mStart, mStop)); + mMs += ms; + } + +private: + cudaEvent_t mStart, mStop; + cudaStream_t mStream; +}; // class GpuTimer + +template +class CpuTimer : public TimerBase +{ +public: + using clock_type = Clock; + + void start() override + { + mStart = Clock::now(); + } + void stop() override + { + mStop = Clock::now(); + mMs += std::chrono::duration{mStop - mStart}.count(); + } + +private: + std::chrono::time_point mStart, mStop; +}; // class CpuTimer + +using PreciseCpuTimer = CpuTimer; + +inline std::vector splitString(std::string str, char delimiter = ',') +{ + std::vector splitVect; + std::stringstream ss(str); + std::string substr; + + while (ss.good()) + { + getline(ss, substr, delimiter); + splitVect.emplace_back(std::move(substr)); + } + return splitVect; +} + +inline int getC(nvinfer1::Dims const& d) +{ + return d.nbDims >= 3 ? static_cast(d.d[d.nbDims - 3]) : 1; +} + +inline int getH(const nvinfer1::Dims& d) +{ + return d.nbDims >= 2 ? static_cast(d.d[d.nbDims - 2]) : 1; +} + +inline int getW(const nvinfer1::Dims& d) +{ + return d.nbDims >= 1 ? static_cast(d.d[d.nbDims - 1]) : 1; +} + +//! Platform-agnostic wrapper around dynamic libraries. +class DynamicLibrary +{ +public: + explicit DynamicLibrary(std::string const& name) + : mLibName{name} + { +#if defined(_WIN32) + mHandle = LoadLibraryA(name.c_str()); +#else // defined(_WIN32) + int32_t flags{RTLD_LAZY}; +#if ENABLE_ASAN + // https://github.com/google/sanitizers/issues/89 + // asan doesn't handle module unloading correctly and there are no plans on doing + // so. In order to get proper stack traces, don't delete the shared library on + // close so that asan can resolve the symbols correctly. + flags |= RTLD_NODELETE; +#endif // ENABLE_ASAN + + mHandle = dlopen(name.c_str(), flags); +#endif // defined(_WIN32) + + if (mHandle == nullptr) + { + std::string errorStr{}; +#if !defined(_WIN32) + errorStr = std::string{" due to "} + std::string{dlerror()}; +#endif + throw std::runtime_error("Unable to open library: " + name + errorStr); + } + } + + DynamicLibrary(DynamicLibrary const&) = delete; + DynamicLibrary(DynamicLibrary const&&) = delete; + + //! + //! Retrieve a function symbol from the loaded library. + //! + //! \return the loaded symbol on success + //! \throw std::invalid_argument if loading the symbol failed. + //! + template + std::function symbolAddress(char const* name) + { + if (mHandle == nullptr) + { + throw std::runtime_error("Handle to library is nullptr."); + } + void* ret; +#if defined(_MSC_VER) + ret = static_cast(GetProcAddress(static_cast(mHandle), name)); +#else + ret = dlsym(mHandle, name); +#endif + if (ret == nullptr) + { + std::string const kERROR_MSG(mLibName + ": error loading symbol: " + std::string(name)); + throw std::invalid_argument(kERROR_MSG); + } + return reinterpret_cast(ret); + } + + ~DynamicLibrary() + { + try + { +#if defined(_WIN32) + ASSERT(static_cast(FreeLibrary(static_cast(mHandle)))); +#else + ASSERT(dlclose(mHandle) == 0); +#endif + } + catch (...) + { + sample::gLogError << "Unable to close library: " << mLibName << std::endl; + } + } + +private: + std::string mLibName{}; //!< Name of the DynamicLibrary + void* mHandle{}; //!< Handle to the DynamicLibrary +}; + +inline std::unique_ptr loadLibrary(std::string const& path) +{ + // make_unique not available until C++14 - we still need to support C++11 builds. + return std::unique_ptr(new DynamicLibrary{path}); +} + +inline int32_t getMaxPersistentCacheSize() +{ + int32_t deviceIndex{}; + CHECK(cudaGetDevice(&deviceIndex)); + + int32_t maxPersistentL2CacheSize{}; +#if CUDART_VERSION >= 11030 && !TRT_WINML + CHECK(cudaDeviceGetAttribute(&maxPersistentL2CacheSize, cudaDevAttrMaxPersistingL2CacheSize, deviceIndex)); +#endif + + return maxPersistentL2CacheSize; +} + +inline bool isDataTypeSupported(nvinfer1::DataType dataType) +{ + auto builder = SampleUniquePtr(createBuilder()); + if (!builder) + { + return false; + } + + if ((dataType == nvinfer1::DataType::kINT8 && !builder->platformHasFastInt8()) + || (dataType == nvinfer1::DataType::kHALF && !builder->platformHasFastFp16())) + { + return false; + } + + return true; +} +} // namespace samplesCommon + +inline std::ostream& operator<<(std::ostream& os, const nvinfer1::Dims& dims) +{ + os << "("; + for (int i = 0; i < dims.nbDims; ++i) + { + os << (i ? ", " : "") << dims.d[i]; + } + return os << ")"; +} + +#endif // TENSORRT_COMMON_H diff --git a/src/Detector/tensorrt_onnx/common/dumpTFWts.py b/src/Detector/tensorrt_onnx/common/dumpTFWts.py new file mode 100644 index 000000000..70770fbd8 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/dumpTFWts.py @@ -0,0 +1,124 @@ +#!/usr/bin/python +# +# SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# 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. +# + +# Script to dump TensorFlow weights in TRT v1 and v2 dump format. +# The V1 format is for TensorRT 4.0. The V2 format is for TensorRT 4.0 and later. + +import sys +import struct +import argparse + +try: + import tensorflow as tf + from tensorflow.python import pywrap_tensorflow +except ImportError as err: + sys.stderr.write("""Error: Failed to import module ({})""".format(err)) + sys.exit() + +parser = argparse.ArgumentParser(description="TensorFlow Weight Dumper") + +parser.add_argument( + "-m", + "--model", + required=True, + help="The checkpoint file basename, example basename(model.ckpt-766908.data-00000-of-00001) -> model.ckpt-766908", +) +parser.add_argument("-o", "--output", required=True, help="The weight file to dump all the weights to.") +parser.add_argument("-1", "--wtsv1", required=False, default=False, type=bool, help="Dump the weights in the wts v1.") + +opt = parser.parse_args() + +if opt.wtsv1: + print("Outputting the trained weights in TensorRT's wts v1 format. This format is documented as:") + print("Line 0: ") + print("Line 1-Num: [buffer name] [buffer type] [buffer size] ") +else: + print("Outputting the trained weights in TensorRT's wts v2 format. This format is documented as:") + print("Line 0: ") + print("Line 1-Num: [buffer name] [buffer type] [(buffer shape{e.g. (1, 2, 3)}] ") + +inputbase = opt.model +outputbase = opt.output + + +def float_to_hex(f): + return hex(struct.unpack(" +#include +#include + +namespace nvinfer1 +{ +namespace utils +{ +FileLock::FileLock(ILogger& logger, std::string const& fileName) + : mLogger(logger) + , mFileName(fileName) +{ + std::string lockFileName = mFileName + ".lock"; +#ifdef _MSC_VER + { + std::stringstream ss; + ss << "Trying to set exclusive file lock " << lockFileName << std::endl; + mLogger.log(ILogger::Severity::kVERBOSE, ss.str().c_str()); + } + // MS docs said this is a blocking IO if "FILE_FLAG_OVERLAPPED" is not provided + mHandle = CreateFileA(lockFileName.c_str(), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL); + if (mHandle == INVALID_HANDLE_VALUE) + { + throw std::runtime_error("Failed to lock " + lockFileName + "!"); + } +#elif defined(__QNX__) + // We once enabled the file lock on QNX, lockf(F_TLOCK) return -1 and the reported error is + // The error generated was 89, which means that the function is not implemented. +#else + mHandle = fopen(lockFileName.c_str(), "wb+"); + if (mHandle == nullptr) + { + throw std::runtime_error("Cannot open " + lockFileName + "!"); + } + { + std::stringstream ss; + ss << "Trying to set exclusive file lock " << lockFileName << std::endl; + mLogger.log(ILogger::Severity::kVERBOSE, ss.str().c_str()); + } + mDescriptor = fileno(mHandle); + auto ret = lockf(mDescriptor, F_LOCK, 0); + if (ret != 0) + { + mDescriptor = -1; + fclose(mHandle); + throw std::runtime_error("Failed to lock " + lockFileName + "!"); + } +#endif +} + +FileLock::~FileLock() +{ + std::string lockFileName = mFileName + ".lock"; +#ifdef _MSC_VER + if (mHandle != INVALID_HANDLE_VALUE) + { + CloseHandle(mHandle); + } +#elif defined(__QNX__) + // We once enabled the file lock on QNX, lockf(F_TLOCK) return -1 and the reported error is + // The error generated was 89 + // That means : Function not implemented +#else + if (mDescriptor != -1) + { + auto ret = lockf(mDescriptor, F_ULOCK, 0); + if (mHandle != nullptr) + { + fclose(mHandle); + } + if (ret != 0) + { + std::stringstream ss; + ss << "Failed to unlock " << lockFileName << ", please remove " << lockFileName << ".lock manually!" + << std::endl; + mLogger.log(ILogger::Severity::kVERBOSE, ss.str().c_str()); + } + } +#endif +} +} // namespace utils +} // namespace nvinfer1 diff --git a/src/Detector/tensorrt_onnx/common/fileLock.h b/src/Detector/tensorrt_onnx/common/fileLock.h new file mode 100644 index 000000000..d0f64a5b4 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/fileLock.h @@ -0,0 +1,86 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef TENSORRT_SAMPLES_COMMON_FILELOCK_H_ +#define TENSORRT_SAMPLES_COMMON_FILELOCK_H_ +#include "NvInfer.h" +#ifdef _MSC_VER +// Needed so that the max/min definitions in windows.h do not conflict with std::max/min. +#define NOMINMAX +#include +#undef NOMINMAX +#else +#include // fileno +#include // lockf +#endif +#include + +namespace nvinfer1 +{ +namespace utils +{ +//! +//! \brief RAII object that locks a the specified file. +//! +//! The FileLock class uses a lock file to specify that the +//! current file is being used by a TensorRT tool or sample +//! so that things like the TimingCache can be updated across +//! processes without having conflicts. +//! +class FileLock +{ +public: + FileLock(nvinfer1::ILogger& logger, std::string const& fileName); + ~FileLock(); + FileLock() = delete; // no default ctor + FileLock(FileLock const&) = delete; // no copy ctor + FileLock& operator=(FileLock const&) = delete; // no copy assignment + FileLock(FileLock&&) = delete; // no move ctor + FileLock& operator=(FileLock&&) = delete; // no move assignment + +private: + //! + //! The logger that emits any error messages that might show up. + //! + nvinfer1::ILogger& mLogger; + + //! + //! The filename that the FileLock is protecting from multiple + //! TensorRT processes from writing to. + //! + std::string const mFileName; + +#ifdef _MSC_VER + //! + //! The file handle on windows for the file lock. + //! + HANDLE mHandle{}; +#else + //! + //! The file handle on linux for the file lock. + //! + FILE* mHandle{}; + //! + //! The file descriptor on linux of the file lock. + //! + int32_t mDescriptor{-1}; +#endif +}; // class FileLock +} // namespace utils +} // namespace nvinfer1 + +#endif // TENSORRT_SAMPLES_COMMON_FILELOCK_H_ diff --git a/src/Detector/tensorrt_onnx/common/getOptions.cpp b/src/Detector/tensorrt_onnx/common/getOptions.cpp new file mode 100644 index 000000000..19cd32811 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/getOptions.cpp @@ -0,0 +1,248 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#include "getOptions.h" +#include "logger.h" + +#include +#include +#include +#include +#include + +namespace nvinfer1 +{ +namespace utility +{ + +//! Matching for TRTOptions is defined as follows: +//! +//! If A and B both have longName set, A matches B if and only if A.longName == +//! B.longName and (A.shortName == B.shortName if both have short name set). +//! +//! If A only has shortName set and B only has longName set, then A does not +//! match B. It is assumed that when 2 TRTOptions are compared, one of them is +//! the definition of a TRTOption in the input to getOptions. As such, if the +//! definition only has shortName set, it will never be equal to a TRTOption +//! that does not have shortName set (and same for longName). +//! +//! If A and B both have shortName set but B does not have longName set, A +//! matches B if and only if A.shortName == B.shortName. +//! +//! If A has neither long or short name set, A matches B if and only if B has +//! neither long or short name set. +bool matches(const TRTOption& a, const TRTOption& b) +{ + if (!a.longName.empty() && !b.longName.empty()) + { + if (a.shortName && b.shortName) + { + return (a.longName == b.longName) && (a.shortName == b.shortName); + } + return a.longName == b.longName; + } + + // If only one of them is not set, this will return false anyway. + return a.shortName == b.shortName; +} + +//! getTRTOptionIndex returns the index of a TRTOption in a vector of +//! TRTOptions, -1 if not found. +int getTRTOptionIndex(const std::vector& options, const TRTOption& opt) +{ + for (size_t i = 0; i < options.size(); ++i) + { + if (matches(opt, options[i])) + { + return i; + } + } + return -1; +} + +//! validateTRTOption will return a string containing an error message if options +//! contain non-numeric characters, or if there are duplicate option names found. +//! Otherwise, returns the empty string. +std::string validateTRTOption( + const std::set& seenShortNames, const std::set& seenLongNames, const TRTOption& opt) +{ + if (opt.shortName != 0) + { + if (!std::isalnum(opt.shortName)) + { + return "Short name '" + std::to_string(opt.shortName) + "' is non-alphanumeric"; + } + + if (seenShortNames.find(opt.shortName) != seenShortNames.end()) + { + return "Short name '" + std::to_string(opt.shortName) + "' is a duplicate"; + } + } + + if (!opt.longName.empty()) + { + for (const char& c : opt.longName) + { + if (!std::isalnum(c) && c != '-' && c != '_') + { + return "Long name '" + opt.longName + "' contains characters that are not '-', '_', or alphanumeric"; + } + } + + if (seenLongNames.find(opt.longName) != seenLongNames.end()) + { + return "Long name '" + opt.longName + "' is a duplicate"; + } + } + return ""; +} + +//! validateTRTOptions will return a string containing an error message if any +//! options contain non-numeric characters, or if there are duplicate option +//! names found. Otherwise, returns the empty string. +std::string validateTRTOptions(const std::vector& options) +{ + std::set seenShortNames; + std::set seenLongNames; + for (size_t i = 0; i < options.size(); ++i) + { + const std::string errMsg = validateTRTOption(seenShortNames, seenLongNames, options[i]); + if (!errMsg.empty()) + { + return "Error '" + errMsg + "' at TRTOption " + std::to_string(i); + } + + seenShortNames.insert(options[i].shortName); + seenLongNames.insert(options[i].longName); + } + return ""; +} + +//! parseArgs parses an argument list and returns a TRTParsedArgs with the +//! fields set accordingly. Assumes that options is validated. +//! ErrMsg will be set if: +//! - an argument is null +//! - an argument is empty +//! - an argument does not have option (i.e. "-" and "--") +//! - a short argument has more than 1 character +//! - the last argument in the list requires a value +TRTParsedArgs parseArgs(int argc, const char* const* argv, const std::vector& options) +{ + TRTParsedArgs parsedArgs; + parsedArgs.values.resize(options.size()); + + for (int i = 1; i < argc; ++i) // index of current command-line argument + { + if (argv[i] == nullptr) + { + return TRTParsedArgs{"Null argument at index " + std::to_string(i)}; + } + + const std::string argStr(argv[i]); + if (argStr.empty()) + { + return TRTParsedArgs{"Empty argument at index " + std::to_string(i)}; + } + + // No starting hyphen means it is a positional argument + if (argStr[0] != '-') + { + parsedArgs.positionalArgs.push_back(argStr); + continue; + } + + if (argStr == "-" || argStr == "--") + { + return TRTParsedArgs{"Argument does not specify an option at index " + std::to_string(i)}; + } + + // If only 1 hyphen, char after is the flag. + TRTOption opt{' ', "", false, ""}; + std::string value; + if (argStr[1] != '-') + { + // Must only have 1 char after the hyphen + if (argStr.size() > 2) + { + return TRTParsedArgs{"Short arg contains more than 1 character at index " + std::to_string(i)}; + } + opt.shortName = argStr[1]; + } + else + { + opt.longName = argStr.substr(2); + + // We need to support --foo=bar syntax, so look for '=' + const size_t eqIndex = opt.longName.find('='); + if (eqIndex < opt.longName.size()) + { + value = opt.longName.substr(eqIndex + 1); + opt.longName = opt.longName.substr(0, eqIndex); + } + } + + const int idx = getTRTOptionIndex(options, opt); + if (idx < 0) + { + continue; + } + + if (options[idx].valueRequired) + { + if (!value.empty()) + { + parsedArgs.values[idx].second.push_back(value); + parsedArgs.values[idx].first = parsedArgs.values[idx].second.size(); + continue; + } + + if (i + 1 >= argc) + { + return TRTParsedArgs{"Last argument requires value, but none given"}; + } + + const std::string nextArg(argv[i + 1]); + if (nextArg.size() >= 1 && nextArg[0] == '-') + { + sample::gLogWarning << "Warning: Using '" << nextArg << "' as a value for '" << argStr + << "', Should this be its own flag?" << std::endl; + } + + parsedArgs.values[idx].second.push_back(nextArg); + i += 1; // Next argument already consumed + + parsedArgs.values[idx].first = parsedArgs.values[idx].second.size(); + } + else + { + parsedArgs.values[idx].first += 1; + } + } + return parsedArgs; +} + +TRTParsedArgs getOptions(int argc, const char* const* argv, const std::vector& options) +{ + const std::string errMsg = validateTRTOptions(options); + if (!errMsg.empty()) + { + return TRTParsedArgs{errMsg}; + } + return parseArgs(argc, argv, options); +} +} // namespace utility +} // namespace nvinfer1 diff --git a/src/Detector/tensorrt_onnx/common/getOptions.h b/src/Detector/tensorrt_onnx/common/getOptions.h new file mode 100644 index 000000000..4bbf9e275 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/getOptions.h @@ -0,0 +1,128 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef TRT_GET_OPTIONS_H +#define TRT_GET_OPTIONS_H + +#include +#include +#include + +namespace nvinfer1 +{ +namespace utility +{ + +//! TRTOption defines a command line option. At least 1 of shortName and longName +//! must be defined. +//! If bool initialization is undefined behavior on your system, valueRequired +//! must also be explicitly defined. +//! helpText is optional. +struct TRTOption +{ + char shortName; //!< Option name in short (single hyphen) form (i.e. -a, -b) + std::string longName; //!< Option name in long (double hyphen) form (i.e. --foo, --bar) + bool valueRequired; //!< True if a value is needed for an option (i.e. -N 4, --foo bar) + std::string helpText; //!< Text to show when printing out the command usage +}; + +//! TRTParsedArgs is returned by getOptions after it has parsed a command line +//! argument list (argv). +//! +//! errMsg is a string containing an error message if any errors occurred. If it +//! is empty, no errors occurred. +//! +//! values stores a vector of pairs for each option (ordered by order in the +//! input). Each pair contains an int (the number of occurrences) and a vector +//! of strings (a list of values). The user should know which of these to use, +//! and which options required values. For non-value options, only occurrences is +//! populated. For value-required options, occurrences == # of values. Values do +//! not need to be unique. +//! +//! positionalArgs stores additional arguments that are passed in without an +//! option (these must not start with a hyphen). +struct TRTParsedArgs +{ + std::string errMsg; + std::vector>> values; + std::vector positionalArgs; +}; + +//! Parse the input arguments passed to main() and extract options as well as +//! positional arguments. +//! +//! Options are supposed to be passed to main() with a preceding hyphen '-'. +//! +//! If there is a single preceding hyphen, there should be exactly 1 character +//! after the hyphen, which is interpreted as the option. +//! +//! If there are 2 preceding hyphens, the entire argument (without the hyphens) +//! is interpreted as the option. +//! +//! If the option requires a value, the next argument is used as the value. +//! +//! Positional arguments must not start with a hyphen. +//! +//! If an argument requires a value, the next argument is interpreted as the +//! value, even if it is the form of a valid option (i.e. --foo --bar will store +//! "--bar" as a value for option "foo" if "foo" requires a value). +//! We also support --name=value syntax. In this case, 'value' would be used as +//! the value, NOT the next argument. +//! +//! For options: +//! { { 'a', "", false }, +//! { 'b', "", false }, +//! { 0, "cee", false }, +//! { 'd', "", true }, +//! { 'e', "", true }, +//! { 'f', "foo", true } } +//! +//! ./main hello world -a -a --cee -d 12 -f 34 +//! and +//! ./main hello world -a -a --cee -d 12 --foo 34 +//! +//! will result in: +//! +//! TRTParsedArgs { +//! errMsg: "", +//! values: { { 2, {} }, +//! { 0, {} }, +//! { 1, {} }, +//! { 1, {"12"} }, +//! { 0, {} }, +//! { 1, {"34"} } } +//! positionalArgs: {"hello", "world"}, +//! } +//! +//! Non-POSIX behavior: +//! - Does not support "-abcde" as a shorthand for "-a -b -c -d -e". Each +//! option must have its own hyphen prefix. +//! - Does not support -e12 as a shorthand for "-e 12". Values MUST be +//! whitespace-separated from the option it is for. +//! +//! @param[in] argc The number of arguments passed to main (including the +//! file name, which is disregarded) +//! @param[in] argv The arguments passed to main (including the file name, +//! which is disregarded) +//! @param[in] options List of TRTOptions to parse +//! @return TRTParsedArgs. See TRTParsedArgs documentation for descriptions of +//! the fields. +TRTParsedArgs getOptions(int argc, const char* const* argv, const std::vector& options); +} // namespace utility +} // namespace nvinfer1 + +#endif // TRT_GET_OPTIONS_H diff --git a/src/Detector/tensorrt_onnx/common/getopt.c b/src/Detector/tensorrt_onnx/common/getopt.c new file mode 100644 index 000000000..c1da08b5b --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/getopt.c @@ -0,0 +1,568 @@ +/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + +/* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "getoptWin.h" +#include +#include +#include +#include +#include +#include + +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +#undef optreset /* see getopt.h */ +#define optreset __mingw_optreset +int optreset; /* reset getopt */ +char* optarg; /* argument associated with option */ +#endif + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int) '?' +#define BADARG ((*options == ':') ? (int) ':' : (int) '?') +#define INORDER (int) 1 + +#ifndef __CYGWIN__ +#define __progname __argv[0] +#else +extern char __declspec(dllimport) * __progname; +#endif + +#ifdef __CYGWIN__ +static char EMSG[] = ""; +#else +#define EMSG "" +#endif + +static int getopt_internal(int, char* const*, char const*, const struct option*, int*, int); +static int parse_long_options(char* const*, char const*, const struct option*, int*, int); +static int gcd(int, int); +static void permute_args(int, int, int, char* const*); + +static char* place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static char const recargchar[] = "option requires an argument -- %c"; +static char const recargstring[] = "option requires an argument -- %s"; +static char const ambig[] = "ambiguous option -- %.*s"; +static char const noarg[] = "option doesn't take an argument -- %.*s"; +static char const illoptchar[] = "unknown option -- %c"; +static char const illoptstring[] = "unknown option -- %s"; + +static void _vwarnx(char const* fmt, va_list ap) +{ + (void) fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) + (void) vfprintf(stderr, fmt, ap); + (void) fprintf(stderr, "\n"); +} + +static void warnx(char const* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + _vwarnx(fmt, ap); + va_end(ap); +} + +/* + * Compute the greatest common divisor of a and b. + */ +static int gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) + { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void permute_args(int panonopt_start, int panonopt_end, int opt_end, char* const* nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char* swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) + { + cstart = panonopt_end + i; + pos = cstart; + for (j = 0; j < cyclelen; j++) + { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char**) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char**) nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int parse_long_options( + char* const* nargv, char const* options, const struct option* long_options, int* idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, ambiguous, match; + +#define IDENTICAL_INTERPRETATION(_x, _y) \ + (long_options[(_x)].has_arg == long_options[(_y)].has_arg && long_options[(_x)].flag == long_options[(_y)].flag \ + && long_options[(_x)].val == long_options[(_y)].val) + + current_argv = place; + match = -1; + ambiguous = 0; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) + { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } + else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) + { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) + { + /* exact match */ + match = i; + ambiguous = 0; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else if (!IDENTICAL_INTERPRETATION(i, match)) + ambiguous = 1; + } + if (ambiguous) + { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int) current_argv_len, current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) + { /* option found */ + if (long_options[match].has_arg == no_argument && has_equal) + { + if (PRINT_ERROR) + warnx(noarg, (int) current_argv_len, current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || long_options[match].has_arg == optional_argument) + { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == required_argument) + { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) + { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } + else + { /* unknown option */ + if (short_too) + { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) + { + *long_options[match].flag = long_options[match].val; + return (0); + } + else + return (long_options[match].val); +#undef IDENTICAL_INTERPRETATION +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int getopt_internal( + int nargc, char* const* nargv, char const* options, const struct option* long_options, int* idx, int flags) +{ + char const* oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + * + * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or + * optreset != 0 for GNU compatibility. + */ + if (posixly_correct == -1 || optreset != 0) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) + { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) + { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) + { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) + { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || (place[1] == '\0' && strchr(options, '-') == NULL)) + { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) + { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) + { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) + { + permute_args(nonopt_start, nonopt_end, optind, nargv); + nonopt_start = optind - (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') + { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) + { + permute_args(nonopt_start, nonopt_end, optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && (*place == '-' || (flags & FLAG_LONGONLY))) + { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, idx, short_too); + if (optchar != -1) + { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int) *place++) == (int) ':' || (optchar == (int) '-' && *place != '\0') + || (oli = strchr(options, optchar)) == NULL) + { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int) '-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') + { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) + { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } + else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') + { /* doesn't take argument */ + if (!*place) + ++optind; + } + else + { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') + { /* arg not optional */ + if (++optind >= nargc) + { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } + else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int getopt(int nargc, char* const* nargv, char const* options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int getopt_long(int nargc, char* const* nargv, char const* options, const struct option* long_options, int* idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int getopt_long_only(int nargc, char* const* nargv, char const* options, const struct option* long_options, int* idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE | FLAG_LONGONLY)); +} diff --git a/src/Detector/tensorrt_onnx/common/getoptWin.h b/src/Detector/tensorrt_onnx/common/getoptWin.h new file mode 100644 index 000000000..a1dc6ffa9 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/getoptWin.h @@ -0,0 +1,124 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef __GETOPT_H__ +/** + * DISCLAIMER + * This file has no copyright assigned and is placed in the Public Domain. + * This file is a part of the w64 mingw-runtime package. + * + * The w64 mingw-runtime package and its code is distributed in the hope that it + * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR + * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to + * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define __GETOPT_H__ + +/* All the headers include this file. */ +#include + +#if defined(WINGETOPT_SHARED_LIB) +#if defined(BUILDING_WINGETOPT_DLL) +#define WINGETOPT_API __declspec(dllexport) +#else +#define WINGETOPT_API __declspec(dllimport) +#endif +#else +#define WINGETOPT_API +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINGETOPT_API extern int optind; /* index of first non-option in argv */ + WINGETOPT_API extern int optopt; /* single option character, as parsed */ + WINGETOPT_API extern int opterr; /* flag to enable built-in diagnostics... */ + /* (user may set to zero, to suppress) */ + + WINGETOPT_API extern char* optarg; /* pointer to argument of current option */ + + extern int getopt(int nargc, char* const* nargv, char const* options); + +#ifdef _BSD_SOURCE +/* + * BSD adds the non-standard `optreset' feature, for reinitialisation + * of `getopt' parsing. We support this feature, for applications which + * proclaim their BSD heritage, before including this header; however, + * to maintain portability, developers are advised to avoid it. + */ +#define optreset __mingw_optreset + extern int optreset; +#endif +#ifdef __cplusplus +} +#endif +/* + * POSIX requires the `getopt' API to be specified in `unistd.h'; + * thus, `unistd.h' includes this header. However, we do not want + * to expose the `getopt_long' or `getopt_long_only' APIs, when + * included in this manner. Thus, close the standard __GETOPT_H__ + * declarations block, and open an additional __GETOPT_LONG_H__ + * specific block, only when *not* __UNISTD_H_SOURCED__, in which + * to declare the extended API. + */ +#endif /* !defined(__GETOPT_H__) */ + +#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) +#define __GETOPT_LONG_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + + struct option /* specification for a long form option... */ + { + char const* name; /* option name, without leading hyphens */ + int has_arg; /* does it take an argument? */ + int* flag; /* where to save its status, or NULL */ + int val; /* its associated status value */ + }; + + enum /* permitted values for its `has_arg' field... */ + { + no_argument = 0, /* option never takes an argument */ + required_argument, /* option always requires an argument */ + optional_argument /* option may take an argument */ + }; + + extern int getopt_long( + int nargc, char* const* nargv, char const* options, const struct option* long_options, int* idx); + extern int getopt_long_only( + int nargc, char* const* nargv, char const* options, const struct option* long_options, int* idx); +/* + * Previous MinGW implementation had... + */ +#ifndef HAVE_DECL_GETOPT +/* + * ...for the long form API only; keep this for compatibility. + */ +#define HAVE_DECL_GETOPT 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ diff --git a/src/Detector/tensorrt_onnx/common/half.h b/src/Detector/tensorrt_onnx/common/half.h new file mode 100644 index 000000000..b997e7db6 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/half.h @@ -0,0 +1,4303 @@ +// half - IEEE 754-based half-precision floating point library. +// +// Copyright (c) 2012-2017 Christian Rau +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +// Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +// Version 1.12.0 + +/// \file +/// Main header file for half precision functionality. + +#ifndef HALF_HALF_HPP +#define HALF_HALF_HPP + +/// Combined gcc version number. +#define HALF_GNUC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +// check C++11 language features +#if defined(__clang__) // clang +#if __has_feature(cxx_static_assert) && !defined(HALF_ENABLE_CPP11_STATIC_ASSERT) +#define HALF_ENABLE_CPP11_STATIC_ASSERT 1 +#endif +#if __has_feature(cxx_constexpr) && !defined(HALF_ENABLE_CPP11_CONSTEXPR) +#define HALF_ENABLE_CPP11_CONSTEXPR 1 +#endif +#if __has_feature(cxx_noexcept) && !defined(HALF_ENABLE_CPP11_NOEXCEPT) +#define HALF_ENABLE_CPP11_NOEXCEPT 1 +#endif +#if __has_feature(cxx_user_literals) && !defined(HALF_ENABLE_CPP11_USER_LITERALS) +#define HALF_ENABLE_CPP11_USER_LITERALS 1 +#endif +#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L) && !defined(HALF_ENABLE_CPP11_LONG_LONG) +#define HALF_ENABLE_CPP11_LONG_LONG 1 +#endif +/*#elif defined(__INTEL_COMPILER) //Intel C++ + #if __INTEL_COMPILER >= 1100 && !defined(HALF_ENABLE_CPP11_STATIC_ASSERT) ???????? + #define HALF_ENABLE_CPP11_STATIC_ASSERT 1 + #endif + #if __INTEL_COMPILER >= 1300 && !defined(HALF_ENABLE_CPP11_CONSTEXPR) ???????? + #define HALF_ENABLE_CPP11_CONSTEXPR 1 + #endif + #if __INTEL_COMPILER >= 1300 && !defined(HALF_ENABLE_CPP11_NOEXCEPT) ???????? + #define HALF_ENABLE_CPP11_NOEXCEPT 1 + #endif + #if __INTEL_COMPILER >= 1100 && !defined(HALF_ENABLE_CPP11_LONG_LONG) ???????? + #define HALF_ENABLE_CPP11_LONG_LONG 1 + #endif*/ +#elif defined(__GNUC__) // gcc +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L +#if HALF_GNUC_VERSION >= 403 && !defined(HALF_ENABLE_CPP11_STATIC_ASSERT) +#define HALF_ENABLE_CPP11_STATIC_ASSERT 1 +#endif +#if HALF_GNUC_VERSION >= 406 && !defined(HALF_ENABLE_CPP11_CONSTEXPR) +#define HALF_ENABLE_CPP11_CONSTEXPR 1 +#endif +#if HALF_GNUC_VERSION >= 406 && !defined(HALF_ENABLE_CPP11_NOEXCEPT) +#define HALF_ENABLE_CPP11_NOEXCEPT 1 +#endif +#if HALF_GNUC_VERSION >= 407 && !defined(HALF_ENABLE_CPP11_USER_LITERALS) +#define HALF_ENABLE_CPP11_USER_LITERALS 1 +#endif +#if !defined(HALF_ENABLE_CPP11_LONG_LONG) +#define HALF_ENABLE_CPP11_LONG_LONG 1 +#endif +#endif +#elif defined(_MSC_VER) // Visual C++ +#if _MSC_VER >= 1900 && !defined(HALF_ENABLE_CPP11_CONSTEXPR) +#define HALF_ENABLE_CPP11_CONSTEXPR 1 +#endif +#if _MSC_VER >= 1900 && !defined(HALF_ENABLE_CPP11_NOEXCEPT) +#define HALF_ENABLE_CPP11_NOEXCEPT 1 +#endif +#if _MSC_VER >= 1900 && !defined(HALF_ENABLE_CPP11_USER_LITERALS) +#define HALF_ENABLE_CPP11_USER_LITERALS 1 +#endif +#if _MSC_VER >= 1600 && !defined(HALF_ENABLE_CPP11_STATIC_ASSERT) +#define HALF_ENABLE_CPP11_STATIC_ASSERT 1 +#endif +#if _MSC_VER >= 1310 && !defined(HALF_ENABLE_CPP11_LONG_LONG) +#define HALF_ENABLE_CPP11_LONG_LONG 1 +#endif +#define HALF_POP_WARNINGS 1 +#pragma warning(push) +#pragma warning(disable : 4099 4127 4146) // struct vs class, constant in if, negative unsigned +#endif + +// check C++11 library features +#include +#if defined(_LIBCPP_VERSION) // libc++ +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103 +#ifndef HALF_ENABLE_CPP11_TYPE_TRAITS +#define HALF_ENABLE_CPP11_TYPE_TRAITS 1 +#endif +#ifndef HALF_ENABLE_CPP11_CSTDINT +#define HALF_ENABLE_CPP11_CSTDINT 1 +#endif +#ifndef HALF_ENABLE_CPP11_CMATH +#define HALF_ENABLE_CPP11_CMATH 1 +#endif +#ifndef HALF_ENABLE_CPP11_HASH +#define HALF_ENABLE_CPP11_HASH 1 +#endif +#endif +#elif defined(__GLIBCXX__) // libstdc++ +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103 +#ifdef __clang__ +#if __GLIBCXX__ >= 20080606 && !defined(HALF_ENABLE_CPP11_TYPE_TRAITS) +#define HALF_ENABLE_CPP11_TYPE_TRAITS 1 +#endif +#if __GLIBCXX__ >= 20080606 && !defined(HALF_ENABLE_CPP11_CSTDINT) +#define HALF_ENABLE_CPP11_CSTDINT 1 +#endif +#if __GLIBCXX__ >= 20080606 && !defined(HALF_ENABLE_CPP11_CMATH) +#define HALF_ENABLE_CPP11_CMATH 1 +#endif +#if __GLIBCXX__ >= 20080606 && !defined(HALF_ENABLE_CPP11_HASH) +#define HALF_ENABLE_CPP11_HASH 1 +#endif +#else +#if HALF_GNUC_VERSION >= 403 && !defined(HALF_ENABLE_CPP11_CSTDINT) +#define HALF_ENABLE_CPP11_CSTDINT 1 +#endif +#if HALF_GNUC_VERSION >= 403 && !defined(HALF_ENABLE_CPP11_CMATH) +#define HALF_ENABLE_CPP11_CMATH 1 +#endif +#if HALF_GNUC_VERSION >= 403 && !defined(HALF_ENABLE_CPP11_HASH) +#define HALF_ENABLE_CPP11_HASH 1 +#endif +#endif +#endif +#elif defined(_CPPLIB_VER) // Dinkumware/Visual C++ +#if _CPPLIB_VER >= 520 +#ifndef HALF_ENABLE_CPP11_TYPE_TRAITS +#define HALF_ENABLE_CPP11_TYPE_TRAITS 1 +#endif +#ifndef HALF_ENABLE_CPP11_CSTDINT +#define HALF_ENABLE_CPP11_CSTDINT 1 +#endif +#ifndef HALF_ENABLE_CPP11_HASH +#define HALF_ENABLE_CPP11_HASH 1 +#endif +#endif +#if _CPPLIB_VER >= 610 +#ifndef HALF_ENABLE_CPP11_CMATH +#define HALF_ENABLE_CPP11_CMATH 1 +#endif +#endif +#endif +#undef HALF_GNUC_VERSION + +// support constexpr +#if HALF_ENABLE_CPP11_CONSTEXPR +#define HALF_CONSTEXPR constexpr +#define HALF_CONSTEXPR_CONST constexpr +#else +#define HALF_CONSTEXPR +#define HALF_CONSTEXPR_CONST const +#endif + +// support noexcept +#if HALF_ENABLE_CPP11_NOEXCEPT +#define HALF_NOEXCEPT noexcept +#define HALF_NOTHROW noexcept +#else +#define HALF_NOEXCEPT +#define HALF_NOTHROW throw() +#endif + +#include +#include +#include +#include +#include +#include +#if HALF_ENABLE_CPP11_TYPE_TRAITS +#include +#endif +#if HALF_ENABLE_CPP11_CSTDINT +#include +#endif +#if HALF_ENABLE_CPP11_HASH +#include +#endif + +/// Default rounding mode. +/// This specifies the rounding mode used for all conversions between [half](\ref half_float::half)s and `float`s as +/// well as for the half_cast() if not specifying a rounding mode explicitly. It can be redefined (before including +/// half.hpp) to one of the standard rounding modes using their respective constants or the equivalent values of +/// `std::float_round_style`: +/// +/// `std::float_round_style` | value | rounding +/// ---------------------------------|-------|------------------------- +/// `std::round_indeterminate` | -1 | fastest (default) +/// `std::round_toward_zero` | 0 | toward zero +/// `std::round_to_nearest` | 1 | to nearest +/// `std::round_toward_infinity` | 2 | toward positive infinity +/// `std::round_toward_neg_infinity` | 3 | toward negative infinity +/// +/// By default this is set to `-1` (`std::round_indeterminate`), which uses truncation (round toward zero, but with +/// overflows set to infinity) and is the fastest rounding mode possible. It can even be set to +/// `std::numeric_limits::round_style` to synchronize the rounding mode with that of the underlying +/// single-precision implementation. +#ifndef HALF_ROUND_STYLE +#define HALF_ROUND_STYLE 1 // = std::round_to_nearest +#endif + +/// Tie-breaking behaviour for round to nearest. +/// This specifies if ties in round to nearest should be resolved by rounding to the nearest even value. By default this +/// is defined to `0` resulting in the faster but slightly more biased behaviour of rounding away from zero in half-way +/// cases (and thus equal to the round() function), but can be redefined to `1` (before including half.hpp) if more +/// IEEE-conformant behaviour is needed. +#ifndef HALF_ROUND_TIES_TO_EVEN +#define HALF_ROUND_TIES_TO_EVEN 0 // ties away from zero +#endif + +/// Value signaling overflow. +/// In correspondence with `HUGE_VAL[F|L]` from `` this symbol expands to a positive value signaling the overflow +/// of an operation, in particular it just evaluates to positive infinity. +#define HUGE_VALH std::numeric_limits::infinity() + +/// Fast half-precision fma function. +/// This symbol is only defined if the fma() function generally executes as fast as, or faster than, a separate +/// half-precision multiplication followed by an addition. Due to the internal single-precision implementation of all +/// arithmetic operations, this is in fact always the case. +#define FP_FAST_FMAH 1 + +#ifndef FP_ILOGB0 +#define FP_ILOGB0 INT_MIN +#endif +#ifndef FP_ILOGBNAN +#define FP_ILOGBNAN INT_MAX +#endif +#ifndef FP_SUBNORMAL +#define FP_SUBNORMAL 0 +#endif +#ifndef FP_ZERO +#define FP_ZERO 1 +#endif +#ifndef FP_NAN +#define FP_NAN 2 +#endif +#ifndef FP_INFINITE +#define FP_INFINITE 3 +#endif +#ifndef FP_NORMAL +#define FP_NORMAL 4 +#endif + +/// Main namespace for half precision functionality. +/// This namespace contains all the functionality provided by the library. +namespace half_float +{ +class half; + +#if HALF_ENABLE_CPP11_USER_LITERALS +/// Library-defined half-precision literals. +/// Import this namespace to enable half-precision floating point literals: +/// ~~~~{.cpp} +/// using namespace half_float::literal; +/// half_float::half = 4.2_h; +/// ~~~~ +namespace literal +{ +half operator"" _h(long double); +} +#endif + +/// \internal +/// \brief Implementation details. +namespace detail +{ +#if HALF_ENABLE_CPP11_TYPE_TRAITS +/// Conditional type. +template +struct conditional : std::conditional +{ +}; + +/// Helper for tag dispatching. +template +struct bool_type : std::integral_constant +{ +}; +using std::false_type; +using std::true_type; + +/// Type traits for floating point types. +template +struct is_float : std::is_floating_point +{ +}; +#else +/// Conditional type. +template +struct conditional +{ + typedef T type; +}; +template +struct conditional +{ + typedef F type; +}; + +/// Helper for tag dispatching. +template +struct bool_type +{ +}; +typedef bool_type true_type; +typedef bool_type false_type; + +/// Type traits for floating point types. +template +struct is_float : false_type +{ +}; +template +struct is_float : is_float +{ +}; +template +struct is_float : is_float +{ +}; +template +struct is_float : is_float +{ +}; +template <> +struct is_float : true_type +{ +}; +template <> +struct is_float : true_type +{ +}; +template <> +struct is_float : true_type +{ +}; +#endif + +/// Type traits for floating point bits. +template +struct bits +{ + typedef unsigned char type; +}; +template +struct bits : bits +{ +}; +template +struct bits : bits +{ +}; +template +struct bits : bits +{ +}; + +#if HALF_ENABLE_CPP11_CSTDINT +/// Unsigned integer of (at least) 16 bits width. +typedef std::uint_least16_t uint16; + +/// Unsigned integer of (at least) 32 bits width. +template <> +struct bits +{ + typedef std::uint_least32_t type; +}; + +/// Unsigned integer of (at least) 64 bits width. +template <> +struct bits +{ + typedef std::uint_least64_t type; +}; +#else +/// Unsigned integer of (at least) 16 bits width. +typedef unsigned short uint16; + +/// Unsigned integer of (at least) 32 bits width. +template <> +struct bits : conditional::digits >= 32, unsigned int, unsigned long> +{ +}; + +#if HALF_ENABLE_CPP11_LONG_LONG +/// Unsigned integer of (at least) 64 bits width. +template <> +struct bits : conditional::digits >= 64, unsigned long, unsigned long long> +{ +}; +#else +/// Unsigned integer of (at least) 64 bits width. +template <> +struct bits +{ + typedef unsigned long type; +}; +#endif +#endif + +/// Tag type for binary construction. +struct binary_t +{ +}; + +/// Tag for binary construction. +HALF_CONSTEXPR_CONST binary_t binary = binary_t(); + +/// Temporary half-precision expression. +/// This class represents a half-precision expression which just stores a single-precision value internally. +struct expr +{ + /// Conversion constructor. + /// \param f single-precision value to convert + explicit HALF_CONSTEXPR expr(float f) HALF_NOEXCEPT : value_(f) {} + + /// Conversion to single-precision. + /// \return single precision value representing expression value + HALF_CONSTEXPR operator float() const HALF_NOEXCEPT + { + return value_; + } + +private: + /// Internal expression value stored in single-precision. + float value_; +}; + +/// SFINAE helper for generic half-precision functions. +/// This class template has to be specialized for each valid combination of argument types to provide a corresponding +/// `type` member equivalent to \a T. +/// \tparam T type to return +template +struct enable +{ +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; +template +struct enable +{ + typedef T type; +}; + +/// Return type for specialized generic 2-argument half-precision functions. +/// This class template has to be specialized for each valid combination of argument types to provide a corresponding +/// `type` member denoting the appropriate return type. +/// \tparam T first argument type +/// \tparam U first argument type +template +struct result : enable +{ +}; +template <> +struct result +{ + typedef half type; +}; + +/// \name Classification helpers +/// \{ + +/// Check for infinity. +/// \tparam T argument type (builtin floating point type) +/// \param arg value to query +/// \retval true if infinity +/// \retval false else +template +bool builtin_isinf(T arg) +{ +#if HALF_ENABLE_CPP11_CMATH + return std::isinf(arg); +#elif defined(_MSC_VER) + return !::_finite(static_cast(arg)) && !::_isnan(static_cast(arg)); +#else + return arg == std::numeric_limits::infinity() || arg == -std::numeric_limits::infinity(); +#endif +} + +/// Check for NaN. +/// \tparam T argument type (builtin floating point type) +/// \param arg value to query +/// \retval true if not a number +/// \retval false else +template +bool builtin_isnan(T arg) +{ +#if HALF_ENABLE_CPP11_CMATH + return std::isnan(arg); +#elif defined(_MSC_VER) + return ::_isnan(static_cast(arg)) != 0; +#else + return arg != arg; +#endif +} + +/// Check sign. +/// \tparam T argument type (builtin floating point type) +/// \param arg value to query +/// \retval true if signbit set +/// \retval false else +template +bool builtin_signbit(T arg) +{ +#if HALF_ENABLE_CPP11_CMATH + return std::signbit(arg); +#else + return arg < T() || (arg == T() && T(1) / arg < T()); +#endif +} + +/// \} +/// \name Conversion +/// \{ + +/// Convert IEEE single-precision to half-precision. +/// Credit for this goes to [Jeroen van der Zijp](ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf). +/// \tparam R rounding mode to use, `std::round_indeterminate` for fastest rounding +/// \param value single-precision value +/// \return binary representation of half-precision value +template +uint16 float2half_impl(float value, true_type) +{ + typedef bits::type uint32; + uint32 bits; // = *reinterpret_cast(&value); //violating strict aliasing! + std::memcpy(&bits, &value, sizeof(float)); + /* uint16 hbits = (bits>>16) & 0x8000; + bits &= 0x7FFFFFFF; + int exp = bits >> 23; + if(exp == 255) + return hbits | 0x7C00 | (0x3FF&-static_cast((bits&0x7FFFFF)!=0)); + if(exp > 142) + { + if(R == std::round_toward_infinity) + return hbits | 0x7C00 - (hbits>>15); + if(R == std::round_toward_neg_infinity) + return hbits | 0x7BFF + (hbits>>15); + return hbits | 0x7BFF + (R!=std::round_toward_zero); + } + int g, s; + if(exp > 112) + { + g = (bits>>12) & 1; + s = (bits&0xFFF) != 0; + hbits |= ((exp-112)<<10) | ((bits>>13)&0x3FF); + } + else if(exp > 101) + { + int i = 125 - exp; + bits = (bits&0x7FFFFF) | 0x800000; + g = (bits>>i) & 1; + s = (bits&((1L<> (i+1); + } + else + { + g = 0; + s = bits != 0; + } + if(R == std::round_to_nearest) + #if HALF_ROUND_TIES_TO_EVEN + hbits += g & (s|hbits); + #else + hbits += g; + #endif + else if(R == std::round_toward_infinity) + hbits += ~(hbits>>15) & (s|g); + else if(R == std::round_toward_neg_infinity) + hbits += (hbits>>15) & (g|s); + */ + static const uint16 base_table[512] = {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, + 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, + 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00, 0x4000, 0x4400, 0x4800, 0x4C00, 0x5000, 0x5400, 0x5800, + 0x5C00, 0x6000, 0x6400, 0x6800, 0x6C00, 0x7000, 0x7400, 0x7800, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, + 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, + 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, + 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, + 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, + 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, + 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, + 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, + 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x7C00, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, + 0x8001, 0x8002, 0x8004, 0x8008, 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, 0x8800, 0x8C00, 0x9000, + 0x9400, 0x9800, 0x9C00, 0xA000, 0xA400, 0xA800, 0xAC00, 0xB000, 0xB400, 0xB800, 0xBC00, 0xC000, 0xC400, 0xC800, + 0xCC00, 0xD000, 0xD400, 0xD800, 0xDC00, 0xE000, 0xE400, 0xE800, 0xEC00, 0xF000, 0xF400, 0xF800, 0xFC00, 0xFC00, + 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, + 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, + 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, + 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, + 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, + 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, + 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, + 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFC00}; + static const unsigned char shift_table[512] = {24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 13, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 13}; + uint16 hbits = base_table[bits >> 23] + static_cast((bits & 0x7FFFFF) >> shift_table[bits >> 23]); + if (R == std::round_to_nearest) + hbits += (((bits & 0x7FFFFF) >> (shift_table[bits >> 23] - 1)) | (((bits >> 23) & 0xFF) == 102)) + & ((hbits & 0x7C00) != 0x7C00) +#if HALF_ROUND_TIES_TO_EVEN + & (((((static_cast(1) << (shift_table[bits >> 23] - 1)) - 1) & bits) != 0) | hbits) +#endif + ; + else if (R == std::round_toward_zero) + hbits -= ((hbits & 0x7FFF) == 0x7C00) & ~shift_table[bits >> 23]; + else if (R == std::round_toward_infinity) + hbits += ((((bits & 0x7FFFFF & ((static_cast(1) << (shift_table[bits >> 23])) - 1)) != 0) + | (((bits >> 23) <= 102) & ((bits >> 23) != 0))) + & (hbits < 0x7C00)) + - ((hbits == 0xFC00) & ((bits >> 23) != 511)); + else if (R == std::round_toward_neg_infinity) + hbits += ((((bits & 0x7FFFFF & ((static_cast(1) << (shift_table[bits >> 23])) - 1)) != 0) + | (((bits >> 23) <= 358) & ((bits >> 23) != 256))) + & (hbits < 0xFC00) & (hbits >> 15)) + - ((hbits == 0x7C00) & ((bits >> 23) != 255)); + return hbits; +} + +/// Convert IEEE double-precision to half-precision. +/// \tparam R rounding mode to use, `std::round_indeterminate` for fastest rounding +/// \param value double-precision value +/// \return binary representation of half-precision value +template +uint16 float2half_impl(double value, true_type) +{ + typedef bits::type uint32; + typedef bits::type uint64; + uint64 bits; // = *reinterpret_cast(&value); //violating strict aliasing! + std::memcpy(&bits, &value, sizeof(double)); + uint32 hi = bits >> 32, lo = bits & 0xFFFFFFFF; + uint16 hbits = (hi >> 16) & 0x8000; + hi &= 0x7FFFFFFF; + int exp = hi >> 20; + if (exp == 2047) + return hbits | 0x7C00 | (0x3FF & -static_cast((bits & 0xFFFFFFFFFFFFF) != 0)); + if (exp > 1038) + { + if (R == std::round_toward_infinity) + return hbits | 0x7C00 - (hbits >> 15); + if (R == std::round_toward_neg_infinity) + return hbits | 0x7BFF + (hbits >> 15); + return hbits | 0x7BFF + (R != std::round_toward_zero); + } + int g, s = lo != 0; + if (exp > 1008) + { + g = (hi >> 9) & 1; + s |= (hi & 0x1FF) != 0; + hbits |= ((exp - 1008) << 10) | ((hi >> 10) & 0x3FF); + } + else if (exp > 997) + { + int i = 1018 - exp; + hi = (hi & 0xFFFFF) | 0x100000; + g = (hi >> i) & 1; + s |= (hi & ((1L << i) - 1)) != 0; + hbits |= hi >> (i + 1); + } + else + { + g = 0; + s |= hi != 0; + } + if (R == std::round_to_nearest) +#if HALF_ROUND_TIES_TO_EVEN + hbits += g & (s | hbits); +#else + hbits += g; +#endif + else if (R == std::round_toward_infinity) + hbits += ~(hbits >> 15) & (s | g); + else if (R == std::round_toward_neg_infinity) + hbits += (hbits >> 15) & (g | s); + return hbits; +} + +/// Convert non-IEEE floating point to half-precision. +/// \tparam R rounding mode to use, `std::round_indeterminate` for fastest rounding +/// \tparam T source type (builtin floating point type) +/// \param value floating point value +/// \return binary representation of half-precision value +template +uint16 float2half_impl(T value, ...) +{ + uint16 hbits = static_cast(builtin_signbit(value)) << 15; + if (value == T()) + return hbits; + if (builtin_isnan(value)) + return hbits | 0x7FFF; + if (builtin_isinf(value)) + return hbits | 0x7C00; + int exp; + std::frexp(value, &exp); + if (exp > 16) + { + if (R == std::round_toward_infinity) + return hbits | (0x7C00 - (hbits >> 15)); + else if (R == std::round_toward_neg_infinity) + return hbits | (0x7BFF + (hbits >> 15)); + return hbits | (0x7BFF + (R != std::round_toward_zero)); + } + if (exp < -13) + value = std::ldexp(value, 24); + else + { + value = std::ldexp(value, 11 - exp); + hbits |= ((exp + 13) << 10); + } + T ival, frac = std::modf(value, &ival); + hbits += static_cast(std::abs(static_cast(ival))); + if (R == std::round_to_nearest) + { + frac = std::abs(frac); +#if HALF_ROUND_TIES_TO_EVEN + hbits += (frac > T(0.5)) | ((frac == T(0.5)) & hbits); +#else + hbits += frac >= T(0.5); +#endif + } + else if (R == std::round_toward_infinity) + hbits += frac > T(); + else if (R == std::round_toward_neg_infinity) + hbits += frac < T(); + return hbits; +} + +/// Convert floating point to half-precision. +/// \tparam R rounding mode to use, `std::round_indeterminate` for fastest rounding +/// \tparam T source type (builtin floating point type) +/// \param value floating point value +/// \return binary representation of half-precision value +template +uint16 float2half(T value) +{ + return float2half_impl( + value, bool_type < std::numeric_limits::is_iec559 && sizeof(typename bits::type) == sizeof(T) > ()); +} + +/// Convert integer to half-precision floating point. +/// \tparam R rounding mode to use, `std::round_indeterminate` for fastest rounding +/// \tparam S `true` if value negative, `false` else +/// \tparam T type to convert (builtin integer type) +/// \param value non-negative integral value +/// \return binary representation of half-precision value +template +uint16 int2half_impl(T value) +{ +#if HALF_ENABLE_CPP11_STATIC_ASSERT && HALF_ENABLE_CPP11_TYPE_TRAITS + static_assert(std::is_integral::value, "int to half conversion only supports builtin integer types"); +#endif + if (S) + value = -value; + uint16 bits = S << 15; + if (value > 0xFFFF) + { + if (R == std::round_toward_infinity) + bits |= 0x7C00 - S; + else if (R == std::round_toward_neg_infinity) + bits |= 0x7BFF + S; + else + bits |= 0x7BFF + (R != std::round_toward_zero); + } + else if (value) + { + uint32_t m = value, exp = 24; + for (; m < 0x400; m <<= 1, --exp) + ; + for (; m > 0x7FF; m >>= 1, ++exp) + ; + bits |= (exp << 10) + m; + if (exp > 24) + { + if (R == std::round_to_nearest) + bits += (value >> (exp - 25)) & 1 +#if HALF_ROUND_TIES_TO_EVEN + & (((((1 << (exp - 25)) - 1) & value) != 0) | bits) +#endif + ; + else if (R == std::round_toward_infinity) + bits += ((value & ((1 << (exp - 24)) - 1)) != 0) & !S; + else if (R == std::round_toward_neg_infinity) + bits += ((value & ((1 << (exp - 24)) - 1)) != 0) & S; + } + } + return bits; +} + +/// Convert integer to half-precision floating point. +/// \tparam R rounding mode to use, `std::round_indeterminate` for fastest rounding +/// \tparam T type to convert (builtin integer type) +/// \param value integral value +/// \return binary representation of half-precision value +template +uint16 int2half(T value) +{ + return (value < 0) ? int2half_impl(value) : int2half_impl(value); +} + +/// Convert half-precision to IEEE single-precision. +/// Credit for this goes to [Jeroen van der Zijp](ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf). +/// \param value binary representation of half-precision value +/// \return single-precision value +inline float half2float_impl(uint16 value, float, true_type) +{ + typedef bits::type uint32; + /* uint32 bits = static_cast(value&0x8000) << 16; + int abs = value & 0x7FFF; + if(abs) + { + bits |= 0x38000000 << static_cast(abs>=0x7C00); + for(; abs<0x400; abs<<=1,bits-=0x800000) ; + bits += static_cast(abs) << 13; + } + */ + static const uint32 mantissa_table[2048] = {0x00000000, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34A00000, + 0x34C00000, 0x34E00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000, 0x35400000, 0x35500000, 0x35600000, + 0x35700000, 0x35800000, 0x35880000, 0x35900000, 0x35980000, 0x35A00000, 0x35A80000, 0x35B00000, 0x35B80000, + 0x35C00000, 0x35C80000, 0x35D00000, 0x35D80000, 0x35E00000, 0x35E80000, 0x35F00000, 0x35F80000, 0x36000000, + 0x36040000, 0x36080000, 0x360C0000, 0x36100000, 0x36140000, 0x36180000, 0x361C0000, 0x36200000, 0x36240000, + 0x36280000, 0x362C0000, 0x36300000, 0x36340000, 0x36380000, 0x363C0000, 0x36400000, 0x36440000, 0x36480000, + 0x364C0000, 0x36500000, 0x36540000, 0x36580000, 0x365C0000, 0x36600000, 0x36640000, 0x36680000, 0x366C0000, + 0x36700000, 0x36740000, 0x36780000, 0x367C0000, 0x36800000, 0x36820000, 0x36840000, 0x36860000, 0x36880000, + 0x368A0000, 0x368C0000, 0x368E0000, 0x36900000, 0x36920000, 0x36940000, 0x36960000, 0x36980000, 0x369A0000, + 0x369C0000, 0x369E0000, 0x36A00000, 0x36A20000, 0x36A40000, 0x36A60000, 0x36A80000, 0x36AA0000, 0x36AC0000, + 0x36AE0000, 0x36B00000, 0x36B20000, 0x36B40000, 0x36B60000, 0x36B80000, 0x36BA0000, 0x36BC0000, 0x36BE0000, + 0x36C00000, 0x36C20000, 0x36C40000, 0x36C60000, 0x36C80000, 0x36CA0000, 0x36CC0000, 0x36CE0000, 0x36D00000, + 0x36D20000, 0x36D40000, 0x36D60000, 0x36D80000, 0x36DA0000, 0x36DC0000, 0x36DE0000, 0x36E00000, 0x36E20000, + 0x36E40000, 0x36E60000, 0x36E80000, 0x36EA0000, 0x36EC0000, 0x36EE0000, 0x36F00000, 0x36F20000, 0x36F40000, + 0x36F60000, 0x36F80000, 0x36FA0000, 0x36FC0000, 0x36FE0000, 0x37000000, 0x37010000, 0x37020000, 0x37030000, + 0x37040000, 0x37050000, 0x37060000, 0x37070000, 0x37080000, 0x37090000, 0x370A0000, 0x370B0000, 0x370C0000, + 0x370D0000, 0x370E0000, 0x370F0000, 0x37100000, 0x37110000, 0x37120000, 0x37130000, 0x37140000, 0x37150000, + 0x37160000, 0x37170000, 0x37180000, 0x37190000, 0x371A0000, 0x371B0000, 0x371C0000, 0x371D0000, 0x371E0000, + 0x371F0000, 0x37200000, 0x37210000, 0x37220000, 0x37230000, 0x37240000, 0x37250000, 0x37260000, 0x37270000, + 0x37280000, 0x37290000, 0x372A0000, 0x372B0000, 0x372C0000, 0x372D0000, 0x372E0000, 0x372F0000, 0x37300000, + 0x37310000, 0x37320000, 0x37330000, 0x37340000, 0x37350000, 0x37360000, 0x37370000, 0x37380000, 0x37390000, + 0x373A0000, 0x373B0000, 0x373C0000, 0x373D0000, 0x373E0000, 0x373F0000, 0x37400000, 0x37410000, 0x37420000, + 0x37430000, 0x37440000, 0x37450000, 0x37460000, 0x37470000, 0x37480000, 0x37490000, 0x374A0000, 0x374B0000, + 0x374C0000, 0x374D0000, 0x374E0000, 0x374F0000, 0x37500000, 0x37510000, 0x37520000, 0x37530000, 0x37540000, + 0x37550000, 0x37560000, 0x37570000, 0x37580000, 0x37590000, 0x375A0000, 0x375B0000, 0x375C0000, 0x375D0000, + 0x375E0000, 0x375F0000, 0x37600000, 0x37610000, 0x37620000, 0x37630000, 0x37640000, 0x37650000, 0x37660000, + 0x37670000, 0x37680000, 0x37690000, 0x376A0000, 0x376B0000, 0x376C0000, 0x376D0000, 0x376E0000, 0x376F0000, + 0x37700000, 0x37710000, 0x37720000, 0x37730000, 0x37740000, 0x37750000, 0x37760000, 0x37770000, 0x37780000, + 0x37790000, 0x377A0000, 0x377B0000, 0x377C0000, 0x377D0000, 0x377E0000, 0x377F0000, 0x37800000, 0x37808000, + 0x37810000, 0x37818000, 0x37820000, 0x37828000, 0x37830000, 0x37838000, 0x37840000, 0x37848000, 0x37850000, + 0x37858000, 0x37860000, 0x37868000, 0x37870000, 0x37878000, 0x37880000, 0x37888000, 0x37890000, 0x37898000, + 0x378A0000, 0x378A8000, 0x378B0000, 0x378B8000, 0x378C0000, 0x378C8000, 0x378D0000, 0x378D8000, 0x378E0000, + 0x378E8000, 0x378F0000, 0x378F8000, 0x37900000, 0x37908000, 0x37910000, 0x37918000, 0x37920000, 0x37928000, + 0x37930000, 0x37938000, 0x37940000, 0x37948000, 0x37950000, 0x37958000, 0x37960000, 0x37968000, 0x37970000, + 0x37978000, 0x37980000, 0x37988000, 0x37990000, 0x37998000, 0x379A0000, 0x379A8000, 0x379B0000, 0x379B8000, + 0x379C0000, 0x379C8000, 0x379D0000, 0x379D8000, 0x379E0000, 0x379E8000, 0x379F0000, 0x379F8000, 0x37A00000, + 0x37A08000, 0x37A10000, 0x37A18000, 0x37A20000, 0x37A28000, 0x37A30000, 0x37A38000, 0x37A40000, 0x37A48000, + 0x37A50000, 0x37A58000, 0x37A60000, 0x37A68000, 0x37A70000, 0x37A78000, 0x37A80000, 0x37A88000, 0x37A90000, + 0x37A98000, 0x37AA0000, 0x37AA8000, 0x37AB0000, 0x37AB8000, 0x37AC0000, 0x37AC8000, 0x37AD0000, 0x37AD8000, + 0x37AE0000, 0x37AE8000, 0x37AF0000, 0x37AF8000, 0x37B00000, 0x37B08000, 0x37B10000, 0x37B18000, 0x37B20000, + 0x37B28000, 0x37B30000, 0x37B38000, 0x37B40000, 0x37B48000, 0x37B50000, 0x37B58000, 0x37B60000, 0x37B68000, + 0x37B70000, 0x37B78000, 0x37B80000, 0x37B88000, 0x37B90000, 0x37B98000, 0x37BA0000, 0x37BA8000, 0x37BB0000, + 0x37BB8000, 0x37BC0000, 0x37BC8000, 0x37BD0000, 0x37BD8000, 0x37BE0000, 0x37BE8000, 0x37BF0000, 0x37BF8000, + 0x37C00000, 0x37C08000, 0x37C10000, 0x37C18000, 0x37C20000, 0x37C28000, 0x37C30000, 0x37C38000, 0x37C40000, + 0x37C48000, 0x37C50000, 0x37C58000, 0x37C60000, 0x37C68000, 0x37C70000, 0x37C78000, 0x37C80000, 0x37C88000, + 0x37C90000, 0x37C98000, 0x37CA0000, 0x37CA8000, 0x37CB0000, 0x37CB8000, 0x37CC0000, 0x37CC8000, 0x37CD0000, + 0x37CD8000, 0x37CE0000, 0x37CE8000, 0x37CF0000, 0x37CF8000, 0x37D00000, 0x37D08000, 0x37D10000, 0x37D18000, + 0x37D20000, 0x37D28000, 0x37D30000, 0x37D38000, 0x37D40000, 0x37D48000, 0x37D50000, 0x37D58000, 0x37D60000, + 0x37D68000, 0x37D70000, 0x37D78000, 0x37D80000, 0x37D88000, 0x37D90000, 0x37D98000, 0x37DA0000, 0x37DA8000, + 0x37DB0000, 0x37DB8000, 0x37DC0000, 0x37DC8000, 0x37DD0000, 0x37DD8000, 0x37DE0000, 0x37DE8000, 0x37DF0000, + 0x37DF8000, 0x37E00000, 0x37E08000, 0x37E10000, 0x37E18000, 0x37E20000, 0x37E28000, 0x37E30000, 0x37E38000, + 0x37E40000, 0x37E48000, 0x37E50000, 0x37E58000, 0x37E60000, 0x37E68000, 0x37E70000, 0x37E78000, 0x37E80000, + 0x37E88000, 0x37E90000, 0x37E98000, 0x37EA0000, 0x37EA8000, 0x37EB0000, 0x37EB8000, 0x37EC0000, 0x37EC8000, + 0x37ED0000, 0x37ED8000, 0x37EE0000, 0x37EE8000, 0x37EF0000, 0x37EF8000, 0x37F00000, 0x37F08000, 0x37F10000, + 0x37F18000, 0x37F20000, 0x37F28000, 0x37F30000, 0x37F38000, 0x37F40000, 0x37F48000, 0x37F50000, 0x37F58000, + 0x37F60000, 0x37F68000, 0x37F70000, 0x37F78000, 0x37F80000, 0x37F88000, 0x37F90000, 0x37F98000, 0x37FA0000, + 0x37FA8000, 0x37FB0000, 0x37FB8000, 0x37FC0000, 0x37FC8000, 0x37FD0000, 0x37FD8000, 0x37FE0000, 0x37FE8000, + 0x37FF0000, 0x37FF8000, 0x38000000, 0x38004000, 0x38008000, 0x3800C000, 0x38010000, 0x38014000, 0x38018000, + 0x3801C000, 0x38020000, 0x38024000, 0x38028000, 0x3802C000, 0x38030000, 0x38034000, 0x38038000, 0x3803C000, + 0x38040000, 0x38044000, 0x38048000, 0x3804C000, 0x38050000, 0x38054000, 0x38058000, 0x3805C000, 0x38060000, + 0x38064000, 0x38068000, 0x3806C000, 0x38070000, 0x38074000, 0x38078000, 0x3807C000, 0x38080000, 0x38084000, + 0x38088000, 0x3808C000, 0x38090000, 0x38094000, 0x38098000, 0x3809C000, 0x380A0000, 0x380A4000, 0x380A8000, + 0x380AC000, 0x380B0000, 0x380B4000, 0x380B8000, 0x380BC000, 0x380C0000, 0x380C4000, 0x380C8000, 0x380CC000, + 0x380D0000, 0x380D4000, 0x380D8000, 0x380DC000, 0x380E0000, 0x380E4000, 0x380E8000, 0x380EC000, 0x380F0000, + 0x380F4000, 0x380F8000, 0x380FC000, 0x38100000, 0x38104000, 0x38108000, 0x3810C000, 0x38110000, 0x38114000, + 0x38118000, 0x3811C000, 0x38120000, 0x38124000, 0x38128000, 0x3812C000, 0x38130000, 0x38134000, 0x38138000, + 0x3813C000, 0x38140000, 0x38144000, 0x38148000, 0x3814C000, 0x38150000, 0x38154000, 0x38158000, 0x3815C000, + 0x38160000, 0x38164000, 0x38168000, 0x3816C000, 0x38170000, 0x38174000, 0x38178000, 0x3817C000, 0x38180000, + 0x38184000, 0x38188000, 0x3818C000, 0x38190000, 0x38194000, 0x38198000, 0x3819C000, 0x381A0000, 0x381A4000, + 0x381A8000, 0x381AC000, 0x381B0000, 0x381B4000, 0x381B8000, 0x381BC000, 0x381C0000, 0x381C4000, 0x381C8000, + 0x381CC000, 0x381D0000, 0x381D4000, 0x381D8000, 0x381DC000, 0x381E0000, 0x381E4000, 0x381E8000, 0x381EC000, + 0x381F0000, 0x381F4000, 0x381F8000, 0x381FC000, 0x38200000, 0x38204000, 0x38208000, 0x3820C000, 0x38210000, + 0x38214000, 0x38218000, 0x3821C000, 0x38220000, 0x38224000, 0x38228000, 0x3822C000, 0x38230000, 0x38234000, + 0x38238000, 0x3823C000, 0x38240000, 0x38244000, 0x38248000, 0x3824C000, 0x38250000, 0x38254000, 0x38258000, + 0x3825C000, 0x38260000, 0x38264000, 0x38268000, 0x3826C000, 0x38270000, 0x38274000, 0x38278000, 0x3827C000, + 0x38280000, 0x38284000, 0x38288000, 0x3828C000, 0x38290000, 0x38294000, 0x38298000, 0x3829C000, 0x382A0000, + 0x382A4000, 0x382A8000, 0x382AC000, 0x382B0000, 0x382B4000, 0x382B8000, 0x382BC000, 0x382C0000, 0x382C4000, + 0x382C8000, 0x382CC000, 0x382D0000, 0x382D4000, 0x382D8000, 0x382DC000, 0x382E0000, 0x382E4000, 0x382E8000, + 0x382EC000, 0x382F0000, 0x382F4000, 0x382F8000, 0x382FC000, 0x38300000, 0x38304000, 0x38308000, 0x3830C000, + 0x38310000, 0x38314000, 0x38318000, 0x3831C000, 0x38320000, 0x38324000, 0x38328000, 0x3832C000, 0x38330000, + 0x38334000, 0x38338000, 0x3833C000, 0x38340000, 0x38344000, 0x38348000, 0x3834C000, 0x38350000, 0x38354000, + 0x38358000, 0x3835C000, 0x38360000, 0x38364000, 0x38368000, 0x3836C000, 0x38370000, 0x38374000, 0x38378000, + 0x3837C000, 0x38380000, 0x38384000, 0x38388000, 0x3838C000, 0x38390000, 0x38394000, 0x38398000, 0x3839C000, + 0x383A0000, 0x383A4000, 0x383A8000, 0x383AC000, 0x383B0000, 0x383B4000, 0x383B8000, 0x383BC000, 0x383C0000, + 0x383C4000, 0x383C8000, 0x383CC000, 0x383D0000, 0x383D4000, 0x383D8000, 0x383DC000, 0x383E0000, 0x383E4000, + 0x383E8000, 0x383EC000, 0x383F0000, 0x383F4000, 0x383F8000, 0x383FC000, 0x38400000, 0x38404000, 0x38408000, + 0x3840C000, 0x38410000, 0x38414000, 0x38418000, 0x3841C000, 0x38420000, 0x38424000, 0x38428000, 0x3842C000, + 0x38430000, 0x38434000, 0x38438000, 0x3843C000, 0x38440000, 0x38444000, 0x38448000, 0x3844C000, 0x38450000, + 0x38454000, 0x38458000, 0x3845C000, 0x38460000, 0x38464000, 0x38468000, 0x3846C000, 0x38470000, 0x38474000, + 0x38478000, 0x3847C000, 0x38480000, 0x38484000, 0x38488000, 0x3848C000, 0x38490000, 0x38494000, 0x38498000, + 0x3849C000, 0x384A0000, 0x384A4000, 0x384A8000, 0x384AC000, 0x384B0000, 0x384B4000, 0x384B8000, 0x384BC000, + 0x384C0000, 0x384C4000, 0x384C8000, 0x384CC000, 0x384D0000, 0x384D4000, 0x384D8000, 0x384DC000, 0x384E0000, + 0x384E4000, 0x384E8000, 0x384EC000, 0x384F0000, 0x384F4000, 0x384F8000, 0x384FC000, 0x38500000, 0x38504000, + 0x38508000, 0x3850C000, 0x38510000, 0x38514000, 0x38518000, 0x3851C000, 0x38520000, 0x38524000, 0x38528000, + 0x3852C000, 0x38530000, 0x38534000, 0x38538000, 0x3853C000, 0x38540000, 0x38544000, 0x38548000, 0x3854C000, + 0x38550000, 0x38554000, 0x38558000, 0x3855C000, 0x38560000, 0x38564000, 0x38568000, 0x3856C000, 0x38570000, + 0x38574000, 0x38578000, 0x3857C000, 0x38580000, 0x38584000, 0x38588000, 0x3858C000, 0x38590000, 0x38594000, + 0x38598000, 0x3859C000, 0x385A0000, 0x385A4000, 0x385A8000, 0x385AC000, 0x385B0000, 0x385B4000, 0x385B8000, + 0x385BC000, 0x385C0000, 0x385C4000, 0x385C8000, 0x385CC000, 0x385D0000, 0x385D4000, 0x385D8000, 0x385DC000, + 0x385E0000, 0x385E4000, 0x385E8000, 0x385EC000, 0x385F0000, 0x385F4000, 0x385F8000, 0x385FC000, 0x38600000, + 0x38604000, 0x38608000, 0x3860C000, 0x38610000, 0x38614000, 0x38618000, 0x3861C000, 0x38620000, 0x38624000, + 0x38628000, 0x3862C000, 0x38630000, 0x38634000, 0x38638000, 0x3863C000, 0x38640000, 0x38644000, 0x38648000, + 0x3864C000, 0x38650000, 0x38654000, 0x38658000, 0x3865C000, 0x38660000, 0x38664000, 0x38668000, 0x3866C000, + 0x38670000, 0x38674000, 0x38678000, 0x3867C000, 0x38680000, 0x38684000, 0x38688000, 0x3868C000, 0x38690000, + 0x38694000, 0x38698000, 0x3869C000, 0x386A0000, 0x386A4000, 0x386A8000, 0x386AC000, 0x386B0000, 0x386B4000, + 0x386B8000, 0x386BC000, 0x386C0000, 0x386C4000, 0x386C8000, 0x386CC000, 0x386D0000, 0x386D4000, 0x386D8000, + 0x386DC000, 0x386E0000, 0x386E4000, 0x386E8000, 0x386EC000, 0x386F0000, 0x386F4000, 0x386F8000, 0x386FC000, + 0x38700000, 0x38704000, 0x38708000, 0x3870C000, 0x38710000, 0x38714000, 0x38718000, 0x3871C000, 0x38720000, + 0x38724000, 0x38728000, 0x3872C000, 0x38730000, 0x38734000, 0x38738000, 0x3873C000, 0x38740000, 0x38744000, + 0x38748000, 0x3874C000, 0x38750000, 0x38754000, 0x38758000, 0x3875C000, 0x38760000, 0x38764000, 0x38768000, + 0x3876C000, 0x38770000, 0x38774000, 0x38778000, 0x3877C000, 0x38780000, 0x38784000, 0x38788000, 0x3878C000, + 0x38790000, 0x38794000, 0x38798000, 0x3879C000, 0x387A0000, 0x387A4000, 0x387A8000, 0x387AC000, 0x387B0000, + 0x387B4000, 0x387B8000, 0x387BC000, 0x387C0000, 0x387C4000, 0x387C8000, 0x387CC000, 0x387D0000, 0x387D4000, + 0x387D8000, 0x387DC000, 0x387E0000, 0x387E4000, 0x387E8000, 0x387EC000, 0x387F0000, 0x387F4000, 0x387F8000, + 0x387FC000, 0x38000000, 0x38002000, 0x38004000, 0x38006000, 0x38008000, 0x3800A000, 0x3800C000, 0x3800E000, + 0x38010000, 0x38012000, 0x38014000, 0x38016000, 0x38018000, 0x3801A000, 0x3801C000, 0x3801E000, 0x38020000, + 0x38022000, 0x38024000, 0x38026000, 0x38028000, 0x3802A000, 0x3802C000, 0x3802E000, 0x38030000, 0x38032000, + 0x38034000, 0x38036000, 0x38038000, 0x3803A000, 0x3803C000, 0x3803E000, 0x38040000, 0x38042000, 0x38044000, + 0x38046000, 0x38048000, 0x3804A000, 0x3804C000, 0x3804E000, 0x38050000, 0x38052000, 0x38054000, 0x38056000, + 0x38058000, 0x3805A000, 0x3805C000, 0x3805E000, 0x38060000, 0x38062000, 0x38064000, 0x38066000, 0x38068000, + 0x3806A000, 0x3806C000, 0x3806E000, 0x38070000, 0x38072000, 0x38074000, 0x38076000, 0x38078000, 0x3807A000, + 0x3807C000, 0x3807E000, 0x38080000, 0x38082000, 0x38084000, 0x38086000, 0x38088000, 0x3808A000, 0x3808C000, + 0x3808E000, 0x38090000, 0x38092000, 0x38094000, 0x38096000, 0x38098000, 0x3809A000, 0x3809C000, 0x3809E000, + 0x380A0000, 0x380A2000, 0x380A4000, 0x380A6000, 0x380A8000, 0x380AA000, 0x380AC000, 0x380AE000, 0x380B0000, + 0x380B2000, 0x380B4000, 0x380B6000, 0x380B8000, 0x380BA000, 0x380BC000, 0x380BE000, 0x380C0000, 0x380C2000, + 0x380C4000, 0x380C6000, 0x380C8000, 0x380CA000, 0x380CC000, 0x380CE000, 0x380D0000, 0x380D2000, 0x380D4000, + 0x380D6000, 0x380D8000, 0x380DA000, 0x380DC000, 0x380DE000, 0x380E0000, 0x380E2000, 0x380E4000, 0x380E6000, + 0x380E8000, 0x380EA000, 0x380EC000, 0x380EE000, 0x380F0000, 0x380F2000, 0x380F4000, 0x380F6000, 0x380F8000, + 0x380FA000, 0x380FC000, 0x380FE000, 0x38100000, 0x38102000, 0x38104000, 0x38106000, 0x38108000, 0x3810A000, + 0x3810C000, 0x3810E000, 0x38110000, 0x38112000, 0x38114000, 0x38116000, 0x38118000, 0x3811A000, 0x3811C000, + 0x3811E000, 0x38120000, 0x38122000, 0x38124000, 0x38126000, 0x38128000, 0x3812A000, 0x3812C000, 0x3812E000, + 0x38130000, 0x38132000, 0x38134000, 0x38136000, 0x38138000, 0x3813A000, 0x3813C000, 0x3813E000, 0x38140000, + 0x38142000, 0x38144000, 0x38146000, 0x38148000, 0x3814A000, 0x3814C000, 0x3814E000, 0x38150000, 0x38152000, + 0x38154000, 0x38156000, 0x38158000, 0x3815A000, 0x3815C000, 0x3815E000, 0x38160000, 0x38162000, 0x38164000, + 0x38166000, 0x38168000, 0x3816A000, 0x3816C000, 0x3816E000, 0x38170000, 0x38172000, 0x38174000, 0x38176000, + 0x38178000, 0x3817A000, 0x3817C000, 0x3817E000, 0x38180000, 0x38182000, 0x38184000, 0x38186000, 0x38188000, + 0x3818A000, 0x3818C000, 0x3818E000, 0x38190000, 0x38192000, 0x38194000, 0x38196000, 0x38198000, 0x3819A000, + 0x3819C000, 0x3819E000, 0x381A0000, 0x381A2000, 0x381A4000, 0x381A6000, 0x381A8000, 0x381AA000, 0x381AC000, + 0x381AE000, 0x381B0000, 0x381B2000, 0x381B4000, 0x381B6000, 0x381B8000, 0x381BA000, 0x381BC000, 0x381BE000, + 0x381C0000, 0x381C2000, 0x381C4000, 0x381C6000, 0x381C8000, 0x381CA000, 0x381CC000, 0x381CE000, 0x381D0000, + 0x381D2000, 0x381D4000, 0x381D6000, 0x381D8000, 0x381DA000, 0x381DC000, 0x381DE000, 0x381E0000, 0x381E2000, + 0x381E4000, 0x381E6000, 0x381E8000, 0x381EA000, 0x381EC000, 0x381EE000, 0x381F0000, 0x381F2000, 0x381F4000, + 0x381F6000, 0x381F8000, 0x381FA000, 0x381FC000, 0x381FE000, 0x38200000, 0x38202000, 0x38204000, 0x38206000, + 0x38208000, 0x3820A000, 0x3820C000, 0x3820E000, 0x38210000, 0x38212000, 0x38214000, 0x38216000, 0x38218000, + 0x3821A000, 0x3821C000, 0x3821E000, 0x38220000, 0x38222000, 0x38224000, 0x38226000, 0x38228000, 0x3822A000, + 0x3822C000, 0x3822E000, 0x38230000, 0x38232000, 0x38234000, 0x38236000, 0x38238000, 0x3823A000, 0x3823C000, + 0x3823E000, 0x38240000, 0x38242000, 0x38244000, 0x38246000, 0x38248000, 0x3824A000, 0x3824C000, 0x3824E000, + 0x38250000, 0x38252000, 0x38254000, 0x38256000, 0x38258000, 0x3825A000, 0x3825C000, 0x3825E000, 0x38260000, + 0x38262000, 0x38264000, 0x38266000, 0x38268000, 0x3826A000, 0x3826C000, 0x3826E000, 0x38270000, 0x38272000, + 0x38274000, 0x38276000, 0x38278000, 0x3827A000, 0x3827C000, 0x3827E000, 0x38280000, 0x38282000, 0x38284000, + 0x38286000, 0x38288000, 0x3828A000, 0x3828C000, 0x3828E000, 0x38290000, 0x38292000, 0x38294000, 0x38296000, + 0x38298000, 0x3829A000, 0x3829C000, 0x3829E000, 0x382A0000, 0x382A2000, 0x382A4000, 0x382A6000, 0x382A8000, + 0x382AA000, 0x382AC000, 0x382AE000, 0x382B0000, 0x382B2000, 0x382B4000, 0x382B6000, 0x382B8000, 0x382BA000, + 0x382BC000, 0x382BE000, 0x382C0000, 0x382C2000, 0x382C4000, 0x382C6000, 0x382C8000, 0x382CA000, 0x382CC000, + 0x382CE000, 0x382D0000, 0x382D2000, 0x382D4000, 0x382D6000, 0x382D8000, 0x382DA000, 0x382DC000, 0x382DE000, + 0x382E0000, 0x382E2000, 0x382E4000, 0x382E6000, 0x382E8000, 0x382EA000, 0x382EC000, 0x382EE000, 0x382F0000, + 0x382F2000, 0x382F4000, 0x382F6000, 0x382F8000, 0x382FA000, 0x382FC000, 0x382FE000, 0x38300000, 0x38302000, + 0x38304000, 0x38306000, 0x38308000, 0x3830A000, 0x3830C000, 0x3830E000, 0x38310000, 0x38312000, 0x38314000, + 0x38316000, 0x38318000, 0x3831A000, 0x3831C000, 0x3831E000, 0x38320000, 0x38322000, 0x38324000, 0x38326000, + 0x38328000, 0x3832A000, 0x3832C000, 0x3832E000, 0x38330000, 0x38332000, 0x38334000, 0x38336000, 0x38338000, + 0x3833A000, 0x3833C000, 0x3833E000, 0x38340000, 0x38342000, 0x38344000, 0x38346000, 0x38348000, 0x3834A000, + 0x3834C000, 0x3834E000, 0x38350000, 0x38352000, 0x38354000, 0x38356000, 0x38358000, 0x3835A000, 0x3835C000, + 0x3835E000, 0x38360000, 0x38362000, 0x38364000, 0x38366000, 0x38368000, 0x3836A000, 0x3836C000, 0x3836E000, + 0x38370000, 0x38372000, 0x38374000, 0x38376000, 0x38378000, 0x3837A000, 0x3837C000, 0x3837E000, 0x38380000, + 0x38382000, 0x38384000, 0x38386000, 0x38388000, 0x3838A000, 0x3838C000, 0x3838E000, 0x38390000, 0x38392000, + 0x38394000, 0x38396000, 0x38398000, 0x3839A000, 0x3839C000, 0x3839E000, 0x383A0000, 0x383A2000, 0x383A4000, + 0x383A6000, 0x383A8000, 0x383AA000, 0x383AC000, 0x383AE000, 0x383B0000, 0x383B2000, 0x383B4000, 0x383B6000, + 0x383B8000, 0x383BA000, 0x383BC000, 0x383BE000, 0x383C0000, 0x383C2000, 0x383C4000, 0x383C6000, 0x383C8000, + 0x383CA000, 0x383CC000, 0x383CE000, 0x383D0000, 0x383D2000, 0x383D4000, 0x383D6000, 0x383D8000, 0x383DA000, + 0x383DC000, 0x383DE000, 0x383E0000, 0x383E2000, 0x383E4000, 0x383E6000, 0x383E8000, 0x383EA000, 0x383EC000, + 0x383EE000, 0x383F0000, 0x383F2000, 0x383F4000, 0x383F6000, 0x383F8000, 0x383FA000, 0x383FC000, 0x383FE000, + 0x38400000, 0x38402000, 0x38404000, 0x38406000, 0x38408000, 0x3840A000, 0x3840C000, 0x3840E000, 0x38410000, + 0x38412000, 0x38414000, 0x38416000, 0x38418000, 0x3841A000, 0x3841C000, 0x3841E000, 0x38420000, 0x38422000, + 0x38424000, 0x38426000, 0x38428000, 0x3842A000, 0x3842C000, 0x3842E000, 0x38430000, 0x38432000, 0x38434000, + 0x38436000, 0x38438000, 0x3843A000, 0x3843C000, 0x3843E000, 0x38440000, 0x38442000, 0x38444000, 0x38446000, + 0x38448000, 0x3844A000, 0x3844C000, 0x3844E000, 0x38450000, 0x38452000, 0x38454000, 0x38456000, 0x38458000, + 0x3845A000, 0x3845C000, 0x3845E000, 0x38460000, 0x38462000, 0x38464000, 0x38466000, 0x38468000, 0x3846A000, + 0x3846C000, 0x3846E000, 0x38470000, 0x38472000, 0x38474000, 0x38476000, 0x38478000, 0x3847A000, 0x3847C000, + 0x3847E000, 0x38480000, 0x38482000, 0x38484000, 0x38486000, 0x38488000, 0x3848A000, 0x3848C000, 0x3848E000, + 0x38490000, 0x38492000, 0x38494000, 0x38496000, 0x38498000, 0x3849A000, 0x3849C000, 0x3849E000, 0x384A0000, + 0x384A2000, 0x384A4000, 0x384A6000, 0x384A8000, 0x384AA000, 0x384AC000, 0x384AE000, 0x384B0000, 0x384B2000, + 0x384B4000, 0x384B6000, 0x384B8000, 0x384BA000, 0x384BC000, 0x384BE000, 0x384C0000, 0x384C2000, 0x384C4000, + 0x384C6000, 0x384C8000, 0x384CA000, 0x384CC000, 0x384CE000, 0x384D0000, 0x384D2000, 0x384D4000, 0x384D6000, + 0x384D8000, 0x384DA000, 0x384DC000, 0x384DE000, 0x384E0000, 0x384E2000, 0x384E4000, 0x384E6000, 0x384E8000, + 0x384EA000, 0x384EC000, 0x384EE000, 0x384F0000, 0x384F2000, 0x384F4000, 0x384F6000, 0x384F8000, 0x384FA000, + 0x384FC000, 0x384FE000, 0x38500000, 0x38502000, 0x38504000, 0x38506000, 0x38508000, 0x3850A000, 0x3850C000, + 0x3850E000, 0x38510000, 0x38512000, 0x38514000, 0x38516000, 0x38518000, 0x3851A000, 0x3851C000, 0x3851E000, + 0x38520000, 0x38522000, 0x38524000, 0x38526000, 0x38528000, 0x3852A000, 0x3852C000, 0x3852E000, 0x38530000, + 0x38532000, 0x38534000, 0x38536000, 0x38538000, 0x3853A000, 0x3853C000, 0x3853E000, 0x38540000, 0x38542000, + 0x38544000, 0x38546000, 0x38548000, 0x3854A000, 0x3854C000, 0x3854E000, 0x38550000, 0x38552000, 0x38554000, + 0x38556000, 0x38558000, 0x3855A000, 0x3855C000, 0x3855E000, 0x38560000, 0x38562000, 0x38564000, 0x38566000, + 0x38568000, 0x3856A000, 0x3856C000, 0x3856E000, 0x38570000, 0x38572000, 0x38574000, 0x38576000, 0x38578000, + 0x3857A000, 0x3857C000, 0x3857E000, 0x38580000, 0x38582000, 0x38584000, 0x38586000, 0x38588000, 0x3858A000, + 0x3858C000, 0x3858E000, 0x38590000, 0x38592000, 0x38594000, 0x38596000, 0x38598000, 0x3859A000, 0x3859C000, + 0x3859E000, 0x385A0000, 0x385A2000, 0x385A4000, 0x385A6000, 0x385A8000, 0x385AA000, 0x385AC000, 0x385AE000, + 0x385B0000, 0x385B2000, 0x385B4000, 0x385B6000, 0x385B8000, 0x385BA000, 0x385BC000, 0x385BE000, 0x385C0000, + 0x385C2000, 0x385C4000, 0x385C6000, 0x385C8000, 0x385CA000, 0x385CC000, 0x385CE000, 0x385D0000, 0x385D2000, + 0x385D4000, 0x385D6000, 0x385D8000, 0x385DA000, 0x385DC000, 0x385DE000, 0x385E0000, 0x385E2000, 0x385E4000, + 0x385E6000, 0x385E8000, 0x385EA000, 0x385EC000, 0x385EE000, 0x385F0000, 0x385F2000, 0x385F4000, 0x385F6000, + 0x385F8000, 0x385FA000, 0x385FC000, 0x385FE000, 0x38600000, 0x38602000, 0x38604000, 0x38606000, 0x38608000, + 0x3860A000, 0x3860C000, 0x3860E000, 0x38610000, 0x38612000, 0x38614000, 0x38616000, 0x38618000, 0x3861A000, + 0x3861C000, 0x3861E000, 0x38620000, 0x38622000, 0x38624000, 0x38626000, 0x38628000, 0x3862A000, 0x3862C000, + 0x3862E000, 0x38630000, 0x38632000, 0x38634000, 0x38636000, 0x38638000, 0x3863A000, 0x3863C000, 0x3863E000, + 0x38640000, 0x38642000, 0x38644000, 0x38646000, 0x38648000, 0x3864A000, 0x3864C000, 0x3864E000, 0x38650000, + 0x38652000, 0x38654000, 0x38656000, 0x38658000, 0x3865A000, 0x3865C000, 0x3865E000, 0x38660000, 0x38662000, + 0x38664000, 0x38666000, 0x38668000, 0x3866A000, 0x3866C000, 0x3866E000, 0x38670000, 0x38672000, 0x38674000, + 0x38676000, 0x38678000, 0x3867A000, 0x3867C000, 0x3867E000, 0x38680000, 0x38682000, 0x38684000, 0x38686000, + 0x38688000, 0x3868A000, 0x3868C000, 0x3868E000, 0x38690000, 0x38692000, 0x38694000, 0x38696000, 0x38698000, + 0x3869A000, 0x3869C000, 0x3869E000, 0x386A0000, 0x386A2000, 0x386A4000, 0x386A6000, 0x386A8000, 0x386AA000, + 0x386AC000, 0x386AE000, 0x386B0000, 0x386B2000, 0x386B4000, 0x386B6000, 0x386B8000, 0x386BA000, 0x386BC000, + 0x386BE000, 0x386C0000, 0x386C2000, 0x386C4000, 0x386C6000, 0x386C8000, 0x386CA000, 0x386CC000, 0x386CE000, + 0x386D0000, 0x386D2000, 0x386D4000, 0x386D6000, 0x386D8000, 0x386DA000, 0x386DC000, 0x386DE000, 0x386E0000, + 0x386E2000, 0x386E4000, 0x386E6000, 0x386E8000, 0x386EA000, 0x386EC000, 0x386EE000, 0x386F0000, 0x386F2000, + 0x386F4000, 0x386F6000, 0x386F8000, 0x386FA000, 0x386FC000, 0x386FE000, 0x38700000, 0x38702000, 0x38704000, + 0x38706000, 0x38708000, 0x3870A000, 0x3870C000, 0x3870E000, 0x38710000, 0x38712000, 0x38714000, 0x38716000, + 0x38718000, 0x3871A000, 0x3871C000, 0x3871E000, 0x38720000, 0x38722000, 0x38724000, 0x38726000, 0x38728000, + 0x3872A000, 0x3872C000, 0x3872E000, 0x38730000, 0x38732000, 0x38734000, 0x38736000, 0x38738000, 0x3873A000, + 0x3873C000, 0x3873E000, 0x38740000, 0x38742000, 0x38744000, 0x38746000, 0x38748000, 0x3874A000, 0x3874C000, + 0x3874E000, 0x38750000, 0x38752000, 0x38754000, 0x38756000, 0x38758000, 0x3875A000, 0x3875C000, 0x3875E000, + 0x38760000, 0x38762000, 0x38764000, 0x38766000, 0x38768000, 0x3876A000, 0x3876C000, 0x3876E000, 0x38770000, + 0x38772000, 0x38774000, 0x38776000, 0x38778000, 0x3877A000, 0x3877C000, 0x3877E000, 0x38780000, 0x38782000, + 0x38784000, 0x38786000, 0x38788000, 0x3878A000, 0x3878C000, 0x3878E000, 0x38790000, 0x38792000, 0x38794000, + 0x38796000, 0x38798000, 0x3879A000, 0x3879C000, 0x3879E000, 0x387A0000, 0x387A2000, 0x387A4000, 0x387A6000, + 0x387A8000, 0x387AA000, 0x387AC000, 0x387AE000, 0x387B0000, 0x387B2000, 0x387B4000, 0x387B6000, 0x387B8000, + 0x387BA000, 0x387BC000, 0x387BE000, 0x387C0000, 0x387C2000, 0x387C4000, 0x387C6000, 0x387C8000, 0x387CA000, + 0x387CC000, 0x387CE000, 0x387D0000, 0x387D2000, 0x387D4000, 0x387D6000, 0x387D8000, 0x387DA000, 0x387DC000, + 0x387DE000, 0x387E0000, 0x387E2000, 0x387E4000, 0x387E6000, 0x387E8000, 0x387EA000, 0x387EC000, 0x387EE000, + 0x387F0000, 0x387F2000, 0x387F4000, 0x387F6000, 0x387F8000, 0x387FA000, 0x387FC000, 0x387FE000}; + static const uint32 exponent_table[64] = {0x00000000, 0x00800000, 0x01000000, 0x01800000, 0x02000000, 0x02800000, + 0x03000000, 0x03800000, 0x04000000, 0x04800000, 0x05000000, 0x05800000, 0x06000000, 0x06800000, 0x07000000, + 0x07800000, 0x08000000, 0x08800000, 0x09000000, 0x09800000, 0x0A000000, 0x0A800000, 0x0B000000, 0x0B800000, + 0x0C000000, 0x0C800000, 0x0D000000, 0x0D800000, 0x0E000000, 0x0E800000, 0x0F000000, 0x47800000, 0x80000000, + 0x80800000, 0x81000000, 0x81800000, 0x82000000, 0x82800000, 0x83000000, 0x83800000, 0x84000000, 0x84800000, + 0x85000000, 0x85800000, 0x86000000, 0x86800000, 0x87000000, 0x87800000, 0x88000000, 0x88800000, 0x89000000, + 0x89800000, 0x8A000000, 0x8A800000, 0x8B000000, 0x8B800000, 0x8C000000, 0x8C800000, 0x8D000000, 0x8D800000, + 0x8E000000, 0x8E800000, 0x8F000000, 0xC7800000}; + static const unsigned short offset_table[64] = {0, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, + 1024, 1024, 0, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, + 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024}; + uint32 bits = mantissa_table[offset_table[value >> 10] + (value & 0x3FF)] + exponent_table[value >> 10]; + // return *reinterpret_cast(&bits); //violating strict aliasing! + float out; + std::memcpy(&out, &bits, sizeof(float)); + return out; +} + +/// Convert half-precision to IEEE double-precision. +/// \param value binary representation of half-precision value +/// \return double-precision value +inline double half2float_impl(uint16 value, double, true_type) +{ + typedef bits::type uint32; + typedef bits::type uint64; + uint32 hi = static_cast(value & 0x8000) << 16; + int abs = value & 0x7FFF; + if (abs) + { + hi |= 0x3F000000 << static_cast(abs >= 0x7C00); + for (; abs < 0x400; abs <<= 1, hi -= 0x100000) + ; + hi += static_cast(abs) << 10; + } + uint64 bits = static_cast(hi) << 32; + // return *reinterpret_cast(&bits); //violating strict aliasing! + double out; + std::memcpy(&out, &bits, sizeof(double)); + return out; +} + +/// Convert half-precision to non-IEEE floating point. +/// \tparam T type to convert to (builtin integer type) +/// \param value binary representation of half-precision value +/// \return floating point value +template +T half2float_impl(uint16 value, T, ...) +{ + T out; + int abs = value & 0x7FFF; + if (abs > 0x7C00) + out = std::numeric_limits::has_quiet_NaN ? std::numeric_limits::quiet_NaN() : T(); + else if (abs == 0x7C00) + out = std::numeric_limits::has_infinity ? std::numeric_limits::infinity() : std::numeric_limits::max(); + else if (abs > 0x3FF) + out = std::ldexp(static_cast((abs & 0x3FF) | 0x400), (abs >> 10) - 25); + else + out = std::ldexp(static_cast(abs), -24); + return (value & 0x8000) ? -out : out; +} + +/// Convert half-precision to floating point. +/// \tparam T type to convert to (builtin integer type) +/// \param value binary representation of half-precision value +/// \return floating point value +template +T half2float(uint16 value) +{ + return half2float_impl( + value, T(), bool_type < std::numeric_limits::is_iec559 && sizeof(typename bits::type) == sizeof(T) > ()); +} + +/// Convert half-precision floating point to integer. +/// \tparam R rounding mode to use, `std::round_indeterminate` for fastest rounding +/// \tparam E `true` for round to even, `false` for round away from zero +/// \tparam T type to convert to (buitlin integer type with at least 16 bits precision, excluding any implicit sign +/// bits) \param value binary representation of half-precision value \return integral value +template +T half2int_impl(uint16 value) +{ +#if HALF_ENABLE_CPP11_STATIC_ASSERT && HALF_ENABLE_CPP11_TYPE_TRAITS + static_assert(std::is_integral::value, "half to int conversion only supports builtin integer types"); +#endif + uint32_t e = value & 0x7FFF; + if (e >= 0x7C00) + return (value & 0x8000) ? std::numeric_limits::min() : std::numeric_limits::max(); + if (e < 0x3800) + { + if (R == std::round_toward_infinity) + return T(~(value >> 15) & (e != 0)); + else if (R == std::round_toward_neg_infinity) + return -T(value > 0x8000); + return T(); + } + uint32_t m = (value & 0x3FF) | 0x400; + e >>= 10; + if (e < 25) + { + if (R == std::round_to_nearest) + m += (1 << (24 - e)) - (~(m >> (25 - e)) & E); + else if (R == std::round_toward_infinity) + m += ((value >> 15) - 1) & ((1 << (25 - e)) - 1U); + else if (R == std::round_toward_neg_infinity) + m += -(value >> 15) & ((1 << (25 - e)) - 1U); + m >>= 25 - e; + } + else + m <<= e - 25; + return (value & 0x8000) ? -static_cast(m) : static_cast(m); +} + +/// Convert half-precision floating point to integer. +/// \tparam R rounding mode to use, `std::round_indeterminate` for fastest rounding +/// \tparam T type to convert to (buitlin integer type with at least 16 bits precision, excluding any implicit sign +/// bits) \param value binary representation of half-precision value \return integral value +template +T half2int(uint16 value) +{ + return half2int_impl(value); +} + +/// Convert half-precision floating point to integer using round-to-nearest-away-from-zero. +/// \tparam T type to convert to (buitlin integer type with at least 16 bits precision, excluding any implicit sign +/// bits) \param value binary representation of half-precision value \return integral value +template +T half2int_up(uint16 value) +{ + return half2int_impl(value); +} + +/// Round half-precision number to nearest integer value. +/// \tparam R rounding mode to use, `std::round_indeterminate` for fastest rounding +/// \tparam E `true` for round to even, `false` for round away from zero +/// \param value binary representation of half-precision value +/// \return half-precision bits for nearest integral value +template +uint16 round_half_impl(uint16 value) +{ + uint32_t e = value & 0x7FFF; + uint16 result = value; + if (e < 0x3C00) + { + result &= 0x8000; + if (R == std::round_to_nearest) + result |= 0x3C00U & -(e >= (0x3800 + E)); + else if (R == std::round_toward_infinity) + result |= 0x3C00U & -(~(value >> 15) & (e != 0)); + else if (R == std::round_toward_neg_infinity) + result |= 0x3C00U & -(value > 0x8000); + } + else if (e < 0x6400) + { + e = 25 - (e >> 10); + uint32_t mask = (1 << e) - 1; + if (R == std::round_to_nearest) + result += (1 << (e - 1)) - (~(result >> e) & E); + else if (R == std::round_toward_infinity) + result += mask & ((value >> 15) - 1); + else if (R == std::round_toward_neg_infinity) + result += mask & -(value >> 15); + result &= ~mask; + } + return result; +} + +/// Round half-precision number to nearest integer value. +/// \tparam R rounding mode to use, `std::round_indeterminate` for fastest rounding +/// \param value binary representation of half-precision value +/// \return half-precision bits for nearest integral value +template +uint16 round_half(uint16 value) +{ + return round_half_impl(value); +} + +/// Round half-precision number to nearest integer value using round-to-nearest-away-from-zero. +/// \param value binary representation of half-precision value +/// \return half-precision bits for nearest integral value +inline uint16 round_half_up(uint16 value) +{ + return round_half_impl(value); +} +/// \} + +struct functions; +template +struct unary_specialized; +template +struct binary_specialized; +template +struct half_caster; +} // namespace detail + +/// Half-precision floating point type. +/// This class implements an IEEE-conformant half-precision floating point type with the usual arithmetic operators and +/// conversions. It is implicitly convertible to single-precision floating point, which makes artihmetic expressions and +/// functions with mixed-type operands to be of the most precise operand type. Additionally all arithmetic operations +/// (and many mathematical functions) are carried out in single-precision internally. All conversions from single- to +/// half-precision are done using the library's default rounding mode, but temporary results inside chained arithmetic +/// expressions are kept in single-precision as long as possible (while of course still maintaining a strong +/// half-precision type). +/// +/// According to the C++98/03 definition, the half type is not a POD type. But according to C++11's less strict and +/// extended definitions it is both a standard layout type and a trivially copyable type (even if not a POD type), which +/// means it can be standard-conformantly copied using raw binary copies. But in this context some more words about the +/// actual size of the type. Although the half is representing an IEEE 16-bit type, it does not neccessarily have to be +/// of exactly 16-bits size. But on any reasonable implementation the actual binary representation of this type will +/// most probably not ivolve any additional "magic" or padding beyond the simple binary representation of the underlying +/// 16-bit IEEE number, even if not strictly guaranteed by the standard. But even then it only has an actual size of 16 +/// bits if your C++ implementation supports an unsigned integer type of exactly 16 bits width. But this should be the +/// case on nearly any reasonable platform. +/// +/// So if your C++ implementation is not totally exotic or imposes special alignment requirements, it is a reasonable +/// assumption that the data of a half is just comprised of the 2 bytes of the underlying IEEE representation. +class half +{ + friend struct detail::functions; + friend struct detail::unary_specialized; + friend struct detail::binary_specialized; + template + friend struct detail::half_caster; + friend class std::numeric_limits; +#if HALF_ENABLE_CPP11_HASH + friend struct std::hash; +#endif +#if HALF_ENABLE_CPP11_USER_LITERALS + friend half literal::operator"" _h(long double); +#endif + +public: + /// Default constructor. + /// This initializes the half to 0. Although this does not match the builtin types' default-initialization semantics + /// and may be less efficient than no initialization, it is needed to provide proper value-initialization semantics. + HALF_CONSTEXPR half() HALF_NOEXCEPT : data_() {} + + /// Copy constructor. + /// \tparam T type of concrete half expression + /// \param rhs half expression to copy from + half(detail::expr rhs) + : data_(detail::float2half(static_cast(rhs))) + { + } + + /// Conversion constructor. + /// \param rhs float to convert + explicit half(float rhs) + : data_(detail::float2half(rhs)) + { + } + + /// Conversion to single-precision. + /// \return single precision value representing expression value + operator float() const + { + return detail::half2float(data_); + } + + /// Assignment operator. + /// \tparam T type of concrete half expression + /// \param rhs half expression to copy from + /// \return reference to this half + half& operator=(detail::expr rhs) + { + return *this = static_cast(rhs); + } + + /// Arithmetic assignment. + /// \tparam T type of concrete half expression + /// \param rhs half expression to add + /// \return reference to this half + template + typename detail::enable::type operator+=(T rhs) + { + return *this += static_cast(rhs); + } + + /// Arithmetic assignment. + /// \tparam T type of concrete half expression + /// \param rhs half expression to subtract + /// \return reference to this half + template + typename detail::enable::type operator-=(T rhs) + { + return *this -= static_cast(rhs); + } + + /// Arithmetic assignment. + /// \tparam T type of concrete half expression + /// \param rhs half expression to multiply with + /// \return reference to this half + template + typename detail::enable::type operator*=(T rhs) + { + return *this *= static_cast(rhs); + } + + /// Arithmetic assignment. + /// \tparam T type of concrete half expression + /// \param rhs half expression to divide by + /// \return reference to this half + template + typename detail::enable::type operator/=(T rhs) + { + return *this /= static_cast(rhs); + } + + /// Assignment operator. + /// \param rhs single-precision value to copy from + /// \return reference to this half + half& operator=(float rhs) + { + data_ = detail::float2half(rhs); + return *this; + } + + /// Arithmetic assignment. + /// \param rhs single-precision value to add + /// \return reference to this half + half& operator+=(float rhs) + { + data_ = detail::float2half(detail::half2float(data_) + rhs); + return *this; + } + + /// Arithmetic assignment. + /// \param rhs single-precision value to subtract + /// \return reference to this half + half& operator-=(float rhs) + { + data_ = detail::float2half(detail::half2float(data_) - rhs); + return *this; + } + + /// Arithmetic assignment. + /// \param rhs single-precision value to multiply with + /// \return reference to this half + half& operator*=(float rhs) + { + data_ = detail::float2half(detail::half2float(data_) * rhs); + return *this; + } + + /// Arithmetic assignment. + /// \param rhs single-precision value to divide by + /// \return reference to this half + half& operator/=(float rhs) + { + data_ = detail::float2half(detail::half2float(data_) / rhs); + return *this; + } + + /// Prefix increment. + /// \return incremented half value + half& operator++() + { + return *this += 1.0F; + } + + /// Prefix decrement. + /// \return decremented half value + half& operator--() + { + return *this -= 1.0F; + } + + /// Postfix increment. + /// \return non-incremented half value + half operator++(int) + { + half out(*this); + ++*this; + return out; + } + + /// Postfix decrement. + /// \return non-decremented half value + half operator--(int) + { + half out(*this); + --*this; + return out; + } + +private: + /// Rounding mode to use + static const std::float_round_style round_style = (std::float_round_style)(HALF_ROUND_STYLE); + + /// Constructor. + /// \param bits binary representation to set half to + HALF_CONSTEXPR half(detail::binary_t, detail::uint16 bits) HALF_NOEXCEPT : data_(bits) {} + + /// Internal binary representation + detail::uint16 data_; +}; + +#if HALF_ENABLE_CPP11_USER_LITERALS +namespace literal +{ +/// Half literal. +/// While this returns an actual half-precision value, half literals can unfortunately not be constant expressions due +/// to rather involved conversions. +/// \param value literal value +/// \return half with given value (if representable) +inline half operator"" _h(long double value) +{ + return half(detail::binary, detail::float2half(value)); +} +} // namespace literal +#endif + +namespace detail +{ +/// Wrapper implementing unspecialized half-precision functions. +struct functions +{ + /// Addition implementation. + /// \param x first operand + /// \param y second operand + /// \return Half-precision sum stored in single-precision + static expr plus(float x, float y) + { + return expr(x + y); + } + + /// Subtraction implementation. + /// \param x first operand + /// \param y second operand + /// \return Half-precision difference stored in single-precision + static expr minus(float x, float y) + { + return expr(x - y); + } + + /// Multiplication implementation. + /// \param x first operand + /// \param y second operand + /// \return Half-precision product stored in single-precision + static expr multiplies(float x, float y) + { + return expr(x * y); + } + + /// Division implementation. + /// \param x first operand + /// \param y second operand + /// \return Half-precision quotient stored in single-precision + static expr divides(float x, float y) + { + return expr(x / y); + } + + /// Output implementation. + /// \param out stream to write to + /// \param arg value to write + /// \return reference to stream + template + static std::basic_ostream& write(std::basic_ostream& out, float arg) + { + return out << arg; + } + + /// Input implementation. + /// \param in stream to read from + /// \param arg half to read into + /// \return reference to stream + template + static std::basic_istream& read(std::basic_istream& in, half& arg) + { + float f; + if (in >> f) + arg = f; + return in; + } + + /// Modulo implementation. + /// \param x first operand + /// \param y second operand + /// \return Half-precision division remainder stored in single-precision + static expr fmod(float x, float y) + { + return expr(std::fmod(x, y)); + } + + /// Remainder implementation. + /// \param x first operand + /// \param y second operand + /// \return Half-precision division remainder stored in single-precision + static expr remainder(float x, float y) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::remainder(x, y)); +#else + if (builtin_isnan(x) || builtin_isnan(y)) + return expr(std::numeric_limits::quiet_NaN()); + float ax = std::fabs(x), ay = std::fabs(y); + if (ax >= 65536.0f || ay < std::ldexp(1.0f, -24)) + return expr(std::numeric_limits::quiet_NaN()); + if (ay >= 65536.0f) + return expr(x); + if (ax == ay) + return expr(builtin_signbit(x) ? -0.0f : 0.0f); + ax = std::fmod(ax, ay + ay); + float y2 = 0.5f * ay; + if (ax > y2) + { + ax -= ay; + if (ax >= y2) + ax -= ay; + } + return expr(builtin_signbit(x) ? -ax : ax); +#endif + } + + /// Remainder implementation. + /// \param x first operand + /// \param y second operand + /// \param quo address to store quotient bits at + /// \return Half-precision division remainder stored in single-precision + static expr remquo(float x, float y, int* quo) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::remquo(x, y, quo)); +#else + if (builtin_isnan(x) || builtin_isnan(y)) + return expr(std::numeric_limits::quiet_NaN()); + bool sign = builtin_signbit(x), qsign = static_cast(sign ^ builtin_signbit(y)); + float ax = std::fabs(x), ay = std::fabs(y); + if (ax >= 65536.0f || ay < std::ldexp(1.0f, -24)) + return expr(std::numeric_limits::quiet_NaN()); + if (ay >= 65536.0f) + return expr(x); + if (ax == ay) + return *quo = qsign ? -1 : 1, expr(sign ? -0.0f : 0.0f); + ax = std::fmod(ax, 8.0f * ay); + int cquo = 0; + if (ax >= 4.0f * ay) + { + ax -= 4.0f * ay; + cquo += 4; + } + if (ax >= 2.0f * ay) + { + ax -= 2.0f * ay; + cquo += 2; + } + float y2 = 0.5f * ay; + if (ax > y2) + { + ax -= ay; + ++cquo; + if (ax >= y2) + { + ax -= ay; + ++cquo; + } + } + return *quo = qsign ? -cquo : cquo, expr(sign ? -ax : ax); +#endif + } + + /// Positive difference implementation. + /// \param x first operand + /// \param y second operand + /// \return Positive difference stored in single-precision + static expr fdim(float x, float y) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::fdim(x, y)); +#else + return expr((x <= y) ? 0.0f : (x - y)); +#endif + } + + /// Fused multiply-add implementation. + /// \param x first operand + /// \param y second operand + /// \param z third operand + /// \return \a x * \a y + \a z stored in single-precision + static expr fma(float x, float y, float z) + { +#if HALF_ENABLE_CPP11_CMATH && defined(FP_FAST_FMAF) + return expr(std::fma(x, y, z)); +#else + return expr(x * y + z); +#endif + } + + /// Get NaN. + /// \return Half-precision quiet NaN + static half nanh() + { + return half(binary, 0x7FFF); + } + + /// Exponential implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr exp(float arg) + { + return expr(std::exp(arg)); + } + + /// Exponential implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr expm1(float arg) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::expm1(arg)); +#else + return expr(static_cast(std::exp(static_cast(arg)) - 1.0)); +#endif + } + + /// Binary exponential implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr exp2(float arg) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::exp2(arg)); +#else + return expr(static_cast(std::exp(arg * 0.69314718055994530941723212145818))); +#endif + } + + /// Logarithm implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr log(float arg) + { + return expr(std::log(arg)); + } + + /// Common logarithm implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr log10(float arg) + { + return expr(std::log10(arg)); + } + + /// Logarithm implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr log1p(float arg) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::log1p(arg)); +#else + return expr(static_cast(std::log(1.0 + arg))); +#endif + } + + /// Binary logarithm implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr log2(float arg) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::log2(arg)); +#else + return expr(static_cast(std::log(static_cast(arg)) * 1.4426950408889634073599246810019)); +#endif + } + + /// Square root implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr sqrt(float arg) + { + return expr(std::sqrt(arg)); + } + + /// Cubic root implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr cbrt(float arg) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::cbrt(arg)); +#else + if (builtin_isnan(arg) || builtin_isinf(arg)) + return expr(arg); + return expr(builtin_signbit(arg) ? -static_cast(std::pow(-static_cast(arg), 1.0 / 3.0)) + : static_cast(std::pow(static_cast(arg), 1.0 / 3.0))); +#endif + } + + /// Hypotenuse implementation. + /// \param x first argument + /// \param y second argument + /// \return function value stored in single-preicision + static expr hypot(float x, float y) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::hypot(x, y)); +#else + return expr((builtin_isinf(x) || builtin_isinf(y)) + ? std::numeric_limits::infinity() + : static_cast(std::sqrt(static_cast(x) * x + static_cast(y) * y))); +#endif + } + + /// Power implementation. + /// \param base value to exponentiate + /// \param exp power to expontiate to + /// \return function value stored in single-preicision + static expr pow(float base, float exp) + { + return expr(std::pow(base, exp)); + } + + /// Sine implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr sin(float arg) + { + return expr(std::sin(arg)); + } + + /// Cosine implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr cos(float arg) + { + return expr(std::cos(arg)); + } + + /// Tan implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr tan(float arg) + { + return expr(std::tan(arg)); + } + + /// Arc sine implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr asin(float arg) + { + return expr(std::asin(arg)); + } + + /// Arc cosine implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr acos(float arg) + { + return expr(std::acos(arg)); + } + + /// Arc tangent implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr atan(float arg) + { + return expr(std::atan(arg)); + } + + /// Arc tangent implementation. + /// \param x first argument + /// \param y second argument + /// \return function value stored in single-preicision + static expr atan2(float x, float y) + { + return expr(std::atan2(x, y)); + } + + /// Hyperbolic sine implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr sinh(float arg) + { + return expr(std::sinh(arg)); + } + + /// Hyperbolic cosine implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr cosh(float arg) + { + return expr(std::cosh(arg)); + } + + /// Hyperbolic tangent implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr tanh(float arg) + { + return expr(std::tanh(arg)); + } + + /// Hyperbolic area sine implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr asinh(float arg) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::asinh(arg)); +#else + return expr((arg == -std::numeric_limits::infinity()) + ? arg + : static_cast(std::log(arg + std::sqrt(arg * arg + 1.0)))); +#endif + } + + /// Hyperbolic area cosine implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr acosh(float arg) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::acosh(arg)); +#else + return expr((arg < -1.0f) ? std::numeric_limits::quiet_NaN() + : static_cast(std::log(arg + std::sqrt(arg * arg - 1.0)))); +#endif + } + + /// Hyperbolic area tangent implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr atanh(float arg) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::atanh(arg)); +#else + return expr(static_cast(0.5 * std::log((1.0 + arg) / (1.0 - arg)))); +#endif + } + + /// Error function implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr erf(float arg) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::erf(arg)); +#else + return expr(static_cast(erf(static_cast(arg)))); +#endif + } + + /// Complementary implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr erfc(float arg) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::erfc(arg)); +#else + return expr(static_cast(1.0 - erf(static_cast(arg)))); +#endif + } + + /// Gamma logarithm implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr lgamma(float arg) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::lgamma(arg)); +#else + if (builtin_isinf(arg)) + return expr(std::numeric_limits::infinity()); + if (arg < 0.0f) + { + float i, f = std::modf(-arg, &i); + if (f == 0.0f) + return expr(std::numeric_limits::infinity()); + return expr(static_cast(1.1447298858494001741434273513531 + - std::log(std::abs(std::sin(3.1415926535897932384626433832795 * f))) - lgamma(1.0 - arg))); + } + return expr(static_cast(lgamma(static_cast(arg)))); +#endif + } + + /// Gamma implementation. + /// \param arg function argument + /// \return function value stored in single-preicision + static expr tgamma(float arg) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::tgamma(arg)); +#else + if (arg == 0.0f) + return builtin_signbit(arg) ? expr(-std::numeric_limits::infinity()) + : expr(std::numeric_limits::infinity()); + if (arg < 0.0f) + { + float i, f = std::modf(-arg, &i); + if (f == 0.0f) + return expr(std::numeric_limits::quiet_NaN()); + double value = 3.1415926535897932384626433832795 + / (std::sin(3.1415926535897932384626433832795 * f) * std::exp(lgamma(1.0 - arg))); + return expr(static_cast((std::fmod(i, 2.0f) == 0.0f) ? -value : value)); + } + if (builtin_isinf(arg)) + return expr(arg); + return expr(static_cast(std::exp(lgamma(static_cast(arg))))); +#endif + } + + /// Floor implementation. + /// \param arg value to round + /// \return rounded value + static half floor(half arg) + { + return half(binary, round_half(arg.data_)); + } + + /// Ceiling implementation. + /// \param arg value to round + /// \return rounded value + static half ceil(half arg) + { + return half(binary, round_half(arg.data_)); + } + + /// Truncation implementation. + /// \param arg value to round + /// \return rounded value + static half trunc(half arg) + { + return half(binary, round_half(arg.data_)); + } + + /// Nearest integer implementation. + /// \param arg value to round + /// \return rounded value + static half round(half arg) + { + return half(binary, round_half_up(arg.data_)); + } + + /// Nearest integer implementation. + /// \param arg value to round + /// \return rounded value + static long lround(half arg) + { + return detail::half2int_up(arg.data_); + } + + /// Nearest integer implementation. + /// \param arg value to round + /// \return rounded value + static half rint(half arg) + { + return half(binary, round_half(arg.data_)); + } + + /// Nearest integer implementation. + /// \param arg value to round + /// \return rounded value + static long lrint(half arg) + { + return detail::half2int(arg.data_); + } + +#if HALF_ENABLE_CPP11_LONG_LONG + /// Nearest integer implementation. + /// \param arg value to round + /// \return rounded value + static long long llround(half arg) + { + return detail::half2int_up(arg.data_); + } + + /// Nearest integer implementation. + /// \param arg value to round + /// \return rounded value + static long long llrint(half arg) + { + return detail::half2int(arg.data_); + } +#endif + + /// Decompression implementation. + /// \param arg number to decompress + /// \param exp address to store exponent at + /// \return normalized significant + static half frexp(half arg, int* exp) + { + int m = arg.data_ & 0x7FFF, e = -14; + if (m >= 0x7C00 || !m) + return *exp = 0, arg; + for (; m < 0x400; m <<= 1, --e) + ; + return *exp = e + (m >> 10), half(binary, (arg.data_ & 0x8000) | 0x3800 | (m & 0x3FF)); + } + + /// Decompression implementation. + /// \param arg number to decompress + /// \param iptr address to store integer part at + /// \return fractional part + static half modf(half arg, half* iptr) + { + uint32_t e = arg.data_ & 0x7FFF; + if (e >= 0x6400) + return *iptr = arg, half(binary, arg.data_ & (0x8000U | -(e > 0x7C00))); + if (e < 0x3C00) + return iptr->data_ = arg.data_ & 0x8000, arg; + e >>= 10; + uint32_t mask = (1 << (25 - e)) - 1, m = arg.data_ & mask; + iptr->data_ = arg.data_ & ~mask; + if (!m) + return half(binary, arg.data_ & 0x8000); + for (; m < 0x400; m <<= 1, --e) + ; + return half(binary, static_cast((arg.data_ & 0x8000) | (e << 10) | (m & 0x3FF))); + } + + /// Scaling implementation. + /// \param arg number to scale + /// \param exp power of two to scale by + /// \return scaled number + static half scalbln(half arg, long exp) + { + uint32_t m = arg.data_ & 0x7FFF; + if (m >= 0x7C00 || !m) + return arg; + for (; m < 0x400; m <<= 1, --exp) + ; + exp += m >> 10; + uint16 value = arg.data_ & 0x8000; + if (exp > 30) + { + if (half::round_style == std::round_toward_zero) + value |= 0x7BFF; + else if (half::round_style == std::round_toward_infinity) + value |= 0x7C00 - (value >> 15); + else if (half::round_style == std::round_toward_neg_infinity) + value |= 0x7BFF + (value >> 15); + else + value |= 0x7C00; + } + else if (exp > 0) + value |= (exp << 10) | (m & 0x3FF); + else if (exp > -11) + { + m = (m & 0x3FF) | 0x400; + if (half::round_style == std::round_to_nearest) + { + m += 1 << -exp; +#if HALF_ROUND_TIES_TO_EVEN + m -= (m >> (1 - exp)) & 1; +#endif + } + else if (half::round_style == std::round_toward_infinity) + m += ((value >> 15) - 1) & ((1 << (1 - exp)) - 1U); + else if (half::round_style == std::round_toward_neg_infinity) + m += -(value >> 15) & ((1 << (1 - exp)) - 1U); + value |= m >> (1 - exp); + } + else if (half::round_style == std::round_toward_infinity) + value -= (value >> 15) - 1; + else if (half::round_style == std::round_toward_neg_infinity) + value += value >> 15; + return half(binary, value); + } + + /// Exponent implementation. + /// \param arg number to query + /// \return floating point exponent + static int ilogb(half arg) + { + int abs = arg.data_ & 0x7FFF; + if (!abs) + return FP_ILOGB0; + if (abs < 0x7C00) + { + int exp = (abs >> 10) - 15; + if (abs < 0x400) + for (; abs < 0x200; abs <<= 1, --exp) + ; + return exp; + } + if (abs > 0x7C00) + return FP_ILOGBNAN; + return INT_MAX; + } + + /// Exponent implementation. + /// \param arg number to query + /// \return floating point exponent + static half logb(half arg) + { + int abs = arg.data_ & 0x7FFF; + if (!abs) + return half(binary, 0xFC00); + if (abs < 0x7C00) + { + int exp = (abs >> 10) - 15; + if (abs < 0x400) + for (; abs < 0x200; abs <<= 1, --exp) + ; + uint16 bits = (exp < 0) << 15; + if (exp) + { + uint32_t m = std::abs(exp) << 6, e = 18; + for (; m < 0x400; m <<= 1, --e) + ; + bits |= (e << 10) + m; + } + return half(binary, bits); + } + if (abs > 0x7C00) + return arg; + return half(binary, 0x7C00); + } + + /// Enumeration implementation. + /// \param from number to increase/decrease + /// \param to direction to enumerate into + /// \return next representable number + static half nextafter(half from, half to) + { + uint16 fabs = from.data_ & 0x7FFF, tabs = to.data_ & 0x7FFF; + if (fabs > 0x7C00) + return from; + if (tabs > 0x7C00 || from.data_ == to.data_ || !(fabs | tabs)) + return to; + if (!fabs) + return half(binary, (to.data_ & 0x8000) + 1); + bool lt = ((fabs == from.data_) ? static_cast(fabs) : -static_cast(fabs)) + < ((tabs == to.data_) ? static_cast(tabs) : -static_cast(tabs)); + return half(binary, from.data_ + (((from.data_ >> 15) ^ static_cast(lt)) << 1) - 1); + } + + /// Enumeration implementation. + /// \param from number to increase/decrease + /// \param to direction to enumerate into + /// \return next representable number + static half nexttoward(half from, long double to) + { + if (isnan(from)) + return from; + long double lfrom = static_cast(from); + if (builtin_isnan(to) || lfrom == to) + return half(static_cast(to)); + if (!(from.data_ & 0x7FFF)) + return half(binary, (static_cast(builtin_signbit(to)) << 15) + 1); + return half(binary, from.data_ + (((from.data_ >> 15) ^ static_cast(lfrom < to)) << 1) - 1); + } + + /// Sign implementation + /// \param x first operand + /// \param y second operand + /// \return composed value + static half copysign(half x, half y) + { + return half(binary, x.data_ ^ ((x.data_ ^ y.data_) & 0x8000)); + } + + /// Classification implementation. + /// \param arg value to classify + /// \retval true if infinite number + /// \retval false else + static int fpclassify(half arg) + { + uint32_t abs = arg.data_ & 0x7FFF; + return abs + ? ((abs > 0x3FF) ? ((abs >= 0x7C00) ? ((abs > 0x7C00) ? FP_NAN : FP_INFINITE) : FP_NORMAL) : FP_SUBNORMAL) + : FP_ZERO; + } + + /// Classification implementation. + /// \param arg value to classify + /// \retval true if finite number + /// \retval false else + static bool isfinite(half arg) + { + return (arg.data_ & 0x7C00) != 0x7C00; + } + + /// Classification implementation. + /// \param arg value to classify + /// \retval true if infinite number + /// \retval false else + static bool isinf(half arg) + { + return (arg.data_ & 0x7FFF) == 0x7C00; + } + + /// Classification implementation. + /// \param arg value to classify + /// \retval true if not a number + /// \retval false else + static bool isnan(half arg) + { + return (arg.data_ & 0x7FFF) > 0x7C00; + } + + /// Classification implementation. + /// \param arg value to classify + /// \retval true if normal number + /// \retval false else + static bool isnormal(half arg) + { + return ((arg.data_ & 0x7C00) != 0) & ((arg.data_ & 0x7C00) != 0x7C00); + } + + /// Sign bit implementation. + /// \param arg value to check + /// \retval true if signed + /// \retval false if unsigned + static bool signbit(half arg) + { + return (arg.data_ & 0x8000) != 0; + } + + /// Comparison implementation. + /// \param x first operand + /// \param y second operand + /// \retval true if operands equal + /// \retval false else + static bool isequal(half x, half y) + { + return (x.data_ == y.data_ || !((x.data_ | y.data_) & 0x7FFF)) && !isnan(x); + } + + /// Comparison implementation. + /// \param x first operand + /// \param y second operand + /// \retval true if operands not equal + /// \retval false else + static bool isnotequal(half x, half y) + { + return (x.data_ != y.data_ && ((x.data_ | y.data_) & 0x7FFF)) || isnan(x); + } + + /// Comparison implementation. + /// \param x first operand + /// \param y second operand + /// \retval true if \a x > \a y + /// \retval false else + static bool isgreater(half x, half y) + { + int xabs = x.data_ & 0x7FFF, yabs = y.data_ & 0x7FFF; + return xabs <= 0x7C00 && yabs <= 0x7C00 + && (((xabs == x.data_) ? xabs : -xabs) > ((yabs == y.data_) ? yabs : -yabs)); + } + + /// Comparison implementation. + /// \param x first operand + /// \param y second operand + /// \retval true if \a x >= \a y + /// \retval false else + static bool isgreaterequal(half x, half y) + { + int xabs = x.data_ & 0x7FFF, yabs = y.data_ & 0x7FFF; + return xabs <= 0x7C00 && yabs <= 0x7C00 + && (((xabs == x.data_) ? xabs : -xabs) >= ((yabs == y.data_) ? yabs : -yabs)); + } + + /// Comparison implementation. + /// \param x first operand + /// \param y second operand + /// \retval true if \a x < \a y + /// \retval false else + static bool isless(half x, half y) + { + int xabs = x.data_ & 0x7FFF, yabs = y.data_ & 0x7FFF; + return xabs <= 0x7C00 && yabs <= 0x7C00 + && (((xabs == x.data_) ? xabs : -xabs) < ((yabs == y.data_) ? yabs : -yabs)); + } + + /// Comparison implementation. + /// \param x first operand + /// \param y second operand + /// \retval true if \a x <= \a y + /// \retval false else + static bool islessequal(half x, half y) + { + int xabs = x.data_ & 0x7FFF, yabs = y.data_ & 0x7FFF; + return xabs <= 0x7C00 && yabs <= 0x7C00 + && (((xabs == x.data_) ? xabs : -xabs) <= ((yabs == y.data_) ? yabs : -yabs)); + } + + /// Comparison implementation. + /// \param x first operand + /// \param y second operand + /// \retval true if either \a x > \a y nor \a x < \a y + /// \retval false else + static bool islessgreater(half x, half y) + { + int xabs = x.data_ & 0x7FFF, yabs = y.data_ & 0x7FFF; + if (xabs > 0x7C00 || yabs > 0x7C00) + return false; + int a = (xabs == x.data_) ? xabs : -xabs, b = (yabs == y.data_) ? yabs : -yabs; + return a < b || a > b; + } + + /// Comparison implementation. + /// \param x first operand + /// \param y second operand + /// \retval true if operand unordered + /// \retval false else + static bool isunordered(half x, half y) + { + return isnan(x) || isnan(y); + } + +private: + static double erf(double arg) + { + if (builtin_isinf(arg)) + return (arg < 0.0) ? -1.0 : 1.0; + double x2 = arg * arg, ax2 = 0.147 * x2, + value = std::sqrt(1.0 - std::exp(-x2 * (1.2732395447351626861510701069801 + ax2) / (1.0 + ax2))); + return builtin_signbit(arg) ? -value : value; + } + + static double lgamma(double arg) + { + double v = 1.0; + for (; arg < 8.0; ++arg) + v *= arg; + double w = 1.0 / (arg * arg); + return (((((((-0.02955065359477124183006535947712 * w + 0.00641025641025641025641025641026) * w + + -0.00191752691752691752691752691753) + * w + + 8.4175084175084175084175084175084e-4) + * w + + -5.952380952380952380952380952381e-4) + * w + + 7.9365079365079365079365079365079e-4) + * w + + -0.00277777777777777777777777777778) + * w + + 0.08333333333333333333333333333333) + / arg + + 0.91893853320467274178032973640562 - std::log(v) - arg + (arg - 0.5) * std::log(arg); + } +}; + +/// Wrapper for unary half-precision functions needing specialization for individual argument types. +/// \tparam T argument type +template +struct unary_specialized +{ + /// Negation implementation. + /// \param arg value to negate + /// \return negated value + static HALF_CONSTEXPR half negate(half arg) + { + return half(binary, arg.data_ ^ 0x8000); + } + + /// Absolute value implementation. + /// \param arg function argument + /// \return absolute value + static half fabs(half arg) + { + return half(binary, arg.data_ & 0x7FFF); + } +}; +template <> +struct unary_specialized +{ + static HALF_CONSTEXPR expr negate(float arg) + { + return expr(-arg); + } + static expr fabs(float arg) + { + return expr(std::fabs(arg)); + } +}; + +/// Wrapper for binary half-precision functions needing specialization for individual argument types. +/// \tparam T first argument type +/// \tparam U first argument type +template +struct binary_specialized +{ + /// Minimum implementation. + /// \param x first operand + /// \param y second operand + /// \return minimum value + static expr fmin(float x, float y) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::fmin(x, y)); +#else + if (builtin_isnan(x)) + return expr(y); + if (builtin_isnan(y)) + return expr(x); + return expr(std::min(x, y)); +#endif + } + + /// Maximum implementation. + /// \param x first operand + /// \param y second operand + /// \return maximum value + static expr fmax(float x, float y) + { +#if HALF_ENABLE_CPP11_CMATH + return expr(std::fmax(x, y)); +#else + if (builtin_isnan(x)) + return expr(y); + if (builtin_isnan(y)) + return expr(x); + return expr(std::max(x, y)); +#endif + } +}; +template <> +struct binary_specialized +{ + static half fmin(half x, half y) + { + int xabs = x.data_ & 0x7FFF, yabs = y.data_ & 0x7FFF; + if (xabs > 0x7C00) + return y; + if (yabs > 0x7C00) + return x; + return (((xabs == x.data_) ? xabs : -xabs) > ((yabs == y.data_) ? yabs : -yabs)) ? y : x; + } + static half fmax(half x, half y) + { + int xabs = x.data_ & 0x7FFF, yabs = y.data_ & 0x7FFF; + if (xabs > 0x7C00) + return y; + if (yabs > 0x7C00) + return x; + return (((xabs == x.data_) ? xabs : -xabs) < ((yabs == y.data_) ? yabs : -yabs)) ? y : x; + } +}; + +/// Helper class for half casts. +/// This class template has to be specialized for all valid cast argument to define an appropriate static `cast` member +/// function and a corresponding `type` member denoting its return type. +/// \tparam T destination type +/// \tparam U source type +/// \tparam R rounding mode to use +template +struct half_caster +{ +}; +template +struct half_caster +{ +#if HALF_ENABLE_CPP11_STATIC_ASSERT && HALF_ENABLE_CPP11_TYPE_TRAITS + static_assert(std::is_arithmetic::value, "half_cast from non-arithmetic type unsupported"); +#endif + + static half cast(U arg) + { + return cast_impl(arg, is_float()); + }; + +private: + static half cast_impl(U arg, true_type) + { + return half(binary, float2half(arg)); + } + static half cast_impl(U arg, false_type) + { + return half(binary, int2half(arg)); + } +}; +template +struct half_caster +{ +#if HALF_ENABLE_CPP11_STATIC_ASSERT && HALF_ENABLE_CPP11_TYPE_TRAITS + static_assert(std::is_arithmetic::value, "half_cast to non-arithmetic type unsupported"); +#endif + + static T cast(half arg) + { + return cast_impl(arg, is_float()); + } + +private: + static T cast_impl(half arg, true_type) + { + return half2float(arg.data_); + } + static T cast_impl(half arg, false_type) + { + return half2int(arg.data_); + } +}; +template +struct half_caster +{ +#if HALF_ENABLE_CPP11_STATIC_ASSERT && HALF_ENABLE_CPP11_TYPE_TRAITS + static_assert(std::is_arithmetic::value, "half_cast to non-arithmetic type unsupported"); +#endif + + static T cast(expr arg) + { + return cast_impl(arg, is_float()); + } + +private: + static T cast_impl(float arg, true_type) + { + return static_cast(arg); + } + static T cast_impl(half arg, false_type) + { + return half2int(arg.data_); + } +}; +template +struct half_caster +{ + static half cast(half arg) + { + return arg; + } +}; +template +struct half_caster : half_caster +{ +}; + +/// \name Comparison operators +/// \{ + +/// Comparison for equality. +/// \param x first operand +/// \param y second operand +/// \retval true if operands equal +/// \retval false else +template +typename enable::type operator==(T x, U y) +{ + return functions::isequal(x, y); +} + +/// Comparison for inequality. +/// \param x first operand +/// \param y second operand +/// \retval true if operands not equal +/// \retval false else +template +typename enable::type operator!=(T x, U y) +{ + return functions::isnotequal(x, y); +} + +/// Comparison for less than. +/// \param x first operand +/// \param y second operand +/// \retval true if \a x less than \a y +/// \retval false else +template +typename enable::type operator<(T x, U y) +{ + return functions::isless(x, y); +} + +/// Comparison for greater than. +/// \param x first operand +/// \param y second operand +/// \retval true if \a x greater than \a y +/// \retval false else +template +typename enable::type operator>(T x, U y) +{ + return functions::isgreater(x, y); +} + +/// Comparison for less equal. +/// \param x first operand +/// \param y second operand +/// \retval true if \a x less equal \a y +/// \retval false else +template +typename enable::type operator<=(T x, U y) +{ + return functions::islessequal(x, y); +} + +/// Comparison for greater equal. +/// \param x first operand +/// \param y second operand +/// \retval true if \a x greater equal \a y +/// \retval false else +template +typename enable::type operator>=(T x, U y) +{ + return functions::isgreaterequal(x, y); +} + +/// \} +/// \name Arithmetic operators +/// \{ + +/// Add halfs. +/// \param x left operand +/// \param y right operand +/// \return sum of half expressions +template +typename enable::type operator+(T x, U y) +{ + return functions::plus(x, y); +} + +/// Subtract halfs. +/// \param x left operand +/// \param y right operand +/// \return difference of half expressions +template +typename enable::type operator-(T x, U y) +{ + return functions::minus(x, y); +} + +/// Multiply halfs. +/// \param x left operand +/// \param y right operand +/// \return product of half expressions +template +typename enable::type operator*(T x, U y) +{ + return functions::multiplies(x, y); +} + +/// Divide halfs. +/// \param x left operand +/// \param y right operand +/// \return quotient of half expressions +template +typename enable::type operator/(T x, U y) +{ + return functions::divides(x, y); +} + +/// Identity. +/// \param arg operand +/// \return uncahnged operand +template +HALF_CONSTEXPR typename enable::type operator+(T arg) +{ + return arg; +} + +/// Negation. +/// \param arg operand +/// \return negated operand +template +HALF_CONSTEXPR typename enable::type operator-(T arg) +{ + return unary_specialized::negate(arg); +} + +/// \} +/// \name Input and output +/// \{ + +/// Output operator. +/// \param out output stream to write into +/// \param arg half expression to write +/// \return reference to output stream +template +typename enable&, T>::type operator<<(std::basic_ostream& out, T arg) +{ + return functions::write(out, arg); +} + +/// Input operator. +/// \param in input stream to read from +/// \param arg half to read into +/// \return reference to input stream +template +std::basic_istream& operator>>(std::basic_istream& in, half& arg) +{ + return functions::read(in, arg); +} + +/// \} +/// \name Basic mathematical operations +/// \{ + +/// Absolute value. +/// \param arg operand +/// \return absolute value of \a arg +// template typename enable::type abs(T arg) { return unary_specialized::fabs(arg); } +inline half abs(half arg) +{ + return unary_specialized::fabs(arg); +} +inline expr abs(expr arg) +{ + return unary_specialized::fabs(arg); +} + +/// Absolute value. +/// \param arg operand +/// \return absolute value of \a arg +// template typename enable::type fabs(T arg) { return unary_specialized::fabs(arg); } +inline half fabs(half arg) +{ + return unary_specialized::fabs(arg); +} +inline expr fabs(expr arg) +{ + return unary_specialized::fabs(arg); +} + +/// Remainder of division. +/// \param x first operand +/// \param y second operand +/// \return remainder of floating point division. +// template typename enable::type fmod(T x, U y) { return functions::fmod(x, y); } +inline expr fmod(half x, half y) +{ + return functions::fmod(x, y); +} +inline expr fmod(half x, expr y) +{ + return functions::fmod(x, y); +} +inline expr fmod(expr x, half y) +{ + return functions::fmod(x, y); +} +inline expr fmod(expr x, expr y) +{ + return functions::fmod(x, y); +} + +/// Remainder of division. +/// \param x first operand +/// \param y second operand +/// \return remainder of floating point division. +// template typename enable::type remainder(T x, U y) { return +// functions::remainder(x, y); } +inline expr remainder(half x, half y) +{ + return functions::remainder(x, y); +} +inline expr remainder(half x, expr y) +{ + return functions::remainder(x, y); +} +inline expr remainder(expr x, half y) +{ + return functions::remainder(x, y); +} +inline expr remainder(expr x, expr y) +{ + return functions::remainder(x, y); +} + +/// Remainder of division. +/// \param x first operand +/// \param y second operand +/// \param quo address to store some bits of quotient at +/// \return remainder of floating point division. +// template typename enable::type remquo(T x, U y, int *quo) { return +// functions::remquo(x, y, quo); } +inline expr remquo(half x, half y, int* quo) +{ + return functions::remquo(x, y, quo); +} +inline expr remquo(half x, expr y, int* quo) +{ + return functions::remquo(x, y, quo); +} +inline expr remquo(expr x, half y, int* quo) +{ + return functions::remquo(x, y, quo); +} +inline expr remquo(expr x, expr y, int* quo) +{ + return functions::remquo(x, y, quo); +} + +/// Fused multiply add. +/// \param x first operand +/// \param y second operand +/// \param z third operand +/// \return ( \a x * \a y ) + \a z rounded as one operation. +// template typename enable::type fma(T x, U y, V z) { return +// functions::fma(x, y, z); } +inline expr fma(half x, half y, half z) +{ + return functions::fma(x, y, z); +} +inline expr fma(half x, half y, expr z) +{ + return functions::fma(x, y, z); +} +inline expr fma(half x, expr y, half z) +{ + return functions::fma(x, y, z); +} +inline expr fma(half x, expr y, expr z) +{ + return functions::fma(x, y, z); +} +inline expr fma(expr x, half y, half z) +{ + return functions::fma(x, y, z); +} +inline expr fma(expr x, half y, expr z) +{ + return functions::fma(x, y, z); +} +inline expr fma(expr x, expr y, half z) +{ + return functions::fma(x, y, z); +} +inline expr fma(expr x, expr y, expr z) +{ + return functions::fma(x, y, z); +} + +/// Maximum of half expressions. +/// \param x first operand +/// \param y second operand +/// \return maximum of operands +// template typename result::type fmax(T x, U y) { return +// binary_specialized::fmax(x, y); } +inline half fmax(half x, half y) +{ + return binary_specialized::fmax(x, y); +} +inline expr fmax(half x, expr y) +{ + return binary_specialized::fmax(x, y); +} +inline expr fmax(expr x, half y) +{ + return binary_specialized::fmax(x, y); +} +inline expr fmax(expr x, expr y) +{ + return binary_specialized::fmax(x, y); +} + +/// Minimum of half expressions. +/// \param x first operand +/// \param y second operand +/// \return minimum of operands +// template typename result::type fmin(T x, U y) { return +// binary_specialized::fmin(x, y); } +inline half fmin(half x, half y) +{ + return binary_specialized::fmin(x, y); +} +inline expr fmin(half x, expr y) +{ + return binary_specialized::fmin(x, y); +} +inline expr fmin(expr x, half y) +{ + return binary_specialized::fmin(x, y); +} +inline expr fmin(expr x, expr y) +{ + return binary_specialized::fmin(x, y); +} + +/// Positive difference. +/// \param x first operand +/// \param y second operand +/// \return \a x - \a y or 0 if difference negative +// template typename enable::type fdim(T x, U y) { return functions::fdim(x, y); } +inline expr fdim(half x, half y) +{ + return functions::fdim(x, y); +} +inline expr fdim(half x, expr y) +{ + return functions::fdim(x, y); +} +inline expr fdim(expr x, half y) +{ + return functions::fdim(x, y); +} +inline expr fdim(expr x, expr y) +{ + return functions::fdim(x, y); +} + +/// Get NaN value. +/// \return quiet NaN +inline half nanh(const char*) +{ + return functions::nanh(); +} + +/// \} +/// \name Exponential functions +/// \{ + +/// Exponential function. +/// \param arg function argument +/// \return e raised to \a arg +// template typename enable::type exp(T arg) { return functions::exp(arg); } +inline expr exp(half arg) +{ + return functions::exp(arg); +} +inline expr exp(expr arg) +{ + return functions::exp(arg); +} + +/// Exponential minus one. +/// \param arg function argument +/// \return e raised to \a arg subtracted by 1 +// template typename enable::type expm1(T arg) { return functions::expm1(arg); } +inline expr expm1(half arg) +{ + return functions::expm1(arg); +} +inline expr expm1(expr arg) +{ + return functions::expm1(arg); +} + +/// Binary exponential. +/// \param arg function argument +/// \return 2 raised to \a arg +// template typename enable::type exp2(T arg) { return functions::exp2(arg); } +inline expr exp2(half arg) +{ + return functions::exp2(arg); +} +inline expr exp2(expr arg) +{ + return functions::exp2(arg); +} + +/// Natural logorithm. +/// \param arg function argument +/// \return logarithm of \a arg to base e +// template typename enable::type log(T arg) { return functions::log(arg); } +inline expr log(half arg) +{ + return functions::log(arg); +} +inline expr log(expr arg) +{ + return functions::log(arg); +} + +/// Common logorithm. +/// \param arg function argument +/// \return logarithm of \a arg to base 10 +// template typename enable::type log10(T arg) { return functions::log10(arg); } +inline expr log10(half arg) +{ + return functions::log10(arg); +} +inline expr log10(expr arg) +{ + return functions::log10(arg); +} + +/// Natural logorithm. +/// \param arg function argument +/// \return logarithm of \a arg plus 1 to base e +// template typename enable::type log1p(T arg) { return functions::log1p(arg); } +inline expr log1p(half arg) +{ + return functions::log1p(arg); +} +inline expr log1p(expr arg) +{ + return functions::log1p(arg); +} + +/// Binary logorithm. +/// \param arg function argument +/// \return logarithm of \a arg to base 2 +// template typename enable::type log2(T arg) { return functions::log2(arg); } +inline expr log2(half arg) +{ + return functions::log2(arg); +} +inline expr log2(expr arg) +{ + return functions::log2(arg); +} + +/// \} +/// \name Power functions +/// \{ + +/// Square root. +/// \param arg function argument +/// \return square root of \a arg +// template typename enable::type sqrt(T arg) { return functions::sqrt(arg); } +inline expr sqrt(half arg) +{ + return functions::sqrt(arg); +} +inline expr sqrt(expr arg) +{ + return functions::sqrt(arg); +} + +/// Cubic root. +/// \param arg function argument +/// \return cubic root of \a arg +// template typename enable::type cbrt(T arg) { return functions::cbrt(arg); } +inline expr cbrt(half arg) +{ + return functions::cbrt(arg); +} +inline expr cbrt(expr arg) +{ + return functions::cbrt(arg); +} + +/// Hypotenuse function. +/// \param x first argument +/// \param y second argument +/// \return square root of sum of squares without internal over- or underflows +// template typename enable::type hypot(T x, U y) { return functions::hypot(x, y); +//} +inline expr hypot(half x, half y) +{ + return functions::hypot(x, y); +} +inline expr hypot(half x, expr y) +{ + return functions::hypot(x, y); +} +inline expr hypot(expr x, half y) +{ + return functions::hypot(x, y); +} +inline expr hypot(expr x, expr y) +{ + return functions::hypot(x, y); +} + +/// Power function. +/// \param base first argument +/// \param exp second argument +/// \return \a base raised to \a exp +// template typename enable::type pow(T base, U exp) { return functions::pow(base, +// exp); } +inline expr pow(half base, half exp) +{ + return functions::pow(base, exp); +} +inline expr pow(half base, expr exp) +{ + return functions::pow(base, exp); +} +inline expr pow(expr base, half exp) +{ + return functions::pow(base, exp); +} +inline expr pow(expr base, expr exp) +{ + return functions::pow(base, exp); +} + +/// \} +/// \name Trigonometric functions +/// \{ + +/// Sine function. +/// \param arg function argument +/// \return sine value of \a arg +// template typename enable::type sin(T arg) { return functions::sin(arg); } +inline expr sin(half arg) +{ + return functions::sin(arg); +} +inline expr sin(expr arg) +{ + return functions::sin(arg); +} + +/// Cosine function. +/// \param arg function argument +/// \return cosine value of \a arg +// template typename enable::type cos(T arg) { return functions::cos(arg); } +inline expr cos(half arg) +{ + return functions::cos(arg); +} +inline expr cos(expr arg) +{ + return functions::cos(arg); +} + +/// Tangent function. +/// \param arg function argument +/// \return tangent value of \a arg +// template typename enable::type tan(T arg) { return functions::tan(arg); } +inline expr tan(half arg) +{ + return functions::tan(arg); +} +inline expr tan(expr arg) +{ + return functions::tan(arg); +} + +/// Arc sine. +/// \param arg function argument +/// \return arc sine value of \a arg +// template typename enable::type asin(T arg) { return functions::asin(arg); } +inline expr asin(half arg) +{ + return functions::asin(arg); +} +inline expr asin(expr arg) +{ + return functions::asin(arg); +} + +/// Arc cosine function. +/// \param arg function argument +/// \return arc cosine value of \a arg +// template typename enable::type acos(T arg) { return functions::acos(arg); } +inline expr acos(half arg) +{ + return functions::acos(arg); +} +inline expr acos(expr arg) +{ + return functions::acos(arg); +} + +/// Arc tangent function. +/// \param arg function argument +/// \return arc tangent value of \a arg +// template typename enable::type atan(T arg) { return functions::atan(arg); } +inline expr atan(half arg) +{ + return functions::atan(arg); +} +inline expr atan(expr arg) +{ + return functions::atan(arg); +} + +/// Arc tangent function. +/// \param x first argument +/// \param y second argument +/// \return arc tangent value +// template typename enable::type atan2(T x, U y) { return functions::atan2(x, y); +//} +inline expr atan2(half x, half y) +{ + return functions::atan2(x, y); +} +inline expr atan2(half x, expr y) +{ + return functions::atan2(x, y); +} +inline expr atan2(expr x, half y) +{ + return functions::atan2(x, y); +} +inline expr atan2(expr x, expr y) +{ + return functions::atan2(x, y); +} + +/// \} +/// \name Hyperbolic functions +/// \{ + +/// Hyperbolic sine. +/// \param arg function argument +/// \return hyperbolic sine value of \a arg +// template typename enable::type sinh(T arg) { return functions::sinh(arg); } +inline expr sinh(half arg) +{ + return functions::sinh(arg); +} +inline expr sinh(expr arg) +{ + return functions::sinh(arg); +} + +/// Hyperbolic cosine. +/// \param arg function argument +/// \return hyperbolic cosine value of \a arg +// template typename enable::type cosh(T arg) { return functions::cosh(arg); } +inline expr cosh(half arg) +{ + return functions::cosh(arg); +} +inline expr cosh(expr arg) +{ + return functions::cosh(arg); +} + +/// Hyperbolic tangent. +/// \param arg function argument +/// \return hyperbolic tangent value of \a arg +// template typename enable::type tanh(T arg) { return functions::tanh(arg); } +inline expr tanh(half arg) +{ + return functions::tanh(arg); +} +inline expr tanh(expr arg) +{ + return functions::tanh(arg); +} + +/// Hyperbolic area sine. +/// \param arg function argument +/// \return area sine value of \a arg +// template typename enable::type asinh(T arg) { return functions::asinh(arg); } +inline expr asinh(half arg) +{ + return functions::asinh(arg); +} +inline expr asinh(expr arg) +{ + return functions::asinh(arg); +} + +/// Hyperbolic area cosine. +/// \param arg function argument +/// \return area cosine value of \a arg +// template typename enable::type acosh(T arg) { return functions::acosh(arg); } +inline expr acosh(half arg) +{ + return functions::acosh(arg); +} +inline expr acosh(expr arg) +{ + return functions::acosh(arg); +} + +/// Hyperbolic area tangent. +/// \param arg function argument +/// \return area tangent value of \a arg +// template typename enable::type atanh(T arg) { return functions::atanh(arg); } +inline expr atanh(half arg) +{ + return functions::atanh(arg); +} +inline expr atanh(expr arg) +{ + return functions::atanh(arg); +} + +/// \} +/// \name Error and gamma functions +/// \{ + +/// Error function. +/// \param arg function argument +/// \return error function value of \a arg +// template typename enable::type erf(T arg) { return functions::erf(arg); } +inline expr erf(half arg) +{ + return functions::erf(arg); +} +inline expr erf(expr arg) +{ + return functions::erf(arg); +} + +/// Complementary error function. +/// \param arg function argument +/// \return 1 minus error function value of \a arg +// template typename enable::type erfc(T arg) { return functions::erfc(arg); } +inline expr erfc(half arg) +{ + return functions::erfc(arg); +} +inline expr erfc(expr arg) +{ + return functions::erfc(arg); +} + +/// Natural logarithm of gamma function. +/// \param arg function argument +/// \return natural logarith of gamma function for \a arg +// template typename enable::type lgamma(T arg) { return functions::lgamma(arg); } +inline expr lgamma(half arg) +{ + return functions::lgamma(arg); +} +inline expr lgamma(expr arg) +{ + return functions::lgamma(arg); +} + +/// Gamma function. +/// \param arg function argument +/// \return gamma function value of \a arg +// template typename enable::type tgamma(T arg) { return functions::tgamma(arg); } +inline expr tgamma(half arg) +{ + return functions::tgamma(arg); +} +inline expr tgamma(expr arg) +{ + return functions::tgamma(arg); +} + +/// \} +/// \name Rounding +/// \{ + +/// Nearest integer not less than half value. +/// \param arg half to round +/// \return nearest integer not less than \a arg +// template typename enable::type ceil(T arg) { return functions::ceil(arg); } +inline half ceil(half arg) +{ + return functions::ceil(arg); +} +inline half ceil(expr arg) +{ + return functions::ceil(arg); +} + +/// Nearest integer not greater than half value. +/// \param arg half to round +/// \return nearest integer not greater than \a arg +// template typename enable::type floor(T arg) { return functions::floor(arg); } +inline half floor(half arg) +{ + return functions::floor(arg); +} +inline half floor(expr arg) +{ + return functions::floor(arg); +} + +/// Nearest integer not greater in magnitude than half value. +/// \param arg half to round +/// \return nearest integer not greater in magnitude than \a arg +// template typename enable::type trunc(T arg) { return functions::trunc(arg); } +inline half trunc(half arg) +{ + return functions::trunc(arg); +} +inline half trunc(expr arg) +{ + return functions::trunc(arg); +} + +/// Nearest integer. +/// \param arg half to round +/// \return nearest integer, rounded away from zero in half-way cases +// template typename enable::type round(T arg) { return functions::round(arg); } +inline half round(half arg) +{ + return functions::round(arg); +} +inline half round(expr arg) +{ + return functions::round(arg); +} + +/// Nearest integer. +/// \param arg half to round +/// \return nearest integer, rounded away from zero in half-way cases +// template typename enable::type lround(T arg) { return functions::lround(arg); } +inline long lround(half arg) +{ + return functions::lround(arg); +} +inline long lround(expr arg) +{ + return functions::lround(arg); +} + +/// Nearest integer using half's internal rounding mode. +/// \param arg half expression to round +/// \return nearest integer using default rounding mode +// template typename enable::type nearbyint(T arg) { return functions::nearbyint(arg); } +inline half nearbyint(half arg) +{ + return functions::rint(arg); +} +inline half nearbyint(expr arg) +{ + return functions::rint(arg); +} + +/// Nearest integer using half's internal rounding mode. +/// \param arg half expression to round +/// \return nearest integer using default rounding mode +// template typename enable::type rint(T arg) { return functions::rint(arg); } +inline half rint(half arg) +{ + return functions::rint(arg); +} +inline half rint(expr arg) +{ + return functions::rint(arg); +} + +/// Nearest integer using half's internal rounding mode. +/// \param arg half expression to round +/// \return nearest integer using default rounding mode +// template typename enable::type lrint(T arg) { return functions::lrint(arg); } +inline long lrint(half arg) +{ + return functions::lrint(arg); +} +inline long lrint(expr arg) +{ + return functions::lrint(arg); +} +#if HALF_ENABLE_CPP11_LONG_LONG +/// Nearest integer. +/// \param arg half to round +/// \return nearest integer, rounded away from zero in half-way cases +// template typename enable::type llround(T arg) { return functions::llround(arg); } +inline long long llround(half arg) +{ + return functions::llround(arg); +} +inline long long llround(expr arg) +{ + return functions::llround(arg); +} + +/// Nearest integer using half's internal rounding mode. +/// \param arg half expression to round +/// \return nearest integer using default rounding mode +// template typename enable::type llrint(T arg) { return functions::llrint(arg); } +inline long long llrint(half arg) +{ + return functions::llrint(arg); +} +inline long long llrint(expr arg) +{ + return functions::llrint(arg); +} +#endif + +/// \} +/// \name Floating point manipulation +/// \{ + +/// Decompress floating point number. +/// \param arg number to decompress +/// \param exp address to store exponent at +/// \return significant in range [0.5, 1) +// template typename enable::type frexp(T arg, int *exp) { return functions::frexp(arg, exp); } +inline half frexp(half arg, int* exp) +{ + return functions::frexp(arg, exp); +} +inline half frexp(expr arg, int* exp) +{ + return functions::frexp(arg, exp); +} + +/// Multiply by power of two. +/// \param arg number to modify +/// \param exp power of two to multiply with +/// \return \a arg multplied by 2 raised to \a exp +// template typename enable::type ldexp(T arg, int exp) { return functions::scalbln(arg, exp); +//} +inline half ldexp(half arg, int exp) +{ + return functions::scalbln(arg, exp); +} +inline half ldexp(expr arg, int exp) +{ + return functions::scalbln(arg, exp); +} + +/// Extract integer and fractional parts. +/// \param arg number to decompress +/// \param iptr address to store integer part at +/// \return fractional part +// template typename enable::type modf(T arg, half *iptr) { return functions::modf(arg, iptr); +//} +inline half modf(half arg, half* iptr) +{ + return functions::modf(arg, iptr); +} +inline half modf(expr arg, half* iptr) +{ + return functions::modf(arg, iptr); +} + +/// Multiply by power of two. +/// \param arg number to modify +/// \param exp power of two to multiply with +/// \return \a arg multplied by 2 raised to \a exp +// template typename enable::type scalbn(T arg, int exp) { return functions::scalbln(arg, exp); +//} +inline half scalbn(half arg, int exp) +{ + return functions::scalbln(arg, exp); +} +inline half scalbn(expr arg, int exp) +{ + return functions::scalbln(arg, exp); +} + +/// Multiply by power of two. +/// \param arg number to modify +/// \param exp power of two to multiply with +/// \return \a arg multplied by 2 raised to \a exp +// template typename enable::type scalbln(T arg, long exp) { return functions::scalbln(arg, +// exp); +//} +inline half scalbln(half arg, long exp) +{ + return functions::scalbln(arg, exp); +} +inline half scalbln(expr arg, long exp) +{ + return functions::scalbln(arg, exp); +} + +/// Extract exponent. +/// \param arg number to query +/// \return floating point exponent +/// \retval FP_ILOGB0 for zero +/// \retval FP_ILOGBNAN for NaN +/// \retval MAX_INT for infinity +// template typename enable::type ilogb(T arg) { return functions::ilogb(arg); } +inline int ilogb(half arg) +{ + return functions::ilogb(arg); +} +inline int ilogb(expr arg) +{ + return functions::ilogb(arg); +} + +/// Extract exponent. +/// \param arg number to query +/// \return floating point exponent +// template typename enable::type logb(T arg) { return functions::logb(arg); } +inline half logb(half arg) +{ + return functions::logb(arg); +} +inline half logb(expr arg) +{ + return functions::logb(arg); +} + +/// Next representable value. +/// \param from value to compute next representable value for +/// \param to direction towards which to compute next value +/// \return next representable value after \a from in direction towards \a to +// template typename enable::type nextafter(T from, U to) { return +// functions::nextafter(from, to); } +inline half nextafter(half from, half to) +{ + return functions::nextafter(from, to); +} +inline half nextafter(half from, expr to) +{ + return functions::nextafter(from, to); +} +inline half nextafter(expr from, half to) +{ + return functions::nextafter(from, to); +} +inline half nextafter(expr from, expr to) +{ + return functions::nextafter(from, to); +} + +/// Next representable value. +/// \param from value to compute next representable value for +/// \param to direction towards which to compute next value +/// \return next representable value after \a from in direction towards \a to +// template typename enable::type nexttoward(T from, long double to) { return +// functions::nexttoward(from, to); } +inline half nexttoward(half from, long double to) +{ + return functions::nexttoward(from, to); +} +inline half nexttoward(expr from, long double to) +{ + return functions::nexttoward(from, to); +} + +/// Take sign. +/// \param x value to change sign for +/// \param y value to take sign from +/// \return value equal to \a x in magnitude and to \a y in sign +// template typename enable::type copysign(T x, U y) { return +// functions::copysign(x, y); } +inline half copysign(half x, half y) +{ + return functions::copysign(x, y); +} +inline half copysign(half x, expr y) +{ + return functions::copysign(x, y); +} +inline half copysign(expr x, half y) +{ + return functions::copysign(x, y); +} +inline half copysign(expr x, expr y) +{ + return functions::copysign(x, y); +} + +/// \} +/// \name Floating point classification +/// \{ + +/// Classify floating point value. +/// \param arg number to classify +/// \retval FP_ZERO for positive and negative zero +/// \retval FP_SUBNORMAL for subnormal numbers +/// \retval FP_INFINITY for positive and negative infinity +/// \retval FP_NAN for NaNs +/// \retval FP_NORMAL for all other (normal) values +// template typename enable::type fpclassify(T arg) { return functions::fpclassify(arg); } +inline int fpclassify(half arg) +{ + return functions::fpclassify(arg); +} +inline int fpclassify(expr arg) +{ + return functions::fpclassify(arg); +} + +/// Check if finite number. +/// \param arg number to check +/// \retval true if neither infinity nor NaN +/// \retval false else +// template typename enable::type isfinite(T arg) { return functions::isfinite(arg); } +inline bool isfinite(half arg) +{ + return functions::isfinite(arg); +} +inline bool isfinite(expr arg) +{ + return functions::isfinite(arg); +} + +/// Check for infinity. +/// \param arg number to check +/// \retval true for positive or negative infinity +/// \retval false else +// template typename enable::type isinf(T arg) { return functions::isinf(arg); } +inline bool isinf(half arg) +{ + return functions::isinf(arg); +} +inline bool isinf(expr arg) +{ + return functions::isinf(arg); +} + +/// Check for NaN. +/// \param arg number to check +/// \retval true for NaNs +/// \retval false else +// template typename enable::type isnan(T arg) { return functions::isnan(arg); } +inline bool isnan(half arg) +{ + return functions::isnan(arg); +} +inline bool isnan(expr arg) +{ + return functions::isnan(arg); +} + +/// Check if normal number. +/// \param arg number to check +/// \retval true if normal number +/// \retval false if either subnormal, zero, infinity or NaN +// template typename enable::type isnormal(T arg) { return functions::isnormal(arg); } +inline bool isnormal(half arg) +{ + return functions::isnormal(arg); +} +inline bool isnormal(expr arg) +{ + return functions::isnormal(arg); +} + +/// Check sign. +/// \param arg number to check +/// \retval true for negative number +/// \retval false for positive number +// template typename enable::type signbit(T arg) { return functions::signbit(arg); } +inline bool signbit(half arg) +{ + return functions::signbit(arg); +} +inline bool signbit(expr arg) +{ + return functions::signbit(arg); +} + +/// \} +/// \name Comparison +/// \{ + +/// Comparison for greater than. +/// \param x first operand +/// \param y second operand +/// \retval true if \a x greater than \a y +/// \retval false else +// template typename enable::type isgreater(T x, U y) { return +// functions::isgreater(x, y); } +inline bool isgreater(half x, half y) +{ + return functions::isgreater(x, y); +} +inline bool isgreater(half x, expr y) +{ + return functions::isgreater(x, y); +} +inline bool isgreater(expr x, half y) +{ + return functions::isgreater(x, y); +} +inline bool isgreater(expr x, expr y) +{ + return functions::isgreater(x, y); +} + +/// Comparison for greater equal. +/// \param x first operand +/// \param y second operand +/// \retval true if \a x greater equal \a y +/// \retval false else +// template typename enable::type isgreaterequal(T x, U y) { return +// functions::isgreaterequal(x, y); } +inline bool isgreaterequal(half x, half y) +{ + return functions::isgreaterequal(x, y); +} +inline bool isgreaterequal(half x, expr y) +{ + return functions::isgreaterequal(x, y); +} +inline bool isgreaterequal(expr x, half y) +{ + return functions::isgreaterequal(x, y); +} +inline bool isgreaterequal(expr x, expr y) +{ + return functions::isgreaterequal(x, y); +} + +/// Comparison for less than. +/// \param x first operand +/// \param y second operand +/// \retval true if \a x less than \a y +/// \retval false else +// template typename enable::type isless(T x, U y) { return functions::isless(x, +// y); +//} +inline bool isless(half x, half y) +{ + return functions::isless(x, y); +} +inline bool isless(half x, expr y) +{ + return functions::isless(x, y); +} +inline bool isless(expr x, half y) +{ + return functions::isless(x, y); +} +inline bool isless(expr x, expr y) +{ + return functions::isless(x, y); +} + +/// Comparison for less equal. +/// \param x first operand +/// \param y second operand +/// \retval true if \a x less equal \a y +/// \retval false else +// template typename enable::type islessequal(T x, U y) { return +// functions::islessequal(x, y); } +inline bool islessequal(half x, half y) +{ + return functions::islessequal(x, y); +} +inline bool islessequal(half x, expr y) +{ + return functions::islessequal(x, y); +} +inline bool islessequal(expr x, half y) +{ + return functions::islessequal(x, y); +} +inline bool islessequal(expr x, expr y) +{ + return functions::islessequal(x, y); +} + +/// Comarison for less or greater. +/// \param x first operand +/// \param y second operand +/// \retval true if either less or greater +/// \retval false else +// template typename enable::type islessgreater(T x, U y) { return +// functions::islessgreater(x, y); } +inline bool islessgreater(half x, half y) +{ + return functions::islessgreater(x, y); +} +inline bool islessgreater(half x, expr y) +{ + return functions::islessgreater(x, y); +} +inline bool islessgreater(expr x, half y) +{ + return functions::islessgreater(x, y); +} +inline bool islessgreater(expr x, expr y) +{ + return functions::islessgreater(x, y); +} + +/// Check if unordered. +/// \param x first operand +/// \param y second operand +/// \retval true if unordered (one or two NaN operands) +/// \retval false else +// template typename enable::type isunordered(T x, U y) { return +// functions::isunordered(x, y); } +inline bool isunordered(half x, half y) +{ + return functions::isunordered(x, y); +} +inline bool isunordered(half x, expr y) +{ + return functions::isunordered(x, y); +} +inline bool isunordered(expr x, half y) +{ + return functions::isunordered(x, y); +} +inline bool isunordered(expr x, expr y) +{ + return functions::isunordered(x, y); +} + +/// \name Casting +/// \{ + +/// Cast to or from half-precision floating point number. +/// This casts between [half](\ref half_float::half) and any built-in arithmetic type. The values are converted +/// directly using the given rounding mode, without any roundtrip over `float` that a `static_cast` would otherwise do. +/// It uses the default rounding mode. +/// +/// Using this cast with neither of the two types being a [half](\ref half_float::half) or with any of the two types +/// not being a built-in arithmetic type (apart from [half](\ref half_float::half), of course) results in a compiler +/// error and casting between [half](\ref half_float::half)s is just a no-op. +/// \tparam T destination type (half or built-in arithmetic type) +/// \tparam U source type (half or built-in arithmetic type) +/// \param arg value to cast +/// \return \a arg converted to destination type +template +T half_cast(U arg) +{ + return half_caster::cast(arg); +} + +/// Cast to or from half-precision floating point number. +/// This casts between [half](\ref half_float::half) and any built-in arithmetic type. The values are converted +/// directly using the given rounding mode, without any roundtrip over `float` that a `static_cast` would otherwise do. +/// +/// Using this cast with neither of the two types being a [half](\ref half_float::half) or with any of the two types +/// not being a built-in arithmetic type (apart from [half](\ref half_float::half), of course) results in a compiler +/// error and casting between [half](\ref half_float::half)s is just a no-op. +/// \tparam T destination type (half or built-in arithmetic type) +/// \tparam R rounding mode to use. +/// \tparam U source type (half or built-in arithmetic type) +/// \param arg value to cast +/// \return \a arg converted to destination type +template +T half_cast(U arg) +{ + return half_caster::cast(arg); +} +/// \} +} // namespace detail + +using detail::operator==; +using detail::operator!=; +using detail::operator<; +using detail::operator>; +using detail::operator<=; +using detail::operator>=; +using detail::operator+; +using detail::operator-; +using detail::operator*; +using detail::operator/; +using detail::operator<<; +using detail::operator>>; + +using detail::abs; +using detail::acos; +using detail::acosh; +using detail::asin; +using detail::asinh; +using detail::atan; +using detail::atan2; +using detail::atanh; +using detail::cbrt; +using detail::ceil; +using detail::cos; +using detail::cosh; +using detail::erf; +using detail::erfc; +using detail::exp; +using detail::exp2; +using detail::expm1; +using detail::fabs; +using detail::fdim; +using detail::floor; +using detail::fma; +using detail::fmax; +using detail::fmin; +using detail::fmod; +using detail::hypot; +using detail::lgamma; +using detail::log; +using detail::log10; +using detail::log1p; +using detail::log2; +using detail::lrint; +using detail::lround; +using detail::nanh; +using detail::nearbyint; +using detail::pow; +using detail::remainder; +using detail::remquo; +using detail::rint; +using detail::round; +using detail::sin; +using detail::sinh; +using detail::sqrt; +using detail::tan; +using detail::tanh; +using detail::tgamma; +using detail::trunc; +#if HALF_ENABLE_CPP11_LONG_LONG +using detail::llrint; +using detail::llround; +#endif +using detail::copysign; +using detail::fpclassify; +using detail::frexp; +using detail::ilogb; +using detail::isfinite; +using detail::isgreater; +using detail::isgreaterequal; +using detail::isinf; +using detail::isless; +using detail::islessequal; +using detail::islessgreater; +using detail::isnan; +using detail::isnormal; +using detail::isunordered; +using detail::ldexp; +using detail::logb; +using detail::modf; +using detail::nextafter; +using detail::nexttoward; +using detail::scalbln; +using detail::scalbn; +using detail::signbit; + +using detail::half_cast; +} // namespace half_float + +/// Extensions to the C++ standard library. +namespace std +{ +/// Numeric limits for half-precision floats. +/// Because of the underlying single-precision implementation of many operations, it inherits some properties from +/// `std::numeric_limits`. +template <> +class numeric_limits : public numeric_limits +{ +public: + /// Supports signed values. + static HALF_CONSTEXPR_CONST bool is_signed = true; + + /// Is not exact. + static HALF_CONSTEXPR_CONST bool is_exact = false; + + /// Doesn't provide modulo arithmetic. + static HALF_CONSTEXPR_CONST bool is_modulo = false; + + /// IEEE conformant. + static HALF_CONSTEXPR_CONST bool is_iec559 = true; + + /// Supports infinity. + static HALF_CONSTEXPR_CONST bool has_infinity = true; + + /// Supports quiet NaNs. + static HALF_CONSTEXPR_CONST bool has_quiet_NaN = true; + + /// Supports subnormal values. + static HALF_CONSTEXPR_CONST float_denorm_style has_denorm = denorm_present; + + /// Rounding mode. + /// Due to the mix of internal single-precision computations (using the rounding mode of the underlying + /// single-precision implementation) with the rounding mode of the single-to-half conversions, the actual rounding + /// mode might be `std::round_indeterminate` if the default half-precision rounding mode doesn't match the + /// single-precision rounding mode. + static HALF_CONSTEXPR_CONST float_round_style round_style + = (std::numeric_limits::round_style == half_float::half::round_style) ? half_float::half::round_style + : round_indeterminate; + + /// Significant digits. + static HALF_CONSTEXPR_CONST int digits = 11; + + /// Significant decimal digits. + static HALF_CONSTEXPR_CONST int digits10 = 3; + + /// Required decimal digits to represent all possible values. + static HALF_CONSTEXPR_CONST int max_digits10 = 5; + + /// Number base. + static HALF_CONSTEXPR_CONST int radix = 2; + + /// One more than smallest exponent. + static HALF_CONSTEXPR_CONST int min_exponent = -13; + + /// Smallest normalized representable power of 10. + static HALF_CONSTEXPR_CONST int min_exponent10 = -4; + + /// One more than largest exponent + static HALF_CONSTEXPR_CONST int max_exponent = 16; + + /// Largest finitely representable power of 10. + static HALF_CONSTEXPR_CONST int max_exponent10 = 4; + + /// Smallest positive normal value. + static HALF_CONSTEXPR half_float::half min() HALF_NOTHROW + { + return half_float::half(half_float::detail::binary, 0x0400); + } + + /// Smallest finite value. + static HALF_CONSTEXPR half_float::half lowest() HALF_NOTHROW + { + return half_float::half(half_float::detail::binary, 0xFBFF); + } + + /// Largest finite value. + static HALF_CONSTEXPR half_float::half max() HALF_NOTHROW + { + return half_float::half(half_float::detail::binary, 0x7BFF); + } + + /// Difference between one and next representable value. + static HALF_CONSTEXPR half_float::half epsilon() HALF_NOTHROW + { + return half_float::half(half_float::detail::binary, 0x1400); + } + + /// Maximum rounding error. + static HALF_CONSTEXPR half_float::half round_error() HALF_NOTHROW + { + return half_float::half(half_float::detail::binary, (round_style == std::round_to_nearest) ? 0x3800 : 0x3C00); + } + + /// Positive infinity. + static HALF_CONSTEXPR half_float::half infinity() HALF_NOTHROW + { + return half_float::half(half_float::detail::binary, 0x7C00); + } + + /// Quiet NaN. + static HALF_CONSTEXPR half_float::half quiet_NaN() HALF_NOTHROW + { + return half_float::half(half_float::detail::binary, 0x7FFF); + } + + /// Signalling NaN. + static HALF_CONSTEXPR half_float::half signaling_NaN() HALF_NOTHROW + { + return half_float::half(half_float::detail::binary, 0x7DFF); + } + + /// Smallest positive subnormal value. + static HALF_CONSTEXPR half_float::half denorm_min() HALF_NOTHROW + { + return half_float::half(half_float::detail::binary, 0x0001); + } +}; + +#if HALF_ENABLE_CPP11_HASH +/// Hash function for half-precision floats. +/// This is only defined if C++11 `std::hash` is supported and enabled. +template <> +struct hash //: unary_function +{ + /// Type of function argument. + typedef half_float::half argument_type; + + /// Function return type. + typedef size_t result_type; + + /// Compute hash function. + /// \param arg half to hash + /// \return hash value + result_type operator()(argument_type arg) const + { + return hash()(static_cast(arg.data_) & -(arg.data_ != 0x8000)); + } +}; +#endif +} // namespace std + +#undef HALF_CONSTEXPR +#undef HALF_CONSTEXPR_CONST +#undef HALF_NOEXCEPT +#undef HALF_NOTHROW +#ifdef HALF_POP_WARNINGS +#pragma warning(pop) +#undef HALF_POP_WARNINGS +#endif + +#endif diff --git a/src/Detector/tensorrt_onnx/common/logger.cpp b/src/Detector/tensorrt_onnx/common/logger.cpp new file mode 100644 index 000000000..909ec0bbd --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/logger.cpp @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#include "logger.h" +#include "ErrorRecorder.h" +#include "logging.h" +using namespace nvinfer1; +SampleErrorRecorder gRecorder; +namespace sample +{ +Logger gLogger{Logger::Severity::kINFO}; +LogStreamConsumer gLogVerbose{LOG_VERBOSE(gLogger)}; +LogStreamConsumer gLogInfo{LOG_INFO(gLogger)}; +LogStreamConsumer gLogWarning{LOG_WARN(gLogger)}; +LogStreamConsumer gLogError{LOG_ERROR(gLogger)}; +LogStreamConsumer gLogFatal{LOG_FATAL(gLogger)}; + +void setReportableSeverity(Logger::Severity severity) +{ + gLogger.setReportableSeverity(severity); + gLogVerbose.setReportableSeverity(severity); + gLogInfo.setReportableSeverity(severity); + gLogWarning.setReportableSeverity(severity); + gLogError.setReportableSeverity(severity); + gLogFatal.setReportableSeverity(severity); +} +} // namespace sample diff --git a/src/Detector/tensorrt_onnx/common/logger.h b/src/Detector/tensorrt_onnx/common/logger.h new file mode 100644 index 000000000..8205e4572 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/logger.h @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef LOGGER_H +#define LOGGER_H + +#include "logging.h" + +class SampleErrorRecorder; +extern SampleErrorRecorder gRecorder; +namespace sample +{ +extern Logger gLogger; +extern LogStreamConsumer gLogVerbose; +extern LogStreamConsumer gLogInfo; +extern LogStreamConsumer gLogWarning; +extern LogStreamConsumer gLogError; +extern LogStreamConsumer gLogFatal; + +void setReportableSeverity(Logger::Severity severity); +} // namespace sample + +#endif // LOGGER_H diff --git a/src/Detector/tensorrt_onnx/common/logging.h b/src/Detector/tensorrt_onnx/common/logging.h new file mode 100644 index 000000000..69273a5ee --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/logging.h @@ -0,0 +1,580 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef TENSORRT_LOGGING_H +#define TENSORRT_LOGGING_H + +#include "NvInferRuntime.h" +#include "sampleOptions.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sample +{ + +using Severity = nvinfer1::ILogger::Severity; + +class LogStreamConsumerBuffer : public std::stringbuf +{ +public: + LogStreamConsumerBuffer(std::ostream& stream, const std::string& prefix, bool shouldLog) + : mOutput(stream) + , mPrefix(prefix) + , mShouldLog(shouldLog) + { + } + + LogStreamConsumerBuffer(LogStreamConsumerBuffer&& other) noexcept + : mOutput(other.mOutput) + , mPrefix(other.mPrefix) + , mShouldLog(other.mShouldLog) + { + } + LogStreamConsumerBuffer(const LogStreamConsumerBuffer& other) = delete; + LogStreamConsumerBuffer() = delete; + LogStreamConsumerBuffer& operator=(const LogStreamConsumerBuffer&) = delete; + LogStreamConsumerBuffer& operator=(LogStreamConsumerBuffer&&) = delete; + + ~LogStreamConsumerBuffer() override + { + // std::streambuf::pbase() gives a pointer to the beginning of the buffered part of the output sequence + // std::streambuf::pptr() gives a pointer to the current position of the output sequence + // if the pointer to the beginning is not equal to the pointer to the current position, + // call putOutput() to log the output to the stream + if (pbase() != pptr()) + { + putOutput(); + } + } + + //! + //! synchronizes the stream buffer and returns 0 on success + //! synchronizing the stream buffer consists of inserting the buffer contents into the stream, + //! resetting the buffer and flushing the stream + //! + int32_t sync() override + { + putOutput(); + return 0; + } + + void putOutput() + { + if (mShouldLog) + { + // prepend timestamp + std::time_t timestamp = std::time(nullptr); + tm* tm_local = std::localtime(×tamp); + mOutput << "["; + mOutput << std::setw(2) << std::setfill('0') << 1 + tm_local->tm_mon << "/"; + mOutput << std::setw(2) << std::setfill('0') << tm_local->tm_mday << "/"; + mOutput << std::setw(4) << std::setfill('0') << 1900 + tm_local->tm_year << "-"; + mOutput << std::setw(2) << std::setfill('0') << tm_local->tm_hour << ":"; + mOutput << std::setw(2) << std::setfill('0') << tm_local->tm_min << ":"; + mOutput << std::setw(2) << std::setfill('0') << tm_local->tm_sec << "] "; + // std::stringbuf::str() gets the string contents of the buffer + // insert the buffer contents pre-appended by the appropriate prefix into the stream + mOutput << mPrefix << str(); + } + // set the buffer to empty + str(""); + // flush the stream + mOutput.flush(); + } + + void setShouldLog(bool shouldLog) + { + mShouldLog = shouldLog; + } + +private: + std::ostream& mOutput; + std::string mPrefix; + bool mShouldLog{}; +}; // class LogStreamConsumerBuffer + +//! +//! \class LogStreamConsumerBase +//! \brief Convenience object used to initialize LogStreamConsumerBuffer before std::ostream in LogStreamConsumer +//! +class LogStreamConsumerBase +{ +public: + LogStreamConsumerBase(std::ostream& stream, const std::string& prefix, bool shouldLog) + : mBuffer(stream, prefix, shouldLog) + { + } + +protected: + std::mutex mLogMutex; + LogStreamConsumerBuffer mBuffer; +}; // class LogStreamConsumerBase + +//! +//! \class LogStreamConsumer +//! \brief Convenience object used to facilitate use of C++ stream syntax when logging messages. +//! Order of base classes is LogStreamConsumerBase and then std::ostream. +//! This is because the LogStreamConsumerBase class is used to initialize the LogStreamConsumerBuffer member field +//! in LogStreamConsumer and then the address of the buffer is passed to std::ostream. +//! This is necessary to prevent the address of an uninitialized buffer from being passed to std::ostream. +//! Please do not change the order of the parent classes. +//! +class LogStreamConsumer : protected LogStreamConsumerBase, public std::ostream +{ +public: + //! + //! \brief Creates a LogStreamConsumer which logs messages with level severity. + //! Reportable severity determines if the messages are severe enough to be logged. + //! + LogStreamConsumer(nvinfer1::ILogger::Severity reportableSeverity, nvinfer1::ILogger::Severity severity) + : LogStreamConsumerBase(severityOstream(severity), severityPrefix(severity), severity <= reportableSeverity) + , std::ostream(&mBuffer) // links the stream buffer with the stream + , mShouldLog(severity <= reportableSeverity) + , mSeverity(severity) + { + } + + LogStreamConsumer(LogStreamConsumer&& other) noexcept + : LogStreamConsumerBase(severityOstream(other.mSeverity), severityPrefix(other.mSeverity), other.mShouldLog) + , std::ostream(&mBuffer) // links the stream buffer with the stream + , mShouldLog(other.mShouldLog) + , mSeverity(other.mSeverity) + { + } + LogStreamConsumer(const LogStreamConsumer& other) = delete; + LogStreamConsumer() = delete; + ~LogStreamConsumer() override = default; + LogStreamConsumer& operator=(const LogStreamConsumer&) = delete; + LogStreamConsumer& operator=(LogStreamConsumer&&) = delete; + + void setReportableSeverity(Severity reportableSeverity) + { + mShouldLog = mSeverity <= reportableSeverity; + mBuffer.setShouldLog(mShouldLog); + } + + std::mutex& getMutex() + { + return mLogMutex; + } + + bool getShouldLog() const + { + return mShouldLog; + } + +private: + static std::ostream& severityOstream(Severity severity) + { + return severity >= Severity::kINFO ? std::cout : std::cerr; + } + + static std::string severityPrefix(Severity severity) + { + switch (severity) + { + case Severity::kINTERNAL_ERROR: return "[F] "; + case Severity::kERROR: return "[E] "; + case Severity::kWARNING: return "[W] "; + case Severity::kINFO: return "[I] "; + case Severity::kVERBOSE: return "[V] "; + default: assert(0); return ""; + } + } + + bool mShouldLog; + Severity mSeverity; +}; // class LogStreamConsumer + +template +LogStreamConsumer& operator<<(LogStreamConsumer& logger, const T& obj) +{ + if (logger.getShouldLog()) + { + std::lock_guard guard(logger.getMutex()); + auto& os = static_cast(logger); + os << obj; + } + return logger; +} + +//! +//! Special handling std::endl +//! +inline LogStreamConsumer& operator<<(LogStreamConsumer& logger, std::ostream& (*f)(std::ostream&) ) +{ + if (logger.getShouldLog()) + { + std::lock_guard guard(logger.getMutex()); + auto& os = static_cast(logger); + os << f; + } + return logger; +} + +inline LogStreamConsumer& operator<<(LogStreamConsumer& logger, const nvinfer1::Dims& dims) +{ + if (logger.getShouldLog()) + { + std::lock_guard guard(logger.getMutex()); + auto& os = static_cast(logger); + for (int32_t i = 0; i < dims.nbDims; ++i) + { + os << (i ? "x" : "") << dims.d[i]; + } + } + return logger; +} + +//! +//! \class Logger +//! +//! \brief Class which manages logging of TensorRT tools and samples +//! +//! \details This class provides a common interface for TensorRT tools and samples to log information to the console, +//! and supports logging two types of messages: +//! +//! - Debugging messages with an associated severity (info, warning, error, or internal error/fatal) +//! - Test pass/fail messages +//! +//! The advantage of having all samples use this class for logging as opposed to emitting directly to stdout/stderr is +//! that the logic for controlling the verbosity and formatting of sample output is centralized in one location. +//! +//! In the future, this class could be extended to support dumping test results to a file in some standard format +//! (for example, JUnit XML), and providing additional metadata (e.g. timing the duration of a test run). +//! +//! TODO: For backwards compatibility with existing samples, this class inherits directly from the nvinfer1::ILogger +//! interface, which is problematic since there isn't a clean separation between messages coming from the TensorRT +//! library and messages coming from the sample. +//! +//! In the future (once all samples are updated to use Logger::getTRTLogger() to access the ILogger) we can refactor the +//! class to eliminate the inheritance and instead make the nvinfer1::ILogger implementation a member of the Logger +//! object. +//! +class Logger : public nvinfer1::ILogger +{ +public: + explicit Logger(Severity severity = Severity::kWARNING) + : mReportableSeverity(severity) + { + } + + //! + //! \enum TestResult + //! \brief Represents the state of a given test + //! + enum class TestResult + { + kRUNNING, //!< The test is running + kPASSED, //!< The test passed + kFAILED, //!< The test failed + kWAIVED //!< The test was waived + }; + + //! + //! \brief Forward-compatible method for retrieving the nvinfer1::ILogger associated with this Logger + //! \return The nvinfer1::ILogger associated with this Logger + //! + //! TODO Once all samples are updated to use this method to register the logger with TensorRT, + //! we can eliminate the inheritance of Logger from ILogger + //! + nvinfer1::ILogger& getTRTLogger() noexcept + { + return *this; + } + + //! + //! \brief Implementation of the nvinfer1::ILogger::log() virtual method + //! + //! Note samples should not be calling this function directly; it will eventually go away once we eliminate the + //! inheritance from nvinfer1::ILogger + //! + void log(Severity severity, const char* msg) noexcept override + { + LogStreamConsumer(mReportableSeverity, severity) << "[TRT] " << std::string(msg) << std::endl; + } + + //! + //! \brief Method for controlling the verbosity of logging output + //! + //! \param severity The logger will only emit messages that have severity of this level or higher. + //! + void setReportableSeverity(Severity severity) noexcept + { + mReportableSeverity = severity; + } + + //! + //! \brief Opaque handle that holds logging information for a particular test + //! + //! This object is an opaque handle to information used by the Logger to print test results. + //! The sample must call Logger::defineTest() in order to obtain a TestAtom that can be used + //! with Logger::reportTest{Start,End}(). + //! + class TestAtom + { + public: + TestAtom(TestAtom&&) = default; + + private: + friend class Logger; + + TestAtom(bool started, const std::string& name, const std::string& cmdline) + : mStarted(started) + , mName(name) + , mCmdline(cmdline) + { + } + + bool mStarted; + std::string mName; + std::string mCmdline; + }; + + //! + //! \brief Define a test for logging + //! + //! \param[in] name The name of the test. This should be a string starting with + //! "TensorRT" and containing dot-separated strings containing + //! the characters [A-Za-z0-9_]. + //! For example, "TensorRT.sample_googlenet" + //! \param[in] cmdline The command line used to reproduce the test + // + //! \return a TestAtom that can be used in Logger::reportTest{Start,End}(). + //! + static TestAtom defineTest(const std::string& name, const std::string& cmdline) + { + return TestAtom(false, name, cmdline); + } + + //! + //! \brief A convenience overloaded version of defineTest() that accepts an array of command-line arguments + //! as input + //! + //! \param[in] name The name of the test + //! \param[in] argc The number of command-line arguments + //! \param[in] argv The array of command-line arguments (given as C strings) + //! + //! \return a TestAtom that can be used in Logger::reportTest{Start,End}(). + //! + static TestAtom defineTest(const std::string& name, int32_t argc, char const* const* argv) + { + // Append TensorRT version as info + const std::string vname = name + " [TensorRT v" + std::to_string(NV_TENSORRT_VERSION) + "] [b" + + std::to_string(NV_TENSORRT_BUILD) + "]"; + auto cmdline = genCmdlineString(argc, argv); + return defineTest(vname, cmdline); + } + + //! + //! \brief Report that a test has started. + //! + //! \pre reportTestStart() has not been called yet for the given testAtom + //! + //! \param[in] testAtom The handle to the test that has started + //! + static void reportTestStart(TestAtom& testAtom) + { + reportTestResult(testAtom, TestResult::kRUNNING); + assert(!testAtom.mStarted); + testAtom.mStarted = true; + } + + //! + //! \brief Report that a test has ended. + //! + //! \pre reportTestStart() has been called for the given testAtom + //! + //! \param[in] testAtom The handle to the test that has ended + //! \param[in] result The result of the test. Should be one of TestResult::kPASSED, + //! TestResult::kFAILED, TestResult::kWAIVED + //! + static void reportTestEnd(TestAtom const& testAtom, TestResult result) + { + assert(result != TestResult::kRUNNING); + assert(testAtom.mStarted); + reportTestResult(testAtom, result); + } + + static int32_t reportPass(TestAtom const& testAtom) + { + reportTestEnd(testAtom, TestResult::kPASSED); + return EXIT_SUCCESS; + } + + static int32_t reportFail(TestAtom const& testAtom) + { + reportTestEnd(testAtom, TestResult::kFAILED); + return EXIT_FAILURE; + } + + static int32_t reportWaive(TestAtom const& testAtom) + { + reportTestEnd(testAtom, TestResult::kWAIVED); + return EXIT_SUCCESS; + } + + static int32_t reportTest(TestAtom const& testAtom, bool pass) + { + return pass ? reportPass(testAtom) : reportFail(testAtom); + } + + Severity getReportableSeverity() const + { + return mReportableSeverity; + } + +private: + //! + //! \brief returns an appropriate string for prefixing a log message with the given severity + //! + static const char* severityPrefix(Severity severity) + { + switch (severity) + { + case Severity::kINTERNAL_ERROR: return "[F] "; + case Severity::kERROR: return "[E] "; + case Severity::kWARNING: return "[W] "; + case Severity::kINFO: return "[I] "; + case Severity::kVERBOSE: return "[V] "; + default: assert(0); return ""; + } + } + + //! + //! \brief returns an appropriate string for prefixing a test result message with the given result + //! + static const char* testResultString(TestResult result) + { + switch (result) + { + case TestResult::kRUNNING: return "RUNNING"; + case TestResult::kPASSED: return "PASSED"; + case TestResult::kFAILED: return "FAILED"; + case TestResult::kWAIVED: return "WAIVED"; + default: assert(0); return ""; + } + } + + //! + //! \brief returns an appropriate output stream (cout or cerr) to use with the given severity + //! + static std::ostream& severityOstream(Severity severity) + { + return severity >= Severity::kINFO ? std::cout : std::cerr; + } + + //! + //! \brief method that implements logging test results + //! + static void reportTestResult(TestAtom const& testAtom, TestResult result) + { + severityOstream(Severity::kINFO) << "&&&& " << testResultString(result) << " " << testAtom.mName << " # " + << testAtom.mCmdline << std::endl; + } + + //! + //! \brief generate a command line string from the given (argc, argv) values + //! + static std::string genCmdlineString(int32_t argc, char const* const* argv) + { + std::stringstream ss; + for (int32_t i = 0; i < argc; i++) + { + if (i > 0) + { + ss << " "; + } + ss << argv[i]; + } + return ss.str(); + } + + Severity mReportableSeverity; +}; // class Logger + +namespace +{ +//! +//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kVERBOSE +//! +//! Example usage: +//! +//! LOG_VERBOSE(logger) << "hello world" << std::endl; +//! +inline LogStreamConsumer LOG_VERBOSE(const Logger& logger) +{ + return LogStreamConsumer(logger.getReportableSeverity(), Severity::kVERBOSE); +} + +//! +//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kINFO +//! +//! Example usage: +//! +//! LOG_INFO(logger) << "hello world" << std::endl; +//! +inline LogStreamConsumer LOG_INFO(const Logger& logger) +{ + return LogStreamConsumer(logger.getReportableSeverity(), Severity::kINFO); +} + +//! +//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kWARNING +//! +//! Example usage: +//! +//! LOG_WARN(logger) << "hello world" << std::endl; +//! +inline LogStreamConsumer LOG_WARN(const Logger& logger) +{ + return LogStreamConsumer(logger.getReportableSeverity(), Severity::kWARNING); +} + +//! +//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kERROR +//! +//! Example usage: +//! +//! LOG_ERROR(logger) << "hello world" << std::endl; +//! +inline LogStreamConsumer LOG_ERROR(const Logger& logger) +{ + return LogStreamConsumer(logger.getReportableSeverity(), Severity::kERROR); +} + +//! +//! \brief produces a LogStreamConsumer object that can be used to log messages of severity kINTERNAL_ERROR +//! ("fatal" severity) +//! +//! Example usage: +//! +//! LOG_FATAL(logger) << "hello world" << std::endl; +//! +inline LogStreamConsumer LOG_FATAL(const Logger& logger) +{ + return LogStreamConsumer(logger.getReportableSeverity(), Severity::kINTERNAL_ERROR); +} +} // anonymous namespace +} // namespace sample +#endif // TENSORRT_LOGGING_H diff --git a/src/Detector/tensorrt_onnx/common/parserOnnxConfig.h b/src/Detector/tensorrt_onnx/common/parserOnnxConfig.h new file mode 100644 index 000000000..67ee6c717 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/parserOnnxConfig.h @@ -0,0 +1,145 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef PARSER_ONNX_CONFIG_H +#define PARSER_ONNX_CONFIG_H + +#include +#include +#include + +#include "NvInfer.h" +#include "NvOnnxConfig.h" +#include "NvOnnxParser.h" + +#define ONNX_DEBUG 1 + +/** + * \class ParserOnnxConfig + * \brief Configuration Manager Class Concrete Implementation + * + * \note: + * + */ + +class ParserOnnxConfig : public nvonnxparser::IOnnxConfig +{ + +protected: + std::string mModelFilename{}; + std::string mTextFilename{}; + std::string mFullTextFilename{}; + nvinfer1::DataType mModelDtype; + nvonnxparser::IOnnxConfig::Verbosity mVerbosity; + bool mPrintLayercInfo; + +public: + ParserOnnxConfig() + : mModelDtype(nvinfer1::DataType::kFLOAT) + , mVerbosity(static_cast(nvinfer1::ILogger::Severity::kWARNING)) + , mPrintLayercInfo(false) + { +#ifdef ONNX_DEBUG + if (isDebug()) + { + std::cout << " ParserOnnxConfig::ctor(): " << this << "\t" << std::endl; + } +#endif + } + + ~ParserOnnxConfig() override + { +#ifdef ONNX_DEBUG + if (isDebug()) + { + std::cout << "ParserOnnxConfig::dtor(): " << this << std::endl; + } +#endif + } + +public: + void setModelDtype(const nvinfer1::DataType modelDtype) noexcept override + { + mModelDtype = modelDtype; + } + + nvinfer1::DataType getModelDtype() const noexcept override + { + return mModelDtype; + } + + const char* getModelFileName() const noexcept override + { + return mModelFilename.c_str(); + } + void setModelFileName(const char* onnxFilename) noexcept override + { + mModelFilename = std::string(onnxFilename); + } + nvonnxparser::IOnnxConfig::Verbosity getVerbosityLevel() const noexcept override + { + return mVerbosity; + } + void addVerbosity() noexcept override + { + ++mVerbosity; + } + void reduceVerbosity() noexcept override + { + --mVerbosity; + } + void setVerbosityLevel(nvonnxparser::IOnnxConfig::Verbosity verbosity) noexcept override + { + mVerbosity = verbosity; + } + + const char* getTextFileName() const noexcept override + { + return mTextFilename.c_str(); + } + void setTextFileName(const char* textFilename) noexcept override + { + mTextFilename = std::string(textFilename); + } + const char* getFullTextFileName() const noexcept override + { + return mFullTextFilename.c_str(); + } + void setFullTextFileName(const char* fullTextFilename) noexcept override + { + mFullTextFilename = std::string(fullTextFilename); + } + bool getPrintLayerInfo() const noexcept override + { + return mPrintLayercInfo; + } + void setPrintLayerInfo(bool src) noexcept override + { + mPrintLayercInfo = src; + } //!< get the boolean variable corresponding to the Layer Info, see getPrintLayerInfo() + + virtual bool isDebug() const noexcept + { +#if ONNX_DEBUG + return (std::getenv("ONNX_DEBUG") ? true : false); +#else + return false; +#endif + } +}; // class ParserOnnxConfig + +#endif diff --git a/src/Detector/tensorrt_onnx/common/safeCommon.h b/src/Detector/tensorrt_onnx/common/safeCommon.h new file mode 100644 index 000000000..2814c44e0 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/safeCommon.h @@ -0,0 +1,384 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef TENSORRT_SAFE_COMMON_H +#define TENSORRT_SAFE_COMMON_H + +#include "cuda_runtime.h" +#include "sampleEntrypoints.h" +#include +#include +#include +#include +#include +#include +#include +#include + +// For safeLoadLibrary +#ifdef _MSC_VER +// Needed so that the max/min definitions in windows.h do not conflict with std::max/min. +#define NOMINMAX +#include +#undef NOMINMAX +#else +#include +#endif +#if IS_QNX_SAFE +#include +#include +#endif // IS_QNX_SAFE + +#undef CHECK +#define CHECK(status) \ + do \ + { \ + auto ret = (status); \ + if (ret != 0) \ + { \ + std::cerr << "Cuda failure: " << ret << std::endl; \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +#undef SAFE_ASSERT +#define SAFE_ASSERT(condition) \ + do \ + { \ + if (!(condition)) \ + { \ + std::cerr << "Assertion failure: " << #condition << std::endl; \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +//! Locate path to file, given its filename or filepath suffix and possible dirs it might lie in. +//! Function will also walk back MAX_DEPTH dirs from CWD to check for such a file path. +inline std::string locateFile( + const std::string& filepathSuffix, const std::vector& directories, bool reportError = true) +{ + const int MAX_DEPTH{10}; + bool found{false}; + std::string filepath; + + for (auto& dir : directories) + { + if (!dir.empty() && dir.back() != '/') + { +#ifdef _MSC_VER + filepath = dir + "\\" + filepathSuffix; +#else + filepath = dir + "/" + filepathSuffix; +#endif + } + else + { + filepath = dir + filepathSuffix; + } + + for (int i = 0; i < MAX_DEPTH && !found; i++) + { + const std::ifstream checkFile(filepath); + found = checkFile.is_open(); + if (found) + { + break; + } + + filepath = "../" + filepath; // Try again in parent dir + } + + if (found) + { + break; + } + + filepath.clear(); + } + + // Could not find the file + if (filepath.empty()) + { + const std::string dirList = std::accumulate(directories.begin() + 1, directories.end(), directories.front(), + [](const std::string& a, const std::string& b) { return a + "\n\t" + b; }); + std::cout << "Could not find " << filepathSuffix << " in data directories:\n\t" << dirList << std::endl; + + if (reportError) + { + std::cout << "&&&& FAILED" << std::endl; + exit(EXIT_FAILURE); + } + } + + return filepath; +} + +inline void readPGMFile(const std::string& fileName, uint8_t* buffer, int32_t inH, int32_t inW) +{ + std::ifstream infile(fileName, std::ifstream::binary); + SAFE_ASSERT(infile.is_open() && "Attempting to read from a file that is not open."); + std::string magic, w, h, max; + infile >> magic >> w >> h >> max; + infile.seekg(1, infile.cur); + infile.read(reinterpret_cast(buffer), inH * inW); +} + +namespace samplesCommon +{ +template +inline std::shared_ptr infer_object(T* obj) +{ + if (!obj) + { + throw std::runtime_error("Failed to create object"); + } + return std::shared_ptr(obj); +} + +inline uint32_t elementSize(nvinfer1::DataType t) +{ + switch (t) + { +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kINT64: return 8; +#endif + case nvinfer1::DataType::kINT32: + case nvinfer1::DataType::kFLOAT: return 4; + case nvinfer1::DataType::kHALF: +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kBF16: return 2; +#endif + case nvinfer1::DataType::kINT8: + case nvinfer1::DataType::kUINT8: + case nvinfer1::DataType::kBOOL: + case nvinfer1::DataType::kFP8: return 1; +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kINT4: + SAFE_ASSERT(false && "Element size is not implemented for sub-byte data-types"); +#endif + } + return 0; +} + +template +inline A divUp(A x, B n) +{ + return (x + n - 1) / n; +} + +inline int64_t volume(nvinfer1::Dims const& d) +{ + return std::accumulate(d.d, d.d + d.nbDims, int64_t{1}, std::multiplies{}); +} + +//! Return m rounded up to nearest multiple of n +template +inline T1 roundUp(T1 m, T2 n) +{ + static_assert(std::is_integral::value && std::is_integral::value, "arguments must be integers"); + static_assert(std::is_signed::value == std::is_signed::value, "mixed signedness not allowed"); + static_assert(sizeof(T1) >= sizeof(T2), "first type must be as least as wide as second type"); + return ((m + n - 1) / n) * n; +} + +//! comps is the number of components in a vector. Ignored if vecDim < 0. +inline int64_t volume(nvinfer1::Dims dims, int32_t vecDim, int32_t comps, int32_t batch) +{ + if (vecDim >= 0) + { + dims.d[vecDim] = roundUp(dims.d[vecDim], comps); + } + return samplesCommon::volume(dims) * std::max(batch, 1); +} + +inline int32_t getSMVersion() +{ +#if 0 + // Use default value for 4090 + int32_t major{8}; + int32_t minor{9}; +#else + int32_t major{}; + int32_t minor{}; + int32_t deviceIndex{}; + CHECK(cudaGetDevice(&deviceIndex)); + CHECK(cudaDeviceGetAttribute(&major, cudaDevAttrComputeCapabilityMajor, deviceIndex)); + CHECK(cudaDeviceGetAttribute(&minor, cudaDevAttrComputeCapabilityMinor, deviceIndex)); +#endif + return ((major << 8) | minor); +} + +inline bool isSMSafe() +{ + const int32_t smVersion = getSMVersion(); + return smVersion == 0x0700 || smVersion == 0x0705 || smVersion == 0x0800 || smVersion == 0x0806 + || smVersion == 0x0807; +} + +inline int32_t calculateSoftmax(float* const prob, int32_t const numDigits) +{ + SAFE_ASSERT(prob != nullptr); + SAFE_ASSERT(numDigits == 10); + float sum{0.0F}; + std::transform(prob, prob + numDigits, prob, [&sum](float v) -> float { + sum += exp(v); + return exp(v); + }); + + SAFE_ASSERT(sum != 0.0F); + std::transform(prob, prob + numDigits, prob, [sum](float v) -> float { return v / sum; }); + int32_t idx = std::max_element(prob, prob + numDigits) - prob; + return idx; +} + +//! +//! \class TrtCudaGraphSafe +//! \brief Managed CUDA graph +//! +class TrtCudaGraphSafe +{ +public: + explicit TrtCudaGraphSafe() = default; + + TrtCudaGraphSafe(const TrtCudaGraphSafe&) = delete; + + TrtCudaGraphSafe& operator=(const TrtCudaGraphSafe&) = delete; + + TrtCudaGraphSafe(TrtCudaGraphSafe&&) = delete; + + TrtCudaGraphSafe& operator=(TrtCudaGraphSafe&&) = delete; + + ~TrtCudaGraphSafe() + { + if (mGraphExec) + { + cudaGraphExecDestroy(mGraphExec); + } + } + + void beginCapture(cudaStream_t& stream) + { + // cudaStreamCaptureModeGlobal is the only allowed mode in SAFE CUDA + CHECK(cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal)); + } + + bool launch(cudaStream_t& stream) + { + return cudaGraphLaunch(mGraphExec, stream) == cudaSuccess; + } + + void endCapture(cudaStream_t& stream) + { + CHECK(cudaStreamEndCapture(stream, &mGraph)); + CHECK(cudaGraphInstantiate(&mGraphExec, mGraph, nullptr, nullptr, 0)); + CHECK(cudaGraphDestroy(mGraph)); + } + + void endCaptureOnError(cudaStream_t& stream) + { + // There are two possibilities why stream capture would fail: + // (1) stream is in cudaErrorStreamCaptureInvalidated state. + // (2) TRT reports a failure. + // In case (1), the returning mGraph should be nullptr. + // In case (2), the returning mGraph is not nullptr, but it should not be used. + const auto ret = cudaStreamEndCapture(stream, &mGraph); + if (ret == cudaErrorStreamCaptureInvalidated) + { + SAFE_ASSERT(mGraph == nullptr); + } + else + { + SAFE_ASSERT(ret == cudaSuccess); + SAFE_ASSERT(mGraph != nullptr); + CHECK(cudaGraphDestroy(mGraph)); + mGraph = nullptr; + } + // Clean up any CUDA error. + cudaGetLastError(); + sample::gLogError << "The CUDA graph capture on the stream has failed." << std::endl; + } + +private: + cudaGraph_t mGraph{}; + cudaGraphExec_t mGraphExec{}; +}; + +inline void safeLoadLibrary(const std::string& path) +{ +#ifdef _MSC_VER + void* handle = LoadLibraryA(path.c_str()); +#else + int32_t flags{RTLD_LAZY}; + void* handle = dlopen(path.c_str(), flags); +#endif + if (handle == nullptr) + { +#ifdef _MSC_VER + sample::gLogError << "Could not load plugin library: " << path << std::endl; +#else + sample::gLogError << "Could not load plugin library: " << path << ", due to: " << dlerror() << std::endl; +#endif + } +} + +inline std::vector safeSplitString(std::string str, char delimiter = ',') +{ + std::vector splitVect; + std::stringstream ss(str); + std::string substr; + + while (ss.good()) + { + getline(ss, substr, delimiter); + splitVect.emplace_back(std::move(substr)); + } + return splitVect; +} + +} // namespace samplesCommon + +namespace safetyCompliance +{ +inline void initSafeCuda() +{ + // According to CUDA initialization in NVIDIA CUDA SAFETY API REFERENCE FOR DRIVE OS + // We will need to do the following in order + // 1. Initialize the calling thread with CUDA specific information (Call any CUDA RT API identified as init) + // 2. Query/Configure and choose the desired CUDA device + // 3. CUDA context initialization. (Call cudaDeviceGetLimit or cuCtxCreate) + size_t stackSizeLimit = 0; + int32_t deviceIndex = 0; + CHECK(cudaGetDevice(&deviceIndex)); + CHECK(cudaDeviceGetLimit(&stackSizeLimit, cudaLimitStackSize)); +#if IS_QNX_SAFE + CHECK(cudaSafeExSelectAPIMode(cudaSafeExAPIModeAsilB)); +#endif // IS_QNX_SAFE +} + +inline void setPromgrAbility() +{ +#if IS_QNX_SAFE + // Comply with DEEPLRN_RES_117 on QNX-safe by dropping PROCMGR_AID_MEM_PHYS ability and locking out any further + // changes + procmgr_ability( + 0, PROCMGR_ADN_NONROOT | PROCMGR_AOP_DENY | PROCMGR_AOP_LOCK | PROCMGR_AID_MEM_PHYS, PROCMGR_AID_EOL); +#endif // IS_QNX_SAFE +} + +} // namespace safetyCompliance + +#endif // TENSORRT_SAFE_COMMON_H diff --git a/src/Detector/tensorrt_onnx/common/sampleConfig.h b/src/Detector/tensorrt_onnx/common/sampleConfig.h new file mode 100644 index 000000000..801a268a4 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleConfig.h @@ -0,0 +1,331 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef SampleConfig_H +#define SampleConfig_H + +#include +#include +#include + +#include "NvInfer.h" +#include "NvOnnxConfig.h" +class SampleConfig : public nvonnxparser::IOnnxConfig +{ +public: + enum class InputDataFormat : int + { + kASCII = 0, + kPPM = 1 + }; + +private: + std::string mModelFilename; + std::string mEngineFilename; + std::string mTextFilename; + std::string mFullTextFilename; + std::string mImageFilename; + std::string mReferenceFilename; + std::string mOutputFilename; + std::string mCalibrationFilename; + std::string mTimingCacheFilename; + int64_t mLabel{-1}; + int64_t mMaxBatchSize{32}; + int64_t mCalibBatchSize{0}; + int64_t mMaxNCalibBatch{0}; + int64_t mFirstCalibBatch{0}; + int64_t mUseDLACore{-1}; + nvinfer1::DataType mModelDtype{nvinfer1::DataType::kFLOAT}; + bool mTF32{true}; + Verbosity mVerbosity{static_cast(nvinfer1::ILogger::Severity::kWARNING)}; + bool mPrintLayercInfo{false}; + bool mDebugBuilder{false}; + InputDataFormat mInputDataFormat{InputDataFormat::kASCII}; + uint64_t mTopK{0}; + float mFailurePercentage{-1.0F}; + float mTolerance{0.0F}; + float mAbsTolerance{1e-5F}; + +public: + SampleConfig() + { +#ifdef ONNX_DEBUG + if (isDebug()) + { + std::cout << " SampleConfig::ctor(): " << this << "\t" << std::endl; + } +#endif + } + + ~SampleConfig() override + { +#ifdef ONNX_DEBUG + if (isDebug()) + { + std::cout << "SampleConfig::dtor(): " << this << std::endl; + } +#endif + } + +public: + void setModelDtype(const nvinfer1::DataType mdt) noexcept override + { + mModelDtype = mdt; + } + + nvinfer1::DataType getModelDtype() const noexcept override + { + return mModelDtype; + } + + bool getTF32() const noexcept + { + return mTF32; + } + + void setTF32(bool enabled) noexcept + { + mTF32 = enabled; + } + + const char* getModelFileName() const noexcept override + { + return mModelFilename.c_str(); + } + + void setModelFileName(const char* onnxFilename) noexcept override + { + mModelFilename = std::string(onnxFilename); + } + Verbosity getVerbosityLevel() const noexcept override + { + return mVerbosity; + } + void addVerbosity() noexcept override + { + ++mVerbosity; + } + void reduceVerbosity() noexcept override + { + --mVerbosity; + } + void setVerbosityLevel(Verbosity v) noexcept override + { + mVerbosity = v; + } + const char* getEngineFileName() const noexcept + { + return mEngineFilename.c_str(); + } + void setEngineFileName(const char* engineFilename) noexcept + { + mEngineFilename = std::string(engineFilename); + } + const char* getTextFileName() const noexcept override + { + return mTextFilename.c_str(); + } + void setTextFileName(const char* textFilename) noexcept override + { + mTextFilename = std::string(textFilename); + } + const char* getFullTextFileName() const noexcept override + { + return mFullTextFilename.c_str(); + } + void setFullTextFileName(const char* fullTextFilename) noexcept override + { + mFullTextFilename = std::string(fullTextFilename); + } + void setLabel(int64_t label) noexcept + { + mLabel = label; + } //!< set the Label + + int64_t getLabel() const noexcept + { + return mLabel; + } //!< get the Label + + bool getPrintLayerInfo() const noexcept override + { + return mPrintLayercInfo; + } + + void setPrintLayerInfo(bool b) noexcept override + { + mPrintLayercInfo = b; + } //!< get the boolean variable corresponding to the Layer Info, see getPrintLayerInfo() + + void setMaxBatchSize(int64_t maxBatchSize) noexcept + { + mMaxBatchSize = maxBatchSize; + } //!< set the Max Batch Size + int64_t getMaxBatchSize() const noexcept + { + return mMaxBatchSize; + } //!< get the Max Batch Size + + void setCalibBatchSize(int64_t CalibBatchSize) noexcept + { + mCalibBatchSize = CalibBatchSize; + } //!< set the calibration batch size + int64_t getCalibBatchSize() const noexcept + { + return mCalibBatchSize; + } //!< get calibration batch size + + void setMaxNCalibBatch(int64_t MaxNCalibBatch) noexcept + { + mMaxNCalibBatch = MaxNCalibBatch; + } //!< set Max Number of Calibration Batches + int64_t getMaxNCalibBatch() const noexcept + { + return mMaxNCalibBatch; + } //!< get the Max Number of Calibration Batches + + void setFirstCalibBatch(int64_t FirstCalibBatch) noexcept + { + mFirstCalibBatch = FirstCalibBatch; + } //!< set the first calibration batch + int64_t getFirstCalibBatch() const noexcept + { + return mFirstCalibBatch; + } //!< get the first calibration batch + + void setUseDLACore(int64_t UseDLACore) noexcept + { + mUseDLACore = UseDLACore; + } //!< set the DLA core to use + int64_t getUseDLACore() const noexcept + { + return mUseDLACore; + } //!< get the DLA core to use + + void setDebugBuilder() noexcept + { + mDebugBuilder = true; + } //!< enable the Debug info, while building the engine. + bool getDebugBuilder() const noexcept + { + return mDebugBuilder; + } //!< get the boolean variable, corresponding to the debug builder + + const char* getImageFileName() const noexcept //!< set Image file name (PPM or ASCII) + { + return mImageFilename.c_str(); + } + void setImageFileName(const char* imageFilename) noexcept //!< get the Image file name + { + mImageFilename = std::string(imageFilename); + } + const char* getReferenceFileName() const noexcept + { + return mReferenceFilename.c_str(); + } + void setReferenceFileName(const char* referenceFilename) noexcept //!< set reference file name + { + mReferenceFilename = std::string(referenceFilename); + } + + void setInputDataFormat(InputDataFormat idt) noexcept + { + mInputDataFormat = idt; + } //!< specifies expected data format of the image file (PPM or ASCII) + InputDataFormat getInputDataFormat() const noexcept + { + return mInputDataFormat; + } //!< returns the expected data format of the image file. + + const char* getOutputFileName() const noexcept //!< specifies the file to save the results + { + return mOutputFilename.c_str(); + } + void setOutputFileName(const char* outputFilename) noexcept //!< get the output file name + { + mOutputFilename = std::string(outputFilename); + } + + const char* getCalibrationFileName() const noexcept + { + return mCalibrationFilename.c_str(); + } //!< specifies the file containing the list of image files for int8 calibration + void setCalibrationFileName(const char* calibrationFilename) noexcept //!< get the int 8 calibration list file name + { + mCalibrationFilename = std::string(calibrationFilename); + } + + uint64_t getTopK() const noexcept + { + return mTopK; + } + void setTopK(uint64_t topK) noexcept + { + mTopK = topK; + } //!< If this options is specified, return the K top probabilities. + + float getFailurePercentage() const noexcept + { + return mFailurePercentage; + } + + void setFailurePercentage(float f) noexcept + { + mFailurePercentage = f; + } + + float getAbsoluteTolerance() const noexcept + { + return mAbsTolerance; + } + + void setAbsoluteTolerance(float a) noexcept + { + mAbsTolerance = a; + } + + float getTolerance() const noexcept + { + return mTolerance; + } + + void setTolerance(float t) noexcept + { + mTolerance = t; + } + + const char* getTimingCacheFilename() const noexcept + { + return mTimingCacheFilename.c_str(); + } + + void setTimingCacheFileName(const char* timingCacheFilename) noexcept + { + mTimingCacheFilename = std::string(timingCacheFilename); + } + + bool isDebug() const noexcept + { +#if ONNX_DEBUG + return (std::getenv("ONNX_DEBUG") ? true : false); +#else + return false; +#endif + } +}; // class SampleConfig + +#endif diff --git a/src/Detector/tensorrt_onnx/common/sampleDevice.cpp b/src/Detector/tensorrt_onnx/common/sampleDevice.cpp new file mode 100644 index 000000000..7964aeb5d --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleDevice.cpp @@ -0,0 +1,133 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#include "sampleDevice.h" + +#include + +namespace sample +{ + +void cudaCheck(cudaError_t ret, std::ostream& err) +{ + if (ret != cudaSuccess) + { + err << "Cuda failure: " << cudaGetErrorString(ret) << std::endl; + exit(EXIT_FAILURE); + } +} + +// Construct GPU UUID string in the same format as nvidia-smi does. +std::string getUuidString(cudaUUID_t uuid) +{ + constexpr int32_t kUUID_SIZE = sizeof(cudaUUID_t); + static_assert(kUUID_SIZE == 16, "Unexpected size for cudaUUID_t!"); + + std::ostringstream ss; + std::vector const splits = {0, 4, 6, 8, 10, kUUID_SIZE}; + + ss << "GPU" << std::hex << std::setfill('0'); + for (int32_t splitIdx = 0; splitIdx < static_cast(splits.size()) - 1; ++splitIdx) + { + ss << "-"; + for (int32_t byteIdx = splits[splitIdx]; byteIdx < splits[splitIdx + 1]; ++byteIdx) + { + ss << std::setw(2) << +static_cast(uuid.bytes[byteIdx]); + } + } + return ss.str(); +} + +void setCudaDevice(int32_t device, std::ostream& os) +{ +#if !TRT_WINML + os << "=== Device Information ===" << std::endl; + + // Get the number of visible GPUs. + int32_t nbDevices{-1}; + cudaCheck(cudaGetDeviceCount(&nbDevices)); + + if (nbDevices <= 0) + { + os << "Cannot find any available devices (GPUs)!" << std::endl; + exit(EXIT_FAILURE); + } + + // Print out the GPU name and PCIe bus ID of each GPU. + os << "Available Devices: " << std::endl; + cudaDeviceProp properties; + for (int32_t deviceIdx = 0; deviceIdx < nbDevices; ++deviceIdx) + { + cudaDeviceProp tempProperties; + cudaCheck(cudaGetDeviceProperties(&tempProperties, deviceIdx)); + + // clang-format off + os << " Device " << deviceIdx << ": \"" << tempProperties.name << "\" UUID: " + << getUuidString(tempProperties.uuid) << std::endl; + // clang-format on + + // Record the properties of the desired GPU. + if (deviceIdx == device) + { + properties = tempProperties; + } + } + + // Exit with error if the requested device ID does not exist. + if (device < 0 || device >= nbDevices) + { + os << "Cannot find device ID " << device << "!" << std::endl; + exit(EXIT_FAILURE); + } + + // Set to the corresponding GPU. + cudaCheck(cudaSetDevice(device)); + + // clang-format off + os << "Selected Device: " << properties.name << std::endl; + os << "Selected Device ID: " << device << std::endl; + os << "Selected Device UUID: " << getUuidString(properties.uuid) << std::endl; + os << "Compute Capability: " << properties.major << "." << properties.minor << std::endl; + os << "SMs: " << properties.multiProcessorCount << std::endl; + os << "Device Global Memory: " << (properties.totalGlobalMem >> 20) << " MiB" << std::endl; + os << "Shared Memory per SM: " << (properties.sharedMemPerMultiprocessor >> 10) << " KiB" << std::endl; + os << "Memory Bus Width: " << properties.memoryBusWidth << " bits" + << " (ECC " << (properties.ECCEnabled != 0 ? "enabled" : "disabled") << ")" << std::endl; + os << "Application Compute Clock Rate: " << properties.clockRate / 1000000.0F << " GHz" << std::endl; + os << "Application Memory Clock Rate: " << properties.memoryClockRate / 1000000.0F << " GHz" << std::endl; + os << std::endl; + os << "Note: The application clock rates do not reflect the actual clock rates that the GPU is " + << "currently running at." << std::endl; + // clang-format on +#endif +} + +int32_t getCudaDriverVersion() +{ + int32_t version{-1}; + cudaCheck(cudaDriverGetVersion(&version)); + return version; +} + +int32_t getCudaRuntimeVersion() +{ + int32_t version{-1}; + cudaCheck(cudaRuntimeGetVersion(&version)); + return version; +} + +} // namespace sample diff --git a/src/Detector/tensorrt_onnx/common/sampleDevice.h b/src/Detector/tensorrt_onnx/common/sampleDevice.h new file mode 100644 index 000000000..d28f02ed5 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleDevice.h @@ -0,0 +1,554 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef TRT_SAMPLE_DEVICE_H +#define TRT_SAMPLE_DEVICE_H + +#include +#include +#include +#include +#include + +#include "sampleUtils.h" + +namespace sample +{ + +//! Check if the CUDA return status shows any error. If so, exit the program immediately. +void cudaCheck(cudaError_t ret, std::ostream& err = std::cerr); + +class TrtCudaEvent; + +namespace +{ + +void cudaSleep(void* sleep) +{ + std::this_thread::sleep_for(std::chrono::duration(*static_cast(sleep))); +} + +} // namespace + +//! +//! \class TrtCudaStream +//! \brief Managed CUDA stream +//! +class TrtCudaStream +{ +public: + TrtCudaStream() + { + cudaCheck(cudaStreamCreate(&mStream)); + } + + TrtCudaStream(const TrtCudaStream&) = delete; + + TrtCudaStream& operator=(const TrtCudaStream&) = delete; + + TrtCudaStream(TrtCudaStream&&) = delete; + + TrtCudaStream& operator=(TrtCudaStream&&) = delete; + + ~TrtCudaStream() + { + cudaCheck(cudaStreamDestroy(mStream)); + } + + cudaStream_t get() const + { + return mStream; + } + + void synchronize() + { + cudaCheck(cudaStreamSynchronize(mStream)); + } + + void wait(TrtCudaEvent& event); + + void sleep(float* ms) + { + cudaCheck(cudaLaunchHostFunc(mStream, cudaSleep, ms)); + } + +private: + cudaStream_t mStream{}; +}; + +//! +//! \class TrtCudaEvent +//! \brief Managed CUDA event +//! +class TrtCudaEvent +{ +public: + explicit TrtCudaEvent(bool blocking = true) + { + const uint32_t flags = blocking ? cudaEventBlockingSync : cudaEventDefault; + cudaCheck(cudaEventCreateWithFlags(&mEvent, flags)); + } + + TrtCudaEvent(const TrtCudaEvent&) = delete; + + TrtCudaEvent& operator=(const TrtCudaEvent&) = delete; + + TrtCudaEvent(TrtCudaEvent&&) = delete; + + TrtCudaEvent& operator=(TrtCudaEvent&&) = delete; + + ~TrtCudaEvent() + { + cudaCheck(cudaEventDestroy(mEvent)); + } + + cudaEvent_t get() const + { + return mEvent; + } + + void record(const TrtCudaStream& stream) + { + cudaCheck(cudaEventRecord(mEvent, stream.get())); + } + + void synchronize() + { + cudaCheck(cudaEventSynchronize(mEvent)); + } + + // Returns time elapsed time in milliseconds + float operator-(const TrtCudaEvent& e) const + { + float time{0}; + cudaCheck(cudaEventElapsedTime(&time, e.get(), get())); + return time; + } + +private: + cudaEvent_t mEvent{}; +}; + +inline void TrtCudaStream::wait(TrtCudaEvent& event) +{ + cudaCheck(cudaStreamWaitEvent(mStream, event.get(), 0)); +} + +//! +//! \class TrtCudaGraph +//! \brief Managed CUDA graph +//! +class TrtCudaGraph +{ +public: + explicit TrtCudaGraph() = default; + + TrtCudaGraph(const TrtCudaGraph&) = delete; + + TrtCudaGraph& operator=(const TrtCudaGraph&) = delete; + + TrtCudaGraph(TrtCudaGraph&&) = delete; + + TrtCudaGraph& operator=(TrtCudaGraph&&) = delete; + + ~TrtCudaGraph() + { + if (mGraphExec) + { + cudaGraphExecDestroy(mGraphExec); + } + } + + void beginCapture(TrtCudaStream& stream) + { + cudaCheck(cudaStreamBeginCapture(stream.get(), cudaStreamCaptureModeThreadLocal)); + } + + bool launch(TrtCudaStream& stream) + { + return cudaGraphLaunch(mGraphExec, stream.get()) == cudaSuccess; + } + + void endCapture(TrtCudaStream& stream) + { + cudaCheck(cudaStreamEndCapture(stream.get(), &mGraph)); + cudaCheck(cudaGraphInstantiate(&mGraphExec, mGraph, nullptr, nullptr, 0)); + cudaCheck(cudaGraphDestroy(mGraph)); + } + + void endCaptureOnError(TrtCudaStream& stream) + { + // There are two possibilities why stream capture would fail: + // (1) stream is in cudaErrorStreamCaptureInvalidated state. + // (2) TRT reports a failure. + // In case (1), the returning mGraph should be nullptr. + // In case (2), the returning mGraph is not nullptr, but it should not be used. + const auto ret = cudaStreamEndCapture(stream.get(), &mGraph); + if (ret == cudaErrorStreamCaptureInvalidated) + { + assert(mGraph == nullptr); + } + else + { + assert(ret == cudaSuccess); + assert(mGraph != nullptr); + cudaCheck(cudaGraphDestroy(mGraph)); + mGraph = nullptr; + } + // Clean up any CUDA error. + cudaGetLastError(); + sample::gLogWarning << "The CUDA graph capture on the stream has failed." << std::endl; + } + +private: + cudaGraph_t mGraph{}; + cudaGraphExec_t mGraphExec{}; +}; + +//! +//! \class TrtCudaBuffer +//! \brief Managed buffer for host and device +//! +template +class TrtCudaBuffer +{ +public: + TrtCudaBuffer() = default; + + TrtCudaBuffer(const TrtCudaBuffer&) = delete; + + TrtCudaBuffer& operator=(const TrtCudaBuffer&) = delete; + + TrtCudaBuffer(TrtCudaBuffer&& rhs) + { + reset(rhs.mPtr, rhs.mSize); + rhs.mPtr = nullptr; + rhs.mSize = 0; + } + + TrtCudaBuffer& operator=(TrtCudaBuffer&& rhs) + { + if (this != &rhs) + { + reset(rhs.mPtr, rhs.mSize); + rhs.mPtr = nullptr; + rhs.mSize = 0; + } + return *this; + } + + ~TrtCudaBuffer() + { + reset(); + } + + TrtCudaBuffer(size_t size) + { + A()(&mPtr, size); + mSize = size; + } + + void allocate(size_t size) + { + reset(); + A()(&mPtr, size); + mSize = size; + } + + void reset(void* ptr = nullptr, size_t size = 0) + { + if (mPtr) + { + D()(mPtr); + } + mPtr = ptr; + mSize = size; + } + + void* get() const + { + return mPtr; + } + + size_t getSize() const + { + return mSize; + } + +private: + void* mPtr{nullptr}; + size_t mSize{0}; +}; + +struct DeviceAllocator +{ + void operator()(void** ptr, size_t size) + { + cudaCheck(cudaMalloc(ptr, size)); + } +}; + +struct DeviceDeallocator +{ + void operator()(void* ptr) + { + cudaCheck(cudaFree(ptr)); + } +}; + +struct ManagedAllocator +{ + void operator()(void** ptr, size_t size) + { + cudaCheck(cudaMallocManaged(ptr, size)); + } +}; + +struct HostAllocator +{ + void operator()(void** ptr, size_t size) + { + cudaCheck(cudaMallocHost(ptr, size)); + } +}; + +struct HostDeallocator +{ + void operator()(void* ptr) + { + cudaCheck(cudaFreeHost(ptr)); + } +}; + +using TrtDeviceBuffer = TrtCudaBuffer; +using TrtManagedBuffer = TrtCudaBuffer; + +using TrtHostBuffer = TrtCudaBuffer; + +//! +//! \class MirroredBuffer +//! \brief Coupled host and device buffers +//! +class IMirroredBuffer +{ +public: + //! + //! Allocate memory for the mirrored buffer give the size + //! of the allocation. + //! + virtual void allocate(size_t size) = 0; + + //! + //! Get the pointer to the device side buffer. + //! + //! \return pointer to device memory or nullptr if uninitialized. + //! + virtual void* getDeviceBuffer() const = 0; + + //! + //! Get the pointer to the host side buffer. + //! + //! \return pointer to host memory or nullptr if uninitialized. + //! + virtual void* getHostBuffer() const = 0; + + //! + //! Copy the memory from host to device. + //! + virtual void hostToDevice(TrtCudaStream& stream) = 0; + + //! + //! Copy the memory from device to host. + //! + virtual void deviceToHost(TrtCudaStream& stream) = 0; + + //! + //! Interface to get the size of the memory + //! + //! \return the size of memory allocated. + //! + virtual size_t getSize() const = 0; + + //! + //! Virtual destructor declaraion + //! + virtual ~IMirroredBuffer() = default; + +}; // class IMirroredBuffer + +//! +//! Class to have a separate memory buffer for discrete device and host allocations. +//! +class DiscreteMirroredBuffer : public IMirroredBuffer +{ +public: + void allocate(size_t size) override + { + mSize = size; + mHostBuffer.allocate(size); + mDeviceBuffer.allocate(size); + } + + void* getDeviceBuffer() const override + { + return mDeviceBuffer.get(); + } + + void* getHostBuffer() const override + { + return mHostBuffer.get(); + } + + void hostToDevice(TrtCudaStream& stream) override + { + cudaCheck(cudaMemcpyAsync(mDeviceBuffer.get(), mHostBuffer.get(), mSize, cudaMemcpyHostToDevice, stream.get())); + } + + void deviceToHost(TrtCudaStream& stream) override + { + cudaCheck(cudaMemcpyAsync(mHostBuffer.get(), mDeviceBuffer.get(), mSize, cudaMemcpyDeviceToHost, stream.get())); + } + + size_t getSize() const override + { + return mSize; + } + +private: + size_t mSize{0}; + TrtHostBuffer mHostBuffer; + TrtDeviceBuffer mDeviceBuffer; +}; // class DiscreteMirroredBuffer + +//! +//! Class to have a unified memory buffer for embedded devices. +//! +class UnifiedMirroredBuffer : public IMirroredBuffer +{ +public: + void allocate(size_t size) override + { + mSize = size; + mBuffer.allocate(size); + } + + void* getDeviceBuffer() const override + { + return mBuffer.get(); + } + + void* getHostBuffer() const override + { + return mBuffer.get(); + } + + void hostToDevice(TrtCudaStream& stream) override + { + // Does nothing since we are using unified memory. + } + + void deviceToHost(TrtCudaStream& stream) override + { + // Does nothing since we are using unified memory. + } + + size_t getSize() const override + { + return mSize; + } + +private: + size_t mSize{0}; + TrtManagedBuffer mBuffer; +}; // class UnifiedMirroredBuffer + +//! +//! Class to allocate memory for outputs with data-dependent shapes. The sizes of those are unknown so pre-allocation is +//! not possible. +//! +class OutputAllocator : public nvinfer1::IOutputAllocator +{ +public: + OutputAllocator(IMirroredBuffer* buffer) + : mBuffer(buffer) + { + } + + void* reallocateOutput( + char const* tensorName, void* currentMemory, uint64_t size, uint64_t alignment) noexcept override + { + // Some memory allocators return nullptr when allocating zero bytes, but TensorRT requires a non-null ptr + // even for empty tensors, so allocate a dummy byte. + size = std::max(size, static_cast(1)); + if (size > mSize) + { + mBuffer->allocate(roundUp(size, alignment)); + mSize = size; + } + return mBuffer->getDeviceBuffer(); + } + + //! IMirroredBuffer does not implement Async allocation, hence this is just a wrap around +#if (NV_TENSORRT_MAJOR > 8) + void* reallocateOutputAsync(char const* tensorName, void* currentMemory, uint64_t size, uint64_t alignment, + cudaStream_t /*stream*/) noexcept override + { + return reallocateOutput(tensorName, currentMemory, size, alignment); + } +#else + void* reallocateOutputAsync(char const* tensorName, void* currentMemory, uint64_t size, uint64_t alignment, + cudaStream_t /*stream*/) noexcept + { + return reallocateOutput(tensorName, currentMemory, size, alignment); + } +#endif + + void notifyShape(char const* tensorName, nvinfer1::Dims const& dims) noexcept override + { + mFinalDims = dims; + } + + IMirroredBuffer* getBuffer() + { + return mBuffer.get(); + } + + nvinfer1::Dims getFinalDims() + { + return mFinalDims; + } + + ~OutputAllocator() override {} + +private: + std::unique_ptr mBuffer; + uint64_t mSize{}; + nvinfer1::Dims mFinalDims; +}; + +//! Set the GPU to run the inference on. +void setCudaDevice(int32_t device, std::ostream& os); + +//! Get the CUDA version of the current CUDA driver. +int32_t getCudaDriverVersion(); + +//! Get the CUDA version of the current CUDA runtime. +int32_t getCudaRuntimeVersion(); + +} // namespace sample + +#endif // TRT_SAMPLE_DEVICE_H diff --git a/src/Detector/tensorrt_onnx/common/sampleEngines.cpp b/src/Detector/tensorrt_onnx/common/sampleEngines.cpp new file mode 100644 index 000000000..dcdfdf2d1 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleEngines.cpp @@ -0,0 +1,1739 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "NvInfer.h" +#include "NvOnnxParser.h" + +#include "ErrorRecorder.h" +#include "common.h" +#include "half.h" +#include "logger.h" +#include "sampleDevice.h" +#include "sampleEngines.h" +#include "sampleOptions.h" +#include "sampleUtils.h" + +using namespace nvinfer1; + +namespace sample +{ + +namespace +{ + +std::map readScalesFromCalibrationCache(std::string const& calibrationFile) +{ + std::map tensorScales; + std::ifstream cache{calibrationFile}; + if (!cache.is_open()) + { + sample::gLogError << "[TRT] Can not open provided calibration cache file" << std::endl; + return tensorScales; + } + std::string line; + while (std::getline(cache, line)) + { + auto colonPos = line.find_last_of(':'); + if (colonPos != std::string::npos) + { + // Scales should be stored in calibration cache as 32-bit floating numbers encoded as 32-bit integers + int32_t scalesAsInt = std::stoi(line.substr(colonPos + 2, 8), nullptr, 16); + auto const tensorName = line.substr(0, colonPos); + tensorScales[tensorName] = *reinterpret_cast(&scalesAsInt); + } + } + cache.close(); + return tensorScales; +} +} // namespace + +nvinfer1::ICudaEngine* LazilyDeserializedEngine::get() +{ + SMP_RETVAL_IF_FALSE( + !mIsSafe, "Safe mode is enabled, but trying to get standard engine!", nullptr, sample::gLogError); + + if (mEngine == nullptr) + { +#if (NV_TENSORRT_MAJOR > 8) + SMP_RETVAL_IF_FALSE(getFileReader().isOpen() || !getBlob().empty(), "Engine is empty. Nothing to deserialize!", + nullptr, sample::gLogError); +#endif + using time_point = std::chrono::time_point; + using duration = std::chrono::duration; + time_point const deserializeStartTime{std::chrono::high_resolution_clock::now()}; + + if (mLeanDLLPath.empty()) + { + mRuntime.reset(createRuntime()); + } + else + { + mParentRuntime.reset(createRuntime()); + ASSERT(mParentRuntime.get() != nullptr); + + mRuntime.reset(mParentRuntime->loadRuntime(mLeanDLLPath.c_str())); + } + ASSERT(mRuntime.get() != nullptr); + + if (mVersionCompatible) + { + // Application needs to opt into allowing deserialization of engines with embedded lean runtime. + mRuntime->setEngineHostCodeAllowed(true); + } + + if (!mTempdir.empty()) + { + mRuntime->setTemporaryDirectory(mTempdir.c_str()); + } + + mRuntime->setTempfileControlFlags(mTempfileControls); + + SMP_RETVAL_IF_FALSE(mRuntime != nullptr, "runtime creation failed", nullptr, sample::gLogError); + if (mDLACore != -1) + { + mRuntime->setDLACore(mDLACore); + } + mRuntime->setErrorRecorder(&gRecorder); +#if !TRT_WINML + for (auto const& pluginPath : mDynamicPlugins) + { + mRuntime->getPluginRegistry().loadLibrary(pluginPath.c_str()); + } +#endif + +#if (NV_TENSORRT_MAJOR > 8) + if (getFileReader().isOpen()) + { + mEngine.reset(mRuntime->deserializeCudaEngine(getFileReader())); + } + else + { + auto const& engineBlob = getBlob(); + mEngine.reset(mRuntime->deserializeCudaEngine(engineBlob.data, engineBlob.size)); + } +#else + auto const& engineBlob = getBlob(); + mEngine.reset(mRuntime->deserializeCudaEngine(engineBlob.data, engineBlob.size)); + std::cerr << "getFileReader is not implemented! Use TensorRT 10.x and higher" << std::endl; +#endif + SMP_RETVAL_IF_FALSE(mEngine != nullptr, "Engine deserialization failed", nullptr, sample::gLogError); + + time_point const deserializeEndTime{std::chrono::high_resolution_clock::now()}; + sample::gLogInfo << "Engine deserialized in " << duration(deserializeEndTime - deserializeStartTime).count() + << " sec." << std::endl; + } + + return mEngine.get(); +} + +nvinfer1::ICudaEngine* LazilyDeserializedEngine::release() +{ + return mEngine.release(); +} + +void setTensorScalesFromCalibration(nvinfer1::INetworkDefinition& network, std::vector const& inputFormats, + std::vector const& outputFormats, std::string const& calibrationFile) +{ + auto const tensorScales = readScalesFromCalibrationCache(calibrationFile); + bool const broadcastInputFormats = broadcastIOFormats(inputFormats, network.getNbInputs()); + for (int32_t i = 0, n = network.getNbInputs(); i < n; ++i) + { + int32_t formatIdx = broadcastInputFormats ? 0 : i; + if (!inputFormats.empty() && inputFormats[formatIdx].first == DataType::kINT8) + { + auto* input = network.getInput(i); + auto const calibScale = tensorScales.at(input->getName()); + input->setDynamicRange(-127 * calibScale, 127 * calibScale); + } + } + bool const broadcastOutputFormats = broadcastIOFormats(outputFormats, network.getNbOutputs()); + for (int32_t i = 0, n = network.getNbOutputs(); i < n; ++i) + { + int32_t formatIdx = broadcastOutputFormats ? 0 : i; + if (!outputFormats.empty() && outputFormats[formatIdx].first == DataType::kINT8) + { + auto* output = network.getOutput(i); + auto const calibScale = tensorScales.at(output->getName()); + output->setDynamicRange(-127 * calibScale, 127 * calibScale); + } + } +} + +//! +//! \brief Generate a network definition for a given model +//! +//! \param[in] model Model options for this network +//! \param[in,out] network Network storing the parsed results +//! \param[in,out] err Error stream +//! \param[out] vcPluginLibrariesUsed If not nullptr, will be populated with paths to VC plugin libraries required by +//! the parsed network. +//! +//! \return Parser The parser used to initialize the network and that holds the weights for the network, or an invalid +//! parser (the returned parser converts to false if tested) +//! +//! Constant input dimensions in the model must not be changed in the corresponding +//! network definition, because its correctness may rely on the constants. +//! +//! \see Parser::operator bool() +//! +Parser modelToNetwork(ModelOptions const& model, BuildOptions const& build, nvinfer1::INetworkDefinition& network, + std::ostream& err, std::vector* vcPluginLibrariesUsed) +{ + sample::gLogInfo << "Start parsing network model." << std::endl; + auto const tBegin = std::chrono::high_resolution_clock::now(); + + Parser parser; + switch (model.baseModel.format) + { + case ModelFormat::kONNX: + { + using namespace nvonnxparser; + parser.onnxParser.reset(createONNXParser(network)); + ASSERT(parser.onnxParser != nullptr); +#if !TRT_WINML + // kNATIVE_INSTANCENORM is ON by default in the parser and must be cleared to use the plugin implementation. + if (build.pluginInstanceNorm) + { + parser.onnxParser->clearFlag(OnnxParserFlag::kNATIVE_INSTANCENORM); + } +#endif + if (!parser.onnxParser->parseFromFile( + model.baseModel.model.c_str(), static_cast(sample::gLogger.getReportableSeverity()))) + { + err << "Failed to parse onnx file" << std::endl; + parser.onnxParser.reset(); + } +#if !TRT_WINML + if (vcPluginLibrariesUsed && parser.onnxParser.get()) + { + int64_t nbPluginLibs; + char const* const* pluginLibArray = parser.onnxParser->getUsedVCPluginLibraries(nbPluginLibs); + if (nbPluginLibs >= 0) + { + vcPluginLibrariesUsed->reserve(nbPluginLibs); + for (int64_t i = 0; i < nbPluginLibs; ++i) + { + sample::gLogInfo << "Using VC plugin library " << pluginLibArray[i] << std::endl; + vcPluginLibrariesUsed->emplace_back(std::string{pluginLibArray[i]}); + } + } + else + { + sample::gLogWarning << "Failure to query VC plugin libraries required by parsed ONNX network" + << std::endl; + } + } +#endif + break; + } + case ModelFormat::kANY: break; + } + + auto const tEnd = std::chrono::high_resolution_clock::now(); + float const parseTime = std::chrono::duration(tEnd - tBegin).count(); + + sample::gLogInfo << "Finished parsing network model. Parse time: " << parseTime << std::endl; + return parser; +} + +namespace +{ + +class RndInt8Calibrator : public nvinfer1::IInt8EntropyCalibrator2 +{ +public: + RndInt8Calibrator(int32_t batches, std::vector& elemCount, std::string const& cacheFile, + nvinfer1::INetworkDefinition const& network, std::ostream& err); + + ~RndInt8Calibrator() override + { + for (auto& elem : mInputDeviceBuffers) + { + cudaCheck(cudaFree(elem.second), mErr); + } + } + + bool getBatch(void* bindings[], char const* names[], int32_t nbBindings) noexcept override; + + int32_t getBatchSize() const noexcept override + { + return 1; + } + + const void* readCalibrationCache(size_t& length) noexcept override; + + void writeCalibrationCache(void const*, size_t) noexcept override {} + +private: + int32_t mBatches{}; + int32_t mCurrentBatch{}; + std::string mCacheFile; + std::map mInputDeviceBuffers; + std::vector mCalibrationCache; + std::ostream& mErr; +}; + +RndInt8Calibrator::RndInt8Calibrator(int32_t batches, std::vector& elemCount, std::string const& cacheFile, + INetworkDefinition const& network, std::ostream& err) + : mBatches(batches) + , mCurrentBatch(0) + , mCacheFile(cacheFile) + , mErr(err) +{ + std::ifstream tryCache(cacheFile, std::ios::binary); + if (tryCache.good()) + { + return; + } + + std::default_random_engine generator; + std::uniform_real_distribution distribution(-1.0F, 1.0F); + auto gen = [&generator, &distribution]() { return distribution(generator); }; + + for (int32_t i = 0; i < network.getNbInputs(); i++) + { + auto* input = network.getInput(i); + std::vector rnd_data(elemCount[i]); + std::generate_n(rnd_data.begin(), elemCount[i], gen); + + void* data; + cudaCheck(cudaMalloc(&data, elemCount[i] * sizeof(float)), mErr); + cudaCheck(cudaMemcpy(data, rnd_data.data(), elemCount[i] * sizeof(float), cudaMemcpyHostToDevice), mErr); + + mInputDeviceBuffers.insert(std::make_pair(input->getName(), data)); + } +} + +bool RndInt8Calibrator::getBatch(void* bindings[], char const* names[], int32_t nbBindings) noexcept +{ + if (mCurrentBatch >= mBatches) + { + return false; + } + + for (int32_t i = 0; i < nbBindings; ++i) + { + bindings[i] = mInputDeviceBuffers[names[i]]; + } + + ++mCurrentBatch; + + return true; +} + +const void* RndInt8Calibrator::readCalibrationCache(size_t& length) noexcept +{ + mCalibrationCache.clear(); + std::ifstream input(mCacheFile, std::ios::binary); + input >> std::noskipws; + if (input.good()) + { + std::copy( + std::istream_iterator(input), std::istream_iterator(), std::back_inserter(mCalibrationCache)); + } + + length = mCalibrationCache.size(); + return !mCalibrationCache.empty() ? mCalibrationCache.data() : nullptr; +} + +bool setTensorDynamicRange(INetworkDefinition const& network, float inRange = 2.0F, float outRange = 4.0F) +{ + // Ensure that all layer inputs have a dynamic range. + for (int32_t l = 0; l < network.getNbLayers(); l++) + { + auto* layer = network.getLayer(l); + for (int32_t i = 0; i < layer->getNbInputs(); i++) + { + ITensor* input{layer->getInput(i)}; + // Optional inputs are nullptr here and are from RNN layers. + if (input && !input->dynamicRangeIsSet()) + { + // Concat should propagate dynamic range from outputs to inputs to avoid + // Re-quantization during the concatenation + auto dynRange = (layer->getType() == LayerType::kCONCATENATION) ? outRange : inRange; + if (!input->setDynamicRange(-dynRange, dynRange)) + { + return false; + } + } + } + for (int32_t o = 0; o < layer->getNbOutputs(); o++) + { + ITensor* output{layer->getOutput(o)}; + // Optional outputs are nullptr here and are from RNN layers. + if (output && !output->dynamicRangeIsSet()) + { + // Pooling must have the same input and output dynamic range. + if (layer->getType() == LayerType::kPOOLING) + { + if (!output->setDynamicRange(-inRange, inRange)) + { + return false; + } + } + else + { + if (!output->setDynamicRange(-outRange, outRange)) + { + return false; + } + } + } + } + } + return true; +} + +bool isNonActivationType(nvinfer1::DataType const type) +{ + return type == nvinfer1::DataType::kINT32 +#if (NV_TENSORRT_MAJOR > 8) + || type == nvinfer1::DataType::kINT64 +#endif + || type == nvinfer1::DataType::kBOOL + || type == nvinfer1::DataType::kUINT8; +} + +void setLayerPrecisions(INetworkDefinition& network, LayerPrecisions const& layerPrecisions) +{ + bool hasLayerPrecisionSkipped{false}; + for (int32_t layerIdx = 0; layerIdx < network.getNbLayers(); ++layerIdx) + { + auto* layer = network.getLayer(layerIdx); + auto const layerName = layer->getName(); + auto exactMatch = layerPrecisions.find(layerName); + auto plausibleMatch = findPlausible(layerPrecisions, layerName); + if (exactMatch != layerPrecisions.end()) + { + sample::gLogInfo << "Set layer " << layerName << " to precision " << exactMatch->second << std::endl; + layer->setPrecision(exactMatch->second); + } + else if (plausibleMatch != layerPrecisions.end()) + { + if (isNonActivationType(layer->getPrecision())) + { + hasLayerPrecisionSkipped = true; + sample::gLogVerbose << "Skipped setting precision for layer " << layerName << " because the " + << " default layer precision is of non-activation type." << std::endl; + continue; + } + if (layer->getType() == nvinfer1::LayerType::kCONSTANT + && (isNonActivationType(static_cast(layer)->getWeights().type))) + { + hasLayerPrecisionSkipped = true; + sample::gLogVerbose << "Skipped setting precision for layer " << layerName << " because this " + << "constant layer has weights of non-activation type." << std::endl; + continue; + } + if (layer->getNbInputs() >= 1 && layer->getInput(0)->isShapeTensor()) + { + hasLayerPrecisionSkipped = true; + sample::gLogVerbose << "Skipped setting precision for layer " << layerName << " because this layer " + << "operates on a shape tensor." << std::endl; + continue; + } + if (layer->getNbInputs() >= 1 && isNonActivationType(layer->getInput(0)->getType()) + && layer->getNbOutputs() >= 1 && isNonActivationType(layer->getOutput(0)->getType())) + { + hasLayerPrecisionSkipped = true; + sample::gLogVerbose << "Skipped setting precision for layer " << layerName << " because this " + << "layer has input and output of non-activation type." << std::endl; + continue; + } + // All heuristics passed. Set the layer precision. + sample::gLogInfo << "Set layer " << layerName << " to precision " << plausibleMatch->second << std::endl; + layer->setPrecision(plausibleMatch->second); + } + } + + if (hasLayerPrecisionSkipped) + { + sample::gLogInfo << "Skipped setting precisions for some layers. Check verbose logs for more details." + << std::endl; + } +} + +void setLayerOutputTypes(INetworkDefinition& network, LayerOutputTypes const& layerOutputTypes) +{ + bool const hasGlobalOutputType{layerOutputTypes.find("*") != layerOutputTypes.end()}; + auto const globalOutputType = hasGlobalOutputType ? layerOutputTypes.at("*").at(0) : nvinfer1::DataType::kFLOAT; + bool hasLayerOutputTypeSkipped{false}; + for (int32_t layerIdx = 0; layerIdx < network.getNbLayers(); ++layerIdx) + { + auto* layer = network.getLayer(layerIdx); + auto const layerName = layer->getName(); + auto const nbOutputs = layer->getNbOutputs(); + auto exactMatch = layerOutputTypes.find(layerName); + auto plausibleMatch = findPlausible(layerOutputTypes, layerName); + if (exactMatch != layerOutputTypes.end()) + { + auto const& outputTypes = exactMatch->second; + bool const isBroadcast = (outputTypes.size() == 1); + if (!isBroadcast && static_cast(outputTypes.size()) != nbOutputs) + { + sample::gLogError << "Layer " << layerName << " has " << nbOutputs << " outputs but " + << outputTypes.size() << " output types are given in --layerOutputTypes flag." + << std::endl; + throw std::invalid_argument("Invalid --layerOutputTypes flag."); + } + for (int32_t outputIdx = 0; outputIdx < nbOutputs; ++outputIdx) + { + auto const outputType = outputTypes.at(isBroadcast ? 0 : outputIdx); + sample::gLogInfo << "Set output " << outputIdx << " of layer " << layerName << " to type " << outputType + << std::endl; + layer->setOutputType(outputIdx, outputType); + } + } + else if (plausibleMatch != layerOutputTypes.end()) + { + auto const& outputTypes = plausibleMatch->second; + bool const isBroadcast = (outputTypes.size() == 1); + + // We should not set the layer output types if its default precision is INT32 or Bool. + if (layer->getPrecision() == nvinfer1::DataType::kINT32 + || layer->getPrecision() == nvinfer1::DataType::kBOOL) + { + hasLayerOutputTypeSkipped = true; + sample::gLogVerbose << "Skipped setting output types for layer " << layerName << " because the " + << " default layer precision is INT32 or Bool." << std::endl; + continue; + } + // We should not set the constant layer output types if its weights are in INT32. + if (layer->getType() == nvinfer1::LayerType::kCONSTANT + && static_cast(layer)->getWeights().type == nvinfer1::DataType::kINT32) + { + hasLayerOutputTypeSkipped = true; + sample::gLogVerbose << "Skipped setting output types for layer " << layerName << " because this " + << "constant layer has INT32 weights." << std::endl; + continue; + } + for (int32_t outputIdx = 0; outputIdx < nbOutputs; ++outputIdx) + { + // We should not set the output type if the output is a shape tensor. + if (layer->getOutput(0)->isShapeTensor()) + { + hasLayerOutputTypeSkipped = true; + sample::gLogVerbose << "Skipped setting output type for output " << outputIdx << " of layer " + << layerName << " because it is a shape tensor." << std::endl; + continue; + } + + auto const outputType = outputTypes.at(isBroadcast ? 0 : outputIdx); + sample::gLogInfo << "Set output " << outputIdx << " of layer " << layerName << " to type " << outputType + << std::endl; + layer->setOutputType(outputIdx, globalOutputType); + } + } + } + + if (hasLayerOutputTypeSkipped) + { + sample::gLogInfo << "Skipped setting output types for some layers. Check verbose logs for more details." + << std::endl; + } +} + +void setLayerDeviceTypes( + INetworkDefinition const& network, IBuilderConfig& config, LayerDeviceTypes const& layerDeviceTypes) +{ + for (int32_t layerIdx = 0; layerIdx < network.getNbLayers(); ++layerIdx) + { + auto* layer = network.getLayer(layerIdx); + auto const layerName = layer->getName(); + auto match = findPlausible(layerDeviceTypes, layerName); + if (match != layerDeviceTypes.end()) + { + DeviceType const deviceType = match->second; + sample::gLogInfo << "Set layer " << layerName << " to device type " << (int)deviceType << std::endl; + config.setDeviceType(layer, deviceType); + } + } +} + +void markDebugTensors(INetworkDefinition& network, StringSet const& debugTensors) +{ +#if (NV_TENSORRT_MAJOR > 8) + for (int64_t inputIndex = 0; inputIndex < network.getNbInputs(); ++inputIndex) + { + auto* t = network.getInput(inputIndex); + auto const tensorName = t->getName(); + if (debugTensors.count(tensorName) > 0) + { + network.markDebug(*t); + } + } + for (int64_t layerIndex = 0; layerIndex < network.getNbLayers(); ++layerIndex) + { + auto* layer = network.getLayer(layerIndex); + for (int64_t outputIndex = 0; outputIndex < layer->getNbOutputs(); ++outputIndex) + { + auto* t = layer->getOutput(outputIndex); + auto const tensorName = t->getName(); + if (debugTensors.count(tensorName) > 0) + { + network.markDebug(*t); + } + } + } +#else + std::cerr << "Can not markDebugTensors. Use TensorRT 10.x or higher" << std::endl; +#endif +} + +void setMemoryPoolLimits(IBuilderConfig& config, BuildOptions const& build) +{ + auto const roundToBytes = [](double const size, bool fromMB = true) { + return static_cast(size * (fromMB ? 1.0_MiB : 1.0_KiB)); + }; + if (build.workspace >= 0) + { + config.setMemoryPoolLimit(MemoryPoolType::kWORKSPACE, roundToBytes(build.workspace)); + } + if (build.dlaSRAM >= 0) + { + size_t const sizeInBytes = roundToBytes(build.dlaSRAM); + size_t sizeInPowerOf2{1}; + // Using 2^30 bytes as a loose upper bound to prevent the possibility of overflows and infinite loops. + while (sizeInPowerOf2 < 31 && (static_cast(1) << sizeInPowerOf2) <= sizeInBytes) + { + ++sizeInPowerOf2; + } + --sizeInPowerOf2; + if (sizeInPowerOf2 == 30) + { + sample::gLogWarning + << "User-specified DLA managed SRAM size is too large and has been clipped to 2^30 bytes. " + << "Please make sure that this is the intended managed SRAM size." << std::endl; + } + config.setMemoryPoolLimit(MemoryPoolType::kDLA_MANAGED_SRAM, static_cast(1) << sizeInPowerOf2); + } + if (build.dlaLocalDRAM >= 0) + { + config.setMemoryPoolLimit(MemoryPoolType::kDLA_LOCAL_DRAM, roundToBytes(build.dlaLocalDRAM)); + } + if (build.dlaGlobalDRAM >= 0) + { + config.setMemoryPoolLimit(MemoryPoolType::kDLA_GLOBAL_DRAM, roundToBytes(build.dlaGlobalDRAM)); + } +#if (NV_TENSORRT_MAJOR > 8) + if (build.tacticSharedMem >= 0) + { + config.setMemoryPoolLimit(MemoryPoolType::kTACTIC_SHARED_MEMORY, roundToBytes(build.tacticSharedMem, false)); + } +#endif +} + +void setPreviewFeatures(IBuilderConfig& config, BuildOptions const& build) +{ + auto const setFlag = [&](PreviewFeature feat) { + int32_t featVal = static_cast(feat); + if (build.previewFeatures.find(featVal) != build.previewFeatures.end()) + { + config.setPreviewFeature(feat, build.previewFeatures.at(featVal)); + } + }; +#if (NV_TENSORRT_MAJOR > 8) + setFlag(PreviewFeature::kALIASED_PLUGIN_IO_10_03); +#endif +} + +} // namespace + +bool setupNetworkAndConfig(BuildOptions const& build, SystemOptions const& sys, IBuilder& builder, + INetworkDefinition& network, IBuilderConfig& config, std::unique_ptr& calibrator, + std::ostream& err, std::vector>& sparseWeights) +{ + std::vector profiles{}; + profiles.resize(build.optProfiles.size()); + for (auto& profile : profiles) + { + profile = builder.createOptimizationProfile(); + } + + bool hasDynamicShapes{false}; + + bool broadcastInputFormats = broadcastIOFormats(build.inputFormats, network.getNbInputs()); + + // Check if the provided input tensor names match the input tensors of the engine. + // Throw an error if the provided input tensor names cannot be found because it implies a potential typo. + for (auto const& shapes : build.optProfiles) + { + for (auto const& shape : shapes) + { + bool tensorNameFound{false}; + for (int32_t i = 0; i < network.getNbInputs(); ++i) + { + if (matchStringWithOneWildcard(shape.first, network.getInput(i)->getName())) + { + tensorNameFound = true; + break; + } + } + if (!tensorNameFound) + { + sample::gLogError << "Cannot find input tensor with name \"" << shape.first << "\" in the network " + << "inputs! Please make sure the input tensor names are correct." << std::endl; + return false; + } + } + } + + for (uint32_t i = 0, n = network.getNbInputs(); i < n; i++) + { + // Set formats and data types of inputs + auto* input = network.getInput(i); + if (!build.inputFormats.empty()) + { + int32_t inputFormatIndex = broadcastInputFormats ? 0 : i; + input->setType(build.inputFormats[inputFormatIndex].first); + input->setAllowedFormats(build.inputFormats[inputFormatIndex].second); + } + + auto const dims = input->getDimensions(); + auto const isScalar = dims.nbDims == 0; + auto const isDynamicInput = std::any_of(dims.d, dims.d + dims.nbDims, [](int32_t dim) { return dim == -1; }) + || input->isShapeTensor(); + if (isDynamicInput) + { + hasDynamicShapes = true; + for (size_t i = 0; i < build.optProfiles.size(); i++) + { + auto const& optShapes = build.optProfiles[i]; + auto profile = profiles[i]; + auto const tensorName = input->getName(); + auto shape = findPlausible(optShapes, tensorName); + ShapeRange shapes{}; + + // If no shape is provided, set dynamic dimensions to 1. + if (shape == optShapes.end()) + { + constexpr int32_t kDEFAULT_DIMENSION{1}; + std::vector staticDims; + if (input->isShapeTensor()) + { + if (isScalar) + { + staticDims.push_back(1); + } + else + { + staticDims.resize(dims.d[0]); + std::fill(staticDims.begin(), staticDims.end(), kDEFAULT_DIMENSION); + } + } + else + { + staticDims.resize(dims.nbDims); + std::transform(dims.d, dims.d + dims.nbDims, staticDims.begin(), + [&](int dimension) { return dimension > 0 ? dimension : kDEFAULT_DIMENSION; }); + } + sample::gLogWarning << "Dynamic dimensions required for input: " << tensorName + << ", but no shapes were provided. Automatically overriding shape to: " + << staticDims << std::endl; + std::fill(shapes.begin(), shapes.end(), staticDims); + } + else + { + shapes = shape->second; + } + + std::vector profileDims{}; + if (input->isShapeTensor()) + { + profileDims = shapes[static_cast(OptProfileSelector::kMIN)]; + SMP_RETVAL_IF_FALSE(profile->setShapeValues(tensorName, OptProfileSelector::kMIN, + profileDims.data(), static_cast(profileDims.size())), + "Error in set shape values MIN", false, err); + profileDims = shapes[static_cast(OptProfileSelector::kOPT)]; + SMP_RETVAL_IF_FALSE(profile->setShapeValues(tensorName, OptProfileSelector::kOPT, + profileDims.data(), static_cast(profileDims.size())), + "Error in set shape values OPT", false, err); + profileDims = shapes[static_cast(OptProfileSelector::kMAX)]; + SMP_RETVAL_IF_FALSE(profile->setShapeValues(tensorName, OptProfileSelector::kMAX, + profileDims.data(), static_cast(profileDims.size())), + "Error in set shape values MAX", false, err); + sample::gLogInfo << "Set input shape tensor " << tensorName << " for optimization profile " << i + << " to:" + << " MIN=" << shapes[static_cast(OptProfileSelector::kMIN)] + << " OPT=" << shapes[static_cast(OptProfileSelector::kOPT)] + << " MAX=" << shapes[static_cast(OptProfileSelector::kMAX)] << std::endl; + } + else + { + profileDims = shapes[static_cast(OptProfileSelector::kMIN)]; + SMP_RETVAL_IF_FALSE( + profile->setDimensions(tensorName, OptProfileSelector::kMIN, toDims(profileDims)), + "Error in set dimensions to profile MIN", false, err); + profileDims = shapes[static_cast(OptProfileSelector::kOPT)]; + SMP_RETVAL_IF_FALSE( + profile->setDimensions(tensorName, OptProfileSelector::kOPT, toDims(profileDims)), + "Error in set dimensions to profile OPT", false, err); + profileDims = shapes[static_cast(OptProfileSelector::kMAX)]; + SMP_RETVAL_IF_FALSE( + profile->setDimensions(tensorName, OptProfileSelector::kMAX, toDims(profileDims)), + "Error in set dimensions to profile MAX", false, err); + sample::gLogInfo << "Set shape of input tensor " << tensorName << " for optimization profile " << i + << " to:" + << " MIN=" << shapes[static_cast(OptProfileSelector::kMIN)] + << " OPT=" << shapes[static_cast(OptProfileSelector::kOPT)] + << " MAX=" << shapes[static_cast(OptProfileSelector::kMAX)] << std::endl; + } + } + } + } + + for (uint32_t i = 0, n = network.getNbOutputs(); i < n; i++) + { + auto* output = network.getOutput(i); + auto const dims = output->getDimensions(); + // A shape tensor output with known static dimensions may have dynamic shape values inside it. + auto const isDynamicOutput = std::any_of(dims.d, dims.d + dims.nbDims, [](int32_t dim) { return dim == -1; }) + || output->isShapeTensor(); + if (isDynamicOutput) + { + hasDynamicShapes = true; + } + } + + if (!hasDynamicShapes && !build.optProfiles[0].empty()) + { + sample::gLogError << "Static model does not take explicit shapes since the shape of inference tensors will be " + "determined by the model itself" + << std::endl; + return false; + } + + if (hasDynamicShapes) + { + for (auto profile : profiles) + { + SMP_RETVAL_IF_FALSE(profile->isValid(), "Required optimization profile is invalid", false, err); + SMP_RETVAL_IF_FALSE( + config.addOptimizationProfile(profile) != -1, "Error in add optimization profile", false, err); + } + } + + bool broadcastOutputFormats = broadcastIOFormats(build.outputFormats, network.getNbOutputs(), false); + + for (uint32_t i = 0, n = network.getNbOutputs(); i < n; i++) + { + // Set formats and data types of outputs + auto* output = network.getOutput(i); + if (!build.outputFormats.empty()) + { + int32_t outputFormatIndex = broadcastOutputFormats ? 0 : i; + output->setType(build.outputFormats[outputFormatIndex].first); + output->setAllowedFormats(build.outputFormats[outputFormatIndex].second); + } + } + + setMemoryPoolLimits(config, build); + + setPreviewFeatures(config, build); + + if (build.builderOptimizationLevel != defaultBuilderOptimizationLevel) + { + config.setBuilderOptimizationLevel(build.builderOptimizationLevel); + } + + if (build.maxTactics != defaultMaxTactics) + { +#if (NV_TENSORRT_MAJOR < 8) + config.setMaxNbTactics(build.maxTactics); +#else + config.setTacticSources(build.maxTactics); +#endif + } + + if (build.timingCacheMode == TimingCacheMode::kDISABLE) + { + config.setFlag(BuilderFlag::kDISABLE_TIMING_CACHE); + } +#if (NV_TENSORRT_MAJOR > 8) + if (build.disableCompilationCache) + { + config.setFlag(BuilderFlag::kDISABLE_COMPILATION_CACHE); + } + + if (build.errorOnTimingCacheMiss) + { + config.setFlag(BuilderFlag::kERROR_ON_TIMING_CACHE_MISS); + } +#endif + if (!build.tf32) + { + config.clearFlag(BuilderFlag::kTF32); + } + + if (build.refittable) + { + config.setFlag(BuilderFlag::kREFIT); + } +#if (NV_TENSORRT_MAJOR > 8) + if (build.stripWeights) + { + // The kREFIT_IDENTICAL is enabled by default when kSTRIP_PLAN is on. + config.setFlag(BuilderFlag::kSTRIP_PLAN); + } +#endif + if (build.versionCompatible) + { + config.setFlag(BuilderFlag::kVERSION_COMPATIBLE); + } +#if !TRT_WINML + std::vector pluginPaths; + for (auto const& pluginPath : sys.setPluginsToSerialize) + { + sample::gLogVerbose << "Setting plugin to serialize: " << pluginPath << std::endl; + pluginPaths.push_back(pluginPath.c_str()); + } + if (!pluginPaths.empty()) + { + config.setPluginsToSerialize(pluginPaths.data(), pluginPaths.size()); + } +#endif + if (build.excludeLeanRuntime) + { + config.setFlag(BuilderFlag::kEXCLUDE_LEAN_RUNTIME); + } + + if (build.sparsity != SparsityFlag::kDISABLE) + { + config.setFlag(BuilderFlag::kSPARSE_WEIGHTS); + if (build.sparsity == SparsityFlag::kFORCE) + { + sparsify(network, sparseWeights); + } + } + + config.setProfilingVerbosity(build.profilingVerbosity); + config.setAvgTimingIterations(build.avgTiming); + + if (build.fp16) + { + config.setFlag(BuilderFlag::kFP16); + } + if (build.int8) + { + config.setFlag(BuilderFlag::kINT8); + } +#if (NV_TENSORRT_MAJOR > 8) + if (build.bf16) + { + config.setFlag(BuilderFlag::kBF16); + } +#endif + + SMP_RETVAL_IF_FALSE(!(build.int8 && build.fp8), "FP8 and INT8 precisions have been specified", false, err); + + if (build.fp8) + { + config.setFlag(BuilderFlag::kFP8); + } +#if (NV_TENSORRT_MAJOR > 8) + if (build.int4) + { + config.setFlag(BuilderFlag::kINT4); + } +#endif + if (build.int8 && !build.fp16) + { + sample::gLogInfo + << "FP32 and INT8 precisions have been specified - more performance might be enabled by additionally " + "specifying --fp16 or --best" + << std::endl; + } + + auto isInt8 = [](const IOFormat& format) { return format.first == DataType::kINT8; }; + auto int8IO = std::count_if(build.inputFormats.begin(), build.inputFormats.end(), isInt8) + + std::count_if(build.outputFormats.begin(), build.outputFormats.end(), isInt8); + + auto hasQDQLayers = [](INetworkDefinition& network) { + // Determine if our network has QDQ layers. + auto const nbLayers = network.getNbLayers(); + for (int32_t i = 0; i < nbLayers; i++) + { + auto const& layer = network.getLayer(i); + if (layer->getType() == LayerType::kQUANTIZE || layer->getType() == LayerType::kDEQUANTIZE) + { + return true; + } + } + return false; + }; + + if (!hasQDQLayers(network) && (build.int8 || int8IO) && build.calibration.empty()) + { + // Explicitly set int8 scales if no calibrator is provided and if I/O tensors use int8, + // because auto calibration does not support this case. + SMP_RETVAL_IF_FALSE(setTensorDynamicRange(network), "Error in set tensor dynamic range.", false, err); + } + else if (build.int8) + { + if (!hasQDQLayers(network) && int8IO) + { + try + { + // Set dynamic ranges of int8 inputs / outputs to match scales loaded from calibration cache + // TODO http://nvbugs/3262234 Change the network validation so that this workaround can be removed + setTensorScalesFromCalibration(network, build.inputFormats, build.outputFormats, build.calibration); + } + catch (std::exception&) + { + sample::gLogError + << "Int8IO was specified but impossible to read tensor scales from provided calibration cache file" + << std::endl; + return false; + } + } + IOptimizationProfile* profileCalib{nullptr}; + if (!build.shapesCalib.empty()) + { + profileCalib = builder.createOptimizationProfile(); + for (uint32_t i = 0, n = network.getNbInputs(); i < n; i++) + { + auto* input = network.getInput(i); + Dims profileDims{}; + auto const tensorName = input->getName(); + auto shape = findPlausible(build.shapesCalib, tensorName); + + if (shape == build.shapesCalib.end()) + { + std::ostringstream msg; + msg << "Calibration profile for tensor " << tensorName << " cannot be found!"; + throw std::invalid_argument(msg.str()); + } + + auto shapesCalib = shape->second; + profileDims = toDims(shapesCalib[static_cast(OptProfileSelector::kOPT)]); + // Here we check only kMIN as all profileDims are the same. + SMP_RETVAL_IF_FALSE(profileCalib->setDimensions(tensorName, OptProfileSelector::kMIN, profileDims), + "Error in set dimensions to calibration profile OPT", false, err); + profileCalib->setDimensions(tensorName, OptProfileSelector::kOPT, profileDims); + profileCalib->setDimensions(tensorName, OptProfileSelector::kMAX, profileDims); + sample::gLogInfo << "Set calibration profile for input tensor " << tensorName << " to " << profileDims + << std::endl; + } + SMP_RETVAL_IF_FALSE(profileCalib->isValid(), "Calibration profile is invalid", false, err); + SMP_RETVAL_IF_FALSE( + config.setCalibrationProfile(profileCalib), "Error in set calibration profile", false, err); + } + + std::vector elemCount{}; + for (int i = 0; i < network.getNbInputs(); i++) + { + auto* input = network.getInput(i); + auto const dims = input->getDimensions(); + auto const isDynamicInput + = std::any_of(dims.d, dims.d + dims.nbDims, [](int32_t dim) { return dim == -1; }); + + if (profileCalib) + { + elemCount.push_back(volume(profileCalib->getDimensions(input->getName(), OptProfileSelector::kOPT))); + } + else if (!profiles.empty() && isDynamicInput) + { + elemCount.push_back( + volume(profiles[build.calibProfile]->getDimensions(input->getName(), OptProfileSelector::kOPT))); + } + else + { + elemCount.push_back(volume(input->getDimensions())); + } + } + + calibrator.reset(new RndInt8Calibrator(1, elemCount, build.calibration, network, err)); + config.setInt8Calibrator(calibrator.get()); + } + + if (build.directIO) + { + config.setFlag(BuilderFlag::kDIRECT_IO); + } + + switch (build.precisionConstraints) + { + case PrecisionConstraints::kNONE: + // It's the default for TensorRT. + break; + case PrecisionConstraints::kOBEY: config.setFlag(BuilderFlag::kOBEY_PRECISION_CONSTRAINTS); break; + case PrecisionConstraints::kPREFER: config.setFlag(BuilderFlag::kPREFER_PRECISION_CONSTRAINTS); break; + } + + if (!build.layerPrecisions.empty() && build.precisionConstraints != PrecisionConstraints::kNONE) + { + setLayerPrecisions(network, build.layerPrecisions); + } + + if (!build.layerOutputTypes.empty() && build.precisionConstraints != PrecisionConstraints::kNONE) + { + setLayerOutputTypes(network, build.layerOutputTypes); + } + + if (!build.layerDeviceTypes.empty()) + { + setLayerDeviceTypes(network, config, build.layerDeviceTypes); + } + + if (!build.debugTensors.empty()) + { + markDebugTensors(network, build.debugTensors); + } + + if (build.safe && sys.DLACore == -1) + { + config.setEngineCapability(EngineCapability::kSAFETY); + } + + if (build.restricted) + { + config.setFlag(BuilderFlag::kSAFETY_SCOPE); + } + + if (sys.DLACore != -1) + { + if (sys.DLACore < builder.getNbDLACores()) + { + config.setDefaultDeviceType(DeviceType::kDLA); + config.setDLACore(sys.DLACore); + config.setFlag(BuilderFlag::kPREFER_PRECISION_CONSTRAINTS); + if (build.buildDLAStandalone) + { + config.setEngineCapability(EngineCapability::kDLA_STANDALONE); + } + if (build.allowGPUFallback) + { + config.setFlag(BuilderFlag::kGPU_FALLBACK); + } + else + { + // Reformatting runs on GPU, so avoid I/O reformatting. + config.setFlag(BuilderFlag::kDIRECT_IO); + } + if (!build.int8) + { + config.setFlag(BuilderFlag::kFP16); + } + } + else + { + err << "Cannot create DLA engine, " << sys.DLACore << " not available" << std::endl; + return false; + } + } + + if (build.enabledTactics || build.disabledTactics) + { + TacticSources tacticSources = config.getTacticSources(); + tacticSources |= build.enabledTactics; + tacticSources &= ~build.disabledTactics; + config.setTacticSources(tacticSources); + } + + config.setHardwareCompatibilityLevel(build.hardwareCompatibilityLevel); +#if (NV_TENSORRT_MAJOR > 8) + config.setRuntimePlatform(build.runtimePlatform); +#endif + + if (build.maxAuxStreams != defaultMaxAuxStreams) + { + config.setMaxAuxStreams(build.maxAuxStreams); + } + + if (build.allowWeightStreaming) + { +#if (NV_TENSORRT_MAJOR > 8) + config.setFlag(BuilderFlag::kWEIGHT_STREAMING); +#else + std::cerr << "BuilderFlag::kWEIGHT_STREAMING not allowed in TensorRT with version less than 10.x" << std::endl; +#endif + } + + return true; +} + +//! +//! \brief Create a serialized engine for a network defintion +//! +//! \return Whether the engine creation succeeds or fails. +//! +bool networkToSerializedEngine( + BuildOptions const& build, SystemOptions const& sys, IBuilder& builder, BuildEnvironment& env, std::ostream& err) +{ + std::unique_ptr config{builder.createBuilderConfig()}; + std::unique_ptr calibrator; + std::vector> sparseWeights; + SMP_RETVAL_IF_FALSE(config != nullptr, "Config creation failed", false, err); + SMP_RETVAL_IF_FALSE( + setupNetworkAndConfig(build, sys, builder, *env.network, *config, calibrator, err, sparseWeights), + "Network And Config setup failed", false, err); + + std::unique_ptr timingCache{}; + // Try to load cache from file. Create a fresh cache if the file doesn't exist + if (build.timingCacheMode == TimingCacheMode::kGLOBAL) + { + timingCache + = samplesCommon::buildTimingCacheFromFile(gLogger.getTRTLogger(), *config, build.timingCacheFile, err); + } + + // CUDA stream used for profiling by the builder. + auto profileStream = samplesCommon::makeCudaStream(); + SMP_RETVAL_IF_FALSE(profileStream != nullptr, "Cuda stream creation failed", false, err); + config->setProfileStream(*profileStream); + + auto const tBegin = std::chrono::high_resolution_clock::now(); + std::unique_ptr serializedEngine{builder.buildSerializedNetwork(*env.network, *config)}; + SMP_RETVAL_IF_FALSE(serializedEngine != nullptr, "Engine could not be created from network", false, err); + auto const tEnd = std::chrono::high_resolution_clock::now(); + float const buildTime = std::chrono::duration(tEnd - tBegin).count(); + sample::gLogInfo << "Engine built in " << buildTime << " sec." << std::endl; + sample::gLogInfo << "Created engine with size: " << (serializedEngine->size() / 1.0_MiB) << " MiB" << std::endl; + + env.engine.setBlob(serializedEngine); + + if (build.timingCacheMode == TimingCacheMode::kGLOBAL) + { + auto timingCache = config->getTimingCache(); + samplesCommon::updateTimingCacheFile(gLogger.getTRTLogger(), build.timingCacheFile, timingCache, builder); + } + + return true; +} + +//! +//! \brief Parse a given model, create a network and an engine. +//! +bool modelToBuildEnv( + ModelOptions const& model, BuildOptions const& build, SystemOptions& sys, BuildEnvironment& env, std::ostream& err) +{ + env.builder.reset(createBuilder()); + SMP_RETVAL_IF_FALSE(env.builder != nullptr, "Builder creation failed", false, err); + env.builder->setErrorRecorder(&gRecorder); +#if (NV_TENSORRT_MAJOR > 8) + auto networkFlags = (build.stronglyTyped) + ? 1U << static_cast(nvinfer1::NetworkDefinitionCreationFlag::kSTRONGLY_TYPED) + : 0U; +#else + auto networkFlags = 0U; +#endif +#if !TRT_WINML + for (auto const& pluginPath : sys.dynamicPlugins) + { + env.builder->getPluginRegistry().loadLibrary(pluginPath.c_str()); + } +#endif + env.network.reset(env.builder->createNetworkV2(networkFlags)); + + std::vector vcPluginLibrariesUsed; + SMP_RETVAL_IF_FALSE(env.network != nullptr, "Network creation failed", false, err); + env.parser + = modelToNetwork(model, build, *env.network, err, build.versionCompatible ? &vcPluginLibrariesUsed : nullptr); + SMP_RETVAL_IF_FALSE(env.parser.operator bool(), "Parsing model failed", false, err); + +#if !TRT_WINML + if (build.versionCompatible && !sys.ignoreParsedPluginLibs && !vcPluginLibrariesUsed.empty()) + { + sample::gLogInfo << "The following plugin libraries were identified by the parser as required for a " + "version-compatible engine:" + << std::endl; + for (auto const& lib : vcPluginLibrariesUsed) + { + sample::gLogInfo << " " << lib << std::endl; + } + if (!build.excludeLeanRuntime) + { + sample::gLogInfo << "These libraries will be added to --setPluginsToSerialize since --excludeLeanRuntime " + "was not specified." + << std::endl; + std::copy(vcPluginLibrariesUsed.begin(), vcPluginLibrariesUsed.end(), + std::back_inserter(sys.setPluginsToSerialize)); + } + sample::gLogInfo << "These libraries will be added to --dynamicPlugins for use at inference time." << std::endl; + std::copy(vcPluginLibrariesUsed.begin(), vcPluginLibrariesUsed.end(), std::back_inserter(sys.dynamicPlugins)); + + // Implicitly-added plugins from ONNX parser should be loaded into plugin registry as well. + for (auto const& pluginPath : vcPluginLibrariesUsed) + { + env.builder->getPluginRegistry().loadLibrary(pluginPath.c_str()); + } + + sample::gLogInfo << "Use --ignoreParsedPluginLibs to disable this behavior." << std::endl; + } +#endif + + SMP_RETVAL_IF_FALSE( + networkToSerializedEngine(build, sys, *env.builder, env, err), "Building engine failed", false, err); + return true; +} + +namespace +{ +std::pair, std::vector> getLayerWeightsRolePair(IRefitter& refitter) +{ + // Get number of refittable items. + auto const nbAll = refitter.getAll(0, nullptr, nullptr); + std::vector layerNames(nbAll); + // Allocate buffers for the items and get them. + std::vector weightsRoles(nbAll); + refitter.getAll(nbAll, layerNames.data(), weightsRoles.data()); + std::vector layerNameStrs(nbAll); + std::transform(layerNames.begin(), layerNames.end(), layerNameStrs.begin(), [](char const* name) { + if (name == nullptr) + { + return std::string{}; + } + return std::string{name}; + }); + return {layerNameStrs, weightsRoles}; +} + +std::pair, std::vector> getMissingLayerWeightsRolePair(IRefitter& refitter) +{ + // Get number of refittable items. + auto const nbMissing = refitter.getMissing(0, nullptr, nullptr); + std::vector layerNames(nbMissing); + // Allocate buffers for the items and get them. + std::vector weightsRoles(nbMissing); + refitter.getMissing(nbMissing, layerNames.data(), weightsRoles.data()); + std::vector layerNameStrs(nbMissing); + std::transform(layerNames.begin(), layerNames.end(), layerNameStrs.begin(), [](char const* name) { + if (name == nullptr) + { + return std::string{}; + } + return std::string{name}; + }); + return {layerNameStrs, weightsRoles}; +} +} // namespace + +bool loadStreamingEngineToBuildEnv(std::string const& filepath, BuildEnvironment& env, std::ostream& err) +{ +#if (NV_TENSORRT_MAJOR > 8) + auto& reader = env.engine.getFileReader(); + SMP_RETVAL_IF_FALSE(reader.open(filepath), "", false, err << "Error opening engine file: " << filepath); +#else + SMP_RETVAL_IF_FALSE(false, "", false, err << "Error opening engine file: " << filepath); +#endif + return true; +} + +bool loadEngineToBuildEnv(std::string const& filepath, BuildEnvironment& env, std::ostream& err) +{ + auto const tBegin = std::chrono::high_resolution_clock::now(); + std::ifstream engineFile(filepath, std::ios::binary); + SMP_RETVAL_IF_FALSE(engineFile.good(), "", false, err << "Error opening engine file: " << filepath); + engineFile.seekg(0, std::ifstream::end); + int64_t fsize = engineFile.tellg(); + engineFile.seekg(0, std::ifstream::beg); + + std::vector engineBlob(fsize); + engineFile.read(reinterpret_cast(engineBlob.data()), fsize); + SMP_RETVAL_IF_FALSE(engineFile.good(), "", false, err << "Error loading engine file: " << filepath); + auto const tEnd = std::chrono::high_resolution_clock::now(); + float const loadTime = std::chrono::duration(tEnd - tBegin).count(); + sample::gLogInfo << "Engine loaded in " << loadTime << " sec." << std::endl; + sample::gLogInfo << "Loaded engine with size: " << (fsize / 1.0_MiB) << " MiB" << std::endl; + + env.engine.setBlob(std::move(engineBlob)); + + return true; +} + +bool printPlanVersion(BuildEnvironment& env, std::ostream& err) +{ + constexpr int64_t kPLAN_SIZE{28}; + std::vector data(kPLAN_SIZE); + auto blob = data.data(); + +#if (NV_TENSORRT_MAJOR > 8) + auto& reader = env.engine.getFileReader(); + if (reader.isOpen()) + { + SMP_RETVAL_IF_FALSE(reader.read(data.data(), kPLAN_SIZE) == kPLAN_SIZE, "Failed to read plan file", false, err); + } + else +#endif + { + SMP_RETVAL_IF_FALSE(env.engine.getBlob().data != nullptr, "Plan file is empty", false, err); + SMP_RETVAL_IF_FALSE(env.engine.getBlob().size >= 28, "Plan file is incorrect", false, err); + blob = static_cast(env.engine.getBlob().data); + } + auto blob32 = reinterpret_cast(blob); + + //! Correct TensorRT plan file starts with this tag + constexpr uint32_t kPLAN_FILE_TAG{0x74727466U}; + SMP_RETVAL_IF_FALSE(blob32[0] == kPLAN_FILE_TAG, "Failed to verify a plan tag.", false, err); + switch (blob32[1]) + { + case 0U: + { + // Blob index to store the plan version may depend on the serialization version. + sample::gLogInfo << "Plan was created with TensorRT version " << static_cast(blob[24]) + << "." << static_cast(blob[25]) << "." << static_cast(blob[26]) + << "." << static_cast(blob[27]) << std::endl; + return true; + } + } + sample::gLogError << "Serialization version is not supported." << std::endl; + return false; +} + +void dumpRefittable(nvinfer1::ICudaEngine& engine) +{ + std::unique_ptr refitter{createRefitter(engine)}; + if (refitter == nullptr) + { + sample::gLogError << "Failed to create a refitter." << std::endl; + return; + } + + auto const& layerWeightsRolePair = getLayerWeightsRolePair(*refitter); + auto const& layerNames = layerWeightsRolePair.first; + auto const& weightsRoles = layerWeightsRolePair.second; + auto const nbAll = layerWeightsRolePair.first.size(); + for (size_t i = 0; i < nbAll; ++i) + { + sample::gLogInfo << layerNames[i] << " " << weightsRoles[i] << std::endl; + } +} + +ICudaEngine* loadEngine(std::string const& engine, int32_t DLACore, std::ostream& err) +{ + BuildEnvironment env(/* isSafe */ false, /* versionCompatible */ false, DLACore, "", getTempfileControlDefaults()); + return loadEngineToBuildEnv(engine, env, err) ? env.engine.release() : nullptr; +} + +bool saveEngine(const ICudaEngine& engine, std::string const& fileName, std::ostream& err) +{ + std::ofstream engineFile(fileName, std::ios::binary); + if (!engineFile) + { + err << "Cannot open engine file: " << fileName << std::endl; + return false; + } + + std::unique_ptr serializedEngine{engine.serialize()}; + if (serializedEngine == nullptr) + { + err << "Engine serialization failed" << std::endl; + return false; + } + + engineFile.write(static_cast(serializedEngine->data()), serializedEngine->size()); + return !engineFile.fail(); +} + +bool getEngineBuildEnv( + const ModelOptions& model, BuildOptions const& build, SystemOptions& sys, BuildEnvironment& env, std::ostream& err) +{ + bool createEngineSuccess{false}; + + if (build.load) + { + if (build.safe) + { + createEngineSuccess = loadEngineToBuildEnv(build.engine, env, err); + } + else + { + createEngineSuccess = loadStreamingEngineToBuildEnv(build.engine, env, err); + } + } + else + { + createEngineSuccess = modelToBuildEnv(model, build, sys, env, err); + } + + SMP_RETVAL_IF_FALSE(createEngineSuccess, "Failed to create engine from model or file.", false, err); + + if (build.getPlanVersionOnly && build.load) + { + SMP_RETVAL_IF_FALSE(printPlanVersion(env, err), "Failed to get plan file version.", false, err); + return true; + } + + if (build.save) + { + std::ofstream engineFile(build.engine, std::ios::binary); + auto& engineBlob = env.engine.getBlob(); + engineFile.write(static_cast(engineBlob.data), engineBlob.size); + SMP_RETVAL_IF_FALSE(!engineFile.fail(), "Saving engine to file failed.", false, err); + engineFile.flush(); + engineFile.close(); + if (!build.safe) + { + env.engine.releaseBlob(); + SMP_RETVAL_IF_FALSE(loadStreamingEngineToBuildEnv(build.engine, env, err), "Reading engine file failed.", false, err); + } + } + + return true; +} + +// There is not a getWeightsName API, so we need to use WeightsRole. +std::vector> getAllRefitWeightsForLayer(const ILayer& l) +{ + switch (l.getType()) + { + case LayerType::kCONSTANT: + { + auto const& layer = static_cast(l); + auto const weights = layer.getWeights(); + switch (weights.type) + { + case DataType::kFLOAT: + case DataType::kHALF: +#if (NV_TENSORRT_MAJOR > 8) + case DataType::kBF16: +#endif + case DataType::kINT8: + case DataType::kINT32: +#if (NV_TENSORRT_MAJOR > 8) + case DataType::kINT64: +#endif + return {std::make_pair(WeightsRole::kCONSTANT, weights)}; + case DataType::kBOOL: + case DataType::kUINT8: + case DataType::kFP8: +#if (NV_TENSORRT_MAJOR > 8) + case DataType::kINT4: +#endif + // Refit not supported for these types. + break; + } + break; + } + case LayerType::kCONVOLUTION: + { + auto const& layer = static_cast(l); + return {std::make_pair(WeightsRole::kKERNEL, layer.getKernelWeights()), + std::make_pair(WeightsRole::kBIAS, layer.getBiasWeights())}; + } + case LayerType::kDECONVOLUTION: + { + auto const& layer = static_cast(l); + return {std::make_pair(WeightsRole::kKERNEL, layer.getKernelWeights()), + std::make_pair(WeightsRole::kBIAS, layer.getBiasWeights())}; + } + case LayerType::kSCALE: + { + auto const& layer = static_cast(l); + return {std::make_pair(WeightsRole::kSCALE, layer.getScale()), + std::make_pair(WeightsRole::kSHIFT, layer.getShift())}; + } + case LayerType::kACTIVATION: + case LayerType::kASSERTION: + case LayerType::kCAST: + case LayerType::kCONCATENATION: + case LayerType::kCONDITION: + case LayerType::kCONDITIONAL_INPUT: + case LayerType::kCONDITIONAL_OUTPUT: + case LayerType::kDEQUANTIZE: + case LayerType::kEINSUM: + case LayerType::kELEMENTWISE: + case LayerType::kFILL: + case LayerType::kGATHER: + case LayerType::kGRID_SAMPLE: + case LayerType::kIDENTITY: + case LayerType::kITERATOR: + case LayerType::kLOOP_OUTPUT: + case LayerType::kLRN: + case LayerType::kMATRIX_MULTIPLY: + case LayerType::kNMS: + case LayerType::kNON_ZERO: + case LayerType::kNORMALIZATION: + case LayerType::kONE_HOT: + case LayerType::kPADDING: + case LayerType::kPARAMETRIC_RELU: + case LayerType::kPLUGIN: + case LayerType::kPLUGIN_V2: +#if (NV_TENSORRT_MAJOR > 8) + case LayerType::kPLUGIN_V3: +#endif + case LayerType::kPOOLING: + case LayerType::kQUANTIZE: + case LayerType::kRAGGED_SOFTMAX: + case LayerType::kRECURRENCE: + case LayerType::kREDUCE: + case LayerType::kRESIZE: + case LayerType::kREVERSE_SEQUENCE: + case LayerType::kSCATTER: + case LayerType::kSELECT: + case LayerType::kSHAPE: + case LayerType::kSHUFFLE: + case LayerType::kSLICE: + case LayerType::kSOFTMAX: + case LayerType::kTOPK: + case LayerType::kTRIP_LIMIT: + case LayerType::kUNARY: return {}; + } + return {}; +} + +bool timeRefit(INetworkDefinition const& network, nvinfer1::ICudaEngine& engine, bool multiThreading) +{ + using time_point = std::chrono::time_point; + using durationMs = std::chrono::duration; + + auto const nbLayers = network.getNbLayers(); + std::unique_ptr refitter{createRefitter(engine)}; + // Set max threads that can be used by refitter. + if (multiThreading && !refitter->setMaxThreads(10)) + { + sample::gLogError << "Failed to set max threads to refitter." << std::endl; + return false; + } + auto const& layerWeightsRolePair = getLayerWeightsRolePair(*refitter); + // We use std::string instead of char const* since we can have copies of layer names. + std::set> layerRoleSet; + + auto const& layerNames = layerWeightsRolePair.first; + auto const& weightsRoles = layerWeightsRolePair.second; + + std::transform(layerNames.begin(), layerNames.end(), weightsRoles.begin(), + std::inserter(layerRoleSet, layerRoleSet.begin()), + [](std::string const& layerName, WeightsRole const role) { return std::make_pair(layerName, role); }); + + auto const isRefittable = [&layerRoleSet](char const* layerName, WeightsRole const role) { + return layerRoleSet.find(std::make_pair(layerName, role)) != layerRoleSet.end(); + }; + + auto const setWeights = [&] { + for (int32_t i = 0; i < nbLayers; i++) + { + auto const layer = network.getLayer(i); + auto const roleWeightsVec = getAllRefitWeightsForLayer(*layer); + for (auto const& roleWeights : roleWeightsVec) + { + if (isRefittable(layer->getName(), roleWeights.first)) + { + bool const success = refitter->setWeights(layer->getName(), roleWeights.first, roleWeights.second); + if (!success) + { + return false; + } + } + } + } + return true; + }; + + auto const reportMissingWeights = [&] { + auto const& missingPair = getMissingLayerWeightsRolePair(*refitter); + auto const& layerNames = missingPair.first; + auto const& weightsRoles = missingPair.second; + for (size_t i = 0; i < layerNames.size(); ++i) + { + sample::gLogError << "Missing (" << layerNames[i] << ", " << weightsRoles[i] << ") for refitting." + << std::endl; + } + return layerNames.empty(); + }; +#if (NV_TENSORRT_MAJOR > 8) + // Skip weights validation since we are confident that the new weights are similar to the weights used to build engine. + refitter->setWeightsValidation(false); +#endif + // Warm up and report missing weights + // We only need to set weights for the first time and that can be reused in later refitting process. + bool const success = setWeights() && reportMissingWeights() && refitter->refitCudaEngine(); + if (!success) + { + return false; + } + + time_point const refitStartTime{std::chrono::steady_clock::now()}; + constexpr int32_t kLOOP = 10; +#if (NV_TENSORRT_MAJOR > 8) + TrtCudaStream stream; + { + for (int32_t l = 0; l < kLOOP; l++) + { + if (!refitter->refitCudaEngineAsync(stream.get())) + { + return false; + } + } + } + stream.synchronize(); +#endif + time_point const refitEndTime{std::chrono::steady_clock::now()}; + + sample::gLogInfo << "Engine refitted" + << " in " << durationMs(refitEndTime - refitStartTime).count() / kLOOP << " ms." << std::endl; + return true; +} + +namespace +{ +void* initSafeRuntime() +{ + void* handle{nullptr}; + // libsafe_executor.so will be renamed to libnvinfer_safe.so when TRTS-9421 completes. + // Currently libsafe_executor_debug.so for samplesCommon::isDebug() is not ready. +#define TRTS_9421_COMPLETED 0 +#if TRTS_9421_COMPLETED +#if !defined(_WIN32) + std::string const dllName{"libsafe_executor.so"}; +#if SANITIZER_BUILD + handle = dlopen(dllName.c_str(), RTLD_LAZY | RTLD_NODELETE); +#else + // RTLD_GLOBAL is used for symbol resolution of subsequently loaded plugin libraries + handle = dlopen(dllName.c_str(), RTLD_LAZY | RTLD_GLOBAL); +#endif +#endif +#endif // TRTS_9421_COMPLETED + return handle; +} + +#if !defined(_WIN32) +struct DllDeleter +{ + void operator()(void* handle) + { + if (handle != nullptr) + { + dlclose(handle); + } + } +}; +const std::unique_ptr safeRuntimeLibrary{initSafeRuntime()}; +#endif +} // namespace + +bool hasSafeRuntime() +{ + bool ret{false}; +#if !defined(_WIN32) + ret = (safeRuntimeLibrary != nullptr); +#endif + return ret; +} + +} // namespace sample diff --git a/src/Detector/tensorrt_onnx/common/sampleEngines.h b/src/Detector/tensorrt_onnx/common/sampleEngines.h new file mode 100644 index 000000000..e6f532acc --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleEngines.h @@ -0,0 +1,326 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef TRT_SAMPLE_ENGINES_H +#define TRT_SAMPLE_ENGINES_H + +#include "NvInfer.h" +#include "NvOnnxParser.h" +#include "sampleOptions.h" +#include "sampleUtils.h" +#include "streamReader.h" +#include +#include + +namespace sample +{ + +struct Parser +{ + std::unique_ptr onnxParser; + + operator bool() const + { + return onnxParser != nullptr; + } +}; + +//! +//! \brief Helper struct to faciliate engine serialization and deserialization. It does not own the underlying memory. +//! +struct EngineBlob +{ + EngineBlob(void* engineData, size_t engineSize) + : data(engineData) + , size(engineSize) + { + } + void* data{}; + size_t size{}; + bool empty() const + { + return size == 0; + } +}; + +//! +//! \brief A helper class to hold a serialized engine (std or safe) and only deserialize it when being accessed. +//! +class LazilyDeserializedEngine +{ +public: + //! + //! \brief Delete default constructor to make sure isSafe and DLACore are always set. + //! + LazilyDeserializedEngine() = delete; + + //! + //! \brief Constructor of LazilyDeserializedEngine. + //! + LazilyDeserializedEngine(bool isSafe, bool versionCompatible, int32_t DLACore, std::string const& tempdir, + nvinfer1::TempfileControlFlags tempfileControls, std::string const& leanDLLPath) + : mIsSafe(isSafe) + , mVersionCompatible(versionCompatible) + , mDLACore(DLACore) + , mTempdir(tempdir) + , mTempfileControls(tempfileControls) + , mLeanDLLPath(leanDLLPath) + { +#if (NV_TENSORRT_MAJOR > 8) + mFileReader = std::make_unique(); +#endif + } + + //! + //! \brief Move from another LazilyDeserializedEngine. + //! + LazilyDeserializedEngine(LazilyDeserializedEngine&& other) = default; + + //! + //! \brief Delete copy constructor. + //! + LazilyDeserializedEngine(LazilyDeserializedEngine const& other) = delete; + + //! + //! \brief Get the pointer to the ICudaEngine. Triggers deserialization if not already done so. + //! + nvinfer1::ICudaEngine* get(); + + //! + //! \brief Get the pointer to the ICudaEngine and release the ownership. + //! + nvinfer1::ICudaEngine* release(); + + //! + //! \brief Get the underlying blob storing serialized engine. + //! + EngineBlob const getBlob() const + { +#if (NV_TENSORRT_MAJOR > 8) + ASSERT((!mFileReader || !mFileReader->isOpen()) + && "Attempting to access the glob when there is an open file reader!"); +#endif + if (!mEngineBlob.empty()) + { + return EngineBlob{const_cast(static_cast(mEngineBlob.data())), mEngineBlob.size()}; + } + if (mEngineBlobHostMemory.get() != nullptr && mEngineBlobHostMemory->size() > 0) + { + return EngineBlob{mEngineBlobHostMemory->data(), mEngineBlobHostMemory->size()}; + } + ASSERT(false && "Attempting to access an empty engine!"); + return EngineBlob{nullptr, 0}; + } + + //! + //! \brief Set the underlying blob storing the serialized engine without duplicating IHostMemory. + //! + void setBlob(std::unique_ptr& data) + { + ASSERT(data.get() && data->size() > 0); + mEngineBlobHostMemory = std::move(data); + mEngine.reset(); + } + + //! + //! \brief Set the underlying blob storing the serialized engine without duplicating vector memory. + //! + void setBlob(std::vector&& engineBlob) + { + mEngineBlob = std::move(engineBlob); + mEngine.reset(); + } + + //! + //! \brief Release the underlying blob without deleting the deserialized engine. + //! + void releaseBlob() + { + mEngineBlob.clear(); + mEngineBlobHostMemory.reset(); + } +#if (NV_TENSORRT_MAJOR > 8) + //! + //! \brief Get the file stream reader used for deserialization + //! + samplesCommon::FileStreamReader& getFileReader() + { + ASSERT(mFileReader); + return *mFileReader; + } +#endif + //! + //! \brief Get if safe mode is enabled. + //! + bool isSafe() + { + return mIsSafe; + } + + void setDynamicPlugins(std::vector const& dynamicPlugins) + { + mDynamicPlugins = dynamicPlugins; + } + +private: + bool mIsSafe{false}; + bool mVersionCompatible{false}; + int32_t mDLACore{-1}; + std::vector mEngineBlob; +#if (NV_TENSORRT_MAJOR > 8) + std::unique_ptr mFileReader; +#endif + // Directly use the host memory of a serialized engine instead of duplicating the engine in CPU memory. + std::unique_ptr mEngineBlobHostMemory; + + std::string mTempdir{}; + nvinfer1::TempfileControlFlags mTempfileControls{getTempfileControlDefaults()}; + std::string mLeanDLLPath{}; + std::vector mDynamicPlugins; + + //! \name Owned TensorRT objects + //! Per TensorRT object lifetime requirements as outlined in the developer guide, + //! the runtime must remain live while any engines created by the runtime are live. + //! DO NOT ADJUST the declaration order here: runtime -> (engine). + //! Destruction occurs in reverse declaration order: (engine) -> runtime. + //!@{ + + //! The runtime used to track parent of mRuntime if one exists. + //! Needed to load mRuntime if lean.so is supplied through file system path. + std::unique_ptr mParentRuntime{}; + + //! The runtime that is used to deserialize the engine. + std::unique_ptr mRuntime{}; + + //! If mIsSafe is false, this points to the deserialized std engine + std::unique_ptr mEngine{}; + + //!@} +}; + +struct BuildEnvironment +{ + BuildEnvironment() = delete; + BuildEnvironment(BuildEnvironment const& other) = delete; + BuildEnvironment(BuildEnvironment&& other) = delete; + BuildEnvironment(bool isSafe, bool versionCompatible, int32_t DLACore, std::string const& tempdir, + nvinfer1::TempfileControlFlags tempfileControls, std::string const& leanDLLPath = "") + : engine(isSafe, versionCompatible, DLACore, tempdir, tempfileControls, leanDLLPath) + { + } + + //! \name Owned TensorRT objects + //! Per TensorRT object lifetime requirements as outlined in the developer guide, + //! factory objects must remain live while the objects created by those factories + //! are live (with the exception of builder -> engine). + //! DO NOT ADJUST the declaration order here: builder -> network -> parser. + //! Destruction occurs in reverse declaration order: parser -> network -> builder. + //!@{ + + //! The builder used to build the engine. + std::unique_ptr builder; + + //! The network used by the builder. + std::unique_ptr network; + + //! The parser used to specify the network. + Parser parser; + + //! The engine. + LazilyDeserializedEngine engine; + //!@} +}; + +//! +//! \brief Set up network and config +//! +//! \return boolean Return true if network and config were successfully set +//! +bool setupNetworkAndConfig(const BuildOptions& build, const SystemOptions& sys, nvinfer1::IBuilder& builder, + nvinfer1::INetworkDefinition& network, nvinfer1::IBuilderConfig& config, std::ostream& err, + std::vector>& sparseWeights); + +//! +//! \brief Log refittable layers and weights of a refittable engine +//! +void dumpRefittable(nvinfer1::ICudaEngine& engine); + +//! +//! \brief Load a serialized engine +//! +//! \return Pointer to the engine loaded or nullptr if the operation failed +//! +nvinfer1::ICudaEngine* loadEngine(std::string const& engine, int32_t DLACore, std::ostream& err); + +//! +//! \brief Save an engine into a file +//! +//! \return boolean Return true if the engine was successfully saved +//! +bool saveEngine(nvinfer1::ICudaEngine const& engine, std::string const& fileName, std::ostream& err); + +//! +//! \brief Create an engine from model or serialized file, and optionally save engine +//! +//! \return Pointer to the engine created or nullptr if the creation failed +//! +bool getEngineBuildEnv( + ModelOptions const& model, BuildOptions const& build, SystemOptions& sys, BuildEnvironment& env, std::ostream& err); + +//! +//! \brief Create a serialized network +//! +//! \return Pointer to a host memory for a serialized network +//! +nvinfer1::IHostMemory* networkToSerialized(const BuildOptions& build, const SystemOptions& sys, + nvinfer1::IBuilder& builder, nvinfer1::INetworkDefinition& network, std::ostream& err); + +//! +//! \brief Tranfer model to a serialized network +//! +//! \return Pointer to a host memory for a serialized network +//! +nvinfer1::IHostMemory* modelToSerialized( + const ModelOptions& model, const BuildOptions& build, const SystemOptions& sys, std::ostream& err); + +//! +//! \brief Serialize network and save it into a file +//! +//! \return boolean Return true if the network was successfully serialized and saved +//! +bool serializeAndSave( + const ModelOptions& model, const BuildOptions& build, const SystemOptions& sys, std::ostream& err); + +bool timeRefit(const nvinfer1::INetworkDefinition& network, nvinfer1::ICudaEngine& engine, bool multiThreading); + +//! +//! \brief Set tensor scales from a calibration table +//! +void setTensorScalesFromCalibration(nvinfer1::INetworkDefinition& network, std::vector const& inputFormats, + std::vector const& outputFormats, std::string const& calibrationFile); + +//! +//! \brief Check if safe runtime is loaded. +//! +bool hasSafeRuntime(); + +bool loadStreamingEngineToBuildEnv(std::string const& engine, BuildEnvironment& env, std::ostream& err); + +bool loadEngineToBuildEnv(std::string const& engine, BuildEnvironment& env, std::ostream& err); +} // namespace sample + +#endif // TRT_SAMPLE_ENGINES_H diff --git a/src/Detector/tensorrt_onnx/common/sampleEntrypoints.h b/src/Detector/tensorrt_onnx/common/sampleEntrypoints.h new file mode 100644 index 000000000..cc8bf1b9b --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleEntrypoints.h @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef TRT_SAMPLE_ENTRYPOINTS_H +#define TRT_SAMPLE_ENTRYPOINTS_H + +//! \file sampleEntrypoints.h +//! +//! Declares and conditionally defines entrypoints needed to create base TensorRT objects, depending +//! on whether the given sample uses TRT at link time or dynamically. Since common code is built once +//! and shared across all samples (both link-time and dynamic TRT), it does not define these entrypoints, +//! so each sample must define them individually. +//! +//! Samples that use TRT at link time can define DEFINE_TRT_ENTRYPOINTS before including this header to +//! pick up the definitions here. + +#include "NvInfer.h" +#include "NvOnnxParser.h" +#include "logger.h" + +extern nvinfer1::IBuilder* createBuilder(); +extern nvinfer1::IRuntime* createRuntime(); +extern nvinfer1::IRefitter* createRefitter(nvinfer1::ICudaEngine& engine); + +extern nvonnxparser::IParser* createONNXParser(nvinfer1::INetworkDefinition& network); + +#if !defined(DEFINE_TRT_ENTRYPOINTS) +#define DEFINE_TRT_ENTRYPOINTS 0 +#endif + +// Allow opting out of individual entrypoints that are unused by the sample +#if !defined(DEFINE_TRT_BUILDER_ENTRYPOINT) +#define DEFINE_TRT_BUILDER_ENTRYPOINT 1 +#endif +#if !defined(DEFINE_TRT_RUNTIME_ENTRYPOINT) +#define DEFINE_TRT_RUNTIME_ENTRYPOINT 1 +#endif +#if !defined(DEFINE_TRT_REFITTER_ENTRYPOINT) +#define DEFINE_TRT_REFITTER_ENTRYPOINT 1 +#endif +#if !defined(DEFINE_TRT_ONNX_PARSER_ENTRYPOINT) +#define DEFINE_TRT_ONNX_PARSER_ENTRYPOINT 1 +#endif +#if !defined(DEFINE_TRT_LEGACY_PARSER_ENTRYPOINT) +#define DEFINE_TRT_LEGACY_PARSER_ENTRYPOINT 1 +#endif + +#if DEFINE_TRT_ENTRYPOINTS +nvinfer1::IBuilder* createBuilder() +{ +#if DEFINE_TRT_BUILDER_ENTRYPOINT + return nvinfer1::createInferBuilder(sample::gLogger.getTRTLogger()); +#else + return {}; +#endif +} + +nvinfer1::IRuntime* createRuntime() +{ +#if DEFINE_TRT_RUNTIME_ENTRYPOINT + return nvinfer1::createInferRuntime(sample::gLogger.getTRTLogger()); +#else + return {}; +#endif +} + +nvinfer1::IRefitter* createRefitter(nvinfer1::ICudaEngine& engine) +{ +#if DEFINE_TRT_REFITTER_ENTRYPOINT + return nvinfer1::createInferRefitter(engine, sample::gLogger.getTRTLogger()); +#else + return {}; +#endif +} + +nvonnxparser::IParser* createONNXParser(nvinfer1::INetworkDefinition& network) +{ +#if DEFINE_TRT_ONNX_PARSER_ENTRYPOINT + return nvonnxparser::createParser(network, sample::gLogger.getTRTLogger()); +#else + return {}; +#endif +} + +#endif // DEFINE_TRT_ENTRYPOINTS + +#endif // TRT_SAMPLE_ENTRYPOINTS_H diff --git a/src/Detector/tensorrt_onnx/common/sampleInference.cpp b/src/Detector/tensorrt_onnx/common/sampleInference.cpp new file mode 100644 index 000000000..f0470bf7d --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleInference.cpp @@ -0,0 +1,1650 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__QNX__) +#include +#include +#endif + +#include "NvInfer.h" + +#include "ErrorRecorder.h" +#include "bfloat16.h" +#include "logger.h" +#include "sampleDevice.h" +#include "sampleEngines.h" +#include "sampleInference.h" +#include "sampleOptions.h" +#include "sampleReporting.h" +#include "sampleUtils.h" +using namespace nvinfer1; +namespace sample +{ + +template +bool validateTensorNames(TMapType const& map, TEngineType const* engine, int32_t const endBindingIndex) +{ + // Check if the provided input tensor names match the input tensors of the engine. + // Throw an error if the provided input tensor names cannot be found because it implies a potential typo. + for (auto const& item : map) + { + bool tensorNameFound{false}; + for (int32_t b = 0; b < endBindingIndex; ++b) + { + auto const tensorName = engine->getIOTensorName(b); + auto const tensorIOMode = engine->getTensorIOMode(tensorName); + if (tensorIOMode == nvinfer1::TensorIOMode::kINPUT && matchStringWithOneWildcard(item.first, tensorName)) + { + tensorNameFound = true; + break; + } + } + if (!tensorNameFound) + { + sample::gLogError << "Cannot find input tensor with name \"" << item.first << "\" in the engine bindings! " + << "Please make sure the input tensor names are correct." << std::endl; + return false; + } + } + return true; +} + +template +class FillBindingClosure +{ +private: + using InputsMap = std::unordered_map; + using BindingsVector = std::vector>; + + TEngineType const* mEngine; + nvinfer1::IExecutionContext const* mContext; + InputsMap const& inputs; + BindingsVector& bindings; + int32_t batch; + int32_t endBindingIndex; + int32_t profileIndex; + + void fillOneBinding(TensorInfo const& tensorInfo) + { + auto const name = tensorInfo.name; + auto const* bindingInOutStr = tensorInfo.isInput ? "Input" : "Output"; + for (auto& binding : bindings) + { + auto const input = findPlausible(inputs, name); + if (tensorInfo.isInput && input != inputs.end()) + { + sample::gLogInfo << "Using values loaded from " << input->second << " for input " << name << std::endl; + binding->addBinding(tensorInfo, input->second); + } + else + { + if (tensorInfo.isInput) + { + sample::gLogInfo << "Using random values for input " << name << std::endl; + } + binding->addBinding(tensorInfo); + } + if (tensorInfo.isDynamic) + { + sample::gLogInfo << bindingInOutStr << " binding for " << name + << " is dynamic and will be created during execution using OutputAllocator." + << std::endl; + } + else + { + sample::gLogInfo << bindingInOutStr << " binding for " << name << " with dimensions " << tensorInfo.dims + << " is created." << std::endl; + } + } + } + + bool fillAllBindings(int32_t batch, int32_t endBindingIndex) + { + if (!validateTensorNames(inputs, mEngine, endBindingIndex)) + { + sample::gLogError << "Invalid tensor names found in --loadInputs flag." << std::endl; + return false; + } + for (int32_t b = 0; b < endBindingIndex; b++) + { + TensorInfo tensorInfo; + tensorInfo.bindingIndex = b; + getTensorInfo(tensorInfo); + tensorInfo.updateVolume(batch); + fillOneBinding(tensorInfo); + } + return true; + } + + void getTensorInfo(TensorInfo& tensorInfo); + +public: + FillBindingClosure(TEngineType const* _engine, nvinfer1::IExecutionContext const* _context, + InputsMap const& _inputs, BindingsVector& _bindings, int32_t _batch, int32_t _endBindingIndex, + int32_t _profileIndex) + : mEngine(_engine) + , mContext(_context) + , inputs(_inputs) + , bindings(_bindings) + , batch(_batch) + , endBindingIndex(_endBindingIndex) + , profileIndex(_profileIndex) + { + } + + bool operator()() + { + return fillAllBindings(batch, endBindingIndex); + } +}; + +template <> +void FillBindingClosure::getTensorInfo(TensorInfo& tensorInfo) +{ + auto const b = tensorInfo.bindingIndex; + auto const name = mEngine->getIOTensorName(b); + tensorInfo.name = name; + tensorInfo.dims = mContext->getTensorShape(name); + tensorInfo.isDynamic = std::any_of( + tensorInfo.dims.d, tensorInfo.dims.d + tensorInfo.dims.nbDims, [](int32_t dim) { return dim == -1; }); + tensorInfo.comps = mEngine->getTensorComponentsPerElement(name, profileIndex); + tensorInfo.strides = mContext->getTensorStrides(name); + tensorInfo.vectorDimIndex = mEngine->getTensorVectorizedDim(name, profileIndex); + tensorInfo.isInput = mEngine->getTensorIOMode(name) == TensorIOMode::kINPUT; + tensorInfo.dataType = mEngine->getTensorDataType(name); +} + +namespace +{ +bool allocateContextMemory(InferenceEnvironment& iEnv, InferenceOptions const& inference) +{ + auto* engine = iEnv.engine.get(); + iEnv.deviceMemory.resize(inference.infStreams); + // Delay context memory allocation until input shapes are specified because runtime allocation would require actual + // input shapes. + for (int32_t i = 0; i < inference.infStreams; ++i) + { + auto const& ec = iEnv.contexts.at(i); + if (inference.memoryAllocationStrategy == MemoryAllocationStrategy::kSTATIC) + { + sample::gLogInfo << "Created execution context with device memory size: " + << (engine->getDeviceMemorySize() / 1.0_MiB) << " MiB" << std::endl; + } + else + { +#if (NV_TENSORRT_MAJOR > 8) + size_t sizeToAlloc{0}; + const char* allocReason{nullptr}; + if (inference.memoryAllocationStrategy == MemoryAllocationStrategy::kPROFILE) + { + auto const p = inference.optProfileIndex; + sizeToAlloc = engine->getDeviceMemorySizeForProfile(p); + allocReason = "current profile"; + } + else if (inference.memoryAllocationStrategy == MemoryAllocationStrategy::kRUNTIME) + { + sizeToAlloc = ec->updateDeviceMemorySizeForShapes(); + allocReason = "current input shapes"; + } + else + { + sample::gLogError << "Unrecognizable memory allocation strategy." << std::endl; + return false; + } + iEnv.deviceMemory.at(i) = TrtDeviceBuffer(sizeToAlloc); + ec->setDeviceMemoryV2(iEnv.deviceMemory.at(i).get(), iEnv.deviceMemory.at(i).getSize()); + sample::gLogInfo << "Maximum device memory size across all profiles: " + << (engine->getDeviceMemorySizeV2() / 1.0_MiB) << " MiB" << std::endl; + sample::gLogInfo << "Only allocated device memory enough for " << allocReason << ": " + << (sizeToAlloc / 1.0_MiB) << " MiB" << std::endl; +#else + std::cerr << "setDeviceMemoryV2 worked only in TensorRT 10.x and higher" << std::endl; +#endif + } + } + return true; +} +} // namespace + +bool setUpInference(InferenceEnvironment& iEnv, InferenceOptions const& inference, SystemOptions const& system) +{ +#if TRT_WINML + int32_t const isIntegrated{}; +#else + int32_t device{}; + cudaCheck(cudaGetDevice(&device)); + + cudaDeviceProp properties; + cudaCheck(cudaGetDeviceProperties(&properties, device)); + int32_t const isIntegrated{properties.integrated}; +#endif + // Use managed memory on integrated devices when transfers are skipped + // and when it is explicitly requested on the commandline. + bool useManagedMemory{(inference.skipTransfers && isIntegrated) || inference.useManaged}; + SMP_RETVAL_IF_FALSE(!iEnv.safe, "Safe inference is not supported!", false, sample::gLogError); + + using FillStdBindings = FillBindingClosure; + + auto* engine = iEnv.engine.get(); + SMP_RETVAL_IF_FALSE(engine != nullptr, "Got invalid engine!", false, sample::gLogError); + + // Release serialized blob to save memory space. + iEnv.engine.releaseBlob(); + +#if (NV_TENSORRT_MAJOR > 8) + // Setup weight streaming if enabled + if (engine->getStreamableWeightsSize() > 0) + { + auto const& budget = inference.weightStreamingBudget; + int64_t wsBudget = budget.bytes; + if (budget.percent != 100.0) + { + double const percent = budget.percent; + ASSERT(percent < 100.0); + auto const max = engine->getStreamableWeightsSize(); + wsBudget = (max >= 0) ? (percent / 100) * (max) : WeightStreamingBudget::kDISABLE; + } + + if (wsBudget == WeightStreamingBudget::kDISABLE) + { + wsBudget = engine->getStreamableWeightsSize(); + } + else if (wsBudget == WeightStreamingBudget::kAUTOMATIC) + { + wsBudget = engine->getWeightStreamingAutomaticBudget(); + } + ASSERT(wsBudget >= 0); + bool success = engine->setWeightStreamingBudgetV2(wsBudget); + SMP_RETVAL_IF_FALSE(success, "Failed to set weight streaming limit!", false, sample::gLogError); + switch (wsBudget) + { + case WeightStreamingBudget::kDISABLE: + { + sample::gLogInfo << "Weight streaming has been disabled at runtime." << std::endl; + break; + } + + case WeightStreamingBudget::kAUTOMATIC: + { + sample::gLogInfo << "The weight streaming budget will automatically be chosen by TensorRT." << std::endl; + break; + } + default: + { + sample::gLogInfo << "Weight streaming is enabled with a device memory limit of " << wsBudget << " bytes." + << std::endl; + break; + } + } + } +#endif + + int32_t const nbOptProfiles = engine->getNbOptimizationProfiles(); + + if (inference.optProfileIndex >= nbOptProfiles) + { + sample::gLogError << "Selected profile index " << inference.optProfileIndex + << " exceeds the number of profiles that the engine holds. " << std::endl; + return false; + } + + if (nbOptProfiles > 1 && !inference.setOptProfile) + { + sample::gLogWarning << nbOptProfiles + << " profiles detected but not set. Running with profile 0. Please use " + "--dumpOptimizationProfile to see all available profiles." + << std::endl; + } + + cudaStream_t setOptProfileStream; + CHECK(cudaStreamCreate(&setOptProfileStream)); + + for (int32_t s = 0; s < inference.infStreams; ++s) + { + IExecutionContext* ec{nullptr}; + if (inference.memoryAllocationStrategy == MemoryAllocationStrategy::kSTATIC) + { + // Let TRT pre-allocate and manage the memory. + ec = engine->createExecutionContext(); + } +#if (NV_TENSORRT_MAJOR > 8) + else + { + // Allocate based on the current profile or runtime shapes. + ec = engine->createExecutionContext(ExecutionContextAllocationStrategy::kUSER_MANAGED); + } +#endif + if (ec == nullptr) + { + sample::gLogError << "Unable to create execution context for stream " << s << "." << std::endl; + return false; + } + ec->setNvtxVerbosity(inference.nvtxVerbosity); + +#if !TRT_WINML + int32_t const persistentCacheLimit + = samplesCommon::getMaxPersistentCacheSize() * inference.persistentCacheRatio; + sample::gLogInfo << "Setting persistentCacheLimit to " << persistentCacheLimit << " bytes." << std::endl; + ec->setPersistentCacheLimit(persistentCacheLimit); +#endif + + auto setProfile = ec->setOptimizationProfileAsync(inference.optProfileIndex, setOptProfileStream); + CHECK(cudaStreamSynchronize(setOptProfileStream)); + + if (!setProfile) + { + sample::gLogError << "Set optimization profile failed. " << std::endl; + if (inference.infStreams > 1) + { + sample::gLogError + << "Please ensure that the engine is built with preview feature profileSharing0806 enabled. " + << std::endl; + } + return false; + } + + iEnv.contexts.emplace_back(ec); + iEnv.bindings.emplace_back(new Bindings(useManagedMemory)); + } + + CHECK(cudaStreamDestroy(setOptProfileStream)); + + if (iEnv.profiler) + { + iEnv.contexts.front()->setProfiler(iEnv.profiler.get()); + // Always run reportToProfiler() after enqueue launch + iEnv.contexts.front()->setEnqueueEmitsProfile(false); + } + + int32_t const endBindingIndex = engine->getNbIOTensors(); + + // Make sure that the tensor names provided in command-line args actually exist in any of the engine bindings + // to avoid silent typos. + if (!validateTensorNames(inference.shapes, engine, endBindingIndex)) + { + sample::gLogError << "Invalid tensor names found in --shapes flag." << std::endl; + return false; + } + + for (int32_t b = 0; b < endBindingIndex; ++b) + { + auto const& name = engine->getIOTensorName(b); + auto const& mode = engine->getTensorIOMode(name); + if (mode == TensorIOMode::kINPUT) + { + Dims const dims = iEnv.contexts.front()->getTensorShape(name); + bool isShapeInferenceIO{false}; + isShapeInferenceIO = engine->isShapeInferenceIO(name); + bool const hasRuntimeDim = std::any_of(dims.d, dims.d + dims.nbDims, [](int32_t dim) { return dim == -1; }); + auto const shape = findPlausible(inference.shapes, name); + if (hasRuntimeDim || isShapeInferenceIO) + { + // Set shapeData to either dimensions of the input (if it has a dynamic shape) + // or set to values of the input (if it is an input shape tensor). + std::vector shapeData; + + if (shape == inference.shapes.end()) + { + // No information provided. Use default value for missing data. + constexpr int32_t kDEFAULT_VALUE = 1; + if (isShapeInferenceIO) + { + // Set shape tensor to all ones. + shapeData.assign(volume(dims, 0, dims.nbDims), kDEFAULT_VALUE); + sample::gLogWarning << "Values missing for input shape tensor: " << name + << "Automatically setting values to: " << shapeData << std::endl; + } + else + { + // Use default value for unspecified runtime dimensions. + shapeData.resize(dims.nbDims); + std::transform(dims.d, dims.d + dims.nbDims, shapeData.begin(), + [&](int32_t dimension) { return dimension >= 0 ? dimension : kDEFAULT_VALUE; }); + sample::gLogWarning << "Shape missing for input with dynamic shape: " << name + << "Automatically setting shape to: " << shapeData << std::endl; + } + } + else if (inference.inputs.count(shape->first) && isShapeInferenceIO) + { + // Load shape tensor from file. + int64_t const size = volume(dims, 0, dims.nbDims); + shapeData.resize(size); + auto const& filename = inference.inputs.at(shape->first); + auto dst = reinterpret_cast(shapeData.data()); + loadFromFile(filename, dst, size * sizeof(decltype(shapeData)::value_type)); + } + else + { + shapeData = shape->second; + } + + int32_t* shapeTensorData{nullptr}; + if (isShapeInferenceIO) + { + // Save the data in iEnv, in a way that it's address does not change + // before enqueueV3 is called. + iEnv.inputShapeTensorValues.emplace_back(shapeData); + shapeTensorData = iEnv.inputShapeTensorValues.back().data(); + } + + for (auto& c : iEnv.contexts) + { + if (isShapeInferenceIO) + { + sample::gLogInfo << "Set input shape tensor " << name << " to: " << shapeData << std::endl; + if (!c->setTensorAddress(name, shapeTensorData)) + { + return false; + } + } + else + { + sample::gLogInfo << "Set shape of input tensor " << name << " to: " << shapeData + << std::endl; + if (!c->setInputShape(name, toDims(shapeData))) + { + return false; + } + } + } + } + else if (nbOptProfiles && shape != inference.shapes.end()) + { + // Check if the provided shape matches the static dimensions in the engine. + for (auto& c : iEnv.contexts) + { + if (!c->setInputShape(name, toDims(shape->second))) + { + sample::gLogError << "The engine was built with static shapes for input tensor " << name + << " but the provided shapes do not match the static shapes!" << std::endl; + return false; + } + } + } + } + } +#if (NV_TENSORRT_MAJOR > 8) + // Create Debug Listener and turn on debug states if client requested dumping debug tensors. + if (!inference.debugTensorFileNames.empty()) + { + iEnv.listener.reset(new DebugTensorWriter(inference.debugTensorFileNames)); + iEnv.contexts.front()->setDebugListener(iEnv.listener.get()); + for (auto const& s : inference.debugTensorFileNames) + { + iEnv.contexts.front()->setTensorDebugState(s.first.c_str(), true); + } + } +#else + std::cerr << "Can not setDebugListener. Use TensorRT 10.x or higher" << std::endl; +#endif + + if (!allocateContextMemory(iEnv, inference)) + { + return false; + } + + auto const* context = iEnv.contexts.front().get(); + return FillStdBindings( + engine, context, inference.inputs, iEnv.bindings, 1, endBindingIndex, inference.optProfileIndex)(); +} + +TaskInferenceEnvironment::TaskInferenceEnvironment( + std::string engineFile, InferenceOptions inference, int32_t deviceId, int32_t DLACore, int32_t bs) + : iOptions(inference) + , device(deviceId) + , batch(bs) +{ + BuildEnvironment bEnv(/* isSafe */ false, /* versionCompatible */ false, DLACore, "", getTempfileControlDefaults()); + loadEngineToBuildEnv(engineFile, bEnv, sample::gLogError); + std::unique_ptr tmp(new InferenceEnvironment(bEnv)); + iEnv = std::move(tmp); + + cudaCheck(cudaSetDevice(device)); + SystemOptions system{}; + system.device = device; + system.DLACore = DLACore; + if (!setUpInference(*iEnv, iOptions, system)) + { + sample::gLogError << "Inference set up failed" << std::endl; + } +} +namespace +{ + +#if defined(__QNX__) +using TimePoint = double; +#else +using TimePoint = std::chrono::time_point; +#endif + +TimePoint getCurrentTime() +{ +#if defined(__QNX__) + uint64_t const currentCycles = ClockCycles(); + uint64_t const cyclesPerSecond = SYSPAGE_ENTRY(qtime)->cycles_per_sec; + // Return current timestamp in ms. + return static_cast(currentCycles) * 1000. / cyclesPerSecond; +#else + return std::chrono::high_resolution_clock::now(); +#endif +} + +//! +//! \struct SyncStruct +//! \brief Threads synchronization structure +//! +struct SyncStruct +{ + std::mutex mutex; + TrtCudaStream mainStream; + TrtCudaEvent gpuStart{cudaEventBlockingSync}; + TimePoint cpuStart{}; + float sleep{}; +}; + +struct Enqueue +{ + explicit Enqueue(nvinfer1::IExecutionContext& context) + : mContext(context) + { + } + + nvinfer1::IExecutionContext& mContext; +}; + +//! +//! \class EnqueueExplicit +//! \brief Functor to enqueue inference with explict batch +//! +class EnqueueExplicit : private Enqueue +{ + +public: + explicit EnqueueExplicit(nvinfer1::IExecutionContext& context, Bindings const& bindings) + : Enqueue(context) + , mBindings(bindings) + { + ASSERT(mBindings.setTensorAddresses(mContext)); + } + + bool operator()(TrtCudaStream& stream) const + { + try + { + bool const result = mContext.enqueueV3(stream.get()); + // Collecting layer timing info from current profile index of execution context, except under capturing + // mode. + if (!isStreamCapturing(stream) && mContext.getProfiler() && !mContext.getEnqueueEmitsProfile() + && !mContext.reportToProfiler()) + { + gLogWarning << "Failed to collect layer timing info from previous enqueueV3()" << std::endl; + } + return result; + } + catch (const std::exception&) + { + return false; + } + return false; + } + +private: + // Helper function to check if a stream is in capturing mode. + bool isStreamCapturing(TrtCudaStream& stream) const + { + cudaStreamCaptureStatus status{cudaStreamCaptureStatusNone}; + cudaCheck(cudaStreamIsCapturing(stream.get(), &status)); + return status != cudaStreamCaptureStatusNone; + } + + Bindings const& mBindings; +}; + +//! +//! \class EnqueueGraph +//! \brief Functor to enqueue inference from CUDA Graph +//! +class EnqueueGraph +{ + +public: + explicit EnqueueGraph(nvinfer1::IExecutionContext& context, TrtCudaGraph& graph) + : mGraph(graph) + , mContext(context) + { + } + + bool operator()(TrtCudaStream& stream) const + { + if (mGraph.launch(stream)) + { + // Collecting layer timing info from current profile index of execution context + if (mContext.getProfiler() && !mContext.getEnqueueEmitsProfile() && !mContext.reportToProfiler()) + { + gLogWarning << "Failed to collect layer timing info from previous CUDA graph launch" << std::endl; + } + return true; + } + return false; + } + + TrtCudaGraph& mGraph; + nvinfer1::IExecutionContext& mContext; +}; + +//! +//! \class EnqueueGraphSafe +//! \brief Functor to enqueue inference from CUDA Graph +//! +class EnqueueGraphSafe +{ + +public: + explicit EnqueueGraphSafe(TrtCudaGraph& graph) + : mGraph(graph) + { + } + + bool operator()(TrtCudaStream& stream) const + { + return mGraph.launch(stream); + } + + TrtCudaGraph& mGraph; +}; + +using EnqueueFunction = std::function; + +enum class StreamType : int32_t +{ + kINPUT = 0, + kCOMPUTE = 1, + kOUTPUT = 2, + kNUM = 3 +}; + +enum class EventType : int32_t +{ + kINPUT_S = 0, + kINPUT_E = 1, + kCOMPUTE_S = 2, + kCOMPUTE_E = 3, + kOUTPUT_S = 4, + kOUTPUT_E = 5, + kNUM = 6 +}; + +using MultiStream = std::array(StreamType::kNUM)>; + +using MultiEvent = std::array, static_cast(EventType::kNUM)>; + +using EnqueueTimes = std::array; + +//! +//! \class Iteration +//! \brief Inference iteration and streams management +//! +class Iteration +{ + +public: + Iteration(int32_t id, InferenceOptions const& inference, nvinfer1::IExecutionContext& context, Bindings& bindings) + : mBindings(bindings) + , mStreamId(id) + , mDepth(1 + inference.overlap) + , mActive(mDepth) + , mEvents(mDepth) + , mEnqueueTimes(mDepth) + , mContext(&context) + { + for (int32_t d = 0; d < mDepth; ++d) + { + for (int32_t e = 0; e < static_cast(EventType::kNUM); ++e) + { + mEvents[d][e].reset(new TrtCudaEvent(!inference.spin)); + } + } + createEnqueueFunction(inference, context, bindings); + } + + bool query(bool skipTransfers) + { + if (mActive[mNext]) + { + return true; + } + + if (!skipTransfers) + { + record(EventType::kINPUT_S, StreamType::kINPUT); + setInputData(false); + record(EventType::kINPUT_E, StreamType::kINPUT); + wait(EventType::kINPUT_E, StreamType::kCOMPUTE); // Wait for input DMA before compute + } + + record(EventType::kCOMPUTE_S, StreamType::kCOMPUTE); + recordEnqueueTime(); + if (!mEnqueue(getStream(StreamType::kCOMPUTE))) + { + return false; + } + recordEnqueueTime(); + record(EventType::kCOMPUTE_E, StreamType::kCOMPUTE); + + if (!skipTransfers) + { + wait(EventType::kCOMPUTE_E, StreamType::kOUTPUT); // Wait for compute before output DMA + record(EventType::kOUTPUT_S, StreamType::kOUTPUT); + fetchOutputData(false); + record(EventType::kOUTPUT_E, StreamType::kOUTPUT); + } + + mActive[mNext] = true; + moveNext(); + return true; + } + + float sync( + TimePoint const& cpuStart, TrtCudaEvent const& gpuStart, std::vector& trace, bool skipTransfers) + { + if (mActive[mNext]) + { + if (skipTransfers) + { + getEvent(EventType::kCOMPUTE_E).synchronize(); + } + else + { + getEvent(EventType::kOUTPUT_E).synchronize(); + } + trace.emplace_back(getTrace(cpuStart, gpuStart, skipTransfers)); + mActive[mNext] = false; + return getEvent(EventType::kCOMPUTE_S) - gpuStart; + } + return 0; + } + + void syncAll( + TimePoint const& cpuStart, TrtCudaEvent const& gpuStart, std::vector& trace, bool skipTransfers) + { + for (int32_t d = 0; d < mDepth; ++d) + { + sync(cpuStart, gpuStart, trace, skipTransfers); + moveNext(); + } + } + + void wait(TrtCudaEvent& gpuStart) + { + getStream(StreamType::kINPUT).wait(gpuStart); + } + + void setInputData(bool sync) + { + mBindings.transferInputToDevice(getStream(StreamType::kINPUT)); + // additional sync to avoid overlapping with inference execution. + if (sync) + { + getStream(StreamType::kINPUT).synchronize(); + } + } + + void fetchOutputData(bool sync) + { + mBindings.transferOutputToHost(getStream(StreamType::kOUTPUT)); + // additional sync to avoid overlapping with inference execution. + if (sync) + { + getStream(StreamType::kOUTPUT).synchronize(); + } + } + +private: + void moveNext() + { + mNext = mDepth - 1 - mNext; + } + + TrtCudaStream& getStream(StreamType t) + { + return mStream[static_cast(t)]; + } + + TrtCudaEvent& getEvent(EventType t) + { + return *mEvents[mNext][static_cast(t)]; + } + + void record(EventType e, StreamType s) + { + getEvent(e).record(getStream(s)); + } + + void recordEnqueueTime() + { + mEnqueueTimes[mNext][enqueueStart] = getCurrentTime(); + enqueueStart = 1 - enqueueStart; + } + + TimePoint getEnqueueTime(bool start) + { + return mEnqueueTimes[mNext][start ? 0 : 1]; + } + + void wait(EventType e, StreamType s) + { + getStream(s).wait(getEvent(e)); + } + + InferenceTrace getTrace(TimePoint const& cpuStart, TrtCudaEvent const& gpuStart, bool skipTransfers) + { + float is + = skipTransfers ? getEvent(EventType::kCOMPUTE_S) - gpuStart : getEvent(EventType::kINPUT_S) - gpuStart; + float ie + = skipTransfers ? getEvent(EventType::kCOMPUTE_S) - gpuStart : getEvent(EventType::kINPUT_E) - gpuStart; + float os + = skipTransfers ? getEvent(EventType::kCOMPUTE_E) - gpuStart : getEvent(EventType::kOUTPUT_S) - gpuStart; + float oe + = skipTransfers ? getEvent(EventType::kCOMPUTE_E) - gpuStart : getEvent(EventType::kOUTPUT_E) - gpuStart; + + return InferenceTrace(mStreamId, + std::chrono::duration(getEnqueueTime(true) - cpuStart).count(), + std::chrono::duration(getEnqueueTime(false) - cpuStart).count(), is, ie, + getEvent(EventType::kCOMPUTE_S) - gpuStart, getEvent(EventType::kCOMPUTE_E) - gpuStart, os, oe); + } + + void createEnqueueFunction( + InferenceOptions const& inference, nvinfer1::IExecutionContext& context, Bindings& bindings) + { + mEnqueue = EnqueueFunction(EnqueueExplicit(context, mBindings)); + if (inference.graph) + { + sample::gLogInfo << "Capturing CUDA graph for the current execution context" << std::endl; + + TrtCudaStream& stream = getStream(StreamType::kCOMPUTE); + // Avoid capturing initialization calls by executing the enqueue function at least + // once before starting CUDA graph capture. + auto const ret = mEnqueue(stream); + if (!ret) + { + throw std::runtime_error("Inference enqueue failed."); + } + stream.synchronize(); + + mGraph.beginCapture(stream); + // The built TRT engine may contain operations that are not permitted under CUDA graph capture mode. + // When the stream is capturing, the enqueue call may return false if the current CUDA graph capture fails. + if (mEnqueue(stream)) + { + mGraph.endCapture(stream); + mEnqueue = EnqueueFunction(EnqueueGraph(context, mGraph)); + sample::gLogInfo << "Successfully captured CUDA graph for the current execution context" << std::endl; + } + else + { + mGraph.endCaptureOnError(stream); + // Ensure any CUDA error has been cleaned up. + cudaCheck(cudaGetLastError()); + sample::gLogWarning << "The built TensorRT engine contains operations that are not permitted under " + "CUDA graph capture mode." + << std::endl; + sample::gLogWarning << "The specified --useCudaGraph flag has been ignored. The inference will be " + "launched without using CUDA graph launch." + << std::endl; + } + } + } + + Bindings& mBindings; + + TrtCudaGraph mGraph; + EnqueueFunction mEnqueue; + + int32_t mStreamId{0}; + int32_t mNext{0}; + int32_t mDepth{2}; // default to double buffer to hide DMA transfers + + std::vector mActive; + MultiStream mStream; + std::vector mEvents; + + int32_t enqueueStart{0}; + std::vector mEnqueueTimes; + nvinfer1::IExecutionContext* mContext{nullptr}; +}; + +bool inferenceLoop(std::vector>& iStreams, TimePoint const& cpuStart, + TrtCudaEvent const& gpuStart, int iterations, float maxDurationMs, float warmupMs, + std::vector& trace, bool skipTransfers, float idleMs) +{ + float durationMs = 0; + int32_t skip = 0; + + if (maxDurationMs == -1.F) + { + sample::gLogWarning << "--duration=-1 is specified, inference will run in an endless loop until" + << " aborted with CTRL-C (SIGINT)" << std::endl; + while (true) + { + for (auto& s : iStreams) + { + if (!s->query(skipTransfers)) + { + return false; + } + } + for (auto& s : iStreams) + { + s->sync(cpuStart, gpuStart, trace, skipTransfers); + } + } + } + + for (int32_t i = 0; i < iterations + skip || durationMs < maxDurationMs; ++i) + { + for (auto& s : iStreams) + { + if (!s->query(skipTransfers)) + { + return false; + } + } + for (auto& s : iStreams) + { + durationMs = std::max(durationMs, s->sync(cpuStart, gpuStart, trace, skipTransfers)); + } + if (durationMs < warmupMs) // Warming up + { + if (durationMs) // Skip complete iterations + { + ++skip; + } + continue; + } + if (idleMs != 0.F) + { + std::this_thread::sleep_for(std::chrono::duration(idleMs)); + } + } + for (auto& s : iStreams) + { + s->syncAll(cpuStart, gpuStart, trace, skipTransfers); + } + return true; +} + +void inferenceExecution(InferenceOptions const& inference, InferenceEnvironment& iEnv, SyncStruct& sync, + int32_t const threadIdx, int32_t const streamsPerThread, int32_t device, + std::vector& trace) noexcept +{ + try + { + float warmupMs = inference.warmup; + float durationMs = -1.F; + if (inference.duration != -1.F) + { + durationMs = inference.duration * 1000.F + warmupMs; + } + + cudaCheck(cudaSetDevice(device)); + + std::vector> iStreams; + + for (int32_t s = 0; s < streamsPerThread; ++s) + { + int32_t const streamId{threadIdx * streamsPerThread + s}; + auto* iteration = new Iteration(streamId, inference, *iEnv.getContext(streamId), *iEnv.bindings[streamId]); + if (inference.skipTransfers) + { + iteration->setInputData(true); + } + iStreams.emplace_back(iteration); + } + + for (auto& s : iStreams) + { + s->wait(sync.gpuStart); + } + + std::vector localTrace; + if (!inferenceLoop(iStreams, sync.cpuStart, sync.gpuStart, inference.iterations, durationMs, warmupMs, + localTrace, inference.skipTransfers, inference.idle)) + { + sync.mutex.lock(); + iEnv.error = true; + sync.mutex.unlock(); + } + + if (inference.skipTransfers) + { + for (auto& s : iStreams) + { + s->fetchOutputData(true); + } + } + + sync.mutex.lock(); + trace.insert(trace.end(), localTrace.begin(), localTrace.end()); + sync.mutex.unlock(); + } + catch (...) + { + sync.mutex.lock(); + iEnv.error = true; + sync.mutex.unlock(); + } +} + +inline std::thread makeThread(InferenceOptions const& inference, InferenceEnvironment& iEnv, SyncStruct& sync, + int32_t threadIdx, int32_t streamsPerThread, int32_t device, std::vector& trace) +{ + return std::thread(inferenceExecution, std::cref(inference), std::ref(iEnv), std::ref(sync), threadIdx, + streamsPerThread, device, std::ref(trace)); +} + +} // namespace + +bool runInference( + InferenceOptions const& inference, InferenceEnvironment& iEnv, int32_t device, std::vector& trace) +{ + SMP_RETVAL_IF_FALSE(!iEnv.safe, "Safe inference is not supported!", false, sample::gLogError); + cudaCheck(cudaProfilerStart()); + + trace.resize(0); + + SyncStruct sync; + sync.sleep = inference.sleep; + sync.mainStream.sleep(&sync.sleep); + sync.cpuStart = getCurrentTime(); + sync.gpuStart.record(sync.mainStream); + + // When multiple streams are used, trtexec can run inference in two modes: + // (1) if inference.threads is true, then run each stream on each thread. + // (2) if inference.threads is false, then run all streams on the same thread. + int32_t const numThreads = inference.threads ? inference.infStreams : 1; + int32_t const streamsPerThread = inference.threads ? 1 : inference.infStreams; + + std::vector threads; + for (int32_t threadIdx = 0; threadIdx < numThreads; ++threadIdx) + { + threads.emplace_back(makeThread(inference, iEnv, sync, threadIdx, streamsPerThread, device, trace)); + } + for (auto& th : threads) + { + th.join(); + } + + cudaCheck(cudaProfilerStop()); + + auto cmpTrace = [](InferenceTrace const& a, InferenceTrace const& b) { return a.h2dStart < b.h2dStart; }; + std::sort(trace.begin(), trace.end(), cmpTrace); + + return !iEnv.error; +} + +bool runMultiTasksInference(std::vector>& tEnvList) +{ + cudaCheck(cudaProfilerStart()); + cudaSetDeviceFlags(cudaDeviceScheduleSpin); + + SyncStruct sync; + sync.sleep = 0; + sync.mainStream.sleep(&sync.sleep); + sync.cpuStart = getCurrentTime(); + sync.gpuStart.record(sync.mainStream); + + std::vector threads; + for (size_t i = 0; i < tEnvList.size(); ++i) + { + auto& tEnv = tEnvList[i]; + threads.emplace_back(makeThread( + tEnv->iOptions, *(tEnv->iEnv), sync, /*threadIdx*/ 0, /*streamsPerThread*/ 1, tEnv->device, tEnv->trace)); + } + for (auto& th : threads) + { + th.join(); + } + + cudaCheck(cudaProfilerStop()); + + auto cmpTrace = [](InferenceTrace const& a, InferenceTrace const& b) { return a.h2dStart < b.h2dStart; }; + for (auto& tEnv : tEnvList) + { + std::sort(tEnv->trace.begin(), tEnv->trace.end(), cmpTrace); + } + + return std::none_of(tEnvList.begin(), tEnvList.end(), + [](std::unique_ptr& tEnv) { return tEnv->iEnv->error; }); +} + +namespace +{ +size_t reportGpuMemory() +{ + static size_t prevFree{0}; + size_t free{0}; + size_t total{0}; + size_t newlyAllocated{0}; + cudaCheck(cudaMemGetInfo(&free, &total)); + sample::gLogInfo << "Free GPU memory = " << free / 1024.0_MiB << " GiB"; + if (prevFree != 0) + { + newlyAllocated = (prevFree - free); + sample::gLogInfo << ", newly allocated GPU memory = " << newlyAllocated / 1024.0_MiB << " GiB"; + } + sample::gLogInfo << ", total GPU memory = " << total / 1024.0_MiB << " GiB" << std::endl; + prevFree = free; + return newlyAllocated; +} +} // namespace + +//! Returns true if deserialization is slower than expected or fails. +bool timeDeserialize(InferenceEnvironment& iEnv, SystemOptions const& sys) +{ + constexpr int32_t kNB_ITERS{20}; + std::unique_ptr rt{createRuntime()}; + std::unique_ptr engine; + + SMP_RETVAL_IF_FALSE(!iEnv.safe, "Safe inference is not supported!", false, sample::gLogError); + + auto timeDeserializeFn = [&]() -> float { + bool deserializeOK{false}; + engine.reset(nullptr); + auto startClock = std::chrono::high_resolution_clock::now(); + SMP_RETVAL_IF_FALSE(!iEnv.safe, "Safe inference is not supported!", false, sample::gLogError); +#if (NV_TENSORRT_MAJOR > 8) + auto& reader = iEnv.engine.getFileReader(); + reader.reset(); + ASSERT(reader.isOpen()); +#endif +#if !TRT_WINML + for (auto const& pluginPath : sys.dynamicPlugins) + { + rt->getPluginRegistry().loadLibrary(pluginPath.c_str()); + } +#endif +#if (NV_TENSORRT_MAJOR > 8) + engine.reset(rt->deserializeCudaEngine(reader)); +#else + std::cerr << "FileReader is not implemented! Use TensorRT 10.x and higher" << std::endl; +#endif + deserializeOK = (engine != nullptr); + auto endClock = std::chrono::high_resolution_clock::now(); + // return NAN if deserialization failed. + return deserializeOK ? std::chrono::duration(endClock - startClock).count() : NAN; + }; + + // Warmup the caches to make sure that cache thrashing isn't throwing off the results + { + sample::gLogInfo << "Begin deserialization warmup..." << std::endl; + for (int32_t i = 0, e = 2; i < e; ++i) + { + timeDeserializeFn(); + } + } + sample::gLogInfo << "Begin deserialization engine timing..." << std::endl; + float const first = timeDeserializeFn(); + + // Check if first deserialization succeeded. + if (std::isnan(first)) + { + sample::gLogError << "Engine deserialization failed." << std::endl; + return true; + } + + sample::gLogInfo << "First deserialization time = " << first << " milliseconds" << std::endl; + + // Record initial gpu memory state. + reportGpuMemory(); + + float totalTime{0.F}; + for (int32_t i = 0; i < kNB_ITERS; ++i) + { + totalTime += timeDeserializeFn(); + } + auto const averageTime = totalTime / kNB_ITERS; + // reportGpuMemory sometimes reports zero after a single deserialization of a small engine, + // so use the size of memory for all the iterations. + auto const totalEngineSizeGpu = reportGpuMemory(); + sample::gLogInfo << "Total deserialization time = " << totalTime << " milliseconds in " << kNB_ITERS + << " iterations, average time = " << averageTime << " milliseconds, first time = " << first + << " milliseconds." << std::endl; + sample::gLogInfo << "Deserialization Bandwidth = " << 1E-6 * totalEngineSizeGpu / totalTime << " GB/s" << std::endl; + + // If the first deserialization is more than tolerance slower than + // the average deserialization, return true, which means an error occurred. + // The tolerance is set to 2x since the deserialization time is quick and susceptible + // to caching issues causing problems in the first timing. + auto const tolerance = 2.0F; + bool const isSlowerThanExpected = first > averageTime * tolerance; + if (isSlowerThanExpected) + { + sample::gLogInfo << "First deserialization time divided by average time is " << (first / averageTime) + << ". Exceeds tolerance of " << tolerance << "x." << std::endl; + } + return isSlowerThanExpected; +} + +std::string getLayerInformation( + nvinfer1::ICudaEngine* engine, nvinfer1::IExecutionContext* context, nvinfer1::LayerInformationFormat format) +{ + auto runtime = std::unique_ptr{createRuntime()}; + auto inspector = std::unique_ptr(engine->createEngineInspector()); + if (context != nullptr) + { + inspector->setExecutionContext(context); + } + std::string result = inspector->getEngineInformation(format); + return result; +} + +void Binding::fill(std::string const& fileName) +{ + loadFromFile(fileName, static_cast(buffer->getHostBuffer()), buffer->getSize()); +} + +void Binding::fill() +{ + switch (dataType) + { + case nvinfer1::DataType::kBOOL: + { + fillBuffer(buffer->getHostBuffer(), volume, 0, 1); + break; + } + case nvinfer1::DataType::kINT32: + { + fillBuffer(buffer->getHostBuffer(), volume, -128, 127); + break; + } +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kINT64: + { + fillBuffer(buffer->getHostBuffer(), volume, -128, 127); + break; + } +#endif + case nvinfer1::DataType::kINT8: + { + fillBuffer(buffer->getHostBuffer(), volume, -128, 127); + break; + } + case nvinfer1::DataType::kFLOAT: + { + fillBuffer(buffer->getHostBuffer(), volume, -1.0F, 1.0F); + break; + } + case nvinfer1::DataType::kHALF: + { + fillBuffer<__half>(buffer->getHostBuffer(), volume, -1.0F, 1.0F); + break; + } +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kBF16: + { + fillBuffer(buffer->getHostBuffer(), volume, -1.0F, 1.0F); + break; + } +#endif + case nvinfer1::DataType::kUINT8: + { + fillBuffer(buffer->getHostBuffer(), volume, 0, 255); + break; + } + case nvinfer1::DataType::kFP8: ASSERT(false && "FP8 is not supported"); +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kINT4: ASSERT(false && "INT4 is not supported"); +#endif + } +} + +void Binding::dump(std::ostream& os, Dims dims, Dims strides, int32_t vectorDim, int32_t spv, + std::string const separator /*= " "*/) const +{ + void* outputBuffer{}; + if (outputAllocator != nullptr) + { + outputBuffer = outputAllocator->getBuffer()->getHostBuffer(); + // Overwrite dimensions with those reported by the output allocator. + dims = outputAllocator->getFinalDims(); + os << "Final shape is " << dims << " reported by the output allocator." << std::endl; + } + else + { + outputBuffer = buffer->getHostBuffer(); + } + switch (dataType) + { + case nvinfer1::DataType::kBOOL: + { + dumpBuffer(outputBuffer, separator, os, dims, strides, vectorDim, spv); + break; + } + case nvinfer1::DataType::kINT32: + { + dumpBuffer(outputBuffer, separator, os, dims, strides, vectorDim, spv); + break; + } + case nvinfer1::DataType::kINT8: + { + dumpBuffer(outputBuffer, separator, os, dims, strides, vectorDim, spv); + break; + } + case nvinfer1::DataType::kFLOAT: + { + dumpBuffer(outputBuffer, separator, os, dims, strides, vectorDim, spv); + break; + } + case nvinfer1::DataType::kHALF: + { + dumpBuffer<__half>(outputBuffer, separator, os, dims, strides, vectorDim, spv); + break; + } +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kBF16: + { + dumpBuffer(outputBuffer, separator, os, dims, strides, vectorDim, spv); + break; + } +#endif + case nvinfer1::DataType::kUINT8: + { + dumpBuffer(outputBuffer, separator, os, dims, strides, vectorDim, spv); + break; + } +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kINT64: + { + dumpBuffer(outputBuffer, separator, os, dims, strides, vectorDim, spv); + break; + } +#endif + case nvinfer1::DataType::kFP8: ASSERT(false && "FP8 is not supported"); +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kINT4: ASSERT(false && "INT4 is not supported"); +#endif + } +} + +void Bindings::addBinding(TensorInfo const& tensorInfo, std::string const& fileName /*= ""*/) +{ + auto const b = tensorInfo.bindingIndex; + while (mBindings.size() <= static_cast(b)) + { + mBindings.emplace_back(); + mDevicePointers.emplace_back(); + } + mNames[tensorInfo.name] = b; + mBindings[b].isInput = tensorInfo.isInput; + mBindings[b].volume = tensorInfo.vol; + mBindings[b].dataType = tensorInfo.dataType; + if (tensorInfo.isDynamic) + { + ASSERT(!tensorInfo.isInput); // Only output shape can be possibly unknown because of DDS. + if (mBindings[b].outputAllocator == nullptr) + { + if (mUseManaged) + { + mBindings[b].outputAllocator.reset(new OutputAllocator(new UnifiedMirroredBuffer)); + } + else + { + mBindings[b].outputAllocator.reset(new OutputAllocator(new DiscreteMirroredBuffer)); + } + } + } + else + { + if (mBindings[b].buffer == nullptr) + { + if (mUseManaged) + { + mBindings[b].buffer.reset(new UnifiedMirroredBuffer); + } + else + { + mBindings[b].buffer.reset(new DiscreteMirroredBuffer); + } + } + // Some memory allocators return nullptr when allocating zero bytes, but TensorRT requires a non-null ptr + // even for empty tensors, so allocate a dummy byte. + if (tensorInfo.vol == 0) + { + mBindings[b].buffer->allocate(1); + } + else + { + mBindings[b].buffer->allocate( + static_cast(tensorInfo.vol) * static_cast(dataTypeSize(tensorInfo.dataType))); + } + mDevicePointers[b] = mBindings[b].buffer->getDeviceBuffer(); + } + if (tensorInfo.isInput) + { + if (fileName.empty()) + { + fill(b); + } + else + { + fill(b, fileName); + } + } +} + +void** Bindings::getDeviceBuffers() +{ + return mDevicePointers.data(); +} + +void Bindings::transferInputToDevice(TrtCudaStream& stream) +{ + for (auto& b : mNames) + { + if (mBindings[b.second].isInput) + { + mBindings[b.second].buffer->hostToDevice(stream); + } + } +} + +void Bindings::transferOutputToHost(TrtCudaStream& stream) +{ + for (auto& b : mNames) + { + if (!mBindings[b.second].isInput) + { + if (mBindings[b.second].outputAllocator != nullptr) + { + mBindings[b.second].outputAllocator->getBuffer()->deviceToHost(stream); + } + else + { + mBindings[b.second].buffer->deviceToHost(stream); + } + } + } +} + +void Bindings::dumpBindingValues(nvinfer1::IExecutionContext const& context, int32_t binding, std::ostream& os, + std::string const& separator /*= " "*/, int32_t batch /*= 1*/) const +{ + auto const tensorName = context.getEngine().getIOTensorName(binding); + Dims dims = context.getTensorShape(tensorName); + Dims strides = context.getTensorStrides(tensorName); + int32_t vectorDim = context.getEngine().getTensorVectorizedDim(tensorName); + int32_t const spv = context.getEngine().getTensorComponentsPerElement(tensorName); + + mBindings[binding].dump(os, dims, strides, vectorDim, spv, separator); +} + +namespace +{ + +std::string genFilenameSafeString(std::string const& s) +{ + std::string res = s; + static std::string const allowedSpecialChars{"._-,"}; + for (auto& c : res) + { + if (!isalnum(c) && allowedSpecialChars.find(c) == std::string::npos) + { + c = '_'; + } + } + return res; +} + +Dims getBindingDimensions(nvinfer1::IExecutionContext const& context, std::string const& name) +{ + return context.getTensorShape(name.c_str()); +} +} // namespace + +void Bindings::dumpRawBindingToFiles(nvinfer1::IExecutionContext const& context, std::ostream& os) const +{ + os << "Dumping I/O Bindings to RAW Files:" << std::endl; + for (auto const& n : mNames) + { + auto name = n.first; + auto bIndex = n.second; + auto const& binding = mBindings[bIndex]; + void* outputBuffer{}; + if (binding.outputAllocator != nullptr) + { + outputBuffer = binding.outputAllocator->getBuffer()->getHostBuffer(); + } + else + { + outputBuffer = binding.buffer->getHostBuffer(); + } + + Dims dims = getBindingDimensions(context, name); + std::string dimsStr; + std::string dotStr; + + for (int32_t i = 0; i < dims.nbDims; i++) + { + dimsStr += dotStr + std::to_string(dims.d[i]); + dotStr = "."; + } + + std::string const bindingTypeStr = (binding.isInput ? "input" : "output"); + + std::stringstream fileName; + fileName << genFilenameSafeString(name) << "." << bindingTypeStr << "." << dimsStr << "." << binding.dataType + << ".raw"; + + os << "Writing file for " << bindingTypeStr << " binding " << name << " (with datatype " << binding.dataType + << " and dimensions " << dimsStr << ") to " << fileName.str() << std::endl; + + std::ofstream f(fileName.str(), std::ios::out | std::ios::binary); + ASSERT(f && "Cannot open file for write"); + f.write(static_cast(outputBuffer), binding.volume * samplesCommon::elementSize(binding.dataType)); + f.close(); + } +} + +void Bindings::dumpBindingDimensions( + std::string const& name, nvinfer1::IExecutionContext const& context, std::ostream& os) const +{ + auto const dims = context.getTensorShape(name.c_str()); + // Do not add a newline terminator, because the caller may be outputting a JSON string. + os << dims; +} + +std::unordered_map Bindings::getBindings(std::function predicate) const +{ + std::unordered_map bindings; + for (auto const& n : mNames) + { + auto const binding = n.second; + if (predicate(mBindings[binding])) + { + bindings.insert(n); + } + } + return bindings; +} + +bool Bindings::setTensorAddresses(nvinfer1::IExecutionContext& context) const +{ + for (auto const& b : mNames) + { + auto const name = b.first.c_str(); + auto const location = context.getEngine().getTensorLocation(name); + if (location == TensorLocation::kDEVICE) + { + if (mBindings[b.second].outputAllocator != nullptr) + { + if (!context.setOutputAllocator(name, mBindings[b.second].outputAllocator.get())) + { + return false; + } + } + else + { + if (!context.setTensorAddress(name, mDevicePointers[b.second])) + { + return false; + } + } + } + } + return true; +} +#if (NV_TENSORRT_MAJOR > 8) +bool DebugTensorWriter::processDebugTensor(void const* addr, nvinfer1::TensorLocation location, nvinfer1::DataType type, + nvinfer1::Dims const& shape, char const* name, cudaStream_t stream) +{ + CHECK(cudaStreamSynchronize(stream)); + // Store data from callback. + int64_t size = std::accumulate(shape.d, shape.d + shape.nbDims, 1LL, std::multiplies{}) + * samplesCommon::elementSize(type); + std::vector hostDataOut(size, 0); + CHECK(cudaMemcpy(hostDataOut.data(), addr, size, cudaMemcpyDeviceToHost)); + + auto it = mDebugTensorFileNames.find(name); + ASSERT(it != mDebugTensorFileNames.end()); + std::string fileName = it->second; + + std::ofstream f(fileName, std::ios::out | std::ios::binary); + ASSERT(f && "Cannot open file for write"); + sample::gLogInfo << "Writing to file " << fileName << " for debug tensor " << name << std::endl; + f.write(hostDataOut.data(), size); + f.close(); + + CHECK(cudaStreamSynchronize(stream)); + return true; +} +#endif +} // namespace sample diff --git a/src/Detector/tensorrt_onnx/common/sampleInference.h b/src/Detector/tensorrt_onnx/common/sampleInference.h new file mode 100644 index 000000000..b1d24df67 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleInference.h @@ -0,0 +1,263 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef TRT_SAMPLE_INFERENCE_H +#define TRT_SAMPLE_INFERENCE_H + +#include "sampleDevice.h" +#include "sampleEngines.h" +#include "sampleReporting.h" +#include "sampleUtils.h" + +#include +#include +#include +#include +#include +#include + +namespace sample +{ +#if (NV_TENSORRT_MAJOR > 8) +// IDebugListener class for writing debug tensors to output file. +class DebugTensorWriter : public nvinfer1::IDebugListener +{ +public: + DebugTensorWriter(std::unordered_map fileNames) + : mDebugTensorFileNames(fileNames) + { + } + + bool processDebugTensor(void const* addr, nvinfer1::TensorLocation location, nvinfer1::DataType type, + nvinfer1::Dims const& shape, char const* name, cudaStream_t stream) override; + +private: + std::unordered_map mDebugTensorFileNames; +}; +#endif + +struct InferenceEnvironment +{ + InferenceEnvironment() = delete; + InferenceEnvironment(InferenceEnvironment const& other) = delete; + InferenceEnvironment(InferenceEnvironment&& other) = delete; + InferenceEnvironment(BuildEnvironment& bEnv) : engine(std::move(bEnv.engine)), safe(bEnv.engine.isSafe()) + { + } + + LazilyDeserializedEngine engine; + std::unique_ptr profiler; + std::vector> contexts; + std::vector + deviceMemory; //< Device memory used for inference when the allocation strategy is not static. + std::vector> bindings; +#if (NV_TENSORRT_MAJOR > 8) + std::unique_ptr listener; +#endif + bool error{false}; + + bool safe{false}; + + inline nvinfer1::IExecutionContext* getContext(int32_t streamIdx); + + //! Storage for input shape tensors. + //! + //! It's important that the addresses of the data do not change between the calls to + //! setTensorAddress/setInputShape (which tells TensorRT where the input shape tensor is) + //! and enqueueV3 (when TensorRT might use the input shape tensor). + //! + //! The input shape tensors could alternatively be handled via member bindings, + //! but it simplifies control-flow to store the data here since it's shared across + //! the bindings. + std::list> inputShapeTensorValues; +}; + +inline nvinfer1::IExecutionContext* InferenceEnvironment::getContext(int32_t streamIdx) +{ + return contexts[streamIdx].get(); +} + +//! +//! \brief Set up contexts and bindings for inference +//! +bool setUpInference(InferenceEnvironment& iEnv, InferenceOptions const& inference, SystemOptions const& system); + +//! +//! \brief Deserialize the engine and time how long it takes. +//! +bool timeDeserialize(InferenceEnvironment& iEnv, SystemOptions const& sys); + +//! +//! \brief Run inference and collect timing, return false if any error hit during inference +//! +bool runInference( + InferenceOptions const& inference, InferenceEnvironment& iEnv, int32_t device, std::vector& trace); + +//! +//! \brief Get layer information of the engine. +//! +std::string getLayerInformation( + nvinfer1::ICudaEngine* engine, nvinfer1::IExecutionContext* context, nvinfer1::LayerInformationFormat format); + +struct Binding +{ + bool isInput{false}; + std::unique_ptr buffer; + std::unique_ptr outputAllocator; + int64_t volume{0}; + nvinfer1::DataType dataType{nvinfer1::DataType::kFLOAT}; + + void fill(std::string const& fileName); + + void fill(); + + void dump(std::ostream& os, nvinfer1::Dims dims, nvinfer1::Dims strides, int32_t vectorDim, int32_t spv, + std::string const separator = " ") const; +}; + +struct TensorInfo +{ + int32_t bindingIndex{-1}; + char const* name{nullptr}; + nvinfer1::Dims dims{}; + bool isDynamic{}; + int32_t comps{-1}; + nvinfer1::Dims strides{}; + int32_t vectorDimIndex{-1}; + bool isInput{}; + nvinfer1::DataType dataType{}; + int64_t vol{-1}; + + void updateVolume(int32_t batch) + { + vol = volume(dims, strides, vectorDimIndex, comps, batch); + } +}; + +class Bindings +{ +public: + Bindings() = delete; + explicit Bindings(bool useManaged) + : mUseManaged(useManaged) + { + } + + void addBinding(TensorInfo const& tensorInfo, std::string const& fileName = ""); + + void** getDeviceBuffers(); + + void transferInputToDevice(TrtCudaStream& stream); + + void transferOutputToHost(TrtCudaStream& stream); + + void fill(int binding, std::string const& fileName) + { + mBindings[binding].fill(fileName); + } + + void fill(int binding) + { + mBindings[binding].fill(); + } + + void dumpBindingDimensions( + std::string const& name, nvinfer1::IExecutionContext const& context, std::ostream& os) const; + + void dumpBindingValues(nvinfer1::IExecutionContext const& context, int32_t binding, std::ostream& os, + std::string const& separator = " ", int32_t batch = 1) const; + + void dumpRawBindingToFiles(nvinfer1::IExecutionContext const& context, std::ostream& os) const; + + void dumpInputs(nvinfer1::IExecutionContext const& context, std::ostream& os) const + { + auto isInput = [](Binding const& b) { return b.isInput; }; + dumpBindings(context, isInput, os); + } + + void dumpOutputs(nvinfer1::IExecutionContext const& context, std::ostream& os) const; + + void dumpBindings(nvinfer1::IExecutionContext const& context, std::ostream& os) const + { + auto all = [](Binding const& b) { return true; }; + dumpBindings(context, all, os); + } + + void dumpBindings(nvinfer1::IExecutionContext const& context, std::function predicate, + std::ostream& os) const + { + for (auto const& n : mNames) + { + auto const name = n.first; + auto const binding = n.second; + if (predicate(mBindings[binding])) + { + os << n.first << ": ("; + dumpBindingDimensions(name, context, os); + os << ")" << std::endl; + + dumpBindingValues(context, binding, os); + os << std::endl; + } + } + } + + std::unordered_map getInputBindings() const + { + auto isInput = [](Binding const& b) { return b.isInput; }; + return getBindings(isInput); + } + + std::unordered_map getOutputBindings() const + { + auto isOutput = [](Binding const& b) { return !b.isInput; }; + return getBindings(isOutput); + } + + std::unordered_map getBindings() const + { + auto all = [](Binding const& b) { return true; }; + return getBindings(all); + } + + std::unordered_map getBindings(std::function predicate) const; + + bool setTensorAddresses(nvinfer1::IExecutionContext& context) const; + +private: + std::unordered_map mNames; + std::vector mBindings; + std::vector mDevicePointers; + bool mUseManaged{false}; +}; + +struct TaskInferenceEnvironment +{ + TaskInferenceEnvironment(std::string engineFile, InferenceOptions inference, int32_t deviceId = 0, + int32_t DLACore = -1, int32_t bs = batchNotProvided); + InferenceOptions iOptions{}; + int32_t device{defaultDevice}; + int32_t batch{batchNotProvided}; + std::unique_ptr iEnv; + std::vector trace; +}; + +bool runMultiTasksInference(std::vector>& tEnvList); + +} // namespace sample + +#endif // TRT_SAMPLE_INFERENCE_H diff --git a/src/Detector/tensorrt_onnx/common/sampleOptions.cpp b/src/Detector/tensorrt_onnx/common/sampleOptions.cpp new file mode 100644 index 000000000..2baa4e2a1 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleOptions.cpp @@ -0,0 +1,2827 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "NvInfer.h" + +#include "logger.h" +#include "sampleOptions.h" +#include "sampleUtils.h" +using namespace nvinfer1; +namespace sample +{ + +namespace +{ + +static const std::map> kUNIT_MULTIPLIERS{ + {'B', {1, "Bytes"}}, + {'K', {1 << 10, "Kibibytes"}}, + {'M', {1 << 20, "Mebibytes"}}, + {'G', {1 << 30, "Gibibytes"}}, +}; + +std::string addDefaultUnitSuffixIfNotSpecified(std::string const& option, char defaultUnit) +{ + char lastChar = option.at(option.size() - 1); + return std::isdigit(lastChar) ? option + defaultUnit : option; +} + +// Returns "B (Bytes), K (Kilobytes), ..." +std::string getAvailableUnitSuffixes() +{ + std::ostringstream ss; + for (auto it = kUNIT_MULTIPLIERS.begin(); it != kUNIT_MULTIPLIERS.end(); ++it) + { + if (it != kUNIT_MULTIPLIERS.begin()) + { + ss << ", "; + } + ss << it->first << " (" << it->second.second << ")"; + } + return ss.str(); +} + +// Numeric trtexec arguments can have unit specifiers in similar to polygraphy. +// E.g. --weightStreamingBudget=20M would be 20 Mebibytes (base 2). +int64_t getUnitMultiplier(std::string const& option) +{ + char lastChar = option.at(option.size() - 1); + if (!std::isdigit(lastChar)) + { + char unit = std::toupper(lastChar); + auto found = kUNIT_MULTIPLIERS.find(unit); + if (found == kUNIT_MULTIPLIERS.end()) + { + std::ostringstream ss; + ss << "Error parsing \"" << option << "\": invalid unit specifier '" << unit + << "'. Valid base-2 unit suffixes include: "; + ss << getAvailableUnitSuffixes() << "."; + throw std::invalid_argument(ss.str()); + } + return found->second.first; + } + + // Return bytes by default + return kUNIT_MULTIPLIERS.at('B').first; +} + +template +T stringToValue(const std::string& option) +{ + return T{option}; +} + +template <> +int32_t stringToValue(const std::string& option) +{ + return std::stoi(option); +} + +template <> +size_t stringToValue(const std::string& option) +{ + return std::stoi(option) * getUnitMultiplier(option); +} + +template <> +float stringToValue(const std::string& option) +{ + return std::stof(option); +} + +template <> +double stringToValue(const std::string& option) +{ + return std::stod(option) * getUnitMultiplier(option); +} + +template <> +bool stringToValue(const std::string& option) +{ + return true; +} + +template <> +std::vector stringToValue>(const std::string& option) +{ + std::vector shape; + if (option == "scalar") + { + return shape; + } + std::vector dimsStrings = splitToStringVec(option, 'x'); + for (const auto& d : dimsStrings) + { + shape.push_back(stringToValue(d)); + } + return shape; +} + +template <> +nvinfer1::DataType stringToValue(const std::string& option) +{ + const std::unordered_map strToDT{{"fp32", nvinfer1::DataType::kFLOAT}, + {"fp16", nvinfer1::DataType::kHALF}, +#if (NV_TENSORRT_MAJOR > 8) + {"bf16", nvinfer1::DataType::kBF16}, +#endif + {"int8", nvinfer1::DataType::kINT8}, + {"fp8", nvinfer1::DataType::kFP8}, + {"int32", nvinfer1::DataType::kINT32}, +#if (NV_TENSORRT_MAJOR > 8) + {"int64", nvinfer1::DataType::kINT64}, +#endif + {"bool", nvinfer1::DataType::kBOOL}, + {"uint8", nvinfer1::DataType::kUINT8} +#if (NV_TENSORRT_MAJOR > 8) + , + {"int4", nvinfer1::DataType::kINT4} +#endif + }; + const auto& dt = strToDT.find(option); + if (dt == strToDT.end()) + { + throw std::invalid_argument("Invalid DataType " + option); + } + return dt->second; +} + +template <> +nvinfer1::DeviceType stringToValue(std::string const& option) +{ + std::unordered_map const strToDevice = { + {"GPU", nvinfer1::DeviceType::kGPU}, + {"DLA", nvinfer1::DeviceType::kDLA}, + }; + auto const& device = strToDevice.find(option); + if (device == strToDevice.end()) + { + throw std::invalid_argument("Invalid Device Type " + option); + } + return device->second; +} + +template <> +nvinfer1::TensorFormats stringToValue(const std::string& option) +{ + std::vector optionStrings = splitToStringVec(option, '+'); + const std::unordered_map strToFmt{{"chw", nvinfer1::TensorFormat::kLINEAR}, + {"chw2", nvinfer1::TensorFormat::kCHW2}, {"chw4", nvinfer1::TensorFormat::kCHW4}, + {"hwc8", nvinfer1::TensorFormat::kHWC8}, {"chw16", nvinfer1::TensorFormat::kCHW16}, + {"chw32", nvinfer1::TensorFormat::kCHW32}, {"dhwc8", nvinfer1::TensorFormat::kDHWC8}, + {"cdhw32", nvinfer1::TensorFormat::kCDHW32}, {"hwc", nvinfer1::TensorFormat::kHWC}, + {"dhwc", nvinfer1::TensorFormat::kDHWC}, {"dla_linear", nvinfer1::TensorFormat::kDLA_LINEAR}, + {"dla_hwc4", nvinfer1::TensorFormat::kDLA_HWC4}}; + nvinfer1::TensorFormats formats{}; + for (auto f : optionStrings) + { + const auto& tf = strToFmt.find(f); + if (tf == strToFmt.end()) + { + throw std::invalid_argument(std::string("Invalid TensorFormat ") + f); + } + formats |= 1U << static_cast(tf->second); + } + + return formats; +} + +template <> +IOFormat stringToValue(const std::string& option) +{ + IOFormat ioFormat{}; + const size_t colon = option.find(':'); + + if (colon == std::string::npos) + { + throw std::invalid_argument(std::string("Invalid IOFormat ") + option); + } + + ioFormat.first = stringToValue(option.substr(0, colon)); + ioFormat.second = stringToValue(option.substr(colon + 1)); + + return ioFormat; +} + +template <> +SparsityFlag stringToValue(std::string const& option) +{ + std::unordered_map const table{ + {"disable", SparsityFlag::kDISABLE}, {"enable", SparsityFlag::kENABLE}, {"force", SparsityFlag::kFORCE}}; + auto search = table.find(option); + if (search == table.end()) + { + throw std::invalid_argument(std::string("Unknown sparsity mode: ") + option); + } + if (search->second == SparsityFlag::kFORCE) + { + sample::gLogWarning << "--sparsity=force has been deprecated. " + << "Please use to rewrite the weights to a sparsity pattern " + << "and then run with --sparsity=enable" << std::endl; + } + + return search->second; +} + +template <> +WeightStreamingBudget stringToValue(std::string const& option) +{ + WeightStreamingBudget budget; + if (option.find('%') != std::string::npos) + { + double percent = std::stod(option); + if (!(percent >= 0 && percent <= 100.0)) + { + std::ostringstream err; + err << "The weight streaming percent must be between 0 and 100."; + throw std::invalid_argument(err.str()); + } + budget.percent = percent; + } + else + { + double bytes = stringToValue(option); + if (!(bytes == WeightStreamingBudget::kAUTOMATIC || bytes == WeightStreamingBudget::kDISABLE || bytes >= 0)) + { + std::ostringstream err; + err << "The weight streaming budget must be " << WeightStreamingBudget::kDISABLE << ", " + << WeightStreamingBudget::kAUTOMATIC << ", or at least 0."; + throw std::invalid_argument(err.str()); + } + budget.bytes = static_cast(bytes); + } + return budget; +} + +template +std::pair splitNameAndValue(const std::string& s) +{ + std::string tensorName; + std::string valueString; + + // Support 'inputName':Path format for --loadInputs flag when dealing with Windows paths. + // i.e. 'inputName':c:\inputData + std::vector quoteNameRange{ splitToStringVec(s, '\'') }; + // splitToStringVec returns the entire string when delimiter is not found, so it's size is always at least 1 + if (quoteNameRange.size() != 1) + { + if (quoteNameRange.size() != 3) + { + std::string errorMsg = std::string("Found invalid number of \'s when parsing ") + s + + std::string(". Expected: 2, received: ") + std::to_string(quoteNameRange.size() -1) + + ". Please ensure that a singular comma is used within each comma-separated key-value pair for options like --inputIOFormats, --optShapes, --optShapesCalib, --layerPrecisions, etc."; + throw std::invalid_argument(errorMsg); + } + // Everything before the second "'" is the name. + tensorName = quoteNameRange[0] + quoteNameRange[1]; + // Path is the last string - ignoring leading ":" so slice it with [1:] + valueString = quoteNameRange[2].substr(1); + return std::pair(tensorName, stringToValue(valueString)); + } + + // Split on the last : + std::vector nameRange{splitToStringVec(s, ':')}; + // Everything before the last : is the name + tensorName = nameRange[0]; + for (size_t i = 1; i < nameRange.size() - 1; i++) + { + tensorName += ":" + nameRange[i]; + } + // Value is the string element after the last : + valueString = nameRange[nameRange.size() - 1]; + return std::pair(tensorName, stringToValue(valueString)); +} + +template +void splitInsertKeyValue(const std::vector& kvList, T& map) +{ + for (const auto& kv : kvList) + { + map.insert(splitNameAndValue(kv)); + } +} + +const char* boolToEnabled(bool enable) +{ + return enable ? "Enabled" : "Disabled"; +} + +//! A helper function similar to sep.join(list) in Python. +template +std::string joinValuesToString(std::vector const& list, std::string const& sep) +{ + std::ostringstream os; + for (int32_t i = 0, n = list.size(); i < n; ++i) + { + os << list[i]; + if (i != n - 1) + { + os << sep; + } + } + return os.str(); +} + +template +std::string joinValuesToString(std::array const& list, std::string const& sep) +{ + return joinValuesToString(std::vector(list.begin(), list.end()), sep); +} + +//! Check if input option exists in input arguments. +//! If it does: set its value, and return true +//! If it does not: return false. +template +bool getOption(Arguments& arguments, const std::string& option, T& value) +{ + auto const match = arguments.find(option); + if (match != arguments.end()) + { + value = stringToValue(match->second.first); + return true; + } + + return false; +} + +//! Check if input option exists in input arguments. +//! If it does: set its value, erase the argument and return true. +//! If it does not: return false. +template +bool getAndDelOption(Arguments& arguments, const std::string& option, T_& value) +{ + bool found = getOption(arguments, option, value); + if (found) + { + const auto match = arguments.find(option); + arguments.erase(match); + } + + return found; +} + +//! Check if input option exists in input arguments. +//! If it does: set its value and position, erase the argument and return true. +//! If it does not: return false. +template +bool getAndDelOptionWithPosition(Arguments& arguments, std::string const& option, T_& value, int32_t& pos) +{ + auto const match = arguments.find(option); + if (match != arguments.end()) + { + value = stringToValue(match->second.first); + pos = match->second.second; + arguments.erase(match); + return true; + } + + return false; +} + +//! Check if input option exists in input arguments behind the position spcecified by pos. +//! If it does: set its value, erase the argument and return true. +//! If it does not: return false. +template +bool getAndDelOptionBehind(Arguments& arguments, std::string const& option, int32_t pos, T_& value) +{ + auto const match = arguments.equal_range(option); + if (match.first == match.second) + { + return false; + } + for (auto i = match.first; i != match.second; ++i) + { + if (i->second.second - pos == 1) + { + value = stringToValue(i->second.first); + arguments.erase(i); + return true; + } + } + return false; +} + +//! Check if input option exists in input arguments. +//! If it does: set false in value, erase the argument and return true. +//! If it does not: return false. +bool getAndDelNegOption(Arguments& arguments, const std::string& option, bool& value) +{ + bool dummy; + if (getAndDelOption(arguments, option, dummy)) + { + value = false; + return true; + } + return false; +} + +//! Check if input option exists in input arguments. +//! If it does: add all the matched arg values to values vector, erase the argument and return true. +//! If it does not: return false. +template +bool getAndDelRepeatedOption(Arguments& arguments, const std::string& option, std::vector& values) +{ + const auto match = arguments.equal_range(option); + if (match.first == match.second) + { + return false; + } + + auto addToValues + = [&values](Arguments::value_type& argValue) { values.emplace_back(stringToValue(argValue.second.first)); }; + std::for_each(match.first, match.second, addToValues); + arguments.erase(match.first, match.second); + + return true; +} + +void insertShapesBuild(BuildOptions::ShapeProfile& shapes, nvinfer1::OptProfileSelector selector, + const std::string& name, const std::vector& dims) +{ + shapes[name][static_cast(selector)] = dims; +} + +void insertShapesInference( + InferenceOptions::ShapeProfile& shapes, std::string const& name, std::vector const& dims) +{ + shapes[name] = dims; +} + +std::string removeSingleQuotationMarks(std::string& str) +{ + std::vector strList{splitToStringVec(str, '\'')}; + // Remove all the escaped single quotation marks + std::string retVal; + // Do not really care about unterminated sequences + for (size_t i = 0; i < strList.size(); i++) + { + retVal += strList[i]; + } + return retVal; +} + +void getLayerPrecisions(Arguments& arguments, char const* argument, LayerPrecisions& layerPrecisions) +{ + std::string list; + if (!getAndDelOption(arguments, argument, list)) + { + return; + } + + // The layerPrecisions flag contains comma-separated layerName:precision pairs. + std::vector precisionList{splitToStringVec(list, ',')}; + for (auto const& s : precisionList) + { + auto namePrecisionPair = splitNameAndValue(s); + auto const layerName = removeSingleQuotationMarks(namePrecisionPair.first); + layerPrecisions[layerName] = namePrecisionPair.second; + } +} + +void getLayerOutputTypes(Arguments& arguments, char const* argument, LayerOutputTypes& layerOutputTypes) +{ + std::string list; + if (!getAndDelOption(arguments, argument, list)) + { + return; + } + + // The layerOutputTypes flag contains comma-separated layerName:types pairs. + std::vector precisionList{splitToStringVec(list, ',')}; + for (auto const& s : precisionList) + { + auto namePrecisionPair = splitNameAndValue(s); + auto const layerName = removeSingleQuotationMarks(namePrecisionPair.first); + auto const typeStrings = splitToStringVec(namePrecisionPair.second, '+'); + std::vector typeVec(typeStrings.size(), nvinfer1::DataType::kFLOAT); + std::transform(typeStrings.begin(), typeStrings.end(), typeVec.begin(), stringToValue); + layerOutputTypes[layerName] = typeVec; + } +} + +void getLayerDeviceTypes(Arguments& arguments, char const* argument, LayerDeviceTypes& layerDeviceTypes) +{ + std::string list; + if (!getAndDelOption(arguments, argument, list)) + { + return; + } + + // The layerDeviceTypes flag contains comma-separated layerName:deviceType pairs. + std::vector deviceList{splitToStringVec(list, ',')}; + for (auto const& s : deviceList) + { + auto nameDevicePair = splitNameAndValue(s); + auto const layerName = removeSingleQuotationMarks(nameDevicePair.first); + layerDeviceTypes[layerName] = stringToValue(nameDevicePair.second); + } +} + +void getStringsSet(Arguments& arguments, char const* argument, StringSet& stringSet) +{ + std::string list; + if (!getAndDelOption(arguments, argument, list)) + { + return; + } + + // The layerPrecisions flag contains comma-separated layerName:precision pairs. + std::vector strings{splitToStringVec(list, ',')}; + for (auto const& s : strings) + { + stringSet.insert(s); + } +} + +bool getShapesBuild(Arguments& arguments, BuildOptions::ShapeProfile& shapes, char const* argument, + nvinfer1::OptProfileSelector selector) +{ + std::string list; + bool retVal = getAndDelOption(arguments, argument, list); + std::vector shapeList{splitToStringVec(list, ',')}; + for (const auto& s : shapeList) + { + auto nameDimsPair = splitNameAndValue>(s); + auto tensorName = removeSingleQuotationMarks(nameDimsPair.first); + auto dims = nameDimsPair.second; + insertShapesBuild(shapes, selector, tensorName, dims); + } + return retVal; +} + +bool getShapesInference(Arguments& arguments, InferenceOptions::ShapeProfile& shapes, const char* argument) +{ + std::string list; + bool retVal = getAndDelOption(arguments, argument, list); + std::vector shapeList{splitToStringVec(list, ',')}; + for (const auto& s : shapeList) + { + auto nameDimsPair = splitNameAndValue>(s); + auto tensorName = removeSingleQuotationMarks(nameDimsPair.first); + auto dims = nameDimsPair.second; + insertShapesInference(shapes, tensorName, dims); + } + return retVal; +} + +void fillShapes(BuildOptions::ShapeProfile& shapes, std::string const& name, ShapeRange const& sourceShapeRange, + nvinfer1::OptProfileSelector minDimsSource, nvinfer1::OptProfileSelector optDimsSource, + nvinfer1::OptProfileSelector maxDimsSource) +{ + insertShapesBuild( + shapes, nvinfer1::OptProfileSelector::kMIN, name, sourceShapeRange[static_cast(minDimsSource)]); + insertShapesBuild( + shapes, nvinfer1::OptProfileSelector::kOPT, name, sourceShapeRange[static_cast(optDimsSource)]); + insertShapesBuild( + shapes, nvinfer1::OptProfileSelector::kMAX, name, sourceShapeRange[static_cast(maxDimsSource)]); +} + +void processShapes(BuildOptions::ShapeProfile& shapes, bool minShapes, bool optShapes, bool maxShapes, bool calib) +{ + // Only accept optShapes only or all three of minShapes, optShapes, maxShapes when calib is set + if (((minShapes || maxShapes) && !optShapes) // minShapes only, maxShapes only, both minShapes and maxShapes + || (minShapes && !maxShapes && optShapes) // both minShapes and optShapes + || (!minShapes && maxShapes && optShapes)) // both maxShapes and optShapes + { + if (calib) + { + throw std::invalid_argument( + "Must specify only --optShapesCalib or all of --minShapesCalib, --optShapesCalib, --maxShapesCalib"); + } + } + + if (!minShapes && !optShapes && !maxShapes) + { + return; + } + + BuildOptions::ShapeProfile newShapes; + for (auto& s : shapes) + { + nvinfer1::OptProfileSelector minDimsSource, optDimsSource, maxDimsSource; + minDimsSource = nvinfer1::OptProfileSelector::kMIN; + optDimsSource = nvinfer1::OptProfileSelector::kOPT; + maxDimsSource = nvinfer1::OptProfileSelector::kMAX; + + // Populate missing minShapes + if (!minShapes) + { + if (optShapes) + { + minDimsSource = optDimsSource; + sample::gLogWarning << "optShapes is being broadcasted to minShapes for tensor " << s.first + << std::endl; + } + else + { + minDimsSource = maxDimsSource; + sample::gLogWarning << "maxShapes is being broadcasted to minShapes for tensor " << s.first + << std::endl; + } + } + + // Populate missing optShapes + if (!optShapes) + { + if (maxShapes) + { + optDimsSource = maxDimsSource; + sample::gLogWarning << "maxShapes is being broadcasted to optShapes for tensor " << s.first + << std::endl; + } + else + { + optDimsSource = minDimsSource; + sample::gLogWarning << "minShapes is being broadcasted to optShapes for tensor " << s.first + << std::endl; + } + } + + // Populate missing maxShapes + if (!maxShapes) + { + if (optShapes) + { + maxDimsSource = optDimsSource; + sample::gLogWarning << "optShapes is being broadcasted to maxShapes for tensor " << s.first + << std::endl; + } + else + { + maxDimsSource = minDimsSource; + sample::gLogWarning << "minShapes is being broadcasted to maxShapes for tensor " << s.first + << std::endl; + } + } + + fillShapes(newShapes, s.first, s.second, minDimsSource, optDimsSource, maxDimsSource); + } + shapes = newShapes; +} + +bool getOptimizationProfiles( + Arguments& arguments, std::vector& optProfiles, char const* argument) +{ + bool retValue{false}; + int32_t pos{}; + size_t profileIndex{}; + + auto getShapes + = [](BuildOptions::ShapeProfile& shapes, std::string const& list, nvinfer1::OptProfileSelector selector) { + std::vector shapeList{splitToStringVec(list, ',')}; + for (auto const& s : shapeList) + { + auto nameDimsPair = splitNameAndValue>(s); + auto tensorName = removeSingleQuotationMarks(nameDimsPair.first); + auto dims = nameDimsPair.second; + insertShapesBuild(shapes, selector, tensorName, dims); + } + }; + + while (getAndDelOptionWithPosition(arguments, argument, profileIndex, pos)) + { + BuildOptions::ShapeProfile optProfile{}; + bool minShapes{false}, maxShapes{false}, optShapes{false}; + for (int32_t i = 0; i < nvinfer1::EnumMax(); i++, pos++) + { + std::string value; + + if (!minShapes && getAndDelOptionBehind(arguments, "--minShapes", pos, value)) + { + minShapes = true; + getShapes(optProfile, value, nvinfer1::OptProfileSelector::kMIN); + } + else if (!maxShapes && getAndDelOptionBehind(arguments, "--maxShapes", pos, value)) + { + maxShapes = true; + getShapes(optProfile, value, nvinfer1::OptProfileSelector::kMAX); + } + else if (!optShapes && getAndDelOptionBehind(arguments, "--optShapes", pos, value)) + { + optShapes = true; + getShapes(optProfile, value, nvinfer1::OptProfileSelector::kOPT); + } + else + { + break; + } + } + processShapes(optProfile, minShapes, optShapes, maxShapes, false); + if (profileIndex >= optProfiles.size()) + { + optProfiles.resize(profileIndex + 1); + } + if (!optProfiles[profileIndex].empty()) + { + throw std::invalid_argument("Optimization profile index cannot be the same."); + } + optProfiles[profileIndex] = optProfile; + retValue = true; + } + + profileIndex = 0; + for (auto const& optProfile : optProfiles) + { + if (optProfile.empty()) + { + throw std::invalid_argument(std::string("Found invalid or missing shape spec at profile index ") + + std::to_string(profileIndex) + std::string(". ")); + } + ++profileIndex; + } + return retValue; +} + +template +void printShapes(std::ostream& os, char const* phase, T const& shapes, int32_t profileIndex) +{ + if (shapes.empty()) + { + os << "Input " << phase << " shapes: model" << std::endl; + } + else + { + std::string profileString = (profileIndex != -1 && strcmp(phase, "build") == 0) + ? "(profile " + std::to_string(profileIndex) + ")" + : ""; + for (auto const& s : shapes) + { + os << "Input " << phase << " shape " << profileString << ": " << s.first << "=" << s.second << std::endl; + } + } +} + +std::ostream& printTacticSources( + std::ostream& os, nvinfer1::TacticSources enabledSources, nvinfer1::TacticSources disabledSources) +{ + if (!enabledSources && !disabledSources) + { + os << "Using default tactic sources"; + } + else + { + auto const addSource = [&](uint32_t source, std::string const& name) { + if (enabledSources & source) + { + os << name << " [ON], "; + } + else if (disabledSources & source) + { + os << name << " [OFF], "; + } + }; + + addSource(1U << static_cast(nvinfer1::TacticSource::kCUBLAS), "cublas"); + addSource(1U << static_cast(nvinfer1::TacticSource::kCUBLAS_LT), "cublasLt"); + addSource(1U << static_cast(nvinfer1::TacticSource::kCUDNN), "cudnn"); + addSource(1U << static_cast(nvinfer1::TacticSource::kEDGE_MASK_CONVOLUTIONS), "edge mask convolutions"); + addSource(1U << static_cast(nvinfer1::TacticSource::kJIT_CONVOLUTIONS), "JIT convolutions"); + } + return os; +} + +std::ostream& printPrecision(std::ostream& os, BuildOptions const& options) +{ + if (options.stronglyTyped) + { + os << "Strongly Typed"; + return os; + } + os << "FP32"; + if (options.fp16) + { + os << "+FP16"; + } + if (options.bf16) + { + os << "+BF16"; + } + if (options.int8) + { + os << "+INT8"; + } + if (options.fp8) + { + os << "+FP8"; + } + if (options.int4) + { + os << "+INT4"; + } + if (options.precisionConstraints == PrecisionConstraints::kOBEY) + { + os << " (obey precision constraints)"; + } + if (options.precisionConstraints == PrecisionConstraints::kPREFER) + { + os << " (prefer precision constraints)"; + } + return os; +} + +std::ostream& printTempfileControls(std::ostream& os, TempfileControlFlags const tempfileControls) +{ + auto getFlag = [&](TempfileControlFlag f) -> char const* { + bool allowed = !!(tempfileControls & (1U << static_cast(f))); + return allowed ? "allow" : "deny"; + }; + auto const inMemory = getFlag(TempfileControlFlag::kALLOW_IN_MEMORY_FILES); + auto const temporary = getFlag(TempfileControlFlag::kALLOW_TEMPORARY_FILES); + + os << "{ in_memory: " << inMemory << ", temporary: " << temporary << " }"; + + return os; +} + +std::ostream& printTimingCache(std::ostream& os, TimingCacheMode const& timingCacheMode) +{ + switch (timingCacheMode) + { + case TimingCacheMode::kGLOBAL: os << "global"; break; + case TimingCacheMode::kLOCAL: os << "local"; break; + case TimingCacheMode::kDISABLE: os << "disable"; break; + } + return os; +} + +std::ostream& printSparsity(std::ostream& os, BuildOptions const& options) +{ + switch (options.sparsity) + { + case SparsityFlag::kDISABLE: os << "Disabled"; break; + case SparsityFlag::kENABLE: os << "Enabled"; break; + case SparsityFlag::kFORCE: os << "Forced"; break; + } + + return os; +} + +std::ostream& printMemoryPools(std::ostream& os, BuildOptions const& options) +{ + auto const printValueOrDefault = [&os](double const val, char const* unit = "MiB") { + if (val >= 0) + { + os << val << " " << unit; + } + else + { + os << "default"; + } + }; + os << "workspace: "; + printValueOrDefault(options.workspace); + os << ", "; + os << "dlaSRAM: "; + printValueOrDefault(options.dlaSRAM); + os << ", "; + os << "dlaLocalDRAM: "; + printValueOrDefault(options.dlaLocalDRAM); + os << ", "; + os << "dlaGlobalDRAM: "; + printValueOrDefault(options.dlaGlobalDRAM); + os << ", "; + os << "tacticSharedMem: "; + printValueOrDefault(options.tacticSharedMem, "KiB"); + return os; +} + +std::string previewFeatureToString(PreviewFeature feature) +{ + // clang-format off + switch (feature) + { + case PreviewFeature::kPROFILE_SHARING_0806: + { + gLogWarning << "profileSharing0806 is on by default in TensorRT 10.0. This flag is deprecated and has no effect." << std::endl; + break; + } +#if (NV_TENSORRT_MAJOR > 8) + case PreviewFeature::kALIASED_PLUGIN_IO_10_03: return "kALIASED_PLUGIN_IO_10_03"; +#endif + } + return "Invalid Preview Feature"; + // clang-format on +} + +std::ostream& printPreviewFlags(std::ostream& os, BuildOptions const& options) +{ + if (options.previewFeatures.empty()) + { + os << "Use default preview flags."; + return os; + } + + auto const addFlag = [&](PreviewFeature feat) { + int32_t featVal = static_cast(feat); + if (options.previewFeatures.find(featVal) != options.previewFeatures.end()) + { + os << previewFeatureToString(feat) << (options.previewFeatures.at(featVal) ? " [ON], " : " [OFF], "); + } + }; +#if (NV_TENSORRT_MAJOR > 8) + addFlag(PreviewFeature::kALIASED_PLUGIN_IO_10_03); +#endif + return os; +} + +} // namespace + +Arguments argsToArgumentsMap(int32_t argc, char* argv[]) +{ + Arguments arguments; + for (int32_t i = 1; i < argc; ++i) + { + auto valuePtr = strchr(argv[i], '='); + if (valuePtr) + { + std::string value{valuePtr + 1}; + arguments.emplace(std::string(argv[i], valuePtr - argv[i]), std::make_pair(value, i)); + } + else + { + arguments.emplace(argv[i], std::make_pair(std::string(""), i)); + } + } + return arguments; +} + +namespace +{ +std::string resolveHomeDirectoryOnLinux(std::string const& model) +{ + std::string filePath{model}; +#ifndef _WIN32 + if (filePath[0] == '~') + { + char const* home = std::getenv("HOME"); + if (home) + { + filePath.replace(0, 1, home); + } + } +#endif + return filePath; +} +} // namespace + +void BaseModelOptions::parse(Arguments& arguments) +{ + if (getAndDelOption(arguments, "--onnx", model)) + { + format = ModelFormat::kONNX; + model = resolveHomeDirectoryOnLinux(model); + } +} + +void ModelOptions::parse(Arguments& arguments) +{ + baseModel.parse(arguments); + + switch (baseModel.format) + { + case ModelFormat::kONNX: + case ModelFormat::kANY: + { + break; + } + } + + if (baseModel.format == ModelFormat::kONNX) + { + if (!outputs.empty()) + { + throw std::invalid_argument("The --output flag should not be used with ONNX models."); + } + } +} + +void getTempfileControls(Arguments& arguments, char const* argument, TempfileControlFlags& tempfileControls) +{ + std::string list; + if (!getAndDelOption(arguments, argument, list)) + { + return; + } + + std::vector controlList{splitToStringVec(list, ',')}; + for (auto const& s : controlList) + { + auto controlAllowPair = splitNameAndValue(s); + bool allowed{false}; + int32_t offset{-1}; + + if (controlAllowPair.second.compare("allow") == 0) + { + allowed = true; + } + else if (controlAllowPair.second.compare("deny") != 0) + { + throw std::invalid_argument("--tempfileControls value should be `deny` or `allow`"); + } + + if (controlAllowPair.first.compare("in_memory") == 0) + { + offset = static_cast(TempfileControlFlag::kALLOW_IN_MEMORY_FILES); + } + else if (controlAllowPair.first.compare("temporary") == 0) + { + offset = static_cast(TempfileControlFlag::kALLOW_TEMPORARY_FILES); + } + else + { + throw std::invalid_argument(std::string{"Unknown --tempfileControls key "} + controlAllowPair.first); + } + + if (allowed) + { + tempfileControls |= (1U << offset); + } + else + { + tempfileControls &= ~(1U << offset); + } + } +} + +void BuildOptions::parse(Arguments& arguments) +{ + auto getFormats = [&arguments](std::vector& formatsVector, const char* argument) { + std::string list; + getAndDelOption(arguments, argument, list); + std::vector formats{splitToStringVec(list, ',')}; + for (const auto& f : formats) + { + formatsVector.push_back(stringToValue(f)); + } + }; + + getFormats(inputFormats, "--inputIOFormats"); + getFormats(outputFormats, "--outputIOFormats"); + + bool getCalibProfile = getAndDelOption(arguments, "--calibProfile", calibProfile); + if (!getOptimizationProfiles(arguments, optProfiles, "--profile")) + { + ShapeProfile shapes; + bool minShapes{false}, optShapes{false}, maxShapes{false}; + try + { + minShapes = getShapesBuild(arguments, shapes, "--minShapes", nvinfer1::OptProfileSelector::kMIN); + optShapes = getShapesBuild(arguments, shapes, "--optShapes", nvinfer1::OptProfileSelector::kOPT); + maxShapes = getShapesBuild(arguments, shapes, "--maxShapes", nvinfer1::OptProfileSelector::kMAX); + } + catch (std::invalid_argument const& arg) + { + throw std::invalid_argument(arg.what() + + std::string(" conversion failure: failed to parse minShapes/optShapes/maxShapes. Please double check " + "your input string.")); + } + + processShapes(shapes, minShapes, optShapes, maxShapes, false); + optProfiles.emplace_back(shapes); + } + + if (calibProfile >= optProfiles.size()) + { + throw std::invalid_argument( + std::string("--calibProfile shouldn't greater than the size of optimization profile.")); + } + + BuildOptions::ShapeProfile dummyShapes; + + bool remainingMinShapes = getShapesBuild(arguments, dummyShapes, "--minShapes", nvinfer1::OptProfileSelector::kMIN); + bool remainingOptShapes = getShapesBuild(arguments, dummyShapes, "--optShapes", nvinfer1::OptProfileSelector::kOPT); + bool remainingMaxShapes = getShapesBuild(arguments, dummyShapes, "--maxShapes", nvinfer1::OptProfileSelector::kMAX); + if (remainingMinShapes || remainingOptShapes || remainingMaxShapes) + { + throw std::invalid_argument("Multiple --minShapes/--optShapes/--maxShapes without --profile are not allowed. "); + } + + bool minShapesCalib{false}, optShapesCalib{false}, maxShapesCalib{false}; + try + { + minShapesCalib = getShapesBuild(arguments, shapesCalib, "--minShapesCalib", nvinfer1::OptProfileSelector::kMIN); + optShapesCalib = getShapesBuild(arguments, shapesCalib, "--optShapesCalib", nvinfer1::OptProfileSelector::kOPT); + maxShapesCalib = getShapesBuild(arguments, shapesCalib, "--maxShapesCalib", nvinfer1::OptProfileSelector::kMAX); + } + catch (std::invalid_argument const& arg) + { + throw std::invalid_argument(arg.what() + + std::string(" conversion failure: failed to parse minShapesCalib/optShapesCalib/maxShapesCalib. Please " + "double check your input string.")); + } + + processShapes(shapesCalib, minShapesCalib, optShapesCalib, maxShapesCalib, true); + + std::string memPoolSizes; + getAndDelOption(arguments, "--memPoolSize", memPoolSizes); + std::vector memPoolSpecs{splitToStringVec(memPoolSizes, ',')}; + for (auto const& memPoolSpec : memPoolSpecs) + { + std::string memPoolName; + double memPoolSize; + try + { + std::string strPoolSize; + std::tie(memPoolName, strPoolSize) = splitNameAndValue(memPoolSpec); + memPoolSize = stringToValue(addDefaultUnitSuffixIfNotSpecified(strPoolSize, 'M')); + } + catch (std::invalid_argument const& arg) + { + throw std::invalid_argument(arg.what() + + std::string( + " conversion failure: failed to parse --memPoolSize. Please double check your input string.")); + } + + if (memPoolSize < 0) + { + throw std::invalid_argument(std::string("Negative memory pool size: ") + std::to_string(memPoolSize)); + } + if (memPoolName == "workspace") + { + // use unit in MB. + workspace = memPoolSize / 1.0_MiB; + } + else if (memPoolName == "dlaSRAM") + { + // use unit in MB. + dlaSRAM = memPoolSize / 1.0_MiB; + } + else if (memPoolName == "dlaLocalDRAM") + { + // use unit in MB. + dlaLocalDRAM = memPoolSize / 1.0_MiB; + } + else if (memPoolName == "dlaGlobalDRAM") + { + // use unit in MB. + dlaGlobalDRAM = memPoolSize / 1.0_MiB; + } + else if (memPoolName == "tacticSharedMem") + { + // use unit in KB. + tacticSharedMem = memPoolSize / 1.0_KiB; + } + else if (!memPoolName.empty()) + { + throw std::invalid_argument(std::string("Unknown memory pool: ") + memPoolName); + } + } + + getAndDelOption(arguments, "--avgTiming", avgTiming); + + bool best{false}; + getAndDelOption(arguments, "--best", best); + if (best) + { + int8 = true; + fp16 = true; + + // BF16 only supported on Ampere+ + if (samplesCommon::getSMVersion() >= 0x0800) + { + bf16 = true; + } + } + + getAndDelOption(arguments, "--refit", refittable); + + getAndDelOption(arguments, "--weightless", stripWeights); + getAndDelOption(arguments, "--stripWeights", stripWeights); + + bool stripAllWeights{}; + getAndDelOption(arguments, "--stripAllWeights", stripAllWeights); + if (stripAllWeights) + { + refittable = true; + stripWeights = true; + } + + // --vc and --versionCompatible are synonyms + getAndDelOption(arguments, "--vc", versionCompatible); + if (!versionCompatible) + { + getAndDelOption(arguments, "--versionCompatible", versionCompatible); + } + +#if !TRT_WINML + // --pi and --pluginInstanceNorm are synonyms + getAndDelOption(arguments, "--pi", pluginInstanceNorm); + if (!pluginInstanceNorm) + { + getAndDelOption(arguments, "--pluginInstanceNorm", pluginInstanceNorm); + } +#endif + + getAndDelOption(arguments, "--excludeLeanRuntime", excludeLeanRuntime); + getAndDelOption(arguments, "--noCompilationCache", disableCompilationCache); + getAndDelNegOption(arguments, "--noTF32", tf32); + getAndDelOption(arguments, "--fp16", fp16); + getAndDelOption(arguments, "--bf16", bf16); + getAndDelOption(arguments, "--int8", int8); + getAndDelOption(arguments, "--fp8", fp8); + getAndDelOption(arguments, "--int4", int4); + getAndDelOption(arguments, "--stronglyTyped", stronglyTyped); + if (stronglyTyped) + { + auto disableAndLog = [](bool& flag, std::string mode, std::string type) { + if (flag) + { + flag = false; + sample::gLogWarning << "Invalid usage, setting " << mode + << " mode is not allowed if graph is strongly typed. Disabling BuilderFlag::" + << type << "." << std::endl; + } + }; + disableAndLog(fp16, "fp16", "kFP16"); + disableAndLog(int8, "int8", "kINT8"); + disableAndLog(bf16, "bf16", "kBF16"); + disableAndLog(fp8, "fp8", "kFP8"); + disableAndLog(int4, "int4", "kINT4"); + } + + if (fp8 && int8) + { + throw std::invalid_argument("Invalid usage, fp8 and int8 aren't allowed to be enabled together."); + } + getAndDelOption(arguments, "--safe", safe); + getAndDelOption(arguments, "--buildDLAStandalone", buildDLAStandalone); + getAndDelOption(arguments, "--allowGPUFallback", allowGPUFallback); + getAndDelOption(arguments, "--restricted", restricted); + getAndDelOption(arguments, "--skipInference", skipInference); + getAndDelOption(arguments, "--directIO", directIO); + + std::string precisionConstraintsString; + getAndDelOption(arguments, "--precisionConstraints", precisionConstraintsString); + if (!precisionConstraintsString.empty()) + { + const std::unordered_map precisionConstraintsMap + = {{"obey", PrecisionConstraints::kOBEY}, {"prefer", PrecisionConstraints::kPREFER}, + {"none", PrecisionConstraints::kNONE}}; + auto it = precisionConstraintsMap.find(precisionConstraintsString); + if (it == precisionConstraintsMap.end()) + { + throw std::invalid_argument(std::string("Unknown precision constraints: ") + precisionConstraintsString); + } + precisionConstraints = it->second; + } + else + { + precisionConstraints = PrecisionConstraints::kNONE; + } + + getLayerPrecisions(arguments, "--layerPrecisions", layerPrecisions); + getLayerOutputTypes(arguments, "--layerOutputTypes", layerOutputTypes); + getLayerDeviceTypes(arguments, "--layerDeviceTypes", layerDeviceTypes); + + if (layerPrecisions.empty() && layerOutputTypes.empty() && precisionConstraints != PrecisionConstraints::kNONE) + { + sample::gLogWarning << R"(When --precisionConstraints flag is set to "obey" or "prefer", please add )" + << "--layerPrecision/--layerOutputTypes flags to set layer-wise precisions and output " + << "types." << std::endl; + } + else if ((!layerPrecisions.empty() || !layerOutputTypes.empty()) + && precisionConstraints == PrecisionConstraints::kNONE) + { + sample::gLogWarning << "--layerPrecision/--layerOutputTypes flags have no effect when --precisionConstraints " + << R"(flag is set to "none".)" << std::endl; + } + + getStringsSet(arguments, "--markDebug", debugTensors); + + getAndDelOption(arguments, "--sparsity", sparsity); + + bool calibCheck = getAndDelOption(arguments, "--calib", calibration); + if (int8 && calibCheck && !optProfiles[calibProfile].empty() && shapesCalib.empty()) + { + shapesCalib = optProfiles[calibProfile]; + } + else if (!shapesCalib.empty() && getCalibProfile) + { + sample::gLogWarning + << "--calibProfile have no effect when --minShapesCalib/--optShapesCalib/--maxShapesCalib is set." + << std::endl; + } + + std::string profilingVerbosityString; + + getAndDelOption(arguments, "--profilingVerbosity", profilingVerbosityString); + if (profilingVerbosityString == "layer_names_only") + { + profilingVerbosity = nvinfer1::ProfilingVerbosity::kLAYER_NAMES_ONLY; + } + else if (profilingVerbosityString == "none") + { + profilingVerbosity = nvinfer1::ProfilingVerbosity::kNONE; + } + else if (profilingVerbosityString == "detailed") + { + profilingVerbosity = nvinfer1::ProfilingVerbosity::kDETAILED; + } + else if (profilingVerbosityString == "default") + { + sample::gLogWarning << "--profilingVerbosity=default has been deprecated by " + "--profilingVerbosity=layer_names_only." + << std::endl; + profilingVerbosity = nvinfer1::ProfilingVerbosity::kLAYER_NAMES_ONLY; + } + else if (profilingVerbosityString == "verbose") + { + sample::gLogWarning << "--profilingVerbosity=verbose has been deprecated by --profilingVerbosity=detailed." + << std::endl; + profilingVerbosity = nvinfer1::ProfilingVerbosity::kDETAILED; + } + else if (!profilingVerbosityString.empty()) + { + throw std::invalid_argument(std::string("Unknown profilingVerbosity: ") + profilingVerbosityString); + } + + if (getAndDelOption(arguments, "--loadEngine", engine)) + { + load = true; + } + getAndDelOption(arguments, "--getPlanVersionOnly", getPlanVersionOnly); + + if (getAndDelOption(arguments, "--saveEngine", engine)) + { + save = true; + } + if (load && save) + { + throw std::invalid_argument("Incompatible load and save engine options selected"); + } + + std::string tacticSourceArgs; + if (getAndDelOption(arguments, "--tacticSources", tacticSourceArgs)) + { + std::vector tacticList = splitToStringVec(tacticSourceArgs, ','); + for (auto& t : tacticList) + { + bool enable{false}; + if (t.front() == '+') + { + enable = true; + } + else if (t.front() != '-') + { + throw std::invalid_argument( + "Tactic source must be prefixed with + or -, indicating whether it should be enabled or disabled " + "respectively."); + } + t.erase(0, 1); + + const auto toUpper = [](std::string& sourceName) { + std::transform( + sourceName.begin(), sourceName.end(), sourceName.begin(), [](char c) { return std::toupper(c); }); + return sourceName; + }; + + nvinfer1::TacticSource source{}; + t = toUpper(t); + if (t == "CUBLAS") + { + source = nvinfer1::TacticSource::kCUBLAS; + } + else if (t == "CUBLASLT" || t == "CUBLAS_LT") + { + source = nvinfer1::TacticSource::kCUBLAS_LT; + } + else if (t == "CUDNN") + { + source = nvinfer1::TacticSource::kCUDNN; + } + else if (t == "EDGE_MASK_CONVOLUTIONS") + { + source = nvinfer1::TacticSource::kEDGE_MASK_CONVOLUTIONS; + } + else if (t == "JIT_CONVOLUTIONS") + { + source = nvinfer1::TacticSource::kJIT_CONVOLUTIONS; + } + else + { + throw std::invalid_argument(std::string("Unknown tactic source: ") + t); + } + + uint32_t sourceBit = 1U << static_cast(source); + + if (enable) + { + enabledTactics |= sourceBit; + } + else + { + disabledTactics |= sourceBit; + } + + if (enabledTactics & disabledTactics) + { + throw std::invalid_argument(std::string("Cannot enable and disable ") + t); + } + } + } + + bool noBuilderCache{false}; + getAndDelOption(arguments, "--noBuilderCache", noBuilderCache); + getAndDelOption(arguments, "--timingCacheFile", timingCacheFile); + if (noBuilderCache) + { + timingCacheMode = TimingCacheMode::kDISABLE; + } + else if (!timingCacheFile.empty()) + { + timingCacheMode = TimingCacheMode::kGLOBAL; + } + else + { + timingCacheMode = TimingCacheMode::kLOCAL; + } + getAndDelOption(arguments, "--errorOnTimingCacheMiss", errorOnTimingCacheMiss); + getAndDelOption(arguments, "--builderOptimizationLevel", builderOptimizationLevel); + getAndDelOption(arguments, "--maxTactics", maxTactics); +#if (NV_TENSORRT_MAJOR > 8) + std::string runtimePlatformArgs; + getAndDelOption(arguments, "--runtimePlatform", runtimePlatformArgs); + if (runtimePlatformArgs == "SameAsBuild" || runtimePlatformArgs.empty()) + { + runtimePlatform = RuntimePlatform::kSAME_AS_BUILD; + } + else if (runtimePlatformArgs == "WindowsAMD64") + { + runtimePlatform = RuntimePlatform::kWINDOWS_AMD64; + } + else + { + throw std::invalid_argument(std::string("Unknown runtime platform: ") + runtimePlatformArgs + + ". Valid options: SameAsBuild, WindowsAMD64."); + } +#endif + std::string hardwareCompatibleArgs; + getAndDelOption(arguments, "--hardwareCompatibilityLevel", hardwareCompatibleArgs); + if (hardwareCompatibleArgs == "none" || hardwareCompatibleArgs.empty()) + { + hardwareCompatibilityLevel = HardwareCompatibilityLevel::kNONE; + } + else if (samplesCommon::toLower(hardwareCompatibleArgs) == "ampere+") + { + hardwareCompatibilityLevel = HardwareCompatibilityLevel::kAMPERE_PLUS; + } + else + { + throw std::invalid_argument(std::string("Unknown hardwareCompatibilityLevel: ") + hardwareCompatibleArgs + + ". Valid options: none, ampere+."); + } + + if (pluginInstanceNorm && (versionCompatible || hardwareCompatibilityLevel == HardwareCompatibilityLevel::kAMPERE_PLUS)) + { + throw std::invalid_argument("Plugin InstanceNorm cannot be used with version compatible or hardware compatible engines!"); + } + + getAndDelOption(arguments, "--maxAuxStreams", maxAuxStreams); + + std::string previewFeaturesBuf; + getAndDelOption(arguments, "--preview", previewFeaturesBuf); + std::vector previewFeaturesVec{splitToStringVec(previewFeaturesBuf, ',')}; + for (auto featureName : previewFeaturesVec) + { + bool enable{false}; + if (featureName.front() == '+') + { + enable = true; + } + else if (featureName.front() != '-') + { + throw std::invalid_argument( + "Preview features must be prefixed with + or -, indicating whether it should be enabled or disabled " + "respectively."); + } + featureName.erase(0, 1); + + PreviewFeature feat{}; + if (featureName == "profileSharing0806") + { + sample::gLogWarning + << "profileSharing0806 is on by default in TensorRT 10.0. This flag is deprecated and has no effect." + << std::endl; + } +#if (NV_TENSORRT_MAJOR > 8) + else if (featureName == "aliasedPluginIO1003") + { + feat = PreviewFeature::kALIASED_PLUGIN_IO_10_03; + } +#endif + else + { + throw std::invalid_argument(std::string("Unknown preview feature: ") + featureName); + } + previewFeatures[static_cast(feat)] = enable; + } + + getAndDelOption(arguments, "--tempdir", tempdir); + getTempfileControls(arguments, "--tempfileControls", tempfileControls); + + std::string runtimeMode; + getAndDelOption(arguments, "--useRuntime", runtimeMode); + if (runtimeMode == "full") + { + useRuntime = RuntimeMode::kFULL; + } + else if (runtimeMode == "dispatch") + { + useRuntime = RuntimeMode::kDISPATCH; + } + else if (runtimeMode == "lean") + { + useRuntime = RuntimeMode::kLEAN; + } + else if (!runtimeMode.empty()) + { + throw std::invalid_argument(std::string("Unknown useRuntime: ") + runtimeMode); + } + + if ((useRuntime == RuntimeMode::kDISPATCH || useRuntime == RuntimeMode::kLEAN) && !versionCompatible) + { + versionCompatible = true; + sample::gLogWarning << "Implicitly enabling --versionCompatible since --useRuntime=" << runtimeMode + << " is set." << std::endl; + } + + if (useRuntime != RuntimeMode::kFULL && !load) + { + throw std::invalid_argument(std::string("Building a TensorRT engine requires --useRuntime=full.")); + } + + getAndDelOption(arguments, "--leanDLLPath", leanDLLPath); + + // Don't delete the option because the inference option parser requires it + getOption(arguments, "--allowWeightStreaming", allowWeightStreaming); +} + +void SystemOptions::parse(Arguments& arguments) +{ + getAndDelOption(arguments, "--device", device); + getAndDelOption(arguments, "--useDLACore", DLACore); +#if !TRT_WINML + std::string pluginName; + while (getAndDelOption(arguments, "--plugins", pluginName)) + { + sample::gLogWarning << "--plugins flag has been deprecated, use --staticPlugins flag instead." << std::endl; + plugins.emplace_back(pluginName); + } + while (getAndDelOption(arguments, "--staticPlugins", pluginName)) + { + plugins.emplace_back(pluginName); + } + while (getAndDelOption(arguments, "--setPluginsToSerialize", pluginName)) + { + setPluginsToSerialize.emplace_back(pluginName); + } + while (getAndDelOption(arguments, "--dynamicPlugins", pluginName)) + { + dynamicPlugins.emplace_back(pluginName); + } + getAndDelOption(arguments, "--ignoreParsedPluginLibs", ignoreParsedPluginLibs); +#endif +} + +constexpr int64_t WeightStreamingBudget::kDISABLE; +constexpr int64_t WeightStreamingBudget::kAUTOMATIC; + +void InferenceOptions::parse(Arguments& arguments) +{ + + if (getAndDelOption(arguments, "--streams", infStreams)) + { + sample::gLogWarning << "--streams flag has been deprecated, use --infStreams flag instead." << std::endl; + } + getAndDelOption(arguments, "--infStreams", infStreams); + + getAndDelOption(arguments, "--iterations", iterations); + getAndDelOption(arguments, "--duration", duration); + getAndDelOption(arguments, "--warmUp", warmup); + getAndDelOption(arguments, "--sleepTime", sleep); + getAndDelOption(arguments, "--idleTime", idle); + bool exposeDMA{false}; + if (getAndDelOption(arguments, "--exposeDMA", exposeDMA)) + { + overlap = !exposeDMA; + } + getAndDelOption(arguments, "--noDataTransfers", skipTransfers); + getAndDelOption(arguments, "--useManagedMemory", useManaged); + getAndDelOption(arguments, "--useSpinWait", spin); + getAndDelOption(arguments, "--threads", threads); + getAndDelOption(arguments, "--useCudaGraph", graph); + getAndDelOption(arguments, "--separateProfileRun", rerun); + getAndDelOption(arguments, "--timeDeserialize", timeDeserialize); + getAndDelOption(arguments, "--timeRefit", timeRefit); + getAndDelOption(arguments, "--persistentCacheRatio", persistentCacheRatio); + + std::string list; + getAndDelOption(arguments, "--loadInputs", list); + std::vector inputsList{splitToStringVec(list, ',')}; + splitInsertKeyValue(inputsList, inputs); + + getShapesInference(arguments, shapes, "--shapes"); + setOptProfile = getAndDelOption(arguments, "--useProfile", optProfileIndex); + + std::string allocationStrategyString; + getAndDelOption(arguments, "--allocationStrategy", allocationStrategyString); + if (allocationStrategyString == "static") + { + memoryAllocationStrategy = MemoryAllocationStrategy::kSTATIC; + } + else if (allocationStrategyString == "profile") + { + memoryAllocationStrategy = MemoryAllocationStrategy::kPROFILE; + } + else if (allocationStrategyString == "runtime") + { + memoryAllocationStrategy = MemoryAllocationStrategy::kRUNTIME; + } + else if (!allocationStrategyString.empty()) + { + throw std::invalid_argument(std::string("Unknown allocationStrategy: ") + allocationStrategyString); + } + + bool allowWs{false}; + getAndDelOption(arguments, "--allowWeightStreaming", allowWs); + bool wsBudgetFound = getAndDelOption(arguments, "--weightStreamingBudget", weightStreamingBudget); + if (wsBudgetFound && !allowWs) + { + throw std::invalid_argument( + "The weight streaming budget can only be set with --allowWeightStreaming specified."); + } + if (allowWs && weightStreamingBudget.isDisabled()) + { + sample::gLogWarning << "The engine can stream its weights but it will not at runtime because " + "--weightStreamingBudget unset or set to " + << WeightStreamingBudget::kDISABLE << "." << std::endl; + } + + std::string debugTensorList; + getAndDelOption(arguments, "--saveDebugTensors", debugTensorList); + std::vector fileNames{splitToStringVec(debugTensorList, ',')}; + splitInsertKeyValue(fileNames, debugTensorFileNames); +} + +void ReportingOptions::parse(Arguments& arguments) +{ + getAndDelOption(arguments, "--avgRuns", avgs); + getAndDelOption(arguments, "--verbose", verbose); + getAndDelOption(arguments, "--dumpRefit", refit); + getAndDelOption(arguments, "--dumpOutput", output); + getAndDelOption(arguments, "--dumpRawBindingsToFile", dumpRawBindings); + getAndDelOption(arguments, "--dumpProfile", profile); + getAndDelOption(arguments, "--dumpLayerInfo", layerInfo); + getAndDelOption(arguments, "--dumpOptimizationProfile", optProfileInfo); + getAndDelOption(arguments, "--exportTimes", exportTimes); + getAndDelOption(arguments, "--exportOutput", exportOutput); + getAndDelOption(arguments, "--exportProfile", exportProfile); + getAndDelOption(arguments, "--exportLayerInfo", exportLayerInfo); + + std::string percentileString; + getAndDelOption(arguments, "--percentile", percentileString); + std::vector percentileStrings = splitToStringVec(percentileString, ','); + if (!percentileStrings.empty()) + { + percentiles.clear(); + } + for (const auto& p : percentileStrings) + { + percentiles.push_back(stringToValue(p)); + } + + for (auto percentile : percentiles) + { + if (percentile < 0.F || percentile > 100.F) + { + throw std::invalid_argument(std::string("Percentile ") + std::to_string(percentile) + "is not in [0,100]"); + } + } +} + +bool parseHelp(Arguments& arguments) +{ + bool helpLong{false}; + bool helpShort{false}; + getAndDelOption(arguments, "--help", helpLong); + getAndDelOption(arguments, "-h", helpShort); + return helpLong || helpShort; +} + +void AllOptions::parse(Arguments& arguments) +{ + model.parse(arguments); + build.parse(arguments); + system.parse(arguments); + inference.parse(arguments); + + if (build.useRuntime != RuntimeMode::kFULL && inference.timeRefit) + { + throw std::invalid_argument("--timeRefit requires --useRuntime=full."); + } + + if (inference.optProfileIndex < static_cast(build.optProfiles.size())) + { + // Propagate shape profile between builder and inference + for (auto const& s : build.optProfiles[inference.optProfileIndex]) + { + if (inference.shapes.find(s.first) == inference.shapes.end()) + { + insertShapesInference( + inference.shapes, s.first, s.second[static_cast(nvinfer1::OptProfileSelector::kOPT)]); + } + } + for (auto const& s : inference.shapes) + { + if (build.optProfiles[inference.optProfileIndex].find(s.first) + == build.optProfiles[inference.optProfileIndex].end()) + { + // assume min/opt/max all the same + insertShapesBuild(build.optProfiles[inference.optProfileIndex], nvinfer1::OptProfileSelector::kMIN, + s.first, s.second); + insertShapesBuild(build.optProfiles[inference.optProfileIndex], nvinfer1::OptProfileSelector::kOPT, + s.first, s.second); + insertShapesBuild(build.optProfiles[inference.optProfileIndex], nvinfer1::OptProfileSelector::kMAX, + s.first, s.second); + } + } + } + + // Set nvtxVerbosity to be the same as build-time profilingVerbosity. + inference.nvtxVerbosity = build.profilingVerbosity; + + reporting.parse(arguments); + helps = parseHelp(arguments); + + if (!helps) + { + if (!build.load && model.baseModel.format == ModelFormat::kANY) + { + throw std::invalid_argument("Model missing or format not recognized"); + } + if (build.safe && system.DLACore >= 0) + { + build.buildDLAStandalone = true; + } +#if (NV_TENSORRT_MAJOR > 8) + if (build.runtimePlatform != nvinfer1::RuntimePlatform::kSAME_AS_BUILD) + { + build.skipInference = true; + } +#endif + if (build.buildDLAStandalone) + { + build.skipInference = true; + auto checkSafeDLAFormats = [](std::vector const& fmt, bool isInput) { + return fmt.empty() ? false : std::all_of(fmt.begin(), fmt.end(), [&](IOFormat const& pair) { + bool supported{false}; + bool const isDLA_LINEAR{ + pair.second == 1U << static_cast(nvinfer1::TensorFormat::kDLA_LINEAR)}; + bool const isHWC4{pair.second == 1U << static_cast(nvinfer1::TensorFormat::kCHW4) + || pair.second == 1U << static_cast(nvinfer1::TensorFormat::kDLA_HWC4)}; + bool const isCHW32{pair.second == 1U << static_cast(nvinfer1::TensorFormat::kCHW32)}; + bool const isCHW16{pair.second == 1U << static_cast(nvinfer1::TensorFormat::kCHW16)}; + supported |= pair.first == nvinfer1::DataType::kINT8 + && (isDLA_LINEAR || (isInput ? isHWC4 : false) || isCHW32); + supported |= pair.first == nvinfer1::DataType::kHALF + && (isDLA_LINEAR || (isInput ? isHWC4 : false) || isCHW16); + return supported; + }); + }; + if (!checkSafeDLAFormats(build.inputFormats, true) || !checkSafeDLAFormats(build.outputFormats, false)) + { + throw std::invalid_argument( + "I/O formats for safe DLA capability are restricted to fp16/int8:dla_linear, fp16/int8:hwc4, " + "fp16:chw16 or " + "int8:chw32"); + } + if (build.allowGPUFallback) + { + throw std::invalid_argument("GPU fallback (--allowGPUFallback) not allowed for DLA standalone mode"); + } + } + } +} + +void TaskInferenceOptions::parse(Arguments& arguments) +{ + getAndDelOption(arguments, "engine", engine); + getAndDelOption(arguments, "device", device); + getAndDelOption(arguments, "batch", batch); + getAndDelOption(arguments, "DLACore", DLACore); + getAndDelOption(arguments, "graph", graph); + getAndDelOption(arguments, "persistentCacheRatio", persistentCacheRatio); +} + +void SafeBuilderOptions::parse(Arguments& arguments) +{ + auto getFormats = [&arguments](std::vector& formatsVector, const char* argument) { + std::string list; + getAndDelOption(arguments, argument, list); + std::vector formats{splitToStringVec(list, ',')}; + for (const auto& f : formats) + { + formatsVector.push_back(stringToValue(f)); + } + }; + + getAndDelOption(arguments, "--serialized", serialized); + getAndDelOption(arguments, "--onnx", onnxModelFile); + getAndDelOption(arguments, "--help", help); + getAndDelOption(arguments, "-h", help); + getAndDelOption(arguments, "--verbose", verbose); + getAndDelOption(arguments, "-v", verbose); + getFormats(inputFormats, "--inputIOFormats"); + getFormats(outputFormats, "--outputIOFormats"); + getAndDelOption(arguments, "--int8", int8); + getAndDelOption(arguments, "--calib", calibFile); + getAndDelOption(arguments, "--std", standard); +#if !TRT_WINML + std::string pluginName; + while (getAndDelOption(arguments, "--plugins", pluginName)) + { + sample::gLogWarning << "--plugins flag has been deprecated, use --staticPlugins flag instead." << std::endl; + plugins.emplace_back(pluginName); + } + while (getAndDelOption(arguments, "--staticPlugins", pluginName)) + { + plugins.emplace_back(pluginName); + } +#endif + bool noBuilderCache{false}; + getAndDelOption(arguments, "--noBuilderCache", noBuilderCache); + getAndDelOption(arguments, "--timingCacheFile", timingCacheFile); + getAndDelOption(arguments, "--avgTiming", avgTiming); + if (noBuilderCache) + { + timingCacheMode = TimingCacheMode::kDISABLE; + } + else if (!timingCacheFile.empty()) + { + timingCacheMode = TimingCacheMode::kGLOBAL; + } + else + { + timingCacheMode = TimingCacheMode::kLOCAL; + } + getAndDelOption(arguments, "--sparsity", sparsity); +} + +std::ostream& operator<<(std::ostream& os, const BaseModelOptions& options) +{ + os << "=== Model Options ===" << std::endl; + + os << "Format: "; + switch (options.format) + { + case ModelFormat::kONNX: + { + os << "ONNX"; + break; + } + case ModelFormat::kANY: os << "*"; break; + } + os << std::endl << "Model: " << options.model << std::endl; + + return os; +} + +std::ostream& operator<<(std::ostream& os, const ModelOptions& options) +{ + os << options.baseModel; + switch (options.baseModel.format) + { + case ModelFormat::kONNX: // Fallthrough: No options to report for ONNX or the generic case + case ModelFormat::kANY: break; + } + + os << "Output:"; + for (const auto& o : options.outputs) + { + os << " " << o; + } + os << std::endl; + + return os; +} + +std::ostream& operator<<(std::ostream& os, nvinfer1::DataType dtype) +{ + switch (dtype) + { + case nvinfer1::DataType::kFLOAT: + { + os << "fp32"; + break; + } + case nvinfer1::DataType::kHALF: + { + os << "fp16"; + break; + } +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kBF16: + { + os << "bf16"; + break; + } +#endif + case nvinfer1::DataType::kINT8: + { + os << "int8"; + break; + } + case nvinfer1::DataType::kINT32: + { + os << "int32"; + break; + } + case nvinfer1::DataType::kBOOL: + { + os << "bool"; + break; + } + case nvinfer1::DataType::kUINT8: + { + os << "uint8"; + break; + } + case nvinfer1::DataType::kFP8: + { + os << "fp8"; + break; + } +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kINT64: + { + os << "int64"; + break; + } + case nvinfer1::DataType::kINT4: + { + os << "int4"; + break; + } +#endif + } + return os; +} + +std::ostream& operator<<(std::ostream& os, IOFormat const& format) +{ + os << format.first << ":"; + + for (int32_t f = 0; f < nvinfer1::EnumMax(); ++f) + { + if ((1U << f) & format.second) + { + if (f) + { + os << "+"; + } + switch (nvinfer1::TensorFormat(f)) + { + case nvinfer1::TensorFormat::kLINEAR: + { + os << "chw"; + break; + } + case nvinfer1::TensorFormat::kCHW2: + { + os << "chw2"; + break; + } + case nvinfer1::TensorFormat::kHWC8: + { + os << "hwc8"; + break; + } + case nvinfer1::TensorFormat::kHWC16: + { + os << "hwc16"; + break; + } + case nvinfer1::TensorFormat::kCHW4: + { + os << "chw4"; + break; + } + case nvinfer1::TensorFormat::kCHW16: + { + os << "chw16"; + break; + } + case nvinfer1::TensorFormat::kCHW32: + { + os << "chw32"; + break; + } + case nvinfer1::TensorFormat::kDHWC8: + { + os << "dhwc8"; + break; + } + case nvinfer1::TensorFormat::kCDHW32: + { + os << "cdhw32"; + break; + } + case nvinfer1::TensorFormat::kHWC: + { + os << "hwc"; + break; + } + case nvinfer1::TensorFormat::kDHWC: + { + os << "dhwc"; + break; + } + case nvinfer1::TensorFormat::kDLA_LINEAR: + { + os << "dla_linear"; + break; + } + case nvinfer1::TensorFormat::kDLA_HWC4: + { + os << "dla_hwc4"; + break; + } + } + } + } + return os; +} + +std::ostream& operator<<(std::ostream& os, nvinfer1::DeviceType devType) +{ + switch (devType) + { + case nvinfer1::DeviceType::kGPU: + { + os << "GPU"; + break; + } + case nvinfer1::DeviceType::kDLA: + { + os << "DLA"; + break; + } + } + return os; +} + +#if (NV_TENSORRT_MAJOR > 8) +std::ostream& operator<<(std::ostream& os, nvinfer1::RuntimePlatform platform) +{ + switch (platform) + { + case nvinfer1::RuntimePlatform::kSAME_AS_BUILD: + { + os << "Same As Build"; + break; + } + case nvinfer1::RuntimePlatform::kWINDOWS_AMD64: + { + os << "Windows AMD64"; + break; + } + } + return os; +} +#endif + +std::ostream& operator<<(std::ostream& os, const ShapeRange& dims) +{ + int32_t i = 0; + for (const auto& d : dims) + { + if (!d.size()) + { + break; + } + os << (i ? "+" : "") << d; + ++i; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, LayerPrecisions const& layerPrecisions) +{ + int32_t i = 0; + for (auto const& layerPrecision : layerPrecisions) + { + os << (i ? "," : "") << layerPrecision.first << ":" << layerPrecision.second; + ++i; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, LayerDeviceTypes const& layerDeviceTypes) +{ + int32_t i = 0; + for (auto const& layerDevicePair : layerDeviceTypes) + { + os << (i++ ? ", " : "") << layerDevicePair.first << ":" << layerDevicePair.second; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, StringSet const& stringSet) +{ + int64_t i = 0; + for (auto const& s : stringSet) + { + os << (i ? "," : "") << s; + ++i; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const BuildOptions& options) +{ + // if loadEngine is specified, BuildOptions are N/A + if (options.load) + { + os << std::endl; + return os; + } + // clang-format off + os << "=== Build Options ===" << std::endl << + "Memory Pools: "; printMemoryPools(os, options) << std::endl << + "avgTiming: " << options.avgTiming << std::endl << + "Precision: "; printPrecision(os, options) << std::endl << + "LayerPrecisions: " << options.layerPrecisions << std::endl << + "Layer Device Types: " << options.layerDeviceTypes << std::endl << + "Calibration: " << (options.int8 && options.calibration.empty() ? "Dynamic" : options.calibration.c_str()) << std::endl << + "Refit: " << boolToEnabled(options.refittable) << std::endl << + "Strip weights: " << boolToEnabled(options.stripWeights) << std::endl << + "Version Compatible: " << boolToEnabled(options.versionCompatible) << std::endl << +#if !TRT_WINML + "ONNX Plugin InstanceNorm: " << boolToEnabled(options.pluginInstanceNorm) << std::endl << +#endif + "TensorRT runtime: " << options.useRuntime << std::endl << + "Lean DLL Path: " << options.leanDLLPath << std::endl << + "Tempfile Controls: "; printTempfileControls(os, options.tempfileControls) << std::endl << + "Exclude Lean Runtime: " << boolToEnabled(options.excludeLeanRuntime) << std::endl << + "Sparsity: "; printSparsity(os, options) << std::endl << + "Safe mode: " << boolToEnabled(options.safe) << std::endl << + "Build DLA standalone loadable: " << boolToEnabled(options.buildDLAStandalone) << std::endl << + "Allow GPU fallback for DLA: " << boolToEnabled(options.allowGPUFallback) << std::endl << + "DirectIO mode: " << boolToEnabled(options.directIO) << std::endl << + "Restricted mode: " << boolToEnabled(options.restricted) << std::endl << + "Skip inference: " << boolToEnabled(options.skipInference) << std::endl << + "Save engine: " << (options.save ? options.engine : "") << std::endl << + "Load engine: " << (options.load ? options.engine : "") << std::endl << + "Profiling verbosity: " << static_cast(options.profilingVerbosity) << std::endl << + "Tactic sources: "; printTacticSources(os, options.enabledTactics, options.disabledTactics) << std::endl << + "timingCacheMode: "; printTimingCache(os, options.timingCacheMode) << std::endl << + "timingCacheFile: " << options.timingCacheFile << std::endl << + "Enable Compilation Cache: "<< boolToEnabled(!options.disableCompilationCache) << std::endl << + "errorOnTimingCacheMiss: " << boolToEnabled(options.errorOnTimingCacheMiss) << std::endl << + "Preview Features: "; printPreviewFlags(os, options) << std::endl << + "MaxAuxStreams: " << options.maxAuxStreams << std::endl << + "BuilderOptimizationLevel: " << options.builderOptimizationLevel << std::endl << + "MaxTactics: " << options.maxTactics << std::endl << + "Calibration Profile Index: " << options.calibProfile << std::endl << + "Weight Streaming: " << boolToEnabled(options.allowWeightStreaming) << std::endl << +#if (NV_TENSORRT_MAJOR > 8) + "Runtime Platform: " << options.runtimePlatform << std::endl << +#endif + "Debug Tensors: " << options.debugTensors << std::endl; + // clang-format on + + auto printIOFormats = [](std::ostream& os, const char* direction, const std::vector formats) { + if (formats.empty()) + { + os << direction << "s format: fp32:CHW" << std::endl; + } + else + { + for (const auto& f : formats) + { + os << direction << ": " << f << std::endl; + } + } + }; + + printIOFormats(os, "Input(s)", options.inputFormats); + printIOFormats(os, "Output(s)", options.outputFormats); + for (size_t i = 0; i < options.optProfiles.size(); i++) + { + printShapes(os, "build", options.optProfiles[i], i); + } + printShapes(os, "calibration", options.shapesCalib, -1); + + return os; +} + +std::ostream& operator<<(std::ostream& os, const SystemOptions& options) +{ + // clang-format off + os << "=== System Options ===" << std::endl << + + "Device: " << options.device << std::endl << + "DLACore: " << (options.DLACore != -1 ? std::to_string(options.DLACore) : "") << std::endl; +#if !TRT_WINML + os << "Plugins:"; + + for (const auto& p : options.plugins) + { + os << " " << p; + } + os << std::endl; + + os << "setPluginsToSerialize:"; + + for (const auto& p : options.setPluginsToSerialize) + { + os << " " << p; + } + os << std::endl; + + os << "dynamicPlugins:"; + + for (const auto& p : options.dynamicPlugins) + { + os << " " << p; + } + os << std::endl; + + os << "ignoreParsedPluginLibs: " << options.ignoreParsedPluginLibs << std::endl; + os << std::endl; +#endif + return os; + // clang-format on +} + +std::ostream& operator<<(std::ostream& os, const InferenceOptions& options) +{ + // clang-format off + os << "=== Inference Options ===" << std::endl << + + "Batch: "; + if (options.batch && options.shapes.empty()) + { + os << options.batch << std::endl; + } + else + { + os << "Explicit" << std::endl; + } + printShapes(os, "inference", options.shapes, options.optProfileIndex); + + std::string wsBudget{"Disabled"}; + if (options.weightStreamingBudget.bytes == WeightStreamingBudget::kAUTOMATIC) + { + wsBudget = "Automatic"; + } + else if (options.weightStreamingBudget.bytes != WeightStreamingBudget::kDISABLE) + { + wsBudget = std::to_string(options.weightStreamingBudget.bytes) + " bytes"; + } + else if (options.weightStreamingBudget.percent != WeightStreamingBudget::kDISABLE) + { + wsBudget = std::to_string(options.weightStreamingBudget.percent) + "%"; + } + + os << "Iterations: " << options.iterations << std::endl << + "Duration: " << options.duration << "s (+ " + << options.warmup << "ms warm up)" << std::endl << + "Sleep time: " << options.sleep << "ms" << std::endl << + "Idle time: " << options.idle << "ms" << std::endl << + "Inference Streams: " << options.infStreams << std::endl << + "ExposeDMA: " << boolToEnabled(!options.overlap) << std::endl << + "Data transfers: " << boolToEnabled(!options.skipTransfers) << std::endl << + "Spin-wait: " << boolToEnabled(options.spin) << std::endl << + "Multithreading: " << boolToEnabled(options.threads) << std::endl << + "CUDA Graph: " << boolToEnabled(options.graph) << std::endl << + "Separate profiling: " << boolToEnabled(options.rerun) << std::endl << + "Time Deserialize: " << boolToEnabled(options.timeDeserialize) << std::endl << + "Time Refit: " << boolToEnabled(options.timeRefit) << std::endl << + "NVTX verbosity: " << static_cast(options.nvtxVerbosity) << std::endl << + "Persistent Cache Ratio: " << static_cast(options.persistentCacheRatio) << std::endl << + "Optimization Profile Index: "<< options.optProfileIndex << std::endl << + "Weight Streaming Budget: " << wsBudget << std::endl; + // clang-format on + + os << "Inputs:" << std::endl; + for (const auto& input : options.inputs) + { + os << input.first << "<-" << input.second << std::endl; + } + + os << "Debug Tensor Save Destinations:" << std::endl; + for (auto const& fileName : options.debugTensorFileNames) + { + os << fileName.first << ": " << fileName.second << std::endl; + } + + return os; +} + +std::ostream& operator<<(std::ostream& os, const ReportingOptions& options) +{ + // clang-format off + os << "=== Reporting Options ===" << std::endl << + "Verbose: " << boolToEnabled(options.verbose) << std::endl << + "Averages: " << options.avgs << " inferences" << std::endl << + "Percentiles: " << joinValuesToString(options.percentiles, ",") << std::endl << + "Dump refittable layers:" << boolToEnabled(options.refit) << std::endl << + "Dump output: " << boolToEnabled(options.output) << std::endl << + "Profile: " << boolToEnabled(options.profile) << std::endl << + "Export timing to JSON file: " << options.exportTimes << std::endl << + "Export output to JSON file: " << options.exportOutput << std::endl << + "Export profile to JSON file: " << options.exportProfile << std::endl; + // clang-format on + + return os; +} + +std::ostream& operator<<(std::ostream& os, const AllOptions& options) +{ + os << options.model << options.build << options.system << options.inference << options.reporting << std::endl; + return os; +} + +std::ostream& operator<<(std::ostream& os, const SafeBuilderOptions& options) +{ + auto printIOFormats = [](std::ostream& os, const char* direction, const std::vector formats) { + if (formats.empty()) + { + os << direction << "s format: fp32:CHW" << std::endl; + } + else + { + for (const auto& f : formats) + { + os << direction << ": " << f << std::endl; + } + } + }; + + os << "=== Build Options ===" << std::endl; + os << "Model ONNX: " << options.onnxModelFile << std::endl; + + os << "Precision: FP16"; + if (options.int8) + { + os << " + INT8"; + } + if (options.fp8) + { + os << " + FP8"; + } + if (options.int4) + { + os << " + INT4"; + } + os << std::endl; + os << "Calibration file: " << options.calibFile << std::endl; + os << "Serialized Network: " << options.serialized << std::endl; + + printIOFormats(os, "Input(s)", options.inputFormats); + printIOFormats(os, "Output(s)", options.outputFormats); +#if !TRT_WINML + os << "Plugins:"; + for (const auto& p : options.plugins) + { + os << " " << p; + } +#endif + os << "timingCacheMode: "; + printTimingCache(os, options.timingCacheMode) << std::endl; + os << "timingCacheFile: " << options.timingCacheFile << std::endl; + os << std::endl; + return os; +} + +void BaseModelOptions::help(std::ostream& os) +{ + // clang-format off + os << " --onnx= ONNX model" << std::endl; + // clang-format on +} + +void ModelOptions::help(std::ostream& os) +{ + // clang-format off + os << "=== Model Options ===" << std::endl; + BaseModelOptions::help(os); + // clang-format on +} + +void BuildOptions::help(std::ostream& os) +{ + // clang-format off + os << "=== Build Options ===" "\n" + " --minShapes=spec Build with dynamic shapes using a profile with the min shapes provided" "\n" + " --optShapes=spec Build with dynamic shapes using a profile with the opt shapes provided" "\n" + " --maxShapes=spec Build with dynamic shapes using a profile with the max shapes provided" "\n" + " --minShapesCalib=spec Calibrate with dynamic shapes using a profile with the min shapes provided" "\n" + " --optShapesCalib=spec Calibrate with dynamic shapes using a profile with the opt shapes provided" "\n" + " --maxShapesCalib=spec Calibrate with dynamic shapes using a profile with the max shapes provided" "\n" + " Note: All three of min, opt and max shapes must be supplied." "\n" + " However, if only opt shapes is supplied then it will be expanded so" "\n" + " that min shapes and max shapes are set to the same values as opt shapes." "\n" + " Input names can be wrapped with escaped single quotes (ex: 'Input:0')." "\n" + " Example input shapes spec: input0:1x3x256x256,input1:1x3x128x128" "\n" + " For scalars (0-D shapes), use input0:scalar or simply input0: with nothing after the colon." "\n" + " Each input shape is supplied as a key-value pair where key is the input name and" "\n" + " value is the dimensions (including the batch dimension) to be used for that input." "\n" + " Each key-value pair has the key and value separated using a colon (:)." "\n" + " Multiple input shapes can be provided via comma-separated key-value pairs, and each input name can" "\n" + " contain at most one wildcard ('*') character." "\n" + " --inputIOFormats=spec Type and format of each of the input tensors (default = all inputs in fp32:chw)" "\n" + " See --outputIOFormats help for the grammar of type and format list." "\n" + " Note: If this option is specified, please set comma-separated types and formats for all" "\n" + " inputs following the same order as network inputs ID (even if only one input" "\n" + " needs specifying IO format) or set the type and format once for broadcasting." "\n" + " --outputIOFormats=spec Type and format of each of the output tensors (default = all outputs in fp32:chw)" "\n" + " Note: If this option is specified, please set comma-separated types and formats for all" "\n" + " outputs following the same order as network outputs ID (even if only one output" "\n" + " needs specifying IO format) or set the type and format once for broadcasting." "\n" + R"( IO Formats: spec ::= IOfmt[","spec])" "\n" + " IOfmt ::= type:fmt" "\n" + R"( type ::= "fp32"|"fp16"|"bf16"|"int32"|"int64"|"int8"|"uint8"|"bool")" "\n" + R"( fmt ::= ("chw"|"chw2"|"chw4"|"hwc8"|"chw16"|"chw32"|"dhwc8"|)" "\n" + R"( "cdhw32"|"hwc"|"dla_linear"|"dla_hwc4")["+"fmt])" "\n" + " --memPoolSize=poolspec Specify the size constraints of the designated memory pool(s)" "\n" + " Supports the following base-2 suffixes: " << getAvailableUnitSuffixes() << "." "\n" + " If none of suffixes is appended, the defualt unit is in MiB." "\n" + " Note: Also accepts decimal sizes, e.g. 0.25M. Will be rounded down to the nearest integer bytes." "\n" + " In particular, for dlaSRAM the bytes will be rounded down to the nearest power of 2." "\n" + R"( Pool constraint: poolspec ::= poolfmt[","poolspec])" "\n" + " poolfmt ::= pool:size" "\n" + R"( pool ::= "workspace"|"dlaSRAM"|"dlaLocalDRAM"|"dlaGlobalDRAM"|"tacticSharedMem")" "\n" + " --profilingVerbosity=mode Specify profiling verbosity. mode ::= layer_names_only|detailed|none (default = layer_names_only)." "\n" + " Please only assign once." "\n" + " --avgTiming=M Set the number of times averaged in each iteration for kernel selection (default = " + << defaultAvgTiming << ")" "\n" + " --refit Mark the engine as refittable. This will allow the inspection of refittable layers " "\n" + " and weights within the engine." "\n" + " --stripWeights Strip weights from plan. This flag works with either refit or refit with identical weights. Default""\n" + " to latter, but you can switch to the former by enabling both --stripWeights and --refit at the same""\n" + " time." "\n" + " --stripAllWeights Alias for combining the --refit and --stripWeights options. It marks all weights as refittable," "\n" + " disregarding any performance impact. Additionally, it strips all refittable weights after the " "\n" + " engine is built." "\n" + " --weightless [Deprecated] this knob has been deprecated. Please use --stripWeights" "\n" + " --versionCompatible, --vc Mark the engine as version compatible. This allows the engine to be used with newer versions" "\n" + " of TensorRT on the same host OS, as well as TensorRT's dispatch and lean runtimes." "\n" +#if !TRT_WINML + " --pluginInstanceNorm, --pi Set `kNATIVE_INSTANCENORM` to false in the ONNX parser. This will cause the ONNX parser to use" "\n" + " a plugin InstanceNorm implementation over the native implementation when parsing." "\n" +#endif + R"( --useRuntime=runtime TensorRT runtime to execute engine. "lean" and "dispatch" require loading VC engine and do)" "\n" + " not support building an engine." "\n" + R"( runtime::= "full"|"lean"|"dispatch")" "\n" + " --leanDLLPath= External lean runtime DLL to use in version compatiable mode." "\n" + " --excludeLeanRuntime When --versionCompatible is enabled, this flag indicates that the generated engine should" "\n" + " not include an embedded lean runtime. If this is set, the user must explicitly specify a" "\n" + " valid lean runtime to use when loading the engine." "\n" + " --sparsity=spec Control sparsity (default = disabled). " "\n" + R"( Sparsity: spec ::= "disable", "enable", "force")" "\n" + " Note: Description about each of these options is as below" "\n" + " disable = do not enable sparse tactics in the builder (this is the default)" "\n" + " enable = enable sparse tactics in the builder (but these tactics will only be" "\n" + " considered if the weights have the right sparsity pattern)" "\n" + " force = enable sparse tactics in the builder and force-overwrite the weights to have" "\n" + " a sparsity pattern (even if you loaded a model yourself)" "\n" + " [Deprecated] this knob has been deprecated." "\n" + " Please use to rewrite the weights." "\n" + " --noTF32 Disable tf32 precision (default is to enable tf32, in addition to fp32)" "\n" + " --fp16 Enable fp16 precision, in addition to fp32 (default = disabled)" "\n" + " --bf16 Enable bf16 precision, in addition to fp32 (default = disabled)" "\n" + " --int8 Enable int8 precision, in addition to fp32 (default = disabled)" "\n" + " --fp8 Enable fp8 precision, in addition to fp32 (default = disabled)" "\n" + " --int4 Enable int4 precision, in addition to fp32 (default = disabled)" "\n" + " --best Enable all precisions to achieve the best performance (default = disabled)" "\n" + " --stronglyTyped Create a strongly typed network. (default = disabled)" "\n" + " --directIO Avoid reformatting at network boundaries. (default = disabled)" "\n" + " --precisionConstraints=spec Control precision constraint setting. (default = none)" "\n" + R"( Precision Constraints: spec ::= "none" | "obey" | "prefer")" "\n" + " none = no constraints" "\n" + " prefer = meet precision constraints set by --layerPrecisions/--layerOutputTypes if possible" "\n" + " obey = meet precision constraints set by --layerPrecisions/--layerOutputTypes or fail" "\n" + " otherwise" "\n" + " --layerPrecisions=spec Control per-layer precision constraints. Effective only when precisionConstraints is set to" "\n" + R"( "obey" or "prefer". (default = none))" "\n" + R"( The specs are read left-to-right, and later ones override earlier ones. Each layer name can)" "\n" + " contain at most one wildcard ('*') character." "\n" + R"( Per-layer precision spec ::= layerPrecision[","spec])" "\n" + R"( layerPrecision ::= layerName":"precision)" "\n" + R"( precision ::= "fp32"|"fp16"|"bf16"|"int32"|"int8")" "\n" + " --layerOutputTypes=spec Control per-layer output type constraints. Effective only when precisionConstraints is set to" "\n" + R"( "obey" or "prefer". (default = none)" "\n" + R"( The specs are read left-to-right, and later ones override earlier ones. Each layer name can)" "\n" + " contain at most one wildcard ('*') character. If a layer has more than" "\n" + R"( one output, then multiple types separated by "+" can be provided for this layer.)" "\n" + R"( Per-layer output type spec ::= layerOutputTypes[","spec])" "\n" + R"( layerOutputTypes ::= layerName":"type)" "\n" + R"( type ::= "fp32"|"fp16"|"bf16"|"int32"|"int8"["+"type])" "\n" + " --layerDeviceTypes=spec Specify layer-specific device type." "\n" + " The specs are read left-to-right, and later ones override earlier ones. If a layer does not have" "\n" + " a device type specified, the layer will opt for the default device type." "\n" + R"( Per-layer device type spec ::= layerDeviceTypePair[","spec])" "\n" + R"( layerDeviceTypePair ::= layerName":"deviceType)" "\n" + R"( deviceType ::= "GPU"|"DLA")" "\n" + " --calib= Read INT8 calibration cache file" "\n" + " --safe Enable build safety certified engine, if DLA is enable, --buildDLAStandalone will be specified" "\n" + " automatically (default = disabled)" "\n" + " --buildDLAStandalone Enable build DLA standalone loadable which can be loaded by cuDLA, when this option is enabled, " "\n" + " --allowGPUFallback is disallowed and --skipInference is enabled by default. Additionally, " "\n" + " specifying --inputIOFormats and --outputIOFormats restricts I/O data type and memory layout" "\n" + " (default = disabled)" "\n" + " --allowGPUFallback When DLA is enabled, allow GPU fallback for unsupported layers (default = disabled)" "\n" + " --restricted Enable safety scope checking with kSAFETY_SCOPE build flag" "\n" + " --saveEngine= Save the serialized engine" "\n" + " --loadEngine= Load a serialized engine" "\n" + " --getPlanVersionOnly Print TensorRT version when loaded plan was created. Works without deserialization of the plan." "\n" + " Use together with --loadEngine. Supported only for engines created with 8.6 and forward." "\n" + " --tacticSources=tactics Specify the tactics to be used by adding (+) or removing (-) tactics from the default " "\n" + " tactic sources (default = all available tactics)." "\n" + " Note: Currently only cuDNN, cuBLAS, cuBLAS-LT, and edge mask convolutions are listed as optional" "\n" + " tactics." "\n" + R"( Tactic Sources: tactics ::= [","tactic])" "\n" + " tactic ::= (+|-)lib" "\n" + R"( lib ::= "CUBLAS"|"CUBLAS_LT"|"CUDNN"|"EDGE_MASK_CONVOLUTIONS")" "\n" + R"( |"JIT_CONVOLUTIONS")" "\n" + " For example, to disable cudnn and enable cublas: --tacticSources=-CUDNN,+CUBLAS" "\n" + " --noBuilderCache Disable timing cache in builder (default is to enable timing cache)" "\n" + " --noCompilationCache Disable Compilation cache in builder, and the cache is part of timing cache (default is to enable compilation cache)" "\n" + " --errorOnTimingCacheMiss Emit error when a tactic being timed is not present in the timing cache (default = false)" "\n" + " --timingCacheFile= Save/load the serialized global timing cache" "\n" + " --preview=features Specify preview feature to be used by adding (+) or removing (-) preview features from the default" "\n" + R"( Preview Features: features ::= [","feature])" "\n" + " feature ::= (+|-)flag" "\n" + R"( flag ::= "aliasedPluginIO1003")" "\n" + R"( |"profileSharing0806")" "\n" + " --builderOptimizationLevel Set the builder optimization level. (default is 3)" "\n" + " Higher level allows TensorRT to spend more building time for more optimization options." "\n" + " Valid values include integers from 0 to the maximum optimization level, which is currently 5." "\n" + " --maxTactics Set the maximum number of tactics to time when there is a choice of tactics. (default is -1)" "\n" + " Larger number of tactics allow TensorRT to spend more building time on evaluating tactics." "\n" + " Default value -1 means TensorRT can decide the number of tactics based on its own heuristic." "\n" + " --hardwareCompatibilityLevel=mode Make the engine file compatible with other GPU architectures. (default = none)" "\n" + R"( Hardware Compatibility Level: mode ::= "none" | "ampere+")" "\n" + " none = no compatibility" "\n" + " ampere+ = compatible with Ampere and newer GPUs" "\n" + " --runtimePlatform=platform Set the target platform for runtime execution. (default = SameAsBuild)" "\n" + " When this option is enabled, --skipInference is enabled by default." "\n" + R"( RuntimePlatfrom: platform ::= "SameAsBuild" | "WindowsAMD64")" "\n" + " SameAsBuild = no requirement for cross-platform compatibility." "\n" + " WindowsAMD64 = set the target platform for engine execution as Windows AMD64 system" "\n" + " --tempdir= Overrides the default temporary directory TensorRT will use when creating temporary files." "\n" + " See IRuntime::setTemporaryDirectory API documentation for more information." "\n" + " --tempfileControls=controls Controls what TensorRT is allowed to use when creating temporary executable files." "\n" + " Should be a comma-separated list with entries in the format (in_memory|temporary):(allow|deny)." "\n" + " in_memory: Controls whether TensorRT is allowed to create temporary in-memory executable files." "\n" + " temporary: Controls whether TensorRT is allowed to create temporary executable files in the" "\n" + " filesystem (in the directory given by --tempdir)." "\n" + " For example, to allow in-memory files and disallow temporary files:" "\n" + " --tempfileControls=in_memory:allow,temporary:deny" "\n" + R"( If a flag is unspecified, the default behavior is "allow".)" "\n" + " --maxAuxStreams=N Set maximum number of auxiliary streams per inference stream that TRT is allowed to use to run " "\n" + " kernels in parallel if the network contains ops that can run in parallel, with the cost of more " "\n" + " memory usage. Set this to 0 for optimal memory usage. (default = using heuristics)" "\n" + " --profile Build with dynamic shapes using a profile with the min/max/opt shapes provided. Can be specified" "\n" + " multiple times to create multiple profiles with contiguous index." "\n" + " (ex: --profile=0 --minShapes= --optShapes= --maxShapes= --profile=1 ...)" "\n" + " --calibProfile Select the optimization profile to calibrate by index. (default = " + << defaultOptProfileIndex << ")" "\n" + " --allowWeightStreaming Enable a weight streaming engine. Must be specified with --stronglyTyped. TensorRT will disable" "\n" + " weight streaming at runtime unless --weightStreamingBudget is specified." "\n" + " --markDebug Specify list of names of tensors to be marked as debug tensors. Separate names with a comma" "\n" + ; + // clang-format on + os << std::flush; +} + +void SystemOptions::help(std::ostream& os) +{ + // clang-format off + os << "=== System Options ===" << std::endl << + " --device=N Select cuda device N (default = " << defaultDevice << ")" << std::endl << + " --useDLACore=N Select DLA core N for layers that support DLA (default = none)" << std::endl << +#if TRT_WINML + std::endl; +#else + " --staticPlugins Plugin library (.so) to load statically (can be specified multiple times)" << std::endl << + " --dynamicPlugins Plugin library (.so) to load dynamically and may be serialized with the engine if they are included in --setPluginsToSerialize (can be specified multiple times)" << std::endl << + " --setPluginsToSerialize Plugin library (.so) to be serialized with the engine (can be specified multiple times)" << std::endl << + " --ignoreParsedPluginLibs By default, when building a version-compatible engine, plugin libraries specified by the ONNX parser " << std::endl << + " are implicitly serialized with the engine (unless --excludeLeanRuntime is specified) and loaded dynamically. " << std::endl << + " Enable this flag to ignore these plugin libraries instead." << std::endl; +#endif + // clang-format on +} + +void InferenceOptions::help(std::ostream& os) +{ + // clang-format off + os << "=== Inference Options ===" << std::endl << + " --shapes=spec Set input shapes for dynamic shapes inference inputs." << std::endl << + R"( Note: Input names can be wrapped with escaped single quotes (ex: 'Input:0').)" << std::endl << + " Example input shapes spec: input0:1x3x256x256, input1:1x3x128x128" << std::endl << + " For scalars (0-D shapes), use input0:scalar or simply input0: with nothing after the colon."<< std::endl << + " Each input shape is supplied as a key-value pair where key is the input name and" << std::endl << + " value is the dimensions (including the batch dimension) to be used for that input." << std::endl << + " Each key-value pair has the key and value separated using a colon (:)." << std::endl << + " Multiple input shapes can be provided via comma-separated key-value pairs, and each input " << std::endl << + " name can contain at most one wildcard ('*') character." << std::endl << + " --loadInputs=spec Load input values from files (default = generate random inputs). Input names can be " + "wrapped with single quotes (ex: 'Input:0')" << std::endl << + R"( Input values spec ::= Ival[","spec])" << std::endl << + R"( Ival ::= name":"file)" << std::endl << + " Consult the README for more information on generating files for custom inputs." << std::endl << + " --iterations=N Run at least N inference iterations (default = " << defaultIterations << ")" << std::endl << + " --warmUp=N Run for N milliseconds to warmup before measuring performance (default = " + << defaultWarmUp << ")" << std::endl << + " --duration=N Run performance measurements for at least N seconds wallclock time (default = " + << defaultDuration << ")" << std::endl << + " If -1 is specified, inference will keep running unless stopped manually" << std::endl << + " --sleepTime=N Delay inference start with a gap of N milliseconds between launch and compute " + "(default = " << defaultSleep << ")" << std::endl << + " --idleTime=N Sleep N milliseconds between two continuous iterations" + "(default = " << defaultIdle << ")" << std::endl << + " --infStreams=N Instantiate N execution contexts to run inference concurrently " + "(default = " << defaultStreams << ")" << std::endl << + " --exposeDMA Serialize DMA transfers to and from device (default = disabled)." << std::endl << + " --noDataTransfers Disable DMA transfers to and from device (default = enabled)." << std::endl << + " --useManagedMemory Use managed memory instead of separate host and device allocations (default = disabled)." << std::endl << + " --useSpinWait Actively synchronize on GPU events. This option may decrease synchronization time but " + "increase CPU usage and power (default = disabled)" << std::endl << + " --threads Enable multithreading to drive engines with independent threads" + " or speed up refitting (default = disabled) " << std::endl << + " --useCudaGraph Use CUDA graph to capture engine execution and then launch inference (default = disabled)." << std::endl << + " This flag may be ignored if the graph capture fails." << std::endl << + " --timeDeserialize Time the amount of time it takes to deserialize the network and exit." << std::endl << + " --timeRefit Time the amount of time it takes to refit the engine before inference." << std::endl << + " --separateProfileRun Do not attach the profiler in the benchmark run; if profiling is enabled, a second " + "profile run will be executed (default = disabled)" << std::endl << + " --skipInference Exit after the engine has been built and skip inference perf measurement " + "(default = disabled)" << std::endl << + " --persistentCacheRatio Set the persistentCacheLimit in ratio, 0.5 represent half of max persistent L2 size " + "(default = 0)" << std::endl << + " --useProfile Set the optimization profile for the inference context " + "(default = " << defaultOptProfileIndex << " )." << std::endl << + " --allocationStrategy=spec Specify how the internal device memory for inference is allocated." << std::endl << + R"( Strategy: spec ::= "static", "profile", "runtime")" << std::endl << + " static = Allocate device memory based on max size across all profiles." << std::endl << + " profile = Allocate device memory based on max size of the current profile." << std::endl << + " runtime = Allocate device memory based on the actual input shapes." << std::endl << + " --saveDebugTensors Specify list of names of tensors to turn on the debug state" << std::endl << + " and filename to save raw outputs to." << std::endl << + " These tensors must be specified as debug tensors during build time." << std::endl << + R"( Input values spec ::= Ival[","spec])" << std::endl << + R"( Ival ::= name":"file)" << std::endl << + " --weightStreamingBudget Set the maximum amount of GPU memory TensorRT is allowed to use for weights." << std::endl << + " It can take on the following values:" << std::endl << + " -2: (default) Disable weight streaming at runtime." << std::endl << + " -1: TensorRT will automatically decide the budget." << std::endl << + " 0-100%: Percentage of streamable weights that reside on the GPU." << std::endl << + " 0% saves the most memory but will have the worst performance." << std::endl << + " Requires the % character." << std::endl << + " >=0B: The exact amount of streamable weights that reside on the GPU. Supports the " << std::endl << + " following base-2 suffixes: " << getAvailableUnitSuffixes() << "." << std::endl; + // clang-format on +} + +void ReportingOptions::help(std::ostream& os) +{ + // clang-format off + os << "=== Reporting Options ===" << std::endl << + " --verbose Use verbose logging (default = false)" << std::endl << + " --avgRuns=N Report performance measurements averaged over N consecutive " + "iterations (default = " << defaultAvgRuns << ")" << std::endl << + " --percentile=P1,P2,P3,... Report performance for the P1,P2,P3,... percentages (0<=P_i<=100, 0 " + "representing max perf, and 100 representing min perf; (default" + " = " << joinValuesToString(defaultPercentiles, ",") << "%)" << std::endl << + " --dumpRefit Print the refittable layers and weights from a refittable " + "engine" << std::endl << + " --dumpOutput Print the output tensor(s) of the last inference iteration " + "(default = disabled)" << std::endl << + " --dumpRawBindingsToFile Print the input/output tensor(s) of the last inference iteration to file" + "(default = disabled)" << std::endl << + " --dumpProfile Print profile information per layer (default = disabled)" << std::endl << + " --dumpLayerInfo Print layer information of the engine to console " + "(default = disabled)" << std::endl << + " --dumpOptimizationProfile Print the optimization profile(s) information " + "(default = disabled)" << std::endl << + " --exportTimes= Write the timing results in a json file (default = disabled)" << std::endl << + " --exportOutput= Write the output tensors to a json file (default = disabled)" << std::endl << + " --exportProfile= Write the profile information per layer in a json file " + "(default = disabled)" << std::endl << + " --exportLayerInfo= Write the layer information of the engine in a json file " + "(default = disabled)" << std::endl; + // clang-format on +} + +void TaskInferenceOptions::help(std::ostream& os) +{ + // clang-format off + os << "=== Task Inference Options ===" << std::endl << + " engine= Specify a serialized engine for this task" << std::endl << + " device=N Specify a GPU device for this task" << std::endl << + " DLACore=N Specify a DLACore for this task" << std::endl << + " batch=N Set batch size for implicit batch engines (default = " << defaultBatch << ")" << std::endl << + " This option should not be used for explicit batch engines" << std::endl << + " graph=1 Use cuda graph for this task" << std::endl << + " persistentCacheRatio=[0-1] Set the persistentCacheLimit ratio for this task (default = 0)" << std::endl; + // clang-format on +} + +void helpHelp(std::ostream& os) +{ + // clang-format off + os << "=== Help ===" << std::endl << + " --help, -h Print this message" << std::endl; + // clang-format on +} + +void AllOptions::help(std::ostream& os) +{ + ModelOptions::help(os); + os << std::endl; + BuildOptions::help(os); + os << std::endl; + InferenceOptions::help(os); + os << std::endl; + ReportingOptions::help(os); + os << std::endl; + SystemOptions::help(os); + os << std::endl; + helpHelp(os); +} + +void SafeBuilderOptions::printHelp(std::ostream& os) +{ + // clang-format off + os << "=== Mandatory ===" << std::endl << + " --onnx= ONNX model" << std::endl << + " " << std::endl << + "=== Optional ===" << std::endl << + " --inputIOFormats=spec Type and format of each of the input tensors (default = all inputs in fp32:chw)" << std::endl << + " See --outputIOFormats help for the grammar of type and format list." << std::endl << + " Note: If this option is specified, please set comma-separated types and formats for all" << std::endl << + " inputs following the same order as network inputs ID (even if only one input" << std::endl << + " needs specifying IO format) or set the type and format once for broadcasting." << std::endl << + " --outputIOFormats=spec Type and format of each of the output tensors (default = all outputs in fp32:chw)" << std::endl << + " Note: If this option is specified, please set comma-separated types and formats for all" << std::endl << + " outputs following the same order as network outputs ID (even if only one output" << std::endl << + " needs specifying IO format) or set the type and format once for broadcasting." << std::endl << + R"( IO Formats: spec ::= IOfmt[","spec])" << std::endl << + " IOfmt ::= type:fmt" << std::endl << + R"( type ::= "fp32"|"fp16"|"int32"|"int8")" << std::endl << + R"( fmt ::= ("chw"|"chw2"|"chw4"|"hwc8"|"chw16"|"chw32"|"dhwc8"|)" << std::endl << + R"( "cdhw32"|"hwc"|"dla_linear"|"dla_hwc4")["+"fmt])" << std::endl << + " --int8 Enable int8 precision, in addition to fp16 (default = disabled)" << std::endl << + " --std Build standard serialized engine, (default = disabled)" << std::endl << + " --calib= Read INT8 calibration cache file" << std::endl << + " --serialized= Save the serialized network" << std::endl << +#if !TRT_WINML + " --staticPlugins Plugin library (.so) to load statically (can be specified multiple times)" << std::endl << +#endif + " --verbose or -v Use verbose logging (default = false)" << std::endl << + " --help or -h Print this message" << std::endl << + " --noBuilderCache Disable timing cache in builder (default is to enable timing cache)" << std::endl << + " --timingCacheFile= Save/load the serialized global timing cache" << std::endl << + " --sparsity=spec Control sparsity (default = disabled). " << std::endl << + R"( Sparsity: spec ::= "disable", "enable", "force")" << std::endl << + " Note: Description about each of these options is as below" << std::endl << + " disable = do not enable sparse tactics in the builder (this is the default)" << std::endl << + " enable = enable sparse tactics in the builder (but these tactics will only be" << std::endl << + " considered if the weights have the right sparsity pattern)" << std::endl << + " force = enable sparse tactics in the builder and force-overwrite the weights to have" << std::endl << + " a sparsity pattern" << std::endl << + " --avgTiming=M Set the number of times averaged in each iteration for kernel selection (default = " << std::endl << + "" << defaultAvgTiming << ")" << std::endl << + "" << std::endl; + // clang-format on +} + +} // namespace sample diff --git a/src/Detector/tensorrt_onnx/common/sampleOptions.h b/src/Detector/tensorrt_onnx/common/sampleOptions.h new file mode 100644 index 000000000..d374b3a4b --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleOptions.h @@ -0,0 +1,495 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef TRT_SAMPLE_OPTIONS_H +#define TRT_SAMPLE_OPTIONS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "NvInfer.h" + +namespace sample +{ + +// Build default params +constexpr int32_t defaultAvgTiming{8}; +constexpr int32_t defaultMaxAuxStreams{-1}; +constexpr int32_t defaultBuilderOptimizationLevel{-1}; +constexpr int32_t defaultMaxTactics{-1}; + +// System default params +constexpr int32_t defaultDevice{0}; + +// Inference default params +constexpr int32_t defaultBatch{1}; +constexpr int32_t batchNotProvided{0}; +constexpr int32_t defaultStreams{1}; +constexpr int32_t defaultIterations{10}; +constexpr int32_t defaultOptProfileIndex{0}; +constexpr float defaultWarmUp{200.F}; +constexpr float defaultDuration{3.F}; +constexpr float defaultSleep{}; +constexpr float defaultIdle{}; +constexpr float defaultPersistentCacheRatio{0}; + +// Reporting default params +constexpr int32_t defaultAvgRuns{10}; +constexpr std::array defaultPercentiles{90, 95, 99}; + +enum class PrecisionConstraints +{ + kNONE, + kOBEY, + kPREFER +}; + +enum class ModelFormat +{ + kANY, + kONNX +}; + +enum class SparsityFlag +{ + kDISABLE, + kENABLE, + kFORCE +}; + +enum class TimingCacheMode +{ + kDISABLE, + kLOCAL, + kGLOBAL +}; + +enum class MemoryAllocationStrategy +{ + kSTATIC, //< Allocate device memory based on max size across all profiles. + kPROFILE, //< Allocate device memory based on max size of the current profile. + kRUNTIME, //< Allocate device memory based on the current input shapes. +}; + +//! +//! \enum RuntimeMode +//! +//! \brief Used to dictate which TensorRT runtime library to dynamically load. +//! +enum class RuntimeMode +{ + //! Maps to libnvinfer.so or nvinfer.dll + kFULL, + + //! Maps to libnvinfer_dispatch.so or nvinfer_dispatch.dll + kDISPATCH, + + //! Maps to libnvinfer_lean.so or nvinfer_lean.dll + kLEAN, +}; + +inline std::ostream& operator<<(std::ostream& os, RuntimeMode const mode) +{ + switch (mode) + { + case RuntimeMode::kFULL: + { + os << "full"; + break; + } + case RuntimeMode::kDISPATCH: + { + os << "dispatch"; + break; + } + case RuntimeMode::kLEAN: + { + os << "lean"; + break; + } + } + + return os; +} + +using Arguments = std::unordered_multimap>; + +using IOFormat = std::pair; + +using ShapeRange = std::array, nvinfer1::EnumMax()>; + +using LayerPrecisions = std::unordered_map; +using LayerOutputTypes = std::unordered_map>; +using LayerDeviceTypes = std::unordered_map; + +using StringSet = std::unordered_set; + +class WeightStreamingBudget +{ +public: + static constexpr int64_t kDISABLE{-2}; + static constexpr int64_t kAUTOMATIC{-1}; + int64_t bytes{kDISABLE}; + double percent{static_cast(100.0)}; + + bool isDisabled() + { + return bytes == kDISABLE && percent == kDISABLE; + } +}; + +class Options +{ +public: + virtual ~Options() = default; + virtual void parse(Arguments& arguments) = 0; +}; + +class BaseModelOptions : public Options +{ +public: + ModelFormat format{ModelFormat::kANY}; + std::string model; + + void parse(Arguments& arguments) override; + + static void help(std::ostream& out); +}; + +class ModelOptions : public Options +{ +public: + BaseModelOptions baseModel; + std::string prototxt; + std::vector outputs; + + void parse(Arguments& arguments) override; + + static void help(std::ostream& out); +}; + +constexpr nvinfer1::TempfileControlFlags getTempfileControlDefaults() +{ + using F = nvinfer1::TempfileControlFlag; + return (1U << static_cast(F::kALLOW_TEMPORARY_FILES)) + | (1U << static_cast(F::kALLOW_IN_MEMORY_FILES)); +} + +class BuildOptions : public Options +{ +public: + // Unit in MB. + double workspace{-1.0}; + // Unit in MB. + double dlaSRAM{-1.0}; + // Unit in MB. + double dlaLocalDRAM{-1.0}; + // Unit in MB. + double dlaGlobalDRAM{-1.0}; + // Unit in KB. + double tacticSharedMem{-1.0}; + int32_t avgTiming{defaultAvgTiming}; + size_t calibProfile{defaultOptProfileIndex}; + bool tf32{true}; + bool fp16{false}; + bool bf16{false}; + bool int8{false}; + bool fp8{false}; + bool int4{false}; + bool stronglyTyped{false}; + bool directIO{false}; + PrecisionConstraints precisionConstraints{PrecisionConstraints::kNONE}; + LayerPrecisions layerPrecisions; + LayerOutputTypes layerOutputTypes; + LayerDeviceTypes layerDeviceTypes; + StringSet debugTensors; + StringSet debugTensorStates; + bool safe{false}; + bool buildDLAStandalone{false}; + bool allowGPUFallback{false}; + bool restricted{false}; + bool skipInference{false}; + bool save{false}; + bool load{false}; + bool refittable{false}; + bool stripWeights{false}; + bool versionCompatible{false}; + bool pluginInstanceNorm{false}; + bool excludeLeanRuntime{false}; + bool disableCompilationCache{false}; + int32_t builderOptimizationLevel{defaultBuilderOptimizationLevel}; + int32_t maxTactics{defaultMaxTactics}; + SparsityFlag sparsity{SparsityFlag::kDISABLE}; + nvinfer1::ProfilingVerbosity profilingVerbosity{nvinfer1::ProfilingVerbosity::kLAYER_NAMES_ONLY}; + std::string engine; + std::string calibration; + using ShapeProfile = std::unordered_map; + std::vector optProfiles; + ShapeProfile shapesCalib; + std::vector inputFormats; + std::vector outputFormats; + nvinfer1::TacticSources enabledTactics{0}; + nvinfer1::TacticSources disabledTactics{0}; + TimingCacheMode timingCacheMode{TimingCacheMode::kLOCAL}; + std::string timingCacheFile{}; + bool errorOnTimingCacheMiss{false}; + // C++11 does not automatically generate hash function for enum class. + // Use int32_t to support C++11 compilers. + std::unordered_map previewFeatures; + nvinfer1::HardwareCompatibilityLevel hardwareCompatibilityLevel{nvinfer1::HardwareCompatibilityLevel::kNONE}; +#if (NV_TENSORRT_MAJOR > 8) + nvinfer1::RuntimePlatform runtimePlatform{nvinfer1::RuntimePlatform::kSAME_AS_BUILD}; +#endif + std::string tempdir{}; + nvinfer1::TempfileControlFlags tempfileControls{getTempfileControlDefaults()}; + RuntimeMode useRuntime{RuntimeMode::kFULL}; + std::string leanDLLPath{}; + int32_t maxAuxStreams{defaultMaxAuxStreams}; + bool getPlanVersionOnly{false}; + + bool allowWeightStreaming{false}; + + void parse(Arguments& arguments) override; + + static void help(std::ostream& out); +}; + +class SystemOptions : public Options +{ +public: + int32_t device{defaultDevice}; + int32_t DLACore{-1}; + bool ignoreParsedPluginLibs{false}; + std::vector plugins; + std::vector setPluginsToSerialize; + std::vector dynamicPlugins; + + void parse(Arguments& arguments) override; + + static void help(std::ostream& out); +}; + +class InferenceOptions : public Options +{ +public: + int32_t batch{batchNotProvided}; + int32_t iterations{defaultIterations}; + int32_t infStreams{defaultStreams}; + int32_t optProfileIndex{defaultOptProfileIndex}; + float warmup{defaultWarmUp}; + float duration{defaultDuration}; + float sleep{defaultSleep}; + float idle{defaultIdle}; + float persistentCacheRatio{defaultPersistentCacheRatio}; + bool overlap{true}; + bool skipTransfers{false}; + bool useManaged{false}; + bool spin{false}; + bool threads{false}; + bool graph{false}; + bool rerun{false}; + bool timeDeserialize{false}; + bool timeRefit{false}; + bool setOptProfile{false}; + std::unordered_map inputs; + using ShapeProfile = std::unordered_map>; + ShapeProfile shapes; + nvinfer1::ProfilingVerbosity nvtxVerbosity{nvinfer1::ProfilingVerbosity::kLAYER_NAMES_ONLY}; + MemoryAllocationStrategy memoryAllocationStrategy{MemoryAllocationStrategy::kSTATIC}; + std::unordered_map debugTensorFileNames; + + WeightStreamingBudget weightStreamingBudget; + + void parse(Arguments& arguments) override; + + static void help(std::ostream& out); +}; + +class ReportingOptions : public Options +{ +public: + bool verbose{false}; + int32_t avgs{defaultAvgRuns}; + std::vector percentiles{defaultPercentiles.begin(), defaultPercentiles.end()}; + bool refit{false}; + bool output{false}; + bool dumpRawBindings{false}; + bool profile{false}; + bool layerInfo{false}; + bool optProfileInfo{false}; + std::string exportTimes; + std::string exportOutput; + std::string exportProfile; + std::string exportLayerInfo; + + void parse(Arguments& arguments) override; + + static void help(std::ostream& out); +}; + +class SafeBuilderOptions : public Options +{ +public: + std::string serialized{}; + std::string onnxModelFile{}; + bool help{false}; + bool verbose{false}; + std::vector inputFormats; + std::vector outputFormats; + bool int8{false}; + bool fp8{false}; + bool int4{false}; + std::string calibFile{}; + std::vector plugins; + bool standard{false}; + TimingCacheMode timingCacheMode{TimingCacheMode::kLOCAL}; + std::string timingCacheFile{}; + SparsityFlag sparsity{SparsityFlag::kDISABLE}; + int32_t avgTiming{defaultAvgTiming}; + + void parse(Arguments& arguments) override; + + static void printHelp(std::ostream& out); +}; + +class AllOptions : public Options +{ +public: + ModelOptions model; + BuildOptions build; + SystemOptions system; + InferenceOptions inference; + ReportingOptions reporting; + bool helps{false}; + + void parse(Arguments& arguments) override; + + static void help(std::ostream& out); +}; + +class TaskInferenceOptions : public Options +{ +public: + std::string engine; + int32_t device{defaultDevice}; + int32_t DLACore{-1}; + int32_t batch{batchNotProvided}; + bool graph{false}; + float persistentCacheRatio{defaultPersistentCacheRatio}; + void parse(Arguments& arguments) override; + static void help(std::ostream& out); +}; + + +Arguments argsToArgumentsMap(int32_t argc, char* argv[]); + +bool parseHelp(Arguments& arguments); + +void helpHelp(std::ostream& out); + +// Functions to print options + +std::ostream& operator<<(std::ostream& os, const BaseModelOptions& options); + +std::ostream& operator<<(std::ostream& os, const IOFormat& format); + +std::ostream& operator<<(std::ostream& os, const ShapeRange& dims); + +std::ostream& operator<<(std::ostream& os, const ModelOptions& options); + +std::ostream& operator<<(std::ostream& os, const BuildOptions& options); + +std::ostream& operator<<(std::ostream& os, const SystemOptions& options); + +std::ostream& operator<<(std::ostream& os, const InferenceOptions& options); + +std::ostream& operator<<(std::ostream& os, const ReportingOptions& options); + +std::ostream& operator<<(std::ostream& os, const AllOptions& options); + +std::ostream& operator<<(std::ostream& os, const SafeBuilderOptions& options); + +std::ostream& operator<<(std::ostream& os, nvinfer1::DataType dtype); + +std::ostream& operator<<(std::ostream& os, nvinfer1::DeviceType devType); + +inline std::ostream& operator<<(std::ostream& os, const nvinfer1::Dims& dims) +{ + for (int32_t i = 0; i < dims.nbDims; ++i) + { + os << (i ? "x" : "") << dims.d[i]; + } + return os; +} +inline std::ostream& operator<<(std::ostream& os, const nvinfer1::WeightsRole role) +{ + switch (role) + { + case nvinfer1::WeightsRole::kKERNEL: + { + os << "Kernel"; + break; + } + case nvinfer1::WeightsRole::kBIAS: + { + os << "Bias"; + break; + } + case nvinfer1::WeightsRole::kSHIFT: + { + os << "Shift"; + break; + } + case nvinfer1::WeightsRole::kSCALE: + { + os << "Scale"; + break; + } + case nvinfer1::WeightsRole::kCONSTANT: + { + os << "Constant"; + break; + } + case nvinfer1::WeightsRole::kANY: + { + os << "Any"; + break; + } + } + + return os; +} + +inline std::ostream& operator<<(std::ostream& os, const std::vector& vec) +{ + for (int32_t i = 0, e = static_cast(vec.size()); i < e; ++i) + { + os << (i ? "x" : "") << vec[i]; + } + return os; +} + +} // namespace sample + +#endif // TRT_SAMPLES_OPTIONS_H diff --git a/src/Detector/tensorrt_onnx/common/sampleReporting.cpp b/src/Detector/tensorrt_onnx/common/sampleReporting.cpp new file mode 100644 index 000000000..e9dda6e0a --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleReporting.cpp @@ -0,0 +1,609 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "sampleInference.h" +#include "sampleOptions.h" +#include "sampleReporting.h" + +using namespace nvinfer1; + +namespace sample +{ + +namespace +{ + +//! +//! \brief Find percentile in an ascending sequence of timings +//! \note percentile must be in [0, 100]. Otherwise, an exception is thrown. +//! +template +float findPercentile(float percentile, std::vector const& timings, T const& toFloat) +{ + int32_t const all = static_cast(timings.size()); + int32_t const exclude = static_cast((1 - percentile / 100) * all); + if (timings.empty()) + { + return std::numeric_limits::infinity(); + } + if (percentile < 0.F || percentile > 100.F) + { + throw std::runtime_error("percentile is not in [0, 100]!"); + } + return toFloat(timings[std::max(all - 1 - exclude, 0)]); +} + +//! +//! \brief Find median in a sorted sequence of timings +//! +template +float findMedian(std::vector const& timings, T const& toFloat) +{ + if (timings.empty()) + { + return std::numeric_limits::infinity(); + } + + int32_t const m = timings.size() / 2; + if (timings.size() % 2) + { + return toFloat(timings[m]); + } + + return (toFloat(timings[m - 1]) + toFloat(timings[m])) / 2; +} + +//! +//! \brief Find coefficient of variance (which is std / mean) in a sorted sequence of timings given the mean +//! +template +float findCoeffOfVariance(std::vector const& timings, T const& toFloat, float mean) +{ + if (timings.empty()) + { + return 0; + } + + if (mean == 0.F) + { + return std::numeric_limits::infinity(); + } + + auto const metricAccumulator = [toFloat, mean](float acc, InferenceTime const& a) { + float const diff = toFloat(a) - mean; + return acc + diff * diff; + }; + float const variance = std::accumulate(timings.begin(), timings.end(), 0.F, metricAccumulator) / timings.size(); + + return std::sqrt(variance) / mean * 100.F; +} + +inline InferenceTime traceToTiming(const InferenceTrace& a) +{ + return InferenceTime( + (a.enqEnd - a.enqStart), (a.h2dEnd - a.h2dStart), (a.computeEnd - a.computeStart), (a.d2hEnd - a.d2hStart)); +} + +inline std::string dimsToString(Dims const& shape) +{ + std::stringstream ss; + + if (shape.nbDims == 0) + { + ss << "scalar"; + } + else + { + for (int32_t i = 0; i < shape.nbDims; i++) + { + ss << shape.d[i] << (i != shape.nbDims - 1 ? "x" : ""); + } + } + return ss.str(); +} + +} // namespace + +void printProlog(int32_t warmups, int32_t timings, float warmupMs, float benchTimeMs, std::ostream& os) +{ + os << "Warmup completed " << warmups << " queries over " << warmupMs << " ms" << std::endl; + os << "Timing trace has " << timings << " queries over " << benchTimeMs / 1000 << " s" << std::endl; +} + +void printTiming(std::vector const& timings, int32_t runsPerAvg, std::ostream& os) +{ + int64_t count = 0; + InferenceTime sum; + + os << std::endl; + os << "=== Trace details ===" << std::endl; + os << "Trace averages of " << runsPerAvg << " runs:" << std::endl; + + // Show only the first N lines and the last N lines, where N = kTIMING_PRINT_THRESHOLD. + constexpr int64_t kTIMING_PRINT_THRESHOLD{200}; + int64_t const maxNbTimings{kTIMING_PRINT_THRESHOLD * runsPerAvg}; + + for (int64_t idx = 0, size = timings.size(); idx < size; ++idx) + { + // Omit some latency printing to avoid very long logs. + if (size > 2 * maxNbTimings && idx == maxNbTimings) + { + os << "... Omitting " << (size - 2 * maxNbTimings) << " lines" << std::endl; + idx = size - kTIMING_PRINT_THRESHOLD * runsPerAvg - 1; + } + + sum += timings[idx]; + + if (++count == runsPerAvg) + { + // clang-format off + os << "Average on " << runsPerAvg << " runs - GPU latency: " << sum.compute / runsPerAvg + << " ms - Host latency: " << sum.latency() / runsPerAvg << " ms (enqueue " << sum.enq / runsPerAvg + << " ms)" << std::endl; + // clang-format on + count = 0; + sum.enq = 0; + sum.h2d = 0; + sum.compute = 0; + sum.d2h = 0; + } + } +} + +void printMetricExplanations(std::ostream& os) +{ + os << std::endl; + os << "=== Explanations of the performance metrics ===" << std::endl; + os << "Total Host Walltime: the host walltime from when the first query (after warmups) is enqueued to when the " + "last query is completed." + << std::endl; + os << "GPU Compute Time: the GPU latency to execute the kernels for a query." << std::endl; + os << "Total GPU Compute Time: the summation of the GPU Compute Time of all the queries. If this is significantly " + "shorter than Total Host Walltime, the GPU may be under-utilized because of host-side overheads or data " + "transfers." + << std::endl; + os << "Throughput: the observed throughput computed by dividing the number of queries by the Total Host Walltime. " + "If this is significantly lower than the reciprocal of GPU Compute Time, the GPU may be under-utilized " + "because of host-side overheads or data transfers." + << std::endl; + os << "Enqueue Time: the host latency to enqueue a query. If this is longer than GPU Compute Time, the GPU may be " + "under-utilized." + << std::endl; + os << "H2D Latency: the latency for host-to-device data transfers for input tensors of a single query." + << std::endl; + os << "D2H Latency: the latency for device-to-host data transfers for output tensors of a single query." + << std::endl; + os << "Latency: the summation of H2D Latency, GPU Compute Time, and D2H Latency. This is the latency to infer a " + "single query." + << std::endl; +} + +PerformanceResult getPerformanceResult(std::vector const& timings, + std::function metricGetter, std::vector const& percentiles) +{ + auto const metricComparator + = [metricGetter](InferenceTime const& a, InferenceTime const& b) { return metricGetter(a) < metricGetter(b); }; + auto const metricAccumulator = [metricGetter](float acc, InferenceTime const& a) { return acc + metricGetter(a); }; + std::vector newTimings = timings; + std::sort(newTimings.begin(), newTimings.end(), metricComparator); + PerformanceResult result; + result.min = metricGetter(newTimings.front()); + result.max = metricGetter(newTimings.back()); + result.mean = std::accumulate(newTimings.begin(), newTimings.end(), 0.0F, metricAccumulator) / newTimings.size(); + result.median = findMedian(newTimings, metricGetter); + for (auto percentile : percentiles) + { + result.percentiles.emplace_back(findPercentile(percentile, newTimings, metricGetter)); + } + result.coeffVar = findCoeffOfVariance(newTimings, metricGetter, result.mean); + return result; +} + +void printEpilog(std::vector const& timings, float walltimeMs, std::vector const& percentiles, + int32_t batchSize, int32_t infStreams, std::ostream& osInfo, std::ostream& osWarning, std::ostream& osVerbose) +{ + float const throughput = batchSize * timings.size() / walltimeMs * 1000; + + auto const getLatency = [](InferenceTime const& t) { return t.latency(); }; + auto const latencyResult = getPerformanceResult(timings, getLatency, percentiles); + + auto const getEnqueue = [](InferenceTime const& t) { return t.enq; }; + auto const enqueueResult = getPerformanceResult(timings, getEnqueue, percentiles); + + auto const getH2d = [](InferenceTime const& t) { return t.h2d; }; + auto const h2dResult = getPerformanceResult(timings, getH2d, percentiles); + + auto const getCompute = [](InferenceTime const& t) { return t.compute; }; + auto const gpuComputeResult = getPerformanceResult(timings, getCompute, percentiles); + + auto const getD2h = [](InferenceTime const& t) { return t.d2h; }; + auto const d2hResult = getPerformanceResult(timings, getD2h, percentiles); + + auto const toPerfString = [&](const PerformanceResult& r) { + std::stringstream s; + s << "min = " << r.min << " ms, max = " << r.max << " ms, mean = " << r.mean << " ms, " + << "median = " << r.median << " ms"; + for (int32_t i = 0, n = percentiles.size(); i < n; ++i) + { + s << ", percentile(" << percentiles[i] << "%) = " << r.percentiles[i] << " ms"; + } + return s.str(); + }; + + osInfo << std::endl; + osInfo << "=== Performance summary ===" << std::endl; + osInfo << "Throughput: " << throughput << " qps" << std::endl; + osInfo << "Latency: " << toPerfString(latencyResult) << std::endl; + osInfo << "Enqueue Time: " << toPerfString(enqueueResult) << std::endl; + osInfo << "H2D Latency: " << toPerfString(h2dResult) << std::endl; + osInfo << "GPU Compute Time: " << toPerfString(gpuComputeResult) << std::endl; + osInfo << "D2H Latency: " << toPerfString(d2hResult) << std::endl; + osInfo << "Total Host Walltime: " << walltimeMs / 1000 << " s" << std::endl; + osInfo << "Total GPU Compute Time: " << gpuComputeResult.mean * timings.size() / 1000 << " s" << std::endl; + + // Report warnings if the throughput is bound by other factors than GPU Compute Time. + constexpr float kENQUEUE_BOUND_REPORTING_THRESHOLD{0.8F}; + if (enqueueResult.median > kENQUEUE_BOUND_REPORTING_THRESHOLD * gpuComputeResult.median) + { + osWarning + << "* Throughput may be bound by Enqueue Time rather than GPU Compute and the GPU may be under-utilized." + << std::endl; + osWarning << " If not already in use, --useCudaGraph (utilize CUDA graphs where possible) may increase the " + "throughput." + << std::endl; + } + if (h2dResult.median >= gpuComputeResult.median) + { + osWarning << "* Throughput may be bound by host-to-device transfers for the inputs rather than GPU Compute and " + "the GPU may be under-utilized." + << std::endl; + osWarning << " Add --noDataTransfers flag to disable data transfers." << std::endl; + } + if (d2hResult.median >= gpuComputeResult.median) + { + osWarning << "* Throughput may be bound by device-to-host transfers for the outputs rather than GPU Compute " + "and the GPU may be under-utilized." + << std::endl; + osWarning << " Add --noDataTransfers flag to disable data transfers." << std::endl; + } + + // Report warnings if the GPU Compute Time is unstable. + constexpr float kUNSTABLE_PERF_REPORTING_THRESHOLD{1.0F}; + if (gpuComputeResult.coeffVar > kUNSTABLE_PERF_REPORTING_THRESHOLD) + { + osWarning << "* GPU compute time is unstable, with coefficient of variance = " << gpuComputeResult.coeffVar + << "%." << std::endl; + osWarning << " If not already in use, locking GPU clock frequency or adding --useSpinWait may improve the " + << "stability." << std::endl; + } + + // Report warnings if multiple inference streams are used. + if (infStreams > 1) + { + osWarning << "* Multiple inference streams are used. Latencies may not be accurate since inferences may run in " + << " parallel. Please use \"Throughput\" as the performance metric instead." << std::endl; + } + + // Explain what the metrics mean. + osInfo << "Explanations of the performance metrics are printed in the verbose logs." << std::endl; + printMetricExplanations(osVerbose); + + osInfo << std::endl; +} + +void printPerformanceReport(std::vector const& trace, ReportingOptions const& reportingOpts, + InferenceOptions const& infOpts, std::ostream& osInfo, std::ostream& osWarning, std::ostream& osVerbose) +{ + int32_t batchSize = infOpts.batch; + float const warmupMs = infOpts.warmup; + auto const isNotWarmup = [&warmupMs](const InferenceTrace& a) { return a.computeStart >= warmupMs; }; + auto const noWarmup = std::find_if(trace.begin(), trace.end(), isNotWarmup); + int32_t const warmups = noWarmup - trace.begin(); + float const benchTime = trace.back().d2hEnd - noWarmup->h2dStart; + // treat inference with explicit batch as a single query and report the throughput + batchSize = batchSize ? batchSize : 1; + printProlog(warmups * batchSize, (trace.size() - warmups) * batchSize, warmupMs, benchTime, osInfo); + + std::vector timings(trace.size() - warmups); + std::transform(noWarmup, trace.end(), timings.begin(), traceToTiming); + printTiming(timings, reportingOpts.avgs, osInfo); + printEpilog( + timings, benchTime, reportingOpts.percentiles, batchSize, infOpts.infStreams, osInfo, osWarning, osVerbose); + + if (!reportingOpts.exportTimes.empty()) + { + exportJSONTrace(trace, reportingOpts.exportTimes, warmups); + } +} + +//! Printed format: +//! [ value, ...] +//! value ::= { "start enq : time, "end enq" : time, "start h2d" : time, "end h2d" : time, "start compute" : time, +//! "end compute" : time, "start d2h" : time, "end d2h" : time, "h2d" : time, "compute" : time, +//! "d2h" : time, "latency" : time } +//! +void exportJSONTrace(std::vector const& trace, std::string const& fileName, int32_t const nbWarmups) +{ + std::ofstream os(fileName, std::ofstream::trunc); + os << "[" << std::endl; + char const* sep = " "; + for (auto iter = trace.begin() + nbWarmups; iter < trace.end(); ++iter) + { + auto const& t = *iter; + InferenceTime const it(traceToTiming(t)); + os << sep << "{ "; + sep = ", "; + // clang-format off + os << "\"startEnqMs\" : " << t.enqStart << sep << "\"endEnqMs\" : " << t.enqEnd << sep + << "\"startH2dMs\" : " << t.h2dStart << sep << "\"endH2dMs\" : " << t.h2dEnd << sep + << "\"startComputeMs\" : " << t.computeStart << sep << "\"endComputeMs\" : " << t.computeEnd << sep + << "\"startD2hMs\" : " << t.d2hStart << sep << "\"endD2hMs\" : " << t.d2hEnd << sep + << "\"h2dMs\" : " << it.h2d << sep << "\"computeMs\" : " << it.compute << sep + << "\"d2hMs\" : " << it.d2h << sep << "\"latencyMs\" : " << it.latency() << " }" + << std::endl; + // clang-format on + } + os << "]" << std::endl; +} + +void Profiler::reportLayerTime(char const* layerName, float timeMs) noexcept +{ + if (mIterator == mLayers.end()) + { + bool const first = !mLayers.empty() && mLayers.begin()->name == layerName; + mUpdatesCount += mLayers.empty() || first; + if (first) + { + mIterator = mLayers.begin(); + } + else + { + mLayers.emplace_back(); + mLayers.back().name = layerName; + mIterator = mLayers.end() - 1; + } + } + + mIterator->timeMs.push_back(timeMs); + ++mIterator; +} + +void Profiler::print(std::ostream& os) const noexcept +{ + std::string const nameHdr(" Layer"); + std::string const timeHdr(" Time(ms)"); + std::string const avgHdr(" Avg.(ms)"); + std::string const medHdr(" Median(ms)"); + std::string const percentageHdr(" Time(%)"); + + float const totalTimeMs = getTotalTime(); + + auto const timeLength = timeHdr.size(); + auto const avgLength = avgHdr.size(); + auto const medLength = medHdr.size(); + auto const percentageLength = percentageHdr.size(); + + os << std::endl + << "=== Profile (" << mUpdatesCount << " iterations ) ===" << std::endl + << timeHdr << avgHdr << medHdr << percentageHdr << nameHdr << std::endl; + + for (auto const& p : mLayers) + { + if (p.timeMs.empty() || getTotalTime(p) == 0.F) + { + // there is no point to print profiling for layer that didn't run at all + continue; + } + // clang-format off + os << std::setw(timeLength) << std::fixed << std::setprecision(2) << getTotalTime(p) + << std::setw(avgLength) << std::fixed << std::setprecision(4) << getAvgTime(p) + << std::setw(medLength) << std::fixed << std::setprecision(4) << getMedianTime(p) + << std::setw(percentageLength) << std::fixed << std::setprecision(1) << getTotalTime(p) / totalTimeMs * 100 + << " " << p.name << std::endl; + } + { + os << std::setw(timeLength) << std::fixed << std::setprecision(2) + << totalTimeMs << std::setw(avgLength) << std::fixed << std::setprecision(4) << totalTimeMs / mUpdatesCount + << std::setw(medLength) << std::fixed << std::setprecision(4) << getMedianTime() + << std::setw(percentageLength) << std::fixed << std::setprecision(1) << 100.0 + << " Total" << std::endl; + // clang-format on + } + os << std::endl; +} + +void Profiler::exportJSONProfile(std::string const& fileName) const noexcept +{ + std::ofstream os(fileName, std::ofstream::trunc); + os << "[" << std::endl << " { \"count\" : " << mUpdatesCount << " }" << std::endl; + + auto const totalTimeMs = getTotalTime(); + + for (auto const& l : mLayers) + { + // clang-format off + os << ", {" << R"( "name" : ")" << l.name << R"(")" + R"(, "timeMs" : )" << getTotalTime(l) + << R"(, "averageMs" : )" << getAvgTime(l) + << R"(, "medianMs" : )" << getMedianTime(l) + << R"(, "percentage" : )" << getTotalTime(l) / totalTimeMs * 100 + << " }" << std::endl; + // clang-format on + } + os << "]" << std::endl; +} + +void dumpInputs(nvinfer1::IExecutionContext const& context, Bindings const& bindings, std::ostream& os) +{ + os << "Input Tensors:" << std::endl; + bindings.dumpInputs(context, os); +} + +void dumpOutputs(nvinfer1::IExecutionContext const& context, Bindings const& bindings, std::ostream& os) +{ + auto isOutput = [](Binding const& b) { return !b.isInput; }; + bindings.dumpBindings(context, isOutput, os); +} + +void dumpRawBindingsToFiles(nvinfer1::IExecutionContext const& context, Bindings const& bindings, std::ostream& os) +{ + bindings.dumpRawBindingToFiles(context, os); +} + +void exportJSONOutput( + nvinfer1::IExecutionContext const& context, Bindings const& bindings, std::string const& fileName, int32_t batch) +{ + std::ofstream os(fileName, std::ofstream::trunc); + std::string sep = " "; + auto const output = bindings.getOutputBindings(); + os << "[" << std::endl; + for (auto const& binding : output) + { + // clang-format off + os << sep << R"({ "name" : ")" << binding.first << "\"" << std::endl; + sep = ", "; + os << " " << sep << R"("dimensions" : ")"; + bindings.dumpBindingDimensions(binding.first, context, os); + os << "\"" << std::endl; + os << " " << sep << "\"values\" : [ "; + bindings.dumpBindingValues(context, binding.second, os, sep, batch); + os << " ]" << std::endl << " }" << std::endl; + // clang-format on + } + os << "]" << std::endl; +} + +void exportJSONOutput( + nvinfer1::IExecutionContext const& context, Bindings const& bindings, std::string const& fileName, int32_t batch); + +void printLayerInfo( + ReportingOptions const& reporting, nvinfer1::ICudaEngine* engine, nvinfer1::IExecutionContext* context) +{ + if (reporting.layerInfo) + { + sample::gLogInfo << "Layer Information:" << std::endl; + sample::gLogInfo << getLayerInformation(engine, context, nvinfer1::LayerInformationFormat::kONELINE) + << std::flush; + } + if (!reporting.exportLayerInfo.empty()) + { + std::ofstream os(reporting.exportLayerInfo, std::ofstream::trunc); + os << getLayerInformation(engine, context, nvinfer1::LayerInformationFormat::kJSON) << std::flush; + } +} + +void printOptimizationProfileInfo(ReportingOptions const& reporting, nvinfer1::ICudaEngine const* engine) +{ + if (reporting.optProfileInfo) + { + sample::gLogInfo << "Optimization Profile Information:" << std::endl; + for (int32_t i = 0; i < engine->getNbOptimizationProfiles(); i++) + { + for (int32_t j = 0, e = engine->getNbIOTensors(); j < e; j++) + { + auto const tensorName = engine->getIOTensorName(j); + + if (engine->getTensorIOMode(tensorName) == nvinfer1::TensorIOMode::kINPUT) + { + auto tensorMinShape = engine->getProfileShape(tensorName, i, nvinfer1::OptProfileSelector::kMIN); + auto tensorOptShape = engine->getProfileShape(tensorName, i, nvinfer1::OptProfileSelector::kOPT); + auto tensorMaxShape = engine->getProfileShape(tensorName, i, nvinfer1::OptProfileSelector::kMAX); + + sample::gLogInfo << "Model input " << tensorName << " (profile " << i << "): " + << "min=" << dimsToString(tensorMinShape) + << ", opt=" << dimsToString(tensorOptShape) + << ", max=" << dimsToString(tensorMaxShape) << std::endl; + } + } + } + } +} + +void printPerformanceProfile(ReportingOptions const& reporting, InferenceEnvironment& iEnv) +{ + if (reporting.profile) + { + iEnv.profiler->print(sample::gLogInfo); + } + if (!reporting.exportProfile.empty()) + { + iEnv.profiler->exportJSONProfile(reporting.exportProfile); + } + + // Print an warning about total per-layer latency when auxiliary streams are used. + if (!iEnv.safe && (reporting.profile || !reporting.exportProfile.empty())) + { + int32_t const nbAuxStreams = iEnv.engine.get()->getNbAuxStreams(); + if (nbAuxStreams > 0) + { + sample::gLogWarning << "The engine uses " << nbAuxStreams << " auxiliary streams, so the \"Total\" latency " + << "may not be accurate because some layers may have run in parallel!" << std::endl; + } + } +} + +namespace details +{ +void dump(std::unique_ptr const& context, std::unique_ptr const& binding, + ReportingOptions const& reporting, int32_t batch) +{ + if (!context) + { + sample::gLogError << "Empty context! Skip printing outputs." << std::endl; + return; + } + if (reporting.output) + { + dumpOutputs(*context, *binding, sample::gLogInfo); + } + if (reporting.dumpRawBindings) + { + dumpRawBindingsToFiles(*context, *binding, sample::gLogInfo); + } + if (!reporting.exportOutput.empty()) + { + exportJSONOutput(*context, *binding, reporting.exportOutput, batch); + } +} +} // namespace details + +void printOutput(ReportingOptions const& reporting, InferenceEnvironment const& iEnv, int32_t batch) +{ + auto const& binding = iEnv.bindings.at(0); + if (!binding) + { + sample::gLogError << "Empty bindings! Skip printing outputs." << std::endl; + return; + } + if (iEnv.safe) + { + sample::gLogError << "Safe inferernce is not supported!" << std::endl; + return; + } + auto const& context = iEnv.contexts.at(0); + details::dump(context, binding, reporting, batch); +} + +} // namespace sample diff --git a/src/Detector/tensorrt_onnx/common/sampleReporting.h b/src/Detector/tensorrt_onnx/common/sampleReporting.h new file mode 100644 index 000000000..922ef3c8b --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleReporting.h @@ -0,0 +1,298 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef TRT_SAMPLE_REPORTING_H +#define TRT_SAMPLE_REPORTING_H + +#include +#include +#include + +#include "sampleOptions.h" + +namespace sample +{ + +class Bindings; + +//! +//! \struct InferenceTime +//! \brief Measurement times in milliseconds +//! +struct InferenceTime +{ + InferenceTime(float q, float i, float c, float o) + : enq(q) + , h2d(i) + , compute(c) + , d2h(o) + { + } + + InferenceTime() = default; + InferenceTime(InferenceTime const&) = default; + InferenceTime(InferenceTime&&) = default; + InferenceTime& operator=(InferenceTime const&) = default; + InferenceTime& operator=(InferenceTime&&) = default; + ~InferenceTime() = default; + + float enq{0}; // Enqueue + float h2d{0}; // Host to Device + float compute{0}; // Compute + float d2h{0}; // Device to Host + + // ideal latency + float latency() const + { + return h2d + compute + d2h; + } +}; + +//! +//! \struct InferenceTrace +//! \brief Measurement points in milliseconds +//! +struct InferenceTrace +{ + InferenceTrace(int32_t s, float es, float ee, float is, float ie, float cs, float ce, float os, float oe) + : stream(s) + , enqStart(es) + , enqEnd(ee) + , h2dStart(is) + , h2dEnd(ie) + , computeStart(cs) + , computeEnd(ce) + , d2hStart(os) + , d2hEnd(oe) + { + } + + InferenceTrace() = default; + InferenceTrace(InferenceTrace const&) = default; + InferenceTrace(InferenceTrace&&) = default; + InferenceTrace& operator=(InferenceTrace const&) = default; + InferenceTrace& operator=(InferenceTrace&&) = default; + ~InferenceTrace() = default; + + int32_t stream{0}; + float enqStart{0}; + float enqEnd{0}; + float h2dStart{0}; + float h2dEnd{0}; + float computeStart{0}; + float computeEnd{0}; + float d2hStart{0}; + float d2hEnd{0}; +}; + +inline InferenceTime operator+(InferenceTime const& a, InferenceTime const& b) +{ + return InferenceTime(a.enq + b.enq, a.h2d + b.h2d, a.compute + b.compute, a.d2h + b.d2h); +} + +inline InferenceTime operator+=(InferenceTime& a, InferenceTime const& b) +{ + return a = a + b; +} + +//! +//! \struct PerformanceResult +//! \brief Performance result of a performance metric +//! +struct PerformanceResult +{ + float min{0.F}; + float max{0.F}; + float mean{0.F}; + float median{0.F}; + std::vector percentiles; + float coeffVar{0.F}; // coefficient of variation +}; + +//! +//! \brief Print benchmarking time and number of traces collected +//! +void printProlog(int32_t warmups, int32_t timings, float warmupMs, float walltime, std::ostream& os); + +//! +//! \brief Print a timing trace +//! +void printTiming(std::vector const& timings, int32_t runsPerAvg, std::ostream& os); + +//! +//! \brief Print the performance summary of a trace +//! +void printEpilog(std::vector const& timings, std::vector const& percentiles, int32_t batchSize, + std::ostream& osInfo, std::ostream& osWarning, std::ostream& osVerbose); + +//! +//! \brief Get the result of a specific performance metric from a trace +//! +PerformanceResult getPerformanceResult(std::vector const& timings, + std::function metricGetter, std::vector const& percentiles); + +//! +//! \brief Print the explanations of the performance metrics printed in printEpilog() function. +//! +void printMetricExplanations(std::ostream& os); + +//! +//! \brief Print and summarize a timing trace +//! +void printPerformanceReport(std::vector const& trace, ReportingOptions const& reportingOpts, + InferenceOptions const& infOpts, std::ostream& osInfo, std::ostream& osWarning, std::ostream& osVerbose); + +//! +//! \brief Export a timing trace to JSON file +//! +void exportJSONTrace( + std::vector const& InferenceTime, std::string const& fileName, int32_t const nbWarmups); + +//! +//! \brief Print input tensors to stream +//! +void dumpInputs(nvinfer1::IExecutionContext const& context, Bindings const& bindings, std::ostream& os); + +//! +//! \brief Print output tensors to stream +//! +void dumpOutputs(nvinfer1::IExecutionContext const& context, Bindings const& bindings, std::ostream& os); + +void dumpRawBindingsToFiles(nvinfer1::IExecutionContext const& context, Bindings const& bindings, std::ostream& os); + +//! +//! \brief Export output tensors to JSON file +//! +void exportJSONOutput( + nvinfer1::IExecutionContext const& context, Bindings const& bindings, std::string const& fileName, int32_t batch); + +//! +//! \struct LayerProfile +//! \brief Layer profile information +//! +struct LayerProfile +{ + std::string name; + std::vector timeMs; +}; + +//! +//! \class Profiler +//! \brief Collect per-layer profile information, assuming times are reported in the same order +//! +class Profiler : public nvinfer1::IProfiler +{ + +public: + void reportLayerTime(char const* layerName, float timeMs) noexcept override; + + void print(std::ostream& os) const noexcept; + + //! + //! \brief Export a profile to JSON file + //! + void exportJSONProfile(std::string const& fileName) const noexcept; + +private: + float getTotalTime() const noexcept + { + auto const plusLayerTime = [](float accumulator, LayerProfile const& lp) { + return accumulator + std::accumulate(lp.timeMs.begin(), lp.timeMs.end(), 0.F, std::plus()); + }; + return std::accumulate(mLayers.begin(), mLayers.end(), 0.0F, plusLayerTime); + } + + float getMedianTime() const noexcept + { + if (mLayers.empty()) + { + return 0.F; + } + std::vector totalTime; + for (size_t run = 0; run < mLayers[0].timeMs.size(); ++run) + { + auto const layerTime + = [&run](float accumulator, LayerProfile const& lp) { return accumulator + lp.timeMs[run]; }; + auto t = std::accumulate(mLayers.begin(), mLayers.end(), 0.F, layerTime); + totalTime.push_back(t); + } + return median(totalTime); + } + + float getMedianTime(LayerProfile const& p) const noexcept + { + return median(p.timeMs); + } + + static float median(std::vector vals) + { + if (vals.empty()) + { + return 0.F; + } + std::sort(vals.begin(), vals.end()); + if (vals.size() % 2U == 1U) + { + return vals[vals.size() / 2U]; + } + return (vals[vals.size() / 2U - 1U] + vals[vals.size() / 2U]) * 0.5F; + } + + //! return the total runtime of given layer profile + float getTotalTime(LayerProfile const& p) const noexcept + { + auto const& vals = p.timeMs; + return std::accumulate(vals.begin(), vals.end(), 0.F, std::plus()); + } + + float getAvgTime(LayerProfile const& p) const noexcept + { + return getTotalTime(p) / p.timeMs.size(); + } + + std::vector mLayers; + std::vector::iterator mIterator{mLayers.begin()}; + int32_t mUpdatesCount{0}; +}; + +//! +//! \brief Print layer info to logger or export it to output JSON file. +//! +void printLayerInfo( + ReportingOptions const& reporting, nvinfer1::ICudaEngine* engine, nvinfer1::IExecutionContext* context); + +//! +//! \brief Print optimization profile info to logger. +//! +void printOptimizationProfileInfo(ReportingOptions const& reporting, nvinfer1::ICudaEngine const* engine); + +//! Forward declaration. +struct InferenceEnvironment; + +//! +//! \brief Print per-layer perf profile data to logger or export it to output JSON file. +//! +void printPerformanceProfile(ReportingOptions const& reporting, InferenceEnvironment& iEnv); + +//! +//! \brief Print binding output values to logger or export them to output JSON file. +//! +void printOutput(ReportingOptions const& reporting, InferenceEnvironment const& iEnv, int32_t batch); + +} // namespace sample + +#endif // TRT_SAMPLE_REPORTING_H diff --git a/src/Detector/tensorrt_onnx/common/sampleUtils.cpp b/src/Detector/tensorrt_onnx/common/sampleUtils.cpp new file mode 100644 index 000000000..8f172afe3 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleUtils.cpp @@ -0,0 +1,599 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#include "sampleUtils.h" +#include "bfloat16.h" +#include "half.h" + +using namespace nvinfer1; + +namespace sample +{ + +size_t dataTypeSize(nvinfer1::DataType dataType) +{ + switch (dataType) + { +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kINT64: return 8U; +#endif + case nvinfer1::DataType::kINT32: + case nvinfer1::DataType::kFLOAT: return 4U; +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kBF16: +#endif + case nvinfer1::DataType::kHALF: return 2U; + case nvinfer1::DataType::kBOOL: + case nvinfer1::DataType::kUINT8: + case nvinfer1::DataType::kINT8: + case nvinfer1::DataType::kFP8: return 1U; +#if (NV_TENSORRT_MAJOR > 8) + case nvinfer1::DataType::kINT4: + ASSERT(false && "Element size is not implemented for sub-byte data-types."); +#endif + } + return 0; +} + +int64_t volume(nvinfer1::Dims const& dims, nvinfer1::Dims const& strides, int32_t vecDim, int32_t comps, int32_t batch) +{ + int64_t maxNbElems = 1; + for (int32_t i = 0; i < dims.nbDims; ++i) + { + // Get effective length of axis. + int64_t d = dims.d[i]; + // Any dimension is 0, it is an empty tensor. + if (d == 0) + { + return 0; + } + if (i == vecDim) + { + d = samplesCommon::divUp(d, comps); + } + maxNbElems = std::max(maxNbElems, d * strides.d[i]); + } + return maxNbElems * batch * (vecDim < 0 ? 1 : comps); +} + +nvinfer1::Dims toDims(std::vector const& vec) +{ + int32_t limit = static_cast(nvinfer1::Dims::MAX_DIMS); + if (static_cast(vec.size()) > limit) + { + sample::gLogWarning << "Vector too long, only first 8 elements are used in dimension." << std::endl; + } + // Pick first nvinfer1::Dims::MAX_DIMS elements + nvinfer1::Dims dims{std::min(static_cast(vec.size()), limit), {}}; + std::copy_n(vec.begin(), dims.nbDims, std::begin(dims.d)); + return dims; +} + +void loadFromFile(std::string const& fileName, char* dst, size_t size) +{ + ASSERT(dst); + + std::ifstream file(fileName, std::ios::in | std::ios::binary); + if (file.is_open()) + { + file.seekg(0, std::ios::end); + int64_t fileSize = static_cast(file.tellg()); + // Due to change from int32_t to int64_t VC engines created with earlier versions + // may expect input of the half of the size + if (fileSize != static_cast(size) && fileSize != static_cast(size * 2)) + { + std::ostringstream msg; + msg << "Unexpected file size for input file: " << fileName << ". Note: Input binding size is: " << size + << " bytes but the file size is " << fileSize + << " bytes. Double check the size and datatype of the provided data."; + throw std::invalid_argument(msg.str()); + } + // Move file pointer back to the beginning after reading file size. + file.seekg(0, std::ios::beg); + file.read(dst, size); + size_t const nbBytesRead = file.gcount(); + file.close(); + if (nbBytesRead != size) + { + std::ostringstream msg; + msg << "Unexpected file size for input file: " << fileName << ". Note: Expected: " << size + << " bytes but only read: " << nbBytesRead << " bytes"; + throw std::invalid_argument(msg.str()); + } + } + else + { + std::ostringstream msg; + msg << "Cannot open file " << fileName << "!"; + throw std::invalid_argument(msg.str()); + } +} + +std::vector splitToStringVec(std::string const& s, char separator, int64_t maxSplit) +{ + std::vector splitted; + + for (size_t start = 0; start < s.length();) + { + // If maxSplit is specified and we have reached maxSplit, emplace back the rest of the string and break the + // loop. + if (maxSplit >= 0 && static_cast(splitted.size()) == maxSplit) + { + splitted.emplace_back(s.substr(start, s.length() - start)); + break; + } + + size_t separatorIndex = s.find(separator, start); + if (separatorIndex == std::string::npos) + { + separatorIndex = s.length(); + } + splitted.emplace_back(s.substr(start, separatorIndex - start)); + + // If the separator is the last character, then we should push an empty string at the end. + if (separatorIndex == s.length() - 1) + { + splitted.emplace_back(""); + } + + start = separatorIndex + 1; + } + + return splitted; +} + +bool broadcastIOFormats(std::vector const& formats, size_t nbBindings, bool isInput /*= true*/) +{ + bool broadcast = formats.size() == 1; + bool validFormatsCount = broadcast || (formats.size() == nbBindings); + if (!formats.empty() && !validFormatsCount) + { + if (isInput) + { + throw std::invalid_argument( + "The number of inputIOFormats must match network's inputs or be one for broadcasting."); + } + + throw std::invalid_argument( + "The number of outputIOFormats must match network's outputs or be one for broadcasting."); + } + return broadcast; +} + +void sparsifyMatMulKernelWeights(nvinfer1::INetworkDefinition& network, std::vector>& sparseWeights) +{ + using TensorToLayer = std::unordered_map; + using LayerToTensor = std::unordered_map; + + // 1. Collect layers and tensors information from the network. + TensorToLayer matmulI2L; + TensorToLayer constO2L; + TensorToLayer shuffleI2L; + LayerToTensor shuffleL2O; + auto collectMappingInfo = [&](int32_t const idx) + { + ILayer* l = network.getLayer(idx); + switch (l->getType()) + { + case nvinfer1::LayerType::kMATRIX_MULTIPLY: + { + // assume weights on the second input. + matmulI2L.insert({l->getInput(1), l}); + break; + } + case nvinfer1::LayerType::kCONSTANT: + { + DataType const dtype = static_cast(l)->getWeights().type; + if (dtype == nvinfer1::DataType::kFLOAT || dtype == nvinfer1::DataType::kHALF) + { + // Sparsify float only. + constO2L.insert({l->getOutput(0), l}); + } + break; + } + case nvinfer1::LayerType::kSHUFFLE: + { + shuffleI2L.insert({l->getInput(0), l}); + shuffleL2O.insert({l, l->getOutput(0)}); + break; + } + default: break; + } + }; + int32_t const nbLayers = network.getNbLayers(); + for (int32_t i = 0; i < nbLayers; ++i) + { + collectMappingInfo(i); + } + if (matmulI2L.size() == 0 || constO2L.size() == 0) + { + // No MatrixMultiply or Constant layer found, no weights to sparsify. + return; + } + + // Helper for analysis + auto isTranspose + = [](nvinfer1::Permutation const& perm) -> bool { return (perm.order[0] == 1 && perm.order[1] == 0); }; + auto is2D = [](nvinfer1::Dims const& dims) -> bool { return dims.nbDims == 2; }; + auto isIdenticalReshape = [](nvinfer1::Dims const& dims) -> bool + { + for (int32_t i = 0; i < dims.nbDims; ++i) + { + if (dims.d[i] != i || dims.d[i] != -1) + { + return false; + } + } + return true; + }; + auto tensorReachedViaTranspose = [&](nvinfer1::ITensor* t, bool& needTranspose) -> ITensor* + { + while (shuffleI2L.find(t) != shuffleI2L.end()) + { + nvinfer1::IShuffleLayer* s = static_cast(shuffleI2L.at(t)); + if (!is2D(s->getInput(0)->getDimensions()) || !is2D(s->getReshapeDimensions()) + || !isIdenticalReshape(s->getReshapeDimensions())) + { + break; + } + + if (isTranspose(s->getFirstTranspose())) + { + needTranspose = !needTranspose; + } + if (isTranspose(s->getSecondTranspose())) + { + needTranspose = !needTranspose; + } + + t = shuffleL2O.at(s); + } + return t; + }; + + // 2. Forward analysis to collect the Constant layers connected to MatMul via Transpose + std::unordered_map constantLayerToSparse; + for (auto& o2l : constO2L) + { + // If need to transpose the weights of the Constant layer. + // Need to transpose by default due to semantic difference. + bool needTranspose{true}; + ITensor* t = tensorReachedViaTranspose(o2l.first, needTranspose); + if (matmulI2L.find(t) == matmulI2L.end()) + { + continue; + } + + // check MatMul params... + IMatrixMultiplyLayer* mm = static_cast(matmulI2L.at(t)); + bool const twoInputs = mm->getNbInputs() == 2; + bool const all2D = is2D(mm->getInput(0)->getDimensions()) && is2D(mm->getInput(1)->getDimensions()); + bool const isSimple = mm->getOperation(0) == nvinfer1::MatrixOperation::kNONE + && mm->getOperation(1) != nvinfer1::MatrixOperation::kVECTOR; + if (!(twoInputs && all2D && isSimple)) + { + continue; + } + if (mm->getOperation(1) == nvinfer1::MatrixOperation::kTRANSPOSE) + { + needTranspose = !needTranspose; + } + + constantLayerToSparse.insert({static_cast(o2l.second), needTranspose}); + } + + // 3. Finally, sparsify the weights + auto sparsifyConstantWeights = [&sparseWeights](nvinfer1::IConstantLayer* layer, bool const needTranspose) + { + Dims dims = layer->getOutput(0)->getDimensions(); + ASSERT(dims.nbDims == 2); + int32_t const idxN = needTranspose ? 1 : 0; + int32_t const n = dims.d[idxN]; + int32_t const k = dims.d[1 - idxN]; + sparseWeights.emplace_back(); + std::vector& spw = sparseWeights.back(); + Weights w = layer->getWeights(); + DataType const dtype = w.type; + ASSERT(dtype == nvinfer1::DataType::kFLOAT + || dtype == nvinfer1::DataType::kHALF); // non-float weights should have been ignored. + + if (needTranspose) + { + if (dtype == nvinfer1::DataType::kFLOAT) + { + spw.resize(w.count * sizeof(float)); + transpose2DWeights(spw.data(), w.values, k, n); + } + else if (dtype == nvinfer1::DataType::kHALF) + { + spw.resize(w.count * sizeof(half_float::half)); + transpose2DWeights(spw.data(), w.values, k, n); + } + + w.values = spw.data(); + std::vector tmpW; + sparsify(w, n, 1, tmpW); + + if (dtype == nvinfer1::DataType::kFLOAT) + { + transpose2DWeights(spw.data(), tmpW.data(), n, k); + } + else if (dtype == nvinfer1::DataType::kHALF) + { + transpose2DWeights(spw.data(), tmpW.data(), n, k); + } + } + else + { + sparsify(w, n, 1, spw); + } + + w.values = spw.data(); + layer->setWeights(w); + }; + for (auto& l : constantLayerToSparse) + { + sparsifyConstantWeights(l.first, l.second); + } +} + +template +void setSparseWeights(L& l, int32_t k, int32_t trs, std::vector& sparseWeights) +{ + auto weights = l.getKernelWeights(); + sparsify(weights, k, trs, sparseWeights); + weights.values = sparseWeights.data(); + l.setKernelWeights(weights); +} + +// Explicit instantiation +template void setSparseWeights( + IConvolutionLayer& l, int32_t k, int32_t trs, std::vector& sparseWeights); + +void sparsify(nvinfer1::INetworkDefinition& network, std::vector>& sparseWeights) +{ + for (int32_t l = 0; l < network.getNbLayers(); ++l) + { + auto* layer = network.getLayer(l); + auto const t = layer->getType(); + if (t == nvinfer1::LayerType::kCONVOLUTION) + { + auto& conv = *static_cast(layer); + auto const& dims = conv.getKernelSizeNd(); + ASSERT(dims.nbDims == 2 || dims.nbDims == 3); + auto const k = conv.getNbOutputMaps(); + auto const trs = std::accumulate(dims.d, dims.d + dims.nbDims, 1, std::multiplies()); + sparseWeights.emplace_back(); + setSparseWeights(conv, k, trs, sparseWeights.back()); + } + } + + sparsifyMatMulKernelWeights(network, sparseWeights); + sample::gLogVerbose << "--sparsity=force pruned " << sparseWeights.size() << " weights to be sparsity pattern." << std::endl; + sample::gLogVerbose << "--sparsity=force has been deprecated. Please use to rewrite the weights to a sparsity pattern and then run with --sparsity=enable" << std::endl; +} + +void sparsify(Weights const& weights, int32_t k, int32_t trs, std::vector& sparseWeights) +{ + switch (weights.type) + { + case DataType::kFLOAT: + sparsify(static_cast(weights.values), weights.count, k, trs, sparseWeights); + break; + case DataType::kHALF: + sparsify(static_cast(weights.values), weights.count, k, trs, sparseWeights); + break; +#if (NV_TENSORRT_MAJOR > 8) + case DataType::kBF16: + sparsify(static_cast(weights.values), weights.count, k, trs, sparseWeights); + break; +#endif + case DataType::kINT8: + case DataType::kINT32: + case DataType::kUINT8: + case DataType::kBOOL: +#if (NV_TENSORRT_MAJOR > 8) + case DataType::kINT4: +#endif + case DataType::kFP8: +#if (NV_TENSORRT_MAJOR > 8) + case DataType::kINT64: +#endif + ASSERT(false && "Unsupported data type"); + } +} + +template +void print(std::ostream& os, T v) +{ + os << v; +} + +void print(std::ostream& os, int8_t v) +{ + os << static_cast(v); +} + +void print(std::ostream& os, __half v) +{ + os << static_cast(v); +} + +template +void dumpBuffer(void const* buffer, std::string const& separator, std::ostream& os, Dims const& dims, + Dims const& strides, int32_t vectorDim, int32_t spv) +{ + auto const vol = volume(dims); + T const* typedBuffer = static_cast(buffer); + std::string sep; + for (int64_t v = 0; v < vol; ++v) + { + int64_t curV = v; + int32_t dataOffset = 0; + for (int32_t dimIndex = dims.nbDims - 1; dimIndex >= 0; --dimIndex) + { + int32_t dimVal = curV % dims.d[dimIndex]; + if (dimIndex == vectorDim) + { + dataOffset += (dimVal / spv) * strides.d[dimIndex] * spv + dimVal % spv; + } + else + { + dataOffset += dimVal * strides.d[dimIndex] * (vectorDim == -1 ? 1 : spv); + } + curV /= dims.d[dimIndex]; + ASSERT(curV >= 0); + } + + os << sep; + sep = separator; + print(os, typedBuffer[dataOffset]); + } +} + +// Explicit instantiation +template void dumpBuffer(void const* buffer, std::string const& separator, std::ostream& os, Dims const& dims, + Dims const& strides, int32_t vectorDim, int32_t spv); +template void dumpBuffer(void const* buffer, std::string const& separator, std::ostream& os, Dims const& dims, + Dims const& strides, int32_t vectorDim, int32_t spv); +template void dumpBuffer(void const* buffer, std::string const& separator, std::ostream& os, Dims const& dims, + Dims const& strides, int32_t vectorDim, int32_t spv); +template void dumpBuffer(void const* buffer, std::string const& separator, std::ostream& os, Dims const& dims, + Dims const& strides, int32_t vectorDim, int32_t spv); +template void dumpBuffer<__half>(void const* buffer, std::string const& separator, std::ostream& os, Dims const& dims, + Dims const& strides, int32_t vectorDim, int32_t spv); +template void dumpBuffer(void const* buffer, std::string const& separator, std::ostream& os, Dims const& dims, + Dims const& strides, int32_t vectorDim, int32_t spv); +template void dumpBuffer(void const* buffer, std::string const& separator, std::ostream& os, Dims const& dims, + Dims const& strides, int32_t vectorDim, int32_t spv); +template void dumpBuffer(void const* buffer, std::string const& separator, std::ostream& os, Dims const& dims, + Dims const& strides, int32_t vectorDim, int32_t spv); + +template +void sparsify(T const* values, int64_t count, int32_t k, int32_t trs, std::vector& sparseWeights) +{ + auto const c = count / (k * trs); + sparseWeights.resize(count * sizeof(T)); + auto* sparseValues = reinterpret_cast(sparseWeights.data()); + + constexpr int32_t window = 4; + constexpr int32_t nonzeros = 2; + + int32_t const crs = c * trs; + auto const getIndex = [=](int32_t ki, int32_t ci, int32_t rsi) { return ki * crs + ci * trs + rsi; }; + + for (int64_t ki = 0; ki < k; ++ki) + { + for (int64_t rsi = 0; rsi < trs; ++rsi) + { + int32_t w = 0; + int32_t nz = 0; + for (int64_t ci = 0; ci < c; ++ci) + { + auto const index = getIndex(ki, ci, rsi); + if (nz < nonzeros) + { + sparseValues[index] = values[index]; + ++nz; + } + else + { + sparseValues[index] = 0; + } + if (++w == window) + { + w = 0; + nz = 0; + } + } + } + } +} + +// Explicit instantiation +template void sparsify( + float const* values, int64_t count, int32_t k, int32_t trs, std::vector& sparseWeights); +template void sparsify( + half_float::half const* values, int64_t count, int32_t k, int32_t trs, std::vector& sparseWeights); + +template +void transpose2DWeights(void* dst, void const* src, int32_t const m, int32_t const n) +{ + ASSERT(dst != src); + T* tdst = reinterpret_cast(dst); + T const* tsrc = reinterpret_cast(src); + for (int32_t mi = 0; mi < m; ++mi) + { + for (int32_t ni = 0; ni < n; ++ni) + { + int32_t const isrc = mi * n + ni; + int32_t const idst = ni * m + mi; + tdst[idst] = tsrc[isrc]; + } + } +} + +// Explicit instantiation +template void transpose2DWeights(void* dst, void const* src, int32_t const m, int32_t const n); +template void transpose2DWeights(void* dst, void const* src, int32_t const m, int32_t const n); + +template ::value, bool>::type> +void fillBuffer(void* buffer, int64_t volume, T min, T max) +{ + T* typedBuffer = static_cast(buffer); + std::default_random_engine engine; + std::uniform_int_distribution distribution(min, max); + auto generator = [&engine, &distribution]() { return static_cast(distribution(engine)); }; + std::generate(typedBuffer, typedBuffer + volume, generator); +} + +template ::value, int32_t>::type> +void fillBuffer(void* buffer, int64_t volume, T min, T max) +{ + T* typedBuffer = static_cast(buffer); + std::default_random_engine engine; + std::uniform_real_distribution distribution(min, max); + auto generator = [&engine, &distribution]() { return static_cast(distribution(engine)); }; + std::generate(typedBuffer, typedBuffer + volume, generator); +} + +// Explicit instantiation +template void fillBuffer(void* buffer, int64_t volume, bool min, bool max); +template void fillBuffer(void* buffer, int64_t volume, float min, float max); +template void fillBuffer(void* buffer, int64_t volume, int32_t min, int32_t max); +template void fillBuffer(void* buffer, int64_t volume, int64_t min, int64_t max); +template void fillBuffer(void* buffer, int64_t volume, int8_t min, int8_t max); +template void fillBuffer<__half>(void* buffer, int64_t volume, __half min, __half max); +template void fillBuffer(void* buffer, int64_t volume, BFloat16 min, BFloat16 max); +template void fillBuffer(void* buffer, int64_t volume, uint8_t min, uint8_t max); + +bool matchStringWithOneWildcard(std::string const& pattern, std::string const& target) +{ + auto const splitPattern = splitToStringVec(pattern, '*', 1); + + // If there is no wildcard, return if the two strings match exactly. + if (splitPattern.size() == 1) + { + return pattern == target; + } + + // Otherwise, target must follow prefix+anything+postfix pattern. + return target.size() >= (splitPattern[0].size() + splitPattern[1].size()) && target.find(splitPattern[0]) == 0 + && target.rfind(splitPattern[1]) == (target.size() - splitPattern[1].size()); +} + +} // namespace sample diff --git a/src/Detector/tensorrt_onnx/common/sampleUtils.h b/src/Detector/tensorrt_onnx/common/sampleUtils.h new file mode 100644 index 000000000..6cd4280b9 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/sampleUtils.h @@ -0,0 +1,127 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef TRT_SAMPLE_UTILS_H +#define TRT_SAMPLE_UTILS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "NvInfer.h" + +#include "common.h" +#include "logger.h" + +#define SMP_RETVAL_IF_FALSE(condition, msg, retval, err) \ + { \ + if ((condition) == false) \ + { \ + (err) << (msg) << std::endl; \ + return retval; \ + } \ + } + +namespace sample +{ + +size_t dataTypeSize(nvinfer1::DataType dataType); + +template +inline T roundUp(T m, T n) +{ + return ((m + n - 1) / n) * n; +} + +//! comps is the number of components in a vector. Ignored if vecDim < 0. +int64_t volume(nvinfer1::Dims const& dims, nvinfer1::Dims const& strides, int32_t vecDim, int32_t comps, int32_t batch); + +using samplesCommon::volume; + +nvinfer1::Dims toDims(std::vector const& vec); + +template ::value, bool>::type = true> +void fillBuffer(void* buffer, int64_t volume, T min, T max); + +template ::value, int32_t>::type = 0> +void fillBuffer(void* buffer, int64_t volume, T min, T max); + +template +void dumpBuffer(void const* buffer, std::string const& separator, std::ostream& os, nvinfer1::Dims const& dims, + nvinfer1::Dims const& strides, int32_t vectorDim, int32_t spv); + +void loadFromFile(std::string const& fileName, char* dst, size_t size); + +std::vector splitToStringVec(std::string const& option, char separator, int64_t maxSplit = -1); + +bool broadcastIOFormats(std::vector const& formats, size_t nbBindings, bool isInput = true); + +int32_t getCudaDriverVersion(); + +int32_t getCudaRuntimeVersion(); + +void sparsify(nvinfer1::INetworkDefinition& network, std::vector>& sparseWeights); +void sparsify(nvinfer1::Weights const& weights, int32_t k, int32_t rs, std::vector& sparseWeights); + +// Walk the weights elements and overwrite (at most) 2 out of 4 elements to 0. +template +void sparsify(T const* values, int64_t count, int32_t k, int32_t rs, std::vector& sparseWeights); + +template +void setSparseWeights(L& l, int32_t k, int32_t rs, std::vector& sparseWeights); + +// Sparsify the weights of Constant layers that are fed to MatMul via Shuffle layers. +// Forward analysis on the API graph to determine which weights to sparsify. +void sparsifyMatMulKernelWeights( + nvinfer1::INetworkDefinition& network, std::vector>& sparseWeights); + +template +void transpose2DWeights(void* dst, void const* src, int32_t const m, int32_t const n); + +//! A helper function to match a target string with a pattern where the pattern can contain up to one wildcard ('*') +//! character that matches to any strings. +bool matchStringWithOneWildcard(std::string const& pattern, std::string const& target); + +//! A helper method to find an item from an unordered_map. If the exact match exists, this is identical to +//! map.find(target). If the exact match does not exist, it returns the first plausible match, taking up to one wildcard +//! into account. If there is no plausible match, then it returns map.end(). +template +typename std::unordered_map::const_iterator findPlausible( + std::unordered_map const& map, std::string const& target) +{ + auto res = map.find(target); + if (res == map.end()) + { + res = std::find_if( + map.begin(), map.end(), [&](typename std::unordered_map::value_type const& item) { + return matchStringWithOneWildcard(item.first, target); + }); + } + return res; +} + +} // namespace sample + +#endif // TRT_SAMPLE_UTILS_H diff --git a/src/Detector/tensorrt_onnx/common/streamReader.h b/src/Detector/tensorrt_onnx/common/streamReader.h new file mode 100644 index 000000000..5bb321448 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/streamReader.h @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifndef STREAM_READER_H +#define STREAM_READER_H + +#include "NvInferRuntime.h" +#include "sampleUtils.h" +#include + +#if (NV_TENSORRT_MAJOR > 8) + +namespace samplesCommon +{ + +//! Implements the TensorRT IStreamReader to allow deserializing an engine directly from the plan file. +class FileStreamReader final : public nvinfer1::IStreamReader +{ +public: + bool open(std::string filepath) + { + mFile.open(filepath, std::ios::binary); + return mFile.is_open(); + } + + void close() + { + if (mFile.is_open()) + { + mFile.close(); + } + } + + ~FileStreamReader() final + { + close(); + } + + int64_t read(void* dest, int64_t bytes) final + { + if (!mFile.good()) + { + return -1; + } + mFile.read(static_cast(dest), bytes); + return mFile.gcount(); + } + + void reset() + { + assert(mFile.good()); + mFile.seekg(0); + } + + bool isOpen() const + { + return mFile.is_open(); + } + +private: + std::ifstream mFile; +}; + +} // namespace samplesCommon + +#endif // #if (NV_TENSORRT_MAJOR > 8) + +#endif // STREAM_READER_H diff --git a/src/Detector/tensorrt_onnx/common/timingCache.cpp b/src/Detector/tensorrt_onnx/common/timingCache.cpp new file mode 100644 index 000000000..18e85ba40 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/timingCache.cpp @@ -0,0 +1,157 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#include "timingCache.h" +#include "NvInfer.h" +#include "fileLock.h" +#include "sampleUtils.h" +#include +#include +#include +#include +#include +#include +using namespace nvinfer1; +namespace nvinfer1 +{ +namespace utils +{ +std::vector loadTimingCacheFile(ILogger& logger, std::string const& inFileName) +{ + try + { + std::unique_ptr fileLock{new FileLock(logger, inFileName)}; + std::ifstream iFile(inFileName, std::ios::in | std::ios::binary); + if (!iFile) + { + std::stringstream ss; + ss << "Could not read timing cache from: " << inFileName + << ". A new timing cache will be generated and written."; + logger.log(ILogger::Severity::kWARNING, ss.str().c_str()); + return std::vector(); + } + iFile.seekg(0, std::ifstream::end); + size_t fsize = iFile.tellg(); + iFile.seekg(0, std::ifstream::beg); + std::vector content(fsize); + iFile.read(content.data(), fsize); + iFile.close(); + std::stringstream ss; + ss << "Loaded " << fsize << " bytes of timing cache from " << inFileName; + logger.log(ILogger::Severity::kINFO, ss.str().c_str()); + return content; + } + catch (std::exception const& e) + { + std::cerr << "Exception detected: " << e.what() << std::endl; + } + return {}; +} + +std::unique_ptr buildTimingCacheFromFile( + ILogger& logger, IBuilderConfig& config, std::string const& timingCacheFile, std::ostream& err) +{ + std::unique_ptr timingCache{}; + auto timingCacheContents = loadTimingCacheFile(logger, timingCacheFile); + timingCache.reset(config.createTimingCache(timingCacheContents.data(), timingCacheContents.size())); + SMP_RETVAL_IF_FALSE(timingCache != nullptr, "TimingCache creation failed", nullptr, err); + config.clearFlag(BuilderFlag::kDISABLE_TIMING_CACHE); + SMP_RETVAL_IF_FALSE( + config.setTimingCache(*timingCache, true), "IBuilderConfig setTimingCache failed", nullptr, err); + return timingCache; +} + +void saveTimingCacheFile(ILogger& logger, std::string const& outFileName, IHostMemory const* blob) +{ + try + { + std::unique_ptr fileLock{new FileLock(logger, outFileName)}; + std::ofstream oFile(outFileName, std::ios::out | std::ios::binary); + if (!oFile) + { + std::stringstream ss; + ss << "Could not write timing cache to: " << outFileName; + logger.log(ILogger::Severity::kWARNING, ss.str().c_str()); + return; + } + oFile.write(reinterpret_cast(blob->data()), blob->size()); + oFile.close(); + std::stringstream ss; + ss << "Saved " << blob->size() << " bytes of timing cache to " << outFileName; + logger.log(ILogger::Severity::kINFO, ss.str().c_str()); + } + catch (std::exception const& e) + { + std::cerr << "Exception detected: " << e.what() << std::endl; + } +} + +void updateTimingCacheFile(nvinfer1::ILogger& logger, std::string const& fileName, + nvinfer1::ITimingCache const* timingCache, nvinfer1::IBuilder& builder) +{ + try + { + // Prepare empty timingCache in case that there is no existing file to read + std::unique_ptr config{builder.createBuilderConfig()}; + std::unique_ptr fileTimingCache{config->createTimingCache(static_cast(nullptr), 0)}; + + std::unique_ptr fileLock{new FileLock(logger, fileName)}; + std::ifstream iFile(fileName, std::ios::in | std::ios::binary); + if (iFile) + { + iFile.seekg(0, std::ifstream::end); + size_t fsize = iFile.tellg(); + iFile.seekg(0, std::ifstream::beg); + std::vector content(fsize); + iFile.read(content.data(), fsize); + iFile.close(); + std::stringstream ss; + ss << "Loaded " << fsize << " bytes of timing cache from " << fileName; + logger.log(ILogger::Severity::kINFO, ss.str().c_str()); + fileTimingCache.reset(config->createTimingCache(static_cast(content.data()), content.size())); + if (!fileTimingCache) + { + throw std::runtime_error("Failed to create timingCache from " + fileName + "!"); + } + } + fileTimingCache->combine(*timingCache, false); + std::unique_ptr blob{fileTimingCache->serialize()}; + if (!blob) + { + throw std::runtime_error("Failed to serialize ITimingCache!"); + } + std::ofstream oFile(fileName, std::ios::out | std::ios::binary); + if (!oFile) + { + std::stringstream ss; + ss << "Could not write timing cache to: " << fileName; + logger.log(ILogger::Severity::kWARNING, ss.str().c_str()); + return; + } + oFile.write(reinterpret_cast(blob->data()), blob->size()); + oFile.close(); + std::stringstream ss; + ss << "Saved " << blob->size() << " bytes of timing cache to " << fileName; + logger.log(ILogger::Severity::kINFO, ss.str().c_str()); + } + catch (std::exception const& e) + { + std::cerr << "Exception detected: " << e.what() << std::endl; + } +} +} // namespace utils +} // namespace nvinfer1 diff --git a/src/Detector/tensorrt_onnx/common/timingCache.h b/src/Detector/tensorrt_onnx/common/timingCache.h new file mode 100644 index 000000000..c4c76e376 --- /dev/null +++ b/src/Detector/tensorrt_onnx/common/timingCache.h @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ +#ifndef TENSORRT_SAMPLES_COMMON_TIMINGCACHE_H_ +#define TENSORRT_SAMPLES_COMMON_TIMINGCACHE_H_ +#include "NvInfer.h" +#include +#include +#include +#include + +namespace nvinfer1 +{ +namespace utils +{ +std::vector loadTimingCacheFile(nvinfer1::ILogger& logger, std::string const& inFileName); +std::unique_ptr buildTimingCacheFromFile( + ILogger& logger, IBuilderConfig& config, std::string const& timingCacheFile, std::ostream& err); +void saveTimingCacheFile(nvinfer1::ILogger& logger, std::string const& outFileName, nvinfer1::IHostMemory const* blob); +void updateTimingCacheFile(nvinfer1::ILogger& logger, std::string const& fileName, + nvinfer1::ITimingCache const* timingCache, nvinfer1::IBuilder& builder); +} // namespace utils +} // namespace nvinfer1 + +#endif // TENSORRT_SAMPLES_COMMON_TIMINGCACHE_H_ diff --git a/Detector/vibe_src/README.md b/src/Detector/vibe_src/README.md similarity index 100% rename from Detector/vibe_src/README.md rename to src/Detector/vibe_src/README.md diff --git a/src/Detector/vibe_src/vibe.cpp b/src/Detector/vibe_src/vibe.cpp new file mode 100644 index 000000000..39ab68bdc --- /dev/null +++ b/src/Detector/vibe_src/vibe.cpp @@ -0,0 +1,194 @@ +#include "vibe.hpp" +#include +#include + +namespace vibe +{ + /// + VIBE::VIBE(int channels, int samples, int pixel_neighbor, int distance_threshold, int matching_threshold, int update_factor) : + m_samples(samples), + m_channels(channels), + m_pixelNeighbor(pixel_neighbor), + m_distanceThreshold(distance_threshold), + m_matchingThreshold(matching_threshold), + m_updateFactor(update_factor) + { + //srand(0); + for (int i = 0; i < RANDOM_BUFFER_SIZE; i++) + { + m_rng[i] = rand(); + } + } + + /// + cv::Vec2i VIBE::getRndNeighbor(int i, int j) + { + int neighbor_count = (m_pixelNeighbor * 2 + 1) * (m_pixelNeighbor * 2 + 1); + int rnd = m_rng[m_rngIdx = (m_rngIdx + 1) % RANDOM_BUFFER_SIZE] % neighbor_count; + int start_i = i - m_pixelNeighbor; + int start_j = j - m_pixelNeighbor; + int area = m_pixelNeighbor * 2 + 1; + int position_i = rnd / area; + int position_j = rnd % area; + int cur_i = std::max(std::min(start_i + position_i, m_size.height - 1), 0); + int cur_j = std::max(std::min(start_j + position_j, m_size.width - 1), 0); + return cv::Vec2i(cur_i, cur_j); + } + + /// + void VIBE::init(const cv::Mat &img) + { + m_size = img.size(); + + const size_t imWidth = static_cast(m_size.width); + const size_t imHeight = static_cast(m_size.height); + const size_t chanSampl = static_cast(m_channels * m_samples); + + m_model.resize(m_channels * static_cast(m_samples) * imWidth * imHeight, 0); + + m_mask = cv::Mat(m_size, CV_8UC1, cv::Scalar::all(0)); + + const uchar* image = img.data; + for (size_t i = 0; i < imHeight; ++i) + { + for (size_t j = 0; j < imWidth; j++) + { + for (size_t c = 0; c < m_channels; ++c) + { + m_model[chanSampl * (imWidth * i + j) + c] = image[m_channels * imWidth * i + m_channels * j + c]; + } + for (size_t s = 1; s < m_samples; ++s) + { + cv::Vec rnd_pos = getRndNeighbor(static_cast(i), static_cast(j)); + size_t img_idx = m_channels * imWidth * rnd_pos[0] + m_channels * rnd_pos[1]; + size_t model_idx = chanSampl * (imWidth * i + j) + m_channels * s; + for (size_t c = 0; c < m_channels; ++c) + { + m_model[model_idx + c] = image[img_idx + c]; + } + } + } + } + } + + /// + void VIBE::update(const cv::Mat& img) + { + if (m_size != img.size()) + { + init(img); + return; + } + + int rowsCount = img.rows; +#pragma omp parallel for + for (int i = 0; i < rowsCount; i++) + { + const uchar* img_ptr = img.ptr(i); + uchar* mask_ptr = m_mask.ptr(i); + + for (int j = 0; j < img.cols; j++) + { + bool flag = false; + int matching_counter = 0; + model_t::value_type* model_ptr = &m_model[m_channels * m_samples * m_size.width * i + m_channels * m_samples * j]; + for (size_t s = 0; s < m_samples; ++s) + { + size_t channels_counter = 0; + for (size_t c = 0; c < m_channels; ++c) + { + if (std::abs((int)model_ptr[c] - img_ptr[c]) < m_distanceThreshold) + ++channels_counter; + } + if (channels_counter == m_channels) + { + if (++matching_counter > m_matchingThreshold) + { + flag = true; + break; + } + } + model_ptr += m_channels; + } + + if (flag) + { + mask_ptr[0] = 0; + if (0 == m_rng[m_rngIdx = (m_rngIdx + 1) % RANDOM_BUFFER_SIZE] % m_updateFactor) + { + int sample = m_rng[m_rngIdx = (m_rngIdx + 1) % RANDOM_BUFFER_SIZE] % m_samples; + size_t model_idx = m_channels * m_samples * m_size.width * i + m_channels * m_samples * j + m_channels * sample; + for (size_t c = 0; c < m_channels; ++c) + { + m_model[model_idx + c] = img_ptr[c]; + } + + cv::Vec2i rnd_pos = getRndNeighbor(i, j); + sample = m_rng[m_rngIdx = (m_rngIdx + 1) % RANDOM_BUFFER_SIZE] % m_samples; + model_idx = m_channels * m_samples * m_size.width * rnd_pos[0] + m_channels * m_samples * rnd_pos[1] + m_channels * sample; + for (size_t c = 0; c < m_channels; ++c) + { + m_model[model_idx + c] = img_ptr[c]; + } + } + } + else + { + mask_ptr[0] = 255; + } + img_ptr += m_channels; + ++mask_ptr; + } + } + } + + /// + cv::Mat& VIBE::getMask() + { + return m_mask; + } + + /// + void VIBE::ResetModel(const cv::Mat& img, const cv::Rect& roiRect) + { + const int top = std::max(0, roiRect.y); + const int bottom = std::min(img.rows, roiRect.y + roiRect.height); + const int left = std::max(0, roiRect.x); + const int right = std::min(img.cols, roiRect.x + roiRect.width); + for (int i = top; i < bottom; i++) + { + const uchar* img_ptr = img.ptr(i) + m_channels * left; + uchar* mask_ptr = m_mask.ptr(i) + left; + + for (int j = left; j < right; j++) + { + if (*mask_ptr) + { + int matching_counter = 0; + model_t::value_type* model_ptr = &m_model[m_channels * m_samples * m_size.width * i + m_channels * m_samples * j]; + for (size_t s = 0; s < m_samples; ++s) + { + size_t channels_counter = 0; + for (size_t c = 0; c < m_channels; ++c) + { + if (std::abs((int)model_ptr[c] - img_ptr[c]) >= m_distanceThreshold) + { + model_ptr[c] = img_ptr[c]; + ++channels_counter; + } + } + if (channels_counter == m_channels) + { + if (++matching_counter > m_matchingThreshold) + break; + } + model_ptr += m_channels; + } + } + + img_ptr += m_channels; + ++mask_ptr; + } + } + } +} diff --git a/src/Detector/vibe_src/vibe.hpp b/src/Detector/vibe_src/vibe.hpp new file mode 100644 index 000000000..46b7b109f --- /dev/null +++ b/src/Detector/vibe_src/vibe.hpp @@ -0,0 +1,45 @@ +#ifndef __VIBE_HPP__ +#define __VIBE_HPP__ + +#include +#include + +namespace vibe +{ + constexpr int RANDOM_BUFFER_SIZE = 65535; + + +class VIBE +{ +public: + VIBE(int channels, int samples, int pixel_neighbor, int distance_threshold, int matching_threshold, int update_factor); + ~VIBE() = default; + + void update(const cv::Mat& img); + cv::Mat& getMask(); + + void ResetModel(const cv::Mat& img, const cv::Rect& roiRect); + +private: + size_t m_samples = 20; + size_t m_channels = 1; + int m_pixelNeighbor = 1; + int m_distanceThreshold = 20; + int m_matchingThreshold = 3; + int m_updateFactor = 16; + + cv::Size m_size; + typedef std::vector model_t; + model_t m_model; + + cv::Mat m_mask; + + unsigned int m_rng[RANDOM_BUFFER_SIZE]; + int m_rngIdx = 0; + + cv::Vec2i getRndNeighbor(int i, int j); + void init(const cv::Mat& img); +}; +} + +#endif /*__VIBE_HPP__*/ diff --git a/src/Tracker/BaseTracker.cpp b/src/Tracker/BaseTracker.cpp new file mode 100644 index 000000000..ec68b858f --- /dev/null +++ b/src/Tracker/BaseTracker.cpp @@ -0,0 +1,680 @@ +#include +#include "BaseTracker.h" +#include "byte_track/BYTETracker.h" +#include "ShortPathCalculator.h" +#include "EmbeddingsCalculator.hpp" +#include "track.h" + +/// +/// \brief The CTracker class +/// +class CTracker final : public BaseTracker +{ +public: + CTracker(const TrackerSettings& settings); + CTracker(const CTracker&) = delete; + CTracker(CTracker&&) = delete; + CTracker& operator=(const CTracker&) = delete; + CTracker& operator=(CTracker&&) = delete; + + ~CTracker(void) = default; + + void Update(const regions_t& regions, cv::UMat currFrame, time_point_t frameTime) override; + + void GetTracks(std::vector& tracks) const override; + void GetRemovedTracks(std::vector& trackIDs) const override; + +private: + TrackerSettings m_settings; + + tracks_t m_tracks; + time_point_t m_lastFrameTime; + + track_id_t m_nextTrackID; + std::vector m_removedObjects; + + cv::UMat m_prevFrame; + + std::unique_ptr m_SPCalculator; + std::map> m_embCalculators; + + void CreateDistaceMatrix(const regions_t& regions, const std::vector& regionEmbeddings, distMatrix_t& costMatrix, track_t maxPossibleCost, track_t& maxCost); + void UpdateTrackingState(const regions_t& regions, cv::UMat currFrame, time_point_t frameTime); + void CalcEmbeddins(std::vector& regionEmbeddings, const regions_t& regions, cv::UMat currFrame) const; + + track_t GetEllipseDist(const CTrack& trackRef, const CRegion& reg); +}; +// ---------------------------------------------------------------------- + +/// +/// \brief CTracker::CTracker +/// Manage tracks: create, remove, update. +/// \param settings +/// +CTracker::CTracker(const TrackerSettings& settings) + : m_settings(settings) +{ + m_SPCalculator.reset(); + SPSettings spSettings = { settings.m_distThres, 12 }; + switch (m_settings.m_matchType) + { + case tracking::MatchHungrian: + m_SPCalculator = std::make_unique(spSettings); + break; + case tracking::MatchLAPJV: + m_SPCalculator = std::make_unique(spSettings); + break; + } + assert(m_SPCalculator); + + for (const auto& embParam : settings.m_embeddings) + { + std::shared_ptr embCalc = std::make_shared(); + if (!embCalc->Initialize(embParam.m_embeddingCfgName, embParam.m_embeddingWeightsName, embParam.m_inputLayer)) + { + std::cerr << "EmbeddingsCalculator initialization error: " << embParam.m_embeddingCfgName << ", " << embParam.m_embeddingWeightsName << std::endl; + } + else + { + for (auto objType : embParam.m_objectTypes) + { + m_embCalculators.try_emplace((objtype_t)objType, embCalc); + } + } + } +} + +/// +/// \brief GetTracks +/// \return +/// +void CTracker::GetTracks(std::vector& tracks) const +{ + tracks.clear(); + + if (m_tracks.size() > tracks.capacity()) + tracks.reserve(m_tracks.size()); + for (const auto& track : m_tracks) + { + tracks.emplace_back(track->ConstructObject(m_lastFrameTime)); + } +} + +/// +/// \brief GetRemovedTracks +/// \return +/// +void CTracker::GetRemovedTracks(std::vector& trackIDs) const +{ + trackIDs.assign(std::begin(m_removedObjects), std::end(m_removedObjects)); +} + +/// +/// \brief CTracker::Update +/// \param regions +/// \param currFrame +/// \param fps +/// +void CTracker::Update(const regions_t& regions, cv::UMat currFrame, time_point_t frameTime) +{ + m_lastFrameTime = frameTime; + m_removedObjects.clear(); + + UpdateTrackingState(regions, currFrame, frameTime); + + currFrame.copyTo(m_prevFrame); +} + +#define DRAW_DBG_ASSIGNMENT 0 + +/// +/// \brief CTracker::UpdateTrackingState +/// \param regions +/// \param currFrame +/// \param fps +/// +void CTracker::UpdateTrackingState(const regions_t& regions, + cv::UMat currFrame, + time_point_t frameTime) +{ + const size_t colsTracks = m_tracks.size(); // Tracking objects + const size_t rowsRegions = regions.size(); // Detections or regions + + assignments_t assignmentT2R(colsTracks, -1); // Assignments: index - track, value - region + + std::vector regionEmbeddings; + CalcEmbeddins(regionEmbeddings, regions, currFrame); + +#if DRAW_DBG_ASSIGNMENT + std::cout << "CTracker::UpdateTrackingState: m_tracks = " << colsTracks << ", regions = " << rowsRegions << std::endl; + + int fontType = cv::FONT_HERSHEY_TRIPLEX; + double fontSize = (currFrame.cols < 1000) ? 0.4 : 0.6; + cv::Scalar colorRegionEllow(100, 100, 100); + cv::Scalar colorMatchedAboveThreshRed(0, 0, 255); + cv::Scalar colorMatchedGreen(0, 255, 0); + cv::Scalar colorMatchedNearMargenta(255, 0, 255); + cv::Scalar colorNotMatchedNearWhite(255, 255, 255); + cv::Scalar colorUnknownBlue(255, 0, 0); + + auto DrawRRect = [](cv::Mat& img, const cv::RotatedRect& rr, const cv::Scalar& cl, int thikness) + { + cv::Point2f rectPoints[4]; + rr.points(rectPoints); + for (int i = 0; i < 4; ++i) + { + cv::line(img, rectPoints[i], rectPoints[(i + 1) % 4], cl, thikness, cv::LINE_4); + } + }; + + cv::Mat dbgAssignment = currFrame.getMat(cv::ACCESS_READ).clone(); + { +#if 0 + cv::Mat foreground(dbgAssignment.size(), CV_8UC1, cv::Scalar(0, 0, 100)); + for (const auto& track : m_tracks) + { + cv::ellipse(foreground, track->GetLastRect(), cv::Scalar(255, 255, 255), cv::FILLED); + } + + const int chans = dbgAssignment.channels(); + const int height = dbgAssignment.rows; +#pragma omp parallel for + for (int y = 0; y < height; ++y) + { + uchar* imgPtr = dbgAssignment.ptr(y); + const uchar* frgrndPtr = foreground.ptr(y); + for (int x = 0; x < dbgAssignment.cols; ++x) + { + for (int ci = chans - 1; ci < chans; ++ci) + { + imgPtr[ci] = cv::saturate_cast(imgPtr[ci] + frgrndPtr[0]); + } + imgPtr += chans; + ++frgrndPtr; + } + } +#endif + for (const auto& reg : regions) + { + DrawRRect(dbgAssignment, reg.m_rrect, colorRegionEllow, 2); + } + } +#endif + + if (!m_tracks.empty()) + { + // Distance matrix between all tracks to all regions +#if DRAW_DBG_ASSIGNMENT + std::cout << "CTracker::UpdateTrackingState: Distance matrix between all tracks to all regions" << std::endl; +#endif + distMatrix_t costMatrix(colsTracks * rowsRegions); + const track_t maxPossibleCost = std::max(static_cast(1.), static_cast(currFrame.cols * currFrame.rows)); + track_t maxCost = 0; + CreateDistaceMatrix(regions, regionEmbeddings, costMatrix, maxPossibleCost, maxCost); +#if DRAW_DBG_ASSIGNMENT + std::cout << "CTracker::UpdateTrackingState: maxPossibleCost = " << maxPossibleCost << ", maxCost = " << maxCost << std::endl; + std::cout << "costMatrix: " << cv::Mat_(rowsRegions, colsTracks, costMatrix.data()) << std::endl; +#endif + + // Solving assignment problem (shortest paths) +#if DRAW_DBG_ASSIGNMENT + std::cout << "CTracker::UpdateTrackingState: Solving assignment problem (shortest paths)" << std::endl; +#endif + m_SPCalculator->Solve(costMatrix, colsTracks, rowsRegions, assignmentT2R, maxCost); + +#if 0 + { + static size_t saveSolveNum = 0; + std::ofstream resCSV("mt_example" + std::to_string(saveSolveNum) + ".csv"); + for (size_t r = 0; r < rowsRegions; ++r) + { + for (size_t c = 0; c < colsTracks; ++c) + { + if (c == colsTracks - 1) + resCSV << std::fixed << std::setw(2) << std::setprecision(2) << costMatrix[c + r * colsTracks] << std::endl; + else + resCSV << std::fixed << std::setw(2) << std::setprecision(2) << costMatrix[c + r * colsTracks] << ", "; + } + } + std::ofstream resSol("mt_example" + std::to_string(saveSolveNum) + ".sol"); + resSol << maxCost << std::endl; + for (size_t r = 0; r < assignmentT2R.size(); ++r) + { + resSol << assignmentT2R[r] << std::endl; + } + ++saveSolveNum; + } +#endif + + // Clean assignment from pairs with large distance +#if DRAW_DBG_ASSIGNMENT + std::cout << "CTracker::UpdateTrackingState: Clean assignment from pairs with large distance" << std::endl; +#endif + for (size_t i = 0; i < assignmentT2R.size(); ++i) + { +#if DRAW_DBG_ASSIGNMENT + std::stringstream ss; + if (assignmentT2R[i] != -1) + { + ss << m_tracks[i]->GetID().ID2Str() << "-" << assignmentT2R[i] << ": " << std::fixed << std::setprecision(2) << costMatrix[i + assignmentT2R[i] * colsTracks]; + + if (costMatrix[i + assignmentT2R[i] * colsTracks] > m_settings.m_distThres) + { + ss << ">" << m_settings.m_distThres; + cv::line(dbgAssignment, m_tracks[i]->GetLastRect().center, regions[assignmentT2R[i]].m_rrect.center, colorMatchedAboveThreshRed, 2); + DrawRRect(dbgAssignment, m_tracks[i]->LastRegion().m_rrect, colorMatchedAboveThreshRed, 1); + } + else + { + ss << "<" << m_settings.m_distThres; + cv::line(dbgAssignment, m_tracks[i]->GetLastRect().center, regions[assignmentT2R[i]].m_rrect.center, colorMatchedGreen, 1); + DrawRRect(dbgAssignment, m_tracks[i]->LastRegion().m_rrect, colorMatchedGreen, 1); + } + + for (size_t ri = 0; ri < regions.size(); ++ri) + { + if (static_cast(ri) != assignmentT2R[i] && costMatrix[i + ri * colsTracks] < 1) + { + std::stringstream liness; + liness << std::fixed << std::setprecision(2) << costMatrix[i + ri * colsTracks]; + auto p1 = m_tracks[i]->GetLastRect().center; + auto p2 = regions[ri].m_rrect.center; + cv::line(dbgAssignment, p1, p2, colorMatchedNearMargenta, 1); + cv::putText(dbgAssignment, liness.str(), cv::Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2), fontType, fontSize, colorMatchedNearMargenta, 1, 8); + } + } + } + else + { + // If track have no assigned detect, then increment skipped frames counter. + DrawRRect(dbgAssignment, m_tracks[i]->LastRegion().m_rrect, colorNotMatchedNearWhite, 1); + for (size_t ri = 0; ri < regions.size(); ++ri) + { + if (costMatrix[i + ri * colsTracks] < 1) + { + std::stringstream liness; + liness << std::fixed << std::setprecision(2) << costMatrix[i + ri * colsTracks]; + auto p1 = m_tracks[i]->GetLastRect().center; + auto p2 = regions[ri].m_rrect.center; + cv::line(dbgAssignment, p1, p2, colorNotMatchedNearWhite, 1); + cv::putText(dbgAssignment, liness.str(), cv::Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2), fontType, fontSize, colorNotMatchedNearWhite, 1, 8); + } + } + } + if (ss.str().length() > 0) + { + auto brect = m_tracks[i]->LastRegion().m_brect; + cv::putText(dbgAssignment, ss.str(), cv::Point(brect.x, brect.y), fontType, fontSize, colorUnknownBlue, 1, 8); + } +#endif + + if (assignmentT2R[i] != -1 && costMatrix[i + assignmentT2R[i] * colsTracks] > m_settings.m_distThres) + assignmentT2R[i] = -1; + } + + // If track didn't get detects long time, remove it +#if DRAW_DBG_ASSIGNMENT + std::cout << "CTracker::UpdateTrackingState: If track did not get detects long time, remove it" << std::endl; +#endif + for (size_t i = 0; i < m_tracks.size();) + { + if (m_tracks[i]->GetLostPeriod(frameTime) > m_settings.m_maximumAllowedLostTime || + m_tracks[i]->IsOutOfTheFrame() || + m_tracks[i]->IsStaticTimeout(frameTime, m_settings.m_maxStaticTime - m_settings.m_minStaticTime)) + { + m_removedObjects.push_back(m_tracks[i]->GetID()); +#if DRAW_DBG_ASSIGNMENT + std::cout << "Remove: " << m_tracks[i]->GetID().ID2Str() << ": lost = " << m_tracks[i]->GetLostPeriod(frameTime) << ", maximumAllowedLostTime = " << m_settings.m_maximumAllowedLostTime << ", out of frame " << m_tracks[i]->IsOutOfTheFrame() << std::endl; +#endif + m_tracks.erase(m_tracks.begin() + i); + assignmentT2R.erase(assignmentT2R.begin() + i); + } + else + { + ++i; + } + } + } + + // Search for unassigned detects and start new tracks for them +#if DRAW_DBG_ASSIGNMENT + std::cout << "CTracker::UpdateTrackingState: Search for unassigned detects and start new tracks for them" << std::endl; +#endif + for (size_t i = 0; i < regions.size(); ++i) + { +#if DRAW_DBG_ASSIGNMENT + std::cout << "CTracker::update: regions[" << i << "].m_rrect: " << regions[i].m_rrect.center << ", " << regions[i].m_rrect.angle << ", " << regions[i].m_rrect.size << std::endl; +#endif + if (std::find(assignmentT2R.begin(), assignmentT2R.end(), i) == assignmentT2R.end()) + { + if (regionEmbeddings.empty()) + m_tracks.push_back(std::make_unique(regions[i], + m_settings.m_kalmanType, + m_settings.m_dt, + m_settings.m_accelNoiseMag, + m_settings.m_useAcceleration, + m_nextTrackID, + m_settings.m_filterGoal, + m_settings.m_lostTrackType, + frameTime)); + else + m_tracks.push_back(std::make_unique(regions[i], + regionEmbeddings[i], + m_settings.m_kalmanType, + m_settings.m_dt, + m_settings.m_accelNoiseMag, + m_settings.m_useAcceleration, + m_nextTrackID, + m_settings.m_filterGoal, + m_settings.m_lostTrackType, + frameTime)); + m_nextTrackID = m_nextTrackID.NextID(); + } + } + + // Update Kalman Filters state +#if DRAW_DBG_ASSIGNMENT + std::cout << "CTracker::UpdateTrackingState: Update Kalman Filters state" << std::endl; +#endif + + const ptrdiff_t stop_i = static_cast(assignmentT2R.size()); +#pragma omp parallel for + for (ptrdiff_t i = 0; i < stop_i; ++i) + { + // If track updated less than one time, than filter state is not correct. + if (assignmentT2R[i] != -1) // If we have assigned detect, then update using its coordinates, + { + m_tracks[i]->ResetLostTime(frameTime); +#if DRAW_DBG_ASSIGNMENT + std::cout << "Update track " << i << " for " << assignmentT2R[i] << " region, regionEmbeddings.size = " << regionEmbeddings.size() << std::endl; +#endif + if (regionEmbeddings.empty()) + m_tracks[i]->Update(regions[assignmentT2R[i]], + true, m_settings.m_maxTraceLength, + m_prevFrame, currFrame, + m_settings.m_useAbandonedDetection ? m_settings.m_minStaticTime : 0, m_settings.m_maxSpeedForStatic, + frameTime); + else + m_tracks[i]->Update(regions[assignmentT2R[i]], regionEmbeddings[assignmentT2R[i]], + true, m_settings.m_maxTraceLength, + m_prevFrame, currFrame, + m_settings.m_useAbandonedDetection ? m_settings.m_minStaticTime : 0, m_settings.m_maxSpeedForStatic, + frameTime); + } + else // if not continue using predictions + { + m_tracks[i]->Update(CRegion(), false, m_settings.m_maxTraceLength, m_prevFrame, currFrame, 0, m_settings.m_maxSpeedForStatic, frameTime); + } + } + +#if DRAW_DBG_ASSIGNMENT + std::cout << "CTracker::UpdateTrackingState: show results" << std::endl; +#ifndef SILENT_WORK + cv::namedWindow("dbgAssignment", cv::WINDOW_NORMAL); + cv::imshow("dbgAssignment", dbgAssignment); + //cv::waitKey(1); +#endif +#endif + +} + +/// +/// \brief CTracker::CreateDistaceMatrix +/// \param regions +/// \param costMatrix +/// \param maxPossibleCost +/// \param maxCost +/// +void CTracker::CreateDistaceMatrix(const regions_t& regions, + const std::vector& regionEmbeddings, + distMatrix_t& costMatrix, + track_t maxPossibleCost, + track_t& maxCost) +{ + const size_t colsTracks = m_tracks.size(); // Tracking objects + maxCost = 0; + + for (size_t i = 0; i < colsTracks; ++i) + { + const auto& track = m_tracks[i]; + + // call kalman prediction fist + if (track->GetFilterGoal() == tracking::FilterGoal::FilterRect || + track->GetFilterGoal() == tracking::FilterGoal::FilterRRect) + track->KalmanPredictRect(); + else + track->KalmanPredictPoint(); + + constexpr bool DIST_LOGS = false; + + // Calc distance between track and regions + for (size_t j = 0; j < regions.size(); ++j) + { + const auto& reg = regions[j]; + + auto dist = maxPossibleCost; + if (m_settings.CheckType(m_tracks[i]->LastRegion().m_type, reg.m_type)) + { + dist = 0; + size_t ind = 0; + // Euclidean distance between centers + if (m_settings.m_distType[ind] > 0.0f && ind == tracking::DistCenters) + { +#if 1 + track_t ellipseDist = GetEllipseDist(*track, reg); + if (ellipseDist > 1) + dist += m_settings.m_distType[ind]; + else + dist += ellipseDist * m_settings.m_distType[ind]; +#else + dist += m_settings.m_distType[ind] * track->CalcDistCenter(reg); +#endif + if constexpr (DIST_LOGS) + { + std::cout << "DistCenters : " << m_settings.m_distType[ind] << ", dist = " << dist << "\n"; + //std::cout << "dist = " << dist << ", ed = " << ellipseDist << ", reg.m_rrect.center = " << reg.m_rrect.center << ", predictedArea: center = " << predictedArea.center << ", size = " << predictedArea.size << ", angle = " << predictedArea.angle << "\n"; + std::cout << "track id = " << m_tracks[i]->GetID().ID2Str() << " type = " << TypeConverter::Type2Str(m_tracks[i]->LastRegion().m_type) << " (" << m_tracks[i]->LastRegion().m_type << "), region id = " << j << ", type = " << TypeConverter::Type2Str(reg.m_type) << " (" << reg.m_type << ")" << std::endl; + } + } + ++ind; + + // Euclidean distance between bounding rectangles + if (m_settings.m_distType[ind] > 0.0f && ind == tracking::DistRects) + { +#if 1 + track_t ellipseDist = GetEllipseDist(*track, reg); + if (ellipseDist < 1) + { + track_t dw = track->WidthDist(reg); + track_t dh = track->HeightDist(reg); + dist += m_settings.m_distType[ind] * (1 - (1 - ellipseDist) * (dw + dh) * 0.5f); + } + else + { + dist += m_settings.m_distType[ind]; + } + + if constexpr (DIST_LOGS) + { + std::cout << "DistRects : " << m_settings.m_distType[ind] << ", dist = " << dist << "\n"; + track_t dw = track->WidthDist(reg); + track_t dh = track->HeightDist(reg); + std::cout << "dist = " << dist << ", ed = " << ellipseDist << ", dw = " << dw << ", dh = " << dh << "\n"; + std::cout << "track type = " << TypeConverter::Type2Str(m_tracks[i]->LastRegion().m_type) << " (" << m_tracks[i]->LastRegion().m_type << "), region type = " << TypeConverter::Type2Str(reg.m_type) << " (" << reg.m_type << ")\n"; + std::cout << "track = " << m_tracks[i]->LastRegion().m_brect << ", reg = " << reg.m_brect << ", rrect = [" << reg.m_rrect.size << " from " << reg.m_rrect.center << ", " << reg.m_rrect.angle << "]" << std::endl; + } +#else + dist += m_settings.m_distType[ind] * track->CalcDistRect(reg); +#endif + } + ++ind; + + // Intersection over Union, IoU + if (m_settings.m_distType[ind] > 0.0f && ind == tracking::DistJaccard) + { + dist += m_settings.m_distType[ind] * track->CalcDistJaccard(reg); + if constexpr (DIST_LOGS) + std::cout << "DistJaccard : " << m_settings.m_distType[ind] << ", dist = " << dist << std::endl; + } + ++ind; + + // Bhatacharia distance between histograms + if (m_settings.m_distType[ind] > 0.0f && ind == tracking::DistHist) + { + dist += m_settings.m_distType[ind] * track->CalcDistHist(regionEmbeddings[j]); + if constexpr (DIST_LOGS) + std::cout << "DistHist : " << m_settings.m_distType[ind] << ", dist = " << dist << std::endl; + } + ++ind; + + // Cosine distance between embeddings + if (m_settings.m_distType[ind] > 0.0f && ind == tracking::DistFeatureCos) + { + if (reg.m_type == track->LastRegion().m_type) + { + auto resCos = track->CalcCosine(regionEmbeddings[j]); + if (resCos.second) + { + dist += m_settings.m_distType[ind] * resCos.first; + //std::cout << "CalcCosine: " << TypeConverter::Type2Str(track->LastRegion().m_type) << ", reg = " << reg.m_brect << ", track = " << track->LastRegion().m_brect << ": res = " << resCos.first << ", dist = " << dist << std::endl; + } + else + { + dist /= m_settings.m_distType[ind]; + //std::cout << "CalcCosine: " << TypeConverter::Type2Str(track->LastRegion().m_type) << ", reg = " << reg.m_brect << ", track = " << track->LastRegion().m_brect << ": res = 1, weight = " << m_settings.m_distType[ind] << ", dist = " << dist << std::endl; + } + } + if constexpr (DIST_LOGS) + std::cout << "DistFeatureCos : " << m_settings.m_distType[ind] << ", dist = " << dist << std::endl; + } + ++ind; + + // Mahalanobis + if (m_settings.m_distType[ind] > 0.0f && ind == tracking::DistMahalanobis) + dist += m_settings.m_distType[ind] * track->CalcMahalanobisDist(reg.m_rrect); + ++ind; + + assert(ind == tracking::DistsCount); + } + + costMatrix[i + j * colsTracks] = dist; + if constexpr (DIST_LOGS) + std::cout << "costMatrix[" << j << "][" << i << "] (or " << (i + j * colsTracks) << ") = " << dist << std::endl; + + if (dist < 0 || dist > maxPossibleCost) + { + assert(0); + exit(-1); + } + + if (dist > maxCost) + maxCost = dist; + } + } +} + +/// +/// \brief CTracker::CalcEmbeddins +/// \param regionEmbeddings +/// \param regions +/// \param currFrame +/// +void CTracker::CalcEmbeddins(std::vector& regionEmbeddings, const regions_t& regions, cv::UMat currFrame) const +{ + if (!regions.empty()) + { + regionEmbeddings.resize(regions.size()); + // Bhatacharia distance between histograms + if (m_settings.m_distType[tracking::DistHist] > 0.0f) + { + for (size_t j = 0; j < regions.size(); ++j) + { + int bins = 64; + std::vector histSize; + std::vector ranges; + std::vector channels; + + for (int i = 0, stop = currFrame.channels(); i < stop; ++i) + { + histSize.push_back(bins); + ranges.push_back(0); + ranges.push_back(255); + channels.push_back(i); + } + + std::vector regROI = { currFrame(regions[j].m_brect) }; + cv::calcHist(regROI, channels, cv::Mat(), regionEmbeddings[j].m_hist, histSize, ranges, false); + cv::normalize(regionEmbeddings[j].m_hist, regionEmbeddings[j].m_hist, 0, 1, cv::NORM_MINMAX, -1, cv::Mat()); + } + } + + // Cosine distance between embeddings + if (m_settings.m_distType[tracking::DistFeatureCos] > 0.0f) + { + for (size_t j = 0; j < regions.size(); ++j) + { + if (regionEmbeddings[j].m_embedding.empty()) + { + // std::cout << "Search embCalc for " << TypeConverter::Type2Str(regions[j].m_type) << ": "; + auto embCalc = m_embCalculators.find(regions[j].m_type); + if (embCalc != std::end(m_embCalculators)) + { + embCalc->second->Calc(currFrame, regions[j].m_brect, regionEmbeddings[j].m_embedding); + + //std::cout << "Founded! m_embedding = " << regionEmbeddings[j].m_embedding.size() << std::endl; + } + else + { + //std::cout << "Not found" << std::endl; + } + } + } + } + } +} + +/// +/// \brief CTracker::GetEllipseDist +/// \param trackRef +/// \param reg +/// \return +/// +track_t CTracker::GetEllipseDist(const CTrack& trackRef, const CRegion& reg) +{ + cv::Size_ minRadius; + + if (m_settings.m_minAreaRadiusPix <= 0) + { + minRadius.width = m_settings.m_minAreaRadiusK * trackRef.LastRegion().m_rrect.size.width; + minRadius.height = m_settings.m_minAreaRadiusK * trackRef.LastRegion().m_rrect.size.height; + } + else + { + minRadius.width = m_settings.m_minAreaRadiusPix; + minRadius.height = m_settings.m_minAreaRadiusPix; + } + + // Calc predicted area for track + cv::RotatedRect predictedArea = trackRef.CalcPredictionEllipse(minRadius); + + return trackRef.IsInsideArea(reg.m_rrect.center, predictedArea); +} + +/// +/// BaseTracker::CreateTracker +/// +std::unique_ptr BaseTracker::CreateTracker(const TrackerSettings& settings, float fps) +{ + switch (settings.m_tracker) + { + case tracking::UniversalTracker: + return std::make_unique(settings); + + case tracking::ByteTrack: + return std::make_unique((fps > 1.f) ? cvRound(fps) : 30, settings.m_byteTrackSettings.m_trackBuffer, + settings.m_byteTrackSettings.m_trackThresh, settings.m_byteTrackSettings.m_highThresh, settings.m_byteTrackSettings.m_matchThresh); + + default: + return std::make_unique(settings); + } +} diff --git a/src/Tracker/BaseTracker.h b/src/Tracker/BaseTracker.h new file mode 100644 index 000000000..7a43ed66d --- /dev/null +++ b/src/Tracker/BaseTracker.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +#include "defines.h" +#include "trajectory.h" +#include "TrackerSettings.h" + +/// +/// \brief The CTracker class +/// +class BaseTracker +{ +public: + BaseTracker() = default; + BaseTracker(const BaseTracker&) = delete; + BaseTracker(BaseTracker&&) = delete; + BaseTracker& operator=(const BaseTracker&) = delete; + BaseTracker& operator=(BaseTracker&&) = delete; + + virtual ~BaseTracker(void) = default; + + virtual void Update(const regions_t& regions, cv::UMat currFrame, time_point_t frameTime) = 0; + virtual void UpdateMat(const regions_t& regions, cv::Mat currFrame, time_point_t frameTime) + { + cv::UMat frame = currFrame.getUMat(cv::ACCESS_READ); + Update(regions, frame, frameTime); + } + + virtual void GetTracks(std::vector& tracks) const = 0; + virtual std::vector GetTracksCopy() const + { + std::vector tracks; + GetTracks(tracks); + return tracks; + } + virtual void GetRemovedTracks(std::vector& trackIDs) const = 0; + + static std::unique_ptr CreateTracker(const TrackerSettings& settings, float fps); +}; diff --git a/src/Tracker/CMakeLists.txt b/src/Tracker/CMakeLists.txt new file mode 100644 index 000000000..fbb355a8b --- /dev/null +++ b/src/Tracker/CMakeLists.txt @@ -0,0 +1,108 @@ +cmake_minimum_required(VERSION 3.9) + +project(mtracking) + +set(main_sources + ../mtracking/nms.h + ../mtracking/defines.h + ../mtracking/object_types.h + ../mtracking/object_types.cpp) + +set(tracker_sources + BaseTracker.cpp + track.cpp + Kalman.cpp + TrackerSettings.cpp + ShortPathCalculator.cpp + HungarianAlg/HungarianAlg.cpp + LAPJV_algorithm/lap.cpp + byte_track/BYTETracker.cpp + byte_track/KalmanFilter.cpp + byte_track/lapjv.cpp + byte_track/STrack.cpp) + +set(tracker_headers + BaseTracker.h + ShortPathCalculator.h + track.h + trajectory.h + Kalman.h + TrackerSettings.h + HungarianAlg/HungarianAlg.h + LAPJV_algorithm/lap.h + EmbeddingsCalculator.hpp + byte_track/BYTETracker.h + byte_track/KalmanFilter.h + byte_track/lapjv.h + byte_track/STrack.h) + +SOURCE_GROUP("Src" FILES ${main_sources}) +SOURCE_GROUP("Tracker" FILES ${tracker_sources} ${tracker_headers}) + +include(CheckIncludeFileCXX) +check_include_file_cxx(opencv2/bgsegm.hpp HAVE_OPENCV_CONTRIB) +if(HAVE_OPENCV_CONTRIB) + add_definitions(-DHAVE_OPENCV_CONTRIB) + + option(USE_OCV_KCF "Should use the KCF tracker from opencv_contrib for collisions resolving?" ON) + +else(HAVE_OPENCV_CONTRIB) + + option(USE_OCV_KCF "Should use the KCF tracker from opencv_contrib for collisions resolving?" OFF) + +endif(HAVE_OPENCV_CONTRIB) + +option(USE_OCV_UKF "Should use the Unscented Kalman Filter from opencv_contrib?" OFF) + +if(USE_OCV_UKF) + add_definitions(-DUSE_OCV_UKF) +else() + remove_definitions(-DUSE_OCV_UKF) +endif(USE_OCV_UKF) + +if(USE_OCV_KCF) + add_definitions(-DUSE_OCV_KCF) +else() + remove_definitions(-DUSE_OCV_KCF) +endif(USE_OCV_KCF) + +option(USE_OCV_EMBEDDINGS "Should use the embeddings from opencv_dnn + OpenVINO?" ON) +if(USE_OCV_EMBEDDINGS) + add_definitions(-DUSE_OCV_EMBEDDINGS) +else() + remove_definitions(-DUSE_OCV_EMBEDDINGS) +endif(USE_OCV_EMBEDDINGS) + + +include_directories(${PROJECT_SOURCE_DIR}) +include_directories(${PROJECT_SOURCE_DIR}/../src) +include_directories(${PROJECT_SOURCE_DIR}/../mtracking) +include_directories(${PROJECT_SOURCE_DIR}/../../thirdparty) + +if (CMAKE_COMPILER_IS_GNUCXX) + add_library(${PROJECT_NAME} SHARED + ${main_sources} + ${tracker_sources}) + set(LIBS + ${OpenCV_LIBS} + inih) +else(CMAKE_COMPILER_IS_GNUCXX) + add_library(${PROJECT_NAME} + ${main_sources} + ${tracker_sources}) + set(LIBS + # ${OpenCV_LIBS} + inih) + target_include_directories(${PROJECT_NAME} PRIVATE ${OpenCV_INCLUDE_DIRS}) +endif() + +target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBS}) + +set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "${tracker_headers};../mtracking/defines.h;../mtracking/object_types.h") +install(TARGETS ${PROJECT_NAME} + EXPORT MTTrackingExports + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + PUBLIC_HEADER DESTINATION include/${PROJECT_NAME}) + +set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "libs") diff --git a/src/Tracker/EmbeddingsCalculator.hpp b/src/Tracker/EmbeddingsCalculator.hpp new file mode 100644 index 000000000..b285903f1 --- /dev/null +++ b/src/Tracker/EmbeddingsCalculator.hpp @@ -0,0 +1,152 @@ +#pragma once + +/// +/// \brief The EmbeddingsCalculator class +/// +class EmbeddingsCalculator +{ +public: + EmbeddingsCalculator() = default; + virtual ~EmbeddingsCalculator() = default; + + /// + bool Initialize(const std::string& cfgName, const std::string& weightsName, const cv::Size& inputLayer) + { +#ifdef USE_OCV_EMBEDDINGS + m_inputLayer = inputLayer; + +#if 1 + m_net = cv::dnn::readNet(weightsName); +#else + m_net = cv::dnn::readNetFromTorch(weightsName); +#endif + + std::cout << "Re-id model " << weightsName << " loaded: " << (!m_net.empty()) << std::endl; + + if (!m_net.empty()) + { +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR >= 2)) || (CV_VERSION_MAJOR > 4)) + std::map dictTargets; + dictTargets[cv::dnn::DNN_TARGET_CPU] = "DNN_TARGET_CPU"; + dictTargets[cv::dnn::DNN_TARGET_OPENCL] = "DNN_TARGET_OPENCL"; + dictTargets[cv::dnn::DNN_TARGET_OPENCL_FP16] = "DNN_TARGET_OPENCL_FP16"; + dictTargets[cv::dnn::DNN_TARGET_MYRIAD] = "DNN_TARGET_MYRIAD"; + dictTargets[cv::dnn::DNN_TARGET_CUDA] = "DNN_TARGET_CUDA"; + dictTargets[cv::dnn::DNN_TARGET_CUDA_FP16] = "DNN_TARGET_CUDA_FP16"; +#if (CV_VERSION_MAJOR > 4) + dictTargets[cv::dnn::DNN_TARGET_HDDL] = "DNN_TARGET_HDDL"; + dictTargets[cv::dnn::DNN_TARGET_NPU] = "DNN_TARGET_NPU"; + dictTargets[cv::dnn::DNN_TARGET_CPU_FP16] = "DNN_TARGET_CPU_FP16"; +#endif + + std::map dictBackends; + dictBackends[cv::dnn::DNN_BACKEND_DEFAULT] = "DNN_BACKEND_DEFAULT"; + dictBackends[cv::dnn::DNN_BACKEND_INFERENCE_ENGINE] = "DNN_BACKEND_INFERENCE_ENGINE"; + dictBackends[cv::dnn::DNN_BACKEND_OPENCV] = "DNN_BACKEND_OPENCV"; + dictBackends[cv::dnn::DNN_BACKEND_VKCOM] = "DNN_BACKEND_VKCOM"; + dictBackends[cv::dnn::DNN_BACKEND_CUDA] = "DNN_BACKEND_CUDA"; +#if (CV_VERSION_MAJOR > 4) + dictBackends[cv::dnn::DNN_BACKEND_WEBNN] = "DNN_BACKEND_WEBNN"; + dictBackends[cv::dnn::DNN_BACKEND_TIMVX] = "DNN_BACKEND_TIMVX"; + dictBackends[cv::dnn::DNN_BACKEND_CANN] = "DNN_BACKEND_CANN"; +#endif + dictBackends[1000000] = "DNN_BACKEND_INFERENCE_ENGINE_NGRAPH"; + dictBackends[1000000 + 1] = "DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019"; + + std::cout << "Avaible pairs for Target - backend:" << std::endl; + std::vector> pairs = cv::dnn::getAvailableBackends(); + for (auto p : pairs) + { + std::cout << dictBackends[p.first] << " (" << p.first << ") - " << dictTargets[p.second] << " (" << p.second << ")" << std::endl; + + if (p.first == cv::dnn::DNN_BACKEND_CUDA) + { + //m_net.setPreferableTarget(p.second); + //m_net.setPreferableBackend(p.first); + //std::cout << "Set!" << std::endl; + } + } +#endif + + auto outNames = m_net.getUnconnectedOutLayersNames(); + auto outLayers = m_net.getUnconnectedOutLayers(); + auto outLayerType = m_net.getLayer(outLayers[0])->type; + +#if (CV_VERSION_MAJOR < 5) + std::vector outputs; + std::vector internals; + m_net.getLayerShapes(cv::dnn::MatShape(), 0, outputs, internals); +#else + std::vector outputs; + std::vector internals; + m_net.getLayerShapes(cv::MatShape(), CV_32F, 0, outputs, internals); +#endif + std::cout << "REID: getLayerShapes: outputs (" << outputs.size() << ") = " << (outputs.size() > 0 ? outputs[0].size() : 0) << ", internals (" << internals.size() << ") = " << (internals.size() > 0 ? internals[0].size() : 0) << std::endl; + if (outputs.size() && outputs[0].size() > 3) + std::cout << "outputs = [" << outputs[0][0] << ", " << outputs[0][1] << ", " << outputs[0][2] << ", " << outputs[0][3] << "], internals = [" << internals[0][0] << ", " << internals[0][1] << ", " << internals[0][2] << ", " << internals[0][3] << "]" << std::endl; + } + return !m_net.empty(); +#else + std::cerr << "EmbeddingsCalculator was disabled in CMAKE! Check SetDistances params." << std::endl; + return false; +#endif + } + + /// + bool IsInitialized() const + { +#ifdef USE_OCV_EMBEDDINGS + return !m_net.empty(); +#else + return false; +#endif + } + + /// + void Calc(const cv::UMat& img, cv::Rect rect, cv::Mat& embedding) + { +#ifdef USE_OCV_EMBEDDINGS + auto Clamp = [](int& v, int& size, int hi) -> int + { + int res = 0; + if (v < 0) + { + res = v; + v = 0; + return res; + } + else if (v + size > hi - 1) + { + res = v; + v = hi - 1 - size; + if (v < 0) + { + size += v; + v = 0; + } + res -= v; + return res; + } + return res; + }; + Clamp(rect.x, rect.width, img.cols); + Clamp(rect.y, rect.height, img.rows); + + cv::Mat obj; + cv::resize(img(rect), obj, m_inputLayer, 0., 0., cv::INTER_CUBIC); + cv::Mat blob = cv::dnn::blobFromImage(obj, 1.0 / 255.0, cv::Size(), cv::Scalar(), false, false, CV_32F); + + m_net.setInput(blob); + //std::cout << "embedding: " << embedding.size() << ", chans = " << embedding.channels() << std::endl; + cv::normalize(m_net.forward(), embedding); +#else + std::cerr << "EmbeddingsCalculator was disabled in CMAKE! Check SetDistances params." << std::endl; +#endif + } + +private: +#ifdef USE_OCV_EMBEDDINGS + cv::dnn::Net m_net; + cv::Size m_inputLayer{ 128, 256 }; +#endif +}; diff --git a/src/Tracker/HungarianAlg/HungarianAlg.cpp b/src/Tracker/HungarianAlg/HungarianAlg.cpp new file mode 100644 index 000000000..195f2acba --- /dev/null +++ b/src/Tracker/HungarianAlg/HungarianAlg.cpp @@ -0,0 +1,694 @@ +#include "HungarianAlg.h" +#include + +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- +track_t AssignmentProblemSolver::Solve(const distMatrix_t& distMatrixIn, + size_t nOfRows, + size_t nOfColumns, + std::vector& assignment, + TMethod Method) +{ + assignment.resize(nOfRows, -1); + + track_t cost = 0; + + switch (Method) + { + case optimal: + assignmentoptimal(assignment, cost, distMatrixIn, nOfRows, nOfColumns); + break; + + case many_forbidden_assignments: + assignmentsuboptimal1(assignment, cost, distMatrixIn, nOfRows, nOfColumns); + break; + + case without_forbidden_assignments: + assignmentsuboptimal2(assignment, cost, distMatrixIn, nOfRows, nOfColumns); + break; + } + + return cost; +} +// -------------------------------------------------------------------------- +// Computes the optimal assignment (minimum overall costs) using Munkres algorithm. +// -------------------------------------------------------------------------- +void AssignmentProblemSolver::assignmentoptimal(assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns) +{ + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: Generate distance cv::Matrix and check cv::Matrix elements positiveness, assignment = " << assignment.size() << ", cost = " << cost << ", distMatrixIn = " << distMatrixIn.size() << ", nOfRows = " << nOfRows << ", nOfColumns = " << nOfColumns << std::endl; + + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: Total elements number" << std::endl; + const size_t nOfElements = nOfRows * nOfColumns; + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: Memory allocation" << std::endl; + m_distMatrix.assign(std::begin(distMatrixIn), std::end(distMatrixIn)); + const track_t* distMatrixEnd = m_distMatrix.data() + nOfElements; + + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: Memory allocation" << std::endl; + bool* coveredColumns = (bool*)calloc(nOfColumns, sizeof(bool)); + bool* coveredRows = (bool*)calloc(nOfRows, sizeof(bool)); + bool* starMatrix = (bool*)calloc(nOfElements, sizeof(bool)); + bool* primeMatrix = (bool*)calloc(nOfElements, sizeof(bool)); + bool* newStarMatrix = (bool*)calloc(nOfElements, sizeof(bool)); // used in step4 + + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: preliminary steps" << std::endl; + if (nOfRows <= nOfColumns) + { + for (size_t row = 0; row < nOfRows; ++row) + { + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: find the smallest element in the row" << std::endl; + track_t* distMatrixTemp = m_distMatrix.data() + row; + track_t minValue = *distMatrixTemp; + distMatrixTemp += nOfRows; + while (distMatrixTemp < distMatrixEnd) + { + track_t value = *distMatrixTemp; + if (value < minValue) + minValue = value; + + distMatrixTemp += nOfRows; + } + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: subtract the smallest element from each element of the row" << std::endl; + distMatrixTemp = m_distMatrix.data() + row; + while (distMatrixTemp < distMatrixEnd) + { + *distMatrixTemp -= minValue; + distMatrixTemp += nOfRows; + } + } + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: Steps 1 and 2a" << std::endl; + for (size_t row = 0; row < nOfRows; ++row) + { + for (size_t col = 0; col < nOfColumns; ++col) + { + if (m_distMatrix[row + nOfRows*col] == 0) + { + if (!coveredColumns[col]) + { + starMatrix[row + nOfRows * col] = true; + coveredColumns[col] = true; + break; + } + } + } + } + } + else // if(nOfRows > nOfColumns) + { + for (size_t col = 0; col < nOfColumns; ++col) + { + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: find the smallest element in the column" << std::endl; + track_t* distMatrixTemp = m_distMatrix.data() + nOfRows*col; + track_t* columnEnd = distMatrixTemp + nOfRows; + track_t minValue = *distMatrixTemp++; + while (distMatrixTemp < columnEnd) + { + track_t value = *distMatrixTemp++; + if (value < minValue) + minValue = value; + } + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: subtract the smallest element from each element of the column" << std::endl; + distMatrixTemp = m_distMatrix.data() + nOfRows*col; + while (distMatrixTemp < columnEnd) + { + *distMatrixTemp++ -= minValue; + } + } + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: Steps 1 and 2a" << std::endl; + for (size_t col = 0; col < nOfColumns; ++col) + { + for (size_t row = 0; row < nOfRows; ++row) + { + if (m_distMatrix[row + nOfRows*col] == 0) + { + if (!coveredRows[row]) + { + starMatrix[row + nOfRows*col] = true; + coveredColumns[col] = true; + coveredRows[row] = true; + break; + } + } + } + } + + for (size_t row = 0; row < nOfRows; ++row) + { + coveredRows[row] = false; + } + } + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: move to step 2b" << std::endl; + step2b(assignment, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, (nOfRows <= nOfColumns) ? nOfRows : nOfColumns); + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: compute cost and remove invalid assignments" << std::endl; + computeassignmentcost(assignment, cost, distMatrixIn, nOfRows); + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentoptimal: free allocated memory" << std::endl; + free(coveredColumns); + free(coveredRows); + free(starMatrix); + free(primeMatrix); + free(newStarMatrix); + return; +} +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- +void AssignmentProblemSolver::buildassignmentvector(assignments_t& assignment, bool *starMatrix, size_t nOfRows, size_t nOfColumns) +{ + for (size_t row = 0; row < nOfRows; ++row) + { + for (size_t col = 0; col < nOfColumns; ++col) + { + if (starMatrix[row + nOfRows * col]) + { + assignment[row] = static_cast(col); + break; + } + } + } +} +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- +void AssignmentProblemSolver::computeassignmentcost(const assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows) +{ + for (size_t row = 0; row < nOfRows; ++row) + { + const int col = assignment[row]; + if (col >= 0) + cost += distMatrixIn[row + nOfRows * col]; + } +} + +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- +void AssignmentProblemSolver::step2a(assignments_t& assignment, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim) +{ + bool *starMatrixTemp, *columnEnd; + if constexpr (HUNGARIAN_LOGS) + std::cout << "step2a: cover every column containing a starred zero" << std::endl; + for (size_t col = 0; col < nOfColumns; ++col) + { + starMatrixTemp = starMatrix + nOfRows * col; + columnEnd = starMatrixTemp + nOfRows; + while (starMatrixTemp < columnEnd) + { + if (*starMatrixTemp++) + { + coveredColumns[col] = true; + break; + } + } + } + if constexpr (HUNGARIAN_LOGS) + std::cout << "step2a: move to step 3" << std::endl; + step2b(assignment, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim); +} + +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- +void AssignmentProblemSolver::step2b(assignments_t& assignment, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim) +{ + if constexpr (HUNGARIAN_LOGS) + std::cout << "step2b: count covered columns" << std::endl; + size_t nOfCoveredColumns = 0; + for (size_t col = 0; col < nOfColumns; ++col) + { + if (coveredColumns[col]) + nOfCoveredColumns++; + } + if (nOfCoveredColumns == minDim) // algorithm finished + buildassignmentvector(assignment, starMatrix, nOfRows, nOfColumns); + else // move to step 3 + step3_5(assignment, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim); +} + +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- +void AssignmentProblemSolver::step3_5(assignments_t& assignment, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim) +{ + for (;;) + { + if constexpr (HUNGARIAN_LOGS) + std::cout << "step3_5: step 3" << std::endl; + bool zerosFound = true; + while (zerosFound) + { + zerosFound = false; + for (size_t col = 0; col < nOfColumns; ++col) + { + if (!coveredColumns[col]) + { + for (size_t row = 0; row < nOfRows; ++row) + { + if ((!coveredRows[row]) && (m_distMatrix[row + nOfRows*col] == 0)) + { + if constexpr (HUNGARIAN_LOGS) + std::cout << "step3_5: prime zero" << std::endl; + primeMatrix[row + nOfRows*col] = true; + if constexpr (HUNGARIAN_LOGS) + std::cout << "step3_5: find starred zero in current row" << std::endl; + size_t starCol = 0; + for (; starCol < nOfColumns; ++starCol) + { + if (starMatrix[row + nOfRows * starCol]) + break; + } + if constexpr (HUNGARIAN_LOGS) + std::cout << "step3_5: starCol = " << starCol << ", nOfColumns = " << nOfColumns << std::endl; + if (starCol == nOfColumns) // no starred zero found + { + if constexpr (HUNGARIAN_LOGS) + std::cout << "step3_5: move to step 4" << std::endl; + step4(assignment, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim, row, col); + return; + } + else + { + coveredRows[row] = true; + coveredColumns[starCol] = false; + zerosFound = true; + break; + } + } + } + } + } + } + if constexpr (HUNGARIAN_LOGS) + std::cout << "step3_5: step 5" << std::endl; + track_t h = std::numeric_limits::max(); + for (size_t row = 0; row < nOfRows; ++row) + { + if (!coveredRows[row]) + { + for (size_t col = 0; col < nOfColumns; ++col) + { + if (!coveredColumns[col]) + { + const track_t value = m_distMatrix[row + nOfRows*col]; + if (value < h) + h = value; + } + } + } + } + if constexpr (HUNGARIAN_LOGS) + std::cout << "step3_5: add h to each covered row, h = " << h << std::endl; + for (size_t row = 0; row < nOfRows; ++row) + { + if (coveredRows[row]) + { + for (size_t col = 0; col < nOfColumns; ++col) + { + m_distMatrix[row + nOfRows*col] += h; + } + } + } + if constexpr (HUNGARIAN_LOGS) + std::cout << "step3_5: subtract h from each uncovered column" << std::endl; + for (size_t col = 0; col < nOfColumns; ++col) + { + if (!coveredColumns[col]) + { + for (size_t row = 0; row < nOfRows; ++row) + { + m_distMatrix[row + nOfRows*col] -= h; + } + } + } + } +} + +// -------------------------------------------------------------------------- +// +// -------------------------------------------------------------------------- +void AssignmentProblemSolver::step4(assignments_t& assignment, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim, size_t row, size_t col) +{ + const size_t nOfElements = nOfRows * nOfColumns; + if constexpr (HUNGARIAN_LOGS) + std::cout << "step4: generate temporary copy of starMatrix" << std::endl; + for (size_t n = 0; n < nOfElements; ++n) + { + newStarMatrix[n] = starMatrix[n]; + } + if constexpr (HUNGARIAN_LOGS) + std::cout << "step4: star current zero" << std::endl; + newStarMatrix[row + nOfRows*col] = true; + if constexpr (HUNGARIAN_LOGS) + std::cout << "step4: find starred zero in current column" << std::endl; + size_t starCol = col; + size_t starRow = 0; + for (; starRow < nOfRows; ++starRow) + { + if (starMatrix[starRow + nOfRows * starCol]) + break; + } + while (starRow < nOfRows) + { + // unstar the starred zero + newStarMatrix[starRow + nOfRows*starCol] = false; + // find primed zero in current row + size_t primeRow = starRow; + size_t primeCol = 0; + for (; primeCol < nOfColumns; ++primeCol) + { + if (primeMatrix[primeRow + nOfRows * primeCol]) + break; + } + // star the primed zero + newStarMatrix[primeRow + nOfRows*primeCol] = true; + // find starred zero in current column + starCol = primeCol; + for (starRow = 0; starRow < nOfRows; ++starRow) + { + if (starMatrix[starRow + nOfRows * starCol]) + break; + } + } + // use temporary copy as new starMatrix + // delete all primes, uncover all rows + for (size_t n = 0; n < nOfElements; ++n) + { + primeMatrix[n] = false; + starMatrix[n] = newStarMatrix[n]; + } + for (size_t n = 0; n < nOfRows; ++n) + { + coveredRows[n] = false; + } + if constexpr (HUNGARIAN_LOGS) + std::cout << "move to step 2a" << std::endl; + step2a(assignment, starMatrix, newStarMatrix, primeMatrix, coveredColumns, coveredRows, nOfRows, nOfColumns, minDim); +} + +// -------------------------------------------------------------------------- +// Computes a suboptimal solution. Good for cases without forbidden assignments. +// -------------------------------------------------------------------------- +void AssignmentProblemSolver::assignmentsuboptimal2(assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns) +{ + if constexpr (HUNGARIAN_LOGS) + std::cout << "make working copy of distance Matrix" << std::endl; + m_distMatrix.assign(std::begin(distMatrixIn), std::end(distMatrixIn)); + + if constexpr (HUNGARIAN_LOGS) + std::cout << "recursively search for the minimum element and do the assignment" << std::endl; + for (;;) + { + // find minimum distance observation-to-track pair + track_t minValue = std::numeric_limits::max(); + size_t tmpRow = 0; + size_t tmpCol = 0; + for (size_t row = 0; row < nOfRows; ++row) + { + for (size_t col = 0; col < nOfColumns; ++col) + { + const track_t value = m_distMatrix[row + nOfRows*col]; + if (value != std::numeric_limits::max() && (value < minValue)) + { + minValue = value; + tmpRow = row; + tmpCol = col; + } + } + } + + if (minValue != std::numeric_limits::max()) + { + assignment[tmpRow] = static_cast(tmpCol); + cost += minValue; + for (size_t n = 0; n < nOfRows; ++n) + { + m_distMatrix[n + nOfRows*tmpCol] = std::numeric_limits::max(); + } + for (size_t n = 0; n < nOfColumns; ++n) + { + m_distMatrix[tmpRow + nOfRows*n] = std::numeric_limits::max(); + } + } + else + { + break; + } + } +} +// -------------------------------------------------------------------------- +// Computes a suboptimal solution. Good for cases with many forbidden assignments. +// -------------------------------------------------------------------------- +void AssignmentProblemSolver::assignmentsuboptimal1(assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns) +{ + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentsuboptimal1: make working copy of distance Matrix" << std::endl; + m_distMatrix.assign(std::begin(distMatrixIn), std::end(distMatrixIn)); + + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentsuboptimal1: allocate memory" << std::endl; + int* nOfValidObservations = (int *)calloc(nOfRows, sizeof(int)); + int* nOfValidTracks = (int *)calloc(nOfColumns, sizeof(int)); + + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentsuboptimal1: compute number of validations" << std::endl; + bool infiniteValueFound = false; + bool finiteValueFound = false; + for (size_t row = 0; row < nOfRows; ++row) + { + for (size_t col = 0; col < nOfColumns; ++col) + { + if (m_distMatrix[row + nOfRows*col] != std::numeric_limits::max()) + { + nOfValidTracks[col] += 1; + nOfValidObservations[row] += 1; + finiteValueFound = true; + } + else + { + infiniteValueFound = true; + } + } + } + + if (infiniteValueFound) + { + if (!finiteValueFound) + { + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentsuboptimal1: free allocated memory" << std::endl; + free(nOfValidObservations); + free(nOfValidTracks); + return; + } + bool repeatSteps = true; + + while (repeatSteps) + { + repeatSteps = false; + + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentsuboptimal1: step 1: reject assignments of multiply validated tracks to singly validated observation" << std::endl; + for (size_t col = 0; col < nOfColumns; ++col) + { + bool singleValidationFound = false; + for (size_t row = 0; row < nOfRows; ++row) + { + if (m_distMatrix[row + nOfRows * col] != std::numeric_limits::max() && (nOfValidObservations[row] == 1)) + { + singleValidationFound = true; + break; + } + } + if (singleValidationFound) + { + for (size_t nestedRow = 0; nestedRow < nOfRows; ++nestedRow) + if ((nOfValidObservations[nestedRow] > 1) && m_distMatrix[nestedRow + nOfRows * col] != std::numeric_limits::max()) + { + m_distMatrix[nestedRow + nOfRows * col] = std::numeric_limits::max(); + nOfValidObservations[nestedRow] -= 1; + nOfValidTracks[col] -= 1; + repeatSteps = true; + } + } + } + + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentsuboptimal1: step 2: reject assignments of multiply validated observations to singly validated tracks" << std::endl; + if (nOfColumns > 1) + { + for (size_t row = 0; row < nOfRows; ++row) + { + bool singleValidationFound = false; + for (size_t col = 0; col < nOfColumns; ++col) + { + if (m_distMatrix[row + nOfRows*col] != std::numeric_limits::max() && (nOfValidTracks[col] == 1)) + { + singleValidationFound = true; + break; + } + } + + if (singleValidationFound) + { + for (size_t col = 0; col < nOfColumns; ++col) + { + if ((nOfValidTracks[col] > 1) && m_distMatrix[row + nOfRows*col] != std::numeric_limits::max()) + { + m_distMatrix[row + nOfRows*col] = std::numeric_limits::max(); + nOfValidObservations[row] -= 1; + nOfValidTracks[col] -= 1; + repeatSteps = true; + } + } + } + } + } + } // while(repeatSteps) + + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentsuboptimal1: for each multiply validated track that validates only with singly validated observations, choose the observation with minimum distance" << std::endl; + for (size_t row = 0; row < nOfRows; ++row) + { + if (nOfValidObservations[row] > 1) + { + bool allSinglyValidated = true; + track_t minValue = std::numeric_limits::max(); + size_t tmpCol = 0; + for (size_t col = 0; col < nOfColumns; ++col) + { + const track_t value = m_distMatrix[row + nOfRows*col]; + if (value != std::numeric_limits::max()) + { + if (nOfValidTracks[col] > 1) + { + allSinglyValidated = false; + break; + } + else if ((nOfValidTracks[col] == 1) && (value < minValue)) + { + tmpCol = col; + minValue = value; + } + } + } + + if (allSinglyValidated) + { + assignment[row] = static_cast(tmpCol); + cost += minValue; + for (size_t n = 0; n < nOfRows; ++n) + { + m_distMatrix[n + nOfRows*tmpCol] = std::numeric_limits::max(); + } + for (size_t n = 0; n < nOfColumns; ++n) + { + m_distMatrix[row + nOfRows*n] = std::numeric_limits::max(); + } + } + } + } + + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentsuboptimal1: for each multiply validated observation that validates only with singly validated track, choose the track with minimum distance" << std::endl; + for (size_t col = 0; col < nOfColumns; ++col) + { + if (nOfValidTracks[col] > 1) + { + bool allSinglyValidated = true; + track_t minValue = std::numeric_limits::max(); + size_t tmpRow = 0; + for (size_t row = 0; row < nOfRows; ++row) + { + const track_t value = m_distMatrix[row + nOfRows*col]; + if (value != std::numeric_limits::max()) + { + if (nOfValidObservations[row] > 1) + { + allSinglyValidated = false; + break; + } + else if ((nOfValidObservations[row] == 1) && (value < minValue)) + { + tmpRow = row; + minValue = value; + } + } + } + + if (allSinglyValidated) + { + assignment[tmpRow] = static_cast(col); + cost += minValue; + for (size_t n = 0; n < nOfRows; ++n) + { + m_distMatrix[n + nOfRows*col] = std::numeric_limits::max(); + } + for (size_t n = 0; n < nOfColumns; ++n) + { + m_distMatrix[tmpRow + nOfRows*n] = std::numeric_limits::max(); + } + } + } + } + } // if(infiniteValueFound) + + + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentsuboptimal1: now, recursively search for the minimum element and do the assignment" << std::endl; + for (;;) + { + // find minimum distance observation-to-track pair + track_t minValue = std::numeric_limits::max(); + size_t tmpRow = 0; + size_t tmpCol = 0; + for (size_t row = 0; row < nOfRows; ++row) + { + for (size_t col = 0; col < nOfColumns; ++col) + { + const track_t value = m_distMatrix[row + nOfRows*col]; + if (value != std::numeric_limits::max() && (value < minValue)) + { + minValue = value; + tmpRow = row; + tmpCol = col; + } + } + } + + if (minValue != std::numeric_limits::max()) + { + assignment[tmpRow] = static_cast(tmpCol); + cost += minValue; + for (size_t n = 0; n < nOfRows; ++n) + { + m_distMatrix[n + nOfRows*tmpCol] = std::numeric_limits::max(); + } + for (size_t n = 0; n < nOfColumns; ++n) + { + m_distMatrix[tmpRow + nOfRows*n] = std::numeric_limits::max(); + } + } + else + { + break; + } + } + + if constexpr (HUNGARIAN_LOGS) + std::cout << "assignmentsuboptimal1: free allocated memory" << std::endl; + free(nOfValidObservations); + free(nOfValidTracks); +} diff --git a/src/Tracker/HungarianAlg/HungarianAlg.h b/src/Tracker/HungarianAlg/HungarianAlg.h new file mode 100644 index 000000000..7a27becc3 --- /dev/null +++ b/src/Tracker/HungarianAlg/HungarianAlg.h @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include "defines.h" +// http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=hungarianAlgorithm + +/// +/// \brief The AssignmentProblemSolver class +/// +class AssignmentProblemSolver +{ +public: + enum TMethod + { + optimal, + many_forbidden_assignments, + without_forbidden_assignments + }; + + AssignmentProblemSolver() = default; + ~AssignmentProblemSolver() = default; + track_t Solve(const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns, assignments_t& assignment, TMethod Method = optimal); + +private: + // Computes the optimal assignment (minimum overall costs) using Munkres algorithm. + void assignmentoptimal(assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns); + void buildassignmentvector(assignments_t& assignment, bool *starMatrix, size_t nOfRows, size_t nOfColumns); + void computeassignmentcost(const assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows); + void step2a(assignments_t& assignment, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim); + void step2b(assignments_t& assignment, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim); + void step3_5(assignments_t& assignment, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim); + void step4(assignments_t& assignment, bool *starMatrix, bool *newStarMatrix, bool *primeMatrix, bool *coveredColumns, bool *coveredRows, size_t nOfRows, size_t nOfColumns, size_t minDim, size_t row, size_t col); + + // Computes a suboptimal solution. Good for cases with many forbidden assignments. + void assignmentsuboptimal1(assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns); + // Computes a suboptimal solution. Good for cases with many forbidden assignments. + void assignmentsuboptimal2(assignments_t& assignment, track_t& cost, const distMatrix_t& distMatrixIn, size_t nOfRows, size_t nOfColumns); + + std::vector m_distMatrix; + + static constexpr bool HUNGARIAN_LOGS = false; +}; diff --git a/src/Tracker/Kalman.cpp b/src/Tracker/Kalman.cpp new file mode 100644 index 000000000..4fc2ed50b --- /dev/null +++ b/src/Tracker/Kalman.cpp @@ -0,0 +1,1271 @@ +#include "Kalman.h" +#include +#include + +#ifdef USE_OCV_UKF +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR < 5)) || ((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR == 5) && (CV_VERSION_REVISION < 1)) || (CV_VERSION_MAJOR == 3)) +namespace kalman = cv::tracking; +#else +namespace kalman = cv::detail::tracking; +#endif +#endif + +/// +/// \brief TKalmanFilter::TKalmanFilter +/// \param type +/// \param useAcceleration +/// \param deltaTime +/// \param accelNoiseMag +/// +TKalmanFilter::TKalmanFilter(tracking::KalmanType type, + bool useAcceleration, + track_t deltaTime, // time increment (lower values makes target more "massive") + track_t accelNoiseMag) + : + m_accelNoiseMag(accelNoiseMag), + m_deltaTime(deltaTime), + m_deltaTimeMin(deltaTime), + m_deltaTimeMax(2 * deltaTime), + m_type(type), + m_useAcceleration(useAcceleration) +{ + m_deltaStep = (m_deltaTimeMax - m_deltaTimeMin) / m_deltaStepsCount; +} + +/// +/// \brief TKalmanFilter::CreateLinear +/// \param xy0 +/// \param xyv0 +/// +void TKalmanFilter::CreateLinear(Point_t xy0, Point_t xyv0) +{ + // We don't know acceleration, so, assume it to process noise. + // But we can guess, the range of acceleration values thich can be achieved by tracked object. + // Process noise. (standard deviation of acceleration: m/s^2) + // shows, woh much target can accelerate. + + // 4 state variables, 2 measurements + m_linearKalman.init(4, 2, 0, El_t); + // Transition cv::Matrix + m_linearKalman.transitionMatrix = (cv::Mat_(4, 4) << + 1, 0, m_deltaTime, 0, + 0, 1, 0, m_deltaTime, + 0, 0, 1, 0, + 0, 0, 0, 1); + + // init... + m_lastPointResult = xy0; + m_linearKalman.statePre.at(0) = xy0.x; // x + m_linearKalman.statePre.at(1) = xy0.y; // y + m_linearKalman.statePre.at(2) = xyv0.x; // vx + m_linearKalman.statePre.at(3) = xyv0.y; // vy + + m_linearKalman.statePost.at(0) = xy0.x; + m_linearKalman.statePost.at(1) = xy0.y; + m_linearKalman.statePost.at(2) = xyv0.x; + m_linearKalman.statePost.at(3) = xyv0.y; + + cv::setIdentity(m_linearKalman.measurementMatrix); + + m_linearKalman.processNoiseCov = (cv::Mat_(4, 4) << + pow(m_deltaTime,4.0)/4.0 ,0 ,pow(m_deltaTime,3.0)/2.0 ,0, + 0 ,pow(m_deltaTime,4.0)/4.0 ,0 ,pow(m_deltaTime,3.0)/2.0, + pow(m_deltaTime,3.0)/2.0 ,0 ,pow(m_deltaTime,2.0) ,0, + 0 ,pow(m_deltaTime,3.0)/2.0 ,0 ,pow(m_deltaTime,2.0)); + + + m_linearKalman.processNoiseCov *= m_accelNoiseMag; + + cv::setIdentity(m_linearKalman.measurementNoiseCov, cv::Scalar::all(0.1)); + + cv::setIdentity(m_linearKalman.errorCovPost, cv::Scalar::all(.1)); + + m_initialPoints.reserve(MIN_INIT_VALS); + + m_initialized = true; +} + +/// +/// \brief TKalmanFilter::CreateLinear +/// \param rect0 +/// \param rectv0 +/// +void TKalmanFilter::CreateLinear(cv::Rect_ rect0, Point_t rectv0) +{ + // We don't know acceleration, so, assume it to process noise. + // But we can guess, the range of acceleration values thich can be achieved by tracked object. + // Process noise. (standard deviation of acceleration: m/s^2) + // shows, woh much target can accelerate. + + // 8 state variables (x, y, vx, vy, width, height, vw, vh), 4 measurements (x, y, width, height) + m_linearKalman.init(8, 4, 0, El_t); + // Transition cv::Matrix + m_linearKalman.transitionMatrix = (cv::Mat_(8, 8) << + 1, 0, 0, 0, m_deltaTime, 0, 0, 0, + 0, 1, 0, 0, 0, m_deltaTime, 0, 0, + 0, 0, 1, 0, 0, 0, m_deltaTime, 0, + 0, 0, 0, 1, 0, 0, 0, m_deltaTime / 10.f, + 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1); + + // init... + m_linearKalman.statePre.at(0) = rect0.x; // x + m_linearKalman.statePre.at(1) = rect0.y; // y + m_linearKalman.statePre.at(2) = rect0.width; // width + m_linearKalman.statePre.at(3) = rect0.height; // height + m_linearKalman.statePre.at(4) = rectv0.x; // dx + m_linearKalman.statePre.at(5) = rectv0.y; // dy + m_linearKalman.statePre.at(6) = 0; // dw + m_linearKalman.statePre.at(7) = 0; // dh + + m_linearKalman.statePost.at(0) = rect0.x; + m_linearKalman.statePost.at(1) = rect0.y; + m_linearKalman.statePost.at(2) = rect0.width; + m_linearKalman.statePost.at(3) = rect0.height; + m_linearKalman.statePost.at(4) = rectv0.x; + m_linearKalman.statePost.at(5) = rectv0.y; + m_linearKalman.statePost.at(6) = 0; + m_linearKalman.statePost.at(7) = 0; + + cv::setIdentity(m_linearKalman.measurementMatrix); + + track_t n1 = pow(m_deltaTime, 4.f) / 4.f; + track_t n2 = pow(m_deltaTime, 3.f) / 2.f; + track_t n3 = pow(m_deltaTime, 2.f); + m_linearKalman.processNoiseCov = (cv::Mat_(8, 8) << + n1, 0, 0, 0, n2, 0, 0, 0, + 0, n1, 0, 0, 0, n2, 0, 0, + 0, 0, n1, 0, 0, 0, n2, 0, + 0, 0, 0, n1, 0, 0, 0, n2, + n2, 0, 0, 0, n3, 0, 0, 0, + 0, n2, 0, 0, 0, n3, 0, 0, + 0, 0, n2, 0, 0, 0, n3, 0, + 0, 0, 0, n2, 0, 0, 0, n3); + + m_linearKalman.processNoiseCov *= m_accelNoiseMag; + + cv::setIdentity(m_linearKalman.measurementNoiseCov, cv::Scalar::all(0.1)); + + cv::setIdentity(m_linearKalman.errorCovPost, cv::Scalar::all(.1)); + + m_initialRects.reserve(MIN_INIT_VALS); + + m_initialized = true; +} + +/// +/// \brief TKalmanFilter::CreateLinear +/// \param rrect0 +/// \param rrectv0 +/// +void TKalmanFilter::CreateLinear(cv::RotatedRect rrect0, Point_t rrectv0) +{ + // We don't know acceleration, so, assume it to process noise. + // But we can guess, the range of acceleration values thich can be achieved by tracked object. + // Process noise. (standard deviation of acceleration: m/s^2) + // shows, woh much target can accelerate. + + // 10 state variables (x, y, vx, vy, width, height, vw, vh, angle, vangle), 5 measurements (x, y, width, height, angle) + m_linearKalman.init(10, 5, 0, El_t); + // Transition cv::Matrix + m_linearKalman.transitionMatrix = (cv::Mat_(10, 10) << + 1, 0, 0, 0, 0, m_deltaTime, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, m_deltaTime, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, m_deltaTime, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, m_deltaTime, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, m_deltaTime, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); + // init... + m_linearKalman.statePre.at(0) = rrect0.center.x; // x + m_linearKalman.statePre.at(1) = rrect0.center.y; // y + m_linearKalman.statePre.at(2) = rrect0.size.width; // width + m_linearKalman.statePre.at(3) = rrect0.size.height; // height + m_linearKalman.statePre.at(4) = rrect0.angle; // angle + m_linearKalman.statePre.at(5) = rrectv0.x; // dx + m_linearKalman.statePre.at(6) = rrectv0.y; // dy + m_linearKalman.statePre.at(7) = 0; // dw + m_linearKalman.statePre.at(8) = 0; // dh + m_linearKalman.statePre.at(9) = 0; // da + + m_linearKalman.statePost.at(0) = rrect0.center.x; + m_linearKalman.statePost.at(1) = rrect0.center.y; + m_linearKalman.statePost.at(2) = rrect0.size.width; + m_linearKalman.statePost.at(3) = rrect0.size.height; + m_linearKalman.statePost.at(4) = rrect0.angle; + m_linearKalman.statePost.at(5) = rrectv0.x; + m_linearKalman.statePost.at(6) = rrectv0.y; + m_linearKalman.statePost.at(7) = 0; + m_linearKalman.statePost.at(8) = 0; + m_linearKalman.statePost.at(9) = 0; + + cv::setIdentity(m_linearKalman.measurementMatrix); + + track_t n1 = pow(m_deltaTime, 4.f) / 4.f; + track_t n2 = pow(m_deltaTime, 3.f) / 2.f; + track_t n3 = pow(m_deltaTime, 2.f); + m_linearKalman.processNoiseCov = (cv::Mat_(10, 10) << + n1, 0, 0, 0, 0, n2, 0, 0, 0, 0, + 0, n1, 0, 0, 0, 0, n2, 0, 0, 0, + 0, 0, n1, 0, 0, 0, 0, n2, 0, 0, + 0, 0, 0, n1, 0, 0, 0, 0, n2, 0, + 0, 0, 0, 0, n1, 0, 0, 0, 0, n2, + n2, 0, 0, 0, 0, n3, 0, 0, 0, 0, + 0, n2, 0, 0, 0, 0, n3, 0, 0, 0, + 0, 0, n2, 0, 0, 0, 0, n3, 0, 0, + 0, 0, 0, n2, 0, 0, 0, 0, n3, 0, + 0, 0, 0, 0, n2, 0, 0, 0, 0, n3); + + m_linearKalman.processNoiseCov *= m_accelNoiseMag; + + cv::setIdentity(m_linearKalman.measurementNoiseCov, cv::Scalar::all(0.1)); + + cv::setIdentity(m_linearKalman.errorCovPost, cv::Scalar::all(.1)); + + m_initialRects.reserve(MIN_INIT_VALS); + + m_initialized = true; +} + +/// +/// \brief TKalmanFilter::CreateLinearAcceleration +/// \param xy0 +/// \param xyv0 +/// +void TKalmanFilter::CreateLinearAcceleration(Point_t xy0, Point_t xyv0) +{ + // 6 state variables, 2 measurements + m_linearKalman.init(6, 2, 0, El_t); + // Transition cv::Matrix + const track_t dt = m_deltaTime; + const track_t dt2 = 0.5f * m_deltaTime * m_deltaTime; + m_linearKalman.transitionMatrix = (cv::Mat_(6, 6) << + 1, 0, dt, 0, dt2, 0, + 0, 1, 0, dt, 0, dt2, + 0, 0, 1, 0, dt, 0, + 0, 0, 0, 1, 0, dt, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1); + + // init... + m_lastPointResult = xy0; + m_linearKalman.statePre.at(0) = xy0.x; // x + m_linearKalman.statePre.at(1) = xy0.y; // y + m_linearKalman.statePre.at(2) = xyv0.x; // vx + m_linearKalman.statePre.at(3) = xyv0.y; // vy + m_linearKalman.statePre.at(4) = 0; // ax + m_linearKalman.statePre.at(5) = 0; // ay + + m_linearKalman.statePost.at(0) = xy0.x; + m_linearKalman.statePost.at(1) = xy0.y; + m_linearKalman.statePost.at(2) = xyv0.x; + m_linearKalman.statePost.at(3) = xyv0.y; + m_linearKalman.statePost.at(4) = 0; + m_linearKalman.statePost.at(5) = 0; + + cv::setIdentity(m_linearKalman.measurementMatrix); + + track_t n1 = pow(m_deltaTime, 4.f) / 4.f; + track_t n2 = pow(m_deltaTime, 3.f) / 2.f; + track_t n3 = pow(m_deltaTime, 2.f); + m_linearKalman.processNoiseCov = (cv::Mat_(6, 6) << + n1, 0, n2, 0, n2, 0, + 0, n1, 0, n2, 0, n2, + n2, 0, n3, 0, n3, 0, + 0, n2, 0, n3, 0, n3, + 0, 0, n2, 0, n3, 0, + 0, 0, 0, n2, 0, n3); + + m_linearKalman.processNoiseCov *= m_accelNoiseMag; + + cv::setIdentity(m_linearKalman.measurementNoiseCov, cv::Scalar::all(0.1)); + + cv::setIdentity(m_linearKalman.errorCovPost, cv::Scalar::all(.1)); + + m_initialPoints.reserve(MIN_INIT_VALS); + + m_initialized = true; +} + +/// +/// \brief TKalmanFilter::CreateLinearAcceleration +/// \param rect0 +/// \param rectv0 +/// +void TKalmanFilter::CreateLinearAcceleration(cv::Rect_ rect0, Point_t rectv0) +{ + // 12 state variables (x, y, vx, vy, ax, ay, width, height, vw, vh, aw, ah), 4 measurements (x, y, width, height) + m_linearKalman.init(12, 4, 0, El_t); + // Transition cv::Matrix + const track_t dt = m_deltaTime; + const track_t dt2 = 0.5f * m_deltaTime * m_deltaTime; + m_linearKalman.transitionMatrix = (cv::Mat_(12, 12) << + 1, 0, 0, 0, dt, 0, 0, 0, dt2, 0, dt2, 0, + 0, 1, 0, 0, 0, dt, 0, 0, 0, dt2, 0, dt2, + 0, 0, 1, 0, 0, 0, dt, 0, 0, 0, dt2, 0, + 0, 0, 0, 1, 0, 0, 0, dt, 0, 0, 0, dt2, + 0, 0, 0, 0, 1, 0, 0, 0, dt, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, dt, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, dt, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, dt, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); + + // init... + m_linearKalman.statePre.at(0) = rect0.x; // x + m_linearKalman.statePre.at(1) = rect0.y; // y + m_linearKalman.statePre.at(2) = rect0.width; // width + m_linearKalman.statePre.at(3) = rect0.height; // height + m_linearKalman.statePre.at(4) = rectv0.x; // dx + m_linearKalman.statePre.at(5) = rectv0.y; // dy + m_linearKalman.statePre.at(6) = 0; // dw + m_linearKalman.statePre.at(7) = 0; // dh + m_linearKalman.statePre.at(8) = 0; // ax + m_linearKalman.statePre.at(9) = 0; // ay + m_linearKalman.statePre.at(10) = 0; // aw + m_linearKalman.statePre.at(11) = 0; // ah + + m_linearKalman.statePost.at(0) = rect0.x; + m_linearKalman.statePost.at(1) = rect0.y; + m_linearKalman.statePost.at(2) = rect0.width; + m_linearKalman.statePost.at(3) = rect0.height; + m_linearKalman.statePost.at(4) = rectv0.x; + m_linearKalman.statePost.at(5) = rectv0.y; + m_linearKalman.statePost.at(6) = 0; + m_linearKalman.statePost.at(7) = 0; + m_linearKalman.statePost.at(8) = 0; + m_linearKalman.statePost.at(9) = 0; + m_linearKalman.statePost.at(10) = 0; + m_linearKalman.statePost.at(11) = 0; + + cv::setIdentity(m_linearKalman.measurementMatrix); + + track_t n1 = pow(m_deltaTime, 4.f) / 4.f; + track_t n2 = pow(m_deltaTime, 3.f) / 2.f; + track_t n3 = pow(m_deltaTime, 2.f); + m_linearKalman.processNoiseCov = (cv::Mat_(12, 12) << + n1, 0, 0, 0, n2, 0, 0, 0, n2, 0, n2, 0, + 0, n1, 0, 0, 0, n2, 0, 0, 0, n2, 0, n2, + 0, 0, n1, 0, 0, 0, n2, 0, 0, 0, n2, 0, + 0, 0, 0, n1, 0, 0, 0, n2, 0, 0, 0, n2, + n2, 0, 0, 0, n3, 0, 0, 0, n3, 0, n3, 0, + 0, n2, 0, 0, 0, n3, 0, 0, 0, n3, 0, n3, + 0, 0, n2, 0, 0, 0, n3, 0, 0, 0, n3, 0, + 0, 0, 0, n2, 0, 0, 0, n3, 0, 0, 0, n3, + n2, 0, 0, 0, n3, 0, 0, 0, n3, 0, 0, 0, + 0, n2, 0, 0, 0, n3, 0, 0, 0, n3, 0, 0, + 0, 0, n2, 0, 0, 0, n3, 0, 0, 0, n3, 0, + 0, 0, 0, n2, 0, 0, 0, n3, 0, 0, 0, n3); + + m_linearKalman.processNoiseCov *= m_accelNoiseMag; + + cv::setIdentity(m_linearKalman.measurementNoiseCov, cv::Scalar::all(0.1)); + + cv::setIdentity(m_linearKalman.errorCovPost, cv::Scalar::all(.1)); + + m_initialRects.reserve(MIN_INIT_VALS); + + m_initialized = true; +} + +/// +/// \brief TKalmanFilter::CreateLinearAcceleration +/// \param rect0 +/// \param rectv0 +/// +void TKalmanFilter::CreateLinearAcceleration(cv::RotatedRect /*rrect0*/, Point_t /*rrectv0*/) +{ + // TODO + assert(0); +} + +#ifdef USE_OCV_UKF +//--------------------------------------------------------------------------- +class AcceleratedModel final : public kalman::UkfSystemModel +{ +public: + AcceleratedModel(track_t deltaTime, bool rectModel) + : + kalman::UkfSystemModel(), + m_deltaTime(deltaTime), + m_rectModel(rectModel) + { + } + + void stateConversionFunction(const cv::Mat& x_k, const cv::Mat& u_k, const cv::Mat& v_k, cv::Mat& x_kplus1) + { + track_t x0 = x_k.at(0, 0); + track_t y0 = x_k.at(1, 0); + track_t vx0 = x_k.at(2, 0); + track_t vy0 = x_k.at(3, 0); + track_t ax0 = x_k.at(4, 0); + track_t ay0 = x_k.at(5, 0); + + x_kplus1.at(0, 0) = x0 + vx0 * m_deltaTime + ax0 * sqr(m_deltaTime) / 2; + x_kplus1.at(1, 0) = y0 + vy0 * m_deltaTime + ay0 * sqr(m_deltaTime) / 2; + x_kplus1.at(2, 0) = vx0 + ax0 * m_deltaTime; + x_kplus1.at(3, 0) = vy0 + ay0 * m_deltaTime; + x_kplus1.at(4, 0) = ax0; + x_kplus1.at(5, 0) = ay0; + + if (m_rectModel) + { + x_kplus1.at(6, 0) = x_k.at(6, 0); + x_kplus1.at(7, 0) = x_k.at(7, 0); + } + + if (v_k.size() == u_k.size()) + x_kplus1 += v_k + u_k; + else + x_kplus1 += v_k; + } + + void measurementFunction(const cv::Mat& x_k, const cv::Mat& n_k, cv::Mat& z_k) + { + track_t x0 = x_k.at(0, 0); + track_t y0 = x_k.at(1, 0); + track_t vx0 = x_k.at(2, 0); + track_t vy0 = x_k.at(3, 0); + track_t ax0 = x_k.at(4, 0); + track_t ay0 = x_k.at(5, 0); + + z_k.at(0, 0) = x0 + vx0 * m_deltaTime + ax0 * sqr(m_deltaTime) / 2 + n_k.at(0, 0); + z_k.at(1, 0) = y0 + vy0 * m_deltaTime + ay0 * sqr(m_deltaTime) / 2 + n_k.at(1, 0); + + if (m_rectModel) + { + z_k.at(2, 0) = x_k.at(6, 0); + z_k.at(3, 0) = x_k.at(7, 0); + } + } + +private: + track_t m_deltaTime; + bool m_rectModel; +}; + +//--------------------------------------------------------------------------- +void TKalmanFilter::CreateUnscented(Point_t xy0, Point_t xyv0) +{ + int MP = 2; + int DP = 6; + int CP = 0; + + cv::Mat processNoiseCov = cv::Mat::zeros(DP, DP, Mat_t(1)); + processNoiseCov.at(0, 0) = 1e-14f; + processNoiseCov.at(1, 1) = 1e-14f; + processNoiseCov.at(2, 2) = 1e-6f; + processNoiseCov.at(3, 3) = 1e-6f; + processNoiseCov.at(4, 4) = 1e-6f; + processNoiseCov.at(5, 5) = 1e-6f; + + cv::Mat measurementNoiseCov = cv::Mat::zeros(MP, MP, Mat_t(1)); + measurementNoiseCov.at(0, 0) = 1e-6f; + measurementNoiseCov.at(1, 1) = 1e-6f; + + cv::Mat initState(DP, 1, Mat_t(1)); + initState.at(0, 0) = xy0.x; + initState.at(1, 0) = xy0.y; + initState.at(2, 0) = xyv0.x; + initState.at(3, 0) = xyv0.y; + initState.at(4, 0) = 0; + initState.at(5, 0) = 0; + + cv::Mat P = 1e-6f * cv::Mat::eye(DP, DP, Mat_t(1)); + + cv::Ptr model(new AcceleratedModel(m_deltaTime, false)); + kalman::UnscentedKalmanFilterParams params(DP, MP, CP, 0, 0, model); + params.dataType = Mat_t(1); + params.stateInit = initState.clone(); + params.errorCovInit = P.clone(); + params.measurementNoiseCov = measurementNoiseCov.clone(); + params.processNoiseCov = processNoiseCov.clone(); + + params.alpha = 1.0; + params.beta = 2.0; + params.k = -2.0; + + m_uncsentedKalman = kalman::createUnscentedKalmanFilter(params); + + m_initialized = true; +} + +//--------------------------------------------------------------------------- +void TKalmanFilter::CreateUnscented(cv::Rect_ rect0, Point_t rectv0) +{ + int MP = 4; + int DP = 8; + int CP = 0; + + cv::Mat processNoiseCov = cv::Mat::zeros(DP, DP, Mat_t(1)); + processNoiseCov.at(0, 0) = 1e-3f; + processNoiseCov.at(1, 1) = 1e-3f; + processNoiseCov.at(2, 2) = 1e-3f; + processNoiseCov.at(3, 3) = 1e-3f; + processNoiseCov.at(4, 4) = 1e-3f; + processNoiseCov.at(5, 5) = 1e-3f; + processNoiseCov.at(6, 6) = 1e-3f; + processNoiseCov.at(7, 7) = 1e-3f; + + cv::Mat measurementNoiseCov = cv::Mat::zeros(MP, MP, Mat_t(1)); + measurementNoiseCov.at(0, 0) = 1e-3f; + measurementNoiseCov.at(1, 1) = 1e-3f; + measurementNoiseCov.at(2, 2) = 1e-3f; + measurementNoiseCov.at(3, 3) = 1e-3f; + + cv::Mat initState(DP, 1, Mat_t(1)); + initState.at(0, 0) = rect0.x; + initState.at(1, 0) = rect0.y; + initState.at(2, 0) = rectv0.x; + initState.at(3, 0) = rectv0.y; + initState.at(4, 0) = 0; + initState.at(5, 0) = 0; + initState.at(6, 0) = rect0.width; + initState.at(7, 0) = rect0.height; + + cv::Mat P = 1e-3f * cv::Mat::eye(DP, DP, Mat_t(1)); + + cv::Ptr model(new AcceleratedModel(m_deltaTime, true)); + kalman::UnscentedKalmanFilterParams params(DP, MP, CP, 0, 0, model); + params.dataType = Mat_t(1); + params.stateInit = initState.clone(); + params.errorCovInit = P.clone(); + params.measurementNoiseCov = measurementNoiseCov.clone(); + params.processNoiseCov = processNoiseCov.clone(); + + params.alpha = 1; + params.beta = 2.0; + params.k = -2.0; + + m_uncsentedKalman = kalman::createUnscentedKalmanFilter(params); + + m_initialized = true; +} + +//--------------------------------------------------------------------------- +void TKalmanFilter::CreateAugmentedUnscented(Point_t xy0, Point_t xyv0) +{ + int MP = 2; + int DP = 6; + int CP = 0; + + cv::Mat processNoiseCov = cv::Mat::zeros(DP, DP, Mat_t(1)); + processNoiseCov.at(0, 0) = 1e-14f; + processNoiseCov.at(1, 1) = 1e-14f; + processNoiseCov.at(2, 2) = 1e-6f; + processNoiseCov.at(3, 3) = 1e-6f; + processNoiseCov.at(4, 4) = 1e-6f; + processNoiseCov.at(5, 5) = 1e-6f; + + cv::Mat measurementNoiseCov = cv::Mat::zeros(MP, MP, Mat_t(1)); + measurementNoiseCov.at(0, 0) = 1e-6f; + measurementNoiseCov.at(1, 1) = 1e-6f; + + cv::Mat initState(DP, 1, Mat_t(1)); + initState.at(0, 0) = xy0.x; + initState.at(1, 0) = xy0.y; + initState.at(2, 0) = xyv0.x; + initState.at(3, 0) = xyv0.y; + initState.at(4, 0) = 0; + initState.at(5, 0) = 0; + + cv::Mat P = 1e-6f * cv::Mat::eye(DP, DP, Mat_t(1)); + + cv::Ptr model(new AcceleratedModel(m_deltaTime, false)); + kalman::AugmentedUnscentedKalmanFilterParams params(DP, MP, CP, 0, 0, model); + params.dataType = Mat_t(1); + params.stateInit = initState.clone(); + params.errorCovInit = P.clone(); + params.measurementNoiseCov = measurementNoiseCov.clone(); + params.processNoiseCov = processNoiseCov.clone(); + + params.alpha = 1; + params.beta = 2.0; + params.k = -2.0; + + m_uncsentedKalman = kalman::createAugmentedUnscentedKalmanFilter(params); + + m_initialized = true; +} + +//--------------------------------------------------------------------------- +void TKalmanFilter::CreateAugmentedUnscented(cv::Rect_ rect0, Point_t rectv0) +{ + int MP = 4; + int DP = 8; + int CP = 0; + + cv::Mat processNoiseCov = cv::Mat::zeros(DP, DP, Mat_t(1)); + processNoiseCov.at(0, 0) = 1e-3f; + processNoiseCov.at(1, 1) = 1e-3f; + processNoiseCov.at(2, 2) = 1e-3f; + processNoiseCov.at(3, 3) = 1e-3f; + processNoiseCov.at(4, 4) = 1e-3f; + processNoiseCov.at(5, 5) = 1e-3f; + processNoiseCov.at(6, 6) = 1e-3f; + processNoiseCov.at(7, 7) = 1e-3f; + + cv::Mat measurementNoiseCov = cv::Mat::zeros(MP, MP, Mat_t(1)); + measurementNoiseCov.at(0, 0) = 1e-3f; + measurementNoiseCov.at(1, 1) = 1e-3f; + measurementNoiseCov.at(2, 2) = 1e-3f; + measurementNoiseCov.at(3, 3) = 1e-3f; + + cv::Mat initState(DP, 1, Mat_t(1)); + initState.at(0, 0) = rect0.x; + initState.at(1, 0) = rect0.y; + initState.at(2, 0) = rectv0.x; + initState.at(3, 0) = rectv0.y; + initState.at(4, 0) = 0; + initState.at(5, 0) = 0; + initState.at(6, 0) = rect0.width; + initState.at(7, 0) = rect0.height; + + cv::Mat P = 1e-3f * cv::Mat::eye(DP, DP, Mat_t(1)); + + cv::Ptr model(new AcceleratedModel(m_deltaTime, true)); + kalman::AugmentedUnscentedKalmanFilterParams params(DP, MP, CP, 0, 0, model); + params.dataType = Mat_t(1); + params.stateInit = initState.clone(); + params.errorCovInit = P.clone(); + params.measurementNoiseCov = measurementNoiseCov.clone(); + params.processNoiseCov = processNoiseCov.clone(); + + params.alpha = 1; + params.beta = 2.0; + params.k = -2.0; + + m_uncsentedKalman = kalman::createAugmentedUnscentedKalmanFilter(params); + + m_initialized = true; +} +#endif + +/// +/// \brief TKalmanFilter::GetPointPrediction +/// \return +/// +Point_t TKalmanFilter::GetPointPrediction() +{ + if (m_initialized) + { + cv::Mat prediction; + + switch (m_type) + { + case tracking::KalmanLinear: + prediction = m_linearKalman.predict(); + break; + + case tracking::KalmanUnscented: + case tracking::KalmanAugmentedUnscented: +#ifdef USE_OCV_UKF + prediction = m_uncsentedKalman->predict(); +#else + prediction = m_linearKalman.predict(); + std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + } + + m_lastPointResult = Point_t(prediction.at(0), prediction.at(1)); + } + return m_lastPointResult; +} + +/// +/// \brief TKalmanFilter::Update +/// \param pt +/// \param dataCorrect +/// \return +/// +Point_t TKalmanFilter::Update(Point_t pt, bool dataCorrect) +{ + if (!m_initialized) + { + if (m_initialPoints.size() < MIN_INIT_VALS) + { + if (dataCorrect) + { + m_initialPoints.push_back(pt); + m_lastPointResult = pt; + } + } + if (m_initialPoints.size() >= MIN_INIT_VALS) + { + track_t kx = 0; + track_t bx = 0; + track_t ky = 0; + track_t by = 0; + get_lin_regress_params(m_initialPoints, 0, MIN_INIT_VALS, kx, bx, ky, by); + Point_t xy0(kx * (MIN_INIT_VALS - 1) + bx, ky * (MIN_INIT_VALS - 1) + by); + Point_t xyv0(kx, ky); + + switch (m_type) + { + case tracking::KalmanLinear: + if (m_useAcceleration) + CreateLinearAcceleration(xy0, xyv0); + else + CreateLinear(xy0, xyv0); + break; + + case tracking::KalmanUnscented: +#ifdef USE_OCV_UKF + CreateUnscented(xy0, xyv0); +#else + if (m_useAcceleration) + CreateLinearAcceleration(xy0, xyv0); + else + CreateLinear(xy0, xyv0); + std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + + case tracking::KalmanAugmentedUnscented: +#ifdef USE_OCV_UKF + CreateAugmentedUnscented(xy0, xyv0); +#else + if (m_useAcceleration) + CreateLinearAcceleration(xy0, xyv0); + else + CreateLinear(xy0, xyv0); + std::cerr << "AugmentedUnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + } + m_lastDist = 0; + } + } + + if (m_initialized) + { + cv::Mat measurement(2, 1, Mat_t(1)); + if (!dataCorrect) + { + measurement.at(0) = m_lastPointResult.x; //update using prediction + measurement.at(1) = m_lastPointResult.y; + } + else + { + measurement.at(0) = pt.x; //update using measurements + measurement.at(1) = pt.y; + } + // Correction + cv::Mat estimated; + switch (m_type) + { + case tracking::KalmanLinear: + { + estimated = m_linearKalman.correct(measurement); + + // Inertia correction + if (!m_useAcceleration) + { + track_t currDist = sqrtf(sqr(estimated.at(0) - pt.x) + sqr(estimated.at(1) - pt.y)); + if (currDist > m_lastDist) + m_deltaTime = std::min(m_deltaTime + m_deltaStep, m_deltaTimeMax); + else + m_deltaTime = std::max(m_deltaTime - m_deltaStep, m_deltaTimeMin); + + m_lastDist = currDist; + + m_linearKalman.transitionMatrix.at(0, 2) = m_deltaTime; + m_linearKalman.transitionMatrix.at(1, 3) = m_deltaTime; + } + break; + } + + case tracking::KalmanUnscented: + case tracking::KalmanAugmentedUnscented: +#ifdef USE_OCV_UKF + estimated = m_uncsentedKalman->correct(measurement); +#else + estimated = m_linearKalman.correct(measurement); + std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + } + + m_lastPointResult.x = estimated.at(0); //update using measurements + m_lastPointResult.y = estimated.at(1); + } + else + { + if (dataCorrect) + m_lastPointResult = pt; + } + return m_lastPointResult; +} + +/// +/// \brief TKalmanFilter::GetRectPrediction +/// \return +/// +cv::Rect TKalmanFilter::GetRectPrediction() +{ + if (m_initialized) + { + cv::Mat prediction; + + switch (m_type) + { + case tracking::KalmanLinear: + prediction = m_linearKalman.predict(); + break; + + case tracking::KalmanUnscented: + case tracking::KalmanAugmentedUnscented: +#ifdef USE_OCV_UKF + prediction = m_uncsentedKalman->predict(); +#else + prediction = m_linearKalman.predict(); + std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + } + + m_lastRectResult = cv::Rect_(prediction.at(0), prediction.at(1), prediction.at(2), prediction.at(3)); + } + return cv::Rect(static_cast(m_lastRectResult.x), static_cast(m_lastRectResult.y), static_cast(m_lastRectResult.width), static_cast(m_lastRectResult.height)); +} + +/// +/// \brief TKalmanFilter::Update +/// \param rect +/// \param dataCorrect +/// \return +/// +cv::Rect TKalmanFilter::Update(cv::Rect rect, bool dataCorrect) +{ + if (!m_initialized) + { + if (m_initialRects.size() < MIN_INIT_VALS) + { + if (dataCorrect) + { + m_initialRects.push_back(rect); + m_lastRectResult.x = static_cast(rect.x); + m_lastRectResult.y = static_cast(rect.y); + m_lastRectResult.width = static_cast(rect.width); + m_lastRectResult.height = static_cast(rect.height); + } + } + if (m_initialRects.size() == MIN_INIT_VALS) + { + std::vector initialPoints; + Point_t averageSize(0, 0); + for (const auto& r : m_initialRects) + { + initialPoints.emplace_back(static_cast(r.x), static_cast(r.y)); + averageSize.x += r.width; + averageSize.y += r.height; + } + averageSize.x /= MIN_INIT_VALS; + averageSize.y /= MIN_INIT_VALS; + + track_t kx = 0; + track_t bx = 0; + track_t ky = 0; + track_t by = 0; + get_lin_regress_params(initialPoints, 0, MIN_INIT_VALS, kx, bx, ky, by); + cv::Rect_ rect0(kx * (MIN_INIT_VALS - 1) + bx, ky * (MIN_INIT_VALS - 1) + by, averageSize.x, averageSize.y); + Point_t rectv0(kx, ky); + + switch (m_type) + { + case tracking::KalmanLinear: + if (m_useAcceleration) + CreateLinearAcceleration(rect0, rectv0); + else + CreateLinear(rect0, rectv0); + break; + + case tracking::KalmanUnscented: +#ifdef USE_OCV_UKF + CreateUnscented(rect0, rectv0); +#else + if (m_useAcceleration) + CreateLinearAcceleration(rect0, rectv0); + else + CreateLinear(rect0, rectv0); + std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + + case tracking::KalmanAugmentedUnscented: +#ifdef USE_OCV_UKF + CreateAugmentedUnscented(rect0, rectv0); +#else + if (m_useAcceleration) + CreateLinearAcceleration(rect0, rectv0); + else + CreateLinear(rect0, rectv0); + std::cerr << "AugmentedUnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + } + } + } + + if (m_initialized) + { + cv::Mat measurement(4, 1, Mat_t(1)); + if (!dataCorrect) + { + measurement.at(0) = m_lastRectResult.x; // update using prediction + measurement.at(1) = m_lastRectResult.y; + measurement.at(2) = m_lastRectResult.width; + measurement.at(3) = m_lastRectResult.height; + } + else + { + measurement.at(0) = static_cast(rect.x); // update using measurements + measurement.at(1) = static_cast(rect.y); + measurement.at(2) = static_cast(rect.width); + measurement.at(3) = static_cast(rect.height); + } + // Correction + cv::Mat estimated; + switch (m_type) + { + case tracking::KalmanLinear: + { + estimated = m_linearKalman.correct(measurement); + + m_lastRectResult.x = estimated.at(0); //update using measurements + m_lastRectResult.y = estimated.at(1); + m_lastRectResult.width = estimated.at(2); + m_lastRectResult.height = estimated.at(3); + + // Inertia correction + if (!m_useAcceleration) + { + track_t currDist = sqrtf(sqr(estimated.at(0) - rect.x) + sqr(estimated.at(1) - rect.y) + sqr(estimated.at(2) - rect.width) + sqr(estimated.at(3) - rect.height)); + if (currDist > m_lastDist) + m_deltaTime = std::min(m_deltaTime + m_deltaStep, m_deltaTimeMax); + else + m_deltaTime = std::max(m_deltaTime - m_deltaStep, m_deltaTimeMin); + + m_lastDist = currDist; + + m_linearKalman.transitionMatrix.at(0, 4) = m_deltaTime; + m_linearKalman.transitionMatrix.at(1, 5) = m_deltaTime; + m_linearKalman.transitionMatrix.at(2, 6) = m_deltaTime; + m_linearKalman.transitionMatrix.at(3, 7) = m_deltaTime; + } + break; + } + + case tracking::KalmanUnscented: + case tracking::KalmanAugmentedUnscented: +#ifdef USE_OCV_UKF + estimated = m_uncsentedKalman->correct(measurement); + + m_lastRectResult.x = estimated.at(0); //update using measurements + m_lastRectResult.y = estimated.at(1); + m_lastRectResult.width = estimated.at(6); + m_lastRectResult.height = estimated.at(7); +#else + estimated = m_linearKalman.correct(measurement); + + m_lastRectResult.x = estimated.at(0); //update using measurements + m_lastRectResult.y = estimated.at(1); + m_lastRectResult.width = estimated.at(2); + m_lastRectResult.height = estimated.at(3); + std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + } + } + else + { + if (dataCorrect) + { + m_lastRectResult.x = static_cast(rect.x); + m_lastRectResult.y = static_cast(rect.y); + m_lastRectResult.width = static_cast(rect.width); + m_lastRectResult.height = static_cast(rect.height); + } + } + return cv::Rect(static_cast(m_lastRectResult.x), static_cast(m_lastRectResult.y), static_cast(m_lastRectResult.width), static_cast(m_lastRectResult.height)); +} + +/// +/// \brief TKalmanFilter::GetRRectPrediction +/// \return +/// +cv::RotatedRect TKalmanFilter::GetRRectPrediction() +{ + if (m_initialized) + { + cv::Mat prediction; + + switch (m_type) + { + case tracking::KalmanLinear: + prediction = m_linearKalman.predict(); + break; + + case tracking::KalmanUnscented: + case tracking::KalmanAugmentedUnscented: +#ifdef USE_OCV_UKF + prediction = m_uncsentedKalman->predict(); +#else + prediction = m_linearKalman.predict(); + std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + } + + m_lastRRectResult.center.x = prediction.at(0); //update using measurements + m_lastRRectResult.center.y = prediction.at(1); + m_lastRRectResult.size.width = prediction.at(2); + m_lastRRectResult.size.height = prediction.at(3); + m_lastRRectResult.angle = prediction.at(4); + } + return m_lastRRectResult; +} + +/// +/// \brief TKalmanFilter::Update +/// \param rrect +/// \param dataCorrect +/// \return +/// +cv::RotatedRect TKalmanFilter::Update(cv::RotatedRect rrect, bool dataCorrect) +{ + if (!m_initialized) + { + if (m_initialRRects.size() < MIN_INIT_VALS) + { + if (dataCorrect) + { + m_initialRRects.push_back(rrect); + m_lastRRectResult = rrect; + } + } + if (m_initialRRects.size() == MIN_INIT_VALS) + { + std::vector initialPoints; + Point_t averageSize(0, 0); + track_t averageAngle = 0; + for (const auto& rr : m_initialRRects) + { + initialPoints.emplace_back(static_cast(rr.center.x), static_cast(rr.center.y)); + averageSize.x += rr.size.width; + averageSize.y += rr.size.height; + averageAngle += rr.angle; + } + averageSize.x /= MIN_INIT_VALS; + averageSize.y /= MIN_INIT_VALS; + averageAngle /= MIN_INIT_VALS; + + track_t kx = 0; + track_t bx = 0; + track_t ky = 0; + track_t by = 0; + get_lin_regress_params(initialPoints, 0, MIN_INIT_VALS, kx, bx, ky, by); + cv::RotatedRect rrect0(cv::Point2f(kx * (MIN_INIT_VALS - 1) + bx, ky * (MIN_INIT_VALS - 1) + by), averageSize, averageAngle); + Point_t rrectv0(kx, ky); + + switch (m_type) + { + case tracking::KalmanLinear: + if (m_useAcceleration) + CreateLinearAcceleration(rrect0, rrectv0); + else + CreateLinear(rrect0, rrectv0); + break; + + case tracking::KalmanUnscented: +#ifdef USE_OCV_UKF + assert(0); + //TODO: CreateUnscented(rrect0, rrectv0); +#else + if (m_useAcceleration) + CreateLinearAcceleration(rrect0, rrectv0); + else + CreateLinear(rrect0, rrectv0); + std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + + case tracking::KalmanAugmentedUnscented: +#ifdef USE_OCV_UKF + assert(0); + // TODO: CreateAugmentedUnscented(rrect0, rrectv0); +#else + if (m_useAcceleration) + CreateLinearAcceleration(rrect0, rrectv0); + else + CreateLinear(rrect0, rrectv0); + std::cerr << "AugmentedUnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + } + } + } + + if (m_initialized) + { + cv::Mat measurement(5, 1, Mat_t(1)); + if (!dataCorrect) + { + measurement.at(0) = m_lastRRectResult.center.x; // update using prediction + measurement.at(1) = m_lastRRectResult.center.y; + measurement.at(2) = m_lastRRectResult.size.width; + measurement.at(3) = m_lastRRectResult.size.height; + measurement.at(4) = m_lastRRectResult.angle; + } + else + { + measurement.at(0) = static_cast(rrect.center.x); // update using measurements + measurement.at(1) = static_cast(rrect.center.y); + measurement.at(2) = static_cast(rrect.size.width); + measurement.at(3) = static_cast(rrect.size.height); + measurement.at(4) = static_cast(rrect.angle); + } + // Correction + cv::Mat estimated; + switch (m_type) + { + case tracking::KalmanLinear: + { + estimated = m_linearKalman.correct(measurement); + + m_lastRRectResult.center.x = estimated.at(0); //update using measurements + m_lastRRectResult.center.y = estimated.at(1); + m_lastRRectResult.size.width = estimated.at(2); + m_lastRRectResult.size.height = estimated.at(3); + m_lastRRectResult.angle = estimated.at(4); + + // Inertia correction + if (!m_useAcceleration) + { + track_t currDist = sqrtf(sqr(estimated.at(0) - rrect.center.x) + sqr(estimated.at(1) - rrect.center.y) + + sqr(estimated.at(2) - rrect.size.width) + sqr(estimated.at(3) - rrect.size.height)); + if (currDist > m_lastDist) + m_deltaTime = std::min(m_deltaTime + m_deltaStep, m_deltaTimeMax); + else + m_deltaTime = std::max(m_deltaTime - m_deltaStep, m_deltaTimeMin); + + m_lastDist = currDist; + + m_linearKalman.transitionMatrix.at(0, 5) = m_deltaTime; + m_linearKalman.transitionMatrix.at(1, 6) = m_deltaTime; + m_linearKalman.transitionMatrix.at(2, 7) = m_deltaTime; + m_linearKalman.transitionMatrix.at(3, 8) = m_deltaTime; + m_linearKalman.transitionMatrix.at(4, 9) = m_deltaTime; + } + break; + } + + case tracking::KalmanUnscented: + case tracking::KalmanAugmentedUnscented: +#ifdef USE_OCV_UKF + estimated = m_uncsentedKalman->correct(measurement); + + m_lastRRectResult.center.x = estimated.at(0); //update using measurements + m_lastRRectResult.center.y = estimated.at(1); + m_lastRRectResult.size.width = estimated.at(6); + m_lastRRectResult.size.height = estimated.at(7); + m_lastRRectResult.angle = estimated.at(9); +#else + estimated = m_linearKalman.correct(measurement); + + m_lastRRectResult.center.x = estimated.at(0); //update using measurements + m_lastRRectResult.center.y = estimated.at(1); + m_lastRRectResult.size.width = estimated.at(2); + m_lastRRectResult.size.height = estimated.at(3); + m_lastRRectResult.angle = estimated.at(4); + std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + } + } + else + { + if (dataCorrect) + m_lastRRectResult = rrect; + } + return m_lastRRectResult; +} + +/// +/// \brief TKalmanFilter::GetVelocity +/// \return +/// +cv::Vec TKalmanFilter::GetVelocity() const +{ + cv::Vec res(0, 0); + if (m_initialized) + { + switch (m_type) + { + case tracking::KalmanLinear: + { + if (m_linearKalman.statePre.rows > 3) + { + int indX = 2; + int indY = 3; + if (m_linearKalman.statePre.rows > 4) + { + indX = 4; + indY = 5; + } + //std::cout << "indX = " << indX << ", indY = " << indY << std::endl; + res[0] = m_linearKalman.statePre.at(indX); + res[1] = m_linearKalman.statePre.at(indY); + } + break; + } + + case tracking::KalmanUnscented: + case tracking::KalmanAugmentedUnscented: +#ifdef USE_OCV_UKF + cv::Mat state = m_uncsentedKalman->getState(); + res[0] = state.at(2); + res[1] = state.at(3); +#else + std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + } + } + return res; +} + +//--------------------------------------------------------------------------- +void TKalmanFilter::GetPtStateAndResCov(cv::Mat& covar, cv::Mat& state) const +{ + if (m_initialized) + { + switch (m_type) + { + case tracking::KalmanLinear: + { + state = m_linearKalman.statePost.clone(); + covar = m_linearKalman.processNoiseCov.clone(); + break; + } + + case tracking::KalmanUnscented: + case tracking::KalmanAugmentedUnscented: +#ifdef USE_OCV_UKF + state = m_uncsentedKalman->getState(); +#else + std::cerr << "UnscentedKalmanFilter was disabled in CMAKE! Set KalmanLinear in constructor." << std::endl; +#endif + break; + } + } +} + diff --git a/src/Tracker/Kalman.h b/src/Tracker/Kalman.h new file mode 100644 index 000000000..6864f783d --- /dev/null +++ b/src/Tracker/Kalman.h @@ -0,0 +1,84 @@ +#pragma once +#include "defines.h" +#include +#include + +#include + +#ifdef USE_OCV_UKF +#include +#include +#endif + +/// +/// \brief The TKalmanFilter class +/// http://www.morethantechnical.com/2011/06/17/simple-kalman-filter-for-tracking-using-opencv-2-2-w-code/ +/// +class TKalmanFilter +{ +public: + TKalmanFilter(tracking::KalmanType type, bool useAcceleration, track_t deltaTime, track_t accelNoiseMag); + ~TKalmanFilter() = default; + + Point_t GetPointPrediction(); + Point_t Update(Point_t pt, bool dataCorrect); + + cv::Rect GetRectPrediction(); + cv::Rect Update(cv::Rect rect, bool dataCorrect); + + cv::RotatedRect GetRRectPrediction(); + cv::RotatedRect Update(cv::RotatedRect rrect, bool dataCorrect); + + cv::Vec GetVelocity() const; + + void GetPtStateAndResCov(cv::Mat& covar, cv::Mat& state) const; + +private: + cv::KalmanFilter m_linearKalman; +#ifdef USE_OCV_UKF +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR < 5)) || ((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR == 5) && (CV_VERSION_REVISION < 1)) || (CV_VERSION_MAJOR == 3)) + cv::Ptr m_uncsentedKalman; +#else + cv::Ptr m_uncsentedKalman; +#endif +#endif + + static constexpr size_t MIN_INIT_VALS = 2; + std::vector m_initialPoints; + std::vector m_initialRects; + std::vector m_initialRRects; + + cv::RotatedRect m_lastRRectResult; + cv::Rect_ m_lastRectResult; + cv::Rect_ m_lastRect; + Point_t m_lastPointResult; + track_t m_accelNoiseMag = 0.5f; + track_t m_deltaTime = 0.2f; + track_t m_deltaTimeMin = 0.2f; + track_t m_deltaTimeMax = 2 * 0.2f; + track_t m_lastDist = 0; + track_t m_deltaStep = 0; + static constexpr int m_deltaStepsCount = 20; + tracking::KalmanType m_type = tracking::KalmanLinear; + bool m_useAcceleration = false; // If set true then will be used motion model x(t) = x0 + v0 * t + a * t^2 / 2 + bool m_initialized = false; + + // Constant velocity model + void CreateLinear(Point_t xy0, Point_t xyv0); + void CreateLinear(cv::Rect_ rect0, Point_t rectv0); + void CreateLinear(cv::RotatedRect rrect0, Point_t rrectv0); + + // Constant acceleration model + // https://www.mathworks.com/help/driving/ug/linear-kalman-filters.html + void CreateLinearAcceleration(Point_t xy0, Point_t xyv0); + void CreateLinearAcceleration(cv::Rect_ rect0, Point_t rectv0); + void CreateLinearAcceleration(cv::RotatedRect rrect0, Point_t rrectv0); + +#ifdef USE_OCV_UKF + void CreateUnscented(Point_t xy0, Point_t xyv0); + void CreateUnscented(cv::Rect_ rect0, Point_t rectv0); + void CreateAugmentedUnscented(Point_t xy0, Point_t xyv0); + void CreateAugmentedUnscented(cv::Rect_ rect0, Point_t rectv0); +#endif +}; + diff --git a/src/Tracker/LAPJV_algorithm/lap.cpp b/src/Tracker/LAPJV_algorithm/lap.cpp new file mode 100644 index 000000000..594ab0391 --- /dev/null +++ b/src/Tracker/LAPJV_algorithm/lap.cpp @@ -0,0 +1,265 @@ +/************************************************************************ +* +* lap.cpp + version 1.0 - 4 September 1996 + author: Roy Jonker @ MagicLogic Optimization Inc. + e-mail: roy_jonker@magiclogic.com + + Code for Linear Assignment Problem, according to + + "A Shortest Augmenting Path Algorithm for Dense and Sparse Linear + Assignment Problems," Computing 38, 325-340, 1987 + + by + + R. Jonker and A. Volgenant, University of Amsterdam. + +* + CHANGED 2016-05-13 by Yong Yang(yongyanglink@gmail.com) in column reduction part according to + matlab version of LAPJV algorithm(Copyright (c) 2010, Yi Cao All rights reserved)-- + https://www.mathworks.com/matlabcentral/fileexchange/26836-lapjv-jonker-volgenant-algorithm-for-linear-assignment-problem-v3-0: +* +*************************************************************************/ + +#include +#include "lap.h" + + +/*This function is the jv shortest augmenting path algorithm to solve the assignment problem*/ +cost lap(const std::vector>& assigncost, + std::vector& rowsol, + std::vector& colsol, + std::vector& u, + std::vector& v) + +// input: +// assigncost - cost matrix + +// output: +// rowsol - column assigned to row in solution +// colsol - row assigned to column in solution +// u - dual variables, row reduction numbers +// v - dual variables, column reduction numbers + +{ + int dimRows = assigncost.size(); + int dimCols = assigncost[0].size(); + bool unassignedfound = false; + row numfree = 0; + col j2 = 0, endofpath = 0, last = 0; + cost min = std::numeric_limits::max(); + + std::vector freeunassigned(dimRows); // list of unassigned rows. + std::vector collist(dimCols); // list of columns to be scanned in various ways. + std::vector matches(dimRows, 0); // counts how many times a row could be assigned. + std::vector d(dimCols); // 'cost-distance' in augmenting path calculation. + std::vector pred(dimCols); // row-predecessor of column in augmenting/alternating path. + + // COLUMN REDUCTION + for (col j = dimCols; j--;) // reverse order gives better results. + { + // find minimum cost over rows. + min = assigncost[0][j]; + row imin = 0; + for (row i = 1; i < dimRows; i++) + if (assigncost[i][j] < min) { + min = assigncost[i][j]; + imin = i; + } + v[j] = min; + if (++matches[imin] == 1) { + // init assignment if minimum row assigned for first time. + rowsol[imin] = j; + colsol[j] = imin; + } else if (v[j] < v[rowsol[imin]]) { + int j1 = rowsol[imin]; + rowsol[imin] = j; + colsol[j] = imin; + colsol[j1] = -1; + } else + colsol[j] = -1; // row already assigned, column not assigned. + } + + // REDUCTION TRANSFER + for (row i = 0; i < dimRows; i++) + if (matches[i] == 0) // fill list of unassigned 'free' rows. + freeunassigned[numfree++] = i; + else if (matches[i] == 1) // transfer reduction from rows that are assigned once. + { + col j1 = rowsol[i]; + min = std::numeric_limits::max(); + for (col j = 0; j < dimCols; j++) + if (j != j1) + if (assigncost[i][j] - v[j] < min) min = assigncost[i][j] - v[j]; + v[j1] = v[j1] - min; + } + + // AUGMENTING ROW REDUCTION + int loopcnt = 0; // do-loop to be done twice. + do { + loopcnt++; + + // scan all free rows. + // in some cases, a free row may be replaced with another one to be scanned next. + row k = 0; + row prvnumfree = numfree; + numfree = 0; // start list of rows still free after augmenting row reduction. + while (k < prvnumfree) { + row i = freeunassigned[k]; + k++; + + // find minimum and second minimum reduced cost over columns. + cost umin = assigncost[i][0] - v[0]; + col j1 = 0; + cost usubmin = std::numeric_limits::max(); + for (col j = 1; j < dimCols; j++) { + cost h = assigncost[i][j] - v[j]; + if (h < usubmin) + if (h >= umin) { + usubmin = h; + j2 = j; + } else { + usubmin = umin; + umin = h; + j2 = j1; + j1 = j; + } + } + + row i0 = colsol[j1]; + if (umin < usubmin) + // change the reduction of the minimum column to increase the minimum + // reduced cost in the row to the subminimum. + v[j1] = v[j1] - (usubmin - umin); + else // minimum and subminimum equal. + if (i0 > -1) // minimum column j1 is assigned. + { + // swap columns j1 and j2, as j2 may be unassigned. + j1 = j2; + i0 = colsol[j2]; + } + + // (re-)assign i to j1, possibly de-assigning an i0. + rowsol[i] = j1; + colsol[j1] = i; + + if (i0 > -1) // minimum column j1 assigned earlier. + if (umin < usubmin) + // put in current k, and go back to that k. + // continue augmenting path i - j1 with i0. + freeunassigned[--k] = i0; + else + // no further augmenting reduction possible. + // store i0 in list of free rows for next phase. + freeunassigned[numfree++] = i0; + } + } while (loopcnt < 2); // repeat once. + + // AUGMENT SOLUTION for each free row. + for (row f = 0; f < numfree; f++) { + row freerow = freeunassigned[f]; // start row of augmenting path. + + // Dijkstra shortest path algorithm. + // runs until unassigned column added to shortest path tree. + for (col j = dimCols; j--;) { + d[j] = assigncost[freerow][j] - v[j]; + pred[j] = freerow; + collist[j] = j; // init column list. + } + + col low = 0; // columns in 0..low-1 are ready, now none. + col up = 0; // columns in low..up-1 are to be scanned for current minimum, now none. + // columns in up..dim-1 are to be considered later to find new minimum, + // at this stage the list simply contains all columns + unassignedfound = false; + do { + if (up == low) // no more columns to be scanned for current minimum. + { + last = low - 1; + + // scan columns for up..dim-1 to find all indices for which new minimum occurs. + // store these indices between low..up-1 (increasing up). + min = d[collist[up++]]; + for (row k = up; k < dimRows; k++) { + col j = collist[k]; + cost h = d[j]; + if (h <= min) { + if (h < min) // new minimum. + { + up = low; // restart list at index low. + min = h; + } + // new index with same minimum, put on undex up, and extend list. + collist[k] = collist[up]; + collist[up++] = j; + } + } + // check if any of the minimum columns happens to be unassigned. + // if so, we have an augmenting path right away. + for (row k = low; k < up; k++) + if (colsol[collist[k]] < 0) { + endofpath = collist[k]; + unassignedfound = true; + break; + } + } + + if (!unassignedfound) { + // update 'distances' between freerow and all unscanned columns, via next scanned + // column. + col j1 = collist[low]; + low++; + row i = colsol[j1]; + cost h = assigncost[i][j1] - v[j1] - min; + + for (row k = up; k < dimRows; k++) { + col j = collist[k]; + cost v2 = assigncost[i][j] - v[j] - h; + if (v2 < d[j]) { + pred[j] = i; + if (v2 == min) // new column found at same minimum value + if (colsol[j] < 0) { + // if unassigned, shortest augmenting path is complete. + endofpath = j; + unassignedfound = true; + break; + } + // else add to list to be scanned right away. + else { + collist[k] = collist[up]; + collist[up++] = j; + } + d[j] = v2; + } + } + } + } while (!unassignedfound); + + // update column prices. + for (row k = last + 1; k--;) { + col j1 = collist[k]; + v[j1] = v[j1] + d[j1] - min; + } + + // reset row and column assignments along the alternating path. + row i = 0; + do { + i = pred[endofpath]; + colsol[endofpath] = i; + col j1 = endofpath; + endofpath = rowsol[i]; + rowsol[i] = j1; + } while (i != freerow); + } + + // calculate optimal cost. + cost lapcost = 0; + // for (i = 0; i < dim; i++) + for (row i = dimRows; i--;) { + col j = rowsol[i]; + u[i] = assigncost[i][j] - v[j]; + lapcost = lapcost + assigncost[i][j]; + } + + return lapcost; +} diff --git a/src/Tracker/LAPJV_algorithm/lap.h b/src/Tracker/LAPJV_algorithm/lap.h new file mode 100644 index 000000000..3a11410ad --- /dev/null +++ b/src/Tracker/LAPJV_algorithm/lap.h @@ -0,0 +1,31 @@ +/************************************************************************ +* +* lap.h + version 1.0 - 21 june 1996 + author Roy Jonker, MagicLogic Optimization Inc. + + header file for LAP +* + pyLAPJV by Harold Cooper (hbc@mit.edu) + 2004-08-13: + -- fixed Jonker's function declarations to actually use row, col, + and cost types + -- row, col, and cost now based on basic types +* +**************************************************************************/ + +#include + +/*************** TYPES *******************/ + +typedef int row; +typedef int col; +typedef double cost; + +/*************** FUNCTIONS *******************/ + +extern cost lap(const std::vector>& assigncost, + std::vector& rowsol, + std::vector& colsol, + std::vector& u, + std::vector& v); diff --git a/src/Tracker/ShortPathCalculator.cpp b/src/Tracker/ShortPathCalculator.cpp new file mode 100644 index 000000000..0d2a9a4c8 --- /dev/null +++ b/src/Tracker/ShortPathCalculator.cpp @@ -0,0 +1,65 @@ +#include "ShortPathCalculator.h" + +#include "LAPJV_algorithm/lap.h" + +/// +void SPLAPJV::Solve(const distMatrix_t& costMatrix, size_t colsTracks, size_t rowsRegions, assignments_t& assignmentT2R, track_t /*maxCost*/) +{ + //std::cout << "SPLAPJV::Solve: colsTracks = " << colsTracks << ", rowsRegions = " << rowsRegions << std::endl; + + if (!colsTracks || !rowsRegions) + return; + + bool swithReg2Track = (rowsRegions > colsTracks); // For this algorithm rows <= cols + + size_t dimRows = swithReg2Track ? colsTracks : rowsRegions; // Set the dimension of matrix to 10, dim is the problem size + size_t dimCols = swithReg2Track ? rowsRegions : colsTracks; + std::vector> costMat; // A matrix to store all the costs from vertex i to vertex j + std::vector rowsol(dimRows, -1); // An array to store column indexes assigned to row in solution + std::vector colsol(dimCols, -1); // An array to store row indexes assigned to column in solution + std::vector u(dimRows); // u - dual variables, row reduction numbers + std::vector v(dimCols); // v - dual variables, column reduction numbers + + costMat.resize(dimRows); + for (size_t i = 0; i < dimRows; i++) + { + costMat[i].resize(dimCols); + for (size_t j = 0; j < dimCols; ++j) + { + costMat[i][j] = swithReg2Track ? costMatrix[j * colsTracks + i] : costMatrix[i * colsTracks + j]; + + //std::cout << std::fixed << std::setw(2) << std::setprecision(2) << costMat[i][j] << " "; + } + //std::cout << std::endl; + } + //std::cout << "Cost matrix created" << std::endl; + cost totalCost = lap(costMat, rowsol, colsol, u, v); // Use lap algorithm to calculate the minimum total cost + //std::cout << "totalCost = " << totalCost << std::endl; + + //for (size_t i = 0; i < rowsol.size(); ++i) + //{ + // std::cout << "row[" << i << "]: " << rowsol[i] << ", u = " << u[i] << std::endl; + //} + //for (size_t i = 0; i < colsol.size(); ++i) + //{ + // std::cout << "col[" << i << "]: " << colsol[i] << ", u = " << v[i] << std::endl; + //} + + + if (swithReg2Track) + { + for (size_t i = 0; i < colsol.size(); ++i) + { + if (colsol[i] >= 0) + assignmentT2R[colsol[i]] = static_cast(i); + } + } + else + { + for (size_t i = 0; i < colsol.size(); ++i) + { + if (colsol[i] >= 0) + assignmentT2R[i] = colsol[i]; + } + } +} diff --git a/src/Tracker/ShortPathCalculator.h b/src/Tracker/ShortPathCalculator.h new file mode 100644 index 000000000..6ae90b06f --- /dev/null +++ b/src/Tracker/ShortPathCalculator.h @@ -0,0 +1,82 @@ +#pragma once +#include "defines.h" +#include "HungarianAlg/HungarianAlg.h" + +/// +/// \brief The SPSettings struct +/// +struct SPSettings +{ + track_t m_distThres = 0.8f; + size_t m_maxHistory = 10; +}; + +/// +/// \brief The ShortPathCalculator class +/// +class ShortPathCalculator +{ +public: + ShortPathCalculator(const SPSettings& settings) + : m_settings(settings) + { + } + virtual ~ShortPathCalculator() = default; + + virtual void Solve(const distMatrix_t& costMatrix, size_t N, size_t M, assignments_t& assignment, track_t maxCost) = 0; + +protected: + SPSettings m_settings; +}; + +/// +/// \brief The SPHungrian class +/// +class SPHungrian final : public ShortPathCalculator +{ +public: + SPHungrian(const SPSettings& settings) + : ShortPathCalculator(settings) + { + //std::cout << "SPHungrian" << std::endl; + } + + void Solve(const distMatrix_t& costMatrix, size_t colsTracks, size_t rowsRegions, assignments_t& assignmentT2R, track_t /*maxCost*/) override + { + //std::cout << "SPHungrian::Solve" << std::endl; + m_solver.Solve(costMatrix, colsTracks, rowsRegions, assignmentT2R, AssignmentProblemSolver::optimal); + } + +private: + AssignmentProblemSolver m_solver; +}; + +/// +/// \brief The SPBipart class +/// +class SPBipart final : public ShortPathCalculator +{ +public: + SPBipart(const SPSettings& settings) + : ShortPathCalculator(settings) + { + //std::cout << "SPBipart" << std::endl; + } + + void Solve(const distMatrix_t& costMatrix, size_t colsTracks, size_t rowsRegions, assignments_t& assignmentT2R, track_t maxCost) override; +}; + +/// +/// \brief The SPLAPJV class +/// +class SPLAPJV final : public ShortPathCalculator +{ +public: + SPLAPJV(const SPSettings& settings) + : ShortPathCalculator(settings) + { + //std::cout << "SPLAPJV" << std::endl; + } + + void Solve(const distMatrix_t& costMatrix, size_t colsTracks, size_t rowsRegions, assignments_t& assignmentT2R, track_t /*maxCost*/) override; +}; diff --git a/src/Tracker/TrackerSettings.cpp b/src/Tracker/TrackerSettings.cpp new file mode 100644 index 000000000..e76dd72e4 --- /dev/null +++ b/src/Tracker/TrackerSettings.cpp @@ -0,0 +1,96 @@ +#include +#include "TrackerSettings.h" +#include + +/// +/// \brief CarsCounting::ParseTrackerSettings +/// +bool ParseTrackerSettings(const std::string& settingsFile, TrackerSettings& trackerSettings) +{ + bool res = false; + + std::cout << "ParseTrackerSettings: " << settingsFile << " ..." << std::endl; + + INIReader reader(settingsFile); + + if (reader.ParseError() >= 0) + { + std::cout << "ParseTrackerSettings - readed" << std::endl; + + trackerSettings = TrackerSettings(); + + // Read tracking settings + auto trackerType = reader.GetInteger("tracking", "tracker_type", -1); + if (trackerType == (int)tracking::ByteTrack) + trackerSettings.m_tracker = tracking::ByteTrack; + else + trackerSettings.m_tracker = tracking::UniversalTracker; + + auto distType = reader.GetInteger("tracking", "distance_type", -1); + if (distType >= 0 && distType < (int)tracking::DistsCount) + trackerSettings.SetDistance((tracking::DistType)distType); + + auto kalmanType = reader.GetInteger("tracking", "kalman_type", -1); + if (kalmanType >= 0 && kalmanType < (int)tracking::KalmanCount) + trackerSettings.m_kalmanType = (tracking::KalmanType)kalmanType; + + auto filterGoal = reader.GetInteger("tracking", "filter_goal", -1); + if (filterGoal >= 0 && filterGoal < (int)tracking::FiltersCount) + trackerSettings.m_filterGoal = (tracking::FilterGoal)filterGoal; + + auto lostTrackType = reader.GetInteger("tracking", "lost_track_type", -1); + if (lostTrackType >= 0 && lostTrackType < (int)tracking::SingleTracksCount) + trackerSettings.m_lostTrackType = (tracking::LostTrackType)lostTrackType; + + auto matchType = reader.GetInteger("tracking", "match_type", -1); + if (matchType >= 0 && matchType < (int)tracking::MatchCount) + trackerSettings.m_matchType = (tracking::MatchType)matchType; + + trackerSettings.m_useAcceleration = reader.GetInteger("tracking", "use_aceleration", 0) != 0; // Use constant acceleration motion model + trackerSettings.m_dt = static_cast(reader.GetReal("tracking", "delta_time", 0.4)); // Delta time for Kalman filter + trackerSettings.m_accelNoiseMag = static_cast(reader.GetReal("tracking", "accel_noise", 0.2)); // Accel noise magnitude for Kalman filter + trackerSettings.m_distThres = static_cast(reader.GetReal("tracking", "dist_thresh", 0.8)); // Distance threshold between region and object on two frames + trackerSettings.m_minAreaRadiusPix = static_cast(reader.GetReal("tracking", "min_area_radius_pix", -1.)); + trackerSettings.m_minAreaRadiusK = static_cast(reader.GetReal("tracking", "min_area_radius_k", 0.8)); + trackerSettings.m_maximumAllowedLostTime = reader.GetReal("tracking", "max_lost_time", 1.); // Maximum lost time in seconds + trackerSettings.m_maxTraceLength = reader.GetReal("tracking", "max_trace_len", 2.); // Maximum trace length in seconds + trackerSettings.m_useAbandonedDetection = reader.GetInteger("tracking", "detect_abandoned", 0) != 0; + trackerSettings.m_minStaticTime = reader.GetInteger("tracking", "min_static_time", 5); + trackerSettings.m_maxStaticTime = reader.GetInteger("tracking", "max_static_time", 25); + trackerSettings.m_maxSpeedForStatic = reader.GetInteger("tracking", "max_speed_for_static", 10); + + trackerSettings.m_byteTrackSettings.m_trackBuffer = reader.GetInteger("tracking", "bytetrack_track_buffer", 30); + trackerSettings.m_byteTrackSettings.m_trackThresh = reader.GetReal("tracking", "bytetrack_track_thresh", 0.5); + trackerSettings.m_byteTrackSettings.m_highThresh = reader.GetReal("tracking", "bytetrack_high_thresh", 0.5); + trackerSettings.m_byteTrackSettings.m_matchThresh = reader.GetReal("tracking", "bytetrack_match_thresh", 0.8); + + // Read detection settings + trackerSettings.m_nnWeights = reader.GetString("detection", "nn_weights", "data/yolov4-tiny_best.weights"); + trackerSettings.m_nnConfig = reader.GetString("detection", "nn_config", "data/yolov4-tiny.cfg"); + trackerSettings.m_classNames = reader.GetString("detection", "class_names", "data/traffic.names"); + trackerSettings.m_confidenceThreshold = static_cast(reader.GetReal("detection", "confidence_threshold", 0.5)); + trackerSettings.m_maxCropRatio = static_cast(reader.GetReal("detection", "max_crop_ratio", -1)); + trackerSettings.m_maxBatch = reader.GetInteger("detection", "max_batch", 1); + trackerSettings.m_gpuId = reader.GetInteger("detection", "gpu_id", 0); + trackerSettings.m_netType = reader.GetString("detection", "net_type", "YOLOV4"); + trackerSettings.m_inferencePrecision = reader.GetString("detection", "inference_precision", "FP16"); + trackerSettings.m_detectorBackend = reader.GetInteger("detection", "detector_backend", (int)tracking::Detectors::DNN_OCV); + trackerSettings.m_dnnTarget = reader.GetString("detection", "ocv_dnn_target", "DNN_TARGET_CPU"); + trackerSettings.m_dnnBackend = reader.GetString("detection", "ocv_dnn_backend", "DNN_BACKEND_OPENCV"); + trackerSettings.m_maxVideoMemory = reader.GetInteger("detection", "video_memory", 0); + trackerSettings.m_inputSize.width = reader.GetInteger("detection", "input_width", 0); + trackerSettings.m_inputSize.height = reader.GetInteger("detection", "input_height", 0); + + std::stringstream whiteList{ reader.GetString("detection", "white_list", "") }; + trackerSettings.m_whiteList.clear(); + std::string wname; + while (std::getline(whiteList, wname, ';')) + { + trackerSettings.m_whiteList.push_back(wname); + } + + res = true; + } + std::cout << "ParseTrackerSettings: " << res << std::endl; + return res; +} diff --git a/src/Tracker/TrackerSettings.h b/src/Tracker/TrackerSettings.h new file mode 100644 index 000000000..8e68b4bfe --- /dev/null +++ b/src/Tracker/TrackerSettings.h @@ -0,0 +1,297 @@ +#pragma once +#include +#include +#include + +#include "defines.h" + +/// +/// \brief The TrackerSettings struct +/// +struct TrackerSettings +{ + /// + /// Tracker settings + /// + /// + + tracking::TrackerTemplate m_tracker = tracking::UniversalTracker; + + tracking::KalmanType m_kalmanType = tracking::KalmanLinear; + tracking::FilterGoal m_filterGoal = tracking::FilterCenter; + tracking::LostTrackType m_lostTrackType = tracking::TrackKCF; // Used if m_filterGoal == tracking::FilterRect + tracking::MatchType m_matchType = tracking::MatchLAPJV; + + std::array m_distType; + + /// + /// \brief m_dt + /// Time step for Kalman + /// + track_t m_dt = 1.0f; + + /// + /// \brief m_accelNoiseMag + /// Noise magnitude for Kalman + /// + track_t m_accelNoiseMag = 0.1f; + + /// + /// \brief m_useAcceleration + /// Constant velocity or constant acceleration motion model + /// + bool m_useAcceleration = false; + + /// + /// \brief m_distThres + /// Distance threshold for Assignment problem: from 0 to 1 + /// + track_t m_distThres = 0.8f; + + /// + /// \brief m_minAreaRadius + /// Minimal area radius in pixels for objects centers + /// + track_t m_minAreaRadiusPix = 20.f; + + /// + /// \brief m_minAreaRadius + /// Minimal area radius in ration for object size. Used if m_minAreaRadiusPix < 0 + /// + track_t m_minAreaRadiusK = 0.5f; + + /// + /// \brief m_maximumAllowedLostTime + /// If the object don't assignment more than this time in seconds then it will be removed + /// + double m_maximumAllowedLostTime = 1.; + + /// + /// \brief m_maxTraceLength + /// The maximum trajectory length in seconds + /// + double m_maxTraceLength = 2.f; + + /// + /// \brief m_useAbandonedDetection + /// Detection abandoned objects + /// + bool m_useAbandonedDetection = false; + + /// + /// \brief m_minStaticTime + /// After this time (in seconds) the object is considered abandoned + /// + int m_minStaticTime = 5; + /// + /// \brief m_maxStaticTime + /// After this time (in seconds) the abandoned object will be removed + /// + int m_maxStaticTime = 25; + /// + /// \brief m_maxSpeedForStatic + /// Speed in meters + /// If speed of object is more that this value than object is non static + /// + track_t m_maxSpeedForStatic = 0.5f; + + /// + /// \brief m_nearTypes + /// Object types that can be matched while tracking + /// + std::map> m_nearTypes; + + /// + /// \brief struct ByteTrackSettings + /// Settings only for m_tracker = tracking::ByteTrack + /// + struct ByteTrackSettings + { + int m_trackBuffer = 30; + float m_trackThresh = 0.5f; + float m_highThresh = 0.5f; + float m_matchThresh = 0.8f; + }; + ByteTrackSettings m_byteTrackSettings; + + + /// + /// Detector settings + /// + + /// + std::string m_nnWeights = "data/yolov4-tiny_best.weights"; + + /// + std::string m_nnConfig = "data/yolov4-tiny.cfg"; + + /// + std::string m_classNames = "data/traffic.names"; + + /// + std::deque m_whiteList; + + /// + float m_confidenceThreshold = 0.5f; + + /// + float m_maxCropRatio = -1.f; + + /// + int m_maxBatch = 1; + + /// + int m_gpuId = 0; + + /// + /// \brief Neural network input size + cv::Size m_inputSize{ 0, 0 }; + + /// + /// YOLOV2 + /// YOLOV3 + /// YOLOV4 + /// YOLOV4_TINY + /// YOLOV5 + std::string m_netType = "YOLOV4"; + + /// + /// INT8 + /// FP16 + /// FP32 + std::string m_inferencePrecision = "FP16"; + + // opencv_dnn = 6 + // tensorrt = 5 + int m_detectorBackend = 5; + + // DNN_TARGET_CPU + // DNN_TARGET_OPENCL + // DNN_TARGET_OPENCL_FP16 + // DNN_TARGET_MYRIAD + // DNN_TARGET_CUDA + // DNN_TARGET_CUDA_FP16 + std::string m_dnnTarget = "DNN_TARGET_CPU"; + + // DNN_BACKEND_DEFAULT + // DNN_BACKEND_HALIDE + // DNN_BACKEND_INFERENCE_ENGINE + // DNN_BACKEND_OPENCV + // DNN_BACKEND_VKCOM + // DNN_BACKEND_CUDA + // DNN_BACKEND_INFERENCE_ENGINE_NGRAPH + // DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 + std::string m_dnnBackend = "DNN_BACKEND_OPENCV"; + + // For TensorRT optimization, bytes + size_t m_maxVideoMemory = 0; + + /// + struct EmbeddingParams + { + /// + /// \brief m_embeddingCfgName + /// Neural network config file for embeddings + /// + std::string m_embeddingCfgName; + /// + /// \brief m_embeddingWeightsName + /// Neural network weights file for embeddings + /// + std::string m_embeddingWeightsName; + + /// + cv::Size m_inputLayer{128, 256}; + + /// + std::vector m_objectTypes; + + EmbeddingParams(const std::string& embeddingCfgName, const std::string& embeddingWeightsName, + const cv::Size& inputLayer, const std::vector& objectTypes) + : m_embeddingCfgName(embeddingCfgName), + m_embeddingWeightsName(embeddingWeightsName), + m_inputLayer(inputLayer), + m_objectTypes(objectTypes) + { + assert(!m_objectTypes.empty()); + } + }; + /// + std::vector m_embeddings; + + /// + TrackerSettings() + { + m_distType[tracking::DistCenters] = static_cast(0.0); + m_distType[tracking::DistRects] = static_cast(0.0); + m_distType[tracking::DistJaccard] = static_cast(0.5); + m_distType[tracking::DistHist] = static_cast(0.5); + m_distType[tracking::DistFeatureCos] = static_cast(0.0); + m_distType[tracking::DistMahalanobis] = static_cast(0.0); + + assert(CheckDistance()); + } + + /// + bool CheckDistance() const + { + track_t sum = std::accumulate(m_distType.begin(), m_distType.end(), static_cast(0.0)); + track_t maxOne = std::max(static_cast(1.0), std::fabs(sum)); + //std::cout << "CheckDistance: " << sum << " - " << (std::numeric_limits::epsilon() * maxOne) << ", " << std::fabs(sum - 1.0f) << std::endl; + return std::fabs(sum - static_cast(1.0)) <= std::numeric_limits::epsilon() * maxOne; + } + + /// + bool SetDistances(std::array distType) + { + bool res = true; + auto oldDists = m_distType; + m_distType = distType; + if (!CheckDistance()) + { + m_distType = oldDists; + res = false; + } + return res; + } + + /// + bool SetDistance(tracking::DistType distType) + { + std::fill(m_distType.begin(), m_distType.end(), 0.0f); + m_distType[distType] = 1.f; + return true; + } + + /// + void AddNearTypes(objtype_t type1, objtype_t type2, bool sym) + { + auto AddOne = [&](objtype_t type1, objtype_t type2) + { + auto it = m_nearTypes.find(type1); + if (it == std::end(m_nearTypes)) + m_nearTypes[type1] = std::set{ type2 }; + else + it->second.insert(type2); + }; + AddOne(type1, type2); + if (sym) + AddOne(type2, type1); + } + + /// + bool CheckType(objtype_t type1, objtype_t type2) const + { + bool res = (type1 == bad_type) || (type2 == bad_type) || (type1 == type2); + if (!res) + { + auto it = m_nearTypes.find(type1); + if (it != std::end(m_nearTypes)) + res = it->second.find(type2) != std::end(it->second); + } + return res; + } +}; + +/// +bool ParseTrackerSettings(const std::string& settingsFile, TrackerSettings& trackerSettings); diff --git a/src/Tracker/byte_track/BYTETracker.cpp b/src/Tracker/byte_track/BYTETracker.cpp new file mode 100644 index 000000000..8a63cf56a --- /dev/null +++ b/src/Tracker/byte_track/BYTETracker.cpp @@ -0,0 +1,583 @@ +#include "BYTETracker.h" + +#include + +#include "defines.h" +#include "trajectory.h" +#include "TrackerSettings.h" + +/// +byte_track::BYTETracker::BYTETracker(const int& frame_rate, + const int& track_buffer, + const float& track_thresh, + const float& high_thresh, + const float& match_thresh) : + track_thresh_(track_thresh), + high_thresh_(high_thresh), + match_thresh_(match_thresh), + max_time_lost_(static_cast(frame_rate / 30.0 * track_buffer)), + frame_id_(0), + track_id_count_(0) +{ +} + +/// +void byte_track::BYTETracker::GetTracks(std::vector& tracks) const +{ + tracks.clear(); + + if (output_stracks_.size() > tracks.capacity()) + tracks.reserve(output_stracks_.size()); + for (const auto& track : output_stracks_) + { + std::chrono::duration period = m_lastFrameTime - m_lastFrameTime; + cv::RotatedRect rr(track->getRect().tl(), cv::Point2f(static_cast(track->getRect().x + track->getRect().width), static_cast(track->getRect().y)), track->getRect().br()); + TrackingObject to(rr, track->getTrackId(), track->getTrace(), false, cvRound(period.count()), false, + track->getType(), track->getScore(), track->getVelocity()); + + tracks.emplace_back(to); + } +} + +/// +void byte_track::BYTETracker::GetRemovedTracks(std::vector& trackIDs) const +{ + if (removed_stracks_.size() > trackIDs.capacity()) + trackIDs.reserve(removed_stracks_.size()); + for (const auto& remTrack : removed_stracks_) + { + trackIDs.emplace_back(remTrack->getTrackId()); + } +} + +/// +void byte_track::BYTETracker::Update(const regions_t& regions, cv::UMat /*currFrame*/, time_point_t frameTime) +{ + m_lastFrameTime = frameTime; + + ////////////////// Step 1: Get detections ////////////////// + frame_id_++; + + // Create new STracks using the result of object detection + std::vector det_stracks; + std::vector det_low_stracks; + + for (const auto ®ion : regions) + { + const auto strack = std::make_shared(region.m_brect, region.m_confidence, region.m_type, frameTime); + if (region.m_confidence >= track_thresh_) + det_stracks.push_back(strack); + else + det_low_stracks.push_back(strack); + } + + // Create lists of existing STrack + std::vector active_stracks; + std::vector non_active_stracks; + std::vector strack_pool; + + for (const auto& tracked_strack : tracked_stracks_) + { + if (!tracked_strack->isActivated()) + non_active_stracks.push_back(tracked_strack); + else + active_stracks.push_back(tracked_strack); + } + + strack_pool = jointStracks(active_stracks, lost_stracks_); + + // Predict current pose by KF + for (auto &strack : strack_pool) + { + strack->predict(); + } + + ////////////////// Step 2: First association, with IoU ////////////////// + std::vector current_tracked_stracks; + std::vector remain_tracked_stracks; + std::vector remain_det_stracks; + std::vector refind_stracks; + + { + std::vector> matches_idx; + std::vector unmatch_detection_idx, unmatch_track_idx; + + const auto dists = calcIouDistance(strack_pool, det_stracks); + linearAssignment(dists, strack_pool.size(), det_stracks.size(), match_thresh_, + matches_idx, unmatch_track_idx, unmatch_detection_idx); + + for (const auto &match_idx : matches_idx) + { + const auto track = strack_pool[match_idx[0]]; + const auto det = det_stracks[match_idx[1]]; + if (track->getSTrackState() == STrackState::Tracked) + { + track->update(*det, frame_id_, frameTime); + current_tracked_stracks.push_back(track); + } + else + { + track->reActivate(*det, frame_id_, -1, frameTime); + refind_stracks.push_back(track); + } + } + + for (const auto &unmatch_idx : unmatch_detection_idx) + { + remain_det_stracks.push_back(det_stracks[unmatch_idx]); + } + + for (const auto &unmatch_idx : unmatch_track_idx) + { + if (strack_pool[unmatch_idx]->getSTrackState() == STrackState::Tracked) + remain_tracked_stracks.push_back(strack_pool[unmatch_idx]); + } + } + + ////////////////// Step 3: Second association, using low score dets ////////////////// + std::vector current_lost_stracks; + + { + std::vector> matches_idx; + std::vector unmatch_track_idx, unmatch_detection_idx; + + const auto dists = calcIouDistance(remain_tracked_stracks, det_low_stracks); + linearAssignment(dists, remain_tracked_stracks.size(), det_low_stracks.size(), 0.5, + matches_idx, unmatch_track_idx, unmatch_detection_idx); + + for (const auto &match_idx : matches_idx) + { + const auto track = remain_tracked_stracks[match_idx[0]]; + const auto det = det_low_stracks[match_idx[1]]; + if (track->getSTrackState() == STrackState::Tracked) + { + track->update(*det, frame_id_, frameTime); + current_tracked_stracks.push_back(track); + } + else + { + track->reActivate(*det, frame_id_, -1, frameTime); + refind_stracks.push_back(track); + } + } + + for (const auto &unmatch_track : unmatch_track_idx) + { + const auto track = remain_tracked_stracks[unmatch_track]; + if (track->getSTrackState() != STrackState::Lost) + { + track->markAsLost(); + current_lost_stracks.push_back(track); + } + } + } + + ////////////////// Step 4: Init new stracks ////////////////// + std::vector current_removed_stracks; + + { + std::vector unmatch_detection_idx; + std::vector unmatch_unconfirmed_idx; + std::vector> matches_idx; + + // Deal with unconfirmed tracks, usually tracks with only one beginning frame + const auto dists = calcIouDistance(non_active_stracks, remain_det_stracks); + linearAssignment(dists, non_active_stracks.size(), remain_det_stracks.size(), 0.7, + matches_idx, unmatch_unconfirmed_idx, unmatch_detection_idx); + + for (const auto &match_idx : matches_idx) + { + non_active_stracks[match_idx[0]]->update(*remain_det_stracks[match_idx[1]], frame_id_, frameTime); + current_tracked_stracks.push_back(non_active_stracks[match_idx[0]]); + } + + for (const auto &unmatch_idx : unmatch_unconfirmed_idx) + { + const auto track = non_active_stracks[unmatch_idx]; + track->markAsRemoved(); + current_removed_stracks.push_back(track); + } + + // Add new stracks + for (const auto &unmatch_idx : unmatch_detection_idx) + { + const auto track = remain_det_stracks[unmatch_idx]; + if (track->getScore() < high_thresh_) + continue; + + track_id_count_++; + track->activate(frame_id_, track_id_count_, frameTime); + current_tracked_stracks.push_back(track); + } + } + + ////////////////// Step 5: Update state ////////////////// + for (const auto &lost_strack : lost_stracks_) + { + if (frame_id_ - lost_strack->getFrameId() > max_time_lost_) + { + lost_strack->markAsRemoved(); + current_removed_stracks.push_back(lost_strack); + } + } + + tracked_stracks_ = jointStracks(current_tracked_stracks, refind_stracks); + lost_stracks_ = subStracks(jointStracks(subStracks(lost_stracks_, tracked_stracks_), current_lost_stracks), removed_stracks_); + removed_stracks_ = jointStracks(removed_stracks_, current_removed_stracks); + + std::vector tracked_stracks_out, lost_stracks_out; + removeDuplicateStracks(tracked_stracks_, lost_stracks_, tracked_stracks_out, lost_stracks_out); + tracked_stracks_ = tracked_stracks_out; + lost_stracks_ = lost_stracks_out; + + + output_stracks_.clear(); + for (const auto &track : tracked_stracks_) + { + if (track->isActivated()) + output_stracks_.push_back(track); + } +} + +/// +std::vector byte_track::BYTETracker::jointStracks(const std::vector &a_tlist, + const std::vector &b_tlist) const +{ + std::map exists; + std::vector res; + for (size_t i = 0; i < a_tlist.size(); i++) + { + exists.emplace(a_tlist[i]->getTrackId(), 1); + res.push_back(a_tlist[i]); + } + for (size_t i = 0; i < b_tlist.size(); i++) + { + const size_t &tid = b_tlist[i]->getTrackId(); + if (!exists[tid] || exists.count(tid) == 0) + { + exists[tid] = 1; + res.push_back(b_tlist[i]); + } + } + return res; +} + +/// +std::vector byte_track::BYTETracker::subStracks(const std::vector &a_tlist, + const std::vector &b_tlist) const +{ + std::map stracks; + for (size_t i = 0; i < a_tlist.size(); i++) + { + stracks.emplace(a_tlist[i]->getTrackId(), a_tlist[i]); + } + + for (size_t i = 0; i < b_tlist.size(); i++) + { + const size_t&tid = b_tlist[i]->getTrackId(); + if (stracks.count(tid) != 0) + stracks.erase(tid); + } + + std::vector res; + std::map::iterator it; + for (it = stracks.begin(); it != stracks.end(); ++it) + { + res.push_back(it->second); + } + + return res; +} + +/// +void byte_track::BYTETracker::removeDuplicateStracks(const std::vector &a_stracks, + const std::vector &b_stracks, + std::vector &a_res, + std::vector &b_res) const +{ + const auto ious = calcIouDistance(a_stracks, b_stracks); + + std::vector> overlapping_combinations; + for (size_t i = 0; i < ious.size(); i++) + { + for (size_t j = 0; j < ious[i].size(); j++) + { + if (ious[i][j] < 0.15) + overlapping_combinations.emplace_back(i, j); + } + } + + std::vector a_overlapping(a_stracks.size(), false), b_overlapping(b_stracks.size(), false); + for (const auto &[a_idx, b_idx] : overlapping_combinations) + { + const int timep = a_stracks[a_idx]->getFrameId() - a_stracks[a_idx]->getStartFrameId(); + const int timeq = b_stracks[b_idx]->getFrameId() - b_stracks[b_idx]->getStartFrameId(); + if (timep > timeq) + b_overlapping[b_idx] = true; + else + a_overlapping[a_idx] = true; + } + + for (size_t ai = 0; ai < a_stracks.size(); ai++) + { + if (!a_overlapping[ai]) + a_res.push_back(a_stracks[ai]); + } + + for (size_t bi = 0; bi < b_stracks.size(); bi++) + { + if (!b_overlapping[bi]) + b_res.push_back(b_stracks[bi]); + } +} + +/// +void byte_track::BYTETracker::linearAssignment(const std::vector> &cost_matrix, + const size_t &cost_matrix_size, + const size_t &cost_matrix_size_size, + const float &thresh, + std::vector> &matches, + std::vector &a_unmatched, + std::vector &b_unmatched) const +{ + if (cost_matrix.size() == 0) + { + for (size_t i = 0; i < cost_matrix_size; i++) + { + a_unmatched.push_back(i); + } + for (size_t i = 0; i < cost_matrix_size_size; i++) + { + b_unmatched.push_back(i); + } + return; + } + + std::vector rowsol; + std::vector colsol; + execLapjv(cost_matrix, rowsol, colsol, true, thresh); + for (size_t i = 0; i < rowsol.size(); i++) + { + if (rowsol[i] >= 0) + { + std::vector match; + match.push_back(i); + match.push_back(rowsol[i]); + matches.push_back(match); + } + else + { + a_unmatched.push_back(i); + } + } + + for (size_t i = 0; i < colsol.size(); i++) + { + if (colsol[i] < 0) + b_unmatched.push_back(i); + } +} + +/// +std::vector> byte_track::BYTETracker::calcIous(const std::vector &a_rect, + const std::vector &b_rect) const +{ + std::vector> ious; + if (a_rect.size() * b_rect.size() == 0) + return ious; + + ious.resize(a_rect.size()); + for (size_t i = 0; i < ious.size(); i++) + { + ious[i].resize(b_rect.size()); + } + + auto calcIoU = [](const cv::Rect2f& r1, const cv::Rect2f& r2) + { + const float box_area = (r2.width + 1) * (r2.height + 1); + const float iw = std::min(r1.x + r1.width, r2.x + r2.width) - std::max(r1.x, r2.x) + 1; + float iou = 0; + if (iw > 0) + { + const float ih = std::min(r1.y + r1.height, r2.y + r2.height) - std::max(r1.y, r2.y) + 1; + if (ih > 0) + { + const float ua = (r1.width + 1) * (r1.height + 1) + box_area - iw * ih; + iou = iw * ih / ua; + } + } + return iou; + }; + + for (size_t bi = 0; bi < b_rect.size(); bi++) + { + for (size_t ai = 0; ai < a_rect.size(); ai++) + { + ious[ai][bi] = calcIoU(b_rect[bi], a_rect[ai]); + } + } + return ious; +} + +/// +std::vector > byte_track::BYTETracker::calcIouDistance(const std::vector &a_tracks, + const std::vector &b_tracks) const +{ + std::vector a_rects, b_rects; + for (size_t i = 0; i < a_tracks.size(); i++) + { + a_rects.push_back(a_tracks[i]->getRect()); + } + + for (size_t i = 0; i < b_tracks.size(); i++) + { + b_rects.push_back(b_tracks[i]->getRect()); + } + + const auto ious = calcIous(a_rects, b_rects); + + std::vector> cost_matrix; + for (size_t i = 0; i < ious.size(); i++) + { + std::vector iou; + for (size_t j = 0; j < ious[i].size(); j++) + { + iou.push_back(1 - ious[i][j]); + } + cost_matrix.push_back(iou); + } + + return cost_matrix; +} + +/// +double byte_track::BYTETracker::execLapjv(const std::vector> &cost, + std::vector &rowsol, + std::vector &colsol, + bool extend_cost, + float cost_limit, + bool return_cost) const +{ + std::vector > cost_c; + cost_c.assign(cost.begin(), cost.end()); + + std::vector > cost_c_extended; + + size_t n_rows = cost.size(); + size_t n_cols = cost[0].size(); + rowsol.resize(n_rows); + colsol.resize(n_cols); + + size_t n = 0; + if (n_rows == n_cols) + { + n = n_rows; + } + else + { + if (!extend_cost) + throw std::runtime_error("The `extend_cost` variable should set True"); + } + + if (extend_cost || cost_limit < std::numeric_limits::max()) + { + n = n_rows + n_cols; + cost_c_extended.resize(n); + for (size_t i = 0; i < cost_c_extended.size(); i++) + cost_c_extended[i].resize(n); + + if (cost_limit < std::numeric_limits::max()) + { + for (size_t i = 0; i < cost_c_extended.size(); i++) + { + for (size_t j = 0; j < cost_c_extended[i].size(); j++) + { + cost_c_extended[i][j] = cost_limit / 2.0f; + } + } + } + else + { + float cost_max = -1; + for (size_t i = 0; i < cost_c.size(); i++) + { + for (size_t j = 0; j < cost_c[i].size(); j++) + { + if (cost_c[i][j] > cost_max) + cost_max = cost_c[i][j]; + } + } + for (size_t i = 0; i < cost_c_extended.size(); i++) + { + for (size_t j = 0; j < cost_c_extended[i].size(); j++) + { + cost_c_extended[i][j] = cost_max + 1; + } + } + } + + for (size_t i = n_rows; i < cost_c_extended.size(); i++) + { + for (size_t j = n_cols; j < cost_c_extended[i].size(); j++) + { + cost_c_extended[i][j] = 0; + } + } + for (size_t i = 0; i < n_rows; i++) + { + for (size_t j = 0; j < n_cols; j++) + { + cost_c_extended[i][j] = cost_c[i][j]; + } + } + + cost_c.clear(); + cost_c.assign(cost_c_extended.begin(), cost_c_extended.end()); + } + + std::vector x_c(n, -1); + std::vector y_c(n, 0); + + int ret = lapjv_internal(n, cost_c, x_c, y_c); + if (ret != 0) + throw std::runtime_error("The result of lapjv_internal() is invalid."); + + double opt = 0.0; + + if (n != n_rows) + { + for (size_t i = 0; i < n; i++) + { + if (x_c[i] >= n_cols) + x_c[i] = -1; + if (y_c[i] >= n_rows) + y_c[i] = -1; + } + for (size_t i = 0; i < n_rows; i++) + { + rowsol[i] = x_c[i]; + } + for (size_t i = 0; i < n_cols; i++) + { + colsol[i] = y_c[i]; + } + + if (return_cost) + { + for (size_t i = 0; i < rowsol.size(); i++) + { + if (rowsol[i] != -1) + opt += cost_c[i][rowsol[i]]; + } + } + } + else if (return_cost) + { + for (size_t i = 0; i < rowsol.size(); i++) + { + opt += cost_c[i][rowsol[i]]; + } + } + + return opt; +} diff --git a/src/Tracker/byte_track/BYTETracker.h b/src/Tracker/byte_track/BYTETracker.h new file mode 100644 index 000000000..95146a7bc --- /dev/null +++ b/src/Tracker/byte_track/BYTETracker.h @@ -0,0 +1,75 @@ +#pragma once + +#include "BaseTracker.h" + +#include "STrack.h" +#include "lapjv.h" + +namespace byte_track +{ +class BYTETracker final : public BaseTracker +{ +public: + using STrackPtr = std::shared_ptr; + + BYTETracker(const int& frame_rate, // 30 + const int& track_buffer, // 30 + const float& track_thresh, // 0.5f + const float& high_thresh, // 0.5f + const float& match_thresh); // 0.8f + ~BYTETracker() = default; + + void Update(const regions_t& regions, cv::UMat currFrame, time_point_t frameTime) override; + + void GetTracks(std::vector& tracks) const override; + void GetRemovedTracks(std::vector& trackIDs) const override; + +private: + std::vector jointStracks(const std::vector &a_tlist, + const std::vector &b_tlist) const; + + std::vector subStracks(const std::vector &a_tlist, + const std::vector &b_tlist) const; + + void removeDuplicateStracks(const std::vector &a_stracks, + const std::vector &b_stracks, + std::vector &a_res, + std::vector &b_res) const; + + void linearAssignment(const std::vector> &cost_matrix, + const size_t &cost_matrix_size, + const size_t &cost_matrix_size_size, + const float &thresh, + std::vector> &matches, + std::vector &b_unmatched, + std::vector &a_unmatched) const; + + std::vector> calcIouDistance(const std::vector &a_tracks, + const std::vector &b_tracks) const; + + std::vector> calcIous(const std::vector &a_rect, + const std::vector &b_rect) const; + + double execLapjv(const std::vector > &cost, + std::vector &rowsol, + std::vector &colsol, + bool extend_cost = false, + float cost_limit = std::numeric_limits::max(), + bool return_cost = true) const; + +private: + const float track_thresh_ = 0.5f; + const float high_thresh_ = 0.6f; + const float match_thresh_ = 0.8f; + const size_t max_time_lost_ = 30; + + time_point_t m_lastFrameTime; + size_t frame_id_ = 0; + size_t track_id_count_ = 0; + + std::vector tracked_stracks_; + std::vector lost_stracks_; + std::vector removed_stracks_; + std::vector output_stracks_; +}; +} \ No newline at end of file diff --git a/src/Tracker/byte_track/KalmanFilter.cpp b/src/Tracker/byte_track/KalmanFilter.cpp new file mode 100644 index 000000000..3591eb05d --- /dev/null +++ b/src/Tracker/byte_track/KalmanFilter.cpp @@ -0,0 +1,108 @@ +#include "KalmanFilter.h" + +namespace byte_track +{ + +KalmanFilter::KalmanFilter(const float& std_weight_position, + const float& std_weight_velocity) : + std_weight_position_(std_weight_position), + std_weight_velocity_(std_weight_velocity) +{ + constexpr size_t ndim = 4; + constexpr float dt = 1.0f; + + motion_mat_ = cv::Matx::eye(); + update_mat_ = cv::Matx::eye(); + + for (size_t i = 0; i < ndim; i++) + { + motion_mat_(i, ndim + i) = dt; + } +} + +void KalmanFilter::initiate(StateMean& mean, StateCov& covariance, const DetectBox& measurement) +{ + for (int i = 0; i < 4; i++) + { + mean(0, i) = measurement(0, i); + mean(0, i + 4) = 0.0f; + } + + StateMean std( + 2 * std_weight_position_ * measurement(3), + 2 * std_weight_position_ * measurement(3), + 1e-2f, + 2 * std_weight_position_ * measurement(3), + 10 * std_weight_velocity_ * measurement(3), + 10 * std_weight_velocity_ * measurement(3), + 1e-5f, + 10 * std_weight_velocity_ * measurement(3)); + + covariance = StateCov::zeros(); + for (int i = 0; i < 8; i++) + { + covariance(i, i) = std(i) * std(i); + } +} + +void KalmanFilter::predict(StateMean& mean, StateCov& covariance) +{ + StateMean std( + std_weight_position_ * mean(3), + std_weight_position_ * mean(3), + 1e-2f, + std_weight_position_ * mean(3), + std_weight_velocity_ * mean(3), + std_weight_velocity_ * mean(3), + 1e-5f, + std_weight_velocity_ * mean(3)); + + StateCov motion_cov = StateCov::zeros(); + for (int i = 0; i < 8; i++) + { + motion_cov(i, i) = std(i) * std(i); + } + + StateMean new_mean = mean * motion_mat_.t(); + mean = new_mean; + + covariance = motion_mat_ * covariance * motion_mat_.t() + motion_cov; +} + +void KalmanFilter::update(StateMean& mean, StateCov& covariance, const DetectBox& measurement) +{ + StateHMean projected_mean; + StateHCov projected_cov; + project(projected_mean, projected_cov, mean, covariance); + + cv::Matx B = (covariance * update_mat_.t()).t(); + + cv::Matx kalman_gain; + cv::solve(projected_cov, B, kalman_gain, cv::DECOMP_CHOLESKY); + + StateHMean innovation = measurement - projected_mean; + + StateMean tmp = innovation * kalman_gain; + mean = mean + tmp; + covariance = covariance - kalman_gain.t() * projected_cov * kalman_gain; +} + +void KalmanFilter::project(StateHMean& projected_mean, StateHCov& projected_covariance, + const StateMean& mean, const StateCov& covariance) +{ + DetectBox std( + std_weight_position_ * mean(3), + std_weight_position_ * mean(3), + 1e-1f, + std_weight_position_ * mean(3)); + + projected_mean = (update_mat_ * mean.t()).t(); + projected_covariance = update_mat_ * covariance * update_mat_.t(); + + for (int i = 0; i < 4; i++) + { + projected_covariance(i, i) += std(i) * std(i); + } +} + +} diff --git a/src/Tracker/byte_track/KalmanFilter.h b/src/Tracker/byte_track/KalmanFilter.h new file mode 100644 index 000000000..79dc6cc84 --- /dev/null +++ b/src/Tracker/byte_track/KalmanFilter.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace byte_track +{ + +class KalmanFilter +{ +public: + using DetectBox = cv::Matx; + using StateMean = cv::Matx; + using StateCov = cv::Matx; + using StateHMean = cv::Matx; + using StateHCov = cv::Matx; + + KalmanFilter(const float& std_weight_position = 1.0f / 20, + const float& std_weight_velocity = 1.0f / 160); + + void initiate(StateMean& mean, StateCov& covariance, const DetectBox& measurement); + void predict(StateMean& mean, StateCov& covariance); + void update(StateMean& mean, StateCov& covariance, const DetectBox& measurement); + +private: + float std_weight_position_; + float std_weight_velocity_; + + cv::Matx motion_mat_; + cv::Matx update_mat_; + + void project(StateHMean& projected_mean, StateHCov& projected_covariance, + const StateMean& mean, const StateCov& covariance); +}; +} diff --git a/src/Tracker/byte_track/STrack.cpp b/src/Tracker/byte_track/STrack.cpp new file mode 100644 index 000000000..728b4e6e2 --- /dev/null +++ b/src/Tracker/byte_track/STrack.cpp @@ -0,0 +1,167 @@ +#include "STrack.h" + +#include + +byte_track::STrack::STrack(const cv::Rect2f& rect, const float& score, objtype_t type, time_point_t currTime) : + kalman_filter_(), + mean_(), + covariance_(), + type_(type), + rect_(rect), + state_(STrackState::New), + is_activated_(false), + score_(score), + track_id_(0), + frame_id_(0), + start_frame_id_(0), + tracklet_len_(0) +{ + Point_t pt(rect.x + rect.width / 2.f, rect.y + rect.height); + trace_.push_back(pt, pt, currTime); +} + +const cv::Rect2f& byte_track::STrack::getRect() const +{ + return rect_; +} + +const byte_track::STrackState& byte_track::STrack::getSTrackState() const +{ + return state_; +} + +const bool& byte_track::STrack::isActivated() const +{ + return is_activated_; +} +const float& byte_track::STrack::getScore() const +{ + return score_; +} + +const size_t& byte_track::STrack::getTrackId() const +{ + return track_id_; +} + +const size_t& byte_track::STrack::getFrameId() const +{ + return frame_id_; +} + +const size_t& byte_track::STrack::getStartFrameId() const +{ + return start_frame_id_; +} + +const size_t& byte_track::STrack::getTrackletLength() const +{ + return tracklet_len_; +} + +objtype_t byte_track::STrack::getType() const +{ + return type_; +} + +const Trace& byte_track::STrack::getTrace() const +{ + return trace_; +} + +cv::Vec byte_track::STrack::getVelocity() const +{ + return cv::Vec(mean_(4), mean_(5)); +} + +byte_track::KalmanFilter::DetectBox GetXyah(const cv::Rect2f& rect) +{ + return byte_track::KalmanFilter::DetectBox( + rect.x + rect.width / 2.f, + rect.y + rect.height / 2.f, + rect.width / rect.height, + rect.height + ); +} + +void byte_track::STrack::activate(const size_t& frame_id, const size_t& track_id, time_point_t currTime) +{ + kalman_filter_.initiate(mean_, covariance_, GetXyah(rect_)); + + updateRect(); + + state_ = STrackState::Tracked; + if (frame_id == 1) + is_activated_ = true; + + track_id_ = track_id; + frame_id_ = frame_id; + start_frame_id_ = frame_id; + tracklet_len_ = 0; + + Point_t pt_pr(rect_.x + rect_.width / 2.f, rect_.y + rect_.height); + trace_.push_back(pt_pr, currTime); +} + +void byte_track::STrack::reActivate(const STrack &new_track, const size_t &frame_id, const int &new_track_id, time_point_t currTime) +{ + kalman_filter_.update(mean_, covariance_, GetXyah(new_track.getRect())); + + updateRect(); + + state_ = STrackState::Tracked; + is_activated_ = true; + score_ = new_track.getScore(); + if (0 <= new_track_id) + track_id_ = new_track_id; + + frame_id_ = frame_id; + tracklet_len_ = 0; + + Point_t pt_pr(rect_.x + rect_.width / 2.f, rect_.y + rect_.height); + Point_t pt_raw(new_track.getRect().x + new_track.getRect().width / 2.f, new_track.getRect().y + new_track.getRect().height); + trace_.push_back(pt_pr, pt_raw, currTime); +} + +void byte_track::STrack::predict() +{ + if (state_ != STrackState::Tracked) + mean_(7) = 0; + + kalman_filter_.predict(mean_, covariance_); +} + +void byte_track::STrack::update(const STrack &new_track, const size_t &frame_id, time_point_t currTime) +{ + kalman_filter_.update(mean_, covariance_, GetXyah(new_track.getRect())); + + updateRect(); + + state_ = STrackState::Tracked; + is_activated_ = true; + score_ = new_track.getScore(); + frame_id_ = frame_id; + tracklet_len_++; + + Point_t pt_pr(rect_.x + rect_.width / 2.f, rect_.y + rect_.height); + Point_t pt_raw(new_track.getRect().x + new_track.getRect().width / 2.f, new_track.getRect().y + new_track.getRect().height); + trace_.push_back(pt_pr, pt_raw, currTime); +} + +void byte_track::STrack::markAsLost() +{ + state_ = STrackState::Lost; +} + +void byte_track::STrack::markAsRemoved() +{ + state_ = STrackState::Removed; +} + +void byte_track::STrack::updateRect() +{ + rect_.width = mean_(2) * mean_(3); + rect_.height = mean_(3); + rect_.x = mean_(0) - rect_.width / 2.f; + rect_.y = mean_(1) - rect_.height / 2.f; +} diff --git a/src/Tracker/byte_track/STrack.h b/src/Tracker/byte_track/STrack.h new file mode 100644 index 000000000..65607f784 --- /dev/null +++ b/src/Tracker/byte_track/STrack.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +#include "KalmanFilter.h" +#include "trajectory.h" + +namespace byte_track +{ +enum class STrackState { + New = 0, + Tracked = 1, + Lost = 2, + Removed = 3, +}; + +class STrack +{ +public: + STrack(const cv::Rect2f& rect, const float& score, objtype_t type, time_point_t currTime); + ~STrack() = default; + + const cv::Rect2f& getRect() const; + const STrackState& getSTrackState() const; + + const bool& isActivated() const; + const float& getScore() const; + const size_t& getTrackId() const; + const size_t& getFrameId() const; + const size_t& getStartFrameId() const; + const size_t& getTrackletLength() const; + objtype_t getType() const; + const Trace& getTrace() const; + cv::Vec getVelocity() const; + + void activate(const size_t& frame_id, const size_t& track_id, time_point_t currTime); + void reActivate(const STrack &new_track, const size_t &frame_id, const int &new_track_id, time_point_t currTime); // new_track_id = -1 + + void predict(); + void update(const STrack &new_track, const size_t &frame_id, time_point_t currTime); + + void markAsLost(); + void markAsRemoved(); + +private: + KalmanFilter kalman_filter_; + KalmanFilter::StateMean mean_; + KalmanFilter::StateCov covariance_; + + objtype_t type_ = bad_type; + cv::Rect2f rect_; + STrackState state_{ STrackState::New }; + + bool is_activated_ = false; + float score_ = 0.f; + size_t track_id_ = 0; + size_t frame_id_ = 0; + size_t start_frame_id_ = 0; + size_t tracklet_len_ = 0; + + Trace trace_; + + void updateRect(); +}; +} \ No newline at end of file diff --git a/src/Tracker/byte_track/lapjv.cpp b/src/Tracker/byte_track/lapjv.cpp new file mode 100644 index 000000000..358be4ae6 --- /dev/null +++ b/src/Tracker/byte_track/lapjv.cpp @@ -0,0 +1,315 @@ +#include +#include +#include +#include +#include + +#include "lapjv.h" + +namespace +{ + constexpr size_t LARGE = std::numeric_limits::max(); + + enum class fp_t { + FP_1 = 1, + FP_2 = 2, + FP_DYNAMIC = 3, + }; + + /** Column-reduction and reduction transfer for a dense cost matrix. + */ + int _ccrrt_dense(const size_t n, const std::vector>& cost, + std::vector& free_rows, std::vector& x, std::vector& y, std::vector& v) + { + for (size_t i = 0; i < n; i++) + { + for (size_t j = 0; j < n; j++) + { + const lapjv_t c = cost[i][j]; + if (c < v[j]) { + v[j] = c; + y[j] = i; + } + } + } + + std::vector unique(n, true); + { + int j = n; + do { + j--; + const int i = y[j]; + if (x[i] < 0) { + x[i] = j; + } + else { + unique[i] = false; + y[j] = -1; + } + } while (j > 0); + } + int n_free_rows = 0; + for (size_t i = 0; i < n; i++) + { + if (x[i] < 0) { + free_rows[n_free_rows++] = i; + } + else if (unique[i]) { + const int j = x[i]; + lapjv_t min = LARGE; + for (size_t j2 = 0; j2 < n; j2++) + { + if (j2 == (size_t)j) + continue; + + const lapjv_t c = cost[i][j2] - v[j2]; + if (c < min) + min = c; + } + v[j] -= min; + } + } + return n_free_rows; + } + + + /** Augmenting row reduction for a dense cost matrix. + */ + int _carr_dense( + const size_t n, const std::vector>& cost, + const size_t n_free_rows, + std::vector& free_rows, std::vector& x, std::vector& y, std::vector& v) + { + size_t current = 0; + int new_free_rows = 0; + size_t rr_cnt = 0; + while (current < n_free_rows) + { + rr_cnt++; + const int free_i = free_rows[current++]; + int j1 = 0; + lapjv_t v1 = cost[free_i][0] - v[0]; + int j2 = -1; + lapjv_t v2 = LARGE; + for (size_t j = 1; j < n; j++) { + const lapjv_t c = cost[free_i][j] - v[j]; + if (c < v2) + { + if (c >= v1) { + v2 = c; + j2 = j; + } + else { + v2 = v1; + v1 = c; + j2 = j1; + j1 = j; + } + } + } + int i0 = y[j1]; + lapjv_t v1_new = v[j1] - (v2 - v1); + bool v1_lowers = v1_new < v[j1]; + if (rr_cnt < current * n) + { + if (v1_lowers) { + v[j1] = v1_new; + } + else if (i0 >= 0 && j2 >= 0) { + j1 = j2; + i0 = y[j2]; + } + if (i0 >= 0) + { + if (v1_lowers) + free_rows[--current] = i0; + else + free_rows[new_free_rows++] = i0; + } + } + else { + if (i0 >= 0) + free_rows[new_free_rows++] = i0; + } + x[free_i] = j1; + y[j1] = free_i; + } + return new_free_rows; + } + + + /** Find columns with minimum d[j] and put them on the SCAN list. + */ + size_t _find_dense(const size_t n, size_t lo, const std::vector& d, std::vector& cols) + { + size_t hi = lo + 1; + lapjv_t mind = d[cols[lo]]; + for (size_t k = hi; k < n; k++) + { + int j = cols[k]; + if (d[j] <= mind) { + if (d[j] < mind) { + hi = lo; + mind = d[j]; + } + cols[k] = cols[hi]; + cols[hi++] = j; + } + } + return hi; + } + + + // Scan all columns in TODO starting from arbitrary column in SCAN + // and try to decrease d of the TODO columns using the SCAN column. + int _scan_dense(const size_t n, const std::vector>& cost, + size_t* plo, size_t* phi, + std::vector& d, std::vector& cols, std::vector& pred, + const std::vector& y, const std::vector& v) + { + size_t lo = *plo; + size_t hi = *phi; + + while (lo != hi) + { + int j = cols[lo++]; + const int i = y[j]; + const lapjv_t mind = d[j]; + lapjv_t h = cost[i][j] - v[j] - mind; + // For all columns in TODO + for (size_t k = hi; k < n; k++) + { + j = cols[k]; + lapjv_t cred_ij = cost[i][j] - v[j] - h; + if (cred_ij < d[j]) + { + d[j] = cred_ij; + pred[j] = i; + if (cred_ij == mind) + { + if (y[j] < 0) + return j; + + cols[k] = cols[hi]; + cols[hi++] = j; + } + } + } + } + *plo = lo; + *phi = hi; + return -1; + } + + + /** Single iteration of modified Dijkstra shortest path algorithm as explained in the JV paper. + * + * This is a dense matrix version. + * + * \return The closest free column index. + */ + int find_path_dense( + const size_t n, const std::vector>& cost, + const int start_i, + std::vector& y, std::vector& v, + std::vector& pred) + { + size_t lo = 0, hi = 0; + int final_j = -1; + size_t n_ready = 0; + + std::vector cols(n); + std::vector d(n); + + for (size_t i = 0; i < n; i++) + { + cols[i] = i; + pred[i] = start_i; + d[i] = cost[start_i][i] - v[i]; + } + + while (final_j == -1) + { + // No columns left on the SCAN list. + if (lo == hi) + { + n_ready = lo; + hi = _find_dense(n, lo, d, cols); + for (size_t k = lo; k < hi; k++) + { + const int j = cols[k]; + if (y[j] < 0) + final_j = j; + } + } + if (final_j == -1) + final_j = _scan_dense(n, cost, &lo, &hi, d, cols, pred, y, v); + } + + { + const lapjv_t mind = d[cols[lo]]; + for (size_t k = 0; k < n_ready; k++) { + const int j = cols[k]; + v[j] += d[j] - mind; + } + } + + return final_j; + } + + + /** Augment for a dense cost matrix. + */ + int _ca_dense( + const size_t n, const std::vector>& cost, + const size_t n_free_rows, + std::vector& free_rows, std::vector& x, std::vector& y, std::vector& v) + { + std::vector pred(n); + + for (size_t pfree_i = 0; pfree_i < n_free_rows; ++pfree_i) + { + int i = -1; + size_t k = 0; + + int j = find_path_dense(n, cost, free_rows[pfree_i], y, v, pred); + if (j < 0) + throw std::runtime_error("Error occured in _ca_dense(): j < 0"); + + if (j >= static_cast(n)) + throw std::runtime_error("Error occured in _ca_dense(): j >= n"); + + while (i != free_rows[pfree_i]) + { + i = pred[j]; + y[j] = i; + std::swap(j, x[i]); + ++k; + if (k >= n) + throw std::runtime_error("Error occured in _ca_dense(): k >= n"); + } + } + return 0; + } +} + +/** Solve dense sparse LAP. */ +int byte_track::lapjv_internal( + const size_t n, const std::vector>& cost, + std::vector& x, std::vector& y) +{ + std::vector free_rows(n); + std::vector v(n, LARGE); + + int ret = _ccrrt_dense(n, cost, free_rows, x, y, v); + int i = 0; + while (ret > 0 && i < 2) + { + ret = _carr_dense(n, cost, ret, free_rows, x, y, v); + ++i; + } + if (ret > 0) + ret = _ca_dense(n, cost, ret, free_rows, x, y, v); + + return ret; +} diff --git a/src/Tracker/byte_track/lapjv.h b/src/Tracker/byte_track/lapjv.h new file mode 100644 index 000000000..55dad2e05 --- /dev/null +++ b/src/Tracker/byte_track/lapjv.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +typedef float lapjv_t; + +namespace byte_track +{ +int lapjv_internal(const size_t n, const std::vector>& cost, std::vector& x, std::vector& y); +} \ No newline at end of file diff --git a/src/Tracker/track.cpp b/src/Tracker/track.cpp new file mode 100644 index 000000000..fa5c167e3 --- /dev/null +++ b/src/Tracker/track.cpp @@ -0,0 +1,1129 @@ +#include "track.h" + +#include "Circular_Code/CircVal.h" +#include "Circular_Code/CircStat.h" + +/// +/// \brief CTrack +/// \param pt +/// \param region +/// \param deltaTime +/// \param accelNoiseMag +/// \param trackID +/// \param filterObjectSize +/// \param externalTrackerForLost +/// +CTrack::CTrack(const CRegion& region, + tracking::KalmanType kalmanType, + track_t deltaTime, + track_t accelNoiseMag, + bool useAcceleration, + track_id_t trackID, + tracking::FilterGoal filterGoal, + tracking::LostTrackType externalTrackerForLost, + time_point_t currTime) + : + m_kalman(kalmanType, useAcceleration, deltaTime, accelNoiseMag), + m_lastRegion(region), + m_predictionRect(region.m_rrect), + m_predictionPoint(region.m_rrect.center), + m_trackID(trackID), + m_lastDetectionTime(currTime), + m_currType(region.m_type), + m_lastType(region.m_type), + m_externalTrackerForLost(externalTrackerForLost), + m_filterGoal(filterGoal) +{ + //std::cout << "CTrack::CTrack1: m_predictionRect: " << m_predictionRect.center << ", " << m_predictionRect.angle << ", " << m_predictionRect.size << std::endl; + + switch (filterGoal) + { + case tracking::FilterGoal::FilterCenter: + m_kalman.Update(m_predictionPoint, true); + break; + case tracking::FilterGoal::FilterRect: + m_kalman.Update(region.m_brect, true); + break; + case tracking::FilterGoal::FilterRRect: + m_kalman.Update(region.m_rrect, true); + break; + }; + + Point_t pt(m_predictionPoint.x, m_predictionPoint.y + region.m_brect.height / 2); + m_trace.push_back(pt, pt, currTime); + ResetLostTime(currTime); +} + +/// +/// \brief CTrack::CTrack +/// \param region +/// \param regionEmbedding +/// \param kalmanType +/// \param deltaTime +/// \param accelNoiseMag +/// \param useAcceleration +/// \param trackID +/// \param filterObjectSize +/// \param externalTrackerForLost +/// +CTrack::CTrack(const CRegion& region, + const RegionEmbedding& regionEmbedding, + tracking::KalmanType kalmanType, + track_t deltaTime, + track_t accelNoiseMag, + bool useAcceleration, + track_id_t trackID, + tracking::FilterGoal filterGoal, + tracking::LostTrackType externalTrackerForLost, + time_point_t currTime) + : + m_kalman(kalmanType, useAcceleration, deltaTime, accelNoiseMag), + m_lastRegion(region), + m_predictionRect(region.m_rrect), + m_predictionPoint(region.m_rrect.center), + m_trackID(trackID), + m_lastDetectionTime(currTime), + m_currType(region.m_type), + m_lastType(region.m_type), + m_externalTrackerForLost(externalTrackerForLost), + m_regionEmbedding(regionEmbedding), + m_filterGoal(filterGoal) +{ + //std::cout << "CTrack::CTrack2: m_predictionRect: " << m_predictionRect.center << ", " << m_predictionRect.angle << ", " << m_predictionRect.size << std::endl; + + switch (filterGoal) + { + case tracking::FilterGoal::FilterCenter: + m_kalman.Update(m_predictionPoint, true); + break; + case tracking::FilterGoal::FilterRect: + m_kalman.Update(region.m_brect, true); + break; + case tracking::FilterGoal::FilterRRect: + m_kalman.Update(region.m_rrect, true); + break; + }; + m_trace.push_back(m_predictionPoint, m_predictionPoint, currTime); +} + +/// +/// \brief CTrack::CalcDistCenter +/// \param reg +/// \return +/// +track_t CTrack::CalcDistCenter(const CRegion& reg) const +{ + Point_t diff = m_predictionPoint - reg.m_rrect.center; + if constexpr (sizeof(track_t) == sizeof(float)) + return sqrtf(sqr(diff.x) + sqr(diff.y)); + else + return sqrt(sqr(diff.x) + sqr(diff.y)); +} + +/// +/// \brief CTrack::CalcDistRect +/// \param reg +/// \return +/// +track_t CTrack::CalcDistRect(const CRegion& reg) const +{ + std::array diff; + diff[0] = reg.m_rrect.center.x - m_lastRegion.m_rrect.center.x; + diff[1] = reg.m_rrect.center.y - m_lastRegion.m_rrect.center.y; + diff[2] = static_cast(m_lastRegion.m_rrect.size.width - reg.m_rrect.size.width); + diff[3] = static_cast(m_lastRegion.m_rrect.size.height - reg.m_rrect.size.height); + diff[4] = static_cast(m_lastRegion.m_rrect.angle - reg.m_rrect.angle); + + track_t dist = 0; + for (size_t i = 0; i < diff.size(); ++i) + { + dist += sqr(diff[i]); + } + if constexpr (sizeof(track_t) == sizeof(float)) + return sqrtf(dist); + else + return sqrt(dist); +} + +/// +/// \brief CTrack::CalcDistJaccard +/// \param reg +/// \return +/// +track_t CTrack::CalcDistJaccard(const CRegion& reg) const +{ + track_t intArea = static_cast((reg.m_brect & m_lastRegion.m_brect).area()); + track_t unionArea = static_cast(reg.m_brect.area() + m_lastRegion.m_brect.area() - intArea + 1e-6); + + return std::fabs(1 - intArea / unionArea); +} + +/// +/// \brief CTrack::CalcMahalanobisDist +/// \param reg +/// \return +/// +track_t CTrack::CalcMahalanobisDist(const cv::RotatedRect& rrect) const +{ + cv::Mat res1, predictPoint; + // res1 = Hn * Pn+1|n+1 * Hn^T + Rn+1 error covariance + // res2 = Hn * Xn+1|n + m_kalman.GetPtStateAndResCov(res1, predictPoint); + + double mahaDist = 0.0; + if (!res1.empty() && !predictPoint.empty()) + { + cv::Mat icovar_Pn; + cv::invert(res1, icovar_Pn, cv::DECOMP_SVD); + cv::Mat measurePoint; + if (predictPoint.rows == 2) // PointUpdate + measurePoint = (cv::Mat_(2, 1) << rrect.center.x, rrect.center.y); // detection + else + measurePoint = (cv::Mat_(4, 1) << rrect.center.x, rrect.center.y, rrect.size.width, rrect.size.height); // predict + mahaDist = cv::Mahalanobis(measurePoint, predictPoint, icovar_Pn); + mahaDist += std::log(cv::determinant(res1)); + } + return static_cast(mahaDist); +} + +/// +/// \brief CTrack::CalcDistHist +/// \param embedding +/// \return +/// +track_t CTrack::CalcDistHist(const RegionEmbedding& embedding) const +{ + track_t res = 1; + + if (!embedding.m_hist.empty() && !m_regionEmbedding.m_hist.empty()) + { +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR < 1)) || (CV_VERSION_MAJOR == 3)) + res = static_cast(cv::compareHist(embedding.m_hist, m_regionEmbedding.m_hist, CV_COMP_BHATTACHARYYA)); + //res = 1.f - static_cast(cv::compareHist(hist, m_regionEmbedding.m_hist, CV_COMP_CORREL)); +#else + res = static_cast(cv::compareHist(embedding.m_hist, m_regionEmbedding.m_hist, cv::HISTCMP_BHATTACHARYYA)); +#endif + } + else + { + assert(0); + CV_Assert(!embedding.m_hist.empty()); + CV_Assert(!m_regionEmbedding.m_hist.empty()); + } + return res; +} + +/// +/// \brief CTrack::CalcCosine +/// \param embedding +/// \return +/// +std::pair CTrack::CalcCosine(const RegionEmbedding& embedding) const +{ + track_t res = 1; + if (!embedding.m_embedding.empty() && !m_regionEmbedding.m_embedding.empty()) + { + cv::Mat mul = embedding.m_embedding * m_regionEmbedding.m_embedding.t(); + res = static_cast(1.f - mul.at(0, 0)); + if (res < 0) + res = 0; + //std::cout << "CTrack::CalcCosine: " << embedding.m_embedding.size() << " - " << m_regionEmbedding.m_embedding.size() << " = " << res << std::endl; + return { res, true }; + } + else + { + //assert(0); + //CV_Assert(!embedding.m_embedding.empty()); + //CV_Assert(!m_regionEmbedding.m_embedding.empty()); + return { (track_t)0, false }; + } +} + +/// +/// \brief CTrack::Update +/// \param region +/// \param dataCorrect +/// \param maxTraceLength +/// \param prevFrame +/// \param currFrame +/// \param trajLen +/// +void CTrack::Update(const CRegion& region, + bool dataCorrect, + double maxTraceLength, + cv::UMat prevFrame, + cv::UMat currFrame, + int trajLen, int maxSpeedForStatic, + time_point_t currTime) +{ + //std::cout << "CTrack::Update: dataCorrect = " << dataCorrect << ", m_predictionRect: " << m_predictionRect.center << ", " << m_predictionRect.angle << ", " << m_predictionRect.size << std::endl; + + if (dataCorrect) + { + if (region.m_type == m_currType) + { + m_anotherTypeCounter = 0; + m_lastType = region.m_type; + } + else + { + if (region.m_type == m_lastType) + { + ++m_anotherTypeCounter; + if (m_anotherTypeCounter > m_changeTypeThreshold) + { + m_currType = region.m_type; + m_anotherTypeCounter = 0; + } + } + else + { + m_lastType = region.m_type; + m_anotherTypeCounter = 0; + } + } + } + + switch (m_filterGoal) + { + case tracking::FilterGoal::FilterCenter: + PointUpdate(region.m_rrect.center, region.m_rrect.size, region.m_rrect.angle, dataCorrect, currFrame.size()); + break; + case tracking::FilterGoal::FilterRect: + RectUpdate(region, dataCorrect, prevFrame, currFrame); + break; + case tracking::FilterGoal::FilterRRect: + RectUpdate(region, dataCorrect, prevFrame, currFrame); + break; + }; + + if (dataCorrect) + { + //std::cout << m_lastRegion.m_brect << " - " << region.m_brect << std::endl; + + m_lastRegion = region; + m_trace.push_back(m_predictionPoint, region.m_rrect.center, currTime); + + CheckStatic(trajLen, currFrame, region, maxSpeedForStatic, currTime); + } + else + { + m_trace.push_back(m_predictionPoint, currTime); + } + + for (;;) + { + std::chrono::duration period = currTime - m_trace.at(0).m_frameTime; + if (period.count() > maxTraceLength) + m_trace.pop_front(1); + else + break; + } +} + +/// +/// \brief CTrack::Update +/// \param region +/// \param regionEmbedding +/// \param dataCorrect +/// \param maxTraceLength +/// \param prevFrame +/// \param currFrame +/// \param trajLen +/// +void CTrack::Update(const CRegion& region, + const RegionEmbedding& regionEmbedding, + bool dataCorrect, + double maxTraceLength, + cv::UMat prevFrame, + cv::UMat currFrame, + int trajLen, int maxSpeedForStatic, + time_point_t currTime) +{ + m_regionEmbedding = regionEmbedding; + + switch (m_filterGoal) + { + case tracking::FilterGoal::FilterCenter: + PointUpdate(region.m_rrect.center, region.m_rrect.size, region.m_rrect.angle, dataCorrect, currFrame.size()); + break; + case tracking::FilterGoal::FilterRect: + RectUpdate(region, dataCorrect, prevFrame, currFrame); + break; + case tracking::FilterGoal::FilterRRect: + RectUpdate(region, dataCorrect, prevFrame, currFrame); + break; + }; + + if (dataCorrect) + { + //std::cout << m_lastRegion.m_brect << " - " << region.m_brect << std::endl; + + m_lastRegion = region; + m_trace.push_back(m_predictionPoint, m_lastRegion.m_rrect.center, currTime); + + CheckStatic(trajLen, currFrame, region, maxSpeedForStatic, currTime); + } + else + { + m_trace.push_back(m_predictionPoint, currTime); + } + + for (;;) + { + std::chrono::duration period = currTime - m_trace.at(0).m_frameTime; + if (period.count() > maxTraceLength) + m_trace.pop_front(1); + else + break; + } +} + +/// +/// \brief CTrack::IsStatic +/// \return +/// +bool CTrack::IsStatic() const +{ + return m_isStatic; +} + +/// +/// \brief CTrack::IsStaticTimeout +/// \param framesTime +/// \return +/// +bool CTrack::IsStaticTimeout(time_point_t currTime, double staticPeriod) const +{ + if (m_isStatic) + { + std::chrono::duration period = currTime - m_staticStartTime; + return period.count() > staticPeriod; + } + else + { + return false; + } +} + +/// +/// \brief CTrack::IsOutOfTheFrame +/// \return +/// +bool CTrack::IsOutOfTheFrame() const +{ + return m_outOfTheFrame; +} + +/// +cv::RotatedRect CTrack::CalcPredictionEllipse(cv::Size_ minRadius) const +{ + // Move ellipse to velocity + auto velocity = m_kalman.GetVelocity(); + Point_t d(3.f * velocity[0], 3.f * velocity[1]); + + cv::RotatedRect rrect(m_predictionPoint, cv::Size2f(std::max(minRadius.width, fabs(d.x)), std::max(minRadius.height, fabs(d.y))), 0); + + if (fabs(d.x) + fabs(d.y) > 4) // pix + { + if (fabs(d.x) > 0.0001f) + { + track_t l = std::min(rrect.size.width, rrect.size.height) / 3; + + track_t p2_l = sqrtf(sqr(d.x) + sqr(d.y)); + rrect.center.x = l * d.x / p2_l + m_predictionPoint.x; + rrect.center.y = l * d.y / p2_l + m_predictionPoint.y; + + rrect.angle = atanf(d.y / d.x); + } + else + { + rrect.center.y += d.y / 3; + rrect.angle = static_cast(CV_PI / 2.); + } + } + return rrect; +} + +/// +/// \brief CTrack::IsInsideArea +/// If result <= 1 then center of the object is inside ellipse with prediction and velocity +/// \param pt +/// \return +/// +track_t CTrack::IsInsideArea(const Point_t& pt, const cv::RotatedRect& rrect) const +{ + Point_t pt_(pt.x - rrect.center.x, pt.y - rrect.center.y); + track_t r = sqrtf(sqr(pt_.x) + sqr(pt_.y)); + track_t t = (r > 1) ? acosf(pt_.x / r) : 0; + track_t t_ = t - rrect.angle; + Point_t pt_rotated(r * cosf(t_), r * sinf(t_)); + + return sqr(pt_rotated.x) / sqr(rrect.size.width) + sqr(pt_rotated.y) / sqr(rrect.size.height); +} + +/// +/// \brief CTrack::WidthDist +/// \param reg +/// \return +/// +track_t CTrack::WidthDist(const CRegion& reg) const +{ + if (m_lastRegion.m_rrect.size.width < reg.m_rrect.size.width) + return m_lastRegion.m_rrect.size.width / reg.m_rrect.size.width; + else + return reg.m_rrect.size.width / m_lastRegion.m_rrect.size.width; +} + +/// +/// \brief CTrack::HeightDist +/// \param reg +/// \return +/// +track_t CTrack::HeightDist(const CRegion& reg) const +{ + if (m_lastRegion.m_rrect.size.height < reg.m_rrect.size.height) + return m_lastRegion.m_rrect.size.height / reg.m_rrect.size.height; + else + return reg.m_rrect.size.height / m_lastRegion.m_rrect.size.height; +} + +/// +/// \brief CTrack::CheckStatic +/// \param trajLen +/// \return +/// +bool CTrack::CheckStatic(int trajLen, cv::UMat currFrame, const CRegion& region, int maxSpeedForStatic, time_point_t currTime) +{ + if (!trajLen || static_cast(m_trace.size()) < trajLen) + { + m_isStatic = false; + m_staticStartTime = currTime; + m_staticFrame = cv::UMat(); + } + else + { + auto velocity = m_kalman.GetVelocity(); + track_t speed = sqrt(sqr(velocity[0]) + sqr(velocity[1])); + + bool inCenter = true; + cv::Rect centerROI(cvRound(m_trace[m_trace.size() - trajLen].x) - region.m_brect.width / 2, + cvRound(m_trace[m_trace.size() - trajLen].y) - region.m_brect.height / 2, region.m_brect.width, region.m_brect.height); + for (size_t i = m_trace.size() - trajLen; i < m_trace.size() - 1; ++i) + { + if (!centerROI.contains(m_trace[i])) + { + inCenter = false; + break; + } + } + + if (inCenter/*speed < maxSpeedForStatic*/) + { + if (!m_isStatic) + { + m_staticFrame = currFrame.clone(); + m_staticRect = region.m_brect; +#if 0 +#ifndef SILENT_WORK + cv::namedWindow("m_staticFrame", cv::WINDOW_NORMAL); + cv::Mat img = m_staticFrame.getMat(cv::ACCESS_READ).clone(); + cv::rectangle(img, m_staticRect, cv::Scalar(255, 0, 255), 1); + for (size_t i = m_trace.size() - trajLen; i < m_trace.size() - 1; ++i) + { + cv::line(img, m_trace[i], m_trace[i + 1], cv::Scalar(0, 0, 0), 1, cv::LINE_8); + } + std::string label = "(" + std::to_string(velocity[0]) + ", " + std::to_string(velocity[1]) + ") = " + std::to_string(speed); + Point_t p0 = m_trace[m_trace.size() - trajLen]; + cv::line(img, + cv::Point(cvRound(p0.x), cvRound(p0.y)), + cv::Point(cvRound(velocity[0] * trajLen + p0.x), cvRound(velocity[1] * trajLen + p0.y)), + cv::Scalar(0, 0, 0), 1, cv::LINE_8); + cv::putText(img, label, m_staticRect.tl(), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0)); + cv::imshow("m_staticFrame", img); + std::cout << "m_staticRect = " << m_staticRect << std::endl; + cv::waitKey(1); +#endif +#endif + } + + m_isStatic = true; + } + else + { + m_isStatic = false; + m_staticStartTime = currTime; + m_staticFrame = cv::UMat(); + } + } + return m_isStatic; +} + +/// +/// \brief GetLastRect +/// \return +/// +cv::RotatedRect CTrack::GetLastRect() const +{ + if (m_filterGoal != tracking::FilterGoal::FilterCenter) + return m_predictionRect; + else + return cv::RotatedRect(cv::Point2f(m_predictionPoint.x, m_predictionPoint.y), m_predictionRect.size, m_predictionRect.angle); +} + +/// +/// \brief CTrack::LastRegion +/// \return +/// +const CRegion& CTrack::LastRegion() const +{ + return m_lastRegion; +} + +/// +/// \brief CTrack::GetCurrType +/// \return +/// +objtype_t CTrack::GetCurrType() const +{ + return m_currType; +} + +/// +/// \brief CTrack::ConstructObject +/// \return +/// +TrackingObject CTrack::ConstructObject(time_point_t frameTime) const +{ + std::chrono::duration period = frameTime - m_staticStartTime; + return TrackingObject(GetLastRect(), m_trackID, m_trace, IsStatic(), cvRound(period.count()), IsOutOfTheFrame(), + m_currType, m_lastRegion.m_confidence, m_kalman.GetVelocity()); +} + +/// +/// \brief CTrack::GetID +/// \return +/// +track_id_t CTrack::GetID() const +{ + return m_trackID; +} + +/// +/// \brief CTrack::GetLostPeriod +/// \return +/// +double CTrack::GetLostPeriod(time_point_t currTime) const +{ + std::chrono::duration period = currTime - m_lastDetectionTime; + return period.count(); +} + +/// +/// \brief CTrack::ResetLostTime +/// \return +/// +void CTrack::ResetLostTime(time_point_t currTime) +{ + m_lastDetectionTime = currTime; +} + +/// +/// \brief CTrack::GetFilterGoal +/// \return +/// +tracking::FilterGoal CTrack::GetFilterGoal() const +{ + return m_filterGoal; +} + +/// +/// \brief CTrack::KalmanPredictRect +/// \return +/// +void CTrack::KalmanPredictRect() +{ + m_kalman.GetRectPrediction(); +} + +/// +/// \brief CTrack::KalmanPredictPoint +/// \return +/// +void CTrack::KalmanPredictPoint() +{ + m_kalman.GetPointPrediction(); +} + +/// +/// \brief RectUpdate +/// \param region +/// \param dataCorrect +/// \param prevFrame +/// \param currFrame +/// +void CTrack::RectUpdate(const CRegion& region, + bool dataCorrect, + cv::UMat prevFrame, + cv::UMat currFrame) +{ + bool wasTracked = false; + cv::RotatedRect trackedRRect; + + auto Clamp = [](int& v, int& size, int hi) -> int + { + int res = 0; + + if (size < 1) + size = 0; + + if (v < 0) + { + res = v; + v = 0; + return res; + } + else if (v + size > hi - 1) + { + res = v; + v = hi - 1 - size; + if (v < 0) + { + size += v; + v = 0; + } + res -= v; + return res; + } + return res; + }; + + auto UpdateRRect = [&](cv::Rect prevRect, cv::Rect newRect) + { + m_predictionRect.center.x += newRect.x - prevRect.x; + m_predictionRect.center.y += newRect.y - prevRect.y; + m_predictionRect.size.width *= newRect.width / static_cast(prevRect.width); + m_predictionRect.size.height *= newRect.height / static_cast(prevRect.height); + }; + + auto InitTracker = [&](cv::Rect& roiRect, bool reinit) + { + bool inited = false; + cv::Rect brect = dataCorrect ? region.m_brect : m_predictionRect.boundingRect(); + roiRect.x = 0; + roiRect.y = 0; + roiRect.width = currFrame.cols; + roiRect.height = currFrame.rows; + + switch (m_externalTrackerForLost) + { + case tracking::TrackNone: + break; + + case tracking::TrackKCF: + case tracking::TrackCSRT: + case tracking::TrackDaSiamRPN: + case tracking::TrackNano: + case tracking::TrackVit: +#ifdef USE_OCV_KCF + { + roiRect.width = std::max(3 * brect.width, currFrame.cols / 4); + roiRect.height = std::max(3 * brect.height, currFrame.rows / 4); + if (roiRect.width > currFrame.cols) + roiRect.width = currFrame.cols; + + if (roiRect.height > currFrame.rows) + roiRect.height = currFrame.rows; + + roiRect.x = brect.x + brect.width / 2 - roiRect.width / 2; + roiRect.y = brect.y + brect.height / 2 - roiRect.height / 2; + Clamp(roiRect.x, roiRect.width, currFrame.cols); + Clamp(roiRect.y, roiRect.height, currFrame.rows); + + if (!m_tracker || m_tracker.empty() || reinit) + { + CreateExternalTracker(currFrame.channels()); + + int dx = 0;//m_predictionRect.width / 8; + int dy = 0;//m_predictionRect.height / 8; + cv::Rect2d lastRect(brect.x - roiRect.x - dx, brect.y - roiRect.y - dy, brect.width + 2 * dx, brect.height + 2 * dy); + + if (lastRect.x >= 0 && + lastRect.y >= 0 && + lastRect.x + lastRect.width < roiRect.width && + lastRect.y + lastRect.height < roiRect.height && + lastRect.area() > 0) + { + m_tracker->init(cv::UMat(currFrame, roiRect), lastRect); +#if 0 +#ifndef SILENT_WORK + cv::Mat tmp = cv::UMat(currFrame, roiRect).getMat(cv::ACCESS_READ).clone(); + cv::rectangle(tmp, lastRect, cv::Scalar(255, 255, 255), 2); + cv::imshow("init " + std::to_string(m_trackID), tmp); +#endif +#endif + inited = true; + m_outOfTheFrame = false; + } + else + { + m_tracker.release(); + m_outOfTheFrame = true; + } + } + } +#else + std::cerr << "KCF tracker was disabled in CMAKE! Set lostTrackType = TrackNone in constructor." << std::endl; +#endif + break; + } + return inited; + }; + + if (m_externalTrackerForLost != tracking::TrackNone) + { +#ifdef USE_OCV_KCF + cv::Rect roiRect; + bool inited = InitTracker(roiRect, false); +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR < 5)) || ((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR == 5) && (CV_VERSION_REVISION < 1)) || (CV_VERSION_MAJOR == 3)) + cv::Rect2d newRect; +#else + cv::Rect newRect; +#endif + if (!inited && !m_tracker.empty() && m_tracker->update(cv::UMat(currFrame, roiRect), newRect)) + { +#if 0 +#ifndef SILENT_WORK + cv::Mat tmp2 = cv::UMat(currFrame, roiRect).getMat(cv::ACCESS_READ).clone(); + cv::rectangle(tmp2, newRect, cv::Scalar(255, 255, 255), 2); + cv::imshow("track " + std::to_string(m_trackID), tmp2); +#endif +#endif + +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR < 5)) || ((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR == 5) && (CV_VERSION_REVISION < 1)) || (CV_VERSION_MAJOR == 3)) + cv::Rect prect(cvRound(newRect.x) + roiRect.x, cvRound(newRect.y) + roiRect.y, cvRound(newRect.width), cvRound(newRect.height)); +#else + cv::Rect prect(newRect.x + roiRect.x, newRect.y + roiRect.y, newRect.width, newRect.height); +#endif + trackedRRect = cv::RotatedRect(cv::Point2f(prect.x + prect.width / 2.f, prect.y + prect.height / 2.f), cv::Size2f(static_cast(prect.width), static_cast(prect.height)), 0); + wasTracked = true; + } +#else + std::cerr << "KCF tracker was disabled in CMAKE! Set lostTrackType = TrackNone in constructor." << std::endl; +#endif + } + + cv::Rect brect = m_predictionRect.boundingRect(); + + if (dataCorrect) + { + if (wasTracked) + { +#if 0 + if (trackedRRect.angle > 0.5f) + { + m_predictionRect = trackedRRect; + m_kalman.Update(trackedRRect.boundingRect(), true); + } + else + { + UpdateRRect(brect, m_kalman.Update(trackedRRect.boundingRect(), true)); + } +#else + auto IoU = [](cv::Rect r1, cv::Rect r2) + { + return (r1 & r2).area() / static_cast((r1 | r2).area()); + }; + auto iou = IoU(trackedRRect.boundingRect(), region.m_brect); + if (iou < 0.5f) + { + cv::Rect roiRect; + InitTracker(roiRect, true); + //std::cout << "Reinit tracker with iou = " << iou << std::endl; + } + +#if 0 +#ifndef SILENT_WORK + { + auto rrr = trackedRRect.boundingRect() | region.m_brect; + cv::Mat tmpFrame = cv::UMat(currFrame, rrr).getMat(cv::ACCESS_READ).clone(); + cv::Rect r1(trackedRRect.boundingRect()); + cv::Rect r2(region.m_brect); + r1.x -= rrr.x; + r1.y -= rrr.y; + r2.x -= rrr.x; + r2.y -= rrr.y; + cv::rectangle(tmpFrame, r1, cv::Scalar(0, 255, 0), 1); + cv::rectangle(tmpFrame, r2, cv::Scalar(255, 0, 255), 1); + cv::imshow("reinit " + std::to_string(m_trackID), tmpFrame); + } +#endif +#endif + if (m_filterGoal == tracking::FilterGoal::FilterRect) + UpdateRRect(brect, m_kalman.Update(region.m_brect, dataCorrect)); + else + m_predictionRect = m_kalman.Update(region.m_rrect, dataCorrect); +#endif + } + else + { + if (m_filterGoal == tracking::FilterGoal::FilterRect) + UpdateRRect(brect, m_kalman.Update(region.m_brect, dataCorrect)); + else + m_predictionRect = m_kalman.Update(region.m_rrect, dataCorrect); + } + } + else + { + if (wasTracked) + { + if (trackedRRect.angle > 0.5f) + { + m_predictionRect = trackedRRect; + m_kalman.Update(trackedRRect.boundingRect(), true); + } + else + { + if (m_filterGoal == tracking::FilterGoal::FilterRect) + UpdateRRect(brect, m_kalman.Update(trackedRRect.boundingRect(), true)); + else + m_predictionRect = m_kalman.Update(trackedRRect, true); + } + } + else + { + if (m_filterGoal == tracking::FilterGoal::FilterRect) + UpdateRRect(brect, m_kalman.Update(region.m_brect, dataCorrect)); + else + m_predictionRect = m_kalman.Update(region.m_rrect, dataCorrect); + } + } + + brect = m_predictionRect.boundingRect(); + int dx = Clamp(brect.x, brect.width, currFrame.cols); + int dy = Clamp(brect.y, brect.height, currFrame.rows); +#if 0 + m_predictionRect.center.x += dx; + m_predictionRect.center.y += dy; +#endif + m_outOfTheFrame = (dx != 0) || (dy != 0) || (brect.width < 1) || (brect.height < 1); + + m_predictionPoint = m_predictionRect.center; + + //std::cout << GetID().ID2Str() << ": brect = " << brect << ", dx = " << dx << ", dy = " << dy << ", outOfTheFrame = " << m_outOfTheFrame << ", predictionPoint = " << m_predictionPoint << std::endl; +} + +/// +/// \brief CreateExternalTracker +/// +void CTrack::CreateExternalTracker(int channels) +{ + switch (m_externalTrackerForLost) + { + case tracking::TrackNone: +#ifdef USE_OCV_KCF + if (m_tracker && !m_tracker.empty()) + m_tracker.release(); +#endif + break; + + case tracking::TrackKCF: +#ifdef USE_OCV_KCF + if (!m_tracker || m_tracker.empty()) + { + cv::TrackerKCF::Params params; + if (channels == 1) + { + params.compressed_size = 1; + params.desc_pca = cv::TrackerKCF::GRAY; + params.desc_npca = cv::TrackerKCF::GRAY; + } + else + { + params.compressed_size = 3; + params.desc_pca = cv::TrackerKCF::CN; + params.desc_npca = cv::TrackerKCF::CN; + } + params.resize = true; + params.detect_thresh = 0.7f; +#if (((CV_VERSION_MAJOR == 3) && (CV_VERSION_MINOR >= 3)) || (CV_VERSION_MAJOR > 3)) + m_tracker = cv::TrackerKCF::create(params); +#else + m_tracker = cv::TrackerKCF::createTracker(params); +#endif + } +#endif + break; + + case tracking::TrackCSRT: +#ifdef USE_OCV_KCF + if (!m_tracker || m_tracker.empty()) + { +#if (CV_VERSION_MAJOR >= 4) + cv::TrackerCSRT::Params params; + params.psr_threshold = 0.04f; // 0.035f; + if (channels == 1) + { + params.use_gray = true; + params.use_rgb = false; + } + else + { + params.use_gray = false; + params.use_rgb = true; + } + m_tracker = cv::TrackerCSRT::create(params); +#endif + } +#endif + break; + + case tracking::TrackDaSiamRPN: +#ifdef USE_OCV_KCF + if (!m_tracker || m_tracker.empty()) + { +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR > 5)) || ((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR == 5) && (CV_VERSION_REVISION > 2)) || (CV_VERSION_MAJOR > 4)) + cv::TrackerDaSiamRPN::Params params; + params.model = "dasiamrpn_model.onnx"; + params.kernel_cls1 = "dasiamrpn_kernel_cls1.onnx"; + params.kernel_r1 = "dasiamrpn_kernel_r1.onnx"; + // backend + // 0: automatically (by default) + // 1: Halide language + // 2: Intel's Deep Learning Inference Engine + // 3: OpenCV implementation + // 4: VKCOM + // 5: CUDA + params.backend = 0; + // target + // 0: CPU target (by default) + // 1: OpenCL + // 2: OpenCL fp16 (half-float precision) + // 3: VPU + // 4: Vulkan + // 6: CUDA + // 7: CUDA fp16 (half-float preprocess) + params.target = 0; + m_tracker = cv::TrackerDaSiamRPN::create(params); +#endif + } +#endif + break; + + case tracking::TrackNano: +#ifdef USE_OCV_KCF + if (!m_tracker || m_tracker.empty()) + { +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR > 6)) || (CV_VERSION_MAJOR > 4)) + cv::TrackerNano::Params params; + params.backbone = "nanotrack_backbone_sim.onnx"; + params.neckhead = "nanotrack_head_sim.onnx"; + // backend + // 0: automatically (by default) + // 1: Halide language + // 2: Intel's Deep Learning Inference Engine + // 3: OpenCV implementation + // 4: VKCOM + // 5: CUDA + params.backend = 0; + // target + // 0: CPU target (by default) + // 1: OpenCL + // 2: OpenCL fp16 (half-float precision) + // 3: VPU + // 4: Vulkan + // 6: CUDA + // 7: CUDA fp16 (half-float preprocess) + params.target = 0; + m_tracker = cv::TrackerNano::create(params); +#endif + } +#endif + break; + + case tracking::TrackVit: +#ifdef USE_OCV_KCF + if (!m_tracker || m_tracker.empty()) + { +#if (((CV_VERSION_MAJOR == 4) && (CV_VERSION_MINOR > 8)) || (CV_VERSION_MAJOR > 4)) + cv::TrackerVit::Params params; + params.net = "vitTracker.onnx"; + // backend + // 0: automatically (by default) + // 1: Halide language + // 2: Intel's Deep Learning Inference Engine + // 3: OpenCV implementation + // 4: VKCOM + // 5: CUDA + params.backend = 0; + // target + // 0: CPU target (by default) + // 1: OpenCL + // 2: OpenCL fp16 (half-float precision) + // 3: VPU + // 4: Vulkan + // 6: CUDA + // 7: CUDA fp16 (half-float preprocess) + params.target = 0; + m_tracker = cv::TrackerVit::create(params); +#endif + } +#endif + break; + } +} + +/// +/// \brief PointUpdate +/// \param pt +/// \param dataCorrect +/// +void CTrack::PointUpdate(const Point_t& pt, + const cv::Size& newObjSize, + float newAngle, + bool dataCorrect, + const cv::Size& frameSize) +{ + m_predictionPoint = m_kalman.Update(pt, dataCorrect); + + if (dataCorrect) + { + const int a1 = 1; + const int a2 = 9; + m_predictionRect.size.width = (a1 * newObjSize.width + a2 * m_predictionRect.size.width) / (a1 + a2); + m_predictionRect.size.height = (a1 * newObjSize.height + a2 * m_predictionRect.size.height) / (a1 + a2); + + std::vector, double>> angles; + + angles.push_back(std::make_pair(static_cast(m_predictionRect.angle), 0.1)); + angles.push_back(std::make_pair(static_cast(newAngle), 0.9)); + auto vals = WeightedCircAverage(angles); + //std::cout << "[" << m_predictionRect.angle << ", " << newAngle << "] -> "; + //for (auto v : vals) + //{ + // std::cout << v.operator double() << " "; + //} + //std::cout << std::endl; + m_predictionRect.angle = static_cast(vals.begin()->operator double()); + } + + auto Clamp = [](track_t& v, int hi) -> bool + { + if (v < 0) + { + v = 0; + return true; + } + else if (hi && v > hi - 1) + { + v = static_cast(hi - 1); + return true; + } + return false; + }; + auto p = m_predictionPoint; + m_outOfTheFrame = Clamp(p.x, frameSize.width) || Clamp(p.y, frameSize.height) || (m_predictionRect.size.width < 1) || (m_predictionRect.size.height < 1); + + //std::cout << "CTrack::PointUpdate: m_predictionRect: " << m_predictionRect.center << ", " << m_predictionRect.angle << ", " << m_predictionRect.size << std::endl; + //std::cout << GetID().ID2Str() << ": predictionRect = " << m_predictionRect.boundingRect() << ", outOfTheFrame = " << m_outOfTheFrame << ", predictionPoint = " << m_predictionPoint << ", newAngle = " << newAngle << std::endl; +} diff --git a/src/Tracker/track.h b/src/Tracker/track.h new file mode 100644 index 000000000..5556c54b4 --- /dev/null +++ b/src/Tracker/track.h @@ -0,0 +1,171 @@ +#pragma once +#include +#include +#include +#include +#include + +#ifdef USE_OCV_KCF +#include +#endif + +#include "defines.h" +#include "trajectory.h" +#include "object_types.h" +#include "Kalman.h" + +/// +/// \brief The RegionEmbedding struct +/// +struct RegionEmbedding +{ + cv::Mat m_hist; + cv::Mat m_embedding; +}; + +/// +/// \brief The CTrack class +/// +class CTrack +{ +public: + CTrack(const CRegion& region, + tracking::KalmanType kalmanType, + track_t deltaTime, + track_t accelNoiseMag, + bool useAcceleration, + track_id_t trackID, + tracking::FilterGoal filterGoal, + tracking::LostTrackType externalTrackerForLost, + time_point_t currTime); + + CTrack(const CRegion& region, + const RegionEmbedding& regionEmbedding, + tracking::KalmanType kalmanType, + track_t deltaTime, + track_t accelNoiseMag, + bool useAcceleration, + track_id_t trackID, + tracking::FilterGoal filterGoal, + tracking::LostTrackType externalTrackerForLost, + time_point_t currTime); + + /// + /// \brief CalcDistCenter + /// Euclidean distance from 0 to 1 between objects centres on two N and N+1 frames + /// \param reg + /// \return + /// + track_t CalcDistCenter(const CRegion& reg) const; + /// + /// \brief CalcDistRect + /// Euclidean distance from 0 to 1 between object contours on two N and N+1 frames + /// \param reg + /// \return + /// + track_t CalcDistRect(const CRegion& reg) const; + /// + /// \brief CalcDistJaccard + /// Jaccard distance from 0 to 1 between object bounding rectangles on two N and N+1 frames + /// \param reg + /// \return + /// + track_t CalcDistJaccard(const CRegion& reg) const; + /// + /// \brief CTrack::CalcMahalanobisDist + /// \param reg + /// \return + /// + track_t CalcMahalanobisDist(const cv::RotatedRect& rrect) const; + /// + /// \brief CalcDistHist + /// Distance from 0 to 1 between objects histogramms on two N and N+1 frames + /// \param embedding + /// \return + /// + track_t CalcDistHist(const RegionEmbedding& embedding) const; + /// + /// \brief CalcCosine + /// Distance from 0 to 1 between objects embeddings on two N and N+1 frames + /// \param embedding + /// \return + /// + std::pair CalcCosine(const RegionEmbedding& embedding) const; + + cv::RotatedRect CalcPredictionEllipse(cv::Size_ minRadius) const; + /// + /// \brief IsInsideArea + /// Test point inside in prediction area: prediction area + object velocity + /// \param pt + /// \param minVal + /// \return + /// + track_t IsInsideArea(const Point_t& pt, const cv::RotatedRect& rrect) const; + + track_t WidthDist(const CRegion& reg) const; + track_t HeightDist(const CRegion& reg) const; + + void Update(const CRegion& region, bool dataCorrect, double maxTraceLength, cv::UMat prevFrame, cv::UMat currFrame, int trajLen, int maxSpeedForStatic, time_point_t currTime); + void Update(const CRegion& region, const RegionEmbedding& regionEmbedding, bool dataCorrect, double maxTraceLength, cv::UMat prevFrame, cv::UMat currFrame, int trajLen, int maxSpeedForStatic, time_point_t currTime); + + bool IsStatic() const; + bool IsStaticTimeout(time_point_t currTime, double staticPeriod) const; + bool IsOutOfTheFrame() const; + + cv::RotatedRect GetLastRect() const; + + const CRegion& LastRegion() const; + objtype_t GetCurrType() const; + double GetLostPeriod(time_point_t currTime) const; + void ResetLostTime(time_point_t currTime); + + TrackingObject ConstructObject(time_point_t frameTime) const; + track_id_t GetID() const; + + tracking::FilterGoal GetFilterGoal() const; + void KalmanPredictRect(); + void KalmanPredictPoint(); + +private: + TKalmanFilter m_kalman; + CRegion m_lastRegion; + Trace m_trace; + cv::RotatedRect m_predictionRect; + Point_t m_predictionPoint; + + track_id_t m_trackID; + time_point_t m_lastDetectionTime; + + objtype_t m_currType = bad_type; + objtype_t m_lastType = bad_type; + size_t m_anotherTypeCounter = 0; + static constexpr size_t m_changeTypeThreshold = 25; + + tracking::LostTrackType m_externalTrackerForLost = tracking::TrackNone; +#ifdef USE_OCV_KCF + cv::Ptr m_tracker; +#endif + + /// + void RectUpdate(const CRegion& region, bool dataCorrect, cv::UMat prevFrame, cv::UMat currFrame); + + /// + void CreateExternalTracker(int channels); + + /// + void PointUpdate(const Point_t& pt, const cv::Size& newObjSize, float newAngle, bool dataCorrect, const cv::Size& frameSize); + + RegionEmbedding m_regionEmbedding; + + /// + bool CheckStatic(int trajLen, cv::UMat currFrame, const CRegion& region, int maxSpeedForStatic, time_point_t currTime); + cv::UMat m_staticFrame; + cv::Rect m_staticRect; + time_point_t m_staticStartTime; + bool m_isStatic = false; + + tracking::FilterGoal m_filterGoal = tracking::FilterGoal::FilterCenter; + bool m_outOfTheFrame = false; +}; + +typedef std::vector> tracks_t; diff --git a/src/Tracker/trajectory.h b/src/Tracker/trajectory.h new file mode 100644 index 000000000..9b4dd22a9 --- /dev/null +++ b/src/Tracker/trajectory.h @@ -0,0 +1,404 @@ +#pragma once +#include +#include "defines.h" + +/// +/// \brief The TrajectoryPoint struct +/// +struct TrajectoryPoint +{ + /// + /// \brief TrajectoryPoint + /// + TrajectoryPoint() = default; + + /// + /// \brief TrajectoryPoint + /// \param prediction + /// + TrajectoryPoint(const Point_t& prediction, time_point_t frameTime) + : m_prediction(prediction), m_frameTime(frameTime) + { + } + + /// + /// \brief TrajectoryPoint + /// \param prediction + /// \param raw + /// + TrajectoryPoint(const Point_t& prediction, const Point_t& raw, time_point_t frameTime) + : + m_prediction(prediction), + m_raw(raw), + m_frameTime(frameTime), + m_hasRaw(true) + { + } + + /// + TrajectoryPoint(const TrajectoryPoint& tp) noexcept + : m_prediction(tp.m_prediction), m_raw(tp.m_raw), m_frameTime(tp.m_frameTime), m_hasRaw(tp.m_hasRaw) + { + } + + /// + TrajectoryPoint& operator=(const TrajectoryPoint& tp) noexcept + { + m_prediction = tp.m_prediction; + m_raw = tp.m_raw; + m_frameTime = tp.m_frameTime; + m_hasRaw = tp.m_hasRaw; + return *this; + } + + /// + TrajectoryPoint(TrajectoryPoint&&) = default; + + Point_t m_prediction; + Point_t m_raw; + time_point_t m_frameTime; + bool m_hasRaw = false; +}; + +/// +/// \brief The Trace class +/// +class Trace +{ +public: + /// + Trace() = default; + /// + Trace(const Trace&) = default; + /// + Trace(Trace&&) = default; + /// + Trace& operator=(const Trace& trace) + { + m_trace = trace.m_trace; + return *this; + } + + /// + /// \brief operator [] + /// \param i + /// \return + /// + const Point_t& operator[](size_t i) const + { + return m_trace[i].m_prediction; + } + + /// + /// \brief operator [] + /// \param i + /// \return + /// + Point_t& operator[](size_t i) + { + return m_trace[i].m_prediction; + } + + /// + /// \brief at + /// \param i + /// \return + /// + const TrajectoryPoint& at(size_t i) const + { + return m_trace[i]; + } + /// + /// \brief at + /// \param i + /// \return + /// + TrajectoryPoint& at(size_t i) + { + return m_trace[i]; + } + + /// + /// \brief size + /// \return + /// + size_t size() const + { + return m_trace.size(); + } + + /// + /// \brief push_back + /// \param prediction + /// + void push_back(const Point_t& prediction, time_point_t currTime) + { + m_trace.emplace_back(prediction, currTime); + } + void push_back(const Point_t& prediction, const Point_t& raw, time_point_t currTime) + { + m_trace.emplace_back(prediction, raw, currTime); + } + + /// + /// \brief pop_front + /// \param count + /// + void pop_front(size_t count) + { + if (count < size()) + m_trace.erase(m_trace.begin(), m_trace.begin() + count); + else + m_trace.clear(); + } + + /// + /// \brief pop_back + /// + void pop_back() + { + m_trace.pop_back(); + } + + /// + /// \brief GetRawCount + /// \param lastPeriod + /// \return + /// + size_t GetRawCount(size_t lastPeriod) const + { + size_t res = 0; + + size_t i = 0; + if (lastPeriod < m_trace.size()) + i = m_trace.size() - lastPeriod; + + for (; i < m_trace.size(); ++i) + { + if (m_trace[i].m_hasRaw) + ++res; + } + + return res; + } + + /// + /// \brief Reserve + /// \param capacity + /// \return + /// + void Reserve(size_t capacity) + { + m_trace.reserve(capacity); + } + +private: + std::vector m_trace; +}; + +/// +/// \brief The TrackingObject class +/// +struct TrackingObject +{ + Trace m_trace; // Trajectory + track_id_t m_ID; // Objects ID + cv::RotatedRect m_rrect; // Coordinates + cv::Vec m_velocity; // pixels/sec + objtype_t m_type = bad_type; // Objects type name or empty value + float m_confidence = -1; // From Detector with score (YOLO or SSD) + bool m_isStatic = false; // Object is abandoned + int m_staticTime = 0; // Object is abandoned, seconds + bool m_outOfTheFrame = false; // Is object out of the frame + mutable bool m_lastRobust = false; // saved latest robust value + + /// + TrackingObject(const cv::RotatedRect& rrect, track_id_t ID, const Trace& trace, + bool isStatic, int staticTime, bool outOfTheFrame, objtype_t type, float confidence, cv::Vec velocity) + : + m_trace(trace), m_ID(ID), m_rrect(rrect), m_velocity(velocity), m_type(type), m_confidence(confidence), + m_isStatic(isStatic), m_staticTime(staticTime), + m_outOfTheFrame(outOfTheFrame) + { + //std::cout << "TrackingObject.m_rrect: " << m_rrect.center << ", " << m_rrect.angle << ", " << m_rrect.size << std::endl; + } + + /// + TrackingObject() = default; + /// + TrackingObject(const TrackingObject&) = default; + /// + TrackingObject(TrackingObject&&) = default; + /// + TrackingObject & operator=(const TrackingObject& track) + { + m_trace = track.m_trace; + m_ID = track.m_ID; + m_rrect = track.m_rrect; + m_velocity = track.m_velocity; + m_type = track.m_type; + m_confidence = track.m_confidence; + m_isStatic = track.m_isStatic; + m_staticTime = track.m_staticTime; + m_outOfTheFrame = track.m_outOfTheFrame; + m_lastRobust = track.m_lastRobust; + + return *this; + } + /// + ~TrackingObject() = default; + + /// + /// \brief IsRobust + /// \param minTraceSize + /// \param minRawRatio + /// \param sizeRatio + /// \return + /// + bool IsRobust(int minTraceSize, float minRawRatio, cv::Size2f sizeRatio, size_t lastDetectsCount = 0) const + { + m_lastRobust = m_trace.size() > static_cast(minTraceSize); + if (lastDetectsCount) + { + size_t raws = m_trace.GetRawCount(lastDetectsCount); + m_lastRobust = (raws > 0); + } + m_lastRobust &= m_trace.GetRawCount(m_trace.size() - 1) / static_cast(m_trace.size()) > minRawRatio; + if (sizeRatio.width + sizeRatio.height > 0) + { + float sr = m_rrect.size.width / m_rrect.size.height; + if (sizeRatio.width > 0) + m_lastRobust &= (sr > sizeRatio.width); + + if (sizeRatio.height > 0) + m_lastRobust &= (sr < sizeRatio.height); + } + if (m_outOfTheFrame) + m_lastRobust = false; + + //std::cout << "lastRobust = " << m_lastRobust << ", outOfTheFrame = " << m_outOfTheFrame << ", raw count = " << m_trace.GetRawCount(m_trace.size() - 1) << ", trace = " << m_trace.size() << std::endl; + + return m_lastRobust; + } + + /// + /// \brief GetTrajectory + /// \return + /// + std::vector GetTrajectory() const + { + std::vector trajectory(m_trace.size()); + for (size_t i = 0; i < m_trace.size(); ++i) + { + trajectory[i] = m_trace.at(i).m_prediction; + } + return trajectory; + } + + /// + /// \brief LeastSquarespoly2 + /// \return + /// + void LeastSquarespoly2(size_t posFrom, size_t count, track_t& ax, track_t& v0x, track_t& x0, track_t& ay, track_t& v0y, track_t& y0) const + { + double b1_x(0), b2_x(0), b3_x(0); + double b1_y(0), b2_y(0), b3_y(0); + double t_0(0.), t_1(0.), t_2(0.), t_3(0.), t_4(0.); + double j = static_cast(posFrom); + for (size_t i = posFrom; i < count; ++i, j += 1.) + { + double sqr_j = sqr(j); + + t_0 += 1.; + t_1 += j; + t_2 += sqr_j; + t_3 += j * sqr_j; + t_4 += sqr(sqr_j); + + const auto& pt = m_trace.at(i).m_prediction; + + b1_x += pt.x; + b2_x += j * pt.x; + b3_x += sqr_j * pt.x; + + b1_y += pt.y; + b2_y += j * pt.y; + b3_y += sqr_j * pt.y; + } + + // Cramers rule for system of linear equations 3x3 + double a11(t_0), a12(t_1), a13(t_2), a21(t_1), a22(t_2), a23(t_3), a31(t_2), a32(t_3), a33(t_4); + + double det_1 = 1. / (a11 * a22 * a33 + a21 * a32 * a13 + a12 * a23 * a31 - a31 * a22 * a13 - a11 * a23 * a32 - a12 * a21 * a33); + x0 = static_cast(det_1 * (b1_x * a22 * a33 + b2_x * a32 * a13 + a12 * a23 * b3_x - b3_x * a22 * a13 - b1_x * a23 * a32 - a12 * b2_x * a33)); + v0x = static_cast(det_1 * (a11 * b2_x * a33 + a21 * b3_x * a13 + b1_x * a23 * a31 - a31 * b2_x * a13 - a11 * a23 * b3_x - b1_x * a21 * a33)); + ax = static_cast(det_1 * (a11 * a22 * b3_x + a21 * a32 * b1_x + a12 * b2_x * a31 - a31 * a22 * b1_x - a11 * b2_x * a32 - a12 * a21 * b3_x)); + y0 = static_cast(det_1 * (b1_y * a22 * a33 + b2_y * a32 * a13 + a12 * a23 * b3_y - b3_y * a22 * a13 - b1_y * a23 * a32 - a12 * b2_y * a33)); + v0y = static_cast(det_1 * (a11 * b2_y * a33 + a21 * b3_y * a13 + b1_y * a23 * a31 - a31 * b2_y * a13 - a11 * a23 * b3_y - b1_y * a21 * a33)); + ay = static_cast(det_1 * (a11 * a22 * b3_y + a21 * a32 * b1_y + a12 * b2_y * a31 - a31 * a22 * b1_y - a11 * b2_y * a32 - a12 * a21 * b3_y)); + } + + /// + struct LSParams + { + track_t m_ax = 0; + track_t m_v0x = 0; + track_t m_x0 = 0; + track_t m_ay = 0; + track_t m_v0y = 0; + track_t m_y0 = 0; + + friend std::ostream& operator<<(std::ostream& os, const LSParams& lsParaml) + { + os << "(" << lsParaml.m_ax << ", " << lsParaml.m_v0x << ", " << lsParaml.m_x0 << "), (" << lsParaml.m_ay << ", " << lsParaml.m_v0y << ", " << lsParaml.m_y0 << ")"; + return os; + } + }; + + /// + /// \brief LeastSquares2 + /// \return + /// + bool LeastSquares2(size_t framesCount, track_t& mean, track_t& stddev, LSParams& lsParams) const + { + bool res = m_trace.size() > 3; + + if (res) + { + size_t startPos = 0; +#if 0 + if (framesCount < m_trace.size()) + startPos = m_trace.size() - framesCount; + else + framesCount = m_trace.size(); +#else + framesCount = m_trace.size(); +#endif + + LeastSquarespoly2(startPos, framesCount, lsParams.m_ax, lsParams.m_v0x, lsParams.m_x0, lsParams.m_ay, lsParams.m_v0y, lsParams.m_y0); + + track_t sum = 0; + track_t sum2 = 0; + for (size_t i = startPos; i < framesCount; ++i) + { + track_t t = static_cast(i); + track_t dist = distance(m_trace[i], Point_t(lsParams.m_ax * sqr(t) + lsParams.m_v0x * t + lsParams.m_x0, + lsParams.m_ay * sqr(t) + lsParams.m_v0y * t + lsParams.m_y0)); + sum += dist; + sum2 += sqr(dist); + } + mean = sum / static_cast(framesCount); + stddev = sqrt(sum2 / static_cast(framesCount) - sqr(mean)); + } + return res; + } + + /// + /// \brief GetTrajectory + /// \return + /// + cv::Rect GetBoundingRect() const + { + return m_rrect.boundingRect(); + } +}; diff --git a/src/mtracking/defines.h b/src/mtracking/defines.h new file mode 100644 index 000000000..245fc0a2b --- /dev/null +++ b/src/mtracking/defines.h @@ -0,0 +1,461 @@ +#pragma once + +#include +#include +#include + +#ifdef HAVE_EXPERIMENTAL_FILESYSTEM +#include +namespace fs = std::experimental::filesystem; +#else +#include +namespace fs = std::filesystem; +#endif + +#include +#include "object_types.h" + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +typedef float track_t; +typedef cv::Point_ Point_t; +#define El_t CV_32F +#define Mat_t CV_32FC + +typedef std::vector assignments_t; +typedef std::vector distMatrix_t; + +typedef std::chrono::time_point time_point_t; + +/// +template +class TrackID +{ +public: + typedef T value_type; + + TrackID() = default; + TrackID(value_type val) + : m_val(val) + { + } + + bool operator==(const TrackID& id) const + { + return m_val == id.m_val; + } + + std::string ID2Str() const + { + return std::to_string(m_val); + } + static TrackID Str2ID(const std::string& id) + { + return TrackID(std::stoi(id)); + } + TrackID NextID() const + { + return TrackID(m_val + 1); + } + size_t ID2Module(size_t module) const + { + return m_val % module; + } + + value_type m_val{ 0 }; +}; + +typedef TrackID track_id_t; +namespace std +{ + template <> + struct hash + { + std::size_t operator()(const track_id_t& k) const + { + return std::hash()(k.m_val); + } + }; + +} + +/// +/// \brief config_t +/// +typedef std::multimap config_t; + +/// +/// \brief The CRegion class +/// +class CRegion +{ +public: + /// + CRegion() = default; + + /// + CRegion(const cv::Rect& rect) noexcept + : m_brect(rect) + { + B2RRect(); + } + + /// + CRegion(const cv::RotatedRect& rrect) noexcept + : m_rrect(rrect) + { + if (m_rrect.size.width < 1) + m_rrect.size.width = 1; + if (m_rrect.size.height < 1) + m_rrect.size.height = 1; + R2BRect(); + } + + /// + CRegion(const cv::RotatedRect& rrect, objtype_t type, float confidence) noexcept + : m_type(type), m_rrect(rrect), m_confidence(confidence) + { + if (m_rrect.size.width < 1) + m_rrect.size.width = 1; + if (m_rrect.size.height < 1) + m_rrect.size.height = 1; + R2BRect(); + } + + /// + CRegion(const cv::RotatedRect& rrect, const cv::Rect& brect, objtype_t type, float confidence, const cv::Mat& boxMask) noexcept + : m_type(type), m_rrect(rrect), m_brect(brect), m_confidence(confidence) + { + m_boxMask = boxMask; + + if (m_rrect.size.width < 1) + m_rrect.size.width = 1; + if (m_rrect.size.height < 1) + m_rrect.size.height = 1; + + if (!m_boxMask.empty() && m_boxMask.size() != m_brect.size()) + { + m_brect.width = m_boxMask.cols; + m_brect.height = m_boxMask.rows; + } + } + + /// + CRegion(const cv::Rect& brect, objtype_t type, float confidence) noexcept + : m_type(type), m_brect(brect), m_confidence(confidence) + { + B2RRect(); + } + + objtype_t m_type = bad_type; + cv::RotatedRect m_rrect; + cv::Rect m_brect; + track_t m_confidence = -1; + cv::Mat m_boxMask; + +private: + /// + /// \brief R2BRect + /// \return + /// + cv::Rect R2BRect() noexcept + { + m_brect = m_rrect.boundingRect(); + return m_brect; + } + /// + /// \brief B2RRect + /// \return + /// + cv::RotatedRect B2RRect() noexcept + { + m_rrect = cv::RotatedRect(m_brect.tl(), cv::Point2f(static_cast(m_brect.x + m_brect.width), static_cast(m_brect.y)), m_brect.br()); + if (m_rrect.size.width < 1) + m_rrect.size.width = 1; + if (m_rrect.size.height < 1) + m_rrect.size.height = 1; + return m_rrect; + } +}; + +typedef std::vector regions_t; + +/// +/// \brief sqr +/// \param val +/// \return +/// +template inline +T sqr(T val) +{ + return val * val; +} + +/// +/// \brief get_lin_regress_params +/// \param in_data +/// \param start_pos +/// \param in_data_size +/// \param kx +/// \param bx +/// \param ky +/// \param by +/// +template +void get_lin_regress_params( + const CONT& in_data, + size_t start_pos, + size_t in_data_size, + T& kx, T& bx, T& ky, T& by) +{ + T m1(0.), m2(0.); + T m3_x(0.), m4_x(0.); + T m3_y(0.), m4_y(0.); + + const T el_count = static_cast(in_data_size - start_pos); + for (size_t i = start_pos; i < in_data_size; ++i) + { + m1 += i; + m2 += sqr(i); + + m3_x += in_data[i].x; + m4_x += i * in_data[i].x; + + m3_y += in_data[i].y; + m4_y += i * in_data[i].y; + } + T det_1 = 1 / (el_count * m2 - sqr(m1)); + + m1 *= -1; + + kx = det_1 * (m1 * m3_x + el_count * m4_x); + bx = det_1 * (m2 * m3_x + m1 * m4_x); + + ky = det_1 * (m1 * m3_y + el_count * m4_y); + by = det_1 * (m2 * m3_y + m1 * m4_y); +} + +/// +/// \brief sqr: Euclid distance between two points +/// \param val +/// \return +/// +template inline +T distance(const POINT_TYPE& p1, const POINT_TYPE& p2) +{ + return sqrt((T)(sqr(p2.x - p1.x) + sqr(p2.y - p1.y))); +} + +/// +/// \brief Clamp: Fit rectangle to frame +/// \param rect +/// \param size +/// \return +/// +inline cv::Rect Clamp(cv::Rect rect, const cv::Size& size) +{ + if (rect.x < 0) + { + rect.width = std::min(rect.width, size.width - 1); + rect.x = 0; + } + else if (rect.x + rect.width >= size.width) + { + rect.x = std::max(0, size.width - rect.width - 1); + rect.width = std::min(rect.width, size.width - 1); + } + if (rect.y < 0) + { + rect.height = std::min(rect.height, size.height - 1); + rect.y = 0; + } + else if (rect.y + rect.height >= size.height) + { + rect.y = std::max(0, size.height - rect.height - 1); + rect.height = std::min(rect.height, size.height - 1); + } + return rect; +} + +/// +/// \brief SaveMat +/// \param m +/// \param name +/// \param path +/// +inline bool SaveMat(const cv::Mat& m, std::string prefix, const std::string& ext, const std::string& savePath, bool compressToImage) +{ + bool res = true; + + std::map depthDict; + depthDict.emplace(CV_8U, "uint8"); + depthDict.emplace(CV_8S, "int8"); + depthDict.emplace(CV_16U, "uint16"); + depthDict.emplace(CV_16S, "int16"); + depthDict.emplace(CV_32S, "int32"); + depthDict.emplace(CV_32F, "float32"); + depthDict.emplace(CV_64F, "float64"); + depthDict.emplace(CV_16F, "float16"); + + auto depth = depthDict.find(m.depth()); + if (depth == std::end(depthDict)) + { + std::cout << "File " << prefix << " has a unknown depth: " << m.depth() << std::endl; + res = false; + return res; + } + assert(depth != std::end(depthDict)); + + fs::path fullPath(savePath); + fullPath.append(prefix + "_" + std::to_string(m.cols) + "x" + std::to_string(m.rows) + "_" + depth->second + "_C" + std::to_string(m.channels()) + ext); + prefix = fullPath.generic_string(); + + if (compressToImage) + { + res = cv::imwrite(prefix, m); + } + else + { + FILE* f = 0; +#ifdef _WIN32 + fopen_s(&f, prefix.c_str(), "wb"); +#else + f = fopen(prefix.c_str(), "wb"); +#endif // _WIN32 + res = f != 0; + if (res) + { + for (int y = 0; y < m.rows; ++y) + { + fwrite(m.ptr(y), 1, m.cols * m.elemSize(), f); + } + fclose(f); + std::cout << "File " << prefix << " was writed" << std::endl; + } + } + if (res) + std::cout << "File " << prefix << " was writed" << std::endl; + else + std::cout << "File " << prefix << " can not be opened!" << std::endl; + return res; +} + +/// +/// \brief DrawFilledRect +/// +inline void DrawFilledRect(cv::Mat& frame, const cv::Rect& rect, cv::Scalar cl, int alpha) +{ + if (alpha) + { + const int alpha_1 = 255 - alpha; + const int nchans = frame.channels(); + int color[3] = { cv::saturate_cast(cl[0]), cv::saturate_cast(cl[1]), cv::saturate_cast(cl[2]) }; + for (int y = std::max(0, rect.y); y < std::min(rect.y + rect.height, frame.rows - 1); ++y) + { + uchar* ptr = frame.ptr(y) + nchans * rect.x; + for (int x = std::max(0, rect.x); x < std::min(rect.x + rect.width, frame.cols - 1); ++x) + { + for (int i = 0; i < nchans; ++i) + { + ptr[i] = cv::saturate_cast((alpha_1 * ptr[i] + alpha * color[i]) / 255); + } + ptr += nchans; + } + } + } + else + { + cv::rectangle(frame, rect, cl, cv::FILLED); + } +} + +/// +/// +/// +namespace tracking +{ +/// +/// \brief The Detectors enum +/// +enum Detectors +{ + Motion_VIBE = 0, + Motion_MOG = 1, + Motion_GMG = 2, + Motion_CNT = 3, + Motion_MOG2 = 4, + ONNX_TensorRT = 5, + DNN_OCV = 6, + DetectorsCount +}; + +/// +/// \brief The TrackerTemplate enum +/// +enum TrackerTemplate +{ + UniversalTracker = 0, + ByteTrack = 1 +}; + +/// +/// \brief The DistType enum +/// +enum DistType +{ + DistCenters, // Euclidean distance between centers, [0, 1] + DistRects, // Euclidean distance between bounding rectangles, [0, 1] + DistJaccard, // Intersection over Union, IoU, [0, 1] + DistHist, // Bhatacharia distance between histograms, [0, 1] + DistFeatureCos, // Cosine distance between embeddings, [0, 1] + DistMahalanobis, // Mahalanobis: https://ww2.mathworks.cn/help/vision/ug/motion-based-multiple-object-tracking.html + DistsCount +}; + +/// +/// \brief The FilterGoal enum +/// +enum FilterGoal +{ + FilterCenter, + FilterRect, + FilterRRect, + FiltersCount +}; + +/// +/// \brief The KalmanType enum +/// +enum KalmanType +{ + KalmanLinear, + KalmanUnscented, + KalmanAugmentedUnscented, + KalmanCount +}; + +/// +/// \brief The MatchType enum +/// +enum MatchType +{ + MatchHungrian, + MatchLAPJV, + MatchCount +}; + +/// +/// \brief The LostTrackType enum +/// +enum LostTrackType +{ + TrackNone, + TrackKCF, + TrackCSRT, + TrackDaSiamRPN, + TrackNano, + TrackVit, + SingleTracksCount +}; +} diff --git a/nms.h b/src/mtracking/nms.h similarity index 62% rename from nms.h rename to src/mtracking/nms.h index ed1e267e5..08f28b2a5 100644 --- a/nms.h +++ b/src/mtracking/nms.h @@ -10,26 +10,22 @@ * @param thresh * @param neighbors */ -inline void nms( - const std::vector& srcRects, - std::vector& resRects, - float thresh, - int neighbors = 0 - ) +inline void nms(const std::vector& srcRects, + std::vector& resRects, + float thresh, + int neighbors = 0) { resRects.clear(); const size_t size = srcRects.size(); if (!size) - { return; - } // Sort the bounding boxes by the bottom - right y - coordinate of the bounding box std::multimap idxs; for (size_t i = 0; i < size; ++i) { - idxs.insert(std::pair(srcRects[i].br().y, i)); + idxs.emplace(srcRects[i].br().y, i); } // keep looping while some indexes still remain in the indexes list @@ -48,8 +44,8 @@ inline void nms( // grab the current rectangle const cv::Rect& rect2 = srcRects[pos->second]; - float intArea = (rect1 & rect2).area(); - float unionArea = rect1.area() + rect2.area() - intArea; + float intArea = static_cast((rect1 & rect2).area()); + float unionArea = static_cast(rect1.area() + rect2.area() - intArea); float overlap = intArea / unionArea; // if there is sufficient overlap, suppress the current bounding box @@ -64,9 +60,7 @@ inline void nms( } } if (neigborsCount >= neighbors) - { resRects.push_back(rect1); - } } } @@ -79,22 +73,18 @@ inline void nms( * @param thresh * @param neighbors */ -inline void nms2( - const std::vector& srcRects, - const std::vector& scores, - std::vector& resRects, - float thresh, - int neighbors = 0, - float minScoresSum = 0.f - ) +inline void nms2(const std::vector& srcRects, + const std::vector& scores, + std::vector& resRects, + float thresh, + int neighbors = 0, + float minScoresSum = 0.f) { resRects.clear(); const size_t size = srcRects.size(); if (!size) - { return; - } assert(srcRects.size() == scores.size()); @@ -102,7 +92,7 @@ inline void nms2( std::multimap idxs; for (size_t i = 0; i < size; ++i) { - idxs.insert(std::pair(scores[i], i)); + idxs.emplace(scores[i], i); } // keep looping while some indexes still remain in the indexes list @@ -122,8 +112,8 @@ inline void nms2( // grab the current rectangle const cv::Rect& rect2 = srcRects[pos->second]; - float intArea = (rect1 & rect2).area(); - float unionArea = rect1.area() + rect2.area() - intArea; + float intArea = static_cast((rect1 & rect2).area()); + float unionArea = static_cast(rect1.area() + rect2.area() - intArea); float overlap = intArea / unionArea; // if there is sufficient overlap, suppress the current bounding box @@ -138,47 +128,41 @@ inline void nms2( ++pos; } } - if (neigborsCount >= neighbors && - scoresSum >= minScoresSum) - { + if (neigborsCount >= neighbors && scoresSum >= minScoresSum) resRects.push_back(rect1); - } } } /** - * @brief nms2 + * @brief nms3 * Non maximum suppression with detection scores * @param srcRects * @param resRects * @param thresh * @param neighbors */ -template -inline void nms3( - const std::vector& srcRects, - std::vector& resRects, - float thresh, - GET_RECT_FUNC GetRect, - GET_SCORE_FUNC GetScore, - int neighbors = 0, - float minScoresSum = 0.f - ) +template +inline void nms3(const std::vector& srcRects, + std::vector& resRects, + T thresh, + GET_RECT_FUNC GetRect, + GET_SCORE_FUNC GetScore, + GET_TYPE_FUNC GetType, + int neighbors = 0, + T minScoresSum = 0) { resRects.clear(); const size_t size = srcRects.size(); if (!size) - { return; - } // Sort the bounding boxes by the detection score - std::multimap idxs; + std::multimap idxs; for (size_t i = 0; i < size; ++i) { - idxs.insert(std::pair(GetScore(srcRects[i]), i)); + idxs.emplace(GetScore(srcRects[i]), i); } // keep looping while some indexes still remain in the indexes list @@ -188,37 +172,43 @@ inline void nms3( auto lastElem = --std::end(idxs); size_t lastPos = lastElem->second; const cv::Rect& rect1 = GetRect(srcRects[lastPos]); + auto type1 = GetType(srcRects[lastPos]); int neigborsCount = 0; - float scoresSum = lastElem->first; + T scoresSum = lastElem->first; idxs.erase(lastElem); for (auto pos = std::begin(idxs); pos != std::end(idxs); ) { // grab the current rectangle - const cv::Rect& rect2 = GetRect(srcRects[pos->second]); - - float intArea = (rect1 & rect2).area(); - float unionArea = rect1.area() + rect2.area() - intArea; - float overlap = intArea / unionArea; - - // if there is sufficient overlap, suppress the current bounding box - if (overlap > thresh) + auto type2 = GetType(srcRects[pos->second]); + if (type1 == type2) { - scoresSum += pos->first; - pos = idxs.erase(pos); - ++neigborsCount; + const cv::Rect& rect2 = GetRect(srcRects[pos->second]); + + float intArea = static_cast((rect1 & rect2).area()); + float unionArea = static_cast(rect1.area() + rect2.area() - intArea); + float overlap = intArea / unionArea; + + // if there is sufficient overlap, suppress the current bounding box + if (overlap > thresh) + { + scoresSum += pos->first; + pos = idxs.erase(pos); + ++neigborsCount; + } + else + { + ++pos; + } } else { ++pos; } } - if (neigborsCount >= neighbors && - scoresSum >= minScoresSum) - { + if (neigborsCount >= neighbors && scoresSum >= minScoresSum) resRects.push_back(srcRects[lastPos]); - } } } diff --git a/src/mtracking/object_types.cpp b/src/mtracking/object_types.cpp new file mode 100644 index 000000000..8484723c8 --- /dev/null +++ b/src/mtracking/object_types.cpp @@ -0,0 +1,89 @@ +#include "object_types.h" + +std::vector TypeConverter::m_typeNames = +{ + "person", + "bicycle", + "car", + "motorbike", + "aeroplane", + "bus", + "train", + "truck", + "boat", + "traffic_light", + "fire_hydrant", + "stop_sign", + "parking_meter", + "bench", + "bird", + "cat", + "dog", + "horse", + "sheep", + "cow", + "elephant", + "bear", + "zebra", + "giraffe", + "backpack", + "umbrella", + "handbag", + "tie", + "suitcase", + "frisbee", + "skis", + "snowboard", + "sports_ball", + "kite", + "baseball_bat", + "baseball_glove", + "skateboard", + "surfboard", + "tennis_racket", + "bottle", + "wine_glass", + "cup", + "fork", + "knife", + "spoon", + "bowl", + "banana", + "apple", + "sandwich", + "orange", + "broccoli", + "carrot", + "hot_dog", + "pizza", + "donut", + "cake", + "chair", + "sofa", + "pottedplant", + "bed", + "diningtable", + "toilet", + "tvmonitor", + "laptop", + "mouse", + "remote", + "keyboard", + "cell_phone", + "microwave", + "oven", + "toaster", + "sink", + "refrigerator", + "book", + "clock", + "vase", + "scissors", + "teddy_bear", + "hair_drier", + "toothbrush", + "vehicle", + "face" +}; + +std::string TypeConverter::m_badTypeName = "unknown"; diff --git a/src/mtracking/object_types.h b/src/mtracking/object_types.h new file mode 100644 index 000000000..25f78a5e7 --- /dev/null +++ b/src/mtracking/object_types.h @@ -0,0 +1,58 @@ +#pragma once +#include +#include +#include + +typedef int objtype_t; +constexpr objtype_t bad_type = -1; + +/// +class TypeConverter +{ +public: + /// + static std::string Type2Str(objtype_t type) + { + return (type == bad_type) ? m_badTypeName : m_typeNames[(size_t)type]; + } + + /// + static objtype_t Str2Type(const std::string& typeName) + { + for (size_t i = 0; i < m_typeNames.size(); ++i) + { + if (typeName == m_typeNames[i]) + { + //std::cout << "Str2Type: " << typeName << " exist: " << i << std::endl; + return static_cast(i); + } + } + m_typeNames.emplace_back(typeName); + //std::cout << "Str2Type: " << typeName << " new: " << (m_typeNames.size()) - 1 << std::endl; + return static_cast(m_typeNames.size()) - 1; + } + + static bool AddNewType(const std::string& typeName) + { + for (size_t i = 0; i < m_typeNames.size(); ++i) + { + if (typeName == m_typeNames[i]) + { + //std::cout << "AddNewType: " << typeName << ": false" << std::endl; + return false; + } + } + m_typeNames.emplace_back(typeName); + //std::cout << "AddNewType: " << typeName << ": " << (m_typeNames.size() - 1) << std::endl; + return true; + } + + static size_t TypesCount() + { + return m_typeNames.size(); + } + +private: + static std::vector m_typeNames; + static std::string m_badTypeName; +}; diff --git a/src/python_bind/mtracker.cpp b/src/python_bind/mtracker.cpp new file mode 100644 index 000000000..c9aa731b2 --- /dev/null +++ b/src/python_bind/mtracker.cpp @@ -0,0 +1,431 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "../mtracking/defines.h" +#include "../Tracker/Ctracker.h" +#include "../Detector/BaseDetector.h" +#include "../Detector/MotionDetector.h" + +namespace py = pybind11; + +PYBIND11_MAKE_OPAQUE(std::multimap) +PYBIND11_MAKE_OPAQUE(std::map) +PYBIND11_MAKE_OPAQUE(std::map) + +#include +#include +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include + +#if PY_VERSION_HEX >= 0x03000000 + #define PyInt_Check PyLong_Check + #define PyInt_AsLong PyLong_AsLong +#endif + +namespace pybind11 { namespace detail{ +template<> +struct type_caster{ +public: + PYBIND11_TYPE_CASTER(cv::Mat, _("numpy.ndarray")); + + //! 1. cast numpy.ndarray to cv::Mat + bool load(handle obj, bool) + { + array b = reinterpret_borrow(obj); + buffer_info info = b.request(); + + //const int ndims = (int)info.ndim; + int nh = 1; + int nw = 1; + int nc = 1; + int ndims = static_cast(info.ndim); + if(ndims == 2){ + nh = static_cast(info.shape[0]); + nw = static_cast(info.shape[1]); + } else if(ndims == 3){ + nh = static_cast(info.shape[0]); + nw = static_cast(info.shape[1]); + nc = static_cast(info.shape[2]); + }else{ + char msg[64]; + std::sprintf(msg, "Unsupported dim %d, only support 2d, or 3-d", ndims); + throw std::logic_error(msg); + return false; + } + + int dtype; + if(info.format == format_descriptor::format()){ + dtype = CV_8UC(nc); + }else if (info.format == format_descriptor::format()){ + dtype = CV_32SC(nc); + }else if (info.format == format_descriptor::format()){ + dtype = CV_32FC(nc); + }else{ + throw std::logic_error("Unsupported type, only support uchar, int32, float"); + return false; + } + + value = cv::Mat(nh, nw, dtype, info.ptr); + return true; + } + + //! 2. cast cv::Mat to numpy.ndarray + static handle cast(const cv::Mat& mat, return_value_policy, handle /*defval*/) + { + std::string format = format_descriptor::format(); + size_t elemsize = sizeof(unsigned char); + int nw = mat.cols; + int nh = mat.rows; + int nc = mat.channels(); + int depth = mat.depth(); + int type = mat.type(); + int dim = (depth == type)? 2 : 3; + + if(depth == CV_8U){ + format = format_descriptor::format(); + elemsize = sizeof(unsigned char); + }else if(depth == CV_32S){ + format = format_descriptor::format(); + elemsize = sizeof(int); + }else if(depth == CV_32F){ + format = format_descriptor::format(); + elemsize = sizeof(float); + }else{ + throw std::logic_error("Unsupport type, only support uchar, int32, float"); + } + + std::vector bufferdim; + std::vector strides; + if (dim == 2) { + bufferdim = {(size_t) nh, (size_t) nw}; + strides = {elemsize * (size_t) nw, elemsize}; + } else if (dim == 3) { + bufferdim = {(size_t) nh, (size_t) nw, (size_t) nc}; + strides = {(size_t) elemsize * nw * nc, (size_t) elemsize * nc, (size_t) elemsize}; + } + return array(buffer_info( mat.data, elemsize, format, dim, bufferdim, strides )).release(); + } +}; + +template +struct type_caster>{ + + PYBIND11_TYPE_CASTER(cv::Size_, _("tuple_wh")); + + bool load(handle obj, bool) + { + if(!py::isinstance(obj)) + { + std::logic_error("Size(w, h) should be a tuple!"); + return false; + } + + py::tuple pt = reinterpret_borrow(obj); + if (pt.size() != 2) + { + std::logic_error("Size(w, h) tuple should be size of 2"); + return false; + } + + value = cv::Size(static_cast(pt[0].cast()), static_cast(pt[1].cast())); + return true; + } + + static handle cast(const cv::Size_& sz, return_value_policy, handle) + { + return py::make_tuple(sz.width, sz.height).release(); + } +}; + +template<> +struct type_caster{ + + PYBIND11_TYPE_CASTER(cv::Point, _("tuple_xy")); + + bool load(handle obj, bool) + { + if (!py::isinstance(obj)) + { + std::logic_error("Point(x,y) should be a tuple!"); + return false; + } + + py::tuple pt = reinterpret_borrow(obj); + if(pt.size()!=2) + { + std::logic_error("Point(x,y) tuple should be size of 2"); + return false; + } + + value = cv::Point(pt[0].cast(), pt[1].cast()); + return true; + } + + static handle cast(const cv::Point& pt, return_value_policy, handle) + { + return py::make_tuple(pt.x, pt.y).release(); + } +}; + + +}}//! end namespace pybind11::detail + + +/// +class PyDetector : public BaseDetector +{ +public: + using BaseDetector::BaseDetector; + + PyDetector() + { + std::cout << "PyDetector" << std::endl; + } + + bool Init(const config_t& config) override + { + PYBIND11_OVERLOAD_PURE(bool, BaseDetector, Init, config); + } + + void DetectMat(cv::Mat frame) override + { + PYBIND11_OVERLOAD(void, BaseDetector, DetectMat, frame); + } + + bool CanGrayProcessing() const override + { + PYBIND11_OVERLOAD_PURE(bool, BaseDetector, CanGrayProcessing, ); + } +}; + +/// +class PyMotionDetector : public MotionDetector +{ +public: + using MotionDetector::MotionDetector; + + bool Init(const config_t& config) override + { + PYBIND11_OVERLOAD(bool, MotionDetector, Init, config); + } + + bool CanGrayProcessing() const override + { + PYBIND11_OVERLOAD(bool, MotionDetector, CanGrayProcessing, ); + } +}; + +/// +cv::Mat read_image(std::string image_name) +{ + return cv::imread(image_name, cv::IMREAD_COLOR); +} + +/// +void show_image(cv::Mat image) +{ +#ifndef SILENT_WORK + cv::imshow("image_from_Cpp", image); + cv::waitKey(0); +#endif +} + +/// +cv::Mat passthru(cv::Mat image) +{ + return image; +} + +/// +cv::Mat cloneimg(cv::Mat image) +{ + return image.clone(); +} + +class PyBaseTracker : public BaseTracker +{ +public: + using BaseTracker::BaseTracker; + + void UpdateMat(const regions_t& regions, cv::Mat currFrame, float fps) override + { + PYBIND11_OVERLOAD_PURE(void, BaseTracker, Update, regions, currFrame, fps); + } + + std::vector GetTracksCopy() const override + { + PYBIND11_OVERLOAD(std::vector, BaseTracker, GetTracksCopy); + } + + void GetRemovedTracks(std::vector& trackIDs) const override + { + PYBIND11_OVERLOAD_PURE(void, BaseTracker, GetRemovedTracks, trackIDs); + } +}; + +/// +PYBIND11_MODULE(pymtracking, m) +{ + m.doc() = R"pbdoc( + mtracking library + ----------------------- + .. currentmodule:: pymtracking + .. autosummary:: + :toctree: _generate + add + subtract + )pbdoc"; + + py::class_(m, "KeyVal") + .def(py::init<>()) + .def("Add", &KeyVal::Add); + + py::bind_map>(m, "MapStringString"); + py::bind_map>(m, "MapStringDouble"); + + py::class_(m, "TrackerSettings") + .def(py::init<>()) + .def("CheckDistance", &TrackerSettings::CheckDistance) + .def("SetDistances", &TrackerSettings::SetDistances) + .def("SetDistance", &TrackerSettings::SetDistance) + .def("AddNearTypes", &TrackerSettings::AddNearTypes) + .def("CheckType", &TrackerSettings::CheckType) + .def_readwrite("kalmanType", &TrackerSettings::m_kalmanType) + .def_readwrite("filterGoal", &TrackerSettings::m_filterGoal) + .def_readwrite("lostTrackType", &TrackerSettings::m_lostTrackType) + .def_readwrite("matchType", &TrackerSettings::m_matchType) + .def_readwrite("dt", &TrackerSettings::m_dt) + .def_readwrite("accelNoiseMag", &TrackerSettings::m_accelNoiseMag) + .def_readwrite("useAcceleration", &TrackerSettings::m_useAcceleration) + .def_readwrite("distThres", &TrackerSettings::m_distThres) + .def_readwrite("minAreaRadiusPix", &TrackerSettings::m_minAreaRadiusPix) + .def_readwrite("minAreaRadiusK", &TrackerSettings::m_minAreaRadiusK) + .def_readwrite("maximumAllowedLostTime", &TrackerSettings::m_maximumAllowedLostTime) + .def_readwrite("maxTraceLength", &TrackerSettings::m_maxTraceLength) + .def_readwrite("useAbandonedDetection", &TrackerSettings::m_useAbandonedDetection) + .def_readwrite("minStaticTime", &TrackerSettings::m_minStaticTime) + .def_readwrite("maxStaticTime", &TrackerSettings::m_maxStaticTime); + + py::class_(m, "CvRect") + .def(py::init<>()) + .def_readwrite("x", &cv::Rect::x) + .def_readwrite("y", &cv::Rect::y) + .def_readwrite("width", &cv::Rect::width) + .def_readwrite("height", &cv::Rect::height); + + py::class_(m, "CvRRect") + .def(py::init<>()) + .def("brect", &cv::RotatedRect::boundingRect); + + py::class_(m, "CRegion") + .def(py::init<>()) + .def_readwrite("brect", &CRegion::m_brect) + .def_readwrite("type", &CRegion::m_type) + .def_readwrite("confidence", &CRegion::m_confidence); + + py::class_(m, "TrackingObject") + .def(py::init<>()) + .def("IsRobust", &TrackingObject::IsRobust) + .def("GetTrajectory", &TrackingObject::GetTrajectory) + .def("GetBoundingRect", &TrackingObject::GetBoundingRect) + .def_readwrite("rrect", &TrackingObject::m_rrect) + .def_readwrite("ID", &TrackingObject::m_ID) + .def_readwrite("isStatic", &TrackingObject::m_isStatic) + .def_readwrite("outOfTheFrame", &TrackingObject::m_outOfTheFrame) + .def_readwrite("type", &TrackingObject::m_type) + .def_readwrite("confidence", &TrackingObject::m_confidence) + .def_readwrite("velocity", &TrackingObject::m_velocity); + + py::class_ mtracker(m, "MTracker"); + mtracker.def(py::init(&BaseTracker::CreateTracker)); + mtracker.def("Update", &BaseTracker::UpdateMat); + mtracker.def("GetTracks", &BaseTracker::GetTracksCopy); + + py::enum_(mtracker, "DistType") + .value("DistCenters", tracking::DistType::DistCenters) + .value("DistRects", tracking::DistType::DistRects) + .value("DistJaccard", tracking::DistType::DistJaccard) + .export_values(); + + py::enum_(mtracker, "FilterGoal") + .value("FilterCenter", tracking::FilterGoal::FilterCenter) + .value("FilterRect", tracking::FilterGoal::FilterRect) + .export_values(); + + py::enum_(mtracker, "KalmanType") + .value("KalmanLinear", tracking::KalmanType::KalmanLinear) + .value("KalmanUnscented", tracking::KalmanType::KalmanUnscented) + .value("KalmanAugmentedUnscented", tracking::KalmanType::KalmanAugmentedUnscented) + .export_values(); + + py::enum_(mtracker, "MatchType") + .value("MatchHungrian", tracking::MatchType::MatchHungrian) + .value("MatchLAPJV", tracking::MatchType::MatchLAPJV) + .export_values(); + + py::enum_(mtracker, "LostTrackType") + .value("TrackNone", tracking::LostTrackType::TrackNone) + .value("TrackKCF", tracking::LostTrackType::TrackKCF) + .value("TrackCSRT", tracking::LostTrackType::TrackCSRT) + .export_values(); + + py::class_ base_detector(m, "BaseDetector"); + base_detector.def(py::init(&BaseDetector::CreateDetectorKV)); + base_detector.def("Init", &BaseDetector::Init); + base_detector.def("Detect", &BaseDetector::DetectMat); + base_detector.def("ResetModel", &BaseDetector::ResetModel); + base_detector.def("CanGrayProcessing", &BaseDetector::CanGrayProcessing); + base_detector.def("SetMinObjectSize", &BaseDetector::SetMinObjectSize); + base_detector.def("GetDetects", &BaseDetector::GetDetects); + base_detector.def("CalcMotionMap", &BaseDetector::CalcMotionMap); + + py::class_ mdetector(m, "MotionDetector"); + mdetector.def(py::init()); + mdetector.def("Init", &MotionDetector::Init); + mdetector.def("Detect", &MotionDetector::Detect); + mdetector.def("ResetModel", &MotionDetector::ResetModel); + mdetector.def("CanGrayProcessing", &MotionDetector::CanGrayProcessing); + mdetector.def("SetMinObjectSize", &MotionDetector::SetMinObjectSize); + mdetector.def("GetDetects", &MotionDetector::GetDetects); + mdetector.def("CalcMotionMap", &MotionDetector::CalcMotionMap); + + py::enum_(mdetector, "BGFG_ALGS") + .value("VIBE", BackgroundSubtract::BGFG_ALGS::ALG_VIBE) + .value("MOG", BackgroundSubtract::BGFG_ALGS::ALG_MOG) + .value("GMG", BackgroundSubtract::BGFG_ALGS::ALG_GMG) + .value("CNT", BackgroundSubtract::BGFG_ALGS::ALG_CNT) + .value("MOG2", BackgroundSubtract::BGFG_ALGS::ALG_MOG2) + .export_values(); + + py::enum_(base_detector, "Detectors") + .value("VIBE", tracking::Detectors::Motion_VIBE) + .value("MOG", tracking::Detectors::Motion_MOG) + .value("GMG", tracking::Detectors::Motion_GMG) + .value("CNT", tracking::Detectors::Motion_CNT) + .value("MOG2", tracking::Detectors::Motion_MOG2) + .value("Yolo_TensorRT", tracking::Detectors::ONNX_TensorRT) + .value("DNN_OCV", tracking::Detectors::DNN_OCV) + .export_values(); + + m.def("read_image", &read_image, "A function that read an image", + py::arg("image")); + + m.def("show_image", &show_image, "A function that show an image", + py::arg("image")); + + m.def("passthru", &passthru, "Passthru function", py::arg("image")); + m.def("clone", &cloneimg, "Clone function", py::arg("image")); + +#define VERSION_INFO "1.0.1" +#ifdef VERSION_INFO + m.attr("__version__") = VERSION_INFO; +#else + m.attr("__version__") = "dev"; +#endif +} diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt new file mode 100644 index 000000000..ddef494c4 --- /dev/null +++ b/thirdparty/CMakeLists.txt @@ -0,0 +1,7 @@ +add_subdirectory(inih) +#add_subdirectory(Circular_Code) +add_subdirectory(spdlog) + +if (USE_CLIP) + add_subdirectory(ruclip) +endif(USE_CLIP) diff --git a/thirdparty/Circular_Code/CircHelper.h b/thirdparty/Circular_Code/CircHelper.h new file mode 100644 index 000000000..4e23696c1 --- /dev/null +++ b/thirdparty/Circular_Code/CircHelper.h @@ -0,0 +1,62 @@ +// ========================================================================== +// Copyright (C) 2011 Lior Kogan (koganlior1@gmail.com) +// ========================================================================== + +#pragma once + +// ========================================================================== +static const double _2Pi= 6.2831853071795864769252867665590057683943387987502116419498891846156328125724179972560696; + +// ========================================================================== +// square (x*x) +template +T Sqr(const T& x) +{ + return x*x; +} + +// ========================================================================== +// Floating-point modulo +// The result (the remainder) has the same sign as the divisor. +// Similar to matlab's mod(); Not similar to fmod() - Mod(-3,4)= 1 fmod(-3,4)= -3 +template +T Mod(T x, T y) +{ + static_assert(!std::numeric_limits::is_exact , "Mod: floating-point type expected"); + + if (0. == y) + return x; + + double m= x - y * floor(x/y); + + // handle boundary cases resulting from floating-point limited accuracy: + + if (y > 0) // modulo range: [0..y) + { + if (m>=y) // Mod(-1e-16 , 360. ): m= 360. + return 0; + + if (m<0 ) + { + if (y+m == y) + return 0 ; // just in case... + else + return y+m; // Mod(106.81415022205296 , _TWO_PI ): m= -1.421e-14 + } + } + else // modulo range: (y..0] + { + if (m<=y) // Mod(1e-16 , -360. ): m= -360. + return 0; + + if (m>0 ) + { + if (y+m == y) + return 0 ; // just in case... + else + return y+m; // Mod(-106.81415022205296, -_TWO_PI): m= 1.421e-14 + } + } + + return m; +} diff --git a/thirdparty/Circular_Code/CircStat.h b/thirdparty/Circular_Code/CircStat.h new file mode 100644 index 000000000..bb413c29d --- /dev/null +++ b/thirdparty/Circular_Code/CircStat.h @@ -0,0 +1,427 @@ +// ========================================================================== +// Copyright (C) 2011 Lior Kogan (koganlior1@gmail.com) +// ========================================================================== +// classes defined here: +// CircAverage - calculate average set of circular-values +// WeightedCircAverage - calculate weighted-average set of circular-values +// CAvrgSampledCircSignal - estimate the average of a sampled continuous-time circular signal, using circular linear interpolation +// CircMedian - calculate median set of circular-values +// ========================================================================== + +#pragma once + +#include +#include +#include +#include +#include +#include // sort + +// ========================================================================== +// calculate average set of circular-values +// return set of average values +// T is a circular value type defined with the CircValTypeDef macro +template +std::set> CircAverage(std::vector> const& A) +{ + std::set> MinAvrgVals ; // results set + + // ---------------------------------------------- + // all vars: UnsignedDegRange [0,360) + double fSum = 0.; // of all elements of A + double fSumSqr = 0.; // of all elements of A + double fMinSumSqrDiff ; // minimal sum of squares of differences + std::vector LowerAngles ; // ascending [ 0,180) + std::vector UpperAngles ; // descending (360,180) + double fTestAvrg ; + + // ---------------------------------------------- + // local functions - implemented as lambdas + // ---------------------------------------------- + + // calc sum(dist(180, Bi)^2) - all values are in set B + // dist(180,Bi)= |180-Bi| + // sum(dist(x, Bi)^2) = sum((180-Bi)^2) = sum(180^2-2*180*Bi + Bi^2) = 180^2*A.size - 360*sum(Ai) + sum(Ai^2) + auto SumSqr = [&]() -> double + { + return 32400.*A.size() - 360.*fSum + fSumSqr; + }; + + // calc sum(dist(x, Ai)^2). A=B+C; set D is empty + // dist(x,Bi)= |x-Bi| + // dist(x,Ci)= 360-(Ci-x) + // sum(dist(x, Bi)^2)= sum( (x-Bi) ^2)= sum( Bi^2 + x^2 - 2*Bi*x) + // sum(dist(x, Ci)^2)= sum((360-(Ci-x))^2)= sum(360^2 + Ci^2 + x^2 - 2*360*Ci + 2*360*x - 2*Ci*x) + // sum(dist(x, Bi)^2) + sum(dist(x, Ci)^2) = nCountC*360^2 + sum(Ai^2) + nCountA*x^2 - 2*360*sum(Ci) + nCountC*2*360*x - 2*x*sum(Ai) + auto SumSqrC= [&](double x, size_t nCountC, double fSumC) -> double + { + return x*(A.size()*x - 2*fSum) + fSumSqr - 2*360.*fSumC + nCountC*( 2*360.*x + 360.*360.); + }; + + // calc sum(dist(x, Ai)^2). A=B+D; set C is empty + // dist(x,Bi)= |x-Bi| + // dist(x,Di)= 360-(x-Di) + // sum(dist(x,Bi)^2)= sum( (x-Bi)^2)= sum( Bi^2 + x^2 - 2*Bi*x) + // sum(dist(x,Di)^2)= sum(360-(x-Di)^2)= sum(360^2 + Di^2 + x^2 + 2*360*Di - 2*360*x - 2*Di*x) + // sum(dist(x, Bi)^2) + sum(dist(x, Di)^2) = nCountD*360^2 + sum(Ai^2) + nCountA*x^2 + 2*360*sum(Di) - nCountD*2*360*x - 2*x*sum(Ai) + auto SumSqrD= [&](double x, size_t nCountD, double fSumD) -> double + { + return x*(A.size()*x - 2*fSum) + fSumSqr + 2*360.*fSumD + nCountD*(-2*360.*x + 360.*360.); + }; + + // update MinAvrgAngles if lower/equal fMinSumSqrDiff found + auto TestSum= [&](double fTestAvrg, double fTestSumDiffSqr) -> void + { + if (fTestSumDiffSqr < fMinSumSqrDiff) + { + MinAvrgVals.clear(); + MinAvrgVals.insert(CircVal(fTestAvrg)); + fMinSumSqrDiff= fTestSumDiffSqr; + } + else if (fTestSumDiffSqr == fMinSumSqrDiff) + MinAvrgVals.insert(CircVal(fTestAvrg)); + }; + + // ---------------------------------------------- + for (const auto& a : A) + { + double v= CircVal(a); // convert to [0.360) + fSum += v ; + fSumSqr+= Sqr(v); + if (v < 180.) LowerAngles.push_back(v); + else if (v > 180.) UpperAngles.push_back(v); + } + + sort(LowerAngles.begin(), LowerAngles.end() ); // ascending [ 0,180) + sort(UpperAngles.begin(), UpperAngles.end(), std::greater()); // descending (360,180) + + // ---------------------------------------------- + // start with avrg= 180, sets c,d are empty + // ---------------------------------------------- + MinAvrgVals.clear(); + MinAvrgVals.insert(CircVal(180.)); + fMinSumSqrDiff= SumSqr(); + + // ---------------------------------------------- + // average in (180,360), set D: values in range [0,avrg-180) + // ---------------------------------------------- + double fLowerBound= 0.; // of current sector + double fSumD = 0.; // of elements of set D + + auto iter= LowerAngles.begin(); + for (size_t d= 0; d < LowerAngles.size(); ++d) + { + // 1st iteration : average in ( 180, lowerAngles[0]+180] + // next iterations: average in (lowerAngles[i-1]+180, lowerAngles[i]+180] + // set D : lowerAngles[0..d] + + fTestAvrg= (fSum + 360.*d)/A.size(); // average for sector, that minimizes SumDiffSqr + + if ((fTestAvrg > fLowerBound+180.) && (fTestAvrg <= *iter+180.)) // if fTestAvrg is within sector + TestSum(fTestAvrg, SumSqrD(fTestAvrg, d, fSumD)); // check if fTestAvrg generates lower SumSqr + + fLowerBound= *iter ; + fSumD += fLowerBound; + ++iter; + } + + // last sector : average in [lowerAngles[lastIdx]+180, 360) + fTestAvrg= (fSum + 360.*LowerAngles.size())/A.size(); // average for sector, that minimizes SumDiffSqr + + if ((fTestAvrg < 360.) && (fTestAvrg > fLowerBound)) // if fTestAvrg is within sector + TestSum(fTestAvrg, SumSqrD(fTestAvrg, LowerAngles.size(), fSumD)); // check if fTestAvrg generates lower SumSqr + + // ---------------------------------------------- + // average in [0,180); set C: values in range (avrg+180, 360) + // ---------------------------------------------- + double fUpperBound= 360.; // of current sector + double fSumC = 0.; // of elements of set C + + iter= UpperAngles.begin(); + for (size_t c= 0; c < UpperAngles.size(); ++c) + { + // 1st iteration : average in [upperAngles[0]-180, 360 ) + // next iterations: average in [upperAngles[i]-180, upperAngles[i-1]-180) + // set C : upperAngles[0..c] (descendingly sorted) + + fTestAvrg= (fSum - 360.*c)/A.size(); // average for sector, that minimizes SumDiffSqr + + if ((fTestAvrg >= *iter-180.) && (fTestAvrg < fUpperBound-180.)) // if fTestAvrg is within sector + TestSum(fTestAvrg, SumSqrC(fTestAvrg, c, fSumC)); // check if fTestAvrg generates lower SumSqr + + fUpperBound= *iter ; + fSumC += fUpperBound; + ++iter; + } + + // last sector : average in [0, upperAngles[lastIdx]-180) + fTestAvrg= (fSum - 360.*UpperAngles.size())/A.size(); // average for sector, that minimizes SumDiffSqr + + if ((fTestAvrg >= 0.) && (fTestAvrg < fUpperBound)) // if fTestAvrg is within sector + TestSum(fTestAvrg, SumSqrC(fTestAvrg, UpperAngles.size(), fSumC)); // check if fTestAvrg generates lower SumSqr + + // ---------------------------------------------- + return MinAvrgVals; +} + +// ========================================================================== +// calculate weighted-average set of circular-values +// return set of average values +// T is a circular value type defined with the CircValTypeDef macro +template +std::set> WeightedCircAverage(std::vector,double>> const& A) // vector +{ + std::set> MinAvrgVals ; // results set + + // ---------------------------------------------- + // all vars: UnsignedDegRange [0,360) + double fASumW = 0.; // sum(Wi ) of all elements of A + double fASumWA = 0.; // sum(Wi*Ai ) of all elements of A + double fASumWA2 = 0.; // sum(Wi*Ai^2) of all elements of A + double fMinSumSqrDiff ; // minimal sum of squares of differences + std::vector> LowerAngles; // ascending [ 0,180) + std::vector> UpperAngles; // descending (360,180) + double fTestAvrg ; + + // ---------------------------------------------- + // local functions - implemented as lambdas + // ---------------------------------------------- + + // calc sum(Wi*dist(180, Bi)^2) - all values are in set B + // dist(180,Bi)= |180-Bi| + // sum(Wi*dist(x, Bi)^2) = sum(Wi*(180-Bi)^2) = sum(Wi*(180^2-2*180*Bi + Bi^2)) = 180^2*fSumW - 360*sum(Wi*Ai) + sum(Wi*Ai^2) + auto SumSqr = [&]() -> double + { + return 32400.*fASumW - 360.*fASumWA + fASumWA2; + }; + + // calc sum(Wi*dist(x, Ai)^2). A=B+C; set D is empty + // dist(x,Bi)= |x-Bi| + // dist(x,Ci)= 360-(Ci-x) + // sum(Wi*dist(x,Bi)^2)= sum(Wi*( (x-Bi) ^2))= sum(Wi*( Bi^2 + x^2 - 2*Bi*x)) + + // sum(Wi*dist(x,Ci)^2)= sum(Wi*((360-(Ci-x))^2))= sum(Wi*(360^2 + Ci^2 + x^2 - 2*360*Ci + 2*360*x - 2*Ci*x)) + // ========================================================== + // sum(Wi*( Ai^2 + x^2 - 2*Ai*x)) + auto SumSqrC= [&](double x , + double fCSumW , // sum(Wi ) of all elements of C + double fCSumWC ) -> double // sum(Wi*Ci) of all elements of C + { + return fASumWA2 + x*x*fASumW -2*x*fASumWA - 720*fCSumWC + (129600+720*x)*fCSumW; + }; + + // calc sum(Wi*dist(x, Ai)^2). A=B+D; set C is empty + // dist(x,Bi)= |x-Bi| + // dist(x,Di)= 360-(x-Di) + // sum(Wi*dist(x,Bi)^2)= sum(Wi*( (x-Bi) ^2))= sum(Wi*( Bi^2 + x^2 - 2*Bi*x)) + // sum(Wi*dist(x,Di)^2)= sum(Wi*((360-(x-Di))^2))= sum(Wi*(360^2 + Di^2 + x^2 + 2*360*Di - 2*360*x - 2*Di*x)) + // ========================================================== + // sum(Wi*( Ai^2 + x^2 - 2*Ai*x)) + auto SumSqrD= [&](double x , + double fDSumW , // sum(Wi ) of all elements of D + double fDSumWD ) -> double // sum(Wi*Di) of all elements of D + { + return fASumWA2 + x*x*fASumW -2*x*fASumWA + 720*fDSumWD + (129600-720*x)*fDSumW; + }; + + // update MinAvrgAngles if lower/equal fMinSumSqrDiff found + auto TestSum= [&](double fTestAvrg, double fTestSumDiffSqr) -> void + { + if (fTestSumDiffSqr < fMinSumSqrDiff) + { + MinAvrgVals.clear(); + MinAvrgVals.insert(CircVal(fTestAvrg)); + fMinSumSqrDiff= fTestSumDiffSqr; + } + else if (fTestSumDiffSqr == fMinSumSqrDiff) + MinAvrgVals.insert(CircVal(fTestAvrg)); + }; + + // ---------------------------------------------- + for (const auto& a : A) + { + double v= CircVal(a.first); // convert to [0.360) + double w= a.second; // weight + fASumW += w ; + fASumWA+= w*v ; + fASumWA2= w*v*v; + + if (v < 180.) LowerAngles.push_back(std::pair(v,w)); + else if (v > 180.) UpperAngles.push_back(std::pair(v,w)); + } + + sort(LowerAngles.begin(), LowerAngles.end() ); // ascending [ 0,180) + sort(UpperAngles.begin(), UpperAngles.end(), std::greater>()); // descending (360,180) + + // ---------------------------------------------- + // start with avrg= 180, sets c,d are empty + // ---------------------------------------------- + MinAvrgVals.clear(); + MinAvrgVals.insert(CircVal(180.)); + fMinSumSqrDiff= SumSqr(); + + // ---------------------------------------------- + // average in (180,360), set D: values in range [0,avrg-180) + // ---------------------------------------------- + double fLowerBound= 0.; // of current sector + double fDSumW = 0.; // sum(Wi ) of all elements of D + double fDSumWD = 0.; // sum(Wi*Di) of all elements of D + + auto iter= LowerAngles.begin(); + for (size_t d= 0; d < LowerAngles.size(); ++d) + { + // 1st iteration : average in ( 180, lowerAngles[0]+180] + // next iterations: average in (lowerAngles[i-1]+180, lowerAngles[i]+180] + // set D : lowerAngles[0..d] + + fTestAvrg= (fASumWA + 360.*fDSumW)/fASumW; // average for sector, that minimizes SumDiffSqr + + if ((fTestAvrg > fLowerBound+180.) && (fTestAvrg <= (*iter).first+180.)) // if fTestAvrg is within sector + TestSum(fTestAvrg, SumSqrD(fTestAvrg, fDSumW, fDSumWD)); // check if fTestAvrg generates lower SumSqr + + fLowerBound= (*iter).first ; + fDSumW += (*iter).second ; + fDSumWD += (*iter).second * (*iter).first; + ++iter; + } + + // last sector : average in [lowerAngles[lastIdx]+180, 360) + fTestAvrg= (fASumWA + 360.*fDSumW)/fASumW; // average for sector, that minimizes SumDiffSqr + + if ((fTestAvrg < 360.) && (fTestAvrg > fLowerBound)) // if fTestAvrg is within sector + TestSum(fTestAvrg, SumSqrD(fTestAvrg, fDSumW, fDSumWD)); // check if fTestAvrg generates lower SumSqr + + // ---------------------------------------------- + // average in [0,180); set C: values in range (avrg+180, 360) + // ---------------------------------------------- + double fUpperBound= 360.; // of current sector + double fCSumW = 0.; // sum(Wi ) of all elements of C + double fCSumWC = 0.; // sum(Wi*Ci) of all elements of C + + iter= UpperAngles.begin(); + for (size_t c= 0; c < UpperAngles.size(); ++c) + { + // 1st iteration : average in [upperAngles[0]-180, 360 ) + // next iterations: average in [upperAngles[i]-180, upperAngles[i-1]-180) + // set C : upperAngles[0..c] (descendingly sorted) + + fTestAvrg= (fASumWA - 360.*fCSumW)/fASumW; // average for sector, that minimizes SumDiffSqr + + if ((fTestAvrg >= (*iter).first-180.) && (fTestAvrg < fUpperBound-180.)) // if fTestAvrg is within sector + TestSum(fTestAvrg, SumSqrC(fTestAvrg, fCSumW, fCSumWC)); // check if fTestAvrg generates lower SumSqr + + fUpperBound= (*iter).first ; + fCSumW += (*iter).second ; + fCSumWC += (*iter).second * (*iter).first; + ++iter; + } + + // last sector : average in [0, upperAngles[lastIdx]-180) + fTestAvrg= (fASumWA - 360.*fCSumW)/fASumW; // average for sector, that minimizes SumDiffSqr + + if ((fTestAvrg >= 0.) && (fTestAvrg < fUpperBound)) // if fTestAvrg is within sector + TestSum(fTestAvrg, SumSqrC(fTestAvrg, fCSumW, fCSumWC)); // check if fTestAvrg generates lower SumSqr + + // ---------------------------------------------- + return MinAvrgVals; +} + +// ========================================================================== +// estimate the average of a sampled continuous-time circular signal, using circular linear interpolation +// T is a circular value type defined with the CircValTypeDef macro +template +class CAvrgSampledCircSignal +{ + size_t m_nSamples ; + CircVal m_fPrevVal ; // previous value + double m_fPrevTime; // previous time + std::vector, double>> m_Intervals; // vector of (avrg,weight) for each interval + +public: + CAvrgSampledCircSignal() + { + m_nSamples= 0; + } + + void AddMeasurement(CircVal fVal, double fTime) + { + if (m_nSamples) + { + assert(fTime > m_fPrevTime); + + double fIntervalAvrg = CircVal::Wrap((double)m_fPrevVal + CircVal::Sdist(m_fPrevVal, fVal)/2.); + double fIntervalWeight= fTime-m_fPrevTime ; + m_Intervals.push_back(std::make_pair(fIntervalAvrg, fIntervalWeight)); + } + + m_fPrevVal = fVal ; + m_fPrevTime= fTime; + ++m_nSamples; + } + + // calculate the weighted average for all intervals + bool GetAvrg(CircVal& fAvrg) + { + switch (m_nSamples) + { + case 0: + fAvrg= CircVal::GetZ(); + return false; + + case 1: + fAvrg= m_fPrevVal; + return true; + + default: + fAvrg= *WeightedCircAverage(m_Intervals).begin(); + return true; + } + } +}; + +// ========================================================================== +// calculate median set of circular-values +// return set of median values +// T is a circular value type defined with the CircValTypeDef macro +template +std::set> CircMedian(std::vector> const& A) +{ + std::set> X; // results set + + // ---------------------------------------------- + std::set> B; + if (A.size() % 2 == 0) // even number of values + { + auto S= A; + std::sort(S.begin(), S.end()); // A, sorted + + for (size_t m= 0; m < S.size(); ++m) + { + size_t n= m+1; if (n==S.size()) n= 0; + double d= CircVal::Sdist(S[m], S[n]); + + // insert average set of each two circular-consecutive values + B.insert(((double)S[m] + d/2.)); + if (d == -CircVal::GetR()/2.) + B.insert(((double)S[n] + d/2.)); + } + } + else // odd number of values + for (size_t m= 0; m < A.size(); ++m) + B.insert(A[m]); // convert vector to set - remove duplicates + + // ---------------------------------------------- + double fMinSum= std::numeric_limits::max(); + + for (const auto& b : B) + { + double fSum= 0.; // sum(|Sdist(a, b)|) + for (const auto& a : A) + fSum+= std::abs(CircVal::Sdist(b, a)); + + if (fSum==fMinSum) X.insert(b); + else if (fSum< fMinSum) { X.clear(); X.insert(b); fMinSum= fSum; } + } + + // ---------------------------------------------- + return X; +} diff --git a/thirdparty/Circular_Code/CircVal.h b/thirdparty/Circular_Code/CircVal.h new file mode 100644 index 000000000..2cac9efb7 --- /dev/null +++ b/thirdparty/Circular_Code/CircVal.h @@ -0,0 +1,346 @@ +// ========================================================================== +// Copyright (C) 2011 Lior Kogan (koganlior1@gmail.com) +// ========================================================================== +// classes defined here: +// CircVal - circular-value +// CircValTester - tester for CircVal class +// ========================================================================== + +#pragma once + +//#define _USE_MATH_DEFINES // M_PI +#include +#include +#include +#include + +#include "FPCompare.h" +#include "CircHelper.h" + +// ========================================================================== +// macro for defining a circular-value type +#define CircValTypeDef(_Name, _L, _H, _Z) \ + struct _Name \ + { \ + static const double L ; /* range: [L,H) */ \ + static const double H ; \ + static const double Z ; /* zero-value */ \ + static const double R ; /* range */ \ + static const double R_2; /* half range */ \ + }; \ + \ + const double _Name::L = (_L) ; \ + const double _Name::H = (_H) ; \ + const double _Name::Z = (_Z) ; \ + const double _Name::R = ((_H)-(_L)) ; \ + const double _Name::R_2= ((_H)-(_L))/2.; + +// ========================================================================== +// basic circular-value types +CircValTypeDef(SignedDegRange , -180., 180., 0. ) +CircValTypeDef(UnsignedDegRange, 0., 360., 0. ) +CircValTypeDef(SignedRadRange , -M_PI, M_PI, 0. ) +CircValTypeDef(UnsignedRadRange, 0., 2*M_PI, 0. ) + +// some additional circular-value types - for testing +CircValTypeDef(TestRange0 , 3., 10., 5.3) +CircValTypeDef(TestRange1 , -3., 10., -3.0) +CircValTypeDef(TestRange2 , -3., 10., 9.9) +CircValTypeDef(TestRange3 , -13., -3., -5.3) + + +// ========================================================================== +// circular-value +// Type should be defined using the CircValTypeDef macro +template +class CircVal +{ + double val; // actual value [L,H) + + // --------------------------------------------- +public: + static double GetL() { return Type::L; } + static double GetH() { return Type::H; } + static double GetZ() { return Type::Z; } + static double GetR() { return Type::R; } + + // --------------------------------------------- + static bool IsInRange(double r) + { + return (r>=Type::L && r=Type::L) + { + if (r< Type::H ) return r ; + else if (r< Type::H+Type::R) return r-Type::R; + } + else + if (r>=Type::L-Type::R) return r+Type::R; + + // general case + return Mod(r - Type::L, Type::R) + Type::L; + } + + // --------------------------------------------- + // the length of shortest directed walk from c1 to c2 + // return value is in [-R/2, R/2) + static double Sdist(const CircVal& c1, const CircVal& c2) + { + double d= c2.val-c1.val; + if (d < -Type::R_2) return d + Type::R; + if (d >= Type::R_2) return d - Type::R; + return d ; + } + + // the length of the shortest increasing walk from c1 to c2 + // return value is in [0, R) + static double Pdist(const CircVal& c1, const CircVal& c2) + { + return c2.val>=c1.val ? c2.val-c1.val : Type::R-c1.val+c2.val; + } + + // --------------------------------------------- + CircVal() + { + val= Type::Z; + } + + // construction based on a floating-point value + // should only be called when the floating-point is a value in the range! + // to translate a floating-point such that 0 is mapped to Type::Z, call ToC() + CircVal(double r) + { + val= Wrap(r); + } + + // construction based on a circular value of the same type + CircVal(const CircVal& c) + { + val= c.val; + } + + // construction based on a circular value of another type + // sample use: CircVal c= c2; -or- CircVal c(c2); + template + CircVal(const CircVal2& c2) + { + double val2= Pdist(CircVal2::GetZ(), c2); + val= Wrap(val2*Type::R / CircVal2::GetR() + Type::Z); + } + + // --------------------------------------------- + operator double() const + { + return val; + } + + // --------------------------------------------- + // assignment from a floating-point value + // should only be called when the floating-point is a value in the range! + // to translate a floating-point such that 0 is mapped to Type::Z, call ToC() + CircVal& operator= (double r) + { + val= Wrap(r); + return *this; + } + + // assignment from another type of circular value + template + CircVal& operator= (const CircVal2& c2) + { + double val2= c2.Pdist(c2.GetZ(), c2); + val= Wrap(val2*Type::R/c2.GetR() + Type::Z); + return *this; + } + + // --------------------------------------------- + // convert circular-value c to real-value [L-Z,H-Z). .Z is converted to 0 + friend double ToR(const CircVal& c) { return c.val - Type::Z; } + + // --------------------------------------------- + const CircVal operator+ ( ) const { return val; } + const CircVal operator- ( ) const { return Wrap(Type::Z-Sdist(Type::Z,val)); } // return negative circular value + const CircVal operator~ ( ) const { return Wrap(val+Type::R_2 ); } // return opposite circular-value + + const CircVal operator+ (const CircVal& c) const { return Wrap(val+c.val - Type::Z); } + const CircVal operator- (const CircVal& c) const { return Wrap(val-c.val + Type::Z); } + const CircVal operator* (const double& r) const { return Wrap((val-Type::Z)*r + Type::Z); } + const CircVal operator/ (const double& r) const { return Wrap((val-Type::Z)/r + Type::Z); } + + CircVal& operator+=(const CircVal& c) { val= Wrap(val+c.val - Type::Z); return *this; } + CircVal& operator-=(const CircVal& c) { val= Wrap(val-c.val + Type::Z); return *this; } + CircVal& operator*=(const double& r) { val= Wrap((val-Type::Z)*r + Type::Z); return *this; } + CircVal& operator/=(const double& r) { val= Wrap((val-Type::Z)/r + Type::Z); return *this; } + + CircVal& operator =(const CircVal& c) { val= c.val ; return *this; } + + bool operator==(const CircVal& c) const { return val == c.val; } + bool operator!=(const CircVal& c) const { return val != c.val; } + + // note that two circular values can be compared in several different ways. + // check carefully if this is really what you need! + bool operator> (const CircVal& c) const { return val > c.val; } + bool operator>=(const CircVal& c) const { return val >= c.val; } + bool operator< (const CircVal& c) const { return val < c.val; } + bool operator<=(const CircVal& c) const { return val <= c.val; } +}; + +// ========================================================================== +template static double sin (const CircVal& c) { return std::sin(ToR(CircVal(c))); } +template static double cos (const CircVal& c) { return std::cos(ToR(CircVal(c))); } +template static double tan (const CircVal& c) { return std::tan(ToR(CircVal(c))); } +template static CircVal asin (double r ) { return CircVal(std::asin (r )); } // calls copy ctor CircVal(CircVal) +template static CircVal acos (double r ) { return CircVal(std::acos (r )); } // calls copy ctor CircVal(CircVal) +template static CircVal atan (double r ) { return CircVal(std::atan (r )); } // calls copy ctor CircVal(CircVal) +template static CircVal atan2(double r1, double r2 ) { return CircVal(std::atan2(r1,r2)); } // calls copy ctor CircVal(CircVal) +template static CircVal ToC (double r ) { return CircVal::Wrap(r + Type::Z); } // convert real-value r to circular-value in the range. 0 is converted to Type.Z + +// ========================================================================== +// tester for CircVal class +template +class CircValTester +{ + // check if 2 circular-values are almost equal + static bool IsCircAlmostEq(const CircVal& _f, const CircVal& _g) + { + double f= _f; + double g= _g; + + if (::IsAlmostEq(f, g)) + return true; + + if (f < g) + return IsAlmostEq(f, g - Type::R); + else + return IsAlmostEq(f, g + Type::R); + } + + // assert that 2 circular-values are almost equal + static void AssertCircAlmostEq(const CircVal& f, const CircVal& g) + { + assert(IsCircAlmostEq(f, g)); + } + + static void Test() + { + CircVal ZeroVal= Type::Z; + + // -------------------------------------------------------- + AssertCircAlmostEq(ZeroVal , -ZeroVal); + + AssertAlmostEq (sin(ZeroVal) , 0. ); + AssertAlmostEq (cos(ZeroVal) , 1. ); + AssertAlmostEq (tan(ZeroVal) , 0. ); + + AssertCircAlmostEq(asin(0.), ZeroVal ); + AssertCircAlmostEq(acos(1.), ZeroVal ); + AssertCircAlmostEq(atan(0.), ZeroVal ); + + AssertCircAlmostEq(ToC(0) , ZeroVal ); + AssertAlmostEq (ToR(ZeroVal) , 0. ); + + // -------------------------------------------------------- + std::default_random_engine rand_engine ; + std::uniform_real_distribution c_uni_dist(Type::L, Type::H); + std::uniform_real_distribution r_uni_dist(0. , 1000. ); // for multiplication,division by real-value + std::uniform_real_distribution t_uni_dist(-1. , 1. ); // for inverse-trigonometric functions + + std::random_device rnd_device; + rand_engine.seed(rnd_device()); // reseed engine + + for (unsigned i= 10000; i--;) + { + CircVal c1(c_uni_dist(rand_engine)); // random circular value + CircVal c2(c_uni_dist(rand_engine)); // random circular value + CircVal c3(c_uni_dist(rand_engine)); // random circular value + double r (r_uni_dist(rand_engine)); // random real value [ 0, 1000) - for testing *,/ operators + double a1(t_uni_dist(rand_engine)); // random real value [ -1, 1) - for testing asin,acos + double a2(t_uni_dist(rand_engine)); // random real value [-1000, 1000) - for testing atan + + assert (c1 == CircVal((double)c1) ); + + AssertCircAlmostEq(+c1 , c1 ); // +c = c + AssertCircAlmostEq(-(-c1) , c1 ); // -(-c) = c + AssertCircAlmostEq(c1 + c2 , c2 + c1 ); // c1+c2 = c2+c1 + AssertCircAlmostEq(c1 + (c2 +c3) , (c1 + c2) + c3 ); // c1+(c2+c3) = (c1+c2)+c3 + AssertCircAlmostEq(c1 + -c1 , ZeroVal ); // c+(-c) = z + AssertCircAlmostEq(c1 + ZeroVal , c1 ); // c+z = c + + AssertCircAlmostEq(c1 - c1 , ZeroVal ); // c-c = z + AssertCircAlmostEq(c1 - ZeroVal , c1 ); // c-z = c + AssertCircAlmostEq(ZeroVal - c1 , -c1 ); // z-c = -c + AssertCircAlmostEq(c1 - c2 , -(c2 - c1) ); // c1-c2 = -(c2-c1) + + AssertCircAlmostEq(c1 * 0. , ZeroVal ); // c*0 = 0 + AssertCircAlmostEq(c1 * 1. , c1 ); // c*1 = c + AssertCircAlmostEq(c1 / 1. , c1 ); // c/1 = c + + AssertCircAlmostEq((c1 * (1./(r+1.))) / (1./(r+1.)) , c1 ); // (c*r)/r = c, 0=1 + + // -------------------------------------------------------- + AssertCircAlmostEq(~(~c1) , c1 ); // opposite(opposite(c) = c + AssertCircAlmostEq(c1 - (~c1) , ToC(Type::R/2.) ); // c - ~c = r/2+z + + // -------------------------------------------------------- + AssertAlmostEq (sin(ToR(CircVal(c1))), sin(c1) ); // member func sin + AssertAlmostEq (cos(ToR(CircVal(c1))), cos(c1) ); // member func cos + AssertAlmostEq (tan(ToR(CircVal(c1))), tan(c1) ); // member func tan + + AssertAlmostEq (sin(-c1) , -sin(c1) ); // sin(-c) = -sin(c) + AssertAlmostEq (cos(-c1) , cos(c1) ); // cos(-c) = cos(c) + AssertAlmostEq (tan(-c1) , -tan(c1) ); // tan(-c1) = -tan(c) the error may be large + + AssertAlmostEq (sin(c1+ToC(Type::R/4.)) , cos(c1) ); // sin(c+r/4) = cos(c) + AssertAlmostEq (cos(c1+ToC(Type::R/4.)) , -sin(c1) ); // cos(c+r/4) = -sin(c) + AssertAlmostEq (sin(c1+ToC(Type::R/2.)) , -sin(c1) ); // sin(c+r/2) = -sin(c) + AssertAlmostEq (cos(c1+ToC(Type::R/2.)) , -cos(c1) ); // cos(c+r/2) = -cos(c) + + AssertAlmostEq (Sqr(sin(c1))+Sqr(cos(c1)) , 1. ); // sin(x)^2+cos(x)^2 = 1 + + AssertAlmostEq (sin(c1)/cos(c1) , tan(c1) ); // sin(x)/cos(x) = tan(x) + + // -------------------------------------------------------- + AssertCircAlmostEq(asin(a1) , CircVal(asin(a1))); // member func asin + AssertCircAlmostEq(acos(a1) , CircVal(acos(a1))); // member func acos + AssertCircAlmostEq(atan(a2) , CircVal(atan(a2))); // member func atan + + AssertCircAlmostEq(asin(a1) + asin(-a1) , ZeroVal ); // asin(r)+asin(-r) = z + AssertCircAlmostEq(acos(a1) + acos(-a1) , ToC(Type::R/2.) ); // acos(r)+acos(-r) = r/2+z + AssertCircAlmostEq(asin(a1) + acos( a1) , ToC(Type::R/4.) ); // asin(r)+acos( r) = r/4+z + AssertCircAlmostEq(atan(a2) + atan(-a2) , ZeroVal ); // atan(r)+atan(-r) = z + + // -------------------------------------------------------- + assert (c1 > c2 == (c2 < c1) ); // c1> c2 <==> c2< c1 + assert (c1 >= c2 == (c2 <= c1) ); // c1>=c2 <==> c2<=c1 + assert (c1 >= c2 == ( (c1 > c2) || (c1 == c2)) ); // c1>=c2 <==> (c1> c2)|| (c1==c2) + assert (c1 <= c2 == ( (c1 < c2) || (c1 == c2)) ); // c1<=c2 <==> (c1< c2)|| (c1==c2) + assert (c1 > c2 == (!(c1 == c2) && !(c1 < c2)) ); // c1> c2 <==> !(c1==c2)&&!(c1< c2) + assert (c1 == c2 == (!(c1 > c2) && !(c1 < c2)) ); // c1= c2 <==> !(c1> c2)&&!(c1< c2) + assert (c1 < c2 == (!(c1 == c2) && !(c1 > c2)) ); // c1< c2 <==> !(c1==c2)&&!(c1> c2) + assert (!(c1>c2) || !(c2>c3) || (c1>c3) ); // (c1>c2)&&(c2>c3) ==> c1>c3 + + // -------------------------------------------------------- + AssertCircAlmostEq(c1 , ToC(ToR( c1) ) ); // c1 = ToC(ToR( c1) + AssertCircAlmostEq(-c1 , ToC(ToR(-c1) ) ); // -c1 = ToC(ToR(-c1) + AssertCircAlmostEq(c1 + c2 , ToC(ToR(c1)+ToR(c2)) ); // c1+c2 = ToC(ToR(c1)+ToR(c2)) + AssertCircAlmostEq(c1 - c2 , ToC(ToR(c1)-ToR(c2)) ); // c1-c2 = ToC(ToR(c1)-ToR(c2)) + AssertCircAlmostEq(c1 * r , ToC(ToR(c1)*r ) ); // c1*r = ToC(ToR(c1)*r ) + AssertCircAlmostEq(c1 / r , ToC(ToR(c1)/r ) ); // c1/r = ToC(ToR(c1)/r ) + + // -------------------------------------------------------- + } + } + +public: + CircValTester() + { + Test(); + } +}; + diff --git a/thirdparty/Circular_Code/FPCompare.h b/thirdparty/Circular_Code/FPCompare.h new file mode 100644 index 000000000..432aee8a8 --- /dev/null +++ b/thirdparty/Circular_Code/FPCompare.h @@ -0,0 +1,313 @@ +// ========================================================================== +// floating-point AlmostEquals checker +// Lior Kogan (koganlior1@gmail.com), 2011 +// based on code extracted from Google Test (http://code.google.com/p/googletest/) +// +// the following code can be used to compare two floating-point values. +// example: +// double f= // something +// double g= // something +// const FloatingPoint lhs(f), rhs(g); +// if (lhs.AlmostEquals(rhs)) { ... } +// ========================================================================== + +#pragma once + +#include +#include + +#define GTEST_OS_WINDOWS 1 + +// ========================================================================== +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) + +// ========================================================================== +// from gtest-1.5.0\include\gtest\internal\gtest-port.h: + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 bytes). +// +// Such functionality should belong to STL, but I cannot find it there. +// +// Google Test uses this class in the implementation of floating-point comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need arises. +template +class TypeWithSize +{ +public: + // This prevents the user from using TypeWithSize with incorrect values of N. + typedef void UInt; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> +{ +public: + // unsigned int has size 4 in both gcc and MSVC. + // As base/basictypes.h doesn't compile on Windows, we cannot use uint32, uint64, and etc here. + typedef int Int ; + typedef unsigned int UInt; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> +{ +public: + typedef int64_t Int; + typedef uint64_t UInt; +}; + +// ========================================================================== +// from gtest-1.5.0.zip\gtest-1.5.0\include\gtest\internal\gtest-internal.h: + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template +class FloatingPoint +{ +public: + // Defines the unsigned integer type that has the same size as the floating point number + typedef typename TypeWithSize::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8*sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = + ~static_cast(0) >> (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. + // + // See the following article for more details on ULP: + // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm. + static const size_t kMaxUlps= 5000000; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) + { + FloatingPoint fp(0); + fp.u_.bits_ = bits; + return fp.u_.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() + { + return ReinterpretBits(kExponentBitMask); + } + + // Non-static methods + + // Returns the bits that represents this number. + const Bits &bits() const { return u_.bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & u_.bits_; } + + // Returns true iff this is NAN (not a number). + bool is_nan() const + { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true iff this number is at most kMaxUlps ULP's away from + // rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const + { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) + return false; + + // Lior Kogan, 25/9/2010: e.g. for comparing 1e-13 with exact 0 + if (fabs(u_.value_ - rhs.u_.value_) < 1e-12) + return true; + + Bits bits= DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_); + + //if (bits > kMaxUlps && bits<100000000) + // __debugbreak(); + + return bits <= kMaxUlps; + } + + private: + // The data type used to store the actual floating-point number. + union FloatingPointUnion + { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; + + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits &sam) + { + if (kSignBitMask & sam) // sam represents a negative number. + return ~sam + 1; + else // sam represents a positive number. + return kSignBitMask | sam; + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, + const Bits &sam2) + { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + Bits bits= (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + return bits; + } + + FloatingPointUnion u_; +}; + +// ========================================================================== +// Lior Kogan, 25/9/2010 +// ========================================================================== + +// check if two floating-points are almost equal +template +static bool IsAlmostEq(T x, T y) +{ + static_assert(!std::numeric_limits::is_exact , "IsAlmostEq: floating-point type expected"); + + FloatingPoint f(x); + FloatingPoint g(y); + + return f.AlmostEquals(g); +} + +// assert that 2 floating-points are almost equal +static void AssertAlmostEq(const double f, const double g) +{ + assert(IsAlmostEq(f, g)); +} diff --git a/thirdparty/Circular_Code/TruncNormalDist.h b/thirdparty/Circular_Code/TruncNormalDist.h new file mode 100644 index 000000000..8da24ec65 --- /dev/null +++ b/thirdparty/Circular_Code/TruncNormalDist.h @@ -0,0 +1,300 @@ +// ========================================================================== +// truncated normal distribution +// Lior Kogan (koganlior1@gmail.com), 2012 +// based on VC 2012 std::normal_distribution (random) as a skeleton +// and on C. H. Jackson's R's implementation of the following paper: +// Robert, C. P. Simulation of truncated normal variables. Statistics and Computing (1995) 5, 121-125 +// ========================================================================== + +#pragma once + +// ========================================================================== +// TEMPLATE CLASS truncated_normal_distribution +template +class truncated_normal_distribution +{ // template class for truncated normal distribution +public: + typedef truncated_normal_distribution<_Ty> _Myt; + typedef _Ty input_type ; + typedef _Ty result_type; + + struct param_type + { // parameter package + typedef _Myt distribution_type; + + param_type(_Ty _Mean0= 0., _Ty _Sigma0= 1., _Ty _A0= 0., _Ty _B0= 0.) + { // construct from parameters + _Init(_Mean0, _Sigma0, _A0, _B0); + } + + bool operator==(const param_type& _Right) const + { // test for equality + return _Mean == _Right._Mean && + _Sigma == _Right._Sigma && + _A == _Right._A && + _B == _Right._B ; + } + + bool operator!=(const param_type& _Right) const + { // test for inequality + return !(*this == _Right); + } + + _Ty mean() const + { // return mean value + return _Mean; + } + + _Ty sigma() const + { // return sigma value + return _Sigma; + } + + _Ty a() const + { // return truncation-range lower-bound + return _A; + } + + _Ty b() const + { // return truncation-range upper-bound + return _B; + } + + _Ty stddev() const + { // return sigma value + return _Sigma; + } + + int alg() const + { // return fastest algorithm for the given parameters + return _Alg; + } + + void _Init(_Ty _Mean0, _Ty _Sigma0, _Ty _A0, _Ty _B0) + { // set internal state + _RNG_ASSERT(0. < _Sigma0, "invalid sigma argument for truncated_normal_distribution"); + _RNG_ASSERT(_A0 < _B0 , "invalid truncation-range for truncated_normal_distribution"); + _Mean = _Mean0 ; + _Sigma= _Sigma0; + _A = _A0 ; + _B = _B0 ; + + _NA= (_A - _Mean) / _Sigma; + _NB= (_B - _Mean) / _Sigma; + + // decide on the fastest algorithm for our case + _Alg= 3; + if ((_NA < 0 ) && ( _NB > 0) && (_NB - _NA > sqrt(_2Pi))) _Alg= 0; + else if ((_NA >= 0) && ( _NB > _NA + 2.*sqrt(exp(1.)) / ( _NA + sqrt(Sqr(_NA) + 4.)) * exp((_NA*2. - _NA*sqrt(Sqr(_NA) + 4.))/4.))) _Alg= 1; + else if ((_NB <= 0) && (-_NA > -_NB + 2.*sqrt(exp(1.)) / (-_NB + sqrt(Sqr(_NB) + 4.)) * exp((_NB*2. - -_NB*sqrt(Sqr(_NB) + 4.))/4.))) _Alg= 2; + } + + _Ty _Mean ; + _Ty _Sigma; + _Ty _A ; + _Ty _B ; + + _Ty _NA ; // _A normalized + _Ty _NB ; // _B normalized + int _Alg ; // algorithm to use + }; + + explicit truncated_normal_distribution(_Ty _Mean0 = 0. , + _Ty _Sigma0= 1. , + _Ty _A0 = std::numeric_limits< _Ty>::min(), // truncation-range lower-bound + _Ty _B0 = std::numeric_limits< _Ty>::max() ) // truncation-range upper-bound + + : _Par(_Mean0, _Sigma0, _A0, _B0), _Valid(false), _X2(0) + { // construct + } + + explicit truncated_normal_distribution(param_type _Par0) + : _Par(_Par0), _Valid(false), _X2(0) + { // construct from parameter package + } + + _Ty mean() const + { // return mean value + return _Par.mean(); + } + + _Ty sigma() const + { // return sigma value + return _Par.sigma(); + } + + _Ty a() const + { // return truncation-range lower-bound + return _Par.a(); + } + + _Ty b() const + { // return truncation-range upper-bound + return _Par.b(); + } + + _Ty stddev() const + { // return sigma value + return _Par.sigma(); + } + + param_type param() const + { // return parameter package + return _Par; + } + + void param(const param_type& _Par0) + { // set parameter package + _Par= _Par0; + reset(); + } + + result_type (min)() const + { // get smallest possible result + return _Par._A; + } + + result_type (max)() const + { // get largest possible result + return _Par._B; + } + + void reset() + { // clear internal state + _Valid= false; + } + + template + result_type operator()(_Engine& _Eng) + { // return next value + return _Eval(_Eng, _Par); + } + + template + result_type operator()(_Engine& _Eng, const param_type& _Par0) + { // return next value, given parameter package + reset(); + return _Eval(_Eng, _Par0, false); + } + + template + basic_istream<_Elem, _Traits>& _Read(basic_istream<_Elem, _Traits>& _Istr) + { // read state from _Istr + _Ty _Mean0 ; + _Ty _Sigma0; + _Ty _A0 ; + _Ty _B0 ; + _In(_Istr, _Mean0 ); + _In(_Istr, _Sigma0); + _In(_Istr, _A0 ); + _In(_Istr, _B0 ); + _Par._Init(_Mean0, _Sigma0, _A0, _B0); + + _Istr >> _Valid; + _In(_Istr, _X2); + return _Istr; + } + + template + basic_ostream<_Elem, _Traits>& _Write(basic_ostream<_Elem, _Traits>& _Ostr) const + { // write state to _Ostr + _Out(_Ostr, _Par._Mean ); + _Out(_Ostr, _Par._Sigma); + _Out(_Ostr, _Par._A ); + _Out(_Ostr, _Par._B ); + + _Ostr << ' ' << _Valid; + _Out(_Ostr, _X2); + return _Ostr; + } + +private: + template + result_type _Eval(_Engine& _Eng, const param_type& _Par0, bool _Keep= true) + { + _Ty r; + + switch (_Par0._Alg) + { + case 0 : + { + normal_distribution<_Ty> nd; + do { r= nd(_Eng); } + while (r<_Par0._NA || r>_Par0._NB); + break; + } + + case 1 : + { + exponential_distribution<_Ty> ed; + _Ty a,u,z; + + do + { + a= (_Par0._NA + sqrt(Sqr(_Par0._NA)+4.))/2.; + z= ed(_Eng, a) + _Par0._NA; + u= _NRAND(_Eng, _Ty); + } + while ((u>exp(-Sqr(z-a)/2.)) || (z>_Par0._NB)); + + r= z; + break; + } + + case 2 : + { + exponential_distribution<_Ty> ed; + _Ty a,u,z; + + do + { + a= (-_Par0._NB + sqrt(Sqr(_Par0._NB)+4.))/2.; + z= ed(_Eng, a) - _Par0._NB; + u= _NRAND(_Eng, _Ty); + } + while ((u>exp(-Sqr(z-a)/2.)) || (z>-_Par0._NA)); + + r= -z; + break; + } + + default: + { + _Ty z,u,rho; + + do + { + uniform_real<_Ty> ud(_Par0._NA, _Par0._NB); + z= ud(_Eng); + u= _NRAND(_Eng, _Ty); + + if (_Par0._NA>0) rho= exp((Sqr(_Par0._NA)-Sqr(z))/2.); + else if (_Par0._NB<0) rho= exp((Sqr(_Par0._NB)-Sqr(z))/2.); + else rho= exp( -Sqr(z) /2.); + } + while (u>rho); + + r= z; + } + } + + return r * _Par0._Sigma + _Par0._Mean; // denormalize result + } + + int _Alg ; // which algorithm to use + param_type _Par ; + bool _Valid; + _Ty _X2 ; +}; + +template +basic_istream<_Elem, _Traits>& operator>>(basic_istream<_Elem, _Traits>& _Istr, truncated_normal_distribution<_Ty>& _Dist) +{ // read state from _Istr + return _Dist._Read(_Istr); +} + +template +basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, const truncated_normal_distribution<_Ty>& _Dist) +{ // write state to _Ostr + return _Dist._Write(_Ostr); +} diff --git a/thirdparty/Circular_Code/WrappedNormalDist.h b/thirdparty/Circular_Code/WrappedNormalDist.h new file mode 100644 index 000000000..b141baf66 --- /dev/null +++ b/thirdparty/Circular_Code/WrappedNormalDist.h @@ -0,0 +1,245 @@ +// ========================================================================== +// wrapped normal distribution +// Lior Kogan (koganlior1@gmail.com), 2012 +// based on VC 2012 std::normal_distribution (random) as a skeleton +// ========================================================================== + +#pragma once + +#include "CircHelper.h" // Mod + +// ========================================================================== +// TEMPLATE CLASS wrapped_normal_distribution +template +class wrapped_normal_distribution +{ // template class for wrapped normal distribution +public: + typedef wrapped_normal_distribution<_Ty> _Myt; + typedef _Ty input_type ; + typedef _Ty result_type; + + struct param_type + { // parameter package + typedef _Myt distribution_type; + + param_type(_Ty _Mean0= 0., _Ty _Sigma0= 1., _Ty _L0= 0., _Ty _H0= 0.) + { // construct from parameters + _Init(_Mean0, _Sigma0, _L0, _H0); + } + + bool operator==(const param_type& _Right) const + { // test for equality + return _Mean == _Right._Mean && + _Sigma == _Right._Sigma && + _L == _Right._L && + _H == _Right._H ; + + } + + bool operator!=(const param_type& _Right) const + { // test for inequality + return !(*this == _Right); + } + + _Ty mean() const + { // return mean value + return _Mean; + } + + _Ty sigma() const + { // return sigma value + return _Sigma; + } + + _Ty l() const + { // return wrapping-range lower-bound + return _L; + } + + _Ty h() const + { // return wrapping-range upper-bound + return _H; + } + + _Ty stddev() const + { // return sigma value + return _Sigma; + } + + void _Init(_Ty _Mean0, _Ty _Sigma0, _Ty _L0, _Ty _H0) + { // set internal state + _RNG_ASSERT(0. < _Sigma0, "invalid sigma argument for wrapped_normal_distribution"); + _RNG_ASSERT(_L0 < _H0 , "invalid wrapping-range for wrapped_normal_distribution"); + _Mean = _Mean0 ; + _Sigma= _Sigma0; + _L = _L0 ; + _H = _H0 ; + } + + _Ty _Mean ; + _Ty _Sigma; + _Ty _L ; + _Ty _H ; + }; + + explicit wrapped_normal_distribution(_Ty _Mean0 = 0., + _Ty _Sigma0= 45., + _Ty _L0 = -180., // wrapping-range lower-bound + _Ty _H0 = 180. ) // wrapping-range upper-bound + : _Par(_Mean0, _Sigma0, _L0, _H0), _Valid(false), _X2(0) + { // construct + } + + explicit wrapped_normal_distribution(param_type _Par0) + : _Par(_Par0), _Valid(false), _X2(0) + { // construct from parameter package + } + + _Ty mean() const + { // return mean value + return _Par.mean(); + } + + _Ty sigma() const + { // return sigma value + return _Par.sigma(); + } + + _Ty l() const + { // return wrapping-range lower-bound + return _Par.l(); + } + + _Ty h() const + { // return wrapping-range upper-bound + return _Par.h(); + } + + _Ty stddev() const + { // return sigma value + return _Par.sigma(); + } + + param_type param() const + { // return parameter package + return _Par; + } + + void param(const param_type& _Par0) + { // set parameter package + _Par= _Par0; + reset(); + } + + result_type (min)() const + { // get smallest possible result + return _Par._L; + } + + result_type (max)() const + { // get largest possible result + return _Par._H; + } + + void reset() + { // clear internal state + _Valid= false; + } + + template + result_type operator()(_Engine& _Eng) + { // return next value + return _Eval(_Eng, _Par); + } + + template + result_type operator()(_Engine& _Eng, const param_type& _Par0) + { // return next value, given parameter package + reset(); + return _Eval(_Eng, _Par0, false); + } + + template + basic_istream<_Elem, _Traits>& _Read(basic_istream<_Elem, _Traits>& _Istr) + { // read state from _Istr + _Ty _Mean0 ; + _Ty _Sigma0; + _Ty _L0 ; + _Ty _H0 ; + _In(_Istr, _Mean0 ); + _In(_Istr, _Sigma0); + _In(_Istr, _L0 ); + _In(_Istr, _H0 ); + _Par._Init(_Mean0, _Sigma0, _L0, _H0); + + _Istr >> _Valid; + _In(_Istr, _X2); + return _Istr; + } + + template + basic_ostream<_Elem, _Traits>& _Write(basic_ostream<_Elem, _Traits>& _Ostr) const + { // write state to _Ostr + _Out(_Ostr, _Par._Mean ); + _Out(_Ostr, _Par._Sigma); + _Out(_Ostr, _Par._L ); + _Out(_Ostr, _Par._H ); + + _Ostr << ' ' << _Valid; + _Out(_Ostr, _X2); + return _Ostr; + } + +private: + template result_type _Eval(_Engine& _Eng, const param_type& _Par0, bool _Keep= true) + { // compute next value + // Knuth, vol. 2, p. 122, alg. P + _Ty r; + + if (_Keep && _Valid) + { // return stored value + r = _X2 ; + _Valid= false; + } + else + { // generate two values, store one, return one + double _V1, _V2, _Sx; + for (; ; ) + { // reject bad values + _V1= 2 * _NRAND(_Eng, _Ty) - 1.; + _V2= 2 * _NRAND(_Eng, _Ty) - 1.; + _Sx= _V1 * _V1 + _V2 * _V2; + if (_Sx < 1.) + break; + } + + double _Fx= _CSTD sqrt(-2. * _CSTD log(_Sx) / _Sx); + if (_Keep) + { // save second value for next call + _X2 = _Fx * _V2; + _Valid= true ; + } + + r= _Fx * _V1; + } + + result_type d= r * _Par0._Sigma + _Par0._Mean; // denormalize result + return Mod(d - _Par0._L, _Par0._H - _Par0._L) + _Par0._L; // wrap result + } + + param_type _Par ; + bool _Valid; + _Ty _X2 ; +}; + +template +basic_istream<_Elem, _Traits>& operator>>(basic_istream<_Elem, _Traits>& _Istr, wrapped_normal_distribution<_Ty>& _Dist) +{ // read state from _Istr + return _Dist._Read(_Istr); +} + +template +basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, const wrapped_normal_distribution<_Ty>& _Dist) +{ // write state to _Ostr + return _Dist._Write(_Ostr); +} diff --git a/thirdparty/Circular_Code/WrappedTruncNormalDist.h b/thirdparty/Circular_Code/WrappedTruncNormalDist.h new file mode 100644 index 000000000..f11e728ef --- /dev/null +++ b/thirdparty/Circular_Code/WrappedTruncNormalDist.h @@ -0,0 +1,339 @@ +// ========================================================================== +// wrapped truncated normal distribution +// Lior Kogan (koganlior1@gmail.com), 2012 +// based on VC 2012 std::normal_distribution (random) as a skeleton +// and on C. H. Jackson's R's implementation of the following paper: +// Robert, C. P. Simulation of truncated normal variables. Statistics and Computing (1995) 5, 121-125 +// ========================================================================== + +#pragma once + +#include "CircHelper.h" // _2Pi, Sqr, Mod + +// ========================================================================== +// TEMPLATE CLASS wrapped_truncated_normal_distribution +template +class wrapped_truncated_normal_distribution +{ // template class for wrapped truncated normal distribution +public: + typedef wrapped_truncated_normal_distribution<_Ty> _Myt; + typedef _Ty input_type ; + typedef _Ty result_type; + + struct param_type + { // parameter package + typedef _Myt distribution_type; + + param_type(_Ty _Mean0= 0., _Ty _Sigma0= 1., _Ty _A0= 0., _Ty _B0= 0., _Ty _L0= 0., _Ty _H0= 0.) + { // construct from parameters + _Init(_Mean0, _Sigma0, _A0, _B0, _L0, _H0); + } + + bool operator==(const param_type& _Right) const + { // test for equality + return _Mean == _Right._Mean && + _Sigma == _Right._Sigma && + _A == _Right._A && + _B == _Right._B && + _L == _Right._L && + _H == _Right._H ; + } + + bool operator!=(const param_type& _Right) const + { // test for inequality + return !(*this == _Right); + } + + _Ty mean() const + { // return mean value + return _Mean; + } + + _Ty sigma() const + { // return sigma value + return _Sigma; + } + + _Ty a() const + { // return truncation-range lower-bound + return _A; + } + + _Ty b() const + { // return truncation-range upper-bound + return _B; + } + + _Ty l() const + { // return wrapping-range lower-bound + return _L; + } + + _Ty h() const + { // return wrapping-range upper-bound + return _H; + } + + _Ty stddev() const + { // return sigma value + return _Sigma; + } + + int alg() const + { // return fastest algorithm for the given parameters + return _Alg; + } + + void _Init(_Ty _Mean0, _Ty _Sigma0, _Ty _A0, _Ty _B0, _Ty _L0, _Ty _H0) + { // set internal state + _RNG_ASSERT(0. < _Sigma0, "invalid sigma argument for wrapped_truncated_normal_distribution" ); + _RNG_ASSERT(_A0 < _B0 , "invalid truncation-range for wrapped_truncated_normal_distribution"); + _RNG_ASSERT(_L0 < _H0 , "invalid wrapping-range for wrapped_truncated_normal_distribution" ); + _Mean = _Mean0 ; + _Sigma= _Sigma0; + _A = _A0 ; + _B = _B0 ; + _L = _L0 ; + _H = _H0 ; + + _NA= (_A - _Mean) / _Sigma; + _NB= (_B - _Mean) / _Sigma; + + // decide on the fastest algorithm for our case + _Alg= 3; + if ((_NA < 0 ) && ( _NB > 0) && (_NB - _NA > sqrt(_2Pi))) _Alg= 0; + else if ((_NA >= 0) && ( _NB > _NA + 2.*sqrt(exp(1.)) / ( _NA + sqrt(Sqr(_NA) + 4.)) * exp((_NA*2. - _NA*sqrt(Sqr(_NA) + 4.))/4.))) _Alg= 1; + else if ((_NB <= 0) && (-_NA > -_NB + 2.*sqrt(exp(1.)) / (-_NB + sqrt(Sqr(_NB) + 4.)) * exp((_NB*2. - -_NB*sqrt(Sqr(_NB) + 4.))/4.))) _Alg= 2; + } + + _Ty _Mean ; + _Ty _Sigma; + _Ty _A ; + _Ty _B ; + _Ty _L ; + _Ty _H ; + + _Ty _NA ; // _A normalized + _Ty _NB ; // _B normalized + int _Alg ; // algorithm to use + }; + + // normal distribution is first truncated, and then wrapped + explicit wrapped_truncated_normal_distribution(_Ty _Mean0 = 0. , + _Ty _Sigma0= 1. , + _Ty _A0 = std::numeric_limits< _Ty>::min(), // truncation-range lower-bound + _Ty _B0 = std::numeric_limits< _Ty>::max(), // truncation-range upper-bound + _Ty _L0 = -180. , // wrapping -range lower-bound + _Ty _H0 = 180. ) // wrapping -range upper-bound + + : _Par(_Mean0, _Sigma0, _A0, _B0, _L0, _H0), _Valid(false), _X2(0) + { // construct + } + + explicit wrapped_truncated_normal_distribution(param_type _Par0) + : _Par(_Par0), _Valid(false), _X2(0) + { // construct from parameter package + } + + _Ty mean() const + { // return mean value + return _Par.mean(); + } + + _Ty sigma() const + { // return sigma value + return _Par.sigma(); + } + + _Ty a() const + { // return truncation-range lower-bound + return _Par.a(); + } + + _Ty b() const + { // return truncation-range upper-bound + return _Par.b(); + } + + _Ty l() const + { // return wrapping-range lower-bound + return _Par.l(); + } + + _Ty h() const + { // return wrapping-range upper-bound + return _Par.h(); + } + + _Ty stddev() const + { // return sigma value + return _Par.sigma(); + } + + param_type param() const + { // return parameter package + return _Par; + } + + void param(const param_type& _Par0) + { // set parameter package + _Par= _Par0; + reset(); + } + + result_type (min)() const + { // get smallest possible result + return _Par._A; + } + + result_type (max)() const + { // get largest possible result + return _Par._B; + } + + void reset() + { // clear internal state + _Valid= false; + } + + template + result_type operator()(_Engine& _Eng) + { // return next value + return _Eval(_Eng, _Par); + } + + template + result_type operator()(_Engine& _Eng, const param_type& _Par0) + { // return next value, given parameter package + reset(); + return _Eval(_Eng, _Par0, false); + } + + template + basic_istream<_Elem, _Traits>& _Read(basic_istream<_Elem, _Traits>& _Istr) + { // read state from _Istr + _Ty _Mean0 ; + _Ty _Sigma0; + _Ty _A0 ; + _Ty _B0 ; + _Ty _L0 ; + _Ty _H0 ; + _In(_Istr, _Mean0 ); + _In(_Istr, _Sigma0); + _In(_Istr, _A0 ); + _In(_Istr, _B0 ); + _In(_Istr, _L0 ); + _In(_Istr, _H0 ); + _Par._Init(_Mean0, _Sigma0, _A0, _B0, _L0, _H0); + + _Istr >> _Valid; + _In(_Istr, _X2); + return _Istr; + } + + template + basic_ostream<_Elem, _Traits>& _Write(basic_ostream<_Elem, _Traits>& _Ostr) const + { // write state to _Ostr + _Out(_Ostr, _Par._Mean ); + _Out(_Ostr, _Par._Sigma); + _Out(_Ostr, _Par._A ); + _Out(_Ostr, _Par._B ); + _Out(_Ostr, _Par._L ); + _Out(_Ostr, _Par._H ); + + _Ostr << ' ' << _Valid; + _Out(_Ostr, _X2); + return _Ostr; + } + +private: + template + result_type _Eval(_Engine& _Eng, const param_type& _Par0, bool _Keep= true) + { + _Ty r; + + switch (_Par0._Alg) + { + case 0 : + { + normal_distribution<_Ty> nd; + do { r= nd(_Eng); } + while (r<_Par0._NA || r>_Par0._NB); + break; + } + + case 1 : + { + exponential_distribution<_Ty> ed; + _Ty a,u,z; + + do + { + a= (_Par0._NA + sqrt(Sqr(_Par0._NA)+4.))/2.; + z= ed(_Eng, a) + _Par0._NA; + u= _NRAND(_Eng, _Ty); + } + while ((u>exp(-Sqr(z-a)/2.)) || (z>_Par0._NB)); + + r= z; + break; + } + + case 2 : + { + exponential_distribution<_Ty> ed; + _Ty a,u,z; + + do + { + a= (-_Par0._NB + sqrt(Sqr(_Par0._NB)+4.))/2.; + z= ed(_Eng, a) - _Par0._NB; + u= _NRAND(_Eng, _Ty); + } + while ((u>exp(-Sqr(z-a)/2.)) || (z>-_Par0._NA)); + + r= -z; + break; + } + + default: + { + _Ty z,u,rho; + + do + { + uniform_real<_Ty> ud(_Par0._NA, _Par0._NB); + z= ud(_Eng); + u= _NRAND(_Eng, _Ty); + + if (_Par0._NA>0) rho= exp((Sqr(_Par0._NA)-Sqr(z))/2.); + else if (_Par0._NB<0) rho= exp((Sqr(_Par0._NB)-Sqr(z))/2.); + else rho= exp( -Sqr(z) /2.); + } + while (u>rho); + + r= z; + } + } + + result_type d= r * _Par0._Sigma + _Par0._Mean; // denormalize result + return Mod(d - _Par0._L, _Par0._H - _Par0._L) + _Par0._L; // wrap result + } + + int _Alg ; // which algorithm to use + param_type _Par ; + bool _Valid; + _Ty _X2 ; +}; + +template +basic_istream<_Elem, _Traits>& operator>>(basic_istream<_Elem, _Traits>& _Istr, wrapped_truncated_normal_distribution<_Ty>& _Dist) +{ // read state from _Istr + return _Dist._Read(_Istr); +} + +template +basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, const wrapped_truncated_normal_distribution<_Ty>& _Dist) +{ // write state to _Ostr + return _Dist._Write(_Ostr); +} diff --git a/thirdparty/inih/CMakeLists.txt b/thirdparty/inih/CMakeLists.txt new file mode 100644 index 000000000..a4b78575d --- /dev/null +++ b/thirdparty/inih/CMakeLists.txt @@ -0,0 +1,20 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.9) + +PROJECT(inih) + +set(INIH_SOURCE ini.c INIReader.cpp) + +set(INIH_HEADERS ini.h INIReader.h) + +ADD_LIBRARY(inih ${INIH_SOURCE} ${INIH_HEADERS}) + +set_target_properties(inih PROPERTIES FOLDER "libs") + +install(TARGETS ${PROJECT_NAME} + EXPORT MTTrackingExports + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + PUBLIC_HEADER DESTINATION include/${PROJECT_NAME}) + +set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "libs") \ No newline at end of file diff --git a/thirdparty/inih/INIReader.cpp b/thirdparty/inih/INIReader.cpp new file mode 100644 index 000000000..e39e0d554 --- /dev/null +++ b/thirdparty/inih/INIReader.cpp @@ -0,0 +1,116 @@ +// Read an INI file into easy-to-access name/value pairs. + +// SPDX-License-Identifier: BSD-3-Clause + +// Copyright (C) 2009-2020, Ben Hoyt + +// inih and INIReader are released under the New BSD license (see LICENSE.txt). +// Go to the project home page for more info: +// +// https://github.com/benhoyt/inih + +#include +#include +#include +#include "ini.h" +#include "INIReader.h" + +using std::string; + +INIReader::INIReader(const string& filename) +{ + _error = ini_parse(filename.c_str(), ValueHandler, this); +} + +INIReader::INIReader(const char *buffer, size_t buffer_size) +{ + string content(buffer, buffer_size); + _error = ini_parse_string(content.c_str(), ValueHandler, this); +} + +int INIReader::ParseError() const +{ + return _error; +} + +string INIReader::Get(const string& section, const string& name, const string& default_value) const +{ + string key = MakeKey(section, name); + // Use _values.find() here instead of _values.at() to support pre C++11 compilers + return _values.count(key) ? _values.find(key)->second : default_value; +} + +string INIReader::GetString(const string& section, const string& name, const string& default_value) const +{ + const string str = Get(section, name, ""); + return str.empty() ? default_value : str; +} + +long INIReader::GetInteger(const string& section, const string& name, long default_value) const +{ + string valstr = Get(section, name, ""); + const char* value = valstr.c_str(); + char* end; + // This parses "1234" (decimal) and also "0x4D2" (hex) + long n = strtol(value, &end, 0); + return end > value ? n : default_value; +} + +double INIReader::GetReal(const string& section, const string& name, double default_value) const +{ + string valstr = Get(section, name, ""); + const char* value = valstr.c_str(); + char* end; + double n = strtod(value, &end); + return end > value ? n : default_value; +} + +bool INIReader::GetBoolean(const string& section, const string& name, bool default_value) const +{ + string valstr = Get(section, name, ""); + // Convert to lower case to make string comparisons case-insensitive + std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower); + if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1") + return true; + else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0") + return false; + else + return default_value; +} + +bool INIReader::HasSection(const string& section) const +{ + const string key = MakeKey(section, ""); + std::map::const_iterator pos = _values.lower_bound(key); + if (pos == _values.end()) + return false; + // Does the key at the lower_bound pos start with "section"? + return pos->first.compare(0, key.length(), key) == 0; +} + +bool INIReader::HasValue(const string& section, const string& name) const +{ + string key = MakeKey(section, name); + return _values.count(key); +} + +string INIReader::MakeKey(const string& section, const string& name) +{ + string key = section + "=" + name; + // Convert to lower case to make section/name lookups case-insensitive + std::transform(key.begin(), key.end(), key.begin(), ::tolower); + return key; +} + +int INIReader::ValueHandler(void* user, const char* section, const char* name, + const char* value) +{ + if (!name) // Happens when INI_CALL_HANDLER_ON_NEW_SECTION enabled + return 1; + INIReader* reader = static_cast(user); + string key = MakeKey(section, name); + if (reader->_values[key].size() > 0) + reader->_values[key] += "\n"; + reader->_values[key] += value ? value : ""; + return 1; +} diff --git a/thirdparty/inih/INIReader.h b/thirdparty/inih/INIReader.h new file mode 100644 index 000000000..fce7e8983 --- /dev/null +++ b/thirdparty/inih/INIReader.h @@ -0,0 +1,73 @@ +// Read an INI file into easy-to-access name/value pairs. + +// SPDX-License-Identifier: BSD-3-Clause + +// Copyright (C) 2009-2020, Ben Hoyt + +// inih and INIReader are released under the New BSD license (see LICENSE.txt). +// Go to the project home page for more info: +// +// https://github.com/benhoyt/inih + +#ifndef INIREADER_H +#define INIREADER_H + +#include +#include + +// Read an INI file into easy-to-access name/value pairs. (Note that I've gone +// for simplicity here rather than speed, but it should be pretty decent.) +class INIReader +{ +public: + // Construct INIReader and parse given filename. See ini.h for more info + // about the parsing. + explicit INIReader(const std::string& filename); + + // Construct INIReader and parse given buffer. See ini.h for more info + // about the parsing. + explicit INIReader(const char *buffer, size_t buffer_size); + + // Return the result of ini_parse(), i.e., 0 on success, line number of + // first error on parse error, or -1 on file open error. + int ParseError() const; + + // Get a string value from INI file, returning default_value if not found. + std::string Get(const std::string& section, const std::string& name, + const std::string& default_value) const; + + // Get a string value from INI file, returning default_value if not found, + // empty, or contains only whitespace. + std::string GetString(const std::string& section, const std::string& name, + const std::string& default_value) const; + + // Get an integer (long) value from INI file, returning default_value if + // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). + long GetInteger(const std::string& section, const std::string& name, long default_value) const; + + // Get a real (floating point double) value from INI file, returning + // default_value if not found or not a valid floating point value + // according to strtod(). + double GetReal(const std::string& section, const std::string& name, double default_value) const; + + // Get a boolean value from INI file, returning default_value if not found or if + // not a valid true/false value. Valid true values are "true", "yes", "on", "1", + // and valid false values are "false", "no", "off", "0" (not case sensitive). + bool GetBoolean(const std::string& section, const std::string& name, bool default_value) const; + + // Return true if the given section exists (section must contain at least + // one name=value pair). + bool HasSection(const std::string& section) const; + + // Return true if a value exists with the given section and field names. + bool HasValue(const std::string& section, const std::string& name) const; + +private: + int _error; + std::map _values; + static std::string MakeKey(const std::string& section, const std::string& name); + static int ValueHandler(void* user, const char* section, const char* name, + const char* value); +}; + +#endif // INIREADER_H diff --git a/thirdparty/inih/ini.c b/thirdparty/inih/ini.c new file mode 100644 index 000000000..e42a5c74a --- /dev/null +++ b/thirdparty/inih/ini.c @@ -0,0 +1,298 @@ +/* inih -- simple .INI file parser + +SPDX-License-Identifier: BSD-3-Clause + +Copyright (C) 2009-2020, Ben Hoyt + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include "ini.h" + +#if !INI_USE_STACK +#if INI_CUSTOM_ALLOCATOR +#include +void* ini_malloc(size_t size); +void ini_free(void* ptr); +void* ini_realloc(void* ptr, size_t size); +#else +#include +#define ini_malloc malloc +#define ini_free free +#define ini_realloc realloc +#endif +#endif + +#define MAX_SECTION 50 +#define MAX_NAME 50 + +/* Used by ini_parse_string() to keep track of string parsing state. */ +typedef struct { + const char* ptr; + size_t num_left; +} ini_parse_string_ctx; + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* lskip(const char* s) +{ + while (*s && isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char (of chars) or inline comment in given string, + or pointer to NUL at end of string if neither found. Inline comment must + be prefixed by a whitespace character to register as a comment. */ +static char* find_chars_or_comment(const char* s, const char* chars) +{ +#if INI_ALLOW_INLINE_COMMENTS + int was_space = 0; + while (*s && (!chars || !strchr(chars, *s)) && + !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { + was_space = isspace((unsigned char)(*s)); + s++; + } +#else + while (*s && (!chars || !strchr(chars, *s))) { + s++; + } +#endif + return (char*)s; +} + +/* Similar to strncpy, but ensures dest (size bytes) is + NUL-terminated, and doesn't pad with NULs. */ +static char* strncpy0(char* dest, const char* src, size_t size) +{ + /* Could use strncpy internally, but it causes gcc warnings (see issue #91) */ + size_t i; + for (i = 0; i < size - 1 && src[i]; i++) + dest[i] = src[i]; + dest[i] = '\0'; + return dest; +} + +/* See documentation in header file. */ +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; + int max_line = INI_MAX_LINE; +#else + char* line; + size_t max_line = INI_INITIAL_ALLOC; +#endif +#if INI_ALLOW_REALLOC && !INI_USE_STACK + char* new_line; + size_t offset; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)ini_malloc(INI_INITIAL_ALLOC); + if (!line) { + return -2; + } +#endif + +#if INI_HANDLER_LINENO +#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) +#else +#define HANDLER(u, s, n, v) handler(u, s, n, v) +#endif + + /* Scan through stream line by line */ + while (reader(line, (int)max_line, stream) != NULL) { +#if INI_ALLOW_REALLOC && !INI_USE_STACK + offset = strlen(line); + while (offset == max_line - 1 && line[offset - 1] != '\n') { + max_line *= 2; + if (max_line > INI_MAX_LINE) + max_line = INI_MAX_LINE; + new_line = ini_realloc(line, max_line); + if (!new_line) { + ini_free(line); + return -2; + } + line = new_line; + if (reader(line + offset, (int)(max_line - offset), stream) == NULL) + break; + if (max_line >= INI_MAX_LINE) + break; + offset += strlen(line + offset); + } +#endif + + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (strchr(INI_START_COMMENT_PREFIXES, *start)) { + /* Start-of-line comment */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* Non-blank line with leading whitespace, treat as continuation + of previous name's value (as per Python configparser). */ + if (!HANDLER(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_chars_or_comment(start + 1, "]"); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; +#if INI_CALL_HANDLER_ON_NEW_SECTION + if (!HANDLER(user, section, NULL, NULL) && !error) + error = lineno; +#endif + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start) { + /* Not a comment, must be a name[=:]value pair */ + end = find_chars_or_comment(start, "=:"); + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = end + 1; +#if INI_ALLOW_INLINE_COMMENTS + end = find_chars_or_comment(value, NULL); + if (*end) + *end = '\0'; +#endif + value = lskip(value); + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!HANDLER(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ +#if INI_ALLOW_NO_VALUE + *end = '\0'; + name = rstrip(start); + if (!HANDLER(user, section, name, NULL) && !error) + error = lineno; +#else + error = lineno; +#endif + } + } + +#if INI_STOP_ON_FIRST_ERROR + if (error) + break; +#endif + } + +#if !INI_USE_STACK + ini_free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +int ini_parse_file(FILE* file, ini_handler handler, void* user) +{ + return ini_parse_stream((ini_reader)fgets, file, handler, user); +} + +/* See documentation in header file. */ +int ini_parse(const char* filename, ini_handler handler, void* user) +{ + FILE* file; + int error; + + file = fopen(filename, "r"); + if (!file) + return -1; + error = ini_parse_file(file, handler, user); + fclose(file); + return error; +} + +/* An ini_reader function to read the next line from a string buffer. This + is the fgets() equivalent used by ini_parse_string(). */ +static char* ini_reader_string(char* str, int num, void* stream) { + ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; + const char* ctx_ptr = ctx->ptr; + size_t ctx_num_left = ctx->num_left; + char* strp = str; + char c; + + if (ctx_num_left == 0 || num < 2) + return NULL; + + while (num > 1 && ctx_num_left != 0) { + c = *ctx_ptr++; + ctx_num_left--; + *strp++ = c; + if (c == '\n') + break; + num--; + } + + *strp = '\0'; + ctx->ptr = ctx_ptr; + ctx->num_left = ctx_num_left; + return str; +} + +/* See documentation in header file. */ +int ini_parse_string(const char* string, ini_handler handler, void* user) { + ini_parse_string_ctx ctx; + + ctx.ptr = string; + ctx.num_left = strlen(string); + return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, + user); +} diff --git a/thirdparty/inih/ini.h b/thirdparty/inih/ini.h new file mode 100644 index 000000000..930d4e0a1 --- /dev/null +++ b/thirdparty/inih/ini.h @@ -0,0 +1,157 @@ +/* inih -- simple .INI file parser + +SPDX-License-Identifier: BSD-3-Clause + +Copyright (C) 2009-2020, Ben Hoyt + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#ifndef INI_H +#define INI_H + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Nonzero if ini_handler callback should accept lineno parameter. */ +#ifndef INI_HANDLER_LINENO +#define INI_HANDLER_LINENO 0 +#endif + +/* Typedef for prototype of handler function. */ +#if INI_HANDLER_LINENO +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value, + int lineno); +#else +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value); +#endif + +/* Typedef for prototype of fgets-style reader function. */ +typedef char* (*ini_reader)(char* str, int num, void* stream); + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's configparser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). +*/ +int ini_parse(const char* filename, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ +int ini_parse_file(FILE* file, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes an ini_reader function pointer instead of + filename. Used for implementing custom or string-based I/O (see also + ini_parse_string). */ +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user); + +/* Same as ini_parse(), but takes a zero-terminated string with the INI data +instead of a file. Useful for parsing INI data from a network socket or +already in memory. */ +int ini_parse_string(const char* string, ini_handler handler, void* user); + +/* Nonzero to allow multi-line value parsing, in the style of Python's + configparser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See https://github.com/benhoyt/inih/issues/21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Chars that begin a start-of-line comment. Per Python configparser, allow + both ; and # comments at the start of a line by default. */ +#ifndef INI_START_COMMENT_PREFIXES +#define INI_START_COMMENT_PREFIXES ";#" +#endif + +/* Nonzero to allow inline comments (with valid inline comment characters + specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match + Python 3.2+ configparser behaviour. */ +#ifndef INI_ALLOW_INLINE_COMMENTS +#define INI_ALLOW_INLINE_COMMENTS 1 +#endif +#ifndef INI_INLINE_COMMENT_PREFIXES +#define INI_INLINE_COMMENT_PREFIXES ";" +#endif + +/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Maximum line length for any line in INI file (stack or heap). Note that + this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +/* Nonzero to allow heap line buffer to grow via realloc(), zero for a + fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is + zero. */ +#ifndef INI_ALLOW_REALLOC +#define INI_ALLOW_REALLOC 0 +#endif + +/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK + is zero. */ +#ifndef INI_INITIAL_ALLOC +#define INI_INITIAL_ALLOC 200 +#endif + +/* Stop parsing on first error (default is to keep parsing). */ +#ifndef INI_STOP_ON_FIRST_ERROR +#define INI_STOP_ON_FIRST_ERROR 0 +#endif + +/* Nonzero to call the handler at the start of each new section (with + name and value NULL). Default is to only call the handler on + each name=value pair. */ +#ifndef INI_CALL_HANDLER_ON_NEW_SECTION +#define INI_CALL_HANDLER_ON_NEW_SECTION 0 +#endif + +/* Nonzero to allow a name without a value (no '=' or ':' on the line) and + call the handler with value NULL in this case. Default is to treat + no-value lines as an error. */ +#ifndef INI_ALLOW_NO_VALUE +#define INI_ALLOW_NO_VALUE 0 +#endif + +/* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory + allocation functions (INI_USE_STACK must also be 0). These functions must + have the same signatures as malloc/free/realloc and behave in a similar + way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */ +#ifndef INI_CUSTOM_ALLOCATOR +#define INI_CUSTOM_ALLOCATOR 0 +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* INI_H */ diff --git a/thirdparty/pybind11/.appveyor.yml b/thirdparty/pybind11/.appveyor.yml new file mode 100644 index 000000000..8fbb72610 --- /dev/null +++ b/thirdparty/pybind11/.appveyor.yml @@ -0,0 +1,70 @@ +version: 1.0.{build} +image: +- Visual Studio 2017 +- Visual Studio 2015 +test: off +skip_branch_with_pr: true +build: + parallel: true +platform: +- x64 +- x86 +environment: + matrix: + - PYTHON: 36 + CPP: 14 + CONFIG: Debug + - PYTHON: 27 + CPP: 14 + CONFIG: Debug + - CONDA: 36 + CPP: latest + CONFIG: Release +matrix: + exclude: + - image: Visual Studio 2015 + platform: x86 + - image: Visual Studio 2015 + CPP: latest + - image: Visual Studio 2017 + CPP: latest + platform: x86 +install: +- ps: | + if ($env:PLATFORM -eq "x64") { $env:CMAKE_ARCH = "x64" } + if ($env:APPVEYOR_JOB_NAME -like "*Visual Studio 2017*") { + $env:CMAKE_GENERATOR = "Visual Studio 15 2017" + $env:CMAKE_INCLUDE_PATH = "C:\Libraries\boost_1_64_0" + $env:CXXFLAGS = "-permissive-" + } else { + $env:CMAKE_GENERATOR = "Visual Studio 14 2015" + } + if ($env:PYTHON) { + if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } + $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" + python -W ignore -m pip install --upgrade pip wheel + python -W ignore -m pip install pytest numpy --no-warn-script-location + } elseif ($env:CONDA) { + if ($env:CONDA -eq "27") { $env:CONDA = "" } + if ($env:PLATFORM -eq "x64") { $env:CONDA = "$env:CONDA-x64" } + $env:PATH = "C:\Miniconda$env:CONDA\;C:\Miniconda$env:CONDA\Scripts\;$env:PATH" + $env:PYTHONHOME = "C:\Miniconda$env:CONDA" + conda --version + conda install -y -q pytest numpy scipy + } +- ps: | + Start-FileDownload 'http://bitbucket.org/eigen/eigen/get/3.3.3.zip' + 7z x 3.3.3.zip -y > $null + $env:CMAKE_INCLUDE_PATH = "eigen-eigen-67e894c6cd8f;$env:CMAKE_INCLUDE_PATH" +build_script: +- cmake -G "%CMAKE_GENERATOR%" -A "%CMAKE_ARCH%" + -DPYBIND11_CPP_STANDARD=/std:c++%CPP% + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DCMAKE_SUPPRESS_REGENERATION=1 + . +- set MSBuildLogger="C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" +- cmake --build . --config %CONFIG% --target pytest -- /m /v:m /logger:%MSBuildLogger% +- cmake --build . --config %CONFIG% --target cpptest -- /m /v:m /logger:%MSBuildLogger% +- if "%CPP%"=="latest" (cmake --build . --config %CONFIG% --target test_cmake_build -- /m /v:m /logger:%MSBuildLogger%) +on_failure: if exist "tests\test_cmake_build" type tests\test_cmake_build\*.log* diff --git a/thirdparty/pybind11/.gitignore b/thirdparty/pybind11/.gitignore new file mode 100644 index 000000000..979fd4431 --- /dev/null +++ b/thirdparty/pybind11/.gitignore @@ -0,0 +1,38 @@ +CMakeCache.txt +CMakeFiles +Makefile +cmake_install.cmake +.DS_Store +*.so +*.pyd +*.dll +*.sln +*.sdf +*.opensdf +*.vcxproj +*.filters +example.dir +Win32 +x64 +Release +Debug +.vs +CTestTestfile.cmake +Testing +autogen +MANIFEST +/.ninja_* +/*.ninja +/docs/.build +*.py[co] +*.egg-info +*~ +.*.swp +.DS_Store +/dist +/build +/cmake/ +.cache/ +sosize-*.txt +pybind11Config*.cmake +pybind11Targets.cmake diff --git a/thirdparty/pybind11/.gitmodules b/thirdparty/pybind11/.gitmodules new file mode 100644 index 000000000..d063a8e89 --- /dev/null +++ b/thirdparty/pybind11/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tools/clang"] + path = tools/clang + url = ../../wjakob/clang-cindex-python3 diff --git a/thirdparty/pybind11/.readthedocs.yml b/thirdparty/pybind11/.readthedocs.yml new file mode 100644 index 000000000..c9c61617c --- /dev/null +++ b/thirdparty/pybind11/.readthedocs.yml @@ -0,0 +1,3 @@ +python: + version: 3 +requirements_file: docs/requirements.txt diff --git a/thirdparty/pybind11/.travis.yml b/thirdparty/pybind11/.travis.yml new file mode 100644 index 000000000..d81cd8c7b --- /dev/null +++ b/thirdparty/pybind11/.travis.yml @@ -0,0 +1,306 @@ +language: cpp +matrix: + include: + # This config does a few things: + # - Checks C++ and Python code styles (check-style.sh and flake8). + # - Makes sure sphinx can build the docs without any errors or warnings. + # - Tests setup.py sdist and install (all header files should be present). + # - Makes sure that everything still works without optional deps (numpy/scipy/eigen) and + # also tests the automatic discovery functions in CMake (Python version, C++ standard). + - os: linux + dist: xenial # Necessary to run doxygen 1.8.15 + name: Style, docs, and pip + cache: false + before_install: + - pyenv global $(pyenv whence 2to3) # activate all python versions + - PY_CMD=python3 + - $PY_CMD -m pip install --user --upgrade pip wheel setuptools + install: + # breathe 4.14 doesn't work with bit fields. See https://github.com/michaeljones/breathe/issues/462 + - $PY_CMD -m pip install --user --upgrade sphinx sphinx_rtd_theme breathe==4.13.1 flake8 pep8-naming pytest + - curl -fsSL https://sourceforge.net/projects/doxygen/files/rel-1.8.15/doxygen-1.8.15.linux.bin.tar.gz/download | tar xz + - export PATH="$PWD/doxygen-1.8.15/bin:$PATH" + script: + - tools/check-style.sh + - flake8 + - $PY_CMD -m sphinx -W -b html docs docs/.build + - | + # Make sure setup.py distributes and installs all the headers + $PY_CMD setup.py sdist + $PY_CMD -m pip install --user -U ./dist/* + installed=$($PY_CMD -c "import pybind11; print(pybind11.get_include(True) + '/pybind11')") + diff -rq $installed ./include/pybind11 + - | + # Barebones build + cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(which $PY_CMD) . + make pytest -j 2 && make cpptest -j 2 + # The following are regular test configurations, including optional dependencies. + # With regard to each other they differ in Python version, C++ standard and compiler. + - os: linux + dist: trusty + name: Python 2.7, c++11, gcc 4.8 + env: PYTHON=2.7 CPP=11 GCC=4.8 + addons: + apt: + packages: + - cmake=2.\* + - cmake-data=2.\* + - os: linux + dist: trusty + name: Python 3.6, c++11, gcc 4.8 + env: PYTHON=3.6 CPP=11 GCC=4.8 + addons: + apt: + sources: + - deadsnakes + packages: + - python3.6-dev + - python3.6-venv + - cmake=2.\* + - cmake-data=2.\* + - os: linux + dist: trusty + env: PYTHON=2.7 CPP=14 GCC=6 CMAKE=1 + name: Python 2.7, c++14, gcc 6, CMake test + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-6 + - os: linux + dist: trusty + name: Python 3.5, c++14, gcc 6, Debug build + # N.B. `ensurepip` could be installed transitively by `python3.5-venv`, but + # seems to have apt conflicts (at least for Trusty). Use Docker instead. + services: docker + env: DOCKER=debian:stretch PYTHON=3.5 CPP=14 GCC=6 DEBUG=1 + - os: linux + dist: xenial + env: PYTHON=3.6 CPP=17 GCC=7 + name: Python 3.6, c++17, gcc 7 + addons: + apt: + sources: + - deadsnakes + - ubuntu-toolchain-r-test + packages: + - g++-7 + - python3.6-dev + - python3.6-venv + - os: linux + dist: xenial + env: PYTHON=3.6 CPP=17 CLANG=7 + name: Python 3.6, c++17, Clang 7 + addons: + apt: + sources: + - deadsnakes + - llvm-toolchain-xenial-7 + packages: + - python3.6-dev + - python3.6-venv + - clang-7 + - libclang-7-dev + - llvm-7-dev + - lld-7 + - libc++-7-dev + - libc++abi-7-dev # Why is this necessary??? + - os: linux + dist: xenial + env: PYTHON=3.8 CPP=17 GCC=7 + name: Python 3.8, c++17, gcc 7 (w/o numpy/scipy) # TODO: update build name when the numpy/scipy wheels become available + addons: + apt: + sources: + - deadsnakes + - ubuntu-toolchain-r-test + packages: + - g++-7 + - python3.8-dev + - python3.8-venv + # Currently there is no numpy/scipy wheels available for python3.8 + # TODO: remove next before_install, install and script clause when the wheels become available + before_install: + - pyenv global $(pyenv whence 2to3) # activate all python versions + - PY_CMD=python3 + - $PY_CMD -m pip install --user --upgrade pip wheel setuptools + install: + - $PY_CMD -m pip install --user --upgrade pytest + script: + - | + # Barebones build + cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(which $PY_CMD) . + make pytest -j 2 && make cpptest -j 2 + - os: osx + name: Python 2.7, c++14, AppleClang 7.3, CMake test + osx_image: xcode7.3 + env: PYTHON=2.7 CPP=14 CLANG CMAKE=1 + - os: osx + name: Python 3.7, c++14, AppleClang 9, Debug build + osx_image: xcode9.4 + env: PYTHON=3.7 CPP=14 CLANG DEBUG=1 + # Test a PyPy 2.7 build + - os: linux + dist: trusty + env: PYPY=5.8 PYTHON=2.7 CPP=11 GCC=4.8 + name: PyPy 5.8, Python 2.7, c++11, gcc 4.8 + addons: + apt: + packages: + - libblas-dev + - liblapack-dev + - gfortran + # Build in 32-bit mode and tests against the CMake-installed version + - os: linux + dist: trusty + services: docker + env: DOCKER=i386/debian:stretch PYTHON=3.5 CPP=14 GCC=6 INSTALL=1 + name: Python 3.5, c++14, gcc 6, 32-bit + script: + - | + # Consolidated 32-bit Docker Build + Install + set -ex + $SCRIPT_RUN_PREFIX sh -c " + set -ex + cmake ${CMAKE_EXTRA_ARGS} -DPYBIND11_INSTALL=1 -DPYBIND11_TEST=0 . + make install + cp -a tests /pybind11-tests + mkdir /build-tests && cd /build-tests + cmake ../pybind11-tests ${CMAKE_EXTRA_ARGS} -DPYBIND11_WERROR=ON + make pytest -j 2" + set +ex +cache: + directories: + - $HOME/.local/bin + - $HOME/.local/lib + - $HOME/.local/include + - $HOME/Library/Python +before_install: +- | + # Configure build variables + set -ex + if [ "$TRAVIS_OS_NAME" = "linux" ]; then + if [ -n "$CLANG" ]; then + export CXX=clang++-$CLANG CC=clang-$CLANG + EXTRA_PACKAGES+=" clang-$CLANG llvm-$CLANG-dev" + else + if [ -z "$GCC" ]; then GCC=4.8 + else EXTRA_PACKAGES+=" g++-$GCC" + fi + export CXX=g++-$GCC CC=gcc-$GCC + fi + elif [ "$TRAVIS_OS_NAME" = "osx" ]; then + export CXX=clang++ CC=clang; + fi + if [ -n "$CPP" ]; then CPP=-std=c++$CPP; fi + if [ "${PYTHON:0:1}" = "3" ]; then PY=3; fi + if [ -n "$DEBUG" ]; then CMAKE_EXTRA_ARGS+=" -DCMAKE_BUILD_TYPE=Debug"; fi + set +ex +- | + # Initialize environment + set -ex + if [ -n "$DOCKER" ]; then + docker pull $DOCKER + + containerid=$(docker run --detach --tty \ + --volume="$PWD":/pybind11 --workdir=/pybind11 \ + --env="CC=$CC" --env="CXX=$CXX" --env="DEBIAN_FRONTEND=$DEBIAN_FRONTEND" \ + --env=GCC_COLORS=\ \ + $DOCKER) + SCRIPT_RUN_PREFIX="docker exec --tty $containerid" + $SCRIPT_RUN_PREFIX sh -c 'for s in 0 15; do sleep $s; apt-get update && apt-get -qy dist-upgrade && break; done' + else + if [ "$PYPY" = "5.8" ]; then + curl -fSL https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.8.0-linux64.tar.bz2 | tar xj + PY_CMD=$(echo `pwd`/pypy2-v5.8.0-linux64/bin/pypy) + CMAKE_EXTRA_ARGS+=" -DPYTHON_EXECUTABLE:FILEPATH=$PY_CMD" + else + PY_CMD=python$PYTHON + if [ "$TRAVIS_OS_NAME" = "osx" ]; then + if [ "$PY" = "3" ]; then + brew update && brew unlink python@2 && brew upgrade python + else + curl -fsSL https://bootstrap.pypa.io/get-pip.py | $PY_CMD - --user + fi + fi + fi + if [ "$PY" = 3 ] || [ -n "$PYPY" ]; then + $PY_CMD -m ensurepip --user + fi + $PY_CMD --version + $PY_CMD -m pip install --user --upgrade pip wheel + fi + set +ex +install: +- | + # Install dependencies + set -ex + cmake --version + if [ -n "$DOCKER" ]; then + if [ -n "$DEBUG" ]; then + PY_DEBUG="python$PYTHON-dbg python$PY-scipy-dbg" + CMAKE_EXTRA_ARGS+=" -DPYTHON_EXECUTABLE=/usr/bin/python${PYTHON}dm" + fi + $SCRIPT_RUN_PREFIX sh -c "for s in 0 15; do sleep \$s; \ + apt-get -qy --no-install-recommends install \ + $PY_DEBUG python$PYTHON-dev python$PY-pytest python$PY-scipy \ + libeigen3-dev libboost-dev cmake make ${EXTRA_PACKAGES} && break; done" + else + + if [ "$CLANG" = "7" ]; then + export CXXFLAGS="-stdlib=libc++" + fi + + export NPY_NUM_BUILD_JOBS=2 + echo "Installing pytest, numpy, scipy..." + local PIP_CMD="" + if [ -n $PYPY ]; then + # For expediency, install only versions that are available on the extra index. + travis_wait 30 \ + $PY_CMD -m pip install --user --upgrade --extra-index-url https://imaginary.ca/trusty-pypi \ + pytest numpy==1.15.4 scipy==1.2.0 + else + $PY_CMD -m pip install --user --upgrade pytest numpy scipy + fi + echo "done." + + mkdir eigen + curl -fsSL https://bitbucket.org/eigen/eigen/get/3.3.4.tar.bz2 | \ + tar --extract -j --directory=eigen --strip-components=1 + export CMAKE_INCLUDE_PATH="${CMAKE_INCLUDE_PATH:+$CMAKE_INCLUDE_PATH:}$PWD/eigen" + fi + set +ex +script: +- | + # CMake Configuration + set -ex + $SCRIPT_RUN_PREFIX cmake ${CMAKE_EXTRA_ARGS} \ + -DPYBIND11_PYTHON_VERSION=$PYTHON \ + -DPYBIND11_CPP_STANDARD=$CPP \ + -DPYBIND11_WERROR=${WERROR:-ON} \ + -DDOWNLOAD_CATCH=${DOWNLOAD_CATCH:-ON} \ + . + set +ex +- | + # pytest + set -ex + $SCRIPT_RUN_PREFIX make pytest -j 2 VERBOSE=1 + set +ex +- | + # cpptest + set -ex + $SCRIPT_RUN_PREFIX make cpptest -j 2 + set +ex +- | + # CMake Build Interface + set -ex + if [ -n "$CMAKE" ]; then $SCRIPT_RUN_PREFIX make test_cmake_build; fi + set +ex +after_failure: cat tests/test_cmake_build/*.log* +after_script: +- | + # Cleanup (Docker) + set -ex + if [ -n "$DOCKER" ]; then docker stop "$containerid"; docker rm "$containerid"; fi + set +ex diff --git a/thirdparty/pybind11/CMakeLists.txt b/thirdparty/pybind11/CMakeLists.txt new file mode 100644 index 000000000..97ee24867 --- /dev/null +++ b/thirdparty/pybind11/CMakeLists.txt @@ -0,0 +1,159 @@ +# CMakeLists.txt -- Build system for the pybind11 modules +# +# Copyright (c) 2015 Wenzel Jakob +# +# All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +cmake_minimum_required(VERSION 2.8.12) + +if (POLICY CMP0048) + # cmake warns if loaded from a min-3.0-required parent dir, so silence the warning: + cmake_policy(SET CMP0048 NEW) +endif() + +# CMake versions < 3.4.0 do not support try_compile/pthread checks without C as active language. +if(CMAKE_VERSION VERSION_LESS 3.4.0) + project(pybind11) +else() + project(pybind11 CXX) +endif() + +# Check if pybind11 is being used directly or via add_subdirectory +set(PYBIND11_MASTER_PROJECT OFF) +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(PYBIND11_MASTER_PROJECT ON) +endif() + +option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) +option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/tools") + +include(pybind11Tools) + +# Cache variables so pybind11_add_module can be used in parent projects +set(PYBIND11_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/include" CACHE INTERNAL "") +set(PYTHON_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} CACHE INTERNAL "") +set(PYTHON_LIBRARIES ${PYTHON_LIBRARIES} CACHE INTERNAL "") +set(PYTHON_MODULE_PREFIX ${PYTHON_MODULE_PREFIX} CACHE INTERNAL "") +set(PYTHON_MODULE_EXTENSION ${PYTHON_MODULE_EXTENSION} CACHE INTERNAL "") +set(PYTHON_VERSION_MAJOR ${PYTHON_VERSION_MAJOR} CACHE INTERNAL "") +set(PYTHON_VERSION_MINOR ${PYTHON_VERSION_MINOR} CACHE INTERNAL "") + +# NB: when adding a header don't forget to also add it to setup.py +set(PYBIND11_HEADERS + include/pybind11/detail/class.h + include/pybind11/detail/common.h + include/pybind11/detail/descr.h + include/pybind11/detail/init.h + include/pybind11/detail/internals.h + include/pybind11/detail/typeid.h + include/pybind11/attr.h + include/pybind11/buffer_info.h + include/pybind11/cast.h + include/pybind11/chrono.h + include/pybind11/common.h + include/pybind11/complex.h + include/pybind11/options.h + include/pybind11/eigen.h + include/pybind11/embed.h + include/pybind11/eval.h + include/pybind11/functional.h + include/pybind11/numpy.h + include/pybind11/operators.h + include/pybind11/pybind11.h + include/pybind11/pytypes.h + include/pybind11/stl.h + include/pybind11/stl_bind.h +) +string(REPLACE "include/" "${CMAKE_CURRENT_SOURCE_DIR}/include/" + PYBIND11_HEADERS "${PYBIND11_HEADERS}") + +if (PYBIND11_TEST) + add_subdirectory(tests) +endif() + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +# extract project version from source +file(STRINGS "${PYBIND11_INCLUDE_DIR}/pybind11/detail/common.h" pybind11_version_defines + REGEX "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) ") +foreach(ver ${pybind11_version_defines}) + if (ver MATCHES "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$") + set(PYBIND11_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "") + endif() +endforeach() +set(${PROJECT_NAME}_VERSION ${PYBIND11_VERSION_MAJOR}.${PYBIND11_VERSION_MINOR}.${PYBIND11_VERSION_PATCH}) +message(STATUS "pybind11 v${${PROJECT_NAME}_VERSION}") + +option (USE_PYTHON_INCLUDE_DIR "Install pybind11 headers in Python include directory instead of default installation prefix" OFF) +if (USE_PYTHON_INCLUDE_DIR) + file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${PYTHON_INCLUDE_DIRS}) +endif() + +if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) # CMake >= 3.0 + # Build an interface library target: + add_library(pybind11 INTERFACE) + add_library(pybind11::pybind11 ALIAS pybind11) # to match exported target + target_include_directories(pybind11 INTERFACE $ + $ + $) + target_compile_options(pybind11 INTERFACE $) + + add_library(module INTERFACE) + add_library(pybind11::module ALIAS module) + if(NOT MSVC) + target_compile_options(module INTERFACE -fvisibility=hidden) + endif() + target_link_libraries(module INTERFACE pybind11::pybind11) + if(WIN32 OR CYGWIN) + target_link_libraries(module INTERFACE $) + elseif(APPLE) + target_link_libraries(module INTERFACE "-undefined dynamic_lookup") + endif() + + add_library(embed INTERFACE) + add_library(pybind11::embed ALIAS embed) + target_link_libraries(embed INTERFACE pybind11::pybind11 $) +endif() + +if (PYBIND11_INSTALL) + install(DIRECTORY ${PYBIND11_INCLUDE_DIR}/pybind11 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + # GNUInstallDirs "DATADIR" wrong here; CMake search path wants "share". + set(PYBIND11_CMAKECONFIG_INSTALL_DIR "share/cmake/${PROJECT_NAME}" CACHE STRING "install path for pybind11Config.cmake") + + configure_package_config_file(tools/${PROJECT_NAME}Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + # Remove CMAKE_SIZEOF_VOID_P from ConfigVersion.cmake since the library does + # not depend on architecture specific settings or libraries. + set(_PYBIND11_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) + unset(CMAKE_SIZEOF_VOID_P) + write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + VERSION ${${PROJECT_NAME}_VERSION} + COMPATIBILITY AnyNewerVersion) + set(CMAKE_SIZEOF_VOID_P ${_PYBIND11_CMAKE_SIZEOF_VOID_P}) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + tools/FindPythonLibsNew.cmake + tools/pybind11Tools.cmake + DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + + if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) + if(NOT PYBIND11_EXPORT_NAME) + set(PYBIND11_EXPORT_NAME "${PROJECT_NAME}Targets") + endif() + + install(TARGETS pybind11 module embed + EXPORT "${PYBIND11_EXPORT_NAME}") + if(PYBIND11_MASTER_PROJECT) + install(EXPORT "${PYBIND11_EXPORT_NAME}" + NAMESPACE "${PROJECT_NAME}::" + DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + endif() + endif() +endif() + +set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "libs") \ No newline at end of file diff --git a/thirdparty/pybind11/CONTRIBUTING.md b/thirdparty/pybind11/CONTRIBUTING.md new file mode 100644 index 000000000..01596d94f --- /dev/null +++ b/thirdparty/pybind11/CONTRIBUTING.md @@ -0,0 +1,49 @@ +Thank you for your interest in this project! Please refer to the following +sections on how to contribute code and bug reports. + +### Reporting bugs + +At the moment, this project is run in the spare time of a single person +([Wenzel Jakob](http://rgl.epfl.ch/people/wjakob)) with very limited resources +for issue tracker tickets. Thus, before submitting a question or bug report, +please take a moment of your time and ensure that your issue isn't already +discussed in the project documentation provided at +[http://pybind11.readthedocs.org/en/latest](http://pybind11.readthedocs.org/en/latest). + +Assuming that you have identified a previously unknown problem or an important +question, it's essential that you submit a self-contained and minimal piece of +code that reproduces the problem. In other words: no external dependencies, +isolate the function(s) that cause breakage, submit matched and complete C++ +and Python snippets that can be easily compiled and run on my end. + +## Pull requests +Contributions are submitted, reviewed, and accepted using Github pull requests. +Please refer to [this +article](https://help.github.com/articles/using-pull-requests) for details and +adhere to the following rules to make the process as smooth as possible: + +* Make a new branch for every feature you're working on. +* Make small and clean pull requests that are easy to review but make sure they + do add value by themselves. +* Add tests for any new functionality and run the test suite (``make pytest``) + to ensure that no existing features break. +* Please run ``flake8`` and ``tools/check-style.sh`` to check your code matches + the project style. (Note that ``check-style.sh`` requires ``gawk``.) +* This project has a strong focus on providing general solutions using a + minimal amount of code, thus small pull requests are greatly preferred. + +### Licensing of contributions + +pybind11 is provided under a BSD-style license that can be found in the +``LICENSE`` file. By using, distributing, or contributing to this project, you +agree to the terms and conditions of this license. + +You are under no obligation whatsoever to provide any bug fixes, patches, or +upgrades to the features, functionality or performance of the source code +("Enhancements") to anyone; however, if you choose to make your Enhancements +available either publicly, or directly to the author of this software, without +imposing a separate written license agreement for such Enhancements, then you +hereby grant the following license: a non-exclusive, royalty-free perpetual +license to install, use, modify, prepare derivative works, incorporate into +other computer software, distribute, and sublicense such enhancements or +derivative works thereof, in binary and source code form. diff --git a/thirdparty/pybind11/ISSUE_TEMPLATE.md b/thirdparty/pybind11/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..75df39981 --- /dev/null +++ b/thirdparty/pybind11/ISSUE_TEMPLATE.md @@ -0,0 +1,17 @@ +Make sure you've completed the following steps before submitting your issue -- thank you! + +1. Check if your question has already been answered in the [FAQ](http://pybind11.readthedocs.io/en/latest/faq.html) section. +2. Make sure you've read the [documentation](http://pybind11.readthedocs.io/en/latest/). Your issue may be addressed there. +3. If those resources didn't help and you only have a short question (not a bug report), consider asking in the [Gitter chat room](https://gitter.im/pybind/Lobby). +4. If you have a genuine bug report or a more complex question which is not answered in the previous items (or not suitable for chat), please fill in the details below. +5. Include a self-contained and minimal piece of code that reproduces the problem. If that's not possible, try to make the description as clear as possible. + +*After reading, remove this checklist and the template text in parentheses below.* + +## Issue description + +(Provide a short description, state the expected behavior and what actually happens.) + +## Reproducible example code + +(The code should be minimal, have no external dependencies, isolate the function(s) that cause breakage. Submit matched and complete C++ and Python snippets that can be easily compiled and run to diagnose the issue.) diff --git a/thirdparty/pybind11/LICENSE b/thirdparty/pybind11/LICENSE new file mode 100644 index 000000000..6f15578cc --- /dev/null +++ b/thirdparty/pybind11/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2016 Wenzel Jakob , All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Please also refer to the file CONTRIBUTING.md, which clarifies licensing of +external contributions to this project including patches, pull requests, etc. diff --git a/thirdparty/pybind11/MANIFEST.in b/thirdparty/pybind11/MANIFEST.in new file mode 100644 index 000000000..6e57baeee --- /dev/null +++ b/thirdparty/pybind11/MANIFEST.in @@ -0,0 +1,2 @@ +recursive-include include/pybind11 *.h +include LICENSE README.md CONTRIBUTING.md diff --git a/thirdparty/pybind11/README.md b/thirdparty/pybind11/README.md new file mode 100644 index 000000000..35d2d76ff --- /dev/null +++ b/thirdparty/pybind11/README.md @@ -0,0 +1,129 @@ +![pybind11 logo](https://github.com/pybind/pybind11/raw/master/docs/pybind11-logo.png) + +# pybind11 — Seamless operability between C++11 and Python + +[![Documentation Status](https://readthedocs.org/projects/pybind11/badge/?version=master)](http://pybind11.readthedocs.org/en/master/?badge=master) +[![Documentation Status](https://readthedocs.org/projects/pybind11/badge/?version=stable)](http://pybind11.readthedocs.org/en/stable/?badge=stable) +[![Gitter chat](https://img.shields.io/gitter/room/gitterHQ/gitter.svg)](https://gitter.im/pybind/Lobby) +[![Build Status](https://travis-ci.org/pybind/pybind11.svg?branch=master)](https://travis-ci.org/pybind/pybind11) +[![Build status](https://ci.appveyor.com/api/projects/status/riaj54pn4h08xy40?svg=true)](https://ci.appveyor.com/project/wjakob/pybind11) + +**pybind11** is a lightweight header-only library that exposes C++ types in Python +and vice versa, mainly to create Python bindings of existing C++ code. Its +goals and syntax are similar to the excellent +[Boost.Python](http://www.boost.org/doc/libs/1_58_0/libs/python/doc/) library +by David Abrahams: to minimize boilerplate code in traditional extension +modules by inferring type information using compile-time introspection. + +The main issue with Boost.Python—and the reason for creating such a similar +project—is Boost. Boost is an enormously large and complex suite of utility +libraries that works with almost every C++ compiler in existence. This +compatibility has its cost: arcane template tricks and workarounds are +necessary to support the oldest and buggiest of compiler specimens. Now that +C++11-compatible compilers are widely available, this heavy machinery has +become an excessively large and unnecessary dependency. + +Think of this library as a tiny self-contained version of Boost.Python with +everything stripped away that isn't relevant for binding generation. Without +comments, the core header files only require ~4K lines of code and depend on +Python (2.7 or 3.x, or PyPy2.7 >= 5.7) and the C++ standard library. This +compact implementation was possible thanks to some of the new C++11 language +features (specifically: tuples, lambda functions and variadic templates). Since +its creation, this library has grown beyond Boost.Python in many ways, leading +to dramatically simpler binding code in many common situations. + +Tutorial and reference documentation is provided at +[http://pybind11.readthedocs.org/en/master](http://pybind11.readthedocs.org/en/master). +A PDF version of the manual is available +[here](https://media.readthedocs.org/pdf/pybind11/master/pybind11.pdf). + +## Core features +pybind11 can map the following core C++ features to Python + +- Functions accepting and returning custom data structures per value, reference, or pointer +- Instance methods and static methods +- Overloaded functions +- Instance attributes and static attributes +- Arbitrary exception types +- Enumerations +- Callbacks +- Iterators and ranges +- Custom operators +- Single and multiple inheritance +- STL data structures +- Smart pointers with reference counting like ``std::shared_ptr`` +- Internal references with correct reference counting +- C++ classes with virtual (and pure virtual) methods can be extended in Python + +## Goodies +In addition to the core functionality, pybind11 provides some extra goodies: + +- Python 2.7, 3.x, and PyPy (PyPy2.7 >= 5.7) are supported with an + implementation-agnostic interface. + +- It is possible to bind C++11 lambda functions with captured variables. The + lambda capture data is stored inside the resulting Python function object. + +- pybind11 uses C++11 move constructors and move assignment operators whenever + possible to efficiently transfer custom data types. + +- It's easy to expose the internal storage of custom data types through + Pythons' buffer protocols. This is handy e.g. for fast conversion between + C++ matrix classes like Eigen and NumPy without expensive copy operations. + +- pybind11 can automatically vectorize functions so that they are transparently + applied to all entries of one or more NumPy array arguments. + +- Python's slice-based access and assignment operations can be supported with + just a few lines of code. + +- Everything is contained in just a few header files; there is no need to link + against any additional libraries. + +- Binaries are generally smaller by a factor of at least 2 compared to + equivalent bindings generated by Boost.Python. A recent pybind11 conversion + of PyRosetta, an enormous Boost.Python binding project, + [reported](http://graylab.jhu.edu/RosettaCon2016/PyRosetta-4.pdf) a binary + size reduction of **5.4x** and compile time reduction by **5.8x**. + +- Function signatures are precomputed at compile time (using ``constexpr``), + leading to smaller binaries. + +- With little extra effort, C++ types can be pickled and unpickled similar to + regular Python objects. + +## Supported compilers + +1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or newer) +2. GCC 4.8 or newer +3. Microsoft Visual Studio 2015 Update 3 or newer +4. Intel C++ compiler 17 or newer (16 with pybind11 v2.0 and 15 with pybind11 v2.0 and a [workaround](https://github.com/pybind/pybind11/issues/276)) +5. Cygwin/GCC (tested on 2.5.1) + +## About + +This project was created by [Wenzel Jakob](http://rgl.epfl.ch/people/wjakob). +Significant features and/or improvements to the code were contributed by +Jonas Adler, +Lori A. Burns, +Sylvain Corlay, +Trent Houliston, +Axel Huebl, +@hulucc, +Sergey Lyskov +Johan Mabille, +Tomasz Miąsko, +Dean Moldovan, +Ben Pritchard, +Jason Rhinelander, +Boris Schäling, +Pim Schellart, +Henry Schreiner, +Ivan Smirnov, and +Patrick Stewart. + +### License + +pybind11 is provided under a BSD-style license that can be found in the +``LICENSE`` file. By using, distributing, or contributing to this project, +you agree to the terms and conditions of this license. diff --git a/thirdparty/pybind11/include/pybind11/attr.h b/thirdparty/pybind11/include/pybind11/attr.h new file mode 100644 index 000000000..6962d6fc5 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/attr.h @@ -0,0 +1,493 @@ +/* + pybind11/attr.h: Infrastructure for processing custom + type and function attributes + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "cast.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// \addtogroup annotations +/// @{ + +/// Annotation for methods +struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; + +/// Annotation for operators +struct is_operator { }; + +/// Annotation for parent scope +struct scope { handle value; scope(const handle &s) : value(s) { } }; + +/// Annotation for documentation +struct doc { const char *value; doc(const char *value) : value(value) { } }; + +/// Annotation for function names +struct name { const char *value; name(const char *value) : value(value) { } }; + +/// Annotation indicating that a function is an overload associated with a given "sibling" +struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; + +/// Annotation indicating that a class derives from another given type +template struct base { + PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") + base() { } +}; + +/// Keep patient alive while nurse lives +template struct keep_alive { }; + +/// Annotation indicating that a class is involved in a multiple inheritance relationship +struct multiple_inheritance { }; + +/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class +struct dynamic_attr { }; + +/// Annotation which enables the buffer protocol for a type +struct buffer_protocol { }; + +/// Annotation which requests that a special metaclass is created for a type +struct metaclass { + handle value; + + PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") + metaclass() {} + + /// Override pybind11's default metaclass + explicit metaclass(handle value) : value(value) { } +}; + +/// Annotation that marks a class as local to the module: +struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } }; + +/// Annotation to mark enums as an arithmetic type +struct arithmetic { }; + +/** \rst + A call policy which places one or more guard variables (``Ts...``) around the function call. + + For example, this definition: + + .. code-block:: cpp + + m.def("foo", foo, py::call_guard()); + + is equivalent to the following pseudocode: + + .. code-block:: cpp + + m.def("foo", [](args...) { + T scope_guard; + return foo(args...); // forwarded arguments + }); + \endrst */ +template struct call_guard; + +template <> struct call_guard<> { using type = detail::void_type; }; + +template +struct call_guard { + static_assert(std::is_default_constructible::value, + "The guard type must be default constructible"); + + using type = T; +}; + +template +struct call_guard { + struct type { + T guard{}; // Compose multiple guard types with left-to-right default-constructor order + typename call_guard::type next{}; + }; +}; + +/// @} annotations + +NAMESPACE_BEGIN(detail) +/* Forward declarations */ +enum op_id : int; +enum op_type : int; +struct undefined_t; +template struct op_; +inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); + +/// Internal data structure which holds metadata about a keyword argument +struct argument_record { + const char *name; ///< Argument name + const char *descr; ///< Human-readable version of the argument value + handle value; ///< Associated Python object + bool convert : 1; ///< True if the argument is allowed to convert when loading + bool none : 1; ///< True if None is allowed when loading + + argument_record(const char *name, const char *descr, handle value, bool convert, bool none) + : name(name), descr(descr), value(value), convert(convert), none(none) { } +}; + +/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) +struct function_record { + function_record() + : is_constructor(false), is_new_style_constructor(false), is_stateless(false), + is_operator(false), has_args(false), has_kwargs(false), is_method(false) { } + + /// Function name + char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ + + // User-specified documentation string + char *doc = nullptr; + + /// Human-readable version of the function signature + char *signature = nullptr; + + /// List of registered keyword arguments + std::vector args; + + /// Pointer to lambda function which converts arguments and performs the actual call + handle (*impl) (function_call &) = nullptr; + + /// Storage for the wrapped function pointer and captured data, if any + void *data[3] = { }; + + /// Pointer to custom destructor for 'data' (if needed) + void (*free_data) (function_record *ptr) = nullptr; + + /// Return value policy associated with this function + return_value_policy policy = return_value_policy::automatic; + + /// True if name == '__init__' + bool is_constructor : 1; + + /// True if this is a new-style `__init__` defined in `detail/init.h` + bool is_new_style_constructor : 1; + + /// True if this is a stateless function pointer + bool is_stateless : 1; + + /// True if this is an operator (__add__), etc. + bool is_operator : 1; + + /// True if the function has a '*args' argument + bool has_args : 1; + + /// True if the function has a '**kwargs' argument + bool has_kwargs : 1; + + /// True if this is a method + bool is_method : 1; + + /// Number of arguments (including py::args and/or py::kwargs, if present) + std::uint16_t nargs; + + /// Python method object + PyMethodDef *def = nullptr; + + /// Python handle to the parent scope (a class or a module) + handle scope; + + /// Python handle to the sibling function representing an overload chain + handle sibling; + + /// Pointer to next overload + function_record *next = nullptr; +}; + +/// Special data structure which (temporarily) holds metadata about a bound class +struct type_record { + PYBIND11_NOINLINE type_record() + : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), + default_holder(true), module_local(false) { } + + /// Handle to the parent scope + handle scope; + + /// Name of the class + const char *name = nullptr; + + // Pointer to RTTI type_info data structure + const std::type_info *type = nullptr; + + /// How large is the underlying C++ type? + size_t type_size = 0; + + /// What is the alignment of the underlying C++ type? + size_t type_align = 0; + + /// How large is the type's holder? + size_t holder_size = 0; + + /// The global operator new can be overridden with a class-specific variant + void *(*operator_new)(size_t) = nullptr; + + /// Function pointer to class_<..>::init_instance + void (*init_instance)(instance *, const void *) = nullptr; + + /// Function pointer to class_<..>::dealloc + void (*dealloc)(detail::value_and_holder &) = nullptr; + + /// List of base classes of the newly created type + list bases; + + /// Optional docstring + const char *doc = nullptr; + + /// Custom metaclass (optional) + handle metaclass; + + /// Multiple inheritance marker + bool multiple_inheritance : 1; + + /// Does the class manage a __dict__? + bool dynamic_attr : 1; + + /// Does the class implement the buffer protocol? + bool buffer_protocol : 1; + + /// Is the default (unique_ptr) holder type used? + bool default_holder : 1; + + /// Is the class definition local to the module shared object? + bool module_local : 1; + + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { + auto base_info = detail::get_type_info(base, false); + if (!base_info) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + + "\" referenced unknown base type \"" + tname + "\""); + } + + if (default_holder != base_info->default_holder) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + + (default_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_info->default_holder ? "does not" : "does")); + } + + bases.append((PyObject *) base_info->type); + + if (base_info->type->tp_dictoffset != 0) + dynamic_attr = true; + + if (caster) + base_info->implicit_casts.emplace_back(type, caster); + } +}; + +inline function_call::function_call(const function_record &f, handle p) : + func(f), parent(p) { + args.reserve(f.nargs); + args_convert.reserve(f.nargs); +} + +/// Tag for a new-style `__init__` defined in `detail/init.h` +struct is_new_style_constructor { }; + +/** + * Partial template specializations to process custom attributes provided to + * cpp_function_ and class_. These are either used to initialize the respective + * fields in the type_record and function_record data structures or executed at + * runtime to deal with custom call policies (e.g. keep_alive). + */ +template struct process_attribute; + +template struct process_attribute_default { + /// Default implementation: do nothing + static void init(const T &, function_record *) { } + static void init(const T &, type_record *) { } + static void precall(function_call &) { } + static void postcall(function_call &, handle) { } +}; + +/// Process an attribute specifying the function's name +template <> struct process_attribute : process_attribute_default { + static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring +template <> struct process_attribute : process_attribute_default { + static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring (provided as a C-style string) +template <> struct process_attribute : process_attribute_default { + static void init(const char *d, function_record *r) { r->doc = const_cast(d); } + static void init(const char *d, type_record *r) { r->doc = const_cast(d); } +}; +template <> struct process_attribute : process_attribute { }; + +/// Process an attribute indicating the function's return value policy +template <> struct process_attribute : process_attribute_default { + static void init(const return_value_policy &p, function_record *r) { r->policy = p; } +}; + +/// Process an attribute which indicates that this is an overloaded function associated with a given sibling +template <> struct process_attribute : process_attribute_default { + static void init(const sibling &s, function_record *r) { r->sibling = s.value; } +}; + +/// Process an attribute which indicates that this function is a method +template <> struct process_attribute : process_attribute_default { + static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } +}; + +/// Process an attribute which indicates the parent scope of a method +template <> struct process_attribute : process_attribute_default { + static void init(const scope &s, function_record *r) { r->scope = s.value; } +}; + +/// Process an attribute which indicates that this function is an operator +template <> struct process_attribute : process_attribute_default { + static void init(const is_operator &, function_record *r) { r->is_operator = true; } +}; + +template <> struct process_attribute : process_attribute_default { + static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } +}; + +/// Process a keyword argument attribute (*without* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg &a, function_record *r) { + if (r->is_method && r->args.empty()) + r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); + r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); + } +}; + +/// Process a keyword argument attribute (*with* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg_v &a, function_record *r) { + if (r->is_method && r->args.empty()) + r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); + + if (!a.value) { +#if !defined(NDEBUG) + std::string descr("'"); + if (a.name) descr += std::string(a.name) + ": "; + descr += a.type + "'"; + if (r->is_method) { + if (r->name) + descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; + else + descr += " in method of '" + (std::string) str(r->scope) + "'"; + } else if (r->name) { + descr += " in function '" + (std::string) r->name + "'"; + } + pybind11_fail("arg(): could not convert default argument " + + descr + " into a Python object (type not registered yet?)"); +#else + pybind11_fail("arg(): could not convert default argument " + "into a Python object (type not registered yet?). " + "Compile in debug mode for more information."); +#endif + } + r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); + } +}; + +/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) +template +struct process_attribute::value>> : process_attribute_default { + static void init(const handle &h, type_record *r) { r->bases.append(h); } +}; + +/// Process a parent class attribute (deprecated, does not support multiple inheritance) +template +struct process_attribute> : process_attribute_default> { + static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } +}; + +/// Process a multiple inheritance attribute +template <> +struct process_attribute : process_attribute_default { + static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; } +}; + +template <> +struct process_attribute : process_attribute_default { + static void init(const module_local &l, type_record *r) { r->module_local = l.value; } +}; + +/// Process an 'arithmetic' attribute for enums (does nothing here) +template <> +struct process_attribute : process_attribute_default {}; + +template +struct process_attribute> : process_attribute_default> { }; + +/** + * Process a keep_alive call policy -- invokes keep_alive_impl during the + * pre-call handler if both Nurse, Patient != 0 and use the post-call handler + * otherwise + */ +template struct process_attribute> : public process_attribute_default> { + template = 0> + static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } + template = 0> + static void postcall(function_call &, handle) { } + template = 0> + static void precall(function_call &) { } + template = 0> + static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } +}; + +/// Recursively iterate over variadic template arguments +template struct process_attributes { + static void init(const Args&... args, function_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + ignore_unused(unused); + } + static void init(const Args&... args, type_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + ignore_unused(unused); + } + static void precall(function_call &call) { + int unused[] = { 0, (process_attribute::type>::precall(call), 0) ... }; + ignore_unused(unused); + } + static void postcall(function_call &call, handle fn_ret) { + int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0) ... }; + ignore_unused(unused); + } +}; + +template +using is_call_guard = is_instantiation; + +/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found) +template +using extract_guard_t = typename exactly_one_t, Extra...>::type; + +/// Check the number of named arguments at compile time +template ::value...), + size_t self = constexpr_sum(std::is_same::value...)> +constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { + return named == 0 || (self + named + has_args + has_kwargs) == nargs; +} + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/buffer_info.h b/thirdparty/pybind11/include/pybind11/buffer_info.h new file mode 100644 index 000000000..1f4115a1f --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/buffer_info.h @@ -0,0 +1,114 @@ +/* + pybind11/buffer_info.h: Python buffer object interface + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// Information record describing a Python buffer object +struct buffer_info { + void *ptr = nullptr; // Pointer to the underlying storage + ssize_t itemsize = 0; // Size of individual items in bytes + ssize_t size = 0; // Total number of entries + std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() + ssize_t ndim = 0; // Number of dimensions + std::vector shape; // Shape of the tensor (1 entry per dimension) + std::vector strides; // Number of bytes between adjacent entries (for each per dimension) + bool readonly = false; // flag to indicate if the underlying storage may be written to + + buffer_info() { } + + buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, + detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) + : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), + shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { + if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) + pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); + for (size_t i = 0; i < (size_t) ndim; ++i) + size *= shape[i]; + } + + template + buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) + : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { } + + buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false) + : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { } + + template + buffer_info(T *ptr, ssize_t size, bool readonly=false) + : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) { } + + template + buffer_info(const T *ptr, ssize_t size, bool readonly=true) + : buffer_info(const_cast(ptr), sizeof(T), format_descriptor::format(), size, readonly) { } + + explicit buffer_info(Py_buffer *view, bool ownview = true) + : buffer_info(view->buf, view->itemsize, view->format, view->ndim, + {view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}, view->readonly) { + this->view = view; + this->ownview = ownview; + } + + buffer_info(const buffer_info &) = delete; + buffer_info& operator=(const buffer_info &) = delete; + + buffer_info(buffer_info &&other) { + (*this) = std::move(other); + } + + buffer_info& operator=(buffer_info &&rhs) { + ptr = rhs.ptr; + itemsize = rhs.itemsize; + size = rhs.size; + format = std::move(rhs.format); + ndim = rhs.ndim; + shape = std::move(rhs.shape); + strides = std::move(rhs.strides); + std::swap(view, rhs.view); + std::swap(ownview, rhs.ownview); + readonly = rhs.readonly; + return *this; + } + + ~buffer_info() { + if (view && ownview) { PyBuffer_Release(view); delete view; } + } + +private: + struct private_ctr_tag { }; + + buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, + detail::any_container &&shape_in, detail::any_container &&strides_in, bool readonly) + : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { } + + Py_buffer *view = nullptr; + bool ownview = false; +}; + +NAMESPACE_BEGIN(detail) + +template struct compare_buffer_info { + static bool compare(const buffer_info& b) { + return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); + } +}; + +template struct compare_buffer_info::value>> { + static bool compare(const buffer_info& b) { + return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || + ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || + ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); + } +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/cast.h b/thirdparty/pybind11/include/pybind11/cast.h new file mode 100644 index 000000000..a0b4d1ba9 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/cast.h @@ -0,0 +1,2179 @@ +/* + pybind11/cast.h: Partial template specializations to cast between + C++ and Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pytypes.h" +#include "detail/typeid.h" +#include "detail/descr.h" +#include "detail/internals.h" +#include +#include +#include +#include + +#if defined(PYBIND11_CPP17) +# if defined(__has_include) +# if __has_include() +# define PYBIND11_HAS_STRING_VIEW +# endif +# elif defined(_MSC_VER) +# define PYBIND11_HAS_STRING_VIEW +# endif +#endif +#ifdef PYBIND11_HAS_STRING_VIEW +#include +#endif + +#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L +# define PYBIND11_HAS_U8STRING +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// A life support system for temporary objects created by `type_caster::load()`. +/// Adding a patient will keep it alive up until the enclosing function returns. +class loader_life_support { +public: + /// A new patient frame is created when a function is entered + loader_life_support() { + get_internals().loader_patient_stack.push_back(nullptr); + } + + /// ... and destroyed after it returns + ~loader_life_support() { + auto &stack = get_internals().loader_patient_stack; + if (stack.empty()) + pybind11_fail("loader_life_support: internal error"); + + auto ptr = stack.back(); + stack.pop_back(); + Py_CLEAR(ptr); + + // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) + if (stack.capacity() > 16 && stack.size() != 0 && stack.capacity() / stack.size() > 2) + stack.shrink_to_fit(); + } + + /// This can only be used inside a pybind11-bound function, either by `argument_loader` + /// at argument preparation time or by `py::cast()` at execution time. + PYBIND11_NOINLINE static void add_patient(handle h) { + auto &stack = get_internals().loader_patient_stack; + if (stack.empty()) + throw cast_error("When called outside a bound function, py::cast() cannot " + "do Python -> C++ conversions which require the creation " + "of temporary values"); + + auto &list_ptr = stack.back(); + if (list_ptr == nullptr) { + list_ptr = PyList_New(1); + if (!list_ptr) + pybind11_fail("loader_life_support: error allocating list"); + PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); + } else { + auto result = PyList_Append(list_ptr, h.ptr()); + if (result == -1) + pybind11_fail("loader_life_support: error adding patient"); + } + } +}; + +// Gets the cache entry for the given type, creating it if necessary. The return value is the pair +// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was +// just created. +inline std::pair all_type_info_get_cache(PyTypeObject *type); + +// Populates a just-created cache entry. +PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) { + std::vector check; + for (handle parent : reinterpret_borrow(t->tp_bases)) + check.push_back((PyTypeObject *) parent.ptr()); + + auto const &type_dict = get_internals().registered_types_py; + for (size_t i = 0; i < check.size(); i++) { + auto type = check[i]; + // Ignore Python2 old-style class super type: + if (!PyType_Check((PyObject *) type)) continue; + + // Check `type` in the current set of registered python types: + auto it = type_dict.find(type); + if (it != type_dict.end()) { + // We found a cache entry for it, so it's either pybind-registered or has pre-computed + // pybind bases, but we have to make sure we haven't already seen the type(s) before: we + // want to follow Python/virtual C++ rules that there should only be one instance of a + // common base. + for (auto *tinfo : it->second) { + // NB: Could use a second set here, rather than doing a linear search, but since + // having a large number of immediate pybind11-registered types seems fairly + // unlikely, that probably isn't worthwhile. + bool found = false; + for (auto *known : bases) { + if (known == tinfo) { found = true; break; } + } + if (!found) bases.push_back(tinfo); + } + } + else if (type->tp_bases) { + // It's some python type, so keep follow its bases classes to look for one or more + // registered types + if (i + 1 == check.size()) { + // When we're at the end, we can pop off the current element to avoid growing + // `check` when adding just one base (which is typical--i.e. when there is no + // multiple inheritance) + check.pop_back(); + i--; + } + for (handle parent : reinterpret_borrow(type->tp_bases)) + check.push_back((PyTypeObject *) parent.ptr()); + } + } +} + +/** + * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will + * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side + * derived class that uses single inheritance. Will contain as many types as required for a Python + * class that uses multiple inheritance to inherit (directly or indirectly) from multiple + * pybind-registered classes. Will be empty if neither the type nor any base classes are + * pybind-registered. + * + * The value is cached for the lifetime of the Python type. + */ +inline const std::vector &all_type_info(PyTypeObject *type) { + auto ins = all_type_info_get_cache(type); + if (ins.second) + // New cache entry: populate it + all_type_info_populate(type, ins.first->second); + + return ins.first->second; +} + +/** + * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any + * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use + * `all_type_info` instead if you want to support multiple bases. + */ +PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { + auto &bases = all_type_info(type); + if (bases.size() == 0) + return nullptr; + if (bases.size() > 1) + pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); + return bases.front(); +} + +inline detail::type_info *get_local_type_info(const std::type_index &tp) { + auto &locals = registered_local_types_cpp(); + auto it = locals.find(tp); + if (it != locals.end()) + return it->second; + return nullptr; +} + +inline detail::type_info *get_global_type_info(const std::type_index &tp) { + auto &types = get_internals().registered_types_cpp; + auto it = types.find(tp); + if (it != types.end()) + return it->second; + return nullptr; +} + +/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr. +PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_index &tp, + bool throw_if_missing = false) { + if (auto ltype = get_local_type_info(tp)) + return ltype; + if (auto gtype = get_global_type_info(tp)) + return gtype; + + if (throw_if_missing) { + std::string tname = tp.name(); + detail::clean_type_id(tname); + pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); + } + return nullptr; +} + +PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) { + detail::type_info *type_info = get_type_info(tp, throw_if_missing); + return handle(type_info ? ((PyObject *) type_info->type) : nullptr); +} + +struct value_and_holder { + instance *inst = nullptr; + size_t index = 0u; + const detail::type_info *type = nullptr; + void **vh = nullptr; + + // Main constructor for a found value/holder: + value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : + inst{i}, index{index}, type{type}, + vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} + {} + + // Default constructor (used to signal a value-and-holder not found by get_value_and_holder()) + value_and_holder() {} + + // Used for past-the-end iterator + value_and_holder(size_t index) : index{index} {} + + template V *&value_ptr() const { + return reinterpret_cast(vh[0]); + } + // True if this `value_and_holder` has a non-null value pointer + explicit operator bool() const { return value_ptr(); } + + template H &holder() const { + return reinterpret_cast(vh[1]); + } + bool holder_constructed() const { + return inst->simple_layout + ? inst->simple_holder_constructed + : inst->nonsimple.status[index] & instance::status_holder_constructed; + } + void set_holder_constructed(bool v = true) { + if (inst->simple_layout) + inst->simple_holder_constructed = v; + else if (v) + inst->nonsimple.status[index] |= instance::status_holder_constructed; + else + inst->nonsimple.status[index] &= (uint8_t) ~instance::status_holder_constructed; + } + bool instance_registered() const { + return inst->simple_layout + ? inst->simple_instance_registered + : inst->nonsimple.status[index] & instance::status_instance_registered; + } + void set_instance_registered(bool v = true) { + if (inst->simple_layout) + inst->simple_instance_registered = v; + else if (v) + inst->nonsimple.status[index] |= instance::status_instance_registered; + else + inst->nonsimple.status[index] &= (uint8_t) ~instance::status_instance_registered; + } +}; + +// Container for accessing and iterating over an instance's values/holders +struct values_and_holders { +private: + instance *inst; + using type_vec = std::vector; + const type_vec &tinfo; + +public: + values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {} + + struct iterator { + private: + instance *inst = nullptr; + const type_vec *types = nullptr; + value_and_holder curr; + friend struct values_and_holders; + iterator(instance *inst, const type_vec *tinfo) + : inst{inst}, types{tinfo}, + curr(inst /* instance */, + types->empty() ? nullptr : (*types)[0] /* type info */, + 0, /* vpos: (non-simple types only): the first vptr comes first */ + 0 /* index */) + {} + // Past-the-end iterator: + iterator(size_t end) : curr(end) {} + public: + bool operator==(const iterator &other) { return curr.index == other.curr.index; } + bool operator!=(const iterator &other) { return curr.index != other.curr.index; } + iterator &operator++() { + if (!inst->simple_layout) + curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; + ++curr.index; + curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; + return *this; + } + value_and_holder &operator*() { return curr; } + value_and_holder *operator->() { return &curr; } + }; + + iterator begin() { return iterator(inst, &tinfo); } + iterator end() { return iterator(tinfo.size()); } + + iterator find(const type_info *find_type) { + auto it = begin(), endit = end(); + while (it != endit && it->type != find_type) ++it; + return it; + } + + size_t size() { return tinfo.size(); } +}; + +/** + * Extracts C++ value and holder pointer references from an instance (which may contain multiple + * values/holders for python-side multiple inheritance) that match the given type. Throws an error + * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If + * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, + * regardless of type (and the resulting .type will be nullptr). + * + * The returned object should be short-lived: in particular, it must not outlive the called-upon + * instance. + */ +PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) { + // Optimize common case: + if (!find_type || Py_TYPE(this) == find_type->type) + return value_and_holder(this, find_type, 0, 0); + + detail::values_and_holders vhs(this); + auto it = vhs.find(find_type); + if (it != vhs.end()) + return *it; + + if (!throw_if_missing) + return value_and_holder(); + +#if defined(NDEBUG) + pybind11_fail("pybind11::detail::instance::get_value_and_holder: " + "type is not a pybind11 base of the given instance " + "(compile in debug mode for type details)"); +#else + pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + + std::string(find_type->type->tp_name) + "' is not a pybind11 base of the given `" + + std::string(Py_TYPE(this)->tp_name) + "' instance"); +#endif +} + +PYBIND11_NOINLINE inline void instance::allocate_layout() { + auto &tinfo = all_type_info(Py_TYPE(this)); + + const size_t n_types = tinfo.size(); + + if (n_types == 0) + pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); + + simple_layout = + n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); + + // Simple path: no python-side multiple inheritance, and a small-enough holder + if (simple_layout) { + simple_value_holder[0] = nullptr; + simple_holder_constructed = false; + simple_instance_registered = false; + } + else { // multiple base types or a too-large holder + // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, + // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool + // values that tracks whether each associated holder has been initialized. Each [block] is + // padded, if necessary, to an integer multiple of sizeof(void *). + size_t space = 0; + for (auto t : tinfo) { + space += 1; // value pointer + space += t->holder_size_in_ptrs; // holder instance + } + size_t flags_at = space; + space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) + + // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, + // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 + // they default to using pymalloc, which is designed to be efficient for small allocations + // like the one we're doing here; in earlier versions (and for larger allocations) they are + // just wrappers around malloc. +#if PY_VERSION_HEX >= 0x03050000 + nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); + if (!nonsimple.values_and_holders) throw std::bad_alloc(); +#else + nonsimple.values_and_holders = (void **) PyMem_New(void *, space); + if (!nonsimple.values_and_holders) throw std::bad_alloc(); + std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); +#endif + nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); + } + owned = true; +} + +PYBIND11_NOINLINE inline void instance::deallocate_layout() { + if (!simple_layout) + PyMem_Free(nonsimple.values_and_holders); +} + +PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { + handle type = detail::get_type_handle(tp, false); + if (!type) + return false; + return isinstance(obj, type); +} + +PYBIND11_NOINLINE inline std::string error_string() { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); + return "Unknown internal error occurred"; + } + + error_scope scope; // Preserve error state + + std::string errorString; + if (scope.type) { + errorString += handle(scope.type).attr("__name__").cast(); + errorString += ": "; + } + if (scope.value) + errorString += (std::string) str(scope.value); + + PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); + +#if PY_MAJOR_VERSION >= 3 + if (scope.trace != nullptr) + PyException_SetTraceback(scope.value, scope.trace); +#endif + +#if !defined(PYPY_VERSION) + if (scope.trace) { + PyTracebackObject *trace = (PyTracebackObject *) scope.trace; + + /* Get the deepest trace possible */ + while (trace->tb_next) + trace = trace->tb_next; + + PyFrameObject *frame = trace->tb_frame; + errorString += "\n\nAt:\n"; + while (frame) { + int lineno = PyFrame_GetLineNumber(frame); + errorString += + " " + handle(frame->f_code->co_filename).cast() + + "(" + std::to_string(lineno) + "): " + + handle(frame->f_code->co_name).cast() + "\n"; + frame = frame->f_back; + } + } +#endif + + return errorString; +} + +PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type ) { + auto &instances = get_internals().registered_instances; + auto range = instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + for (auto vh : values_and_holders(it->second)) { + if (vh.type == type) + return handle((PyObject *) it->second); + } + } + return handle(); +} + +inline PyThreadState *get_thread_state_unchecked() { +#if defined(PYPY_VERSION) + return PyThreadState_GET(); +#elif PY_VERSION_HEX < 0x03000000 + return _PyThreadState_Current; +#elif PY_VERSION_HEX < 0x03050000 + return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); +#elif PY_VERSION_HEX < 0x03050200 + return (PyThreadState*) _PyThreadState_Current.value; +#else + return _PyThreadState_UncheckedGet(); +#endif +} + +// Forward declarations +inline void keep_alive_impl(handle nurse, handle patient); +inline PyObject *make_new_instance(PyTypeObject *type); + +class type_caster_generic { +public: + PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) + : typeinfo(get_type_info(type_info)), cpptype(&type_info) { } + + type_caster_generic(const type_info *typeinfo) + : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) { } + + bool load(handle src, bool convert) { + return load_impl(src, convert); + } + + PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, + const detail::type_info *tinfo, + void *(*copy_constructor)(const void *), + void *(*move_constructor)(const void *), + const void *existing_holder = nullptr) { + if (!tinfo) // no type info: error will be set already + return handle(); + + void *src = const_cast(_src); + if (src == nullptr) + return none().release(); + + auto it_instances = get_internals().registered_instances.equal_range(src); + for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { + for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { + if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) + return handle((PyObject *) it_i->second).inc_ref(); + } + } + + auto inst = reinterpret_steal(make_new_instance(tinfo->type)); + auto wrapper = reinterpret_cast(inst.ptr()); + wrapper->owned = false; + void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); + + switch (policy) { + case return_value_policy::automatic: + case return_value_policy::take_ownership: + valueptr = src; + wrapper->owned = true; + break; + + case return_value_policy::automatic_reference: + case return_value_policy::reference: + valueptr = src; + wrapper->owned = false; + break; + + case return_value_policy::copy: + if (copy_constructor) + valueptr = copy_constructor(src); + else { +#if defined(NDEBUG) + throw cast_error("return_value_policy = copy, but type is " + "non-copyable! (compile in debug mode for details)"); +#else + std::string type_name(tinfo->cpptype->name()); + detail::clean_type_id(type_name); + throw cast_error("return_value_policy = copy, but type " + + type_name + " is non-copyable!"); +#endif + } + wrapper->owned = true; + break; + + case return_value_policy::move: + if (move_constructor) + valueptr = move_constructor(src); + else if (copy_constructor) + valueptr = copy_constructor(src); + else { +#if defined(NDEBUG) + throw cast_error("return_value_policy = move, but type is neither " + "movable nor copyable! " + "(compile in debug mode for details)"); +#else + std::string type_name(tinfo->cpptype->name()); + detail::clean_type_id(type_name); + throw cast_error("return_value_policy = move, but type " + + type_name + " is neither movable nor copyable!"); +#endif + } + wrapper->owned = true; + break; + + case return_value_policy::reference_internal: + valueptr = src; + wrapper->owned = false; + keep_alive_impl(inst, parent); + break; + + default: + throw cast_error("unhandled return_value_policy: should not happen!"); + } + + tinfo->init_instance(wrapper, existing_holder); + + return inst.release(); + } + + // Base methods for generic caster; there are overridden in copyable_holder_caster + void load_value(value_and_holder &&v_h) { + auto *&vptr = v_h.value_ptr(); + // Lazy allocation for unallocated values: + if (vptr == nullptr) { + auto *type = v_h.type ? v_h.type : typeinfo; + if (type->operator_new) { + vptr = type->operator_new(type->type_size); + } else { + #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) + if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + vptr = ::operator new(type->type_size, + std::align_val_t(type->type_align)); + else + #endif + vptr = ::operator new(type->type_size); + } + } + value = vptr; + } + bool try_implicit_casts(handle src, bool convert) { + for (auto &cast : typeinfo->implicit_casts) { + type_caster_generic sub_caster(*cast.first); + if (sub_caster.load(src, convert)) { + value = cast.second(sub_caster.value); + return true; + } + } + return false; + } + bool try_direct_conversions(handle src) { + for (auto &converter : *typeinfo->direct_conversions) { + if (converter(src.ptr(), value)) + return true; + } + return false; + } + void check_holder_compat() {} + + PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { + auto caster = type_caster_generic(ti); + if (caster.load(src, false)) + return caster.value; + return nullptr; + } + + /// Try to load with foreign typeinfo, if available. Used when there is no + /// native typeinfo, or when the native one wasn't able to produce a value. + PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) { + constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; + const auto pytype = src.get_type(); + if (!hasattr(pytype, local_key)) + return false; + + type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); + // Only consider this foreign loader if actually foreign and is a loader of the correct cpp type + if (foreign_typeinfo->module_local_load == &local_load + || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) + return false; + + if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { + value = result; + return true; + } + return false; + } + + // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant + // bits of code between here and copyable_holder_caster where the two classes need different + // logic (without having to resort to virtual inheritance). + template + PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { + if (!src) return false; + if (!typeinfo) return try_load_foreign_module_local(src); + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + value = nullptr; + return true; + } + + auto &this_ = static_cast(*this); + this_.check_holder_compat(); + + PyTypeObject *srctype = Py_TYPE(src.ptr()); + + // Case 1: If src is an exact type match for the target type then we can reinterpret_cast + // the instance's value pointer to the target type: + if (srctype == typeinfo->type) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); + return true; + } + // Case 2: We have a derived class + else if (PyType_IsSubtype(srctype, typeinfo->type)) { + auto &bases = all_type_info(srctype); + bool no_cpp_mi = typeinfo->simple_type; + + // Case 2a: the python type is a Python-inherited derived class that inherits from just + // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of + // the right type and we can use reinterpret_cast. + // (This is essentially the same as case 2b, but because not using multiple inheritance + // is extremely common, we handle it specially to avoid the loop iterator and type + // pointer lookup overhead) + if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); + return true; + } + // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if + // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we + // can safely reinterpret_cast to the relevant pointer. + else if (bases.size() > 1) { + for (auto base : bases) { + if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { + this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); + return true; + } + } + } + + // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match + // in the registered bases, above, so try implicit casting (needed for proper C++ casting + // when MI is involved). + if (this_.try_implicit_casts(src, convert)) + return true; + } + + // Perform an implicit conversion + if (convert) { + for (auto &converter : typeinfo->implicit_conversions) { + auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); + if (load_impl(temp, false)) { + loader_life_support::add_patient(temp); + return true; + } + } + if (this_.try_direct_conversions(src)) + return true; + } + + // Failed to match local typeinfo. Try again with global. + if (typeinfo->module_local) { + if (auto gtype = get_global_type_info(*typeinfo->cpptype)) { + typeinfo = gtype; + return load(src, false); + } + } + + // Global typeinfo has precedence over foreign module_local + return try_load_foreign_module_local(src); + } + + + // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast + // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair + // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). + PYBIND11_NOINLINE static std::pair src_and_type( + const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { + if (auto *tpi = get_type_info(cast_type)) + return {src, const_cast(tpi)}; + + // Not found, set error: + std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); + detail::clean_type_id(tname); + std::string msg = "Unregistered type : " + tname; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return {nullptr, nullptr}; + } + + const type_info *typeinfo = nullptr; + const std::type_info *cpptype = nullptr; + void *value = nullptr; +}; + +/** + * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster + * needs to provide `operator T*()` and `operator T&()` operators. + * + * If the type supports moving the value away via an `operator T&&() &&` method, it should use + * `movable_cast_op_type` instead. + */ +template +using cast_op_type = + conditional_t>::value, + typename std::add_pointer>::type, + typename std::add_lvalue_reference>::type>; + +/** + * Determine suitable casting operator for a type caster with a movable value. Such a type caster + * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be + * called in appropriate contexts where the value can be moved rather than copied. + * + * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. + */ +template +using movable_cast_op_type = + conditional_t::type>::value, + typename std::add_pointer>::type, + conditional_t::value, + typename std::add_rvalue_reference>::type, + typename std::add_lvalue_reference>::type>>; + +// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when +// T is non-copyable, but code containing such a copy constructor fails to actually compile. +template struct is_copy_constructible : std::is_copy_constructible {}; + +// Specialization for types that appear to be copy constructible but also look like stl containers +// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if +// so, copy constructability depends on whether the value_type is copy constructible. +template struct is_copy_constructible, + std::is_same, + // Avoid infinite recursion + negation> + >::value>> : is_copy_constructible {}; + +// Likewise for std::pair +// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves +// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers). +template struct is_copy_constructible> + : all_of, is_copy_constructible> {}; + +// The same problems arise with std::is_copy_assignable, so we use the same workaround. +template struct is_copy_assignable : std::is_copy_assignable {}; +template struct is_copy_assignable, + std::is_same + >::value>> : is_copy_assignable {}; +template struct is_copy_assignable> + : all_of, is_copy_assignable> {}; + +NAMESPACE_END(detail) + +// polymorphic_type_hook::get(src, tinfo) determines whether the object pointed +// to by `src` actually is an instance of some class derived from `itype`. +// If so, it sets `tinfo` to point to the std::type_info representing that derived +// type, and returns a pointer to the start of the most-derived object of that type +// (in which `src` is a subobject; this will be the same address as `src` in most +// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src` +// and leaves `tinfo` at its default value of nullptr. +// +// The default polymorphic_type_hook just returns src. A specialization for polymorphic +// types determines the runtime type of the passed object and adjusts the this-pointer +// appropriately via dynamic_cast. This is what enables a C++ Animal* to appear +// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is +// registered with pybind11, and this Animal is in fact a Dog). +// +// You may specialize polymorphic_type_hook yourself for types that want to appear +// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern +// in performance-sensitive applications, used most notably in LLVM.) +template +struct polymorphic_type_hook +{ + static const void *get(const itype *src, const std::type_info*&) { return src; } +}; +template +struct polymorphic_type_hook::value>> +{ + static const void *get(const itype *src, const std::type_info*& type) { + type = src ? &typeid(*src) : nullptr; + return dynamic_cast(src); + } +}; + +NAMESPACE_BEGIN(detail) + +/// Generic type caster for objects stored on the heap +template class type_caster_base : public type_caster_generic { + using itype = intrinsic_t; + +public: + static constexpr auto name = _(); + + type_caster_base() : type_caster_base(typeid(type)) { } + explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } + + static handle cast(const itype &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast(&src, policy, parent); + } + + static handle cast(itype &&src, return_value_policy, handle parent) { + return cast(&src, return_value_policy::move, parent); + } + + // Returns a (pointer, type_info) pair taking care of necessary type lookup for a + // polymorphic type (using RTTI by default, but can be overridden by specializing + // polymorphic_type_hook). If the instance isn't derived, returns the base version. + static std::pair src_and_type(const itype *src) { + auto &cast_type = typeid(itype); + const std::type_info *instance_type = nullptr; + const void *vsrc = polymorphic_type_hook::get(src, instance_type); + if (instance_type && !same_type(cast_type, *instance_type)) { + // This is a base pointer to a derived type. If the derived type is registered + // with pybind11, we want to make the full derived object available. + // In the typical case where itype is polymorphic, we get the correct + // derived pointer (which may be != base pointer) by a dynamic_cast to + // most derived type. If itype is not polymorphic, we won't get here + // except via a user-provided specialization of polymorphic_type_hook, + // and the user has promised that no this-pointer adjustment is + // required in that case, so it's OK to use static_cast. + if (const auto *tpi = get_type_info(*instance_type)) + return {vsrc, tpi}; + } + // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so + // don't do a cast + return type_caster_generic::src_and_type(src, cast_type, instance_type); + } + + static handle cast(const itype *src, return_value_policy policy, handle parent) { + auto st = src_and_type(src); + return type_caster_generic::cast( + st.first, policy, parent, st.second, + make_copy_constructor(src), make_move_constructor(src)); + } + + static handle cast_holder(const itype *src, const void *holder) { + auto st = src_and_type(src); + return type_caster_generic::cast( + st.first, return_value_policy::take_ownership, {}, st.second, + nullptr, nullptr, holder); + } + + template using cast_op_type = detail::cast_op_type; + + operator itype*() { return (type *) value; } + operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } + +protected: + using Constructor = void *(*)(const void *); + + /* Only enabled when the types are {copy,move}-constructible *and* when the type + does not have a private operator new implementation. */ + template ::value>> + static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor{}) { + return [](const void *arg) -> void * { + return new T(*reinterpret_cast(arg)); + }; + } + + template ::value>> + static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor{}) { + return [](const void *arg) -> void * { + return new T(std::move(*const_cast(reinterpret_cast(arg)))); + }; + } + + static Constructor make_copy_constructor(...) { return nullptr; } + static Constructor make_move_constructor(...) { return nullptr; } +}; + +template class type_caster : public type_caster_base { }; +template using make_caster = type_caster>; + +// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T +template typename make_caster::template cast_op_type cast_op(make_caster &caster) { + return caster.operator typename make_caster::template cast_op_type(); +} +template typename make_caster::template cast_op_type::type> +cast_op(make_caster &&caster) { + return std::move(caster).operator + typename make_caster::template cast_op_type::type>(); +} + +template class type_caster> { +private: + using caster_t = make_caster; + caster_t subcaster; + using subcaster_cast_op_type = typename caster_t::template cast_op_type; + static_assert(std::is_same::type &, subcaster_cast_op_type>::value, + "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); +public: + bool load(handle src, bool convert) { return subcaster.load(src, convert); } + static constexpr auto name = caster_t::name; + static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { + // It is definitely wrong to take ownership of this pointer, so mask that rvp + if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) + policy = return_value_policy::automatic_reference; + return caster_t::cast(&src.get(), policy, parent); + } + template using cast_op_type = std::reference_wrapper; + operator std::reference_wrapper() { return subcaster.operator subcaster_cast_op_type&(); } +}; + +#define PYBIND11_TYPE_CASTER(type, py_name) \ + protected: \ + type value; \ + public: \ + static constexpr auto name = py_name; \ + template >::value, int> = 0> \ + static handle cast(T_ *src, return_value_policy policy, handle parent) { \ + if (!src) return none().release(); \ + if (policy == return_value_policy::take_ownership) { \ + auto h = cast(std::move(*src), policy, parent); delete src; return h; \ + } else { \ + return cast(*src, policy, parent); \ + } \ + } \ + operator type*() { return &value; } \ + operator type&() { return value; } \ + operator type&&() && { return std::move(value); } \ + template using cast_op_type = pybind11::detail::movable_cast_op_type + + +template using is_std_char_type = any_of< + std::is_same, /* std::string */ +#if defined(PYBIND11_HAS_U8STRING) + std::is_same, /* std::u8string */ +#endif + std::is_same, /* std::u16string */ + std::is_same, /* std::u32string */ + std::is_same /* std::wstring */ +>; + +template +struct type_caster::value && !is_std_char_type::value>> { + using _py_type_0 = conditional_t; + using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; + using py_type = conditional_t::value, double, _py_type_1>; +public: + + bool load(handle src, bool convert) { + py_type py_value; + + if (!src) + return false; + + if (std::is_floating_point::value) { + if (convert || PyFloat_Check(src.ptr())) + py_value = (py_type) PyFloat_AsDouble(src.ptr()); + else + return false; + } else if (PyFloat_Check(src.ptr())) { + return false; + } else if (std::is_unsigned::value) { + py_value = as_unsigned(src.ptr()); + } else { // signed integer: + py_value = sizeof(T) <= sizeof(long) + ? (py_type) PyLong_AsLong(src.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); + } + + bool py_err = py_value == (py_type) -1 && PyErr_Occurred(); + + // Protect std::numeric_limits::min/max with parentheses + if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && + (py_value < (py_type) (std::numeric_limits::min)() || + py_value > (py_type) (std::numeric_limits::max)()))) { + bool type_error = py_err && PyErr_ExceptionMatches( +#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) + PyExc_SystemError +#else + PyExc_TypeError +#endif + ); + PyErr_Clear(); + if (type_error && convert && PyNumber_Check(src.ptr())) { + auto tmp = reinterpret_steal(std::is_floating_point::value + ? PyNumber_Float(src.ptr()) + : PyNumber_Long(src.ptr())); + PyErr_Clear(); + return load(tmp, false); + } + return false; + } + + value = (T) py_value; + return true; + } + + template + static typename std::enable_if::value, handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyFloat_FromDouble((double) src); + } + + template + static typename std::enable_if::value && std::is_signed::value && (sizeof(U) <= sizeof(long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_SIGNED((long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); + } + + template + static typename std::enable_if::value && std::is_signed::value && (sizeof(U) > sizeof(long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromLongLong((long long) src); + } + + template + static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) > sizeof(unsigned long)), handle>::type + cast(U src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromUnsignedLongLong((unsigned long long) src); + } + + PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); +}; + +template struct void_caster { +public: + bool load(handle src, bool) { + if (src && src.is_none()) + return true; + return false; + } + static handle cast(T, return_value_policy /* policy */, handle /* parent */) { + return none().inc_ref(); + } + PYBIND11_TYPE_CASTER(T, _("None")); +}; + +template <> class type_caster : public void_caster {}; + +template <> class type_caster : public type_caster { +public: + using type_caster::cast; + + bool load(handle h, bool) { + if (!h) { + return false; + } else if (h.is_none()) { + value = nullptr; + return true; + } + + /* Check if this is a capsule */ + if (isinstance(h)) { + value = reinterpret_borrow(h); + return true; + } + + /* Check if this is a C++ type */ + auto &bases = all_type_info((PyTypeObject *) h.get_type().ptr()); + if (bases.size() == 1) { // Only allowing loading from a single-value type + value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); + return true; + } + + /* Fail */ + return false; + } + + static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { + if (ptr) + return capsule(ptr).release(); + else + return none().inc_ref(); + } + + template using cast_op_type = void*&; + operator void *&() { return value; } + static constexpr auto name = _("capsule"); +private: + void *value = nullptr; +}; + +template <> class type_caster : public void_caster { }; + +template <> class type_caster { +public: + bool load(handle src, bool convert) { + if (!src) return false; + else if (src.ptr() == Py_True) { value = true; return true; } + else if (src.ptr() == Py_False) { value = false; return true; } + else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { + // (allow non-implicit conversion for numpy booleans) + + Py_ssize_t res = -1; + if (src.is_none()) { + res = 0; // None is implicitly converted to False + } + #if defined(PYPY_VERSION) + // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists + else if (hasattr(src, PYBIND11_BOOL_ATTR)) { + res = PyObject_IsTrue(src.ptr()); + } + #else + // Alternate approach for CPython: this does the same as the above, but optimized + // using the CPython API so as to avoid an unneeded attribute lookup. + else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { + if (PYBIND11_NB_BOOL(tp_as_number)) { + res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); + } + } + #endif + if (res == 0 || res == 1) { + value = (bool) res; + return true; + } else { + PyErr_Clear(); + } + } + return false; + } + static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { + return handle(src ? Py_True : Py_False).inc_ref(); + } + PYBIND11_TYPE_CASTER(bool, _("bool")); +}; + +// Helper class for UTF-{8,16,32} C++ stl strings: +template struct string_caster { + using CharT = typename StringType::value_type; + + // Simplify life by being able to assume standard char sizes (the standard only guarantees + // minimums, but Python requires exact sizes) + static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); +#if defined(PYBIND11_HAS_U8STRING) + static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char8_t size != 1"); +#endif + static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); + static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); + // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) + static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, + "Unsupported wchar_t size != 2/4"); + static constexpr size_t UTF_N = 8 * sizeof(CharT); + + bool load(handle src, bool) { +#if PY_MAJOR_VERSION < 3 + object temp; +#endif + handle load_src = src; + if (!src) { + return false; + } else if (!PyUnicode_Check(load_src.ptr())) { +#if PY_MAJOR_VERSION >= 3 + return load_bytes(load_src); +#else + if (std::is_same::value) { + return load_bytes(load_src); + } + + // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false + if (!PYBIND11_BYTES_CHECK(load_src.ptr())) + return false; + + temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); + if (!temp) { PyErr_Clear(); return false; } + load_src = temp; +#endif + } + + object utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( + load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); + if (!utfNbytes) { PyErr_Clear(); return false; } + + const CharT *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); + size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); + if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 + value = StringType(buffer, length); + + // If we're loading a string_view we need to keep the encoded Python object alive: + if (IsView) + loader_life_support::add_patient(utfNbytes); + + return true; + } + + static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { + const char *buffer = reinterpret_cast(src.data()); + ssize_t nbytes = ssize_t(src.size() * sizeof(CharT)); + handle s = decode_utfN(buffer, nbytes); + if (!s) throw error_already_set(); + return s; + } + + PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); + +private: + static handle decode_utfN(const char *buffer, ssize_t nbytes) { +#if !defined(PYPY_VERSION) + return + UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : + UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : + PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); +#else + // PyPy seems to have multiple problems related to PyUnicode_UTF*: the UTF8 version + // sometimes segfaults for unknown reasons, while the UTF16 and 32 versions require a + // non-const char * arguments, which is also a nuisance, so bypass the whole thing by just + // passing the encoding as a string value, which works properly: + return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); +#endif + } + + // When loading into a std::string or char*, accept a bytes object as-is (i.e. + // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. + // which supports loading a unicode from a str, doesn't take this path. + template + bool load_bytes(enable_if_t::value, handle> src) { + if (PYBIND11_BYTES_CHECK(src.ptr())) { + // We were passed a Python 3 raw bytes; accept it into a std::string or char* + // without any encoding attempt. + const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); + if (bytes) { + value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); + return true; + } + } + + return false; + } + + template + bool load_bytes(enable_if_t::value, handle>) { return false; } +}; + +template +struct type_caster, enable_if_t::value>> + : string_caster> {}; + +#ifdef PYBIND11_HAS_STRING_VIEW +template +struct type_caster, enable_if_t::value>> + : string_caster, true> {}; +#endif + +// Type caster for C-style strings. We basically use a std::string type caster, but also add the +// ability to use None as a nullptr char* (which the string caster doesn't allow). +template struct type_caster::value>> { + using StringType = std::basic_string; + using StringCaster = type_caster; + StringCaster str_caster; + bool none = false; + CharT one_char = 0; +public: + bool load(handle src, bool convert) { + if (!src) return false; + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + none = true; + return true; + } + return str_caster.load(src, convert); + } + + static handle cast(const CharT *src, return_value_policy policy, handle parent) { + if (src == nullptr) return pybind11::none().inc_ref(); + return StringCaster::cast(StringType(src), policy, parent); + } + + static handle cast(CharT src, return_value_policy policy, handle parent) { + if (std::is_same::value) { + handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); + if (!s) throw error_already_set(); + return s; + } + return StringCaster::cast(StringType(1, src), policy, parent); + } + + operator CharT*() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } + operator CharT&() { + if (none) + throw value_error("Cannot convert None to a character"); + + auto &value = static_cast(str_caster); + size_t str_len = value.size(); + if (str_len == 0) + throw value_error("Cannot convert empty string to a character"); + + // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that + // is too high, and one for multiple unicode characters (caught later), so we need to figure + // out how long the first encoded character is in bytes to distinguish between these two + // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those + // can fit into a single char value. + if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { + unsigned char v0 = static_cast(value[0]); + size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 + (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence + (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence + 4; // 0b11110xxx - start of 4-byte sequence + + if (char0_bytes == str_len) { + // If we have a 128-255 value, we can decode it into a single char: + if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx + one_char = static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); + return one_char; + } + // Otherwise we have a single character, but it's > U+00FF + throw value_error("Character code point not in range(0x100)"); + } + } + + // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a + // surrogate pair with total length 2 instantly indicates a range error (but not a "your + // string was too long" error). + else if (StringCaster::UTF_N == 16 && str_len == 2) { + one_char = static_cast(value[0]); + if (one_char >= 0xD800 && one_char < 0xE000) + throw value_error("Character code point not in range(0x10000)"); + } + + if (str_len != 1) + throw value_error("Expected a character, but multi-character string found"); + + one_char = value[0]; + return one_char; + } + + static constexpr auto name = _(PYBIND11_STRING_NAME); + template using cast_op_type = pybind11::detail::cast_op_type<_T>; +}; + +// Base implementation for std::tuple and std::pair +template class Tuple, typename... Ts> class tuple_caster { + using type = Tuple; + static constexpr auto size = sizeof...(Ts); + using indices = make_index_sequence; +public: + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + const auto seq = reinterpret_borrow(src); + if (seq.size() != size) + return false; + return load_impl(seq, convert, indices{}); + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + return cast_impl(std::forward(src), policy, parent, indices{}); + } + + static constexpr auto name = _("Tuple[") + concat(make_caster::name...) + _("]"); + + template using cast_op_type = type; + + operator type() & { return implicit_cast(indices{}); } + operator type() && { return std::move(*this).implicit_cast(indices{}); } + +protected: + template + type implicit_cast(index_sequence) & { return type(cast_op(std::get(subcasters))...); } + template + type implicit_cast(index_sequence) && { return type(cast_op(std::move(std::get(subcasters)))...); } + + static constexpr bool load_impl(const sequence &, bool, index_sequence<>) { return true; } + + template + bool load_impl(const sequence &seq, bool convert, index_sequence) { +#ifdef __cpp_fold_expressions + if ((... || !std::get(subcasters).load(seq[Is], convert))) + return false; +#else + for (bool r : {std::get(subcasters).load(seq[Is], convert)...}) + if (!r) + return false; +#endif + return true; + } + + /* Implementation: Convert a C++ tuple into a Python tuple */ + template + static handle cast_impl(T &&src, return_value_policy policy, handle parent, index_sequence) { + std::array entries{{ + reinterpret_steal(make_caster::cast(std::get(std::forward(src)), policy, parent))... + }}; + for (const auto &entry: entries) + if (!entry) + return handle(); + tuple result(size); + int counter = 0; + for (auto & entry: entries) + PyTuple_SET_ITEM(result.ptr(), counter++, entry.release().ptr()); + return result.release(); + } + + Tuple...> subcasters; +}; + +template class type_caster> + : public tuple_caster {}; + +template class type_caster> + : public tuple_caster {}; + +/// Helper class which abstracts away certain actions. Users can provide specializations for +/// custom holders, but it's only necessary if the type has a non-standard interface. +template +struct holder_helper { + static auto get(const T &p) -> decltype(p.get()) { return p.get(); } +}; + +/// Type caster for holder types like std::shared_ptr, etc. +template +struct copyable_holder_caster : public type_caster_base { +public: + using base = type_caster_base; + static_assert(std::is_base_of>::value, + "Holder classes are only supported for custom types"); + using base::base; + using base::cast; + using base::typeinfo; + using base::value; + + bool load(handle src, bool convert) { + return base::template load_impl>(src, convert); + } + + explicit operator type*() { return this->value; } + explicit operator type&() { return *(this->value); } + explicit operator holder_type*() { return std::addressof(holder); } + + // Workaround for Intel compiler bug + // see pybind11 issue 94 + #if defined(__ICC) || defined(__INTEL_COMPILER) + operator holder_type&() { return holder; } + #else + explicit operator holder_type&() { return holder; } + #endif + + static handle cast(const holder_type &src, return_value_policy, handle) { + const auto *ptr = holder_helper::get(src); + return type_caster_base::cast_holder(ptr, &src); + } + +protected: + friend class type_caster_generic; + void check_holder_compat() { + if (typeinfo->default_holder) + throw cast_error("Unable to load a custom holder type from a default-holder instance"); + } + + bool load_value(value_and_holder &&v_h) { + if (v_h.holder_constructed()) { + value = v_h.value_ptr(); + holder = v_h.template holder(); + return true; + } else { + throw cast_error("Unable to cast from non-held to held instance (T& to Holder) " +#if defined(NDEBUG) + "(compile in debug mode for type information)"); +#else + "of type '" + type_id() + "''"); +#endif + } + } + + template ::value, int> = 0> + bool try_implicit_casts(handle, bool) { return false; } + + template ::value, int> = 0> + bool try_implicit_casts(handle src, bool convert) { + for (auto &cast : typeinfo->implicit_casts) { + copyable_holder_caster sub_caster(*cast.first); + if (sub_caster.load(src, convert)) { + value = cast.second(sub_caster.value); + holder = holder_type(sub_caster.holder, (type *) value); + return true; + } + } + return false; + } + + static bool try_direct_conversions(handle) { return false; } + + + holder_type holder; +}; + +/// Specialize for the common std::shared_ptr, so users don't need to +template +class type_caster> : public copyable_holder_caster> { }; + +template +struct move_only_holder_caster { + static_assert(std::is_base_of, type_caster>::value, + "Holder classes are only supported for custom types"); + + static handle cast(holder_type &&src, return_value_policy, handle) { + auto *ptr = holder_helper::get(src); + return type_caster_base::cast_holder(ptr, std::addressof(src)); + } + static constexpr auto name = type_caster_base::name; +}; + +template +class type_caster> + : public move_only_holder_caster> { }; + +template +using type_caster_holder = conditional_t::value, + copyable_holder_caster, + move_only_holder_caster>; + +template struct always_construct_holder { static constexpr bool value = Value; }; + +/// Create a specialization for custom holder types (silently ignores std::shared_ptr) +#define PYBIND11_DECLARE_HOLDER_TYPE(type, holder_type, ...) \ + namespace pybind11 { namespace detail { \ + template \ + struct always_construct_holder : always_construct_holder { }; \ + template \ + class type_caster::value>> \ + : public type_caster_holder { }; \ + }} + +// PYBIND11_DECLARE_HOLDER_TYPE holder types: +template struct is_holder_type : + std::is_base_of, detail::type_caster> {}; +// Specialization for always-supported unique_ptr holders: +template struct is_holder_type> : + std::true_type {}; + +template struct handle_type_name { static constexpr auto name = _(); }; +template <> struct handle_type_name { static constexpr auto name = _(PYBIND11_BYTES_NAME); }; +template <> struct handle_type_name { static constexpr auto name = _("*args"); }; +template <> struct handle_type_name { static constexpr auto name = _("**kwargs"); }; + +template +struct pyobject_caster { + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { value = src; return static_cast(value); } + + template ::value, int> = 0> + bool load(handle src, bool /* convert */) { + if (!isinstance(src)) + return false; + value = reinterpret_borrow(src); + return true; + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name); +}; + +template +class type_caster::value>> : public pyobject_caster { }; + +// Our conditions for enabling moving are quite restrictive: +// At compile time: +// - T needs to be a non-const, non-pointer, non-reference type +// - type_caster::operator T&() must exist +// - the type must be move constructible (obviously) +// At run-time: +// - if the type is non-copy-constructible, the object must be the sole owner of the type (i.e. it +// must have ref_count() == 1)h +// If any of the above are not satisfied, we fall back to copying. +template using move_is_plain_type = satisfies_none_of; +template struct move_always : std::false_type {}; +template struct move_always, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template struct move_if_unreferenced : std::false_type {}; +template struct move_if_unreferenced, + negation>, + std::is_move_constructible, + std::is_same>().operator T&()), T&> +>::value>> : std::true_type {}; +template using move_never = none_of, move_if_unreferenced>; + +// Detect whether returning a `type` from a cast on type's type_caster is going to result in a +// reference or pointer to a local variable of the type_caster. Basically, only +// non-reference/pointer `type`s and reference/pointers from a type_caster_generic are safe; +// everything else returns a reference/pointer to a local variable. +template using cast_is_temporary_value_reference = bool_constant< + (std::is_reference::value || std::is_pointer::value) && + !std::is_base_of>::value && + !std::is_same, void>::value +>; + +// When a value returned from a C++ function is being cast back to Python, we almost always want to +// force `policy = move`, regardless of the return value policy the function/method was declared +// with. +template struct return_value_policy_override { + static return_value_policy policy(return_value_policy p) { return p; } +}; + +template struct return_value_policy_override>::value, void>> { + static return_value_policy policy(return_value_policy p) { + return !std::is_lvalue_reference::value && + !std::is_pointer::value + ? return_value_policy::move : p; + } +}; + +// Basic python -> C++ casting; throws if casting fails +template type_caster &load_type(type_caster &conv, const handle &handle) { + if (!conv.load(handle, true)) { +#if defined(NDEBUG) + throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)"); +#else + throw cast_error("Unable to cast Python instance of type " + + (std::string) str(handle.get_type()) + " to C++ type '" + type_id() + "'"); +#endif + } + return conv; +} +// Wrapper around the above that also constructs and returns a type_caster +template make_caster load_type(const handle &handle) { + make_caster conv; + load_type(conv, handle); + return conv; +} + +NAMESPACE_END(detail) + +// pytype -> C++ type +template ::value, int> = 0> +T cast(const handle &handle) { + using namespace detail; + static_assert(!cast_is_temporary_value_reference::value, + "Unable to cast type to reference: value is local to type caster"); + return cast_op(load_type(handle)); +} + +// pytype -> pytype (calls converting constructor) +template ::value, int> = 0> +T cast(const handle &handle) { return T(reinterpret_borrow(handle)); } + +// C++ type -> py::object +template ::value, int> = 0> +object cast(const T &value, return_value_policy policy = return_value_policy::automatic_reference, + handle parent = handle()) { + if (policy == return_value_policy::automatic) + policy = std::is_pointer::value ? return_value_policy::take_ownership : return_value_policy::copy; + else if (policy == return_value_policy::automatic_reference) + policy = std::is_pointer::value ? return_value_policy::reference : return_value_policy::copy; + return reinterpret_steal(detail::make_caster::cast(value, policy, parent)); +} + +template T handle::cast() const { return pybind11::cast(*this); } +template <> inline void handle::cast() const { return; } + +template +detail::enable_if_t::value, T> move(object &&obj) { + if (obj.ref_count() > 1) +#if defined(NDEBUG) + throw cast_error("Unable to cast Python instance to C++ rvalue: instance has multiple references" + " (compile in debug mode for details)"); +#else + throw cast_error("Unable to move from Python " + (std::string) str(obj.get_type()) + + " instance to C++ " + type_id() + " instance: instance has multiple references"); +#endif + + // Move into a temporary and return that, because the reference may be a local value of `conv` + T ret = std::move(detail::load_type(obj).operator T&()); + return ret; +} + +// Calling cast() on an rvalue calls pybind::cast with the object rvalue, which does: +// - If we have to move (because T has no copy constructor), do it. This will fail if the moved +// object has multiple references, but trying to copy will fail to compile. +// - If both movable and copyable, check ref count: if 1, move; otherwise copy +// - Otherwise (not movable), copy. +template detail::enable_if_t::value, T> cast(object &&object) { + return move(std::move(object)); +} +template detail::enable_if_t::value, T> cast(object &&object) { + if (object.ref_count() > 1) + return cast(object); + else + return move(std::move(object)); +} +template detail::enable_if_t::value, T> cast(object &&object) { + return cast(object); +} + +template T object::cast() const & { return pybind11::cast(*this); } +template T object::cast() && { return pybind11::cast(std::move(*this)); } +template <> inline void object::cast() const & { return; } +template <> inline void object::cast() && { return; } + +NAMESPACE_BEGIN(detail) + +// Declared in pytypes.h: +template ::value, int>> +object object_or_cast(T &&o) { return pybind11::cast(std::forward(o)); } + +struct overload_unused {}; // Placeholder type for the unneeded (and dead code) static variable in the OVERLOAD_INT macro +template using overload_caster_t = conditional_t< + cast_is_temporary_value_reference::value, make_caster, overload_unused>; + +// Trampoline use: for reference/pointer types to value-converted values, we do a value cast, then +// store the result in the given variable. For other types, this is a no-op. +template enable_if_t::value, T> cast_ref(object &&o, make_caster &caster) { + return cast_op(load_type(caster, o)); +} +template enable_if_t::value, T> cast_ref(object &&, overload_unused &) { + pybind11_fail("Internal error: cast_ref fallback invoked"); } + +// Trampoline use: Having a pybind11::cast with an invalid reference type is going to static_assert, even +// though if it's in dead code, so we provide a "trampoline" to pybind11::cast that only does anything in +// cases where pybind11::cast is valid. +template enable_if_t::value, T> cast_safe(object &&o) { + return pybind11::cast(std::move(o)); } +template enable_if_t::value, T> cast_safe(object &&) { + pybind11_fail("Internal error: cast_safe fallback invoked"); } +template <> inline void cast_safe(object &&) {} + +NAMESPACE_END(detail) + +template +tuple make_tuple() { return tuple(0); } + +template tuple make_tuple(Args&&... args_) { + constexpr size_t size = sizeof...(Args); + std::array args { + { reinterpret_steal(detail::make_caster::cast( + std::forward(args_), policy, nullptr))... } + }; + for (size_t i = 0; i < args.size(); i++) { + if (!args[i]) { +#if defined(NDEBUG) + throw cast_error("make_tuple(): unable to convert arguments to Python object (compile in debug mode for details)"); +#else + std::array argtypes { {type_id()...} }; + throw cast_error("make_tuple(): unable to convert argument of type '" + + argtypes[i] + "' to Python object"); +#endif + } + } + tuple result(size); + int counter = 0; + for (auto &arg_value : args) + PyTuple_SET_ITEM(result.ptr(), counter++, arg_value.release().ptr()); + return result; +} + +/// \ingroup annotations +/// Annotation for arguments +struct arg { + /// Constructs an argument with the name of the argument; if null or omitted, this is a positional argument. + constexpr explicit arg(const char *name = nullptr) : name(name), flag_noconvert(false), flag_none(true) { } + /// Assign a value to this argument + template arg_v operator=(T &&value) const; + /// Indicate that the type should not be converted in the type caster + arg &noconvert(bool flag = true) { flag_noconvert = flag; return *this; } + /// Indicates that the argument should/shouldn't allow None (e.g. for nullable pointer args) + arg &none(bool flag = true) { flag_none = flag; return *this; } + + const char *name; ///< If non-null, this is a named kwargs argument + bool flag_noconvert : 1; ///< If set, do not allow conversion (requires a supporting type caster!) + bool flag_none : 1; ///< If set (the default), allow None to be passed to this argument +}; + +/// \ingroup annotations +/// Annotation for arguments with values +struct arg_v : arg { +private: + template + arg_v(arg &&base, T &&x, const char *descr = nullptr) + : arg(base), + value(reinterpret_steal( + detail::make_caster::cast(x, return_value_policy::automatic, {}) + )), + descr(descr) +#if !defined(NDEBUG) + , type(type_id()) +#endif + { } + +public: + /// Direct construction with name, default, and description + template + arg_v(const char *name, T &&x, const char *descr = nullptr) + : arg_v(arg(name), std::forward(x), descr) { } + + /// Called internally when invoking `py::arg("a") = value` + template + arg_v(const arg &base, T &&x, const char *descr = nullptr) + : arg_v(arg(base), std::forward(x), descr) { } + + /// Same as `arg::noconvert()`, but returns *this as arg_v&, not arg& + arg_v &noconvert(bool flag = true) { arg::noconvert(flag); return *this; } + + /// Same as `arg::nonone()`, but returns *this as arg_v&, not arg& + arg_v &none(bool flag = true) { arg::none(flag); return *this; } + + /// The default value + object value; + /// The (optional) description of the default value + const char *descr; +#if !defined(NDEBUG) + /// The C++ type name of the default value (only available when compiled in debug mode) + std::string type; +#endif +}; + +template +arg_v arg::operator=(T &&value) const { return {std::move(*this), std::forward(value)}; } + +/// Alias for backward compatibility -- to be removed in version 2.0 +template using arg_t = arg_v; + +inline namespace literals { +/** \rst + String literal version of `arg` + \endrst */ +constexpr arg operator"" _a(const char *name, size_t) { return arg(name); } +} + +NAMESPACE_BEGIN(detail) + +// forward declaration (definition in attr.h) +struct function_record; + +/// Internal data associated with a single function call +struct function_call { + function_call(const function_record &f, handle p); // Implementation in attr.h + + /// The function data: + const function_record &func; + + /// Arguments passed to the function: + std::vector args; + + /// The `convert` value the arguments should be loaded with + std::vector args_convert; + + /// Extra references for the optional `py::args` and/or `py::kwargs` arguments (which, if + /// present, are also in `args` but without a reference). + object args_ref, kwargs_ref; + + /// The parent, if any + handle parent; + + /// If this is a call to an initializer, this argument contains `self` + handle init_self; +}; + + +/// Helper class which loads arguments for C++ functions called from Python +template +class argument_loader { + using indices = make_index_sequence; + + template using argument_is_args = std::is_same, args>; + template using argument_is_kwargs = std::is_same, kwargs>; + // Get args/kwargs argument positions relative to the end of the argument list: + static constexpr auto args_pos = constexpr_first() - (int) sizeof...(Args), + kwargs_pos = constexpr_first() - (int) sizeof...(Args); + + static constexpr bool args_kwargs_are_last = kwargs_pos >= - 1 && args_pos >= kwargs_pos - 1; + + static_assert(args_kwargs_are_last, "py::args/py::kwargs are only permitted as the last argument(s) of a function"); + +public: + static constexpr bool has_kwargs = kwargs_pos < 0; + static constexpr bool has_args = args_pos < 0; + + static constexpr auto arg_names = concat(type_descr(make_caster::name)...); + + bool load_args(function_call &call) { + return load_impl_sequence(call, indices{}); + } + + template + enable_if_t::value, Return> call(Func &&f) && { + return std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); + } + + template + enable_if_t::value, void_type> call(Func &&f) && { + std::move(*this).template call_impl(std::forward(f), indices{}, Guard{}); + return void_type(); + } + +private: + + static bool load_impl_sequence(function_call &, index_sequence<>) { return true; } + + template + bool load_impl_sequence(function_call &call, index_sequence) { +#ifdef __cpp_fold_expressions + if ((... || !std::get(argcasters).load(call.args[Is], call.args_convert[Is]))) + return false; +#else + for (bool r : {std::get(argcasters).load(call.args[Is], call.args_convert[Is])...}) + if (!r) + return false; +#endif + return true; + } + + template + Return call_impl(Func &&f, index_sequence, Guard &&) && { + return std::forward(f)(cast_op(std::move(std::get(argcasters)))...); + } + + std::tuple...> argcasters; +}; + +/// Helper class which collects only positional arguments for a Python function call. +/// A fancier version below can collect any argument, but this one is optimal for simple calls. +template +class simple_collector { +public: + template + explicit simple_collector(Ts &&...values) + : m_args(pybind11::make_tuple(std::forward(values)...)) { } + + const tuple &args() const & { return m_args; } + dict kwargs() const { return {}; } + + tuple args() && { return std::move(m_args); } + + /// Call a Python function and pass the collected arguments + object call(PyObject *ptr) const { + PyObject *result = PyObject_CallObject(ptr, m_args.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); + } + +private: + tuple m_args; +}; + +/// Helper class which collects positional, keyword, * and ** arguments for a Python function call +template +class unpacking_collector { +public: + template + explicit unpacking_collector(Ts &&...values) { + // Tuples aren't (easily) resizable so a list is needed for collection, + // but the actual function call strictly requires a tuple. + auto args_list = list(); + int _[] = { 0, (process(args_list, std::forward(values)), 0)... }; + ignore_unused(_); + + m_args = std::move(args_list); + } + + const tuple &args() const & { return m_args; } + const dict &kwargs() const & { return m_kwargs; } + + tuple args() && { return std::move(m_args); } + dict kwargs() && { return std::move(m_kwargs); } + + /// Call a Python function and pass the collected arguments + object call(PyObject *ptr) const { + PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); + } + +private: + template + void process(list &args_list, T &&x) { + auto o = reinterpret_steal(detail::make_caster::cast(std::forward(x), policy, {})); + if (!o) { +#if defined(NDEBUG) + argument_cast_error(); +#else + argument_cast_error(std::to_string(args_list.size()), type_id()); +#endif + } + args_list.append(o); + } + + void process(list &args_list, detail::args_proxy ap) { + for (const auto &a : ap) + args_list.append(a); + } + + void process(list &/*args_list*/, arg_v a) { + if (!a.name) +#if defined(NDEBUG) + nameless_argument_error(); +#else + nameless_argument_error(a.type); +#endif + + if (m_kwargs.contains(a.name)) { +#if defined(NDEBUG) + multiple_values_error(); +#else + multiple_values_error(a.name); +#endif + } + if (!a.value) { +#if defined(NDEBUG) + argument_cast_error(); +#else + argument_cast_error(a.name, a.type); +#endif + } + m_kwargs[a.name] = a.value; + } + + void process(list &/*args_list*/, detail::kwargs_proxy kp) { + if (!kp) + return; + for (const auto &k : reinterpret_borrow(kp)) { + if (m_kwargs.contains(k.first)) { +#if defined(NDEBUG) + multiple_values_error(); +#else + multiple_values_error(str(k.first)); +#endif + } + m_kwargs[k.first] = k.second; + } + } + + [[noreturn]] static void nameless_argument_error() { + throw type_error("Got kwargs without a name; only named arguments " + "may be passed via py::arg() to a python function call. " + "(compile in debug mode for details)"); + } + [[noreturn]] static void nameless_argument_error(std::string type) { + throw type_error("Got kwargs without a name of type '" + type + "'; only named " + "arguments may be passed via py::arg() to a python function call. "); + } + [[noreturn]] static void multiple_values_error() { + throw type_error("Got multiple values for keyword argument " + "(compile in debug mode for details)"); + } + + [[noreturn]] static void multiple_values_error(std::string name) { + throw type_error("Got multiple values for keyword argument '" + name + "'"); + } + + [[noreturn]] static void argument_cast_error() { + throw cast_error("Unable to convert call argument to Python object " + "(compile in debug mode for details)"); + } + + [[noreturn]] static void argument_cast_error(std::string name, std::string type) { + throw cast_error("Unable to convert call argument '" + name + + "' of type '" + type + "' to Python object"); + } + +private: + tuple m_args; + dict m_kwargs; +}; + +/// Collect only positional arguments for a Python function call +template ...>::value>> +simple_collector collect_arguments(Args &&...args) { + return simple_collector(std::forward(args)...); +} + +/// Collect all arguments, including keywords and unpacking (only instantiated when needed) +template ...>::value>> +unpacking_collector collect_arguments(Args &&...args) { + // Following argument order rules for generalized unpacking according to PEP 448 + static_assert( + constexpr_last() < constexpr_first() + && constexpr_last() < constexpr_first(), + "Invalid function call: positional args must precede keywords and ** unpacking; " + "* unpacking must precede ** unpacking" + ); + return unpacking_collector(std::forward(args)...); +} + +template +template +object object_api::operator()(Args &&...args) const { + return detail::collect_arguments(std::forward(args)...).call(derived().ptr()); +} + +template +template +object object_api::call(Args &&...args) const { + return operator()(std::forward(args)...); +} + +NAMESPACE_END(detail) + +#define PYBIND11_MAKE_OPAQUE(...) \ + namespace pybind11 { namespace detail { \ + template<> class type_caster<__VA_ARGS__> : public type_caster_base<__VA_ARGS__> { }; \ + }} + +/// Lets you pass a type containing a `,` through a macro parameter without needing a separate +/// typedef, e.g.: `PYBIND11_OVERLOAD(PYBIND11_TYPE(ReturnType), PYBIND11_TYPE(Parent), f, arg)` +#define PYBIND11_TYPE(...) __VA_ARGS__ + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/chrono.h b/thirdparty/pybind11/include/pybind11/chrono.h new file mode 100644 index 000000000..ea777e696 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/chrono.h @@ -0,0 +1,184 @@ +/* + pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime + + Copyright (c) 2016 Trent Houliston and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include +#include +#include +#include + +// Backport the PyDateTime_DELTA functions from Python3.3 if required +#ifndef PyDateTime_DELTA_GET_DAYS +#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) +#endif +#ifndef PyDateTime_DELTA_GET_SECONDS +#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) +#endif +#ifndef PyDateTime_DELTA_GET_MICROSECONDS +#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template class duration_caster { +public: + typedef typename type::rep rep; + typedef typename type::period period; + + typedef std::chrono::duration> days; + + bool load(handle src, bool) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + if (!src) return false; + // If invoked with datetime.delta object + if (PyDelta_Check(src.ptr())) { + value = type(duration_cast>( + days(PyDateTime_DELTA_GET_DAYS(src.ptr())) + + seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr())) + + microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr())))); + return true; + } + // If invoked with a float we assume it is seconds and convert + else if (PyFloat_Check(src.ptr())) { + value = type(duration_cast>(duration(PyFloat_AsDouble(src.ptr())))); + return true; + } + else return false; + } + + // If this is a duration just return it back + static const std::chrono::duration& get_duration(const std::chrono::duration &src) { + return src; + } + + // If this is a time_point get the time_since_epoch + template static std::chrono::duration get_duration(const std::chrono::time_point> &src) { + return src.time_since_epoch(); + } + + static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) { + using namespace std::chrono; + + // Use overloaded function to get our duration from our source + // Works out if it is a duration or time_point and get the duration + auto d = get_duration(src); + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + // Declare these special duration types so the conversions happen with the correct primitive types (int) + using dd_t = duration>; + using ss_t = duration>; + using us_t = duration; + + auto dd = duration_cast(d); + auto subd = d - dd; + auto ss = duration_cast(subd); + auto us = duration_cast(subd - ss); + return PyDelta_FromDSU(dd.count(), ss.count(), us.count()); + } + + PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); +}; + +// This is for casting times on the system clock into datetime.datetime instances +template class type_caster> { +public: + typedef std::chrono::time_point type; + bool load(handle src, bool) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + if (!src) return false; + + std::tm cal; + microseconds msecs; + + if (PyDateTime_Check(src.ptr())) { + cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr()); + cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr()); + cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr()); + cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); + cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; + cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; + cal.tm_isdst = -1; + msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr())); + } else if (PyDate_Check(src.ptr())) { + cal.tm_sec = 0; + cal.tm_min = 0; + cal.tm_hour = 0; + cal.tm_mday = PyDateTime_GET_DAY(src.ptr()); + cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1; + cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900; + cal.tm_isdst = -1; + msecs = microseconds(0); + } else if (PyTime_Check(src.ptr())) { + cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr()); + cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr()); + cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr()); + cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70) + cal.tm_mon = 0; // represents 1-Jan-1970, which is the first + cal.tm_year = 70; // earliest available date for Python's datetime + cal.tm_isdst = -1; + msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr())); + } + else return false; + + value = system_clock::from_time_t(std::mktime(&cal)) + msecs; + return true; + } + + static handle cast(const std::chrono::time_point &src, return_value_policy /* policy */, handle /* parent */) { + using namespace std::chrono; + + // Lazy initialise the PyDateTime import + if (!PyDateTimeAPI) { PyDateTime_IMPORT; } + + std::time_t tt = system_clock::to_time_t(time_point_cast(src)); + // this function uses static memory so it's best to copy it out asap just in case + // otherwise other code that is using localtime may break this (not just python code) + std::tm localtime = *std::localtime(&tt); + + // Declare these special duration types so the conversions happen with the correct primitive types (int) + using us_t = duration; + + return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, + localtime.tm_mon + 1, + localtime.tm_mday, + localtime.tm_hour, + localtime.tm_min, + localtime.tm_sec, + (duration_cast(src.time_since_epoch() % seconds(1))).count()); + } + PYBIND11_TYPE_CASTER(type, _("datetime.datetime")); +}; + +// Other clocks that are not the system clock are not measured as datetime.datetime objects +// since they are not measured on calendar time. So instead we just make them timedeltas +// Or if they have passed us a time as a float we convert that +template class type_caster> +: public duration_caster> { +}; + +template class type_caster> +: public duration_caster> { +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/common.h b/thirdparty/pybind11/include/pybind11/common.h new file mode 100644 index 000000000..6c8a4f1e8 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/common.h @@ -0,0 +1,2 @@ +#include "detail/common.h" +#warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'." diff --git a/thirdparty/pybind11/include/pybind11/complex.h b/thirdparty/pybind11/include/pybind11/complex.h new file mode 100644 index 000000000..3f8963857 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/complex.h @@ -0,0 +1,65 @@ +/* + pybind11/complex.h: Complex number support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include + +/// glibc defines I as a macro which breaks things, e.g., boost template names +#ifdef I +# undef I +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +template struct format_descriptor, detail::enable_if_t::value>> { + static constexpr const char c = format_descriptor::c; + static constexpr const char value[3] = { 'Z', c, '\0' }; + static std::string format() { return std::string(value); } +}; + +#ifndef PYBIND11_CPP17 + +template constexpr const char format_descriptor< + std::complex, detail::enable_if_t::value>>::value[3]; + +#endif + +NAMESPACE_BEGIN(detail) + +template struct is_fmt_numeric, detail::enable_if_t::value>> { + static constexpr bool value = true; + static constexpr int index = is_fmt_numeric::index + 3; +}; + +template class type_caster> { +public: + bool load(handle src, bool convert) { + if (!src) + return false; + if (!convert && !PyComplex_Check(src.ptr())) + return false; + Py_complex result = PyComplex_AsCComplex(src.ptr()); + if (result.real == -1.0 && PyErr_Occurred()) { + PyErr_Clear(); + return false; + } + value = std::complex((T) result.real, (T) result.imag); + return true; + } + + static handle cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { + return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); + } + + PYBIND11_TYPE_CASTER(std::complex, _("complex")); +}; +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/detail/class.h b/thirdparty/pybind11/include/pybind11/detail/class.h new file mode 100644 index 000000000..edfa7de68 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/detail/class.h @@ -0,0 +1,639 @@ +/* + pybind11/detail/class.h: Python C API implementation details for py::class_ + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "../attr.h" +#include "../options.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +#if PY_VERSION_HEX >= 0x03030000 +# define PYBIND11_BUILTIN_QUALNAME +# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) +#else +// In pre-3.3 Python, we still set __qualname__ so that we can produce reliable function type +// signatures; in 3.3+ this macro expands to nothing: +# define PYBIND11_SET_OLDPY_QUALNAME(obj, nameobj) setattr((PyObject *) obj, "__qualname__", nameobj) +#endif + +inline PyTypeObject *type_incref(PyTypeObject *type) { + Py_INCREF(type); + return type; +} + +#if !defined(PYPY_VERSION) + +/// `pybind11_static_property.__get__()`: Always pass the class instead of the instance. +extern "C" inline PyObject *pybind11_static_get(PyObject *self, PyObject * /*ob*/, PyObject *cls) { + return PyProperty_Type.tp_descr_get(self, cls, cls); +} + +/// `pybind11_static_property.__set__()`: Just like the above `__get__()`. +extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObject *value) { + PyObject *cls = PyType_Check(obj) ? obj : (PyObject *) Py_TYPE(obj); + return PyProperty_Type.tp_descr_set(self, cls, value); +} + +/** A `static_property` is the same as a `property` but the `__get__()` and `__set__()` + methods are modified to always use the object type instead of a concrete instance. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + constexpr auto *name = "pybind11_static_property"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_static_property_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyProperty_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + type->tp_descr_get = pybind11_static_get; + type->tp_descr_set = pybind11_static_set; + + if (PyType_Ready(type) < 0) + pybind11_fail("make_static_property_type(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + return type; +} + +#else // PYPY + +/** PyPy has some issues with the above C API, so we evaluate Python code instead. + This function will only be called once so performance isn't really a concern. + Return value: New reference. */ +inline PyTypeObject *make_static_property_type() { + auto d = dict(); + PyObject *result = PyRun_String(R"(\ + class pybind11_static_property(property): + def __get__(self, obj, cls): + return property.__get__(self, cls, cls) + + def __set__(self, obj, value): + cls = obj if isinstance(obj, type) else type(obj) + property.__set__(self, cls, value) + )", Py_file_input, d.ptr(), d.ptr() + ); + if (result == nullptr) + throw error_already_set(); + Py_DECREF(result); + return (PyTypeObject *) d["pybind11_static_property"].cast().release().ptr(); +} + +#endif // PYPY + +/** Types with static properties need to handle `Type.static_prop = x` in a specific way. + By default, Python replaces the `static_property` itself, but for wrapped C++ types + we need to call `static_property.__set__()` in order to propagate the new value to + the underlying C++ data structure. */ +extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { + // Use `_PyType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw + // descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`). + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + + // The following assignment combinations are possible: + // 1. `Type.static_prop = value` --> descr_set: `Type.static_prop.__set__(value)` + // 2. `Type.static_prop = other_static_prop` --> setattro: replace existing `static_prop` + // 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment + const auto static_prop = (PyObject *) get_internals().static_property_type; + const auto call_descr_set = descr && PyObject_IsInstance(descr, static_prop) + && !PyObject_IsInstance(value, static_prop); + if (call_descr_set) { + // Call `static_property.__set__()` instead of replacing the `static_property`. +#if !defined(PYPY_VERSION) + return Py_TYPE(descr)->tp_descr_set(descr, obj, value); +#else + if (PyObject *result = PyObject_CallMethod(descr, "__set__", "OO", obj, value)) { + Py_DECREF(result); + return 0; + } else { + return -1; + } +#endif + } else { + // Replace existing attribute. + return PyType_Type.tp_setattro(obj, name, value); + } +} + +#if PY_MAJOR_VERSION >= 3 +/** + * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing + * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, + * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here + * to do a special case bypass for PyInstanceMethod_Types. + */ +extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { + PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); + if (descr && PyInstanceMethod_Check(descr)) { + Py_INCREF(descr); + return descr; + } + else { + return PyType_Type.tp_getattro(obj, name); + } +} +#endif + +/** This metaclass is assigned by default to all pybind11 types and is required in order + for static properties to function correctly. Users may override this using `py::metaclass`. + Return value: New reference. */ +inline PyTypeObject* make_default_metaclass() { + constexpr auto *name = "pybind11_type"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + if (!heap_type) + pybind11_fail("make_default_metaclass(): error allocating metaclass!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyType_Type); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_setattro = pybind11_meta_setattro; +#if PY_MAJOR_VERSION >= 3 + type->tp_getattro = pybind11_meta_getattro; +#endif + + if (PyType_Ready(type) < 0) + pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!"); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + return type; +} + +/// For multiple inheritance types we need to recursively register/deregister base pointers for any +/// base classes with pointers that are difference from the instance value pointer so that we can +/// correctly recognize an offset base class pointer. This calls a function with any offset base ptrs. +inline void traverse_offset_bases(void *valueptr, const detail::type_info *tinfo, instance *self, + bool (*f)(void * /*parentptr*/, instance * /*self*/)) { + for (handle h : reinterpret_borrow(tinfo->type->tp_bases)) { + if (auto parent_tinfo = get_type_info((PyTypeObject *) h.ptr())) { + for (auto &c : parent_tinfo->implicit_casts) { + if (c.first == tinfo->cpptype) { + auto *parentptr = c.second(valueptr); + if (parentptr != valueptr) + f(parentptr, self); + traverse_offset_bases(parentptr, parent_tinfo, self, f); + break; + } + } + } + } +} + +inline bool register_instance_impl(void *ptr, instance *self) { + get_internals().registered_instances.emplace(ptr, self); + return true; // unused, but gives the same signature as the deregister func +} +inline bool deregister_instance_impl(void *ptr, instance *self) { + auto ®istered_instances = get_internals().registered_instances; + auto range = registered_instances.equal_range(ptr); + for (auto it = range.first; it != range.second; ++it) { + if (Py_TYPE(self) == Py_TYPE(it->second)) { + registered_instances.erase(it); + return true; + } + } + return false; +} + +inline void register_instance(instance *self, void *valptr, const type_info *tinfo) { + register_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, register_instance_impl); +} + +inline bool deregister_instance(instance *self, void *valptr, const type_info *tinfo) { + bool ret = deregister_instance_impl(valptr, self); + if (!tinfo->simple_ancestors) + traverse_offset_bases(valptr, tinfo, self, deregister_instance_impl); + return ret; +} + +/// Instance creation function for all pybind11 types. It allocates the internal instance layout for +/// holding C++ objects and holders. Allocation is done lazily (the first time the instance is cast +/// to a reference or pointer), and initialization is done by an `__init__` function. +inline PyObject *make_new_instance(PyTypeObject *type) { +#if defined(PYPY_VERSION) + // PyPy gets tp_basicsize wrong (issue 2482) under multiple inheritance when the first inherited + // object is a a plain Python type (i.e. not derived from an extension type). Fix it. + ssize_t instance_size = static_cast(sizeof(instance)); + if (type->tp_basicsize < instance_size) { + type->tp_basicsize = instance_size; + } +#endif + PyObject *self = type->tp_alloc(type, 0); + auto inst = reinterpret_cast(self); + // Allocate the value/holder internals: + inst->allocate_layout(); + + inst->owned = true; + + return self; +} + +/// Instance creation function for all pybind11 types. It only allocates space for the +/// C++ object, but doesn't call the constructor -- an `__init__` function must do that. +extern "C" inline PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *) { + return make_new_instance(type); +} + +/// An `__init__` function constructs the C++ object. Users should provide at least one +/// of these using `py::init` or directly with `.def(__init__, ...)`. Otherwise, the +/// following default function will be used which simply throws an exception. +extern "C" inline int pybind11_object_init(PyObject *self, PyObject *, PyObject *) { + PyTypeObject *type = Py_TYPE(self); + std::string msg; +#if defined(PYPY_VERSION) + msg += handle((PyObject *) type).attr("__module__").cast() + "."; +#endif + msg += type->tp_name; + msg += ": No constructor defined!"; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return -1; +} + +inline void add_patient(PyObject *nurse, PyObject *patient) { + auto &internals = get_internals(); + auto instance = reinterpret_cast(nurse); + instance->has_patients = true; + Py_INCREF(patient); + internals.patients[nurse].push_back(patient); +} + +inline void clear_patients(PyObject *self) { + auto instance = reinterpret_cast(self); + auto &internals = get_internals(); + auto pos = internals.patients.find(self); + assert(pos != internals.patients.end()); + // Clearing the patients can cause more Python code to run, which + // can invalidate the iterator. Extract the vector of patients + // from the unordered_map first. + auto patients = std::move(pos->second); + internals.patients.erase(pos); + instance->has_patients = false; + for (PyObject *&patient : patients) + Py_CLEAR(patient); +} + +/// Clears all internal data from the instance and removes it from registered instances in +/// preparation for deallocation. +inline void clear_instance(PyObject *self) { + auto instance = reinterpret_cast(self); + + // Deallocate any values/holders, if present: + for (auto &v_h : values_and_holders(instance)) { + if (v_h) { + + // We have to deregister before we call dealloc because, for virtual MI types, we still + // need to be able to get the parent pointers. + if (v_h.instance_registered() && !deregister_instance(instance, v_h.value_ptr(), v_h.type)) + pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!"); + + if (instance->owned || v_h.holder_constructed()) + v_h.type->dealloc(v_h); + } + } + // Deallocate the value/holder layout internals: + instance->deallocate_layout(); + + if (instance->weakrefs) + PyObject_ClearWeakRefs(self); + + PyObject **dict_ptr = _PyObject_GetDictPtr(self); + if (dict_ptr) + Py_CLEAR(*dict_ptr); + + if (instance->has_patients) + clear_patients(self); +} + +/// Instance destructor function for all pybind11 types. It calls `type_info.dealloc` +/// to destroy the C++ object itself, while the rest is Python bookkeeping. +extern "C" inline void pybind11_object_dealloc(PyObject *self) { + clear_instance(self); + + auto type = Py_TYPE(self); + type->tp_free(self); + +#if PY_VERSION_HEX < 0x03080000 + // `type->tp_dealloc != pybind11_object_dealloc` means that we're being called + // as part of a derived type's dealloc, in which case we're not allowed to decref + // the type here. For cross-module compatibility, we shouldn't compare directly + // with `pybind11_object_dealloc`, but with the common one stashed in internals. + auto pybind11_object_type = (PyTypeObject *) get_internals().instance_base; + if (type->tp_dealloc == pybind11_object_type->tp_dealloc) + Py_DECREF(type); +#else + // This was not needed before Python 3.8 (Python issue 35810) + // https://github.com/pybind/pybind11/issues/1946 + Py_DECREF(type); +#endif +} + +/** Create the type which can be used as a common base for all classes. This is + needed in order to satisfy Python's requirements for multiple inheritance. + Return value: New reference. */ +inline PyObject *make_object_base_type(PyTypeObject *metaclass) { + constexpr auto *name = "pybind11_object"; + auto name_obj = reinterpret_steal(PYBIND11_FROM_STRING(name)); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail("make_object_base_type(): error allocating type!"); + + heap_type->ht_name = name_obj.inc_ref().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = name_obj.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = name; + type->tp_base = type_incref(&PyBaseObject_Type); + type->tp_basicsize = static_cast(sizeof(instance)); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; + + type->tp_new = pybind11_object_new; + type->tp_init = pybind11_object_init; + type->tp_dealloc = pybind11_object_dealloc; + + /* Support weak references (needed for the keep_alive feature) */ + type->tp_weaklistoffset = offsetof(instance, weakrefs); + + if (PyType_Ready(type) < 0) + pybind11_fail("PyType_Ready failed in make_object_base_type():" + error_string()); + + setattr((PyObject *) type, "__module__", str("pybind11_builtins")); + PYBIND11_SET_OLDPY_QUALNAME(type, name_obj); + + assert(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + return (PyObject *) heap_type; +} + +/// dynamic_attr: Support for `d = instance.__dict__`. +extern "C" inline PyObject *pybind11_get_dict(PyObject *self, void *) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + if (!dict) + dict = PyDict_New(); + Py_XINCREF(dict); + return dict; +} + +/// dynamic_attr: Support for `instance.__dict__ = dict()`. +extern "C" inline int pybind11_set_dict(PyObject *self, PyObject *new_dict, void *) { + if (!PyDict_Check(new_dict)) { + PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, not a '%.200s'", + Py_TYPE(new_dict)->tp_name); + return -1; + } + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_INCREF(new_dict); + Py_CLEAR(dict); + dict = new_dict; + return 0; +} + +/// dynamic_attr: Allow the garbage collector to traverse the internal instance `__dict__`. +extern "C" inline int pybind11_traverse(PyObject *self, visitproc visit, void *arg) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_VISIT(dict); + return 0; +} + +/// dynamic_attr: Allow the GC to clear the dictionary. +extern "C" inline int pybind11_clear(PyObject *self) { + PyObject *&dict = *_PyObject_GetDictPtr(self); + Py_CLEAR(dict); + return 0; +} + +/// Give instances of this type a `__dict__` and opt into garbage collection. +inline void enable_dynamic_attributes(PyHeapTypeObject *heap_type) { + auto type = &heap_type->ht_type; +#if defined(PYPY_VERSION) + pybind11_fail(std::string(type->tp_name) + ": dynamic attributes are " + "currently not supported in " + "conjunction with PyPy!"); +#endif + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + type->tp_dictoffset = type->tp_basicsize; // place dict at the end + type->tp_basicsize += (ssize_t)sizeof(PyObject *); // and allocate enough space for it + type->tp_traverse = pybind11_traverse; + type->tp_clear = pybind11_clear; + + static PyGetSetDef getset[] = { + {const_cast("__dict__"), pybind11_get_dict, pybind11_set_dict, nullptr, nullptr}, + {nullptr, nullptr, nullptr, nullptr, nullptr} + }; + type->tp_getset = getset; +} + +/// buffer_protocol: Fill in the view as specified by flags. +extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int flags) { + // Look for a `get_buffer` implementation in this type's info or any bases (following MRO). + type_info *tinfo = nullptr; + for (auto type : reinterpret_borrow(Py_TYPE(obj)->tp_mro)) { + tinfo = get_type_info((PyTypeObject *) type.ptr()); + if (tinfo && tinfo->get_buffer) + break; + } + if (view == nullptr || !tinfo || !tinfo->get_buffer) { + if (view) + view->obj = nullptr; + PyErr_SetString(PyExc_BufferError, "pybind11_getbuffer(): Internal error"); + return -1; + } + std::memset(view, 0, sizeof(Py_buffer)); + buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); + view->obj = obj; + view->ndim = 1; + view->internal = info; + view->buf = info->ptr; + view->itemsize = info->itemsize; + view->len = view->itemsize; + for (auto s : info->shape) + view->len *= s; + view->readonly = info->readonly; + if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) { + if (view) + view->obj = nullptr; + PyErr_SetString(PyExc_BufferError, "Writable buffer requested for readonly storage"); + return -1; + } + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) + view->format = const_cast(info->format.c_str()); + if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { + view->ndim = (int) info->ndim; + view->strides = &info->strides[0]; + view->shape = &info->shape[0]; + } + Py_INCREF(view->obj); + return 0; +} + +/// buffer_protocol: Release the resources of the buffer. +extern "C" inline void pybind11_releasebuffer(PyObject *, Py_buffer *view) { + delete (buffer_info *) view->internal; +} + +/// Give this type a buffer interface. +inline void enable_buffer_protocol(PyHeapTypeObject *heap_type) { + heap_type->ht_type.tp_as_buffer = &heap_type->as_buffer; +#if PY_MAJOR_VERSION < 3 + heap_type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; +#endif + + heap_type->as_buffer.bf_getbuffer = pybind11_getbuffer; + heap_type->as_buffer.bf_releasebuffer = pybind11_releasebuffer; +} + +/** Create a brand new Python type according to the `type_record` specification. + Return value: New reference. */ +inline PyObject* make_new_python_type(const type_record &rec) { + auto name = reinterpret_steal(PYBIND11_FROM_STRING(rec.name)); + + auto qualname = name; + if (rec.scope && !PyModule_Check(rec.scope.ptr()) && hasattr(rec.scope, "__qualname__")) { +#if PY_MAJOR_VERSION >= 3 + qualname = reinterpret_steal( + PyUnicode_FromFormat("%U.%U", rec.scope.attr("__qualname__").ptr(), name.ptr())); +#else + qualname = str(rec.scope.attr("__qualname__").cast() + "." + rec.name); +#endif + } + + object module; + if (rec.scope) { + if (hasattr(rec.scope, "__module__")) + module = rec.scope.attr("__module__"); + else if (hasattr(rec.scope, "__name__")) + module = rec.scope.attr("__name__"); + } + + auto full_name = c_str( +#if !defined(PYPY_VERSION) + module ? str(module).cast() + "." + rec.name : +#endif + rec.name); + + char *tp_doc = nullptr; + if (rec.doc && options::show_user_defined_docstrings()) { + /* Allocate memory for docstring (using PyObject_MALLOC, since + Python will free this later on) */ + size_t size = strlen(rec.doc) + 1; + tp_doc = (char *) PyObject_MALLOC(size); + memcpy((void *) tp_doc, rec.doc, size); + } + + auto &internals = get_internals(); + auto bases = tuple(rec.bases); + auto base = (bases.size() == 0) ? internals.instance_base + : bases[0].ptr(); + + /* Danger zone: from now (and until PyType_Ready), make sure to + issue no Python C API calls which could potentially invoke the + garbage collector (the GC will call type_traverse(), which will in + turn find the newly constructed type in an invalid state) */ + auto metaclass = rec.metaclass.ptr() ? (PyTypeObject *) rec.metaclass.ptr() + : internals.default_metaclass; + + auto heap_type = (PyHeapTypeObject *) metaclass->tp_alloc(metaclass, 0); + if (!heap_type) + pybind11_fail(std::string(rec.name) + ": Unable to create type object!"); + + heap_type->ht_name = name.release().ptr(); +#ifdef PYBIND11_BUILTIN_QUALNAME + heap_type->ht_qualname = qualname.inc_ref().ptr(); +#endif + + auto type = &heap_type->ht_type; + type->tp_name = full_name; + type->tp_doc = tp_doc; + type->tp_base = type_incref((PyTypeObject *)base); + type->tp_basicsize = static_cast(sizeof(instance)); + if (bases.size() > 0) + type->tp_bases = bases.release().ptr(); + + /* Don't inherit base __init__ */ + type->tp_init = pybind11_object_init; + + /* Supported protocols */ + type->tp_as_number = &heap_type->as_number; + type->tp_as_sequence = &heap_type->as_sequence; + type->tp_as_mapping = &heap_type->as_mapping; +#if PY_VERSION_HEX >= 0x03050000 + type->tp_as_async = &heap_type->as_async; +#endif + + /* Flags */ + type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; +#if PY_MAJOR_VERSION < 3 + type->tp_flags |= Py_TPFLAGS_CHECKTYPES; +#endif + + if (rec.dynamic_attr) + enable_dynamic_attributes(heap_type); + + if (rec.buffer_protocol) + enable_buffer_protocol(heap_type); + + if (PyType_Ready(type) < 0) + pybind11_fail(std::string(rec.name) + ": PyType_Ready failed (" + error_string() + ")!"); + + assert(rec.dynamic_attr ? PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC) + : !PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)); + + /* Register type with the parent scope */ + if (rec.scope) + setattr(rec.scope, rec.name, (PyObject *) type); + else + Py_INCREF(type); // Keep it alive forever (reference leak) + + if (module) // Needed by pydoc + setattr((PyObject *) type, "__module__", module); + + PYBIND11_SET_OLDPY_QUALNAME(type, qualname); + + return (PyObject *) type; +} + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/detail/common.h b/thirdparty/pybind11/include/pybind11/detail/common.h new file mode 100644 index 000000000..e53f502d6 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/detail/common.h @@ -0,0 +1,820 @@ +/* + pybind11/detail/common.h -- Basic macros + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#if !defined(NAMESPACE_BEGIN) +# define NAMESPACE_BEGIN(name) namespace name { +#endif +#if !defined(NAMESPACE_END) +# define NAMESPACE_END(name) } +#endif + +// Robust support for some features and loading modules compiled against different pybind versions +// requires forcing hidden visibility on pybind code, so we enforce this by setting the attribute on +// the main `pybind11` namespace. +#if !defined(PYBIND11_NAMESPACE) +# ifdef __GNUG__ +# define PYBIND11_NAMESPACE pybind11 __attribute__((visibility("hidden"))) +# else +# define PYBIND11_NAMESPACE pybind11 +# endif +#endif + +#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) +# if __cplusplus >= 201402L +# define PYBIND11_CPP14 +# if __cplusplus >= 201703L +# define PYBIND11_CPP17 +# endif +# endif +#elif defined(_MSC_VER) && __cplusplus == 199711L +// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) +// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer +# if _MSVC_LANG >= 201402L +# define PYBIND11_CPP14 +# if _MSVC_LANG > 201402L && _MSC_VER >= 1910 +# define PYBIND11_CPP17 +# endif +# endif +#endif + +// Compiler version assertions +#if defined(__INTEL_COMPILER) +# if __INTEL_COMPILER < 1700 +# error pybind11 requires Intel C++ compiler v17 or newer +# endif +#elif defined(__clang__) && !defined(__apple_build_version__) +# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 3) +# error pybind11 requires clang 3.3 or newer +# endif +#elif defined(__clang__) +// Apple changes clang version macros to its Xcode version; the first Xcode release based on +// (upstream) clang 3.3 was Xcode 5: +# if __clang_major__ < 5 +# error pybind11 requires Xcode/clang 5.0 or newer +# endif +#elif defined(__GNUG__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) +# error pybind11 requires gcc 4.8 or newer +# endif +#elif defined(_MSC_VER) +// Pybind hits various compiler bugs in 2015u2 and earlier, and also makes use of some stl features +// (e.g. std::negation) added in 2015u3: +# if _MSC_FULL_VER < 190024210 +# error pybind11 requires MSVC 2015 update 3 or newer +# endif +#endif + +#if !defined(PYBIND11_EXPORT) +# if defined(WIN32) || defined(_WIN32) +# define PYBIND11_EXPORT __declspec(dllexport) +# else +# define PYBIND11_EXPORT __attribute__ ((visibility("default"))) +# endif +#endif + +#if defined(_MSC_VER) +# define PYBIND11_NOINLINE __declspec(noinline) +#else +# define PYBIND11_NOINLINE __attribute__ ((noinline)) +#endif + +#if defined(PYBIND11_CPP14) +# define PYBIND11_DEPRECATED(reason) [[deprecated(reason)]] +#else +# define PYBIND11_DEPRECATED(reason) __attribute__((deprecated(reason))) +#endif + +#define PYBIND11_VERSION_MAJOR 2 +#define PYBIND11_VERSION_MINOR 5 +#define PYBIND11_VERSION_PATCH 0 + +/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode +#if defined(_MSC_VER) +# if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 4) +# define HAVE_ROUND 1 +# endif +# pragma warning(push) +# pragma warning(disable: 4510 4610 4512 4005) +# if defined(_DEBUG) && !defined(Py_DEBUG) +# define PYBIND11_DEBUG_MARKER +# undef _DEBUG +# endif +#endif + +#include +#include +#include + +/* Python #defines overrides on all sorts of core functions, which + tends to weak havok in C++ codebases that expect these to work + like regular functions (potentially with several overloads) */ +#if defined(isalnum) +# undef isalnum +# undef isalpha +# undef islower +# undef isspace +# undef isupper +# undef tolower +# undef toupper +#endif + +#if defined(copysign) +# undef copysign +#endif + +#if defined(_MSC_VER) +# if defined(PYBIND11_DEBUG_MARKER) +# define _DEBUG +# undef PYBIND11_DEBUG_MARKER +# endif +# pragma warning(pop) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) +#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyBytes_Check +#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyBytes_AsString +#define PYBIND11_BYTES_SIZE PyBytes_Size +#define PYBIND11_LONG_CHECK(o) PyLong_Check(o) +#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) +#define PYBIND11_LONG_FROM_SIGNED(o) PyLong_FromSsize_t((ssize_t) o) +#define PYBIND11_LONG_FROM_UNSIGNED(o) PyLong_FromSize_t((size_t) o) +#define PYBIND11_BYTES_NAME "bytes" +#define PYBIND11_STRING_NAME "str" +#define PYBIND11_SLICE_OBJECT PyObject +#define PYBIND11_FROM_STRING PyUnicode_FromString +#define PYBIND11_STR_TYPE ::pybind11::str +#define PYBIND11_BOOL_ATTR "__bool__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool) +// Providing a separate declaration to make Clang's -Wmissing-prototypes happy +#define PYBIND11_PLUGIN_IMPL(name) \ + extern "C" PYBIND11_EXPORT PyObject *PyInit_##name(); \ + extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() + +#else +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) +#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check +#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION +#define PYBIND11_BYTES_CHECK PyString_Check +#define PYBIND11_BYTES_FROM_STRING PyString_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyString_AsString +#define PYBIND11_BYTES_SIZE PyString_Size +#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) +#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) +#define PYBIND11_LONG_FROM_SIGNED(o) PyInt_FromSsize_t((ssize_t) o) // Returns long if needed. +#define PYBIND11_LONG_FROM_UNSIGNED(o) PyInt_FromSize_t((size_t) o) // Returns long if needed. +#define PYBIND11_BYTES_NAME "str" +#define PYBIND11_STRING_NAME "unicode" +#define PYBIND11_SLICE_OBJECT PySliceObject +#define PYBIND11_FROM_STRING PyString_FromString +#define PYBIND11_STR_TYPE ::pybind11::bytes +#define PYBIND11_BOOL_ATTR "__nonzero__" +#define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero) +// Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy +#define PYBIND11_PLUGIN_IMPL(name) \ + static PyObject *pybind11_init_wrapper(); \ + extern "C" PYBIND11_EXPORT void init##name(); \ + extern "C" PYBIND11_EXPORT void init##name() { \ + (void)pybind11_init_wrapper(); \ + } \ + PyObject *pybind11_init_wrapper() +#endif + +#if PY_VERSION_HEX >= 0x03050000 && PY_VERSION_HEX < 0x03050200 +extern "C" { + struct _Py_atomic_address { void *value; }; + PyAPI_DATA(_Py_atomic_address) _PyThreadState_Current; +} +#endif + +#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code +#define PYBIND11_STRINGIFY(x) #x +#define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x) +#define PYBIND11_CONCAT(first, second) first##second +#define PYBIND11_ENSURE_INTERNALS_READY \ + pybind11::detail::get_internals(); + +#define PYBIND11_CHECK_PYTHON_VERSION \ + { \ + const char *compiled_ver = PYBIND11_TOSTRING(PY_MAJOR_VERSION) \ + "." PYBIND11_TOSTRING(PY_MINOR_VERSION); \ + const char *runtime_ver = Py_GetVersion(); \ + size_t len = std::strlen(compiled_ver); \ + if (std::strncmp(runtime_ver, compiled_ver, len) != 0 \ + || (runtime_ver[len] >= '0' && runtime_ver[len] <= '9')) { \ + PyErr_Format(PyExc_ImportError, \ + "Python version mismatch: module was compiled for Python %s, " \ + "but the interpreter version is incompatible: %s.", \ + compiled_ver, runtime_ver); \ + return nullptr; \ + } \ + } + +#define PYBIND11_CATCH_INIT_EXCEPTIONS \ + catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + +/** \rst + ***Deprecated in favor of PYBIND11_MODULE*** + + This macro creates the entry point that will be invoked when the Python interpreter + imports a plugin library. Please create a `module` in the function body and return + the pointer to its underlying Python object at the end. + + .. code-block:: cpp + + PYBIND11_PLUGIN(example) { + pybind11::module m("example", "pybind11 example plugin"); + /// Set up bindings here + return m.ptr(); + } +\endrst */ +#define PYBIND11_PLUGIN(name) \ + PYBIND11_DEPRECATED("PYBIND11_PLUGIN is deprecated, use PYBIND11_MODULE") \ + static PyObject *pybind11_init(); \ + PYBIND11_PLUGIN_IMPL(name) { \ + PYBIND11_CHECK_PYTHON_VERSION \ + PYBIND11_ENSURE_INTERNALS_READY \ + try { \ + return pybind11_init(); \ + } PYBIND11_CATCH_INIT_EXCEPTIONS \ + } \ + PyObject *pybind11_init() + +/** \rst + This macro creates the entry point that will be invoked when the Python interpreter + imports an extension module. The module name is given as the fist argument and it + should not be in quotes. The second macro argument defines a variable of type + `py::module` which can be used to initialize the module. + + .. code-block:: cpp + + PYBIND11_MODULE(example, m) { + m.doc() = "pybind11 example module"; + + // Add bindings here + m.def("foo", []() { + return "Hello, World!"; + }); + } +\endrst */ +#define PYBIND11_MODULE(name, variable) \ + static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ + PYBIND11_PLUGIN_IMPL(name) { \ + PYBIND11_CHECK_PYTHON_VERSION \ + PYBIND11_ENSURE_INTERNALS_READY \ + auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ + try { \ + PYBIND11_CONCAT(pybind11_init_, name)(m); \ + return m.ptr(); \ + } PYBIND11_CATCH_INIT_EXCEPTIONS \ + } \ + void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) + + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +using ssize_t = Py_ssize_t; +using size_t = std::size_t; + +/// Approach used to cast a previously unknown C++ instance into a Python object +enum class return_value_policy : uint8_t { + /** This is the default return value policy, which falls back to the policy + return_value_policy::take_ownership when the return value is a pointer. + Otherwise, it uses return_value::move or return_value::copy for rvalue + and lvalue references, respectively. See below for a description of what + all of these different policies do. */ + automatic = 0, + + /** As above, but use policy return_value_policy::reference when the return + value is a pointer. This is the default conversion policy for function + arguments when calling Python functions manually from C++ code (i.e. via + handle::operator()). You probably won't need to use this. */ + automatic_reference, + + /** Reference an existing object (i.e. do not create a new copy) and take + ownership. Python will call the destructor and delete operator when the + object’s reference count reaches zero. Undefined behavior ensues when + the C++ side does the same.. */ + take_ownership, + + /** Create a new copy of the returned object, which will be owned by + Python. This policy is comparably safe because the lifetimes of the two + instances are decoupled. */ + copy, + + /** Use std::move to move the return value contents into a new instance + that will be owned by Python. This policy is comparably safe because the + lifetimes of the two instances (move source and destination) are + decoupled. */ + move, + + /** Reference an existing object, but do not take ownership. The C++ side + is responsible for managing the object’s lifetime and deallocating it + when it is no longer used. Warning: undefined behavior will ensue when + the C++ side deletes an object that is still referenced and used by + Python. */ + reference, + + /** This policy only applies to methods and properties. It references the + object without taking ownership similar to the above + return_value_policy::reference policy. In contrast to that policy, the + function or property’s implicit this argument (called the parent) is + considered to be the the owner of the return value (the child). + pybind11 then couples the lifetime of the parent to the child via a + reference relationship that ensures that the parent cannot be garbage + collected while Python is still using the child. More advanced + variations of this scheme are also possible using combinations of + return_value_policy::reference and the keep_alive call policy */ + reference_internal +}; + +NAMESPACE_BEGIN(detail) + +inline static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } + +// Returns the size as a multiple of sizeof(void *), rounded up. +inline static constexpr size_t size_in_ptrs(size_t s) { return 1 + ((s - 1) >> log2(sizeof(void *))); } + +/** + * The space to allocate for simple layout instance holders (see below) in multiple of the size of + * a pointer (e.g. 2 means 16 bytes on 64-bit architectures). The default is the minimum required + * to holder either a std::unique_ptr or std::shared_ptr (which is almost always + * sizeof(std::shared_ptr)). + */ +constexpr size_t instance_simple_holder_in_ptrs() { + static_assert(sizeof(std::shared_ptr) >= sizeof(std::unique_ptr), + "pybind assumes std::shared_ptrs are at least as big as std::unique_ptrs"); + return size_in_ptrs(sizeof(std::shared_ptr)); +} + +// Forward declarations +struct type_info; +struct value_and_holder; + +struct nonsimple_values_and_holders { + void **values_and_holders; + uint8_t *status; +}; + +/// The 'instance' type which needs to be standard layout (need to be able to use 'offsetof') +struct instance { + PyObject_HEAD + /// Storage for pointers and holder; see simple_layout, below, for a description + union { + void *simple_value_holder[1 + instance_simple_holder_in_ptrs()]; + nonsimple_values_and_holders nonsimple; + }; + /// Weak references + PyObject *weakrefs; + /// If true, the pointer is owned which means we're free to manage it with a holder. + bool owned : 1; + /** + * An instance has two possible value/holder layouts. + * + * Simple layout (when this flag is true), means the `simple_value_holder` is set with a pointer + * and the holder object governing that pointer, i.e. [val1*][holder]. This layout is applied + * whenever there is no python-side multiple inheritance of bound C++ types *and* the type's + * holder will fit in the default space (which is large enough to hold either a std::unique_ptr + * or std::shared_ptr). + * + * Non-simple layout applies when using custom holders that require more space than `shared_ptr` + * (which is typically the size of two pointers), or when multiple inheritance is used on the + * python side. Non-simple layout allocates the required amount of memory to have multiple + * bound C++ classes as parents. Under this layout, `nonsimple.values_and_holders` is set to a + * pointer to allocated space of the required space to hold a sequence of value pointers and + * holders followed `status`, a set of bit flags (1 byte each), i.e. + * [val1*][holder1][val2*][holder2]...[bb...] where each [block] is rounded up to a multiple of + * `sizeof(void *)`. `nonsimple.status` is, for convenience, a pointer to the + * beginning of the [bb...] block (but not independently allocated). + * + * Status bits indicate whether the associated holder is constructed (& + * status_holder_constructed) and whether the value pointer is registered (& + * status_instance_registered) in `registered_instances`. + */ + bool simple_layout : 1; + /// For simple layout, tracks whether the holder has been constructed + bool simple_holder_constructed : 1; + /// For simple layout, tracks whether the instance is registered in `registered_instances` + bool simple_instance_registered : 1; + /// If true, get_internals().patients has an entry for this object + bool has_patients : 1; + + /// Initializes all of the above type/values/holders data (but not the instance values themselves) + void allocate_layout(); + + /// Destroys/deallocates all of the above + void deallocate_layout(); + + /// Returns the value_and_holder wrapper for the given type (or the first, if `find_type` + /// omitted). Returns a default-constructed (with `.inst = nullptr`) object on failure if + /// `throw_if_missing` is false. + value_and_holder get_value_and_holder(const type_info *find_type = nullptr, bool throw_if_missing = true); + + /// Bit values for the non-simple status flags + static constexpr uint8_t status_holder_constructed = 1; + static constexpr uint8_t status_instance_registered = 2; +}; + +static_assert(std::is_standard_layout::value, "Internal error: `pybind11::detail::instance` is not standard layout!"); + +/// from __cpp_future__ import (convenient aliases from C++14/17) +#if defined(PYBIND11_CPP14) && (!defined(_MSC_VER) || _MSC_VER >= 1910) +using std::enable_if_t; +using std::conditional_t; +using std::remove_cv_t; +using std::remove_reference_t; +#else +template using enable_if_t = typename std::enable_if::type; +template using conditional_t = typename std::conditional::type; +template using remove_cv_t = typename std::remove_cv::type; +template using remove_reference_t = typename std::remove_reference::type; +#endif + +/// Index sequences +#if defined(PYBIND11_CPP14) +using std::index_sequence; +using std::make_index_sequence; +#else +template struct index_sequence { }; +template struct make_index_sequence_impl : make_index_sequence_impl { }; +template struct make_index_sequence_impl <0, S...> { typedef index_sequence type; }; +template using make_index_sequence = typename make_index_sequence_impl::type; +#endif + +/// Make an index sequence of the indices of true arguments +template struct select_indices_impl { using type = ISeq; }; +template struct select_indices_impl, I, B, Bs...> + : select_indices_impl, index_sequence>, I + 1, Bs...> {}; +template using select_indices = typename select_indices_impl, 0, Bs...>::type; + +/// Backports of std::bool_constant and std::negation to accommodate older compilers +template using bool_constant = std::integral_constant; +template struct negation : bool_constant { }; + +template struct void_t_impl { using type = void; }; +template using void_t = typename void_t_impl::type; + +/// Compile-time all/any/none of that check the boolean value of all template types +#if defined(__cpp_fold_expressions) && !(defined(_MSC_VER) && (_MSC_VER < 1916)) +template using all_of = bool_constant<(Ts::value && ...)>; +template using any_of = bool_constant<(Ts::value || ...)>; +#elif !defined(_MSC_VER) +template struct bools {}; +template using all_of = std::is_same< + bools, + bools>; +template using any_of = negation...>>; +#else +// MSVC has trouble with the above, but supports std::conjunction, which we can use instead (albeit +// at a slight loss of compilation efficiency). +template using all_of = std::conjunction; +template using any_of = std::disjunction; +#endif +template using none_of = negation>; + +template class... Predicates> using satisfies_all_of = all_of...>; +template class... Predicates> using satisfies_any_of = any_of...>; +template class... Predicates> using satisfies_none_of = none_of...>; + +/// Strip the class from a method type +template struct remove_class { }; +template struct remove_class { typedef R type(A...); }; +template struct remove_class { typedef R type(A...); }; + +/// Helper template to strip away type modifiers +template struct intrinsic_type { typedef T type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template struct intrinsic_type { typedef typename intrinsic_type::type type; }; +template using intrinsic_t = typename intrinsic_type::type; + +/// Helper type to replace 'void' in some expressions +struct void_type { }; + +/// Helper template which holds a list of types +template struct type_list { }; + +/// Compile-time integer sum +#ifdef __cpp_fold_expressions +template constexpr size_t constexpr_sum(Ts... ns) { return (0 + ... + size_t{ns}); } +#else +constexpr size_t constexpr_sum() { return 0; } +template +constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); } +#endif + +NAMESPACE_BEGIN(constexpr_impl) +/// Implementation details for constexpr functions +constexpr int first(int i) { return i; } +template +constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); } + +constexpr int last(int /*i*/, int result) { return result; } +template +constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); } +NAMESPACE_END(constexpr_impl) + +/// Return the index of the first type in Ts which satisfies Predicate. Returns sizeof...(Ts) if +/// none match. +template class Predicate, typename... Ts> +constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate::value...); } + +/// Return the index of the last type in Ts which satisfies Predicate, or -1 if none match. +template class Predicate, typename... Ts> +constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate::value...); } + +/// Return the Nth element from the parameter pack +template +struct pack_element { using type = typename pack_element::type; }; +template +struct pack_element<0, T, Ts...> { using type = T; }; + +/// Return the one and only type which matches the predicate, or Default if none match. +/// If more than one type matches the predicate, fail at compile-time. +template class Predicate, typename Default, typename... Ts> +struct exactly_one { + static constexpr auto found = constexpr_sum(Predicate::value...); + static_assert(found <= 1, "Found more than one type matching the predicate"); + + static constexpr auto index = found ? constexpr_first() : 0; + using type = conditional_t::type, Default>; +}; +template class P, typename Default> +struct exactly_one { using type = Default; }; + +template class Predicate, typename Default, typename... Ts> +using exactly_one_t = typename exactly_one::type; + +/// Defer the evaluation of type T until types Us are instantiated +template struct deferred_type { using type = T; }; +template using deferred_t = typename deferred_type::type; + +/// Like is_base_of, but requires a strict base (i.e. `is_strict_base_of::value == false`, +/// unlike `std::is_base_of`) +template using is_strict_base_of = bool_constant< + std::is_base_of::value && !std::is_same::value>; + +/// Like is_base_of, but also requires that the base type is accessible (i.e. that a Derived pointer +/// can be converted to a Base pointer) +template using is_accessible_base_of = bool_constant< + std::is_base_of::value && std::is_convertible::value>; + +template class Base> +struct is_template_base_of_impl { + template static std::true_type check(Base *); + static std::false_type check(...); +}; + +/// Check if a template is the base of a type. For example: +/// `is_template_base_of` is true if `struct T : Base {}` where U can be anything +template class Base, typename T> +#if !defined(_MSC_VER) +using is_template_base_of = decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)); +#else // MSVC2015 has trouble with decltype in template aliases +struct is_template_base_of : decltype(is_template_base_of_impl::check((intrinsic_t*)nullptr)) { }; +#endif + +/// Check if T is an instantiation of the template `Class`. For example: +/// `is_instantiation` is true if `T == shared_ptr` where U can be anything. +template class Class, typename T> +struct is_instantiation : std::false_type { }; +template class Class, typename... Us> +struct is_instantiation> : std::true_type { }; + +/// Check if T is std::shared_ptr where U can be anything +template using is_shared_ptr = is_instantiation; + +/// Check if T looks like an input iterator +template struct is_input_iterator : std::false_type {}; +template +struct is_input_iterator()), decltype(++std::declval())>> + : std::true_type {}; + +template using is_function_pointer = bool_constant< + std::is_pointer::value && std::is_function::type>::value>; + +template struct strip_function_object { + using type = typename remove_class::type; +}; + +// Extracts the function signature from a function, function pointer or lambda. +template > +using function_signature_t = conditional_t< + std::is_function::value, + F, + typename conditional_t< + std::is_pointer::value || std::is_member_pointer::value, + std::remove_pointer, + strip_function_object + >::type +>; + +/// Returns true if the type looks like a lambda: that is, isn't a function, pointer or member +/// pointer. Note that this can catch all sorts of other things, too; this is intended to be used +/// in a place where passing a lambda makes sense. +template using is_lambda = satisfies_none_of, + std::is_function, std::is_pointer, std::is_member_pointer>; + +/// Ignore that a variable is unused in compiler warnings +inline void ignore_unused(const int *) { } + +/// Apply a function over each element of a parameter pack +#ifdef __cpp_fold_expressions +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) (((PATTERN), void()), ...) +#else +using expand_side_effects = bool[]; +#define PYBIND11_EXPAND_SIDE_EFFECTS(PATTERN) pybind11::detail::expand_side_effects{ ((PATTERN), void(), false)..., false } +#endif + +NAMESPACE_END(detail) + +/// C++ bindings of builtin Python exceptions +class builtin_exception : public std::runtime_error { +public: + using std::runtime_error::runtime_error; + /// Set the error using the Python C API + virtual void set_error() const = 0; +}; + +#define PYBIND11_RUNTIME_EXCEPTION(name, type) \ + class name : public builtin_exception { public: \ + using builtin_exception::builtin_exception; \ + name() : name("") { } \ + void set_error() const override { PyErr_SetString(type, what()); } \ + }; + +PYBIND11_RUNTIME_EXCEPTION(stop_iteration, PyExc_StopIteration) +PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError) +PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError) +PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError) +PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError) +PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError) +PYBIND11_RUNTIME_EXCEPTION(import_error, PyExc_ImportError) +PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a type casting error +PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally + +[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } +[[noreturn]] PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } + +template struct format_descriptor { }; + +NAMESPACE_BEGIN(detail) +// Returns the index of the given type in the type char array below, and in the list in numpy.h +// The order here is: bool; 8 ints ((signed,unsigned)x(8,16,32,64)bits); float,double,long double; +// complex float,double,long double. Note that the long double types only participate when long +// double is actually longer than double (it isn't under MSVC). +// NB: not only the string below but also complex.h and numpy.h rely on this order. +template struct is_fmt_numeric { static constexpr bool value = false; }; +template struct is_fmt_numeric::value>> { + static constexpr bool value = true; + static constexpr int index = std::is_same::value ? 0 : 1 + ( + std::is_integral::value ? detail::log2(sizeof(T))*2 + std::is_unsigned::value : 8 + ( + std::is_same::value ? 1 : std::is_same::value ? 2 : 0)); +}; +NAMESPACE_END(detail) + +template struct format_descriptor::value>> { + static constexpr const char c = "?bBhHiIqQfdg"[detail::is_fmt_numeric::index]; + static constexpr const char value[2] = { c, '\0' }; + static std::string format() { return std::string(1, c); } +}; + +#if !defined(PYBIND11_CPP17) + +template constexpr const char format_descriptor< + T, detail::enable_if_t::value>>::value[2]; + +#endif + +/// RAII wrapper that temporarily clears any Python error state +struct error_scope { + PyObject *type, *value, *trace; + error_scope() { PyErr_Fetch(&type, &value, &trace); } + ~error_scope() { PyErr_Restore(type, value, trace); } +}; + +/// Dummy destructor wrapper that can be used to expose classes with a private destructor +struct nodelete { template void operator()(T*) { } }; + +NAMESPACE_BEGIN(detail) +template +struct overload_cast_impl { + constexpr overload_cast_impl() {} // MSVC 2015 needs this + + template + constexpr auto operator()(Return (*pf)(Args...)) const noexcept + -> decltype(pf) { return pf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept + -> decltype(pmf) { return pmf; } + + template + constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept + -> decltype(pmf) { return pmf; } +}; +NAMESPACE_END(detail) + +// overload_cast requires variable templates: C++14 +#if defined(PYBIND11_CPP14) +#define PYBIND11_OVERLOAD_CAST 1 +/// Syntax sugar for resolving overloaded function pointers: +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func) +template +static constexpr detail::overload_cast_impl overload_cast = {}; +// MSVC 2015 only accepts this particular initialization syntax for this variable template. +#endif + +/// Const member function selector for overload_cast +/// - regular: static_cast(&Class::func) +/// - sweet: overload_cast(&Class::func, const_) +static constexpr auto const_ = std::true_type{}; + +#if !defined(PYBIND11_CPP14) // no overload_cast: providing something that static_assert-fails: +template struct overload_cast { + static_assert(detail::deferred_t::value, + "pybind11::overload_cast<...> requires compiling in C++14 mode"); +}; +#endif // overload_cast + +NAMESPACE_BEGIN(detail) + +// Adaptor for converting arbitrary container arguments into a vector; implicitly convertible from +// any standard container (or C-style array) supporting std::begin/std::end, any singleton +// arithmetic type (if T is arithmetic), or explicitly constructible from an iterator pair. +template +class any_container { + std::vector v; +public: + any_container() = default; + + // Can construct from a pair of iterators + template ::value>> + any_container(It first, It last) : v(first, last) { } + + // Implicit conversion constructor from any arbitrary container type with values convertible to T + template ())), T>::value>> + any_container(const Container &c) : any_container(std::begin(c), std::end(c)) { } + + // initializer_list's aren't deducible, so don't get matched by the above template; we need this + // to explicitly allow implicit conversion from one: + template ::value>> + any_container(const std::initializer_list &c) : any_container(c.begin(), c.end()) { } + + // Avoid copying if given an rvalue vector of the correct type. + any_container(std::vector &&v) : v(std::move(v)) { } + + // Moves the vector out of an rvalue any_container + operator std::vector &&() && { return std::move(v); } + + // Dereferencing obtains a reference to the underlying vector + std::vector &operator*() { return v; } + const std::vector &operator*() const { return v; } + + // -> lets you call methods on the underlying vector + std::vector *operator->() { return &v; } + const std::vector *operator->() const { return &v; } +}; + +NAMESPACE_END(detail) + + + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/detail/descr.h b/thirdparty/pybind11/include/pybind11/detail/descr.h new file mode 100644 index 000000000..8d404e534 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/detail/descr.h @@ -0,0 +1,100 @@ +/* + pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +#if !defined(_MSC_VER) +# define PYBIND11_DESCR_CONSTEXPR static constexpr +#else +# define PYBIND11_DESCR_CONSTEXPR const +#endif + +/* Concatenate type signatures at compile time */ +template +struct descr { + char text[N + 1]; + + constexpr descr() : text{'\0'} { } + constexpr descr(char const (&s)[N+1]) : descr(s, make_index_sequence()) { } + + template + constexpr descr(char const (&s)[N+1], index_sequence) : text{s[Is]..., '\0'} { } + + template + constexpr descr(char c, Chars... cs) : text{c, static_cast(cs)..., '\0'} { } + + static constexpr std::array types() { + return {{&typeid(Ts)..., nullptr}}; + } +}; + +template +constexpr descr plus_impl(const descr &a, const descr &b, + index_sequence, index_sequence) { + return {a.text[Is1]..., b.text[Is2]...}; +} + +template +constexpr descr operator+(const descr &a, const descr &b) { + return plus_impl(a, b, make_index_sequence(), make_index_sequence()); +} + +template +constexpr descr _(char const(&text)[N]) { return descr(text); } +constexpr descr<0> _(char const(&)[1]) { return {}; } + +template struct int_to_str : int_to_str { }; +template struct int_to_str<0, Digits...> { + static constexpr auto digits = descr(('0' + Digits)...); +}; + +// Ternary description (like std::conditional) +template +constexpr enable_if_t> _(char const(&text1)[N1], char const(&)[N2]) { + return _(text1); +} +template +constexpr enable_if_t> _(char const(&)[N1], char const(&text2)[N2]) { + return _(text2); +} + +template +constexpr enable_if_t _(const T1 &d, const T2 &) { return d; } +template +constexpr enable_if_t _(const T1 &, const T2 &d) { return d; } + +template auto constexpr _() -> decltype(int_to_str::digits) { + return int_to_str::digits; +} + +template constexpr descr<1, Type> _() { return {'%'}; } + +constexpr descr<0> concat() { return {}; } + +template +constexpr descr concat(const descr &descr) { return descr; } + +template +constexpr auto concat(const descr &d, const Args &...args) + -> decltype(std::declval>() + concat(args...)) { + return d + _(", ") + concat(args...); +} + +template +constexpr descr type_descr(const descr &descr) { + return _("{") + descr + _("}"); +} + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/detail/init.h b/thirdparty/pybind11/include/pybind11/detail/init.h new file mode 100644 index 000000000..acfe00bdb --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/detail/init.h @@ -0,0 +1,335 @@ +/* + pybind11/detail/init.h: init factory function implementation and support code. + + Copyright (c) 2017 Jason Rhinelander + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "class.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template <> +class type_caster { +public: + bool load(handle h, bool) { + value = reinterpret_cast(h.ptr()); + return true; + } + + template using cast_op_type = value_and_holder &; + operator value_and_holder &() { return *value; } + static constexpr auto name = _(); + +private: + value_and_holder *value = nullptr; +}; + +NAMESPACE_BEGIN(initimpl) + +inline void no_nullptr(void *ptr) { + if (!ptr) throw type_error("pybind11::init(): factory function returned nullptr"); +} + +// Implementing functions for all forms of py::init<...> and py::init(...) +template using Cpp = typename Class::type; +template using Alias = typename Class::type_alias; +template using Holder = typename Class::holder_type; + +template using is_alias_constructible = std::is_constructible, Cpp &&>; + +// Takes a Cpp pointer and returns true if it actually is a polymorphic Alias instance. +template = 0> +bool is_alias(Cpp *ptr) { + return dynamic_cast *>(ptr) != nullptr; +} +// Failing fallback version of the above for a no-alias class (always returns false) +template +constexpr bool is_alias(void *) { return false; } + +// Constructs and returns a new object; if the given arguments don't map to a constructor, we fall +// back to brace aggregate initiailization so that for aggregate initialization can be used with +// py::init, e.g. `py::init` to initialize a `struct T { int a; int b; }`. For +// non-aggregate types, we need to use an ordinary T(...) constructor (invoking as `T{...}` usually +// works, but will not do the expected thing when `T` has an `initializer_list` constructor). +template ::value, int> = 0> +inline Class *construct_or_initialize(Args &&...args) { return new Class(std::forward(args)...); } +template ::value, int> = 0> +inline Class *construct_or_initialize(Args &&...args) { return new Class{std::forward(args)...}; } + +// Attempts to constructs an alias using a `Alias(Cpp &&)` constructor. This allows types with +// an alias to provide only a single Cpp factory function as long as the Alias can be +// constructed from an rvalue reference of the base Cpp type. This means that Alias classes +// can, when appropriate, simply define a `Alias(Cpp &&)` constructor rather than needing to +// inherit all the base class constructors. +template +void construct_alias_from_cpp(std::true_type /*is_alias_constructible*/, + value_and_holder &v_h, Cpp &&base) { + v_h.value_ptr() = new Alias(std::move(base)); +} +template +[[noreturn]] void construct_alias_from_cpp(std::false_type /*!is_alias_constructible*/, + value_and_holder &, Cpp &&) { + throw type_error("pybind11::init(): unable to convert returned instance to required " + "alias class: no `Alias(Class &&)` constructor available"); +} + +// Error-generating fallback for factories that don't match one of the below construction +// mechanisms. +template +void construct(...) { + static_assert(!std::is_same::value /* always false */, + "pybind11::init(): init function must return a compatible pointer, " + "holder, or value"); +} + +// Pointer return v1: the factory function returns a class pointer for a registered class. +// If we don't need an alias (because this class doesn't have one, or because the final type is +// inherited on the Python side) we can simply take over ownership. Otherwise we need to try to +// construct an Alias from the returned base instance. +template +void construct(value_and_holder &v_h, Cpp *ptr, bool need_alias) { + no_nullptr(ptr); + if (Class::has_alias && need_alias && !is_alias(ptr)) { + // We're going to try to construct an alias by moving the cpp type. Whether or not + // that succeeds, we still need to destroy the original cpp pointer (either the + // moved away leftover, if the alias construction works, or the value itself if we + // throw an error), but we can't just call `delete ptr`: it might have a special + // deleter, or might be shared_from_this. So we construct a holder around it as if + // it was a normal instance, then steal the holder away into a local variable; thus + // the holder and destruction happens when we leave the C++ scope, and the holder + // class gets to handle the destruction however it likes. + v_h.value_ptr() = ptr; + v_h.set_instance_registered(true); // To prevent init_instance from registering it + v_h.type->init_instance(v_h.inst, nullptr); // Set up the holder + Holder temp_holder(std::move(v_h.holder>())); // Steal the holder + v_h.type->dealloc(v_h); // Destroys the moved-out holder remains, resets value ptr to null + v_h.set_instance_registered(false); + + construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(*ptr)); + } else { + // Otherwise the type isn't inherited, so we don't need an Alias + v_h.value_ptr() = ptr; + } +} + +// Pointer return v2: a factory that always returns an alias instance ptr. We simply take over +// ownership of the pointer. +template = 0> +void construct(value_and_holder &v_h, Alias *alias_ptr, bool) { + no_nullptr(alias_ptr); + v_h.value_ptr() = static_cast *>(alias_ptr); +} + +// Holder return: copy its pointer, and move or copy the returned holder into the new instance's +// holder. This also handles types like std::shared_ptr and std::unique_ptr where T is a +// derived type (through those holder's implicit conversion from derived class holder constructors). +template +void construct(value_and_holder &v_h, Holder holder, bool need_alias) { + auto *ptr = holder_helper>::get(holder); + // If we need an alias, check that the held pointer is actually an alias instance + if (Class::has_alias && need_alias && !is_alias(ptr)) + throw type_error("pybind11::init(): construction failed: returned holder-wrapped instance " + "is not an alias instance"); + + v_h.value_ptr() = ptr; + v_h.type->init_instance(v_h.inst, &holder); +} + +// return-by-value version 1: returning a cpp class by value. If the class has an alias and an +// alias is required the alias must have an `Alias(Cpp &&)` constructor so that we can construct +// the alias from the base when needed (i.e. because of Python-side inheritance). When we don't +// need it, we simply move-construct the cpp value into a new instance. +template +void construct(value_and_holder &v_h, Cpp &&result, bool need_alias) { + static_assert(std::is_move_constructible>::value, + "pybind11::init() return-by-value factory function requires a movable class"); + if (Class::has_alias && need_alias) + construct_alias_from_cpp(is_alias_constructible{}, v_h, std::move(result)); + else + v_h.value_ptr() = new Cpp(std::move(result)); +} + +// return-by-value version 2: returning a value of the alias type itself. We move-construct an +// Alias instance (even if no the python-side inheritance is involved). The is intended for +// cases where Alias initialization is always desired. +template +void construct(value_and_holder &v_h, Alias &&result, bool) { + static_assert(std::is_move_constructible>::value, + "pybind11::init() return-by-alias-value factory function requires a movable alias class"); + v_h.value_ptr() = new Alias(std::move(result)); +} + +// Implementing class for py::init<...>() +template +struct constructor { + template = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } + + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + if (Py_TYPE(v_h.inst) == v_h.type->type) + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + else + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } + + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } +}; + +// Implementing class for py::init_alias<...>() +template struct alias_constructor { + template , Args...>::value, int> = 0> + static void execute(Class &cl, const Extra&... extra) { + cl.def("__init__", [](value_and_holder &v_h, Args... args) { + v_h.value_ptr() = construct_or_initialize>(std::forward(args)...); + }, is_new_style_constructor(), extra...); + } +}; + +// Implementation class for py::init(Func) and py::init(Func, AliasFunc) +template , typename = function_signature_t> +struct factory; + +// Specialization for py::init(Func) +template +struct factory { + remove_reference_t class_factory; + + factory(Func &&f) : class_factory(std::forward(f)) { } + + // The given class either has no alias or has no separate alias factory; + // this always constructs the class itself. If the class is registered with an alias + // type and an alias instance is needed (i.e. because the final type is a Python class + // inheriting from the C++ type) the returned value needs to either already be an alias + // instance, or the alias needs to be constructible from a `Class &&` argument. + template + void execute(Class &cl, const Extra &...extra) && { + #if defined(PYBIND11_CPP14) + cl.def("__init__", [func = std::move(class_factory)] + #else + auto &func = class_factory; + cl.def("__init__", [func] + #endif + (value_and_holder &v_h, Args... args) { + construct(v_h, func(std::forward(args)...), + Py_TYPE(v_h.inst) != v_h.type->type); + }, is_new_style_constructor(), extra...); + } +}; + +// Specialization for py::init(Func, AliasFunc) +template +struct factory { + static_assert(sizeof...(CArgs) == sizeof...(AArgs), + "pybind11::init(class_factory, alias_factory): class and alias factories " + "must have identical argument signatures"); + static_assert(all_of...>::value, + "pybind11::init(class_factory, alias_factory): class and alias factories " + "must have identical argument signatures"); + + remove_reference_t class_factory; + remove_reference_t alias_factory; + + factory(CFunc &&c, AFunc &&a) + : class_factory(std::forward(c)), alias_factory(std::forward(a)) { } + + // The class factory is called when the `self` type passed to `__init__` is the direct + // class (i.e. not inherited), the alias factory when `self` is a Python-side subtype. + template + void execute(Class &cl, const Extra&... extra) && { + static_assert(Class::has_alias, "The two-argument version of `py::init()` can " + "only be used if the class has an alias"); + #if defined(PYBIND11_CPP14) + cl.def("__init__", [class_func = std::move(class_factory), alias_func = std::move(alias_factory)] + #else + auto &class_func = class_factory; + auto &alias_func = alias_factory; + cl.def("__init__", [class_func, alias_func] + #endif + (value_and_holder &v_h, CArgs... args) { + if (Py_TYPE(v_h.inst) == v_h.type->type) + // If the instance type equals the registered type we don't have inheritance, so + // don't need the alias and can construct using the class function: + construct(v_h, class_func(std::forward(args)...), false); + else + construct(v_h, alias_func(std::forward(args)...), true); + }, is_new_style_constructor(), extra...); + } +}; + +/// Set just the C++ state. Same as `__init__`. +template +void setstate(value_and_holder &v_h, T &&result, bool need_alias) { + construct(v_h, std::forward(result), need_alias); +} + +/// Set both the C++ and Python states +template ::value, int> = 0> +void setstate(value_and_holder &v_h, std::pair &&result, bool need_alias) { + construct(v_h, std::move(result.first), need_alias); + setattr((PyObject *) v_h.inst, "__dict__", result.second); +} + +/// Implementation for py::pickle(GetState, SetState) +template , typename = function_signature_t> +struct pickle_factory; + +template +struct pickle_factory { + static_assert(std::is_same, intrinsic_t>::value, + "The type returned by `__getstate__` must be the same " + "as the argument accepted by `__setstate__`"); + + remove_reference_t get; + remove_reference_t set; + + pickle_factory(Get get, Set set) + : get(std::forward(get)), set(std::forward(set)) { } + + template + void execute(Class &cl, const Extra &...extra) && { + cl.def("__getstate__", std::move(get)); + +#if defined(PYBIND11_CPP14) + cl.def("__setstate__", [func = std::move(set)] +#else + auto &func = set; + cl.def("__setstate__", [func] +#endif + (value_and_holder &v_h, ArgState state) { + setstate(v_h, func(std::forward(state)), + Py_TYPE(v_h.inst) != v_h.type->type); + }, is_new_style_constructor(), extra...); + } +}; + +NAMESPACE_END(initimpl) +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/thirdparty/pybind11/include/pybind11/detail/internals.h b/thirdparty/pybind11/include/pybind11/detail/internals.h new file mode 100644 index 000000000..6224dfb22 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/detail/internals.h @@ -0,0 +1,349 @@ +/* + pybind11/detail/internals.h: Internal data structure and related functions + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "../pytypes.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) +// Forward declarations +inline PyTypeObject *make_static_property_type(); +inline PyTypeObject *make_default_metaclass(); +inline PyObject *make_object_base_type(PyTypeObject *metaclass); + +// The old Python Thread Local Storage (TLS) API is deprecated in Python 3.7 in favor of the new +// Thread Specific Storage (TSS) API. +#if PY_VERSION_HEX >= 0x03070000 +# define PYBIND11_TLS_KEY_INIT(var) Py_tss_t *var = nullptr +# define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key)) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value)) +# define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr) +# define PYBIND11_TLS_FREE(key) PyThread_tss_free(key) +#else + // Usually an int but a long on Cygwin64 with Python 3.x +# define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0 +# define PYBIND11_TLS_GET_VALUE(key) PyThread_get_key_value((key)) +# if PY_MAJOR_VERSION < 3 +# define PYBIND11_TLS_DELETE_VALUE(key) \ + PyThread_delete_key_value(key) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ + do { \ + PyThread_delete_key_value((key)); \ + PyThread_set_key_value((key), (value)); \ + } while (false) +# else +# define PYBIND11_TLS_DELETE_VALUE(key) \ + PyThread_set_key_value((key), nullptr) +# define PYBIND11_TLS_REPLACE_VALUE(key, value) \ + PyThread_set_key_value((key), (value)) +# endif +# define PYBIND11_TLS_FREE(key) (void)key +#endif + +// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly +// other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module +// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under +// libstdc++, this doesn't happen: equality and the type_index hash are based on the type name, +// which works. If not under a known-good stl, provide our own name-based hash and equality +// functions that use the type name. +#if defined(__GLIBCXX__) +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { return lhs == rhs; } +using type_hash = std::hash; +using type_equal_to = std::equal_to; +#else +inline bool same_type(const std::type_info &lhs, const std::type_info &rhs) { + return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; +} + +struct type_hash { + size_t operator()(const std::type_index &t) const { + size_t hash = 5381; + const char *ptr = t.name(); + while (auto c = static_cast(*ptr++)) + hash = (hash * 33) ^ c; + return hash; + } +}; + +struct type_equal_to { + bool operator()(const std::type_index &lhs, const std::type_index &rhs) const { + return lhs.name() == rhs.name() || std::strcmp(lhs.name(), rhs.name()) == 0; + } +}; +#endif + +template +using type_map = std::unordered_map; + +struct overload_hash { + inline size_t operator()(const std::pair& v) const { + size_t value = std::hash()(v.first); + value ^= std::hash()(v.second) + 0x9e3779b9 + (value<<6) + (value>>2); + return value; + } +}; + +/// Internal data structure used to track registered instances and types. +/// Whenever binary incompatible changes are made to this structure, +/// `PYBIND11_INTERNALS_VERSION` must be incremented. +struct internals { + type_map registered_types_cpp; // std::type_index -> pybind11's type information + std::unordered_map> registered_types_py; // PyTypeObject* -> base type_info(s) + std::unordered_multimap registered_instances; // void * -> instance* + std::unordered_set, overload_hash> inactive_overload_cache; + type_map> direct_conversions; + std::unordered_map> patients; + std::forward_list registered_exception_translators; + std::unordered_map shared_data; // Custom data to be shared across extensions + std::vector loader_patient_stack; // Used by `loader_life_support` + std::forward_list static_strings; // Stores the std::strings backing detail::c_str() + PyTypeObject *static_property_type; + PyTypeObject *default_metaclass; + PyObject *instance_base; +#if defined(WITH_THREAD) + PYBIND11_TLS_KEY_INIT(tstate); + PyInterpreterState *istate = nullptr; + ~internals() { + // This destructor is called *after* Py_Finalize() in finalize_interpreter(). + // That *SHOULD BE* fine. The following details what happens whe PyThread_tss_free is called. + // PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does nothing. + // PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree. + // PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX). Neither + // of those have anything to do with CPython internals. + // PyMem_RawFree *requires* that the `tstate` be allocated with the CPython allocator. + PYBIND11_TLS_FREE(tstate); + } +#endif +}; + +/// Additional type information which does not fit into the PyTypeObject. +/// Changes to this struct also require bumping `PYBIND11_INTERNALS_VERSION`. +struct type_info { + PyTypeObject *type; + const std::type_info *cpptype; + size_t type_size, type_align, holder_size_in_ptrs; + void *(*operator_new)(size_t); + void (*init_instance)(instance *, const void *); + void (*dealloc)(value_and_holder &v_h); + std::vector implicit_conversions; + std::vector> implicit_casts; + std::vector *direct_conversions; + buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; + void *get_buffer_data = nullptr; + void *(*module_local_load)(PyObject *, const type_info *) = nullptr; + /* A simple type never occurs as a (direct or indirect) parent + * of a class that makes use of multiple inheritance */ + bool simple_type : 1; + /* True if there is no multiple inheritance in this type's inheritance tree */ + bool simple_ancestors : 1; + /* for base vs derived holder_type checks */ + bool default_holder : 1; + /* true if this is a type registered with py::module_local */ + bool module_local : 1; +}; + +/// Tracks the `internals` and `type_info` ABI version independent of the main library version +#define PYBIND11_INTERNALS_VERSION 4 + +/// On MSVC, debug and release builds are not ABI-compatible! +#if defined(_MSC_VER) && defined(_DEBUG) +# define PYBIND11_BUILD_TYPE "_debug" +#else +# define PYBIND11_BUILD_TYPE "" +#endif + +/// Let's assume that different compilers are ABI-incompatible. +#if defined(_MSC_VER) +# define PYBIND11_COMPILER_TYPE "_msvc" +#elif defined(__INTEL_COMPILER) +# define PYBIND11_COMPILER_TYPE "_icc" +#elif defined(__clang__) +# define PYBIND11_COMPILER_TYPE "_clang" +#elif defined(__PGI) +# define PYBIND11_COMPILER_TYPE "_pgi" +#elif defined(__MINGW32__) +# define PYBIND11_COMPILER_TYPE "_mingw" +#elif defined(__CYGWIN__) +# define PYBIND11_COMPILER_TYPE "_gcc_cygwin" +#elif defined(__GNUC__) +# define PYBIND11_COMPILER_TYPE "_gcc" +#else +# define PYBIND11_COMPILER_TYPE "_unknown" +#endif + +#if defined(_LIBCPP_VERSION) +# define PYBIND11_STDLIB "_libcpp" +#elif defined(__GLIBCXX__) || defined(__GLIBCPP__) +# define PYBIND11_STDLIB "_libstdcpp" +#else +# define PYBIND11_STDLIB "" +#endif + +/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility. +#if defined(__GXX_ABI_VERSION) +# define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION) +#else +# define PYBIND11_BUILD_ABI "" +#endif + +#if defined(WITH_THREAD) +# define PYBIND11_INTERNALS_KIND "" +#else +# define PYBIND11_INTERNALS_KIND "_without_thread" +#endif + +#define PYBIND11_INTERNALS_ID "__pybind11_internals_v" \ + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" + +#define PYBIND11_MODULE_LOCAL_ID "__pybind11_module_local_v" \ + PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__" + +/// Each module locally stores a pointer to the `internals` data. The data +/// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`. +inline internals **&get_internals_pp() { + static internals **internals_pp = nullptr; + return internals_pp; +} + +inline void translate_exception(std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return; + } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return; + } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return; + } catch (const std::overflow_error &e) { PyErr_SetString(PyExc_OverflowError, e.what()); return; + } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return; + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); + return; + } +} + +#if !defined(__GLIBCXX__) +inline void translate_local_exception(std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (error_already_set &e) { e.restore(); return; + } catch (const builtin_exception &e) { e.set_error(); return; + } +} +#endif + +/// Return a reference to the current `internals` data +PYBIND11_NOINLINE inline internals &get_internals() { + auto **&internals_pp = get_internals_pp(); + if (internals_pp && *internals_pp) + return **internals_pp; + + // Ensure that the GIL is held since we will need to make Python calls. + // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals. + struct gil_scoped_acquire_local { + gil_scoped_acquire_local() : state (PyGILState_Ensure()) {} + ~gil_scoped_acquire_local() { PyGILState_Release(state); } + const PyGILState_STATE state; + } gil; + + constexpr auto *id = PYBIND11_INTERNALS_ID; + auto builtins = handle(PyEval_GetBuiltins()); + if (builtins.contains(id) && isinstance(builtins[id])) { + internals_pp = static_cast(capsule(builtins[id])); + + // We loaded builtins through python's builtins, which means that our `error_already_set` + // and `builtin_exception` may be different local classes than the ones set up in the + // initial exception translator, below, so add another for our local exception classes. + // + // libstdc++ doesn't require this (types there are identified only by name) +#if !defined(__GLIBCXX__) + (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception); +#endif + } else { + if (!internals_pp) internals_pp = new internals*(); + auto *&internals_ptr = *internals_pp; + internals_ptr = new internals(); +#if defined(WITH_THREAD) + PyEval_InitThreads(); + PyThreadState *tstate = PyThreadState_Get(); + #if PY_VERSION_HEX >= 0x03070000 + internals_ptr->tstate = PyThread_tss_alloc(); + if (!internals_ptr->tstate || PyThread_tss_create(internals_ptr->tstate)) + pybind11_fail("get_internals: could not successfully initialize the TSS key!"); + PyThread_tss_set(internals_ptr->tstate, tstate); + #else + internals_ptr->tstate = PyThread_create_key(); + if (internals_ptr->tstate == -1) + pybind11_fail("get_internals: could not successfully initialize the TLS key!"); + PyThread_set_key_value(internals_ptr->tstate, tstate); + #endif + internals_ptr->istate = tstate->interp; +#endif + builtins[id] = capsule(internals_pp); + internals_ptr->registered_exception_translators.push_front(&translate_exception); + internals_ptr->static_property_type = make_static_property_type(); + internals_ptr->default_metaclass = make_default_metaclass(); + internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass); + } + return **internals_pp; +} + +/// Works like `internals.registered_types_cpp`, but for module-local registered types: +inline type_map ®istered_local_types_cpp() { + static type_map locals{}; + return locals; +} + +/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its +/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only +/// cleared when the program exits or after interpreter shutdown (when embedding), and so are +/// suitable for c-style strings needed by Python internals (such as PyTypeObject's tp_name). +template +const char *c_str(Args &&...args) { + auto &strings = get_internals().static_strings; + strings.emplace_front(std::forward(args)...); + return strings.front().c_str(); +} + +NAMESPACE_END(detail) + +/// Returns a named pointer that is shared among all extension modules (using the same +/// pybind11 version) running in the current interpreter. Names starting with underscores +/// are reserved for internal usage. Returns `nullptr` if no matching entry was found. +inline PYBIND11_NOINLINE void *get_shared_data(const std::string &name) { + auto &internals = detail::get_internals(); + auto it = internals.shared_data.find(name); + return it != internals.shared_data.end() ? it->second : nullptr; +} + +/// Set the shared data that can be later recovered by `get_shared_data()`. +inline PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) { + detail::get_internals().shared_data[name] = data; + return data; +} + +/// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if +/// such entry exists. Otherwise, a new object of default-constructible type `T` is +/// added to the shared data under the given name and a reference to it is returned. +template +T &get_or_create_shared_data(const std::string &name) { + auto &internals = detail::get_internals(); + auto it = internals.shared_data.find(name); + T *ptr = (T *) (it != internals.shared_data.end() ? it->second : nullptr); + if (!ptr) { + ptr = new T(); + internals.shared_data[name] = ptr; + } + return *ptr; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/detail/typeid.h b/thirdparty/pybind11/include/pybind11/detail/typeid.h new file mode 100644 index 000000000..9c8a4fc69 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/detail/typeid.h @@ -0,0 +1,55 @@ +/* + pybind11/detail/typeid.h: Compiler-independent access to type identifiers + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include +#include + +#if defined(__GNUG__) +#include +#endif + +#include "common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) +/// Erase all occurrences of a substring +inline void erase_all(std::string &string, const std::string &search) { + for (size_t pos = 0;;) { + pos = string.find(search, pos); + if (pos == std::string::npos) break; + string.erase(pos, search.length()); + } +} + +PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { +#if defined(__GNUG__) + int status = 0; + std::unique_ptr res { + abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; + if (status == 0) + name = res.get(); +#else + detail::erase_all(name, "class "); + detail::erase_all(name, "struct "); + detail::erase_all(name, "enum "); +#endif + detail::erase_all(name, "pybind11::"); +} +NAMESPACE_END(detail) + +/// Return a string representation of a C++ type +template static std::string type_id() { + std::string name(typeid(T).name()); + detail::clean_type_id(name); + return name; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/eigen.h b/thirdparty/pybind11/include/pybind11/eigen.h new file mode 100644 index 000000000..d963d9650 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/eigen.h @@ -0,0 +1,607 @@ +/* + pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "numpy.h" + +#if defined(__INTEL_COMPILER) +# pragma warning(disable: 1682) // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +#elif defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# ifdef __clang__ +// Eigen generates a bunch of implicit-copy-constructor-is-deprecated warnings with -Wdeprecated +// under Clang, so disable that warning here: +# pragma GCC diagnostic ignored "-Wdeprecated" +# endif +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wint-in-bool-context" +# endif +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +# pragma warning(disable: 4996) // warning C4996: std::unary_negate is deprecated in C++17 +#endif + +#include +#include + +// Eigen prior to 3.2.7 doesn't have proper move constructors--but worse, some classes get implicit +// move constructors that break things. We could detect this an explicitly copy, but an extra copy +// of matrices seems highly undesirable. +static_assert(EIGEN_VERSION_AT_LEAST(3,2,7), "Eigen support in pybind11 requires Eigen >= 3.2.7"); + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +// Provide a convenience alias for easier pass-by-ref usage with fully dynamic strides: +using EigenDStride = Eigen::Stride; +template using EigenDRef = Eigen::Ref; +template using EigenDMap = Eigen::Map; + +NAMESPACE_BEGIN(detail) + +#if EIGEN_VERSION_AT_LEAST(3,3,0) +using EigenIndex = Eigen::Index; +#else +using EigenIndex = EIGEN_DEFAULT_DENSE_INDEX_TYPE; +#endif + +// Matches Eigen::Map, Eigen::Ref, blocks, etc: +template using is_eigen_dense_map = all_of, std::is_base_of, T>>; +template using is_eigen_mutable_map = std::is_base_of, T>; +template using is_eigen_dense_plain = all_of>, is_template_base_of>; +template using is_eigen_sparse = is_template_base_of; +// Test for objects inheriting from EigenBase that aren't captured by the above. This +// basically covers anything that can be assigned to a dense matrix but that don't have a typical +// matrix data layout that can be copied from their .data(). For example, DiagonalMatrix and +// SelfAdjointView fall into this category. +template using is_eigen_other = all_of< + is_template_base_of, + negation, is_eigen_dense_plain, is_eigen_sparse>> +>; + +// Captures numpy/eigen conformability status (returned by EigenProps::conformable()): +template struct EigenConformable { + bool conformable = false; + EigenIndex rows = 0, cols = 0; + EigenDStride stride{0, 0}; // Only valid if negativestrides is false! + bool negativestrides = false; // If true, do not use stride! + + EigenConformable(bool fits = false) : conformable{fits} {} + // Matrix type: + EigenConformable(EigenIndex r, EigenIndex c, + EigenIndex rstride, EigenIndex cstride) : + conformable{true}, rows{r}, cols{c} { + // TODO: when Eigen bug #747 is fixed, remove the tests for non-negativity. http://eigen.tuxfamily.org/bz/show_bug.cgi?id=747 + if (rstride < 0 || cstride < 0) { + negativestrides = true; + } else { + stride = {EigenRowMajor ? rstride : cstride /* outer stride */, + EigenRowMajor ? cstride : rstride /* inner stride */ }; + } + } + // Vector type: + EigenConformable(EigenIndex r, EigenIndex c, EigenIndex stride) + : EigenConformable(r, c, r == 1 ? c*stride : stride, c == 1 ? r : r*stride) {} + + template bool stride_compatible() const { + // To have compatible strides, we need (on both dimensions) one of fully dynamic strides, + // matching strides, or a dimension size of 1 (in which case the stride value is irrelevant) + return + !negativestrides && + (props::inner_stride == Eigen::Dynamic || props::inner_stride == stride.inner() || + (EigenRowMajor ? cols : rows) == 1) && + (props::outer_stride == Eigen::Dynamic || props::outer_stride == stride.outer() || + (EigenRowMajor ? rows : cols) == 1); + } + operator bool() const { return conformable; } +}; + +template struct eigen_extract_stride { using type = Type; }; +template +struct eigen_extract_stride> { using type = StrideType; }; +template +struct eigen_extract_stride> { using type = StrideType; }; + +// Helper struct for extracting information from an Eigen type +template struct EigenProps { + using Type = Type_; + using Scalar = typename Type::Scalar; + using StrideType = typename eigen_extract_stride::type; + static constexpr EigenIndex + rows = Type::RowsAtCompileTime, + cols = Type::ColsAtCompileTime, + size = Type::SizeAtCompileTime; + static constexpr bool + row_major = Type::IsRowMajor, + vector = Type::IsVectorAtCompileTime, // At least one dimension has fixed size 1 + fixed_rows = rows != Eigen::Dynamic, + fixed_cols = cols != Eigen::Dynamic, + fixed = size != Eigen::Dynamic, // Fully-fixed size + dynamic = !fixed_rows && !fixed_cols; // Fully-dynamic size + + template using if_zero = std::integral_constant; + static constexpr EigenIndex inner_stride = if_zero::value, + outer_stride = if_zero::value; + static constexpr bool dynamic_stride = inner_stride == Eigen::Dynamic && outer_stride == Eigen::Dynamic; + static constexpr bool requires_row_major = !dynamic_stride && !vector && (row_major ? inner_stride : outer_stride) == 1; + static constexpr bool requires_col_major = !dynamic_stride && !vector && (row_major ? outer_stride : inner_stride) == 1; + + // Takes an input array and determines whether we can make it fit into the Eigen type. If + // the array is a vector, we attempt to fit it into either an Eigen 1xN or Nx1 vector + // (preferring the latter if it will fit in either, i.e. for a fully dynamic matrix type). + static EigenConformable conformable(const array &a) { + const auto dims = a.ndim(); + if (dims < 1 || dims > 2) + return false; + + if (dims == 2) { // Matrix type: require exact match (or dynamic) + + EigenIndex + np_rows = a.shape(0), + np_cols = a.shape(1), + np_rstride = a.strides(0) / static_cast(sizeof(Scalar)), + np_cstride = a.strides(1) / static_cast(sizeof(Scalar)); + if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols)) + return false; + + return {np_rows, np_cols, np_rstride, np_cstride}; + } + + // Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever + // is used, we want the (single) numpy stride value. + const EigenIndex n = a.shape(0), + stride = a.strides(0) / static_cast(sizeof(Scalar)); + + if (vector) { // Eigen type is a compile-time vector + if (fixed && size != n) + return false; // Vector size mismatch + return {rows == 1 ? 1 : n, cols == 1 ? 1 : n, stride}; + } + else if (fixed) { + // The type has a fixed size, but is not a vector: abort + return false; + } + else if (fixed_cols) { + // Since this isn't a vector, cols must be != 1. We allow this only if it exactly + // equals the number of elements (rows is Dynamic, and so 1 row is allowed). + if (cols != n) return false; + return {1, n, stride}; + } + else { + // Otherwise it's either fully dynamic, or column dynamic; both become a column vector + if (fixed_rows && rows != n) return false; + return {n, 1, stride}; + } + } + + static constexpr bool show_writeable = is_eigen_dense_map::value && is_eigen_mutable_map::value; + static constexpr bool show_order = is_eigen_dense_map::value; + static constexpr bool show_c_contiguous = show_order && requires_row_major; + static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; + + static constexpr auto descriptor = + _("numpy.ndarray[") + npy_format_descriptor::name + + _("[") + _(_<(size_t) rows>(), _("m")) + + _(", ") + _(_<(size_t) cols>(), _("n")) + + _("]") + + // For a reference type (e.g. Ref) we have other constraints that might need to be + // satisfied: writeable=True (for a mutable reference), and, depending on the map's stride + // options, possibly f_contiguous or c_contiguous. We include them in the descriptor output + // to provide some hint as to why a TypeError is occurring (otherwise it can be confusing to + // see that a function accepts a 'numpy.ndarray[float64[3,2]]' and an error message that you + // *gave* a numpy.ndarray of the right type and dimensions. + _(", flags.writeable", "") + + _(", flags.c_contiguous", "") + + _(", flags.f_contiguous", "") + + _("]"); +}; + +// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, +// otherwise it'll make a copy. writeable lets you turn off the writeable flag for the array. +template handle eigen_array_cast(typename props::Type const &src, handle base = handle(), bool writeable = true) { + constexpr ssize_t elem_size = sizeof(typename props::Scalar); + array a; + if (props::vector) + a = array({ src.size() }, { elem_size * src.innerStride() }, src.data(), base); + else + a = array({ src.rows(), src.cols() }, { elem_size * src.rowStride(), elem_size * src.colStride() }, + src.data(), base); + + if (!writeable) + array_proxy(a.ptr())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_; + + return a.release(); +} + +// Takes an lvalue ref to some Eigen type and a (python) base object, creating a numpy array that +// reference the Eigen object's data with `base` as the python-registered base class (if omitted, +// the base will be set to None, and lifetime management is up to the caller). The numpy array is +// non-writeable if the given type is const. +template +handle eigen_ref_array(Type &src, handle parent = none()) { + // none here is to get past array's should-we-copy detection, which currently always + // copies when there is no base. Setting the base to None should be harmless. + return eigen_array_cast(src, parent, !std::is_const::value); +} + +// Takes a pointer to some dense, plain Eigen type, builds a capsule around it, then returns a numpy +// array that references the encapsulated data with a python-side reference to the capsule to tie +// its destruction to that of any dependent python objects. Const-ness is determined by whether or +// not the Type of the pointer given is const. +template ::value>> +handle eigen_encapsulate(Type *src) { + capsule base(src, [](void *o) { delete static_cast(o); }); + return eigen_ref_array(*src, base); +} + +// Type caster for regular, dense matrix types (e.g. MatrixXd), but not maps/refs/etc. of dense +// types. +template +struct type_caster::value>> { + using Scalar = typename Type::Scalar; + using props = EigenProps; + + bool load(handle src, bool convert) { + // If we're in no-convert mode, only load if given an array of the correct type + if (!convert && !isinstance>(src)) + return false; + + // Coerce into an array, but don't do type conversion yet; the copy below handles it. + auto buf = array::ensure(src); + + if (!buf) + return false; + + auto dims = buf.ndim(); + if (dims < 1 || dims > 2) + return false; + + auto fits = props::conformable(buf); + if (!fits) + return false; + + // Allocate the new type, then build a numpy reference into it + value = Type(fits.rows, fits.cols); + auto ref = reinterpret_steal(eigen_ref_array(value)); + if (dims == 1) ref = ref.squeeze(); + else if (ref.ndim() == 1) buf = buf.squeeze(); + + int result = detail::npy_api::get().PyArray_CopyInto_(ref.ptr(), buf.ptr()); + + if (result < 0) { // Copy failed! + PyErr_Clear(); + return false; + } + + return true; + } + +private: + + // Cast implementation + template + static handle cast_impl(CType *src, return_value_policy policy, handle parent) { + switch (policy) { + case return_value_policy::take_ownership: + case return_value_policy::automatic: + return eigen_encapsulate(src); + case return_value_policy::move: + return eigen_encapsulate(new CType(std::move(*src))); + case return_value_policy::copy: + return eigen_array_cast(*src); + case return_value_policy::reference: + case return_value_policy::automatic_reference: + return eigen_ref_array(*src); + case return_value_policy::reference_internal: + return eigen_ref_array(*src, parent); + default: + throw cast_error("unhandled return_value_policy: should not happen!"); + }; + } + +public: + + // Normal returned non-reference, non-const value: + static handle cast(Type &&src, return_value_policy /* policy */, handle parent) { + return cast_impl(&src, return_value_policy::move, parent); + } + // If you return a non-reference const, we mark the numpy array readonly: + static handle cast(const Type &&src, return_value_policy /* policy */, handle parent) { + return cast_impl(&src, return_value_policy::move, parent); + } + // lvalue reference return; default (automatic) becomes copy + static handle cast(Type &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast_impl(&src, policy, parent); + } + // const lvalue reference return; default (automatic) becomes copy + static handle cast(const Type &src, return_value_policy policy, handle parent) { + if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) + policy = return_value_policy::copy; + return cast(&src, policy, parent); + } + // non-const pointer return + static handle cast(Type *src, return_value_policy policy, handle parent) { + return cast_impl(src, policy, parent); + } + // const pointer return + static handle cast(const Type *src, return_value_policy policy, handle parent) { + return cast_impl(src, policy, parent); + } + + static constexpr auto name = props::descriptor; + + operator Type*() { return &value; } + operator Type&() { return value; } + operator Type&&() && { return std::move(value); } + template using cast_op_type = movable_cast_op_type; + +private: + Type value; +}; + +// Base class for casting reference/map/block/etc. objects back to python. +template struct eigen_map_caster { +private: + using props = EigenProps; + +public: + + // Directly referencing a ref/map's data is a bit dangerous (whatever the map/ref points to has + // to stay around), but we'll allow it under the assumption that you know what you're doing (and + // have an appropriate keep_alive in place). We return a numpy array pointing directly at the + // ref's data (The numpy array ends up read-only if the ref was to a const matrix type.) Note + // that this means you need to ensure you don't destroy the object in some other way (e.g. with + // an appropriate keep_alive, or with a reference to a statically allocated matrix). + static handle cast(const MapType &src, return_value_policy policy, handle parent) { + switch (policy) { + case return_value_policy::copy: + return eigen_array_cast(src); + case return_value_policy::reference_internal: + return eigen_array_cast(src, parent, is_eigen_mutable_map::value); + case return_value_policy::reference: + case return_value_policy::automatic: + case return_value_policy::automatic_reference: + return eigen_array_cast(src, none(), is_eigen_mutable_map::value); + default: + // move, take_ownership don't make any sense for a ref/map: + pybind11_fail("Invalid return_value_policy for Eigen Map/Ref/Block type"); + } + } + + static constexpr auto name = props::descriptor; + + // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return + // types but not bound arguments). We still provide them (with an explicitly delete) so that + // you end up here if you try anyway. + bool load(handle, bool) = delete; + operator MapType() = delete; + template using cast_op_type = MapType; +}; + +// We can return any map-like object (but can only load Refs, specialized next): +template struct type_caster::value>> + : eigen_map_caster {}; + +// Loader for Ref<...> arguments. See the documentation for info on how to make this work without +// copying (it requires some extra effort in many cases). +template +struct type_caster< + Eigen::Ref, + enable_if_t>::value> +> : public eigen_map_caster> { +private: + using Type = Eigen::Ref; + using props = EigenProps; + using Scalar = typename props::Scalar; + using MapType = Eigen::Map; + using Array = array_t; + static constexpr bool need_writeable = is_eigen_mutable_map::value; + // Delay construction (these have no default constructor) + std::unique_ptr map; + std::unique_ptr ref; + // Our array. When possible, this is just a numpy array pointing to the source data, but + // sometimes we can't avoid copying (e.g. input is not a numpy array at all, has an incompatible + // layout, or is an array of a type that needs to be converted). Using a numpy temporary + // (rather than an Eigen temporary) saves an extra copy when we need both type conversion and + // storage order conversion. (Note that we refuse to use this temporary copy when loading an + // argument for a Ref with M non-const, i.e. a read-write reference). + Array copy_or_ref; +public: + bool load(handle src, bool convert) { + // First check whether what we have is already an array of the right type. If not, we can't + // avoid a copy (because the copy is also going to do type conversion). + bool need_copy = !isinstance(src); + + EigenConformable fits; + if (!need_copy) { + // We don't need a converting copy, but we also need to check whether the strides are + // compatible with the Ref's stride requirements + Array aref = reinterpret_borrow(src); + + if (aref && (!need_writeable || aref.writeable())) { + fits = props::conformable(aref); + if (!fits) return false; // Incompatible dimensions + if (!fits.template stride_compatible()) + need_copy = true; + else + copy_or_ref = std::move(aref); + } + else { + need_copy = true; + } + } + + if (need_copy) { + // We need to copy: If we need a mutable reference, or we're not supposed to convert + // (either because we're in the no-convert overload pass, or because we're explicitly + // instructed not to copy (via `py::arg().noconvert()`) we have to fail loading. + if (!convert || need_writeable) return false; + + Array copy = Array::ensure(src); + if (!copy) return false; + fits = props::conformable(copy); + if (!fits || !fits.template stride_compatible()) + return false; + copy_or_ref = std::move(copy); + loader_life_support::add_patient(copy_or_ref); + } + + ref.reset(); + map.reset(new MapType(data(copy_or_ref), fits.rows, fits.cols, make_stride(fits.stride.outer(), fits.stride.inner()))); + ref.reset(new Type(*map)); + + return true; + } + + operator Type*() { return ref.get(); } + operator Type&() { return *ref; } + template using cast_op_type = pybind11::detail::cast_op_type<_T>; + +private: + template ::value, int> = 0> + Scalar *data(Array &a) { return a.mutable_data(); } + + template ::value, int> = 0> + const Scalar *data(Array &a) { return a.data(); } + + // Attempt to figure out a constructor of `Stride` that will work. + // If both strides are fixed, use a default constructor: + template using stride_ctor_default = bool_constant< + S::InnerStrideAtCompileTime != Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && + std::is_default_constructible::value>; + // Otherwise, if there is a two-index constructor, assume it is (outer,inner) like + // Eigen::Stride, and use it: + template using stride_ctor_dual = bool_constant< + !stride_ctor_default::value && std::is_constructible::value>; + // Otherwise, if there is a one-index constructor, and just one of the strides is dynamic, use + // it (passing whichever stride is dynamic). + template using stride_ctor_outer = bool_constant< + !any_of, stride_ctor_dual>::value && + S::OuterStrideAtCompileTime == Eigen::Dynamic && S::InnerStrideAtCompileTime != Eigen::Dynamic && + std::is_constructible::value>; + template using stride_ctor_inner = bool_constant< + !any_of, stride_ctor_dual>::value && + S::InnerStrideAtCompileTime == Eigen::Dynamic && S::OuterStrideAtCompileTime != Eigen::Dynamic && + std::is_constructible::value>; + + template ::value, int> = 0> + static S make_stride(EigenIndex, EigenIndex) { return S(); } + template ::value, int> = 0> + static S make_stride(EigenIndex outer, EigenIndex inner) { return S(outer, inner); } + template ::value, int> = 0> + static S make_stride(EigenIndex outer, EigenIndex) { return S(outer); } + template ::value, int> = 0> + static S make_stride(EigenIndex, EigenIndex inner) { return S(inner); } + +}; + +// type_caster for special matrix types (e.g. DiagonalMatrix), which are EigenBase, but not +// EigenDense (i.e. they don't have a data(), at least not with the usual matrix layout). +// load() is not supported, but we can cast them into the python domain by first copying to a +// regular Eigen::Matrix, then casting that. +template +struct type_caster::value>> { +protected: + using Matrix = Eigen::Matrix; + using props = EigenProps; +public: + static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { + handle h = eigen_encapsulate(new Matrix(src)); + return h; + } + static handle cast(const Type *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } + + static constexpr auto name = props::descriptor; + + // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return + // types but not bound arguments). We still provide them (with an explicitly delete) so that + // you end up here if you try anyway. + bool load(handle, bool) = delete; + operator Type() = delete; + template using cast_op_type = Type; +}; + +template +struct type_caster::value>> { + typedef typename Type::Scalar Scalar; + typedef remove_reference_t().outerIndexPtr())> StorageIndex; + typedef typename Type::Index Index; + static constexpr bool rowMajor = Type::IsRowMajor; + + bool load(handle src, bool) { + if (!src) + return false; + + auto obj = reinterpret_borrow(src); + object sparse_module = module::import("scipy.sparse"); + object matrix_type = sparse_module.attr( + rowMajor ? "csr_matrix" : "csc_matrix"); + + if (!obj.get_type().is(matrix_type)) { + try { + obj = matrix_type(obj); + } catch (const error_already_set &) { + return false; + } + } + + auto values = array_t((object) obj.attr("data")); + auto innerIndices = array_t((object) obj.attr("indices")); + auto outerIndices = array_t((object) obj.attr("indptr")); + auto shape = pybind11::tuple((pybind11::object) obj.attr("shape")); + auto nnz = obj.attr("nnz").cast(); + + if (!values || !innerIndices || !outerIndices) + return false; + + value = Eigen::MappedSparseMatrix( + shape[0].cast(), shape[1].cast(), nnz, + outerIndices.mutable_data(), innerIndices.mutable_data(), values.mutable_data()); + + return true; + } + + static handle cast(const Type &src, return_value_policy /* policy */, handle /* parent */) { + const_cast(src).makeCompressed(); + + object matrix_type = module::import("scipy.sparse").attr( + rowMajor ? "csr_matrix" : "csc_matrix"); + + array data(src.nonZeros(), src.valuePtr()); + array outerIndices((rowMajor ? src.rows() : src.cols()) + 1, src.outerIndexPtr()); + array innerIndices(src.nonZeros(), src.innerIndexPtr()); + + return matrix_type( + std::make_tuple(data, innerIndices, outerIndices), + std::make_pair(src.rows(), src.cols()) + ).release(); + } + + PYBIND11_TYPE_CASTER(Type, _<(Type::IsRowMajor) != 0>("scipy.sparse.csr_matrix[", "scipy.sparse.csc_matrix[") + + npy_format_descriptor::name + _("]")); +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/thirdparty/pybind11/include/pybind11/embed.h b/thirdparty/pybind11/include/pybind11/embed.h new file mode 100644 index 000000000..f814c783e --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/embed.h @@ -0,0 +1,202 @@ +/* + pybind11/embed.h: Support for embedding the interpreter + + Copyright (c) 2017 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include "eval.h" + +#if defined(PYPY_VERSION) +# error Embedding the interpreter is not supported with PyPy +#endif + +#if PY_MAJOR_VERSION >= 3 +# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" PyObject *pybind11_init_impl_##name(); \ + extern "C" PyObject *pybind11_init_impl_##name() { \ + return pybind11_init_wrapper_##name(); \ + } +#else +# define PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + extern "C" void pybind11_init_impl_##name(); \ + extern "C" void pybind11_init_impl_##name() { \ + pybind11_init_wrapper_##name(); \ + } +#endif + +/** \rst + Add a new module to the table of builtins for the interpreter. Must be + defined in global scope. The first macro parameter is the name of the + module (without quotes). The second parameter is the variable which will + be used as the interface to add functions and classes to the module. + + .. code-block:: cpp + + PYBIND11_EMBEDDED_MODULE(example, m) { + // ... initialize functions and classes here + m.def("foo", []() { + return "Hello, World!"; + }); + } + \endrst */ +#define PYBIND11_EMBEDDED_MODULE(name, variable) \ + static void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &); \ + static PyObject PYBIND11_CONCAT(*pybind11_init_wrapper_, name)() { \ + auto m = pybind11::module(PYBIND11_TOSTRING(name)); \ + try { \ + PYBIND11_CONCAT(pybind11_init_, name)(m); \ + return m.ptr(); \ + } catch (pybind11::error_already_set &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } catch (const std::exception &e) { \ + PyErr_SetString(PyExc_ImportError, e.what()); \ + return nullptr; \ + } \ + } \ + PYBIND11_EMBEDDED_MODULE_IMPL(name) \ + pybind11::detail::embedded_module name(PYBIND11_TOSTRING(name), \ + PYBIND11_CONCAT(pybind11_init_impl_, name)); \ + void PYBIND11_CONCAT(pybind11_init_, name)(pybind11::module &variable) + + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Python 2.7/3.x compatible version of `PyImport_AppendInittab` and error checks. +struct embedded_module { +#if PY_MAJOR_VERSION >= 3 + using init_t = PyObject *(*)(); +#else + using init_t = void (*)(); +#endif + embedded_module(const char *name, init_t init) { + if (Py_IsInitialized()) + pybind11_fail("Can't add new modules after the interpreter has been initialized"); + + auto result = PyImport_AppendInittab(name, init); + if (result == -1) + pybind11_fail("Insufficient memory to add a new module"); + } +}; + +NAMESPACE_END(detail) + +/** \rst + Initialize the Python interpreter. No other pybind11 or CPython API functions can be + called before this is done; with the exception of `PYBIND11_EMBEDDED_MODULE`. The + optional parameter can be used to skip the registration of signal handlers (see the + `Python documentation`_ for details). Calling this function again after the interpreter + has already been initialized is a fatal error. + + If initializing the Python interpreter fails, then the program is terminated. (This + is controlled by the CPython runtime and is an exception to pybind11's normal behavior + of throwing exceptions on errors.) + + .. _Python documentation: https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx + \endrst */ +inline void initialize_interpreter(bool init_signal_handlers = true) { + if (Py_IsInitialized()) + pybind11_fail("The interpreter is already running"); + + Py_InitializeEx(init_signal_handlers ? 1 : 0); + + // Make .py files in the working directory available by default + module::import("sys").attr("path").cast().append("."); +} + +/** \rst + Shut down the Python interpreter. No pybind11 or CPython API functions can be called + after this. In addition, pybind11 objects must not outlive the interpreter: + + .. code-block:: cpp + + { // BAD + py::initialize_interpreter(); + auto hello = py::str("Hello, World!"); + py::finalize_interpreter(); + } // <-- BOOM, hello's destructor is called after interpreter shutdown + + { // GOOD + py::initialize_interpreter(); + { // scoped + auto hello = py::str("Hello, World!"); + } // <-- OK, hello is cleaned up properly + py::finalize_interpreter(); + } + + { // BETTER + py::scoped_interpreter guard{}; + auto hello = py::str("Hello, World!"); + } + + .. warning:: + + The interpreter can be restarted by calling `initialize_interpreter` again. + Modules created using pybind11 can be safely re-initialized. However, Python + itself cannot completely unload binary extension modules and there are several + caveats with regard to interpreter restarting. All the details can be found + in the CPython documentation. In short, not all interpreter memory may be + freed, either due to reference cycles or user-created global data. + + \endrst */ +inline void finalize_interpreter() { + handle builtins(PyEval_GetBuiltins()); + const char *id = PYBIND11_INTERNALS_ID; + + // Get the internals pointer (without creating it if it doesn't exist). It's possible for the + // internals to be created during Py_Finalize() (e.g. if a py::capsule calls `get_internals()` + // during destruction), so we get the pointer-pointer here and check it after Py_Finalize(). + detail::internals **internals_ptr_ptr = detail::get_internals_pp(); + // It could also be stashed in builtins, so look there too: + if (builtins.contains(id) && isinstance(builtins[id])) + internals_ptr_ptr = capsule(builtins[id]); + + Py_Finalize(); + + if (internals_ptr_ptr) { + delete *internals_ptr_ptr; + *internals_ptr_ptr = nullptr; + } +} + +/** \rst + Scope guard version of `initialize_interpreter` and `finalize_interpreter`. + This a move-only guard and only a single instance can exist. + + .. code-block:: cpp + + #include + + int main() { + py::scoped_interpreter guard{}; + py::print(Hello, World!); + } // <-- interpreter shutdown + \endrst */ +class scoped_interpreter { +public: + scoped_interpreter(bool init_signal_handlers = true) { + initialize_interpreter(init_signal_handlers); + } + + scoped_interpreter(const scoped_interpreter &) = delete; + scoped_interpreter(scoped_interpreter &&other) noexcept { other.is_valid = false; } + scoped_interpreter &operator=(const scoped_interpreter &) = delete; + scoped_interpreter &operator=(scoped_interpreter &&) = delete; + + ~scoped_interpreter() { + if (is_valid) + finalize_interpreter(); + } + +private: + bool is_valid = true; +}; + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/eval.h b/thirdparty/pybind11/include/pybind11/eval.h new file mode 100644 index 000000000..ea85ba1db --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/eval.h @@ -0,0 +1,117 @@ +/* + pybind11/exec.h: Support for evaluating Python expressions and statements + from strings and files + + Copyright (c) 2016 Klemens Morgenstern and + Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +enum eval_mode { + /// Evaluate a string containing an isolated expression + eval_expr, + + /// Evaluate a string containing a single statement. Returns \c none + eval_single_statement, + + /// Evaluate a string containing a sequence of statement. Returns \c none + eval_statements +}; + +template +object eval(str expr, object global = globals(), object local = object()) { + if (!local) + local = global; + + /* PyRun_String does not accept a PyObject / encoding specifier, + this seems to be the only alternative */ + std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr()); + if (!result) + throw error_already_set(); + return reinterpret_steal(result); +} + +template +object eval(const char (&s)[N], object global = globals(), object local = object()) { + /* Support raw string literals by removing common leading whitespace */ + auto expr = (s[0] == '\n') ? str(module::import("textwrap").attr("dedent")(s)) + : str(s); + return eval(expr, global, local); +} + +inline void exec(str expr, object global = globals(), object local = object()) { + eval(expr, global, local); +} + +template +void exec(const char (&s)[N], object global = globals(), object local = object()) { + eval(s, global, local); +} + +template +object eval_file(str fname, object global = globals(), object local = object()) { + if (!local) + local = global; + + int start; + switch (mode) { + case eval_expr: start = Py_eval_input; break; + case eval_single_statement: start = Py_single_input; break; + case eval_statements: start = Py_file_input; break; + default: pybind11_fail("invalid evaluation mode"); + } + + int closeFile = 1; + std::string fname_str = (std::string) fname; +#if PY_VERSION_HEX >= 0x03040000 + FILE *f = _Py_fopen_obj(fname.ptr(), "r"); +#elif PY_VERSION_HEX >= 0x03000000 + FILE *f = _Py_fopen(fname.ptr(), "r"); +#else + /* No unicode support in open() :( */ + auto fobj = reinterpret_steal(PyFile_FromString( + const_cast(fname_str.c_str()), + const_cast("r"))); + FILE *f = nullptr; + if (fobj) + f = PyFile_AsFile(fobj.ptr()); + closeFile = 0; +#endif + if (!f) { + PyErr_Clear(); + pybind11_fail("File \"" + fname_str + "\" could not be opened!"); + } + +#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION) + PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(), + local.ptr()); + (void) closeFile; +#else + PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(), + local.ptr(), closeFile); +#endif + + if (!result) + throw error_already_set(); + return reinterpret_steal(result); +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/functional.h b/thirdparty/pybind11/include/pybind11/functional.h new file mode 100644 index 000000000..f8bda6483 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/functional.h @@ -0,0 +1,101 @@ +/* + pybind11/functional.h: std::function<> support + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template +struct type_caster> { + using type = std::function; + using retval_type = conditional_t::value, void_type, Return>; + using function_type = Return (*) (Args...); + +public: + bool load(handle src, bool convert) { + if (src.is_none()) { + // Defer accepting None to other overloads (if we aren't in convert mode): + if (!convert) return false; + return true; + } + + if (!isinstance(src)) + return false; + + auto func = reinterpret_borrow(src); + + /* + When passing a C++ function as an argument to another C++ + function via Python, every function call would normally involve + a full C++ -> Python -> C++ roundtrip, which can be prohibitive. + Here, we try to at least detect the case where the function is + stateless (i.e. function pointer or lambda function without + captured variables), in which case the roundtrip can be avoided. + */ + if (auto cfunc = func.cpp_function()) { + auto c = reinterpret_borrow(PyCFunction_GET_SELF(cfunc.ptr())); + auto rec = (function_record *) c; + + if (rec && rec->is_stateless && + same_type(typeid(function_type), *reinterpret_cast(rec->data[1]))) { + struct capture { function_type f; }; + value = ((capture *) &rec->data)->f; + return true; + } + } + + // ensure GIL is held during functor destruction + struct func_handle { + function f; + func_handle(function&& f_) : f(std::move(f_)) {} + func_handle(const func_handle&) = default; + ~func_handle() { + gil_scoped_acquire acq; + function kill_f(std::move(f)); + } + }; + + // to emulate 'move initialization capture' in C++11 + struct func_wrapper { + func_handle hfunc; + func_wrapper(func_handle&& hf): hfunc(std::move(hf)) {} + Return operator()(Args... args) const { + gil_scoped_acquire acq; + object retval(hfunc.f(std::forward(args)...)); + /* Visual studio 2015 parser issue: need parentheses around this expression */ + return (retval.template cast()); + } + }; + + value = func_wrapper(func_handle(std::move(func))); + return true; + } + + template + static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { + if (!f_) + return none().inc_ref(); + + auto result = f_.template target(); + if (result) + return cpp_function(*result, policy).release(); + else + return cpp_function(std::forward(f_), policy).release(); + } + + PYBIND11_TYPE_CASTER(type, _("Callable[[") + concat(make_caster::name...) + _("], ") + + make_caster::name + _("]")); +}; + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/iostream.h b/thirdparty/pybind11/include/pybind11/iostream.h new file mode 100644 index 000000000..c43b7c93a --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/iostream.h @@ -0,0 +1,209 @@ +/* + pybind11/iostream.h -- Tools to assist with redirecting cout and cerr to Python + + Copyright (c) 2017 Henry F. Schreiner + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +#include +#include +#include +#include +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +// Buffer that writes to Python instead of C++ +class pythonbuf : public std::streambuf { +private: + using traits_type = std::streambuf::traits_type; + + const size_t buf_size; + std::unique_ptr d_buffer; + object pywrite; + object pyflush; + + int overflow(int c) { + if (!traits_type::eq_int_type(c, traits_type::eof())) { + *pptr() = traits_type::to_char_type(c); + pbump(1); + } + return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof(); + } + + int sync() { + if (pbase() != pptr()) { + // This subtraction cannot be negative, so dropping the sign + str line(pbase(), static_cast(pptr() - pbase())); + + { + gil_scoped_acquire tmp; + pywrite(line); + pyflush(); + } + + setp(pbase(), epptr()); + } + return 0; + } + +public: + + pythonbuf(object pyostream, size_t buffer_size = 1024) + : buf_size(buffer_size), + d_buffer(new char[buf_size]), + pywrite(pyostream.attr("write")), + pyflush(pyostream.attr("flush")) { + setp(d_buffer.get(), d_buffer.get() + buf_size - 1); + } + + pythonbuf(pythonbuf&&) = default; + + /// Sync before destroy + ~pythonbuf() { + sync(); + } +}; + +NAMESPACE_END(detail) + + +/** \rst + This a move-only guard that redirects output. + + .. code-block:: cpp + + #include + + ... + + { + py::scoped_ostream_redirect output; + std::cout << "Hello, World!"; // Python stdout + } // <-- return std::cout to normal + + You can explicitly pass the c++ stream and the python object, + for example to guard stderr instead. + + .. code-block:: cpp + + { + py::scoped_ostream_redirect output{std::cerr, py::module::import("sys").attr("stderr")}; + std::cerr << "Hello, World!"; + } + \endrst */ +class scoped_ostream_redirect { +protected: + std::streambuf *old; + std::ostream &costream; + detail::pythonbuf buffer; + +public: + scoped_ostream_redirect( + std::ostream &costream = std::cout, + object pyostream = module::import("sys").attr("stdout")) + : costream(costream), buffer(pyostream) { + old = costream.rdbuf(&buffer); + } + + ~scoped_ostream_redirect() { + costream.rdbuf(old); + } + + scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; + scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; + scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete; + scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; +}; + + +/** \rst + Like `scoped_ostream_redirect`, but redirects cerr by default. This class + is provided primary to make ``py::call_guard`` easier to make. + + .. code-block:: cpp + + m.def("noisy_func", &noisy_func, + py::call_guard()); + +\endrst */ +class scoped_estream_redirect : public scoped_ostream_redirect { +public: + scoped_estream_redirect( + std::ostream &costream = std::cerr, + object pyostream = module::import("sys").attr("stderr")) + : scoped_ostream_redirect(costream,pyostream) {} +}; + + +NAMESPACE_BEGIN(detail) + +// Class to redirect output as a context manager. C++ backend. +class OstreamRedirect { + bool do_stdout_; + bool do_stderr_; + std::unique_ptr redirect_stdout; + std::unique_ptr redirect_stderr; + +public: + OstreamRedirect(bool do_stdout = true, bool do_stderr = true) + : do_stdout_(do_stdout), do_stderr_(do_stderr) {} + + void enter() { + if (do_stdout_) + redirect_stdout.reset(new scoped_ostream_redirect()); + if (do_stderr_) + redirect_stderr.reset(new scoped_estream_redirect()); + } + + void exit() { + redirect_stdout.reset(); + redirect_stderr.reset(); + } +}; + +NAMESPACE_END(detail) + +/** \rst + This is a helper function to add a C++ redirect context manager to Python + instead of using a C++ guard. To use it, add the following to your binding code: + + .. code-block:: cpp + + #include + + ... + + py::add_ostream_redirect(m, "ostream_redirect"); + + You now have a Python context manager that redirects your output: + + .. code-block:: python + + with m.ostream_redirect(): + m.print_to_cout_function() + + This manager can optionally be told which streams to operate on: + + .. code-block:: python + + with m.ostream_redirect(stdout=true, stderr=true): + m.noisy_function_with_error_printing() + + \endrst */ +inline class_ add_ostream_redirect(module m, std::string name = "ostream_redirect") { + return class_(m, name.c_str(), module_local()) + .def(init(), arg("stdout")=true, arg("stderr")=true) + .def("__enter__", &detail::OstreamRedirect::enter) + .def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); }); +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/numpy.h b/thirdparty/pybind11/include/pybind11/numpy.h new file mode 100644 index 000000000..a67452105 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/numpy.h @@ -0,0 +1,1643 @@ +/* + pybind11/numpy.h: Basic NumPy support, vectorize() wrapper + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include "complex.h" +#include "detail/common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +/* This will be true on all flat address space platforms and allows us to reduce the + whole npy_intp / ssize_t / Py_intptr_t business down to just ssize_t for all size + and dimension types (e.g. shape, strides, indexing), instead of inflicting this + upon the library user. */ +static_assert(sizeof(ssize_t) == sizeof(Py_intptr_t), "ssize_t != Py_intptr_t"); + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +class array; // Forward declaration + +NAMESPACE_BEGIN(detail) +template struct npy_format_descriptor; + +struct PyArrayDescr_Proxy { + PyObject_HEAD + PyObject *typeobj; + char kind; + char type; + char byteorder; + char flags; + int type_num; + int elsize; + int alignment; + char *subarray; + PyObject *fields; + PyObject *names; +}; + +struct PyArray_Proxy { + PyObject_HEAD + char *data; + int nd; + ssize_t *dimensions; + ssize_t *strides; + PyObject *base; + PyObject *descr; + int flags; +}; + +struct PyVoidScalarObject_Proxy { + PyObject_VAR_HEAD + char *obval; + PyArrayDescr_Proxy *descr; + int flags; + PyObject *base; +}; + +struct numpy_type_info { + PyObject* dtype_ptr; + std::string format_str; +}; + +struct numpy_internals { + std::unordered_map registered_dtypes; + + numpy_type_info *get_type_info(const std::type_info& tinfo, bool throw_if_missing = true) { + auto it = registered_dtypes.find(std::type_index(tinfo)); + if (it != registered_dtypes.end()) + return &(it->second); + if (throw_if_missing) + pybind11_fail(std::string("NumPy type info missing for ") + tinfo.name()); + return nullptr; + } + + template numpy_type_info *get_type_info(bool throw_if_missing = true) { + return get_type_info(typeid(typename std::remove_cv::type), throw_if_missing); + } +}; + +inline PYBIND11_NOINLINE void load_numpy_internals(numpy_internals* &ptr) { + ptr = &get_or_create_shared_data("_numpy_internals"); +} + +inline numpy_internals& get_numpy_internals() { + static numpy_internals* ptr = nullptr; + if (!ptr) + load_numpy_internals(ptr); + return *ptr; +} + +template struct same_size { + template using as = bool_constant; +}; + +template constexpr int platform_lookup() { return -1; } + +// Lookup a type according to its size, and return a value corresponding to the NumPy typenum. +template +constexpr int platform_lookup(int I, Ints... Is) { + return sizeof(Concrete) == sizeof(T) ? I : platform_lookup(Is...); +} + +struct npy_api { + enum constants { + NPY_ARRAY_C_CONTIGUOUS_ = 0x0001, + NPY_ARRAY_F_CONTIGUOUS_ = 0x0002, + NPY_ARRAY_OWNDATA_ = 0x0004, + NPY_ARRAY_FORCECAST_ = 0x0010, + NPY_ARRAY_ENSUREARRAY_ = 0x0040, + NPY_ARRAY_ALIGNED_ = 0x0100, + NPY_ARRAY_WRITEABLE_ = 0x0400, + NPY_BOOL_ = 0, + NPY_BYTE_, NPY_UBYTE_, + NPY_SHORT_, NPY_USHORT_, + NPY_INT_, NPY_UINT_, + NPY_LONG_, NPY_ULONG_, + NPY_LONGLONG_, NPY_ULONGLONG_, + NPY_FLOAT_, NPY_DOUBLE_, NPY_LONGDOUBLE_, + NPY_CFLOAT_, NPY_CDOUBLE_, NPY_CLONGDOUBLE_, + NPY_OBJECT_ = 17, + NPY_STRING_, NPY_UNICODE_, NPY_VOID_, + // Platform-dependent normalization + NPY_INT8_ = NPY_BYTE_, + NPY_UINT8_ = NPY_UBYTE_, + NPY_INT16_ = NPY_SHORT_, + NPY_UINT16_ = NPY_USHORT_, + // `npy_common.h` defines the integer aliases. In order, it checks: + // NPY_BITSOF_LONG, NPY_BITSOF_LONGLONG, NPY_BITSOF_INT, NPY_BITSOF_SHORT, NPY_BITSOF_CHAR + // and assigns the alias to the first matching size, so we should check in this order. + NPY_INT32_ = platform_lookup( + NPY_LONG_, NPY_INT_, NPY_SHORT_), + NPY_UINT32_ = platform_lookup( + NPY_ULONG_, NPY_UINT_, NPY_USHORT_), + NPY_INT64_ = platform_lookup( + NPY_LONG_, NPY_LONGLONG_, NPY_INT_), + NPY_UINT64_ = platform_lookup( + NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_), + }; + + typedef struct { + Py_intptr_t *ptr; + int len; + } PyArray_Dims; + + static npy_api& get() { + static npy_api api = lookup(); + return api; + } + + bool PyArray_Check_(PyObject *obj) const { + return (bool) PyObject_TypeCheck(obj, PyArray_Type_); + } + bool PyArrayDescr_Check_(PyObject *obj) const { + return (bool) PyObject_TypeCheck(obj, PyArrayDescr_Type_); + } + + unsigned int (*PyArray_GetNDArrayCFeatureVersion_)(); + PyObject *(*PyArray_DescrFromType_)(int); + PyObject *(*PyArray_NewFromDescr_) + (PyTypeObject *, PyObject *, int, Py_intptr_t *, + Py_intptr_t *, void *, int, PyObject *); + PyObject *(*PyArray_DescrNewFromType_)(int); + int (*PyArray_CopyInto_)(PyObject *, PyObject *); + PyObject *(*PyArray_NewCopy_)(PyObject *, int); + PyTypeObject *PyArray_Type_; + PyTypeObject *PyVoidArrType_Type_; + PyTypeObject *PyArrayDescr_Type_; + PyObject *(*PyArray_DescrFromScalar_)(PyObject *); + PyObject *(*PyArray_FromAny_) (PyObject *, PyObject *, int, int, int, PyObject *); + int (*PyArray_DescrConverter_) (PyObject *, PyObject **); + bool (*PyArray_EquivTypes_) (PyObject *, PyObject *); + int (*PyArray_GetArrayParamsFromObject_)(PyObject *, PyObject *, char, PyObject **, int *, + Py_ssize_t *, PyObject **, PyObject *); + PyObject *(*PyArray_Squeeze_)(PyObject *); + int (*PyArray_SetBaseObject_)(PyObject *, PyObject *); + PyObject* (*PyArray_Resize_)(PyObject*, PyArray_Dims*, int, int); +private: + enum functions { + API_PyArray_GetNDArrayCFeatureVersion = 211, + API_PyArray_Type = 2, + API_PyArrayDescr_Type = 3, + API_PyVoidArrType_Type = 39, + API_PyArray_DescrFromType = 45, + API_PyArray_DescrFromScalar = 57, + API_PyArray_FromAny = 69, + API_PyArray_Resize = 80, + API_PyArray_CopyInto = 82, + API_PyArray_NewCopy = 85, + API_PyArray_NewFromDescr = 94, + API_PyArray_DescrNewFromType = 9, + API_PyArray_DescrConverter = 174, + API_PyArray_EquivTypes = 182, + API_PyArray_GetArrayParamsFromObject = 278, + API_PyArray_Squeeze = 136, + API_PyArray_SetBaseObject = 282 + }; + + static npy_api lookup() { + module m = module::import("numpy.core.multiarray"); + auto c = m.attr("_ARRAY_API"); +#if PY_MAJOR_VERSION >= 3 + void **api_ptr = (void **) PyCapsule_GetPointer(c.ptr(), NULL); +#else + void **api_ptr = (void **) PyCObject_AsVoidPtr(c.ptr()); +#endif + npy_api api; +#define DECL_NPY_API(Func) api.Func##_ = (decltype(api.Func##_)) api_ptr[API_##Func]; + DECL_NPY_API(PyArray_GetNDArrayCFeatureVersion); + if (api.PyArray_GetNDArrayCFeatureVersion_() < 0x7) + pybind11_fail("pybind11 numpy support requires numpy >= 1.7.0"); + DECL_NPY_API(PyArray_Type); + DECL_NPY_API(PyVoidArrType_Type); + DECL_NPY_API(PyArrayDescr_Type); + DECL_NPY_API(PyArray_DescrFromType); + DECL_NPY_API(PyArray_DescrFromScalar); + DECL_NPY_API(PyArray_FromAny); + DECL_NPY_API(PyArray_Resize); + DECL_NPY_API(PyArray_CopyInto); + DECL_NPY_API(PyArray_NewCopy); + DECL_NPY_API(PyArray_NewFromDescr); + DECL_NPY_API(PyArray_DescrNewFromType); + DECL_NPY_API(PyArray_DescrConverter); + DECL_NPY_API(PyArray_EquivTypes); + DECL_NPY_API(PyArray_GetArrayParamsFromObject); + DECL_NPY_API(PyArray_Squeeze); + DECL_NPY_API(PyArray_SetBaseObject); +#undef DECL_NPY_API + return api; + } +}; + +inline PyArray_Proxy* array_proxy(void* ptr) { + return reinterpret_cast(ptr); +} + +inline const PyArray_Proxy* array_proxy(const void* ptr) { + return reinterpret_cast(ptr); +} + +inline PyArrayDescr_Proxy* array_descriptor_proxy(PyObject* ptr) { + return reinterpret_cast(ptr); +} + +inline const PyArrayDescr_Proxy* array_descriptor_proxy(const PyObject* ptr) { + return reinterpret_cast(ptr); +} + +inline bool check_flags(const void* ptr, int flag) { + return (flag == (array_proxy(ptr)->flags & flag)); +} + +template struct is_std_array : std::false_type { }; +template struct is_std_array> : std::true_type { }; +template struct is_complex : std::false_type { }; +template struct is_complex> : std::true_type { }; + +template struct array_info_scalar { + typedef T type; + static constexpr bool is_array = false; + static constexpr bool is_empty = false; + static constexpr auto extents = _(""); + static void append_extents(list& /* shape */) { } +}; +// Computes underlying type and a comma-separated list of extents for array +// types (any mix of std::array and built-in arrays). An array of char is +// treated as scalar because it gets special handling. +template struct array_info : array_info_scalar { }; +template struct array_info> { + using type = typename array_info::type; + static constexpr bool is_array = true; + static constexpr bool is_empty = (N == 0) || array_info::is_empty; + static constexpr size_t extent = N; + + // appends the extents to shape + static void append_extents(list& shape) { + shape.append(N); + array_info::append_extents(shape); + } + + static constexpr auto extents = _::is_array>( + concat(_(), array_info::extents), _() + ); +}; +// For numpy we have special handling for arrays of characters, so we don't include +// the size in the array extents. +template struct array_info : array_info_scalar { }; +template struct array_info> : array_info_scalar> { }; +template struct array_info : array_info> { }; +template using remove_all_extents_t = typename array_info::type; + +template using is_pod_struct = all_of< + std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type +#if !defined(__GNUG__) || defined(_LIBCPP_VERSION) || defined(_GLIBCXX_USE_CXX11_ABI) + // _GLIBCXX_USE_CXX11_ABI indicates that we're using libstdc++ from GCC 5 or newer, independent + // of the actual compiler (Clang can also use libstdc++, but it always defines __GNUC__ == 4). + std::is_trivially_copyable, +#else + // GCC 4 doesn't implement is_trivially_copyable, so approximate it + std::is_trivially_destructible, + satisfies_any_of, +#endif + satisfies_none_of +>; + +template ssize_t byte_offset_unsafe(const Strides &) { return 0; } +template +ssize_t byte_offset_unsafe(const Strides &strides, ssize_t i, Ix... index) { + return i * strides[Dim] + byte_offset_unsafe(strides, index...); +} + +/** + * Proxy class providing unsafe, unchecked const access to array data. This is constructed through + * the `unchecked()` method of `array` or the `unchecked()` method of `array_t`. `Dims` + * will be -1 for dimensions determined at runtime. + */ +template +class unchecked_reference { +protected: + static constexpr bool Dynamic = Dims < 0; + const unsigned char *data_; + // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to + // make large performance gains on big, nested loops, but requires compile-time dimensions + conditional_t> + shape_, strides_; + const ssize_t dims_; + + friend class pybind11::array; + // Constructor for compile-time dimensions: + template + unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t) + : data_{reinterpret_cast(data)}, dims_{Dims} { + for (size_t i = 0; i < (size_t) dims_; i++) { + shape_[i] = shape[i]; + strides_[i] = strides[i]; + } + } + // Constructor for runtime dimensions: + template + unchecked_reference(const void *data, const ssize_t *shape, const ssize_t *strides, enable_if_t dims) + : data_{reinterpret_cast(data)}, shape_{shape}, strides_{strides}, dims_{dims} {} + +public: + /** + * Unchecked const reference access to data at the given indices. For a compile-time known + * number of dimensions, this requires the correct number of arguments; for run-time + * dimensionality, this is not checked (and so is up to the caller to use safely). + */ + template const T &operator()(Ix... index) const { + static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, + "Invalid number of indices for unchecked array reference"); + return *reinterpret_cast(data_ + byte_offset_unsafe(strides_, ssize_t(index)...)); + } + /** + * Unchecked const reference access to data; this operator only participates if the reference + * is to a 1-dimensional array. When present, this is exactly equivalent to `obj(index)`. + */ + template > + const T &operator[](ssize_t index) const { return operator()(index); } + + /// Pointer access to the data at the given indices. + template const T *data(Ix... ix) const { return &operator()(ssize_t(ix)...); } + + /// Returns the item size, i.e. sizeof(T) + constexpr static ssize_t itemsize() { return sizeof(T); } + + /// Returns the shape (i.e. size) of dimension `dim` + ssize_t shape(ssize_t dim) const { return shape_[(size_t) dim]; } + + /// Returns the number of dimensions of the array + ssize_t ndim() const { return dims_; } + + /// Returns the total number of elements in the referenced array, i.e. the product of the shapes + template + enable_if_t size() const { + return std::accumulate(shape_.begin(), shape_.end(), (ssize_t) 1, std::multiplies()); + } + template + enable_if_t size() const { + return std::accumulate(shape_, shape_ + ndim(), (ssize_t) 1, std::multiplies()); + } + + /// Returns the total number of bytes used by the referenced data. Note that the actual span in + /// memory may be larger if the referenced array has non-contiguous strides (e.g. for a slice). + ssize_t nbytes() const { + return size() * itemsize(); + } +}; + +template +class unchecked_mutable_reference : public unchecked_reference { + friend class pybind11::array; + using ConstBase = unchecked_reference; + using ConstBase::ConstBase; + using ConstBase::Dynamic; +public: + /// Mutable, unchecked access to data at the given indices. + template T& operator()(Ix... index) { + static_assert(ssize_t{sizeof...(Ix)} == Dims || Dynamic, + "Invalid number of indices for unchecked array reference"); + return const_cast(ConstBase::operator()(index...)); + } + /** + * Mutable, unchecked access data at the given index; this operator only participates if the + * reference is to a 1-dimensional array (or has runtime dimensions). When present, this is + * exactly equivalent to `obj(index)`. + */ + template > + T &operator[](ssize_t index) { return operator()(index); } + + /// Mutable pointer access to the data at the given indices. + template T *mutable_data(Ix... ix) { return &operator()(ssize_t(ix)...); } +}; + +template +struct type_caster> { + static_assert(Dim == 0 && Dim > 0 /* always fail */, "unchecked array proxy object is not castable"); +}; +template +struct type_caster> : type_caster> {}; + +NAMESPACE_END(detail) + +class dtype : public object { +public: + PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_); + + explicit dtype(const buffer_info &info) { + dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format))); + // If info.itemsize == 0, use the value calculated from the format string + m_ptr = descr.strip_padding(info.itemsize ? info.itemsize : descr.itemsize()).release().ptr(); + } + + explicit dtype(const std::string &format) { + m_ptr = from_args(pybind11::str(format)).release().ptr(); + } + + dtype(const char *format) : dtype(std::string(format)) { } + + dtype(list names, list formats, list offsets, ssize_t itemsize) { + dict args; + args["names"] = names; + args["formats"] = formats; + args["offsets"] = offsets; + args["itemsize"] = pybind11::int_(itemsize); + m_ptr = from_args(args).release().ptr(); + } + + /// This is essentially the same as calling numpy.dtype(args) in Python. + static dtype from_args(object args) { + PyObject *ptr = nullptr; + if (!detail::npy_api::get().PyArray_DescrConverter_(args.ptr(), &ptr) || !ptr) + throw error_already_set(); + return reinterpret_steal(ptr); + } + + /// Return dtype associated with a C++ type. + template static dtype of() { + return detail::npy_format_descriptor::type>::dtype(); + } + + /// Size of the data type in bytes. + ssize_t itemsize() const { + return detail::array_descriptor_proxy(m_ptr)->elsize; + } + + /// Returns true for structured data types. + bool has_fields() const { + return detail::array_descriptor_proxy(m_ptr)->names != nullptr; + } + + /// Single-character type code. + char kind() const { + return detail::array_descriptor_proxy(m_ptr)->kind; + } + +private: + static object _dtype_from_pep3118() { + static PyObject *obj = module::import("numpy.core._internal") + .attr("_dtype_from_pep3118").cast().release().ptr(); + return reinterpret_borrow(obj); + } + + dtype strip_padding(ssize_t itemsize) { + // Recursively strip all void fields with empty names that are generated for + // padding fields (as of NumPy v1.11). + if (!has_fields()) + return *this; + + struct field_descr { PYBIND11_STR_TYPE name; object format; pybind11::int_ offset; }; + std::vector field_descriptors; + + for (auto field : attr("fields").attr("items")()) { + auto spec = field.cast(); + auto name = spec[0].cast(); + auto format = spec[1].cast()[0].cast(); + auto offset = spec[1].cast()[1].cast(); + if (!len(name) && format.kind() == 'V') + continue; + field_descriptors.push_back({(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset}); + } + + std::sort(field_descriptors.begin(), field_descriptors.end(), + [](const field_descr& a, const field_descr& b) { + return a.offset.cast() < b.offset.cast(); + }); + + list names, formats, offsets; + for (auto& descr : field_descriptors) { + names.append(descr.name); + formats.append(descr.format); + offsets.append(descr.offset); + } + return dtype(names, formats, offsets, itemsize); + } +}; + +class array : public buffer { +public: + PYBIND11_OBJECT_CVT(array, buffer, detail::npy_api::get().PyArray_Check_, raw_array) + + enum { + c_style = detail::npy_api::NPY_ARRAY_C_CONTIGUOUS_, + f_style = detail::npy_api::NPY_ARRAY_F_CONTIGUOUS_, + forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_ + }; + + array() : array({{0}}, static_cast(nullptr)) {} + + using ShapeContainer = detail::any_container; + using StridesContainer = detail::any_container; + + // Constructs an array taking shape/strides from arbitrary container types + array(const pybind11::dtype &dt, ShapeContainer shape, StridesContainer strides, + const void *ptr = nullptr, handle base = handle()) { + + if (strides->empty()) + *strides = c_strides(*shape, dt.itemsize()); + + auto ndim = shape->size(); + if (ndim != strides->size()) + pybind11_fail("NumPy: shape ndim doesn't match strides ndim"); + auto descr = dt; + + int flags = 0; + if (base && ptr) { + if (isinstance(base)) + /* Copy flags from base (except ownership bit) */ + flags = reinterpret_borrow(base).flags() & ~detail::npy_api::NPY_ARRAY_OWNDATA_; + else + /* Writable by default, easy to downgrade later on if needed */ + flags = detail::npy_api::NPY_ARRAY_WRITEABLE_; + } + + auto &api = detail::npy_api::get(); + auto tmp = reinterpret_steal(api.PyArray_NewFromDescr_( + api.PyArray_Type_, descr.release().ptr(), (int) ndim, shape->data(), strides->data(), + const_cast(ptr), flags, nullptr)); + if (!tmp) + throw error_already_set(); + if (ptr) { + if (base) { + api.PyArray_SetBaseObject_(tmp.ptr(), base.inc_ref().ptr()); + } else { + tmp = reinterpret_steal(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */)); + } + } + m_ptr = tmp.release().ptr(); + } + + array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle()) + : array(dt, std::move(shape), {}, ptr, base) { } + + template ::value && !std::is_same::value>> + array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle()) + : array(dt, {{count}}, ptr, base) { } + + template + array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle()) + : array(pybind11::dtype::of(), std::move(shape), std::move(strides), ptr, base) { } + + template + array(ShapeContainer shape, const T *ptr, handle base = handle()) + : array(std::move(shape), {}, ptr, base) { } + + template + explicit array(ssize_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { } + + explicit array(const buffer_info &info) + : array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { } + + /// Array descriptor (dtype) + pybind11::dtype dtype() const { + return reinterpret_borrow(detail::array_proxy(m_ptr)->descr); + } + + /// Total number of elements + ssize_t size() const { + return std::accumulate(shape(), shape() + ndim(), (ssize_t) 1, std::multiplies()); + } + + /// Byte size of a single element + ssize_t itemsize() const { + return detail::array_descriptor_proxy(detail::array_proxy(m_ptr)->descr)->elsize; + } + + /// Total number of bytes + ssize_t nbytes() const { + return size() * itemsize(); + } + + /// Number of dimensions + ssize_t ndim() const { + return detail::array_proxy(m_ptr)->nd; + } + + /// Base object + object base() const { + return reinterpret_borrow(detail::array_proxy(m_ptr)->base); + } + + /// Dimensions of the array + const ssize_t* shape() const { + return detail::array_proxy(m_ptr)->dimensions; + } + + /// Dimension along a given axis + ssize_t shape(ssize_t dim) const { + if (dim >= ndim()) + fail_dim_check(dim, "invalid axis"); + return shape()[dim]; + } + + /// Strides of the array + const ssize_t* strides() const { + return detail::array_proxy(m_ptr)->strides; + } + + /// Stride along a given axis + ssize_t strides(ssize_t dim) const { + if (dim >= ndim()) + fail_dim_check(dim, "invalid axis"); + return strides()[dim]; + } + + /// Return the NumPy array flags + int flags() const { + return detail::array_proxy(m_ptr)->flags; + } + + /// If set, the array is writeable (otherwise the buffer is read-only) + bool writeable() const { + return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_WRITEABLE_); + } + + /// If set, the array owns the data (will be freed when the array is deleted) + bool owndata() const { + return detail::check_flags(m_ptr, detail::npy_api::NPY_ARRAY_OWNDATA_); + } + + /// Pointer to the contained data. If index is not provided, points to the + /// beginning of the buffer. May throw if the index would lead to out of bounds access. + template const void* data(Ix... index) const { + return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); + } + + /// Mutable pointer to the contained data. If index is not provided, points to the + /// beginning of the buffer. May throw if the index would lead to out of bounds access. + /// May throw if the array is not writeable. + template void* mutable_data(Ix... index) { + check_writeable(); + return static_cast(detail::array_proxy(m_ptr)->data + offset_at(index...)); + } + + /// Byte offset from beginning of the array to a given index (full or partial). + /// May throw if the index would lead to out of bounds access. + template ssize_t offset_at(Ix... index) const { + if ((ssize_t) sizeof...(index) > ndim()) + fail_dim_check(sizeof...(index), "too many indices for an array"); + return byte_offset(ssize_t(index)...); + } + + ssize_t offset_at() const { return 0; } + + /// Item count from beginning of the array to a given index (full or partial). + /// May throw if the index would lead to out of bounds access. + template ssize_t index_at(Ix... index) const { + return offset_at(index...) / itemsize(); + } + + /** + * Returns a proxy object that provides access to the array's data without bounds or + * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with + * care: the array must not be destroyed or reshaped for the duration of the returned object, + * and the caller must take care not to access invalid dimensions or dimension indices. + */ + template detail::unchecked_mutable_reference mutable_unchecked() & { + if (Dims >= 0 && ndim() != Dims) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + + "; expected " + std::to_string(Dims)); + return detail::unchecked_mutable_reference(mutable_data(), shape(), strides(), ndim()); + } + + /** + * Returns a proxy object that provides const access to the array's data without bounds or + * dimensionality checking. Unlike `mutable_unchecked()`, this does not require that the + * underlying array have the `writable` flag. Use with care: the array must not be destroyed or + * reshaped for the duration of the returned object, and the caller must take care not to access + * invalid dimensions or dimension indices. + */ + template detail::unchecked_reference unchecked() const & { + if (Dims >= 0 && ndim() != Dims) + throw std::domain_error("array has incorrect number of dimensions: " + std::to_string(ndim()) + + "; expected " + std::to_string(Dims)); + return detail::unchecked_reference(data(), shape(), strides(), ndim()); + } + + /// Return a new view with all of the dimensions of length 1 removed + array squeeze() { + auto& api = detail::npy_api::get(); + return reinterpret_steal(api.PyArray_Squeeze_(m_ptr)); + } + + /// Resize array to given shape + /// If refcheck is true and more that one reference exist to this array + /// then resize will succeed only if it makes a reshape, i.e. original size doesn't change + void resize(ShapeContainer new_shape, bool refcheck = true) { + detail::npy_api::PyArray_Dims d = { + new_shape->data(), int(new_shape->size()) + }; + // try to resize, set ordering param to -1 cause it's not used anyway + object new_array = reinterpret_steal( + detail::npy_api::get().PyArray_Resize_(m_ptr, &d, int(refcheck), -1) + ); + if (!new_array) throw error_already_set(); + if (isinstance(new_array)) { *this = std::move(new_array); } + } + + /// Ensure that the argument is a NumPy array + /// In case of an error, nullptr is returned and the Python error is cleared. + static array ensure(handle h, int ExtraFlags = 0) { + auto result = reinterpret_steal(raw_array(h.ptr(), ExtraFlags)); + if (!result) + PyErr_Clear(); + return result; + } + +protected: + template friend struct detail::npy_format_descriptor; + + void fail_dim_check(ssize_t dim, const std::string& msg) const { + throw index_error(msg + ": " + std::to_string(dim) + + " (ndim = " + std::to_string(ndim()) + ")"); + } + + template ssize_t byte_offset(Ix... index) const { + check_dimensions(index...); + return detail::byte_offset_unsafe(strides(), ssize_t(index)...); + } + + void check_writeable() const { + if (!writeable()) + throw std::domain_error("array is not writeable"); + } + + // Default, C-style strides + static std::vector c_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + if (ndim > 0) + for (size_t i = ndim - 1; i > 0; --i) + strides[i - 1] = strides[i] * shape[i]; + return strides; + } + + // F-style strides; default when constructing an array_t with `ExtraFlags & f_style` + static std::vector f_strides(const std::vector &shape, ssize_t itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim, itemsize); + for (size_t i = 1; i < ndim; ++i) + strides[i] = strides[i - 1] * shape[i - 1]; + return strides; + } + + template void check_dimensions(Ix... index) const { + check_dimensions_impl(ssize_t(0), shape(), ssize_t(index)...); + } + + void check_dimensions_impl(ssize_t, const ssize_t*) const { } + + template void check_dimensions_impl(ssize_t axis, const ssize_t* shape, ssize_t i, Ix... index) const { + if (i >= *shape) { + throw index_error(std::string("index ") + std::to_string(i) + + " is out of bounds for axis " + std::to_string(axis) + + " with size " + std::to_string(*shape)); + } + check_dimensions_impl(axis + 1, shape + 1, index...); + } + + /// Create array from any object -- always returns a new reference + static PyObject *raw_array(PyObject *ptr, int ExtraFlags = 0) { + if (ptr == nullptr) { + PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array from a nullptr"); + return nullptr; + } + return detail::npy_api::get().PyArray_FromAny_( + ptr, nullptr, 0, 0, detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); + } +}; + +template class array_t : public array { +private: + struct private_ctor {}; + // Delegating constructor needed when both moving and accessing in the same constructor + array_t(private_ctor, ShapeContainer &&shape, StridesContainer &&strides, const T *ptr, handle base) + : array(std::move(shape), std::move(strides), ptr, base) {} +public: + static_assert(!detail::array_info::is_array, "Array types cannot be used with array_t"); + + using value_type = T; + + array_t() : array(0, static_cast(nullptr)) {} + array_t(handle h, borrowed_t) : array(h, borrowed_t{}) { } + array_t(handle h, stolen_t) : array(h, stolen_t{}) { } + + PYBIND11_DEPRECATED("Use array_t::ensure() instead") + array_t(handle h, bool is_borrowed) : array(raw_array_t(h.ptr()), stolen_t{}) { + if (!m_ptr) PyErr_Clear(); + if (!is_borrowed) Py_XDECREF(h.ptr()); + } + + array_t(const object &o) : array(raw_array_t(o.ptr()), stolen_t{}) { + if (!m_ptr) throw error_already_set(); + } + + explicit array_t(const buffer_info& info) : array(info) { } + + array_t(ShapeContainer shape, StridesContainer strides, const T *ptr = nullptr, handle base = handle()) + : array(std::move(shape), std::move(strides), ptr, base) { } + + explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle()) + : array_t(private_ctor{}, std::move(shape), + ExtraFlags & f_style ? f_strides(*shape, itemsize()) : c_strides(*shape, itemsize()), + ptr, base) { } + + explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle()) + : array({count}, {}, ptr, base) { } + + constexpr ssize_t itemsize() const { + return sizeof(T); + } + + template ssize_t index_at(Ix... index) const { + return offset_at(index...) / itemsize(); + } + + template const T* data(Ix... index) const { + return static_cast(array::data(index...)); + } + + template T* mutable_data(Ix... index) { + return static_cast(array::mutable_data(index...)); + } + + // Reference to element at a given index + template const T& at(Ix... index) const { + if ((ssize_t) sizeof...(index) != ndim()) + fail_dim_check(sizeof...(index), "index dimension mismatch"); + return *(static_cast(array::data()) + byte_offset(ssize_t(index)...) / itemsize()); + } + + // Mutable reference to element at a given index + template T& mutable_at(Ix... index) { + if ((ssize_t) sizeof...(index) != ndim()) + fail_dim_check(sizeof...(index), "index dimension mismatch"); + return *(static_cast(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize()); + } + + /** + * Returns a proxy object that provides access to the array's data without bounds or + * dimensionality checking. Will throw if the array is missing the `writeable` flag. Use with + * care: the array must not be destroyed or reshaped for the duration of the returned object, + * and the caller must take care not to access invalid dimensions or dimension indices. + */ + template detail::unchecked_mutable_reference mutable_unchecked() & { + return array::mutable_unchecked(); + } + + /** + * Returns a proxy object that provides const access to the array's data without bounds or + * dimensionality checking. Unlike `unchecked()`, this does not require that the underlying + * array have the `writable` flag. Use with care: the array must not be destroyed or reshaped + * for the duration of the returned object, and the caller must take care not to access invalid + * dimensions or dimension indices. + */ + template detail::unchecked_reference unchecked() const & { + return array::unchecked(); + } + + /// Ensure that the argument is a NumPy array of the correct dtype (and if not, try to convert + /// it). In case of an error, nullptr is returned and the Python error is cleared. + static array_t ensure(handle h) { + auto result = reinterpret_steal(raw_array_t(h.ptr())); + if (!result) + PyErr_Clear(); + return result; + } + + static bool check_(handle h) { + const auto &api = detail::npy_api::get(); + return api.PyArray_Check_(h.ptr()) + && api.PyArray_EquivTypes_(detail::array_proxy(h.ptr())->descr, dtype::of().ptr()); + } + +protected: + /// Create array from any object -- always returns a new reference + static PyObject *raw_array_t(PyObject *ptr) { + if (ptr == nullptr) { + PyErr_SetString(PyExc_ValueError, "cannot create a pybind11::array_t from a nullptr"); + return nullptr; + } + return detail::npy_api::get().PyArray_FromAny_( + ptr, dtype::of().release().ptr(), 0, 0, + detail::npy_api::NPY_ARRAY_ENSUREARRAY_ | ExtraFlags, nullptr); + } +}; + +template +struct format_descriptor::value>> { + static std::string format() { + return detail::npy_format_descriptor::type>::format(); + } +}; + +template struct format_descriptor { + static std::string format() { return std::to_string(N) + "s"; } +}; +template struct format_descriptor> { + static std::string format() { return std::to_string(N) + "s"; } +}; + +template +struct format_descriptor::value>> { + static std::string format() { + return format_descriptor< + typename std::remove_cv::type>::type>::format(); + } +}; + +template +struct format_descriptor::is_array>> { + static std::string format() { + using namespace detail; + static constexpr auto extents = _("(") + array_info::extents + _(")"); + return extents.text + format_descriptor>::format(); + } +}; + +NAMESPACE_BEGIN(detail) +template +struct pyobject_caster> { + using type = array_t; + + bool load(handle src, bool convert) { + if (!convert && !type::check_(src)) + return false; + value = type::ensure(src); + return static_cast(value); + } + + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); + } + PYBIND11_TYPE_CASTER(type, handle_type_name::name); +}; + +template +struct compare_buffer_info::value>> { + static bool compare(const buffer_info& b) { + return npy_api::get().PyArray_EquivTypes_(dtype::of().ptr(), dtype(b).ptr()); + } +}; + +template +struct npy_format_descriptor_name; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value>( + _("bool"), _::value>("int", "uint") + _() + ); +}; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value || std::is_same::value>( + _("float") + _(), _("longdouble") + ); +}; + +template +struct npy_format_descriptor_name::value>> { + static constexpr auto name = _::value + || std::is_same::value>( + _("complex") + _(), _("longcomplex") + ); +}; + +template +struct npy_format_descriptor::value>> + : npy_format_descriptor_name { +private: + // NB: the order here must match the one in common.h + constexpr static const int values[15] = { + npy_api::NPY_BOOL_, + npy_api::NPY_BYTE_, npy_api::NPY_UBYTE_, npy_api::NPY_INT16_, npy_api::NPY_UINT16_, + npy_api::NPY_INT32_, npy_api::NPY_UINT32_, npy_api::NPY_INT64_, npy_api::NPY_UINT64_, + npy_api::NPY_FLOAT_, npy_api::NPY_DOUBLE_, npy_api::NPY_LONGDOUBLE_, + npy_api::NPY_CFLOAT_, npy_api::NPY_CDOUBLE_, npy_api::NPY_CLONGDOUBLE_ + }; + +public: + static constexpr int value = values[detail::is_fmt_numeric::index]; + + static pybind11::dtype dtype() { + if (auto ptr = npy_api::get().PyArray_DescrFromType_(value)) + return reinterpret_steal(ptr); + pybind11_fail("Unsupported buffer format!"); + } +}; + +#define PYBIND11_DECL_CHAR_FMT \ + static constexpr auto name = _("S") + _(); \ + static pybind11::dtype dtype() { return pybind11::dtype(std::string("S") + std::to_string(N)); } +template struct npy_format_descriptor { PYBIND11_DECL_CHAR_FMT }; +template struct npy_format_descriptor> { PYBIND11_DECL_CHAR_FMT }; +#undef PYBIND11_DECL_CHAR_FMT + +template struct npy_format_descriptor::is_array>> { +private: + using base_descr = npy_format_descriptor::type>; +public: + static_assert(!array_info::is_empty, "Zero-sized arrays are not supported"); + + static constexpr auto name = _("(") + array_info::extents + _(")") + base_descr::name; + static pybind11::dtype dtype() { + list shape; + array_info::append_extents(shape); + return pybind11::dtype::from_args(pybind11::make_tuple(base_descr::dtype(), shape)); + } +}; + +template struct npy_format_descriptor::value>> { +private: + using base_descr = npy_format_descriptor::type>; +public: + static constexpr auto name = base_descr::name; + static pybind11::dtype dtype() { return base_descr::dtype(); } +}; + +struct field_descriptor { + const char *name; + ssize_t offset; + ssize_t size; + std::string format; + dtype descr; +}; + +inline PYBIND11_NOINLINE void register_structured_dtype( + any_container fields, + const std::type_info& tinfo, ssize_t itemsize, + bool (*direct_converter)(PyObject *, void *&)) { + + auto& numpy_internals = get_numpy_internals(); + if (numpy_internals.get_type_info(tinfo, false)) + pybind11_fail("NumPy: dtype is already registered"); + + // Use ordered fields because order matters as of NumPy 1.14: + // https://docs.scipy.org/doc/numpy/release.html#multiple-field-indexing-assignment-of-structured-arrays + std::vector ordered_fields(std::move(fields)); + std::sort(ordered_fields.begin(), ordered_fields.end(), + [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); + + list names, formats, offsets; + for (auto& field : ordered_fields) { + if (!field.descr) + pybind11_fail(std::string("NumPy: unsupported field dtype: `") + + field.name + "` @ " + tinfo.name()); + names.append(PYBIND11_STR_TYPE(field.name)); + formats.append(field.descr); + offsets.append(pybind11::int_(field.offset)); + } + auto dtype_ptr = pybind11::dtype(names, formats, offsets, itemsize).release().ptr(); + + // There is an existing bug in NumPy (as of v1.11): trailing bytes are + // not encoded explicitly into the format string. This will supposedly + // get fixed in v1.12; for further details, see these: + // - https://github.com/numpy/numpy/issues/7797 + // - https://github.com/numpy/numpy/pull/7798 + // Because of this, we won't use numpy's logic to generate buffer format + // strings and will just do it ourselves. + ssize_t offset = 0; + std::ostringstream oss; + // mark the structure as unaligned with '^', because numpy and C++ don't + // always agree about alignment (particularly for complex), and we're + // explicitly listing all our padding. This depends on none of the fields + // overriding the endianness. Putting the ^ in front of individual fields + // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049 + oss << "^T{"; + for (auto& field : ordered_fields) { + if (field.offset > offset) + oss << (field.offset - offset) << 'x'; + oss << field.format << ':' << field.name << ':'; + offset = field.offset + field.size; + } + if (itemsize > offset) + oss << (itemsize - offset) << 'x'; + oss << '}'; + auto format_str = oss.str(); + + // Sanity check: verify that NumPy properly parses our buffer format string + auto& api = npy_api::get(); + auto arr = array(buffer_info(nullptr, itemsize, format_str, 1)); + if (!api.PyArray_EquivTypes_(dtype_ptr, arr.dtype().ptr())) + pybind11_fail("NumPy: invalid buffer descriptor!"); + + auto tindex = std::type_index(tinfo); + numpy_internals.registered_dtypes[tindex] = { dtype_ptr, format_str }; + get_internals().direct_conversions[tindex].push_back(direct_converter); +} + +template struct npy_format_descriptor { + static_assert(is_pod_struct::value, "Attempt to use a non-POD or unimplemented POD type as a numpy dtype"); + + static constexpr auto name = make_caster::name; + + static pybind11::dtype dtype() { + return reinterpret_borrow(dtype_ptr()); + } + + static std::string format() { + static auto format_str = get_numpy_internals().get_type_info(true)->format_str; + return format_str; + } + + static void register_dtype(any_container fields) { + register_structured_dtype(std::move(fields), typeid(typename std::remove_cv::type), + sizeof(T), &direct_converter); + } + +private: + static PyObject* dtype_ptr() { + static PyObject* ptr = get_numpy_internals().get_type_info(true)->dtype_ptr; + return ptr; + } + + static bool direct_converter(PyObject *obj, void*& value) { + auto& api = npy_api::get(); + if (!PyObject_TypeCheck(obj, api.PyVoidArrType_Type_)) + return false; + if (auto descr = reinterpret_steal(api.PyArray_DescrFromScalar_(obj))) { + if (api.PyArray_EquivTypes_(dtype_ptr(), descr.ptr())) { + value = ((PyVoidScalarObject_Proxy *) obj)->obval; + return true; + } + } + return false; + } +}; + +#ifdef __CLION_IDE__ // replace heavy macro with dummy code for the IDE (doesn't affect code) +# define PYBIND11_NUMPY_DTYPE(Type, ...) ((void)0) +# define PYBIND11_NUMPY_DTYPE_EX(Type, ...) ((void)0) +#else + +#define PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, Name) \ + ::pybind11::detail::field_descriptor { \ + Name, offsetof(T, Field), sizeof(decltype(std::declval().Field)), \ + ::pybind11::format_descriptor().Field)>::format(), \ + ::pybind11::detail::npy_format_descriptor().Field)>::dtype() \ + } + +// Extract name, offset and format descriptor for a struct field +#define PYBIND11_FIELD_DESCRIPTOR(T, Field) PYBIND11_FIELD_DESCRIPTOR_EX(T, Field, #Field) + +// The main idea of this macro is borrowed from https://github.com/swansontec/map-macro +// (C) William Swanson, Paul Fultz +#define PYBIND11_EVAL0(...) __VA_ARGS__ +#define PYBIND11_EVAL1(...) PYBIND11_EVAL0 (PYBIND11_EVAL0 (PYBIND11_EVAL0 (__VA_ARGS__))) +#define PYBIND11_EVAL2(...) PYBIND11_EVAL1 (PYBIND11_EVAL1 (PYBIND11_EVAL1 (__VA_ARGS__))) +#define PYBIND11_EVAL3(...) PYBIND11_EVAL2 (PYBIND11_EVAL2 (PYBIND11_EVAL2 (__VA_ARGS__))) +#define PYBIND11_EVAL4(...) PYBIND11_EVAL3 (PYBIND11_EVAL3 (PYBIND11_EVAL3 (__VA_ARGS__))) +#define PYBIND11_EVAL(...) PYBIND11_EVAL4 (PYBIND11_EVAL4 (PYBIND11_EVAL4 (__VA_ARGS__))) +#define PYBIND11_MAP_END(...) +#define PYBIND11_MAP_OUT +#define PYBIND11_MAP_COMMA , +#define PYBIND11_MAP_GET_END() 0, PYBIND11_MAP_END +#define PYBIND11_MAP_NEXT0(test, next, ...) next PYBIND11_MAP_OUT +#define PYBIND11_MAP_NEXT1(test, next) PYBIND11_MAP_NEXT0 (test, next, 0) +#define PYBIND11_MAP_NEXT(test, next) PYBIND11_MAP_NEXT1 (PYBIND11_MAP_GET_END test, next) +#ifdef _MSC_VER // MSVC is not as eager to expand macros, hence this workaround +#define PYBIND11_MAP_LIST_NEXT1(test, next) \ + PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) +#else +#define PYBIND11_MAP_LIST_NEXT1(test, next) \ + PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) +#endif +#define PYBIND11_MAP_LIST_NEXT(test, next) \ + PYBIND11_MAP_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) +#define PYBIND11_MAP_LIST0(f, t, x, peek, ...) \ + f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST1) (f, t, peek, __VA_ARGS__) +#define PYBIND11_MAP_LIST1(f, t, x, peek, ...) \ + f(t, x) PYBIND11_MAP_LIST_NEXT (peek, PYBIND11_MAP_LIST0) (f, t, peek, __VA_ARGS__) +// PYBIND11_MAP_LIST(f, t, a1, a2, ...) expands to f(t, a1), f(t, a2), ... +#define PYBIND11_MAP_LIST(f, t, ...) \ + PYBIND11_EVAL (PYBIND11_MAP_LIST1 (f, t, __VA_ARGS__, (), 0)) + +#define PYBIND11_NUMPY_DTYPE(Type, ...) \ + ::pybind11::detail::npy_format_descriptor::register_dtype \ + (::std::vector<::pybind11::detail::field_descriptor> \ + {PYBIND11_MAP_LIST (PYBIND11_FIELD_DESCRIPTOR, Type, __VA_ARGS__)}) + +#ifdef _MSC_VER +#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ + PYBIND11_EVAL0 (PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0)) +#else +#define PYBIND11_MAP2_LIST_NEXT1(test, next) \ + PYBIND11_MAP_NEXT0 (test, PYBIND11_MAP_COMMA next, 0) +#endif +#define PYBIND11_MAP2_LIST_NEXT(test, next) \ + PYBIND11_MAP2_LIST_NEXT1 (PYBIND11_MAP_GET_END test, next) +#define PYBIND11_MAP2_LIST0(f, t, x1, x2, peek, ...) \ + f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST1) (f, t, peek, __VA_ARGS__) +#define PYBIND11_MAP2_LIST1(f, t, x1, x2, peek, ...) \ + f(t, x1, x2) PYBIND11_MAP2_LIST_NEXT (peek, PYBIND11_MAP2_LIST0) (f, t, peek, __VA_ARGS__) +// PYBIND11_MAP2_LIST(f, t, a1, a2, ...) expands to f(t, a1, a2), f(t, a3, a4), ... +#define PYBIND11_MAP2_LIST(f, t, ...) \ + PYBIND11_EVAL (PYBIND11_MAP2_LIST1 (f, t, __VA_ARGS__, (), 0)) + +#define PYBIND11_NUMPY_DTYPE_EX(Type, ...) \ + ::pybind11::detail::npy_format_descriptor::register_dtype \ + (::std::vector<::pybind11::detail::field_descriptor> \ + {PYBIND11_MAP2_LIST (PYBIND11_FIELD_DESCRIPTOR_EX, Type, __VA_ARGS__)}) + +#endif // __CLION_IDE__ + +template +using array_iterator = typename std::add_pointer::type; + +template +array_iterator array_begin(const buffer_info& buffer) { + return array_iterator(reinterpret_cast(buffer.ptr)); +} + +template +array_iterator array_end(const buffer_info& buffer) { + return array_iterator(reinterpret_cast(buffer.ptr) + buffer.size); +} + +class common_iterator { +public: + using container_type = std::vector; + using value_type = container_type::value_type; + using size_type = container_type::size_type; + + common_iterator() : p_ptr(0), m_strides() {} + + common_iterator(void* ptr, const container_type& strides, const container_type& shape) + : p_ptr(reinterpret_cast(ptr)), m_strides(strides.size()) { + m_strides.back() = static_cast(strides.back()); + for (size_type i = m_strides.size() - 1; i != 0; --i) { + size_type j = i - 1; + value_type s = static_cast(shape[i]); + m_strides[j] = strides[j] + m_strides[i] - strides[i] * s; + } + } + + void increment(size_type dim) { + p_ptr += m_strides[dim]; + } + + void* data() const { + return p_ptr; + } + +private: + char* p_ptr; + container_type m_strides; +}; + +template class multi_array_iterator { +public: + using container_type = std::vector; + + multi_array_iterator(const std::array &buffers, + const container_type &shape) + : m_shape(shape.size()), m_index(shape.size(), 0), + m_common_iterator() { + + // Manual copy to avoid conversion warning if using std::copy + for (size_t i = 0; i < shape.size(); ++i) + m_shape[i] = shape[i]; + + container_type strides(shape.size()); + for (size_t i = 0; i < N; ++i) + init_common_iterator(buffers[i], shape, m_common_iterator[i], strides); + } + + multi_array_iterator& operator++() { + for (size_t j = m_index.size(); j != 0; --j) { + size_t i = j - 1; + if (++m_index[i] != m_shape[i]) { + increment_common_iterator(i); + break; + } else { + m_index[i] = 0; + } + } + return *this; + } + + template T* data() const { + return reinterpret_cast(m_common_iterator[K].data()); + } + +private: + + using common_iter = common_iterator; + + void init_common_iterator(const buffer_info &buffer, + const container_type &shape, + common_iter &iterator, + container_type &strides) { + auto buffer_shape_iter = buffer.shape.rbegin(); + auto buffer_strides_iter = buffer.strides.rbegin(); + auto shape_iter = shape.rbegin(); + auto strides_iter = strides.rbegin(); + + while (buffer_shape_iter != buffer.shape.rend()) { + if (*shape_iter == *buffer_shape_iter) + *strides_iter = *buffer_strides_iter; + else + *strides_iter = 0; + + ++buffer_shape_iter; + ++buffer_strides_iter; + ++shape_iter; + ++strides_iter; + } + + std::fill(strides_iter, strides.rend(), 0); + iterator = common_iter(buffer.ptr, strides, shape); + } + + void increment_common_iterator(size_t dim) { + for (auto &iter : m_common_iterator) + iter.increment(dim); + } + + container_type m_shape; + container_type m_index; + std::array m_common_iterator; +}; + +enum class broadcast_trivial { non_trivial, c_trivial, f_trivial }; + +// Populates the shape and number of dimensions for the set of buffers. Returns a broadcast_trivial +// enum value indicating whether the broadcast is "trivial"--that is, has each buffer being either a +// singleton or a full-size, C-contiguous (`c_trivial`) or Fortran-contiguous (`f_trivial`) storage +// buffer; returns `non_trivial` otherwise. +template +broadcast_trivial broadcast(const std::array &buffers, ssize_t &ndim, std::vector &shape) { + ndim = std::accumulate(buffers.begin(), buffers.end(), ssize_t(0), [](ssize_t res, const buffer_info &buf) { + return std::max(res, buf.ndim); + }); + + shape.clear(); + shape.resize((size_t) ndim, 1); + + // Figure out the output size, and make sure all input arrays conform (i.e. are either size 1 or + // the full size). + for (size_t i = 0; i < N; ++i) { + auto res_iter = shape.rbegin(); + auto end = buffers[i].shape.rend(); + for (auto shape_iter = buffers[i].shape.rbegin(); shape_iter != end; ++shape_iter, ++res_iter) { + const auto &dim_size_in = *shape_iter; + auto &dim_size_out = *res_iter; + + // Each input dimension can either be 1 or `n`, but `n` values must match across buffers + if (dim_size_out == 1) + dim_size_out = dim_size_in; + else if (dim_size_in != 1 && dim_size_in != dim_size_out) + pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!"); + } + } + + bool trivial_broadcast_c = true; + bool trivial_broadcast_f = true; + for (size_t i = 0; i < N && (trivial_broadcast_c || trivial_broadcast_f); ++i) { + if (buffers[i].size == 1) + continue; + + // Require the same number of dimensions: + if (buffers[i].ndim != ndim) + return broadcast_trivial::non_trivial; + + // Require all dimensions be full-size: + if (!std::equal(buffers[i].shape.cbegin(), buffers[i].shape.cend(), shape.cbegin())) + return broadcast_trivial::non_trivial; + + // Check for C contiguity (but only if previous inputs were also C contiguous) + if (trivial_broadcast_c) { + ssize_t expect_stride = buffers[i].itemsize; + auto end = buffers[i].shape.crend(); + for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin(); + trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) { + if (expect_stride == *stride_iter) + expect_stride *= *shape_iter; + else + trivial_broadcast_c = false; + } + } + + // Check for Fortran contiguity (if previous inputs were also F contiguous) + if (trivial_broadcast_f) { + ssize_t expect_stride = buffers[i].itemsize; + auto end = buffers[i].shape.cend(); + for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin(); + trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) { + if (expect_stride == *stride_iter) + expect_stride *= *shape_iter; + else + trivial_broadcast_f = false; + } + } + } + + return + trivial_broadcast_c ? broadcast_trivial::c_trivial : + trivial_broadcast_f ? broadcast_trivial::f_trivial : + broadcast_trivial::non_trivial; +} + +template +struct vectorize_arg { + static_assert(!std::is_rvalue_reference::value, "Functions with rvalue reference arguments cannot be vectorized"); + // The wrapped function gets called with this type: + using call_type = remove_reference_t; + // Is this a vectorized argument? + static constexpr bool vectorize = + satisfies_any_of::value && + satisfies_none_of::value && + (!std::is_reference::value || + (std::is_lvalue_reference::value && std::is_const::value)); + // Accept this type: an array for vectorized types, otherwise the type as-is: + using type = conditional_t, array::forcecast>, T>; +}; + +template +struct vectorize_helper { +private: + static constexpr size_t N = sizeof...(Args); + static constexpr size_t NVectorized = constexpr_sum(vectorize_arg::vectorize...); + static_assert(NVectorized >= 1, + "pybind11::vectorize(...) requires a function with at least one vectorizable argument"); + +public: + template + explicit vectorize_helper(T &&f) : f(std::forward(f)) { } + + object operator()(typename vectorize_arg::type... args) { + return run(args..., + make_index_sequence(), + select_indices::vectorize...>(), + make_index_sequence()); + } + +private: + remove_reference_t f; + + // Internal compiler error in MSVC 19.16.27025.1 (Visual Studio 2017 15.9.4), when compiling with "/permissive-" flag + // when arg_call_types is manually inlined. + using arg_call_types = std::tuple::call_type...>; + template using param_n_t = typename std::tuple_element::type; + + // Runs a vectorized function given arguments tuple and three index sequences: + // - Index is the full set of 0 ... (N-1) argument indices; + // - VIndex is the subset of argument indices with vectorized parameters, letting us access + // vectorized arguments (anything not in this sequence is passed through) + // - BIndex is a incremental sequence (beginning at 0) of the same size as VIndex, so that + // we can store vectorized buffer_infos in an array (argument VIndex has its buffer at + // index BIndex in the array). + template object run( + typename vectorize_arg::type &...args, + index_sequence i_seq, index_sequence vi_seq, index_sequence bi_seq) { + + // Pointers to values the function was called with; the vectorized ones set here will start + // out as array_t pointers, but they will be changed them to T pointers before we make + // call the wrapped function. Non-vectorized pointers are left as-is. + std::array params{{ &args... }}; + + // The array of `buffer_info`s of vectorized arguments: + std::array buffers{{ reinterpret_cast(params[VIndex])->request()... }}; + + /* Determine dimensions parameters of output array */ + ssize_t nd = 0; + std::vector shape(0); + auto trivial = broadcast(buffers, nd, shape); + size_t ndim = (size_t) nd; + + size_t size = std::accumulate(shape.begin(), shape.end(), (size_t) 1, std::multiplies()); + + // If all arguments are 0-dimension arrays (i.e. single values) return a plain value (i.e. + // not wrapped in an array). + if (size == 1 && ndim == 0) { + PYBIND11_EXPAND_SIDE_EFFECTS(params[VIndex] = buffers[BIndex].ptr); + return cast(f(*reinterpret_cast *>(params[Index])...)); + } + + array_t result; + if (trivial == broadcast_trivial::f_trivial) result = array_t(shape); + else result = array_t(shape); + + if (size == 0) return std::move(result); + + /* Call the function */ + if (trivial == broadcast_trivial::non_trivial) + apply_broadcast(buffers, params, result, i_seq, vi_seq, bi_seq); + else + apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq); + + return std::move(result); + } + + template + void apply_trivial(std::array &buffers, + std::array ¶ms, + Return *out, + size_t size, + index_sequence, index_sequence, index_sequence) { + + // Initialize an array of mutable byte references and sizes with references set to the + // appropriate pointer in `params`; as we iterate, we'll increment each pointer by its size + // (except for singletons, which get an increment of 0). + std::array, NVectorized> vecparams{{ + std::pair( + reinterpret_cast(params[VIndex] = buffers[BIndex].ptr), + buffers[BIndex].size == 1 ? 0 : sizeof(param_n_t) + )... + }}; + + for (size_t i = 0; i < size; ++i) { + out[i] = f(*reinterpret_cast *>(params[Index])...); + for (auto &x : vecparams) x.first += x.second; + } + } + + template + void apply_broadcast(std::array &buffers, + std::array ¶ms, + array_t &output_array, + index_sequence, index_sequence, index_sequence) { + + buffer_info output = output_array.request(); + multi_array_iterator input_iter(buffers, output.shape); + + for (array_iterator iter = array_begin(output), end = array_end(output); + iter != end; + ++iter, ++input_iter) { + PYBIND11_EXPAND_SIDE_EFFECTS(( + params[VIndex] = input_iter.template data() + )); + *iter = f(*reinterpret_cast *>(std::get(params))...); + } + } +}; + +template +vectorize_helper +vectorize_extractor(const Func &f, Return (*) (Args ...)) { + return detail::vectorize_helper(f); +} + +template struct handle_type_name> { + static constexpr auto name = _("numpy.ndarray[") + npy_format_descriptor::name + _("]"); +}; + +NAMESPACE_END(detail) + +// Vanilla pointer vectorizer: +template +detail::vectorize_helper +vectorize(Return (*f) (Args ...)) { + return detail::vectorize_helper(f); +} + +// lambda vectorizer: +template ::value, int> = 0> +auto vectorize(Func &&f) -> decltype( + detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr)) { + return detail::vectorize_extractor(std::forward(f), (detail::function_signature_t *) nullptr); +} + +// Vectorize a class method (non-const): +template ())), Return, Class *, Args...>> +Helper vectorize(Return (Class::*f)(Args...)) { + return Helper(std::mem_fn(f)); +} + +// Vectorize a class method (const): +template ())), Return, const Class *, Args...>> +Helper vectorize(Return (Class::*f)(Args...) const) { + return Helper(std::mem_fn(f)); +} + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/thirdparty/pybind11/include/pybind11/operators.h b/thirdparty/pybind11/include/pybind11/operators.h new file mode 100644 index 000000000..b3dd62c3b --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/operators.h @@ -0,0 +1,168 @@ +/* + pybind11/operator.h: Metatemplates for operator overloading + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" + +#if defined(__clang__) && !defined(__INTEL_COMPILER) +# pragma clang diagnostic ignored "-Wunsequenced" // multiple unsequenced modifications to 'self' (when using def(py::self OP Type())) +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Enumeration with all supported operator types +enum op_id : int { + op_add, op_sub, op_mul, op_div, op_mod, op_divmod, op_pow, op_lshift, + op_rshift, op_and, op_xor, op_or, op_neg, op_pos, op_abs, op_invert, + op_int, op_long, op_float, op_str, op_cmp, op_gt, op_ge, op_lt, op_le, + op_eq, op_ne, op_iadd, op_isub, op_imul, op_idiv, op_imod, op_ilshift, + op_irshift, op_iand, op_ixor, op_ior, op_complex, op_bool, op_nonzero, + op_repr, op_truediv, op_itruediv, op_hash +}; + +enum op_type : int { + op_l, /* base type on left */ + op_r, /* base type on right */ + op_u /* unary operator */ +}; + +struct self_t { }; +static const self_t self = self_t(); + +/// Type for an unused type slot +struct undefined_t { }; + +/// Don't warn about an unused variable +inline self_t __self() { return self; } + +/// base template of operator implementations +template struct op_impl { }; + +/// Operator implementation generator +template struct op_ { + template void execute(Class &cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = conditional_t::value, Base, L>; + using R_type = conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute, is_operator(), extra...); + #if PY_MAJOR_VERSION < 3 + if (id == op_truediv || id == op_itruediv) + cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", + &op::execute, is_operator(), extra...); + #endif + } + template void execute_cast(Class &cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = conditional_t::value, Base, L>; + using R_type = conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute_cast, is_operator(), extra...); + #if PY_MAJOR_VERSION < 3 + if (id == op_truediv || id == op_itruediv) + cl.def(id == op_itruediv ? "__idiv__" : ot == op_l ? "__div__" : "__rdiv__", + &op::execute, is_operator(), extra...); + #endif + } +}; + +#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L &l, const R &r) -> decltype(expr) { return (expr); } \ + static B execute_cast(const L &l, const R &r) { return B(expr); } \ +}; \ +template struct op_impl { \ + static char const* name() { return "__" #rid "__"; } \ + static auto execute(const R &r, const L &l) -> decltype(expr) { return (expr); } \ + static B execute_cast(const R &r, const L &l) { return B(expr); } \ +}; \ +inline op_ op(const self_t &, const self_t &) { \ + return op_(); \ +} \ +template op_ op(const self_t &, const T &) { \ + return op_(); \ +} \ +template op_ op(const T &, const self_t &) { \ + return op_(); \ +} + +#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(L &l, const R &r) -> decltype(expr) { return expr; } \ + static B execute_cast(L &l, const R &r) { return B(expr); } \ +}; \ +template op_ op(const self_t &, const T &) { \ + return op_(); \ +} + +#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ +template struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L &l) -> decltype(expr) { return expr; } \ + static B execute_cast(const L &l) { return B(expr); } \ +}; \ +inline op_ op(const self_t &) { \ + return op_(); \ +} + +PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) +PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) +PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l * r) +PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) +PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) +PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) +PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) +PYBIND11_BINARY_OPERATOR(and, rand, operator&, l & r) +PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) +PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) +PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) +PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) +PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) +PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) +PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) +PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) +//PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) +PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) +PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) +PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) +PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) +PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) +PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) +PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) +PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) +PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) +PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) +PYBIND11_UNARY_OPERATOR(neg, operator-, -l) +PYBIND11_UNARY_OPERATOR(pos, operator+, +l) +PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) +PYBIND11_UNARY_OPERATOR(hash, hash, std::hash()(l)) +PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) +PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) +PYBIND11_UNARY_OPERATOR(int, int_, (int) l) +PYBIND11_UNARY_OPERATOR(float, float_, (double) l) + +#undef PYBIND11_BINARY_OPERATOR +#undef PYBIND11_INPLACE_OPERATOR +#undef PYBIND11_UNARY_OPERATOR +NAMESPACE_END(detail) + +using detail::self; + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif diff --git a/thirdparty/pybind11/include/pybind11/options.h b/thirdparty/pybind11/include/pybind11/options.h new file mode 100644 index 000000000..cc1e1f6f0 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/options.h @@ -0,0 +1,65 @@ +/* + pybind11/options.h: global settings that are configurable at runtime. + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +class options { +public: + + // Default RAII constructor, which leaves settings as they currently are. + options() : previous_state(global_state()) {} + + // Class is non-copyable. + options(const options&) = delete; + options& operator=(const options&) = delete; + + // Destructor, which restores settings that were in effect before. + ~options() { + global_state() = previous_state; + } + + // Setter methods (affect the global state): + + options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; } + + options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; } + + options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; } + + options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; } + + // Getter methods (return the global state): + + static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; } + + static bool show_function_signatures() { return global_state().show_function_signatures; } + + // This type is not meant to be allocated on the heap. + void* operator new(size_t) = delete; + +private: + + struct state { + bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings. + bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings. + }; + + static state &global_state() { + static state instance; + return instance; + } + + state previous_state; +}; + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/pybind11.h b/thirdparty/pybind11/include/pybind11/pybind11.h new file mode 100644 index 000000000..d95d61f7b --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/pybind11.h @@ -0,0 +1,2183 @@ +/* + pybind11/pybind11.h: Main header file of the C++11 python + binding generator library + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#if defined(__INTEL_COMPILER) +# pragma warning push +# pragma warning disable 68 // integer conversion resulted in a change of sign +# pragma warning disable 186 // pointless comparison of unsigned integer with zero +# pragma warning disable 878 // incompatible exception specifications +# pragma warning disable 1334 // the "template" keyword used for syntactic disambiguation may only be used within a template +# pragma warning disable 1682 // implicit conversion of a 64-bit integral type to a smaller integral type (potential portability problem) +# pragma warning disable 1786 // function "strdup" was declared deprecated +# pragma warning disable 1875 // offsetof applied to non-POD (Plain Old Data) types is nonstandard +# pragma warning disable 2196 // warning #2196: routine is both "inline" and "noinline" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted +# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) +# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name +# pragma warning(disable: 4702) // warning C4702: unreachable code +# pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified +#elif defined(__GNUG__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-but-set-parameter" +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +# pragma GCC diagnostic ignored "-Wattributes" +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wnoexcept-type" +# endif +#endif + +#include "attr.h" +#include "options.h" +#include "detail/class.h" +#include "detail/init.h" + +#if defined(__GNUG__) && !defined(__clang__) +# include +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object +class cpp_function : public function { +public: + cpp_function() { } + cpp_function(std::nullptr_t) { } + + /// Construct a cpp_function from a vanilla function pointer + template + cpp_function(Return (*f)(Args...), const Extra&... extra) { + initialize(f, f, extra...); + } + + /// Construct a cpp_function from a lambda function (possibly with internal state) + template ::value>> + cpp_function(Func &&f, const Extra&... extra) { + initialize(std::forward(f), + (detail::function_signature_t *) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (non-const) + template + cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { + initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*) (Class *, Arg...)) nullptr, extra...); + } + + /// Construct a cpp_function from a class method (const) + template + cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { + initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, + (Return (*)(const Class *, Arg ...)) nullptr, extra...); + } + + /// Return the function name + object name() const { return attr("__name__"); } + +protected: + /// Space optimization: don't inline this frequently instantiated fragment + PYBIND11_NOINLINE detail::function_record *make_function_record() { + return new detail::function_record(); + } + + /// Special internal constructor for functors, lambda functions, etc. + template + void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { + using namespace detail; + struct capture { remove_reference_t f; }; + + /* Store the function including any extra state it might have (e.g. a lambda capture object) */ + auto rec = make_function_record(); + + /* Store the capture object directly in the function record if there is enough space */ + if (sizeof(capture) <= sizeof(rec->data)) { + /* Without these pragmas, GCC warns that there might not be + enough space to use the placement new operator. However, the + 'if' statement above ensures that this is the case. */ +#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wplacement-new" +#endif + new ((capture *) &rec->data) capture { std::forward(f) }; +#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6 +# pragma GCC diagnostic pop +#endif + if (!std::is_trivially_destructible::value) + rec->free_data = [](function_record *r) { ((capture *) &r->data)->~capture(); }; + } else { + rec->data[0] = new capture { std::forward(f) }; + rec->free_data = [](function_record *r) { delete ((capture *) r->data[0]); }; + } + + /* Type casters for the function arguments and return value */ + using cast_in = argument_loader; + using cast_out = make_caster< + conditional_t::value, void_type, Return> + >; + + static_assert(expected_num_args(sizeof...(Args), cast_in::has_args, cast_in::has_kwargs), + "The number of argument annotations does not match the number of function arguments"); + + /* Dispatch code which converts function arguments and performs the actual function call */ + rec->impl = [](function_call &call) -> handle { + cast_in args_converter; + + /* Try to cast the function arguments into the C++ domain */ + if (!args_converter.load_args(call)) + return PYBIND11_TRY_NEXT_OVERLOAD; + + /* Invoke call policy pre-call hook */ + process_attributes::precall(call); + + /* Get a pointer to the capture object */ + auto data = (sizeof(capture) <= sizeof(call.func.data) + ? &call.func.data : call.func.data[0]); + capture *cap = const_cast(reinterpret_cast(data)); + + /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ + return_value_policy policy = return_value_policy_override::policy(call.func.policy); + + /* Function scope guard -- defaults to the compile-to-nothing `void_type` */ + using Guard = extract_guard_t; + + /* Perform the function call */ + handle result = cast_out::cast( + std::move(args_converter).template call(cap->f), policy, call.parent); + + /* Invoke call policy post-call hook */ + process_attributes::postcall(call, result); + + return result; + }; + + /* Process any user-provided function attributes */ + process_attributes::init(extra..., rec); + + /* Generate a readable signature describing the function's arguments and return value types */ + static constexpr auto signature = _("(") + cast_in::arg_names + _(") -> ") + cast_out::name; + PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); + + /* Register the function with Python from generic (non-templated) code */ + initialize_generic(rec, signature.text, types.data(), sizeof...(Args)); + + if (cast_in::has_args) rec->has_args = true; + if (cast_in::has_kwargs) rec->has_kwargs = true; + + /* Stash some additional information used by an important optimization in 'functional.h' */ + using FunctionType = Return (*)(Args...); + constexpr bool is_function_ptr = + std::is_convertible::value && + sizeof(capture) == sizeof(void *); + if (is_function_ptr) { + rec->is_stateless = true; + rec->data[1] = const_cast(reinterpret_cast(&typeid(FunctionType))); + } + } + + /// Register a function call with Python (generic non-templated code goes here) + void initialize_generic(detail::function_record *rec, const char *text, + const std::type_info *const *types, size_t args) { + + /* Create copies of all referenced C-style strings */ + rec->name = strdup(rec->name ? rec->name : ""); + if (rec->doc) rec->doc = strdup(rec->doc); + for (auto &a: rec->args) { + if (a.name) + a.name = strdup(a.name); + if (a.descr) + a.descr = strdup(a.descr); + else if (a.value) + a.descr = strdup(a.value.attr("__repr__")().cast().c_str()); + } + + rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__"); + +#if !defined(NDEBUG) && !defined(PYBIND11_DISABLE_NEW_STYLE_INIT_WARNING) + if (rec->is_constructor && !rec->is_new_style_constructor) { + const auto class_name = std::string(((PyTypeObject *) rec->scope.ptr())->tp_name); + const auto func_name = std::string(rec->name); + PyErr_WarnEx( + PyExc_FutureWarning, + ("pybind11-bound class '" + class_name + "' is using an old-style " + "placement-new '" + func_name + "' which has been deprecated. See " + "the upgrade guide in pybind11's docs. This message is only visible " + "when compiled in debug mode.").c_str(), 0 + ); + } +#endif + + /* Generate a proper function signature */ + std::string signature; + size_t type_index = 0, arg_index = 0; + for (auto *pc = text; *pc != '\0'; ++pc) { + const auto c = *pc; + + if (c == '{') { + // Write arg name for everything except *args and **kwargs. + if (*(pc + 1) == '*') + continue; + + if (arg_index < rec->args.size() && rec->args[arg_index].name) { + signature += rec->args[arg_index].name; + } else if (arg_index == 0 && rec->is_method) { + signature += "self"; + } else { + signature += "arg" + std::to_string(arg_index - (rec->is_method ? 1 : 0)); + } + signature += ": "; + } else if (c == '}') { + // Write default value if available. + if (arg_index < rec->args.size() && rec->args[arg_index].descr) { + signature += " = "; + signature += rec->args[arg_index].descr; + } + arg_index++; + } else if (c == '%') { + const std::type_info *t = types[type_index++]; + if (!t) + pybind11_fail("Internal error while parsing type signature (1)"); + if (auto tinfo = detail::get_type_info(*t)) { + handle th((PyObject *) tinfo->type); + signature += + th.attr("__module__").cast() + "." + + th.attr("__qualname__").cast(); // Python 3.3+, but we backport it to earlier versions + } else if (rec->is_new_style_constructor && arg_index == 0) { + // A new-style `__init__` takes `self` as `value_and_holder`. + // Rewrite it to the proper class type. + signature += + rec->scope.attr("__module__").cast() + "." + + rec->scope.attr("__qualname__").cast(); + } else { + std::string tname(t->name()); + detail::clean_type_id(tname); + signature += tname; + } + } else { + signature += c; + } + } + if (arg_index != args || types[type_index] != nullptr) + pybind11_fail("Internal error while parsing type signature (2)"); + +#if PY_MAJOR_VERSION < 3 + if (strcmp(rec->name, "__next__") == 0) { + std::free(rec->name); + rec->name = strdup("next"); + } else if (strcmp(rec->name, "__bool__") == 0) { + std::free(rec->name); + rec->name = strdup("__nonzero__"); + } +#endif + rec->signature = strdup(signature.c_str()); + rec->args.shrink_to_fit(); + rec->nargs = (std::uint16_t) args; + + if (rec->sibling && PYBIND11_INSTANCE_METHOD_CHECK(rec->sibling.ptr())) + rec->sibling = PYBIND11_INSTANCE_METHOD_GET_FUNCTION(rec->sibling.ptr()); + + detail::function_record *chain = nullptr, *chain_start = rec; + if (rec->sibling) { + if (PyCFunction_Check(rec->sibling.ptr())) { + auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(rec->sibling.ptr())); + chain = (detail::function_record *) rec_capsule; + /* Never append a method to an overload chain of a parent class; + instead, hide the parent's overloads in this case */ + if (!chain->scope.is(rec->scope)) + chain = nullptr; + } + // Don't trigger for things like the default __init__, which are wrapper_descriptors that we are intentionally replacing + else if (!rec->sibling.is_none() && rec->name[0] != '_') + pybind11_fail("Cannot overload existing non-function object \"" + std::string(rec->name) + + "\" with a function of the same name"); + } + + if (!chain) { + /* No existing overload was found, create a new function object */ + rec->def = new PyMethodDef(); + std::memset(rec->def, 0, sizeof(PyMethodDef)); + rec->def->ml_name = rec->name; + rec->def->ml_meth = reinterpret_cast(reinterpret_cast(*dispatcher)); + rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; + + capsule rec_capsule(rec, [](void *ptr) { + destruct((detail::function_record *) ptr); + }); + + object scope_module; + if (rec->scope) { + if (hasattr(rec->scope, "__module__")) { + scope_module = rec->scope.attr("__module__"); + } else if (hasattr(rec->scope, "__name__")) { + scope_module = rec->scope.attr("__name__"); + } + } + + m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); + if (!m_ptr) + pybind11_fail("cpp_function::cpp_function(): Could not allocate function object"); + } else { + /* Append at the end of the overload chain */ + m_ptr = rec->sibling.ptr(); + inc_ref(); + chain_start = chain; + if (chain->is_method != rec->is_method) + pybind11_fail("overloading a method with both static and instance methods is not supported; " + #if defined(NDEBUG) + "compile in debug mode for more details" + #else + "error while attempting to bind " + std::string(rec->is_method ? "instance" : "static") + " method " + + std::string(pybind11::str(rec->scope.attr("__name__"))) + "." + std::string(rec->name) + signature + #endif + ); + while (chain->next) + chain = chain->next; + chain->next = rec; + } + + std::string signatures; + int index = 0; + /* Create a nice pydoc rec including all signatures and + docstrings of the functions in the overload chain */ + if (chain && options::show_function_signatures()) { + // First a generic signature + signatures += rec->name; + signatures += "(*args, **kwargs)\n"; + signatures += "Overloaded function.\n\n"; + } + // Then specific overload signatures + bool first_user_def = true; + for (auto it = chain_start; it != nullptr; it = it->next) { + if (options::show_function_signatures()) { + if (index > 0) signatures += "\n"; + if (chain) + signatures += std::to_string(++index) + ". "; + signatures += rec->name; + signatures += it->signature; + signatures += "\n"; + } + if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) { + // If we're appending another docstring, and aren't printing function signatures, we + // need to append a newline first: + if (!options::show_function_signatures()) { + if (first_user_def) first_user_def = false; + else signatures += "\n"; + } + if (options::show_function_signatures()) signatures += "\n"; + signatures += it->doc; + if (options::show_function_signatures()) signatures += "\n"; + } + } + + /* Install docstring */ + PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; + if (func->m_ml->ml_doc) + std::free(const_cast(func->m_ml->ml_doc)); + func->m_ml->ml_doc = strdup(signatures.c_str()); + + if (rec->is_method) { + m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); + if (!m_ptr) + pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); + Py_DECREF(func); + } + } + + /// When a cpp_function is GCed, release any memory allocated by pybind11 + static void destruct(detail::function_record *rec) { + while (rec) { + detail::function_record *next = rec->next; + if (rec->free_data) + rec->free_data(rec); + std::free((char *) rec->name); + std::free((char *) rec->doc); + std::free((char *) rec->signature); + for (auto &arg: rec->args) { + std::free(const_cast(arg.name)); + std::free(const_cast(arg.descr)); + arg.value.dec_ref(); + } + if (rec->def) { + std::free(const_cast(rec->def->ml_doc)); + delete rec->def; + } + delete rec; + rec = next; + } + } + + /// Main dispatch logic for calls to functions bound using pybind11 + static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { + using namespace detail; + + /* Iterator over the list of potentially admissible overloads */ + const function_record *overloads = (function_record *) PyCapsule_GetPointer(self, nullptr), + *it = overloads; + + /* Need to know how many arguments + keyword arguments there are to pick the right overload */ + const size_t n_args_in = (size_t) PyTuple_GET_SIZE(args_in); + + handle parent = n_args_in > 0 ? PyTuple_GET_ITEM(args_in, 0) : nullptr, + result = PYBIND11_TRY_NEXT_OVERLOAD; + + auto self_value_and_holder = value_and_holder(); + if (overloads->is_constructor) { + const auto tinfo = get_type_info((PyTypeObject *) overloads->scope.ptr()); + const auto pi = reinterpret_cast(parent.ptr()); + self_value_and_holder = pi->get_value_and_holder(tinfo, false); + + if (!self_value_and_holder.type || !self_value_and_holder.inst) { + PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid `self` argument"); + return nullptr; + } + + // If this value is already registered it must mean __init__ is invoked multiple times; + // we really can't support that in C++, so just ignore the second __init__. + if (self_value_and_holder.instance_registered()) + return none().release().ptr(); + } + + try { + // We do this in two passes: in the first pass, we load arguments with `convert=false`; + // in the second, we allow conversion (except for arguments with an explicit + // py::arg().noconvert()). This lets us prefer calls without conversion, with + // conversion as a fallback. + std::vector second_pass; + + // However, if there are no overloads, we can just skip the no-convert pass entirely + const bool overloaded = it != nullptr && it->next != nullptr; + + for (; it != nullptr; it = it->next) { + + /* For each overload: + 1. Copy all positional arguments we were given, also checking to make sure that + named positional arguments weren't *also* specified via kwarg. + 2. If we weren't given enough, try to make up the omitted ones by checking + whether they were provided by a kwarg matching the `py::arg("name")` name. If + so, use it (and remove it from kwargs; if not, see if the function binding + provided a default that we can use. + 3. Ensure that either all keyword arguments were "consumed", or that the function + takes a kwargs argument to accept unconsumed kwargs. + 4. Any positional arguments still left get put into a tuple (for args), and any + leftover kwargs get put into a dict. + 5. Pack everything into a vector; if we have py::args or py::kwargs, they are an + extra tuple or dict at the end of the positional arguments. + 6. Call the function call dispatcher (function_record::impl) + + If one of these fail, move on to the next overload and keep trying until we get a + result other than PYBIND11_TRY_NEXT_OVERLOAD. + */ + + const function_record &func = *it; + size_t pos_args = func.nargs; // Number of positional arguments that we need + if (func.has_args) --pos_args; // (but don't count py::args + if (func.has_kwargs) --pos_args; // or py::kwargs) + + if (!func.has_args && n_args_in > pos_args) + continue; // Too many arguments for this overload + + if (n_args_in < pos_args && func.args.size() < pos_args) + continue; // Not enough arguments given, and not enough defaults to fill in the blanks + + function_call call(func, parent); + + size_t args_to_copy = (std::min)(pos_args, n_args_in); // Protect std::min with parentheses + size_t args_copied = 0; + + // 0. Inject new-style `self` argument + if (func.is_new_style_constructor) { + // The `value` may have been preallocated by an old-style `__init__` + // if it was a preceding candidate for overload resolution. + if (self_value_and_holder) + self_value_and_holder.type->dealloc(self_value_and_holder); + + call.init_self = PyTuple_GET_ITEM(args_in, 0); + call.args.push_back(reinterpret_cast(&self_value_and_holder)); + call.args_convert.push_back(false); + ++args_copied; + } + + // 1. Copy any position arguments given. + bool bad_arg = false; + for (; args_copied < args_to_copy; ++args_copied) { + const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; + if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { + bad_arg = true; + break; + } + + handle arg(PyTuple_GET_ITEM(args_in, args_copied)); + if (arg_rec && !arg_rec->none && arg.is_none()) { + bad_arg = true; + break; + } + call.args.push_back(arg); + call.args_convert.push_back(arg_rec ? arg_rec->convert : true); + } + if (bad_arg) + continue; // Maybe it was meant for another overload (issue #688) + + // We'll need to copy this if we steal some kwargs for defaults + dict kwargs = reinterpret_borrow(kwargs_in); + + // 2. Check kwargs and, failing that, defaults that may help complete the list + if (args_copied < pos_args) { + bool copied_kwargs = false; + + for (; args_copied < pos_args; ++args_copied) { + const auto &arg = func.args[args_copied]; + + handle value; + if (kwargs_in && arg.name) + value = PyDict_GetItemString(kwargs.ptr(), arg.name); + + if (value) { + // Consume a kwargs value + if (!copied_kwargs) { + kwargs = reinterpret_steal(PyDict_Copy(kwargs.ptr())); + copied_kwargs = true; + } + PyDict_DelItemString(kwargs.ptr(), arg.name); + } else if (arg.value) { + value = arg.value; + } + + if (value) { + call.args.push_back(value); + call.args_convert.push_back(arg.convert); + } + else + break; + } + + if (args_copied < pos_args) + continue; // Not enough arguments, defaults, or kwargs to fill the positional arguments + } + + // 3. Check everything was consumed (unless we have a kwargs arg) + if (kwargs && kwargs.size() > 0 && !func.has_kwargs) + continue; // Unconsumed kwargs, but no py::kwargs argument to accept them + + // 4a. If we have a py::args argument, create a new tuple with leftovers + if (func.has_args) { + tuple extra_args; + if (args_to_copy == 0) { + // We didn't copy out any position arguments from the args_in tuple, so we + // can reuse it directly without copying: + extra_args = reinterpret_borrow(args_in); + } else if (args_copied >= n_args_in) { + extra_args = tuple(0); + } else { + size_t args_size = n_args_in - args_copied; + extra_args = tuple(args_size); + for (size_t i = 0; i < args_size; ++i) { + extra_args[i] = PyTuple_GET_ITEM(args_in, args_copied + i); + } + } + call.args.push_back(extra_args); + call.args_convert.push_back(false); + call.args_ref = std::move(extra_args); + } + + // 4b. If we have a py::kwargs, pass on any remaining kwargs + if (func.has_kwargs) { + if (!kwargs.ptr()) + kwargs = dict(); // If we didn't get one, send an empty one + call.args.push_back(kwargs); + call.args_convert.push_back(false); + call.kwargs_ref = std::move(kwargs); + } + + // 5. Put everything in a vector. Not technically step 5, we've been building it + // in `call.args` all along. + #if !defined(NDEBUG) + if (call.args.size() != func.nargs || call.args_convert.size() != func.nargs) + pybind11_fail("Internal error: function call dispatcher inserted wrong number of arguments!"); + #endif + + std::vector second_pass_convert; + if (overloaded) { + // We're in the first no-convert pass, so swap out the conversion flags for a + // set of all-false flags. If the call fails, we'll swap the flags back in for + // the conversion-allowed call below. + second_pass_convert.resize(func.nargs, false); + call.args_convert.swap(second_pass_convert); + } + + // 6. Call the function. + try { + loader_life_support guard{}; + result = func.impl(call); + } catch (reference_cast_error &) { + result = PYBIND11_TRY_NEXT_OVERLOAD; + } + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) + break; + + if (overloaded) { + // The (overloaded) call failed; if the call has at least one argument that + // permits conversion (i.e. it hasn't been explicitly specified `.noconvert()`) + // then add this call to the list of second pass overloads to try. + for (size_t i = func.is_method ? 1 : 0; i < pos_args; i++) { + if (second_pass_convert[i]) { + // Found one: swap the converting flags back in and store the call for + // the second pass. + call.args_convert.swap(second_pass_convert); + second_pass.push_back(std::move(call)); + break; + } + } + } + } + + if (overloaded && !second_pass.empty() && result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + // The no-conversion pass finished without success, try again with conversion allowed + for (auto &call : second_pass) { + try { + loader_life_support guard{}; + result = call.func.impl(call); + } catch (reference_cast_error &) { + result = PYBIND11_TRY_NEXT_OVERLOAD; + } + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) { + // The error reporting logic below expects 'it' to be valid, as it would be + // if we'd encountered this failure in the first-pass loop. + if (!result) + it = &call.func; + break; + } + } + } + } catch (error_already_set &e) { + e.restore(); + return nullptr; +#if defined(__GNUG__) && !defined(__clang__) + } catch ( abi::__forced_unwind& ) { + throw; +#endif + } catch (...) { + /* When an exception is caught, give each registered exception + translator a chance to translate it to a Python exception + in reverse order of registration. + + A translator may choose to do one of the following: + + - catch the exception and call PyErr_SetString or PyErr_SetObject + to set a standard (or custom) Python exception, or + - do nothing and let the exception fall through to the next translator, or + - delegate translation to the next translator by throwing a new type of exception. */ + + auto last_exception = std::current_exception(); + auto ®istered_exception_translators = get_internals().registered_exception_translators; + for (auto& translator : registered_exception_translators) { + try { + translator(last_exception); + } catch (...) { + last_exception = std::current_exception(); + continue; + } + return nullptr; + } + PyErr_SetString(PyExc_SystemError, "Exception escaped from default exception translator!"); + return nullptr; + } + + auto append_note_if_missing_header_is_suspected = [](std::string &msg) { + if (msg.find("std::") != std::string::npos) { + msg += "\n\n" + "Did you forget to `#include `? Or ,\n" + ", , etc. Some automatic\n" + "conversions are optional and require extra headers to be included\n" + "when compiling your pybind11 module."; + } + }; + + if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + if (overloads->is_operator) + return handle(Py_NotImplemented).inc_ref().ptr(); + + std::string msg = std::string(overloads->name) + "(): incompatible " + + std::string(overloads->is_constructor ? "constructor" : "function") + + " arguments. The following argument types are supported:\n"; + + int ctr = 0; + for (const function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { + msg += " "+ std::to_string(++ctr) + ". "; + + bool wrote_sig = false; + if (overloads->is_constructor) { + // For a constructor, rewrite `(self: Object, arg0, ...) -> NoneType` as `Object(arg0, ...)` + std::string sig = it2->signature; + size_t start = sig.find('(') + 7; // skip "(self: " + if (start < sig.size()) { + // End at the , for the next argument + size_t end = sig.find(", "), next = end + 2; + size_t ret = sig.rfind(" -> "); + // Or the ), if there is no comma: + if (end >= sig.size()) next = end = sig.find(')'); + if (start < end && next < sig.size()) { + msg.append(sig, start, end - start); + msg += '('; + msg.append(sig, next, ret - next); + wrote_sig = true; + } + } + } + if (!wrote_sig) msg += it2->signature; + + msg += "\n"; + } + msg += "\nInvoked with: "; + auto args_ = reinterpret_borrow(args_in); + bool some_args = false; + for (size_t ti = overloads->is_constructor ? 1 : 0; ti < args_.size(); ++ti) { + if (!some_args) some_args = true; + else msg += ", "; + msg += pybind11::repr(args_[ti]); + } + if (kwargs_in) { + auto kwargs = reinterpret_borrow(kwargs_in); + if (kwargs.size() > 0) { + if (some_args) msg += "; "; + msg += "kwargs: "; + bool first = true; + for (auto kwarg : kwargs) { + if (first) first = false; + else msg += ", "; + msg += pybind11::str("{}={!r}").format(kwarg.first, kwarg.second); + } + } + } + + append_note_if_missing_header_is_suspected(msg); + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else if (!result) { + std::string msg = "Unable to convert function return value to a " + "Python type! The signature was\n\t"; + msg += it->signature; + append_note_if_missing_header_is_suspected(msg); + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else { + if (overloads->is_constructor && !self_value_and_holder.holder_constructed()) { + auto *pi = reinterpret_cast(parent.ptr()); + self_value_and_holder.type->init_instance(pi, nullptr); + } + return result.ptr(); + } + } +}; + +/// Wrapper for Python extension modules +class module : public object { +public: + PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check) + + /// Create a new top-level Python module with the given name and docstring + explicit module(const char *name, const char *doc = nullptr) { + if (!options::show_user_defined_docstrings()) doc = nullptr; +#if PY_MAJOR_VERSION >= 3 + PyModuleDef *def = new PyModuleDef(); + std::memset(def, 0, sizeof(PyModuleDef)); + def->m_name = name; + def->m_doc = doc; + def->m_size = -1; + Py_INCREF(def); + m_ptr = PyModule_Create(def); +#else + m_ptr = Py_InitModule3(name, nullptr, doc); +#endif + if (m_ptr == nullptr) + pybind11_fail("Internal error in module::module()"); + inc_ref(); + } + + /** \rst + Create Python binding for a new function within the module scope. ``Func`` + can be a plain C++ function, a function pointer, or a lambda function. For + details on the ``Extra&& ... extra`` argument, see section :ref:`extras`. + \endrst */ + template + module &def(const char *name_, Func &&f, const Extra& ... extra) { + cpp_function func(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); + // NB: allow overwriting here because cpp_function sets up a chain with the intention of + // overwriting (and has already checked internally that it isn't overwriting non-functions). + add_object(name_, func, true /* overwrite */); + return *this; + } + + /** \rst + Create and return a new Python submodule with the given name and docstring. + This also works recursively, i.e. + + .. code-block:: cpp + + py::module m("example", "pybind11 example plugin"); + py::module m2 = m.def_submodule("sub", "A submodule of 'example'"); + py::module m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); + \endrst */ + module def_submodule(const char *name, const char *doc = nullptr) { + std::string full_name = std::string(PyModule_GetName(m_ptr)) + + std::string(".") + std::string(name); + auto result = reinterpret_borrow(PyImport_AddModule(full_name.c_str())); + if (doc && options::show_user_defined_docstrings()) + result.attr("__doc__") = pybind11::str(doc); + attr(name) = result; + return result; + } + + /// Import and return a module or throws `error_already_set`. + static module import(const char *name) { + PyObject *obj = PyImport_ImportModule(name); + if (!obj) + throw error_already_set(); + return reinterpret_steal(obj); + } + + /// Reload the module or throws `error_already_set`. + void reload() { + PyObject *obj = PyImport_ReloadModule(ptr()); + if (!obj) + throw error_already_set(); + *this = reinterpret_steal(obj); + } + + // Adds an object to the module using the given name. Throws if an object with the given name + // already exists. + // + // overwrite should almost always be false: attempting to overwrite objects that pybind11 has + // established will, in most cases, break things. + PYBIND11_NOINLINE void add_object(const char *name, handle obj, bool overwrite = false) { + if (!overwrite && hasattr(*this, name)) + pybind11_fail("Error during initialization: multiple incompatible definitions with name \"" + + std::string(name) + "\""); + + PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); + } +}; + +/// \ingroup python_builtins +/// Return a dictionary representing the global variables in the current execution frame, +/// or ``__main__.__dict__`` if there is no frame (usually when the interpreter is embedded). +inline dict globals() { + PyObject *p = PyEval_GetGlobals(); + return reinterpret_borrow(p ? p : module::import("__main__").attr("__dict__").ptr()); +} + +NAMESPACE_BEGIN(detail) +/// Generic support for creating new Python heap types +class generic_type : public object { + template friend class class_; +public: + PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) +protected: + void initialize(const type_record &rec) { + if (rec.scope && hasattr(rec.scope, rec.name)) + pybind11_fail("generic_type: cannot initialize type \"" + std::string(rec.name) + + "\": an object with that name is already defined"); + + if (rec.module_local ? get_local_type_info(*rec.type) : get_global_type_info(*rec.type)) + pybind11_fail("generic_type: type \"" + std::string(rec.name) + + "\" is already registered!"); + + m_ptr = make_new_python_type(rec); + + /* Register supplemental type information in C++ dict */ + auto *tinfo = new detail::type_info(); + tinfo->type = (PyTypeObject *) m_ptr; + tinfo->cpptype = rec.type; + tinfo->type_size = rec.type_size; + tinfo->type_align = rec.type_align; + tinfo->operator_new = rec.operator_new; + tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); + tinfo->init_instance = rec.init_instance; + tinfo->dealloc = rec.dealloc; + tinfo->simple_type = true; + tinfo->simple_ancestors = true; + tinfo->default_holder = rec.default_holder; + tinfo->module_local = rec.module_local; + + auto &internals = get_internals(); + auto tindex = std::type_index(*rec.type); + tinfo->direct_conversions = &internals.direct_conversions[tindex]; + if (rec.module_local) + registered_local_types_cpp()[tindex] = tinfo; + else + internals.registered_types_cpp[tindex] = tinfo; + internals.registered_types_py[(PyTypeObject *) m_ptr] = { tinfo }; + + if (rec.bases.size() > 1 || rec.multiple_inheritance) { + mark_parents_nonsimple(tinfo->type); + tinfo->simple_ancestors = false; + } + else if (rec.bases.size() == 1) { + auto parent_tinfo = get_type_info((PyTypeObject *) rec.bases[0].ptr()); + tinfo->simple_ancestors = parent_tinfo->simple_ancestors; + } + + if (rec.module_local) { + // Stash the local typeinfo and loader so that external modules can access it. + tinfo->module_local_load = &type_caster_generic::local_load; + setattr(m_ptr, PYBIND11_MODULE_LOCAL_ID, capsule(tinfo)); + } + } + + /// Helper function which tags all parents of a type using mult. inheritance + void mark_parents_nonsimple(PyTypeObject *value) { + auto t = reinterpret_borrow(value->tp_bases); + for (handle h : t) { + auto tinfo2 = get_type_info((PyTypeObject *) h.ptr()); + if (tinfo2) + tinfo2->simple_type = false; + mark_parents_nonsimple((PyTypeObject *) h.ptr()); + } + } + + void install_buffer_funcs( + buffer_info *(*get_buffer)(PyObject *, void *), + void *get_buffer_data) { + PyHeapTypeObject *type = (PyHeapTypeObject*) m_ptr; + auto tinfo = detail::get_type_info(&type->ht_type); + + if (!type->ht_type.tp_as_buffer) + pybind11_fail( + "To be able to register buffer protocol support for the type '" + + std::string(tinfo->type->tp_name) + + "' the associated class<>(..) invocation must " + "include the pybind11::buffer_protocol() annotation!"); + + tinfo->get_buffer = get_buffer; + tinfo->get_buffer_data = get_buffer_data; + } + + // rec_func must be set for either fget or fset. + void def_property_static_impl(const char *name, + handle fget, handle fset, + detail::function_record *rec_func) { + const auto is_static = rec_func && !(rec_func->is_method && rec_func->scope); + const auto has_doc = rec_func && rec_func->doc && pybind11::options::show_user_defined_docstrings(); + auto property = handle((PyObject *) (is_static ? get_internals().static_property_type + : &PyProperty_Type)); + attr(name) = property(fget.ptr() ? fget : none(), + fset.ptr() ? fset : none(), + /*deleter*/none(), + pybind11::str(has_doc ? rec_func->doc : "")); + } +}; + +/// Set the pointer to operator new if it exists. The cast is needed because it can be overloaded. +template (T::operator new))>> +void set_operator_new(type_record *r) { r->operator_new = &T::operator new; } + +template void set_operator_new(...) { } + +template struct has_operator_delete : std::false_type { }; +template struct has_operator_delete(T::operator delete))>> + : std::true_type { }; +template struct has_operator_delete_size : std::false_type { }; +template struct has_operator_delete_size(T::operator delete))>> + : std::true_type { }; +/// Call class-specific delete if it exists or global otherwise. Can also be an overload set. +template ::value, int> = 0> +void call_operator_delete(T *p, size_t, size_t) { T::operator delete(p); } +template ::value && has_operator_delete_size::value, int> = 0> +void call_operator_delete(T *p, size_t s, size_t) { T::operator delete(p, s); } + +inline void call_operator_delete(void *p, size_t s, size_t a) { + (void)s; (void)a; + #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) + if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + #ifdef __cpp_sized_deallocation + ::operator delete(p, s, std::align_val_t(a)); + #else + ::operator delete(p, std::align_val_t(a)); + #endif + return; + } + #endif + #ifdef __cpp_sized_deallocation + ::operator delete(p, s); + #else + ::operator delete(p); + #endif +} + +NAMESPACE_END(detail) + +/// Given a pointer to a member function, cast it to its `Derived` version. +/// Forward everything else unchanged. +template +auto method_adaptor(F &&f) -> decltype(std::forward(f)) { return std::forward(f); } + +template +auto method_adaptor(Return (Class::*pmf)(Args...)) -> Return (Derived::*)(Args...) { + static_assert(detail::is_accessible_base_of::value, + "Cannot bind an inaccessible base class method; use a lambda definition instead"); + return pmf; +} + +template +auto method_adaptor(Return (Class::*pmf)(Args...) const) -> Return (Derived::*)(Args...) const { + static_assert(detail::is_accessible_base_of::value, + "Cannot bind an inaccessible base class method; use a lambda definition instead"); + return pmf; +} + +template +class class_ : public detail::generic_type { + template using is_holder = detail::is_holder_type; + template using is_subtype = detail::is_strict_base_of; + template using is_base = detail::is_strict_base_of; + // struct instead of using here to help MSVC: + template struct is_valid_class_option : + detail::any_of, is_subtype, is_base> {}; + +public: + using type = type_; + using type_alias = detail::exactly_one_t; + constexpr static bool has_alias = !std::is_void::value; + using holder_type = detail::exactly_one_t, options...>; + + static_assert(detail::all_of...>::value, + "Unknown/invalid class_ template parameters provided"); + + static_assert(!has_alias || std::is_polymorphic::value, + "Cannot use an alias class with a non-polymorphic type"); + + PYBIND11_OBJECT(class_, generic_type, PyType_Check) + + template + class_(handle scope, const char *name, const Extra &... extra) { + using namespace detail; + + // MI can only be specified via class_ template options, not constructor parameters + static_assert( + none_of...>::value || // no base class arguments, or: + ( constexpr_sum(is_pyobject::value...) == 1 && // Exactly one base + constexpr_sum(is_base::value...) == 0 && // no template option bases + none_of...>::value), // no multiple_inheritance attr + "Error: multiple inheritance bases must be specified via class_ template options"); + + type_record record; + record.scope = scope; + record.name = name; + record.type = &typeid(type); + record.type_size = sizeof(conditional_t); + record.type_align = alignof(conditional_t&); + record.holder_size = sizeof(holder_type); + record.init_instance = init_instance; + record.dealloc = dealloc; + record.default_holder = detail::is_instantiation::value; + + set_operator_new(&record); + + /* Register base classes specified via template arguments to class_, if any */ + PYBIND11_EXPAND_SIDE_EFFECTS(add_base(record)); + + /* Process optional arguments, if any */ + process_attributes::init(extra..., &record); + + generic_type::initialize(record); + + if (has_alias) { + auto &instances = record.module_local ? registered_local_types_cpp() : get_internals().registered_types_cpp; + instances[std::type_index(typeid(type_alias))] = instances[std::type_index(typeid(type))]; + } + } + + template ::value, int> = 0> + static void add_base(detail::type_record &rec) { + rec.add_base(typeid(Base), [](void *src) -> void * { + return static_cast(reinterpret_cast(src)); + }); + } + + template ::value, int> = 0> + static void add_base(detail::type_record &) { } + + template + class_ &def(const char *name_, Func&& f, const Extra&... extra) { + cpp_function cf(method_adaptor(std::forward(f)), name(name_), is_method(*this), + sibling(getattr(*this, name_, none())), extra...); + attr(cf.name()) = cf; + return *this; + } + + template class_ & + def_static(const char *name_, Func &&f, const Extra&... extra) { + static_assert(!std::is_member_function_pointer::value, + "def_static(...) called with a non-static member function pointer"); + cpp_function cf(std::forward(f), name(name_), scope(*this), + sibling(getattr(*this, name_, none())), extra...); + attr(cf.name()) = staticmethod(cf); + return *this; + } + + template + class_ &def(const detail::op_ &op, const Extra&... extra) { + op.execute(*this, extra...); + return *this; + } + + template + class_ & def_cast(const detail::op_ &op, const Extra&... extra) { + op.execute_cast(*this, extra...); + return *this; + } + + template + class_ &def(const detail::initimpl::constructor &init, const Extra&... extra) { + init.execute(*this, extra...); + return *this; + } + + template + class_ &def(const detail::initimpl::alias_constructor &init, const Extra&... extra) { + init.execute(*this, extra...); + return *this; + } + + template + class_ &def(detail::initimpl::factory &&init, const Extra&... extra) { + std::move(init).execute(*this, extra...); + return *this; + } + + template + class_ &def(detail::initimpl::pickle_factory &&pf, const Extra &...extra) { + std::move(pf).execute(*this, extra...); + return *this; + } + + template class_& def_buffer(Func &&func) { + struct capture { Func func; }; + capture *ptr = new capture { std::forward(func) }; + install_buffer_funcs([](PyObject *obj, void *ptr) -> buffer_info* { + detail::make_caster caster; + if (!caster.load(obj, false)) + return nullptr; + return new buffer_info(((capture *) ptr)->func(caster)); + }, ptr); + return *this; + } + + template + class_ &def_buffer(Return (Class::*func)(Args...)) { + return def_buffer([func] (type &obj) { return (obj.*func)(); }); + } + + template + class_ &def_buffer(Return (Class::*func)(Args...) const) { + return def_buffer([func] (const type &obj) { return (obj.*func)(); }); + } + + template + class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { + static_assert(std::is_same::value || std::is_base_of::value, "def_readwrite() requires a class member (or base class member)"); + cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)), + fset([pm](type &c, const D &value) { c.*pm = value; }, is_method(*this)); + def_property(name, fget, fset, return_value_policy::reference_internal, extra...); + return *this; + } + + template + class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { + static_assert(std::is_same::value || std::is_base_of::value, "def_readonly() requires a class member (or base class member)"); + cpp_function fget([pm](const type &c) -> const D &{ return c.*pm; }, is_method(*this)); + def_property_readonly(name, fget, return_value_policy::reference_internal, extra...); + return *this; + } + + template + class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)), + fset([pm](object, const D &value) { *pm = value; }, scope(*this)); + def_property_static(name, fget, fset, return_value_policy::reference, extra...); + return *this; + } + + template + class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, scope(*this)); + def_property_readonly_static(name, fget, return_value_policy::reference, extra...); + return *this; + } + + /// Uses return_value_policy::reference_internal by default + template + class_ &def_property_readonly(const char *name, const Getter &fget, const Extra& ...extra) { + return def_property_readonly(name, cpp_function(method_adaptor(fget)), + return_value_policy::reference_internal, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) { + return def_property(name, fget, nullptr, extra...); + } + + /// Uses return_value_policy::reference by default + template + class_ &def_property_readonly_static(const char *name, const Getter &fget, const Extra& ...extra) { + return def_property_readonly_static(name, cpp_function(fget), return_value_policy::reference, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) { + return def_property_static(name, fget, nullptr, extra...); + } + + /// Uses return_value_policy::reference_internal by default + template + class_ &def_property(const char *name, const Getter &fget, const Setter &fset, const Extra& ...extra) { + return def_property(name, fget, cpp_function(method_adaptor(fset)), extra...); + } + template + class_ &def_property(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property(name, cpp_function(method_adaptor(fget)), fset, + return_value_policy::reference_internal, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property_static(name, fget, fset, is_method(*this), extra...); + } + + /// Uses return_value_policy::reference by default + template + class_ &def_property_static(const char *name, const Getter &fget, const cpp_function &fset, const Extra& ...extra) { + return def_property_static(name, cpp_function(fget), fset, return_value_policy::reference, extra...); + } + + /// Uses cpp_function's return_value_policy by default + template + class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) { + static_assert( 0 == detail::constexpr_sum(std::is_base_of::value...), + "Argument annotations are not allowed for properties"); + auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset); + auto *rec_active = rec_fget; + if (rec_fget) { + char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */ + detail::process_attributes::init(extra..., rec_fget); + if (rec_fget->doc && rec_fget->doc != doc_prev) { + free(doc_prev); + rec_fget->doc = strdup(rec_fget->doc); + } + } + if (rec_fset) { + char *doc_prev = rec_fset->doc; + detail::process_attributes::init(extra..., rec_fset); + if (rec_fset->doc && rec_fset->doc != doc_prev) { + free(doc_prev); + rec_fset->doc = strdup(rec_fset->doc); + } + if (! rec_active) rec_active = rec_fset; + } + def_property_static_impl(name, fget, fset, rec_active); + return *this; + } + +private: + /// Initialize holder object, variant 1: object derives from enable_shared_from_this + template + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, + const holder_type * /* unused */, const std::enable_shared_from_this * /* dummy */) { + try { + auto sh = std::dynamic_pointer_cast( + v_h.value_ptr()->shared_from_this()); + if (sh) { + new (std::addressof(v_h.holder())) holder_type(std::move(sh)); + v_h.set_holder_constructed(); + } + } catch (const std::bad_weak_ptr &) {} + + if (!v_h.holder_constructed() && inst->owned) { + new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); + v_h.set_holder_constructed(); + } + } + + static void init_holder_from_existing(const detail::value_and_holder &v_h, + const holder_type *holder_ptr, std::true_type /*is_copy_constructible*/) { + new (std::addressof(v_h.holder())) holder_type(*reinterpret_cast(holder_ptr)); + } + + static void init_holder_from_existing(const detail::value_and_holder &v_h, + const holder_type *holder_ptr, std::false_type /*is_copy_constructible*/) { + new (std::addressof(v_h.holder())) holder_type(std::move(*const_cast(holder_ptr))); + } + + /// Initialize holder object, variant 2: try to construct from existing holder object, if possible + static void init_holder(detail::instance *inst, detail::value_and_holder &v_h, + const holder_type *holder_ptr, const void * /* dummy -- not enable_shared_from_this) */) { + if (holder_ptr) { + init_holder_from_existing(v_h, holder_ptr, std::is_copy_constructible()); + v_h.set_holder_constructed(); + } else if (inst->owned || detail::always_construct_holder::value) { + new (std::addressof(v_h.holder())) holder_type(v_h.value_ptr()); + v_h.set_holder_constructed(); + } + } + + /// Performs instance initialization including constructing a holder and registering the known + /// instance. Should be called as soon as the `type` value_ptr is set for an instance. Takes an + /// optional pointer to an existing holder to use; if not specified and the instance is + /// `.owned`, a new holder will be constructed to manage the value pointer. + static void init_instance(detail::instance *inst, const void *holder_ptr) { + auto v_h = inst->get_value_and_holder(detail::get_type_info(typeid(type))); + if (!v_h.instance_registered()) { + register_instance(inst, v_h.value_ptr(), v_h.type); + v_h.set_instance_registered(); + } + init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr()); + } + + /// Deallocates an instance; via holder, if constructed; otherwise via operator delete. + static void dealloc(detail::value_and_holder &v_h) { + if (v_h.holder_constructed()) { + v_h.holder().~holder_type(); + v_h.set_holder_constructed(false); + } + else { + detail::call_operator_delete(v_h.value_ptr(), + v_h.type->type_size, + v_h.type->type_align + ); + } + v_h.value_ptr() = nullptr; + } + + static detail::function_record *get_function_record(handle h) { + h = detail::get_function(h); + return h ? (detail::function_record *) reinterpret_borrow(PyCFunction_GET_SELF(h.ptr())) + : nullptr; + } +}; + +/// Binds an existing constructor taking arguments Args... +template detail::initimpl::constructor init() { return {}; } +/// Like `init()`, but the instance is always constructed through the alias class (even +/// when not inheriting on the Python side). +template detail::initimpl::alias_constructor init_alias() { return {}; } + +/// Binds a factory function as a constructor +template > +Ret init(Func &&f) { return {std::forward(f)}; } + +/// Dual-argument factory function: the first function is called when no alias is needed, the second +/// when an alias is needed (i.e. due to python-side inheritance). Arguments must be identical. +template > +Ret init(CFunc &&c, AFunc &&a) { + return {std::forward(c), std::forward(a)}; +} + +/// Binds pickling functions `__getstate__` and `__setstate__` and ensures that the type +/// returned by `__getstate__` is the same as the argument accepted by `__setstate__`. +template +detail::initimpl::pickle_factory pickle(GetState &&g, SetState &&s) { + return {std::forward(g), std::forward(s)}; +} + +NAMESPACE_BEGIN(detail) +struct enum_base { + enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { } + + PYBIND11_NOINLINE void init(bool is_arithmetic, bool is_convertible) { + m_base.attr("__entries") = dict(); + auto property = handle((PyObject *) &PyProperty_Type); + auto static_property = handle((PyObject *) get_internals().static_property_type); + + m_base.attr("__repr__") = cpp_function( + [](handle arg) -> str { + handle type = arg.get_type(); + object type_name = type.attr("__name__"); + dict entries = type.attr("__entries"); + for (const auto &kv : entries) { + object other = kv.second[int_(0)]; + if (other.equal(arg)) + return pybind11::str("{}.{}").format(type_name, kv.first); + } + return pybind11::str("{}.???").format(type_name); + }, is_method(m_base) + ); + + m_base.attr("name") = property(cpp_function( + [](handle arg) -> str { + dict entries = arg.get_type().attr("__entries"); + for (const auto &kv : entries) { + if (handle(kv.second[int_(0)]).equal(arg)) + return pybind11::str(kv.first); + } + return "???"; + }, is_method(m_base) + )); + + m_base.attr("__doc__") = static_property(cpp_function( + [](handle arg) -> std::string { + std::string docstring; + dict entries = arg.attr("__entries"); + if (((PyTypeObject *) arg.ptr())->tp_doc) + docstring += std::string(((PyTypeObject *) arg.ptr())->tp_doc) + "\n\n"; + docstring += "Members:"; + for (const auto &kv : entries) { + auto key = std::string(pybind11::str(kv.first)); + auto comment = kv.second[int_(1)]; + docstring += "\n\n " + key; + if (!comment.is_none()) + docstring += " : " + (std::string) pybind11::str(comment); + } + return docstring; + } + ), none(), none(), ""); + + m_base.attr("__members__") = static_property(cpp_function( + [](handle arg) -> dict { + dict entries = arg.attr("__entries"), m; + for (const auto &kv : entries) + m[kv.first] = kv.second[int_(0)]; + return m; + }), none(), none(), "" + ); + + #define PYBIND11_ENUM_OP_STRICT(op, expr, strict_behavior) \ + m_base.attr(op) = cpp_function( \ + [](object a, object b) { \ + if (!a.get_type().is(b.get_type())) \ + strict_behavior; \ + return expr; \ + }, \ + is_method(m_base)) + + #define PYBIND11_ENUM_OP_CONV(op, expr) \ + m_base.attr(op) = cpp_function( \ + [](object a_, object b_) { \ + int_ a(a_), b(b_); \ + return expr; \ + }, \ + is_method(m_base)) + + #define PYBIND11_ENUM_OP_CONV_LHS(op, expr) \ + m_base.attr(op) = cpp_function( \ + [](object a_, object b) { \ + int_ a(a_); \ + return expr; \ + }, \ + is_method(m_base)) + + if (is_convertible) { + PYBIND11_ENUM_OP_CONV_LHS("__eq__", !b.is_none() && a.equal(b)); + PYBIND11_ENUM_OP_CONV_LHS("__ne__", b.is_none() || !a.equal(b)); + + if (is_arithmetic) { + PYBIND11_ENUM_OP_CONV("__lt__", a < b); + PYBIND11_ENUM_OP_CONV("__gt__", a > b); + PYBIND11_ENUM_OP_CONV("__le__", a <= b); + PYBIND11_ENUM_OP_CONV("__ge__", a >= b); + PYBIND11_ENUM_OP_CONV("__and__", a & b); + PYBIND11_ENUM_OP_CONV("__rand__", a & b); + PYBIND11_ENUM_OP_CONV("__or__", a | b); + PYBIND11_ENUM_OP_CONV("__ror__", a | b); + PYBIND11_ENUM_OP_CONV("__xor__", a ^ b); + PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b); + m_base.attr("__invert__") = cpp_function( + [](object arg) { return ~(int_(arg)); }, is_method(m_base)); + } + } else { + PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false); + PYBIND11_ENUM_OP_STRICT("__ne__", !int_(a).equal(int_(b)), return true); + + if (is_arithmetic) { + #define PYBIND11_THROW throw type_error("Expected an enumeration of matching type!"); + PYBIND11_ENUM_OP_STRICT("__lt__", int_(a) < int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__gt__", int_(a) > int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__le__", int_(a) <= int_(b), PYBIND11_THROW); + PYBIND11_ENUM_OP_STRICT("__ge__", int_(a) >= int_(b), PYBIND11_THROW); + #undef PYBIND11_THROW + } + } + + #undef PYBIND11_ENUM_OP_CONV_LHS + #undef PYBIND11_ENUM_OP_CONV + #undef PYBIND11_ENUM_OP_STRICT + + object getstate = cpp_function( + [](object arg) { return int_(arg); }, is_method(m_base)); + + m_base.attr("__getstate__") = getstate; + m_base.attr("__hash__") = getstate; + } + + PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) { + dict entries = m_base.attr("__entries"); + str name(name_); + if (entries.contains(name)) { + std::string type_name = (std::string) str(m_base.attr("__name__")); + throw value_error(type_name + ": element \"" + std::string(name_) + "\" already exists!"); + } + + entries[name] = std::make_pair(value, doc); + m_base.attr(name) = value; + } + + PYBIND11_NOINLINE void export_values() { + dict entries = m_base.attr("__entries"); + for (const auto &kv : entries) + m_parent.attr(kv.first) = kv.second[int_(0)]; + } + + handle m_base; + handle m_parent; +}; + +NAMESPACE_END(detail) + +/// Binds C++ enumerations and enumeration classes to Python +template class enum_ : public class_ { +public: + using Base = class_; + using Base::def; + using Base::attr; + using Base::def_property_readonly; + using Base::def_property_readonly_static; + using Scalar = typename std::underlying_type::type; + + template + enum_(const handle &scope, const char *name, const Extra&... extra) + : class_(scope, name, extra...), m_base(*this, scope) { + constexpr bool is_arithmetic = detail::any_of...>::value; + constexpr bool is_convertible = std::is_convertible::value; + m_base.init(is_arithmetic, is_convertible); + + def(init([](Scalar i) { return static_cast(i); })); + def("__int__", [](Type value) { return (Scalar) value; }); + #if PY_MAJOR_VERSION < 3 + def("__long__", [](Type value) { return (Scalar) value; }); + #endif + #if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 8) + def("__index__", [](Type value) { return (Scalar) value; }); + #endif + + cpp_function setstate( + [](Type &value, Scalar arg) { value = static_cast(arg); }, + is_method(*this)); + attr("__setstate__") = setstate; + } + + /// Export enumeration entries into the parent scope + enum_& export_values() { + m_base.export_values(); + return *this; + } + + /// Add an enumeration entry + enum_& value(char const* name, Type value, const char *doc = nullptr) { + m_base.value(name, pybind11::cast(value, return_value_policy::copy), doc); + return *this; + } + +private: + detail::enum_base m_base; +}; + +NAMESPACE_BEGIN(detail) + + +inline void keep_alive_impl(handle nurse, handle patient) { + if (!nurse || !patient) + pybind11_fail("Could not activate keep_alive!"); + + if (patient.is_none() || nurse.is_none()) + return; /* Nothing to keep alive or nothing to be kept alive by */ + + auto tinfo = all_type_info(Py_TYPE(nurse.ptr())); + if (!tinfo.empty()) { + /* It's a pybind-registered type, so we can store the patient in the + * internal list. */ + add_patient(nurse.ptr(), patient.ptr()); + } + else { + /* Fall back to clever approach based on weak references taken from + * Boost.Python. This is not used for pybind-registered types because + * the objects can be destroyed out-of-order in a GC pass. */ + cpp_function disable_lifesupport( + [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); + + weakref wr(nurse, disable_lifesupport); + + patient.inc_ref(); /* reference patient and leak the weak reference */ + (void) wr.release(); + } +} + +PYBIND11_NOINLINE inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret) { + auto get_arg = [&](size_t n) { + if (n == 0) + return ret; + else if (n == 1 && call.init_self) + return call.init_self; + else if (n <= call.args.size()) + return call.args[n - 1]; + return handle(); + }; + + keep_alive_impl(get_arg(Nurse), get_arg(Patient)); +} + +inline std::pair all_type_info_get_cache(PyTypeObject *type) { + auto res = get_internals().registered_types_py +#ifdef __cpp_lib_unordered_map_try_emplace + .try_emplace(type); +#else + .emplace(type, std::vector()); +#endif + if (res.second) { + // New cache entry created; set up a weak reference to automatically remove it if the type + // gets destroyed: + weakref((PyObject *) type, cpp_function([type](handle wr) { + get_internals().registered_types_py.erase(type); + wr.dec_ref(); + })).release(); + } + + return res; +} + +template +struct iterator_state { + Iterator it; + Sentinel end; + bool first_or_done; +}; + +NAMESPACE_END(detail) + +/// Makes a python iterator from a first and past-the-end C++ InputIterator. +template ()), + typename... Extra> +iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { + typedef detail::iterator_state state; + + if (!detail::get_type_info(typeid(state), false)) { + class_(handle(), "iterator", pybind11::module_local()) + .def("__iter__", [](state &s) -> state& { return s; }) + .def("__next__", [](state &s) -> ValueType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return *s.it; + }, std::forward(extra)..., Policy); + } + + return cast(state{first, last, true}); +} + +/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a +/// first and past-the-end InputIterator. +template ()).first), + typename... Extra> +iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { + typedef detail::iterator_state state; + + if (!detail::get_type_info(typeid(state), false)) { + class_(handle(), "iterator", pybind11::module_local()) + .def("__iter__", [](state &s) -> state& { return s; }) + .def("__next__", [](state &s) -> KeyType { + if (!s.first_or_done) + ++s.it; + else + s.first_or_done = false; + if (s.it == s.end) { + s.first_or_done = true; + throw stop_iteration(); + } + return (*s.it).first; + }, std::forward(extra)..., Policy); + } + + return cast(state{first, last, true}); +} + +/// Makes an iterator over values of an stl container or other container supporting +/// `std::begin()`/`std::end()` +template iterator make_iterator(Type &value, Extra&&... extra) { + return make_iterator(std::begin(value), std::end(value), extra...); +} + +/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting +/// `std::begin()`/`std::end()` +template iterator make_key_iterator(Type &value, Extra&&... extra) { + return make_key_iterator(std::begin(value), std::end(value), extra...); +} + +template void implicitly_convertible() { + struct set_flag { + bool &flag; + set_flag(bool &flag) : flag(flag) { flag = true; } + ~set_flag() { flag = false; } + }; + auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * { + static bool currently_used = false; + if (currently_used) // implicit conversions are non-reentrant + return nullptr; + set_flag flag_helper(currently_used); + if (!detail::make_caster().load(obj, false)) + return nullptr; + tuple args(1); + args[0] = obj; + PyObject *result = PyObject_Call((PyObject *) type, args.ptr(), nullptr); + if (result == nullptr) + PyErr_Clear(); + return result; + }; + + if (auto tinfo = detail::get_type_info(typeid(OutputType))) + tinfo->implicit_conversions.push_back(implicit_caster); + else + pybind11_fail("implicitly_convertible: Unable to find type " + type_id()); +} + +template +void register_exception_translator(ExceptionTranslator&& translator) { + detail::get_internals().registered_exception_translators.push_front( + std::forward(translator)); +} + +/** + * Wrapper to generate a new Python exception type. + * + * This should only be used with PyErr_SetString for now. + * It is not (yet) possible to use as a py::base. + * Template type argument is reserved for future use. + */ +template +class exception : public object { +public: + exception() = default; + exception(handle scope, const char *name, PyObject *base = PyExc_Exception) { + std::string full_name = scope.attr("__name__").cast() + + std::string(".") + name; + m_ptr = PyErr_NewException(const_cast(full_name.c_str()), base, NULL); + if (hasattr(scope, name)) + pybind11_fail("Error during initialization: multiple incompatible " + "definitions with name \"" + std::string(name) + "\""); + scope.attr(name) = *this; + } + + // Sets the current python exception to this exception object with the given message + void operator()(const char *message) { + PyErr_SetString(m_ptr, message); + } +}; + +NAMESPACE_BEGIN(detail) +// Returns a reference to a function-local static exception object used in the simple +// register_exception approach below. (It would be simpler to have the static local variable +// directly in register_exception, but that makes clang <3.5 segfault - issue #1349). +template +exception &get_exception_object() { static exception ex; return ex; } +NAMESPACE_END(detail) + +/** + * Registers a Python exception in `m` of the given `name` and installs an exception translator to + * translate the C++ exception to the created Python exception using the exceptions what() method. + * This is intended for simple exception translations; for more complex translation, register the + * exception object and translator directly. + */ +template +exception ®ister_exception(handle scope, + const char *name, + PyObject *base = PyExc_Exception) { + auto &ex = detail::get_exception_object(); + if (!ex) ex = exception(scope, name, base); + + register_exception_translator([](std::exception_ptr p) { + if (!p) return; + try { + std::rethrow_exception(p); + } catch (const CppException &e) { + detail::get_exception_object()(e.what()); + } + }); + return ex; +} + +NAMESPACE_BEGIN(detail) +PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) { + auto strings = tuple(args.size()); + for (size_t i = 0; i < args.size(); ++i) { + strings[i] = str(args[i]); + } + auto sep = kwargs.contains("sep") ? kwargs["sep"] : cast(" "); + auto line = sep.attr("join")(strings); + + object file; + if (kwargs.contains("file")) { + file = kwargs["file"].cast(); + } else { + try { + file = module::import("sys").attr("stdout"); + } catch (const error_already_set &) { + /* If print() is called from code that is executed as + part of garbage collection during interpreter shutdown, + importing 'sys' can fail. Give up rather than crashing the + interpreter in this case. */ + return; + } + } + + auto write = file.attr("write"); + write(line); + write(kwargs.contains("end") ? kwargs["end"] : cast("\n")); + + if (kwargs.contains("flush") && kwargs["flush"].cast()) + file.attr("flush")(); +} +NAMESPACE_END(detail) + +template +void print(Args &&...args) { + auto c = detail::collect_arguments(std::forward(args)...); + detail::print(c.args(), c.kwargs()); +} + +#if defined(WITH_THREAD) && !defined(PYPY_VERSION) + +/* The functions below essentially reproduce the PyGILState_* API using a RAII + * pattern, but there are a few important differences: + * + * 1. When acquiring the GIL from an non-main thread during the finalization + * phase, the GILState API blindly terminates the calling thread, which + * is often not what is wanted. This API does not do this. + * + * 2. The gil_scoped_release function can optionally cut the relationship + * of a PyThreadState and its associated thread, which allows moving it to + * another thread (this is a fairly rare/advanced use case). + * + * 3. The reference count of an acquired thread state can be controlled. This + * can be handy to prevent cases where callbacks issued from an external + * thread would otherwise constantly construct and destroy thread state data + * structures. + * + * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an + * example which uses features 2 and 3 to migrate the Python thread of + * execution to another thread (to run the event loop on the original thread, + * in this case). + */ + +class gil_scoped_acquire { +public: + PYBIND11_NOINLINE gil_scoped_acquire() { + auto const &internals = detail::get_internals(); + tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate); + + if (!tstate) { + /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if + calling from a Python thread). Since we use a different key, this ensures + we don't create a new thread state and deadlock in PyEval_AcquireThread + below. Note we don't save this state with internals.tstate, since we don't + create it we would fail to clear it (its reference count should be > 0). */ + tstate = PyGILState_GetThisThreadState(); + } + + if (!tstate) { + tstate = PyThreadState_New(internals.istate); + #if !defined(NDEBUG) + if (!tstate) + pybind11_fail("scoped_acquire: could not create thread state!"); + #endif + tstate->gilstate_counter = 0; + PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate); + } else { + release = detail::get_thread_state_unchecked() != tstate; + } + + if (release) { + /* Work around an annoying assertion in PyThreadState_Swap */ + #if defined(Py_DEBUG) + PyInterpreterState *interp = tstate->interp; + tstate->interp = nullptr; + #endif + PyEval_AcquireThread(tstate); + #if defined(Py_DEBUG) + tstate->interp = interp; + #endif + } + + inc_ref(); + } + + void inc_ref() { + ++tstate->gilstate_counter; + } + + PYBIND11_NOINLINE void dec_ref() { + --tstate->gilstate_counter; + #if !defined(NDEBUG) + if (detail::get_thread_state_unchecked() != tstate) + pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); + if (tstate->gilstate_counter < 0) + pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); + #endif + if (tstate->gilstate_counter == 0) { + #if !defined(NDEBUG) + if (!release) + pybind11_fail("scoped_acquire::dec_ref(): internal error!"); + #endif + PyThreadState_Clear(tstate); + PyThreadState_DeleteCurrent(); + PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate); + release = false; + } + } + + PYBIND11_NOINLINE ~gil_scoped_acquire() { + dec_ref(); + if (release) + PyEval_SaveThread(); + } +private: + PyThreadState *tstate = nullptr; + bool release = true; +}; + +class gil_scoped_release { +public: + explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { + // `get_internals()` must be called here unconditionally in order to initialize + // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an + // initialization race could occur as multiple threads try `gil_scoped_acquire`. + const auto &internals = detail::get_internals(); + tstate = PyEval_SaveThread(); + if (disassoc) { + auto key = internals.tstate; + PYBIND11_TLS_DELETE_VALUE(key); + } + } + ~gil_scoped_release() { + if (!tstate) + return; + PyEval_RestoreThread(tstate); + if (disassoc) { + auto key = detail::get_internals().tstate; + PYBIND11_TLS_REPLACE_VALUE(key, tstate); + } + } +private: + PyThreadState *tstate; + bool disassoc; +}; +#elif defined(PYPY_VERSION) +class gil_scoped_acquire { + PyGILState_STATE state; +public: + gil_scoped_acquire() { state = PyGILState_Ensure(); } + ~gil_scoped_acquire() { PyGILState_Release(state); } +}; + +class gil_scoped_release { + PyThreadState *state; +public: + gil_scoped_release() { state = PyEval_SaveThread(); } + ~gil_scoped_release() { PyEval_RestoreThread(state); } +}; +#else +class gil_scoped_acquire { }; +class gil_scoped_release { }; +#endif + +error_already_set::~error_already_set() { + if (m_type) { + gil_scoped_acquire gil; + error_scope scope; + m_type.release().dec_ref(); + m_value.release().dec_ref(); + m_trace.release().dec_ref(); + } +} + +inline function get_type_overload(const void *this_ptr, const detail::type_info *this_type, const char *name) { + handle self = detail::get_object_handle(this_ptr, this_type); + if (!self) + return function(); + handle type = self.get_type(); + auto key = std::make_pair(type.ptr(), name); + + /* Cache functions that aren't overloaded in Python to avoid + many costly Python dictionary lookups below */ + auto &cache = detail::get_internals().inactive_overload_cache; + if (cache.find(key) != cache.end()) + return function(); + + function overload = getattr(self, name, function()); + if (overload.is_cpp_function()) { + cache.insert(key); + return function(); + } + + /* Don't call dispatch code if invoked from overridden function. + Unfortunately this doesn't work on PyPy. */ +#if !defined(PYPY_VERSION) + PyFrameObject *frame = PyThreadState_Get()->frame; + if (frame && (std::string) str(frame->f_code->co_name) == name && + frame->f_code->co_argcount > 0) { + PyFrame_FastToLocals(frame); + PyObject *self_caller = PyDict_GetItem( + frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); + if (self_caller == self.ptr()) + return function(); + } +#else + /* PyPy currently doesn't provide a detailed cpyext emulation of + frame objects, so we have to emulate this using Python. This + is going to be slow..*/ + dict d; d["self"] = self; d["name"] = pybind11::str(name); + PyObject *result = PyRun_String( + "import inspect\n" + "frame = inspect.currentframe()\n" + "if frame is not None:\n" + " frame = frame.f_back\n" + " if frame is not None and str(frame.f_code.co_name) == name and " + "frame.f_code.co_argcount > 0:\n" + " self_caller = frame.f_locals[frame.f_code.co_varnames[0]]\n" + " if self_caller == self:\n" + " self = None\n", + Py_file_input, d.ptr(), d.ptr()); + if (result == nullptr) + throw error_already_set(); + if (d["self"].is_none()) + return function(); + Py_DECREF(result); +#endif + + return overload; +} + +/** \rst + Try to retrieve a python method by the provided name from the instance pointed to by the this_ptr. + + :this_ptr: The pointer to the object the overload should be retrieved for. This should be the first + non-trampoline class encountered in the inheritance chain. + :name: The name of the overloaded Python method to retrieve. + :return: The Python method by this name from the object or an empty function wrapper. + \endrst */ +template function get_overload(const T *this_ptr, const char *name) { + auto tinfo = detail::get_type_info(typeid(T)); + return tinfo ? get_type_overload(this_ptr, tinfo, name) : function(); +} + +#define PYBIND11_OVERLOAD_INT(ret_type, cname, name, ...) { \ + pybind11::gil_scoped_acquire gil; \ + pybind11::function overload = pybind11::get_overload(static_cast(this), name); \ + if (overload) { \ + auto o = overload(__VA_ARGS__); \ + if (pybind11::detail::cast_is_temporary_value_reference::value) { \ + static pybind11::detail::overload_caster_t caster; \ + return pybind11::detail::cast_ref(std::move(o), caster); \ + } \ + else return pybind11::detail::cast_safe(std::move(o)); \ + } \ + } + +/** \rst + Macro to populate the virtual method in the trampoline class. This macro tries to look up a method named 'fn' + from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return + the appropriate type. See :ref:`overriding_virtuals` for more information. This macro should be used when the method + name in C is not the same as the method name in Python. For example with `__str__`. + + .. code-block:: cpp + + std::string toString() override { + PYBIND11_OVERLOAD_NAME( + std::string, // Return type (ret_type) + Animal, // Parent class (cname) + toString, // Name of function in C++ (name) + "__str__", // Name of method in Python (fn) + ); + } +\endrst */ +#define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ + PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ + return cname::fn(__VA_ARGS__) + +/** \rst + Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERLOAD_NAME`, except that it + throws if no overload can be found. +\endrst */ +#define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ + PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ + pybind11::pybind11_fail("Tried to call pure virtual function \"" PYBIND11_STRINGIFY(cname) "::" name "\""); + +/** \rst + Macro to populate the virtual method in the trampoline class. This macro tries to look up the method + from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return + the appropriate type. This macro should be used if the method name in C and in Python are identical. + See :ref:`overriding_virtuals` for more information. + + .. code-block:: cpp + + class PyAnimal : public Animal { + public: + // Inherit the constructors + using Animal::Animal; + + // Trampoline (need one for each virtual function) + std::string go(int n_times) override { + PYBIND11_OVERLOAD_PURE( + std::string, // Return type (ret_type) + Animal, // Parent class (cname) + go, // Name of function in C++ (must match Python name) (fn) + n_times // Argument(s) (...) + ); + } + }; +\endrst */ +#define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ + PYBIND11_OVERLOAD_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) + +/** \rst + Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERLOAD`, except that it throws + if no overload can be found. +\endrst */ +#define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ + PYBIND11_OVERLOAD_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) +# pragma warning(pop) +#elif defined(__GNUG__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif diff --git a/thirdparty/pybind11/include/pybind11/pytypes.h b/thirdparty/pybind11/include/pybind11/pytypes.h new file mode 100644 index 000000000..4003d6918 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/pytypes.h @@ -0,0 +1,1484 @@ +/* + pybind11/pytypes.h: Convenience wrapper classes for basic Python types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "buffer_info.h" +#include +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +/* A few forward declarations */ +class handle; class object; +class str; class iterator; +struct arg; struct arg_v; + +NAMESPACE_BEGIN(detail) +class args_proxy; +inline bool isinstance_generic(handle obj, const std::type_info &tp); + +// Accessor forward declarations +template class accessor; +namespace accessor_policies { + struct obj_attr; + struct str_attr; + struct generic_item; + struct sequence_item; + struct list_item; + struct tuple_item; +} +using obj_attr_accessor = accessor; +using str_attr_accessor = accessor; +using item_accessor = accessor; +using sequence_accessor = accessor; +using list_accessor = accessor; +using tuple_accessor = accessor; + +/// Tag and check to identify a class which implements the Python object API +class pyobject_tag { }; +template using is_pyobject = std::is_base_of>; + +/** \rst + A mixin class which adds common functions to `handle`, `object` and various accessors. + The only requirement for `Derived` is to implement ``PyObject *Derived::ptr() const``. +\endrst */ +template +class object_api : public pyobject_tag { + const Derived &derived() const { return static_cast(*this); } + +public: + /** \rst + Return an iterator equivalent to calling ``iter()`` in Python. The object + must be a collection which supports the iteration protocol. + \endrst */ + iterator begin() const; + /// Return a sentinel which ends iteration. + iterator end() const; + + /** \rst + Return an internal functor to invoke the object's sequence protocol. Casting + the returned ``detail::item_accessor`` instance to a `handle` or `object` + subclass causes a corresponding call to ``__getitem__``. Assigning a `handle` + or `object` subclass causes a call to ``__setitem__``. + \endrst */ + item_accessor operator[](handle key) const; + /// See above (the only difference is that they key is provided as a string literal) + item_accessor operator[](const char *key) const; + + /** \rst + Return an internal functor to access the object's attributes. Casting the + returned ``detail::obj_attr_accessor`` instance to a `handle` or `object` + subclass causes a corresponding call to ``getattr``. Assigning a `handle` + or `object` subclass causes a call to ``setattr``. + \endrst */ + obj_attr_accessor attr(handle key) const; + /// See above (the only difference is that they key is provided as a string literal) + str_attr_accessor attr(const char *key) const; + + /** \rst + Matches * unpacking in Python, e.g. to unpack arguments out of a ``tuple`` + or ``list`` for a function call. Applying another * to the result yields + ** unpacking, e.g. to unpack a dict as function keyword arguments. + See :ref:`calling_python_functions`. + \endrst */ + args_proxy operator*() const; + + /// Check if the given item is contained within this object, i.e. ``item in obj``. + template bool contains(T &&item) const; + + /** \rst + Assuming the Python object is a function or implements the ``__call__`` + protocol, ``operator()`` invokes the underlying function, passing an + arbitrary set of parameters. The result is returned as a `object` and + may need to be converted back into a Python object using `handle::cast()`. + + When some of the arguments cannot be converted to Python objects, the + function will throw a `cast_error` exception. When the Python function + call fails, a `error_already_set` exception is thrown. + \endrst */ + template + object operator()(Args &&...args) const; + template + PYBIND11_DEPRECATED("call(...) was deprecated in favor of operator()(...)") + object call(Args&&... args) const; + + /// Equivalent to ``obj is other`` in Python. + bool is(object_api const& other) const { return derived().ptr() == other.derived().ptr(); } + /// Equivalent to ``obj is None`` in Python. + bool is_none() const { return derived().ptr() == Py_None; } + /// Equivalent to obj == other in Python + bool equal(object_api const &other) const { return rich_compare(other, Py_EQ); } + bool not_equal(object_api const &other) const { return rich_compare(other, Py_NE); } + bool operator<(object_api const &other) const { return rich_compare(other, Py_LT); } + bool operator<=(object_api const &other) const { return rich_compare(other, Py_LE); } + bool operator>(object_api const &other) const { return rich_compare(other, Py_GT); } + bool operator>=(object_api const &other) const { return rich_compare(other, Py_GE); } + + object operator-() const; + object operator~() const; + object operator+(object_api const &other) const; + object operator+=(object_api const &other) const; + object operator-(object_api const &other) const; + object operator-=(object_api const &other) const; + object operator*(object_api const &other) const; + object operator*=(object_api const &other) const; + object operator/(object_api const &other) const; + object operator/=(object_api const &other) const; + object operator|(object_api const &other) const; + object operator|=(object_api const &other) const; + object operator&(object_api const &other) const; + object operator&=(object_api const &other) const; + object operator^(object_api const &other) const; + object operator^=(object_api const &other) const; + object operator<<(object_api const &other) const; + object operator<<=(object_api const &other) const; + object operator>>(object_api const &other) const; + object operator>>=(object_api const &other) const; + + PYBIND11_DEPRECATED("Use py::str(obj) instead") + pybind11::str str() const; + + /// Get or set the object's docstring, i.e. ``obj.__doc__``. + str_attr_accessor doc() const; + + /// Return the object's current reference count + int ref_count() const { return static_cast(Py_REFCNT(derived().ptr())); } + /// Return a handle to the Python type object underlying the instance + handle get_type() const; + +private: + bool rich_compare(object_api const &other, int value) const; +}; + +NAMESPACE_END(detail) + +/** \rst + Holds a reference to a Python object (no reference counting) + + The `handle` class is a thin wrapper around an arbitrary Python object (i.e. a + ``PyObject *`` in Python's C API). It does not perform any automatic reference + counting and merely provides a basic C++ interface to various Python API functions. + + .. seealso:: + The `object` class inherits from `handle` and adds automatic reference + counting features. +\endrst */ +class handle : public detail::object_api { +public: + /// The default constructor creates a handle with a ``nullptr``-valued pointer + handle() = default; + /// Creates a ``handle`` from the given raw Python object pointer + handle(PyObject *ptr) : m_ptr(ptr) { } // Allow implicit conversion from PyObject* + + /// Return the underlying ``PyObject *`` pointer + PyObject *ptr() const { return m_ptr; } + PyObject *&ptr() { return m_ptr; } + + /** \rst + Manually increase the reference count of the Python object. Usually, it is + preferable to use the `object` class which derives from `handle` and calls + this function automatically. Returns a reference to itself. + \endrst */ + const handle& inc_ref() const & { Py_XINCREF(m_ptr); return *this; } + + /** \rst + Manually decrease the reference count of the Python object. Usually, it is + preferable to use the `object` class which derives from `handle` and calls + this function automatically. Returns a reference to itself. + \endrst */ + const handle& dec_ref() const & { Py_XDECREF(m_ptr); return *this; } + + /** \rst + Attempt to cast the Python object into the given C++ type. A `cast_error` + will be throw upon failure. + \endrst */ + template T cast() const; + /// Return ``true`` when the `handle` wraps a valid Python object + explicit operator bool() const { return m_ptr != nullptr; } + /** \rst + Deprecated: Check that the underlying pointers are the same. + Equivalent to ``obj1 is obj2`` in Python. + \endrst */ + PYBIND11_DEPRECATED("Use obj1.is(obj2) instead") + bool operator==(const handle &h) const { return m_ptr == h.m_ptr; } + PYBIND11_DEPRECATED("Use !obj1.is(obj2) instead") + bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; } + PYBIND11_DEPRECATED("Use handle::operator bool() instead") + bool check() const { return m_ptr != nullptr; } +protected: + PyObject *m_ptr = nullptr; +}; + +/** \rst + Holds a reference to a Python object (with reference counting) + + Like `handle`, the `object` class is a thin wrapper around an arbitrary Python + object (i.e. a ``PyObject *`` in Python's C API). In contrast to `handle`, it + optionally increases the object's reference count upon construction, and it + *always* decreases the reference count when the `object` instance goes out of + scope and is destructed. When using `object` instances consistently, it is much + easier to get reference counting right at the first attempt. +\endrst */ +class object : public handle { +public: + object() = default; + PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") + object(handle h, bool is_borrowed) : handle(h) { if (is_borrowed) inc_ref(); } + /// Copy constructor; always increases the reference count + object(const object &o) : handle(o) { inc_ref(); } + /// Move constructor; steals the object from ``other`` and preserves its reference count + object(object &&other) noexcept { m_ptr = other.m_ptr; other.m_ptr = nullptr; } + /// Destructor; automatically calls `handle::dec_ref()` + ~object() { dec_ref(); } + + /** \rst + Resets the internal pointer to ``nullptr`` without without decreasing the + object's reference count. The function returns a raw handle to the original + Python object. + \endrst */ + handle release() { + PyObject *tmp = m_ptr; + m_ptr = nullptr; + return handle(tmp); + } + + object& operator=(const object &other) { + other.inc_ref(); + dec_ref(); + m_ptr = other.m_ptr; + return *this; + } + + object& operator=(object &&other) noexcept { + if (this != &other) { + handle temp(m_ptr); + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + temp.dec_ref(); + } + return *this; + } + + // Calling cast() on an object lvalue just copies (via handle::cast) + template T cast() const &; + // Calling on an object rvalue does a move, if needed and/or possible + template T cast() &&; + +protected: + // Tags for choosing constructors from raw PyObject * + struct borrowed_t { }; + struct stolen_t { }; + + template friend T reinterpret_borrow(handle); + template friend T reinterpret_steal(handle); + +public: + // Only accessible from derived classes and the reinterpret_* functions + object(handle h, borrowed_t) : handle(h) { inc_ref(); } + object(handle h, stolen_t) : handle(h) { } +}; + +/** \rst + Declare that a `handle` or ``PyObject *`` is a certain type and borrow the reference. + The target type ``T`` must be `object` or one of its derived classes. The function + doesn't do any conversions or checks. It's up to the user to make sure that the + target type is correct. + + .. code-block:: cpp + + PyObject *p = PyList_GetItem(obj, index); + py::object o = reinterpret_borrow(p); + // or + py::tuple t = reinterpret_borrow(p); // <-- `p` must be already be a `tuple` +\endrst */ +template T reinterpret_borrow(handle h) { return {h, object::borrowed_t{}}; } + +/** \rst + Like `reinterpret_borrow`, but steals the reference. + + .. code-block:: cpp + + PyObject *p = PyObject_Str(obj); + py::str s = reinterpret_steal(p); // <-- `p` must be already be a `str` +\endrst */ +template T reinterpret_steal(handle h) { return {h, object::stolen_t{}}; } + +NAMESPACE_BEGIN(detail) +inline std::string error_string(); +NAMESPACE_END(detail) + +/// Fetch and hold an error which was already set in Python. An instance of this is typically +/// thrown to propagate python-side errors back through C++ which can either be caught manually or +/// else falls back to the function dispatcher (which then raises the captured error back to +/// python). +class error_already_set : public std::runtime_error { +public: + /// Constructs a new exception from the current Python error indicator, if any. The current + /// Python error indicator will be cleared. + error_already_set() : std::runtime_error(detail::error_string()) { + PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr()); + } + + error_already_set(const error_already_set &) = default; + error_already_set(error_already_set &&) = default; + + inline ~error_already_set(); + + /// Give the currently-held error back to Python, if any. If there is currently a Python error + /// already set it is cleared first. After this call, the current object no longer stores the + /// error variables (but the `.what()` string is still available). + void restore() { PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr()); } + + // Does nothing; provided for backwards compatibility. + PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated") + void clear() {} + + /// Check if the currently trapped error type matches the given Python exception class (or a + /// subclass thereof). May also be passed a tuple to search for any exception class matches in + /// the given tuple. + bool matches(handle exc) const { return PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()); } + + const object& type() const { return m_type; } + const object& value() const { return m_value; } + const object& trace() const { return m_trace; } + +private: + object m_type, m_value, m_trace; +}; + +/** \defgroup python_builtins _ + Unless stated otherwise, the following C++ functions behave the same + as their Python counterparts. + */ + +/** \ingroup python_builtins + \rst + Return true if ``obj`` is an instance of ``T``. Type ``T`` must be a subclass of + `object` or a class which was exposed to Python as ``py::class_``. +\endrst */ +template ::value, int> = 0> +bool isinstance(handle obj) { return T::check_(obj); } + +template ::value, int> = 0> +bool isinstance(handle obj) { return detail::isinstance_generic(obj, typeid(T)); } + +template <> inline bool isinstance(handle obj) = delete; +template <> inline bool isinstance(handle obj) { return obj.ptr() != nullptr; } + +/// \ingroup python_builtins +/// Return true if ``obj`` is an instance of the ``type``. +inline bool isinstance(handle obj, handle type) { + const auto result = PyObject_IsInstance(obj.ptr(), type.ptr()); + if (result == -1) + throw error_already_set(); + return result != 0; +} + +/// \addtogroup python_builtins +/// @{ +inline bool hasattr(handle obj, handle name) { + return PyObject_HasAttr(obj.ptr(), name.ptr()) == 1; +} + +inline bool hasattr(handle obj, const char *name) { + return PyObject_HasAttrString(obj.ptr(), name) == 1; +} + +inline void delattr(handle obj, handle name) { + if (PyObject_DelAttr(obj.ptr(), name.ptr()) != 0) { throw error_already_set(); } +} + +inline void delattr(handle obj, const char *name) { + if (PyObject_DelAttrString(obj.ptr(), name) != 0) { throw error_already_set(); } +} + +inline object getattr(handle obj, handle name) { + PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} + +inline object getattr(handle obj, const char *name) { + PyObject *result = PyObject_GetAttrString(obj.ptr(), name); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} + +inline object getattr(handle obj, handle name, handle default_) { + if (PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr())) { + return reinterpret_steal(result); + } else { + PyErr_Clear(); + return reinterpret_borrow(default_); + } +} + +inline object getattr(handle obj, const char *name, handle default_) { + if (PyObject *result = PyObject_GetAttrString(obj.ptr(), name)) { + return reinterpret_steal(result); + } else { + PyErr_Clear(); + return reinterpret_borrow(default_); + } +} + +inline void setattr(handle obj, handle name, handle value) { + if (PyObject_SetAttr(obj.ptr(), name.ptr(), value.ptr()) != 0) { throw error_already_set(); } +} + +inline void setattr(handle obj, const char *name, handle value) { + if (PyObject_SetAttrString(obj.ptr(), name, value.ptr()) != 0) { throw error_already_set(); } +} + +inline ssize_t hash(handle obj) { + auto h = PyObject_Hash(obj.ptr()); + if (h == -1) { throw error_already_set(); } + return h; +} + +/// @} python_builtins + +NAMESPACE_BEGIN(detail) +inline handle get_function(handle value) { + if (value) { +#if PY_MAJOR_VERSION >= 3 + if (PyInstanceMethod_Check(value.ptr())) + value = PyInstanceMethod_GET_FUNCTION(value.ptr()); + else +#endif + if (PyMethod_Check(value.ptr())) + value = PyMethod_GET_FUNCTION(value.ptr()); + } + return value; +} + +// Helper aliases/functions to support implicit casting of values given to python accessors/methods. +// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes +// through pybind11::cast(obj) to convert it to an `object`. +template ::value, int> = 0> +auto object_or_cast(T &&o) -> decltype(std::forward(o)) { return std::forward(o); } +// The following casting version is implemented in cast.h: +template ::value, int> = 0> +object object_or_cast(T &&o); +// Match a PyObject*, which we want to convert directly to handle via its converting constructor +inline handle object_or_cast(PyObject *ptr) { return ptr; } + +template +class accessor : public object_api> { + using key_type = typename Policy::key_type; + +public: + accessor(handle obj, key_type key) : obj(obj), key(std::move(key)) { } + accessor(const accessor &) = default; + accessor(accessor &&) = default; + + // accessor overload required to override default assignment operator (templates are not allowed + // to replace default compiler-generated assignments). + void operator=(const accessor &a) && { std::move(*this).operator=(handle(a)); } + void operator=(const accessor &a) & { operator=(handle(a)); } + + template void operator=(T &&value) && { + Policy::set(obj, key, object_or_cast(std::forward(value))); + } + template void operator=(T &&value) & { + get_cache() = reinterpret_borrow(object_or_cast(std::forward(value))); + } + + template + PYBIND11_DEPRECATED("Use of obj.attr(...) as bool is deprecated in favor of pybind11::hasattr(obj, ...)") + explicit operator enable_if_t::value || + std::is_same::value, bool>() const { + return hasattr(obj, key); + } + template + PYBIND11_DEPRECATED("Use of obj[key] as bool is deprecated in favor of obj.contains(key)") + explicit operator enable_if_t::value, bool>() const { + return obj.contains(key); + } + + operator object() const { return get_cache(); } + PyObject *ptr() const { return get_cache().ptr(); } + template T cast() const { return get_cache().template cast(); } + +private: + object &get_cache() const { + if (!cache) { cache = Policy::get(obj, key); } + return cache; + } + +private: + handle obj; + key_type key; + mutable object cache; +}; + +NAMESPACE_BEGIN(accessor_policies) +struct obj_attr { + using key_type = object; + static object get(handle obj, handle key) { return getattr(obj, key); } + static void set(handle obj, handle key, handle val) { setattr(obj, key, val); } +}; + +struct str_attr { + using key_type = const char *; + static object get(handle obj, const char *key) { return getattr(obj, key); } + static void set(handle obj, const char *key, handle val) { setattr(obj, key, val); } +}; + +struct generic_item { + using key_type = object; + + static object get(handle obj, handle key) { + PyObject *result = PyObject_GetItem(obj.ptr(), key.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); + } + + static void set(handle obj, handle key, handle val) { + if (PyObject_SetItem(obj.ptr(), key.ptr(), val.ptr()) != 0) { throw error_already_set(); } + } +}; + +struct sequence_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PySequence_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); + } + + static void set(handle obj, size_t index, handle val) { + // PySequence_SetItem does not steal a reference to 'val' + if (PySequence_SetItem(obj.ptr(), static_cast(index), val.ptr()) != 0) { + throw error_already_set(); + } + } +}; + +struct list_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PyList_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_borrow(result); + } + + static void set(handle obj, size_t index, handle val) { + // PyList_SetItem steals a reference to 'val' + if (PyList_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { + throw error_already_set(); + } + } +}; + +struct tuple_item { + using key_type = size_t; + + static object get(handle obj, size_t index) { + PyObject *result = PyTuple_GetItem(obj.ptr(), static_cast(index)); + if (!result) { throw error_already_set(); } + return reinterpret_borrow(result); + } + + static void set(handle obj, size_t index, handle val) { + // PyTuple_SetItem steals a reference to 'val' + if (PyTuple_SetItem(obj.ptr(), static_cast(index), val.inc_ref().ptr()) != 0) { + throw error_already_set(); + } + } +}; +NAMESPACE_END(accessor_policies) + +/// STL iterator template used for tuple, list, sequence and dict +template +class generic_iterator : public Policy { + using It = generic_iterator; + +public: + using difference_type = ssize_t; + using iterator_category = typename Policy::iterator_category; + using value_type = typename Policy::value_type; + using reference = typename Policy::reference; + using pointer = typename Policy::pointer; + + generic_iterator() = default; + generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { } + + reference operator*() const { return Policy::dereference(); } + reference operator[](difference_type n) const { return *(*this + n); } + pointer operator->() const { return **this; } + + It &operator++() { Policy::increment(); return *this; } + It operator++(int) { auto copy = *this; Policy::increment(); return copy; } + It &operator--() { Policy::decrement(); return *this; } + It operator--(int) { auto copy = *this; Policy::decrement(); return copy; } + It &operator+=(difference_type n) { Policy::advance(n); return *this; } + It &operator-=(difference_type n) { Policy::advance(-n); return *this; } + + friend It operator+(const It &a, difference_type n) { auto copy = a; return copy += n; } + friend It operator+(difference_type n, const It &b) { return b + n; } + friend It operator-(const It &a, difference_type n) { auto copy = a; return copy -= n; } + friend difference_type operator-(const It &a, const It &b) { return a.distance_to(b); } + + friend bool operator==(const It &a, const It &b) { return a.equal(b); } + friend bool operator!=(const It &a, const It &b) { return !(a == b); } + friend bool operator< (const It &a, const It &b) { return b - a > 0; } + friend bool operator> (const It &a, const It &b) { return b < a; } + friend bool operator>=(const It &a, const It &b) { return !(a < b); } + friend bool operator<=(const It &a, const It &b) { return !(a > b); } +}; + +NAMESPACE_BEGIN(iterator_policies) +/// Quick proxy class needed to implement ``operator->`` for iterators which can't return pointers +template +struct arrow_proxy { + T value; + + arrow_proxy(T &&value) : value(std::move(value)) { } + T *operator->() const { return &value; } +}; + +/// Lightweight iterator policy using just a simple pointer: see ``PySequence_Fast_ITEMS`` +class sequence_fast_readonly { +protected: + using iterator_category = std::random_access_iterator_tag; + using value_type = handle; + using reference = const handle; + using pointer = arrow_proxy; + + sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { } + + reference dereference() const { return *ptr; } + void increment() { ++ptr; } + void decrement() { --ptr; } + void advance(ssize_t n) { ptr += n; } + bool equal(const sequence_fast_readonly &b) const { return ptr == b.ptr; } + ssize_t distance_to(const sequence_fast_readonly &b) const { return ptr - b.ptr; } + +private: + PyObject **ptr; +}; + +/// Full read and write access using the sequence protocol: see ``detail::sequence_accessor`` +class sequence_slow_readwrite { +protected: + using iterator_category = std::random_access_iterator_tag; + using value_type = object; + using reference = sequence_accessor; + using pointer = arrow_proxy; + + sequence_slow_readwrite(handle obj, ssize_t index) : obj(obj), index(index) { } + + reference dereference() const { return {obj, static_cast(index)}; } + void increment() { ++index; } + void decrement() { --index; } + void advance(ssize_t n) { index += n; } + bool equal(const sequence_slow_readwrite &b) const { return index == b.index; } + ssize_t distance_to(const sequence_slow_readwrite &b) const { return index - b.index; } + +private: + handle obj; + ssize_t index; +}; + +/// Python's dictionary protocol permits this to be a forward iterator +class dict_readonly { +protected: + using iterator_category = std::forward_iterator_tag; + using value_type = std::pair; + using reference = const value_type; + using pointer = arrow_proxy; + + dict_readonly() = default; + dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); } + + reference dereference() const { return {key, value}; } + void increment() { if (!PyDict_Next(obj.ptr(), &pos, &key, &value)) { pos = -1; } } + bool equal(const dict_readonly &b) const { return pos == b.pos; } + +private: + handle obj; + PyObject *key = nullptr, *value = nullptr; + ssize_t pos = -1; +}; +NAMESPACE_END(iterator_policies) + +#if !defined(PYPY_VERSION) +using tuple_iterator = generic_iterator; +using list_iterator = generic_iterator; +#else +using tuple_iterator = generic_iterator; +using list_iterator = generic_iterator; +#endif + +using sequence_iterator = generic_iterator; +using dict_iterator = generic_iterator; + +inline bool PyIterable_Check(PyObject *obj) { + PyObject *iter = PyObject_GetIter(obj); + if (iter) { + Py_DECREF(iter); + return true; + } else { + PyErr_Clear(); + return false; + } +} + +inline bool PyNone_Check(PyObject *o) { return o == Py_None; } +#if PY_MAJOR_VERSION >= 3 +inline bool PyEllipsis_Check(PyObject *o) { return o == Py_Ellipsis; } +#endif + +inline bool PyUnicode_Check_Permissive(PyObject *o) { return PyUnicode_Check(o) || PYBIND11_BYTES_CHECK(o); } + +inline bool PyStaticMethod_Check(PyObject *o) { return o->ob_type == &PyStaticMethod_Type; } + +class kwargs_proxy : public handle { +public: + explicit kwargs_proxy(handle h) : handle(h) { } +}; + +class args_proxy : public handle { +public: + explicit args_proxy(handle h) : handle(h) { } + kwargs_proxy operator*() const { return kwargs_proxy(*this); } +}; + +/// Python argument categories (using PEP 448 terms) +template using is_keyword = std::is_base_of; +template using is_s_unpacking = std::is_same; // * unpacking +template using is_ds_unpacking = std::is_same; // ** unpacking +template using is_positional = satisfies_none_of; +template using is_keyword_or_ds = satisfies_any_of; + +// Call argument collector forward declarations +template +class simple_collector; +template +class unpacking_collector; + +NAMESPACE_END(detail) + +// TODO: After the deprecated constructors are removed, this macro can be simplified by +// inheriting ctors: `using Parent::Parent`. It's not an option right now because +// the `using` statement triggers the parent deprecation warning even if the ctor +// isn't even used. +#define PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + public: \ + PYBIND11_DEPRECATED("Use reinterpret_borrow<"#Name">() or reinterpret_steal<"#Name">()") \ + Name(handle h, bool is_borrowed) : Parent(is_borrowed ? Parent(h, borrowed_t{}) : Parent(h, stolen_t{})) { } \ + Name(handle h, borrowed_t) : Parent(h, borrowed_t{}) { } \ + Name(handle h, stolen_t) : Parent(h, stolen_t{}) { } \ + PYBIND11_DEPRECATED("Use py::isinstance(obj) instead") \ + bool check() const { return m_ptr != nullptr && (bool) CheckFun(m_ptr); } \ + static bool check_(handle h) { return h.ptr() != nullptr && CheckFun(h.ptr()); } + +#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, ConvertFun) \ + PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ + Name(const object &o) \ + : Parent(check_(o) ? o.inc_ref().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ + { if (!m_ptr) throw error_already_set(); } \ + Name(object &&o) \ + : Parent(check_(o) ? o.release().ptr() : ConvertFun(o.ptr()), stolen_t{}) \ + { if (!m_ptr) throw error_already_set(); } \ + template \ + Name(const ::pybind11::detail::accessor &a) : Name(object(a)) { } + +#define PYBIND11_OBJECT(Name, Parent, CheckFun) \ + PYBIND11_OBJECT_COMMON(Name, Parent, CheckFun) \ + /* This is deliberately not 'explicit' to allow implicit conversion from object: */ \ + Name(const object &o) : Parent(o) { } \ + Name(object &&o) : Parent(std::move(o)) { } + +#define PYBIND11_OBJECT_DEFAULT(Name, Parent, CheckFun) \ + PYBIND11_OBJECT(Name, Parent, CheckFun) \ + Name() : Parent() { } + +/// \addtogroup pytypes +/// @{ + +/** \rst + Wraps a Python iterator so that it can also be used as a C++ input iterator + + Caveat: copying an iterator does not (and cannot) clone the internal + state of the Python iterable. This also applies to the post-increment + operator. This iterator should only be used to retrieve the current + value using ``operator*()``. +\endrst */ +class iterator : public object { +public: + using iterator_category = std::input_iterator_tag; + using difference_type = ssize_t; + using value_type = handle; + using reference = const handle; + using pointer = const handle *; + + PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check) + + iterator& operator++() { + advance(); + return *this; + } + + iterator operator++(int) { + auto rv = *this; + advance(); + return rv; + } + + reference operator*() const { + if (m_ptr && !value.ptr()) { + auto& self = const_cast(*this); + self.advance(); + } + return value; + } + + pointer operator->() const { operator*(); return &value; } + + /** \rst + The value which marks the end of the iteration. ``it == iterator::sentinel()`` + is equivalent to catching ``StopIteration`` in Python. + + .. code-block:: cpp + + void foo(py::iterator it) { + while (it != py::iterator::sentinel()) { + // use `*it` + ++it; + } + } + \endrst */ + static iterator sentinel() { return {}; } + + friend bool operator==(const iterator &a, const iterator &b) { return a->ptr() == b->ptr(); } + friend bool operator!=(const iterator &a, const iterator &b) { return a->ptr() != b->ptr(); } + +private: + void advance() { + value = reinterpret_steal(PyIter_Next(m_ptr)); + if (PyErr_Occurred()) { throw error_already_set(); } + } + +private: + object value = {}; +}; + +class iterable : public object { +public: + PYBIND11_OBJECT_DEFAULT(iterable, object, detail::PyIterable_Check) +}; + +class bytes; + +class str : public object { +public: + PYBIND11_OBJECT_CVT(str, object, detail::PyUnicode_Check_Permissive, raw_str) + + str(const char *c, size_t n) + : object(PyUnicode_FromStringAndSize(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } + + // 'explicit' is explicitly omitted from the following constructors to allow implicit conversion to py::str from C++ string-like objects + str(const char *c = "") + : object(PyUnicode_FromString(c), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } + + str(const std::string &s) : str(s.data(), s.size()) { } + + explicit str(const bytes &b); + + /** \rst + Return a string representation of the object. This is analogous to + the ``str()`` function in Python. + \endrst */ + explicit str(handle h) : object(raw_str(h.ptr()), stolen_t{}) { } + + operator std::string() const { + object temp = *this; + if (PyUnicode_Check(m_ptr)) { + temp = reinterpret_steal(PyUnicode_AsUTF8String(m_ptr)); + if (!temp) + pybind11_fail("Unable to extract string contents! (encoding issue)"); + } + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract string contents! (invalid type)"); + return std::string(buffer, (size_t) length); + } + + template + str format(Args &&...args) const { + return attr("format")(std::forward(args)...); + } + +private: + /// Return string representation -- always returns a new reference, even if already a str + static PyObject *raw_str(PyObject *op) { + PyObject *str_value = PyObject_Str(op); +#if PY_MAJOR_VERSION < 3 + if (!str_value) throw error_already_set(); + PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); + Py_XDECREF(str_value); str_value = unicode; +#endif + return str_value; + } +}; +/// @} pytypes + +inline namespace literals { +/** \rst + String literal version of `str` + \endrst */ +inline str operator"" _s(const char *s, size_t size) { return {s, size}; } +} + +/// \addtogroup pytypes +/// @{ +class bytes : public object { +public: + PYBIND11_OBJECT(bytes, object, PYBIND11_BYTES_CHECK) + + // Allow implicit conversion: + bytes(const char *c = "") + : object(PYBIND11_BYTES_FROM_STRING(c), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } + + bytes(const char *c, size_t n) + : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(c, (ssize_t) n), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } + + // Allow implicit conversion: + bytes(const std::string &s) : bytes(s.data(), s.size()) { } + + explicit bytes(const pybind11::str &s); + + operator std::string() const { + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length)) + pybind11_fail("Unable to extract bytes contents!"); + return std::string(buffer, (size_t) length); + } +}; + +inline bytes::bytes(const pybind11::str &s) { + object temp = s; + if (PyUnicode_Check(s.ptr())) { + temp = reinterpret_steal(PyUnicode_AsUTF8String(s.ptr())); + if (!temp) + pybind11_fail("Unable to extract string contents! (encoding issue)"); + } + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract string contents! (invalid type)"); + auto obj = reinterpret_steal(PYBIND11_BYTES_FROM_STRING_AND_SIZE(buffer, length)); + if (!obj) + pybind11_fail("Could not allocate bytes object!"); + m_ptr = obj.release().ptr(); +} + +inline str::str(const bytes& b) { + char *buffer; + ssize_t length; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(b.ptr(), &buffer, &length)) + pybind11_fail("Unable to extract bytes contents!"); + auto obj = reinterpret_steal(PyUnicode_FromStringAndSize(buffer, (ssize_t) length)); + if (!obj) + pybind11_fail("Could not allocate string object!"); + m_ptr = obj.release().ptr(); +} + +class none : public object { +public: + PYBIND11_OBJECT(none, object, detail::PyNone_Check) + none() : object(Py_None, borrowed_t{}) { } +}; + +#if PY_MAJOR_VERSION >= 3 +class ellipsis : public object { +public: + PYBIND11_OBJECT(ellipsis, object, detail::PyEllipsis_Check) + ellipsis() : object(Py_Ellipsis, borrowed_t{}) { } +}; +#endif + +class bool_ : public object { +public: + PYBIND11_OBJECT_CVT(bool_, object, PyBool_Check, raw_bool) + bool_() : object(Py_False, borrowed_t{}) { } + // Allow implicit conversion from and to `bool`: + bool_(bool value) : object(value ? Py_True : Py_False, borrowed_t{}) { } + operator bool() const { return m_ptr && PyLong_AsLong(m_ptr) != 0; } + +private: + /// Return the truth value of an object -- always returns a new reference + static PyObject *raw_bool(PyObject *op) { + const auto value = PyObject_IsTrue(op); + if (value == -1) return nullptr; + return handle(value ? Py_True : Py_False).inc_ref().ptr(); + } +}; + +NAMESPACE_BEGIN(detail) +// Converts a value to the given unsigned type. If an error occurs, you get back (Unsigned) -1; +// otherwise you get back the unsigned long or unsigned long long value cast to (Unsigned). +// (The distinction is critically important when casting a returned -1 error value to some other +// unsigned type: (A)-1 != (B)-1 when A and B are unsigned types of different sizes). +template +Unsigned as_unsigned(PyObject *o) { + if (sizeof(Unsigned) <= sizeof(unsigned long) +#if PY_VERSION_HEX < 0x03000000 + || PyInt_Check(o) +#endif + ) { + unsigned long v = PyLong_AsUnsignedLong(o); + return v == (unsigned long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; + } + else { + unsigned long long v = PyLong_AsUnsignedLongLong(o); + return v == (unsigned long long) -1 && PyErr_Occurred() ? (Unsigned) -1 : (Unsigned) v; + } +} +NAMESPACE_END(detail) + +class int_ : public object { +public: + PYBIND11_OBJECT_CVT(int_, object, PYBIND11_LONG_CHECK, PyNumber_Long) + int_() : object(PyLong_FromLong(0), stolen_t{}) { } + // Allow implicit conversion from C++ integral types: + template ::value, int> = 0> + int_(T value) { + if (sizeof(T) <= sizeof(long)) { + if (std::is_signed::value) + m_ptr = PyLong_FromLong((long) value); + else + m_ptr = PyLong_FromUnsignedLong((unsigned long) value); + } else { + if (std::is_signed::value) + m_ptr = PyLong_FromLongLong((long long) value); + else + m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value); + } + if (!m_ptr) pybind11_fail("Could not allocate int object!"); + } + + template ::value, int> = 0> + operator T() const { + return std::is_unsigned::value + ? detail::as_unsigned(m_ptr) + : sizeof(T) <= sizeof(long) + ? (T) PyLong_AsLong(m_ptr) + : (T) PYBIND11_LONG_AS_LONGLONG(m_ptr); + } +}; + +class float_ : public object { +public: + PYBIND11_OBJECT_CVT(float_, object, PyFloat_Check, PyNumber_Float) + // Allow implicit conversion from float/double: + float_(float value) : object(PyFloat_FromDouble((double) value), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate float object!"); + } + float_(double value = .0) : object(PyFloat_FromDouble((double) value), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate float object!"); + } + operator float() const { return (float) PyFloat_AsDouble(m_ptr); } + operator double() const { return (double) PyFloat_AsDouble(m_ptr); } +}; + +class weakref : public object { +public: + PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check) + explicit weakref(handle obj, handle callback = {}) + : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate weak reference!"); + } +}; + +class slice : public object { +public: + PYBIND11_OBJECT_DEFAULT(slice, object, PySlice_Check) + slice(ssize_t start_, ssize_t stop_, ssize_t step_) { + int_ start(start_), stop(stop_), step(step_); + m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); + if (!m_ptr) pybind11_fail("Could not allocate slice object!"); + } + bool compute(size_t length, size_t *start, size_t *stop, size_t *step, + size_t *slicelength) const { + return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, + (ssize_t) length, (ssize_t *) start, + (ssize_t *) stop, (ssize_t *) step, + (ssize_t *) slicelength) == 0; + } + bool compute(ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step, + ssize_t *slicelength) const { + return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, + length, start, + stop, step, + slicelength) == 0; + } +}; + +class capsule : public object { +public: + PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact) + PYBIND11_DEPRECATED("Use reinterpret_borrow() or reinterpret_steal()") + capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed_t{}) : object(ptr, stolen_t{})) { } + + explicit capsule(const void *value, const char *name = nullptr, void (*destructor)(PyObject *) = nullptr) + : object(PyCapsule_New(const_cast(value), name, destructor), stolen_t{}) { + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + PYBIND11_DEPRECATED("Please pass a destructor that takes a void pointer as input") + capsule(const void *value, void (*destruct)(PyObject *)) + : object(PyCapsule_New(const_cast(value), nullptr, destruct), stolen_t{}) { + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + capsule(const void *value, void (*destructor)(void *)) { + m_ptr = PyCapsule_New(const_cast(value), nullptr, [](PyObject *o) { + auto destructor = reinterpret_cast(PyCapsule_GetContext(o)); + void *ptr = PyCapsule_GetPointer(o, nullptr); + destructor(ptr); + }); + + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + + if (PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) + pybind11_fail("Could not set capsule context!"); + } + + capsule(void (*destructor)()) { + m_ptr = PyCapsule_New(reinterpret_cast(destructor), nullptr, [](PyObject *o) { + auto destructor = reinterpret_cast(PyCapsule_GetPointer(o, nullptr)); + destructor(); + }); + + if (!m_ptr) + pybind11_fail("Could not allocate capsule object!"); + } + + template operator T *() const { + auto name = this->name(); + T * result = static_cast(PyCapsule_GetPointer(m_ptr, name)); + if (!result) pybind11_fail("Unable to extract capsule contents!"); + return result; + } + + const char *name() const { return PyCapsule_GetName(m_ptr); } +}; + +class tuple : public object { +public: + PYBIND11_OBJECT_CVT(tuple, object, PyTuple_Check, PySequence_Tuple) + explicit tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); + } + size_t size() const { return (size_t) PyTuple_Size(m_ptr); } + bool empty() const { return size() == 0; } + detail::tuple_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } + detail::tuple_iterator begin() const { return {*this, 0}; } + detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; } +}; + +class dict : public object { +public: + PYBIND11_OBJECT_CVT(dict, object, PyDict_Check, raw_dict) + dict() : object(PyDict_New(), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate dict object!"); + } + template ...>::value>, + // MSVC workaround: it can't compile an out-of-line definition, so defer the collector + typename collector = detail::deferred_t, Args...>> + explicit dict(Args &&...args) : dict(collector(std::forward(args)...).kwargs()) { } + + size_t size() const { return (size_t) PyDict_Size(m_ptr); } + bool empty() const { return size() == 0; } + detail::dict_iterator begin() const { return {*this, 0}; } + detail::dict_iterator end() const { return {}; } + void clear() const { PyDict_Clear(ptr()); } + template bool contains(T &&key) const { + return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward(key)).ptr()) == 1; + } + +private: + /// Call the `dict` Python type -- always returns a new reference + static PyObject *raw_dict(PyObject *op) { + if (PyDict_Check(op)) + return handle(op).inc_ref().ptr(); + return PyObject_CallFunctionObjArgs((PyObject *) &PyDict_Type, op, nullptr); + } +}; + +class sequence : public object { +public: + PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) + size_t size() const { return (size_t) PySequence_Size(m_ptr); } + bool empty() const { return size() == 0; } + detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } + detail::sequence_iterator begin() const { return {*this, 0}; } + detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; } +}; + +class list : public object { +public: + PYBIND11_OBJECT_CVT(list, object, PyList_Check, PySequence_List) + explicit list(size_t size = 0) : object(PyList_New((ssize_t) size), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate list object!"); + } + size_t size() const { return (size_t) PyList_Size(m_ptr); } + bool empty() const { return size() == 0; } + detail::list_accessor operator[](size_t index) const { return {*this, index}; } + detail::item_accessor operator[](handle h) const { return object::operator[](h); } + detail::list_iterator begin() const { return {*this, 0}; } + detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } + template void append(T &&val) const { + PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()); + } + template void insert(size_t index, T &&val) const { + PyList_Insert(m_ptr, static_cast(index), + detail::object_or_cast(std::forward(val)).ptr()); + } +}; + +class args : public tuple { PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check) }; +class kwargs : public dict { PYBIND11_OBJECT_DEFAULT(kwargs, dict, PyDict_Check) }; + +class set : public object { +public: + PYBIND11_OBJECT_CVT(set, object, PySet_Check, PySet_New) + set() : object(PySet_New(nullptr), stolen_t{}) { + if (!m_ptr) pybind11_fail("Could not allocate set object!"); + } + size_t size() const { return (size_t) PySet_Size(m_ptr); } + bool empty() const { return size() == 0; } + template bool add(T &&val) const { + return PySet_Add(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 0; + } + void clear() const { PySet_Clear(m_ptr); } + template bool contains(T &&val) const { + return PySet_Contains(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) == 1; + } +}; + +class function : public object { +public: + PYBIND11_OBJECT_DEFAULT(function, object, PyCallable_Check) + handle cpp_function() const { + handle fun = detail::get_function(m_ptr); + if (fun && PyCFunction_Check(fun.ptr())) + return fun; + return handle(); + } + bool is_cpp_function() const { return (bool) cpp_function(); } +}; + +class staticmethod : public object { +public: + PYBIND11_OBJECT_CVT(staticmethod, object, detail::PyStaticMethod_Check, PyStaticMethod_New) +}; + +class buffer : public object { +public: + PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer) + + buffer_info request(bool writable = false) const { + int flags = PyBUF_STRIDES | PyBUF_FORMAT; + if (writable) flags |= PyBUF_WRITABLE; + Py_buffer *view = new Py_buffer(); + if (PyObject_GetBuffer(m_ptr, view, flags) != 0) { + delete view; + throw error_already_set(); + } + return buffer_info(view); + } +}; + +class memoryview : public object { +public: + explicit memoryview(const buffer_info& info) { + static Py_buffer buf { }; + // Py_buffer uses signed sizes, strides and shape!.. + static std::vector py_strides { }; + static std::vector py_shape { }; + buf.buf = info.ptr; + buf.itemsize = info.itemsize; + buf.format = const_cast(info.format.c_str()); + buf.ndim = (int) info.ndim; + buf.len = info.size; + py_strides.clear(); + py_shape.clear(); + for (size_t i = 0; i < (size_t) info.ndim; ++i) { + py_strides.push_back(info.strides[i]); + py_shape.push_back(info.shape[i]); + } + buf.strides = py_strides.data(); + buf.shape = py_shape.data(); + buf.suboffsets = nullptr; + buf.readonly = info.readonly; + buf.internal = nullptr; + + m_ptr = PyMemoryView_FromBuffer(&buf); + if (!m_ptr) + pybind11_fail("Unable to create memoryview from buffer descriptor"); + } + + PYBIND11_OBJECT_CVT(memoryview, object, PyMemoryView_Check, PyMemoryView_FromObject) +}; +/// @} pytypes + +/// \addtogroup python_builtins +/// @{ +inline size_t len(handle h) { + ssize_t result = PyObject_Length(h.ptr()); + if (result < 0) + pybind11_fail("Unable to compute length of object"); + return (size_t) result; +} + +inline size_t len_hint(handle h) { +#if PY_VERSION_HEX >= 0x03040000 + ssize_t result = PyObject_LengthHint(h.ptr(), 0); +#else + ssize_t result = PyObject_Length(h.ptr()); +#endif + if (result < 0) { + // Sometimes a length can't be determined at all (eg generators) + // In which case simply return 0 + PyErr_Clear(); + return 0; + } + return (size_t) result; +} + +inline str repr(handle h) { + PyObject *str_value = PyObject_Repr(h.ptr()); + if (!str_value) throw error_already_set(); +#if PY_MAJOR_VERSION < 3 + PyObject *unicode = PyUnicode_FromEncodedObject(str_value, "utf-8", nullptr); + Py_XDECREF(str_value); str_value = unicode; + if (!str_value) throw error_already_set(); +#endif + return reinterpret_steal(str_value); +} + +inline iterator iter(handle obj) { + PyObject *result = PyObject_GetIter(obj.ptr()); + if (!result) { throw error_already_set(); } + return reinterpret_steal(result); +} +/// @} python_builtins + +NAMESPACE_BEGIN(detail) +template iterator object_api::begin() const { return iter(derived()); } +template iterator object_api::end() const { return iterator::sentinel(); } +template item_accessor object_api::operator[](handle key) const { + return {derived(), reinterpret_borrow(key)}; +} +template item_accessor object_api::operator[](const char *key) const { + return {derived(), pybind11::str(key)}; +} +template obj_attr_accessor object_api::attr(handle key) const { + return {derived(), reinterpret_borrow(key)}; +} +template str_attr_accessor object_api::attr(const char *key) const { + return {derived(), key}; +} +template args_proxy object_api::operator*() const { + return args_proxy(derived().ptr()); +} +template template bool object_api::contains(T &&item) const { + return attr("__contains__")(std::forward(item)).template cast(); +} + +template +pybind11::str object_api::str() const { return pybind11::str(derived()); } + +template +str_attr_accessor object_api::doc() const { return attr("__doc__"); } + +template +handle object_api::get_type() const { return (PyObject *) Py_TYPE(derived().ptr()); } + +template +bool object_api::rich_compare(object_api const &other, int value) const { + int rv = PyObject_RichCompareBool(derived().ptr(), other.derived().ptr(), value); + if (rv == -1) + throw error_already_set(); + return rv == 1; +} + +#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \ + template object object_api::op() const { \ + object result = reinterpret_steal(fn(derived().ptr())); \ + if (!result.ptr()) \ + throw error_already_set(); \ + return result; \ + } + +#define PYBIND11_MATH_OPERATOR_BINARY(op, fn) \ + template \ + object object_api::op(object_api const &other) const { \ + object result = reinterpret_steal( \ + fn(derived().ptr(), other.derived().ptr())); \ + if (!result.ptr()) \ + throw error_already_set(); \ + return result; \ + } + +PYBIND11_MATH_OPERATOR_UNARY (operator~, PyNumber_Invert) +PYBIND11_MATH_OPERATOR_UNARY (operator-, PyNumber_Negative) +PYBIND11_MATH_OPERATOR_BINARY(operator+, PyNumber_Add) +PYBIND11_MATH_OPERATOR_BINARY(operator+=, PyNumber_InPlaceAdd) +PYBIND11_MATH_OPERATOR_BINARY(operator-, PyNumber_Subtract) +PYBIND11_MATH_OPERATOR_BINARY(operator-=, PyNumber_InPlaceSubtract) +PYBIND11_MATH_OPERATOR_BINARY(operator*, PyNumber_Multiply) +PYBIND11_MATH_OPERATOR_BINARY(operator*=, PyNumber_InPlaceMultiply) +PYBIND11_MATH_OPERATOR_BINARY(operator/, PyNumber_TrueDivide) +PYBIND11_MATH_OPERATOR_BINARY(operator/=, PyNumber_InPlaceTrueDivide) +PYBIND11_MATH_OPERATOR_BINARY(operator|, PyNumber_Or) +PYBIND11_MATH_OPERATOR_BINARY(operator|=, PyNumber_InPlaceOr) +PYBIND11_MATH_OPERATOR_BINARY(operator&, PyNumber_And) +PYBIND11_MATH_OPERATOR_BINARY(operator&=, PyNumber_InPlaceAnd) +PYBIND11_MATH_OPERATOR_BINARY(operator^, PyNumber_Xor) +PYBIND11_MATH_OPERATOR_BINARY(operator^=, PyNumber_InPlaceXor) +PYBIND11_MATH_OPERATOR_BINARY(operator<<, PyNumber_Lshift) +PYBIND11_MATH_OPERATOR_BINARY(operator<<=, PyNumber_InPlaceLshift) +PYBIND11_MATH_OPERATOR_BINARY(operator>>, PyNumber_Rshift) +PYBIND11_MATH_OPERATOR_BINARY(operator>>=, PyNumber_InPlaceRshift) + +#undef PYBIND11_MATH_OPERATOR_UNARY +#undef PYBIND11_MATH_OPERATOR_BINARY + +NAMESPACE_END(detail) +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/include/pybind11/stl.h b/thirdparty/pybind11/include/pybind11/stl.h new file mode 100644 index 000000000..32f8d294a --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/stl.h @@ -0,0 +1,386 @@ +/* + pybind11/stl.h: Transparent conversion for STL data types + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "pybind11.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +#endif + +#ifdef __has_include +// std::optional (but including it in c++14 mode isn't allowed) +# if defined(PYBIND11_CPP17) && __has_include() +# include +# define PYBIND11_HAS_OPTIONAL 1 +# endif +// std::experimental::optional (but not allowed in c++11 mode) +# if defined(PYBIND11_CPP14) && (__has_include() && \ + !__has_include()) +# include +# define PYBIND11_HAS_EXP_OPTIONAL 1 +# endif +// std::variant +# if defined(PYBIND11_CPP17) && __has_include() +# include +# define PYBIND11_HAS_VARIANT 1 +# endif +#elif defined(_MSC_VER) && defined(PYBIND11_CPP17) +# include +# include +# define PYBIND11_HAS_OPTIONAL 1 +# define PYBIND11_HAS_VARIANT 1 +#endif + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Extracts an const lvalue reference or rvalue reference for U based on the type of T (e.g. for +/// forwarding a container element). Typically used indirect via forwarded_type(), below. +template +using forwarded_type = conditional_t< + std::is_lvalue_reference::value, remove_reference_t &, remove_reference_t &&>; + +/// Forwards a value U as rvalue or lvalue according to whether T is rvalue or lvalue; typically +/// used for forwarding a container's elements. +template +forwarded_type forward_like(U &&u) { + return std::forward>(std::forward(u)); +} + +template struct set_caster { + using type = Type; + using key_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.clear(); + for (auto entry : s) { + key_conv conv; + if (!conv.load(entry, convert)) + return false; + value.insert(cast_op(std::move(conv))); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + if (!std::is_lvalue_reference::value) + policy = return_value_policy_override::policy(policy); + pybind11::set s; + for (auto &&value : src) { + auto value_ = reinterpret_steal(key_conv::cast(forward_like(value), policy, parent)); + if (!value_ || !s.add(value_)) + return handle(); + } + return s.release(); + } + + PYBIND11_TYPE_CASTER(type, _("Set[") + key_conv::name + _("]")); +}; + +template struct map_caster { + using key_conv = make_caster; + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto d = reinterpret_borrow(src); + value.clear(); + for (auto it : d) { + key_conv kconv; + value_conv vconv; + if (!kconv.load(it.first.ptr(), convert) || + !vconv.load(it.second.ptr(), convert)) + return false; + value.emplace(cast_op(std::move(kconv)), cast_op(std::move(vconv))); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + dict d; + return_value_policy policy_key = policy; + return_value_policy policy_value = policy; + if (!std::is_lvalue_reference::value) { + policy_key = return_value_policy_override::policy(policy_key); + policy_value = return_value_policy_override::policy(policy_value); + } + for (auto &&kv : src) { + auto key = reinterpret_steal(key_conv::cast(forward_like(kv.first), policy_key, parent)); + auto value = reinterpret_steal(value_conv::cast(forward_like(kv.second), policy_value, parent)); + if (!key || !value) + return handle(); + d[key] = value; + } + return d.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("Dict[") + key_conv::name + _(", ") + value_conv::name + _("]")); +}; + +template struct list_caster { + using value_conv = make_caster; + + bool load(handle src, bool convert) { + if (!isinstance(src) || isinstance(src)) + return false; + auto s = reinterpret_borrow(src); + value.clear(); + reserve_maybe(s, &value); + for (auto it : s) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value.push_back(cast_op(std::move(conv))); + } + return true; + } + +private: + template ().reserve(0)), void>::value, int> = 0> + void reserve_maybe(sequence s, Type *) { value.reserve(s.size()); } + void reserve_maybe(sequence, void *) { } + +public: + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + if (!std::is_lvalue_reference::value) + policy = return_value_policy_override::policy(policy); + list l(src.size()); + size_t index = 0; + for (auto &&value : src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(Type, _("List[") + value_conv::name + _("]")); +}; + +template struct type_caster> + : list_caster, Type> { }; + +template struct type_caster> + : list_caster, Type> { }; + +template struct type_caster> + : list_caster, Type> { }; + +template struct array_caster { + using value_conv = make_caster; + +private: + template + bool require_size(enable_if_t size) { + if (value.size() != size) + value.resize(size); + return true; + } + template + bool require_size(enable_if_t size) { + return size == Size; + } + +public: + bool load(handle src, bool convert) { + if (!isinstance(src)) + return false; + auto l = reinterpret_borrow(src); + if (!require_size(l.size())) + return false; + size_t ctr = 0; + for (auto it : l) { + value_conv conv; + if (!conv.load(it, convert)) + return false; + value[ctr++] = cast_op(std::move(conv)); + } + return true; + } + + template + static handle cast(T &&src, return_value_policy policy, handle parent) { + list l(src.size()); + size_t index = 0; + for (auto &&value : src) { + auto value_ = reinterpret_steal(value_conv::cast(forward_like(value), policy, parent)); + if (!value_) + return handle(); + PyList_SET_ITEM(l.ptr(), (ssize_t) index++, value_.release().ptr()); // steals a reference + } + return l.release(); + } + + PYBIND11_TYPE_CASTER(ArrayType, _("List[") + value_conv::name + _(_(""), _("[") + _() + _("]")) + _("]")); +}; + +template struct type_caster> + : array_caster, Type, false, Size> { }; + +template struct type_caster> + : array_caster, Type, true> { }; + +template struct type_caster> + : set_caster, Key> { }; + +template struct type_caster> + : set_caster, Key> { }; + +template struct type_caster> + : map_caster, Key, Value> { }; + +template struct type_caster> + : map_caster, Key, Value> { }; + +// This type caster is intended to be used for std::optional and std::experimental::optional +template struct optional_caster { + using value_conv = make_caster; + + template + static handle cast(T_ &&src, return_value_policy policy, handle parent) { + if (!src) + return none().inc_ref(); + policy = return_value_policy_override::policy(policy); + return value_conv::cast(*std::forward(src), policy, parent); + } + + bool load(handle src, bool convert) { + if (!src) { + return false; + } else if (src.is_none()) { + return true; // default-constructed value is already empty + } + value_conv inner_caster; + if (!inner_caster.load(src, convert)) + return false; + + value.emplace(cast_op(std::move(inner_caster))); + return true; + } + + PYBIND11_TYPE_CASTER(T, _("Optional[") + value_conv::name + _("]")); +}; + +#if PYBIND11_HAS_OPTIONAL +template struct type_caster> + : public optional_caster> {}; + +template<> struct type_caster + : public void_caster {}; +#endif + +#if PYBIND11_HAS_EXP_OPTIONAL +template struct type_caster> + : public optional_caster> {}; + +template<> struct type_caster + : public void_caster {}; +#endif + +/// Visit a variant and cast any found type to Python +struct variant_caster_visitor { + return_value_policy policy; + handle parent; + + using result_type = handle; // required by boost::variant in C++11 + + template + result_type operator()(T &&src) const { + return make_caster::cast(std::forward(src), policy, parent); + } +}; + +/// Helper class which abstracts away variant's `visit` function. `std::variant` and similar +/// `namespace::variant` types which provide a `namespace::visit()` function are handled here +/// automatically using argument-dependent lookup. Users can provide specializations for other +/// variant-like classes, e.g. `boost::variant` and `boost::apply_visitor`. +template class Variant> +struct visit_helper { + template + static auto call(Args &&...args) -> decltype(visit(std::forward(args)...)) { + return visit(std::forward(args)...); + } +}; + +/// Generic variant caster +template struct variant_caster; + +template class V, typename... Ts> +struct variant_caster> { + static_assert(sizeof...(Ts) > 0, "Variant must consist of at least one alternative."); + + template + bool load_alternative(handle src, bool convert, type_list) { + auto caster = make_caster(); + if (caster.load(src, convert)) { + value = cast_op(caster); + return true; + } + return load_alternative(src, convert, type_list{}); + } + + bool load_alternative(handle, bool, type_list<>) { return false; } + + bool load(handle src, bool convert) { + // Do a first pass without conversions to improve constructor resolution. + // E.g. `py::int_(1).cast>()` needs to fill the `int` + // slot of the variant. Without two-pass loading `double` would be filled + // because it appears first and a conversion is possible. + if (convert && load_alternative(src, false, type_list{})) + return true; + return load_alternative(src, convert, type_list{}); + } + + template + static handle cast(Variant &&src, return_value_policy policy, handle parent) { + return visit_helper::call(variant_caster_visitor{policy, parent}, + std::forward(src)); + } + + using Type = V; + PYBIND11_TYPE_CASTER(Type, _("Union[") + detail::concat(make_caster::name...) + _("]")); +}; + +#if PYBIND11_HAS_VARIANT +template +struct type_caster> : variant_caster> { }; +#endif + +NAMESPACE_END(detail) + +inline std::ostream &operator<<(std::ostream &os, const handle &obj) { + os << (std::string) str(obj); + return os; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/thirdparty/pybind11/include/pybind11/stl_bind.h b/thirdparty/pybind11/include/pybind11/stl_bind.h new file mode 100644 index 000000000..da233eca9 --- /dev/null +++ b/thirdparty/pybind11/include/pybind11/stl_bind.h @@ -0,0 +1,656 @@ +/* + pybind11/std_bind.h: Binding generators for STL data types + + Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "detail/common.h" +#include "operators.h" + +#include +#include + +NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/* SFINAE helper class used by 'is_comparable */ +template struct container_traits { + template static std::true_type test_comparable(decltype(std::declval() == std::declval())*); + template static std::false_type test_comparable(...); + template static std::true_type test_value(typename T2::value_type *); + template static std::false_type test_value(...); + template static std::true_type test_pair(typename T2::first_type *, typename T2::second_type *); + template static std::false_type test_pair(...); + + static constexpr const bool is_comparable = std::is_same(nullptr))>::value; + static constexpr const bool is_pair = std::is_same(nullptr, nullptr))>::value; + static constexpr const bool is_vector = std::is_same(nullptr))>::value; + static constexpr const bool is_element = !is_pair && !is_vector; +}; + +/* Default: is_comparable -> std::false_type */ +template +struct is_comparable : std::false_type { }; + +/* For non-map data structures, check whether operator== can be instantiated */ +template +struct is_comparable< + T, enable_if_t::is_element && + container_traits::is_comparable>> + : std::true_type { }; + +/* For a vector/map data structure, recursively check the value type (which is std::pair for maps) */ +template +struct is_comparable::is_vector>> { + static constexpr const bool value = + is_comparable::value; +}; + +/* For pairs, recursively check the two data types */ +template +struct is_comparable::is_pair>> { + static constexpr const bool value = + is_comparable::value && + is_comparable::value; +}; + +/* Fallback functions */ +template void vector_if_copy_constructible(const Args &...) { } +template void vector_if_equal_operator(const Args &...) { } +template void vector_if_insertion_operator(const Args &...) { } +template void vector_modifiers(const Args &...) { } + +template +void vector_if_copy_constructible(enable_if_t::value, Class_> &cl) { + cl.def(init(), "Copy constructor"); +} + +template +void vector_if_equal_operator(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + + cl.def(self == self); + cl.def(self != self); + + cl.def("count", + [](const Vector &v, const T &x) { + return std::count(v.begin(), v.end(), x); + }, + arg("x"), + "Return the number of times ``x`` appears in the list" + ); + + cl.def("remove", [](Vector &v, const T &x) { + auto p = std::find(v.begin(), v.end(), x); + if (p != v.end()) + v.erase(p); + else + throw value_error(); + }, + arg("x"), + "Remove the first item from the list whose value is x. " + "It is an error if there is no such item." + ); + + cl.def("__contains__", + [](const Vector &v, const T &x) { + return std::find(v.begin(), v.end(), x) != v.end(); + }, + arg("x"), + "Return true the container contains ``x``" + ); +} + +// Vector modifiers -- requires a copyable vector_type: +// (Technically, some of these (pop and __delitem__) don't actually require copyability, but it seems +// silly to allow deletion but not insertion, so include them here too.) +template +void vector_modifiers(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; + + auto wrap_i = [](DiffType i, SizeType n) { + if (i < 0) + i += n; + if (i < 0 || (SizeType)i >= n) + throw index_error(); + return i; + }; + + cl.def("append", + [](Vector &v, const T &value) { v.push_back(value); }, + arg("x"), + "Add an item to the end of the list"); + + cl.def(init([](iterable it) { + auto v = std::unique_ptr(new Vector()); + v->reserve(len_hint(it)); + for (handle h : it) + v->push_back(h.cast()); + return v.release(); + })); + + cl.def("clear", + [](Vector &v) { + v.clear(); + }, + "Clear the contents" + ); + + cl.def("extend", + [](Vector &v, const Vector &src) { + v.insert(v.end(), src.begin(), src.end()); + }, + arg("L"), + "Extend the list by appending all the items in the given list" + ); + + cl.def("extend", + [](Vector &v, iterable it) { + const size_t old_size = v.size(); + v.reserve(old_size + len_hint(it)); + try { + for (handle h : it) { + v.push_back(h.cast()); + } + } catch (const cast_error &) { + v.erase(v.begin() + static_cast(old_size), v.end()); + try { + v.shrink_to_fit(); + } catch (const std::exception &) { + // Do nothing + } + throw; + } + }, + arg("L"), + "Extend the list by appending all the items in the given list" + ); + + cl.def("insert", + [](Vector &v, DiffType i, const T &x) { + // Can't use wrap_i; i == v.size() is OK + if (i < 0) + i += v.size(); + if (i < 0 || (SizeType)i > v.size()) + throw index_error(); + v.insert(v.begin() + i, x); + }, + arg("i") , arg("x"), + "Insert an item at a given position." + ); + + cl.def("pop", + [](Vector &v) { + if (v.empty()) + throw index_error(); + T t = v.back(); + v.pop_back(); + return t; + }, + "Remove and return the last item" + ); + + cl.def("pop", + [wrap_i](Vector &v, DiffType i) { + i = wrap_i(i, v.size()); + T t = v[(SizeType) i]; + v.erase(v.begin() + i); + return t; + }, + arg("i"), + "Remove and return the item at index ``i``" + ); + + cl.def("__setitem__", + [wrap_i](Vector &v, DiffType i, const T &t) { + i = wrap_i(i, v.size()); + v[(SizeType)i] = t; + } + ); + + /// Slicing protocol + cl.def("__getitem__", + [](const Vector &v, slice slice) -> Vector * { + size_t start, stop, step, slicelength; + + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + Vector *seq = new Vector(); + seq->reserve((size_t) slicelength); + + for (size_t i=0; ipush_back(v[start]); + start += step; + } + return seq; + }, + arg("s"), + "Retrieve list elements using a slice object" + ); + + cl.def("__setitem__", + [](Vector &v, slice slice, const Vector &value) { + size_t start, stop, step, slicelength; + if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) + throw error_already_set(); + + if (slicelength != value.size()) + throw std::runtime_error("Left and right hand size of slice assignment have different sizes!"); + + for (size_t i=0; i), +// we have to access by copying; otherwise we return by reference. +template using vector_needs_copy = negation< + std::is_same()[typename Vector::size_type()]), typename Vector::value_type &>>; + +// The usual case: access and iterate by reference +template +void vector_accessor(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; + using ItType = typename Vector::iterator; + + auto wrap_i = [](DiffType i, SizeType n) { + if (i < 0) + i += n; + if (i < 0 || (SizeType)i >= n) + throw index_error(); + return i; + }; + + cl.def("__getitem__", + [wrap_i](Vector &v, DiffType i) -> T & { + i = wrap_i(i, v.size()); + return v[(SizeType)i]; + }, + return_value_policy::reference_internal // ref + keepalive + ); + + cl.def("__iter__", + [](Vector &v) { + return make_iterator< + return_value_policy::reference_internal, ItType, ItType, T&>( + v.begin(), v.end()); + }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); +} + +// The case for special objects, like std::vector, that have to be returned-by-copy: +template +void vector_accessor(enable_if_t::value, Class_> &cl) { + using T = typename Vector::value_type; + using SizeType = typename Vector::size_type; + using DiffType = typename Vector::difference_type; + using ItType = typename Vector::iterator; + cl.def("__getitem__", + [](const Vector &v, DiffType i) -> T { + if (i < 0 && (i += v.size()) < 0) + throw index_error(); + if ((SizeType)i >= v.size()) + throw index_error(); + return v[(SizeType)i]; + } + ); + + cl.def("__iter__", + [](Vector &v) { + return make_iterator< + return_value_policy::copy, ItType, ItType, T>( + v.begin(), v.end()); + }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); +} + +template auto vector_if_insertion_operator(Class_ &cl, std::string const &name) + -> decltype(std::declval() << std::declval(), void()) { + using size_type = typename Vector::size_type; + + cl.def("__repr__", + [name](Vector &v) { + std::ostringstream s; + s << name << '['; + for (size_type i=0; i < v.size(); ++i) { + s << v[i]; + if (i != v.size() - 1) + s << ", "; + } + s << ']'; + return s.str(); + }, + "Return the canonical string representation of this list." + ); +} + +// Provide the buffer interface for vectors if we have data() and we have a format for it +// GCC seems to have "void std::vector::data()" - doing SFINAE on the existence of data() is insufficient, we need to check it returns an appropriate pointer +template +struct vector_has_data_and_format : std::false_type {}; +template +struct vector_has_data_and_format::format(), std::declval().data()), typename Vector::value_type*>::value>> : std::true_type {}; + +// Add the buffer interface to a vector +template +enable_if_t...>::value> +vector_buffer(Class_& cl) { + using T = typename Vector::value_type; + + static_assert(vector_has_data_and_format::value, "There is not an appropriate format descriptor for this vector"); + + // numpy.h declares this for arbitrary types, but it may raise an exception and crash hard at runtime if PYBIND11_NUMPY_DTYPE hasn't been called, so check here + format_descriptor::format(); + + cl.def_buffer([](Vector& v) -> buffer_info { + return buffer_info(v.data(), static_cast(sizeof(T)), format_descriptor::format(), 1, {v.size()}, {sizeof(T)}); + }); + + cl.def(init([](buffer buf) { + auto info = buf.request(); + if (info.ndim != 1 || info.strides[0] % static_cast(sizeof(T))) + throw type_error("Only valid 1D buffers can be copied to a vector"); + if (!detail::compare_buffer_info::compare(info) || (ssize_t) sizeof(T) != info.itemsize) + throw type_error("Format mismatch (Python: " + info.format + " C++: " + format_descriptor::format() + ")"); + + auto vec = std::unique_ptr(new Vector()); + vec->reserve((size_t) info.shape[0]); + T *p = static_cast(info.ptr); + ssize_t step = info.strides[0] / static_cast(sizeof(T)); + T *end = p + info.shape[0] * step; + for (; p != end; p += step) + vec->push_back(*p); + return vec.release(); + })); + + return; +} + +template +enable_if_t...>::value> vector_buffer(Class_&) {} + +NAMESPACE_END(detail) + +// +// std::vector +// +template , typename... Args> +class_ bind_vector(handle scope, std::string const &name, Args&&... args) { + using Class_ = class_; + + // If the value_type is unregistered (e.g. a converting type) or is itself registered + // module-local then make the vector binding module-local as well: + using vtype = typename Vector::value_type; + auto vtype_info = detail::get_type_info(typeid(vtype)); + bool local = !vtype_info || vtype_info->module_local; + + Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); + + // Declare the buffer interface if a buffer_protocol() is passed in + detail::vector_buffer(cl); + + cl.def(init<>()); + + // Register copy constructor (if possible) + detail::vector_if_copy_constructible(cl); + + // Register comparison-related operators and functions (if possible) + detail::vector_if_equal_operator(cl); + + // Register stream insertion operator (if possible) + detail::vector_if_insertion_operator(cl, name); + + // Modifiers require copyable vector value type + detail::vector_modifiers(cl); + + // Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive + detail::vector_accessor(cl); + + cl.def("__bool__", + [](const Vector &v) -> bool { + return !v.empty(); + }, + "Check whether the list is nonempty" + ); + + cl.def("__len__", &Vector::size); + + + + +#if 0 + // C++ style functions deprecated, leaving it here as an example + cl.def(init()); + + cl.def("resize", + (void (Vector::*) (size_type count)) & Vector::resize, + "changes the number of elements stored"); + + cl.def("erase", + [](Vector &v, SizeType i) { + if (i >= v.size()) + throw index_error(); + v.erase(v.begin() + i); + }, "erases element at index ``i``"); + + cl.def("empty", &Vector::empty, "checks whether the container is empty"); + cl.def("size", &Vector::size, "returns the number of elements"); + cl.def("push_back", (void (Vector::*)(const T&)) &Vector::push_back, "adds an element to the end"); + cl.def("pop_back", &Vector::pop_back, "removes the last element"); + + cl.def("max_size", &Vector::max_size, "returns the maximum possible number of elements"); + cl.def("reserve", &Vector::reserve, "reserves storage"); + cl.def("capacity", &Vector::capacity, "returns the number of elements that can be held in currently allocated storage"); + cl.def("shrink_to_fit", &Vector::shrink_to_fit, "reduces memory usage by freeing unused memory"); + + cl.def("clear", &Vector::clear, "clears the contents"); + cl.def("swap", &Vector::swap, "swaps the contents"); + + cl.def("front", [](Vector &v) { + if (v.size()) return v.front(); + else throw index_error(); + }, "access the first element"); + + cl.def("back", [](Vector &v) { + if (v.size()) return v.back(); + else throw index_error(); + }, "access the last element "); + +#endif + + return cl; +} + + + +// +// std::map, std::unordered_map +// + +NAMESPACE_BEGIN(detail) + +/* Fallback functions */ +template void map_if_insertion_operator(const Args &...) { } +template void map_assignment(const Args &...) { } + +// Map assignment when copy-assignable: just copy the value +template +void map_assignment(enable_if_t::value, Class_> &cl) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + + cl.def("__setitem__", + [](Map &m, const KeyType &k, const MappedType &v) { + auto it = m.find(k); + if (it != m.end()) it->second = v; + else m.emplace(k, v); + } + ); +} + +// Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting +template +void map_assignment(enable_if_t< + !is_copy_assignable::value && + is_copy_constructible::value, + Class_> &cl) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + + cl.def("__setitem__", + [](Map &m, const KeyType &k, const MappedType &v) { + // We can't use m[k] = v; because value type might not be default constructable + auto r = m.emplace(k, v); + if (!r.second) { + // value type is not copy assignable so the only way to insert it is to erase it first... + m.erase(r.first); + m.emplace(k, v); + } + } + ); +} + + +template auto map_if_insertion_operator(Class_ &cl, std::string const &name) +-> decltype(std::declval() << std::declval() << std::declval(), void()) { + + cl.def("__repr__", + [name](Map &m) { + std::ostringstream s; + s << name << '{'; + bool f = false; + for (auto const &kv : m) { + if (f) + s << ", "; + s << kv.first << ": " << kv.second; + f = true; + } + s << '}'; + return s.str(); + }, + "Return the canonical string representation of this map." + ); +} + + +NAMESPACE_END(detail) + +template , typename... Args> +class_ bind_map(handle scope, const std::string &name, Args&&... args) { + using KeyType = typename Map::key_type; + using MappedType = typename Map::mapped_type; + using Class_ = class_; + + // If either type is a non-module-local bound type then make the map binding non-local as well; + // otherwise (e.g. both types are either module-local or converting) the map will be + // module-local. + auto tinfo = detail::get_type_info(typeid(MappedType)); + bool local = !tinfo || tinfo->module_local; + if (local) { + tinfo = detail::get_type_info(typeid(KeyType)); + local = !tinfo || tinfo->module_local; + } + + Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); + + cl.def(init<>()); + + // Register stream insertion operator (if possible) + detail::map_if_insertion_operator(cl, name); + + cl.def("__bool__", + [](const Map &m) -> bool { return !m.empty(); }, + "Check whether the map is nonempty" + ); + + cl.def("__iter__", + [](Map &m) { return make_key_iterator(m.begin(), m.end()); }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); + + cl.def("items", + [](Map &m) { return make_iterator(m.begin(), m.end()); }, + keep_alive<0, 1>() /* Essential: keep list alive while iterator exists */ + ); + + cl.def("__getitem__", + [](Map &m, const KeyType &k) -> MappedType & { + auto it = m.find(k); + if (it == m.end()) + throw key_error(); + return it->second; + }, + return_value_policy::reference_internal // ref + keepalive + ); + + cl.def("__contains__", + [](Map &m, const KeyType &k) -> bool { + auto it = m.find(k); + if (it == m.end()) + return false; + return true; + } + ); + + // Assignment provided only if the type is copyable + detail::map_assignment(cl); + + cl.def("__delitem__", + [](Map &m, const KeyType &k) { + auto it = m.find(k); + if (it == m.end()) + throw key_error(); + m.erase(it); + } + ); + + cl.def("__len__", &Map::size); + + return cl; +} + +NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/thirdparty/pybind11/pybind11/__init__.py b/thirdparty/pybind11/pybind11/__init__.py new file mode 100644 index 000000000..4b1de3efa --- /dev/null +++ b/thirdparty/pybind11/pybind11/__init__.py @@ -0,0 +1,12 @@ +from ._version import version_info, __version__ # noqa: F401 imported but unused + + +def get_include(user=False): + import os + d = os.path.dirname(__file__) + if os.path.exists(os.path.join(d, "include")): + # Package is installed + return os.path.join(d, "include") + else: + # Package is from a source directory + return os.path.join(os.path.dirname(d), "include") diff --git a/thirdparty/pybind11/pybind11/__main__.py b/thirdparty/pybind11/pybind11/__main__.py new file mode 100644 index 000000000..89b263a8a --- /dev/null +++ b/thirdparty/pybind11/pybind11/__main__.py @@ -0,0 +1,36 @@ +from __future__ import print_function + +import argparse +import sys +import sysconfig + +from . import get_include + + +def print_includes(): + dirs = [sysconfig.get_path('include'), + sysconfig.get_path('platinclude'), + get_include()] + + # Make unique but preserve order + unique_dirs = [] + for d in dirs: + if d not in unique_dirs: + unique_dirs.append(d) + + print(' '.join('-I' + d for d in unique_dirs)) + + +def main(): + parser = argparse.ArgumentParser(prog='python -m pybind11') + parser.add_argument('--includes', action='store_true', + help='Include flags for both pybind11 and Python headers.') + args = parser.parse_args() + if not sys.argv[1:]: + parser.print_help() + if args.includes: + print_includes() + + +if __name__ == '__main__': + main() diff --git a/thirdparty/pybind11/pybind11/_version.py b/thirdparty/pybind11/pybind11/_version.py new file mode 100644 index 000000000..8d5aa5c76 --- /dev/null +++ b/thirdparty/pybind11/pybind11/_version.py @@ -0,0 +1,2 @@ +version_info = (2, 5, 0) +__version__ = '.'.join(map(str, version_info)) diff --git a/thirdparty/pybind11/setup.cfg b/thirdparty/pybind11/setup.cfg new file mode 100644 index 000000000..002f38d10 --- /dev/null +++ b/thirdparty/pybind11/setup.cfg @@ -0,0 +1,12 @@ +[bdist_wheel] +universal=1 + +[flake8] +max-line-length = 99 +show_source = True +exclude = .git, __pycache__, build, dist, docs, tools, venv +ignore = + # required for pretty matrix formatting: multiple spaces after `,` and `[` + E201, E241, W504, + # camelcase 'cPickle' imported as lowercase 'pickle' + N813 diff --git a/thirdparty/pybind11/setup.py b/thirdparty/pybind11/setup.py new file mode 100644 index 000000000..473ea1ee0 --- /dev/null +++ b/thirdparty/pybind11/setup.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python + +# Setup script for PyPI; use CMakeFile.txt to build extension modules + +from setuptools import setup +from distutils.command.install_headers import install_headers +from distutils.command.build_py import build_py +from pybind11 import __version__ +import os + +package_data = [ + 'include/pybind11/detail/class.h', + 'include/pybind11/detail/common.h', + 'include/pybind11/detail/descr.h', + 'include/pybind11/detail/init.h', + 'include/pybind11/detail/internals.h', + 'include/pybind11/detail/typeid.h', + 'include/pybind11/attr.h', + 'include/pybind11/buffer_info.h', + 'include/pybind11/cast.h', + 'include/pybind11/chrono.h', + 'include/pybind11/common.h', + 'include/pybind11/complex.h', + 'include/pybind11/eigen.h', + 'include/pybind11/embed.h', + 'include/pybind11/eval.h', + 'include/pybind11/functional.h', + 'include/pybind11/iostream.h', + 'include/pybind11/numpy.h', + 'include/pybind11/operators.h', + 'include/pybind11/options.h', + 'include/pybind11/pybind11.h', + 'include/pybind11/pytypes.h', + 'include/pybind11/stl.h', + 'include/pybind11/stl_bind.h', +] + +# Prevent installation of pybind11 headers by setting +# PYBIND11_USE_CMAKE. +if os.environ.get('PYBIND11_USE_CMAKE'): + headers = [] +else: + headers = package_data + + +class InstallHeaders(install_headers): + """Use custom header installer because the default one flattens subdirectories""" + def run(self): + if not self.distribution.headers: + return + + for header in self.distribution.headers: + subdir = os.path.dirname(os.path.relpath(header, 'include/pybind11')) + install_dir = os.path.join(self.install_dir, subdir) + self.mkpath(install_dir) + + (out, _) = self.copy_file(header, install_dir) + self.outfiles.append(out) + + +# Install the headers inside the package as well +class BuildPy(build_py): + def build_package_data(self): + build_py.build_package_data(self) + for header in package_data: + target = os.path.join(self.build_lib, 'pybind11', header) + self.mkpath(os.path.dirname(target)) + self.copy_file(header, target, preserve_mode=False) + + +setup( + name='pybind11', + version=__version__, + description='Seamless operability between C++11 and Python', + author='Wenzel Jakob', + author_email='wenzel.jakob@epfl.ch', + url='https://github.com/pybind/pybind11', + download_url='https://github.com/pybind/pybind11/tarball/v' + __version__, + packages=['pybind11'], + license='BSD', + headers=headers, + zip_safe=False, + cmdclass=dict(install_headers=InstallHeaders, build_py=BuildPy), + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Topic :: Utilities', + 'Programming Language :: C++', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'License :: OSI Approved :: BSD License' + ], + keywords='C++11, Python bindings', + long_description="""pybind11 is a lightweight header-only library that +exposes C++ types in Python and vice versa, mainly to create Python bindings of +existing C++ code. Its goals and syntax are similar to the excellent +Boost.Python by David Abrahams: to minimize boilerplate code in traditional +extension modules by inferring type information using compile-time +introspection. + +The main issue with Boost.Python-and the reason for creating such a similar +project-is Boost. Boost is an enormously large and complex suite of utility +libraries that works with almost every C++ compiler in existence. This +compatibility has its cost: arcane template tricks and workarounds are +necessary to support the oldest and buggiest of compiler specimens. Now that +C++11-compatible compilers are widely available, this heavy machinery has +become an excessively large and unnecessary dependency. + +Think of this library as a tiny self-contained version of Boost.Python with +everything stripped away that isn't relevant for binding generation. Without +comments, the core header files only require ~4K lines of code and depend on +Python (2.7 or 3.x, or PyPy2.7 >= 5.7) and the C++ standard library. This +compact implementation was possible thanks to some of the new C++11 language +features (specifically: tuples, lambda functions and variadic templates). Since +its creation, this library has grown beyond Boost.Python in many ways, leading +to dramatically simpler binding code in many common situations.""") diff --git a/thirdparty/pybind11/tools/FindCatch.cmake b/thirdparty/pybind11/tools/FindCatch.cmake new file mode 100644 index 000000000..9d490c5aa --- /dev/null +++ b/thirdparty/pybind11/tools/FindCatch.cmake @@ -0,0 +1,57 @@ +# - Find the Catch test framework or download it (single header) +# +# This is a quick module for internal use. It assumes that Catch is +# REQUIRED and that a minimum version is provided (not EXACT). If +# a suitable version isn't found locally, the single header file +# will be downloaded and placed in the build dir: PROJECT_BINARY_DIR. +# +# This code sets the following variables: +# CATCH_INCLUDE_DIR - path to catch.hpp +# CATCH_VERSION - version number + +if(NOT Catch_FIND_VERSION) + message(FATAL_ERROR "A version number must be specified.") +elseif(Catch_FIND_REQUIRED) + message(FATAL_ERROR "This module assumes Catch is not required.") +elseif(Catch_FIND_VERSION_EXACT) + message(FATAL_ERROR "Exact version numbers are not supported, only minimum.") +endif() + +# Extract the version number from catch.hpp +function(_get_catch_version) + file(STRINGS "${CATCH_INCLUDE_DIR}/catch.hpp" version_line REGEX "Catch v.*" LIMIT_COUNT 1) + if(version_line MATCHES "Catch v([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(CATCH_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE) + endif() +endfunction() + +# Download the single-header version of Catch +function(_download_catch version destination_dir) + message(STATUS "Downloading catch v${version}...") + set(url https://github.com/philsquared/Catch/releases/download/v${version}/catch.hpp) + file(DOWNLOAD ${url} "${destination_dir}/catch.hpp" STATUS status) + list(GET status 0 error) + if(error) + message(FATAL_ERROR "Could not download ${url}") + endif() + set(CATCH_INCLUDE_DIR "${destination_dir}" CACHE INTERNAL "") +endfunction() + +# Look for catch locally +find_path(CATCH_INCLUDE_DIR NAMES catch.hpp PATH_SUFFIXES catch) +if(CATCH_INCLUDE_DIR) + _get_catch_version() +endif() + +# Download the header if it wasn't found or if it's outdated +if(NOT CATCH_VERSION OR CATCH_VERSION VERSION_LESS ${Catch_FIND_VERSION}) + if(DOWNLOAD_CATCH) + _download_catch(${Catch_FIND_VERSION} "${PROJECT_BINARY_DIR}/catch/") + _get_catch_version() + else() + set(CATCH_FOUND FALSE) + return() + endif() +endif() + +set(CATCH_FOUND TRUE) diff --git a/thirdparty/pybind11/tools/FindEigen3.cmake b/thirdparty/pybind11/tools/FindEigen3.cmake new file mode 100644 index 000000000..9c546a05d --- /dev/null +++ b/thirdparty/pybind11/tools/FindEigen3.cmake @@ -0,0 +1,81 @@ +# - Try to find Eigen3 lib +# +# This module supports requiring a minimum version, e.g. you can do +# find_package(Eigen3 3.1.2) +# to require version 3.1.2 or newer of Eigen3. +# +# Once done this will define +# +# EIGEN3_FOUND - system has eigen lib with correct version +# EIGEN3_INCLUDE_DIR - the eigen include directory +# EIGEN3_VERSION - eigen version + +# Copyright (c) 2006, 2007 Montel Laurent, +# Copyright (c) 2008, 2009 Gael Guennebaud, +# Copyright (c) 2009 Benoit Jacob +# Redistribution and use is allowed according to the terms of the 2-clause BSD license. + +if(NOT Eigen3_FIND_VERSION) + if(NOT Eigen3_FIND_VERSION_MAJOR) + set(Eigen3_FIND_VERSION_MAJOR 2) + endif(NOT Eigen3_FIND_VERSION_MAJOR) + if(NOT Eigen3_FIND_VERSION_MINOR) + set(Eigen3_FIND_VERSION_MINOR 91) + endif(NOT Eigen3_FIND_VERSION_MINOR) + if(NOT Eigen3_FIND_VERSION_PATCH) + set(Eigen3_FIND_VERSION_PATCH 0) + endif(NOT Eigen3_FIND_VERSION_PATCH) + + set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}") +endif(NOT Eigen3_FIND_VERSION) + +macro(_eigen3_check_version) + file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) + + string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}") + set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}") + set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}") + set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}") + + set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION}) + if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + set(EIGEN3_VERSION_OK FALSE) + else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + set(EIGEN3_VERSION_OK TRUE) + endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + + if(NOT EIGEN3_VERSION_OK) + + message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, " + "but at least version ${Eigen3_FIND_VERSION} is required") + endif(NOT EIGEN3_VERSION_OK) +endmacro(_eigen3_check_version) + +if (EIGEN3_INCLUDE_DIR) + + # in cache already + _eigen3_check_version() + set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) + +else (EIGEN3_INCLUDE_DIR) + + find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library + PATHS + ${CMAKE_INSTALL_PREFIX}/include + ${KDE4_INCLUDE_DIR} + PATH_SUFFIXES eigen3 eigen + ) + + if(EIGEN3_INCLUDE_DIR) + _eigen3_check_version() + endif(EIGEN3_INCLUDE_DIR) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK) + + mark_as_advanced(EIGEN3_INCLUDE_DIR) + +endif(EIGEN3_INCLUDE_DIR) + diff --git a/thirdparty/pybind11/tools/FindPythonLibsNew.cmake b/thirdparty/pybind11/tools/FindPythonLibsNew.cmake new file mode 100644 index 000000000..9ea6036e3 --- /dev/null +++ b/thirdparty/pybind11/tools/FindPythonLibsNew.cmake @@ -0,0 +1,202 @@ +# - Find python libraries +# This module finds the libraries corresponding to the Python interpreter +# FindPythonInterp provides. +# This code sets the following variables: +# +# PYTHONLIBS_FOUND - have the Python libs been found +# PYTHON_PREFIX - path to the Python installation +# PYTHON_LIBRARIES - path to the python library +# PYTHON_INCLUDE_DIRS - path to where Python.h is found +# PYTHON_MODULE_EXTENSION - lib extension, e.g. '.so' or '.pyd' +# PYTHON_MODULE_PREFIX - lib name prefix: usually an empty string +# PYTHON_SITE_PACKAGES - path to installation site-packages +# PYTHON_IS_DEBUG - whether the Python interpreter is a debug build +# +# Thanks to talljimbo for the patch adding the 'LDVERSION' config +# variable usage. + +#============================================================================= +# Copyright 2001-2009 Kitware, Inc. +# Copyright 2012 Continuum Analytics, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# Checking for the extension makes sure that `LibsNew` was found and not just `Libs`. +if(PYTHONLIBS_FOUND AND PYTHON_MODULE_EXTENSION) + return() +endif() + +# Use the Python interpreter to find the libs. +if(PythonLibsNew_FIND_REQUIRED) + find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} REQUIRED) +else() + find_package(PythonInterp ${PythonLibsNew_FIND_VERSION}) +endif() + +if(NOT PYTHONINTERP_FOUND) + set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) + return() +endif() + +# According to http://stackoverflow.com/questions/646518/python-how-to-detect-debug-interpreter +# testing whether sys has the gettotalrefcount function is a reliable, cross-platform +# way to detect a CPython debug interpreter. +# +# The library suffix is from the config var LDVERSION sometimes, otherwise +# VERSION. VERSION will typically be like "2.7" on unix, and "27" on windows. +execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" + "from distutils import sysconfig as s;import sys;import struct; +print('.'.join(str(v) for v in sys.version_info)); +print(sys.prefix); +print(s.get_python_inc(plat_specific=True)); +print(s.get_python_lib(plat_specific=True)); +print(s.get_config_var('SO')); +print(hasattr(sys, 'gettotalrefcount')+0); +print(struct.calcsize('@P')); +print(s.get_config_var('LDVERSION') or s.get_config_var('VERSION')); +print(s.get_config_var('LIBDIR') or ''); +print(s.get_config_var('MULTIARCH') or ''); +" + RESULT_VARIABLE _PYTHON_SUCCESS + OUTPUT_VARIABLE _PYTHON_VALUES + ERROR_VARIABLE _PYTHON_ERROR_VALUE) + +if(NOT _PYTHON_SUCCESS MATCHES 0) + if(PythonLibsNew_FIND_REQUIRED) + message(FATAL_ERROR + "Python config failure:\n${_PYTHON_ERROR_VALUE}") + endif() + set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) + return() +endif() + +# Convert the process output into a list +if(WIN32) + string(REGEX REPLACE "\\\\" "/" _PYTHON_VALUES ${_PYTHON_VALUES}) +endif() +string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES}) +string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES}) +list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST) +list(GET _PYTHON_VALUES 1 PYTHON_PREFIX) +list(GET _PYTHON_VALUES 2 PYTHON_INCLUDE_DIR) +list(GET _PYTHON_VALUES 3 PYTHON_SITE_PACKAGES) +list(GET _PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION) +list(GET _PYTHON_VALUES 5 PYTHON_IS_DEBUG) +list(GET _PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P) +list(GET _PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX) +list(GET _PYTHON_VALUES 8 PYTHON_LIBDIR) +list(GET _PYTHON_VALUES 9 PYTHON_MULTIARCH) + +# Make sure the Python has the same pointer-size as the chosen compiler +# Skip if CMAKE_SIZEOF_VOID_P is not defined +if(CMAKE_SIZEOF_VOID_P AND (NOT "${PYTHON_SIZEOF_VOID_P}" STREQUAL "${CMAKE_SIZEOF_VOID_P}")) + if(PythonLibsNew_FIND_REQUIRED) + math(EXPR _PYTHON_BITS "${PYTHON_SIZEOF_VOID_P} * 8") + math(EXPR _CMAKE_BITS "${CMAKE_SIZEOF_VOID_P} * 8") + message(FATAL_ERROR + "Python config failure: Python is ${_PYTHON_BITS}-bit, " + "chosen compiler is ${_CMAKE_BITS}-bit") + endif() + set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) + return() +endif() + +# The built-in FindPython didn't always give the version numbers +string(REGEX REPLACE "\\." ";" _PYTHON_VERSION_LIST ${_PYTHON_VERSION_LIST}) +list(GET _PYTHON_VERSION_LIST 0 PYTHON_VERSION_MAJOR) +list(GET _PYTHON_VERSION_LIST 1 PYTHON_VERSION_MINOR) +list(GET _PYTHON_VERSION_LIST 2 PYTHON_VERSION_PATCH) + +# Make sure all directory separators are '/' +string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX "${PYTHON_PREFIX}") +string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}") +string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES "${PYTHON_SITE_PACKAGES}") + +if(CMAKE_HOST_WIN32 AND NOT (MINGW AND DEFINED ENV{MSYSTEM})) + set(PYTHON_LIBRARY + "${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") + + # when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the + # original python installation. They may be found relative to PYTHON_INCLUDE_DIR. + if(NOT EXISTS "${PYTHON_LIBRARY}") + get_filename_component(_PYTHON_ROOT ${PYTHON_INCLUDE_DIR} DIRECTORY) + set(PYTHON_LIBRARY + "${_PYTHON_ROOT}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") + endif() + + # raise an error if the python libs are still not found. + if(NOT EXISTS "${PYTHON_LIBRARY}") + message(FATAL_ERROR "Python libraries not found") + endif() + +else() + if(PYTHON_MULTIARCH) + set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}/${PYTHON_MULTIARCH}" "${PYTHON_LIBDIR}") + else() + set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}") + endif() + #message(STATUS "Searching for Python libs in ${_PYTHON_LIBS_SEARCH}") + # Probably this needs to be more involved. It would be nice if the config + # information the python interpreter itself gave us were more complete. + find_library(PYTHON_LIBRARY + NAMES "python${PYTHON_LIBRARY_SUFFIX}" + PATHS ${_PYTHON_LIBS_SEARCH} + NO_DEFAULT_PATH) + + # If all else fails, just set the name/version and let the linker figure out the path. + if(NOT PYTHON_LIBRARY) + set(PYTHON_LIBRARY python${PYTHON_LIBRARY_SUFFIX}) + endif() +endif() + +MARK_AS_ADVANCED( + PYTHON_LIBRARY + PYTHON_INCLUDE_DIR +) + +# We use PYTHON_INCLUDE_DIR, PYTHON_LIBRARY and PYTHON_DEBUG_LIBRARY for the +# cache entries because they are meant to specify the location of a single +# library. We now set the variables listed by the documentation for this +# module. +SET(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}") +SET(PYTHON_LIBRARIES "${PYTHON_LIBRARY}") +SET(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}") + +find_package_message(PYTHON + "Found PythonLibs: ${PYTHON_LIBRARY}" + "${PYTHON_EXECUTABLE}${PYTHON_VERSION}") + +set(PYTHONLIBS_FOUND TRUE) +set(PythonLibsNew_FOUND TRUE) diff --git a/thirdparty/pybind11/tools/check-style.sh b/thirdparty/pybind11/tools/check-style.sh new file mode 100644 index 000000000..0a9f7d24f --- /dev/null +++ b/thirdparty/pybind11/tools/check-style.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# +# Script to check include/test code for common pybind11 code style errors. +# +# This script currently checks for +# +# 1. use of tabs instead of spaces +# 2. MSDOS-style CRLF endings +# 3. trailing spaces +# 4. missing space between keyword and parenthesis, e.g.: for(, if(, while( +# 5. Missing space between right parenthesis and brace, e.g. 'for (...){' +# 6. opening brace on its own line. It should always be on the same line as the +# if/while/for/do statement. +# +# Invoke as: tools/check-style.sh +# + +check_style_errors=0 +IFS=$'\n' + +found="$( GREP_COLORS='mt=41' GREP_COLOR='41' grep $'\t' include tests/*.{cpp,py,h} docs/*.rst -rn --color=always )" +if [ -n "$found" ]; then + # The mt=41 sets a red background for matched tabs: + echo -e '\033[31;01mError: found tab characters in the following files:\033[0m' + check_style_errors=1 + echo "$found" | sed -e 's/^/ /' +fi + + +found="$( grep -IUlr $'\r' include tests/*.{cpp,py,h} docs/*.rst --color=always )" +if [ -n "$found" ]; then + echo -e '\033[31;01mError: found CRLF characters in the following files:\033[0m' + check_style_errors=1 + echo "$found" | sed -e 's/^/ /' +fi + +found="$(GREP_COLORS='mt=41' GREP_COLOR='41' grep '[[:blank:]]\+$' include tests/*.{cpp,py,h} docs/*.rst -rn --color=always )" +if [ -n "$found" ]; then + # The mt=41 sets a red background for matched trailing spaces + echo -e '\033[31;01mError: found trailing spaces in the following files:\033[0m' + check_style_errors=1 + echo "$found" | sed -e 's/^/ /' +fi + +found="$(grep '\<\(if\|for\|while\|catch\)(\|){' include tests/*.{cpp,h} -rn --color=always)" +if [ -n "$found" ]; then + echo -e '\033[31;01mError: found the following coding style problems:\033[0m' + check_style_errors=1 + echo "$found" | sed -e 's/^/ /' +fi + +found="$(awk ' +function prefix(filename, lineno) { + return " \033[35m" filename "\033[36m:\033[32m" lineno "\033[36m:\033[0m" +} +function mark(pattern, string) { sub(pattern, "\033[01;31m&\033[0m", string); return string } +last && /^\s*{/ { + print prefix(FILENAME, FNR-1) mark("\\)\\s*$", last) + print prefix(FILENAME, FNR) mark("^\\s*{", $0) + last="" +} +{ last = /(if|for|while|catch|switch)\s*\(.*\)\s*$/ ? $0 : "" } +' $(find include -type f) tests/*.{cpp,h} docs/*.rst)" +if [ -n "$found" ]; then + check_style_errors=1 + echo -e '\033[31;01mError: braces should occur on the same line as the if/while/.. statement. Found issues in the following files:\033[0m' + echo "$found" +fi + +exit $check_style_errors diff --git a/thirdparty/pybind11/tools/clang/.gitignore b/thirdparty/pybind11/tools/clang/.gitignore new file mode 100644 index 000000000..8819bdaf3 --- /dev/null +++ b/thirdparty/pybind11/tools/clang/.gitignore @@ -0,0 +1,4 @@ +*.swp +*.swo +*.pyc +__pycache__ diff --git a/thirdparty/pybind11/tools/clang/LICENSE.TXT b/thirdparty/pybind11/tools/clang/LICENSE.TXT new file mode 100644 index 000000000..6c224f84c --- /dev/null +++ b/thirdparty/pybind11/tools/clang/LICENSE.TXT @@ -0,0 +1,63 @@ +============================================================================== +LLVM Release License +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2007-2012 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== +The LLVM software contains code written by third parties. Such software will +have its own individual LICENSE.TXT file in the directory in which it appears. +This file will describe the copyrights, license, and restrictions which apply +to that code. + +The disclaimer of warranty in the University of Illinois Open Source License +applies to all code in the LLVM Distribution, and nothing in any of the +other licenses gives permission to use the names of the LLVM Team or the +University of Illinois to endorse or promote products derived from this +Software. + +The following pieces of software have additional or alternate copyrights, +licenses, and/or restrictions: + +Program Directory +------- --------- + + diff --git a/thirdparty/pybind11/tools/clang/README.md b/thirdparty/pybind11/tools/clang/README.md new file mode 100644 index 000000000..efb892166 --- /dev/null +++ b/thirdparty/pybind11/tools/clang/README.md @@ -0,0 +1,2 @@ +This is simply clang's Python bindings (clang.cindex) ported to Python 3. Please see http://llvm.org/svn/llvm-project/cfe/trunk/bindings/python/ for the original project. + diff --git a/thirdparty/pybind11/tools/clang/__init__.py b/thirdparty/pybind11/tools/clang/__init__.py new file mode 100644 index 000000000..88f308123 --- /dev/null +++ b/thirdparty/pybind11/tools/clang/__init__.py @@ -0,0 +1,24 @@ +#===- __init__.py - Clang Python Bindings --------------------*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +r""" +Clang Library Bindings +====================== + +This package provides access to the Clang compiler and libraries. + +The available modules are: + + cindex + + Bindings for the Clang indexing library. +""" + +__all__ = ['cindex'] + diff --git a/thirdparty/pybind11/tools/clang/cindex.py b/thirdparty/pybind11/tools/clang/cindex.py new file mode 100644 index 000000000..3a083de0d --- /dev/null +++ b/thirdparty/pybind11/tools/clang/cindex.py @@ -0,0 +1,3884 @@ +#===- cindex.py - Python Indexing Library Bindings -----------*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +r""" +Clang Indexing Library Bindings +=============================== + +This module provides an interface to the Clang indexing library. It is a +low-level interface to the indexing library which attempts to match the Clang +API directly while also being "pythonic". Notable differences from the C API +are: + + * string results are returned as Python strings, not CXString objects. + + * null cursors are translated to None. + + * access to child cursors is done via iteration, not visitation. + +The major indexing objects are: + + Index + + The top-level object which manages some global library state. + + TranslationUnit + + High-level object encapsulating the AST for a single translation unit. These + can be loaded from .ast files or parsed on the fly. + + Cursor + + Generic object for representing a node in the AST. + + SourceRange, SourceLocation, and File + + Objects representing information about the input source. + +Most object information is exposed using properties, when the underlying API +call is efficient. +""" + +# TODO +# ==== +# +# o API support for invalid translation units. Currently we can't even get the +# diagnostics on failure because they refer to locations in an object that +# will have been invalidated. +# +# o fix memory management issues (currently client must hold on to index and +# translation unit, or risk crashes). +# +# o expose code completion APIs. +# +# o cleanup ctypes wrapping, would be nice to separate the ctypes details more +# clearly, and hide from the external interface (i.e., help(cindex)). +# +# o implement additional SourceLocation, SourceRange, and File methods. + +from ctypes import * +import collections + +import clang.enumerations + +# ctypes doesn't implicitly convert c_void_p to the appropriate wrapper +# object. This is a problem, because it means that from_parameter will see an +# integer and pass the wrong value on platforms where int != void*. Work around +# this by marshalling object arguments as void**. +c_object_p = POINTER(c_void_p) + +callbacks = {} + +### Exception Classes ### + +class TranslationUnitLoadError(Exception): + """Represents an error that occurred when loading a TranslationUnit. + + This is raised in the case where a TranslationUnit could not be + instantiated due to failure in the libclang library. + + FIXME: Make libclang expose additional error information in this scenario. + """ + pass + +class TranslationUnitSaveError(Exception): + """Represents an error that occurred when saving a TranslationUnit. + + Each error has associated with it an enumerated value, accessible under + e.save_error. Consumers can compare the value with one of the ERROR_ + constants in this class. + """ + + # Indicates that an unknown error occurred. This typically indicates that + # I/O failed during save. + ERROR_UNKNOWN = 1 + + # Indicates that errors during translation prevented saving. The errors + # should be available via the TranslationUnit's diagnostics. + ERROR_TRANSLATION_ERRORS = 2 + + # Indicates that the translation unit was somehow invalid. + ERROR_INVALID_TU = 3 + + def __init__(self, enumeration, message): + assert isinstance(enumeration, int) + + if enumeration < 1 or enumeration > 3: + raise Exception("Encountered undefined TranslationUnit save error " + "constant: %d. Please file a bug to have this " + "value supported." % enumeration) + + self.save_error = enumeration + Exception.__init__(self, 'Error %d: %s' % (enumeration, message)) + +### Structures and Utility Classes ### + +class CachedProperty(object): + """Decorator that lazy-loads the value of a property. + + The first time the property is accessed, the original property function is + executed. The value it returns is set as the new value of that instance's + property, replacing the original method. + """ + + def __init__(self, wrapped): + self.wrapped = wrapped + try: + self.__doc__ = wrapped.__doc__ + except: + pass + + def __get__(self, instance, instance_type=None): + if instance is None: + return self + + value = self.wrapped(instance) + setattr(instance, self.wrapped.__name__, value) + + return value + + +class _CXString(Structure): + """Helper for transforming CXString results.""" + + _fields_ = [("spelling", c_char_p), ("free", c_int)] + + def __del__(self): + conf.lib.clang_disposeString(self) + + @staticmethod + def from_result(res, fn, args): + assert isinstance(res, _CXString) + return conf.lib.clang_getCString(res) + +class SourceLocation(Structure): + """ + A SourceLocation represents a particular location within a source file. + """ + _fields_ = [("ptr_data", c_void_p * 2), ("int_data", c_uint)] + _data = None + + def _get_instantiation(self): + if self._data is None: + f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint() + conf.lib.clang_getInstantiationLocation(self, byref(f), byref(l), + byref(c), byref(o)) + if f: + f = File(f) + else: + f = None + self._data = (f, int(l.value), int(c.value), int(o.value)) + return self._data + + @staticmethod + def from_position(tu, file, line, column): + """ + Retrieve the source location associated with a given file/line/column in + a particular translation unit. + """ + return conf.lib.clang_getLocation(tu, file, line, column) + + @staticmethod + def from_offset(tu, file, offset): + """Retrieve a SourceLocation from a given character offset. + + tu -- TranslationUnit file belongs to + file -- File instance to obtain offset from + offset -- Integer character offset within file + """ + return conf.lib.clang_getLocationForOffset(tu, file, offset) + + @property + def file(self): + """Get the file represented by this source location.""" + return self._get_instantiation()[0] + + @property + def line(self): + """Get the line represented by this source location.""" + return self._get_instantiation()[1] + + @property + def column(self): + """Get the column represented by this source location.""" + return self._get_instantiation()[2] + + @property + def offset(self): + """Get the file offset represented by this source location.""" + return self._get_instantiation()[3] + + def __eq__(self, other): + return conf.lib.clang_equalLocations(self, other) + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + if self.file: + filename = self.file.name + else: + filename = None + return "" % ( + filename, self.line, self.column) + +class SourceRange(Structure): + """ + A SourceRange describes a range of source locations within the source + code. + """ + _fields_ = [ + ("ptr_data", c_void_p * 2), + ("begin_int_data", c_uint), + ("end_int_data", c_uint)] + + # FIXME: Eliminate this and make normal constructor? Requires hiding ctypes + # object. + @staticmethod + def from_locations(start, end): + return conf.lib.clang_getRange(start, end) + + @property + def start(self): + """ + Return a SourceLocation representing the first character within a + source range. + """ + return conf.lib.clang_getRangeStart(self) + + @property + def end(self): + """ + Return a SourceLocation representing the last character within a + source range. + """ + return conf.lib.clang_getRangeEnd(self) + + def __eq__(self, other): + return conf.lib.clang_equalRanges(self, other) + + def __ne__(self, other): + return not self.__eq__(other) + + def __contains__(self, other): + """Useful to detect the Token/Lexer bug""" + if not isinstance(other, SourceLocation): + return False + if other.file is None and self.start.file is None: + pass + elif ( self.start.file.name != other.file.name or + other.file.name != self.end.file.name): + # same file name + return False + # same file, in between lines + if self.start.line < other.line < self.end.line: + return True + elif self.start.line == other.line: + # same file first line + if self.start.column <= other.column: + return True + elif other.line == self.end.line: + # same file last line + if other.column <= self.end.column: + return True + return False + + def __repr__(self): + return "" % (self.start, self.end) + +class Diagnostic(object): + """ + A Diagnostic is a single instance of a Clang diagnostic. It includes the + diagnostic severity, the message, the location the diagnostic occurred, as + well as additional source ranges and associated fix-it hints. + """ + + Ignored = 0 + Note = 1 + Warning = 2 + Error = 3 + Fatal = 4 + + def __init__(self, ptr): + self.ptr = ptr + + def __del__(self): + conf.lib.clang_disposeDiagnostic(self) + + @property + def severity(self): + return conf.lib.clang_getDiagnosticSeverity(self) + + @property + def location(self): + return conf.lib.clang_getDiagnosticLocation(self) + + @property + def spelling(self): + return conf.lib.clang_getDiagnosticSpelling(self) + + @property + def ranges(self): + class RangeIterator: + def __init__(self, diag): + self.diag = diag + + def __len__(self): + return int(conf.lib.clang_getDiagnosticNumRanges(self.diag)) + + def __getitem__(self, key): + if (key >= len(self)): + raise IndexError + return conf.lib.clang_getDiagnosticRange(self.diag, key) + + return RangeIterator(self) + + @property + def fixits(self): + class FixItIterator: + def __init__(self, diag): + self.diag = diag + + def __len__(self): + return int(conf.lib.clang_getDiagnosticNumFixIts(self.diag)) + + def __getitem__(self, key): + range = SourceRange() + value = conf.lib.clang_getDiagnosticFixIt(self.diag, key, + byref(range)) + if len(value) == 0: + raise IndexError + + return FixIt(range, value) + + return FixItIterator(self) + + @property + def children(self): + class ChildDiagnosticsIterator: + def __init__(self, diag): + self.diag_set = conf.lib.clang_getChildDiagnostics(diag) + + def __len__(self): + return int(conf.lib.clang_getNumDiagnosticsInSet(self.diag_set)) + + def __getitem__(self, key): + diag = conf.lib.clang_getDiagnosticInSet(self.diag_set, key) + if not diag: + raise IndexError + return Diagnostic(diag) + + return ChildDiagnosticsIterator(self) + + @property + def category_number(self): + """The category number for this diagnostic or 0 if unavailable.""" + return conf.lib.clang_getDiagnosticCategory(self) + + @property + def category_name(self): + """The string name of the category for this diagnostic.""" + return conf.lib.clang_getDiagnosticCategoryText(self) + + @property + def option(self): + """The command-line option that enables this diagnostic.""" + return conf.lib.clang_getDiagnosticOption(self, None) + + @property + def disable_option(self): + """The command-line option that disables this diagnostic.""" + disable = _CXString() + conf.lib.clang_getDiagnosticOption(self, byref(disable)) + + return conf.lib.clang_getCString(disable) + + def __repr__(self): + return "" % ( + self.severity, self.location, self.spelling) + + def from_param(self): + return self.ptr + +class FixIt(object): + """ + A FixIt represents a transformation to be applied to the source to + "fix-it". The fix-it shouldbe applied by replacing the given source range + with the given value. + """ + + def __init__(self, range, value): + self.range = range + self.value = value + + def __repr__(self): + return "" % (self.range, self.value) + +class TokenGroup(object): + """Helper class to facilitate token management. + + Tokens are allocated from libclang in chunks. They must be disposed of as a + collective group. + + One purpose of this class is for instances to represent groups of allocated + tokens. Each token in a group contains a reference back to an instance of + this class. When all tokens from a group are garbage collected, it allows + this class to be garbage collected. When this class is garbage collected, + it calls the libclang destructor which invalidates all tokens in the group. + + You should not instantiate this class outside of this module. + """ + def __init__(self, tu, memory, count): + self._tu = tu + self._memory = memory + self._count = count + + def __del__(self): + conf.lib.clang_disposeTokens(self._tu, self._memory, self._count) + + @staticmethod + def get_tokens(tu, extent): + """Helper method to return all tokens in an extent. + + This functionality is needed multiple places in this module. We define + it here because it seems like a logical place. + """ + tokens_memory = POINTER(Token)() + tokens_count = c_uint() + + conf.lib.clang_tokenize(tu, extent, byref(tokens_memory), + byref(tokens_count)) + + count = int(tokens_count.value) + + # If we get no tokens, no memory was allocated. Be sure not to return + # anything and potentially call a destructor on nothing. + if count < 1: + return + + tokens_array = cast(tokens_memory, POINTER(Token * count)).contents + + token_group = TokenGroup(tu, tokens_memory, tokens_count) + + for i in range(0, count): + token = Token() + token.int_data = tokens_array[i].int_data + token.ptr_data = tokens_array[i].ptr_data + token._tu = tu + token._group = token_group + + yield token + +class TokenKind(object): + """Describes a specific type of a Token.""" + + _value_map = {} # int -> TokenKind + + def __init__(self, value, name): + """Create a new TokenKind instance from a numeric value and a name.""" + self.value = value + self.name = name + + def __repr__(self): + return 'TokenKind.%s' % (self.name,) + + @staticmethod + def from_value(value): + """Obtain a registered TokenKind instance from its value.""" + result = TokenKind._value_map.get(value, None) + + if result is None: + raise ValueError('Unknown TokenKind: %d' % value) + + return result + + @staticmethod + def register(value, name): + """Register a new TokenKind enumeration. + + This should only be called at module load time by code within this + package. + """ + if value in TokenKind._value_map: + raise ValueError('TokenKind already registered: %d' % value) + + kind = TokenKind(value, name) + TokenKind._value_map[value] = kind + setattr(TokenKind, name, kind) + +### Cursor Kinds ### +class BaseEnumeration(object): + """ + Common base class for named enumerations held in sync with Index.h values. + + Subclasses must define their own _kinds and _name_map members, as: + _kinds = [] + _name_map = None + These values hold the per-subclass instances and value-to-name mappings, + respectively. + + """ + + def __init__(self, value): + if value >= len(self.__class__._kinds): + self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) + if self.__class__._kinds[value] is not None: + raise ValueError('{0} value {1} already loaded'.format( + str(self.__class__), value)) + self.value = value + self.__class__._kinds[value] = self + self.__class__._name_map = None + + + def from_param(self): + return self.value + + @property + def name(self): + """Get the enumeration name of this cursor kind.""" + if self._name_map is None: + self._name_map = {} + for key, value in list(self.__class__.__dict__.items()): + if isinstance(value, self.__class__): + self._name_map[value] = key + return self._name_map[self] + + @classmethod + def from_id(cls, id): + if id >= len(cls._kinds) or cls._kinds[id] is None: + raise ValueError('Unknown template argument kind %d' % id) + return cls._kinds[id] + + def __repr__(self): + return '%s.%s' % (self.__class__, self.name,) + + +class CursorKind(BaseEnumeration): + """ + A CursorKind describes the kind of entity that a cursor points to. + """ + + # The required BaseEnumeration declarations. + _kinds = [] + _name_map = None + + @staticmethod + def get_all_kinds(): + """Return all CursorKind enumeration instances.""" + return [_f for _f in CursorKind._kinds if _f] + + def is_declaration(self): + """Test if this is a declaration kind.""" + return conf.lib.clang_isDeclaration(self) + + def is_reference(self): + """Test if this is a reference kind.""" + return conf.lib.clang_isReference(self) + + def is_expression(self): + """Test if this is an expression kind.""" + return conf.lib.clang_isExpression(self) + + def is_statement(self): + """Test if this is a statement kind.""" + return conf.lib.clang_isStatement(self) + + def is_attribute(self): + """Test if this is an attribute kind.""" + return conf.lib.clang_isAttribute(self) + + def is_invalid(self): + """Test if this is an invalid kind.""" + return conf.lib.clang_isInvalid(self) + + def is_translation_unit(self): + """Test if this is a translation unit kind.""" + return conf.lib.clang_isTranslationUnit(self) + + def is_preprocessing(self): + """Test if this is a preprocessing kind.""" + return conf.lib.clang_isPreprocessing(self) + + def is_unexposed(self): + """Test if this is an unexposed kind.""" + return conf.lib.clang_isUnexposed(self) + + def __repr__(self): + return 'CursorKind.%s' % (self.name,) + +### +# Declaration Kinds + +# A declaration whose specific kind is not exposed via this interface. +# +# Unexposed declarations have the same operations as any other kind of +# declaration; one can extract their location information, spelling, find their +# definitions, etc. However, the specific kind of the declaration is not +# reported. +CursorKind.UNEXPOSED_DECL = CursorKind(1) + +# A C or C++ struct. +CursorKind.STRUCT_DECL = CursorKind(2) + +# A C or C++ union. +CursorKind.UNION_DECL = CursorKind(3) + +# A C++ class. +CursorKind.CLASS_DECL = CursorKind(4) + +# An enumeration. +CursorKind.ENUM_DECL = CursorKind(5) + +# A field (in C) or non-static data member (in C++) in a struct, union, or C++ +# class. +CursorKind.FIELD_DECL = CursorKind(6) + +# An enumerator constant. +CursorKind.ENUM_CONSTANT_DECL = CursorKind(7) + +# A function. +CursorKind.FUNCTION_DECL = CursorKind(8) + +# A variable. +CursorKind.VAR_DECL = CursorKind(9) + +# A function or method parameter. +CursorKind.PARM_DECL = CursorKind(10) + +# An Objective-C @interface. +CursorKind.OBJC_INTERFACE_DECL = CursorKind(11) + +# An Objective-C @interface for a category. +CursorKind.OBJC_CATEGORY_DECL = CursorKind(12) + +# An Objective-C @protocol declaration. +CursorKind.OBJC_PROTOCOL_DECL = CursorKind(13) + +# An Objective-C @property declaration. +CursorKind.OBJC_PROPERTY_DECL = CursorKind(14) + +# An Objective-C instance variable. +CursorKind.OBJC_IVAR_DECL = CursorKind(15) + +# An Objective-C instance method. +CursorKind.OBJC_INSTANCE_METHOD_DECL = CursorKind(16) + +# An Objective-C class method. +CursorKind.OBJC_CLASS_METHOD_DECL = CursorKind(17) + +# An Objective-C @implementation. +CursorKind.OBJC_IMPLEMENTATION_DECL = CursorKind(18) + +# An Objective-C @implementation for a category. +CursorKind.OBJC_CATEGORY_IMPL_DECL = CursorKind(19) + +# A typedef. +CursorKind.TYPEDEF_DECL = CursorKind(20) + +# A C++ class method. +CursorKind.CXX_METHOD = CursorKind(21) + +# A C++ namespace. +CursorKind.NAMESPACE = CursorKind(22) + +# A linkage specification, e.g. 'extern "C"'. +CursorKind.LINKAGE_SPEC = CursorKind(23) + +# A C++ constructor. +CursorKind.CONSTRUCTOR = CursorKind(24) + +# A C++ destructor. +CursorKind.DESTRUCTOR = CursorKind(25) + +# A C++ conversion function. +CursorKind.CONVERSION_FUNCTION = CursorKind(26) + +# A C++ template type parameter +CursorKind.TEMPLATE_TYPE_PARAMETER = CursorKind(27) + +# A C++ non-type template paramater. +CursorKind.TEMPLATE_NON_TYPE_PARAMETER = CursorKind(28) + +# A C++ template template parameter. +CursorKind.TEMPLATE_TEMPLATE_PARAMETER = CursorKind(29) + +# A C++ function template. +CursorKind.FUNCTION_TEMPLATE = CursorKind(30) + +# A C++ class template. +CursorKind.CLASS_TEMPLATE = CursorKind(31) + +# A C++ class template partial specialization. +CursorKind.CLASS_TEMPLATE_PARTIAL_SPECIALIZATION = CursorKind(32) + +# A C++ namespace alias declaration. +CursorKind.NAMESPACE_ALIAS = CursorKind(33) + +# A C++ using directive +CursorKind.USING_DIRECTIVE = CursorKind(34) + +# A C++ using declaration +CursorKind.USING_DECLARATION = CursorKind(35) + +# A Type alias decl. +CursorKind.TYPE_ALIAS_DECL = CursorKind(36) + +# A Objective-C synthesize decl +CursorKind.OBJC_SYNTHESIZE_DECL = CursorKind(37) + +# A Objective-C dynamic decl +CursorKind.OBJC_DYNAMIC_DECL = CursorKind(38) + +# A C++ access specifier decl. +CursorKind.CXX_ACCESS_SPEC_DECL = CursorKind(39) + + +### +# Reference Kinds + +CursorKind.OBJC_SUPER_CLASS_REF = CursorKind(40) +CursorKind.OBJC_PROTOCOL_REF = CursorKind(41) +CursorKind.OBJC_CLASS_REF = CursorKind(42) + +# A reference to a type declaration. +# +# A type reference occurs anywhere where a type is named but not +# declared. For example, given: +# typedef unsigned size_type; +# size_type size; +# +# The typedef is a declaration of size_type (CXCursor_TypedefDecl), +# while the type of the variable "size" is referenced. The cursor +# referenced by the type of size is the typedef for size_type. +CursorKind.TYPE_REF = CursorKind(43) +CursorKind.CXX_BASE_SPECIFIER = CursorKind(44) + +# A reference to a class template, function template, template +# template parameter, or class template partial specialization. +CursorKind.TEMPLATE_REF = CursorKind(45) + +# A reference to a namespace or namepsace alias. +CursorKind.NAMESPACE_REF = CursorKind(46) + +# A reference to a member of a struct, union, or class that occurs in +# some non-expression context, e.g., a designated initializer. +CursorKind.MEMBER_REF = CursorKind(47) + +# A reference to a labeled statement. +CursorKind.LABEL_REF = CursorKind(48) + +# A reference to a set of overloaded functions or function templates +# that has not yet been resolved to a specific function or function template. +CursorKind.OVERLOADED_DECL_REF = CursorKind(49) + +# A reference to a variable that occurs in some non-expression +# context, e.g., a C++ lambda capture list. +CursorKind.VARIABLE_REF = CursorKind(50) + +### +# Invalid/Error Kinds + +CursorKind.INVALID_FILE = CursorKind(70) +CursorKind.NO_DECL_FOUND = CursorKind(71) +CursorKind.NOT_IMPLEMENTED = CursorKind(72) +CursorKind.INVALID_CODE = CursorKind(73) + +### +# Expression Kinds + +# An expression whose specific kind is not exposed via this interface. +# +# Unexposed expressions have the same operations as any other kind of +# expression; one can extract their location information, spelling, children, +# etc. However, the specific kind of the expression is not reported. +CursorKind.UNEXPOSED_EXPR = CursorKind(100) + +# An expression that refers to some value declaration, such as a function, +# varible, or enumerator. +CursorKind.DECL_REF_EXPR = CursorKind(101) + +# An expression that refers to a member of a struct, union, class, Objective-C +# class, etc. +CursorKind.MEMBER_REF_EXPR = CursorKind(102) + +# An expression that calls a function. +CursorKind.CALL_EXPR = CursorKind(103) + +# An expression that sends a message to an Objective-C object or class. +CursorKind.OBJC_MESSAGE_EXPR = CursorKind(104) + +# An expression that represents a block literal. +CursorKind.BLOCK_EXPR = CursorKind(105) + +# An integer literal. +CursorKind.INTEGER_LITERAL = CursorKind(106) + +# A floating point number literal. +CursorKind.FLOATING_LITERAL = CursorKind(107) + +# An imaginary number literal. +CursorKind.IMAGINARY_LITERAL = CursorKind(108) + +# A string literal. +CursorKind.STRING_LITERAL = CursorKind(109) + +# A character literal. +CursorKind.CHARACTER_LITERAL = CursorKind(110) + +# A parenthesized expression, e.g. "(1)". +# +# This AST node is only formed if full location information is requested. +CursorKind.PAREN_EXPR = CursorKind(111) + +# This represents the unary-expression's (except sizeof and +# alignof). +CursorKind.UNARY_OPERATOR = CursorKind(112) + +# [C99 6.5.2.1] Array Subscripting. +CursorKind.ARRAY_SUBSCRIPT_EXPR = CursorKind(113) + +# A builtin binary operation expression such as "x + y" or +# "x <= y". +CursorKind.BINARY_OPERATOR = CursorKind(114) + +# Compound assignment such as "+=". +CursorKind.COMPOUND_ASSIGNMENT_OPERATOR = CursorKind(115) + +# The ?: ternary operator. +CursorKind.CONDITIONAL_OPERATOR = CursorKind(116) + +# An explicit cast in C (C99 6.5.4) or a C-style cast in C++ +# (C++ [expr.cast]), which uses the syntax (Type)expr. +# +# For example: (int)f. +CursorKind.CSTYLE_CAST_EXPR = CursorKind(117) + +# [C99 6.5.2.5] +CursorKind.COMPOUND_LITERAL_EXPR = CursorKind(118) + +# Describes an C or C++ initializer list. +CursorKind.INIT_LIST_EXPR = CursorKind(119) + +# The GNU address of label extension, representing &&label. +CursorKind.ADDR_LABEL_EXPR = CursorKind(120) + +# This is the GNU Statement Expression extension: ({int X=4; X;}) +CursorKind.StmtExpr = CursorKind(121) + +# Represents a C11 generic selection. +CursorKind.GENERIC_SELECTION_EXPR = CursorKind(122) + +# Implements the GNU __null extension, which is a name for a null +# pointer constant that has integral type (e.g., int or long) and is the same +# size and alignment as a pointer. +# +# The __null extension is typically only used by system headers, which define +# NULL as __null in C++ rather than using 0 (which is an integer that may not +# match the size of a pointer). +CursorKind.GNU_NULL_EXPR = CursorKind(123) + +# C++'s static_cast<> expression. +CursorKind.CXX_STATIC_CAST_EXPR = CursorKind(124) + +# C++'s dynamic_cast<> expression. +CursorKind.CXX_DYNAMIC_CAST_EXPR = CursorKind(125) + +# C++'s reinterpret_cast<> expression. +CursorKind.CXX_REINTERPRET_CAST_EXPR = CursorKind(126) + +# C++'s const_cast<> expression. +CursorKind.CXX_CONST_CAST_EXPR = CursorKind(127) + +# Represents an explicit C++ type conversion that uses "functional" +# notion (C++ [expr.type.conv]). +# +# Example: +# \code +# x = int(0.5); +# \endcode +CursorKind.CXX_FUNCTIONAL_CAST_EXPR = CursorKind(128) + +# A C++ typeid expression (C++ [expr.typeid]). +CursorKind.CXX_TYPEID_EXPR = CursorKind(129) + +# [C++ 2.13.5] C++ Boolean Literal. +CursorKind.CXX_BOOL_LITERAL_EXPR = CursorKind(130) + +# [C++0x 2.14.7] C++ Pointer Literal. +CursorKind.CXX_NULL_PTR_LITERAL_EXPR = CursorKind(131) + +# Represents the "this" expression in C++ +CursorKind.CXX_THIS_EXPR = CursorKind(132) + +# [C++ 15] C++ Throw Expression. +# +# This handles 'throw' and 'throw' assignment-expression. When +# assignment-expression isn't present, Op will be null. +CursorKind.CXX_THROW_EXPR = CursorKind(133) + +# A new expression for memory allocation and constructor calls, e.g: +# "new CXXNewExpr(foo)". +CursorKind.CXX_NEW_EXPR = CursorKind(134) + +# A delete expression for memory deallocation and destructor calls, +# e.g. "delete[] pArray". +CursorKind.CXX_DELETE_EXPR = CursorKind(135) + +# Represents a unary expression. +CursorKind.CXX_UNARY_EXPR = CursorKind(136) + +# ObjCStringLiteral, used for Objective-C string literals i.e. "foo". +CursorKind.OBJC_STRING_LITERAL = CursorKind(137) + +# ObjCEncodeExpr, used for in Objective-C. +CursorKind.OBJC_ENCODE_EXPR = CursorKind(138) + +# ObjCSelectorExpr used for in Objective-C. +CursorKind.OBJC_SELECTOR_EXPR = CursorKind(139) + +# Objective-C's protocol expression. +CursorKind.OBJC_PROTOCOL_EXPR = CursorKind(140) + +# An Objective-C "bridged" cast expression, which casts between +# Objective-C pointers and C pointers, transferring ownership in the process. +# +# \code +# NSString *str = (__bridge_transfer NSString *)CFCreateString(); +# \endcode +CursorKind.OBJC_BRIDGE_CAST_EXPR = CursorKind(141) + +# Represents a C++0x pack expansion that produces a sequence of +# expressions. +# +# A pack expansion expression contains a pattern (which itself is an +# expression) followed by an ellipsis. For example: +CursorKind.PACK_EXPANSION_EXPR = CursorKind(142) + +# Represents an expression that computes the length of a parameter +# pack. +CursorKind.SIZE_OF_PACK_EXPR = CursorKind(143) + +# Represents a C++ lambda expression that produces a local function +# object. +# +# \code +# void abssort(float *x, unsigned N) { +# std::sort(x, x + N, +# [](float a, float b) { +# return std::abs(a) < std::abs(b); +# }); +# } +# \endcode +CursorKind.LAMBDA_EXPR = CursorKind(144) + +# Objective-c Boolean Literal. +CursorKind.OBJ_BOOL_LITERAL_EXPR = CursorKind(145) + +# Represents the "self" expression in a ObjC method. +CursorKind.OBJ_SELF_EXPR = CursorKind(146) + + +# A statement whose specific kind is not exposed via this interface. +# +# Unexposed statements have the same operations as any other kind of statement; +# one can extract their location information, spelling, children, etc. However, +# the specific kind of the statement is not reported. +CursorKind.UNEXPOSED_STMT = CursorKind(200) + +# A labelled statement in a function. +CursorKind.LABEL_STMT = CursorKind(201) + +# A compound statement +CursorKind.COMPOUND_STMT = CursorKind(202) + +# A case statement. +CursorKind.CASE_STMT = CursorKind(203) + +# A default statement. +CursorKind.DEFAULT_STMT = CursorKind(204) + +# An if statement. +CursorKind.IF_STMT = CursorKind(205) + +# A switch statement. +CursorKind.SWITCH_STMT = CursorKind(206) + +# A while statement. +CursorKind.WHILE_STMT = CursorKind(207) + +# A do statement. +CursorKind.DO_STMT = CursorKind(208) + +# A for statement. +CursorKind.FOR_STMT = CursorKind(209) + +# A goto statement. +CursorKind.GOTO_STMT = CursorKind(210) + +# An indirect goto statement. +CursorKind.INDIRECT_GOTO_STMT = CursorKind(211) + +# A continue statement. +CursorKind.CONTINUE_STMT = CursorKind(212) + +# A break statement. +CursorKind.BREAK_STMT = CursorKind(213) + +# A return statement. +CursorKind.RETURN_STMT = CursorKind(214) + +# A GNU-style inline assembler statement. +CursorKind.ASM_STMT = CursorKind(215) + +# Objective-C's overall @try-@catch-@finally statement. +CursorKind.OBJC_AT_TRY_STMT = CursorKind(216) + +# Objective-C's @catch statement. +CursorKind.OBJC_AT_CATCH_STMT = CursorKind(217) + +# Objective-C's @finally statement. +CursorKind.OBJC_AT_FINALLY_STMT = CursorKind(218) + +# Objective-C's @throw statement. +CursorKind.OBJC_AT_THROW_STMT = CursorKind(219) + +# Objective-C's @synchronized statement. +CursorKind.OBJC_AT_SYNCHRONIZED_STMT = CursorKind(220) + +# Objective-C's autorealease pool statement. +CursorKind.OBJC_AUTORELEASE_POOL_STMT = CursorKind(221) + +# Objective-C's for collection statement. +CursorKind.OBJC_FOR_COLLECTION_STMT = CursorKind(222) + +# C++'s catch statement. +CursorKind.CXX_CATCH_STMT = CursorKind(223) + +# C++'s try statement. +CursorKind.CXX_TRY_STMT = CursorKind(224) + +# C++'s for (* : *) statement. +CursorKind.CXX_FOR_RANGE_STMT = CursorKind(225) + +# Windows Structured Exception Handling's try statement. +CursorKind.SEH_TRY_STMT = CursorKind(226) + +# Windows Structured Exception Handling's except statement. +CursorKind.SEH_EXCEPT_STMT = CursorKind(227) + +# Windows Structured Exception Handling's finally statement. +CursorKind.SEH_FINALLY_STMT = CursorKind(228) + +# A MS inline assembly statement extension. +CursorKind.MS_ASM_STMT = CursorKind(229) + +# The null statement. +CursorKind.NULL_STMT = CursorKind(230) + +# Adaptor class for mixing declarations with statements and expressions. +CursorKind.DECL_STMT = CursorKind(231) + +# OpenMP parallel directive. +CursorKind.OMP_PARALLEL_DIRECTIVE = CursorKind(232) + +# OpenMP SIMD directive. +CursorKind.OMP_SIMD_DIRECTIVE = CursorKind(233) + +# OpenMP for directive. +CursorKind.OMP_FOR_DIRECTIVE = CursorKind(234) + +# OpenMP sections directive. +CursorKind.OMP_SECTIONS_DIRECTIVE = CursorKind(235) + +# OpenMP section directive. +CursorKind.OMP_SECTION_DIRECTIVE = CursorKind(236) + +# OpenMP single directive. +CursorKind.OMP_SINGLE_DIRECTIVE = CursorKind(237) + +# OpenMP parallel for directive. +CursorKind.OMP_PARALLEL_FOR_DIRECTIVE = CursorKind(238) + +# OpenMP parallel sections directive. +CursorKind.OMP_PARALLEL_SECTIONS_DIRECTIVE = CursorKind(239) + +# OpenMP task directive. +CursorKind.OMP_TASK_DIRECTIVE = CursorKind(240) + +# OpenMP master directive. +CursorKind.OMP_MASTER_DIRECTIVE = CursorKind(241) + +# OpenMP critical directive. +CursorKind.OMP_CRITICAL_DIRECTIVE = CursorKind(242) + +# OpenMP taskyield directive. +CursorKind.OMP_TASKYIELD_DIRECTIVE = CursorKind(243) + +# OpenMP barrier directive. +CursorKind.OMP_BARRIER_DIRECTIVE = CursorKind(244) + +# OpenMP taskwait directive. +CursorKind.OMP_TASKWAIT_DIRECTIVE = CursorKind(245) + +# OpenMP flush directive. +CursorKind.OMP_FLUSH_DIRECTIVE = CursorKind(246) + +# Windows Structured Exception Handling's leave statement. +CursorKind.SEH_LEAVE_STMT = CursorKind(247) + +# OpenMP ordered directive. +CursorKind.OMP_ORDERED_DIRECTIVE = CursorKind(248) + +# OpenMP atomic directive. +CursorKind.OMP_ATOMIC_DIRECTIVE = CursorKind(249) + +# OpenMP for SIMD directive. +CursorKind.OMP_FOR_SIMD_DIRECTIVE = CursorKind(250) + +# OpenMP parallel for SIMD directive. +CursorKind.OMP_PARALLELFORSIMD_DIRECTIVE = CursorKind(251) + +# OpenMP target directive. +CursorKind.OMP_TARGET_DIRECTIVE = CursorKind(252) + +# OpenMP teams directive. +CursorKind.OMP_TEAMS_DIRECTIVE = CursorKind(253) + +# OpenMP taskgroup directive. +CursorKind.OMP_TASKGROUP_DIRECTIVE = CursorKind(254) + +# OpenMP cancellation point directive. +CursorKind.OMP_CANCELLATION_POINT_DIRECTIVE = CursorKind(255) + +# OpenMP cancel directive. +CursorKind.OMP_CANCEL_DIRECTIVE = CursorKind(256) + +# OpenMP target data directive. +CursorKind.OMP_TARGET_DATA_DIRECTIVE = CursorKind(257) + +# OpenMP taskloop directive. +CursorKind.OMP_TASK_LOOP_DIRECTIVE = CursorKind(258) + +# OpenMP taskloop simd directive. +CursorKind.OMP_TASK_LOOP_SIMD_DIRECTIVE = CursorKind(259) + +# OpenMP distribute directive. +CursorKind.OMP_DISTRIBUTE_DIRECTIVE = CursorKind(260) + +# OpenMP target enter data directive. +CursorKind.OMP_TARGET_ENTER_DATA_DIRECTIVE = CursorKind(261) + +# OpenMP target exit data directive. +CursorKind.OMP_TARGET_EXIT_DATA_DIRECTIVE = CursorKind(262) + +# OpenMP target parallel directive. +CursorKind.OMP_TARGET_PARALLEL_DIRECTIVE = CursorKind(263) + +# OpenMP target parallel for directive. +CursorKind.OMP_TARGET_PARALLELFOR_DIRECTIVE = CursorKind(264) + +# OpenMP target update directive. +CursorKind.OMP_TARGET_UPDATE_DIRECTIVE = CursorKind(265) + +# OpenMP distribute parallel for directive. +CursorKind.OMP_DISTRIBUTE_PARALLELFOR_DIRECTIVE = CursorKind(266) + +# OpenMP distribute parallel for simd directive. +CursorKind.OMP_DISTRIBUTE_PARALLEL_FOR_SIMD_DIRECTIVE = CursorKind(267) + +# OpenMP distribute simd directive. +CursorKind.OMP_DISTRIBUTE_SIMD_DIRECTIVE = CursorKind(268) + +# OpenMP target parallel for simd directive. +CursorKind.OMP_TARGET_PARALLEL_FOR_SIMD_DIRECTIVE = CursorKind(269) + +# OpenMP target simd directive. +CursorKind.OMP_TARGET_SIMD_DIRECTIVE = CursorKind(270) + +# OpenMP teams distribute directive. +CursorKind.OMP_TEAMS_DISTRIBUTE_DIRECTIVE = CursorKind(271) + +### +# Other Kinds + +# Cursor that represents the translation unit itself. +# +# The translation unit cursor exists primarily to act as the root cursor for +# traversing the contents of a translation unit. +CursorKind.TRANSLATION_UNIT = CursorKind(300) + +### +# Attributes + +# An attribute whoe specific kind is note exposed via this interface +CursorKind.UNEXPOSED_ATTR = CursorKind(400) + +CursorKind.IB_ACTION_ATTR = CursorKind(401) +CursorKind.IB_OUTLET_ATTR = CursorKind(402) +CursorKind.IB_OUTLET_COLLECTION_ATTR = CursorKind(403) + +CursorKind.CXX_FINAL_ATTR = CursorKind(404) +CursorKind.CXX_OVERRIDE_ATTR = CursorKind(405) +CursorKind.ANNOTATE_ATTR = CursorKind(406) +CursorKind.ASM_LABEL_ATTR = CursorKind(407) +CursorKind.PACKED_ATTR = CursorKind(408) +CursorKind.PURE_ATTR = CursorKind(409) +CursorKind.CONST_ATTR = CursorKind(410) +CursorKind.NODUPLICATE_ATTR = CursorKind(411) +CursorKind.CUDACONSTANT_ATTR = CursorKind(412) +CursorKind.CUDADEVICE_ATTR = CursorKind(413) +CursorKind.CUDAGLOBAL_ATTR = CursorKind(414) +CursorKind.CUDAHOST_ATTR = CursorKind(415) +CursorKind.CUDASHARED_ATTR = CursorKind(416) + +CursorKind.VISIBILITY_ATTR = CursorKind(417) + +CursorKind.DLLEXPORT_ATTR = CursorKind(418) +CursorKind.DLLIMPORT_ATTR = CursorKind(419) + +### +# Preprocessing +CursorKind.PREPROCESSING_DIRECTIVE = CursorKind(500) +CursorKind.MACRO_DEFINITION = CursorKind(501) +CursorKind.MACRO_INSTANTIATION = CursorKind(502) +CursorKind.INCLUSION_DIRECTIVE = CursorKind(503) + +### +# Extra declaration + +# A module import declaration. +CursorKind.MODULE_IMPORT_DECL = CursorKind(600) +# A type alias template declaration +CursorKind.TYPE_ALIAS_TEMPLATE_DECL = CursorKind(601) +# A static_assert or _Static_assert node +CursorKind.STATIC_ASSERT = CursorKind(602) +# A friend declaration +CursorKind.FRIEND_DECL = CursorKind(603) + +# A code completion overload candidate. +CursorKind.OVERLOAD_CANDIDATE = CursorKind(700) + +### Template Argument Kinds ### +class TemplateArgumentKind(BaseEnumeration): + """ + A TemplateArgumentKind describes the kind of entity that a template argument + represents. + """ + + # The required BaseEnumeration declarations. + _kinds = [] + _name_map = None + +TemplateArgumentKind.NULL = TemplateArgumentKind(0) +TemplateArgumentKind.TYPE = TemplateArgumentKind(1) +TemplateArgumentKind.DECLARATION = TemplateArgumentKind(2) +TemplateArgumentKind.NULLPTR = TemplateArgumentKind(3) +TemplateArgumentKind.INTEGRAL = TemplateArgumentKind(4) + +### Cursors ### + +class Cursor(Structure): + """ + The Cursor class represents a reference to an element within the AST. It + acts as a kind of iterator. + """ + _fields_ = [("_kind_id", c_int), ("xdata", c_int), ("data", c_void_p * 3)] + + @staticmethod + def from_location(tu, location): + # We store a reference to the TU in the instance so the TU won't get + # collected before the cursor. + cursor = conf.lib.clang_getCursor(tu, location) + cursor._tu = tu + + return cursor + + def __eq__(self, other): + return conf.lib.clang_equalCursors(self, other) + + def __ne__(self, other): + return not self.__eq__(other) + + def is_definition(self): + """ + Returns true if the declaration pointed at by the cursor is also a + definition of that entity. + """ + return conf.lib.clang_isCursorDefinition(self) + + def is_const_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared 'const'. + """ + return conf.lib.clang_CXXMethod_isConst(self) + + def is_converting_constructor(self): + """Returns True if the cursor refers to a C++ converting constructor. + """ + return conf.lib.clang_CXXConstructor_isConvertingConstructor(self) + + def is_copy_constructor(self): + """Returns True if the cursor refers to a C++ copy constructor. + """ + return conf.lib.clang_CXXConstructor_isCopyConstructor(self) + + def is_default_constructor(self): + """Returns True if the cursor refers to a C++ default constructor. + """ + return conf.lib.clang_CXXConstructor_isDefaultConstructor(self) + + def is_move_constructor(self): + """Returns True if the cursor refers to a C++ move constructor. + """ + return conf.lib.clang_CXXConstructor_isMoveConstructor(self) + + def is_default_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared '= default'. + """ + return conf.lib.clang_CXXMethod_isDefaulted(self) + + def is_mutable_field(self): + """Returns True if the cursor refers to a C++ field that is declared + 'mutable'. + """ + return conf.lib.clang_CXXField_isMutable(self) + + def is_pure_virtual_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared pure virtual. + """ + return conf.lib.clang_CXXMethod_isPureVirtual(self) + + def is_static_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared 'static'. + """ + return conf.lib.clang_CXXMethod_isStatic(self) + + def is_virtual_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared 'virtual'. + """ + return conf.lib.clang_CXXMethod_isVirtual(self) + + def get_definition(self): + """ + If the cursor is a reference to a declaration or a declaration of + some entity, return a cursor that points to the definition of that + entity. + """ + # TODO: Should probably check that this is either a reference or + # declaration prior to issuing the lookup. + return conf.lib.clang_getCursorDefinition(self) + + def get_usr(self): + """Return the Unified Symbol Resultion (USR) for the entity referenced + by the given cursor (or None). + + A Unified Symbol Resolution (USR) is a string that identifies a + particular entity (function, class, variable, etc.) within a + program. USRs can be compared across translation units to determine, + e.g., when references in one translation refer to an entity defined in + another translation unit.""" + return conf.lib.clang_getCursorUSR(self) + + @property + def kind(self): + """Return the kind of this cursor.""" + return CursorKind.from_id(self._kind_id) + + @property + def spelling(self): + """Return the spelling of the entity pointed at by the cursor.""" + if not hasattr(self, '_spelling'): + self._spelling = conf.lib.clang_getCursorSpelling(self) + + return self._spelling + + @property + def displayname(self): + """ + Return the display name for the entity referenced by this cursor. + + The display name contains extra information that helps identify the + cursor, such as the parameters of a function or template or the + arguments of a class template specialization. + """ + if not hasattr(self, '_displayname'): + self._displayname = conf.lib.clang_getCursorDisplayName(self) + + return self._displayname + + @property + def mangled_name(self): + """Return the mangled name for the entity referenced by this cursor.""" + if not hasattr(self, '_mangled_name'): + self._mangled_name = conf.lib.clang_Cursor_getMangling(self) + + return self._mangled_name + + @property + def location(self): + """ + Return the source location (the starting character) of the entity + pointed at by the cursor. + """ + if not hasattr(self, '_loc'): + self._loc = conf.lib.clang_getCursorLocation(self) + + return self._loc + + @property + def extent(self): + """ + Return the source range (the range of text) occupied by the entity + pointed at by the cursor. + """ + if not hasattr(self, '_extent'): + self._extent = conf.lib.clang_getCursorExtent(self) + + return self._extent + + @property + def storage_class(self): + """ + Retrieves the storage class (if any) of the entity pointed at by the + cursor. + """ + if not hasattr(self, '_storage_class'): + self._storage_class = conf.lib.clang_Cursor_getStorageClass(self) + + return StorageClass.from_id(self._storage_class) + + @property + def access_specifier(self): + """ + Retrieves the access specifier (if any) of the entity pointed at by the + cursor. + """ + if not hasattr(self, '_access_specifier'): + self._access_specifier = conf.lib.clang_getCXXAccessSpecifier(self) + + return AccessSpecifier.from_id(self._access_specifier) + + @property + def type(self): + """ + Retrieve the Type (if any) of the entity pointed at by the cursor. + """ + if not hasattr(self, '_type'): + self._type = conf.lib.clang_getCursorType(self) + + return self._type + + @property + def canonical(self): + """Return the canonical Cursor corresponding to this Cursor. + + The canonical cursor is the cursor which is representative for the + underlying entity. For example, if you have multiple forward + declarations for the same class, the canonical cursor for the forward + declarations will be identical. + """ + if not hasattr(self, '_canonical'): + self._canonical = conf.lib.clang_getCanonicalCursor(self) + + return self._canonical + + @property + def result_type(self): + """Retrieve the Type of the result for this Cursor.""" + if not hasattr(self, '_result_type'): + self._result_type = conf.lib.clang_getResultType(self.type) + + return self._result_type + + @property + def underlying_typedef_type(self): + """Return the underlying type of a typedef declaration. + + Returns a Type for the typedef this cursor is a declaration for. If + the current cursor is not a typedef, this raises. + """ + if not hasattr(self, '_underlying_type'): + assert self.kind.is_declaration() + self._underlying_type = \ + conf.lib.clang_getTypedefDeclUnderlyingType(self) + + return self._underlying_type + + @property + def enum_type(self): + """Return the integer type of an enum declaration. + + Returns a Type corresponding to an integer. If the cursor is not for an + enum, this raises. + """ + if not hasattr(self, '_enum_type'): + assert self.kind == CursorKind.ENUM_DECL + self._enum_type = conf.lib.clang_getEnumDeclIntegerType(self) + + return self._enum_type + + @property + def enum_value(self): + """Return the value of an enum constant.""" + if not hasattr(self, '_enum_value'): + assert self.kind == CursorKind.ENUM_CONSTANT_DECL + # Figure out the underlying type of the enum to know if it + # is a signed or unsigned quantity. + underlying_type = self.type + if underlying_type.kind == TypeKind.ENUM: + underlying_type = underlying_type.get_declaration().enum_type + if underlying_type.kind in (TypeKind.CHAR_U, + TypeKind.UCHAR, + TypeKind.CHAR16, + TypeKind.CHAR32, + TypeKind.USHORT, + TypeKind.UINT, + TypeKind.ULONG, + TypeKind.ULONGLONG, + TypeKind.UINT128): + self._enum_value = \ + conf.lib.clang_getEnumConstantDeclUnsignedValue(self) + else: + self._enum_value = conf.lib.clang_getEnumConstantDeclValue(self) + return self._enum_value + + @property + def objc_type_encoding(self): + """Return the Objective-C type encoding as a str.""" + if not hasattr(self, '_objc_type_encoding'): + self._objc_type_encoding = \ + conf.lib.clang_getDeclObjCTypeEncoding(self) + + return self._objc_type_encoding + + @property + def hash(self): + """Returns a hash of the cursor as an int.""" + if not hasattr(self, '_hash'): + self._hash = conf.lib.clang_hashCursor(self) + + return self._hash + + @property + def semantic_parent(self): + """Return the semantic parent for this cursor.""" + if not hasattr(self, '_semantic_parent'): + self._semantic_parent = conf.lib.clang_getCursorSemanticParent(self) + + return self._semantic_parent + + @property + def lexical_parent(self): + """Return the lexical parent for this cursor.""" + if not hasattr(self, '_lexical_parent'): + self._lexical_parent = conf.lib.clang_getCursorLexicalParent(self) + + return self._lexical_parent + + @property + def translation_unit(self): + """Returns the TranslationUnit to which this Cursor belongs.""" + # If this triggers an AttributeError, the instance was not properly + # created. + return self._tu + + @property + def referenced(self): + """ + For a cursor that is a reference, returns a cursor + representing the entity that it references. + """ + if not hasattr(self, '_referenced'): + self._referenced = conf.lib.clang_getCursorReferenced(self) + + return self._referenced + + @property + def brief_comment(self): + """Returns the brief comment text associated with that Cursor""" + return conf.lib.clang_Cursor_getBriefCommentText(self) + + @property + def raw_comment(self): + """Returns the raw comment text associated with that Cursor""" + return conf.lib.clang_Cursor_getRawCommentText(self) + + def get_arguments(self): + """Return an iterator for accessing the arguments of this cursor.""" + num_args = conf.lib.clang_Cursor_getNumArguments(self) + for i in range(0, num_args): + yield conf.lib.clang_Cursor_getArgument(self, i) + + def get_num_template_arguments(self): + """Returns the number of template args associated with this cursor.""" + return conf.lib.clang_Cursor_getNumTemplateArguments(self) + + def get_template_argument_kind(self, num): + """Returns the TemplateArgumentKind for the indicated template + argument.""" + return conf.lib.clang_Cursor_getTemplateArgumentKind(self, num) + + def get_template_argument_type(self, num): + """Returns the CXType for the indicated template argument.""" + return conf.lib.clang_Cursor_getTemplateArgumentType(self, num) + + def get_template_argument_value(self, num): + """Returns the value of the indicated arg as a signed 64b integer.""" + return conf.lib.clang_Cursor_getTemplateArgumentValue(self, num) + + def get_template_argument_unsigned_value(self, num): + """Returns the value of the indicated arg as an unsigned 64b integer.""" + return conf.lib.clang_Cursor_getTemplateArgumentUnsignedValue(self, num) + + def get_children(self): + """Return an iterator for accessing the children of this cursor.""" + + # FIXME: Expose iteration from CIndex, PR6125. + def visitor(child, parent, children): + # FIXME: Document this assertion in API. + # FIXME: There should just be an isNull method. + assert child != conf.lib.clang_getNullCursor() + + # Create reference to TU so it isn't GC'd before Cursor. + child._tu = self._tu + children.append(child) + return 1 # continue + children = [] + conf.lib.clang_visitChildren(self, callbacks['cursor_visit'](visitor), + children) + return iter(children) + + def walk_preorder(self): + """Depth-first preorder walk over the cursor and its descendants. + + Yields cursors. + """ + yield self + for child in self.get_children(): + for descendant in child.walk_preorder(): + yield descendant + + def get_tokens(self): + """Obtain Token instances formulating that compose this Cursor. + + This is a generator for Token instances. It returns all tokens which + occupy the extent this cursor occupies. + """ + return TokenGroup.get_tokens(self._tu, self.extent) + + def get_field_offsetof(self): + """Returns the offsetof the FIELD_DECL pointed by this Cursor.""" + return conf.lib.clang_Cursor_getOffsetOfField(self) + + def is_anonymous(self): + """ + Check if the record is anonymous. + """ + if self.kind == CursorKind.FIELD_DECL: + return self.type.get_declaration().is_anonymous() + return conf.lib.clang_Cursor_isAnonymous(self) + + def is_bitfield(self): + """ + Check if the field is a bitfield. + """ + return conf.lib.clang_Cursor_isBitField(self) + + def get_bitfield_width(self): + """ + Retrieve the width of a bitfield. + """ + return conf.lib.clang_getFieldDeclBitWidth(self) + + @staticmethod + def from_result(res, fn, args): + assert isinstance(res, Cursor) + # FIXME: There should just be an isNull method. + if res == conf.lib.clang_getNullCursor(): + return None + + # Store a reference to the TU in the Python object so it won't get GC'd + # before the Cursor. + tu = None + for arg in args: + if isinstance(arg, TranslationUnit): + tu = arg + break + + if hasattr(arg, 'translation_unit'): + tu = arg.translation_unit + break + + assert tu is not None + + res._tu = tu + return res + + @staticmethod + def from_cursor_result(res, fn, args): + assert isinstance(res, Cursor) + if res == conf.lib.clang_getNullCursor(): + return None + + res._tu = args[0]._tu + return res + +class StorageClass(object): + """ + Describes the storage class of a declaration + """ + + # The unique kind objects, index by id. + _kinds = [] + _name_map = None + + def __init__(self, value): + if value >= len(StorageClass._kinds): + StorageClass._kinds += [None] * (value - len(StorageClass._kinds) + 1) + if StorageClass._kinds[value] is not None: + raise ValueError('StorageClass already loaded') + self.value = value + StorageClass._kinds[value] = self + StorageClass._name_map = None + + def from_param(self): + return self.value + + @property + def name(self): + """Get the enumeration name of this storage class.""" + if self._name_map is None: + self._name_map = {} + for key,value in list(StorageClass.__dict__.items()): + if isinstance(value,StorageClass): + self._name_map[value] = key + return self._name_map[self] + + @staticmethod + def from_id(id): + if id >= len(StorageClass._kinds) or not StorageClass._kinds[id]: + raise ValueError('Unknown storage class %d' % id) + return StorageClass._kinds[id] + + def __repr__(self): + return 'StorageClass.%s' % (self.name,) + +StorageClass.INVALID = StorageClass(0) +StorageClass.NONE = StorageClass(1) +StorageClass.EXTERN = StorageClass(2) +StorageClass.STATIC = StorageClass(3) +StorageClass.PRIVATEEXTERN = StorageClass(4) +StorageClass.OPENCLWORKGROUPLOCAL = StorageClass(5) +StorageClass.AUTO = StorageClass(6) +StorageClass.REGISTER = StorageClass(7) + + +### C++ access specifiers ### + +class AccessSpecifier(BaseEnumeration): + """ + Describes the access of a C++ class member + """ + + # The unique kind objects, index by id. + _kinds = [] + _name_map = None + + def from_param(self): + return self.value + + def __repr__(self): + return 'AccessSpecifier.%s' % (self.name,) + +AccessSpecifier.INVALID = AccessSpecifier(0) +AccessSpecifier.PUBLIC = AccessSpecifier(1) +AccessSpecifier.PROTECTED = AccessSpecifier(2) +AccessSpecifier.PRIVATE = AccessSpecifier(3) +AccessSpecifier.NONE = AccessSpecifier(4) + +### Type Kinds ### + +class TypeKind(BaseEnumeration): + """ + Describes the kind of type. + """ + + # The unique kind objects, indexed by id. + _kinds = [] + _name_map = None + + @property + def spelling(self): + """Retrieve the spelling of this TypeKind.""" + return conf.lib.clang_getTypeKindSpelling(self.value) + + def __repr__(self): + return 'TypeKind.%s' % (self.name,) + +TypeKind.INVALID = TypeKind(0) +TypeKind.UNEXPOSED = TypeKind(1) +TypeKind.VOID = TypeKind(2) +TypeKind.BOOL = TypeKind(3) +TypeKind.CHAR_U = TypeKind(4) +TypeKind.UCHAR = TypeKind(5) +TypeKind.CHAR16 = TypeKind(6) +TypeKind.CHAR32 = TypeKind(7) +TypeKind.USHORT = TypeKind(8) +TypeKind.UINT = TypeKind(9) +TypeKind.ULONG = TypeKind(10) +TypeKind.ULONGLONG = TypeKind(11) +TypeKind.UINT128 = TypeKind(12) +TypeKind.CHAR_S = TypeKind(13) +TypeKind.SCHAR = TypeKind(14) +TypeKind.WCHAR = TypeKind(15) +TypeKind.SHORT = TypeKind(16) +TypeKind.INT = TypeKind(17) +TypeKind.LONG = TypeKind(18) +TypeKind.LONGLONG = TypeKind(19) +TypeKind.INT128 = TypeKind(20) +TypeKind.FLOAT = TypeKind(21) +TypeKind.DOUBLE = TypeKind(22) +TypeKind.LONGDOUBLE = TypeKind(23) +TypeKind.NULLPTR = TypeKind(24) +TypeKind.OVERLOAD = TypeKind(25) +TypeKind.DEPENDENT = TypeKind(26) +TypeKind.OBJCID = TypeKind(27) +TypeKind.OBJCCLASS = TypeKind(28) +TypeKind.OBJCSEL = TypeKind(29) +TypeKind.FLOAT128 = TypeKind(30) +TypeKind.HALF = TypeKind(31) +TypeKind.COMPLEX = TypeKind(100) +TypeKind.POINTER = TypeKind(101) +TypeKind.BLOCKPOINTER = TypeKind(102) +TypeKind.LVALUEREFERENCE = TypeKind(103) +TypeKind.RVALUEREFERENCE = TypeKind(104) +TypeKind.RECORD = TypeKind(105) +TypeKind.ENUM = TypeKind(106) +TypeKind.TYPEDEF = TypeKind(107) +TypeKind.OBJCINTERFACE = TypeKind(108) +TypeKind.OBJCOBJECTPOINTER = TypeKind(109) +TypeKind.FUNCTIONNOPROTO = TypeKind(110) +TypeKind.FUNCTIONPROTO = TypeKind(111) +TypeKind.CONSTANTARRAY = TypeKind(112) +TypeKind.VECTOR = TypeKind(113) +TypeKind.INCOMPLETEARRAY = TypeKind(114) +TypeKind.VARIABLEARRAY = TypeKind(115) +TypeKind.DEPENDENTSIZEDARRAY = TypeKind(116) +TypeKind.MEMBERPOINTER = TypeKind(117) +TypeKind.AUTO = TypeKind(118) +TypeKind.ELABORATED = TypeKind(119) + +class RefQualifierKind(BaseEnumeration): + """Describes a specific ref-qualifier of a type.""" + + # The unique kind objects, indexed by id. + _kinds = [] + _name_map = None + + def from_param(self): + return self.value + + def __repr__(self): + return 'RefQualifierKind.%s' % (self.name,) + +RefQualifierKind.NONE = RefQualifierKind(0) +RefQualifierKind.LVALUE = RefQualifierKind(1) +RefQualifierKind.RVALUE = RefQualifierKind(2) + +class Type(Structure): + """ + The type of an element in the abstract syntax tree. + """ + _fields_ = [("_kind_id", c_int), ("data", c_void_p * 2)] + + @property + def kind(self): + """Return the kind of this type.""" + return TypeKind.from_id(self._kind_id) + + def argument_types(self): + """Retrieve a container for the non-variadic arguments for this type. + + The returned object is iterable and indexable. Each item in the + container is a Type instance. + """ + class ArgumentsIterator(collections.Sequence): + def __init__(self, parent): + self.parent = parent + self.length = None + + def __len__(self): + if self.length is None: + self.length = conf.lib.clang_getNumArgTypes(self.parent) + + return self.length + + def __getitem__(self, key): + # FIXME Support slice objects. + if not isinstance(key, int): + raise TypeError("Must supply a non-negative int.") + + if key < 0: + raise IndexError("Only non-negative indexes are accepted.") + + if key >= len(self): + raise IndexError("Index greater than container length: " + "%d > %d" % ( key, len(self) )) + + result = conf.lib.clang_getArgType(self.parent, key) + if result.kind == TypeKind.INVALID: + raise IndexError("Argument could not be retrieved.") + + return result + + assert self.kind == TypeKind.FUNCTIONPROTO + return ArgumentsIterator(self) + + @property + def element_type(self): + """Retrieve the Type of elements within this Type. + + If accessed on a type that is not an array, complex, or vector type, an + exception will be raised. + """ + result = conf.lib.clang_getElementType(self) + if result.kind == TypeKind.INVALID: + raise Exception('Element type not available on this type.') + + return result + + @property + def element_count(self): + """Retrieve the number of elements in this type. + + Returns an int. + + If the Type is not an array or vector, this raises. + """ + result = conf.lib.clang_getNumElements(self) + if result < 0: + raise Exception('Type does not have elements.') + + return result + + @property + def translation_unit(self): + """The TranslationUnit to which this Type is associated.""" + # If this triggers an AttributeError, the instance was not properly + # instantiated. + return self._tu + + @staticmethod + def from_result(res, fn, args): + assert isinstance(res, Type) + + tu = None + for arg in args: + if hasattr(arg, 'translation_unit'): + tu = arg.translation_unit + break + + assert tu is not None + res._tu = tu + + return res + + def get_canonical(self): + """ + Return the canonical type for a Type. + + Clang's type system explicitly models typedefs and all the + ways a specific type can be represented. The canonical type + is the underlying type with all the "sugar" removed. For + example, if 'T' is a typedef for 'int', the canonical type for + 'T' would be 'int'. + """ + return conf.lib.clang_getCanonicalType(self) + + def is_const_qualified(self): + """Determine whether a Type has the "const" qualifier set. + + This does not look through typedefs that may have added "const" + at a different level. + """ + return conf.lib.clang_isConstQualifiedType(self) + + def is_volatile_qualified(self): + """Determine whether a Type has the "volatile" qualifier set. + + This does not look through typedefs that may have added "volatile" + at a different level. + """ + return conf.lib.clang_isVolatileQualifiedType(self) + + def is_restrict_qualified(self): + """Determine whether a Type has the "restrict" qualifier set. + + This does not look through typedefs that may have added "restrict" at + a different level. + """ + return conf.lib.clang_isRestrictQualifiedType(self) + + def is_function_variadic(self): + """Determine whether this function Type is a variadic function type.""" + assert self.kind == TypeKind.FUNCTIONPROTO + + return conf.lib.clang_isFunctionTypeVariadic(self) + + def is_pod(self): + """Determine whether this Type represents plain old data (POD).""" + return conf.lib.clang_isPODType(self) + + def get_pointee(self): + """ + For pointer types, returns the type of the pointee. + """ + return conf.lib.clang_getPointeeType(self) + + def get_declaration(self): + """ + Return the cursor for the declaration of the given type. + """ + return conf.lib.clang_getTypeDeclaration(self) + + def get_result(self): + """ + Retrieve the result type associated with a function type. + """ + return conf.lib.clang_getResultType(self) + + def get_array_element_type(self): + """ + Retrieve the type of the elements of the array type. + """ + return conf.lib.clang_getArrayElementType(self) + + def get_array_size(self): + """ + Retrieve the size of the constant array. + """ + return conf.lib.clang_getArraySize(self) + + def get_class_type(self): + """ + Retrieve the class type of the member pointer type. + """ + return conf.lib.clang_Type_getClassType(self) + + def get_named_type(self): + """ + Retrieve the type named by the qualified-id. + """ + return conf.lib.clang_Type_getNamedType(self) + def get_align(self): + """ + Retrieve the alignment of the record. + """ + return conf.lib.clang_Type_getAlignOf(self) + + def get_size(self): + """ + Retrieve the size of the record. + """ + return conf.lib.clang_Type_getSizeOf(self) + + def get_offset(self, fieldname): + """ + Retrieve the offset of a field in the record. + """ + return conf.lib.clang_Type_getOffsetOf(self, c_char_p(fieldname)) + + def get_ref_qualifier(self): + """ + Retrieve the ref-qualifier of the type. + """ + return RefQualifierKind.from_id( + conf.lib.clang_Type_getCXXRefQualifier(self)) + + def get_fields(self): + """Return an iterator for accessing the fields of this type.""" + + def visitor(field, children): + assert field != conf.lib.clang_getNullCursor() + + # Create reference to TU so it isn't GC'd before Cursor. + field._tu = self._tu + fields.append(field) + return 1 # continue + fields = [] + conf.lib.clang_Type_visitFields(self, + callbacks['fields_visit'](visitor), fields) + return iter(fields) + + @property + def spelling(self): + """Retrieve the spelling of this Type.""" + return conf.lib.clang_getTypeSpelling(self) + + def __eq__(self, other): + if type(other) != type(self): + return False + + return conf.lib.clang_equalTypes(self, other) + + def __ne__(self, other): + return not self.__eq__(other) + +## CIndex Objects ## + +# CIndex objects (derived from ClangObject) are essentially lightweight +# wrappers attached to some underlying object, which is exposed via CIndex as +# a void*. + +class ClangObject(object): + """ + A helper for Clang objects. This class helps act as an intermediary for + the ctypes library and the Clang CIndex library. + """ + def __init__(self, obj): + assert isinstance(obj, c_object_p) and obj + self.obj = self._as_parameter_ = obj + + def from_param(self): + return self._as_parameter_ + + +class _CXUnsavedFile(Structure): + """Helper for passing unsaved file arguments.""" + _fields_ = [("name", c_char_p), ("contents", c_char_p), ('length', c_ulong)] + +# Functions calls through the python interface are rather slow. Fortunately, +# for most symboles, we do not need to perform a function call. Their spelling +# never changes and is consequently provided by this spelling cache. +SpellingCache = { + # 0: CompletionChunk.Kind("Optional"), + # 1: CompletionChunk.Kind("TypedText"), + # 2: CompletionChunk.Kind("Text"), + # 3: CompletionChunk.Kind("Placeholder"), + # 4: CompletionChunk.Kind("Informative"), + # 5 : CompletionChunk.Kind("CurrentParameter"), + 6: '(', # CompletionChunk.Kind("LeftParen"), + 7: ')', # CompletionChunk.Kind("RightParen"), + 8: '[', # CompletionChunk.Kind("LeftBracket"), + 9: ']', # CompletionChunk.Kind("RightBracket"), + 10: '{', # CompletionChunk.Kind("LeftBrace"), + 11: '}', # CompletionChunk.Kind("RightBrace"), + 12: '<', # CompletionChunk.Kind("LeftAngle"), + 13: '>', # CompletionChunk.Kind("RightAngle"), + 14: ', ', # CompletionChunk.Kind("Comma"), + # 15: CompletionChunk.Kind("ResultType"), + 16: ':', # CompletionChunk.Kind("Colon"), + 17: ';', # CompletionChunk.Kind("SemiColon"), + 18: '=', # CompletionChunk.Kind("Equal"), + 19: ' ', # CompletionChunk.Kind("HorizontalSpace"), + # 20: CompletionChunk.Kind("VerticalSpace") +} + +class CompletionChunk: + class Kind: + def __init__(self, name): + self.name = name + + def __str__(self): + return self.name + + def __repr__(self): + return "" % self + + def __init__(self, completionString, key): + self.cs = completionString + self.key = key + self.__kindNumberCache = -1 + + def __repr__(self): + return "{'" + self.spelling + "', " + str(self.kind) + "}" + + @CachedProperty + def spelling(self): + if self.__kindNumber in SpellingCache: + return SpellingCache[self.__kindNumber] + return conf.lib.clang_getCompletionChunkText(self.cs, self.key).spelling + + # We do not use @CachedProperty here, as the manual implementation is + # apparently still significantly faster. Please profile carefully if you + # would like to add CachedProperty back. + @property + def __kindNumber(self): + if self.__kindNumberCache == -1: + self.__kindNumberCache = \ + conf.lib.clang_getCompletionChunkKind(self.cs, self.key) + return self.__kindNumberCache + + @CachedProperty + def kind(self): + return completionChunkKindMap[self.__kindNumber] + + @CachedProperty + def string(self): + res = conf.lib.clang_getCompletionChunkCompletionString(self.cs, + self.key) + + if (res): + return CompletionString(res) + else: + None + + def isKindOptional(self): + return self.__kindNumber == 0 + + def isKindTypedText(self): + return self.__kindNumber == 1 + + def isKindPlaceHolder(self): + return self.__kindNumber == 3 + + def isKindInformative(self): + return self.__kindNumber == 4 + + def isKindResultType(self): + return self.__kindNumber == 15 + +completionChunkKindMap = { + 0: CompletionChunk.Kind("Optional"), + 1: CompletionChunk.Kind("TypedText"), + 2: CompletionChunk.Kind("Text"), + 3: CompletionChunk.Kind("Placeholder"), + 4: CompletionChunk.Kind("Informative"), + 5: CompletionChunk.Kind("CurrentParameter"), + 6: CompletionChunk.Kind("LeftParen"), + 7: CompletionChunk.Kind("RightParen"), + 8: CompletionChunk.Kind("LeftBracket"), + 9: CompletionChunk.Kind("RightBracket"), + 10: CompletionChunk.Kind("LeftBrace"), + 11: CompletionChunk.Kind("RightBrace"), + 12: CompletionChunk.Kind("LeftAngle"), + 13: CompletionChunk.Kind("RightAngle"), + 14: CompletionChunk.Kind("Comma"), + 15: CompletionChunk.Kind("ResultType"), + 16: CompletionChunk.Kind("Colon"), + 17: CompletionChunk.Kind("SemiColon"), + 18: CompletionChunk.Kind("Equal"), + 19: CompletionChunk.Kind("HorizontalSpace"), + 20: CompletionChunk.Kind("VerticalSpace")} + +class CompletionString(ClangObject): + class Availability: + def __init__(self, name): + self.name = name + + def __str__(self): + return self.name + + def __repr__(self): + return "" % self + + def __len__(self): + return self.num_chunks + + @CachedProperty + def num_chunks(self): + return conf.lib.clang_getNumCompletionChunks(self.obj) + + def __getitem__(self, key): + if self.num_chunks <= key: + raise IndexError + return CompletionChunk(self.obj, key) + + @property + def priority(self): + return conf.lib.clang_getCompletionPriority(self.obj) + + @property + def availability(self): + res = conf.lib.clang_getCompletionAvailability(self.obj) + return availabilityKinds[res] + + @property + def briefComment(self): + if conf.function_exists("clang_getCompletionBriefComment"): + return conf.lib.clang_getCompletionBriefComment(self.obj) + return _CXString() + + def __repr__(self): + return " | ".join([str(a) for a in self]) \ + + " || Priority: " + str(self.priority) \ + + " || Availability: " + str(self.availability) \ + + " || Brief comment: " + str(self.briefComment.spelling) + +availabilityKinds = { + 0: CompletionChunk.Kind("Available"), + 1: CompletionChunk.Kind("Deprecated"), + 2: CompletionChunk.Kind("NotAvailable"), + 3: CompletionChunk.Kind("NotAccessible")} + +class CodeCompletionResult(Structure): + _fields_ = [('cursorKind', c_int), ('completionString', c_object_p)] + + def __repr__(self): + return str(CompletionString(self.completionString)) + + @property + def kind(self): + return CursorKind.from_id(self.cursorKind) + + @property + def string(self): + return CompletionString(self.completionString) + +class CCRStructure(Structure): + _fields_ = [('results', POINTER(CodeCompletionResult)), + ('numResults', c_int)] + + def __len__(self): + return self.numResults + + def __getitem__(self, key): + if len(self) <= key: + raise IndexError + + return self.results[key] + +class CodeCompletionResults(ClangObject): + def __init__(self, ptr): + assert isinstance(ptr, POINTER(CCRStructure)) and ptr + self.ptr = self._as_parameter_ = ptr + + def from_param(self): + return self._as_parameter_ + + def __del__(self): + conf.lib.clang_disposeCodeCompleteResults(self) + + @property + def results(self): + return self.ptr.contents + + @property + def diagnostics(self): + class DiagnosticsItr: + def __init__(self, ccr): + self.ccr= ccr + + def __len__(self): + return int(\ + conf.lib.clang_codeCompleteGetNumDiagnostics(self.ccr)) + + def __getitem__(self, key): + return conf.lib.clang_codeCompleteGetDiagnostic(self.ccr, key) + + return DiagnosticsItr(self) + + +class Index(ClangObject): + """ + The Index type provides the primary interface to the Clang CIndex library, + primarily by providing an interface for reading and parsing translation + units. + """ + + @staticmethod + def create(excludeDecls=False): + """ + Create a new Index. + Parameters: + excludeDecls -- Exclude local declarations from translation units. + """ + return Index(conf.lib.clang_createIndex(excludeDecls, 0)) + + def __del__(self): + conf.lib.clang_disposeIndex(self) + + def read(self, path): + """Load a TranslationUnit from the given AST file.""" + return TranslationUnit.from_ast_file(path, self) + + def parse(self, path, args=None, unsaved_files=None, options = 0): + """Load the translation unit from the given source code file by running + clang and generating the AST before loading. Additional command line + parameters can be passed to clang via the args parameter. + + In-memory contents for files can be provided by passing a list of pairs + to as unsaved_files, the first item should be the filenames to be mapped + and the second should be the contents to be substituted for the + file. The contents may be passed as strings or file objects. + + If an error was encountered during parsing, a TranslationUnitLoadError + will be raised. + """ + return TranslationUnit.from_source(path, args, unsaved_files, options, + self) + +class TranslationUnit(ClangObject): + """Represents a source code translation unit. + + This is one of the main types in the API. Any time you wish to interact + with Clang's representation of a source file, you typically start with a + translation unit. + """ + + # Default parsing mode. + PARSE_NONE = 0 + + # Instruct the parser to create a detailed processing record containing + # metadata not normally retained. + PARSE_DETAILED_PROCESSING_RECORD = 1 + + # Indicates that the translation unit is incomplete. This is typically used + # when parsing headers. + PARSE_INCOMPLETE = 2 + + # Instruct the parser to create a pre-compiled preamble for the translation + # unit. This caches the preamble (included files at top of source file). + # This is useful if the translation unit will be reparsed and you don't + # want to incur the overhead of reparsing the preamble. + PARSE_PRECOMPILED_PREAMBLE = 4 + + # Cache code completion information on parse. This adds time to parsing but + # speeds up code completion. + PARSE_CACHE_COMPLETION_RESULTS = 8 + + # Flags with values 16 and 32 are deprecated and intentionally omitted. + + # Do not parse function bodies. This is useful if you only care about + # searching for declarations/definitions. + PARSE_SKIP_FUNCTION_BODIES = 64 + + # Used to indicate that brief documentation comments should be included + # into the set of code completions returned from this translation unit. + PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION = 128 + + @classmethod + def from_source(cls, filename, args=None, unsaved_files=None, options=0, + index=None): + """Create a TranslationUnit by parsing source. + + This is capable of processing source code both from files on the + filesystem as well as in-memory contents. + + Command-line arguments that would be passed to clang are specified as + a list via args. These can be used to specify include paths, warnings, + etc. e.g. ["-Wall", "-I/path/to/include"]. + + In-memory file content can be provided via unsaved_files. This is an + iterable of 2-tuples. The first element is the str filename. The + second element defines the content. Content can be provided as str + source code or as file objects (anything with a read() method). If + a file object is being used, content will be read until EOF and the + read cursor will not be reset to its original position. + + options is a bitwise or of TranslationUnit.PARSE_XXX flags which will + control parsing behavior. + + index is an Index instance to utilize. If not provided, a new Index + will be created for this TranslationUnit. + + To parse source from the filesystem, the filename of the file to parse + is specified by the filename argument. Or, filename could be None and + the args list would contain the filename(s) to parse. + + To parse source from an in-memory buffer, set filename to the virtual + filename you wish to associate with this source (e.g. "test.c"). The + contents of that file are then provided in unsaved_files. + + If an error occurs, a TranslationUnitLoadError is raised. + + Please note that a TranslationUnit with parser errors may be returned. + It is the caller's responsibility to check tu.diagnostics for errors. + + Also note that Clang infers the source language from the extension of + the input filename. If you pass in source code containing a C++ class + declaration with the filename "test.c" parsing will fail. + """ + if args is None: + args = [] + + if unsaved_files is None: + unsaved_files = [] + + if index is None: + index = Index.create() + + if isinstance(filename, str): + filename = filename.encode('utf8') + + args_length = len(args) + if args_length > 0: + args = (arg.encode('utf8') if isinstance(arg, str) else arg + for arg in args) + args_array = (c_char_p * args_length)(* args) + + unsaved_array = None + if len(unsaved_files) > 0: + unsaved_array = (_CXUnsavedFile * len(unsaved_files))() + for i, (name, contents) in enumerate(unsaved_files): + if hasattr(contents, "read"): + contents = contents.read() + + unsaved_array[i].name = name + unsaved_array[i].contents = contents + unsaved_array[i].length = len(contents) + + ptr = conf.lib.clang_parseTranslationUnit(index, filename, args_array, + args_length, unsaved_array, + len(unsaved_files), options) + + if not ptr: + raise TranslationUnitLoadError("Error parsing translation unit.") + + return cls(ptr, index=index) + + @classmethod + def from_ast_file(cls, filename, index=None): + """Create a TranslationUnit instance from a saved AST file. + + A previously-saved AST file (provided with -emit-ast or + TranslationUnit.save()) is loaded from the filename specified. + + If the file cannot be loaded, a TranslationUnitLoadError will be + raised. + + index is optional and is the Index instance to use. If not provided, + a default Index will be created. + """ + if index is None: + index = Index.create() + + ptr = conf.lib.clang_createTranslationUnit(index, filename) + if not ptr: + raise TranslationUnitLoadError(filename) + + return cls(ptr=ptr, index=index) + + def __init__(self, ptr, index): + """Create a TranslationUnit instance. + + TranslationUnits should be created using one of the from_* @classmethod + functions above. __init__ is only called internally. + """ + assert isinstance(index, Index) + self.index = index + ClangObject.__init__(self, ptr) + + def __del__(self): + conf.lib.clang_disposeTranslationUnit(self) + + @property + def cursor(self): + """Retrieve the cursor that represents the given translation unit.""" + return conf.lib.clang_getTranslationUnitCursor(self) + + @property + def spelling(self): + """Get the original translation unit source file name.""" + return conf.lib.clang_getTranslationUnitSpelling(self) + + def get_includes(self): + """ + Return an iterable sequence of FileInclusion objects that describe the + sequence of inclusions in a translation unit. The first object in + this sequence is always the input file. Note that this method will not + recursively iterate over header files included through precompiled + headers. + """ + def visitor(fobj, lptr, depth, includes): + if depth > 0: + loc = lptr.contents + includes.append(FileInclusion(loc.file, File(fobj), loc, depth)) + + # Automatically adapt CIndex/ctype pointers to python objects + includes = [] + conf.lib.clang_getInclusions(self, + callbacks['translation_unit_includes'](visitor), includes) + + return iter(includes) + + def get_file(self, filename): + """Obtain a File from this translation unit.""" + + return File.from_name(self, filename) + + def get_location(self, filename, position): + """Obtain a SourceLocation for a file in this translation unit. + + The position can be specified by passing: + + - Integer file offset. Initial file offset is 0. + - 2-tuple of (line number, column number). Initial file position is + (0, 0) + """ + f = self.get_file(filename) + + if isinstance(position, int): + return SourceLocation.from_offset(self, f, position) + + return SourceLocation.from_position(self, f, position[0], position[1]) + + def get_extent(self, filename, locations): + """Obtain a SourceRange from this translation unit. + + The bounds of the SourceRange must ultimately be defined by a start and + end SourceLocation. For the locations argument, you can pass: + + - 2 SourceLocation instances in a 2-tuple or list. + - 2 int file offsets via a 2-tuple or list. + - 2 2-tuple or lists of (line, column) pairs in a 2-tuple or list. + + e.g. + + get_extent('foo.c', (5, 10)) + get_extent('foo.c', ((1, 1), (1, 15))) + """ + f = self.get_file(filename) + + if len(locations) < 2: + raise Exception('Must pass object with at least 2 elements') + + start_location, end_location = locations + + if hasattr(start_location, '__len__'): + start_location = SourceLocation.from_position(self, f, + start_location[0], start_location[1]) + elif isinstance(start_location, int): + start_location = SourceLocation.from_offset(self, f, + start_location) + + if hasattr(end_location, '__len__'): + end_location = SourceLocation.from_position(self, f, + end_location[0], end_location[1]) + elif isinstance(end_location, int): + end_location = SourceLocation.from_offset(self, f, end_location) + + assert isinstance(start_location, SourceLocation) + assert isinstance(end_location, SourceLocation) + + return SourceRange.from_locations(start_location, end_location) + + @property + def diagnostics(self): + """ + Return an iterable (and indexable) object containing the diagnostics. + """ + class DiagIterator: + def __init__(self, tu): + self.tu = tu + + def __len__(self): + return int(conf.lib.clang_getNumDiagnostics(self.tu)) + + def __getitem__(self, key): + diag = conf.lib.clang_getDiagnostic(self.tu, key) + if not diag: + raise IndexError + return Diagnostic(diag) + + return DiagIterator(self) + + def reparse(self, unsaved_files=None, options=0): + """ + Reparse an already parsed translation unit. + + In-memory contents for files can be provided by passing a list of pairs + as unsaved_files, the first items should be the filenames to be mapped + and the second should be the contents to be substituted for the + file. The contents may be passed as strings or file objects. + """ + if unsaved_files is None: + unsaved_files = [] + + unsaved_files_array = 0 + if len(unsaved_files): + unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))() + for i,(name,value) in enumerate(unsaved_files): + if not isinstance(value, str): + # FIXME: It would be great to support an efficient version + # of this, one day. + value = value.read() + print(value) + if not isinstance(value, str): + raise TypeError('Unexpected unsaved file contents.') + unsaved_files_array[i].name = name + unsaved_files_array[i].contents = value + unsaved_files_array[i].length = len(value) + ptr = conf.lib.clang_reparseTranslationUnit(self, len(unsaved_files), + unsaved_files_array, options) + + def save(self, filename): + """Saves the TranslationUnit to a file. + + This is equivalent to passing -emit-ast to the clang frontend. The + saved file can be loaded back into a TranslationUnit. Or, if it + corresponds to a header, it can be used as a pre-compiled header file. + + If an error occurs while saving, a TranslationUnitSaveError is raised. + If the error was TranslationUnitSaveError.ERROR_INVALID_TU, this means + the constructed TranslationUnit was not valid at time of save. In this + case, the reason(s) why should be available via + TranslationUnit.diagnostics(). + + filename -- The path to save the translation unit to. + """ + options = conf.lib.clang_defaultSaveOptions(self) + result = int(conf.lib.clang_saveTranslationUnit(self, filename, + options)) + if result != 0: + raise TranslationUnitSaveError(result, + 'Error saving TranslationUnit.') + + def codeComplete(self, path, line, column, unsaved_files=None, + include_macros=False, include_code_patterns=False, + include_brief_comments=False): + """ + Code complete in this translation unit. + + In-memory contents for files can be provided by passing a list of pairs + as unsaved_files, the first items should be the filenames to be mapped + and the second should be the contents to be substituted for the + file. The contents may be passed as strings or file objects. + """ + options = 0 + + if include_macros: + options += 1 + + if include_code_patterns: + options += 2 + + if include_brief_comments: + options += 4 + + if unsaved_files is None: + unsaved_files = [] + + unsaved_files_array = 0 + if len(unsaved_files): + unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))() + for i,(name,value) in enumerate(unsaved_files): + if not isinstance(value, str): + # FIXME: It would be great to support an efficient version + # of this, one day. + value = value.read() + print(value) + if not isinstance(value, str): + raise TypeError('Unexpected unsaved file contents.') + unsaved_files_array[i].name = name + unsaved_files_array[i].contents = value + unsaved_files_array[i].length = len(value) + ptr = conf.lib.clang_codeCompleteAt(self, path, line, column, + unsaved_files_array, len(unsaved_files), options) + if ptr: + return CodeCompletionResults(ptr) + return None + + def get_tokens(self, locations=None, extent=None): + """Obtain tokens in this translation unit. + + This is a generator for Token instances. The caller specifies a range + of source code to obtain tokens for. The range can be specified as a + 2-tuple of SourceLocation or as a SourceRange. If both are defined, + behavior is undefined. + """ + if locations is not None: + extent = SourceRange(start=locations[0], end=locations[1]) + + return TokenGroup.get_tokens(self, extent) + +class File(ClangObject): + """ + The File class represents a particular source file that is part of a + translation unit. + """ + + @staticmethod + def from_name(translation_unit, file_name): + """Retrieve a file handle within the given translation unit.""" + return File(conf.lib.clang_getFile(translation_unit, file_name)) + + @property + def name(self): + """Return the complete file and path name of the file.""" + return conf.lib.clang_getCString(conf.lib.clang_getFileName(self)) + + @property + def time(self): + """Return the last modification time of the file.""" + return conf.lib.clang_getFileTime(self) + + def __bytes__(self): + return self.name + + def __repr__(self): + return "" % (self.name) + + @staticmethod + def from_cursor_result(res, fn, args): + assert isinstance(res, File) + + # Copy a reference to the TranslationUnit to prevent premature GC. + res._tu = args[0]._tu + return res + +class FileInclusion(object): + """ + The FileInclusion class represents the inclusion of one source file by + another via a '#include' directive or as the input file for the translation + unit. This class provides information about the included file, the including + file, the location of the '#include' directive and the depth of the included + file in the stack. Note that the input file has depth 0. + """ + + def __init__(self, src, tgt, loc, depth): + self.source = src + self.include = tgt + self.location = loc + self.depth = depth + + @property + def is_input_file(self): + """True if the included file is the input file.""" + return self.depth == 0 + +class CompilationDatabaseError(Exception): + """Represents an error that occurred when working with a CompilationDatabase + + Each error is associated to an enumerated value, accessible under + e.cdb_error. Consumers can compare the value with one of the ERROR_ + constants in this class. + """ + + # An unknown error occurred + ERROR_UNKNOWN = 0 + + # The database could not be loaded + ERROR_CANNOTLOADDATABASE = 1 + + def __init__(self, enumeration, message): + assert isinstance(enumeration, int) + + if enumeration > 1: + raise Exception("Encountered undefined CompilationDatabase error " + "constant: %d. Please file a bug to have this " + "value supported." % enumeration) + + self.cdb_error = enumeration + Exception.__init__(self, 'Error %d: %s' % (enumeration, message)) + +class CompileCommand(object): + """Represents the compile command used to build a file""" + def __init__(self, cmd, ccmds): + self.cmd = cmd + # Keep a reference to the originating CompileCommands + # to prevent garbage collection + self.ccmds = ccmds + + @property + def directory(self): + """Get the working directory for this CompileCommand""" + return conf.lib.clang_CompileCommand_getDirectory(self.cmd) + + @property + def filename(self): + """Get the working filename for this CompileCommand""" + return conf.lib.clang_CompileCommand_getFilename(self.cmd) + + @property + def arguments(self): + """ + Get an iterable object providing each argument in the + command line for the compiler invocation as a _CXString. + + Invariant : the first argument is the compiler executable + """ + length = conf.lib.clang_CompileCommand_getNumArgs(self.cmd) + for i in range(length): + yield conf.lib.clang_CompileCommand_getArg(self.cmd, i) + +class CompileCommands(object): + """ + CompileCommands is an iterable object containing all CompileCommand + that can be used for building a specific file. + """ + def __init__(self, ccmds): + self.ccmds = ccmds + + def __del__(self): + conf.lib.clang_CompileCommands_dispose(self.ccmds) + + def __len__(self): + return int(conf.lib.clang_CompileCommands_getSize(self.ccmds)) + + def __getitem__(self, i): + cc = conf.lib.clang_CompileCommands_getCommand(self.ccmds, i) + if not cc: + raise IndexError + return CompileCommand(cc, self) + + @staticmethod + def from_result(res, fn, args): + if not res: + return None + return CompileCommands(res) + +class CompilationDatabase(ClangObject): + """ + The CompilationDatabase is a wrapper class around + clang::tooling::CompilationDatabase + + It enables querying how a specific source file can be built. + """ + + def __del__(self): + conf.lib.clang_CompilationDatabase_dispose(self) + + @staticmethod + def from_result(res, fn, args): + if not res: + raise CompilationDatabaseError(0, + "CompilationDatabase loading failed") + return CompilationDatabase(res) + + @staticmethod + def fromDirectory(buildDir): + """Builds a CompilationDatabase from the database found in buildDir""" + errorCode = c_uint() + try: + cdb = conf.lib.clang_CompilationDatabase_fromDirectory(buildDir, + byref(errorCode)) + except CompilationDatabaseError as e: + raise CompilationDatabaseError(int(errorCode.value), + "CompilationDatabase loading failed") + return cdb + + def getCompileCommands(self, filename): + """ + Get an iterable object providing all the CompileCommands available to + build filename. Returns None if filename is not found in the database. + """ + return conf.lib.clang_CompilationDatabase_getCompileCommands(self, + filename) + + def getAllCompileCommands(self): + """ + Get an iterable object providing all the CompileCommands available from + the database. + """ + return conf.lib.clang_CompilationDatabase_getAllCompileCommands(self) + + +class Token(Structure): + """Represents a single token from the preprocessor. + + Tokens are effectively segments of source code. Source code is first parsed + into tokens before being converted into the AST and Cursors. + + Tokens are obtained from parsed TranslationUnit instances. You currently + can't create tokens manually. + """ + _fields_ = [ + ('int_data', c_uint * 4), + ('ptr_data', c_void_p) + ] + + @property + def spelling(self): + """The spelling of this token. + + This is the textual representation of the token in source. + """ + return conf.lib.clang_getTokenSpelling(self._tu, self) + + @property + def kind(self): + """Obtain the TokenKind of the current token.""" + return TokenKind.from_value(conf.lib.clang_getTokenKind(self)) + + @property + def location(self): + """The SourceLocation this Token occurs at.""" + return conf.lib.clang_getTokenLocation(self._tu, self) + + @property + def extent(self): + """The SourceRange this Token occupies.""" + return conf.lib.clang_getTokenExtent(self._tu, self) + + @property + def cursor(self): + """The Cursor this Token corresponds to.""" + cursor = Cursor() + + conf.lib.clang_annotateTokens(self._tu, byref(self), 1, byref(cursor)) + + return cursor + +# Now comes the plumbing to hook up the C library. + +# Register callback types in common container. +callbacks['translation_unit_includes'] = CFUNCTYPE(None, c_object_p, + POINTER(SourceLocation), c_uint, py_object) +callbacks['cursor_visit'] = CFUNCTYPE(c_int, Cursor, Cursor, py_object) +callbacks['fields_visit'] = CFUNCTYPE(c_int, Cursor, py_object) + +# Functions strictly alphabetical order. +functionList = [ + ("clang_annotateTokens", + [TranslationUnit, POINTER(Token), c_uint, POINTER(Cursor)]), + + ("clang_CompilationDatabase_dispose", + [c_object_p]), + + ("clang_CompilationDatabase_fromDirectory", + [c_char_p, POINTER(c_uint)], + c_object_p, + CompilationDatabase.from_result), + + ("clang_CompilationDatabase_getAllCompileCommands", + [c_object_p], + c_object_p, + CompileCommands.from_result), + + ("clang_CompilationDatabase_getCompileCommands", + [c_object_p, c_char_p], + c_object_p, + CompileCommands.from_result), + + ("clang_CompileCommands_dispose", + [c_object_p]), + + ("clang_CompileCommands_getCommand", + [c_object_p, c_uint], + c_object_p), + + ("clang_CompileCommands_getSize", + [c_object_p], + c_uint), + + ("clang_CompileCommand_getArg", + [c_object_p, c_uint], + _CXString, + _CXString.from_result), + + ("clang_CompileCommand_getDirectory", + [c_object_p], + _CXString, + _CXString.from_result), + + ("clang_CompileCommand_getFilename", + [c_object_p], + _CXString, + _CXString.from_result), + + ("clang_CompileCommand_getNumArgs", + [c_object_p], + c_uint), + + ("clang_codeCompleteAt", + [TranslationUnit, c_char_p, c_int, c_int, c_void_p, c_int, c_int], + POINTER(CCRStructure)), + + ("clang_codeCompleteGetDiagnostic", + [CodeCompletionResults, c_int], + Diagnostic), + + ("clang_codeCompleteGetNumDiagnostics", + [CodeCompletionResults], + c_int), + + ("clang_createIndex", + [c_int, c_int], + c_object_p), + + ("clang_createTranslationUnit", + [Index, c_char_p], + c_object_p), + + ("clang_CXXConstructor_isConvertingConstructor", + [Cursor], + bool), + + ("clang_CXXConstructor_isCopyConstructor", + [Cursor], + bool), + + ("clang_CXXConstructor_isDefaultConstructor", + [Cursor], + bool), + + ("clang_CXXConstructor_isMoveConstructor", + [Cursor], + bool), + + ("clang_CXXField_isMutable", + [Cursor], + bool), + + ("clang_CXXMethod_isConst", + [Cursor], + bool), + + ("clang_CXXMethod_isDefaulted", + [Cursor], + bool), + + ("clang_CXXMethod_isPureVirtual", + [Cursor], + bool), + + ("clang_CXXMethod_isStatic", + [Cursor], + bool), + + ("clang_CXXMethod_isVirtual", + [Cursor], + bool), + + ("clang_defaultDiagnosticDisplayOptions", + [], + c_uint), + + ("clang_defaultSaveOptions", + [TranslationUnit], + c_uint), + + ("clang_disposeCodeCompleteResults", + [CodeCompletionResults]), + +# ("clang_disposeCXTUResourceUsage", +# [CXTUResourceUsage]), + + ("clang_disposeDiagnostic", + [Diagnostic]), + + ("clang_disposeIndex", + [Index]), + + ("clang_disposeString", + [_CXString]), + + ("clang_disposeTokens", + [TranslationUnit, POINTER(Token), c_uint]), + + ("clang_disposeTranslationUnit", + [TranslationUnit]), + + ("clang_equalCursors", + [Cursor, Cursor], + bool), + + ("clang_equalLocations", + [SourceLocation, SourceLocation], + bool), + + ("clang_equalRanges", + [SourceRange, SourceRange], + bool), + + ("clang_equalTypes", + [Type, Type], + bool), + + ("clang_formatDiagnostic", + [Diagnostic, c_uint], + _CXString), + + ("clang_getArgType", + [Type, c_uint], + Type, + Type.from_result), + + ("clang_getArrayElementType", + [Type], + Type, + Type.from_result), + + ("clang_getArraySize", + [Type], + c_longlong), + + ("clang_getFieldDeclBitWidth", + [Cursor], + c_int), + + ("clang_getCanonicalCursor", + [Cursor], + Cursor, + Cursor.from_cursor_result), + + ("clang_getCanonicalType", + [Type], + Type, + Type.from_result), + + ("clang_getChildDiagnostics", + [Diagnostic], + c_object_p), + + ("clang_getCompletionAvailability", + [c_void_p], + c_int), + + ("clang_getCompletionBriefComment", + [c_void_p], + _CXString), + + ("clang_getCompletionChunkCompletionString", + [c_void_p, c_int], + c_object_p), + + ("clang_getCompletionChunkKind", + [c_void_p, c_int], + c_int), + + ("clang_getCompletionChunkText", + [c_void_p, c_int], + _CXString), + + ("clang_getCompletionPriority", + [c_void_p], + c_int), + + ("clang_getCString", + [_CXString], + c_char_p), + + ("clang_getCursor", + [TranslationUnit, SourceLocation], + Cursor), + + ("clang_getCursorDefinition", + [Cursor], + Cursor, + Cursor.from_result), + + ("clang_getCursorDisplayName", + [Cursor], + _CXString, + _CXString.from_result), + + ("clang_getCursorExtent", + [Cursor], + SourceRange), + + ("clang_getCursorLexicalParent", + [Cursor], + Cursor, + Cursor.from_cursor_result), + + ("clang_getCursorLocation", + [Cursor], + SourceLocation), + + ("clang_getCursorReferenced", + [Cursor], + Cursor, + Cursor.from_result), + + ("clang_getCursorReferenceNameRange", + [Cursor, c_uint, c_uint], + SourceRange), + + ("clang_getCursorSemanticParent", + [Cursor], + Cursor, + Cursor.from_cursor_result), + + ("clang_getCursorSpelling", + [Cursor], + _CXString, + _CXString.from_result), + + ("clang_getCursorType", + [Cursor], + Type, + Type.from_result), + + ("clang_getCursorUSR", + [Cursor], + _CXString, + _CXString.from_result), + + ("clang_Cursor_getMangling", + [Cursor], + _CXString, + _CXString.from_result), + +# ("clang_getCXTUResourceUsage", +# [TranslationUnit], +# CXTUResourceUsage), + + ("clang_getCXXAccessSpecifier", + [Cursor], + c_uint), + + ("clang_getDeclObjCTypeEncoding", + [Cursor], + _CXString, + _CXString.from_result), + + ("clang_getDiagnostic", + [c_object_p, c_uint], + c_object_p), + + ("clang_getDiagnosticCategory", + [Diagnostic], + c_uint), + + ("clang_getDiagnosticCategoryText", + [Diagnostic], + _CXString, + _CXString.from_result), + + ("clang_getDiagnosticFixIt", + [Diagnostic, c_uint, POINTER(SourceRange)], + _CXString, + _CXString.from_result), + + ("clang_getDiagnosticInSet", + [c_object_p, c_uint], + c_object_p), + + ("clang_getDiagnosticLocation", + [Diagnostic], + SourceLocation), + + ("clang_getDiagnosticNumFixIts", + [Diagnostic], + c_uint), + + ("clang_getDiagnosticNumRanges", + [Diagnostic], + c_uint), + + ("clang_getDiagnosticOption", + [Diagnostic, POINTER(_CXString)], + _CXString, + _CXString.from_result), + + ("clang_getDiagnosticRange", + [Diagnostic, c_uint], + SourceRange), + + ("clang_getDiagnosticSeverity", + [Diagnostic], + c_int), + + ("clang_getDiagnosticSpelling", + [Diagnostic], + _CXString, + _CXString.from_result), + + ("clang_getElementType", + [Type], + Type, + Type.from_result), + + ("clang_getEnumConstantDeclUnsignedValue", + [Cursor], + c_ulonglong), + + ("clang_getEnumConstantDeclValue", + [Cursor], + c_longlong), + + ("clang_getEnumDeclIntegerType", + [Cursor], + Type, + Type.from_result), + + ("clang_getFile", + [TranslationUnit, c_char_p], + c_object_p), + + ("clang_getFileName", + [File], + _CXString), # TODO go through _CXString.from_result? + + ("clang_getFileTime", + [File], + c_uint), + + ("clang_getIBOutletCollectionType", + [Cursor], + Type, + Type.from_result), + + ("clang_getIncludedFile", + [Cursor], + File, + File.from_cursor_result), + + ("clang_getInclusions", + [TranslationUnit, callbacks['translation_unit_includes'], py_object]), + + ("clang_getInstantiationLocation", + [SourceLocation, POINTER(c_object_p), POINTER(c_uint), POINTER(c_uint), + POINTER(c_uint)]), + + ("clang_getLocation", + [TranslationUnit, File, c_uint, c_uint], + SourceLocation), + + ("clang_getLocationForOffset", + [TranslationUnit, File, c_uint], + SourceLocation), + + ("clang_getNullCursor", + None, + Cursor), + + ("clang_getNumArgTypes", + [Type], + c_uint), + + ("clang_getNumCompletionChunks", + [c_void_p], + c_int), + + ("clang_getNumDiagnostics", + [c_object_p], + c_uint), + + ("clang_getNumDiagnosticsInSet", + [c_object_p], + c_uint), + + ("clang_getNumElements", + [Type], + c_longlong), + + ("clang_getNumOverloadedDecls", + [Cursor], + c_uint), + + ("clang_getOverloadedDecl", + [Cursor, c_uint], + Cursor, + Cursor.from_cursor_result), + + ("clang_getPointeeType", + [Type], + Type, + Type.from_result), + + ("clang_getRange", + [SourceLocation, SourceLocation], + SourceRange), + + ("clang_getRangeEnd", + [SourceRange], + SourceLocation), + + ("clang_getRangeStart", + [SourceRange], + SourceLocation), + + ("clang_getResultType", + [Type], + Type, + Type.from_result), + + ("clang_getSpecializedCursorTemplate", + [Cursor], + Cursor, + Cursor.from_cursor_result), + + ("clang_getTemplateCursorKind", + [Cursor], + c_uint), + + ("clang_getTokenExtent", + [TranslationUnit, Token], + SourceRange), + + ("clang_getTokenKind", + [Token], + c_uint), + + ("clang_getTokenLocation", + [TranslationUnit, Token], + SourceLocation), + + ("clang_getTokenSpelling", + [TranslationUnit, Token], + _CXString, + _CXString.from_result), + + ("clang_getTranslationUnitCursor", + [TranslationUnit], + Cursor, + Cursor.from_result), + + ("clang_getTranslationUnitSpelling", + [TranslationUnit], + _CXString, + _CXString.from_result), + + ("clang_getTUResourceUsageName", + [c_uint], + c_char_p), + + ("clang_getTypeDeclaration", + [Type], + Cursor, + Cursor.from_result), + + ("clang_getTypedefDeclUnderlyingType", + [Cursor], + Type, + Type.from_result), + + ("clang_getTypeKindSpelling", + [c_uint], + _CXString, + _CXString.from_result), + + ("clang_getTypeSpelling", + [Type], + _CXString, + _CXString.from_result), + + ("clang_hashCursor", + [Cursor], + c_uint), + + ("clang_isAttribute", + [CursorKind], + bool), + + ("clang_isConstQualifiedType", + [Type], + bool), + + ("clang_isCursorDefinition", + [Cursor], + bool), + + ("clang_isDeclaration", + [CursorKind], + bool), + + ("clang_isExpression", + [CursorKind], + bool), + + ("clang_isFileMultipleIncludeGuarded", + [TranslationUnit, File], + bool), + + ("clang_isFunctionTypeVariadic", + [Type], + bool), + + ("clang_isInvalid", + [CursorKind], + bool), + + ("clang_isPODType", + [Type], + bool), + + ("clang_isPreprocessing", + [CursorKind], + bool), + + ("clang_isReference", + [CursorKind], + bool), + + ("clang_isRestrictQualifiedType", + [Type], + bool), + + ("clang_isStatement", + [CursorKind], + bool), + + ("clang_isTranslationUnit", + [CursorKind], + bool), + + ("clang_isUnexposed", + [CursorKind], + bool), + + ("clang_isVirtualBase", + [Cursor], + bool), + + ("clang_isVolatileQualifiedType", + [Type], + bool), + + ("clang_parseTranslationUnit", + [Index, c_char_p, c_void_p, c_int, c_void_p, c_int, c_int], + c_object_p), + + ("clang_reparseTranslationUnit", + [TranslationUnit, c_int, c_void_p, c_int], + c_int), + + ("clang_saveTranslationUnit", + [TranslationUnit, c_char_p, c_uint], + c_int), + + ("clang_tokenize", + [TranslationUnit, SourceRange, POINTER(POINTER(Token)), POINTER(c_uint)]), + + ("clang_visitChildren", + [Cursor, callbacks['cursor_visit'], py_object], + c_uint), + + ("clang_Cursor_getNumArguments", + [Cursor], + c_int), + + ("clang_Cursor_getArgument", + [Cursor, c_uint], + Cursor, + Cursor.from_result), + + ("clang_Cursor_getNumTemplateArguments", + [Cursor], + c_int), + + ("clang_Cursor_getTemplateArgumentKind", + [Cursor, c_uint], + TemplateArgumentKind.from_id), + + ("clang_Cursor_getTemplateArgumentType", + [Cursor, c_uint], + Type, + Type.from_result), + + ("clang_Cursor_getTemplateArgumentValue", + [Cursor, c_uint], + c_longlong), + + ("clang_Cursor_getTemplateArgumentUnsignedValue", + [Cursor, c_uint], + c_ulonglong), + + ("clang_Cursor_isAnonymous", + [Cursor], + bool), + + ("clang_Cursor_isBitField", + [Cursor], + bool), + + ("clang_Cursor_getBriefCommentText", + [Cursor], + _CXString, + _CXString.from_result), + + ("clang_Cursor_getRawCommentText", + [Cursor], + _CXString, + _CXString.from_result), + + ("clang_Cursor_getOffsetOfField", + [Cursor], + c_longlong), + + ("clang_Type_getAlignOf", + [Type], + c_longlong), + + ("clang_Type_getClassType", + [Type], + Type, + Type.from_result), + + ("clang_Type_getOffsetOf", + [Type, c_char_p], + c_longlong), + + ("clang_Type_getSizeOf", + [Type], + c_longlong), + + ("clang_Type_getCXXRefQualifier", + [Type], + c_uint), + + ("clang_Type_getNamedType", + [Type], + Type, + Type.from_result), + + ("clang_Type_visitFields", + [Type, callbacks['fields_visit'], py_object], + c_uint), +] + +class LibclangError(Exception): + def __init__(self, message): + self.m = message + + def __str__(self): + return self.m + +def register_function(lib, item, ignore_errors): + # A function may not exist, if these bindings are used with an older or + # incompatible version of libclang.so. + try: + func = getattr(lib, item[0]) + except AttributeError as e: + msg = str(e) + ". Please ensure that your python bindings are "\ + "compatible with your libclang.so version." + if ignore_errors: + return + raise LibclangError(msg) + + if len(item) >= 2: + func.argtypes = item[1] + + if len(item) >= 3: + func.restype = item[2] + + if len(item) == 4: + func.errcheck = item[3] + +def register_functions(lib, ignore_errors): + """Register function prototypes with a libclang library instance. + + This must be called as part of library instantiation so Python knows how + to call out to the shared library. + """ + + def register(item): + return register_function(lib, item, ignore_errors) + + for f in functionList: + register(f) + +class Config: + library_path = None + library_file = None + compatibility_check = False + loaded = False + + @staticmethod + def set_library_path(path): + """Set the path in which to search for libclang""" + if Config.loaded: + raise Exception("library path must be set before before using " \ + "any other functionalities in libclang.") + + Config.library_path = path + + @staticmethod + def set_library_file(filename): + """Set the exact location of libclang""" + if Config.loaded: + raise Exception("library file must be set before before using " \ + "any other functionalities in libclang.") + + Config.library_file = filename + + @staticmethod + def set_compatibility_check(check_status): + """ Perform compatibility check when loading libclang + + The python bindings are only tested and evaluated with the version of + libclang they are provided with. To ensure correct behavior a (limited) + compatibility check is performed when loading the bindings. This check + will throw an exception, as soon as it fails. + + In case these bindings are used with an older version of libclang, parts + that have been stable between releases may still work. Users of the + python bindings can disable the compatibility check. This will cause + the python bindings to load, even though they are written for a newer + version of libclang. Failures now arise if unsupported or incompatible + features are accessed. The user is required to test themselves if the + features they are using are available and compatible between different + libclang versions. + """ + if Config.loaded: + raise Exception("compatibility_check must be set before before " \ + "using any other functionalities in libclang.") + + Config.compatibility_check = check_status + + @CachedProperty + def lib(self): + lib = self.get_cindex_library() + register_functions(lib, not Config.compatibility_check) + Config.loaded = True + return lib + + def get_filename(self): + if Config.library_file: + return Config.library_file + + import platform + name = platform.system() + + if name == 'Darwin': + file = 'libclang.dylib' + elif name == 'Windows': + file = 'libclang.dll' + else: + file = 'libclang.so' + + if Config.library_path: + file = Config.library_path + '/' + file + + return file + + def get_cindex_library(self): + try: + library = cdll.LoadLibrary(self.get_filename()) + except OSError as e: + msg = str(e) + ". To provide a path to libclang use " \ + "Config.set_library_path() or " \ + "Config.set_library_file()." + raise LibclangError(msg) + + return library + + def function_exists(self, name): + try: + getattr(self.lib, name) + except AttributeError: + return False + + return True + +def register_enumerations(): + for name, value in clang.enumerations.TokenKinds: + TokenKind.register(value, name) + +conf = Config() +register_enumerations() + +__all__ = [ + 'Config', + 'CodeCompletionResults', + 'CompilationDatabase', + 'CompileCommands', + 'CompileCommand', + 'CursorKind', + 'Cursor', + 'Diagnostic', + 'File', + 'FixIt', + 'Index', + 'SourceLocation', + 'SourceRange', + 'TokenKind', + 'Token', + 'TranslationUnitLoadError', + 'TranslationUnit', + 'TypeKind', + 'Type', +] diff --git a/thirdparty/pybind11/tools/clang/enumerations.py b/thirdparty/pybind11/tools/clang/enumerations.py new file mode 100644 index 000000000..a86a48ade --- /dev/null +++ b/thirdparty/pybind11/tools/clang/enumerations.py @@ -0,0 +1,34 @@ +#===- enumerations.py - Python Enumerations ------------------*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +""" +Clang Enumerations +================== + +This module provides static definitions of enumerations that exist in libclang. + +Enumerations are typically defined as a list of tuples. The exported values are +typically munged into other types or classes at module load time. + +All enumerations are centrally defined in this file so they are all grouped +together and easier to audit. And, maybe even one day this file will be +automatically generated by scanning the libclang headers! +""" + +# Maps to CXTokenKind. Note that libclang maintains a separate set of token +# enumerations from the C++ API. +TokenKinds = [ + ('PUNCTUATION', 0), + ('KEYWORD', 1), + ('IDENTIFIER', 2), + ('LITERAL', 3), + ('COMMENT', 4), +] + +__all__ = ['TokenKinds'] diff --git a/thirdparty/pybind11/tools/libsize.py b/thirdparty/pybind11/tools/libsize.py new file mode 100644 index 000000000..5dcb8b0d0 --- /dev/null +++ b/thirdparty/pybind11/tools/libsize.py @@ -0,0 +1,38 @@ +from __future__ import print_function, division +import os +import sys + +# Internal build script for generating debugging test .so size. +# Usage: +# python libsize.py file.so save.txt -- displays the size of file.so and, if save.txt exists, compares it to the +# size in it, then overwrites save.txt with the new size for future runs. + +if len(sys.argv) != 3: + sys.exit("Invalid arguments: usage: python libsize.py file.so save.txt") + +lib = sys.argv[1] +save = sys.argv[2] + +if not os.path.exists(lib): + sys.exit("Error: requested file ({}) does not exist".format(lib)) + +libsize = os.path.getsize(lib) + +print("------", os.path.basename(lib), "file size:", libsize, end='') + +if os.path.exists(save): + with open(save) as sf: + oldsize = int(sf.readline()) + + if oldsize > 0: + change = libsize - oldsize + if change == 0: + print(" (no change)") + else: + print(" (change of {:+} bytes = {:+.2%})".format(change, change / oldsize)) +else: + print() + +with open(save, 'w') as sf: + sf.write(str(libsize)) + diff --git a/thirdparty/pybind11/tools/mkdoc.py b/thirdparty/pybind11/tools/mkdoc.py new file mode 100644 index 000000000..44164af3d --- /dev/null +++ b/thirdparty/pybind11/tools/mkdoc.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python3 +# +# Syntax: mkdoc.py [-I ..] [.. a list of header files ..] +# +# Extract documentation from C++ header files to use it in Python bindings +# + +import os +import sys +import platform +import re +import textwrap + +from clang import cindex +from clang.cindex import CursorKind +from collections import OrderedDict +from glob import glob +from threading import Thread, Semaphore +from multiprocessing import cpu_count + +RECURSE_LIST = [ + CursorKind.TRANSLATION_UNIT, + CursorKind.NAMESPACE, + CursorKind.CLASS_DECL, + CursorKind.STRUCT_DECL, + CursorKind.ENUM_DECL, + CursorKind.CLASS_TEMPLATE +] + +PRINT_LIST = [ + CursorKind.CLASS_DECL, + CursorKind.STRUCT_DECL, + CursorKind.ENUM_DECL, + CursorKind.ENUM_CONSTANT_DECL, + CursorKind.CLASS_TEMPLATE, + CursorKind.FUNCTION_DECL, + CursorKind.FUNCTION_TEMPLATE, + CursorKind.CONVERSION_FUNCTION, + CursorKind.CXX_METHOD, + CursorKind.CONSTRUCTOR, + CursorKind.FIELD_DECL +] + +PREFIX_BLACKLIST = [ + CursorKind.TRANSLATION_UNIT +] + +CPP_OPERATORS = { + '<=': 'le', '>=': 'ge', '==': 'eq', '!=': 'ne', '[]': 'array', + '+=': 'iadd', '-=': 'isub', '*=': 'imul', '/=': 'idiv', '%=': + 'imod', '&=': 'iand', '|=': 'ior', '^=': 'ixor', '<<=': 'ilshift', + '>>=': 'irshift', '++': 'inc', '--': 'dec', '<<': 'lshift', '>>': + 'rshift', '&&': 'land', '||': 'lor', '!': 'lnot', '~': 'bnot', + '&': 'band', '|': 'bor', '+': 'add', '-': 'sub', '*': 'mul', '/': + 'div', '%': 'mod', '<': 'lt', '>': 'gt', '=': 'assign', '()': 'call' +} + +CPP_OPERATORS = OrderedDict( + sorted(CPP_OPERATORS.items(), key=lambda t: -len(t[0]))) + +job_count = cpu_count() +job_semaphore = Semaphore(job_count) + + +class NoFilenamesError(ValueError): + pass + + +def d(s): + return s if isinstance(s, str) else s.decode('utf8') + + +def sanitize_name(name): + name = re.sub(r'type-parameter-0-([0-9]+)', r'T\1', name) + for k, v in CPP_OPERATORS.items(): + name = name.replace('operator%s' % k, 'operator_%s' % v) + name = re.sub('<.*>', '', name) + name = ''.join([ch if ch.isalnum() else '_' for ch in name]) + name = re.sub('_$', '', re.sub('_+', '_', name)) + return '__doc_' + name + + +def process_comment(comment): + result = '' + + # Remove C++ comment syntax + leading_spaces = float('inf') + for s in comment.expandtabs(tabsize=4).splitlines(): + s = s.strip() + if s.startswith('/*'): + s = s[2:].lstrip('*') + elif s.endswith('*/'): + s = s[:-2].rstrip('*') + elif s.startswith('///'): + s = s[3:] + if s.startswith('*'): + s = s[1:] + if len(s) > 0: + leading_spaces = min(leading_spaces, len(s) - len(s.lstrip())) + result += s + '\n' + + if leading_spaces != float('inf'): + result2 = "" + for s in result.splitlines(): + result2 += s[leading_spaces:] + '\n' + result = result2 + + # Doxygen tags + cpp_group = '([\w:]+)' + param_group = '([\[\w:\]]+)' + + s = result + s = re.sub(r'\\c\s+%s' % cpp_group, r'``\1``', s) + s = re.sub(r'\\a\s+%s' % cpp_group, r'*\1*', s) + s = re.sub(r'\\e\s+%s' % cpp_group, r'*\1*', s) + s = re.sub(r'\\em\s+%s' % cpp_group, r'*\1*', s) + s = re.sub(r'\\b\s+%s' % cpp_group, r'**\1**', s) + s = re.sub(r'\\ingroup\s+%s' % cpp_group, r'', s) + s = re.sub(r'\\param%s?\s+%s' % (param_group, cpp_group), + r'\n\n$Parameter ``\2``:\n\n', s) + s = re.sub(r'\\tparam%s?\s+%s' % (param_group, cpp_group), + r'\n\n$Template parameter ``\2``:\n\n', s) + + for in_, out_ in { + 'return': 'Returns', + 'author': 'Author', + 'authors': 'Authors', + 'copyright': 'Copyright', + 'date': 'Date', + 'remark': 'Remark', + 'sa': 'See also', + 'see': 'See also', + 'extends': 'Extends', + 'throw': 'Throws', + 'throws': 'Throws' + }.items(): + s = re.sub(r'\\%s\s*' % in_, r'\n\n$%s:\n\n' % out_, s) + + s = re.sub(r'\\details\s*', r'\n\n', s) + s = re.sub(r'\\brief\s*', r'', s) + s = re.sub(r'\\short\s*', r'', s) + s = re.sub(r'\\ref\s*', r'', s) + + s = re.sub(r'\\code\s?(.*?)\s?\\endcode', + r"```\n\1\n```\n", s, flags=re.DOTALL) + + # HTML/TeX tags + s = re.sub(r'(.*?)', r'``\1``', s, flags=re.DOTALL) + s = re.sub(r'
(.*?)
', r"```\n\1\n```\n", s, flags=re.DOTALL) + s = re.sub(r'(.*?)', r'*\1*', s, flags=re.DOTALL) + s = re.sub(r'(.*?)', r'**\1**', s, flags=re.DOTALL) + s = re.sub(r'\\f\$(.*?)\\f\$', r'$\1$', s, flags=re.DOTALL) + s = re.sub(r'
  • ', r'\n\n* ', s) + s = re.sub(r'', r'', s) + s = re.sub(r'
  • ', r'\n\n', s) + + s = s.replace('``true``', '``True``') + s = s.replace('``false``', '``False``') + + # Re-flow text + wrapper = textwrap.TextWrapper() + wrapper.expand_tabs = True + wrapper.replace_whitespace = True + wrapper.drop_whitespace = True + wrapper.width = 70 + wrapper.initial_indent = wrapper.subsequent_indent = '' + + result = '' + in_code_segment = False + for x in re.split(r'(```)', s): + if x == '```': + if not in_code_segment: + result += '```\n' + else: + result += '\n```\n\n' + in_code_segment = not in_code_segment + elif in_code_segment: + result += x.strip() + else: + for y in re.split(r'(?: *\n *){2,}', x): + wrapped = wrapper.fill(re.sub(r'\s+', ' ', y).strip()) + if len(wrapped) > 0 and wrapped[0] == '$': + result += wrapped[1:] + '\n' + wrapper.initial_indent = \ + wrapper.subsequent_indent = ' ' * 4 + else: + if len(wrapped) > 0: + result += wrapped + '\n\n' + wrapper.initial_indent = wrapper.subsequent_indent = '' + return result.rstrip().lstrip('\n') + + +def extract(filename, node, prefix, output): + if not (node.location.file is None or + os.path.samefile(d(node.location.file.name), filename)): + return 0 + if node.kind in RECURSE_LIST: + sub_prefix = prefix + if node.kind not in PREFIX_BLACKLIST: + if len(sub_prefix) > 0: + sub_prefix += '_' + sub_prefix += d(node.spelling) + for i in node.get_children(): + extract(filename, i, sub_prefix, output) + if node.kind in PRINT_LIST: + comment = d(node.raw_comment) if node.raw_comment is not None else '' + comment = process_comment(comment) + sub_prefix = prefix + if len(sub_prefix) > 0: + sub_prefix += '_' + if len(node.spelling) > 0: + name = sanitize_name(sub_prefix + d(node.spelling)) + output.append((name, filename, comment)) + + +class ExtractionThread(Thread): + def __init__(self, filename, parameters, output): + Thread.__init__(self) + self.filename = filename + self.parameters = parameters + self.output = output + job_semaphore.acquire() + + def run(self): + print('Processing "%s" ..' % self.filename, file=sys.stderr) + try: + index = cindex.Index( + cindex.conf.lib.clang_createIndex(False, True)) + tu = index.parse(self.filename, self.parameters) + extract(self.filename, tu.cursor, '', self.output) + finally: + job_semaphore.release() + + +def read_args(args): + parameters = [] + filenames = [] + if "-x" not in args: + parameters.extend(['-x', 'c++']) + if not any(it.startswith("-std=") for it in args): + parameters.append('-std=c++11') + + if platform.system() == 'Darwin': + dev_path = '/Applications/Xcode.app/Contents/Developer/' + lib_dir = dev_path + 'Toolchains/XcodeDefault.xctoolchain/usr/lib/' + sdk_dir = dev_path + 'Platforms/MacOSX.platform/Developer/SDKs' + libclang = lib_dir + 'libclang.dylib' + + if os.path.exists(libclang): + cindex.Config.set_library_path(os.path.dirname(libclang)) + + if os.path.exists(sdk_dir): + sysroot_dir = os.path.join(sdk_dir, next(os.walk(sdk_dir))[1][0]) + parameters.append('-isysroot') + parameters.append(sysroot_dir) + elif platform.system() == 'Linux': + # clang doesn't find its own base includes by default on Linux, + # but different distros install them in different paths. + # Try to autodetect, preferring the highest numbered version. + def clang_folder_version(d): + return [int(ver) for ver in re.findall(r'(?:${PYBIND11_CPP_STANDARD}>) + endif() + + get_property(_iid TARGET ${PN}::pybind11 PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + get_property(_ill TARGET ${PN}::module PROPERTY INTERFACE_LINK_LIBRARIES) + set(${PN}_INCLUDE_DIRS ${_iid}) + set(${PN}_LIBRARIES ${_ico} ${_ill}) +endif() +endif() diff --git a/thirdparty/pybind11/tools/pybind11Tools.cmake b/thirdparty/pybind11/tools/pybind11Tools.cmake new file mode 100644 index 000000000..508e47429 --- /dev/null +++ b/thirdparty/pybind11/tools/pybind11Tools.cmake @@ -0,0 +1,227 @@ +# tools/pybind11Tools.cmake -- Build system for the pybind11 modules +# +# Copyright (c) 2015 Wenzel Jakob +# +# All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +cmake_minimum_required(VERSION 2.8.12) + +# Add a CMake parameter for choosing a desired Python version +if(NOT PYBIND11_PYTHON_VERSION) + set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules") +endif() + +set(Python_ADDITIONAL_VERSIONS 3.9 3.8 3.7 3.6 3.5 3.4) +find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED) + +include(CheckCXXCompilerFlag) +include(CMakeParseArguments) + +if(NOT PYBIND11_CPP_STANDARD AND NOT CMAKE_CXX_STANDARD) + if(NOT MSVC) + check_cxx_compiler_flag("-std=c++14" HAS_CPP14_FLAG) + + if (HAS_CPP14_FLAG) + set(PYBIND11_CPP_STANDARD -std=c++14) + else() + check_cxx_compiler_flag("-std=c++11" HAS_CPP11_FLAG) + if (HAS_CPP11_FLAG) + set(PYBIND11_CPP_STANDARD -std=c++11) + else() + message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!") + endif() + endif() + elseif(MSVC) + set(PYBIND11_CPP_STANDARD /std:c++14) + endif() + + set(PYBIND11_CPP_STANDARD ${PYBIND11_CPP_STANDARD} CACHE STRING + "C++ standard flag, e.g. -std=c++11, -std=c++14, /std:c++14. Defaults to C++14 mode." FORCE) +endif() + +# Checks whether the given CXX/linker flags can compile and link a cxx file. cxxflags and +# linkerflags are lists of flags to use. The result variable is a unique variable name for each set +# of flags: the compilation result will be cached base on the result variable. If the flags work, +# sets them in cxxflags_out/linkerflags_out internal cache variables (in addition to ${result}). +function(_pybind11_return_if_cxx_and_linker_flags_work result cxxflags linkerflags cxxflags_out linkerflags_out) + set(CMAKE_REQUIRED_LIBRARIES ${linkerflags}) + check_cxx_compiler_flag("${cxxflags}" ${result}) + if (${result}) + set(${cxxflags_out} "${cxxflags}" CACHE INTERNAL "" FORCE) + set(${linkerflags_out} "${linkerflags}" CACHE INTERNAL "" FORCE) + endif() +endfunction() + +# Internal: find the appropriate link time optimization flags for this compiler +function(_pybind11_add_lto_flags target_name prefer_thin_lto) + if (NOT DEFINED PYBIND11_LTO_CXX_FLAGS) + set(PYBIND11_LTO_CXX_FLAGS "" CACHE INTERNAL "") + set(PYBIND11_LTO_LINKER_FLAGS "" CACHE INTERNAL "") + + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + set(cxx_append "") + set(linker_append "") + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE) + # Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it + set(linker_append ";$<$:-O3>") + elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(cxx_append ";-fno-fat-lto-objects") + endif() + + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND prefer_thin_lto) + _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO_THIN + "-flto=thin${cxx_append}" "-flto=thin${linker_append}" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + + if (NOT HAS_FLTO_THIN) + _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO + "-flto${cxx_append}" "-flto${linker_append}" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel") + # Intel equivalent to LTO is called IPO + _pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO + "-ipo" "-ipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + elseif(MSVC) + # cmake only interprets libraries as linker flags when they start with a - (otherwise it + # converts /LTCG to \LTCG as if it was a Windows path). Luckily MSVC supports passing flags + # with - instead of /, even if it is a bit non-standard: + _pybind11_return_if_cxx_and_linker_flags_work(HAS_MSVC_GL_LTCG + "/GL" "-LTCG" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + + if (PYBIND11_LTO_CXX_FLAGS) + message(STATUS "LTO enabled") + else() + message(STATUS "LTO disabled (not supported by the compiler and/or linker)") + endif() + endif() + + # Enable LTO flags if found, except for Debug builds + if (PYBIND11_LTO_CXX_FLAGS) + target_compile_options(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_CXX_FLAGS}>") + endif() + if (PYBIND11_LTO_LINKER_FLAGS) + target_link_libraries(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_LINKER_FLAGS}>") + endif() +endfunction() + +# Build a Python extension module: +# pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] +# [NO_EXTRAS] [SYSTEM] [THIN_LTO] source1 [source2 ...]) +# +function(pybind11_add_module target_name) + set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS SYSTEM THIN_LTO) + cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) + + if(ARG_MODULE AND ARG_SHARED) + message(FATAL_ERROR "Can't be both MODULE and SHARED") + elseif(ARG_SHARED) + set(lib_type SHARED) + else() + set(lib_type MODULE) + endif() + + if(ARG_EXCLUDE_FROM_ALL) + set(exclude_from_all EXCLUDE_FROM_ALL) + endif() + + add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) + + if(ARG_SYSTEM) + set(inc_isystem SYSTEM) + endif() + + target_include_directories(${target_name} ${inc_isystem} + PRIVATE ${PYBIND11_INCLUDE_DIR} # from project CMakeLists.txt + PRIVATE ${pybind11_INCLUDE_DIR} # from pybind11Config + PRIVATE ${PYTHON_INCLUDE_DIRS}) + + # Python debug libraries expose slightly different objects + # https://docs.python.org/3.6/c-api/intro.html#debugging-builds + # https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib + if(PYTHON_IS_DEBUG) + target_compile_definitions(${target_name} PRIVATE Py_DEBUG) + endif() + + # The prefix and extension are provided by FindPythonLibsNew.cmake + set_target_properties(${target_name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") + set_target_properties(${target_name} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") + + # -fvisibility=hidden is required to allow multiple modules compiled against + # different pybind versions to work properly, and for some features (e.g. + # py::module_local). We force it on everything inside the `pybind11` + # namespace; also turning it on for a pybind module compilation here avoids + # potential warnings or issues from having mixed hidden/non-hidden types. + set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden") + set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden") + + if(WIN32 OR CYGWIN) + # Link against the Python shared library on Windows + target_link_libraries(${target_name} PRIVATE ${PYTHON_LIBRARIES}) + elseif(APPLE) + # It's quite common to have multiple copies of the same Python version + # installed on one's system. E.g.: one copy from the OS and another copy + # that's statically linked into an application like Blender or Maya. + # If we link our plugin library against the OS Python here and import it + # into Blender or Maya later on, this will cause segfaults when multiple + # conflicting Python instances are active at the same time (even when they + # are of the same version). + + # Windows is not affected by this issue since it handles DLL imports + # differently. The solution for Linux and Mac OS is simple: we just don't + # link against the Python library. The resulting shared library will have + # missing symbols, but that's perfectly fine -- they will be resolved at + # import time. + + target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup") + + if(ARG_SHARED) + # Suppress CMake >= 3.0 warning for shared libraries + set_target_properties(${target_name} PROPERTIES MACOSX_RPATH ON) + endif() + endif() + + # Make sure C++11/14 are enabled + if(CMAKE_VERSION VERSION_LESS 3.3) + target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD}) + else() + target_compile_options(${target_name} PUBLIC $<$:${PYBIND11_CPP_STANDARD}>) + endif() + + if(ARG_NO_EXTRAS) + return() + endif() + + _pybind11_add_lto_flags(${target_name} ${ARG_THIN_LTO}) + + if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo) + # Strip unnecessary sections of the binary on Linux/Mac OS + if(CMAKE_STRIP) + if(APPLE) + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_STRIP} -x $) + else() + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_STRIP} $) + endif() + endif() + endif() + + if(MSVC) + # /MP enables multithreaded builds (relevant when there are many files), /bigobj is + # needed for bigger binding projects due to the limit to 64k addressable sections + target_compile_options(${target_name} PRIVATE /bigobj) + if(CMAKE_VERSION VERSION_LESS 3.11) + target_compile_options(${target_name} PRIVATE $<$>:/MP>) + else() + # Only set these options for C++ files. This is important so that, for + # instance, projects that include other types of source files like CUDA + # .cu files don't get these options propagated to nvcc since that would + # cause the build to fail. + target_compile_options(${target_name} PRIVATE $<$>:$<$:/MP>>) + endif() + endif() +endfunction() diff --git a/thirdparty/ruclip/CMakeLists.txt b/thirdparty/ruclip/CMakeLists.txt new file mode 100644 index 000000000..74f991368 --- /dev/null +++ b/thirdparty/ruclip/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) +project(ruclip) + +find_package(Torch REQUIRED) + +include_directories(${CMAKE_SOURCE_DIR}/youtokentome) +include_directories(${CMAKE_SOURCE_DIR}/youtokentome/third_party) + +set(RUCLIP_SOURCE_FILES RuCLIP.cpp + RuCLIPProcessor.cpp + youtokentome/utf8.cpp + youtokentome/utils.cpp + youtokentome/bpe.cpp + ClipAPI.cpp) +set(RUCLIP_HEADER_FILES TorchHeader.h + RuCLIP.h + json.hpp + json_fwd.hpp + youtokentome/utf8.h + youtokentome/utils.h + youtokentome/bpe.h + RuCLIPProcessor.h + ClipAPI.h) + +add_library(${PROJECT_NAME} SHARED ${RUCLIP_SOURCE_FILES} ${RUCLIP_HEADER_FILES}) + +if (MSVC) + get_filename_component(full_path_nvtools_lib "C:/Program Files/NVIDIA Corporation/NvToolsExt/lib/x64/nvToolsExt64_1.lib" ABSOLUTE) + message("${full_path_nvtools_lib}") + list (REMOVE_ITEM TORCH_LIBRARIES "${full_path_nvtools_lib}") +endif(MSVC) +message("Torch libs: ${TORCH_LIBRARIES}") + +set(RUCLIP_LIBS + ${OpenCV_LIBS} + ${TORCH_LIBRARIES} +) + +target_link_libraries(${PROJECT_NAME} ${RUCLIP_LIBS}) + +install(TARGETS ${PROJECT_NAME} + EXPORT MTTrackingExports + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + PUBLIC_HEADER DESTINATION include/${PROJECT_NAME}) + +set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "libs") diff --git a/thirdparty/ruclip/ClipAPI.cpp b/thirdparty/ruclip/ClipAPI.cpp new file mode 100644 index 000000000..663230ae4 --- /dev/null +++ b/thirdparty/ruclip/ClipAPI.cpp @@ -0,0 +1,158 @@ +#include "ClipAPI.h" + +#include "TorchHeader.h" +#include "RuCLIP.h" +#include "RuCLIPProcessor.h" + +#include "../../src/mtracking/defines.h" + +/// +class ClassificationCLIP::ClassificationCLIPImpl +{ +public: + ClassificationCLIPImpl() = default; + ~ClassificationCLIPImpl() = default; + + /// + bool Init(const std::string& pathToClip, const std::string& pathToBPE, int inputImgSize, int indGPU, const std::vector& labels) + { + bool res = true; + + m_pathToClip = pathToClip; + m_indGPU = indGPU; + m_labels = labels; + + //torch::manual_seed(24); + + std::cout << "Set Torch device (" << m_indGPU << "): " << ((m_indGPU < 0) ? "CPU" : "GPU") << std::endl; + if (m_indGPU >= 0 && torch::cuda::is_available()) + { + std::cout << "CUDA is available! Running on GPU." << std::endl; + m_device = torch::Device(torch::kCUDA, m_indGPU); + } + else + { + std::cout << "CUDA is not available! Running on CPU." << std::endl; + } + + std::cout << "Load clip from: " << pathToClip << std::endl; + m_clip = FromPretrained(pathToClip); + m_clip->to(m_device); + + std::cout << "Load processor from: " << pathToBPE << std::endl; + m_processor = RuCLIPProcessor::FromPretrained(m_pathToClip); + + m_processor.CacheText(m_labels); + + return res; + } + + /// + bool ProcessFrame(const cv::Mat& frame, const std::vector& rois, std::vector& result) + { + bool res = false; + + if (rois.empty()) + return res; + + result.resize(rois.size()); + + std::map img2roi; + + std::cout << "Resizing..." << std::endl; + std::vector images; + images.reserve(rois.size()); + for (size_t i = 0; i < rois.size(); ++i) + { + cv::Rect r = Clamp(rois[i], frame.size()); + if (r.width > m_processor.GetImageSize() / 10 && r.height > m_processor.GetImageSize() / 10) + { + img2roi[images.size()] = i; + images.emplace_back(cv::Mat(frame, r)); + } + } + if (images.empty()) + { + std::cout << "CLIP::ProcessFrame: empty images" << std::endl; + return res; + } + + std::cout << "Running on " << images.size() << "..." << std::endl; + auto dummy_input = m_processor.operator()(images); + try + { + torch::Tensor logits_per_image = m_clip->forward(dummy_input.first.to(m_device), dummy_input.second.to(m_device)); + torch::Tensor logits_per_text = logits_per_image.t(); + auto probs = logits_per_image.softmax(/*dim = */-1).detach().cpu(); + //std::cout << "probs per image: " << probs << std::endl; + + const float* tensorData = reinterpret_cast(probs.data_ptr()); + for (size_t imgInd = 0; imgInd < images.size(); ++imgInd) + { + float bestConf = 0.; + size_t bestInd = 0; + for (size_t labelInd = 0; labelInd < m_labels.size(); ++labelInd) + { + if (bestConf < tensorData[labelInd]) + { + bestConf = tensorData[labelInd]; + bestInd = labelInd; + } + } + result[img2roi[imgInd]] = CLIPResult(m_labels[bestInd], bestConf); + std::cout << "Object: " << m_labels[bestInd] << " - " << bestConf << std::endl; + tensorData += m_labels.size(); + } + res = true; + } + catch (std::exception& e) + { + res = false; + std::cout << "ClassificationCLIP::ProcessFrame: " << e.what() << std::endl; + } + + return res; + } + + +private: + std::string m_pathToClip = ""; + int m_indGPU = -1; // -1 - use CPU + + torch::Device m_device{ torch::kCPU }; + CLIP m_clip = nullptr; + RuCLIPProcessor m_processor; + + std::vector m_labels{ "human", "pedestrian", "car", "vehicle", "truck", "bus" }; +}; + +/// +ClassificationCLIP::ClassificationCLIP() noexcept +{ +} + +/// +ClassificationCLIP::~ClassificationCLIP() +{ + if (m_pImpl) + delete m_pImpl; +} + +/// +bool ClassificationCLIP::Init(const std::string& pathToClip, const std::string& pathToBPE, int inputImgSize, int indGPU, const std::vector& labels) +{ + if (m_pImpl) + delete m_pImpl; + + m_pImpl = new ClassificationCLIPImpl(); + + bool res = m_pImpl->Init(pathToClip, pathToBPE, inputImgSize, indGPU, labels); + assert(res); + return res; +} + +/// +bool ClassificationCLIP::ProcessFrame(const cv::Mat& frame, const std::vector& rois, std::vector& result) +{ + return m_pImpl->ProcessFrame(frame, rois, result); +} diff --git a/thirdparty/ruclip/ClipAPI.h b/thirdparty/ruclip/ClipAPI.h new file mode 100644 index 000000000..80168a2ed --- /dev/null +++ b/thirdparty/ruclip/ClipAPI.h @@ -0,0 +1,40 @@ +#pragma once + +#include + + +#if defined(_MSC_VER) +#define LIB_API __declspec(dllexport) +#else +#define LIB_API __attribute__((visibility("default"))) +#endif + +/// +struct CLIPResult +{ + CLIPResult() = default; + CLIPResult(const std::string& label, float conf) noexcept + : m_label(label), m_conf(conf) + { + } + + std::string m_label; + float m_conf = 0.f; +}; + +/// +class LIB_API ClassificationCLIP +{ +public: + ClassificationCLIP() noexcept; + ~ClassificationCLIP() noexcept; + + bool Init(const std::string& pathToClip, const std::string& pathToBPE, int inputImgSize, int indGPU, const std::vector& labels); + + bool ProcessFrame(const cv::Mat& frame, const std::vector& rois, std::vector& result); + + class ClassificationCLIPImpl; + +private: + ClassificationCLIPImpl* m_pImpl = nullptr; +}; diff --git a/thirdparty/ruclip/RuCLIP.cpp b/thirdparty/ruclip/RuCLIP.cpp new file mode 100644 index 000000000..c64b840b1 --- /dev/null +++ b/thirdparty/ruclip/RuCLIP.cpp @@ -0,0 +1,227 @@ +#include "RuCLIP.h" + +ResidualAttentionBlockImpl :: ResidualAttentionBlockImpl(const std::string &module_name, const int d_model, const int n_head, const torch::Tensor &attn_mask) + : torch::nn::Module(module_name) +{ + Attn = torch::nn::MultiheadAttention(d_model, n_head); + Ln1 = RCLayerNorm(std::vector() = { (int64_t)d_model }); + Mlp = torch::nn::Sequential({ + {"c_fc", torch::nn::Linear(d_model, d_model * 4)}, + {"gelu", QuickGELU()}, + {"c_proj", torch::nn::Linear(d_model * 4, d_model)} + }); + Ln2 = RCLayerNorm(std::vector() = { (int64_t)d_model }); + AttnMask = attn_mask; + + register_module("attn", Attn); + register_module("ln_1", Ln1); + register_module("mlp", Mlp); + register_module("ln_2", Ln2); + //register_buffer("attn_mask", AttnMask); +} + +torch::Tensor ResidualAttentionBlockImpl :: Attention(const torch::Tensor &x) +{ + if (AttnMask.defined() && (AttnMask.numel() != 0)) + AttnMask = AttnMask.to(x.dtype()).to(x.device()); + /*return Attn(x, x, x, weights = False, attn_mask = self.attn_mask)[0];*/ + //std::tuple forward(const Tensor & query, const Tensor & key, const Tensor & value, const Tensor & key_padding_mask = {}, bool need_weights = true, const Tensor & attn_mask = {}, bool average_attn_weights = true) + return std::get<0>(Attn->forward(x, x, x, {}, false, AttnMask)); +} + +torch::Tensor ResidualAttentionBlockImpl :: forward(const torch::Tensor &x) +{ + auto result = x + Attention(Ln1(x)); + result = result + Mlp->forward(Ln2(result)); + return result; +} + + + +TransformerImpl :: TransformerImpl(const std::string &module_name, const int width, const int layers, const int heads, const torch::Tensor &attn_mask /*= torch::Tensor()*/) + : torch::nn::Module(module_name), Width(width), Layers(layers), Heads(heads)/*, AttnMask(attn_mask)*/ +{ + for (int i = 0; i < layers; i++) + Resblocks->push_back(ResidualAttentionBlock(module_name + "_" + std::to_string(i), width, heads, attn_mask)); + + register_module("resblocks", Resblocks); //??? + //for (int i = 0; i < Resblocks->size(); i++) + // register_module(module_name + "_res_attn_block_" + std::to_string(i), Resblocks[i]); +} + +torch::Tensor TransformerImpl :: forward(const torch::Tensor& x) +{ + //!!!Сделать проверку и преобразование if (x.type() != ) + return Resblocks->forward(x); +} + +void TransformerImpl :: InitializeParameters() +{ + float proj_std = powf((float)Width, -0.5f) * powf(2.f * Layers, -0.5f); + float attn_std = powf((float)Width, -0.5f); + float fc_std = powf(2.f * Width, -0.5f); + + for (int i = 0; i < Resblocks->size(); i++) + { + auto block = Resblocks[i]->as(); + torch::nn::init::normal_(block->GetAttn()->in_proj_weight, 0., attn_std); + torch::nn::init::normal_(block->GetAttn()->out_proj->weight, 0., proj_std); + auto mlp = block->GetMlp(); + for (int j = 0; j < mlp->size(); j++) + { + if (mlp[j]->name() == "c_fc") + torch::nn::init::normal_(mlp[j]->as()->weight, 0., fc_std); + if (mlp[j]->name() == "c_proj") + torch::nn::init::normal_(mlp[j]->as()->weight, 0., proj_std); + } + } +} + + + +VisionTransformerImpl :: VisionTransformerImpl( + const std::string &module_name, + const int input_resolution, + const int patch_size, + const int width, + const int layers, + const int heads, + const int output_dim +) : torch::nn::Module(module_name), InputResolution(input_resolution), OutputDim(output_dim) +{ + Conv1 = torch::nn::Conv2d(torch::nn::Conv2dOptions(3, width, patch_size).stride(patch_size).bias(false)); + float scale = powf((float)width, -0.5); + ClassEmbedding = scale * torch::randn(width); + PositionalEmbedding = scale * torch::randn({ (int)pow(input_resolution / patch_size/*деление нацело*/, 2) + 1, width }); + LnPre = RCLayerNorm(std::vector() = { (int64_t)width }); + VTTransformer = Transformer("visual", width, layers, heads); + LnPost = RCLayerNorm(std::vector() = { (int64_t)width }); + Proj = scale * torch::randn({ width, output_dim }); + + register_buffer("class_embedding", ClassEmbedding); + register_buffer("positional_embedding", PositionalEmbedding); + register_buffer("proj", Proj); + register_module("conv1", Conv1); + register_module("ln_pre", LnPre); + register_module("ln_post", LnPost); + register_module("transformer", VTTransformer); +} + +torch::Tensor VisionTransformerImpl :: forward(const torch::Tensor &x) +{ + //!!!Сделать проверку и преобразование if (x.type() != ) + auto res = Conv1(x); //shape = [*, width, grid, grid] + res = res.reshape({ res.sizes()[0], res.sizes()[1], -1 }); //shape = [*, width, grid **2] + res = res.permute({ 0, 2, 1 }); //shape = [*, grid **2, width] + res = torch::cat({ + ClassEmbedding.to(res.dtype()) + torch::zeros({res.sizes()[0], 1, res.sizes().back()}, res.dtype()).to(x.device()), + res + }, 1); //shape = [*, grid **2 + 1, width] + res = res + PositionalEmbedding.to(res.dtype()); + res = LnPre(res); + res = res.permute({ 1, 0, 2 }); // NLD->LND + res = VTTransformer(res); + res = res.permute({ 1, 0, 2 }); // LND->NLD + res = LnPost(res.index({ torch::indexing::Slice(), 0, torch::indexing::Slice() })); + if (Proj.defined() && Proj.numel() != 0) + res = torch::mm(res, Proj); + return res; +} + + + +CLIPImpl :: CLIPImpl( + const std::string &module_name, + const int embed_dim, + const int image_resolution, + const int vision_layers, + const int vision_width, + const int vision_patch_size, + const int context_length, + const int vocab_size, + const int transformer_width, + const int transformer_heads, + const int transformer_layers, + const int eos_id /*= 3*/ +) : torch::nn::Module(module_name), EosId(eos_id), ContextLength(context_length), VocabSize(vocab_size), TransformerWidth(transformer_width), TransformerLayers(transformer_layers) +{ + int vision_heads = vision_width / 64; + Visual = VisionTransformer("visual", image_resolution, vision_patch_size, vision_width, vision_layers, vision_heads, embed_dim); + NVTransformer = Transformer("transformer", transformer_width, transformer_layers, transformer_heads, BuildAttentionMask()); + + TokenEmbedding = torch::nn::Embedding(vocab_size, transformer_width); + PositionalEmbedding = torch::empty({ context_length, transformer_width }); //!!!type, device + + std::cout << "transformer_width: " << transformer_width<< std::endl; + + LnFinal = RCLayerNorm(std::vector() = { (int64_t)transformer_width }); + TextProjection = torch::empty({ transformer_width, embed_dim }); //!!!type, device + LogitScale = torch::ones({}) * logf(1.f / 0.07f); + + register_module("visual", Visual); + register_module("transformer", NVTransformer); + register_module("token_embedding", TokenEmbedding); + register_module("ln_final", LnFinal); + register_buffer("positional_embedding", PositionalEmbedding); + register_buffer("text_projection", TextProjection); + register_buffer("logit_scale", LogitScale); + + InitializeParameters(); +} + +void CLIPImpl :: InitializeParameters() +{ + torch::nn::init::normal_(TokenEmbedding->weight, 0., 0.02); + torch::nn::init::normal_(PositionalEmbedding, 0., 0.01); + NVTransformer->InitializeParameters(); + if (TextProjection.defined() && TextProjection.numel() != 0) + torch::nn::init::normal_(TextProjection, 0., pow(TransformerWidth, -0.5)); +} + +torch::Tensor CLIPImpl :: BuildAttentionMask() +{ + auto mask = torch::empty({ ContextLength, ContextLength }); + mask.fill_(-std::numeric_limits::infinity()); + mask.triu_(1); + return mask; +} + +///pixel_values : torch::Tensor Processed images from RuCLIPProcessor class, out: image_latents : torch::Tensor Image embeddings +torch::Tensor CLIPImpl :: EncodeImage(torch::Tensor pixel_values) +{ + return Visual(pixel_values); +} + +///input_ids : torch::Tensor Tokenized texts from RuCLIPProcessor class, out: text_latents : torch::Tensor Text embeddings +torch::Tensor CLIPImpl :: EncodeText(torch::Tensor input_ids) +{ + auto x = TokenEmbedding(input_ids); //.type(dtype()) // [batch_size, n_ctx, d_model] + x = x + PositionalEmbedding; //.type(dtype()) + x = x.permute({ 1, 0, 2 }); //NLD->LND + x = NVTransformer(x); + x = x.permute({ 1, 0, 2 }); //LND->NLD + x = LnFinal(x); // type(self.dtype) //x.shape = [batch_size, n_ctx, transformer.width] + x = torch::mm(x.index({ torch::arange(x.sizes()[0]), torch::where(input_ids == EosId)[1] }), TextProjection); + return x; +} + +torch::Tensor CLIPImpl :: forward(torch::Tensor input_ids, torch::Tensor pixel_values) +{ + //std::cout << "pixel_values: " << pixel_values.sizes() << ", input_ids: " << input_ids.sizes() << std::endl; + + auto image_features = EncodeImage(pixel_values); + auto text_features = EncodeText(input_ids); + + //std::cout << "image_features: " << image_features.sizes() << ", text_features: " << text_features.sizes() << std::endl; + + //normalize features + image_features = image_features / image_features.norm(2/*L2*/, -1, true); + text_features = text_features / text_features.norm(2/*L2*/, -1, true); + + //cosine similarity as logits + auto scale = LogitScale.exp(); + auto logits_per_image = scale * torch::mm(image_features, text_features.t()); + auto logits_per_text = logits_per_image.t(); + + return logits_per_image; +} diff --git a/thirdparty/ruclip/RuCLIP.h b/thirdparty/ruclip/RuCLIP.h new file mode 100644 index 000000000..e0996183b --- /dev/null +++ b/thirdparty/ruclip/RuCLIP.h @@ -0,0 +1,194 @@ +#pragma once + +#include "json.hpp" +#include +#include + +#include "TorchHeader.h" + +///to handle fp16 +class RCLayerNormImpl : public torch::nn::LayerNormImpl { +protected: +public: + RCLayerNormImpl(std::vector normalized_shape) : LayerNormImpl(normalized_shape) {} + virtual ~RCLayerNormImpl() {} + + torch::Tensor forward(const torch::Tensor &x) ///!override + { + auto orig_type = x.dtype(); + auto result = torch::nn::LayerNormImpl::forward(x.to(torch::kFloat32)); + return result.to(orig_type); + } +}; +TORCH_MODULE(RCLayerNorm); + + +class QuickGELUImpl : public torch::nn::Module { +protected: +public: + QuickGELUImpl() : torch::nn::Module() {} + virtual ~QuickGELUImpl() {} + + torch::Tensor forward(const torch::Tensor &x) + { + return x * torch::sigmoid(1.702f * x); + } +}; +TORCH_MODULE(QuickGELU); + + +class ResidualAttentionBlockImpl : public torch::nn::Module { +protected: + torch::nn::MultiheadAttention Attn{ nullptr }; + RCLayerNorm Ln1{ nullptr }; + torch::nn::Sequential Mlp{ nullptr }; + RCLayerNorm Ln2{ nullptr }; + torch::Tensor AttnMask; +public: + ResidualAttentionBlockImpl(const std::string& module_name, const int d_model, const int n_head, const torch::Tensor& attn_mask); + virtual ~ResidualAttentionBlockImpl() {} + torch::Tensor Attention(const torch::Tensor &x); + torch::Tensor forward(const torch::Tensor &x); + torch::nn::MultiheadAttention GetAttn() { return Attn; } + torch::nn::Sequential GetMlp() { return Mlp; } +}; +TORCH_MODULE(ResidualAttentionBlock); + + +class TransformerImpl : public torch::nn::Module { +protected: + int Width, + Layers, + Heads; + //torch::Tensor AttnMask; + torch::nn::Sequential Resblocks; +public: + TransformerImpl(const std::string &module_name, const int width, const int layers, const int heads, const torch::Tensor &attn_mask = torch::Tensor()); + virtual ~TransformerImpl() {} + torch::Tensor forward(const torch::Tensor &x); + void InitializeParameters(); +}; +TORCH_MODULE(Transformer); + + +class VisionTransformerImpl : public torch::nn::Module { +protected: + int InputResolution, + OutputDim; + torch::nn::Conv2d Conv1{ nullptr }; + torch::Tensor ClassEmbedding, + PositionalEmbedding, + Proj; + RCLayerNorm LnPre{ nullptr }, + LnPost{ nullptr }; + Transformer VTTransformer{ nullptr }; +public: + VisionTransformerImpl( + const std::string& module_name, + const int input_resolution, + const int patch_size, + const int width, + const int layers, + const int heads, + const int output_dim + ); + virtual ~VisionTransformerImpl() {} + torch::Tensor forward(const torch::Tensor& x); +}; +TORCH_MODULE(VisionTransformer); + + +class CLIPImpl : public torch::nn::Module { +protected: + int EosId, + //EmbedDim, + //ImageResolution, + //VisionLayers, + //VisionWidth, + //VisionPatchSize, + ContextLength, + VocabSize, + TransformerWidth, + /*TransformerHeads,*/ + TransformerLayers; + VisionTransformer Visual{ nullptr }; + Transformer NVTransformer{ nullptr }; + torch::nn::Embedding TokenEmbedding{ nullptr }; + torch::Tensor PositionalEmbedding; + RCLayerNorm LnFinal{ nullptr }; + torch::Tensor TextProjection, + LogitScale; +public: + CLIPImpl( + const std::string &module_name, + const int embed_dim, + const int image_resolution, + const int vision_layers, + const int vision_width, + const int vision_patch_size, + const int context_length, + const int vocab_size, + const int transformer_width, + const int transformer_heads, + const int transformer_layers, + const int eos_id = 3 + ); + virtual ~CLIPImpl() {} + + void InitializeParameters(); + torch::Tensor BuildAttentionMask(); + + //auto dtype() + //{ + // return Visual.conv1.weight.dtype(); + //} + + ///pixel_values : torch::Tensor Processed images from RuCLIPProcessor class, out: image_latents : torch::Tensor Image embeddings + torch::Tensor EncodeImage(torch::Tensor pixel_values); + + ///input_ids : torch::Tensor Tokenized texts from RuCLIPProcessor class, out: text_latents : torch::Tensor Text embeddings + torch::Tensor EncodeText(torch::Tensor input_ids); + + torch::Tensor forward(torch::Tensor input_ids, torch::Tensor pixel_values); + + torch::Tensor GetLogitScale() { return LogitScale; } +}; +TORCH_MODULE(CLIP); + +inline CLIP FromPretrained(const std::filesystem::path &folder) +{ + using json = nlohmann::json; + std::filesystem::path path = folder / "config.json"; + std::cout << path << std::endl; + std::ifstream f(path); + json config = json::parse(f); + + // Создание модели + auto clip = CLIP("ruclip", + int(config["embed_dim"]), + int(config["image_resolution"]), + int(config["vision_layers"]), + int(config["vision_width"]), + int(config["vision_patch_size"]), + int(config["context_length"]), + int(config["vocab_size"]), + int(config["transformer_width"]), + int(config["transformer_heads"]), + int(config["transformer_layers"])); + + //for (auto &k : clip->named_parameters()) + // std::cout << k.key() << std::endl; + //std::cout << "Model params count: " << Trainable::ParamsCount(clip) << std::endl; + + // Загрузка состояния модели из файла + try + { + torch::load(clip, (folder / "jit_model.zip").string()); + } + catch (std::exception& e) + { + std::cout << e.what() << std::endl; + } + + return clip; +} diff --git a/thirdparty/ruclip/RuCLIPProcessor.cpp b/thirdparty/ruclip/RuCLIPProcessor.cpp new file mode 100644 index 000000000..1167df800 --- /dev/null +++ b/thirdparty/ruclip/RuCLIPProcessor.cpp @@ -0,0 +1,175 @@ +#include "RuCLIPProcessor.h" + +/// +inline torch::Tensor CVMatToTorchTensor(const cv::Mat img, const bool perm = true) +{ + auto tensor_image = torch::from_blob(img.data, { img.rows, img.cols, img.channels() }, at::kByte); + if (perm) + tensor_image = tensor_image.permute({ 2,0,1 }); + tensor_image.unsqueeze_(0); + tensor_image = tensor_image.toType(c10::kFloat).div(255); + return tensor_image; //tensor_image.clone(); +} + +/// +inline cv::Mat TorchTensorToCVMat(const torch::Tensor tensor_image, const bool perm = true) +{ + auto t = tensor_image.detach().squeeze().cpu(); + if (perm) + t = t.permute({ 1, 2, 0 }); + t = t.mul(255).clamp(0, 255).to(torch::kU8); + t = t.contiguous(); + cv::Mat result_img; + cv::Mat(static_cast(t.size(0)), static_cast(t.size(1)), CV_MAKETYPE(CV_8U, t.sizes().size() >= 3 ? static_cast(t.size(2)) : 1), t.data_ptr()).copyTo(result_img); + return result_img; +} + +/// +RuCLIPProcessor :: RuCLIPProcessor( + const std::string& tokenizer_path, + const int image_size /*= 224*/, + const int text_seq_length /*= 77*/, + const std::vector norm_mean /*= { 0.48145466, 0.4578275, 0.40821073 }*/, + const std::vector norm_std /*= { 0.26862954, 0.26130258, 0.27577711 }*/ +) : ImageSize(image_size), TextSeqLength(text_seq_length), NormMean(norm_mean), NormStd(norm_std) +{ + vkcom::Status status; + Tokenizer = std::make_unique(tokenizer_path, -1, &status); +} + +///!!!Локали-юникоды +torch::Tensor RuCLIPProcessor :: EncodeText(const/*std::vector<*/std::string &text) const +{ + std::vector> ret_ids; + vkcom::Status status; + ////for (auto &it : text) + //// it = lowercase(it); + //text = lowercase(text); + //output_type = vkcom::OutputType::ID, bos = false, eos = false, reverse = false, dropout_prob = 0.0 + std::vector texts{ text }; + status = Tokenizer->encode_as_ids(texts, &ret_ids); + if (status.code != 0) + throw std::runtime_error("RuCLIPProcessor::EncodeText error : " + status.message); + auto it = ret_ids[0]; + //for (auto &it : ret_ids) + //{ + if (it.size() > TextSeqLength - 2) + it.resize(TextSeqLength - 2); + it.insert(it.begin(), bos_id); //vector сдвинет при вставке + it.push_back(eos_id); + //} + return PrepareTokens(it); +} + +/// +cv::Mat RuCLIPProcessor::ResizeToInput(const cv::Mat& img, bool saveAspectRatio) const +{ + cv::Mat newImg(cv::Size(ImageSize, ImageSize), img.type(), cv::Scalar(0, 0, 0)); + + if (saveAspectRatio) + { + // resize the image with aspect ratio + float r = std::min(static_cast(ImageSize) / static_cast(img.rows), static_cast(ImageSize) / static_cast(img.cols)); + int newHeight = cvRound(img.rows * r); + int newWidth = cvRound(img.cols * r); + + // Additional checks for images with non even dims + if ((ImageSize - newWidth) % 2) + newWidth--; + if ((ImageSize - newHeight) % 2) + newHeight--; + assert((ImageSize - newWidth) % 2 == 0); + assert((ImageSize - newHeight) % 2 == 0); + + int xOffset = (ImageSize - newWidth) / 2; + int yOffset = (ImageSize - newHeight) / 2; + + assert(2 * xOffset + newWidth == ImageSize); + assert(2 * yOffset + newHeight == ImageSize); + + cv::resize(img, newImg(cv::Rect(xOffset, yOffset, newWidth, newHeight)), cv::Size(newWidth, newHeight), 0, 0, cv::INTER_CUBIC); + } + else + { + cv::resize(img, newImg, newImg.size(), 0, 0, cv::INTER_CUBIC); + } + return newImg; +} + +/// +torch::Tensor RuCLIPProcessor::EncodeImage(const cv::Mat& img) const +{ + torch::Tensor img_tensor = CVMatToTorchTensor(ResizeToInput(img), true); + img_tensor = torch::data::transforms::Normalize<>(NormMean, NormStd)(img_tensor); + return img_tensor; +} + +/// +torch::Tensor RuCLIPProcessor::PrepareTokens(/*std::vector<*/std::vector tokens) const //Передаю по значению чтобы внутри иметь дело с копией +{ + torch::Tensor result; + if (tokens.size() > TextSeqLength) + { + int32_t back = tokens.back(); + tokens.resize(TextSeqLength); + tokens.back() = back; + } + int empty_positions = TextSeqLength - static_cast(tokens.size()); + if (empty_positions > 0) + result = torch::cat({ torch::tensor(tokens, torch::kLong), torch::zeros(empty_positions, torch::kLong) }); //position tokens after text + return result; +} + +/// +void RuCLIPProcessor::CacheText(const std::vector & texts) +{ + m_textsTensors.clear(); + for (auto& it : texts) + { + std::string s = it; + torch::Tensor text_tensor = EncodeText(s); + m_textsTensors.push_back(text_tensor); + } +} + +/// +const std::vector& RuCLIPProcessor::GetTextTensors() const +{ + return m_textsTensors; +} + +/// +std::pair RuCLIPProcessor::operator()(const std::vector &texts, const std::vector &images) const +{ + std::vector texts_tensors; + for (auto& it : texts) + { + std::string s = it; + torch::Tensor text_tensor = EncodeText(s); + texts_tensors.push_back(text_tensor); + } + + std::vector images_tensors; + for (auto &it : images) + { + torch::Tensor img_tensor = CVMatToTorchTensor(ResizeToInput(it), true); + img_tensor = torch::data::transforms::Normalize<>(NormMean, NormStd)(img_tensor); + //img_tensor.clone(); + images_tensors.push_back(img_tensor); + } + return std::make_pair(!texts_tensors.empty()?/*torch::pad_sequence*/torch::stack(texts_tensors):torch::Tensor(), torch::pad_sequence(images_tensors).squeeze(0)); +} + +/// +std::pair RuCLIPProcessor::operator()(const std::vector & images) const +{ + std::vector images_tensors; + for (auto& it : images) + { + torch::Tensor img_tensor = CVMatToTorchTensor(ResizeToInput(it), true); + img_tensor = torch::data::transforms::Normalize<>(NormMean, NormStd)(img_tensor); + //img_tensor.clone(); + images_tensors.push_back(img_tensor); + } + return std::make_pair(torch::stack(m_textsTensors), torch::pad_sequence(images_tensors).squeeze(0)); +} diff --git a/thirdparty/ruclip/RuCLIPProcessor.h b/thirdparty/ruclip/RuCLIPProcessor.h new file mode 100644 index 000000000..780231166 --- /dev/null +++ b/thirdparty/ruclip/RuCLIPProcessor.h @@ -0,0 +1,149 @@ +#pragma once + +#include "json.hpp" +#include "youtokentome/bpe.h" +#include "TorchHeader.h" + +#include +#include +#include + +/// +class RuCLIPProcessor +{ +public: + RuCLIPProcessor() = default; + + RuCLIPProcessor(const std::string& tokenizer_path, + const int image_size = 224, + const int text_seq_length = 77, + const std::vector norm_mean = { 0.48145466, 0.4578275, 0.40821073 }, + const std::vector norm_std = { 0.26862954, 0.26130258, 0.27577711 }); + ~RuCLIPProcessor() = default; + + /// + RuCLIPProcessor& operator=(RuCLIPProcessor&& processor) noexcept + { + eos_id = processor.eos_id; + bos_id = processor.bos_id; + unk_id = processor.unk_id; + pad_id = processor.pad_id; + ImageSize = processor.ImageSize; + TextSeqLength = processor.TextSeqLength; + NormMean = processor.NormMean; + NormStd = processor.NormStd; + + Tokenizer = std::move(processor.Tokenizer); + + m_textsTensors = processor.m_textsTensors; + + return *this; + } + + ///!!!Локали-юникоды + torch::Tensor EncodeText(const /*std::vector<*/std::string &text) const; + torch::Tensor PrepareTokens(/*std::vector<*/std::vector tokens) const; //Передаю по значению чтобы внутри иметь дело с копией + torch::Tensor EncodeImage(const cv::Mat& img) const; + std::pair operator()(const std::vector & texts, const std::vector & images) const; + std::pair operator()(const std::vector & images) const; + + void CacheText(const std::vector & texts); + + /// + int GetImageSize() const noexcept + { + return ImageSize; + } + + /// + static RuCLIPProcessor FromPretrained(const std::filesystem::path &folder) + { + std::filesystem::path tokenizer_path = folder / "bpe.model"; + using json = nlohmann::json; + std::cout << tokenizer_path << std::endl; + std::ifstream f(folder / "config.json"); + json config = json::parse(f); + + auto mean = config["mean"].template get>(); + auto std = config["std"].template get>(); + + return RuCLIPProcessor(tokenizer_path.string(), + int(config["image_resolution"]), + int(config["context_length"]), + mean, std); + } + + const std::vector& GetTextTensors() const; + +private: + uint32_t eos_id = 3; + uint32_t bos_id = 2; + uint32_t unk_id = 1; + uint32_t pad_id = 0; + int ImageSize{ 224 }; + int TextSeqLength{ 77 }; + std::vector NormMean; + std::vector NormStd; + std::unique_ptr Tokenizer; + + std::vector m_textsTensors; + + /// + cv::Mat ResizeToInput(const cv::Mat& img, bool saveAspectRatio = true) const; +}; + +///relevancy for batch size == 1 at this moment, float lv = result.index({0,0}).item(); +/// +///std::vector canon_texts_tensors; +///canon_texts_tensors.push_back(ClipProcessor->EncodeText(std::string("объект"))); +///canon_texts_tensors.push_back(ClipProcessor->EncodeText(std::string("вещи"))); +///canon_texts_tensors.push_back(ClipProcessor->EncodeText(std::string("текстура"))); +///int negatives_len = (int)canon_texts_tensors.size(); +///auto canon_features = Clip->EncodeText(torch::stack(canon_texts_tensors).to(Device)).to(torch::kCPU); ///[3, 768] +///canon_features = canon_features / canon_features.norm(2/*L2*/, -1, true); +///auto input = ClipProcessor->EncodeText(std::string("малый барабан")); +///auto text_features = Clip->EncodeText(input.unsqueeze(0).to(Device)).to(torch::kCPU); ///[1, 768] +///text_features = text_features / text_features.norm(2/*L2*/, -1, true); +///torch::Tensor image_features = PyramidClipEmbedding.GetPixelValue(i,j,0.5f,img_id,pyramid_embedder_properties,cv::Size(data.W, data.H)).to(torch::kCPU); +///image_features = image_features / image_features.norm(2/*L2*/, -1, true); +///torch::Tensor rel = Relevancy(image_features, text_features, canon_features); +///float lv = rel.index({0,0}).item(); + +inline torch::Tensor Relevancy(torch::Tensor embeds, torch::Tensor positives, torch::Tensor negatives) +{ +#if 0 + std::cout << "Relevancy: 0" << std::endl; + auto embeds2 = torch::cat({ positives, negatives }); + std::cout << "Relevancy: 1" << std::endl; + auto logits = /*scale * */torch::mm(embeds, embeds2.t()); //[batch_size x phrases] + std::cout << "Relevancy: 2" << std::endl; + auto positive_vals = logits.index({ "...", torch::indexing::Slice(0, positives.sizes()[0]) }); // [batch_size x 1] + std::cout << "Relevancy: 3" << std::endl; + auto negative_vals = logits.index({ "...", torch::indexing::Slice(positives.sizes()[0], torch::indexing::None) }); // [batch_size x negative_phrase_n] + std::cout << "Relevancy: 4" << std::endl; + auto repeated_pos = positive_vals.repeat({ 1, negatives.sizes()[0] }); //[batch_size x negative_phrase_n] + std::cout << "Relevancy: 5: repeated_pos: " << repeated_pos.sizes() << ", negative_vals: " << negative_vals.sizes() << std::endl; + auto sims = torch::stack({ repeated_pos, negative_vals }, -1); //[batch_size x negative_phrase_n x 2] + std::cout << "Relevancy: 6" << std::endl; + auto smx = torch::softmax(10 * sims, -1); // [batch_size x negative_phrase_n x 2] + std::cout << "Relevancy: 7" << std::endl; + auto best_id = smx.index({ "...", 0 }).argmin(1); // [batch_size x 2] + std::cout << "Relevancy: 8" << std::endl; + auto result = torch::gather(smx, 1, best_id.index({ "...", torch::indexing::None, torch::indexing::None }).expand({ best_id.sizes()[0], negatives.sizes()[0], 2 }) + ).index({ torch::indexing::Slice(), 0, torch::indexing::Slice() });// [batch_size x 2] + return result; +#else + auto embeds2 = torch::cat({ positives, negatives }, 0); + auto logits = torch::mm(embeds, embeds2.t()); // [batch_size, 1 + negatives_len] + auto positive_vals = logits.index({ "...", torch::indexing::Slice(0, 1) }); // [batch_size, 1] + auto negative_vals = logits.index({ "...", torch::indexing::Slice(1, torch::indexing::None) }); // [batch_size, negatives_len] + auto repeated_pos = positive_vals.repeat({ 1, negatives.sizes()[0] }); // [batch_size, negatives_len] + auto sims = torch::stack({ repeated_pos, negative_vals }, -1); // [batch_size, negatives_len, 2] + auto smx = torch::softmax(10 * sims, -1); // [batch_size, negatives_len, 2] + //Находим индекс самого сложного негатива (с минимальной вероятностью позитивного класса) + auto best_id = smx.index({ "...", 0 }).argmin(1, /*keepdim=*/true); // [batch_size, 1] + //Собираем результаты для выбранных негативов + auto result = torch::gather(smx, 1, best_id.unsqueeze(-1).expand({ -1, -1, 2 })); + return result.squeeze(1); // [batch_size, 2] +#endif +} diff --git a/thirdparty/ruclip/TorchHeader.h b/thirdparty/ruclip/TorchHeader.h new file mode 100644 index 000000000..dce611ab0 --- /dev/null +++ b/thirdparty/ruclip/TorchHeader.h @@ -0,0 +1,27 @@ +#pragma once + +#if defined(_MSC_VER) +#define DISABLE_WARNING_PUSH __pragma(warning( push )) +#define DISABLE_WARNING_POP __pragma(warning( pop )) +#define DISABLE_WARNING(warningNumber) __pragma(warning( disable : warningNumber )) +#elif defined(__GNUC__) || defined(__clang__) +#define DO_PRAGMA(X) _Pragma(#X) +#define DISABLE_WARNING_PUSH DO_PRAGMA(GCC diagnostic push) +#define DISABLE_WARNING_POP DO_PRAGMA(GCC diagnostic pop) +#define DISABLE_WARNING(warningName) DO_PRAGMA(GCC diagnostic ignored #warningName) +#else +#define DISABLE_WARNING_PUSH +#define DISABLE_WARNING_POP +#define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER +#define DISABLE_WARNING_UNREFERENCED_FUNCTION +#endif + +DISABLE_WARNING_PUSH + +#if defined(_MSC_VER) +DISABLE_WARNING(4624) +#endif + +#include + +DISABLE_WARNING_POP diff --git a/thirdparty/ruclip/json.hpp b/thirdparty/ruclip/json.hpp new file mode 100644 index 000000000..2448bf22d --- /dev/null +++ b/thirdparty/ruclip/json.hpp @@ -0,0 +1,24640 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file docs/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 2 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann +#endif + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // nullptr_t +#include // exception +#if JSON_DIAGNOSTICS + #include // accumulate +#endif +#include // runtime_error +#include // to_string +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // declval, pair +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +// https://en.cppreference.com/w/cpp/experimental/is_detected +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + + +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson +// SPDX-License-Identifier: MIT + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + + +// This file contains all internal macro definitions (except those affecting ABI) +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// #include + + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#ifdef __has_include + #if __has_include() + #include + #endif +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1914 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ + && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + +#ifndef JSON_HAS_RANGES + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error + #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 + #define JSON_HAS_RANGES 0 + #elif defined(__cpp_lib_ranges) + #define JSON_HAS_RANGES 1 + #else + #define JSON_HAS_RANGES 0 + #endif +#endif + +#ifdef JSON_HAS_CPP_17 + #define JSON_INLINE_VARIABLE inline +#else + #define JSON_INLINE_VARIABLE +#endif + +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow disabling exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow overriding assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType, \ + class CustomBaseClass> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DISABLE_ENUM_SERIALIZATION + #define JSON_DISABLE_ENUM_SERIALIZATION 0 +#endif + +#ifndef JSON_USE_GLOBAL_UDLS + #define JSON_USE_GLOBAL_UDLS 1 +#endif + +#if JSON_HAS_THREE_WAY_COMPARISON + #include // partial_ordering +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +#if JSON_HAS_THREE_WAY_COMPARISON + inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* +#else + inline bool operator<(const value_t lhs, const value_t rhs) noexcept +#endif +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); +#if JSON_HAS_THREE_WAY_COMPARISON + if (l_index < order.size() && r_index < order.size()) + { + return order[l_index] <=> order[r_index]; // *NOPAD* + } + return std::partial_ordering::unordered; +#else + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +#endif +} + +// GCC selects the built-in operator< over an operator rewritten from +// a user-defined spaceship operator +// Clang, MSVC, and ICC select the rewritten candidate +// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) +#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + return std::is_lt(lhs <=> rhs); // *NOPAD* +} +#endif + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != StringType::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +template +inline StringType escape(StringType s) +{ + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +template +static void unescape(StringType& s) +{ + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // size_t + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-FileCopyrightText: 2018 The Abseil Authors +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static JSON_INLINE_VARIABLE constexpr T value{}; +}; + +#ifndef JSON_HAS_CPP_17 + template + constexpr T static_const::value; +#endif + +template +inline constexpr std::array make_array(Args&& ... args) +{ + return std::array {{static_cast(std::forward(args))...}}; +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // random_access_iterator_tag + +// #include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); + +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); + +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ + #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + #include // int64_t, uint64_t + #include // map + #include // allocator + #include // string + #include // vector + + // #include + + + /*! + @brief namespace for Niels Lohmann + @see https://github.com/nlohmann + @since version 1.0.0 + */ + NLOHMANN_JSON_NAMESPACE_BEGIN + + /*! + @brief default JSONSerializer template argument + + This serializer ignores the template arguments and uses ADL + ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) + for serialization. + */ + template + struct adl_serializer; + + /// a class to store JSON values + /// @sa https://json.nlohmann.me/api/basic_json/ + template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector, // cppcheck-suppress syntaxError + class CustomBaseClass = void> + class basic_json; + + /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document + /// @sa https://json.nlohmann.me/api/json_pointer/ + template + class json_pointer; + + /*! + @brief default specialization + @sa https://json.nlohmann.me/api/json/ + */ + using json = basic_json<>; + + /// @brief a minimal map-like container that preserves insertion order + /// @sa https://json.nlohmann.me/api/ordered_map/ + template + struct ordered_map; + + /// @brief specialization that maintains the insertion order of object keys + /// @sa https://json.nlohmann.me/api/ordered_json/ + using ordered_json = basic_json; + + NLOHMANN_JSON_NAMESPACE_END + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +NLOHMANN_JSON_NAMESPACE_BEGIN +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ + +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +// used by exceptions create() member functions +// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// false_type otherwise +template +struct is_basic_json_context : + std::integral_constant < bool, + is_basic_json::type>::type>::value + || std::is_same::value > +{}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B { }; +template +struct conjunction +: std::conditional(B::value), conjunction, B>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type +{ + // launder type through decltype() to fix compilation failure on ICPC +#ifdef __INTEL_COMPILER + using laundered_type = decltype(std::declval()); +#else + using laundered_type = ConstructibleStringType; +#endif + + static constexpr auto value = + conjunction < + is_constructible, + is_detected_exact>::value; +}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < + is_detected::value&& + is_iterator_traits>>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 + !std::is_same>::value >> +{ + static constexpr bool value = + is_constructible>::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 +!std::is_same>::value&& + is_complete_type < + detected_t>::value >> +{ + using value_type = range_value_t; + + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +template +struct is_json_iterator_of : std::false_type {}; + +template +struct is_json_iterator_of : std::true_type {}; + +template +struct is_json_iterator_of : std::true_type +{}; + +// checks if a given type T is a template specialization of Primary +template