├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── CM_definitions.cmake ├── CM_dependencies.cmake ├── CM_package.cmake ├── CM_source.cmake ├── CM_version.cmake ├── CMakeLists.txt ├── build_linux.sh ├── build_windows.cmd ├── example_project ├── CMakeLists.txt ├── example.cpp └── readme.txt ├── license.txt ├── readme.md ├── readme_linux.txt ├── readme_windows.txt ├── src-apps ├── CMakeLists.txt ├── display_single_image.cpp ├── display_single_image_custom_settings.cpp ├── display_single_image_snapping.cpp ├── display_using_bundle.cpp ├── process_many_images_on_threads.cpp ├── process_single_image.cpp ├── process_using_bundle_and_dhthreads.cpp ├── process_video_webcam.cpp ├── readme.md ├── resize_corners.cpp ├── rotate_images.cpp ├── save_webcam_to_video.cpp ├── using_c_api.cpp ├── video_display_realtime.cpp └── video_object_counter.cpp ├── src-cam ├── CMakeLists.txt ├── Cam.cpp ├── CamOptions.cpp └── CamOptions.hpp ├── src-doc ├── 01_mainpage.dox ├── 02_summary.dox ├── 03_changelist.dox ├── 03_license.dox ├── 04_building_darkhelp.dox ├── 05_darkhelp_api.dox ├── 05_darkhelp_c_api.dox ├── 05_darkhelp_python_api.dox ├── 06_darkhelp_tool.dox ├── 07_parms.dox ├── 08_darkhelp_server.dox ├── 08_darkhelp_webcam.dox ├── 08_shell_script.dox ├── 09_tiling.dox ├── CMakeLists.txt ├── DarkHelp.png ├── Doxyfile.in ├── auto_hide_labels_false.png ├── auto_hide_labels_true.png ├── barcode_100_percent.png ├── barcode_with_timestamp.png ├── darkhelp_cam.jpg ├── darkmark_build_thumbnail.png ├── driving_annotations.png ├── footer_ccoderun.html ├── footer_doxygen.html ├── header_ccoderun.html ├── include_percentage_false.png ├── include_percentage_true.png ├── mailbox_13.png ├── mailbox_13_annotated.png ├── mailbox_13_invalid_coordinates.png ├── mailboxes.png ├── mailboxes_1024x768_and_416x320.png ├── mailboxes_2x2_tiles.png ├── mailboxes_2x2_tiles_detection.png ├── mailboxes_detection.png ├── mailboxes_suppress.png ├── pixelate_off.png ├── pixelate_size_05.png ├── pixelate_size_15.png ├── pixelate_size_25.png ├── red_swirl_darknet_75x75.png ├── shade_0pcnt.png ├── shade_100pcnt.png ├── shade_25pcnt.png ├── shade_50pcnt.png ├── shade_75pcnt.png ├── snapping_disabled.png ├── snapping_enabled.png ├── snapping_h01_v01.png ├── snapping_h02_v01.png ├── snapping_h03_v01.png ├── snapping_h07_v01.png ├── snapping_h07_v03.png ├── snapping_h07_v15.png ├── swirl_75x75_red.jpg ├── tile_combine_1.png ├── tile_combine_2.png ├── tile_combine_3.png ├── tile_combine_4.png ├── tile_combine_similar_0.png ├── tile_combine_similar_1.png ├── tile_combine_similar_2.png ├── tile_combine_similar_3.png ├── tile_combine_similar_4.png ├── tile_combine_similar_5.png ├── tile_edge_0.png ├── tile_edge_1.png ├── tile_edge_2.png ├── tile_rect_0.png ├── tile_rect_1.png ├── tile_rect_2.png ├── tracking_cars.jpg ├── tracking_pigs.jpg └── xkcd_bike.png ├── src-lib ├── CMakeLists.txt ├── DarkHelp.hpp ├── DarkHelpConfig.cpp ├── DarkHelpConfig.hpp ├── DarkHelpNN.cpp ├── DarkHelpNN.hpp ├── DarkHelpPositionTracker.cpp ├── DarkHelpPositionTracker.hpp ├── DarkHelpPredictionResult.cpp ├── DarkHelpPredictionResult.hpp ├── DarkHelpThreads.cpp ├── DarkHelpThreads.hpp ├── DarkHelpUtils.cpp ├── DarkHelpUtils.hpp ├── DarkHelp_C_API.cpp └── DarkHelp_C_API.h ├── src-python ├── DarkHelp.py └── example.py ├── src-tool ├── CMakeLists.txt ├── DarkHelp.ico ├── DarkHelp.rc ├── DarkHelpCli.cpp ├── DarkHelpCombine.cpp ├── DarkHelpServer.cpp └── json.hpp └── version.txt /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | indent_style = tab 11 | indent_size = 4 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: stephanecharette # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['paypal.me/ccoderun'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.kdev4 2 | build 3 | build_and_upload.sh 4 | /.vs 5 | /.vscode 6 | -------------------------------------------------------------------------------- /CM_definitions.cmake: -------------------------------------------------------------------------------- 1 | # DarkHelp - C++ helper class for Darknet's C API. 2 | # Copyright 2019-2024 Stephane Charette 3 | # MIT license applies. See "license.txt" for details. 4 | 5 | 6 | IF (WIN32) 7 | ADD_COMPILE_OPTIONS ( /W4 ) # warning level (high) 8 | ADD_COMPILE_OPTIONS ( /WX ) # treat warnings as errors 9 | ADD_COMPILE_OPTIONS ( /permissive- ) # stick to C++ standards (turn off Microsoft-specific extensions) 10 | ADD_COMPILE_OPTIONS ( /wd4100 ) # disable "unreferenced formal parameter" 11 | ADD_COMPILE_OPTIONS ( /wd4127 ) # disable "conditional expression is constant" 12 | ADD_COMPILE_DEFINITIONS ( _CRT_SECURE_NO_WARNINGS ) # don't complain about localtime() 13 | SET ( CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" ) 14 | ELSE () 15 | ADD_COMPILE_OPTIONS ( -Wall -Wextra -Werror -Wno-unused-parameter ) 16 | ENDIF () 17 | -------------------------------------------------------------------------------- /CM_dependencies.cmake: -------------------------------------------------------------------------------- 1 | # DarkHelp - C++ helper class for Darknet's C API. 2 | # Copyright 2019-2024 Stephane Charette 3 | # MIT license applies. See "license.txt" for details. 4 | 5 | 6 | IF (WIN32) 7 | SET (CMAKE_HAVE_THREADS_LIBRARY 1) 8 | SET (CMAKE_USE_WIN32_THREADS_INIT 1) 9 | SET (CMAKE_USE_PTHREADS_INIT 0) 10 | SET (THREADS_PREFER_PTHREAD_FLAG ON) 11 | ENDIF () 12 | 13 | FIND_PACKAGE (Threads REQUIRED ) 14 | FIND_PACKAGE (OpenCV CONFIG REQUIRED ) 15 | INCLUDE_DIRECTORIES (${OpenCV_INCLUDE_DIRS} ) 16 | 17 | # On Linux, Darknet should be installed as /usr/lib/libdarknet.so. 18 | # On Windows, it is user-defined but probably is C:/Program Files/Darknet/lib/darknet.lib. 19 | # If that is not the case, be prepared to manually edit the CMake cache file. 20 | FIND_LIBRARY (Darknet darknet) 21 | IF (WIN32) 22 | GET_FILENAME_COMPONENT(DARKNET_PARENT_DIR "${Darknet}" DIRECTORY) 23 | INCLUDE_DIRECTORIES (${DARKNET_PARENT_DIR}/../include/) 24 | ENDIF () 25 | 26 | SET (StdCppFS "") 27 | 28 | IF (NOT WIN32) 29 | FIND_LIBRARY (Magic magic) # sudo apt-get install libmagic-dev 30 | 31 | # On older 18.04, we need to use "experimental/filesystem" instead of "filesystem" 32 | # and we need to pass in the -lstdc++fs flag when linking. This seems to have no 33 | # impact even when using newer versions of g++ which technically doesn't need this 34 | # to link. (Does this need to be fixed in a different manner?) 35 | SET ( StdCppFS stdc++fs ) 36 | ENDIF () 37 | 38 | FIND_PATH (TCLAP_INCLUDE_DIRS "tclap/Arg.h") # sudo apt-get install libtclap-dev 39 | INCLUDE_DIRECTORIES (${TCLAP_INCLUDE_DIRS}) 40 | -------------------------------------------------------------------------------- /CM_package.cmake: -------------------------------------------------------------------------------- 1 | # DarkHelp - C++ helper class for Darknet's C API. 2 | # Copyright 2019-2024 Stephane Charette 3 | # MIT license applies. See "license.txt" for details. 4 | 5 | 6 | SET ( CPACK_PACKAGE_VENDOR "Stephane Charette" ) 7 | SET ( CPACK_PACKAGE_CONTACT "stephanecharette@gmail.com" ) 8 | SET ( CPACK_PACKAGE_VERSION ${DH_VERSION} ) 9 | SET ( CPACK_PACKAGE_VERSION_MAJOR ${DH_VER_MAJOR} ) 10 | SET ( CPACK_PACKAGE_VERSION_MINOR ${DH_VER_MINOR} ) 11 | SET ( CPACK_PACKAGE_VERSION_PATCH ${DH_VER_PATCH} ) 12 | SET ( CPACK_PACKAGE_INSTALL_DIRECTORY "DarkHelp" ) 13 | SET ( CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/license.txt ) 14 | SET ( CPACK_PACKAGE_NAME "darkhelp" ) 15 | SET ( CPACK_PACKAGE_DESCRIPTION_SUMMARY "DarkHelp C++ library" ) 16 | SET ( CPACK_PACKAGE_DESCRIPTION "DarkHelp C++ library" ) 17 | SET ( CPACK_PACKAGE_HOMEPAGE_URL "https://www.ccoderun.ca/DarkHelp/" ) 18 | 19 | IF ( WIN32 ) 20 | SET ( CPACK_PACKAGE_FILE_NAME "darkhelp-${DH_VERSION}-Windows-64" ) 21 | SET ( CPACK_NSIS_PACKAGE_NAME "DarkHelp" ) 22 | SET ( CPACK_NSIS_DISPLAY_NAME "DarkHelp v${DH_VERSION}" ) 23 | SET ( CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}\\\\src-tool\\\\DarkHelp.ico" ) 24 | SET ( CPACK_NSIS_MUI_UNIICON "${CMAKE_CURRENT_SOURCE_DIR}\\\\src-tool\\\\DarkHelp.ico" ) 25 | SET ( CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}\\\\src-tool\\\\DarkHelp.ico" ) 26 | SET ( CPACK_NSIS_CONTACT "stephanecharette@gmail.com" ) 27 | SET ( CPACK_NSIS_URL_INFO_ABOUT "https://www.ccoderun.ca/DarkHelp/" ) 28 | SET ( CPACK_NSIS_HELP_LINK "https://www.ccoderun.ca/DarkHelp/" ) 29 | SET ( CPACK_NSIS_MODIFY_PATH "ON" ) 30 | SET ( CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL "ON" ) 31 | SET ( CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\DarkHelp.ico" ) 32 | SET ( CPACK_GENERATOR "NSIS" ) 33 | ELSE () 34 | # Get the kernel name. This should give us a string such as "Linux". 35 | EXECUTE_PROCESS ( 36 | COMMAND uname -s 37 | OUTPUT_VARIABLE DH_UNAME_S 38 | OUTPUT_STRIP_TRAILING_WHITESPACE ) 39 | 40 | # Get the machine hardware name. This should give us a string such as "x86_64" or "aarch64" (Jetson Nano for example). 41 | EXECUTE_PROCESS ( 42 | COMMAND uname -m 43 | OUTPUT_VARIABLE DH_UNAME_M 44 | OUTPUT_STRIP_TRAILING_WHITESPACE ) 45 | 46 | # Get the name "Ubuntu". 47 | EXECUTE_PROCESS ( 48 | COMMAND lsb_release --id 49 | COMMAND cut -d\t -f2 50 | OUTPUT_VARIABLE DH_LSB_ID 51 | OUTPUT_STRIP_TRAILING_WHITESPACE ) 52 | 53 | # Get the version number "20.04". 54 | EXECUTE_PROCESS ( 55 | COMMAND lsb_release --release 56 | COMMAND cut -d\t -f2 57 | OUTPUT_VARIABLE DH_LSB_REL 58 | OUTPUT_STRIP_TRAILING_WHITESPACE ) 59 | 60 | SET ( CPACK_PACKAGE_FILE_NAME "darkhelp-${DH_VERSION}-${DH_UNAME_S}-${DH_UNAME_M}-${DH_LSB_ID}-${DH_LSB_REL}" ) 61 | SET ( CPACK_DEBIAN_PACKAGE_SECTION "other" ) 62 | SET ( CPACK_DEBIAN_PACKAGE_PRIORITY "optional" ) 63 | SET ( CPACK_DEBIAN_PACKAGE_MAINTAINER "Stephane Charette " ) 64 | SET ( CPACK_DEBIAN_PACKAGE_SHLIBDEPS "ON" ) 65 | SET ( CPACK_GENERATOR "DEB" ) 66 | SET ( CPACK_SOURCE_IGNORE_FILES ".svn" ".git" ".kdev4" "build/" "build_and_upload.sh" ) 67 | SET ( CPACK_SOURCE_GENERATOR "TGZ;ZIP" ) 68 | ENDIF () 69 | 70 | INCLUDE( CPack ) 71 | -------------------------------------------------------------------------------- /CM_source.cmake: -------------------------------------------------------------------------------- 1 | # DarkHelp - C++ helper class for Darknet's C API. 2 | # Copyright 2019-2024 Stephane Charette 3 | # MIT license applies. See "license.txt" for details. 4 | 5 | 6 | INCLUDE_DIRECTORIES (src-lib) 7 | INCLUDE_DIRECTORIES (src-tool) 8 | 9 | ADD_SUBDIRECTORY (src-lib) 10 | ADD_SUBDIRECTORY (src-tool) 11 | ADD_SUBDIRECTORY (src-apps) 12 | ADD_SUBDIRECTORY (src-cam) 13 | ADD_SUBDIRECTORY (src-doc) 14 | -------------------------------------------------------------------------------- /CM_version.cmake: -------------------------------------------------------------------------------- 1 | # DarkHelp - C++ helper class for Darknet's C API. 2 | # Copyright 2019-2024 Stephane Charette 3 | # MIT license applies. See "license.txt" for details. 4 | 5 | 6 | FILE ( READ version.txt VERSION_TXT ) 7 | STRING ( STRIP "${VERSION_TXT}" VERSION_TXT ) 8 | STRING ( REGEX MATCHALL "^([0-9]+)\\.([0-9]+)\\.([0-9]+)-([0-9]+)$" OUTPUT ${VERSION_TXT} ) 9 | 10 | SET ( DH_VER_MAJOR ${CMAKE_MATCH_1} ) 11 | SET ( DH_VER_MINOR ${CMAKE_MATCH_2} ) 12 | SET ( DH_VER_PATCH ${CMAKE_MATCH_3} ) 13 | SET ( DH_VER_COMMIT ${CMAKE_MATCH_4} ) 14 | 15 | SET ( DH_VERSION ${DH_VER_MAJOR}.${DH_VER_MINOR}.${DH_VER_PATCH}-${DH_VER_COMMIT} ) 16 | MESSAGE ( "Building ver: ${DH_VERSION}" ) 17 | 18 | ADD_DEFINITIONS ( -DDH_VERSION="${DH_VERSION}" ) 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # DarkHelp - C++ helper class for Darknet's C API. 2 | # Copyright 2019-2024 Stephane Charette 3 | # MIT license applies. See "license.txt" for details. 4 | 5 | 6 | # for anyone trying to build this in Ubuntu 18.04, if the minimum version 7 | # is causing you problems, please use the cmake from vcpkg or snap 8 | CMAKE_MINIMUM_REQUIRED ( VERSION 3.20 ) 9 | 10 | PROJECT ( DarkHelp C CXX ) 11 | 12 | IF ( NOT CMAKE_BUILD_TYPE ) 13 | SET ( CMAKE_BUILD_TYPE Release ) 14 | ENDIF () 15 | 16 | SET ( CMAKE_CXX_STANDARD 17 ) 17 | SET ( CMAKE_CXX_STANDARD_REQUIRED ON ) 18 | 19 | 20 | INCLUDE ( CM_version.cmake ) # get the version number 21 | INCLUDE ( CM_definitions.cmake ) # compiler definitions 22 | INCLUDE ( CM_dependencies.cmake ) # find all required dependencies 23 | INCLUDE ( CM_source.cmake ) # source code that needs to be built 24 | INCLUDE ( CM_package.cmake ) # create .exe or .deb packages 25 | -------------------------------------------------------------------------------- /build_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo This script assumes you''ve already read through and run the steps described in readme_linux.txt! 4 | echo The entire "build" directory will be deleted and re-created. 5 | read -rn1 -p "Press any key to continue..." 6 | 7 | rm -rf build 8 | mkdir build 9 | cd build 10 | 11 | BUILD_TYPE=Release 12 | #BUILD_TYPE=Debug 13 | 14 | cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. 15 | make -j $(nproc) 16 | make package 17 | 18 | -------------------------------------------------------------------------------- /build_windows.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if not exist build ( 4 | echo You need to manually run through the build steps at least once before you use this script. 5 | echo Please see readme.md for details. 6 | exit /b 1 7 | ) 8 | 9 | cd build 10 | 11 | set ARCHITECTURE=x64 12 | rem set BUILD_TYPE=Debug 13 | set BUILD_TYPE=Release 14 | set VCPKG_PATH=C:/src/vcpkg 15 | rem set TRIPLET=x64-windows-static 16 | set TRIPLET=x64-windows 17 | 18 | cmake -A %ARCHITECTURE% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_PATH%/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=%TRIPLET% -DDarknet="C:/Program Files/Darknet/lib/darknet.lib" .. 19 | if %ERRORLEVEL% neq 0 goto END 20 | 21 | msbuild.exe /property:Platform=%ARCHITECTURE%;Configuration=%BUILD_TYPE% /target:Build -maxCpuCount -verbosity:normal -detailedSummary DarkHelp.sln 22 | if %ERRORLEVEL% neq 0 goto END 23 | 24 | msbuild.exe /property:Platform=%ARCHITECTURE%;Configuration=%BUILD_TYPE% PACKAGE.vcxproj 25 | if %ERRORLEVEL% neq 0 goto END 26 | 27 | :END 28 | cd .. 29 | -------------------------------------------------------------------------------- /example_project/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED (VERSION 3.0) 2 | 3 | PROJECT (ExampleProject C CXX) 4 | 5 | SET (CMAKE_BUILD_TYPE Release) 6 | SET (CMAKE_CXX_STANDARD 17) 7 | SET (CMAKE_CXX_STANDARD_REQUIRED ON) 8 | 9 | IF (WIN32) 10 | ADD_COMPILE_OPTIONS ( /permissive- ) # stick to C++ standards (turn off Microsoft-specific extensions) 11 | ELSE () 12 | ADD_COMPILE_OPTIONS ( -Wall -Wextra -Werror -Wno-unused-parameter ) 13 | ENDIF () 14 | 15 | FIND_PACKAGE (Threads REQUIRED) 16 | FIND_PACKAGE (OpenCV REQUIRED) 17 | FIND_LIBRARY (DARKHELP darkhelp) 18 | FIND_LIBRARY (DARKNET darknet ) 19 | 20 | INCLUDE_DIRECTORIES (${OpenCV_INCLUDE_DIRS}) 21 | 22 | FILE (GLOB SOURCE *.cpp) 23 | LIST (SORT SOURCE) 24 | 25 | ADD_EXECUTABLE (example ${SOURCE}) 26 | TARGET_LINK_LIBRARIES (example Threads::Threads ${DARKHELP} ${DARKNET} ${OpenCV_LIBS}) 27 | -------------------------------------------------------------------------------- /example_project/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // ----------------------------------------------------------------------------- 4 | // Please also see the other source code examples in the ../src-apps/ directory. 5 | // ----------------------------------------------------------------------------- 6 | 7 | int main() 8 | { 9 | int rc = 0; 10 | 11 | try 12 | { 13 | DarkHelp::Config cfg("example.cfg", "example_best.weights", "example.names"); 14 | cfg.enable_tiles = false; 15 | cfg.annotation_auto_hide_labels = false; 16 | cfg.annotation_include_duration = true; 17 | cfg.annotation_include_timestamp = false; 18 | cfg.threshold = 0.2f; 19 | // lots of other options, scroll down this page to see what can be done: https://www.ccoderun.ca/darkhelp/api/classDarkHelp_1_1Config.html#details 20 | 21 | DarkHelp::NN nn(cfg); 22 | 23 | // you can further modify the configuration even after the neural network has been created 24 | nn.config.annotation_line_thickness = 1; 25 | nn.config.annotation_shade_predictions = 0.36f; 26 | 27 | // apply the neural network to an image on disk 28 | const auto results = nn.predict("example.jpg"); 29 | 30 | // print the neural network results on the console 31 | std::cout << results << std::endl; 32 | 33 | // display both the original image and the annotated image using OpenCV HighGUI 34 | cv::Mat img1 = nn.original_image; 35 | cv::Mat img2 = nn.annotate(); 36 | 37 | const cv::Size size(1024, 768); 38 | cv::imshow("original", DarkHelp::resize_keeping_aspect_ratio(img1, size)); 39 | cv::imshow("detected", DarkHelp::resize_keeping_aspect_ratio(img2, size)); 40 | cv::waitKey(); 41 | } 42 | catch (const std::exception & e) 43 | { 44 | std::cout << e.what() << std::endl; 45 | rc = 1; 46 | } 47 | 48 | return rc; 49 | } 50 | 51 | -------------------------------------------------------------------------------- /example_project/readme.txt: -------------------------------------------------------------------------------- 1 | This is an example of how to create a CMake-based C++ project that uses the 2 | DarkHelp library. 3 | 4 | The CMakeLists.txt file will pull in darknet, OpenCV, and DarkHelp. 5 | 6 | The example.cpp file loads a neural network, sets several example options, 7 | annotates a single image file, and displays the results. 8 | 9 | To build on Linux: 10 | 11 | mkdir build 12 | cd build 13 | cmake .. 14 | make 15 | 16 | To build on Windows (you may need to adjust, similar commands as the ones you used to build Darknet and DarkHelp): 17 | 18 | mkdir build 19 | cd build 20 | cmake -DCMAKE_TOOLCHAIN_FILE=C:/src/vcpkg/scripts/buildsystems/vcpkg.cmake -DDARKNET="C:/Program Files/Darknet/lib/darknet.lib" -DDARKHELP="C:/Program Files/DarkHelp/lib/darkhelp.lib" .. 21 | msbuild.exe ...etc... or use Visual Studio 22 | 23 | IMPORTANT! See the other source code examples in ../src-apps/. 24 | 25 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright 2019-2024 Stephane Charette 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # What is the DarkHelp C++ API? 2 | 3 | The DarkHelp C++ API is a wrapper to make it easier to use the Darknet neural network framework within a C++ application. DarkHelp performs the following: 4 | 5 | - load a [Darknet](https://github.com/hank-ai/darknet)-style neural network (.cfg, .names, .weights) 6 | - run inference on images -- either filenames or OpenCV `cv::Mat` images and video frames -- and return [a vector of results](https://www.ccoderun.ca/DarkHelp/api/structDarkHelp_1_1PredictionResult.html#details) 7 | - optionally annotate images/frames with the inference results 8 | 9 | Example annotated image after calling [`DarkHelp::NN::predict()`](https://www.ccoderun.ca/DarkHelp/api/classDarkHelp_1_1NN.html#a827eaa61af42451f0796a4f0adb43013) 10 | and [`DarkHelp::NN::annotate()`](https://www.ccoderun.ca/DarkHelp/api/classDarkHelp_1_1NN.html#a718c604a24ffb20efca54bbd73d79de5): 11 | 12 | ![annotated image example](src-doc/shade_25pcnt.png) 13 | 14 | # What is the DarkHelp CLI? 15 | 16 | DarkHelp also has [a very simple command-line tool](https://www.ccoderun.ca/darkhelp/api/Tool.html) that uses the DarkHelp C++ API so some of the functionality can be accessed directly from the command-line. This can be useful to run tests or for shell scripting. 17 | 18 | # What is the DarkHelp Server? 19 | 20 | DarkHelp Server is a command-line tool that loads a neural network once, and then keeps running in the background. It repeatedly applies the network to images or video frames and saves the results. 21 | 22 | Unlike Darknet and the DarkHelp CLI which have to re-load the neural network every time they're called, DarkHelp Server only does this once. DarkHelp Server [can be configured](https://www.ccoderun.ca/darkhelp/api/Server.html) to save the results in `.txt` format, `.json` format, annotate images, and can also crop the objects and create individual image files from each of the objects detected by the neural network. 23 | 24 | # License 25 | 26 | DarkHelp is open source and published using the MIT license. Meaning you can use it in your commercial application. See license.txt for details. 27 | 28 | # How to Build DarkHelp (Linux) 29 | 30 | Extremely simple easy-to-follow tutorial on how to build [Darknet](https://github.com/hank-ai/darknet#table-of-contents), DarkHelp, and [DarkMark](https://github.com/stephanecharette/DarkMark). 31 | 32 | [![DarkHelp build tutorial](https://github.com/hank-ai/darknet/raw/master/doc/linux_build_thumbnail.jpg)](https://www.youtube.com/watch?v=WTT1s8JjLFk) 33 | 34 | DarkHelp requires that [Darknet](https://github.com/hank-ai/darknet) has already been built and installed, since DarkHelp is a *wrapper* for the C functionality available in `libdarknet.so`. 35 | 36 | ## Building Darknet (Linux) 37 | 38 | You must build and install Darknet first. See the [Darknet repo](https://github.com/hank-ai/darknet#linux-cmake-method) for details. 39 | 40 | ## Building DarkHelp (Linux) 41 | 42 | Now that Darknet is built and installed, you can go ahead and build DarkHelp. On Ubuntu: 43 | 44 | ```sh 45 | sudo apt-get install build-essential libtclap-dev libmagic-dev libopencv-dev 46 | cd ~/src 47 | git clone https://github.com/stephanecharette/DarkHelp.git 48 | cd DarkHelp 49 | mkdir build 50 | cd build 51 | cmake -DCMAKE_BUILD_TYPE=Release .. 52 | make 53 | make package 54 | sudo dpkg -i darkhelp*.deb 55 | ``` 56 | 57 | ## Building Darknet (Windows) 58 | 59 | You must build and install Darknet first. See the [Darknet repo](https://github.com/hank-ai/darknet#windows-cmake-method) for details. 60 | 61 | ## Building DarkHelp (Windows) 62 | 63 | Once you finish building and installing Darknet, run the following commands in the "Developer Command Prompt for VS" to build DarkHelp: 64 | 65 | ```bat 66 | cd c:\src\vcpkg 67 | vcpkg.exe install tclap:x64-windows 68 | cd c:\src 69 | git clone https://github.com/stephanecharette/DarkHelp.git 70 | cd darkhelp 71 | mkdir build 72 | cd build 73 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=C:/src/vcpkg/scripts/buildsystems/vcpkg.cmake .. 74 | msbuild.exe /property:Platform=x64;Configuration=Release /target:Build -maxCpuCount -verbosity:normal -detailedSummary DarkHelp.sln 75 | msbuild.exe /property:Platform=x64;Configuration=Release PACKAGE.vcxproj 76 | ``` 77 | 78 | Make sure you update the path to the toolchain file if you used a different directory. Once that last command finishes, you should have a .exe file you can run to install DarkHelp on your system. 79 | 80 | > If the CMake command several lines above gives an error about not finding `Darknet`, and you're certain it is installed correctly, then you can specify the path where CMake can find it. Try this: 81 | 82 | ```bat 83 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=C:/src/vcpkg/scripts/buildsystems/vcpkg.cmake -DDarknet="C:/Program Files/Darknet/lib/darknet.lib" .. 84 | ``` 85 | 86 | > If you get an error about `Could not resolve runtime dependencies: darknet.dll`, then copy that DLL into the `src-tool` directory: 87 | 88 | ```bat 89 | copy "C:\Program Files\Darknet\bin\darknet.dll" c:\src\DarkHelp\build\src-tool\Release\ 90 | ``` 91 | 92 | > ...and re-run the command that failed: 93 | 94 | ```bat 95 | msbuild.exe /property:Platform=x64;Configuration=Release PACKAGE.vcxproj 96 | ``` 97 | 98 | > Similarly, if you get an error about missing either the CUDA or cuDNN DLLs, then you must copy them like what was done when building Darknet. For example: 99 | 100 | ```bat 101 | copy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.2\bin\*.dll" c:\src\DarkHelp\build\src-tool\Release\ 102 | copy "C:\Program Files\NVIDIA\CUDNN\v8.x\bin\cudnn64_8.dll" c:\src\DarkHelp\build\src-tool\Release\ 103 | ``` 104 | 105 | > Once the files have been copied, re-run the last `msbuild.exe` command to generate the NSIS installation package: 106 | 107 | ```bat 108 | msbuild.exe /property:Platform=x64;Configuration=Release PACKAGE.vcxproj 109 | ``` 110 | 111 | # Example Code 112 | 113 | DarkHelp has many optional settings that impact the output, especially [`DarkHelp::NN::annotate()`](https://www.ccoderun.ca/darkhelp/api/classDarkHelp_1_1NN.html#a718c604a24ffb20efca54bbd73d79de5). See the documentation for [`DarkHelp::Config`](https://www.ccoderun.ca/darkhelp/api/classDarkHelp_1_1Config.html#details) for details. 114 | 115 | ```cpp 116 | // Include DarkHelp.hpp and link against DarkHelp, Darknet, and OpenCV. 117 | 118 | const auto samples_images = {"dog.jpg", "cat.jpg", "horse.jpg"}; 119 | 120 | // Only load the neural network once, prior to the loop. You don't want 121 | // to keep reloading the network inside the loop because loading the 122 | // network is actually a long process. Note the order in which the files 123 | // are specified is not important. DarkHelp should automatically detect 124 | // the differences between the three file types. 125 | DarkHelp::NN nn("animals.cfg", "animals_best.weights", "animals.names"); 126 | 127 | // Customize several settings which alters the output of both Darknet and 128 | // DarkHelp. Note there are many other settings, this is just an example. 129 | nn.config.annotation_line_thickness = 1; 130 | nn.config.shade_predictions = 0.15; 131 | nn.config.snapping_enabled = true; 132 | nn.config.threshold = 0.25; 133 | 134 | for (const auto & filename : samples_images) 135 | { 136 | // Get the predictions. On a decent GPU this should take milliseconds, 137 | // while on a CPU this will take longer. 138 | const auto results = nn.predict(filename); 139 | 140 | // The results are stored in a std::vector, with one entry for each 141 | // detected object. This can be easily displayed on the console or 142 | // logged to a file. Either loop through the vector one entry at a 143 | // time, or use the operator<<() to send it to a stream. 144 | std::cout << results << std::endl; 145 | 146 | // Draw bounding boxes showing the detected objects and save to disk. 147 | cv::Mat output = nn.annotate(); 148 | cv::imwrite("output_" + filename, output, {cv::ImwriteFlags::IMWRITE_PNG_COMPRESSION, 9}); 149 | } 150 | ``` 151 | 152 | More examples showing how to work with both static images and videos can be found in the [src-apps directory](src-apps/) and the [example_project directory](example_project/). 153 | 154 | # C++ API Doxygen Output 155 | 156 | The official DarkHelp documentation and web site is at . 157 | 158 | Some links to specific useful pages: 159 | 160 | - [`DarkHelp` namespace](https://www.ccoderun.ca/darkhelp/api/namespaceDarkHelp.html) 161 | - [`DarkHelp::NN` class for "neural network"](https://www.ccoderun.ca/darkhelp/api/classDarkHelp_1_1NN.html#details) 162 | - [`DarkHelp::Config` class for configuration items](https://www.ccoderun.ca/darkhelp/api/classDarkHelp_1_1Config.html#details) 163 | - [Image tiling](https://www.ccoderun.ca/darkhelp/api/Tiling.html) 164 | - [DarkHelp Server](https://www.ccoderun.ca/darkhelp/api/Server.html) 165 | 166 | ![tiled image example](src-doc/mailboxes_2x2_tiles_detection.png) 167 | -------------------------------------------------------------------------------- /readme_linux.txt: -------------------------------------------------------------------------------- 1 | # DarkHelp - C++ helper class for Darknet's C API. 2 | # Copyright 2019-2024 Stephane Charette 3 | # MIT license applies. See "license.txt" for details. 4 | 5 | 6 | Most recent documentation for DarkHelp: https://github.com/stephanecharette/DarkHelp/ 7 | 8 | @warning These instructions are no longer up-to-date. Using vcpkg was causing too many problems since it doesn't 9 | build OpenCV correctly on Linux. (For example, vcpkg doesn't build highgui or the video plugins required to import 10 | video files.) For this reason, building with vcpkg is no longer supported. See the build instructions on the 11 | DarkHelp project page instead. 12 | 13 | 14 | # ------------------------- 15 | # SETTING UP PRE-REQUISITES 16 | # ------------------------- 17 | 18 | Assuming Ubuntu or similar Debian-based Linux distribution, run the following command: 19 | 20 | sudo apt-get install build-essential tar curl zip unzip git cmake libmagic-dev libgtk2.0-dev pkg-config yasm 21 | 22 | 23 | # ---------------- 24 | # CUDA/CUDNN & GPU 25 | # ---------------- 26 | 27 | If you only want the "CPU" version of OpenCV and Darknet, then skip to the next section called "BUILDING OPENCV & DARKNET". 28 | 29 | If you want to use your CUDA-supported GPU with Darknet and DarkHelp: 30 | 31 | - (These notes are from a while back. The process may have changed.) 32 | - Visit https://developer.nvidia.com/cuda-downloads or https://developer.nvidia.com/cuda-toolkit 33 | - Click on Linux -> x86_64 -> Ubuntu -> 18.04, deb (network) 34 | - The instructions should be similar to this: 35 | wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-ubuntu1804.pin 36 | sudo mv cuda-ubuntu1804.pin /etc/apt/preferences.d/cuda-repository-pin-600 37 | sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub 38 | sudo add-apt-repository "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/ /" 39 | sudo apt-get -y install cuda 40 | - You'll need to add nsight-compute and cuda to the path. For example, since I'm using fish as my shell, I run this: 41 | set --universal fish_user_paths /opt/nvidia/nsight-compute/2019.5.0 $fish_user_paths 42 | set --universal fish_user_paths /usr/local/cuda/bin $fish_user_paths 43 | - At the end of the next section, the installation command you run needs to be the one that includes CUDA/CUDNN support. 44 | 45 | 46 | # ------------------------- 47 | # BUILDING OPENCV & DARKNET 48 | # ------------------------- 49 | 50 | Run the following commands: 51 | 52 | mkdir ~/src 53 | cd ~/src 54 | git clone https://github.com/microsoft/vcpkg 55 | cd vcpkg 56 | ./bootstrap-vcpkg.sh 57 | ./vcpkg integrate install 58 | ./vcpkg integrate bash 59 | 60 | Edit the file ~/src/vcpkg/ports/opencv4/portfile.cmake and look for the this line: 61 | -DWITH_GTK=OFF 62 | 63 | Change that line to this: 64 | -DWITH_GTK=ON 65 | 66 | Save the portfile.cmake file, exit from the editor, and run *ONE* of the following two "install" commands: 67 | 68 | - This one is if you have a supported GPU and CUDA: 69 | ./vcpkg install --triplet x64-linux tclap cuda cudnn opencv-cuda darknet[cuda,cudnn,opencv-cuda] 70 | 71 | - This one is if you only want support for CPU: 72 | ./vcpkg install --triplet x64-linux tclap opencv darknet[opencv-base] 73 | 74 | Note the install command will take some time to run since it has to download and build OpenCV, Darknet, and various dependencies. 75 | 76 | Once it has finished, you can free up some disk space by deleting these two subdirectories: 77 | 78 | rm -rf buildtrees downloads 79 | 80 | 81 | # ----------------- 82 | # BUILDING DARKHELP 83 | # ----------------- 84 | 85 | Download DarkHelp from https://www.ccoderun.ca/ 86 | Extract the source into ~/src/DarkHelp/ 87 | Run the following commands: 88 | 89 | cd ~/src/DarkHelp/ 90 | mkdir build 91 | cd build 92 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=~/src/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_PREFIX_PATH=~/src/vcpkg/installed/x64-linux -DVCPKG_TARGET_TRIPLET=x64-linux .. 93 | make -j $(nproc) 94 | make package 95 | 96 | This will build a shared library named "libdarkhelp.so", and the "DarkHelp" command-line tool. 97 | 98 | See build_linux.cmd which contains the steps from "BUILDING DARKHELP". 99 | -------------------------------------------------------------------------------- /readme_windows.txt: -------------------------------------------------------------------------------- 1 | # DarkHelp - C++ helper class for Darknet's C API. 2 | # Copyright 2019-2024 Stephane Charette 3 | # MIT license applies. See "license.txt" for details. 4 | 5 | 6 | Most recent documentation for DarkHelp: https://github.com/stephanecharette/DarkHelp/ 7 | 8 | # -------- 9 | # WARNING! 10 | # -------- 11 | 12 | These instructions are no longer up-to-date. I strongly recommend you do NOT 13 | install things this way. See the build instructions on the DarkHelp project 14 | page instead (readme.md) for the installation steps. 15 | 16 | 17 | # ------------------------- 18 | # SETTING UP PRE-REQUISITES 19 | # ------------------------- 20 | 21 | Install Visual Studio 2019 from https://visualstudio.microsoft.com/vs/ 22 | During installation, select "Desktop development with C++". 23 | 24 | Download and install WinGet from https://github.com/microsoft/winget-cli/releases 25 | Run the following commands: 26 | 27 | winget install git.git 28 | winget install kitware.cmake 29 | winget install nsis 30 | 31 | 32 | # ------------------------- 33 | # BUILDING OPENCV & DARKNET 34 | # ------------------------- 35 | 36 | Run the following commands: 37 | 38 | mkdir c:\src 39 | cd c:\src 40 | git clone https://github.com/microsoft/vcpkg 41 | cd vcpkg 42 | bootstrap-vcpkg.bat 43 | vcpkg.exe integrate install 44 | vcpkg.exe integrate powershell 45 | vcpkg.exe install opencv[contrib,core,dnn,ffmpeg,jpeg,png,quirc,tiff,webp]:x64-windows darknet[opencv-base]:x64-windows 46 | 47 | 48 | # ----------------- 49 | # BUILDING DARKHELP 50 | # ----------------- 51 | 52 | Assuming you already followed the steps above which included installing vcpkg, run the following commands: 53 | 54 | cd c:\src\vcpkg 55 | vcpkg.exe install tclap:x64-windows 56 | cd c:\src 57 | git clone https://github.com/stephanecharette/DarkHelp.git 58 | cd darkhelp 59 | mkdir build 60 | cd build 61 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=C:/src/vcpkg/scripts/buildsystems/vcpkg.cmake .. 62 | 63 | You should now have a Visual Studio project file. You can build using Visual Studio, or from the command line: 64 | 65 | msbuild.exe /property:Platform=x64;Configuration=Release /target:Build -maxCpuCount -verbosity:normal -detailedSummary DarkHelp.sln 66 | 67 | This will build a static library named "darkhelp.lib", and the "DarkHelp.exe" example command-line tool. 68 | 69 | Create the installation package: 70 | 71 | msbuild.exe /property:Platform=x64;Configuration=Release PACKAGE.vcxproj 72 | 73 | Also see the script build_windows.cmd. 74 | -------------------------------------------------------------------------------- /src-apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # DarkHelp - C++ helper class for Darknet's C API. 2 | # Copyright 2019-2024 Stephane Charette 3 | # MIT license applies. See "license.txt" for details. 4 | 5 | 6 | IF ( WIN32 ) 7 | ADD_COMPILE_DEFINITIONS ( _USE_MATH_DEFINES ) 8 | ADD_COMPILE_OPTIONS ( /wd4244 ) # conversion between types 9 | ENDIF () 10 | 11 | 12 | FILE ( GLOB SRC_APPS *.cpp ) 13 | LIST ( SORT SRC_APPS ) 14 | 15 | 16 | FOREACH ( filename IN LISTS SRC_APPS ) 17 | CMAKE_PATH ( GET filename STEM stem ) 18 | ADD_EXECUTABLE ( ${stem} ${filename} ) 19 | TARGET_LINK_LIBRARIES ( ${stem} PRIVATE Threads::Threads dh ${Darknet} ${OpenCV_LIBS} ) 20 | ENDFOREACH () 21 | -------------------------------------------------------------------------------- /src-apps/display_single_image.cpp: -------------------------------------------------------------------------------- 1 | /* DarkHelp - C++ helper class for Darknet's C API. 2 | * Copyright 2019-2024 Stephane Charette 3 | * MIT license applies. See "license.txt" for details. 4 | */ 5 | 6 | #include "DarkHelp.hpp" 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | int rc = 0; 11 | 12 | try 13 | { 14 | if (argc != 5) 15 | { 16 | std::cout 17 | << "Usage:" << std::endl 18 | << argv[0] << " " << std::endl; 19 | throw std::invalid_argument("wrong number of arguments"); 20 | } 21 | 22 | std::string fn1 = argv[1]; 23 | std::string fn2 = argv[2]; 24 | std::string fn3 = argv[3]; 25 | std::string image = argv[4]; 26 | 27 | // Load the neural network. The order of the 3 files does not matter, DarkHelp should figure out which file is which. 28 | DarkHelp::NN nn(fn1, fn2, fn3); 29 | 30 | // Use OpenCV to load the image. 31 | cv::Mat original_image = cv::imread(image); 32 | 33 | // Call on the neural network to process the given filename 34 | nn.predict(original_image); 35 | 36 | // Annotate the image using the results. 37 | cv::Mat annotated_image = nn.annotate(); 38 | 39 | // Display the results using OpenCV. 40 | cv::imshow("original", original_image); 41 | cv::imshow("annotated", annotated_image); 42 | cv::waitKey(); 43 | } 44 | catch (const std::exception & e) 45 | { 46 | std::cout << e.what() << std::endl; 47 | rc = 1; 48 | } 49 | 50 | return rc; 51 | } 52 | -------------------------------------------------------------------------------- /src-apps/display_single_image_custom_settings.cpp: -------------------------------------------------------------------------------- 1 | /* DarkHelp - C++ helper class for Darknet's C API. 2 | * Copyright 2019-2024 Stephane Charette 3 | * MIT license applies. See "license.txt" for details. 4 | */ 5 | 6 | #include "DarkHelp.hpp" 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | int rc = 0; 11 | 12 | try 13 | { 14 | if (argc != 5) 15 | { 16 | std::cout 17 | << "Usage:" << std::endl 18 | << argv[0] << " " << std::endl; 19 | throw std::invalid_argument("wrong number of arguments"); 20 | } 21 | 22 | std::string fn1 = argv[1]; 23 | std::string fn2 = argv[2]; 24 | std::string fn3 = argv[3]; 25 | std::string image = argv[4]; 26 | 27 | // You can customize settings before you load the network, or after you load the network. Or both, like we do in this example. 28 | DarkHelp::Config config(fn1, fn2, fn3); 29 | config.annotation_auto_hide_labels = false; 30 | config.annotation_include_duration = true; 31 | config.annotation_include_timestamp = false; 32 | config.threshold = 0.25; 33 | 34 | // Load the neural network using the configuration object. 35 | DarkHelp::NN nn(config); 36 | 37 | /* At this point the "config" object above is no longer used. The DarkHelp::NN object made a copy of it. When you 38 | * want to alter additional config settings after this point, you'll need to do so via that copy in nn.config. 39 | */ 40 | nn.config.enable_tiles = false; 41 | nn.config.annotation_line_thickness = 1; 42 | nn.config.annotation_font_scale = 0.75; 43 | 44 | // Call on the neural network to process the given filename 45 | nn.predict(image); 46 | 47 | // Display the annotated image. 48 | cv::imshow("annotated", nn.annotate()); 49 | cv::waitKey(); 50 | } 51 | catch (const std::exception & e) 52 | { 53 | std::cout << e.what() << std::endl; 54 | rc = 1; 55 | } 56 | 57 | return rc; 58 | } 59 | -------------------------------------------------------------------------------- /src-apps/display_single_image_snapping.cpp: -------------------------------------------------------------------------------- 1 | /* DarkHelp - C++ helper class for Darknet's C API. 2 | * Copyright 2019-2024 Stephane Charette 3 | * MIT license applies. See "license.txt" for details. 4 | */ 5 | 6 | #include "DarkHelp.hpp" 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | int rc = 0; 11 | 12 | try 13 | { 14 | if (argc != 5) 15 | { 16 | std::cout 17 | << "Usage:" << std::endl 18 | << argv[0] << " " << std::endl; 19 | throw std::invalid_argument("wrong number of arguments"); 20 | } 21 | 22 | std::string fn1 = argv[1]; 23 | std::string fn2 = argv[2]; 24 | std::string fn3 = argv[3]; 25 | std::string image = argv[4]; 26 | 27 | // Load the neural network. The order of the 3 files does not matter, DarkHelp should figure out which file is which. 28 | DarkHelp::NN nn(fn1, fn2, fn3); 29 | 30 | /* Turn on snapping. This will try to grow and/or shrink the bounding boxes in cases where Darknet/YOLO doesn't 31 | * provide perfect results. This is also impacted by the black-and-white threshold values, and may not apply to 32 | * all image types. 33 | */ 34 | nn.config.snapping_enabled = true; 35 | 36 | // Use OpenCV to load the image. 37 | cv::Mat original_image = cv::imread(image); 38 | 39 | // Call on the neural network to process the given filename 40 | nn.predict(original_image); 41 | 42 | // Annotate the image using the results. 43 | cv::Mat annotated_image = nn.annotate(); 44 | 45 | // Display the results using OpenCV. 46 | cv::imshow("original", original_image); 47 | cv::imshow("annotated", annotated_image); 48 | cv::waitKey(); 49 | } 50 | catch (const std::exception & e) 51 | { 52 | std::cout << e.what() << std::endl; 53 | rc = 1; 54 | } 55 | 56 | return rc; 57 | } 58 | -------------------------------------------------------------------------------- /src-apps/display_using_bundle.cpp: -------------------------------------------------------------------------------- 1 | /* DarkHelp - C++ helper class for Darknet's C API. 2 | * Copyright 2019-2024 Stephane Charette 3 | * MIT license applies. See "license.txt" for details. 4 | */ 5 | 6 | #include "DarkHelp.hpp" 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | int rc = 0; 11 | 12 | try 13 | { 14 | if (argc != 4) 15 | { 16 | std::cout 17 | << "Usage:" << std::endl 18 | << argv[0] << " " << std::endl; 19 | throw std::invalid_argument("wrong number of arguments"); 20 | } 21 | 22 | std::string dh = argv[1]; 23 | std::string key = argv[2]; 24 | std::string image = argv[3]; 25 | 26 | // Load the neural network. The .dh filename must have been created with the DarkHelpCombine CLI tool. 27 | DarkHelp::NN nn(false, dh, key); 28 | 29 | // Use OpenCV to load the image. 30 | cv::Mat original_image = cv::imread(image); 31 | 32 | // Call on the neural network to process the given filename 33 | nn.predict(original_image); 34 | 35 | // Annotate the image using the results. 36 | cv::Mat annotated_image = nn.annotate(); 37 | 38 | // Display the results using OpenCV. 39 | cv::imshow("original", original_image); 40 | cv::imshow("annotated", annotated_image); 41 | cv::waitKey(); 42 | } 43 | catch (const std::exception & e) 44 | { 45 | std::cout << e.what() << std::endl; 46 | rc = 1; 47 | } 48 | 49 | return rc; 50 | } 51 | -------------------------------------------------------------------------------- /src-apps/process_many_images_on_threads.cpp: -------------------------------------------------------------------------------- 1 | /* DarkHelp - C++ helper class for Darknet's C API. 2 | * Copyright 2019-2024 Stephane Charette 3 | * MIT license applies. See "license.txt" for details. 4 | */ 5 | 6 | #include "DarkHelpThreads.hpp" 7 | 8 | 9 | int main(int argc, char * argv[]) 10 | { 11 | int rc = 1; 12 | 13 | try 14 | { 15 | if (argc < 5) 16 | { 17 | std::cout 18 | << "Usage:" << std::endl 19 | << argv[0] << " []" << std::endl; 20 | throw std::invalid_argument("wrong number of arguments"); 21 | } 22 | 23 | // these are just a few of the DarkHelp settings that can be set 24 | // for additional examples, see https://www.ccoderun.ca/darkhelp/api/classDarkHelp_1_1Config.html#details 25 | DarkHelp::Config cfg(argv[1], argv[2], argv[3]); 26 | cfg.threshold = 0.2f; 27 | cfg.enable_tiles = false; 28 | cfg.snapping_enabled = false; 29 | cfg.annotation_auto_hide_labels = false; 30 | cfg.annotation_include_duration = false; 31 | cfg.annotation_include_timestamp = false; 32 | cfg.annotation_pixelate_enabled = false; 33 | cfg.annotation_line_thickness = 1; 34 | 35 | const size_t number_of_threads_to_start = 10; 36 | 37 | DarkHelp::DHThreads dht(cfg, number_of_threads_to_start, "/tmp/output/"); 38 | 39 | #if 0 40 | // test adding a bunch of image filenames 41 | for (int i = 4; i < argc; i ++) 42 | { 43 | // call this as many times as necessary, with either a subdirectory name or a specific image filename 44 | dht.add_images(argv[i]); 45 | } 46 | #else 47 | // test adding a bunch of *images* (not filenames) 48 | for (int i = 4 ; i < argc; i ++) 49 | { 50 | // hopefully for this example all of the these are image files and not directory names 51 | cv::Mat mat = cv::imread(argv[i]); 52 | dht.add_image(mat); 53 | } 54 | #endif 55 | 56 | const auto results = dht.wait_for_results(); 57 | 58 | // display all of the filenames and the results for each one 59 | for (const auto & [key, val] : results) 60 | { 61 | std::cout << key << ": " << val << std::endl; 62 | } 63 | 64 | rc = 0; 65 | } 66 | catch (const std::exception & e) 67 | { 68 | std::cout << "Exception: " << e.what() << std::endl; 69 | } 70 | 71 | return rc; 72 | } 73 | -------------------------------------------------------------------------------- /src-apps/process_single_image.cpp: -------------------------------------------------------------------------------- 1 | /* DarkHelp - C++ helper class for Darknet's C API. 2 | * Copyright 2019-2024 Stephane Charette 3 | * MIT license applies. See "license.txt" for details. 4 | */ 5 | 6 | #include "DarkHelp.hpp" 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | int rc = 0; 11 | 12 | try 13 | { 14 | if (argc != 5) 15 | { 16 | std::cout 17 | << "Usage:" << std::endl 18 | << argv[0] << " " << std::endl; 19 | throw std::invalid_argument("wrong number of arguments"); 20 | } 21 | 22 | std::string fn1 = argv[1]; 23 | std::string fn2 = argv[2]; 24 | std::string fn3 = argv[3]; 25 | std::string image = argv[4]; 26 | 27 | // Load the neural network. The order of the 3 files does not matter, DarkHelp should figure out which file is which. 28 | DarkHelp::NN nn(fn1, fn2, fn3); 29 | 30 | // Call on the neural network to process the given filename 31 | const auto results = nn.predict(image); 32 | 33 | // Display the results on the console. 34 | std::cout << image << " " << results << std::endl; 35 | } 36 | catch (const std::exception & e) 37 | { 38 | std::cout << e.what() << std::endl; 39 | rc = 1; 40 | } 41 | 42 | return rc; 43 | } 44 | -------------------------------------------------------------------------------- /src-apps/process_using_bundle_and_dhthreads.cpp: -------------------------------------------------------------------------------- 1 | /* DarkHelp - C++ helper class for Darknet's C API. 2 | * Copyright 2019-2024 Stephane Charette 3 | * MIT license applies. See "license.txt" for details. 4 | */ 5 | 6 | #include "DarkHelp.hpp" 7 | #include "DarkHelpThreads.hpp" 8 | 9 | 10 | int main(int argc, char * argv[]) 11 | { 12 | int rc = 0; 13 | 14 | try 15 | { 16 | if (argc < 4) 17 | { 18 | std::cout 19 | << "Usage:" << std::endl 20 | << argv[0] << " [ ...]" << std::endl; 21 | throw std::invalid_argument("wrong number of arguments"); 22 | } 23 | 24 | std::string dh = argv[1]; 25 | std::string key = argv[2]; 26 | const size_t number_of_threads_to_start = 10; 27 | 28 | DarkHelp::DHThreads dht(dh, key, number_of_threads_to_start); 29 | 30 | // if you want to change some of the configuration settings, you'll need to do it *after* the network has been loaded 31 | for (size_t i = 0; i < number_of_threads_to_start; i++) 32 | { 33 | DarkHelp::NN * nn = dht.get_nn(i); 34 | if (nn) 35 | { 36 | nn->config.threshold = 0.2f; 37 | nn->config.enable_tiles = false; 38 | nn->config.snapping_enabled = false; 39 | } 40 | } 41 | 42 | // add the images and immediately start processing them on different threads 43 | for (int i = 3; i < argc; i++) 44 | { 45 | dht.add_images(argv[i]); 46 | } 47 | 48 | const auto results = dht.wait_for_results(); 49 | 50 | // display all of the filenames and the results for each one 51 | for (const auto & [fn, predictions] : results) 52 | { 53 | std::cout << fn << ": " << predictions << std::endl; 54 | } 55 | 56 | } 57 | catch (const std::exception & e) 58 | { 59 | std::cout << e.what() << std::endl; 60 | rc = 1; 61 | } 62 | 63 | return rc; 64 | } 65 | -------------------------------------------------------------------------------- /src-apps/process_video_webcam.cpp: -------------------------------------------------------------------------------- 1 | /* DarkHelp - C++ helper class for Darknet's C API. 2 | * Copyright 2019-2024 Stephane Charette 3 | * MIT license applies. See "license.txt" for details. 4 | */ 5 | 6 | #include "DarkHelp.hpp" 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | int rc = 0; 11 | 12 | try 13 | { 14 | if (argc != 4) 15 | { 16 | std::cout 17 | << "Usage:" << std::endl 18 | << argv[0] << " " << std::endl; 19 | throw std::invalid_argument("wrong number of arguments"); 20 | } 21 | 22 | // Load the neural network. The order of the 3 files does not matter, DarkHelp should figure out which file is which. 23 | DarkHelp::NN nn(argv[1], argv[2], argv[3]); 24 | 25 | // Use OpenCV to open the webcam. Index zero is the first webcam. Attemp to set a few camera properties. 26 | cv::VideoCapture cap(0); 27 | if (not cap.isOpened()) 28 | { 29 | throw std::runtime_error("failed to open the webcam"); 30 | } 31 | cap.set(cv::CAP_PROP_FRAME_WIDTH, 640.0); 32 | cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480.0); 33 | cap.set(cv::CAP_PROP_FPS, 30.0); 34 | 35 | while (cap.isOpened()) 36 | { 37 | cv::Mat frame; 38 | cap >> frame; 39 | if (frame.empty()) 40 | { 41 | break; 42 | } 43 | 44 | nn.predict(frame); 45 | frame = nn.annotate(); 46 | 47 | /* This is an over-simplistic example, where the waitKey() timeout is hard-coded. You want the timeout to be short 48 | * enough for the OpenCV HighGUI event loop to have time to process events, but not too long that the code doesn't 49 | * have time to keep up with new incoming video frames. 50 | * 51 | * An assumption is made that most webcams might default to 30 FPS, meaning 33.33333 milliseconds per frame. Sleep 52 | * just half of that time to process HighGUI events, and instead spend the extra remaining time waiting for the next 53 | * frame to be extracted. 54 | */ 55 | cv::imshow("video", frame); 56 | const auto key = cv::waitKey(15); 57 | if (key == 27) 58 | { 59 | break; 60 | } 61 | } 62 | } 63 | catch (const std::exception & e) 64 | { 65 | std::cout << e.what() << std::endl; 66 | rc = 1; 67 | } 68 | 69 | return rc; 70 | } 71 | -------------------------------------------------------------------------------- /src-apps/readme.md: -------------------------------------------------------------------------------- 1 | # What is the src-apps directory? 2 | 3 | These are a series of very simple single-purpose apps to show how to use the DarkHelp C++ API. These are meant to be used as a starting point for people who want to use Darknet and DarkHelp. They are not meant to be final production or commercial-ready applications. 4 | 5 | Find the one that seems to do what you want, and modify it as needed. The examples as shown below are typically done with [the Rolodex example dataset](https://www.ccoderun.ca/programming/2023-11-06_Rolodex/). 6 | 7 | > When running these example apps on Windows, note the DLL files in the `C:\Program Files\Darknet\bin\` and `C:\Program Files\DarkHelp\bin\` directories. The examples in this directory will need access to the same dependencies (OpenCV, Darknet, possibly CUDA and cuDNN, etc). 8 | 9 | # App Descriptions 10 | 11 | * `process_single_image.cpp` Loads a neural network, call `predict()` on a single image, and display the results on the console. Should look similar to this: 12 | * Run: `src-apps/process_single_image Rolodex.cfg Rolodex.names Rolodex_best.weights page_70.png` 13 | * Output should look similar to this: 14 | ```txt 15 | page_70.png prediction results: 10 16 | -> 1/10: "user level 2 100%" #7 prob=0.996683 x=236 y=421 w=15 h=23 oid=0 tile=0 entries=1 17 | -> 2/10: "date of birth 100%" #2 prob=0.999682 x=143 y=331 w=207 h=23 oid=0 tile=0 entries=1 18 | -> 3/10: "female 100%" #4 prob=0.999683 x=535 y=331 w=13 h=22 oid=0 tile=0 entries=1 19 | -> 4/10: "marital status 100%" #5 prob=0.999713 x=299 y=371 w=16 h=23 oid=0 tile=0 entries=1 20 | -> 5/10: "serial number 100%" #12 prob=0.999727 x=423 y=461 w=219 h=23 oid=0 tile=0 entries=1 21 | -> 6/10: "address 100%" #1 prob=0.999777 x=50 y=240 w=437 h=67 oid=0 tile=0 entries=1 22 | -> 7/10: "avt 100%" #11 prob=0.999786 x=533 y=420 w=208 h=24 oid=0 tile=0 entries=1 23 | -> 8/10: "barcode 100%" #14 prob=0.999866 x=548 y=523 w=186 h=92 oid=0 tile=0 entries=1 24 | -> 9/10: "name 100%" #0 prob=0.999887 x=60 y=176 w=430 h=42 oid=0 tile=0 entries=1 25 | -> 10/10: "qr code 100%" #13 prob=0.999931 x=98 y=522 w=101 h=100 oid=0 tile=0 entries=1 26 | ``` 27 | 28 | * `display_single_image.cpp` Loads a neural network, call `predict()` on a single image, annotate the image with the results, and display using a GUI window. 29 | * Run: `src-apps/display_single_image Rolodex.cfg Rolodex.names Rolodex_best.weights page_70.png` 30 | 31 | * `display_single_image_snapping.cpp` Similar to the previous app, but enables [the "snapping" feature](https://www.ccoderun.ca/darkhelp/api/classDarkHelp_1_1Config.html#af5a408e8347469584373338271007ede). 32 | * Run: `src-apps/display_single_image_snapping Rolodex.cfg Rolodex.names Rolodex_best.weights page_70.png` 33 | 34 | * `display_single_image_custom_settings.cpp` Similiar to the previous app, shows how to customize a number of the DarkHelp and Darknet settings. 35 | * Run: `src-apps/display_single_image_custom_settings Rolodex.cfg Rolodex.names Rolodex_best.weights page_70.png` 36 | 37 | * `process_video_webcam.cpp` Loads a neural network, open the first webcam, and continuously apply the network to every video frame. Press `ESC` to exit. 38 | * If you have a webcam, try and run the MSCOCO pre-trained weights with this one. Download [the MSCOCO YOLOv4-tiny weights](https://github.com/hank-ai/darknet#mscoco-pre-trained-weights). 39 | * Run: `src-apps/process_video_webcam yolov4-tiny.cfg coco.names yolov4-tiny.weights` 40 | 41 | * `save_webcam_to_video.cpp` Similar to the previous app, but the output is saved back out to disk, named `out.mp4`. Press `ESC` to stop recording and exit. 42 | * Run: `src-apps/save_webcam_to_video yolov4-tiny.cfg coco.names yolov4-tiny.weights` 43 | 44 | * `rotate_images.cpp` Detects image rotation, rotates the images back to level, and then re-runs the neural network to get more accurate detections. 45 | * See: 46 | * Run: `src-apps/rotate_image Perfs.cfg Perfs.names Perfs_best.weights ~/nn/Perfs/set_01/film*.jpg` 47 | 48 | * `video_display_realtime.cpp` Loads a video file and displays it at the exact speed it was recorded, running the neural network on each frame. 49 | * Run: `src-apps/video_display_realtime animals.cfg animals.names animals_best.weights zoo.m4v` 50 | 51 | * `video_object_counter.cpp` Combines predictions and object tracker to count the number of objects. 52 | * See: output should be similar to this: 53 | * Run: `src-apps/video_object_counter pigs.cfg pigs.names pigs_best.weights farm.m4v` 54 | 55 | * `using_c_api.cpp` Demonstrates how to use the C API. 56 | * Run: `src-apps/using_c_api Rolodex.cfg Rolodex.names Rolodex_best.weights page_70.png` 57 | 58 | * `process_many_images_on_threads.cpp` Demonstrates how to use the DHThreads class to load multiple copies of a neural network to process many image files at once. 59 | * Run: `src-apps/process_many_images_on_threads yolov4-tiny.cfg coco.names yolov4-tiny.weights images/` 60 | 61 | * `display_using_bundle.cpp` Demonstrates how to use a .dh combined bundle file with DarkHelp. Run `DarkHelpCombine` to create the .dh file. Compare this code against `display_single_image.cpp`. 62 | * Run: `DarkHelpCombine password Rolodex.cfg Rolodex.names Rolodex_best.weights` followed by `display_using_bundle Rolodex.dh password page_70.png` 63 | 64 | * `resize_corners.cpp` Looks for classes named "TL", "TR", "BR", "BL" and resizes those annotations to a fixed size. 65 | * Run: `src-apps/resize_corners ~/nn/animals/animals.names` 66 | -------------------------------------------------------------------------------- /src-apps/rotate_images.cpp: -------------------------------------------------------------------------------- 1 | /* DarkHelp - C++ helper class for Darknet's C API. 2 | * Copyright 2019-2024 Stephane Charette 3 | * MIT license applies. See "license.txt" for details. 4 | */ 5 | 6 | #include "DarkHelp.hpp" 7 | 8 | // see this blog post for details on this example code: https://www.ccoderun.ca/programming/2023-11-26_YOLO_and_image_rotation/ 9 | 10 | int main(int argc, char * argv[]) 11 | { 12 | int rc = 0; 13 | 14 | try 15 | { 16 | if (argc < 5) 17 | { 18 | std::cout 19 | << "Usage:" << std::endl 20 | << argv[0] << " []" << std::endl; 21 | throw std::invalid_argument("wrong number of arguments"); 22 | } 23 | 24 | const cv::Scalar light_blue (255 , 128 , 64 ); 25 | const cv::Scalar red (0 , 0 , 255 ); 26 | const cv::Scalar yellow (0 , 255 , 255 ); 27 | const cv::Scalar white (255 , 255 , 255 ); 28 | const cv::Scalar black (0 , 0 , 0 ); 29 | 30 | // Load the neural network. The order of the 3 files does not matter, DarkHelp should figure out which file is which. 31 | DarkHelp::NN nn(argv[1], argv[2], argv[3]); 32 | nn.config.annotation_auto_hide_labels = false; 33 | nn.config.annotation_include_duration = false; 34 | nn.config.annotation_colours[0] = light_blue; 35 | nn.config.annotation_colours[1] = yellow; 36 | 37 | /* If the difference between the two landmarks is less than this value then we don't bother torotate the image. 38 | * We'll consider it "level". Use a value that makes sense for your project. This will likely depend on the 39 | * size of the images as well as the type of objects you are detecting. 40 | */ 41 | const int tolerance_difference_in_pixels = 5; 42 | 43 | // Loop through all the images provided as argv[4]...argv[n]. 44 | for (int idx = 4; idx < argc; idx ++) 45 | { 46 | std::string image = argv[idx]; 47 | std::cout << image << ": processing..." << std::endl; 48 | 49 | cv::Mat original_image = cv::imread(image); 50 | 51 | // Start by displaying some blank images. This is purely cosmetic and can be deleted since it has nothing to do with the image rotation. 52 | if (true) 53 | { 54 | cv::Mat blank(original_image.size(), CV_8UC3, white); 55 | cv::imshow("markup" , blank); 56 | cv::imshow("annotated (pre-rotation)" , blank); 57 | cv::imshow("annotated (post-rotation)" , blank); 58 | } 59 | 60 | // Call on the neural network to process the image. 61 | auto results = nn.predict(original_image); 62 | 63 | // this block is for display/debug purpose only and can be deleted 64 | if (true) 65 | { 66 | cv::Mat annotated_pre = nn.annotate(); 67 | cv::imshow("annotated (pre-rotation)", annotated_pre); 68 | } 69 | 70 | cv::Point left_landmark; 71 | cv::Point right_landmark; 72 | for (const auto & pred : results) 73 | { 74 | // With the film perforation network, class #0 is the perforations and class #1 are the frames. 75 | // Remember to set this appropriately for you own network! 76 | if (pred.best_class == 0) 77 | { 78 | // remember the landmarks -- doesn't matter which is left or right, we'll swap them if necessary once we have two to compare 79 | if (left_landmark == cv::Point(0, 0)) 80 | { 81 | left_landmark = pred.rect.tl(); 82 | } 83 | else 84 | { 85 | right_landmark = pred.rect.tl(); 86 | if (left_landmark.x > right_landmark.x) 87 | { 88 | std::swap(left_landmark, right_landmark); 89 | } 90 | break; 91 | } 92 | } 93 | } 94 | 95 | if (left_landmark == cv::Point(0, 0) or // failed to find any landmark 96 | right_landmark == cv::Point(0, 0) or // failed to find a 2nd landmark 97 | std::abs(left_landmark.y - right_landmark.y) < tolerance_difference_in_pixels) 98 | { 99 | std::cout << image << ": no rotation to apply: left=" << left_landmark << " right=" << right_landmark << std::endl; 100 | } 101 | else 102 | { 103 | // figure out the angle between the left and right landmarks 104 | const float delta_x = right_landmark.x - left_landmark.x; 105 | const float delta_y = left_landmark.y - right_landmark.y; // reverse left and right since Y axis grows down 106 | const float radians = std::atan2(delta_y, delta_x); 107 | const float degrees = radians * 180.0f / M_PI; 108 | 109 | // this next block of code is purely cosmetic for debug/display purposes and can be deleted 110 | if (true) 111 | { 112 | cv::Mat markup = nn.annotated_image.clone(); 113 | cv::circle(markup, left_landmark , 5, red, cv::FILLED); 114 | cv::circle(markup, right_landmark , 5, red, cv::FILLED); 115 | cv::line(markup, left_landmark, right_landmark, red, 2, cv::LINE_AA); 116 | 117 | cv::Point p(right_landmark.x, left_landmark.y); 118 | cv::line(markup, left_landmark, p , light_blue, 2, cv::LINE_AA); 119 | cv::line(markup, p, right_landmark , light_blue, 2, cv::LINE_AA); 120 | 121 | const std::string text = "angle = " + std::to_string(degrees) + " degrees"; 122 | std::cout << image << ": " << text << std::endl; 123 | 124 | p.x = (left_landmark.x + right_landmark.x) / 2 - 100; // (too lazy to calculate the exact length of the string!) 125 | p.y = (left_landmark.y + right_landmark.y) / 2; 126 | cv::putText(markup, text, p, cv::HersheyFonts::FONT_HERSHEY_PLAIN, 1.0, white , 4, cv::LineTypes::LINE_AA); 127 | cv::putText(markup, text, p, cv::HersheyFonts::FONT_HERSHEY_PLAIN, 1.0, red , 1, cv::LineTypes::LINE_AA); 128 | 129 | cv::imshow("markup", markup); 130 | } 131 | 132 | // apply a counter-rotation for the angle we figured out above 133 | const auto angle = 0.0f - degrees; 134 | const float x = original_image.cols / 2.0f; 135 | const float y = original_image.rows / 2.0f; 136 | const cv::Point2f center(x, y); 137 | 138 | // this is where the original image is actually rotated 139 | cv::Mat rotation_matrix = cv::getRotationMatrix2D(center, angle, 1.0); 140 | cv::Rect2f box = cv::RotatedRect(center, original_image.size(), angle).boundingRect2f(); 141 | rotation_matrix.at(0,2) += box.width / 2.0 - center.x; 142 | rotation_matrix.at(1,2) += box.height / 2.0 - center.y; 143 | cv::Mat rotated_image; 144 | cv::warpAffine(original_image, rotated_image, rotation_matrix, box.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT, white); 145 | 146 | // now that the image has been rotated back to level, apply the neural network again to get more accurate results 147 | results = nn.predict(rotated_image); 148 | 149 | if (true) 150 | { 151 | // display the predictions so we can compare pre-rotation and post-rotation results 152 | cv::imshow("annotated (post-rotation)", nn.annotate()); 153 | } 154 | } 155 | 156 | std::cout << image << ": " << results << std::endl; 157 | 158 | const auto key = cv::waitKey(); 159 | if (key == 27) 160 | { 161 | break; 162 | } 163 | } 164 | } 165 | catch (const std::exception & e) 166 | { 167 | std::cout << e.what() << std::endl; 168 | rc = 1; 169 | } 170 | 171 | return rc; 172 | } 173 | -------------------------------------------------------------------------------- /src-apps/save_webcam_to_video.cpp: -------------------------------------------------------------------------------- 1 | /* DarkHelp - C++ helper class for Darknet's C API. 2 | * Copyright 2019-2024 Stephane Charette 3 | * MIT license applies. See "license.txt" for details. 4 | */ 5 | 6 | #include "DarkHelp.hpp" 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | int rc = 0; 11 | 12 | try 13 | { 14 | if (argc != 4) 15 | { 16 | std::cout 17 | << "Usage:" << std::endl 18 | << argv[0] << " " << std::endl; 19 | throw std::invalid_argument("wrong number of arguments"); 20 | } 21 | 22 | // Load the neural network. The order of the 3 files does not matter, DarkHelp should figure out which file is which. 23 | DarkHelp::NN nn(argv[1], argv[2], argv[3]); 24 | 25 | // Use OpenCV to open the webcam. Index zero is the first webcam. Attemp to set a few camera properties. 26 | cv::VideoCapture cap(0); 27 | if (not cap.isOpened()) 28 | { 29 | throw std::runtime_error("failed to open the webcam"); 30 | } 31 | cap.set(cv::CAP_PROP_FPS, 30.0); 32 | cap.set(cv::CAP_PROP_FRAME_WIDTH, 640.0); 33 | cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480.0); 34 | 35 | /* Be careful here. Just because we requested a certain size and FPS doesn't mean the camera supports that mode, 36 | * or that OpenCV was successful in setting those values. You should check to see if the values we think we set are 37 | * the values that OpenCV and the webcam are using. 38 | */ 39 | cv::Mat frame; 40 | cap >> frame; 41 | const double fps = cap.get(cv::CAP_PROP_FPS); 42 | cv::VideoWriter out("out.mp4", cv::VideoWriter::fourcc('m', 'p', '4', 'v'), fps, frame.size()); 43 | 44 | /* The length of time we wait should be 1000 milliseconds divided by the FPS. So for 30 FPS video, we might wait 45 | * 33.333 milliseconds between each frame. But this example is very simple, and we're not getting the current time 46 | * to calibrate against to ensure each frame is shown for the right duration. Plus we make no attempt in the code 47 | * below to take into account the time it takes to run nn.predict() and nn.annotate(). 48 | * 49 | * So as a simple solution for a simple app, let's divide the wait time in half. This should give the HighGUI event 50 | * loop time to run, but shouldn't be too much where we introduce lag. 51 | */ 52 | const int milliseconds_to_wait = std::round(1000.0 / 2.0 / fps); 53 | while (cap.isOpened()) 54 | { 55 | cap >> frame; 56 | if (frame.empty()) 57 | { 58 | // something went wrong -- for example, webcam may have been disconnected 59 | break; 60 | } 61 | 62 | nn.predict(frame); 63 | auto annotated_frame = nn.annotate(); 64 | out.write(annotated_frame); 65 | 66 | cv::imshow("video", annotated_frame); 67 | const auto key = cv::waitKey(milliseconds_to_wait); 68 | if (key == 27) 69 | { 70 | break; 71 | } 72 | } 73 | } 74 | catch (const std::exception & e) 75 | { 76 | std::cout << e.what() << std::endl; 77 | rc = 1; 78 | } 79 | 80 | return rc; 81 | } 82 | -------------------------------------------------------------------------------- /src-apps/using_c_api.cpp: -------------------------------------------------------------------------------- 1 | /* DarkHelp - C++ helper class for Darknet's C API. 2 | * Copyright 2019-2024 Stephane Charette 3 | * MIT license applies. See "license.txt" for details. 4 | */ 5 | 6 | 7 | // This source file is written in C++ so we can use OpenCV easily, 8 | // but the calls demonstrated are from the "C" DarkHelp API. 9 | #include "DarkHelp_C_API.h" 10 | #include 11 | #include 12 | 13 | 14 | int main(int argc, char * argv[]) 15 | { 16 | int rc = 0; 17 | 18 | try 19 | { 20 | if (argc != 5) 21 | { 22 | std::cout 23 | << "Usage:" << std::endl 24 | << argv[0] << " " << std::endl; 25 | throw std::invalid_argument("wrong number of arguments"); 26 | } 27 | 28 | const std::string fn1 = argv[1]; 29 | const std::string fn2 = argv[2]; 30 | const std::string fn3 = argv[3]; 31 | const std::string image = argv[4]; 32 | 33 | std::cout 34 | << "Darknet v" << DarknetVersion() << std::endl 35 | << "DarkHelp v" << DarkHelpVersion() << std::endl; 36 | 37 | DarkHelpPtr ptr = CreateDarkHelpNN(fn1.c_str(), fn2.c_str(), fn3.c_str()); 38 | 39 | /* All of the DarkHelp settings have default values described in the documentation. Calling these here 40 | * just for demonstration. You don't have to call all of these if the default value works for you. 41 | */ 42 | SetThreshold(ptr, 0.25); 43 | EnableNamesIncludePercentage(ptr, true); 44 | EnableAnnotationAutoHideLabels(ptr, false); 45 | SetAnnotationShadePredictions(ptr, 0.15f); 46 | SetAnnotationFontScale(ptr, 0.5); 47 | SetAnnotationFontThickness(ptr, 1); 48 | SetAnnotationLineThickness(ptr, 1); 49 | EnableAnnotationIncludeDuration(ptr, false); 50 | EnableAnnotationIncludeTimestamp(ptr, false); 51 | EnableTiles(ptr, false); 52 | EnableSnapping(ptr, true); 53 | 54 | PredictFN(ptr, image.c_str()); 55 | 56 | const char * json = GetPredictionResults(ptr); 57 | std::cout << "results=" << json << std::endl; 58 | 59 | Annotate(ptr, "testing.jpg"); 60 | 61 | /* Once the DarkHelp object has been destroyed, make sure you don't make any further 62 | * DarkHelp "C" calls other than CreateDarkHelpNN() to create a new instance. 63 | */ 64 | DestroyDarkHelpNN(ptr); 65 | 66 | // see what the output image looks like 67 | cv::Mat mat = cv::imread("testing.jpg"); 68 | cv::imshow("annotated", mat); 69 | cv::waitKey(); 70 | } 71 | catch (const std::exception & e) 72 | { 73 | std::cout << e.what() << std::endl; 74 | rc = 1; 75 | } 76 | 77 | return rc; 78 | } 79 | -------------------------------------------------------------------------------- /src-apps/video_display_realtime.cpp: -------------------------------------------------------------------------------- 1 | /* DarkHelp - C++ helper class for Darknet's C API. 2 | * Copyright 2019-2024 Stephane Charette 3 | * MIT license applies. See "license.txt" for details. 4 | */ 5 | 6 | #include "DarkHelp.hpp" 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | int rc = 0; 11 | 12 | try 13 | { 14 | if (argc != 5) 15 | { 16 | std::cout 17 | << "Usage:" << std::endl 18 | << argv[0] << "