From 9dd78c3c845f14b8d644098d19a4ee971fb6b108 Mon Sep 17 00:00:00 2001 From: gineshidalgo99 Date: Wed, 26 Apr 2017 18:17:18 -0400 Subject: [PATCH] Doxygen doc for Wrapper --- README.md | 17 +- doc/doc_autogeneration.doxygen | 4 +- doc/installation.md | 7 +- doc/library_add_new_module.md | 2 +- examples/openpose/rtpose.cpp | 12 +- .../tutorial_wrapper/1_user_asynchronous.cpp | 12 +- .../tutorial_wrapper/2_user_synchronous.cpp | 10 +- include/openpose/gui/enumClasses.hpp | 3 +- include/openpose/producer/enumClasses.hpp | 3 +- include/openpose/thread/enumClasses.hpp | 18 +- include/openpose/thread/threadManager.hpp | 80 +-- include/openpose/wrapper/enumClasses.hpp | 13 - include/openpose/wrapper/headers.hpp | 5 +- include/openpose/wrapper/wrapper.hpp | 579 ++++++++++-------- .../openpose/wrapper/wrapperStructHands.hpp | 32 + .../openpose/wrapper/wrapperStructInput.hpp | 65 ++ .../openpose/wrapper/wrapperStructOutput.hpp | 103 ++++ .../openpose/wrapper/wrapperStructPose.hpp | 135 ++++ src/openpose/wrapper/wrapper.cpp | 66 -- src/openpose/wrapper/wrapperStructHands.cpp | 12 + src/openpose/wrapper/wrapperStructInput.cpp | 16 + src/openpose/wrapper/wrapperStructOutput.cpp | 23 + src/openpose/wrapper/wrapperStructPose.cpp | 28 + 23 files changed, 850 insertions(+), 395 deletions(-) delete mode 100644 include/openpose/wrapper/enumClasses.hpp create mode 100644 include/openpose/wrapper/wrapperStructHands.hpp create mode 100644 include/openpose/wrapper/wrapperStructInput.hpp create mode 100644 include/openpose/wrapper/wrapperStructOutput.hpp create mode 100644 include/openpose/wrapper/wrapperStructPose.hpp delete mode 100644 src/openpose/wrapper/wrapper.cpp create mode 100644 src/openpose/wrapper/wrapperStructHands.cpp create mode 100644 src/openpose/wrapper/wrapperStructInput.cpp create mode 100644 src/openpose/wrapper/wrapperStructOutput.cpp create mode 100644 src/openpose/wrapper/wrapperStructPose.cpp diff --git a/README.md b/README.md index 0420d7d5..59482d5a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ OpenPose ## Introduction -OpenPose is a **library for real-time multi-person key-point detection and multi-threading written in C++** using OpenCV and Caffe, authored by [Gines Hidalgo](https://www.linkedin.com/in/gineshidalgo/), [Zhe Cao](http://www.andrew.cmu.edu/user/zhecao), [Tomas Simon](http://www.cs.cmu.edu/~tsimon/), [Shih-En Wei](https://scholar.google.com/citations?user=sFQD3k4AAAAJ&hl=en), [Yaser Sheikh](http://www.cs.cmu.edu/~yaser/). +OpenPose is a **library for real-time multi-person key-point detection and multi-threading written in C++** using OpenCV and Caffe, authored by [Gines Hidalgo](https://www.linkedin.com/in/gineshidalgo/), [Zhe Cao](http://www.andrew.cmu.edu/user/zhecao), [Tomas Simon](http://www.cs.cmu.edu/~tsimon/), [Shih-En Wei](https://scholar.google.com/citations?user=sFQD3k4AAAAJ&hl=en) and [Yaser Sheikh](http://www.cs.cmu.edu/~yaser/). OpenPose is freely available for free non-commercial use, and may be redistributed under these conditions. Please, see the [license](LICENSE) for further details. Contact us for commercial purposes. @@ -53,7 +53,7 @@ This work is based on the C++ code from [C++ real-time ECCV 2016 demo](https://g ## Installation -Installation steps on [installation.md](doc/installation.md). +Installation steps on [doc/installation.md](doc/installation.md). @@ -65,7 +65,7 @@ Most users cases should not need to dive deep into the library, they might just #### Demo Your case if you just want to process a folder of images or video or webcam and display or save the pose results. -Forget about the OpenPose library details and just read the [demo_overview.md](doc/demo_overview.md) 1-page section. +Forget about the OpenPose library details and just read the [doc/demo_overview.md](doc/demo_overview.md) 1-page section. #### OpenPose Wrapper Your case if you want to read a specific format of image source and/or add a specific post-processing function and/or implement your own display/saving. @@ -77,16 +77,17 @@ Note: you should not need to modify OpenPose source code or examples, so that yo #### OpenPose Library Your case if you want to change internal functions and/or extend its functionality. First, take a look to the [Demo](#demo) and [OpenPose Wrapper](#openpose-wrapper). Secondly, read the 2 following subsections: OpenPose Overview and Extending Functionality. -1. OpenPose Overview: Learn the basics about our library source code on [library_overview.md](doc/library_overview.md). +1. OpenPose Overview: Learn the basics about our library source code on [doc/library_overview.md](doc/library_overview.md). -2. Extending Functionality: Learn how to extend our library on [library_extend_functionality.md](doc/library_extend_functionality.md). +2. Extending Functionality: Learn how to extend our library on [doc/library_extend_functionality.md](doc/library_extend_functionality.md). -3. Adding An Extra Module: Learn how to add an extra module on [library_add_new_module.md](doc/library_add_new_module.md). +3. Adding An Extra Module: Learn how to add an extra module on [doc/library_add_new_module.md](doc/library_add_new_module.md). #### Doxygen Documentation Autogeneration -You can generate the documentation by running the following command. The documentation will be generated on `doc/doxygen/html/index.html`. You can simply open it with double click and your favourite browser will display it. +You can generate the documentation by running the following command. The documentation will be generated on `doc/doxygen/html/index.html`. You can simply open it with double click (your default browser should automatically display it). ``` -doxygen doc/doc_autogeneration.doxygen +cd doc/ +doxygen doc_autogeneration.doxygen ``` diff --git a/doc/doc_autogeneration.doxygen b/doc/doc_autogeneration.doxygen index 5818a283..a256f24c 100644 --- a/doc/doc_autogeneration.doxygen +++ b/doc/doc_autogeneration.doxygen @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = /home/gines/Dropbox/Perceptual_Computing_Lab/cpm/cpm/doc/doxygen +OUTPUT_DIRECTORY = doxygen # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -781,7 +781,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = /home/gines/Dropbox/Perceptual_Computing_Lab/cpm/cpm/include +INPUT = ../include/openpose/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/doc/installation.md b/doc/installation.md index 66eec730..2e07ec46 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -1,14 +1,15 @@ OpenPose Library - Compilation and Installation ==================================== -# Requirements +## Requirements +- Ubuntu (tested on 14 and 16) - GPU with at least 2 GB and 1.5 GB available (the `nvidia-smi` command checks the available GPU memory in Ubuntu). - At least 2 GB of free RAM memory. - Highly recommended: A CPU with at least 8 cores. Note: This requirements assume the default configuration (i.e. `--net_resolution "656x368"` and `num_scales 1`). You might need more (with a greater net resolution and/or number of scales) or less resources (with smaller net resolution and/or using the MPI and MPI_4 models). -# How to Compile It +## How to Compile It 1. Required: CUDA, cuDNN and OpenCV installed on your machine. OpenCV can be easily installed by running `apt-get install libopencv-dev`. 2. If you have installed OpenCV 2.4 in your system, go to step 3. If you are using OpenCV 3, uncomment the line `# OPENCV_VERSION := 3` on the file `Makefile.config.Ubuntu14.example` (for Ubuntu 14) and/or `Makefile.config.Ubuntu16.example` (for Ubuntu 15 or 16). In addition, OpenCV 3 does not incorporate the `opencv_contrib` module by default. Assuming you have manually installed it and you need to use it, append `opencv_contrib` at the end of the line `LIBRARIES += opencv_core opencv_highgui opencv_imgproc` in the `Makefile` file. 3. Build Caffe & the OpenPose library + download the required Caffe models for Ubuntu 14.04 or 16.04 and cuda 8: @@ -49,7 +50,7 @@ make clean make all -j$(NUM_CORES) ``` -# Custom Caffe +## Custom Caffe We slightly modified some Caffe compilation flags and minor details. In case you want to use your own Caffe distribution, these are the files we added and modified: 1. Added files: `install_caffe_and_openpose.sh`; as well as `Makefile.config.Ubuntu14.example`, `Makefile.config.Ubuntu16.example`, `Makefile.config.Ubuntu14_cuda_7.example` and `Makefile.config.Ubuntu16_cuda_7.example` (extracted from `Makefile.config.example`). diff --git a/doc/library_add_new_module.md b/doc/library_add_new_module.md index 080e3727..7a4b1167 100644 --- a/doc/library_add_new_module.md +++ b/doc/library_add_new_module.md @@ -16,7 +16,7 @@ In order to add a new module, these are the recommended steps in order to develo 2. Use the whole op::Datum as unique argument of your auxiliary functions. - 3. Use the OpenPose Wrapper in ThreadMode::SingleThread mode (e.g. it allows you to directly use cv::imshow). + 3. Use the OpenPose Wrapper in ThreadManagerMode::SingleThread mode (e.g. it allows you to directly use cv::imshow). 4. If you are using your own custom Caffe -> initially change the Caffe for your version. It should directly work. diff --git a/examples/openpose/rtpose.cpp b/examples/openpose/rtpose.cpp index c9d15428..f1052fd4 100755 --- a/examples/openpose/rtpose.cpp +++ b/examples/openpose/rtpose.cpp @@ -248,20 +248,20 @@ int opRealTimePoseDemo() // OpenPose wrapper op::log("Configuring OpenPose wrapper.", op::Priority::Low, __LINE__, __FUNCTION__, __FILE__); op::Wrapper> opWrapper; - const op::WrapperPoseStruct wrapperPoseStruct{netInputSize, outputSize, scaleMode, FLAGS_num_gpu, FLAGS_num_gpu_start, FLAGS_num_scales, (float)FLAGS_scale_gap, + const op::WrapperStructPose wrapperStructPose{netInputSize, outputSize, scaleMode, FLAGS_num_gpu, FLAGS_num_gpu_start, FLAGS_num_scales, (float)FLAGS_scale_gap, !FLAGS_no_render_output, poseModel, !FLAGS_disable_blending, (float)FLAGS_alpha_pose, (float)FLAGS_alpha_heatmap, FLAGS_part_to_show, FLAGS_model_folder, heatMapTypes, op::ScaleMode::UnsignedChar}; - const op::WrapperInputStruct wrapperInputStruct{producerSharedPtr, FLAGS_frame_first, FLAGS_frame_last, FLAGS_process_real_time, FLAGS_frame_flip, + const op::WrapperStructInput wrapperStructInput{producerSharedPtr, FLAGS_frame_first, FLAGS_frame_last, FLAGS_process_real_time, FLAGS_frame_flip, FLAGS_frame_rotate, FLAGS_frames_repeat}; - const op::WrapperOutputStruct wrapperOutputStruct{!FLAGS_no_display, !FLAGS_no_gui_verbose, FLAGS_fullscreen, FLAGS_write_pose, op::stringToDataFormat(FLAGS_write_pose_format), + const op::WrapperStructOutput wrapperStructOutput{!FLAGS_no_display, !FLAGS_no_gui_verbose, FLAGS_fullscreen, FLAGS_write_pose, op::stringToDataFormat(FLAGS_write_pose_format), FLAGS_write_pose_json, FLAGS_write_coco_json, FLAGS_write_images, FLAGS_write_images_format, FLAGS_write_video, FLAGS_write_heatmaps, FLAGS_write_heatmaps_format}; - // Pose configuration (use WrapperPoseStruct{} for default and recommended configuration) + // Pose configuration (use WrapperStructPose{} for default and recommended configuration) // Producer (use default to disable any input) // Consumer (comment or use default argument to disable any output) - opWrapper.configure(wrapperPoseStruct, wrapperInputStruct, wrapperOutputStruct); + opWrapper.configure(wrapperStructPose, wrapperStructInput, wrapperStructOutput); // Set to single-thread running (for debugging purposes) - // opWrapper.setWrapperMode(op::WrapperMode::SingleThread); + // opWrapper.disableMultiThreading(); // Start processing // Two different ways of running the program on multithread enviroment diff --git a/examples/tutorial_wrapper/1_user_asynchronous.cpp b/examples/tutorial_wrapper/1_user_asynchronous.cpp index 2c1be7e2..8841e843 100644 --- a/examples/tutorial_wrapper/1_user_asynchronous.cpp +++ b/examples/tutorial_wrapper/1_user_asynchronous.cpp @@ -282,22 +282,22 @@ int openPoseTutorialWrapper1() op::log("", op::Priority::Low, __LINE__, __FUNCTION__, __FILE__); // Configure OpenPose - op::Wrapper> opWrapper{op::ThreadMode::Asynchronous}; + op::Wrapper> opWrapper{op::ThreadManagerMode::Asynchronous}; const bool displayGui = false; const bool guiVerbose = false; const bool fullScreen = false; - const op::WrapperPoseStruct wrapperPoseStruct{netInputSize, outputSize, scaleMode, FLAGS_num_gpu, FLAGS_num_gpu_start, FLAGS_num_scales, (float)FLAGS_scale_gap, + const op::WrapperStructPose wrapperStructPose{netInputSize, outputSize, scaleMode, FLAGS_num_gpu, FLAGS_num_gpu_start, FLAGS_num_scales, (float)FLAGS_scale_gap, !FLAGS_no_render_output, poseModel, !FLAGS_disable_blending, (float)FLAGS_alpha_pose, (float)FLAGS_alpha_heatmap, FLAGS_part_to_show, FLAGS_model_folder, heatMapTypes, heatMapsScaleMode}; - const op::WrapperOutputStruct wrapperOutputStruct{displayGui, guiVerbose, fullScreen, FLAGS_write_pose, op::stringToDataFormat(FLAGS_write_pose_format), + const op::WrapperStructOutput wrapperStructOutput{displayGui, guiVerbose, fullScreen, FLAGS_write_pose, op::stringToDataFormat(FLAGS_write_pose_format), FLAGS_write_pose_json, FLAGS_write_coco_json, FLAGS_write_images, FLAGS_write_images_format, FLAGS_write_video, FLAGS_write_heatmaps, FLAGS_write_heatmaps_format}; - // Pose configuration (use WrapperPoseStruct{} for default and recommended configuration) + // Pose configuration (use WrapperStructPose{} for default and recommended configuration) // Producer (use default to disable any input) // Consumer (comment or use default argument to disable any output) - opWrapper.configure(wrapperPoseStruct, op::WrapperInputStruct{}, wrapperOutputStruct); + opWrapper.configure(wrapperStructPose, op::WrapperStructInput{}, wrapperStructOutput); // Set to single-thread running (for debugging purposes) - // opWrapper.setWrapperMode(op::WrapperMode::SingleThread); + // opWrapper.disableMultiThreading(); op::log("Starting thread(s)", op::Priority::Max); opWrapper.start(); diff --git a/examples/tutorial_wrapper/2_user_synchronous.cpp b/examples/tutorial_wrapper/2_user_synchronous.cpp index 4551baf8..d37ff4f6 100644 --- a/examples/tutorial_wrapper/2_user_synchronous.cpp +++ b/examples/tutorial_wrapper/2_user_synchronous.cpp @@ -348,18 +348,18 @@ int openPoseTutorialWrapper2() const bool displayGui = false; const bool guiVerbose = false; const bool fullScreen = false; - const op::WrapperPoseStruct wrapperPoseStruct{netInputSize, outputSize, scaleMode, FLAGS_num_gpu, FLAGS_num_gpu_start, FLAGS_num_scales, (float)FLAGS_scale_gap, + const op::WrapperStructPose wrapperStructPose{netInputSize, outputSize, scaleMode, FLAGS_num_gpu, FLAGS_num_gpu_start, FLAGS_num_scales, (float)FLAGS_scale_gap, !FLAGS_no_render_output, poseModel, !FLAGS_disable_blending, (float)FLAGS_alpha_pose, (float)FLAGS_alpha_heatmap, FLAGS_part_to_show, FLAGS_model_folder, heatMapTypes, heatMapsScaleMode}; - const op::WrapperOutputStruct wrapperOutputStruct{displayGui, guiVerbose, fullScreen, FLAGS_write_pose, op::stringToDataFormat(FLAGS_write_pose_format), + const op::WrapperStructOutput wrapperStructOutput{displayGui, guiVerbose, fullScreen, FLAGS_write_pose, op::stringToDataFormat(FLAGS_write_pose_format), FLAGS_write_pose_json, FLAGS_write_coco_json, FLAGS_write_images, FLAGS_write_images_format, FLAGS_write_video, FLAGS_write_heatmaps, FLAGS_write_heatmaps_format}; - // Pose configuration (use WrapperPoseStruct{} for default and recommended configuration) + // Pose configuration (use WrapperStructPose{} for default and recommended configuration) // Producer (use default to disable any input) // Consumer (comment or use default argument to disable any output) - opWrapper.configure(wrapperPoseStruct, op::WrapperInputStruct{}, wrapperOutputStruct); + opWrapper.configure(wrapperStructPose, op::WrapperStructInput{}, wrapperStructOutput); // Set to single-thread running (for debugging purposes) - // opWrapper.setWrapperMode(op::WrapperMode::SingleThread); + // opWrapper.disableMultiThreading(); op::log("Starting thread(s)", op::Priority::Max); // Two different ways of running the program on multithread enviroment diff --git a/include/openpose/gui/enumClasses.hpp b/include/openpose/gui/enumClasses.hpp index e69bf50d..fa72d84f 100644 --- a/include/openpose/gui/enumClasses.hpp +++ b/include/openpose/gui/enumClasses.hpp @@ -4,7 +4,8 @@ namespace op { /** - * A class enum with the different output screen options (e.g. full screen, windored or disabling the display). + * GUI display modes. + * An enum class with the different output screen options (e.g. full screen, windored or disabling the display). */ enum class GuiDisplayMode : bool { diff --git a/include/openpose/producer/enumClasses.hpp b/include/openpose/producer/enumClasses.hpp index d9217c73..b1469e99 100644 --- a/include/openpose/producer/enumClasses.hpp +++ b/include/openpose/producer/enumClasses.hpp @@ -18,7 +18,8 @@ namespace op }; /** - * A class enum in which all the possible type of Producer are included. In order to add a new Producer, + * Type of producers + * An enum class in which all the possible type of Producer are included. In order to add a new Producer, * include its name in this enum and add a new 'else if' statement inside ProducerFactory::createProducer(). */ enum class ProducerType : unsigned char diff --git a/include/openpose/thread/enumClasses.hpp b/include/openpose/thread/enumClasses.hpp index 7aa29a85..b3e43cd0 100644 --- a/include/openpose/thread/enumClasses.hpp +++ b/include/openpose/thread/enumClasses.hpp @@ -3,11 +3,23 @@ namespace op { - enum class ThreadMode : unsigned char + /** + * ThreadManager synchronization mode. + */ + enum class ThreadManagerMode : unsigned char { + /** + * First and last queues of ThreadManager will be given to the user, so he must push elements to the first queue and retrieve + * them from the last one after being processed. + * Recommended for prototyping environments (easier to test but more error-prone and potentially slower in performance). + */ Asynchronous, - AsynchronousIn, - AsynchronousOut, + AsynchronousIn, /**< Similar to Asynchronous, but only the input (first) queue is given to the user. */ + AsynchronousOut, /**< Similar to Asynchronous, but only the output (last) queue is given to the user. */ + /** + * Everything will run inside the ThreadManager. + * Recommended for production environments (more difficult to set up but faster in performance and less error-prone). + */ Synchronous, }; } diff --git a/include/openpose/thread/threadManager.hpp b/include/openpose/thread/threadManager.hpp index 049e6096..b765cbcf 100644 --- a/include/openpose/thread/threadManager.hpp +++ b/include/openpose/thread/threadManager.hpp @@ -19,7 +19,7 @@ namespace op { public: // Completely customizable case - explicit ThreadManager(const ThreadMode threadMode = ThreadMode::Synchronous); + explicit ThreadManager(const ThreadManagerMode threadManagerMode = ThreadManagerMode::Synchronous); void setDefaultMaxSizeQueues(const long long defaultMaxSizeQueues = -1); @@ -58,7 +58,7 @@ namespace op bool waitAndPop(TDatums& tDatums); private: - const ThreadMode mThreadMode; + const ThreadManagerMode mThreadManagerMode; std::shared_ptr> spIsRunning; long long mDefaultMaxSizeQueues; std::multiset, unsigned long long, unsigned long long>> mThreadWorkerQueues; @@ -95,8 +95,8 @@ namespace op namespace op { template - ThreadManager::ThreadManager(const ThreadMode threadMode) : - mThreadMode{threadMode}, + ThreadManager::ThreadManager(const ThreadManagerMode threadManagerMode) : + mThreadManagerMode{threadManagerMode}, spIsRunning{std::make_shared>(false)}, mDefaultMaxSizeQueues{-1ll} { @@ -130,7 +130,8 @@ namespace op } template - void ThreadManager::add(const unsigned long long threadId, const TWorker& tWorker, const unsigned long long queueInId, const unsigned long long queueOutId) + void ThreadManager::add(const unsigned long long threadId, const TWorker& tWorker, const unsigned long long queueInId, + const unsigned long long queueOutId) { try { @@ -227,8 +228,8 @@ namespace op { try { - if (mThreadMode != ThreadMode::Asynchronous && mThreadMode != ThreadMode::AsynchronousIn) - error("Not available for this ThreadMode.", __LINE__, __FUNCTION__, __FILE__); + if (mThreadManagerMode != ThreadManagerMode::Asynchronous && mThreadManagerMode != ThreadManagerMode::AsynchronousIn) + error("Not available for this ThreadManagerMode.", __LINE__, __FUNCTION__, __FILE__); if (mTQueues.empty()) error("ThreadManager already stopped or not started yet.", __LINE__, __FUNCTION__, __FILE__); return mTQueues[0]->tryEmplace(tDatums); @@ -245,8 +246,8 @@ namespace op { try { - if (mThreadMode != ThreadMode::Asynchronous && mThreadMode != ThreadMode::AsynchronousIn) - error("Not available for this ThreadMode.", __LINE__, __FUNCTION__, __FILE__); + if (mThreadManagerMode != ThreadManagerMode::Asynchronous && mThreadManagerMode != ThreadManagerMode::AsynchronousIn) + error("Not available for this ThreadManagerMode.", __LINE__, __FUNCTION__, __FILE__); if (mTQueues.empty()) error("ThreadManager already stopped or not started yet.", __LINE__, __FUNCTION__, __FILE__); return mTQueues[0]->waitAndEmplace(tDatums); @@ -263,8 +264,8 @@ namespace op { try { - if (mThreadMode != ThreadMode::Asynchronous && mThreadMode != ThreadMode::AsynchronousIn) - error("Not available for this ThreadMode.", __LINE__, __FUNCTION__, __FILE__); + if (mThreadManagerMode != ThreadManagerMode::Asynchronous && mThreadManagerMode != ThreadManagerMode::AsynchronousIn) + error("Not available for this ThreadManagerMode.", __LINE__, __FUNCTION__, __FILE__); if (mTQueues.empty()) error("ThreadManager already stopped or not started yet.", __LINE__, __FUNCTION__, __FILE__); return mTQueues[0]->tryPush(tDatums); @@ -281,8 +282,8 @@ namespace op { try { - if (mThreadMode != ThreadMode::Asynchronous && mThreadMode != ThreadMode::AsynchronousIn) - error("Not available for this ThreadMode.", __LINE__, __FUNCTION__, __FILE__); + if (mThreadManagerMode != ThreadManagerMode::Asynchronous && mThreadManagerMode != ThreadManagerMode::AsynchronousIn) + error("Not available for this ThreadManagerMode.", __LINE__, __FUNCTION__, __FILE__); if (mTQueues.empty()) error("ThreadManager already stopped or not started yet.", __LINE__, __FUNCTION__, __FILE__); return mTQueues[0]->waitAndPush(tDatums); @@ -299,8 +300,8 @@ namespace op { try { - if (mThreadMode != ThreadMode::Asynchronous && mThreadMode != ThreadMode::AsynchronousOut) - error("Not available for this ThreadMode.", __LINE__, __FUNCTION__, __FILE__); + if (mThreadManagerMode != ThreadManagerMode::Asynchronous && mThreadManagerMode != ThreadManagerMode::AsynchronousOut) + error("Not available for this ThreadManagerMode.", __LINE__, __FUNCTION__, __FILE__); if (mTQueues.empty()) error("ThreadManager already stopped or not started yet.", __LINE__, __FUNCTION__, __FILE__); return (*mTQueues.rbegin())->tryPop(tDatums); @@ -317,8 +318,8 @@ namespace op { try { - if (mThreadMode != ThreadMode::Asynchronous && mThreadMode != ThreadMode::AsynchronousOut) - error("Not available for this ThreadMode.", __LINE__, __FUNCTION__, __FILE__); + if (mThreadManagerMode != ThreadManagerMode::Asynchronous && mThreadManagerMode != ThreadManagerMode::AsynchronousOut) + error("Not available for this ThreadManagerMode.", __LINE__, __FUNCTION__, __FILE__); if (mTQueues.empty()) error("ThreadManager already stopped or not started yet.", __LINE__, __FUNCTION__, __FILE__); return (*mTQueues.rbegin())->waitAndPop(tDatums); @@ -331,7 +332,8 @@ namespace op } template - void ThreadManager::add(const std::vector, unsigned long long, unsigned long long>>& threadWorkerQueues) + void ThreadManager::add(const std::vector, + unsigned long long, unsigned long long>>& threadWorkerQueues) { try { @@ -345,12 +347,14 @@ namespace op } template - void ThreadManager::add(const std::vector>& threadWorkerQueues) + void ThreadManager::add(const std::vector>& threadWorkerQueues) { try { for (const auto& threadWorkerQueue : threadWorkerQueues) - add({std::make_tuple(std::get<0>(threadWorkerQueue), std::vector{std::get<1>(threadWorkerQueue)}, std::get<2>(threadWorkerQueue), std::get<3>(threadWorkerQueue))}); + add({std::make_tuple(std::get<0>(threadWorkerQueue), std::vector{std::get<1>(threadWorkerQueue)}, + std::get<2>(threadWorkerQueue), std::get<3>(threadWorkerQueue))}); } catch (const std::exception& e) { @@ -379,32 +383,32 @@ namespace op { auto& thread = mThreads[std::get<0>(threadWorkerQueue)]; const auto& tWorkers = std::get<1>(threadWorkerQueue); - const auto queueInId = std::get<2>(threadWorkerQueue); - const auto queueOutId = std::get<3>(threadWorkerQueue); + const auto queueIn = std::get<2>(threadWorkerQueue); + const auto queueOut = std::get<3>(threadWorkerQueue); std::shared_ptr> subThread; // If AsynchronousIn -> queue indexes are OK - if (mThreadMode == ThreadMode::Asynchronous || mThreadMode == ThreadMode::AsynchronousIn) + if (mThreadManagerMode == ThreadManagerMode::Asynchronous || mThreadManagerMode == ThreadManagerMode::AsynchronousIn) { - if (mThreadMode == ThreadMode::AsynchronousIn && queueOutId == mTQueues.size()) - subThread = {std::make_shared>(tWorkers, mTQueues.at(queueInId))}; + if (mThreadManagerMode == ThreadManagerMode::AsynchronousIn && queueOut == mTQueues.size()) + subThread = {std::make_shared>(tWorkers, mTQueues.at(queueIn))}; else - subThread = {std::make_shared>(tWorkers, mTQueues.at(queueInId), mTQueues.at(queueOutId))}; + subThread = {std::make_shared>(tWorkers, mTQueues.at(queueIn), mTQueues.at(queueOut))}; } // If !AsynchronousIn -> queue indexes - 1 - else if (queueOutId != maxQueueIdSynchronous || mThreadMode == ThreadMode::AsynchronousOut) + else if (queueOut != maxQueueIdSynchronous || mThreadManagerMode == ThreadManagerMode::AsynchronousOut) { // Queue in + out - if (queueInId != 0) - subThread = {std::make_shared>(tWorkers, mTQueues.at(queueInId-1), mTQueues.at(queueOutId-1))}; + if (queueIn != 0) + subThread = {std::make_shared>(tWorkers, mTQueues.at(queueIn-1), mTQueues.at(queueOut-1))}; // Case queue out (first TWorker(s)) else - subThread = {std::make_shared>(tWorkers, mTQueues.at(queueOutId-1))}; + subThread = {std::make_shared>(tWorkers, mTQueues.at(queueOut-1))}; } // Case queue in (last TWorker(s)) - else if (queueInId != 0) // && queueOutId == maxQueueIdSynchronous - subThread = {std::make_shared>(tWorkers, mTQueues.at(queueInId-1))}; + else if (queueIn != 0) // && queueOut == maxQueueIdSynchronous + subThread = {std::make_shared>(tWorkers, mTQueues.at(queueIn-1))}; // Case no queue - else // if (queueInId == 0 && queueOutId == maxQueueIdSynchronous) + else // if (queueIn == 0 && queueOut == maxQueueIdSynchronous) subThread = {std::make_shared>(tWorkers)}; thread->add(subThread); } @@ -430,7 +434,7 @@ namespace op { const auto currentThreadId = std::get<0>(threadWorkerQueue); if (currentThreadId - previousThreadId > 1) - error("Missing thread id " + std::to_string(currentThreadId) + " (of " + std::to_string(maxThreadId) + ").", __LINE__, __FUNCTION__, __FILE__); + error("Missing thread id " + std::to_string(currentThreadId) + " of " + std::to_string(maxThreadId) + ".", __LINE__, __FUNCTION__, __FILE__); previousThreadId = currentThreadId; } @@ -480,14 +484,14 @@ namespace op } // Create Queues - if (mThreadMode == ThreadMode::Asynchronous) + if (mThreadManagerMode == ThreadManagerMode::Asynchronous) mTQueues.resize(maxQueueId+1); // First and last one are queues - else if (mThreadMode == ThreadMode::Synchronous) + else if (mThreadManagerMode == ThreadManagerMode::Synchronous) mTQueues.resize(maxQueueId-1); // First and last one are not actually queues - else if (mThreadMode == ThreadMode::AsynchronousIn || mThreadMode == ThreadMode::AsynchronousOut) + else if (mThreadManagerMode == ThreadManagerMode::AsynchronousIn || mThreadManagerMode == ThreadManagerMode::AsynchronousOut) mTQueues.resize(maxQueueId); // First or last one is queue else - error("Unknown ThreadMode", __LINE__, __FUNCTION__, __FILE__); + error("Unknown ThreadManagerMode", __LINE__, __FUNCTION__, __FILE__); for (auto& tQueue : mTQueues) tQueue = {std::make_shared(mDefaultMaxSizeQueues)}; } diff --git a/include/openpose/wrapper/enumClasses.hpp b/include/openpose/wrapper/enumClasses.hpp deleted file mode 100644 index 1232c1d2..00000000 --- a/include/openpose/wrapper/enumClasses.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef OPENPOSE__WRAPPER__ENUM_CLASSES_HPP -#define OPENPOSE__WRAPPER__ENUM_CLASSES_HPP - -namespace op -{ - enum class WrapperMode : bool - { - SingleThread, - MultiThread, - }; -} - -#endif // OPENPOSE__WRAPPER__ENUM_CLASSES_HPP diff --git a/include/openpose/wrapper/headers.hpp b/include/openpose/wrapper/headers.hpp index 5359e9be..528a9ab0 100644 --- a/include/openpose/wrapper/headers.hpp +++ b/include/openpose/wrapper/headers.hpp @@ -2,7 +2,10 @@ #define OPENPOSE__WRAPPER__HEADERS_HPP // wrapper module -#include "enumClasses.hpp" #include "wrapper.hpp" +#include "wrapperStructHands.hpp" +#include "wrapperStructInput.hpp" +#include "wrapperStructOutput.hpp" +#include "wrapperStructPose.hpp" #endif // OPENPOSE__WRAPPER__HEADERS_HPP diff --git a/include/openpose/wrapper/wrapper.hpp b/include/openpose/wrapper/wrapper.hpp index fc5920fc..190cf2ec 100644 --- a/include/openpose/wrapper/wrapper.hpp +++ b/include/openpose/wrapper/wrapper.hpp @@ -1,158 +1,177 @@ #ifndef OPENPOSE__WRAPPER__WRAPPER_HPP #define OPENPOSE__WRAPPER__WRAPPER_HPP -#include "../core/headers.hpp" -#include "../experimental/hands/headers.hpp" -#include "../filestream/headers.hpp" -#include "../pose/headers.hpp" -#include "../producer/headers.hpp" #include "../thread/headers.hpp" -#include "enumClasses.hpp" +#include "wrapperStructPose.hpp" +#include "wrapperStructHands.hpp" +#include "wrapperStructInput.hpp" +#include "wrapperStructOutput.hpp" namespace op { - struct WrapperPoseStruct - { - cv::Size netInputSize; - cv::Size outputSize; - ScaleMode scaleMode; - int gpuNumber; - int gpuNumberStart; - int scalesNumber; - float scaleGap; - bool renderOutput; - PoseModel poseModel; - bool blendOriginalFrame; - float alphaPose; - float alphaHeatMap; - int defaultPartToRender; - std::string modelFolder; - std::vector heatMapTypes; - ScaleMode heatMapScaleMode; - - WrapperPoseStruct(const cv::Size& netInputSize = cv::Size{656, 368}, const cv::Size& outputSize = cv::Size{1280, 720}, - const ScaleMode scaleMode = ScaleMode::InputResolution, const int gpuNumber = 1, const int gpuNumberStart = 0, const int scalesNumber = 1, - const float scaleGap = 0.15f, const bool renderOutput = false, const PoseModel poseModel = PoseModel::COCO_18, - const bool blendOriginalFrame = true, const float alphaPose = POSE_DEFAULT_ALPHA_POSE, const float alphaHeatMap = POSE_DEFAULT_ALPHA_HEATMAP, - const int defaultPartToRender = 0, const std::string& modelFolder = "models/", - const std::vector& heatMapTypes = {}, const ScaleMode heatMapScaleMode = ScaleMode::ZeroToOne); - }; - - namespace experimental - { - struct WrapperHandsStruct - { - bool extractAndRenderHands; - - WrapperHandsStruct(const bool extractAndRenderHands = false); - }; - } - - struct WrapperInputStruct - { - std::shared_ptr producerSharedPtr; - unsigned long long frameFirst; - unsigned long long frameLast; - bool realTimeProcessing; - bool frameFlip; - int frameRotate; - bool framesRepeat; - - WrapperInputStruct(const std::shared_ptr producerSharedPtr = nullptr, const unsigned long long frameFirst = 0, - const unsigned long long frameLast = -1, const bool realTimeProcessing = false, const bool frameFlip = false, - const int frameRotate = 0, const bool framesRepeat = false); - }; - - struct WrapperOutputStruct - { - bool displayGui; - bool guiVerbose; - bool fullScreen; - std::string writePose; - DataFormat dataFormat; - std::string writePoseJson; - std::string writeCocoJson; - std::string writeImages; - std::string writeImagesFormat; - std::string writeVideo; - std::string writeHeatMaps; - std::string writeHeatMapsFormat; - - WrapperOutputStruct(const bool displayGui = false, const bool guiVerbose = false, const bool fullScreen = false, const std::string& writePose = "", - const DataFormat dataFormat = DataFormat::Json, const std::string& writePoseJson = "", const std::string& writeCocoJson = "", - const std::string& writeImages = "", const std::string& writeImagesFormat = "", const std::string& writeVideo = "", - const std::string& writeHeatMaps = "", const std::string& writeHeatMapsFormat = ""); - }; - - // This function can be used in 2 ways: - // - Synchronous mode: call the full constructor with your desired input and output workers - // - Asynchronous mode: call the empty constructor Wrapper() + use the emplace and pop functions to push the original frames and retrieve the processed ones - // - Mix of them: - // Synchronous input + asynchronous output: call the constructor Wrapper(ThreadMode::Synchronous, workersInput, {}, true) - // Asynchronous input + synchronous output: call the constructor Wrapper(ThreadMode::Synchronous, nullptr, workersOutput, irrelevantBoolean, true) + /** + * Wrapper: OpenPose all-in-one wrapper template class. + * Wrapper allows the user to set up the input (video, webcam, custom input, etc.), pose, face and/or hands estimation and rendering, + * and output (integrated small GUI, custom output, etc.). + * + * This function can be used in 2 ways: + * - Synchronous mode: call the full constructor with your desired input and output workers. + * - Asynchronous mode: call the empty constructor Wrapper() + use the emplace and pop functions to push the original frames and + * retrieve the processed ones. + * - Mix of them: + * - Synchronous input + asynchronous output: call the constructor Wrapper(ThreadManagerMode::Synchronous, workersInput, {}, true) + * - Asynchronous input + synchronous output: call the constructor + * Wrapper(ThreadManagerMode::Synchronous, nullptr, workersOutput, irrelevantBoolean, true) + */ template>>, typename TQueue = Queue>> class Wrapper { public: - explicit Wrapper(const ThreadMode threadMode = ThreadMode::Synchronous); - + /** + * Constructor. + * @param threadManagerMode Thread syncronization mode. If set to ThreadManagerMode::Synchronous, everything will run inside the Wrapper. If + * ThreadManagerMode::Synchronous(In/Out), then input (frames producer) and/or output (GUI, writing results, etc.) will be controlled + * outside the Wrapper class by the user. See ThreadManagerMode for a detailed explanation of when to use each one. + */ + explicit Wrapper(const ThreadManagerMode threadManagerMode = ThreadManagerMode::Synchronous); + + /** + * Destructor. + * It automatically frees resources. + */ ~Wrapper(); - // Useful for debugging -> all the Workers will run in the same thread (workerOnNewThread will not make any effect) - void setWrapperMode(const WrapperMode wrapperMode); - + /** + * Disable multi-threading. + * Useful for debugging and logging, all the Workers will run in the same thread. + * Note that workerOnNewThread (argument for setWorkerInput, setWorkerPostProcessing and setWorkerOutput) will not make any effect. + */ + void disableMultiThreading(); + + /** + * Add an user-defined extra Worker as frames generator. + * @param worker TWorker to be added. + * @param workerOnNewThread Whether to add this TWorker on a new thread (if it is computationally demanding) or simply reuse + * existing threads (for light functions). Set to true if the performance time is unknown. + */ void setWorkerInput(const TWorker& worker, const bool workerOnNewThread = true); + /** + * Add an user-defined extra Worker as frames post-processor. + * @param worker TWorker to be added. + * @param workerOnNewThread Whether to add this TWorker on a new thread (if it is computationally demanding) or simply reuse + * existing threads (for light functions). Set to true if the performance time is unknown. + */ void setWorkerPostProcessing(const TWorker& worker, const bool workerOnNewThread = true); + /** + * Add an user-defined extra Worker as frames consumer (custom display and/or saving). + * @param worker TWorker to be added. + * @param workerOnNewThread Whether to add this TWorker on a new thread (if it is computationally demanding) or simply reuse + * existing threads (for light functions). Set to true if the performance time is unknown. + */ void setWorkerOutput(const TWorker& worker, const bool workerOnNewThread = true); - // If output is not required, just use this function until the renderOutput argument. Keep the default values for the other parameters in order not to display/save any output. - void configure(const WrapperPoseStruct& wrapperPoseStruct, - // Producer (set producerSharedPtr = nullptr or use the default WrapperInputStruct{} to disable any input) - const WrapperInputStruct& wrapperInputStruct = WrapperInputStruct{}, + // If output is not required, just use this function until the renderOutput argument. Keep the default values for the other parameters + // in order not to display/save any output. + void configure(const WrapperStructPose& wrapperStructPose, + // Producer (set producerSharedPtr = nullptr or use the default WrapperStructInput{} to disable any input) + const WrapperStructInput& wrapperStructInput = WrapperStructInput{}, // Consumer (keep default values to disable any output) - const WrapperOutputStruct& wrapperOutputStruct = WrapperOutputStruct{}); + const WrapperStructOutput& wrapperStructOutput = WrapperStructOutput{}); // Similar to the previos configure, but it includes hand extraction and rendering - void configure(const WrapperPoseStruct& wrapperPoseStruct = WrapperPoseStruct{}, - // Hand (use the default WrapperHandsStruct{} to disable any hand detector) - const experimental::WrapperHandsStruct& wrapperHandStruct = experimental::WrapperHandsStruct{}, - // Producer (set producerSharedPtr = nullptr or use the default WrapperInputStruct{} to disable any input) - const WrapperInputStruct& wrapperInputStruct = WrapperInputStruct{}, + void configure(const WrapperStructPose& wrapperStructPose = WrapperStructPose{}, + // Hand (use the default WrapperStructHands{} to disable any hand detector) + const experimental::WrapperStructHands& wrapperHandStruct = experimental::WrapperStructHands{}, + // Producer (set producerSharedPtr = nullptr or use the default WrapperStructInput{} to disable any input) + const WrapperStructInput& wrapperStructInput = WrapperStructInput{}, // Consumer (keep default values to disable any output) - const WrapperOutputStruct& wrapperOutputStruct = WrapperOutputStruct{}); + const WrapperStructOutput& wrapperStructOutput = WrapperStructOutput{}); + /** + * Function to start multi-threading. + * Similar to start(), but exec() blocks the thread that calls the function (it saves 1 thread). Use exec() instead of + * start() if the calling thread will otherwise be waiting for the Wrapper to end. + */ void exec(); + /** + * Function to start multi-threading. + * Similar to exec(), but start() does not block the thread that calls the function. It just opens new threads, so it + * lets the user perform other tasks meanwhile on the calling thread. + */ void start(); + /** + * Function to stop multi-threading. + * It can be called internally or externally. + */ void stop(); - void reset(); - + /** + * Whether the Wrapper is running. + * It will return true after exec() or start() and before stop(), and false otherwise. + * @return Boolean specifying whether the Wrapper is running. + */ bool isRunning() const; - // Asynchronous(In) mode + /** + * Emplace (move) an element on the first (input) queue. + * Only valid if ThreadManagerMode::Asynchronous or ThreadManagerMode::AsynchronousIn. + * If the input queue is full or the Wrapper was stopped, it will return false and not emplace it. + * @param tDatums std::shared_ptr element to be emplaced. + * @return Boolean specifying whether the tDatums could be emplaced. + */ bool tryEmplace(std::shared_ptr& tDatums); - // Asynchronous(In) mode + /** + * Emplace (move) an element on the first (input) queue. + * Similar to tryEmplace. + * However, if the input queue is full, it will wait until it can emplace it. + * If the Wrapper class is stopped before adding the element, it will return false and not emplace it. + * @param tDatums std::shared_ptr element to be emplaced. + * @return Boolean specifying whether the tDatums could be emplaced. + */ bool waitAndEmplace(std::shared_ptr& tDatums); - // Asynchronous(In) mode + /** + * Push (copy) an element on the first (input) queue. + * Same as tryEmplace, but it copies the data instead of moving it. + * @param tDatums std::shared_ptr element to be pushed. + * @return Boolean specifying whether the tDatums could be pushed. + */ bool tryPush(const std::shared_ptr& tDatums); - // Asynchronous(In) mode + /** + * Push (copy) an element on the first (input) queue. + * Same as waitAndEmplace, but it copies the data instead of moving it. + * @param tDatums std::shared_ptr element to be pushed. + * @return Boolean specifying whether the tDatums could be pushed. + */ bool waitAndPush(const std::shared_ptr& tDatums); - // Asynchronous(Out) mode + /** + * Pop (retrieve) an element from the last (output) queue. + * Only valid if ThreadManagerMode::Asynchronous or ThreadManagerMode::AsynchronousOut. + * If the output queue is empty or the Wrapper was stopped, it will return false and not retrieve it. + * @param tDatums std::shared_ptr element where the retrieved element will be placed. + * @return Boolean specifying whether the tDatums could be retrieved. + */ bool tryPop(std::shared_ptr& tDatums); - // Asynchronous(Out) mode + /** + * Pop (retrieve) an element from the last (output) queue. + * Similar to tryPop. + * However, if the output queue is empty, it will wait until it can pop an element. + * If the Wrapper class is stopped before popping the element, it will return false and not retrieve it. + * @param tDatums std::shared_ptr element where the retrieved element will be placed. + * @return Boolean specifying whether the tDatums could be retrieved. + */ bool waitAndPop(std::shared_ptr& tDatums); private: - const ThreadMode mThreadMode; + const ThreadManagerMode mThreadManagerMode; const std::shared_ptr, std::atomic>> spVideoSeek; ThreadManager> mThreadManager; int mGpuNumber; @@ -160,7 +179,7 @@ namespace op bool mUserPostProcessingWsOnNewThread; bool mUserOutputWsOnNewThread; unsigned int mThreadId; - WrapperMode mWrapperMode; + bool mMultiThreadEnabled; // Workers std::vector mUserInputWs; TWorker wDatumProducer; @@ -174,10 +193,38 @@ namespace op TWorker spWGui; std::vector mUserOutputWs; + /** + * Frees TWorker variables (private internal function). + * For most cases, this class is non-necessary, since std::shared_ptr are automatically cleaned on destruction of each class. + * However, it might be useful if the same Wrapper is gonna be started twice (not recommended on most cases). + */ + void reset(); + + /** + * Set ThreadManager from TWorkers (private internal function). + * After any configure() has been called, the TWorkers are initialized. This function resets the ThreadManager and adds them. + * Common code for start() and exec(). + */ void configureThreadManager(); + /** + * Thread ID increase (private internal function). + * If multi-threading mode, it increases the thread ID. + * If single-threading mode (for debugging), it does not modify it. + * Note that mThreadId must be re-initialized to 0 before starting a new Wrapper configuration. + * @return unsigned int with the next thread id value. + */ unsigned int threadIdPP(); + /** + * TWorker concatenator (private internal function). + * Auxiliary function that concatenate std::vectors of TWorker. Since TWorker is some kind of smart pointer (usually + * std::shared_ptr), its copy still shares the same internal data. It will not work for TWorker classes that do not share + * the data when moved. + * @param workersA First std::shared_ptr element to be concatenated. + * @param workersB Second std::shared_ptr element to be concatenated. + * @return Concatenated std::vector of both workersA and workersB. + */ std::vector mergeWorkers(const std::vector& workersA, const std::vector& workersB); DELETE_COPY(Wrapper); @@ -189,18 +236,22 @@ namespace op // Implementation +#include "../core/headers.hpp" #include "../experimental/headers.hpp" +#include "../filestream/headers.hpp" #include "../gui/headers.hpp" +#include "../pose/headers.hpp" +#include "../producer/headers.hpp" #include "../utilities/errorAndLog.hpp" #include "../utilities/fileSystem.hpp" namespace op { template - Wrapper::Wrapper(const ThreadMode threadMode) : - mThreadMode{threadMode}, + Wrapper::Wrapper(const ThreadManagerMode threadManagerMode) : + mThreadManagerMode{threadManagerMode}, spVideoSeek{std::make_shared, std::atomic>>()}, - mThreadManager{threadMode}, - mWrapperMode{WrapperMode::MultiThread} + mThreadManager{threadManagerMode}, + mMultiThreadEnabled{true} { try { @@ -229,11 +280,11 @@ namespace op } template - void Wrapper::setWrapperMode(const WrapperMode wrapperMode) + void Wrapper::disableMultiThreading() { try { - mWrapperMode = {wrapperMode}; + mMultiThreadEnabled = false; } catch (const std::exception& e) { @@ -293,12 +344,12 @@ namespace op } template - void Wrapper::configure(const WrapperPoseStruct& wrapperPoseStruct, const WrapperInputStruct& wrapperInputStruct, - const WrapperOutputStruct& wrapperOutputStruct) + void Wrapper::configure(const WrapperStructPose& wrapperStructPose, const WrapperStructInput& wrapperStructInput, + const WrapperStructOutput& wrapperStructOutput) { try { - configure(wrapperPoseStruct, experimental::WrapperHandsStruct{}, wrapperInputStruct, wrapperOutputStruct); + configure(wrapperStructPose, experimental::WrapperStructHands{}, wrapperStructInput, wrapperStructOutput); } catch (const std::exception& e) { @@ -307,8 +358,8 @@ namespace op } template - void Wrapper::configure(const WrapperPoseStruct& wrapperPoseStruct, const experimental::WrapperHandsStruct& wrapperHandStruct, - const WrapperInputStruct& wrapperInputStruct, const WrapperOutputStruct& wrapperOutputStruct) + void Wrapper::configure(const WrapperStructPose& wrapperStructPose, const experimental::WrapperStructHands& wrapperHandStruct, + const WrapperStructInput& wrapperStructInput, const WrapperStructOutput& wrapperStructOutput) { try { @@ -318,119 +369,145 @@ namespace op typedef std::shared_ptr TDatumsPtr; // Check no contradictory flags enabled - if (wrapperPoseStruct.alphaPose < 0. || wrapperPoseStruct.alphaPose > 1. || wrapperPoseStruct.alphaHeatMap < 0. || wrapperPoseStruct.alphaHeatMap > 1.) + if (wrapperStructPose.alphaPose < 0. || wrapperStructPose.alphaPose > 1. || wrapperStructPose.alphaHeatMap < 0. + || wrapperStructPose.alphaHeatMap > 1.) error("Alpha value for blending must be in the range [0,1].", __LINE__, __FUNCTION__, __FILE__); - if (wrapperPoseStruct.scaleGap <= 0.f && wrapperPoseStruct.scalesNumber > 1) + if (wrapperStructPose.scaleGap <= 0.f && wrapperStructPose.scalesNumber > 1) error("The scale gap must be greater than 0 (it has no effect if the number of scales is 1).", __LINE__, __FUNCTION__, __FILE__); - if (!wrapperPoseStruct.renderOutput && (!wrapperOutputStruct.writeImages.empty() || !wrapperOutputStruct.writeVideo.empty())) - error("In order to save the rendered frames (`write_images` or `write_video`), you must set `render_output` to true.", __LINE__, __FUNCTION__, __FILE__); - if (!wrapperOutputStruct.writeHeatMaps.empty() && wrapperPoseStruct.heatMapTypes.empty()) + if (!wrapperStructPose.renderOutput && (!wrapperStructOutput.writeImages.empty() || !wrapperStructOutput.writeVideo.empty())) + { + const auto message = "In order to save the rendered frames (`write_images` or `write_video`), you must set `render_output` to true."; + error(message, __LINE__, __FUNCTION__, __FILE__); + } + if (!wrapperStructOutput.writeHeatMaps.empty() && wrapperStructPose.heatMapTypes.empty()) { const auto message = "In order to save the heatmaps (`write_heatmaps`), you need to pick which heat maps you want to save: `heatmaps_add_X`" - " flags or fill the wrapperPoseStruct.heatMapTypes."; + " flags or fill the wrapperStructPose.heatMapTypes."; error(message, __LINE__, __FUNCTION__, __FILE__); } - if (!wrapperOutputStruct.writeHeatMaps.empty() && wrapperPoseStruct.heatMapScaleMode != ScaleMode::UnsignedChar) - error("In order to save the heatmaps, you must set wrapperPoseStruct.heatMapScaleMode to ScaleMode::UnsignedChar, i.e. range [0, 255].", __LINE__, __FUNCTION__, __FILE__); - if (mUserOutputWs.empty() && mThreadMode != ThreadMode::Asynchronous && mThreadMode != ThreadMode::AsynchronousOut) + if (!wrapperStructOutput.writeHeatMaps.empty() && wrapperStructPose.heatMapScaleMode != ScaleMode::UnsignedChar) { - const std::string additionalMessage = " You could also set mThreadMode = mThreadMode::Asynchronous(Out) and/or add your own output worker class" - " before calling this function."; - const auto savingSomething = (!wrapperOutputStruct.writeImages.empty() || !wrapperOutputStruct.writeVideo.empty() || !wrapperOutputStruct.writePose.empty() - || !wrapperOutputStruct.writePoseJson.empty() || !wrapperOutputStruct.writeCocoJson.empty() - || !wrapperOutputStruct.writeHeatMaps.empty()); - if (!wrapperOutputStruct.displayGui && !savingSomething) + const auto message = "In order to save the heatmaps, you must set wrapperStructPose.heatMapScaleMode to ScaleMode::UnsignedChar," + " i.e. range [0, 255]."; + error(message, __LINE__, __FUNCTION__, __FILE__); + } + if (mUserOutputWs.empty() && mThreadManagerMode != ThreadManagerMode::Asynchronous && mThreadManagerMode != ThreadManagerMode::AsynchronousOut) + { + const std::string additionalMessage = " You could also set mThreadManagerMode = mThreadManagerMode::Asynchronous(Out) and/or add your own output worker" + " class before calling this function."; + const auto savingSomething = (!wrapperStructOutput.writeImages.empty() || !wrapperStructOutput.writeVideo.empty() + || !wrapperStructOutput.writePose.empty() || !wrapperStructOutput.writePoseJson.empty() + || !wrapperStructOutput.writeCocoJson.empty() || !wrapperStructOutput.writeHeatMaps.empty()); + if (!wrapperStructOutput.displayGui && !savingSomething) { - const auto message = "No output is selected (`no_display`) and no results are generated (no `write_X` flags enabled). Thus, no output would be " - "generated." + additionalMessage; + const auto message = "No output is selected (`no_display`) and no results are generated (no `write_X` flags enabled). Thus," + " no output would be generated." + additionalMessage; error(message, __LINE__, __FUNCTION__, __FILE__); } - if ((wrapperOutputStruct.displayGui && wrapperOutputStruct.guiVerbose) && !wrapperPoseStruct.renderOutput) + if ((wrapperStructOutput.displayGui && wrapperStructOutput.guiVerbose) && !wrapperStructPose.renderOutput) { - const auto message = "No render is enabled (`no_render_output`), so you should also remove the display (set `no_display` or `no_gui_verbose`)." - + additionalMessage; + const auto message = "No render is enabled (`no_render_output`), so you should also remove the display (set `no_display`" + " or `no_gui_verbose`)." + additionalMessage; error(message, __LINE__, __FUNCTION__, __FILE__); } - if (wrapperInputStruct.framesRepeat && savingSomething) + if (wrapperStructInput.framesRepeat && savingSomething) { const auto message = "Frames repetition (`frames_repeat`) is enabled as well as some writing function (`write_X`). This program would" " never stop recording the same frames over and over. Please, disable repetition or remove writing."; error(message, __LINE__, __FUNCTION__, __FILE__); } - if (wrapperInputStruct.realTimeProcessing && savingSomething) + if (wrapperStructInput.realTimeProcessing && savingSomething) { - const auto message = "Real time processing is enabled as well as some writing function. Thus, some frames might be skipped. Consider disabling" - " real time processing if you intend to save any results."; + const auto message = "Real time processing is enabled as well as some writing function. Thus, some frames might be skipped. Consider" + " disabling real time processing if you intend to save any results."; log(message, Priority::Max, __LINE__, __FUNCTION__, __FILE__); } } - if (!wrapperOutputStruct.writeVideo.empty() && wrapperInputStruct.producerSharedPtr == nullptr) - error("Writting video is only available if the OpenPose producer is used (i.e. wrapperInputStruct.producerSharedPtr cannot be a nullptr)."); + if (!wrapperStructOutput.writeVideo.empty() && wrapperStructInput.producerSharedPtr == nullptr) + error("Writting video is only available if the OpenPose producer is used (i.e. wrapperStructInput.producerSharedPtr cannot be a nullptr)."); // Proper format - const auto writeImagesCleaned = formatAsDirectory(wrapperOutputStruct.writeImages); - const auto writePoseCleaned = formatAsDirectory(wrapperOutputStruct.writePose); - const auto writePoseJsonCleaned = formatAsDirectory(wrapperOutputStruct.writePoseJson); - const auto writeHeatMapsCleaned = formatAsDirectory(wrapperOutputStruct.writeHeatMaps); + const auto writeImagesCleaned = formatAsDirectory(wrapperStructOutput.writeImages); + const auto writePoseCleaned = formatAsDirectory(wrapperStructOutput.writePose); + const auto writePoseJsonCleaned = formatAsDirectory(wrapperStructOutput.writePoseJson); + const auto writeHeatMapsCleaned = formatAsDirectory(wrapperStructOutput.writeHeatMaps); // Common parameters - auto finalOutputSize = wrapperPoseStruct.outputSize; + auto finalOutputSize = wrapperStructPose.outputSize; cv::Size producerSize{-1,-1}; - if (wrapperInputStruct.producerSharedPtr != nullptr) + if (wrapperStructInput.producerSharedPtr != nullptr) { // 1. Set producer properties - const auto displayProducerFpsMode = (wrapperInputStruct.realTimeProcessing ? ProducerFpsMode::OriginalFps : ProducerFpsMode::RetrievalFps); - wrapperInputStruct.producerSharedPtr->setProducerFpsMode(displayProducerFpsMode); - wrapperInputStruct.producerSharedPtr->set(ProducerProperty::Flip, wrapperInputStruct.frameFlip); - wrapperInputStruct.producerSharedPtr->set(ProducerProperty::Rotation, wrapperInputStruct.frameRotate); - wrapperInputStruct.producerSharedPtr->set(ProducerProperty::AutoRepeat, wrapperInputStruct.framesRepeat); + const auto displayProducerFpsMode = (wrapperStructInput.realTimeProcessing ? ProducerFpsMode::OriginalFps : ProducerFpsMode::RetrievalFps); + wrapperStructInput.producerSharedPtr->setProducerFpsMode(displayProducerFpsMode); + wrapperStructInput.producerSharedPtr->set(ProducerProperty::Flip, wrapperStructInput.frameFlip); + wrapperStructInput.producerSharedPtr->set(ProducerProperty::Rotation, wrapperStructInput.frameRotate); + wrapperStructInput.producerSharedPtr->set(ProducerProperty::AutoRepeat, wrapperStructInput.framesRepeat); // 2. Set finalOutputSize - producerSize = cv::Size{(int)wrapperInputStruct.producerSharedPtr->get(CV_CAP_PROP_FRAME_WIDTH), (int)wrapperInputStruct.producerSharedPtr->get(CV_CAP_PROP_FRAME_HEIGHT)}; - if (wrapperPoseStruct.outputSize.width == -1 || wrapperPoseStruct.outputSize.height == -1) + producerSize = cv::Size{(int)wrapperStructInput.producerSharedPtr->get(CV_CAP_PROP_FRAME_WIDTH), + (int)wrapperStructInput.producerSharedPtr->get(CV_CAP_PROP_FRAME_HEIGHT)}; + if (wrapperStructPose.outputSize.width == -1 || wrapperStructPose.outputSize.height == -1) { if (producerSize.area() > 0) finalOutputSize = producerSize; else - error("Output resolution = input resolution not valid for image reading (size might change between images).", __LINE__, __FUNCTION__, __FILE__); + { + const auto message = "Output resolution = input resolution not valid for image reading (size might change between images)."; + error(message, __LINE__, __FUNCTION__, __FILE__); + } } } else if (finalOutputSize.width == -1 || finalOutputSize.height == -1) - error("Output resolution cannot be (-1 x -1) unless wrapperInputStruct.producerSharedPtr is also set.", __LINE__, __FUNCTION__, __FILE__); + { + const auto message = "Output resolution cannot be (-1 x -1) unless wrapperStructInput.producerSharedPtr is also set."; + error(message, __LINE__, __FUNCTION__, __FILE__); + } // Update global parameter - mGpuNumber = wrapperPoseStruct.gpuNumber; + mGpuNumber = wrapperStructPose.gpuNumber; // Producer - if (wrapperInputStruct.producerSharedPtr != nullptr) + if (wrapperStructInput.producerSharedPtr != nullptr) { - const auto datumProducer = std::make_shared>(wrapperInputStruct.producerSharedPtr, wrapperInputStruct.frameFirst, wrapperInputStruct.frameLast, spVideoSeek); + const auto datumProducer = std::make_shared>( + wrapperStructInput.producerSharedPtr, wrapperStructInput.frameFirst, wrapperStructInput.frameLast, spVideoSeek + ); wDatumProducer = std::make_shared>(datumProducer); } else wDatumProducer = nullptr; // Pose estimators - const cv::Size& netOutputSize = wrapperPoseStruct.netInputSize; + const cv::Size& netOutputSize = wrapperStructPose.netInputSize; std::vector> poseExtractors; - for (auto gpuId = 0; gpuId < wrapperPoseStruct.gpuNumber; gpuId++) - poseExtractors.emplace_back(std::make_shared(wrapperPoseStruct.netInputSize, netOutputSize, finalOutputSize, wrapperPoseStruct.scalesNumber, - wrapperPoseStruct.scaleGap, wrapperPoseStruct.poseModel, wrapperPoseStruct.modelFolder, - gpuId + wrapperPoseStruct.gpuNumberStart, wrapperPoseStruct.heatMapTypes, - wrapperPoseStruct.heatMapScaleMode)); + for (auto gpuId = 0; gpuId < wrapperStructPose.gpuNumber; gpuId++) + poseExtractors.emplace_back(std::make_shared( + wrapperStructPose.netInputSize, netOutputSize, finalOutputSize, wrapperStructPose.scalesNumber, + wrapperStructPose.scaleGap, wrapperStructPose.poseModel, wrapperStructPose.modelFolder, + gpuId + wrapperStructPose.gpuNumberStart, wrapperStructPose.heatMapTypes, wrapperStructPose.heatMapScaleMode + )); // Pose renderers std::vector> poseRenderers; - if (wrapperPoseStruct.renderOutput) + if (wrapperStructPose.renderOutput) + { for (auto gpuId = 0; gpuId < poseExtractors.size(); gpuId++) - poseRenderers.emplace_back(std::make_shared(netOutputSize, finalOutputSize, wrapperPoseStruct.poseModel, poseExtractors[gpuId], - wrapperPoseStruct.blendOriginalFrame, wrapperPoseStruct.alphaPose, - wrapperPoseStruct.alphaHeatMap, wrapperPoseStruct.defaultPartToRender)); + { + poseRenderers.emplace_back(std::make_shared( + netOutputSize, finalOutputSize, wrapperStructPose.poseModel, poseExtractors[gpuId], + wrapperStructPose.blendOriginalFrame, wrapperStructPose.alphaPose, + wrapperStructPose.alphaHeatMap, wrapperStructPose.defaultPartToRender + )); + } + } log("", Priority::Low, __LINE__, __FUNCTION__, __FILE__); // Input cvMat to OpenPose format - const auto cvMatToOpInput = std::make_shared(wrapperPoseStruct.netInputSize, wrapperPoseStruct.scalesNumber, wrapperPoseStruct.scaleGap); + const auto cvMatToOpInput = std::make_shared( + wrapperStructPose.netInputSize, wrapperStructPose.scalesNumber, wrapperStructPose.scaleGap + ); spWCvMatToOpInput = std::make_shared>(cvMatToOpInput); - const auto cvMatToOpOutput = std::make_shared(finalOutputSize, wrapperPoseStruct.renderOutput); + const auto cvMatToOpOutput = std::make_shared(finalOutputSize, wrapperStructPose.renderOutput); spWCvMatToOpOutput = std::make_shared>(cvMatToOpOutput); // Pose extractor(s) @@ -444,8 +521,9 @@ namespace op { for (auto gpuId = 0; gpuId < spWPoses.size(); gpuId++) { - const auto handsExtractor = std::make_shared(wrapperPoseStruct.modelFolder, gpuId + wrapperPoseStruct.gpuNumberStart, - wrapperPoseStruct.poseModel); + const auto handsExtractor = std::make_shared( + wrapperStructPose.modelFolder, gpuId + wrapperStructPose.gpuNumberStart, wrapperStructPose.poseModel + ); spWPoses.at(gpuId).emplace_back(std::make_shared>(handsExtractor)); } } @@ -461,7 +539,7 @@ namespace op for (auto i = 0; i < spWPoses.size(); i++) { // Construct hands renderer - const auto handsRenderer = std::make_shared(wrapperPoseStruct.outputSize); + const auto handsRenderer = std::make_shared(finalOutputSize); // Performance boost -> share spGpuMemoryPtr for all renderers if (!poseRenderers.empty()) { @@ -479,16 +557,17 @@ namespace op if (spWPoses.size() > 1) mPostProcessingWs.emplace_back(std::make_shared>()); // Frames processor (OpenPose format -> cv::Mat format) - if (wrapperPoseStruct.renderOutput) + if (wrapperStructPose.renderOutput) { const auto opOutputToCvMat = std::make_shared(finalOutputSize); mPostProcessingWs.emplace_back(std::make_shared>(opOutputToCvMat)); } - // Resize pose to input size if we want to save any results - if (wrapperPoseStruct.scaleMode != ScaleMode::OutputResolution && (wrapperPoseStruct.scaleMode != ScaleMode::InputResolution || (finalOutputSize != producerSize)) - && (wrapperPoseStruct.scaleMode != ScaleMode::NetOutputResolution || (finalOutputSize != netOutputSize))) + // Re-scale pose if desired + if (wrapperStructPose.poseScaleMode != ScaleMode::OutputResolution + && (wrapperStructPose.poseScaleMode != ScaleMode::InputResolution || (finalOutputSize != producerSize)) + && (wrapperStructPose.poseScaleMode != ScaleMode::NetOutputResolution || (finalOutputSize != netOutputSize))) { - auto arrayScaler = std::make_shared(wrapperPoseStruct.scaleMode); + auto arrayScaler = std::make_shared(wrapperStructPose.poseScaleMode); mPostProcessingWs.emplace_back(std::make_shared>(arrayScaler)); } @@ -496,7 +575,7 @@ namespace op // Write people pose data on disk (json for OpenCV >= 3, xml, yml...) if (!writePoseCleaned.empty()) { - const auto poseSaver = std::make_shared(writePoseCleaned, wrapperOutputStruct.dataFormat); + const auto poseSaver = std::make_shared(writePoseCleaned, wrapperStructOutput.writePoseDataFormat); mOutputWs.emplace_back(std::make_shared>(poseSaver)); } // Write people pose data on disk (json format) @@ -506,44 +585,50 @@ namespace op mOutputWs.emplace_back(std::make_shared>(poseJsonSaver)); } // Write people pose data on disk (COCO validation json format) - if (!wrapperOutputStruct.writeCocoJson.empty()) + if (!wrapperStructOutput.writeCocoJson.empty()) { const auto humanFormat = true; // If true, bigger size (and potentially slower to process), but easier for a human to read it - const auto poseJsonCocoSaver = std::make_shared(wrapperOutputStruct.writeCocoJson, humanFormat); + const auto poseJsonCocoSaver = std::make_shared(wrapperStructOutput.writeCocoJson, humanFormat); mOutputWs.emplace_back(std::make_shared>(poseJsonCocoSaver)); } // Write frames as desired image format on hard disk if (!writeImagesCleaned.empty()) { - const auto imageSaver = std::make_shared(writeImagesCleaned, wrapperOutputStruct.writeImagesFormat); + const auto imageSaver = std::make_shared(writeImagesCleaned, wrapperStructOutput.writeImagesFormat); mOutputWs.emplace_back(std::make_shared>(imageSaver)); } // Write frames as *.avi video on hard disk - if (!wrapperOutputStruct.writeVideo.empty() && wrapperInputStruct.producerSharedPtr != nullptr) + if (!wrapperStructOutput.writeVideo.empty() && wrapperStructInput.producerSharedPtr != nullptr) { - const auto originalVideoFps = (wrapperInputStruct.producerSharedPtr->getType() != ProducerType::Webcam && wrapperInputStruct.producerSharedPtr->get(CV_CAP_PROP_FPS) > 0. - ? wrapperInputStruct.producerSharedPtr->get(CV_CAP_PROP_FPS) : 30.); - const auto videoSaver = std::make_shared(wrapperOutputStruct.writeVideo, CV_FOURCC('M','J','P','G'), originalVideoFps, finalOutputSize); + const auto originalVideoFps = (wrapperStructInput.producerSharedPtr->getType() != ProducerType::Webcam + && wrapperStructInput.producerSharedPtr->get(CV_CAP_PROP_FPS) > 0. + ? wrapperStructInput.producerSharedPtr->get(CV_CAP_PROP_FPS) : 30.); + const auto videoSaver = std::make_shared( + wrapperStructOutput.writeVideo, CV_FOURCC('M','J','P','G'), originalVideoFps, finalOutputSize + ); mOutputWs.emplace_back(std::make_shared>(videoSaver)); } // Write heat maps as desired image format on hard disk if (!writeHeatMapsCleaned.empty()) { - const auto heatMapSaver = std::make_shared(writeHeatMapsCleaned, wrapperOutputStruct.writeHeatMapsFormat); + const auto heatMapSaver = std::make_shared(writeHeatMapsCleaned, wrapperStructOutput.writeHeatMapsFormat); mOutputWs.emplace_back(std::make_shared>(heatMapSaver)); } // Add frame information for GUI - // If this WGuiInfoAdder instance is placed before the WImageSaver or WVideoSaver, then the resulting recorded frames will look exactly as the final displayed image by the GUI - if (wrapperOutputStruct.displayGui && wrapperOutputStruct.guiVerbose) + // If this WGuiInfoAdder instance is placed before the WImageSaver or WVideoSaver, then the resulting recorded frames will + // look exactly as the final displayed image by the GUI + if (wrapperStructOutput.displayGui && wrapperStructOutput.guiVerbose) { - const auto guiInfoAdder = std::make_shared(finalOutputSize, wrapperPoseStruct.gpuNumber); + const auto guiInfoAdder = std::make_shared(finalOutputSize, wrapperStructPose.gpuNumber); mOutputWs.emplace_back(std::make_shared>(guiInfoAdder)); } // Minimal graphical user interface (GUI) spWGui = nullptr; - if (wrapperOutputStruct.displayGui) + if (wrapperStructOutput.displayGui) { - const auto gui = std::make_shared(wrapperOutputStruct.fullScreen, finalOutputSize, mThreadManager.getIsRunningSharedPtr(), spVideoSeek, poseExtractors, poseRenderers); + const auto gui = std::make_shared( + wrapperStructOutput.fullScreen, finalOutputSize, mThreadManager.getIsRunningSharedPtr(), spVideoSeek, poseExtractors, poseRenderers + ); spWGui = {std::make_shared>(gui)}; } log("", Priority::Low, __LINE__, __FUNCTION__, __FILE__); @@ -595,30 +680,6 @@ namespace op } } - template - void Wrapper::reset() - { - try - { - mThreadManager.reset(); - // Reset - mUserInputWs.clear(); - wDatumProducer = nullptr; - spWCvMatToOpInput = nullptr; - spWCvMatToOpOutput = nullptr; - spWPoses.clear(); - mPostProcessingWs.clear(); - mUserPostProcessingWs.clear(); - mOutputWs.clear(); - spWGui = nullptr; - mUserOutputWs.clear(); - } - catch (const std::exception& e) - { - error(e.what(), __LINE__, __FUNCTION__, __FILE__); - } - } - template bool Wrapper::isRunning() const { @@ -729,6 +790,31 @@ namespace op } } + template + void Wrapper::reset() + { + try + { + mThreadManager.reset(); + mThreadId = 0ull; + // Reset + mUserInputWs.clear(); + wDatumProducer = nullptr; + spWCvMatToOpInput = nullptr; + spWCvMatToOpOutput = nullptr; + spWPoses.clear(); + mPostProcessingWs.clear(); + mUserPostProcessingWs.clear(); + mOutputWs.clear(); + spWGui = nullptr; + mUserOutputWs.clear(); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + template void Wrapper::configureThreadManager() { @@ -739,11 +825,19 @@ namespace op // Security checks if (spWCvMatToOpInput == nullptr || spWCvMatToOpOutput == nullptr) error("Configure the Wrapper class before calling `start()`.", __LINE__, __FUNCTION__, __FILE__); - if ((wDatumProducer == nullptr) == (mUserInputWs.empty()) && mThreadMode != ThreadMode::Asynchronous && mThreadMode != ThreadMode::AsynchronousIn) - error("You need to have 1 and only 1 producer selected. You can introduce your own producer by using setWorkerInput() or use the OpenPose default" - " producer by configuring it in the configure function) or use the ThreadMode::Asynchronous(In) mode.", __LINE__, __FUNCTION__, __FILE__); - if (mOutputWs.empty() && mUserOutputWs.empty() && spWGui == nullptr && mThreadMode != ThreadMode::Asynchronous && mThreadMode != ThreadMode::AsynchronousOut) + if ((wDatumProducer == nullptr) == (mUserInputWs.empty()) + && mThreadManagerMode != ThreadManagerMode::Asynchronous && mThreadManagerMode != ThreadManagerMode::AsynchronousIn) + { + const auto message = "You need to have 1 and only 1 producer selected. You can introduce your own producer by using setWorkerInput() or" + " use the OpenPose default producer by configuring it in the configure function) or use the" + " ThreadManagerMode::Asynchronous(In) mode."; + error(message, __LINE__, __FUNCTION__, __FILE__); + } + if (mOutputWs.empty() && mUserOutputWs.empty() && spWGui == nullptr && mThreadManagerMode != ThreadManagerMode::Asynchronous + && mThreadManagerMode != ThreadManagerMode::AsynchronousOut) + { error("No output selected.", __LINE__, __FUNCTION__, __FILE__); + } // Thread Manager: // Clean previous thread manager (avoid configure to crash the program if used more than once) @@ -755,9 +849,9 @@ namespace op spWIdGenerator = std::make_shared>>(); if (!mUserInputWs.empty() && mUserInputWsOnNewThread) { - mThreadManager.add(mThreadId, mUserInputWs, queueIn++, queueOut++); // Thread 0, queues 0 -> 1 + mThreadManager.add(mThreadId, mUserInputWs, queueIn++, queueOut++); // Thread 0, queues 0 -> 1 threadIdPP(); - mThreadManager.add(mThreadId, {spWIdGenerator, spWCvMatToOpInput, spWCvMatToOpOutput}, queueIn++, queueOut++); // Thread 1, queues 1 -> 2 + mThreadManager.add(mThreadId, {spWIdGenerator, spWCvMatToOpInput, spWCvMatToOpOutput}, queueIn++, queueOut++); // Thread 1, queues 1 -> 2 } // If custom user Worker in same thread or producer on same thread else @@ -770,17 +864,17 @@ namespace op else if (wDatumProducer != nullptr) workersAux = mergeWorkers(workersAux, {wDatumProducer}); // Otherwise - else if (mThreadMode != ThreadMode::Asynchronous && mThreadMode != ThreadMode::AsynchronousIn) + else if (mThreadManagerMode != ThreadManagerMode::Asynchronous && mThreadManagerMode != ThreadManagerMode::AsynchronousIn) error("No input selected.", __LINE__, __FUNCTION__, __FILE__); workersAux = mergeWorkers(workersAux, {spWIdGenerator, spWCvMatToOpInput, spWCvMatToOpOutput}); - mThreadManager.add(mThreadId, workersAux, queueIn++, queueOut++); // Thread 0 or 1, queues 0 -> 1 + mThreadManager.add(mThreadId, workersAux, queueIn++, queueOut++); // Thread 0 or 1, queues 0 -> 1 } threadIdPP(); // Pose estimation & rendering - if (!spWPoses.empty()) // Thread 1 or 2...X, queues 1 -> 2, X = 2 + number GPUs + if (!spWPoses.empty()) // Thread 1 or 2...X, queues 1 -> 2, X = 2 + #GPUs { - if (mWrapperMode == WrapperMode::MultiThread) + if (mMultiThreadEnabled) { for (auto& wPose : spWPoses) { @@ -789,7 +883,10 @@ namespace op } } else + { + log("Debugging activated, only 1 thread running, all spWPoses have been disabled but the first one."); mThreadManager.add(mThreadId, spWPoses.at(0), queueIn, queueOut); + } queueIn++; queueOut++; } @@ -799,16 +896,16 @@ namespace op // Post processing workers if (!mPostProcessingWs.empty()) { - mThreadManager.add(mThreadId, mPostProcessingWs, queueIn++, queueOut++); // Thread 2 or 3, queues 2 -> 3 + mThreadManager.add(mThreadId, mPostProcessingWs, queueIn++, queueOut++); // Thread 2 or 3, queues 2 -> 3 threadIdPP(); } // User processing workers - mThreadManager.add(mThreadId, mUserPostProcessingWs, queueIn++, queueOut++); // Thread 3 or 4, queues 3 -> 4 + mThreadManager.add(mThreadId, mUserPostProcessingWs, queueIn++, queueOut++); // Thread 3 or 4, queues 3 -> 4 threadIdPP(); // Output workers if (!mOutputWs.empty()) { - mThreadManager.add(mThreadId, mOutputWs, queueIn++, queueOut++); // Thread 4 or 5, queues 4 -> 5 + mThreadManager.add(mThreadId, mOutputWs, queueIn++, queueOut++); // Thread 4 or 5, queues 4 -> 5 threadIdPP(); } } @@ -820,12 +917,12 @@ namespace op workersAux = mergeWorkers(workersAux, mOutputWs); if (!workersAux.empty()) { - mThreadManager.add(mThreadId, workersAux, queueIn++, queueOut++); // Thread 2 or 3, queues 2 -> 3 + mThreadManager.add(mThreadId, workersAux, queueIn++, queueOut++); // Thread 2 or 3, queues 2 -> 3 threadIdPP(); } } // User output worker - if (!mUserOutputWs.empty()) // Thread Y, queues Q -> Q+1 + if (!mUserOutputWs.empty()) // Thread Y, queues Q -> Q+1 { if (mUserOutputWsOnNewThread) { @@ -838,7 +935,7 @@ namespace op // OpenPose GUI if (spWGui != nullptr) { - mThreadManager.add(mThreadId, spWGui, queueIn++, queueOut++); // Thread Y+1, queues Q+1 -> Q+2 + mThreadManager.add(mThreadId, spWGui, queueIn++, queueOut++); // Thread Y+1, queues Q+1 -> Q+2 threadIdPP(); } log("", Priority::Low, __LINE__, __FUNCTION__, __FILE__); @@ -854,7 +951,7 @@ namespace op { try { - if (mWrapperMode == WrapperMode::MultiThread) + if (mMultiThreadEnabled) mThreadId++; return mThreadId; } diff --git a/include/openpose/wrapper/wrapperStructHands.hpp b/include/openpose/wrapper/wrapperStructHands.hpp new file mode 100644 index 00000000..20a95ed2 --- /dev/null +++ b/include/openpose/wrapper/wrapperStructHands.hpp @@ -0,0 +1,32 @@ +#ifndef OPENPOSE__WRAPPER__WRAPPER_STRUCT_HANDS_HPP +#define OPENPOSE__WRAPPER__WRAPPER_STRUCT_HANDS_HPP + +namespace op +{ + namespace experimental + { + /** + * WrapperStructHands: Hands estimation and rendering configuration struct. + * DO NOT USE. CODE TO BE FINISHED. + * WrapperStructHands allows the user to set up the hands estimation and rendering parameters that will be used for the OpenPose Wrapper + * class. + */ + struct WrapperStructHands + { + /** + * PROVISIONAL PARAMETER. IT WILL BE CHANGED. + * Whether to extract and render hands. + */ + bool extractAndRenderHands; + + /** + * Constructor of the struct. + * It has the recommended and default values we recommend for each element of the struct. + * Since all the elements of the struct are public, they can also be manually filled. + */ + WrapperStructHands(const bool extractAndRenderHands = false); + }; + } +} + +#endif // OPENPOSE__WRAPPER__WRAPPER_STRUCT_HANDS_HPP diff --git a/include/openpose/wrapper/wrapperStructInput.hpp b/include/openpose/wrapper/wrapperStructInput.hpp new file mode 100644 index 00000000..bdfec5b3 --- /dev/null +++ b/include/openpose/wrapper/wrapperStructInput.hpp @@ -0,0 +1,65 @@ +#ifndef OPENPOSE__WRAPPER__WRAPPER_STRUCT_INPUT_HPP +#define OPENPOSE__WRAPPER__WRAPPER_STRUCT_INPUT_HPP + +#include +#include "../producer/producer.hpp" + +namespace op +{ + /** + * WrapperStructInput: Input (images, video, webcam, etc.) configuration struct. + * WrapperStructInput allows the user to set up the input frames generator. + */ + struct WrapperStructInput + { + /** + * Producer which will generate the frames. + * Set to nullptr to disable the whole input, i.e. if the user is going to use his own frames generator. + */ + std::shared_ptr producerSharedPtr; + + /** + * First image to obtain. + * Default: 0. + */ + unsigned long long frameFirst; + + /** + * Last image to obtain. + * Default: -1 (i.e. obtain all frames). + */ + unsigned long long frameLast; + + /** + * Whether to skip or sleep in order to keep the same FPS as the frames producer. + */ + bool realTimeProcessing; + + /** + * Whether to flip (mirror) the image. + */ + bool frameFlip; + + /** + * Image rotation. + * Only 4 possible values: 0 (default, no rotation), 90, 180 or 270 degrees + */ + int frameRotate; + + /** + * Whether to re-open the producer if it reaches the end (e.g. video or image directory after the last frame). + */ + bool framesRepeat; + + /** + * Constructor of the struct. + * It has the recommended and default values we recommend for each element of the struct. + * Since all the elements of the struct are public, they can also be manually filled. + */ + WrapperStructInput(const std::shared_ptr producerSharedPtr = nullptr, const unsigned long long frameFirst = 0, + const unsigned long long frameLast = -1, const bool realTimeProcessing = false, const bool frameFlip = false, + const int frameRotate = 0, const bool framesRepeat = false); + }; +} + +#endif // OPENPOSE__WRAPPER__WRAPPER_STRUCT_INPUT_HPP diff --git a/include/openpose/wrapper/wrapperStructOutput.hpp b/include/openpose/wrapper/wrapperStructOutput.hpp new file mode 100644 index 00000000..0492430c --- /dev/null +++ b/include/openpose/wrapper/wrapperStructOutput.hpp @@ -0,0 +1,103 @@ +#ifndef OPENPOSE__WRAPPER__WRAPPER_STRUCT_OUTPUT_HPP +#define OPENPOSE__WRAPPER__WRAPPER_STRUCT_OUTPUT_HPP + +#include +#include "../filestream/enumClasses.hpp" + +namespace op +{ + /** + * WrapperStructOutput: Output (small GUI, writing rendered results and/or pose data, etc.) configuration struct. + * WrapperStructOutput allows the user to set up the input frames generator. + */ + struct WrapperStructOutput + { + /** + * Whether to display the OpenPose small integrated GUI. + */ + bool displayGui; + + /** + * Whether to add some information to the frame (number of frame, number people detected, etc.) after it is saved on disk + * and before it is displayed and/or returned to the user. + */ + bool guiVerbose; + + /** + * Whether to display the OpenPose small integrated GUI on fullscreen mode. It can be changed by interacting with the GUI itself. + */ + bool fullScreen; + + /** + * Pose (x, y, score) locations saving folder location. + * If it is empty (default), it is disabled. + * Select format with writePoseDataFormat. + */ + std::string writePose; + + /** + * Data format to save Pose (x, y, score) locations. + * Options: DataFormat::Json (default), DataFormat::Xml and DataFormat::Yml (equivalent to DataFormat::Yaml) + * JSON option only available for OpenCV >= 3.0. + */ + DataFormat writePoseDataFormat; + + /** + * Pose (x, y, score) locations saving folder location in JSON format (e.g. useful when needed JSON but using OpenCV < 3.0). + * If it is empty (default), it is disabled. + */ + std::string writePoseJson; + + /** + * Pose (x, y, score) locations saving folder location in JSON COCO validation format. + * If it is empty (default), it is disabled. + */ + std::string writeCocoJson; + + /** + * Rendered image saving folder. + * If it is empty (default), it is disabled. + */ + std::string writeImages; + + /** + * Rendered image saving folder format. + * Check your OpenCV version documentation for a list of compatible formats. + * E.g. png, jpg, etc. + * If writeImages is empty (default), it makes no effect. + */ + std::string writeImagesFormat; + + /** + * Rendered images saving video path. + * Please, use *.avi format. + * If it is empty (default), it is disabled. + */ + std::string writeVideo; + + /** + * Rendered heat maps saving folder. + * In order to save the heatmaps, WrapperStructPose.heatMapTypes must also be filled. + * If it is empty (default), it is disabled. + */ + std::string writeHeatMaps; + + /** + * Heat maps image saving format. + * Analogous to writeImagesFormat. + */ + std::string writeHeatMapsFormat; + + /** + * Constructor of the struct. + * It has the recommended and default values we recommend for each element of the struct. + * Since all the elements of the struct are public, they can also be manually filled. + */ + WrapperStructOutput(const bool displayGui = false, const bool guiVerbose = false, const bool fullScreen = false, const std::string& writePose = "", + const DataFormat writePoseDataFormat = DataFormat::Xml, const std::string& writePoseJson = "", const std::string& writeCocoJson = "", + const std::string& writeImages = "", const std::string& writeImagesFormat = "", const std::string& writeVideo = "", + const std::string& writeHeatMaps = "", const std::string& writeHeatMapsFormat = ""); + }; +} + +#endif // OPENPOSE__WRAPPER__WRAPPER_STRUCT_OUTPUT_HPP diff --git a/include/openpose/wrapper/wrapperStructPose.hpp b/include/openpose/wrapper/wrapperStructPose.hpp new file mode 100644 index 00000000..9c389800 --- /dev/null +++ b/include/openpose/wrapper/wrapperStructPose.hpp @@ -0,0 +1,135 @@ +#ifndef OPENPOSE__WRAPPER__WRAPPER_STRUCT_POSE_HPP +#define OPENPOSE__WRAPPER__WRAPPER_STRUCT_POSE_HPP + +#include +#include "../core/enumClasses.hpp" +#include "../pose/enumClasses.hpp" +#include "../pose/poseParameters.hpp" + +namespace op +{ + /** + * WrapperStructPose: Pose estimation and rendering configuration struct. + * WrapperStructPose allows the user to set up the pose estimation and rendering parameters that will be used for the OpenPose Wrapper + * class. + */ + struct WrapperStructPose + { + /** + * CCN (Conv Net) input size. + * The greater, the slower and more memory it will be needed, but it will potentially increase accuracy. + * Both width and height must be divisible by 16. + */ + cv::Size netInputSize; + + /** + * Output size of the final rendered image. + * It barely affects performance compared to netInputSize. + * The final Datum.pose can be scaled with respect to outputSize if `poseScaleMode` is set to ScaleMode::OutputResolution, even if the + * rendering is disabled. + */ + cv::Size outputSize; + + /** + * Final scale of the Array Datum.pose and the writen pose data. + * The final Datum.pose can be scaled with respect to input size (ScaleMode::InputResolution), net output size (ScaleMode::NetOutputResolution), + * output rendering size (ScaleMode::OutputResolution), from 0 to 1 (ScaleMode::ZeroToOne), and -1 to 1 (ScaleMode::PlusMinusOne). + */ + ScaleMode poseScaleMode; + + /** + * Number of GPUs processing in parallel. + * The greater, the faster the algorithm will run, but potentially higher lag will appear (which only affects in real-time webcam scenarios). + */ + int gpuNumber; + + /** + * First GPU device. + * Such as the GPUs used will be the ones in the range: [gpuNumberStart, gpuNumberStart + gpuNumber]. + */ + int gpuNumberStart; + + /** + * Number of scales to process. + * The greater, the slower and more memory it will be needed, but it will potentially increase accuracy. + * This parameter is related with scaleGap, such as the final pose estimation will be an average of the predicted results for each scale. + */ + int scalesNumber; + + /** + * Gap between successive scales. + * The pose estimation will be estimation for the scales in the range [1, 1-scaleGap*scalesNumber], with a gap of scaleGap. + */ + float scaleGap; + + /** + * Whether to render the output (pose locations, body, background or PAF heat maps). + */ + bool renderOutput; + + /** + * Pose model, it affects the number of body parts to render + * Select PoseModel::COCO_18 for 18 body-part COCO, PoseModel::MPI_15 for 15 body-part MPI, PoseModel::MPI_15_4 for faster version + * of MPI, etc.). + */ + PoseModel poseModel; + + /** + * Whether to blend the final results on top of the original image, or just render them on a flat background. + */ + bool blendOriginalFrame; + + /** + * Rendering blending alpha value of the pose point locations with respect to the background image. + * Value in the range [0, 1]. 0 will only render the background, 1 will fully render the pose. + */ + float alphaPose; + + /** + * Rendering blending alpha value of the heat maps (body part, background or PAF) with respect to the background image. + * Value in the range [0, 1]. 0 will only render the background, 1 will only render the heat map. + */ + float alphaHeatMap; + + /** + * Element to initially render. + * Set 0 for pose, [1, #body parts] for each body part following the order on POSE_BODY_PART_MAPPING on + * `include/pose/poseParameters.hpp`, #body parts+1 for background, #body parts+2 for all body parts overlapped, + * #body parts+3 for all PAFs, and [#body parts+4, #body parts+4+#pair pairs] for each PAF following the order on POSE_BODY_PART_PAIRS. + */ + int defaultPartToRender; + + /** + * Folder where the pose Caffe models are located. + */ + std::string modelFolder; + + /** + * Whether and which heat maps to save on the Array Datum.heatmaps. + * Use HeatMapType::Parts for body parts, HeatMapType::Background for the background, and HeatMapType::PAFs for the Part Affinity Fields. + */ + std::vector heatMapTypes; + + /** + * Scale of the Datum.heatmaps. + * Select ScaleMode::ZeroToOne for range [0,1], ScaleMode::PlusMinusOne for [-1,1] and ScaleMode::UnsignedChar for [0, 255] + * If heatMapTypes.empty(), then this parameters makes no effect. + */ + ScaleMode heatMapScaleMode; + + /** + * Constructor of the struct. + * It has the recommended and default values we recommend for each element of the struct. + * Since all the elements of the struct are public, they can also be manually filled. + */ + WrapperStructPose(const cv::Size& netInputSize = cv::Size{656, 368}, const cv::Size& outputSize = cv::Size{1280, 720}, + const ScaleMode poseScaleMode = ScaleMode::InputResolution, const int gpuNumber = 1, const int gpuNumberStart = 0, + const int scalesNumber = 1, const float scaleGap = 0.15f, const bool renderOutput = false, + const PoseModel poseModel = PoseModel::COCO_18, const bool blendOriginalFrame = true, + const float alphaPose = POSE_DEFAULT_ALPHA_POSE, const float alphaHeatMap = POSE_DEFAULT_ALPHA_HEATMAP, + const int defaultPartToRender = 0, const std::string& modelFolder = "models/", + const std::vector& heatMapTypes = {}, const ScaleMode heatMapScaleMode = ScaleMode::ZeroToOne); + }; +} + +#endif // OPENPOSE__WRAPPER__WRAPPER_STRUCT_POSE_HPP diff --git a/src/openpose/wrapper/wrapper.cpp b/src/openpose/wrapper/wrapper.cpp deleted file mode 100644 index caf26eaf..00000000 --- a/src/openpose/wrapper/wrapper.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "openpose/wrapper/wrapper.hpp" - -namespace op -{ - WrapperPoseStruct::WrapperPoseStruct(const cv::Size& netInputSize_, const cv::Size& outputSize_, const ScaleMode scaleMode_, const int gpuNumber_, - const int gpuNumberStart_, const int scalesNumber_, const float scaleGap_, const bool renderOutput_, const PoseModel poseModel_, - const bool blendOriginalFrame_, const float alphaPose_, const float alphaHeatMap_, const int defaultPartToRender_, - const std::string& modelFolder_, const std::vector& heatMapTypes_, const ScaleMode heatMapScaleMode_) : - netInputSize{netInputSize_}, - outputSize{outputSize_}, - scaleMode{scaleMode_}, - gpuNumber{gpuNumber_}, - gpuNumberStart{gpuNumberStart_}, - scalesNumber{scalesNumber_}, - scaleGap{scaleGap_}, - renderOutput{renderOutput_}, - poseModel{poseModel_}, - blendOriginalFrame{blendOriginalFrame_}, - alphaPose{alphaPose_}, - alphaHeatMap{alphaHeatMap_}, - defaultPartToRender{defaultPartToRender_}, - modelFolder{modelFolder_}, - heatMapTypes{heatMapTypes_}, - heatMapScaleMode{heatMapScaleMode_} - { - } - - namespace experimental - { - WrapperHandsStruct::WrapperHandsStruct(const bool extractAndRenderHands_) : - extractAndRenderHands{extractAndRenderHands_} - { - } - } - - WrapperInputStruct::WrapperInputStruct(const std::shared_ptr producerSharedPtr_, const unsigned long long frameFirst_, const unsigned long long frameLast_, - const bool realTimeProcessing_, const bool frameFlip_, const int frameRotate_, const bool framesRepeat_) : - producerSharedPtr{producerSharedPtr_}, - frameFirst{frameFirst_}, - frameLast{frameLast_}, - realTimeProcessing{realTimeProcessing_}, - frameFlip{frameFlip_}, - frameRotate{frameRotate_}, - framesRepeat{framesRepeat_} - { - } - - WrapperOutputStruct::WrapperOutputStruct(const bool displayGui_, const bool guiVerbose_, const bool fullScreen_, const std::string& writePose_, - const DataFormat dataFormat_, const std::string& writePoseJson_, const std::string& writeCocoJson_, - const std::string& writeImages_, const std::string& writeImagesFormat_, const std::string& writeVideo_, - const std::string& writeHeatMaps_, const std::string& writeHeatMapsFormat_) : - displayGui{displayGui_}, - guiVerbose{guiVerbose_}, - fullScreen{fullScreen_}, - writePose{writePose_}, - dataFormat{dataFormat_}, - writePoseJson{writePoseJson_}, - writeCocoJson{writeCocoJson_}, - writeImages{writeImages_}, - writeImagesFormat{writeImagesFormat_}, - writeVideo{writeVideo_}, - writeHeatMaps{writeHeatMaps_}, - writeHeatMapsFormat{writeHeatMapsFormat_} - { - } -} diff --git a/src/openpose/wrapper/wrapperStructHands.cpp b/src/openpose/wrapper/wrapperStructHands.cpp new file mode 100644 index 00000000..36f2d779 --- /dev/null +++ b/src/openpose/wrapper/wrapperStructHands.cpp @@ -0,0 +1,12 @@ +#include "openpose/wrapper/wrapperStructHands.hpp" + +namespace op +{ + namespace experimental + { + WrapperStructHands::WrapperStructHands(const bool extractAndRenderHands_) : + extractAndRenderHands{extractAndRenderHands_} + { + } + } +} diff --git a/src/openpose/wrapper/wrapperStructInput.cpp b/src/openpose/wrapper/wrapperStructInput.cpp new file mode 100644 index 00000000..e31139a3 --- /dev/null +++ b/src/openpose/wrapper/wrapperStructInput.cpp @@ -0,0 +1,16 @@ +#include "openpose/wrapper/wrapperStructInput.hpp" + +namespace op +{ + WrapperStructInput::WrapperStructInput(const std::shared_ptr producerSharedPtr_, const unsigned long long frameFirst_, const unsigned long long frameLast_, + const bool realTimeProcessing_, const bool frameFlip_, const int frameRotate_, const bool framesRepeat_) : + producerSharedPtr{producerSharedPtr_}, + frameFirst{frameFirst_}, + frameLast{frameLast_}, + realTimeProcessing{realTimeProcessing_}, + frameFlip{frameFlip_}, + frameRotate{frameRotate_}, + framesRepeat{framesRepeat_} + { + } +} diff --git a/src/openpose/wrapper/wrapperStructOutput.cpp b/src/openpose/wrapper/wrapperStructOutput.cpp new file mode 100644 index 00000000..4e1e60db --- /dev/null +++ b/src/openpose/wrapper/wrapperStructOutput.cpp @@ -0,0 +1,23 @@ +#include "openpose/wrapper/wrapperStructOutput.hpp" + +namespace op +{ + WrapperStructOutput::WrapperStructOutput(const bool displayGui_, const bool guiVerbose_, const bool fullScreen_, const std::string& writePose_, + const DataFormat writePoseDataFormat_, const std::string& writePoseJson_, const std::string& writeCocoJson_, + const std::string& writeImages_, const std::string& writeImagesFormat_, const std::string& writeVideo_, + const std::string& writeHeatMaps_, const std::string& writeHeatMapsFormat_) : + displayGui{displayGui_}, + guiVerbose{guiVerbose_}, + fullScreen{fullScreen_}, + writePose{writePose_}, + writePoseDataFormat{writePoseDataFormat_}, + writePoseJson{writePoseJson_}, + writeCocoJson{writeCocoJson_}, + writeImages{writeImages_}, + writeImagesFormat{writeImagesFormat_}, + writeVideo{writeVideo_}, + writeHeatMaps{writeHeatMaps_}, + writeHeatMapsFormat{writeHeatMapsFormat_} + { + } +} diff --git a/src/openpose/wrapper/wrapperStructPose.cpp b/src/openpose/wrapper/wrapperStructPose.cpp new file mode 100644 index 00000000..a5d9b974 --- /dev/null +++ b/src/openpose/wrapper/wrapperStructPose.cpp @@ -0,0 +1,28 @@ +#include "openpose/wrapper/wrapperStructPose.hpp" + +namespace op +{ + WrapperStructPose::WrapperStructPose(const cv::Size& netInputSize_, const cv::Size& outputSize_, const ScaleMode poseScaleMode_, const int gpuNumber_, + const int gpuNumberStart_, const int scalesNumber_, const float scaleGap_, const bool renderOutput_, + const PoseModel poseModel_, const bool blendOriginalFrame_, const float alphaPose_, const float alphaHeatMap_, + const int defaultPartToRender_, const std::string& modelFolder_, const std::vector& heatMapTypes_, + const ScaleMode heatMapScaleMode_) : + netInputSize{netInputSize_}, + outputSize{outputSize_}, + poseScaleMode{poseScaleMode_}, + gpuNumber{gpuNumber_}, + gpuNumberStart{gpuNumberStart_}, + scalesNumber{scalesNumber_}, + scaleGap{scaleGap_}, + renderOutput{renderOutput_}, + poseModel{poseModel_}, + blendOriginalFrame{blendOriginalFrame_}, + alphaPose{alphaPose_}, + alphaHeatMap{alphaHeatMap_}, + defaultPartToRender{defaultPartToRender_}, + modelFolder{modelFolder_}, + heatMapTypes{heatMapTypes_}, + heatMapScaleMode{heatMapScaleMode_} + { + } +} -- GitLab